Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 15 Oct 2016 01:19:05 +0000 (18:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 15 Oct 2016 01:19:05 +0000 (18:19 -0700)
Pull more misc uaccess and vfs updates from Al Viro:
 "The rest of the stuff from -next (more uaccess work) + assorted fixes"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  score: traps: Add missing include file to fix build error
  fs/super.c: don't fool lockdep in freeze_super() and thaw_super() paths
  fs/super.c: fix race between freeze_super() and thaw_super()
  overlayfs: Fix setting IOP_XATTR flag
  iov_iter: kernel-doc import_iovec() and rw_copy_check_uvector()
  blackfin: no access_ok() for __copy_{to,from}_user()
  arm64: don't zero in __copy_from_user{,_inatomic}
  arm: don't zero in __copy_from_user_inatomic()/__copy_from_user()
  arc: don't leak bits of kernel stack into coredump
  alpha: get rid of tail-zeroing in __copy_user()

2034 files changed:
Documentation/00-INDEX
Documentation/80211/cfg80211.rst [new file with mode: 0644]
Documentation/80211/conf.py [new file with mode: 0644]
Documentation/80211/index.rst [new file with mode: 0644]
Documentation/80211/introduction.rst [new file with mode: 0644]
Documentation/80211/mac80211-advanced.rst [new file with mode: 0644]
Documentation/80211/mac80211.rst [new file with mode: 0644]
Documentation/DMA-attributes.txt
Documentation/DocBook/80211.tmpl [deleted file]
Documentation/DocBook/Makefile
Documentation/DocBook/kernel-hacking.tmpl
Documentation/Makefile
Documentation/RCU/lockdep-splat.txt
Documentation/accounting/.gitignore [deleted file]
Documentation/accounting/Makefile [deleted file]
Documentation/accounting/delay-accounting.txt
Documentation/accounting/getdelays.c [deleted file]
Documentation/arm/00-INDEX
Documentation/auxdisplay/.gitignore [deleted file]
Documentation/auxdisplay/Makefile [deleted file]
Documentation/auxdisplay/cfag12864b
Documentation/auxdisplay/cfag12864b-example.c [deleted file]
Documentation/blackfin/00-INDEX
Documentation/blackfin/Makefile [deleted file]
Documentation/blackfin/gptimers-example.c [deleted file]
Documentation/dev-tools/kmemleak.rst
Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/bridge/tda998x.txt
Documentation/devicetree/bindings/display/msm/hdmi.txt
Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt [new file with mode: 0644]
Documentation/devicetree/bindings/display/rockchip/rockchip-vop.txt
Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt
Documentation/devicetree/bindings/display/tilcdc/tilcdc.txt
Documentation/devicetree/bindings/i2c/i2c.txt
Documentation/devicetree/bindings/i2c/trivial-devices.txt
Documentation/devicetree/bindings/infiniband/hisilicon-hns-roce.txt
Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/pwm-meson.txt [new file with mode: 0644]
Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
Documentation/devicetree/bindings/pwm/pwm-st.txt
Documentation/devicetree/bindings/pwm/pwm-sun4i.txt
Documentation/devicetree/bindings/rtc/dallas,ds1390.txt
Documentation/devicetree/bindings/rtc/epson,rx8900.txt [new file with mode: 0644]
Documentation/devicetree/bindings/rtc/rtc-omap.txt
Documentation/devicetree/bindings/thermal/max77620_thermal.txt [new file with mode: 0644]
Documentation/devicetree/bindings/thermal/mediatek-thermal.txt
Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt
Documentation/devicetree/bindings/thermal/qcom-tsens.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/devicetree/bindings/watchdog/of-xilinx-wdt.txt
Documentation/devicetree/bindings/watchdog/st_lpc_wdt.txt
Documentation/filesystems/.gitignore [deleted file]
Documentation/filesystems/00-INDEX
Documentation/filesystems/Makefile [deleted file]
Documentation/filesystems/autofs4-mount-control.txt
Documentation/filesystems/autofs4.txt
Documentation/filesystems/dnotify_test.c [deleted file]
Documentation/gpu/drm-internals.rst
Documentation/gpu/drm-kms-helpers.rst
Documentation/gpu/drm-kms.rst
Documentation/gpu/drm-mm.rst
Documentation/gpu/drm-uapi.rst
Documentation/gpu/i915.rst
Documentation/gpu/index.rst
Documentation/gpu/kms-properties.csv
Documentation/gpu/vgaarbiter.rst [new file with mode: 0644]
Documentation/ia64/.gitignore [deleted file]
Documentation/ia64/Makefile [deleted file]
Documentation/ia64/aliasing-test.c [deleted file]
Documentation/index.rst
Documentation/input/alps.txt
Documentation/kbuild/makefiles.txt
Documentation/kernel-parameters.txt
Documentation/kselftest.txt
Documentation/laptops/.gitignore [deleted file]
Documentation/laptops/00-INDEX
Documentation/laptops/Makefile [deleted file]
Documentation/laptops/dslm.c [deleted file]
Documentation/laptops/laptop-mode.txt
Documentation/mic/Makefile [deleted file]
Documentation/mic/mpssd/.gitignore [deleted file]
Documentation/mic/mpssd/Makefile [deleted file]
Documentation/mic/mpssd/micctrl [deleted file]
Documentation/mic/mpssd/mpss [deleted file]
Documentation/mic/mpssd/mpssd.c [deleted file]
Documentation/mic/mpssd/mpssd.h [deleted file]
Documentation/mic/mpssd/sysfs.c [deleted file]
Documentation/misc-devices/Makefile [deleted file]
Documentation/misc-devices/mei/.gitignore [deleted file]
Documentation/misc-devices/mei/Makefile [deleted file]
Documentation/misc-devices/mei/TODO [deleted file]
Documentation/misc-devices/mei/mei-amt-version.c [deleted file]
Documentation/networking/00-INDEX
Documentation/networking/Makefile [deleted file]
Documentation/networking/timestamping/.gitignore [deleted file]
Documentation/networking/timestamping/Makefile [deleted file]
Documentation/networking/timestamping/hwtstamp_config.c [deleted file]
Documentation/networking/timestamping/timestamping.c [deleted file]
Documentation/networking/timestamping/txtimestamp.c [deleted file]
Documentation/pcmcia/.gitignore [deleted file]
Documentation/pcmcia/Makefile [deleted file]
Documentation/pcmcia/crc32hash.c [deleted file]
Documentation/pcmcia/devicetable.txt
Documentation/prctl/.gitignore [deleted file]
Documentation/prctl/Makefile [deleted file]
Documentation/prctl/disable-tsc-ctxt-sw-stress-test.c [deleted file]
Documentation/prctl/disable-tsc-on-off-stress-test.c [deleted file]
Documentation/prctl/disable-tsc-test.c [deleted file]
Documentation/ptp/.gitignore [deleted file]
Documentation/ptp/Makefile [deleted file]
Documentation/ptp/testptp.c [deleted file]
Documentation/ptp/testptp.mk [deleted file]
Documentation/scsi/g_NCR5380.txt
Documentation/spi/00-INDEX
Documentation/sync_file.txt
Documentation/thermal/sysfs-api.txt
Documentation/timers/.gitignore [deleted file]
Documentation/timers/00-INDEX
Documentation/timers/Makefile [deleted file]
Documentation/timers/hpet.txt
Documentation/timers/hpet_example.c [deleted file]
Documentation/vDSO/.gitignore [deleted file]
Documentation/vDSO/Makefile [deleted file]
Documentation/vDSO/parse_vdso.c [deleted file]
Documentation/vDSO/vdso_standalone_test_x86.c [deleted file]
Documentation/vDSO/vdso_test.c [deleted file]
Documentation/vgaarbiter.txt [deleted file]
Documentation/watchdog/Makefile [deleted file]
Documentation/watchdog/src/.gitignore [deleted file]
Documentation/watchdog/src/Makefile [deleted file]
Documentation/watchdog/src/watchdog-simple.c [deleted file]
Documentation/watchdog/src/watchdog-test.c [deleted file]
Documentation/watchdog/watchdog-api.txt
Documentation/watchdog/watchdog-kernel-api.txt
Documentation/watchdog/wdt.txt
MAINTAINERS
Makefile
arch/Kconfig
arch/alpha/include/asm/Kbuild
arch/alpha/kernel/Makefile
arch/alpha/kernel/alpha_ksyms.c [deleted file]
arch/alpha/kernel/machvec_impl.h
arch/alpha/kernel/setup.c
arch/alpha/lib/callback_srm.S
arch/alpha/lib/checksum.c
arch/alpha/lib/clear_page.S
arch/alpha/lib/clear_user.S
arch/alpha/lib/copy_page.S
arch/alpha/lib/copy_user.S
arch/alpha/lib/csum_ipv6_magic.S
arch/alpha/lib/csum_partial_copy.c
arch/alpha/lib/dec_and_lock.c
arch/alpha/lib/divide.S
arch/alpha/lib/ev6-clear_page.S
arch/alpha/lib/ev6-clear_user.S
arch/alpha/lib/ev6-copy_page.S
arch/alpha/lib/ev6-copy_user.S
arch/alpha/lib/ev6-csum_ipv6_magic.S
arch/alpha/lib/ev6-divide.S
arch/alpha/lib/ev6-memchr.S
arch/alpha/lib/ev6-memcpy.S
arch/alpha/lib/ev6-memset.S
arch/alpha/lib/ev67-strcat.S
arch/alpha/lib/ev67-strchr.S
arch/alpha/lib/ev67-strlen.S
arch/alpha/lib/ev67-strncat.S
arch/alpha/lib/ev67-strrchr.S
arch/alpha/lib/fpreg.c
arch/alpha/lib/memchr.S
arch/alpha/lib/memcpy.c
arch/alpha/lib/memmove.S
arch/alpha/lib/memset.S
arch/alpha/lib/strcat.S
arch/alpha/lib/strchr.S
arch/alpha/lib/strcpy.S
arch/alpha/lib/strlen.S
arch/alpha/lib/strncat.S
arch/alpha/lib/strncpy.S
arch/alpha/lib/strrchr.S
arch/arm/boot/dts/am335x-boneblack.dts
arch/arm/boot/dts/tegra124-jetson-tk1.dts
arch/arm/boot/dts/tegra124.dtsi
arch/arm/configs/exynos_defconfig
arch/arm/include/asm/Kbuild
arch/arm/include/asm/trusted_foundations.h
arch/arm/kernel/Makefile
arch/arm/kernel/armksyms.c [deleted file]
arch/arm/kernel/entry-ftrace.S
arch/arm/kernel/head.S
arch/arm/kernel/process.c
arch/arm/kernel/smccc-call.S
arch/arm/lib/ashldi3.S
arch/arm/lib/ashrdi3.S
arch/arm/lib/bitops.h
arch/arm/lib/bswapsdi2.S
arch/arm/lib/clear_user.S
arch/arm/lib/copy_from_user.S
arch/arm/lib/copy_page.S
arch/arm/lib/copy_to_user.S
arch/arm/lib/csumipv6.S
arch/arm/lib/csumpartial.S
arch/arm/lib/csumpartialcopy.S
arch/arm/lib/csumpartialcopygeneric.S
arch/arm/lib/csumpartialcopyuser.S
arch/arm/lib/delay.c
arch/arm/lib/div64.S
arch/arm/lib/findbit.S
arch/arm/lib/getuser.S
arch/arm/lib/io-readsb.S
arch/arm/lib/io-readsl.S
arch/arm/lib/io-readsw-armv3.S
arch/arm/lib/io-readsw-armv4.S
arch/arm/lib/io-writesb.S
arch/arm/lib/io-writesl.S
arch/arm/lib/io-writesw-armv3.S
arch/arm/lib/io-writesw-armv4.S
arch/arm/lib/lib1funcs.S
arch/arm/lib/lshrdi3.S
arch/arm/lib/memchr.S
arch/arm/lib/memcpy.S
arch/arm/lib/memmove.S
arch/arm/lib/memset.S
arch/arm/lib/memzero.S
arch/arm/lib/muldi3.S
arch/arm/lib/putuser.S
arch/arm/lib/strchr.S
arch/arm/lib/strrchr.S
arch/arm/lib/uaccess_with_memcpy.c
arch/arm/lib/ucmpdi2.S
arch/arm/mach-imx/Makefile
arch/arm/mach-imx/ssi-fiq-ksym.c [deleted file]
arch/arm/mach-imx/ssi-fiq.S
arch/arm/mm/fault.h
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
arch/arm64/boot/dts/nvidia/tegra132.dtsi
arch/arm64/boot/dts/nvidia/tegra210.dtsi
arch/arm64/include/asm/alternative.h
arch/arm64/kernel/process.c
arch/frv/include/asm/pgtable.h
arch/frv/include/asm/segment.h
arch/frv/include/asm/uaccess.h
arch/ia64/hp/sim/boot/Makefile
arch/ia64/include/asm/export.h [new file with mode: 0644]
arch/ia64/include/asm/libata-portmap.h
arch/ia64/kernel/entry.S
arch/ia64/kernel/esi_stub.S
arch/ia64/kernel/head.S
arch/ia64/kernel/ia64_ksyms.c
arch/ia64/kernel/ivt.S
arch/ia64/kernel/pal.S
arch/ia64/kernel/setup.c
arch/ia64/lib/Makefile
arch/ia64/lib/clear_page.S
arch/ia64/lib/clear_user.S
arch/ia64/lib/copy_page.S
arch/ia64/lib/copy_page_mck.S
arch/ia64/lib/copy_user.S
arch/ia64/lib/flush.S
arch/ia64/lib/idiv32.S
arch/ia64/lib/idiv64.S
arch/ia64/lib/ip_fast_csum.S
arch/ia64/lib/memcpy.S
arch/ia64/lib/memcpy_mck.S
arch/ia64/lib/memset.S
arch/ia64/lib/strlen.S
arch/ia64/lib/strlen_user.S
arch/ia64/lib/strncpy_from_user.S
arch/ia64/lib/strnlen_user.S
arch/ia64/lib/xor.S
arch/m68k/include/asm/export.h [new file with mode: 0644]
arch/m68k/include/asm/uaccess_no.h
arch/m68k/kernel/Makefile
arch/m68k/kernel/m68k_ksyms.c [deleted file]
arch/m68k/lib/ashldi3.c
arch/m68k/lib/ashrdi3.c
arch/m68k/lib/divsi3.S
arch/m68k/lib/lshrdi3.c
arch/m68k/lib/modsi3.S
arch/m68k/lib/muldi3.c
arch/m68k/lib/mulsi3.S
arch/m68k/lib/udivsi3.S
arch/m68k/lib/umodsi3.S
arch/metag/include/asm/atomic.h
arch/microblaze/include/asm/uaccess.h
arch/mips/cavium-octeon/setup.c
arch/mips/include/asm/extable.h [new file with mode: 0644]
arch/mips/include/asm/kexec.h
arch/mips/include/asm/mach-loongson64/loongson.h
arch/mips/include/asm/module.h
arch/mips/include/asm/uaccess.h
arch/mips/include/uapi/asm/unistd.h
arch/mips/kernel/crash.c
arch/mips/kernel/machine_kexec.c
arch/mips/kernel/scall32-o32.S
arch/mips/kernel/scall64-64.S
arch/mips/kernel/scall64-n32.S
arch/mips/kernel/scall64-o32.S
arch/mips/lasat/picvue_proc.c
arch/mips/math-emu/cp1emu.c
arch/mips/net/bpf_jit.c
arch/mn10300/include/asm/processor.h
arch/mn10300/include/asm/uaccess.h
arch/mn10300/kernel/signal.c
arch/openrisc/include/asm/uaccess.h
arch/parisc/include/asm/pgtable.h
arch/parisc/include/asm/traps.h
arch/parisc/kernel/traps.c
arch/parisc/kernel/vmlinux.lds.S
arch/parisc/mm/fault.c
arch/parisc/mm/init.c
arch/powerpc/Makefile
arch/powerpc/configs/dpaa.config [new file with mode: 0644]
arch/powerpc/include/asm/Kbuild
arch/powerpc/include/asm/cputable.h
arch/powerpc/include/asm/hw_irq.h
arch/powerpc/include/asm/libata-portmap.h
arch/powerpc/include/asm/ppc-opcode.h
arch/powerpc/include/asm/reg.h
arch/powerpc/include/asm/reg_8xx.h
arch/powerpc/kernel/Makefile
arch/powerpc/kernel/cputable.c
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/entry_64.S
arch/powerpc/kernel/epapr_hcalls.S
arch/powerpc/kernel/exceptions-64s.S
arch/powerpc/kernel/fpu.S
arch/powerpc/kernel/head_32.S
arch/powerpc/kernel/head_40x.S
arch/powerpc/kernel/head_44x.S
arch/powerpc/kernel/head_64.S
arch/powerpc/kernel/head_8xx.S
arch/powerpc/kernel/head_fsl_booke.S
arch/powerpc/kernel/iommu.c
arch/powerpc/kernel/misc.S
arch/powerpc/kernel/misc_32.S
arch/powerpc/kernel/misc_64.S
arch/powerpc/kernel/pci-common.c
arch/powerpc/kernel/pci_32.c
arch/powerpc/kernel/ppc_ksyms.c [deleted file]
arch/powerpc/kernel/ppc_ksyms_32.c [deleted file]
arch/powerpc/kernel/setup-common.c
arch/powerpc/kernel/setup_32.c
arch/powerpc/kernel/time.c
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/vector.S
arch/powerpc/lib/Makefile
arch/powerpc/lib/checksum_32.S
arch/powerpc/lib/checksum_64.S
arch/powerpc/lib/copy_32.S
arch/powerpc/lib/copypage_64.S
arch/powerpc/lib/copyuser_64.S
arch/powerpc/lib/hweight_64.S
arch/powerpc/lib/mem_64.S
arch/powerpc/lib/memcmp_64.S
arch/powerpc/lib/memcpy_64.S
arch/powerpc/lib/ppc_ksyms.c [deleted file]
arch/powerpc/lib/string.S
arch/powerpc/lib/string_64.S
arch/powerpc/mm/hash_low_32.S
arch/powerpc/mm/hash_utils_64.c
arch/powerpc/platforms/82xx/Kconfig
arch/powerpc/platforms/82xx/ep8248e.c
arch/powerpc/platforms/83xx/asp834x.c
arch/powerpc/platforms/83xx/km83xx.c
arch/powerpc/platforms/83xx/misc.c
arch/powerpc/platforms/83xx/mpc830x_rdb.c
arch/powerpc/platforms/83xx/mpc831x_rdb.c
arch/powerpc/platforms/83xx/mpc832x_mds.c
arch/powerpc/platforms/83xx/mpc832x_rdb.c
arch/powerpc/platforms/83xx/mpc834x_itx.c
arch/powerpc/platforms/83xx/mpc834x_mds.c
arch/powerpc/platforms/83xx/mpc836x_mds.c
arch/powerpc/platforms/83xx/mpc836x_rdk.c
arch/powerpc/platforms/83xx/mpc837x_mds.c
arch/powerpc/platforms/83xx/mpc837x_rdb.c
arch/powerpc/platforms/83xx/mpc83xx.h
arch/powerpc/platforms/83xx/sbc834x.c
arch/powerpc/platforms/85xx/Kconfig
arch/powerpc/platforms/85xx/bsc913x_qds.c
arch/powerpc/platforms/85xx/bsc913x_rdb.c
arch/powerpc/platforms/85xx/c293pcie.c
arch/powerpc/platforms/85xx/corenet_generic.c
arch/powerpc/platforms/85xx/ge_imp3a.c
arch/powerpc/platforms/85xx/mpc8536_ds.c
arch/powerpc/platforms/85xx/mpc85xx_ads.c
arch/powerpc/platforms/85xx/mpc85xx_cds.c
arch/powerpc/platforms/85xx/mpc85xx_ds.c
arch/powerpc/platforms/85xx/mpc85xx_mds.c
arch/powerpc/platforms/85xx/mpc85xx_rdb.c
arch/powerpc/platforms/85xx/mvme2500.c
arch/powerpc/platforms/85xx/p1010rdb.c
arch/powerpc/platforms/85xx/p1022_ds.c
arch/powerpc/platforms/85xx/p1022_rdk.c
arch/powerpc/platforms/85xx/p1023_rdb.c
arch/powerpc/platforms/85xx/ppa8548.c
arch/powerpc/platforms/85xx/qemu_e500.c
arch/powerpc/platforms/85xx/sbc8548.c
arch/powerpc/platforms/85xx/sgy_cts1000.c
arch/powerpc/platforms/85xx/socrates.c
arch/powerpc/platforms/85xx/stx_gp3.c
arch/powerpc/platforms/85xx/tqm85xx.c
arch/powerpc/platforms/85xx/twr_p102x.c
arch/powerpc/platforms/85xx/xes_mpc85xx.c
arch/powerpc/platforms/86xx/gef_ppc9a.c
arch/powerpc/platforms/86xx/gef_sbc310.c
arch/powerpc/platforms/86xx/gef_sbc610.c
arch/powerpc/platforms/86xx/mpc8610_hpcd.c
arch/powerpc/platforms/86xx/mpc86xx_hpcn.c
arch/powerpc/platforms/86xx/mvme7100.c
arch/powerpc/platforms/86xx/sbc8641d.c
arch/powerpc/platforms/pseries/lpar.c
arch/powerpc/relocs_check.sh
arch/powerpc/sysdev/cpm1.c
arch/powerpc/sysdev/cpm2.c
arch/powerpc/sysdev/cpm_common.c
arch/powerpc/sysdev/dcr-low.S
arch/powerpc/sysdev/fsl_pci.c
arch/powerpc/sysdev/fsl_soc.c
arch/powerpc/sysdev/fsl_soc.h
arch/powerpc/sysdev/mpic.c
arch/s390/include/asm/Kbuild
arch/s390/kernel/Makefile
arch/s390/kernel/entry.S
arch/s390/kernel/mcount.S
arch/s390/kernel/s390_ksyms.c [deleted file]
arch/s390/lib/mem.S
arch/score/include/asm/extable.h [new file with mode: 0644]
arch/score/include/asm/module.h
arch/score/include/asm/uaccess.h
arch/sh/include/asm/uaccess.h
arch/sparc/include/asm/Kbuild
arch/sparc/include/asm/elf_64.h
arch/sparc/include/asm/extable_64.h [new file with mode: 0644]
arch/sparc/include/asm/string.h
arch/sparc/include/asm/string_32.h
arch/sparc/include/asm/string_64.h
arch/sparc/include/asm/uaccess_64.h
arch/sparc/kernel/Makefile
arch/sparc/kernel/entry.S
arch/sparc/kernel/head_32.S
arch/sparc/kernel/head_64.S
arch/sparc/kernel/helpers.S
arch/sparc/kernel/hvcalls.S
arch/sparc/kernel/sparc_ksyms.c [new file with mode: 0644]
arch/sparc/kernel/sparc_ksyms_32.c [deleted file]
arch/sparc/kernel/sparc_ksyms_64.c [deleted file]
arch/sparc/lib/Makefile
arch/sparc/lib/U1memcpy.S
arch/sparc/lib/VISsave.S
arch/sparc/lib/ashldi3.S
arch/sparc/lib/ashrdi3.S
arch/sparc/lib/atomic_64.S
arch/sparc/lib/bitops.S
arch/sparc/lib/blockops.S
arch/sparc/lib/bzero.S
arch/sparc/lib/checksum_32.S
arch/sparc/lib/checksum_64.S
arch/sparc/lib/clear_page.S
arch/sparc/lib/copy_in_user.S
arch/sparc/lib/copy_page.S
arch/sparc/lib/copy_user.S
arch/sparc/lib/csum_copy.S
arch/sparc/lib/divdi3.S
arch/sparc/lib/ffs.S
arch/sparc/lib/hweight.S
arch/sparc/lib/ipcsum.S
arch/sparc/lib/ksyms.c [deleted file]
arch/sparc/lib/locks.S
arch/sparc/lib/lshrdi3.S
arch/sparc/lib/mcount.S
arch/sparc/lib/memcmp.S
arch/sparc/lib/memcpy.S
arch/sparc/lib/memmove.S
arch/sparc/lib/memscan_32.S
arch/sparc/lib/memscan_64.S
arch/sparc/lib/memset.S
arch/sparc/lib/muldi3.S
arch/sparc/lib/strlen.S
arch/sparc/lib/strncmp_32.S
arch/sparc/lib/strncmp_64.S
arch/sparc/lib/xor.S
arch/tile/mm/mmap.c
arch/unicore32/kernel/process.c
arch/x86/entry/entry_32.S
arch/x86/entry/entry_64.S
arch/x86/entry/thunk_32.S
arch/x86/entry/thunk_64.S
arch/x86/include/asm/cacheflush.h
arch/x86/include/asm/export.h [new file with mode: 0644]
arch/x86/include/asm/extable.h [new file with mode: 0644]
arch/x86/include/asm/kexec.h
arch/x86/include/asm/percpu.h
arch/x86/include/asm/sections.h
arch/x86/include/asm/smp.h
arch/x86/include/asm/uaccess.h
arch/x86/kernel/Makefile
arch/x86/kernel/crash.c
arch/x86/kernel/early-quirks.c
arch/x86/kernel/head_32.S
arch/x86/kernel/head_64.S
arch/x86/kernel/i386_ksyms_32.c [deleted file]
arch/x86/kernel/machine_kexec_64.c
arch/x86/kernel/mcount_64.S
arch/x86/kernel/process.c
arch/x86/kernel/smp.c
arch/x86/kernel/sys_x86_64.c
arch/x86/kernel/x8664_ksyms_64.c [deleted file]
arch/x86/kvm/i8254.c
arch/x86/lib/checksum_32.S
arch/x86/lib/clear_page_64.S
arch/x86/lib/cmpxchg8b_emu.S
arch/x86/lib/copy_page_64.S
arch/x86/lib/copy_user_64.S
arch/x86/lib/csum-partial_64.c
arch/x86/lib/getuser.S
arch/x86/lib/hweight.S
arch/x86/lib/memcpy_64.S
arch/x86/lib/memmove_64.S
arch/x86/lib/memset_64.S
arch/x86/lib/putuser.S
arch/x86/lib/strstr_32.c
arch/x86/mm/fault.c
arch/x86/um/Makefile
arch/x86/um/checksum_32.S
arch/x86/um/ksyms.c [deleted file]
arch/xtensa/include/asm/asm-uaccess.h [new file with mode: 0644]
arch/xtensa/include/asm/uaccess.h
arch/xtensa/kernel/coprocessor.S
arch/xtensa/kernel/entry.S
block/blk-cgroup.c
block/blk-lib.c
block/ioctl.c
crypto/crypto_engine.c
drivers/acpi/acpi_pad.c
drivers/acpi/ec.c
drivers/acpi/fan.c
drivers/acpi/osl.c
drivers/acpi/property.c
drivers/acpi/thermal.c
drivers/ata/ahci.c
drivers/ata/ahci.h
drivers/ata/ahci_qoriq.c
drivers/ata/ahci_st.c
drivers/ata/libahci.c
drivers/ata/libata-scsi.c
drivers/ata/pata_at91.c
drivers/ata/pata_octeon_cf.c
drivers/ata/sata_mv.c
drivers/block/loop.c
drivers/char/agp/intel-gtt.c
drivers/char/random.c
drivers/char/tb0219.c
drivers/char/virtio_console.c
drivers/cpufreq/cppc_cpufreq.c
drivers/cpufreq/cpufreq_conservative.c
drivers/cpufreq/intel_pstate.c
drivers/devfreq/devfreq.c
drivers/devfreq/event/Kconfig
drivers/devfreq/event/exynos-nocp.c
drivers/dma-buf/dma-buf.c
drivers/dma-buf/fence-array.c
drivers/dma-buf/reservation.c
drivers/dma-buf/sync_debug.c
drivers/dma-buf/sync_file.c
drivers/gpio/gpio-pca953x.c
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/amd/amdgpu/Kconfig
drivers/gpu/drm/amd/amdgpu/Makefile
drivers/gpu/drm/amd/amdgpu/ObjectID.h
drivers/gpu/drm/amd/amdgpu/amdgpu.h
drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c
drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h
drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c
drivers/gpu/drm/amd/amdgpu/amdgpu_benchmark.c
drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c
drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c
drivers/gpu/drm/amd/amdgpu/amdgpu_gart.c
drivers/gpu/drm/amd/amdgpu/amdgpu_gds.h
drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.c
drivers/gpu/drm/amd/amdgpu/amdgpu_i2c.h
drivers/gpu/drm/amd/amdgpu/amdgpu_ib.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ih.c
drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
drivers/gpu/drm/amd/amdgpu/amdgpu_object.h
drivers/gpu/drm/amd/amdgpu/amdgpu_pll.c
drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_powerplay.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c
drivers/gpu/drm/amd/amdgpu/amdgpu_test.c
drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/amdgpu_ucode.c
drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
drivers/gpu/drm/amd/amdgpu/amdgpu_vce.h
drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drivers/gpu/drm/amd/amdgpu/atombios_crtc.c
drivers/gpu/drm/amd/amdgpu/atombios_dp.c
drivers/gpu/drm/amd/amdgpu/atombios_i2c.c
drivers/gpu/drm/amd/amdgpu/ci_dpm.c
drivers/gpu/drm/amd/amdgpu/cik.c
drivers/gpu/drm/amd/amdgpu/cik_sdma.c
drivers/gpu/drm/amd/amdgpu/cikd.h
drivers/gpu/drm/amd/amdgpu/cz_dpm.c
drivers/gpu/drm/amd/amdgpu/cz_smc.c
drivers/gpu/drm/amd/amdgpu/dce_v10_0.c
drivers/gpu/drm/amd/amdgpu/dce_v10_0.h
drivers/gpu/drm/amd/amdgpu/dce_v11_0.c
drivers/gpu/drm/amd/amdgpu/dce_v11_0.h
drivers/gpu/drm/amd/amdgpu/dce_v6_0.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/dce_v6_0.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/dce_v8_0.c
drivers/gpu/drm/amd/amdgpu/dce_v8_0.h
drivers/gpu/drm/amd/amdgpu/dce_virtual.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/dce_virtual.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/fiji_dpm.c [deleted file]
drivers/gpu/drm/amd/amdgpu/fiji_smc.c [deleted file]
drivers/gpu/drm/amd/amdgpu/fiji_smum.h [deleted file]
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/gfx_v6_0.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v8_0.h
drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/gmc_v6_0.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c
drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c
drivers/gpu/drm/amd/amdgpu/iceland_dpm.c [deleted file]
drivers/gpu/drm/amd/amdgpu/iceland_smc.c [deleted file]
drivers/gpu/drm/amd/amdgpu/iceland_smum.h [deleted file]
drivers/gpu/drm/amd/amdgpu/kv_dpm.c
drivers/gpu/drm/amd/amdgpu/r600_dpm.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/sdma_v2_4.c
drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c
drivers/gpu/drm/amd/amdgpu/si.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_dma.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_dma.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_dpm.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_dpm.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_ih.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_ih.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/si_smc.c [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/sislands_smc.h [new file with mode: 0644]
drivers/gpu/drm/amd/amdgpu/tonga_dpm.c [deleted file]
drivers/gpu/drm/amd/amdgpu/tonga_ih.c
drivers/gpu/drm/amd/amdgpu/tonga_smc.c [deleted file]
drivers/gpu/drm/amd/amdgpu/tonga_smum.h [deleted file]
drivers/gpu/drm/amd/amdgpu/uvd_v4_2.c
drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c
drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
drivers/gpu/drm/amd/amdgpu/vce_v2_0.c
drivers/gpu/drm/amd/amdgpu/vce_v3_0.c
drivers/gpu/drm/amd/amdgpu/vi.c
drivers/gpu/drm/amd/amdgpu/vid.h
drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
drivers/gpu/drm/amd/amdkfd/kfd_kernel_queue.c
drivers/gpu/drm/amd/amdkfd/kfd_priv.h
drivers/gpu/drm/amd/amdkfd/kfd_process.c
drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
drivers/gpu/drm/amd/amdkfd/kfd_queue.c
drivers/gpu/drm/amd/amdkfd/kfd_topology.c
drivers/gpu/drm/amd/include/amd_shared.h
drivers/gpu/drm/amd/include/asic_reg/si/clearstate_si.h [new file with mode: 0644]
drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h [new file with mode: 0644]
drivers/gpu/drm/amd/include/asic_reg/si/sid.h [new file with mode: 0644]
drivers/gpu/drm/amd/include/asic_reg/uvd/uvd_4_2_d.h
drivers/gpu/drm/amd/include/asic_reg/uvd/uvd_5_0_d.h
drivers/gpu/drm/amd/include/asic_reg/uvd/uvd_6_0_d.h
drivers/gpu/drm/amd/include/atombios.h
drivers/gpu/drm/amd/include/cgs_common.h [changed mode: 0644->0755]
drivers/gpu/drm/amd/powerplay/Kconfig [deleted file]
drivers/gpu/drm/amd/powerplay/amd_powerplay.c
drivers/gpu/drm/amd/powerplay/eventmgr/eventactionchains.c
drivers/gpu/drm/amd/powerplay/eventmgr/psm.c
drivers/gpu/drm/amd/powerplay/hwmgr/Makefile
drivers/gpu/drm/amd/powerplay/hwmgr/cz_hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_dyn_defaults.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c
drivers/gpu/drm/amd/powerplay/hwmgr/hwmgr.c
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_dyn_defaults.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/ppatomctrl.c
drivers/gpu/drm/amd/powerplay/hwmgr/pptable_v1_0.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/processpptables.c
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_dyn_defaults.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_dyn_defaults.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_powertune.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_pptable.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.h [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.c [deleted file]
drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.h [deleted file]
drivers/gpu/drm/amd/powerplay/inc/amd_powerplay.h
drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h
drivers/gpu/drm/amd/powerplay/inc/hwmgr.h
drivers/gpu/drm/amd/powerplay/inc/polaris10_pwrvirus.h
drivers/gpu/drm/amd/powerplay/inc/power_state.h
drivers/gpu/drm/amd/powerplay/inc/pp_debug.h
drivers/gpu/drm/amd/powerplay/inc/smu71.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/inc/smu71_discrete.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/inc/smu7_common.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/inc/smumgr.h
drivers/gpu/drm/amd/powerplay/smumgr/Makefile
drivers/gpu/drm/amd/powerplay/smumgr/cz_smumgr.c
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.c [changed mode: 0644->0755]
drivers/gpu/drm/amd/powerplay/smumgr/fiji_smumgr.h
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.c
drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smumgr.h
drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/smumgr.c
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.h [new file with mode: 0644]
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.c
drivers/gpu/drm/amd/powerplay/smumgr/tonga_smumgr.h
drivers/gpu/drm/arc/arcpgu_crtc.c
drivers/gpu/drm/arc/arcpgu_drv.c
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/arm/malidp_drv.h
drivers/gpu/drm/arm/malidp_planes.c
drivers/gpu/drm/armada/armada_drv.c
drivers/gpu/drm/armada/armada_fbdev.c
drivers/gpu/drm/armada/armada_gem.c
drivers/gpu/drm/armada/armada_overlay.c
drivers/gpu/drm/ast/ast_fb.c
drivers/gpu/drm/ast/ast_ttm.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
drivers/gpu/drm/bochs/bochs.h
drivers/gpu/drm/bochs/bochs_drv.c
drivers/gpu/drm/bochs/bochs_kms.c
drivers/gpu/drm/bochs/bochs_mm.c
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/Makefile
drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
drivers/gpu/drm/bridge/adv7511/adv7533.c
drivers/gpu/drm/bridge/analogix-anx78xx.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h
drivers/gpu/drm/bridge/dumb-vga-dac.c [new file with mode: 0644]
drivers/gpu/drm/bridge/dw-hdmi-ahb-audio.c
drivers/gpu/drm/bridge/dw-hdmi.c
drivers/gpu/drm/bridge/nxp-ptn3460.c
drivers/gpu/drm/bridge/parade-ps8622.c
drivers/gpu/drm/bridge/tc358767.c
drivers/gpu/drm/cirrus/cirrus_drv.c
drivers/gpu/drm/cirrus/cirrus_fbdev.c
drivers/gpu/drm/cirrus/cirrus_ttm.c
drivers/gpu/drm/drm_agpsupport.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_auth.c
drivers/gpu/drm/drm_blend.c
drivers/gpu/drm/drm_bridge.c
drivers/gpu/drm/drm_bufs.c
drivers/gpu/drm/drm_color_mgmt.c [new file with mode: 0644]
drivers/gpu/drm/drm_connector.c [new file with mode: 0644]
drivers/gpu/drm/drm_context.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_crtc_helper_internal.h [new file with mode: 0644]
drivers/gpu/drm/drm_crtc_internal.h
drivers/gpu/drm/drm_dma.c
drivers/gpu/drm/drm_dp_aux_dev.c
drivers/gpu/drm/drm_dp_helper.c
drivers/gpu/drm/drm_drv.c
drivers/gpu/drm/drm_edid.c
drivers/gpu/drm/drm_encoder.c [new file with mode: 0644]
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_fourcc.c
drivers/gpu/drm/drm_framebuffer.c [new file with mode: 0644]
drivers/gpu/drm/drm_gem.c
drivers/gpu/drm/drm_global.c
drivers/gpu/drm/drm_hashtab.c
drivers/gpu/drm/drm_info.c
drivers/gpu/drm/drm_internal.h
drivers/gpu/drm/drm_ioc32.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_irq.c
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/drm_lock.c
drivers/gpu/drm/drm_mipi_dsi.c
drivers/gpu/drm/drm_mm.c
drivers/gpu/drm/drm_mode_object.c [new file with mode: 0644]
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_modeset_helper.c [new file with mode: 0644]
drivers/gpu/drm/drm_pci.c
drivers/gpu/drm/drm_plane.c [new file with mode: 0644]
drivers/gpu/drm/drm_plane_helper.c
drivers/gpu/drm/drm_platform.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_probe_helper.c
drivers/gpu/drm/drm_property.c [new file with mode: 0644]
drivers/gpu/drm/drm_rect.c
drivers/gpu/drm/drm_scatter.c
drivers/gpu/drm/drm_simple_kms_helper.c
drivers/gpu/drm/drm_sysfs.c
drivers/gpu/drm/drm_vma_manager.c
drivers/gpu/drm/etnaviv/etnaviv_buffer.c
drivers/gpu/drm/etnaviv/etnaviv_drv.c
drivers/gpu/drm/etnaviv/etnaviv_drv.h
drivers/gpu/drm/etnaviv/etnaviv_dump.c
drivers/gpu/drm/etnaviv/etnaviv_gpu.c
drivers/gpu/drm/etnaviv/etnaviv_gpu.h
drivers/gpu/drm/etnaviv/etnaviv_iommu.c
drivers/gpu/drm/etnaviv/etnaviv_iommu.h
drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.c
drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h [deleted file]
drivers/gpu/drm/etnaviv/etnaviv_mmu.c
drivers/gpu/drm/etnaviv/etnaviv_mmu.h
drivers/gpu/drm/etnaviv/state_hi.xml.h
drivers/gpu/drm/exynos/exynos5433_drm_decon.c
drivers/gpu/drm/exynos/exynos7_drm_decon.c
drivers/gpu/drm/exynos/exynos_dp.c
drivers/gpu/drm/exynos/exynos_drm_crtc.c
drivers/gpu/drm/exynos/exynos_drm_drv.c
drivers/gpu/drm/exynos/exynos_drm_drv.h
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_g2d.c
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
drivers/gpu/drm/fsl-dcu/fsl_tcon.c
drivers/gpu/drm/gma500/accel_2d.c
drivers/gpu/drm/gma500/cdv_intel_lvds.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/mdfld_dsi_output.c
drivers/gpu/drm/gma500/opregion.c
drivers/gpu/drm/gma500/psb_intel_lvds.c
drivers/gpu/drm/gma500/psb_intel_modes.c
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c
drivers/gpu/drm/i2c/Kconfig
drivers/gpu/drm/i2c/tda998x_drv.c
drivers/gpu/drm/i810/i810_drv.c
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_cmd_parser.c
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_gem.c
drivers/gpu/drm/i915/i915_gem_batch_pool.c
drivers/gpu/drm/i915/i915_gem_batch_pool.h
drivers/gpu/drm/i915/i915_gem_context.c
drivers/gpu/drm/i915/i915_gem_debug.c [deleted file]
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/i915/i915_gem_evict.c
drivers/gpu/drm/i915/i915_gem_execbuffer.c
drivers/gpu/drm/i915/i915_gem_fence.c
drivers/gpu/drm/i915/i915_gem_gtt.c
drivers/gpu/drm/i915/i915_gem_gtt.h
drivers/gpu/drm/i915/i915_gem_render_state.c
drivers/gpu/drm/i915/i915_gem_render_state.h
drivers/gpu/drm/i915/i915_gem_request.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_gem_request.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_gem_shrinker.c
drivers/gpu/drm/i915/i915_gem_stolen.c
drivers/gpu/drm/i915/i915_gem_tiling.c
drivers/gpu/drm/i915/i915_gem_userptr.c
drivers/gpu/drm/i915/i915_gpu_error.c
drivers/gpu/drm/i915/i915_guc_reg.h
drivers/gpu/drm/i915/i915_guc_submission.c
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_memcpy.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_mm.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_params.c
drivers/gpu/drm/i915/i915_params.h
drivers/gpu/drm/i915/i915_pci.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_sw_fence.c [new file with mode: 0644]
drivers/gpu/drm/i915/i915_sw_fence.h [new file with mode: 0644]
drivers/gpu/drm/i915/i915_sysfs.c
drivers/gpu/drm/i915/i915_trace.h
drivers/gpu/drm/i915/i915_vgpu.c
drivers/gpu/drm/i915/intel_atomic_plane.c
drivers/gpu/drm/i915/intel_audio.c
drivers/gpu/drm/i915/intel_breadcrumbs.c
drivers/gpu/drm/i915/intel_color.c
drivers/gpu/drm/i915/intel_crt.c
drivers/gpu/drm/i915/intel_csr.c
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_device_info.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_dp_link_training.c
drivers/gpu/drm/i915/intel_dp_mst.c
drivers/gpu/drm/i915/intel_dpll_mgr.c
drivers/gpu/drm/i915/intel_dpll_mgr.h
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_dsi.c
drivers/gpu/drm/i915/intel_dvo.c
drivers/gpu/drm/i915/intel_engine_cs.c [new file with mode: 0644]
drivers/gpu/drm/i915/intel_fbc.c
drivers/gpu/drm/i915/intel_fbdev.c
drivers/gpu/drm/i915/intel_frontbuffer.c
drivers/gpu/drm/i915/intel_frontbuffer.h [new file with mode: 0644]
drivers/gpu/drm/i915/intel_guc.h
drivers/gpu/drm/i915/intel_guc_fwif.h
drivers/gpu/drm/i915/intel_guc_loader.c
drivers/gpu/drm/i915/intel_hdmi.c
drivers/gpu/drm/i915/intel_hotplug.c
drivers/gpu/drm/i915/intel_i2c.c
drivers/gpu/drm/i915/intel_lrc.c
drivers/gpu/drm/i915/intel_lrc.h
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_mocs.c
drivers/gpu/drm/i915/intel_mocs.h
drivers/gpu/drm/i915/intel_modes.c
drivers/gpu/drm/i915/intel_overlay.c
drivers/gpu/drm/i915/intel_panel.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/i915/intel_psr.c
drivers/gpu/drm/i915/intel_renderstate.h
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/i915/intel_ringbuffer.h
drivers/gpu/drm/i915/intel_runtime_pm.c
drivers/gpu/drm/i915/intel_sdvo.c
drivers/gpu/drm/i915/intel_sprite.c
drivers/gpu/drm/i915/intel_tv.c
drivers/gpu/drm/i915/intel_uncore.c
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/imx/imx-drm.h
drivers/gpu/drm/imx/imx-ldb.c
drivers/gpu/drm/imx/imx-tve.c
drivers/gpu/drm/imx/ipuv3-crtc.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/imx/parallel-display.c
drivers/gpu/drm/mediatek/mtk_disp_ovl.c
drivers/gpu/drm/mediatek/mtk_disp_rdma.c
drivers/gpu/drm/mediatek/mtk_drm_crtc.c
drivers/gpu/drm/mediatek/mtk_drm_crtc.h
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
drivers/gpu/drm/mediatek/mtk_drm_drv.c
drivers/gpu/drm/mediatek/mtk_drm_plane.c
drivers/gpu/drm/mediatek/mtk_drm_plane.h
drivers/gpu/drm/mediatek/mtk_hdmi.c
drivers/gpu/drm/mga/mga_drv.c
drivers/gpu/drm/mgag200/mgag200_drv.c
drivers/gpu/drm/mgag200/mgag200_fb.c
drivers/gpu/drm/mgag200/mgag200_main.c
drivers/gpu/drm/mgag200/mgag200_ttm.c
drivers/gpu/drm/msm/Kconfig
drivers/gpu/drm/msm/hdmi/hdmi.c
drivers/gpu/drm/msm/hdmi/hdmi_i2c.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_lcdc_encoder.c
drivers/gpu/drm/msm/mdp/mdp4/mdp4_plane.c
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/msm_atomic.c
drivers/gpu/drm/msm/msm_drv.c
drivers/gpu/drm/msm/msm_gem.c
drivers/gpu/drm/msm/msm_gem_submit.c
drivers/gpu/drm/msm/msm_gpu.c
drivers/gpu/drm/msm/msm_gpu.h
drivers/gpu/drm/nouveau/nouveau_bo.c
drivers/gpu/drm/nouveau/nouveau_drm.c
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c
drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c
drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c
drivers/gpu/drm/omapdrm/dss/dss-of.c
drivers/gpu/drm/omapdrm/omap_drv.c
drivers/gpu/drm/omapdrm/omap_fb.c
drivers/gpu/drm/omapdrm/omap_plane.c
drivers/gpu/drm/panel/Kconfig
drivers/gpu/drm/panel/Makefile
drivers/gpu/drm/panel/panel-jdi-lt070me05000.c [new file with mode: 0644]
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/qxl/qxl_draw.c
drivers/gpu/drm/qxl/qxl_drv.h
drivers/gpu/drm/qxl/qxl_fb.c
drivers/gpu/drm/qxl/qxl_object.c
drivers/gpu/drm/qxl/qxl_release.c
drivers/gpu/drm/qxl/qxl_ttm.c
drivers/gpu/drm/r128/r128_drv.c
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/atombios_dp.c
drivers/gpu/drm/radeon/cik.c
drivers/gpu/drm/radeon/cikd.h
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/evergreend.h
drivers/gpu/drm/radeon/ni.c
drivers/gpu/drm/radeon/nid.h
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/r600.c
drivers/gpu/drm/radeon/r600d.h
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_acpi.c
drivers/gpu/drm/radeon/radeon_atpx_handler.c
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_dp_auxch.c
drivers/gpu/drm/radeon/radeon_drv.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_i2c.c
drivers/gpu/drm/radeon/radeon_kms.c
drivers/gpu/drm/radeon/radeon_ttm.c
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/radeon/rv770d.h
drivers/gpu/drm/radeon/si.c
drivers/gpu/drm/radeon/si_dpm.c
drivers/gpu/drm/radeon/sid.h
drivers/gpu/drm/radeon/sislands_smc.h
drivers/gpu/drm/rcar-du/rcar_du_drv.c
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/gpu/drm/rockchip/Makefile
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.c
drivers/gpu/drm/rockchip/rockchip_drm_drv.h
drivers/gpu/drm/rockchip/rockchip_drm_fb.c
drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c
drivers/gpu/drm/rockchip/rockchip_drm_psr.c [new file with mode: 0644]
drivers/gpu/drm/rockchip/rockchip_drm_psr.h [new file with mode: 0644]
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.h
drivers/gpu/drm/rockchip/rockchip_vop_reg.c
drivers/gpu/drm/rockchip/rockchip_vop_reg.h
drivers/gpu/drm/savage/savage_drv.c
drivers/gpu/drm/savage/savage_state.c
drivers/gpu/drm/sis/sis_drv.c
drivers/gpu/drm/sti/Kconfig
drivers/gpu/drm/sti/Makefile
drivers/gpu/drm/sti/sti_compositor.c
drivers/gpu/drm/sti/sti_compositor.h
drivers/gpu/drm/sti/sti_crtc.c
drivers/gpu/drm/sti/sti_cursor.c
drivers/gpu/drm/sti/sti_drv.c
drivers/gpu/drm/sti/sti_dvo.c
drivers/gpu/drm/sti/sti_gdp.c
drivers/gpu/drm/sti/sti_hda.c
drivers/gpu/drm/sti/sti_hdmi.c
drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c [deleted file]
drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h [deleted file]
drivers/gpu/drm/sti/sti_hqvdp.c
drivers/gpu/drm/sti/sti_mixer.c
drivers/gpu/drm/sti/sti_tvout.c
drivers/gpu/drm/sti/sti_vid.c
drivers/gpu/drm/sti/sti_vtac.c
drivers/gpu/drm/sti/sti_vtg.c
drivers/gpu/drm/sun4i/Makefile
drivers/gpu/drm/sun4i/sun4i_backend.c
drivers/gpu/drm/sun4i/sun4i_backend.h
drivers/gpu/drm/sun4i/sun4i_dotclock.c
drivers/gpu/drm/sun4i/sun4i_drv.c
drivers/gpu/drm/sun4i/sun4i_framebuffer.c
drivers/gpu/drm/sun4i/sun4i_layer.c
drivers/gpu/drm/sun4i/sun4i_rgb.c
drivers/gpu/drm/sun4i/sun4i_tcon.c
drivers/gpu/drm/sun4i/sun4i_tcon.h
drivers/gpu/drm/sun4i/sun4i_tv.c
drivers/gpu/drm/sun4i/sun6i_drc.c [new file with mode: 0644]
drivers/gpu/drm/tdfx/tdfx_drv.c
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/gem.c
drivers/gpu/drm/tilcdc/Makefile
drivers/gpu/drm/tilcdc/tilcdc_crtc.c
drivers/gpu/drm/tilcdc/tilcdc_drv.c
drivers/gpu/drm/tilcdc/tilcdc_drv.h
drivers/gpu/drm/tilcdc/tilcdc_external.c
drivers/gpu/drm/tilcdc/tilcdc_external.h
drivers/gpu/drm/tilcdc/tilcdc_panel.c
drivers/gpu/drm/tilcdc/tilcdc_plane.c [new file with mode: 0644]
drivers/gpu/drm/tilcdc/tilcdc_regs.h
drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c
drivers/gpu/drm/tilcdc/tilcdc_tfp410.c
drivers/gpu/drm/ttm/ttm_bo.c
drivers/gpu/drm/ttm/ttm_bo_util.c
drivers/gpu/drm/ttm/ttm_memory.c
drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
drivers/gpu/drm/ttm/ttm_tt.c
drivers/gpu/drm/udl/udl_connector.c
drivers/gpu/drm/udl/udl_dmabuf.c
drivers/gpu/drm/udl/udl_drv.c
drivers/gpu/drm/udl/udl_drv.h
drivers/gpu/drm/udl/udl_main.c
drivers/gpu/drm/udl/udl_modeset.c
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_dpi.c
drivers/gpu/drm/vc4/vc4_drv.c
drivers/gpu/drm/vc4/vc4_drv.h
drivers/gpu/drm/vc4/vc4_gem.c
drivers/gpu/drm/vc4/vc4_hdmi.c
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_plane.c
drivers/gpu/drm/vc4/vc4_regs.h
drivers/gpu/drm/vc4/vc4_render_cl.c
drivers/gpu/drm/vc4/vc4_validate.c
drivers/gpu/drm/vgem/vgem_drv.c
drivers/gpu/drm/via/via_drv.c
drivers/gpu/drm/virtio/virtgpu_display.c
drivers/gpu/drm/virtio/virtgpu_drm_bus.c
drivers/gpu/drm/virtio/virtgpu_drv.c
drivers/gpu/drm/virtio/virtgpu_drv.h
drivers/gpu/drm/virtio/virtgpu_fence.c
drivers/gpu/drm/virtio/virtgpu_ioctl.c
drivers/gpu/drm/virtio/virtgpu_kms.c
drivers/gpu/drm/virtio/virtgpu_plane.c
drivers/gpu/drm/vmwgfx/Kconfig
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
drivers/gpu/ipu-v3/Makefile
drivers/gpu/ipu-v3/ipu-common.c
drivers/gpu/ipu-v3/ipu-cpmem.c
drivers/gpu/ipu-v3/ipu-csi.c
drivers/gpu/ipu-v3/ipu-dmfc.c
drivers/gpu/ipu-v3/ipu-ic.c
drivers/gpu/ipu-v3/ipu-image-convert.c [new file with mode: 0644]
drivers/gpu/ipu-v3/ipu-prv.h
drivers/gpu/ipu-v3/ipu-vdi.c [new file with mode: 0644]
drivers/gpu/vga/vgaarb.c
drivers/i2c/i2c-core.c
drivers/infiniband/Kconfig
drivers/infiniband/hw/Makefile
drivers/infiniband/hw/hns/hns_roce_cq.c
drivers/infiniband/hw/hns/hns_roce_device.h
drivers/infiniband/hw/hns/hns_roce_eq.c
drivers/infiniband/hw/hns/hns_roce_eq.h
drivers/infiniband/hw/hns/hns_roce_hem.c
drivers/infiniband/hw/hns/hns_roce_hem.h
drivers/infiniband/hw/hns/hns_roce_hw_v1.c
drivers/infiniband/hw/hns/hns_roce_hw_v1.h
drivers/infiniband/hw/hns/hns_roce_main.c
drivers/infiniband/hw/hns/hns_roce_mr.c
drivers/infiniband/hw/hns/hns_roce_pd.c
drivers/infiniband/hw/hns/hns_roce_qp.c
drivers/infiniband/hw/qedr/Kconfig [new file with mode: 0644]
drivers/infiniband/hw/qedr/Makefile [new file with mode: 0644]
drivers/infiniband/hw/qedr/main.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_cm.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_cm.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_hsi.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/qedr_hsi_rdma.h [new file with mode: 0644]
drivers/infiniband/hw/qedr/verbs.c [new file with mode: 0644]
drivers/infiniband/hw/qedr/verbs.h [new file with mode: 0644]
drivers/infiniband/sw/rdmavt/cq.c
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/elantech.c
drivers/input/rmi4/rmi_bus.c
drivers/input/rmi4/rmi_driver.c
drivers/input/rmi4/rmi_f01.c
drivers/input/rmi4/rmi_f11.c
drivers/input/rmi4/rmi_i2c.c
drivers/input/rmi4/rmi_spi.c
drivers/input/serio/i8042-io.h
drivers/input/serio/i8042-ip22io.h
drivers/input/serio/i8042-ppcio.h
drivers/input/serio/i8042-sparcio.h
drivers/input/serio/i8042-unicore32io.h
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/i8042.c
drivers/input/touchscreen/melfas_mip4.c
drivers/irqchip/irq-bcm6345-l1.c
drivers/irqchip/irq-bcm7038-l1.c
drivers/irqchip/irq-bcm7120-l2.c
drivers/irqchip/irq-brcmstb-l2.c
drivers/md/dm-rq.c
drivers/md/dm.c
drivers/media/dvb-frontends/af9013.h
drivers/media/dvb-frontends/af9033.h
drivers/media/dvb-frontends/ascot2e.h
drivers/media/dvb-frontends/atbm8830.h
drivers/media/dvb-frontends/au8522.h
drivers/media/dvb-frontends/cx22702.h
drivers/media/dvb-frontends/cx24113.h
drivers/media/dvb-frontends/cx24116.h
drivers/media/dvb-frontends/cx24117.h
drivers/media/dvb-frontends/cx24120.h
drivers/media/dvb-frontends/cx24123.h
drivers/media/dvb-frontends/cxd2820r.h
drivers/media/dvb-frontends/cxd2841er.h
drivers/media/dvb-frontends/dib3000mc.h
drivers/media/dvb-frontends/dib7000m.h
drivers/media/dvb-frontends/dib7000p.h
drivers/media/dvb-frontends/drxd.h
drivers/media/dvb-frontends/drxk.h
drivers/media/dvb-frontends/ds3000.h
drivers/media/dvb-frontends/dvb_dummy_fe.h
drivers/media/dvb-frontends/ec100.h
drivers/media/dvb-frontends/hd29l2.h
drivers/media/dvb-frontends/helene.h
drivers/media/dvb-frontends/horus3a.h
drivers/media/dvb-frontends/ix2505v.h
drivers/media/dvb-frontends/lg2160.h
drivers/media/dvb-frontends/lgdt3305.h
drivers/media/dvb-frontends/lgs8gl5.h
drivers/media/dvb-frontends/lgs8gxx.h
drivers/media/dvb-frontends/lnbh24.h
drivers/media/dvb-frontends/lnbh25.h
drivers/media/dvb-frontends/lnbp21.h
drivers/media/dvb-frontends/lnbp22.h
drivers/media/dvb-frontends/m88rs2000.h
drivers/media/dvb-frontends/mb86a20s.h
drivers/media/dvb-frontends/s5h1409.h
drivers/media/dvb-frontends/s5h1411.h
drivers/media/dvb-frontends/s5h1432.h
drivers/media/dvb-frontends/s921.h
drivers/media/dvb-frontends/si21xx.h
drivers/media/dvb-frontends/sp2.h
drivers/media/dvb-frontends/stb6000.h
drivers/media/dvb-frontends/stv0288.h
drivers/media/dvb-frontends/stv0367.h
drivers/media/dvb-frontends/stv0900.h
drivers/media/dvb-frontends/stv6110.h
drivers/media/dvb-frontends/tda10048.h
drivers/media/dvb-frontends/tda18271c2dd.h
drivers/media/dvb-frontends/ts2020.h
drivers/media/dvb-frontends/zl10036.h
drivers/media/dvb-frontends/zl10039.h
drivers/media/pci/cx23885/altera-ci.h
drivers/media/pci/ivtv/ivtv-driver.c
drivers/media/pci/ivtv/ivtv-irq.c
drivers/media/tuners/fc0011.h
drivers/media/tuners/fc0012.h
drivers/media/tuners/fc0013.h
drivers/media/tuners/max2165.h
drivers/media/tuners/mc44s803.h
drivers/media/tuners/mxl5005s.h
drivers/media/tuners/r820t.h
drivers/media/tuners/si2157.h
drivers/media/tuners/tda18212.h
drivers/media/tuners/tda18218.h
drivers/media/tuners/xc5000.h
drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h
drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h
drivers/media/usb/dvb-usb/dibusb-common.c
drivers/media/usb/hdpvr/hdpvr-video.c
drivers/media/v4l2-core/Kconfig
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/net/bonding/bond_main.c
drivers/net/dsa/b53/b53_mmap.c
drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_mac.h
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_main.h
drivers/net/ethernet/hisilicon/hns/hns_dsaf_rcb.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/pagealloc.c
drivers/net/ethernet/microchip/encx24j600.c
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/qed/qed_ll2.c
drivers/net/ethernet/qlogic/qed/qed_roce.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c
drivers/net/ethernet/sun/ldmvsw.c
drivers/net/ethernet/ti/tlan.c
drivers/net/ethernet/wiznet/w5100.c
drivers/net/ethernet/wiznet/w5300.c
drivers/net/ethernet/xilinx/xilinx_axienet_main.c
drivers/net/hyperv/netvsc_drv.c
drivers/net/phy/phy.c
drivers/net/usb/qmi_wwan.c
drivers/net/xen-netback/common.h
drivers/net/xen-netback/hash.c
drivers/net/xen-netback/rx.c
drivers/net/xen-netback/xenbus.c
drivers/nvme/host/pci.c
drivers/pci/host/pci-aardvark.c
drivers/pci/host/pci-dra7xx.c
drivers/pci/host/pci-exynos.c
drivers/pci/host/pci-imx6.c
drivers/pci/host/pci-keystone-dw.c
drivers/pci/host/pci-keystone.c
drivers/pci/host/pci-keystone.h
drivers/pci/host/pci-layerscape.c
drivers/pci/host/pci-mvebu.c
drivers/pci/host/pci-rcar-gen2.c
drivers/pci/host/pci-tegra.c
drivers/pci/host/pci-xgene.c
drivers/pci/host/pcie-altera.c
drivers/pci/host/pcie-armada8k.c
drivers/pci/host/pcie-artpec6.c
drivers/pci/host/pcie-designware-plat.c
drivers/pci/host/pcie-designware.c
drivers/pci/host/pcie-designware.h
drivers/pci/host/pcie-hisi.c
drivers/pci/host/pcie-iproc-bcma.c
drivers/pci/host/pcie-iproc-platform.c
drivers/pci/host/pcie-iproc.c
drivers/pci/host/pcie-qcom.c
drivers/pci/host/pcie-rcar.c
drivers/pci/host/pcie-rockchip.c
drivers/pci/host/pcie-spear13xx.c
drivers/pci/host/pcie-xilinx-nwl.c
drivers/pci/host/pcie-xilinx.c
drivers/platform/x86/acerhdf.c
drivers/platform/x86/asus-laptop.c
drivers/platform/x86/asus-nb-wmi.c
drivers/platform/x86/asus-wmi.c
drivers/platform/x86/asus-wmi.h
drivers/platform/x86/dell-smo8800.c
drivers/platform/x86/intel_pmc_core.c
drivers/platform/x86/intel_pmc_ipc.c
drivers/platform/x86/toshiba_acpi.c
drivers/platform/x86/toshiba_bluetooth.c
drivers/platform/x86/toshiba_haps.c
drivers/pps/Kconfig
drivers/ptp/ptp_chardev.c
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-berlin.c
drivers/pwm/pwm-cros-ec.c
drivers/pwm/pwm-lpc18xx-sct.c
drivers/pwm/pwm-meson.c [new file with mode: 0644]
drivers/pwm/pwm-mtk-disp.c
drivers/pwm/pwm-samsung.c
drivers/pwm/pwm-sti.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-tipwmss.c
drivers/pwm/pwm-twl.c
drivers/pwm/sysfs.c
drivers/rapidio/rio_cm.c
drivers/regulator/max8973-regulator.c
drivers/rtc/Kconfig
drivers/rtc/Makefile
drivers/rtc/rtc-ac100.c
drivers/rtc/rtc-asm9260.c
drivers/rtc/rtc-at32ap700x.c
drivers/rtc/rtc-bq32k.c
drivers/rtc/rtc-cmos.c
drivers/rtc/rtc-coh901331.c
drivers/rtc/rtc-davinci.c
drivers/rtc/rtc-digicolor.c
drivers/rtc/rtc-ds1302.c
drivers/rtc/rtc-ds1307.c
drivers/rtc/rtc-ds1347.c
drivers/rtc/rtc-gemini.c
drivers/rtc/rtc-isl12057.c [deleted file]
drivers/rtc/rtc-jz4740.c
drivers/rtc/rtc-mcp795.c
drivers/rtc/rtc-mt6397.c
drivers/rtc/rtc-nuc900.c
drivers/rtc/rtc-omap.c
drivers/rtc/rtc-palmas.c
drivers/rtc/rtc-pcf2123.c
drivers/rtc/rtc-pcf50633.c
drivers/rtc/rtc-pic32.c
drivers/rtc/rtc-rv8803.c
drivers/rtc/rtc-rx6110.c
drivers/rtc/rtc-rx8025.c
drivers/rtc/rtc-spear.c
drivers/rtc/rtc-stmp3xxx.c
drivers/rtc/rtc-sysfs.c
drivers/rtc/rtc-tegra.c
drivers/rtc/rtc-twl.c
drivers/scsi/be2iscsi/be_main.c
drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
drivers/scsi/g_NCR5380.c
drivers/scsi/g_NCR5380.h
drivers/scsi/ufs/Kconfig
drivers/scsi/ufs/ufs_quirks.h
drivers/scsi/ufs/ufshcd.c
drivers/soc/Kconfig
drivers/soc/fsl/Makefile
drivers/soc/fsl/qbman/Kconfig [new file with mode: 0644]
drivers/soc/fsl/qbman/Makefile [new file with mode: 0644]
drivers/soc/fsl/qbman/bman.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_ccsr.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_portal.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_priv.h [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test.c [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test.h [new file with mode: 0644]
drivers/soc/fsl/qbman/bman_test_api.c [new file with mode: 0644]
drivers/soc/fsl/qbman/dpaa_sys.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_ccsr.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_portal.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_priv.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test.h [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test_api.c [new file with mode: 0644]
drivers/soc/fsl/qbman/qman_test_stash.c [new file with mode: 0644]
drivers/soc/fsl/qe/gpio.c
drivers/soc/fsl/qe/qe.c
drivers/soc/fsl/qe/qe_common.c
drivers/soc/fsl/qe/qe_tdm.c
drivers/spi/spi.c
drivers/staging/lustre/lustre/llite/vvp_page.c
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/cpu_cooling.c
drivers/thermal/db8500_thermal.c
drivers/thermal/devfreq_cooling.c
drivers/thermal/gov_bang_bang.c
drivers/thermal/hisi_thermal.c
drivers/thermal/imx_thermal.c
drivers/thermal/int340x_thermal/int3402_thermal.c
drivers/thermal/int340x_thermal/int3403_thermal.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.c
drivers/thermal/int340x_thermal/int340x_thermal_zone.h
drivers/thermal/int340x_thermal/processor_thermal_device.c
drivers/thermal/intel_bxt_pmic_thermal.c [new file with mode: 0644]
drivers/thermal/intel_soc_dts_iosf.c
drivers/thermal/max77620_thermal.c [new file with mode: 0644]
drivers/thermal/mtk_thermal.c
drivers/thermal/of-thermal.c
drivers/thermal/qcom-spmi-temp-alarm.c
drivers/thermal/qcom/Kconfig [new file with mode: 0644]
drivers/thermal/qcom/Makefile [new file with mode: 0644]
drivers/thermal/qcom/tsens-8916.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8960.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8974.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-8996.c [new file with mode: 0644]
drivers/thermal/qcom/tsens-common.c [new file with mode: 0644]
drivers/thermal/qcom/tsens.c [new file with mode: 0644]
drivers/thermal/qcom/tsens.h [new file with mode: 0644]
drivers/thermal/qoriq_thermal.c [new file with mode: 0644]
drivers/thermal/rcar_thermal.c
drivers/thermal/rockchip_thermal.c
drivers/thermal/samsung/exynos_tmu.c
drivers/thermal/st/st_thermal_memmap.c
drivers/thermal/tango_thermal.c
drivers/thermal/tegra/soctherm.c
drivers/thermal/tegra/soctherm.h
drivers/thermal/tegra/tegra124-soctherm.c
drivers/thermal/tegra/tegra132-soctherm.c
drivers/thermal/tegra/tegra210-soctherm.c
drivers/thermal/thermal_core.c
drivers/thermal/ti-soc-thermal/ti-thermal-common.c
drivers/thermal/user_space.c
drivers/thermal/x86_pkg_temp_thermal.c
drivers/tty/serial/sc16is7xx.c
drivers/usb/early/ehci-dbgp.c
drivers/usb/gadget/udc/bcm63xx_udc.c
drivers/usb/host/pci-quirks.c
drivers/video/fbdev/Kconfig
drivers/video/fbdev/Makefile
drivers/video/fbdev/amba-clcd-nomadik.c [new file with mode: 0644]
drivers/video/fbdev/amba-clcd-nomadik.h [new file with mode: 0644]
drivers/video/fbdev/amba-clcd-versatile.c
drivers/video/fbdev/amba-clcd-versatile.h [new file with mode: 0644]
drivers/video/fbdev/amba-clcd.c
drivers/video/fbdev/arcfb.c
drivers/video/fbdev/asiliantfb.c
drivers/video/fbdev/aty/aty128fb.c
drivers/video/fbdev/aty/atyfb_base.c
drivers/video/fbdev/aty/radeon_monitor.c
drivers/video/fbdev/au1200fb.c
drivers/video/fbdev/bfin_adv7393fb.c
drivers/video/fbdev/efifb.c
drivers/video/fbdev/exynos/Kconfig [deleted file]
drivers/video/fbdev/exynos/Makefile [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h [deleted file]
drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h [deleted file]
drivers/video/fbdev/exynos/s6e8ax0.c [deleted file]
drivers/video/fbdev/hecubafb.c
drivers/video/fbdev/hgafb.c
drivers/video/fbdev/i740fb.c
drivers/video/fbdev/i810/i810_main.c
drivers/video/fbdev/intelfb/intelfbdrv.c
drivers/video/fbdev/kyro/fbdev.c
drivers/video/fbdev/matrox/matroxfb_Ti3026.c
drivers/video/fbdev/matrox/matroxfb_g450.c
drivers/video/fbdev/mb862xx/mb862xx-i2c.c
drivers/video/fbdev/mx3fb.c
drivers/video/fbdev/mxsfb.c
drivers/video/fbdev/offb.c
drivers/video/fbdev/omap/lcd_mipid.c
drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c
drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c
drivers/video/fbdev/omap2/omapfb/dss/dsi.c
drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c
drivers/video/fbdev/omap2/omapfb/dss/hdmi5.c
drivers/video/fbdev/pm2fb.c
drivers/video/fbdev/pxafb.c
drivers/video/fbdev/s1d13xxxfb.c
drivers/video/fbdev/s3c2410fb.c
drivers/video/fbdev/s3c2410fb.h
drivers/video/fbdev/savage/savagefb_driver.c
drivers/video/fbdev/simplefb.c
drivers/video/fbdev/sm712fb.c
drivers/video/fbdev/smscufx.c
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/tdfxfb.c
drivers/video/fbdev/uvesafb.c
drivers/video/fbdev/vfb.c
drivers/video/fbdev/vga16fb.c
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/asm9260_wdt.c
drivers/watchdog/ath79_wdt.c
drivers/watchdog/bcm7038_wdt.c
drivers/watchdog/cadence_wdt.c
drivers/watchdog/dw_wdt.c
drivers/watchdog/hpwdt.c
drivers/watchdog/iTCO_wdt.c
drivers/watchdog/imx2_wdt.c
drivers/watchdog/kempld_wdt.c
drivers/watchdog/mt7621_wdt.c
drivers/watchdog/of_xilinx_wdt.c
drivers/watchdog/pretimeout_noop.c [new file with mode: 0644]
drivers/watchdog/pretimeout_panic.c [new file with mode: 0644]
drivers/watchdog/rn5t618_wdt.c
drivers/watchdog/rt2880_wdt.c
drivers/watchdog/softdog.c
drivers/watchdog/st_lpc_wdt.c
drivers/watchdog/tegra_wdt.c
drivers/watchdog/txx9wdt.c
drivers/watchdog/w83627hf_wdt.c
drivers/watchdog/watchdog_core.c
drivers/watchdog/watchdog_dev.c
drivers/watchdog/watchdog_pretimeout.c [new file with mode: 0644]
drivers/watchdog/watchdog_pretimeout.h [new file with mode: 0644]
drivers/watchdog/ziirave_wdt.c
fs/afs/write.c
fs/autofs4/autofs_i.h
fs/autofs4/dev-ioctl.c
fs/autofs4/inode.c
fs/autofs4/root.c
fs/block_dev.c
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/extent_io.c
fs/btrfs/extent_io.h
fs/btrfs/free-space-tree.c
fs/btrfs/tests/extent-io-tests.c
fs/btrfs/tests/free-space-tree-tests.c
fs/buffer.c
fs/cifs/cifs_debug.c
fs/cifs/cifs_fs_sb.h
fs/cifs/cifs_ioctl.h
fs/cifs/cifsacl.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/file.c
fs/cifs/ioctl.c
fs/cifs/misc.c
fs/cifs/readdir.c
fs/cifs/smb2inode.c
fs/cifs/smb2misc.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h
fs/cifs/xattr.c
fs/compat_ioctl.c
fs/exofs/inode.c
fs/exportfs/expfs.c
fs/ext4/page-io.c
fs/f2fs/data.c
fs/jbd2/commit.c
fs/kernfs/dir.c
fs/lockd/procfs.h
fs/namei.c
fs/nfs/cache_lib.c
fs/nfs/callback.c
fs/nfs/callback.h
fs/nfs/callback_proc.c
fs/nfs/callback_xdr.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/direct.c
fs/nfs/file.c
fs/nfs/flexfilelayout/flexfilelayout.c
fs/nfs/internal.h
fs/nfs/netns.h
fs/nfs/nfs42proc.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4client.c
fs/nfs/nfs4proc.c
fs/nfs/nfs4session.h
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/pnfs.c
fs/nfs/pnfs.h
fs/nfs/pnfs_nfs.c
fs/nfs/super.c
fs/nfsd/flexfilelayout.c
fs/nfsd/netns.h
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4layouts.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsproc.c
fs/nfsd/nfssvc.c
fs/nfsd/pnfs.h
fs/nfsd/state.h
fs/nfsd/vfs.c
fs/nfsd/vfs.h
fs/nfsd/xdr4.h
fs/nfsd/xdr4cb.h
fs/ocfs2/dlm/dlmmaster.c
fs/open.c
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/inode.c
fs/overlayfs/super.c
fs/pipe.c
fs/select.c
fs/sysfs/dir.c
fs/xfs/Makefile
fs/xfs/libxfs/xfs_ag_resv.c
fs/xfs/libxfs/xfs_alloc.c
fs/xfs/libxfs/xfs_bmap.c
fs/xfs/libxfs/xfs_bmap.h
fs/xfs/libxfs/xfs_bmap_btree.c
fs/xfs/libxfs/xfs_btree.c
fs/xfs/libxfs/xfs_btree.h
fs/xfs/libxfs/xfs_defer.h
fs/xfs/libxfs/xfs_format.h
fs/xfs/libxfs/xfs_fs.h
fs/xfs/libxfs/xfs_inode_buf.c
fs/xfs/libxfs/xfs_inode_buf.h
fs/xfs/libxfs/xfs_inode_fork.c
fs/xfs/libxfs/xfs_inode_fork.h
fs/xfs/libxfs/xfs_log_format.h
fs/xfs/libxfs/xfs_refcount.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount.h [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount_btree.c [new file with mode: 0644]
fs/xfs/libxfs/xfs_refcount_btree.h [new file with mode: 0644]
fs/xfs/libxfs/xfs_rmap.c
fs/xfs/libxfs/xfs_rmap.h
fs/xfs/libxfs/xfs_rmap_btree.c
fs/xfs/libxfs/xfs_rmap_btree.h
fs/xfs/libxfs/xfs_sb.c
fs/xfs/libxfs/xfs_shared.h
fs/xfs/libxfs/xfs_trans_resv.c
fs/xfs/libxfs/xfs_trans_resv.h
fs/xfs/libxfs/xfs_trans_space.h
fs/xfs/libxfs/xfs_types.h
fs/xfs/xfs_aops.c
fs/xfs/xfs_aops.h
fs/xfs/xfs_bmap_item.c [new file with mode: 0644]
fs/xfs/xfs_bmap_item.h [new file with mode: 0644]
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_dir2_readdir.c
fs/xfs/xfs_error.h
fs/xfs/xfs_file.c
fs/xfs/xfs_fsops.c
fs/xfs/xfs_fsops.h
fs/xfs/xfs_globals.c
fs/xfs/xfs_icache.c
fs/xfs/xfs_icache.h
fs/xfs/xfs_inode.c
fs/xfs/xfs_inode.h
fs/xfs/xfs_inode_item.c
fs/xfs/xfs_ioctl.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_iomap.h
fs/xfs/xfs_iops.c
fs/xfs/xfs_itable.c
fs/xfs/xfs_linux.h
fs/xfs/xfs_log_recover.c
fs/xfs/xfs_mount.c
fs/xfs/xfs_mount.h
fs/xfs/xfs_ondisk.h
fs/xfs/xfs_pnfs.c
fs/xfs/xfs_refcount_item.c [new file with mode: 0644]
fs/xfs/xfs_refcount_item.h [new file with mode: 0644]
fs/xfs/xfs_reflink.c [new file with mode: 0644]
fs/xfs/xfs_reflink.h [new file with mode: 0644]
fs/xfs/xfs_rmap_item.c
fs/xfs/xfs_stats.c
fs/xfs/xfs_stats.h
fs/xfs/xfs_super.c
fs/xfs/xfs_sysctl.c
fs/xfs/xfs_sysctl.h
fs/xfs/xfs_trace.h
fs/xfs/xfs_trans.h
fs/xfs/xfs_trans_bmap.c [new file with mode: 0644]
fs/xfs/xfs_trans_refcount.c [new file with mode: 0644]
fs/xfs/xfs_trans_rmap.c
include/asm-generic/export.h [new file with mode: 0644]
include/asm-generic/libata-portmap.h [deleted file]
include/asm-generic/percpu.h
include/asm-generic/uaccess.h
include/asm-generic/vmlinux.lds.h
include/drm/bridge/analogix_dp.h
include/drm/drmP.h
include/drm/drm_atomic.h
include/drm/drm_atomic_helper.h
include/drm/drm_blend.h [new file with mode: 0644]
include/drm/drm_bridge.h [new file with mode: 0644]
include/drm/drm_color_mgmt.h [new file with mode: 0644]
include/drm/drm_connector.h [new file with mode: 0644]
include/drm/drm_core.h [deleted file]
include/drm/drm_crtc.h
include/drm/drm_crtc_helper.h
include/drm/drm_dp_aux_dev.h [deleted file]
include/drm/drm_dp_helper.h
include/drm/drm_edid.h
include/drm/drm_encoder.h [new file with mode: 0644]
include/drm/drm_fb_helper.h
include/drm/drm_fourcc.h
include/drm/drm_framebuffer.h [new file with mode: 0644]
include/drm/drm_gem.h
include/drm/drm_mipi_dsi.h
include/drm/drm_mm.h
include/drm/drm_mode_object.h [new file with mode: 0644]
include/drm/drm_modes.h
include/drm/drm_modeset_helper.h [new file with mode: 0644]
include/drm/drm_modeset_helper_vtables.h
include/drm/drm_plane.h [new file with mode: 0644]
include/drm/drm_plane_helper.h
include/drm/drm_property.h [new file with mode: 0644]
include/drm/drm_simple_kms_helper.h
include/drm/drm_vma_manager.h
include/drm/i2c/tda998x.h
include/drm/i915_drm.h
include/drm/i915_pciids.h
include/drm/ttm/ttm_bo_api.h
include/drm/ttm/ttm_bo_driver.h
include/drm/ttm/ttm_memory.h
include/drm/ttm/ttm_placement.h
include/dt-bindings/display/tda998x.h [new file with mode: 0644]
include/dt-bindings/thermal/tegra124-soctherm.h
include/linux/acpi.h
include/linux/amba/clcd.h
include/linux/ata.h
include/linux/auto_dev-ioctl.h
include/linux/auto_fs.h
include/linux/blk-cgroup.h
include/linux/cgroup.h
include/linux/compiler.h
include/linux/cpufreq.h
include/linux/ctype.h
include/linux/dma-mapping.h
include/linux/export.h
include/linux/exportfs.h
include/linux/falloc.h
include/linux/fence-array.h
include/linux/fence.h
include/linux/fs.h
include/linux/gpio/driver.h
include/linux/init.h
include/linux/io-mapping.h
include/linux/kernfs.h
include/linux/kexec.h
include/linux/kmemleak.h
include/linux/kthread.h
include/linux/libata.h
include/linux/mlx5/device.h
include/linux/nfs4.h
include/linux/nfs_fs_sb.h
include/linux/nfs_xdr.h
include/linux/pagemap.h
include/linux/pkeys.h
include/linux/pwm.h
include/linux/radix-tree.h
include/linux/random.h
include/linux/relay.h
include/linux/sem.h
include/linux/sunrpc/auth.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/rpc_rdma.h
include/linux/sunrpc/sched.h
include/linux/sunrpc/svc_rdma.h
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
include/linux/sunrpc/xprtmultipath.h
include/linux/sunrpc/xprtrdma.h
include/linux/sync_file.h
include/linux/thermal.h
include/linux/vgaarb.h
include/linux/watchdog.h
include/net/bonding.h
include/net/cfg80211.h
include/net/l3mdev.h
include/soc/fsl/bman.h [new file with mode: 0644]
include/soc/fsl/qman.h [new file with mode: 0644]
include/trace/events/cgroup.h [new file with mode: 0644]
include/uapi/drm/amdgpu_drm.h
include/uapi/drm/drm.h
include/uapi/drm/drm_mode.h
include/uapi/drm/i915_drm.h
include/uapi/drm/msm_drm.h
include/uapi/linux/Kbuild
include/uapi/linux/auto_dev-ioctl.h [new file with mode: 0644]
include/uapi/linux/auto_fs.h
include/uapi/linux/btrfs.h
include/uapi/linux/falloc.h
include/uapi/linux/fs.h
include/uapi/linux/nfs4.h
include/uapi/linux/pci_regs.h
include/uapi/linux/sync_file.h
include/uapi/rdma/qedr-abi.h [new file with mode: 0644]
include/video/exynos_mipi_dsim.h [deleted file]
include/video/imx-ipu-image-convert.h [new file with mode: 0644]
include/video/imx-ipu-v3.h
init/Kconfig
init/Makefile
ipc/msg.c
ipc/sem.c
kernel/cgroup.c
kernel/configs/android-base.config
kernel/configs/android-recommended.config
kernel/cpuset.c
kernel/hung_task.c
kernel/kprobes.c
kernel/kthread.c
kernel/panic.c
kernel/ptrace.c
kernel/relay.c
kernel/sched/debug.c
kernel/smpboot.c
kernel/trace/Makefile
kernel/workqueue.c
lib/Kconfig.debug
lib/Makefile
lib/bitmap.c
lib/kstrtox.c
lib/percpu-refcount.c
lib/strncpy_from_user.c
mm/Makefile
mm/bootmem.c
mm/cma.c
mm/kmemleak.c
mm/memblock.c
mm/nobootmem.c
mm/percpu.c
net/batman-adv/debugfs.h
net/bridge/br_sysfs_if.c
net/core/rtnetlink.c
net/ipv4/route.c
net/ipv6/tcp_ipv6.c
net/openvswitch/flow.c
net/openvswitch/vport-internal_dev.c
net/openvswitch/vport.c
net/sched/act_api.c
net/sched/cls_api.c
net/strparser/strparser.c
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_unix.c
net/sunrpc/backchannel_rqst.c
net/sunrpc/cache.c
net/sunrpc/clnt.c
net/sunrpc/sched.c
net/sunrpc/svc.c
net/sunrpc/xdr.c
net/sunrpc/xprt.c
net/sunrpc/xprtmultipath.c
net/sunrpc/xprtrdma/backchannel.c
net/sunrpc/xprtrdma/fmr_ops.c
net/sunrpc/xprtrdma/frwr_ops.c
net/sunrpc/xprtrdma/rpc_rdma.c
net/sunrpc/xprtrdma/svc_rdma_backchannel.c
net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
net/sunrpc/xprtrdma/svc_rdma_sendto.c
net/sunrpc/xprtrdma/svc_rdma_transport.c
net/sunrpc/xprtrdma/transport.c
net/sunrpc/xprtrdma/verbs.c
net/sunrpc/xprtrdma/xprt_rdma.h
net/sunrpc/xprtsock.c
net/tipc/udp_media.c
samples/Kconfig
samples/Makefile
samples/auxdisplay/.gitignore [new file with mode: 0644]
samples/auxdisplay/Makefile [new file with mode: 0644]
samples/auxdisplay/cfag12864b-example.c [new file with mode: 0644]
samples/blackfin/Makefile [new file with mode: 0644]
samples/blackfin/gptimers-example.c [new file with mode: 0644]
samples/mei/.gitignore [new file with mode: 0644]
samples/mei/Makefile [new file with mode: 0644]
samples/mei/TODO [new file with mode: 0644]
samples/mei/mei-amt-version.c [new file with mode: 0644]
samples/mic/mpssd/.gitignore [new file with mode: 0644]
samples/mic/mpssd/Makefile [new file with mode: 0644]
samples/mic/mpssd/micctrl [new file with mode: 0755]
samples/mic/mpssd/mpss [new file with mode: 0755]
samples/mic/mpssd/mpssd.c [new file with mode: 0644]
samples/mic/mpssd/mpssd.h [new file with mode: 0644]
samples/mic/mpssd/sysfs.c [new file with mode: 0644]
samples/timers/.gitignore [new file with mode: 0644]
samples/timers/Makefile [new file with mode: 0644]
samples/timers/hpet_example.c [new file with mode: 0644]
samples/watchdog/.gitignore [new file with mode: 0644]
samples/watchdog/Makefile [new file with mode: 0644]
samples/watchdog/watchdog-simple.c [new file with mode: 0644]
scripts/Makefile.build
scripts/Makefile.modpost
scripts/basic/fixdep.c
scripts/checkpatch.pl
scripts/coccicheck
scripts/coccinelle/api/memdup_user.cocci
scripts/coccinelle/api/pm_runtime.cocci
scripts/coccinelle/misc/cond_no_effect.cocci [new file with mode: 0644]
scripts/const_structs.checkpatch [new file with mode: 0644]
scripts/gen_initramfs_list.sh
scripts/genksyms/lex.l
scripts/genksyms/lex.lex.c_shipped
scripts/link-vmlinux.sh
scripts/tags.sh
sound/soc/intel/baytrail/sst-baytrail-ipc.c
sound/soc/intel/common/sst-acpi.h
sound/soc/intel/common/sst-ipc.c
sound/soc/intel/haswell/sst-haswell-ipc.c
sound/soc/intel/skylake/skl-sst-ipc.c
tools/accounting/.gitignore [new file with mode: 0644]
tools/accounting/Makefile [new file with mode: 0644]
tools/accounting/getdelays.c [new file with mode: 0644]
tools/laptop/dslm/.gitignore [new file with mode: 0644]
tools/laptop/dslm/Makefile [new file with mode: 0644]
tools/laptop/dslm/dslm.c [new file with mode: 0644]
tools/pcmcia/.gitignore [new file with mode: 0644]
tools/pcmcia/Makefile [new file with mode: 0644]
tools/pcmcia/crc32hash.c [new file with mode: 0644]
tools/testing/nvdimm/config_check.c
tools/testing/radix-tree/Makefile
tools/testing/radix-tree/iteration_check.c [new file with mode: 0644]
tools/testing/radix-tree/main.c
tools/testing/radix-tree/regression1.c
tools/testing/radix-tree/test.h
tools/testing/selftests/filesystems/.gitignore [new file with mode: 0644]
tools/testing/selftests/filesystems/Makefile [new file with mode: 0644]
tools/testing/selftests/filesystems/dnotify_test.c [new file with mode: 0644]
tools/testing/selftests/futex/functional/run.sh
tools/testing/selftests/futex/run.sh
tools/testing/selftests/ia64/.gitignore [new file with mode: 0644]
tools/testing/selftests/ia64/Makefile [new file with mode: 0644]
tools/testing/selftests/ia64/aliasing-test.c [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/.gitignore [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/Makefile [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/hwtstamp_config.c [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/timestamping.c [new file with mode: 0644]
tools/testing/selftests/networking/timestamping/txtimestamp.c [new file with mode: 0644]
tools/testing/selftests/powerpc/copyloops/asm/export.h [new file with mode: 0644]
tools/testing/selftests/powerpc/math/.gitignore
tools/testing/selftests/powerpc/signal/.gitignore [new file with mode: 0644]
tools/testing/selftests/powerpc/stringloops/asm/export.h [new file with mode: 0644]
tools/testing/selftests/powerpc/tm/.gitignore
tools/testing/selftests/prctl/.gitignore [new file with mode: 0644]
tools/testing/selftests/prctl/Makefile [new file with mode: 0644]
tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c [new file with mode: 0644]
tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c [new file with mode: 0644]
tools/testing/selftests/prctl/disable-tsc-test.c [new file with mode: 0644]
tools/testing/selftests/ptp/.gitignore [new file with mode: 0644]
tools/testing/selftests/ptp/Makefile [new file with mode: 0644]
tools/testing/selftests/ptp/testptp.c [new file with mode: 0644]
tools/testing/selftests/ptp/testptp.mk [new file with mode: 0644]
tools/testing/selftests/timers/posix_timers.c
tools/testing/selftests/vDSO/.gitignore [new file with mode: 0644]
tools/testing/selftests/vDSO/Makefile [new file with mode: 0644]
tools/testing/selftests/vDSO/parse_vdso.c [new file with mode: 0644]
tools/testing/selftests/vDSO/vdso_standalone_test_x86.c [new file with mode: 0644]
tools/testing/selftests/vDSO/vdso_test.c [new file with mode: 0644]
tools/testing/selftests/watchdog/.gitignore [new file with mode: 0644]
tools/testing/selftests/watchdog/Makefile [new file with mode: 0644]
tools/testing/selftests/watchdog/watchdog-test.c [new file with mode: 0644]
tools/testing/selftests/zram/README

index cb9a6c6..3acc4f1 100644 (file)
@@ -46,7 +46,8 @@ IRQ.txt
 Intel-IOMMU.txt
        - basic info on the Intel IOMMU virtualization support.
 Makefile
-       - some files in Documentation dir are actually sample code to build
+       - This file does nothing. Removing it breaks make htmldocs and
+         make distclean.
 ManagementStyle
        - how to (attempt to) manage kernel hackers.
 RCU/
diff --git a/Documentation/80211/cfg80211.rst b/Documentation/80211/cfg80211.rst
new file mode 100644 (file)
index 0000000..b1e149e
--- /dev/null
@@ -0,0 +1,345 @@
+==================
+cfg80211 subsystem
+==================
+
+Device registration
+===================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Device registration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_rate_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_rate
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_sta_ht_cap
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_supported_band
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_signal_type
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_params_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wireless_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_new
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_register
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_unregister
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_free
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_name
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_priv
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: priv_to_wiphy
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: set_wiphy_dev
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wdev_priv
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_iface_limit
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_iface_combination
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_check_combinations
+
+Actions and configuration
+=========================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Actions and configuration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ops
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: vif_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: key_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: survey_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: survey_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_beacon_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ap_settings
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: station_parameters
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: rate_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: rate_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: station_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: monitor_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: mpath_info_flags
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: mpath_info
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: bss_parameters
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_txq_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_crypto_settings
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_auth_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_assoc_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_deauth_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_disassoc_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ibss_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_params
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_pmksa
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_mlme_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_auth_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_assoc_resp
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_assoc_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_tx_mlme_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ibss_joined
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_result
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_connect_timeout
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_roamed
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_disconnected
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ready_on_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_remain_on_channel_expired
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_new_sta
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_rx_mgmt
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_mgmt_tx_status
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_cqm_rssi_notify
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_cqm_pktloss_notify
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_michael_mic_failure
+
+Scanning and BSS list handling
+==============================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Scanning and BSS list handling
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_ssid
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_scan_request
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_scan_done
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss_frame_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_inform_bss_data
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_unlink_bss
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_find_ie
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_bss_get_ie
+
+Utility functions
+=================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Utility functions
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_channel_to_frequency
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_frequency_to_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_channel
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_response_rate
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_hdrlen
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_get_hdrlen_from_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_radiotap_iterator
+
+Data path helpers
+=================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Data path helpers
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_data_to_8023
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_data_from_8023
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: ieee80211_amsdu_to_8023s
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_classify8021d
+
+Regulatory enforcement infrastructure
+=====================================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Regulatory enforcement infrastructure
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: regulatory_hint
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_apply_custom_regulatory
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: freq_reg_info
+
+RFkill integration
+==================
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: RFkill integration
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_set_hw_state
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_start_polling
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: wiphy_rfkill_stop_polling
+
+Test mode
+=========
+
+.. kernel-doc:: include/net/cfg80211.h
+   :doc: Test mode
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_alloc_reply_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_reply
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_alloc_event_skb
+
+.. kernel-doc:: include/net/cfg80211.h
+   :functions: cfg80211_testmode_event
diff --git a/Documentation/80211/conf.py b/Documentation/80211/conf.py
new file mode 100644 (file)
index 0000000..20c7c27
--- /dev/null
@@ -0,0 +1,5 @@
+# -*- coding: utf-8; mode: python -*-
+
+project = "Linux 802.11 Driver Developer's Guide"
+
+tags.add("subproject")
diff --git a/Documentation/80211/index.rst b/Documentation/80211/index.rst
new file mode 100644 (file)
index 0000000..90bba47
--- /dev/null
@@ -0,0 +1,17 @@
+=====================================
+Linux 802.11 Driver Developer's Guide
+=====================================
+
+.. toctree::
+
+   introduction
+   cfg80211
+   mac80211
+   mac80211-advanced
+
+.. only::  subproject
+
+   Indices
+   =======
+
+   * :ref:`genindex`
diff --git a/Documentation/80211/introduction.rst b/Documentation/80211/introduction.rst
new file mode 100644 (file)
index 0000000..4938fa8
--- /dev/null
@@ -0,0 +1,17 @@
+============
+Introduction
+============
+
+Explaining wireless 802.11 networking in the Linux kernel
+
+Copyright 2007-2009 Johannes Berg
+
+These books attempt to give a description of the various subsystems
+that play a role in 802.11 wireless networking in Linux. Since these
+books are for kernel developers they attempts to document the
+structures and functions used in the kernel as well as giving a
+higher-level overview.
+
+The reader is expected to be familiar with the 802.11 standard as
+published by the IEEE in 802.11-2007 (or possibly later versions).
+References to this standard will be given as "802.11-2007 8.1.5".
diff --git a/Documentation/80211/mac80211-advanced.rst b/Documentation/80211/mac80211-advanced.rst
new file mode 100644 (file)
index 0000000..70a89b2
--- /dev/null
@@ -0,0 +1,295 @@
+=============================
+mac80211 subsystem (advanced)
+=============================
+
+Information contained within this part of the book is of interest only
+for advanced interaction of mac80211 with drivers to exploit more
+hardware capabilities and improve performance.
+
+LED support
+===========
+
+Mac80211 supports various ways of blinking LEDs. Wherever possible,
+device LEDs should be exposed as LED class devices and hooked up to the
+appropriate trigger, which will then be triggered appropriately by
+mac80211.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tx_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_rx_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_assoc_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_radio_led_name
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tpt_blink
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tpt_led_trigger_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_create_tpt_led_trigger
+
+Hardware crypto acceleration
+============================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Hardware crypto acceleration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: set_key_cmd
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_key_conf
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_key_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p1k
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p1k_iv
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_tkip_p2k
+
+Powersave support
+=================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Powersave support
+
+Beacon filter support
+=====================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Beacon filter support
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_beacon_loss
+
+Multiple queues and QoS support
+===============================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_queue_params
+
+Access point mode support
+=========================
+
+TBD
+
+Some parts of the if_conf should be discussed here instead
+
+Insert notes about VLAN interfaces with hw crypto here or in the hw
+crypto chapter.
+
+support for powersaving clients
+-------------------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: AP support for powersaving clients
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_get_buffered_bc
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_beacon_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_eosp
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_frame_release_type
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_ps_transition
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_ps_transition_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_set_buffered
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta_block_awake
+
+Supporting multiple virtual interfaces
+======================================
+
+TBD
+
+Note: WDS with identical MAC address should almost always be OK
+
+Insert notes about having multiple virtual interfaces with different MAC
+addresses here, note which configurations are supported by mac80211, add
+notes about supporting hw crypto with it.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_iterate_active_interfaces
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_iterate_active_interfaces_atomic
+
+Station handling
+================
+
+TODO
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_sta
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: sta_notify_cmd
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_find_sta
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_find_sta_by_ifaddr
+
+Hardware scan offload
+=====================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_scan_completed
+
+Aggregation
+===========
+
+TX A-MPDU aggregation
+---------------------
+
+.. kernel-doc:: net/mac80211/agg-tx.c
+   :doc: TX A-MPDU aggregation
+
+.. WARNING: DOCPROC directive not supported: !Cnet/mac80211/agg-tx.c
+
+RX A-MPDU aggregation
+---------------------
+
+.. kernel-doc:: net/mac80211/agg-rx.c
+   :doc: RX A-MPDU aggregation
+
+.. WARNING: DOCPROC directive not supported: !Cnet/mac80211/agg-rx.c
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ampdu_mlme_action
+
+Spatial Multiplexing Powersave (SMPS)
+=====================================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Spatial multiplexing power save
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_request_smps
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_smps_mode
+
+TBD
+
+This part of the book describes the rate control algorithm interface and
+how it relates to mac80211 and drivers.
+
+Rate Control API
+================
+
+TBD
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_start_tx_ba_session
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_start_tx_ba_cb_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_tx_ba_session
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_tx_ba_cb_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rate_control_changed
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_rate_control
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: rate_control_send_low
+
+TBD
+
+This part of the book describes mac80211 internals.
+
+Key handling
+============
+
+Key handling basics
+-------------------
+
+.. kernel-doc:: net/mac80211/key.c
+   :doc: Key handling basics
+
+MORE TBD
+--------
+
+TBD
+
+Receive processing
+==================
+
+TBD
+
+Transmit processing
+===================
+
+TBD
+
+Station info handling
+=====================
+
+Programming information
+-----------------------
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: sta_info
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: ieee80211_sta_info_flags
+
+STA information lifetime rules
+------------------------------
+
+.. kernel-doc:: net/mac80211/sta_info.c
+   :doc: STA information lifetime rules
+
+Aggregation
+===========
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: sta_ampdu_mlme
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: tid_ampdu_tx
+
+.. kernel-doc:: net/mac80211/sta_info.h
+   :functions: tid_ampdu_rx
+
+Synchronisation
+===============
+
+TBD
+
+Locking, lots of RCU
diff --git a/Documentation/80211/mac80211.rst b/Documentation/80211/mac80211.rst
new file mode 100644 (file)
index 0000000..85a8335
--- /dev/null
@@ -0,0 +1,216 @@
+===========================
+mac80211 subsystem (basics)
+===========================
+
+You should read and understand the information contained within this
+part of the book while implementing a mac80211 driver. In some chapters,
+advanced usage is noted, those may be skipped if this isn't needed.
+
+This part of the book only covers station and monitor mode
+functionality, additional information required to implement the other
+modes is covered in the second part of the book.
+
+Basic hardware handling
+=======================
+
+TBD
+
+This chapter shall contain information on getting a hw struct allocated
+and registered with mac80211.
+
+Since it is required to allocate rates/modes before registering a hw
+struct, this chapter shall also contain information on setting up the
+rate/mode structs.
+
+Additionally, some discussion about the callbacks and the general
+programming model should be in here, including the definition of
+ieee80211_ops which will be referred to a lot.
+
+Finally, a discussion of hardware capabilities should be done with
+references to other parts of the book.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_hw_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: SET_IEEE80211_DEV
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: SET_IEEE80211_PERM_ADDR
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ops
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_alloc_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_register_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_unregister_hw
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_free_hw
+
+PHY configuration
+=================
+
+TBD
+
+This chapter should describe PHY handling including start/stop callbacks
+and the various structures used.
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_conf
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_conf_flags
+
+Virtual interfaces
+==================
+
+TBD
+
+This chapter should describe virtual interface basics that are relevant
+to the driver (VLANs, MGMT etc are not.) It should explain the use of
+the add_iface/remove_iface callbacks as well as the interface
+configuration callbacks.
+
+Things related to AP mode should be discussed there.
+
+Things related to supporting multiple interfaces should be in the
+appropriate chapter, a BIG FAT note should be here about this though and
+the recommendation to allow only a single interface in STA mode at
+first!
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_vif
+
+Receive and transmit processing
+===============================
+
+what should be here
+-------------------
+
+TBD
+
+This should describe the receive and transmit paths in mac80211/the
+drivers as well as transmit status handling.
+
+Frame format
+------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Frame format
+
+Packet alignment
+----------------
+
+.. kernel-doc:: net/mac80211/rx.c
+   :doc: Packet alignment
+
+Calling into mac80211 from interrupts
+-------------------------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Calling mac80211 from interrupts
+
+functions/definitions
+---------------------
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_rx_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_tx_info_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_tx_control_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: mac80211_rate_control_flags
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_rate
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_info
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_info_clear_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rx_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status_ni
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_tx_status_irqsafe
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rts_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_rts_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ctstoself_get
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_ctstoself_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_generic_frame_duration
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_wake_queue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_queue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_wake_queues
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_stop_queues
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_stopped
+
+Frame filtering
+===============
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: Frame filtering
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_filter_flags
+
+The mac80211 workqueue
+======================
+
+.. kernel-doc:: include/net/mac80211.h
+   :doc: mac80211 workqueue
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_work
+
+.. kernel-doc:: include/net/mac80211.h
+   :functions: ieee80211_queue_delayed_work
index 2d455a5..98bf7ac 100644 (file)
@@ -126,3 +126,20 @@ means that we won't try quite as hard to get them.
 
 NOTE: At the moment DMA_ATTR_ALLOC_SINGLE_PAGES is only implemented on ARM,
 though ARM64 patches will likely be posted soon.
+
+DMA_ATTR_NO_WARN
+----------------
+
+This tells the DMA-mapping subsystem to suppress allocation failure reports
+(similarly to __GFP_NOWARN).
+
+On some architectures allocation failures are reported with error messages
+to the system logs.  Although this can help to identify and debug problems,
+drivers which handle failures (eg, retry later) have no problems with them,
+and can actually flood the system logs with error messages that aren't any
+problem at all, depending on the implementation of the retry mechanism.
+
+So, this provides a way for drivers to avoid those error messages on calls
+where allocation failures are not a problem, and shouldn't bother the logs.
+
+NOTE: At the moment DMA_ATTR_NO_WARN is only implemented on PowerPC.
diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl
deleted file mode 100644 (file)
index 800fe7a..0000000
+++ /dev/null
@@ -1,584 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE set PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
-       "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
-<set>
-  <setinfo>
-    <title>The 802.11 subsystems &ndash; for kernel developers</title>
-    <subtitle>
-      Explaining wireless 802.11 networking in the Linux kernel
-    </subtitle>
-
-    <copyright>
-      <year>2007-2009</year>
-      <holder>Johannes Berg</holder>
-    </copyright>
-
-    <authorgroup>
-      <author>
-        <firstname>Johannes</firstname>
-        <surname>Berg</surname>
-        <affiliation>
-          <address><email>johannes@sipsolutions.net</email></address>
-        </affiliation>
-      </author>
-    </authorgroup>
-
-    <legalnotice>
-      <para>
-        This documentation is free software; you can redistribute
-        it and/or modify it under the terms of the GNU General Public
-        License version 2 as published by the Free Software Foundation.
-      </para>
-      <para>
-        This documentation is distributed in the hope that it will be
-        useful, but WITHOUT ANY WARRANTY; without even the implied
-        warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-        See the GNU General Public License for more details.
-      </para>
-      <para>
-        You should have received a copy of the GNU General Public
-        License along with this documentation; if not, write to the Free
-        Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-        MA 02111-1307 USA
-      </para>
-      <para>
-        For more details see the file COPYING in the source
-        distribution of Linux.
-      </para>
-    </legalnotice>
-
-    <abstract>
-      <para>
-        These books attempt to give a description of the
-        various subsystems that play a role in 802.11 wireless
-        networking in Linux. Since these books are for kernel
-        developers they attempts to document the structures
-        and functions used in the kernel as well as giving a
-        higher-level overview.
-      </para>
-      <para>
-       The reader is expected to be familiar with the 802.11
-       standard as published by the IEEE in 802.11-2007 (or
-       possibly later versions). References to this standard
-       will be given as "802.11-2007 8.1.5".
-      </para>
-    </abstract>
-  </setinfo>
-  <book id="cfg80211-developers-guide">
-    <bookinfo>
-      <title>The cfg80211 subsystem</title>
-
-      <abstract>
-!Pinclude/net/cfg80211.h Introduction
-      </abstract>
-    </bookinfo>
-      <chapter>
-      <title>Device registration</title>
-!Pinclude/net/cfg80211.h Device registration
-!Finclude/net/cfg80211.h ieee80211_channel_flags
-!Finclude/net/cfg80211.h ieee80211_channel
-!Finclude/net/cfg80211.h ieee80211_rate_flags
-!Finclude/net/cfg80211.h ieee80211_rate
-!Finclude/net/cfg80211.h ieee80211_sta_ht_cap
-!Finclude/net/cfg80211.h ieee80211_supported_band
-!Finclude/net/cfg80211.h cfg80211_signal_type
-!Finclude/net/cfg80211.h wiphy_params_flags
-!Finclude/net/cfg80211.h wiphy_flags
-!Finclude/net/cfg80211.h wiphy
-!Finclude/net/cfg80211.h wireless_dev
-!Finclude/net/cfg80211.h wiphy_new
-!Finclude/net/cfg80211.h wiphy_register
-!Finclude/net/cfg80211.h wiphy_unregister
-!Finclude/net/cfg80211.h wiphy_free
-
-!Finclude/net/cfg80211.h wiphy_name
-!Finclude/net/cfg80211.h wiphy_dev
-!Finclude/net/cfg80211.h wiphy_priv
-!Finclude/net/cfg80211.h priv_to_wiphy
-!Finclude/net/cfg80211.h set_wiphy_dev
-!Finclude/net/cfg80211.h wdev_priv
-!Finclude/net/cfg80211.h ieee80211_iface_limit
-!Finclude/net/cfg80211.h ieee80211_iface_combination
-!Finclude/net/cfg80211.h cfg80211_check_combinations
-      </chapter>
-      <chapter>
-      <title>Actions and configuration</title>
-!Pinclude/net/cfg80211.h Actions and configuration
-!Finclude/net/cfg80211.h cfg80211_ops
-!Finclude/net/cfg80211.h vif_params
-!Finclude/net/cfg80211.h key_params
-!Finclude/net/cfg80211.h survey_info_flags
-!Finclude/net/cfg80211.h survey_info
-!Finclude/net/cfg80211.h cfg80211_beacon_data
-!Finclude/net/cfg80211.h cfg80211_ap_settings
-!Finclude/net/cfg80211.h station_parameters
-!Finclude/net/cfg80211.h rate_info_flags
-!Finclude/net/cfg80211.h rate_info
-!Finclude/net/cfg80211.h station_info
-!Finclude/net/cfg80211.h monitor_flags
-!Finclude/net/cfg80211.h mpath_info_flags
-!Finclude/net/cfg80211.h mpath_info
-!Finclude/net/cfg80211.h bss_parameters
-!Finclude/net/cfg80211.h ieee80211_txq_params
-!Finclude/net/cfg80211.h cfg80211_crypto_settings
-!Finclude/net/cfg80211.h cfg80211_auth_request
-!Finclude/net/cfg80211.h cfg80211_assoc_request
-!Finclude/net/cfg80211.h cfg80211_deauth_request
-!Finclude/net/cfg80211.h cfg80211_disassoc_request
-!Finclude/net/cfg80211.h cfg80211_ibss_params
-!Finclude/net/cfg80211.h cfg80211_connect_params
-!Finclude/net/cfg80211.h cfg80211_pmksa
-!Finclude/net/cfg80211.h cfg80211_rx_mlme_mgmt
-!Finclude/net/cfg80211.h cfg80211_auth_timeout
-!Finclude/net/cfg80211.h cfg80211_rx_assoc_resp
-!Finclude/net/cfg80211.h cfg80211_assoc_timeout
-!Finclude/net/cfg80211.h cfg80211_tx_mlme_mgmt
-!Finclude/net/cfg80211.h cfg80211_ibss_joined
-!Finclude/net/cfg80211.h cfg80211_connect_result
-!Finclude/net/cfg80211.h cfg80211_connect_bss
-!Finclude/net/cfg80211.h cfg80211_connect_timeout
-!Finclude/net/cfg80211.h cfg80211_roamed
-!Finclude/net/cfg80211.h cfg80211_disconnected
-!Finclude/net/cfg80211.h cfg80211_ready_on_channel
-!Finclude/net/cfg80211.h cfg80211_remain_on_channel_expired
-!Finclude/net/cfg80211.h cfg80211_new_sta
-!Finclude/net/cfg80211.h cfg80211_rx_mgmt
-!Finclude/net/cfg80211.h cfg80211_mgmt_tx_status
-!Finclude/net/cfg80211.h cfg80211_cqm_rssi_notify
-!Finclude/net/cfg80211.h cfg80211_cqm_pktloss_notify
-!Finclude/net/cfg80211.h cfg80211_michael_mic_failure
-      </chapter>
-      <chapter>
-      <title>Scanning and BSS list handling</title>
-!Pinclude/net/cfg80211.h Scanning and BSS list handling
-!Finclude/net/cfg80211.h cfg80211_ssid
-!Finclude/net/cfg80211.h cfg80211_scan_request
-!Finclude/net/cfg80211.h cfg80211_scan_done
-!Finclude/net/cfg80211.h cfg80211_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss
-!Finclude/net/cfg80211.h cfg80211_inform_bss_frame_data
-!Finclude/net/cfg80211.h cfg80211_inform_bss_data
-!Finclude/net/cfg80211.h cfg80211_unlink_bss
-!Finclude/net/cfg80211.h cfg80211_find_ie
-!Finclude/net/cfg80211.h ieee80211_bss_get_ie
-      </chapter>
-      <chapter>
-      <title>Utility functions</title>
-!Pinclude/net/cfg80211.h Utility functions
-!Finclude/net/cfg80211.h ieee80211_channel_to_frequency
-!Finclude/net/cfg80211.h ieee80211_frequency_to_channel
-!Finclude/net/cfg80211.h ieee80211_get_channel
-!Finclude/net/cfg80211.h ieee80211_get_response_rate
-!Finclude/net/cfg80211.h ieee80211_hdrlen
-!Finclude/net/cfg80211.h ieee80211_get_hdrlen_from_skb
-!Finclude/net/cfg80211.h ieee80211_radiotap_iterator
-      </chapter>
-      <chapter>
-      <title>Data path helpers</title>
-!Pinclude/net/cfg80211.h Data path helpers
-!Finclude/net/cfg80211.h ieee80211_data_to_8023
-!Finclude/net/cfg80211.h ieee80211_data_from_8023
-!Finclude/net/cfg80211.h ieee80211_amsdu_to_8023s
-!Finclude/net/cfg80211.h cfg80211_classify8021d
-      </chapter>
-      <chapter>
-      <title>Regulatory enforcement infrastructure</title>
-!Pinclude/net/cfg80211.h Regulatory enforcement infrastructure
-!Finclude/net/cfg80211.h regulatory_hint
-!Finclude/net/cfg80211.h wiphy_apply_custom_regulatory
-!Finclude/net/cfg80211.h freq_reg_info
-      </chapter>
-      <chapter>
-      <title>RFkill integration</title>
-!Pinclude/net/cfg80211.h RFkill integration
-!Finclude/net/cfg80211.h wiphy_rfkill_set_hw_state
-!Finclude/net/cfg80211.h wiphy_rfkill_start_polling
-!Finclude/net/cfg80211.h wiphy_rfkill_stop_polling
-      </chapter>
-      <chapter>
-      <title>Test mode</title>
-!Pinclude/net/cfg80211.h Test mode
-!Finclude/net/cfg80211.h cfg80211_testmode_alloc_reply_skb
-!Finclude/net/cfg80211.h cfg80211_testmode_reply
-!Finclude/net/cfg80211.h cfg80211_testmode_alloc_event_skb
-!Finclude/net/cfg80211.h cfg80211_testmode_event
-      </chapter>
-  </book>
-  <book id="mac80211-developers-guide">
-    <bookinfo>
-      <title>The mac80211 subsystem</title>
-      <abstract>
-!Pinclude/net/mac80211.h Introduction
-!Pinclude/net/mac80211.h Warning
-      </abstract>
-    </bookinfo>
-
-    <toc></toc>
-
-  <!--
-  Generally, this document shall be ordered by increasing complexity.
-  It is important to note that readers should be able to read only
-  the first few sections to get a working driver and only advanced
-  usage should require reading the full document.
-  -->
-
-    <part>
-      <title>The basic mac80211 driver interface</title>
-      <partintro>
-        <para>
-          You should read and understand the information contained
-          within this part of the book while implementing a driver.
-          In some chapters, advanced usage is noted, that may be
-          skipped at first.
-        </para>
-        <para>
-          This part of the book only covers station and monitor mode
-          functionality, additional information required to implement
-          the other modes is covered in the second part of the book.
-        </para>
-      </partintro>
-
-      <chapter id="basics">
-        <title>Basic hardware handling</title>
-        <para>TBD</para>
-        <para>
-          This chapter shall contain information on getting a hw
-          struct allocated and registered with mac80211.
-        </para>
-        <para>
-          Since it is required to allocate rates/modes before registering
-          a hw struct, this chapter shall also contain information on setting
-          up the rate/mode structs.
-        </para>
-        <para>
-          Additionally, some discussion about the callbacks and
-          the general programming model should be in here, including
-          the definition of ieee80211_ops which will be referred to
-          a lot.
-        </para>
-        <para>
-          Finally, a discussion of hardware capabilities should be done
-          with references to other parts of the book.
-        </para>
-  <!-- intentionally multiple !F lines to get proper order -->
-!Finclude/net/mac80211.h ieee80211_hw
-!Finclude/net/mac80211.h ieee80211_hw_flags
-!Finclude/net/mac80211.h SET_IEEE80211_DEV
-!Finclude/net/mac80211.h SET_IEEE80211_PERM_ADDR
-!Finclude/net/mac80211.h ieee80211_ops
-!Finclude/net/mac80211.h ieee80211_alloc_hw
-!Finclude/net/mac80211.h ieee80211_register_hw
-!Finclude/net/mac80211.h ieee80211_unregister_hw
-!Finclude/net/mac80211.h ieee80211_free_hw
-      </chapter>
-
-      <chapter id="phy-handling">
-        <title>PHY configuration</title>
-        <para>TBD</para>
-        <para>
-          This chapter should describe PHY handling including
-          start/stop callbacks and the various structures used.
-        </para>
-!Finclude/net/mac80211.h ieee80211_conf
-!Finclude/net/mac80211.h ieee80211_conf_flags
-      </chapter>
-
-      <chapter id="iface-handling">
-        <title>Virtual interfaces</title>
-        <para>TBD</para>
-        <para>
-          This chapter should describe virtual interface basics
-          that are relevant to the driver (VLANs, MGMT etc are not.)
-          It should explain the use of the add_iface/remove_iface
-          callbacks as well as the interface configuration callbacks.
-        </para>
-        <para>Things related to AP mode should be discussed there.</para>
-        <para>
-          Things related to supporting multiple interfaces should be
-          in the appropriate chapter, a BIG FAT note should be here about
-          this though and the recommendation to allow only a single
-          interface in STA mode at first!
-        </para>
-!Finclude/net/mac80211.h ieee80211_vif
-      </chapter>
-
-      <chapter id="rx-tx">
-        <title>Receive and transmit processing</title>
-        <sect1>
-          <title>what should be here</title>
-          <para>TBD</para>
-          <para>
-            This should describe the receive and transmit
-            paths in mac80211/the drivers as well as
-            transmit status handling.
-          </para>
-        </sect1>
-        <sect1>
-          <title>Frame format</title>
-!Pinclude/net/mac80211.h Frame format
-        </sect1>
-        <sect1>
-          <title>Packet alignment</title>
-!Pnet/mac80211/rx.c Packet alignment
-        </sect1>
-        <sect1>
-          <title>Calling into mac80211 from interrupts</title>
-!Pinclude/net/mac80211.h Calling mac80211 from interrupts
-        </sect1>
-        <sect1>
-          <title>functions/definitions</title>
-!Finclude/net/mac80211.h ieee80211_rx_status
-!Finclude/net/mac80211.h mac80211_rx_flags
-!Finclude/net/mac80211.h mac80211_tx_info_flags
-!Finclude/net/mac80211.h mac80211_tx_control_flags
-!Finclude/net/mac80211.h mac80211_rate_control_flags
-!Finclude/net/mac80211.h ieee80211_tx_rate
-!Finclude/net/mac80211.h ieee80211_tx_info
-!Finclude/net/mac80211.h ieee80211_tx_info_clear_status
-!Finclude/net/mac80211.h ieee80211_rx
-!Finclude/net/mac80211.h ieee80211_rx_ni
-!Finclude/net/mac80211.h ieee80211_rx_irqsafe
-!Finclude/net/mac80211.h ieee80211_tx_status
-!Finclude/net/mac80211.h ieee80211_tx_status_ni
-!Finclude/net/mac80211.h ieee80211_tx_status_irqsafe
-!Finclude/net/mac80211.h ieee80211_rts_get
-!Finclude/net/mac80211.h ieee80211_rts_duration
-!Finclude/net/mac80211.h ieee80211_ctstoself_get
-!Finclude/net/mac80211.h ieee80211_ctstoself_duration
-!Finclude/net/mac80211.h ieee80211_generic_frame_duration
-!Finclude/net/mac80211.h ieee80211_wake_queue
-!Finclude/net/mac80211.h ieee80211_stop_queue
-!Finclude/net/mac80211.h ieee80211_wake_queues
-!Finclude/net/mac80211.h ieee80211_stop_queues
-!Finclude/net/mac80211.h ieee80211_queue_stopped
-        </sect1>
-      </chapter>
-
-      <chapter id="filters">
-        <title>Frame filtering</title>
-!Pinclude/net/mac80211.h Frame filtering
-!Finclude/net/mac80211.h ieee80211_filter_flags
-      </chapter>
-
-      <chapter id="workqueue">
-        <title>The mac80211 workqueue</title>
-!Pinclude/net/mac80211.h mac80211 workqueue
-!Finclude/net/mac80211.h ieee80211_queue_work
-!Finclude/net/mac80211.h ieee80211_queue_delayed_work
-      </chapter>
-    </part>
-
-    <part id="advanced">
-      <title>Advanced driver interface</title>
-      <partintro>
-        <para>
-         Information contained within this part of the book is
-         of interest only for advanced interaction of mac80211
-         with drivers to exploit more hardware capabilities and
-         improve performance.
-        </para>
-      </partintro>
-
-      <chapter id="led-support">
-        <title>LED support</title>
-        <para>
-         Mac80211 supports various ways of blinking LEDs. Wherever possible,
-         device LEDs should be exposed as LED class devices and hooked up to
-         the appropriate trigger, which will then be triggered appropriately
-         by mac80211.
-        </para>
-!Finclude/net/mac80211.h ieee80211_get_tx_led_name
-!Finclude/net/mac80211.h ieee80211_get_rx_led_name
-!Finclude/net/mac80211.h ieee80211_get_assoc_led_name
-!Finclude/net/mac80211.h ieee80211_get_radio_led_name
-!Finclude/net/mac80211.h ieee80211_tpt_blink
-!Finclude/net/mac80211.h ieee80211_tpt_led_trigger_flags
-!Finclude/net/mac80211.h ieee80211_create_tpt_led_trigger
-      </chapter>
-
-      <chapter id="hardware-crypto-offload">
-        <title>Hardware crypto acceleration</title>
-!Pinclude/net/mac80211.h Hardware crypto acceleration
-  <!-- intentionally multiple !F lines to get proper order -->
-!Finclude/net/mac80211.h set_key_cmd
-!Finclude/net/mac80211.h ieee80211_key_conf
-!Finclude/net/mac80211.h ieee80211_key_flags
-!Finclude/net/mac80211.h ieee80211_get_tkip_p1k
-!Finclude/net/mac80211.h ieee80211_get_tkip_p1k_iv
-!Finclude/net/mac80211.h ieee80211_get_tkip_p2k
-      </chapter>
-
-      <chapter id="powersave">
-        <title>Powersave support</title>
-!Pinclude/net/mac80211.h Powersave support
-      </chapter>
-
-      <chapter id="beacon-filter">
-        <title>Beacon filter support</title>
-!Pinclude/net/mac80211.h Beacon filter support
-!Finclude/net/mac80211.h ieee80211_beacon_loss
-      </chapter>
-
-      <chapter id="qos">
-        <title>Multiple queues and QoS support</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_tx_queue_params
-      </chapter>
-
-      <chapter id="AP">
-        <title>Access point mode support</title>
-        <para>TBD</para>
-        <para>Some parts of the if_conf should be discussed here instead</para>
-        <para>
-          Insert notes about VLAN interfaces with hw crypto here or
-          in the hw crypto chapter.
-        </para>
-      <section id="ps-client">
-        <title>support for powersaving clients</title>
-!Pinclude/net/mac80211.h AP support for powersaving clients
-!Finclude/net/mac80211.h ieee80211_get_buffered_bc
-!Finclude/net/mac80211.h ieee80211_beacon_get
-!Finclude/net/mac80211.h ieee80211_sta_eosp
-!Finclude/net/mac80211.h ieee80211_frame_release_type
-!Finclude/net/mac80211.h ieee80211_sta_ps_transition
-!Finclude/net/mac80211.h ieee80211_sta_ps_transition_ni
-!Finclude/net/mac80211.h ieee80211_sta_set_buffered
-!Finclude/net/mac80211.h ieee80211_sta_block_awake
-      </section>
-      </chapter>
-
-      <chapter id="multi-iface">
-        <title>Supporting multiple virtual interfaces</title>
-        <para>TBD</para>
-        <para>
-          Note: WDS with identical MAC address should almost always be OK
-        </para>
-        <para>
-          Insert notes about having multiple virtual interfaces with
-          different MAC addresses here, note which configurations are
-          supported by mac80211, add notes about supporting hw crypto
-          with it.
-        </para>
-!Finclude/net/mac80211.h ieee80211_iterate_active_interfaces
-!Finclude/net/mac80211.h ieee80211_iterate_active_interfaces_atomic
-      </chapter>
-
-      <chapter id="station-handling">
-        <title>Station handling</title>
-        <para>TODO</para>
-!Finclude/net/mac80211.h ieee80211_sta
-!Finclude/net/mac80211.h sta_notify_cmd
-!Finclude/net/mac80211.h ieee80211_find_sta
-!Finclude/net/mac80211.h ieee80211_find_sta_by_ifaddr
-      </chapter>
-
-      <chapter id="hardware-scan-offload">
-        <title>Hardware scan offload</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_scan_completed
-      </chapter>
-
-      <chapter id="aggregation">
-        <title>Aggregation</title>
-        <sect1>
-          <title>TX A-MPDU aggregation</title>
-!Pnet/mac80211/agg-tx.c TX A-MPDU aggregation
-!Cnet/mac80211/agg-tx.c
-        </sect1>
-        <sect1>
-          <title>RX A-MPDU aggregation</title>
-!Pnet/mac80211/agg-rx.c RX A-MPDU aggregation
-!Cnet/mac80211/agg-rx.c
-!Finclude/net/mac80211.h ieee80211_ampdu_mlme_action
-        </sect1>
-      </chapter>
-
-      <chapter id="smps">
-        <title>Spatial Multiplexing Powersave (SMPS)</title>
-!Pinclude/net/mac80211.h Spatial multiplexing power save
-!Finclude/net/mac80211.h ieee80211_request_smps
-!Finclude/net/mac80211.h ieee80211_smps_mode
-      </chapter>
-    </part>
-
-    <part id="rate-control">
-      <title>Rate control interface</title>
-      <partintro>
-        <para>TBD</para>
-        <para>
-         This part of the book describes the rate control algorithm
-         interface and how it relates to mac80211 and drivers.
-        </para>
-      </partintro>
-      <chapter id="ratecontrol-api">
-        <title>Rate Control API</title>
-        <para>TBD</para>
-!Finclude/net/mac80211.h ieee80211_start_tx_ba_session
-!Finclude/net/mac80211.h ieee80211_start_tx_ba_cb_irqsafe
-!Finclude/net/mac80211.h ieee80211_stop_tx_ba_session
-!Finclude/net/mac80211.h ieee80211_stop_tx_ba_cb_irqsafe
-!Finclude/net/mac80211.h ieee80211_rate_control_changed
-!Finclude/net/mac80211.h ieee80211_tx_rate_control
-!Finclude/net/mac80211.h rate_control_send_low
-      </chapter>
-    </part>
-
-    <part id="internal">
-      <title>Internals</title>
-      <partintro>
-        <para>TBD</para>
-        <para>
-         This part of the book describes mac80211 internals.
-        </para>
-      </partintro>
-
-      <chapter id="key-handling">
-        <title>Key handling</title>
-        <sect1>
-          <title>Key handling basics</title>
-!Pnet/mac80211/key.c Key handling basics
-        </sect1>
-        <sect1>
-          <title>MORE TBD</title>
-          <para>TBD</para>
-        </sect1>
-      </chapter>
-
-      <chapter id="rx-processing">
-        <title>Receive processing</title>
-        <para>TBD</para>
-      </chapter>
-
-      <chapter id="tx-processing">
-        <title>Transmit processing</title>
-        <para>TBD</para>
-      </chapter>
-
-      <chapter id="sta-info">
-        <title>Station info handling</title>
-        <sect1>
-          <title>Programming information</title>
-!Fnet/mac80211/sta_info.h sta_info
-!Fnet/mac80211/sta_info.h ieee80211_sta_info_flags
-        </sect1>
-        <sect1>
-          <title>STA information lifetime rules</title>
-!Pnet/mac80211/sta_info.c STA information lifetime rules
-        </sect1>
-      </chapter>
-
-      <chapter id="aggregation-internals">
-        <title>Aggregation</title>
-!Fnet/mac80211/sta_info.h sta_ampdu_mlme
-!Fnet/mac80211/sta_info.h tid_ampdu_tx
-!Fnet/mac80211/sta_info.h tid_ampdu_rx
-      </chapter>
-
-      <chapter id="synchronisation">
-        <title>Synchronisation</title>
-        <para>TBD</para>
-        <para>Locking, lots of RCU</para>
-      </chapter>
-    </part>
-  </book>
-</set>
index 736f591..fdf8232 100644 (file)
@@ -12,7 +12,7 @@ DOCBOOKS := z8530book.xml  \
            kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
            gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
            genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
-           80211.xml debugobjects.xml sh.xml regulator.xml \
+           debugobjects.xml sh.xml regulator.xml \
            alsa-driver-api.xml writing-an-alsa-driver.xml \
            tracepoint.xml w1.xml \
            writing_musb_glue_layer.xml crypto-API.xml iio.xml
index 589b40c..2a27227 100644 (file)
@@ -483,7 +483,7 @@ printk(KERN_INFO "my ip: %pI4\n", &amp;ipaddress);
     <function>get_user()</function>
     /
     <function>put_user()</function>
-    <filename class="headerfile">include/asm/uaccess.h</filename>
+    <filename class="headerfile">include/linux/uaccess.h</filename>
    </title>  
 
    <para>
index de955e1..c2a4691 100644 (file)
@@ -1,3 +1 @@
-subdir-y := accounting auxdisplay blackfin \
-       filesystems filesystems ia64 laptops mic misc-devices \
-       networking pcmcia prctl ptp timers vDSO watchdog
+subdir-y :=
index bf90611..238e9f6 100644 (file)
@@ -57,7 +57,7 @@ Call Trace:
  [<ffffffff817db154>] kernel_thread_helper+0x4/0x10
  [<ffffffff81066430>] ? finish_task_switch+0x80/0x110
  [<ffffffff817d9c04>] ? retint_restore_args+0xe/0xe
- [<ffffffff81097510>] ? __init_kthread_worker+0x70/0x70
+ [<ffffffff81097510>] ? __kthread_init_worker+0x70/0x70
  [<ffffffff817db150>] ? gs_change+0xb/0xb
 
 Line 2776 of block/cfq-iosched.c in v3.0-rc5 is as follows:
diff --git a/Documentation/accounting/.gitignore b/Documentation/accounting/.gitignore
deleted file mode 100644 (file)
index 8648520..0000000
+++ /dev/null
@@ -1 +0,0 @@
-getdelays
diff --git a/Documentation/accounting/Makefile b/Documentation/accounting/Makefile
deleted file mode 100644 (file)
index 7e232cb..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := getdelays
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_getdelays.o += -I$(objtree)/usr/include
index 8a12f07..042ea59 100644 (file)
@@ -54,9 +54,9 @@ are sent to userspace without requiring a command. If it is the last exiting
 task of a thread group, the per-tgid statistics are also sent. More details
 are given in the taskstats interface description.
 
-The getdelays.c userspace utility in this directory allows simple commands to
-be run and the corresponding delay statistics to be displayed. It also serves
-as an example of using the taskstats interface.
+The getdelays.c userspace utility in tools/accounting directory allows simple
+commands to be run and the corresponding delay statistics to be displayed. It
+also serves as an example of using the taskstats interface.
 
 Usage
 -----
diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c
deleted file mode 100644 (file)
index b5ca536..0000000
+++ /dev/null
@@ -1,550 +0,0 @@
-/* getdelays.c
- *
- * Utility to get per-pid and per-tgid delay accounting statistics
- * Also illustrates usage of the taskstats interface
- *
- * Copyright (C) Shailabh Nagar, IBM Corp. 2005
- * Copyright (C) Balbir Singh, IBM Corp. 2006
- * Copyright (c) Jay Lan, SGI. 2006
- *
- * Compile with
- *     gcc -I/usr/src/linux/include getdelays.c -o getdelays
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <unistd.h>
-#include <poll.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <signal.h>
-
-#include <linux/genetlink.h>
-#include <linux/taskstats.h>
-#include <linux/cgroupstats.h>
-
-/*
- * Generic macros for dealing with netlink sockets. Might be duplicated
- * elsewhere. It is recommended that commercial grade applications use
- * libnl or libnetlink and use the interfaces provided by the library
- */
-#define GENLMSG_DATA(glh)      ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
-#define GENLMSG_PAYLOAD(glh)   (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
-#define NLA_DATA(na)           ((void *)((char*)(na) + NLA_HDRLEN))
-#define NLA_PAYLOAD(len)       (len - NLA_HDRLEN)
-
-#define err(code, fmt, arg...)                 \
-       do {                                    \
-               fprintf(stderr, fmt, ##arg);    \
-               exit(code);                     \
-       } while (0)
-
-int done;
-int rcvbufsz;
-char name[100];
-int dbg;
-int print_delays;
-int print_io_accounting;
-int print_task_context_switch_counts;
-
-#define PRINTF(fmt, arg...) {                  \
-           if (dbg) {                          \
-               printf(fmt, ##arg);             \
-           }                                   \
-       }
-
-/* Maximum size of response requested or message sent */
-#define MAX_MSG_SIZE   1024
-/* Maximum number of cpus expected to be specified in a cpumask */
-#define MAX_CPUS       32
-
-struct msgtemplate {
-       struct nlmsghdr n;
-       struct genlmsghdr g;
-       char buf[MAX_MSG_SIZE];
-};
-
-char cpumask[100+6*MAX_CPUS];
-
-static void usage(void)
-{
-       fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] "
-                       "[-m cpumask] [-t tgid] [-p pid]\n");
-       fprintf(stderr, "  -d: print delayacct stats\n");
-       fprintf(stderr, "  -i: print IO accounting (works only with -p)\n");
-       fprintf(stderr, "  -l: listen forever\n");
-       fprintf(stderr, "  -v: debug on\n");
-       fprintf(stderr, "  -C: container path\n");
-}
-
-/*
- * Create a raw netlink socket and bind
- */
-static int create_nl_socket(int protocol)
-{
-       int fd;
-       struct sockaddr_nl local;
-
-       fd = socket(AF_NETLINK, SOCK_RAW, protocol);
-       if (fd < 0)
-               return -1;
-
-       if (rcvbufsz)
-               if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
-                               &rcvbufsz, sizeof(rcvbufsz)) < 0) {
-                       fprintf(stderr, "Unable to set socket rcv buf size to %d\n",
-                               rcvbufsz);
-                       goto error;
-               }
-
-       memset(&local, 0, sizeof(local));
-       local.nl_family = AF_NETLINK;
-
-       if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
-               goto error;
-
-       return fd;
-error:
-       close(fd);
-       return -1;
-}
-
-
-static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
-            __u8 genl_cmd, __u16 nla_type,
-            void *nla_data, int nla_len)
-{
-       struct nlattr *na;
-       struct sockaddr_nl nladdr;
-       int r, buflen;
-       char *buf;
-
-       struct msgtemplate msg;
-
-       msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
-       msg.n.nlmsg_type = nlmsg_type;
-       msg.n.nlmsg_flags = NLM_F_REQUEST;
-       msg.n.nlmsg_seq = 0;
-       msg.n.nlmsg_pid = nlmsg_pid;
-       msg.g.cmd = genl_cmd;
-       msg.g.version = 0x1;
-       na = (struct nlattr *) GENLMSG_DATA(&msg);
-       na->nla_type = nla_type;
-       na->nla_len = nla_len + 1 + NLA_HDRLEN;
-       memcpy(NLA_DATA(na), nla_data, nla_len);
-       msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
-
-       buf = (char *) &msg;
-       buflen = msg.n.nlmsg_len ;
-       memset(&nladdr, 0, sizeof(nladdr));
-       nladdr.nl_family = AF_NETLINK;
-       while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr,
-                          sizeof(nladdr))) < buflen) {
-               if (r > 0) {
-                       buf += r;
-                       buflen -= r;
-               } else if (errno != EAGAIN)
-                       return -1;
-       }
-       return 0;
-}
-
-
-/*
- * Probe the controller in genetlink to find the family id
- * for the TASKSTATS family
- */
-static int get_family_id(int sd)
-{
-       struct {
-               struct nlmsghdr n;
-               struct genlmsghdr g;
-               char buf[256];
-       } ans;
-
-       int id = 0, rc;
-       struct nlattr *na;
-       int rep_len;
-
-       strcpy(name, TASKSTATS_GENL_NAME);
-       rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY,
-                       CTRL_ATTR_FAMILY_NAME, (void *)name,
-                       strlen(TASKSTATS_GENL_NAME)+1);
-       if (rc < 0)
-               return 0;       /* sendto() failure? */
-
-       rep_len = recv(sd, &ans, sizeof(ans), 0);
-       if (ans.n.nlmsg_type == NLMSG_ERROR ||
-           (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len))
-               return 0;
-
-       na = (struct nlattr *) GENLMSG_DATA(&ans);
-       na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
-       if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
-               id = *(__u16 *) NLA_DATA(na);
-       }
-       return id;
-}
-
-#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
-
-static void print_delayacct(struct taskstats *t)
-{
-       printf("\n\nCPU   %15s%15s%15s%15s%15s\n"
-              "      %15llu%15llu%15llu%15llu%15.3fms\n"
-              "IO    %15s%15s%15s\n"
-              "      %15llu%15llu%15llums\n"
-              "SWAP  %15s%15s%15s\n"
-              "      %15llu%15llu%15llums\n"
-              "RECLAIM  %12s%15s%15s\n"
-              "      %15llu%15llu%15llums\n",
-              "count", "real total", "virtual total",
-              "delay total", "delay average",
-              (unsigned long long)t->cpu_count,
-              (unsigned long long)t->cpu_run_real_total,
-              (unsigned long long)t->cpu_run_virtual_total,
-              (unsigned long long)t->cpu_delay_total,
-              average_ms((double)t->cpu_delay_total, t->cpu_count),
-              "count", "delay total", "delay average",
-              (unsigned long long)t->blkio_count,
-              (unsigned long long)t->blkio_delay_total,
-              average_ms(t->blkio_delay_total, t->blkio_count),
-              "count", "delay total", "delay average",
-              (unsigned long long)t->swapin_count,
-              (unsigned long long)t->swapin_delay_total,
-              average_ms(t->swapin_delay_total, t->swapin_count),
-              "count", "delay total", "delay average",
-              (unsigned long long)t->freepages_count,
-              (unsigned long long)t->freepages_delay_total,
-              average_ms(t->freepages_delay_total, t->freepages_count));
-}
-
-static void task_context_switch_counts(struct taskstats *t)
-{
-       printf("\n\nTask   %15s%15s\n"
-              "       %15llu%15llu\n",
-              "voluntary", "nonvoluntary",
-              (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw);
-}
-
-static void print_cgroupstats(struct cgroupstats *c)
-{
-       printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, "
-               "uninterruptible %llu\n", (unsigned long long)c->nr_sleeping,
-               (unsigned long long)c->nr_io_wait,
-               (unsigned long long)c->nr_running,
-               (unsigned long long)c->nr_stopped,
-               (unsigned long long)c->nr_uninterruptible);
-}
-
-
-static void print_ioacct(struct taskstats *t)
-{
-       printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n",
-               t->ac_comm,
-               (unsigned long long)t->read_bytes,
-               (unsigned long long)t->write_bytes,
-               (unsigned long long)t->cancelled_write_bytes);
-}
-
-int main(int argc, char *argv[])
-{
-       int c, rc, rep_len, aggr_len, len2;
-       int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC;
-       __u16 id;
-       __u32 mypid;
-
-       struct nlattr *na;
-       int nl_sd = -1;
-       int len = 0;
-       pid_t tid = 0;
-       pid_t rtid = 0;
-
-       int fd = 0;
-       int count = 0;
-       int write_file = 0;
-       int maskset = 0;
-       char *logfile = NULL;
-       int loop = 0;
-       int containerset = 0;
-       char *containerpath = NULL;
-       int cfd = 0;
-       int forking = 0;
-       sigset_t sigset;
-
-       struct msgtemplate msg;
-
-       while (!forking) {
-               c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:");
-               if (c < 0)
-                       break;
-
-               switch (c) {
-               case 'd':
-                       printf("print delayacct stats ON\n");
-                       print_delays = 1;
-                       break;
-               case 'i':
-                       printf("printing IO accounting\n");
-                       print_io_accounting = 1;
-                       break;
-               case 'q':
-                       printf("printing task/process context switch rates\n");
-                       print_task_context_switch_counts = 1;
-                       break;
-               case 'C':
-                       containerset = 1;
-                       containerpath = optarg;
-                       break;
-               case 'w':
-                       logfile = strdup(optarg);
-                       printf("write to file %s\n", logfile);
-                       write_file = 1;
-                       break;
-               case 'r':
-                       rcvbufsz = atoi(optarg);
-                       printf("receive buf size %d\n", rcvbufsz);
-                       if (rcvbufsz < 0)
-                               err(1, "Invalid rcv buf size\n");
-                       break;
-               case 'm':
-                       strncpy(cpumask, optarg, sizeof(cpumask));
-                       cpumask[sizeof(cpumask) - 1] = '\0';
-                       maskset = 1;
-                       printf("cpumask %s maskset %d\n", cpumask, maskset);
-                       break;
-               case 't':
-                       tid = atoi(optarg);
-                       if (!tid)
-                               err(1, "Invalid tgid\n");
-                       cmd_type = TASKSTATS_CMD_ATTR_TGID;
-                       break;
-               case 'p':
-                       tid = atoi(optarg);
-                       if (!tid)
-                               err(1, "Invalid pid\n");
-                       cmd_type = TASKSTATS_CMD_ATTR_PID;
-                       break;
-               case 'c':
-
-                       /* Block SIGCHLD for sigwait() later */
-                       if (sigemptyset(&sigset) == -1)
-                               err(1, "Failed to empty sigset");
-                       if (sigaddset(&sigset, SIGCHLD))
-                               err(1, "Failed to set sigchld in sigset");
-                       sigprocmask(SIG_BLOCK, &sigset, NULL);
-
-                       /* fork/exec a child */
-                       tid = fork();
-                       if (tid < 0)
-                               err(1, "Fork failed\n");
-                       if (tid == 0)
-                               if (execvp(argv[optind - 1],
-                                   &argv[optind - 1]) < 0)
-                                       exit(-1);
-
-                       /* Set the command type and avoid further processing */
-                       cmd_type = TASKSTATS_CMD_ATTR_PID;
-                       forking = 1;
-                       break;
-               case 'v':
-                       printf("debug on\n");
-                       dbg = 1;
-                       break;
-               case 'l':
-                       printf("listen forever\n");
-                       loop = 1;
-                       break;
-               default:
-                       usage();
-                       exit(-1);
-               }
-       }
-
-       if (write_file) {
-               fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC,
-                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-               if (fd == -1) {
-                       perror("Cannot open output file\n");
-                       exit(1);
-               }
-       }
-
-       nl_sd = create_nl_socket(NETLINK_GENERIC);
-       if (nl_sd < 0)
-               err(1, "error creating Netlink socket\n");
-
-
-       mypid = getpid();
-       id = get_family_id(nl_sd);
-       if (!id) {
-               fprintf(stderr, "Error getting family id, errno %d\n", errno);
-               goto err;
-       }
-       PRINTF("family id %d\n", id);
-
-       if (maskset) {
-               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
-                             TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
-                             &cpumask, strlen(cpumask) + 1);
-               PRINTF("Sent register cpumask, retval %d\n", rc);
-               if (rc < 0) {
-                       fprintf(stderr, "error sending register cpumask\n");
-                       goto err;
-               }
-       }
-
-       if (tid && containerset) {
-               fprintf(stderr, "Select either -t or -C, not both\n");
-               goto err;
-       }
-
-       /*
-        * If we forked a child, wait for it to exit. Cannot use waitpid()
-        * as all the delicious data would be reaped as part of the wait
-        */
-       if (tid && forking) {
-               int sig_received;
-               sigwait(&sigset, &sig_received);
-       }
-
-       if (tid) {
-               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
-                             cmd_type, &tid, sizeof(__u32));
-               PRINTF("Sent pid/tgid, retval %d\n", rc);
-               if (rc < 0) {
-                       fprintf(stderr, "error sending tid/tgid cmd\n");
-                       goto done;
-               }
-       }
-
-       if (containerset) {
-               cfd = open(containerpath, O_RDONLY);
-               if (cfd < 0) {
-                       perror("error opening container file");
-                       goto err;
-               }
-               rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET,
-                             CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32));
-               if (rc < 0) {
-                       perror("error sending cgroupstats command");
-                       goto err;
-               }
-       }
-       if (!maskset && !tid && !containerset) {
-               usage();
-               goto err;
-       }
-
-       do {
-               rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
-               PRINTF("received %d bytes\n", rep_len);
-
-               if (rep_len < 0) {
-                       fprintf(stderr, "nonfatal reply error: errno %d\n",
-                               errno);
-                       continue;
-               }
-               if (msg.n.nlmsg_type == NLMSG_ERROR ||
-                   !NLMSG_OK((&msg.n), rep_len)) {
-                       struct nlmsgerr *err = NLMSG_DATA(&msg);
-                       fprintf(stderr, "fatal reply error,  errno %d\n",
-                               err->error);
-                       goto done;
-               }
-
-               PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
-                      sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len);
-
-
-               rep_len = GENLMSG_PAYLOAD(&msg.n);
-
-               na = (struct nlattr *) GENLMSG_DATA(&msg);
-               len = 0;
-               while (len < rep_len) {
-                       len += NLA_ALIGN(na->nla_len);
-                       switch (na->nla_type) {
-                       case TASKSTATS_TYPE_AGGR_TGID:
-                               /* Fall through */
-                       case TASKSTATS_TYPE_AGGR_PID:
-                               aggr_len = NLA_PAYLOAD(na->nla_len);
-                               len2 = 0;
-                               /* For nested attributes, na follows */
-                               na = (struct nlattr *) NLA_DATA(na);
-                               done = 0;
-                               while (len2 < aggr_len) {
-                                       switch (na->nla_type) {
-                                       case TASKSTATS_TYPE_PID:
-                                               rtid = *(int *) NLA_DATA(na);
-                                               if (print_delays)
-                                                       printf("PID\t%d\n", rtid);
-                                               break;
-                                       case TASKSTATS_TYPE_TGID:
-                                               rtid = *(int *) NLA_DATA(na);
-                                               if (print_delays)
-                                                       printf("TGID\t%d\n", rtid);
-                                               break;
-                                       case TASKSTATS_TYPE_STATS:
-                                               count++;
-                                               if (print_delays)
-                                                       print_delayacct((struct taskstats *) NLA_DATA(na));
-                                               if (print_io_accounting)
-                                                       print_ioacct((struct taskstats *) NLA_DATA(na));
-                                               if (print_task_context_switch_counts)
-                                                       task_context_switch_counts((struct taskstats *) NLA_DATA(na));
-                                               if (fd) {
-                                                       if (write(fd, NLA_DATA(na), na->nla_len) < 0) {
-                                                               err(1,"write error\n");
-                                                       }
-                                               }
-                                               if (!loop)
-                                                       goto done;
-                                               break;
-                                       case TASKSTATS_TYPE_NULL:
-                                               break;
-                                       default:
-                                               fprintf(stderr, "Unknown nested"
-                                                       " nla_type %d\n",
-                                                       na->nla_type);
-                                               break;
-                                       }
-                                       len2 += NLA_ALIGN(na->nla_len);
-                                       na = (struct nlattr *)((char *)na +
-                                                              NLA_ALIGN(na->nla_len));
-                               }
-                               break;
-
-                       case CGROUPSTATS_TYPE_CGROUP_STATS:
-                               print_cgroupstats(NLA_DATA(na));
-                               break;
-                       default:
-                               fprintf(stderr, "Unknown nla_type %d\n",
-                                       na->nla_type);
-                       case TASKSTATS_TYPE_NULL:
-                               break;
-                       }
-                       na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
-               }
-       } while (loop);
-done:
-       if (maskset) {
-               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
-                             TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
-                             &cpumask, strlen(cpumask) + 1);
-               printf("Sent deregister mask, retval %d\n", rc);
-               if (rc < 0)
-                       err(rc, "error sending deregister cpumask\n");
-       }
-err:
-       close(nl_sd);
-       if (fd)
-               close(fd);
-       if (cfd)
-               close(cfd);
-       return 0;
-}
index dea011c..b6e69fd 100644 (file)
@@ -8,8 +8,6 @@ Interrupts
        - ARM Interrupt subsystem documentation
 IXP4xx
        - Intel IXP4xx Network processor.
-Makefile
-       - Build sourcefiles as part of the Documentation-build for arm
 Netwinder
        - Netwinder specific documentation
 Porting
diff --git a/Documentation/auxdisplay/.gitignore b/Documentation/auxdisplay/.gitignore
deleted file mode 100644 (file)
index 7af2228..0000000
+++ /dev/null
@@ -1 +0,0 @@
-cfag12864b-example
diff --git a/Documentation/auxdisplay/Makefile b/Documentation/auxdisplay/Makefile
deleted file mode 100644 (file)
index ada4dac..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := cfag12864b-example
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_cfag12864b-example.o += -I$(objtree)/usr/include
index eb7be39..12fd51b 100644 (file)
@@ -101,5 +101,5 @@ Although the LCD won't get updated until the next refresh time arrives.
 Also, you can mmap the framebuffer: open & mmap, munmap & close...
 which is the best option for most uses.
 
-Check Documentation/auxdisplay/cfag12864b-example.c
+Check samples/auxdisplay/cfag12864b-example.c
 for a real working userspace complete program with usage examples.
diff --git a/Documentation/auxdisplay/cfag12864b-example.c b/Documentation/auxdisplay/cfag12864b-example.c
deleted file mode 100644 (file)
index e7823ff..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- *    Filename: cfag12864b-example.c
- *     Version: 0.1.0
- * Description: cfag12864b LCD userspace example program
- *     License: GPLv2
- *
- *      Author: Copyright (C) Miguel Ojeda Sandonis
- *        Date: 2006-10-31
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- */
-
-/*
- * ------------------------
- * start of cfag12864b code
- * ------------------------
- */
-
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#define CFAG12864B_WIDTH               (128)
-#define CFAG12864B_HEIGHT              (64)
-#define CFAG12864B_SIZE                        (128 * 64 / 8)
-#define CFAG12864B_BPB                 (8)
-#define CFAG12864B_ADDRESS(x, y)       ((y) * CFAG12864B_WIDTH / \
-                                       CFAG12864B_BPB + (x) / CFAG12864B_BPB)
-#define CFAG12864B_BIT(n)              (((unsigned char) 1) << (n))
-
-#undef CFAG12864B_DOCHECK
-#ifdef CFAG12864B_DOCHECK
-       #define CFAG12864B_CHECK(x, y)          ((x) < CFAG12864B_WIDTH && \
-                                               (y) < CFAG12864B_HEIGHT)
-#else
-       #define CFAG12864B_CHECK(x, y)          (1)
-#endif
-
-int cfag12864b_fd;
-unsigned char * cfag12864b_mem;
-unsigned char cfag12864b_buffer[CFAG12864B_SIZE];
-
-/*
- * init a cfag12864b framebuffer device
- *
- * No error:       return = 0
- * Unable to open: return = -1
- * Unable to mmap: return = -2
- */
-static int cfag12864b_init(char *path)
-{
-       cfag12864b_fd = open(path, O_RDWR);
-       if (cfag12864b_fd == -1)
-               return -1;
-
-       cfag12864b_mem = mmap(0, CFAG12864B_SIZE, PROT_READ | PROT_WRITE,
-               MAP_SHARED, cfag12864b_fd, 0);
-       if (cfag12864b_mem == MAP_FAILED) {
-               close(cfag12864b_fd);
-               return -2;
-       }
-
-       return 0;
-}
-
-/*
- * exit a cfag12864b framebuffer device
- */
-static void cfag12864b_exit(void)
-{
-       munmap(cfag12864b_mem, CFAG12864B_SIZE);
-       close(cfag12864b_fd);
-}
-
-/*
- * set (x, y) pixel
- */
-static void cfag12864b_set(unsigned char x, unsigned char y)
-{
-       if (CFAG12864B_CHECK(x, y))
-               cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] |=
-                       CFAG12864B_BIT(x % CFAG12864B_BPB);
-}
-
-/*
- * unset (x, y) pixel
- */
-static void cfag12864b_unset(unsigned char x, unsigned char y)
-{
-       if (CFAG12864B_CHECK(x, y))
-               cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &=
-                       ~CFAG12864B_BIT(x % CFAG12864B_BPB);
-}
-
-/*
- * is set (x, y) pixel?
- *
- * Pixel off: return = 0
- * Pixel on:  return = 1
- */
-static unsigned char cfag12864b_isset(unsigned char x, unsigned char y)
-{
-       if (CFAG12864B_CHECK(x, y))
-               if (cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &
-                       CFAG12864B_BIT(x % CFAG12864B_BPB))
-                       return 1;
-
-       return 0;
-}
-
-/*
- * not (x, y) pixel
- */
-static void cfag12864b_not(unsigned char x, unsigned char y)
-{
-       if (cfag12864b_isset(x, y))
-               cfag12864b_unset(x, y);
-       else
-               cfag12864b_set(x, y);
-}
-
-/*
- * fill (set all pixels)
- */
-static void cfag12864b_fill(void)
-{
-       unsigned short i;
-
-       for (i = 0; i < CFAG12864B_SIZE; i++)
-               cfag12864b_buffer[i] = 0xFF;
-}
-
-/*
- * clear (unset all pixels)
- */
-static void cfag12864b_clear(void)
-{
-       unsigned short i;
-
-       for (i = 0; i < CFAG12864B_SIZE; i++)
-               cfag12864b_buffer[i] = 0;
-}
-
-/*
- * format a [128*64] matrix
- *
- * Pixel off: src[i] = 0
- * Pixel on:  src[i] > 0
- */
-static void cfag12864b_format(unsigned char * matrix)
-{
-       unsigned char i, j, n;
-
-       for (i = 0; i < CFAG12864B_HEIGHT; i++)
-       for (j = 0; j < CFAG12864B_WIDTH / CFAG12864B_BPB; j++) {
-               cfag12864b_buffer[i * CFAG12864B_WIDTH / CFAG12864B_BPB +
-                       j] = 0;
-               for (n = 0; n < CFAG12864B_BPB; n++)
-                       if (matrix[i * CFAG12864B_WIDTH +
-                               j * CFAG12864B_BPB + n])
-                               cfag12864b_buffer[i * CFAG12864B_WIDTH /
-                                       CFAG12864B_BPB + j] |=
-                                       CFAG12864B_BIT(n);
-       }
-}
-
-/*
- * blit buffer to lcd
- */
-static void cfag12864b_blit(void)
-{
-       memcpy(cfag12864b_mem, cfag12864b_buffer, CFAG12864B_SIZE);
-}
-
-/*
- * ----------------------
- * end of cfag12864b code
- * ----------------------
- */
-
-#include <stdio.h>
-
-#define EXAMPLES       6
-
-static void example(unsigned char n)
-{
-       unsigned short i, j;
-       unsigned char matrix[CFAG12864B_WIDTH * CFAG12864B_HEIGHT];
-
-       if (n > EXAMPLES)
-               return;
-
-       printf("Example %i/%i - ", n, EXAMPLES);
-
-       switch (n) {
-       case 1:
-               printf("Draw points setting bits");
-               cfag12864b_clear();
-               for (i = 0; i < CFAG12864B_WIDTH; i += 2)
-                       for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
-                               cfag12864b_set(i, j);
-               break;
-
-       case 2:
-               printf("Clear the LCD");
-               cfag12864b_clear();
-               break;
-
-       case 3:
-               printf("Draw rows formatting a [128*64] matrix");
-               memset(matrix, 0, CFAG12864B_WIDTH * CFAG12864B_HEIGHT);
-               for (i = 0; i < CFAG12864B_WIDTH; i++)
-                       for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
-                               matrix[j * CFAG12864B_WIDTH + i] = 1;
-               cfag12864b_format(matrix);
-               break;
-
-       case 4:
-               printf("Fill the lcd");
-               cfag12864b_fill();
-               break;
-
-       case 5:
-               printf("Draw columns unsetting bits");
-               for (i = 0; i < CFAG12864B_WIDTH; i += 2)
-                       for (j = 0; j < CFAG12864B_HEIGHT; j++)
-                               cfag12864b_unset(i, j);
-               break;
-
-       case 6:
-               printf("Do negative not-ing all bits");
-               for (i = 0; i < CFAG12864B_WIDTH; i++)
-                       for (j = 0; j < CFAG12864B_HEIGHT; j ++)
-                               cfag12864b_not(i, j);
-               break;
-       }
-
-       puts(" - [Press Enter]");
-}
-
-int main(int argc, char *argv[])
-{
-       unsigned char n;
-
-       if (argc != 2) {
-               printf(
-                       "Sintax:  %s fbdev\n"
-                       "Usually: /dev/fb0, /dev/fb1...\n", argv[0]);
-               return -1;
-       }
-
-       if (cfag12864b_init(argv[1])) {
-               printf("Can't init %s fbdev\n", argv[1]);
-               return -2;
-       }
-
-       for (n = 1; n <= EXAMPLES; n++) {
-               example(n);
-               cfag12864b_blit();
-               while (getchar() != '\n');
-       }
-
-       cfag12864b_exit();
-
-       return 0;
-}
index c54fcdd..265a1ef 100644 (file)
@@ -1,10 +1,6 @@
 00-INDEX
        - This file
-Makefile
-       - Makefile for gptimers example file.
 bfin-gpio-notes.txt
        - Notes in developing/using bfin-gpio driver.
 bfin-spi-notes.txt
        - Notes for using bfin spi bus driver.
-gptimers-example.c
-       - gptimers example
diff --git a/Documentation/blackfin/Makefile b/Documentation/blackfin/Makefile
deleted file mode 100644 (file)
index 6782c58..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-ifneq ($(CONFIG_BLACKFIN),)
-ifneq ($(CONFIG_BFIN_GPTIMERS),)
-obj-m := gptimers-example.o
-endif
-endif
diff --git a/Documentation/blackfin/gptimers-example.c b/Documentation/blackfin/gptimers-example.c
deleted file mode 100644 (file)
index 283eba9..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Simple gptimers example
- *     http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:drivers:gptimers
- *
- * Copyright 2007-2009 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/interrupt.h>
-#include <linux/module.h>
-
-#include <asm/gptimers.h>
-#include <asm/portmux.h>
-
-/* ... random driver includes ... */
-
-#define DRIVER_NAME "gptimer_example"
-
-#ifdef IRQ_TIMER5
-#define SAMPLE_IRQ_TIMER IRQ_TIMER5
-#else
-#define SAMPLE_IRQ_TIMER IRQ_TIMER2
-#endif
-
-struct gptimer_data {
-       uint32_t period, width;
-};
-static struct gptimer_data data;
-
-/* ... random driver state ... */
-
-static irqreturn_t gptimer_example_irq(int irq, void *dev_id)
-{
-       struct gptimer_data *data = dev_id;
-
-       /* make sure it was our timer which caused the interrupt */
-       if (!get_gptimer_intr(TIMER5_id))
-               return IRQ_NONE;
-
-       /* read the width/period values that were captured for the waveform */
-       data->width = get_gptimer_pwidth(TIMER5_id);
-       data->period = get_gptimer_period(TIMER5_id);
-
-       /* acknowledge the interrupt */
-       clear_gptimer_intr(TIMER5_id);
-
-       /* tell the upper layers we took care of things */
-       return IRQ_HANDLED;
-}
-
-/* ... random driver code ... */
-
-static int __init gptimer_example_init(void)
-{
-       int ret;
-
-       /* grab the peripheral pins */
-       ret = peripheral_request(P_TMR5, DRIVER_NAME);
-       if (ret) {
-               printk(KERN_NOTICE DRIVER_NAME ": peripheral request failed\n");
-               return ret;
-       }
-
-       /* grab the IRQ for the timer */
-       ret = request_irq(SAMPLE_IRQ_TIMER, gptimer_example_irq,
-                       IRQF_SHARED, DRIVER_NAME, &data);
-       if (ret) {
-               printk(KERN_NOTICE DRIVER_NAME ": IRQ request failed\n");
-               peripheral_free(P_TMR5);
-               return ret;
-       }
-
-       /* setup the timer and enable it */
-       set_gptimer_config(TIMER5_id,
-                       WDTH_CAP | PULSE_HI | PERIOD_CNT | IRQ_ENA);
-       enable_gptimers(TIMER5bit);
-
-       return 0;
-}
-module_init(gptimer_example_init);
-
-static void __exit gptimer_example_exit(void)
-{
-       disable_gptimers(TIMER5bit);
-       free_irq(SAMPLE_IRQ_TIMER, &data);
-       peripheral_free(P_TMR5);
-}
-module_exit(gptimer_example_exit);
-
-MODULE_LICENSE("BSD");
index 1788722..b2391b8 100644 (file)
@@ -162,6 +162,15 @@ See the include/linux/kmemleak.h header for the functions prototype.
 - ``kmemleak_alloc_recursive`` - as kmemleak_alloc but checks the recursiveness
 - ``kmemleak_free_recursive``   - as kmemleak_free but checks the recursiveness
 
+The following functions take a physical address as the object pointer
+and only perform the corresponding action if the address has a lowmem
+mapping:
+
+- ``kmemleak_alloc_phys``
+- ``kmemleak_free_part_phys``
+- ``kmemleak_not_leak_phys``
+- ``kmemleak_ignore_phys``
+
 Dealing with false positives/negatives
 --------------------------------------
 
diff --git a/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt b/Documentation/devicetree/bindings/display/bridge/dumb-vga-dac.txt
new file mode 100644 (file)
index 0000000..003bc24
--- /dev/null
@@ -0,0 +1,48 @@
+Dumb RGB to VGA DAC bridge
+---------------------------
+
+This binding is aimed for dumb RGB to VGA DAC based bridges that do not require
+any configuration.
+
+Required properties:
+
+- compatible: Must be "dumb-vga-dac"
+
+Required nodes:
+
+This device has two video ports. Their connections are modelled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for RGB input
+- Video port 1 for VGA output
+
+
+Example
+-------
+
+bridge {
+       compatible = "dumb-vga-dac";
+       #address-cells = <1>;
+       #size-cells = <0>;
+
+       ports {
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+               port@0 {
+                       reg = <0>;
+
+                       vga_bridge_in: endpoint {
+                               remote-endpoint = <&tcon0_out_vga>;
+                       };
+               };
+
+               port@1 {
+                       reg = <1>;
+
+                       vga_bridge_out: endpoint {
+                               remote-endpoint = <&vga_con_in>;
+                       };
+               };
+       };
+};
index e178e6b..24cc246 100644 (file)
@@ -21,8 +21,19 @@ Optional properties:
   - video-ports: 24 bits value which defines how the video controller
        output is wired to the TDA998x input - default: <0x230145>
 
+  - audio-ports: array of 8-bit values, 2 values per one DAI[1].
+       The first value defines the DAI type: TDA998x_SPDIF or TDA998x_I2S[2].
+       The second value defines the tda998x AP_ENA reg content when the DAI
+       in question is used. The implementation allows one or two DAIs. If two
+       DAIs are defined, they must be of different type.
+
+[1] Documentation/sound/alsa/soc/DAI.txt
+[2] include/dt-bindings/display/tda998x.h
+
 Example:
 
+#include <dt-bindings/display/tda998x.h>
+
        tda998x: hdmi-encoder {
                compatible = "nxp,tda998x";
                reg = <0x70>;
@@ -30,4 +41,11 @@ Example:
                interrupts = <27 2>;            /* falling edge */
                pinctrl-0 = <&pmx_camera>;
                pinctrl-names = "default";
+               video-ports = <0x230145>;
+
+               #sound-dai-cells = <2>;
+                            /* DAI-format      AP_ENA reg value */
+               audio-ports = < TDA998x_SPDIF   0x04
+                               TDA998x_I2S     0x03>;
+
        };
index b63f614..2ad5789 100644 (file)
@@ -14,17 +14,16 @@ Required properties:
 - power-domains: Should be <&mmcc MDSS_GDSC>.
 - clocks: device clocks
   See ../clocks/clock-bindings.txt for details.
-- qcom,hdmi-tx-ddc-clk-gpio: ddc clk pin
-- qcom,hdmi-tx-ddc-data-gpio: ddc data pin
-- qcom,hdmi-tx-hpd-gpio: hpd pin
 - core-vdda-supply: phandle to supply regulator
 - hdmi-mux-supply: phandle to mux regulator
 - phys: the phandle for the HDMI PHY device
 - phy-names: the name of the corresponding PHY device
 
 Optional properties:
-- qcom,hdmi-tx-mux-en-gpio: hdmi mux enable pin
-- qcom,hdmi-tx-mux-sel-gpio: hdmi mux select pin
+- hpd-gpios: hpd pin
+- qcom,hdmi-tx-mux-en-gpios: hdmi mux enable pin
+- qcom,hdmi-tx-mux-sel-gpios: hdmi mux select pin
+- qcom,hdmi-tx-mux-lpm-gpios: hdmi mux lpm pin
 - power-domains: reference to the power domain(s), if available.
 - pinctrl-names: the pin control state names; should contain "default"
 - pinctrl-0: the default pinctrl state (active)
diff --git a/Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt b/Documentation/devicetree/bindings/display/panel/innolux,g101ice-l01.txt
new file mode 100644 (file)
index 0000000..9e75904
--- /dev/null
@@ -0,0 +1,7 @@
+Innolux Corporation 10.1" G101ICE-L01 WXGA (1280x800) LVDS panel
+
+Required properties:
+- compatible: should be "innolux,g101ice-l01"
+
+This binding is compatible with the simple-panel binding, which is specified
+in simple-panel.txt in this directory.
diff --git a/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt b/Documentation/devicetree/bindings/display/panel/jdi,lt070me05000.txt
new file mode 100644 (file)
index 0000000..4989c91
--- /dev/null
@@ -0,0 +1,31 @@
+JDI model LT070ME05000 1200x1920 7" DSI Panel
+
+Required properties:
+- compatible: should be "jdi,lt070me05000"
+- vddp-supply: phandle of the regulator that provides the supply voltage
+  Power IC supply (3-5V)
+- iovcc-supply: phandle of the regulator that provides the supply voltage
+  IOVCC , power supply for LCM (1.8V)
+- enable-gpios: phandle of gpio for enable line
+  LED_EN, LED backlight enable, High active
+- reset-gpios: phandle of gpio for reset line
+  This should be 8mA, gpio can be configured using mux, pinctrl, pinctrl-names
+  XRES, Reset, Low active
+- dcdc-en-gpios: phandle of the gpio for power ic line
+  Power IC supply enable, High active
+
+Example:
+
+       dsi0: qcom,mdss_dsi@4700000 {
+               panel@0 {
+                       compatible = "jdi,lt070me05000";
+                       reg = <0>;
+
+                       vddp-supply = <&pm8921_l17>;
+                       iovcc-supply = <&pm8921_lvs7>;
+
+                       enable-gpios = <&pm8921_gpio 36 GPIO_ACTIVE_HIGH>;
+                       reset-gpios = <&tlmm_pinmux 54 GPIO_ACTIVE_LOW>;
+                       dcdc-en-gpios = <&pm8921_gpio 23 GPIO_ACTIVE_HIGH>;
+               };
+       };
index 5489b59..9eb3f0a 100644 (file)
@@ -6,8 +6,10 @@ buffer to an external LCD interface.
 
 Required properties:
 - compatible: value should be one of the following
-               "rockchip,rk3288-vop";
                "rockchip,rk3036-vop";
+               "rockchip,rk3288-vop";
+               "rockchip,rk3399-vop-big";
+               "rockchip,rk3399-vop-lit";
 
 - interrupts: should contain a list of all VOP IP block interrupts in the
                 order: VSYNC, LCD_SYSTEM. The interrupt specifier
index df8f4ae..b95696d 100644 (file)
@@ -26,13 +26,14 @@ TCON
 The TCON acts as a timing controller for RGB, LVDS and TV interfaces.
 
 Required properties:
- - compatible: value should be "allwinner,sun5i-a13-tcon".
+ - compatible: value must be either:
+   * allwinner,sun5i-a13-tcon
+   * allwinner,sun8i-a33-tcon
  - reg: base address and size of memory-mapped region
  - interrupts: interrupt associated to this IP
  - clocks: phandles to the clocks feeding the TCON. Three are needed:
    - 'ahb': the interface clocks
    - 'tcon-ch0': The clock driving the TCON channel 0
-   - 'tcon-ch1': The clock driving the TCON channel 1
  - resets: phandles to the reset controllers driving the encoder
    - "lcd": the reset line for the TCON channel 0
 
@@ -49,6 +50,33 @@ Required properties:
   second the block connected to the TCON channel 1 (usually the TV
   encoder)
 
+On the A13, there is one more clock required:
+   - 'tcon-ch1': The clock driving the TCON channel 1
+
+DRC
+---
+
+The DRC (Dynamic Range Controller), found in the latest Allwinner SoCs
+(A31, A23, A33), allows to dynamically adjust pixel
+brightness/contrast based on histogram measurements for LCD content
+adaptive backlight control.
+
+
+Required properties:
+  - compatible: value must be one of:
+    * allwinner,sun8i-a33-drc
+  - reg: base address and size of the memory-mapped region.
+  - interrupts: interrupt associated to this IP
+  - clocks: phandles to the clocks feeding the DRC
+    * ahb: the DRC interface clock
+    * mod: the DRC module clock
+    * ram: the DRC DRAM clock
+  - clock-names: the clock names mentioned above
+  - resets: phandles to the reset line driving the DRC
+
+- ports: A ports node with endpoint definitions as defined in
+  Documentation/devicetree/bindings/media/video-interfaces.txt. The
+  first port should be the input endpoints, the second one the outputs
 
 Display Engine Backend
 ----------------------
@@ -59,6 +87,7 @@ system.
 Required properties:
   - compatible: value must be one of:
     * allwinner,sun5i-a13-display-backend
+    * allwinner,sun8i-a33-display-backend
   - reg: base address and size of the memory-mapped region.
   - clocks: phandles to the clocks feeding the frontend and backend
     * ahb: the backend interface clock
@@ -71,6 +100,14 @@ Required properties:
   Documentation/devicetree/bindings/media/video-interfaces.txt. The
   first port should be the input endpoints, the second one the output
 
+On the A33, some additional properties are required:
+  - reg needs to have an additional region corresponding to the SAT
+  - reg-names need to be set, with "be" and "sat"
+  - clocks and clock-names need to have a phandle to the SAT bus
+    clocks, whose name will be "sat"
+  - resets and reset-names need to have a phandle to the SAT bus
+    resets, whose name will be "sat"
+
 Display Engine Frontend
 -----------------------
 
@@ -80,6 +117,7 @@ deinterlacing and color space conversion.
 Required properties:
   - compatible: value must be one of:
     * allwinner,sun5i-a13-display-frontend
+    * allwinner,sun8i-a33-display-frontend
   - reg: base address and size of the memory-mapped region.
   - interrupts: interrupt associated to this IP
   - clocks: phandles to the clocks feeding the frontend and backend
@@ -104,6 +142,7 @@ extra node.
 Required properties:
   - compatible: value must be one of:
     * allwinner,sun5i-a13-display-engine
+    * allwinner,sun8i-a33-display-engine
 
   - allwinner,pipelines: list of phandle to the display engine
     frontends available.
index 2136ee8..a83abd7 100644 (file)
@@ -17,6 +17,18 @@ Optional properties:
    the lcd controller.
  - max-pixelclock: The maximum pixel clock that can be supported
    by the lcd controller in KHz.
+ - blue-and-red-wiring: Recognized values "straight" or "crossed".
+   This property deals with the LCDC revision 2 (found on AM335x)
+   color errata [1].
+    - "straight" indicates normal wiring that supports RGB565,
+      BGR888, and XBGR8888 color formats.
+    - "crossed" indicates wiring that has blue and red wires
+      crossed. This setup supports BGR565, RGB888 and XRGB8888
+      formats.
+    - If the property is not present or its value is not recognized
+      the legacy mode is assumed. This configuration supports RGB565,
+      RGB888 and XRGB8888 formats. However, depending on wiring, the red
+      and blue colors are swapped in either 16 or 24-bit color modes.
 
 Optional nodes:
 
@@ -24,6 +36,18 @@ Optional nodes:
    binding follows Documentation/devicetree/bindings/graph.txt and
    suppors a single port with a single endpoint.
 
+ - See also Documentation/devicetree/bindings/display/tilcdc/panel.txt and
+   Documentation/devicetree/bindings/display/tilcdc/tfp410.txt for connecting
+   tfp410 DVI encoder or lcd panel to lcdc
+
+[1] There is an errata about AM335x color wiring. For 16-bit color mode
+    the wires work as they should (LCD_DATA[0:4] is for Blue[3:7]),
+    but for 24 bit color modes the wiring of blue and red components is
+    crossed and LCD_DATA[0:4] is for Red[3:7] and LCD_DATA[11:15] is
+    for Blue[3-7]. For more details see section 3.1.1 in AM335x
+    Silicon Errata:
+    http://www.ti.com/general/docs/lit/getliterature.tsp?baseLiteratureNumber=sprz360
+
 Example:
 
        fb: fb@4830e000 {
@@ -33,6 +57,8 @@ Example:
                interrupts = <36>;
                ti,hwmods = "lcdc";
 
+               blue-and-red-wiring = "crossed";
+
                port {
                        lcdc_0: endpoint@0 {
                                remote-endpoint = <&hdmi_0>;
index f31b2ad..5fa691e 100644 (file)
@@ -32,6 +32,14 @@ wants to support one of the below features, it should adapt the bindings below.
 - clock-frequency
        frequency of bus clock in Hz.
 
+- i2c-bus
+       For I2C adapters that have child nodes that are a mixture of both I2C
+       devices and non-I2C devices, the 'i2c-bus' subnode can be used for
+       populating I2C devices. If the 'i2c-bus' subnode is present, only
+       subnodes of this will be considered as I2C slaves. The properties,
+       '#address-cells' and '#size-cells' must be defined under this subnode
+       if present.
+
 - i2c-scl-falling-time-ns
        Number of nanoseconds the SCL signal takes to fall; t(f) in the I2C
        specification.
index 1416c6a..fbbad64 100644 (file)
@@ -51,7 +51,6 @@ fsl,sgtl5000          SGTL5000: Ultra Low-Power Audio Codec
 gmt,g751               G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
 infineon,slb9635tt     Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
 infineon,slb9645tt     Infineon SLB9645 I2C TPM (new protocol, max 400khz)
-isil,isl12057          Intersil ISL12057 I2C RTC Chip
 isil,isl29028          Intersil ISL29028 Ambient Light and Proximity Sensor
 maxim,ds1050           5 Bit Programmable, Pulse-Width Modulator
 maxim,max1237          Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
index f97993b..d3b273e 100644 (file)
@@ -14,6 +14,7 @@ length of memory mapped region.
 representing a ethernet device.
 - dsaf-handle: phandle, specifies a reference to a node
 representing a dsaf device.
+- node_guid: a number that uniquely identifies a device or component
 - #address-cells: must be 2
 - #size-cells: must be 2
 Optional properties:
@@ -32,6 +33,7 @@ Example:
                        dma-coherent;
                        eth-handle = <&eth2 &eth3 &eth4 &eth5 &eth6 &eth7>;
                        dsaf-handle = <&soc0_dsa>;
+                       node-guid = [00 9A CD 00 00 01 02 03];
                        #address-cells = <2>;
                        #size-cells = <2>;
                        interrupt-parent = <&mbigen_dsa>;
diff --git a/Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt b/Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
new file mode 100644 (file)
index 0000000..7b8944c
--- /dev/null
@@ -0,0 +1,21 @@
+* MELFAS MIP4 Touchscreen
+
+Required properties:
+- compatible: must be "melfas,mip4_ts"
+- reg: I2C slave address of the chip (0x48 or 0x34)
+- interrupt-parent: interrupt controller to which the chip is connected
+- interrupts: interrupt to which the chip is connected
+
+Optional properties:
+- ce-gpios: GPIO connected to the CE (chip enable) pin of the chip
+
+Example:
+       i2c@00000000 {
+               touchscreen: melfas_mip4@48 {
+                       compatible = "melfas,mip4_ts";
+                       reg = <0x48>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
+                       ce-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/pwm/pwm-meson.txt b/Documentation/devicetree/bindings/pwm/pwm-meson.txt
new file mode 100644 (file)
index 0000000..5376a44
--- /dev/null
@@ -0,0 +1,23 @@
+Amlogic Meson PWM Controller
+============================
+
+Required properties:
+- compatible: Shall contain "amlogic,meson8b-pwm" or "amlogic,meson-gxbb-pwm".
+- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of
+  the cells format.
+
+Optional properties:
+- clocks: Could contain one or two parents clocks phandle for each of the two
+  PWM channels.
+- clock-names: Could contain at least the "clkin0" and/or "clkin1" names.
+
+Example:
+
+       pwm_ab: pwm@8550 {
+               compatible = "amlogic,meson-gxbb-pwm";
+               reg = <0x0 0x08550 0x0 0x10>;
+               #pwm-cells = <3>;
+               status = "disabled";
+               clocks = <&xtal>, <&xtal>;
+               clock-names = "clkin0", "clkin1";
+       }
index f8f59ba..6f8af2b 100644 (file)
@@ -2,8 +2,9 @@ MediaTek display PWM controller
 
 Required properties:
  - compatible: should be "mediatek,<name>-disp-pwm":
-   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
+   - "mediatek,mt2701-disp-pwm": found on mt2701 SoC.
    - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
+   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
  - reg: physical base address and length of the controller's registers.
  - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
    the cell format.
index 84d2fb8..19fce77 100644 (file)
@@ -13,13 +13,14 @@ Required parameters:
 - pinctrl-0:           List of phandles pointing to pin configuration nodes
                        for PWM module.
                        For Pinctrl properties, please refer to [1].
-- clock-names:                 Set to "pwm".
+- clock-names:                 Valid entries are "pwm" and/or "capture".
 - clocks:              phandle of the clock used by the PWM module.
                        For Clk properties, please refer to [2].
+- interrupts:          IRQ for the Capture device
 
 Optional properties:
-- st,pwm-num-chan:     Number of available channels. If not passed, the driver
-                       will consider single channel by default.
+- st,pwm-num-chan:     Number of available PWM channels.  Default is 0.
+- st,capture-num-chan: Number of available Capture channels.  Default is 0.
 
 [1] Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
 [2] Documentation/devicetree/bindings/clock/clock-bindings.txt
@@ -38,4 +39,5 @@ pwm1: pwm@fe510000 {
        clocks = <&clk_sysin>;
        clock-names = "pwm";
        st,pwm-num-chan = <4>;
+       st,capture-num-chan = <2>;
 };
index cf6068b..f1cbeef 100644 (file)
@@ -6,6 +6,7 @@ Required properties:
     - "allwinner,sun5i-a10s-pwm"
     - "allwinner,sun5i-a13-pwm"
     - "allwinner,sun7i-a20-pwm"
+    - "allwinner,sun8i-h3-pwm"
   - reg: physical base address and length of the controller's registers
   - #pwm-cells: should be 3. See pwm.txt in this directory for a description of
     the cells format.
index 8e76f26..9882b81 100644 (file)
@@ -11,7 +11,7 @@ Optional properties:
 - trickle-diode-disable : Do not use internal trickle charger diode
        Should be given if internal trickle charger diode should be disabled
 Example:
-       ds1390: rtc@68 {
+       ds1390: rtc@0 {
                compatible = "dallas,ds1390";
                trickle-resistor-ohms = <250>;
                reg = <0>;
diff --git a/Documentation/devicetree/bindings/rtc/epson,rx8900.txt b/Documentation/devicetree/bindings/rtc/epson,rx8900.txt
new file mode 100644 (file)
index 0000000..3f61e51
--- /dev/null
@@ -0,0 +1,22 @@
+Real Time Clock driver for:
+  - Epson RX8900
+  - Micro Crystal rv8803
+
+Required properties:
+- compatible: should be: "microcrystal,rv8803" or "epson,rx8900"
+- reg : the I2C address of the device for I2C
+
+Optional properties:
+- epson,vdet-disable : boolean, if present will disable voltage detector.
+  Should be set if no backup battery is used.
+- trickle-diode-disable : boolean, if present will disable internal trickle
+  charger diode
+
+Example:
+
+       rtc: rtc@32 {
+               compatible = "epson,rx8900"
+               reg = <0x32>;
+               epson,vdet-disable;
+               trickle-diode-disable;
+       };
index bf7d11a..bee41f9 100644 (file)
@@ -18,6 +18,18 @@ Optional properties:
   through pmic_power_en
 - clocks: Any internal or external clocks feeding in to rtc
 - clock-names: Corresponding names of the clocks
+- pinctrl-0: a phandle pointing to the pin settings for the device
+- pinctrl-names: should be "default"
+
+Optional subnodes:
+- generic pinctrl node
+
+Required pinctrl subnodes properties:
+- pins - Names of ext_wakeup pins to configure
+
+Optional pinctrl subnodes properties:
+- input-enable - Enables ext_wakeup
+- ti,active-high - Set input active high (by default active low)
 
 Example:
 
@@ -30,4 +42,13 @@ rtc@1c23000 {
        system-power-controller;
        clocks = <&clk_32k_rtc>, <&clk_32768_ck>;
        clock-names = "ext-clk", "int-clk";
+
+       pinctrl-0 = <&ext_wakeup>;
+       pinctrl-names = "default";
+
+       ext_wakeup: ext-wakeup {
+               pins = "ext_wakeup0";
+               input-enable;
+               ti,active-high;
+       };
 };
diff --git a/Documentation/devicetree/bindings/thermal/max77620_thermal.txt b/Documentation/devicetree/bindings/thermal/max77620_thermal.txt
new file mode 100644 (file)
index 0000000..323a3b3
--- /dev/null
@@ -0,0 +1,70 @@
+Thermal driver for MAX77620 Power management IC from Maxim Semiconductor.
+
+Maxim Semiconductor MAX77620 supports alarm interrupts when its
+die temperature crosses 120C and 140C. These threshold temperatures
+are not configurable. Device does not provide the real temperature
+of die other than just indicating whether temperature is above or
+below threshold level.
+
+Required properties:
+-------------------
+#thermal-sensor-cells: Please refer <devicetree/bindings/thermal/thermal.txt>
+                       for more details.
+                       The value must be 0.
+
+For more details, please refer generic thermal DT binding document
+<devicetree/bindings/thermal/thermal.txt>.
+
+Please refer <devicetree/bindings/mfd/max77620.txt> for mfd DT binding
+document for the MAX77620.
+
+Example:
+--------
+#include <dt-bindings/mfd/max77620.h>
+#include <dt-bindings/thermal/thermal.h>
+...
+
+i2c@7000d000 {
+       spmic: max77620@3c {
+               compatible = "maxim,max77620";
+               :::::
+               #thermal-sensor-cells = <0>;
+               :::
+       };
+};
+
+cool_dev: cool-dev {
+       compatible = "cooling-dev";
+       #cooling-cells = <2>;
+};
+
+thermal-zones {
+       PMIC-Die {
+               polling-delay = <0>;
+               polling-delay-passive = <0>;
+               thermal-sensors = <&spmic>;
+
+               trips {
+                       pmic_die_warn_temp_thresh: hot-die {
+                               temperature = <120000>;
+                               type = "hot";
+                               hysteresis = <0>;
+                       };
+
+                       pmic_die_cirt_temp_thresh: cirtical-die {
+                               temperature = <140000>;
+                               type = "critical";
+                               hysteresis = <0>;
+                       };
+               };
+
+               cooling-maps {
+                       map0 {
+                               trip = <&pmic_die_warn_temp_thresh>;
+                               cooling-device = <&cool_dev THERMAL_NO_LIMIT
+                                                 THERMAL_NO_LIMIT>;
+                               contribution = <100>;
+                       };
+               };
+       };
+};
index 81f9a51..e2f494d 100644 (file)
@@ -8,7 +8,9 @@ apmixedsys register space via AHB bus accesses, so a phandle to the APMIXEDSYS
 is also needed.
 
 Required properties:
-- compatible: "mediatek,mt8173-thermal"
+- compatible:
+  - "mediatek,mt8173-thermal" : For MT8173 family of SoCs
+  - "mediatek,mt2701-thermal" : For MT2701 family of SoCs
 - reg: Address range of the thermal controller
 - interrupts: IRQ for the thermal controller
 - clocks, clock-names: Clocks needed for the thermal controller. required
index edebfa0..b6c0ae5 100644 (file)
@@ -10,8 +10,14 @@ Required properties :
 - compatible : For Tegra124, must contain "nvidia,tegra124-soctherm".
   For Tegra132, must contain "nvidia,tegra132-soctherm".
   For Tegra210, must contain "nvidia,tegra210-soctherm".
-- reg : Should contain 1 entry:
+- reg : Should contain at least 2 entries for each entry in reg-names:
   - SOCTHERM register set
+  - Tegra CAR register set: Required for Tegra124 and Tegra210.
+  - CCROC register set: Required for Tegra132.
+- reg-names :  Should contain at least 2 entries:
+  - soctherm-reg
+  - car-reg
+  - ccroc-reg
 - interrupts : Defines the interrupt used by SOCTHERM
 - clocks : Must contain an entry for each entry in clock-names.
   See ../clocks/clock-bindings.txt for details.
@@ -25,17 +31,45 @@ Required properties :
 - #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description
     of this property. See <dt-bindings/thermal/tegra124-soctherm.h> for a
     list of valid values when referring to thermal sensors.
+- throttle-cfgs: A sub-node which is a container of configuration for each
+    hardware throttle events. These events can be set as cooling devices.
+  * throttle events: Sub-nodes must be named as "light" or "heavy".
+      Properties:
+      - nvidia,priority: Each throttles has its own throttle settings, so the
+        SW need to set priorities for various throttle, the HW arbiter can select
+        the final throttle settings.
+        Bigger value indicates higher priority, In general, higher priority
+        translates to lower target frequency. SW needs to ensure that critical
+        thermal alarms are given higher priority, and ensure that there is
+        no race if priority of two vectors is set to the same value.
+        The range of this value is 1~100.
+      - nvidia,cpu-throt-percent: This property is for Tegra124 and Tegra210.
+        It is the throttling depth of pulse skippers, it's the percentage
+        throttling.
+      - nvidia,cpu-throt-level: This property is only for Tegra132, it is the
+        level of pulse skippers, which used to throttle clock frequencies. It
+        indicates cpu clock throttling depth, and the depth can be programmed.
+        Must set as following values:
+        TEGRA_SOCTHERM_THROT_LEVEL_LOW, TEGRA_SOCTHERM_THROT_LEVEL_MED
+        TEGRA_SOCTHERM_THROT_LEVEL_HIGH, TEGRA_SOCTHERM_THROT_LEVEL_NONE
+      - #cooling-cells: Should be 1. This cooling device only support on/off state.
+        See ./thermal.txt for a description of this property.
 
 Note:
 - the "critical" type trip points will be set to SOC_THERM hardware as the
 shut down temperature. Once the temperature of this thermal zone is higher
 than it, the system will be shutdown or reset by hardware.
+- the "hot" type trip points will be set to SOC_THERM hardware as the throttle
+temperature. Once the the temperature of this thermal zone is higher
+than it, it will trigger the HW throttle event.
 
 Example :
 
        soctherm@700e2000 {
                compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               reg = <0x0 0x700e2000 0x0 0x600  /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400 /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
@@ -44,6 +78,76 @@ Example :
                reset-names = "soctherm";
 
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       /*
+                        * When the "heavy" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in 85% depth
+                        */
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * When the "light" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in 50% depth
+                        */
+                       throttle_light: light {
+                               nvidia,priority = <80>;
+                               nvidia,cpu-throt-percent = <50>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * If these two devices are triggered in same time, the HW throttle
+                        * arbiter will select the highest priority as the final throttle
+                        * settings to skip cpu pulse.
+                        */
+               };
+       };
+
+Example: referring to Tegra132's "reg", "reg-names" and "throttle-cfgs" :
+
+       soctherm@700e2000 {
+               compatible = "nvidia,tegra132-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600  /* SOC_THERM reg_base */
+                       0x0 0x70040000 0x0 0x200>; /* CCROC reg_base */;
+               reg-names = "soctherm-reg", "ccroc-reg";
+
+               throttle-cfgs {
+                       /*
+                        * When the "heavy" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in HIGH level
+                        */
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * When the "light" cooling device triggered,
+                        * the HW will skip cpu clock's pulse in MED level
+                        */
+                       throttle_light: light {
+                               nvidia,priority = <80>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_MED>;
+
+                               #cooling-cells = <1>;
+                       };
+
+                       /*
+                        * If these two devices are triggered in same time, the HW throttle
+                        * arbiter will select the highest priority as the final throttle
+                        * settings to skip cpu pulse.
+                        */
+
+               };
        };
 
 Example: referring to thermal sensors :
@@ -62,6 +166,19 @@ Example: referring to thermal sensors :
                                        hysteresis = <1000>;
                                        type = "critical";
                                };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
                        };
                 };
        };
diff --git a/Documentation/devicetree/bindings/thermal/qcom-tsens.txt b/Documentation/devicetree/bindings/thermal/qcom-tsens.txt
new file mode 100644 (file)
index 0000000..292ed89
--- /dev/null
@@ -0,0 +1,21 @@
+* QCOM SoC Temperature Sensor (TSENS)
+
+Required properties:
+- compatible :
+ - "qcom,msm8916-tsens" : For 8916 Family of SoCs
+ - "qcom,msm8974-tsens" : For 8974 Family of SoCs
+ - "qcom,msm8996-tsens" : For 8996 Family of SoCs
+
+- reg: Address range of the thermal registers
+- #thermal-sensor-cells : Should be 1. See ./thermal.txt for a description.
+- Refer to Documentation/devicetree/bindings/nvmem/nvmem.txt to know how to specify
+nvmem cells
+
+Example:
+tsens: thermal-sensor@900000 {
+               compatible = "qcom,msm8916-tsens";
+               reg = <0x4a8000 0x2000>;
+               nvmem-cells = <&tsens_caldata>, <&tsens_calsel>;
+               nvmem-cell-names = "caldata", "calsel";
+               #thermal-sensor-cells = <1>;
+       };
index 24c6f65..f0a48ea 100644 (file)
@@ -163,9 +163,11 @@ maxim      Maxim Integrated Products
 meas   Measurement Specialties
 mediatek       MediaTek Inc.
 melexis        Melexis N.V.
+melfas MELFAS Inc.
 merrii Merrii Technology Co., Ltd.
 micrel Micrel Inc.
 microchip      Microchip Technology Inc.
+microcrystal   Micro Crystal AG
 micron Micron Technology Inc.
 minix  MINIX Technology Ltd.
 mitsubishi     Mitsubishi Electric Corporation
index 6d63782..c6ae9c9 100644 (file)
@@ -7,6 +7,8 @@ Required properties:
 - reg                  : Physical base address and size
 
 Optional properties:
+- clocks               : Input clock specifier. Refer to common clock
+                         bindings.
 - clock-frequency      : Frequency of clock in Hz
 - xlnx,wdt-enable-once : 0 - Watchdog can be restarted
                          1 - Watchdog can be enabled just once
@@ -17,6 +19,7 @@ Example:
 axi-timebase-wdt@40100000 {
        clock-frequency = <50000000>;
        compatible = "xlnx,xps-timebase-wdt-1.00.a";
+       clocks = <&clkc 15>;
        reg = <0x40100000 0x10000>;
        xlnx,wdt-enable-once = <0x0>;
        xlnx,wdt-interval = <0x1b>;
index 039c5ca..b949039 100644 (file)
@@ -9,8 +9,7 @@ functionality.
 
 Required properties
 
-- compatible   : Must be one of: "st,stih407-lpc" "st,stih416-lpc"
-                                 "st,stih415-lpc" "st,stid127-lpc"
+- compatible   : Should be: "st,stih407-lpc"
 - reg          : LPC registers base address + size
 - interrupts    : LPC interrupt line number and associated flags
 - clocks       : Clock used by LPC device (See: ../clock/clock-bindings.txt)
diff --git a/Documentation/filesystems/.gitignore b/Documentation/filesystems/.gitignore
deleted file mode 100644 (file)
index 31d6e42..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dnotify_test
index 9922939..f66e748 100644 (file)
@@ -2,8 +2,6 @@
        - this file (info on some of the filesystems supported by linux).
 Locking
        - info on locking rules as they pertain to Linux VFS.
-Makefile
-       - Makefile for building the filsystems-part of DocBook.
 9p.txt
        - 9p (v9fs) is an implementation of the Plan 9 remote fs protocol.
 adfs.txt
diff --git a/Documentation/filesystems/Makefile b/Documentation/filesystems/Makefile
deleted file mode 100644 (file)
index 883010c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := dnotify_test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index aff2211..50a3e01 100644 (file)
@@ -179,8 +179,19 @@ struct autofs_dev_ioctl {
                                 * including this struct */
        __s32 ioctlfd;          /* automount command fd */
 
-       __u32 arg1;             /* Command parameters */
-       __u32 arg2;
+       union {
+               struct args_protover            protover;
+               struct args_protosubver         protosubver;
+               struct args_openmount           openmount;
+               struct args_ready               ready;
+               struct args_fail                fail;
+               struct args_setpipefd           setpipefd;
+               struct args_timeout             timeout;
+               struct args_requester           requester;
+               struct args_expire              expire;
+               struct args_askumount           askumount;
+               struct args_ismountpoint        ismountpoint;
+       };
 
        char path[0];
 };
@@ -192,8 +203,8 @@ optionally be used to check a specific mount corresponding to a given
 mount point file descriptor, and when requesting the uid and gid of the
 last successful mount on a directory within the autofs file system.
 
-The fields arg1 and arg2 are used to communicate parameters and results of
-calls made as described below.
+The union is used to communicate parameters and results of calls made
+as described below.
 
 The path field is used to pass a path where it is needed and the size field
 is used account for the increased structure length when translating the
@@ -245,9 +256,9 @@ AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD
 Get the major and minor version of the autofs4 protocol version understood
 by loaded module. This call requires an initialized struct autofs_dev_ioctl
 with the ioctlfd field set to a valid autofs mount point descriptor
-and sets the requested version number in structure field arg1. These
-commands return 0 on success or one of the negative error codes if
-validation fails.
+and sets the requested version number in version field of struct args_protover
+or sub_version field of struct args_protosubver. These commands return
+0 on success or one of the negative error codes if validation fails.
 
 
 AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
@@ -256,9 +267,9 @@ AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
 Obtain and release a file descriptor for an autofs managed mount point
 path. The open call requires an initialized struct autofs_dev_ioctl with
 the path field set and the size field adjusted appropriately as well
-as the arg1 field set to the device number of the autofs mount. The
-device number can be obtained from the mount options shown in
-/proc/mounts. The close call requires an initialized struct
+as the devid field of struct args_openmount set to the device number of
+the autofs mount. The device number can be obtained from the mount options
+shown in /proc/mounts. The close call requires an initialized struct
 autofs_dev_ioct with the ioctlfd field set to the descriptor obtained
 from the open call. The release of the file descriptor can also be done
 with close(2) so any open descriptors will also be closed at process exit.
@@ -272,10 +283,10 @@ AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD
 Return mount and expire result status from user space to the kernel.
 Both of these calls require an initialized struct autofs_dev_ioctl
 with the ioctlfd field set to the descriptor obtained from the open
-call and the arg1 field set to the wait queue token number, received
-by user space in the foregoing mount or expire request. The arg2 field
-is set to the status to be returned. For the ready call this is always
-0 and for the fail call it is set to the errno of the operation.
+call and the token field of struct args_ready or struct args_fail set
+to the wait queue token number, received by user space in the foregoing
+mount or expire request. The status field of struct args_fail is set to
+the errno of the operation. It is set to 0 on success.
 
 
 AUTOFS_DEV_IOCTL_SETPIPEFD_CMD
@@ -290,9 +301,10 @@ mount be catatonic (see next call).
 
 The call requires an initialized struct autofs_dev_ioctl with the
 ioctlfd field set to the descriptor obtained from the open call and
-the arg1 field set to descriptor of the pipe. On success the call
-also sets the process group id used to identify the controlling process
-(eg. the owning automount(8) daemon) to the process group of the caller.
+the pipefd field of struct args_setpipefd set to descriptor of the pipe.
+On success the call also sets the process group id used to identify the
+controlling process (eg. the owning automount(8) daemon) to the process
+group of the caller.
 
 
 AUTOFS_DEV_IOCTL_CATATONIC_CMD
@@ -323,9 +335,8 @@ mount on the given path dentry.
 
 The call requires an initialized struct autofs_dev_ioctl with the path
 field set to the mount point in question and the size field adjusted
-appropriately as well as the arg1 field set to the device number of the
-containing autofs mount. Upon return the struct field arg1 contains the
-uid and arg2 the gid.
+appropriately. Upon return the uid field of struct args_requester contains
+the uid and gid field the gid.
 
 When reconstructing an autofs mount tree with active mounts we need to
 re-connect to mounts that may have used the original process uid and
@@ -343,8 +354,9 @@ this ioctl is called until no further expire candidates are found.
 The call requires an initialized struct autofs_dev_ioctl with the
 ioctlfd field set to the descriptor obtained from the open call. In
 addition an immediate expire, independent of the mount timeout, can be
-requested by setting the arg1 field to 1. If no expire candidates can
-be found the ioctl returns -1 with errno set to EAGAIN.
+requested by setting the how field of struct args_expire to 1. If no
+expire candidates can be found the ioctl returns -1 with errno set to
+EAGAIN.
 
 This call causes the kernel module to check the mount corresponding
 to the given ioctlfd for mounts that can be expired, issues an expire
@@ -357,7 +369,8 @@ Checks if an autofs mount point is in use.
 
 The call requires an initialized struct autofs_dev_ioctl with the
 ioctlfd field set to the descriptor obtained from the open call and
-it returns the result in the arg1 field, 1 for busy and 0 otherwise.
+it returns the result in the may_umount field of struct args_askumount,
+1 for busy and 0 otherwise.
 
 
 AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD
@@ -369,12 +382,12 @@ The call requires an initialized struct autofs_dev_ioctl. There are two
 possible variations. Both use the path field set to the path of the mount
 point to check and the size field adjusted appropriately. One uses the
 ioctlfd field to identify a specific mount point to check while the other
-variation uses the path and optionally arg1 set to an autofs mount type.
-The call returns 1 if this is a mount point and sets arg1 to the device
-number of the mount and field arg2 to the relevant super block magic
-number (described below) or 0 if it isn't a mountpoint. In both cases
-the the device number (as returned by new_encode_dev()) is returned
-in field arg1.
+variation uses the path and optionally in.type field of struct args_ismountpoint
+set to an autofs mount type. The call returns 1 if this is a mount point
+and sets out.devid field to the device number of the mount and out.magic
+field to the relevant super block magic number (described below) or 0 if
+it isn't a mountpoint. In both cases the the device number (as returned
+by new_encode_dev()) is returned in out.devid field.
 
 If supplied with a file descriptor we're looking for a specific mount,
 not necessarily at the top of the mounted stack. In this case the path
index 39d02e1..8fac3fe 100644 (file)
@@ -203,9 +203,9 @@ initiated or is being considered, otherwise it returns 0.
 Mountpoint expiry
 -----------------
 
-The VFS has a mechansim for automatically expiring unused mounts,
+The VFS has a mechanism for automatically expiring unused mounts,
 much as it can expire any unused dentry information from the dcache.
-This is guided by the MNT_SHRINKABLE flag.  This  only applies to
+This is guided by the MNT_SHRINKABLE flag.  This only applies to
 mounts that were created by `d_automount()` returning a filesystem to be
 mounted.  As autofs doesn't return such a filesystem but leaves the
 mounting to the automount daemon, it must involve the automount daemon
@@ -298,7 +298,7 @@ remove directories and symlinks using normal filesystem operations.
 autofs knows whether a process requesting some operation is the daemon
 or not based on its process-group id number (see getpgid(1)).
 
-When an autofs filesystem it mounted the pgid of the mounting
+When an autofs filesystem is mounted the pgid of the mounting
 processes is recorded unless the "pgrp=" option is given, in which
 case that number is recorded instead.  Any request arriving from a
 process in that process group is considered to come from the daemon.
@@ -450,7 +450,7 @@ Commands are:
     numbers for existing filesystems can be found in
     `/proc/self/mountinfo`.
 - **AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD**: same as `close(ioctlfd)`.
-- **AUTOFS_DEV_IOCTL_SETPIPEFD_CMD**: if the  filesystem is in
+- **AUTOFS_DEV_IOCTL_SETPIPEFD_CMD**: if the filesystem is in
     catatonic mode, this can provide the write end of a new pipe
     in `arg1` to re-establish communication with a daemon.  The
     process group of the calling process is used to identify the
diff --git a/Documentation/filesystems/dnotify_test.c b/Documentation/filesystems/dnotify_test.c
deleted file mode 100644 (file)
index 8b37b4a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#define _GNU_SOURCE    /* needed to get the defines */
-#include <fcntl.h>     /* in glibc 2.2 this has the needed
-                                  values defined */
-#include <signal.h>
-#include <stdio.h>
-#include <unistd.h>
-
-static volatile int event_fd;
-
-static void handler(int sig, siginfo_t *si, void *data)
-{
-       event_fd = si->si_fd;
-}
-
-int main(void)
-{
-       struct sigaction act;
-       int fd;
-
-       act.sa_sigaction = handler;
-       sigemptyset(&act.sa_mask);
-       act.sa_flags = SA_SIGINFO;
-       sigaction(SIGRTMIN + 1, &act, NULL);
-
-       fd = open(".", O_RDONLY);
-       fcntl(fd, F_SETSIG, SIGRTMIN + 1);
-       fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT);
-       /* we will now be notified if any of the files
-          in "." is modified or new files are created */
-       while (1) {
-               pause();
-               printf("Got event on fd=%d\n", event_fd);
-       }
-}
index 3bb2613..37284bc 100644 (file)
@@ -53,9 +53,12 @@ u32 driver_features;
 DRIVER_USE_AGP
     Driver uses AGP interface, the DRM core will manage AGP resources.
 
-DRIVER_REQUIRE_AGP
-    Driver needs AGP interface to function. AGP initialization failure
-    will become a fatal error.
+DRIVER_LEGACY
+    Denote a legacy driver using shadow attach. Don't use.
+
+DRIVER_KMS_LEGACY_CONTEXT
+    Used only by nouveau for backwards compatibility with existing userspace.
+    Don't use.
 
 DRIVER_PCI_DMA
     Driver is capable of PCI DMA, mapping of PCI DMA buffers to
index 0b302fe..bb4254d 100644 (file)
@@ -2,38 +2,45 @@
 Mode Setting Helper Functions
 =============================
 
-The plane, CRTC, encoder and connector functions provided by the drivers
-implement the DRM API. They're called by the DRM core and ioctl handlers
-to handle device state changes and configuration request. As
-implementing those functions often requires logic not specific to
-drivers, mid-layer helper functions are available to avoid duplicating
-boilerplate code.
-
-The DRM core contains one mid-layer implementation. The mid-layer
-provides implementations of several plane, CRTC, encoder and connector
-functions (called from the top of the mid-layer) that pre-process
-requests and call lower-level functions provided by the driver (at the
-bottom of the mid-layer). For instance, the
-:c:func:`drm_crtc_helper_set_config()` function can be used to
-fill the :c:type:`struct drm_crtc_funcs <drm_crtc_funcs>`
-set_config field. When called, it will split the set_config operation
-in smaller, simpler operations and call the driver to handle them.
-
-To use the mid-layer, drivers call
-:c:func:`drm_crtc_helper_add()`,
-:c:func:`drm_encoder_helper_add()` and
-:c:func:`drm_connector_helper_add()` functions to install their
-mid-layer bottom operations handlers, and fill the :c:type:`struct
-drm_crtc_funcs <drm_crtc_funcs>`, :c:type:`struct
-drm_encoder_funcs <drm_encoder_funcs>` and :c:type:`struct
-drm_connector_funcs <drm_connector_funcs>` structures with
-pointers to the mid-layer top API functions. Installing the mid-layer
-bottom operation handlers is best done right after registering the
-corresponding KMS object.
-
-The mid-layer is not split between CRTC, encoder and connector
-operations. To use it, a driver must provide bottom functions for all of
-the three KMS entities.
+The DRM subsystem aims for a strong separation between core code and helper
+libraries. Core code takes care of general setup and teardown and decoding
+userspace requests to kernel internal objects. Everything else is handled by a
+large set of helper libraries, which can be combined freely to pick and choose
+for each driver what fits, and avoid shared code where special behaviour is
+needed.
+
+This distinction between core code and helpers is especially strong in the
+modesetting code, where there's a shared userspace ABI for all drivers. This is
+in contrast to the render side, where pretty much everything (with very few
+exceptions) can be considered optional helper code.
+
+There are a few areas these helpers can grouped into:
+
+* Helpers to implement modesetting. The important ones here are the atomic
+  helpers. Old drivers still often use the legacy CRTC helpers. They both share
+  the same set of common helper vtables. For really simple drivers (anything
+  that would have been a great fit in the deprecated fbdev subsystem) there's
+  also the simple display pipe helpers.
+
+* There's a big pile of helpers for handling outputs. First the generic bridge
+  helpers for handling encoder and transcoder IP blocks. Second the panel helpers
+  for handling panel-related information and logic. Plus then a big set of
+  helpers for the various sink standards (DisplayPort, HDMI, MIPI DSI). Finally
+  there's also generic helpers for handling output probing, and for dealing with
+  EDIDs.
+
+* The last group of helpers concerns itself with the frontend side of a display
+  pipeline: Planes, handling rectangles for visibility checking and scissoring,
+  flip queues and assorted bits.
+
+Modeset Helper Reference for Common Vtables
+===========================================
+
+.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
+   :internal:
+
+.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
+   :doc: overview
 
 Atomic Modeset Helper Functions Reference
 =========================================
@@ -62,33 +69,27 @@ Atomic State Reset and Initialization
 .. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c
    :export:
 
-Modeset Helper Reference for Common Vtables
-===========================================
-
-.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
-   :internal:
-
-.. kernel-doc:: include/drm/drm_modeset_helper_vtables.h
-   :doc: overview
-
 Legacy CRTC/Modeset Helper Functions Reference
 ==============================================
 
 .. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
-   :export:
+   :doc: overview
 
 .. kernel-doc:: drivers/gpu/drm/drm_crtc_helper.c
-   :doc: overview
+   :export:
 
-Output Probing Helper Functions Reference
-=========================================
+Simple KMS Helper Reference
+===========================
 
-.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
-   :doc: output probing helper overview
+.. kernel-doc:: include/drm/drm_simple_kms_helper.h
+   :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
+.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
    :export:
 
+.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
+   :doc: overview
+
 fbdev Helper Functions Reference
 ================================
 
@@ -110,6 +111,43 @@ Framebuffer CMA Helper Functions Reference
 .. kernel-doc:: drivers/gpu/drm/drm_fb_cma_helper.c
    :export:
 
+Bridges
+=======
+
+Overview
+--------
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: overview
+
+Default bridge callback sequence
+--------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: bridge callbacks
+
+
+Bridge Helper Reference
+-------------------------
+
+.. kernel-doc:: include/drm/drm_bridge.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :export:
+
+Panel Helper Reference
+======================
+
+.. kernel-doc:: include/drm/drm_panel.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_panel.c
+   :export:
+
+.. kernel-doc:: drivers/gpu/drm/drm_panel.c
+   :doc: drm panel
+
 Display Port Helper Functions Reference
 =======================================
 
@@ -158,9 +196,21 @@ MIPI DSI Helper Functions Reference
 .. kernel-doc:: drivers/gpu/drm/drm_mipi_dsi.c
    :export:
 
+Output Probing Helper Functions Reference
+=========================================
+
+.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
+   :doc: output probing helper overview
+
+.. kernel-doc:: drivers/gpu/drm/drm_probe_helper.c
+   :export:
+
 EDID Helper Functions Reference
 ===============================
 
+.. kernel-doc:: include/drm/drm_edid.h
+   :internal:
+
 .. kernel-doc:: drivers/gpu/drm/drm_edid.c
    :export:
 
@@ -176,18 +226,6 @@ Rectangle Utilities Reference
 .. kernel-doc:: drivers/gpu/drm/drm_rect.c
    :export:
 
-Flip-work Helper Reference
-==========================
-
-.. kernel-doc:: include/drm/drm_flip_work.h
-   :doc: flip utils
-
-.. kernel-doc:: include/drm/drm_flip_work.h
-   :internal:
-
-.. kernel-doc:: drivers/gpu/drm/drm_flip_work.c
-   :export:
-
 HDMI Infoframes Helper Reference
 ================================
 
@@ -202,59 +240,40 @@ libraries and hence is also included here.
 .. kernel-doc:: drivers/video/hdmi.c
    :export:
 
-Plane Helper Reference
-======================
-
-.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
-   :export:
-
-.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
-   :doc: overview
+Flip-work Helper Reference
+==========================
 
-Tile group
-----------
+.. kernel-doc:: include/drm/drm_flip_work.h
+   :doc: flip utils
 
-.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
-   :doc: Tile group
+.. kernel-doc:: include/drm/drm_flip_work.h
+   :internal:
 
-Bridges
-=======
+.. kernel-doc:: drivers/gpu/drm/drm_flip_work.c
+   :export:
 
-Overview
---------
+Plane Helper Reference
+======================
 
-.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
    :doc: overview
 
-Default bridge callback sequence
---------------------------------
-
-.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
-   :doc: bridge callbacks
-
-.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+.. kernel-doc:: drivers/gpu/drm/drm_plane_helper.c
    :export:
 
-Panel Helper Reference
-======================
-
-.. kernel-doc:: include/drm/drm_panel.h
-   :internal:
+Tile group
+==========
 
-.. kernel-doc:: drivers/gpu/drm/drm_panel.c
-   :export:
+# FIXME: This should probably be moved into a property documentation section
 
-.. kernel-doc:: drivers/gpu/drm/drm_panel.c
-   :doc: drm panel
+.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
+   :doc: Tile group
 
-Simple KMS Helper Reference
-===========================
+Auxiliary Modeset Helpers
+=========================
 
-.. kernel-doc:: include/drm/drm_simple_kms_helper.h
-   :internal:
+.. kernel-doc:: drivers/gpu/drm/drm_modeset_helper.c
+   :doc: aux kms helpers
 
-.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
+.. kernel-doc:: drivers/gpu/drm/drm_modeset_helper.c
    :export:
-
-.. kernel-doc:: drivers/gpu/drm/drm_simple_kms_helper.c
-   :doc: overview
index 8dfa4b2..53b872c 100644 (file)
@@ -2,9 +2,6 @@
 Kernel Mode Setting (KMS)
 =========================
 
-Mode Setting
-============
-
 Drivers must initialize the mode setting core by calling
 :c:func:`drm_mode_config_init()` on the DRM device. The function
 initializes the :c:type:`struct drm_device <drm_device>`
@@ -18,60 +15,59 @@ be setup by initializing the following fields.
 -  struct drm_mode_config_funcs \*funcs;
    Mode setting functions.
 
-Display Modes Function Reference
---------------------------------
+Modeset Base Object Abstraction
+===============================
 
-.. kernel-doc:: include/drm/drm_modes.h
+.. kernel-doc:: include/drm/drm_mode_object.h
    :internal:
 
-.. kernel-doc:: drivers/gpu/drm/drm_modes.c
+.. kernel-doc:: drivers/gpu/drm/drm_mode_object.c
+   :export:
+
+KMS Data Structures
+===================
+
+.. kernel-doc:: include/drm/drm_crtc.h
+   :internal:
+
+KMS API Functions
+=================
+
+.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
    :export:
 
 Atomic Mode Setting Function Reference
---------------------------------------
+======================================
 
 .. kernel-doc:: drivers/gpu/drm/drm_atomic.c
    :export:
 
-.. kernel-doc:: drivers/gpu/drm/drm_atomic.c
+.. kernel-doc:: include/drm/drm_atomic.h
    :internal:
 
 Frame Buffer Abstraction
-------------------------
-
-Frame buffers are abstract memory objects that provide a source of
-pixels to scanout to a CRTC. Applications explicitly request the
-creation of frame buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls
-and receive an opaque handle that can be passed to the KMS CRTC control,
-plane configuration and page flip functions.
-
-Frame buffers rely on the underneath memory manager for low-level memory
-operations. When creating a frame buffer applications pass a memory
-handle (or a list of memory handles for multi-planar formats) through
-the ``drm_mode_fb_cmd2`` argument. For drivers using GEM as their
-userspace buffer management interface this would be a GEM handle.
-Drivers are however free to use their own backing storage object
-handles, e.g. vmwgfx directly exposes special TTM handles to userspace
-and so expects TTM handles in the create ioctl and not GEM handles.
-
-The lifetime of a drm framebuffer is controlled with a reference count,
-drivers can grab additional references with
-:c:func:`drm_framebuffer_reference()`and drop them again with
-:c:func:`drm_framebuffer_unreference()`. For driver-private
-framebuffers for which the last reference is never dropped (e.g. for the
-fbdev framebuffer when the struct :c:type:`struct drm_framebuffer
-<drm_framebuffer>` is embedded into the fbdev helper struct)
-drivers can manually clean up a framebuffer at module unload time with
-:c:func:`drm_framebuffer_unregister_private()`.
+========================
+
+.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
+   :doc: overview
+
+Frame Buffer Functions Reference
+--------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_framebuffer.c
+   :export:
+
+.. kernel-doc:: include/drm/drm_framebuffer.h
+   :internal:
 
 DRM Format Handling
--------------------
+===================
 
 .. kernel-doc:: drivers/gpu/drm/drm_fourcc.c
    :export:
 
 Dumb Buffer Objects
--------------------
+===================
 
 The KMS API doesn't standardize backing storage object creation and
 leaves it to driver-specific ioctls. Furthermore actually creating a
@@ -114,14 +110,59 @@ Note that dumb objects may not be used for gpu acceleration, as has been
 attempted on some ARM embedded platforms. Such drivers really must have
 a hardware-specific ioctl to allocate suitable buffer objects.
 
-Output Polling
---------------
+Plane Abstraction
+=================
+
+.. kernel-doc:: drivers/gpu/drm/drm_plane.c
+   :doc: overview
+
+Plane Functions Reference
+-------------------------
+
+.. kernel-doc:: include/drm/drm_plane.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_plane.c
+   :export:
+
+Display Modes Function Reference
+================================
+
+.. kernel-doc:: include/drm/drm_modes.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_modes.c
+   :export:
 
-void (\*output_poll_changed)(struct drm_device \*dev);
-This operation notifies the driver that the status of one or more
-connectors has changed. Drivers that use the fb helper can just call the
-:c:func:`drm_fb_helper_hotplug_event()` function to handle this
-operation.
+Connector Abstraction
+=====================
+
+.. kernel-doc:: drivers/gpu/drm/drm_connector.c
+   :doc: overview
+
+Connector Functions Reference
+-----------------------------
+
+.. kernel-doc:: include/drm/drm_connector.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_connector.c
+   :export:
+
+Encoder Abstraction
+===================
+
+.. kernel-doc:: drivers/gpu/drm/drm_encoder.c
+   :doc: overview
+
+Encoder Functions Reference
+---------------------------
+
+.. kernel-doc:: include/drm/drm_encoder.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_encoder.c
+   :export:
 
 KMS Initialization and Cleanup
 ==============================
@@ -151,250 +192,6 @@ allocated and zeroed by the driver, possibly as part of a larger
 structure, and registered with a call to :c:func:`drm_crtc_init()`
 with a pointer to CRTC functions.
 
-Planes (:c:type:`struct drm_plane <drm_plane>`)
------------------------------------------------
-
-A plane represents an image source that can be blended with or overlayed
-on top of a CRTC during the scanout process. Planes are associated with
-a frame buffer to crop a portion of the image memory (source) and
-optionally scale it to a destination size. The result is then blended
-with or overlayed on top of a CRTC.
-
-The DRM core recognizes three types of planes:
-
--  DRM_PLANE_TYPE_PRIMARY represents a "main" plane for a CRTC.
-   Primary planes are the planes operated upon by CRTC modesetting and
-   flipping operations described in the page_flip hook in
-   :c:type:`struct drm_crtc_funcs <drm_crtc_funcs>`.
--  DRM_PLANE_TYPE_CURSOR represents a "cursor" plane for a CRTC.
-   Cursor planes are the planes operated upon by the
-   DRM_IOCTL_MODE_CURSOR and DRM_IOCTL_MODE_CURSOR2 ioctls.
--  DRM_PLANE_TYPE_OVERLAY represents all non-primary, non-cursor
-   planes. Some drivers refer to these types of planes as "sprites"
-   internally.
-
-For compatibility with legacy userspace, only overlay planes are made
-available to userspace by default. Userspace clients may set the
-DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate
-that they wish to receive a universal plane list containing all plane
-types.
-
-Plane Initialization
-~~~~~~~~~~~~~~~~~~~~
-
-To create a plane, a KMS drivers allocates and zeroes an instances of
-:c:type:`struct drm_plane <drm_plane>` (possibly as part of a
-larger structure) and registers it with a call to
-:c:func:`drm_universal_plane_init()`. The function takes a
-bitmask of the CRTCs that can be associated with the plane, a pointer to
-the plane functions, a list of format supported formats, and the type of
-plane (primary, cursor, or overlay) being initialized.
-
-Cursor and overlay planes are optional. All drivers should provide one
-primary plane per CRTC (although this requirement may change in the
-future); drivers that do not wish to provide special handling for
-primary planes may make use of the helper functions described in ? to
-create and register a primary plane with standard capabilities.
-
-Encoders (:c:type:`struct drm_encoder <drm_encoder>`)
------------------------------------------------------
-
-An encoder takes pixel data from a CRTC and converts it to a format
-suitable for any attached connectors. On some devices, it may be
-possible to have a CRTC send data to more than one encoder. In that
-case, both encoders would receive data from the same scanout buffer,
-resulting in a "cloned" display configuration across the connectors
-attached to each encoder.
-
-Encoder Initialization
-~~~~~~~~~~~~~~~~~~~~~~
-
-As for CRTCs, a KMS driver must create, initialize and register at least
-one :c:type:`struct drm_encoder <drm_encoder>` instance. The
-instance is allocated and zeroed by the driver, possibly as part of a
-larger structure.
-
-Drivers must initialize the :c:type:`struct drm_encoder
-<drm_encoder>` possible_crtcs and possible_clones fields before
-registering the encoder. Both fields are bitmasks of respectively the
-CRTCs that the encoder can be connected to, and sibling encoders
-candidate for cloning.
-
-After being initialized, the encoder must be registered with a call to
-:c:func:`drm_encoder_init()`. The function takes a pointer to the
-encoder functions and an encoder type. Supported types are
-
--  DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A
--  DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort
--  DRM_MODE_ENCODER_LVDS for display panels
--  DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video,
-   Component, SCART)
--  DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
-
-Encoders must be attached to a CRTC to be used. DRM drivers leave
-encoders unattached at initialization time. Applications (or the fbdev
-compatibility layer when implemented) are responsible for attaching the
-encoders they want to use to a CRTC.
-
-Connectors (:c:type:`struct drm_connector <drm_connector>`)
------------------------------------------------------------
-
-A connector is the final destination for pixel data on a device, and
-usually connects directly to an external display device like a monitor
-or laptop panel. A connector can only be attached to one encoder at a
-time. The connector is also the structure where information about the
-attached display is kept, so it contains fields for display data, EDID
-data, DPMS & connection status, and information about modes supported on
-the attached displays.
-
-Connector Initialization
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Finally a KMS driver must create, initialize, register and attach at
-least one :c:type:`struct drm_connector <drm_connector>`
-instance. The instance is created as other KMS objects and initialized
-by setting the following fields.
-
-interlace_allowed
-    Whether the connector can handle interlaced modes.
-
-doublescan_allowed
-    Whether the connector can handle doublescan.
-
-display_info
-    Display information is filled from EDID information when a display
-    is detected. For non hot-pluggable displays such as flat panels in
-    embedded systems, the driver should initialize the
-    display_info.width_mm and display_info.height_mm fields with the
-    physical size of the display.
-
-polled
-    Connector polling mode, a combination of
-
-    DRM_CONNECTOR_POLL_HPD
-        The connector generates hotplug events and doesn't need to be
-        periodically polled. The CONNECT and DISCONNECT flags must not
-        be set together with the HPD flag.
-
-    DRM_CONNECTOR_POLL_CONNECT
-        Periodically poll the connector for connection.
-
-    DRM_CONNECTOR_POLL_DISCONNECT
-        Periodically poll the connector for disconnection.
-
-    Set to 0 for connectors that don't support connection status
-    discovery.
-
-The connector is then registered with a call to
-:c:func:`drm_connector_init()` with a pointer to the connector
-functions and a connector type, and exposed through sysfs with a call to
-:c:func:`drm_connector_register()`.
-
-Supported connector types are
-
--  DRM_MODE_CONNECTOR_VGA
--  DRM_MODE_CONNECTOR_DVII
--  DRM_MODE_CONNECTOR_DVID
--  DRM_MODE_CONNECTOR_DVIA
--  DRM_MODE_CONNECTOR_Composite
--  DRM_MODE_CONNECTOR_SVIDEO
--  DRM_MODE_CONNECTOR_LVDS
--  DRM_MODE_CONNECTOR_Component
--  DRM_MODE_CONNECTOR_9PinDIN
--  DRM_MODE_CONNECTOR_DisplayPort
--  DRM_MODE_CONNECTOR_HDMIA
--  DRM_MODE_CONNECTOR_HDMIB
--  DRM_MODE_CONNECTOR_TV
--  DRM_MODE_CONNECTOR_eDP
--  DRM_MODE_CONNECTOR_VIRTUAL
-
-Connectors must be attached to an encoder to be used. For devices that
-map connectors to encoders 1:1, the connector should be attached at
-initialization time with a call to
-:c:func:`drm_mode_connector_attach_encoder()`. The driver must
-also set the :c:type:`struct drm_connector <drm_connector>`
-encoder field to point to the attached encoder.
-
-Finally, drivers must initialize the connectors state change detection
-with a call to :c:func:`drm_kms_helper_poll_init()`. If at least
-one connector is pollable but can't generate hotplug interrupts
-(indicated by the DRM_CONNECTOR_POLL_CONNECT and
-DRM_CONNECTOR_POLL_DISCONNECT connector flags), a delayed work will
-automatically be queued to periodically poll for changes. Connectors
-that can generate hotplug interrupts must be marked with the
-DRM_CONNECTOR_POLL_HPD flag instead, and their interrupt handler must
-call :c:func:`drm_helper_hpd_irq_event()`. The function will
-queue a delayed work to check the state of all connectors, but no
-periodic polling will be done.
-
-Connector Operations
-~~~~~~~~~~~~~~~~~~~~
-
-    **Note**
-
-    Unless otherwise state, all operations are mandatory.
-
-DPMS
-''''
-
-void (\*dpms)(struct drm_connector \*connector, int mode);
-The DPMS operation sets the power state of a connector. The mode
-argument is one of
-
--  DRM_MODE_DPMS_ON
-
--  DRM_MODE_DPMS_STANDBY
-
--  DRM_MODE_DPMS_SUSPEND
-
--  DRM_MODE_DPMS_OFF
-
-In all but DPMS_ON mode the encoder to which the connector is attached
-should put the display in low-power mode by driving its signals
-appropriately. If more than one connector is attached to the encoder
-care should be taken not to change the power state of other displays as
-a side effect. Low-power mode should be propagated to the encoders and
-CRTCs when all related connectors are put in low-power mode.
-
-Modes
-'''''
-
-int (\*fill_modes)(struct drm_connector \*connector, uint32_t
-max_width, uint32_t max_height);
-Fill the mode list with all supported modes for the connector. If the
-``max_width`` and ``max_height`` arguments are non-zero, the
-implementation must ignore all modes wider than ``max_width`` or higher
-than ``max_height``.
-
-The connector must also fill in this operation its display_info
-width_mm and height_mm fields with the connected display physical size
-in millimeters. The fields should be set to 0 if the value isn't known
-or is not applicable (for instance for projector devices).
-
-Connection Status
-'''''''''''''''''
-
-The connection status is updated through polling or hotplug events when
-supported (see ?). The status value is reported to userspace through
-ioctls and must not be used inside the driver, as it only gets
-initialized by a call to :c:func:`drm_mode_getconnector()` from
-userspace.
-
-enum drm_connector_status (\*detect)(struct drm_connector
-\*connector, bool force);
-Check to see if anything is attached to the connector. The ``force``
-parameter is set to false whilst polling or to true when checking the
-connector due to user request. ``force`` can be used by the driver to
-avoid expensive, destructive operations during automated probing.
-
-Return connector_status_connected if something is connected to the
-connector, connector_status_disconnected if nothing is connected and
-connector_status_unknown if the connection state isn't known.
-
-Drivers should only return connector_status_connected if the
-connection status has really been probed as connected. Connectors that
-can't detect the connection status, or failed connection status probes,
-should return connector_status_unknown.
 
 Cleanup
 -------
@@ -463,20 +260,8 @@ created for fetching EDID data and performing monitor detection. Once
 the process is complete, the new connector is registered with sysfs to
 make its properties available to applications.
 
-KMS API Functions
------------------
-
-.. kernel-doc:: drivers/gpu/drm/drm_crtc.c
-   :export:
-
-KMS Data Structures
--------------------
-
-.. kernel-doc:: include/drm/drm_crtc.h
-   :internal:
-
 KMS Locking
------------
+===========
 
 .. kernel-doc:: drivers/gpu/drm/drm_modeset_lock.c
    :doc: kms locking
@@ -490,90 +275,38 @@ KMS Locking
 KMS Properties
 ==============
 
-Drivers may need to expose additional parameters to applications than
-those described in the previous sections. KMS supports attaching
-properties to CRTCs, connectors and planes and offers a userspace API to
-list, get and set the property values.
-
-Properties are identified by a name that uniquely defines the property
-purpose, and store an associated value. For all property types except
-blob properties the value is a 64-bit unsigned integer.
-
-KMS differentiates between properties and property instances. Drivers
-first create properties and then create and associate individual
-instances of those properties to objects. A property can be instantiated
-multiple times and associated with different objects. Values are stored
-in property instances, and all other property information are stored in
-the property and shared between all instances of the property.
-
-Every property is created with a type that influences how the KMS core
-handles the property. Supported property types are
-
-DRM_MODE_PROP_RANGE
-    Range properties report their minimum and maximum admissible values.
-    The KMS core verifies that values set by application fit in that
-    range.
-
-DRM_MODE_PROP_ENUM
-    Enumerated properties take a numerical value that ranges from 0 to
-    the number of enumerated values defined by the property minus one,
-    and associate a free-formed string name to each value. Applications
-    can retrieve the list of defined value-name pairs and use the
-    numerical value to get and set property instance values.
-
-DRM_MODE_PROP_BITMASK
-    Bitmask properties are enumeration properties that additionally
-    restrict all enumerated values to the 0..63 range. Bitmask property
-    instance values combine one or more of the enumerated bits defined
-    by the property.
-
-DRM_MODE_PROP_BLOB
-    Blob properties store a binary blob without any format restriction.
-    The binary blobs are created as KMS standalone objects, and blob
-    property instance values store the ID of their associated blob
-    object.
-
-    Blob properties are only used for the connector EDID property and
-    cannot be created by drivers.
-
-To create a property drivers call one of the following functions
-depending on the property type. All property creation functions take
-property flags and name, as well as type-specific arguments.
-
--  struct drm_property \*drm_property_create_range(struct
-   drm_device \*dev, int flags, const char \*name, uint64_t min,
-   uint64_t max);
-   Create a range property with the given minimum and maximum values.
-
--  struct drm_property \*drm_property_create_enum(struct drm_device
-   \*dev, int flags, const char \*name, const struct
-   drm_prop_enum_list \*props, int num_values);
-   Create an enumerated property. The ``props`` argument points to an
-   array of ``num_values`` value-name pairs.
-
--  struct drm_property \*drm_property_create_bitmask(struct
-   drm_device \*dev, int flags, const char \*name, const struct
-   drm_prop_enum_list \*props, int num_values);
-   Create a bitmask property. The ``props`` argument points to an array
-   of ``num_values`` value-name pairs.
-
-Properties can additionally be created as immutable, in which case they
-will be read-only for applications but can be modified by the driver. To
-create an immutable property drivers must set the
-DRM_MODE_PROP_IMMUTABLE flag at property creation time.
-
-When no array of value-name pairs is readily available at property
-creation time for enumerated or range properties, drivers can create the
-property using the :c:func:`drm_property_create()` function and
-manually add enumeration value-name pairs by calling the
-:c:func:`drm_property_add_enum()` function. Care must be taken to
-properly specify the property type through the ``flags`` argument.
-
-After creating properties drivers can attach property instances to CRTC,
-connector and plane objects by calling the
-:c:func:`drm_object_attach_property()`. The function takes a
-pointer to the target object, a pointer to the previously created
-property and an initial instance value.
+Property Types and Blob Property Support
+----------------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_property.c
+   :doc: overview
+
+.. kernel-doc:: include/drm/drm_property.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_property.c
+   :export:
+
+Plane Composition Properties
+----------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_blend.c
+   :doc: overview
+
+.. kernel-doc:: drivers/gpu/drm/drm_blend.c
+   :export:
+
+Color Management Properties
+---------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
+   :doc: overview
+
+.. kernel-doc:: include/drm/drm_color_mgmt.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c
+   :export:
 
 Existing KMS Properties
 -----------------------
index 59f9822..bca8085 100644 (file)
@@ -26,12 +26,12 @@ TTM, but has no video RAM management capabilities and is thus limited to
 UMA devices.
 
 The Translation Table Manager (TTM)
------------------------------------
+===================================
 
 TTM design background and information belongs here.
 
 TTM initialization
-~~~~~~~~~~~~~~~~~~
+------------------
 
     **Warning**
 
@@ -77,7 +77,7 @@ object, ttm_global_item_ref() is used to create an initial reference
 count for the TTM, which will call your initialization function.
 
 The Graphics Execution Manager (GEM)
-------------------------------------
+====================================
 
 The GEM design approach has resulted in a memory manager that doesn't
 provide full coverage of all (or even all common) use cases in its
@@ -114,7 +114,7 @@ read & write, mapping, and domain ownership transfers are left to
 driver-specific ioctls.
 
 GEM Initialization
-~~~~~~~~~~~~~~~~~~
+------------------
 
 Drivers that use GEM must set the DRIVER_GEM bit in the struct
 :c:type:`struct drm_driver <drm_driver>` driver_features
@@ -132,7 +132,7 @@ typically not managed by GEM, and must be initialized separately into
 its own DRM MM object.
 
 GEM Objects Creation
-~~~~~~~~~~~~~~~~~~~~
+--------------------
 
 GEM splits creation of GEM objects and allocation of the memory that
 backs them in two distinct operations.
@@ -173,7 +173,7 @@ a call to :c:func:`drm_gem_private_object_init()` instead of
 must be managed by drivers.
 
 GEM Objects Lifetime
-~~~~~~~~~~~~~~~~~~~~
+--------------------
 
 All GEM objects are reference-counted by the GEM core. References can be
 acquired and release by :c:func:`calling
@@ -196,7 +196,7 @@ resources created by the GEM core, which need to be released with
 :c:func:`drm_gem_object_release()`.
 
 GEM Objects Naming
-~~~~~~~~~~~~~~~~~~
+------------------
 
 Communication between userspace and the kernel refers to GEM objects
 using local handles, global names or, more recently, file descriptors.
@@ -245,7 +245,7 @@ Furthermore PRIME also allows cross-device buffer sharing since it is
 based on dma-bufs.
 
 GEM Objects Mapping
-~~~~~~~~~~~~~~~~~~~
+-------------------
 
 Because mapping operations are fairly heavyweight GEM favours
 read/write-like access to buffers, implemented through driver-specific
@@ -304,7 +304,7 @@ Drivers that want to map the GEM object upfront instead of handling page
 faults can implement their own mmap file operation handler.
 
 Memory Coherency
-~~~~~~~~~~~~~~~~
+----------------
 
 When mapped to the device or used in a command buffer, backing pages for
 an object are flushed to memory and marked write combined so as to be
@@ -320,7 +320,7 @@ blocks the client and waits for rendering to complete before performing
 any necessary flushing operations).
 
 Command Execution
-~~~~~~~~~~~~~~~~~
+-----------------
 
 Perhaps the most important GEM function for GPU devices is providing a
 command execution interface to clients. Client programs construct
@@ -348,8 +348,20 @@ GEM Function Reference
 .. kernel-doc:: include/drm/drm_gem.h
    :internal:
 
+GEM CMA Helper Functions Reference
+----------------------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
+   :doc: cma helpers
+
+.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
+   :export:
+
+.. kernel-doc:: include/drm/drm_gem_cma_helper.h
+   :internal:
+
 VMA Offset Manager
-------------------
+==================
 
 .. kernel-doc:: drivers/gpu/drm/drm_vma_manager.c
    :doc: vma offset manager
@@ -361,14 +373,14 @@ VMA Offset Manager
    :internal:
 
 PRIME Buffer Sharing
---------------------
+====================
 
 PRIME is the cross device buffer sharing framework in drm, originally
 created for the OPTIMUS range of multi-gpu platforms. To userspace PRIME
 buffers are dma-buf based file descriptors.
 
 Overview and Driver Interface
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-----------------------------
 
 Similar to GEM global names, PRIME file descriptors are also used to
 share buffer objects across processes. They offer additional security:
@@ -406,7 +418,7 @@ struct drm_gem_object \*obj, int flags); struct drm_gem_object \*
 support PRIME.
 
 PRIME Helper Functions
-~~~~~~~~~~~~~~~~~~~~~~
+----------------------
 
 .. kernel-doc:: drivers/gpu/drm/drm_prime.c
    :doc: PRIME Helpers
@@ -418,16 +430,16 @@ PRIME Function References
    :export:
 
 DRM MM Range Allocator
-----------------------
+======================
 
 Overview
-~~~~~~~~
+--------
 
 .. kernel-doc:: drivers/gpu/drm/drm_mm.c
    :doc: Overview
 
 LRU Scan/Eviction Support
-~~~~~~~~~~~~~~~~~~~~~~~~~
+-------------------------
 
 .. kernel-doc:: drivers/gpu/drm/drm_mm.c
    :doc: lru scan roaster
@@ -440,15 +452,3 @@ DRM MM Range Allocator Function References
 
 .. kernel-doc:: include/drm/drm_mm.h
    :internal:
-
-CMA Helper Functions Reference
-------------------------------
-
-.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
-   :doc: cma helpers
-
-.. kernel-doc:: drivers/gpu/drm/drm_gem_cma_helper.c
-   :export:
-
-.. kernel-doc:: include/drm/drm_gem_cma_helper.h
-   :internal:
index 536bf3e..1ba301c 100644 (file)
@@ -33,6 +33,76 @@ Primary Nodes, DRM Master and Authentication
 .. kernel-doc:: include/drm/drm_auth.h
    :internal:
 
+Open-Source Userspace Requirements
+==================================
+
+The DRM subsystem has stricter requirements than most other kernel subsystems on
+what the userspace side for new uAPI needs to look like. This section here
+explains what exactly those requirements are, and why they exist.
+
+The short summary is that any addition of DRM uAPI requires corresponding
+open-sourced userspace patches, and those patches must be reviewed and ready for
+merging into a suitable and canonical upstream project.
+
+GFX devices (both display and render/GPU side) are really complex bits of
+hardware, with userspace and kernel by necessity having to work together really
+closely.  The interfaces, for rendering and modesetting, must be extremely wide
+and flexible, and therefore it is almost always impossible to precisely define
+them for every possible corner case. This in turn makes it really practically
+infeasible to differentiate between behaviour that's required by userspace, and
+which must not be changed to avoid regressions, and behaviour which is only an
+accidental artifact of the current implementation.
+
+Without access to the full source code of all userspace users that means it
+becomes impossible to change the implementation details, since userspace could
+depend upon the accidental behaviour of the current implementation in minute
+details. And debugging such regressions without access to source code is pretty
+much impossible. As a consequence this means:
+
+- The Linux kernel's "no regression" policy holds in practice only for
+  open-source userspace of the DRM subsystem. DRM developers are perfectly fine
+  if closed-source blob drivers in userspace use the same uAPI as the open
+  drivers, but they must do so in the exact same way as the open drivers.
+  Creative (ab)use of the interfaces will, and in the past routinely has, lead
+  to breakage.
+
+- Any new userspace interface must have an open-source implementation as
+  demonstration vehicle.
+
+The other reason for requiring open-source userspace is uAPI review. Since the
+kernel and userspace parts of a GFX stack must work together so closely, code
+review can only assess whether a new interface achieves its goals by looking at
+both sides. Making sure that the interface indeed covers the use-case fully
+leads to a few additional requirements:
+
+- The open-source userspace must not be a toy/test application, but the real
+  thing. Specifically it needs to handle all the usual error and corner cases.
+  These are often the places where new uAPI falls apart and hence essential to
+  assess the fitness of a proposed interface.
+
+- The userspace side must be fully reviewed and tested to the standards of that
+  userspace project. For e.g. mesa this means piglit testcases and review on the
+  mailing list. This is again to ensure that the new interface actually gets the
+  job done.
+
+- The userspace patches must be against the canonical upstream, not some vendor
+  fork. This is to make sure that no one cheats on the review and testing
+  requirements by doing a quick fork.
+
+- The kernel patch can only be merged after all the above requirements are met,
+  but it **must** be merged **before** the userspace patches land. uAPI always flows
+  from the kernel, doing things the other way round risks divergence of the uAPI
+  definitions and header files.
+
+These are fairly steep requirements, but have grown out from years of shared
+pain and experience with uAPI added hastily, and almost always regretted about
+just as fast. GFX devices change really fast, requiring a paradigm shift and
+entire new set of uAPI interfaces every few years at least. Together with the
+Linux kernel's guarantee to keep existing userspace running for 10+ years this
+is already rather painful for the DRM subsystem, with multiple different uAPIs
+for the same thing co-existing. If we add a few more complete mistakes into the
+mix every year it would be entirely unmanageable.
+
 Render nodes
 ============
 
@@ -86,6 +156,43 @@ other hand, a driver requires shared state between clients which is
 visible to user-space and accessible beyond open-file boundaries, they
 cannot support render nodes.
 
+Validating changes with IGT
+===========================
+
+There's a collection of tests that aims to cover the whole functionality of
+DRM drivers and that can be used to check that changes to DRM drivers or the
+core don't regress existing functionality. This test suite is called IGT and
+its code can be found in https://cgit.freedesktop.org/drm/igt-gpu-tools/.
+
+To build IGT, start by installing its build dependencies. In Debian-based
+systems::
+
+       # apt-get build-dep intel-gpu-tools
+
+And in Fedora-based systems::
+
+       # dnf builddep intel-gpu-tools
+
+Then clone the repository::
+
+       $ git clone git://anongit.freedesktop.org/drm/igt-gpu-tools
+
+Configure the build system and start the build::
+
+       $ cd igt-gpu-tools && ./autogen.sh && make -j6
+
+Download the piglit dependency::
+
+       $ ./scripts/run-tests.sh -d
+
+And run the tests::
+
+       $ ./scripts/run-tests.sh -t kms -t core -s
+
+run-tests.sh is a wrapper around piglit that will execute the tests matching
+the -t options. A report in HTML format will be available in
+./results/html/index.html. Results can be compared with piglit.
+
 VBlank event handling
 =====================
 
index 2fe5952..87aaffc 100644 (file)
@@ -70,6 +70,9 @@ Frontbuffer Tracking
 .. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
    :doc: frontbuffer tracking
 
+.. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.h
+   :internal:
+
 .. kernel-doc:: drivers/gpu/drm/i915/intel_frontbuffer.c
    :internal:
 
index 5ff3d2b..be0dafc 100644 (file)
@@ -12,6 +12,7 @@ Linux GPU Driver Developer's Guide
    drm-uapi
    i915
    vga-switcheroo
+   vgaarbiter
 
 .. only::  subproject
 
index 4c5ce3e..981873a 100644 (file)
@@ -1,23 +1,10 @@
 Owner Module/Drivers,Group,Property Name,Type,Property Values,Object attached,Description/Restrictions
-DRM,Generic,“rotation”,BITMASK,"{ 0, ""rotate-0"" }, { 1, ""rotate-90"" }, { 2, ""rotate-180"" }, { 3, ""rotate-270"" }, { 4, ""reflect-x"" }, { 5, ""reflect-y"" }","CRTC, Plane",rotate-(degrees) rotates the image by the specified amount in degrees in counter clockwise direction. reflect-x and reflect-y reflects the image along the specified axis prior to rotation
 ,,“scaling mode”,ENUM,"{ ""None"", ""Full"", ""Center"", ""Full aspect"" }",Connector,"Supported by: amdgpu, gma500, i915, nouveau and radeon."
 ,Connector,“EDID”,BLOB | IMMUTABLE,0,Connector,Contains id of edid blob ptr object.
 ,,“DPMS”,ENUM,"{ “On”, “Standby”, “Suspend”, “Off” }",Connector,Contains DPMS operation mode value.
 ,,“PATH”,BLOB | IMMUTABLE,0,Connector,Contains topology path to a connector.
 ,,“TILE”,BLOB | IMMUTABLE,0,Connector,Contains tiling information for a connector.
 ,,“CRTC_ID”,OBJECT,DRM_MODE_OBJECT_CRTC,Connector,CRTC that connector is attached to (atomic)
-,Plane,“type”,ENUM | IMMUTABLE,"{ ""Overlay"", ""Primary"", ""Cursor"" }",Plane,Plane type
-,,“SRC_X”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source x coordinate in 16.16 fixed point (atomic)
-,,“SRC_Y”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source y coordinate in 16.16 fixed point (atomic)
-,,“SRC_W”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source width in 16.16 fixed point (atomic)
-,,“SRC_H”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout source height in 16.16 fixed point (atomic)
-,,“CRTC_X”,SIGNED_RANGE,"Min=INT_MIN, Max=INT_MAX",Plane,Scanout CRTC (destination) x coordinate (atomic)
-,,“CRTC_Y”,SIGNED_RANGE,"Min=INT_MIN, Max=INT_MAX",Plane,Scanout CRTC (destination) y coordinate (atomic)
-,,“CRTC_W”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout CRTC (destination) width (atomic)
-,,“CRTC_H”,RANGE,"Min=0, Max=UINT_MAX",Plane,Scanout CRTC (destination) height (atomic)
-,,“FB_ID”,OBJECT,DRM_MODE_OBJECT_FB,Plane,Scanout framebuffer (atomic)
-,,“CRTC_ID”,OBJECT,DRM_MODE_OBJECT_CRTC,Plane,CRTC that plane is attached to (atomic)
-,,“zpos”,RANGE,"Min=0, Max=UINT_MAX","Plane,Z-order of the plane.Planes with higher Z-order values are displayed on top, planes with identical Z-order values are display in an undefined order"
 ,DVI-I,“subconnector”,ENUM,"{ “Unknown”, “DVI-D”, “DVI-A” }",Connector,TBD
 ,,“select subconnector”,ENUM,"{ “Automatic”, “DVI-D”, “DVI-A” }",Connector,TBD
 ,TV,“subconnector”,ENUM,"{ ""Unknown"", ""Composite"", ""SVIDEO"", ""Component"", ""SCART"" }",Connector,TBD
@@ -36,12 +23,6 @@ DRM,Generic,“rotation”,BITMASK,"{ 0, ""rotate-0"" }, { 1, ""rotate-90"" }, {
 ,Virtual GPU,“suggested X”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an X offset for a connector
 ,,“suggested Y”,RANGE,"Min=0, Max=0xffffffff",Connector,property to suggest an Y offset for a connector
 ,Optional,"""aspect ratio""",ENUM,"{ ""None"", ""4:3"", ""16:9"" }",Connector,TDB
-,,“dirty”,ENUM | IMMUTABLE,"{ ""Off"", ""On"", ""Annotate"" }",Connector,TBD
-,,“DEGAMMA_LUT”,BLOB,0,CRTC,DRM property to set the degamma lookup table (LUT) mapping pixel data from the framebuffer before it is given to the transformation matrix. The data is an interpreted as an array of struct drm_color_lut elements. Hardware might choose not to use the full precision of the LUT elements nor use all the elements of the LUT (for example the hardware might choose to interpolate between LUT[0] and LUT[4]).
-,,“DEGAMMA_LUT_SIZE”,RANGE | IMMUTABLE,"Min=0, Max=UINT_MAX",CRTC,DRM property to gives the size of the lookup table to be set on the DEGAMMA_LUT property (the size depends on the underlying hardware).
-,,“CTM”,BLOB,0,CRTC,DRM property to set the current transformation matrix (CTM) apply to pixel data after the lookup through the degamma LUT and before the lookup through the gamma LUT. The data is an interpreted as a struct drm_color_ctm.
-,,“GAMMA_LUT”,BLOB,0,CRTC,DRM property to set the gamma lookup table (LUT) mapping pixel data after to the transformation matrix to data sent to the connector. The data is an interpreted as an array of struct drm_color_lut elements. Hardware might choose not to use the full precision of the LUT elements nor use all the elements of the LUT (for example the hardware might choose to interpolate between LUT[0] and LUT[4]).
-,,“GAMMA_LUT_SIZE”,RANGE | IMMUTABLE,"Min=0, Max=UINT_MAX",CRTC,DRM property to gives the size of the lookup table to be set on the GAMMA_LUT property (the size depends on the underlying hardware).
 i915,Generic,"""Broadcast RGB""",ENUM,"{ ""Automatic"", ""Full"", ""Limited 16:235"" }",Connector,"When this property is set to Limited 16:235 and CTM is set, the hardware will be programmed with the result of the multiplication of CTM by the limited range matrix to ensure the pixels normaly in the range 0..1.0 are remapped to the range 16/255..235/255."
 ,,“audio”,ENUM,"{ ""force-dvi"", ""off"", ""auto"", ""on"" }",Connector,TBD
 ,SDVO-TV,“mode”,ENUM,"{ ""NTSC_M"", ""NTSC_J"", ""NTSC_443"", ""PAL_B"" } etc.",Connector,TBD
@@ -95,7 +76,6 @@ armada,CRTC,"""CSC_YUV""",ENUM,"{ ""Auto"" , ""CCIR601"", ""CCIR709"" }",CRTC,TB
 ,,"""contrast""",RANGE,"Min=0, Max=0x7fff",Plane,TBD
 ,,"""saturation""",RANGE,"Min=0, Max=0x7fff",Plane,TBD
 exynos,CRTC,“mode”,ENUM,"{ ""normal"", ""blank"" }",CRTC,TBD
-,Overlay,“zpos”,RANGE,"Min=0, Max=MAX_PLANE-1",Plane,TBD
 i2c/ch7006_drv,Generic,“scale”,RANGE,"Min=0, Max=2",Connector,TBD
 ,TV,“mode”,ENUM,"{ ""PAL"", ""PAL-M"",""PAL-N""}, ”PAL-Nc"" , ""PAL-60"", ""NTSC-M"", ""NTSC-J"" }",Connector,TBD
 nouveau,NV10 Overlay,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
@@ -126,4 +106,3 @@ radeon,DVI-I,“coherent”,RANGE,"Min=0, Max=1",Connector,TBD
 ,FMT Dithering,“dither”,ENUM,"{ ""off"", ""on"" }",Connector,TBD
 rcar-du,Generic,"""alpha""",RANGE,"Min=0, Max=255",Plane,TBD
 ,,"""colorkey""",RANGE,"Min=0, Max=0x01ffffff",Plane,TBD
-,,"""zpos""",RANGE,"Min=1, Max=7",Plane,TBD
diff --git a/Documentation/gpu/vgaarbiter.rst b/Documentation/gpu/vgaarbiter.rst
new file mode 100644 (file)
index 0000000..0b41b05
--- /dev/null
@@ -0,0 +1,191 @@
+===========
+VGA Arbiter
+===========
+
+Graphic devices are accessed through ranges in I/O or memory space. While most
+modern devices allow relocation of such ranges, some "Legacy" VGA devices
+implemented on PCI will typically have the same "hard-decoded" addresses as
+they did on ISA. For more details see "PCI Bus Binding to IEEE Std 1275-1994
+Standard for Boot (Initialization Configuration) Firmware Revision 2.1"
+Section 7, Legacy Devices.
+
+The Resource Access Control (RAC) module inside the X server [0] existed for
+the legacy VGA arbitration task (besides other bus management tasks) when more
+than one legacy device co-exists on the same machine. But the problem happens
+when these devices are trying to be accessed by different userspace clients
+(e.g. two server in parallel). Their address assignments conflict. Moreover,
+ideally, being a userspace application, it is not the role of the X server to
+control bus resources. Therefore an arbitration scheme outside of the X server
+is needed to control the sharing of these resources. This document introduces
+the operation of the VGA arbiter implemented for the Linux kernel.
+
+vgaarb kernel/userspace ABI
+---------------------------
+
+The vgaarb is a module of the Linux Kernel. When it is initially loaded, it
+scans all PCI devices and adds the VGA ones inside the arbitration. The
+arbiter then enables/disables the decoding on different devices of the VGA
+legacy instructions. Devices which do not want/need to use the arbiter may
+explicitly tell it by calling vga_set_legacy_decoding().
+
+The kernel exports a char device interface (/dev/vga_arbiter) to the clients,
+which has the following semantics:
+
+open
+        Opens a user instance of the arbiter. By default, it's attached to the
+        default VGA device of the system.
+
+close
+        Close a user instance. Release locks made by the user
+
+read
+        Return a string indicating the status of the target like:
+
+        "<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
+
+        An IO state string is of the form {io,mem,io+mem,none}, mc and
+        ic are respectively mem and io lock counts (for debugging/
+        diagnostic only). "decodes" indicate what the card currently
+        decodes, "owns" indicates what is currently enabled on it, and
+        "locks" indicates what is locked by this card. If the card is
+        unplugged, we get "invalid" then for card_ID and an -ENODEV
+        error is returned for any command until a new card is targeted.
+
+
+write
+        Write a command to the arbiter. List of commands:
+
+        target <card_ID>
+                switch target to card <card_ID> (see below)
+        lock <io_state>
+                acquires locks on target ("none" is an invalid io_state)
+        trylock <io_state>
+                non-blocking acquire locks on target (returns EBUSY if
+                unsuccessful)
+        unlock <io_state>
+                release locks on target
+        unlock all
+                release all locks on target held by this user (not implemented
+                yet)
+        decodes <io_state>
+                set the legacy decoding attributes for the card
+
+        poll
+                event if something changes on any card (not just the target)
+
+        card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
+        to go back to the system default card (TODO: not implemented yet). Currently,
+        only PCI is supported as a prefix, but the userland API may support other bus
+        types in the future, even if the current kernel implementation doesn't.
+
+Note about locks:
+
+The driver keeps track of which user has which locks on which card. It
+supports stacking, like the kernel one. This complexifies the implementation
+a bit, but makes the arbiter more tolerant to user space problems and able
+to properly cleanup in all cases when a process dies.
+Currently, a max of 16 cards can have locks simultaneously issued from
+user space for a given user (file descriptor instance) of the arbiter.
+
+In the case of devices hot-{un,}plugged, there is a hook - pci_notify() - to
+notify them being added/removed in the system and automatically added/removed
+in the arbiter.
+
+There is also an in-kernel API of the arbiter in case DRM, vgacon, or other
+drivers want to use it.
+
+In-kernel interface
+-------------------
+
+.. kernel-doc:: include/linux/vgaarb.h
+   :internal:
+
+.. kernel-doc:: drivers/gpu/vga/vgaarb.c
+   :export:
+
+libpciaccess
+------------
+
+To use the vga arbiter char device it was implemented an API inside the
+libpciaccess library. One field was added to struct pci_device (each device
+on the system)::
+
+    /* the type of resource decoded by the device */
+    int vgaarb_rsrc;
+
+Besides it, in pci_system were added::
+
+    int vgaarb_fd;
+    int vga_count;
+    struct pci_device *vga_target;
+    struct pci_device *vga_default_dev;
+
+The vga_count is used to track how many cards are being arbitrated, so for
+instance, if there is only one card, then it can completely escape arbitration.
+
+These functions below acquire VGA resources for the given card and mark those
+resources as locked. If the resources requested are "normal" (and not legacy)
+resources, the arbiter will first check whether the card is doing legacy
+decoding for that type of resource. If yes, the lock is "converted" into a
+legacy resource lock. The arbiter will first look for all VGA cards that
+might conflict and disable their IOs and/or Memory access, including VGA
+forwarding on P2P bridges if necessary, so that the requested resources can
+be used. Then, the card is marked as locking these resources and the IO and/or
+Memory access is enabled on the card (including VGA forwarding on parent
+P2P bridges if any). In the case of vga_arb_lock(), the function will block
+if some conflicting card is already locking one of the required resources (or
+any resource on a different bus segment, since P2P bridges don't differentiate
+VGA memory and IO afaik). If the card already owns the resources, the function
+succeeds.  vga_arb_trylock() will return (-EBUSY) instead of blocking. Nested
+calls are supported (a per-resource counter is maintained).
+
+Set the target device of this client. ::
+
+    int  pci_device_vgaarb_set_target   (struct pci_device *dev);
+
+For instance, in x86 if two devices on the same bus want to lock different
+resources, both will succeed (lock). If devices are in different buses and
+trying to lock different resources, only the first who tried succeeds. ::
+
+    int  pci_device_vgaarb_lock         (void);
+    int  pci_device_vgaarb_trylock      (void);
+
+Unlock resources of device. ::
+
+    int  pci_device_vgaarb_unlock       (void);
+
+Indicates to the arbiter if the card decodes legacy VGA IOs, legacy VGA
+Memory, both, or none. All cards default to both, the card driver (fbdev for
+example) should tell the arbiter if it has disabled legacy decoding, so the
+card can be left out of the arbitration process (and can be safe to take
+interrupts at any time. ::
+
+    int  pci_device_vgaarb_decodes      (int new_vgaarb_rsrc);
+
+Connects to the arbiter device, allocates the struct ::
+
+    int  pci_device_vgaarb_init         (void);
+
+Close the connection ::
+
+    void pci_device_vgaarb_fini         (void);
+
+xf86VGAArbiter (X server implementation)
+----------------------------------------
+
+X server basically wraps all the functions that touch VGA registers somehow.
+
+References
+----------
+
+Benjamin Herrenschmidt (IBM?) started this work when he discussed such design
+with the Xorg community in 2005 [1, 2]. In the end of 2007, Paulo Zanoni and
+Tiago Vignatti (both of C3SL/Federal University of Paraná) proceeded his work
+enhancing the kernel code to adapt as a kernel module and also did the
+implementation of the user space side [3]. Now (2009) Tiago Vignatti and Dave
+Airlie finally put this work in shape and queued to Jesse Barnes' PCI tree.
+
+0) http://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
+1) http://lists.freedesktop.org/archives/xorg/2005-March/006663.html
+2) http://lists.freedesktop.org/archives/xorg/2005-March/006745.html
+3) http://lists.freedesktop.org/archives/xorg/2007-October/029507.html
diff --git a/Documentation/ia64/.gitignore b/Documentation/ia64/.gitignore
deleted file mode 100644 (file)
index ab806ed..0000000
+++ /dev/null
@@ -1 +0,0 @@
-aliasing-test
diff --git a/Documentation/ia64/Makefile b/Documentation/ia64/Makefile
deleted file mode 100644 (file)
index d493163..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := aliasing-test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
diff --git a/Documentation/ia64/aliasing-test.c b/Documentation/ia64/aliasing-test.c
deleted file mode 100644 (file)
index 62a190d..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Exercise /dev/mem mmap cases that have been troublesome in the past
- *
- * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
- *     Bjorn Helgaas <bjorn.helgaas@hp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <linux/pci.h>
-
-int sum;
-
-static int map_mem(char *path, off_t offset, size_t length, int touch)
-{
-       int fd, rc;
-       void *addr;
-       int *c;
-
-       fd = open(path, O_RDWR);
-       if (fd == -1) {
-               perror(path);
-               return -1;
-       }
-
-       if (fnmatch("/proc/bus/pci/*", path, 0) == 0) {
-               rc = ioctl(fd, PCIIOC_MMAP_IS_MEM);
-               if (rc == -1)
-                       perror("PCIIOC_MMAP_IS_MEM ioctl");
-       }
-
-       addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
-       if (addr == MAP_FAILED)
-               return 1;
-
-       if (touch) {
-               c = (int *) addr;
-               while (c < (int *) (addr + length))
-                       sum += *c++;
-       }
-
-       rc = munmap(addr, length);
-       if (rc == -1) {
-               perror("munmap");
-               return -1;
-       }
-
-       close(fd);
-       return 0;
-}
-
-static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch)
-{
-       struct dirent **namelist;
-       char *name, *path2;
-       int i, n, r, rc = 0, result = 0;
-       struct stat buf;
-
-       n = scandir(path, &namelist, 0, alphasort);
-       if (n < 0) {
-               perror("scandir");
-               return -1;
-       }
-
-       for (i = 0; i < n; i++) {
-               name = namelist[i]->d_name;
-
-               if (fnmatch(".", name, 0) == 0)
-                       goto skip;
-               if (fnmatch("..", name, 0) == 0)
-                       goto skip;
-
-               path2 = malloc(strlen(path) + strlen(name) + 3);
-               strcpy(path2, path);
-               strcat(path2, "/");
-               strcat(path2, name);
-
-               if (fnmatch(file, name, 0) == 0) {
-                       rc = map_mem(path2, offset, length, touch);
-                       if (rc == 0)
-                               fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable");
-                       else if (rc > 0)
-                               fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length);
-                       else {
-                               fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length);
-                               return rc;
-                       }
-               } else {
-                       r = lstat(path2, &buf);
-                       if (r == 0 && S_ISDIR(buf.st_mode)) {
-                               rc = scan_tree(path2, file, offset, length, touch);
-                               if (rc < 0)
-                                       return rc;
-                       }
-               }
-
-               result |= rc;
-               free(path2);
-
-skip:
-               free(namelist[i]);
-       }
-       free(namelist);
-       return result;
-}
-
-char buf[1024];
-
-static int read_rom(char *path)
-{
-       int fd, rc;
-       size_t size = 0;
-
-       fd = open(path, O_RDWR);
-       if (fd == -1) {
-               perror(path);
-               return -1;
-       }
-
-       rc = write(fd, "1", 2);
-       if (rc <= 0) {
-               close(fd);
-               perror("write");
-               return -1;
-       }
-
-       do {
-               rc = read(fd, buf, sizeof(buf));
-               if (rc > 0)
-                       size += rc;
-       } while (rc > 0);
-
-       close(fd);
-       return size;
-}
-
-static int scan_rom(char *path, char *file)
-{
-       struct dirent **namelist;
-       char *name, *path2;
-       int i, n, r, rc = 0, result = 0;
-       struct stat buf;
-
-       n = scandir(path, &namelist, 0, alphasort);
-       if (n < 0) {
-               perror("scandir");
-               return -1;
-       }
-
-       for (i = 0; i < n; i++) {
-               name = namelist[i]->d_name;
-
-               if (fnmatch(".", name, 0) == 0)
-                       goto skip;
-               if (fnmatch("..", name, 0) == 0)
-                       goto skip;
-
-               path2 = malloc(strlen(path) + strlen(name) + 3);
-               strcpy(path2, path);
-               strcat(path2, "/");
-               strcat(path2, name);
-
-               if (fnmatch(file, name, 0) == 0) {
-                       rc = read_rom(path2);
-
-                       /*
-                        * It's OK if the ROM is unreadable.  Maybe there
-                        * is no ROM, or some other error occurred.  The
-                        * important thing is that no MCA happened.
-                        */
-                       if (rc > 0)
-                               fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc);
-                       else {
-                               fprintf(stderr, "PASS: %s not readable\n", path2);
-                               return rc;
-                       }
-               } else {
-                       r = lstat(path2, &buf);
-                       if (r == 0 && S_ISDIR(buf.st_mode)) {
-                               rc = scan_rom(path2, file);
-                               if (rc < 0)
-                                       return rc;
-                       }
-               }
-
-               result |= rc;
-               free(path2);
-
-skip:
-               free(namelist[i]);
-       }
-       free(namelist);
-       return result;
-}
-
-int main(void)
-{
-       int rc;
-
-       if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0)
-               fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n");
-       else
-               fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n");
-
-       /*
-        * It's not safe to blindly read the VGA frame buffer.  If you know
-        * how to poke the card the right way, it should respond, but it's
-        * not safe in general.  Many machines, e.g., Intel chipsets, cover
-        * up a non-responding card by just returning -1, but others will
-        * report the failure as a machine check.
-        */
-       if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0)
-               fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n");
-       else
-               fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n");
-
-       if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0)
-               fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n");
-       else
-               fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n");
-
-       /*
-        * Often you can map all the individual pieces above (0-0xA0000,
-        * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
-        * thing at once.  This is because the individual pieces use different
-        * attributes, and there's no single attribute supported over the
-        * whole region.
-        */
-       rc = map_mem("/dev/mem", 0, 1024*1024, 0);
-       if (rc == 0)
-               fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n");
-       else if (rc > 0)
-               fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n");
-       else
-               fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n");
-
-       scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1);
-       scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0);
-       scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1);
-       scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0);
-
-       scan_rom("/sys/devices", "rom");
-
-       scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1);
-       scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0);
-       scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1);
-       scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0);
-
-       return rc;
-}
index d9ccb94..c53d089 100644 (file)
@@ -17,6 +17,7 @@ Contents:
    driver-api/index
    media/index
    gpu/index
+   80211/index
 
 Indices and tables
 ==================
index 1fec113..8d1341c 100644 (file)
@@ -319,3 +319,60 @@ For touchpad packet, the format is:
                otherwise byte 0 bit 4 must be set and byte 0/4/5 are
                in NEW fmt
  F:         Number of fingers - 3, 0 means 3 fingers, 1 means 4 ...
+
+
+ALPS Absolute Mode - Protocol Version 8
+---------------------------------------
+
+Spoken by SS4 (73 03 14) and SS5 (73 03 28) hardware.
+
+The packet type is given by the APD field, bits 4-5 of byte 3.
+
+Touchpad packet (APD = 0x2):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1    0    0   X7
+ byte 1:    0   X6   X5   X4   X3   X2   X1   X0
+ byte 2:    0   Y6   Y5   Y4   Y3   Y2   Y1   Y0
+ byte 3:    0  T&P    1    0    1    0    0   Y7
+ byte 4:    0   Z6   Z5   Z4   Z3   Z2   Z1   Z0
+ byte 5:    0    0    0    0    0    0    0    0
+
+SWM, SWR, SWL: Middle, Right, and Left button states
+
+Touchpad 1 Finger packet (APD = 0x0):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1   X2   X1   X0
+ byte 1:   X9   X8   X7    1   X6   X5   X4   X3
+ byte 2:    0  X11  X10  LFB   Y3   Y2   Y1   Y0
+ byte 3:   Y5   Y4    0    0    1 TAPF2 TAPF1 TAPF0
+ byte 4:  Zv7  Y11  Y10    1   Y9   Y8   Y7   Y6
+ byte 5:  Zv6  Zv5  Zv4    0  Zv3  Zv2  Zv1  Zv0
+
+TAPF: ???
+LFB:  ???
+
+Touchpad 2 Finger packet (APD = 0x1):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1  AX6  AX5  AX4
+ byte 1: AX11 AX10  AX9  AX8  AX7  AZ1  AY4  AZ0
+ byte 2: AY11 AY10  AY9  CONT AY8  AY7  AY6  AY5
+ byte 3:    0    0    0    1    1  BX6  BX5  BX4
+ byte 4: BX11 BX10  BX9  BX8  BX7  BZ1  BY4  BZ0
+ byte 5: BY11 BY10  BY9    0  BY8  BY7  BY5  BY5
+
+CONT: A 3-or-4 Finger packet is to follow
+
+Touchpad 3-or-4 Finger packet (APD = 0x3):
+
+           b7   b6   b5   b4   b3   b2   b1   b0
+ byte 0:  SWM  SWR  SWL    1    1  AX6  AX5  AX4
+ byte 1: AX11 AX10  AX9  AX8  AX7  AZ1  AY4  AZ0
+ byte 2: AY11 AY10  AY9  OVF  AY8  AY7  AY6  AY5
+ byte 3:    0    0    1    1    1  BX6  BX5  BX4
+ byte 4: BX11 BX10  BX9  BX8  BX7  BZ1  BY4  BZ0
+ byte 5: BY11 BY10  BY9    0  BY8  BY7  BY5  BY5
+
+OVF: 5th finger detected
index 385a5ef..9b9c479 100644 (file)
@@ -41,6 +41,7 @@ This document describes the Linux kernel Makefiles.
           --- 6.8 Custom kbuild commands
           --- 6.9 Preprocessing linker scripts
           --- 6.10 Generic header files
+          --- 6.11 Post-link pass
 
        === 7 Kbuild syntax for exported headers
                --- 7.1 header-y
@@ -1237,6 +1238,21 @@ When kbuild executes, the following steps are followed (roughly):
        to list the file in the Kbuild file.
        See "7.4 generic-y" for further info on syntax etc.
 
+--- 6.11 Post-link pass
+
+       If the file arch/xxx/Makefile.postlink exists, this makefile
+       will be invoked for post-link objects (vmlinux and modules.ko)
+       for architectures to run post-link passes on. Must also handle
+       the clean target.
+
+       This pass runs after kallsyms generation. If the architecture
+       needs to modify symbol locations, rather than manipulate the
+       kallsyms, it may be easier to add another postlink target for
+       .tmp_vmlinux? targets to be called from link-vmlinux.sh.
+
+       For example, powerpc uses this to check relocation sanity of
+       the linked vmlinux file.
+
 === 7 Kbuild syntax for exported headers
 
 The kernel includes a set of headers that is exported to userspace.
index 705fb91..37babf9 100644 (file)
@@ -33,6 +33,37 @@ can also be entered as
 Double-quotes can be used to protect spaces in values, e.g.:
        param="spaces in here"
 
+cpu lists:
+----------
+
+Some kernel parameters take a list of CPUs as a value, e.g.  isolcpus,
+nohz_full, irqaffinity, rcu_nocbs.  The format of this list is:
+
+       <cpu number>,...,<cpu number>
+
+or
+
+       <cpu number>-<cpu number>
+       (must be a positive range in ascending order)
+
+or a mixture
+
+<cpu number>,...,<cpu number>-<cpu number>
+
+Note that for the special case of a range one can split the range into equal
+sized groups and for each group use some amount from the beginning of that
+group:
+
+       <cpu number>-cpu number>:<used size>/<group size>
+
+For example one can add to the command line following parameter:
+
+       isolcpus=1,2,10-20,100-2000:2/25
+
+where the final item represents CPUs 100,101,125,126,150,151,...
+
+
+
 This document may not be entirely up to date and comprehensive. The command
 "modinfo -p ${modulename}" shows a current list of all parameters of a loadable
 module. Loadable modules, after being loaded into the running kernel, also
@@ -1480,7 +1511,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        i8042.nopnp     [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
                             controllers
        i8042.notimeout [HW] Ignore timeout condition signalled by controller
-       i8042.reset     [HW] Reset the controller during init and cleanup
+       i8042.reset     [HW] Reset the controller during init, cleanup and
+                            suspend-to-ram transitions, only during s2r
+                            transitions, or never reset
+                       Format: { 1 | Y | y | 0 | N | n }
+                       1, Y, y: always reset controller
+                       0, N, n: don't ever reset controller
+                       Default: only on s2r transitions on x86; most other
+                       architectures force reset to be always executed
        i8042.unlock    [HW] Unlock (ignore) the keylock
        i8042.kbdreset  [HW] Reset device connected to KBD port
 
@@ -1789,13 +1827,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        See Documentation/filesystems/nfs/nfsroot.txt.
 
        irqaffinity=    [SMP] Set the default irq affinity mask
-                       Format:
-                       <cpu number>,...,<cpu number>
-                       or
-                       <cpu number>-<cpu number>
-                       (must be a positive range in ascending order)
-                       or a mixture
-                       <cpu number>,...,<cpu number>-<cpu number>
+                       The argument is a cpu list, as described above.
 
        irqfixup        [HW]
                        When an interrupt is not handled search all handlers
@@ -1812,13 +1844,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Format: <RDP>,<reset>,<pci_scan>,<verbosity>
 
        isolcpus=       [KNL,SMP] Isolate CPUs from the general scheduler.
-                       Format:
-                       <cpu number>,...,<cpu number>
-                       or
-                       <cpu number>-<cpu number>
-                       (must be a positive range in ascending order)
-                       or a mixture
-                       <cpu number>,...,<cpu number>-<cpu number>
+                       The argument is a cpu list, as described above.
 
                        This option can be used to specify one or more CPUs
                        to isolate from the general SMP balancing and scheduling
@@ -2451,6 +2477,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        nfsrootdebug    [NFS] enable nfsroot debugging messages.
                        See Documentation/filesystems/nfs/nfsroot.txt.
 
+       nfs.callback_nr_threads=
+                       [NFSv4] set the total number of threads that the
+                       NFS client will assign to service NFSv4 callback
+                       requests.
+
        nfs.callback_tcpport=
                        [NFS] set the TCP port on which the NFSv4 callback
                        channel should listen.
@@ -2474,6 +2505,13 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        of returning the full 64-bit number.
                        The default is to return 64-bit inode numbers.
 
+       nfs.max_session_cb_slots=
+                       [NFSv4.1] Sets the maximum number of session
+                       slots the client will assign to the callback
+                       channel. This determines the maximum number of
+                       callbacks the client will process in parallel for
+                       a particular server.
+
        nfs.max_session_slots=
                        [NFSv4.1] Sets the maximum number of session slots
                        the client will attempt to negotiate with the server.
@@ -2680,6 +2718,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        Default: on
 
        nohz_full=      [KNL,BOOT]
+                       The argument is a cpu list, as described above.
                        In kernels built with CONFIG_NO_HZ_FULL=y, set
                        the specified list of CPUs whose tick will be stopped
                        whenever possible. The boot CPU will be forced outside
@@ -3285,6 +3324,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        See Documentation/blockdev/ramdisk.txt.
 
        rcu_nocbs=      [KNL]
+                       The argument is a cpu list, as described above.
+
                        In kernels built with CONFIG_RCU_NOCB_CPU=y, set
                        the specified list of CPUs to be no-callback CPUs.
                        Invocation of these CPUs' RCU callbacks will
index 979eaca..54bee77 100644 (file)
@@ -1,8 +1,9 @@
 Linux Kernel Selftests
 
 The kernel contains a set of "self tests" under the tools/testing/selftests/
-directory. These are intended to be small unit tests to exercise individual
-code paths in the kernel.
+directory. These are intended to be small tests to exercise individual code
+paths in the kernel. Tests are intended to be run after building, installing
+and booting a kernel.
 
 On some systems, hot-plug tests could hang forever waiting for cpu and
 memory to be ready to be offlined. A special hot-plug target is created
diff --git a/Documentation/laptops/.gitignore b/Documentation/laptops/.gitignore
deleted file mode 100644 (file)
index 9fc984e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-dslm
index 7c0ac2a..86169dc 100644 (file)
@@ -1,13 +1,9 @@
 00-INDEX
        - This file
-Makefile
-       - Makefile for building dslm example program.
 asus-laptop.txt
        - information on the Asus Laptop Extras driver.
 disk-shock-protection.txt
        - information on hard disk shock protection.
-dslm.c
-       - Simple Disk Sleep Monitor program
 laptop-mode.txt
        - how to conserve battery power using laptop-mode.
 sony-laptop.txt
diff --git a/Documentation/laptops/Makefile b/Documentation/laptops/Makefile
deleted file mode 100644 (file)
index 0abe44f..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := dslm
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
diff --git a/Documentation/laptops/dslm.c b/Documentation/laptops/dslm.c
deleted file mode 100644 (file)
index d5dd2d4..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * dslm.c
- * Simple Disk Sleep Monitor
- *  by Bartek Kania
- * Licensed under the GPL
- */
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <time.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <linux/hdreg.h>
-
-#ifdef DEBUG
-#define D(x) x
-#else
-#define D(x)
-#endif
-
-int endit = 0;
-
-/* Check if the disk is in powersave-mode
- * Most of the code is stolen from hdparm.
- * 1 = active, 0 = standby/sleep, -1 = unknown */
-static int check_powermode(int fd)
-{
-    unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
-    int state;
-
-    if (ioctl(fd, HDIO_DRIVE_CMD, &args)
-       && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
-       && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
-       if (errno != EIO || args[0] != 0 || args[1] != 0) {
-           state = -1; /* "unknown"; */
-       } else
-           state = 0; /* "sleeping"; */
-    } else {
-       state = (args[2] == 255) ? 1 : 0;
-    }
-    D(printf(" drive state is:  %d\n", state));
-
-    return state;
-}
-
-static char *state_name(int i)
-{
-    if (i == -1) return "unknown";
-    if (i == 0) return "sleeping";
-    if (i == 1) return "active";
-
-    return "internal error";
-}
-
-static char *myctime(time_t time)
-{
-    char *ts = ctime(&time);
-    ts[strlen(ts) - 1] = 0;
-
-    return ts;
-}
-
-static void measure(int fd)
-{
-    time_t start_time;
-    int last_state;
-    time_t last_time;
-    int curr_state;
-    time_t curr_time = 0;
-    time_t time_diff;
-    time_t active_time = 0;
-    time_t sleep_time = 0;
-    time_t unknown_time = 0;
-    time_t total_time = 0;
-    int changes = 0;
-    float tmp;
-
-    printf("Starting measurements\n");
-
-    last_state = check_powermode(fd);
-    start_time = last_time = time(0);
-    printf("  System is in state %s\n\n", state_name(last_state));
-
-    while(!endit) {
-       sleep(1);
-       curr_state = check_powermode(fd);
-
-       if (curr_state != last_state || endit) {
-           changes++;
-           curr_time = time(0);
-           time_diff = curr_time - last_time;
-
-           if (last_state == 1) active_time += time_diff;
-           else if (last_state == 0) sleep_time += time_diff;
-           else unknown_time += time_diff;
-
-           last_state = curr_state;
-           last_time = curr_time;
-
-           printf("%s: State-change to %s\n", myctime(curr_time),
-                  state_name(curr_state));
-       }
-    }
-    changes--; /* Compensate for SIGINT */
-
-    total_time = time(0) - start_time;
-    printf("\nTotal running time:  %lus\n", curr_time - start_time);
-    printf(" State changed %d times\n", changes);
-
-    tmp = (float)sleep_time / (float)total_time * 100;
-    printf(" Time in sleep state:   %lus (%.2f%%)\n", sleep_time, tmp);
-    tmp = (float)active_time / (float)total_time * 100;
-    printf(" Time in active state:  %lus (%.2f%%)\n", active_time, tmp);
-    tmp = (float)unknown_time / (float)total_time * 100;
-    printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
-}
-
-static void ender(int s)
-{
-    endit = 1;
-}
-
-static void usage(void)
-{
-    puts("usage: dslm [-w <time>] <disk>");
-    exit(0);
-}
-
-int main(int argc, char **argv)
-{
-    int fd;
-    char *disk = 0;
-    int settle_time = 60;
-
-    /* Parse the simple command-line */
-    if (argc == 2)
-       disk = argv[1];
-    else if (argc == 4) {
-       settle_time = atoi(argv[2]);
-       disk = argv[3];
-    } else
-       usage();
-
-    if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
-       printf("Can't open %s, because: %s\n", disk, strerror(errno));
-       exit(-1);
-    }
-
-    if (settle_time) {
-       printf("Waiting %d seconds for the system to settle down to "
-              "'normal'\n", settle_time);
-       sleep(settle_time);
-    } else
-       puts("Not waiting for system to settle down");
-
-    signal(SIGINT, ender);
-
-    measure(fd);
-
-    close(fd);
-
-    return 0;
-}
index 4ebbfc3..19276f5 100644 (file)
@@ -779,4 +779,4 @@ Monitoring tool
 ---------------
 
 Bartek Kania submitted this, it can be used to measure how much time your disk
-spends spun up/down.  See Documentation/laptops/dslm.c
+spends spun up/down.  See tools/laptop/dslm/dslm.c
diff --git a/Documentation/mic/Makefile b/Documentation/mic/Makefile
deleted file mode 100644 (file)
index a191d45..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mpssd
diff --git a/Documentation/mic/mpssd/.gitignore b/Documentation/mic/mpssd/.gitignore
deleted file mode 100644 (file)
index 8b7c72f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-mpssd
diff --git a/Documentation/mic/mpssd/Makefile b/Documentation/mic/mpssd/Makefile
deleted file mode 100644 (file)
index 06871b0..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-ifndef CROSS_COMPILE
-# List of programs to build
-hostprogs-$(CONFIG_X86_64) := mpssd
-
-mpssd-objs := mpssd.o sysfs.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS += -I$(objtree)/usr/include -I$(srctree)/tools/include
-
-ifdef DEBUG
-HOSTCFLAGS += -DDEBUG=$(DEBUG)
-endif
-
-HOSTLOADLIBES_mpssd := -lpthread
-
-install:
-       install mpssd /usr/sbin/mpssd
-       install micctrl /usr/sbin/micctrl
-endif
diff --git a/Documentation/mic/mpssd/micctrl b/Documentation/mic/mpssd/micctrl
deleted file mode 100755 (executable)
index 8f2629b..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/bin/bash
-# Intel MIC Platform Software Stack (MPSS)
-#
-# Copyright(c) 2013 Intel Corporation.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License, version 2, as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# The full GNU General Public License is included in this distribution in
-# the file called "COPYING".
-#
-# Intel MIC User Space Tools.
-#
-# micctrl - Controls MIC boot/start/stop.
-#
-# chkconfig: 2345 95 05
-# description: start MPSS stack processing.
-#
-### BEGIN INIT INFO
-# Provides: micctrl
-### END INIT INFO
-
-# Source function library.
-. /etc/init.d/functions
-
-sysfs="/sys/class/mic"
-
-_status()
-{
-       f=$sysfs/$1
-       echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`"
-}
-
-status()
-{
-       if [ "`echo $1 | head -c3`" == "mic" ]; then
-               _status $1
-               return $?
-       fi
-       for f in $sysfs/*
-       do
-               _status `basename $f`
-               RETVAL=$?
-               [ $RETVAL -ne 0 ] && return $RETVAL
-       done
-       return 0
-}
-
-_reset()
-{
-       f=$sysfs/$1
-       echo reset > $f/state
-}
-
-reset()
-{
-       if [ "`echo $1 | head -c3`" == "mic" ]; then
-               _reset $1
-               return $?
-       fi
-       for f in $sysfs/*
-       do
-               _reset `basename $f`
-               RETVAL=$?
-               [ $RETVAL -ne 0 ] && return $RETVAL
-       done
-       return 0
-}
-
-_boot()
-{
-       f=$sysfs/$1
-       echo "linux" > $f/bootmode
-       echo "mic/uos.img" > $f/firmware
-       echo "mic/$1.image" > $f/ramdisk
-       echo "boot" > $f/state
-}
-
-boot()
-{
-       if [ "`echo $1 | head -c3`" == "mic" ]; then
-               _boot $1
-               return $?
-       fi
-       for f in $sysfs/*
-       do
-               _boot `basename $f`
-               RETVAL=$?
-               [ $RETVAL -ne 0 ] && return $RETVAL
-       done
-       return 0
-}
-
-_shutdown()
-{
-       f=$sysfs/$1
-       echo shutdown > $f/state
-}
-
-shutdown()
-{
-       if [ "`echo $1 | head -c3`" == "mic" ]; then
-               _shutdown $1
-               return $?
-       fi
-       for f in $sysfs/*
-       do
-               _shutdown `basename $f`
-               RETVAL=$?
-               [ $RETVAL -ne 0 ] && return $RETVAL
-       done
-       return 0
-}
-
-_wait()
-{
-       f=$sysfs/$1
-       while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ]
-       do
-               sleep 1
-               echo -e "Waiting for $1 to go offline"
-       done
-}
-
-wait()
-{
-       if [ "`echo $1 | head -c3`" == "mic" ]; then
-               _wait $1
-               return $?
-       fi
-       # Wait for the cards to go offline
-       for f in $sysfs/*
-       do
-               _wait `basename $f`
-               RETVAL=$?
-               [ $RETVAL -ne 0 ] && return $RETVAL
-       done
-       return 0
-}
-
-if [ ! -d "$sysfs" ]; then
-       echo -e $"Module unloaded "
-       exit 3
-fi
-
-case $1 in
-       -s)
-               status $2
-               ;;
-       -r)
-               reset $2
-               ;;
-       -b)
-               boot $2
-               ;;
-       -S)
-               shutdown $2
-               ;;
-       -w)
-               wait $2
-               ;;
-       *)
-               echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}"
-               exit 2
-esac
-
-exit $?
diff --git a/Documentation/mic/mpssd/mpss b/Documentation/mic/mpssd/mpss
deleted file mode 100755 (executable)
index 5fcf9fa..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/bin/bash
-# Intel MIC Platform Software Stack (MPSS)
-#
-# Copyright(c) 2013 Intel Corporation.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License, version 2, as
-# published by the Free Software Foundation.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# The full GNU General Public License is included in this distribution in
-# the file called "COPYING".
-#
-# Intel MIC User Space Tools.
-#
-# mpss Start mpssd.
-#
-# chkconfig: 2345 95 05
-# description: start MPSS stack processing.
-#
-### BEGIN INIT INFO
-# Provides: mpss
-# Required-Start:
-# Required-Stop:
-# Short-Description: MPSS stack control
-# Description: MPSS stack control
-### END INIT INFO
-
-# Source function library.
-. /etc/init.d/functions
-
-exec=/usr/sbin/mpssd
-sysfs="/sys/class/mic"
-mic_modules="mic_host mic_x100_dma scif vop"
-
-start()
-{
-       [ -x $exec ] || exit 5
-
-       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then
-               echo -e $"MPSSD already running! "
-               success
-               echo
-               return 0
-       fi
-
-       echo -e $"Starting MPSS Stack"
-       echo -e $"Loading MIC drivers:" $mic_modules
-
-       modprobe -a $mic_modules
-       RETVAL=$?
-       if [ $RETVAL -ne 0 ]; then
-               failure
-               echo
-               return $RETVAL
-       fi
-
-       # Start the daemon
-       echo -n $"Starting MPSSD "
-       $exec
-       RETVAL=$?
-       if [ $RETVAL -ne 0 ]; then
-               failure
-               echo
-               return $RETVAL
-       fi
-       success
-       echo
-
-       sleep 5
-
-       # Boot the cards
-       micctrl -b
-
-       # Wait till ping works
-       for f in $sysfs/*
-       do
-               count=100
-               ipaddr=`cat $f/cmdline`
-               ipaddr=${ipaddr#*address,}
-               ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1`
-               while [ $count -ge 0 ]
-               do
-                       echo -e "Pinging "`basename $f`" "
-                       ping -c 1 $ipaddr &> /dev/null
-                       RETVAL=$?
-                       if [ $RETVAL -eq 0 ]; then
-                               success
-                               break
-                       fi
-                       sleep 1
-                       count=`expr $count - 1`
-               done
-               [ $RETVAL -ne 0 ] && failure || success
-               echo
-       done
-       return $RETVAL
-}
-
-stop()
-{
-       echo -e $"Shutting down MPSS Stack: "
-
-       # Bail out if module is unloaded
-       if [ ! -d "$sysfs" ]; then
-               echo -n $"Module unloaded "
-               success
-               echo
-               return 0
-       fi
-
-       # Shut down the cards.
-       micctrl -S
-
-       # Wait for the cards to go offline
-       for f in $sysfs/*
-       do
-               while [ "`cat $f/state`" != "ready" ]
-               do
-                       sleep 1
-                       echo -e "Waiting for "`basename $f`" to become ready"
-               done
-       done
-
-       # Display the status of the cards
-       micctrl -s
-
-       # Kill MPSSD now
-       echo -n $"Killing MPSSD"
-       killall -9 mpssd 2>/dev/null
-       RETVAL=$?
-       [ $RETVAL -ne 0 ] && failure || success
-       echo
-       return $RETVAL
-}
-
-restart()
-{
-       stop
-       sleep 5
-       start
-}
-
-status()
-{
-       micctrl -s
-       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then
-               echo "mpssd is running"
-       else
-               echo "mpssd is stopped"
-       fi
-       return 0
-}
-
-unload()
-{
-       if [ ! -d "$sysfs" ]; then
-               echo -n $"No MIC_HOST Module: "
-               success
-               echo
-               return
-       fi
-
-       stop
-
-       sleep 5
-       echo -n $"Removing MIC drivers:" $mic_modules
-       modprobe -r $mic_modules
-       RETVAL=$?
-       [ $RETVAL -ne 0 ] && failure || success
-       echo
-       return $RETVAL
-}
-
-case $1 in
-       start)
-               start
-               ;;
-       stop)
-               stop
-               ;;
-       restart)
-               restart
-               ;;
-       status)
-               status
-               ;;
-       unload)
-               unload
-               ;;
-       *)
-               echo $"Usage: $0 {start|stop|restart|status|unload}"
-               exit 2
-esac
-
-exit $?
diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c
deleted file mode 100644 (file)
index 49db1de..0000000
+++ /dev/null
@@ -1,1826 +0,0 @@
-/*
- * Intel MIC Platform Software Stack (MPSS)
- *
- * Copyright(c) 2013 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Intel MIC User Space Tools.
- */
-
-#define _GNU_SOURCE
-
-#include <stdlib.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <assert.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <signal.h>
-#include <poll.h>
-#include <features.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <sys/socket.h>
-#include <linux/virtio_ring.h>
-#include <linux/virtio_net.h>
-#include <linux/virtio_console.h>
-#include <linux/virtio_blk.h>
-#include <linux/version.h>
-#include "mpssd.h"
-#include <linux/mic_ioctl.h>
-#include <linux/mic_common.h>
-#include <tools/endian.h>
-
-static void *init_mic(void *arg);
-
-static FILE *logfp;
-static struct mic_info mic_list;
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
-
-#define min_t(type, x, y) ({                           \
-               type __min1 = (x);                      \
-               type __min2 = (y);                      \
-               __min1 < __min2 ? __min1 : __min2; })
-
-/* align addr on a size boundary - adjust address up/down if needed */
-#define _ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
-#define _ALIGN_UP(addr, size)    _ALIGN_DOWN(addr + size - 1, size)
-
-/* align addr on a size boundary - adjust address up if needed */
-#define _ALIGN(addr, size)     _ALIGN_UP(addr, size)
-
-/* to align the pointer to the (next) page boundary */
-#define PAGE_ALIGN(addr)        _ALIGN(addr, PAGE_SIZE)
-
-#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
-
-#define GSO_ENABLED            1
-#define MAX_GSO_SIZE           (64 * 1024)
-#define ETH_H_LEN              14
-#define MAX_NET_PKT_SIZE       (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64))
-#define MIC_DEVICE_PAGE_END    0x1000
-
-#ifndef VIRTIO_NET_HDR_F_DATA_VALID
-#define VIRTIO_NET_HDR_F_DATA_VALID    2       /* Csum is valid */
-#endif
-
-static struct {
-       struct mic_device_desc dd;
-       struct mic_vqconfig vqconfig[2];
-       __u32 host_features, guest_acknowledgements;
-       struct virtio_console_config cons_config;
-} virtcons_dev_page = {
-       .dd = {
-               .type = VIRTIO_ID_CONSOLE,
-               .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig),
-               .feature_len = sizeof(virtcons_dev_page.host_features),
-               .config_len = sizeof(virtcons_dev_page.cons_config),
-       },
-       .vqconfig[0] = {
-               .num = htole16(MIC_VRING_ENTRIES),
-       },
-       .vqconfig[1] = {
-               .num = htole16(MIC_VRING_ENTRIES),
-       },
-};
-
-static struct {
-       struct mic_device_desc dd;
-       struct mic_vqconfig vqconfig[2];
-       __u32 host_features, guest_acknowledgements;
-       struct virtio_net_config net_config;
-} virtnet_dev_page = {
-       .dd = {
-               .type = VIRTIO_ID_NET,
-               .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig),
-               .feature_len = sizeof(virtnet_dev_page.host_features),
-               .config_len = sizeof(virtnet_dev_page.net_config),
-       },
-       .vqconfig[0] = {
-               .num = htole16(MIC_VRING_ENTRIES),
-       },
-       .vqconfig[1] = {
-               .num = htole16(MIC_VRING_ENTRIES),
-       },
-#if GSO_ENABLED
-       .host_features = htole32(
-               1 << VIRTIO_NET_F_CSUM |
-               1 << VIRTIO_NET_F_GSO |
-               1 << VIRTIO_NET_F_GUEST_TSO4 |
-               1 << VIRTIO_NET_F_GUEST_TSO6 |
-               1 << VIRTIO_NET_F_GUEST_ECN),
-#else
-               .host_features = 0,
-#endif
-};
-
-static const char *mic_config_dir = "/etc/mpss";
-static const char *virtblk_backend = "VIRTBLK_BACKEND";
-static struct {
-       struct mic_device_desc dd;
-       struct mic_vqconfig vqconfig[1];
-       __u32 host_features, guest_acknowledgements;
-       struct virtio_blk_config blk_config;
-} virtblk_dev_page = {
-       .dd = {
-               .type = VIRTIO_ID_BLOCK,
-               .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig),
-               .feature_len = sizeof(virtblk_dev_page.host_features),
-               .config_len = sizeof(virtblk_dev_page.blk_config),
-       },
-       .vqconfig[0] = {
-               .num = htole16(MIC_VRING_ENTRIES),
-       },
-       .host_features =
-               htole32(1<<VIRTIO_BLK_F_SEG_MAX),
-       .blk_config = {
-               .seg_max = htole32(MIC_VRING_ENTRIES - 2),
-               .capacity = htole64(0),
-        }
-};
-
-static char *myname;
-
-static int
-tap_configure(struct mic_info *mic, char *dev)
-{
-       pid_t pid;
-       char *ifargv[7];
-       char ipaddr[IFNAMSIZ];
-       int ret = 0;
-
-       pid = fork();
-       if (pid == 0) {
-               ifargv[0] = "ip";
-               ifargv[1] = "link";
-               ifargv[2] = "set";
-               ifargv[3] = dev;
-               ifargv[4] = "up";
-               ifargv[5] = NULL;
-               mpsslog("Configuring %s\n", dev);
-               ret = execvp("ip", ifargv);
-               if (ret < 0) {
-                       mpsslog("%s execvp failed errno %s\n",
-                               mic->name, strerror(errno));
-                       return ret;
-               }
-       }
-       if (pid < 0) {
-               mpsslog("%s fork failed errno %s\n",
-                       mic->name, strerror(errno));
-               return ret;
-       }
-
-       ret = waitpid(pid, NULL, 0);
-       if (ret < 0) {
-               mpsslog("%s waitpid failed errno %s\n",
-                       mic->name, strerror(errno));
-               return ret;
-       }
-
-       snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1);
-
-       pid = fork();
-       if (pid == 0) {
-               ifargv[0] = "ip";
-               ifargv[1] = "addr";
-               ifargv[2] = "add";
-               ifargv[3] = ipaddr;
-               ifargv[4] = "dev";
-               ifargv[5] = dev;
-               ifargv[6] = NULL;
-               mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr);
-               ret = execvp("ip", ifargv);
-               if (ret < 0) {
-                       mpsslog("%s execvp failed errno %s\n",
-                               mic->name, strerror(errno));
-                       return ret;
-               }
-       }
-       if (pid < 0) {
-               mpsslog("%s fork failed errno %s\n",
-                       mic->name, strerror(errno));
-               return ret;
-       }
-
-       ret = waitpid(pid, NULL, 0);
-       if (ret < 0) {
-               mpsslog("%s waitpid failed errno %s\n",
-                       mic->name, strerror(errno));
-               return ret;
-       }
-       mpsslog("MIC name %s %s %d DONE!\n",
-               mic->name, __func__, __LINE__);
-       return 0;
-}
-
-static int tun_alloc(struct mic_info *mic, char *dev)
-{
-       struct ifreq ifr;
-       int fd, err;
-#if GSO_ENABLED
-       unsigned offload;
-#endif
-       fd = open("/dev/net/tun", O_RDWR);
-       if (fd < 0) {
-               mpsslog("Could not open /dev/net/tun %s\n", strerror(errno));
-               goto done;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-
-       ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
-       if (*dev)
-               strncpy(ifr.ifr_name, dev, IFNAMSIZ);
-
-       err = ioctl(fd, TUNSETIFF, (void *)&ifr);
-       if (err < 0) {
-               mpsslog("%s %s %d TUNSETIFF failed %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               close(fd);
-               return err;
-       }
-#if GSO_ENABLED
-       offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN;
-
-       err = ioctl(fd, TUNSETOFFLOAD, offload);
-       if (err < 0) {
-               mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               close(fd);
-               return err;
-       }
-#endif
-       strcpy(dev, ifr.ifr_name);
-       mpsslog("Created TAP %s\n", dev);
-done:
-       return fd;
-}
-
-#define NET_FD_VIRTIO_NET 0
-#define NET_FD_TUN 1
-#define MAX_NET_FD 2
-
-static void set_dp(struct mic_info *mic, int type, void *dp)
-{
-       switch (type) {
-       case VIRTIO_ID_CONSOLE:
-               mic->mic_console.console_dp = dp;
-               return;
-       case VIRTIO_ID_NET:
-               mic->mic_net.net_dp = dp;
-               return;
-       case VIRTIO_ID_BLOCK:
-               mic->mic_virtblk.block_dp = dp;
-               return;
-       }
-       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
-       assert(0);
-}
-
-static void *get_dp(struct mic_info *mic, int type)
-{
-       switch (type) {
-       case VIRTIO_ID_CONSOLE:
-               return mic->mic_console.console_dp;
-       case VIRTIO_ID_NET:
-               return mic->mic_net.net_dp;
-       case VIRTIO_ID_BLOCK:
-               return mic->mic_virtblk.block_dp;
-       }
-       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
-       assert(0);
-       return NULL;
-}
-
-static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type)
-{
-       struct mic_device_desc *d;
-       int i;
-       void *dp = get_dp(mic, type);
-
-       for (i = sizeof(struct mic_bootparam); i < PAGE_SIZE;
-               i += mic_total_desc_size(d)) {
-               d = dp + i;
-
-               /* End of list */
-               if (d->type == 0)
-                       break;
-
-               if (d->type == -1)
-                       continue;
-
-               mpsslog("%s %s d-> type %d d %p\n",
-                       mic->name, __func__, d->type, d);
-
-               if (d->type == (__u8)type)
-                       return d;
-       }
-       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
-       return NULL;
-}
-
-/* See comments in vhost.c for explanation of next_desc() */
-static unsigned next_desc(struct vring_desc *desc)
-{
-       unsigned int next;
-
-       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT))
-               return -1U;
-       next = le16toh(desc->next);
-       return next;
-}
-
-/* Sum up all the IOVEC length */
-static ssize_t
-sum_iovec_len(struct mic_copy_desc *copy)
-{
-       ssize_t sum = 0;
-       unsigned int i;
-
-       for (i = 0; i < copy->iovcnt; i++)
-               sum += copy->iov[i].iov_len;
-       return sum;
-}
-
-static inline void verify_out_len(struct mic_info *mic,
-       struct mic_copy_desc *copy)
-{
-       if (copy->out_len != sum_iovec_len(copy)) {
-               mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n",
-                       mic->name, __func__, __LINE__,
-                       copy->out_len, sum_iovec_len(copy));
-               assert(copy->out_len == sum_iovec_len(copy));
-       }
-}
-
-/* Display an iovec */
-static void
-disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy,
-          const char *s, int line)
-{
-       unsigned int i;
-
-       for (i = 0; i < copy->iovcnt; i++)
-               mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n",
-                       mic->name, s, line, i,
-                       copy->iov[i].iov_base, copy->iov[i].iov_len);
-}
-
-static inline __u16 read_avail_idx(struct mic_vring *vr)
-{
-       return ACCESS_ONCE(vr->info->avail_idx);
-}
-
-static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr,
-                               struct mic_copy_desc *copy, ssize_t len)
-{
-       copy->vr_idx = tx ? 0 : 1;
-       copy->update_used = true;
-       if (type == VIRTIO_ID_NET)
-               copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr);
-       else
-               copy->iov[0].iov_len = len;
-}
-
-/* Central API which triggers the copies */
-static int
-mic_virtio_copy(struct mic_info *mic, int fd,
-               struct mic_vring *vr, struct mic_copy_desc *copy)
-{
-       int ret;
-
-       ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy);
-       if (ret) {
-               mpsslog("%s %s %d errno %s ret %d\n",
-                       mic->name, __func__, __LINE__,
-                       strerror(errno), ret);
-       }
-       return ret;
-}
-
-static inline unsigned _vring_size(unsigned int num, unsigned long align)
-{
-       return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num)
-                               + align - 1) & ~(align - 1))
-               + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num;
-}
-
-/*
- * This initialization routine requires at least one
- * vring i.e. vr0. vr1 is optional.
- */
-static void *
-init_vr(struct mic_info *mic, int fd, int type,
-       struct mic_vring *vr0, struct mic_vring *vr1, int num_vq)
-{
-       int vr_size;
-       char *va;
-
-       vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
-                                        MIC_VIRTIO_RING_ALIGN) +
-                            sizeof(struct _mic_vring_info));
-       va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq,
-               PROT_READ, MAP_SHARED, fd, 0);
-       if (MAP_FAILED == va) {
-               mpsslog("%s %s %d mmap failed errno %s\n",
-                       mic->name, __func__, __LINE__,
-                       strerror(errno));
-               goto done;
-       }
-       set_dp(mic, type, va);
-       vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END];
-       vr0->info = vr0->va +
-               _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
-       vring_init(&vr0->vr,
-                  MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN);
-       mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ",
-               __func__, mic->name, vr0->va, vr0->info, vr_size,
-               _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
-       mpsslog("magic 0x%x expected 0x%x\n",
-               le32toh(vr0->info->magic), MIC_MAGIC + type);
-       assert(le32toh(vr0->info->magic) == MIC_MAGIC + type);
-       if (vr1) {
-               vr1->va = (struct mic_vring *)
-                       &va[MIC_DEVICE_PAGE_END + vr_size];
-               vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES,
-                       MIC_VIRTIO_RING_ALIGN);
-               vring_init(&vr1->vr,
-                          MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN);
-               mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ",
-                       __func__, mic->name, vr1->va, vr1->info, vr_size,
-                       _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
-               mpsslog("magic 0x%x expected 0x%x\n",
-                       le32toh(vr1->info->magic), MIC_MAGIC + type + 1);
-               assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1);
-       }
-done:
-       return va;
-}
-
-static int
-wait_for_card_driver(struct mic_info *mic, int fd, int type)
-{
-       struct pollfd pollfd;
-       int err;
-       struct mic_device_desc *desc = get_device_desc(mic, type);
-       __u8 prev_status;
-
-       if (!desc)
-               return -ENODEV;
-       prev_status = desc->status;
-       pollfd.fd = fd;
-       mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n",
-               mic->name, __func__, type, desc->status);
-
-       while (1) {
-               pollfd.events = POLLIN;
-               pollfd.revents = 0;
-               err = poll(&pollfd, 1, -1);
-               if (err < 0) {
-                       mpsslog("%s %s poll failed %s\n",
-                               mic->name, __func__, strerror(errno));
-                       continue;
-               }
-
-               if (pollfd.revents) {
-                       if (desc->status != prev_status) {
-                               mpsslog("%s %s Waiting... desc-> type %d "
-                                       "status 0x%x\n",
-                                       mic->name, __func__, type,
-                                       desc->status);
-                               prev_status = desc->status;
-                       }
-                       if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
-                               mpsslog("%s %s poll.revents %d\n",
-                                       mic->name, __func__, pollfd.revents);
-                               mpsslog("%s %s desc-> type %d status 0x%x\n",
-                                       mic->name, __func__, type,
-                                       desc->status);
-                               break;
-                       }
-               }
-       }
-       return 0;
-}
-
-/* Spin till we have some descriptors */
-static void
-spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr)
-{
-       __u16 avail_idx = read_avail_idx(vr);
-
-       while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) {
-#ifdef DEBUG
-               mpsslog("%s %s waiting for desc avail %d info_avail %d\n",
-                       mic->name, __func__,
-                       le16toh(vr->vr.avail->idx), vr->info->avail_idx);
-#endif
-               sched_yield();
-       }
-}
-
-static void *
-virtio_net(void *arg)
-{
-       static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)];
-       static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __attribute__ ((aligned(64)));
-       struct iovec vnet_iov[2][2] = {
-               { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) },
-                 { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } },
-               { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) },
-                 { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } },
-       };
-       struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1];
-       struct mic_info *mic = (struct mic_info *)arg;
-       char if_name[IFNAMSIZ];
-       struct pollfd net_poll[MAX_NET_FD];
-       struct mic_vring tx_vr, rx_vr;
-       struct mic_copy_desc copy;
-       struct mic_device_desc *desc;
-       int err;
-
-       snprintf(if_name, IFNAMSIZ, "mic%d", mic->id);
-       mic->mic_net.tap_fd = tun_alloc(mic, if_name);
-       if (mic->mic_net.tap_fd < 0)
-               goto done;
-
-       if (tap_configure(mic, if_name))
-               goto done;
-       mpsslog("MIC name %s id %d\n", mic->name, mic->id);
-
-       net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd;
-       net_poll[NET_FD_VIRTIO_NET].events = POLLIN;
-       net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd;
-       net_poll[NET_FD_TUN].events = POLLIN;
-
-       if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd,
-                                 VIRTIO_ID_NET, &tx_vr, &rx_vr,
-               virtnet_dev_page.dd.num_vq)) {
-               mpsslog("%s init_vr failed %s\n",
-                       mic->name, strerror(errno));
-               goto done;
-       }
-
-       copy.iovcnt = 2;
-       desc = get_device_desc(mic, VIRTIO_ID_NET);
-
-       while (1) {
-               ssize_t len;
-
-               net_poll[NET_FD_VIRTIO_NET].revents = 0;
-               net_poll[NET_FD_TUN].revents = 0;
-
-               /* Start polling for data from tap and virtio net */
-               err = poll(net_poll, 2, -1);
-               if (err < 0) {
-                       mpsslog("%s poll failed %s\n",
-                               __func__, strerror(errno));
-                       continue;
-               }
-               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
-                       err = wait_for_card_driver(mic,
-                                                  mic->mic_net.virtio_net_fd,
-                                                  VIRTIO_ID_NET);
-                       if (err) {
-                               mpsslog("%s %s %d Exiting...\n",
-                                       mic->name, __func__, __LINE__);
-                               break;
-                       }
-               }
-               /*
-                * Check if there is data to be read from TUN and write to
-                * virtio net fd if there is.
-                */
-               if (net_poll[NET_FD_TUN].revents & POLLIN) {
-                       copy.iov = iov0;
-                       len = readv(net_poll[NET_FD_TUN].fd,
-                               copy.iov, copy.iovcnt);
-                       if (len > 0) {
-                               struct virtio_net_hdr *hdr
-                                       = (struct virtio_net_hdr *)vnet_hdr[0];
-
-                               /* Disable checksums on the card since we are on
-                                  a reliable PCIe link */
-                               hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID;
-#ifdef DEBUG
-                               mpsslog("%s %s %d hdr->flags 0x%x ", mic->name,
-                                       __func__, __LINE__, hdr->flags);
-                               mpsslog("copy.out_len %d hdr->gso_type 0x%x\n",
-                                       copy.out_len, hdr->gso_type);
-#endif
-#ifdef DEBUG
-                               disp_iovec(mic, copy, __func__, __LINE__);
-                               mpsslog("%s %s %d read from tap 0x%lx\n",
-                                       mic->name, __func__, __LINE__,
-                                       len);
-#endif
-                               spin_for_descriptors(mic, &tx_vr);
-                               txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, &copy,
-                                            len);
-
-                               err = mic_virtio_copy(mic,
-                                       mic->mic_net.virtio_net_fd, &tx_vr,
-                                       &copy);
-                               if (err < 0) {
-                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
-                                               mic->name, __func__, __LINE__,
-                                               strerror(errno));
-                               }
-                               if (!err)
-                                       verify_out_len(mic, &copy);
-#ifdef DEBUG
-                               disp_iovec(mic, copy, __func__, __LINE__);
-                               mpsslog("%s %s %d wrote to net 0x%lx\n",
-                                       mic->name, __func__, __LINE__,
-                                       sum_iovec_len(&copy));
-#endif
-                               /* Reinitialize IOV for next run */
-                               iov0[1].iov_len = MAX_NET_PKT_SIZE;
-                       } else if (len < 0) {
-                               disp_iovec(mic, &copy, __func__, __LINE__);
-                               mpsslog("%s %s %d read failed %s ", mic->name,
-                                       __func__, __LINE__, strerror(errno));
-                               mpsslog("cnt %d sum %zd\n",
-                                       copy.iovcnt, sum_iovec_len(&copy));
-                       }
-               }
-
-               /*
-                * Check if there is data to be read from virtio net and
-                * write to TUN if there is.
-                */
-               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) {
-                       while (rx_vr.info->avail_idx !=
-                               le16toh(rx_vr.vr.avail->idx)) {
-                               copy.iov = iov1;
-                               txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, &copy,
-                                            MAX_NET_PKT_SIZE
-                                       + sizeof(struct virtio_net_hdr));
-
-                               err = mic_virtio_copy(mic,
-                                       mic->mic_net.virtio_net_fd, &rx_vr,
-                                       &copy);
-                               if (!err) {
-#ifdef DEBUG
-                                       struct virtio_net_hdr *hdr
-                                               = (struct virtio_net_hdr *)
-                                                       vnet_hdr[1];
-
-                                       mpsslog("%s %s %d hdr->flags 0x%x, ",
-                                               mic->name, __func__, __LINE__,
-                                               hdr->flags);
-                                       mpsslog("out_len %d gso_type 0x%x\n",
-                                               copy.out_len,
-                                               hdr->gso_type);
-#endif
-                                       /* Set the correct output iov_len */
-                                       iov1[1].iov_len = copy.out_len -
-                                               sizeof(struct virtio_net_hdr);
-                                       verify_out_len(mic, &copy);
-#ifdef DEBUG
-                                       disp_iovec(mic, copy, __func__,
-                                                  __LINE__);
-                                       mpsslog("%s %s %d ",
-                                               mic->name, __func__, __LINE__);
-                                       mpsslog("read from net 0x%lx\n",
-                                               sum_iovec_len(copy));
-#endif
-                                       len = writev(net_poll[NET_FD_TUN].fd,
-                                               copy.iov, copy.iovcnt);
-                                       if (len != sum_iovec_len(&copy)) {
-                                               mpsslog("Tun write failed %s ",
-                                                       strerror(errno));
-                                               mpsslog("len 0x%zx ", len);
-                                               mpsslog("read_len 0x%zx\n",
-                                                       sum_iovec_len(&copy));
-                                       } else {
-#ifdef DEBUG
-                                               disp_iovec(mic, &copy, __func__,
-                                                          __LINE__);
-                                               mpsslog("%s %s %d ",
-                                                       mic->name, __func__,
-                                                       __LINE__);
-                                               mpsslog("wrote to tap 0x%lx\n",
-                                                       len);
-#endif
-                                       }
-                               } else {
-                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
-                                               mic->name, __func__, __LINE__,
-                                               strerror(errno));
-                                       break;
-                               }
-                       }
-               }
-               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
-                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
-       }
-done:
-       pthread_exit(NULL);
-}
-
-/* virtio_console */
-#define VIRTIO_CONSOLE_FD 0
-#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1)
-#define MAX_CONSOLE_FD (MONITOR_FD + 1)  /* must be the last one + 1 */
-#define MAX_BUFFER_SIZE PAGE_SIZE
-
-static void *
-virtio_console(void *arg)
-{
-       static __u8 vcons_buf[2][PAGE_SIZE];
-       struct iovec vcons_iov[2] = {
-               { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) },
-               { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) },
-       };
-       struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1];
-       struct mic_info *mic = (struct mic_info *)arg;
-       int err;
-       struct pollfd console_poll[MAX_CONSOLE_FD];
-       int pty_fd;
-       char *pts_name;
-       ssize_t len;
-       struct mic_vring tx_vr, rx_vr;
-       struct mic_copy_desc copy;
-       struct mic_device_desc *desc;
-
-       pty_fd = posix_openpt(O_RDWR);
-       if (pty_fd < 0) {
-               mpsslog("can't open a pseudoterminal master device: %s\n",
-                       strerror(errno));
-               goto _return;
-       }
-       pts_name = ptsname(pty_fd);
-       if (pts_name == NULL) {
-               mpsslog("can't get pts name\n");
-               goto _close_pty;
-       }
-       printf("%s console message goes to %s\n", mic->name, pts_name);
-       mpsslog("%s console message goes to %s\n", mic->name, pts_name);
-       err = grantpt(pty_fd);
-       if (err < 0) {
-               mpsslog("can't grant access: %s %s\n",
-                       pts_name, strerror(errno));
-               goto _close_pty;
-       }
-       err = unlockpt(pty_fd);
-       if (err < 0) {
-               mpsslog("can't unlock a pseudoterminal: %s %s\n",
-                       pts_name, strerror(errno));
-               goto _close_pty;
-       }
-       console_poll[MONITOR_FD].fd = pty_fd;
-       console_poll[MONITOR_FD].events = POLLIN;
-
-       console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd;
-       console_poll[VIRTIO_CONSOLE_FD].events = POLLIN;
-
-       if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd,
-                                 VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr,
-               virtcons_dev_page.dd.num_vq)) {
-               mpsslog("%s init_vr failed %s\n",
-                       mic->name, strerror(errno));
-               goto _close_pty;
-       }
-
-       copy.iovcnt = 1;
-       desc = get_device_desc(mic, VIRTIO_ID_CONSOLE);
-
-       for (;;) {
-               console_poll[MONITOR_FD].revents = 0;
-               console_poll[VIRTIO_CONSOLE_FD].revents = 0;
-               err = poll(console_poll, MAX_CONSOLE_FD, -1);
-               if (err < 0) {
-                       mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__,
-                               strerror(errno));
-                       continue;
-               }
-               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
-                       err = wait_for_card_driver(mic,
-                                       mic->mic_console.virtio_console_fd,
-                                       VIRTIO_ID_CONSOLE);
-                       if (err) {
-                               mpsslog("%s %s %d Exiting...\n",
-                                       mic->name, __func__, __LINE__);
-                               break;
-                       }
-               }
-
-               if (console_poll[MONITOR_FD].revents & POLLIN) {
-                       copy.iov = iov0;
-                       len = readv(pty_fd, copy.iov, copy.iovcnt);
-                       if (len > 0) {
-#ifdef DEBUG
-                               disp_iovec(mic, copy, __func__, __LINE__);
-                               mpsslog("%s %s %d read from tap 0x%lx\n",
-                                       mic->name, __func__, __LINE__,
-                                       len);
-#endif
-                               spin_for_descriptors(mic, &tx_vr);
-                               txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr,
-                                            &copy, len);
-
-                               err = mic_virtio_copy(mic,
-                                       mic->mic_console.virtio_console_fd,
-                                       &tx_vr, &copy);
-                               if (err < 0) {
-                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
-                                               mic->name, __func__, __LINE__,
-                                               strerror(errno));
-                               }
-                               if (!err)
-                                       verify_out_len(mic, &copy);
-#ifdef DEBUG
-                               disp_iovec(mic, copy, __func__, __LINE__);
-                               mpsslog("%s %s %d wrote to net 0x%lx\n",
-                                       mic->name, __func__, __LINE__,
-                                       sum_iovec_len(copy));
-#endif
-                               /* Reinitialize IOV for next run */
-                               iov0->iov_len = PAGE_SIZE;
-                       } else if (len < 0) {
-                               disp_iovec(mic, &copy, __func__, __LINE__);
-                               mpsslog("%s %s %d read failed %s ",
-                                       mic->name, __func__, __LINE__,
-                                       strerror(errno));
-                               mpsslog("cnt %d sum %zd\n",
-                                       copy.iovcnt, sum_iovec_len(&copy));
-                       }
-               }
-
-               if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) {
-                       while (rx_vr.info->avail_idx !=
-                               le16toh(rx_vr.vr.avail->idx)) {
-                               copy.iov = iov1;
-                               txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr,
-                                            &copy, PAGE_SIZE);
-
-                               err = mic_virtio_copy(mic,
-                                       mic->mic_console.virtio_console_fd,
-                                       &rx_vr, &copy);
-                               if (!err) {
-                                       /* Set the correct output iov_len */
-                                       iov1->iov_len = copy.out_len;
-                                       verify_out_len(mic, &copy);
-#ifdef DEBUG
-                                       disp_iovec(mic, copy, __func__,
-                                                  __LINE__);
-                                       mpsslog("%s %s %d ",
-                                               mic->name, __func__, __LINE__);
-                                       mpsslog("read from net 0x%lx\n",
-                                               sum_iovec_len(copy));
-#endif
-                                       len = writev(pty_fd,
-                                               copy.iov, copy.iovcnt);
-                                       if (len != sum_iovec_len(&copy)) {
-                                               mpsslog("Tun write failed %s ",
-                                                       strerror(errno));
-                                               mpsslog("len 0x%zx ", len);
-                                               mpsslog("read_len 0x%zx\n",
-                                                       sum_iovec_len(&copy));
-                                       } else {
-#ifdef DEBUG
-                                               disp_iovec(mic, copy, __func__,
-                                                          __LINE__);
-                                               mpsslog("%s %s %d ",
-                                                       mic->name, __func__,
-                                                       __LINE__);
-                                               mpsslog("wrote to tap 0x%lx\n",
-                                                       len);
-#endif
-                                       }
-                               } else {
-                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
-                                               mic->name, __func__, __LINE__,
-                                               strerror(errno));
-                                       break;
-                               }
-                       }
-               }
-               if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
-                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
-       }
-_close_pty:
-       close(pty_fd);
-_return:
-       pthread_exit(NULL);
-}
-
-static void
-add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd)
-{
-       char path[PATH_MAX];
-       int fd, err;
-
-       snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id);
-       fd = open(path, O_RDWR);
-       if (fd < 0) {
-               mpsslog("Could not open %s %s\n", path, strerror(errno));
-               return;
-       }
-
-       err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd);
-       if (err < 0) {
-               mpsslog("Could not add %d %s\n", dd->type, strerror(errno));
-               close(fd);
-               return;
-       }
-       switch (dd->type) {
-       case VIRTIO_ID_NET:
-               mic->mic_net.virtio_net_fd = fd;
-               mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name);
-               break;
-       case VIRTIO_ID_CONSOLE:
-               mic->mic_console.virtio_console_fd = fd;
-               mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name);
-               break;
-       case VIRTIO_ID_BLOCK:
-               mic->mic_virtblk.virtio_block_fd = fd;
-               mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name);
-               break;
-       }
-}
-
-static bool
-set_backend_file(struct mic_info *mic)
-{
-       FILE *config;
-       char buff[PATH_MAX], *line, *evv, *p;
-
-       snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id);
-       config = fopen(buff, "r");
-       if (config == NULL)
-               return false;
-       do {  /* look for "virtblk_backend=XXXX" */
-               line = fgets(buff, PATH_MAX, config);
-               if (line == NULL)
-                       break;
-               if (*line == '#')
-                       continue;
-               p = strchr(line, '\n');
-               if (p)
-                       *p = '\0';
-       } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0);
-       fclose(config);
-       if (line == NULL)
-               return false;
-       evv = strchr(line, '=');
-       if (evv == NULL)
-               return false;
-       mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1);
-       if (mic->mic_virtblk.backend_file == NULL) {
-               mpsslog("%s %d can't allocate memory\n", mic->name, mic->id);
-               return false;
-       }
-       strcpy(mic->mic_virtblk.backend_file, evv + 1);
-       return true;
-}
-
-#define SECTOR_SIZE 512
-static bool
-set_backend_size(struct mic_info *mic)
-{
-       mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0,
-               SEEK_END);
-       if (mic->mic_virtblk.backend_size < 0) {
-               mpsslog("%s: can't seek: %s\n",
-                       mic->name, mic->mic_virtblk.backend_file);
-               return false;
-       }
-       virtblk_dev_page.blk_config.capacity =
-               mic->mic_virtblk.backend_size / SECTOR_SIZE;
-       if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0)
-               virtblk_dev_page.blk_config.capacity++;
-
-       virtblk_dev_page.blk_config.capacity =
-               htole64(virtblk_dev_page.blk_config.capacity);
-
-       return true;
-}
-
-static bool
-open_backend(struct mic_info *mic)
-{
-       if (!set_backend_file(mic))
-               goto _error_exit;
-       mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR);
-       if (mic->mic_virtblk.backend < 0) {
-               mpsslog("%s: can't open: %s\n", mic->name,
-                       mic->mic_virtblk.backend_file);
-               goto _error_free;
-       }
-       if (!set_backend_size(mic))
-               goto _error_close;
-       mic->mic_virtblk.backend_addr = mmap(NULL,
-               mic->mic_virtblk.backend_size,
-               PROT_READ|PROT_WRITE, MAP_SHARED,
-               mic->mic_virtblk.backend, 0L);
-       if (mic->mic_virtblk.backend_addr == MAP_FAILED) {
-               mpsslog("%s: can't map: %s %s\n",
-                       mic->name, mic->mic_virtblk.backend_file,
-                       strerror(errno));
-               goto _error_close;
-       }
-       return true;
-
- _error_close:
-       close(mic->mic_virtblk.backend);
- _error_free:
-       free(mic->mic_virtblk.backend_file);
- _error_exit:
-       return false;
-}
-
-static void
-close_backend(struct mic_info *mic)
-{
-       munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size);
-       close(mic->mic_virtblk.backend);
-       free(mic->mic_virtblk.backend_file);
-}
-
-static bool
-start_virtblk(struct mic_info *mic, struct mic_vring *vring)
-{
-       if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) {
-               mpsslog("%s: blk_config is not 8 byte aligned.\n",
-                       mic->name);
-               return false;
-       }
-       add_virtio_device(mic, &virtblk_dev_page.dd);
-       if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd,
-                                 VIRTIO_ID_BLOCK, vring, NULL,
-                                 virtblk_dev_page.dd.num_vq)) {
-               mpsslog("%s init_vr failed %s\n",
-                       mic->name, strerror(errno));
-               return false;
-       }
-       return true;
-}
-
-static void
-stop_virtblk(struct mic_info *mic)
-{
-       int vr_size, ret;
-
-       vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
-                                        MIC_VIRTIO_RING_ALIGN) +
-                            sizeof(struct _mic_vring_info));
-       ret = munmap(mic->mic_virtblk.block_dp,
-               MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq);
-       if (ret < 0)
-               mpsslog("%s munmap errno %d\n", mic->name, errno);
-       close(mic->mic_virtblk.virtio_block_fd);
-}
-
-static __u8
-header_error_check(struct vring_desc *desc)
-{
-       if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) {
-               mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n",
-                       __func__, __LINE__);
-               return -EIO;
-       }
-       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) {
-               mpsslog("%s() %d: alone\n",
-                       __func__, __LINE__);
-               return -EIO;
-       }
-       if (le16toh(desc->flags) & VRING_DESC_F_WRITE) {
-               mpsslog("%s() %d: not read\n",
-                       __func__, __LINE__);
-               return -EIO;
-       }
-       return 0;
-}
-
-static int
-read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx)
-{
-       struct iovec iovec;
-       struct mic_copy_desc copy;
-
-       iovec.iov_len = sizeof(*hdr);
-       iovec.iov_base = hdr;
-       copy.iov = &iovec;
-       copy.iovcnt = 1;
-       copy.vr_idx = 0;  /* only one vring on virtio_block */
-       copy.update_used = false;  /* do not update used index */
-       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
-}
-
-static int
-transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt)
-{
-       struct mic_copy_desc copy;
-
-       copy.iov = iovec;
-       copy.iovcnt = iovcnt;
-       copy.vr_idx = 0;  /* only one vring on virtio_block */
-       copy.update_used = false;  /* do not update used index */
-       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
-}
-
-static __u8
-status_error_check(struct vring_desc *desc)
-{
-       if (le32toh(desc->len) != sizeof(__u8)) {
-               mpsslog("%s() %d: length is not sizeof(status)\n",
-                       __func__, __LINE__);
-               return -EIO;
-       }
-       return 0;
-}
-
-static int
-write_status(int fd, __u8 *status)
-{
-       struct iovec iovec;
-       struct mic_copy_desc copy;
-
-       iovec.iov_base = status;
-       iovec.iov_len = sizeof(*status);
-       copy.iov = &iovec;
-       copy.iovcnt = 1;
-       copy.vr_idx = 0;  /* only one vring on virtio_block */
-       copy.update_used = true; /* Update used index */
-       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
-}
-
-#ifndef VIRTIO_BLK_T_GET_ID
-#define VIRTIO_BLK_T_GET_ID    8
-#endif
-
-static void *
-virtio_block(void *arg)
-{
-       struct mic_info *mic = (struct mic_info *)arg;
-       int ret;
-       struct pollfd block_poll;
-       struct mic_vring vring;
-       __u16 avail_idx;
-       __u32 desc_idx;
-       struct vring_desc *desc;
-       struct iovec *iovec, *piov;
-       __u8 status;
-       __u32 buffer_desc_idx;
-       struct virtio_blk_outhdr hdr;
-       void *fos;
-
-       for (;;) {  /* forever */
-               if (!open_backend(mic)) { /* No virtblk */
-                       for (mic->mic_virtblk.signaled = 0;
-                               !mic->mic_virtblk.signaled;)
-                               sleep(1);
-                       continue;
-               }
-
-               /* backend file is specified. */
-               if (!start_virtblk(mic, &vring))
-                       goto _close_backend;
-               iovec = malloc(sizeof(*iovec) *
-                       le32toh(virtblk_dev_page.blk_config.seg_max));
-               if (!iovec) {
-                       mpsslog("%s: can't alloc iovec: %s\n",
-                               mic->name, strerror(ENOMEM));
-                       goto _stop_virtblk;
-               }
-
-               block_poll.fd = mic->mic_virtblk.virtio_block_fd;
-               block_poll.events = POLLIN;
-               for (mic->mic_virtblk.signaled = 0;
-                    !mic->mic_virtblk.signaled;) {
-                       block_poll.revents = 0;
-                                       /* timeout in 1 sec to see signaled */
-                       ret = poll(&block_poll, 1, 1000);
-                       if (ret < 0) {
-                               mpsslog("%s %d: poll failed: %s\n",
-                                       __func__, __LINE__,
-                                       strerror(errno));
-                               continue;
-                       }
-
-                       if (!(block_poll.revents & POLLIN)) {
-#ifdef DEBUG
-                               mpsslog("%s %d: block_poll.revents=0x%x\n",
-                                       __func__, __LINE__, block_poll.revents);
-#endif
-                               continue;
-                       }
-
-                       /* POLLIN */
-                       while (vring.info->avail_idx !=
-                               le16toh(vring.vr.avail->idx)) {
-                               /* read header element */
-                               avail_idx =
-                                       vring.info->avail_idx &
-                                       (vring.vr.num - 1);
-                               desc_idx = le16toh(
-                                       vring.vr.avail->ring[avail_idx]);
-                               desc = &vring.vr.desc[desc_idx];
-#ifdef DEBUG
-                               mpsslog("%s() %d: avail_idx=%d ",
-                                       __func__, __LINE__,
-                                       vring.info->avail_idx);
-                               mpsslog("vring.vr.num=%d desc=%p\n",
-                                       vring.vr.num, desc);
-#endif
-                               status = header_error_check(desc);
-                               ret = read_header(
-                                       mic->mic_virtblk.virtio_block_fd,
-                                       &hdr, desc_idx);
-                               if (ret < 0) {
-                                       mpsslog("%s() %d %s: ret=%d %s\n",
-                                               __func__, __LINE__,
-                                               mic->name, ret,
-                                               strerror(errno));
-                                       break;
-                               }
-                               /* buffer element */
-                               piov = iovec;
-                               status = 0;
-                               fos = mic->mic_virtblk.backend_addr +
-                                       (hdr.sector * SECTOR_SIZE);
-                               buffer_desc_idx = next_desc(desc);
-                               desc_idx = buffer_desc_idx;
-                               for (desc = &vring.vr.desc[buffer_desc_idx];
-                                    desc->flags & VRING_DESC_F_NEXT;
-                                    desc_idx = next_desc(desc),
-                                            desc = &vring.vr.desc[desc_idx]) {
-                                       piov->iov_len = desc->len;
-                                       piov->iov_base = fos;
-                                       piov++;
-                                       fos += desc->len;
-                               }
-                               /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */
-                               if (hdr.type & ~(VIRTIO_BLK_T_OUT |
-                                       VIRTIO_BLK_T_GET_ID)) {
-                                       /*
-                                         VIRTIO_BLK_T_IN - does not do
-                                         anything. Probably for documenting.
-                                         VIRTIO_BLK_T_SCSI_CMD - for
-                                         virtio_scsi.
-                                         VIRTIO_BLK_T_FLUSH - turned off in
-                                         config space.
-                                         VIRTIO_BLK_T_BARRIER - defined but not
-                                         used in anywhere.
-                                       */
-                                       mpsslog("%s() %d: type %x ",
-                                               __func__, __LINE__,
-                                               hdr.type);
-                                       mpsslog("is not supported\n");
-                                       status = -ENOTSUP;
-
-                               } else {
-                                       ret = transfer_blocks(
-                                       mic->mic_virtblk.virtio_block_fd,
-                                               iovec,
-                                               piov - iovec);
-                                       if (ret < 0 &&
-                                           status != 0)
-                                               status = ret;
-                               }
-                               /* write status and update used pointer */
-                               if (status != 0)
-                                       status = status_error_check(desc);
-                               ret = write_status(
-                                       mic->mic_virtblk.virtio_block_fd,
-                                       &status);
-#ifdef DEBUG
-                               mpsslog("%s() %d: write status=%d on desc=%p\n",
-                                       __func__, __LINE__,
-                                       status, desc);
-#endif
-                       }
-               }
-               free(iovec);
-_stop_virtblk:
-               stop_virtblk(mic);
-_close_backend:
-               close_backend(mic);
-       }  /* forever */
-
-       pthread_exit(NULL);
-}
-
-static void
-reset(struct mic_info *mic)
-{
-#define RESET_TIMEOUT 120
-       int i = RESET_TIMEOUT;
-       setsysfs(mic->name, "state", "reset");
-       while (i) {
-               char *state;
-               state = readsysfs(mic->name, "state");
-               if (!state)
-                       goto retry;
-               mpsslog("%s: %s %d state %s\n",
-                       mic->name, __func__, __LINE__, state);
-
-               if (!strcmp(state, "ready")) {
-                       free(state);
-                       break;
-               }
-               free(state);
-retry:
-               sleep(1);
-               i--;
-       }
-}
-
-static int
-get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status)
-{
-       if (!strcmp(shutdown_status, "nop"))
-               return MIC_NOP;
-       if (!strcmp(shutdown_status, "crashed"))
-               return MIC_CRASHED;
-       if (!strcmp(shutdown_status, "halted"))
-               return MIC_HALTED;
-       if (!strcmp(shutdown_status, "poweroff"))
-               return MIC_POWER_OFF;
-       if (!strcmp(shutdown_status, "restart"))
-               return MIC_RESTART;
-       mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status);
-       /* Invalid state */
-       assert(0);
-};
-
-static int get_mic_state(struct mic_info *mic)
-{
-       char *state = NULL;
-       enum mic_states mic_state;
-
-       while (!state) {
-               state = readsysfs(mic->name, "state");
-               sleep(1);
-       }
-       mpsslog("%s: %s %d state %s\n",
-               mic->name, __func__, __LINE__, state);
-
-       if (!strcmp(state, "ready")) {
-               mic_state = MIC_READY;
-       } else if (!strcmp(state, "booting")) {
-               mic_state = MIC_BOOTING;
-       } else if (!strcmp(state, "online")) {
-               mic_state = MIC_ONLINE;
-       } else if (!strcmp(state, "shutting_down")) {
-               mic_state = MIC_SHUTTING_DOWN;
-       } else if (!strcmp(state, "reset_failed")) {
-               mic_state = MIC_RESET_FAILED;
-       } else if (!strcmp(state, "resetting")) {
-               mic_state = MIC_RESETTING;
-       } else {
-               mpsslog("%s: BUG invalid state %s\n", mic->name, state);
-               assert(0);
-       }
-
-       free(state);
-       return mic_state;
-};
-
-static void mic_handle_shutdown(struct mic_info *mic)
-{
-#define SHUTDOWN_TIMEOUT 60
-       int i = SHUTDOWN_TIMEOUT;
-       char *shutdown_status;
-       while (i) {
-               shutdown_status = readsysfs(mic->name, "shutdown_status");
-               if (!shutdown_status) {
-                       sleep(1);
-                       continue;
-               }
-               mpsslog("%s: %s %d shutdown_status %s\n",
-                       mic->name, __func__, __LINE__, shutdown_status);
-               switch (get_mic_shutdown_status(mic, shutdown_status)) {
-               case MIC_RESTART:
-                       mic->restart = 1;
-               case MIC_HALTED:
-               case MIC_POWER_OFF:
-               case MIC_CRASHED:
-                       free(shutdown_status);
-                       goto reset;
-               default:
-                       break;
-               }
-               free(shutdown_status);
-               sleep(1);
-               i--;
-       }
-reset:
-       if (!i)
-               mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n",
-                       mic->name, __func__, __LINE__, shutdown_status);
-       reset(mic);
-}
-
-static int open_state_fd(struct mic_info *mic)
-{
-       char pathname[PATH_MAX];
-       int fd;
-
-       snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
-                MICSYSFSDIR, mic->name, "state");
-
-       fd = open(pathname, O_RDONLY);
-       if (fd < 0)
-               mpsslog("%s: opening file %s failed %s\n",
-                       mic->name, pathname, strerror(errno));
-       return fd;
-}
-
-static int block_till_state_change(int fd, struct mic_info *mic)
-{
-       struct pollfd ufds[1];
-       char value[PAGE_SIZE];
-       int ret;
-
-       ufds[0].fd = fd;
-       ufds[0].events = POLLERR | POLLPRI;
-       ret = poll(ufds, 1, -1);
-       if (ret < 0) {
-               mpsslog("%s: %s %d poll failed %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               return ret;
-       }
-
-       ret = lseek(fd, 0, SEEK_SET);
-       if (ret < 0) {
-               mpsslog("%s: %s %d Failed to seek to 0: %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               return ret;
-       }
-
-       ret = read(fd, value, sizeof(value));
-       if (ret < 0) {
-               mpsslog("%s: %s %d Failed to read sysfs entry: %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               return ret;
-       }
-
-       return 0;
-}
-
-static void *
-mic_config(void *arg)
-{
-       struct mic_info *mic = (struct mic_info *)arg;
-       int fd, ret, stat = 0;
-
-       fd = open_state_fd(mic);
-       if (fd < 0) {
-               mpsslog("%s: %s %d open state fd failed %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               goto exit;
-       }
-
-       do {
-               ret = block_till_state_change(fd, mic);
-               if (ret < 0) {
-                       mpsslog("%s: %s %d block_till_state_change error %s\n",
-                               mic->name, __func__, __LINE__, strerror(errno));
-                       goto close_exit;
-               }
-
-               switch (get_mic_state(mic)) {
-               case MIC_SHUTTING_DOWN:
-                       mic_handle_shutdown(mic);
-                       break;
-               case MIC_READY:
-               case MIC_RESET_FAILED:
-                       ret = kill(mic->pid, SIGTERM);
-                       mpsslog("%s: %s %d kill pid %d ret %d\n",
-                               mic->name, __func__, __LINE__,
-                               mic->pid, ret);
-                       if (!ret) {
-                               ret = waitpid(mic->pid, &stat,
-                                             WIFSIGNALED(stat));
-                               mpsslog("%s: %s %d waitpid ret %d pid %d\n",
-                                       mic->name, __func__, __LINE__,
-                                       ret, mic->pid);
-                       }
-                       if (mic->boot_on_resume) {
-                               setsysfs(mic->name, "state", "boot");
-                               mic->boot_on_resume = 0;
-                       }
-                       goto close_exit;
-               default:
-                       break;
-               }
-       } while (1);
-
-close_exit:
-       close(fd);
-exit:
-       init_mic(mic);
-       pthread_exit(NULL);
-}
-
-static void
-set_cmdline(struct mic_info *mic)
-{
-       char buffer[PATH_MAX];
-       int len;
-
-       len = snprintf(buffer, PATH_MAX,
-               "clocksource=tsc highres=off nohz=off ");
-       len += snprintf(buffer + len, PATH_MAX - len,
-               "cpufreq_on;corec6_off;pc3_off;pc6_off ");
-       len += snprintf(buffer + len, PATH_MAX - len,
-               "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
-               mic->id + 1);
-
-       setsysfs(mic->name, "cmdline", buffer);
-       mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer);
-       snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1);
-       mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer);
-}
-
-static void
-set_log_buf_info(struct mic_info *mic)
-{
-       int fd;
-       off_t len;
-       char system_map[] = "/lib/firmware/mic/System.map";
-       char *map, *temp, log_buf[17] = {'\0'};
-
-       fd = open(system_map, O_RDONLY);
-       if (fd < 0) {
-               mpsslog("%s: Opening System.map failed: %d\n",
-                       mic->name, errno);
-               return;
-       }
-       len = lseek(fd, 0, SEEK_END);
-       if (len < 0) {
-               mpsslog("%s: Reading System.map size failed: %d\n",
-                       mic->name, errno);
-               close(fd);
-               return;
-       }
-       map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (map == MAP_FAILED) {
-               mpsslog("%s: mmap of System.map failed: %d\n",
-                       mic->name, errno);
-               close(fd);
-               return;
-       }
-       temp = strstr(map, "__log_buf");
-       if (!temp) {
-               mpsslog("%s: __log_buf not found: %d\n", mic->name, errno);
-               munmap(map, len);
-               close(fd);
-               return;
-       }
-       strncpy(log_buf, temp - 19, 16);
-       setsysfs(mic->name, "log_buf_addr", log_buf);
-       mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf);
-       temp = strstr(map, "log_buf_len");
-       if (!temp) {
-               mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno);
-               munmap(map, len);
-               close(fd);
-               return;
-       }
-       strncpy(log_buf, temp - 19, 16);
-       setsysfs(mic->name, "log_buf_len", log_buf);
-       mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf);
-       munmap(map, len);
-       close(fd);
-}
-
-static void
-change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
-{
-       struct mic_info *mic;
-
-       for (mic = mic_list.next; mic != NULL; mic = mic->next)
-               mic->mic_virtblk.signaled = 1/* true */;
-}
-
-static void
-set_mic_boot_params(struct mic_info *mic)
-{
-       set_log_buf_info(mic);
-       set_cmdline(mic);
-}
-
-static void *
-init_mic(void *arg)
-{
-       struct mic_info *mic = (struct mic_info *)arg;
-       struct sigaction ignore = {
-               .sa_flags = 0,
-               .sa_handler = SIG_IGN
-       };
-       struct sigaction act = {
-               .sa_flags = SA_SIGINFO,
-               .sa_sigaction = change_virtblk_backend,
-       };
-       char buffer[PATH_MAX];
-       int err, fd;
-
-       /*
-        * Currently, one virtio block device is supported for each MIC card
-        * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon.
-        * The signal informs the virtio block backend about a change in the
-        * configuration file which specifies the virtio backend file name on
-        * the host. Virtio block backend then re-reads the configuration file
-        * and switches to the new block device. This signalling mechanism may
-        * not be required once multiple virtio block devices are supported by
-        * the MIC daemon.
-        */
-       sigaction(SIGUSR1, &ignore, NULL);
-retry:
-       fd = open_state_fd(mic);
-       if (fd < 0) {
-               mpsslog("%s: %s %d open state fd failed %s\n",
-                       mic->name, __func__, __LINE__, strerror(errno));
-               sleep(2);
-               goto retry;
-       }
-
-       if (mic->restart) {
-               snprintf(buffer, PATH_MAX, "boot");
-               setsysfs(mic->name, "state", buffer);
-               mpsslog("%s restarting mic %d\n",
-                       mic->name, mic->restart);
-               mic->restart = 0;
-       }
-
-       while (1) {
-               while (block_till_state_change(fd, mic)) {
-                       mpsslog("%s: %s %d block_till_state_change error %s\n",
-                               mic->name, __func__, __LINE__, strerror(errno));
-                       sleep(2);
-                       continue;
-               }
-
-               if (get_mic_state(mic) == MIC_BOOTING)
-                       break;
-       }
-
-       mic->pid = fork();
-       switch (mic->pid) {
-       case 0:
-               add_virtio_device(mic, &virtcons_dev_page.dd);
-               add_virtio_device(mic, &virtnet_dev_page.dd);
-               err = pthread_create(&mic->mic_console.console_thread, NULL,
-                       virtio_console, mic);
-               if (err)
-                       mpsslog("%s virtcons pthread_create failed %s\n",
-                               mic->name, strerror(err));
-               err = pthread_create(&mic->mic_net.net_thread, NULL,
-                       virtio_net, mic);
-               if (err)
-                       mpsslog("%s virtnet pthread_create failed %s\n",
-                               mic->name, strerror(err));
-               err = pthread_create(&mic->mic_virtblk.block_thread, NULL,
-                       virtio_block, mic);
-               if (err)
-                       mpsslog("%s virtblk pthread_create failed %s\n",
-                               mic->name, strerror(err));
-               sigemptyset(&act.sa_mask);
-               err = sigaction(SIGUSR1, &act, NULL);
-               if (err)
-                       mpsslog("%s sigaction SIGUSR1 failed %s\n",
-                               mic->name, strerror(errno));
-               while (1)
-                       sleep(60);
-       case -1:
-               mpsslog("fork failed MIC name %s id %d errno %d\n",
-                       mic->name, mic->id, errno);
-               break;
-       default:
-               err = pthread_create(&mic->config_thread, NULL,
-                                    mic_config, mic);
-               if (err)
-                       mpsslog("%s mic_config pthread_create failed %s\n",
-                               mic->name, strerror(err));
-       }
-
-       return NULL;
-}
-
-static void
-start_daemon(void)
-{
-       struct mic_info *mic;
-       int err;
-
-       for (mic = mic_list.next; mic; mic = mic->next) {
-               set_mic_boot_params(mic);
-               err = pthread_create(&mic->init_thread, NULL, init_mic, mic);
-               if (err)
-                       mpsslog("%s init_mic pthread_create failed %s\n",
-                               mic->name, strerror(err));
-       }
-
-       while (1)
-               sleep(60);
-}
-
-static int
-init_mic_list(void)
-{
-       struct mic_info *mic = &mic_list;
-       struct dirent *file;
-       DIR *dp;
-       int cnt = 0;
-
-       dp = opendir(MICSYSFSDIR);
-       if (!dp)
-               return 0;
-
-       while ((file = readdir(dp)) != NULL) {
-               if (!strncmp(file->d_name, "mic", 3)) {
-                       mic->next = calloc(1, sizeof(struct mic_info));
-                       if (mic->next) {
-                               mic = mic->next;
-                               mic->id = atoi(&file->d_name[3]);
-                               mic->name = malloc(strlen(file->d_name) + 16);
-                               if (mic->name)
-                                       strcpy(mic->name, file->d_name);
-                               mpsslog("MIC name %s id %d\n", mic->name,
-                                       mic->id);
-                               cnt++;
-                       }
-               }
-       }
-
-       closedir(dp);
-       return cnt;
-}
-
-void
-mpsslog(char *format, ...)
-{
-       va_list args;
-       char buffer[4096];
-       char ts[52], *ts1;
-       time_t t;
-
-       if (logfp == NULL)
-               return;
-
-       va_start(args, format);
-       vsprintf(buffer, format, args);
-       va_end(args);
-
-       time(&t);
-       ts1 = ctime_r(&t, ts);
-       ts1[strlen(ts1) - 1] = '\0';
-       fprintf(logfp, "%s: %s", ts1, buffer);
-
-       fflush(logfp);
-}
-
-int
-main(int argc, char *argv[])
-{
-       int cnt;
-       pid_t pid;
-
-       myname = argv[0];
-
-       logfp = fopen(LOGFILE_NAME, "a+");
-       if (!logfp) {
-               fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME);
-               exit(1);
-       }
-       pid = fork();
-       switch (pid) {
-       case 0:
-               break;
-       case -1:
-               exit(2);
-       default:
-               exit(0);
-       }
-
-       mpsslog("MIC Daemon start\n");
-
-       cnt = init_mic_list();
-       if (cnt == 0) {
-               mpsslog("MIC module not loaded\n");
-               exit(3);
-       }
-       mpsslog("MIC found %d devices\n", cnt);
-
-       start_daemon();
-
-       exit(0);
-}
diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h
deleted file mode 100644 (file)
index 8bd6494..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Intel MIC Platform Software Stack (MPSS)
- *
- * Copyright(c) 2013 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Intel MIC User Space Tools.
- */
-#ifndef _MPSSD_H_
-#define _MPSSD_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <dirent.h>
-#include <libgen.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <time.h>
-#include <errno.h>
-#include <sys/dir.h>
-#include <sys/ioctl.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <pthread.h>
-#include <signal.h>
-#include <limits.h>
-#include <syslog.h>
-#include <getopt.h>
-#include <net/if.h>
-#include <linux/if_tun.h>
-#include <linux/if_tun.h>
-#include <linux/virtio_ids.h>
-
-#define MICSYSFSDIR "/sys/class/mic"
-#define LOGFILE_NAME "/var/log/mpssd"
-#define PAGE_SIZE 4096
-
-struct mic_console_info {
-       pthread_t       console_thread;
-       int             virtio_console_fd;
-       void            *console_dp;
-};
-
-struct mic_net_info {
-       pthread_t       net_thread;
-       int             virtio_net_fd;
-       int             tap_fd;
-       void            *net_dp;
-};
-
-struct mic_virtblk_info {
-       pthread_t       block_thread;
-       int             virtio_block_fd;
-       void            *block_dp;
-       volatile sig_atomic_t   signaled;
-       char            *backend_file;
-       int             backend;
-       void            *backend_addr;
-       long            backend_size;
-};
-
-struct mic_info {
-       int             id;
-       char            *name;
-       pthread_t       config_thread;
-       pthread_t       init_thread;
-       pid_t           pid;
-       struct mic_console_info mic_console;
-       struct mic_net_info     mic_net;
-       struct mic_virtblk_info mic_virtblk;
-       int             restart;
-       int             boot_on_resume;
-       struct mic_info *next;
-};
-
-__attribute__((format(printf, 1, 2)))
-void mpsslog(char *format, ...);
-char *readsysfs(char *dir, char *entry);
-int setsysfs(char *dir, char *entry, char *value);
-#endif
diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c
deleted file mode 100644 (file)
index 8dd3269..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Intel MIC Platform Software Stack (MPSS)
- *
- * Copyright(c) 2013 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution in
- * the file called "COPYING".
- *
- * Intel MIC User Space Tools.
- */
-
-#include "mpssd.h"
-
-#define PAGE_SIZE 4096
-
-char *
-readsysfs(char *dir, char *entry)
-{
-       char filename[PATH_MAX];
-       char value[PAGE_SIZE];
-       char *string = NULL;
-       int fd;
-       int len;
-
-       if (dir == NULL)
-               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
-       else
-               snprintf(filename, PATH_MAX,
-                        "%s/%s/%s", MICSYSFSDIR, dir, entry);
-
-       fd = open(filename, O_RDONLY);
-       if (fd < 0) {
-               mpsslog("Failed to open sysfs entry '%s': %s\n",
-                       filename, strerror(errno));
-               return NULL;
-       }
-
-       len = read(fd, value, sizeof(value));
-       if (len < 0) {
-               mpsslog("Failed to read sysfs entry '%s': %s\n",
-                       filename, strerror(errno));
-               goto readsys_ret;
-       }
-       if (len == 0)
-               goto readsys_ret;
-
-       value[len - 1] = '\0';
-
-       string = malloc(strlen(value) + 1);
-       if (string)
-               strcpy(string, value);
-
-readsys_ret:
-       close(fd);
-       return string;
-}
-
-int
-setsysfs(char *dir, char *entry, char *value)
-{
-       char filename[PATH_MAX];
-       char *oldvalue;
-       int fd, ret = 0;
-
-       if (dir == NULL)
-               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
-       else
-               snprintf(filename, PATH_MAX, "%s/%s/%s",
-                        MICSYSFSDIR, dir, entry);
-
-       oldvalue = readsysfs(dir, entry);
-
-       fd = open(filename, O_RDWR);
-       if (fd < 0) {
-               ret = errno;
-               mpsslog("Failed to open sysfs entry '%s': %s\n",
-                       filename, strerror(errno));
-               goto done;
-       }
-
-       if (!oldvalue || strcmp(value, oldvalue)) {
-               if (write(fd, value, strlen(value)) < 0) {
-                       ret = errno;
-                       mpsslog("Failed to write new sysfs entry '%s': %s\n",
-                               filename, strerror(errno));
-               }
-       }
-       close(fd);
-done:
-       if (oldvalue)
-               free(oldvalue);
-       return ret;
-}
diff --git a/Documentation/misc-devices/Makefile b/Documentation/misc-devices/Makefile
deleted file mode 100644 (file)
index e2b7aa4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := mei
diff --git a/Documentation/misc-devices/mei/.gitignore b/Documentation/misc-devices/mei/.gitignore
deleted file mode 100644 (file)
index f356b81..0000000
+++ /dev/null
@@ -1 +0,0 @@
-mei-amt-version
diff --git a/Documentation/misc-devices/mei/Makefile b/Documentation/misc-devices/mei/Makefile
deleted file mode 100644 (file)
index d758047..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := mei-amt-version
-HOSTCFLAGS_mei-amt-version.o += -I$(objtree)/usr/include
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
diff --git a/Documentation/misc-devices/mei/TODO b/Documentation/misc-devices/mei/TODO
deleted file mode 100644 (file)
index 6b3625d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-TODO:
-       - Cleanup and split the timer function
diff --git a/Documentation/misc-devices/mei/mei-amt-version.c b/Documentation/misc-devices/mei/mei-amt-version.c
deleted file mode 100644 (file)
index 57d0d87..0000000
+++ /dev/null
@@ -1,479 +0,0 @@
-/******************************************************************************
- * Intel Management Engine Interface (Intel MEI) Linux driver
- * Intel MEI Interface Header
- *
- * This file is provided under a dual BSD/GPLv2 license.  When using or
- * redistributing this file, you may do so under either license.
- *
- * GPL LICENSE SUMMARY
- *
- * Copyright(c) 2012 Intel Corporation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
- * USA
- *
- * The full GNU General Public License is included in this distribution
- * in the file called LICENSE.GPL.
- *
- * Contact Information:
- *     Intel Corporation.
- *     linux-mei@linux.intel.com
- *     http://www.intel.com
- *
- * BSD LICENSE
- *
- * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name Intel Corporation nor the names of its
- *    contributors may be used to endorse or promote products derived
- *    from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *****************************************************************************/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <bits/wordsize.h>
-#include <linux/mei.h>
-
-/*****************************************************************************
- * Intel Management Engine Interface
- *****************************************************************************/
-
-#define mei_msg(_me, fmt, ARGS...) do {         \
-       if (_me->verbose)                       \
-               fprintf(stderr, fmt, ##ARGS);   \
-} while (0)
-
-#define mei_err(_me, fmt, ARGS...) do {         \
-       fprintf(stderr, "Error: " fmt, ##ARGS); \
-} while (0)
-
-struct mei {
-       uuid_le guid;
-       bool initialized;
-       bool verbose;
-       unsigned int buf_size;
-       unsigned char prot_ver;
-       int fd;
-};
-
-static void mei_deinit(struct mei *cl)
-{
-       if (cl->fd != -1)
-               close(cl->fd);
-       cl->fd = -1;
-       cl->buf_size = 0;
-       cl->prot_ver = 0;
-       cl->initialized = false;
-}
-
-static bool mei_init(struct mei *me, const uuid_le *guid,
-               unsigned char req_protocol_version, bool verbose)
-{
-       int result;
-       struct mei_client *cl;
-       struct mei_connect_client_data data;
-
-       me->verbose = verbose;
-
-       me->fd = open("/dev/mei", O_RDWR);
-       if (me->fd == -1) {
-               mei_err(me, "Cannot establish a handle to the Intel MEI driver\n");
-               goto err;
-       }
-       memcpy(&me->guid, guid, sizeof(*guid));
-       memset(&data, 0, sizeof(data));
-       me->initialized = true;
-
-       memcpy(&data.in_client_uuid, &me->guid, sizeof(me->guid));
-       result = ioctl(me->fd, IOCTL_MEI_CONNECT_CLIENT, &data);
-       if (result) {
-               mei_err(me, "IOCTL_MEI_CONNECT_CLIENT receive message. err=%d\n", result);
-               goto err;
-       }
-       cl = &data.out_client_properties;
-       mei_msg(me, "max_message_length %d\n", cl->max_msg_length);
-       mei_msg(me, "protocol_version %d\n", cl->protocol_version);
-
-       if ((req_protocol_version > 0) &&
-            (cl->protocol_version != req_protocol_version)) {
-               mei_err(me, "Intel MEI protocol version not supported\n");
-               goto err;
-       }
-
-       me->buf_size = cl->max_msg_length;
-       me->prot_ver = cl->protocol_version;
-
-       return true;
-err:
-       mei_deinit(me);
-       return false;
-}
-
-static ssize_t mei_recv_msg(struct mei *me, unsigned char *buffer,
-                       ssize_t len, unsigned long timeout)
-{
-       ssize_t rc;
-
-       mei_msg(me, "call read length = %zd\n", len);
-
-       rc = read(me->fd, buffer, len);
-       if (rc < 0) {
-               mei_err(me, "read failed with status %zd %s\n",
-                               rc, strerror(errno));
-               mei_deinit(me);
-       } else {
-               mei_msg(me, "read succeeded with result %zd\n", rc);
-       }
-       return rc;
-}
-
-static ssize_t mei_send_msg(struct mei *me, const unsigned char *buffer,
-                       ssize_t len, unsigned long timeout)
-{
-       struct timeval tv;
-       ssize_t written;
-       ssize_t rc;
-       fd_set set;
-
-       tv.tv_sec = timeout / 1000;
-       tv.tv_usec = (timeout % 1000) * 1000000;
-
-       mei_msg(me, "call write length = %zd\n", len);
-
-       written = write(me->fd, buffer, len);
-       if (written < 0) {
-               rc = -errno;
-               mei_err(me, "write failed with status %zd %s\n",
-                       written, strerror(errno));
-               goto out;
-       }
-
-       FD_ZERO(&set);
-       FD_SET(me->fd, &set);
-       rc = select(me->fd + 1 , &set, NULL, NULL, &tv);
-       if (rc > 0 && FD_ISSET(me->fd, &set)) {
-               mei_msg(me, "write success\n");
-       } else if (rc == 0) {
-               mei_err(me, "write failed on timeout with status\n");
-               goto out;
-       } else { /* rc < 0 */
-               mei_err(me, "write failed on select with status %zd\n", rc);
-               goto out;
-       }
-
-       rc = written;
-out:
-       if (rc < 0)
-               mei_deinit(me);
-
-       return rc;
-}
-
-/***************************************************************************
- * Intel Advanced Management Technology ME Client
- ***************************************************************************/
-
-#define AMT_MAJOR_VERSION 1
-#define AMT_MINOR_VERSION 1
-
-#define AMT_STATUS_SUCCESS                0x0
-#define AMT_STATUS_INTERNAL_ERROR         0x1
-#define AMT_STATUS_NOT_READY              0x2
-#define AMT_STATUS_INVALID_AMT_MODE       0x3
-#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4
-
-#define AMT_STATUS_HOST_IF_EMPTY_RESPONSE  0x4000
-#define AMT_STATUS_SDK_RESOURCES      0x1004
-
-
-#define AMT_BIOS_VERSION_LEN   65
-#define AMT_VERSIONS_NUMBER    50
-#define AMT_UNICODE_STRING_LEN 20
-
-struct amt_unicode_string {
-       uint16_t length;
-       char string[AMT_UNICODE_STRING_LEN];
-} __attribute__((packed));
-
-struct amt_version_type {
-       struct amt_unicode_string description;
-       struct amt_unicode_string version;
-} __attribute__((packed));
-
-struct amt_version {
-       uint8_t major;
-       uint8_t minor;
-} __attribute__((packed));
-
-struct amt_code_versions {
-       uint8_t bios[AMT_BIOS_VERSION_LEN];
-       uint32_t count;
-       struct amt_version_type versions[AMT_VERSIONS_NUMBER];
-} __attribute__((packed));
-
-/***************************************************************************
- * Intel Advanced Management Technology Host Interface
- ***************************************************************************/
-
-struct amt_host_if_msg_header {
-       struct amt_version version;
-       uint16_t _reserved;
-       uint32_t command;
-       uint32_t length;
-} __attribute__((packed));
-
-struct amt_host_if_resp_header {
-       struct amt_host_if_msg_header header;
-       uint32_t status;
-       unsigned char data[0];
-} __attribute__((packed));
-
-const uuid_le MEI_IAMTHIF = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,  \
-                               0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c);
-
-#define AMT_HOST_IF_CODE_VERSIONS_REQUEST  0x0400001A
-#define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A
-
-const struct amt_host_if_msg_header CODE_VERSION_REQ = {
-       .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION},
-       ._reserved = 0,
-       .command = AMT_HOST_IF_CODE_VERSIONS_REQUEST,
-       .length = 0
-};
-
-
-struct amt_host_if {
-       struct mei mei_cl;
-       unsigned long send_timeout;
-       bool initialized;
-};
-
-
-static bool amt_host_if_init(struct amt_host_if *acmd,
-                     unsigned long send_timeout, bool verbose)
-{
-       acmd->send_timeout = (send_timeout) ? send_timeout : 20000;
-       acmd->initialized = mei_init(&acmd->mei_cl, &MEI_IAMTHIF, 0, verbose);
-       return acmd->initialized;
-}
-
-static void amt_host_if_deinit(struct amt_host_if *acmd)
-{
-       mei_deinit(&acmd->mei_cl);
-       acmd->initialized = false;
-}
-
-static uint32_t amt_verify_code_versions(const struct amt_host_if_resp_header *resp)
-{
-       uint32_t status = AMT_STATUS_SUCCESS;
-       struct amt_code_versions *code_ver;
-       size_t code_ver_len;
-       uint32_t ver_type_cnt;
-       uint32_t len;
-       uint32_t i;
-
-       code_ver = (struct amt_code_versions *)resp->data;
-       /* length - sizeof(status) */
-       code_ver_len = resp->header.length - sizeof(uint32_t);
-       ver_type_cnt = code_ver_len -
-                       sizeof(code_ver->bios) -
-                       sizeof(code_ver->count);
-       if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) {
-               status = AMT_STATUS_INTERNAL_ERROR;
-               goto out;
-       }
-
-       for (i = 0; i < code_ver->count; i++) {
-               len = code_ver->versions[i].description.length;
-
-               if (len > AMT_UNICODE_STRING_LEN) {
-                       status = AMT_STATUS_INTERNAL_ERROR;
-                       goto out;
-               }
-
-               len = code_ver->versions[i].version.length;
-               if (code_ver->versions[i].version.string[len] != '\0' ||
-                   len != strlen(code_ver->versions[i].version.string)) {
-                       status = AMT_STATUS_INTERNAL_ERROR;
-                       goto out;
-               }
-       }
-out:
-       return status;
-}
-
-static uint32_t amt_verify_response_header(uint32_t command,
-                               const struct amt_host_if_msg_header *resp_hdr,
-                               uint32_t response_size)
-{
-       if (response_size < sizeof(struct amt_host_if_resp_header)) {
-               return AMT_STATUS_INTERNAL_ERROR;
-       } else if (response_size != (resp_hdr->length +
-                               sizeof(struct amt_host_if_msg_header))) {
-               return AMT_STATUS_INTERNAL_ERROR;
-       } else if (resp_hdr->command != command) {
-               return AMT_STATUS_INTERNAL_ERROR;
-       } else if (resp_hdr->_reserved != 0) {
-               return AMT_STATUS_INTERNAL_ERROR;
-       } else if (resp_hdr->version.major != AMT_MAJOR_VERSION ||
-                  resp_hdr->version.minor < AMT_MINOR_VERSION) {
-               return AMT_STATUS_INTERNAL_ERROR;
-       }
-       return AMT_STATUS_SUCCESS;
-}
-
-static uint32_t amt_host_if_call(struct amt_host_if *acmd,
-                       const unsigned char *command, ssize_t command_sz,
-                       uint8_t **read_buf, uint32_t rcmd,
-                       unsigned int expected_sz)
-{
-       uint32_t in_buf_sz;
-       uint32_t out_buf_sz;
-       ssize_t written;
-       uint32_t status;
-       struct amt_host_if_resp_header *msg_hdr;
-
-       in_buf_sz = acmd->mei_cl.buf_size;
-       *read_buf = (uint8_t *)malloc(sizeof(uint8_t) * in_buf_sz);
-       if (*read_buf == NULL)
-               return AMT_STATUS_SDK_RESOURCES;
-       memset(*read_buf, 0, in_buf_sz);
-       msg_hdr = (struct amt_host_if_resp_header *)*read_buf;
-
-       written = mei_send_msg(&acmd->mei_cl,
-                               command, command_sz, acmd->send_timeout);
-       if (written != command_sz)
-               return AMT_STATUS_INTERNAL_ERROR;
-
-       out_buf_sz = mei_recv_msg(&acmd->mei_cl, *read_buf, in_buf_sz, 2000);
-       if (out_buf_sz <= 0)
-               return AMT_STATUS_HOST_IF_EMPTY_RESPONSE;
-
-       status = msg_hdr->status;
-       if (status != AMT_STATUS_SUCCESS)
-               return status;
-
-       status = amt_verify_response_header(rcmd,
-                               &msg_hdr->header, out_buf_sz);
-       if (status != AMT_STATUS_SUCCESS)
-               return status;
-
-       if (expected_sz && expected_sz != out_buf_sz)
-               return AMT_STATUS_INTERNAL_ERROR;
-
-       return AMT_STATUS_SUCCESS;
-}
-
-
-static uint32_t amt_get_code_versions(struct amt_host_if *cmd,
-                              struct amt_code_versions *versions)
-{
-       struct amt_host_if_resp_header *response = NULL;
-       uint32_t status;
-
-       status = amt_host_if_call(cmd,
-                       (const unsigned char *)&CODE_VERSION_REQ,
-                       sizeof(CODE_VERSION_REQ),
-                       (uint8_t **)&response,
-                       AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0);
-
-       if (status != AMT_STATUS_SUCCESS)
-               goto out;
-
-       status = amt_verify_code_versions(response);
-       if (status != AMT_STATUS_SUCCESS)
-               goto out;
-
-       memcpy(versions, response->data, sizeof(struct amt_code_versions));
-out:
-       if (response != NULL)
-               free(response);
-
-       return status;
-}
-
-/************************** end of amt_host_if_command ***********************/
-int main(int argc, char **argv)
-{
-       struct amt_code_versions ver;
-       struct amt_host_if acmd;
-       unsigned int i;
-       uint32_t status;
-       int ret;
-       bool verbose;
-
-       verbose = (argc > 1 && strcmp(argv[1], "-v") == 0);
-
-       if (!amt_host_if_init(&acmd, 5000, verbose)) {
-               ret = 1;
-               goto out;
-       }
-
-       status = amt_get_code_versions(&acmd, &ver);
-
-       amt_host_if_deinit(&acmd);
-
-       switch (status) {
-       case AMT_STATUS_HOST_IF_EMPTY_RESPONSE:
-               printf("Intel AMT: DISABLED\n");
-               ret = 0;
-               break;
-       case AMT_STATUS_SUCCESS:
-               printf("Intel AMT: ENABLED\n");
-               for (i = 0; i < ver.count; i++) {
-                       printf("%s:\t%s\n", ver.versions[i].description.string,
-                               ver.versions[i].version.string);
-               }
-               ret = 0;
-               break;
-       default:
-               printf("An error has occurred\n");
-               ret = 1;
-               break;
-       }
-
-out:
-       return ret;
-}
index a769778..c6beb5f 100644 (file)
@@ -10,8 +10,6 @@ LICENSE.qlge
        - GPLv2 for QLogic Linux qlge NIC Driver
 LICENSE.qlcnic
        - GPLv2 for QLogic Linux qlcnic NIC Driver
-Makefile
-       - Makefile for docsrc.
 PLIP.txt
        - PLIP: The Parallel Line Internet Protocol device driver
 README.ipw2100
diff --git a/Documentation/networking/Makefile b/Documentation/networking/Makefile
deleted file mode 100644 (file)
index 4c5d7c4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := timestamping
diff --git a/Documentation/networking/timestamping/.gitignore b/Documentation/networking/timestamping/.gitignore
deleted file mode 100644 (file)
index 9e69e98..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-timestamping
-txtimestamp
-hwtstamp_config
diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile
deleted file mode 100644 (file)
index 8c20dfa..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-# To compile, from the source root
-#
-#    make headers_install
-#    make M=documentation
-
-# List of programs to build
-hostprogs-y := hwtstamp_config timestamping txtimestamp
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include
-HOSTCFLAGS_txtimestamp.o += -I$(objtree)/usr/include
-HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include
diff --git a/Documentation/networking/timestamping/hwtstamp_config.c b/Documentation/networking/timestamping/hwtstamp_config.c
deleted file mode 100644 (file)
index e8b685a..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/* Test program for SIOC{G,S}HWTSTAMP
- * Copyright 2013 Solarflare Communications
- * Author: Ben Hutchings
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-
-#include <linux/if.h>
-#include <linux/net_tstamp.h>
-#include <linux/sockios.h>
-
-static int
-lookup_value(const char **names, int size, const char *name)
-{
-       int value;
-
-       for (value = 0; value < size; value++)
-               if (names[value] && strcasecmp(names[value], name) == 0)
-                       return value;
-
-       return -1;
-}
-
-static const char *
-lookup_name(const char **names, int size, int value)
-{
-       return (value >= 0 && value < size) ? names[value] : NULL;
-}
-
-static void list_names(FILE *f, const char **names, int size)
-{
-       int value;
-
-       for (value = 0; value < size; value++)
-               if (names[value])
-                       fprintf(f, "    %s\n", names[value]);
-}
-
-static const char *tx_types[] = {
-#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name
-       TX_TYPE(OFF),
-       TX_TYPE(ON),
-       TX_TYPE(ONESTEP_SYNC)
-#undef TX_TYPE
-};
-#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0])))
-
-static const char *rx_filters[] = {
-#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name
-       RX_FILTER(NONE),
-       RX_FILTER(ALL),
-       RX_FILTER(SOME),
-       RX_FILTER(PTP_V1_L4_EVENT),
-       RX_FILTER(PTP_V1_L4_SYNC),
-       RX_FILTER(PTP_V1_L4_DELAY_REQ),
-       RX_FILTER(PTP_V2_L4_EVENT),
-       RX_FILTER(PTP_V2_L4_SYNC),
-       RX_FILTER(PTP_V2_L4_DELAY_REQ),
-       RX_FILTER(PTP_V2_L2_EVENT),
-       RX_FILTER(PTP_V2_L2_SYNC),
-       RX_FILTER(PTP_V2_L2_DELAY_REQ),
-       RX_FILTER(PTP_V2_EVENT),
-       RX_FILTER(PTP_V2_SYNC),
-       RX_FILTER(PTP_V2_DELAY_REQ),
-#undef RX_FILTER
-};
-#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0])))
-
-static void usage(void)
-{
-       fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n"
-             "tx_type is any of (case-insensitive):\n",
-             stderr);
-       list_names(stderr, tx_types, N_TX_TYPES);
-       fputs("rx_filter is any of (case-insensitive):\n", stderr);
-       list_names(stderr, rx_filters, N_RX_FILTERS);
-}
-
-int main(int argc, char **argv)
-{
-       struct ifreq ifr;
-       struct hwtstamp_config config;
-       const char *name;
-       int sock;
-
-       if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) {
-               usage();
-               return 2;
-       }
-
-       if (argc == 4) {
-               config.flags = 0;
-               config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]);
-               config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]);
-               if (config.tx_type < 0 || config.rx_filter < 0) {
-                       usage();
-                       return 2;
-               }
-       }
-
-       sock = socket(AF_INET, SOCK_DGRAM, 0);
-       if (sock < 0) {
-               perror("socket");
-               return 1;
-       }
-
-       strcpy(ifr.ifr_name, argv[1]);
-       ifr.ifr_data = (caddr_t)&config;
-
-       if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) {
-               perror("ioctl");
-               return 1;
-       }
-
-       printf("flags = %#x\n", config.flags);
-       name = lookup_name(tx_types, N_TX_TYPES, config.tx_type);
-       if (name)
-               printf("tx_type = %s\n", name);
-       else
-               printf("tx_type = %d\n", config.tx_type);
-       name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter);
-       if (name)
-               printf("rx_filter = %s\n", name);
-       else
-               printf("rx_filter = %d\n", config.rx_filter);
-
-       return 0;
-}
diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c
deleted file mode 100644 (file)
index 5cdfd74..0000000
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * This program demonstrates how the various time stamping features in
- * the Linux kernel work. It emulates the behavior of a PTP
- * implementation in stand-alone master mode by sending PTPv1 Sync
- * multicasts once every second. It looks for similar packets, but
- * beyond that doesn't actually implement PTP.
- *
- * Outgoing packets are time stamped with SO_TIMESTAMPING with or
- * without hardware support.
- *
- * Incoming packets are time stamped with SO_TIMESTAMPING with or
- * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
- * SO_TIMESTAMP[NS].
- *
- * Copyright (C) 2009 Intel Corporation.
- * Author: Patrick Ohly <patrick.ohly@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/select.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <net/if.h>
-
-#include <asm/types.h>
-#include <linux/net_tstamp.h>
-#include <linux/errqueue.h>
-
-#ifndef SO_TIMESTAMPING
-# define SO_TIMESTAMPING         37
-# define SCM_TIMESTAMPING        SO_TIMESTAMPING
-#endif
-
-#ifndef SO_TIMESTAMPNS
-# define SO_TIMESTAMPNS 35
-#endif
-
-#ifndef SIOCGSTAMPNS
-# define SIOCGSTAMPNS 0x8907
-#endif
-
-#ifndef SIOCSHWTSTAMP
-# define SIOCSHWTSTAMP 0x89b0
-#endif
-
-static void usage(const char *error)
-{
-       if (error)
-               printf("invalid option: %s\n", error);
-       printf("timestamping interface option*\n\n"
-              "Options:\n"
-              "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
-              "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
-              "  SO_TIMESTAMPNS - more accurate software time stamping\n"
-              "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
-              "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
-              "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
-              "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
-              "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
-              "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
-              "  SIOCGSTAMP - check last socket time stamp\n"
-              "  SIOCGSTAMPNS - more accurate socket time stamp\n");
-       exit(1);
-}
-
-static void bail(const char *error)
-{
-       printf("%s: %s\n", error, strerror(errno));
-       exit(1);
-}
-
-static const unsigned char sync[] = {
-       0x00, 0x01, 0x00, 0x01,
-       0x5f, 0x44, 0x46, 0x4c,
-       0x54, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x01, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x01, 0x00, 0x37,
-       0x00, 0x00, 0x00, 0x08,
-       0x00, 0x00, 0x00, 0x00,
-       0x49, 0x05, 0xcd, 0x01,
-       0x29, 0xb1, 0x8d, 0xb0,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x00, 0x00, 0x37,
-       0x00, 0x00, 0x00, 0x04,
-       0x44, 0x46, 0x4c, 0x54,
-       0x00, 0x00, 0xf0, 0x60,
-       0x00, 0x01, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x01,
-       0x00, 0x00, 0xf0, 0x60,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x04,
-       0x44, 0x46, 0x4c, 0x54,
-       0x00, 0x01,
-
-       /* fake uuid */
-       0x00, 0x01,
-       0x02, 0x03, 0x04, 0x05,
-
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00,
-       0x00, 0x00, 0x00, 0x00
-};
-
-static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
-{
-       struct timeval now;
-       int res;
-
-       res = sendto(sock, sync, sizeof(sync), 0,
-               addr, addr_len);
-       gettimeofday(&now, 0);
-       if (res < 0)
-               printf("%s: %s\n", "send", strerror(errno));
-       else
-               printf("%ld.%06ld: sent %d bytes\n",
-                      (long)now.tv_sec, (long)now.tv_usec,
-                      res);
-}
-
-static void printpacket(struct msghdr *msg, int res,
-                       char *data,
-                       int sock, int recvmsg_flags,
-                       int siocgstamp, int siocgstampns)
-{
-       struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
-       struct cmsghdr *cmsg;
-       struct timeval tv;
-       struct timespec ts;
-       struct timeval now;
-
-       gettimeofday(&now, 0);
-
-       printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
-              (long)now.tv_sec, (long)now.tv_usec,
-              (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
-              res,
-              inet_ntoa(from_addr->sin_addr),
-              msg->msg_controllen);
-       for (cmsg = CMSG_FIRSTHDR(msg);
-            cmsg;
-            cmsg = CMSG_NXTHDR(msg, cmsg)) {
-               printf("   cmsg len %zu: ", cmsg->cmsg_len);
-               switch (cmsg->cmsg_level) {
-               case SOL_SOCKET:
-                       printf("SOL_SOCKET ");
-                       switch (cmsg->cmsg_type) {
-                       case SO_TIMESTAMP: {
-                               struct timeval *stamp =
-                                       (struct timeval *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMP %ld.%06ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_usec);
-                               break;
-                       }
-                       case SO_TIMESTAMPNS: {
-                               struct timespec *stamp =
-                                       (struct timespec *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMPNS %ld.%09ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               break;
-                       }
-                       case SO_TIMESTAMPING: {
-                               struct timespec *stamp =
-                                       (struct timespec *)CMSG_DATA(cmsg);
-                               printf("SO_TIMESTAMPING ");
-                               printf("SW %ld.%09ld ",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               stamp++;
-                               /* skip deprecated HW transformed */
-                               stamp++;
-                               printf("HW raw %ld.%09ld",
-                                      (long)stamp->tv_sec,
-                                      (long)stamp->tv_nsec);
-                               break;
-                       }
-                       default:
-                               printf("type %d", cmsg->cmsg_type);
-                               break;
-                       }
-                       break;
-               case IPPROTO_IP:
-                       printf("IPPROTO_IP ");
-                       switch (cmsg->cmsg_type) {
-                       case IP_RECVERR: {
-                               struct sock_extended_err *err =
-                                       (struct sock_extended_err *)CMSG_DATA(cmsg);
-                               printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
-                                       strerror(err->ee_errno),
-                                       err->ee_origin,
-#ifdef SO_EE_ORIGIN_TIMESTAMPING
-                                       err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
-                                       "bounced packet" : "unexpected origin"
-#else
-                                       "probably SO_EE_ORIGIN_TIMESTAMPING"
-#endif
-                                       );
-                               if (res < sizeof(sync))
-                                       printf(" => truncated data?!");
-                               else if (!memcmp(sync, data + res - sizeof(sync),
-                                                       sizeof(sync)))
-                                       printf(" => GOT OUR DATA BACK (HURRAY!)");
-                               break;
-                       }
-                       case IP_PKTINFO: {
-                               struct in_pktinfo *pktinfo =
-                                       (struct in_pktinfo *)CMSG_DATA(cmsg);
-                               printf("IP_PKTINFO interface index %u",
-                                       pktinfo->ipi_ifindex);
-                               break;
-                       }
-                       default:
-                               printf("type %d", cmsg->cmsg_type);
-                               break;
-                       }
-                       break;
-               default:
-                       printf("level %d type %d",
-                               cmsg->cmsg_level,
-                               cmsg->cmsg_type);
-                       break;
-               }
-               printf("\n");
-       }
-
-       if (siocgstamp) {
-               if (ioctl(sock, SIOCGSTAMP, &tv))
-                       printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
-               else
-                       printf("SIOCGSTAMP %ld.%06ld\n",
-                              (long)tv.tv_sec,
-                              (long)tv.tv_usec);
-       }
-       if (siocgstampns) {
-               if (ioctl(sock, SIOCGSTAMPNS, &ts))
-                       printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
-               else
-                       printf("SIOCGSTAMPNS %ld.%09ld\n",
-                              (long)ts.tv_sec,
-                              (long)ts.tv_nsec);
-       }
-}
-
-static void recvpacket(int sock, int recvmsg_flags,
-                      int siocgstamp, int siocgstampns)
-{
-       char data[256];
-       struct msghdr msg;
-       struct iovec entry;
-       struct sockaddr_in from_addr;
-       struct {
-               struct cmsghdr cm;
-               char control[512];
-       } control;
-       int res;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.msg_iov = &entry;
-       msg.msg_iovlen = 1;
-       entry.iov_base = data;
-       entry.iov_len = sizeof(data);
-       msg.msg_name = (caddr_t)&from_addr;
-       msg.msg_namelen = sizeof(from_addr);
-       msg.msg_control = &control;
-       msg.msg_controllen = sizeof(control);
-
-       res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
-       if (res < 0) {
-               printf("%s %s: %s\n",
-                      "recvmsg",
-                      (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
-                      strerror(errno));
-       } else {
-               printpacket(&msg, res, data,
-                           sock, recvmsg_flags,
-                           siocgstamp, siocgstampns);
-       }
-}
-
-int main(int argc, char **argv)
-{
-       int so_timestamping_flags = 0;
-       int so_timestamp = 0;
-       int so_timestampns = 0;
-       int siocgstamp = 0;
-       int siocgstampns = 0;
-       int ip_multicast_loop = 0;
-       char *interface;
-       int i;
-       int enabled = 1;
-       int sock;
-       struct ifreq device;
-       struct ifreq hwtstamp;
-       struct hwtstamp_config hwconfig, hwconfig_requested;
-       struct sockaddr_in addr;
-       struct ip_mreq imr;
-       struct in_addr iaddr;
-       int val;
-       socklen_t len;
-       struct timeval next;
-
-       if (argc < 2)
-               usage(0);
-       interface = argv[1];
-
-       for (i = 2; i < argc; i++) {
-               if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
-                       so_timestamp = 1;
-               else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
-                       so_timestampns = 1;
-               else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
-                       siocgstamp = 1;
-               else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
-                       siocgstampns = 1;
-               else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
-                       ip_multicast_loop = 1;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
-               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
-                       so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
-               else
-                       usage(argv[i]);
-       }
-
-       sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       if (sock < 0)
-               bail("socket");
-
-       memset(&device, 0, sizeof(device));
-       strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
-       if (ioctl(sock, SIOCGIFADDR, &device) < 0)
-               bail("getting interface IP address");
-
-       memset(&hwtstamp, 0, sizeof(hwtstamp));
-       strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
-       hwtstamp.ifr_data = (void *)&hwconfig;
-       memset(&hwconfig, 0, sizeof(hwconfig));
-       hwconfig.tx_type =
-               (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
-               HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
-       hwconfig.rx_filter =
-               (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
-               HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
-       hwconfig_requested = hwconfig;
-       if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
-               if ((errno == EINVAL || errno == ENOTSUP) &&
-                   hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
-                   hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
-                       printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
-               else
-                       bail("SIOCSHWTSTAMP");
-       }
-       printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
-              hwconfig_requested.tx_type, hwconfig.tx_type,
-              hwconfig_requested.rx_filter, hwconfig.rx_filter);
-
-       /* bind to PTP port */
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-       addr.sin_port = htons(319 /* PTP event port */);
-       if (bind(sock,
-                (struct sockaddr *)&addr,
-                sizeof(struct sockaddr_in)) < 0)
-               bail("bind");
-
-       /* set multicast group for outgoing packets */
-       inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
-       addr.sin_addr = iaddr;
-       imr.imr_multiaddr.s_addr = iaddr.s_addr;
-       imr.imr_interface.s_addr =
-               ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
-       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
-                      &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
-               bail("set multicast");
-
-       /* join multicast group, loop our own packet */
-       if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
-                      &imr, sizeof(struct ip_mreq)) < 0)
-               bail("join multicast group");
-
-       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
-                      &ip_multicast_loop, sizeof(enabled)) < 0) {
-               bail("loop multicast");
-       }
-
-       /* set socket options for time stamping */
-       if (so_timestamp &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
-                          &enabled, sizeof(enabled)) < 0)
-               bail("setsockopt SO_TIMESTAMP");
-
-       if (so_timestampns &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
-                          &enabled, sizeof(enabled)) < 0)
-               bail("setsockopt SO_TIMESTAMPNS");
-
-       if (so_timestamping_flags &&
-               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
-                          &so_timestamping_flags,
-                          sizeof(so_timestamping_flags)) < 0)
-               bail("setsockopt SO_TIMESTAMPING");
-
-       /* request IP_PKTINFO for debugging purposes */
-       if (setsockopt(sock, SOL_IP, IP_PKTINFO,
-                      &enabled, sizeof(enabled)) < 0)
-               printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
-
-       /* verify socket options */
-       len = sizeof(val);
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
-       else
-               printf("SO_TIMESTAMP %d\n", val);
-
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
-                      strerror(errno));
-       else
-               printf("SO_TIMESTAMPNS %d\n", val);
-
-       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
-               printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
-                      strerror(errno));
-       } else {
-               printf("SO_TIMESTAMPING %d\n", val);
-               if (val != so_timestamping_flags)
-                       printf("   not the expected value %d\n",
-                              so_timestamping_flags);
-       }
-
-       /* send packets forever every five seconds */
-       gettimeofday(&next, 0);
-       next.tv_sec = (next.tv_sec + 1) / 5 * 5;
-       next.tv_usec = 0;
-       while (1) {
-               struct timeval now;
-               struct timeval delta;
-               long delta_us;
-               int res;
-               fd_set readfs, errorfs;
-
-               gettimeofday(&now, 0);
-               delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
-                       (long)(next.tv_usec - now.tv_usec);
-               if (delta_us > 0) {
-                       /* continue waiting for timeout or data */
-                       delta.tv_sec = delta_us / 1000000;
-                       delta.tv_usec = delta_us % 1000000;
-
-                       FD_ZERO(&readfs);
-                       FD_ZERO(&errorfs);
-                       FD_SET(sock, &readfs);
-                       FD_SET(sock, &errorfs);
-                       printf("%ld.%06ld: select %ldus\n",
-                              (long)now.tv_sec, (long)now.tv_usec,
-                              delta_us);
-                       res = select(sock + 1, &readfs, 0, &errorfs, &delta);
-                       gettimeofday(&now, 0);
-                       printf("%ld.%06ld: select returned: %d, %s\n",
-                              (long)now.tv_sec, (long)now.tv_usec,
-                              res,
-                              res < 0 ? strerror(errno) : "success");
-                       if (res > 0) {
-                               if (FD_ISSET(sock, &readfs))
-                                       printf("ready for reading\n");
-                               if (FD_ISSET(sock, &errorfs))
-                                       printf("has error\n");
-                               recvpacket(sock, 0,
-                                          siocgstamp,
-                                          siocgstampns);
-                               recvpacket(sock, MSG_ERRQUEUE,
-                                          siocgstamp,
-                                          siocgstampns);
-                       }
-               } else {
-                       /* write one packet */
-                       sendpacket(sock,
-                                  (struct sockaddr *)&addr,
-                                  sizeof(addr));
-                       next.tv_sec += 5;
-                       continue;
-               }
-       }
-
-       return 0;
-}
diff --git a/Documentation/networking/timestamping/txtimestamp.c b/Documentation/networking/timestamping/txtimestamp.c
deleted file mode 100644 (file)
index 5df0704..0000000
+++ /dev/null
@@ -1,549 +0,0 @@
-/*
- * Copyright 2014 Google Inc.
- * Author: willemb@google.com (Willem de Bruijn)
- *
- * Test software tx timestamping, including
- *
- * - SCHED, SND and ACK timestamps
- * - RAW, UDP and TCP
- * - IPv4 and IPv6
- * - various packet sizes (to test GSO and TSO)
- *
- * Consult the command line arguments for help on running
- * the various testcases.
- *
- * This test requires a dummy TCP server.
- * A simple `nc6 [-u] -l -p $DESTPORT` will do
- *
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define _GNU_SOURCE
-
-#include <arpa/inet.h>
-#include <asm/types.h>
-#include <error.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <linux/errqueue.h>
-#include <linux/if_ether.h>
-#include <linux/net_tstamp.h>
-#include <netdb.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
-#include <netinet/tcp.h>
-#include <netpacket/packet.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-/* command line parameters */
-static int cfg_proto = SOCK_STREAM;
-static int cfg_ipproto = IPPROTO_TCP;
-static int cfg_num_pkts = 4;
-static int do_ipv4 = 1;
-static int do_ipv6 = 1;
-static int cfg_payload_len = 10;
-static bool cfg_show_payload;
-static bool cfg_do_pktinfo;
-static bool cfg_loop_nodata;
-static uint16_t dest_port = 9000;
-
-static struct sockaddr_in daddr;
-static struct sockaddr_in6 daddr6;
-static struct timespec ts_prev;
-
-static void __print_timestamp(const char *name, struct timespec *cur,
-                             uint32_t key, int payload_len)
-{
-       if (!(cur->tv_sec | cur->tv_nsec))
-               return;
-
-       fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",
-                       name, cur->tv_sec, cur->tv_nsec / 1000,
-                       key, payload_len);
-
-       if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
-               int64_t cur_ms, prev_ms;
-
-               cur_ms = (long) cur->tv_sec * 1000 * 1000;
-               cur_ms += cur->tv_nsec / 1000;
-
-               prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
-               prev_ms += ts_prev.tv_nsec / 1000;
-
-               fprintf(stderr, "  (%+" PRId64 " us)", cur_ms - prev_ms);
-       }
-
-       ts_prev = *cur;
-       fprintf(stderr, "\n");
-}
-
-static void print_timestamp_usr(void)
-{
-       struct timespec ts;
-       struct timeval tv;      /* avoid dependency on -lrt */
-
-       gettimeofday(&tv, NULL);
-       ts.tv_sec = tv.tv_sec;
-       ts.tv_nsec = tv.tv_usec * 1000;
-
-       __print_timestamp("  USR", &ts, 0, 0);
-}
-
-static void print_timestamp(struct scm_timestamping *tss, int tstype,
-                           int tskey, int payload_len)
-{
-       const char *tsname;
-
-       switch (tstype) {
-       case SCM_TSTAMP_SCHED:
-               tsname = "  ENQ";
-               break;
-       case SCM_TSTAMP_SND:
-               tsname = "  SND";
-               break;
-       case SCM_TSTAMP_ACK:
-               tsname = "  ACK";
-               break;
-       default:
-               error(1, 0, "unknown timestamp type: %u",
-               tstype);
-       }
-       __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
-}
-
-/* TODO: convert to check_and_print payload once API is stable */
-static void print_payload(char *data, int len)
-{
-       int i;
-
-       if (!len)
-               return;
-
-       if (len > 70)
-               len = 70;
-
-       fprintf(stderr, "payload: ");
-       for (i = 0; i < len; i++)
-               fprintf(stderr, "%02hhx ", data[i]);
-       fprintf(stderr, "\n");
-}
-
-static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
-{
-       char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
-
-       fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",
-               ifindex,
-               saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
-               daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
-}
-
-static void __poll(int fd)
-{
-       struct pollfd pollfd;
-       int ret;
-
-       memset(&pollfd, 0, sizeof(pollfd));
-       pollfd.fd = fd;
-       ret = poll(&pollfd, 1, 100);
-       if (ret != 1)
-               error(1, errno, "poll");
-}
-
-static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
-{
-       struct sock_extended_err *serr = NULL;
-       struct scm_timestamping *tss = NULL;
-       struct cmsghdr *cm;
-       int batch = 0;
-
-       for (cm = CMSG_FIRSTHDR(msg);
-            cm && cm->cmsg_len;
-            cm = CMSG_NXTHDR(msg, cm)) {
-               if (cm->cmsg_level == SOL_SOCKET &&
-                   cm->cmsg_type == SCM_TIMESTAMPING) {
-                       tss = (void *) CMSG_DATA(cm);
-               } else if ((cm->cmsg_level == SOL_IP &&
-                           cm->cmsg_type == IP_RECVERR) ||
-                          (cm->cmsg_level == SOL_IPV6 &&
-                           cm->cmsg_type == IPV6_RECVERR)) {
-                       serr = (void *) CMSG_DATA(cm);
-                       if (serr->ee_errno != ENOMSG ||
-                           serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
-                               fprintf(stderr, "unknown ip error %d %d\n",
-                                               serr->ee_errno,
-                                               serr->ee_origin);
-                               serr = NULL;
-                       }
-               } else if (cm->cmsg_level == SOL_IP &&
-                          cm->cmsg_type == IP_PKTINFO) {
-                       struct in_pktinfo *info = (void *) CMSG_DATA(cm);
-                       print_pktinfo(AF_INET, info->ipi_ifindex,
-                                     &info->ipi_spec_dst, &info->ipi_addr);
-               } else if (cm->cmsg_level == SOL_IPV6 &&
-                          cm->cmsg_type == IPV6_PKTINFO) {
-                       struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
-                       print_pktinfo(AF_INET6, info6->ipi6_ifindex,
-                                     NULL, &info6->ipi6_addr);
-               } else
-                       fprintf(stderr, "unknown cmsg %d,%d\n",
-                                       cm->cmsg_level, cm->cmsg_type);
-
-               if (serr && tss) {
-                       print_timestamp(tss, serr->ee_info, serr->ee_data,
-                                       payload_len);
-                       serr = NULL;
-                       tss = NULL;
-                       batch++;
-               }
-       }
-
-       if (batch > 1)
-               fprintf(stderr, "batched %d timestamps\n", batch);
-}
-
-static int recv_errmsg(int fd)
-{
-       static char ctrl[1024 /* overprovision*/];
-       static struct msghdr msg;
-       struct iovec entry;
-       static char *data;
-       int ret = 0;
-
-       data = malloc(cfg_payload_len);
-       if (!data)
-               error(1, 0, "malloc");
-
-       memset(&msg, 0, sizeof(msg));
-       memset(&entry, 0, sizeof(entry));
-       memset(ctrl, 0, sizeof(ctrl));
-
-       entry.iov_base = data;
-       entry.iov_len = cfg_payload_len;
-       msg.msg_iov = &entry;
-       msg.msg_iovlen = 1;
-       msg.msg_name = NULL;
-       msg.msg_namelen = 0;
-       msg.msg_control = ctrl;
-       msg.msg_controllen = sizeof(ctrl);
-
-       ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
-       if (ret == -1 && errno != EAGAIN)
-               error(1, errno, "recvmsg");
-
-       if (ret >= 0) {
-               __recv_errmsg_cmsg(&msg, ret);
-               if (cfg_show_payload)
-                       print_payload(data, cfg_payload_len);
-       }
-
-       free(data);
-       return ret == -1;
-}
-
-static void do_test(int family, unsigned int opt)
-{
-       char *buf;
-       int fd, i, val = 1, total_len;
-
-       if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
-               /* due to lack of checksum generation code */
-               fprintf(stderr, "test: skipping datagram over IPv6\n");
-               return;
-       }
-
-       total_len = cfg_payload_len;
-       if (cfg_proto == SOCK_RAW) {
-               total_len += sizeof(struct udphdr);
-               if (cfg_ipproto == IPPROTO_RAW)
-                       total_len += sizeof(struct iphdr);
-       }
-
-       buf = malloc(total_len);
-       if (!buf)
-               error(1, 0, "malloc");
-
-       fd = socket(family, cfg_proto, cfg_ipproto);
-       if (fd < 0)
-               error(1, errno, "socket");
-
-       if (cfg_proto == SOCK_STREAM) {
-               if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
-                              (char*) &val, sizeof(val)))
-                       error(1, 0, "setsockopt no nagle");
-
-               if (family == PF_INET) {
-                       if (connect(fd, (void *) &daddr, sizeof(daddr)))
-                               error(1, errno, "connect ipv4");
-               } else {
-                       if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
-                               error(1, errno, "connect ipv6");
-               }
-       }
-
-       if (cfg_do_pktinfo) {
-               if (family == AF_INET6) {
-                       if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
-                                      &val, sizeof(val)))
-                               error(1, errno, "setsockopt pktinfo ipv6");
-               } else {
-                       if (setsockopt(fd, SOL_IP, IP_PKTINFO,
-                                      &val, sizeof(val)))
-                               error(1, errno, "setsockopt pktinfo ipv4");
-               }
-       }
-
-       opt |= SOF_TIMESTAMPING_SOFTWARE |
-              SOF_TIMESTAMPING_OPT_CMSG |
-              SOF_TIMESTAMPING_OPT_ID;
-       if (cfg_loop_nodata)
-               opt |= SOF_TIMESTAMPING_OPT_TSONLY;
-
-       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
-                      (char *) &opt, sizeof(opt)))
-               error(1, 0, "setsockopt timestamping");
-
-       for (i = 0; i < cfg_num_pkts; i++) {
-               memset(&ts_prev, 0, sizeof(ts_prev));
-               memset(buf, 'a' + i, total_len);
-
-               if (cfg_proto == SOCK_RAW) {
-                       struct udphdr *udph;
-                       int off = 0;
-
-                       if (cfg_ipproto == IPPROTO_RAW) {
-                               struct iphdr *iph = (void *) buf;
-
-                               memset(iph, 0, sizeof(*iph));
-                               iph->ihl      = 5;
-                               iph->version  = 4;
-                               iph->ttl      = 2;
-                               iph->daddr    = daddr.sin_addr.s_addr;
-                               iph->protocol = IPPROTO_UDP;
-                               /* kernel writes saddr, csum, len */
-
-                               off = sizeof(*iph);
-                       }
-
-                       udph = (void *) buf + off;
-                       udph->source = ntohs(9000);     /* random spoof */
-                       udph->dest   = ntohs(dest_port);
-                       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
-                       udph->check  = 0;       /* not allowed for IPv6 */
-               }
-
-               print_timestamp_usr();
-               if (cfg_proto != SOCK_STREAM) {
-                       if (family == PF_INET)
-                               val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
-                       else
-                               val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
-               } else {
-                       val = send(fd, buf, cfg_payload_len, 0);
-               }
-               if (val != total_len)
-                       error(1, errno, "send");
-
-               /* wait for all errors to be queued, else ACKs arrive OOO */
-               usleep(50 * 1000);
-
-               __poll(fd);
-
-               while (!recv_errmsg(fd)) {}
-       }
-
-       if (close(fd))
-               error(1, errno, "close");
-
-       free(buf);
-       usleep(400 * 1000);
-}
-
-static void __attribute__((noreturn)) usage(const char *filepath)
-{
-       fprintf(stderr, "\nUsage: %s [options] hostname\n"
-                       "\nwhere options are:\n"
-                       "  -4:   only IPv4\n"
-                       "  -6:   only IPv6\n"
-                       "  -h:   show this message\n"
-                       "  -I:   request PKTINFO\n"
-                       "  -l N: send N bytes at a time\n"
-                       "  -n:   set no-payload option\n"
-                       "  -r:   use raw\n"
-                       "  -R:   use raw (IP_HDRINCL)\n"
-                       "  -p N: connect to port N\n"
-                       "  -u:   use udp\n"
-                       "  -x:   show payload (up to 70 bytes)\n",
-                       filepath);
-       exit(1);
-}
-
-static void parse_opt(int argc, char **argv)
-{
-       int proto_count = 0;
-       char c;
-
-       while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) {
-               switch (c) {
-               case '4':
-                       do_ipv6 = 0;
-                       break;
-               case '6':
-                       do_ipv4 = 0;
-                       break;
-               case 'I':
-                       cfg_do_pktinfo = true;
-                       break;
-               case 'n':
-                       cfg_loop_nodata = true;
-                       break;
-               case 'r':
-                       proto_count++;
-                       cfg_proto = SOCK_RAW;
-                       cfg_ipproto = IPPROTO_UDP;
-                       break;
-               case 'R':
-                       proto_count++;
-                       cfg_proto = SOCK_RAW;
-                       cfg_ipproto = IPPROTO_RAW;
-                       break;
-               case 'u':
-                       proto_count++;
-                       cfg_proto = SOCK_DGRAM;
-                       cfg_ipproto = IPPROTO_UDP;
-                       break;
-               case 'l':
-                       cfg_payload_len = strtoul(optarg, NULL, 10);
-                       break;
-               case 'p':
-                       dest_port = strtoul(optarg, NULL, 10);
-                       break;
-               case 'x':
-                       cfg_show_payload = true;
-                       break;
-               case 'h':
-               default:
-                       usage(argv[0]);
-               }
-       }
-
-       if (!cfg_payload_len)
-               error(1, 0, "payload may not be nonzero");
-       if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
-               error(1, 0, "udp packet might exceed expected MTU");
-       if (!do_ipv4 && !do_ipv6)
-               error(1, 0, "pass -4 or -6, not both");
-       if (proto_count > 1)
-               error(1, 0, "pass -r, -R or -u, not multiple");
-
-       if (optind != argc - 1)
-               error(1, 0, "missing required hostname argument");
-}
-
-static void resolve_hostname(const char *hostname)
-{
-       struct addrinfo *addrs, *cur;
-       int have_ipv4 = 0, have_ipv6 = 0;
-
-       if (getaddrinfo(hostname, NULL, NULL, &addrs))
-               error(1, errno, "getaddrinfo");
-
-       cur = addrs;
-       while (cur && !have_ipv4 && !have_ipv6) {
-               if (!have_ipv4 && cur->ai_family == AF_INET) {
-                       memcpy(&daddr, cur->ai_addr, sizeof(daddr));
-                       daddr.sin_port = htons(dest_port);
-                       have_ipv4 = 1;
-               }
-               else if (!have_ipv6 && cur->ai_family == AF_INET6) {
-                       memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
-                       daddr6.sin6_port = htons(dest_port);
-                       have_ipv6 = 1;
-               }
-               cur = cur->ai_next;
-       }
-       if (addrs)
-               freeaddrinfo(addrs);
-
-       do_ipv4 &= have_ipv4;
-       do_ipv6 &= have_ipv6;
-}
-
-static void do_main(int family)
-{
-       fprintf(stderr, "family:       %s\n",
-                       family == PF_INET ? "INET" : "INET6");
-
-       fprintf(stderr, "test SND\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
-
-       fprintf(stderr, "test ENQ\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SCHED);
-
-       fprintf(stderr, "test ENQ + SND\n");
-       do_test(family, SOF_TIMESTAMPING_TX_SCHED |
-                       SOF_TIMESTAMPING_TX_SOFTWARE);
-
-       if (cfg_proto == SOCK_STREAM) {
-               fprintf(stderr, "\ntest ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_ACK);
-
-               fprintf(stderr, "\ntest SND + ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
-                               SOF_TIMESTAMPING_TX_ACK);
-
-               fprintf(stderr, "\ntest ENQ + SND + ACK\n");
-               do_test(family, SOF_TIMESTAMPING_TX_SCHED |
-                               SOF_TIMESTAMPING_TX_SOFTWARE |
-                               SOF_TIMESTAMPING_TX_ACK);
-       }
-}
-
-const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
-
-int main(int argc, char **argv)
-{
-       if (argc == 1)
-               usage(argv[0]);
-
-       parse_opt(argc, argv);
-       resolve_hostname(argv[argc - 1]);
-
-       fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);
-       fprintf(stderr, "payload:      %u\n", cfg_payload_len);
-       fprintf(stderr, "server port:  %u\n", dest_port);
-       fprintf(stderr, "\n");
-
-       if (do_ipv4)
-               do_main(PF_INET);
-       if (do_ipv6)
-               do_main(PF_INET6);
-
-       return 0;
-}
diff --git a/Documentation/pcmcia/.gitignore b/Documentation/pcmcia/.gitignore
deleted file mode 100644 (file)
index 53d0813..0000000
+++ /dev/null
@@ -1 +0,0 @@
-crc32hash
diff --git a/Documentation/pcmcia/Makefile b/Documentation/pcmcia/Makefile
deleted file mode 100644 (file)
index 47a8fa1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-# List of programs to build
-hostprogs-y := crc32hash
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_crc32hash.o += -I$(objtree)/usr/include
diff --git a/Documentation/pcmcia/crc32hash.c b/Documentation/pcmcia/crc32hash.c
deleted file mode 100644 (file)
index 44f8bee..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/* crc32hash.c - derived from linux/lib/crc32.c, GNU GPL v2 */
-/* Usage example:
-$ ./crc32hash "Dual Speed"
-*/
-
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-static unsigned int crc32(unsigned char const *p, unsigned int len)
-{
-       int i;
-       unsigned int crc = 0;
-       while (len--) {
-               crc ^= *p++;
-               for (i = 0; i < 8; i++)
-                       crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
-       }
-       return crc;
-}
-
-int main(int argc, char **argv) {
-       unsigned int result;
-       if (argc != 2) {
-               printf("no string passed as argument\n");
-               return -1;
-       }
-       result = crc32((unsigned char const *)argv[1], strlen(argv[1]));
-       printf("0x%x\n", result);
-       return 0;
-}
index 199afd1..5f3e00a 100644 (file)
@@ -27,7 +27,7 @@ pcmcia:m0149cC1ABf06pfn00fn00pa725B842DpbF1EFEE84pc0877B627pd00000000
 The hex value after "pa" is the hash of product ID string 1, after "pb" for
 string 2 and so on.
 
-Alternatively, you can use crc32hash (see Documentation/pcmcia/crc32hash.c)
+Alternatively, you can use crc32hash (see tools/pcmcia/crc32hash.c)
 to determine the crc32 hash.  Simply pass the string you want to evaluate
 as argument to this program, e.g.:
-$ ./crc32hash "Dual Speed"
+$ tools/pcmcia/crc32hash "Dual Speed"
diff --git a/Documentation/prctl/.gitignore b/Documentation/prctl/.gitignore
deleted file mode 100644 (file)
index 0b5c274..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-disable-tsc-ctxt-sw-stress-test
-disable-tsc-on-off-stress-test
-disable-tsc-test
diff --git a/Documentation/prctl/Makefile b/Documentation/prctl/Makefile
deleted file mode 100644 (file)
index 44de308..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-ifndef CROSS_COMPILE
-# List of programs to build
-hostprogs-$(CONFIG_X86) := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test disable-tsc-test
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_disable-tsc-ctxt-sw-stress-test.o += -I$(objtree)/usr/include
-HOSTCFLAGS_disable-tsc-on-off-stress-test.o += -I$(objtree)/usr/include
-HOSTCFLAGS_disable-tsc-test.o += -I$(objtree)/usr/include
-endif
diff --git a/Documentation/prctl/disable-tsc-ctxt-sw-stress-test.c b/Documentation/prctl/disable-tsc-ctxt-sw-stress-test.c
deleted file mode 100644 (file)
index f7499d1..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
- *
- * Tests if the control register is updated correctly
- * at context switches
- *
- * Warning: this test will cause a very high load for a few seconds
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <wait.h>
-
-
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-
-/* Get/set the process' ability to use the timestamp counter instruction */
-#ifndef PR_GET_TSC
-#define PR_GET_TSC 25
-#define PR_SET_TSC 26
-# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
-# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
-#endif
-
-static uint64_t rdtsc(void)
-{
-uint32_t lo, hi;
-/* We cannot use "=A", since this would use %rax on x86_64 */
-__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
-return (uint64_t)hi << 32 | lo;
-}
-
-static void sigsegv_expect(int sig)
-{
-       /* */
-}
-
-static void segvtask(void)
-{
-       if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
-       {
-               perror("prctl");
-               exit(0);
-       }
-       signal(SIGSEGV, sigsegv_expect);
-       alarm(10);
-       rdtsc();
-       fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
-       exit(0);
-}
-
-
-static void sigsegv_fail(int sig)
-{
-       fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
-       exit(0);
-}
-
-static void rdtsctask(void)
-{
-       if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
-       {
-               perror("prctl");
-               exit(0);
-       }
-       signal(SIGSEGV, sigsegv_fail);
-       alarm(10);
-       for(;;) rdtsc();
-}
-
-
-int main(void)
-{
-       int n_tasks = 100, i;
-
-       fprintf(stderr, "[No further output means we're allright]\n");
-
-       for (i=0; i<n_tasks; i++)
-               if (fork() == 0)
-               {
-                       if (i & 1)
-                               segvtask();
-                       else
-                               rdtsctask();
-               }
-
-       for (i=0; i<n_tasks; i++)
-               wait(NULL);
-
-       exit(0);
-}
-
diff --git a/Documentation/prctl/disable-tsc-on-off-stress-test.c b/Documentation/prctl/disable-tsc-on-off-stress-test.c
deleted file mode 100644 (file)
index a06f027..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
- *
- * Tests if the control register is updated correctly
- * when set with prctl()
- *
- * Warning: this test will cause a very high load for a few seconds
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-#include <wait.h>
-
-
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-
-/* Get/set the process' ability to use the timestamp counter instruction */
-#ifndef PR_GET_TSC
-#define PR_GET_TSC 25
-#define PR_SET_TSC 26
-# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
-# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
-#endif
-
-/* snippet from wikipedia :-) */
-
-static uint64_t rdtsc(void)
-{
-uint32_t lo, hi;
-/* We cannot use "=A", since this would use %rax on x86_64 */
-__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
-return (uint64_t)hi << 32 | lo;
-}
-
-int should_segv = 0;
-
-static void sigsegv_cb(int sig)
-{
-       if (!should_segv)
-       {
-               fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
-               exit(0);
-       }
-       if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
-       {
-               perror("prctl");
-               exit(0);
-       }
-       should_segv = 0;
-
-       rdtsc();
-}
-
-static void task(void)
-{
-       signal(SIGSEGV, sigsegv_cb);
-       alarm(10);
-       for(;;)
-       {
-               rdtsc();
-               if (should_segv)
-               {
-                       fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
-                       exit(0);
-               }
-               if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
-               {
-                       perror("prctl");
-                       exit(0);
-               }
-               should_segv = 1;
-       }
-}
-
-
-int main(void)
-{
-       int n_tasks = 100, i;
-
-       fprintf(stderr, "[No further output means we're allright]\n");
-
-       for (i=0; i<n_tasks; i++)
-               if (fork() == 0)
-                       task();
-
-       for (i=0; i<n_tasks; i++)
-               wait(NULL);
-
-       exit(0);
-}
-
diff --git a/Documentation/prctl/disable-tsc-test.c b/Documentation/prctl/disable-tsc-test.c
deleted file mode 100644 (file)
index 8d494f7..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
- *
- * Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <inttypes.h>
-
-
-#include <sys/prctl.h>
-#include <linux/prctl.h>
-
-/* Get/set the process' ability to use the timestamp counter instruction */
-#ifndef PR_GET_TSC
-#define PR_GET_TSC 25
-#define PR_SET_TSC 26
-# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
-# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
-#endif
-
-const char *tsc_names[] =
-{
-       [0] = "[not set]",
-       [PR_TSC_ENABLE] = "PR_TSC_ENABLE",
-       [PR_TSC_SIGSEGV] = "PR_TSC_SIGSEGV",
-};
-
-static uint64_t rdtsc(void)
-{
-uint32_t lo, hi;
-/* We cannot use "=A", since this would use %rax on x86_64 */
-__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
-return (uint64_t)hi << 32 | lo;
-}
-
-static void sigsegv_cb(int sig)
-{
-       int tsc_val = 0;
-
-       printf("[ SIG_SEGV ]\n");
-       printf("prctl(PR_GET_TSC, &tsc_val); ");
-       fflush(stdout);
-
-       if ( prctl(PR_GET_TSC, &tsc_val) == -1)
-               perror("prctl");
-
-       printf("tsc_val == %s\n", tsc_names[tsc_val]);
-       printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
-       fflush(stdout);
-       if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
-               perror("prctl");
-
-       printf("rdtsc() == ");
-}
-
-int main(void)
-{
-       int tsc_val = 0;
-
-       signal(SIGSEGV, sigsegv_cb);
-
-       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
-       printf("prctl(PR_GET_TSC, &tsc_val); ");
-       fflush(stdout);
-
-       if ( prctl(PR_GET_TSC, &tsc_val) == -1)
-               perror("prctl");
-
-       printf("tsc_val == %s\n", tsc_names[tsc_val]);
-       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
-       printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
-       fflush(stdout);
-
-       if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
-               perror("prctl");
-
-       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
-       printf("prctl(PR_SET_TSC, PR_TSC_SIGSEGV)\n");
-       fflush(stdout);
-
-       if ( prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1)
-               perror("prctl");
-
-       printf("rdtsc() == ");
-       fflush(stdout);
-       printf("%llu\n", (unsigned long long)rdtsc());
-       fflush(stdout);
-
-       exit(EXIT_SUCCESS);
-}
-
diff --git a/Documentation/ptp/.gitignore b/Documentation/ptp/.gitignore
deleted file mode 100644 (file)
index f562e49..0000000
+++ /dev/null
@@ -1 +0,0 @@
-testptp
diff --git a/Documentation/ptp/Makefile b/Documentation/ptp/Makefile
deleted file mode 100644 (file)
index 293d6c0..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-# List of programs to build
-hostprogs-y := testptp
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS_testptp.o += -I$(objtree)/usr/include
-HOSTLOADLIBES_testptp := -lrt
diff --git a/Documentation/ptp/testptp.c b/Documentation/ptp/testptp.c
deleted file mode 100644 (file)
index 5d2eae1..0000000
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * PTP 1588 clock support - User space test program
- *
- * Copyright (C) 2010 OMICRON electronics GmbH
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-#define _GNU_SOURCE
-#define __SANE_USERSPACE_TYPES__        /* For PPC64, to get LL64 types */
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <math.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/timex.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <linux/ptp_clock.h>
-
-#define DEVICE "/dev/ptp0"
-
-#ifndef ADJ_SETOFFSET
-#define ADJ_SETOFFSET 0x0100
-#endif
-
-#ifndef CLOCK_INVALID
-#define CLOCK_INVALID -1
-#endif
-
-/* clock_adjtime is not available in GLIBC < 2.14 */
-#if !__GLIBC_PREREQ(2, 14)
-#include <sys/syscall.h>
-static int clock_adjtime(clockid_t id, struct timex *tx)
-{
-       return syscall(__NR_clock_adjtime, id, tx);
-}
-#endif
-
-static clockid_t get_clockid(int fd)
-{
-#define CLOCKFD 3
-#define FD_TO_CLOCKID(fd)      ((~(clockid_t) (fd) << 3) | CLOCKFD)
-
-       return FD_TO_CLOCKID(fd);
-}
-
-static void handle_alarm(int s)
-{
-       printf("received signal %d\n", s);
-}
-
-static int install_handler(int signum, void (*handler)(int))
-{
-       struct sigaction action;
-       sigset_t mask;
-
-       /* Unblock the signal. */
-       sigemptyset(&mask);
-       sigaddset(&mask, signum);
-       sigprocmask(SIG_UNBLOCK, &mask, NULL);
-
-       /* Install the signal handler. */
-       action.sa_handler = handler;
-       action.sa_flags = 0;
-       sigemptyset(&action.sa_mask);
-       sigaction(signum, &action, NULL);
-
-       return 0;
-}
-
-static long ppb_to_scaled_ppm(int ppb)
-{
-       /*
-        * The 'freq' field in the 'struct timex' is in parts per
-        * million, but with a 16 bit binary fractional field.
-        * Instead of calculating either one of
-        *
-        *    scaled_ppm = (ppb / 1000) << 16  [1]
-        *    scaled_ppm = (ppb << 16) / 1000  [2]
-        *
-        * we simply use double precision math, in order to avoid the
-        * truncation in [1] and the possible overflow in [2].
-        */
-       return (long) (ppb * 65.536);
-}
-
-static int64_t pctns(struct ptp_clock_time *t)
-{
-       return t->sec * 1000000000LL + t->nsec;
-}
-
-static void usage(char *progname)
-{
-       fprintf(stderr,
-               "usage: %s [options]\n"
-               " -a val     request a one-shot alarm after 'val' seconds\n"
-               " -A val     request a periodic alarm every 'val' seconds\n"
-               " -c         query the ptp clock's capabilities\n"
-               " -d name    device to open\n"
-               " -e val     read 'val' external time stamp events\n"
-               " -f val     adjust the ptp clock frequency by 'val' ppb\n"
-               " -g         get the ptp clock time\n"
-               " -h         prints this message\n"
-               " -i val     index for event/trigger\n"
-               " -k val     measure the time offset between system and phc clock\n"
-               "            for 'val' times (Maximum 25)\n"
-               " -l         list the current pin configuration\n"
-               " -L pin,val configure pin index 'pin' with function 'val'\n"
-               "            the channel index is taken from the '-i' option\n"
-               "            'val' specifies the auxiliary function:\n"
-               "            0 - none\n"
-               "            1 - external time stamp\n"
-               "            2 - periodic output\n"
-               " -p val     enable output with a period of 'val' nanoseconds\n"
-               " -P val     enable or disable (val=1|0) the system clock PPS\n"
-               " -s         set the ptp clock time from the system time\n"
-               " -S         set the system time from the ptp clock time\n"
-               " -t val     shift the ptp clock time by 'val' seconds\n"
-               " -T val     set the ptp clock time to 'val' seconds\n",
-               progname);
-}
-
-int main(int argc, char *argv[])
-{
-       struct ptp_clock_caps caps;
-       struct ptp_extts_event event;
-       struct ptp_extts_request extts_request;
-       struct ptp_perout_request perout_request;
-       struct ptp_pin_desc desc;
-       struct timespec ts;
-       struct timex tx;
-
-       static timer_t timerid;
-       struct itimerspec timeout;
-       struct sigevent sigevent;
-
-       struct ptp_clock_time *pct;
-       struct ptp_sys_offset *sysoff;
-
-
-       char *progname;
-       unsigned int i;
-       int c, cnt, fd;
-
-       char *device = DEVICE;
-       clockid_t clkid;
-       int adjfreq = 0x7fffffff;
-       int adjtime = 0;
-       int capabilities = 0;
-       int extts = 0;
-       int gettime = 0;
-       int index = 0;
-       int list_pins = 0;
-       int oneshot = 0;
-       int pct_offset = 0;
-       int n_samples = 0;
-       int periodic = 0;
-       int perout = -1;
-       int pin_index = -1, pin_func;
-       int pps = -1;
-       int seconds = 0;
-       int settime = 0;
-
-       int64_t t1, t2, tp;
-       int64_t interval, offset;
-
-       progname = strrchr(argv[0], '/');
-       progname = progname ? 1+progname : argv[0];
-       while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
-               switch (c) {
-               case 'a':
-                       oneshot = atoi(optarg);
-                       break;
-               case 'A':
-                       periodic = atoi(optarg);
-                       break;
-               case 'c':
-                       capabilities = 1;
-                       break;
-               case 'd':
-                       device = optarg;
-                       break;
-               case 'e':
-                       extts = atoi(optarg);
-                       break;
-               case 'f':
-                       adjfreq = atoi(optarg);
-                       break;
-               case 'g':
-                       gettime = 1;
-                       break;
-               case 'i':
-                       index = atoi(optarg);
-                       break;
-               case 'k':
-                       pct_offset = 1;
-                       n_samples = atoi(optarg);
-                       break;
-               case 'l':
-                       list_pins = 1;
-                       break;
-               case 'L':
-                       cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
-                       if (cnt != 2) {
-                               usage(progname);
-                               return -1;
-                       }
-                       break;
-               case 'p':
-                       perout = atoi(optarg);
-                       break;
-               case 'P':
-                       pps = atoi(optarg);
-                       break;
-               case 's':
-                       settime = 1;
-                       break;
-               case 'S':
-                       settime = 2;
-                       break;
-               case 't':
-                       adjtime = atoi(optarg);
-                       break;
-               case 'T':
-                       settime = 3;
-                       seconds = atoi(optarg);
-                       break;
-               case 'h':
-                       usage(progname);
-                       return 0;
-               case '?':
-               default:
-                       usage(progname);
-                       return -1;
-               }
-       }
-
-       fd = open(device, O_RDWR);
-       if (fd < 0) {
-               fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
-               return -1;
-       }
-
-       clkid = get_clockid(fd);
-       if (CLOCK_INVALID == clkid) {
-               fprintf(stderr, "failed to read clock id\n");
-               return -1;
-       }
-
-       if (capabilities) {
-               if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
-                       perror("PTP_CLOCK_GETCAPS");
-               } else {
-                       printf("capabilities:\n"
-                              "  %d maximum frequency adjustment (ppb)\n"
-                              "  %d programmable alarms\n"
-                              "  %d external time stamp channels\n"
-                              "  %d programmable periodic signals\n"
-                              "  %d pulse per second\n"
-                              "  %d programmable pins\n"
-                              "  %d cross timestamping\n",
-                              caps.max_adj,
-                              caps.n_alarm,
-                              caps.n_ext_ts,
-                              caps.n_per_out,
-                              caps.pps,
-                              caps.n_pins,
-                              caps.cross_timestamping);
-               }
-       }
-
-       if (0x7fffffff != adjfreq) {
-               memset(&tx, 0, sizeof(tx));
-               tx.modes = ADJ_FREQUENCY;
-               tx.freq = ppb_to_scaled_ppm(adjfreq);
-               if (clock_adjtime(clkid, &tx)) {
-                       perror("clock_adjtime");
-               } else {
-                       puts("frequency adjustment okay");
-               }
-       }
-
-       if (adjtime) {
-               memset(&tx, 0, sizeof(tx));
-               tx.modes = ADJ_SETOFFSET;
-               tx.time.tv_sec = adjtime;
-               tx.time.tv_usec = 0;
-               if (clock_adjtime(clkid, &tx) < 0) {
-                       perror("clock_adjtime");
-               } else {
-                       puts("time shift okay");
-               }
-       }
-
-       if (gettime) {
-               if (clock_gettime(clkid, &ts)) {
-                       perror("clock_gettime");
-               } else {
-                       printf("clock time: %ld.%09ld or %s",
-                              ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
-               }
-       }
-
-       if (settime == 1) {
-               clock_gettime(CLOCK_REALTIME, &ts);
-               if (clock_settime(clkid, &ts)) {
-                       perror("clock_settime");
-               } else {
-                       puts("set time okay");
-               }
-       }
-
-       if (settime == 2) {
-               clock_gettime(clkid, &ts);
-               if (clock_settime(CLOCK_REALTIME, &ts)) {
-                       perror("clock_settime");
-               } else {
-                       puts("set time okay");
-               }
-       }
-
-       if (settime == 3) {
-               ts.tv_sec = seconds;
-               ts.tv_nsec = 0;
-               if (clock_settime(clkid, &ts)) {
-                       perror("clock_settime");
-               } else {
-                       puts("set time okay");
-               }
-       }
-
-       if (extts) {
-               memset(&extts_request, 0, sizeof(extts_request));
-               extts_request.index = index;
-               extts_request.flags = PTP_ENABLE_FEATURE;
-               if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
-                       perror("PTP_EXTTS_REQUEST");
-                       extts = 0;
-               } else {
-                       puts("external time stamp request okay");
-               }
-               for (; extts; extts--) {
-                       cnt = read(fd, &event, sizeof(event));
-                       if (cnt != sizeof(event)) {
-                               perror("read");
-                               break;
-                       }
-                       printf("event index %u at %lld.%09u\n", event.index,
-                              event.t.sec, event.t.nsec);
-                       fflush(stdout);
-               }
-               /* Disable the feature again. */
-               extts_request.flags = 0;
-               if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
-                       perror("PTP_EXTTS_REQUEST");
-               }
-       }
-
-       if (list_pins) {
-               int n_pins = 0;
-               if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
-                       perror("PTP_CLOCK_GETCAPS");
-               } else {
-                       n_pins = caps.n_pins;
-               }
-               for (i = 0; i < n_pins; i++) {
-                       desc.index = i;
-                       if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
-                               perror("PTP_PIN_GETFUNC");
-                               break;
-                       }
-                       printf("name %s index %u func %u chan %u\n",
-                              desc.name, desc.index, desc.func, desc.chan);
-               }
-       }
-
-       if (oneshot) {
-               install_handler(SIGALRM, handle_alarm);
-               /* Create a timer. */
-               sigevent.sigev_notify = SIGEV_SIGNAL;
-               sigevent.sigev_signo = SIGALRM;
-               if (timer_create(clkid, &sigevent, &timerid)) {
-                       perror("timer_create");
-                       return -1;
-               }
-               /* Start the timer. */
-               memset(&timeout, 0, sizeof(timeout));
-               timeout.it_value.tv_sec = oneshot;
-               if (timer_settime(timerid, 0, &timeout, NULL)) {
-                       perror("timer_settime");
-                       return -1;
-               }
-               pause();
-               timer_delete(timerid);
-       }
-
-       if (periodic) {
-               install_handler(SIGALRM, handle_alarm);
-               /* Create a timer. */
-               sigevent.sigev_notify = SIGEV_SIGNAL;
-               sigevent.sigev_signo = SIGALRM;
-               if (timer_create(clkid, &sigevent, &timerid)) {
-                       perror("timer_create");
-                       return -1;
-               }
-               /* Start the timer. */
-               memset(&timeout, 0, sizeof(timeout));
-               timeout.it_interval.tv_sec = periodic;
-               timeout.it_value.tv_sec = periodic;
-               if (timer_settime(timerid, 0, &timeout, NULL)) {
-                       perror("timer_settime");
-                       return -1;
-               }
-               while (1) {
-                       pause();
-               }
-               timer_delete(timerid);
-       }
-
-       if (perout >= 0) {
-               if (clock_gettime(clkid, &ts)) {
-                       perror("clock_gettime");
-                       return -1;
-               }
-               memset(&perout_request, 0, sizeof(perout_request));
-               perout_request.index = index;
-               perout_request.start.sec = ts.tv_sec + 2;
-               perout_request.start.nsec = 0;
-               perout_request.period.sec = 0;
-               perout_request.period.nsec = perout;
-               if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
-                       perror("PTP_PEROUT_REQUEST");
-               } else {
-                       puts("periodic output request okay");
-               }
-       }
-
-       if (pin_index >= 0) {
-               memset(&desc, 0, sizeof(desc));
-               desc.index = pin_index;
-               desc.func = pin_func;
-               desc.chan = index;
-               if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
-                       perror("PTP_PIN_SETFUNC");
-               } else {
-                       puts("set pin function okay");
-               }
-       }
-
-       if (pps != -1) {
-               int enable = pps ? 1 : 0;
-               if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
-                       perror("PTP_ENABLE_PPS");
-               } else {
-                       puts("pps for system time request okay");
-               }
-       }
-
-       if (pct_offset) {
-               if (n_samples <= 0 || n_samples > 25) {
-                       puts("n_samples should be between 1 and 25");
-                       usage(progname);
-                       return -1;
-               }
-
-               sysoff = calloc(1, sizeof(*sysoff));
-               if (!sysoff) {
-                       perror("calloc");
-                       return -1;
-               }
-               sysoff->n_samples = n_samples;
-
-               if (ioctl(fd, PTP_SYS_OFFSET, sysoff))
-                       perror("PTP_SYS_OFFSET");
-               else
-                       puts("system and phc clock time offset request okay");
-
-               pct = &sysoff->ts[0];
-               for (i = 0; i < sysoff->n_samples; i++) {
-                       t1 = pctns(pct+2*i);
-                       tp = pctns(pct+2*i+1);
-                       t2 = pctns(pct+2*i+2);
-                       interval = t2 - t1;
-                       offset = (t2 + t1) / 2 - tp;
-
-                       printf("system time: %lld.%u\n",
-                               (pct+2*i)->sec, (pct+2*i)->nsec);
-                       printf("phc    time: %lld.%u\n",
-                               (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
-                       printf("system time: %lld.%u\n",
-                               (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
-                       printf("system/phc clock time offset is %" PRId64 " ns\n"
-                              "system     clock time delay  is %" PRId64 " ns\n",
-                               offset, interval);
-               }
-
-               free(sysoff);
-       }
-
-       close(fd);
-       return 0;
-}
diff --git a/Documentation/ptp/testptp.mk b/Documentation/ptp/testptp.mk
deleted file mode 100644 (file)
index 4ef2d97..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-# PTP 1588 clock support - User space test program
-#
-# Copyright (C) 2010 OMICRON electronics GmbH
-#
-#  This program is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-#
-#  This program is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-#
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-CC        = $(CROSS_COMPILE)gcc
-INC       = -I$(KBUILD_OUTPUT)/usr/include
-CFLAGS    = -Wall $(INC)
-LDLIBS    = -lrt
-PROGS     = testptp
-
-all: $(PROGS)
-
-testptp: testptp.o
-
-clean:
-       rm -f testptp.o
-
-distclean: clean
-       rm -f $(PROGS)
index fd88015..e2c1879 100644 (file)
@@ -21,16 +21,6 @@ NCR53c400 card, the Trantor T130B in its default configuration:
 The NCR53c400 does not support DMA but it does have Pseudo-DMA which is
 supported by the driver.
 
-If the default configuration does not work for you, you can use the kernel
-command lines (eg using the lilo append command):
-       ncr5380=addr,irq
-       ncr53c400=addr,irq
-       ncr53c400a=addr,irq
-       dtc3181e=addr,irq
-
-The driver does not probe for any addresses or ports other than those in
-the OVERRIDE or given to the kernel as above.
-
 This driver provides some information on what it has detected in
 /proc/scsi/g_NCR5380/x where x is the scsi card number as detected at boot
 time. More info to come in the future.
@@ -38,6 +28,16 @@ time. More info to come in the future.
 This driver works as a module.
 When included as a module, parameters can be passed on the insmod/modprobe
 command line:
+  irq=xx[,...] the interrupt(s)
+  base=xx[,...]        the port or base address(es) (for port or memory mapped, resp.)
+  card=xx[,...]        card type(s):
+               0 = NCR5380,
+               1 = NCR53C400,
+               2 = NCR53C400A,
+               3 = Domex Technology Corp 3181E (DTC3181E)
+               4 = Hewlett Packard C2502
+
+These old-style parameters can support only one card:
   ncr_irq=xx   the interrupt
   ncr_addr=xx  the port or base address (for port or memory
                mapped, resp.)
@@ -46,11 +46,19 @@ command line:
   ncr_53c400a=1 to set up for a NCR53C400A board
   dtc_3181e=1  to set up for a Domex Technology Corp 3181E board
   hp_c2502=1   to set up for a Hewlett Packard C2502 board
+
 e.g.
-modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+OLD: modprobe g_NCR5380 ncr_irq=5 ncr_addr=0x350 ncr_5380=1
+NEW: modprobe g_NCR5380 irq=5 base=0x350 card=0
   for a port mapped NCR5380 board or
-modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
-  for a memory mapped NCR53C400 board with interrupts disabled.
+
+OLD: modprobe g_NCR5380 ncr_irq=255 ncr_addr=0xc8000 ncr_53c400=1
+NEW: modprobe g_NCR5380 irq=255 base=0xc8000 card=1
+  for a memory mapped NCR53C400 board with interrupts disabled or
+
+NEW: modprobe g_NCR5380 irq=0,7 base=0x240,0x300 card=3,4
+  for two cards: DTC3181 (in non-PnP mode) at 0x240 with no IRQ
+             and HP C2502 at 0x300 with IRQ 7
 
 (255 should be specified for no or DMA interrupt, 254 to autoprobe for an 
      IRQ line if overridden on the command line.)
index 4644bf0..8e4bb17 100644 (file)
@@ -1,7 +1,5 @@
 00-INDEX
        - this file.
-Makefile
-       - Makefile for the example sourcefiles.
 butterfly
        - AVR Butterfly SPI driver overview and pin configuration.
 ep93xx_spi
index e8e2eba..b63a685 100644 (file)
@@ -64,6 +64,20 @@ The sync_file fd now can be sent to userspace.
 If the creation process fail, or the sync_file needs to be released by any
 other reason fput(sync_file->file) should be used.
 
+Receiving Sync Files from Userspace
+-----------------------------------
+
+When userspace needs to send an in-fence to the driver it passes file descriptor
+of the Sync File to the kernel. The kernel can then retrieve the fences
+from it.
+
+Interface:
+       struct fence *sync_file_get_fence(int fd);
+
+
+The returned reference is owned by the caller and must be disposed of
+afterwards using fence_put(). In case of error, a NULL is returned instead.
+
 References:
 [1] struct sync_file in include/linux/sync_file.h
 [2] All interfaces mentioned above defined in include/linux/sync_file.h
index efc3f3d..ef473dc 100644 (file)
@@ -49,6 +49,9 @@ temperature) and throttle appropriate devices.
        .bind: bind the thermal zone device with a thermal cooling device.
        .unbind: unbind the thermal zone device with a thermal cooling device.
        .get_temp: get the current temperature of the thermal zone.
+       .set_trips: set the trip points window. Whenever the current temperature
+                   is updated, the trip points immediately below and above the
+                   current temperature are found.
        .get_mode: get the current mode (enabled/disabled) of the thermal zone.
            - "enabled" means the kernel thermal management is enabled.
            - "disabled" will prevent kernel thermal driver action upon trip points
@@ -95,6 +98,10 @@ temperature) and throttle appropriate devices.
                        get_temp:       a pointer to a function that reads the
                                        sensor temperature. This is mandatory
                                        callback provided by sensor driver.
+                       set_trips:      a pointer to a function that sets a
+                                       temperature window. When this window is
+                                       left the driver must inform the thermal
+                                       core via thermal_zone_device_update.
                        get_trend:      a pointer to a function that reads the
                                        sensor temperature trend.
                        set_emul_temp:  a pointer to a function that sets
@@ -140,6 +147,18 @@ temperature) and throttle appropriate devices.
        Normally this function will not need to be called and the resource
        management code will ensure that the resource is freed.
 
+1.1.7 int thermal_zone_get_slope(struct thermal_zone_device *tz)
+
+       This interface is used to read the slope attribute value
+       for the thermal zone device, which might be useful for platform
+       drivers for temperature calculations.
+
+1.1.8 int thermal_zone_get_offset(struct thermal_zone_device *tz)
+
+       This interface is used to read the offset attribute value
+       for the thermal zone device, which might be useful for platform
+       drivers for temperature calculations.
+
 1.2 thermal cooling device interface
 1.2.1 struct thermal_cooling_device *thermal_cooling_device_register(char *name,
                void *devdata, struct thermal_cooling_device_ops *)
diff --git a/Documentation/timers/.gitignore b/Documentation/timers/.gitignore
deleted file mode 100644 (file)
index c5c45d7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-hpet_example
index ee212a2..3be05fe 100644 (file)
@@ -4,12 +4,8 @@ highres.txt
        - High resolution timers and dynamic ticks design notes
 hpet.txt
        - High Precision Event Timer Driver for Linux
-hpet_example.c
-       - sample hpet timer test program
 hrtimers.txt
        - subsystem for high-resolution kernel timers
-Makefile
-       - Build and link hpet_example
 NO_HZ.txt
        - Summary of the different methods for the scheduler clock-interrupts management.
 timekeeping.txt
diff --git a/Documentation/timers/Makefile b/Documentation/timers/Makefile
deleted file mode 100644 (file)
index 6c09ee6..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-$(CONFIG_X86) := hpet_example
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
index a484d2c..895345e 100644 (file)
@@ -25,4 +25,4 @@ arch/x86/kernel/hpet.c.
 
 The driver provides a userspace API which resembles the API found in the
 RTC driver framework.  An example user space program is provided in
-file:Documentation/timers/hpet_example.c
+file:samples/timers/hpet_example.c
diff --git a/Documentation/timers/hpet_example.c b/Documentation/timers/hpet_example.c
deleted file mode 100644 (file)
index 3ab4993..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <memory.h>
-#include <malloc.h>
-#include <time.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <linux/hpet.h>
-
-
-extern void hpet_open_close(int, const char **);
-extern void hpet_info(int, const char **);
-extern void hpet_poll(int, const char **);
-extern void hpet_fasync(int, const char **);
-extern void hpet_read(int, const char **);
-
-#include <sys/poll.h>
-#include <sys/ioctl.h>
-
-struct hpet_command {
-       char            *command;
-       void            (*func)(int argc, const char ** argv);
-} hpet_command[] = {
-       {
-               "open-close",
-               hpet_open_close
-       },
-       {
-               "info",
-               hpet_info
-       },
-       {
-               "poll",
-               hpet_poll
-       },
-       {
-               "fasync",
-               hpet_fasync
-       },
-};
-
-int
-main(int argc, const char ** argv)
-{
-       unsigned int    i;
-
-       argc--;
-       argv++;
-
-       if (!argc) {
-               fprintf(stderr, "-hpet: requires command\n");
-               return -1;
-       }
-
-
-       for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++)
-               if (!strcmp(argv[0], hpet_command[i].command)) {
-                       argc--;
-                       argv++;
-                       fprintf(stderr, "-hpet: executing %s\n",
-                               hpet_command[i].command);
-                       hpet_command[i].func(argc, argv);
-                       return 0;
-               }
-
-       fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]);
-
-       return -1;
-}
-
-void
-hpet_open_close(int argc, const char **argv)
-{
-       int     fd;
-
-       if (argc != 1) {
-               fprintf(stderr, "hpet_open_close: device-name\n");
-               return;
-       }
-
-       fd = open(argv[0], O_RDONLY);
-       if (fd < 0)
-               fprintf(stderr, "hpet_open_close: open failed\n");
-       else
-               close(fd);
-
-       return;
-}
-
-void
-hpet_info(int argc, const char **argv)
-{
-       struct hpet_info        info;
-       int                     fd;
-
-       if (argc != 1) {
-               fprintf(stderr, "hpet_info: device-name\n");
-               return;
-       }
-
-       fd = open(argv[0], O_RDONLY);
-       if (fd < 0) {
-               fprintf(stderr, "hpet_info: open of %s failed\n", argv[0]);
-               return;
-       }
-
-       if (ioctl(fd, HPET_INFO, &info) < 0) {
-               fprintf(stderr, "hpet_info: failed to get info\n");
-               goto out;
-       }
-
-       fprintf(stderr, "hpet_info: hi_irqfreq 0x%lx hi_flags 0x%lx ",
-               info.hi_ireqfreq, info.hi_flags);
-       fprintf(stderr, "hi_hpet %d hi_timer %d\n",
-               info.hi_hpet, info.hi_timer);
-
-out:
-       close(fd);
-       return;
-}
-
-void
-hpet_poll(int argc, const char **argv)
-{
-       unsigned long           freq;
-       int                     iterations, i, fd;
-       struct pollfd           pfd;
-       struct hpet_info        info;
-       struct timeval          stv, etv;
-       struct timezone         tz;
-       long                    usec;
-
-       if (argc != 3) {
-               fprintf(stderr, "hpet_poll: device-name freq iterations\n");
-               return;
-       }
-
-       freq = atoi(argv[1]);
-       iterations = atoi(argv[2]);
-
-       fd = open(argv[0], O_RDONLY);
-
-       if (fd < 0) {
-               fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]);
-               return;
-       }
-
-       if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
-               fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n");
-               goto out;
-       }
-
-       if (ioctl(fd, HPET_INFO, &info) < 0) {
-               fprintf(stderr, "hpet_poll: failed to get info\n");
-               goto out;
-       }
-
-       fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags);
-
-       if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
-               fprintf(stderr, "hpet_poll: HPET_EPI failed\n");
-               goto out;
-       }
-
-       if (ioctl(fd, HPET_IE_ON, 0) < 0) {
-               fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n");
-               goto out;
-       }
-
-       pfd.fd = fd;
-       pfd.events = POLLIN;
-
-       for (i = 0; i < iterations; i++) {
-               pfd.revents = 0;
-               gettimeofday(&stv, &tz);
-               if (poll(&pfd, 1, -1) < 0)
-                       fprintf(stderr, "hpet_poll: poll failed\n");
-               else {
-                       long    data;
-
-                       gettimeofday(&etv, &tz);
-                       usec = stv.tv_sec * 1000000 + stv.tv_usec;
-                       usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec;
-
-                       fprintf(stderr,
-                               "hpet_poll: expired time = 0x%lx\n", usec);
-
-                       fprintf(stderr, "hpet_poll: revents = 0x%x\n",
-                               pfd.revents);
-
-                       if (read(fd, &data, sizeof(data)) != sizeof(data)) {
-                               fprintf(stderr, "hpet_poll: read failed\n");
-                       }
-                       else
-                               fprintf(stderr, "hpet_poll: data 0x%lx\n",
-                                       data);
-               }
-       }
-
-out:
-       close(fd);
-       return;
-}
-
-static int hpet_sigio_count;
-
-static void
-hpet_sigio(int val)
-{
-       fprintf(stderr, "hpet_sigio: called\n");
-       hpet_sigio_count++;
-}
-
-void
-hpet_fasync(int argc, const char **argv)
-{
-       unsigned long           freq;
-       int                     iterations, i, fd, value;
-       sig_t                   oldsig;
-       struct hpet_info        info;
-
-       hpet_sigio_count = 0;
-       fd = -1;
-
-       if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) {
-               fprintf(stderr, "hpet_fasync: failed to set signal handler\n");
-               return;
-       }
-
-       if (argc != 3) {
-               fprintf(stderr, "hpet_fasync: device-name freq iterations\n");
-               goto out;
-       }
-
-       fd = open(argv[0], O_RDONLY);
-
-       if (fd < 0) {
-               fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]);
-               return;
-       }
-
-
-       if ((fcntl(fd, F_SETOWN, getpid()) == 1) ||
-               ((value = fcntl(fd, F_GETFL)) == 1) ||
-               (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) {
-               fprintf(stderr, "hpet_fasync: fcntl failed\n");
-               goto out;
-       }
-
-       freq = atoi(argv[1]);
-       iterations = atoi(argv[2]);
-
-       if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
-               fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n");
-               goto out;
-       }
-
-       if (ioctl(fd, HPET_INFO, &info) < 0) {
-               fprintf(stderr, "hpet_fasync: failed to get info\n");
-               goto out;
-       }
-
-       fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags);
-
-       if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
-               fprintf(stderr, "hpet_fasync: HPET_EPI failed\n");
-               goto out;
-       }
-
-       if (ioctl(fd, HPET_IE_ON, 0) < 0) {
-               fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n");
-               goto out;
-       }
-
-       for (i = 0; i < iterations; i++) {
-               (void) pause();
-               fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count);
-       }
-
-out:
-       signal(SIGIO, oldsig);
-
-       if (fd >= 0)
-               close(fd);
-
-       return;
-}
diff --git a/Documentation/vDSO/.gitignore b/Documentation/vDSO/.gitignore
deleted file mode 100644 (file)
index 133bf9e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-vdso_test
-vdso_standalone_test_x86
diff --git a/Documentation/vDSO/Makefile b/Documentation/vDSO/Makefile
deleted file mode 100644 (file)
index b12e987..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-ifndef CROSS_COMPILE
-# vdso_test won't build for glibc < 2.16, so disable it
-# hostprogs-y := vdso_test
-hostprogs-$(CONFIG_X86) := vdso_standalone_test_x86
-vdso_standalone_test_x86-objs := vdso_standalone_test_x86.o parse_vdso.o
-vdso_test-objs := parse_vdso.o vdso_test.o
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
-
-HOSTCFLAGS := -I$(objtree)/usr/include -std=gnu99
-HOSTCFLAGS_vdso_standalone_test_x86.o := -fno-asynchronous-unwind-tables -fno-stack-protector
-HOSTLOADLIBES_vdso_standalone_test_x86 := -nostdlib
-ifeq ($(CONFIG_X86_32),y)
-HOSTLOADLIBES_vdso_standalone_test_x86 += -lgcc_s
-endif
-endif
diff --git a/Documentation/vDSO/parse_vdso.c b/Documentation/vDSO/parse_vdso.c
deleted file mode 100644 (file)
index 1dbb4b8..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * parse_vdso.c: Linux reference vDSO parser
- * Written by Andrew Lutomirski, 2011-2014.
- *
- * This code is meant to be linked in to various programs that run on Linux.
- * As such, it is available with as few restrictions as possible.  This file
- * is licensed under the Creative Commons Zero License, version 1.0,
- * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode
- *
- * The vDSO is a regular ELF DSO that the kernel maps into user space when
- * it starts a program.  It works equally well in statically and dynamically
- * linked binaries.
- *
- * This code is tested on x86.  In principle it should work on any
- * architecture that has a vDSO.
- */
-
-#include <stdbool.h>
-#include <stdint.h>
-#include <string.h>
-#include <limits.h>
-#include <elf.h>
-
-/*
- * To use this vDSO parser, first call one of the vdso_init_* functions.
- * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR
- * to vdso_init_from_sysinfo_ehdr.  Otherwise pass auxv to vdso_init_from_auxv.
- * Then call vdso_sym for each symbol you want.  For example, to look up
- * gettimeofday on x86_64, use:
- *
- *     <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday");
- * or
- *     <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
- *
- * vdso_sym will return 0 if the symbol doesn't exist or if the init function
- * failed or was not called.  vdso_sym is a little slow, so its return value
- * should be cached.
- *
- * vdso_sym is threadsafe; the init functions are not.
- *
- * These are the prototypes:
- */
-extern void vdso_init_from_auxv(void *auxv);
-extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
-extern void *vdso_sym(const char *version, const char *name);
-
-
-/* And here's the code. */
-#ifndef ELF_BITS
-# if ULONG_MAX > 0xffffffffUL
-#  define ELF_BITS 64
-# else
-#  define ELF_BITS 32
-# endif
-#endif
-
-#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
-#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
-#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
-
-static struct vdso_info
-{
-       bool valid;
-
-       /* Load information */
-       uintptr_t load_addr;
-       uintptr_t load_offset;  /* load_addr - recorded vaddr */
-
-       /* Symbol table */
-       ELF(Sym) *symtab;
-       const char *symstrings;
-       ELF(Word) *bucket, *chain;
-       ELF(Word) nbucket, nchain;
-
-       /* Version table */
-       ELF(Versym) *versym;
-       ELF(Verdef) *verdef;
-} vdso_info;
-
-/* Straight from the ELF specification. */
-static unsigned long elf_hash(const unsigned char *name)
-{
-       unsigned long h = 0, g;
-       while (*name)
-       {
-               h = (h << 4) + *name++;
-               if (g = h & 0xf0000000)
-                       h ^= g >> 24;
-               h &= ~g;
-       }
-       return h;
-}
-
-void vdso_init_from_sysinfo_ehdr(uintptr_t base)
-{
-       size_t i;
-       bool found_vaddr = false;
-
-       vdso_info.valid = false;
-
-       vdso_info.load_addr = base;
-
-       ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;
-       if (hdr->e_ident[EI_CLASS] !=
-           (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) {
-               return;  /* Wrong ELF class -- check ELF_BITS */
-       }
-
-       ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff);
-       ELF(Dyn) *dyn = 0;
-
-       /*
-        * We need two things from the segment table: the load offset
-        * and the dynamic table.
-        */
-       for (i = 0; i < hdr->e_phnum; i++)
-       {
-               if (pt[i].p_type == PT_LOAD && !found_vaddr) {
-                       found_vaddr = true;
-                       vdso_info.load_offset = base
-                               + (uintptr_t)pt[i].p_offset
-                               - (uintptr_t)pt[i].p_vaddr;
-               } else if (pt[i].p_type == PT_DYNAMIC) {
-                       dyn = (ELF(Dyn)*)(base + pt[i].p_offset);
-               }
-       }
-
-       if (!found_vaddr || !dyn)
-               return;  /* Failed */
-
-       /*
-        * Fish out the useful bits of the dynamic table.
-        */
-       ELF(Word) *hash = 0;
-       vdso_info.symstrings = 0;
-       vdso_info.symtab = 0;
-       vdso_info.versym = 0;
-       vdso_info.verdef = 0;
-       for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
-               switch (dyn[i].d_tag) {
-               case DT_STRTAB:
-                       vdso_info.symstrings = (const char *)
-                               ((uintptr_t)dyn[i].d_un.d_ptr
-                                + vdso_info.load_offset);
-                       break;
-               case DT_SYMTAB:
-                       vdso_info.symtab = (ELF(Sym) *)
-                               ((uintptr_t)dyn[i].d_un.d_ptr
-                                + vdso_info.load_offset);
-                       break;
-               case DT_HASH:
-                       hash = (ELF(Word) *)
-                               ((uintptr_t)dyn[i].d_un.d_ptr
-                                + vdso_info.load_offset);
-                       break;
-               case DT_VERSYM:
-                       vdso_info.versym = (ELF(Versym) *)
-                               ((uintptr_t)dyn[i].d_un.d_ptr
-                                + vdso_info.load_offset);
-                       break;
-               case DT_VERDEF:
-                       vdso_info.verdef = (ELF(Verdef) *)
-                               ((uintptr_t)dyn[i].d_un.d_ptr
-                                + vdso_info.load_offset);
-                       break;
-               }
-       }
-       if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
-               return;  /* Failed */
-
-       if (!vdso_info.verdef)
-               vdso_info.versym = 0;
-
-       /* Parse the hash table header. */
-       vdso_info.nbucket = hash[0];
-       vdso_info.nchain = hash[1];
-       vdso_info.bucket = &hash[2];
-       vdso_info.chain = &hash[vdso_info.nbucket + 2];
-
-       /* That's all we need. */
-       vdso_info.valid = true;
-}
-
-static bool vdso_match_version(ELF(Versym) ver,
-                              const char *name, ELF(Word) hash)
-{
-       /*
-        * This is a helper function to check if the version indexed by
-        * ver matches name (which hashes to hash).
-        *
-        * The version definition table is a mess, and I don't know how
-        * to do this in better than linear time without allocating memory
-        * to build an index.  I also don't know why the table has
-        * variable size entries in the first place.
-        *
-        * For added fun, I can't find a comprehensible specification of how
-        * to parse all the weird flags in the table.
-        *
-        * So I just parse the whole table every time.
-        */
-
-       /* First step: find the version definition */
-       ver &= 0x7fff;  /* Apparently bit 15 means "hidden" */
-       ELF(Verdef) *def = vdso_info.verdef;
-       while(true) {
-               if ((def->vd_flags & VER_FLG_BASE) == 0
-                   && (def->vd_ndx & 0x7fff) == ver)
-                       break;
-
-               if (def->vd_next == 0)
-                       return false;  /* No definition. */
-
-               def = (ELF(Verdef) *)((char *)def + def->vd_next);
-       }
-
-       /* Now figure out whether it matches. */
-       ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux);
-       return def->vd_hash == hash
-               && !strcmp(name, vdso_info.symstrings + aux->vda_name);
-}
-
-void *vdso_sym(const char *version, const char *name)
-{
-       unsigned long ver_hash;
-       if (!vdso_info.valid)
-               return 0;
-
-       ver_hash = elf_hash(version);
-       ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
-
-       for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
-               ELF(Sym) *sym = &vdso_info.symtab[chain];
-
-               /* Check for a defined global or weak function w/ right name. */
-               if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
-                       continue;
-               if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
-                   ELF64_ST_BIND(sym->st_info) != STB_WEAK)
-                       continue;
-               if (sym->st_shndx == SHN_UNDEF)
-                       continue;
-               if (strcmp(name, vdso_info.symstrings + sym->st_name))
-                       continue;
-
-               /* Check symbol version. */
-               if (vdso_info.versym
-                   && !vdso_match_version(vdso_info.versym[chain],
-                                          version, ver_hash))
-                       continue;
-
-               return (void *)(vdso_info.load_offset + sym->st_value);
-       }
-
-       return 0;
-}
-
-void vdso_init_from_auxv(void *auxv)
-{
-       ELF(auxv_t) *elf_auxv = auxv;
-       for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)
-       {
-               if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
-                       vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val);
-                       return;
-               }
-       }
-
-       vdso_info.valid = false;
-}
diff --git a/Documentation/vDSO/vdso_standalone_test_x86.c b/Documentation/vDSO/vdso_standalone_test_x86.c
deleted file mode 100644 (file)
index 93b0ebf..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * vdso_test.c: Sample code to test parse_vdso.c on x86
- * Copyright (c) 2011-2014 Andy Lutomirski
- * Subject to the GNU General Public License, version 2
- *
- * You can amuse yourself by compiling with:
- * gcc -std=gnu99 -nostdlib
- *     -Os -fno-asynchronous-unwind-tables -flto -lgcc_s
- *      vdso_standalone_test_x86.c parse_vdso.c
- * to generate a small binary.  On x86_64, you can omit -lgcc_s
- * if you want the binary to be completely standalone.
- */
-
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <stdint.h>
-
-extern void *vdso_sym(const char *version, const char *name);
-extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
-extern void vdso_init_from_auxv(void *auxv);
-
-/* We need a libc functions... */
-int strcmp(const char *a, const char *b)
-{
-       /* This implementation is buggy: it never returns -1. */
-       while (*a || *b) {
-               if (*a != *b)
-                       return 1;
-               if (*a == 0 || *b == 0)
-                       return 1;
-               a++;
-               b++;
-       }
-
-       return 0;
-}
-
-/* ...and two syscalls.  This is x86-specific. */
-static inline long x86_syscall3(long nr, long a0, long a1, long a2)
-{
-       long ret;
-#ifdef __x86_64__
-       asm volatile ("syscall" : "=a" (ret) : "a" (nr),
-                     "D" (a0), "S" (a1), "d" (a2) :
-                     "cc", "memory", "rcx",
-                     "r8", "r9", "r10", "r11" );
-#else
-       asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
-                     "b" (a0), "c" (a1), "d" (a2) :
-                     "cc", "memory" );
-#endif
-       return ret;
-}
-
-static inline long linux_write(int fd, const void *data, size_t len)
-{
-       return x86_syscall3(__NR_write, fd, (long)data, (long)len);
-}
-
-static inline void linux_exit(int code)
-{
-       x86_syscall3(__NR_exit, code, 0, 0);
-}
-
-void to_base10(char *lastdig, time_t n)
-{
-       while (n) {
-               *lastdig = (n % 10) + '0';
-               n /= 10;
-               lastdig--;
-       }
-}
-
-__attribute__((externally_visible)) void c_main(void **stack)
-{
-       /* Parse the stack */
-       long argc = (long)*stack;
-       stack += argc + 2;
-
-       /* Now we're pointing at the environment.  Skip it. */
-       while(*stack)
-               stack++;
-       stack++;
-
-       /* Now we're pointing at auxv.  Initialize the vDSO parser. */
-       vdso_init_from_auxv((void *)stack);
-
-       /* Find gettimeofday. */
-       typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
-       gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
-
-       if (!gtod)
-               linux_exit(1);
-
-       struct timeval tv;
-       long ret = gtod(&tv, 0);
-
-       if (ret == 0) {
-               char buf[] = "The time is                     .000000\n";
-               to_base10(buf + 31, tv.tv_sec);
-               to_base10(buf + 38, tv.tv_usec);
-               linux_write(1, buf, sizeof(buf) - 1);
-       } else {
-               linux_exit(ret);
-       }
-
-       linux_exit(0);
-}
-
-/*
- * This is the real entry point.  It passes the initial stack into
- * the C entry point.
- */
-asm (
-       ".text\n"
-       ".global _start\n"
-       ".type _start,@function\n"
-       "_start:\n\t"
-#ifdef __x86_64__
-       "mov %rsp,%rdi\n\t"
-       "jmp c_main"
-#else
-       "push %esp\n\t"
-       "call c_main\n\t"
-       "int $3"
-#endif
-       );
diff --git a/Documentation/vDSO/vdso_test.c b/Documentation/vDSO/vdso_test.c
deleted file mode 100644 (file)
index 8daeb7d..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * vdso_test.c: Sample code to test parse_vdso.c
- * Copyright (c) 2014 Andy Lutomirski
- * Subject to the GNU General Public License, version 2
- *
- * Compile with:
- * gcc -std=gnu99 vdso_test.c parse_vdso.c
- *
- * Tested on x86, 32-bit and 64-bit.  It may work on other architectures, too.
- */
-
-#include <stdint.h>
-#include <elf.h>
-#include <stdio.h>
-#include <sys/auxv.h>
-#include <sys/time.h>
-
-extern void *vdso_sym(const char *version, const char *name);
-extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
-extern void vdso_init_from_auxv(void *auxv);
-
-int main(int argc, char **argv)
-{
-       unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
-       if (!sysinfo_ehdr) {
-               printf("AT_SYSINFO_EHDR is not present!\n");
-               return 0;
-       }
-
-       vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
-
-       /* Find gettimeofday. */
-       typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
-       gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
-
-       if (!gtod) {
-               printf("Could not find __vdso_gettimeofday\n");
-               return 1;
-       }
-
-       struct timeval tv;
-       long ret = gtod(&tv, 0);
-
-       if (ret == 0) {
-               printf("The time is %lld.%06lld\n",
-                      (long long)tv.tv_sec, (long long)tv.tv_usec);
-       } else {
-               printf("__vdso_gettimeofday failed\n");
-       }
-
-       return 0;
-}
diff --git a/Documentation/vgaarbiter.txt b/Documentation/vgaarbiter.txt
deleted file mode 100644 (file)
index 014423e..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-
-VGA Arbiter
-===========
-
-Graphic devices are accessed through ranges in I/O or memory space. While most
-modern devices allow relocation of such ranges, some "Legacy" VGA devices
-implemented on PCI will typically have the same "hard-decoded" addresses as
-they did on ISA. For more details see "PCI Bus Binding to IEEE Std 1275-1994
-Standard for Boot (Initialization Configuration) Firmware Revision 2.1"
-Section 7, Legacy Devices.
-
-The Resource Access Control (RAC) module inside the X server [0] existed for
-the legacy VGA arbitration task (besides other bus management tasks) when more
-than one legacy device co-exists on the same machine. But the problem happens
-when these devices are trying to be accessed by different userspace clients
-(e.g. two server in parallel). Their address assignments conflict. Moreover,
-ideally, being a userspace application, it is not the role of the X server to
-control bus resources. Therefore an arbitration scheme outside of the X server
-is needed to control the sharing of these resources. This document introduces
-the operation of the VGA arbiter implemented for the Linux kernel.
-
-----------------------------------------------------------------------------
-
-I.  Details and Theory of Operation
-        I.1 vgaarb
-        I.2 libpciaccess
-        I.3 xf86VGAArbiter (X server implementation)
-II. Credits
-III.References
-
-
-I. Details and Theory of Operation
-==================================
-
-I.1 vgaarb
-----------
-
-The vgaarb is a module of the Linux Kernel. When it is initially loaded, it
-scans all PCI devices and adds the VGA ones inside the arbitration. The
-arbiter then enables/disables the decoding on different devices of the VGA
-legacy instructions. Devices which do not want/need to use the arbiter may
-explicitly tell it by calling vga_set_legacy_decoding().
-
-The kernel exports a char device interface (/dev/vga_arbiter) to the clients,
-which has the following semantics:
-
- open       : open user instance of the arbiter. By default, it's attached to
-              the default VGA device of the system.
-
- close      : close user instance. Release locks made by the user
-
- read       : return a string indicating the status of the target like:
-
-              "<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)"
-
-              An IO state string is of the form {io,mem,io+mem,none}, mc and
-              ic are respectively mem and io lock counts (for debugging/
-              diagnostic only). "decodes" indicate what the card currently
-              decodes, "owns" indicates what is currently enabled on it, and
-              "locks" indicates what is locked by this card. If the card is
-              unplugged, we get "invalid" then for card_ID and an -ENODEV
-              error is returned for any command until a new card is targeted.
-
-
- write       : write a command to the arbiter. List of commands:
-
-  target <card_ID>   : switch target to card <card_ID> (see below)
-  lock <io_state>    : acquires locks on target ("none" is an invalid io_state)
-  trylock <io_state> : non-blocking acquire locks on target (returns EBUSY if
-                       unsuccessful)
-  unlock <io_state>  : release locks on target
-  unlock all         : release all locks on target held by this user (not
-                       implemented yet)
-  decodes <io_state> : set the legacy decoding attributes for the card
-
-  poll               : event if something changes on any card (not just the
-                       target)
-
-  card_ID is of the form "PCI:domain:bus:dev.fn". It can be set to "default"
-  to go back to the system default card (TODO: not implemented yet). Currently,
-  only PCI is supported as a prefix, but the userland API may support other bus
-  types in the future, even if the current kernel implementation doesn't.
-
-Note about locks:
-
-The driver keeps track of which user has which locks on which card. It
-supports stacking, like the kernel one. This complexifies the implementation
-a bit, but makes the arbiter more tolerant to user space problems and able
-to properly cleanup in all cases when a process dies.
-Currently, a max of 16 cards can have locks simultaneously issued from
-user space for a given user (file descriptor instance) of the arbiter.
-
-In the case of devices hot-{un,}plugged, there is a hook - pci_notify() - to
-notify them being added/removed in the system and automatically added/removed
-in the arbiter.
-
-There is also an in-kernel API of the arbiter in case DRM, vgacon, or other
-drivers want to use it.
-
-
-I.2 libpciaccess
-----------------
-
-To use the vga arbiter char device it was implemented an API inside the
-libpciaccess library. One field was added to struct pci_device (each device
-on the system):
-
-    /* the type of resource decoded by the device */
-    int vgaarb_rsrc;
-
-Besides it, in pci_system were added:
-
-    int vgaarb_fd;
-    int vga_count;
-    struct pci_device *vga_target;
-    struct pci_device *vga_default_dev;
-
-
-The vga_count is used to track how many cards are being arbitrated, so for
-instance, if there is only one card, then it can completely escape arbitration.
-
-
-These functions below acquire VGA resources for the given card and mark those
-resources as locked. If the resources requested are "normal" (and not legacy)
-resources, the arbiter will first check whether the card is doing legacy
-decoding for that type of resource. If yes, the lock is "converted" into a
-legacy resource lock. The arbiter will first look for all VGA cards that
-might conflict and disable their IOs and/or Memory access, including VGA
-forwarding on P2P bridges if necessary, so that the requested resources can
-be used. Then, the card is marked as locking these resources and the IO and/or
-Memory access is enabled on the card (including VGA forwarding on parent
-P2P bridges if any). In the case of vga_arb_lock(), the function will block
-if some conflicting card is already locking one of the required resources (or
-any resource on a different bus segment, since P2P bridges don't differentiate
-VGA memory and IO afaik). If the card already owns the resources, the function
-succeeds.  vga_arb_trylock() will return (-EBUSY) instead of blocking. Nested
-calls are supported (a per-resource counter is maintained).
-
-
-Set the target device of this client.
-    int  pci_device_vgaarb_set_target   (struct pci_device *dev);
-
-
-For instance, in x86 if two devices on the same bus want to lock different
-resources, both will succeed (lock). If devices are in different buses and
-trying to lock different resources, only the first who tried succeeds.
-    int  pci_device_vgaarb_lock         (void);
-    int  pci_device_vgaarb_trylock      (void);
-
-Unlock resources of device.
-    int  pci_device_vgaarb_unlock       (void);
-
-Indicates to the arbiter if the card decodes legacy VGA IOs, legacy VGA
-Memory, both, or none. All cards default to both, the card driver (fbdev for
-example) should tell the arbiter if it has disabled legacy decoding, so the
-card can be left out of the arbitration process (and can be safe to take
-interrupts at any time.
-    int  pci_device_vgaarb_decodes      (int new_vgaarb_rsrc);
-
-Connects to the arbiter device, allocates the struct
-    int  pci_device_vgaarb_init         (void);
-
-Close the connection
-    void pci_device_vgaarb_fini         (void);
-
-
-I.3 xf86VGAArbiter (X server implementation)
---------------------------------------------
-
-(TODO)
-
-X server basically wraps all the functions that touch VGA registers somehow.
-
-
-II. Credits
-===========
-
-Benjamin Herrenschmidt (IBM?) started this work when he discussed such design
-with the Xorg community in 2005 [1, 2]. In the end of 2007, Paulo Zanoni and
-Tiago Vignatti (both of C3SL/Federal University of Paraná) proceeded his work
-enhancing the kernel code to adapt as a kernel module and also did the
-implementation of the user space side [3]. Now (2009) Tiago Vignatti and Dave
-Airlie finally put this work in shape and queued to Jesse Barnes' PCI tree.
-
-
-III. References
-==============
-
-[0] http://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
-[1] http://lists.freedesktop.org/archives/xorg/2005-March/006663.html
-[2] http://lists.freedesktop.org/archives/xorg/2005-March/006745.html
-[3] http://lists.freedesktop.org/archives/xorg/2007-October/029507.html
diff --git a/Documentation/watchdog/Makefile b/Documentation/watchdog/Makefile
deleted file mode 100644 (file)
index 6018f45..0000000
+++ /dev/null
@@ -1 +0,0 @@
-subdir-y := src
diff --git a/Documentation/watchdog/src/.gitignore b/Documentation/watchdog/src/.gitignore
deleted file mode 100644 (file)
index ac90997..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-watchdog-simple
-watchdog-test
diff --git a/Documentation/watchdog/src/Makefile b/Documentation/watchdog/src/Makefile
deleted file mode 100644 (file)
index 4a892c3..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-# List of programs to build
-hostprogs-y := watchdog-simple watchdog-test
-
-# Tell kbuild to always build the programs
-always := $(hostprogs-y)
diff --git a/Documentation/watchdog/src/watchdog-simple.c b/Documentation/watchdog/src/watchdog-simple.c
deleted file mode 100644 (file)
index ba45803..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-int main(void)
-{
-       int fd = open("/dev/watchdog", O_WRONLY);
-       int ret = 0;
-       if (fd == -1) {
-               perror("watchdog");
-               exit(EXIT_FAILURE);
-       }
-       while (1) {
-               ret = write(fd, "\0", 1);
-               if (ret != 1) {
-                       ret = -1;
-                       break;
-               }
-               sleep(10);
-       }
-       close(fd);
-       return ret;
-}
diff --git a/Documentation/watchdog/src/watchdog-test.c b/Documentation/watchdog/src/watchdog-test.c
deleted file mode 100644 (file)
index 6983d05..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Watchdog Driver Test Program
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/ioctl.h>
-#include <linux/types.h>
-#include <linux/watchdog.h>
-
-int fd;
-const char v = 'V';
-
-/*
- * This function simply sends an IOCTL to the driver, which in turn ticks
- * the PC Watchdog card to reset its internal timer so it doesn't trigger
- * a computer reset.
- */
-static void keep_alive(void)
-{
-    int dummy;
-
-    printf(".");
-    ioctl(fd, WDIOC_KEEPALIVE, &dummy);
-}
-
-/*
- * The main program.  Run the program with "-d" to disable the card,
- * or "-e" to enable the card.
- */
-
-static void term(int sig)
-{
-    int ret = write(fd, &v, 1);
-
-    close(fd);
-    if (ret < 0)
-       printf("\nStopping watchdog ticks failed (%d)...\n", errno);
-    else
-       printf("\nStopping watchdog ticks...\n");
-    exit(0);
-}
-
-int main(int argc, char *argv[])
-{
-    int flags;
-    unsigned int ping_rate = 1;
-    int ret;
-
-    setbuf(stdout, NULL);
-
-    fd = open("/dev/watchdog", O_WRONLY);
-
-    if (fd == -1) {
-       printf("Watchdog device not enabled.\n");
-       exit(-1);
-    }
-
-    if (argc > 1) {
-       if (!strncasecmp(argv[1], "-d", 2)) {
-           flags = WDIOS_DISABLECARD;
-           ioctl(fd, WDIOC_SETOPTIONS, &flags);
-           printf("Watchdog card disabled.\n");
-           goto end;
-       } else if (!strncasecmp(argv[1], "-e", 2)) {
-           flags = WDIOS_ENABLECARD;
-           ioctl(fd, WDIOC_SETOPTIONS, &flags);
-           printf("Watchdog card enabled.\n");
-           goto end;
-       } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
-           flags = atoi(argv[2]);
-           ioctl(fd, WDIOC_SETTIMEOUT, &flags);
-           printf("Watchdog timeout set to %u seconds.\n", flags);
-           goto end;
-       } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
-           ping_rate = strtoul(argv[2], NULL, 0);
-           printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
-       } else {
-           printf("-d to disable, -e to enable, -t <n> to set " \
-               "the timeout,\n-p <n> to set the ping rate, and \n");
-           printf("run by itself to tick the card.\n");
-           goto end;
-       }
-    }
-
-    printf("Watchdog Ticking Away!\n");
-
-    signal(SIGINT, term);
-
-    while(1) {
-       keep_alive();
-       sleep(ping_rate);
-    }
-end:
-    ret = write(fd, &v, 1);
-    if (ret < 0)
-       printf("Stopping watchdog ticks failed (%d)...\n", errno);
-    close(fd);
-    return 0;
-}
index b3a701f..0e62ba3 100644 (file)
@@ -37,7 +37,7 @@ activates as soon as /dev/watchdog is opened and will reboot unless
 the watchdog is pinged within a certain time, this time is called the
 timeout or margin.  The simplest way to ping the watchdog is to write
 some data to the device.  So a very simple watchdog daemon would look
-like this source file:  see Documentation/watchdog/src/watchdog-simple.c
+like this source file:  see samples/watchdog/watchdog-simple.c
 
 A more advanced driver could for example check that a HTTP server is
 still responding before doing the write call to ping the watchdog.
index 7f31125..ea27747 100644 (file)
@@ -48,8 +48,10 @@ struct watchdog_device {
        const struct attribute_group **groups;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
+       const struct watchdog_governor *gov;
        unsigned int bootstatus;
        unsigned int timeout;
+       unsigned int pretimeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
        unsigned int min_hw_heartbeat_ms;
@@ -74,9 +76,11 @@ It contains following fields:
 * info: a pointer to a watchdog_info structure. This structure gives some
   additional information about the watchdog timer itself. (Like it's unique name)
 * ops: a pointer to the list of watchdog operations that the watchdog supports.
+* gov: a pointer to the assigned watchdog device pretimeout governor or NULL.
 * timeout: the watchdog timer's timeout value (in seconds).
   This is the time after which the system will reboot if user space does
   not send a heartbeat request if WDOG_ACTIVE is set.
+* pretimeout: the watchdog timer's pretimeout value (in seconds).
 * min_timeout: the watchdog timer's minimum timeout value (in seconds).
   If set, the minimum configurable value for 'timeout'.
 * max_timeout: the watchdog timer's maximum timeout value (in seconds),
@@ -121,6 +125,7 @@ struct watchdog_ops {
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
+       int (*set_pretimeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
        int (*restart)(struct watchdog_device *);
        void (*ref)(struct watchdog_device *) __deprecated;
@@ -188,6 +193,23 @@ they are supported. These optional routines/operations are:
   If set_timeout is not provided but, WDIOF_SETTIMEOUT is set, the watchdog
   infrastructure updates the timeout value of the watchdog_device internally
   to the requested value.
+  If the pretimeout feature is used (WDIOF_PRETIMEOUT), then set_timeout must
+  also take care of checking if pretimeout is still valid and set up the timer
+  accordingly. This can't be done in the core without races, so it is the
+  duty of the driver.
+* set_pretimeout: this routine checks and changes the pretimeout value of
+  the watchdog. It is optional because not all watchdogs support pretimeout
+  notification. The timeout value is not an absolute time, but the number of
+  seconds before the actual timeout would happen. It returns 0 on success,
+  -EINVAL for "parameter out of range" and -EIO for "could not write value to
+  the watchdog". A value of 0 disables pretimeout notification.
+  (Note: the WDIOF_PRETIMEOUT needs to be set in the options field of the
+  watchdog's info structure).
+  If the watchdog driver does not have to perform any action but setting the
+  watchdog_device.pretimeout, this callback can be omitted. That means if
+  set_pretimeout is not provided but WDIOF_PRETIMEOUT is set, the watchdog
+  infrastructure updates the pretimeout value of the watchdog_device internally
+  to the requested value.
 * get_timeleft: this routines returns the time that's left before a reset.
 * restart: this routine restarts the machine. It returns 0 on success or a
   negative errno code for failure.
@@ -268,3 +290,14 @@ User should follow the following guidelines for setting the priority:
 * 128: default restart handler, use if no other handler is expected to be
   available, and/or if restart is sufficient to restart the entire system
 * 255: highest priority, will preempt all other restart handlers
+
+To raise a pretimeout notification, the following function should be used:
+
+void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+
+The function can be called in the interrupt context. If watchdog pretimeout
+governor framework (kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV symbol) is enabled,
+an action is taken by a preconfigured pretimeout governor preassigned to
+the watchdog device. If watchdog pretimeout governor framework is not
+enabled, watchdog_notify_pretimeout() prints a notification message to
+the kernel log buffer.
index 061c2e3..ed2f0b8 100644 (file)
@@ -47,4 +47,4 @@ The external event interfaces on the WDT boards are not currently supported.
 Minor numbers are however allocated for it.
 
 
-Example Watchdog Driver:  see Documentation/watchdog/src/watchdog-simple.c
+Example Watchdog Driver:  see samples/watchdog/watchdog-simple.c
index dd78462..5f5e5ca 100644 (file)
@@ -316,6 +316,14 @@ W: https://01.org/linux-acpi
 S:     Supported
 F:     drivers/acpi/fan.c
 
+ACPI FOR ARM64 (ACPI/arm64)
+M:     Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
+M:     Hanjun Guo <hanjun.guo@linaro.org>
+M:     Sudeep Holla <sudeep.holla@arm.com>
+L:     linux-acpi@vger.kernel.org
+S:     Maintained
+F:     drivers/acpi/arm64
+
 ACPI THERMAL DRIVER
 M:     Zhang Rui <rui.zhang@intel.com>
 L:     linux-acpi@vger.kernel.org
@@ -4122,6 +4130,14 @@ S:       Orphan / Obsolete
 F:     drivers/gpu/drm/i810/
 F:     include/uapi/drm/i810_drm.h
 
+DRM DRIVERS FOR MEDIATEK
+M:     CK Hu <ck.hu@mediatek.com>
+M:     Philipp Zabel <p.zabel@pengutronix.de>
+L:     dri-devel@lists.freedesktop.org
+S:     Supported
+F:     drivers/gpu/drm/mediatek/
+F:     Documentation/devicetree/bindings/display/mediatek/
+
 DRM DRIVER FOR MSM ADRENO GPU
 M:     Rob Clark <robdclark@gmail.com>
 L:     linux-arm-msm@vger.kernel.org
@@ -4767,15 +4783,6 @@ L:       iommu@lists.linux-foundation.org
 S:     Maintained
 F:     drivers/iommu/exynos-iommu.c
 
-EXYNOS MIPI DISPLAY DRIVERS
-M:     Inki Dae <inki.dae@samsung.com>
-M:     Donghwa Lee <dh09.lee@samsung.com>
-M:     Kyungmin Park <kyungmin.park@samsung.com>
-L:     linux-fbdev@vger.kernel.org
-S:     Maintained
-F:     drivers/video/fbdev/exynos/exynos_mipi*
-F:     include/video/exynos_mipi*
-
 EZchip NPS platform support
 M:     Noam Camus <noamc@ezchip.com>
 S:     Supported
@@ -4954,12 +4961,9 @@ F:       drivers/net/wan/dlci.c
 F:     drivers/net/wan/sdla.c
 
 FRAMEBUFFER LAYER
-M:     Jean-Christophe Plagniol-Villard <plagnioj@jcrosoft.com>
 M:     Tomi Valkeinen <tomi.valkeinen@ti.com>
 L:     linux-fbdev@vger.kernel.org
-W:     http://linux-fbdev.sourceforge.net/
 Q:     http://patchwork.kernel.org/project/linux-fbdev/list/
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/plagnioj/linux-fbdev.git
 S:     Maintained
 F:     Documentation/fb/
 F:     drivers/video/
@@ -6438,6 +6442,7 @@ F:        include/linux/mei_cl_bus.h
 F:     drivers/misc/mei/*
 F:     drivers/watchdog/mei_wdt.c
 F:     Documentation/misc-devices/mei/*
+F:     samples/mei/*
 
 INTEL MIC DRIVERS (mic)
 M:     Sudeep Dutt <sudeep.dutt@intel.com>
@@ -6624,10 +6629,10 @@ S:      Maintained
 F:     drivers/firmware/iscsi_ibft*
 
 ISCSI
-M:     Mike Christie <michaelc@cs.wisc.edu>
+M:     Lee Duncan <lduncan@suse.com>
+M:     Chris Leech <cleech@redhat.com>
 L:     open-iscsi@googlegroups.com
-W:     www.open-iscsi.org
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/mnc/linux-2.6-iscsi.git
+W:     www.open-iscsi.com
 S:     Maintained
 F:     drivers/scsi/*iscsi*
 F:     include/scsi/*iscsi*
@@ -7205,17 +7210,11 @@ F:      drivers/lightnvm/
 F:     include/linux/lightnvm.h
 F:     include/uapi/linux/lightnvm.h
 
-LINUX FOR IBM pSERIES (RS/6000)
-M:     Paul Mackerras <paulus@au.ibm.com>
-W:     http://www.ibm.com/linux/ltc/projects/ppc
-S:     Supported
-F:     arch/powerpc/boot/rs6000.h
-
 LINUX FOR POWERPC (32-BIT AND 64-BIT)
 M:     Benjamin Herrenschmidt <benh@kernel.crashing.org>
 M:     Paul Mackerras <paulus@samba.org>
 M:     Michael Ellerman <mpe@ellerman.id.au>
-W:     http://www.penguinppc.org/
+W:     https://github.com/linuxppc/linux/wiki
 L:     linuxppc-dev@lists.ozlabs.org
 Q:     http://patchwork.ozlabs.org/project/linuxppc-dev/list/
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux.git
@@ -7230,6 +7229,7 @@ F:        drivers/net/ethernet/ibm/ibmvnic.*
 F:     drivers/pci/hotplug/pnv_php.c
 F:     drivers/pci/hotplug/rpa*
 F:     drivers/scsi/ibmvscsi/
+F:     tools/testing/selftests/powerpc
 N:     opal
 N:     /pmac
 N:     powermac
@@ -7286,9 +7286,8 @@ F:        arch/powerpc/platforms/83xx/
 F:     arch/powerpc/platforms/85xx/
 
 LINUX FOR POWERPC PA SEMI PWRFICIENT
-M:     Olof Johansson <olof@lixom.net>
 L:     linuxppc-dev@lists.ozlabs.org
-S:     Maintained
+S:     Orphan
 F:     arch/powerpc/platforms/pasemi/
 F:     drivers/*/*pasemi*
 F:     drivers/*/*/*pasemi*
@@ -7831,6 +7830,13 @@ F:       Documentation/scsi/megaraid.txt
 F:     drivers/scsi/megaraid.*
 F:     drivers/scsi/megaraid/
 
+MELFAS MIP4 TOUCHSCREEN DRIVER
+M:     Sangwon Jee <jeesw@melfas.com>
+W:     http://www.melfas.com
+S:     Supported
+F:     drivers/input/touchscreen/melfas_mip4.c
+F:     Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
+
 MELLANOX ETHERNET DRIVER (mlx4_en)
 M:     Tariq Toukan <tariqt@mellanox.com>
 L:     netdev@vger.kernel.org
@@ -9023,15 +9029,13 @@ S:      Maintained
 F:     drivers/net/wireless/intersil/p54/
 
 PA SEMI ETHERNET DRIVER
-M:     Olof Johansson <olof@lixom.net>
 L:     netdev@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/net/ethernet/pasemi/*
 
 PA SEMI SMBUS DRIVER
-M:     Olof Johansson <olof@lixom.net>
 L:     linux-i2c@vger.kernel.org
-S:     Maintained
+S:     Orphan
 F:     drivers/i2c/busses/i2c-pasemi.c
 
 PADATA PARALLEL EXECUTION MECHANISM
@@ -9193,6 +9197,14 @@ S:       Maintained
 F:     Documentation/devicetree/bindings/pci/versatile.txt
 F:     drivers/pci/host/pci-versatile.c
 
+PCI DRIVER FOR ARMADA 8K
+M:     Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+L:     linux-pci@vger.kernel.org
+L:     linux-arm-kernel@lists.infradead.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/pci/pci-armada8k.txt
+F:     drivers/pci/host/pcie-armada8k.c
+
 PCI DRIVER FOR APPLIEDMICRO XGENE
 M:     Tanmay Inamdar <tinamdar@apm.com>
 L:     linux-pci@vger.kernel.org
@@ -9239,6 +9251,7 @@ M:        Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 L:     linux-pci@vger.kernel.org
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:     Maintained
+F:     Documentation/devicetree/bindings/pci/aardvark-pci.txt
 F:     drivers/pci/host/pci-aardvark.c
 
 PCI DRIVER FOR NVIDIA TEGRA
@@ -9371,6 +9384,7 @@ W:        http://lists.infradead.org/mailman/listinfo/linux-pcmcia
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia.git
 S:     Maintained
 F:     Documentation/pcmcia/
+F:     tools/pcmcia/
 F:     drivers/pcmcia/
 F:     include/pcmcia/
 
index addb235..d8c784d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -621,6 +621,12 @@ include arch/$(SRCARCH)/Makefile
 
 KBUILD_CFLAGS  += $(call cc-option,-fno-delete-null-pointer-checks,)
 KBUILD_CFLAGS  += $(call cc-disable-warning,maybe-uninitialized,)
+KBUILD_CFLAGS  += $(call cc-disable-warning,frame-address,)
+
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+KBUILD_CFLAGS  += $(call cc-option,-ffunction-sections,)
+KBUILD_CFLAGS  += $(call cc-option,-fdata-sections,)
+endif
 
 ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
 KBUILD_CFLAGS  += -Os
@@ -802,6 +808,10 @@ LDFLAGS_BUILD_ID = $(patsubst -Wl$(comma)%,%,\
 KBUILD_LDFLAGS_MODULE += $(LDFLAGS_BUILD_ID)
 LDFLAGS_vmlinux += $(LDFLAGS_BUILD_ID)
 
+ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+LDFLAGS_vmlinux        += $(call ld-option, --gc-sections,)
+endif
+
 ifeq ($(CONFIG_STRIP_ASM_SYMS),y)
 LDFLAGS_vmlinux        += $(call ld-option, -X,)
 endif
@@ -926,9 +936,6 @@ vmlinux_prereq: $(vmlinux-deps) FORCE
 ifdef CONFIG_HEADERS_CHECK
        $(Q)$(MAKE) -f $(srctree)/Makefile headers_check
 endif
-ifdef CONFIG_BUILD_DOCSRC
-       $(Q)$(MAKE) $(build)=Documentation
-endif
 ifdef CONFIG_GDB_SCRIPTS
        $(Q)ln -fsn `cd $(srctree) && /bin/pwd`/scripts/gdb/vmlinux-gdb.py
 endif
@@ -941,9 +948,12 @@ endif
 include/generated/autoksyms.h: FORCE
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/adjust_autoksyms.sh true
 
-# Final link of vmlinux
-      cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux)
-quiet_cmd_link-vmlinux = LINK    $@
+ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
+
+# Final link of vmlinux with optional arch pass after final link
+    cmd_link-vmlinux =                                                 \
+       $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) ;       \
+       $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
 
 vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE
        +$(call if_changed,link-vmlinux)
@@ -1270,6 +1280,7 @@ $(clean-dirs):
 
 vmlinuxclean:
        $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
+       $(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean)
 
 clean: archclean vmlinuxclean
 
index 180ea33..11d3495 100644 (file)
@@ -450,6 +450,27 @@ config CC_STACKPROTECTOR_STRONG
 
 endchoice
 
+config THIN_ARCHIVES
+       bool
+       help
+         Select this if the architecture wants to use thin archives
+         instead of ld -r to create the built-in.o files.
+
+config LD_DEAD_CODE_DATA_ELIMINATION
+       bool
+       help
+         Select this if the architecture wants to do dead code and
+         data elimination with the linker by compiling with
+         -ffunction-sections -fdata-sections and linking with
+         --gc-sections.
+
+         This requires that the arch annotates or otherwise protects
+         its external entry points from being discarded. Linker scripts
+         must also merge .text.*, .data.*, and .bss.* correctly into
+         output sections. Care must be taken not to pull in unrelated
+         sections (e.g., '.text.init'). Typically '.' in section names
+         is used to distinguish them from label names / C identifiers.
+
 config HAVE_ARCH_WITHIN_STACK_FRAMES
        bool
        help
index ffd9cf5..bf8475c 100644 (file)
@@ -3,6 +3,7 @@
 generic-y += clkdev.h
 generic-y += cputime.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += mm-arch-hooks.h
index 3ecac01..8ce13d7 100644 (file)
@@ -8,7 +8,7 @@ ccflags-y       := -Wno-sign-compare
 
 obj-y    := entry.o traps.o process.o osf_sys.o irq.o \
            irq_alpha.o signal.o setup.o ptrace.o time.o \
-           alpha_ksyms.o systbls.o err_common.o io.o
+           systbls.o err_common.o io.o
 
 obj-$(CONFIG_VGA_HOSE) += console.o
 obj-$(CONFIG_SMP)      += smp.o
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
deleted file mode 100644 (file)
index f4c7ab6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * linux/arch/alpha/kernel/alpha_ksyms.c
- *
- * Export the alpha-specific functions that are needed for loadable
- * modules.
- */
-
-#include <linux/module.h>
-#include <asm/console.h>
-#include <asm/uaccess.h>
-#include <asm/checksum.h>
-#include <asm/fpu.h>
-#include <asm/machvec.h>
-
-#include <linux/syscalls.h>
-
-/* these are C runtime functions with special calling conventions: */
-extern void __divl (void);
-extern void __reml (void);
-extern void __divq (void);
-extern void __remq (void);
-extern void __divlu (void);
-extern void __remlu (void);
-extern void __divqu (void);
-extern void __remqu (void);
-
-EXPORT_SYMBOL(alpha_mv);
-EXPORT_SYMBOL(callback_getenv);
-EXPORT_SYMBOL(callback_setenv);
-EXPORT_SYMBOL(callback_save_env);
-
-/* platform dependent support */
-EXPORT_SYMBOL(strcat);
-EXPORT_SYMBOL(strcpy);
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strncat);
-EXPORT_SYMBOL(strchr);
-EXPORT_SYMBOL(strrchr);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(__memcpy);
-EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(___memset);
-EXPORT_SYMBOL(__memsetw);
-EXPORT_SYMBOL(__constant_c_memset);
-EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(clear_page);
-
-EXPORT_SYMBOL(alpha_read_fp_reg);
-EXPORT_SYMBOL(alpha_read_fp_reg_s);
-EXPORT_SYMBOL(alpha_write_fp_reg);
-EXPORT_SYMBOL(alpha_write_fp_reg_s);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_tcpudp_magic);
-EXPORT_SYMBOL(ip_compute_csum);
-EXPORT_SYMBOL(ip_fast_csum);
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-EXPORT_SYMBOL(csum_ipv6_magic);
-
-#ifdef CONFIG_MATHEMU_MODULE
-extern long (*alpha_fp_emul_imprecise)(struct pt_regs *, unsigned long);
-extern long (*alpha_fp_emul) (unsigned long pc);
-EXPORT_SYMBOL(alpha_fp_emul_imprecise);
-EXPORT_SYMBOL(alpha_fp_emul);
-#endif
-
-/*
- * The following are specially called from the uaccess assembly stubs.
- */
-EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__do_clear_user);
-
-/* 
- * SMP-specific symbols.
- */
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(_atomic_dec_and_lock);
-#endif /* CONFIG_SMP */
-
-/*
- * The following are special because they're not called
- * explicitly (the C compiler or assembler generates them in
- * response to division operations).  Fortunately, their
- * interface isn't gonna change any time soon now, so it's OK
- * to leave it out of version control.
- */
-# undef memcpy
-# undef memset
-EXPORT_SYMBOL(__divl);
-EXPORT_SYMBOL(__divlu);
-EXPORT_SYMBOL(__divq);
-EXPORT_SYMBOL(__divqu);
-EXPORT_SYMBOL(__reml);
-EXPORT_SYMBOL(__remlu);
-EXPORT_SYMBOL(__remq);
-EXPORT_SYMBOL(__remqu);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memchr);
index d3398f6..b7d6960 100644 (file)
    else beforehand.  Fine.  We'll do it ourselves.  */
 #if 0
 #define ALIAS_MV(system) \
-  struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv")));
+  struct alpha_machine_vector alpha_mv __attribute__((alias(#system "_mv"))); \
+  EXPORT_SYMBOL(alpha_mv);
 #else
 #define ALIAS_MV(system) \
-  asm(".global alpha_mv\nalpha_mv = " #system "_mv");
+  asm(".global alpha_mv\nalpha_mv = " #system "_mv"); \
+  EXPORT_SYMBOL(alpha_mv);
 #endif
 #endif /* GENERIC */
index b20af76..4811e54 100644 (file)
@@ -115,6 +115,7 @@ unsigned long alpha_agpgart_size = DEFAULT_AGP_APER_SIZE;
 
 #ifdef CONFIG_ALPHA_GENERIC
 struct alpha_machine_vector alpha_mv;
+EXPORT_SYMBOL(alpha_mv);
 #endif
 
 #ifndef alpha_using_srm
index 8804bec..6093add 100644 (file)
@@ -3,6 +3,7 @@
  */
 
 #include <asm/console.h>
+#include <asm/export.h>
 
 .text
 #define HWRPB_CRB_OFFSET 0xc0
@@ -92,6 +93,10 @@ CALLBACK(reset_env, CCB_RESET_ENV, 4)
 CALLBACK(save_env, CCB_SAVE_ENV, 1)
 CALLBACK(pswitch, CCB_PSWITCH, 3)
 CALLBACK(bios_emul, CCB_BIOS_EMUL, 5)
+
+EXPORT_SYMBOL(callback_getenv)
+EXPORT_SYMBOL(callback_setenv)
+EXPORT_SYMBOL(callback_save_env)
        
 .data
 __alpha_using_srm:             # For use by bootpheader
index 377f9e3..b57f800 100644 (file)
@@ -48,6 +48,7 @@ __sum16 csum_tcpudp_magic(__be32 saddr, __be32 daddr,
                (__force u64)saddr + (__force u64)daddr +
                (__force u64)sum + ((len + proto) << 8));
 }
+EXPORT_SYMBOL(csum_tcpudp_magic);
 
 __wsum csum_tcpudp_nofold(__be32 saddr, __be32 daddr,
                          __u32 len, __u8 proto, __wsum sum)
@@ -144,6 +145,7 @@ __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
 {
        return (__force __sum16)~do_csum(iph,ihl*4);
 }
+EXPORT_SYMBOL(ip_fast_csum);
 
 /*
  * computes the checksum of a memory block at buff, length len,
@@ -178,3 +180,4 @@ __sum16 ip_compute_csum(const void *buff, int len)
 {
        return (__force __sum16)~from64to16(do_csum(buff,len));
 }
+EXPORT_SYMBOL(ip_compute_csum);
index a221ae2..263d739 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Zero an entire page.
  */
-
+#include <asm/export.h>
        .text
        .align 4
        .global clear_page
@@ -37,3 +37,4 @@ clear_page:
        nop
 
        .end clear_page
+       EXPORT_SYMBOL(clear_page)
index 8860316..bf5b931 100644 (file)
@@ -24,6 +24,7 @@
  * Clobbers:
  *     $1,$2,$3,$4,$5,$6
  */
+#include <asm/export.h>
 
 /* Allow an exception for an insn; exit if we get one.  */
 #define EX(x,y...)                     \
@@ -111,3 +112,4 @@ $exception:
        ret     $31, ($28), 1   # .. e1 :
 
        .end __do_clear_user
+       EXPORT_SYMBOL(__do_clear_user)
index 9f3b974..2ee0bd0 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copy an entire page.
  */
-
+#include <asm/export.h>
        .text
        .align 4
        .global copy_page
@@ -47,3 +47,4 @@ copy_page:
        nop
 
        .end copy_page
+       EXPORT_SYMBOL(copy_page)
index ac9c376..509f62b 100644 (file)
@@ -26,6 +26,8 @@
  *     $1,$2,$3,$4,$5,$6,$7
  */
 
+#include <asm/export.h>
+
 /* Allow an exception for an insn; exit if we get one.  */
 #define EXI(x,y...)                    \
        99: x,##y;                      \
@@ -129,3 +131,4 @@ $exitout:
        ret $31,($28),1
 
        .end __copy_user
+EXPORT_SYMBOL(__copy_user)
index 2c2acb9..e74b454 100644 (file)
@@ -12,6 +12,7 @@
  * added by Ivan Kokshaysky <ink@jurassic.park.msu.ru>
  */
 
+#include <asm/export.h>
        .globl csum_ipv6_magic
        .align 4
        .ent csum_ipv6_magic
@@ -113,3 +114,4 @@ csum_ipv6_magic:
        ret                     # .. e1 :
 
        .end csum_ipv6_magic
+       EXPORT_SYMBOL(csum_ipv6_magic)
index 5675dca..b4ff3b6 100644 (file)
@@ -374,6 +374,7 @@ csum_partial_copy_from_user(const void __user *src, void *dst, int len,
        }
        return (__force __wsum)checksum;
 }
+EXPORT_SYMBOL(csum_partial_copy_from_user);
 
 __wsum
 csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
@@ -386,3 +387,4 @@ csum_partial_copy_nocheck(const void *src, void *dst, int len, __wsum sum)
        set_fs(oldfs);
        return checksum;
 }
+EXPORT_SYMBOL(csum_partial_copy_nocheck);
index f9f5fe8..4221b40 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
+#include <linux/export.h>
 
   asm (".text                                  \n\
        .global _atomic_dec_and_lock            \n\
@@ -39,3 +40,4 @@ static int __used atomic_dec_and_lock_1(atomic_t *atomic, spinlock_t *lock)
        spin_unlock(lock);
        return 0;
 }
+EXPORT_SYMBOL(_atomic_dec_and_lock);
index 2d1a048..1e33bd1 100644 (file)
@@ -45,6 +45,7 @@
  *     $28 - compare status
  */
 
+#include <asm/export.h>
 #define halt .long 0
 
 /*
@@ -151,6 +152,7 @@ ufunction:
        addq    $30,STACK,$30
        ret     $31,($23),1
        .end    ufunction
+EXPORT_SYMBOL(ufunction)
 
 /*
  * Uhh.. Ugly signed division. I'd rather not have it at all, but
@@ -193,3 +195,4 @@ sfunction:
        addq    $30,STACK,$30
        ret     $31,($23),1
        .end    sfunction
+EXPORT_SYMBOL(sfunction)
index adf4f7b..abe99e6 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Zero an entire page.
  */
-
+#include <asm/export.h>
         .text
         .align 4
         .global clear_page
@@ -52,3 +52,4 @@ clear_page:
        nop
 
        .end clear_page
+       EXPORT_SYMBOL(clear_page)
index 4f42a16..05bef6b 100644 (file)
@@ -43,6 +43,7 @@
  *     want to leave a hole (and we also want to avoid repeating lots of work)
  */
 
+#include <asm/export.h>
 /* Allow an exception for an insn; exit if we get one.  */
 #define EX(x,y...)                     \
        99: x,##y;                      \
@@ -222,4 +223,4 @@ $exception:                 # Destination for exception recovery(?)
        nop                     # .. E  .. ..   :
        ret     $31, ($28), 1   # L0 .. .. ..   : L U L U
        .end __do_clear_user
-
+       EXPORT_SYMBOL(__do_clear_user)
index b789db1..7793506 100644 (file)
@@ -56,7 +56,7 @@
    destination pages are in the dcache, but it is my guess that this is
    less important than the dcache miss case.  */
 
-
+#include <asm/export.h>
        .text
        .align 4
        .global copy_page
@@ -201,3 +201,4 @@ copy_page:
        nop
 
        .end copy_page
+       EXPORT_SYMBOL(copy_page)
index c4d0689..be720b5 100644 (file)
@@ -37,6 +37,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
 
+#include <asm/export.h>
 /* Allow an exception for an insn; exit if we get one.  */
 #define EXI(x,y...)                    \
        99: x,##y;                      \
@@ -235,4 +236,4 @@ $exitout:                   # Destination for exception recovery(?)
        ret $31,($28),1         # L0 .. .. ..   : L U L U
 
        .end __copy_user
-
+       EXPORT_SYMBOL(__copy_user)
index fc0bc39..de62627 100644 (file)
@@ -52,6 +52,7 @@
  * may cause additional delay in rare cases (load-load replay traps).
  */
 
+#include <asm/export.h>
        .globl csum_ipv6_magic
        .align 4
        .ent csum_ipv6_magic
@@ -148,3 +149,4 @@ csum_ipv6_magic:
        ret                     # L0 : L U L U
 
        .end csum_ipv6_magic
+       EXPORT_SYMBOL(csum_ipv6_magic)
index 2a82b9b..d18dc0e 100644 (file)
@@ -55,6 +55,7 @@
  * Try not to change the actual algorithm if possible for consistency.
  */
 
+#include <asm/export.h>
 #define halt .long 0
 
 /*
@@ -205,6 +206,7 @@ ufunction:
        addq    $30,STACK,$30           # E :
        ret     $31,($23),1             # L0 : L U U L
        .end    ufunction
+EXPORT_SYMBOL(ufunction)
 
 /*
  * Uhh.. Ugly signed division. I'd rather not have it at all, but
@@ -257,3 +259,4 @@ sfunction:
        addq    $30,STACK,$30           # E :
        ret     $31,($23),1             # L0 : L U U L
        .end    sfunction
+EXPORT_SYMBOL(sfunction)
index 1a5f71b..419adc5 100644 (file)
@@ -27,7 +27,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  * Try not to change the actual algorithm if possible for consistency.
  */
-
+#include <asm/export.h>
         .set noreorder
         .set noat
 
@@ -189,3 +189,4 @@ $not_found:
        ret                     # L0 :
 
         .end memchr
+       EXPORT_SYMBOL(memchr)
index 52b37b0..b19798b 100644 (file)
@@ -19,7 +19,7 @@
  * Temp usage notes:
  *     $1,$2,          - scratch
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -242,6 +242,7 @@ $nomoredata:
        nop                             # E :
 
        .end memcpy
+       EXPORT_SYMBOL(memcpy)
 
 /* For backwards module compatibility.  */
 __memcpy = memcpy
index 356bb2f..fed21c6 100644 (file)
@@ -26,7 +26,7 @@
  * as fixes will need to be made in multiple places.  The performance gain
  * is worth it.
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 .text
@@ -229,6 +229,7 @@ end_b:
        nop
        ret $31,($26),1         # L0 :
        .end ___memset
+       EXPORT_SYMBOL(___memset)
 
        /*
         * This is the original body of code, prior to replication and
@@ -406,6 +407,7 @@ end:
        nop
        ret $31,($26),1         # L0 :
        .end __constant_c_memset
+       EXPORT_SYMBOL(__constant_c_memset)
 
        /*
         * This is a replicant of the __constant_c_memset code, rescheduled
@@ -594,6 +596,9 @@ end_w:
        ret $31,($26),1         # L0 :
 
        .end __memsetw
+       EXPORT_SYMBOL(__memsetw)
 
 memset = ___memset
 __memset = ___memset
+       EXPORT_SYMBOL(memset)
+       EXPORT_SYMBOL(__memset)
index c426fe3..b69f604 100644 (file)
@@ -19,7 +19,7 @@
  * string once.
  */
 
-
+#include <asm/export.h>
        .text
 
        .align 4
@@ -52,3 +52,4 @@ $found:       cttz    $2, $3          # U0 :
        br      __stxcpy        # L0 :
 
        .end strcat
+       EXPORT_SYMBOL(strcat)
index fbb7b4f..ea8f2f3 100644 (file)
@@ -15,7 +15,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  * Try not to change the actual algorithm if possible for consistency.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -86,3 +86,4 @@ $found:       negq    t0, t1          # E : clear all but least set bit
        ret                     # L0 :
 
        .end strchr
+       EXPORT_SYMBOL(strchr)
index 5039280..736fd41 100644 (file)
@@ -17,7 +17,7 @@
  *     U       - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -47,3 +47,4 @@ $found:
        ret     $31, ($26)      # L0 :
 
        .end    strlen
+       EXPORT_SYMBOL(strlen)
index 4ae716c..cd35cba 100644 (file)
@@ -20,7 +20,7 @@
  * Try not to change the actual algorithm if possible for consistency.
  */
 
-
+#include <asm/export.h>
        .text
 
        .align 4
@@ -92,3 +92,4 @@ $zerocount:
        ret                     # L0 :
 
        .end strncat
+       EXPORT_SYMBOL(strncat)
index dd0d8c6..747455f 100644 (file)
@@ -18,7 +18,7 @@
  *     L       - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
  */
 
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -107,3 +107,4 @@ $eos:
        nop
 
        .end strrchr
+       EXPORT_SYMBOL(strrchr)
index 05017ba..4aa6dbf 100644 (file)
@@ -4,6 +4,9 @@
  * (C) Copyright 1998 Linus Torvalds
  */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define STT(reg,val)  asm volatile ("ftoit $f"#reg",%0" : "=r"(val));
 #else
@@ -52,6 +55,7 @@ alpha_read_fp_reg (unsigned long reg)
        }
        return val;
 }
+EXPORT_SYMBOL(alpha_read_fp_reg);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define LDT(reg,val)  asm volatile ("itoft %0,$f"#reg : : "r"(val));
@@ -97,6 +101,7 @@ alpha_write_fp_reg (unsigned long reg, unsigned long val)
              case 31: LDT(31, val); break;
        }
 }
+EXPORT_SYMBOL(alpha_write_fp_reg);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define STS(reg,val)  asm volatile ("ftois $f"#reg",%0" : "=r"(val));
@@ -146,6 +151,7 @@ alpha_read_fp_reg_s (unsigned long reg)
        }
        return val;
 }
+EXPORT_SYMBOL(alpha_read_fp_reg_s);
 
 #if defined(CONFIG_ALPHA_EV6) || defined(CONFIG_ALPHA_EV67)
 #define LDS(reg,val)  asm volatile ("itofs %0,$f"#reg : : "r"(val));
@@ -191,3 +197,4 @@ alpha_write_fp_reg_s (unsigned long reg, unsigned long val)
              case 31: LDS(31, val); break;
        }
 }
+EXPORT_SYMBOL(alpha_write_fp_reg_s);
index 14427ee..c13d3ec 100644 (file)
@@ -31,7 +31,7 @@ For correctness consider that:
       - only minimum number of quadwords may be accessed
       - the third argument is an unsigned long
 */
-
+#include <asm/export.h>
         .set noreorder
         .set noat
 
@@ -162,3 +162,4 @@ $not_found:
        ret                     # .. e1 :
 
         .end memchr
+       EXPORT_SYMBOL(memchr)
index 64083fc..57d9291 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <linux/types.h>
+#include <linux/export.h>
 
 /*
  * This should be done in one go with ldq_u*2/mask/stq_u. Do it
@@ -158,6 +159,4 @@ void * memcpy(void * dest, const void *src, size_t n)
        __memcpy_unaligned_up ((unsigned long) dest, (unsigned long) src, n);
        return dest;
 }
-
-/* For backward modules compatibility, define __memcpy.  */
-asm("__memcpy = memcpy; .globl __memcpy");
+EXPORT_SYMBOL(memcpy);
index eb3b6e0..6872c85 100644 (file)
@@ -6,7 +6,7 @@
  * This is hand-massaged output from the original memcpy.c.  We defer to
  * memcpy whenever possible; the backwards copy loops are not unrolled.
  */
-        
+#include <asm/export.h>        
        .set noat
        .set noreorder
        .text
@@ -179,3 +179,4 @@ $egress:
        nop
 
        .end memmove
+       EXPORT_SYMBOL(memmove)
index 76ccc6d..89a26f5 100644 (file)
@@ -13,7 +13,7 @@
  * The scheduling comments are according to the EV5 documentation (and done by 
  * hand, so they might well be incorrect, please do tell me about it..)
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 .text
@@ -106,6 +106,8 @@ within_one_quad:
 end:
        ret $31,($26),1         /* E1 */
        .end ___memset
+EXPORT_SYMBOL(___memset)
+EXPORT_SYMBOL(__constant_c_memset)
 
        .align 5
        .ent __memsetw
@@ -122,6 +124,9 @@ __memsetw:
        br __constant_c_memset  /* .. E1 */
 
        .end __memsetw
+EXPORT_SYMBOL(__memsetw)
 
 memset = ___memset
 __memset = ___memset
+       EXPORT_SYMBOL(memset)
+       EXPORT_SYMBOL(__memset)
index 393f503..249837b 100644 (file)
@@ -4,6 +4,7 @@
  *
  * Append a null-terminated string from SRC to DST.
  */
+#include <asm/export.h>
 
        .text
 
@@ -50,3 +51,4 @@ $found:       negq    $2, $3          # clear all but least set bit
        br      __stxcpy
 
        .end strcat
+EXPORT_SYMBOL(strcat);
index 011a175..7412a17 100644 (file)
@@ -5,7 +5,7 @@
  * Return the address of a given character within a null-terminated
  * string, or null if it is not found.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -68,3 +68,4 @@ $retnull:
        ret                     # .. e1 :
 
        .end strchr
+       EXPORT_SYMBOL(strchr)
index e0728e4..98deae1 100644 (file)
@@ -5,7 +5,7 @@
  * Copy a null-terminated string from SRC to DST.  Return a pointer
  * to the null-terminator in the source.
  */
-
+#include <asm/export.h>
        .text
 
        .align 3
@@ -21,3 +21,4 @@ strcpy:
        br      __stxcpy        # do the copy
 
        .end strcpy
+       EXPORT_SYMBOL(strcpy)
index fe63353..79c416f 100644 (file)
@@ -11,7 +11,7 @@
  *       do this instead of the 9 instructions that
  *       binary search needs).
  */
-
+#include <asm/export.h>
        .set noreorder
        .set noat
 
@@ -55,3 +55,4 @@ done: subq    $0, $16, $0
        ret     $31, ($26)
 
        .end    strlen
+       EXPORT_SYMBOL(strlen)
index a827816..6c29ea6 100644 (file)
@@ -9,7 +9,7 @@
  * past count, whereas libc may write to count+1.  This follows the generic
  * implementation in lib/string.c and is, IMHO, more sensible.
  */
-
+#include <asm/export.h>
        .text
 
        .align 3
@@ -82,3 +82,4 @@ $zerocount:
        ret
 
        .end strncat
+       EXPORT_SYMBOL(strncat)
index a46f7f3..e102cf1 100644 (file)
@@ -10,7 +10,7 @@
  * version has cropped that bit o' nastiness as well as assuming that
  * __stxncpy is in range of a branch.
  */
-
+#include <asm/export.h>
        .set noat
        .set noreorder
 
@@ -79,3 +79,4 @@ $zerolen:
        ret
 
        .end    strncpy
+       EXPORT_SYMBOL(strncpy)
index 1970dc0..4bc6cb4 100644 (file)
@@ -5,7 +5,7 @@
  * Return the address of the last occurrence of a given character
  * within a null-terminated string, or null if it is not found.
  */
-
+#include <asm/export.h>
 #include <asm/regdef.h>
 
        .set noreorder
@@ -85,3 +85,4 @@ $retnull:
        ret                     # .. e1 :
 
        .end strrchr
+       EXPORT_SYMBOL(strrchr)
index 55c0e95..6bbb1fe 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "am33xx.dtsi"
 #include "am335x-bone-common.dtsi"
+#include <dt-bindings/display/tda998x.h>
 
 / {
        model = "TI AM335x BeagleBone Black";
                        AM33XX_IOPAD(0x9b0, PIN_OUTPUT_PULLDOWN | MUX_MODE3)    /* xdma_event_intr0 */
                >;
        };
+
+       mcasp0_pins: mcasp0_pins {
+               pinctrl-single,pins = <
+                       AM33XX_IOPAD(0x9ac, PIN_INPUT_PULLUP | MUX_MODE0) /* mcasp0_ahcklx.mcasp0_ahclkx */
+                       AM33XX_IOPAD(0x99c, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* mcasp0_ahclkr.mcasp0_axr2*/
+                       AM33XX_IOPAD(0x994, PIN_OUTPUT_PULLUP | MUX_MODE0) /* mcasp0_fsx.mcasp0_fsx */
+                       AM33XX_IOPAD(0x990, PIN_OUTPUT_PULLDOWN | MUX_MODE0) /* mcasp0_aclkx.mcasp0_aclkx */
+                       AM33XX_IOPAD(0x86c, PIN_OUTPUT_PULLDOWN | MUX_MODE7) /* gpmc_a11.GPIO1_27 */
+               >;
+       };
 };
 
 &lcdc {
 };
 
 &i2c0 {
-       tda19988 {
+       tda19988: tda19988 {
                compatible = "nxp,tda998x";
                reg = <0x70>;
+
                pinctrl-names = "default", "off";
                pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
                pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
 
-               port {
-                       hdmi_0: endpoint@0 {
-                               remote-endpoint = <&lcdc_0>;
+               #sound-dai-cells = <0>;
+               audio-ports = < TDA998x_I2S     0x03>;
+
+               ports {
+                       port@0 {
+                               hdmi_0: endpoint@0 {
+                                       remote-endpoint = <&lcdc_0>;
+                               };
                        };
                };
        };
 &rtc {
        system-power-controller;
 };
+
+&mcasp0        {
+       #sound-dai-cells = <0>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&mcasp0_pins>;
+       status = "okay";
+       op-mode = <0>;  /* MCASP_IIS_MODE */
+       tdm-slots = <2>;
+       serial-dir = <  /* 0: INACTIVE, 1: TX, 2: RX */
+                       0 0 1 0
+               >;
+       tx-num-evt = <32>;
+       rx-num-evt = <32>;
+};
+
+/ {
+       clk_mcasp0_fixed: clk_mcasp0_fixed {
+               #clock-cells = <0>;
+               compatible = "fixed-clock";
+               clock-frequency = <24576000>;
+       };
+
+       clk_mcasp0: clk_mcasp0 {
+               #clock-cells = <0>;
+               compatible = "gpio-gate-clock";
+               clocks = <&clk_mcasp0_fixed>;
+               enable-gpios = <&gpio1 27 0>; /* BeagleBone Black Clk enable on GPIO1_27 */
+       };
+
+       sound {
+               compatible = "simple-audio-card";
+               simple-audio-card,name = "TI BeagleBone Black";
+               simple-audio-card,format = "i2s";
+               simple-audio-card,bitclock-master = <&dailink0_master>;
+               simple-audio-card,frame-master = <&dailink0_master>;
+
+               dailink0_master: simple-audio-card,cpu {
+                       sound-dai = <&mcasp0>;
+                       clocks = <&clk_mcasp0>;
+               };
+
+               simple-audio-card,codec {
+                       sound-dai = <&tda19988>;
+               };
+       };
+};
index e52b824..53994f9 100644 (file)
        thermal-zones {
                cpu {
                        trips {
-                               trip {
+                               cpu-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
 
                mem {
                        trips {
-                               trip {
+                               mem-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
 
                gpu {
                        trips {
-                               trip {
+                               gpu-shutdown-trip {
                                        temperature = <101000>;
                                        hysteresis = <0>;
                                        type = "critical";
                                };
                        };
-
-                       cooling-maps {
-                               /* There are currently no cooling maps because there are no cooling devices */
-                       };
                };
        };
 };
index ea340f9..187a36c 100644 (file)
 
        soctherm: thermal-sensor@700e2000 {
                compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400>; /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
                resets = <&tegra_car 78>;
                reset-names = "soctherm";
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
        };
 
        dfll: clock@70110000 {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
                };
 
                mem {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
                };
 
                gpu {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu-shutdown-trip {
+                                       temperature = <101000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <99000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
                };
 
                pllx {
 
                        thermal-sensors =
                                <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
                };
        };
 
index 4e484f4..c58f684 100644 (file)
@@ -168,8 +168,6 @@ CONFIG_DRM_PANEL_SAMSUNG_LD9040=y
 CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0=y
 CONFIG_DRM_NXP_PTN3460=y
 CONFIG_DRM_PARADE_PS8622=y
-CONFIG_EXYNOS_VIDEO=y
-CONFIG_EXYNOS_MIPI_DSI=y
 CONFIG_LCD_CLASS_DEVICE=y
 CONFIG_LCD_PLATFORM=y
 CONFIG_BACKLIGHT_PWM=y
index 55e0e3e..0745538 100644 (file)
@@ -8,6 +8,7 @@ generic-y += early_ioremap.h
 generic-y += emergency-restart.h
 generic-y += errno.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += ioctl.h
 generic-y += ipcbuf.h
 generic-y += irq_regs.h
index 624e1d4..0074835 100644 (file)
@@ -26,7 +26,6 @@
 #ifndef __ASM_ARM_TRUSTED_FOUNDATIONS_H
 #define __ASM_ARM_TRUSTED_FOUNDATIONS_H
 
-#include <linux/kconfig.h>
 #include <linux/printk.h>
 #include <linux/bug.h>
 #include <linux/of.h>
index ad325a8..68c2c09 100644 (file)
@@ -33,7 +33,7 @@ endif
 obj-$(CONFIG_CPU_IDLE)         += cpuidle.o
 obj-$(CONFIG_ISA_DMA_API)      += dma.o
 obj-$(CONFIG_FIQ)              += fiq.o fiqasm.o
-obj-$(CONFIG_MODULES)          += armksyms.o module.o
+obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_ARM_MODULE_PLTS)  += module-plts.o
 obj-$(CONFIG_ISA_DMA)          += dma-isa.o
 obj-$(CONFIG_PCI)              += bios32.o isa.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
deleted file mode 100644 (file)
index 7e45f69..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- *  linux/arch/arm/kernel/armksyms.c
- *
- *  Copyright (C) 2000 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/export.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/cryptohash.h>
-#include <linux/delay.h>
-#include <linux/in6.h>
-#include <linux/syscalls.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/arm-smccc.h>
-
-#include <asm/checksum.h>
-#include <asm/ftrace.h>
-
-/*
- * libgcc functions - functions that are used internally by the
- * compiler...  (prototypes are not correct though, but that
- * doesn't really matter since they're not versioned).
- */
-extern void __ashldi3(void);
-extern void __ashrdi3(void);
-extern void __divsi3(void);
-extern void __lshrdi3(void);
-extern void __modsi3(void);
-extern void __muldi3(void);
-extern void __ucmpdi2(void);
-extern void __udivsi3(void);
-extern void __umodsi3(void);
-extern void __do_div64(void);
-extern void __bswapsi2(void);
-extern void __bswapdi2(void);
-
-extern void __aeabi_idiv(void);
-extern void __aeabi_idivmod(void);
-extern void __aeabi_lasr(void);
-extern void __aeabi_llsl(void);
-extern void __aeabi_llsr(void);
-extern void __aeabi_lmul(void);
-extern void __aeabi_uidiv(void);
-extern void __aeabi_uidivmod(void);
-extern void __aeabi_ulcmp(void);
-
-extern void fpundefinstr(void);
-
-void mmioset(void *, unsigned int, size_t);
-void mmiocpy(void *, const void *, size_t);
-
-       /* platform dependent support */
-EXPORT_SYMBOL(arm_delay_ops);
-
-       /* networking */
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_from_user);
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(__csum_ipv6_magic);
-
-       /* io */
-#ifndef __raw_readsb
-EXPORT_SYMBOL(__raw_readsb);
-#endif
-#ifndef __raw_readsw
-EXPORT_SYMBOL(__raw_readsw);
-#endif
-#ifndef __raw_readsl
-EXPORT_SYMBOL(__raw_readsl);
-#endif
-#ifndef __raw_writesb
-EXPORT_SYMBOL(__raw_writesb);
-#endif
-#ifndef __raw_writesw
-EXPORT_SYMBOL(__raw_writesw);
-#endif
-#ifndef __raw_writesl
-EXPORT_SYMBOL(__raw_writesl);
-#endif
-
-       /* string / mem functions */
-EXPORT_SYMBOL(strchr);
-EXPORT_SYMBOL(strrchr);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memchr);
-EXPORT_SYMBOL(__memzero);
-
-EXPORT_SYMBOL(mmioset);
-EXPORT_SYMBOL(mmiocpy);
-
-#ifdef CONFIG_MMU
-EXPORT_SYMBOL(copy_page);
-
-EXPORT_SYMBOL(arm_copy_from_user);
-EXPORT_SYMBOL(arm_copy_to_user);
-EXPORT_SYMBOL(arm_clear_user);
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-
-#ifdef __ARMEB__
-EXPORT_SYMBOL(__get_user_64t_1);
-EXPORT_SYMBOL(__get_user_64t_2);
-EXPORT_SYMBOL(__get_user_64t_4);
-EXPORT_SYMBOL(__get_user_32t_8);
-#endif
-
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-#endif
-
-       /* gcc lib functions */
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__muldi3);
-EXPORT_SYMBOL(__ucmpdi2);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__umodsi3);
-EXPORT_SYMBOL(__do_div64);
-EXPORT_SYMBOL(__bswapsi2);
-EXPORT_SYMBOL(__bswapdi2);
-
-#ifdef CONFIG_AEABI
-EXPORT_SYMBOL(__aeabi_idiv);
-EXPORT_SYMBOL(__aeabi_idivmod);
-EXPORT_SYMBOL(__aeabi_lasr);
-EXPORT_SYMBOL(__aeabi_llsl);
-EXPORT_SYMBOL(__aeabi_llsr);
-EXPORT_SYMBOL(__aeabi_lmul);
-EXPORT_SYMBOL(__aeabi_uidiv);
-EXPORT_SYMBOL(__aeabi_uidivmod);
-EXPORT_SYMBOL(__aeabi_ulcmp);
-#endif
-
-       /* bitops */
-EXPORT_SYMBOL(_set_bit);
-EXPORT_SYMBOL(_test_and_set_bit);
-EXPORT_SYMBOL(_clear_bit);
-EXPORT_SYMBOL(_test_and_clear_bit);
-EXPORT_SYMBOL(_change_bit);
-EXPORT_SYMBOL(_test_and_change_bit);
-EXPORT_SYMBOL(_find_first_zero_bit_le);
-EXPORT_SYMBOL(_find_next_zero_bit_le);
-EXPORT_SYMBOL(_find_first_bit_le);
-EXPORT_SYMBOL(_find_next_bit_le);
-
-#ifdef __ARMEB__
-EXPORT_SYMBOL(_find_first_zero_bit_be);
-EXPORT_SYMBOL(_find_next_zero_bit_be);
-EXPORT_SYMBOL(_find_first_bit_be);
-EXPORT_SYMBOL(_find_next_bit_be);
-#endif
-
-#ifdef CONFIG_FUNCTION_TRACER
-#ifdef CONFIG_OLD_MCOUNT
-EXPORT_SYMBOL(mcount);
-#endif
-EXPORT_SYMBOL(__gnu_mcount_nc);
-#endif
-
-#ifdef CONFIG_ARM_PATCH_PHYS_VIRT
-EXPORT_SYMBOL(__pv_phys_pfn_offset);
-EXPORT_SYMBOL(__pv_offset);
-#endif
-
-#ifdef CONFIG_HAVE_ARM_SMCCC
-EXPORT_SYMBOL(arm_smccc_smc);
-EXPORT_SYMBOL(arm_smccc_hvc);
-#endif
index c73c403..b629d3f 100644 (file)
@@ -7,6 +7,7 @@
 #include <asm/assembler.h>
 #include <asm/ftrace.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #include "entry-header.S"
 
@@ -153,6 +154,7 @@ ENTRY(mcount)
        __mcount _old
 #endif
 ENDPROC(mcount)
+EXPORT_SYMBOL(mcount)
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 ENTRY(ftrace_caller_old)
@@ -205,6 +207,7 @@ UNWIND(.fnstart)
 #endif
 UNWIND(.fnend)
 ENDPROC(__gnu_mcount_nc)
+EXPORT_SYMBOL(__gnu_mcount_nc)
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 ENTRY(ftrace_caller)
index 04286fd..f41cee4 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/memory.h>
 #include <asm/thread_info.h>
 #include <asm/pgtable.h>
+#include <asm/export.h>
 
 #if defined(CONFIG_DEBUG_LL) && !defined(CONFIG_DEBUG_SEMIHOSTING)
 #include CONFIG_DEBUG_LL_INCLUDE
@@ -727,6 +728,8 @@ __pv_phys_pfn_offset:
 __pv_offset:
        .quad   0
        .size   __pv_offset, . -__pv_offset
+EXPORT_SYMBOL(__pv_phys_pfn_offset)
+EXPORT_SYMBOL(__pv_offset)
 #endif
 
 #include "head-common.S"
index 612eb53..91d2d5b 100644 (file)
@@ -318,8 +318,7 @@ unsigned long get_wchan(struct task_struct *p)
 
 unsigned long arch_randomize_brk(struct mm_struct *mm)
 {
-       unsigned long range_end = mm->brk + 0x02000000;
-       return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+       return randomize_page(mm->brk, 0x02000000);
 }
 
 #ifdef CONFIG_MMU
index 2e48b67..37669e7 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/opcodes-sec.h>
 #include <asm/opcodes-virt.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        /*
         * Wrap c macros in asm macros to delay expansion until after the
@@ -51,6 +52,7 @@ UNWIND(       .fnend)
 ENTRY(arm_smccc_smc)
        SMCCC SMCCC_SMC
 ENDPROC(arm_smccc_smc)
+EXPORT_SYMBOL(arm_smccc_smc)
 
 /*
  * void smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a2,
@@ -60,3 +62,4 @@ ENDPROC(arm_smccc_smc)
 ENTRY(arm_smccc_hvc)
        SMCCC SMCCC_HVC
 ENDPROC(arm_smccc_hvc)
+EXPORT_SYMBOL(arm_smccc_hvc)
index b05e958..a7e7de8 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_llsl)
 
 ENDPROC(__ashldi3)
 ENDPROC(__aeabi_llsl)
+EXPORT_SYMBOL(__ashldi3)
+EXPORT_SYMBOL(__aeabi_llsl)
index 275d7d2..490336e 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_lasr)
 
 ENDPROC(__ashrdi3)
 ENDPROC(__aeabi_lasr)
+EXPORT_SYMBOL(__ashrdi3)
+EXPORT_SYMBOL(__aeabi_lasr)
index 7d807cf..df06638 100644 (file)
@@ -1,5 +1,6 @@
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #if __LINUX_ARM_ARCH__ >= 6
        .macro  bitop, name, instr
@@ -25,6 +26,7 @@ UNWIND(       .fnstart        )
        bx      lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 
        .macro  testop, name, instr, store
@@ -55,6 +57,7 @@ UNWIND(       .fnstart        )
 2:     bx      lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 #else
        .macro  bitop, name, instr
@@ -74,6 +77,7 @@ UNWIND(       .fnstart        )
        ret     lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 
 /**
@@ -102,5 +106,6 @@ UNWIND(     .fnstart        )
        ret     lr
 UNWIND(        .fnend          )
 ENDPROC(\name          )
+EXPORT_SYMBOL(\name    )
        .endm
 #endif
index 07cda73..f05f782 100644 (file)
@@ -1,5 +1,6 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #if __LINUX_ARM_ARCH__ >= 6
 ENTRY(__bswapsi2)
@@ -35,3 +36,5 @@ ENTRY(__bswapdi2)
        ret lr
 ENDPROC(__bswapdi2)
 #endif
+EXPORT_SYMBOL(__bswapsi2)
+EXPORT_SYMBOL(__bswapdi2)
index e936352..b566154 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
                .text
 
@@ -50,6 +51,9 @@ USER(         strnebt r2, [r0])
 UNWIND(.fnend)
 ENDPROC(arm_clear_user)
 ENDPROC(__clear_user_std)
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
+EXPORT_SYMBOL(arm_clear_user)
+#endif
 
                .pushsection .text.fixup,"ax"
                .align  0
index 7a4b060..63e4c1e 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 /*
  * Prototype:
@@ -94,6 +95,7 @@ ENTRY(arm_copy_from_user)
 #include "copy_template.S"
 
 ENDPROC(arm_copy_from_user)
+EXPORT_SYMBOL(arm_copy_from_user)
 
        .pushsection .fixup,"ax"
        .align 0
index 6ee2f67..d97851d 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/assembler.h>
 #include <asm/asm-offsets.h>
 #include <asm/cache.h>
+#include <asm/export.h>
 
 #define COPY_COUNT (PAGE_SZ / (2 * L1_CACHE_BYTES) PLD( -1 ))
 
@@ -45,3 +46,4 @@ ENTRY(copy_page)
        PLD(    beq     2b                      )
                ldmfd   sp!, {r4, pc}                   @       3
 ENDPROC(copy_page)
+EXPORT_SYMBOL(copy_page)
index caf5019..592c179 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 /*
  * Prototype:
@@ -99,6 +100,9 @@ WEAK(arm_copy_to_user)
 
 ENDPROC(arm_copy_to_user)
 ENDPROC(__copy_to_user_std)
+#ifndef CONFIG_UACCESS_WITH_MEMCPY
+EXPORT_SYMBOL(arm_copy_to_user)
+#endif
 
        .pushsection .text.fixup,"ax"
        .align 0
index 3ac6ef0..68603b5 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
 
@@ -30,4 +31,4 @@ ENTRY(__csum_ipv6_magic)
                adcs    r0, r0, #0
                ldmfd   sp!, {pc}
 ENDPROC(__csum_ipv6_magic)
-
+EXPORT_SYMBOL(__csum_ipv6_magic)
index 984e0f2..830b20e 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
 
@@ -140,3 +141,4 @@ ENTRY(csum_partial)
                bne     4b
                b       .Lless4
 ENDPROC(csum_partial)
+EXPORT_SYMBOL(csum_partial)
index d03fc71..9c3383f 100644 (file)
@@ -49,5 +49,6 @@
 
 #define FN_ENTRY       ENTRY(csum_partial_copy_nocheck)
 #define FN_EXIT                ENDPROC(csum_partial_copy_nocheck)
+#define FN_EXPORT      EXPORT_SYMBOL(csum_partial_copy_nocheck)
 
 #include "csumpartialcopygeneric.S"
index 10b4590..8b94d20 100644 (file)
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 /*
  * unsigned int
@@ -331,3 +332,4 @@ FN_ENTRY
                mov     r5, r4, get_byte_1
                b       .Lexit
 FN_EXIT
+FN_EXPORT
index 1712f13..5d495ed 100644 (file)
@@ -73,6 +73,7 @@
 
 #define FN_ENTRY       ENTRY(csum_partial_copy_from_user)
 #define FN_EXIT                ENDPROC(csum_partial_copy_from_user)
+#define FN_EXPORT      EXPORT_SYMBOL(csum_partial_copy_from_user)
 
 #include "csumpartialcopygeneric.S"
 
index 2cef118..69aad80 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/export.h>
 #include <linux/timex.h>
 
 /*
@@ -34,6 +35,7 @@ struct arm_delay_ops arm_delay_ops __ro_after_init = {
        .const_udelay   = __loop_const_udelay,
        .udelay         = __loop_udelay,
 };
+EXPORT_SYMBOL(arm_delay_ops);
 
 static const struct delay_timer *delay_timer;
 static bool delay_calibrated;
index a9eafe4..0c9e1c1 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -210,3 +211,4 @@ Ldiv0_64:
 
 UNWIND(.fnend)
 ENDPROC(__do_div64)
+EXPORT_SYMBOL(__do_div64)
index 7848780..26302b8 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
                 .text
 
 /*
@@ -37,6 +38,7 @@ ENTRY(_find_first_zero_bit_le)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_zero_bit_le)
+EXPORT_SYMBOL(_find_first_zero_bit_le)
 
 /*
  * Purpose  : Find next 'zero' bit
@@ -57,6 +59,7 @@ ENTRY(_find_next_zero_bit_le)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_zero_bit_le)
+EXPORT_SYMBOL(_find_next_zero_bit_le)
 
 /*
  * Purpose  : Find a 'one' bit
@@ -78,6 +81,7 @@ ENTRY(_find_first_bit_le)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_bit_le)
+EXPORT_SYMBOL(_find_first_bit_le)
 
 /*
  * Purpose  : Find next 'one' bit
@@ -97,6 +101,7 @@ ENTRY(_find_next_bit_le)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_bit_le)
+EXPORT_SYMBOL(_find_next_bit_le)
 
 #ifdef __ARMEB__
 
@@ -116,6 +121,7 @@ ENTRY(_find_first_zero_bit_be)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_zero_bit_be)
+EXPORT_SYMBOL(_find_first_zero_bit_be)
 
 ENTRY(_find_next_zero_bit_be)
                teq     r1, #0
@@ -133,6 +139,7 @@ ENTRY(_find_next_zero_bit_be)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_zero_bit_be)
+EXPORT_SYMBOL(_find_next_zero_bit_be)
 
 ENTRY(_find_first_bit_be)
                teq     r1, #0
@@ -150,6 +157,7 @@ ENTRY(_find_first_bit_be)
 3:             mov     r0, r1                  @ no free bits
                ret     lr
 ENDPROC(_find_first_bit_be)
+EXPORT_SYMBOL(_find_first_bit_be)
 
 ENTRY(_find_next_bit_be)
                teq     r1, #0
@@ -166,6 +174,7 @@ ENTRY(_find_next_bit_be)
                add     r2, r2, #1              @ align bit pointer
                b       2b                      @ loop for next bit
 ENDPROC(_find_next_bit_be)
+EXPORT_SYMBOL(_find_next_bit_be)
 
 #endif
 
index 8ecfd15..9d09a38 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
+#include <asm/export.h>
 
 ENTRY(__get_user_1)
        check_uaccess r0, 1, r1, r2, __get_user_bad
@@ -38,6 +39,7 @@ ENTRY(__get_user_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_1)
+EXPORT_SYMBOL(__get_user_1)
 
 ENTRY(__get_user_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad
@@ -58,6 +60,7 @@ rb    .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_2)
+EXPORT_SYMBOL(__get_user_2)
 
 ENTRY(__get_user_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad
@@ -65,6 +68,7 @@ ENTRY(__get_user_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_4)
+EXPORT_SYMBOL(__get_user_4)
 
 ENTRY(__get_user_8)
        check_uaccess r0, 8, r1, r2, __get_user_bad
@@ -78,6 +82,7 @@ ENTRY(__get_user_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_8)
+EXPORT_SYMBOL(__get_user_8)
 
 #ifdef __ARMEB__
 ENTRY(__get_user_32t_8)
@@ -91,6 +96,7 @@ ENTRY(__get_user_32t_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_32t_8)
+EXPORT_SYMBOL(__get_user_32t_8)
 
 ENTRY(__get_user_64t_1)
        check_uaccess r0, 1, r1, r2, __get_user_bad8
@@ -98,6 +104,7 @@ ENTRY(__get_user_64t_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_1)
+EXPORT_SYMBOL(__get_user_64t_1)
 
 ENTRY(__get_user_64t_2)
        check_uaccess r0, 2, r1, r2, __get_user_bad8
@@ -114,6 +121,7 @@ rb  .req    r0
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_2)
+EXPORT_SYMBOL(__get_user_64t_2)
 
 ENTRY(__get_user_64t_4)
        check_uaccess r0, 4, r1, r2, __get_user_bad8
@@ -121,6 +129,7 @@ ENTRY(__get_user_64t_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__get_user_64t_4)
+EXPORT_SYMBOL(__get_user_64t_4)
 #endif
 
 __get_user_bad8:
index c31b2f3..3dff7a3 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Linsb_align:  rsb     ip, ip, #4
                cmp     ip, r2
@@ -121,3 +122,4 @@ ENTRY(__raw_readsb)
 
                ldmfd   sp!, {r4 - r6, pc}
 ENDPROC(__raw_readsb)
+EXPORT_SYMBOL(__raw_readsb)
index 2ed86fa..bfd3968 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 ENTRY(__raw_readsl)
                teq     r2, #0          @ do we have to check for the zero len?
@@ -77,3 +78,4 @@ ENTRY(__raw_readsl)
                strb    r3, [r1, #0]
                ret     lr
 ENDPROC(__raw_readsl)
+EXPORT_SYMBOL(__raw_readsl)
index 413da99..b3af3db 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Linsw_bad_alignment:
                adr     r0, .Linsw_bad_align_msg
@@ -103,4 +104,4 @@ ENTRY(__raw_readsw)
 
                ldmfd   sp!, {r4, r5, r6, pc}
 
-
+EXPORT_SYMBOL(__raw_readsw)
index d9a45e9..3c7a7a4 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  pack, rd, hw1, hw2
 #ifndef __ARMEB__
@@ -129,3 +130,4 @@ ENTRY(__raw_readsw)
                strneb  ip, [r1]
                ldmfd   sp!, {r4, pc}
 ENDPROC(__raw_readsw)
+EXPORT_SYMBOL(__raw_readsw)
index a46bbc9..fa36335 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  outword, rd
 #ifndef __ARMEB__
@@ -92,3 +93,4 @@ ENTRY(__raw_writesb)
 
                ldmfd   sp!, {r4, r5, pc}
 ENDPROC(__raw_writesb)
+EXPORT_SYMBOL(__raw_writesb)
index 4ea2435..98ed6ae 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 ENTRY(__raw_writesl)
                teq     r2, #0          @ do we have to check for the zero len?
@@ -65,3 +66,4 @@ ENTRY(__raw_writesl)
                bne     6b
                ret     lr
 ENDPROC(__raw_writesl)
+EXPORT_SYMBOL(__raw_writesl)
index 121789e..577184c 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 .Loutsw_bad_alignment:
                adr     r0, .Loutsw_bad_align_msg
@@ -124,3 +125,4 @@ ENTRY(__raw_writesw)
                strne   ip, [r0]
 
                ldmfd   sp!, {r4, r5, r6, pc}
+EXPORT_SYMBOL(__raw_writesw)
index 269f90c..e335f48 100644 (file)
@@ -9,6 +9,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .macro  outword, rd
 #ifndef __ARMEB__
@@ -98,3 +99,4 @@ ENTRY(__raw_writesw)
                strneh  ip, [r0]
                ret     lr
 ENDPROC(__raw_writesw)
+EXPORT_SYMBOL(__raw_writesw)
index 9397b2e..f541bc0 100644 (file)
@@ -36,6 +36,7 @@ Boston, MA 02111-1307, USA.  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 .macro ARM_DIV_BODY dividend, divisor, result, curbit
 
@@ -238,6 +239,8 @@ UNWIND(.fnstart)
 UNWIND(.fnend)
 ENDPROC(__udivsi3)
 ENDPROC(__aeabi_uidiv)
+EXPORT_SYMBOL(__udivsi3)
+EXPORT_SYMBOL(__aeabi_uidiv)
 
 ENTRY(__umodsi3)
 UNWIND(.fnstart)
@@ -256,6 +259,7 @@ UNWIND(.fnstart)
 
 UNWIND(.fnend)
 ENDPROC(__umodsi3)
+EXPORT_SYMBOL(__umodsi3)
 
 #ifdef CONFIG_ARM_PATCH_IDIV
        .align 3
@@ -303,6 +307,8 @@ UNWIND(.fnstart)
 UNWIND(.fnend)
 ENDPROC(__divsi3)
 ENDPROC(__aeabi_idiv)
+EXPORT_SYMBOL(__divsi3)
+EXPORT_SYMBOL(__aeabi_idiv)
 
 ENTRY(__modsi3)
 UNWIND(.fnstart)
@@ -327,6 +333,7 @@ UNWIND(.fnstart)
 
 UNWIND(.fnend)
 ENDPROC(__modsi3)
+EXPORT_SYMBOL(__modsi3)
 
 #ifdef CONFIG_AEABI
 
@@ -343,6 +350,7 @@ UNWIND(.save {r0, r1, ip, lr}       )
 
 UNWIND(.fnend)
 ENDPROC(__aeabi_uidivmod)
+EXPORT_SYMBOL(__aeabi_uidivmod)
 
 ENTRY(__aeabi_idivmod)
 UNWIND(.fnstart)
@@ -356,6 +364,7 @@ UNWIND(.save {r0, r1, ip, lr}       )
 
 UNWIND(.fnend)
 ENDPROC(__aeabi_idivmod)
+EXPORT_SYMBOL(__aeabi_idivmod)
 
 #endif
 
index 922dcd8..e408339 100644 (file)
@@ -28,6 +28,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define al r1
@@ -52,3 +53,5 @@ ENTRY(__aeabi_llsr)
 
 ENDPROC(__lshrdi3)
 ENDPROC(__aeabi_llsr)
+EXPORT_SYMBOL(__lshrdi3)
+EXPORT_SYMBOL(__aeabi_llsr)
index 74a5bed..44182bf 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -24,3 +25,4 @@ ENTRY(memchr)
 2:     movne   r0, #0
        ret     lr
 ENDPROC(memchr)
+EXPORT_SYMBOL(memchr)
index 64111bd..1be5b6d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
 #define LDR1W_SHIFT    0
 #define STR1W_SHIFT    0
@@ -68,3 +69,5 @@ ENTRY(memcpy)
 
 ENDPROC(memcpy)
 ENDPROC(mmiocpy)
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(mmiocpy)
index 69a9d47..71dcc54 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
                .text
 
@@ -225,3 +226,4 @@ ENTRY(memmove)
 18:            backward_copy_shift     push=24 pull=8
 
 ENDPROC(memmove)
+EXPORT_SYMBOL(memmove)
index 3c65e3b..7b72044 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -135,3 +136,5 @@ UNWIND( .fnstart            )
 UNWIND( .fnend   )
 ENDPROC(memset)
 ENDPROC(mmioset)
+EXPORT_SYMBOL(memset)
+EXPORT_SYMBOL(mmioset)
index 0eded95..6dec26e 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/linkage.h>
 #include <asm/assembler.h>
 #include <asm/unwind.h>
+#include <asm/export.h>
 
        .text
        .align  5
@@ -135,3 +136,4 @@ UNWIND(     .fnstart                        )
        ret     lr                      @ 1
 UNWIND(        .fnend                          )
 ENDPROC(__memzero)
+EXPORT_SYMBOL(__memzero)
index 2043059..b8f1238 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -46,3 +47,5 @@ ENTRY(__aeabi_lmul)
 
 ENDPROC(__muldi3)
 ENDPROC(__aeabi_lmul)
+EXPORT_SYMBOL(__muldi3)
+EXPORT_SYMBOL(__aeabi_lmul)
index 38d660d..11de126 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/assembler.h>
 #include <asm/errno.h>
 #include <asm/domain.h>
+#include <asm/export.h>
 
 ENTRY(__put_user_1)
        check_uaccess r0, 1, r1, ip, __put_user_bad
@@ -38,6 +39,7 @@ ENTRY(__put_user_1)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_1)
+EXPORT_SYMBOL(__put_user_1)
 
 ENTRY(__put_user_2)
        check_uaccess r0, 2, r1, ip, __put_user_bad
@@ -62,6 +64,7 @@ ENTRY(__put_user_2)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_2)
+EXPORT_SYMBOL(__put_user_2)
 
 ENTRY(__put_user_4)
        check_uaccess r0, 4, r1, ip, __put_user_bad
@@ -69,6 +72,7 @@ ENTRY(__put_user_4)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_4)
+EXPORT_SYMBOL(__put_user_4)
 
 ENTRY(__put_user_8)
        check_uaccess r0, 8, r1, ip, __put_user_bad
@@ -82,6 +86,7 @@ ENTRY(__put_user_8)
        mov     r0, #0
        ret     lr
 ENDPROC(__put_user_8)
+EXPORT_SYMBOL(__put_user_8)
 
 __put_user_bad:
        mov     r0, #-EFAULT
index 013d64c..7301f6e 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
                .align  5
@@ -25,3 +26,4 @@ ENTRY(strchr)
                subeq   r0, r0, #1
                ret     lr
 ENDPROC(strchr)
+EXPORT_SYMBOL(strchr)
index 3cec1c7..aaf9fd9 100644 (file)
@@ -11,6 +11,7 @@
  */
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
                .text
                .align  5
@@ -24,3 +25,4 @@ ENTRY(strrchr)
                mov     r0, r3
                ret     lr
 ENDPROC(strrchr)
+EXPORT_SYMBOL(strrchr)
index 6bd1089..1626e3a 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/gfp.h>
 #include <linux/highmem.h>
 #include <linux/hugetlb.h>
+#include <linux/export.h>
 #include <asm/current.h>
 #include <asm/page.h>
 
@@ -156,6 +157,7 @@ arm_copy_to_user(void __user *to, const void *from, unsigned long n)
        }
        return n;
 }
+EXPORT_SYMBOL(arm_copy_to_user);
        
 static unsigned long noinline
 __clear_user_memset(void __user *addr, unsigned long n)
@@ -213,6 +215,7 @@ unsigned long arm_clear_user(void __user *addr, unsigned long n)
        }
        return n;
 }
+EXPORT_SYMBOL(arm_clear_user);
 
 #if 0
 
index ad4a630..127a91a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 #ifdef __ARMEB__
 #define xh r0
@@ -35,6 +36,7 @@ ENTRY(__ucmpdi2)
        ret     lr
 
 ENDPROC(__ucmpdi2)
+EXPORT_SYMBOL(__ucmpdi2)
 
 #ifdef CONFIG_AEABI
 
@@ -48,6 +50,7 @@ ENTRY(__aeabi_ulcmp)
        ret     lr
 
 ENDPROC(__aeabi_ulcmp)
+EXPORT_SYMBOL(__aeabi_ulcmp)
 
 #endif
 
index cab1289..737450f 100644 (file)
@@ -32,7 +32,6 @@ endif
 
 ifdef CONFIG_SND_IMX_SOC
 obj-y += ssi-fiq.o
-obj-y += ssi-fiq-ksym.o
 endif
 
 # i.MX21 based machines
diff --git a/arch/arm/mach-imx/ssi-fiq-ksym.c b/arch/arm/mach-imx/ssi-fiq-ksym.c
deleted file mode 100644 (file)
index 792090f..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Exported ksyms for the SSI FIQ handler
- *
- * Copyright (C) 2009, Sascha Hauer <s.hauer@pengutronix.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-
-#include <linux/platform_data/asoc-imx-ssi.h>
-
-EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer);
-EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer);
-EXPORT_SYMBOL(imx_ssi_fiq_start);
-EXPORT_SYMBOL(imx_ssi_fiq_end);
-EXPORT_SYMBOL(imx_ssi_fiq_base);
-
index a8b93c5..fd7917f 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/linkage.h>
 #include <asm/assembler.h>
+#include <asm/export.h>
 
 /*
  * r8  = bit 0-15: tx offset, bit 16-31: tx buffer size
@@ -144,4 +145,8 @@ imx_ssi_fiq_tx_buffer:
                .word 0x0
 .L_imx_ssi_fiq_end:
 imx_ssi_fiq_end:
-
+EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer)
+EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer)
+EXPORT_SYMBOL(imx_ssi_fiq_start)
+EXPORT_SYMBOL(imx_ssi_fiq_end)
+EXPORT_SYMBOL(imx_ssi_fiq_base)
index 05ec5e0..67532f2 100644 (file)
@@ -23,7 +23,6 @@ static inline int fsr_fs(unsigned int fsr)
 #endif
 
 void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);
-unsigned long search_exception_table(unsigned long addr);
 void early_abt_enable(void);
 
 #endif /* __ARCH_ARM_FAULT_H */
index 58635f7..220ac70 100644 (file)
                };
 
                sata: sata@3200000 {
-                       compatible = "fsl,ls1043a-ahci", "fsl,ls1021a-ahci";
+                       compatible = "fsl,ls1043a-ahci";
                        reg = <0x0 0x3200000 0x0 0x10000>;
                        interrupts = <0 69 0x4>;
                        clocks = <&clockgen 4 0>;
+                       dma-coherent;
                };
 
                msi1: msi-controller1@1571000 {
index d105976..337da90 100644 (file)
                        reg = <0x0 0x3200000 0x0 0x10000>;
                        interrupts = <0 133 0x4>; /* Level high type */
                        clocks = <&clockgen 4 3>;
+                       dma-coherent;
                };
 
                sata1: sata@3210000 {
                        reg = <0x0 0x3210000 0x0 0x10000>;
                        interrupts = <0 136 0x4>; /* Level high type */
                        clocks = <&clockgen 4 3>;
+                       dma-coherent;
                };
 
                usb0: usb3@3100000 {
index 2013f89..3f3a46a 100644 (file)
@@ -4,6 +4,7 @@
 #include <dt-bindings/pinctrl/pinctrl-tegra.h>
 #include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/thermal/tegra124-soctherm.h>
 
 / {
        compatible = "nvidia,tegra132", "nvidia,tegra124";
        };
 
        soctherm: thermal-sensor@700e2000 {
-               compatible = "nvidia,tegra124-soctherm";
-               reg = <0x0 0x700e2000 0x0 0x1000>;
+               compatible = "nvidia,tegra132-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600 /* 0: SOC_THERM reg_base */
+                       0x0 0x70040000 0x0 0x200>; /* 2: CCROC reg_base */
+               reg-names = "soctherm-reg", "ccroc-reg";
                interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
                clocks = <&tegra_car TEGRA124_CLK_TSENSOR>,
                        <&tegra_car TEGRA124_CLK_SOC_THERM>;
                resets = <&tegra_car 78>;
                reset-names = "soctherm";
                #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-level = <TEGRA_SOCTHERM_THROT_LEVEL_HIGH>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
+       };
+
+       thermal-zones {
+               cpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu_shutdown_trip {
+                                       temperature = <105000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <102000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               mem {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem_shutdown_trip {
+                                       temperature = <101000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+               gpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu_shutdown_trip {
+                                       temperature = <101000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <99000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               pllx {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx_shutdown_trip {
+                                       temperature = <105000>;
+                                       hysteresis = <1000>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
        };
 
        ahub@70300000 {
index f673979..46045fe 100644 (file)
@@ -3,6 +3,7 @@
 #include <dt-bindings/memory/tegra210-mc.h>
 #include <dt-bindings/pinctrl/pinctrl-tegra.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/thermal/tegra124-soctherm.h>
 
 / {
        compatible = "nvidia,tegra210";
                                (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
                interrupt-parent = <&gic>;
        };
+
+       soctherm: thermal-sensor@700e2000 {
+               compatible = "nvidia,tegra210-soctherm";
+               reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
+                       0x0 0x60006000 0x0 0x400>; /* CAR reg_base */
+               reg-names = "soctherm-reg", "car-reg";
+               interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+               clocks = <&tegra_car TEGRA210_CLK_TSENSOR>,
+                       <&tegra_car TEGRA210_CLK_SOC_THERM>;
+               clock-names = "tsensor", "soctherm";
+               resets = <&tegra_car 78>;
+               reset-names = "soctherm";
+               #thermal-sensor-cells = <1>;
+
+               throttle-cfgs {
+                       throttle_heavy: heavy {
+                               nvidia,priority = <100>;
+                               nvidia,cpu-throt-percent = <85>;
+
+                               #cooling-cells = <2>;
+                       };
+               };
+       };
+
+       thermal-zones {
+               cpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_CPU>;
+
+                       trips {
+                               cpu-shutdown-trip {
+                                       temperature = <102500>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+
+                               cpu_throttle_trip: throttle-trip {
+                                       temperature = <98500>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&cpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               mem {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_MEM>;
+
+                       trips {
+                               mem-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+               gpu {
+                       polling-delay-passive = <1000>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_GPU>;
+
+                       trips {
+                               gpu-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+
+                               gpu_throttle_trip: throttle-trip {
+                                       temperature = <100000>;
+                                       hysteresis = <1000>;
+                                       type = "hot";
+                               };
+                       };
+
+                       cooling-maps {
+                               map0 {
+                                       trip = <&gpu_throttle_trip>;
+                                       cooling-device = <&throttle_heavy 1 1>;
+                               };
+                       };
+               };
+               pllx {
+                       polling-delay-passive = <0>;
+                       polling-delay = <0>;
+
+                       thermal-sensors =
+                               <&soctherm TEGRA124_SOCTHERM_SENSOR_PLLX>;
+
+                       trips {
+                               pllx-shutdown-trip {
+                                       temperature = <103000>;
+                                       hysteresis = <0>;
+                                       type = "critical";
+                               };
+                       };
+
+                       cooling-maps {
+                               /*
+                                * There are currently no cooling maps,
+                                * because there are no cooling devices.
+                                */
+                       };
+               };
+       };
 };
index 55101bd..39feb85 100644 (file)
@@ -7,7 +7,6 @@
 #ifndef __ASSEMBLY__
 
 #include <linux/init.h>
-#include <linux/kconfig.h>
 #include <linux/types.h>
 #include <linux/stddef.h>
 #include <linux/stringify.h>
index a4f5f76..27b2f13 100644 (file)
@@ -372,12 +372,8 @@ unsigned long arch_align_stack(unsigned long sp)
 
 unsigned long arch_randomize_brk(struct mm_struct *mm)
 {
-       unsigned long range_end = mm->brk;
-
        if (is_compat_task())
-               range_end += 0x02000000;
+               return randomize_page(mm->brk, 0x02000000);
        else
-               range_end += 0x40000000;
-
-       return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+               return randomize_page(mm->brk, 0x40000000);
 }
index 07d7a7e..a0513d4 100644 (file)
@@ -522,5 +522,6 @@ extern void __init pgtable_cache_init(void);
 #ifndef __ASSEMBLY__
 extern void __init paging_init(void);
 #endif /* !__ASSEMBLY__ */
+#define HAVE_ARCH_UNMAPPED_AREA
 
 #endif /* _ASM_PGTABLE_H */
index 4377c89..2305142 100644 (file)
@@ -32,7 +32,6 @@ typedef struct {
 #define get_ds()               (KERNEL_DS)
 #define get_fs()               (__current_thread_info->addr_limit)
 #define segment_eq(a, b)       ((a).seg == (b).seg)
-#define __kernel_ds_p()                segment_eq(get_fs(), KERNEL_DS)
 #define get_addr_limit()       (get_fs().seg)
 
 #define set_fs(_x)                                     \
index 87d9e34..c0f4057 100644 (file)
@@ -20,8 +20,6 @@
 #include <asm/segment.h>
 #include <asm/sections.h>
 
-#define HAVE_ARCH_UNMAPPED_AREA        /* we decide where to put mmaps */
-
 #define __ptr(x) ((unsigned long __force *)(x))
 
 #define VERIFY_READ    0
index 2e805e0..df6e996 100644 (file)
@@ -33,5 +33,5 @@ $(obj)/vmlinux.bin: vmlinux FORCE
 LDFLAGS_bootloader = -static -T
 
 $(obj)/bootloader: $(src)/bootloader.lds $(obj)/bootloader.o $(obj)/boot_head.o $(obj)/fw-emu.o \
-                   lib/lib.a arch/ia64/lib/built-in.o arch/ia64/lib/lib.a FORCE
+                   lib/lib.a arch/ia64/lib/lib.a FORCE
        $(call if_changed,ld)
diff --git a/arch/ia64/include/asm/export.h b/arch/ia64/include/asm/export.h
new file mode 100644 (file)
index 0000000..ad18c65
--- /dev/null
@@ -0,0 +1,3 @@
+/* EXPORT_DATA_SYMBOL != EXPORT_SYMBOL here */
+#define KSYM_FUNC(name) @fptr(name)
+#include <asm-generic/export.h>
index 0e00c9a..7a1f831 100644 (file)
@@ -1,12 +1,8 @@
 #ifndef __ASM_IA64_LIBATA_PORTMAP_H
 #define __ASM_IA64_LIBATA_PORTMAP_H
 
-#define ATA_PRIMARY_CMD                0x1F0
-#define ATA_PRIMARY_CTL                0x3F6
 #define ATA_PRIMARY_IRQ(dev)   isa_irq_to_vector(14)
 
-#define ATA_SECONDARY_CMD      0x170
-#define ATA_SECONDARY_CTL      0x376
 #define ATA_SECONDARY_IRQ(dev) isa_irq_to_vector(15)
 
 #endif
index cfaa7b2..6f27a66 100644 (file)
@@ -48,6 +48,7 @@
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
 #include <asm/ftrace.h>
+#include <asm/export.h>
 
 #include "minstate.h"
 
@@ -1345,12 +1346,14 @@ GLOBAL_ENTRY(unw_init_running)
        mov rp=loc0
        br.ret.sptk.many rp
 END(unw_init_running)
+EXPORT_SYMBOL(unw_init_running)
 
 #ifdef CONFIG_FUNCTION_TRACER
 #ifdef CONFIG_DYNAMIC_FTRACE
 GLOBAL_ENTRY(_mcount)
        br ftrace_stub
 END(_mcount)
+EXPORT_SYMBOL(_mcount)
 
 .here:
        br.ret.sptk.many b0
index 6b3d6c1..2c369bf 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <asm/processor.h>
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 /*
  * Inputs:
@@ -94,3 +95,4 @@ GLOBAL_ENTRY(esi_call_phys)
        mov gp=loc2
        br.ret.sptk.many rp
 END(esi_call_phys)
+EXPORT_SYMBOL_GPL(esi_call_phys)
index bb748c5..c9b5e94 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/mca_asm.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_HOTPLUG_CPU
 #define SAL_PSR_BITS_TO_SET                            \
@@ -168,6 +169,7 @@ RestRR:                                                                                     \
        __PAGE_ALIGNED_DATA
 
        .global empty_zero_page
+EXPORT_DATA_SYMBOL_GPL(empty_zero_page)
 empty_zero_page:
        .skip PAGE_SIZE
 
index 0967310..d111248 100644 (file)
 /*
  * Architecture-specific kernel symbols
- *
- * Don't put any exports here unless it's defined in an assembler file.
- * All other exports should be put directly after the definition.
  */
 
-#include <linux/module.h>
-
-#include <linux/string.h>
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(strlen);
-
-#include <asm/pgtable.h>
-EXPORT_SYMBOL_GPL(empty_zero_page);
-
-#include <asm/checksum.h>
-EXPORT_SYMBOL(ip_fast_csum);           /* hand-coded assembly */
-EXPORT_SYMBOL(csum_ipv6_magic);
-
-#include <asm/page.h>
-EXPORT_SYMBOL(clear_page);
-EXPORT_SYMBOL(copy_page);
-
 #ifdef CONFIG_VIRTUAL_MEM_MAP
+#include <linux/compiler.h>
+#include <linux/export.h>
 #include <linux/bootmem.h>
 EXPORT_SYMBOL(min_low_pfn);    /* defined by bootmem.c, but not exported by generic code */
 EXPORT_SYMBOL(max_low_pfn);    /* defined by bootmem.c, but not exported by generic code */
 #endif
-
-#include <asm/processor.h>
-EXPORT_SYMBOL(ia64_cpu_info);
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(local_per_cpu_offset);
-#endif
-
-#include <asm/uaccess.h>
-EXPORT_SYMBOL(__copy_user);
-EXPORT_SYMBOL(__do_clear_user);
-EXPORT_SYMBOL(__strlen_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__strnlen_user);
-
-/* from arch/ia64/lib */
-extern void __divsi3(void);
-extern void __udivsi3(void);
-extern void __modsi3(void);
-extern void __umodsi3(void);
-extern void __divdi3(void);
-extern void __udivdi3(void);
-extern void __moddi3(void);
-extern void __umoddi3(void);
-
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__umodsi3);
-EXPORT_SYMBOL(__divdi3);
-EXPORT_SYMBOL(__udivdi3);
-EXPORT_SYMBOL(__moddi3);
-EXPORT_SYMBOL(__umoddi3);
-
-#if defined(CONFIG_MD_RAID456) || defined(CONFIG_MD_RAID456_MODULE)
-extern void xor_ia64_2(void);
-extern void xor_ia64_3(void);
-extern void xor_ia64_4(void);
-extern void xor_ia64_5(void);
-
-EXPORT_SYMBOL(xor_ia64_2);
-EXPORT_SYMBOL(xor_ia64_3);
-EXPORT_SYMBOL(xor_ia64_4);
-EXPORT_SYMBOL(xor_ia64_5);
-#endif
-
-#include <asm/pal.h>
-EXPORT_SYMBOL(ia64_pal_call_phys_stacked);
-EXPORT_SYMBOL(ia64_pal_call_phys_static);
-EXPORT_SYMBOL(ia64_pal_call_stacked);
-EXPORT_SYMBOL(ia64_pal_call_static);
-EXPORT_SYMBOL(ia64_load_scratch_fpregs);
-EXPORT_SYMBOL(ia64_save_scratch_fpregs);
-
-#include <asm/unwind.h>
-EXPORT_SYMBOL(unw_init_running);
-
-#if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
-extern void esi_call_phys (void);
-EXPORT_SYMBOL_GPL(esi_call_phys);
-#endif
-extern char ia64_ivt[];
-EXPORT_SYMBOL(ia64_ivt);
-
-#include <asm/ftrace.h>
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount is defined in assembly */
-EXPORT_SYMBOL(_mcount);
-#endif
-
-#include <asm/cacheflush.h>
-EXPORT_SYMBOL_GPL(flush_icache_range);
index b1c3cfc..44a103a 100644 (file)
@@ -57,6 +57,7 @@
 #include <asm/thread_info.h>
 #include <asm/unistd.h>
 #include <asm/errno.h>
+#include <asm/export.h>
 
 #if 0
 # define PSR_DEFAULT_BITS      psr.ac
@@ -85,6 +86,7 @@
 
        .align 32768    // align on 32KB boundary
        .global ia64_ivt
+       EXPORT_DATA_SYMBOL(ia64_ivt)
 ia64_ivt:
 /////////////////////////////////////////////////////////////////////////////////////////
 // 0x0000 Entry 0 (size 64 bundles) VHPT Translation (8,20,47)
index 0b53344..94fb2e3 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <asm/asmmacro.h>
 #include <asm/processor.h>
+#include <asm/export.h>
 
        .data
 pal_entry_point:
@@ -87,6 +88,7 @@ GLOBAL_ENTRY(ia64_pal_call_static)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_static)
+EXPORT_SYMBOL(ia64_pal_call_static)
 
 /*
  * Make a PAL call using the stacked registers calling convention.
@@ -122,6 +124,7 @@ GLOBAL_ENTRY(ia64_pal_call_stacked)
        srlz.d                          // serialize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_stacked)
+EXPORT_SYMBOL(ia64_pal_call_stacked)
 
 /*
  * Make a physical mode PAL call using the static registers calling convention.
@@ -193,6 +196,7 @@ GLOBAL_ENTRY(ia64_pal_call_phys_static)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_phys_static)
+EXPORT_SYMBOL(ia64_pal_call_phys_static)
 
 /*
  * Make a PAL call using the stacked registers in physical mode.
@@ -250,6 +254,7 @@ GLOBAL_ENTRY(ia64_pal_call_phys_stacked)
        srlz.d                          // seralize restoration of psr.l
        br.ret.sptk.many b0
 END(ia64_pal_call_phys_stacked)
+EXPORT_SYMBOL(ia64_pal_call_phys_stacked)
 
 /*
  * Save scratch fp scratch regs which aren't saved in pt_regs already
@@ -275,6 +280,7 @@ GLOBAL_ENTRY(ia64_save_scratch_fpregs)
        stf.spill [r2]  = f15,32
        br.ret.sptk.many rp
 END(ia64_save_scratch_fpregs)
+EXPORT_SYMBOL(ia64_save_scratch_fpregs)
 
 /*
  * Load scratch fp scratch regs (fp10-fp15)
@@ -296,3 +302,4 @@ GLOBAL_ENTRY(ia64_load_scratch_fpregs)
        ldf.fill  f15 = [r2],32
        br.ret.sptk.many rp
 END(ia64_load_scratch_fpregs)
+EXPORT_SYMBOL(ia64_load_scratch_fpregs)
index afddb3e..7ec7acc 100644 (file)
@@ -71,7 +71,11 @@ EXPORT_SYMBOL(__per_cpu_offset);
 #endif
 
 DEFINE_PER_CPU(struct cpuinfo_ia64, ia64_cpu_info);
+EXPORT_SYMBOL(ia64_cpu_info);
 DEFINE_PER_CPU(unsigned long, local_per_cpu_offset);
+#ifdef CONFIG_SMP
+EXPORT_SYMBOL(local_per_cpu_offset);
+#endif
 unsigned long ia64_cycles_per_usec;
 struct ia64_boot_param *ia64_boot_param;
 struct screen_info screen_info;
index 98771e2..1f3d387 100644 (file)
@@ -2,17 +2,15 @@
 # Makefile for ia64-specific library routines..
 #
 
-obj-y := io.o
-
-lib-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o                 \
+lib-y := io.o __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o            \
        __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o                   \
        checksum.o clear_page.o csum_partial_copy.o                     \
        clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o   \
        flush.o ip_fast_csum.o do_csum.o                                \
        memset.o strlen.o xor.o
 
-obj-$(CONFIG_ITANIUM)  += copy_page.o copy_user.o memcpy.o
-obj-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o
+lib-$(CONFIG_ITANIUM)  += copy_page.o copy_user.o memcpy.o
+lib-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o
 lib-$(CONFIG_PERFMON)  += carta_random.o
 
 AFLAGS___divdi3.o      =
index 2d814e7..3cf5b76 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_ITANIUM
 # define L3_LINE_SIZE  64      // Itanium L3 line size
@@ -74,3 +75,4 @@ GLOBAL_ENTRY(clear_page)
        mov ar.lc = saved_lc            // restore lc
        br.ret.sptk.many rp
 END(clear_page)
+EXPORT_SYMBOL(clear_page)
index eecd857..7b40731 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // arguments
@@ -207,3 +208,4 @@ GLOBAL_ENTRY(__do_clear_user)
        mov ar.lc=saved_lc
        br.ret.sptk.many rp
 END(__do_clear_user)
+EXPORT_SYMBOL(__do_clear_user)
index 127d1d0..cbdb9e3 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define PIPE_DEPTH     3
 #define EPI            p[PIPE_DEPTH-1]
@@ -96,3 +97,4 @@ GLOBAL_ENTRY(copy_page)
        mov ar.lc=saved_lc
        br.ret.sptk.many rp
 END(copy_page)
+EXPORT_SYMBOL(copy_page)
index 3c45d60..c13f690 100644 (file)
@@ -61,6 +61,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define PREFETCH_DIST  8               // McKinley sustains 16 outstanding L2 misses (8 ld, 8 st)
 
@@ -183,3 +184,4 @@ GLOBAL_ENTRY(copy_page)
        mov pr = saved_pr, -1
        br.ret.sptk.many rp
 END(copy_page)
+EXPORT_SYMBOL(copy_page)
index c952bdc..66facd5 100644 (file)
@@ -30,6 +30,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // Tuneable parameters
@@ -608,3 +609,4 @@ GLOBAL_ENTRY(__copy_user)
        mov ar.pfs=saved_pfs
        br.ret.sptk.many rp
 END(__copy_user)
+EXPORT_SYMBOL(__copy_user)
index 1d8c888..9a5a2f9 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 
        /*
@@ -60,6 +61,7 @@ GLOBAL_ENTRY(flush_icache_range)
        mov     ar.lc=r3                // restore ar.lc
        br.ret.sptk.many rp
 END(flush_icache_range)
+EXPORT_SYMBOL_GPL(flush_icache_range)
 
        /*
         * clflush_cache_range(start,size)
index c91b5b0..715aed7 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #ifdef MODULO
 # define OP    mod
@@ -81,3 +82,4 @@ GLOBAL_ENTRY(NAME)
        getf.sig r8 = f6                // transfer result to result register
        br.ret.sptk.many rp
 END(NAME)
+EXPORT_SYMBOL(NAME)
index 627573c..25840f6 100644 (file)
@@ -15,6 +15,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #ifdef MODULO
 # define OP    mod
@@ -78,3 +79,4 @@ GLOBAL_ENTRY(NAME)
        getf.sig r8 = f11               // transfer result to result register
        br.ret.sptk.many rp
 END(NAME)
+EXPORT_SYMBOL(NAME)
index 620d9dc..648e0d4 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 /*
  * Since we know that most likely this function is called with buf aligned
@@ -92,6 +93,7 @@ GLOBAL_ENTRY(ip_fast_csum)
        mov     b0=r34
        br.ret.sptk.many b0
 END(ip_fast_csum)
+EXPORT_SYMBOL(ip_fast_csum)
 
 GLOBAL_ENTRY(csum_ipv6_magic)
        ld4     r20=[in0],4
@@ -142,3 +144,4 @@ GLOBAL_ENTRY(csum_ipv6_magic)
        andcm   r8=r9,r8
        br.ret.sptk.many b0
 END(csum_ipv6_magic)
+EXPORT_SYMBOL(csum_ipv6_magic)
index 448908d..ba172fd 100644 (file)
@@ -14,6 +14,7 @@
  *     David Mosberger-Tang <davidm@hpl.hp.com>
  */
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(memcpy)
 
@@ -299,3 +300,4 @@ GLOBAL_ENTRY(memcpy)
        COPY(56, 0)
 
 END(memcpy)
+EXPORT_SYMBOL(memcpy)
index ab0f876..b264b6a 100644 (file)
@@ -15,6 +15,7 @@
  */
 #include <asm/asmmacro.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
 #define EK(y...) EX(y)
 
@@ -78,6 +79,7 @@ GLOBAL_ENTRY(memcpy)
        br.cond.sptk .common_code
        ;;
 END(memcpy)
+EXPORT_SYMBOL(memcpy)
 GLOBAL_ENTRY(__copy_user)
        .prologue
 // check dest alignment
@@ -664,3 +666,4 @@ EK(.ex_handler,  (p17)      st8     [dst1]=r39,8);                                          \
 
 /* end of McKinley specific optimization */
 END(__copy_user)
+EXPORT_SYMBOL(__copy_user)
index f26c16a..87b9747 100644 (file)
@@ -18,6 +18,7 @@
    to get peak speed when value = 0.  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 #undef ret
 
 #define dest           in0
@@ -360,3 +361,4 @@ GLOBAL_ENTRY(memset)
        br.ret.sptk.many rp
 }
 END(memset)
+EXPORT_SYMBOL(memset)
index e0cdac0..1a6e17c 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 //
@@ -190,3 +191,4 @@ GLOBAL_ENTRY(strlen)
        mov ar.pfs=saved_pfs    // because of ar.ec, restore no matter what
        br.ret.sptk.many rp     // end of successful recovery code
 END(strlen)
+EXPORT_SYMBOL(strlen)
index c71eded..9d25768 100644 (file)
@@ -16,6 +16,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 //
 // int strlen_user(char *)
@@ -196,3 +197,4 @@ GLOBAL_ENTRY(__strlen_user)
        mov ar.pfs=saved_pfs    // because of ar.ec, restore no matter what
        br.ret.sptk.many rp
 END(__strlen_user)
+EXPORT_SYMBOL(__strlen_user)
index a504381..ca9ccf2 100644 (file)
@@ -17,6 +17,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(__strncpy_from_user)
        alloc r2=ar.pfs,3,0,0,0
@@ -42,3 +43,4 @@ GLOBAL_ENTRY(__strncpy_from_user)
 [.Lexit:]
        br.ret.sptk.many rp
 END(__strncpy_from_user)
+EXPORT_SYMBOL(__strncpy_from_user)
index d09066b..80a5dfd 100644 (file)
@@ -13,6 +13,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(__strnlen_user)
        .prologue
@@ -43,3 +44,4 @@ GLOBAL_ENTRY(__strnlen_user)
        mov ar.lc=r16                   // restore ar.lc
        br.ret.sptk.many rp
 END(__strnlen_user)
+EXPORT_SYMBOL(__strnlen_user)
index 54e3f7e..c83f1c4 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 GLOBAL_ENTRY(xor_ia64_2)
        .prologue
@@ -51,6 +52,7 @@ GLOBAL_ENTRY(xor_ia64_2)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_2)
+EXPORT_SYMBOL(xor_ia64_2)
 
 GLOBAL_ENTRY(xor_ia64_3)
        .prologue
@@ -91,6 +93,7 @@ GLOBAL_ENTRY(xor_ia64_3)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_3)
+EXPORT_SYMBOL(xor_ia64_3)
 
 GLOBAL_ENTRY(xor_ia64_4)
        .prologue
@@ -134,6 +137,7 @@ GLOBAL_ENTRY(xor_ia64_4)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_4)
+EXPORT_SYMBOL(xor_ia64_4)
 
 GLOBAL_ENTRY(xor_ia64_5)
        .prologue
@@ -182,3 +186,4 @@ GLOBAL_ENTRY(xor_ia64_5)
        mov pr = r29, -1
        br.ret.sptk.few rp
 END(xor_ia64_5)
+EXPORT_SYMBOL(xor_ia64_5)
diff --git a/arch/m68k/include/asm/export.h b/arch/m68k/include/asm/export.h
new file mode 100644 (file)
index 0000000..0af20f4
--- /dev/null
@@ -0,0 +1,3 @@
+#define KSYM_ALIGN 2
+#define KCRC_ALIGN 2
+#include <asm-generic/export.h>
index 1bdf152..36deeb3 100644 (file)
@@ -44,9 +44,6 @@ struct exception_table_entry
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-
 
 /*
  * These are the main single-value transfer routines.  They automatically
index 8a1c4d3..74c898c 100644 (file)
@@ -13,7 +13,7 @@ extra-$(CONFIG_SUN3X) := head.o
 extra-$(CONFIG_SUN3)   := sun3-head.o
 extra-y                        += vmlinux.lds
 
-obj-y  := entry.o irq.o m68k_ksyms.o module.o process.o ptrace.o
+obj-y  := entry.o irq.o module.o process.o ptrace.o
 obj-y  += setup.o signal.o sys_m68k.o syscalltable.o time.o traps.o
 
 obj-$(CONFIG_MMU_MOTOROLA) += ints.o vectors.o
diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c
deleted file mode 100644 (file)
index 774c1bd..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#include <linux/module.h>
-
-asmlinkage long long __ashldi3 (long long, int);
-asmlinkage long long __ashrdi3 (long long, int);
-asmlinkage long long __lshrdi3 (long long, int);
-asmlinkage long long __muldi3 (long long, long long);
-
-/* The following are special because they're not called
-   explicitly (the C compiler generates them).  Fortunately,
-   their interface isn't gonna change any time soon now, so
-   it's OK to leave it out of version control.  */
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__muldi3);
-
-#if defined(CONFIG_CPU_HAS_NO_MULDIV64)
-/*
- * Simpler 68k and ColdFire parts also need a few other gcc functions.
- */
-extern long long __divsi3(long long, long long);
-extern long long __modsi3(long long, long long);
-extern long long __mulsi3(long long, long long);
-extern long long __udivsi3(long long, long long);
-extern long long __umodsi3(long long, long long);
-
-EXPORT_SYMBOL(__divsi3);
-EXPORT_SYMBOL(__modsi3);
-EXPORT_SYMBOL(__mulsi3);
-EXPORT_SYMBOL(__udivsi3);
-EXPORT_SYMBOL(__umodsi3);
-#endif
index 37234c2..8dffd36 100644 (file)
@@ -13,6 +13,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details. */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -55,3 +58,4 @@ __ashldi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__ashldi3);
index 1d59345..e6565a3 100644 (file)
@@ -13,6 +13,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details. */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -56,3 +59,4 @@ __ashrdi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__ashrdi3);
index 2c0ec85..3a2143f 100644 (file)
@@ -33,6 +33,8 @@ General Public License for more details. */
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
 
+#include <asm/export.h>
+
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -118,3 +120,4 @@ L2: movel   d1, sp@-
 L3:    movel   sp@+, d2
        rts
 
+       EXPORT_SYMBOL(__divsi3)
index 49e1ec8..0397797 100644 (file)
@@ -13,6 +13,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details. */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #define BITS_PER_UNIT 8
 
 typedef                 int SItype     __attribute__ ((mode (SI)));
@@ -55,3 +58,4 @@ __lshrdi3 (DItype u, word_type b)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__lshrdi3);
index 1d9e0ef..1c96764 100644 (file)
@@ -33,6 +33,8 @@ General Public License for more details. */
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
 
+#include <asm/export.h>
+
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -106,3 +108,4 @@ SYM (__modsi3):
        movel   d1, d0
        rts
 
+       EXPORT_SYMBOL(__modsi3)
index 9006d15..6459af5 100644 (file)
@@ -14,6 +14,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details. */
 
+#include <linux/compiler.h>
+#include <linux/export.h>
+
 #ifdef CONFIG_CPU_HAS_NO_MULDIV64
 
 #define SI_TYPE_SIZE 32
@@ -90,3 +93,4 @@ __muldi3 (DItype u, DItype v)
 
   return w.ll;
 }
+EXPORT_SYMBOL(__muldi3);
index c39ad4e..855675e 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -102,4 +102,4 @@ SYM (__mulsi3):
        addl    d1, d0
 
        rts
-
+       EXPORT_SYMBOL(__mulsi3)
index 35a5446..78440ae 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -154,4 +154,4 @@ L2: subql   IMM (1),d4
        unlk    a6              | and return
        rts
 #endif /* __mcf5200__ || __mcoldfire__ */
-
+       EXPORT_SYMBOL(__udivsi3)
index 099da51..b6fd11f 100644 (file)
@@ -32,7 +32,7 @@ General Public License for more details. */
    Some of this code comes from MINIX, via the folks at ericsson.
    D. V. Henkel-Wallace (gumby@cygnus.com) Fete Bastille, 1992
 */
-
+#include <asm/export.h>
 /* These are predefined by new versions of GNU cpp.  */
 
 #ifndef __USER_LABEL_PREFIX__
@@ -105,4 +105,4 @@ SYM (__umodsi3):
        subl    d0, d1          /* d1 = a - (a/b)*b */
        movel   d1, d0
        rts
-
+       EXPORT_SYMBOL(__umodsi3)
index 470e365..8ff0a70 100644 (file)
 #define atomic_dec(v) atomic_sub(1, (v))
 
 #define atomic_inc_not_zero(v) atomic_add_unless((v), 1, 0)
+#define atomic_dec_if_positive(v)       atomic_sub_if_positive(1, v)
 
 #endif
 
-#define atomic_dec_if_positive(v)       atomic_sub_if_positive(1, v)
-
 #include <asm-generic/atomic64.h>
 
 #endif /* __ASM_METAG_ATOMIC_H */
index 8266767..253a67e 100644 (file)
@@ -71,9 +71,6 @@ struct exception_table_entry {
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-
 #ifndef CONFIG_MMU
 
 /* Check against bounds of physical memory */
index cb16fcc..5537f95 100644 (file)
@@ -267,6 +267,17 @@ static void octeon_crash_shutdown(struct pt_regs *regs)
        default_machine_crash_shutdown(regs);
 }
 
+#ifdef CONFIG_SMP
+void octeon_crash_smp_send_stop(void)
+{
+       int cpu;
+
+       /* disable watchdogs */
+       for_each_online_cpu(cpu)
+               cvmx_write_csr(CVMX_CIU_WDOGX(cpu_logical_map(cpu)), 0);
+}
+#endif
+
 #endif /* CONFIG_KEXEC */
 
 #ifdef CONFIG_CAVIUM_RESERVE32
@@ -911,6 +922,9 @@ void __init prom_init(void)
        _machine_kexec_shutdown = octeon_shutdown;
        _machine_crash_shutdown = octeon_crash_shutdown;
        _machine_kexec_prepare = octeon_kexec_prepare;
+#ifdef CONFIG_SMP
+       _crash_smp_send_stop = octeon_crash_smp_send_stop;
+#endif
 #endif
 
        octeon_user_io_init();
diff --git a/arch/mips/include/asm/extable.h b/arch/mips/include/asm/extable.h
new file mode 100644 (file)
index 0000000..dce7a62
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _ASM_EXTABLE_H
+#define _ASM_EXTABLE_H
+
+struct exception_table_entry
+{
+       unsigned long insn;
+       unsigned long nextinsn;
+};
+
+struct pt_regs;
+extern int fixup_exception(struct pt_regs *regs);
+
+#endif
index ee25ebb..493a3cc 100644 (file)
@@ -45,6 +45,7 @@ extern const unsigned char kexec_smp_wait[];
 extern unsigned long secondary_kexec_args[4];
 extern void (*relocated_kexec_smp_wait) (void *);
 extern atomic_t kexec_ready_to_reboot;
+extern void (*_crash_smp_send_stop)(void);
 #endif
 #endif
 
index d1ff774..c68c0cc 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/io.h>
 #include <linux/init.h>
 #include <linux/irq.h>
-#include <linux/kconfig.h>
 #include <boot_param.h>
 
 /* loongson internal northbridge initialization */
index 0aaf9a0..702c273 100644 (file)
@@ -3,7 +3,7 @@
 
 #include <linux/list.h>
 #include <linux/elf.h>
-#include <asm/uaccess.h>
+#include <asm/extable.h>
 
 struct mod_arch_specific {
        /* Data Bus Error exception tables */
index 21a2aab..4daf839 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/thread_info.h>
 #include <linux/string.h>
 #include <asm/asm-eva.h>
+#include <asm/extable.h>
 
 /*
  * The fs value determines whether argument validity checking should be
@@ -1485,12 +1486,4 @@ static inline long strnlen_user(const char __user *s, long n)
        return res;
 }
 
-struct exception_table_entry
-{
-       unsigned long insn;
-       unsigned long nextinsn;
-};
-
-extern int fixup_exception(struct pt_regs *regs);
-
 #endif /* _ASM_UACCESS_H */
index 24ad815..3e940db 100644 (file)
 #define __NR_copy_file_range           (__NR_Linux + 360)
 #define __NR_preadv2                   (__NR_Linux + 361)
 #define __NR_pwritev2                  (__NR_Linux + 362)
+#define __NR_pkey_mprotect             (__NR_Linux + 363)
+#define __NR_pkey_alloc                        (__NR_Linux + 364)
+#define __NR_pkey_free                 (__NR_Linux + 365)
+
 
 /*
  * Offset of the last Linux o32 flavoured syscall
  */
-#define __NR_Linux_syscalls            362
+#define __NR_Linux_syscalls            365
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
 
 #define __NR_O32_Linux                 4000
-#define __NR_O32_Linux_syscalls                362
+#define __NR_O32_Linux_syscalls                365
 
 #if _MIPS_SIM == _MIPS_SIM_ABI64
 
 #define __NR_copy_file_range           (__NR_Linux + 320)
 #define __NR_preadv2                   (__NR_Linux + 321)
 #define __NR_pwritev2                  (__NR_Linux + 322)
+#define __NR_pkey_mprotect             (__NR_Linux + 323)
+#define __NR_pkey_alloc                        (__NR_Linux + 324)
+#define __NR_pkey_free                 (__NR_Linux + 325)
 
 /*
  * Offset of the last Linux 64-bit flavoured syscall
  */
-#define __NR_Linux_syscalls            322
+#define __NR_Linux_syscalls            325
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */
 
 #define __NR_64_Linux                  5000
-#define __NR_64_Linux_syscalls         322
+#define __NR_64_Linux_syscalls         325
 
 #if _MIPS_SIM == _MIPS_SIM_NABI32
 
 #define __NR_copy_file_range           (__NR_Linux + 324)
 #define __NR_preadv2                   (__NR_Linux + 325)
 #define __NR_pwritev2                  (__NR_Linux + 326)
+#define __NR_pkey_mprotect             (__NR_Linux + 327)
+#define __NR_pkey_alloc                        (__NR_Linux + 328)
+#define __NR_pkey_free                 (__NR_Linux + 329)
 
 /*
  * Offset of the last N32 flavoured syscall
  */
-#define __NR_Linux_syscalls            326
+#define __NR_Linux_syscalls            329
 
 #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */
 
 #define __NR_N32_Linux                 6000
-#define __NR_N32_Linux_syscalls                326
+#define __NR_N32_Linux_syscalls                329
 
 #endif /* _UAPI_ASM_UNISTD_H */
index 610f0f3..1723b17 100644 (file)
@@ -47,9 +47,14 @@ static void crash_shutdown_secondary(void *passed_regs)
 
 static void crash_kexec_prepare_cpus(void)
 {
+       static int cpus_stopped;
        unsigned int msecs;
+       unsigned int ncpus;
 
-       unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
+       if (cpus_stopped)
+               return;
+
+       ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
 
        dump_send_ipi(crash_shutdown_secondary);
        smp_wmb();
@@ -64,6 +69,17 @@ static void crash_kexec_prepare_cpus(void)
                cpu_relax();
                mdelay(1);
        }
+
+       cpus_stopped = 1;
+}
+
+/* Override the weak function in kernel/panic.c */
+void crash_smp_send_stop(void)
+{
+       if (_crash_smp_send_stop)
+               _crash_smp_send_stop();
+
+       crash_kexec_prepare_cpus();
 }
 
 #else /* !defined(CONFIG_SMP)  */
index 50980bf..5972520 100644 (file)
@@ -25,6 +25,7 @@ void (*_machine_crash_shutdown)(struct pt_regs *regs) = NULL;
 #ifdef CONFIG_SMP
 void (*relocated_kexec_smp_wait) (void *);
 atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0);
+void (*_crash_smp_send_stop)(void) = NULL;
 #endif
 
 int
index c8e43e0..c29d397 100644 (file)
@@ -597,3 +597,6 @@ EXPORT(sys_call_table)
        PTR     sys_copy_file_range             /* 4360 */
        PTR     sys_preadv2
        PTR     sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 4365 */
index e6ede12..0687f96 100644 (file)
@@ -435,4 +435,7 @@ EXPORT(sys_call_table)
        PTR     sys_copy_file_range             /* 5320 */
        PTR     sys_preadv2
        PTR     sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 5325 */
        .size   sys_call_table,.-sys_call_table
index 51d3988..0331ba3 100644 (file)
@@ -430,4 +430,7 @@ EXPORT(sysn32_call_table)
        PTR     sys_copy_file_range
        PTR     compat_sys_preadv2              /* 6325 */
        PTR     compat_sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free
        .size   sysn32_call_table,.-sysn32_call_table
index 6efa713..5a47042 100644 (file)
@@ -585,4 +585,7 @@ EXPORT(sys32_call_table)
        PTR     sys_copy_file_range             /* 4360 */
        PTR     compat_sys_preadv2
        PTR     compat_sys_pwritev2
+       PTR     sys_pkey_mprotect
+       PTR     sys_pkey_alloc
+       PTR     sys_pkey_free                   /* 4365 */
        .size   sys32_call_table,.-sys32_call_table
index 27533c1..dd292dc 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/timer.h>
 #include <linux/mutex.h>
+#include <linux/uaccess.h>
 
 #include "picvue.h"
 
index 36775d2..f8b7bf8 100644 (file)
@@ -35,7 +35,6 @@
  */
 #include <linux/sched.h>
 #include <linux/debugfs.h>
-#include <linux/kconfig.h>
 #include <linux/percpu-defs.h>
 #include <linux/perf_event.h>
 
index 39e7b47..49a2e22 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/errno.h>
 #include <linux/filter.h>
 #include <linux/if_vlan.h>
-#include <linux/kconfig.h>
 #include <linux/moduleloader.h>
 #include <linux/netdevice.h>
 #include <linux/string.h>
index 769d5ed..b10ba12 100644 (file)
@@ -18,7 +18,6 @@
 #include <asm/page.h>
 #include <asm/ptrace.h>
 #include <asm/cpu-regs.h>
-#include <asm/uaccess.h>
 #include <asm/current.h>
 
 /* Forward declaration, a strange C thing */
index d012e87..2eedf6f 100644 (file)
@@ -38,7 +38,6 @@
 #define get_ds()       (KERNEL_DS)
 #define get_fs()       (current_thread_info()->addr_limit)
 #define set_fs(x)      (current_thread_info()->addr_limit = (x))
-#define __kernel_ds_p() (current_thread_info()->addr_limit.seg == 0x9FFFFFFF)
 
 #define segment_eq(a, b) ((a).seg == (b).seg)
 
@@ -72,12 +71,6 @@ static inline int ___range_ok(unsigned long addr, unsigned int size)
 #define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0)
 #define __access_ok(addr, size)     (__range_ok((addr), (size)) == 0)
 
-static inline int verify_area(int type, const void *addr, unsigned long size)
-{
-       return access_ok(type, addr, size) ? 0 : -EFAULT;
-}
-
-
 /*
  * The exception table consists of pairs of addresses: the first is the
  * address of an instruction that is allowed to fault, and the second is
index dfd0301..cd8cb1d 100644 (file)
@@ -75,7 +75,7 @@ static int restore_sigcontext(struct pt_regs *regs,
                struct fpucontext *buf;
                err |= __get_user(buf, &sc->fpucontext);
                if (buf) {
-                       if (verify_area(VERIFY_READ, buf, sizeof(*buf)))
+                       if (!access_ok(VERIFY_READ, buf, sizeof(*buf)))
                                goto badframe;
                        err |= fpu_restore_sigcontext(buf);
                }
@@ -98,7 +98,7 @@ asmlinkage long sys_sigreturn(void)
        long d0;
 
        frame = (struct sigframe __user *) current_frame()->sp;
-       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+       if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
        if (__get_user(set.sig[0], &frame->sc.oldmask))
                goto badframe;
@@ -130,7 +130,7 @@ asmlinkage long sys_rt_sigreturn(void)
        long d0;
 
        frame = (struct rt_sigframe __user *) current_frame()->sp;
-       if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
+       if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
                goto badframe;
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
index 5cc6b4f..140faa1 100644 (file)
@@ -82,10 +82,6 @@ struct exception_table_entry {
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-extern void sort_exception_table(void);
-
 /*
  * These are the main single-value transfer routines.  They automatically
  * use the right size if we just have the right pointer type.
index e44bdb9..c2c43f7 100644 (file)
@@ -83,10 +83,10 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
        printk("%s:%d: bad pgd %08lx.\n", __FILE__, __LINE__, (unsigned long)pgd_val(e))
 
 /* This is the size of the initially mapped kernel memory */
-#if defined(CONFIG_64BIT) || defined(CONFIG_SMP)
-#define KERNEL_INITIAL_ORDER   25      /* 1<<25 = 32MB */
+#if defined(CONFIG_64BIT)
+#define KERNEL_INITIAL_ORDER   26      /* 1<<26 = 64MB */
 #else
-#define KERNEL_INITIAL_ORDER   24      /* 1<<24 = 16MB */
+#define KERNEL_INITIAL_ORDER   25      /* 1<<25 = 32MB */
 #endif
 #define KERNEL_INITIAL_SIZE    (1 << KERNEL_INITIAL_ORDER)
 
index 5e953ab..6367023 100644 (file)
@@ -11,6 +11,7 @@ void parisc_terminate(char *msg, struct pt_regs *regs,
 void die_if_kernel(char *str, struct pt_regs *regs, long err);
 
 /* mm/fault.c */
+const char *trap_name(unsigned long code);
 void do_page_fault(struct pt_regs *regs, unsigned long code,
                unsigned long address);
 #endif
index 97d6b20..378df92 100644 (file)
@@ -458,8 +458,8 @@ void parisc_terminate(char *msg, struct pt_regs *regs, int code, unsigned long o
        }
 
        printk("\n");
-       printk(KERN_CRIT "%s: Code=%d regs=%p (Addr=" RFMT ")\n",
-                       msg, code, regs, offset);
+       pr_crit("%s: Code=%d (%s) regs=%p (Addr=" RFMT ")\n",
+               msg, code, trap_name(code), regs, offset);
        show_regs(regs);
 
        spin_unlock(&terminate_lock);
index b37787d..3d6ef1b 100644 (file)
@@ -90,8 +90,9 @@ SECTIONS
        /* Start of data section */
        _sdata = .;
 
-       RO_DATA_SECTION(8)
-
+       /* Architecturally we need to keep __gp below 0x1000000 and thus
+        * in front of RO_DATA_SECTION() which stores lots of tracepoint
+        * and ftrace symbols. */
 #ifdef CONFIG_64BIT
        . = ALIGN(16);
        /* Linkage tables */
@@ -106,6 +107,12 @@ SECTIONS
        }
 #endif
 
+       RO_DATA_SECTION(8)
+
+       /* RO because of BUILDTIME_EXTABLE_SORT */
+       EXCEPTION_TABLE(8)
+       NOTES
+
        /* unwind info */
        .PARISC.unwind : {
                __start___unwind = .;
@@ -121,9 +128,6 @@ SECTIONS
        . = ALIGN(HUGEPAGE_SIZE);
        data_start = .;
 
-       EXCEPTION_TABLE(8)
-       NOTES
-
        /* Data */
        RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, PAGE_SIZE)
 
index 47a6ca4..8ff9253 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/ptrace.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
-#include <linux/module.h>
+#include <linux/extable.h>
 #include <linux/uaccess.h>
 
 #include <asm/traps.h>
@@ -204,6 +204,16 @@ static const char * const trap_description[] = {
        [28] "Unaligned data reference trap",
 };
 
+const char *trap_name(unsigned long code)
+{
+       const char *t = NULL;
+
+       if (code < ARRAY_SIZE(trap_description))
+               t = trap_description[code];
+
+       return t ? t : "Unknown trap";
+}
+
 /*
  * Print out info about fatal segfaults, if the show_unhandled_signals
  * sysctl is set:
@@ -213,8 +223,6 @@ show_signal_msg(struct pt_regs *regs, unsigned long code,
                unsigned long address, struct task_struct *tsk,
                struct vm_area_struct *vma)
 {
-       const char *trap_name = NULL;
-
        if (!unhandled_signal(tsk, SIGSEGV))
                return;
 
@@ -226,10 +234,7 @@ show_signal_msg(struct pt_regs *regs, unsigned long code,
            tsk->comm, code, address);
        print_vma_addr(KERN_CONT " in ", regs->iaoq[0]);
 
-       if (code < ARRAY_SIZE(trap_description))
-               trap_name = trap_description[code];
-       pr_warn(KERN_CONT " trap #%lu: %s%c", code,
-               trap_name ? trap_name : "unknown",
+       pr_cont(" trap #%lu: %s%c", code, trap_name(code),
                vma ? ',':'\n');
 
        if (vma)
index 356f384..e02ada3 100644 (file)
@@ -105,6 +105,8 @@ static void * __init get_memblock(unsigned long size)
        else
                panic("get_memblock() failed.\n");
 
+       memset(__va(phys), 0, size);
+
        return __va(phys);
 }
 
index 50d020a..617dece 100644 (file)
@@ -318,12 +318,12 @@ mpc85xx_smp_defconfig:
 PHONY += corenet32_smp_defconfig
 corenet32_smp_defconfig:
        $(call merge_into_defconfig,corenet_basic_defconfig,\
-               85xx-32bit 85xx-smp 85xx-hw fsl-emb-nonhw)
+               85xx-32bit 85xx-smp 85xx-hw fsl-emb-nonhw dpaa)
 
 PHONY += corenet64_smp_defconfig
 corenet64_smp_defconfig:
        $(call merge_into_defconfig,corenet_basic_defconfig,\
-               85xx-64bit 85xx-smp altivec 85xx-hw fsl-emb-nonhw)
+               85xx-64bit 85xx-smp altivec 85xx-hw fsl-emb-nonhw dpaa)
 
 PHONY += mpc86xx_defconfig
 mpc86xx_defconfig:
diff --git a/arch/powerpc/configs/dpaa.config b/arch/powerpc/configs/dpaa.config
new file mode 100644 (file)
index 0000000..efa99c0
--- /dev/null
@@ -0,0 +1 @@
+CONFIG_FSL_DPAA=y
index ab9f4e0..5c4fbc8 100644 (file)
@@ -1,5 +1,6 @@
 generic-y += clkdev.h
 generic-y += div64.h
+generic-y += export.h
 generic-y += irq_regs.h
 generic-y += irq_work.h
 generic-y += local64.h
index f752e6f..ab68d0e 100644 (file)
@@ -43,6 +43,7 @@ extern int machine_check_e500mc(struct pt_regs *regs);
 extern int machine_check_e500(struct pt_regs *regs);
 extern int machine_check_e200(struct pt_regs *regs);
 extern int machine_check_47x(struct pt_regs *regs);
+int machine_check_8xx(struct pt_regs *regs);
 
 extern void cpu_down_flush_e500v2(void);
 extern void cpu_down_flush_e500mc(void);
index c7d82ff..eba6041 100644 (file)
@@ -155,6 +155,8 @@ static inline unsigned long arch_local_irq_save(void)
        unsigned long flags = arch_local_save_flags();
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 0" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EID);
 #else
        SET_MSR_EE(flags & ~MSR_EE);
 #endif
@@ -165,6 +167,8 @@ static inline void arch_local_irq_disable(void)
 {
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 0" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EID);
 #else
        arch_local_irq_save();
 #endif
@@ -174,6 +178,8 @@ static inline void arch_local_irq_enable(void)
 {
 #ifdef CONFIG_BOOKE
        asm volatile("wrteei 1" : : : "memory");
+#elif defined(CONFIG_PPC_8xx)
+       wrtspr(SPRN_EIE);
 #else
        unsigned long msr = mfmsr();
        SET_MSR_EE(msr | MSR_EE);
index 4d85180..4396db5 100644 (file)
@@ -1,12 +1,8 @@
 #ifndef __ASM_POWERPC_LIBATA_PORTMAP_H
 #define __ASM_POWERPC_LIBATA_PORTMAP_H
 
-#define ATA_PRIMARY_CMD        0x1F0
-#define ATA_PRIMARY_CTL        0x3F6
 #define ATA_PRIMARY_IRQ(dev)   pci_get_legacy_ide_irq(dev, 0)
 
-#define ATA_SECONDARY_CMD      0x170
-#define ATA_SECONDARY_CTL      0x376
 #define ATA_SECONDARY_IRQ(dev) pci_get_legacy_ide_irq(dev, 1)
 
 #endif
index 54ff8ce..0132831 100644 (file)
 #define PPC_INST_LWSYNC                        0x7c2004ac
 #define PPC_INST_SYNC                  0x7c0004ac
 #define PPC_INST_SYNC_MASK             0xfc0007fe
+#define PPC_INST_ISYNC                 0x4c00012c
 #define PPC_INST_LXVD2X                        0x7c000698
 #define PPC_INST_MCRXR                 0x7c000400
 #define PPC_INST_MCRXR_MASK            0xfc0007fe
index 2a62078..9cd4e8c 100644 (file)
@@ -1250,6 +1250,8 @@ static inline void mtmsr_isync(unsigned long val)
                                     : "r" ((unsigned long)(v)) \
                                     : "memory")
 #endif
+#define wrtspr(rn)     asm volatile("mtspr " __stringify(rn) ",0" : \
+                                    : : "memory")
 
 extern unsigned long msr_check_and_set(unsigned long bits);
 extern bool strict_msr_control;
index 94d01f8..0197e12 100644 (file)
 #define SPRN_MD_RAM0   825
 #define SPRN_MD_RAM1   826
 
+/* Special MSR manipulation registers */
+#define SPRN_EIE       80      /* External interrupt enable (EE=1, RI=1) */
+#define SPRN_EID       81      /* External interrupt disable (EE=0, RI=1) */
+
 /* Commands.  Only the first few are available to the instruction cache.
 */
 #define        IDC_ENABLE      0x02000000      /* Cache enable */
index aded29a..6913f67 100644 (file)
@@ -90,10 +90,6 @@ obj-$(CONFIG_RELOCATABLE)    += reloc_$(BITS).o
 obj-$(CONFIG_PPC32)            += entry_32.o setup_32.o
 obj-$(CONFIG_PPC64)            += dma-iommu.o iommu.o
 obj-$(CONFIG_KGDB)             += kgdb.o
-obj-$(CONFIG_MODULES)          += ppc_ksyms.o
-ifeq ($(CONFIG_PPC32),y)
-obj-$(CONFIG_MODULES)          += ppc_ksyms_32.o
-endif
 obj-$(CONFIG_BOOTX_TEXT)       += btext.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
index 6c4646a..6a82ef0 100644 (file)
@@ -1248,6 +1248,7 @@ static struct cpu_spec __initdata cpu_specs[] = {
                .mmu_features           = MMU_FTR_TYPE_8xx,
                .icache_bsize           = 16,
                .dcache_bsize           = 16,
+               .machine_check          = machine_check_8xx,
                .platform               = "ppc823",
        },
 #endif /* CONFIG_8xx */
index 83428a2..3841d74 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/unistd.h>
 #include <asm/ftrace.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /*
  * MSR_KERNEL is > 0x10000 on 4xx/Book-E since it include MSR_CE.
@@ -1358,6 +1359,7 @@ _GLOBAL(_mcount)
        MCOUNT_RESTORE_FRAME
        bctr
 #endif
+EXPORT_SYMBOL(_mcount)
 
 _GLOBAL(ftrace_stub)
        blr
index 51df82b..6432d4b 100644 (file)
@@ -38,6 +38,7 @@
 #include <asm/context_tracking.h>
 #include <asm/tm.h>
 #include <asm/ppc-opcode.h>
+#include <asm/export.h>
 
 /*
  * System calls.
@@ -1177,6 +1178,7 @@ _GLOBAL(enter_prom)
 #ifdef CONFIG_DYNAMIC_FTRACE
 _GLOBAL(mcount)
 _GLOBAL(_mcount)
+EXPORT_SYMBOL(_mcount)
        mflr    r12
        mtctr   r12
        mtlr    r0
@@ -1413,6 +1415,7 @@ livepatch_handler:
 
 #else
 _GLOBAL_TOC(_mcount)
+EXPORT_SYMBOL(_mcount)
        /* Taken from output of objdump from lib64/glibc */
        mflr    r3
        ld      r11, 0(r1)
index 9f1ebf7..52ca247 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-compat.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
 #ifndef CONFIG_PPC64
 /* epapr_ev_idle() was derived from e500_idle() */
@@ -53,3 +54,4 @@ epapr_hypercall_start:
        nop
        nop
        blr
+EXPORT_SYMBOL(epapr_hypercall_start)
index 08992f8..f129408 100644 (file)
@@ -1377,7 +1377,7 @@ __end_interrupts:
 DEFINE_FIXED_SYMBOL(__end_interrupts)
 
 #ifdef CONFIG_PPC_970_NAP
-TRAMP_REAL_BEGIN(power4_fixup_nap)
+EXC_COMMON_BEGIN(power4_fixup_nap)
        andc    r9,r9,r10
        std     r9,TI_LOCAL_FLAGS(r11)
        ld      r10,_LINK(r1)           /* make idle task do the */
index 08d14b0..6c509f3 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_VSX
 #define __REST_32FPVSRS(n,c,base)                                      \
@@ -59,6 +60,7 @@ _GLOBAL(load_fp_state)
        MTFSF_L(fr0)
        REST_32FPVSRS(0, R4, R3)
        blr
+EXPORT_SYMBOL(load_fp_state)
 
 /*
  * Store FP state into memory, including FPSCR
@@ -69,6 +71,7 @@ _GLOBAL(store_fp_state)
        mffs    fr0
        stfd    fr0,FPSTATE_FPSCR(r3)
        blr
+EXPORT_SYMBOL(store_fp_state)
 
 /*
  * This task wants to use the FPU now.
index a3f821e..9d96354 100644 (file)
@@ -34,6 +34,7 @@
 #include <asm/ptrace.h>
 #include <asm/bug.h>
 #include <asm/kvm_book3s_asm.h>
+#include <asm/export.h>
 
 /* 601 only have IBAT; cr0.eq is set on 601 when using this macro */
 #define LOAD_BAT(n, reg, RA, RB)       \
@@ -738,6 +739,7 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_NEED_DTLB_SW_LRU)
 
        .globl mol_trampoline
        .set mol_trampoline, i0x2f00
+       EXPORT_SYMBOL(mol_trampoline)
 
        . = 0x3000
 
@@ -1045,6 +1047,7 @@ _ENTRY(switch_mmu_context)
 4:     trap
        EMIT_BUG_ENTRY 4b,__FILE__,__LINE__,0
        blr
+EXPORT_SYMBOL(switch_mmu_context)
 
 /*
  * An undocumented "feature" of 604e requires that the v bit
@@ -1272,6 +1275,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
 
        .globl  swapper_pg_dir
 swapper_pg_dir:
@@ -1285,6 +1289,7 @@ intercept_table:
        .long 0, 0, 0, 0, 0, 0, 0, 0
        .long 0, 0, 0, 0, 0, 0, 0, 0
        .long 0, 0, 0, 0, 0, 0, 0, 0
+EXPORT_SYMBOL(intercept_table)
 
 /* Room for two PTE pointers, usually the kernel and current user pointers
  * to their respective root page table.
index 7d7d863..41374a4 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /* As with the other PowerPC ports, it is expected that when code
  * execution begins here, the following registers contain valid, yet
@@ -971,6 +972,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
        .globl  swapper_pg_dir
 swapper_pg_dir:
        .space  PGD_TABLE_SIZE
index 9cdf5c7..37e4a7c 100644 (file)
@@ -39,6 +39,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
 #include <asm/synch.h>
+#include <asm/export.h>
 #include "head_booke.h"
 
 
@@ -1254,6 +1255,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
 /*
  * To support >32-bit physical addresses, we use an 8KB pgdir.
index 79da064..04c546e 100644 (file)
@@ -43,6 +43,7 @@
 #include <asm/hw_irq.h>
 #include <asm/cputhreads.h>
 #include <asm/ppc-opcode.h>
+#include <asm/export.h>
 
 /* The physical memory is laid out such that the secondary processor
  * spin code sits at 0x0000...0x00ff. On server, the vectors follow
@@ -1002,3 +1003,4 @@ swapper_pg_dir:
        .globl  empty_zero_page
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
index 3a185c5..fb133a1 100644 (file)
@@ -31,6 +31,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ptrace.h>
 #include <asm/fixmap.h>
+#include <asm/export.h>
 
 /* Macro to make the code more readable. */
 #ifdef CONFIG_8xx_CPU6
@@ -226,7 +227,7 @@ i##n:                                                               \
                          ret_from_except)
 
 /* System reset */
-       EXCEPTION(0x100, Reset, unknown_exception, EXC_XFER_STD)
+       EXCEPTION(0x100, Reset, system_reset_exception, EXC_XFER_STD)
 
 /* Machine check */
        . = 0x200
@@ -321,7 +322,7 @@ SystemCall:
 #endif
 
 InstructionTLBMiss:
-#ifdef CONFIG_8xx_CPU6
+#if defined(CONFIG_8xx_CPU6) || defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        mtspr   SPRN_SPRG_SCRATCH2, r3
 #endif
        EXCEPTION_PROLOG_0
@@ -329,23 +330,20 @@ InstructionTLBMiss:
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
         */
+       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
+       INVALIDATE_ADJACENT_PAGES_CPU15(r11, r10)
 #if defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        /* Only modules will cause ITLB Misses as we always
         * pin the first 8MB of kernel memory */
-       mfspr   r11, SPRN_SRR0  /* Get effective address of fault */
-       INVALIDATE_ADJACENT_PAGES_CPU15(r10, r11)
-       mfcr    r10
-       IS_KERNEL(r11, r11)
+       mfcr    r3
+       IS_KERNEL(r11, r10)
+#endif
        mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+#if defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        BRANCH_UNLESS_KERNEL(3f)
        lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
 3:
-       mtcr    r10
-       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
-#else
-       mfspr   r10, SPRN_SRR0  /* Get effective address of fault */
-       INVALIDATE_ADJACENT_PAGES_CPU15(r11, r10)
-       mfspr   r11, SPRN_M_TW  /* Get level 1 table base address */
+       mtcr    r3
 #endif
        /* Insert level 1 index */
        rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
@@ -377,58 +375,39 @@ InstructionTLBMiss:
        MTSPR_CPU6(SPRN_MI_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
-#ifdef CONFIG_8xx_CPU6
+#if defined(CONFIG_8xx_CPU6) || defined(CONFIG_MODULES) || defined (CONFIG_DEBUG_PAGEALLOC)
        mfspr   r3, SPRN_SPRG_SCRATCH2
 #endif
        EXCEPTION_EPILOG_0
        rfi
 
-/*
- * Bottom part of DataStoreTLBMiss handler for IMMR area
- * not enough space in the DataStoreTLBMiss area
- */
-DTLBMissIMMR:
-       mtcr    r10
-       /* Set 512k byte guarded page and mark it valid */
-       li      r10, MD_PS512K | MD_GUARDED | MD_SVALID
-       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
-       mfspr   r10, SPRN_IMMR                  /* Get current IMMR */
-       rlwinm  r10, r10, 0, 0xfff80000         /* Get 512 kbytes boundary */
-       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
-                         _PAGE_PRESENT | _PAGE_NO_CACHE
-       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
-
-       li      r11, RPN_PATTERN
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       EXCEPTION_EPILOG_0
-       rfi
-
        . = 0x1200
 DataStoreTLBMiss:
+       mtspr   SPRN_SPRG_SCRATCH2, r3
        EXCEPTION_PROLOG_0
-       mfcr    r10
+       mfcr    r3
 
        /* If we are faulting a kernel address, we have to use the
         * kernel page tables.
         */
-       mfspr   r11, SPRN_MD_EPN
-       rlwinm  r11, r11, 16, 0xfff8
+       mfspr   r10, SPRN_MD_EPN
+       rlwinm  r10, r10, 16, 0xfff8
+       cmpli   cr0, r10, PAGE_OFFSET@h
+       mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+       blt+    3f
 #ifndef CONFIG_PIN_TLB_IMMR
-       cmpli   cr0, r11, VIRT_IMMR_BASE@h
+       cmpli   cr0, r10, VIRT_IMMR_BASE@h
 #endif
-       cmpli   cr7, r11, PAGE_OFFSET@h
+_ENTRY(DTLBMiss_cmp)
+       cmpli   cr7, r10, (PAGE_OFFSET + 0x1800000)@h
+       lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
 #ifndef CONFIG_PIN_TLB_IMMR
 _ENTRY(DTLBMiss_jmp)
        beq-    DTLBMissIMMR
 #endif
-       bge-    cr7, 4f
-
-       mfspr   r11, SPRN_M_TW  /* Get level 1 table */
+       blt     cr7, DTLBMissLinear
 3:
-       mtcr    r10
-#ifdef CONFIG_8xx_CPU6
-       mtspr   SPRN_SPRG_SCRATCH2, r3
-#endif
+       mtcr    r3
        mfspr   r10, SPRN_MD_EPN
 
        /* Insert level 1 index */
@@ -481,30 +460,7 @@ _ENTRY(DTLBMiss_jmp)
        MTSPR_CPU6(SPRN_MD_RPN, r10, r3)        /* Update TLB entry */
 
        /* Restore registers */
-#ifdef CONFIG_8xx_CPU6
        mfspr   r3, SPRN_SPRG_SCRATCH2
-#endif
-       mtspr   SPRN_DAR, r11   /* Tag DAR */
-       EXCEPTION_EPILOG_0
-       rfi
-
-4:
-_ENTRY(DTLBMiss_cmp)
-       cmpli   cr0, r11, (PAGE_OFFSET + 0x1800000)@h
-       lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
-       bge-    3b
-
-       mtcr    r10
-       /* Set 8M byte page and mark it valid */
-       li      r10, MD_PS8MEG | MD_SVALID
-       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
-       mfspr   r10, SPRN_MD_EPN
-       rlwinm  r10, r10, 0, 0x0f800000         /* 8xx supports max 256Mb RAM */
-       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
-                         _PAGE_PRESENT
-       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
-
-       li      r11, RPN_PATTERN
        mtspr   SPRN_DAR, r11   /* Tag DAR */
        EXCEPTION_EPILOG_0
        rfi
@@ -570,6 +526,43 @@ DARFixed:/* Return from dcbx instruction bug workaround */
 
        . = 0x2000
 
+/*
+ * Bottom part of DataStoreTLBMiss handlers for IMMR area and linear RAM.
+ * not enough space in the DataStoreTLBMiss area.
+ */
+DTLBMissIMMR:
+       mtcr    r3
+       /* Set 512k byte guarded page and mark it valid */
+       li      r10, MD_PS512K | MD_GUARDED | MD_SVALID
+       MTSPR_CPU6(SPRN_MD_TWC, r10, r11)
+       mfspr   r10, SPRN_IMMR                  /* Get current IMMR */
+       rlwinm  r10, r10, 0, 0xfff80000         /* Get 512 kbytes boundary */
+       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
+                         _PAGE_PRESENT | _PAGE_NO_CACHE
+       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
+
+       li      r11, RPN_PATTERN
+       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r3, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
+       rfi
+
+DTLBMissLinear:
+       mtcr    r3
+       /* Set 8M byte page and mark it valid */
+       li      r11, MD_PS8MEG | MD_SVALID
+       MTSPR_CPU6(SPRN_MD_TWC, r11, r3)
+       rlwinm  r10, r10, 16, 0x0f800000        /* 8xx supports max 256Mb RAM */
+       ori     r10, r10, 0xf0 | MD_SPS16K | _PAGE_SHARED | _PAGE_DIRTY | \
+                         _PAGE_PRESENT
+       MTSPR_CPU6(SPRN_MD_RPN, r10, r11)       /* Update TLB entry */
+
+       li      r11, RPN_PATTERN
+       mtspr   SPRN_DAR, r11   /* Tag DAR */
+       mfspr   r3, SPRN_SPRG_SCRATCH2
+       EXCEPTION_EPILOG_0
+       rfi
+
 /* This is the procedure to calculate the data EA for buggy dcbx,dcbi instructions
  * by decoding the registers used by the dcbx instruction and adding them.
  * DAR is set to the calculated address.
@@ -586,7 +579,9 @@ FixupDAR:/* Entry point for dcbx workaround. */
        rlwinm  r11, r10, 16, 0xfff8
 _ENTRY(FixupDAR_cmp)
        cmpli   cr7, r11, (PAGE_OFFSET + 0x1800000)@h
-       blt-    cr7, 200f
+       /* create physical page address from effective address */
+       tophys(r11, r10)
+       blt-    cr7, 201f
        lis     r11, (swapper_pg_dir-PAGE_OFFSET)@ha
        /* Insert level 1 index */
 3:     rlwimi  r11, r10, 32 - ((PAGE_SHIFT - 2) << 1), (PAGE_SHIFT - 2) << 1, 29
@@ -616,10 +611,6 @@ _ENTRY(FixupDAR_cmp)
 141:   mfspr   r10,SPRN_SPRG_SCRATCH2
        b       DARFixed        /* Nope, go back to normal TLB processing */
 
-       /* create physical page address from effective address */
-200:   tophys(r11, r10)
-       b       201b
-
 144:   mfspr   r10, SPRN_DSISR
        rlwinm  r10, r10,0,7,5  /* Clear store bit for buggy dcbst insn */
        mtspr   SPRN_DSISR, r10
@@ -894,6 +885,7 @@ sdata:
        .align  PAGE_SHIFT
 empty_zero_page:
        .space  PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
        .globl  swapper_pg_dir
 swapper_pg_dir:
index 3bfa315..bf4c602 100644 (file)
@@ -42,6 +42,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/cache.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 #include "head_booke.h"
 
 /* As with the other PowerPC ports, it is expected that when code
@@ -1223,6 +1224,7 @@ sdata:
        .globl  empty_zero_page
 empty_zero_page:
        .space  4096
+EXPORT_SYMBOL(empty_zero_page)
        .globl  swapper_pg_dir
 swapper_pg_dir:
        .space  PGD_TABLE_SIZE
index 37d6e74..5f202a5 100644 (file)
@@ -479,7 +479,8 @@ int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 
                /* Handle failure */
                if (unlikely(entry == DMA_ERROR_CODE)) {
-                       if (printk_ratelimit())
+                       if (!(attrs & DMA_ATTR_NO_WARN) &&
+                           printk_ratelimit())
                                dev_info(dev, "iommu_alloc failed, tbl %p "
                                         "vaddr %lx npages %lu\n", tbl, vaddr,
                                         npages);
@@ -776,7 +777,8 @@ dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
                                         mask >> tbl->it_page_shift, align,
                                         attrs);
                if (dma_handle == DMA_ERROR_CODE) {
-                       if (printk_ratelimit())  {
+                       if (!(attrs & DMA_ATTR_NO_WARN) &&
+                           printk_ratelimit())  {
                                dev_info(dev, "iommu_alloc failed, tbl %p "
                                         "vaddr %p npages %d\n", tbl, vaddr,
                                         npages);
index 0d43219..384357c 100644 (file)
@@ -18,6 +18,7 @@
 #include <asm/unistd.h>
 #include <asm/asm-compat.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
        .text
 
@@ -118,3 +119,4 @@ _GLOBAL(longjmp)
 _GLOBAL(current_stack_pointer)
        PPC_LL  r3,0(r1)
        blr
+EXPORT_SYMBOL(current_stack_pointer)
index 03756ff..93cf7a5 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/kexec.h>
 #include <asm/bug.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
        .text
 
@@ -319,6 +320,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_UNIFIED_ID_CACHE)
 #endif /* CONFIG_4xx */
        isync
        blr
+EXPORT_SYMBOL(flush_instruction_cache)
 #endif /* CONFIG_PPC_8xx */
 
 /*
@@ -359,6 +361,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        isync
        blr
 _ASM_NOKPROBE_SYMBOL(flush_icache_range)
+EXPORT_SYMBOL(flush_icache_range)
 
 /*
  * Flush a particular page from the data cache to RAM.
@@ -497,6 +500,7 @@ _GLOBAL(copy_page)
        li      r0,MAX_COPY_PREFETCH
        li      r11,4
        b       2b
+EXPORT_SYMBOL(copy_page)
 
 /*
  * Extended precision shifts.
@@ -524,6 +528,7 @@ _GLOBAL(__ashrdi3)
        sraw    r3,r3,r5        # MSW = MSW >> count
        or      r4,r4,r7        # LSW |= t2
        blr
+EXPORT_SYMBOL(__ashrdi3)
 
 _GLOBAL(__ashldi3)
        subfic  r6,r5,32
@@ -535,6 +540,7 @@ _GLOBAL(__ashldi3)
        slw     r4,r4,r5        # LSW = LSW << count
        or      r3,r3,r7        # MSW |= t2
        blr
+EXPORT_SYMBOL(__ashldi3)
 
 _GLOBAL(__lshrdi3)
        subfic  r6,r5,32
@@ -546,6 +552,7 @@ _GLOBAL(__lshrdi3)
        srw     r3,r3,r5        # MSW = MSW >> count
        or      r4,r4,r7        # LSW |= t2
        blr
+EXPORT_SYMBOL(__lshrdi3)
 
 /*
  * 64-bit comparison: __cmpdi2(s64 a, s64 b)
@@ -561,6 +568,7 @@ _GLOBAL(__cmpdi2)
        bltlr
        li      r3,2
        blr
+EXPORT_SYMBOL(__cmpdi2)
 /*
  * 64-bit comparison: __ucmpdi2(u64 a, u64 b)
  * Returns 0 if a < b, 1 if a == b, 2 if a > b.
@@ -575,6 +583,7 @@ _GLOBAL(__ucmpdi2)
        bltlr
        li      r3,2
        blr
+EXPORT_SYMBOL(__ucmpdi2)
 
 _GLOBAL(__bswapdi2)
        rotlwi  r9,r4,8
@@ -586,6 +595,7 @@ _GLOBAL(__bswapdi2)
        mr      r3,r9
        mr      r4,r10
        blr
+EXPORT_SYMBOL(__bswapdi2)
 
 #ifdef CONFIG_SMP
 _GLOBAL(start_secondary_resume)
index 9f0bed2..4f17867 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/kexec.h>
 #include <asm/ptrace.h>
 #include <asm/mmu.h>
+#include <asm/export.h>
 
        .text
 
@@ -110,6 +111,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        isync
        blr
 _ASM_NOKPROBE_SYMBOL(flush_icache_range)
+EXPORT_SYMBOL(flush_icache_range)
 
 /*
  * Like above, but only do the D-cache.
@@ -140,6 +142,7 @@ _GLOBAL(flush_dcache_range)
        bdnz    0b
        sync
        blr
+EXPORT_SYMBOL(flush_dcache_range)
 
 /*
  * Like above, but works on non-mapped physical addresses.
@@ -243,6 +246,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_COHERENT_ICACHE)
        blr
 
 _GLOBAL(__bswapdi2)
+EXPORT_SYMBOL(__bswapdi2)
        srdi    r8,r3,32
        rlwinm  r7,r3,8,0xffffffff
        rlwimi  r7,r3,24,0,7
index 95d3769..74bec54 100644 (file)
@@ -56,6 +56,7 @@ static DECLARE_BITMAP(phb_bitmap, MAX_PHBS);
 
 /* ISA Memory physical address */
 resource_size_t isa_mem_base;
+EXPORT_SYMBOL(isa_mem_base);
 
 
 static struct dma_map_ops *pci_dma_ops = &dma_direct_ops;
index 1f79300..678f87a 100644 (file)
@@ -32,6 +32,8 @@
 unsigned long isa_io_base     = 0;
 unsigned long pci_dram_offset = 0;
 int pcibios_assign_bus_offset = 1;
+EXPORT_SYMBOL(isa_io_base);
+EXPORT_SYMBOL(pci_dram_offset);
 
 void pcibios_make_OF_bus_map(void);
 
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
deleted file mode 100644 (file)
index 9f01e28..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#include <linux/ftrace.h>
-#include <linux/mm.h>
-
-#include <asm/processor.h>
-#include <asm/switch_to.h>
-#include <asm/cacheflush.h>
-#include <asm/epapr_hcalls.h>
-
-#ifdef CONFIG_PPC64
-EXPORT_SYMBOL(flush_dcache_range);
-#endif
-EXPORT_SYMBOL(flush_icache_range);
-
-EXPORT_SYMBOL(empty_zero_page);
-
-long long __bswapdi2(long long);
-EXPORT_SYMBOL(__bswapdi2);
-
-#ifdef CONFIG_FUNCTION_TRACER
-EXPORT_SYMBOL(_mcount);
-#endif
-
-#ifdef CONFIG_PPC_FPU
-EXPORT_SYMBOL(load_fp_state);
-EXPORT_SYMBOL(store_fp_state);
-#endif
-
-#ifdef CONFIG_ALTIVEC
-EXPORT_SYMBOL(load_vr_state);
-EXPORT_SYMBOL(store_vr_state);
-#endif
-
-#ifdef CONFIG_EPAPR_PARAVIRT
-EXPORT_SYMBOL(epapr_hypercall_start);
-#endif
-
-EXPORT_SYMBOL(current_stack_pointer);
diff --git a/arch/powerpc/kernel/ppc_ksyms_32.c b/arch/powerpc/kernel/ppc_ksyms_32.c
deleted file mode 100644 (file)
index 2bfaafe..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-#include <linux/export.h>
-#include <linux/smp.h>
-
-#include <asm/page.h>
-#include <asm/dma.h>
-#include <asm/io.h>
-#include <asm/hw_irq.h>
-#include <asm/time.h>
-#include <asm/mmu_context.h>
-#include <asm/pgtable.h>
-#include <asm/dcr.h>
-
-EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
-EXPORT_SYMBOL(DMA_MODE_READ);
-EXPORT_SYMBOL(DMA_MODE_WRITE);
-
-#if defined(CONFIG_PCI)
-EXPORT_SYMBOL(isa_io_base);
-EXPORT_SYMBOL(isa_mem_base);
-EXPORT_SYMBOL(pci_dram_offset);
-#endif
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(smp_hw_index);
-#endif
-
-long long __ashrdi3(long long, int);
-long long __ashldi3(long long, int);
-long long __lshrdi3(long long, int);
-int __ucmpdi2(unsigned long long, unsigned long long);
-int __cmpdi2(long long, long long);
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__ucmpdi2);
-EXPORT_SYMBOL(__cmpdi2);
-
-EXPORT_SYMBOL(timer_interrupt);
-EXPORT_SYMBOL(tb_ticks_per_jiffy);
-
-EXPORT_SYMBOL(switch_mmu_context);
-
-#ifdef CONFIG_PPC_STD_MMU_32
-extern long mol_trampoline;
-EXPORT_SYMBOL(mol_trampoline); /* For MOL */
-EXPORT_SYMBOL(flush_hash_pages); /* For MOL */
-#ifdef CONFIG_SMP
-extern int mmu_hash_lock;
-EXPORT_SYMBOL(mmu_hash_lock); /* For MOL */
-#endif /* CONFIG_SMP */
-extern long *intercept_table;
-EXPORT_SYMBOL(intercept_table);
-#endif /* CONFIG_PPC_STD_MMU_32 */
-
-#ifdef CONFIG_PPC_DCR_NATIVE
-EXPORT_SYMBOL(__mtdcr);
-EXPORT_SYMBOL(__mfdcr);
-#endif
-
-EXPORT_SYMBOL(flush_instruction_cache);
index dba265c..270ee30 100644 (file)
@@ -131,15 +131,26 @@ void machine_shutdown(void)
                ppc_md.machine_shutdown();
 }
 
+static void machine_hang(void)
+{
+       pr_emerg("System Halted, OK to turn off power\n");
+       local_irq_disable();
+       while (1)
+               ;
+}
+
 void machine_restart(char *cmd)
 {
        machine_shutdown();
        if (ppc_md.restart)
                ppc_md.restart(cmd);
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+
+       do_kernel_restart(cmd);
+       mdelay(1000);
+
+       machine_hang();
 }
 
 void machine_power_off(void)
@@ -147,10 +158,9 @@ void machine_power_off(void)
        machine_shutdown();
        if (pm_power_off)
                pm_power_off();
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+       machine_hang();
 }
 /* Used by the G5 thermal driver */
 EXPORT_SYMBOL_GPL(machine_power_off);
@@ -163,10 +173,9 @@ void machine_halt(void)
        machine_shutdown();
        if (ppc_md.halt)
                ppc_md.halt();
+
        smp_send_stop();
-       printk(KERN_EMERG "System Halted, OK to turn off power\n");
-       local_irq_disable();
-       while (1) ;
+       machine_hang();
 }
 
 
index 24ec3ea..5fe7918 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/cpu.h>
 #include <linux/console.h>
 #include <linux/memblock.h>
+#include <linux/export.h>
 
 #include <asm/io.h>
 #include <asm/prom.h>
@@ -47,11 +48,16 @@ int boot_cpuid_phys;
 EXPORT_SYMBOL_GPL(boot_cpuid_phys);
 
 int smp_hw_index[NR_CPUS];
+EXPORT_SYMBOL(smp_hw_index);
 
 unsigned long ISA_DMA_THRESHOLD;
 unsigned int DMA_MODE_READ;
 unsigned int DMA_MODE_WRITE;
 
+EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
+EXPORT_SYMBOL(DMA_MODE_READ);
+EXPORT_SYMBOL(DMA_MODE_WRITE);
+
 /*
  * These are used in binfmt_elf.c to put aux entries on the stack
  * for each elf executable being started.
index 67859b7..bc3f7d0 100644 (file)
@@ -596,6 +596,7 @@ void timer_interrupt(struct pt_regs * regs)
        irq_exit();
        set_irq_regs(old_regs);
 }
+EXPORT_SYMBOL(timer_interrupt);
 
 /*
  * Hypervisor decrementer interrupts shouldn't occur but are sometimes
index a1f8f56..023a462 100644 (file)
@@ -273,7 +273,6 @@ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
        force_sig_info(signr, &info, current);
 }
 
-#ifdef CONFIG_PPC64
 void system_reset_exception(struct pt_regs *regs)
 {
        /* See if any machine dependent calls */
@@ -291,6 +290,7 @@ void system_reset_exception(struct pt_regs *regs)
        /* What should we do here? We could issue a shutdown or hard reset. */
 }
 
+#ifdef CONFIG_PPC64
 /*
  * This function is called in real mode. Strictly no printk's please.
  *
@@ -352,12 +352,11 @@ static inline int check_io_access(struct pt_regs *regs)
                 * For the debug message, we look at the preceding
                 * load or store.
                 */
-               if (*nip == 0x60000000)         /* nop */
+               if (*nip == PPC_INST_NOP)
                        nip -= 2;
-               else if (*nip == 0x4c00012c)    /* isync */
+               else if (*nip == PPC_INST_ISYNC)
                        --nip;
-               if (*nip == 0x7c0004ac || (*nip >> 26) == 3) {
-                       /* sync or twi */
+               if (*nip == PPC_INST_SYNC || (*nip >> 26) == OP_TRAP) {
                        unsigned int rb;
 
                        --nip;
@@ -668,6 +667,31 @@ int machine_check_e200(struct pt_regs *regs)
 
        return 0;
 }
+#elif defined(CONFIG_PPC_8xx)
+int machine_check_8xx(struct pt_regs *regs)
+{
+       unsigned long reason = get_mc_reason(regs);
+
+       pr_err("Machine check in kernel mode.\n");
+       pr_err("Caused by (from SRR1=%lx): ", reason);
+       if (reason & 0x40000000)
+               pr_err("Fetch error at address %lx\n", regs->nip);
+       else
+               pr_err("Data access error at address %lx\n", regs->dar);
+
+#ifdef CONFIG_PCI
+       /* the qspan pci read routines can cause machine checks -- Cort
+        *
+        * yuck !!! that totally needs to go away ! There are better ways
+        * to deal with that than having a wart in the mcheck handler.
+        * -- BenH
+        */
+       bad_page_fault(regs, regs->dar, SIGBUS);
+       return 1;
+#else
+       return 0;
+#endif
+}
 #else
 int machine_check_generic(struct pt_regs *regs)
 {
@@ -727,17 +751,6 @@ void machine_check_exception(struct pt_regs *regs)
        if (recover > 0)
                goto bail;
 
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-       /* the qspan pci read routines can cause machine checks -- Cort
-        *
-        * yuck !!! that totally needs to go away ! There are better ways
-        * to deal with that than having a wart in the mcheck handler.
-        * -- BenH
-        */
-       bad_page_fault(regs, regs->dar, SIGBUS);
-       goto bail;
-#endif
-
        if (debugger_fault_handler(regs))
                goto bail;
 
index bc85bdf..0c123f3 100644 (file)
@@ -6,6 +6,7 @@
 #include <asm/thread_info.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /*
  * Load state from memory into VMX registers including VSCR.
@@ -17,6 +18,7 @@ _GLOBAL(load_vr_state)
        mtvscr  v0
        REST_32VRS(0,r4,r3)
        blr
+EXPORT_SYMBOL(load_vr_state)
 
 /*
  * Store VMX state into memory, including VSCR.
@@ -28,6 +30,7 @@ _GLOBAL(store_vr_state)
        li      r4, VRSTATE_VSCR
        stvx    v0, r4, r3
        blr
+EXPORT_SYMBOL(store_vr_state)
 
 /*
  * Disable VMX for the task which had it previously,
index ad52900..309361e 100644 (file)
@@ -9,7 +9,7 @@ ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
 CFLAGS_REMOVE_code-patching.o = $(CC_FLAGS_FTRACE)
 CFLAGS_REMOVE_feature-fixups.o = $(CC_FLAGS_FTRACE)
 
-obj-y += string.o alloc.o crtsavres.o ppc_ksyms.o code-patching.o \
+obj-y += string.o alloc.o crtsavres.o code-patching.o \
         feature-fixups.o
 
 obj-$(CONFIG_PPC32)    += div64.o copy_32.o
index aa8214f..ea29a5d 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .text
 
@@ -68,6 +69,7 @@ _GLOBAL(__csum_partial)
        adde    r5,r5,r0
 5:     addze   r3,r5           /* add in final carry */
        blr
+EXPORT_SYMBOL(__csum_partial)
 
 /*
  * Computes the checksum of a memory block at src, length len,
@@ -297,3 +299,4 @@ dst_error:
        .long   41b,dst_error
        .long   50b,src_error
        .long   51b,dst_error
+EXPORT_SYMBOL(csum_partial_copy_generic)
index fdec6e6..fd91766 100644 (file)
@@ -16,6 +16,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 /*
  * Computes the checksum of a memory block at buff, length len,
@@ -176,6 +177,7 @@ _GLOBAL(__csum_partial)
        add     r3,r4,r0
        srdi    r3,r3,32
        blr
+EXPORT_SYMBOL(__csum_partial)
 
 
        .macro srcnr
@@ -430,3 +432,4 @@ dstnr;      stb     r6,0(r4)
        li      r6,-EFAULT
        stw     r6,0(r8)
        blr
+EXPORT_SYMBOL(csum_partial_copy_generic)
index 99f37f2..40cce33 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/cache.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #define COPY_16_BYTES          \
        lwz     r7,4(r4);       \
@@ -92,6 +93,7 @@ _GLOBAL(memset)
        subf    r6,r0,r6
        cmplwi  0,r4,0
        bne     2f      /* Use normal procedure if r4 is not zero */
+EXPORT_SYMBOL(memset)
 _GLOBAL(memset_nocache_branch)
        b       2f      /* Skip optimised bloc until cache is enabled */
 
@@ -216,6 +218,8 @@ _GLOBAL(memcpy)
        stbu    r0,1(r6)
        bdnz    40b
 65:    blr
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(memmove)
 
 generic_memcpy:
        srwi.   r7,r5,3
@@ -507,3 +511,4 @@ _GLOBAL(__copy_tofrom_user)
        .long   112b,120b
        .long   114b,120b
        .text
+EXPORT_SYMBOL(__copy_tofrom_user)
index a3c4dc4..21367b3 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
         .section        ".toc","aw"
 PPC64_CACHES:
@@ -110,3 +111,4 @@ END_FTR_SECTION_IFSET(CPU_FTR_CP_USE_DCBTZ)
        std     r11,120(r3)
        std     r12,128(r3)
        blr
+EXPORT_SYMBOL(copy_page)
index f09899e..60386b2 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #ifdef __BIG_ENDIAN__
 #define sLd sld                /* Shift towards low-numbered address. */
@@ -359,6 +360,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
        addi    r3,r3,8
 171:
 177:
+179:
        addi    r3,r3,8
 370:
 372:
@@ -373,7 +375,6 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
 173:
 174:
 175:
-179:
 181:
 184:
 186:
@@ -671,3 +672,4 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
        .llong  89b,100b
        .llong  90b,100b
        .llong  91b,100b
+EXPORT_SYMBOL(__copy_tofrom_user)
index 19e6600..3de7ac1 100644 (file)
@@ -19,6 +19,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 /* Note: This code relies on -mminimal-toc */
 
@@ -32,6 +33,7 @@ FTR_SECTION_ELSE
        clrldi  r3,r3,64-8
        blr
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight8)
 
 _GLOBAL(__arch_hweight16)
 BEGIN_FTR_SECTION
@@ -54,6 +56,7 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 50)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight16)
 
 _GLOBAL(__arch_hweight32)
 BEGIN_FTR_SECTION
@@ -79,6 +82,7 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 51)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight32)
 
 _GLOBAL(__arch_hweight64)
 BEGIN_FTR_SECTION
@@ -108,3 +112,4 @@ FTR_SECTION_ELSE
        blr
   ALT_FTR_SECTION_END_NESTED_IFCLR(CPU_FTR_POPCNTD, 52)
 ALT_FTR_SECTION_END_IFCLR(CPU_FTR_POPCNTB)
+EXPORT_SYMBOL(__arch_hweight64)
index eda7a96..85fa986 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 _GLOBAL(memset)
        neg     r0,r3
@@ -77,6 +78,7 @@ _GLOBAL(memset)
 10:    bflr    31
        stb     r4,0(r6)
        blr
+EXPORT_SYMBOL(memset)
 
 _GLOBAL_TOC(memmove)
        cmplw   0,r3,r4
@@ -119,3 +121,4 @@ _GLOBAL(backwards_memcpy)
        beq     2b
        mtctr   r7
        b       1b
+EXPORT_SYMBOL(memmove)
index 8953d23..d75d18b 100644 (file)
@@ -8,6 +8,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
 #define off8   r6
 #define off16  r7
@@ -231,3 +232,4 @@ _GLOBAL(memcmp)
        ld      r28,-32(r1)
        ld      r27,-40(r1)
        blr
+EXPORT_SYMBOL(memcmp)
index 32a06ec..f4d6088 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include <asm/processor.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .align  7
 _GLOBAL_TOC(memcpy)
@@ -219,3 +220,4 @@ END_FTR_SECTION_IFCLR(CPU_FTR_UNALIGNED_LD_STD)
 4:     ld      r3,-STACKFRAMESIZE+STK_REG(R31)(r1)     /* return dest pointer */
        blr
 #endif
+EXPORT_SYMBOL(memcpy)
diff --git a/arch/powerpc/lib/ppc_ksyms.c b/arch/powerpc/lib/ppc_ksyms.c
deleted file mode 100644 (file)
index ae69d84..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#include <linux/string.h>
-#include <linux/uaccess.h>
-#include <linux/bitops.h>
-#include <net/checksum.h>
-
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memchr);
-
-EXPORT_SYMBOL(strncpy);
-EXPORT_SYMBOL(strncmp);
-
-#ifndef CONFIG_GENERIC_CSUM
-EXPORT_SYMBOL(__csum_partial);
-EXPORT_SYMBOL(csum_partial_copy_generic);
-#endif
-
-EXPORT_SYMBOL(__copy_tofrom_user);
-EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(copy_page);
-
-#ifdef CONFIG_PPC64
-EXPORT_SYMBOL(__arch_hweight8);
-EXPORT_SYMBOL(__arch_hweight16);
-EXPORT_SYMBOL(__arch_hweight32);
-EXPORT_SYMBOL(__arch_hweight64);
-#endif
index beabc68..d13e076 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/processor.h>
 #include <asm/errno.h>
 #include <asm/ppc_asm.h>
+#include <asm/export.h>
 
        .section __ex_table,"a"
        PPC_LONG_ALIGN
@@ -36,6 +37,7 @@ _GLOBAL(strncpy)
 2:     stbu    r0,1(r6)        /* clear it out if so */
        bdnz    2b
        blr
+EXPORT_SYMBOL(strncpy)
 
 _GLOBAL(strncmp)
        PPC_LCMPI 0,r5,0
@@ -53,6 +55,7 @@ _GLOBAL(strncmp)
        blr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(strncmp)
 
 #ifdef CONFIG_PPC32
 _GLOBAL(memcmp)
@@ -68,6 +71,7 @@ _GLOBAL(memcmp)
        blr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(memcmp)
 #endif
 
 _GLOBAL(memchr)
@@ -82,6 +86,7 @@ _GLOBAL(memchr)
        beqlr
 2:     li      r3,0
        blr
+EXPORT_SYMBOL(memchr)
 
 #ifdef CONFIG_PPC32
 _GLOBAL(__clear_user)
@@ -125,4 +130,5 @@ _GLOBAL(__clear_user)
        PPC_LONG        1b,91b
        PPC_LONG        8b,92b
        .text
+EXPORT_SYMBOL(__clear_user)
 #endif
index 7bd9549..57ace35 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <asm/ppc_asm.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
        .section        ".toc","aw"
 PPC64_CACHES:
@@ -200,3 +201,4 @@ err1;       dcbz    r0,r3
        cmpdi   r4,32
        blt     .Lshort_clear
        b       .Lmedium_clear
+EXPORT_SYMBOL(__clear_user)
index 115347f..09cc50c 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/thread_info.h>
 #include <asm/asm-offsets.h>
+#include <asm/export.h>
 
 #ifdef CONFIG_SMP
        .section .bss
@@ -33,6 +34,7 @@
        .globl mmu_hash_lock
 mmu_hash_lock:
        .space  4
+EXPORT_SYMBOL(mmu_hash_lock)
 #endif /* CONFIG_SMP */
 
 /*
@@ -575,6 +577,7 @@ _GLOBAL(flush_hash_pages)
        rlwinm  r8,r8,0,31,29           /* clear HASHPTE bit */
        stwcx.  r8,0,r5                 /* update the pte */
        bne-    33b
+EXPORT_SYMBOL(flush_hash_pages)
 
        /* Get the address of the primary PTE group in the hash table (r3) */
 _GLOBAL(flush_hash_patch_A)
index 90480e2..44d3c3a 100644 (file)
@@ -529,7 +529,7 @@ static bool might_have_hea(void)
         */
 #ifdef CONFIG_IBMEBUS
        return !cpu_has_feature(CPU_FTR_ARCH_207S) &&
-               !firmware_has_feature(FW_FEATURE_SPLPAR);
+               firmware_has_feature(FW_FEATURE_SPLPAR);
 #else
        return false;
 #endif
index 7c7df40..994d1a9 100644 (file)
@@ -30,8 +30,8 @@ config EP8248E
        select 8272
        select 8260
        select FSL_SOC
-       select PHYLIB
-       select MDIO_BITBANG
+       select PHYLIB if NETDEVICES
+       select MDIO_BITBANG if PHYLIB
        help
          This enables support for the Embedded Planet EP8248E board.
 
index cdab847..8fec050 100644 (file)
@@ -298,7 +298,9 @@ static const struct of_device_id of_bus_ids[] __initconst = {
 static int __init declare_of_platform_devices(void)
 {
        of_platform_bus_probe(NULL, of_bus_ids, NULL);
-       platform_driver_register(&ep8248e_mdio_driver);
+
+       if (IS_ENABLED(CONFIG_MDIO_BITBANG))
+               platform_driver_register(&ep8248e_mdio_driver);
 
        return 0;
 }
index 17e5433..575afd6 100644 (file)
@@ -30,9 +30,7 @@
  */
 static void __init asp834x_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("asp834x_setup_arch()", 0);
-
+       mpc83xx_setup_arch();
        mpc834x_usb_cfg();
 }
 
index e7fbd63..d8642a4 100644 (file)
@@ -130,10 +130,7 @@ static void __init mpc83xx_km_setup_arch(void)
        struct device_node *np;
 #endif
 
-       if (ppc_md.progress)
-               ppc_md.progress("kmpbec83xx_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
 #ifdef CONFIG_QUICC_ENGINE
        np = of_find_node_by_name(NULL, "par_io");
index 8899aa9..d75c981 100644 (file)
@@ -142,3 +142,11 @@ void __init mpc83xx_setup_pci(void)
                mpc83xx_add_bridge(np);
 }
 #endif
+
+void __init mpc83xx_setup_arch(void)
+{
+       if (ppc_md.progress)
+               ppc_md.progress("mpc83xx_setup_arch()", 0);
+
+       mpc83xx_setup_pci();
+}
index 040d5d0..272c41c 100644 (file)
  */
 static void __init mpc830x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc830x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc831x_usb_cfg();
 }
 
index 40e0d83..fd80fd5 100644 (file)
  */
 static void __init mpc831x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc831x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc831x_usb_cfg();
 }
 
index cdfa47c..bb7b25a 100644 (file)
@@ -58,8 +58,7 @@ static void __init mpc832x_sys_setup_arch(void)
        struct device_node *np;
        u8 __iomem *bcsr_regs = NULL;
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc832x_sys_setup_arch()", 0);
+       mpc83xx_setup_arch();
 
        /* Map BCSR area */
        np = of_find_node_by_name(NULL, "bcsr");
@@ -71,8 +70,6 @@ static void __init mpc832x_sys_setup_arch(void)
                of_node_put(np);
        }
 
-       mpc83xx_setup_pci();
-
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
                par_io_init(np);
index 0d6a62f..d7c9b18 100644 (file)
@@ -197,10 +197,7 @@ static void __init mpc832x_rdb_setup_arch(void)
        struct device_node *np;
 #endif
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc832x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
index 8fd0c1e..73a5267 100644 (file)
@@ -57,10 +57,7 @@ machine_device_initcall(mpc834x_itx, mpc834x_itx_declare_of_platform_devices);
  */
 static void __init mpc834x_itx_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc834x_itx_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
        mpc834x_usb_cfg();
 }
index eeaee61..009cfc1 100644 (file)
@@ -76,10 +76,7 @@ static int mpc834xemds_usb_cfg(void)
  */
 static void __init mpc834x_mds_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc834x_mds_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 
        mpc834xemds_usb_cfg();
 }
index dacf4c2..4fc3051 100644 (file)
@@ -66,8 +66,7 @@ static void __init mpc836x_mds_setup_arch(void)
        struct device_node *np;
        u8 __iomem *bcsr_regs = NULL;
 
-       if (ppc_md.progress)
-               ppc_md.progress("mpc836x_mds_setup_arch()", 0);
+       mpc83xx_setup_arch();
 
        /* Map BCSR area */
        np = of_find_node_by_name(NULL, "bcsr");
@@ -79,8 +78,6 @@ static void __init mpc836x_mds_setup_arch(void)
                of_node_put(np);
        }
 
-       mpc83xx_setup_pci();
-
 #ifdef CONFIG_QUICC_ENGINE
        if ((np = of_find_node_by_name(NULL, "par_io")) != NULL) {
                par_io_init(np);
index cf67ac9..93f024f 100644 (file)
@@ -31,10 +31,7 @@ machine_device_initcall(mpc836x_rdk, mpc83xx_declare_of_platform_devices);
 
 static void __init mpc836x_rdk_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc836x_rdk_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 }
 
 /*
index 652b97d..3b34cc1 100644 (file)
@@ -79,10 +79,7 @@ out:
  */
 static void __init mpc837x_mds_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc837x_mds_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc837xmds_usb_cfg();
 }
 
index 667731d..0c55fa6 100644 (file)
@@ -50,10 +50,7 @@ static void mpc837x_rdb_sd_cfg(void)
  */
 static void __init mpc837x_rdb_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("mpc837x_rdb_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
        mpc837x_usb_cfg();
        mpc837x_rdb_sd_cfg();
 }
index ad48419..636eb9d 100644 (file)
@@ -86,5 +86,6 @@ extern void mpc83xx_setup_pci(void);
 #endif
 
 extern int mpc83xx_declare_of_platform_devices(void);
+extern void mpc83xx_setup_arch(void);
 
 #endif                         /* __MPC83XX_H__ */
index b867e88..cb4bdab 100644 (file)
  */
 static void __init sbc834x_setup_arch(void)
 {
-       if (ppc_md.progress)
-               ppc_md.progress("sbc834x_setup_arch()", 0);
-
-       mpc83xx_setup_pci();
+       mpc83xx_setup_arch();
 }
 
 machine_device_initcall(sbc834x, mpc83xx_declare_of_platform_devices);
index df25a3e..9dc1d28 100644 (file)
@@ -72,7 +72,7 @@ config MPC85xx_CDS
 config MPC85xx_MDS
        bool "Freescale MPC85xx MDS"
        select DEFAULT_UIMAGE
-       select PHYLIB
+       select PHYLIB if NETDEVICES
        select HAS_RAPIDIO
        select SWIOTLB
        help
index 07dd6ae..d2f4556 100644 (file)
@@ -72,7 +72,6 @@ define_machine(bsc9132_qds) {
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index e48f671..0ffdb4a 100644 (file)
@@ -59,7 +59,6 @@ define_machine(bsc9131_rdb) {
        .setup_arch             = bsc913x_rdb_setup_arch,
        .init_IRQ               = bsc913x_rdb_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 3b9e3f0..4df1b40 100644 (file)
@@ -65,7 +65,6 @@ define_machine(c293_pcie) {
        .setup_arch             = c293_pcie_setup_arch,
        .init_IRQ               = c293_pcie_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 3a6a84f..1179115 100644 (file)
@@ -225,7 +225,6 @@ define_machine(corenet_generic) {
 #else
        .get_irq                = mpic_get_coreint_irq,
 #endif
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 #ifdef CONFIG_PPC64
index 14af36a..f29c6f0 100644 (file)
@@ -215,7 +215,6 @@ define_machine(ge_imp3a) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 6ba687f..94a7f92 100644 (file)
@@ -77,7 +77,6 @@ define_machine(mpc8536_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 8756715..f3e055f 100644 (file)
@@ -170,7 +170,6 @@ define_machine(mpc85xx_ads) {
        .init_IRQ               = mpc85xx_ads_pic_init,
        .show_cpuinfo           = mpc85xx_ads_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 86f2015..224db30 100644 (file)
@@ -83,7 +83,8 @@ static int mpc85xx_exclude_device(struct pci_controller *hose,
                return PCIBIOS_SUCCESSFUL;
 }
 
-static void __noreturn mpc85xx_cds_restart(char *cmd)
+static int mpc85xx_cds_restart(struct notifier_block *this,
+                              unsigned long mode, void *cmd)
 {
        struct pci_dev *dev;
        u_char tmp;
@@ -108,12 +109,25 @@ static void __noreturn mpc85xx_cds_restart(char *cmd)
        }
 
        /*
-        *  If we can't find the VIA chip (maybe the P2P bridge is disabled)
-        *  or the VIA chip reset didn't work, just use the default reset.
+        *  If we can't find the VIA chip (maybe the P2P bridge is
+        *  disabled) or the VIA chip reset didn't work, just return
+        *  and let default reset sequence happen.
         */
-       fsl_rstcr_restart(NULL);
+       return NOTIFY_DONE;
 }
 
+static int mpc85xx_cds_restart_register(void)
+{
+       static struct notifier_block restart_handler;
+
+       restart_handler.notifier_call = mpc85xx_cds_restart;
+       restart_handler.priority = 192;
+
+       return register_restart_handler(&restart_handler);
+}
+machine_arch_initcall(mpc85xx_cds, mpc85xx_cds_restart_register);
+
+
 static void __init mpc85xx_cds_pci_irq_fixup(struct pci_dev *dev)
 {
        u_char c;
@@ -380,11 +394,8 @@ define_machine(mpc85xx_cds) {
        .show_cpuinfo   = mpc85xx_cds_show_cpuinfo,
        .get_irq        = mpic_get_irq,
 #ifdef CONFIG_PCI
-       .restart        = mpc85xx_cds_restart,
        .pcibios_fixup_bus      = mpc85xx_cds_fixup_bus,
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
-#else
-       .restart        = fsl_rstcr_restart,
 #endif
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
index ed69c7e..dc9e035 100644 (file)
@@ -204,7 +204,6 @@ define_machine(mpc8544_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -219,7 +218,6 @@ define_machine(mpc8572_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -234,7 +232,6 @@ define_machine(p2020_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index fa9cd71..d7e440e 100644 (file)
@@ -63,6 +63,8 @@
 #define DBG(fmt...)
 #endif
 
+#if IS_BUILTIN(CONFIG_PHYLIB)
+
 #define MV88E1111_SCR  0x10
 #define MV88E1111_SCR_125CLK   0x0010
 static int mpc8568_fixup_125_clock(struct phy_device *phydev)
@@ -152,6 +154,8 @@ static int mpc8568_mds_phy_fixups(struct phy_device *phydev)
        return err;
 }
 
+#endif
+
 /* ************************************************************************
  *
  * Setup the architecture
@@ -313,6 +317,7 @@ static void __init mpc85xx_mds_setup_arch(void)
        swiotlb_detect_4g();
 }
 
+#if IS_BUILTIN(CONFIG_PHYLIB)
 
 static int __init board_fixups(void)
 {
@@ -342,9 +347,12 @@ static int __init board_fixups(void)
 
        return 0;
 }
+
 machine_arch_initcall(mpc8568_mds, board_fixups);
 machine_arch_initcall(mpc8569_mds, board_fixups);
 
+#endif
+
 static int __init mpc85xx_publish_devices(void)
 {
        if (machine_is(mpc8568_mds))
@@ -385,7 +393,6 @@ define_machine(mpc8568_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -405,7 +412,6 @@ define_machine(mpc8569_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -426,7 +432,6 @@ define_machine(p1021_mds) {
        .setup_arch     = mpc85xx_mds_setup_arch,
        .init_IRQ       = mpc85xx_mds_pic_init,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 #ifdef CONFIG_PCI
@@ -434,4 +439,3 @@ define_machine(p1021_mds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
 };
-
index c1499cb..1006950 100644 (file)
@@ -213,7 +213,6 @@ define_machine(p2020_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -228,7 +227,6 @@ define_machine(p1020_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -243,7 +241,6 @@ define_machine(p1021_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -258,7 +255,6 @@ define_machine(p2020_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -273,7 +269,6 @@ define_machine(p1025_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -288,7 +283,6 @@ define_machine(p1020_mbg_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -303,7 +297,6 @@ define_machine(p1020_utm_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -318,7 +311,6 @@ define_machine(p1020_rdb_pc) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -333,7 +325,6 @@ define_machine(p1020_rdb_pd) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -348,7 +339,6 @@ define_machine(p1024_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index acc3d0d..d5af072 100644 (file)
@@ -66,7 +66,6 @@ define_machine(mvme2500) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 661d7b5..78d13b3 100644 (file)
@@ -79,7 +79,6 @@ define_machine(p1010_rdb) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 63568d6..0908abd 100644 (file)
@@ -568,7 +568,6 @@ define_machine(p1022_ds) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 2f29436..276e00a 100644 (file)
@@ -148,7 +148,6 @@ define_machine(p1022_rdk) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 40d8de5..3e8cd03 100644 (file)
@@ -110,7 +110,6 @@ define_machine(p1023_rdb) {
        .setup_arch             = mpc85xx_rdb_setup_arch,
        .init_IRQ               = mpc85xx_rdb_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 #ifdef CONFIG_PCI
index 2410167..33c5ba6 100644 (file)
@@ -91,7 +91,6 @@ define_machine(ppa8548) {
        .init_IRQ       = ppa8548_pic_init,
        .show_cpuinfo   = ppa8548_show_cpuinfo,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
        .calibrate_decr = generic_calibrate_decr,
        .progress       = udbg_progress,
 };
index 50d7458..b63a854 100644 (file)
@@ -77,7 +77,6 @@ define_machine(qemu_e500) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_coreint_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 62b6c45..2c67084 100644 (file)
@@ -130,7 +130,6 @@ define_machine(sbc8548) {
        .init_IRQ       = sbc8548_pic_init,
        .show_cpuinfo   = sbc8548_show_cpuinfo,
        .get_irq        = mpic_get_irq,
-       .restart        = fsl_rstcr_restart,
 #ifdef CONFIG_PCI
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
index 79fd0df..21d6aaa 100644 (file)
@@ -38,18 +38,18 @@ static void gpio_halt_wfn(struct work_struct *work)
 }
 static DECLARE_WORK(gpio_halt_wq, gpio_halt_wfn);
 
-static void gpio_halt_cb(void)
+static void __noreturn gpio_halt_cb(void)
 {
        enum of_gpio_flags flags;
        int trigger, gpio;
 
        if (!halt_node)
-               return;
+               panic("No reset GPIO information was provided in DT\n");
 
        gpio = of_get_gpio_flags(halt_node, 0, &flags);
 
        if (!gpio_is_valid(gpio))
-               return;
+               panic("Provided GPIO is invalid\n");
 
        trigger = (flags == OF_GPIO_ACTIVE_LOW);
 
@@ -57,6 +57,8 @@ static void gpio_halt_cb(void)
 
        /* Probably wont return */
        gpio_set_value(gpio, trigger);
+
+       panic("Halt failed\n");
 }
 
 /* This IRQ means someone pressed the power button and it is waiting for us
index cd255ac..8da4ed9 100644 (file)
@@ -91,7 +91,6 @@ define_machine(socrates) {
        .setup_arch             = socrates_setup_arch,
        .init_IRQ               = socrates_pic_init,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 91b824c..1a1d44e 100644 (file)
@@ -103,7 +103,6 @@ define_machine(stx_gp3) {
        .init_IRQ               = stx_gp3_pic_init,
        .show_cpuinfo           = stx_gp3_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index b7c5445..9fc20a3 100644 (file)
@@ -132,7 +132,6 @@ define_machine(tqm85xx) {
        .init_IRQ               = tqm85xx_pic_init,
        .show_cpuinfo           = tqm85xx_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index 1bc02a8..360f625 100644 (file)
@@ -140,7 +140,6 @@ define_machine(twr_p1025) {
        .pcibios_fixup_bus      = fsl_pcibios_fixup_bus,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index cf0c70f..cd6ce84 100644 (file)
@@ -167,7 +167,6 @@ define_machine(xes_mpc8572) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -182,7 +181,6 @@ define_machine(xes_mpc8548) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
@@ -197,7 +195,6 @@ define_machine(xes_mpc8540) {
        .pcibios_fixup_phb      = fsl_pcibios_fixup_phb,
 #endif
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
 };
index ef684af..6b99300 100644 (file)
@@ -204,7 +204,6 @@ define_machine(gef_ppc9a) {
        .init_IRQ               = gef_ppc9a_init_irq,
        .show_cpuinfo           = gef_ppc9a_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 67dd0c2..8cdeca0 100644 (file)
@@ -191,7 +191,6 @@ define_machine(gef_sbc310) {
        .init_IRQ               = gef_sbc310_init_irq,
        .show_cpuinfo           = gef_sbc310_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 8050269..da8723a 100644 (file)
@@ -181,7 +181,6 @@ define_machine(gef_sbc610) {
        .init_IRQ               = gef_sbc610_init_irq,
        .show_cpuinfo           = gef_sbc610_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index fef0582..a5d73fa 100644 (file)
@@ -331,7 +331,6 @@ define_machine(mpc86xx_hpcd) {
        .setup_arch             = mpc86xx_hpcd_setup_arch,
        .init_IRQ               = mpc86xx_init_irq,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 5ae42a0..a0e989e 100644 (file)
@@ -130,7 +130,6 @@ define_machine(mpc86xx_hpcn) {
        .init_IRQ               = mpc86xx_init_irq,
        .show_cpuinfo           = mpc86xx_hpcn_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index addb41e..835352e 100644 (file)
@@ -111,7 +111,6 @@ define_machine(mvme7100) {
        .setup_arch             = mvme7100_setup_arch,
        .init_IRQ               = mpc86xx_init_irq,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 52af573..93db35d 100644 (file)
@@ -82,7 +82,6 @@ define_machine(sbc8641) {
        .init_IRQ               = mpc86xx_init_irq,
        .show_cpuinfo           = sbc8641_show_cpuinfo,
        .get_irq                = mpic_get_irq,
-       .restart                = fsl_rstcr_restart,
        .time_init              = mpc86xx_time_init,
        .calibrate_decr         = generic_calibrate_decr,
        .progress               = udbg_progress,
index 86707e6..aa35245 100644 (file)
@@ -393,7 +393,7 @@ static void __pSeries_lpar_hugepage_invalidate(unsigned long *slot,
                                             unsigned long *vpn, int count,
                                             int psize, int ssize)
 {
-       unsigned long param[8];
+       unsigned long param[PLPAR_HCALL9_BUFSIZE];
        int i = 0, pix = 0, rc;
        unsigned long flags = 0;
        int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
@@ -522,7 +522,7 @@ static void pSeries_lpar_flush_hash_range(unsigned long number, int local)
        unsigned long flags = 0;
        struct ppc64_tlb_batch *batch = this_cpu_ptr(&ppc64_tlb_batch);
        int lock_tlbie = !mmu_has_feature(MMU_FTR_LOCKLESS_TLBIE);
-       unsigned long param[9];
+       unsigned long param[PLPAR_HCALL9_BUFSIZE];
        unsigned long hash, index, shift, hidx, slot;
        real_pte_t pte;
        int psize, ssize;
index 2e4ebd0..ec2d5c8 100755 (executable)
@@ -30,6 +30,7 @@ bad_relocs=$(
        # On PPC64:
        #       R_PPC64_RELATIVE, R_PPC64_NONE
        #       R_PPC64_ADDR64 mach_<name>
+       #       R_PPC64_ADDR64 __crc_<name>
        # On PPC:
        #       R_PPC_RELATIVE, R_PPC_ADDR16_HI,
        #       R_PPC_ADDR16_HA,R_PPC_ADDR16_LO,
@@ -41,7 +42,8 @@ R_PPC_ADDR16_HI
 R_PPC_ADDR16_HA
 R_PPC_RELATIVE
 R_PPC_NONE' |
-       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+mach_'
+       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+mach_' |
+       grep -E -v '\<R_PPC64_ADDR64[[:space:]]+__crc_'
 )
 
 if [ -z "$bad_relocs" ]; then
index 3c0eb9b..986cd11 100644 (file)
@@ -233,8 +233,6 @@ void __init cpm_reset(void)
        else
                out_be32(&siu_conf->sc_sdcr, 1);
        immr_unmap(siu_conf);
-
-       cpm_muram_init();
 }
 
 static DEFINE_SPINLOCK(cmd_lock);
index 8dc1e24..f78ff84 100644 (file)
@@ -66,10 +66,6 @@ void __init cpm2_reset(void)
        cpm2_immr = ioremap(get_immrbase(), CPM_MAP_SIZE);
 #endif
 
-       /* Reclaim the DP memory for our use.
-        */
-       cpm_muram_init();
-
        /* Tell everyone where the comm processor resides.
         */
        cpmp = &cpm2_immr->im_cpm;
index 947f420..51bf749 100644 (file)
 #include <linux/of_gpio.h>
 #endif
 
+static int __init cpm_init(void)
+{
+       struct device_node *np;
+
+       np = of_find_compatible_node(NULL, NULL, "fsl,cpm1");
+       if (!np)
+               np = of_find_compatible_node(NULL, NULL, "fsl,cpm2");
+       if (!np)
+               return -ENODEV;
+       cpm_muram_init();
+       of_node_put(np);
+       return 0;
+}
+subsys_initcall(cpm_init);
+
 #ifdef CONFIG_PPC_EARLY_DEBUG_CPM
 static u32 __iomem *cpm_udbg_txdesc;
 static u8 __iomem *cpm_udbg_txbuf;
index d3098ef..e687bb2 100644 (file)
@@ -12,6 +12,7 @@
 #include <asm/ppc_asm.h>
 #include <asm/processor.h>
 #include <asm/bug.h>
+#include <asm/export.h>
 
 #define DCR_ACCESS_PROLOG(table) \
        cmpli   cr0,r3,1024;     \
 
 _GLOBAL(__mfdcr)
        DCR_ACCESS_PROLOG(__mfdcr_table)
+EXPORT_SYMBOL(__mfdcr)
 
 _GLOBAL(__mtdcr)
        DCR_ACCESS_PROLOG(__mtdcr_table)
+EXPORT_SYMBOL(__mtdcr)
 
 __mfdcr_table:
        mfdcr  r3,0; blr
index 0ef9df4..d3a5974 100644 (file)
@@ -111,8 +111,7 @@ static struct pci_ops fsl_indirect_pcie_ops =
        .write = indirect_write_config,
 };
 
-#define MAX_PHYS_ADDR_BITS     40
-static u64 pci64_dma_offset = 1ull << MAX_PHYS_ADDR_BITS;
+static u64 pci64_dma_offset;
 
 #ifdef CONFIG_SWIOTLB
 static void setup_swiotlb_ops(struct pci_controller *hose)
@@ -132,12 +131,10 @@ static int fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask)
                return -EIO;
 
        /*
-        * Fixup PCI devices that are able to DMA to above the physical
-        * address width of the SoC such that we can address any internal
-        * SoC address from across PCI if needed
+        * Fix up PCI devices that are able to DMA to the large inbound
+        * mapping that allows addressing any RAM address from across PCI.
         */
-       if ((dev_is_pci(dev)) &&
-           dma_mask >= DMA_BIT_MASK(MAX_PHYS_ADDR_BITS)) {
+       if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) {
                set_dma_ops(dev, &dma_direct_ops);
                set_dma_offset(dev, pci64_dma_offset);
        }
@@ -387,6 +384,7 @@ static void setup_pci_atmu(struct pci_controller *hose)
                                mem_log++;
 
                        piwar = (piwar & ~PIWAR_SZ_MASK) | (mem_log - 1);
+                       pci64_dma_offset = 1ULL << mem_log;
 
                        if (setup_inbound) {
                                /* Setup inbound memory window */
index a09ca70..d93056e 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/fsl_devices.h>
 #include <linux/fs_enet_pd.h>
 #include <linux/fs_uart_pd.h>
+#include <linux/reboot.h>
 
 #include <linux/atomic.h>
 #include <asm/io.h>
@@ -180,23 +181,38 @@ EXPORT_SYMBOL(get_baudrate);
 #if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx)
 static __be32 __iomem *rstcr;
 
+static int fsl_rstcr_restart(struct notifier_block *this,
+                            unsigned long mode, void *cmd)
+{
+       local_irq_disable();
+       /* set reset control register */
+       out_be32(rstcr, 0x2);   /* HRESET_REQ */
+
+       return NOTIFY_DONE;
+}
+
 static int __init setup_rstcr(void)
 {
        struct device_node *np;
 
+       static struct notifier_block restart_handler = {
+               .notifier_call = fsl_rstcr_restart,
+               .priority = 128,
+       };
+
        for_each_node_by_name(np, "global-utilities") {
                if ((of_get_property(np, "fsl,has-rstcr", NULL))) {
                        rstcr = of_iomap(np, 0) + 0xb0;
-                       if (!rstcr)
+                       if (!rstcr) {
                                printk (KERN_ERR "Error: reset control "
                                                "register not mapped!\n");
+                       } else {
+                               register_restart_handler(&restart_handler);
+                       }
                        break;
                }
        }
 
-       if (!rstcr && ppc_md.restart == fsl_rstcr_restart)
-               printk(KERN_ERR "No RSTCR register, warm reboot won't work\n");
-
        of_node_put(np);
 
        return 0;
@@ -204,15 +220,6 @@ static int __init setup_rstcr(void)
 
 arch_initcall(setup_rstcr);
 
-void __noreturn fsl_rstcr_restart(char *cmd)
-{
-       local_irq_disable();
-       if (rstcr)
-               /* set reset control register */
-               out_be32(rstcr, 0x2);   /* HRESET_REQ */
-
-       while (1) ;
-}
 #endif
 
 #if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
index 433566a..d73daa4 100644 (file)
@@ -19,8 +19,6 @@ extern u32 fsl_get_sys_freq(void);
 struct spi_board_info;
 struct device_node;
 
-extern void __noreturn fsl_rstcr_restart(char *cmd);
-
 /* The different ports that the DIU can be connected to */
 enum fsl_diu_monitor_port {
        FSL_DIU_PORT_DVI,       /* DVI */
index 4d48cec..b9aac95 100644 (file)
@@ -1249,7 +1249,7 @@ struct mpic * __init mpic_alloc(struct device_node *node,
        /* Pick the physical address from the device tree if unspecified */
        if (!phys_addr) {
                /* Check if it is DCR-based */
-               if (of_get_property(node, "dcr-reg", NULL)) {
+               if (of_property_read_bool(node, "dcr-reg")) {
                        flags |= MPIC_USES_DCR;
                } else {
                        struct resource r;
index 9043d2e..20f196b 100644 (file)
@@ -1,6 +1,7 @@
 
 
 generic-y += clkdev.h
+generic-y += export.h
 generic-y += irq_work.h
 generic-y += mcs_spinlock.h
 generic-y += mm-arch-hooks.h
index 72ccc41..1f0fe98 100644 (file)
@@ -61,7 +61,7 @@ obj-y += entry.o reipl.o relocate_kernel.o
 
 extra-y                                += head.o head64.o vmlinux.lds
 
-obj-$(CONFIG_MODULES)          += s390_ksyms.o module.o
+obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_SCHED_TOPOLOGY)   += topology.o
 obj-$(CONFIG_HIBERNATION)      += suspend.o swsusp.o
index c51650a..49a3073 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/vx-insn.h>
 #include <asm/setup.h>
 #include <asm/nmi.h>
+#include <asm/export.h>
 
 __PT_R0      = __PT_GPRS
 __PT_R1      = __PT_GPRS + 8
@@ -259,6 +260,8 @@ sie_exit:
 
        EX_TABLE(.Lrewind_pad,.Lsie_fault)
        EX_TABLE(sie_exit,.Lsie_fault)
+EXPORT_SYMBOL(sie64a)
+EXPORT_SYMBOL(sie_exit)
 #endif
 
 /*
@@ -825,6 +828,9 @@ ENTRY(save_fpu_regs)
        oi      __LC_CPU_FLAGS+7,_CIF_FPU
        br      %r14
 .Lsave_fpu_regs_end:
+#if IS_ENABLED(CONFIG_KVM)
+EXPORT_SYMBOL(save_fpu_regs)
+#endif
 
 /*
  * Load floating-point controls and floating-point or vector registers.
index e499370..9a17e44 100644 (file)
@@ -9,6 +9,7 @@
 #include <asm/asm-offsets.h>
 #include <asm/ftrace.h>
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
        .section .kprobes.text, "ax"
 
@@ -23,6 +24,8 @@ ENTRY(ftrace_stub)
 ENTRY(_mcount)
        br      %r14
 
+EXPORT_SYMBOL(_mcount)
+
 ENTRY(ftrace_caller)
        .globl  ftrace_regs_caller
        .set    ftrace_regs_caller,ftrace_caller
diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c
deleted file mode 100644 (file)
index e67453b..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#include <linux/module.h>
-#include <linux/kvm_host.h>
-#include <asm/fpu/api.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-EXPORT_SYMBOL(_mcount);
-#endif
-#if IS_ENABLED(CONFIG_KVM)
-EXPORT_SYMBOL(sie64a);
-EXPORT_SYMBOL(sie_exit);
-EXPORT_SYMBOL(save_fpu_regs);
-#endif
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
index c6d553e..be9fa65 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 /*
  * memset implementation
@@ -60,6 +61,7 @@ ENTRY(memset)
        xc      0(1,%r1),0(%r1)
 .Lmemset_mvc:
        mvc     1(1,%r1),0(%r1)
+EXPORT_SYMBOL(memset)
 
 /*
  * memcpy implementation
@@ -86,3 +88,4 @@ ENTRY(memcpy)
        j       .Lmemcpy_rest
 .Lmemcpy_mvc:
        mvc     0(1,%r1),0(%r3)
+EXPORT_SYMBOL(memcpy)
diff --git a/arch/score/include/asm/extable.h b/arch/score/include/asm/extable.h
new file mode 100644 (file)
index 0000000..c4423cc
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _ASM_SCORE_EXTABLE_H
+#define _ASM_SCORE_EXTABLE_H
+
+struct exception_table_entry {
+       unsigned long insn;
+       unsigned long fixup;
+};
+
+struct pt_regs;
+extern int fixup_exception(struct pt_regs *regs);
+#endif
index abf395b..6dc1f29 100644 (file)
@@ -2,7 +2,7 @@
 #define _ASM_SCORE_MODULE_H
 
 #include <linux/list.h>
-#include <asm/uaccess.h>
+#include <asm/extable.h>
 #include <asm-generic/module.h>
 
 struct mod_arch_specific {
index 01aec8c..db58ab9 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/thread_info.h>
+#include <asm/extable.h>
 
 #define VERIFY_READ            0
 #define VERIFY_WRITE           1
@@ -420,12 +421,5 @@ static inline long strnlen_user(const char __user *str, long len)
                return __strnlen_user(str, len);
 }
 
-struct exception_table_entry {
-       unsigned long insn;
-       unsigned long fixup;
-};
-
-extern int fixup_exception(struct pt_regs *regs);
-
 #endif /* __SCORE_UACCESS_H */
 
index 92ade79..a38d0c7 100644 (file)
@@ -192,8 +192,6 @@ struct exception_table_entry {
 #endif
 
 int fixup_exception(struct pt_regs *regs);
-/* Returns 0 if exception not found and fixup.unit otherwise.  */
-unsigned long search_exception_table(unsigned long addr);
 const struct exception_table_entry *search_exception_tables(unsigned long addr);
 
 extern void *set_exception_table_vec(unsigned int vec, void *handler);
index 6024c26..cfc9180 100644 (file)
@@ -6,6 +6,7 @@ generic-y += cputime.h
 generic-y += div64.h
 generic-y += emergency-restart.h
 generic-y += exec.h
+generic-y += export.h
 generic-y += irq_regs.h
 generic-y += irq_work.h
 generic-y += linkage.h
index 9331083..3f2d403 100644 (file)
@@ -7,7 +7,7 @@
 
 #include <asm/ptrace.h>
 #include <asm/processor.h>
-#include <asm/uaccess.h>
+#include <asm/extable_64.h>
 #include <asm/spitfire.h>
 
 /*
diff --git a/arch/sparc/include/asm/extable_64.h b/arch/sparc/include/asm/extable_64.h
new file mode 100644 (file)
index 0000000..1121cb0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __ASM_EXTABLE64_H
+#define __ASM_EXTABLE64_H
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry {
+        unsigned int insn, fixup;
+};
+
+#endif
index 98b72a0..86f34be 100644 (file)
@@ -5,4 +5,38 @@
 #else
 #include <asm/string_32.h>
 #endif
+
+/* First the mem*() things. */
+#define __HAVE_ARCH_MEMMOVE
+void *memmove(void *, const void *, __kernel_size_t);
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) __builtin_memset(s, c, count)
+
+#define __HAVE_ARCH_MEMSCAN
+
+#define memscan(__arg0, __char, __arg2)                                                \
+({                                                                             \
+       void *__memscan_zero(void *, size_t);                                   \
+       void *__memscan_generic(void *, int, size_t);                           \
+       void *__retval, *__addr = (__arg0);                                     \
+       size_t __size = (__arg2);                                               \
+                                                                               \
+       if(__builtin_constant_p(__char) && !(__char))                           \
+               __retval = __memscan_zero(__addr, __size);                      \
+       else                                                                    \
+               __retval = __memscan_generic(__addr, (__char), __size);         \
+                                                                               \
+       __retval;                                                               \
+})
+
+#define __HAVE_ARCH_MEMCMP
+int memcmp(const void *,const void *,__kernel_size_t);
+
+#define __HAVE_ARCH_STRNCMP
+int strncmp(const char *, const char *, __kernel_size_t);
+
 #endif
index 69974e9..6494124 100644 (file)
 
 #include <asm/page.h>
 
-/* Really, userland/ksyms should not see any of this stuff. */
-
-#ifdef __KERNEL__
-
-void __memmove(void *,const void *,__kernel_size_t);
-
-#ifndef EXPORT_SYMTAB_STROPS
-
-/* First the mem*() things. */
-#define __HAVE_ARCH_MEMMOVE
-#undef memmove
-#define memmove(_to, _from, _n) \
-({ \
-       void *_t = (_to); \
-       __memmove(_t, (_from), (_n)); \
-       _t; \
-})
-
-#define __HAVE_ARCH_MEMCPY
-#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
-
-#define __HAVE_ARCH_MEMSET
-#define memset(s, c, count) __builtin_memset(s, c, count)
-
-#define __HAVE_ARCH_MEMSCAN
-
-#undef memscan
-#define memscan(__arg0, __char, __arg2)                                                \
-({                                                                             \
-       void *__memscan_zero(void *, size_t);                                   \
-       void *__memscan_generic(void *, int, size_t);                           \
-       void *__retval, *__addr = (__arg0);                                     \
-       size_t __size = (__arg2);                                               \
-                                                                               \
-       if(__builtin_constant_p(__char) && !(__char))                           \
-               __retval = __memscan_zero(__addr, __size);                      \
-       else                                                                    \
-               __retval = __memscan_generic(__addr, (__char), __size);         \
-                                                                               \
-       __retval;                                                               \
-})
-
-#define __HAVE_ARCH_MEMCMP
-int memcmp(const void *,const void *,__kernel_size_t);
-
-/* Now the str*() stuff... */
-#define __HAVE_ARCH_STRLEN
-__kernel_size_t strlen(const char *);
-
-#define __HAVE_ARCH_STRNCMP
-int strncmp(const char *, const char *, __kernel_size_t);
-
-#endif /* !EXPORT_SYMTAB_STROPS */
-
-#endif /* __KERNEL__ */
-
 #endif /* !(__SPARC_STRING_H__) */
index 5936b8f..6b9ccb3 100644 (file)
@@ -9,54 +9,10 @@
 #ifndef __SPARC64_STRING_H__
 #define __SPARC64_STRING_H__
 
-/* Really, userland/ksyms should not see any of this stuff. */
-
-#ifdef __KERNEL__
-
 #include <asm/asi.h>
 
-#ifndef EXPORT_SYMTAB_STROPS
-
-/* First the mem*() things. */
-#define __HAVE_ARCH_MEMMOVE
-void *memmove(void *, const void *, __kernel_size_t);
-
-#define __HAVE_ARCH_MEMCPY
-#define memcpy(t, f, n) __builtin_memcpy(t, f, n)
-
-#define __HAVE_ARCH_MEMSET
-#define memset(s, c, count) __builtin_memset(s, c, count)
-
-#define __HAVE_ARCH_MEMSCAN
-
-#undef memscan
-#define memscan(__arg0, __char, __arg2)                                        \
-({                                                                     \
-       void *__memscan_zero(void *, size_t);                           \
-       void *__memscan_generic(void *, int, size_t);                   \
-       void *__retval, *__addr = (__arg0);                             \
-       size_t __size = (__arg2);                                       \
-                                                                       \
-       if(__builtin_constant_p(__char) && !(__char))                   \
-               __retval = __memscan_zero(__addr, __size);              \
-       else                                                            \
-               __retval = __memscan_generic(__addr, (__char), __size); \
-                                                                       \
-       __retval;                                                       \
-})
-
-#define __HAVE_ARCH_MEMCMP
-int memcmp(const void *,const void *,__kernel_size_t);
-
 /* Now the str*() stuff... */
 #define __HAVE_ARCH_STRLEN
 __kernel_size_t strlen(const char *);
 
-#define __HAVE_ARCH_STRNCMP
-int strncmp(const char *, const char *, __kernel_size_t);
-
-#endif /* !EXPORT_SYMTAB_STROPS */
-
-#endif /* __KERNEL__ */
-
 #endif /* !(__SPARC64_STRING_H__) */
index 37a315d..b68acc5 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/asi.h>
 #include <asm/spitfire.h>
 #include <asm-generic/uaccess-unaligned.h>
+#include <asm/extable_64.h>
 #endif
 
 #ifndef __ASSEMBLY__
@@ -81,23 +82,6 @@ static inline int access_ok(int type, const void __user * addr, unsigned long si
        return 1;
 }
 
-/*
- * The exception table consists of pairs of addresses: the first is the
- * address of an instruction that is allowed to fault, and the second is
- * the address at which the program should continue.  No registers are
- * modified, so it is entirely up to the continuation code to figure out
- * what to do.
- *
- * All the routines below use bits of fixup code that are out of line
- * with the main instruction path.  This means when everything is well,
- * we don't even have to jump over them.  Further, they do not intrude
- * on our cache or tlb entries.
- */
-
-struct exception_table_entry {
-        unsigned int insn, fixup;
-};
-
 void __ret_efault(void);
 void __retl_efault(void);
 
index fdb1332..fa3c02d 100644 (file)
@@ -86,7 +86,7 @@ obj-y                     += auxio_$(BITS).o
 obj-$(CONFIG_SUN_PM)      += apc.o pmc.o
 
 obj-$(CONFIG_MODULES)     += module.o
-obj-$(CONFIG_MODULES)     += sparc_ksyms_$(BITS).o
+obj-$(CONFIG_MODULES)     += sparc_ksyms.o
 obj-$(CONFIG_SPARC_LED)   += led.o
 obj-$(CONFIG_KGDB)        += kgdb_$(BITS).o
 
index 07918ab..d85bdb9 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/unistd.h>
 
 #include <asm/asmmacro.h>
+#include <asm/export.h>
 
 #define curptr      g6
 
@@ -1207,6 +1208,8 @@ delay_continue:
        
        ret
        restore
+EXPORT_SYMBOL(__udelay)
+EXPORT_SYMBOL(__ndelay)
 
        /* Handle a software breakpoint */
        /* We have to inform parent that child has stopped */
index 3d92c0a..7bb317b 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/thread_info.h>   /* TI_UWINMASK */
 #include <asm/errno.h>
 #include <asm/pgtsrmmu.h>      /* SRMMU_PGDIR_SHIFT */
+#include <asm/export.h>
 
        .data
 /* The following are used with the prom_vector node-ops to figure out
@@ -60,6 +61,7 @@ sun4e_notsup:
  */
        .globl empty_zero_page
 empty_zero_page:       .skip PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
        .global root_flags
        .global ram_flags
@@ -813,3 +815,4 @@ lvl14_save:
 __ret_efault:
         ret
          restore %g0, -EFAULT, %o0
+EXPORT_SYMBOL(__ret_efault)
index a076b42..beba6c1 100644 (file)
@@ -32,7 +32,8 @@
 #include <asm/estate.h>
 #include <asm/sfafsr.h>
 #include <asm/unistd.h>
-       
+#include <asm/export.h>
+
 /* This section from from _start to sparc64_boot_end should fit into
  * 0x0000000000404000 to 0x0000000000408000.
  */
@@ -143,6 +144,7 @@ prom_cpu_compatible:
        .skip   64
 prom_root_node:
        .word   0
+EXPORT_SYMBOL(prom_root_node)
 prom_mmu_ihandle_cache:
        .word   0
 prom_boot_mapped_pc:
@@ -158,6 +160,7 @@ is_sun4v:
        .word   0
 sun4v_chip_type:
        .word   SUN4V_CHIP_INVALID
+EXPORT_SYMBOL(sun4v_chip_type)
 1:
        rd      %pc, %l0
 
@@ -920,6 +923,7 @@ swapper_4m_tsb:
        .globl  prom_tba, tlb_type
 prom_tba:      .xword  0
 tlb_type:      .word   0       /* Must NOT end up in BSS */
+EXPORT_SYMBOL(tlb_type)
        .section        ".fixup",#alloc,#execinstr
 
        .globl  __ret_efault, __retl_efault, __ret_one, __retl_one
@@ -927,6 +931,7 @@ ENTRY(__ret_efault)
        ret
         restore %g0, -EFAULT, %o0
 ENDPROC(__ret_efault)
+EXPORT_SYMBOL(__ret_efault)
 
 ENTRY(__retl_efault)
        retl
index 314dd0c..e4e5b83 100644 (file)
@@ -15,6 +15,7 @@ __flushw_user:
 2:     retl
         nop
        .size   __flushw_user,.-__flushw_user
+EXPORT_SYMBOL(__flushw_user)
 
        /* Flush %fp and %i7 to the stack for all register
         * windows active inside of the cpu.  This allows
@@ -61,3 +62,4 @@ real_hard_smp_processor_id:
        .size           hard_smp_processor_id,.-hard_smp_processor_id
 #endif
        .size           real_hard_smp_processor_id,.-real_hard_smp_processor_id
+EXPORT_SYMBOL_GPL(real_hard_smp_processor_id)
index d127130..4116ee5 100644 (file)
@@ -343,6 +343,7 @@ ENTRY(sun4v_mach_set_watchdog)
 0:     retl
         nop
 ENDPROC(sun4v_mach_set_watchdog)
+EXPORT_SYMBOL(sun4v_mach_set_watchdog)
 
        /* No inputs and does not return.  */
 ENTRY(sun4v_mach_sir)
@@ -776,6 +777,7 @@ ENTRY(sun4v_niagara_getperf)
        retl
         nop
 ENDPROC(sun4v_niagara_getperf)
+EXPORT_SYMBOL(sun4v_niagara_getperf)
 
 ENTRY(sun4v_niagara_setperf)
        mov     HV_FAST_SET_PERFREG, %o5
@@ -783,6 +785,7 @@ ENTRY(sun4v_niagara_setperf)
        retl
         nop
 ENDPROC(sun4v_niagara_setperf)
+EXPORT_SYMBOL(sun4v_niagara_setperf)
 
 ENTRY(sun4v_niagara2_getperf)
        mov     %o0, %o4
@@ -792,6 +795,7 @@ ENTRY(sun4v_niagara2_getperf)
        retl
         nop
 ENDPROC(sun4v_niagara2_getperf)
+EXPORT_SYMBOL(sun4v_niagara2_getperf)
 
 ENTRY(sun4v_niagara2_setperf)
        mov     HV_FAST_N2_SET_PERFREG, %o5
@@ -799,6 +803,7 @@ ENTRY(sun4v_niagara2_setperf)
        retl
         nop
 ENDPROC(sun4v_niagara2_setperf)
+EXPORT_SYMBOL(sun4v_niagara2_setperf)
 
 ENTRY(sun4v_reboot_data_set)
        mov     HV_FAST_REBOOT_DATA_SET, %o5
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
new file mode 100644 (file)
index 0000000..09aa69e
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+
+/* This is needed only for drivers/sbus/char/openprom.c */
+EXPORT_SYMBOL(saved_command_line);
diff --git a/arch/sparc/kernel/sparc_ksyms_32.c b/arch/sparc/kernel/sparc_ksyms_32.c
deleted file mode 100644 (file)
index bf4ccb1..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
- *
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- */
-
-#include <linux/module.h>
-
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/delay.h>
-#include <asm/head.h>
-#include <asm/dma.h>
-
-struct poll {
-       int fd;
-       short events;
-       short revents;
-};
-
-/* from entry.S */
-EXPORT_SYMBOL(__udelay);
-EXPORT_SYMBOL(__ndelay);
-
-/* from head_32.S */
-EXPORT_SYMBOL(__ret_efault);
-EXPORT_SYMBOL(empty_zero_page);
-
-/* Exporting a symbol from /init/main.c */
-EXPORT_SYMBOL(saved_command_line);
diff --git a/arch/sparc/kernel/sparc_ksyms_64.c b/arch/sparc/kernel/sparc_ksyms_64.c
deleted file mode 100644 (file)
index 9e034f2..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
- *
- * Copyright (C) 1996, 2007 David S. Miller (davem@davemloft.net)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1999 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-#include <linux/export.h>
-#include <linux/pci.h>
-#include <linux/bitops.h>
-
-#include <asm/cpudata.h>
-#include <asm/uaccess.h>
-#include <asm/spitfire.h>
-#include <asm/oplib.h>
-#include <asm/hypervisor.h>
-#include <asm/cacheflush.h>
-
-struct poll {
-       int fd;
-       short events;
-       short revents;
-};
-
-/* from helpers.S */
-EXPORT_SYMBOL(__flushw_user);
-EXPORT_SYMBOL_GPL(real_hard_smp_processor_id);
-
-/* from head_64.S */
-EXPORT_SYMBOL(__ret_efault);
-EXPORT_SYMBOL(tlb_type);
-EXPORT_SYMBOL(sun4v_chip_type);
-EXPORT_SYMBOL(prom_root_node);
-
-/* from hvcalls.S */
-EXPORT_SYMBOL(sun4v_niagara_getperf);
-EXPORT_SYMBOL(sun4v_niagara_setperf);
-EXPORT_SYMBOL(sun4v_niagara2_getperf);
-EXPORT_SYMBOL(sun4v_niagara2_setperf);
-EXPORT_SYMBOL(sun4v_mach_set_watchdog);
-
-/* from hweight.S */
-EXPORT_SYMBOL(__arch_hweight8);
-EXPORT_SYMBOL(__arch_hweight16);
-EXPORT_SYMBOL(__arch_hweight32);
-EXPORT_SYMBOL(__arch_hweight64);
-
-/* from ffs_ffz.S */
-EXPORT_SYMBOL(ffs);
-EXPORT_SYMBOL(__ffs);
-
-/* Exporting a symbol from /init/main.c */
-EXPORT_SYMBOL(saved_command_line);
index 3269b02..885f00e 100644 (file)
@@ -43,5 +43,4 @@ lib-$(CONFIG_SPARC64) += mcount.o ipcsum.o xor.o hweight.o ffs.o
 
 obj-$(CONFIG_SPARC64) += iomap.o
 obj-$(CONFIG_SPARC32) += atomic32.o ucmpdi2.o
-obj-y                 += ksyms.o
 obj-$(CONFIG_SPARC64) += PeeCeeI.o
index 3e6209e..97e1b21 100644 (file)
@@ -7,6 +7,7 @@
 #ifdef __KERNEL__
 #include <asm/visasm.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 #define GLOBAL_SPARE   g7
 #else
 #define GLOBAL_SPARE   g5
@@ -567,3 +568,4 @@ FUNC_NAME:          /* %o0=dst, %o1=src, %o2=len */
         mov            EX_RETVAL(%o4), %o0
 
        .size           FUNC_NAME, .-FUNC_NAME
+EXPORT_SYMBOL(FUNC_NAME)
index 62c2647..1c7b6a3 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/ptrace.h>
 #include <asm/visasm.h>
 #include <asm/thread_info.h>
+#include <asm/export.h>
 
        /* On entry: %o5=current FPRS value, %g7 is callers address */
        /* May clobber %o5, %g1, %g2, %g3, %g7, %icc, %xcc */
@@ -79,3 +80,4 @@ vis1: ldub            [%g6 + TI_FPSAVED], %g3
 80:    jmpl            %g7 + %g0, %g0
         nop
 ENDPROC(VISenter)
+EXPORT_SYMBOL(VISenter)
index 86f60de..c8b1cf7 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__ashldi3)
@@ -33,3 +34,4 @@ ENTRY(__ashldi3)
        retl
         nop
 ENDPROC(__ashldi3)
+EXPORT_SYMBOL(__ashldi3)
index 6eb8ba2..4310256 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__ashrdi3)
@@ -35,3 +36,4 @@ ENTRY(__ashrdi3)
        jmpl    %o7 + 8, %g0
         nop
 ENDPROC(__ashrdi3)
+EXPORT_SYMBOL(__ashrdi3)
index a5c5a02..1c6a1bd 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/linkage.h>
 #include <asm/asi.h>
 #include <asm/backoff.h>
+#include <asm/export.h>
 
        .text
 
@@ -29,6 +30,7 @@ ENTRY(atomic_##op) /* %o0 = increment, %o1 = atomic_ptr */            \
         nop;                                                           \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
 ENDPROC(atomic_##op);                                                  \
+EXPORT_SYMBOL(atomic_##op);
 
 #define ATOMIC_OP_RETURN(op)                                           \
 ENTRY(atomic_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */    \
@@ -42,7 +44,8 @@ ENTRY(atomic_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */   \
        retl;                                                           \
         sra    %g1, 0, %o0;                                            \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic_##op##_return);
+ENDPROC(atomic_##op##_return);                                         \
+EXPORT_SYMBOL(atomic_##op##_return);
 
 #define ATOMIC_FETCH_OP(op)                                            \
 ENTRY(atomic_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */       \
@@ -56,7 +59,8 @@ ENTRY(atomic_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */      \
        retl;                                                           \
         sra    %g1, 0, %o0;                                            \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic_fetch_##op);
+ENDPROC(atomic_fetch_##op);                                            \
+EXPORT_SYMBOL(atomic_fetch_##op);
 
 #define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
 
@@ -88,6 +92,7 @@ ENTRY(atomic64_##op) /* %o0 = increment, %o1 = atomic_ptr */          \
         nop;                                                           \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
 ENDPROC(atomic64_##op);                                                        \
+EXPORT_SYMBOL(atomic64_##op);
 
 #define ATOMIC64_OP_RETURN(op)                                         \
 ENTRY(atomic64_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */  \
@@ -101,7 +106,8 @@ ENTRY(atomic64_##op##_return) /* %o0 = increment, %o1 = atomic_ptr */       \
        retl;                                                           \
         op     %g1, %o0, %o0;                                          \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic64_##op##_return);
+ENDPROC(atomic64_##op##_return);                                       \
+EXPORT_SYMBOL(atomic64_##op##_return);
 
 #define ATOMIC64_FETCH_OP(op)                                          \
 ENTRY(atomic64_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */     \
@@ -115,7 +121,8 @@ ENTRY(atomic64_fetch_##op) /* %o0 = increment, %o1 = atomic_ptr */  \
        retl;                                                           \
         mov    %g1, %o0;                                               \
 2:     BACKOFF_SPIN(%o2, %o3, 1b);                                     \
-ENDPROC(atomic64_fetch_##op);
+ENDPROC(atomic64_fetch_##op);                                          \
+EXPORT_SYMBOL(atomic64_fetch_##op);
 
 #define ATOMIC64_OPS(op) ATOMIC64_OP(op) ATOMIC64_OP_RETURN(op) ATOMIC64_FETCH_OP(op)
 
@@ -147,3 +154,4 @@ ENTRY(atomic64_dec_if_positive) /* %o0 = atomic_ptr */
         sub    %g1, 1, %o0
 2:     BACKOFF_SPIN(%o2, %o3, 1b)
 ENDPROC(atomic64_dec_if_positive)
+EXPORT_SYMBOL(atomic64_dec_if_positive)
index 36f72cc..7031bf1 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/linkage.h>
 #include <asm/asi.h>
 #include <asm/backoff.h>
+#include <asm/export.h>
 
        .text
 
@@ -29,6 +30,7 @@ ENTRY(test_and_set_bit)       /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_set_bit)
+EXPORT_SYMBOL(test_and_set_bit)
 
 ENTRY(test_and_clear_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -50,6 +52,7 @@ ENTRY(test_and_clear_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_clear_bit)
+EXPORT_SYMBOL(test_and_clear_bit)
 
 ENTRY(test_and_change_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -71,6 +74,7 @@ ENTRY(test_and_change_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(test_and_change_bit)
+EXPORT_SYMBOL(test_and_change_bit)
 
 ENTRY(set_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -90,6 +94,7 @@ ENTRY(set_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(set_bit)
+EXPORT_SYMBOL(set_bit)
 
 ENTRY(clear_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -109,6 +114,7 @@ ENTRY(clear_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(clear_bit)
+EXPORT_SYMBOL(clear_bit)
 
 ENTRY(change_bit) /* %o0=nr, %o1=addr */
        BACKOFF_SETUP(%o3)
@@ -128,3 +134,4 @@ ENTRY(change_bit) /* %o0=nr, %o1=addr */
         nop
 2:     BACKOFF_SPIN(%o3, %o4, 1b)
 ENDPROC(change_bit)
+EXPORT_SYMBOL(change_bit)
index 3c77101..1f2692d 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/page.h>
+#include <asm/export.h>
 
        /* Zero out 64 bytes of memory at (buf + offset).
         * Assumes %g1 contains zero.
@@ -64,6 +65,7 @@ ENTRY(bzero_1page)
        retl
         nop
 ENDPROC(bzero_1page)
+EXPORT_SYMBOL(bzero_1page)
 
 ENTRY(__copy_1page)
 /* NOTE: If you change the number of insns of this routine, please check
@@ -87,3 +89,4 @@ ENTRY(__copy_1page)
        retl
         nop
 ENDPROC(__copy_1page)
+EXPORT_SYMBOL(__copy_1page)
index 8c05811..3bb1914 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 
@@ -78,6 +79,8 @@ __bzero_done:
         mov            %o3, %o0
 ENDPROC(__bzero)
 ENDPROC(memset)
+EXPORT_SYMBOL(__bzero)
+EXPORT_SYMBOL(memset)
 
 #define EX_ST(x,y)             \
 98:    x,y;                    \
@@ -143,3 +146,4 @@ __clear_user_done:
        retl
         clr            %o0
 ENDPROC(__clear_user)
+EXPORT_SYMBOL(__clear_user)
index 0084c33..c9d8b62 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <asm/errno.h>
+#include <asm/export.h>
 
 #define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5)        \
        ldd     [buf + offset + 0x00], t0;                      \
@@ -104,6 +105,7 @@ csum_partial_fix_alignment:
         * buffer of size 0x20.  Follow the code path for that case.
         */
        .globl  csum_partial
+       EXPORT_SYMBOL(csum_partial)
 csum_partial:                  /* %o0=buf, %o1=len, %o2=sum */
        andcc   %o0, 0x7, %g0                           ! alignment problems?
        bne     csum_partial_fix_alignment              ! yep, handle it
@@ -335,6 +337,7 @@ cc_dword_align:
         */
        .align  8
        .globl  __csum_partial_copy_sparc_generic
+       EXPORT_SYMBOL(__csum_partial_copy_sparc_generic)
 __csum_partial_copy_sparc_generic:
                                        /* %o0=src, %o1=dest, %g1=len, %g7=sum */
        xor     %o0, %o1, %o4           ! get changing bits
index 1d230f6..f673217 100644 (file)
@@ -13,6 +13,7 @@
  *     BSD4.4 portable checksum routine
  */
 
+#include <asm/export.h>
        .text
 
 csum_partial_fix_alignment:
@@ -37,6 +38,7 @@ csum_partial_fix_alignment:
 
        .align          32
        .globl          csum_partial
+       EXPORT_SYMBOL(csum_partial)
 csum_partial:          /* %o0=buff, %o1=len, %o2=sum */
        prefetch        [%o0 + 0x000], #n_reads
        clr             %o4
index 46272df..f30d6b7 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/pgtable.h>
 #include <asm/spitfire.h>
 #include <asm/head.h>
+#include <asm/export.h>
 
        /* What we used to do was lock a TLB entry into a specific
         * TLB slot, clear the page with interrupts disabled, then
@@ -26,6 +27,7 @@
        .text
 
        .globl          _clear_page
+       EXPORT_SYMBOL(_clear_page)
 _clear_page:           /* %o0=dest */
        ba,pt           %xcc, clear_page_common
         clr            %o4
@@ -35,6 +37,7 @@ _clear_page:          /* %o0=dest */
         */
        .align          32
        .globl          clear_user_page
+       EXPORT_SYMBOL(clear_user_page)
 clear_user_page:       /* %o0=dest, %o1=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o2
        sethi           %hi(PAGE_OFFSET), %g2
index 302c0e6..482de09 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 
 #define XCC xcc
 
@@ -90,3 +91,4 @@ ENTRY(___copy_in_user)        /* %o0=dst, %o1=src, %o2=len */
        retl
         clr            %o0
 ENDPROC(___copy_in_user)
+EXPORT_SYMBOL(___copy_in_user)
index dd16c61..7197b72 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/pgtable.h>
 #include <asm/spitfire.h>
 #include <asm/head.h>
+#include <asm/export.h>
 
        /* What we used to do was lock a TLB entry into a specific
         * TLB slot, clear the page with interrupts disabled, then
@@ -44,6 +45,7 @@
        .align          32
        .globl          copy_user_page
        .type           copy_user_page,#function
+       EXPORT_SYMBOL(copy_user_page)
 copy_user_page:                /* %o0=dest, %o1=src, %o2=vaddr */
        lduw            [%g6 + TI_PRE_COUNT], %o4
        sethi           %hi(PAGE_OFFSET), %g2
index ef095b6..cea644d 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/asmmacro.h>
 #include <asm/page.h>
 #include <asm/thread_info.h>
+#include <asm/export.h>
 
 /* Work around cpp -rob */
 #define ALLOC #alloc
 __copy_user_begin:
 
        .globl  __copy_user
+       EXPORT_SYMBOL(__copy_user)
 dword_align:
        andcc   %o1, 1, %g0
        be      4f
index e566c77..0ecbafc 100644 (file)
@@ -3,6 +3,8 @@
  * Copyright (C) 2005 David S. Miller <davem@davemloft.net>
  */
 
+#include <asm/export.h>
+
 #ifdef __KERNEL__
 #define GLOBAL_SPARE   %g7
 #else
@@ -63,6 +65,7 @@
         add            %o5, %o4, %o4
 
        .globl          FUNC_NAME
+       EXPORT_SYMBOL(FUNC_NAME)
 FUNC_NAME:             /* %o0=src, %o1=dst, %o2=len, %o3=sum */
        LOAD(prefetch, %o0 + 0x000, #n_reads)
        xor             %o0, %o1, %g1
index 9614b48..a2b5a97 100644 (file)
@@ -17,6 +17,7 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
+#include <asm/export.h>
        .text
        .align 4
        .globl __divdi3
@@ -279,3 +280,4 @@ __divdi3:
 .LL81:
        ret
        restore
+EXPORT_SYMBOL(__divdi3)
index b39389f..23aab14 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .register       %g2,#scratch
 
@@ -65,6 +66,8 @@ ENTRY(__ffs)
         add    %o2, %g1, %o0
 ENDPROC(ffs)
 ENDPROC(__ffs)
+EXPORT_SYMBOL(__ffs)
+EXPORT_SYMBOL(ffs)
 
        .section        .popc_6insn_patch, "ax"
        .word           ffs
index 95414e0..f9985f1 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
        .align  32
@@ -7,6 +8,7 @@ ENTRY(__arch_hweight8)
         nop
        nop
 ENDPROC(__arch_hweight8)
+EXPORT_SYMBOL(__arch_hweight8)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight8
        sllx            %o0, 64-8, %g1
@@ -19,6 +21,7 @@ ENTRY(__arch_hweight16)
         nop
        nop
 ENDPROC(__arch_hweight16)
+EXPORT_SYMBOL(__arch_hweight16)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight16
        sllx            %o0, 64-16, %g1
@@ -31,6 +34,7 @@ ENTRY(__arch_hweight32)
         nop
        nop
 ENDPROC(__arch_hweight32)
+EXPORT_SYMBOL(__arch_hweight32)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight32
        sllx            %o0, 64-32, %g1
@@ -43,6 +47,7 @@ ENTRY(__arch_hweight64)
         nop
        nop
 ENDPROC(__arch_hweight64)
+EXPORT_SYMBOL(__arch_hweight64)
        .section        .popc_3insn_patch, "ax"
        .word           __arch_hweight64
        retl
index 4742d59..5d61648 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(ip_fast_csum) /* %o0 = iph, %o1 = ihl */
@@ -31,3 +32,4 @@ ENTRY(ip_fast_csum) /* %o0 = iph, %o1 = ihl */
        retl
         and    %o2, %o1, %o0
 ENDPROC(ip_fast_csum)
+EXPORT_SYMBOL(ip_fast_csum)
diff --git a/arch/sparc/lib/ksyms.c b/arch/sparc/lib/ksyms.c
deleted file mode 100644 (file)
index de5e978..0000000
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Export of symbols defined in assembler
- */
-
-/* Tell string.h we don't want memcpy etc. as cpp defines */
-#define EXPORT_SYMTAB_STROPS
-
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/types.h>
-
-#include <asm/checksum.h>
-#include <asm/uaccess.h>
-#include <asm/ftrace.h>
-
-/* string functions */
-EXPORT_SYMBOL(strlen);
-EXPORT_SYMBOL(strncmp);
-
-/* mem* functions */
-extern void *__memscan_zero(void *, size_t);
-extern void *__memscan_generic(void *, int, size_t);
-extern void *__bzero(void *, size_t);
-
-EXPORT_SYMBOL(memscan);
-EXPORT_SYMBOL(__memscan_zero);
-EXPORT_SYMBOL(__memscan_generic);
-EXPORT_SYMBOL(memcmp);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memmove);
-EXPORT_SYMBOL(__bzero);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial);
-
-#ifdef CONFIG_MCOUNT
-EXPORT_SYMBOL(_mcount);
-#endif
-
-/*
- * sparc
- */
-#ifdef CONFIG_SPARC32
-extern int __ashrdi3(int, int);
-extern int __ashldi3(int, int);
-extern int __lshrdi3(int, int);
-extern int __muldi3(int, int);
-extern int __divdi3(int, int);
-
-extern void (*__copy_1page)(void *, const void *);
-extern void (*bzero_1page)(void *);
-
-extern void ___rw_read_enter(void);
-extern void ___rw_read_try(void);
-extern void ___rw_read_exit(void);
-extern void ___rw_write_enter(void);
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(__csum_partial_copy_sparc_generic);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(__copy_1page);
-EXPORT_SYMBOL(__memmove);
-EXPORT_SYMBOL(bzero_1page);
-
-/* Moving data to/from/in userspace. */
-EXPORT_SYMBOL(__copy_user);
-
-/* Used by asm/spinlock.h */
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(___rw_read_enter);
-EXPORT_SYMBOL(___rw_read_try);
-EXPORT_SYMBOL(___rw_read_exit);
-EXPORT_SYMBOL(___rw_write_enter);
-#endif
-
-EXPORT_SYMBOL(__ashrdi3);
-EXPORT_SYMBOL(__ashldi3);
-EXPORT_SYMBOL(__lshrdi3);
-EXPORT_SYMBOL(__muldi3);
-EXPORT_SYMBOL(__divdi3);
-#endif
-
-/*
- * sparc64
- */
-#ifdef CONFIG_SPARC64
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial_copy_nocheck);
-EXPORT_SYMBOL(__csum_partial_copy_from_user);
-EXPORT_SYMBOL(__csum_partial_copy_to_user);
-EXPORT_SYMBOL(ip_fast_csum);
-
-/* Moving data to/from/in userspace. */
-EXPORT_SYMBOL(___copy_to_user);
-EXPORT_SYMBOL(___copy_from_user);
-EXPORT_SYMBOL(___copy_in_user);
-EXPORT_SYMBOL(__clear_user);
-
-/* Atomic counter implementation. */
-#define ATOMIC_OP(op)                                                  \
-EXPORT_SYMBOL(atomic_##op);                                            \
-EXPORT_SYMBOL(atomic64_##op);
-
-#define ATOMIC_OP_RETURN(op)                                           \
-EXPORT_SYMBOL(atomic_##op##_return);                                   \
-EXPORT_SYMBOL(atomic64_##op##_return);
-
-#define ATOMIC_FETCH_OP(op)                                            \
-EXPORT_SYMBOL(atomic_fetch_##op);                                      \
-EXPORT_SYMBOL(atomic64_fetch_##op);
-
-#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_OP_RETURN(op) ATOMIC_FETCH_OP(op)
-
-ATOMIC_OPS(add)
-ATOMIC_OPS(sub)
-
-#undef ATOMIC_OPS
-#define ATOMIC_OPS(op) ATOMIC_OP(op) ATOMIC_FETCH_OP(op)
-
-ATOMIC_OPS(and)
-ATOMIC_OPS(or)
-ATOMIC_OPS(xor)
-
-#undef ATOMIC_OPS
-#undef ATOMIC_FETCH_OP
-#undef ATOMIC_OP_RETURN
-#undef ATOMIC_OP
-
-EXPORT_SYMBOL(atomic64_dec_if_positive);
-
-/* Atomic bit operations. */
-EXPORT_SYMBOL(test_and_set_bit);
-EXPORT_SYMBOL(test_and_clear_bit);
-EXPORT_SYMBOL(test_and_change_bit);
-EXPORT_SYMBOL(set_bit);
-EXPORT_SYMBOL(clear_bit);
-EXPORT_SYMBOL(change_bit);
-
-/* Special internal versions of library functions. */
-EXPORT_SYMBOL(_clear_page);
-EXPORT_SYMBOL(clear_user_page);
-EXPORT_SYMBOL(copy_user_page);
-
-/* RAID code needs this */
-void VISenter(void);
-EXPORT_SYMBOL(VISenter);
-
-extern void xor_vis_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_vis_3(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *);
-extern void xor_vis_4(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *);
-extern void xor_vis_5(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *, unsigned long *);
-EXPORT_SYMBOL(xor_vis_2);
-EXPORT_SYMBOL(xor_vis_3);
-EXPORT_SYMBOL(xor_vis_4);
-EXPORT_SYMBOL(xor_vis_5);
-
-extern void xor_niagara_2(unsigned long, unsigned long *, unsigned long *);
-extern void xor_niagara_3(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *);
-extern void xor_niagara_4(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *);
-extern void xor_niagara_5(unsigned long, unsigned long *, unsigned long *,
-               unsigned long *, unsigned long *, unsigned long *);
-
-EXPORT_SYMBOL(xor_niagara_2);
-EXPORT_SYMBOL(xor_niagara_3);
-EXPORT_SYMBOL(xor_niagara_4);
-EXPORT_SYMBOL(xor_niagara_5);
-#endif
index 64f53f2..f38c4e5 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/psr.h>
 #include <asm/smp.h>
 #include <asm/spinlock.h>
+#include <asm/export.h>
 
        .text
        .align  4
@@ -48,6 +49,7 @@ ___rw_write_enter_spin_on_wlock:
         ld     [%g1], %g2
 
        .globl  ___rw_read_enter
+EXPORT_SYMBOL(___rw_read_enter)
 ___rw_read_enter:
        orcc    %g2, 0x0, %g0
        bne,a   ___rw_read_enter_spin_on_wlock
@@ -59,6 +61,7 @@ ___rw_read_enter:
         mov    %g4, %o7
 
        .globl  ___rw_read_exit
+EXPORT_SYMBOL(___rw_read_exit)
 ___rw_read_exit:
        orcc    %g2, 0x0, %g0
        bne,a   ___rw_read_exit_spin_on_wlock
@@ -70,6 +73,7 @@ ___rw_read_exit:
         mov    %g4, %o7
 
        .globl  ___rw_read_try
+EXPORT_SYMBOL(___rw_read_try)
 ___rw_read_try:
        orcc    %g2, 0x0, %g0
        bne     ___rw_read_try_spin_on_wlock
@@ -81,6 +85,7 @@ ___rw_read_try:
         mov    %g4, %o7
 
        .globl  ___rw_write_enter
+EXPORT_SYMBOL(___rw_write_enter)
 ___rw_write_enter:
        orcc    %g2, 0x0, %g0
        bne     ___rw_write_enter_spin_on_wlock
index 60ebc7c..c9b9373 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 ENTRY(__lshrdi3)
        cmp     %o2, 0
@@ -25,3 +26,4 @@ ENTRY(__lshrdi3)
        retl 
         nop 
 ENDPROC(__lshrdi3)
+EXPORT_SYMBOL(__lshrdi3)
index 0b0ed4d..194f383 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 /*
  * This is the main variant and is called by C code.  GCC's -pg option
@@ -16,6 +17,7 @@
        .align          32
        .globl          _mcount
        .type           _mcount,#function
+       EXPORT_SYMBOL(_mcount)
        .globl          mcount
        .type           mcount,#function
 _mcount:
index efa106c..cee7f30 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asm.h>
+#include <asm/export.h>
 
        .text
 ENTRY(memcmp)
@@ -25,3 +26,4 @@ ENTRY(memcmp)
 2:     retl
         mov    0, %o0
 ENDPROC(memcmp)
+EXPORT_SYMBOL(memcmp)
index 4d8c497..8913fea 100644 (file)
@@ -7,6 +7,7 @@
  * Copyright (C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
  */
 
+#include <asm/export.h>
 #define FUNC(x)                \
        .globl  x;              \
        .type   x,@function;    \
@@ -58,93 +59,11 @@ x:
        stb     %t0, [%dst - (offset) - 0x02]; \
        stb     %t1, [%dst - (offset) - 0x01];
 
-/* Both these macros have to start with exactly the same insn */
-#define RMOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
-       ldd     [%src - (offset) - 0x20], %t0; \
-       ldd     [%src - (offset) - 0x18], %t2; \
-       ldd     [%src - (offset) - 0x10], %t4; \
-       ldd     [%src - (offset) - 0x08], %t6; \
-       st      %t0, [%dst - (offset) - 0x20]; \
-       st      %t1, [%dst - (offset) - 0x1c]; \
-       st      %t2, [%dst - (offset) - 0x18]; \
-       st      %t3, [%dst - (offset) - 0x14]; \
-       st      %t4, [%dst - (offset) - 0x10]; \
-       st      %t5, [%dst - (offset) - 0x0c]; \
-       st      %t6, [%dst - (offset) - 0x08]; \
-       st      %t7, [%dst - (offset) - 0x04];
-
-#define RMOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
-       ldd     [%src - (offset) - 0x20], %t0; \
-       ldd     [%src - (offset) - 0x18], %t2; \
-       ldd     [%src - (offset) - 0x10], %t4; \
-       ldd     [%src - (offset) - 0x08], %t6; \
-       std     %t0, [%dst - (offset) - 0x20]; \
-       std     %t2, [%dst - (offset) - 0x18]; \
-       std     %t4, [%dst - (offset) - 0x10]; \
-       std     %t6, [%dst - (offset) - 0x08];
-
-#define RMOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       st      %t0, [%dst + (offset) + 0x00]; \
-       st      %t1, [%dst + (offset) + 0x04]; \
-       st      %t2, [%dst + (offset) + 0x08]; \
-       st      %t3, [%dst + (offset) + 0x0c];
-
-#define RMOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
-       ldub    [%src + (offset) + 0x00], %t0; \
-       ldub    [%src + (offset) + 0x01], %t1; \
-       stb     %t0, [%dst + (offset) + 0x00]; \
-       stb     %t1, [%dst + (offset) + 0x01];
-
-#define SMOVE_CHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       srl     %t0, shir, %t5; \
-       srl     %t1, shir, %t6; \
-       sll     %t0, shil, %t0; \
-       or      %t5, %prev, %t5; \
-       sll     %t1, shil, %prev; \
-       or      %t6, %t0, %t0; \
-       srl     %t2, shir, %t1; \
-       srl     %t3, shir, %t6; \
-       sll     %t2, shil, %t2; \
-       or      %t1, %prev, %t1; \
-       std     %t4, [%dst + (offset) + (offset2) - 0x04]; \
-       std     %t0, [%dst + (offset) + (offset2) + 0x04]; \
-       sll     %t3, shil, %prev; \
-       or      %t6, %t2, %t4;
-
-#define SMOVE_ALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, prev, shil, shir, offset2) \
-       ldd     [%src + (offset) + 0x00], %t0; \
-       ldd     [%src + (offset) + 0x08], %t2; \
-       srl     %t0, shir, %t4; \
-       srl     %t1, shir, %t5; \
-       sll     %t0, shil, %t6; \
-       or      %t4, %prev, %t0; \
-       sll     %t1, shil, %prev; \
-       or      %t5, %t6, %t1; \
-       srl     %t2, shir, %t4; \
-       srl     %t3, shir, %t5; \
-       sll     %t2, shil, %t6; \
-       or      %t4, %prev, %t2; \
-       sll     %t3, shil, %prev; \
-       or      %t5, %t6, %t3; \
-       std     %t0, [%dst + (offset) + (offset2) + 0x00]; \
-       std     %t2, [%dst + (offset) + (offset2) + 0x08];
-
        .text
        .align  4
 
-0:
-       retl
-        nop            ! Only bcopy returns here and it retuns void...
-
-#ifdef __KERNEL__
-FUNC(amemmove)
-FUNC(__memmove)
-#endif
 FUNC(memmove)
+EXPORT_SYMBOL(memmove)
        cmp             %o0, %o1
        mov             %o0, %g7
        bleu            9f
@@ -202,6 +121,7 @@ FUNC(memmove)
         add            %o0, 2, %o0
 
 FUNC(memcpy)   /* %o0=dst %o1=src %o2=len */
+EXPORT_SYMBOL(memcpy)
 
        sub             %o0, %o1, %o4
        mov             %o0, %g7
index 857ad4f..012cdb6 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(memmove) /* o0=dst o1=src o2=len */
@@ -57,3 +58,4 @@ ENTRY(memmove) /* o0=dst o1=src o2=len */
         stb            %g7, [%o0 - 0x1]
        ba,a,pt         %xcc, 99b
 ENDPROC(memmove)
+EXPORT_SYMBOL(memmove)
index 4ff1657..51ce690 100644 (file)
@@ -4,6 +4,8 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <asm/export.h>
+
 /* In essence, this is just a fancy strlen. */
 
 #define LO_MAGIC 0x01010101
@@ -13,6 +15,8 @@
        .align  4
        .globl  __memscan_zero, __memscan_generic
        .globl  memscan
+EXPORT_SYMBOL(__memscan_zero)
+EXPORT_SYMBOL(__memscan_generic)
 __memscan_zero:
        /* %o0 = addr, %o1 = size */
        cmp     %o1, 0
index 5686dfa..daa96f4 100644 (file)
@@ -5,6 +5,8 @@
  * Copyright (C) 1998 David S. Miller (davem@redhat.com)
  */
 
+       #include <asm/export.h>
+
 #define HI_MAGIC       0x8080808080808080
 #define LO_MAGIC       0x0101010101010101
 #define ASI_PL         0x88
@@ -13,6 +15,8 @@
        .align  32
        .globl          __memscan_zero, __memscan_generic
        .globl          memscan
+       EXPORT_SYMBOL(__memscan_zero)
+       EXPORT_SYMBOL(__memscan_generic)
 
 __memscan_zero:
        /* %o0 = bufp, %o1 = size */
index f75e690..bb539b4 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include <asm/ptrace.h>
+#include <asm/export.h>
 
 /* Work around cpp -rob */
 #define ALLOC #alloc
@@ -63,6 +64,8 @@ __bzero_begin:
 
        .globl  __bzero
        .globl  memset
+       EXPORT_SYMBOL(__bzero)
+       EXPORT_SYMBOL(memset)
        .globl  __memset_start, __memset_end
 __memset_start:
 memset:
index 9794939..17a0f49 100644 (file)
@@ -17,6 +17,7 @@ along with GNU CC; see the file COPYING.  If not, write to
 the Free Software Foundation, 59 Temple Place - Suite 330,
 Boston, MA 02111-1307, USA.  */
 
+#include <asm/export.h>
        .text
        .align 4
        .globl __muldi3
@@ -74,3 +75,4 @@ __muldi3:
        add  %l2, %l0, %i0
        ret 
        restore  %g0, %l3, %o1
+EXPORT_SYMBOL(__muldi3)
index 536f835..ca0e707 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asm.h>
+#include <asm/export.h>
 
 #define LO_MAGIC 0x01010101
 #define HI_MAGIC 0x80808080
@@ -78,3 +79,4 @@ ENTRY(strlen)
        retl
         mov    2, %o0
 ENDPROC(strlen)
+EXPORT_SYMBOL(strlen)
index c0d1b56..e3fe014 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
        .text
 ENTRY(strncmp)
@@ -116,3 +117,4 @@ ENTRY(strncmp)
        retl
         sub    %o3, %o0, %o0
 ENDPROC(strncmp)
+EXPORT_SYMBOL(strncmp)
index 0656627..efb5f88 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/linkage.h>
 #include <asm/asi.h>
+#include <asm/export.h>
 
        .text
 ENTRY(strncmp)
@@ -28,3 +29,4 @@ ENTRY(strncmp)
        retl
         clr    %o0
 ENDPROC(strncmp)
+EXPORT_SYMBOL(strncmp)
index 2c05641..45a49cb 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/asi.h>
 #include <asm/dcu.h>
 #include <asm/spitfire.h>
+#include <asm/export.h>
 
 /*
  *     Requirements:
@@ -90,6 +91,7 @@ ENTRY(xor_vis_2)
        retl
          wr    %g0, 0, %fprs
 ENDPROC(xor_vis_2)
+EXPORT_SYMBOL(xor_vis_2)
 
 ENTRY(xor_vis_3)
        rd      %fprs, %o5
@@ -156,6 +158,7 @@ ENTRY(xor_vis_3)
        retl
         wr     %g0, 0, %fprs
 ENDPROC(xor_vis_3)
+EXPORT_SYMBOL(xor_vis_3)
 
 ENTRY(xor_vis_4)
        rd      %fprs, %o5
@@ -241,6 +244,7 @@ ENTRY(xor_vis_4)
        retl
         wr     %g0, 0, %fprs
 ENDPROC(xor_vis_4)
+EXPORT_SYMBOL(xor_vis_4)
 
 ENTRY(xor_vis_5)
        save    %sp, -192, %sp
@@ -347,6 +351,7 @@ ENTRY(xor_vis_5)
        ret
         restore
 ENDPROC(xor_vis_5)
+EXPORT_SYMBOL(xor_vis_5)
 
        /* Niagara versions. */
 ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */
@@ -393,6 +398,7 @@ ENTRY(xor_niagara_2) /* %o0=bytes, %o1=dest, %o2=src */
        ret
         restore
 ENDPROC(xor_niagara_2)
+EXPORT_SYMBOL(xor_niagara_2)
 
 ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */
        save            %sp, -192, %sp
@@ -454,6 +460,7 @@ ENTRY(xor_niagara_3) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2 */
        ret
         restore
 ENDPROC(xor_niagara_3)
+EXPORT_SYMBOL(xor_niagara_3)
 
 ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */
        save            %sp, -192, %sp
@@ -536,6 +543,7 @@ ENTRY(xor_niagara_4) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3 */
        ret
         restore
 ENDPROC(xor_niagara_4)
+EXPORT_SYMBOL(xor_niagara_4)
 
 ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=src4 */
        save            %sp, -192, %sp
@@ -634,3 +642,4 @@ ENTRY(xor_niagara_5) /* %o0=bytes, %o1=dest, %o2=src1, %o3=src2, %o4=src3, %o5=s
        ret
         restore
 ENDPROC(xor_niagara_5)
+EXPORT_SYMBOL(xor_niagara_5)
index 851a94e..ef61c59 100644 (file)
@@ -88,6 +88,5 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 
 unsigned long arch_randomize_brk(struct mm_struct *mm)
 {
-       unsigned long range_end = mm->brk + 0x02000000;
-       return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+       return randomize_page(mm->brk, 0x02000000);
 }
index 00299c9..d7c6b67 100644 (file)
@@ -295,8 +295,7 @@ unsigned long get_wchan(struct task_struct *p)
 
 unsigned long arch_randomize_brk(struct mm_struct *mm)
 {
-       unsigned long range_end = mm->brk + 0x02000000;
-       return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+       return randomize_page(mm->brk, 0x02000000);
 }
 
 /*
index b75a8bc..21b352a 100644 (file)
@@ -44,6 +44,7 @@
 #include <asm/alternative-asm.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
        .section .entry.text, "ax"
 
@@ -991,6 +992,7 @@ trace:
        jmp     ftrace_stub
 END(mcount)
 #endif /* CONFIG_DYNAMIC_FTRACE */
+EXPORT_SYMBOL(mcount)
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
index c98ec2e..ef766a3 100644 (file)
@@ -35,6 +35,7 @@
 #include <asm/asm.h>
 #include <asm/smap.h>
 #include <asm/pgtable_types.h>
+#include <asm/export.h>
 #include <linux/err.h>
 
 /* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this.  */
@@ -875,6 +876,7 @@ ENTRY(native_load_gs_index)
        popfq
        ret
 END(native_load_gs_index)
+EXPORT_SYMBOL(native_load_gs_index)
 
        _ASM_EXTABLE(.Lgs_change, bad_gs)
        .section .fixup, "ax"
index e5a1711..fee6bc7 100644 (file)
@@ -6,6 +6,7 @@
  */
        #include <linux/linkage.h>
        #include <asm/asm.h>
+       #include <asm/export.h>
 
        /* put return address in eax (arg1) */
        .macro THUNK name, func, put_ret_addr_in_eax=0
@@ -36,5 +37,7 @@
 #ifdef CONFIG_PREEMPT
        THUNK ___preempt_schedule, preempt_schedule
        THUNK ___preempt_schedule_notrace, preempt_schedule_notrace
+       EXPORT_SYMBOL(___preempt_schedule)
+       EXPORT_SYMBOL(___preempt_schedule_notrace)
 #endif
 
index 627ecbc..be36bf4 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include "calling.h"
 #include <asm/asm.h>
+#include <asm/export.h>
 
        /* rdi: arg1 ... normal C conventions. rax is saved/restored. */
        .macro THUNK name, func, put_ret_addr_in_rdi=0
@@ -49,6 +50,8 @@
 #ifdef CONFIG_PREEMPT
        THUNK ___preempt_schedule, preempt_schedule
        THUNK ___preempt_schedule_notrace, preempt_schedule_notrace
+       EXPORT_SYMBOL(___preempt_schedule)
+       EXPORT_SYMBOL(___preempt_schedule_notrace)
 #endif
 
 #if defined(CONFIG_TRACE_IRQFLAGS) \
index 61518cf..872877d 100644 (file)
@@ -4,7 +4,6 @@
 /* Caches aren't brain-dead on the intel. */
 #include <asm-generic/cacheflush.h>
 #include <asm/special_insns.h>
-#include <asm/uaccess.h>
 
 /*
  * The set_memory_* API can be used to change various attributes of a virtual
diff --git a/arch/x86/include/asm/export.h b/arch/x86/include/asm/export.h
new file mode 100644 (file)
index 0000000..138de56
--- /dev/null
@@ -0,0 +1,4 @@
+#ifdef CONFIG_64BIT
+#define KSYM_ALIGN 16
+#endif
+#include <asm-generic/export.h>
diff --git a/arch/x86/include/asm/extable.h b/arch/x86/include/asm/extable.h
new file mode 100644 (file)
index 0000000..b8ad261
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef _ASM_X86_EXTABLE_H
+#define _ASM_X86_EXTABLE_H
+/*
+ * The exception table consists of triples of addresses relative to the
+ * exception table entry itself. The first address is of an instruction
+ * that is allowed to fault, the second is the target at which the program
+ * should continue. The third is a handler function to deal with the fault
+ * caused by the instruction in the first field.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry {
+       int insn, fixup, handler;
+};
+struct pt_regs;
+
+#define ARCH_HAS_RELATIVE_EXTABLE
+
+#define swap_ex_entry_fixup(a, b, tmp, delta)                  \
+       do {                                                    \
+               (a)->fixup = (b)->fixup + (delta);              \
+               (b)->fixup = (tmp).fixup - (delta);             \
+               (a)->handler = (b)->handler + (delta);          \
+               (b)->handler = (tmp).handler - (delta);         \
+       } while (0)
+
+extern int fixup_exception(struct pt_regs *regs, int trapnr);
+extern bool ex_has_fault_handler(unsigned long ip);
+extern void early_fixup_exception(struct pt_regs *regs, int trapnr);
+
+#endif
index d2434c1..282630e 100644 (file)
@@ -210,6 +210,7 @@ struct kexec_entry64_regs {
 
 typedef void crash_vmclear_fn(void);
 extern crash_vmclear_fn __rcu *crash_vmclear_loaded_vmcss;
+extern void kdump_nmi_shootdown_cpus(void);
 
 #endif /* __ASSEMBLY__ */
 
index e02e3f8..84f58de 100644 (file)
@@ -521,7 +521,8 @@ do {                                                                        \
 static __always_inline bool x86_this_cpu_constant_test_bit(unsigned int nr,
                         const unsigned long __percpu *addr)
 {
-       unsigned long __percpu *a = (unsigned long *)addr + nr / BITS_PER_LONG;
+       unsigned long __percpu *a =
+               (unsigned long __percpu *)addr + nr / BITS_PER_LONG;
 
 #ifdef CONFIG_X86_64
        return ((1UL << (nr % BITS_PER_LONG)) & raw_cpu_read_8(*a)) != 0;
@@ -538,7 +539,7 @@ static inline bool x86_this_cpu_variable_test_bit(int nr,
        asm volatile("bt "__percpu_arg(2)",%1\n\t"
                        CC_SET(c)
                        : CC_OUT(c) (oldbit)
-                       : "m" (*(unsigned long *)addr), "Ir" (nr));
+                       : "m" (*(unsigned long __percpu *)addr), "Ir" (nr));
 
        return oldbit;
 }
index 13b6cdd..2f75f30 100644 (file)
@@ -2,7 +2,7 @@
 #define _ASM_X86_SECTIONS_H
 
 #include <asm-generic/sections.h>
-#include <asm/uaccess.h>
+#include <asm/extable.h>
 
 extern char __brk_base[], __brk_limit[];
 extern struct exception_table_entry __stop___ex_table[];
index 19980b3..026ea82 100644 (file)
@@ -47,6 +47,7 @@ struct smp_ops {
        void (*smp_cpus_done)(unsigned max_cpus);
 
        void (*stop_other_cpus)(int wait);
+       void (*crash_stop_other_cpus)(void);
        void (*smp_send_reschedule)(int cpu);
 
        int (*cpu_up)(unsigned cpu, struct task_struct *tidle);
index 2131c4c..faf3687 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/asm.h>
 #include <asm/page.h>
 #include <asm/smap.h>
+#include <asm/extable.h>
 
 #define VERIFY_READ 0
 #define VERIFY_WRITE 1
@@ -90,37 +91,6 @@ static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, un
 #define access_ok(type, addr, size) \
        likely(!__range_not_ok(addr, size, user_addr_max()))
 
-/*
- * The exception table consists of triples of addresses relative to the
- * exception table entry itself. The first address is of an instruction
- * that is allowed to fault, the second is the target at which the program
- * should continue. The third is a handler function to deal with the fault
- * caused by the instruction in the first field.
- *
- * All the routines below use bits of fixup code that are out of line
- * with the main instruction path.  This means when everything is well,
- * we don't even have to jump over them.  Further, they do not intrude
- * on our cache or tlb entries.
- */
-
-struct exception_table_entry {
-       int insn, fixup, handler;
-};
-
-#define ARCH_HAS_RELATIVE_EXTABLE
-
-#define swap_ex_entry_fixup(a, b, tmp, delta)                  \
-       do {                                                    \
-               (a)->fixup = (b)->fixup + (delta);              \
-               (b)->fixup = (tmp).fixup - (delta);             \
-               (a)->handler = (b)->handler + (delta);          \
-               (b)->handler = (tmp).handler - (delta);         \
-       } while (0)
-
-extern int fixup_exception(struct pt_regs *regs, int trapnr);
-extern bool ex_has_fault_handler(unsigned long ip);
-extern void early_fixup_exception(struct pt_regs *regs, int trapnr);
-
 /*
  * These are the main single-value transfer routines.  They automatically
  * use the right size if we just have the right pointer type.
index 4dd5d50..79076d7 100644 (file)
@@ -46,9 +46,7 @@ obj-$(CONFIG_MODIFY_LDT_SYSCALL)      += ldt.o
 obj-y                  += setup.o x86_init.o i8259.o irqinit.o jump_label.o
 obj-$(CONFIG_IRQ_WORK)  += irq_work.o
 obj-y                  += probe_roms.o
-obj-$(CONFIG_X86_32)   += i386_ksyms_32.o
-obj-$(CONFIG_X86_64)   += sys_x86_64.o x8664_ksyms_64.o
-obj-$(CONFIG_X86_64)   += mcount_64.o
+obj-$(CONFIG_X86_64)   += sys_x86_64.o mcount_64.o
 obj-$(CONFIG_X86_ESPFIX64)     += espfix_64.o
 obj-$(CONFIG_SYSFS)    += ksysfs.o
 obj-y                  += bootflag.o e820.o
index 9616cf7..650830e 100644 (file)
@@ -133,15 +133,31 @@ static void kdump_nmi_callback(int cpu, struct pt_regs *regs)
        disable_local_APIC();
 }
 
-static void kdump_nmi_shootdown_cpus(void)
+void kdump_nmi_shootdown_cpus(void)
 {
        nmi_shootdown_cpus(kdump_nmi_callback);
 
        disable_local_APIC();
 }
 
+/* Override the weak function in kernel/panic.c */
+void crash_smp_send_stop(void)
+{
+       static int cpus_stopped;
+
+       if (cpus_stopped)
+               return;
+
+       if (smp_ops.crash_stop_other_cpus)
+               smp_ops.crash_stop_other_cpus();
+       else
+               smp_send_stop();
+
+       cpus_stopped = 1;
+}
+
 #else
-static void kdump_nmi_shootdown_cpus(void)
+void crash_smp_send_stop(void)
 {
        /* There are no cpus to shootdown */
 }
@@ -160,7 +176,7 @@ void native_machine_crash_shutdown(struct pt_regs *regs)
        /* The kernel is broken so disable interrupts */
        local_irq_disable();
 
-       kdump_nmi_shootdown_cpus();
+       crash_smp_send_stop();
 
        /*
         * VMCLEAR VMCSs loaded on this cpu if needed.
index 18bb3a6..6a08e25 100644 (file)
@@ -317,16 +317,11 @@ static phys_addr_t __init i85x_stolen_base(int num, int slot, int func,
 static phys_addr_t __init i865_stolen_base(int num, int slot, int func,
                                           size_t stolen_size)
 {
-       u16 toud;
+       u16 toud = 0;
 
-       /*
-        * FIXME is the graphics stolen memory region
-        * always at TOUD? Ie. is it always the last
-        * one to be allocated by the BIOS?
-        */
        toud = read_pci_config_16(0, 0, 0, I865_TOUD);
 
-       return (phys_addr_t)toud << 16;
+       return (phys_addr_t)(toud << 16) + i845_tseg_size();
 }
 
 static phys_addr_t __init gen3_stolen_base(int num, int slot, int func,
@@ -512,8 +507,7 @@ static const struct pci_device_id intel_early_ids[] __initconst = {
        INTEL_I915GM_IDS(&gen3_early_ops),
        INTEL_I945G_IDS(&gen3_early_ops),
        INTEL_I945GM_IDS(&gen3_early_ops),
-       INTEL_VLV_M_IDS(&gen6_early_ops),
-       INTEL_VLV_D_IDS(&gen6_early_ops),
+       INTEL_VLV_IDS(&gen6_early_ops),
        INTEL_PINEVIEW_IDS(&gen3_early_ops),
        INTEL_I965G_IDS(&gen3_early_ops),
        INTEL_G33_IDS(&gen3_early_ops),
@@ -526,10 +520,8 @@ static const struct pci_device_id intel_early_ids[] __initconst = {
        INTEL_SNB_M_IDS(&gen6_early_ops),
        INTEL_IVB_M_IDS(&gen6_early_ops),
        INTEL_IVB_D_IDS(&gen6_early_ops),
-       INTEL_HSW_D_IDS(&gen6_early_ops),
-       INTEL_HSW_M_IDS(&gen6_early_ops),
-       INTEL_BDW_M_IDS(&gen8_early_ops),
-       INTEL_BDW_D_IDS(&gen8_early_ops),
+       INTEL_HSW_IDS(&gen6_early_ops),
+       INTEL_BDW_IDS(&gen8_early_ops),
        INTEL_CHV_IDS(&chv_early_ops),
        INTEL_SKL_IDS(&gen9_early_ops),
        INTEL_BXT_IDS(&gen9_early_ops),
index 5f40126..b6b2f02 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/percpu.h>
 #include <asm/nops.h>
 #include <asm/bootparam.h>
+#include <asm/export.h>
 
 /* Physical address */
 #define pa(X) ((X) - __PAGE_OFFSET)
@@ -673,6 +674,7 @@ ENTRY(empty_zero_page)
        .fill 4096,1,0
 ENTRY(swapper_pg_dir)
        .fill 1024,4,0
+EXPORT_SYMBOL(empty_zero_page)
 
 /*
  * This starts the data section.
index c98a559..b4421cc 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/percpu.h>
 #include <asm/nops.h>
 #include "../entry/calling.h"
+#include <asm/export.h>
 
 #ifdef CONFIG_PARAVIRT
 #include <asm/asm-offsets.h>
@@ -486,10 +487,12 @@ early_gdt_descr_base:
 ENTRY(phys_base)
        /* This must match the first entry in level2_kernel_pgt */
        .quad   0x0000000000000000
+EXPORT_SYMBOL(phys_base)
 
 #include "../../x86/xen/xen-head.S"
        
        __PAGE_ALIGNED_BSS
 NEXT_PAGE(empty_zero_page)
        .skip PAGE_SIZE
+EXPORT_SYMBOL(empty_zero_page)
 
diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c
deleted file mode 100644 (file)
index 1f9b878..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#include <linux/export.h>
-#include <linux/spinlock_types.h>
-
-#include <asm/checksum.h>
-#include <asm/pgtable.h>
-#include <asm/desc.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount is defined in assembly */
-EXPORT_SYMBOL(mcount);
-#endif
-
-/*
- * Note, this is a prototype to get at the symbol for
- * the export, but dont use it from C code, it is used
- * by assembly code and is not using C calling convention!
- */
-#ifndef CONFIG_X86_CMPXCHG64
-extern void cmpxchg8b_emu(void);
-EXPORT_SYMBOL(cmpxchg8b_emu);
-#endif
-
-/* Networking helper routines. */
-EXPORT_SYMBOL(csum_partial_copy_generic);
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-
-EXPORT_SYMBOL(strstr);
-
-EXPORT_SYMBOL(csum_partial);
-EXPORT_SYMBOL(empty_zero_page);
-
-#ifdef CONFIG_PREEMPT
-EXPORT_SYMBOL(___preempt_schedule);
-EXPORT_SYMBOL(___preempt_schedule_notrace);
-#endif
-
-EXPORT_SYMBOL(__sw_hweight32);
index 5a294e4..8c1f218 100644 (file)
@@ -337,6 +337,9 @@ void arch_crash_save_vmcoreinfo(void)
 #endif
        vmcoreinfo_append_str("KERNELOFFSET=%lx\n",
                              kaslr_offset());
+       VMCOREINFO_PAGE_OFFSET(PAGE_OFFSET);
+       VMCOREINFO_VMALLOC_START(VMALLOC_START);
+       VMCOREINFO_VMEMMAP_START(VMEMMAP_START);
 }
 
 /* arch-dependent functionality related to kexec file-based syscall */
index 6192422..efe73aa 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/linkage.h>
 #include <asm/ptrace.h>
 #include <asm/ftrace.h>
+#include <asm/export.h>
 
 
        .code64
@@ -294,6 +295,7 @@ trace:
        jmp fgraph_trace
 END(function_hook)
 #endif /* CONFIG_DYNAMIC_FTRACE */
+EXPORT_SYMBOL(function_hook)
 #endif /* CONFIG_FUNCTION_TRACER */
 
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
index 28cea78..0888a87 100644 (file)
@@ -509,8 +509,7 @@ unsigned long arch_align_stack(unsigned long sp)
 
 unsigned long arch_randomize_brk(struct mm_struct *mm)
 {
-       unsigned long range_end = mm->brk + 0x02000000;
-       return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
+       return randomize_page(mm->brk, 0x02000000);
 }
 
 /*
index 658777c..68f8cc2 100644 (file)
@@ -32,6 +32,8 @@
 #include <asm/nmi.h>
 #include <asm/mce.h>
 #include <asm/trace/irq_vectors.h>
+#include <asm/kexec.h>
+
 /*
  *     Some notes on x86 processor bugs affecting SMP operation:
  *
@@ -342,6 +344,9 @@ struct smp_ops smp_ops = {
        .smp_cpus_done          = native_smp_cpus_done,
 
        .stop_other_cpus        = native_stop_other_cpus,
+#if defined(CONFIG_KEXEC_CORE)
+       .crash_stop_other_cpus  = kdump_nmi_shootdown_cpus,
+#endif
        .smp_send_reschedule    = native_smp_send_reschedule,
 
        .cpu_up                 = native_cpu_up,
index 10e0272..a55ed63 100644 (file)
@@ -101,7 +101,6 @@ static void find_start_end(unsigned long flags, unsigned long *begin,
                           unsigned long *end)
 {
        if (!test_thread_flag(TIF_ADDR32) && (flags & MAP_32BIT)) {
-               unsigned long new_begin;
                /* This is usually used needed to map code in small
                   model, so it needs to be in the first 31bit. Limit
                   it to that.  This means we need to move the
@@ -112,9 +111,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin,
                *begin = 0x40000000;
                *end = 0x80000000;
                if (current->flags & PF_RANDOMIZE) {
-                       new_begin = randomize_range(*begin, *begin + 0x02000000, 0);
-                       if (new_begin)
-                               *begin = new_begin;
+                       *begin = randomize_page(*begin, 0x02000000);
                }
        } else {
                *begin = current->mm->mmap_legacy_base;
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
deleted file mode 100644 (file)
index b2cee3d..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Exports for assembly files.
-   All C exports should go in the respective C files. */
-
-#include <linux/export.h>
-#include <linux/spinlock_types.h>
-#include <linux/smp.h>
-
-#include <net/checksum.h>
-
-#include <asm/processor.h>
-#include <asm/pgtable.h>
-#include <asm/uaccess.h>
-#include <asm/desc.h>
-#include <asm/ftrace.h>
-
-#ifdef CONFIG_FUNCTION_TRACER
-/* mcount and __fentry__ are defined in assembly */
-#ifdef CC_USING_FENTRY
-EXPORT_SYMBOL(__fentry__);
-#else
-EXPORT_SYMBOL(mcount);
-#endif
-#endif
-
-EXPORT_SYMBOL(__get_user_1);
-EXPORT_SYMBOL(__get_user_2);
-EXPORT_SYMBOL(__get_user_4);
-EXPORT_SYMBOL(__get_user_8);
-EXPORT_SYMBOL(__put_user_1);
-EXPORT_SYMBOL(__put_user_2);
-EXPORT_SYMBOL(__put_user_4);
-EXPORT_SYMBOL(__put_user_8);
-
-EXPORT_SYMBOL(copy_user_generic_string);
-EXPORT_SYMBOL(copy_user_generic_unrolled);
-EXPORT_SYMBOL(copy_user_enhanced_fast_string);
-EXPORT_SYMBOL(__copy_user_nocache);
-EXPORT_SYMBOL(_copy_from_user);
-EXPORT_SYMBOL(_copy_to_user);
-
-EXPORT_SYMBOL_GPL(memcpy_mcsafe_unrolled);
-
-EXPORT_SYMBOL(copy_page);
-EXPORT_SYMBOL(clear_page);
-
-EXPORT_SYMBOL(csum_partial);
-
-EXPORT_SYMBOL(__sw_hweight32);
-EXPORT_SYMBOL(__sw_hweight64);
-
-/*
- * Export string functions. We normally rely on gcc builtin for most of these,
- * but gcc sometimes decides not to inline them.
- */
-#undef memcpy
-#undef memset
-#undef memmove
-
-extern void *__memset(void *, int, __kernel_size_t);
-extern void *__memcpy(void *, const void *, __kernel_size_t);
-extern void *__memmove(void *, const void *, __kernel_size_t);
-extern void *memset(void *, int, __kernel_size_t);
-extern void *memcpy(void *, const void *, __kernel_size_t);
-extern void *memmove(void *, const void *, __kernel_size_t);
-
-EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(__memcpy);
-EXPORT_SYMBOL(__memmove);
-
-EXPORT_SYMBOL(memset);
-EXPORT_SYMBOL(memcpy);
-EXPORT_SYMBOL(memmove);
-
-#ifndef CONFIG_DEBUG_VIRTUAL
-EXPORT_SYMBOL(phys_base);
-#endif
-EXPORT_SYMBOL(empty_zero_page);
-#ifndef CONFIG_PARAVIRT
-EXPORT_SYMBOL(native_load_gs_index);
-#endif
-
-#ifdef CONFIG_PREEMPT
-EXPORT_SYMBOL(___preempt_schedule);
-EXPORT_SYMBOL(___preempt_schedule_notrace);
-#endif
index 5fb6c62..16a7134 100644 (file)
@@ -212,7 +212,7 @@ static void kvm_pit_ack_irq(struct kvm_irq_ack_notifier *kian)
         */
        smp_mb();
        if (atomic_dec_if_positive(&ps->pending) > 0)
-               queue_kthread_work(&pit->worker, &pit->expired);
+               kthread_queue_work(&pit->worker, &pit->expired);
 }
 
 void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
@@ -233,7 +233,7 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
 static void destroy_pit_timer(struct kvm_pit *pit)
 {
        hrtimer_cancel(&pit->pit_state.timer);
-       flush_kthread_work(&pit->expired);
+       kthread_flush_work(&pit->expired);
 }
 
 static void pit_do_work(struct kthread_work *work)
@@ -272,7 +272,7 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
        if (atomic_read(&ps->reinject))
                atomic_inc(&ps->pending);
 
-       queue_kthread_work(&pt->worker, &pt->expired);
+       kthread_queue_work(&pt->worker, &pt->expired);
 
        if (ps->is_periodic) {
                hrtimer_add_expires_ns(&ps->timer, ps->period);
@@ -324,7 +324,7 @@ static void create_pit_timer(struct kvm_pit *pit, u32 val, int is_period)
 
        /* TODO The new value only affected after the retriggered */
        hrtimer_cancel(&ps->timer);
-       flush_kthread_work(&pit->expired);
+       kthread_flush_work(&pit->expired);
        ps->period = interval;
        ps->is_periodic = is_period;
 
@@ -667,13 +667,13 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
        pid_nr = pid_vnr(pid);
        put_pid(pid);
 
-       init_kthread_worker(&pit->worker);
+       kthread_init_worker(&pit->worker);
        pit->worker_task = kthread_run(kthread_worker_fn, &pit->worker,
                                       "kvm-pit/%d", pid_nr);
        if (IS_ERR(pit->worker_task))
                goto fail_kthread;
 
-       init_kthread_work(&pit->expired, pit_do_work);
+       kthread_init_work(&pit->expired, pit_do_work);
 
        pit->kvm = kvm;
 
@@ -730,7 +730,7 @@ void kvm_free_pit(struct kvm *kvm)
                kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &pit->speaker_dev);
                kvm_pit_set_reinject(pit, false);
                hrtimer_cancel(&pit->pit_state.timer);
-               flush_kthread_work(&pit->expired);
+               kthread_flush_work(&pit->expired);
                kthread_stop(pit->worker_task);
                kvm_free_irq_source_id(kvm, pit->irq_source_id);
                kfree(pit);
index c1e6232..4d34bb5 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/linkage.h>
 #include <asm/errno.h>
 #include <asm/asm.h>
+#include <asm/export.h>
                                
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -251,6 +252,7 @@ ENTRY(csum_partial)
 ENDPROC(csum_partial)
                                
 #endif
+EXPORT_SYMBOL(csum_partial)
 
 /*
 unsigned int csum_partial_copy_generic (const char *src, char *dst,
@@ -490,3 +492,4 @@ ENDPROC(csum_partial_copy_generic)
 #undef ROUND1          
                
 #endif
+EXPORT_SYMBOL(csum_partial_copy_generic)
index 65be7cf..5e2af3a 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * Most CPUs support enhanced REP MOVSB/STOSB instructions. It is
@@ -23,6 +24,7 @@ ENTRY(clear_page)
        rep stosq
        ret
 ENDPROC(clear_page)
+EXPORT_SYMBOL(clear_page)
 
 ENTRY(clear_page_orig)
 
index ad53497..03a186f 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 .text
 
@@ -48,3 +49,4 @@ ENTRY(cmpxchg8b_emu)
        ret
 
 ENDPROC(cmpxchg8b_emu)
+EXPORT_SYMBOL(cmpxchg8b_emu)
index 24ef1c2..e850815 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * Some CPUs run faster using the string copy instructions (sane microcode).
@@ -17,6 +18,7 @@ ENTRY(copy_page)
        rep     movsq
        ret
 ENDPROC(copy_page)
+EXPORT_SYMBOL(copy_page)
 
 ENTRY(copy_page_regs)
        subq    $2*8,   %rsp
index bf603eb..d376e4b 100644 (file)
@@ -14,6 +14,7 @@
 #include <asm/alternative-asm.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
 /* Standard copy_to_user with segment limit checking */
 ENTRY(_copy_to_user)
@@ -29,6 +30,7 @@ ENTRY(_copy_to_user)
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
 ENDPROC(_copy_to_user)
+EXPORT_SYMBOL(_copy_to_user)
 
 /* Standard copy_from_user with segment limit checking */
 ENTRY(_copy_from_user)
@@ -44,6 +46,8 @@ ENTRY(_copy_from_user)
                      "jmp copy_user_enhanced_fast_string",     \
                      X86_FEATURE_ERMS
 ENDPROC(_copy_from_user)
+EXPORT_SYMBOL(_copy_from_user)
+
 
        .section .fixup,"ax"
        /* must zero dest */
@@ -155,6 +159,7 @@ ENTRY(copy_user_generic_unrolled)
        _ASM_EXTABLE(21b,50b)
        _ASM_EXTABLE(22b,50b)
 ENDPROC(copy_user_generic_unrolled)
+EXPORT_SYMBOL(copy_user_generic_unrolled)
 
 /* Some CPUs run faster using the string copy instructions.
  * This is also a lot simpler. Use them when possible.
@@ -200,6 +205,7 @@ ENTRY(copy_user_generic_string)
        _ASM_EXTABLE(1b,11b)
        _ASM_EXTABLE(3b,12b)
 ENDPROC(copy_user_generic_string)
+EXPORT_SYMBOL(copy_user_generic_string)
 
 /*
  * Some CPUs are adding enhanced REP MOVSB/STOSB instructions.
@@ -229,6 +235,7 @@ ENTRY(copy_user_enhanced_fast_string)
 
        _ASM_EXTABLE(1b,12b)
 ENDPROC(copy_user_enhanced_fast_string)
+EXPORT_SYMBOL(copy_user_enhanced_fast_string)
 
 /*
  * copy_user_nocache - Uncached memory copy with exception handling
@@ -379,3 +386,4 @@ ENTRY(__copy_user_nocache)
        _ASM_EXTABLE(40b,.L_fixup_1b_copy)
        _ASM_EXTABLE(41b,.L_fixup_1b_copy)
 ENDPROC(__copy_user_nocache)
+EXPORT_SYMBOL(__copy_user_nocache)
index 9a7fe6a..378e5d5 100644 (file)
@@ -135,6 +135,7 @@ __wsum csum_partial(const void *buff, int len, __wsum sum)
        return (__force __wsum)add32_with_carry(do_csum(buff, len),
                                                (__force u32)sum);
 }
+EXPORT_SYMBOL(csum_partial);
 
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
index 0ef5128..37b62d4 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/thread_info.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
        .text
 ENTRY(__get_user_1)
@@ -44,6 +45,7 @@ ENTRY(__get_user_1)
        ASM_CLAC
        ret
 ENDPROC(__get_user_1)
+EXPORT_SYMBOL(__get_user_1)
 
 ENTRY(__get_user_2)
        add $1,%_ASM_AX
@@ -57,6 +59,7 @@ ENTRY(__get_user_2)
        ASM_CLAC
        ret
 ENDPROC(__get_user_2)
+EXPORT_SYMBOL(__get_user_2)
 
 ENTRY(__get_user_4)
        add $3,%_ASM_AX
@@ -70,6 +73,7 @@ ENTRY(__get_user_4)
        ASM_CLAC
        ret
 ENDPROC(__get_user_4)
+EXPORT_SYMBOL(__get_user_4)
 
 ENTRY(__get_user_8)
 #ifdef CONFIG_X86_64
@@ -97,6 +101,7 @@ ENTRY(__get_user_8)
        ret
 #endif
 ENDPROC(__get_user_8)
+EXPORT_SYMBOL(__get_user_8)
 
 
 bad_get_user:
index 8a602a1..23d893c 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/linkage.h>
+#include <asm/export.h>
 
 #include <asm/asm.h>
 
@@ -32,6 +33,7 @@ ENTRY(__sw_hweight32)
        __ASM_SIZE(pop,) %__ASM_REG(dx)
        ret
 ENDPROC(__sw_hweight32)
+EXPORT_SYMBOL(__sw_hweight32)
 
 ENTRY(__sw_hweight64)
 #ifdef CONFIG_X86_64
@@ -77,3 +79,4 @@ ENTRY(__sw_hweight64)
        ret
 #endif
 ENDPROC(__sw_hweight64)
+EXPORT_SYMBOL(__sw_hweight64)
index 49e6eba..779782f 100644 (file)
@@ -4,6 +4,7 @@
 #include <asm/errno.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 /*
  * We build a jump to memcpy_orig by default which gets NOPped out on
@@ -40,6 +41,8 @@ ENTRY(memcpy)
        ret
 ENDPROC(memcpy)
 ENDPROC(__memcpy)
+EXPORT_SYMBOL(memcpy)
+EXPORT_SYMBOL(__memcpy)
 
 /*
  * memcpy_erms() - enhanced fast string memcpy. This is faster and
@@ -274,6 +277,7 @@ ENTRY(memcpy_mcsafe_unrolled)
        xorq %rax, %rax
        ret
 ENDPROC(memcpy_mcsafe_unrolled)
+EXPORT_SYMBOL_GPL(memcpy_mcsafe_unrolled)
 
        .section .fixup, "ax"
        /* Return -EFAULT for any failure */
index 90ce01b..15de86c 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 #undef memmove
 
@@ -207,3 +208,5 @@ ENTRY(__memmove)
        retq
 ENDPROC(__memmove)
 ENDPROC(memmove)
+EXPORT_SYMBOL(__memmove)
+EXPORT_SYMBOL(memmove)
index e1229ec..55b95db 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/linkage.h>
 #include <asm/cpufeatures.h>
 #include <asm/alternative-asm.h>
+#include <asm/export.h>
 
 .weak memset
 
@@ -43,6 +44,8 @@ ENTRY(__memset)
        ret
 ENDPROC(memset)
 ENDPROC(__memset)
+EXPORT_SYMBOL(memset)
+EXPORT_SYMBOL(__memset)
 
 /*
  * ISO C memset - set a memory block to a byte value. This function uses
index c891ece..cd5d716 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/errno.h>
 #include <asm/asm.h>
 #include <asm/smap.h>
+#include <asm/export.h>
 
 
 /*
@@ -43,6 +44,7 @@ ENTRY(__put_user_1)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_1)
+EXPORT_SYMBOL(__put_user_1)
 
 ENTRY(__put_user_2)
        ENTER
@@ -55,6 +57,7 @@ ENTRY(__put_user_2)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_2)
+EXPORT_SYMBOL(__put_user_2)
 
 ENTRY(__put_user_4)
        ENTER
@@ -67,6 +70,7 @@ ENTRY(__put_user_4)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_4)
+EXPORT_SYMBOL(__put_user_4)
 
 ENTRY(__put_user_8)
        ENTER
@@ -82,6 +86,7 @@ ENTRY(__put_user_8)
        xor %eax,%eax
        EXIT
 ENDPROC(__put_user_8)
+EXPORT_SYMBOL(__put_user_8)
 
 bad_put_user:
        movl $-EFAULT,%eax
index 8e2d55f..a03b1c7 100644 (file)
@@ -1,4 +1,5 @@
 #include <linux/string.h>
+#include <linux/export.h>
 
 char *strstr(const char *cs, const char *ct)
 {
@@ -28,4 +29,4 @@ __asm__ __volatile__(
        : "dx", "di");
 return __res;
 }
-
+EXPORT_SYMBOL(strstr);
index 4dc1334..9f72ca3 100644 (file)
@@ -5,7 +5,7 @@
  */
 #include <linux/sched.h>               /* test_thread_flag(), ...      */
 #include <linux/kdebug.h>              /* oops_begin/end, ...          */
-#include <linux/extable.h>             /* search_exception_table       */
+#include <linux/extable.h>             /* search_exception_tables      */
 #include <linux/bootmem.h>             /* max_low_pfn                  */
 #include <linux/kprobes.h>             /* NOKPROBE_SYMBOL, ...         */
 #include <linux/mmiotrace.h>           /* kmmio_handler, ...           */
index 3ee2bb6..e7e7055 100644 (file)
@@ -8,7 +8,7 @@ else
        BITS := 64
 endif
 
-obj-y = bug.o bugs_$(BITS).o delay.o fault.o ksyms.o ldt.o \
+obj-y = bug.o bugs_$(BITS).o delay.o fault.o ldt.o \
        ptrace_$(BITS).o ptrace_user.o setjmp_$(BITS).o signal.o \
        stub_$(BITS).o stub_segv.o \
        sys_call_table_$(BITS).o sysrq_$(BITS).o tls_$(BITS).o \
index fa4b8b9..b9933eb 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <asm/errno.h>
 #include <asm/asm.h>
+#include <asm/export.h>
                                
 /*
  * computes a partial checksum, e.g. for TCP/UDP fragments
@@ -214,3 +215,4 @@ csum_partial:
        ret
                                
 #endif
+       EXPORT_SYMBOL(csum_partial)
diff --git a/arch/x86/um/ksyms.c b/arch/x86/um/ksyms.c
deleted file mode 100644 (file)
index 2e8f43e..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <linux/module.h>
-#include <asm/string.h>
-#include <asm/checksum.h>
-
-#ifndef CONFIG_X86_32
-/*XXX: we need them because they would be exported by x86_64 */
-#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) || __GNUC__ > 4
-EXPORT_SYMBOL(memcpy);
-#else
-EXPORT_SYMBOL(__memcpy);
-#endif
-#endif
-EXPORT_SYMBOL(csum_partial);
diff --git a/arch/xtensa/include/asm/asm-uaccess.h b/arch/xtensa/include/asm/asm-uaccess.h
new file mode 100644 (file)
index 0000000..a7a1100
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * include/asm-xtensa/uaccess.h
+ *
+ * User space memory access functions
+ *
+ * These routines provide basic accessing functions to the user memory
+ * space for the kernel. This header file provides functions such as:
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2001 - 2005 Tensilica Inc.
+ */
+
+#ifndef _XTENSA_ASM_UACCESS_H
+#define _XTENSA_ASM_UACCESS_H
+
+#include <linux/errno.h>
+#include <asm/types.h>
+
+#define VERIFY_READ    0
+#define VERIFY_WRITE   1
+
+#include <asm/current.h>
+#include <asm/asm-offsets.h>
+#include <asm/processor.h>
+
+/*
+ * These assembly macros mirror the C macros in asm/uaccess.h.  They
+ * should always have identical functionality.  See
+ * arch/xtensa/kernel/sys.S for usage.
+ */
+
+#define KERNEL_DS      0
+#define USER_DS                1
+
+#define get_ds         (KERNEL_DS)
+
+/*
+ * get_fs reads current->thread.current_ds into a register.
+ * On Entry:
+ *     <ad>    anything
+ *     <sp>    stack
+ * On Exit:
+ *     <ad>    contains current->thread.current_ds
+ */
+       .macro  get_fs  ad, sp
+       GET_CURRENT(\ad,\sp)
+#if THREAD_CURRENT_DS > 1020
+       addi    \ad, \ad, TASK_THREAD
+       l32i    \ad, \ad, THREAD_CURRENT_DS - TASK_THREAD
+#else
+       l32i    \ad, \ad, THREAD_CURRENT_DS
+#endif
+       .endm
+
+/*
+ * set_fs sets current->thread.current_ds to some value.
+ * On Entry:
+ *     <at>    anything (temp register)
+ *     <av>    value to write
+ *     <sp>    stack
+ * On Exit:
+ *     <at>    destroyed (actually, current)
+ *     <av>    preserved, value to write
+ */
+       .macro  set_fs  at, av, sp
+       GET_CURRENT(\at,\sp)
+       s32i    \av, \at, THREAD_CURRENT_DS
+       .endm
+
+/*
+ * kernel_ok determines whether we should bypass addr/size checking.
+ * See the equivalent C-macro version below for clarity.
+ * On success, kernel_ok branches to a label indicated by parameter
+ * <success>.  This implies that the macro falls through to the next
+ * insruction on an error.
+ *
+ * Note that while this macro can be used independently, we designed
+ * in for optimal use in the access_ok macro below (i.e., we fall
+ * through on error).
+ *
+ * On Entry:
+ *     <at>            anything (temp register)
+ *     <success>       label to branch to on success; implies
+ *                     fall-through macro on error
+ *     <sp>            stack pointer
+ * On Exit:
+ *     <at>            destroyed (actually, current->thread.current_ds)
+ */
+
+#if ((KERNEL_DS != 0) || (USER_DS == 0))
+# error Assembly macro kernel_ok fails
+#endif
+       .macro  kernel_ok  at, sp, success
+       get_fs  \at, \sp
+       beqz    \at, \success
+       .endm
+
+/*
+ * user_ok determines whether the access to user-space memory is allowed.
+ * See the equivalent C-macro version below for clarity.
+ *
+ * On error, user_ok branches to a label indicated by parameter
+ * <error>.  This implies that the macro falls through to the next
+ * instruction on success.
+ *
+ * Note that while this macro can be used independently, we designed
+ * in for optimal use in the access_ok macro below (i.e., we fall
+ * through on success).
+ *
+ * On Entry:
+ *     <aa>    register containing memory address
+ *     <as>    register containing memory size
+ *     <at>    temp register
+ *     <error> label to branch to on error; implies fall-through
+ *             macro on success
+ * On Exit:
+ *     <aa>    preserved
+ *     <as>    preserved
+ *     <at>    destroyed (actually, (TASK_SIZE + 1 - size))
+ */
+       .macro  user_ok aa, as, at, error
+       movi    \at, __XTENSA_UL_CONST(TASK_SIZE)
+       bgeu    \as, \at, \error
+       sub     \at, \at, \as
+       bgeu    \aa, \at, \error
+       .endm
+
+/*
+ * access_ok determines whether a memory access is allowed.  See the
+ * equivalent C-macro version below for clarity.
+ *
+ * On error, access_ok branches to a label indicated by parameter
+ * <error>.  This implies that the macro falls through to the next
+ * instruction on success.
+ *
+ * Note that we assume success is the common case, and we optimize the
+ * branch fall-through case on success.
+ *
+ * On Entry:
+ *     <aa>    register containing memory address
+ *     <as>    register containing memory size
+ *     <at>    temp register
+ *     <sp>
+ *     <error> label to branch to on error; implies fall-through
+ *             macro on success
+ * On Exit:
+ *     <aa>    preserved
+ *     <as>    preserved
+ *     <at>    destroyed
+ */
+       .macro  access_ok  aa, as, at, sp, error
+       kernel_ok  \at, \sp, .Laccess_ok_\@
+       user_ok    \aa, \as, \at, \error
+.Laccess_ok_\@:
+       .endm
+
+#endif /* _XTENSA_ASM_UACCESS_H */
index 147b26e..848a3d7 100644 (file)
 #define _XTENSA_UACCESS_H
 
 #include <linux/errno.h>
-#ifndef __ASSEMBLY__
 #include <linux/prefetch.h>
-#endif
 #include <asm/types.h>
 
 #define VERIFY_READ    0
 #define VERIFY_WRITE   1
 
-#ifdef __ASSEMBLY__
-
-#include <asm/current.h>
-#include <asm/asm-offsets.h>
-#include <asm/processor.h>
-
-/*
- * These assembly macros mirror the C macros that follow below.  They
- * should always have identical functionality.  See
- * arch/xtensa/kernel/sys.S for usage.
- */
-
-#define KERNEL_DS      0
-#define USER_DS                1
-
-#define get_ds         (KERNEL_DS)
-
-/*
- * get_fs reads current->thread.current_ds into a register.
- * On Entry:
- *     <ad>    anything
- *     <sp>    stack
- * On Exit:
- *     <ad>    contains current->thread.current_ds
- */
-       .macro  get_fs  ad, sp
-       GET_CURRENT(\ad,\sp)
-#if THREAD_CURRENT_DS > 1020
-       addi    \ad, \ad, TASK_THREAD
-       l32i    \ad, \ad, THREAD_CURRENT_DS - TASK_THREAD
-#else
-       l32i    \ad, \ad, THREAD_CURRENT_DS
-#endif
-       .endm
-
-/*
- * set_fs sets current->thread.current_ds to some value.
- * On Entry:
- *     <at>    anything (temp register)
- *     <av>    value to write
- *     <sp>    stack
- * On Exit:
- *     <at>    destroyed (actually, current)
- *     <av>    preserved, value to write
- */
-       .macro  set_fs  at, av, sp
-       GET_CURRENT(\at,\sp)
-       s32i    \av, \at, THREAD_CURRENT_DS
-       .endm
-
-/*
- * kernel_ok determines whether we should bypass addr/size checking.
- * See the equivalent C-macro version below for clarity.
- * On success, kernel_ok branches to a label indicated by parameter
- * <success>.  This implies that the macro falls through to the next
- * insruction on an error.
- *
- * Note that while this macro can be used independently, we designed
- * in for optimal use in the access_ok macro below (i.e., we fall
- * through on error).
- *
- * On Entry:
- *     <at>            anything (temp register)
- *     <success>       label to branch to on success; implies
- *                     fall-through macro on error
- *     <sp>            stack pointer
- * On Exit:
- *     <at>            destroyed (actually, current->thread.current_ds)
- */
-
-#if ((KERNEL_DS != 0) || (USER_DS == 0))
-# error Assembly macro kernel_ok fails
-#endif
-       .macro  kernel_ok  at, sp, success
-       get_fs  \at, \sp
-       beqz    \at, \success
-       .endm
-
-/*
- * user_ok determines whether the access to user-space memory is allowed.
- * See the equivalent C-macro version below for clarity.
- *
- * On error, user_ok branches to a label indicated by parameter
- * <error>.  This implies that the macro falls through to the next
- * instruction on success.
- *
- * Note that while this macro can be used independently, we designed
- * in for optimal use in the access_ok macro below (i.e., we fall
- * through on success).
- *
- * On Entry:
- *     <aa>    register containing memory address
- *     <as>    register containing memory size
- *     <at>    temp register
- *     <error> label to branch to on error; implies fall-through
- *             macro on success
- * On Exit:
- *     <aa>    preserved
- *     <as>    preserved
- *     <at>    destroyed (actually, (TASK_SIZE + 1 - size))
- */
-       .macro  user_ok aa, as, at, error
-       movi    \at, __XTENSA_UL_CONST(TASK_SIZE)
-       bgeu    \as, \at, \error
-       sub     \at, \at, \as
-       bgeu    \aa, \at, \error
-       .endm
-
-/*
- * access_ok determines whether a memory access is allowed.  See the
- * equivalent C-macro version below for clarity.
- *
- * On error, access_ok branches to a label indicated by parameter
- * <error>.  This implies that the macro falls through to the next
- * instruction on success.
- *
- * Note that we assume success is the common case, and we optimize the
- * branch fall-through case on success.
- *
- * On Entry:
- *     <aa>    register containing memory address
- *     <as>    register containing memory size
- *     <at>    temp register
- *     <sp>
- *     <error> label to branch to on error; implies fall-through
- *             macro on success
- * On Exit:
- *     <aa>    preserved
- *     <as>    preserved
- *     <at>    destroyed
- */
-       .macro  access_ok  aa, as, at, sp, error
-       kernel_ok  \at, \sp, .Laccess_ok_\@
-       user_ok    \aa, \as, \at, \error
-.Laccess_ok_\@:
-       .endm
-
-#else /* __ASSEMBLY__ not defined */
-
 #include <linux/sched.h>
 
 /*
@@ -495,16 +354,4 @@ struct exception_table_entry
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup.unit otherwise.  */
-
-extern unsigned long search_exception_table(unsigned long addr);
-extern void sort_exception_table(void);
-
-/* Returns the new pc */
-#define fixup_exception(map_reg, fixup_unit, pc)                \
-({                                                              \
-       fixup_unit;                                             \
-})
-
-#endif /* __ASSEMBLY__ */
 #endif /* _XTENSA_UACCESS_H */
index a482df5..6911e38 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/processor.h>
 #include <asm/coprocessor.h>
 #include <asm/thread_info.h>
-#include <asm/uaccess.h>
+#include <asm/asm-uaccess.h>
 #include <asm/unistd.h>
 #include <asm/ptrace.h>
 #include <asm/current.h>
index fa04d9d..f5ef3cc 100644 (file)
@@ -17,7 +17,7 @@
 #include <asm/processor.h>
 #include <asm/coprocessor.h>
 #include <asm/thread_info.h>
-#include <asm/uaccess.h>
+#include <asm/asm-uaccess.h>
 #include <asm/unistd.h>
 #include <asm/ptrace.h>
 #include <asm/current.h>
index dd38e5c..b08ccbb 100644 (file)
@@ -1340,10 +1340,8 @@ int blkcg_policy_register(struct blkcg_policy *pol)
                        struct blkcg_policy_data *cpd;
 
                        cpd = pol->cpd_alloc_fn(GFP_KERNEL);
-                       if (!cpd) {
-                               mutex_unlock(&blkcg_pol_mutex);
+                       if (!cpd)
                                goto err_free_cpds;
-                       }
 
                        blkcg->cpd[pol->plid] = cpd;
                        cpd->blkcg = blkcg;
index 083e56f..46fe924 100644 (file)
@@ -31,6 +31,7 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
        unsigned int granularity;
        enum req_op op;
        int alignment;
+       sector_t bs_mask;
 
        if (!q)
                return -ENXIO;
@@ -50,6 +51,10 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
                op = REQ_OP_DISCARD;
        }
 
+       bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
+       if ((sector | nr_sects) & bs_mask)
+               return -EINVAL;
+
        /* Zero-sector (unknown) and one-sector granularities are the same.  */
        granularity = max(q->limits.discard_granularity >> 9, 1U);
        alignment = (bdev_discard_alignment(bdev) >> 9) % granularity;
@@ -150,10 +155,15 @@ int blkdev_issue_write_same(struct block_device *bdev, sector_t sector,
        unsigned int max_write_same_sectors;
        struct bio *bio = NULL;
        int ret = 0;
+       sector_t bs_mask;
 
        if (!q)
                return -ENXIO;
 
+       bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
+       if ((sector | nr_sects) & bs_mask)
+               return -EINVAL;
+
        /* Ensure that max_write_same_sectors doesn't overflow bi_size */
        max_write_same_sectors = UINT_MAX >> 9;
 
@@ -202,6 +212,11 @@ static int __blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
        int ret;
        struct bio *bio = NULL;
        unsigned int sz;
+       sector_t bs_mask;
+
+       bs_mask = (bdev_logical_block_size(bdev) >> 9) - 1;
+       if ((sector | nr_sects) & bs_mask)
+               return -EINVAL;
 
        while (nr_sects != 0) {
                bio = next_bio(bio, min(nr_sects, (sector_t)BIO_MAX_PAGES),
index ed2397f..755119c 100644 (file)
@@ -225,7 +225,8 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
                unsigned long arg)
 {
        uint64_t range[2];
-       uint64_t start, len;
+       struct address_space *mapping;
+       uint64_t start, end, len;
 
        if (!(mode & FMODE_WRITE))
                return -EBADF;
@@ -235,18 +236,23 @@ static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
 
        start = range[0];
        len = range[1];
+       end = start + len - 1;
 
        if (start & 511)
                return -EINVAL;
        if (len & 511)
                return -EINVAL;
-       start >>= 9;
-       len >>= 9;
-
-       if (start + len > (i_size_read(bdev->bd_inode) >> 9))
+       if (end >= (uint64_t)i_size_read(bdev->bd_inode))
+               return -EINVAL;
+       if (end < start)
                return -EINVAL;
 
-       return blkdev_issue_zeroout(bdev, start, len, GFP_KERNEL, false);
+       /* Invalidate the page cache, including dirty pages */
+       mapping = bdev->bd_inode->i_mapping;
+       truncate_inode_pages_range(mapping, start, end);
+
+       return blkdev_issue_zeroout(bdev, start >> 9, len >> 9, GFP_KERNEL,
+                                   false);
 }
 
 static int put_ushort(unsigned long arg, unsigned short val)
index bfb92ac..6989ba0 100644 (file)
@@ -47,7 +47,7 @@ static void crypto_pump_requests(struct crypto_engine *engine,
 
        /* If another context is idling then defer */
        if (engine->idling) {
-               queue_kthread_work(&engine->kworker, &engine->pump_requests);
+               kthread_queue_work(&engine->kworker, &engine->pump_requests);
                goto out;
        }
 
@@ -58,7 +58,7 @@ static void crypto_pump_requests(struct crypto_engine *engine,
 
                /* Only do teardown in the thread */
                if (!in_kthread) {
-                       queue_kthread_work(&engine->kworker,
+                       kthread_queue_work(&engine->kworker,
                                           &engine->pump_requests);
                        goto out;
                }
@@ -189,7 +189,7 @@ int crypto_transfer_cipher_request(struct crypto_engine *engine,
        ret = ablkcipher_enqueue_request(&engine->queue, req);
 
        if (!engine->busy && need_pump)
-               queue_kthread_work(&engine->kworker, &engine->pump_requests);
+               kthread_queue_work(&engine->kworker, &engine->pump_requests);
 
        spin_unlock_irqrestore(&engine->queue_lock, flags);
        return ret;
@@ -231,7 +231,7 @@ int crypto_transfer_hash_request(struct crypto_engine *engine,
        ret = ahash_enqueue_request(&engine->queue, req);
 
        if (!engine->busy && need_pump)
-               queue_kthread_work(&engine->kworker, &engine->pump_requests);
+               kthread_queue_work(&engine->kworker, &engine->pump_requests);
 
        spin_unlock_irqrestore(&engine->queue_lock, flags);
        return ret;
@@ -284,7 +284,7 @@ void crypto_finalize_cipher_request(struct crypto_engine *engine,
 
        req->base.complete(&req->base, err);
 
-       queue_kthread_work(&engine->kworker, &engine->pump_requests);
+       kthread_queue_work(&engine->kworker, &engine->pump_requests);
 }
 EXPORT_SYMBOL_GPL(crypto_finalize_cipher_request);
 
@@ -321,7 +321,7 @@ void crypto_finalize_hash_request(struct crypto_engine *engine,
 
        req->base.complete(&req->base, err);
 
-       queue_kthread_work(&engine->kworker, &engine->pump_requests);
+       kthread_queue_work(&engine->kworker, &engine->pump_requests);
 }
 EXPORT_SYMBOL_GPL(crypto_finalize_hash_request);
 
@@ -345,7 +345,7 @@ int crypto_engine_start(struct crypto_engine *engine)
        engine->running = true;
        spin_unlock_irqrestore(&engine->queue_lock, flags);
 
-       queue_kthread_work(&engine->kworker, &engine->pump_requests);
+       kthread_queue_work(&engine->kworker, &engine->pump_requests);
 
        return 0;
 }
@@ -422,7 +422,7 @@ struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
        crypto_init_queue(&engine->queue, CRYPTO_ENGINE_MAX_QLEN);
        spin_lock_init(&engine->queue_lock);
 
-       init_kthread_worker(&engine->kworker);
+       kthread_init_worker(&engine->kworker);
        engine->kworker_task = kthread_run(kthread_worker_fn,
                                           &engine->kworker, "%s",
                                           engine->name);
@@ -430,7 +430,7 @@ struct crypto_engine *crypto_engine_alloc_init(struct device *dev, bool rt)
                dev_err(dev, "failed to create crypto request pump task\n");
                return NULL;
        }
-       init_kthread_work(&engine->pump_requests, crypto_pump_work);
+       kthread_init_work(&engine->pump_requests, crypto_pump_work);
 
        if (engine->rt) {
                dev_info(dev, "will run requests pump with realtime priority\n");
@@ -455,7 +455,7 @@ int crypto_engine_exit(struct crypto_engine *engine)
        if (ret)
                return ret;
 
-       flush_kthread_worker(&engine->kworker);
+       kthread_flush_worker(&engine->kworker);
        kthread_stop(engine->kworker_task);
 
        return 0;
index 8ea8211..eb76a4c 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <linux/acpi.h>
 #include <asm/mwait.h>
+#include <xen/xen.h>
 
 #define ACPI_PROCESSOR_AGGREGATOR_CLASS        "acpi_pad"
 #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
@@ -477,6 +478,10 @@ static struct acpi_driver acpi_pad_driver = {
 
 static int __init acpi_pad_init(void)
 {
+       /* Xen ACPI PAD is used when running as Xen Dom0. */
+       if (xen_initial_domain())
+               return -ENODEV;
+
        power_saving_mwait_init();
        if (power_saving_mwait_eax == 0)
                return -EINVAL;
index 6805310..48e19d0 100644 (file)
@@ -526,6 +526,7 @@ static void acpi_ec_enable_event(struct acpi_ec *ec)
                acpi_ec_clear(ec);
 }
 
+#ifdef CONFIG_PM_SLEEP
 static bool acpi_ec_query_flushed(struct acpi_ec *ec)
 {
        bool flushed;
@@ -557,6 +558,7 @@ static void acpi_ec_disable_event(struct acpi_ec *ec)
        spin_unlock_irqrestore(&ec->lock, flags);
        __acpi_ec_flush_event(ec);
 }
+#endif /* CONFIG_PM_SLEEP */
 
 static bool acpi_ec_guard_event(struct acpi_ec *ec)
 {
index 384cfc3..6cf4988 100644 (file)
@@ -129,8 +129,18 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
 
        control = obj->package.elements[1].integer.value;
        for (i = 0; i < fan->fps_count; i++) {
-               if (control == fan->fps[i].control)
+               /*
+                * When Fine Grain Control is set, return the state
+                * corresponding to maximum fan->fps[i].control
+                * value compared to the current speed. Here the
+                * fan->fps[] is sorted array with increasing speed.
+                */
+               if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) {
+                       i = (i > 0) ? i - 1 : 0;
                        break;
+               } else if (control == fan->fps[i].control) {
+                       break;
+               }
        }
        if (i == fan->fps_count) {
                dev_dbg(&device->dev, "Invalid control value returned\n");
index 4305ee9..416953a 100644 (file)
@@ -162,11 +162,18 @@ void acpi_os_vprintf(const char *fmt, va_list args)
        if (acpi_in_debugger) {
                kdb_printf("%s", buffer);
        } else {
-               printk(KERN_CONT "%s", buffer);
+               if (printk_get_level(buffer))
+                       printk("%s", buffer);
+               else
+                       printk(KERN_CONT "%s", buffer);
        }
 #else
-       if (acpi_debugger_write_log(buffer) < 0)
-               printk(KERN_CONT "%s", buffer);
+       if (acpi_debugger_write_log(buffer) < 0) {
+               if (printk_get_level(buffer))
+                       printk("%s", buffer);
+               else
+                       printk(KERN_CONT "%s", buffer);
+       }
 #endif
 }
 
index f2fd3fe..03f5ec1 100644 (file)
@@ -468,10 +468,11 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
 }
 
 /**
- * acpi_data_get_property_reference - returns handle to the referenced object
- * @data: ACPI device data object containing the property
+ * __acpi_node_get_property_reference - returns handle to the referenced object
+ * @fwnode: Firmware node to get the property from
  * @propname: Name of the property
  * @index: Index of the reference to return
+ * @num_args: Maximum number of arguments after each reference
  * @args: Location to store the returned reference with optional arguments
  *
  * Find property with @name, verifify that it is a package containing at least
@@ -482,17 +483,40 @@ static int acpi_data_get_property_array(struct acpi_device_data *data,
  * If there's more than one reference in the property value package, @index is
  * used to select the one to return.
  *
+ * It is possible to leave holes in the property value set like in the
+ * example below:
+ *
+ * Package () {
+ *     "cs-gpios",
+ *     Package () {
+ *        ^GPIO, 19, 0, 0,
+ *        ^GPIO, 20, 0, 0,
+ *        0,
+ *        ^GPIO, 21, 0, 0,
+ *     }
+ * }
+ *
+ * Calling this function with index %2 return %-ENOENT and with index %3
+ * returns the last entry. If the property does not contain any more values
+ * %-ENODATA is returned. The NULL entry must be single integer and
+ * preferably contain value %0.
+ *
  * Return: %0 on success, negative error code on failure.
  */
-static int acpi_data_get_property_reference(struct acpi_device_data *data,
-                                           const char *propname, size_t index,
-                                           struct acpi_reference_args *args)
+int __acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+       const char *propname, size_t index, size_t num_args,
+       struct acpi_reference_args *args)
 {
        const union acpi_object *element, *end;
        const union acpi_object *obj;
+       struct acpi_device_data *data;
        struct acpi_device *device;
        int ret, idx = 0;
 
+       data = acpi_device_data_of_node(fwnode);
+       if (!data)
+               return -EINVAL;
+
        ret = acpi_data_get_property(data, propname, ACPI_TYPE_ANY, &obj);
        if (ret)
                return ret;
@@ -532,59 +556,54 @@ static int acpi_data_get_property_reference(struct acpi_device_data *data,
        while (element < end) {
                u32 nargs, i;
 
-               if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
-                       return -EPROTO;
-
-               ret = acpi_bus_get_device(element->reference.handle, &device);
-               if (ret)
-                       return -ENODEV;
-
-               element++;
-               nargs = 0;
-
-               /* assume following integer elements are all args */
-               for (i = 0; element + i < end; i++) {
-                       int type = element[i].type;
+               if (element->type == ACPI_TYPE_LOCAL_REFERENCE) {
+                       ret = acpi_bus_get_device(element->reference.handle,
+                                                 &device);
+                       if (ret)
+                               return -ENODEV;
+
+                       nargs = 0;
+                       element++;
+
+                       /* assume following integer elements are all args */
+                       for (i = 0; element + i < end && i < num_args; i++) {
+                               int type = element[i].type;
+
+                               if (type == ACPI_TYPE_INTEGER)
+                                       nargs++;
+                               else if (type == ACPI_TYPE_LOCAL_REFERENCE)
+                                       break;
+                               else
+                                       return -EPROTO;
+                       }
 
-                       if (type == ACPI_TYPE_INTEGER)
-                               nargs++;
-                       else if (type == ACPI_TYPE_LOCAL_REFERENCE)
-                               break;
-                       else
+                       if (nargs > MAX_ACPI_REFERENCE_ARGS)
                                return -EPROTO;
-               }
 
-               if (idx++ == index) {
-                       args->adev = device;
-                       args->nargs = nargs;
-                       for (i = 0; i < nargs; i++)
-                               args->args[i] = element[i].integer.value;
+                       if (idx == index) {
+                               args->adev = device;
+                               args->nargs = nargs;
+                               for (i = 0; i < nargs; i++)
+                                       args->args[i] = element[i].integer.value;
 
-                       return 0;
+                               return 0;
+                       }
+
+                       element += nargs;
+               } else if (element->type == ACPI_TYPE_INTEGER) {
+                       if (idx == index)
+                               return -ENOENT;
+                       element++;
+               } else {
+                       return -EPROTO;
                }
 
-               element += nargs;
+               idx++;
        }
 
-       return -EPROTO;
-}
-
-/**
- * acpi_node_get_property_reference - get a handle to the referenced object.
- * @fwnode: Firmware node to get the property from.
- * @propname: Name of the property.
- * @index: Index of the reference to return.
- * @args: Location to store the returned reference with optional arguments.
- */
-int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
-                                    const char *name, size_t index,
-                                    struct acpi_reference_args *args)
-{
-       struct acpi_device_data *data = acpi_device_data_of_node(fwnode);
-
-       return data ? acpi_data_get_property_reference(data, name, index, args) : -EINVAL;
+       return -ENODATA;
 }
-EXPORT_SYMBOL_GPL(acpi_node_get_property_reference);
+EXPORT_SYMBOL_GPL(__acpi_node_get_property_reference);
 
 static int acpi_data_prop_read_single(struct acpi_device_data *data,
                                      const char *propname,
index f4ebe39..35e8fbc 100644 (file)
@@ -520,7 +520,8 @@ static void acpi_thermal_check(void *data)
        if (!tz->tz_enabled)
                return;
 
-       thermal_zone_device_update(tz->thermal_zone);
+       thermal_zone_device_update(tz->thermal_zone,
+                                  THERMAL_EVENT_UNSPECIFIED);
 }
 
 /* sys I/F for generic thermal sysfs support */
index 90eabaf..ba5f11c 100644 (file)
@@ -1400,142 +1400,56 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance)
 }
 #endif
 
-/*
- * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer
- * to single msi.
- */
-static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports,
-                         struct ahci_host_priv *hpriv, unsigned long flags)
+static int ahci_get_irq_vector(struct ata_host *host, int port)
 {
-       int nvec, i, rc;
-
-       /* Do not init MSI-X if MSI is disabled for the device */
-       if (hpriv->flags & AHCI_HFLAG_NO_MSI)
-               return -ENODEV;
-
-       nvec = pci_msix_vec_count(pdev);
-       if (nvec < 0)
-               return nvec;
-
-       /*
-        * Proper MSI-X implementations will have a vector per-port.
-        * Barring that, we prefer single-MSI over single-MSIX.  If this
-        * check fails (not enough MSI-X vectors for all ports) we will
-        * be called again with the flag clear iff ahci_init_msi()
-        * fails.
-        */
-       if (flags & AHCI_HFLAG_MULTI_MSIX) {
-               if (nvec < n_ports)
-                       return -ENODEV;
-               nvec = n_ports;
-       } else if (nvec) {
-               nvec = 1;
-       } else {
-               /*
-                * Emit dev_err() since this was the non-legacy irq
-                * method of last resort.
-                */
-               rc = -ENODEV;
-               goto fail;
-       }
-
-       for (i = 0; i < nvec; i++)
-               hpriv->msix[i].entry = i;
-       rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec);
-       if (rc < 0)
-               goto fail;
-
-       if (nvec > 1)
-               hpriv->flags |= AHCI_HFLAG_MULTI_MSIX;
-       hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */
-
-       return nvec;
-fail:
-       dev_err(&pdev->dev,
-               "failed to enable MSI-X with error %d, # of vectors: %d\n",
-               rc, nvec);
-
-       return rc;
+       return pci_irq_vector(to_pci_dev(host->dev), port);
 }
 
 static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports,
                        struct ahci_host_priv *hpriv)
 {
-       int rc, nvec;
+       int nvec;
 
        if (hpriv->flags & AHCI_HFLAG_NO_MSI)
                return -ENODEV;
 
-       nvec = pci_msi_vec_count(pdev);
-       if (nvec < 0)
-               return nvec;
-
        /*
         * If number of MSIs is less than number of ports then Sharing Last
         * Message mode could be enforced. In this case assume that advantage
         * of multipe MSIs is negated and use single MSI mode instead.
         */
-       if (nvec < n_ports)
-               goto single_msi;
-
-       rc = pci_enable_msi_exact(pdev, nvec);
-       if (rc == -ENOSPC)
-               goto single_msi;
-       if (rc < 0)
-               return rc;
+       nvec = pci_alloc_irq_vectors(pdev, n_ports, INT_MAX,
+                       PCI_IRQ_MSIX | PCI_IRQ_MSI);
+       if (nvec > 0) {
+               if (!(readl(hpriv->mmio + HOST_CTL) & HOST_MRSM)) {
+                       hpriv->get_irq_vector = ahci_get_irq_vector;
+                       hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
+                       return nvec;
+               }
 
-       /* fallback to single MSI mode if the controller enforced MRSM mode */
-       if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) {
-               pci_disable_msi(pdev);
+               /*
+                * Fallback to single MSI mode if the controller enforced MRSM
+                * mode.
+                */
                printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n");
-               goto single_msi;
+               pci_free_irq_vectors(pdev);
        }
 
-       if (nvec > 1)
-               hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
-
-       goto out;
-
-single_msi:
-       nvec = 1;
-
-       rc = pci_enable_msi(pdev);
-       if (rc < 0)
-               return rc;
-out:
-       hpriv->irq = pdev->irq;
-
-       return nvec;
-}
-
-static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports,
-                               struct ahci_host_priv *hpriv)
-{
-       int nvec;
-
        /*
-        * Try to enable per-port MSI-X.  If the host is not capable
-        * fall back to single MSI before finally attempting single
-        * MSI-X.
+        * -ENOSPC indicated we don't have enough vectors.  Don't bother trying
+        * a single vectors for any other error:
         */
-       nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX);
-       if (nvec >= 0)
+       if (nvec < 0 && nvec != -ENOSPC)
                return nvec;
 
-       nvec = ahci_init_msi(pdev, n_ports, hpriv);
-       if (nvec >= 0)
-               return nvec;
-
-       /* try single-msix */
-       nvec = ahci_init_msix(pdev, n_ports, hpriv, 0);
-       if (nvec >= 0)
+       /*
+        * If the host is not capable of supporting per-port vectors, fall
+        * back to single MSI before finally attempting single MSI-X.
+        */
+       nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
+       if (nvec == 1)
                return nvec;
-
-       /* legacy intx interrupts */
-       pci_intx(pdev, 1);
-       hpriv->irq = pdev->irq;
-
-       return 0;
+       return pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
 }
 
 static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
@@ -1698,11 +1612,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (!host)
                return -ENOMEM;
        host->private_data = hpriv;
-       hpriv->msix = devm_kzalloc(&pdev->dev,
-                       sizeof(struct msix_entry) * n_ports, GFP_KERNEL);
-       if (!hpriv->msix)
-               return -ENOMEM;
-       ahci_init_interrupts(pdev, n_ports, hpriv);
+
+       if (ahci_init_msi(pdev, n_ports, hpriv) < 0) {
+               /* legacy intx interrupts */
+               pci_intx(pdev, 1);
+       }
+       hpriv->irq = pdev->irq;
 
        if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss)
                host->flags |= ATA_HOST_PARALLEL_SCAN;
index 70b06bc..0cc08f8 100644 (file)
@@ -242,12 +242,10 @@ enum {
        AHCI_HFLAG_NO_FBS               = (1 << 18), /* no FBS */
 
 #ifdef CONFIG_PCI_MSI
-       AHCI_HFLAG_MULTI_MSI            = (1 << 20), /* multiple PCI MSIs */
-       AHCI_HFLAG_MULTI_MSIX           = (1 << 21), /* per-port MSI-X */
+       AHCI_HFLAG_MULTI_MSI            = (1 << 20), /* per-port MSI(-X) */
 #else
        /* compile out MSI infrastructure */
        AHCI_HFLAG_MULTI_MSI            = 0,
-       AHCI_HFLAG_MULTI_MSIX           = 0,
 #endif
        AHCI_HFLAG_WAKE_BEFORE_STOP     = (1 << 22), /* wake before DMA stop */
 
@@ -351,7 +349,6 @@ struct ahci_host_priv {
         * the PHY position in this array.
         */
        struct phy              **phys;
-       struct msix_entry       *msix;          /* Optional MSI-X support */
        unsigned                nports;         /* Number of ports */
        void                    *plat_data;     /* Other platform data */
        unsigned int            irq;            /* interrupt line */
@@ -362,22 +359,11 @@ struct ahci_host_priv {
         */
        void                    (*start_engine)(struct ata_port *ap);
        irqreturn_t             (*irq_handler)(int irq, void *dev_instance);
-};
 
-#ifdef CONFIG_PCI_MSI
-static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
-{
-       if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX)
-               return hpriv->msix[port].vector;
-       else
-               return hpriv->irq + port;
-}
-#else
-static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port)
-{
-       return hpriv->irq;
-}
-#endif
+       /* only required for per-port MSI(-X) support */
+       int                     (*get_irq_vector)(struct ata_host *host,
+                                                 int port);
+};
 
 extern int ahci_ignore_sss;
 
index 7bdee9b..1eba8df 100644 (file)
 #define PORT_PHY3      0xB0
 #define PORT_PHY4      0xB4
 #define PORT_PHY5      0xB8
+#define PORT_AXICC     0xBC
 #define PORT_TRANS     0xC8
 
 /* port register default value */
 #define AHCI_PORT_PHY_1_CFG    0xa003fffe
 #define AHCI_PORT_TRANS_CFG    0x08000029
+#define AHCI_PORT_AXICC_CFG    0x3fffffff
 
 /* for ls1021a */
 #define LS1021A_PORT_PHY2      0x28183414
 #define LS1021A_PORT_PHY3      0x0e080e06
 #define LS1021A_PORT_PHY4      0x064a080b
 #define LS1021A_PORT_PHY5      0x2aa86470
+#define LS1021A_AXICC_ADDR     0xC0
 
 #define SATA_ECC_DISABLE       0x00020000
 
-/* for ls1043a */
-#define LS1043A_PORT_PHY2      0x28184d1f
-#define LS1043A_PORT_PHY3      0x0e081509
-
 enum ahci_qoriq_type {
        AHCI_LS1021A,
        AHCI_LS1043A,
@@ -137,7 +136,7 @@ static struct ata_port_operations ahci_qoriq_ops = {
        .hardreset      = ahci_qoriq_hardreset,
 };
 
-static struct ata_port_info ahci_qoriq_port_info = {
+static const struct ata_port_info ahci_qoriq_port_info = {
        .flags          = AHCI_FLAG_COMMON | ATA_FLAG_NCQ,
        .pio_mask       = ATA_PIO4,
        .udma_mask      = ATA_UDMA6,
@@ -162,18 +161,19 @@ static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv)
                writel(LS1021A_PORT_PHY4, reg_base + PORT_PHY4);
                writel(LS1021A_PORT_PHY5, reg_base + PORT_PHY5);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + LS1021A_AXICC_ADDR);
                break;
 
        case AHCI_LS1043A:
                writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
-               writel(LS1043A_PORT_PHY2, reg_base + PORT_PHY2);
-               writel(LS1043A_PORT_PHY3, reg_base + PORT_PHY3);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
                break;
 
        case AHCI_LS2080A:
                writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1);
                writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS);
+               writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC);
                break;
        }
 
@@ -221,12 +221,6 @@ static int ahci_qoriq_probe(struct platform_device *pdev)
        if (rc)
                goto disable_resources;
 
-       /* Workaround for ls2080a */
-       if (qoriq_priv->type == AHCI_LS2080A) {
-               hpriv->flags |= AHCI_HFLAG_NO_NCQ;
-               ahci_qoriq_port_info.flags &= ~ATA_FLAG_NCQ;
-       }
-
        rc = ahci_platform_init_host(pdev, hpriv, &ahci_qoriq_port_info,
                                     &ahci_qoriq_sht);
        if (rc)
index 8ff428f..bc345f2 100644 (file)
@@ -147,6 +147,7 @@ static struct scsi_host_template ahci_platform_sht = {
 
 static int st_ahci_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct st_ahci_drv_data *drv_data;
        struct ahci_host_priv *hpriv;
        int err;
@@ -170,6 +171,9 @@ static int st_ahci_probe(struct platform_device *pdev)
 
        st_ahci_configure_oob(hpriv->mmio);
 
+       of_property_read_u32(dev->of_node,
+                            "ports-implemented", &hpriv->force_port_map);
+
        err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info,
                                      &ahci_platform_sht);
        if (err) {
index dcf2c72..0d028ea 100644 (file)
@@ -2520,7 +2520,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host,
         */
        for (i = 0; i < host->n_ports; i++) {
                struct ahci_port_priv *pp = host->ports[i]->private_data;
-               int irq = ahci_irq_vector(hpriv, i);
+               int irq = hpriv->get_irq_vector(host, i);
 
                /* Do not receive interrupts sent by dummy ports */
                if (!pp) {
@@ -2556,10 +2556,15 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht)
        int irq = hpriv->irq;
        int rc;
 
-       if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) {
+       if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) {
                if (hpriv->irq_handler)
                        dev_warn(host->dev,
                                 "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n");
+               if (!hpriv->get_irq_vector) {
+                       dev_err(host->dev,
+                               "AHCI_HFLAG_MULTI_MSI requires ->get_irq_vector!\n");
+                       return -EIO;
+               }
 
                rc = ahci_host_activate_multi_irqs(host, sht);
        } else {
index e207b33..9cceb4a 100644 (file)
@@ -1159,8 +1159,6 @@ static void ata_scsi_sdev_config(struct scsi_device *sdev)
 {
        sdev->use_10_for_rw = 1;
        sdev->use_10_for_ms = 1;
-       sdev->no_report_opcodes = 1;
-       sdev->no_write_same = 1;
 
        /* Schedule policy is determined by ->qc_defer() callback and
         * it needs to see every deferred qc.  Set dev_blocked to 1 to
@@ -3282,18 +3280,125 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
        return 1;
 }
 
+/**
+ * ata_format_dsm_trim_descr() - SATL Write Same to DSM Trim
+ * @cmd: SCSI command being translated
+ * @trmax: Maximum number of entries that will fit in sector_size bytes.
+ * @sector: Starting sector
+ * @count: Total Range of request in logical sectors
+ *
+ * Rewrite the WRITE SAME descriptor to be a DSM TRIM little-endian formatted
+ * descriptor.
+ *
+ * Upto 64 entries of the format:
+ *   63:48 Range Length
+ *   47:0  LBA
+ *
+ *  Range Length of 0 is ignored.
+ *  LBA's should be sorted order and not overlap.
+ *
+ * NOTE: this is the same format as ADD LBA(S) TO NV CACHE PINNED SET
+ *
+ * Return: Number of bytes copied into sglist.
+ */
+static size_t ata_format_dsm_trim_descr(struct scsi_cmnd *cmd, u32 trmax,
+                                       u64 sector, u32 count)
+{
+       struct scsi_device *sdp = cmd->device;
+       size_t len = sdp->sector_size;
+       size_t r;
+       __le64 *buf;
+       u32 i = 0;
+       unsigned long flags;
+
+       WARN_ON(len > ATA_SCSI_RBUF_SIZE);
+
+       if (len > ATA_SCSI_RBUF_SIZE)
+               len = ATA_SCSI_RBUF_SIZE;
+
+       spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
+       buf = ((void *)ata_scsi_rbuf);
+       memset(buf, 0, len);
+       while (i < trmax) {
+               u64 entry = sector |
+                       ((u64)(count > 0xffff ? 0xffff : count) << 48);
+               buf[i++] = __cpu_to_le64(entry);
+               if (count <= 0xffff)
+                       break;
+               count -= 0xffff;
+               sector += 0xffff;
+       }
+       r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len);
+       spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
+
+       return r;
+}
+
+/**
+ * ata_format_dsm_trim_descr() - SATL Write Same to ATA SCT Write Same
+ * @cmd: SCSI command being translated
+ * @lba: Starting sector
+ * @num: Number of sectors to be zero'd.
+ *
+ * Rewrite the WRITE SAME payload to be an SCT Write Same formatted
+ * descriptor.
+ * NOTE: Writes a pattern (0's) in the foreground.
+ *
+ * Return: Number of bytes copied into sglist.
+ */
+static size_t ata_format_sct_write_same(struct scsi_cmnd *cmd, u64 lba, u64 num)
+{
+       struct scsi_device *sdp = cmd->device;
+       size_t len = sdp->sector_size;
+       size_t r;
+       u16 *buf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ata_scsi_rbuf_lock, flags);
+       buf = ((void *)ata_scsi_rbuf);
+
+       put_unaligned_le16(0x0002,  &buf[0]); /* SCT_ACT_WRITE_SAME */
+       put_unaligned_le16(0x0101,  &buf[1]); /* WRITE PTRN FG */
+       put_unaligned_le64(lba,     &buf[2]);
+       put_unaligned_le64(num,     &buf[6]);
+       put_unaligned_le32(0u,      &buf[10]); /* pattern */
+
+       WARN_ON(len > ATA_SCSI_RBUF_SIZE);
+
+       if (len > ATA_SCSI_RBUF_SIZE)
+               len = ATA_SCSI_RBUF_SIZE;
+
+       r = sg_copy_from_buffer(scsi_sglist(cmd), scsi_sg_count(cmd), buf, len);
+       spin_unlock_irqrestore(&ata_scsi_rbuf_lock, flags);
+
+       return r;
+}
+
+/**
+ * ata_scsi_write_same_xlat() - SATL Write Same to ATA SCT Write Same
+ * @qc: Command to be translated
+ *
+ * Translate a SCSI WRITE SAME command to be either a DSM TRIM command or
+ * an SCT Write Same command.
+ * Based on WRITE SAME has the UNMAP flag
+ *   When set translate to DSM TRIM
+ *   When clear translate to SCT Write Same
+ */
 static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
 {
        struct ata_taskfile *tf = &qc->tf;
        struct scsi_cmnd *scmd = qc->scsicmd;
+       struct scsi_device *sdp = scmd->device;
+       size_t len = sdp->sector_size;
        struct ata_device *dev = qc->dev;
        const u8 *cdb = scmd->cmnd;
        u64 block;
        u32 n_block;
+       const u32 trmax = len >> 3;
        u32 size;
-       void *buf;
        u16 fp;
        u8 bp = 0xff;
+       u8 unmap = cdb[1] & 0x8;
 
        /* we may not issue DMA commands if no DMA mode is set */
        if (unlikely(!dev->dma_mode))
@@ -3305,11 +3410,26 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
        }
        scsi_16_lba_len(cdb, &block, &n_block);
 
-       /* for now we only support WRITE SAME with the unmap bit set */
-       if (unlikely(!(cdb[1] & 0x8))) {
-               fp = 1;
-               bp = 3;
-               goto invalid_fld;
+       if (unmap) {
+               /* If trim is not enabled the cmd is invalid. */
+               if ((dev->horkage & ATA_HORKAGE_NOTRIM) ||
+                   !ata_id_has_trim(dev->id)) {
+                       fp = 1;
+                       bp = 3;
+                       goto invalid_fld;
+               }
+               /* If the request is too large the cmd is invalid */
+               if (n_block > 0xffff * trmax) {
+                       fp = 2;
+                       goto invalid_fld;
+               }
+       } else {
+               /* If write same is not available the cmd is invalid */
+               if (!ata_id_sct_write_same(dev->id)) {
+                       fp = 1;
+                       bp = 3;
+                       goto invalid_fld;
+               }
        }
 
        /*
@@ -3319,32 +3439,54 @@ static unsigned int ata_scsi_write_same_xlat(struct ata_queued_cmd *qc)
        if (!scsi_sg_count(scmd))
                goto invalid_param_len;
 
-       buf = page_address(sg_page(scsi_sglist(scmd)));
-
-       if (n_block <= 65535 * ATA_MAX_TRIM_RNUM) {
-               size = ata_set_lba_range_entries(buf, ATA_MAX_TRIM_RNUM, block, n_block);
-       } else {
-               fp = 2;
-               goto invalid_fld;
-       }
+       /*
+        * size must match sector size in bytes
+        * For DATA SET MANAGEMENT TRIM in ACS-2 nsect (aka count)
+        * is defined as number of 512 byte blocks to be transferred.
+        */
+       if (unmap) {
+               size = ata_format_dsm_trim_descr(scmd, trmax, block, n_block);
+               if (size != len)
+                       goto invalid_param_len;
 
-       if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
-               /* Newer devices support queued TRIM commands */
-               tf->protocol = ATA_PROT_NCQ;
-               tf->command = ATA_CMD_FPDMA_SEND;
-               tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
-               tf->nsect = qc->tag << 3;
-               tf->hob_feature = (size / 512) >> 8;
-               tf->feature = size / 512;
+               if (ata_ncq_enabled(dev) && ata_fpdma_dsm_supported(dev)) {
+                       /* Newer devices support queued TRIM commands */
+                       tf->protocol = ATA_PROT_NCQ;
+                       tf->command = ATA_CMD_FPDMA_SEND;
+                       tf->hob_nsect = ATA_SUBCMD_FPDMA_SEND_DSM & 0x1f;
+                       tf->nsect = qc->tag << 3;
+                       tf->hob_feature = (size / 512) >> 8;
+                       tf->feature = size / 512;
 
-               tf->auxiliary = 1;
+                       tf->auxiliary = 1;
+               } else {
+                       tf->protocol = ATA_PROT_DMA;
+                       tf->hob_feature = 0;
+                       tf->feature = ATA_DSM_TRIM;
+                       tf->hob_nsect = (size / 512) >> 8;
+                       tf->nsect = size / 512;
+                       tf->command = ATA_CMD_DSM;
+               }
        } else {
-               tf->protocol = ATA_PROT_DMA;
+               size = ata_format_sct_write_same(scmd, block, n_block);
+               if (size != len)
+                       goto invalid_param_len;
+
                tf->hob_feature = 0;
-               tf->feature = ATA_DSM_TRIM;
-               tf->hob_nsect = (size / 512) >> 8;
-               tf->nsect = size / 512;
-               tf->command = ATA_CMD_DSM;
+               tf->feature = 0;
+               tf->hob_nsect = 0;
+               tf->nsect = 1;
+               tf->lbah = 0;
+               tf->lbam = 0;
+               tf->lbal = ATA_CMD_STANDBYNOW1;
+               tf->hob_lbah = 0;
+               tf->hob_lbam = 0;
+               tf->hob_lbal = 0;
+               tf->device = ATA_CMD_STANDBYNOW1;
+               tf->protocol = ATA_PROT_DMA;
+               tf->command = ATA_CMD_WRITE_LOG_DMA_EXT;
+               if (unlikely(dev->flags & ATA_DFLAG_PIO))
+                       tf->command = ATA_CMD_WRITE_LOG_EXT;
        }
 
        tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 |
@@ -3367,6 +3509,76 @@ invalid_opcode:
        return 1;
 }
 
+/**
+ *     ata_scsiop_maint_in - Simulate a subset of MAINTENANCE_IN
+ *     @args: device MAINTENANCE_IN data / SCSI command of interest.
+ *     @rbuf: Response buffer, to which simulated SCSI cmd output is sent.
+ *
+ *     Yields a subset to satisfy scsi_report_opcode()
+ *
+ *     LOCKING:
+ *     spin_lock_irqsave(host lock)
+ */
+static unsigned int ata_scsiop_maint_in(struct ata_scsi_args *args, u8 *rbuf)
+{
+       struct ata_device *dev = args->dev;
+       u8 *cdb = args->cmd->cmnd;
+       u8 supported = 0;
+       unsigned int err = 0;
+
+       if (cdb[2] != 1) {
+               ata_dev_warn(dev, "invalid command format %d\n", cdb[2]);
+               err = 2;
+               goto out;
+       }
+       switch (cdb[3]) {
+       case INQUIRY:
+       case MODE_SENSE:
+       case MODE_SENSE_10:
+       case READ_CAPACITY:
+       case SERVICE_ACTION_IN_16:
+       case REPORT_LUNS:
+       case REQUEST_SENSE:
+       case SYNCHRONIZE_CACHE:
+       case REZERO_UNIT:
+       case SEEK_6:
+       case SEEK_10:
+       case TEST_UNIT_READY:
+       case SEND_DIAGNOSTIC:
+       case MAINTENANCE_IN:
+       case READ_6:
+       case READ_10:
+       case READ_16:
+       case WRITE_6:
+       case WRITE_10:
+       case WRITE_16:
+       case ATA_12:
+       case ATA_16:
+       case VERIFY:
+       case VERIFY_16:
+       case MODE_SELECT:
+       case MODE_SELECT_10:
+       case START_STOP:
+               supported = 3;
+               break;
+       case WRITE_SAME_16:
+               if (!ata_id_sct_write_same(dev->id))
+                       break;
+               /* fallthrough: if SCT ... only enable for ZBC */
+       case ZBC_IN:
+       case ZBC_OUT:
+               if (ata_id_zoned_cap(dev->id) ||
+                   dev->class == ATA_DEV_ZAC)
+                       supported = 3;
+               break;
+       default:
+               break;
+       }
+out:
+       rbuf[1] = supported; /* supported */
+       return err;
+}
+
 /**
  *     ata_scsi_report_zones_complete - convert ATA output
  *     @qc: command structure returning the data
@@ -3610,7 +3822,7 @@ static int ata_mselect_caching(struct ata_queued_cmd *qc,
 {
        struct ata_taskfile *tf = &qc->tf;
        struct ata_device *dev = qc->dev;
-       char mpage[CACHE_MPAGE_LEN];
+       u8 mpage[CACHE_MPAGE_LEN];
        u8 wce;
        int i;
 
@@ -3666,7 +3878,7 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
                               const u8 *buf, int len, u16 *fp)
 {
        struct ata_device *dev = qc->dev;
-       char mpage[CONTROL_MPAGE_LEN];
+       u8 mpage[CONTROL_MPAGE_LEN];
        u8 d_sense;
        int i;
 
@@ -3701,8 +3913,6 @@ static int ata_mselect_control(struct ata_queued_cmd *qc,
                dev->flags |= ATA_DFLAG_D_SENSE;
        else
                dev->flags &= ~ATA_DFLAG_D_SENSE;
-       qc->scsicmd->result = SAM_STAT_GOOD;
-       qc->scsicmd->scsi_done(qc->scsicmd);
        return 0;
 }
 
@@ -3829,6 +4039,8 @@ static unsigned int ata_scsi_mode_select_xlat(struct ata_queued_cmd *qc)
                if (ata_mselect_control(qc, p, pg_len, &fp) < 0) {
                        fp += hdr_len + bd_len;
                        goto invalid_param;
+               } else {
+                       goto skip; /* No ATA command to send */
                }
                break;
        default:                /* invalid page code */
@@ -4147,6 +4359,13 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd)
                        ata_scsi_invalid_field(dev, cmd, 1);
                break;
 
+       case MAINTENANCE_IN:
+               if (scsicmd[1] == MI_REPORT_SUPPORTED_OPERATION_CODES)
+                       ata_scsi_rbuf_fill(&args, ata_scsiop_maint_in);
+               else
+                       ata_scsi_invalid_field(dev, cmd, 1);
+               break;
+
        /* all other commands */
        default:
                ata_scsi_set_sense(dev, cmd, ILLEGAL_REQUEST, 0x20, 0x0);
@@ -4179,7 +4398,6 @@ int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht)
                shost->max_lun = 1;
                shost->max_channel = 1;
                shost->max_cmd_len = 16;
-               shost->no_write_same = 1;
 
                /* Schedule policy is determined by ->qc_defer()
                 * callback and it needs to see every deferred qc.
index 9f27b14..1611e0e 100644 (file)
@@ -347,10 +347,8 @@ static int at91sam9_smc_fields_init(struct device *dev)
 
        field.reg = AT91SAM9_SMC_MODE(AT91SAM9_SMC_GENERIC);
        fields.mode = devm_regmap_field_alloc(dev, smc, field);
-       if (IS_ERR(fields.mode))
-               return PTR_ERR(fields.mode);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(fields.mode);
 }
 
 static int pata_at91_probe(struct platform_device *pdev)
index 2724595..475a006 100644 (file)
@@ -152,8 +152,7 @@ static void octeon_cf_set_piomode(struct ata_port *ap, struct ata_device *dev)
                div = 8;
        T = (int)((1000000000000LL * div) / octeon_get_io_clock_rate());
 
-       if (ata_timing_compute(dev, dev->pio_mode, &timing, T, T))
-               BUG();
+       BUG_ON(ata_timing_compute(dev, dev->pio_mode, &timing, T, T));
 
        t1 = timing.setup;
        if (t1)
index 745489a..efc48bf 100644 (file)
@@ -1727,15 +1727,13 @@ static int mv_port_start(struct ata_port *ap)
                return -ENOMEM;
        ap->private_data = pp;
 
-       pp->crqb = dma_pool_alloc(hpriv->crqb_pool, GFP_KERNEL, &pp->crqb_dma);
+       pp->crqb = dma_pool_zalloc(hpriv->crqb_pool, GFP_KERNEL, &pp->crqb_dma);
        if (!pp->crqb)
                return -ENOMEM;
-       memset(pp->crqb, 0, MV_CRQB_Q_SZ);
 
-       pp->crpb = dma_pool_alloc(hpriv->crpb_pool, GFP_KERNEL, &pp->crpb_dma);
+       pp->crpb = dma_pool_zalloc(hpriv->crpb_pool, GFP_KERNEL, &pp->crpb_dma);
        if (!pp->crpb)
                goto out_port_free_dma_mem;
-       memset(pp->crpb, 0, MV_CRPB_Q_SZ);
 
        /* 6041/6081 Rev. "C0" (and newer) are okay with async notify */
        if (hpriv->hp_flags & MV_HP_ERRATA_60X1C0)
index cbdb3b1..fa1b7a9 100644 (file)
@@ -840,13 +840,13 @@ static void loop_config_discard(struct loop_device *lo)
 
 static void loop_unprepare_queue(struct loop_device *lo)
 {
-       flush_kthread_worker(&lo->worker);
+       kthread_flush_worker(&lo->worker);
        kthread_stop(lo->worker_task);
 }
 
 static int loop_prepare_queue(struct loop_device *lo)
 {
-       init_kthread_worker(&lo->worker);
+       kthread_init_worker(&lo->worker);
        lo->worker_task = kthread_run(kthread_worker_fn,
                        &lo->worker, "loop%d", lo->lo_number);
        if (IS_ERR(lo->worker_task))
@@ -1658,7 +1658,7 @@ static int loop_queue_rq(struct blk_mq_hw_ctx *hctx,
                break;
        }
 
-       queue_kthread_work(&lo->worker, &cmd->work);
+       kthread_queue_work(&lo->worker, &cmd->work);
 
        return BLK_MQ_RQ_QUEUE_OK;
 }
@@ -1696,7 +1696,7 @@ static int loop_init_request(void *data, struct request *rq,
        struct loop_cmd *cmd = blk_mq_rq_to_pdu(rq);
 
        cmd->rq = rq;
-       init_kthread_work(&cmd->work, loop_queue_work);
+       kthread_init_work(&cmd->work, loop_queue_work);
 
        return 0;
 }
index 4431129..0f7d28a 100644 (file)
@@ -845,6 +845,8 @@ void intel_gtt_insert_page(dma_addr_t addr,
                           unsigned int flags)
 {
        intel_private.driver->write_entry(addr, pg, flags);
+       if (intel_private.driver->chipset_flush)
+               intel_private.driver->chipset_flush();
 }
 EXPORT_SYMBOL(intel_gtt_insert_page);
 
index 3efb3bf..d131e15 100644 (file)
@@ -2100,23 +2100,37 @@ unsigned long get_random_long(void)
 }
 EXPORT_SYMBOL(get_random_long);
 
-/*
- * randomize_range() returns a start address such that
+/**
+ * randomize_page - Generate a random, page aligned address
+ * @start:     The smallest acceptable address the caller will take.
+ * @range:     The size of the area, starting at @start, within which the
+ *             random address must fall.
+ *
+ * If @start + @range would overflow, @range is capped.
  *
- *    [...... <range> .....]
- *  start                  end
+ * NOTE: Historical use of randomize_range, which this replaces, presumed that
+ * @start was already page aligned.  We now align it regardless.
  *
- * a <range> with size "len" starting at the return value is inside in the
- * area defined by [start, end], but is otherwise randomized.
+ * Return: A page aligned address within [start, start + range).  On error,
+ * @start is returned.
  */
 unsigned long
-randomize_range(unsigned long start, unsigned long end, unsigned long len)
+randomize_page(unsigned long start, unsigned long range)
 {
-       unsigned long range = end - len - start;
+       if (!PAGE_ALIGNED(start)) {
+               range -= PAGE_ALIGN(start) - start;
+               start = PAGE_ALIGN(start);
+       }
 
-       if (end <= start + len)
-               return 0;
-       return PAGE_ALIGN(get_random_int() % range + start);
+       if (start > ULONG_MAX - range)
+               range = ULONG_MAX - start;
+
+       range >>= PAGE_SHIFT;
+
+       if (range == 0)
+               return start;
+
+       return start + (get_random_long() % range << PAGE_SHIFT);
 }
 
 /* Interface for in-kernel drivers of true hardware RNGs.
index 480a777..7c19d9b 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/uaccess.h>
 
 #include <asm/io.h>
 #include <asm/reboot.h>
index 8114744..d433b1d 100644 (file)
@@ -38,7 +38,6 @@
 #include <linux/workqueue.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
-#include <linux/kconfig.h>
 #include "../tty/hvc/hvc_console.h"
 
 #define is_rproc_enabled IS_ENABLED(CONFIG_REMOTEPROC)
index 1b2f28f..4852d9e 100644 (file)
@@ -80,11 +80,17 @@ static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
 {
        struct cppc_cpudata *cpu;
        struct cpufreq_freqs freqs;
+       u32 desired_perf;
        int ret = 0;
 
        cpu = all_cpu_data[policy->cpu];
 
-       cpu->perf_ctrls.desired_perf = (u64)target_freq * policy->max / cppc_dmi_max_khz;
+       desired_perf = (u64)target_freq * cpu->perf_caps.highest_perf / cppc_dmi_max_khz;
+       /* Return if it is exactly the same perf */
+       if (desired_perf == cpu->perf_ctrls.desired_perf)
+               return ret;
+
+       cpu->perf_ctrls.desired_perf = desired_perf;
        freqs.old = policy->cur;
        freqs.new = target_freq;
 
index 18da4f8..1347589 100644 (file)
@@ -17,6 +17,7 @@
 struct cs_policy_dbs_info {
        struct policy_dbs_info policy_dbs;
        unsigned int down_skip;
+       unsigned int requested_freq;
 };
 
 static inline struct cs_policy_dbs_info *to_dbs_info(struct policy_dbs_info *policy_dbs)
@@ -61,6 +62,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
 {
        struct policy_dbs_info *policy_dbs = policy->governor_data;
        struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy_dbs);
+       unsigned int requested_freq = dbs_info->requested_freq;
        struct dbs_data *dbs_data = policy_dbs->dbs_data;
        struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
        unsigned int load = dbs_update(policy);
@@ -72,10 +74,16 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
        if (cs_tuners->freq_step == 0)
                goto out;
 
+       /*
+        * If requested_freq is out of range, it is likely that the limits
+        * changed in the meantime, so fall back to current frequency in that
+        * case.
+        */
+       if (requested_freq > policy->max || requested_freq < policy->min)
+               requested_freq = policy->cur;
+
        /* Check for frequency increase */
        if (load > dbs_data->up_threshold) {
-               unsigned int requested_freq = policy->cur;
-
                dbs_info->down_skip = 0;
 
                /* if we are already at full speed then break out early */
@@ -83,8 +91,11 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
                        goto out;
 
                requested_freq += get_freq_target(cs_tuners, policy);
+               if (requested_freq > policy->max)
+                       requested_freq = policy->max;
 
                __cpufreq_driver_target(policy, requested_freq, CPUFREQ_RELATION_H);
+               dbs_info->requested_freq = requested_freq;
                goto out;
        }
 
@@ -95,7 +106,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
 
        /* Check for frequency decrease */
        if (load < cs_tuners->down_threshold) {
-               unsigned int freq_target, requested_freq = policy->cur;
+               unsigned int freq_target;
                /*
                 * if we cannot reduce the frequency anymore, break out early
                 */
@@ -109,6 +120,7 @@ static unsigned int cs_dbs_timer(struct cpufreq_policy *policy)
                        requested_freq = policy->min;
 
                __cpufreq_driver_target(policy, requested_freq, CPUFREQ_RELATION_L);
+               dbs_info->requested_freq = requested_freq;
        }
 
  out:
@@ -287,6 +299,7 @@ static void cs_start(struct cpufreq_policy *policy)
        struct cs_policy_dbs_info *dbs_info = to_dbs_info(policy->governor_data);
 
        dbs_info->down_skip = 0;
+       dbs_info->requested_freq = policy->cur;
 }
 
 static struct dbs_governor cs_governor = {
index 806f203..f535f81 100644 (file)
@@ -225,7 +225,7 @@ struct cpudata {
 static struct cpudata **all_cpu_data;
 
 /**
- * struct pid_adjust_policy - Stores static PID configuration data
+ * struct pstate_adjust_policy - Stores static PID configuration data
  * @sample_rate_ms:    PID calculation sample rate in ms
  * @sample_rate_ns:    Sample rate calculation in ns
  * @deadband:          PID deadband
@@ -562,12 +562,12 @@ static void intel_pstate_hwp_set(const struct cpumask *cpumask)
        int min, hw_min, max, hw_max, cpu, range, adj_range;
        u64 value, cap;
 
-       rdmsrl(MSR_HWP_CAPABILITIES, cap);
-       hw_min = HWP_LOWEST_PERF(cap);
-       hw_max = HWP_HIGHEST_PERF(cap);
-       range = hw_max - hw_min;
-
        for_each_cpu(cpu, cpumask) {
+               rdmsrl_on_cpu(cpu, MSR_HWP_CAPABILITIES, &cap);
+               hw_min = HWP_LOWEST_PERF(cap);
+               hw_max = HWP_HIGHEST_PERF(cap);
+               range = hw_max - hw_min;
+
                rdmsrl_on_cpu(cpu, MSR_HWP_REQUEST, &value);
                adj_range = limits->min_perf_pct * range / 100;
                min = hw_min + adj_range;
@@ -1232,6 +1232,7 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
 {
        struct sample *sample = &cpu->sample;
        int32_t busy_frac, boost;
+       int target, avg_pstate;
 
        busy_frac = div_fp(sample->mperf, sample->tsc);
 
@@ -1242,7 +1243,26 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu)
                busy_frac = boost;
 
        sample->busy_scaled = busy_frac * 100;
-       return get_avg_pstate(cpu) - pid_calc(&cpu->pid, sample->busy_scaled);
+
+       target = limits->no_turbo || limits->turbo_disabled ?
+                       cpu->pstate.max_pstate : cpu->pstate.turbo_pstate;
+       target += target >> 2;
+       target = mul_fp(target, busy_frac);
+       if (target < cpu->pstate.min_pstate)
+               target = cpu->pstate.min_pstate;
+
+       /*
+        * If the average P-state during the previous cycle was higher than the
+        * current target, add 50% of the difference to the target to reduce
+        * possible performance oscillations and offset possible performance
+        * loss related to moving the workload from one CPU to another within
+        * a package/module.
+        */
+       avg_pstate = get_avg_pstate(cpu);
+       if (avg_pstate > target)
+               target += (avg_pstate - target) >> 1;
+
+       return target;
 }
 
 static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
@@ -1251,10 +1271,11 @@ static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu)
        u64 duration_ns;
 
        /*
-        * perf_scaled is the average performance during the last sampling
-        * period scaled by the ratio of the maximum P-state to the P-state
-        * requested last time (in percent).  That measures the system's
-        * response to the previous P-state selection.
+        * perf_scaled is the ratio of the average P-state during the last
+        * sampling period to the P-state requested last time (in percent).
+        *
+        * That measures the system's response to the previous P-state
+        * selection.
         */
        max_pstate = cpu->pstate.max_pstate_physical;
        current_pstate = cpu->pstate.current_pstate;
index 478006b..bf3ea76 100644 (file)
@@ -137,6 +137,10 @@ static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
 
        cur_time = jiffies;
 
+       /* Immediately exit if previous_freq is not initialized yet. */
+       if (!devfreq->previous_freq)
+               goto out;
+
        prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
        if (prev_lev < 0) {
                ret = prev_lev;
@@ -594,17 +598,19 @@ struct devfreq *devfreq_add_device(struct device *dev,
        if (devfreq->governor)
                err = devfreq->governor->event_handler(devfreq,
                                        DEVFREQ_GOV_START, NULL);
-       mutex_unlock(&devfreq_list_lock);
        if (err) {
                dev_err(dev, "%s: Unable to start governor for the device\n",
                        __func__);
                goto err_init;
        }
+       mutex_unlock(&devfreq_list_lock);
 
        return devfreq;
 
 err_init:
        list_del(&devfreq->node);
+       mutex_unlock(&devfreq_list_lock);
+
        device_unregister(&devfreq->dev);
 err_out:
        return ERR_PTR(err);
index 0fdae86..cd94980 100644 (file)
@@ -17,6 +17,7 @@ config DEVFREQ_EVENT_EXYNOS_NOCP
        tristate "EXYNOS NoC (Network On Chip) Probe DEVFREQ event Driver"
        depends on ARCH_EXYNOS || COMPILE_TEST
        select PM_OPP
+       select REGMAP_MMIO
        help
          This add the devfreq-event driver for Exynos SoC. It provides NoC
          (Network on Chip) Probe counters to measure the bandwidth of AXI bus.
index a584140..49e712a 100644 (file)
@@ -176,9 +176,6 @@ static int exynos_nocp_get_event(struct devfreq_event_dev *edev,
        return 0;
 
 out:
-       edata->load_count = 0;
-       edata->total_count = 0;
-
        dev_err(nocp->dev, "Failed to read the counter of NoC probe device\n");
 
        return ret;
index ddaee60..cf04d24 100644 (file)
@@ -586,6 +586,22 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach,
 }
 EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
 
+static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
+                                     enum dma_data_direction direction)
+{
+       bool write = (direction == DMA_BIDIRECTIONAL ||
+                     direction == DMA_TO_DEVICE);
+       struct reservation_object *resv = dmabuf->resv;
+       long ret;
+
+       /* Wait on any implicit rendering fences */
+       ret = reservation_object_wait_timeout_rcu(resv, write, true,
+                                                 MAX_SCHEDULE_TIMEOUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
 
 /**
  * dma_buf_begin_cpu_access - Must be called before accessing a dma_buf from the
@@ -608,6 +624,13 @@ int dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
        if (dmabuf->ops->begin_cpu_access)
                ret = dmabuf->ops->begin_cpu_access(dmabuf, direction);
 
+       /* Ensure that all fences are waited upon - but we first allow
+        * the native handler the chance to do so more efficiently if it
+        * chooses. A double invocation here will be reasonably cheap no-op.
+        */
+       if (ret == 0)
+               ret = __dma_buf_begin_cpu_access(dmabuf, direction);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
index a8731c8..f1989fc 100644 (file)
@@ -99,6 +99,7 @@ const struct fence_ops fence_array_ops = {
        .wait = fence_default_wait,
        .release = fence_array_release,
 };
+EXPORT_SYMBOL(fence_array_ops);
 
 /**
  * fence_array_create - Create a custom fence array
@@ -106,14 +107,14 @@ const struct fence_ops fence_array_ops = {
  * @fences:            [in]    array containing the fences
  * @context:           [in]    fence context to use
  * @seqno:             [in]    sequence number to use
- * @signal_on_any      [in]    signal on any fence in the array
+ * @signal_on_any:     [in]    signal on any fence in the array
  *
  * Allocate a fence_array object and initialize the base fence with fence_init().
  * In case of error it returns NULL.
  *
- * The caller should allocte the fences array with num_fences size
+ * The caller should allocate the fences array with num_fences size
  * and fill it with the fences it wants to add to the object. Ownership of this
- * array is take and fence_put() is used on each fence on release.
+ * array is taken and fence_put() is used on each fence on release.
  *
  * If @signal_on_any is true the fence array signals if any fence in the array
  * signals, otherwise it signals when all fences in the array signal.
index 9566a62..723d8af 100644 (file)
@@ -205,7 +205,7 @@ done:
  * @fence: the shared fence to add
  *
  * Add a fence to a shared slot, obj->lock must be held, and
- * reservation_object_reserve_shared_fence has been called.
+ * reservation_object_reserve_shared() has been called.
  */
 void reservation_object_add_shared_fence(struct reservation_object *obj,
                                         struct fence *fence)
index fab9520..2dd4c3d 100644 (file)
@@ -135,10 +135,16 @@ static void sync_print_sync_file(struct seq_file *s,
        int i;
 
        seq_printf(s, "[%p] %s: %s\n", sync_file, sync_file->name,
-                  sync_status_str(atomic_read(&sync_file->status)));
+                  sync_status_str(!fence_is_signaled(sync_file->fence)));
 
-       for (i = 0; i < sync_file->num_fences; ++i)
-               sync_print_fence(s, sync_file->cbs[i].fence, true);
+       if (fence_is_array(sync_file->fence)) {
+               struct fence_array *array = to_fence_array(sync_file->fence);
+
+               for (i = 0; i < array->num_fences; ++i)
+                       sync_print_fence(s, array->fences[i], true);
+       } else {
+               sync_print_fence(s, sync_file->fence, true);
+       }
 }
 
 static int sync_debugfs_show(struct seq_file *s, void *unused)
index 9aaa608..b29a9e8 100644 (file)
 
 static const struct file_operations sync_file_fops;
 
-static struct sync_file *sync_file_alloc(int size)
+static struct sync_file *sync_file_alloc(void)
 {
        struct sync_file *sync_file;
 
-       sync_file = kzalloc(size, GFP_KERNEL);
+       sync_file = kzalloc(sizeof(*sync_file), GFP_KERNEL);
        if (!sync_file)
                return NULL;
 
@@ -45,6 +45,8 @@ static struct sync_file *sync_file_alloc(int size)
 
        init_waitqueue_head(&sync_file->wq);
 
+       INIT_LIST_HEAD(&sync_file->cb.node);
+
        return sync_file;
 
 err:
@@ -54,14 +56,11 @@ err:
 
 static void fence_check_cb_func(struct fence *f, struct fence_cb *cb)
 {
-       struct sync_file_cb *check;
        struct sync_file *sync_file;
 
-       check = container_of(cb, struct sync_file_cb, cb);
-       sync_file = check->sync_file;
+       sync_file = container_of(cb, struct sync_file, cb);
 
-       if (atomic_dec_and_test(&sync_file->status))
-               wake_up_all(&sync_file->wq);
+       wake_up_all(&sync_file->wq);
 }
 
 /**
@@ -76,23 +75,17 @@ struct sync_file *sync_file_create(struct fence *fence)
 {
        struct sync_file *sync_file;
 
-       sync_file = sync_file_alloc(offsetof(struct sync_file, cbs[1]));
+       sync_file = sync_file_alloc();
        if (!sync_file)
                return NULL;
 
-       sync_file->num_fences = 1;
-       atomic_set(&sync_file->status, 1);
+       sync_file->fence = fence;
+
        snprintf(sync_file->name, sizeof(sync_file->name), "%s-%s%llu-%d",
                 fence->ops->get_driver_name(fence),
                 fence->ops->get_timeline_name(fence), fence->context,
                 fence->seqno);
 
-       sync_file->cbs[0].fence = fence;
-       sync_file->cbs[0].sync_file = sync_file;
-       if (fence_add_callback(fence, &sync_file->cbs[0].cb,
-                              fence_check_cb_func))
-               atomic_dec(&sync_file->status);
-
        return sync_file;
 }
 EXPORT_SYMBOL(sync_file_create);
@@ -121,14 +114,73 @@ err:
        return NULL;
 }
 
-static void sync_file_add_pt(struct sync_file *sync_file, int *i,
-                            struct fence *fence)
+/**
+ * sync_file_get_fence - get the fence related to the sync_file fd
+ * @fd:                sync_file fd to get the fence from
+ *
+ * Ensures @fd references a valid sync_file and returns a fence that
+ * represents all fence in the sync_file. On error NULL is returned.
+ */
+struct fence *sync_file_get_fence(int fd)
+{
+       struct sync_file *sync_file;
+       struct fence *fence;
+
+       sync_file = sync_file_fdget(fd);
+       if (!sync_file)
+               return NULL;
+
+       fence = fence_get(sync_file->fence);
+       fput(sync_file->file);
+
+       return fence;
+}
+EXPORT_SYMBOL(sync_file_get_fence);
+
+static int sync_file_set_fence(struct sync_file *sync_file,
+                              struct fence **fences, int num_fences)
+{
+       struct fence_array *array;
+
+       /*
+        * The reference for the fences in the new sync_file and held
+        * in add_fence() during the merge procedure, so for num_fences == 1
+        * we already own a new reference to the fence. For num_fence > 1
+        * we own the reference of the fence_array creation.
+        */
+       if (num_fences == 1) {
+               sync_file->fence = fences[0];
+               kfree(fences);
+       } else {
+               array = fence_array_create(num_fences, fences,
+                                          fence_context_alloc(1), 1, false);
+               if (!array)
+                       return -ENOMEM;
+
+               sync_file->fence = &array->base;
+       }
+
+       return 0;
+}
+
+static struct fence **get_fences(struct sync_file *sync_file, int *num_fences)
+{
+       if (fence_is_array(sync_file->fence)) {
+               struct fence_array *array = to_fence_array(sync_file->fence);
+
+               *num_fences = array->num_fences;
+               return array->fences;
+       }
+
+       *num_fences = 1;
+       return &sync_file->fence;
+}
+
+static void add_fence(struct fence **fences, int *i, struct fence *fence)
 {
-       sync_file->cbs[*i].fence = fence;
-       sync_file->cbs[*i].sync_file = sync_file;
+       fences[*i] = fence;
 
-       if (!fence_add_callback(fence, &sync_file->cbs[*i].cb,
-                               fence_check_cb_func)) {
+       if (!fence_is_signaled(fence)) {
                fence_get(fence);
                (*i)++;
        }
@@ -147,16 +199,24 @@ static void sync_file_add_pt(struct sync_file *sync_file, int *i,
 static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
                                         struct sync_file *b)
 {
-       int num_fences = a->num_fences + b->num_fences;
        struct sync_file *sync_file;
-       int i, i_a, i_b;
-       unsigned long size = offsetof(struct sync_file, cbs[num_fences]);
+       struct fence **fences, **nfences, **a_fences, **b_fences;
+       int i, i_a, i_b, num_fences, a_num_fences, b_num_fences;
 
-       sync_file = sync_file_alloc(size);
+       sync_file = sync_file_alloc();
        if (!sync_file)
                return NULL;
 
-       atomic_set(&sync_file->status, num_fences);
+       a_fences = get_fences(a, &a_num_fences);
+       b_fences = get_fences(b, &b_num_fences);
+       if (a_num_fences > INT_MAX - b_num_fences)
+               return NULL;
+
+       num_fences = a_num_fences + b_num_fences;
+
+       fences = kcalloc(num_fences, sizeof(*fences), GFP_KERNEL);
+       if (!fences)
+               goto err;
 
        /*
         * Assume sync_file a and b are both ordered and have no
@@ -165,55 +225,69 @@ static struct sync_file *sync_file_merge(const char *name, struct sync_file *a,
         * If a sync_file can only be created with sync_file_merge
         * and sync_file_create, this is a reasonable assumption.
         */
-       for (i = i_a = i_b = 0; i_a < a->num_fences && i_b < b->num_fences; ) {
-               struct fence *pt_a = a->cbs[i_a].fence;
-               struct fence *pt_b = b->cbs[i_b].fence;
+       for (i = i_a = i_b = 0; i_a < a_num_fences && i_b < b_num_fences; ) {
+               struct fence *pt_a = a_fences[i_a];
+               struct fence *pt_b = b_fences[i_b];
 
                if (pt_a->context < pt_b->context) {
-                       sync_file_add_pt(sync_file, &i, pt_a);
+                       add_fence(fences, &i, pt_a);
 
                        i_a++;
                } else if (pt_a->context > pt_b->context) {
-                       sync_file_add_pt(sync_file, &i, pt_b);
+                       add_fence(fences, &i, pt_b);
 
                        i_b++;
                } else {
                        if (pt_a->seqno - pt_b->seqno <= INT_MAX)
-                               sync_file_add_pt(sync_file, &i, pt_a);
+                               add_fence(fences, &i, pt_a);
                        else
-                               sync_file_add_pt(sync_file, &i, pt_b);
+                               add_fence(fences, &i, pt_b);
 
                        i_a++;
                        i_b++;
                }
        }
 
-       for (; i_a < a->num_fences; i_a++)
-               sync_file_add_pt(sync_file, &i, a->cbs[i_a].fence);
+       for (; i_a < a_num_fences; i_a++)
+               add_fence(fences, &i, a_fences[i_a]);
+
+       for (; i_b < b_num_fences; i_b++)
+               add_fence(fences, &i, b_fences[i_b]);
 
-       for (; i_b < b->num_fences; i_b++)
-               sync_file_add_pt(sync_file, &i, b->cbs[i_b].fence);
+       if (i == 0)
+               fences[i++] = fence_get(a_fences[0]);
 
-       if (num_fences > i)
-               atomic_sub(num_fences - i, &sync_file->status);
-       sync_file->num_fences = i;
+       if (num_fences > i) {
+               nfences = krealloc(fences, i * sizeof(*fences),
+                                 GFP_KERNEL);
+               if (!nfences)
+                       goto err;
+
+               fences = nfences;
+       }
+
+       if (sync_file_set_fence(sync_file, fences, i) < 0) {
+               kfree(fences);
+               goto err;
+       }
 
        strlcpy(sync_file->name, name, sizeof(sync_file->name));
        return sync_file;
+
+err:
+       fput(sync_file->file);
+       return NULL;
+
 }
 
 static void sync_file_free(struct kref *kref)
 {
        struct sync_file *sync_file = container_of(kref, struct sync_file,
                                                     kref);
-       int i;
-
-       for (i = 0; i < sync_file->num_fences; ++i) {
-               fence_remove_callback(sync_file->cbs[i].fence,
-                                     &sync_file->cbs[i].cb);
-               fence_put(sync_file->cbs[i].fence);
-       }
 
+       if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
+               fence_remove_callback(sync_file->fence, &sync_file->cb);
+       fence_put(sync_file->fence);
        kfree(sync_file);
 }
 
@@ -228,17 +302,17 @@ static int sync_file_release(struct inode *inode, struct file *file)
 static unsigned int sync_file_poll(struct file *file, poll_table *wait)
 {
        struct sync_file *sync_file = file->private_data;
-       int status;
 
        poll_wait(file, &sync_file->wq, wait);
 
-       status = atomic_read(&sync_file->status);
+       if (!poll_does_not_wait(wait) &&
+           !test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) {
+               if (fence_add_callback(sync_file->fence, &sync_file->cb,
+                                      fence_check_cb_func) < 0)
+                       wake_up_all(&sync_file->wq);
+       }
 
-       if (!status)
-               return POLLIN;
-       if (status < 0)
-               return POLLERR;
-       return 0;
+       return fence_is_signaled(sync_file->fence) ? POLLIN : 0;
 }
 
 static long sync_file_ioctl_merge(struct sync_file *sync_file,
@@ -315,8 +389,9 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
 {
        struct sync_file_info info;
        struct sync_fence_info *fence_info = NULL;
+       struct fence **fences;
        __u32 size;
-       int ret, i;
+       int num_fences, ret, i;
 
        if (copy_from_user(&info, (void __user *)arg, sizeof(info)))
                return -EFAULT;
@@ -324,6 +399,8 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
        if (info.flags || info.pad)
                return -EINVAL;
 
+       fences = get_fences(sync_file, &num_fences);
+
        /*
         * Passing num_fences = 0 means that userspace doesn't want to
         * retrieve any sync_fence_info. If num_fences = 0 we skip filling
@@ -333,16 +410,16 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
        if (!info.num_fences)
                goto no_fences;
 
-       if (info.num_fences < sync_file->num_fences)
+       if (info.num_fences < num_fences)
                return -EINVAL;
 
-       size = sync_file->num_fences * sizeof(*fence_info);
+       size = num_fences * sizeof(*fence_info);
        fence_info = kzalloc(size, GFP_KERNEL);
        if (!fence_info)
                return -ENOMEM;
 
-       for (i = 0; i < sync_file->num_fences; ++i)
-               sync_fill_fence_info(sync_file->cbs[i].fence, &fence_info[i]);
+       for (i = 0; i < num_fences; i++)
+               sync_fill_fence_info(fences[i], &fence_info[i]);
 
        if (copy_to_user(u64_to_user_ptr(info.sync_fence_info), fence_info,
                         size)) {
@@ -352,11 +429,8 @@ static long sync_file_ioctl_fence_info(struct sync_file *sync_file,
 
 no_fences:
        strlcpy(info.name, sync_file->name, sizeof(info.name));
-       info.status = atomic_read(&sync_file->status);
-       if (info.status >= 0)
-               info.status = !info.status;
-
-       info.num_fences = sync_file->num_fences;
+       info.status = fence_is_signaled(sync_file->fence);
+       info.num_fences = num_fences;
 
        if (copy_to_user((void __user *)arg, &info, sizeof(info)))
                ret = -EFAULT;
index 45c8817..e422568 100644 (file)
@@ -794,6 +794,22 @@ static int pca953x_probe(struct i2c_client *client,
        }
 
        mutex_init(&chip->i2c_lock);
+       /*
+        * In case we have an i2c-mux controlled by a GPIO provided by an
+        * expander using the same driver higher on the device tree, read the
+        * i2c adapter nesting depth and use the retrieved value as lockdep
+        * subclass for chip->i2c_lock.
+        *
+        * REVISIT: This solution is not complete. It protects us from lockdep
+        * false positives when the expander controlling the i2c-mux is on
+        * a different level on the device tree, but not when it's on the same
+        * level on a different branch (in which case the subclass number
+        * would be the same).
+        *
+        * TODO: Once a correct solution is developed, a similar fix should be
+        * applied to all other i2c-controlled GPIO expanders (and potentially
+        * regmap-i2c).
+        */
        lockdep_set_subclass(&chip->i2c_lock,
                             i2c_adapter_depth(client->adapter));
 
index fc35731..483059a 100644 (file)
@@ -108,33 +108,13 @@ config DRM_KMS_CMA_HELPER
 
 source "drivers/gpu/drm/i2c/Kconfig"
 
-config DRM_TDFX
-       tristate "3dfx Banshee/Voodoo3+"
-       depends on DRM && PCI
-       help
-         Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
-         graphics card.  If M is selected, the module will be called tdfx.
-
 source "drivers/gpu/drm/arm/Kconfig"
 
-config DRM_R128
-       tristate "ATI Rage 128"
-       depends on DRM && PCI
-       select FW_LOADER
-       help
-         Choose this option if you have an ATI Rage 128 graphics card.  If M
-         is selected, the module will be called r128.  AGP support for
-         this card is strongly suggested (unless you have a PCI version).
-
 config DRM_RADEON
        tristate "ATI Radeon"
        depends on DRM && PCI
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
        select FW_LOADER
         select DRM_KMS_HELPER
-       select DRM_KMS_FB_HELPER
         select DRM_TTM
        select POWER_SUPPLY
        select HWMON
@@ -153,12 +133,8 @@ source "drivers/gpu/drm/radeon/Kconfig"
 config DRM_AMDGPU
        tristate "AMD GPU"
        depends on DRM && PCI
-       select FB_CFB_FILLRECT
-       select FB_CFB_COPYAREA
-       select FB_CFB_IMAGEBLIT
        select FW_LOADER
         select DRM_KMS_HELPER
-       select DRM_KMS_FB_HELPER
         select DRM_TTM
        select POWER_SUPPLY
        select HWMON
@@ -171,55 +147,11 @@ config DRM_AMDGPU
          If M is selected, the module will be called amdgpu.
 
 source "drivers/gpu/drm/amd/amdgpu/Kconfig"
-source "drivers/gpu/drm/amd/powerplay/Kconfig"
-
-source "drivers/gpu/drm/amd/acp/Kconfig"
 
 source "drivers/gpu/drm/nouveau/Kconfig"
 
-config DRM_I810
-       tristate "Intel I810"
-       # !PREEMPT because of missing ioctl locking
-       depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN)
-       help
-         Choose this option if you have an Intel I810 graphics card.  If M is
-         selected, the module will be called i810.  AGP support is required
-         for this driver to work.
-
 source "drivers/gpu/drm/i915/Kconfig"
 
-config DRM_MGA
-       tristate "Matrox g200/g400"
-       depends on DRM && PCI
-       select FW_LOADER
-       help
-         Choose this option if you have a Matrox G200, G400 or G450 graphics
-         card.  If M is selected, the module will be called mga.  AGP
-         support is required for this driver to work.
-
-config DRM_SIS
-       tristate "SiS video cards"
-       depends on DRM && AGP
-       depends on FB_SIS || FB_SIS=n
-       help
-         Choose this option if you have a SiS 630 or compatible video
-          chipset. If M is selected the module will be called sis. AGP
-          support is required for this driver to work.
-
-config DRM_VIA
-       tristate "Via unichrome video cards"
-       depends on DRM && PCI
-       help
-         Choose this option if you have a Via unichrome or compatible video
-         chipset. If M is selected the module will be called via.
-
-config DRM_SAVAGE
-       tristate "Savage video cards"
-       depends on DRM && PCI
-       help
-         Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
-         chipset. If M is selected the module will be called savage.
-
 config DRM_VGEM
        tristate "Virtual GEM provider"
        depends on DRM
@@ -290,3 +222,81 @@ source "drivers/gpu/drm/arc/Kconfig"
 source "drivers/gpu/drm/hisilicon/Kconfig"
 
 source "drivers/gpu/drm/mediatek/Kconfig"
+
+# Keep legacy drivers last
+
+menuconfig DRM_LEGACY
+       bool "Enable legacy drivers (DANGEROUS)"
+       depends on DRM
+       help
+         Enable legacy DRI1 drivers. Those drivers expose unsafe and dangerous
+         APIs to user-space, which can be used to circumvent access
+         restrictions and other security measures. For backwards compatibility
+         those drivers are still available, but their use is highly
+         inadvisable and might harm your system.
+
+         You are recommended to use the safe modeset-only drivers instead, and
+         perform 3D emulation in user-space.
+
+         Unless you have strong reasons to go rogue, say "N".
+
+if DRM_LEGACY
+
+config DRM_TDFX
+       tristate "3dfx Banshee/Voodoo3+"
+       depends on DRM && PCI
+       help
+         Choose this option if you have a 3dfx Banshee or Voodoo3 (or later),
+         graphics card.  If M is selected, the module will be called tdfx.
+
+config DRM_R128
+       tristate "ATI Rage 128"
+       depends on DRM && PCI
+       select FW_LOADER
+       help
+         Choose this option if you have an ATI Rage 128 graphics card.  If M
+         is selected, the module will be called r128.  AGP support for
+         this card is strongly suggested (unless you have a PCI version).
+
+config DRM_I810
+       tristate "Intel I810"
+       # !PREEMPT because of missing ioctl locking
+       depends on DRM && AGP && AGP_INTEL && (!PREEMPT || BROKEN)
+       help
+         Choose this option if you have an Intel I810 graphics card.  If M is
+         selected, the module will be called i810.  AGP support is required
+         for this driver to work.
+
+config DRM_MGA
+       tristate "Matrox g200/g400"
+       depends on DRM && PCI
+       select FW_LOADER
+       help
+         Choose this option if you have a Matrox G200, G400 or G450 graphics
+         card.  If M is selected, the module will be called mga.  AGP
+         support is required for this driver to work.
+
+config DRM_SIS
+       tristate "SiS video cards"
+       depends on DRM && AGP
+       depends on FB_SIS || FB_SIS=n
+       help
+         Choose this option if you have a SiS 630 or compatible video
+         chipset. If M is selected the module will be called sis. AGP
+         support is required for this driver to work.
+
+config DRM_VIA
+       tristate "Via unichrome video cards"
+       depends on DRM && PCI
+       help
+         Choose this option if you have a Via unichrome or compatible video
+         chipset. If M is selected the module will be called via.
+
+config DRM_SAVAGE
+       tristate "Savage video cards"
+       depends on DRM && PCI
+       help
+         Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
+         chipset. If M is selected the module will be called savage.
+
+endif # DRM_LEGACY
index 0238bf8..25c7204 100644 (file)
@@ -12,7 +12,10 @@ drm-y       :=       drm_auth.o drm_bufs.o drm_cache.o \
                drm_info.o drm_debugfs.o drm_encoder_slave.o \
                drm_trace_points.o drm_global.o drm_prime.o \
                drm_rect.o drm_vma_manager.o drm_flip_work.o \
-               drm_modeset_lock.o drm_atomic.o drm_bridge.o
+               drm_modeset_lock.o drm_atomic.o drm_bridge.o \
+               drm_framebuffer.o drm_connector.o drm_blend.o \
+               drm_encoder.o drm_mode_object.o drm_property.o \
+               drm_plane.o drm_color_mgmt.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
 drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
@@ -24,7 +27,7 @@ drm-$(CONFIG_AGP) += drm_agpsupport.o
 drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
                drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
                drm_kms_helper_common.o drm_dp_dual_mode_helper.o \
-               drm_simple_kms_helper.o drm_blend.o
+               drm_simple_kms_helper.o drm_modeset_helper.o
 
 drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
 drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
@@ -46,7 +49,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/
 obj-$(CONFIG_DRM_AMDGPU)+= amd/amdgpu/
 obj-$(CONFIG_DRM_MGA)  += mga/
 obj-$(CONFIG_DRM_I810) += i810/
-obj-$(CONFIG_DRM_I915)  += i915/
+obj-$(CONFIG_DRM_I915) += i915/
 obj-$(CONFIG_DRM_MGAG200) += mgag200/
 obj-$(CONFIG_DRM_VC4)  += vc4/
 obj-$(CONFIG_DRM_CIRRUS_QEMU) += cirrus/
index 7335c04..61360e2 100644 (file)
@@ -1,3 +1,10 @@
+config DRM_AMDGPU_SI
+       bool "Enable amdgpu support for SI parts"
+       depends on DRM_AMDGPU
+       help
+         Choose this option if you want to enable experimental support
+         for SI asics.
+
 config DRM_AMDGPU_CIK
        bool "Enable amdgpu support for CIK parts"
        depends on DRM_AMDGPU
@@ -25,3 +32,4 @@ config DRM_AMDGPU_GART_DEBUGFS
          Selecting this option creates a debugfs file to inspect the mapped
          pages. Uses more memory for housekeeping, enable only for debugging.
 
+source "drivers/gpu/drm/amd/acp/Kconfig"
index c7fcdce..248a05d 100644 (file)
@@ -23,13 +23,16 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
        amdgpu_pm.o atombios_dp.o amdgpu_afmt.o amdgpu_trace_points.o \
        atombios_encoders.o amdgpu_sa.o atombios_i2c.o \
        amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \
-       amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o
+       amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o \
+       amdgpu_gtt_mgr.o
 
 # add asic specific block
 amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \
        ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o \
        amdgpu_amdkfd_gfx_v7.o
 
+amdgpu-$(CONFIG_DRM_AMDGPU_SI)+= si.o gmc_v6_0.o gfx_v6_0.o si_ih.o si_dma.o dce_v6_0.o si_dpm.o si_smc.o
+
 amdgpu-y += \
        vi.o
 
@@ -50,15 +53,13 @@ amdgpu-y += \
 amdgpu-y += \
        amdgpu_dpm.o \
        amdgpu_powerplay.o \
-       cz_smc.o cz_dpm.o \
-       tonga_smc.o tonga_dpm.o \
-       fiji_smc.o fiji_dpm.o \
-       iceland_smc.o iceland_dpm.o
+       cz_smc.o cz_dpm.o
 
 # add DCE block
 amdgpu-y += \
        dce_v10_0.o \
-       dce_v11_0.o
+       dce_v11_0.o \
+       dce_virtual.o
 
 # add GFX block
 amdgpu-y += \
@@ -110,14 +111,10 @@ amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o
 amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o
 amdgpu-$(CONFIG_MMU_NOTIFIER) += amdgpu_mn.o
 
-ifneq ($(CONFIG_DRM_AMD_POWERPLAY),)
-
 include $(FULL_AMD_PATH)/powerplay/Makefile
 
 amdgpu-y += $(AMD_POWERPLAY_FILES)
 
-endif
-
 obj-$(CONFIG_DRM_AMDGPU)+= amdgpu.o
 
 CFLAGS_amdgpu_trace_points.o := -I$(src)
index 0619269..b8d6667 100644 (file)
@@ -90,6 +90,7 @@
 #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_VIRTUAL                 0x28
 
 #define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO    0xFF
 
 #define CONNECTOR_OBJECT_ID_eDP                   0x14
 #define CONNECTOR_OBJECT_ID_MXM                   0x15
 #define CONNECTOR_OBJECT_ID_LVDS_eDP              0x16
+#define CONNECTOR_OBJECT_ID_VIRTUAL               0x17
 
 /* deleted */
 
 #define GRAPH_OBJECT_ENUM_ID5                     0x05
 #define GRAPH_OBJECT_ENUM_ID6                     0x06
 #define GRAPH_OBJECT_ENUM_ID7                     0x07
+#define GRAPH_OBJECT_ENUM_VIRTUAL                 0x08
 
 /****************************************************/
 /* Graphics Object ID Bit definition                */
                                                   GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
                                                   ENCODER_OBJECT_ID_HDMI_ANX9805 << OBJECT_ID_SHIFT)
 
+#define ENCODER_VIRTUAL_ENUM_VIRTUAL            ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+                                                  GRAPH_OBJECT_ENUM_VIRTUAL << ENUM_ID_SHIFT |\
+                                                  ENCODER_OBJECT_ID_VIRTUAL << OBJECT_ID_SHIFT)
+
 /****************************************************/
 /* Connector Object ID definition - Shared with BIOS */
 /****************************************************/
index 700c56b..039b57e 100644 (file)
 #include "amdgpu_ih.h"
 #include "amdgpu_irq.h"
 #include "amdgpu_ucode.h"
+#include "amdgpu_ttm.h"
 #include "amdgpu_gds.h"
 #include "amd_powerplay.h"
 #include "amdgpu_acp.h"
 
 #include "gpu_scheduler.h"
+#include "amdgpu_virt.h"
 
 /*
  * Modules parameters.
@@ -63,6 +65,7 @@
 extern int amdgpu_modeset;
 extern int amdgpu_vram_limit;
 extern int amdgpu_gart_size;
+extern int amdgpu_moverate;
 extern int amdgpu_benchmarking;
 extern int amdgpu_testing;
 extern int amdgpu_audio;
@@ -91,6 +94,9 @@ extern unsigned amdgpu_pcie_lane_cap;
 extern unsigned amdgpu_cg_mask;
 extern unsigned amdgpu_pg_mask;
 extern char *amdgpu_disable_cu;
+extern int amdgpu_sclk_deep_sleep_en;
+extern char *amdgpu_virtual_display;
+extern unsigned amdgpu_pp_feature_mask;
 
 #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS         3000
 #define AMDGPU_MAX_USEC_TIMEOUT                        100000  /* 100 ms */
@@ -105,7 +111,7 @@ extern char *amdgpu_disable_cu;
 #define AMDGPU_MAX_RINGS                       16
 #define AMDGPU_MAX_GFX_RINGS                   1
 #define AMDGPU_MAX_COMPUTE_RINGS               8
-#define AMDGPU_MAX_VCE_RINGS                   2
+#define AMDGPU_MAX_VCE_RINGS                   3
 
 /* max number of IP instances */
 #define AMDGPU_MAX_SDMA_INSTANCES              2
@@ -248,10 +254,9 @@ struct amdgpu_vm_pte_funcs {
                         uint64_t pe, uint64_t src,
                         unsigned count);
        /* write pte one entry at a time with addr mapping */
-       void (*write_pte)(struct amdgpu_ib *ib,
-                         const dma_addr_t *pages_addr, uint64_t pe,
-                         uint64_t addr, unsigned count,
-                         uint32_t incr, uint32_t flags);
+       void (*write_pte)(struct amdgpu_ib *ib, uint64_t pe,
+                         uint64_t value, unsigned count,
+                         uint32_t incr);
        /* for linear pte/pde updates without addr mapping */
        void (*set_pte_pde)(struct amdgpu_ib *ib,
                            uint64_t pe,
@@ -316,6 +321,10 @@ struct amdgpu_ring_funcs {
        /* note usage for clock and power gating */
        void (*begin_use)(struct amdgpu_ring *ring);
        void (*end_use)(struct amdgpu_ring *ring);
+       void (*emit_switch_buffer) (struct amdgpu_ring *ring);
+       void (*emit_cntxcntl) (struct amdgpu_ring *ring, uint32_t flags);
+       unsigned (*get_emit_ib_size) (struct amdgpu_ring *ring);
+       unsigned (*get_dma_frame_size) (struct amdgpu_ring *ring);
 };
 
 /*
@@ -396,48 +405,8 @@ int amdgpu_fence_wait_empty(struct amdgpu_ring *ring);
 unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring);
 
 /*
- * TTM.
+ * BO.
  */
-
-#define AMDGPU_TTM_LRU_SIZE    20
-
-struct amdgpu_mman_lru {
-       struct list_head                *lru[TTM_NUM_MEM_TYPES];
-       struct list_head                *swap_lru;
-};
-
-struct amdgpu_mman {
-       struct ttm_bo_global_ref        bo_global_ref;
-       struct drm_global_reference     mem_global_ref;
-       struct ttm_bo_device            bdev;
-       bool                            mem_global_referenced;
-       bool                            initialized;
-
-#if defined(CONFIG_DEBUG_FS)
-       struct dentry                   *vram;
-       struct dentry                   *gtt;
-#endif
-
-       /* buffer handling */
-       const struct amdgpu_buffer_funcs        *buffer_funcs;
-       struct amdgpu_ring                      *buffer_funcs_ring;
-       /* Scheduler entity for buffer moves */
-       struct amd_sched_entity                 entity;
-
-       /* custom LRU management */
-       struct amdgpu_mman_lru                  log2_size[AMDGPU_TTM_LRU_SIZE];
-       /* guard for log2_size array, don't add anything in between */
-       struct amdgpu_mman_lru                  guard;
-};
-
-int amdgpu_copy_buffer(struct amdgpu_ring *ring,
-                      uint64_t src_offset,
-                      uint64_t dst_offset,
-                      uint32_t byte_count,
-                      struct reservation_object *resv,
-                      struct fence **fence);
-int amdgpu_mmap(struct file *filp, struct vm_area_struct *vma);
-
 struct amdgpu_bo_list_entry {
        struct amdgpu_bo                *robj;
        struct ttm_validate_buffer      tv;
@@ -476,8 +445,6 @@ struct amdgpu_bo_va {
 #define AMDGPU_GEM_DOMAIN_MAX          0x3
 
 struct amdgpu_bo {
-       /* Protected by gem.mutex */
-       struct list_head                list;
        /* Protected by tbo.reserved */
        u32                             prefered_domains;
        u32                             allowed_domains;
@@ -500,10 +467,12 @@ struct amdgpu_bo {
        struct amdgpu_device            *adev;
        struct drm_gem_object           gem_base;
        struct amdgpu_bo                *parent;
+       struct amdgpu_bo                *shadow;
 
        struct ttm_bo_kmap_obj          dma_buf_vmap;
        struct amdgpu_mn                *mn;
        struct list_head                mn_list;
+       struct list_head                shadow_list;
 };
 #define gem_to_amdgpu_bo(gobj) container_of((gobj), struct amdgpu_bo, gem_base)
 
@@ -653,6 +622,7 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
 int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
                     int pages, struct page **pagelist,
                     dma_addr_t *dma_addr, uint32_t flags);
+int amdgpu_ttm_recover_gart(struct amdgpu_device *adev);
 
 /*
  * GPU MC structures, functions & helpers
@@ -679,6 +649,8 @@ struct amdgpu_mc {
        uint32_t                fw_version;
        struct amdgpu_irq_src   vm_fault;
        uint32_t                vram_type;
+       uint32_t                srbm_soft_reset;
+       struct amdgpu_mode_mc_save save;
 };
 
 /*
@@ -723,13 +695,14 @@ void amdgpu_doorbell_get_kfd_info(struct amdgpu_device *adev,
  */
 
 struct amdgpu_flip_work {
-       struct work_struct              flip_work;
+       struct delayed_work             flip_work;
        struct work_struct              unpin_work;
        struct amdgpu_device            *adev;
        int                             crtc_id;
+       u32                             target_vblank;
        uint64_t                        base;
        struct drm_pending_vblank_event *event;
-       struct amdgpu_bo                *old_rbo;
+       struct amdgpu_bo                *old_abo;
        struct fence                    *excl;
        unsigned                        shared_count;
        struct fence                    **shared;
@@ -817,13 +790,17 @@ struct amdgpu_ring {
 /* maximum number of VMIDs */
 #define AMDGPU_NUM_VM  16
 
+/* Maximum number of PTEs the hardware can write with one command */
+#define AMDGPU_VM_MAX_UPDATE_SIZE      0x3FFFF
+
 /* number of entries in page table */
 #define AMDGPU_VM_PTE_COUNT (1 << amdgpu_vm_block_size)
 
 /* PTBs (Page Table Blocks) need to be aligned to 32K */
 #define AMDGPU_VM_PTB_ALIGN_SIZE   32768
-#define AMDGPU_VM_PTB_ALIGN_MASK (AMDGPU_VM_PTB_ALIGN_SIZE - 1)
-#define AMDGPU_VM_PTB_ALIGN(a) (((a) + AMDGPU_VM_PTB_ALIGN_MASK) & ~AMDGPU_VM_PTB_ALIGN_MASK)
+
+/* LOG2 number of continuous pages for the fragment field */
+#define AMDGPU_LOG2_PAGES_PER_FRAG 4
 
 #define AMDGPU_PTE_VALID       (1 << 0)
 #define AMDGPU_PTE_SYSTEM      (1 << 1)
@@ -835,10 +812,7 @@ struct amdgpu_ring {
 #define AMDGPU_PTE_READABLE    (1 << 5)
 #define AMDGPU_PTE_WRITEABLE   (1 << 6)
 
-/* PTE (Page Table Entry) fragment field for different page sizes */
-#define AMDGPU_PTE_FRAG_4KB    (0 << 7)
-#define AMDGPU_PTE_FRAG_64KB   (4 << 7)
-#define AMDGPU_LOG2_PAGES_PER_FRAG 4
+#define AMDGPU_PTE_FRAG(x)     ((x & 0x1f) << 7)
 
 /* How to programm VM fault handling */
 #define AMDGPU_VM_FAULT_STOP_NEVER     0
@@ -848,6 +822,7 @@ struct amdgpu_ring {
 struct amdgpu_vm_pt {
        struct amdgpu_bo_list_entry     entry;
        uint64_t                        addr;
+       uint64_t                        shadow_addr;
 };
 
 struct amdgpu_vm {
@@ -950,7 +925,6 @@ int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
                      struct amdgpu_job *job);
 int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job);
 void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vm_id);
-uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr);
 int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
                                    struct amdgpu_vm *vm);
 int amdgpu_vm_clear_freed(struct amdgpu_device *adev,
@@ -959,7 +933,7 @@ int amdgpu_vm_clear_invalids(struct amdgpu_device *adev, struct amdgpu_vm *vm,
                             struct amdgpu_sync *sync);
 int amdgpu_vm_bo_update(struct amdgpu_device *adev,
                        struct amdgpu_bo_va *bo_va,
-                       struct ttm_mem_reg *mem);
+                       bool clear);
 void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
                             struct amdgpu_bo *bo);
 struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm,
@@ -994,6 +968,7 @@ struct amdgpu_ctx {
        spinlock_t              ring_lock;
        struct fence            **fences;
        struct amdgpu_ctx_ring  rings[AMDGPU_MAX_RINGS];
+       bool preamble_presented;
 };
 
 struct amdgpu_ctx_mgr {
@@ -1197,6 +1172,10 @@ struct amdgpu_gfx {
        unsigned                        ce_ram_size;
        struct amdgpu_cu_info           cu_info;
        const struct amdgpu_gfx_funcs   *funcs;
+
+       /* reset mask */
+       uint32_t                        grbm_soft_reset;
+       uint32_t                        srbm_soft_reset;
 };
 
 int amdgpu_ib_get(struct amdgpu_device *adev, struct amdgpu_vm *vm,
@@ -1249,11 +1228,16 @@ struct amdgpu_cs_parser {
        struct fence                    *fence;
        uint64_t                        bytes_moved_threshold;
        uint64_t                        bytes_moved;
+       struct amdgpu_bo_list_entry     *evictable;
 
        /* user fence */
        struct amdgpu_bo_list_entry     uf_entry;
 };
 
+#define AMDGPU_PREAMBLE_IB_PRESENT          (1 << 0) /* bit set means command submit involves a preamble IB */
+#define AMDGPU_PREAMBLE_IB_PRESENT_FIRST    (1 << 1) /* bit set means preamble IB is first presented in belonging context */
+#define AMDGPU_HAVE_CTX_SWITCH              (1 << 2) /* bit set means context switch occured */
+
 struct amdgpu_job {
        struct amd_sched_job    base;
        struct amdgpu_device    *adev;
@@ -1262,9 +1246,10 @@ struct amdgpu_job {
        struct amdgpu_sync      sync;
        struct amdgpu_ib        *ibs;
        struct fence            *fence; /* the hw fence */
+       uint32_t                preamble_status;
        uint32_t                num_ibs;
        void                    *owner;
-       uint64_t                ctx;
+       uint64_t                fence_ctx; /* the fence_context this job uses */
        bool                    vm_needs_flush;
        unsigned                vm_id;
        uint64_t                vm_pd_addr;
@@ -1685,6 +1670,7 @@ struct amdgpu_uvd {
        bool                    address_64_bit;
        bool                    use_ctx_buf;
        struct amd_sched_entity entity;
+       uint32_t                srbm_soft_reset;
 };
 
 /*
@@ -1711,6 +1697,8 @@ struct amdgpu_vce {
        struct amdgpu_irq_src   irq;
        unsigned                harvest_config;
        struct amd_sched_entity entity;
+       uint32_t                srbm_soft_reset;
+       unsigned                num_rings;
 };
 
 /*
@@ -1728,9 +1716,14 @@ struct amdgpu_sdma_instance {
 
 struct amdgpu_sdma {
        struct amdgpu_sdma_instance instance[AMDGPU_MAX_SDMA_INSTANCES];
+#ifdef CONFIG_DRM_AMDGPU_SI
+       //SI DMA has a difference trap irq number for the second engine
+       struct amdgpu_irq_src   trap_irq_1;
+#endif
        struct amdgpu_irq_src   trap_irq;
        struct amdgpu_irq_src   illegal_inst_irq;
        int                     num_instances;
+       uint32_t                    srbm_soft_reset;
 };
 
 /*
@@ -1832,6 +1825,7 @@ struct amdgpu_asic_funcs {
        bool (*read_disabled_bios)(struct amdgpu_device *adev);
        bool (*read_bios_from_rom)(struct amdgpu_device *adev,
                                   u8 *bios, u32 length_bytes);
+       void (*detect_hw_virtualization) (struct amdgpu_device *adev);
        int (*read_register)(struct amdgpu_device *adev, u32 se_num,
                             u32 sh_num, u32 reg_offset, u32 *value);
        void (*set_vga_state)(struct amdgpu_device *adev, bool state);
@@ -1841,8 +1835,9 @@ struct amdgpu_asic_funcs {
        /* MM block clocks */
        int (*set_uvd_clocks)(struct amdgpu_device *adev, u32 vclk, u32 dclk);
        int (*set_vce_clocks)(struct amdgpu_device *adev, u32 evclk, u32 ecclk);
-       /* query virtual capabilities */
-       u32 (*get_virtual_caps)(struct amdgpu_device *adev);
+       /* static power management */
+       int (*get_pcie_lanes)(struct amdgpu_device *adev);
+       void (*set_pcie_lanes)(struct amdgpu_device *adev, int lanes);
 };
 
 /*
@@ -1935,16 +1930,6 @@ struct amdgpu_atcs {
 struct cgs_device *amdgpu_cgs_create_device(struct amdgpu_device *adev);
 void amdgpu_cgs_destroy_device(struct cgs_device *cgs_device);
 
-
-/* GPU virtualization */
-#define AMDGPU_VIRT_CAPS_SRIOV_EN       (1 << 0)
-#define AMDGPU_VIRT_CAPS_IS_VF          (1 << 1)
-struct amdgpu_virtualization {
-       bool supports_sr_iov;
-       bool is_virtual;
-       u32 caps;
-};
-
 /*
  * Core structure, functions and helpers.
  */
@@ -1958,6 +1943,8 @@ struct amdgpu_ip_block_status {
        bool valid;
        bool sw;
        bool hw;
+       bool late_initialized;
+       bool hang;
 };
 
 struct amdgpu_device {
@@ -2016,6 +2003,8 @@ struct amdgpu_device {
        spinlock_t pcie_idx_lock;
        amdgpu_rreg_t                   pcie_rreg;
        amdgpu_wreg_t                   pcie_wreg;
+       amdgpu_rreg_t                   pciep_rreg;
+       amdgpu_wreg_t                   pciep_wreg;
        /* protects concurrent UVD register access */
        spinlock_t uvd_ctx_idx_lock;
        amdgpu_rreg_t                   uvd_ctx_rreg;
@@ -2056,7 +2045,16 @@ struct amdgpu_device {
        atomic64_t                      num_evictions;
        atomic_t                        gpu_reset_counter;
 
+       /* data for buffer migration throttling */
+       struct {
+               spinlock_t              lock;
+               s64                     last_update_us;
+               s64                     accum_us; /* accumulated microseconds */
+               u32                     log2_max_MBps;
+       } mm_stats;
+
        /* display */
+       bool                            enable_virtual_display;
        struct amdgpu_mode_info         mode_info;
        struct work_struct              hotplug_work;
        struct amdgpu_irq_src           crtc_irq;
@@ -2119,6 +2117,14 @@ struct amdgpu_device {
        struct kfd_dev          *kfd;
 
        struct amdgpu_virtualization virtualization;
+
+       /* link all shadow bo */
+       struct list_head                shadow_list;
+       struct mutex                    shadow_list_lock;
+       /* link all gtt */
+       spinlock_t                      gtt_list_lock;
+       struct list_head                gtt_list;
+
 };
 
 bool amdgpu_device_is_px(struct drm_device *dev);
@@ -2151,6 +2157,8 @@ void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v);
 #define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK)
 #define RREG32_PCIE(reg) adev->pcie_rreg(adev, (reg))
 #define WREG32_PCIE(reg, v) adev->pcie_wreg(adev, (reg), (v))
+#define RREG32_PCIE_PORT(reg) adev->pciep_rreg(adev, (reg))
+#define WREG32_PCIE_PORT(reg, v) adev->pciep_wreg(adev, (reg), (v))
 #define RREG32_SMC(reg) adev->smc_rreg(adev, (reg))
 #define WREG32_SMC(reg, v) adev->smc_wreg(adev, (reg), (v))
 #define RREG32_UVD_CTX(reg) adev->uvd_ctx_rreg(adev, (reg))
@@ -2194,6 +2202,9 @@ void amdgpu_mm_wdoorbell(struct amdgpu_device *adev, u32 index, u32 v);
 #define REG_GET_FIELD(value, reg, field)                               \
        (((value) & REG_FIELD_MASK(reg, field)) >> REG_FIELD_SHIFT(reg, field))
 
+#define WREG32_FIELD(reg, field, val)  \
+       WREG32(mm##reg, (RREG32(mm##reg) & ~REG_FIELD_MASK(reg, field)) | (val) << REG_FIELD_SHIFT(reg, field))
+
 /*
  * BIOS helpers.
  */
@@ -2237,14 +2248,17 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
 #define amdgpu_asic_get_xclk(adev) (adev)->asic_funcs->get_xclk((adev))
 #define amdgpu_asic_set_uvd_clocks(adev, v, d) (adev)->asic_funcs->set_uvd_clocks((adev), (v), (d))
 #define amdgpu_asic_set_vce_clocks(adev, ev, ec) (adev)->asic_funcs->set_vce_clocks((adev), (ev), (ec))
-#define amdgpu_asic_get_virtual_caps(adev) ((adev)->asic_funcs->get_virtual_caps((adev)))
+#define amdgpu_get_pcie_lanes(adev) (adev)->asic_funcs->get_pcie_lanes((adev))
+#define amdgpu_set_pcie_lanes(adev, l) (adev)->asic_funcs->set_pcie_lanes((adev), (l))
+#define amdgpu_asic_get_gpu_clock_counter(adev) (adev)->asic_funcs->get_gpu_clock_counter((adev))
 #define amdgpu_asic_read_disabled_bios(adev) (adev)->asic_funcs->read_disabled_bios((adev))
 #define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l))
+#define amdgpu_asic_detect_hw_virtualization(adev) (adev)->asic_funcs->detect_hw_virtualization((adev))
 #define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v)))
 #define amdgpu_gart_flush_gpu_tlb(adev, vmid) (adev)->gart.gart_funcs->flush_gpu_tlb((adev), (vmid))
 #define amdgpu_gart_set_pte_pde(adev, pt, idx, addr, flags) (adev)->gart.gart_funcs->set_pte_pde((adev), (pt), (idx), (addr), (flags))
 #define amdgpu_vm_copy_pte(adev, ib, pe, src, count) ((adev)->vm_manager.vm_pte_funcs->copy_pte((ib), (pe), (src), (count)))
-#define amdgpu_vm_write_pte(adev, ib, pa, pe, addr, count, incr, flags) ((adev)->vm_manager.vm_pte_funcs->write_pte((ib), (pa), (pe), (addr), (count), (incr), (flags)))
+#define amdgpu_vm_write_pte(adev, ib, pe, value, count, incr) ((adev)->vm_manager.vm_pte_funcs->write_pte((ib), (pe), (value), (count), (incr)))
 #define amdgpu_vm_set_pte_pde(adev, ib, pe, addr, count, incr, flags) ((adev)->vm_manager.vm_pte_funcs->set_pte_pde((ib), (pe), (addr), (count), (incr), (flags)))
 #define amdgpu_ring_parse_cs(r, p, ib) ((r)->funcs->parse_cs((p), (ib)))
 #define amdgpu_ring_test_ring(r) (r)->funcs->test_ring((r))
@@ -2259,9 +2273,13 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
 #define amdgpu_ring_emit_gds_switch(r, v, db, ds, wb, ws, ab, as) (r)->funcs->emit_gds_switch((r), (v), (db), (ds), (wb), (ws), (ab), (as))
 #define amdgpu_ring_emit_hdp_flush(r) (r)->funcs->emit_hdp_flush((r))
 #define amdgpu_ring_emit_hdp_invalidate(r) (r)->funcs->emit_hdp_invalidate((r))
+#define amdgpu_ring_emit_switch_buffer(r) (r)->funcs->emit_switch_buffer((r))
+#define amdgpu_ring_emit_cntxcntl(r, d) (r)->funcs->emit_cntxcntl((r), (d))
 #define amdgpu_ring_pad_ib(r, ib) ((r)->funcs->pad_ib((r), (ib)))
 #define amdgpu_ring_init_cond_exec(r) (r)->funcs->init_cond_exec((r))
 #define amdgpu_ring_patch_cond_exec(r,o) (r)->funcs->patch_cond_exec((r),(o))
+#define amdgpu_ring_get_emit_ib_size(r) (r)->funcs->get_emit_ib_size((r))
+#define amdgpu_ring_get_dma_frame_size(r) (r)->funcs->get_dma_frame_size((r))
 #define amdgpu_ih_get_wptr(adev) (adev)->irq.ih_funcs->get_wptr((adev))
 #define amdgpu_ih_decode_iv(adev, iv) (adev)->irq.ih_funcs->decode_iv((adev), (iv))
 #define amdgpu_ih_set_rptr(adev) (adev)->irq.ih_funcs->set_rptr((adev))
@@ -2293,6 +2311,11 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
 #define amdgpu_gfx_get_gpu_clock_counter(adev) (adev)->gfx.funcs->get_gpu_clock_counter((adev))
 #define amdgpu_gfx_select_se_sh(adev, se, sh, instance) (adev)->gfx.funcs->select_se_sh((adev), (se), (sh), (instance))
 
+#define amdgpu_dpm_read_sensor(adev, idx, value) \
+       ((adev)->pp_enabled ? \
+               (adev)->powerplay.pp_funcs->read_sensor(adev->powerplay.pp_handle, (idx), (value)) : \
+               -EINVAL)
+
 #define amdgpu_dpm_get_temperature(adev) \
        ((adev)->pp_enabled ?                                           \
              (adev)->powerplay.pp_funcs->get_temperature((adev)->powerplay.pp_handle) : \
@@ -2344,11 +2367,6 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
              (adev)->powerplay.pp_funcs->powergate_vce((adev)->powerplay.pp_handle, (g)) : \
              (adev)->pm.funcs->powergate_vce((adev), (g)))
 
-#define amdgpu_dpm_debugfs_print_current_performance_level(adev, m) \
-       ((adev)->pp_enabled ?                                           \
-             (adev)->powerplay.pp_funcs->print_current_performance_level((adev)->powerplay.pp_handle, (m)) : \
-             (adev)->pm.funcs->debugfs_print_current_performance_level((adev), (m)))
-
 #define amdgpu_dpm_get_current_power_state(adev) \
        (adev)->powerplay.pp_funcs->get_current_power_state((adev)->powerplay.pp_handle)
 
@@ -2389,6 +2407,7 @@ amdgpu_get_sdma_instance(struct amdgpu_ring *ring)
 
 /* Common functions */
 int amdgpu_gpu_reset(struct amdgpu_device *adev);
+bool amdgpu_need_backup(struct amdgpu_device *adev);
 void amdgpu_pci_config_reset(struct amdgpu_device *adev);
 bool amdgpu_card_posted(struct amdgpu_device *adev);
 void amdgpu_update_display_priority(struct amdgpu_device *adev);
@@ -2397,7 +2416,7 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data);
 int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type,
                       u32 ip_instance, u32 ring,
                       struct amdgpu_ring **out_ring);
-void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain);
+void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *abo, u32 domain);
 bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo);
 int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages);
 int amdgpu_ttm_tt_set_userptr(struct ttm_tt *ttm, uint64_t addr,
@@ -2414,6 +2433,10 @@ uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
 void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base);
 void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc);
 void amdgpu_ttm_set_active_vram_size(struct amdgpu_device *adev, u64 size);
+u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev);
+int amdgpu_ttm_global_init(struct amdgpu_device *adev);
+int amdgpu_ttm_init(struct amdgpu_device *adev);
+void amdgpu_ttm_fini(struct amdgpu_device *adev);
 void amdgpu_program_register_sequence(struct amdgpu_device *adev,
                                             const u32 *registers,
                                             const u32 array_size);
@@ -2425,11 +2448,13 @@ void amdgpu_register_atpx_handler(void);
 void amdgpu_unregister_atpx_handler(void);
 bool amdgpu_has_atpx_dgpu_power_cntl(void);
 bool amdgpu_is_atpx_hybrid(void);
+bool amdgpu_atpx_dgpu_req_power_for_displays(void);
 #else
 static inline void amdgpu_register_atpx_handler(void) {}
 static inline void amdgpu_unregister_atpx_handler(void) {}
 static inline bool amdgpu_has_atpx_dgpu_power_cntl(void) { return false; }
 static inline bool amdgpu_is_atpx_hybrid(void) { return false; }
+static inline bool amdgpu_atpx_dgpu_req_power_for_displays(void) { return false; }
 #endif
 
 /*
@@ -2446,8 +2471,8 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
                                 struct drm_file *file_priv);
 void amdgpu_driver_preclose_kms(struct drm_device *dev,
                                struct drm_file *file_priv);
-int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon);
-int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon);
+int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon);
+int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon);
 u32 amdgpu_get_vblank_counter_kms(struct drm_device *dev, unsigned int pipe);
 int amdgpu_enable_vblank_kms(struct drm_device *dev, unsigned int pipe);
 void amdgpu_disable_vblank_kms(struct drm_device *dev, unsigned int pipe);
@@ -2493,6 +2518,7 @@ static inline void amdgpu_acpi_fini(struct amdgpu_device *adev) { }
 struct amdgpu_bo_va_mapping *
 amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
                       uint64_t addr, struct amdgpu_bo **bo);
+int amdgpu_cs_sysvm_access_required(struct amdgpu_cs_parser *parser);
 
 #include "amdgpu_object.h"
 #endif
index 5cd7b73..5796539 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/acpi.h>
 #include <linux/slab.h>
 #include <linux/power_supply.h>
+#include <linux/pm_runtime.h>
 #include <acpi/video.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
@@ -333,6 +334,16 @@ int amdgpu_atif_handler(struct amdgpu_device *adev,
 #endif
                }
        }
+       if (req.pending & ATIF_DGPU_DISPLAY_EVENT) {
+               if ((adev->flags & AMD_IS_PX) &&
+                   amdgpu_atpx_dgpu_req_power_for_displays()) {
+                       pm_runtime_get_sync(adev->ddev->dev);
+                       /* Just fire off a uevent and let userspace tell us what to do */
+                       drm_helper_hpd_irq_event(adev->ddev);
+                       pm_runtime_mark_last_busy(adev->ddev->dev);
+                       pm_runtime_put_autosuspend(adev->ddev->dev);
+               }
+       }
        /* TODO: check other events */
 
        /* We've handled the event, stop the notifier chain. The ACPI interface
index d080d08..dba8a5b 100644 (file)
@@ -143,14 +143,6 @@ int amdgpu_amdkfd_resume(struct amdgpu_device *rdev)
        return r;
 }
 
-u32 pool_to_domain(enum kgd_memory_pool p)
-{
-       switch (p) {
-       case KGD_POOL_FRAMEBUFFER: return AMDGPU_GEM_DOMAIN_VRAM;
-       default: return AMDGPU_GEM_DOMAIN_GTT;
-       }
-}
-
 int alloc_gtt_mem(struct kgd_dev *kgd, size_t size,
                        void **mem_obj, uint64_t *gpu_addr,
                        void **cpu_ptr)
index 362bedc..1a0a5f7 100644 (file)
@@ -103,11 +103,11 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address,
                                uint32_t pipe_id, uint32_t queue_id);
 
 static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
-                               unsigned int timeout, uint32_t pipe_id,
+                               unsigned int utimeout, uint32_t pipe_id,
                                uint32_t queue_id);
 static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd);
 static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
-                               unsigned int timeout);
+                               unsigned int utimeout);
 static int kgd_address_watch_disable(struct kgd_dev *kgd);
 static int kgd_address_watch_execute(struct kgd_dev *kgd,
                                        unsigned int watch_point_id,
@@ -437,11 +437,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd)
 }
 
 static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
-                               unsigned int timeout, uint32_t pipe_id,
+                               unsigned int utimeout, uint32_t pipe_id,
                                uint32_t queue_id)
 {
        struct amdgpu_device *adev = get_amdgpu_device(kgd);
        uint32_t temp;
+       int timeout = utimeout;
 
        acquire_queue(kgd, pipe_id, queue_id);
        WREG32(mmCP_HQD_PQ_DOORBELL_CONTROL, 0);
@@ -452,9 +453,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
                temp = RREG32(mmCP_HQD_ACTIVE);
                if (temp & CP_HQD_ACTIVE__ACTIVE_MASK)
                        break;
-               if (timeout == 0) {
-                       pr_err("kfd: cp queue preemption time out (%dms)\n",
-                               temp);
+               if (timeout <= 0) {
+                       pr_err("kfd: cp queue preemption time out.\n");
                        release_queue(kgd);
                        return -ETIME;
                }
@@ -467,12 +467,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
 }
 
 static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
-                               unsigned int timeout)
+                               unsigned int utimeout)
 {
        struct amdgpu_device *adev = get_amdgpu_device(kgd);
        struct cik_sdma_rlc_registers *m;
        uint32_t sdma_base_addr;
        uint32_t temp;
+       int timeout = utimeout;
 
        m = get_sdma_mqd(mqd);
        sdma_base_addr = get_sdma_base_addr(m);
@@ -485,7 +486,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
                temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS);
                if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT)
                        break;
-               if (timeout == 0)
+               if (timeout <= 0)
                        return -ETIME;
                msleep(20);
                timeout -= 20;
index 04b744d..6697612 100644 (file)
@@ -62,10 +62,10 @@ static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address,
                uint32_t pipe_id, uint32_t queue_id);
 static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd);
 static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
-                               unsigned int timeout, uint32_t pipe_id,
+                               unsigned int utimeout, uint32_t pipe_id,
                                uint32_t queue_id);
 static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
-                               unsigned int timeout);
+                               unsigned int utimeout);
 static void write_vmid_invalidate_request(struct kgd_dev *kgd, uint8_t vmid);
 static int kgd_address_watch_disable(struct kgd_dev *kgd);
 static int kgd_address_watch_execute(struct kgd_dev *kgd,
@@ -349,11 +349,12 @@ static bool kgd_hqd_sdma_is_occupied(struct kgd_dev *kgd, void *mqd)
 }
 
 static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
-                               unsigned int timeout, uint32_t pipe_id,
+                               unsigned int utimeout, uint32_t pipe_id,
                                uint32_t queue_id)
 {
        struct amdgpu_device *adev = get_amdgpu_device(kgd);
        uint32_t temp;
+       int timeout = utimeout;
 
        acquire_queue(kgd, pipe_id, queue_id);
 
@@ -363,9 +364,8 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
                temp = RREG32(mmCP_HQD_ACTIVE);
                if (temp & CP_HQD_ACTIVE__ACTIVE_MASK)
                        break;
-               if (timeout == 0) {
-                       pr_err("kfd: cp queue preemption time out (%dms)\n",
-                               temp);
+               if (timeout <= 0) {
+                       pr_err("kfd: cp queue preemption time out.\n");
                        release_queue(kgd);
                        return -ETIME;
                }
@@ -378,12 +378,13 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type,
 }
 
 static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
-                               unsigned int timeout)
+                               unsigned int utimeout)
 {
        struct amdgpu_device *adev = get_amdgpu_device(kgd);
        struct cik_sdma_rlc_registers *m;
        uint32_t sdma_base_addr;
        uint32_t temp;
+       int timeout = utimeout;
 
        m = get_sdma_mqd(mqd);
        sdma_base_addr = get_sdma_base_addr(m);
@@ -396,7 +397,7 @@ static int kgd_hqd_sdma_destroy(struct kgd_dev *kgd, void *mqd,
                temp = RREG32(sdma_base_addr + mmSDMA0_RLC0_CONTEXT_STATUS);
                if (temp & SDMA0_STATUS_REG__RB_CMD_IDLE__SHIFT)
                        break;
-               if (timeout == 0)
+               if (timeout <= 0)
                        return -ETIME;
                msleep(20);
                timeout -= 20;
index fe872b8..8e6bf54 100644 (file)
@@ -259,6 +259,33 @@ static const int object_connector_convert[] = {
        DRM_MODE_CONNECTOR_Unknown
 };
 
+bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev)
+{
+       struct amdgpu_mode_info *mode_info = &adev->mode_info;
+       struct atom_context *ctx = mode_info->atom_context;
+       int index = GetIndexIntoMasterTable(DATA, Object_Header);
+       u16 size, data_offset;
+       u8 frev, crev;
+       ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
+       ATOM_OBJECT_HEADER *obj_header;
+
+       if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset))
+               return false;
+
+       if (crev < 2)
+               return false;
+
+       obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset);
+       path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)
+           (ctx->bios + data_offset +
+            le16_to_cpu(obj_header->usDisplayPathTableOffset));
+
+       if (path_obj->ucNumOfDispPath)
+               return true;
+       else
+               return false;
+}
+
 bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev)
 {
        struct amdgpu_mode_info *mode_info = &adev->mode_info;
@@ -964,6 +991,48 @@ int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
                return -EINVAL;
 
        switch (crev) {
+       case 2:
+       case 3:
+       case 5:
+               /* r6xx, r7xx, evergreen, ni, si.
+                * TODO: add support for asic_type <= CHIP_RV770*/
+               if (clock_type == COMPUTE_ENGINE_PLL_PARAM) {
+                       args.v3.ulClockParams = cpu_to_le32((clock_type << 24) | clock);
+
+                       amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+
+                       dividers->post_div = args.v3.ucPostDiv;
+                       dividers->enable_post_div = (args.v3.ucCntlFlag &
+                                                    ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false;
+                       dividers->enable_dithen = (args.v3.ucCntlFlag &
+                                                  ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true;
+                       dividers->whole_fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDiv);
+                       dividers->frac_fb_div = le16_to_cpu(args.v3.ulFbDiv.usFbDivFrac);
+                       dividers->ref_div = args.v3.ucRefDiv;
+                       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 (adev->asic_type >= 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;
+
+                       amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+
+                       dividers->post_div = args.v5.ucPostDiv;
+                       dividers->enable_post_div = (args.v5.ucCntlFlag &
+                                                    ATOM_PLL_CNTL_FLAG_PLL_POST_DIV_EN) ? true : false;
+                       dividers->enable_dithen = (args.v5.ucCntlFlag &
+                                                  ATOM_PLL_CNTL_FLAG_FRACTION_DISABLE) ? false : true;
+                       dividers->whole_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDiv);
+                       dividers->frac_fb_div = le16_to_cpu(args.v5.ulFbDiv.usFbDivFrac);
+                       dividers->ref_div = args.v5.ucRefDiv;
+                       dividers->vco_mode = (args.v5.ucCntlFlag &
+                                             ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0;
+               }
+               break;
        case 4:
                /* fusion */
                args.v4.ulClock = cpu_to_le32(clock);   /* 10 khz */
@@ -1108,6 +1177,32 @@ void amdgpu_atombios_set_engine_dram_timings(struct amdgpu_device *adev,
        amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
 }
 
+void amdgpu_atombios_get_default_voltages(struct amdgpu_device *adev,
+                                         u16 *vddc, u16 *vddci, u16 *mvdd)
+{
+       struct amdgpu_mode_info *mode_info = &adev->mode_info;
+       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
+       u8 frev, crev;
+       u16 data_offset;
+       union firmware_info *firmware_info;
+
+       *vddc = 0;
+       *vddci = 0;
+       *mvdd = 0;
+
+       if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
+                                  &frev, &crev, &data_offset)) {
+               firmware_info =
+                       (union firmware_info *)(mode_info->atom_context->bios +
+                                               data_offset);
+               *vddc = le16_to_cpu(firmware_info->info_14.usBootUpVDDCVoltage);
+               if ((frev == 2) && (crev >= 2)) {
+                       *vddci = le16_to_cpu(firmware_info->info_22.usBootUpVDDCIVoltage);
+                       *mvdd = le16_to_cpu(firmware_info->info_22.usBootUpMVDDCVoltage);
+               }
+       }
+}
+
 union set_voltage {
        struct _SET_VOLTAGE_PS_ALLOCATION alloc;
        struct _SET_VOLTAGE_PARAMETERS v1;
@@ -1115,6 +1210,52 @@ union set_voltage {
        struct _SET_VOLTAGE_PARAMETERS_V1_3 v3;
 };
 
+int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
+                            u16 voltage_id, u16 *voltage)
+{
+       union set_voltage args;
+       int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
+       u8 frev, crev;
+
+       if (!amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, index, &frev, &crev))
+               return -EINVAL;
+
+       switch (crev) {
+       case 1:
+               return -EINVAL;
+       case 2:
+               args.v2.ucVoltageType = SET_VOLTAGE_GET_MAX_VOLTAGE;
+               args.v2.ucVoltageMode = 0;
+               args.v2.usVoltageLevel = 0;
+
+               amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+
+               *voltage = le16_to_cpu(args.v2.usVoltageLevel);
+               break;
+       case 3:
+               args.v3.ucVoltageType = voltage_type;
+               args.v3.ucVoltageMode = ATOM_GET_VOLTAGE_LEVEL;
+               args.v3.usVoltageLevel = cpu_to_le16(voltage_id);
+
+               amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
+
+               *voltage = le16_to_cpu(args.v3.usVoltageLevel);
+               break;
+       default:
+               DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(struct amdgpu_device *adev,
+                                                     u16 *voltage,
+                                                     u16 leakage_idx)
+{
+       return amdgpu_atombios_get_max_vddc(adev, VOLTAGE_TYPE_VDDC, leakage_idx, voltage);
+}
+
 void amdgpu_atombios_set_voltage(struct amdgpu_device *adev,
                                 u16 voltage_level,
                                 u8 voltage_type)
@@ -1335,6 +1476,50 @@ static ATOM_VOLTAGE_OBJECT_V3 *amdgpu_atombios_lookup_voltage_object_v3(ATOM_VOL
        return NULL;
 }
 
+int amdgpu_atombios_get_svi2_info(struct amdgpu_device *adev,
+                             u8 voltage_type,
+                             u8 *svd_gpio_id, u8 *svc_gpio_id)
+{
+       int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo);
+       u8 frev, crev;
+       u16 data_offset, size;
+       union voltage_object_info *voltage_info;
+       union voltage_object *voltage_object = NULL;
+
+       if (amdgpu_atom_parse_data_header(adev->mode_info.atom_context, index, &size,
+                                  &frev, &crev, &data_offset)) {
+               voltage_info = (union voltage_object_info *)
+                       (adev->mode_info.atom_context->bios + data_offset);
+
+               switch (frev) {
+               case 3:
+                       switch (crev) {
+                       case 1:
+                               voltage_object = (union voltage_object *)
+                                       amdgpu_atombios_lookup_voltage_object_v3(&voltage_info->v3,
+                                                                     voltage_type,
+                                                                     VOLTAGE_OBJ_SVID2);
+                               if (voltage_object) {
+                                       *svd_gpio_id = voltage_object->v3.asSVID2Obj.ucSVDGpioId;
+                                       *svc_gpio_id = voltage_object->v3.asSVID2Obj.ucSVCGpioId;
+                               } else {
+                                       return -EINVAL;
+                               }
+                               break;
+                       default:
+                               DRM_ERROR("unknown voltage object table\n");
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       DRM_ERROR("unknown voltage object table\n");
+                       return -EINVAL;
+               }
+
+       }
+       return 0;
+}
+
 bool
 amdgpu_atombios_is_voltage_gpio(struct amdgpu_device *adev,
                                u8 voltage_type, u8 voltage_mode)
index 8c2e696..1735615 100644 (file)
@@ -140,6 +140,8 @@ struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *
                                                          uint8_t id);
 void amdgpu_atombios_i2c_init(struct amdgpu_device *adev);
 
+bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev);
+
 bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev);
 
 int amdgpu_atombios_get_clock_info(struct amdgpu_device *adev);
@@ -206,5 +208,19 @@ void amdgpu_atombios_scratch_regs_save(struct amdgpu_device *adev);
 void amdgpu_atombios_scratch_regs_restore(struct amdgpu_device *adev);
 
 void amdgpu_atombios_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le);
-
+int amdgpu_atombios_get_max_vddc(struct amdgpu_device *adev, u8 voltage_type,
+                            u16 voltage_id, u16 *voltage);
+int amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(struct amdgpu_device *adev,
+                                                     u16 *voltage,
+                                                     u16 leakage_idx);
+void amdgpu_atombios_get_default_voltages(struct amdgpu_device *adev,
+                                         u16 *vddc, u16 *vddci, u16 *mvdd);
+int amdgpu_atombios_get_clock_dividers(struct amdgpu_device *adev,
+                                      u8 clock_type,
+                                      u32 clock,
+                                      bool strobe_mode,
+                                      struct atom_clock_dividers *dividers);
+int amdgpu_atombios_get_svi2_info(struct amdgpu_device *adev,
+                             u8 voltage_type,
+                             u8 *svd_gpio_id, u8 *svc_gpio_id);
 #endif
index 10b5ddf..dae35a9 100644 (file)
@@ -29,6 +29,7 @@ struct amdgpu_atpx {
        acpi_handle handle;
        struct amdgpu_atpx_functions functions;
        bool is_hybrid;
+       bool dgpu_req_power_for_displays;
 };
 
 static struct amdgpu_atpx_priv {
@@ -73,6 +74,10 @@ bool amdgpu_is_atpx_hybrid(void) {
        return amdgpu_atpx_priv.atpx.is_hybrid;
 }
 
+bool amdgpu_atpx_dgpu_req_power_for_displays(void) {
+       return amdgpu_atpx_priv.atpx.dgpu_req_power_for_displays;
+}
+
 /**
  * amdgpu_atpx_call - call an ATPX method
  *
@@ -204,6 +209,10 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx)
                atpx->is_hybrid = true;
        }
 
+       atpx->dgpu_req_power_for_displays = false;
+       if (valid_bits & ATPX_DGPU_REQ_POWER_FOR_DISPLAYS)
+               atpx->dgpu_req_power_for_displays = true;
+
        return 0;
 }
 
index 33e47a4..3453052 100644 (file)
@@ -39,7 +39,8 @@ static int amdgpu_benchmark_do_move(struct amdgpu_device *adev, unsigned size,
        start_jiffies = jiffies;
        for (i = 0; i < n; i++) {
                struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
-               r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence);
+               r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence,
+                                      false);
                if (r)
                        goto exit_do_move;
                r = fence_wait(fence, false);
index bc0440f..7a8bfa3 100644 (file)
@@ -616,7 +616,7 @@ static int amdgpu_cgs_irq_put(struct cgs_device *cgs_device, unsigned src_id, un
        return amdgpu_irq_put(adev, adev->irq.sources[src_id], type);
 }
 
-int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
+static int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
                                  enum amd_ip_block_type block_type,
                                  enum amd_clockgating_state state)
 {
@@ -637,7 +637,7 @@ int amdgpu_cgs_set_clockgating_state(struct cgs_device *cgs_device,
        return r;
 }
 
-int amdgpu_cgs_set_powergating_state(struct cgs_device *cgs_device,
+static int amdgpu_cgs_set_powergating_state(struct cgs_device *cgs_device,
                                  enum amd_ip_block_type block_type,
                                  enum amd_powergating_state state)
 {
@@ -711,6 +711,47 @@ static int amdgpu_cgs_rel_firmware(struct cgs_device *cgs_device, enum cgs_ucode
        return -EINVAL;
 }
 
+static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device,
+                                       enum cgs_ucode_id type)
+{
+       CGS_FUNC_ADEV;
+       uint16_t fw_version;
+
+       switch (type) {
+               case CGS_UCODE_ID_SDMA0:
+                       fw_version = adev->sdma.instance[0].fw_version;
+                       break;
+               case CGS_UCODE_ID_SDMA1:
+                       fw_version = adev->sdma.instance[1].fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_CE:
+                       fw_version = adev->gfx.ce_fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_PFP:
+                       fw_version = adev->gfx.pfp_fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_ME:
+                       fw_version = adev->gfx.me_fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_MEC:
+                       fw_version = adev->gfx.mec_fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_MEC_JT1:
+                       fw_version = adev->gfx.mec_fw_version;
+                       break;
+               case CGS_UCODE_ID_CP_MEC_JT2:
+                       fw_version = adev->gfx.mec_fw_version;
+                       break;
+               case CGS_UCODE_ID_RLC_G:
+                       fw_version = adev->gfx.rlc_fw_version;
+                       break;
+               default:
+                       DRM_ERROR("firmware type %d do not have version\n", type);
+                       fw_version = 0;
+       }
+       return fw_version;
+}
+
 static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                                        enum cgs_ucode_id type,
                                        struct cgs_firmware_info *info)
@@ -741,6 +782,7 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
                info->mc_addr = gpu_addr;
                info->image_size = data_size;
                info->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
+               info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
                info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
        } else {
                char fw_name[30] = {0};
@@ -848,6 +890,12 @@ static int amdgpu_cgs_query_system_info(struct cgs_device *cgs_device,
        case CGS_SYSTEM_INFO_GFX_SE_INFO:
                sys_info->value = adev->gfx.config.max_shader_engines;
                break;
+       case CGS_SYSTEM_INFO_PCIE_SUB_SYS_ID:
+               sys_info->value = adev->pdev->subsystem_device;
+               break;
+       case CGS_SYSTEM_INFO_PCIE_SUB_SYS_VENDOR_ID:
+               sys_info->value = adev->pdev->subsystem_vendor;
+               break;
        default:
                return -ENODEV;
        }
index ff0b55a..2e3a054 100644 (file)
@@ -168,12 +168,12 @@ int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector)
                }
 
                /* Any defined maximum tmds clock limit we must not exceed? */
-               if (connector->max_tmds_clock > 0) {
+               if (connector->display_info.max_tmds_clock > 0) {
                        /* mode_clock is clock in kHz for mode to be modeset on this connector */
                        mode_clock = amdgpu_connector->pixelclock_for_modeset;
 
                        /* Maximum allowable input clock in kHz */
-                       max_tmds_clock = connector->max_tmds_clock * 1000;
+                       max_tmds_clock = connector->display_info.max_tmds_clock;
 
                        DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n",
                                  connector->name, mode_clock, max_tmds_clock);
@@ -769,8 +769,10 @@ static void amdgpu_connector_destroy(struct drm_connector *connector)
 {
        struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
-       if (amdgpu_connector->ddc_bus->has_aux)
+       if (amdgpu_connector->ddc_bus->has_aux) {
                drm_dp_aux_unregister(&amdgpu_connector->ddc_bus->aux);
+               amdgpu_connector->ddc_bus->has_aux = false;
+       }
        amdgpu_connector_free_edid(connector);
        kfree(amdgpu_connector->con_priv);
        drm_connector_unregister(connector);
@@ -1504,6 +1506,88 @@ static const struct drm_connector_funcs amdgpu_connector_edp_funcs = {
        .force = amdgpu_connector_dvi_force,
 };
 
+static struct drm_encoder *
+amdgpu_connector_virtual_encoder(struct drm_connector *connector)
+{
+       int enc_id = connector->encoder_ids[0];
+       struct drm_encoder *encoder;
+       int i;
+       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+               if (connector->encoder_ids[i] == 0)
+                       break;
+
+               encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]);
+               if (!encoder)
+                       continue;
+
+               if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL)
+                       return encoder;
+       }
+
+       /* pick the first one */
+       if (enc_id)
+               return drm_encoder_find(connector->dev, enc_id);
+       return NULL;
+}
+
+static int amdgpu_connector_virtual_get_modes(struct drm_connector *connector)
+{
+       struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector);
+
+       if (encoder) {
+               amdgpu_connector_add_common_modes(encoder, connector);
+       }
+
+       return 0;
+}
+
+static int amdgpu_connector_virtual_mode_valid(struct drm_connector *connector,
+                                          struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static int
+amdgpu_connector_virtual_dpms(struct drm_connector *connector, int mode)
+{
+       return 0;
+}
+
+static enum drm_connector_status
+
+amdgpu_connector_virtual_detect(struct drm_connector *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static int
+amdgpu_connector_virtual_set_property(struct drm_connector *connector,
+                                 struct drm_property *property,
+                                 uint64_t val)
+{
+       return 0;
+}
+
+static void amdgpu_connector_virtual_force(struct drm_connector *connector)
+{
+       return;
+}
+
+static const struct drm_connector_helper_funcs amdgpu_connector_virtual_helper_funcs = {
+       .get_modes = amdgpu_connector_virtual_get_modes,
+       .mode_valid = amdgpu_connector_virtual_mode_valid,
+       .best_encoder = amdgpu_connector_virtual_encoder,
+};
+
+static const struct drm_connector_funcs amdgpu_connector_virtual_funcs = {
+       .dpms = amdgpu_connector_virtual_dpms,
+       .detect = amdgpu_connector_virtual_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = amdgpu_connector_virtual_set_property,
+       .destroy = amdgpu_connector_destroy,
+       .force = amdgpu_connector_virtual_force,
+};
+
 void
 amdgpu_connector_add(struct amdgpu_device *adev,
                      uint32_t connector_id,
@@ -1888,6 +1972,17 @@ amdgpu_connector_add(struct amdgpu_device *adev,
                        connector->interlace_allowed = false;
                        connector->doublescan_allowed = false;
                        break;
+               case DRM_MODE_CONNECTOR_VIRTUAL:
+                       amdgpu_dig_connector = kzalloc(sizeof(struct amdgpu_connector_atom_dig), GFP_KERNEL);
+                       if (!amdgpu_dig_connector)
+                               goto failed;
+                       amdgpu_connector->con_priv = amdgpu_dig_connector;
+                       drm_connector_init(dev, &amdgpu_connector->base, &amdgpu_connector_virtual_funcs, connector_type);
+                       drm_connector_helper_add(&amdgpu_connector->base, &amdgpu_connector_virtual_helper_funcs);
+                       subpixel_order = SubPixelHorizontalRGB;
+                       connector->interlace_allowed = false;
+                       connector->doublescan_allowed = false;
+                       break;
                }
        }
 
index 0307ff5..b0f6e69 100644 (file)
@@ -91,6 +91,7 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
                                      uint32_t *offset)
 {
        struct drm_gem_object *gobj;
+       unsigned long size;
 
        gobj = drm_gem_object_lookup(p->filp, data->handle);
        if (gobj == NULL)
@@ -101,6 +102,11 @@ static int amdgpu_cs_user_fence_chunk(struct amdgpu_cs_parser *p,
        p->uf_entry.tv.bo = &p->uf_entry.robj->tbo;
        p->uf_entry.tv.shared = true;
        p->uf_entry.user_pages = NULL;
+
+       size = amdgpu_bo_size(p->uf_entry.robj);
+       if (size != PAGE_SIZE || (data->offset + 8) > size)
+               return -EINVAL;
+
        *offset = data->offset;
 
        drm_gem_object_unreference_unlocked(gobj);
@@ -235,70 +241,212 @@ free_chunk:
        return ret;
 }
 
-/* Returns how many bytes TTM can move per IB.
+/* Convert microseconds to bytes. */
+static u64 us_to_bytes(struct amdgpu_device *adev, s64 us)
+{
+       if (us <= 0 || !adev->mm_stats.log2_max_MBps)
+               return 0;
+
+       /* Since accum_us is incremented by a million per second, just
+        * multiply it by the number of MB/s to get the number of bytes.
+        */
+       return us << adev->mm_stats.log2_max_MBps;
+}
+
+static s64 bytes_to_us(struct amdgpu_device *adev, u64 bytes)
+{
+       if (!adev->mm_stats.log2_max_MBps)
+               return 0;
+
+       return bytes >> adev->mm_stats.log2_max_MBps;
+}
+
+/* Returns how many bytes TTM can move right now. If no bytes can be moved,
+ * it returns 0. If it returns non-zero, it's OK to move at least one buffer,
+ * which means it can go over the threshold once. If that happens, the driver
+ * will be in debt and no other buffer migrations can be done until that debt
+ * is repaid.
+ *
+ * This approach allows moving a buffer of any size (it's important to allow
+ * that).
+ *
+ * The currency is simply time in microseconds and it increases as the clock
+ * ticks. The accumulated microseconds (us) are converted to bytes and
+ * returned.
  */
 static u64 amdgpu_cs_get_threshold_for_moves(struct amdgpu_device *adev)
 {
-       u64 real_vram_size = adev->mc.real_vram_size;
-       u64 vram_usage = atomic64_read(&adev->vram_usage);
+       s64 time_us, increment_us;
+       u64 max_bytes;
+       u64 free_vram, total_vram, used_vram;
 
-       /* This function is based on the current VRAM usage.
+       /* Allow a maximum of 200 accumulated ms. This is basically per-IB
+        * throttling.
         *
-        * - If all of VRAM is free, allow relocating the number of bytes that
-        *   is equal to 1/4 of the size of VRAM for this IB.
+        * It means that in order to get full max MBps, at least 5 IBs per
+        * second must be submitted and not more than 200ms apart from each
+        * other.
+        */
+       const s64 us_upper_bound = 200000;
 
-        * - If more than one half of VRAM is occupied, only allow relocating
-        *   1 MB of data for this IB.
-        *
-        * - From 0 to one half of used VRAM, the threshold decreases
-        *   linearly.
-        *         __________________
-        * 1/4 of -|\               |
-        * VRAM    | \              |
-        *         |  \             |
-        *         |   \            |
-        *         |    \           |
-        *         |     \          |
-        *         |      \         |
-        *         |       \________|1 MB
-        *         |----------------|
-        *    VRAM 0 %             100 %
-        *         used            used
-        *
-        * Note: It's a threshold, not a limit. The threshold must be crossed
-        * for buffer relocations to stop, so any buffer of an arbitrary size
-        * can be moved as long as the threshold isn't crossed before
-        * the relocation takes place. We don't want to disable buffer
-        * relocations completely.
+       if (!adev->mm_stats.log2_max_MBps)
+               return 0;
+
+       total_vram = adev->mc.real_vram_size - adev->vram_pin_size;
+       used_vram = atomic64_read(&adev->vram_usage);
+       free_vram = used_vram >= total_vram ? 0 : total_vram - used_vram;
+
+       spin_lock(&adev->mm_stats.lock);
+
+       /* Increase the amount of accumulated us. */
+       time_us = ktime_to_us(ktime_get());
+       increment_us = time_us - adev->mm_stats.last_update_us;
+       adev->mm_stats.last_update_us = time_us;
+       adev->mm_stats.accum_us = min(adev->mm_stats.accum_us + increment_us,
+                                      us_upper_bound);
+
+       /* This prevents the short period of low performance when the VRAM
+        * usage is low and the driver is in debt or doesn't have enough
+        * accumulated us to fill VRAM quickly.
         *
-        * The idea is that buffers should be placed in VRAM at creation time
-        * and TTM should only do a minimum number of relocations during
-        * command submission. In practice, you need to submit at least
-        * a dozen IBs to move all buffers to VRAM if they are in GTT.
+        * The situation can occur in these cases:
+        * - a lot of VRAM is freed by userspace
+        * - the presence of a big buffer causes a lot of evictions
+        *   (solution: split buffers into smaller ones)
         *
-        * Also, things can get pretty crazy under memory pressure and actual
-        * VRAM usage can change a lot, so playing safe even at 50% does
-        * consistently increase performance.
+        * If 128 MB or 1/8th of VRAM is free, start filling it now by setting
+        * accum_us to a positive number.
+        */
+       if (free_vram >= 128 * 1024 * 1024 || free_vram >= total_vram / 8) {
+               s64 min_us;
+
+               /* Be more aggresive on dGPUs. Try to fill a portion of free
+                * VRAM now.
+                */
+               if (!(adev->flags & AMD_IS_APU))
+                       min_us = bytes_to_us(adev, free_vram / 4);
+               else
+                       min_us = 0; /* Reset accum_us on APUs. */
+
+               adev->mm_stats.accum_us = max(min_us, adev->mm_stats.accum_us);
+       }
+
+       /* This returns 0 if the driver is in debt to disallow (optional)
+        * buffer moves.
+        */
+       max_bytes = us_to_bytes(adev, adev->mm_stats.accum_us);
+
+       spin_unlock(&adev->mm_stats.lock);
+       return max_bytes;
+}
+
+/* Report how many bytes have really been moved for the last command
+ * submission. This can result in a debt that can stop buffer migrations
+ * temporarily.
+ */
+static void amdgpu_cs_report_moved_bytes(struct amdgpu_device *adev,
+                                        u64 num_bytes)
+{
+       spin_lock(&adev->mm_stats.lock);
+       adev->mm_stats.accum_us -= bytes_to_us(adev, num_bytes);
+       spin_unlock(&adev->mm_stats.lock);
+}
+
+static int amdgpu_cs_bo_validate(struct amdgpu_cs_parser *p,
+                                struct amdgpu_bo *bo)
+{
+       u64 initial_bytes_moved;
+       uint32_t domain;
+       int r;
+
+       if (bo->pin_count)
+               return 0;
+
+       /* Don't move this buffer if we have depleted our allowance
+        * to move it. Don't move anything if the threshold is zero.
         */
+       if (p->bytes_moved < p->bytes_moved_threshold)
+               domain = bo->prefered_domains;
+       else
+               domain = bo->allowed_domains;
+
+retry:
+       amdgpu_ttm_placement_from_domain(bo, domain);
+       initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
+       r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+       p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
+               initial_bytes_moved;
+
+       if (unlikely(r == -ENOMEM) && domain != bo->allowed_domains) {
+               domain = bo->allowed_domains;
+               goto retry;
+       }
 
-       u64 half_vram = real_vram_size >> 1;
-       u64 half_free_vram = vram_usage >= half_vram ? 0 : half_vram - vram_usage;
-       u64 bytes_moved_threshold = half_free_vram >> 1;
-       return max(bytes_moved_threshold, 1024*1024ull);
+       return r;
 }
 
-int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
+/* Last resort, try to evict something from the current working set */
+static bool amdgpu_cs_try_evict(struct amdgpu_cs_parser *p,
+                               struct amdgpu_bo_list_entry *lobj)
+{
+       uint32_t domain = lobj->robj->allowed_domains;
+       int r;
+
+       if (!p->evictable)
+               return false;
+
+       for (;&p->evictable->tv.head != &p->validated;
+            p->evictable = list_prev_entry(p->evictable, tv.head)) {
+
+               struct amdgpu_bo_list_entry *candidate = p->evictable;
+               struct amdgpu_bo *bo = candidate->robj;
+               u64 initial_bytes_moved;
+               uint32_t other;
+
+               /* If we reached our current BO we can forget it */
+               if (candidate == lobj)
+                       break;
+
+               other = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type);
+
+               /* Check if this BO is in one of the domains we need space for */
+               if (!(other & domain))
+                       continue;
+
+               /* Check if we can move this BO somewhere else */
+               other = bo->allowed_domains & ~domain;
+               if (!other)
+                       continue;
+
+               /* Good we can try to move this BO somewhere else */
+               amdgpu_ttm_placement_from_domain(bo, other);
+               initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
+               r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
+               p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
+                       initial_bytes_moved;
+
+               if (unlikely(r))
+                       break;
+
+               p->evictable = list_prev_entry(p->evictable, tv.head);
+               list_move(&candidate->tv.head, &p->validated);
+
+               return true;
+       }
+
+       return false;
+}
+
+static int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
                            struct list_head *validated)
 {
        struct amdgpu_bo_list_entry *lobj;
-       u64 initial_bytes_moved;
        int r;
 
        list_for_each_entry(lobj, validated, tv.head) {
                struct amdgpu_bo *bo = lobj->robj;
                bool binding_userptr = false;
                struct mm_struct *usermm;
-               uint32_t domain;
 
                usermm = amdgpu_ttm_tt_get_usermm(bo->tbo.ttm);
                if (usermm && usermm != current->mm)
@@ -313,35 +461,19 @@ int amdgpu_cs_list_validate(struct amdgpu_cs_parser *p,
                        binding_userptr = true;
                }
 
-               if (bo->pin_count)
-                       continue;
-
-               /* Avoid moving this one if we have moved too many buffers
-                * for this IB already.
-                *
-                * Note that this allows moving at least one buffer of
-                * any size, because it doesn't take the current "bo"
-                * into account. We don't want to disallow buffer moves
-                * completely.
-                */
-               if (p->bytes_moved <= p->bytes_moved_threshold)
-                       domain = bo->prefered_domains;
-               else
-                       domain = bo->allowed_domains;
-
-       retry:
-               amdgpu_ttm_placement_from_domain(bo, domain);
-               initial_bytes_moved = atomic64_read(&bo->adev->num_bytes_moved);
-               r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
-               p->bytes_moved += atomic64_read(&bo->adev->num_bytes_moved) -
-                              initial_bytes_moved;
+               if (p->evictable == lobj)
+                       p->evictable = NULL;
 
-               if (unlikely(r)) {
-                       if (r != -ERESTARTSYS && domain != bo->allowed_domains) {
-                               domain = bo->allowed_domains;
-                               goto retry;
-                       }
+               do {
+                       r = amdgpu_cs_bo_validate(p, bo);
+               } while (r == -ENOMEM && amdgpu_cs_try_evict(p, lobj));
+               if (r)
                        return r;
+
+               if (bo->shadow) {
+                       r = amdgpu_cs_bo_validate(p, bo);
+                       if (r)
+                               return r;
                }
 
                if (binding_userptr) {
@@ -386,8 +518,10 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
 
                r = ttm_eu_reserve_buffers(&p->ticket, &p->validated, true,
                                           &duplicates);
-               if (unlikely(r != 0))
+               if (unlikely(r != 0)) {
+                       DRM_ERROR("ttm_eu_reserve_buffers failed.\n");
                        goto error_free_pages;
+               }
 
                /* Without a BO list we don't have userptr BOs */
                if (!p->bo_list)
@@ -427,9 +561,10 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
                /* Unreserve everything again. */
                ttm_eu_backoff_reservation(&p->ticket, &p->validated);
 
-               /* We tried to often, just abort */
+               /* We tried too many times, just abort */
                if (!--tries) {
                        r = -EDEADLK;
+                       DRM_ERROR("deadlock in %s\n", __func__);
                        goto error_free_pages;
                }
 
@@ -441,11 +576,13 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
                                                         sizeof(struct page*));
                        if (!e->user_pages) {
                                r = -ENOMEM;
+                               DRM_ERROR("calloc failure in %s\n", __func__);
                                goto error_free_pages;
                        }
 
                        r = amdgpu_ttm_tt_get_user_pages(ttm, e->user_pages);
                        if (r) {
+                               DRM_ERROR("amdgpu_ttm_tt_get_user_pages failed.\n");
                                drm_free_large(e->user_pages);
                                e->user_pages = NULL;
                                goto error_free_pages;
@@ -460,14 +597,23 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
 
        p->bytes_moved_threshold = amdgpu_cs_get_threshold_for_moves(p->adev);
        p->bytes_moved = 0;
+       p->evictable = list_last_entry(&p->validated,
+                                      struct amdgpu_bo_list_entry,
+                                      tv.head);
 
        r = amdgpu_cs_list_validate(p, &duplicates);
-       if (r)
+       if (r) {
+               DRM_ERROR("amdgpu_cs_list_validate(duplicates) failed.\n");
                goto error_validate;
+       }
 
        r = amdgpu_cs_list_validate(p, &p->validated);
-       if (r)
+       if (r) {
+               DRM_ERROR("amdgpu_cs_list_validate(validated) failed.\n");
                goto error_validate;
+       }
+
+       amdgpu_cs_report_moved_bytes(p->adev, p->bytes_moved);
 
        fpriv->vm.last_eviction_counter =
                atomic64_read(&p->adev->num_evictions);
@@ -499,8 +645,12 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
                }
        }
 
-       if (p->uf_entry.robj)
-               p->job->uf_addr += amdgpu_bo_gpu_offset(p->uf_entry.robj);
+       if (!r && p->uf_entry.robj) {
+               struct amdgpu_bo *uf = p->uf_entry.robj;
+
+               r = amdgpu_ttm_bind(&uf->tbo, &uf->tbo.mem);
+               p->job->uf_addr += amdgpu_bo_gpu_offset(uf);
+       }
 
 error_validate:
        if (r) {
@@ -617,7 +767,7 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
                        if (bo_va == NULL)
                                continue;
 
-                       r = amdgpu_vm_bo_update(adev, bo_va, &bo->tbo.mem);
+                       r = amdgpu_vm_bo_update(adev, bo_va, false);
                        if (r)
                                return r;
 
@@ -710,6 +860,14 @@ static int amdgpu_cs_ib_fill(struct amdgpu_device *adev,
                if (r)
                        return r;
 
+               if (ib->flags & AMDGPU_IB_FLAG_PREAMBLE) {
+                       parser->job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT;
+                       if (!parser->ctx->preamble_presented) {
+                               parser->job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT_FIRST;
+                               parser->ctx->preamble_presented = true;
+                       }
+               }
+
                if (parser->job->ring && parser->job->ring != ring)
                        return -EINVAL;
 
@@ -849,7 +1007,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
        }
 
        job->owner = p->filp;
-       job->ctx = entity->fence_context;
+       job->fence_ctx = entity->fence_context;
        p->fence = fence_get(&job->base.s_fence->finished);
        cs->out.handle = amdgpu_ctx_add_fence(p->ctx, ring, p->fence);
        job->uf_sequence = cs->out.handle;
@@ -1015,3 +1173,29 @@ amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
 
        return NULL;
 }
+
+/**
+ * amdgpu_cs_sysvm_access_required - make BOs accessible by the system VM
+ *
+ * @parser: command submission parser context
+ *
+ * Helper for UVD/VCE VM emulation, make sure BOs are accessible by the system VM.
+ */
+int amdgpu_cs_sysvm_access_required(struct amdgpu_cs_parser *parser)
+{
+       unsigned i;
+       int r;
+
+       if (!parser->bo_list)
+               return 0;
+
+       for (i = 0; i < parser->bo_list->num_entries; i++) {
+               struct amdgpu_bo *bo = parser->bo_list->array[i].robj;
+
+               r = amdgpu_ttm_bind(&bo->tbo, &bo->tbo.mem);
+               if (unlikely(r))
+                       return r;
+       }
+
+       return 0;
+}
index 17e1362..e203e55 100644 (file)
@@ -60,6 +60,7 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev, struct amdgpu_ctx *ctx)
                        amd_sched_entity_fini(&adev->rings[j]->sched,
                                              &ctx->rings[j].entity);
                kfree(ctx->fences);
+               ctx->fences = NULL;
                return r;
        }
        return 0;
@@ -77,6 +78,7 @@ static void amdgpu_ctx_fini(struct amdgpu_ctx *ctx)
                for (j = 0; j < amdgpu_sched_jobs; ++j)
                        fence_put(ctx->rings[i].fences[j]);
        kfree(ctx->fences);
+       ctx->fences = NULL;
 
        for (i = 0; i < adev->num_rings; i++)
                amd_sched_entity_fini(&adev->rings[i]->sched,
index 39c01b9..7dbe85d 100644 (file)
 #include "atom.h"
 #include "amdgpu_atombios.h"
 #include "amd_pcie.h"
+#ifdef CONFIG_DRM_AMDGPU_SI
+#include "si.h"
+#endif
 #ifdef CONFIG_DRM_AMDGPU_CIK
 #include "cik.h"
 #endif
 #include "vi.h"
 #include "bif/bif_4_1_d.h"
+#include <linux/pci.h>
+#include <linux/firmware.h>
 
 static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev);
 static void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev);
 
 static const char *amdgpu_asic_name[] = {
+       "TAHITI",
+       "PITCAIRN",
+       "VERDE",
+       "OLAND",
+       "HAINAN",
        "BONAIRE",
        "KAVERI",
        "KABINI",
@@ -101,7 +111,7 @@ void amdgpu_mm_wreg(struct amdgpu_device *adev, uint32_t reg, uint32_t v,
                    bool always_indirect)
 {
        trace_amdgpu_mm_wreg(adev->pdev->device, reg, v);
-       
+
        if ((reg * 4) < adev->rmmio_size && !always_indirect)
                writel(v, ((void __iomem *)adev->rmmio) + (reg * 4));
        else {
@@ -642,6 +652,46 @@ bool amdgpu_card_posted(struct amdgpu_device *adev)
 
 }
 
+static bool amdgpu_vpost_needed(struct amdgpu_device *adev)
+{
+       if (amdgpu_sriov_vf(adev))
+               return false;
+
+       if (amdgpu_passthrough(adev)) {
+               /* for FIJI: In whole GPU pass-through virtualization case
+                * old smc fw won't clear some registers (e.g. MEM_SIZE, BIOS_SCRATCH)
+                * so amdgpu_card_posted return false and driver will incorrectly skip vPost.
+                * but if we force vPost do in pass-through case, the driver reload will hang.
+                * whether doing vPost depends on amdgpu_card_posted if smc version is above
+                * 00160e00 for FIJI.
+                */
+               if (adev->asic_type == CHIP_FIJI) {
+                       int err;
+                       uint32_t fw_ver;
+                       err = request_firmware(&adev->pm.fw, "amdgpu/fiji_smc.bin", adev->dev);
+                       /* force vPost if error occured */
+                       if (err)
+                               return true;
+
+                       fw_ver = *((uint32_t *)adev->pm.fw->data + 69);
+                       if (fw_ver >= 0x00160e00)
+                               return !amdgpu_card_posted(adev);
+               }
+       } else {
+               /* in bare-metal case, amdgpu_card_posted return false
+                * after system reboot/boot, and return true if driver
+                * reloaded.
+                * we shouldn't do vPost after driver reload otherwise GPU
+                * could hang.
+                */
+               if (amdgpu_card_posted(adev))
+                       return false;
+       }
+
+       /* we assume vPost is neede for all other cases */
+       return true;
+}
+
 /**
  * amdgpu_dummy_page_init - init dummy page used by the driver
  *
@@ -1026,7 +1076,7 @@ static void amdgpu_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
                /* don't suspend or resume card normally */
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 
-               amdgpu_resume_kms(dev, true, true);
+               amdgpu_device_resume(dev, true, true);
 
                dev->pdev->d3_delay = d3_delay;
 
@@ -1036,7 +1086,7 @@ static void amdgpu_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
                printk(KERN_INFO "amdgpu: switched off\n");
                drm_kms_helper_poll_disable(dev);
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
-               amdgpu_suspend_kms(dev, true, true);
+               amdgpu_device_suspend(dev, true, true);
                dev->switch_power_state = DRM_SWITCH_POWER_OFF;
        }
 }
@@ -1181,10 +1231,38 @@ int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev,
        return 1;
 }
 
+static void amdgpu_whether_enable_virtual_display(struct amdgpu_device *adev)
+{
+       adev->enable_virtual_display = false;
+
+       if (amdgpu_virtual_display) {
+               struct drm_device *ddev = adev->ddev;
+               const char *pci_address_name = pci_name(ddev->pdev);
+               char *pciaddstr, *pciaddstr_tmp, *pciaddname;
+
+               pciaddstr = kstrdup(amdgpu_virtual_display, GFP_KERNEL);
+               pciaddstr_tmp = pciaddstr;
+               while ((pciaddname = strsep(&pciaddstr_tmp, ";"))) {
+                       if (!strcmp(pci_address_name, pciaddname)) {
+                               adev->enable_virtual_display = true;
+                               break;
+                       }
+               }
+
+               DRM_INFO("virtual display string:%s, %s:virtual_display:%d\n",
+                                amdgpu_virtual_display, pci_address_name,
+                                adev->enable_virtual_display);
+
+               kfree(pciaddstr);
+       }
+}
+
 static int amdgpu_early_init(struct amdgpu_device *adev)
 {
        int i, r;
 
+       amdgpu_whether_enable_virtual_display(adev);
+
        switch (adev->asic_type) {
        case CHIP_TOPAZ:
        case CHIP_TONGA:
@@ -1202,6 +1280,18 @@ static int amdgpu_early_init(struct amdgpu_device *adev)
                if (r)
                        return r;
                break;
+#ifdef CONFIG_DRM_AMDGPU_SI
+       case CHIP_VERDE:
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+       case CHIP_OLAND:
+       case CHIP_HAINAN:
+               adev->family = AMDGPU_FAMILY_SI;
+               r = si_set_ip_blocks(adev);
+               if (r)
+                       return r;
+               break;
+#endif
 #ifdef CONFIG_DRM_AMDGPU_CIK
        case CHIP_BONAIRE:
        case CHIP_HAWAII:
@@ -1318,6 +1408,9 @@ static int amdgpu_late_init(struct amdgpu_device *adev)
        for (i = 0; i < adev->num_ip_blocks; i++) {
                if (!adev->ip_block_status[i].valid)
                        continue;
+               if (adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_UVD ||
+                       adev->ip_blocks[i].type == AMD_IP_BLOCK_TYPE_VCE)
+                       continue;
                /* enable clockgating to save power */
                r = adev->ip_blocks[i].funcs->set_clockgating_state((void *)adev,
                                                                    AMD_CG_STATE_GATE);
@@ -1331,6 +1424,7 @@ static int amdgpu_late_init(struct amdgpu_device *adev)
                                DRM_ERROR("late_init of IP block <%s> failed %d\n", adev->ip_blocks[i].funcs->name, r);
                                return r;
                        }
+                       adev->ip_block_status[i].late_initialized = true;
                }
        }
 
@@ -1376,8 +1470,11 @@ static int amdgpu_fini(struct amdgpu_device *adev)
        }
 
        for (i = adev->num_ip_blocks - 1; i >= 0; i--) {
+               if (!adev->ip_block_status[i].late_initialized)
+                       continue;
                if (adev->ip_blocks[i].funcs->late_fini)
                        adev->ip_blocks[i].funcs->late_fini((void *)adev);
+               adev->ip_block_status[i].late_initialized = false;
        }
 
        return 0;
@@ -1433,13 +1530,10 @@ static int amdgpu_resume(struct amdgpu_device *adev)
        return 0;
 }
 
-static bool amdgpu_device_is_virtual(void)
+static void amdgpu_device_detect_sriov_bios(struct amdgpu_device *adev)
 {
-#ifdef CONFIG_X86
-       return boot_cpu_has(X86_FEATURE_HYPERVISOR);
-#else
-       return false;
-#endif
+       if (amdgpu_atombios_has_gpu_virtualization_table(adev))
+               adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_SRIOV_VBIOS;
 }
 
 /**
@@ -1461,6 +1555,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
 {
        int r, i;
        bool runtime = false;
+       u32 max_MBps;
 
        adev->shutdown = false;
        adev->dev = &pdev->dev;
@@ -1484,6 +1579,8 @@ int amdgpu_device_init(struct amdgpu_device *adev,
        adev->smc_wreg = &amdgpu_invalid_wreg;
        adev->pcie_rreg = &amdgpu_invalid_rreg;
        adev->pcie_wreg = &amdgpu_invalid_wreg;
+       adev->pciep_rreg = &amdgpu_invalid_rreg;
+       adev->pciep_wreg = &amdgpu_invalid_wreg;
        adev->uvd_ctx_rreg = &amdgpu_invalid_rreg;
        adev->uvd_ctx_wreg = &amdgpu_invalid_wreg;
        adev->didt_rreg = &amdgpu_invalid_rreg;
@@ -1520,9 +1617,22 @@ int amdgpu_device_init(struct amdgpu_device *adev,
        spin_lock_init(&adev->didt_idx_lock);
        spin_lock_init(&adev->gc_cac_idx_lock);
        spin_lock_init(&adev->audio_endpt_idx_lock);
+       spin_lock_init(&adev->mm_stats.lock);
+
+       INIT_LIST_HEAD(&adev->shadow_list);
+       mutex_init(&adev->shadow_list_lock);
+
+       INIT_LIST_HEAD(&adev->gtt_list);
+       spin_lock_init(&adev->gtt_list_lock);
+
+       if (adev->asic_type >= CHIP_BONAIRE) {
+               adev->rmmio_base = pci_resource_start(adev->pdev, 5);
+               adev->rmmio_size = pci_resource_len(adev->pdev, 5);
+       } else {
+               adev->rmmio_base = pci_resource_start(adev->pdev, 2);
+               adev->rmmio_size = pci_resource_len(adev->pdev, 2);
+       }
 
-       adev->rmmio_base = pci_resource_start(adev->pdev, 5);
-       adev->rmmio_size = pci_resource_len(adev->pdev, 5);
        adev->rmmio = ioremap(adev->rmmio_base, adev->rmmio_size);
        if (adev->rmmio == NULL) {
                return -ENOMEM;
@@ -1530,8 +1640,9 @@ int amdgpu_device_init(struct amdgpu_device *adev,
        DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)adev->rmmio_base);
        DRM_INFO("register mmio size: %u\n", (unsigned)adev->rmmio_size);
 
-       /* doorbell bar mapping */
-       amdgpu_doorbell_init(adev);
+       if (adev->asic_type >= CHIP_BONAIRE)
+               /* doorbell bar mapping */
+               amdgpu_doorbell_init(adev);
 
        /* io port mapping */
        for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
@@ -1579,25 +1690,24 @@ int amdgpu_device_init(struct amdgpu_device *adev,
                goto failed;
        }
 
-       /* See if the asic supports SR-IOV */
-       adev->virtualization.supports_sr_iov =
-               amdgpu_atombios_has_gpu_virtualization_table(adev);
-
-       /* Check if we are executing in a virtualized environment */
-       adev->virtualization.is_virtual = amdgpu_device_is_virtual();
-       adev->virtualization.caps = amdgpu_asic_get_virtual_caps(adev);
+       /* detect if we are with an SRIOV vbios */
+       amdgpu_device_detect_sriov_bios(adev);
 
        /* Post card if necessary */
-       if (!amdgpu_card_posted(adev) ||
-           (adev->virtualization.is_virtual &&
-            !(adev->virtualization.caps & AMDGPU_VIRT_CAPS_SRIOV_EN))) {
+       if (amdgpu_vpost_needed(adev)) {
                if (!adev->bios) {
-                       dev_err(adev->dev, "Card not posted and no BIOS - ignoring\n");
+                       dev_err(adev->dev, "no vBIOS found\n");
                        r = -EINVAL;
                        goto failed;
                }
-               DRM_INFO("GPU not posted. posting now...\n");
-               amdgpu_atom_asic_init(adev->mode_info.atom_context);
+               DRM_INFO("GPU posting now...\n");
+               r = amdgpu_atom_asic_init(adev->mode_info.atom_context);
+               if (r) {
+                       dev_err(adev->dev, "gpu post error!\n");
+                       goto failed;
+               }
+       } else {
+               DRM_INFO("GPU post is not needed\n");
        }
 
        /* Initialize clocks */
@@ -1628,6 +1738,14 @@ int amdgpu_device_init(struct amdgpu_device *adev,
 
        adev->accel_working = true;
 
+       /* Initialize the buffer migration limit. */
+       if (amdgpu_moverate >= 0)
+               max_MBps = amdgpu_moverate;
+       else
+               max_MBps = 8; /* Allow 8 MB/s. */
+       /* Get a log2 for easy divisions. */
+       adev->mm_stats.log2_max_MBps = ilog2(max(1u, max_MBps));
+
        amdgpu_fbdev_init(adev);
 
        r = amdgpu_ib_pool_init(adev);
@@ -1732,7 +1850,8 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
        adev->rio_mem = NULL;
        iounmap(adev->rmmio);
        adev->rmmio = NULL;
-       amdgpu_doorbell_fini(adev);
+       if (adev->asic_type >= CHIP_BONAIRE)
+               amdgpu_doorbell_fini(adev);
        amdgpu_debugfs_regs_cleanup(adev);
        amdgpu_debugfs_remove_files(adev);
 }
@@ -1742,7 +1861,7 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
  * Suspend & resume.
  */
 /**
- * amdgpu_suspend_kms - initiate device suspend
+ * amdgpu_device_suspend - initiate device suspend
  *
  * @pdev: drm dev pointer
  * @state: suspend state
@@ -1751,7 +1870,7 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
  * Returns 0 for success or an error on failure.
  * Called at driver suspend.
  */
-int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
+int amdgpu_device_suspend(struct drm_device *dev, bool suspend, bool fbcon)
 {
        struct amdgpu_device *adev;
        struct drm_crtc *crtc;
@@ -1819,6 +1938,10 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
                /* Shut down the device */
                pci_disable_device(dev->pdev);
                pci_set_power_state(dev->pdev, PCI_D3hot);
+       } else {
+               r = amdgpu_asic_reset(adev);
+               if (r)
+                       DRM_ERROR("amdgpu asic reset failed\n");
        }
 
        if (fbcon) {
@@ -1830,7 +1953,7 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
 }
 
 /**
- * amdgpu_resume_kms - initiate device resume
+ * amdgpu_device_resume - initiate device resume
  *
  * @pdev: drm dev pointer
  *
@@ -1838,7 +1961,7 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
  * Returns 0 for success or an error on failure.
  * Called at driver resume.
  */
-int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
+int amdgpu_device_resume(struct drm_device *dev, bool resume, bool fbcon)
 {
        struct drm_connector *connector;
        struct amdgpu_device *adev = dev->dev_private;
@@ -1848,22 +1971,26 @@ int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
        if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       if (fbcon) {
+       if (fbcon)
                console_lock();
-       }
+
        if (resume) {
                pci_set_power_state(dev->pdev, PCI_D0);
                pci_restore_state(dev->pdev);
-               if (pci_enable_device(dev->pdev)) {
+               r = pci_enable_device(dev->pdev);
+               if (r) {
                        if (fbcon)
                                console_unlock();
-                       return -1;
+                       return r;
                }
        }
 
        /* post card */
-       if (!amdgpu_card_posted(adev))
-               amdgpu_atom_asic_init(adev->mode_info.atom_context);
+       if (!amdgpu_card_posted(adev) || !resume) {
+               r = amdgpu_atom_asic_init(adev->mode_info.atom_context);
+               if (r)
+                       DRM_ERROR("amdgpu asic init failed\n");
+       }
 
        r = amdgpu_resume(adev);
        if (r)
@@ -1937,6 +2064,126 @@ int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
        return 0;
 }
 
+static bool amdgpu_check_soft_reset(struct amdgpu_device *adev)
+{
+       int i;
+       bool asic_hang = false;
+
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].valid)
+                       continue;
+               if (adev->ip_blocks[i].funcs->check_soft_reset)
+                       adev->ip_blocks[i].funcs->check_soft_reset(adev);
+               if (adev->ip_block_status[i].hang) {
+                       DRM_INFO("IP block:%d is hang!\n", i);
+                       asic_hang = true;
+               }
+       }
+       return asic_hang;
+}
+
+static int amdgpu_pre_soft_reset(struct amdgpu_device *adev)
+{
+       int i, r = 0;
+
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].valid)
+                       continue;
+               if (adev->ip_block_status[i].hang &&
+                   adev->ip_blocks[i].funcs->pre_soft_reset) {
+                       r = adev->ip_blocks[i].funcs->pre_soft_reset(adev);
+                       if (r)
+                               return r;
+               }
+       }
+
+       return 0;
+}
+
+static bool amdgpu_need_full_reset(struct amdgpu_device *adev)
+{
+       if (adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang ||
+           adev->ip_block_status[AMD_IP_BLOCK_TYPE_SMC].hang ||
+           adev->ip_block_status[AMD_IP_BLOCK_TYPE_ACP].hang ||
+           adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang) {
+               DRM_INFO("Some block need full reset!\n");
+               return true;
+       }
+       return false;
+}
+
+static int amdgpu_soft_reset(struct amdgpu_device *adev)
+{
+       int i, r = 0;
+
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].valid)
+                       continue;
+               if (adev->ip_block_status[i].hang &&
+                   adev->ip_blocks[i].funcs->soft_reset) {
+                       r = adev->ip_blocks[i].funcs->soft_reset(adev);
+                       if (r)
+                               return r;
+               }
+       }
+
+       return 0;
+}
+
+static int amdgpu_post_soft_reset(struct amdgpu_device *adev)
+{
+       int i, r = 0;
+
+       for (i = 0; i < adev->num_ip_blocks; i++) {
+               if (!adev->ip_block_status[i].valid)
+                       continue;
+               if (adev->ip_block_status[i].hang &&
+                   adev->ip_blocks[i].funcs->post_soft_reset)
+                       r = adev->ip_blocks[i].funcs->post_soft_reset(adev);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+bool amdgpu_need_backup(struct amdgpu_device *adev)
+{
+       if (adev->flags & AMD_IS_APU)
+               return false;
+
+       return amdgpu_lockup_timeout > 0 ? true : false;
+}
+
+static int amdgpu_recover_vram_from_shadow(struct amdgpu_device *adev,
+                                          struct amdgpu_ring *ring,
+                                          struct amdgpu_bo *bo,
+                                          struct fence **fence)
+{
+       uint32_t domain;
+       int r;
+
+       if (!bo->shadow)
+               return 0;
+
+       r = amdgpu_bo_reserve(bo, false);
+       if (r)
+               return r;
+       domain = amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type);
+       /* if bo has been evicted, then no need to recover */
+       if (domain == AMDGPU_GEM_DOMAIN_VRAM) {
+               r = amdgpu_bo_restore_from_shadow(adev, ring, bo,
+                                                NULL, fence, true);
+               if (r) {
+                       DRM_ERROR("recover page table failed!\n");
+                       goto err;
+               }
+       }
+err:
+       amdgpu_bo_unreserve(bo);
+       return r;
+}
+
 /**
  * amdgpu_gpu_reset - reset the asic
  *
@@ -1949,6 +2196,12 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev)
 {
        int i, r;
        int resched;
+       bool need_full_reset;
+
+       if (!amdgpu_check_soft_reset(adev)) {
+               DRM_INFO("No hardware hang detected. Did some blocks stall?\n");
+               return 0;
+       }
 
        atomic_inc(&adev->gpu_reset_counter);
 
@@ -1967,40 +2220,93 @@ int amdgpu_gpu_reset(struct amdgpu_device *adev)
        /* after all hw jobs are reset, hw fence is meaningless, so force_completion */
        amdgpu_fence_driver_force_completion(adev);
 
-       /* save scratch */
-       amdgpu_atombios_scratch_regs_save(adev);
-       r = amdgpu_suspend(adev);
+       need_full_reset = amdgpu_need_full_reset(adev);
 
-retry:
-       /* Disable fb access */
-       if (adev->mode_info.num_crtc) {
-               struct amdgpu_mode_mc_save save;
-               amdgpu_display_stop_mc_access(adev, &save);
-               amdgpu_wait_for_idle(adev, AMD_IP_BLOCK_TYPE_GMC);
+       if (!need_full_reset) {
+               amdgpu_pre_soft_reset(adev);
+               r = amdgpu_soft_reset(adev);
+               amdgpu_post_soft_reset(adev);
+               if (r || amdgpu_check_soft_reset(adev)) {
+                       DRM_INFO("soft reset failed, will fallback to full reset!\n");
+                       need_full_reset = true;
+               }
        }
 
-       r = amdgpu_asic_reset(adev);
-       /* post card */
-       amdgpu_atom_asic_init(adev->mode_info.atom_context);
+       if (need_full_reset) {
+               /* save scratch */
+               amdgpu_atombios_scratch_regs_save(adev);
+               r = amdgpu_suspend(adev);
 
-       if (!r) {
-               dev_info(adev->dev, "GPU reset succeeded, trying to resume\n");
-               r = amdgpu_resume(adev);
+retry:
+               /* Disable fb access */
+               if (adev->mode_info.num_crtc) {
+                       struct amdgpu_mode_mc_save save;
+                       amdgpu_display_stop_mc_access(adev, &save);
+                       amdgpu_wait_for_idle(adev, AMD_IP_BLOCK_TYPE_GMC);
+               }
+
+               r = amdgpu_asic_reset(adev);
+               /* post card */
+               amdgpu_atom_asic_init(adev->mode_info.atom_context);
+
+               if (!r) {
+                       dev_info(adev->dev, "GPU reset succeeded, trying to resume\n");
+                       r = amdgpu_resume(adev);
+               }
+               /* restore scratch */
+               amdgpu_atombios_scratch_regs_restore(adev);
        }
-       /* restore scratch */
-       amdgpu_atombios_scratch_regs_restore(adev);
        if (!r) {
+               amdgpu_irq_gpu_reset_resume_helper(adev);
+               if (need_full_reset && amdgpu_need_backup(adev)) {
+                       r = amdgpu_ttm_recover_gart(adev);
+                       if (r)
+                               DRM_ERROR("gart recovery failed!!!\n");
+               }
                r = amdgpu_ib_ring_tests(adev);
                if (r) {
                        dev_err(adev->dev, "ib ring test failed (%d).\n", r);
                        r = amdgpu_suspend(adev);
+                       need_full_reset = true;
                        goto retry;
                }
+               /**
+                * recovery vm page tables, since we cannot depend on VRAM is
+                * consistent after gpu full reset.
+                */
+               if (need_full_reset && amdgpu_need_backup(adev)) {
+                       struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
+                       struct amdgpu_bo *bo, *tmp;
+                       struct fence *fence = NULL, *next = NULL;
+
+                       DRM_INFO("recover vram bo from shadow\n");
+                       mutex_lock(&adev->shadow_list_lock);
+                       list_for_each_entry_safe(bo, tmp, &adev->shadow_list, shadow_list) {
+                               amdgpu_recover_vram_from_shadow(adev, ring, bo, &next);
+                               if (fence) {
+                                       r = fence_wait(fence, false);
+                                       if (r) {
+                                               WARN(r, "recovery from shadow isn't comleted\n");
+                                               break;
+                                       }
+                               }
 
+                               fence_put(fence);
+                               fence = next;
+                       }
+                       mutex_unlock(&adev->shadow_list_lock);
+                       if (fence) {
+                               r = fence_wait(fence, false);
+                               if (r)
+                                       WARN(r, "recovery from shadow isn't comleted\n");
+                       }
+                       fence_put(fence);
+               }
                for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
                        struct amdgpu_ring *ring = adev->rings[i];
                        if (!ring)
                                continue;
+
                        amd_sched_job_recovery(&ring->sched);
                        kthread_unpark(ring->sched.thread);
                }
@@ -2020,7 +2326,6 @@ retry:
                /* bad news, how to tell it to userspace ? */
                dev_info(adev->dev, "GPU reset failed\n");
        }
-       amdgpu_irq_gpu_reset_resume_helper(adev);
 
        return r;
 }
@@ -2178,22 +2483,26 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
        struct amdgpu_device *adev = f->f_inode->i_private;
        ssize_t result = 0;
        int r;
-       bool use_bank;
+       bool pm_pg_lock, use_bank;
        unsigned instance_bank, sh_bank, se_bank;
 
        if (size & 0x3 || *pos & 0x3)
                return -EINVAL;
 
+       /* are we reading registers for which a PG lock is necessary? */
+       pm_pg_lock = (*pos >> 23) & 1;
+
        if (*pos & (1ULL << 62)) {
                se_bank = (*pos >> 24) & 0x3FF;
                sh_bank = (*pos >> 34) & 0x3FF;
                instance_bank = (*pos >> 44) & 0x3FF;
                use_bank = 1;
-               *pos &= 0xFFFFFF;
        } else {
                use_bank = 0;
        }
 
+       *pos &= 0x3FFFF;
+
        if (use_bank) {
                if (sh_bank >= adev->gfx.config.max_sh_per_se ||
                    se_bank >= adev->gfx.config.max_shader_engines)
@@ -2203,6 +2512,9 @@ static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf,
                                        sh_bank, instance_bank);
        }
 
+       if (pm_pg_lock)
+               mutex_lock(&adev->pm.mutex);
+
        while (size) {
                uint32_t value;
 
@@ -2228,6 +2540,9 @@ end:
                mutex_unlock(&adev->grbm_idx_mutex);
        }
 
+       if (pm_pg_lock)
+               mutex_unlock(&adev->pm.mutex);
+
        return result;
 }
 
@@ -2385,7 +2700,7 @@ static ssize_t amdgpu_debugfs_regs_smc_read(struct file *f, char __user *buf,
        while (size) {
                uint32_t value;
 
-               value = RREG32_SMC(*pos >> 2);
+               value = RREG32_SMC(*pos);
                r = put_user(value, (uint32_t *)buf);
                if (r)
                        return r;
@@ -2416,7 +2731,7 @@ static ssize_t amdgpu_debugfs_regs_smc_write(struct file *f, const char __user *
                if (r)
                        return r;
 
-               WREG32_SMC(*pos >> 2, value);
+               WREG32_SMC(*pos, value);
 
                result += 4;
                buf += 4;
@@ -2438,12 +2753,12 @@ static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf,
        if (size & 0x3 || *pos & 0x3)
                return -EINVAL;
 
-       config = kmalloc(256 * sizeof(*config), GFP_KERNEL);
+       config = kmalloc_array(256, sizeof(*config), GFP_KERNEL);
        if (!config)
                return -ENOMEM;
 
        /* version, increment each time something is added */
-       config[no_regs++] = 0;
+       config[no_regs++] = 2;
        config[no_regs++] = adev->gfx.config.max_shader_engines;
        config[no_regs++] = adev->gfx.config.max_tile_pipes;
        config[no_regs++] = adev->gfx.config.max_cu_per_sh;
@@ -2468,6 +2783,15 @@ static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf,
        config[no_regs++] = adev->gfx.config.gb_addr_config;
        config[no_regs++] = adev->gfx.config.num_rbs;
 
+       /* rev==1 */
+       config[no_regs++] = adev->rev_id;
+       config[no_regs++] = adev->pg_flags;
+       config[no_regs++] = adev->cg_flags;
+
+       /* rev==2 */
+       config[no_regs++] = adev->family;
+       config[no_regs++] = adev->external_rev_id;
+
        while (size && (*pos < no_regs * 4)) {
                uint32_t value;
 
@@ -2488,6 +2812,29 @@ static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf,
        return result;
 }
 
+static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf,
+                                       size_t size, loff_t *pos)
+{
+       struct amdgpu_device *adev = f->f_inode->i_private;
+       int idx, r;
+       int32_t value;
+
+       if (size != 4 || *pos & 0x3)
+               return -EINVAL;
+
+       /* convert offset to sensor number */
+       idx = *pos >> 2;
+
+       if (adev->powerplay.pp_funcs && adev->powerplay.pp_funcs->read_sensor)
+               r = adev->powerplay.pp_funcs->read_sensor(adev->powerplay.pp_handle, idx, &value);
+       else
+               return -EINVAL;
+
+       if (!r)
+               r = put_user(value, (int32_t *)buf);
+
+       return !r ? 4 : r;
+}
 
 static const struct file_operations amdgpu_debugfs_regs_fops = {
        .owner = THIS_MODULE,
@@ -2520,12 +2867,19 @@ static const struct file_operations amdgpu_debugfs_gca_config_fops = {
        .llseek = default_llseek
 };
 
+static const struct file_operations amdgpu_debugfs_sensors_fops = {
+       .owner = THIS_MODULE,
+       .read = amdgpu_debugfs_sensor_read,
+       .llseek = default_llseek
+};
+
 static const struct file_operations *debugfs_regs[] = {
        &amdgpu_debugfs_regs_fops,
        &amdgpu_debugfs_regs_didt_fops,
        &amdgpu_debugfs_regs_pcie_fops,
        &amdgpu_debugfs_regs_smc_fops,
        &amdgpu_debugfs_gca_config_fops,
+       &amdgpu_debugfs_sensors_fops,
 };
 
 static const char *debugfs_regs_names[] = {
@@ -2534,6 +2888,7 @@ static const char *debugfs_regs_names[] = {
        "amdgpu_regs_pcie",
        "amdgpu_regs_smc",
        "amdgpu_gca_config",
+       "amdgpu_sensors",
 };
 
 static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
index 76f9602..083e2b4 100644 (file)
@@ -41,7 +41,7 @@ static void amdgpu_flip_callback(struct fence *f, struct fence_cb *cb)
                container_of(cb, struct amdgpu_flip_work, cb);
 
        fence_put(f);
-       schedule_work(&work->flip_work);
+       schedule_work(&work->flip_work.work);
 }
 
 static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
@@ -63,16 +63,17 @@ static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
 
 static void amdgpu_flip_work_func(struct work_struct *__work)
 {
+       struct delayed_work *delayed_work =
+               container_of(__work, struct delayed_work, work);
        struct amdgpu_flip_work *work =
-               container_of(__work, struct amdgpu_flip_work, flip_work);
+               container_of(delayed_work, struct amdgpu_flip_work, flip_work);
        struct amdgpu_device *adev = work->adev;
        struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id];
 
        struct drm_crtc *crtc = &amdgpuCrtc->base;
        unsigned long flags;
-       unsigned i, repcnt = 4;
-       int vpos, hpos, stat, min_udelay = 0;
-       struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
+       unsigned i;
+       int vpos, hpos;
 
        if (amdgpu_flip_handle_fence(work, &work->excl))
                return;
@@ -81,55 +82,23 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
                if (amdgpu_flip_handle_fence(work, &work->shared[i]))
                        return;
 
-       /* We borrow the event spin lock for protecting flip_status */
-       spin_lock_irqsave(&crtc->dev->event_lock, flags);
-
-       /* If this happens to execute within the "virtually extended" vblank
-        * interval before the start of the real vblank interval then it needs
-        * to delay programming the mmio flip until the real vblank is entered.
-        * This prevents completing a flip too early due to the way we fudge
-        * our vblank counter and vblank timestamps in order to work around the
-        * problem that the hw fires vblank interrupts before actual start of
-        * vblank (when line buffer refilling is done for a frame). It
-        * complements the fudging logic in amdgpu_get_crtc_scanoutpos() for
-        * timestamping and amdgpu_get_vblank_counter_kms() for vblank counts.
-        *
-        * In practice this won't execute very often unless on very fast
-        * machines because the time window for this to happen is very small.
+       /* Wait until we're out of the vertical blank period before the one
+        * targeted by the flip
         */
-       while (amdgpuCrtc->enabled && --repcnt) {
-               /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
-                * start in hpos, and to the "fudged earlier" vblank start in
-                * vpos.
-                */
-               stat = amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id,
-                                                 GET_DISTANCE_TO_VBLANKSTART,
-                                                 &vpos, &hpos, NULL, NULL,
-                                                 &crtc->hwmode);
-
-               if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
-                   (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) ||
-                   !(vpos >= 0 && hpos <= 0))
-                       break;
-
-               /* Sleep at least until estimated real start of hw vblank */
-               min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
-               if (min_udelay > vblank->framedur_ns / 2000) {
-                       /* Don't wait ridiculously long - something is wrong */
-                       repcnt = 0;
-                       break;
-               }
-               spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-               usleep_range(min_udelay, 2 * min_udelay);
-               spin_lock_irqsave(&crtc->dev->event_lock, flags);
+       if (amdgpuCrtc->enabled &&
+           (amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, 0,
+                                       &vpos, &hpos, NULL, NULL,
+                                       &crtc->hwmode)
+            & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
+           (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
+           (int)(work->target_vblank -
+                 amdgpu_get_vblank_counter_kms(adev->ddev, amdgpuCrtc->crtc_id)) > 0) {
+               schedule_delayed_work(&work->flip_work, usecs_to_jiffies(1000));
+               return;
        }
 
-       if (!repcnt)
-               DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, "
-                                "framedur %d, linedur %d, stat %d, vpos %d, "
-                                "hpos %d\n", work->crtc_id, min_udelay,
-                                vblank->framedur_ns / 1000,
-                                vblank->linedur_ns / 1000, stat, vpos, hpos);
+       /* We borrow the event spin lock for protecting flip_status */
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
 
        /* Do the flip (mmio) */
        adev->mode_info.funcs->page_flip(adev, work->crtc_id, work->base, work->async);
@@ -154,25 +123,25 @@ static void amdgpu_unpin_work_func(struct work_struct *__work)
        int r;
 
        /* unpin of the old buffer */
-       r = amdgpu_bo_reserve(work->old_rbo, false);
+       r = amdgpu_bo_reserve(work->old_abo, false);
        if (likely(r == 0)) {
-               r = amdgpu_bo_unpin(work->old_rbo);
+               r = amdgpu_bo_unpin(work->old_abo);
                if (unlikely(r != 0)) {
                        DRM_ERROR("failed to unpin buffer after flip\n");
                }
-               amdgpu_bo_unreserve(work->old_rbo);
+               amdgpu_bo_unreserve(work->old_abo);
        } else
                DRM_ERROR("failed to reserve buffer after flip\n");
 
-       amdgpu_bo_unref(&work->old_rbo);
+       amdgpu_bo_unref(&work->old_abo);
        kfree(work->shared);
        kfree(work);
 }
 
-int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
-                         struct drm_framebuffer *fb,
-                         struct drm_pending_vblank_event *event,
-                         uint32_t page_flip_flags)
+int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb,
+                                struct drm_pending_vblank_event *event,
+                                uint32_t page_flip_flags, uint32_t target)
 {
        struct drm_device *dev = crtc->dev;
        struct amdgpu_device *adev = dev->dev_private;
@@ -181,7 +150,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
        struct amdgpu_framebuffer *new_amdgpu_fb;
        struct drm_gem_object *obj;
        struct amdgpu_flip_work *work;
-       struct amdgpu_bo *new_rbo;
+       struct amdgpu_bo *new_abo;
        unsigned long flags;
        u64 tiling_flags;
        u64 base;
@@ -191,7 +160,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
        if (work == NULL)
                return -ENOMEM;
 
-       INIT_WORK(&work->flip_work, amdgpu_flip_work_func);
+       INIT_DELAYED_WORK(&work->flip_work, amdgpu_flip_work_func);
        INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func);
 
        work->event = event;
@@ -204,28 +173,28 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
        obj = old_amdgpu_fb->obj;
 
        /* take a reference to the old object */
-       work->old_rbo = gem_to_amdgpu_bo(obj);
-       amdgpu_bo_ref(work->old_rbo);
+       work->old_abo = gem_to_amdgpu_bo(obj);
+       amdgpu_bo_ref(work->old_abo);
 
        new_amdgpu_fb = to_amdgpu_framebuffer(fb);
        obj = new_amdgpu_fb->obj;
-       new_rbo = gem_to_amdgpu_bo(obj);
+       new_abo = gem_to_amdgpu_bo(obj);
 
        /* pin the new buffer */
-       r = amdgpu_bo_reserve(new_rbo, false);
+       r = amdgpu_bo_reserve(new_abo, false);
        if (unlikely(r != 0)) {
-               DRM_ERROR("failed to reserve new rbo buffer before flip\n");
+               DRM_ERROR("failed to reserve new abo buffer before flip\n");
                goto cleanup;
        }
 
-       r = amdgpu_bo_pin_restricted(new_rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, &base);
+       r = amdgpu_bo_pin_restricted(new_abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, &base);
        if (unlikely(r != 0)) {
                r = -EINVAL;
-               DRM_ERROR("failed to pin new rbo buffer before flip\n");
+               DRM_ERROR("failed to pin new abo buffer before flip\n");
                goto unreserve;
        }
 
-       r = reservation_object_get_fences_rcu(new_rbo->tbo.resv, &work->excl,
+       r = reservation_object_get_fences_rcu(new_abo->tbo.resv, &work->excl,
                                              &work->shared_count,
                                              &work->shared);
        if (unlikely(r != 0)) {
@@ -233,16 +202,12 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
                goto unpin;
        }
 
-       amdgpu_bo_get_tiling_flags(new_rbo, &tiling_flags);
-       amdgpu_bo_unreserve(new_rbo);
+       amdgpu_bo_get_tiling_flags(new_abo, &tiling_flags);
+       amdgpu_bo_unreserve(new_abo);
 
        work->base = base;
-
-       r = drm_crtc_vblank_get(crtc);
-       if (r) {
-               DRM_ERROR("failed to get vblank before flip\n");
-               goto pflip_cleanup;
-       }
+       work->target_vblank = target - drm_crtc_vblank_count(crtc) +
+               amdgpu_get_vblank_counter_kms(dev, work->crtc_id);
 
        /* we borrow the event spin lock for protecting flip_wrok */
        spin_lock_irqsave(&crtc->dev->event_lock, flags);
@@ -250,7 +215,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
                DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
                r = -EBUSY;
-               goto vblank_cleanup;
+               goto pflip_cleanup;
        }
 
        amdgpu_crtc->pflip_status = AMDGPU_FLIP_PENDING;
@@ -262,26 +227,23 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
        /* update crtc fb */
        crtc->primary->fb = fb;
        spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-       amdgpu_flip_work_func(&work->flip_work);
+       amdgpu_flip_work_func(&work->flip_work.work);
        return 0;
 
-vblank_cleanup:
-       drm_crtc_vblank_put(crtc);
-
 pflip_cleanup:
-       if (unlikely(amdgpu_bo_reserve(new_rbo, false) != 0)) {
-               DRM_ERROR("failed to reserve new rbo in error path\n");
+       if (unlikely(amdgpu_bo_reserve(new_abo, false) != 0)) {
+               DRM_ERROR("failed to reserve new abo in error path\n");
                goto cleanup;
        }
 unpin:
-       if (unlikely(amdgpu_bo_unpin(new_rbo) != 0)) {
-               DRM_ERROR("failed to unpin new rbo in error path\n");
+       if (unlikely(amdgpu_bo_unpin(new_abo) != 0)) {
+               DRM_ERROR("failed to unpin new abo in error path\n");
        }
 unreserve:
-       amdgpu_bo_unreserve(new_rbo);
+       amdgpu_bo_unreserve(new_abo);
 
 cleanup:
-       amdgpu_bo_unref(&work->old_rbo);
+       amdgpu_bo_unref(&work->old_abo);
        fence_put(work->excl);
        for (i = 0; i < work->shared_count; ++i)
                fence_put(work->shared[i]);
@@ -335,7 +297,7 @@ int amdgpu_crtc_set_config(struct drm_mode_set *set)
        return ret;
 }
 
-static const char *encoder_names[38] = {
+static const char *encoder_names[41] = {
        "NONE",
        "INTERNAL_LVDS",
        "INTERNAL_TMDS1",
@@ -374,6 +336,9 @@ static const char *encoder_names[38] = {
        "TRAVIS",
        "INTERNAL_VCE",
        "INTERNAL_UNIPHY3",
+       "HDMI_ANX9805",
+       "INTERNAL_AMCLK",
+       "VIRTUAL",
 };
 
 static const char *hpd_names[6] = {
index 9aa533c..71ed27e 100644 (file)
  * - 3.2.0 - GFX8: Uses EOP_TC_WB_ACTION_EN, so UMDs don't have to do the same
  *           at the end of IBs.
  * - 3.3.0 - Add VM support for UVD on supported hardware.
+ * - 3.4.0 - Add AMDGPU_INFO_NUM_EVICTIONS.
+ * - 3.5.0 - Add support for new UVD_NO_OP register.
+ * - 3.6.0 - kmd involves use CONTEXT_CONTROL in ring buffer.
+ * - 3.7.0 - Add support for VCE clock list packet
+ * - 3.8.0 - Add support raster config init in the kernel
  */
 #define KMS_DRIVER_MAJOR       3
-#define KMS_DRIVER_MINOR       3
+#define KMS_DRIVER_MINOR       8
 #define KMS_DRIVER_PATCHLEVEL  0
 
 int amdgpu_vram_limit = 0;
 int amdgpu_gart_size = -1; /* auto */
+int amdgpu_moverate = -1; /* auto */
 int amdgpu_benchmarking = 0;
 int amdgpu_testing = 0;
 int amdgpu_audio = -1;
@@ -84,11 +90,14 @@ int amdgpu_sched_jobs = 32;
 int amdgpu_sched_hw_submission = 2;
 int amdgpu_powerplay = -1;
 int amdgpu_powercontainment = 1;
+int amdgpu_sclk_deep_sleep_en = 1;
 unsigned amdgpu_pcie_gen_cap = 0;
 unsigned amdgpu_pcie_lane_cap = 0;
 unsigned amdgpu_cg_mask = 0xffffffff;
 unsigned amdgpu_pg_mask = 0xffffffff;
 char *amdgpu_disable_cu = NULL;
+char *amdgpu_virtual_display = NULL;
+unsigned amdgpu_pp_feature_mask = 0xffffffff;
 
 MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes");
 module_param_named(vramlimit, amdgpu_vram_limit, int, 0600);
@@ -96,6 +105,9 @@ module_param_named(vramlimit, amdgpu_vram_limit, int, 0600);
 MODULE_PARM_DESC(gartsize, "Size of PCIE/IGP gart to setup in megabytes (32, 64, etc., -1 = auto)");
 module_param_named(gartsize, amdgpu_gart_size, int, 0600);
 
+MODULE_PARM_DESC(moverate, "Maximum buffer migration rate in MB/s. (32, 64, etc., -1=auto, 0=1=disabled)");
+module_param_named(moverate, amdgpu_moverate, int, 0600);
+
 MODULE_PARM_DESC(benchmark, "Run benchmark");
 module_param_named(benchmark, amdgpu_benchmarking, int, 0444);
 
@@ -162,13 +174,17 @@ module_param_named(sched_jobs, amdgpu_sched_jobs, int, 0444);
 MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default 2)");
 module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444);
 
-#ifdef CONFIG_DRM_AMD_POWERPLAY
 MODULE_PARM_DESC(powerplay, "Powerplay component (1 = enable, 0 = disable, -1 = auto (default))");
 module_param_named(powerplay, amdgpu_powerplay, int, 0444);
 
 MODULE_PARM_DESC(powercontainment, "Power Containment (1 = enable (default), 0 = disable)");
 module_param_named(powercontainment, amdgpu_powercontainment, int, 0444);
-#endif
+
+MODULE_PARM_DESC(ppfeaturemask, "all power features enabled (default))");
+module_param_named(ppfeaturemask, amdgpu_pp_feature_mask, int, 0444);
+
+MODULE_PARM_DESC(sclkdeepsleep, "SCLK Deep Sleep (1 = enable (default), 0 = disable)");
+module_param_named(sclkdeepsleep, amdgpu_sclk_deep_sleep_en, int, 0444);
 
 MODULE_PARM_DESC(pcie_gen_cap, "PCIE Gen Caps (0: autodetect (default))");
 module_param_named(pcie_gen_cap, amdgpu_pcie_gen_cap, uint, 0444);
@@ -185,7 +201,84 @@ module_param_named(pg_mask, amdgpu_pg_mask, uint, 0444);
 MODULE_PARM_DESC(disable_cu, "Disable CUs (se.sh.cu,...)");
 module_param_named(disable_cu, amdgpu_disable_cu, charp, 0444);
 
+MODULE_PARM_DESC(virtual_display, "Enable virtual display feature (the virtual_display will be set like xxxx:xx:xx.x;xxxx:xx:xx.x)");
+module_param_named(virtual_display, amdgpu_virtual_display, charp, 0444);
+
 static const struct pci_device_id pciidlist[] = {
+#ifdef  CONFIG_DRM_AMDGPU_SI
+       {0x1002, 0x6780, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6784, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6788, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x678A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6790, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6791, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6792, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6798, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6799, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x679A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x679B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x679E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x679F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TAHITI},
+       {0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY},
+       {0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY},
+       {0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|AMD_IS_MOBILITY},
+       {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6811, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN},
+       {0x1002, 0x6600, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6601, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6602, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6603, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6604, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6605, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6606, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6607, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6608, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND},
+       {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND},
+       {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND},
+       {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND},
+       {0x1002, 0x6617, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|AMD_IS_MOBILITY},
+       {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND},
+       {0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6821, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6822, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6823, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6824, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6825, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6826, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6827, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6828, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x6829, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x682A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x682B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x682C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x682D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x682F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|AMD_IS_MOBILITY},
+       {0x1002, 0x6835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x6837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x6838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x6839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x683B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x683D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x683F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE},
+       {0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+       {0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+       {0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+       {0x1002, 0x6665, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+       {0x1002, 0x6667, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+       {0x1002, 0x666F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|AMD_IS_MOBILITY},
+#endif
 #ifdef CONFIG_DRM_AMDGPU_CIK
        /* Kaveri */
        {0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
@@ -341,7 +434,7 @@ static int amdgpu_kick_out_firmware_fb(struct pci_dev *pdev)
 #ifdef CONFIG_X86
        primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 #endif
-       remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "amdgpudrmfb", primary);
        kfree(ap);
 
        return 0;
@@ -383,32 +476,70 @@ amdgpu_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
+static void
+amdgpu_pci_shutdown(struct pci_dev *pdev)
+{
+       /* if we are running in a VM, make sure the device
+        * torn down properly on reboot/shutdown.
+        * unfortunately we can't detect certain
+        * hypervisors so just do this all the time.
+        */
+       amdgpu_pci_remove(pdev);
+}
+
 static int amdgpu_pmops_suspend(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
-       return amdgpu_suspend_kms(drm_dev, true, true);
+       return amdgpu_device_suspend(drm_dev, true, true);
 }
 
 static int amdgpu_pmops_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
-       return amdgpu_resume_kms(drm_dev, true, true);
+
+       /* GPU comes up enabled by the bios on resume */
+       if (amdgpu_device_is_px(drm_dev)) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
+
+       return amdgpu_device_resume(drm_dev, true, true);
 }
 
 static int amdgpu_pmops_freeze(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
-       return amdgpu_suspend_kms(drm_dev, false, true);
+       return amdgpu_device_suspend(drm_dev, false, true);
 }
 
 static int amdgpu_pmops_thaw(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
+
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return amdgpu_device_resume(drm_dev, false, true);
+}
+
+static int amdgpu_pmops_poweroff(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       return amdgpu_device_suspend(drm_dev, true, true);
+}
+
+static int amdgpu_pmops_restore(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
-       return amdgpu_resume_kms(drm_dev, false, true);
+       return amdgpu_device_resume(drm_dev, false, true);
 }
 
 static int amdgpu_pmops_runtime_suspend(struct device *dev)
@@ -426,7 +557,7 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
        drm_kms_helper_poll_disable(drm_dev);
        vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
 
-       ret = amdgpu_suspend_kms(drm_dev, false, false);
+       ret = amdgpu_device_suspend(drm_dev, false, false);
        pci_save_state(pdev);
        pci_disable_device(pdev);
        pci_ignore_hotplug(pdev);
@@ -459,7 +590,7 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
                return ret;
        pci_set_master(pdev);
 
-       ret = amdgpu_resume_kms(drm_dev, false, false);
+       ret = amdgpu_device_resume(drm_dev, false, false);
        drm_kms_helper_poll_enable(drm_dev);
        vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
        drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
@@ -513,8 +644,8 @@ static const struct dev_pm_ops amdgpu_pm_ops = {
        .resume = amdgpu_pmops_resume,
        .freeze = amdgpu_pmops_freeze,
        .thaw = amdgpu_pmops_thaw,
-       .poweroff = amdgpu_pmops_freeze,
-       .restore = amdgpu_pmops_resume,
+       .poweroff = amdgpu_pmops_poweroff,
+       .restore = amdgpu_pmops_restore,
        .runtime_suspend = amdgpu_pmops_runtime_suspend,
        .runtime_resume = amdgpu_pmops_runtime_resume,
        .runtime_idle = amdgpu_pmops_runtime_idle,
@@ -596,6 +727,7 @@ static struct pci_driver amdgpu_kms_pci_driver = {
        .id_table = pciidlist,
        .probe = amdgpu_pci_probe,
        .remove = amdgpu_pci_remove,
+       .shutdown = amdgpu_pci_shutdown,
        .driver.pm = &amdgpu_pm_ops,
 };
 
index 9191467..9fb8aa4 100644 (file)
@@ -25,7 +25,7 @@
  */
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/fb.h>
+#include <linux/pm_runtime.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -48,8 +48,35 @@ struct amdgpu_fbdev {
        struct amdgpu_device *adev;
 };
 
+static int
+amdgpufb_open(struct fb_info *info, int user)
+{
+       struct amdgpu_fbdev *rfbdev = info->par;
+       struct amdgpu_device *adev = rfbdev->adev;
+       int ret = pm_runtime_get_sync(adev->ddev->dev);
+       if (ret < 0 && ret != -EACCES) {
+               pm_runtime_mark_last_busy(adev->ddev->dev);
+               pm_runtime_put_autosuspend(adev->ddev->dev);
+               return ret;
+       }
+       return 0;
+}
+
+static int
+amdgpufb_release(struct fb_info *info, int user)
+{
+       struct amdgpu_fbdev *rfbdev = info->par;
+       struct amdgpu_device *adev = rfbdev->adev;
+
+       pm_runtime_mark_last_busy(adev->ddev->dev);
+       pm_runtime_put_autosuspend(adev->ddev->dev);
+       return 0;
+}
+
 static struct fb_ops amdgpufb_ops = {
        .owner = THIS_MODULE,
+       .fb_open = amdgpufb_open,
+       .fb_release = amdgpufb_release,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
        .fb_fillrect = drm_fb_helper_cfb_fillrect,
@@ -88,14 +115,14 @@ int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tile
 
 static void amdgpufb_destroy_pinned_object(struct drm_gem_object *gobj)
 {
-       struct amdgpu_bo *rbo = gem_to_amdgpu_bo(gobj);
+       struct amdgpu_bo *abo = gem_to_amdgpu_bo(gobj);
        int ret;
 
-       ret = amdgpu_bo_reserve(rbo, false);
+       ret = amdgpu_bo_reserve(abo, false);
        if (likely(ret == 0)) {
-               amdgpu_bo_kunmap(rbo);
-               amdgpu_bo_unpin(rbo);
-               amdgpu_bo_unreserve(rbo);
+               amdgpu_bo_kunmap(abo);
+               amdgpu_bo_unpin(abo);
+               amdgpu_bo_unreserve(abo);
        }
        drm_gem_object_unreference_unlocked(gobj);
 }
@@ -106,7 +133,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev,
 {
        struct amdgpu_device *adev = rfbdev->adev;
        struct drm_gem_object *gobj = NULL;
-       struct amdgpu_bo *rbo = NULL;
+       struct amdgpu_bo *abo = NULL;
        bool fb_tiled = false; /* useful for testing */
        u32 tiling_flags = 0;
        int ret;
@@ -132,30 +159,30 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev,
                       aligned_size);
                return -ENOMEM;
        }
-       rbo = gem_to_amdgpu_bo(gobj);
+       abo = gem_to_amdgpu_bo(gobj);
 
        if (fb_tiled)
                tiling_flags = AMDGPU_TILING_SET(ARRAY_MODE, GRPH_ARRAY_2D_TILED_THIN1);
 
-       ret = amdgpu_bo_reserve(rbo, false);
+       ret = amdgpu_bo_reserve(abo, false);
        if (unlikely(ret != 0))
                goto out_unref;
 
        if (tiling_flags) {
-               ret = amdgpu_bo_set_tiling_flags(rbo,
+               ret = amdgpu_bo_set_tiling_flags(abo,
                                                 tiling_flags);
                if (ret)
                        dev_err(adev->dev, "FB failed to set tiling flags\n");
        }
 
 
-       ret = amdgpu_bo_pin_restricted(rbo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL);
+       ret = amdgpu_bo_pin_restricted(abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL);
        if (ret) {
-               amdgpu_bo_unreserve(rbo);
+               amdgpu_bo_unreserve(abo);
                goto out_unref;
        }
-       ret = amdgpu_bo_kmap(rbo, NULL);
-       amdgpu_bo_unreserve(rbo);
+       ret = amdgpu_bo_kmap(abo, NULL);
+       amdgpu_bo_unreserve(abo);
        if (ret) {
                goto out_unref;
        }
@@ -177,7 +204,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
        struct drm_framebuffer *fb = NULL;
        struct drm_mode_fb_cmd2 mode_cmd;
        struct drm_gem_object *gobj = NULL;
-       struct amdgpu_bo *rbo = NULL;
+       struct amdgpu_bo *abo = NULL;
        int ret;
        unsigned long tmp;
 
@@ -196,7 +223,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
                return ret;
        }
 
-       rbo = gem_to_amdgpu_bo(gobj);
+       abo = gem_to_amdgpu_bo(gobj);
 
        /* okay we have an object now allocate the framebuffer */
        info = drm_fb_helper_alloc_fbi(helper);
@@ -219,7 +246,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
        /* setup helper */
        rfbdev->helper.fb = fb;
 
-       memset_io(rbo->kptr, 0x0, amdgpu_bo_size(rbo));
+       memset_io(abo->kptr, 0x0, amdgpu_bo_size(abo));
 
        strcpy(info->fix.id, "amdgpudrmfb");
 
@@ -228,11 +255,11 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
        info->fbops = &amdgpufb_ops;
 
-       tmp = amdgpu_bo_gpu_offset(rbo) - adev->mc.vram_start;
+       tmp = amdgpu_bo_gpu_offset(abo) - adev->mc.vram_start;
        info->fix.smem_start = adev->mc.aper_base + tmp;
-       info->fix.smem_len = amdgpu_bo_size(rbo);
-       info->screen_base = rbo->kptr;
-       info->screen_size = amdgpu_bo_size(rbo);
+       info->fix.smem_len = amdgpu_bo_size(abo);
+       info->screen_base = abo->kptr;
+       info->screen_size = amdgpu_bo_size(abo);
 
        drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
 
@@ -249,7 +276,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
 
        DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
        DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)adev->mc.aper_base);
-       DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(rbo));
+       DRM_INFO("size %lu\n", (unsigned long)amdgpu_bo_size(abo));
        DRM_INFO("fb depth is %d\n", fb->depth);
        DRM_INFO("   pitch is %d\n", fb->pitches[0]);
 
@@ -259,7 +286,7 @@ static int amdgpufb_create(struct drm_fb_helper *helper,
 out_destroy_fbi:
        drm_fb_helper_release_fbi(helper);
 out_unref:
-       if (rbo) {
+       if (abo) {
 
        }
        if (fb && ret) {
index 0b109ae..3a2e42f 100644 (file)
@@ -454,6 +454,7 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev)
                for (j = 0; j <= ring->fence_drv.num_fences_mask; ++j)
                        fence_put(ring->fence_drv.fences[j]);
                kfree(ring->fence_drv.fences);
+               ring->fence_drv.fences = NULL;
                ring->fence_drv.initialized = false;
        }
 }
index 0feea34..21a1242 100644 (file)
@@ -238,7 +238,7 @@ void amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
        t = offset / AMDGPU_GPU_PAGE_SIZE;
        p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE);
        for (i = 0; i < pages; i++, p++) {
-#ifdef CONFIG_AMDGPU_GART_DEBUGFS
+#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
                adev->gart.pages[p] = NULL;
 #endif
                page_base = adev->dummy_page.addr;
@@ -286,7 +286,7 @@ int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
        p = t / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE);
 
        for (i = 0; i < pages; i++, p++) {
-#ifdef CONFIG_AMDGPU_GART_DEBUGFS
+#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
                adev->gart.pages[p] = pagelist[i];
 #endif
                if (adev->gart.ptr) {
@@ -331,7 +331,7 @@ int amdgpu_gart_init(struct amdgpu_device *adev)
        DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n",
                 adev->gart.num_cpu_pages, adev->gart.num_gpu_pages);
 
-#ifdef CONFIG_AMDGPU_GART_DEBUGFS
+#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
        /* Allocate pages table */
        adev->gart.pages = vzalloc(sizeof(void *) * adev->gart.num_cpu_pages);
        if (adev->gart.pages == NULL) {
@@ -357,7 +357,7 @@ void amdgpu_gart_fini(struct amdgpu_device *adev)
                amdgpu_gart_unbind(adev, 0, adev->gart.num_cpu_pages);
        }
        adev->gart.ready = false;
-#ifdef CONFIG_AMDGPU_GART_DEBUGFS
+#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
        vfree(adev->gart.pages);
        adev->gart.pages = NULL;
 #endif
index 503d540..e73728d 100644 (file)
 #define AMDGPU_GWS_SHIFT       PAGE_SHIFT
 #define AMDGPU_OA_SHIFT                PAGE_SHIFT
 
-#define AMDGPU_PL_GDS          TTM_PL_PRIV0
-#define AMDGPU_PL_GWS          TTM_PL_PRIV1
-#define AMDGPU_PL_OA           TTM_PL_PRIV2
-
-#define AMDGPU_PL_FLAG_GDS             TTM_PL_FLAG_PRIV0
-#define AMDGPU_PL_FLAG_GWS             TTM_PL_FLAG_PRIV1
-#define AMDGPU_PL_FLAG_OA              TTM_PL_FLAG_PRIV2
-
 struct amdgpu_ring;
 struct amdgpu_bo;
 
index 88fbed2..a7ea9a3 100644 (file)
@@ -118,23 +118,23 @@ void amdgpu_gem_force_release(struct amdgpu_device *adev)
  */
 int amdgpu_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_priv)
 {
-       struct amdgpu_bo *rbo = gem_to_amdgpu_bo(obj);
-       struct amdgpu_device *adev = rbo->adev;
+       struct amdgpu_bo *abo = gem_to_amdgpu_bo(obj);
+       struct amdgpu_device *adev = abo->adev;
        struct amdgpu_fpriv *fpriv = file_priv->driver_priv;
        struct amdgpu_vm *vm = &fpriv->vm;
        struct amdgpu_bo_va *bo_va;
        int r;
-       r = amdgpu_bo_reserve(rbo, false);
+       r = amdgpu_bo_reserve(abo, false);
        if (r)
                return r;
 
-       bo_va = amdgpu_vm_bo_find(vm, rbo);
+       bo_va = amdgpu_vm_bo_find(vm, abo);
        if (!bo_va) {
-               bo_va = amdgpu_vm_bo_add(adev, vm, rbo);
+               bo_va = amdgpu_vm_bo_add(adev, vm, abo);
        } else {
                ++bo_va->ref_count;
        }
-       amdgpu_bo_unreserve(rbo);
+       amdgpu_bo_unreserve(abo);
        return 0;
 }
 
@@ -528,7 +528,7 @@ static void amdgpu_gem_va_update_vm(struct amdgpu_device *adev,
                goto error_unreserve;
 
        if (operation == AMDGPU_VA_OP_MAP)
-               r = amdgpu_vm_bo_update(adev, bo_va, &bo_va->bo->tbo.mem);
+               r = amdgpu_vm_bo_update(adev, bo_va, false);
 
 error_unreserve:
        ttm_eu_backoff_reservation(&ticket, &list);
@@ -547,7 +547,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
        struct drm_gem_object *gobj;
        struct amdgpu_device *adev = dev->dev_private;
        struct amdgpu_fpriv *fpriv = filp->driver_priv;
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        struct amdgpu_bo_va *bo_va;
        struct ttm_validate_buffer tv, tv_pd;
        struct ww_acquire_ctx ticket;
@@ -587,10 +587,10 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
        gobj = drm_gem_object_lookup(filp, args->handle);
        if (gobj == NULL)
                return -ENOENT;
-       rbo = gem_to_amdgpu_bo(gobj);
+       abo = gem_to_amdgpu_bo(gobj);
        INIT_LIST_HEAD(&list);
        INIT_LIST_HEAD(&duplicates);
-       tv.bo = &rbo->tbo;
+       tv.bo = &abo->tbo;
        tv.shared = true;
        list_add(&tv.head, &list);
 
@@ -604,7 +604,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data,
                return r;
        }
 
-       bo_va = amdgpu_vm_bo_find(&fpriv->vm, rbo);
+       bo_va = amdgpu_vm_bo_find(&fpriv->vm, abo);
        if (!bo_va) {
                ttm_eu_backoff_reservation(&ticket, &list);
                drm_gem_object_unreference_unlocked(gobj);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
new file mode 100644 (file)
index 0000000..f86c844
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2016 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: Christian König
+ */
+
+#include <drm/drmP.h>
+#include "amdgpu.h"
+
+struct amdgpu_gtt_mgr {
+       struct drm_mm mm;
+       spinlock_t lock;
+       uint64_t available;
+};
+
+/**
+ * amdgpu_gtt_mgr_init - init GTT manager and DRM MM
+ *
+ * @man: TTM memory type manager
+ * @p_size: maximum size of GTT
+ *
+ * Allocate and initialize the GTT manager.
+ */
+static int amdgpu_gtt_mgr_init(struct ttm_mem_type_manager *man,
+                              unsigned long p_size)
+{
+       struct amdgpu_gtt_mgr *mgr;
+
+       mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
+       if (!mgr)
+               return -ENOMEM;
+
+       drm_mm_init(&mgr->mm, 0, p_size);
+       spin_lock_init(&mgr->lock);
+       mgr->available = p_size;
+       man->priv = mgr;
+       return 0;
+}
+
+/**
+ * amdgpu_gtt_mgr_fini - free and destroy GTT manager
+ *
+ * @man: TTM memory type manager
+ *
+ * Destroy and free the GTT manager, returns -EBUSY if ranges are still
+ * allocated inside it.
+ */
+static int amdgpu_gtt_mgr_fini(struct ttm_mem_type_manager *man)
+{
+       struct amdgpu_gtt_mgr *mgr = man->priv;
+
+       spin_lock(&mgr->lock);
+       if (!drm_mm_clean(&mgr->mm)) {
+               spin_unlock(&mgr->lock);
+               return -EBUSY;
+       }
+
+       drm_mm_takedown(&mgr->mm);
+       spin_unlock(&mgr->lock);
+       kfree(mgr);
+       man->priv = NULL;
+       return 0;
+}
+
+/**
+ * amdgpu_gtt_mgr_alloc - allocate new ranges
+ *
+ * @man: TTM memory type manager
+ * @tbo: TTM BO we need this range for
+ * @place: placement flags and restrictions
+ * @mem: the resulting mem object
+ *
+ * Allocate the address space for a node.
+ */
+int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man,
+                        struct ttm_buffer_object *tbo,
+                        const struct ttm_place *place,
+                        struct ttm_mem_reg *mem)
+{
+       struct amdgpu_gtt_mgr *mgr = man->priv;
+       struct drm_mm_node *node = mem->mm_node;
+       enum drm_mm_search_flags sflags = DRM_MM_SEARCH_BEST;
+       enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT;
+       unsigned long fpfn, lpfn;
+       int r;
+
+       if (node->start != AMDGPU_BO_INVALID_OFFSET)
+               return 0;
+
+       if (place)
+               fpfn = place->fpfn;
+       else
+               fpfn = 0;
+
+       if (place && place->lpfn)
+               lpfn = place->lpfn;
+       else
+               lpfn = man->size;
+
+       if (place && place->flags & TTM_PL_FLAG_TOPDOWN) {
+               sflags = DRM_MM_SEARCH_BELOW;
+               aflags = DRM_MM_CREATE_TOP;
+       }
+
+       spin_lock(&mgr->lock);
+       r = drm_mm_insert_node_in_range_generic(&mgr->mm, node, mem->num_pages,
+                                               mem->page_alignment, 0,
+                                               fpfn, lpfn, sflags, aflags);
+       spin_unlock(&mgr->lock);
+
+       if (!r) {
+               mem->start = node->start;
+               if (&tbo->mem == mem)
+                       tbo->offset = (tbo->mem.start << PAGE_SHIFT) +
+                           tbo->bdev->man[tbo->mem.mem_type].gpu_offset;
+       }
+
+       return r;
+}
+
+/**
+ * amdgpu_gtt_mgr_new - allocate a new node
+ *
+ * @man: TTM memory type manager
+ * @tbo: TTM BO we need this range for
+ * @place: placement flags and restrictions
+ * @mem: the resulting mem object
+ *
+ * Dummy, allocate the node but no space for it yet.
+ */
+static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man,
+                             struct ttm_buffer_object *tbo,
+                             const struct ttm_place *place,
+                             struct ttm_mem_reg *mem)
+{
+       struct amdgpu_gtt_mgr *mgr = man->priv;
+       struct drm_mm_node *node;
+       int r;
+
+       spin_lock(&mgr->lock);
+       if (mgr->available < mem->num_pages) {
+               spin_unlock(&mgr->lock);
+               return 0;
+       }
+       mgr->available -= mem->num_pages;
+       spin_unlock(&mgr->lock);
+
+       node = kzalloc(sizeof(*node), GFP_KERNEL);
+       if (!node)
+               return -ENOMEM;
+
+       node->start = AMDGPU_BO_INVALID_OFFSET;
+       mem->mm_node = node;
+
+       if (place->fpfn || place->lpfn || place->flags & TTM_PL_FLAG_TOPDOWN) {
+               r = amdgpu_gtt_mgr_alloc(man, tbo, place, mem);
+               if (unlikely(r)) {
+                       kfree(node);
+                       mem->mm_node = NULL;
+               }
+       } else {
+               mem->start = node->start;
+       }
+
+       return 0;
+}
+
+/**
+ * amdgpu_gtt_mgr_del - free ranges
+ *
+ * @man: TTM memory type manager
+ * @tbo: TTM BO we need this range for
+ * @place: placement flags and restrictions
+ * @mem: TTM memory object
+ *
+ * Free the allocated GTT again.
+ */
+static void amdgpu_gtt_mgr_del(struct ttm_mem_type_manager *man,
+                              struct ttm_mem_reg *mem)
+{
+       struct amdgpu_gtt_mgr *mgr = man->priv;
+       struct drm_mm_node *node = mem->mm_node;
+
+       if (!node)
+               return;
+
+       spin_lock(&mgr->lock);
+       if (node->start != AMDGPU_BO_INVALID_OFFSET)
+               drm_mm_remove_node(node);
+       mgr->available += mem->num_pages;
+       spin_unlock(&mgr->lock);
+
+       kfree(node);
+       mem->mm_node = NULL;
+}
+
+/**
+ * amdgpu_gtt_mgr_debug - dump VRAM table
+ *
+ * @man: TTM memory type manager
+ * @prefix: text prefix
+ *
+ * Dump the table content using printk.
+ */
+static void amdgpu_gtt_mgr_debug(struct ttm_mem_type_manager *man,
+                                 const char *prefix)
+{
+       struct amdgpu_gtt_mgr *mgr = man->priv;
+
+       spin_lock(&mgr->lock);
+       drm_mm_debug_table(&mgr->mm, prefix);
+       spin_unlock(&mgr->lock);
+}
+
+const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func = {
+       amdgpu_gtt_mgr_init,
+       amdgpu_gtt_mgr_fini,
+       amdgpu_gtt_mgr_new,
+       amdgpu_gtt_mgr_del,
+       amdgpu_gtt_mgr_debug
+};
index 31a6763..91d3673 100644 (file)
@@ -158,8 +158,8 @@ static const struct i2c_algorithm amdgpu_atombios_i2c_algo = {
 };
 
 struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev,
-                                           struct amdgpu_i2c_bus_rec *rec,
-                                           const char *name)
+                                         const struct amdgpu_i2c_bus_rec *rec,
+                                         const char *name)
 {
        struct amdgpu_i2c_chan *i2c;
        int ret;
@@ -186,10 +186,8 @@ struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev,
                         "AMDGPU i2c hw bus %s", name);
                i2c->adapter.algo = &amdgpu_atombios_i2c_algo;
                ret = i2c_add_adapter(&i2c->adapter);
-               if (ret) {
-                       DRM_ERROR("Failed to register hw i2c %s\n", name);
+               if (ret)
                        goto out_free;
-               }
        } else {
                /* set the amdgpu bit adapter */
                snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
@@ -222,6 +220,7 @@ void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c)
 {
        if (!i2c)
                return;
+       WARN_ON(i2c->has_aux);
        i2c_del_adapter(&i2c->adapter);
        kfree(i2c);
 }
@@ -251,8 +250,8 @@ void amdgpu_i2c_fini(struct amdgpu_device *adev)
 
 /* Add additional buses */
 void amdgpu_i2c_add(struct amdgpu_device *adev,
-                    struct amdgpu_i2c_bus_rec *rec,
-                    const char *name)
+                   const struct amdgpu_i2c_bus_rec *rec,
+                   const char *name)
 {
        struct drm_device *dev = adev->ddev;
        int i;
@@ -268,7 +267,7 @@ void amdgpu_i2c_add(struct amdgpu_device *adev,
 /* looks up bus based on id */
 struct amdgpu_i2c_chan *
 amdgpu_i2c_lookup(struct amdgpu_device *adev,
-                  struct amdgpu_i2c_bus_rec *i2c_bus)
+                 const struct amdgpu_i2c_bus_rec *i2c_bus)
 {
        int i;
 
@@ -338,7 +337,7 @@ static void amdgpu_i2c_put_byte(struct amdgpu_i2c_chan *i2c_bus,
 
 /* ddc router switching */
 void
-amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector)
+amdgpu_i2c_router_select_ddc_port(const struct amdgpu_connector *amdgpu_connector)
 {
        u8 val;
 
@@ -367,7 +366,7 @@ amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector)
 
 /* clock/data router switching */
 void
-amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector)
+amdgpu_i2c_router_select_cd_port(const struct amdgpu_connector *amdgpu_connector)
 {
        u8 val;
 
index d81e19b..63c2ff7 100644 (file)
 #define __AMDGPU_I2C_H__
 
 struct amdgpu_i2c_chan *amdgpu_i2c_create(struct drm_device *dev,
-                                           struct amdgpu_i2c_bus_rec *rec,
-                                           const char *name);
+                                         const struct amdgpu_i2c_bus_rec *rec,
+                                         const char *name);
 void amdgpu_i2c_destroy(struct amdgpu_i2c_chan *i2c);
 void amdgpu_i2c_init(struct amdgpu_device *adev);
 void amdgpu_i2c_fini(struct amdgpu_device *adev);
 void amdgpu_i2c_add(struct amdgpu_device *adev,
-                    struct amdgpu_i2c_bus_rec *rec,
-                    const char *name);
+                   const struct amdgpu_i2c_bus_rec *rec,
+                   const char *name);
 struct amdgpu_i2c_chan *
 amdgpu_i2c_lookup(struct amdgpu_device *adev,
-                  struct amdgpu_i2c_bus_rec *i2c_bus);
+                 const struct amdgpu_i2c_bus_rec *i2c_bus);
 void
-amdgpu_i2c_router_select_ddc_port(struct amdgpu_connector *amdgpu_connector);
+amdgpu_i2c_router_select_ddc_port(const struct amdgpu_connector *connector);
 void
-amdgpu_i2c_router_select_cd_port(struct amdgpu_connector *amdgpu_connector);
+amdgpu_i2c_router_select_cd_port(const struct amdgpu_connector *connector);
 
 #endif
index ec1282a..6a6c86c 100644 (file)
@@ -124,7 +124,8 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs,
        bool skip_preamble, need_ctx_switch;
        unsigned patch_offset = ~0;
        struct amdgpu_vm *vm;
-       uint64_t ctx;
+       uint64_t fence_ctx;
+       uint32_t status = 0, alloc_size;
 
        unsigned i;
        int r = 0;
@@ -135,14 +136,14 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs,
        /* ring tests don't use a job */
        if (job) {
                vm = job->vm;
-               ctx = job->ctx;
+               fence_ctx = job->fence_ctx;
        } else {
                vm = NULL;
-               ctx = 0;
+               fence_ctx = 0;
        }
 
        if (!ring->ready) {
-               dev_err(adev->dev, "couldn't schedule ib\n");
+               dev_err(adev->dev, "couldn't schedule ib on ring <%s>\n", ring->name);
                return -EINVAL;
        }
 
@@ -151,7 +152,10 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs,
                return -EINVAL;
        }
 
-       r = amdgpu_ring_alloc(ring, 256 * num_ibs);
+       alloc_size = amdgpu_ring_get_dma_frame_size(ring) +
+               num_ibs * amdgpu_ring_get_emit_ib_size(ring);
+
+       r = amdgpu_ring_alloc(ring, alloc_size);
        if (r) {
                dev_err(adev->dev, "scheduling IB failed (%d).\n", r);
                return r;
@@ -174,13 +178,22 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs,
        /* always set cond_exec_polling to CONTINUE */
        *ring->cond_exe_cpu_addr = 1;
 
-       skip_preamble = ring->current_ctx == ctx;
-       need_ctx_switch = ring->current_ctx != ctx;
+       skip_preamble = ring->current_ctx == fence_ctx;
+       need_ctx_switch = ring->current_ctx != fence_ctx;
+       if (job && ring->funcs->emit_cntxcntl) {
+               if (need_ctx_switch)
+                       status |= AMDGPU_HAVE_CTX_SWITCH;
+               status |= job->preamble_status;
+               amdgpu_ring_emit_cntxcntl(ring, status);
+       }
+
        for (i = 0; i < num_ibs; ++i) {
                ib = &ibs[i];
 
                /* drop preamble IBs if we don't have a context switch */
-               if ((ib->flags & AMDGPU_IB_FLAG_PREAMBLE) && skip_preamble)
+               if ((ib->flags & AMDGPU_IB_FLAG_PREAMBLE) &&
+                       skip_preamble &&
+                       !(status & AMDGPU_PREAMBLE_IB_PRESENT_FIRST))
                        continue;
 
                amdgpu_ring_emit_ib(ring, ib, job ? job->vm_id : 0,
@@ -209,7 +222,9 @@ int amdgpu_ib_schedule(struct amdgpu_ring *ring, unsigned num_ibs,
        if (patch_offset != ~0 && ring->funcs->patch_cond_exec)
                amdgpu_ring_patch_cond_exec(ring, patch_offset);
 
-       ring->current_ctx = ctx;
+       ring->current_ctx = fence_ctx;
+       if (ring->funcs->emit_switch_buffer)
+               amdgpu_ring_emit_switch_buffer(ring);
        amdgpu_ring_commit(ring);
        return 0;
 }
index 534fc04..3ab4c65 100644 (file)
@@ -40,32 +40,15 @@ static int amdgpu_ih_ring_alloc(struct amdgpu_device *adev)
 
        /* Allocate ring buffer */
        if (adev->irq.ih.ring_obj == NULL) {
-               r = amdgpu_bo_create(adev, adev->irq.ih.ring_size,
-                                    PAGE_SIZE, true,
-                                    AMDGPU_GEM_DOMAIN_GTT, 0,
-                                    NULL, NULL, &adev->irq.ih.ring_obj);
+               r = amdgpu_bo_create_kernel(adev, adev->irq.ih.ring_size,
+                                           PAGE_SIZE, AMDGPU_GEM_DOMAIN_GTT,
+                                           &adev->irq.ih.ring_obj,
+                                           &adev->irq.ih.gpu_addr,
+                                           (void **)&adev->irq.ih.ring);
                if (r) {
                        DRM_ERROR("amdgpu: failed to create ih ring buffer (%d).\n", r);
                        return r;
                }
-               r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false);
-               if (unlikely(r != 0))
-                       return r;
-               r = amdgpu_bo_pin(adev->irq.ih.ring_obj,
-                                 AMDGPU_GEM_DOMAIN_GTT,
-                                 &adev->irq.ih.gpu_addr);
-               if (r) {
-                       amdgpu_bo_unreserve(adev->irq.ih.ring_obj);
-                       DRM_ERROR("amdgpu: failed to pin ih ring buffer (%d).\n", r);
-                       return r;
-               }
-               r = amdgpu_bo_kmap(adev->irq.ih.ring_obj,
-                                  (void **)&adev->irq.ih.ring);
-               amdgpu_bo_unreserve(adev->irq.ih.ring_obj);
-               if (r) {
-                       DRM_ERROR("amdgpu: failed to map ih ring buffer (%d).\n", r);
-                       return r;
-               }
        }
        return 0;
 }
@@ -136,8 +119,6 @@ int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size,
  */
 void amdgpu_ih_ring_fini(struct amdgpu_device *adev)
 {
-       int r;
-
        if (adev->irq.ih.use_bus_addr) {
                if (adev->irq.ih.ring) {
                        /* add 8 bytes for the rptr/wptr shadows and
@@ -149,17 +130,9 @@ void amdgpu_ih_ring_fini(struct amdgpu_device *adev)
                        adev->irq.ih.ring = NULL;
                }
        } else {
-               if (adev->irq.ih.ring_obj) {
-                       r = amdgpu_bo_reserve(adev->irq.ih.ring_obj, false);
-                       if (likely(r == 0)) {
-                               amdgpu_bo_kunmap(adev->irq.ih.ring_obj);
-                               amdgpu_bo_unpin(adev->irq.ih.ring_obj);
-                               amdgpu_bo_unreserve(adev->irq.ih.ring_obj);
-                       }
-                       amdgpu_bo_unref(&adev->irq.ih.ring_obj);
-                       adev->irq.ih.ring = NULL;
-                       adev->irq.ih.ring_obj = NULL;
-               }
+               amdgpu_bo_free_kernel(&adev->irq.ih.ring_obj,
+                                     &adev->irq.ih.gpu_addr,
+                                     (void **)&adev->irq.ih.ring);
                amdgpu_wb_free(adev, adev->irq.ih.wptr_offs);
                amdgpu_wb_free(adev, adev->irq.ih.rptr_offs);
        }
index 7ef0935..f016464 100644 (file)
@@ -70,6 +70,7 @@ struct amdgpu_irq {
        /* gen irq stuff */
        struct irq_domain               *domain; /* GPU irq controller domain */
        unsigned                        virq[AMDGPU_MAX_IRQ_SRC_ID];
+       uint32_t                        srbm_soft_reset;
 };
 
 void amdgpu_irq_preinstall(struct drm_device *dev);
index 6674d40..8c58079 100644 (file)
@@ -91,7 +91,7 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
                amdgpu_ib_free(job->adev, &job->ibs[i], f);
 }
 
-void amdgpu_job_free_cb(struct amd_sched_job *s_job)
+static void amdgpu_job_free_cb(struct amd_sched_job *s_job)
 {
        struct amdgpu_job *job = container_of(s_job, struct amdgpu_job, base);
 
@@ -124,7 +124,7 @@ int amdgpu_job_submit(struct amdgpu_job *job, struct amdgpu_ring *ring,
                return r;
 
        job->owner = owner;
-       job->ctx = entity->fence_context;
+       job->fence_ctx = entity->fence_context;
        *f = fence_get(&job->base.s_fence->finished);
        amdgpu_job_free_resources(job);
        amd_sched_entity_push_job(&job->base);
index d942654..c2c7fb1 100644 (file)
@@ -292,14 +292,14 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
                        type = AMD_IP_BLOCK_TYPE_UVD;
                        ring_mask = adev->uvd.ring.ready ? 1 : 0;
                        ib_start_alignment = AMDGPU_GPU_PAGE_SIZE;
-                       ib_size_alignment = 8;
+                       ib_size_alignment = 16;
                        break;
                case AMDGPU_HW_IP_VCE:
                        type = AMD_IP_BLOCK_TYPE_VCE;
-                       for (i = 0; i < AMDGPU_MAX_VCE_RINGS; i++)
+                       for (i = 0; i < adev->vce.num_rings; i++)
                                ring_mask |= ((adev->vce.ring[i].ready ? 1 : 0) << i);
                        ib_start_alignment = AMDGPU_GPU_PAGE_SIZE;
-                       ib_size_alignment = 8;
+                       ib_size_alignment = 1;
                        break;
                default:
                        return -EINVAL;
@@ -373,6 +373,9 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
        case AMDGPU_INFO_NUM_BYTES_MOVED:
                ui64 = atomic64_read(&adev->num_bytes_moved);
                return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
+       case AMDGPU_INFO_NUM_EVICTIONS:
+               ui64 = atomic64_read(&adev->num_evictions);
+               return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
        case AMDGPU_INFO_VRAM_USAGE:
                ui64 = atomic64_read(&adev->vram_usage);
                return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
@@ -539,12 +542,16 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
                return r;
 
        fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
-       if (unlikely(!fpriv))
-               return -ENOMEM;
+       if (unlikely(!fpriv)) {
+               r = -ENOMEM;
+               goto out_suspend;
+       }
 
        r = amdgpu_vm_init(adev, &fpriv->vm);
-       if (r)
-               goto error_free;
+       if (r) {
+               kfree(fpriv);
+               goto out_suspend;
+       }
 
        mutex_init(&fpriv->bo_list_lock);
        idr_init(&fpriv->bo_list_handles);
@@ -553,12 +560,9 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
 
        file_priv->driver_priv = fpriv;
 
+out_suspend:
        pm_runtime_mark_last_busy(dev->dev);
        pm_runtime_put_autosuspend(dev->dev);
-       return 0;
-
-error_free:
-       kfree(fpriv);
 
        return r;
 }
@@ -597,6 +601,9 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
 
        kfree(fpriv);
        file_priv->driver_priv = NULL;
+
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
 }
 
 /**
@@ -611,6 +618,7 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
 void amdgpu_driver_preclose_kms(struct drm_device *dev,
                                struct drm_file *file_priv)
 {
+       pm_runtime_get_sync(dev->dev);
 }
 
 /*
index 6b1d7d3..7b0eff7 100644 (file)
@@ -39,6 +39,8 @@
 #include <drm/drm_plane_helper.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <linux/hrtimer.h>
+#include "amdgpu_irq.h"
 
 struct amdgpu_bo;
 struct amdgpu_device;
@@ -339,6 +341,8 @@ struct amdgpu_mode_info {
        int                     num_dig; /* number of dig blocks */
        int                     disp_priority;
        const struct amdgpu_display_funcs *funcs;
+       struct hrtimer vblank_timer;
+       enum amdgpu_interrupt_state vsync_timer_enabled;
 };
 
 #define AMDGPU_MAX_BL_LEVEL 0xFF
@@ -587,10 +591,10 @@ int amdgpu_align_pitch(struct amdgpu_device *adev, int width, int bpp, bool tile
 void amdgpu_print_display_setup(struct drm_device *dev);
 int amdgpu_modeset_create_props(struct amdgpu_device *adev);
 int amdgpu_crtc_set_config(struct drm_mode_set *set);
-int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
-                         struct drm_framebuffer *fb,
-                         struct drm_pending_vblank_event *event,
-                         uint32_t page_flip_flags);
+int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
+                                struct drm_framebuffer *fb,
+                                struct drm_pending_vblank_event *event,
+                                uint32_t page_flip_flags, uint32_t target);
 extern const struct drm_mode_config_funcs amdgpu_mode_funcs;
 
 #endif
index 6f0873c..aa074fa 100644 (file)
 #include "amdgpu_trace.h"
 
 
-int amdgpu_ttm_init(struct amdgpu_device *adev);
-void amdgpu_ttm_fini(struct amdgpu_device *adev);
 
 static u64 amdgpu_get_vis_part_size(struct amdgpu_device *adev,
                                                struct ttm_mem_reg *mem)
 {
-       u64 ret = 0;
-       if (mem->start << PAGE_SHIFT < adev->mc.visible_vram_size) {
-               ret = (u64)((mem->start << PAGE_SHIFT) + mem->size) >
-                          adev->mc.visible_vram_size ?
-                          adev->mc.visible_vram_size - (mem->start << PAGE_SHIFT) :
-                          mem->size;
-       }
-       return ret;
+       if (mem->start << PAGE_SHIFT >= adev->mc.visible_vram_size)
+               return 0;
+
+       return ((mem->start << PAGE_SHIFT) + mem->size) >
+               adev->mc.visible_vram_size ?
+               adev->mc.visible_vram_size - (mem->start << PAGE_SHIFT) :
+               mem->size;
 }
 
 static void amdgpu_update_memory_usage(struct amdgpu_device *adev,
@@ -99,6 +96,11 @@ static void amdgpu_ttm_bo_destroy(struct ttm_buffer_object *tbo)
 
        drm_gem_object_release(&bo->gem_base);
        amdgpu_bo_unref(&bo->parent);
+       if (!list_empty(&bo->shadow_list)) {
+               mutex_lock(&bo->adev->shadow_list_lock);
+               list_del_init(&bo->shadow_list);
+               mutex_unlock(&bo->adev->shadow_list_lock);
+       }
        kfree(bo->metadata);
        kfree(bo);
 }
@@ -112,90 +114,99 @@ bool amdgpu_ttm_bo_is_amdgpu_bo(struct ttm_buffer_object *bo)
 
 static void amdgpu_ttm_placement_init(struct amdgpu_device *adev,
                                      struct ttm_placement *placement,
-                                     struct ttm_place *placements,
+                                     struct ttm_place *places,
                                      u32 domain, u64 flags)
 {
-       u32 c = 0, i;
-
-       placement->placement = placements;
-       placement->busy_placement = placements;
+       u32 c = 0;
 
        if (domain & AMDGPU_GEM_DOMAIN_VRAM) {
+               unsigned visible_pfn = adev->mc.visible_vram_size >> PAGE_SHIFT;
+
                if (flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS &&
-                       adev->mc.visible_vram_size < adev->mc.real_vram_size) {
-                       placements[c].fpfn =
-                               adev->mc.visible_vram_size >> PAGE_SHIFT;
-                       placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
-                               TTM_PL_FLAG_VRAM | TTM_PL_FLAG_TOPDOWN;
+                   !(flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) &&
+                   adev->mc.visible_vram_size < adev->mc.real_vram_size) {
+                       places[c].fpfn = visible_pfn;
+                       places[c].lpfn = 0;
+                       places[c].flags = TTM_PL_FLAG_WC |
+                               TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM |
+                               TTM_PL_FLAG_TOPDOWN;
+                       c++;
                }
-               placements[c].fpfn = 0;
-               placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
+
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
                        TTM_PL_FLAG_VRAM;
-               if (!(flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED))
-                       placements[c - 1].flags |= TTM_PL_FLAG_TOPDOWN;
+               if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED)
+                       places[c].lpfn = visible_pfn;
+               else
+                       places[c].flags |= TTM_PL_FLAG_TOPDOWN;
+               c++;
        }
 
        if (domain & AMDGPU_GEM_DOMAIN_GTT) {
-               if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) {
-                       placements[c].fpfn = 0;
-                       placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT |
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_TT;
+               if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC)
+                       places[c].flags |= TTM_PL_FLAG_WC |
                                TTM_PL_FLAG_UNCACHED;
-               } else {
-                       placements[c].fpfn = 0;
-                       placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT;
-               }
+               else
+                       places[c].flags |= TTM_PL_FLAG_CACHED;
+               c++;
        }
 
        if (domain & AMDGPU_GEM_DOMAIN_CPU) {
-               if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC) {
-                       placements[c].fpfn = 0;
-                       placements[c++].flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_SYSTEM |
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_SYSTEM;
+               if (flags & AMDGPU_GEM_CREATE_CPU_GTT_USWC)
+                       places[c].flags |= TTM_PL_FLAG_WC |
                                TTM_PL_FLAG_UNCACHED;
-               } else {
-                       placements[c].fpfn = 0;
-                       placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_SYSTEM;
-               }
+               else
+                       places[c].flags |= TTM_PL_FLAG_CACHED;
+               c++;
        }
 
        if (domain & AMDGPU_GEM_DOMAIN_GDS) {
-               placements[c].fpfn = 0;
-               placements[c++].flags = TTM_PL_FLAG_UNCACHED |
-                       AMDGPU_PL_FLAG_GDS;
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_GDS;
+               c++;
        }
+
        if (domain & AMDGPU_GEM_DOMAIN_GWS) {
-               placements[c].fpfn = 0;
-               placements[c++].flags = TTM_PL_FLAG_UNCACHED |
-                       AMDGPU_PL_FLAG_GWS;
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_GWS;
+               c++;
        }
+
        if (domain & AMDGPU_GEM_DOMAIN_OA) {
-               placements[c].fpfn = 0;
-               placements[c++].flags = TTM_PL_FLAG_UNCACHED |
-                       AMDGPU_PL_FLAG_OA;
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_FLAG_UNCACHED | AMDGPU_PL_FLAG_OA;
+               c++;
        }
 
        if (!c) {
-               placements[c].fpfn = 0;
-               placements[c++].flags = TTM_PL_MASK_CACHING |
-                       TTM_PL_FLAG_SYSTEM;
+               places[c].fpfn = 0;
+               places[c].lpfn = 0;
+               places[c].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+               c++;
        }
+
        placement->num_placement = c;
-       placement->num_busy_placement = c;
+       placement->placement = places;
 
-       for (i = 0; i < c; i++) {
-               if ((flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) &&
-                       (placements[i].flags & TTM_PL_FLAG_VRAM) &&
-                       !placements[i].fpfn)
-                       placements[i].lpfn =
-                               adev->mc.visible_vram_size >> PAGE_SHIFT;
-               else
-                       placements[i].lpfn = 0;
-       }
+       placement->num_busy_placement = c;
+       placement->busy_placement = places;
 }
 
-void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *rbo, u32 domain)
+void amdgpu_ttm_placement_from_domain(struct amdgpu_bo *abo, u32 domain)
 {
-       amdgpu_ttm_placement_init(rbo->adev, &rbo->placement,
-                                 rbo->placements, domain, rbo->flags);
+       amdgpu_ttm_placement_init(abo->adev, &abo->placement,
+                                 abo->placements, domain, abo->flags);
 }
 
 static void amdgpu_fill_placement_to_bo(struct amdgpu_bo *bo,
@@ -211,6 +222,98 @@ static void amdgpu_fill_placement_to_bo(struct amdgpu_bo *bo,
        bo->placement.busy_placement = bo->placements;
 }
 
+/**
+ * amdgpu_bo_create_kernel - create BO for kernel use
+ *
+ * @adev: amdgpu device object
+ * @size: size for the new BO
+ * @align: alignment for the new BO
+ * @domain: where to place it
+ * @bo_ptr: resulting BO
+ * @gpu_addr: GPU addr of the pinned BO
+ * @cpu_addr: optional CPU address mapping
+ *
+ * Allocates and pins a BO for kernel internal use.
+ *
+ * Returns 0 on success, negative error code otherwise.
+ */
+int amdgpu_bo_create_kernel(struct amdgpu_device *adev,
+                           unsigned long size, int align,
+                           u32 domain, struct amdgpu_bo **bo_ptr,
+                           u64 *gpu_addr, void **cpu_addr)
+{
+       int r;
+
+       r = amdgpu_bo_create(adev, size, align, true, domain,
+                            AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
+                            NULL, NULL, bo_ptr);
+       if (r) {
+               dev_err(adev->dev, "(%d) failed to allocate kernel bo\n", r);
+               return r;
+       }
+
+       r = amdgpu_bo_reserve(*bo_ptr, false);
+       if (r) {
+               dev_err(adev->dev, "(%d) failed to reserve kernel bo\n", r);
+               goto error_free;
+       }
+
+       r = amdgpu_bo_pin(*bo_ptr, domain, gpu_addr);
+       if (r) {
+               dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
+               goto error_unreserve;
+       }
+
+       if (cpu_addr) {
+               r = amdgpu_bo_kmap(*bo_ptr, cpu_addr);
+               if (r) {
+                       dev_err(adev->dev, "(%d) kernel bo map failed\n", r);
+                       goto error_unreserve;
+               }
+       }
+
+       amdgpu_bo_unreserve(*bo_ptr);
+
+       return 0;
+
+error_unreserve:
+       amdgpu_bo_unreserve(*bo_ptr);
+
+error_free:
+       amdgpu_bo_unref(bo_ptr);
+
+       return r;
+}
+
+/**
+ * amdgpu_bo_free_kernel - free BO for kernel use
+ *
+ * @bo: amdgpu BO to free
+ *
+ * unmaps and unpin a BO for kernel internal use.
+ */
+void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr,
+                          void **cpu_addr)
+{
+       if (*bo == NULL)
+               return;
+
+       if (likely(amdgpu_bo_reserve(*bo, false) == 0)) {
+               if (cpu_addr)
+                       amdgpu_bo_kunmap(*bo);
+
+               amdgpu_bo_unpin(*bo);
+               amdgpu_bo_unreserve(*bo);
+       }
+       amdgpu_bo_unref(bo);
+
+       if (gpu_addr)
+               *gpu_addr = 0;
+
+       if (cpu_addr)
+               *cpu_addr = NULL;
+}
+
 int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
                                unsigned long size, int byte_align,
                                bool kernel, u32 domain, u64 flags,
@@ -249,7 +352,7 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
                return r;
        }
        bo->adev = adev;
-       INIT_LIST_HEAD(&bo->list);
+       INIT_LIST_HEAD(&bo->shadow_list);
        INIT_LIST_HEAD(&bo->va);
        bo->prefered_domains = domain & (AMDGPU_GEM_DOMAIN_VRAM |
                                         AMDGPU_GEM_DOMAIN_GTT |
@@ -277,11 +380,79 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
        if (unlikely(r != 0)) {
                return r;
        }
+
+       if (flags & AMDGPU_GEM_CREATE_VRAM_CLEARED &&
+           bo->tbo.mem.placement & TTM_PL_FLAG_VRAM) {
+               struct fence *fence;
+
+               if (adev->mman.buffer_funcs_ring == NULL ||
+                  !adev->mman.buffer_funcs_ring->ready) {
+                       r = -EBUSY;
+                       goto fail_free;
+               }
+
+               r = amdgpu_bo_reserve(bo, false);
+               if (unlikely(r != 0))
+                       goto fail_free;
+
+               amdgpu_ttm_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_VRAM);
+               r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
+               if (unlikely(r != 0))
+                       goto fail_unreserve;
+
+               amdgpu_fill_buffer(bo, 0, bo->tbo.resv, &fence);
+               amdgpu_bo_fence(bo, fence, false);
+               amdgpu_bo_unreserve(bo);
+               fence_put(bo->tbo.moving);
+               bo->tbo.moving = fence_get(fence);
+               fence_put(fence);
+       }
        *bo_ptr = bo;
 
        trace_amdgpu_bo_create(bo);
 
        return 0;
+
+fail_unreserve:
+       amdgpu_bo_unreserve(bo);
+fail_free:
+       amdgpu_bo_unref(&bo);
+       return r;
+}
+
+static int amdgpu_bo_create_shadow(struct amdgpu_device *adev,
+                                  unsigned long size, int byte_align,
+                                  struct amdgpu_bo *bo)
+{
+       struct ttm_placement placement = {0};
+       struct ttm_place placements[AMDGPU_GEM_DOMAIN_MAX + 1];
+       int r;
+
+       if (bo->shadow)
+               return 0;
+
+       bo->flags |= AMDGPU_GEM_CREATE_SHADOW;
+       memset(&placements, 0,
+              (AMDGPU_GEM_DOMAIN_MAX + 1) * sizeof(struct ttm_place));
+
+       amdgpu_ttm_placement_init(adev, &placement,
+                                 placements, AMDGPU_GEM_DOMAIN_GTT,
+                                 AMDGPU_GEM_CREATE_CPU_GTT_USWC);
+
+       r = amdgpu_bo_create_restricted(adev, size, byte_align, true,
+                                       AMDGPU_GEM_DOMAIN_GTT,
+                                       AMDGPU_GEM_CREATE_CPU_GTT_USWC,
+                                       NULL, &placement,
+                                       bo->tbo.resv,
+                                       &bo->shadow);
+       if (!r) {
+               bo->shadow->parent = amdgpu_bo_ref(bo);
+               mutex_lock(&adev->shadow_list_lock);
+               list_add_tail(&bo->shadow_list, &adev->shadow_list);
+               mutex_unlock(&adev->shadow_list_lock);
+       }
+
+       return r;
 }
 
 int amdgpu_bo_create(struct amdgpu_device *adev,
@@ -293,6 +464,7 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
 {
        struct ttm_placement placement = {0};
        struct ttm_place placements[AMDGPU_GEM_DOMAIN_MAX + 1];
+       int r;
 
        memset(&placements, 0,
               (AMDGPU_GEM_DOMAIN_MAX + 1) * sizeof(struct ttm_place));
@@ -300,9 +472,83 @@ int amdgpu_bo_create(struct amdgpu_device *adev,
        amdgpu_ttm_placement_init(adev, &placement,
                                  placements, domain, flags);
 
-       return amdgpu_bo_create_restricted(adev, size, byte_align, kernel,
-                                          domain, flags, sg, &placement,
-                                          resv, bo_ptr);
+       r = amdgpu_bo_create_restricted(adev, size, byte_align, kernel,
+                                       domain, flags, sg, &placement,
+                                       resv, bo_ptr);
+       if (r)
+               return r;
+
+       if (amdgpu_need_backup(adev) && (flags & AMDGPU_GEM_CREATE_SHADOW)) {
+               r = amdgpu_bo_create_shadow(adev, size, byte_align, (*bo_ptr));
+               if (r)
+                       amdgpu_bo_unref(bo_ptr);
+       }
+
+       return r;
+}
+
+int amdgpu_bo_backup_to_shadow(struct amdgpu_device *adev,
+                              struct amdgpu_ring *ring,
+                              struct amdgpu_bo *bo,
+                              struct reservation_object *resv,
+                              struct fence **fence,
+                              bool direct)
+
+{
+       struct amdgpu_bo *shadow = bo->shadow;
+       uint64_t bo_addr, shadow_addr;
+       int r;
+
+       if (!shadow)
+               return -EINVAL;
+
+       bo_addr = amdgpu_bo_gpu_offset(bo);
+       shadow_addr = amdgpu_bo_gpu_offset(bo->shadow);
+
+       r = reservation_object_reserve_shared(bo->tbo.resv);
+       if (r)
+               goto err;
+
+       r = amdgpu_copy_buffer(ring, bo_addr, shadow_addr,
+                              amdgpu_bo_size(bo), resv, fence,
+                              direct);
+       if (!r)
+               amdgpu_bo_fence(bo, *fence, true);
+
+err:
+       return r;
+}
+
+int amdgpu_bo_restore_from_shadow(struct amdgpu_device *adev,
+                                 struct amdgpu_ring *ring,
+                                 struct amdgpu_bo *bo,
+                                 struct reservation_object *resv,
+                                 struct fence **fence,
+                                 bool direct)
+
+{
+       struct amdgpu_bo *shadow = bo->shadow;
+       uint64_t bo_addr, shadow_addr;
+       int r;
+
+       if (!shadow)
+               return -EINVAL;
+
+       bo_addr = amdgpu_bo_gpu_offset(bo);
+       shadow_addr = amdgpu_bo_gpu_offset(bo->shadow);
+
+       r = reservation_object_reserve_shared(bo->tbo.resv);
+       if (r)
+               goto err;
+
+       r = amdgpu_copy_buffer(ring, shadow_addr, bo_addr,
+                              amdgpu_bo_size(bo), resv, fence,
+                              direct);
+       if (!r)
+               amdgpu_bo_fence(bo, *fence, true);
+
+err:
+       return r;
 }
 
 int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr)
@@ -380,16 +626,17 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
                return -EINVAL;
 
        if (bo->pin_count) {
+               uint32_t mem_type = bo->tbo.mem.mem_type;
+
+               if (domain != amdgpu_mem_type_to_domain(mem_type))
+                       return -EINVAL;
+
                bo->pin_count++;
                if (gpu_addr)
                        *gpu_addr = amdgpu_bo_gpu_offset(bo);
 
                if (max_offset != 0) {
-                       u64 domain_start;
-                       if (domain == AMDGPU_GEM_DOMAIN_VRAM)
-                               domain_start = bo->adev->mc.vram_start;
-                       else
-                               domain_start = bo->adev->mc.gtt_start;
+                       u64 domain_start = bo->tbo.bdev->man[mem_type].gpu_offset;
                        WARN_ON_ONCE(max_offset <
                                     (amdgpu_bo_gpu_offset(bo) - domain_start));
                }
@@ -401,7 +648,8 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
                /* force to pin into visible video ram */
                if ((bo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
                    !(bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS) &&
-                   (!max_offset || max_offset > bo->adev->mc.visible_vram_size)) {
+                   (!max_offset || max_offset >
+                    bo->adev->mc.visible_vram_size)) {
                        if (WARN_ON_ONCE(min_offset >
                                         bo->adev->mc.visible_vram_size))
                                return -EINVAL;
@@ -420,19 +668,28 @@ int amdgpu_bo_pin_restricted(struct amdgpu_bo *bo, u32 domain,
        }
 
        r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
-       if (likely(r == 0)) {
-               bo->pin_count = 1;
-               if (gpu_addr != NULL)
-                       *gpu_addr = amdgpu_bo_gpu_offset(bo);
-               if (domain == AMDGPU_GEM_DOMAIN_VRAM) {
-                       bo->adev->vram_pin_size += amdgpu_bo_size(bo);
-                       if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
-                               bo->adev->invisible_pin_size += amdgpu_bo_size(bo);
-               } else
-                       bo->adev->gart_pin_size += amdgpu_bo_size(bo);
-       } else {
+       if (unlikely(r)) {
                dev_err(bo->adev->dev, "%p pin failed\n", bo);
+               goto error;
+       }
+       r = amdgpu_ttm_bind(&bo->tbo, &bo->tbo.mem);
+       if (unlikely(r)) {
+               dev_err(bo->adev->dev, "%p bind failed\n", bo);
+               goto error;
        }
+
+       bo->pin_count = 1;
+       if (gpu_addr != NULL)
+               *gpu_addr = amdgpu_bo_gpu_offset(bo);
+       if (domain == AMDGPU_GEM_DOMAIN_VRAM) {
+               bo->adev->vram_pin_size += amdgpu_bo_size(bo);
+               if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
+                       bo->adev->invisible_pin_size += amdgpu_bo_size(bo);
+       } else if (domain == AMDGPU_GEM_DOMAIN_GTT) {
+               bo->adev->gart_pin_size += amdgpu_bo_size(bo);
+       }
+
+error:
        return r;
 }
 
@@ -457,16 +714,20 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo)
                bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT;
        }
        r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
-       if (likely(r == 0)) {
-               if (bo->tbo.mem.mem_type == TTM_PL_VRAM) {
-                       bo->adev->vram_pin_size -= amdgpu_bo_size(bo);
-                       if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
-                               bo->adev->invisible_pin_size -= amdgpu_bo_size(bo);
-               } else
-                       bo->adev->gart_pin_size -= amdgpu_bo_size(bo);
-       } else {
+       if (unlikely(r)) {
                dev_err(bo->adev->dev, "%p validate failed for unpin\n", bo);
+               goto error;
        }
+
+       if (bo->tbo.mem.mem_type == TTM_PL_VRAM) {
+               bo->adev->vram_pin_size -= amdgpu_bo_size(bo);
+               if (bo->flags & AMDGPU_GEM_CREATE_NO_CPU_ACCESS)
+                       bo->adev->invisible_pin_size -= amdgpu_bo_size(bo);
+       } else if (bo->tbo.mem.mem_type == TTM_PL_TT) {
+               bo->adev->gart_pin_size -= amdgpu_bo_size(bo);
+       }
+
+error:
        return r;
 }
 
@@ -588,23 +849,23 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
 void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
                           struct ttm_mem_reg *new_mem)
 {
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        struct ttm_mem_reg *old_mem = &bo->mem;
 
        if (!amdgpu_ttm_bo_is_amdgpu_bo(bo))
                return;
 
-       rbo = container_of(bo, struct amdgpu_bo, tbo);
-       amdgpu_vm_bo_invalidate(rbo->adev, rbo);
+       abo = container_of(bo, struct amdgpu_bo, tbo);
+       amdgpu_vm_bo_invalidate(abo->adev, abo);
 
        /* update statistics */
        if (!new_mem)
                return;
 
        /* move_notify is called before move happens */
-       amdgpu_update_memory_usage(rbo->adev, &bo->mem, new_mem);
+       amdgpu_update_memory_usage(abo->adev, &bo->mem, new_mem);
 
-       trace_amdgpu_ttm_bo_move(rbo, new_mem->mem_type, old_mem->mem_type);
+       trace_amdgpu_ttm_bo_move(abo, new_mem->mem_type, old_mem->mem_type);
 }
 
 int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
@@ -637,7 +898,8 @@ int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
        for (i = 0; i < abo->placement.num_placement; i++) {
                /* Force into visible VRAM */
                if ((abo->placements[i].flags & TTM_PL_FLAG_VRAM) &&
-                   (!abo->placements[i].lpfn || abo->placements[i].lpfn > lpfn))
+                   (!abo->placements[i].lpfn ||
+                    abo->placements[i].lpfn > lpfn))
                        abo->placements[i].lpfn = lpfn;
        }
        r = ttm_bo_validate(bo, &abo->placement, false, false);
@@ -674,3 +936,24 @@ void amdgpu_bo_fence(struct amdgpu_bo *bo, struct fence *fence,
        else
                reservation_object_add_excl_fence(resv, fence);
 }
+
+/**
+ * amdgpu_bo_gpu_offset - return GPU offset of bo
+ * @bo:        amdgpu object for which we query the offset
+ *
+ * Returns current GPU offset of the object.
+ *
+ * Note: object should either be pinned or reserved when calling this
+ * function, it might be useful to add check for this for debugging.
+ */
+u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo)
+{
+       WARN_ON_ONCE(bo->tbo.mem.mem_type == TTM_PL_SYSTEM);
+       WARN_ON_ONCE(bo->tbo.mem.mem_type == TTM_PL_TT &&
+                    !amdgpu_ttm_is_bound(bo->tbo.ttm));
+       WARN_ON_ONCE(!ww_mutex_is_locked(&bo->tbo.resv->lock) &&
+                    !bo->pin_count);
+       WARN_ON_ONCE(bo->tbo.mem.start == AMDGPU_BO_INVALID_OFFSET);
+
+       return bo->tbo.offset;
+}
index bdb01d9..8255034 100644 (file)
@@ -31,6 +31,8 @@
 #include <drm/amdgpu_drm.h>
 #include "amdgpu.h"
 
+#define AMDGPU_BO_INVALID_OFFSET       LONG_MAX
+
 /**
  * amdgpu_mem_type_to_domain - return domain corresponding to mem_type
  * @mem_type:  ttm memory type
@@ -85,21 +87,6 @@ static inline void amdgpu_bo_unreserve(struct amdgpu_bo *bo)
        ttm_bo_unreserve(&bo->tbo);
 }
 
-/**
- * amdgpu_bo_gpu_offset - return GPU offset of bo
- * @bo:        amdgpu object for which we query the offset
- *
- * Returns current GPU offset of the object.
- *
- * Note: object should either be pinned or reserved when calling this
- * function, it might be useful to add check for this for debugging.
- */
-static inline u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo)
-{
-       WARN_ON_ONCE(bo->tbo.mem.mem_type == TTM_PL_SYSTEM);
-       return bo->tbo.offset;
-}
-
 static inline unsigned long amdgpu_bo_size(struct amdgpu_bo *bo)
 {
        return bo->tbo.num_pages << PAGE_SHIFT;
@@ -139,6 +126,12 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
                                struct ttm_placement *placement,
                                struct reservation_object *resv,
                                struct amdgpu_bo **bo_ptr);
+int amdgpu_bo_create_kernel(struct amdgpu_device *adev,
+                           unsigned long size, int align,
+                           u32 domain, struct amdgpu_bo **bo_ptr,
+                           u64 *gpu_addr, void **cpu_addr);
+void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr,
+                          void **cpu_addr);
 int amdgpu_bo_kmap(struct amdgpu_bo *bo, void **ptr);
 void amdgpu_bo_kunmap(struct amdgpu_bo *bo);
 struct amdgpu_bo *amdgpu_bo_ref(struct amdgpu_bo *bo);
@@ -165,6 +158,19 @@ void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
 int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
 void amdgpu_bo_fence(struct amdgpu_bo *bo, struct fence *fence,
                     bool shared);
+u64 amdgpu_bo_gpu_offset(struct amdgpu_bo *bo);
+int amdgpu_bo_backup_to_shadow(struct amdgpu_device *adev,
+                              struct amdgpu_ring *ring,
+                              struct amdgpu_bo *bo,
+                              struct reservation_object *resv,
+                              struct fence **fence, bool direct);
+int amdgpu_bo_restore_from_shadow(struct amdgpu_device *adev,
+                                 struct amdgpu_ring *ring,
+                                 struct amdgpu_bo *bo,
+                                 struct reservation_object *resv,
+                                 struct fence **fence,
+                                 bool direct);
+
 
 /*
  * sub allocation
index d153149..8e67c12 100644 (file)
@@ -25,6 +25,7 @@
 #include "amdgpu.h"
 #include "atom.h"
 #include "atombios_encoders.h"
+#include "amdgpu_pll.h"
 #include <asm/div64.h>
 #include <linux/gcd.h>
 
index 5cc7052..accc908 100644 (file)
@@ -1103,54 +1103,46 @@ force:
 
 void amdgpu_dpm_enable_uvd(struct amdgpu_device *adev, bool enable)
 {
-       if (adev->pp_enabled)
+       if (adev->pp_enabled || adev->pm.funcs->powergate_uvd) {
+               /* enable/disable UVD */
+               mutex_lock(&adev->pm.mutex);
                amdgpu_dpm_powergate_uvd(adev, !enable);
-       else {
-               if (adev->pm.funcs->powergate_uvd) {
+               mutex_unlock(&adev->pm.mutex);
+       } else {
+               if (enable) {
                        mutex_lock(&adev->pm.mutex);
-                       /* enable/disable UVD */
-                       amdgpu_dpm_powergate_uvd(adev, !enable);
+                       adev->pm.dpm.uvd_active = true;
+                       adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD;
                        mutex_unlock(&adev->pm.mutex);
                } else {
-                       if (enable) {
-                               mutex_lock(&adev->pm.mutex);
-                               adev->pm.dpm.uvd_active = true;
-                               adev->pm.dpm.state = POWER_STATE_TYPE_INTERNAL_UVD;
-                               mutex_unlock(&adev->pm.mutex);
-                       } else {
-                               mutex_lock(&adev->pm.mutex);
-                               adev->pm.dpm.uvd_active = false;
-                               mutex_unlock(&adev->pm.mutex);
-                       }
-                       amdgpu_pm_compute_clocks(adev);
+                       mutex_lock(&adev->pm.mutex);
+                       adev->pm.dpm.uvd_active = false;
+                       mutex_unlock(&adev->pm.mutex);
                }
-
+               amdgpu_pm_compute_clocks(adev);
        }
 }
 
 void amdgpu_dpm_enable_vce(struct amdgpu_device *adev, bool enable)
 {
-       if (adev->pp_enabled)
+       if (adev->pp_enabled || adev->pm.funcs->powergate_vce) {
+               /* enable/disable VCE */
+               mutex_lock(&adev->pm.mutex);
                amdgpu_dpm_powergate_vce(adev, !enable);
-       else {
-               if (adev->pm.funcs->powergate_vce) {
+               mutex_unlock(&adev->pm.mutex);
+       } else {
+               if (enable) {
                        mutex_lock(&adev->pm.mutex);
-                       amdgpu_dpm_powergate_vce(adev, !enable);
+                       adev->pm.dpm.vce_active = true;
+                       /* XXX select vce level based on ring/task */
+                       adev->pm.dpm.vce_level = AMDGPU_VCE_LEVEL_AC_ALL;
                        mutex_unlock(&adev->pm.mutex);
                } else {
-                       if (enable) {
-                               mutex_lock(&adev->pm.mutex);
-                               adev->pm.dpm.vce_active = true;
-                               /* XXX select vce level based on ring/task */
-                               adev->pm.dpm.vce_level = AMDGPU_VCE_LEVEL_AC_ALL;
-                               mutex_unlock(&adev->pm.mutex);
-                       } else {
-                               mutex_lock(&adev->pm.mutex);
-                               adev->pm.dpm.vce_active = false;
-                               mutex_unlock(&adev->pm.mutex);
-                       }
-                       amdgpu_pm_compute_clocks(adev);
+                       mutex_lock(&adev->pm.mutex);
+                       adev->pm.dpm.vce_active = false;
+                       mutex_unlock(&adev->pm.mutex);
                }
+               amdgpu_pm_compute_clocks(adev);
        }
 }
 
@@ -1330,6 +1322,64 @@ void amdgpu_pm_compute_clocks(struct amdgpu_device *adev)
  */
 #if defined(CONFIG_DEBUG_FS)
 
+static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *adev)
+{
+       int32_t value;
+
+       /* sanity check PP is enabled */
+       if (!(adev->powerplay.pp_funcs &&
+             adev->powerplay.pp_funcs->read_sensor))
+             return -EINVAL;
+
+       /* GPU Clocks */
+       seq_printf(m, "GFX Clocks and Power:\n");
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GFX_MCLK, &value))
+               seq_printf(m, "\t%u MHz (MCLK)\n", value/100);
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GFX_SCLK, &value))
+               seq_printf(m, "\t%u MHz (SCLK)\n", value/100);
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VDDGFX, &value))
+               seq_printf(m, "\t%u mV (VDDGFX)\n", value);
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VDDNB, &value))
+               seq_printf(m, "\t%u mV (VDDNB)\n", value);
+       seq_printf(m, "\n");
+
+       /* GPU Temp */
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_TEMP, &value))
+               seq_printf(m, "GPU Temperature: %u C\n", value/1000);
+
+       /* GPU Load */
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_LOAD, &value))
+               seq_printf(m, "GPU Load: %u %%\n", value);
+       seq_printf(m, "\n");
+
+       /* UVD clocks */
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_POWER, &value)) {
+               if (!value) {
+                       seq_printf(m, "UVD: Disabled\n");
+               } else {
+                       seq_printf(m, "UVD: Enabled\n");
+                       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_DCLK, &value))
+                               seq_printf(m, "\t%u MHz (DCLK)\n", value/100);
+                       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_UVD_VCLK, &value))
+                               seq_printf(m, "\t%u MHz (VCLK)\n", value/100);
+               }
+       }
+       seq_printf(m, "\n");
+
+       /* VCE clocks */
+       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VCE_POWER, &value)) {
+               if (!value) {
+                       seq_printf(m, "VCE: Disabled\n");
+               } else {
+                       seq_printf(m, "VCE: Enabled\n");
+                       if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_VCE_ECCLK, &value))
+                               seq_printf(m, "\t%u MHz (ECCLK)\n", value/100);
+               }
+       }
+
+       return 0;
+}
+
 static int amdgpu_debugfs_pm_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -1345,11 +1395,11 @@ static int amdgpu_debugfs_pm_info(struct seq_file *m, void *data)
             (ddev->switch_power_state != DRM_SWITCH_POWER_ON)) {
                seq_printf(m, "PX asic powered off\n");
        } else if (adev->pp_enabled) {
-               amdgpu_dpm_debugfs_print_current_performance_level(adev, m);
+               return amdgpu_debugfs_pm_info_pp(m, adev);
        } else {
                mutex_lock(&adev->pm.mutex);
                if (adev->pm.funcs->debugfs_print_current_performance_level)
-                       amdgpu_dpm_debugfs_print_current_performance_level(adev, m);
+                       adev->pm.funcs->debugfs_print_current_performance_level(adev, m);
                else
                        seq_printf(m, "Debugfs support not implemented for this asic\n");
                mutex_unlock(&adev->pm.mutex);
index c5738a2..7532ff8 100644 (file)
@@ -30,6 +30,7 @@
 #include "amdgpu_pm.h"
 #include <drm/amdgpu_drm.h>
 #include "amdgpu_powerplay.h"
+#include "si_dpm.h"
 #include "cik_dpm.h"
 #include "vi_dpm.h"
 
@@ -41,7 +42,6 @@ static int amdgpu_powerplay_init(struct amdgpu_device *adev)
        amd_pp = &(adev->powerplay);
 
        if (adev->pp_enabled) {
-#ifdef CONFIG_DRM_AMD_POWERPLAY
                struct amd_pp_init *pp_init;
 
                pp_init = kzalloc(sizeof(struct amd_pp_init), GFP_KERNEL);
@@ -52,15 +52,21 @@ static int amdgpu_powerplay_init(struct amdgpu_device *adev)
                pp_init->chip_family = adev->family;
                pp_init->chip_id = adev->asic_type;
                pp_init->device = amdgpu_cgs_create_device(adev);
-               pp_init->powercontainment_enabled = amdgpu_powercontainment;
-
                ret = amd_powerplay_init(pp_init, amd_pp);
                kfree(pp_init);
-#endif
        } else {
                amd_pp->pp_handle = (void *)adev;
 
                switch (adev->asic_type) {
+#ifdef CONFIG_DRM_AMDGPU_SI
+               case CHIP_TAHITI:
+               case CHIP_PITCAIRN:
+               case CHIP_VERDE:
+               case CHIP_OLAND:
+               case CHIP_HAINAN:
+                       amd_pp->ip_funcs = &si_dpm_ip_funcs;
+               break;
+#endif
 #ifdef CONFIG_DRM_AMDGPU_CIK
                case CHIP_BONAIRE:
                case CHIP_HAWAII:
@@ -72,15 +78,6 @@ static int amdgpu_powerplay_init(struct amdgpu_device *adev)
                        amd_pp->ip_funcs = &kv_dpm_ip_funcs;
                        break;
 #endif
-               case CHIP_TOPAZ:
-                       amd_pp->ip_funcs = &iceland_dpm_ip_funcs;
-                       break;
-               case CHIP_TONGA:
-                       amd_pp->ip_funcs = &tonga_dpm_ip_funcs;
-                       break;
-               case CHIP_FIJI:
-                       amd_pp->ip_funcs = &fiji_dpm_ip_funcs;
-                       break;
                case CHIP_CARRIZO:
                case CHIP_STONEY:
                        amd_pp->ip_funcs = &cz_dpm_ip_funcs;
@@ -98,19 +95,17 @@ static int amdgpu_pp_early_init(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        int ret = 0;
 
-#ifdef CONFIG_DRM_AMD_POWERPLAY
        switch (adev->asic_type) {
        case CHIP_POLARIS11:
        case CHIP_POLARIS10:
-               adev->pp_enabled = true;
-               break;
        case CHIP_TONGA:
        case CHIP_FIJI:
-               adev->pp_enabled = (amdgpu_powerplay == 0) ? false : true;
+       case CHIP_TOPAZ:
+               adev->pp_enabled = true;
                break;
        case CHIP_CARRIZO:
        case CHIP_STONEY:
-               adev->pp_enabled = (amdgpu_powerplay > 0) ? true : false;
+               adev->pp_enabled = (amdgpu_powerplay == 0) ? false : true;
                break;
        /* These chips don't have powerplay implemenations */
        case CHIP_BONAIRE:
@@ -118,14 +113,10 @@ static int amdgpu_pp_early_init(void *handle)
        case CHIP_KABINI:
        case CHIP_MULLINS:
        case CHIP_KAVERI:
-       case CHIP_TOPAZ:
        default:
                adev->pp_enabled = false;
                break;
        }
-#else
-       adev->pp_enabled = false;
-#endif
 
        ret = amdgpu_powerplay_init(adev);
        if (ret)
@@ -147,12 +138,11 @@ static int amdgpu_pp_late_init(void *handle)
                ret = adev->powerplay.ip_funcs->late_init(
                                        adev->powerplay.pp_handle);
 
-#ifdef CONFIG_DRM_AMD_POWERPLAY
        if (adev->pp_enabled && adev->pm.dpm_enabled) {
                amdgpu_pm_sysfs_init(adev);
                amdgpu_dpm_dispatch_task(adev, AMD_PP_EVENT_COMPLETE_INIT, NULL, NULL);
        }
-#endif
+
        return ret;
 }
 
@@ -165,10 +155,8 @@ static int amdgpu_pp_sw_init(void *handle)
                ret = adev->powerplay.ip_funcs->sw_init(
                                        adev->powerplay.pp_handle);
 
-#ifdef CONFIG_DRM_AMD_POWERPLAY
        if (adev->pp_enabled)
                adev->pm.dpm_enabled = true;
-#endif
 
        return ret;
 }
@@ -219,7 +207,6 @@ static int amdgpu_pp_hw_fini(void *handle)
 
 static void amdgpu_pp_late_fini(void *handle)
 {
-#ifdef CONFIG_DRM_AMD_POWERPLAY
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        if (adev->pp_enabled) {
@@ -230,7 +217,6 @@ static void amdgpu_pp_late_fini(void *handle)
        if (adev->powerplay.ip_funcs->late_fini)
                adev->powerplay.ip_funcs->late_fini(
                          adev->powerplay.pp_handle);
-#endif
 }
 
 static int amdgpu_pp_suspend(void *handle)
index 85aeb0a..e1fa873 100644 (file)
@@ -222,33 +222,16 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
 
        /* Allocate ring buffer */
        if (ring->ring_obj == NULL) {
-               r = amdgpu_bo_create(adev, ring->ring_size, PAGE_SIZE, true,
-                                    AMDGPU_GEM_DOMAIN_GTT, 0,
-                                    NULL, NULL, &ring->ring_obj);
+               r = amdgpu_bo_create_kernel(adev, ring->ring_size, PAGE_SIZE,
+                                           AMDGPU_GEM_DOMAIN_GTT,
+                                           &ring->ring_obj,
+                                           &ring->gpu_addr,
+                                           (void **)&ring->ring);
                if (r) {
                        dev_err(adev->dev, "(%d) ring create failed\n", r);
                        return r;
                }
-               r = amdgpu_bo_reserve(ring->ring_obj, false);
-               if (unlikely(r != 0))
-                       return r;
-               r = amdgpu_bo_pin(ring->ring_obj, AMDGPU_GEM_DOMAIN_GTT,
-                                       &ring->gpu_addr);
-               if (r) {
-                       amdgpu_bo_unreserve(ring->ring_obj);
-                       dev_err(adev->dev, "(%d) ring pin failed\n", r);
-                       return r;
-               }
-               r = amdgpu_bo_kmap(ring->ring_obj,
-                                      (void **)&ring->ring);
-
                memset((void *)ring->ring, 0, ring->ring_size);
-
-               amdgpu_bo_unreserve(ring->ring_obj);
-               if (r) {
-                       dev_err(adev->dev, "(%d) ring map failed\n", r);
-                       return r;
-               }
        }
        ring->ptr_mask = (ring->ring_size / 4) - 1;
        ring->max_dw = max_dw;
@@ -269,29 +252,20 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
  */
 void amdgpu_ring_fini(struct amdgpu_ring *ring)
 {
-       int r;
-       struct amdgpu_bo *ring_obj;
-
-       ring_obj = ring->ring_obj;
        ring->ready = false;
-       ring->ring = NULL;
-       ring->ring_obj = NULL;
 
        amdgpu_wb_free(ring->adev, ring->cond_exe_offs);
        amdgpu_wb_free(ring->adev, ring->fence_offs);
        amdgpu_wb_free(ring->adev, ring->rptr_offs);
        amdgpu_wb_free(ring->adev, ring->wptr_offs);
 
-       if (ring_obj) {
-               r = amdgpu_bo_reserve(ring_obj, false);
-               if (likely(r == 0)) {
-                       amdgpu_bo_kunmap(ring_obj);
-                       amdgpu_bo_unpin(ring_obj);
-                       amdgpu_bo_unreserve(ring_obj);
-               }
-               amdgpu_bo_unref(&ring_obj);
-       }
+       amdgpu_bo_free_kernel(&ring->ring_obj,
+                             &ring->gpu_addr,
+                             (void **)&ring->ring);
+
        amdgpu_debugfs_ring_fini(ring);
+
+       ring->adev->rings[ring->idx] = NULL;
 }
 
 /*
index 05a53f4..b827c75 100644 (file)
@@ -111,7 +111,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
                amdgpu_bo_kunmap(gtt_obj[i]);
 
                r = amdgpu_copy_buffer(ring, gtt_addr, vram_addr,
-                                      size, NULL, &fence);
+                                      size, NULL, &fence, false);
 
                if (r) {
                        DRM_ERROR("Failed GTT->VRAM copy %d\n", i);
@@ -156,7 +156,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
                amdgpu_bo_kunmap(vram_obj);
 
                r = amdgpu_copy_buffer(ring, vram_addr, gtt_addr,
-                                      size, NULL, &fence);
+                                      size, NULL, &fence, false);
 
                if (r) {
                        DRM_ERROR("Failed VRAM->GTT copy %d\n", i);
index 0d8d65e..067e5e6 100644 (file)
@@ -247,7 +247,7 @@ DEFINE_EVENT(amdgpu_vm_mapping, amdgpu_vm_bo_mapping,
            TP_ARGS(mapping)
 );
 
-TRACE_EVENT(amdgpu_vm_set_page,
+TRACE_EVENT(amdgpu_vm_set_ptes,
            TP_PROTO(uint64_t pe, uint64_t addr, unsigned count,
                     uint32_t incr, uint32_t flags),
            TP_ARGS(pe, addr, count, incr, flags),
@@ -271,6 +271,24 @@ TRACE_EVENT(amdgpu_vm_set_page,
                      __entry->flags, __entry->count)
 );
 
+TRACE_EVENT(amdgpu_vm_copy_ptes,
+           TP_PROTO(uint64_t pe, uint64_t src, unsigned count),
+           TP_ARGS(pe, src, count),
+           TP_STRUCT__entry(
+                            __field(u64, pe)
+                            __field(u64, src)
+                            __field(u32, count)
+                            ),
+
+           TP_fast_assign(
+                          __entry->pe = pe;
+                          __entry->src = src;
+                          __entry->count = count;
+                          ),
+           TP_printk("pe=%010Lx, src=%010Lx, count=%u",
+                     __entry->pe, __entry->src, __entry->count)
+);
+
 TRACE_EVENT(amdgpu_vm_flush,
            TP_PROTO(uint64_t pd_addr, unsigned ring, unsigned id),
            TP_ARGS(pd_addr, ring, id),
index 716f2af..887483b 100644 (file)
@@ -34,6 +34,7 @@
 #include <ttm/ttm_placement.h>
 #include <ttm/ttm_module.h>
 #include <ttm/ttm_page_alloc.h>
+#include <ttm/ttm_memory.h>
 #include <drm/drmP.h>
 #include <drm/amdgpu_drm.h>
 #include <linux/seq_file.h>
@@ -74,7 +75,7 @@ static void amdgpu_ttm_mem_global_release(struct drm_global_reference *ref)
        ttm_mem_global_release(ref->object);
 }
 
-static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
+int amdgpu_ttm_global_init(struct amdgpu_device *adev)
 {
        struct drm_global_reference *global_ref;
        struct amdgpu_ring *ring;
@@ -88,10 +89,10 @@ static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
        global_ref->init = &amdgpu_ttm_mem_global_init;
        global_ref->release = &amdgpu_ttm_mem_global_release;
        r = drm_global_item_ref(global_ref);
-       if (r != 0) {
+       if (r) {
                DRM_ERROR("Failed setting up TTM memory accounting "
                          "subsystem.\n");
-               return r;
+               goto error_mem;
        }
 
        adev->mman.bo_global_ref.mem_glob =
@@ -102,26 +103,30 @@ static int amdgpu_ttm_global_init(struct amdgpu_device *adev)
        global_ref->init = &ttm_bo_global_init;
        global_ref->release = &ttm_bo_global_release;
        r = drm_global_item_ref(global_ref);
-       if (r != 0) {
+       if (r) {
                DRM_ERROR("Failed setting up TTM BO subsystem.\n");
-               drm_global_item_unref(&adev->mman.mem_global_ref);
-               return r;
+               goto error_bo;
        }
 
        ring = adev->mman.buffer_funcs_ring;
        rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_KERNEL];
        r = amd_sched_entity_init(&ring->sched, &adev->mman.entity,
                                  rq, amdgpu_sched_jobs);
-       if (r != 0) {
+       if (r) {
                DRM_ERROR("Failed setting up TTM BO move run queue.\n");
-               drm_global_item_unref(&adev->mman.mem_global_ref);
-               drm_global_item_unref(&adev->mman.bo_global_ref.ref);
-               return r;
+               goto error_entity;
        }
 
        adev->mman.mem_global_referenced = true;
 
        return 0;
+
+error_entity:
+       drm_global_item_unref(&adev->mman.bo_global_ref.ref);
+error_bo:
+       drm_global_item_unref(&adev->mman.mem_global_ref);
+error_mem:
+       return r;
 }
 
 static void amdgpu_ttm_global_fini(struct amdgpu_device *adev)
@@ -155,7 +160,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                man->default_caching = TTM_PL_FLAG_CACHED;
                break;
        case TTM_PL_TT:
-               man->func = &ttm_bo_manager_func;
+               man->func = &amdgpu_gtt_mgr_func;
                man->gpu_offset = adev->mc.gtt_start;
                man->available_caching = TTM_PL_MASK_CACHING;
                man->default_caching = TTM_PL_FLAG_CACHED;
@@ -190,12 +195,13 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
 static void amdgpu_evict_flags(struct ttm_buffer_object *bo,
                                struct ttm_placement *placement)
 {
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        static struct ttm_place placements = {
                .fpfn = 0,
                .lpfn = 0,
                .flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM
        };
+       unsigned i;
 
        if (!amdgpu_ttm_bo_is_amdgpu_bo(bo)) {
                placement->placement = &placements;
@@ -204,28 +210,44 @@ static void amdgpu_evict_flags(struct ttm_buffer_object *bo,
                placement->num_busy_placement = 1;
                return;
        }
-       rbo = container_of(bo, struct amdgpu_bo, tbo);
+       abo = container_of(bo, struct amdgpu_bo, tbo);
        switch (bo->mem.mem_type) {
        case TTM_PL_VRAM:
-               if (rbo->adev->mman.buffer_funcs_ring->ready == false)
-                       amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_CPU);
-               else
-                       amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_GTT);
+               if (abo->adev->mman.buffer_funcs_ring->ready == false) {
+                       amdgpu_ttm_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_CPU);
+               } else {
+                       amdgpu_ttm_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_GTT);
+                       for (i = 0; i < abo->placement.num_placement; ++i) {
+                               if (!(abo->placements[i].flags &
+                                     TTM_PL_FLAG_TT))
+                                       continue;
+
+                               if (abo->placements[i].lpfn)
+                                       continue;
+
+                               /* set an upper limit to force directly
+                                * allocating address space for the BO.
+                                */
+                               abo->placements[i].lpfn =
+                                       abo->adev->mc.gtt_size >> PAGE_SHIFT;
+                       }
+               }
                break;
        case TTM_PL_TT:
        default:
-               amdgpu_ttm_placement_from_domain(rbo, AMDGPU_GEM_DOMAIN_CPU);
+               amdgpu_ttm_placement_from_domain(abo, AMDGPU_GEM_DOMAIN_CPU);
        }
-       *placement = rbo->placement;
+       *placement = abo->placement;
 }
 
 static int amdgpu_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 {
-       struct amdgpu_bo *rbo = container_of(bo, struct amdgpu_bo, tbo);
+       struct amdgpu_bo *abo = container_of(bo, struct amdgpu_bo, tbo);
 
        if (amdgpu_ttm_tt_get_usermm(bo->ttm))
                return -EPERM;
-       return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp);
+       return drm_vma_node_verify_access(&abo->gem_base.vma_node,
+                                         filp->private_data);
 }
 
 static void amdgpu_move_null(struct ttm_buffer_object *bo,
@@ -251,26 +273,30 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo,
 
        adev = amdgpu_get_adev(bo->bdev);
        ring = adev->mman.buffer_funcs_ring;
-       old_start = (u64)old_mem->start << PAGE_SHIFT;
-       new_start = (u64)new_mem->start << PAGE_SHIFT;
 
        switch (old_mem->mem_type) {
-       case TTM_PL_VRAM:
-               old_start += adev->mc.vram_start;
-               break;
        case TTM_PL_TT:
-               old_start += adev->mc.gtt_start;
+               r = amdgpu_ttm_bind(bo, old_mem);
+               if (r)
+                       return r;
+
+       case TTM_PL_VRAM:
+               old_start = (u64)old_mem->start << PAGE_SHIFT;
+               old_start += bo->bdev->man[old_mem->mem_type].gpu_offset;
                break;
        default:
                DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
                return -EINVAL;
        }
        switch (new_mem->mem_type) {
-       case TTM_PL_VRAM:
-               new_start += adev->mc.vram_start;
-               break;
        case TTM_PL_TT:
-               new_start += adev->mc.gtt_start;
+               r = amdgpu_ttm_bind(bo, new_mem);
+               if (r)
+                       return r;
+
+       case TTM_PL_VRAM:
+               new_start = (u64)new_mem->start << PAGE_SHIFT;
+               new_start += bo->bdev->man[new_mem->mem_type].gpu_offset;
                break;
        default:
                DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
@@ -285,7 +311,7 @@ static int amdgpu_move_blit(struct ttm_buffer_object *bo,
 
        r = amdgpu_copy_buffer(ring, old_start, new_start,
                               new_mem->num_pages * PAGE_SIZE, /* bytes */
-                              bo->resv, &fence);
+                              bo->resv, &fence, false);
        if (r)
                return r;
 
@@ -314,7 +340,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo,
        placement.num_busy_placement = 1;
        placement.busy_placement = &placements;
        placements.fpfn = 0;
-       placements.lpfn = 0;
+       placements.lpfn = adev->mc.gtt_size >> PAGE_SHIFT;
        placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
        r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
                             interruptible, no_wait_gpu);
@@ -335,7 +361,7 @@ static int amdgpu_move_vram_ram(struct ttm_buffer_object *bo,
        if (unlikely(r)) {
                goto out_cleanup;
        }
-       r = ttm_bo_move_ttm(bo, true, interruptible, no_wait_gpu, new_mem);
+       r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, new_mem);
 out_cleanup:
        ttm_bo_mem_put(bo, &tmp_mem);
        return r;
@@ -361,14 +387,14 @@ static int amdgpu_move_ram_vram(struct ttm_buffer_object *bo,
        placement.num_busy_placement = 1;
        placement.busy_placement = &placements;
        placements.fpfn = 0;
-       placements.lpfn = 0;
+       placements.lpfn = adev->mc.gtt_size >> PAGE_SHIFT;
        placements.flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
        r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
                             interruptible, no_wait_gpu);
        if (unlikely(r)) {
                return r;
        }
-       r = ttm_bo_move_ttm(bo, true, interruptible, no_wait_gpu, &tmp_mem);
+       r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, &tmp_mem);
        if (unlikely(r)) {
                goto out_cleanup;
        }
@@ -435,8 +461,7 @@ static int amdgpu_bo_move(struct ttm_buffer_object *bo,
 
        if (r) {
 memcpy:
-               r = ttm_bo_move_memcpy(bo, evict, interruptible,
-                                      no_wait_gpu, new_mem);
+               r = ttm_bo_move_memcpy(bo, interruptible, no_wait_gpu, new_mem);
                if (r) {
                        return r;
                }
@@ -524,6 +549,7 @@ struct amdgpu_ttm_tt {
        spinlock_t              guptasklock;
        struct list_head        guptasks;
        atomic_t                mmu_invalidations;
+       struct list_head        list;
 };
 
 int amdgpu_ttm_tt_get_user_pages(struct ttm_tt *ttm, struct page **pages)
@@ -641,7 +667,6 @@ static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
                                   struct ttm_mem_reg *bo_mem)
 {
        struct amdgpu_ttm_tt *gtt = (void*)ttm;
-       uint32_t flags = amdgpu_ttm_tt_pte_flags(gtt->adev, ttm, bo_mem);
        int r;
 
        if (gtt->userptr) {
@@ -651,7 +676,6 @@ static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
                        return r;
                }
        }
-       gtt->offset = (unsigned long)(bo_mem->start << PAGE_SHIFT);
        if (!ttm->num_pages) {
                WARN(1, "nothing to bind %lu pages for mreg %p back %p!\n",
                     ttm->num_pages, bo_mem, ttm);
@@ -662,14 +686,71 @@ static int amdgpu_ttm_backend_bind(struct ttm_tt *ttm,
            bo_mem->mem_type == AMDGPU_PL_OA)
                return -EINVAL;
 
+       return 0;
+}
+
+bool amdgpu_ttm_is_bound(struct ttm_tt *ttm)
+{
+       struct amdgpu_ttm_tt *gtt = (void *)ttm;
+
+       return gtt && !list_empty(&gtt->list);
+}
+
+int amdgpu_ttm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *bo_mem)
+{
+       struct ttm_tt *ttm = bo->ttm;
+       struct amdgpu_ttm_tt *gtt = (void *)bo->ttm;
+       uint32_t flags;
+       int r;
+
+       if (!ttm || amdgpu_ttm_is_bound(ttm))
+               return 0;
+
+       r = amdgpu_gtt_mgr_alloc(&bo->bdev->man[TTM_PL_TT], bo,
+                                NULL, bo_mem);
+       if (r) {
+               DRM_ERROR("Failed to allocate GTT address space (%d)\n", r);
+               return r;
+       }
+
+       flags = amdgpu_ttm_tt_pte_flags(gtt->adev, ttm, bo_mem);
+       gtt->offset = (u64)bo_mem->start << PAGE_SHIFT;
        r = amdgpu_gart_bind(gtt->adev, gtt->offset, ttm->num_pages,
                ttm->pages, gtt->ttm.dma_address, flags);
 
        if (r) {
-               DRM_ERROR("failed to bind %lu pages at 0x%08X\n",
-                         ttm->num_pages, (unsigned)gtt->offset);
+               DRM_ERROR("failed to bind %lu pages at 0x%08llX\n",
+                         ttm->num_pages, gtt->offset);
                return r;
        }
+       spin_lock(&gtt->adev->gtt_list_lock);
+       list_add_tail(&gtt->list, &gtt->adev->gtt_list);
+       spin_unlock(&gtt->adev->gtt_list_lock);
+       return 0;
+}
+
+int amdgpu_ttm_recover_gart(struct amdgpu_device *adev)
+{
+       struct amdgpu_ttm_tt *gtt, *tmp;
+       struct ttm_mem_reg bo_mem;
+       uint32_t flags;
+       int r;
+
+       bo_mem.mem_type = TTM_PL_TT;
+       spin_lock(&adev->gtt_list_lock);
+       list_for_each_entry_safe(gtt, tmp, &adev->gtt_list, list) {
+               flags = amdgpu_ttm_tt_pte_flags(gtt->adev, &gtt->ttm.ttm, &bo_mem);
+               r = amdgpu_gart_bind(adev, gtt->offset, gtt->ttm.ttm.num_pages,
+                                    gtt->ttm.ttm.pages, gtt->ttm.dma_address,
+                                    flags);
+               if (r) {
+                       spin_unlock(&adev->gtt_list_lock);
+                       DRM_ERROR("failed to bind %lu pages at 0x%08llX\n",
+                                 gtt->ttm.ttm.num_pages, gtt->offset);
+                       return r;
+               }
+       }
+       spin_unlock(&adev->gtt_list_lock);
        return 0;
 }
 
@@ -677,12 +758,19 @@ static int amdgpu_ttm_backend_unbind(struct ttm_tt *ttm)
 {
        struct amdgpu_ttm_tt *gtt = (void *)ttm;
 
+       if (gtt->userptr)
+               amdgpu_ttm_tt_unpin_userptr(ttm);
+
+       if (!amdgpu_ttm_is_bound(ttm))
+               return 0;
+
        /* unbind shouldn't be done for GDS/GWS/OA in ttm_bo_clean_mm */
        if (gtt->adev->gart.ready)
                amdgpu_gart_unbind(gtt->adev, gtt->offset, ttm->num_pages);
 
-       if (gtt->userptr)
-               amdgpu_ttm_tt_unpin_userptr(ttm);
+       spin_lock(&gtt->adev->gtt_list_lock);
+       list_del_init(&gtt->list);
+       spin_unlock(&gtt->adev->gtt_list_lock);
 
        return 0;
 }
@@ -720,6 +808,7 @@ static struct ttm_tt *amdgpu_ttm_tt_create(struct ttm_bo_device *bdev,
                kfree(gtt);
                return NULL;
        }
+       INIT_LIST_HEAD(&gtt->list);
        return &gtt->ttm.ttm;
 }
 
@@ -991,10 +1080,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
        unsigned i, j;
        int r;
 
-       r = amdgpu_ttm_global_init(adev);
-       if (r) {
-               return r;
-       }
        /* No others user of address space so set it to 0 */
        r = ttm_bo_device_init(&adev->mman.bdev,
                               adev->mman.bo_global_ref.ref.object,
@@ -1159,7 +1244,7 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring,
                       uint64_t dst_offset,
                       uint32_t byte_count,
                       struct reservation_object *resv,
-                      struct fence **fence)
+                      struct fence **fence, bool direct_submit)
 {
        struct amdgpu_device *adev = ring->adev;
        struct amdgpu_job *job;
@@ -1201,10 +1286,81 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring,
                byte_count -= cur_size_in_bytes;
        }
 
+       amdgpu_ring_pad_ib(ring, &job->ibs[0]);
+       WARN_ON(job->ibs[0].length_dw > num_dw);
+       if (direct_submit) {
+               r = amdgpu_ib_schedule(ring, job->num_ibs, job->ibs,
+                                      NULL, NULL, fence);
+               job->fence = fence_get(*fence);
+               if (r)
+                       DRM_ERROR("Error scheduling IBs (%d)\n", r);
+               amdgpu_job_free(job);
+       } else {
+               r = amdgpu_job_submit(job, ring, &adev->mman.entity,
+                                     AMDGPU_FENCE_OWNER_UNDEFINED, fence);
+               if (r)
+                       goto error_free;
+       }
+
+       return r;
+
+error_free:
+       amdgpu_job_free(job);
+       return r;
+}
+
+int amdgpu_fill_buffer(struct amdgpu_bo *bo,
+               uint32_t src_data,
+               struct reservation_object *resv,
+               struct fence **fence)
+{
+       struct amdgpu_device *adev = bo->adev;
+       struct amdgpu_job *job;
+       struct amdgpu_ring *ring = adev->mman.buffer_funcs_ring;
+
+       uint32_t max_bytes, byte_count;
+       uint64_t dst_offset;
+       unsigned int num_loops, num_dw;
+       unsigned int i;
+       int r;
+
+       byte_count = bo->tbo.num_pages << PAGE_SHIFT;
+       max_bytes = adev->mman.buffer_funcs->fill_max_bytes;
+       num_loops = DIV_ROUND_UP(byte_count, max_bytes);
+       num_dw = num_loops * adev->mman.buffer_funcs->fill_num_dw;
+
+       /* for IB padding */
+       while (num_dw & 0x7)
+               num_dw++;
+
+       r = amdgpu_job_alloc_with_ib(adev, num_dw * 4, &job);
+       if (r)
+               return r;
+
+       if (resv) {
+               r = amdgpu_sync_resv(adev, &job->sync, resv,
+                               AMDGPU_FENCE_OWNER_UNDEFINED);
+               if (r) {
+                       DRM_ERROR("sync failed (%d).\n", r);
+                       goto error_free;
+               }
+       }
+
+       dst_offset = bo->tbo.mem.start << PAGE_SHIFT;
+       for (i = 0; i < num_loops; i++) {
+               uint32_t cur_size_in_bytes = min(byte_count, max_bytes);
+
+               amdgpu_emit_fill_buffer(adev, &job->ibs[0], src_data,
+                               dst_offset, cur_size_in_bytes);
+
+               dst_offset += cur_size_in_bytes;
+               byte_count -= cur_size_in_bytes;
+       }
+
        amdgpu_ring_pad_ib(ring, &job->ibs[0]);
        WARN_ON(job->ibs[0].length_dw > num_dw);
        r = amdgpu_job_submit(job, ring, &adev->mman.entity,
-                             AMDGPU_FENCE_OWNER_UNDEFINED, fence);
+                       AMDGPU_FENCE_OWNER_UNDEFINED, fence);
        if (r)
                goto error_free;
 
@@ -1395,3 +1551,8 @@ static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev)
 
 #endif
 }
+
+u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev)
+{
+       return ttm_get_kernel_zone_memory_size(adev->mman.mem_global_ref.object);
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h
new file mode 100644 (file)
index 0000000..9812c80
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016 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 __AMDGPU_TTM_H__
+#define __AMDGPU_TTM_H__
+
+#include "gpu_scheduler.h"
+
+#define AMDGPU_PL_GDS          (TTM_PL_PRIV + 0)
+#define AMDGPU_PL_GWS          (TTM_PL_PRIV + 1)
+#define AMDGPU_PL_OA           (TTM_PL_PRIV + 2)
+
+#define AMDGPU_PL_FLAG_GDS             (TTM_PL_FLAG_PRIV << 0)
+#define AMDGPU_PL_FLAG_GWS             (TTM_PL_FLAG_PRIV << 1)
+#define AMDGPU_PL_FLAG_OA              (TTM_PL_FLAG_PRIV << 2)
+
+#define AMDGPU_TTM_LRU_SIZE    20
+
+struct amdgpu_mman_lru {
+       struct list_head                *lru[TTM_NUM_MEM_TYPES];
+       struct list_head                *swap_lru;
+};
+
+struct amdgpu_mman {
+       struct ttm_bo_global_ref        bo_global_ref;
+       struct drm_global_reference     mem_global_ref;
+       struct ttm_bo_device            bdev;
+       bool                            mem_global_referenced;
+       bool                            initialized;
+
+#if defined(CONFIG_DEBUG_FS)
+       struct dentry                   *vram;
+       struct dentry                   *gtt;
+#endif
+
+       /* buffer handling */
+       const struct amdgpu_buffer_funcs        *buffer_funcs;
+       struct amdgpu_ring                      *buffer_funcs_ring;
+       /* Scheduler entity for buffer moves */
+       struct amd_sched_entity                 entity;
+
+       /* custom LRU management */
+       struct amdgpu_mman_lru                  log2_size[AMDGPU_TTM_LRU_SIZE];
+       /* guard for log2_size array, don't add anything in between */
+       struct amdgpu_mman_lru                  guard;
+};
+
+extern const struct ttm_mem_type_manager_func amdgpu_gtt_mgr_func;
+
+int amdgpu_gtt_mgr_alloc(struct ttm_mem_type_manager *man,
+                        struct ttm_buffer_object *tbo,
+                        const struct ttm_place *place,
+                        struct ttm_mem_reg *mem);
+
+int amdgpu_copy_buffer(struct amdgpu_ring *ring,
+                      uint64_t src_offset,
+                      uint64_t dst_offset,
+                      uint32_t byte_count,
+                      struct reservation_object *resv,
+                      struct fence **fence, bool direct_submit);
+int amdgpu_fill_buffer(struct amdgpu_bo *bo,
+                       uint32_t src_data,
+                       struct reservation_object *resv,
+                       struct fence **fence);
+
+int amdgpu_mmap(struct file *filp, struct vm_area_struct *vma);
+bool amdgpu_ttm_is_bound(struct ttm_tt *ttm);
+int amdgpu_ttm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *bo_mem);
+
+#endif
index 5cc95f1..cb3d252 100644 (file)
@@ -247,40 +247,32 @@ int amdgpu_ucode_init_bo(struct amdgpu_device *adev)
        const struct common_firmware_header *header = NULL;
 
        err = amdgpu_bo_create(adev, adev->firmware.fw_size, PAGE_SIZE, true,
-                       AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, bo);
+                              AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, bo);
        if (err) {
                dev_err(adev->dev, "(%d) Firmware buffer allocate failed\n", err);
-               err = -ENOMEM;
                goto failed;
        }
 
        err = amdgpu_bo_reserve(*bo, false);
        if (err) {
-               amdgpu_bo_unref(bo);
                dev_err(adev->dev, "(%d) Firmware buffer reserve failed\n", err);
-               goto failed;
+               goto failed_reserve;
        }
 
        err = amdgpu_bo_pin(*bo, AMDGPU_GEM_DOMAIN_GTT, &fw_mc_addr);
        if (err) {
-               amdgpu_bo_unreserve(*bo);
-               amdgpu_bo_unref(bo);
                dev_err(adev->dev, "(%d) Firmware buffer pin failed\n", err);
-               goto failed;
+               goto failed_pin;
        }
 
        err = amdgpu_bo_kmap(*bo, &fw_buf_ptr);
        if (err) {
                dev_err(adev->dev, "(%d) Firmware buffer kmap failed\n", err);
-               amdgpu_bo_unpin(*bo);
-               amdgpu_bo_unreserve(*bo);
-               amdgpu_bo_unref(bo);
-               goto failed;
+               goto failed_kmap;
        }
 
        amdgpu_bo_unreserve(*bo);
 
-       fw_offset = 0;
        for (i = 0; i < AMDGPU_UCODE_ID_MAXIMUM; i++) {
                ucode = &adev->firmware.ucode[i];
                if (ucode->fw) {
@@ -290,10 +282,16 @@ int amdgpu_ucode_init_bo(struct amdgpu_device *adev)
                        fw_offset += ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE);
                }
        }
+       return 0;
 
+failed_kmap:
+       amdgpu_bo_unpin(*bo);
+failed_pin:
+       amdgpu_bo_unreserve(*bo);
+failed_reserve:
+       amdgpu_bo_unref(bo);
 failed:
-       if (err)
-               adev->firmware.smu_load = false;
+       adev->firmware.smu_load = false;
 
        return err;
 }
index 4aa993d..e3281ca 100644 (file)
@@ -201,39 +201,14 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
        bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8)
                  +  AMDGPU_UVD_STACK_SIZE + AMDGPU_UVD_HEAP_SIZE
                  +  AMDGPU_UVD_SESSION_SIZE * adev->uvd.max_handles;
-       r = amdgpu_bo_create(adev, bo_size, PAGE_SIZE, true,
-                            AMDGPU_GEM_DOMAIN_VRAM,
-                            AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                            NULL, NULL, &adev->uvd.vcpu_bo);
+       r = amdgpu_bo_create_kernel(adev, bo_size, PAGE_SIZE,
+                                   AMDGPU_GEM_DOMAIN_VRAM, &adev->uvd.vcpu_bo,
+                                   &adev->uvd.gpu_addr, &adev->uvd.cpu_addr);
        if (r) {
                dev_err(adev->dev, "(%d) failed to allocate UVD bo\n", r);
                return r;
        }
 
-       r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false);
-       if (r) {
-               amdgpu_bo_unref(&adev->uvd.vcpu_bo);
-               dev_err(adev->dev, "(%d) failed to reserve UVD bo\n", r);
-               return r;
-       }
-
-       r = amdgpu_bo_pin(adev->uvd.vcpu_bo, AMDGPU_GEM_DOMAIN_VRAM,
-                         &adev->uvd.gpu_addr);
-       if (r) {
-               amdgpu_bo_unreserve(adev->uvd.vcpu_bo);
-               amdgpu_bo_unref(&adev->uvd.vcpu_bo);
-               dev_err(adev->dev, "(%d) UVD bo pin failed\n", r);
-               return r;
-       }
-
-       r = amdgpu_bo_kmap(adev->uvd.vcpu_bo, &adev->uvd.cpu_addr);
-       if (r) {
-               dev_err(adev->dev, "(%d) UVD map failed\n", r);
-               return r;
-       }
-
-       amdgpu_bo_unreserve(adev->uvd.vcpu_bo);
-
        ring = &adev->uvd.ring;
        rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL];
        r = amd_sched_entity_init(&ring->sched, &adev->uvd.entity,
@@ -274,22 +249,13 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
 
 int amdgpu_uvd_sw_fini(struct amdgpu_device *adev)
 {
-       int r;
-
        kfree(adev->uvd.saved_bo);
 
        amd_sched_entity_fini(&adev->uvd.ring.sched, &adev->uvd.entity);
 
-       if (adev->uvd.vcpu_bo) {
-               r = amdgpu_bo_reserve(adev->uvd.vcpu_bo, false);
-               if (!r) {
-                       amdgpu_bo_kunmap(adev->uvd.vcpu_bo);
-                       amdgpu_bo_unpin(adev->uvd.vcpu_bo);
-                       amdgpu_bo_unreserve(adev->uvd.vcpu_bo);
-               }
-
-               amdgpu_bo_unref(&adev->uvd.vcpu_bo);
-       }
+       amdgpu_bo_free_kernel(&adev->uvd.vcpu_bo,
+                             &adev->uvd.gpu_addr,
+                             (void **)&adev->uvd.cpu_addr);
 
        amdgpu_ring_fini(&adev->uvd.ring);
 
@@ -323,7 +289,7 @@ int amdgpu_uvd_suspend(struct amdgpu_device *adev)
        if (!adev->uvd.saved_bo)
                return -ENOMEM;
 
-       memcpy(adev->uvd.saved_bo, ptr, size);
+       memcpy_fromio(adev->uvd.saved_bo, ptr, size);
 
        return 0;
 }
@@ -340,7 +306,7 @@ int amdgpu_uvd_resume(struct amdgpu_device *adev)
        ptr = adev->uvd.cpu_addr;
 
        if (adev->uvd.saved_bo != NULL) {
-               memcpy(ptr, adev->uvd.saved_bo, size);
+               memcpy_toio(ptr, adev->uvd.saved_bo, size);
                kfree(adev->uvd.saved_bo);
                adev->uvd.saved_bo = NULL;
        } else {
@@ -349,11 +315,11 @@ int amdgpu_uvd_resume(struct amdgpu_device *adev)
 
                hdr = (const struct common_firmware_header *)adev->uvd.fw->data;
                offset = le32_to_cpu(hdr->ucode_array_offset_bytes);
-               memcpy(adev->uvd.cpu_addr, (adev->uvd.fw->data) + offset,
-                       (adev->uvd.fw->size) - offset);
+               memcpy_toio(adev->uvd.cpu_addr, adev->uvd.fw->data + offset,
+                           le32_to_cpu(hdr->ucode_size_bytes));
                size -= le32_to_cpu(hdr->ucode_size_bytes);
                ptr += le32_to_cpu(hdr->ucode_size_bytes);
-               memset(ptr, 0, size);
+               memset_io(ptr, 0, size);
        }
 
        return 0;
@@ -385,12 +351,12 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
        }
 }
 
-static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *rbo)
+static void amdgpu_uvd_force_into_uvd_segment(struct amdgpu_bo *abo)
 {
        int i;
-       for (i = 0; i < rbo->placement.num_placement; ++i) {
-               rbo->placements[i].fpfn = 0 >> PAGE_SHIFT;
-               rbo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT;
+       for (i = 0; i < abo->placement.num_placement; ++i) {
+               abo->placements[i].fpfn = 0 >> PAGE_SHIFT;
+               abo->placements[i].lpfn = (256 * 1024 * 1024) >> PAGE_SHIFT;
        }
 }
 
@@ -843,6 +809,7 @@ static int amdgpu_uvd_cs_reg(struct amdgpu_uvd_cs_ctx *ctx,
                                return r;
                        break;
                case mmUVD_ENGINE_CNTL:
+               case mmUVD_NO_OP:
                        break;
                default:
                        DRM_ERROR("Invalid reg 0x%X!\n", reg);
@@ -915,6 +882,10 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx)
                return -EINVAL;
        }
 
+       r = amdgpu_cs_sysvm_access_required(parser);
+       if (r)
+               return r;
+
        ctx.parser = parser;
        ctx.buf_sizes = buf_sizes;
        ctx.ib_idx = ib_idx;
@@ -981,8 +952,10 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring, struct amdgpu_bo *bo,
        ib->ptr[3] = addr >> 32;
        ib->ptr[4] = PACKET0(mmUVD_GPCOM_VCPU_CMD, 0);
        ib->ptr[5] = 0;
-       for (i = 6; i < 16; ++i)
-               ib->ptr[i] = PACKET2(0);
+       for (i = 6; i < 16; i += 2) {
+               ib->ptr[i] = PACKET0(mmUVD_NO_OP, 0);
+               ib->ptr[i+1] = 0;
+       }
        ib->length_dw = 16;
 
        if (direct) {
@@ -1114,15 +1087,9 @@ static void amdgpu_uvd_idle_work_handler(struct work_struct *work)
 {
        struct amdgpu_device *adev =
                container_of(work, struct amdgpu_device, uvd.idle_work.work);
-       unsigned i, fences, handles = 0;
-
-       fences = amdgpu_fence_count_emitted(&adev->uvd.ring);
-
-       for (i = 0; i < adev->uvd.max_handles; ++i)
-               if (atomic_read(&adev->uvd.handles[i]))
-                       ++handles;
+       unsigned fences = amdgpu_fence_count_emitted(&adev->uvd.ring);
 
-       if (fences == 0 && handles == 0) {
+       if (fences == 0) {
                if (adev->pm.dpm_enabled) {
                        amdgpu_dpm_enable_uvd(adev, false);
                } else {
index 05865ce..7fe8fd8 100644 (file)
@@ -210,6 +210,8 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
  */
 int amdgpu_vce_sw_fini(struct amdgpu_device *adev)
 {
+       unsigned i;
+
        if (adev->vce.vcpu_bo == NULL)
                return 0;
 
@@ -217,8 +219,8 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev)
 
        amdgpu_bo_unref(&adev->vce.vcpu_bo);
 
-       amdgpu_ring_fini(&adev->vce.ring[0]);
-       amdgpu_ring_fini(&adev->vce.ring[1]);
+       for (i = 0; i < adev->vce.num_rings; i++)
+               amdgpu_ring_fini(&adev->vce.ring[i]);
 
        release_firmware(adev->vce.fw);
        mutex_destroy(&adev->vce.idle_mutex);
@@ -282,8 +284,8 @@ int amdgpu_vce_resume(struct amdgpu_device *adev)
 
        hdr = (const struct common_firmware_header *)adev->vce.fw->data;
        offset = le32_to_cpu(hdr->ucode_array_offset_bytes);
-       memcpy(cpu_addr, (adev->vce.fw->data) + offset,
-               (adev->vce.fw->size) - offset);
+       memcpy_toio(cpu_addr, adev->vce.fw->data + offset,
+                   adev->vce.fw->size - offset);
 
        amdgpu_bo_kunmap(adev->vce.vcpu_bo);
 
@@ -303,9 +305,12 @@ static void amdgpu_vce_idle_work_handler(struct work_struct *work)
 {
        struct amdgpu_device *adev =
                container_of(work, struct amdgpu_device, vce.idle_work.work);
+       unsigned i, count = 0;
+
+       for (i = 0; i < adev->vce.num_rings; i++)
+               count += amdgpu_fence_count_emitted(&adev->vce.ring[i]);
 
-       if ((amdgpu_fence_count_emitted(&adev->vce.ring[0]) == 0) &&
-           (amdgpu_fence_count_emitted(&adev->vce.ring[1]) == 0)) {
+       if (count == 0) {
                if (adev->pm.dpm_enabled) {
                        amdgpu_dpm_enable_vce(adev, false);
                } else {
@@ -634,7 +639,11 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)
        uint32_t allocated = 0;
        uint32_t tmp, handle = 0;
        uint32_t *size = &tmp;
-       int i, r = 0, idx = 0;
+       int i, r, idx = 0;
+
+       r = amdgpu_cs_sysvm_access_required(p);
+       if (r)
+               return r;
 
        while (idx < ib->length_dw) {
                uint32_t len = amdgpu_get_ib_value(p, ib_idx, idx);
@@ -687,6 +696,21 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx)
                case 0x04000008: /* rdo */
                case 0x04000009: /* vui */
                case 0x05000002: /* auxiliary buffer */
+               case 0x05000009: /* clock table */
+                       break;
+
+               case 0x0500000c: /* hw config */
+                       switch (p->adev->asic_type) {
+#ifdef CONFIG_DRM_AMDGPU_CIK
+                       case CHIP_KAVERI:
+                       case CHIP_MULLINS:
+#endif
+                       case CHIP_CARRIZO:
+                               break;
+                       default:
+                               r = -EINVAL;
+                               goto out;
+                       }
                        break;
 
                case 0x03000001: /* encode */
@@ -799,6 +823,18 @@ void amdgpu_vce_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
        amdgpu_ring_write(ring, VCE_CMD_END);
 }
 
+unsigned amdgpu_vce_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               4; /* amdgpu_vce_ring_emit_ib */
+}
+
+unsigned amdgpu_vce_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               6; /* amdgpu_vce_ring_emit_fence  x1 no user fence */
+}
+
 /**
  * amdgpu_vce_ring_test_ring - test if VCE ring is working
  *
@@ -850,8 +886,8 @@ int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring, long timeout)
        struct fence *fence = NULL;
        long r;
 
-       /* skip vce ring1 ib test for now, since it's not reliable */
-       if (ring == &ring->adev->vce.ring[1])
+       /* skip vce ring1/2 ib test for now, since it's not reliable */
+       if (ring != &ring->adev->vce.ring[0])
                return 0;
 
        r = amdgpu_vce_get_create_msg(ring, 1, NULL);
index 63f83d0..12729d2 100644 (file)
@@ -42,5 +42,7 @@ int amdgpu_vce_ring_test_ring(struct amdgpu_ring *ring);
 int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring, long timeout);
 void amdgpu_vce_ring_begin_use(struct amdgpu_ring *ring);
 void amdgpu_vce_ring_end_use(struct amdgpu_ring *ring);
+unsigned amdgpu_vce_ring_get_emit_ib_size(struct amdgpu_ring *ring);
+unsigned amdgpu_vce_ring_get_dma_frame_size(struct amdgpu_ring *ring);
 
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.h
new file mode 100644 (file)
index 0000000..2c37a37
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2016 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.
+ *
+ * Author: Monk.liu@amd.com
+ */
+#ifndef AMDGPU_VIRT_H
+#define AMDGPU_VIRT_H
+
+#define AMDGPU_SRIOV_CAPS_SRIOV_VBIOS  (1 << 0) /* vBIOS is sr-iov ready */
+#define AMDGPU_SRIOV_CAPS_ENABLE_IOV   (1 << 1) /* sr-iov is enabled on this GPU */
+#define AMDGPU_SRIOV_CAPS_IS_VF        (1 << 2) /* this GPU is a virtual function */
+#define AMDGPU_PASSTHROUGH_MODE        (1 << 3) /* thw whole GPU is pass through for VM */
+/* GPU virtualization */
+struct amdgpu_virtualization {
+       uint32_t virtual_caps;
+};
+
+#define amdgpu_sriov_enabled(adev) \
+((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_ENABLE_IOV)
+
+#define amdgpu_sriov_vf(adev) \
+((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_IS_VF)
+
+#define amdgpu_sriov_bios(adev) \
+((adev)->virtualization.virtual_caps & AMDGPU_SRIOV_CAPS_SRIOV_VBIOS)
+
+#define amdgpu_passthrough(adev) \
+((adev)->virtualization.virtual_caps & AMDGPU_PASSTHROUGH_MODE)
+
+static inline bool is_virtual_machine(void)
+{
+#ifdef CONFIG_X86
+       return boot_cpu_has(X86_FEATURE_HYPERVISOR);
+#else
+       return false;
+#endif
+}
+
+#endif
\ No newline at end of file
index 80120fa..06f2432 100644 (file)
  * SI supports 16.
  */
 
-/* Special value that no flush is necessary */
-#define AMDGPU_VM_NO_FLUSH (~0ll)
-
 /* Local structure. Encapsulate some VM table update parameters to reduce
  * the number of function parameters
  */
-struct amdgpu_vm_update_params {
+struct amdgpu_pte_update_params {
+       /* amdgpu device we do this update for */
+       struct amdgpu_device *adev;
        /* address where to copy page table entries from */
        uint64_t src;
-       /* DMA addresses to use for mapping */
-       dma_addr_t *pages_addr;
        /* indirect buffer to fill with commands */
        struct amdgpu_ib *ib;
+       /* Function which actually does the update */
+       void (*func)(struct amdgpu_pte_update_params *params, uint64_t pe,
+                    uint64_t addr, unsigned count, uint32_t incr,
+                    uint32_t flags);
+       /* indicate update pt or its shadow */
+       bool shadow;
 };
 
 /**
@@ -467,10 +470,9 @@ struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm,
 }
 
 /**
- * amdgpu_vm_update_pages - helper to call the right asic function
+ * amdgpu_vm_do_set_ptes - helper to call the right asic function
  *
- * @adev: amdgpu_device pointer
- * @vm_update_params: see amdgpu_vm_update_params definition
+ * @params: see amdgpu_pte_update_params definition
  * @pe: addr of the page entry
  * @addr: dst addr to write into pe
  * @count: number of page entries to update
@@ -480,32 +482,46 @@ struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm,
  * Traces the parameters and calls the right asic functions
  * to setup the page table using the DMA.
  */
-static void amdgpu_vm_update_pages(struct amdgpu_device *adev,
-                                  struct amdgpu_vm_update_params
-                                       *vm_update_params,
+static void amdgpu_vm_do_set_ptes(struct amdgpu_pte_update_params *params,
+                                 uint64_t pe, uint64_t addr,
+                                 unsigned count, uint32_t incr,
+                                 uint32_t flags)
+{
+       trace_amdgpu_vm_set_ptes(pe, addr, count, incr, flags);
+
+       if (count < 3) {
+               amdgpu_vm_write_pte(params->adev, params->ib, pe,
+                                   addr | flags, count, incr);
+
+       } else {
+               amdgpu_vm_set_pte_pde(params->adev, params->ib, pe, addr,
+                                     count, incr, flags);
+       }
+}
+
+/**
+ * amdgpu_vm_do_copy_ptes - copy the PTEs from the GART
+ *
+ * @params: see amdgpu_pte_update_params definition
+ * @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: hw access flags
+ *
+ * Traces the parameters and calls the DMA function to copy the PTEs.
+ */
+static void amdgpu_vm_do_copy_ptes(struct amdgpu_pte_update_params *params,
                                   uint64_t pe, uint64_t addr,
                                   unsigned count, uint32_t incr,
                                   uint32_t flags)
 {
-       trace_amdgpu_vm_set_page(pe, addr, count, incr, flags);
-
-       if (vm_update_params->src) {
-               amdgpu_vm_copy_pte(adev, vm_update_params->ib,
-                       pe, (vm_update_params->src + (addr >> 12) * 8), count);
+       uint64_t src = (params->src + (addr >> 12) * 8);
 
-       } else if (vm_update_params->pages_addr) {
-               amdgpu_vm_write_pte(adev, vm_update_params->ib,
-                       vm_update_params->pages_addr,
-                       pe, addr, count, incr, flags);
 
-       } else if (count < 3) {
-               amdgpu_vm_write_pte(adev, vm_update_params->ib, NULL, pe, addr,
-                                   count, incr, flags);
+       trace_amdgpu_vm_copy_ptes(pe, src, count);
 
-       } else {
-               amdgpu_vm_set_pte_pde(adev, vm_update_params->ib, pe, addr,
-                                     count, incr, flags);
-       }
+       amdgpu_vm_copy_pte(params->adev, params->ib, pe, src, count);
 }
 
 /**
@@ -523,12 +539,11 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
        struct amdgpu_ring *ring;
        struct fence *fence = NULL;
        struct amdgpu_job *job;
-       struct amdgpu_vm_update_params vm_update_params;
+       struct amdgpu_pte_update_params params;
        unsigned entries;
        uint64_t addr;
        int r;
 
-       memset(&vm_update_params, 0, sizeof(vm_update_params));
        ring = container_of(vm->entity.sched, struct amdgpu_ring, sched);
 
        r = reservation_object_reserve_shared(bo->tbo.resv);
@@ -539,6 +554,10 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
        if (r)
                goto error;
 
+       r = amdgpu_ttm_bind(&bo->tbo, &bo->tbo.mem);
+       if (r)
+               goto error;
+
        addr = amdgpu_bo_gpu_offset(bo);
        entries = amdgpu_bo_size(bo) / 8;
 
@@ -546,9 +565,10 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
        if (r)
                goto error;
 
-       vm_update_params.ib = &job->ibs[0];
-       amdgpu_vm_update_pages(adev, &vm_update_params, addr, 0, entries,
-                              0, 0);
+       memset(&params, 0, sizeof(params));
+       params.adev = adev;
+       params.ib = &job->ibs[0];
+       amdgpu_vm_do_set_ptes(&params, addr, 0, entries, 0, 0);
        amdgpu_ring_pad_ib(ring, &job->ibs[0]);
 
        WARN_ON(job->ibs[0].length_dw > 64);
@@ -577,55 +597,46 @@ error:
  * Look up the physical address of the page that the pte resolves
  * to and return the pointer for the page table entry.
  */
-uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr)
+static uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr)
 {
        uint64_t result;
 
-       if (pages_addr) {
-               /* page table offset */
-               result = pages_addr[addr >> PAGE_SHIFT];
-
-               /* in case cpu page size != gpu page size*/
-               result |= addr & (~PAGE_MASK);
+       /* page table offset */
+       result = pages_addr[addr >> PAGE_SHIFT];
 
-       } else {
-               /* No mapping required */
-               result = addr;
-       }
+       /* in case cpu page size != gpu page size*/
+       result |= addr & (~PAGE_MASK);
 
        result &= 0xFFFFFFFFFFFFF000ULL;
 
        return result;
 }
 
-/**
- * amdgpu_vm_update_pdes - make sure that page directory is valid
- *
- * @adev: amdgpu_device pointer
- * @vm: requested vm
- * @start: start of GPU address range
- * @end: end of GPU address range
- *
- * Allocates new page tables if necessary
- * and updates the page directory.
- * Returns 0 for success, error for failure.
- */
-int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
-                                   struct amdgpu_vm *vm)
+static int amdgpu_vm_update_pd_or_shadow(struct amdgpu_device *adev,
+                                        struct amdgpu_vm *vm,
+                                        bool shadow)
 {
        struct amdgpu_ring *ring;
-       struct amdgpu_bo *pd = vm->page_directory;
-       uint64_t pd_addr = amdgpu_bo_gpu_offset(pd);
+       struct amdgpu_bo *pd = shadow ? vm->page_directory->shadow :
+               vm->page_directory;
+       uint64_t pd_addr;
        uint32_t incr = AMDGPU_VM_PTE_COUNT * 8;
        uint64_t last_pde = ~0, last_pt = ~0;
        unsigned count = 0, pt_idx, ndw;
        struct amdgpu_job *job;
-       struct amdgpu_vm_update_params vm_update_params;
+       struct amdgpu_pte_update_params params;
        struct fence *fence = NULL;
 
        int r;
 
-       memset(&vm_update_params, 0, sizeof(vm_update_params));
+       if (!pd)
+               return 0;
+
+       r = amdgpu_ttm_bind(&pd->tbo, &pd->tbo.mem);
+       if (r)
+               return r;
+
+       pd_addr = amdgpu_bo_gpu_offset(pd);
        ring = container_of(vm->entity.sched, struct amdgpu_ring, sched);
 
        /* padding, etc. */
@@ -638,7 +649,9 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
        if (r)
                return r;
 
-       vm_update_params.ib = &job->ibs[0];
+       memset(&params, 0, sizeof(params));
+       params.adev = adev;
+       params.ib = &job->ibs[0];
 
        /* walk over the address space and update the page directory */
        for (pt_idx = 0; pt_idx <= vm->max_pde_used; ++pt_idx) {
@@ -648,20 +661,34 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
                if (bo == NULL)
                        continue;
 
+               if (bo->shadow) {
+                       struct amdgpu_bo *shadow = bo->shadow;
+
+                       r = amdgpu_ttm_bind(&shadow->tbo, &shadow->tbo.mem);
+                       if (r)
+                               return r;
+               }
+
                pt = amdgpu_bo_gpu_offset(bo);
-               if (vm->page_tables[pt_idx].addr == pt)
-                       continue;
-               vm->page_tables[pt_idx].addr = pt;
+               if (!shadow) {
+                       if (vm->page_tables[pt_idx].addr == pt)
+                               continue;
+                       vm->page_tables[pt_idx].addr = pt;
+               } else {
+                       if (vm->page_tables[pt_idx].shadow_addr == pt)
+                               continue;
+                       vm->page_tables[pt_idx].shadow_addr = pt;
+               }
 
                pde = pd_addr + pt_idx * 8;
                if (((last_pde + 8 * count) != pde) ||
-                   ((last_pt + incr * count) != pt)) {
+                   ((last_pt + incr * count) != pt) ||
+                   (count == AMDGPU_VM_MAX_UPDATE_SIZE)) {
 
                        if (count) {
-                               amdgpu_vm_update_pages(adev, &vm_update_params,
-                                                      last_pde, last_pt,
-                                                      count, incr,
-                                                      AMDGPU_PTE_VALID);
+                               amdgpu_vm_do_set_ptes(&params, last_pde,
+                                                     last_pt, count, incr,
+                                                     AMDGPU_PTE_VALID);
                        }
 
                        count = 1;
@@ -673,15 +700,14 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
        }
 
        if (count)
-               amdgpu_vm_update_pages(adev, &vm_update_params,
-                                       last_pde, last_pt,
-                                       count, incr, AMDGPU_PTE_VALID);
+               amdgpu_vm_do_set_ptes(&params, last_pde, last_pt,
+                                     count, incr, AMDGPU_PTE_VALID);
 
-       if (vm_update_params.ib->length_dw != 0) {
-               amdgpu_ring_pad_ib(ring, vm_update_params.ib);
+       if (params.ib->length_dw != 0) {
+               amdgpu_ring_pad_ib(ring, params.ib);
                amdgpu_sync_resv(adev, &job->sync, pd->tbo.resv,
                                 AMDGPU_FENCE_OWNER_VM);
-               WARN_ON(vm_update_params.ib->length_dw > ndw);
+               WARN_ON(params.ib->length_dw > ndw);
                r = amdgpu_job_submit(job, ring, &vm->entity,
                                      AMDGPU_FENCE_OWNER_VM, &fence);
                if (r)
@@ -703,92 +729,33 @@ error_free:
        return r;
 }
 
-/**
- * amdgpu_vm_frag_ptes - add fragment information to PTEs
+/*
+ * amdgpu_vm_update_pdes - make sure that page directory is valid
  *
  * @adev: amdgpu_device pointer
- * @vm_update_params: see amdgpu_vm_update_params definition
- * @pe_start: first PTE to handle
- * @pe_end: last PTE to handle
- * @addr: addr those PTEs should point to
- * @flags: hw mapping flags
+ * @vm: requested vm
+ * @start: start of GPU address range
+ * @end: end of GPU address range
+ *
+ * Allocates new page tables if necessary
+ * and updates the page directory.
+ * Returns 0 for success, error for failure.
  */
-static void amdgpu_vm_frag_ptes(struct amdgpu_device *adev,
-                               struct amdgpu_vm_update_params
-                                       *vm_update_params,
-                               uint64_t pe_start, uint64_t pe_end,
-                               uint64_t addr, uint32_t flags)
+int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
+                                   struct amdgpu_vm *vm)
 {
-       /**
-        * The MC L1 TLB supports variable sized pages, based on a fragment
-        * field in the PTE. When this field is set to a non-zero value, page
-        * granularity is increased from 4KB to (1 << (12 + frag)). The PTE
-        * flags are considered valid for all PTEs within the fragment range
-        * and corresponding mappings are assumed to be physically contiguous.
-        *
-        * The L1 TLB can store a single PTE for the whole fragment,
-        * significantly increasing the space available for translation
-        * caching. This leads to large improvements in throughput when the
-        * TLB is under pressure.
-        *
-        * The L2 TLB distributes small and large fragments into two
-        * asymmetric partitions. The large fragment cache is significantly
-        * larger. Thus, we try to use large fragments wherever possible.
-        * Userspace can support this by aligning virtual base address and
-        * allocation size to the fragment size.
-        */
-
-       /* SI and newer are optimized for 64KB */
-       uint64_t frag_flags = AMDGPU_PTE_FRAG_64KB;
-       uint64_t frag_align = 0x80;
-
-       uint64_t frag_start = ALIGN(pe_start, frag_align);
-       uint64_t frag_end = pe_end & ~(frag_align - 1);
-
-       unsigned count;
-
-       /* Abort early if there isn't anything to do */
-       if (pe_start == pe_end)
-               return;
-
-       /* system pages are non continuously */
-       if (vm_update_params->src || vm_update_params->pages_addr ||
-               !(flags & AMDGPU_PTE_VALID) || (frag_start >= frag_end)) {
-
-               count = (pe_end - pe_start) / 8;
-               amdgpu_vm_update_pages(adev, vm_update_params, pe_start,
-                                      addr, count, AMDGPU_GPU_PAGE_SIZE,
-                                      flags);
-               return;
-       }
-
-       /* handle the 4K area at the beginning */
-       if (pe_start != frag_start) {
-               count = (frag_start - pe_start) / 8;
-               amdgpu_vm_update_pages(adev, vm_update_params, pe_start, addr,
-                                      count, AMDGPU_GPU_PAGE_SIZE, flags);
-               addr += AMDGPU_GPU_PAGE_SIZE * count;
-       }
-
-       /* handle the area in the middle */
-       count = (frag_end - frag_start) / 8;
-       amdgpu_vm_update_pages(adev, vm_update_params, frag_start, addr, count,
-                              AMDGPU_GPU_PAGE_SIZE, flags | frag_flags);
+       int r;
 
-       /* handle the 4K area at the end */
-       if (frag_end != pe_end) {
-               addr += AMDGPU_GPU_PAGE_SIZE * count;
-               count = (pe_end - frag_end) / 8;
-               amdgpu_vm_update_pages(adev, vm_update_params, frag_end, addr,
-                                      count, AMDGPU_GPU_PAGE_SIZE, flags);
-       }
+       r = amdgpu_vm_update_pd_or_shadow(adev, vm, true);
+       if (r)
+               return r;
+       return amdgpu_vm_update_pd_or_shadow(adev, vm, false);
 }
 
 /**
  * amdgpu_vm_update_ptes - make sure that page tables are valid
  *
- * @adev: amdgpu_device pointer
- * @vm_update_params: see amdgpu_vm_update_params definition
+ * @params: see amdgpu_pte_update_params definition
  * @vm: requested vm
  * @start: start of GPU address range
  * @end: end of GPU address range
@@ -797,16 +764,14 @@ static void amdgpu_vm_frag_ptes(struct amdgpu_device *adev,
  *
  * Update the page tables in the range @start - @end.
  */
-static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
-                                 struct amdgpu_vm_update_params
-                                       *vm_update_params,
+static void amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
                                  struct amdgpu_vm *vm,
                                  uint64_t start, uint64_t end,
                                  uint64_t dst, uint32_t flags)
 {
        const uint64_t mask = AMDGPU_VM_PTE_COUNT - 1;
 
-       uint64_t cur_pe_start, cur_pe_end, cur_dst;
+       uint64_t cur_pe_start, cur_nptes, cur_dst;
        uint64_t addr; /* next GPU address to be updated */
        uint64_t pt_idx;
        struct amdgpu_bo *pt;
@@ -817,7 +782,11 @@ static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
        addr = start;
        pt_idx = addr >> amdgpu_vm_block_size;
        pt = vm->page_tables[pt_idx].entry.robj;
-
+       if (params->shadow) {
+               if (!pt->shadow)
+                       return;
+               pt = vm->page_tables[pt_idx].entry.robj->shadow;
+       }
        if ((addr & ~mask) == (end & ~mask))
                nptes = end - addr;
        else
@@ -825,7 +794,7 @@ static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
 
        cur_pe_start = amdgpu_bo_gpu_offset(pt);
        cur_pe_start += (addr & mask) * 8;
-       cur_pe_end = cur_pe_start + 8 * nptes;
+       cur_nptes = nptes;
        cur_dst = dst;
 
        /* for next ptb*/
@@ -836,6 +805,11 @@ static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
        while (addr < end) {
                pt_idx = addr >> amdgpu_vm_block_size;
                pt = vm->page_tables[pt_idx].entry.robj;
+               if (params->shadow) {
+                       if (!pt->shadow)
+                               return;
+                       pt = vm->page_tables[pt_idx].entry.robj->shadow;
+               }
 
                if ((addr & ~mask) == (end & ~mask))
                        nptes = end - addr;
@@ -845,19 +819,19 @@ static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
                next_pe_start = amdgpu_bo_gpu_offset(pt);
                next_pe_start += (addr & mask) * 8;
 
-               if (cur_pe_end == next_pe_start) {
+               if ((cur_pe_start + 8 * cur_nptes) == next_pe_start &&
+                   ((cur_nptes + nptes) <= AMDGPU_VM_MAX_UPDATE_SIZE)) {
                        /* The next ptb is consecutive to current ptb.
-                        * Don't call amdgpu_vm_frag_ptes now.
+                        * Don't call the update function now.
                         * Will update two ptbs together in future.
                        */
-                       cur_pe_end += 8 * nptes;
+                       cur_nptes += nptes;
                } else {
-                       amdgpu_vm_frag_ptes(adev, vm_update_params,
-                                           cur_pe_start, cur_pe_end,
-                                           cur_dst, flags);
+                       params->func(params, cur_pe_start, cur_dst, cur_nptes,
+                                    AMDGPU_GPU_PAGE_SIZE, flags);
 
                        cur_pe_start = next_pe_start;
-                       cur_pe_end = next_pe_start + 8 * nptes;
+                       cur_nptes = nptes;
                        cur_dst = dst;
                }
 
@@ -866,8 +840,75 @@ static void amdgpu_vm_update_ptes(struct amdgpu_device *adev,
                dst += nptes * AMDGPU_GPU_PAGE_SIZE;
        }
 
-       amdgpu_vm_frag_ptes(adev, vm_update_params, cur_pe_start,
-                           cur_pe_end, cur_dst, flags);
+       params->func(params, cur_pe_start, cur_dst, cur_nptes,
+                    AMDGPU_GPU_PAGE_SIZE, flags);
+}
+
+/*
+ * amdgpu_vm_frag_ptes - add fragment information to PTEs
+ *
+ * @params: see amdgpu_pte_update_params definition
+ * @vm: requested vm
+ * @start: first PTE to handle
+ * @end: last PTE to handle
+ * @dst: addr those PTEs should point to
+ * @flags: hw mapping flags
+ */
+static void amdgpu_vm_frag_ptes(struct amdgpu_pte_update_params        *params,
+                               struct amdgpu_vm *vm,
+                               uint64_t start, uint64_t end,
+                               uint64_t dst, uint32_t flags)
+{
+       /**
+        * The MC L1 TLB supports variable sized pages, based on a fragment
+        * field in the PTE. When this field is set to a non-zero value, page
+        * granularity is increased from 4KB to (1 << (12 + frag)). The PTE
+        * flags are considered valid for all PTEs within the fragment range
+        * and corresponding mappings are assumed to be physically contiguous.
+        *
+        * The L1 TLB can store a single PTE for the whole fragment,
+        * significantly increasing the space available for translation
+        * caching. This leads to large improvements in throughput when the
+        * TLB is under pressure.
+        *
+        * The L2 TLB distributes small and large fragments into two
+        * asymmetric partitions. The large fragment cache is significantly
+        * larger. Thus, we try to use large fragments wherever possible.
+        * Userspace can support this by aligning virtual base address and
+        * allocation size to the fragment size.
+        */
+
+       /* SI and newer are optimized for 64KB */
+       uint64_t frag_flags = AMDGPU_PTE_FRAG(AMDGPU_LOG2_PAGES_PER_FRAG);
+       uint64_t frag_align = 1 << AMDGPU_LOG2_PAGES_PER_FRAG;
+
+       uint64_t frag_start = ALIGN(start, frag_align);
+       uint64_t frag_end = end & ~(frag_align - 1);
+
+       /* system pages are non continuously */
+       if (params->src || !(flags & AMDGPU_PTE_VALID) ||
+           (frag_start >= frag_end)) {
+
+               amdgpu_vm_update_ptes(params, vm, start, end, dst, flags);
+               return;
+       }
+
+       /* handle the 4K area at the beginning */
+       if (start != frag_start) {
+               amdgpu_vm_update_ptes(params, vm, start, frag_start,
+                                     dst, flags);
+               dst += (frag_start - start) * AMDGPU_GPU_PAGE_SIZE;
+       }
+
+       /* handle the area in the middle */
+       amdgpu_vm_update_ptes(params, vm, frag_start, frag_end, dst,
+                             flags | frag_flags);
+
+       /* handle the 4K area at the end */
+       if (frag_end != end) {
+               dst += (frag_end - frag_start) * AMDGPU_GPU_PAGE_SIZE;
+               amdgpu_vm_update_ptes(params, vm, frag_end, end, dst, flags);
+       }
 }
 
 /**
@@ -900,14 +941,19 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
        void *owner = AMDGPU_FENCE_OWNER_VM;
        unsigned nptes, ncmds, ndw;
        struct amdgpu_job *job;
-       struct amdgpu_vm_update_params vm_update_params;
+       struct amdgpu_pte_update_params params;
        struct fence *f = NULL;
        int r;
 
+       memset(&params, 0, sizeof(params));
+       params.adev = adev;
+       params.src = src;
+
        ring = container_of(vm->entity.sched, struct amdgpu_ring, sched);
-       memset(&vm_update_params, 0, sizeof(vm_update_params));
-       vm_update_params.src = src;
-       vm_update_params.pages_addr = pages_addr;
+
+       memset(&params, 0, sizeof(params));
+       params.adev = adev;
+       params.src = src;
 
        /* sync to everything on unmapping */
        if (!(flags & AMDGPU_PTE_VALID))
@@ -924,30 +970,53 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
        /* padding, etc. */
        ndw = 64;
 
-       if (vm_update_params.src) {
+       if (src) {
                /* only copy commands needed */
                ndw += ncmds * 7;
 
-       } else if (vm_update_params.pages_addr) {
-               /* header for write data commands */
-               ndw += ncmds * 4;
+               params.func = amdgpu_vm_do_copy_ptes;
+
+       } else if (pages_addr) {
+               /* copy commands needed */
+               ndw += ncmds * 7;
 
-               /* body of write data command */
+               /* and also PTEs */
                ndw += nptes * 2;
 
+               params.func = amdgpu_vm_do_copy_ptes;
+
        } else {
                /* set page commands needed */
                ndw += ncmds * 10;
 
                /* two extra commands for begin/end of fragment */
                ndw += 2 * 10;
+
+               params.func = amdgpu_vm_do_set_ptes;
        }
 
        r = amdgpu_job_alloc_with_ib(adev, ndw * 4, &job);
        if (r)
                return r;
 
-       vm_update_params.ib = &job->ibs[0];
+       params.ib = &job->ibs[0];
+
+       if (!src && pages_addr) {
+               uint64_t *pte;
+               unsigned i;
+
+               /* Put the PTEs at the end of the IB. */
+               i = ndw - nptes * 2;
+               pte= (uint64_t *)&(job->ibs->ptr[i]);
+               params.src = job->ibs->gpu_addr + i * 4;
+
+               for (i = 0; i < nptes; ++i) {
+                       pte[i] = amdgpu_vm_map_gart(pages_addr, addr + i *
+                                                   AMDGPU_GPU_PAGE_SIZE);
+                       pte[i] |= flags;
+               }
+               addr = 0;
+       }
 
        r = amdgpu_sync_fence(adev, &job->sync, exclusive);
        if (r)
@@ -962,11 +1031,13 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
        if (r)
                goto error_free;
 
-       amdgpu_vm_update_ptes(adev, &vm_update_params, vm, start,
-                             last + 1, addr, flags);
+       params.shadow = true;
+       amdgpu_vm_frag_ptes(&params, vm, start, last + 1, addr, flags);
+       params.shadow = false;
+       amdgpu_vm_frag_ptes(&params, vm, start, last + 1, addr, flags);
 
-       amdgpu_ring_pad_ib(ring, vm_update_params.ib);
-       WARN_ON(vm_update_params.ib->length_dw > ndw);
+       amdgpu_ring_pad_ib(ring, params.ib);
+       WARN_ON(params.ib->length_dw > ndw);
        r = amdgpu_job_submit(job, ring, &vm->entity,
                              AMDGPU_FENCE_OWNER_VM, &f);
        if (r)
@@ -1062,28 +1133,32 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev,
  *
  * @adev: amdgpu_device pointer
  * @bo_va: requested BO and VM object
- * @mem: ttm mem
+ * @clear: if true clear the entries
  *
  * Fill in the page table entries for @bo_va.
  * Returns 0 for success, -EINVAL for failure.
- *
- * Object have to be reserved and mutex must be locked!
  */
 int amdgpu_vm_bo_update(struct amdgpu_device *adev,
                        struct amdgpu_bo_va *bo_va,
-                       struct ttm_mem_reg *mem)
+                       bool clear)
 {
        struct amdgpu_vm *vm = bo_va->vm;
        struct amdgpu_bo_va_mapping *mapping;
        dma_addr_t *pages_addr = NULL;
        uint32_t gtt_flags, flags;
+       struct ttm_mem_reg *mem;
        struct fence *exclusive;
        uint64_t addr;
        int r;
 
-       if (mem) {
+       if (clear) {
+               mem = NULL;
+               addr = 0;
+               exclusive = NULL;
+       } else {
                struct ttm_dma_tt *ttm;
 
+               mem = &bo_va->bo->tbo.mem;
                addr = (u64)mem->start << PAGE_SHIFT;
                switch (mem->mem_type) {
                case TTM_PL_TT:
@@ -1101,13 +1176,11 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
                }
 
                exclusive = reservation_object_get_excl(bo_va->bo->tbo.resv);
-       } else {
-               addr = 0;
-               exclusive = NULL;
        }
 
        flags = amdgpu_ttm_tt_pte_flags(adev, bo_va->bo->tbo.ttm, mem);
-       gtt_flags = (adev == bo_va->bo->adev) ? flags : 0;
+       gtt_flags = (amdgpu_ttm_is_bound(bo_va->bo->tbo.ttm) &&
+               adev == bo_va->bo->adev) ? flags : 0;
 
        spin_lock(&vm->status_lock);
        if (!list_empty(&bo_va->vm_status))
@@ -1134,7 +1207,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
        spin_lock(&vm->status_lock);
        list_splice_init(&bo_va->invalids, &bo_va->valids);
        list_del_init(&bo_va->vm_status);
-       if (!mem)
+       if (clear)
                list_add(&bo_va->vm_status, &vm->cleared);
        spin_unlock(&vm->status_lock);
 
@@ -1197,7 +1270,7 @@ int amdgpu_vm_clear_invalids(struct amdgpu_device *adev,
                        struct amdgpu_bo_va, vm_status);
                spin_unlock(&vm->status_lock);
 
-               r = amdgpu_vm_bo_update(adev, bo_va, NULL);
+               r = amdgpu_vm_bo_update(adev, bo_va, true);
                if (r)
                        return r;
 
@@ -1342,7 +1415,8 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev,
                r = amdgpu_bo_create(adev, AMDGPU_VM_PTE_COUNT * 8,
                                     AMDGPU_GPU_PAGE_SIZE, true,
                                     AMDGPU_GEM_DOMAIN_VRAM,
-                                    AMDGPU_GEM_CREATE_NO_CPU_ACCESS,
+                                    AMDGPU_GEM_CREATE_NO_CPU_ACCESS |
+                                    AMDGPU_GEM_CREATE_SHADOW,
                                     NULL, resv, &pt);
                if (r)
                        goto error_free;
@@ -1354,10 +1428,20 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev,
 
                r = amdgpu_vm_clear_bo(adev, vm, pt);
                if (r) {
+                       amdgpu_bo_unref(&pt->shadow);
                        amdgpu_bo_unref(&pt);
                        goto error_free;
                }
 
+               if (pt->shadow) {
+                       r = amdgpu_vm_clear_bo(adev, vm, pt->shadow);
+                       if (r) {
+                               amdgpu_bo_unref(&pt->shadow);
+                               amdgpu_bo_unref(&pt);
+                               goto error_free;
+                       }
+               }
+
                entry->robj = pt;
                entry->priority = 0;
                entry->tv.bo = &entry->robj->tbo;
@@ -1541,7 +1625,8 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm)
 
        r = amdgpu_bo_create(adev, pd_size, align, true,
                             AMDGPU_GEM_DOMAIN_VRAM,
-                            AMDGPU_GEM_CREATE_NO_CPU_ACCESS,
+                            AMDGPU_GEM_CREATE_NO_CPU_ACCESS |
+                            AMDGPU_GEM_CREATE_SHADOW,
                             NULL, NULL, &vm->page_directory);
        if (r)
                goto error_free_sched_entity;
@@ -1551,14 +1636,25 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm)
                goto error_free_page_directory;
 
        r = amdgpu_vm_clear_bo(adev, vm, vm->page_directory);
-       amdgpu_bo_unreserve(vm->page_directory);
        if (r)
-               goto error_free_page_directory;
+               goto error_unreserve;
+
+       if (vm->page_directory->shadow) {
+               r = amdgpu_vm_clear_bo(adev, vm, vm->page_directory->shadow);
+               if (r)
+                       goto error_unreserve;
+       }
+
        vm->last_eviction_counter = atomic64_read(&adev->num_evictions);
+       amdgpu_bo_unreserve(vm->page_directory);
 
        return 0;
 
+error_unreserve:
+       amdgpu_bo_unreserve(vm->page_directory);
+
 error_free_page_directory:
+       amdgpu_bo_unref(&vm->page_directory->shadow);
        amdgpu_bo_unref(&vm->page_directory);
        vm->page_directory = NULL;
 
@@ -1600,10 +1696,18 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
                kfree(mapping);
        }
 
-       for (i = 0; i < amdgpu_vm_num_pdes(adev); i++)
-               amdgpu_bo_unref(&vm->page_tables[i].entry.robj);
+       for (i = 0; i < amdgpu_vm_num_pdes(adev); i++) {
+               struct amdgpu_bo *pt = vm->page_tables[i].entry.robj;
+
+               if (!pt)
+                       continue;
+
+               amdgpu_bo_unref(&pt->shadow);
+               amdgpu_bo_unref(&pt);
+       }
        drm_free_large(vm->page_tables);
 
+       amdgpu_bo_unref(&vm->page_directory->shadow);
        amdgpu_bo_unref(&vm->page_directory);
        fence_put(vm->page_directory_fence);
 }
index 49a39b1..f7d236f 100644 (file)
@@ -497,7 +497,13 @@ void amdgpu_atombios_crtc_set_disp_eng_pll(struct amdgpu_device *adev,
                         * SetPixelClock provides the dividers
                         */
                        args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk);
-                       args.v6.ucPpll = ATOM_EXT_PLL1;
+                       if (adev->asic_type == CHIP_TAHITI ||
+                           adev->asic_type == CHIP_PITCAIRN ||
+                           adev->asic_type == CHIP_VERDE ||
+                           adev->asic_type == CHIP_OLAND)
+                               args.v6.ucPpll = ATOM_PPLL0;
+                       else
+                               args.v6.ucPpll = ATOM_EXT_PLL1;
                        break;
                default:
                        DRM_ERROR("Unknown table version %d %d\n", frev, crev);
index 7f85c2c..f81068b 100644 (file)
@@ -88,7 +88,6 @@ static int amdgpu_atombios_dp_process_aux_ch(struct amdgpu_i2c_chan *chan,
 
        /* timeout */
        if (args.v2.ucReplyStatus == 1) {
-               DRM_DEBUG_KMS("dp_aux_ch timeout\n");
                r = -ETIMEDOUT;
                goto done;
        }
@@ -339,22 +338,21 @@ int amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
 {
        struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
        u8 msg[DP_DPCD_SIZE];
-       int ret, i;
+       int ret;
 
-       for (i = 0; i < 7; i++) {
-               ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_DPCD_REV, msg,
-                                      DP_DPCD_SIZE);
-               if (ret == DP_DPCD_SIZE) {
-                       memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
+       ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_DPCD_REV,
+                              msg, DP_DPCD_SIZE);
+       if (ret == DP_DPCD_SIZE) {
+               memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
 
-                       DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
-                                     dig_connector->dpcd);
+               DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
+                             dig_connector->dpcd);
 
-                       amdgpu_atombios_dp_probe_oui(amdgpu_connector);
+               amdgpu_atombios_dp_probe_oui(amdgpu_connector);
 
-                       return 0;
-               }
+               return 0;
        }
+
        dig_connector->dpcd[0] = 0;
        return -EINVAL;
 }
index bc56c8a..b374653 100644 (file)
@@ -27,6 +27,7 @@
 #include "amdgpu.h"
 #include "atom.h"
 #include "amdgpu_atombios.h"
+#include "atombios_i2c.h"
 
 #define TARGET_HW_I2C_CLOCK 50
 
index a5c94b4..1d8c375 100644 (file)
@@ -5396,7 +5396,7 @@ static void ci_dpm_disable(struct amdgpu_device *adev)
        amdgpu_irq_put(adev, &adev->pm.dpm.thermal.irq,
                       AMDGPU_THERMAL_IRQ_HIGH_TO_LOW);
 
-       ci_dpm_powergate_uvd(adev, false);
+       ci_dpm_powergate_uvd(adev, true);
 
        if (!amdgpu_ci_is_smc_running(adev))
                return;
@@ -5874,7 +5874,10 @@ static int ci_dpm_init(struct amdgpu_device *adev)
        pi->pcie_dpm_key_disabled = 0;
        pi->thermal_sclk_dpm_enabled = 0;
 
-       pi->caps_sclk_ds = true;
+       if (amdgpu_sclk_deep_sleep_en)
+               pi->caps_sclk_ds = true;
+       else
+               pi->caps_sclk_ds = false;
 
        pi->mclk_strobe_mode_threshold = 40000;
        pi->mclk_stutter_mode_threshold = 40000;
@@ -6033,7 +6036,7 @@ static int ci_dpm_init(struct amdgpu_device *adev)
 
        pi->caps_dynamic_ac_timing = true;
 
-       pi->uvd_power_gated = false;
+       pi->uvd_power_gated = true;
 
        /* make sure dc limits are valid */
        if ((adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
@@ -6176,8 +6179,6 @@ static int ci_dpm_late_init(void *handle)
        if (ret)
                return ret;
 
-       ci_dpm_powergate_uvd(adev, true);
-
        return 0;
 }
 
index 4efc901..a845b6a 100644 (file)
@@ -67,6 +67,7 @@
 
 #include "amdgpu_amdkfd.h"
 #include "amdgpu_powerplay.h"
+#include "dce_virtual.h"
 
 /*
  * Indirect registers accessor
@@ -962,12 +963,6 @@ static bool cik_read_bios_from_rom(struct amdgpu_device *adev,
        return true;
 }
 
-static u32 cik_get_virtual_caps(struct amdgpu_device *adev)
-{
-       /* CIK does not support SR-IOV */
-       return 0;
-}
-
 static const struct amdgpu_allowed_register_entry cik_allowed_read_registers[] = {
        {mmGRBM_STATUS, false},
        {mmGB_ADDR_CONFIG, false},
@@ -1640,6 +1635,12 @@ static uint32_t cik_get_rev_id(struct amdgpu_device *adev)
                >> CC_DRM_ID_STRAPS__ATI_REV_ID__SHIFT;
 }
 
+static void cik_detect_hw_virtualization(struct amdgpu_device *adev)
+{
+       if (is_virtual_machine()) /* passthrough mode */
+               adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE;
+}
+
 static const struct amdgpu_ip_block_version bonaire_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1708,6 +1709,74 @@ static const struct amdgpu_ip_block_version bonaire_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version bonaire_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 8,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 7,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &gfx_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_sdma_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 4,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &uvd_v4_2_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v2_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version hawaii_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1776,6 +1845,74 @@ static const struct amdgpu_ip_block_version hawaii_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version hawaii_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 8,
+               .minor = 5,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 7,
+               .minor = 3,
+               .rev = 0,
+               .funcs = &gfx_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_sdma_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 4,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &uvd_v4_2_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v2_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version kabini_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1844,6 +1981,74 @@ static const struct amdgpu_ip_block_version kabini_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version kabini_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 8,
+               .minor = 3,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 7,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &gfx_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_sdma_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 4,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &uvd_v4_2_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v2_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version mullins_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1912,6 +2117,74 @@ static const struct amdgpu_ip_block_version mullins_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version mullins_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 8,
+               .minor = 3,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 7,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &gfx_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_sdma_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 4,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &uvd_v4_2_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v2_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version kaveri_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1980,32 +2253,128 @@ static const struct amdgpu_ip_block_version kaveri_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version kaveri_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 8,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 7,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &gfx_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cik_sdma_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 4,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &uvd_v4_2_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v2_0_ip_funcs,
+       },
+};
+
 int cik_set_ip_blocks(struct amdgpu_device *adev)
 {
-       switch (adev->asic_type) {
-       case CHIP_BONAIRE:
-               adev->ip_blocks = bonaire_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(bonaire_ip_blocks);
-               break;
-       case CHIP_HAWAII:
-               adev->ip_blocks = hawaii_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(hawaii_ip_blocks);
-               break;
-       case CHIP_KAVERI:
-               adev->ip_blocks = kaveri_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(kaveri_ip_blocks);
-               break;
-       case CHIP_KABINI:
-               adev->ip_blocks = kabini_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(kabini_ip_blocks);
-               break;
-       case CHIP_MULLINS:
-               adev->ip_blocks = mullins_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(mullins_ip_blocks);
-               break;
-       default:
-               /* FIXME: not supported yet */
-               return -EINVAL;
+       if (adev->enable_virtual_display) {
+               switch (adev->asic_type) {
+               case CHIP_BONAIRE:
+                       adev->ip_blocks = bonaire_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(bonaire_ip_blocks_vd);
+                       break;
+               case CHIP_HAWAII:
+                       adev->ip_blocks = hawaii_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(hawaii_ip_blocks_vd);
+                       break;
+               case CHIP_KAVERI:
+                       adev->ip_blocks = kaveri_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(kaveri_ip_blocks_vd);
+                       break;
+               case CHIP_KABINI:
+                       adev->ip_blocks = kabini_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(kabini_ip_blocks_vd);
+                       break;
+               case CHIP_MULLINS:
+                       adev->ip_blocks = mullins_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(mullins_ip_blocks_vd);
+                       break;
+               default:
+                       /* FIXME: not supported yet */
+                       return -EINVAL;
+               }
+       } else {
+               switch (adev->asic_type) {
+               case CHIP_BONAIRE:
+                       adev->ip_blocks = bonaire_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(bonaire_ip_blocks);
+                       break;
+               case CHIP_HAWAII:
+                       adev->ip_blocks = hawaii_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(hawaii_ip_blocks);
+                       break;
+               case CHIP_KAVERI:
+                       adev->ip_blocks = kaveri_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(kaveri_ip_blocks);
+                       break;
+               case CHIP_KABINI:
+                       adev->ip_blocks = kabini_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(kabini_ip_blocks);
+                       break;
+               case CHIP_MULLINS:
+                       adev->ip_blocks = mullins_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(mullins_ip_blocks);
+                       break;
+               default:
+                       /* FIXME: not supported yet */
+                       return -EINVAL;
+               }
        }
 
        return 0;
@@ -2015,13 +2384,13 @@ static const struct amdgpu_asic_funcs cik_asic_funcs =
 {
        .read_disabled_bios = &cik_read_disabled_bios,
        .read_bios_from_rom = &cik_read_bios_from_rom,
+       .detect_hw_virtualization = cik_detect_hw_virtualization,
        .read_register = &cik_read_register,
        .reset = &cik_asic_reset,
        .set_vga_state = &cik_vga_set_state,
        .get_xclk = &cik_get_xclk,
        .set_uvd_clocks = &cik_set_uvd_clocks,
        .set_vce_clocks = &cik_set_vce_clocks,
-       .get_virtual_caps = &cik_get_virtual_caps,
 };
 
 static int cik_common_early_init(void *handle)
index 77fdd99..cb952ac 100644 (file)
@@ -695,24 +695,16 @@ static void cik_sdma_vm_copy_pte(struct amdgpu_ib *ib,
                                 uint64_t pe, uint64_t src,
                                 unsigned count)
 {
-       while (count) {
-               unsigned bytes = count * 8;
-               if (bytes > 0x1FFFF8)
-                       bytes = 0x1FFFF8;
-
-               ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY,
-                       SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
-               ib->ptr[ib->length_dw++] = bytes;
-               ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
-               ib->ptr[ib->length_dw++] = lower_32_bits(src);
-               ib->ptr[ib->length_dw++] = upper_32_bits(src);
-               ib->ptr[ib->length_dw++] = lower_32_bits(pe);
-               ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
-               pe += bytes;
-               src += bytes;
-               count -= bytes / 8;
-       }
+       unsigned bytes = count * 8;
+
+       ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_COPY,
+               SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+       ib->ptr[ib->length_dw++] = bytes;
+       ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
+       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+       ib->ptr[ib->length_dw++] = upper_32_bits(src);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
 }
 
 /**
@@ -720,39 +712,27 @@ static void cik_sdma_vm_copy_pte(struct amdgpu_ib *ib,
  *
  * @ib: indirect buffer to fill with commands
  * @pe: addr of the page entry
- * @addr: dst addr to write into pe
+ * @value: dst addr to write into pe
  * @count: number of page entries to update
  * @incr: increase next addr by incr bytes
- * @flags: access flags
  *
  * Update PTEs by writing them manually using sDMA (CIK).
  */
-static void cik_sdma_vm_write_pte(struct amdgpu_ib *ib,
-                                 const dma_addr_t *pages_addr, uint64_t pe,
-                                 uint64_t addr, unsigned count,
-                                 uint32_t incr, uint32_t flags)
+static void cik_sdma_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
+                                 uint64_t value, unsigned count,
+                                 uint32_t incr)
 {
-       uint64_t value;
-       unsigned ndw;
-
-       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) {
-                       value = amdgpu_vm_map_gart(pages_addr, addr);
-                       addr += incr;
-                       value |= flags;
-                       ib->ptr[ib->length_dw++] = value;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(value);
-               }
+       unsigned ndw = count * 2;
+
+       ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_WRITE,
+               SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       ib->ptr[ib->length_dw++] = ndw;
+       for (; ndw > 0; ndw -= 2) {
+               ib->ptr[ib->length_dw++] = lower_32_bits(value);
+               ib->ptr[ib->length_dw++] = upper_32_bits(value);
+               value += incr;
        }
 }
 
@@ -768,40 +748,21 @@ static void cik_sdma_vm_write_pte(struct amdgpu_ib *ib,
  *
  * Update the page tables using sDMA (CIK).
  */
-static void cik_sdma_vm_set_pte_pde(struct amdgpu_ib *ib,
-                                   uint64_t pe,
+static void cik_sdma_vm_set_pte_pde(struct amdgpu_ib *ib, uint64_t pe,
                                    uint64_t addr, unsigned count,
                                    uint32_t incr, uint32_t flags)
 {
-       uint64_t value;
-       unsigned ndw;
-
-       while (count) {
-               ndw = count;
-               if (ndw > 0x7FFFF)
-                       ndw = 0x7FFFF;
-
-               if (flags & AMDGPU_PTE_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++] = 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;
-       }
+       /* for physically contiguous pages (vram) */
+       ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_GENERATE_PTE_PDE, 0, 0);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe); /* dst addr */
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       ib->ptr[ib->length_dw++] = flags; /* mask */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = lower_32_bits(addr); /* value */
+       ib->ptr[ib->length_dw++] = upper_32_bits(addr);
+       ib->ptr[ib->length_dw++] = incr; /* increment size */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = count; /* number of entries */
 }
 
 /**
@@ -887,6 +848,22 @@ static void cik_sdma_ring_emit_vm_flush(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, (0xfff << 16) | 10); /* retry count, poll interval */
 }
 
+static unsigned cik_sdma_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               7 + 4; /* cik_sdma_ring_emit_ib */
+}
+
+static unsigned cik_sdma_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               6 + /* cik_sdma_ring_emit_hdp_flush */
+               3 + /* cik_sdma_ring_emit_hdp_invalidate */
+               6 + /* cik_sdma_ring_emit_pipeline_sync */
+               12 + /* cik_sdma_ring_emit_vm_flush */
+               9 + 9 + 9; /* cik_sdma_ring_emit_fence x3 for user fence, vm fence */
+}
+
 static void cik_enable_sdma_mgcg(struct amdgpu_device *adev,
                                 bool enable)
 {
@@ -1262,6 +1239,8 @@ static const struct amdgpu_ring_funcs cik_sdma_ring_funcs = {
        .test_ib = cik_sdma_ring_test_ib,
        .insert_nop = cik_sdma_ring_insert_nop,
        .pad_ib = cik_sdma_ring_pad_ib,
+       .get_emit_ib_size = cik_sdma_ring_get_emit_ib_size,
+       .get_dma_frame_size = cik_sdma_ring_get_dma_frame_size,
 };
 
 static void cik_sdma_set_ring_funcs(struct amdgpu_device *adev)
index c4f6f00..8659852 100644 (file)
@@ -562,4 +562,40 @@ enum {
        MTYPE_NONCACHED = 3
 };
 
+/* mmPA_SC_RASTER_CONFIG mask */
+#define RB_MAP_PKR0(x)                         ((x) << 0)
+#define RB_MAP_PKR0_MASK                       (0x3 << 0)
+#define RB_MAP_PKR1(x)                         ((x) << 2)
+#define RB_MAP_PKR1_MASK                       (0x3 << 2)
+#define RB_XSEL2(x)                            ((x) << 4)
+#define RB_XSEL2_MASK                          (0x3 << 4)
+#define RB_XSEL                                        (1 << 6)
+#define RB_YSEL                                        (1 << 7)
+#define PKR_MAP(x)                             ((x) << 8)
+#define PKR_MAP_MASK                           (0x3 << 8)
+#define PKR_XSEL(x)                            ((x) << 10)
+#define PKR_XSEL_MASK                          (0x3 << 10)
+#define PKR_YSEL(x)                            ((x) << 12)
+#define PKR_YSEL_MASK                          (0x3 << 12)
+#define SC_MAP(x)                              ((x) << 16)
+#define SC_MAP_MASK                            (0x3 << 16)
+#define SC_XSEL(x)                             ((x) << 18)
+#define SC_XSEL_MASK                           (0x3 << 18)
+#define SC_YSEL(x)                             ((x) << 20)
+#define SC_YSEL_MASK                           (0x3 << 20)
+#define SE_MAP(x)                              ((x) << 24)
+#define SE_MAP_MASK                            (0x3 << 24)
+#define SE_XSEL(x)                             ((x) << 26)
+#define SE_XSEL_MASK                           (0x3 << 26)
+#define SE_YSEL(x)                             ((x) << 28)
+#define SE_YSEL_MASK                           (0x3 << 28)
+
+/* mmPA_SC_RASTER_CONFIG_1 mask */
+#define SE_PAIR_MAP(x)                         ((x) << 0)
+#define SE_PAIR_MAP_MASK                       (0x3 << 0)
+#define SE_PAIR_XSEL(x)                                ((x) << 2)
+#define SE_PAIR_XSEL_MASK                      (0x3 << 2)
+#define SE_PAIR_YSEL(x)                                ((x) << 4)
+#define SE_PAIR_YSEL_MASK                      (0x3 << 4)
+
 #endif
index 2a11413..f80a083 100644 (file)
@@ -44,6 +44,7 @@
 
 static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate);
 static void cz_dpm_powergate_vce(struct amdgpu_device *adev, bool gate);
+static void cz_dpm_fini(struct amdgpu_device *adev);
 
 static struct cz_ps *cz_get_ps(struct amdgpu_ps *rps)
 {
@@ -350,6 +351,8 @@ static int cz_parse_power_table(struct amdgpu_device *adev)
 
                ps = kzalloc(sizeof(struct cz_ps), GFP_KERNEL);
                if (ps == NULL) {
+                       for (j = 0; j < i; j++)
+                               kfree(adev->pm.dpm.ps[j].ps_priv);
                        kfree(adev->pm.dpm.ps);
                        return -ENOMEM;
                }
@@ -409,11 +412,11 @@ static int cz_dpm_init(struct amdgpu_device *adev)
 
        ret = amdgpu_get_platform_caps(adev);
        if (ret)
-               return ret;
+               goto err;
 
        ret = amdgpu_parse_extended_power_table(adev);
        if (ret)
-               return ret;
+               goto err;
 
        pi->sram_end = SMC_RAM_END;
 
@@ -435,7 +438,11 @@ static int cz_dpm_init(struct amdgpu_device *adev)
                pi->caps_td_ramping = true;
                pi->caps_tcp_ramping = true;
        }
-       pi->caps_sclk_ds = true;
+       if (amdgpu_sclk_deep_sleep_en)
+               pi->caps_sclk_ds = true;
+       else
+               pi->caps_sclk_ds = false;
+
        pi->voting_clients = 0x00c00033;
        pi->auto_thermal_throttling_enabled = true;
        pi->bapm_enabled = false;
@@ -463,23 +470,26 @@ static int cz_dpm_init(struct amdgpu_device *adev)
 
        ret = cz_parse_sys_info_table(adev);
        if (ret)
-               return ret;
+               goto err;
 
        cz_patch_voltage_values(adev);
        cz_construct_boot_state(adev);
 
        ret = cz_parse_power_table(adev);
        if (ret)
-               return ret;
+               goto err;
 
        ret = cz_process_firmware_header(adev);
        if (ret)
-               return ret;
+               goto err;
 
        pi->dpm_enabled = true;
        pi->uvd_dynamic_pg = false;
 
        return 0;
+err:
+       cz_dpm_fini(adev);
+       return ret;
 }
 
 static void cz_dpm_fini(struct amdgpu_device *adev)
@@ -668,17 +678,12 @@ static void cz_reset_ap_mask(struct amdgpu_device *adev)
        struct cz_power_info *pi = cz_get_pi(adev);
 
        pi->active_process_mask = 0;
-
 }
 
 static int cz_dpm_download_pptable_from_smu(struct amdgpu_device *adev,
                                                        void **table)
 {
-       int ret = 0;
-
-       ret = cz_smu_download_pptable(adev, table);
-
-       return ret;
+       return cz_smu_download_pptable(adev, table);
 }
 
 static int cz_dpm_upload_pptable_to_smu(struct amdgpu_device *adev)
@@ -818,9 +823,9 @@ static void cz_init_sclk_limit(struct amdgpu_device *adev)
        pi->sclk_dpm.hard_min_clk = 0;
        cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxSclkLevel);
        level = cz_get_argument(adev);
-       if (level < table->count)
+       if (level < table->count) {
                clock = table->entries[level].clk;
-       else {
+       else {
                DRM_ERROR("Invalid SLCK Voltage Dependency table entry.\n");
                clock = table->entries[table->count - 1].clk;
        }
@@ -846,9 +851,9 @@ static void cz_init_uvd_limit(struct amdgpu_device *adev)
        pi->uvd_dpm.hard_min_clk = 0;
        cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel);
        level = cz_get_argument(adev);
-       if (level < table->count)
+       if (level < table->count) {
                clock = table->entries[level].vclk;
-       else {
+       else {
                DRM_ERROR("Invalid UVD Voltage Dependency table entry.\n");
                clock = table->entries[table->count - 1].vclk;
        }
@@ -874,9 +879,9 @@ static void cz_init_vce_limit(struct amdgpu_device *adev)
        pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;
        cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel);
        level = cz_get_argument(adev);
-       if (level < table->count)
+       if (level < table->count) {
                clock = table->entries[level].ecclk;
-       else {
+       else {
                /* future BIOS would fix this error */
                DRM_ERROR("Invalid VCE Voltage Dependency table entry.\n");
                clock = table->entries[table->count - 1].ecclk;
@@ -903,9 +908,9 @@ static void cz_init_acp_limit(struct amdgpu_device *adev)
        pi->acp_dpm.hard_min_clk = 0;
        cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxAclkLevel);
        level = cz_get_argument(adev);
-       if (level < table->count)
+       if (level < table->count) {
                clock = table->entries[level].clk;
-       else {
+       else {
                DRM_ERROR("Invalid ACP Voltage Dependency table entry.\n");
                clock = table->entries[table->count - 1].clk;
        }
@@ -930,7 +935,6 @@ static void cz_init_sclk_threshold(struct amdgpu_device *adev)
        struct cz_power_info *pi = cz_get_pi(adev);
 
        pi->low_sclk_interrupt_threshold = 0;
-
 }
 
 static void cz_dpm_setup_asic(struct amdgpu_device *adev)
@@ -1203,7 +1207,7 @@ static int cz_enable_didt(struct amdgpu_device *adev, bool enable)
        int ret;
 
        if (pi->caps_sq_ramping || pi->caps_db_ramping ||
-                       pi->caps_td_ramping || pi->caps_tcp_ramping) {
+           pi->caps_td_ramping || pi->caps_tcp_ramping) {
                if (adev->gfx.gfx_current_status != AMDGPU_GFX_SAFE_MODE) {
                        ret = cz_disable_cgpg(adev);
                        if (ret) {
@@ -1277,7 +1281,7 @@ static void cz_apply_state_adjust_rules(struct amdgpu_device *adev,
        ps->force_high = false;
        ps->need_dfs_bypass = true;
        pi->video_start = new_rps->dclk || new_rps->vclk ||
-                               new_rps->evclk || new_rps->ecclk;
+                         new_rps->evclk || new_rps->ecclk;
 
        if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
                        ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
@@ -1335,7 +1339,6 @@ static int cz_dpm_enable(struct amdgpu_device *adev)
        }
 
        cz_reset_acp_boot_level(adev);
-
        cz_update_current_ps(adev, adev->pm.dpm.boot_ps);
 
        return 0;
@@ -1665,7 +1668,6 @@ static void cz_dpm_post_set_power_state(struct amdgpu_device *adev)
        struct amdgpu_ps *ps = &pi->requested_rps;
 
        cz_update_current_ps(adev, ps);
-
 }
 
 static int cz_dpm_force_highest(struct amdgpu_device *adev)
@@ -2108,29 +2110,58 @@ static void cz_dpm_powergate_uvd(struct amdgpu_device *adev, bool gate)
                        /* disable clockgating so we can properly shut down the block */
                        ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
                                                            AMD_CG_STATE_UNGATE);
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating failed to set clockgating state\n");
+                               return;
+                       }
+
                        /* shutdown the UVD block */
                        ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
                                                            AMD_PG_STATE_GATE);
-                       /* XXX: check for errors */
+
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating failed to set powergating state\n");
+                               return;
+                       }
                }
                cz_update_uvd_dpm(adev, gate);
-               if (pi->caps_uvd_pg)
+               if (pi->caps_uvd_pg) {
                        /* power off the UVD block */
-                       cz_send_msg_to_smc(adev, PPSMC_MSG_UVDPowerOFF);
+                       ret = cz_send_msg_to_smc(adev, PPSMC_MSG_UVDPowerOFF);
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating failed to send SMU PowerOFF message\n");
+                               return;
+                       }
+               }
        } else {
                if (pi->caps_uvd_pg) {
                        /* power on the UVD block */
                        if (pi->uvd_dynamic_pg)
-                               cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 1);
+                               ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 1);
                        else
-                               cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 0);
+                               ret = cz_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_UVDPowerON, 0);
+
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating Failed to send SMU PowerON message\n");
+                               return;
+                       }
+
                        /* re-init the UVD block */
                        ret = amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
                                                            AMD_PG_STATE_UNGATE);
+
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating Failed to set powergating state\n");
+                               return;
+                       }
+
                        /* enable clockgating. hw will dynamically gate/ungate clocks on the fly */
                        ret = amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_UVD,
                                                            AMD_CG_STATE_GATE);
-                       /* XXX: check for errors */
+                       if (ret) {
+                               DRM_ERROR("UVD DPM Power Gating Failed to set clockgating state\n");
+                               return;
+                       }
                }
                cz_update_uvd_dpm(adev, gate);
        }
@@ -2168,7 +2199,6 @@ static int cz_update_vce_dpm(struct amdgpu_device *adev)
        /* Stable Pstate is enabled and we need to set the VCE DPM to highest level */
        if (pi->caps_stable_power_state) {
                pi->vce_dpm.hard_min_clk = table->entries[table->count-1].ecclk;
-
        } else { /* non-stable p-state cases. without vce.Arbiter.EcclkHardMin */
                /* leave it as set by user */
                /*pi->vce_dpm.hard_min_clk = table->entries[0].ecclk;*/
index ac7fee7..aed7033 100644 (file)
@@ -29,6 +29,8 @@
 #include "cz_smumgr.h"
 #include "smu_ucode_xfer_cz.h"
 #include "amdgpu_ucode.h"
+#include "cz_dpm.h"
+#include "vi_dpm.h"
 
 #include "smu/smu_8_0_d.h"
 #include "smu/smu_8_0_sh_mask.h"
@@ -48,7 +50,7 @@ static struct cz_smu_private_data *cz_smu_get_priv(struct amdgpu_device *adev)
        return priv;
 }
 
-int cz_send_msg_to_smc_async(struct amdgpu_device *adev, u16 msg)
+static int cz_send_msg_to_smc_async(struct amdgpu_device *adev, u16 msg)
 {
        int i;
        u32 content = 0, tmp;
@@ -99,13 +101,6 @@ int cz_send_msg_to_smc(struct amdgpu_device *adev, u16 msg)
        return 0;
 }
 
-int cz_send_msg_to_smc_with_parameter_async(struct amdgpu_device *adev,
-                                               u16 msg, u32 parameter)
-{
-       WREG32(mmSMU_MP1_SRBM2P_ARG_0, parameter);
-       return cz_send_msg_to_smc_async(adev, msg);
-}
-
 int cz_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
                                                u16 msg, u32 parameter)
 {
@@ -140,7 +135,7 @@ int cz_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
        return 0;
 }
 
-int cz_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+static int cz_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
                                                u32 value, u32 limit)
 {
        int ret;
index c1b04e9..613ebb7 100644 (file)
@@ -221,7 +221,7 @@ static bool dce_v10_0_is_counter_moving(struct amdgpu_device *adev, int crtc)
  */
 static void dce_v10_0_vblank_wait(struct amdgpu_device *adev, int crtc)
 {
-       unsigned i = 0;
+       unsigned i = 100;
 
        if (crtc >= adev->mode_info.num_crtc)
                return;
@@ -233,14 +233,16 @@ static void dce_v10_0_vblank_wait(struct amdgpu_device *adev, int crtc)
         * wait for another frame.
         */
        while (dce_v10_0_is_in_vblank(adev, crtc)) {
-               if (i++ % 100 == 0) {
+               if (i++ == 100) {
+                       i = 0;
                        if (!dce_v10_0_is_counter_moving(adev, crtc))
                                break;
                }
        }
 
        while (!dce_v10_0_is_in_vblank(adev, crtc)) {
-               if (i++ % 100 == 0) {
+               if (i++ == 100) {
+                       i = 0;
                        if (!dce_v10_0_is_counter_moving(adev, crtc))
                                break;
                }
@@ -425,16 +427,6 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
-               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
-                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
-                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
-                        * aux dp channel on imac and help (but not completely fix)
-                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
-                        * also avoid interrupt storms during dpms.
-                        */
-                       continue;
-               }
-
                switch (amdgpu_connector->hpd.hpd) {
                case AMDGPU_HPD_1:
                        idx = 0;
@@ -458,6 +450,19 @@ static void dce_v10_0_hpd_init(struct amdgpu_device *adev)
                        continue;
                }
 
+               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
+                        * aux dp channel on imac and help (but not completely fix)
+                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+                        * also avoid interrupt storms during dpms.
+                        */
+                       tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+                       tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+                       WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+                       continue;
+               }
+
                tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
                tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
                WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
@@ -646,8 +651,8 @@ static void dce_v10_0_resume_mc_access(struct amdgpu_device *adev,
 
                if (save->crtc_enabled[i]) {
                        tmp = RREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i]);
-                       if (REG_GET_FIELD(tmp, MASTER_UPDATE_MODE, MASTER_UPDATE_MODE) != 3) {
-                               tmp = REG_SET_FIELD(tmp, MASTER_UPDATE_MODE, MASTER_UPDATE_MODE, 3);
+                       if (REG_GET_FIELD(tmp, MASTER_UPDATE_MODE, MASTER_UPDATE_MODE) != 0) {
+                               tmp = REG_SET_FIELD(tmp, MASTER_UPDATE_MODE, MASTER_UPDATE_MODE, 0);
                                WREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i], tmp);
                        }
                        tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]);
@@ -712,6 +717,45 @@ static void dce_v10_0_set_vga_render_state(struct amdgpu_device *adev,
        WREG32(mmVGA_RENDER_CONTROL, tmp);
 }
 
+static int dce_v10_0_get_num_crtc(struct amdgpu_device *adev)
+{
+       int num_crtc = 0;
+
+       switch (adev->asic_type) {
+       case CHIP_FIJI:
+       case CHIP_TONGA:
+               num_crtc = 6;
+               break;
+       default:
+               num_crtc = 0;
+       }
+       return num_crtc;
+}
+
+void dce_v10_0_disable_dce(struct amdgpu_device *adev)
+{
+       /*Disable VGA render and enabled crtc, if has DCE engine*/
+       if (amdgpu_atombios_has_dce_engine_info(adev)) {
+               u32 tmp;
+               int crtc_enabled, i;
+
+               dce_v10_0_set_vga_render_state(adev, false);
+
+               /*Disable crtc*/
+               for (i = 0; i < dce_v10_0_get_num_crtc(adev); i++) {
+                       crtc_enabled = REG_GET_FIELD(RREG32(mmCRTC_CONTROL + crtc_offsets[i]),
+                                                                        CRTC_CONTROL, CRTC_MASTER_EN);
+                       if (crtc_enabled) {
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
+                               tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]);
+                               tmp = REG_SET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN, 0);
+                               WREG32(mmCRTC_CONTROL + crtc_offsets[i], tmp);
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
+                       }
+               }
+       }
+}
+
 static void dce_v10_0_program_fmt(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
@@ -2063,7 +2107,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc,
        struct amdgpu_framebuffer *amdgpu_fb;
        struct drm_framebuffer *target_fb;
        struct drm_gem_object *obj;
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        uint64_t fb_location, tiling_flags;
        uint32_t fb_format, fb_pitch_pixels;
        u32 fb_swap = REG_SET_FIELD(0, GRPH_SWAP_CNTL, GRPH_ENDIAN_SWAP, ENDIAN_NONE);
@@ -2071,6 +2115,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc,
        u32 tmp, viewport_w, viewport_h;
        int r;
        bool bypass_lut = false;
+       char *format_name;
 
        /* no fb bound */
        if (!atomic && !crtc->primary->fb) {
@@ -2090,23 +2135,23 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc,
         * just update base pointers
         */
        obj = amdgpu_fb->obj;
-       rbo = gem_to_amdgpu_bo(obj);
-       r = amdgpu_bo_reserve(rbo, false);
+       abo = gem_to_amdgpu_bo(obj);
+       r = amdgpu_bo_reserve(abo, false);
        if (unlikely(r != 0))
                return r;
 
        if (atomic) {
-               fb_location = amdgpu_bo_gpu_offset(rbo);
+               fb_location = amdgpu_bo_gpu_offset(abo);
        } else {
-               r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
+               r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
                if (unlikely(r != 0)) {
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unreserve(abo);
                        return -EINVAL;
                }
        }
 
-       amdgpu_bo_get_tiling_flags(rbo, &tiling_flags);
-       amdgpu_bo_unreserve(rbo);
+       amdgpu_bo_get_tiling_flags(abo, &tiling_flags);
+       amdgpu_bo_unreserve(abo);
 
        pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
 
@@ -2182,8 +2227,9 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc,
                bypass_lut = true;
                break;
        default:
-               DRM_ERROR("Unsupported screen format %s\n",
-                       drm_get_format_name(target_fb->pixel_format));
+               format_name = drm_get_format_name(target_fb->pixel_format);
+               DRM_ERROR("Unsupported screen format %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
@@ -2275,17 +2321,17 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(mmVIEWPORT_SIZE + amdgpu_crtc->crtc_offset,
               (viewport_w << 16) | viewport_h);
 
-       /* set pageflip to happen only at start of vblank interval (front porch) */
-       WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 3);
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->primary->fb) {
                amdgpu_fb = to_amdgpu_framebuffer(fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r != 0))
                        return r;
-               amdgpu_bo_unpin(rbo);
-               amdgpu_bo_unreserve(rbo);
+               amdgpu_bo_unpin(abo);
+               amdgpu_bo_unreserve(abo);
        }
 
        /* Bytes per pixel may have changed */
@@ -2698,7 +2744,7 @@ static const struct drm_crtc_funcs dce_v10_0_crtc_funcs = {
        .gamma_set = dce_v10_0_crtc_gamma_set,
        .set_config = amdgpu_crtc_set_config,
        .destroy = dce_v10_0_crtc_destroy,
-       .page_flip = amdgpu_crtc_page_flip,
+       .page_flip_target = amdgpu_crtc_page_flip_target,
 };
 
 static void dce_v10_0_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -2765,16 +2811,16 @@ static void dce_v10_0_crtc_disable(struct drm_crtc *crtc)
        if (crtc->primary->fb) {
                int r;
                struct amdgpu_framebuffer *amdgpu_fb;
-               struct amdgpu_bo *rbo;
+               struct amdgpu_bo *abo;
 
                amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r))
-                       DRM_ERROR("failed to reserve rbo before unpin\n");
+                       DRM_ERROR("failed to reserve abo before unpin\n");
                else {
-                       amdgpu_bo_unpin(rbo);
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unpin(abo);
+                       amdgpu_bo_unreserve(abo);
                }
        }
        /* disable the GRPH */
@@ -2962,10 +3008,11 @@ static int dce_v10_0_early_init(void *handle)
        dce_v10_0_set_display_funcs(adev);
        dce_v10_0_set_irq_funcs(adev);
 
+       adev->mode_info.num_crtc = dce_v10_0_get_num_crtc(adev);
+
        switch (adev->asic_type) {
        case CHIP_FIJI:
        case CHIP_TONGA:
-               adev->mode_info.num_crtc = 6; /* XXX 7??? */
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 7;
                break;
@@ -3141,11 +3188,26 @@ static int dce_v10_0_wait_for_idle(void *handle)
        return 0;
 }
 
+static int dce_v10_0_check_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (dce_v10_0_is_display_hung(adev))
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang = true;
+       else
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang = false;
+
+       return 0;
+}
+
 static int dce_v10_0_soft_reset(void *handle)
 {
        u32 srbm_soft_reset = 0, tmp;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_DCE].hang)
+               return 0;
+
        if (dce_v10_0_is_display_hung(adev))
                srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK;
 
@@ -3512,6 +3574,7 @@ const struct amd_ip_funcs dce_v10_0_ip_funcs = {
        .resume = dce_v10_0_resume,
        .is_idle = dce_v10_0_is_idle,
        .wait_for_idle = dce_v10_0_wait_for_idle,
+       .check_soft_reset = dce_v10_0_check_soft_reset,
        .soft_reset = dce_v10_0_soft_reset,
        .set_clockgating_state = dce_v10_0_set_clockgating_state,
        .set_powergating_state = dce_v10_0_set_powergating_state,
index 1bfa48d..e3dc04d 100644 (file)
@@ -26,4 +26,6 @@
 
 extern const struct amd_ip_funcs dce_v10_0_ip_funcs;
 
+void dce_v10_0_disable_dce(struct amdgpu_device *adev);
+
 #endif
index d4bf133..f264b8f 100644 (file)
@@ -443,16 +443,6 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
-               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
-                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
-                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
-                        * aux dp channel on imac and help (but not completely fix)
-                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
-                        * also avoid interrupt storms during dpms.
-                        */
-                       continue;
-               }
-
                switch (amdgpu_connector->hpd.hpd) {
                case AMDGPU_HPD_1:
                        idx = 0;
@@ -476,6 +466,19 @@ static void dce_v11_0_hpd_init(struct amdgpu_device *adev)
                        continue;
                }
 
+               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
+                        * aux dp channel on imac and help (but not completely fix)
+                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+                        * also avoid interrupt storms during dpms.
+                        */
+                       tmp = RREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx]);
+                       tmp = REG_SET_FIELD(tmp, DC_HPD_INT_CONTROL, DC_HPD_INT_EN, 0);
+                       WREG32(mmDC_HPD_INT_CONTROL + hpd_offsets[idx], tmp);
+                       continue;
+               }
+
                tmp = RREG32(mmDC_HPD_CONTROL + hpd_offsets[idx]);
                tmp = REG_SET_FIELD(tmp, DC_HPD_CONTROL, DC_HPD_EN, 1);
                WREG32(mmDC_HPD_CONTROL + hpd_offsets[idx], tmp);
@@ -673,6 +676,53 @@ static void dce_v11_0_set_vga_render_state(struct amdgpu_device *adev,
        WREG32(mmVGA_RENDER_CONTROL, tmp);
 }
 
+static int dce_v11_0_get_num_crtc (struct amdgpu_device *adev)
+{
+       int num_crtc = 0;
+
+       switch (adev->asic_type) {
+       case CHIP_CARRIZO:
+               num_crtc = 3;
+               break;
+       case CHIP_STONEY:
+               num_crtc = 2;
+               break;
+       case CHIP_POLARIS10:
+               num_crtc = 6;
+               break;
+       case CHIP_POLARIS11:
+               num_crtc = 5;
+               break;
+       default:
+               num_crtc = 0;
+       }
+       return num_crtc;
+}
+
+void dce_v11_0_disable_dce(struct amdgpu_device *adev)
+{
+       /*Disable VGA render and enabled crtc, if has DCE engine*/
+       if (amdgpu_atombios_has_dce_engine_info(adev)) {
+               u32 tmp;
+               int crtc_enabled, i;
+
+               dce_v11_0_set_vga_render_state(adev, false);
+
+               /*Disable crtc*/
+               for (i = 0; i < dce_v11_0_get_num_crtc(adev); i++) {
+                       crtc_enabled = REG_GET_FIELD(RREG32(mmCRTC_CONTROL + crtc_offsets[i]),
+                                                                        CRTC_CONTROL, CRTC_MASTER_EN);
+                       if (crtc_enabled) {
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
+                               tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]);
+                               tmp = REG_SET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN, 0);
+                               WREG32(mmCRTC_CONTROL + crtc_offsets[i], tmp);
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
+                       }
+               }
+       }
+}
+
 static void dce_v11_0_program_fmt(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
@@ -2038,7 +2088,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc,
        struct amdgpu_framebuffer *amdgpu_fb;
        struct drm_framebuffer *target_fb;
        struct drm_gem_object *obj;
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        uint64_t fb_location, tiling_flags;
        uint32_t fb_format, fb_pitch_pixels;
        u32 fb_swap = REG_SET_FIELD(0, GRPH_SWAP_CNTL, GRPH_ENDIAN_SWAP, ENDIAN_NONE);
@@ -2046,6 +2096,7 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc,
        u32 tmp, viewport_w, viewport_h;
        int r;
        bool bypass_lut = false;
+       char *format_name;
 
        /* no fb bound */
        if (!atomic && !crtc->primary->fb) {
@@ -2065,23 +2116,23 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc,
         * just update base pointers
         */
        obj = amdgpu_fb->obj;
-       rbo = gem_to_amdgpu_bo(obj);
-       r = amdgpu_bo_reserve(rbo, false);
+       abo = gem_to_amdgpu_bo(obj);
+       r = amdgpu_bo_reserve(abo, false);
        if (unlikely(r != 0))
                return r;
 
        if (atomic) {
-               fb_location = amdgpu_bo_gpu_offset(rbo);
+               fb_location = amdgpu_bo_gpu_offset(abo);
        } else {
-               r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
+               r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
                if (unlikely(r != 0)) {
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unreserve(abo);
                        return -EINVAL;
                }
        }
 
-       amdgpu_bo_get_tiling_flags(rbo, &tiling_flags);
-       amdgpu_bo_unreserve(rbo);
+       amdgpu_bo_get_tiling_flags(abo, &tiling_flags);
+       amdgpu_bo_unreserve(abo);
 
        pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
 
@@ -2157,8 +2208,9 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc,
                bypass_lut = true;
                break;
        default:
-               DRM_ERROR("Unsupported screen format %s\n",
-                       drm_get_format_name(target_fb->pixel_format));
+               format_name = drm_get_format_name(target_fb->pixel_format);
+               DRM_ERROR("Unsupported screen format %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
@@ -2250,17 +2302,17 @@ static int dce_v11_0_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(mmVIEWPORT_SIZE + amdgpu_crtc->crtc_offset,
               (viewport_w << 16) | viewport_h);
 
-       /* set pageflip to happen only at start of vblank interval (front porch) */
-       WREG32(mmCRTC_MASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 3);
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(mmCRTC_MASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->primary->fb) {
                amdgpu_fb = to_amdgpu_framebuffer(fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r != 0))
                        return r;
-               amdgpu_bo_unpin(rbo);
-               amdgpu_bo_unreserve(rbo);
+               amdgpu_bo_unpin(abo);
+               amdgpu_bo_unreserve(abo);
        }
 
        /* Bytes per pixel may have changed */
@@ -2708,7 +2760,7 @@ static const struct drm_crtc_funcs dce_v11_0_crtc_funcs = {
        .gamma_set = dce_v11_0_crtc_gamma_set,
        .set_config = amdgpu_crtc_set_config,
        .destroy = dce_v11_0_crtc_destroy,
-       .page_flip = amdgpu_crtc_page_flip,
+       .page_flip_target = amdgpu_crtc_page_flip_target,
 };
 
 static void dce_v11_0_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -2775,16 +2827,16 @@ static void dce_v11_0_crtc_disable(struct drm_crtc *crtc)
        if (crtc->primary->fb) {
                int r;
                struct amdgpu_framebuffer *amdgpu_fb;
-               struct amdgpu_bo *rbo;
+               struct amdgpu_bo *abo;
 
                amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r))
-                       DRM_ERROR("failed to reserve rbo before unpin\n");
+                       DRM_ERROR("failed to reserve abo before unpin\n");
                else {
-                       amdgpu_bo_unpin(rbo);
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unpin(abo);
+                       amdgpu_bo_unreserve(abo);
                }
        }
        /* disable the GRPH */
@@ -2999,24 +3051,22 @@ static int dce_v11_0_early_init(void *handle)
        dce_v11_0_set_display_funcs(adev);
        dce_v11_0_set_irq_funcs(adev);
 
+       adev->mode_info.num_crtc = dce_v11_0_get_num_crtc(adev);
+
        switch (adev->asic_type) {
        case CHIP_CARRIZO:
-               adev->mode_info.num_crtc = 3;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 9;
                break;
        case CHIP_STONEY:
-               adev->mode_info.num_crtc = 2;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 9;
                break;
        case CHIP_POLARIS10:
-               adev->mode_info.num_crtc = 6;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 6;
                break;
        case CHIP_POLARIS11:
-               adev->mode_info.num_crtc = 5;
                adev->mode_info.num_hpd = 5;
                adev->mode_info.num_dig = 5;
                break;
@@ -3109,6 +3159,7 @@ static int dce_v11_0_sw_fini(void *handle)
 
        dce_v11_0_afmt_fini(adev);
 
+       drm_mode_config_cleanup(adev->ddev);
        adev->mode_info.mode_config_initialized = false;
 
        return 0;
index 84e4618..1f58a65 100644 (file)
@@ -26,4 +26,6 @@
 
 extern const struct amd_ip_funcs dce_v11_0_ip_funcs;
 
+void dce_v11_0_disable_dce(struct amdgpu_device *adev);
+
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c
new file mode 100644 (file)
index 0000000..b948d6c
--- /dev/null
@@ -0,0 +1,3176 @@
+/*
+ * Copyright 2015 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 "amdgpu.h"
+#include "amdgpu_pm.h"
+#include "amdgpu_i2c.h"
+#include "atom.h"
+#include "amdgpu_atombios.h"
+#include "atombios_crtc.h"
+#include "atombios_encoders.h"
+#include "amdgpu_pll.h"
+#include "amdgpu_connectors.h"
+#include "si/si_reg.h"
+#include "si/sid.h"
+
+static void dce_v6_0_set_display_funcs(struct amdgpu_device *adev);
+static void dce_v6_0_set_irq_funcs(struct amdgpu_device *adev);
+
+static const u32 crtc_offsets[6] =
+{
+       SI_CRTC0_REGISTER_OFFSET,
+       SI_CRTC1_REGISTER_OFFSET,
+       SI_CRTC2_REGISTER_OFFSET,
+       SI_CRTC3_REGISTER_OFFSET,
+       SI_CRTC4_REGISTER_OFFSET,
+       SI_CRTC5_REGISTER_OFFSET
+};
+
+static const uint32_t dig_offsets[] = {
+       SI_CRTC0_REGISTER_OFFSET,
+       SI_CRTC1_REGISTER_OFFSET,
+       SI_CRTC2_REGISTER_OFFSET,
+       SI_CRTC3_REGISTER_OFFSET,
+       SI_CRTC4_REGISTER_OFFSET,
+       SI_CRTC5_REGISTER_OFFSET,
+       (0x13830 - 0x7030) >> 2,
+};
+
+static const struct {
+       uint32_t        reg;
+       uint32_t        vblank;
+       uint32_t        vline;
+       uint32_t        hpd;
+
+} interrupt_status_offsets[6] = { {
+       .reg = DISP_INTERRUPT_STATUS,
+       .vblank = DISP_INTERRUPT_STATUS__LB_D1_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS__LB_D1_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS__DC_HPD1_INTERRUPT_MASK
+}, {
+       .reg = DISP_INTERRUPT_STATUS_CONTINUE,
+       .vblank = DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS_CONTINUE__DC_HPD2_INTERRUPT_MASK
+}, {
+       .reg = DISP_INTERRUPT_STATUS_CONTINUE2,
+       .vblank = DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS_CONTINUE2__DC_HPD3_INTERRUPT_MASK
+}, {
+       .reg = DISP_INTERRUPT_STATUS_CONTINUE3,
+       .vblank = DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS_CONTINUE3__DC_HPD4_INTERRUPT_MASK
+}, {
+       .reg = DISP_INTERRUPT_STATUS_CONTINUE4,
+       .vblank = DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS_CONTINUE4__DC_HPD5_INTERRUPT_MASK
+}, {
+       .reg = DISP_INTERRUPT_STATUS_CONTINUE5,
+       .vblank = DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VBLANK_INTERRUPT_MASK,
+       .vline = DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VLINE_INTERRUPT_MASK,
+       .hpd = DISP_INTERRUPT_STATUS_CONTINUE5__DC_HPD6_INTERRUPT_MASK
+} };
+
+static const uint32_t hpd_int_control_offsets[6] = {
+       DC_HPD1_INT_CONTROL,
+       DC_HPD2_INT_CONTROL,
+       DC_HPD3_INT_CONTROL,
+       DC_HPD4_INT_CONTROL,
+       DC_HPD5_INT_CONTROL,
+       DC_HPD6_INT_CONTROL,
+};
+
+static u32 dce_v6_0_audio_endpt_rreg(struct amdgpu_device *adev,
+                                    u32 block_offset, u32 reg)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_endpt_rreg ----no impl!!!!\n");
+       return 0;
+}
+
+static void dce_v6_0_audio_endpt_wreg(struct amdgpu_device *adev,
+                                     u32 block_offset, u32 reg, u32 v)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_endpt_wreg ----no impl!!!!\n");
+}
+
+static bool dce_v6_0_is_in_vblank(struct amdgpu_device *adev, int crtc)
+{
+       if (RREG32(EVERGREEN_CRTC_STATUS + crtc_offsets[crtc]) & EVERGREEN_CRTC_V_BLANK)
+               return true;
+       else
+               return false;
+}
+
+static bool dce_v6_0_is_counter_moving(struct amdgpu_device *adev, int crtc)
+{
+       u32 pos1, pos2;
+
+       pos1 = RREG32(EVERGREEN_CRTC_STATUS_POSITION + crtc_offsets[crtc]);
+       pos2 = RREG32(EVERGREEN_CRTC_STATUS_POSITION + crtc_offsets[crtc]);
+
+       if (pos1 != pos2)
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce_v6_0_wait_for_vblank - vblank wait asic callback.
+ *
+ * @crtc: crtc to wait for vblank on
+ *
+ * Wait for vblank on the requested crtc (evergreen+).
+ */
+static void dce_v6_0_vblank_wait(struct amdgpu_device *adev, int crtc)
+{
+       unsigned i = 100;
+
+       if (crtc >= adev->mode_info.num_crtc)
+               return;
+
+       if (!(RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[crtc]) & EVERGREEN_CRTC_MASTER_EN))
+               return;
+
+       /* depending on when we hit vblank, we may be close to active; if so,
+        * wait for another frame.
+        */
+       while (dce_v6_0_is_in_vblank(adev, crtc)) {
+               if (i++ == 100) {
+                       i = 0;
+                       if (!dce_v6_0_is_counter_moving(adev, crtc))
+                               break;
+               }
+       }
+
+       while (!dce_v6_0_is_in_vblank(adev, crtc)) {
+               if (i++ == 100) {
+                       i = 0;
+                       if (!dce_v6_0_is_counter_moving(adev, crtc))
+                               break;
+               }
+       }
+}
+
+static u32 dce_v6_0_vblank_get_counter(struct amdgpu_device *adev, int crtc)
+{
+       if (crtc >= adev->mode_info.num_crtc)
+               return 0;
+       else
+               return RREG32(CRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]);
+}
+
+static void dce_v6_0_pageflip_interrupt_init(struct amdgpu_device *adev)
+{
+       unsigned i;
+
+       /* Enable pflip interrupts */
+       for (i = 0; i < adev->mode_info.num_crtc; i++)
+               amdgpu_irq_get(adev, &adev->pageflip_irq, i);
+}
+
+static void dce_v6_0_pageflip_interrupt_fini(struct amdgpu_device *adev)
+{
+       unsigned i;
+
+       /* Disable pflip interrupts */
+       for (i = 0; i < adev->mode_info.num_crtc; i++)
+               amdgpu_irq_put(adev, &adev->pageflip_irq, i);
+}
+
+/**
+ * dce_v6_0_page_flip - pageflip callback.
+ *
+ * @adev: amdgpu_device pointer
+ * @crtc_id: crtc to cleanup pageflip on
+ * @crtc_base: new address of the crtc (GPU MC address)
+ *
+ * Does the actual pageflip (evergreen+).
+ * During vblank we take the crtc lock and wait for the update_pending
+ * bit to go high, when it does, we release the lock, and allow the
+ * double buffered update to take place.
+ * Returns the current update pending status.
+ */
+static void dce_v6_0_page_flip(struct amdgpu_device *adev,
+                              int crtc_id, u64 crtc_base, bool async)
+{
+       struct amdgpu_crtc *amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
+
+       /* flip at hsync for async, default is vsync */
+       WREG32(EVERGREEN_GRPH_FLIP_CONTROL + amdgpu_crtc->crtc_offset, async ?
+              EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN : 0);
+       /* update the scanout addresses */
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc->crtc_offset,
+              upper_32_bits(crtc_base));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc->crtc_offset,
+              (u32)crtc_base);
+
+       /* post the write */
+       RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc->crtc_offset);
+}
+
+static int dce_v6_0_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
+                                       u32 *vbl, u32 *position)
+{
+       if ((crtc < 0) || (crtc >= adev->mode_info.num_crtc))
+               return -EINVAL;
+       *vbl = RREG32(EVERGREEN_CRTC_V_BLANK_START_END + crtc_offsets[crtc]);
+       *position = RREG32(EVERGREEN_CRTC_STATUS_POSITION + crtc_offsets[crtc]);
+
+       return 0;
+
+}
+
+/**
+ * dce_v6_0_hpd_sense - hpd sense callback.
+ *
+ * @adev: amdgpu_device pointer
+ * @hpd: hpd (hotplug detect) pin
+ *
+ * Checks if a digital monitor is connected (evergreen+).
+ * Returns true if connected, false if not connected.
+ */
+static bool dce_v6_0_hpd_sense(struct amdgpu_device *adev,
+                              enum amdgpu_hpd_id hpd)
+{
+       bool connected = false;
+
+       switch (hpd) {
+       case AMDGPU_HPD_1:
+               if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       case AMDGPU_HPD_2:
+               if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       case AMDGPU_HPD_3:
+               if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       case AMDGPU_HPD_4:
+               if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       case AMDGPU_HPD_5:
+               if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       case AMDGPU_HPD_6:
+               if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE)
+                       connected = true;
+               break;
+       default:
+               break;
+       }
+
+       return connected;
+}
+
+/**
+ * dce_v6_0_hpd_set_polarity - hpd set polarity callback.
+ *
+ * @adev: amdgpu_device pointer
+ * @hpd: hpd (hotplug detect) pin
+ *
+ * Set the polarity of the hpd pin (evergreen+).
+ */
+static void dce_v6_0_hpd_set_polarity(struct amdgpu_device *adev,
+                                     enum amdgpu_hpd_id hpd)
+{
+       u32 tmp;
+       bool connected = dce_v6_0_hpd_sense(adev, hpd);
+
+       switch (hpd) {
+       case AMDGPU_HPD_1:
+               tmp = RREG32(DC_HPD1_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD1_INT_CONTROL, tmp);
+               break;
+       case AMDGPU_HPD_2:
+               tmp = RREG32(DC_HPD2_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD2_INT_CONTROL, tmp);
+               break;
+       case AMDGPU_HPD_3:
+               tmp = RREG32(DC_HPD3_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD3_INT_CONTROL, tmp);
+               break;
+       case AMDGPU_HPD_4:
+               tmp = RREG32(DC_HPD4_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD4_INT_CONTROL, tmp);
+               break;
+       case AMDGPU_HPD_5:
+               tmp = RREG32(DC_HPD5_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD5_INT_CONTROL, tmp);
+                       break;
+       case AMDGPU_HPD_6:
+               tmp = RREG32(DC_HPD6_INT_CONTROL);
+               if (connected)
+                       tmp &= ~DC_HPDx_INT_POLARITY;
+               else
+                       tmp |= DC_HPDx_INT_POLARITY;
+               WREG32(DC_HPD6_INT_CONTROL, tmp);
+               break;
+       default:
+               break;
+       }
+}
+
+/**
+ * dce_v6_0_hpd_init - hpd setup callback.
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Setup the hpd pins used by the card (evergreen+).
+ * Enable the pin, set the polarity, and enable the hpd interrupts.
+ */
+static void dce_v6_0_hpd_init(struct amdgpu_device *adev)
+{
+       struct drm_device *dev = adev->ddev;
+       struct drm_connector *connector;
+       u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) |
+               DC_HPDx_RX_INT_TIMER(0xfa) | DC_HPDx_EN;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
+
+               switch (amdgpu_connector->hpd.hpd) {
+               case AMDGPU_HPD_1:
+                       WREG32(DC_HPD1_CONTROL, tmp);
+                       break;
+               case AMDGPU_HPD_2:
+                       WREG32(DC_HPD2_CONTROL, tmp);
+                       break;
+               case AMDGPU_HPD_3:
+                       WREG32(DC_HPD3_CONTROL, tmp);
+                       break;
+               case AMDGPU_HPD_4:
+                       WREG32(DC_HPD4_CONTROL, tmp);
+                       break;
+               case AMDGPU_HPD_5:
+                       WREG32(DC_HPD5_CONTROL, tmp);
+                       break;
+               case AMDGPU_HPD_6:
+                       WREG32(DC_HPD6_CONTROL, tmp);
+                       break;
+               default:
+                       break;
+               }
+
+               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
+                        * aux dp channel on imac and help (but not completely fix)
+                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+                        * also avoid interrupt storms during dpms.
+                        */
+                       u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl;
+
+                       switch (amdgpu_connector->hpd.hpd) {
+                       case AMDGPU_HPD_1:
+                               dc_hpd_int_cntl_reg = DC_HPD1_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_2:
+                               dc_hpd_int_cntl_reg = DC_HPD2_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_3:
+                               dc_hpd_int_cntl_reg = DC_HPD3_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_4:
+                               dc_hpd_int_cntl_reg = DC_HPD4_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_5:
+                               dc_hpd_int_cntl_reg = DC_HPD5_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_6:
+                               dc_hpd_int_cntl_reg = DC_HPD6_INT_CONTROL;
+                               break;
+                       default:
+                               continue;
+                       }
+
+                       dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+                       dc_hpd_int_cntl &= ~DC_HPDx_INT_EN;
+                       WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+                       continue;
+               }
+
+               dce_v6_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
+               amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
+       }
+
+}
+
+/**
+ * dce_v6_0_hpd_fini - hpd tear down callback.
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Tear down the hpd pins used by the card (evergreen+).
+ * Disable the hpd interrupts.
+ */
+static void dce_v6_0_hpd_fini(struct amdgpu_device *adev)
+{
+       struct drm_device *dev = adev->ddev;
+       struct drm_connector *connector;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
+
+               switch (amdgpu_connector->hpd.hpd) {
+               case AMDGPU_HPD_1:
+                       WREG32(DC_HPD1_CONTROL, 0);
+                       break;
+               case AMDGPU_HPD_2:
+                       WREG32(DC_HPD2_CONTROL, 0);
+                       break;
+               case AMDGPU_HPD_3:
+                       WREG32(DC_HPD3_CONTROL, 0);
+                       break;
+               case AMDGPU_HPD_4:
+                       WREG32(DC_HPD4_CONTROL, 0);
+                       break;
+               case AMDGPU_HPD_5:
+                       WREG32(DC_HPD5_CONTROL, 0);
+                       break;
+               case AMDGPU_HPD_6:
+                       WREG32(DC_HPD6_CONTROL, 0);
+                       break;
+               default:
+                       break;
+               }
+               amdgpu_irq_put(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
+       }
+}
+
+static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev)
+{
+       return SI_DC_GPIO_HPD_A;
+}
+
+static bool dce_v6_0_is_display_hung(struct amdgpu_device *adev)
+{
+       DRM_INFO("xxxx: dce_v6_0_is_display_hung ----no imp!!!!!\n");
+
+       return true;
+}
+
+static u32 evergreen_get_vblank_counter(struct amdgpu_device* adev, int crtc)
+{
+       if (crtc >= adev->mode_info.num_crtc)
+               return 0;
+       else
+               return RREG32(CRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]);
+}
+
+static void dce_v6_0_stop_mc_access(struct amdgpu_device *adev,
+                                   struct amdgpu_mode_mc_save *save)
+{
+       u32 crtc_enabled, tmp, frame_count;
+       int i, j;
+
+       save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
+       save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
+
+       /* disable VGA render */
+       WREG32(VGA_RENDER_CONTROL, 0);
+
+       /* blank the display controllers */
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               crtc_enabled = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]) & EVERGREEN_CRTC_MASTER_EN;
+               if (crtc_enabled) {
+                       save->crtc_enabled[i] = true;
+                       tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]);
+
+                       if (!(tmp & EVERGREEN_CRTC_BLANK_DATA_EN)) {
+                               dce_v6_0_vblank_wait(adev, i);
+                               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
+                               tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
+                               WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
+                               WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
+                       }
+                       /* wait for the next frame */
+                       frame_count = evergreen_get_vblank_counter(adev, i);
+                       for (j = 0; j < adev->usec_timeout; j++) {
+                               if (evergreen_get_vblank_counter(adev, i) != frame_count)
+                                       break;
+                               udelay(1);
+                       }
+
+                       /* XXX this is a hack to avoid strange behavior with EFI on certain systems */
+                       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
+                       tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
+                       tmp &= ~EVERGREEN_CRTC_MASTER_EN;
+                       WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp);
+                       WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
+                       save->crtc_enabled[i] = false;
+                       /* ***** */
+               } else {
+                       save->crtc_enabled[i] = false;
+               }
+       }
+}
+
+static void dce_v6_0_resume_mc_access(struct amdgpu_device *adev,
+                                     struct amdgpu_mode_mc_save *save)
+{
+       u32 tmp;
+       int i, j;
+
+       /* update crtc base addresses */
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
+                      upper_32_bits(adev->mc.vram_start));
+               WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
+                      upper_32_bits(adev->mc.vram_start));
+               WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i],
+                      (u32)adev->mc.vram_start);
+               WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i],
+                      (u32)adev->mc.vram_start);
+       }
+
+       WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(adev->mc.vram_start));
+       WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS, (u32)adev->mc.vram_start);
+
+       /* unlock regs and wait for update */
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               if (save->crtc_enabled[i]) {
+                       tmp = RREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i]);
+                       if ((tmp & 0x7) != 3) {
+                               tmp &= ~0x7;
+                               tmp |= 0x3;
+                               WREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i], tmp);
+                       }
+                       tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]);
+                       if (tmp & EVERGREEN_GRPH_UPDATE_LOCK) {
+                               tmp &= ~EVERGREEN_GRPH_UPDATE_LOCK;
+                               WREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i], tmp);
+                       }
+                       tmp = RREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i]);
+                       if (tmp & 1) {
+                               tmp &= ~1;
+                               WREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i], tmp);
+                       }
+                       for (j = 0; j < adev->usec_timeout; j++) {
+                               tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]);
+                               if ((tmp & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING) == 0)
+                                       break;
+                               udelay(1);
+                       }
+               }
+       }
+
+       /* Unlock vga access */
+       WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
+       mdelay(1);
+       WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
+
+}
+
+static void dce_v6_0_set_vga_render_state(struct amdgpu_device *adev,
+                                         bool render)
+{
+       if (!render) 
+               WREG32(R_000300_VGA_RENDER_CONTROL,
+                       RREG32(R_000300_VGA_RENDER_CONTROL) & C_000300_VGA_VSTATUS_CNTL);
+
+}
+
+static void dce_v6_0_program_fmt(struct drm_encoder *encoder)
+{
+
+       struct drm_device *dev = encoder->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder);
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(encoder->crtc);
+       int bpc = 0;
+       u32 tmp = 0;
+       enum amdgpu_connector_dither dither = AMDGPU_FMT_DITHER_DISABLE;
+
+       if (connector) {
+               struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
+               bpc = amdgpu_connector_get_monitor_bpc(connector);
+               dither = amdgpu_connector->dither;
+       }
+
+       /* LVDS FMT is set up by atom */
+       if (amdgpu_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
+               return;
+
+       if (bpc == 0)
+               return;
+
+
+       switch (bpc) {
+       case 6:
+               if (dither == AMDGPU_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN);
+               else
+                       tmp |= FMT_TRUNCATE_EN;
+               break;
+       case 8:
+               if (dither == AMDGPU_FMT_DITHER_ENABLE)
+                       /* XXX sort out optimal dither settings */
+                       tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
+                               FMT_RGB_RANDOM_ENABLE |
+                               FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH);
+               else
+                       tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH);
+               break;
+       case 10:
+       default:
+               /* not needed */
+               break;
+       }
+
+       WREG32(FMT_BIT_DEPTH_CONTROL + amdgpu_crtc->crtc_offset, tmp);
+}
+
+/**
+ * cik_get_number_of_dram_channels - get the number of dram channels
+ *
+ * @adev: amdgpu_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 si_get_number_of_dram_channels(struct amdgpu_device *adev)
+{
+       u32 tmp = RREG32(MC_SHARED_CHMAP);
+
+       switch ((tmp & MC_SHARED_CHMAP__NOOFCHAN_MASK) >> MC_SHARED_CHMAP__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 dce6_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 */
+};
+
+/**
+ * dce_v6_0_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 dce_v6_0_dram_bandwidth(struct dce6_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);
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_dram_bandwidth_for_display(struct dce6_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);
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_data_return_bandwidth(struct dce6_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);
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_dmif_request_bandwidth(struct dce6_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);
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_available_bandwidth(struct dce6_wm_params *wm)
+{
+       /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
+       u32 dram_bandwidth = dce_v6_0_dram_bandwidth(wm);
+       u32 data_return_bandwidth = dce_v6_0_data_return_bandwidth(wm);
+       u32 dmif_req_bandwidth = dce_v6_0_dmif_request_bandwidth(wm);
+
+       return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth));
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_average_bandwidth(struct dce6_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);
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_latency_watermark(struct dce6_wm_params *wm)
+{
+       /* First calculate the latency in ns */
+       u32 mc_latency = 2000; /* 2000 ns. */
+       u32 available_bandwidth = dce_v6_0_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);
+
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display(struct dce6_wm_params *wm)
+{
+       if (dce_v6_0_average_bandwidth(wm) <=
+           (dce_v6_0_dram_bandwidth_for_display(wm) / wm->num_heads))
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_average_bandwidth_vs_available_bandwidth(struct dce6_wm_params *wm)
+{
+       if (dce_v6_0_average_bandwidth(wm) <=
+           (dce_v6_0_available_bandwidth(wm) / wm->num_heads))
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce_v6_0_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 dce_v6_0_check_latency_hiding(struct dce6_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 (dce_v6_0_latency_watermark(wm) <= latency_hiding)
+               return true;
+       else
+               return false;
+}
+
+/**
+ * dce_v6_0_program_watermarks - program display watermarks
+ *
+ * @adev: amdgpu_device pointer
+ * @amdgpu_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 dce_v6_0_program_watermarks(struct amdgpu_device *adev,
+                                       struct amdgpu_crtc *amdgpu_crtc,
+                                       u32 lb_size, u32 num_heads)
+{
+       struct drm_display_mode *mode = &amdgpu_crtc->base.mode;
+       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;
+       u32 priority_a_mark = 0, priority_b_mark = 0;
+       u32 priority_a_cnt = PRIORITY_OFF;
+       u32 priority_b_cnt = PRIORITY_OFF;
+       u32 tmp, arb_control3;
+       fixed20_12 a, b, c;
+
+       if (amdgpu_crtc->base.enabled && num_heads && mode) {
+               pixel_period = 1000000 / (u32)mode->clock;
+               line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+               priority_a_cnt = 0;
+               priority_b_cnt = 0;
+
+               dram_channels = si_get_number_of_dram_channels(adev);
+
+               /* watermark for high clocks */
+               if (adev->pm.dpm_enabled) {
+                       wm_high.yclk =
+                               amdgpu_dpm_get_mclk(adev, false) * 10;
+                       wm_high.sclk =
+                               amdgpu_dpm_get_sclk(adev, false) * 10;
+               } else {
+                       wm_high.yclk = adev->pm.current_mclk * 10;
+                       wm_high.sclk = adev->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 = amdgpu_crtc->vsc;
+               wm_high.vtaps = 1;
+               if (amdgpu_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;
+
+               if (adev->pm.dpm_enabled) {
+               /* watermark for low clocks */
+                       wm_low.yclk =
+                               amdgpu_dpm_get_mclk(adev, true) * 10;
+                       wm_low.sclk =
+                               amdgpu_dpm_get_sclk(adev, true) * 10;
+               } else {
+                       wm_low.yclk = adev->pm.current_mclk * 10;
+                       wm_low.sclk = adev->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 = amdgpu_crtc->vsc;
+               wm_low.vtaps = 1;
+               if (amdgpu_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(dce_v6_0_latency_watermark(&wm_high), (u32)65535);
+               /* set for low clocks */
+               latency_watermark_b = min(dce_v6_0_latency_watermark(&wm_low), (u32)65535);
+
+               /* possibly force display priority to high */
+               /* should really do this at mode validation time... */
+               if (!dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) ||
+                   !dce_v6_0_average_bandwidth_vs_available_bandwidth(&wm_high) ||
+                   !dce_v6_0_check_latency_hiding(&wm_high) ||
+                   (adev->mode_info.disp_priority == 2)) {
+                       DRM_DEBUG_KMS("force priority to high\n");
+                       priority_a_cnt |= PRIORITY_ALWAYS_ON;
+                       priority_b_cnt |= PRIORITY_ALWAYS_ON;
+               }
+               if (!dce_v6_0_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) ||
+                   !dce_v6_0_average_bandwidth_vs_available_bandwidth(&wm_low) ||
+                   !dce_v6_0_check_latency_hiding(&wm_low) ||
+                   (adev->mode_info.disp_priority == 2)) {
+                       DRM_DEBUG_KMS("force priority to high\n");
+                       priority_a_cnt |= PRIORITY_ALWAYS_ON;
+                       priority_b_cnt |= PRIORITY_ALWAYS_ON;
+               }
+
+               a.full = dfixed_const(1000);
+               b.full = dfixed_const(mode->clock);
+               b.full = dfixed_div(b, a);
+               c.full = dfixed_const(latency_watermark_a);
+               c.full = dfixed_mul(c, b);
+               c.full = dfixed_mul(c, amdgpu_crtc->hsc);
+               c.full = dfixed_div(c, a);
+               a.full = dfixed_const(16);
+               c.full = dfixed_div(c, a);
+               priority_a_mark = dfixed_trunc(c);
+               priority_a_cnt |= priority_a_mark & PRIORITY_MARK_MASK;
+
+               a.full = dfixed_const(1000);
+               b.full = dfixed_const(mode->clock);
+               b.full = dfixed_div(b, a);
+               c.full = dfixed_const(latency_watermark_b);
+               c.full = dfixed_mul(c, b);
+               c.full = dfixed_mul(c, amdgpu_crtc->hsc);
+               c.full = dfixed_div(c, a);
+               a.full = dfixed_const(16);
+               c.full = dfixed_div(c, a);
+               priority_b_mark = dfixed_trunc(c);
+               priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK;
+       }
+
+       /* select wm A */
+       arb_control3 = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc->crtc_offset);
+       tmp = arb_control3;
+       tmp &= ~LATENCY_WATERMARK_MASK(3);
+       tmp |= LATENCY_WATERMARK_MASK(1);
+       WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc->crtc_offset, tmp);
+       WREG32(DPG_PIPE_LATENCY_CONTROL + amdgpu_crtc->crtc_offset,
+              (LATENCY_LOW_WATERMARK(latency_watermark_a) |
+               LATENCY_HIGH_WATERMARK(line_time)));
+       /* select wm B */
+       tmp = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc->crtc_offset);
+       tmp &= ~LATENCY_WATERMARK_MASK(3);
+       tmp |= LATENCY_WATERMARK_MASK(2);
+       WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc->crtc_offset, tmp);
+       WREG32(DPG_PIPE_LATENCY_CONTROL + amdgpu_crtc->crtc_offset,
+              (LATENCY_LOW_WATERMARK(latency_watermark_b) |
+               LATENCY_HIGH_WATERMARK(line_time)));
+       /* restore original selection */
+       WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + amdgpu_crtc->crtc_offset, arb_control3);
+
+       /* write the priority marks */
+       WREG32(PRIORITY_A_CNT + amdgpu_crtc->crtc_offset, priority_a_cnt);
+       WREG32(PRIORITY_B_CNT + amdgpu_crtc->crtc_offset, priority_b_cnt);
+
+       /* save values for DPM */
+       amdgpu_crtc->line_time = line_time;
+       amdgpu_crtc->wm_high = latency_watermark_a;
+}
+
+/* watermark setup */
+static u32 dce_v6_0_line_buffer_adjust(struct amdgpu_device *adev,
+                                  struct amdgpu_crtc *amdgpu_crtc,
+                                  struct drm_display_mode *mode,
+                                  struct drm_display_mode *other_mode)
+{
+       u32 tmp, buffer_alloc, i;
+       u32 pipe_offset = amdgpu_crtc->crtc_id * 0x8;
+       /*
+        * Line Buffer Setup
+        * There are 3 line buffers, each one shared by 2 display controllers.
+        * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between
+        * the display controllers.  The paritioning is done via one of four
+        * preset allocations specified in bits 21:20:
+        *  0 - half lb
+        *  2 - whole lb, other crtc must be disabled
+        */
+       /* this can get tricky if we have two large displays on a paired group
+        * of crtcs.  Ideally for multiple large displays we'd assign them to
+        * non-linked crtcs for maximum line buffer allocation.
+        */
+       if (amdgpu_crtc->base.enabled && mode) {
+               if (other_mode) {
+                       tmp = 0; /* 1/2 */
+                       buffer_alloc = 1;
+               } else {
+                       tmp = 2; /* whole */
+                       buffer_alloc = 2;
+               }
+       } else {
+               tmp = 0;
+               buffer_alloc = 0;
+       }
+
+       WREG32(DC_LB_MEMORY_SPLIT + amdgpu_crtc->crtc_offset,
+              DC_LB_MEMORY_CONFIG(tmp));
+
+       WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset,
+              DMIF_BUFFERS_ALLOCATED(buffer_alloc));
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) &
+                   DMIF_BUFFERS_ALLOCATED_COMPLETED)
+                       break;
+               udelay(1);
+       }
+
+       if (amdgpu_crtc->base.enabled && mode) {
+               switch (tmp) {
+               case 0:
+               default:
+                       return 4096 * 2;
+               case 2:
+                       return 8192 * 2;
+               }
+       }
+
+       /* controller not enabled, so no lb used */
+       return 0;
+}
+
+
+/**
+ *
+ * dce_v6_0_bandwidth_update - program display watermarks
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Calculate and program the display watermarks and line
+ * buffer allocation (CIK).
+ */
+static void dce_v6_0_bandwidth_update(struct amdgpu_device *adev)
+{
+       struct drm_display_mode *mode0 = NULL;
+       struct drm_display_mode *mode1 = NULL;
+       u32 num_heads = 0, lb_size;
+       int i;
+
+       if (!adev->mode_info.mode_config_initialized)
+               return;
+
+       amdgpu_update_display_priority(adev);
+
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               if (adev->mode_info.crtcs[i]->base.enabled)
+                       num_heads++;
+       }
+       for (i = 0; i < adev->mode_info.num_crtc; i += 2) {
+               mode0 = &adev->mode_info.crtcs[i]->base.mode;
+               mode1 = &adev->mode_info.crtcs[i+1]->base.mode;
+               lb_size = dce_v6_0_line_buffer_adjust(adev, adev->mode_info.crtcs[i], mode0, mode1);
+               dce_v6_0_program_watermarks(adev, adev->mode_info.crtcs[i], lb_size, num_heads);
+               lb_size = dce_v6_0_line_buffer_adjust(adev, adev->mode_info.crtcs[i+1], mode1, mode0);
+               dce_v6_0_program_watermarks(adev, adev->mode_info.crtcs[i+1], lb_size, num_heads);
+       }
+}
+/*
+static void dce_v6_0_audio_get_connected_pins(struct amdgpu_device *adev)
+{
+       int i;
+       u32 offset, tmp;
+
+       for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
+               offset = adev->mode_info.audio.pin[i].offset;
+               tmp = RREG32_AUDIO_ENDPT(offset,
+                                     AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT);
+               if (((tmp & PORT_CONNECTIVITY_MASK) >> PORT_CONNECTIVITY_SHIFT) == 1)
+                       adev->mode_info.audio.pin[i].connected = false;
+               else
+                       adev->mode_info.audio.pin[i].connected = true;
+       }
+
+}
+
+static struct amdgpu_audio_pin *dce_v6_0_audio_get_pin(struct amdgpu_device *adev)
+{
+       int i;
+
+       dce_v6_0_audio_get_connected_pins(adev);
+
+       for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
+               if (adev->mode_info.audio.pin[i].connected)
+                       return &adev->mode_info.audio.pin[i];
+       }
+       DRM_ERROR("No connected audio pins found!\n");
+       return NULL;
+}
+
+static void dce_v6_0_afmt_audio_select_pin(struct drm_encoder *encoder)
+{
+       struct amdgpu_device *adev = encoder->dev->dev_private;
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
+       u32 offset;
+
+       if (!dig || !dig->afmt || !dig->afmt->pin)
+               return;
+
+       offset = dig->afmt->offset;
+
+       WREG32(AFMT_AUDIO_SRC_CONTROL + offset,
+              AFMT_AUDIO_SRC_SELECT(dig->afmt->pin->id));
+
+}
+
+static void dce_v6_0_audio_write_latency_fields(struct drm_encoder *encoder,
+                                               struct drm_display_mode *mode)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_write_latency_fields---no imp!!!!!\n");
+}
+
+static void dce_v6_0_audio_write_speaker_allocation(struct drm_encoder *encoder)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_write_speaker_allocation---no imp!!!!!\n");
+}
+
+static void dce_v6_0_audio_write_sad_regs(struct drm_encoder *encoder)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_write_sad_regs---no imp!!!!!\n");
+
+}
+*/
+static void dce_v6_0_audio_enable(struct amdgpu_device *adev,
+                                 struct amdgpu_audio_pin *pin,
+                                 bool enable)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_enable---no imp!!!!!\n");
+}
+
+static const u32 pin_offsets[7] =
+{
+       (0x1780 - 0x1780),
+       (0x1786 - 0x1780),
+       (0x178c - 0x1780),
+       (0x1792 - 0x1780),
+       (0x1798 - 0x1780),
+       (0x179d - 0x1780),
+       (0x17a4 - 0x1780),
+};
+
+static int dce_v6_0_audio_init(struct amdgpu_device *adev)
+{
+       return 0;
+}
+
+static void dce_v6_0_audio_fini(struct amdgpu_device *adev)
+{
+
+}
+
+/*
+static void dce_v6_0_afmt_update_ACR(struct drm_encoder *encoder, uint32_t clock)
+{
+       DRM_INFO("xxxx: dce_v6_0_afmt_update_ACR---no imp!!!!!\n");
+}
+*/
+/*
+ * build a HDMI Video Info Frame
+ */
+/*
+static void dce_v6_0_afmt_update_avi_infoframe(struct drm_encoder *encoder,
+                                              void *buffer, size_t size)
+{
+       DRM_INFO("xxxx: dce_v6_0_afmt_update_avi_infoframe---no imp!!!!!\n");
+}
+
+static void dce_v6_0_audio_set_dto(struct drm_encoder *encoder, u32 clock)
+{
+       DRM_INFO("xxxx: dce_v6_0_audio_set_dto---no imp!!!!!\n");
+}
+*/
+/*
+ * update the info frames with the data from the current display mode
+ */
+static void dce_v6_0_afmt_setmode(struct drm_encoder *encoder,
+                                 struct drm_display_mode *mode)
+{
+       DRM_INFO("xxxx: dce_v6_0_afmt_setmode ----no impl !!!!!!!!\n");
+}
+
+static void dce_v6_0_afmt_enable(struct drm_encoder *encoder, bool enable)
+{
+       struct drm_device *dev = encoder->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
+
+       if (!dig || !dig->afmt)
+               return;
+
+       /* Silent, r600_hdmi_enable will raise WARN for us */
+       if (enable && dig->afmt->enabled)
+               return;
+       if (!enable && !dig->afmt->enabled)
+               return;
+
+       if (!enable && dig->afmt->pin) {
+               dce_v6_0_audio_enable(adev, dig->afmt->pin, false);
+               dig->afmt->pin = NULL;
+       }
+
+       dig->afmt->enabled = enable;
+
+       DRM_DEBUG("%sabling AFMT interface @ 0x%04X for encoder 0x%x\n",
+                 enable ? "En" : "Dis", dig->afmt->offset, amdgpu_encoder->encoder_id);
+}
+
+static int dce_v6_0_afmt_init(struct amdgpu_device *adev)
+{
+       int i, j;
+
+       for (i = 0; i < adev->mode_info.num_dig; i++)
+               adev->mode_info.afmt[i] = NULL;
+
+       /* DCE6 has audio blocks tied to DIG encoders */
+       for (i = 0; i < adev->mode_info.num_dig; i++) {
+               adev->mode_info.afmt[i] = kzalloc(sizeof(struct amdgpu_afmt), GFP_KERNEL);
+               if (adev->mode_info.afmt[i]) {
+                       adev->mode_info.afmt[i]->offset = dig_offsets[i];
+                       adev->mode_info.afmt[i]->id = i;
+               } else {
+                       for (j = 0; j < i; j++) {
+                               kfree(adev->mode_info.afmt[j]);
+                               adev->mode_info.afmt[j] = NULL;
+                       }
+                       DRM_ERROR("Out of memory allocating afmt table\n");
+                       return -ENOMEM;
+               }
+       }
+       return 0;
+}
+
+static void dce_v6_0_afmt_fini(struct amdgpu_device *adev)
+{
+       int i;
+
+       for (i = 0; i < adev->mode_info.num_dig; i++) {
+               kfree(adev->mode_info.afmt[i]);
+               adev->mode_info.afmt[i] = NULL;
+       }
+}
+
+static const u32 vga_control_regs[6] =
+{
+       AVIVO_D1VGA_CONTROL,
+       AVIVO_D2VGA_CONTROL,
+       EVERGREEN_D3VGA_CONTROL,
+       EVERGREEN_D4VGA_CONTROL,
+       EVERGREEN_D5VGA_CONTROL,
+       EVERGREEN_D6VGA_CONTROL,
+};
+
+static void dce_v6_0_vga_enable(struct drm_crtc *crtc, bool enable)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       u32 vga_control;
+
+       vga_control = RREG32(vga_control_regs[amdgpu_crtc->crtc_id]) & ~1;
+       WREG32(vga_control_regs[amdgpu_crtc->crtc_id], vga_control | (enable ? 1 : 0));
+}
+
+static void dce_v6_0_grph_enable(struct drm_crtc *crtc, bool enable)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+
+       WREG32(EVERGREEN_GRPH_ENABLE + amdgpu_crtc->crtc_offset, enable ? 1 : 0);
+}
+
+static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc,
+                                    struct drm_framebuffer *fb,
+                                    int x, int y, int atomic)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_framebuffer *amdgpu_fb;
+       struct drm_framebuffer *target_fb;
+       struct drm_gem_object *obj;
+       struct amdgpu_bo *abo;
+       uint64_t fb_location, tiling_flags;
+       uint32_t fb_format, fb_pitch_pixels, pipe_config;
+       u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
+       u32 viewport_w, viewport_h;
+       int r;
+       bool bypass_lut = false;
+
+       /* no fb bound */
+       if (!atomic && !crtc->primary->fb) {
+               DRM_DEBUG_KMS("No FB bound\n");
+               return 0;
+       }
+
+       if (atomic) {
+               amdgpu_fb = to_amdgpu_framebuffer(fb);
+               target_fb = fb;
+       } else {
+               amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
+               target_fb = crtc->primary->fb;
+       }
+
+       /* If atomic, assume fb object is pinned & idle & fenced and
+        * just update base pointers
+        */
+       obj = amdgpu_fb->obj;
+       abo = gem_to_amdgpu_bo(obj);
+       r = amdgpu_bo_reserve(abo, false);
+       if (unlikely(r != 0))
+               return r;
+
+       if (atomic) {
+               fb_location = amdgpu_bo_gpu_offset(abo);
+       } else {
+               r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
+               if (unlikely(r != 0)) {
+                       amdgpu_bo_unreserve(abo);
+                       return -EINVAL;
+               }
+       }
+
+       amdgpu_bo_get_tiling_flags(abo, &tiling_flags);
+       amdgpu_bo_unreserve(abo);
+
+       switch (target_fb->pixel_format) {
+       case DRM_FORMAT_C8:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_8BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_INDEXED));
+               break;
+       case DRM_FORMAT_XRGB4444:
+       case DRM_FORMAT_ARGB4444:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB4444));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
+#endif
+               break;
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_ARGB1555:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB1555));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
+#endif
+               break;
+       case DRM_FORMAT_BGRX5551:
+       case DRM_FORMAT_BGRA5551:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_BGRA5551));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
+#endif
+               break;
+       case DRM_FORMAT_RGB565:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_16BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB565));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN16);
+#endif
+               break;
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_ARGB8888:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB8888));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
+#endif
+               break;
+       case DRM_FORMAT_XRGB2101010:
+       case DRM_FORMAT_ARGB2101010:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_ARGB2101010));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
+#endif
+               /* Greater 8 bpc fb needs to bypass hw-lut to retain precision */
+               bypass_lut = true;
+               break;
+       case DRM_FORMAT_BGRX1010102:
+       case DRM_FORMAT_BGRA1010102:
+               fb_format = (EVERGREEN_GRPH_DEPTH(EVERGREEN_GRPH_DEPTH_32BPP) |
+                            EVERGREEN_GRPH_FORMAT(EVERGREEN_GRPH_FORMAT_BGRA1010102));
+#ifdef __BIG_ENDIAN
+               fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_8IN32);
+#endif
+               /* Greater 8 bpc fb needs to bypass hw-lut to retain precision */
+               bypass_lut = true;
+               break;
+       default:
+               DRM_ERROR("Unsupported screen format %s\n",
+                         drm_get_format_name(target_fb->pixel_format));
+               return -EINVAL;
+       }
+
+       if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_2D_TILED_THIN1) {
+               unsigned bankw, bankh, mtaspect, tile_split, num_banks;
+
+               bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH);
+               bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT);
+               mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT);
+               tile_split = AMDGPU_TILING_GET(tiling_flags, TILE_SPLIT);
+               num_banks = AMDGPU_TILING_GET(tiling_flags, NUM_BANKS);
+
+               fb_format |= EVERGREEN_GRPH_NUM_BANKS(num_banks);
+               fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1);
+               fb_format |= EVERGREEN_GRPH_TILE_SPLIT(tile_split);
+               fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw);
+               fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh);
+               fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect);
+       } else if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == ARRAY_1D_TILED_THIN1) {
+               fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1);
+       }
+
+       pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
+       fb_format |= SI_GRPH_PIPE_CONFIG(pipe_config);
+
+       dce_v6_0_vga_enable(crtc, false);
+
+       /* Make sure surface address is updated at vertical blank rather than
+        * horizontal blank
+        */
+       WREG32(EVERGREEN_GRPH_FLIP_CONTROL + amdgpu_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc->crtc_offset,
+              upper_32_bits(fb_location));
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + amdgpu_crtc->crtc_offset,
+              upper_32_bits(fb_location));
+       WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + amdgpu_crtc->crtc_offset,
+              (u32)fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK);
+       WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + amdgpu_crtc->crtc_offset,
+              (u32) fb_location & EVERGREEN_GRPH_SURFACE_ADDRESS_MASK);
+       WREG32(EVERGREEN_GRPH_CONTROL + amdgpu_crtc->crtc_offset, fb_format);
+       WREG32(EVERGREEN_GRPH_SWAP_CONTROL + amdgpu_crtc->crtc_offset, fb_swap);
+
+       /*
+        * The LUT only has 256 slots for indexing by a 8 bpc fb. Bypass the LUT
+        * for > 8 bpc scanout to avoid truncation of fb indices to 8 msb's, to
+        * retain the full precision throughout the pipeline.
+        */
+       WREG32_P(EVERGREEN_GRPH_LUT_10BIT_BYPASS_CONTROL + amdgpu_crtc->crtc_offset,
+                (bypass_lut ? EVERGREEN_LUT_10BIT_BYPASS_EN : 0),
+                ~EVERGREEN_LUT_10BIT_BYPASS_EN);
+
+       if (bypass_lut)
+               DRM_DEBUG_KMS("Bypassing hardware LUT due to 10 bit fb scanout.\n");
+
+       WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_X + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_SURFACE_OFFSET_Y + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_X_START + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_Y_START + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_GRPH_X_END + amdgpu_crtc->crtc_offset, target_fb->width);
+       WREG32(EVERGREEN_GRPH_Y_END + amdgpu_crtc->crtc_offset, target_fb->height);
+
+       fb_pitch_pixels = target_fb->pitches[0] / (target_fb->bits_per_pixel / 8);
+       WREG32(EVERGREEN_GRPH_PITCH + amdgpu_crtc->crtc_offset, fb_pitch_pixels);
+
+       dce_v6_0_grph_enable(crtc, true);
+
+       WREG32(EVERGREEN_DESKTOP_HEIGHT + amdgpu_crtc->crtc_offset,
+                      target_fb->height);
+       x &= ~3;
+       y &= ~1;
+       WREG32(EVERGREEN_VIEWPORT_START + amdgpu_crtc->crtc_offset,
+              (x << 16) | y);
+       viewport_w = crtc->mode.hdisplay;
+       viewport_h = (crtc->mode.vdisplay + 1) & ~1;
+
+       WREG32(EVERGREEN_VIEWPORT_SIZE + amdgpu_crtc->crtc_offset,
+              (viewport_w << 16) | viewport_h);
+
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(EVERGREEN_MASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0);
+
+       if (!atomic && fb && fb != crtc->primary->fb) {
+               amdgpu_fb = to_amdgpu_framebuffer(fb);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
+               if (unlikely(r != 0))
+                       return r;
+               amdgpu_bo_unpin(abo);
+               amdgpu_bo_unreserve(abo);
+       }
+
+       /* Bytes per pixel may have changed */
+       dce_v6_0_bandwidth_update(adev);
+
+       return 0;
+
+}
+
+static void dce_v6_0_set_interleave(struct drm_crtc *crtc,
+                                   struct drm_display_mode *mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               WREG32(EVERGREEN_DATA_FORMAT + amdgpu_crtc->crtc_offset,
+                      EVERGREEN_INTERLEAVE_EN);
+       else
+               WREG32(EVERGREEN_DATA_FORMAT + amdgpu_crtc->crtc_offset, 0);
+}
+
+static void dce_v6_0_crtc_load_lut(struct drm_crtc *crtc)
+{
+
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       int i;
+
+       DRM_DEBUG_KMS("%d\n", amdgpu_crtc->crtc_id);
+
+       WREG32(NI_INPUT_CSC_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_INPUT_CSC_GRPH_MODE(NI_INPUT_CSC_BYPASS) |
+               NI_INPUT_CSC_OVL_MODE(NI_INPUT_CSC_BYPASS)));
+       WREG32(NI_PRESCALE_GRPH_CONTROL + amdgpu_crtc->crtc_offset,
+              NI_GRPH_PRESCALE_BYPASS);
+       WREG32(NI_PRESCALE_OVL_CONTROL + amdgpu_crtc->crtc_offset,
+              NI_OVL_PRESCALE_BYPASS);
+       WREG32(NI_INPUT_GAMMA_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_GRPH_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT) |
+               NI_OVL_INPUT_GAMMA_MODE(NI_INPUT_GAMMA_USE_LUT)));
+
+
+
+       WREG32(EVERGREEN_DC_LUT_CONTROL + amdgpu_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_BLACK_OFFSET_RED + amdgpu_crtc->crtc_offset, 0);
+
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE + amdgpu_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN + amdgpu_crtc->crtc_offset, 0xffff);
+       WREG32(EVERGREEN_DC_LUT_WHITE_OFFSET_RED + amdgpu_crtc->crtc_offset, 0xffff);
+
+       WREG32(EVERGREEN_DC_LUT_RW_MODE + amdgpu_crtc->crtc_offset, 0);
+       WREG32(EVERGREEN_DC_LUT_WRITE_EN_MASK + amdgpu_crtc->crtc_offset, 0x00000007);
+
+       WREG32(EVERGREEN_DC_LUT_RW_INDEX + amdgpu_crtc->crtc_offset, 0);
+       for (i = 0; i < 256; i++) {
+               WREG32(EVERGREEN_DC_LUT_30_COLOR + amdgpu_crtc->crtc_offset,
+                      (amdgpu_crtc->lut_r[i] << 20) |
+                      (amdgpu_crtc->lut_g[i] << 10) |
+                      (amdgpu_crtc->lut_b[i] << 0));
+       }
+
+       WREG32(NI_DEGAMMA_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_GRPH_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
+               NI_OVL_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
+               NI_ICON_DEGAMMA_MODE(NI_DEGAMMA_BYPASS) |
+               NI_CURSOR_DEGAMMA_MODE(NI_DEGAMMA_BYPASS)));
+       WREG32(NI_GAMUT_REMAP_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_GRPH_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS) |
+               NI_OVL_GAMUT_REMAP_MODE(NI_GAMUT_REMAP_BYPASS)));
+       WREG32(NI_REGAMMA_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_GRPH_REGAMMA_MODE(NI_REGAMMA_BYPASS) |
+               NI_OVL_REGAMMA_MODE(NI_REGAMMA_BYPASS)));
+       WREG32(NI_OUTPUT_CSC_CONTROL + amdgpu_crtc->crtc_offset,
+              (NI_OUTPUT_CSC_GRPH_MODE(0) |
+               NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS)));
+       /* XXX match this to the depth of the crtc fmt block, move to modeset? */
+       WREG32(0x1a50 + amdgpu_crtc->crtc_offset, 0);
+
+
+}
+
+static int dce_v6_0_pick_dig_encoder(struct drm_encoder *encoder)
+{
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
+
+       switch (amdgpu_encoder->encoder_id) {
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+               return dig->linkb ? 1 : 0;
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+               return dig->linkb ? 3 : 2;
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+               return dig->linkb ? 5 : 4;
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
+               return 6;
+       default:
+               DRM_ERROR("invalid encoder_id: 0x%x\n", amdgpu_encoder->encoder_id);
+               return 0;
+       }
+}
+
+/**
+ * dce_v6_0_pick_pll - Allocate a PPLL for use by the crtc.
+ *
+ * @crtc: drm crtc
+ *
+ * Returns the PPLL (Pixel PLL) to be used by the crtc.  For DP monitors
+ * a single PPLL can be used for all DP crtcs/encoders.  For non-DP
+ * monitors a dedicated PPLL must be used.  If a particular board has
+ * an external DP PLL, return ATOM_PPLL_INVALID to skip PLL programming
+ * as there is no need to program the PLL itself.  If we are not able to
+ * allocate a PLL, return ATOM_PPLL_INVALID to skip PLL programming to
+ * avoid messing up an existing monitor.
+ *
+ *
+ */
+static u32 dce_v6_0_pick_pll(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       u32 pll_in_use;
+       int pll;
+
+       if (ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(amdgpu_crtc->encoder))) {
+               if (adev->clock.dp_extclk)
+                       /* skip PPLL programming if using ext clock */
+                       return ATOM_PPLL_INVALID;
+               else
+                       return ATOM_PPLL0;
+       } else {
+               /* use the same PPLL for all monitors with the same clock */
+               pll = amdgpu_pll_get_shared_nondp_ppll(crtc);
+               if (pll != ATOM_PPLL_INVALID)
+                       return pll;
+       }
+
+       /*  PPLL1, and PPLL2 */
+       pll_in_use = amdgpu_pll_get_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;
+}
+
+static void dce_v6_0_lock_cursor(struct drm_crtc *crtc, bool lock)
+{
+       struct amdgpu_device *adev = crtc->dev->dev_private;
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       uint32_t cur_lock;
+
+       cur_lock = RREG32(EVERGREEN_CUR_UPDATE + amdgpu_crtc->crtc_offset);
+       if (lock)
+               cur_lock |= EVERGREEN_CURSOR_UPDATE_LOCK;
+       else
+               cur_lock &= ~EVERGREEN_CURSOR_UPDATE_LOCK;
+       WREG32(EVERGREEN_CUR_UPDATE + amdgpu_crtc->crtc_offset, cur_lock);
+}
+
+static void dce_v6_0_hide_cursor(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct amdgpu_device *adev = crtc->dev->dev_private;
+
+       WREG32_IDX(EVERGREEN_CUR_CONTROL + amdgpu_crtc->crtc_offset,
+                  EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+                  EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
+
+
+}
+
+static void dce_v6_0_show_cursor(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct amdgpu_device *adev = crtc->dev->dev_private;
+
+       WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + amdgpu_crtc->crtc_offset,
+              upper_32_bits(amdgpu_crtc->cursor_addr));
+       WREG32(EVERGREEN_CUR_SURFACE_ADDRESS + amdgpu_crtc->crtc_offset,
+              lower_32_bits(amdgpu_crtc->cursor_addr));
+
+       WREG32_IDX(EVERGREEN_CUR_CONTROL + amdgpu_crtc->crtc_offset,
+                  EVERGREEN_CURSOR_EN |
+                  EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+                  EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
+
+}
+
+static int dce_v6_0_cursor_move_locked(struct drm_crtc *crtc,
+                                      int x, int y)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct amdgpu_device *adev = crtc->dev->dev_private;
+       int xorigin = 0, yorigin = 0;
+
+       int w = amdgpu_crtc->cursor_width;
+
+       /* avivo cursor are offset into the total surface */
+       x += crtc->x;
+       y += crtc->y;
+       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, amdgpu_crtc->max_cursor_width - 1);
+               x = 0;
+       }
+       if (y < 0) {
+               yorigin = min(-y, amdgpu_crtc->max_cursor_height - 1);
+               y = 0;
+       }
+
+       WREG32(EVERGREEN_CUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y);
+       WREG32(EVERGREEN_CUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin);
+       WREG32(EVERGREEN_CUR_SIZE + amdgpu_crtc->crtc_offset,
+              ((w - 1) << 16) | (amdgpu_crtc->cursor_height - 1));
+
+       amdgpu_crtc->cursor_x = x;
+       amdgpu_crtc->cursor_y = y;
+       return 0;
+}
+
+static int dce_v6_0_crtc_cursor_move(struct drm_crtc *crtc,
+                                    int x, int y)
+{
+       int ret;
+
+       dce_v6_0_lock_cursor(crtc, true);
+       ret = dce_v6_0_cursor_move_locked(crtc, x, y);
+       dce_v6_0_lock_cursor(crtc, false);
+
+       return ret;
+}
+
+static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc,
+                                    struct drm_file *file_priv,
+                                    uint32_t handle,
+                                    uint32_t width,
+                                    uint32_t height,
+                                    int32_t hot_x,
+                                    int32_t hot_y)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_gem_object *obj;
+       struct amdgpu_bo *aobj;
+       int ret;
+
+       if (!handle) {
+               /* turn off cursor */
+               dce_v6_0_hide_cursor(crtc);
+               obj = NULL;
+               goto unpin;
+       }
+
+       if ((width > amdgpu_crtc->max_cursor_width) ||
+           (height > amdgpu_crtc->max_cursor_height)) {
+               DRM_ERROR("bad cursor width or height %d x %d\n", width, height);
+               return -EINVAL;
+       }
+
+       obj = drm_gem_object_lookup(file_priv, handle);
+       if (!obj) {
+               DRM_ERROR("Cannot find cursor object %x for crtc %d\n", handle, amdgpu_crtc->crtc_id);
+               return -ENOENT;
+       }
+
+       aobj = gem_to_amdgpu_bo(obj);
+       ret = amdgpu_bo_reserve(aobj, false);
+       if (ret != 0) {
+               drm_gem_object_unreference_unlocked(obj);
+               return ret;
+       }
+
+       ret = amdgpu_bo_pin(aobj, AMDGPU_GEM_DOMAIN_VRAM, &amdgpu_crtc->cursor_addr);
+       amdgpu_bo_unreserve(aobj);
+       if (ret) {
+               DRM_ERROR("Failed to pin new cursor BO (%d)\n", ret);
+               drm_gem_object_unreference_unlocked(obj);
+               return ret;
+       }
+
+       amdgpu_crtc->cursor_width = width;
+       amdgpu_crtc->cursor_height = height;
+
+       dce_v6_0_lock_cursor(crtc, true);
+
+       if (hot_x != amdgpu_crtc->cursor_hot_x ||
+           hot_y != amdgpu_crtc->cursor_hot_y) {
+               int x, y;
+
+               x = amdgpu_crtc->cursor_x + amdgpu_crtc->cursor_hot_x - hot_x;
+               y = amdgpu_crtc->cursor_y + amdgpu_crtc->cursor_hot_y - hot_y;
+
+               dce_v6_0_cursor_move_locked(crtc, x, y);
+
+               amdgpu_crtc->cursor_hot_x = hot_x;
+               amdgpu_crtc->cursor_hot_y = hot_y;
+       }
+
+       dce_v6_0_show_cursor(crtc);
+       dce_v6_0_lock_cursor(crtc, false);
+
+unpin:
+       if (amdgpu_crtc->cursor_bo) {
+               struct amdgpu_bo *aobj = gem_to_amdgpu_bo(amdgpu_crtc->cursor_bo);
+               ret = amdgpu_bo_reserve(aobj, false);
+               if (likely(ret == 0)) {
+                       amdgpu_bo_unpin(aobj);
+                       amdgpu_bo_unreserve(aobj);
+               }
+               drm_gem_object_unreference_unlocked(amdgpu_crtc->cursor_bo);
+       }
+
+       amdgpu_crtc->cursor_bo = obj;
+       return 0;
+}
+
+static void dce_v6_0_cursor_reset(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       if (amdgpu_crtc->cursor_bo) {
+               dce_v6_0_lock_cursor(crtc, true);
+
+               dce_v6_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x,
+                                           amdgpu_crtc->cursor_y);
+
+               dce_v6_0_show_cursor(crtc);
+               dce_v6_0_lock_cursor(crtc, false);
+       }
+}
+
+static int dce_v6_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                  u16 *blue, uint32_t size)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       int i;
+
+       /* userspace palettes are always correct as is */
+       for (i = 0; i < size; i++) {
+               amdgpu_crtc->lut_r[i] = red[i] >> 6;
+               amdgpu_crtc->lut_g[i] = green[i] >> 6;
+               amdgpu_crtc->lut_b[i] = blue[i] >> 6;
+       }
+       dce_v6_0_crtc_load_lut(crtc);
+
+       return 0;
+}
+
+static void dce_v6_0_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       drm_crtc_cleanup(crtc);
+       kfree(amdgpu_crtc);
+}
+
+static const struct drm_crtc_funcs dce_v6_0_crtc_funcs = {
+       .cursor_set2 = dce_v6_0_crtc_cursor_set2,
+       .cursor_move = dce_v6_0_crtc_cursor_move,
+       .gamma_set = dce_v6_0_crtc_gamma_set,
+       .set_config = amdgpu_crtc_set_config,
+       .destroy = dce_v6_0_crtc_destroy,
+       .page_flip_target = amdgpu_crtc_page_flip_target,
+};
+
+static void dce_v6_0_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       unsigned type;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               amdgpu_crtc->enabled = true;
+               amdgpu_atombios_crtc_enable(crtc, ATOM_ENABLE);
+               amdgpu_atombios_crtc_blank(crtc, ATOM_DISABLE);
+               /* Make sure VBLANK and PFLIP interrupts are still enabled */
+               type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id);
+               amdgpu_irq_update(adev, &adev->crtc_irq, type);
+               amdgpu_irq_update(adev, &adev->pageflip_irq, type);
+               drm_vblank_post_modeset(dev, amdgpu_crtc->crtc_id);
+               dce_v6_0_crtc_load_lut(crtc);
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+       case DRM_MODE_DPMS_OFF:
+               drm_vblank_pre_modeset(dev, amdgpu_crtc->crtc_id);
+               if (amdgpu_crtc->enabled)
+                       amdgpu_atombios_crtc_blank(crtc, ATOM_ENABLE);
+               amdgpu_atombios_crtc_enable(crtc, ATOM_DISABLE);
+               amdgpu_crtc->enabled = false;
+               break;
+       }
+       /* adjust pm to dpms */
+       amdgpu_pm_compute_clocks(adev);
+}
+
+static void dce_v6_0_crtc_prepare(struct drm_crtc *crtc)
+{
+       /* disable crtc pair power gating before programming */
+       amdgpu_atombios_crtc_powergate(crtc, ATOM_DISABLE);
+       amdgpu_atombios_crtc_lock(crtc, ATOM_ENABLE);
+       dce_v6_0_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void dce_v6_0_crtc_commit(struct drm_crtc *crtc)
+{
+       dce_v6_0_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+       amdgpu_atombios_crtc_lock(crtc, ATOM_DISABLE);
+}
+
+static void dce_v6_0_crtc_disable(struct drm_crtc *crtc)
+{
+
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_atom_ss ss;
+       int i;
+
+       dce_v6_0_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       if (crtc->primary->fb) {
+               int r;
+               struct amdgpu_framebuffer *amdgpu_fb;
+               struct amdgpu_bo *abo;
+
+               amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
+               if (unlikely(r))
+                       DRM_ERROR("failed to reserve abo before unpin\n");
+               else {
+                       amdgpu_bo_unpin(abo);
+                       amdgpu_bo_unreserve(abo);
+               }
+       }
+       /* disable the GRPH */
+       dce_v6_0_grph_enable(crtc, false);
+
+       amdgpu_atombios_crtc_powergate(crtc, ATOM_ENABLE);
+
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               if (adev->mode_info.crtcs[i] &&
+                   adev->mode_info.crtcs[i]->enabled &&
+                   i != amdgpu_crtc->crtc_id &&
+                   amdgpu_crtc->pll_id == adev->mode_info.crtcs[i]->pll_id) {
+                       /* one other crtc is using this pll don't turn
+                        * off the pll
+                        */
+                       goto done;
+               }
+       }
+
+       switch (amdgpu_crtc->pll_id) {
+       case ATOM_PPLL1:
+       case ATOM_PPLL2:
+               /* disable the ppll */
+               amdgpu_atombios_crtc_program_pll(crtc, amdgpu_crtc->crtc_id, amdgpu_crtc->pll_id,
+                                                0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
+               break;
+       default:
+               break;
+       }
+done:
+       amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
+       amdgpu_crtc->adjusted_clock = 0;
+       amdgpu_crtc->encoder = NULL;
+       amdgpu_crtc->connector = NULL;
+}
+
+static int dce_v6_0_crtc_mode_set(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode,
+                                 int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       if (!amdgpu_crtc->adjusted_clock)
+               return -EINVAL;
+
+       amdgpu_atombios_crtc_set_pll(crtc, adjusted_mode);
+       amdgpu_atombios_crtc_set_dtd_timing(crtc, adjusted_mode);
+       dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y, 0);
+       amdgpu_atombios_crtc_overscan_setup(crtc, mode, adjusted_mode);
+       amdgpu_atombios_crtc_scaler_setup(crtc);
+       dce_v6_0_cursor_reset(crtc);
+       /* update the hw version fpr dpm */
+       amdgpu_crtc->hw_mode = *adjusted_mode;
+
+       return 0;
+}
+
+static bool dce_v6_0_crtc_mode_fixup(struct drm_crtc *crtc,
+                                    const struct drm_display_mode *mode,
+                                    struct drm_display_mode *adjusted_mode)
+{
+
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct drm_encoder *encoder;
+
+       /* assign the encoder to the amdgpu crtc to avoid repeated lookups later */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc == crtc) {
+                       amdgpu_crtc->encoder = encoder;
+                       amdgpu_crtc->connector = amdgpu_get_connector_for_encoder(encoder);
+                       break;
+               }
+       }
+       if ((amdgpu_crtc->encoder == NULL) || (amdgpu_crtc->connector == NULL)) {
+               amdgpu_crtc->encoder = NULL;
+               amdgpu_crtc->connector = NULL;
+               return false;
+       }
+       if (!amdgpu_crtc_scaling_mode_fixup(crtc, mode, adjusted_mode))
+               return false;
+       if (amdgpu_atombios_crtc_prepare_pll(crtc, adjusted_mode))
+               return false;
+       /* pick pll */
+       amdgpu_crtc->pll_id = dce_v6_0_pick_pll(crtc);
+       /* if we can't get a PPLL for a non-DP encoder, fail */
+       if ((amdgpu_crtc->pll_id == ATOM_PPLL_INVALID) &&
+           !ENCODER_MODE_IS_DP(amdgpu_atombios_encoder_get_encoder_mode(amdgpu_crtc->encoder)))
+               return false;
+
+       return true;
+}
+
+static int dce_v6_0_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+                                 struct drm_framebuffer *old_fb)
+{
+       return dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y, 0);
+}
+
+static int dce_v6_0_crtc_set_base_atomic(struct drm_crtc *crtc,
+                                        struct drm_framebuffer *fb,
+                                        int x, int y, enum mode_set_atomic state)
+{
+       return dce_v6_0_crtc_do_set_base(crtc, fb, x, y, 1);
+}
+
+static const struct drm_crtc_helper_funcs dce_v6_0_crtc_helper_funcs = {
+       .dpms = dce_v6_0_crtc_dpms,
+       .mode_fixup = dce_v6_0_crtc_mode_fixup,
+       .mode_set = dce_v6_0_crtc_mode_set,
+       .mode_set_base = dce_v6_0_crtc_set_base,
+       .mode_set_base_atomic = dce_v6_0_crtc_set_base_atomic,
+       .prepare = dce_v6_0_crtc_prepare,
+       .commit = dce_v6_0_crtc_commit,
+       .load_lut = dce_v6_0_crtc_load_lut,
+       .disable = dce_v6_0_crtc_disable,
+};
+
+static int dce_v6_0_crtc_init(struct amdgpu_device *adev, int index)
+{
+       struct amdgpu_crtc *amdgpu_crtc;
+       int i;
+
+       amdgpu_crtc = kzalloc(sizeof(struct amdgpu_crtc) +
+                             (AMDGPUFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+       if (amdgpu_crtc == NULL)
+               return -ENOMEM;
+
+       drm_crtc_init(adev->ddev, &amdgpu_crtc->base, &dce_v6_0_crtc_funcs);
+
+       drm_mode_crtc_set_gamma_size(&amdgpu_crtc->base, 256);
+       amdgpu_crtc->crtc_id = index;
+       adev->mode_info.crtcs[index] = amdgpu_crtc;
+
+       amdgpu_crtc->max_cursor_width = CURSOR_WIDTH;
+       amdgpu_crtc->max_cursor_height = CURSOR_HEIGHT;
+       adev->ddev->mode_config.cursor_width = amdgpu_crtc->max_cursor_width;
+       adev->ddev->mode_config.cursor_height = amdgpu_crtc->max_cursor_height;
+
+       for (i = 0; i < 256; i++) {
+               amdgpu_crtc->lut_r[i] = i << 2;
+               amdgpu_crtc->lut_g[i] = i << 2;
+               amdgpu_crtc->lut_b[i] = i << 2;
+       }
+
+       amdgpu_crtc->crtc_offset = crtc_offsets[amdgpu_crtc->crtc_id];
+
+       amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
+       amdgpu_crtc->adjusted_clock = 0;
+       amdgpu_crtc->encoder = NULL;
+       amdgpu_crtc->connector = NULL;
+       drm_crtc_helper_add(&amdgpu_crtc->base, &dce_v6_0_crtc_helper_funcs);
+
+       return 0;
+}
+
+static int dce_v6_0_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       adev->audio_endpt_rreg = &dce_v6_0_audio_endpt_rreg;
+       adev->audio_endpt_wreg = &dce_v6_0_audio_endpt_wreg;
+
+       dce_v6_0_set_display_funcs(adev);
+       dce_v6_0_set_irq_funcs(adev);
+
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+       case CHIP_VERDE:
+               adev->mode_info.num_crtc = 6;
+               adev->mode_info.num_hpd = 6;
+               adev->mode_info.num_dig = 6;
+               break;
+       case CHIP_OLAND:
+               adev->mode_info.num_crtc = 2;
+               adev->mode_info.num_hpd = 2;
+               adev->mode_info.num_dig = 2;
+               break;
+       default:
+               /* FIXME: not supported yet */
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int dce_v6_0_sw_init(void *handle)
+{
+       int r, i;
+       bool ret;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               r = amdgpu_irq_add_id(adev, i + 1, &adev->crtc_irq);
+               if (r)
+                       return r;
+       }
+
+       for (i = 8; i < 20; i += 2) {
+               r = amdgpu_irq_add_id(adev, i, &adev->pageflip_irq);
+               if (r)
+                       return r;
+       }
+
+       /* HPD hotplug */
+       r = amdgpu_irq_add_id(adev, 42, &adev->hpd_irq);
+       if (r)
+               return r;
+
+       adev->mode_info.mode_config_initialized = true;
+
+       adev->ddev->mode_config.funcs = &amdgpu_mode_funcs;
+       adev->ddev->mode_config.async_page_flip = true;
+       adev->ddev->mode_config.max_width = 16384;
+       adev->ddev->mode_config.max_height = 16384;
+       adev->ddev->mode_config.preferred_depth = 24;
+       adev->ddev->mode_config.prefer_shadow = 1;
+       adev->ddev->mode_config.fb_base = adev->mc.aper_base;
+
+       r = amdgpu_modeset_create_props(adev);
+       if (r)
+               return r;
+
+       adev->ddev->mode_config.max_width = 16384;
+       adev->ddev->mode_config.max_height = 16384;
+
+       /* allocate crtcs */
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               r = dce_v6_0_crtc_init(adev, i);
+               if (r)
+                       return r;
+       }
+
+       ret = amdgpu_atombios_get_connector_info_from_object_table(adev);
+       if (ret)
+               amdgpu_print_display_setup(adev->ddev);
+       else
+               return -EINVAL;
+
+       /* setup afmt */
+       r = dce_v6_0_afmt_init(adev);
+       if (r)
+               return r;
+
+       r = dce_v6_0_audio_init(adev);
+       if (r)
+               return r;
+
+       drm_kms_helper_poll_init(adev->ddev);
+
+       return r;
+}
+
+static int dce_v6_0_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       kfree(adev->mode_info.bios_hardcoded_edid);
+
+       drm_kms_helper_poll_fini(adev->ddev);
+
+       dce_v6_0_audio_fini(adev);
+       dce_v6_0_afmt_fini(adev);
+
+       drm_mode_config_cleanup(adev->ddev);
+       adev->mode_info.mode_config_initialized = false;
+
+       return 0;
+}
+
+static int dce_v6_0_hw_init(void *handle)
+{
+       int i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       /* init dig PHYs, disp eng pll */
+       amdgpu_atombios_encoder_init_dig(adev);
+       amdgpu_atombios_crtc_set_disp_eng_pll(adev, adev->clock.default_dispclk);
+
+       /* initialize hpd */
+       dce_v6_0_hpd_init(adev);
+
+       for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
+               dce_v6_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false);
+       }
+
+       dce_v6_0_pageflip_interrupt_init(adev);
+
+       return 0;
+}
+
+static int dce_v6_0_hw_fini(void *handle)
+{
+       int i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       dce_v6_0_hpd_fini(adev);
+
+       for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
+               dce_v6_0_audio_enable(adev, &adev->mode_info.audio.pin[i], false);
+       }
+
+       dce_v6_0_pageflip_interrupt_fini(adev);
+
+       return 0;
+}
+
+static int dce_v6_0_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       amdgpu_atombios_scratch_regs_save(adev);
+
+       return dce_v6_0_hw_fini(handle);
+}
+
+static int dce_v6_0_resume(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       int ret;
+
+       ret = dce_v6_0_hw_init(handle);
+
+       amdgpu_atombios_scratch_regs_restore(adev);
+
+       /* turn on the BL */
+       if (adev->mode_info.bl_encoder) {
+               u8 bl_level = amdgpu_display_backlight_get_level(adev,
+                                                                 adev->mode_info.bl_encoder);
+               amdgpu_display_backlight_set_level(adev, adev->mode_info.bl_encoder,
+                                                   bl_level);
+       }
+
+       return ret;
+}
+
+static bool dce_v6_0_is_idle(void *handle)
+{
+       return true;
+}
+
+static int dce_v6_0_wait_for_idle(void *handle)
+{
+       return 0;
+}
+
+static int dce_v6_0_soft_reset(void *handle)
+{
+       DRM_INFO("xxxx: dce_v6_0_soft_reset --- no impl!!\n");
+       return 0;
+}
+
+static void dce_v6_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
+                                                    int crtc,
+                                                    enum amdgpu_interrupt_state state)
+{
+       u32 reg_block, interrupt_mask;
+
+       if (crtc >= adev->mode_info.num_crtc) {
+               DRM_DEBUG("invalid crtc %d\n", crtc);
+               return;
+       }
+
+       switch (crtc) {
+       case 0:
+               reg_block = SI_CRTC0_REGISTER_OFFSET;
+               break;
+       case 1:
+               reg_block = SI_CRTC1_REGISTER_OFFSET;
+               break;
+       case 2:
+               reg_block = SI_CRTC2_REGISTER_OFFSET;
+               break;
+       case 3:
+               reg_block = SI_CRTC3_REGISTER_OFFSET;
+               break;
+       case 4:
+               reg_block = SI_CRTC4_REGISTER_OFFSET;
+               break;
+       case 5:
+               reg_block = SI_CRTC5_REGISTER_OFFSET;
+               break;
+       default:
+               DRM_DEBUG("invalid crtc %d\n", crtc);
+               return;
+       }
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               interrupt_mask = RREG32(INT_MASK + reg_block);
+               interrupt_mask &= ~VBLANK_INT_MASK;
+               WREG32(INT_MASK + reg_block, interrupt_mask);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               interrupt_mask = RREG32(INT_MASK + reg_block);
+               interrupt_mask |= VBLANK_INT_MASK;
+               WREG32(INT_MASK + reg_block, interrupt_mask);
+               break;
+       default:
+               break;
+       }
+}
+
+static void dce_v6_0_set_crtc_vline_interrupt_state(struct amdgpu_device *adev,
+                                                   int crtc,
+                                                   enum amdgpu_interrupt_state state)
+{
+
+}
+
+static int dce_v6_0_set_hpd_interrupt_state(struct amdgpu_device *adev,
+                                           struct amdgpu_irq_src *src,
+                                           unsigned type,
+                                           enum amdgpu_interrupt_state state)
+{
+       u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl;
+
+       switch (type) {
+       case AMDGPU_HPD_1:
+               dc_hpd_int_cntl_reg = DC_HPD1_INT_CONTROL;
+               break;
+       case AMDGPU_HPD_2:
+               dc_hpd_int_cntl_reg = DC_HPD2_INT_CONTROL;
+               break;
+       case AMDGPU_HPD_3:
+               dc_hpd_int_cntl_reg = DC_HPD3_INT_CONTROL;
+               break;
+       case AMDGPU_HPD_4:
+               dc_hpd_int_cntl_reg = DC_HPD4_INT_CONTROL;
+               break;
+       case AMDGPU_HPD_5:
+               dc_hpd_int_cntl_reg = DC_HPD5_INT_CONTROL;
+               break;
+       case AMDGPU_HPD_6:
+               dc_hpd_int_cntl_reg = DC_HPD6_INT_CONTROL;
+               break;
+       default:
+               DRM_DEBUG("invalid hdp %d\n", type);
+               return 0;
+       }
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+               dc_hpd_int_cntl &= ~(DC_HPDx_INT_EN | DC_HPDx_RX_INT_EN);
+               WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+               dc_hpd_int_cntl |= (DC_HPDx_INT_EN | DC_HPDx_RX_INT_EN);
+               WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int dce_v6_0_set_crtc_interrupt_state(struct amdgpu_device *adev,
+                                            struct amdgpu_irq_src *src,
+                                            unsigned type,
+                                            enum amdgpu_interrupt_state state)
+{
+       switch (type) {
+       case AMDGPU_CRTC_IRQ_VBLANK1:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 0, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VBLANK2:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 1, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VBLANK3:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 2, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VBLANK4:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 3, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VBLANK5:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 4, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VBLANK6:
+               dce_v6_0_set_crtc_vblank_interrupt_state(adev, 5, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE1:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 0, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE2:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 1, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE3:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 2, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE4:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 3, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE5:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 4, state);
+               break;
+       case AMDGPU_CRTC_IRQ_VLINE6:
+               dce_v6_0_set_crtc_vline_interrupt_state(adev, 5, state);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int dce_v6_0_crtc_irq(struct amdgpu_device *adev,
+                            struct amdgpu_irq_src *source,
+                            struct amdgpu_iv_entry *entry)
+{
+       unsigned crtc = entry->src_id - 1;
+       uint32_t disp_int = RREG32(interrupt_status_offsets[crtc].reg);
+       unsigned irq_type = amdgpu_crtc_idx_to_irq_type(adev, crtc);
+
+       switch (entry->src_data) {
+       case 0: /* vblank */
+               if (disp_int & interrupt_status_offsets[crtc].vblank)
+                       WREG32(VBLANK_STATUS + crtc_offsets[crtc], VBLANK_ACK);
+               else
+                       DRM_DEBUG("IH: IH event w/o asserted irq bit?\n");
+
+               if (amdgpu_irq_enabled(adev, source, irq_type)) {
+                       drm_handle_vblank(adev->ddev, crtc);
+               }
+               DRM_DEBUG("IH: D%d vblank\n", crtc + 1);
+               break;
+       case 1: /* vline */
+               if (disp_int & interrupt_status_offsets[crtc].vline)
+                       WREG32(VLINE_STATUS + crtc_offsets[crtc], VLINE_ACK);
+               else
+                       DRM_DEBUG("IH: IH event w/o asserted irq bit?\n");
+
+               DRM_DEBUG("IH: D%d vline\n", crtc + 1);
+               break;
+       default:
+               DRM_DEBUG("Unhandled interrupt: %d %d\n", entry->src_id, entry->src_data);
+               break;
+       }
+
+       return 0;
+}
+
+static int dce_v6_0_set_pageflip_interrupt_state(struct amdgpu_device *adev,
+                                                struct amdgpu_irq_src *src,
+                                                unsigned type,
+                                                enum amdgpu_interrupt_state state)
+{
+       u32 reg;
+
+       if (type >= adev->mode_info.num_crtc) {
+               DRM_ERROR("invalid pageflip crtc %d\n", type);
+               return -EINVAL;
+       }
+
+       reg = RREG32(GRPH_INT_CONTROL + crtc_offsets[type]);
+       if (state == AMDGPU_IRQ_STATE_DISABLE)
+               WREG32(GRPH_INT_CONTROL + crtc_offsets[type],
+                      reg & ~GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK);
+       else
+               WREG32(GRPH_INT_CONTROL + crtc_offsets[type],
+                      reg | GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK);
+
+       return 0;
+}
+
+static int dce_v6_0_pageflip_irq(struct amdgpu_device *adev,
+                                struct amdgpu_irq_src *source,
+                                struct amdgpu_iv_entry *entry)
+{
+               unsigned long flags;
+       unsigned crtc_id;
+       struct amdgpu_crtc *amdgpu_crtc;
+       struct amdgpu_flip_work *works;
+
+       crtc_id = (entry->src_id - 8) >> 1;
+       amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
+
+       if (crtc_id >= adev->mode_info.num_crtc) {
+               DRM_ERROR("invalid pageflip crtc %d\n", crtc_id);
+               return -EINVAL;
+       }
+
+       if (RREG32(GRPH_INT_STATUS + crtc_offsets[crtc_id]) &
+           GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_OCCURRED_MASK)
+               WREG32(GRPH_INT_STATUS + crtc_offsets[crtc_id],
+                      GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK);
+
+       /* IRQ could occur when in initial stage */
+       if (amdgpu_crtc == NULL)
+               return 0;
+
+       spin_lock_irqsave(&adev->ddev->event_lock, flags);
+       works = amdgpu_crtc->pflip_works;
+       if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED){
+               DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d != "
+                                               "AMDGPU_FLIP_SUBMITTED(%d)\n",
+                                               amdgpu_crtc->pflip_status,
+                                               AMDGPU_FLIP_SUBMITTED);
+               spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
+               return 0;
+       }
+
+       /* page flip completed. clean up */
+       amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
+       amdgpu_crtc->pflip_works = NULL;
+
+       /* wakeup usersapce */
+       if (works->event)
+               drm_crtc_send_vblank_event(&amdgpu_crtc->base, works->event);
+
+       spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
+
+       drm_crtc_vblank_put(&amdgpu_crtc->base);
+       schedule_work(&works->unpin_work);
+
+       return 0;
+}
+
+static int dce_v6_0_hpd_irq(struct amdgpu_device *adev,
+                           struct amdgpu_irq_src *source,
+                           struct amdgpu_iv_entry *entry)
+{
+       uint32_t disp_int, mask, int_control, tmp;
+       unsigned hpd;
+
+       if (entry->src_data >= adev->mode_info.num_hpd) {
+               DRM_DEBUG("Unhandled interrupt: %d %d\n", entry->src_id, entry->src_data);
+               return 0;
+       }
+
+       hpd = entry->src_data;
+       disp_int = RREG32(interrupt_status_offsets[hpd].reg);
+       mask = interrupt_status_offsets[hpd].hpd;
+       int_control = hpd_int_control_offsets[hpd];
+
+       if (disp_int & mask) {
+               tmp = RREG32(int_control);
+               tmp |= DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK;
+               WREG32(int_control, tmp);
+               schedule_work(&adev->hotplug_work);
+               DRM_INFO("IH: HPD%d\n", hpd + 1);
+       }
+
+       return 0;
+
+}
+
+static int dce_v6_0_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int dce_v6_0_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       return 0;
+}
+
+const struct amd_ip_funcs dce_v6_0_ip_funcs = {
+       .name = "dce_v6_0",
+       .early_init = dce_v6_0_early_init,
+       .late_init = NULL,
+       .sw_init = dce_v6_0_sw_init,
+       .sw_fini = dce_v6_0_sw_fini,
+       .hw_init = dce_v6_0_hw_init,
+       .hw_fini = dce_v6_0_hw_fini,
+       .suspend = dce_v6_0_suspend,
+       .resume = dce_v6_0_resume,
+       .is_idle = dce_v6_0_is_idle,
+       .wait_for_idle = dce_v6_0_wait_for_idle,
+       .soft_reset = dce_v6_0_soft_reset,
+       .set_clockgating_state = dce_v6_0_set_clockgating_state,
+       .set_powergating_state = dce_v6_0_set_powergating_state,
+};
+
+static void
+dce_v6_0_encoder_mode_set(struct drm_encoder *encoder,
+                         struct drm_display_mode *mode,
+                         struct drm_display_mode *adjusted_mode)
+{
+
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+
+       amdgpu_encoder->pixel_clock = adjusted_mode->clock;
+
+       /* need to call this here rather than in prepare() since we need some crtc info */
+       amdgpu_atombios_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+       /* set scaler clears this on some chips */
+       dce_v6_0_set_interleave(encoder->crtc, mode);
+
+       if (amdgpu_atombios_encoder_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI) {
+               dce_v6_0_afmt_enable(encoder, true);
+               dce_v6_0_afmt_setmode(encoder, adjusted_mode);
+       }
+}
+
+static void dce_v6_0_encoder_prepare(struct drm_encoder *encoder)
+{
+
+       struct amdgpu_device *adev = encoder->dev->dev_private;
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct drm_connector *connector = amdgpu_get_connector_for_encoder(encoder);
+
+       if ((amdgpu_encoder->active_device &
+            (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) ||
+           (amdgpu_encoder_get_dp_bridge_encoder_id(encoder) !=
+            ENCODER_OBJECT_ID_NONE)) {
+               struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
+               if (dig) {
+                       dig->dig_encoder = dce_v6_0_pick_dig_encoder(encoder);
+                       if (amdgpu_encoder->active_device & ATOM_DEVICE_DFP_SUPPORT)
+                               dig->afmt = adev->mode_info.afmt[dig->dig_encoder];
+               }
+       }
+
+       amdgpu_atombios_scratch_regs_lock(adev, true);
+
+       if (connector) {
+               struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
+
+               /* select the clock/data port if it uses a router */
+               if (amdgpu_connector->router.cd_valid)
+                       amdgpu_i2c_router_select_cd_port(amdgpu_connector);
+
+               /* turn eDP panel on for mode set */
+               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+                       amdgpu_atombios_encoder_set_edp_panel_power(connector,
+                                                            ATOM_TRANSMITTER_ACTION_POWER_ON);
+       }
+
+       /* this is needed for the pll/ss setup to work correctly in some cases */
+       amdgpu_atombios_encoder_set_crtc_source(encoder);
+       /* set up the FMT blocks */
+       dce_v6_0_program_fmt(encoder);
+}
+
+static void dce_v6_0_encoder_commit(struct drm_encoder *encoder)
+{
+
+       struct drm_device *dev = encoder->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+
+       /* need to call this here as we need the crtc set up */
+       amdgpu_atombios_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
+       amdgpu_atombios_scratch_regs_lock(adev, false);
+}
+
+static void dce_v6_0_encoder_disable(struct drm_encoder *encoder)
+{
+
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       struct amdgpu_encoder_atom_dig *dig;
+
+       amdgpu_atombios_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
+
+       if (amdgpu_atombios_encoder_is_digital(encoder)) {
+               if (amdgpu_atombios_encoder_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_HDMI)
+                       dce_v6_0_afmt_enable(encoder, false);
+               dig = amdgpu_encoder->enc_priv;
+               dig->dig_encoder = -1;
+       }
+       amdgpu_encoder->active_device = 0;
+}
+
+/* these are handled by the primary encoders */
+static void dce_v6_0_ext_prepare(struct drm_encoder *encoder)
+{
+
+}
+
+static void dce_v6_0_ext_commit(struct drm_encoder *encoder)
+{
+
+}
+
+static void
+dce_v6_0_ext_mode_set(struct drm_encoder *encoder,
+                     struct drm_display_mode *mode,
+                     struct drm_display_mode *adjusted_mode)
+{
+
+}
+
+static void dce_v6_0_ext_disable(struct drm_encoder *encoder)
+{
+
+}
+
+static void
+dce_v6_0_ext_dpms(struct drm_encoder *encoder, int mode)
+{
+
+}
+
+static bool dce_v6_0_ext_mode_fixup(struct drm_encoder *encoder,
+                                   const struct drm_display_mode *mode,
+                                   struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs dce_v6_0_ext_helper_funcs = {
+       .dpms = dce_v6_0_ext_dpms,
+       .mode_fixup = dce_v6_0_ext_mode_fixup,
+       .prepare = dce_v6_0_ext_prepare,
+       .mode_set = dce_v6_0_ext_mode_set,
+       .commit = dce_v6_0_ext_commit,
+       .disable = dce_v6_0_ext_disable,
+       /* no detect for TMDS/LVDS yet */
+};
+
+static const struct drm_encoder_helper_funcs dce_v6_0_dig_helper_funcs = {
+       .dpms = amdgpu_atombios_encoder_dpms,
+       .mode_fixup = amdgpu_atombios_encoder_mode_fixup,
+       .prepare = dce_v6_0_encoder_prepare,
+       .mode_set = dce_v6_0_encoder_mode_set,
+       .commit = dce_v6_0_encoder_commit,
+       .disable = dce_v6_0_encoder_disable,
+       .detect = amdgpu_atombios_encoder_dig_detect,
+};
+
+static const struct drm_encoder_helper_funcs dce_v6_0_dac_helper_funcs = {
+       .dpms = amdgpu_atombios_encoder_dpms,
+       .mode_fixup = amdgpu_atombios_encoder_mode_fixup,
+       .prepare = dce_v6_0_encoder_prepare,
+       .mode_set = dce_v6_0_encoder_mode_set,
+       .commit = dce_v6_0_encoder_commit,
+       .detect = amdgpu_atombios_encoder_dac_detect,
+};
+
+static void dce_v6_0_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+       if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+               amdgpu_atombios_encoder_fini_backlight(amdgpu_encoder);
+       kfree(amdgpu_encoder->enc_priv);
+       drm_encoder_cleanup(encoder);
+       kfree(amdgpu_encoder);
+}
+
+static const struct drm_encoder_funcs dce_v6_0_encoder_funcs = {
+       .destroy = dce_v6_0_encoder_destroy,
+};
+
+static void dce_v6_0_encoder_add(struct amdgpu_device *adev,
+                                uint32_t encoder_enum,
+                                uint32_t supported_device,
+                                u16 caps)
+{
+       struct drm_device *dev = adev->ddev;
+       struct drm_encoder *encoder;
+       struct amdgpu_encoder *amdgpu_encoder;
+
+       /* see if we already added it */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               amdgpu_encoder = to_amdgpu_encoder(encoder);
+               if (amdgpu_encoder->encoder_enum == encoder_enum) {
+                       amdgpu_encoder->devices |= supported_device;
+                       return;
+               }
+
+       }
+
+       /* add a new one */
+       amdgpu_encoder = kzalloc(sizeof(struct amdgpu_encoder), GFP_KERNEL);
+       if (!amdgpu_encoder)
+               return;
+
+       encoder = &amdgpu_encoder->base;
+       switch (adev->mode_info.num_crtc) {
+       case 1:
+               encoder->possible_crtcs = 0x1;
+               break;
+       case 2:
+       default:
+               encoder->possible_crtcs = 0x3;
+               break;
+       case 4:
+               encoder->possible_crtcs = 0xf;
+               break;
+       case 6:
+               encoder->possible_crtcs = 0x3f;
+               break;
+       }
+
+       amdgpu_encoder->enc_priv = NULL;
+       amdgpu_encoder->encoder_enum = encoder_enum;
+       amdgpu_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+       amdgpu_encoder->devices = supported_device;
+       amdgpu_encoder->rmx_type = RMX_OFF;
+       amdgpu_encoder->underscan_type = UNDERSCAN_OFF;
+       amdgpu_encoder->is_ext_encoder = false;
+       amdgpu_encoder->caps = caps;
+
+       switch (amdgpu_encoder->encoder_id) {
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2:
+               drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                DRM_MODE_ENCODER_DAC, NULL);
+               drm_encoder_helper_add(encoder, &dce_v6_0_dac_helper_funcs);
+               break;
+       case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+       case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3:
+               if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+                       amdgpu_encoder->rmx_type = RMX_FULL;
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_LVDS, NULL);
+                       amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_lcd_info(amdgpu_encoder);
+               } else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) {
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_DAC, NULL);
+                       amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
+               } else {
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_TMDS, NULL);
+                       amdgpu_encoder->enc_priv = amdgpu_atombios_encoder_get_dig_info(amdgpu_encoder);
+               }
+               drm_encoder_helper_add(encoder, &dce_v6_0_dig_helper_funcs);
+               break;
+       case ENCODER_OBJECT_ID_SI170B:
+       case ENCODER_OBJECT_ID_CH7303:
+       case ENCODER_OBJECT_ID_EXTERNAL_SDVOA:
+       case ENCODER_OBJECT_ID_EXTERNAL_SDVOB:
+       case ENCODER_OBJECT_ID_TITFP513:
+       case ENCODER_OBJECT_ID_VT1623:
+       case ENCODER_OBJECT_ID_HDMI_SI1930:
+       case ENCODER_OBJECT_ID_TRAVIS:
+       case ENCODER_OBJECT_ID_NUTMEG:
+               /* these are handled by the primary encoders */
+               amdgpu_encoder->is_ext_encoder = true;
+               if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT))
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_LVDS, NULL);
+               else if (amdgpu_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT))
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_DAC, NULL);
+               else
+                       drm_encoder_init(dev, encoder, &dce_v6_0_encoder_funcs,
+                                        DRM_MODE_ENCODER_TMDS, NULL);
+               drm_encoder_helper_add(encoder, &dce_v6_0_ext_helper_funcs);
+               break;
+       }
+}
+
+static const struct amdgpu_display_funcs dce_v6_0_display_funcs = {
+       .set_vga_render_state = &dce_v6_0_set_vga_render_state,
+       .bandwidth_update = &dce_v6_0_bandwidth_update,
+       .vblank_get_counter = &dce_v6_0_vblank_get_counter,
+       .vblank_wait = &dce_v6_0_vblank_wait,
+       .is_display_hung = &dce_v6_0_is_display_hung,
+       .backlight_set_level = &amdgpu_atombios_encoder_set_backlight_level,
+       .backlight_get_level = &amdgpu_atombios_encoder_get_backlight_level,
+       .hpd_sense = &dce_v6_0_hpd_sense,
+       .hpd_set_polarity = &dce_v6_0_hpd_set_polarity,
+       .hpd_get_gpio_reg = &dce_v6_0_hpd_get_gpio_reg,
+       .page_flip = &dce_v6_0_page_flip,
+       .page_flip_get_scanoutpos = &dce_v6_0_crtc_get_scanoutpos,
+       .add_encoder = &dce_v6_0_encoder_add,
+       .add_connector = &amdgpu_connector_add,
+       .stop_mc_access = &dce_v6_0_stop_mc_access,
+       .resume_mc_access = &dce_v6_0_resume_mc_access,
+};
+
+static void dce_v6_0_set_display_funcs(struct amdgpu_device *adev)
+{
+       if (adev->mode_info.funcs == NULL)
+               adev->mode_info.funcs = &dce_v6_0_display_funcs;
+}
+
+static const struct amdgpu_irq_src_funcs dce_v6_0_crtc_irq_funcs = {
+       .set = dce_v6_0_set_crtc_interrupt_state,
+       .process = dce_v6_0_crtc_irq,
+};
+
+static const struct amdgpu_irq_src_funcs dce_v6_0_pageflip_irq_funcs = {
+       .set = dce_v6_0_set_pageflip_interrupt_state,
+       .process = dce_v6_0_pageflip_irq,
+};
+
+static const struct amdgpu_irq_src_funcs dce_v6_0_hpd_irq_funcs = {
+       .set = dce_v6_0_set_hpd_interrupt_state,
+       .process = dce_v6_0_hpd_irq,
+};
+
+static void dce_v6_0_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->crtc_irq.num_types = AMDGPU_CRTC_IRQ_LAST;
+       adev->crtc_irq.funcs = &dce_v6_0_crtc_irq_funcs;
+
+       adev->pageflip_irq.num_types = AMDGPU_PAGEFLIP_IRQ_LAST;
+       adev->pageflip_irq.funcs = &dce_v6_0_pageflip_irq_funcs;
+
+       adev->hpd_irq.num_types = AMDGPU_HPD_LAST;
+       adev->hpd_irq.funcs = &dce_v6_0_hpd_irq_funcs;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.h b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.h
new file mode 100644 (file)
index 0000000..6a55281
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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 __DCE_V6_0_H__
+#define __DCE_V6_0_H__
+
+extern const struct amd_ip_funcs dce_v6_0_ip_funcs;
+
+#endif
index 4fdfab1..5966166 100644 (file)
@@ -170,7 +170,7 @@ static bool dce_v8_0_is_counter_moving(struct amdgpu_device *adev, int crtc)
  */
 static void dce_v8_0_vblank_wait(struct amdgpu_device *adev, int crtc)
 {
-       unsigned i = 0;
+       unsigned i = 100;
 
        if (crtc >= adev->mode_info.num_crtc)
                return;
@@ -182,14 +182,16 @@ static void dce_v8_0_vblank_wait(struct amdgpu_device *adev, int crtc)
         * wait for another frame.
         */
        while (dce_v8_0_is_in_vblank(adev, crtc)) {
-               if (i++ % 100 == 0) {
+               if (i++ == 100) {
+                       i = 0;
                        if (!dce_v8_0_is_counter_moving(adev, crtc))
                                break;
                }
        }
 
        while (!dce_v8_0_is_in_vblank(adev, crtc)) {
-               if (i++ % 100 == 0) {
+               if (i++ == 100) {
+                       i = 0;
                        if (!dce_v8_0_is_counter_moving(adev, crtc))
                                break;
                }
@@ -395,15 +397,6 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
 
-               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
-                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
-                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
-                        * aux dp channel on imac and help (but not completely fix)
-                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
-                        * also avoid interrupt storms during dpms.
-                        */
-                       continue;
-               }
                switch (amdgpu_connector->hpd.hpd) {
                case AMDGPU_HPD_1:
                        WREG32(mmDC_HPD1_CONTROL, tmp);
@@ -426,6 +419,45 @@ static void dce_v8_0_hpd_init(struct amdgpu_device *adev)
                default:
                        break;
                }
+
+               if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
+                   connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+                       /* don't try to enable hpd on eDP or LVDS avoid breaking the
+                        * aux dp channel on imac and help (but not completely fix)
+                        * https://bugzilla.redhat.com/show_bug.cgi?id=726143
+                        * also avoid interrupt storms during dpms.
+                        */
+                       u32 dc_hpd_int_cntl_reg, dc_hpd_int_cntl;
+
+                       switch (amdgpu_connector->hpd.hpd) {
+                       case AMDGPU_HPD_1:
+                               dc_hpd_int_cntl_reg = mmDC_HPD1_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_2:
+                               dc_hpd_int_cntl_reg = mmDC_HPD2_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_3:
+                               dc_hpd_int_cntl_reg = mmDC_HPD3_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_4:
+                               dc_hpd_int_cntl_reg = mmDC_HPD4_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_5:
+                               dc_hpd_int_cntl_reg = mmDC_HPD5_INT_CONTROL;
+                               break;
+                       case AMDGPU_HPD_6:
+                               dc_hpd_int_cntl_reg = mmDC_HPD6_INT_CONTROL;
+                               break;
+                       default:
+                               continue;
+                       }
+
+                       dc_hpd_int_cntl = RREG32(dc_hpd_int_cntl_reg);
+                       dc_hpd_int_cntl &= ~DC_HPD1_INT_CONTROL__DC_HPD1_INT_EN_MASK;
+                       WREG32(dc_hpd_int_cntl_reg, dc_hpd_int_cntl);
+                       continue;
+               }
+
                dce_v8_0_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd);
                amdgpu_irq_get(adev, &adev->hpd_irq, amdgpu_connector->hpd.hpd);
        }
@@ -604,6 +636,52 @@ static void dce_v8_0_set_vga_render_state(struct amdgpu_device *adev,
        WREG32(mmVGA_RENDER_CONTROL, tmp);
 }
 
+static int dce_v8_0_get_num_crtc(struct amdgpu_device *adev)
+{
+       int num_crtc = 0;
+
+       switch (adev->asic_type) {
+       case CHIP_BONAIRE:
+       case CHIP_HAWAII:
+               num_crtc = 6;
+               break;
+       case CHIP_KAVERI:
+               num_crtc = 4;
+               break;
+       case CHIP_KABINI:
+       case CHIP_MULLINS:
+               num_crtc = 2;
+               break;
+       default:
+               num_crtc = 0;
+       }
+       return num_crtc;
+}
+
+void dce_v8_0_disable_dce(struct amdgpu_device *adev)
+{
+       /*Disable VGA render and enabled crtc, if has DCE engine*/
+       if (amdgpu_atombios_has_dce_engine_info(adev)) {
+               u32 tmp;
+               int crtc_enabled, i;
+
+               dce_v8_0_set_vga_render_state(adev, false);
+
+               /*Disable crtc*/
+               for (i = 0; i < dce_v8_0_get_num_crtc(adev); i++) {
+                       crtc_enabled = REG_GET_FIELD(RREG32(mmCRTC_CONTROL + crtc_offsets[i]),
+                                                                        CRTC_CONTROL, CRTC_MASTER_EN);
+                       if (crtc_enabled) {
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 1);
+                               tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]);
+                               tmp = REG_SET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN, 0);
+                               WREG32(mmCRTC_CONTROL + crtc_offsets[i], tmp);
+                               WREG32(mmCRTC_UPDATE_LOCK + crtc_offsets[i], 0);
+                       }
+               }
+       }
+}
+
 static void dce_v8_0_program_fmt(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
@@ -1501,13 +1579,13 @@ static void dce_v8_0_audio_write_sad_regs(struct drm_encoder *encoder)
 
                        if (sad->format == eld_reg_to_type[i][1]) {
                                if (sad->channels > max_channels) {
-                               value = (sad->channels <<
-                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__MAX_CHANNELS__SHIFT) |
-                               (sad->byte2 <<
-                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__DESCRIPTOR_BYTE_2__SHIFT) |
-                               (sad->freq <<
-                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__SUPPORTED_FREQUENCIES__SHIFT);
-                               max_channels = sad->channels;
+                                       value = (sad->channels <<
+                                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__MAX_CHANNELS__SHIFT) |
+                                               (sad->byte2 <<
+                                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__DESCRIPTOR_BYTE_2__SHIFT) |
+                                               (sad->freq <<
+                                                AZALIA_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0__SUPPORTED_FREQUENCIES__SHIFT);
+                                       max_channels = sad->channels;
                                }
 
                                if (sad->format == HDMI_AUDIO_CODING_TYPE_PCM)
@@ -1613,7 +1691,7 @@ static void dce_v8_0_afmt_update_ACR(struct drm_encoder *encoder, uint32_t clock
        struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
        uint32_t offset = dig->afmt->offset;
 
-       WREG32(mmHDMI_ACR_32_0 + offset, (acr.cts_32khz << HDMI_ACR_44_0__HDMI_ACR_CTS_44__SHIFT));
+       WREG32(mmHDMI_ACR_32_0 + offset, (acr.cts_32khz << HDMI_ACR_32_0__HDMI_ACR_CTS_32__SHIFT));
        WREG32(mmHDMI_ACR_32_1 + offset, acr.n_32khz);
 
        WREG32(mmHDMI_ACR_44_0 + offset, (acr.cts_44_1khz << HDMI_ACR_44_0__HDMI_ACR_CTS_44__SHIFT));
@@ -1693,6 +1771,7 @@ static void dce_v8_0_afmt_setmode(struct drm_encoder *encoder,
        /* Silent, r600_hdmi_enable will raise WARN for us */
        if (!dig->afmt->enabled)
                return;
+
        offset = dig->afmt->offset;
 
        /* hdmi deep color mode general control packets setup, if bpc > 8 */
@@ -1817,7 +1896,7 @@ static void dce_v8_0_afmt_setmode(struct drm_encoder *encoder,
 
        WREG32_OR(mmHDMI_INFOFRAME_CONTROL0 + offset,
                  HDMI_INFOFRAME_CONTROL0__HDMI_AVI_INFO_SEND_MASK | /* enable AVI info frames */
-                 HDMI_INFOFRAME_CONTROL0__HDMI_AVI_INFO_SEND_MASK); /* required for audio info values to be updated */
+                 HDMI_INFOFRAME_CONTROL0__HDMI_AVI_INFO_CONT_MASK); /* required for audio info values to be updated */
 
        WREG32_P(mmHDMI_INFOFRAME_CONTROL1 + offset,
                 (2 << HDMI_INFOFRAME_CONTROL1__HDMI_AVI_INFO_LINE__SHIFT), /* anything other than 0 */
@@ -1826,13 +1905,12 @@ static void dce_v8_0_afmt_setmode(struct drm_encoder *encoder,
        WREG32_OR(mmAFMT_AUDIO_PACKET_CONTROL + offset,
                  AFMT_AUDIO_PACKET_CONTROL__AFMT_AUDIO_SAMPLE_SEND_MASK); /* send audio packets */
 
-       /* it's unknown what these bits do excatly, but it's indeed quite useful for debugging */
        WREG32(mmAFMT_RAMP_CONTROL0 + offset, 0x00FFFFFF);
        WREG32(mmAFMT_RAMP_CONTROL1 + offset, 0x007FFFFF);
        WREG32(mmAFMT_RAMP_CONTROL2 + offset, 0x00000001);
        WREG32(mmAFMT_RAMP_CONTROL3 + offset, 0x00000001);
 
-       /* enable audio after to setting up hw */
+       /* enable audio after setting up hw */
        dce_v8_0_audio_enable(adev, dig->afmt->pin, true);
 }
 
@@ -1944,7 +2022,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
        struct amdgpu_framebuffer *amdgpu_fb;
        struct drm_framebuffer *target_fb;
        struct drm_gem_object *obj;
-       struct amdgpu_bo *rbo;
+       struct amdgpu_bo *abo;
        uint64_t fb_location, tiling_flags;
        uint32_t fb_format, fb_pitch_pixels;
        u32 fb_swap = (GRPH_ENDIAN_NONE << GRPH_SWAP_CNTL__GRPH_ENDIAN_SWAP__SHIFT);
@@ -1952,6 +2030,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
        u32 viewport_w, viewport_h;
        int r;
        bool bypass_lut = false;
+       char *format_name;
 
        /* no fb bound */
        if (!atomic && !crtc->primary->fb) {
@@ -1971,23 +2050,23 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
         * just update base pointers
         */
        obj = amdgpu_fb->obj;
-       rbo = gem_to_amdgpu_bo(obj);
-       r = amdgpu_bo_reserve(rbo, false);
+       abo = gem_to_amdgpu_bo(obj);
+       r = amdgpu_bo_reserve(abo, false);
        if (unlikely(r != 0))
                return r;
 
        if (atomic) {
-               fb_location = amdgpu_bo_gpu_offset(rbo);
+               fb_location = amdgpu_bo_gpu_offset(abo);
        } else {
-               r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
+               r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, &fb_location);
                if (unlikely(r != 0)) {
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unreserve(abo);
                        return -EINVAL;
                }
        }
 
-       amdgpu_bo_get_tiling_flags(rbo, &tiling_flags);
-       amdgpu_bo_unreserve(rbo);
+       amdgpu_bo_get_tiling_flags(abo, &tiling_flags);
+       amdgpu_bo_unreserve(abo);
 
        pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG);
 
@@ -1999,7 +2078,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
        case DRM_FORMAT_XRGB4444:
        case DRM_FORMAT_ARGB4444:
                fb_format = ((GRPH_DEPTH_16BPP << GRPH_CONTROL__GRPH_DEPTH__SHIFT) |
-                            (GRPH_FORMAT_ARGB1555 << GRPH_CONTROL__GRPH_FORMAT__SHIFT));
+                            (GRPH_FORMAT_ARGB4444 << GRPH_CONTROL__GRPH_FORMAT__SHIFT));
 #ifdef __BIG_ENDIAN
                fb_swap = (GRPH_ENDIAN_8IN16 << GRPH_SWAP_CNTL__GRPH_ENDIAN_SWAP__SHIFT);
 #endif
@@ -2056,8 +2135,9 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
                bypass_lut = true;
                break;
        default:
-               DRM_ERROR("Unsupported screen format %s\n",
-                         drm_get_format_name(target_fb->pixel_format));
+               format_name = drm_get_format_name(target_fb->pixel_format);
+               DRM_ERROR("Unsupported screen format %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
@@ -2137,17 +2217,17 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(mmVIEWPORT_SIZE + amdgpu_crtc->crtc_offset,
               (viewport_w << 16) | viewport_h);
 
-       /* set pageflip to happen only at start of vblank interval (front porch) */
-       WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 3);
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->primary->fb) {
                amdgpu_fb = to_amdgpu_framebuffer(fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r != 0))
                        return r;
-               amdgpu_bo_unpin(rbo);
-               amdgpu_bo_unreserve(rbo);
+               amdgpu_bo_unpin(abo);
+               amdgpu_bo_unreserve(abo);
        }
 
        /* Bytes per pixel may have changed */
@@ -2552,7 +2632,7 @@ static const struct drm_crtc_funcs dce_v8_0_crtc_funcs = {
        .gamma_set = dce_v8_0_crtc_gamma_set,
        .set_config = amdgpu_crtc_set_config,
        .destroy = dce_v8_0_crtc_destroy,
-       .page_flip = amdgpu_crtc_page_flip,
+       .page_flip_target = amdgpu_crtc_page_flip_target,
 };
 
 static void dce_v8_0_crtc_dpms(struct drm_crtc *crtc, int mode)
@@ -2619,16 +2699,16 @@ static void dce_v8_0_crtc_disable(struct drm_crtc *crtc)
        if (crtc->primary->fb) {
                int r;
                struct amdgpu_framebuffer *amdgpu_fb;
-               struct amdgpu_bo *rbo;
+               struct amdgpu_bo *abo;
 
                amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
-               rbo = gem_to_amdgpu_bo(amdgpu_fb->obj);
-               r = amdgpu_bo_reserve(rbo, false);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
                if (unlikely(r))
-                       DRM_ERROR("failed to reserve rbo before unpin\n");
+                       DRM_ERROR("failed to reserve abo before unpin\n");
                else {
-                       amdgpu_bo_unpin(rbo);
-                       amdgpu_bo_unreserve(rbo);
+                       amdgpu_bo_unpin(abo);
+                       amdgpu_bo_unreserve(abo);
                }
        }
        /* disable the GRPH */
@@ -2653,7 +2733,7 @@ static void dce_v8_0_crtc_disable(struct drm_crtc *crtc)
        case ATOM_PPLL2:
                /* disable the ppll */
                amdgpu_atombios_crtc_program_pll(crtc, amdgpu_crtc->crtc_id, amdgpu_crtc->pll_id,
-                                         0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
+                                                 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
                break;
        case ATOM_PPLL0:
                /* disable the ppll */
@@ -2803,21 +2883,20 @@ static int dce_v8_0_early_init(void *handle)
        dce_v8_0_set_display_funcs(adev);
        dce_v8_0_set_irq_funcs(adev);
 
+       adev->mode_info.num_crtc = dce_v8_0_get_num_crtc(adev);
+
        switch (adev->asic_type) {
        case CHIP_BONAIRE:
        case CHIP_HAWAII:
-               adev->mode_info.num_crtc = 6;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 6;
                break;
        case CHIP_KAVERI:
-               adev->mode_info.num_crtc = 4;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 7;
                break;
        case CHIP_KABINI:
        case CHIP_MULLINS:
-               adev->mode_info.num_crtc = 2;
                adev->mode_info.num_hpd = 6;
                adev->mode_info.num_dig = 6; /* ? */
                break;
@@ -3236,7 +3315,6 @@ static int dce_v8_0_crtc_irq(struct amdgpu_device *adev,
                        drm_handle_vblank(adev->ddev, crtc);
                }
                DRM_DEBUG("IH: D%d vblank\n", crtc + 1);
-
                break;
        case 1: /* vline */
                if (disp_int & interrupt_status_offsets[crtc].vline)
@@ -3245,7 +3323,6 @@ static int dce_v8_0_crtc_irq(struct amdgpu_device *adev,
                        DRM_DEBUG("IH: IH event w/o asserted irq bit?\n");
 
                DRM_DEBUG("IH: D%d vline\n", crtc + 1);
-
                break;
        default:
                DRM_DEBUG("Unhandled interrupt: %d %d\n", entry->src_id, entry->src_data);
index 7701685..7d0770c 100644 (file)
@@ -26,4 +26,6 @@
 
 extern const struct amd_ip_funcs dce_v8_0_ip_funcs;
 
+void dce_v8_0_disable_dce(struct amdgpu_device *adev);
+
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c
new file mode 100644 (file)
index 0000000..c2bd9f0
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * Copyright 2014 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 "amdgpu.h"
+#include "amdgpu_pm.h"
+#include "amdgpu_i2c.h"
+#include "atom.h"
+#include "amdgpu_pll.h"
+#include "amdgpu_connectors.h"
+#ifdef CONFIG_DRM_AMDGPU_CIK
+#include "dce_v8_0.h"
+#endif
+#include "dce_v10_0.h"
+#include "dce_v11_0.h"
+#include "dce_virtual.h"
+
+static void dce_virtual_set_display_funcs(struct amdgpu_device *adev);
+static void dce_virtual_set_irq_funcs(struct amdgpu_device *adev);
+static int dce_virtual_pageflip_irq(struct amdgpu_device *adev,
+                                 struct amdgpu_irq_src *source,
+                                 struct amdgpu_iv_entry *entry);
+
+/**
+ * dce_virtual_vblank_wait - vblank wait asic callback.
+ *
+ * @adev: amdgpu_device pointer
+ * @crtc: crtc to wait for vblank on
+ *
+ * Wait for vblank on the requested crtc (evergreen+).
+ */
+static void dce_virtual_vblank_wait(struct amdgpu_device *adev, int crtc)
+{
+       return;
+}
+
+static u32 dce_virtual_vblank_get_counter(struct amdgpu_device *adev, int crtc)
+{
+       return 0;
+}
+
+static void dce_virtual_page_flip(struct amdgpu_device *adev,
+                             int crtc_id, u64 crtc_base, bool async)
+{
+       return;
+}
+
+static int dce_virtual_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
+                                       u32 *vbl, u32 *position)
+{
+       *vbl = 0;
+       *position = 0;
+
+       return -EINVAL;
+}
+
+static bool dce_virtual_hpd_sense(struct amdgpu_device *adev,
+                              enum amdgpu_hpd_id hpd)
+{
+       return true;
+}
+
+static void dce_virtual_hpd_set_polarity(struct amdgpu_device *adev,
+                                     enum amdgpu_hpd_id hpd)
+{
+       return;
+}
+
+static u32 dce_virtual_hpd_get_gpio_reg(struct amdgpu_device *adev)
+{
+       return 0;
+}
+
+static bool dce_virtual_is_display_hung(struct amdgpu_device *adev)
+{
+       return false;
+}
+
+static void dce_virtual_stop_mc_access(struct amdgpu_device *adev,
+                             struct amdgpu_mode_mc_save *save)
+{
+       switch (adev->asic_type) {
+#ifdef CONFIG_DRM_AMDGPU_CIK
+       case CHIP_BONAIRE:
+       case CHIP_HAWAII:
+       case CHIP_KAVERI:
+       case CHIP_KABINI:
+       case CHIP_MULLINS:
+               dce_v8_0_disable_dce(adev);
+               break;
+#endif
+       case CHIP_FIJI:
+       case CHIP_TONGA:
+               dce_v10_0_disable_dce(adev);
+               break;
+       case CHIP_CARRIZO:
+       case CHIP_STONEY:
+       case CHIP_POLARIS11:
+       case CHIP_POLARIS10:
+               dce_v11_0_disable_dce(adev);
+               break;
+       case CHIP_TOPAZ:
+               /* no DCE */
+               return;
+       default:
+               DRM_ERROR("Virtual display unsupported ASIC type: 0x%X\n", adev->asic_type);
+       }
+
+       return;
+}
+static void dce_virtual_resume_mc_access(struct amdgpu_device *adev,
+                               struct amdgpu_mode_mc_save *save)
+{
+       return;
+}
+
+static void dce_virtual_set_vga_render_state(struct amdgpu_device *adev,
+                                   bool render)
+{
+       return;
+}
+
+/**
+ * dce_virtual_bandwidth_update - program display watermarks
+ *
+ * @adev: amdgpu_device pointer
+ *
+ * Calculate and program the display watermarks and line
+ * buffer allocation (CIK).
+ */
+static void dce_virtual_bandwidth_update(struct amdgpu_device *adev)
+{
+       return;
+}
+
+static int dce_virtual_crtc_gamma_set(struct drm_crtc *crtc, u16 *red,
+                                     u16 *green, u16 *blue, uint32_t size)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       int i;
+
+       /* userspace palettes are always correct as is */
+       for (i = 0; i < size; i++) {
+               amdgpu_crtc->lut_r[i] = red[i] >> 6;
+               amdgpu_crtc->lut_g[i] = green[i] >> 6;
+               amdgpu_crtc->lut_b[i] = blue[i] >> 6;
+       }
+
+       return 0;
+}
+
+static void dce_virtual_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       drm_crtc_cleanup(crtc);
+       kfree(amdgpu_crtc);
+}
+
+static const struct drm_crtc_funcs dce_virtual_crtc_funcs = {
+       .cursor_set2 = NULL,
+       .cursor_move = NULL,
+       .gamma_set = dce_virtual_crtc_gamma_set,
+       .set_config = amdgpu_crtc_set_config,
+       .destroy = dce_virtual_crtc_destroy,
+       .page_flip_target = amdgpu_crtc_page_flip_target,
+};
+
+static void dce_virtual_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct amdgpu_device *adev = dev->dev_private;
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       unsigned type;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               amdgpu_crtc->enabled = true;
+               /* Make sure VBLANK and PFLIP interrupts are still enabled */
+               type = amdgpu_crtc_idx_to_irq_type(adev, amdgpu_crtc->crtc_id);
+               amdgpu_irq_update(adev, &adev->crtc_irq, type);
+               amdgpu_irq_update(adev, &adev->pageflip_irq, type);
+               drm_vblank_on(dev, amdgpu_crtc->crtc_id);
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+       case DRM_MODE_DPMS_OFF:
+               drm_vblank_off(dev, amdgpu_crtc->crtc_id);
+               amdgpu_crtc->enabled = false;
+               break;
+       }
+}
+
+
+static void dce_virtual_crtc_prepare(struct drm_crtc *crtc)
+{
+       dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void dce_virtual_crtc_commit(struct drm_crtc *crtc)
+{
+       dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static void dce_virtual_crtc_disable(struct drm_crtc *crtc)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       dce_virtual_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       if (crtc->primary->fb) {
+               int r;
+               struct amdgpu_framebuffer *amdgpu_fb;
+               struct amdgpu_bo *abo;
+
+               amdgpu_fb = to_amdgpu_framebuffer(crtc->primary->fb);
+               abo = gem_to_amdgpu_bo(amdgpu_fb->obj);
+               r = amdgpu_bo_reserve(abo, false);
+               if (unlikely(r))
+                       DRM_ERROR("failed to reserve abo before unpin\n");
+               else {
+                       amdgpu_bo_unpin(abo);
+                       amdgpu_bo_unreserve(abo);
+               }
+       }
+
+       amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
+       amdgpu_crtc->encoder = NULL;
+       amdgpu_crtc->connector = NULL;
+}
+
+static int dce_virtual_crtc_mode_set(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode,
+                                 int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+
+       /* update the hw version fpr dpm */
+       amdgpu_crtc->hw_mode = *adjusted_mode;
+
+       return 0;
+}
+
+static bool dce_virtual_crtc_mode_fixup(struct drm_crtc *crtc,
+                                    const struct drm_display_mode *mode,
+                                    struct drm_display_mode *adjusted_mode)
+{
+       struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct drm_encoder *encoder;
+
+       /* assign the encoder to the amdgpu crtc to avoid repeated lookups later */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc == crtc) {
+                       amdgpu_crtc->encoder = encoder;
+                       amdgpu_crtc->connector = amdgpu_get_connector_for_encoder(encoder);
+                       break;
+               }
+       }
+       if ((amdgpu_crtc->encoder == NULL) || (amdgpu_crtc->connector == NULL)) {
+               amdgpu_crtc->encoder = NULL;
+               amdgpu_crtc->connector = NULL;
+               return false;
+       }
+
+       return true;
+}
+
+
+static int dce_virtual_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+                                 struct drm_framebuffer *old_fb)
+{
+       return 0;
+}
+
+static void dce_virtual_crtc_load_lut(struct drm_crtc *crtc)
+{
+       return;
+}
+
+static int dce_virtual_crtc_set_base_atomic(struct drm_crtc *crtc,
+                                        struct drm_framebuffer *fb,
+                                        int x, int y, enum mode_set_atomic state)
+{
+       return 0;
+}
+
+static const struct drm_crtc_helper_funcs dce_virtual_crtc_helper_funcs = {
+       .dpms = dce_virtual_crtc_dpms,
+       .mode_fixup = dce_virtual_crtc_mode_fixup,
+       .mode_set = dce_virtual_crtc_mode_set,
+       .mode_set_base = dce_virtual_crtc_set_base,
+       .mode_set_base_atomic = dce_virtual_crtc_set_base_atomic,
+       .prepare = dce_virtual_crtc_prepare,
+       .commit = dce_virtual_crtc_commit,
+       .load_lut = dce_virtual_crtc_load_lut,
+       .disable = dce_virtual_crtc_disable,
+};
+
+static int dce_virtual_crtc_init(struct amdgpu_device *adev, int index)
+{
+       struct amdgpu_crtc *amdgpu_crtc;
+       int i;
+
+       amdgpu_crtc = kzalloc(sizeof(struct amdgpu_crtc) +
+                             (AMDGPUFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
+       if (amdgpu_crtc == NULL)
+               return -ENOMEM;
+
+       drm_crtc_init(adev->ddev, &amdgpu_crtc->base, &dce_virtual_crtc_funcs);
+
+       drm_mode_crtc_set_gamma_size(&amdgpu_crtc->base, 256);
+       amdgpu_crtc->crtc_id = index;
+       adev->mode_info.crtcs[index] = amdgpu_crtc;
+
+       for (i = 0; i < 256; i++) {
+               amdgpu_crtc->lut_r[i] = i << 2;
+               amdgpu_crtc->lut_g[i] = i << 2;
+               amdgpu_crtc->lut_b[i] = i << 2;
+       }
+
+       amdgpu_crtc->pll_id = ATOM_PPLL_INVALID;
+       amdgpu_crtc->encoder = NULL;
+       amdgpu_crtc->connector = NULL;
+       drm_crtc_helper_add(&amdgpu_crtc->base, &dce_virtual_crtc_helper_funcs);
+
+       return 0;
+}
+
+static int dce_virtual_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       adev->mode_info.vsync_timer_enabled = AMDGPU_IRQ_STATE_DISABLE;
+       dce_virtual_set_display_funcs(adev);
+       dce_virtual_set_irq_funcs(adev);
+
+       adev->mode_info.num_crtc = 1;
+       adev->mode_info.num_hpd = 1;
+       adev->mode_info.num_dig = 1;
+       return 0;
+}
+
+static bool dce_virtual_get_connector_info(struct amdgpu_device *adev)
+{
+       struct amdgpu_i2c_bus_rec ddc_bus;
+       struct amdgpu_router router;
+       struct amdgpu_hpd hpd;
+
+       /* look up gpio for ddc, hpd */
+       ddc_bus.valid = false;
+       hpd.hpd = AMDGPU_HPD_NONE;
+       /* needed for aux chan transactions */
+       ddc_bus.hpd = hpd.hpd;
+
+       memset(&router, 0, sizeof(router));
+       router.ddc_valid = false;
+       router.cd_valid = false;
+       amdgpu_display_add_connector(adev,
+                                     0,
+                                     ATOM_DEVICE_CRT1_SUPPORT,
+                                     DRM_MODE_CONNECTOR_VIRTUAL, &ddc_bus,
+                                     CONNECTOR_OBJECT_ID_VIRTUAL,
+                                     &hpd,
+                                     &router);
+
+       amdgpu_display_add_encoder(adev, ENCODER_VIRTUAL_ENUM_VIRTUAL,
+                                                       ATOM_DEVICE_CRT1_SUPPORT,
+                                                       0);
+
+       amdgpu_link_encoder_connector(adev->ddev);
+
+       return true;
+}
+
+static int dce_virtual_sw_init(void *handle)
+{
+       int r, i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       r = amdgpu_irq_add_id(adev, 229, &adev->crtc_irq);
+       if (r)
+               return r;
+
+       adev->ddev->max_vblank_count = 0;
+
+       adev->ddev->mode_config.funcs = &amdgpu_mode_funcs;
+
+       adev->ddev->mode_config.max_width = 16384;
+       adev->ddev->mode_config.max_height = 16384;
+
+       adev->ddev->mode_config.preferred_depth = 24;
+       adev->ddev->mode_config.prefer_shadow = 1;
+
+       adev->ddev->mode_config.fb_base = adev->mc.aper_base;
+
+       r = amdgpu_modeset_create_props(adev);
+       if (r)
+               return r;
+
+       adev->ddev->mode_config.max_width = 16384;
+       adev->ddev->mode_config.max_height = 16384;
+
+       /* allocate crtcs */
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               r = dce_virtual_crtc_init(adev, i);
+               if (r)
+                       return r;
+       }
+
+       dce_virtual_get_connector_info(adev);
+       amdgpu_print_display_setup(adev->ddev);
+
+       drm_kms_helper_poll_init(adev->ddev);
+
+       adev->mode_info.mode_config_initialized = true;
+       return 0;
+}
+
+static int dce_virtual_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       kfree(adev->mode_info.bios_hardcoded_edid);
+
+       drm_kms_helper_poll_fini(adev->ddev);
+
+       drm_mode_config_cleanup(adev->ddev);
+       adev->mode_info.mode_config_initialized = false;
+       return 0;
+}
+
+static int dce_virtual_hw_init(void *handle)
+{
+       return 0;
+}
+
+static int dce_virtual_hw_fini(void *handle)
+{
+       return 0;
+}
+
+static int dce_virtual_suspend(void *handle)
+{
+       return dce_virtual_hw_fini(handle);
+}
+
+static int dce_virtual_resume(void *handle)
+{
+       return dce_virtual_hw_init(handle);
+}
+
+static bool dce_virtual_is_idle(void *handle)
+{
+       return true;
+}
+
+static int dce_virtual_wait_for_idle(void *handle)
+{
+       return 0;
+}
+
+static int dce_virtual_soft_reset(void *handle)
+{
+       return 0;
+}
+
+static int dce_virtual_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int dce_virtual_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       return 0;
+}
+
+const struct amd_ip_funcs dce_virtual_ip_funcs = {
+       .name = "dce_virtual",
+       .early_init = dce_virtual_early_init,
+       .late_init = NULL,
+       .sw_init = dce_virtual_sw_init,
+       .sw_fini = dce_virtual_sw_fini,
+       .hw_init = dce_virtual_hw_init,
+       .hw_fini = dce_virtual_hw_fini,
+       .suspend = dce_virtual_suspend,
+       .resume = dce_virtual_resume,
+       .is_idle = dce_virtual_is_idle,
+       .wait_for_idle = dce_virtual_wait_for_idle,
+       .soft_reset = dce_virtual_soft_reset,
+       .set_clockgating_state = dce_virtual_set_clockgating_state,
+       .set_powergating_state = dce_virtual_set_powergating_state,
+};
+
+/* these are handled by the primary encoders */
+static void dce_virtual_encoder_prepare(struct drm_encoder *encoder)
+{
+       return;
+}
+
+static void dce_virtual_encoder_commit(struct drm_encoder *encoder)
+{
+       return;
+}
+
+static void
+dce_virtual_encoder_mode_set(struct drm_encoder *encoder,
+                     struct drm_display_mode *mode,
+                     struct drm_display_mode *adjusted_mode)
+{
+       return;
+}
+
+static void dce_virtual_encoder_disable(struct drm_encoder *encoder)
+{
+       return;
+}
+
+static void
+dce_virtual_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       return;
+}
+
+static bool dce_virtual_encoder_mode_fixup(struct drm_encoder *encoder,
+                                   const struct drm_display_mode *mode,
+                                   struct drm_display_mode *adjusted_mode)
+{
+
+       /* set the active encoder to connector routing */
+       amdgpu_encoder_set_active_device(encoder);
+
+       return true;
+}
+
+static const struct drm_encoder_helper_funcs dce_virtual_encoder_helper_funcs = {
+       .dpms = dce_virtual_encoder_dpms,
+       .mode_fixup = dce_virtual_encoder_mode_fixup,
+       .prepare = dce_virtual_encoder_prepare,
+       .mode_set = dce_virtual_encoder_mode_set,
+       .commit = dce_virtual_encoder_commit,
+       .disable = dce_virtual_encoder_disable,
+};
+
+static void dce_virtual_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
+
+       kfree(amdgpu_encoder->enc_priv);
+       drm_encoder_cleanup(encoder);
+       kfree(amdgpu_encoder);
+}
+
+static const struct drm_encoder_funcs dce_virtual_encoder_funcs = {
+       .destroy = dce_virtual_encoder_destroy,
+};
+
+static void dce_virtual_encoder_add(struct amdgpu_device *adev,
+                                uint32_t encoder_enum,
+                                uint32_t supported_device,
+                                u16 caps)
+{
+       struct drm_device *dev = adev->ddev;
+       struct drm_encoder *encoder;
+       struct amdgpu_encoder *amdgpu_encoder;
+
+       /* see if we already added it */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               amdgpu_encoder = to_amdgpu_encoder(encoder);
+               if (amdgpu_encoder->encoder_enum == encoder_enum) {
+                       amdgpu_encoder->devices |= supported_device;
+                       return;
+               }
+
+       }
+
+       /* add a new one */
+       amdgpu_encoder = kzalloc(sizeof(struct amdgpu_encoder), GFP_KERNEL);
+       if (!amdgpu_encoder)
+               return;
+
+       encoder = &amdgpu_encoder->base;
+       encoder->possible_crtcs = 0x1;
+       amdgpu_encoder->enc_priv = NULL;
+       amdgpu_encoder->encoder_enum = encoder_enum;
+       amdgpu_encoder->encoder_id = (encoder_enum & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
+       amdgpu_encoder->devices = supported_device;
+       amdgpu_encoder->rmx_type = RMX_OFF;
+       amdgpu_encoder->underscan_type = UNDERSCAN_OFF;
+       amdgpu_encoder->is_ext_encoder = false;
+       amdgpu_encoder->caps = caps;
+
+       drm_encoder_init(dev, encoder, &dce_virtual_encoder_funcs,
+                                        DRM_MODE_ENCODER_VIRTUAL, NULL);
+       drm_encoder_helper_add(encoder, &dce_virtual_encoder_helper_funcs);
+       DRM_INFO("[FM]encoder: %d is VIRTUAL\n", amdgpu_encoder->encoder_id);
+}
+
+static const struct amdgpu_display_funcs dce_virtual_display_funcs = {
+       .set_vga_render_state = &dce_virtual_set_vga_render_state,
+       .bandwidth_update = &dce_virtual_bandwidth_update,
+       .vblank_get_counter = &dce_virtual_vblank_get_counter,
+       .vblank_wait = &dce_virtual_vblank_wait,
+       .is_display_hung = &dce_virtual_is_display_hung,
+       .backlight_set_level = NULL,
+       .backlight_get_level = NULL,
+       .hpd_sense = &dce_virtual_hpd_sense,
+       .hpd_set_polarity = &dce_virtual_hpd_set_polarity,
+       .hpd_get_gpio_reg = &dce_virtual_hpd_get_gpio_reg,
+       .page_flip = &dce_virtual_page_flip,
+       .page_flip_get_scanoutpos = &dce_virtual_crtc_get_scanoutpos,
+       .add_encoder = &dce_virtual_encoder_add,
+       .add_connector = &amdgpu_connector_add,
+       .stop_mc_access = &dce_virtual_stop_mc_access,
+       .resume_mc_access = &dce_virtual_resume_mc_access,
+};
+
+static void dce_virtual_set_display_funcs(struct amdgpu_device *adev)
+{
+       if (adev->mode_info.funcs == NULL)
+               adev->mode_info.funcs = &dce_virtual_display_funcs;
+}
+
+static enum hrtimer_restart dce_virtual_vblank_timer_handle(struct hrtimer *vblank_timer)
+{
+       struct amdgpu_mode_info *mode_info = container_of(vblank_timer, struct amdgpu_mode_info ,vblank_timer);
+       struct amdgpu_device *adev = container_of(mode_info, struct amdgpu_device ,mode_info);
+       unsigned crtc = 0;
+       drm_handle_vblank(adev->ddev, crtc);
+       dce_virtual_pageflip_irq(adev, NULL, NULL);
+       hrtimer_start(vblank_timer, ktime_set(0, DCE_VIRTUAL_VBLANK_PERIOD), HRTIMER_MODE_REL);
+       return HRTIMER_NORESTART;
+}
+
+static void dce_virtual_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev,
+                                                    int crtc,
+                                                    enum amdgpu_interrupt_state state)
+{
+       if (crtc >= adev->mode_info.num_crtc) {
+               DRM_DEBUG("invalid crtc %d\n", crtc);
+               return;
+       }
+
+       if (state && !adev->mode_info.vsync_timer_enabled) {
+               DRM_DEBUG("Enable software vsync timer\n");
+               hrtimer_init(&adev->mode_info.vblank_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+               hrtimer_set_expires(&adev->mode_info.vblank_timer, ktime_set(0, DCE_VIRTUAL_VBLANK_PERIOD));
+               adev->mode_info.vblank_timer.function = dce_virtual_vblank_timer_handle;
+               hrtimer_start(&adev->mode_info.vblank_timer, ktime_set(0, DCE_VIRTUAL_VBLANK_PERIOD), HRTIMER_MODE_REL);
+       } else if (!state && adev->mode_info.vsync_timer_enabled) {
+               DRM_DEBUG("Disable software vsync timer\n");
+               hrtimer_cancel(&adev->mode_info.vblank_timer);
+       }
+
+       adev->mode_info.vsync_timer_enabled = state;
+       DRM_DEBUG("[FM]set crtc %d vblank interrupt state %d\n", crtc, state);
+}
+
+
+static int dce_virtual_set_crtc_irq_state(struct amdgpu_device *adev,
+                                       struct amdgpu_irq_src *source,
+                                       unsigned type,
+                                       enum amdgpu_interrupt_state state)
+{
+       switch (type) {
+       case AMDGPU_CRTC_IRQ_VBLANK1:
+               dce_virtual_set_crtc_vblank_interrupt_state(adev, 0, state);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static void dce_virtual_crtc_vblank_int_ack(struct amdgpu_device *adev,
+                                         int crtc)
+{
+       if (crtc >= adev->mode_info.num_crtc) {
+               DRM_DEBUG("invalid crtc %d\n", crtc);
+               return;
+       }
+}
+
+static int dce_virtual_crtc_irq(struct amdgpu_device *adev,
+                             struct amdgpu_irq_src *source,
+                             struct amdgpu_iv_entry *entry)
+{
+       unsigned crtc = 0;
+       unsigned irq_type = AMDGPU_CRTC_IRQ_VBLANK1;
+
+       dce_virtual_crtc_vblank_int_ack(adev, crtc);
+
+       if (amdgpu_irq_enabled(adev, source, irq_type)) {
+               drm_handle_vblank(adev->ddev, crtc);
+       }
+       dce_virtual_pageflip_irq(adev, NULL, NULL);
+       DRM_DEBUG("IH: D%d vblank\n", crtc + 1);
+       return 0;
+}
+
+static int dce_virtual_set_pageflip_irq_state(struct amdgpu_device *adev,
+                                           struct amdgpu_irq_src *src,
+                                           unsigned type,
+                                           enum amdgpu_interrupt_state state)
+{
+       if (type >= adev->mode_info.num_crtc) {
+               DRM_ERROR("invalid pageflip crtc %d\n", type);
+               return -EINVAL;
+       }
+       DRM_DEBUG("[FM]set pageflip irq type %d state %d\n", type, state);
+
+       return 0;
+}
+
+static int dce_virtual_pageflip_irq(struct amdgpu_device *adev,
+                                 struct amdgpu_irq_src *source,
+                                 struct amdgpu_iv_entry *entry)
+{
+       unsigned long flags;
+       unsigned crtc_id = 0;
+       struct amdgpu_crtc *amdgpu_crtc;
+       struct amdgpu_flip_work *works;
+
+       crtc_id = 0;
+       amdgpu_crtc = adev->mode_info.crtcs[crtc_id];
+
+       if (crtc_id >= adev->mode_info.num_crtc) {
+               DRM_ERROR("invalid pageflip crtc %d\n", crtc_id);
+               return -EINVAL;
+       }
+
+       /* IRQ could occur when in initial stage */
+       if (amdgpu_crtc == NULL)
+               return 0;
+
+       spin_lock_irqsave(&adev->ddev->event_lock, flags);
+       works = amdgpu_crtc->pflip_works;
+       if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
+               DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d != "
+                       "AMDGPU_FLIP_SUBMITTED(%d)\n",
+                       amdgpu_crtc->pflip_status,
+                       AMDGPU_FLIP_SUBMITTED);
+               spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
+               return 0;
+       }
+
+       /* page flip completed. clean up */
+       amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
+       amdgpu_crtc->pflip_works = NULL;
+
+       /* wakeup usersapce */
+       if (works->event)
+               drm_crtc_send_vblank_event(&amdgpu_crtc->base, works->event);
+
+       spin_unlock_irqrestore(&adev->ddev->event_lock, flags);
+
+       drm_crtc_vblank_put(&amdgpu_crtc->base);
+       schedule_work(&works->unpin_work);
+
+       return 0;
+}
+
+static const struct amdgpu_irq_src_funcs dce_virtual_crtc_irq_funcs = {
+       .set = dce_virtual_set_crtc_irq_state,
+       .process = dce_virtual_crtc_irq,
+};
+
+static const struct amdgpu_irq_src_funcs dce_virtual_pageflip_irq_funcs = {
+       .set = dce_virtual_set_pageflip_irq_state,
+       .process = dce_virtual_pageflip_irq,
+};
+
+static void dce_virtual_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->crtc_irq.num_types = AMDGPU_CRTC_IRQ_LAST;
+       adev->crtc_irq.funcs = &dce_virtual_crtc_irq_funcs;
+
+       adev->pageflip_irq.num_types = AMDGPU_PAGEFLIP_IRQ_LAST;
+       adev->pageflip_irq.funcs = &dce_virtual_pageflip_irq_funcs;
+}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.h b/drivers/gpu/drm/amd/amdgpu/dce_virtual.h
new file mode 100644 (file)
index 0000000..e239243
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014 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 __DCE_VIRTUAL_H__
+#define __DCE_VIRTUAL_H__
+
+extern const struct amd_ip_funcs dce_virtual_ip_funcs;
+#define DCE_VIRTUAL_VBLANK_PERIOD 16666666
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/amdgpu/fiji_dpm.c b/drivers/gpu/drm/amd/amdgpu/fiji_dpm.c
deleted file mode 100644 (file)
index ed03b75..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "fiji_smum.h"
-
-MODULE_FIRMWARE("amdgpu/fiji_smc.bin");
-
-static void fiji_dpm_set_funcs(struct amdgpu_device *adev);
-
-static int fiji_dpm_early_init(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       fiji_dpm_set_funcs(adev);
-
-       return 0;
-}
-
-static int fiji_dpm_init_microcode(struct amdgpu_device *adev)
-{
-       char fw_name[30] = "amdgpu/fiji_smc.bin";
-       int err;
-
-       err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
-       if (err)
-               goto out;
-       err = amdgpu_ucode_validate(adev->pm.fw);
-
-out:
-       if (err) {
-               DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
-               release_firmware(adev->pm.fw);
-               adev->pm.fw = NULL;
-       }
-       return err;
-}
-
-static int fiji_dpm_sw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       ret = fiji_dpm_init_microcode(adev);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int fiji_dpm_sw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       release_firmware(adev->pm.fw);
-       adev->pm.fw = NULL;
-
-       return 0;
-}
-
-static int fiji_dpm_hw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-
-       ret = fiji_smu_init(adev);
-       if (ret) {
-               DRM_ERROR("SMU initialization failed\n");
-               goto fail;
-       }
-
-       ret = fiji_smu_start(adev);
-       if (ret) {
-               DRM_ERROR("SMU start failed\n");
-               goto fail;
-       }
-
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-
-fail:
-       adev->firmware.smu_load = false;
-       mutex_unlock(&adev->pm.mutex);
-       return -EINVAL;
-}
-
-static int fiji_dpm_hw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-       mutex_lock(&adev->pm.mutex);
-       fiji_smu_fini(adev);
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-}
-
-static int fiji_dpm_suspend(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       fiji_dpm_hw_fini(adev);
-
-       return 0;
-}
-
-static int fiji_dpm_resume(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       fiji_dpm_hw_init(adev);
-
-       return 0;
-}
-
-static int fiji_dpm_set_clockgating_state(void *handle,
-                       enum amd_clockgating_state state)
-{
-       return 0;
-}
-
-static int fiji_dpm_set_powergating_state(void *handle,
-                       enum amd_powergating_state state)
-{
-       return 0;
-}
-
-const struct amd_ip_funcs fiji_dpm_ip_funcs = {
-       .name = "fiji_dpm",
-       .early_init = fiji_dpm_early_init,
-       .late_init = NULL,
-       .sw_init = fiji_dpm_sw_init,
-       .sw_fini = fiji_dpm_sw_fini,
-       .hw_init = fiji_dpm_hw_init,
-       .hw_fini = fiji_dpm_hw_fini,
-       .suspend = fiji_dpm_suspend,
-       .resume = fiji_dpm_resume,
-       .is_idle = NULL,
-       .wait_for_idle = NULL,
-       .soft_reset = NULL,
-       .set_clockgating_state = fiji_dpm_set_clockgating_state,
-       .set_powergating_state = fiji_dpm_set_powergating_state,
-};
-
-static const struct amdgpu_dpm_funcs fiji_dpm_funcs = {
-       .get_temperature = NULL,
-       .pre_set_power_state = NULL,
-       .set_power_state = NULL,
-       .post_set_power_state = NULL,
-       .display_configuration_changed = NULL,
-       .get_sclk = NULL,
-       .get_mclk = NULL,
-       .print_power_state = NULL,
-       .debugfs_print_current_performance_level = NULL,
-       .force_performance_level = NULL,
-       .vblank_too_short = NULL,
-       .powergate_uvd = NULL,
-};
-
-static void fiji_dpm_set_funcs(struct amdgpu_device *adev)
-{
-       if (NULL == adev->pm.funcs)
-               adev->pm.funcs = &fiji_dpm_funcs;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/fiji_smc.c b/drivers/gpu/drm/amd/amdgpu/fiji_smc.c
deleted file mode 100644 (file)
index b3e19ba..0000000
+++ /dev/null
@@ -1,863 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "fiji_ppsmc.h"
-#include "fiji_smum.h"
-#include "smu_ucode_xfer_vi.h"
-#include "amdgpu_ucode.h"
-
-#include "smu/smu_7_1_3_d.h"
-#include "smu/smu_7_1_3_sh_mask.h"
-
-#define FIJI_SMC_SIZE 0x20000
-
-static int fiji_set_smc_sram_address(struct amdgpu_device *adev, uint32_t smc_address, uint32_t limit)
-{
-       uint32_t val;
-
-       if (smc_address & 3)
-               return -EINVAL;
-
-       if ((smc_address + 3) > limit)
-               return -EINVAL;
-
-       WREG32(mmSMC_IND_INDEX_0, smc_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       return 0;
-}
-
-static int fiji_copy_bytes_to_smc(struct amdgpu_device *adev, uint32_t smc_start_address, const uint8_t *src, uint32_t byte_count, uint32_t limit)
-{
-       uint32_t addr;
-       uint32_t data, orig_data;
-       int result = 0;
-       uint32_t extra_shift;
-       unsigned long flags;
-
-       if (smc_start_address & 3)
-               return -EINVAL;
-
-       if ((smc_start_address + byte_count) > limit)
-               return -EINVAL;
-
-       addr = smc_start_address;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       while (byte_count >= 4) {
-               /* Bytes are written into the SMC addres space with the MSB first */
-               data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
-
-               result = fiji_set_smc_sram_address(adev, addr, limit);
-
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-
-               src += 4;
-               byte_count -= 4;
-               addr += 4;
-       }
-
-       if (0 != byte_count) {
-               /* Now write odd bytes left, do a read modify write cycle */
-               data = 0;
-
-               result = fiji_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               orig_data = RREG32(mmSMC_IND_DATA_0);
-               extra_shift = 8 * (4 - byte_count);
-
-               while (byte_count > 0) {
-                       data = (data << 8) + *src++;
-                       byte_count--;
-               }
-
-               data <<= extra_shift;
-               data |= (orig_data & ~((~0UL) << extra_shift));
-
-               result = fiji_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-       }
-
-out:
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int fiji_program_jump_on_start(struct amdgpu_device *adev)
-{
-       static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
-       fiji_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
-
-       return 0;
-}
-
-static bool fiji_is_smc_ram_running(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
-
-       return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
-}
-
-static int wait_smu_response(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32(mmSMC_RESP_0);
-               if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int fiji_send_msg_to_smc_offset(struct amdgpu_device *adev)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, 0x20000);
-       WREG32(mmSMC_MESSAGE_0, PPSMC_MSG_Test);
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send message\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int fiji_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
-{
-       if (!fiji_is_smc_ram_running(adev))
-       {
-               return -EINVAL;
-       }
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send message\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int fiji_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
-                                               PPSMC_Msg msg)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-static int fiji_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
-                                               PPSMC_Msg msg,
-                                               uint32_t parameter)
-{
-       if (!fiji_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return fiji_send_msg_to_smc(adev, msg);
-}
-
-static int fiji_send_msg_to_smc_with_parameter_without_waiting(
-                                       struct amdgpu_device *adev,
-                                       PPSMC_Msg msg, uint32_t parameter)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return fiji_send_msg_to_smc_without_waiting(adev, msg);
-}
-
-#if 0 /* not used yet */
-static int fiji_wait_for_smc_inactive(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       if (!fiji_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-               if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-#endif
-
-static int fiji_smu_upload_firmware_image(struct amdgpu_device *adev)
-{
-       const struct smc_firmware_header_v1_0 *hdr;
-       uint32_t ucode_size;
-       uint32_t ucode_start_address;
-       const uint8_t *src;
-       uint32_t val;
-       uint32_t byte_count;
-       uint32_t *data;
-       unsigned long flags;
-
-       if (!adev->pm.fw)
-               return -EINVAL;
-
-       /* Skip SMC ucode loading on SR-IOV capable boards.
-        * vbios does this for us in asic_init in that case.
-        */
-       if (adev->virtualization.supports_sr_iov)
-               return 0;
-
-       hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
-       amdgpu_ucode_print_smc_hdr(&hdr->header);
-
-       adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
-       ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
-       ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
-       src = (const uint8_t *)
-               (adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
-
-       if (ucode_size & 3) {
-               DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
-               return -EINVAL;
-       }
-
-       if (ucode_size > FIJI_SMC_SIZE) {
-               DRM_ERROR("SMC address is beyond the SMC RAM area\n");
-               return -EINVAL;
-       }
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       byte_count = ucode_size;
-       data = (uint32_t *)src;
-       for (; byte_count >= 4; data++, byte_count -= 4)
-               WREG32(mmSMC_IND_DATA_0, data[0]);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-
-       return 0;
-}
-
-#if 0 /* not used yet */
-static int fiji_read_smc_sram_dword(struct amdgpu_device *adev,
-                               uint32_t smc_address,
-                               uint32_t *value,
-                               uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = fiji_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               *value = RREG32(mmSMC_IND_DATA_0);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int fiji_write_smc_sram_dword(struct amdgpu_device *adev,
-                               uint32_t smc_address,
-                               uint32_t value,
-                               uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = fiji_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               WREG32(mmSMC_IND_DATA_0, value);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int fiji_smu_stop_smc(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       return 0;
-}
-#endif
-
-static enum AMDGPU_UCODE_ID fiji_convert_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case UCODE_ID_SDMA0:
-                       return AMDGPU_UCODE_ID_SDMA0;
-               case UCODE_ID_SDMA1:
-                       return AMDGPU_UCODE_ID_SDMA1;
-               case UCODE_ID_CP_CE:
-                       return AMDGPU_UCODE_ID_CP_CE;
-               case UCODE_ID_CP_PFP:
-                       return AMDGPU_UCODE_ID_CP_PFP;
-               case UCODE_ID_CP_ME:
-                       return AMDGPU_UCODE_ID_CP_ME;
-               case UCODE_ID_CP_MEC:
-               case UCODE_ID_CP_MEC_JT1:
-               case UCODE_ID_CP_MEC_JT2:
-                       return AMDGPU_UCODE_ID_CP_MEC1;
-               case UCODE_ID_RLC_G:
-                       return AMDGPU_UCODE_ID_RLC_G;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return AMDGPU_UCODE_ID_MAXIMUM;
-       }
-}
-
-static int fiji_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
-                                               uint32_t fw_type,
-                                               struct SMU_Entry *entry)
-{
-       enum AMDGPU_UCODE_ID id = fiji_convert_fw_type(fw_type);
-       struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
-       const struct gfx_firmware_header_v1_0 *header = NULL;
-       uint64_t gpu_addr;
-       uint32_t data_size;
-
-       if (ucode->fw == NULL)
-               return -EINVAL;
-       gpu_addr  = ucode->mc_addr;
-       header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
-       data_size = le32_to_cpu(header->header.ucode_size_bytes);
-
-       if ((fw_type == UCODE_ID_CP_MEC_JT1) ||
-               (fw_type == UCODE_ID_CP_MEC_JT2)) {
-               gpu_addr += le32_to_cpu(header->jt_offset) << 2;
-               data_size = le32_to_cpu(header->jt_size) << 2;
-       }
-
-       entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
-       entry->id = (uint16_t)fw_type;
-       entry->image_addr_high = upper_32_bits(gpu_addr);
-       entry->image_addr_low = lower_32_bits(gpu_addr);
-       entry->meta_data_addr_high = 0;
-       entry->meta_data_addr_low = 0;
-       entry->data_size_byte = data_size;
-       entry->num_register_entries = 0;
-
-       if (fw_type == UCODE_ID_RLC_G)
-               entry->flags = 1;
-       else
-               entry->flags = 0;
-
-       return 0;
-}
-
-static int fiji_smu_request_load_fw(struct amdgpu_device *adev)
-{
-       struct fiji_smu_private_data *private = (struct fiji_smu_private_data *)adev->smu.priv;
-       struct SMU_DRAMData_TOC *toc;
-       uint32_t fw_to_load;
-
-       WREG32_SMC(ixSOFT_REGISTERS_TABLE_28, 0);
-
-       fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_HI, private->smu_buffer_addr_high);
-       fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_LO, private->smu_buffer_addr_low);
-
-       toc = (struct SMU_DRAMData_TOC *)private->header;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       if (!adev->firmware.smu_load)
-               return 0;
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for RLC\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for CE\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for PFP\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for ME\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT2,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC_JT2\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA0\n");
-               return -EINVAL;
-       }
-
-       if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA1\n");
-               return -EINVAL;
-       }
-
-       fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
-       fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK |
-                       UCODE_ID_SDMA0_MASK |
-                       UCODE_ID_SDMA1_MASK |
-                       UCODE_ID_CP_CE_MASK |
-                       UCODE_ID_CP_ME_MASK |
-                       UCODE_ID_CP_PFP_MASK |
-                       UCODE_ID_CP_MEC_MASK;
-
-       if (fiji_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
-               DRM_ERROR("Fail to request SMU load ucode\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static uint32_t fiji_smu_get_mask_for_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case AMDGPU_UCODE_ID_SDMA0:
-                       return UCODE_ID_SDMA0_MASK;
-               case AMDGPU_UCODE_ID_SDMA1:
-                       return UCODE_ID_SDMA1_MASK;
-               case AMDGPU_UCODE_ID_CP_CE:
-                       return UCODE_ID_CP_CE_MASK;
-               case AMDGPU_UCODE_ID_CP_PFP:
-                       return UCODE_ID_CP_PFP_MASK;
-               case AMDGPU_UCODE_ID_CP_ME:
-                       return UCODE_ID_CP_ME_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC1:
-                       return UCODE_ID_CP_MEC_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC2:
-                       return UCODE_ID_CP_MEC_MASK;
-               case AMDGPU_UCODE_ID_RLC_G:
-                       return UCODE_ID_RLC_G_MASK;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return 0;
-       }
-}
-
-static int fiji_smu_check_fw_load_finish(struct amdgpu_device *adev,
-                                       uint32_t fw_type)
-{
-       uint32_t fw_mask = fiji_smu_get_mask_for_fw_type(fw_type);
-       int i;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_28) & fw_mask))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("check firmware loading failed\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int fiji_smu_start_in_protection_mode(struct amdgpu_device *adev)
-{
-       int result;
-       uint32_t val;
-       int i;
-
-       /* Assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       result = fiji_smu_upload_firmware_image(adev);
-       if (result)
-               return result;
-
-       /* Clear status */
-       WREG32_SMC(ixSMU_STATUS, 0);
-
-       /* Enable clock */
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       /* De-assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       /* Set SMU Auto Start */
-       val = RREG32_SMC(ixSMU_INPUT_DATA);
-       val = REG_SET_FIELD(val, SMU_INPUT_DATA, AUTO_START, 1);
-       WREG32_SMC(ixSMU_INPUT_DATA, val);
-
-       /* Clear firmware interrupt enable flag */
-       WREG32_SMC(ixFIRMWARE_FLAGS, 0);
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixRCU_UC_EVENTS);
-               if (REG_GET_FIELD(val, RCU_UC_EVENTS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Interrupt is not enabled by firmware\n");
-               return -EINVAL;
-       }
-
-       /* Call Test SMU message with 0x20000 offset
-        * to trigger SMU start
-        */
-       fiji_send_msg_to_smc_offset(adev);
-       DRM_INFO("[FM]try triger smu start\n");
-       /* Wait for done bit to be set */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixSMU_STATUS);
-               if (REG_GET_FIELD(val, SMU_STATUS, SMU_DONE))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Timeout for SMU start\n");
-               return -EINVAL;
-       }
-
-       /* Check pass/failed indicator */
-       val = RREG32_SMC(ixSMU_STATUS);
-       if (!REG_GET_FIELD(val, SMU_STATUS, SMU_PASS)) {
-               DRM_ERROR("SMU Firmware start failed\n");
-               return -EINVAL;
-       }
-       DRM_INFO("[FM]smu started\n");
-       /* Wait for firmware to initialize */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixFIRMWARE_FLAGS);
-               if(REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("SMU firmware initialization failed\n");
-               return -EINVAL;
-       }
-       DRM_INFO("[FM]smu initialized\n");
-
-       return 0;
-}
-
-static int fiji_smu_start_in_non_protection_mode(struct amdgpu_device *adev)
-{
-       int i, result;
-       uint32_t val;
-
-       /* wait for smc boot up */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixRCU_UC_EVENTS);
-               val = REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done);
-               if (val)
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("SMC boot sequence is not completed\n");
-               return -EINVAL;
-       }
-
-       /* Clear firmware interrupt enable flag */
-       WREG32_SMC(ixFIRMWARE_FLAGS, 0);
-
-       /* Assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       result = fiji_smu_upload_firmware_image(adev);
-       if (result)
-               return result;
-
-       /* Set smc instruct start point at 0x0 */
-       fiji_program_jump_on_start(adev);
-
-       /* Enable clock */
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       /* De-assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       /* Wait for firmware to initialize */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixFIRMWARE_FLAGS);
-               if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Timeout for SMC firmware initialization\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-int fiji_smu_start(struct amdgpu_device *adev)
-{
-       int result;
-       uint32_t val;
-
-       if (!fiji_is_smc_ram_running(adev)) {
-               val = RREG32_SMC(ixSMU_FIRMWARE);
-               if (!REG_GET_FIELD(val, SMU_FIRMWARE, SMU_MODE)) {
-                       DRM_INFO("[FM]start smu in nonprotection mode\n");
-                       result = fiji_smu_start_in_non_protection_mode(adev);
-                       if (result)
-                               return result;
-               } else {
-                       DRM_INFO("[FM]start smu in protection mode\n");
-                       result = fiji_smu_start_in_protection_mode(adev);
-                       if (result)
-                               return result;
-               }
-       }
-
-       return fiji_smu_request_load_fw(adev);
-}
-
-static const struct amdgpu_smumgr_funcs fiji_smumgr_funcs = {
-       .check_fw_load_finish = fiji_smu_check_fw_load_finish,
-       .request_smu_load_fw = NULL,
-       .request_smu_specific_fw = NULL,
-};
-
-int fiji_smu_init(struct amdgpu_device *adev)
-{
-       struct fiji_smu_private_data *private;
-       uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       uint32_t smu_internal_buffer_size = 200*4096;
-       struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
-       struct amdgpu_bo **smu_buf = &adev->smu.smu_buf;
-       uint64_t mc_addr;
-       void *toc_buf_ptr;
-       void *smu_buf_ptr;
-       int ret;
-
-       private = kzalloc(sizeof(struct fiji_smu_private_data), GFP_KERNEL);
-       if (NULL == private)
-               return -ENOMEM;
-
-       /* allocate firmware buffers */
-       if (adev->firmware.smu_load)
-               amdgpu_ucode_init_bo(adev);
-
-       adev->smu.priv = private;
-       adev->smu.fw_flags = 0;
-
-       /* Allocate FW image data structure and header buffer */
-       ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
-                              true, AMDGPU_GEM_DOMAIN_VRAM,
-                              AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                              NULL, NULL, toc_buf);
-       if (ret) {
-               DRM_ERROR("Failed to allocate memory for TOC buffer\n");
-               return -ENOMEM;
-       }
-
-       /* Allocate buffer for SMU internal buffer */
-       ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE,
-                              true, AMDGPU_GEM_DOMAIN_VRAM,
-                              AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                              NULL, NULL, smu_buf);
-       if (ret) {
-               DRM_ERROR("Failed to allocate memory for SMU internal buffer\n");
-               return -ENOMEM;
-       }
-
-       /* Retrieve GPU address for header buffer and internal buffer */
-       ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
-       if (ret) {
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to reserve the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to pin the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to map the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       amdgpu_bo_unreserve(adev->smu.toc_buf);
-       private->header_addr_low = lower_32_bits(mc_addr);
-       private->header_addr_high = upper_32_bits(mc_addr);
-       private->header = toc_buf_ptr;
-
-       ret = amdgpu_bo_reserve(adev->smu.smu_buf, false);
-       if (ret) {
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to reserve the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to pin the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to map the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       amdgpu_bo_unreserve(adev->smu.smu_buf);
-       private->smu_buffer_addr_low = lower_32_bits(mc_addr);
-       private->smu_buffer_addr_high = upper_32_bits(mc_addr);
-
-       adev->smu.smumgr_funcs = &fiji_smumgr_funcs;
-
-       return 0;
-}
-
-int fiji_smu_fini(struct amdgpu_device *adev)
-{
-       amdgpu_bo_unref(&adev->smu.toc_buf);
-       amdgpu_bo_unref(&adev->smu.smu_buf);
-       kfree(adev->smu.priv);
-       adev->smu.priv = NULL;
-       if (adev->firmware.fw_buf)
-               amdgpu_ucode_fini_bo(adev);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/fiji_smum.h b/drivers/gpu/drm/amd/amdgpu/fiji_smum.h
deleted file mode 100644 (file)
index 1cef03d..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2014 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 FIJI_SMUMGR_H
-#define FIJI_SMUMGR_H
-
-#include "fiji_ppsmc.h"
-
-int fiji_smu_init(struct amdgpu_device *adev);
-int fiji_smu_fini(struct amdgpu_device *adev);
-int fiji_smu_start(struct amdgpu_device *adev);
-
-struct fiji_smu_private_data
-{
-       uint8_t *header;
-       uint32_t smu_buffer_addr_high;
-       uint32_t smu_buffer_addr_low;
-       uint32_t header_addr_high;
-       uint32_t header_addr_low;
-};
-
-#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c
new file mode 100644 (file)
index 0000000..40abb6b
--- /dev/null
@@ -0,0 +1,3362 @@
+/*
+ * Copyright 2015 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 "amdgpu.h"
+#include "amdgpu_ih.h"
+#include "amdgpu_gfx.h"
+#include "amdgpu_ucode.h"
+#include "si/clearstate_si.h"
+#include "si/sid.h"
+
+#define GFX6_NUM_GFX_RINGS     1
+#define GFX6_NUM_COMPUTE_RINGS 2
+#define STATIC_PER_CU_PG_ENABLE                    (1 << 3)
+#define DYN_PER_CU_PG_ENABLE                       (1 << 2)
+#define RLC_SAVE_AND_RESTORE_STARTING_OFFSET 0x90
+#define RLC_CLEAR_STATE_DESCRIPTOR_OFFSET    0x3D
+
+
+static void gfx_v6_0_set_ring_funcs(struct amdgpu_device *adev);
+static void gfx_v6_0_set_irq_funcs(struct amdgpu_device *adev);
+static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev);
+
+MODULE_FIRMWARE("radeon/tahiti_pfp.bin");
+MODULE_FIRMWARE("radeon/tahiti_me.bin");
+MODULE_FIRMWARE("radeon/tahiti_ce.bin");
+MODULE_FIRMWARE("radeon/tahiti_rlc.bin");
+
+MODULE_FIRMWARE("radeon/pitcairn_pfp.bin");
+MODULE_FIRMWARE("radeon/pitcairn_me.bin");
+MODULE_FIRMWARE("radeon/pitcairn_ce.bin");
+MODULE_FIRMWARE("radeon/pitcairn_rlc.bin");
+
+MODULE_FIRMWARE("radeon/verde_pfp.bin");
+MODULE_FIRMWARE("radeon/verde_me.bin");
+MODULE_FIRMWARE("radeon/verde_ce.bin");
+MODULE_FIRMWARE("radeon/verde_rlc.bin");
+
+MODULE_FIRMWARE("radeon/oland_pfp.bin");
+MODULE_FIRMWARE("radeon/oland_me.bin");
+MODULE_FIRMWARE("radeon/oland_ce.bin");
+MODULE_FIRMWARE("radeon/oland_rlc.bin");
+
+MODULE_FIRMWARE("radeon/hainan_pfp.bin");
+MODULE_FIRMWARE("radeon/hainan_me.bin");
+MODULE_FIRMWARE("radeon/hainan_ce.bin");
+MODULE_FIRMWARE("radeon/hainan_rlc.bin");
+
+static u32 gfx_v6_0_get_csb_size(struct amdgpu_device *adev);
+static void gfx_v6_0_get_csb_buffer(struct amdgpu_device *adev, volatile u32 *buffer);
+//static void gfx_v6_0_init_cp_pg_table(struct amdgpu_device *adev);
+static void gfx_v6_0_init_pg(struct amdgpu_device *adev);
+
+
+static const u32 verde_rlc_save_restore_register_list[] =
+{
+       (0x8000 << 16) | (0x98f4 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x98f4 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0xe80 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0xe80 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x89bc >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x89bc >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x8c1c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x8c1c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x98f0 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xe7c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9148 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9148 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9150 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x897c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8d8c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac54 >> 2),
+       0X00000000,
+       0x3,
+       (0x9c00 << 16) | (0x98f8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9910 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9914 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9918 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x991c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9920 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9924 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9928 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x992c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9930 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9934 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9938 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x993c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9940 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9944 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9948 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x994c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9950 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9954 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9958 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x995c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9960 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9964 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9968 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x996c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9970 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9974 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9978 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x997c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9980 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9984 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9988 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x998c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c00 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c04 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c08 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9b7c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9b7c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0xe84 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0xe84 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x89c0 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x89c0 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x914c >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x914c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x8c20 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x8c20 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9354 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9354 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9060 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9364 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9100 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x913c >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e0 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e4 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x90e8 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e0 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e4 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x90e8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8bcc >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8b24 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88c4 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e50 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8c0c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e58 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8e5c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9508 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x950c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9494 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac0c >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac10 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xae00 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0xac08 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88d4 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88c8 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x88cc >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x89b0 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8b10 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x8a14 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9830 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9834 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9838 >> 2),
+       0x00000000,
+       (0x9c00 << 16) | (0x9a10 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8000 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8001 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8001 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8040 << 16) | (0x9874 >> 2),
+       0x00000000,
+       (0x8041 << 16) | (0x9870 >> 2),
+       0x00000000,
+       (0x8041 << 16) | (0x9874 >> 2),
+       0x00000000,
+       0x00000000
+};
+
+static int gfx_v6_0_init_microcode(struct amdgpu_device *adev)
+{
+       const char *chip_name;
+       char fw_name[30];
+       int err;
+       const struct gfx_firmware_header_v1_0 *cp_hdr;
+       const struct rlc_firmware_header_v1_0 *rlc_hdr;
+
+       DRM_DEBUG("\n");
+
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               chip_name = "tahiti";
+               break;
+       case CHIP_PITCAIRN:
+               chip_name = "pitcairn";
+               break;
+       case CHIP_VERDE:
+               chip_name = "verde";
+               break;
+       case CHIP_OLAND:
+               chip_name = "oland";
+               break;
+       case CHIP_HAINAN:
+               chip_name = "hainan";
+               break;
+       default: BUG();
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+       err = request_firmware(&adev->gfx.pfp_fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+       err = amdgpu_ucode_validate(adev->gfx.pfp_fw);
+       if (err)
+               goto out;
+       cp_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.pfp_fw->data;
+       adev->gfx.pfp_fw_version = le32_to_cpu(cp_hdr->header.ucode_version);
+       adev->gfx.pfp_feature_version = le32_to_cpu(cp_hdr->ucode_feature_version);
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+       err = request_firmware(&adev->gfx.me_fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+       err = amdgpu_ucode_validate(adev->gfx.me_fw);
+       if (err)
+               goto out;
+       cp_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.me_fw->data;
+       adev->gfx.me_fw_version = le32_to_cpu(cp_hdr->header.ucode_version);
+       adev->gfx.me_feature_version = le32_to_cpu(cp_hdr->ucode_feature_version);
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+       err = request_firmware(&adev->gfx.ce_fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+       err = amdgpu_ucode_validate(adev->gfx.ce_fw);
+       if (err)
+               goto out;
+       cp_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.ce_fw->data;
+       adev->gfx.ce_fw_version = le32_to_cpu(cp_hdr->header.ucode_version);
+       adev->gfx.ce_feature_version = le32_to_cpu(cp_hdr->ucode_feature_version);
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name);
+       err = request_firmware(&adev->gfx.rlc_fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+       err = amdgpu_ucode_validate(adev->gfx.rlc_fw);
+       rlc_hdr = (const struct rlc_firmware_header_v1_0 *)adev->gfx.rlc_fw->data;
+       adev->gfx.rlc_fw_version = le32_to_cpu(rlc_hdr->header.ucode_version);
+       adev->gfx.rlc_feature_version = le32_to_cpu(rlc_hdr->ucode_feature_version);
+
+out:
+       if (err) {
+               printk(KERN_ERR
+                      "gfx6: Failed to load firmware \"%s\"\n",
+                      fw_name);
+               release_firmware(adev->gfx.pfp_fw);
+               adev->gfx.pfp_fw = NULL;
+               release_firmware(adev->gfx.me_fw);
+               adev->gfx.me_fw = NULL;
+               release_firmware(adev->gfx.ce_fw);
+               adev->gfx.ce_fw = NULL;
+               release_firmware(adev->gfx.rlc_fw);
+               adev->gfx.rlc_fw = NULL;
+       }
+       return err;
+}
+
+static void gfx_v6_0_tiling_mode_table_init(struct amdgpu_device *adev)
+{
+       const u32 num_tile_mode_states = 32;
+       u32 reg_offset, gb_tile_moden, split_equal_to_row_size;
+
+       switch (adev->gfx.config.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;
+       }
+
+       if (adev->asic_type == CHIP_VERDE ||
+               adev->asic_type == CHIP_OLAND ||
+               adev->asic_type == CHIP_HAINAN) {
+               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(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 1: 
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 2:
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 3:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 4:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 5:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 6:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 7:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 8: 
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 9:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 10:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 11:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 12:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 13:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 14:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 15:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 16:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 17:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 21:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 22:  
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 23: 
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 24: 
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 25: 
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       adev->gfx.config.tile_mode_array[reg_offset] = gb_tile_moden;
+                       WREG32(GB_TILE_MODE0 + reg_offset, gb_tile_moden);
+               }
+       } else if ((adev->asic_type == CHIP_TAHITI) || (adev->asic_type == CHIP_PITCAIRN)) {
+               for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
+                       switch (reg_offset) {
+                       case 0:  /* non-AA compressed depth or any compressed stencil */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 1:  /* 2xAA/4xAA compressed depth only */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 2:  /* 8xAA compressed depth only */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 3:  /* 2xAA/4xAA compressed depth with stencil (for depth buffer) */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 4:  /* Maps w/ a dimension less than the 2D macro-tile dimensions (for mipmapped depth textures) */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 5:  /* Uncompressed 16bpp depth - and stencil buffer allocated with it */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 6:  /* Uncompressed 32bpp depth - and stencil buffer allocated with it */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 7:  /* Uncompressed 8bpp stencil without depth (drivers typically do not use) */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 8:  /* 1D and 1D Array Surfaces */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 9:  /* Displayable maps. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 10:  /* Display 8bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 11:  /* Display 16bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 12:  /* Display 32bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 13:  /* Thin. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 14:  /* Thin 8 bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 15:  /* Thin 16 bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 16:  /* Thin 32 bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 17:  /* Thin 64 bpp. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(split_equal_to_row_size) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       case 21:  /* 8 bpp PRT. */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 22:  /* 16 bpp PRT */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+                               break;
+                       case 23:  /* 32 bpp PRT */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 24:  /* 64 bpp PRT */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+                                                NUM_BANKS(ADDR_SURF_16_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+                               break;
+                       case 25:  /* 128 bpp PRT */
+                               gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+                                                MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+                                                PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+                                                TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) |
+                                                NUM_BANKS(ADDR_SURF_8_BANK) |
+                                                BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+                                                BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+                                                MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+                               break;
+                       default:
+                               gb_tile_moden = 0;
+                               break;
+                       }
+                       adev->gfx.config.tile_mode_array[reg_offset] = gb_tile_moden;
+                       WREG32(GB_TILE_MODE0 + reg_offset, gb_tile_moden);
+               }
+       } else{
+
+               DRM_ERROR("unknown asic: 0x%x\n", adev->asic_type);
+       }
+
+}
+
+static void gfx_v6_0_select_se_sh(struct amdgpu_device *adev, u32 se_num,
+                                 u32 sh_num, u32 instance)
+{
+       u32 data;
+
+       if (instance == 0xffffffff)
+               data = INSTANCE_BROADCAST_WRITES;
+       else
+               data = INSTANCE_INDEX(instance);
+
+       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);
+}
+
+static u32 gfx_v6_0_create_bitmask(u32 bit_width)
+{
+       return (u32)(((u64)1 << bit_width) - 1);
+}
+
+static u32 gfx_v6_0_get_rb_disabled(struct amdgpu_device *adev,
+                                   u32 max_rb_num_per_se,
+                                   u32 sh_per_se)
+{
+       u32 data, mask;
+
+       data = RREG32(CC_RB_BACKEND_DISABLE);
+       data &= BACKEND_DISABLE_MASK;
+       data |= RREG32(GC_USER_RB_BACKEND_DISABLE);
+
+       data >>= BACKEND_DISABLE_SHIFT;
+
+       mask = gfx_v6_0_create_bitmask(max_rb_num_per_se / sh_per_se);
+
+       return data & mask;
+}
+
+static void gfx_v6_0_raster_config(struct amdgpu_device *adev, u32 *rconf)
+{
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+               *rconf |= RB_XSEL2(2) | RB_XSEL | PKR_MAP(2) | PKR_YSEL(1) |
+                         SE_MAP(2) | SE_XSEL(2) | SE_YSEL(2);
+               break;
+       case CHIP_VERDE:
+               *rconf |= RB_XSEL | PKR_MAP(2) | PKR_YSEL(1);
+               break;
+       case CHIP_OLAND:
+               *rconf |= RB_YSEL;
+               break;
+       case CHIP_HAINAN:
+               *rconf |= 0x0;
+               break;
+       default:
+               DRM_ERROR("unknown asic: 0x%x\n", adev->asic_type);
+               break;
+       }
+}
+
+static void gfx_v6_0_write_harvested_raster_configs(struct amdgpu_device *adev,
+                                                   u32 raster_config, unsigned rb_mask,
+                                                   unsigned num_rb)
+{
+       unsigned sh_per_se = max_t(unsigned, adev->gfx.config.max_sh_per_se, 1);
+       unsigned num_se = max_t(unsigned, adev->gfx.config.max_shader_engines, 1);
+       unsigned rb_per_pkr = min_t(unsigned, num_rb / num_se / sh_per_se, 2);
+       unsigned rb_per_se = num_rb / num_se;
+       unsigned se_mask[4];
+       unsigned se;
+
+       se_mask[0] = ((1 << rb_per_se) - 1) & rb_mask;
+       se_mask[1] = (se_mask[0] << rb_per_se) & rb_mask;
+       se_mask[2] = (se_mask[1] << rb_per_se) & rb_mask;
+       se_mask[3] = (se_mask[2] << rb_per_se) & rb_mask;
+
+       WARN_ON(!(num_se == 1 || num_se == 2 || num_se == 4));
+       WARN_ON(!(sh_per_se == 1 || sh_per_se == 2));
+       WARN_ON(!(rb_per_pkr == 1 || rb_per_pkr == 2));
+
+       for (se = 0; se < num_se; se++) {
+               unsigned raster_config_se = raster_config;
+               unsigned pkr0_mask = ((1 << rb_per_pkr) - 1) << (se * rb_per_se);
+               unsigned pkr1_mask = pkr0_mask << rb_per_pkr;
+               int idx = (se / 2) * 2;
+
+               if ((num_se > 1) && (!se_mask[idx] || !se_mask[idx + 1])) {
+                       raster_config_se &= ~SE_MAP_MASK;
+
+                       if (!se_mask[idx]) {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_3);
+                       } else {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_0);
+                       }
+               }
+
+               pkr0_mask &= rb_mask;
+               pkr1_mask &= rb_mask;
+               if (rb_per_se > 2 && (!pkr0_mask || !pkr1_mask)) {
+                       raster_config_se &= ~PKR_MAP_MASK;
+
+                       if (!pkr0_mask) {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_3);
+                       } else {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_0);
+                       }
+               }
+
+               if (rb_per_se >= 2) {
+                       unsigned rb0_mask = 1 << (se * rb_per_se);
+                       unsigned rb1_mask = rb0_mask << 1;
+
+                       rb0_mask &= rb_mask;
+                       rb1_mask &= rb_mask;
+                       if (!rb0_mask || !rb1_mask) {
+                               raster_config_se &= ~RB_MAP_PKR0_MASK;
+
+                               if (!rb0_mask) {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_3);
+                               } else {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_0);
+                               }
+                       }
+
+                       if (rb_per_se > 2) {
+                               rb0_mask = 1 << (se * rb_per_se + rb_per_pkr);
+                               rb1_mask = rb0_mask << 1;
+                               rb0_mask &= rb_mask;
+                               rb1_mask &= rb_mask;
+                               if (!rb0_mask || !rb1_mask) {
+                                       raster_config_se &= ~RB_MAP_PKR1_MASK;
+
+                                       if (!rb0_mask) {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_3);
+                                       } else {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_0);
+                                       }
+                               }
+                       }
+               }
+
+               /* GRBM_GFX_INDEX has a different offset on SI */
+               gfx_v6_0_select_se_sh(adev, se, 0xffffffff, 0xffffffff);
+               WREG32(PA_SC_RASTER_CONFIG, raster_config_se);
+       }
+
+       /* GRBM_GFX_INDEX has a different offset on SI */
+       gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+}
+
+static void gfx_v6_0_setup_rb(struct amdgpu_device *adev,
+                             u32 se_num, u32 sh_per_se,
+                             u32 max_rb_num_per_se)
+{
+       int i, j;
+       u32 data, mask;
+       u32 disabled_rbs = 0;
+       u32 enabled_rbs = 0;
+       unsigned num_rb_pipes;
+
+       mutex_lock(&adev->grbm_idx_mutex);
+       for (i = 0; i < se_num; i++) {
+               for (j = 0; j < sh_per_se; j++) {
+                       gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff);
+                       data = gfx_v6_0_get_rb_disabled(adev, max_rb_num_per_se, sh_per_se);
+                       disabled_rbs |= data << ((i * sh_per_se + j) * TAHITI_RB_BITMAP_WIDTH_PER_SH);
+               }
+       }
+       gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+       mutex_unlock(&adev->grbm_idx_mutex);
+
+       mask = 1;
+       for (i = 0; i < max_rb_num_per_se * se_num; i++) {
+               if (!(disabled_rbs & mask))
+                       enabled_rbs |= mask;
+               mask <<= 1;
+       }
+
+       adev->gfx.config.backend_enable_mask = enabled_rbs;
+       adev->gfx.config.num_rbs = hweight32(enabled_rbs);
+
+       num_rb_pipes = min_t(unsigned, adev->gfx.config.max_backends_per_se *
+                            adev->gfx.config.max_shader_engines, 16);
+
+       mutex_lock(&adev->grbm_idx_mutex);
+       for (i = 0; i < se_num; i++) {
+               gfx_v6_0_select_se_sh(adev, i, 0xffffffff, 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;
+               }
+               gfx_v6_0_raster_config(adev, &data);
+
+               if (!adev->gfx.config.backend_enable_mask ||
+                               adev->gfx.config.num_rbs >= num_rb_pipes)
+                       WREG32(PA_SC_RASTER_CONFIG, data);
+               else
+                       gfx_v6_0_write_harvested_raster_configs(adev, data,
+                                                               adev->gfx.config.backend_enable_mask,
+                                                               num_rb_pipes);
+       }
+       gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+       mutex_unlock(&adev->grbm_idx_mutex);
+}
+/*
+static void gmc_v6_0_init_compute_vmid(struct amdgpu_device *adev)
+{
+}
+*/
+
+static u32 gfx_v6_0_get_cu_enabled(struct amdgpu_device *adev, u32 cu_per_sh)
+{
+       u32 data, mask;
+
+       data = RREG32(CC_GC_SHADER_ARRAY_CONFIG);
+       data &= INACTIVE_CUS_MASK;
+       data |= RREG32(GC_USER_SHADER_ARRAY_CONFIG);
+
+       data >>= INACTIVE_CUS_SHIFT;
+
+       mask = gfx_v6_0_create_bitmask(cu_per_sh);
+
+       return ~data & mask;
+}
+
+
+static void gfx_v6_0_setup_spi(struct amdgpu_device *adev,
+                        u32 se_num, u32 sh_per_se,
+                        u32 cu_per_sh)
+{
+       int i, j, k;
+       u32 data, mask;
+       u32 active_cu = 0;
+
+       mutex_lock(&adev->grbm_idx_mutex);
+       for (i = 0; i < se_num; i++) {
+               for (j = 0; j < sh_per_se; j++) {
+                       gfx_v6_0_select_se_sh(adev, i, j, 0xffffffff);
+                       data = RREG32(SPI_STATIC_THREAD_MGMT_3);
+                       active_cu = gfx_v6_0_get_cu_enabled(adev, cu_per_sh);
+
+                       mask = 1;
+                       for (k = 0; k < 16; k++) {
+                               mask <<= k;
+                               if (active_cu & mask) {
+                                       data &= ~mask;
+                                       WREG32(SPI_STATIC_THREAD_MGMT_3, data);
+                                       break;
+                               }
+                       }
+               }
+       }
+       gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+       mutex_unlock(&adev->grbm_idx_mutex);
+}
+
+static void gfx_v6_0_gpu_init(struct amdgpu_device *adev)
+{
+       u32 gb_addr_config = 0;
+       u32 mc_shared_chmap, mc_arb_ramcfg;
+       u32 sx_debug_1;
+       u32 hdp_host_path_cntl;
+       u32 tmp;
+
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               adev->gfx.config.max_shader_engines = 2;
+               adev->gfx.config.max_tile_pipes = 12;
+               adev->gfx.config.max_cu_per_sh = 8;
+               adev->gfx.config.max_sh_per_se = 2;
+               adev->gfx.config.max_backends_per_se = 4;
+               adev->gfx.config.max_texture_channel_caches = 12;
+               adev->gfx.config.max_gprs = 256;
+               adev->gfx.config.max_gs_threads = 32;
+               adev->gfx.config.max_hw_contexts = 8;
+
+               adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
+               adev->gfx.config.sc_prim_fifo_size_backend = 0x100;
+               adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
+               adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = TAHITI_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       case CHIP_PITCAIRN:
+               adev->gfx.config.max_shader_engines = 2;
+               adev->gfx.config.max_tile_pipes = 8;
+               adev->gfx.config.max_cu_per_sh = 5;
+               adev->gfx.config.max_sh_per_se = 2;
+               adev->gfx.config.max_backends_per_se = 4;
+               adev->gfx.config.max_texture_channel_caches = 8;
+               adev->gfx.config.max_gprs = 256;
+               adev->gfx.config.max_gs_threads = 32;
+               adev->gfx.config.max_hw_contexts = 8;
+
+               adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
+               adev->gfx.config.sc_prim_fifo_size_backend = 0x100;
+               adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
+               adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = TAHITI_GB_ADDR_CONFIG_GOLDEN;
+               break;
+
+       case CHIP_VERDE:
+               adev->gfx.config.max_shader_engines = 1;
+               adev->gfx.config.max_tile_pipes = 4;
+               adev->gfx.config.max_cu_per_sh = 5;
+               adev->gfx.config.max_sh_per_se = 2;
+               adev->gfx.config.max_backends_per_se = 4;
+               adev->gfx.config.max_texture_channel_caches = 4;
+               adev->gfx.config.max_gprs = 256;
+               adev->gfx.config.max_gs_threads = 32;
+               adev->gfx.config.max_hw_contexts = 8;
+
+               adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
+               adev->gfx.config.sc_prim_fifo_size_backend = 0x40;
+               adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
+               adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = VERDE_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       case CHIP_OLAND:
+               adev->gfx.config.max_shader_engines = 1;
+               adev->gfx.config.max_tile_pipes = 4;
+               adev->gfx.config.max_cu_per_sh = 6;
+               adev->gfx.config.max_sh_per_se = 1;
+               adev->gfx.config.max_backends_per_se = 2;
+               adev->gfx.config.max_texture_channel_caches = 4;
+               adev->gfx.config.max_gprs = 256;
+               adev->gfx.config.max_gs_threads = 16;
+               adev->gfx.config.max_hw_contexts = 8;
+
+               adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
+               adev->gfx.config.sc_prim_fifo_size_backend = 0x40;
+               adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
+               adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = VERDE_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       case CHIP_HAINAN:
+               adev->gfx.config.max_shader_engines = 1;
+               adev->gfx.config.max_tile_pipes = 4;
+               adev->gfx.config.max_cu_per_sh = 5;
+               adev->gfx.config.max_sh_per_se = 1;
+               adev->gfx.config.max_backends_per_se = 1;
+               adev->gfx.config.max_texture_channel_caches = 2;
+               adev->gfx.config.max_gprs = 256;
+               adev->gfx.config.max_gs_threads = 16;
+               adev->gfx.config.max_hw_contexts = 8;
+
+               adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
+               adev->gfx.config.sc_prim_fifo_size_backend = 0x40;
+               adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
+               adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
+               gb_addr_config = HAINAN_GB_ADDR_CONFIG_GOLDEN;
+               break;
+       default:
+               BUG();
+               break;
+       }
+
+       WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+       WREG32(SRBM_INT_CNTL, 1);
+       WREG32(SRBM_INT_ACK, 1);
+
+       WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN);
+
+       mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
+       mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
+
+       adev->gfx.config.num_tile_pipes = adev->gfx.config.max_tile_pipes;
+       adev->gfx.config.mem_max_burst_length_bytes = 256;
+       tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT;
+       adev->gfx.config.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024;
+       if (adev->gfx.config.mem_row_size_in_kb > 4)
+               adev->gfx.config.mem_row_size_in_kb = 4;
+       adev->gfx.config.shader_engine_tile_size = 32;
+       adev->gfx.config.num_gpus = 1;
+       adev->gfx.config.multi_gpu_tile_size = 64;
+
+       gb_addr_config &= ~ROW_SIZE_MASK;
+       switch (adev->gfx.config.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;
+       }
+       adev->gfx.config.gb_addr_config = gb_addr_config;
+
+       WREG32(GB_ADDR_CONFIG, gb_addr_config);
+       WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
+       WREG32(DMIF_ADDR_CALC, gb_addr_config);
+       WREG32(HDP_ADDR_CONFIG, gb_addr_config);
+       WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config);
+       WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config);
+#if 0
+       if (adev->has_uvd) {
+               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);
+       }
+#endif
+       gfx_v6_0_tiling_mode_table_init(adev);
+
+       gfx_v6_0_setup_rb(adev, adev->gfx.config.max_shader_engines,
+                   adev->gfx.config.max_sh_per_se,
+                   adev->gfx.config.max_backends_per_se);
+
+       gfx_v6_0_setup_spi(adev, adev->gfx.config.max_shader_engines,
+                    adev->gfx.config.max_sh_per_se,
+                    adev->gfx.config.max_cu_per_sh);
+
+       gfx_v6_0_get_cu_info(adev);
+
+       WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) |
+                                    ROQ_IB2_START(0x2b)));
+       WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60));
+
+       sx_debug_1 = RREG32(SX_DEBUG_1);
+       WREG32(SX_DEBUG_1, sx_debug_1);
+
+       WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4));
+
+       WREG32(PA_SC_FIFO_SIZE, (SC_FRONTEND_PRIM_FIFO_SIZE(adev->gfx.config.sc_prim_fifo_size_frontend) |
+                                SC_BACKEND_PRIM_FIFO_SIZE(adev->gfx.config.sc_prim_fifo_size_backend) |
+                                SC_HIZ_TILE_FIFO_SIZE(adev->gfx.config.sc_hiz_tile_fifo_size) |
+                                SC_EARLYZ_TILE_FIFO_SIZE(adev->gfx.config.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);
+
+       WREG32(CB_PERFCOUNTER0_SELECT0, 0);
+       WREG32(CB_PERFCOUNTER0_SELECT1, 0);
+       WREG32(CB_PERFCOUNTER1_SELECT0, 0);
+       WREG32(CB_PERFCOUNTER1_SELECT1, 0);
+       WREG32(CB_PERFCOUNTER2_SELECT0, 0);
+       WREG32(CB_PERFCOUNTER2_SELECT1, 0);
+       WREG32(CB_PERFCOUNTER3_SELECT0, 0);
+       WREG32(CB_PERFCOUNTER3_SELECT1, 0);
+
+       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));
+
+       udelay(50);
+}
+
+
+static void gfx_v6_0_scratch_init(struct amdgpu_device *adev)
+{
+       int i;
+
+       adev->gfx.scratch.num_reg = 7;
+       adev->gfx.scratch.reg_base = SCRATCH_REG0;
+       for (i = 0; i < adev->gfx.scratch.num_reg; i++) {
+               adev->gfx.scratch.free[i] = true;
+               adev->gfx.scratch.reg[i] = adev->gfx.scratch.reg_base + i;
+       }
+}
+
+static int gfx_v6_0_ring_test_ring(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+       uint32_t scratch;
+       uint32_t tmp = 0;
+       unsigned i;
+       int r;
+
+       r = amdgpu_gfx_scratch_get(adev, &scratch);
+       if (r) {
+               DRM_ERROR("amdgpu: cp failed to get scratch reg (%d).\n", r);
+               return r;
+       }
+       WREG32(scratch, 0xCAFEDEAD);
+
+       r = amdgpu_ring_alloc(ring, 3);
+       if (r) {
+               DRM_ERROR("amdgpu: cp failed to lock ring %d (%d).\n", ring->idx, r);
+               amdgpu_gfx_scratch_free(adev, scratch);
+               return r;
+       }
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+       amdgpu_ring_write(ring, (scratch - PACKET3_SET_CONFIG_REG_START));
+       amdgpu_ring_write(ring, 0xDEADBEEF);
+       amdgpu_ring_commit(ring);
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               tmp = RREG32(scratch);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+       if (i < adev->usec_timeout) {
+               DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
+       } else {
+               DRM_ERROR("amdgpu: ring %d test failed (scratch(0x%04X)=0x%08X)\n",
+                         ring->idx, scratch, tmp);
+               r = -EINVAL;
+       }
+       amdgpu_gfx_scratch_free(adev, scratch);
+       return r;
+}
+
+static void gfx_v6_0_ring_emit_hdp_flush(struct amdgpu_ring *ring)
+{
+       /* flush hdp cache */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
+                                WRITE_DATA_DST_SEL(0)));
+       amdgpu_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL);
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 0x1);
+}
+
+/**
+ * gfx_v6_0_ring_emit_hdp_invalidate - emit an hdp invalidate on the cp
+ *
+ * @adev: amdgpu_device pointer
+ * @ridx: amdgpu ring index
+ *
+ * Emits an hdp invalidate on the cp.
+ */
+static void gfx_v6_0_ring_emit_hdp_invalidate(struct amdgpu_ring *ring)
+{
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
+                                WRITE_DATA_DST_SEL(0)));
+       amdgpu_ring_write(ring, HDP_DEBUG0);
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 0x1);
+}
+
+static void gfx_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
+                                    u64 seq, unsigned flags)
+{
+       bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
+       bool int_sel = flags & AMDGPU_FENCE_FLAG_INT;
+       /* flush read cache over gart */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+       amdgpu_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START));
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3));
+       amdgpu_ring_write(ring, PACKET3_TCL1_ACTION_ENA |
+                         PACKET3_TC_ACTION_ENA |
+                         PACKET3_SH_KCACHE_ACTION_ENA |
+                         PACKET3_SH_ICACHE_ACTION_ENA);
+       amdgpu_ring_write(ring, 0xFFFFFFFF);
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 10); /* poll interval */
+       /* EVENT_WRITE_EOP - flush caches, send int */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
+       amdgpu_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5));
+       amdgpu_ring_write(ring, addr & 0xfffffffc);
+       amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xffff) |
+                               DATA_SEL(write64bit ? 2 : 1) | INT_SEL(int_sel ? 2 : 0));
+       amdgpu_ring_write(ring, lower_32_bits(seq));
+       amdgpu_ring_write(ring, upper_32_bits(seq));
+}
+
+static void gfx_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
+                                 struct amdgpu_ib *ib,
+                                 unsigned vm_id, bool ctx_switch)
+{
+       u32 header, control = 0;
+
+       /* insert SWITCH_BUFFER packet before first IB in the ring frame */
+       if (ctx_switch) {
+               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               amdgpu_ring_write(ring, 0);
+       }
+
+       if (ib->flags & AMDGPU_IB_FLAG_CE)
+               header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
+       else
+               header = PACKET3(PACKET3_INDIRECT_BUFFER, 2);
+
+       control |= ib->length_dw | (vm_id << 24);
+
+       amdgpu_ring_write(ring, header);
+       amdgpu_ring_write(ring,
+#ifdef __BIG_ENDIAN
+                         (2 << 0) |
+#endif
+                         (ib->gpu_addr & 0xFFFFFFFC));
+       amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF);
+       amdgpu_ring_write(ring, control);
+}
+
+/**
+ * gfx_v6_0_ring_test_ib - basic ring IB test
+ *
+ * @ring: amdgpu_ring structure holding ring information
+ *
+ * Allocate an IB and execute it on the gfx ring (SI).
+ * Provides a basic gfx ring test to verify that IBs are working.
+ * Returns 0 on success, error on failure.
+ */
+static int gfx_v6_0_ring_test_ib(struct amdgpu_ring *ring, long timeout)
+{
+       struct amdgpu_device *adev = ring->adev;
+       struct amdgpu_ib ib;
+       struct fence *f = NULL;
+       uint32_t scratch;
+       uint32_t tmp = 0;
+       long r;
+
+       r = amdgpu_gfx_scratch_get(adev, &scratch);
+       if (r) {
+               DRM_ERROR("amdgpu: failed to get scratch reg (%ld).\n", r);
+               return r;
+       }
+       WREG32(scratch, 0xCAFEDEAD);
+       memset(&ib, 0, sizeof(ib));
+       r = amdgpu_ib_get(adev, NULL, 256, &ib);
+       if (r) {
+               DRM_ERROR("amdgpu: failed to get ib (%ld).\n", r);
+               goto err1;
+       }
+       ib.ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1);
+       ib.ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_START));
+       ib.ptr[2] = 0xDEADBEEF;
+       ib.length_dw = 3;
+
+       r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f);
+       if (r)
+               goto err2;
+
+       r = fence_wait_timeout(f, false, timeout);
+       if (r == 0) {
+               DRM_ERROR("amdgpu: IB test timed out\n");
+               r = -ETIMEDOUT;
+               goto err2;
+       } else if (r < 0) {
+               DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r);
+               goto err2;
+       }
+       tmp = RREG32(scratch);
+       if (tmp == 0xDEADBEEF) {
+               DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
+               r = 0;
+       } else {
+               DRM_ERROR("amdgpu: ib test failed (scratch(0x%04X)=0x%08X)\n",
+                         scratch, tmp);
+               r = -EINVAL;
+       }
+
+err2:
+       amdgpu_ib_free(adev, &ib, NULL);
+       fence_put(f);
+err1:
+       amdgpu_gfx_scratch_free(adev, scratch);
+       return r;
+}
+
+static void gfx_v6_0_cp_gfx_enable(struct amdgpu_device *adev, bool enable)
+{
+       int i;
+       if (enable)
+               WREG32(CP_ME_CNTL, 0);
+       else {
+               WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT));
+               WREG32(SCRATCH_UMSK, 0);
+               for (i = 0; i < adev->gfx.num_gfx_rings; i++)
+                       adev->gfx.gfx_ring[i].ready = false;
+               for (i = 0; i < adev->gfx.num_compute_rings; i++)
+                       adev->gfx.compute_ring[i].ready = false;
+       }
+       udelay(50);
+}
+
+static int gfx_v6_0_cp_gfx_load_microcode(struct amdgpu_device *adev)
+{
+       unsigned i;
+       const struct gfx_firmware_header_v1_0 *pfp_hdr;
+       const struct gfx_firmware_header_v1_0 *ce_hdr;
+       const struct gfx_firmware_header_v1_0 *me_hdr;
+       const __le32 *fw_data;
+       u32 fw_size;
+
+       if (!adev->gfx.me_fw || !adev->gfx.pfp_fw || !adev->gfx.ce_fw)
+               return -EINVAL;
+
+       gfx_v6_0_cp_gfx_enable(adev, false);
+       pfp_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.pfp_fw->data;
+       ce_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.ce_fw->data;
+       me_hdr = (const struct gfx_firmware_header_v1_0 *)adev->gfx.me_fw->data;
+
+       amdgpu_ucode_print_gfx_hdr(&pfp_hdr->header);
+       amdgpu_ucode_print_gfx_hdr(&ce_hdr->header);
+       amdgpu_ucode_print_gfx_hdr(&me_hdr->header);
+
+       /* PFP */
+       fw_data = (const __le32 *)
+               (adev->gfx.pfp_fw->data + le32_to_cpu(pfp_hdr->header.ucode_array_offset_bytes));
+       fw_size = le32_to_cpu(pfp_hdr->header.ucode_size_bytes) / 4;
+       WREG32(CP_PFP_UCODE_ADDR, 0);
+       for (i = 0; i < fw_size; i++)
+               WREG32(CP_PFP_UCODE_DATA, le32_to_cpup(fw_data++));
+       WREG32(CP_PFP_UCODE_ADDR, 0);
+
+       /* CE */
+       fw_data = (const __le32 *)
+               (adev->gfx.ce_fw->data + le32_to_cpu(ce_hdr->header.ucode_array_offset_bytes));
+       fw_size = le32_to_cpu(ce_hdr->header.ucode_size_bytes) / 4;
+       WREG32(CP_CE_UCODE_ADDR, 0);
+       for (i = 0; i < fw_size; i++)
+               WREG32(CP_CE_UCODE_DATA, le32_to_cpup(fw_data++));
+       WREG32(CP_CE_UCODE_ADDR, 0);
+
+       /* ME */
+       fw_data = (const __be32 *)
+               (adev->gfx.me_fw->data + le32_to_cpu(me_hdr->header.ucode_array_offset_bytes));
+       fw_size = le32_to_cpu(me_hdr->header.ucode_size_bytes) / 4;
+       WREG32(CP_ME_RAM_WADDR, 0);
+       for (i = 0; i < fw_size; i++)
+               WREG32(CP_ME_RAM_DATA, le32_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;
+}
+
+static int gfx_v6_0_cp_gfx_start(struct amdgpu_device *adev)
+{
+       const struct cs_section_def *sect = NULL;
+       const struct cs_extent_def *ext = NULL;
+       struct amdgpu_ring *ring = &adev->gfx.gfx_ring[0];
+       int r, i;
+
+       r = amdgpu_ring_alloc(ring, 7 + 4);
+       if (r) {
+               DRM_ERROR("amdgpu: cp failed to lock ring (%d).\n", r);
+               return r;
+       }
+       amdgpu_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5));
+       amdgpu_ring_write(ring, 0x1);
+       amdgpu_ring_write(ring, 0x0);
+       amdgpu_ring_write(ring, adev->gfx.config.max_hw_contexts - 1);
+       amdgpu_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1));
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 0);
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
+       amdgpu_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
+       amdgpu_ring_write(ring, 0xc000);
+       amdgpu_ring_write(ring, 0xe000);
+       amdgpu_ring_commit(ring);
+
+       gfx_v6_0_cp_gfx_enable(adev, true);
+
+       r = amdgpu_ring_alloc(ring, gfx_v6_0_get_csb_size(adev) + 10);
+       if (r) {
+               DRM_ERROR("amdgpu: cp failed to lock ring (%d).\n", r);
+               return r;
+       }
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       amdgpu_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
+
+       for (sect = adev->gfx.rlc.cs_data; sect->section != NULL; ++sect) {
+               for (ext = sect->section; ext->extent != NULL; ++ext) {
+                       if (sect->id == SECT_CONTEXT) {
+                               amdgpu_ring_write(ring,
+                                                 PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count));
+                               amdgpu_ring_write(ring, ext->reg_index - PACKET3_SET_CONTEXT_REG_START);
+                               for (i = 0; i < ext->reg_count; i++)
+                                       amdgpu_ring_write(ring, ext->extent[i]);
+                       }
+               }
+       }
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       amdgpu_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE);
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0));
+       amdgpu_ring_write(ring, 0);
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+       amdgpu_ring_write(ring, 0x00000316);
+       amdgpu_ring_write(ring, 0x0000000e);
+       amdgpu_ring_write(ring, 0x00000010);
+
+       amdgpu_ring_commit(ring);
+
+       return 0;
+}
+
+static int gfx_v6_0_cp_gfx_resume(struct amdgpu_device *adev)
+{
+       struct amdgpu_ring *ring;
+       u32 tmp;
+       u32 rb_bufsz;
+       int r;
+       u64 rptr_addr;
+
+       WREG32(CP_SEM_WAIT_TIMER, 0x0);
+       WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0);
+
+       /* Set the write pointer delay */
+       WREG32(CP_RB_WPTR_DELAY, 0);
+
+       WREG32(CP_DEBUG, 0);
+       WREG32(SCRATCH_ADDR, 0);
+
+       /* ring 0 - compute and gfx */
+       /* Set ring buffer size */
+       ring = &adev->gfx.gfx_ring[0];
+       rb_bufsz = order_base_2(ring->ring_size / 8);
+       tmp = (order_base_2(AMDGPU_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 whether it's enabled or not */
+       rptr_addr = adev->wb.gpu_addr + (ring->rptr_offs * 4);
+       WREG32(CP_RB0_RPTR_ADDR, lower_32_bits(rptr_addr));
+       WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
+
+       WREG32(SCRATCH_UMSK, 0);
+
+       mdelay(1);
+       WREG32(CP_RB0_CNTL, tmp);
+
+       WREG32(CP_RB0_BASE, ring->gpu_addr >> 8);
+
+       /* start the rings */
+       gfx_v6_0_cp_gfx_start(adev);
+       ring->ready = true;
+       r = amdgpu_ring_test_ring(ring);
+       if (r) {
+               ring->ready = false;
+               return r;
+       }
+
+       return 0;
+}
+
+static u32 gfx_v6_0_ring_get_rptr(struct amdgpu_ring *ring)
+{
+       return ring->adev->wb.wb[ring->rptr_offs];
+}
+
+static u32 gfx_v6_0_ring_get_wptr(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+
+       if (ring == &adev->gfx.gfx_ring[0])
+               return RREG32(CP_RB0_WPTR);
+       else if (ring == &adev->gfx.compute_ring[0])
+               return RREG32(CP_RB1_WPTR);
+       else if (ring == &adev->gfx.compute_ring[1])
+               return RREG32(CP_RB2_WPTR);
+       else
+               BUG();
+}
+
+static void gfx_v6_0_ring_set_wptr_gfx(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+
+       WREG32(CP_RB0_WPTR, ring->wptr);
+       (void)RREG32(CP_RB0_WPTR);
+}
+
+static void gfx_v6_0_ring_set_wptr_compute(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+
+       if (ring == &adev->gfx.compute_ring[0]) {
+               WREG32(CP_RB1_WPTR, ring->wptr);
+               (void)RREG32(CP_RB1_WPTR);
+       } else if (ring == &adev->gfx.compute_ring[1]) {
+               WREG32(CP_RB2_WPTR, ring->wptr);
+               (void)RREG32(CP_RB2_WPTR);
+       } else {
+               BUG();
+       }
+
+}
+
+static int gfx_v6_0_cp_compute_resume(struct amdgpu_device *adev)
+{
+       struct amdgpu_ring *ring;
+       u32 tmp;
+       u32 rb_bufsz;
+       int r;
+       u64 rptr_addr;
+
+       /* ring1  - compute only */
+       /* Set ring buffer size */
+
+       ring = &adev->gfx.compute_ring[0];
+       rb_bufsz = order_base_2(ring->ring_size / 8);
+       tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+       tmp |= BUF_SWAP_32BIT;
+#endif
+       WREG32(CP_RB1_CNTL, tmp);
+
+       WREG32(CP_RB1_CNTL, tmp | RB_RPTR_WR_ENA);
+       ring->wptr = 0;
+       WREG32(CP_RB1_WPTR, ring->wptr);
+
+       rptr_addr = adev->wb.gpu_addr + (ring->rptr_offs * 4);
+       WREG32(CP_RB1_RPTR_ADDR, lower_32_bits(rptr_addr));
+       WREG32(CP_RB1_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
+
+       mdelay(1);
+       WREG32(CP_RB1_CNTL, tmp);
+       WREG32(CP_RB1_BASE, ring->gpu_addr >> 8);
+
+       ring = &adev->gfx.compute_ring[1];
+       rb_bufsz = order_base_2(ring->ring_size / 8);
+       tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+       tmp |= BUF_SWAP_32BIT;
+#endif
+       WREG32(CP_RB2_CNTL, tmp);
+
+       WREG32(CP_RB2_CNTL, tmp | RB_RPTR_WR_ENA);
+       ring->wptr = 0;
+       WREG32(CP_RB2_WPTR, ring->wptr);
+       rptr_addr = adev->wb.gpu_addr + (ring->rptr_offs * 4);
+       WREG32(CP_RB2_RPTR_ADDR, lower_32_bits(rptr_addr));
+       WREG32(CP_RB2_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
+
+       mdelay(1);
+       WREG32(CP_RB2_CNTL, tmp);
+       WREG32(CP_RB2_BASE, ring->gpu_addr >> 8);
+
+       adev->gfx.compute_ring[0].ready = true;
+       adev->gfx.compute_ring[1].ready = true;
+
+       r = amdgpu_ring_test_ring(&adev->gfx.compute_ring[0]);
+       if (r) {
+               adev->gfx.compute_ring[0].ready = false;
+               return r;
+       }
+
+       r = amdgpu_ring_test_ring(&adev->gfx.compute_ring[1]);
+       if (r) {
+               adev->gfx.compute_ring[1].ready = false;
+               return r;
+       }
+
+       return 0;
+}
+
+static void gfx_v6_0_cp_enable(struct amdgpu_device *adev, bool enable)
+{
+       gfx_v6_0_cp_gfx_enable(adev, enable);
+}
+
+static int gfx_v6_0_cp_load_microcode(struct amdgpu_device *adev)
+{
+       return gfx_v6_0_cp_gfx_load_microcode(adev);
+}
+
+static void gfx_v6_0_enable_gui_idle_interrupt(struct amdgpu_device *adev,
+                                              bool enable)
+{      
+       u32 tmp = RREG32(CP_INT_CNTL_RING0);
+       u32 mask;
+       int i;
+
+       if (enable)
+               tmp |= (CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       else
+               tmp &= ~(CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+       WREG32(CP_INT_CNTL_RING0, tmp);
+
+       if (!enable) {
+               /* read a gfx register */
+               tmp = RREG32(DB_DEPTH_INFO);
+
+               mask = RLC_BUSY_STATUS | GFX_POWER_STATUS | GFX_CLOCK_STATUS | GFX_LS_STATUS;
+               for (i = 0; i < adev->usec_timeout; i++) {
+                       if ((RREG32(RLC_STAT) & mask) == (GFX_CLOCK_STATUS | GFX_POWER_STATUS))
+                               break;
+                       udelay(1);
+               }
+       }
+}
+
+static int gfx_v6_0_cp_resume(struct amdgpu_device *adev)
+{
+       int r;
+
+       gfx_v6_0_enable_gui_idle_interrupt(adev, false);
+
+       r = gfx_v6_0_cp_load_microcode(adev);
+       if (r)
+               return r;
+
+       r = gfx_v6_0_cp_gfx_resume(adev);
+       if (r)
+               return r;
+       r = gfx_v6_0_cp_compute_resume(adev);
+       if (r)
+               return r;
+
+       gfx_v6_0_enable_gui_idle_interrupt(adev, true);
+
+       return 0;
+}
+
+static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
+{
+       int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX);
+       uint32_t seq = ring->fence_drv.sync_seq;
+       uint64_t addr = ring->fence_drv.gpu_addr;
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5));
+       amdgpu_ring_write(ring, (WAIT_REG_MEM_MEM_SPACE(1) | /* memory */
+                                WAIT_REG_MEM_FUNCTION(3) | /* equal */
+                                WAIT_REG_MEM_ENGINE(usepfp)));   /* pfp or me */
+       amdgpu_ring_write(ring, addr & 0xfffffffc);
+       amdgpu_ring_write(ring, upper_32_bits(addr) & 0xffffffff);
+       amdgpu_ring_write(ring, seq);
+       amdgpu_ring_write(ring, 0xffffffff);
+       amdgpu_ring_write(ring, 4); /* poll interval */
+
+       if (usepfp) {
+               /* synce CE with ME to prevent CE fetch CEIB before context switch done */
+               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               amdgpu_ring_write(ring, 0);
+               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               amdgpu_ring_write(ring, 0);
+       }
+}
+
+static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
+                                       unsigned vm_id, uint64_t pd_addr)
+{
+       int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX);
+
+       /* write new base address */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
+                                WRITE_DATA_DST_SEL(0)));
+       if (vm_id < 8) {
+               amdgpu_ring_write(ring, (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + vm_id ));
+       } else {
+               amdgpu_ring_write(ring, (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + (vm_id - 8)));
+       }
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, pd_addr >> 12);
+
+       /* bits 0-15 are the VM contexts0-15 */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
+       amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(1) |
+                                WRITE_DATA_DST_SEL(0)));
+       amdgpu_ring_write(ring, VM_INVALIDATE_REQUEST);
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 1 << vm_id);
+
+       /* wait for the invalidate to complete */
+       amdgpu_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5));
+       amdgpu_ring_write(ring, (WAIT_REG_MEM_FUNCTION(0) |  /* always */
+                                WAIT_REG_MEM_ENGINE(0))); /* me */
+       amdgpu_ring_write(ring, VM_INVALIDATE_REQUEST);
+       amdgpu_ring_write(ring, 0);
+       amdgpu_ring_write(ring, 0); /* ref */
+       amdgpu_ring_write(ring, 0); /* mask */
+       amdgpu_ring_write(ring, 0x20); /* poll interval */
+
+       if (usepfp) {
+               /* sync PFP to ME, otherwise we might get invalid PFP reads */
+               amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
+               amdgpu_ring_write(ring, 0x0);
+
+               /* synce CE with ME to prevent CE fetch CEIB before context switch done */
+               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               amdgpu_ring_write(ring, 0);
+               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+               amdgpu_ring_write(ring, 0);
+       }
+}
+
+
+static void gfx_v6_0_rlc_fini(struct amdgpu_device *adev)
+{
+       int r;
+
+       if (adev->gfx.rlc.save_restore_obj) {
+               r = amdgpu_bo_reserve(adev->gfx.rlc.save_restore_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(adev->dev, "(%d) reserve RLC sr bo failed\n", r);
+               amdgpu_bo_unpin(adev->gfx.rlc.save_restore_obj);
+               amdgpu_bo_unreserve(adev->gfx.rlc.save_restore_obj);
+
+               amdgpu_bo_unref(&adev->gfx.rlc.save_restore_obj);
+               adev->gfx.rlc.save_restore_obj = NULL;
+       }
+
+       if (adev->gfx.rlc.clear_state_obj) {
+               r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(adev->dev, "(%d) reserve RLC c bo failed\n", r);
+               amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
+               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
+
+               amdgpu_bo_unref(&adev->gfx.rlc.clear_state_obj);
+               adev->gfx.rlc.clear_state_obj = NULL;
+       }
+
+       if (adev->gfx.rlc.cp_table_obj) {
+               r = amdgpu_bo_reserve(adev->gfx.rlc.cp_table_obj, false);
+               if (unlikely(r != 0))
+                       dev_warn(adev->dev, "(%d) reserve RLC cp table bo failed\n", r);
+               amdgpu_bo_unpin(adev->gfx.rlc.cp_table_obj);
+               amdgpu_bo_unreserve(adev->gfx.rlc.cp_table_obj);
+
+               amdgpu_bo_unref(&adev->gfx.rlc.cp_table_obj);
+               adev->gfx.rlc.cp_table_obj = NULL;
+       }
+}
+
+static int gfx_v6_0_rlc_init(struct amdgpu_device *adev)
+{
+       const u32 *src_ptr;
+       volatile u32 *dst_ptr;
+       u32 dws, i;
+       u64 reg_list_mc_addr;
+       const struct cs_section_def *cs_data;
+       int r;
+
+       adev->gfx.rlc.reg_list = verde_rlc_save_restore_register_list;
+       adev->gfx.rlc.reg_list_size =
+                       (u32)ARRAY_SIZE(verde_rlc_save_restore_register_list);
+
+       adev->gfx.rlc.cs_data = si_cs_data;
+       src_ptr = adev->gfx.rlc.reg_list;
+       dws = adev->gfx.rlc.reg_list_size;
+       cs_data = adev->gfx.rlc.cs_data;
+
+       if (src_ptr) {
+               /* save restore block */
+               if (adev->gfx.rlc.save_restore_obj == NULL) {
+
+                       r = amdgpu_bo_create(adev, dws * 4, PAGE_SIZE, true,
+                                            AMDGPU_GEM_DOMAIN_VRAM,
+                                            AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
+                                            NULL, NULL,
+                                            &adev->gfx.rlc.save_restore_obj);
+
+                       if (r) {
+                               dev_warn(adev->dev, "(%d) create RLC sr bo failed\n", r);
+                               return r;
+                       }
+               }
+
+               r = amdgpu_bo_reserve(adev->gfx.rlc.save_restore_obj, false);
+               if (unlikely(r != 0)) {
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+               r = amdgpu_bo_pin(adev->gfx.rlc.save_restore_obj, AMDGPU_GEM_DOMAIN_VRAM,
+                                 &adev->gfx.rlc.save_restore_gpu_addr);
+               if (r) {
+                       amdgpu_bo_unreserve(adev->gfx.rlc.save_restore_obj);
+                       dev_warn(adev->dev, "(%d) pin RLC sr bo failed\n", r);
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+
+               r = amdgpu_bo_kmap(adev->gfx.rlc.save_restore_obj, (void **)&adev->gfx.rlc.sr_ptr);
+               if (r) {
+                       dev_warn(adev->dev, "(%d) map RLC sr bo failed\n", r);
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+               /* write the sr buffer */
+               dst_ptr = adev->gfx.rlc.sr_ptr;
+               for (i = 0; i < adev->gfx.rlc.reg_list_size; i++)
+                       dst_ptr[i] = cpu_to_le32(src_ptr[i]);
+               amdgpu_bo_kunmap(adev->gfx.rlc.save_restore_obj);
+               amdgpu_bo_unreserve(adev->gfx.rlc.save_restore_obj);
+       }
+
+       if (cs_data) {
+               /* clear state block */
+               adev->gfx.rlc.clear_state_size = gfx_v6_0_get_csb_size(adev);
+               dws = adev->gfx.rlc.clear_state_size + (256 / 4);
+
+               if (adev->gfx.rlc.clear_state_obj == NULL) {
+                       r = amdgpu_bo_create(adev, dws * 4, PAGE_SIZE, true,
+                                            AMDGPU_GEM_DOMAIN_VRAM,
+                                            AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
+                                            NULL, NULL,
+                                            &adev->gfx.rlc.clear_state_obj);
+
+                       if (r) {
+                               dev_warn(adev->dev, "(%d) create RLC c bo failed\n", r);
+                               gfx_v6_0_rlc_fini(adev);
+                               return r;
+                       }
+               }
+               r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
+               if (unlikely(r != 0)) {
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+               r = amdgpu_bo_pin(adev->gfx.rlc.clear_state_obj, AMDGPU_GEM_DOMAIN_VRAM,
+                                 &adev->gfx.rlc.clear_state_gpu_addr);
+               if (r) {
+                       amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
+                       dev_warn(adev->dev, "(%d) pin RLC c bo failed\n", r);
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+
+               r = amdgpu_bo_kmap(adev->gfx.rlc.clear_state_obj, (void **)&adev->gfx.rlc.cs_ptr);
+               if (r) {
+                       dev_warn(adev->dev, "(%d) map RLC c bo failed\n", r);
+                       gfx_v6_0_rlc_fini(adev);
+                       return r;
+               }
+               /* set up the cs buffer */
+               dst_ptr = adev->gfx.rlc.cs_ptr;
+               reg_list_mc_addr = adev->gfx.rlc.clear_state_gpu_addr + 256;
+               dst_ptr[0] = cpu_to_le32(upper_32_bits(reg_list_mc_addr));
+               dst_ptr[1] = cpu_to_le32(lower_32_bits(reg_list_mc_addr));
+               dst_ptr[2] = cpu_to_le32(adev->gfx.rlc.clear_state_size);
+               gfx_v6_0_get_csb_buffer(adev, &dst_ptr[(256/4)]);
+               amdgpu_bo_kunmap(adev->gfx.rlc.clear_state_obj);
+               amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
+       }
+
+       return 0;
+}
+
+static void gfx_v6_0_enable_lbpw(struct amdgpu_device *adev, bool enable)
+{
+       u32 tmp;
+
+       tmp = RREG32(RLC_LB_CNTL);
+       if (enable)
+               tmp |= LOAD_BALANCE_ENABLE;
+       else
+               tmp &= ~LOAD_BALANCE_ENABLE;
+       WREG32(RLC_LB_CNTL, tmp);
+
+       if (!enable) {
+               gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+               WREG32(SPI_LB_CU_MASK, 0x00ff);
+       }
+
+}
+
+static void gfx_v6_0_wait_for_rlc_serdes(struct amdgpu_device *adev)
+{
+       int i;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (RREG32(RLC_SERDES_MASTER_BUSY_0) == 0)
+                       break;
+               udelay(1);
+       }
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (RREG32(RLC_SERDES_MASTER_BUSY_1) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static void gfx_v6_0_update_rlc(struct amdgpu_device *adev, u32 rlc)
+{
+       u32 tmp;
+
+       tmp = RREG32(RLC_CNTL);
+       if (tmp != rlc)
+               WREG32(RLC_CNTL, rlc);
+}
+
+static u32 gfx_v6_0_halt_rlc(struct amdgpu_device *adev)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(RLC_CNTL);
+
+       if (data & RLC_ENABLE) {
+               data &= ~RLC_ENABLE;
+               WREG32(RLC_CNTL, data);
+
+               gfx_v6_0_wait_for_rlc_serdes(adev);
+       }
+
+       return orig;
+}
+
+static void gfx_v6_0_rlc_stop(struct amdgpu_device *adev)
+{
+       WREG32(RLC_CNTL, 0);
+
+       gfx_v6_0_enable_gui_idle_interrupt(adev, false);
+       gfx_v6_0_wait_for_rlc_serdes(adev);
+}
+
+static void gfx_v6_0_rlc_start(struct amdgpu_device *adev)
+{
+       WREG32(RLC_CNTL, RLC_ENABLE);
+
+       gfx_v6_0_enable_gui_idle_interrupt(adev, true);
+
+       udelay(50);
+}
+
+static void gfx_v6_0_rlc_reset(struct amdgpu_device *adev)
+{
+       u32 tmp = RREG32(GRBM_SOFT_RESET);
+
+       tmp |= SOFT_RESET_RLC;
+       WREG32(GRBM_SOFT_RESET, tmp);
+       udelay(50);
+       tmp &= ~SOFT_RESET_RLC;
+       WREG32(GRBM_SOFT_RESET, tmp);
+       udelay(50);
+}
+
+static bool gfx_v6_0_lbpw_supported(struct amdgpu_device *adev)
+{
+       u32 tmp;
+
+       /* Enable LBPW only for DDR3 */
+       tmp = RREG32(MC_SEQ_MISC0);
+       if ((tmp & 0xF0000000) == 0xB0000000)
+               return true;
+       return false;
+}
+static void gfx_v6_0_init_cg(struct amdgpu_device *adev)
+{
+}
+
+static int gfx_v6_0_rlc_resume(struct amdgpu_device *adev)
+{
+       u32 i;
+       const struct rlc_firmware_header_v1_0 *hdr;
+       const __le32 *fw_data;
+       u32 fw_size;
+
+
+       if (!adev->gfx.rlc_fw)
+               return -EINVAL;
+
+       gfx_v6_0_rlc_stop(adev);
+       gfx_v6_0_rlc_reset(adev);
+       gfx_v6_0_init_pg(adev);
+       gfx_v6_0_init_cg(adev);
+
+       WREG32(RLC_RL_BASE, 0);
+       WREG32(RLC_RL_SIZE, 0);
+       WREG32(RLC_LB_CNTL, 0);
+       WREG32(RLC_LB_CNTR_MAX, 0xffffffff);
+       WREG32(RLC_LB_CNTR_INIT, 0);
+       WREG32(RLC_LB_INIT_CU_MASK, 0xffffffff);
+
+       WREG32(RLC_MC_CNTL, 0);
+       WREG32(RLC_UCODE_CNTL, 0);
+
+       hdr = (const struct rlc_firmware_header_v1_0 *)adev->gfx.rlc_fw->data;
+       fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+       fw_data = (const __le32 *)
+               (adev->gfx.rlc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+
+       amdgpu_ucode_print_rlc_hdr(&hdr->header);
+
+       for (i = 0; i < fw_size; i++) {
+               WREG32(RLC_UCODE_ADDR, i);
+               WREG32(RLC_UCODE_DATA, le32_to_cpup(fw_data++));
+       }
+       WREG32(RLC_UCODE_ADDR, 0);
+
+       gfx_v6_0_enable_lbpw(adev, gfx_v6_0_lbpw_supported(adev));
+       gfx_v6_0_rlc_start(adev);
+
+       return 0;
+}
+
+static void gfx_v6_0_enable_cgcg(struct amdgpu_device *adev, bool enable)
+{
+       u32 data, orig, tmp;
+
+       orig = data = RREG32(RLC_CGCG_CGLS_CTRL);
+
+       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGCG)) {
+               gfx_v6_0_enable_gui_idle_interrupt(adev, true);
+
+               WREG32(RLC_GCPM_GENERAL_3, 0x00000080);
+
+               tmp = gfx_v6_0_halt_rlc(adev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00b000ff);
+
+               gfx_v6_0_wait_for_rlc_serdes(adev);
+               gfx_v6_0_update_rlc(adev, tmp);
+
+               WREG32(RLC_SERDES_WR_CTRL, 0x007000ff);
+
+               data |= CGCG_EN | CGLS_EN;
+       } else {
+               gfx_v6_0_enable_gui_idle_interrupt(adev, false);
+
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+               RREG32(CB_CGTT_SCLK_CTRL);
+
+               data &= ~(CGCG_EN | CGLS_EN);
+       }
+
+       if (orig != data)
+               WREG32(RLC_CGCG_CGLS_CTRL, data);
+
+}
+
+static void gfx_v6_0_enable_mgcg(struct amdgpu_device *adev, bool enable)
+{
+
+       u32 data, orig, tmp = 0;
+
+       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_MGCG)) {
+               orig = data = RREG32(CGTS_SM_CTRL_REG);
+               data = 0x96940200;
+               if (orig != data)
+                       WREG32(CGTS_SM_CTRL_REG, data);
+
+               if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CP_LS) {
+                       orig = data = RREG32(CP_MEM_SLP_CNTL);
+                       data |= CP_MEM_LS_EN;
+                       if (orig != data)
+                               WREG32(CP_MEM_SLP_CNTL, data);
+               }
+
+               orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE);
+               data &= 0xffffffc0;
+               if (orig != data)
+                       WREG32(RLC_CGTT_MGCG_OVERRIDE, data);
+
+               tmp = gfx_v6_0_halt_rlc(adev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00d000ff);
+
+               gfx_v6_0_update_rlc(adev, tmp);
+       } else {
+               orig = data = RREG32(RLC_CGTT_MGCG_OVERRIDE);
+               data |= 0x00000003;
+               if (orig != data)
+                       WREG32(RLC_CGTT_MGCG_OVERRIDE, data);
+
+               data = RREG32(CP_MEM_SLP_CNTL);
+               if (data & CP_MEM_LS_EN) {
+                       data &= ~CP_MEM_LS_EN;
+                       WREG32(CP_MEM_SLP_CNTL, data);
+               }
+               orig = data = RREG32(CGTS_SM_CTRL_REG);
+               data |= LS_OVERRIDE | OVERRIDE;
+               if (orig != data)
+                       WREG32(CGTS_SM_CTRL_REG, data);
+
+               tmp = gfx_v6_0_halt_rlc(adev);
+
+               WREG32(RLC_SERDES_WR_MASTER_MASK_0, 0xffffffff);
+               WREG32(RLC_SERDES_WR_MASTER_MASK_1, 0xffffffff);
+               WREG32(RLC_SERDES_WR_CTRL, 0x00e000ff);
+
+               gfx_v6_0_update_rlc(adev, tmp);
+       }
+}
+/*
+static void gfx_v6_0_update_cg(struct amdgpu_device *adev,
+                              bool enable)
+{
+       gfx_v6_0_enable_gui_idle_interrupt(adev, false);
+       if (enable) {
+               gfx_v6_0_enable_mgcg(adev, true);
+               gfx_v6_0_enable_cgcg(adev, true);
+       } else {
+               gfx_v6_0_enable_cgcg(adev, false);
+               gfx_v6_0_enable_mgcg(adev, false);
+       }
+       gfx_v6_0_enable_gui_idle_interrupt(adev, true);
+}
+*/
+static void gfx_v6_0_enable_sclk_slowdown_on_pu(struct amdgpu_device *adev,
+                                               bool enable)
+{
+}
+
+static void gfx_v6_0_enable_sclk_slowdown_on_pd(struct amdgpu_device *adev,
+                                               bool enable)
+{
+}
+
+static void gfx_v6_0_enable_cp_pg(struct amdgpu_device *adev, bool enable)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(RLC_PG_CNTL);
+       if (enable && (adev->pg_flags & AMD_PG_SUPPORT_CP))
+               data &= ~0x8000;
+       else
+               data |= 0x8000;
+       if (orig != data)
+               WREG32(RLC_PG_CNTL, data);
+}
+
+static void gfx_v6_0_enable_gds_pg(struct amdgpu_device *adev, bool enable)
+{
+}
+/*
+static void gfx_v6_0_init_cp_pg_table(struct amdgpu_device *adev)
+{
+       const __le32 *fw_data;
+       volatile u32 *dst_ptr;
+       int me, i, max_me = 4;
+       u32 bo_offset = 0;
+       u32 table_offset, table_size;
+
+       if (adev->asic_type == CHIP_KAVERI)
+               max_me = 5;
+
+       if (adev->gfx.rlc.cp_table_ptr == NULL)
+               return;
+
+       dst_ptr = adev->gfx.rlc.cp_table_ptr;
+       for (me = 0; me < max_me; me++) {
+               if (me == 0) {
+                       const struct gfx_firmware_header_v1_0 *hdr =
+                               (const struct gfx_firmware_header_v1_0 *)adev->gfx.ce_fw->data;
+                       fw_data = (const __le32 *)
+                               (adev->gfx.ce_fw->data +
+                                le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+                       table_offset = le32_to_cpu(hdr->jt_offset);
+                       table_size = le32_to_cpu(hdr->jt_size);
+               } else if (me == 1) {
+                       const struct gfx_firmware_header_v1_0 *hdr =
+                               (const struct gfx_firmware_header_v1_0 *)adev->gfx.pfp_fw->data;
+                       fw_data = (const __le32 *)
+                               (adev->gfx.pfp_fw->data +
+                                le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+                       table_offset = le32_to_cpu(hdr->jt_offset);
+                       table_size = le32_to_cpu(hdr->jt_size);
+               } else if (me == 2) {
+                       const struct gfx_firmware_header_v1_0 *hdr =
+                               (const struct gfx_firmware_header_v1_0 *)adev->gfx.me_fw->data;
+                       fw_data = (const __le32 *)
+                               (adev->gfx.me_fw->data +
+                                le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+                       table_offset = le32_to_cpu(hdr->jt_offset);
+                       table_size = le32_to_cpu(hdr->jt_size);
+               } else if (me == 3) {
+                       const struct gfx_firmware_header_v1_0 *hdr =
+                               (const struct gfx_firmware_header_v1_0 *)adev->gfx.mec_fw->data;
+                       fw_data = (const __le32 *)
+                               (adev->gfx.mec_fw->data +
+                                le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+                       table_offset = le32_to_cpu(hdr->jt_offset);
+                       table_size = le32_to_cpu(hdr->jt_size);
+               } else {
+                       const struct gfx_firmware_header_v1_0 *hdr =
+                               (const struct gfx_firmware_header_v1_0 *)adev->gfx.mec2_fw->data;
+                       fw_data = (const __le32 *)
+                               (adev->gfx.mec2_fw->data +
+                                le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+                       table_offset = le32_to_cpu(hdr->jt_offset);
+                       table_size = le32_to_cpu(hdr->jt_size);
+               }
+
+               for (i = 0; i < table_size; i ++) {
+                       dst_ptr[bo_offset + i] =
+                               cpu_to_le32(le32_to_cpu(fw_data[table_offset + i]));
+               }
+
+               bo_offset += table_size;
+       }
+}
+*/
+static void gfx_v6_0_enable_gfx_cgpg(struct amdgpu_device *adev,
+                                    bool enable)
+{
+
+       u32 tmp;
+
+       if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG)) {
+               tmp = RLC_PUD(0x10) | RLC_PDD(0x10) | RLC_TTPD(0x10) | RLC_MSD(0x10);
+               WREG32(RLC_TTOP_D, tmp);
+
+               tmp = RREG32(RLC_PG_CNTL);
+               tmp |= GFX_PG_ENABLE;
+               WREG32(RLC_PG_CNTL, tmp);
+
+               tmp = RREG32(RLC_AUTO_PG_CTRL);
+               tmp |= AUTO_PG_EN;
+               WREG32(RLC_AUTO_PG_CTRL, tmp);
+       } else {
+               tmp = RREG32(RLC_AUTO_PG_CTRL);
+               tmp &= ~AUTO_PG_EN;
+               WREG32(RLC_AUTO_PG_CTRL, tmp);
+
+               tmp = RREG32(DB_RENDER_CONTROL);
+       }
+}
+
+static u32 gfx_v6_0_get_cu_active_bitmap(struct amdgpu_device *adev,
+                                        u32 se, u32 sh)
+{
+
+       u32 mask = 0, tmp, tmp1;
+       int i;
+
+       mutex_lock(&adev->grbm_idx_mutex);
+       gfx_v6_0_select_se_sh(adev, se, sh, 0xffffffff);
+       tmp = RREG32(CC_GC_SHADER_ARRAY_CONFIG);
+       tmp1 = RREG32(GC_USER_SHADER_ARRAY_CONFIG);
+       gfx_v6_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+       mutex_unlock(&adev->grbm_idx_mutex);
+
+       tmp &= 0xffff0000;
+
+       tmp |= tmp1;
+       tmp >>= 16;
+
+       for (i = 0; i < adev->gfx.config.max_cu_per_sh; i ++) {
+               mask <<= 1;
+               mask |= 1;
+       }
+
+       return (~tmp) & mask;
+}
+
+static void gfx_v6_0_init_ao_cu_mask(struct amdgpu_device *adev)
+{
+       u32 i, j, k, active_cu_number = 0;
+
+       u32 mask, counter, cu_bitmap;
+       u32 tmp = 0;
+
+       for (i = 0; i < adev->gfx.config.max_shader_engines; i++) {
+               for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) {
+                       mask = 1;
+                       cu_bitmap = 0;
+                       counter  = 0;
+                       for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) {
+                               if (gfx_v6_0_get_cu_active_bitmap(adev, i, j) & mask) {
+                                       if (counter < 2)
+                                               cu_bitmap |= mask;
+                                       counter++;
+                               }
+                               mask <<= 1;
+                       }
+
+                       active_cu_number += counter;
+                       tmp |= (cu_bitmap << (i * 16 + j * 8));
+               }
+       }
+
+       WREG32(RLC_PG_AO_CU_MASK, tmp);
+
+       tmp = RREG32(RLC_MAX_PG_CU);
+       tmp &= ~MAX_PU_CU_MASK;
+       tmp |= MAX_PU_CU(active_cu_number);
+       WREG32(RLC_MAX_PG_CU, tmp);
+}
+
+static void gfx_v6_0_enable_gfx_static_mgpg(struct amdgpu_device *adev,
+                                           bool enable)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(RLC_PG_CNTL);
+       if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_SMG))
+               data |= STATIC_PER_CU_PG_ENABLE;
+       else
+               data &= ~STATIC_PER_CU_PG_ENABLE;
+       if (orig != data)
+               WREG32(RLC_PG_CNTL, data);
+}
+
+static void gfx_v6_0_enable_gfx_dynamic_mgpg(struct amdgpu_device *adev,
+                                            bool enable)
+{
+       u32 data, orig;
+
+       orig = data = RREG32(RLC_PG_CNTL);
+       if (enable && (adev->pg_flags & AMD_PG_SUPPORT_GFX_DMG))
+               data |= DYN_PER_CU_PG_ENABLE;
+       else
+               data &= ~DYN_PER_CU_PG_ENABLE;
+       if (orig != data)
+               WREG32(RLC_PG_CNTL, data);
+}
+
+static void gfx_v6_0_init_gfx_cgpg(struct amdgpu_device *adev)
+{
+       u32 tmp;
+
+       WREG32(RLC_SAVE_AND_RESTORE_BASE, adev->gfx.rlc.save_restore_gpu_addr >> 8);
+
+       tmp = RREG32(RLC_PG_CNTL);
+       tmp |= GFX_PG_SRC;
+       WREG32(RLC_PG_CNTL, tmp);
+
+       WREG32(RLC_CLEAR_STATE_RESTORE_BASE, adev->gfx.rlc.clear_state_gpu_addr >> 8);
+
+       tmp = RREG32(RLC_AUTO_PG_CTRL);
+
+       tmp &= ~GRBM_REG_SGIT_MASK;
+       tmp |= GRBM_REG_SGIT(0x700);
+       tmp &= ~PG_AFTER_GRBM_REG_ST_MASK;
+       WREG32(RLC_AUTO_PG_CTRL, tmp);
+}
+
+static void gfx_v6_0_update_gfx_pg(struct amdgpu_device *adev, bool enable)
+{
+       gfx_v6_0_enable_gfx_cgpg(adev, enable);
+       gfx_v6_0_enable_gfx_static_mgpg(adev, enable);
+       gfx_v6_0_enable_gfx_dynamic_mgpg(adev, enable);
+}
+
+static u32 gfx_v6_0_get_csb_size(struct amdgpu_device *adev)
+{
+       u32 count = 0;
+       const struct cs_section_def *sect = NULL;
+       const struct cs_extent_def *ext = NULL;
+
+       if (adev->gfx.rlc.cs_data == NULL)
+               return 0;
+
+       /* begin clear state */
+       count += 2;
+       /* context control state */
+       count += 3;
+
+       for (sect = adev->gfx.rlc.cs_data; sect->section != NULL; ++sect) {
+               for (ext = sect->section; ext->extent != NULL; ++ext) {
+                       if (sect->id == SECT_CONTEXT)
+                               count += 2 + ext->reg_count;
+                       else
+                               return 0;
+               }
+       }
+       /* pa_sc_raster_config */
+       count += 3;
+       /* end clear state */
+       count += 2;
+       /* clear state */
+       count += 2;
+
+       return count;
+}
+
+static void gfx_v6_0_get_csb_buffer(struct amdgpu_device *adev,
+                                   volatile u32 *buffer)
+{
+       u32 count = 0, i;
+       const struct cs_section_def *sect = NULL;
+       const struct cs_extent_def *ext = NULL;
+
+       if (adev->gfx.rlc.cs_data == NULL)
+               return;
+       if (buffer == NULL)
+               return;
+
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
+
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       buffer[count++] = cpu_to_le32(0x80000000);
+       buffer[count++] = cpu_to_le32(0x80000000);
+
+       for (sect = adev->gfx.rlc.cs_data; sect->section != NULL; ++sect) {
+               for (ext = sect->section; ext->extent != NULL; ++ext) {
+                       if (sect->id == SECT_CONTEXT) {
+                               buffer[count++] =
+                                       cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, ext->reg_count));
+                               buffer[count++] = cpu_to_le32(ext->reg_index - 0xa000);
+                               for (i = 0; i < ext->reg_count; i++)
+                                       buffer[count++] = cpu_to_le32(ext->extent[i]);
+                       } else {
+                               return;
+                       }
+               }
+       }
+
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_SET_CONTEXT_REG, 1));
+       buffer[count++] = cpu_to_le32(PA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START);
+
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+               buffer[count++] = cpu_to_le32(0x2a00126a);
+               break;
+       case CHIP_VERDE:
+               buffer[count++] = cpu_to_le32(0x0000124a);
+               break;
+       case CHIP_OLAND:
+               buffer[count++] = cpu_to_le32(0x00000082);
+               break;
+       case CHIP_HAINAN:
+               buffer[count++] = cpu_to_le32(0x00000000);
+               break;
+       default:
+               buffer[count++] = cpu_to_le32(0x00000000);
+               break;
+       }
+
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+       buffer[count++] = cpu_to_le32(PACKET3_PREAMBLE_END_CLEAR_STATE);
+
+       buffer[count++] = cpu_to_le32(PACKET3(PACKET3_CLEAR_STATE, 0));
+       buffer[count++] = cpu_to_le32(0);
+}
+
+static void gfx_v6_0_init_pg(struct amdgpu_device *adev)
+{
+       if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG |
+                             AMD_PG_SUPPORT_GFX_SMG |
+                             AMD_PG_SUPPORT_GFX_DMG |
+                             AMD_PG_SUPPORT_CP |
+                             AMD_PG_SUPPORT_GDS |
+                             AMD_PG_SUPPORT_RLC_SMU_HS)) {
+               gfx_v6_0_enable_sclk_slowdown_on_pu(adev, true);
+               gfx_v6_0_enable_sclk_slowdown_on_pd(adev, true);
+               if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) {
+                       gfx_v6_0_init_gfx_cgpg(adev);
+                       gfx_v6_0_enable_cp_pg(adev, true);
+                       gfx_v6_0_enable_gds_pg(adev, true);
+               } else {
+                       WREG32(RLC_SAVE_AND_RESTORE_BASE, adev->gfx.rlc.save_restore_gpu_addr >> 8);
+                       WREG32(RLC_CLEAR_STATE_RESTORE_BASE, adev->gfx.rlc.clear_state_gpu_addr >> 8);
+
+               }
+               gfx_v6_0_init_ao_cu_mask(adev);
+               gfx_v6_0_update_gfx_pg(adev, true);
+       } else {
+
+               WREG32(RLC_SAVE_AND_RESTORE_BASE, adev->gfx.rlc.save_restore_gpu_addr >> 8);
+               WREG32(RLC_CLEAR_STATE_RESTORE_BASE, adev->gfx.rlc.clear_state_gpu_addr >> 8);
+       }
+}
+
+static void gfx_v6_0_fini_pg(struct amdgpu_device *adev)
+{
+       if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG |
+                             AMD_PG_SUPPORT_GFX_SMG |
+                             AMD_PG_SUPPORT_GFX_DMG |
+                             AMD_PG_SUPPORT_CP |
+                             AMD_PG_SUPPORT_GDS |
+                             AMD_PG_SUPPORT_RLC_SMU_HS)) {
+               gfx_v6_0_update_gfx_pg(adev, false);
+               if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) {
+                       gfx_v6_0_enable_cp_pg(adev, false);
+                       gfx_v6_0_enable_gds_pg(adev, false);
+               }
+       }
+}
+
+static uint64_t gfx_v6_0_get_gpu_clock_counter(struct amdgpu_device *adev)
+{
+       uint64_t clock;
+
+       mutex_lock(&adev->gfx.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(&adev->gfx.gpu_clock_mutex);
+       return clock;
+}
+
+static void gfx_v6_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
+{
+       amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       amdgpu_ring_write(ring, 0x80000000);
+       amdgpu_ring_write(ring, 0);
+}
+
+static unsigned gfx_v6_0_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               6; /* gfx_v6_0_ring_emit_ib */
+}
+
+static unsigned gfx_v6_0_ring_get_dma_frame_size_gfx(struct amdgpu_ring *ring)
+{
+       return
+               5 + /* gfx_v6_0_ring_emit_hdp_flush */
+               5 + /* gfx_v6_0_ring_emit_hdp_invalidate */
+               14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
+               7 + 4 + /* gfx_v6_0_ring_emit_pipeline_sync */
+               17 + 6 + /* gfx_v6_0_ring_emit_vm_flush */
+               3; /* gfx_v6_ring_emit_cntxcntl */
+}
+
+static unsigned gfx_v6_0_ring_get_dma_frame_size_compute(struct amdgpu_ring *ring)
+{
+       return
+               5 + /* gfx_v6_0_ring_emit_hdp_flush */
+               5 + /* gfx_v6_0_ring_emit_hdp_invalidate */
+               7 + /* gfx_v6_0_ring_emit_pipeline_sync */
+               17 + /* gfx_v6_0_ring_emit_vm_flush */
+               14 + 14 + 14; /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
+}
+
+static const struct amdgpu_gfx_funcs gfx_v6_0_gfx_funcs = {
+       .get_gpu_clock_counter = &gfx_v6_0_get_gpu_clock_counter,
+       .select_se_sh = &gfx_v6_0_select_se_sh,
+};
+
+static int gfx_v6_0_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       adev->gfx.num_gfx_rings = GFX6_NUM_GFX_RINGS;
+       adev->gfx.num_compute_rings = GFX6_NUM_COMPUTE_RINGS;
+       adev->gfx.funcs = &gfx_v6_0_gfx_funcs;
+       gfx_v6_0_set_ring_funcs(adev);
+       gfx_v6_0_set_irq_funcs(adev);
+
+       return 0;
+}
+
+static int gfx_v6_0_sw_init(void *handle)
+{
+       struct amdgpu_ring *ring;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       int i, r;
+
+       r = amdgpu_irq_add_id(adev, 181, &adev->gfx.eop_irq);
+       if (r)
+               return r;
+
+       r = amdgpu_irq_add_id(adev, 184, &adev->gfx.priv_reg_irq);
+       if (r)
+               return r;
+
+       r = amdgpu_irq_add_id(adev, 185, &adev->gfx.priv_inst_irq);
+       if (r)
+               return r;
+
+       gfx_v6_0_scratch_init(adev);
+
+       r = gfx_v6_0_init_microcode(adev);
+       if (r) {
+               DRM_ERROR("Failed to load gfx firmware!\n");
+               return r;
+       }
+
+       r = gfx_v6_0_rlc_init(adev);
+       if (r) {
+               DRM_ERROR("Failed to init rlc BOs!\n");
+               return r;
+       }
+
+       for (i = 0; i < adev->gfx.num_gfx_rings; i++) {
+               ring = &adev->gfx.gfx_ring[i];
+               ring->ring_obj = NULL;
+               sprintf(ring->name, "gfx");
+               r = amdgpu_ring_init(adev, ring, 1024,
+                                    0x80000000, 0xf,
+                                    &adev->gfx.eop_irq, AMDGPU_CP_IRQ_GFX_EOP,
+                                    AMDGPU_RING_TYPE_GFX);
+               if (r)
+                       return r;
+       }
+
+       for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+               unsigned irq_type;
+
+               if ((i >= 32) || (i >= AMDGPU_MAX_COMPUTE_RINGS)) {
+                       DRM_ERROR("Too many (%d) compute rings!\n", i);
+                       break;
+               }
+               ring = &adev->gfx.compute_ring[i];
+               ring->ring_obj = NULL;
+               ring->use_doorbell = false;
+               ring->doorbell_index = 0;
+               ring->me = 1;
+               ring->pipe = i;
+               ring->queue = i;
+               sprintf(ring->name, "comp %d.%d.%d", ring->me, ring->pipe, ring->queue);
+               irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + ring->pipe;
+               r = amdgpu_ring_init(adev, ring, 1024,
+                                    0x80000000, 0xf,
+                                    &adev->gfx.eop_irq, irq_type,
+                                    AMDGPU_RING_TYPE_COMPUTE);
+               if (r)
+                       return r;
+       }
+
+       return r;
+}
+
+static int gfx_v6_0_sw_fini(void *handle)
+{
+       int i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       amdgpu_bo_unref(&adev->gds.oa_gfx_bo);
+       amdgpu_bo_unref(&adev->gds.gws_gfx_bo);
+       amdgpu_bo_unref(&adev->gds.gds_gfx_bo);
+
+       for (i = 0; i < adev->gfx.num_gfx_rings; i++)
+               amdgpu_ring_fini(&adev->gfx.gfx_ring[i]);
+       for (i = 0; i < adev->gfx.num_compute_rings; i++)
+               amdgpu_ring_fini(&adev->gfx.compute_ring[i]);
+
+       gfx_v6_0_rlc_fini(adev);
+
+       return 0;
+}
+
+static int gfx_v6_0_hw_init(void *handle)
+{
+       int r;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       gfx_v6_0_gpu_init(adev);
+
+       r = gfx_v6_0_rlc_resume(adev);
+       if (r)
+               return r;
+
+       r = gfx_v6_0_cp_resume(adev);
+       if (r)
+               return r;
+
+       adev->gfx.ce_ram_size = 0x8000;
+
+       return r;
+}
+
+static int gfx_v6_0_hw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       gfx_v6_0_cp_enable(adev, false);
+       gfx_v6_0_rlc_stop(adev);
+       gfx_v6_0_fini_pg(adev);
+
+       return 0;
+}
+
+static int gfx_v6_0_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return gfx_v6_0_hw_fini(adev);
+}
+
+static int gfx_v6_0_resume(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return gfx_v6_0_hw_init(adev);
+}
+
+static bool gfx_v6_0_is_idle(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (RREG32(GRBM_STATUS) & GRBM_STATUS__GUI_ACTIVE_MASK)
+               return false;
+       else
+               return true;
+}
+
+static int gfx_v6_0_wait_for_idle(void *handle)
+{
+       unsigned i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (gfx_v6_0_is_idle(handle))
+                       return 0;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+}
+
+static int gfx_v6_0_soft_reset(void *handle)
+{
+       return 0;
+}
+
+static void gfx_v6_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev,
+                                                enum amdgpu_interrupt_state state)
+{
+       u32 cp_int_cntl;
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl &= ~CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl |= CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       default:
+               break;
+       }
+}
+
+static void gfx_v6_0_set_compute_eop_interrupt_state(struct amdgpu_device *adev,
+                                                    int ring,
+                                                    enum amdgpu_interrupt_state state)
+{
+       u32 cp_int_cntl;
+       switch (state){
+       case AMDGPU_IRQ_STATE_DISABLE:
+               if (ring == 0) {
+                       cp_int_cntl = RREG32(CP_INT_CNTL_RING1);
+                       cp_int_cntl &= ~CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+                       WREG32(CP_INT_CNTL_RING1, cp_int_cntl);
+                       break;
+               } else {
+                       cp_int_cntl = RREG32(CP_INT_CNTL_RING2);
+                       cp_int_cntl &= ~CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+                       WREG32(CP_INT_CNTL_RING2, cp_int_cntl);
+                       break;
+
+               }
+       case AMDGPU_IRQ_STATE_ENABLE:
+               if (ring == 0) {
+                       cp_int_cntl = RREG32(CP_INT_CNTL_RING1);
+                       cp_int_cntl |= CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+                       WREG32(CP_INT_CNTL_RING1, cp_int_cntl);
+                       break;
+               } else {
+                       cp_int_cntl = RREG32(CP_INT_CNTL_RING2);
+                       cp_int_cntl |= CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK;
+                       WREG32(CP_INT_CNTL_RING2, cp_int_cntl);
+                       break;
+
+               }
+
+       default:
+               BUG();
+               break;
+
+       }
+}
+
+static int gfx_v6_0_set_priv_reg_fault_state(struct amdgpu_device *adev,
+                                            struct amdgpu_irq_src *src,
+                                            unsigned type,
+                                            enum amdgpu_interrupt_state state)
+{
+       u32 cp_int_cntl;
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl &= ~CP_INT_CNTL_RING0__PRIV_REG_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl |= CP_INT_CNTL_RING0__PRIV_REG_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int gfx_v6_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
+                                             struct amdgpu_irq_src *src,
+                                             unsigned type,
+                                             enum amdgpu_interrupt_state state)
+{
+       u32 cp_int_cntl;
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl &= ~CP_INT_CNTL_RING0__PRIV_INSTR_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               cp_int_cntl = RREG32(CP_INT_CNTL_RING0);
+               cp_int_cntl |= CP_INT_CNTL_RING0__PRIV_INSTR_INT_ENABLE_MASK;
+               WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int gfx_v6_0_set_eop_interrupt_state(struct amdgpu_device *adev,
+                                           struct amdgpu_irq_src *src,
+                                           unsigned type,
+                                           enum amdgpu_interrupt_state state)
+{
+       switch (type) {
+       case AMDGPU_CP_IRQ_GFX_EOP:
+               gfx_v6_0_set_gfx_eop_interrupt_state(adev, state);
+               break;
+       case AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP:
+               gfx_v6_0_set_compute_eop_interrupt_state(adev, 0, state);
+               break;
+       case AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE1_EOP:
+               gfx_v6_0_set_compute_eop_interrupt_state(adev, 1, state);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int gfx_v6_0_eop_irq(struct amdgpu_device *adev,
+                           struct amdgpu_irq_src *source,
+                           struct amdgpu_iv_entry *entry)
+{
+       switch (entry->ring_id) {
+       case 0:
+               amdgpu_fence_process(&adev->gfx.gfx_ring[0]);
+               break;
+       case 1:
+       case 2:
+               amdgpu_fence_process(&adev->gfx.compute_ring[entry->ring_id -1]);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int gfx_v6_0_priv_reg_irq(struct amdgpu_device *adev,
+                                struct amdgpu_irq_src *source,
+                                struct amdgpu_iv_entry *entry)
+{
+       DRM_ERROR("Illegal register access in command stream\n");
+       schedule_work(&adev->reset_work);
+       return 0;
+}
+
+static int gfx_v6_0_priv_inst_irq(struct amdgpu_device *adev,
+                                 struct amdgpu_irq_src *source,
+                                 struct amdgpu_iv_entry *entry)
+{
+       DRM_ERROR("Illegal instruction in command stream\n");
+       schedule_work(&adev->reset_work);
+       return 0;
+}
+
+static int gfx_v6_0_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       bool gate = false;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (state == AMD_CG_STATE_GATE)
+               gate = true;
+
+       gfx_v6_0_enable_gui_idle_interrupt(adev, false);
+       if (gate) {
+               gfx_v6_0_enable_mgcg(adev, true);
+               gfx_v6_0_enable_cgcg(adev, true);
+       } else {
+               gfx_v6_0_enable_cgcg(adev, false);
+               gfx_v6_0_enable_mgcg(adev, false);
+       }
+       gfx_v6_0_enable_gui_idle_interrupt(adev, true);
+
+       return 0;
+}
+
+static int gfx_v6_0_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       bool gate = false;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (state == AMD_PG_STATE_GATE)
+               gate = true;
+
+       if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG |
+                             AMD_PG_SUPPORT_GFX_SMG |
+                             AMD_PG_SUPPORT_GFX_DMG |
+                             AMD_PG_SUPPORT_CP |
+                             AMD_PG_SUPPORT_GDS |
+                             AMD_PG_SUPPORT_RLC_SMU_HS)) {
+               gfx_v6_0_update_gfx_pg(adev, gate);
+               if (adev->pg_flags & AMD_PG_SUPPORT_GFX_PG) {
+                       gfx_v6_0_enable_cp_pg(adev, gate);
+                       gfx_v6_0_enable_gds_pg(adev, gate);
+               }
+       }
+
+       return 0;
+}
+
+const struct amd_ip_funcs gfx_v6_0_ip_funcs = {
+       .name = "gfx_v6_0",
+       .early_init = gfx_v6_0_early_init,
+       .late_init = NULL,
+       .sw_init = gfx_v6_0_sw_init,
+       .sw_fini = gfx_v6_0_sw_fini,
+       .hw_init = gfx_v6_0_hw_init,
+       .hw_fini = gfx_v6_0_hw_fini,
+       .suspend = gfx_v6_0_suspend,
+       .resume = gfx_v6_0_resume,
+       .is_idle = gfx_v6_0_is_idle,
+       .wait_for_idle = gfx_v6_0_wait_for_idle,
+       .soft_reset = gfx_v6_0_soft_reset,
+       .set_clockgating_state = gfx_v6_0_set_clockgating_state,
+       .set_powergating_state = gfx_v6_0_set_powergating_state,
+};
+
+static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
+       .get_rptr = gfx_v6_0_ring_get_rptr,
+       .get_wptr = gfx_v6_0_ring_get_wptr,
+       .set_wptr = gfx_v6_0_ring_set_wptr_gfx,
+       .parse_cs = NULL,
+       .emit_ib = gfx_v6_0_ring_emit_ib,
+       .emit_fence = gfx_v6_0_ring_emit_fence,
+       .emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
+       .emit_vm_flush = gfx_v6_0_ring_emit_vm_flush,
+       .emit_hdp_flush = gfx_v6_0_ring_emit_hdp_flush,
+       .emit_hdp_invalidate = gfx_v6_0_ring_emit_hdp_invalidate,
+       .test_ring = gfx_v6_0_ring_test_ring,
+       .test_ib = gfx_v6_0_ring_test_ib,
+       .insert_nop = amdgpu_ring_insert_nop,
+       .emit_cntxcntl = gfx_v6_ring_emit_cntxcntl,
+       .get_emit_ib_size = gfx_v6_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = gfx_v6_0_ring_get_dma_frame_size_gfx,
+};
+
+static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
+       .get_rptr = gfx_v6_0_ring_get_rptr,
+       .get_wptr = gfx_v6_0_ring_get_wptr,
+       .set_wptr = gfx_v6_0_ring_set_wptr_compute,
+       .parse_cs = NULL,
+       .emit_ib = gfx_v6_0_ring_emit_ib,
+       .emit_fence = gfx_v6_0_ring_emit_fence,
+       .emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
+       .emit_vm_flush = gfx_v6_0_ring_emit_vm_flush,
+       .emit_hdp_flush = gfx_v6_0_ring_emit_hdp_flush,
+       .emit_hdp_invalidate = gfx_v6_0_ring_emit_hdp_invalidate,
+       .test_ring = gfx_v6_0_ring_test_ring,
+       .test_ib = gfx_v6_0_ring_test_ib,
+       .insert_nop = amdgpu_ring_insert_nop,
+       .get_emit_ib_size = gfx_v6_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = gfx_v6_0_ring_get_dma_frame_size_compute,
+};
+
+static void gfx_v6_0_set_ring_funcs(struct amdgpu_device *adev)
+{
+       int i;
+
+       for (i = 0; i < adev->gfx.num_gfx_rings; i++)
+               adev->gfx.gfx_ring[i].funcs = &gfx_v6_0_ring_funcs_gfx;
+       for (i = 0; i < adev->gfx.num_compute_rings; i++)
+               adev->gfx.compute_ring[i].funcs = &gfx_v6_0_ring_funcs_compute;
+}
+
+static const struct amdgpu_irq_src_funcs gfx_v6_0_eop_irq_funcs = {
+       .set = gfx_v6_0_set_eop_interrupt_state,
+       .process = gfx_v6_0_eop_irq,
+};
+
+static const struct amdgpu_irq_src_funcs gfx_v6_0_priv_reg_irq_funcs = {
+       .set = gfx_v6_0_set_priv_reg_fault_state,
+       .process = gfx_v6_0_priv_reg_irq,
+};
+
+static const struct amdgpu_irq_src_funcs gfx_v6_0_priv_inst_irq_funcs = {
+       .set = gfx_v6_0_set_priv_inst_fault_state,
+       .process = gfx_v6_0_priv_inst_irq,
+};
+
+static void gfx_v6_0_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->gfx.eop_irq.num_types = AMDGPU_CP_IRQ_LAST;
+       adev->gfx.eop_irq.funcs = &gfx_v6_0_eop_irq_funcs;
+
+       adev->gfx.priv_reg_irq.num_types = 1;
+       adev->gfx.priv_reg_irq.funcs = &gfx_v6_0_priv_reg_irq_funcs;
+
+       adev->gfx.priv_inst_irq.num_types = 1;
+       adev->gfx.priv_inst_irq.funcs = &gfx_v6_0_priv_inst_irq_funcs;
+}
+
+static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev)
+{
+       int i, j, k, counter, active_cu_number = 0;
+       u32 mask, bitmap, ao_bitmap, ao_cu_mask = 0;
+       struct amdgpu_cu_info *cu_info = &adev->gfx.cu_info;
+
+       memset(cu_info, 0, sizeof(*cu_info));
+
+       for (i = 0; i < adev->gfx.config.max_shader_engines; i++) {
+               for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) {
+                       mask = 1;
+                       ao_bitmap = 0;
+                       counter = 0;
+                       bitmap = gfx_v6_0_get_cu_active_bitmap(adev, i, j);
+                       cu_info->bitmap[i][j] = bitmap;
+
+                       for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) {
+                               if (bitmap & mask) {
+                                       if (counter < 2)
+                                               ao_bitmap |= mask;
+                                       counter ++;
+                               }
+                               mask <<= 1;
+                       }
+                       active_cu_number += counter;
+                       ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8));
+               }
+       }
+
+       cu_info->number = active_cu_number;
+       cu_info->ao_cu_mask = ao_cu_mask;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.h b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.h
new file mode 100644 (file)
index 0000000..b9657e7
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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 __GFX_V6_0_H__
+#define __GFX_V6_0_H__
+
+extern const struct amd_ip_funcs gfx_v6_0_ip_funcs;
+
+#endif
index 425413f..71116da 100644 (file)
@@ -1645,6 +1645,147 @@ static u32 gfx_v7_0_get_rb_active_bitmap(struct amdgpu_device *adev)
        return (~data) & mask;
 }
 
+static void
+gfx_v7_0_raster_config(struct amdgpu_device *adev, u32 *rconf, u32 *rconf1)
+{
+       switch (adev->asic_type) {
+       case CHIP_BONAIRE:
+               *rconf |= RB_MAP_PKR0(2) | RB_XSEL2(1) | SE_MAP(2) |
+                         SE_XSEL(1) | SE_YSEL(1);
+               *rconf1 |= 0x0;
+               break;
+       case CHIP_HAWAII:
+               *rconf |= RB_MAP_PKR0(2) | RB_MAP_PKR1(2) |
+                         RB_XSEL2(1) | PKR_MAP(2) | PKR_XSEL(1) |
+                         PKR_YSEL(1) | SE_MAP(2) | SE_XSEL(2) |
+                         SE_YSEL(3);
+               *rconf1 |= SE_PAIR_MAP(2) | SE_PAIR_XSEL(3) |
+                          SE_PAIR_YSEL(2);
+               break;
+       case CHIP_KAVERI:
+               *rconf |= RB_MAP_PKR0(2);
+               *rconf1 |= 0x0;
+               break;
+       case CHIP_KABINI:
+       case CHIP_MULLINS:
+               *rconf |= 0x0;
+               *rconf1 |= 0x0;
+               break;
+       default:
+               DRM_ERROR("unknown asic: 0x%x\n", adev->asic_type);
+               break;
+       }
+}
+
+static void
+gfx_v7_0_write_harvested_raster_configs(struct amdgpu_device *adev,
+                                       u32 raster_config, u32 raster_config_1,
+                                       unsigned rb_mask, unsigned num_rb)
+{
+       unsigned sh_per_se = max_t(unsigned, adev->gfx.config.max_sh_per_se, 1);
+       unsigned num_se = max_t(unsigned, adev->gfx.config.max_shader_engines, 1);
+       unsigned rb_per_pkr = min_t(unsigned, num_rb / num_se / sh_per_se, 2);
+       unsigned rb_per_se = num_rb / num_se;
+       unsigned se_mask[4];
+       unsigned se;
+
+       se_mask[0] = ((1 << rb_per_se) - 1) & rb_mask;
+       se_mask[1] = (se_mask[0] << rb_per_se) & rb_mask;
+       se_mask[2] = (se_mask[1] << rb_per_se) & rb_mask;
+       se_mask[3] = (se_mask[2] << rb_per_se) & rb_mask;
+
+       WARN_ON(!(num_se == 1 || num_se == 2 || num_se == 4));
+       WARN_ON(!(sh_per_se == 1 || sh_per_se == 2));
+       WARN_ON(!(rb_per_pkr == 1 || rb_per_pkr == 2));
+
+       if ((num_se > 2) && ((!se_mask[0] && !se_mask[1]) ||
+                            (!se_mask[2] && !se_mask[3]))) {
+               raster_config_1 &= ~SE_PAIR_MAP_MASK;
+
+               if (!se_mask[0] && !se_mask[1]) {
+                       raster_config_1 |=
+                               SE_PAIR_MAP(RASTER_CONFIG_SE_PAIR_MAP_3);
+               } else {
+                       raster_config_1 |=
+                               SE_PAIR_MAP(RASTER_CONFIG_SE_PAIR_MAP_0);
+               }
+       }
+
+       for (se = 0; se < num_se; se++) {
+               unsigned raster_config_se = raster_config;
+               unsigned pkr0_mask = ((1 << rb_per_pkr) - 1) << (se * rb_per_se);
+               unsigned pkr1_mask = pkr0_mask << rb_per_pkr;
+               int idx = (se / 2) * 2;
+
+               if ((num_se > 1) && (!se_mask[idx] || !se_mask[idx + 1])) {
+                       raster_config_se &= ~SE_MAP_MASK;
+
+                       if (!se_mask[idx]) {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_3);
+                       } else {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_0);
+                       }
+               }
+
+               pkr0_mask &= rb_mask;
+               pkr1_mask &= rb_mask;
+               if (rb_per_se > 2 && (!pkr0_mask || !pkr1_mask)) {
+                       raster_config_se &= ~PKR_MAP_MASK;
+
+                       if (!pkr0_mask) {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_3);
+                       } else {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_0);
+                       }
+               }
+
+               if (rb_per_se >= 2) {
+                       unsigned rb0_mask = 1 << (se * rb_per_se);
+                       unsigned rb1_mask = rb0_mask << 1;
+
+                       rb0_mask &= rb_mask;
+                       rb1_mask &= rb_mask;
+                       if (!rb0_mask || !rb1_mask) {
+                               raster_config_se &= ~RB_MAP_PKR0_MASK;
+
+                               if (!rb0_mask) {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_3);
+                               } else {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_0);
+                               }
+                       }
+
+                       if (rb_per_se > 2) {
+                               rb0_mask = 1 << (se * rb_per_se + rb_per_pkr);
+                               rb1_mask = rb0_mask << 1;
+                               rb0_mask &= rb_mask;
+                               rb1_mask &= rb_mask;
+                               if (!rb0_mask || !rb1_mask) {
+                                       raster_config_se &= ~RB_MAP_PKR1_MASK;
+
+                                       if (!rb0_mask) {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_3);
+                                       } else {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_0);
+                                       }
+                               }
+                       }
+               }
+
+               /* GRBM_GFX_INDEX has a different offset on CI+ */
+               gfx_v7_0_select_se_sh(adev, se, 0xffffffff, 0xffffffff);
+               WREG32(mmPA_SC_RASTER_CONFIG, raster_config_se);
+               WREG32(mmPA_SC_RASTER_CONFIG_1, raster_config_1);
+       }
+
+       /* GRBM_GFX_INDEX has a different offset on CI+ */
+       gfx_v7_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+}
+
 /**
  * gfx_v7_0_setup_rb - setup the RBs on the asic
  *
@@ -1658,9 +1799,11 @@ static void gfx_v7_0_setup_rb(struct amdgpu_device *adev)
 {
        int i, j;
        u32 data;
+       u32 raster_config = 0, raster_config_1 = 0;
        u32 active_rbs = 0;
        u32 rb_bitmap_width_per_sh = adev->gfx.config.max_backends_per_se /
                                        adev->gfx.config.max_sh_per_se;
+       unsigned num_rb_pipes;
 
        mutex_lock(&adev->grbm_idx_mutex);
        for (i = 0; i < adev->gfx.config.max_shader_engines; i++) {
@@ -1672,10 +1815,25 @@ static void gfx_v7_0_setup_rb(struct amdgpu_device *adev)
                }
        }
        gfx_v7_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
-       mutex_unlock(&adev->grbm_idx_mutex);
 
        adev->gfx.config.backend_enable_mask = active_rbs;
        adev->gfx.config.num_rbs = hweight32(active_rbs);
+
+       num_rb_pipes = min_t(unsigned, adev->gfx.config.max_backends_per_se *
+                            adev->gfx.config.max_shader_engines, 16);
+
+       gfx_v7_0_raster_config(adev, &raster_config, &raster_config_1);
+
+       if (!adev->gfx.config.backend_enable_mask ||
+                       adev->gfx.config.num_rbs >= num_rb_pipes) {
+               WREG32(mmPA_SC_RASTER_CONFIG, raster_config);
+               WREG32(mmPA_SC_RASTER_CONFIG_1, raster_config_1);
+       } else {
+               gfx_v7_0_write_harvested_raster_configs(adev, raster_config, raster_config_1,
+                                                       adev->gfx.config.backend_enable_mask,
+                                                       num_rb_pipes);
+       }
+       mutex_unlock(&adev->grbm_idx_mutex);
 }
 
 /**
@@ -2096,6 +2254,25 @@ static void gfx_v7_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, control);
 }
 
+static void gfx_v7_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
+{
+       uint32_t dw2 = 0;
+
+       dw2 |= 0x80000000; /* set load_enable otherwise this package is just NOPs */
+       if (flags & AMDGPU_HAVE_CTX_SWITCH) {
+               /* set load_global_config & load_global_uconfig */
+               dw2 |= 0x8001;
+               /* set load_cs_sh_regs */
+               dw2 |= 0x01000000;
+               /* set load_per_context_state & load_gfx_sh_regs */
+               dw2 |= 0x10002;
+       }
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       amdgpu_ring_write(ring, dw2);
+       amdgpu_ring_write(ring, 0);
+}
+
 /**
  * gfx_v7_0_ring_test_ib - basic ring IB test
  *
@@ -2443,7 +2620,7 @@ static int gfx_v7_0_cp_gfx_resume(struct amdgpu_device *adev)
        return 0;
 }
 
-static u32 gfx_v7_0_ring_get_rptr_gfx(struct amdgpu_ring *ring)
+static u32 gfx_v7_0_ring_get_rptr(struct amdgpu_ring *ring)
 {
        return ring->adev->wb.wb[ring->rptr_offs];
 }
@@ -2463,11 +2640,6 @@ static void gfx_v7_0_ring_set_wptr_gfx(struct amdgpu_ring *ring)
        (void)RREG32(mmCP_RB0_WPTR);
 }
 
-static u32 gfx_v7_0_ring_get_rptr_compute(struct amdgpu_ring *ring)
-{
-       return ring->adev->wb.wb[ring->rptr_offs];
-}
-
 static u32 gfx_v7_0_ring_get_wptr_compute(struct amdgpu_ring *ring)
 {
        /* XXX check if swapping is necessary on BE */
@@ -4182,6 +4354,41 @@ static void gfx_v7_0_ring_emit_gds_switch(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, (1 << (oa_size + oa_base)) - (1 << oa_base));
 }
 
+static unsigned gfx_v7_0_ring_get_emit_ib_size_gfx(struct amdgpu_ring *ring)
+{
+       return
+               4; /* gfx_v7_0_ring_emit_ib_gfx */
+}
+
+static unsigned gfx_v7_0_ring_get_dma_frame_size_gfx(struct amdgpu_ring *ring)
+{
+       return
+               20 + /* gfx_v7_0_ring_emit_gds_switch */
+               7 + /* gfx_v7_0_ring_emit_hdp_flush */
+               5 + /* gfx_v7_0_ring_emit_hdp_invalidate */
+               12 + 12 + 12 + /* gfx_v7_0_ring_emit_fence_gfx x3 for user fence, vm fence */
+               7 + 4 + /* gfx_v7_0_ring_emit_pipeline_sync */
+               17 + 6 + /* gfx_v7_0_ring_emit_vm_flush */
+               3; /* gfx_v7_ring_emit_cntxcntl */
+}
+
+static unsigned gfx_v7_0_ring_get_emit_ib_size_compute(struct amdgpu_ring *ring)
+{
+       return
+               4; /* gfx_v7_0_ring_emit_ib_compute */
+}
+
+static unsigned gfx_v7_0_ring_get_dma_frame_size_compute(struct amdgpu_ring *ring)
+{
+       return
+               20 + /* gfx_v7_0_ring_emit_gds_switch */
+               7 + /* gfx_v7_0_ring_emit_hdp_flush */
+               5 + /* gfx_v7_0_ring_emit_hdp_invalidate */
+               7 + /* gfx_v7_0_ring_emit_pipeline_sync */
+               17 + /* gfx_v7_0_ring_emit_vm_flush */
+               7 + 7 + 7; /* gfx_v7_0_ring_emit_fence_compute x3 for user fence, vm fence */
+}
+
 static const struct amdgpu_gfx_funcs gfx_v7_0_gfx_funcs = {
        .get_gpu_clock_counter = &gfx_v7_0_get_gpu_clock_counter,
        .select_se_sh = &gfx_v7_0_select_se_sh,
@@ -4471,24 +4678,21 @@ static int gfx_v7_0_sw_init(void *handle)
        }
 
        /* reserve GDS, GWS and OA resource for gfx */
-       r = amdgpu_bo_create(adev, adev->gds.mem.gfx_partition_size,
-                       PAGE_SIZE, true,
-                       AMDGPU_GEM_DOMAIN_GDS, 0,
-                       NULL, NULL, &adev->gds.gds_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.mem.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_GDS,
+                                   &adev->gds.gds_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
-       r = amdgpu_bo_create(adev, adev->gds.gws.gfx_partition_size,
-               PAGE_SIZE, true,
-               AMDGPU_GEM_DOMAIN_GWS, 0,
-               NULL, NULL, &adev->gds.gws_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.gws.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_GWS,
+                                   &adev->gds.gws_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
-       r = amdgpu_bo_create(adev, adev->gds.oa.gfx_partition_size,
-                       PAGE_SIZE, true,
-                       AMDGPU_GEM_DOMAIN_OA, 0,
-                       NULL, NULL, &adev->gds.oa_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.oa.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_OA,
+                                   &adev->gds.oa_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
@@ -4504,9 +4708,9 @@ static int gfx_v7_0_sw_fini(void *handle)
        int i;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       amdgpu_bo_unref(&adev->gds.oa_gfx_bo);
-       amdgpu_bo_unref(&adev->gds.gws_gfx_bo);
-       amdgpu_bo_unref(&adev->gds.gds_gfx_bo);
+       amdgpu_bo_free_kernel(&adev->gds.oa_gfx_bo, NULL, NULL);
+       amdgpu_bo_free_kernel(&adev->gds.gws_gfx_bo, NULL, NULL);
+       amdgpu_bo_free_kernel(&adev->gds.gds_gfx_bo, NULL, NULL);
 
        for (i = 0; i < adev->gfx.num_gfx_rings; i++)
                amdgpu_ring_fini(&adev->gfx.gfx_ring[i]);
@@ -4937,7 +5141,7 @@ const struct amd_ip_funcs gfx_v7_0_ip_funcs = {
 };
 
 static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = {
-       .get_rptr = gfx_v7_0_ring_get_rptr_gfx,
+       .get_rptr = gfx_v7_0_ring_get_rptr,
        .get_wptr = gfx_v7_0_ring_get_wptr_gfx,
        .set_wptr = gfx_v7_0_ring_set_wptr_gfx,
        .parse_cs = NULL,
@@ -4952,10 +5156,13 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_gfx = {
        .test_ib = gfx_v7_0_ring_test_ib,
        .insert_nop = amdgpu_ring_insert_nop,
        .pad_ib = amdgpu_ring_generic_pad_ib,
+       .emit_cntxcntl = gfx_v7_ring_emit_cntxcntl,
+       .get_emit_ib_size = gfx_v7_0_ring_get_emit_ib_size_gfx,
+       .get_dma_frame_size = gfx_v7_0_ring_get_dma_frame_size_gfx,
 };
 
 static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_compute = {
-       .get_rptr = gfx_v7_0_ring_get_rptr_compute,
+       .get_rptr = gfx_v7_0_ring_get_rptr,
        .get_wptr = gfx_v7_0_ring_get_wptr_compute,
        .set_wptr = gfx_v7_0_ring_set_wptr_compute,
        .parse_cs = NULL,
@@ -4970,6 +5177,8 @@ static const struct amdgpu_ring_funcs gfx_v7_0_ring_funcs_compute = {
        .test_ib = gfx_v7_0_ring_test_ib,
        .insert_nop = amdgpu_ring_insert_nop,
        .pad_ib = amdgpu_ring_generic_pad_ib,
+       .get_emit_ib_size = gfx_v7_0_ring_get_emit_ib_size_compute,
+       .get_dma_frame_size = gfx_v7_0_ring_get_dma_frame_size_compute,
 };
 
 static void gfx_v7_0_set_ring_funcs(struct amdgpu_device *adev)
index b818461..6c6ff57 100644 (file)
@@ -703,7 +703,10 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev)
                                                 polaris10_golden_common_all,
                                                 (const u32)ARRAY_SIZE(polaris10_golden_common_all));
                WREG32_SMC(ixCG_ACLK_CNTL, 0x0000001C);
-               if (adev->pdev->revision == 0xc7) {
+               if (adev->pdev->revision == 0xc7 &&
+                   ((adev->pdev->subsystem_device == 0xb37 && adev->pdev->subsystem_vendor == 0x1002) ||
+                    (adev->pdev->subsystem_device == 0x4a8 && adev->pdev->subsystem_vendor == 0x1043) ||
+                    (adev->pdev->subsystem_device == 0x9480 && adev->pdev->subsystem_vendor == 0x1682))) {
                        amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1E, 0xDD);
                        amdgpu_atombios_i2c_channel_trans(adev, 0x10, 0x96, 0x1F, 0xD0);
                }
@@ -1233,10 +1236,9 @@ static void gfx_v8_0_rlc_fini(struct amdgpu_device *adev)
        if (adev->gfx.rlc.clear_state_obj) {
                r = amdgpu_bo_reserve(adev->gfx.rlc.clear_state_obj, false);
                if (unlikely(r != 0))
-                       dev_warn(adev->dev, "(%d) reserve RLC c bo failed\n", r);
+                       dev_warn(adev->dev, "(%d) reserve RLC cbs bo failed\n", r);
                amdgpu_bo_unpin(adev->gfx.rlc.clear_state_obj);
                amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-
                amdgpu_bo_unref(&adev->gfx.rlc.clear_state_obj);
                adev->gfx.rlc.clear_state_obj = NULL;
        }
@@ -1248,7 +1250,6 @@ static void gfx_v8_0_rlc_fini(struct amdgpu_device *adev)
                        dev_warn(adev->dev, "(%d) reserve RLC cp table bo failed\n", r);
                amdgpu_bo_unpin(adev->gfx.rlc.cp_table_obj);
                amdgpu_bo_unreserve(adev->gfx.rlc.cp_table_obj);
-
                amdgpu_bo_unref(&adev->gfx.rlc.cp_table_obj);
                adev->gfx.rlc.cp_table_obj = NULL;
        }
@@ -1290,14 +1291,14 @@ static int gfx_v8_0_rlc_init(struct amdgpu_device *adev)
                                  &adev->gfx.rlc.clear_state_gpu_addr);
                if (r) {
                        amdgpu_bo_unreserve(adev->gfx.rlc.clear_state_obj);
-                       dev_warn(adev->dev, "(%d) pin RLC c bo failed\n", r);
+                       dev_warn(adev->dev, "(%d) pin RLC cbs bo failed\n", r);
                        gfx_v8_0_rlc_fini(adev);
                        return r;
                }
 
                r = amdgpu_bo_kmap(adev->gfx.rlc.clear_state_obj, (void **)&adev->gfx.rlc.cs_ptr);
                if (r) {
-                       dev_warn(adev->dev, "(%d) map RLC c bo failed\n", r);
+                       dev_warn(adev->dev, "(%d) map RLC cbs bo failed\n", r);
                        gfx_v8_0_rlc_fini(adev);
                        return r;
                }
@@ -1332,7 +1333,7 @@ static int gfx_v8_0_rlc_init(struct amdgpu_device *adev)
                                  &adev->gfx.rlc.cp_table_gpu_addr);
                if (r) {
                        amdgpu_bo_unreserve(adev->gfx.rlc.cp_table_obj);
-                       dev_warn(adev->dev, "(%d) pin RLC cp_table bo failed\n", r);
+                       dev_warn(adev->dev, "(%d) pin RLC cp table bo failed\n", r);
                        return r;
                }
                r = amdgpu_bo_kmap(adev->gfx.rlc.cp_table_obj, (void **)&adev->gfx.rlc.cp_table_ptr);
@@ -1345,7 +1346,6 @@ static int gfx_v8_0_rlc_init(struct amdgpu_device *adev)
 
                amdgpu_bo_kunmap(adev->gfx.rlc.cp_table_obj);
                amdgpu_bo_unreserve(adev->gfx.rlc.cp_table_obj);
-
        }
 
        return 0;
@@ -1361,7 +1361,6 @@ static void gfx_v8_0_mec_fini(struct amdgpu_device *adev)
                        dev_warn(adev->dev, "(%d) reserve HPD EOP bo failed\n", r);
                amdgpu_bo_unpin(adev->gfx.mec.hpd_eop_obj);
                amdgpu_bo_unreserve(adev->gfx.mec.hpd_eop_obj);
-
                amdgpu_bo_unref(&adev->gfx.mec.hpd_eop_obj);
                adev->gfx.mec.hpd_eop_obj = NULL;
        }
@@ -2082,24 +2081,21 @@ static int gfx_v8_0_sw_init(void *handle)
        }
 
        /* reserve GDS, GWS and OA resource for gfx */
-       r = amdgpu_bo_create(adev, adev->gds.mem.gfx_partition_size,
-                       PAGE_SIZE, true,
-                       AMDGPU_GEM_DOMAIN_GDS, 0, NULL,
-                       NULL, &adev->gds.gds_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.mem.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_GDS,
+                                   &adev->gds.gds_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
-       r = amdgpu_bo_create(adev, adev->gds.gws.gfx_partition_size,
-               PAGE_SIZE, true,
-               AMDGPU_GEM_DOMAIN_GWS, 0, NULL,
-               NULL, &adev->gds.gws_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.gws.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_GWS,
+                                   &adev->gds.gws_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
-       r = amdgpu_bo_create(adev, adev->gds.oa.gfx_partition_size,
-                       PAGE_SIZE, true,
-                       AMDGPU_GEM_DOMAIN_OA, 0, NULL,
-                       NULL, &adev->gds.oa_gfx_bo);
+       r = amdgpu_bo_create_kernel(adev, adev->gds.oa.gfx_partition_size,
+                                   PAGE_SIZE, AMDGPU_GEM_DOMAIN_OA,
+                                   &adev->gds.oa_gfx_bo, NULL, NULL);
        if (r)
                return r;
 
@@ -2117,9 +2113,9 @@ static int gfx_v8_0_sw_fini(void *handle)
        int i;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       amdgpu_bo_unref(&adev->gds.oa_gfx_bo);
-       amdgpu_bo_unref(&adev->gds.gws_gfx_bo);
-       amdgpu_bo_unref(&adev->gds.gds_gfx_bo);
+       amdgpu_bo_free_kernel(&adev->gds.oa_gfx_bo, NULL, NULL);
+       amdgpu_bo_free_kernel(&adev->gds.gws_gfx_bo, NULL, NULL);
+       amdgpu_bo_free_kernel(&adev->gds.gds_gfx_bo, NULL, NULL);
 
        for (i = 0; i < adev->gfx.num_gfx_rings; i++)
                amdgpu_ring_fini(&adev->gfx.gfx_ring[i]);
@@ -2127,9 +2123,7 @@ static int gfx_v8_0_sw_fini(void *handle)
                amdgpu_ring_fini(&adev->gfx.compute_ring[i]);
 
        gfx_v8_0_mec_fini(adev);
-
        gfx_v8_0_rlc_fini(adev);
-
        gfx_v8_0_free_microcode(adev);
 
        return 0;
@@ -3465,19 +3459,16 @@ static void gfx_v8_0_select_se_sh(struct amdgpu_device *adev,
        else
                data = REG_SET_FIELD(0, GRBM_GFX_INDEX, INSTANCE_INDEX, instance);
 
-       if ((se_num == 0xffffffff) && (sh_num == 0xffffffff)) {
-               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1);
+       if (se_num == 0xffffffff)
                data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_BROADCAST_WRITES, 1);
-       } else if (se_num == 0xffffffff) {
-               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_INDEX, sh_num);
-               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_BROADCAST_WRITES, 1);
-       } else if (sh_num == 0xffffffff) {
-               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1);
+       else
                data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_INDEX, se_num);
-       } else {
+
+       if (sh_num == 0xffffffff)
+               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1);
+       else
                data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_INDEX, sh_num);
-               data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_INDEX, se_num);
-       }
+
        WREG32(mmGRBM_GFX_INDEX, data);
 }
 
@@ -3490,11 +3481,10 @@ static u32 gfx_v8_0_get_rb_active_bitmap(struct amdgpu_device *adev)
 {
        u32 data, mask;
 
-       data = RREG32(mmCC_RB_BACKEND_DISABLE);
-       data |= RREG32(mmGC_USER_RB_BACKEND_DISABLE);
+       data =  RREG32(mmCC_RB_BACKEND_DISABLE) |
+               RREG32(mmGC_USER_RB_BACKEND_DISABLE);
 
-       data &= CC_RB_BACKEND_DISABLE__BACKEND_DISABLE_MASK;
-       data >>= GC_USER_RB_BACKEND_DISABLE__BACKEND_DISABLE__SHIFT;
+       data = REG_GET_FIELD(data, GC_USER_RB_BACKEND_DISABLE, BACKEND_DISABLE);
 
        mask = gfx_v8_0_create_bitmask(adev->gfx.config.max_backends_per_se /
                                       adev->gfx.config.max_sh_per_se);
@@ -3502,13 +3492,163 @@ static u32 gfx_v8_0_get_rb_active_bitmap(struct amdgpu_device *adev)
        return (~data) & mask;
 }
 
+static void
+gfx_v8_0_raster_config(struct amdgpu_device *adev, u32 *rconf, u32 *rconf1)
+{
+       switch (adev->asic_type) {
+       case CHIP_FIJI:
+               *rconf |= RB_MAP_PKR0(2) | RB_MAP_PKR1(2) |
+                         RB_XSEL2(1) | PKR_MAP(2) |
+                         PKR_XSEL(1) | PKR_YSEL(1) |
+                         SE_MAP(2) | SE_XSEL(2) | SE_YSEL(3);
+               *rconf1 |= SE_PAIR_MAP(2) | SE_PAIR_XSEL(3) |
+                          SE_PAIR_YSEL(2);
+               break;
+       case CHIP_TONGA:
+       case CHIP_POLARIS10:
+               *rconf |= RB_MAP_PKR0(2) | RB_XSEL2(1) | SE_MAP(2) |
+                         SE_XSEL(1) | SE_YSEL(1);
+               *rconf1 |= SE_PAIR_MAP(2) | SE_PAIR_XSEL(2) |
+                          SE_PAIR_YSEL(2);
+               break;
+       case CHIP_TOPAZ:
+       case CHIP_CARRIZO:
+               *rconf |= RB_MAP_PKR0(2);
+               *rconf1 |= 0x0;
+               break;
+       case CHIP_POLARIS11:
+               *rconf |= RB_MAP_PKR0(2) | RB_XSEL2(1) | SE_MAP(2) |
+                         SE_XSEL(1) | SE_YSEL(1);
+               *rconf1 |= 0x0;
+               break;
+       case CHIP_STONEY:
+               *rconf |= 0x0;
+               *rconf1 |= 0x0;
+               break;
+       default:
+               DRM_ERROR("unknown asic: 0x%x\n", adev->asic_type);
+               break;
+       }
+}
+
+static void
+gfx_v8_0_write_harvested_raster_configs(struct amdgpu_device *adev,
+                                       u32 raster_config, u32 raster_config_1,
+                                       unsigned rb_mask, unsigned num_rb)
+{
+       unsigned sh_per_se = max_t(unsigned, adev->gfx.config.max_sh_per_se, 1);
+       unsigned num_se = max_t(unsigned, adev->gfx.config.max_shader_engines, 1);
+       unsigned rb_per_pkr = min_t(unsigned, num_rb / num_se / sh_per_se, 2);
+       unsigned rb_per_se = num_rb / num_se;
+       unsigned se_mask[4];
+       unsigned se;
+
+       se_mask[0] = ((1 << rb_per_se) - 1) & rb_mask;
+       se_mask[1] = (se_mask[0] << rb_per_se) & rb_mask;
+       se_mask[2] = (se_mask[1] << rb_per_se) & rb_mask;
+       se_mask[3] = (se_mask[2] << rb_per_se) & rb_mask;
+
+       WARN_ON(!(num_se == 1 || num_se == 2 || num_se == 4));
+       WARN_ON(!(sh_per_se == 1 || sh_per_se == 2));
+       WARN_ON(!(rb_per_pkr == 1 || rb_per_pkr == 2));
+
+       if ((num_se > 2) && ((!se_mask[0] && !se_mask[1]) ||
+                            (!se_mask[2] && !se_mask[3]))) {
+               raster_config_1 &= ~SE_PAIR_MAP_MASK;
+
+               if (!se_mask[0] && !se_mask[1]) {
+                       raster_config_1 |=
+                               SE_PAIR_MAP(RASTER_CONFIG_SE_PAIR_MAP_3);
+               } else {
+                       raster_config_1 |=
+                               SE_PAIR_MAP(RASTER_CONFIG_SE_PAIR_MAP_0);
+               }
+       }
+
+       for (se = 0; se < num_se; se++) {
+               unsigned raster_config_se = raster_config;
+               unsigned pkr0_mask = ((1 << rb_per_pkr) - 1) << (se * rb_per_se);
+               unsigned pkr1_mask = pkr0_mask << rb_per_pkr;
+               int idx = (se / 2) * 2;
+
+               if ((num_se > 1) && (!se_mask[idx] || !se_mask[idx + 1])) {
+                       raster_config_se &= ~SE_MAP_MASK;
+
+                       if (!se_mask[idx]) {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_3);
+                       } else {
+                               raster_config_se |= SE_MAP(RASTER_CONFIG_SE_MAP_0);
+                       }
+               }
+
+               pkr0_mask &= rb_mask;
+               pkr1_mask &= rb_mask;
+               if (rb_per_se > 2 && (!pkr0_mask || !pkr1_mask)) {
+                       raster_config_se &= ~PKR_MAP_MASK;
+
+                       if (!pkr0_mask) {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_3);
+                       } else {
+                               raster_config_se |= PKR_MAP(RASTER_CONFIG_PKR_MAP_0);
+                       }
+               }
+
+               if (rb_per_se >= 2) {
+                       unsigned rb0_mask = 1 << (se * rb_per_se);
+                       unsigned rb1_mask = rb0_mask << 1;
+
+                       rb0_mask &= rb_mask;
+                       rb1_mask &= rb_mask;
+                       if (!rb0_mask || !rb1_mask) {
+                               raster_config_se &= ~RB_MAP_PKR0_MASK;
+
+                               if (!rb0_mask) {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_3);
+                               } else {
+                                       raster_config_se |=
+                                               RB_MAP_PKR0(RASTER_CONFIG_RB_MAP_0);
+                               }
+                       }
+
+                       if (rb_per_se > 2) {
+                               rb0_mask = 1 << (se * rb_per_se + rb_per_pkr);
+                               rb1_mask = rb0_mask << 1;
+                               rb0_mask &= rb_mask;
+                               rb1_mask &= rb_mask;
+                               if (!rb0_mask || !rb1_mask) {
+                                       raster_config_se &= ~RB_MAP_PKR1_MASK;
+
+                                       if (!rb0_mask) {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_3);
+                                       } else {
+                                               raster_config_se |=
+                                                       RB_MAP_PKR1(RASTER_CONFIG_RB_MAP_0);
+                                       }
+                               }
+                       }
+               }
+
+               /* GRBM_GFX_INDEX has a different offset on VI */
+               gfx_v8_0_select_se_sh(adev, se, 0xffffffff, 0xffffffff);
+               WREG32(mmPA_SC_RASTER_CONFIG, raster_config_se);
+               WREG32(mmPA_SC_RASTER_CONFIG_1, raster_config_1);
+       }
+
+       /* GRBM_GFX_INDEX has a different offset on VI */
+       gfx_v8_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+}
+
 static void gfx_v8_0_setup_rb(struct amdgpu_device *adev)
 {
        int i, j;
        u32 data;
+       u32 raster_config = 0, raster_config_1 = 0;
        u32 active_rbs = 0;
        u32 rb_bitmap_width_per_sh = adev->gfx.config.max_backends_per_se /
                                        adev->gfx.config.max_sh_per_se;
+       unsigned num_rb_pipes;
 
        mutex_lock(&adev->grbm_idx_mutex);
        for (i = 0; i < adev->gfx.config.max_shader_engines; i++) {
@@ -3520,10 +3660,26 @@ static void gfx_v8_0_setup_rb(struct amdgpu_device *adev)
                }
        }
        gfx_v8_0_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
-       mutex_unlock(&adev->grbm_idx_mutex);
 
        adev->gfx.config.backend_enable_mask = active_rbs;
        adev->gfx.config.num_rbs = hweight32(active_rbs);
+
+       num_rb_pipes = min_t(unsigned, adev->gfx.config.max_backends_per_se *
+                            adev->gfx.config.max_shader_engines, 16);
+
+       gfx_v8_0_raster_config(adev, &raster_config, &raster_config_1);
+
+       if (!adev->gfx.config.backend_enable_mask ||
+                       adev->gfx.config.num_rbs >= num_rb_pipes) {
+               WREG32(mmPA_SC_RASTER_CONFIG, raster_config);
+               WREG32(mmPA_SC_RASTER_CONFIG_1, raster_config_1);
+       } else {
+               gfx_v8_0_write_harvested_raster_configs(adev, raster_config, raster_config_1,
+                                                       adev->gfx.config.backend_enable_mask,
+                                                       num_rb_pipes);
+       }
+
+       mutex_unlock(&adev->grbm_idx_mutex);
 }
 
 /**
@@ -3576,16 +3732,12 @@ static void gfx_v8_0_gpu_init(struct amdgpu_device *adev)
        u32 tmp;
        int i;
 
-       tmp = RREG32(mmGRBM_CNTL);
-       tmp = REG_SET_FIELD(tmp, GRBM_CNTL, READ_TIMEOUT, 0xff);
-       WREG32(mmGRBM_CNTL, tmp);
-
+       WREG32_FIELD(GRBM_CNTL, READ_TIMEOUT, 0xFF);
        WREG32(mmGB_ADDR_CONFIG, adev->gfx.config.gb_addr_config);
        WREG32(mmHDP_ADDR_CONFIG, adev->gfx.config.gb_addr_config);
        WREG32(mmDMIF_ADDR_CALC, adev->gfx.config.gb_addr_config);
 
        gfx_v8_0_tiling_mode_table_init(adev);
-
        gfx_v8_0_setup_rb(adev);
        gfx_v8_0_get_cu_info(adev);
 
@@ -3769,9 +3921,7 @@ static int gfx_v8_0_init_save_restore_list(struct amdgpu_device *adev)
                                sizeof(indirect_start_offsets)/sizeof(int));
 
        /* save and restore list */
-       temp = RREG32(mmRLC_SRM_CNTL);
-       temp |= RLC_SRM_CNTL__AUTO_INCR_ADDR_MASK;
-       WREG32(mmRLC_SRM_CNTL, temp);
+       WREG32_FIELD(RLC_SRM_CNTL, AUTO_INCR_ADDR, 1);
 
        WREG32(mmRLC_SRM_ARAM_ADDR, 0);
        for (i = 0; i < adev->gfx.rlc.reg_list_size_bytes >> 2; i++)
@@ -3808,11 +3958,7 @@ static int gfx_v8_0_init_save_restore_list(struct amdgpu_device *adev)
 
 static void gfx_v8_0_enable_save_restore_machine(struct amdgpu_device *adev)
 {
-       uint32_t data;
-
-       data = RREG32(mmRLC_SRM_CNTL);
-       data |= RLC_SRM_CNTL__SRM_ENABLE_MASK;
-       WREG32(mmRLC_SRM_CNTL, data);
+       WREG32_FIELD(RLC_SRM_CNTL, SRM_ENABLE, 1);
 }
 
 static void gfx_v8_0_init_power_gating(struct amdgpu_device *adev)
@@ -3822,75 +3968,34 @@ static void gfx_v8_0_init_power_gating(struct amdgpu_device *adev)
        if (adev->pg_flags & (AMD_PG_SUPPORT_GFX_PG |
                              AMD_PG_SUPPORT_GFX_SMG |
                              AMD_PG_SUPPORT_GFX_DMG)) {
-               data = RREG32(mmCP_RB_WPTR_POLL_CNTL);
-               data &= ~CP_RB_WPTR_POLL_CNTL__IDLE_POLL_COUNT_MASK;
-               data |= (0x60 << CP_RB_WPTR_POLL_CNTL__IDLE_POLL_COUNT__SHIFT);
-               WREG32(mmCP_RB_WPTR_POLL_CNTL, data);
-
-               data = 0;
-               data |= (0x10 << RLC_PG_DELAY__POWER_UP_DELAY__SHIFT);
-               data |= (0x10 << RLC_PG_DELAY__POWER_DOWN_DELAY__SHIFT);
-               data |= (0x10 << RLC_PG_DELAY__CMD_PROPAGATE_DELAY__SHIFT);
-               data |= (0x10 << RLC_PG_DELAY__MEM_SLEEP_DELAY__SHIFT);
-               WREG32(mmRLC_PG_DELAY, data);
+               WREG32_FIELD(CP_RB_WPTR_POLL_CNTL, IDLE_POLL_COUNT, 0x60);
 
-               data = RREG32(mmRLC_PG_DELAY_2);
-               data &= ~RLC_PG_DELAY_2__SERDES_CMD_DELAY_MASK;
-               data |= (0x3 << RLC_PG_DELAY_2__SERDES_CMD_DELAY__SHIFT);
-               WREG32(mmRLC_PG_DELAY_2, data);
+               data = REG_SET_FIELD(0, RLC_PG_DELAY, POWER_UP_DELAY, 0x10);
+               data = REG_SET_FIELD(data, RLC_PG_DELAY, POWER_DOWN_DELAY, 0x10);
+               data = REG_SET_FIELD(data, RLC_PG_DELAY, CMD_PROPAGATE_DELAY, 0x10);
+               data = REG_SET_FIELD(data, RLC_PG_DELAY, MEM_SLEEP_DELAY, 0x10);
+               WREG32(mmRLC_PG_DELAY, data);
 
-               data = RREG32(mmRLC_AUTO_PG_CTRL);
-               data &= ~RLC_AUTO_PG_CTRL__GRBM_REG_SAVE_GFX_IDLE_THRESHOLD_MASK;
-               data |= (0x55f0 << RLC_AUTO_PG_CTRL__GRBM_REG_SAVE_GFX_IDLE_THRESHOLD__SHIFT);
-               WREG32(mmRLC_AUTO_PG_CTRL, data);
+               WREG32_FIELD(RLC_PG_DELAY_2, SERDES_CMD_DELAY, 0x3);
+               WREG32_FIELD(RLC_AUTO_PG_CTRL, GRBM_REG_SAVE_GFX_IDLE_THRESHOLD, 0x55f0);
        }
 }
 
 static void cz_enable_sck_slow_down_on_power_up(struct amdgpu_device *adev,
                                                bool enable)
 {
-       u32 data, orig;
-
-       orig = data = RREG32(mmRLC_PG_CNTL);
-
-       if (enable)
-               data |= RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PU_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PU_ENABLE_MASK;
-
-       if (orig != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, SMU_CLK_SLOWDOWN_ON_PU_ENABLE, enable ? 1 : 0);
 }
 
 static void cz_enable_sck_slow_down_on_power_down(struct amdgpu_device *adev,
                                                  bool enable)
 {
-       u32 data, orig;
-
-       orig = data = RREG32(mmRLC_PG_CNTL);
-
-       if (enable)
-               data |= RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PD_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__SMU_CLK_SLOWDOWN_ON_PD_ENABLE_MASK;
-
-       if (orig != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, SMU_CLK_SLOWDOWN_ON_PD_ENABLE, enable ? 1 : 0);
 }
 
 static void cz_enable_cp_power_gating(struct amdgpu_device *adev, bool enable)
 {
-       u32 data, orig;
-
-       orig = data = RREG32(mmRLC_PG_CNTL);
-
-       if (enable)
-               data &= ~RLC_PG_CNTL__CP_PG_DISABLE_MASK;
-       else
-               data |= RLC_PG_CNTL__CP_PG_DISABLE_MASK;
-
-       if (orig != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, CP_PG_DISABLE, enable ? 1 : 0);
 }
 
 static void gfx_v8_0_init_pg(struct amdgpu_device *adev)
@@ -3927,36 +4032,26 @@ static void gfx_v8_0_init_pg(struct amdgpu_device *adev)
        }
 }
 
-void gfx_v8_0_rlc_stop(struct amdgpu_device *adev)
+static void gfx_v8_0_rlc_stop(struct amdgpu_device *adev)
 {
-       u32 tmp = RREG32(mmRLC_CNTL);
-
-       tmp = REG_SET_FIELD(tmp, RLC_CNTL, RLC_ENABLE_F32, 0);
-       WREG32(mmRLC_CNTL, tmp);
+       WREG32_FIELD(RLC_CNTL, RLC_ENABLE_F32, 0);
 
        gfx_v8_0_enable_gui_idle_interrupt(adev, false);
-
        gfx_v8_0_wait_for_rlc_serdes(adev);
 }
 
 static void gfx_v8_0_rlc_reset(struct amdgpu_device *adev)
 {
-       u32 tmp = RREG32(mmGRBM_SOFT_RESET);
-
-       tmp = REG_SET_FIELD(tmp, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1);
-       WREG32(mmGRBM_SOFT_RESET, tmp);
+       WREG32_FIELD(GRBM_SOFT_RESET, SOFT_RESET_RLC, 1);
        udelay(50);
-       tmp = REG_SET_FIELD(tmp, GRBM_SOFT_RESET, SOFT_RESET_RLC, 0);
-       WREG32(mmGRBM_SOFT_RESET, tmp);
+
+       WREG32_FIELD(GRBM_SOFT_RESET, SOFT_RESET_RLC, 0);
        udelay(50);
 }
 
 static void gfx_v8_0_rlc_start(struct amdgpu_device *adev)
 {
-       u32 tmp = RREG32(mmRLC_CNTL);
-
-       tmp = REG_SET_FIELD(tmp, RLC_CNTL, RLC_ENABLE_F32, 1);
-       WREG32(mmRLC_CNTL, tmp);
+       WREG32_FIELD(RLC_CNTL, RLC_ENABLE_F32, 1);
 
        /* carrizo do enable cp interrupt after cp inited */
        if (!(adev->flags & AMD_IS_APU))
@@ -3998,14 +4093,13 @@ static int gfx_v8_0_rlc_resume(struct amdgpu_device *adev)
        /* disable CG */
        WREG32(mmRLC_CGCG_CGLS_CTRL, 0);
        if (adev->asic_type == CHIP_POLARIS11 ||
-               adev->asic_type == CHIP_POLARIS10)
+           adev->asic_type == CHIP_POLARIS10)
                WREG32(mmRLC_CGCG_CGLS_CTRL_3D, 0);
 
        /* disable PG */
        WREG32(mmRLC_PG_CNTL, 0);
 
        gfx_v8_0_rlc_reset(adev);
-
        gfx_v8_0_init_pg(adev);
 
        if (!adev->pp_enabled) {
@@ -4300,12 +4394,10 @@ static int gfx_v8_0_cp_gfx_resume(struct amdgpu_device *adev)
        gfx_v8_0_cp_gfx_start(adev);
        ring->ready = true;
        r = amdgpu_ring_test_ring(ring);
-       if (r) {
+       if (r)
                ring->ready = false;
-               return r;
-       }
 
-       return 0;
+       return r;
 }
 
 static void gfx_v8_0_cp_compute_enable(struct amdgpu_device *adev, bool enable)
@@ -4980,7 +5072,6 @@ static int gfx_v8_0_hw_init(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        gfx_v8_0_init_golden_registers(adev);
-
        gfx_v8_0_gpu_init(adev);
 
        r = gfx_v8_0_rlc_resume(adev);
@@ -4988,8 +5079,6 @@ static int gfx_v8_0_hw_init(void *handle)
                return r;
 
        r = gfx_v8_0_cp_resume(adev);
-       if (r)
-               return r;
 
        return r;
 }
@@ -5037,25 +5126,22 @@ static bool gfx_v8_0_is_idle(void *handle)
 static int gfx_v8_0_wait_for_idle(void *handle)
 {
        unsigned i;
-       u32 tmp;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        for (i = 0; i < adev->usec_timeout; i++) {
-               /* read MC_STATUS */
-               tmp = RREG32(mmGRBM_STATUS) & GRBM_STATUS__GUI_ACTIVE_MASK;
-
-               if (!REG_GET_FIELD(tmp, GRBM_STATUS, GUI_ACTIVE))
+               if (gfx_v8_0_is_idle(handle))
                        return 0;
+
                udelay(1);
        }
        return -ETIMEDOUT;
 }
 
-static int gfx_v8_0_soft_reset(void *handle)
+static int gfx_v8_0_check_soft_reset(void *handle)
 {
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
        u32 tmp;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        /* GRBM_STATUS */
        tmp = RREG32(mmGRBM_STATUS);
@@ -5064,16 +5150,12 @@ static int gfx_v8_0_soft_reset(void *handle)
                   GRBM_STATUS__TA_BUSY_MASK | GRBM_STATUS__VGT_BUSY_MASK |
                   GRBM_STATUS__DB_BUSY_MASK | GRBM_STATUS__CB_BUSY_MASK |
                   GRBM_STATUS__GDS_BUSY_MASK | GRBM_STATUS__SPI_BUSY_MASK |
-                  GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK)) {
+                  GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK |
+                  GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK)) {
                grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
                                                GRBM_SOFT_RESET, SOFT_RESET_CP, 1);
                grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
                                                GRBM_SOFT_RESET, SOFT_RESET_GFX, 1);
-       }
-
-       if (tmp & (GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK)) {
-               grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
-                                               GRBM_SOFT_RESET, SOFT_RESET_CP, 1);
                srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
                                                SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
        }
@@ -5084,73 +5166,199 @@ static int gfx_v8_0_soft_reset(void *handle)
                grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset,
                                                GRBM_SOFT_RESET, SOFT_RESET_RLC, 1);
 
+       if (REG_GET_FIELD(tmp, GRBM_STATUS2, CPF_BUSY) ||
+           REG_GET_FIELD(tmp, GRBM_STATUS2, CPC_BUSY) ||
+           REG_GET_FIELD(tmp, GRBM_STATUS2, CPG_BUSY)) {
+               grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
+                                               SOFT_RESET_CPF, 1);
+               grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
+                                               SOFT_RESET_CPC, 1);
+               grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET,
+                                               SOFT_RESET_CPG, 1);
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
+                                               SOFT_RESET_GRBM, 1);
+       }
+
        /* SRBM_STATUS */
        tmp = RREG32(mmSRBM_STATUS);
        if (REG_GET_FIELD(tmp, SRBM_STATUS, GRBM_RQ_PENDING))
                srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
                                                SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
+       if (REG_GET_FIELD(tmp, SRBM_STATUS, SEM_BUSY))
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
+                                               SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
 
        if (grbm_soft_reset || srbm_soft_reset) {
-               /* stop the rlc */
-               gfx_v8_0_rlc_stop(adev);
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang = true;
+               adev->gfx.grbm_soft_reset = grbm_soft_reset;
+               adev->gfx.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang = false;
+               adev->gfx.grbm_soft_reset = 0;
+               adev->gfx.srbm_soft_reset = 0;
+       }
+
+       return 0;
+}
+
+static void gfx_v8_0_inactive_hqd(struct amdgpu_device *adev,
+                                 struct amdgpu_ring *ring)
+{
+       int i;
+
+       vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
+       if (RREG32(mmCP_HQD_ACTIVE) & CP_HQD_ACTIVE__ACTIVE_MASK) {
+               u32 tmp;
+               tmp = RREG32(mmCP_HQD_DEQUEUE_REQUEST);
+               tmp = REG_SET_FIELD(tmp, CP_HQD_DEQUEUE_REQUEST,
+                                   DEQUEUE_REQ, 2);
+               WREG32(mmCP_HQD_DEQUEUE_REQUEST, tmp);
+               for (i = 0; i < adev->usec_timeout; i++) {
+                       if (!(RREG32(mmCP_HQD_ACTIVE) & CP_HQD_ACTIVE__ACTIVE_MASK))
+                               break;
+                       udelay(1);
+               }
+       }
+}
+
+static int gfx_v8_0_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+               return 0;
+
+       grbm_soft_reset = adev->gfx.grbm_soft_reset;
+       srbm_soft_reset = adev->gfx.srbm_soft_reset;
+
+       /* stop the rlc */
+       gfx_v8_0_rlc_stop(adev);
 
+       if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX))
                /* Disable GFX parsing/prefetching */
                gfx_v8_0_cp_gfx_enable(adev, false);
 
+       if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) {
+               int i;
+
+               for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+                       struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
+
+                       gfx_v8_0_inactive_hqd(adev, ring);
+               }
                /* Disable MEC parsing/prefetching */
                gfx_v8_0_cp_compute_enable(adev, false);
+       }
 
-               if (grbm_soft_reset || srbm_soft_reset) {
-                       tmp = RREG32(mmGMCON_DEBUG);
-                       tmp = REG_SET_FIELD(tmp,
-                                           GMCON_DEBUG, GFX_STALL, 1);
-                       tmp = REG_SET_FIELD(tmp,
-                                           GMCON_DEBUG, GFX_CLEAR, 1);
-                       WREG32(mmGMCON_DEBUG, tmp);
+       return 0;
+}
 
-                       udelay(50);
-               }
+static int gfx_v8_0_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
+       u32 tmp;
 
-               if (grbm_soft_reset) {
-                       tmp = RREG32(mmGRBM_SOFT_RESET);
-                       tmp |= grbm_soft_reset;
-                       dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp);
-                       WREG32(mmGRBM_SOFT_RESET, tmp);
-                       tmp = RREG32(mmGRBM_SOFT_RESET);
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+               return 0;
 
-                       udelay(50);
+       grbm_soft_reset = adev->gfx.grbm_soft_reset;
+       srbm_soft_reset = adev->gfx.srbm_soft_reset;
 
-                       tmp &= ~grbm_soft_reset;
-                       WREG32(mmGRBM_SOFT_RESET, tmp);
-                       tmp = RREG32(mmGRBM_SOFT_RESET);
-               }
+       if (grbm_soft_reset || srbm_soft_reset) {
+               tmp = RREG32(mmGMCON_DEBUG);
+               tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 1);
+               tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 1);
+               WREG32(mmGMCON_DEBUG, tmp);
+               udelay(50);
+       }
 
-               if (srbm_soft_reset) {
-                       tmp = RREG32(mmSRBM_SOFT_RESET);
-                       tmp |= srbm_soft_reset;
-                       dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
-                       WREG32(mmSRBM_SOFT_RESET, tmp);
-                       tmp = RREG32(mmSRBM_SOFT_RESET);
+       if (grbm_soft_reset) {
+               tmp = RREG32(mmGRBM_SOFT_RESET);
+               tmp |= grbm_soft_reset;
+               dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(mmGRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmGRBM_SOFT_RESET);
 
-                       udelay(50);
+               udelay(50);
 
-                       tmp &= ~srbm_soft_reset;
-                       WREG32(mmSRBM_SOFT_RESET, tmp);
-                       tmp = RREG32(mmSRBM_SOFT_RESET);
-               }
+               tmp &= ~grbm_soft_reset;
+               WREG32(mmGRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmGRBM_SOFT_RESET);
+       }
 
-               if (grbm_soft_reset || srbm_soft_reset) {
-                       tmp = RREG32(mmGMCON_DEBUG);
-                       tmp = REG_SET_FIELD(tmp,
-                                           GMCON_DEBUG, GFX_STALL, 0);
-                       tmp = REG_SET_FIELD(tmp,
-                                           GMCON_DEBUG, GFX_CLEAR, 0);
-                       WREG32(mmGMCON_DEBUG, tmp);
-               }
+       if (srbm_soft_reset) {
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
 
-               /* Wait a little for things to settle down */
                udelay(50);
+
+               tmp &= ~srbm_soft_reset;
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+       }
+
+       if (grbm_soft_reset || srbm_soft_reset) {
+               tmp = RREG32(mmGMCON_DEBUG);
+               tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 0);
+               tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 0);
+               WREG32(mmGMCON_DEBUG, tmp);
        }
+
+       /* Wait a little for things to settle down */
+       udelay(50);
+
+       return 0;
+}
+
+static void gfx_v8_0_init_hqd(struct amdgpu_device *adev,
+                             struct amdgpu_ring *ring)
+{
+       vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0);
+       WREG32(mmCP_HQD_DEQUEUE_REQUEST, 0);
+       WREG32(mmCP_HQD_PQ_RPTR, 0);
+       WREG32(mmCP_HQD_PQ_WPTR, 0);
+       vi_srbm_select(adev, 0, 0, 0, 0);
+}
+
+static int gfx_v8_0_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GFX].hang)
+               return 0;
+
+       grbm_soft_reset = adev->gfx.grbm_soft_reset;
+       srbm_soft_reset = adev->gfx.srbm_soft_reset;
+
+       if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX))
+               gfx_v8_0_cp_gfx_resume(adev);
+
+       if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) ||
+           REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) {
+               int i;
+
+               for (i = 0; i < adev->gfx.num_compute_rings; i++) {
+                       struct amdgpu_ring *ring = &adev->gfx.compute_ring[i];
+
+                       gfx_v8_0_init_hqd(adev, ring);
+               }
+               gfx_v8_0_cp_compute_resume(adev);
+       }
+       gfx_v8_0_rlc_start(adev);
+
        return 0;
 }
 
@@ -5269,8 +5477,6 @@ static int gfx_v8_0_late_init(void *handle)
 static void gfx_v8_0_enable_gfx_static_mg_power_gating(struct amdgpu_device *adev,
                                                       bool enable)
 {
-       uint32_t data, temp;
-
        if (adev->asic_type == CHIP_POLARIS11)
                /* Send msg to SMU via Powerplay */
                amdgpu_set_powergating_state(adev,
@@ -5278,83 +5484,35 @@ static void gfx_v8_0_enable_gfx_static_mg_power_gating(struct amdgpu_device *ade
                                             enable ?
                                             AMD_PG_STATE_GATE : AMD_PG_STATE_UNGATE);
 
-       temp = data = RREG32(mmRLC_PG_CNTL);
-       /* Enable static MGPG */
-       if (enable)
-               data |= RLC_PG_CNTL__STATIC_PER_CU_PG_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__STATIC_PER_CU_PG_ENABLE_MASK;
-
-       if (temp != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, STATIC_PER_CU_PG_ENABLE, enable ? 1 : 0);
 }
 
 static void gfx_v8_0_enable_gfx_dynamic_mg_power_gating(struct amdgpu_device *adev,
                                                        bool enable)
 {
-       uint32_t data, temp;
-
-       temp = data = RREG32(mmRLC_PG_CNTL);
-       /* Enable dynamic MGPG */
-       if (enable)
-               data |= RLC_PG_CNTL__DYN_PER_CU_PG_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__DYN_PER_CU_PG_ENABLE_MASK;
-
-       if (temp != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, DYN_PER_CU_PG_ENABLE, enable ? 1 : 0);
 }
 
 static void polaris11_enable_gfx_quick_mg_power_gating(struct amdgpu_device *adev,
                bool enable)
 {
-       uint32_t data, temp;
-
-       temp = data = RREG32(mmRLC_PG_CNTL);
-       /* Enable quick PG */
-       if (enable)
-               data |= RLC_PG_CNTL__QUICK_PG_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__QUICK_PG_ENABLE_MASK;
-
-       if (temp != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, QUICK_PG_ENABLE, enable ? 1 : 0);
 }
 
 static void cz_enable_gfx_cg_power_gating(struct amdgpu_device *adev,
                                          bool enable)
 {
-       u32 data, orig;
-
-       orig = data = RREG32(mmRLC_PG_CNTL);
-
-       if (enable)
-               data |= RLC_PG_CNTL__GFX_POWER_GATING_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__GFX_POWER_GATING_ENABLE_MASK;
-
-       if (orig != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, GFX_POWER_GATING_ENABLE, enable ? 1 : 0);
 }
 
 static void cz_enable_gfx_pipeline_power_gating(struct amdgpu_device *adev,
                                                bool enable)
 {
-       u32 data, orig;
-
-       orig = data = RREG32(mmRLC_PG_CNTL);
-
-       if (enable)
-               data |= RLC_PG_CNTL__GFX_PIPELINE_PG_ENABLE_MASK;
-       else
-               data &= ~RLC_PG_CNTL__GFX_PIPELINE_PG_ENABLE_MASK;
-
-       if (orig != data)
-               WREG32(mmRLC_PG_CNTL, data);
+       WREG32_FIELD(RLC_PG_CNTL, GFX_PIPELINE_PG_ENABLE, enable ? 1 : 0);
 
        /* Read any GFX register to wake up GFX. */
        if (!enable)
-               data = RREG32(mmDB_RENDER_CONTROL);
+               RREG32(mmDB_RENDER_CONTROL);
 }
 
 static void cz_update_gfx_cg_power_gating(struct amdgpu_device *adev,
@@ -5430,15 +5588,15 @@ static void gfx_v8_0_send_serdes_cmd(struct amdgpu_device *adev,
 
        data = RREG32(mmRLC_SERDES_WR_CTRL);
        if (adev->asic_type == CHIP_STONEY)
-                       data &= ~(RLC_SERDES_WR_CTRL__WRITE_COMMAND_MASK |
-                       RLC_SERDES_WR_CTRL__READ_COMMAND_MASK |
-                       RLC_SERDES_WR_CTRL__P1_SELECT_MASK |
-                       RLC_SERDES_WR_CTRL__P2_SELECT_MASK |
-                       RLC_SERDES_WR_CTRL__RDDATA_RESET_MASK |
-                       RLC_SERDES_WR_CTRL__POWER_DOWN_MASK |
-                       RLC_SERDES_WR_CTRL__POWER_UP_MASK |
-                       RLC_SERDES_WR_CTRL__SHORT_FORMAT_MASK |
-                       RLC_SERDES_WR_CTRL__SRBM_OVERRIDE_MASK);
+               data &= ~(RLC_SERDES_WR_CTRL__WRITE_COMMAND_MASK |
+                         RLC_SERDES_WR_CTRL__READ_COMMAND_MASK |
+                         RLC_SERDES_WR_CTRL__P1_SELECT_MASK |
+                         RLC_SERDES_WR_CTRL__P2_SELECT_MASK |
+                         RLC_SERDES_WR_CTRL__RDDATA_RESET_MASK |
+                         RLC_SERDES_WR_CTRL__POWER_DOWN_MASK |
+                         RLC_SERDES_WR_CTRL__POWER_UP_MASK |
+                         RLC_SERDES_WR_CTRL__SHORT_FORMAT_MASK |
+                         RLC_SERDES_WR_CTRL__SRBM_OVERRIDE_MASK);
        else
                data &= ~(RLC_SERDES_WR_CTRL__WRITE_COMMAND_MASK |
                          RLC_SERDES_WR_CTRL__READ_COMMAND_MASK |
@@ -5461,10 +5619,10 @@ static void gfx_v8_0_send_serdes_cmd(struct amdgpu_device *adev,
 
 #define MSG_ENTER_RLC_SAFE_MODE     1
 #define MSG_EXIT_RLC_SAFE_MODE      0
-
-#define RLC_GPR_REG2__REQ_MASK           0x00000001
-#define RLC_GPR_REG2__MESSAGE__SHIFT     0x00000001
-#define RLC_GPR_REG2__MESSAGE_MASK       0x0000001e
+#define RLC_GPR_REG2__REQ_MASK 0x00000001
+#define RLC_GPR_REG2__REQ__SHIFT 0
+#define RLC_GPR_REG2__MESSAGE__SHIFT 0x00000001
+#define RLC_GPR_REG2__MESSAGE_MASK 0x0000001e
 
 static void cz_enter_rlc_safe_mode(struct amdgpu_device *adev)
 {
@@ -5494,7 +5652,7 @@ static void cz_enter_rlc_safe_mode(struct amdgpu_device *adev)
                }
 
                for (i = 0; i < adev->usec_timeout; i++) {
-                       if ((RREG32(mmRLC_GPR_REG2) & RLC_GPR_REG2__REQ_MASK) == 0)
+                       if (!REG_GET_FIELD(RREG32(mmRLC_GPR_REG2), RLC_GPR_REG2, REQ))
                                break;
                        udelay(1);
                }
@@ -5522,7 +5680,7 @@ static void cz_exit_rlc_safe_mode(struct amdgpu_device *adev)
        }
 
        for (i = 0; i < adev->usec_timeout; i++) {
-               if ((RREG32(mmRLC_GPR_REG2) & RLC_GPR_REG2__REQ_MASK) == 0)
+               if (!REG_GET_FIELD(RREG32(mmRLC_GPR_REG2), RLC_GPR_REG2, REQ))
                        break;
                udelay(1);
        }
@@ -5554,7 +5712,7 @@ static void iceland_enter_rlc_safe_mode(struct amdgpu_device *adev)
                }
 
                for (i = 0; i < adev->usec_timeout; i++) {
-                       if ((RREG32(mmRLC_SAFE_MODE) & RLC_SAFE_MODE__CMD_MASK) == 0)
+                       if (!REG_GET_FIELD(RREG32(mmRLC_SAFE_MODE), RLC_SAFE_MODE, CMD))
                                break;
                        udelay(1);
                }
@@ -5581,7 +5739,7 @@ static void iceland_exit_rlc_safe_mode(struct amdgpu_device *adev)
        }
 
        for (i = 0; i < adev->usec_timeout; i++) {
-               if ((RREG32(mmRLC_SAFE_MODE) & RLC_SAFE_MODE__CMD_MASK) == 0)
+               if (!REG_GET_FIELD(RREG32(mmRLC_SAFE_MODE), RLC_SAFE_MODE, CMD))
                        break;
                udelay(1);
        }
@@ -5622,21 +5780,12 @@ static void gfx_v8_0_update_medium_grain_clock_gating(struct amdgpu_device *adev
        /* It is disabled by HW by default */
        if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_MGCG)) {
                if (adev->cg_flags & AMD_CG_SUPPORT_GFX_MGLS) {
-                       if (adev->cg_flags & AMD_CG_SUPPORT_GFX_RLC_LS) {
+                       if (adev->cg_flags & AMD_CG_SUPPORT_GFX_RLC_LS)
                                /* 1 - RLC memory Light sleep */
-                               temp = data = RREG32(mmRLC_MEM_SLP_CNTL);
-                               data |= RLC_MEM_SLP_CNTL__RLC_MEM_LS_EN_MASK;
-                               if (temp != data)
-                                       WREG32(mmRLC_MEM_SLP_CNTL, data);
-                       }
+                               WREG32_FIELD(RLC_MEM_SLP_CNTL, RLC_MEM_LS_EN, 1);
 
-                       if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CP_LS) {
-                               /* 2 - CP memory Light sleep */
-                               temp = data = RREG32(mmCP_MEM_SLP_CNTL);
-                               data |= CP_MEM_SLP_CNTL__CP_MEM_LS_EN_MASK;
-                               if (temp != data)
-                                       WREG32(mmCP_MEM_SLP_CNTL, data);
-                       }
+                       if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CP_LS)
+                               WREG32_FIELD(CP_MEM_SLP_CNTL, CP_MEM_LS_EN, 1);
                }
 
                /* 3 - RLC_CGTT_MGCG_OVERRIDE */
@@ -5834,6 +5983,76 @@ static int gfx_v8_0_update_gfx_clock_gating(struct amdgpu_device *adev,
        return 0;
 }
 
+static int gfx_v8_0_tonga_update_gfx_clock_gating(struct amdgpu_device *adev,
+                                         enum amd_clockgating_state state)
+{
+       uint32_t msg_id, pp_state;
+       void *pp_handle = adev->powerplay.pp_handle;
+
+       if (state == AMD_CG_STATE_UNGATE)
+               pp_state = 0;
+       else
+               pp_state = PP_STATE_CG | PP_STATE_LS;
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_CG,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_MG,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       return 0;
+}
+
+static int gfx_v8_0_polaris_update_gfx_clock_gating(struct amdgpu_device *adev,
+                                         enum amd_clockgating_state state)
+{
+       uint32_t msg_id, pp_state;
+       void *pp_handle = adev->powerplay.pp_handle;
+
+       if (state == AMD_CG_STATE_UNGATE)
+               pp_state = 0;
+       else
+               pp_state = PP_STATE_CG | PP_STATE_LS;
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_CG,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_3D,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_MG,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_RLC,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
+                       PP_BLOCK_GFX_CP,
+                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                       pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       return 0;
+}
+
 static int gfx_v8_0_set_clockgating_state(void *handle,
                                          enum amd_clockgating_state state)
 {
@@ -5846,33 +6065,33 @@ static int gfx_v8_0_set_clockgating_state(void *handle,
                gfx_v8_0_update_gfx_clock_gating(adev,
                                                 state == AMD_CG_STATE_GATE ? true : false);
                break;
+       case CHIP_TONGA:
+               gfx_v8_0_tonga_update_gfx_clock_gating(adev, state);
+               break;
+       case CHIP_POLARIS10:
+       case CHIP_POLARIS11:
+               gfx_v8_0_polaris_update_gfx_clock_gating(adev, state);
+               break;
        default:
                break;
        }
        return 0;
 }
 
-static u32 gfx_v8_0_ring_get_rptr_gfx(struct amdgpu_ring *ring)
+static u32 gfx_v8_0_ring_get_rptr(struct amdgpu_ring *ring)
 {
-       u32 rptr;
-
-       rptr = ring->adev->wb.wb[ring->rptr_offs];
-
-       return rptr;
+       return ring->adev->wb.wb[ring->rptr_offs];
 }
 
 static u32 gfx_v8_0_ring_get_wptr_gfx(struct amdgpu_ring *ring)
 {
        struct amdgpu_device *adev = ring->adev;
-       u32 wptr;
 
        if (ring->use_doorbell)
                /* XXX check if swapping is necessary on BE */
-               wptr = ring->adev->wb.wb[ring->wptr_offs];
+               return ring->adev->wb.wb[ring->wptr_offs];
        else
-               wptr = RREG32(mmCP_RB0_WPTR);
-
-       return wptr;
+               return RREG32(mmCP_RB0_WPTR);
 }
 
 static void gfx_v8_0_ring_set_wptr_gfx(struct amdgpu_ring *ring)
@@ -5939,12 +6158,6 @@ static void gfx_v8_0_ring_emit_ib_gfx(struct amdgpu_ring *ring,
 {
        u32 header, control = 0;
 
-       /* insert SWITCH_BUFFER packet before first IB in the ring frame */
-       if (ctx_switch) {
-               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-               amdgpu_ring_write(ring, 0);
-       }
-
        if (ib->flags & AMDGPU_IB_FLAG_CE)
                header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
        else
@@ -5971,9 +6184,9 @@ static void gfx_v8_0_ring_emit_ib_compute(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2));
        amdgpu_ring_write(ring,
 #ifdef __BIG_ENDIAN
-                                         (2 << 0) |
+                               (2 << 0) |
 #endif
-                                         (ib->gpu_addr & 0xFFFFFFFC));
+                               (ib->gpu_addr & 0xFFFFFFFC));
        amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF);
        amdgpu_ring_write(ring, control);
 }
@@ -6014,14 +6227,6 @@ static void gfx_v8_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
        amdgpu_ring_write(ring, seq);
        amdgpu_ring_write(ring, 0xffffffff);
        amdgpu_ring_write(ring, 4); /* poll interval */
-
-       if (usepfp) {
-               /* synce CE with ME to prevent CE fetch CEIB before context switch done */
-               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-               amdgpu_ring_write(ring, 0);
-               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-               amdgpu_ring_write(ring, 0);
-       }
 }
 
 static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
@@ -6029,6 +6234,10 @@ static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
 {
        int usepfp = (ring->type == AMDGPU_RING_TYPE_GFX);
 
+       /* GFX8 emits 128 dw nop to prevent DE do vm_flush before CE finish CEIB */
+       if (usepfp)
+               amdgpu_ring_insert_nop(ring, 128);
+
        amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
        amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
                                 WRITE_DATA_DST_SEL(0)) |
@@ -6068,18 +6277,11 @@ static void gfx_v8_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
                /* sync PFP to ME, otherwise we might get invalid PFP reads */
                amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
                amdgpu_ring_write(ring, 0x0);
-               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-               amdgpu_ring_write(ring, 0);
-               amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
-               amdgpu_ring_write(ring, 0);
+               /* GFX8 emits 128 dw nop to prevent CE access VM before vm_flush finish */
+               amdgpu_ring_insert_nop(ring, 128);
        }
 }
 
-static u32 gfx_v8_0_ring_get_rptr_compute(struct amdgpu_ring *ring)
-{
-       return ring->adev->wb.wb[ring->rptr_offs];
-}
-
 static u32 gfx_v8_0_ring_get_wptr_compute(struct amdgpu_ring *ring)
 {
        return ring->adev->wb.wb[ring->wptr_offs];
@@ -6115,36 +6317,88 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, upper_32_bits(seq));
 }
 
-static void gfx_v8_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev,
-                                                enum amdgpu_interrupt_state state)
+static void gfx_v8_ring_emit_sb(struct amdgpu_ring *ring)
 {
-       u32 cp_int_cntl;
+       amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
+       amdgpu_ring_write(ring, 0);
+}
 
-       switch (state) {
-       case AMDGPU_IRQ_STATE_DISABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl = REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                           TIME_STAMP_INT_ENABLE, 0);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       case AMDGPU_IRQ_STATE_ENABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl =
-                       REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                     TIME_STAMP_INT_ENABLE, 1);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       default:
-               break;
+static void gfx_v8_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
+{
+       uint32_t dw2 = 0;
+
+       dw2 |= 0x80000000; /* set load_enable otherwise this package is just NOPs */
+       if (flags & AMDGPU_HAVE_CTX_SWITCH) {
+               /* set load_global_config & load_global_uconfig */
+               dw2 |= 0x8001;
+               /* set load_cs_sh_regs */
+               dw2 |= 0x01000000;
+               /* set load_per_context_state & load_gfx_sh_regs for GFX */
+               dw2 |= 0x10002;
+
+               /* set load_ce_ram if preamble presented */
+               if (AMDGPU_PREAMBLE_IB_PRESENT & flags)
+                       dw2 |= 0x10000000;
+       } else {
+               /* still load_ce_ram if this is the first time preamble presented
+                * although there is no context switch happens.
+                */
+               if (AMDGPU_PREAMBLE_IB_PRESENT_FIRST & flags)
+                       dw2 |= 0x10000000;
        }
+
+       amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
+       amdgpu_ring_write(ring, dw2);
+       amdgpu_ring_write(ring, 0);
+}
+
+static unsigned gfx_v8_0_ring_get_emit_ib_size_gfx(struct amdgpu_ring *ring)
+{
+       return
+               4; /* gfx_v8_0_ring_emit_ib_gfx */
+}
+
+static unsigned gfx_v8_0_ring_get_dma_frame_size_gfx(struct amdgpu_ring *ring)
+{
+       return
+               20 + /* gfx_v8_0_ring_emit_gds_switch */
+               7 + /* gfx_v8_0_ring_emit_hdp_flush */
+               5 + /* gfx_v8_0_ring_emit_hdp_invalidate */
+               6 + 6 + 6 +/* gfx_v8_0_ring_emit_fence_gfx x3 for user fence, vm fence */
+               7 + /* gfx_v8_0_ring_emit_pipeline_sync */
+               256 + 19 + /* gfx_v8_0_ring_emit_vm_flush */
+               2 + /* gfx_v8_ring_emit_sb */
+               3; /* gfx_v8_ring_emit_cntxcntl */
+}
+
+static unsigned gfx_v8_0_ring_get_emit_ib_size_compute(struct amdgpu_ring *ring)
+{
+       return
+               4; /* gfx_v8_0_ring_emit_ib_compute */
+}
+
+static unsigned gfx_v8_0_ring_get_dma_frame_size_compute(struct amdgpu_ring *ring)
+{
+       return
+               20 + /* gfx_v8_0_ring_emit_gds_switch */
+               7 + /* gfx_v8_0_ring_emit_hdp_flush */
+               5 + /* gfx_v8_0_ring_emit_hdp_invalidate */
+               7 + /* gfx_v8_0_ring_emit_pipeline_sync */
+               17 + /* gfx_v8_0_ring_emit_vm_flush */
+               7 + 7 + 7; /* gfx_v8_0_ring_emit_fence_compute x3 for user fence, vm fence */
+}
+
+static void gfx_v8_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev,
+                                                enum amdgpu_interrupt_state state)
+{
+       WREG32_FIELD(CP_INT_CNTL_RING0, TIME_STAMP_INT_ENABLE,
+                    state == AMDGPU_IRQ_STATE_DISABLE ? 0 : 1);
 }
 
 static void gfx_v8_0_set_compute_eop_interrupt_state(struct amdgpu_device *adev,
                                                     int me, int pipe,
                                                     enum amdgpu_interrupt_state state)
 {
-       u32 mec_int_cntl, mec_int_cntl_reg;
-
        /*
         * amdgpu controls only pipe 0 of MEC1. That's why this function only
         * handles the setting of interrupts for this specific pipe. All other
@@ -6154,7 +6408,6 @@ static void gfx_v8_0_set_compute_eop_interrupt_state(struct amdgpu_device *adev,
        if (me == 1) {
                switch (pipe) {
                case 0:
-                       mec_int_cntl_reg = mmCP_ME1_PIPE0_INT_CNTL;
                        break;
                default:
                        DRM_DEBUG("invalid pipe %d\n", pipe);
@@ -6165,22 +6418,8 @@ static void gfx_v8_0_set_compute_eop_interrupt_state(struct amdgpu_device *adev,
                return;
        }
 
-       switch (state) {
-       case AMDGPU_IRQ_STATE_DISABLE:
-               mec_int_cntl = RREG32(mec_int_cntl_reg);
-               mec_int_cntl = REG_SET_FIELD(mec_int_cntl, CP_ME1_PIPE0_INT_CNTL,
-                                            TIME_STAMP_INT_ENABLE, 0);
-               WREG32(mec_int_cntl_reg, mec_int_cntl);
-               break;
-       case AMDGPU_IRQ_STATE_ENABLE:
-               mec_int_cntl = RREG32(mec_int_cntl_reg);
-               mec_int_cntl = REG_SET_FIELD(mec_int_cntl, CP_ME1_PIPE0_INT_CNTL,
-                                            TIME_STAMP_INT_ENABLE, 1);
-               WREG32(mec_int_cntl_reg, mec_int_cntl);
-               break;
-       default:
-               break;
-       }
+       WREG32_FIELD(CP_ME1_PIPE0_INT_CNTL, TIME_STAMP_INT_ENABLE,
+                    state == AMDGPU_IRQ_STATE_DISABLE ? 0 : 1);
 }
 
 static int gfx_v8_0_set_priv_reg_fault_state(struct amdgpu_device *adev,
@@ -6188,24 +6427,8 @@ static int gfx_v8_0_set_priv_reg_fault_state(struct amdgpu_device *adev,
                                             unsigned type,
                                             enum amdgpu_interrupt_state state)
 {
-       u32 cp_int_cntl;
-
-       switch (state) {
-       case AMDGPU_IRQ_STATE_DISABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl = REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                           PRIV_REG_INT_ENABLE, 0);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       case AMDGPU_IRQ_STATE_ENABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl = REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                           PRIV_REG_INT_ENABLE, 1);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       default:
-               break;
-       }
+       WREG32_FIELD(CP_INT_CNTL_RING0, PRIV_REG_INT_ENABLE,
+                    state == AMDGPU_IRQ_STATE_DISABLE ? 0 : 1);
 
        return 0;
 }
@@ -6215,24 +6438,8 @@ static int gfx_v8_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
                                              unsigned type,
                                              enum amdgpu_interrupt_state state)
 {
-       u32 cp_int_cntl;
-
-       switch (state) {
-       case AMDGPU_IRQ_STATE_DISABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl = REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                           PRIV_INSTR_INT_ENABLE, 0);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       case AMDGPU_IRQ_STATE_ENABLE:
-               cp_int_cntl = RREG32(mmCP_INT_CNTL_RING0);
-               cp_int_cntl = REG_SET_FIELD(cp_int_cntl, CP_INT_CNTL_RING0,
-                                           PRIV_INSTR_INT_ENABLE, 1);
-               WREG32(mmCP_INT_CNTL_RING0, cp_int_cntl);
-               break;
-       default:
-               break;
-       }
+       WREG32_FIELD(CP_INT_CNTL_RING0, PRIV_INSTR_INT_ENABLE,
+                    state == AMDGPU_IRQ_STATE_DISABLE ? 0 : 1);
 
        return 0;
 }
@@ -6338,13 +6545,16 @@ const struct amd_ip_funcs gfx_v8_0_ip_funcs = {
        .resume = gfx_v8_0_resume,
        .is_idle = gfx_v8_0_is_idle,
        .wait_for_idle = gfx_v8_0_wait_for_idle,
+       .check_soft_reset = gfx_v8_0_check_soft_reset,
+       .pre_soft_reset = gfx_v8_0_pre_soft_reset,
        .soft_reset = gfx_v8_0_soft_reset,
+       .post_soft_reset = gfx_v8_0_post_soft_reset,
        .set_clockgating_state = gfx_v8_0_set_clockgating_state,
        .set_powergating_state = gfx_v8_0_set_powergating_state,
 };
 
 static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = {
-       .get_rptr = gfx_v8_0_ring_get_rptr_gfx,
+       .get_rptr = gfx_v8_0_ring_get_rptr,
        .get_wptr = gfx_v8_0_ring_get_wptr_gfx,
        .set_wptr = gfx_v8_0_ring_set_wptr_gfx,
        .parse_cs = NULL,
@@ -6359,10 +6569,14 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_gfx = {
        .test_ib = gfx_v8_0_ring_test_ib,
        .insert_nop = amdgpu_ring_insert_nop,
        .pad_ib = amdgpu_ring_generic_pad_ib,
+       .emit_switch_buffer = gfx_v8_ring_emit_sb,
+       .emit_cntxcntl = gfx_v8_ring_emit_cntxcntl,
+       .get_emit_ib_size = gfx_v8_0_ring_get_emit_ib_size_gfx,
+       .get_dma_frame_size = gfx_v8_0_ring_get_dma_frame_size_gfx,
 };
 
 static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = {
-       .get_rptr = gfx_v8_0_ring_get_rptr_compute,
+       .get_rptr = gfx_v8_0_ring_get_rptr,
        .get_wptr = gfx_v8_0_ring_get_wptr_compute,
        .set_wptr = gfx_v8_0_ring_set_wptr_compute,
        .parse_cs = NULL,
@@ -6377,6 +6591,8 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = {
        .test_ib = gfx_v8_0_ring_test_ib,
        .insert_nop = amdgpu_ring_insert_nop,
        .pad_ib = amdgpu_ring_generic_pad_ib,
+       .get_emit_ib_size = gfx_v8_0_ring_get_emit_ib_size_compute,
+       .get_dma_frame_size = gfx_v8_0_ring_get_dma_frame_size_compute,
 };
 
 static void gfx_v8_0_set_ring_funcs(struct amdgpu_device *adev)
@@ -6479,15 +6695,12 @@ static u32 gfx_v8_0_get_cu_active_bitmap(struct amdgpu_device *adev)
 {
        u32 data, mask;
 
-       data = RREG32(mmCC_GC_SHADER_ARRAY_CONFIG);
-       data |= RREG32(mmGC_USER_SHADER_ARRAY_CONFIG);
-
-       data &= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK;
-       data >>= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT;
+       data =  RREG32(mmCC_GC_SHADER_ARRAY_CONFIG) |
+               RREG32(mmGC_USER_SHADER_ARRAY_CONFIG);
 
        mask = gfx_v8_0_create_bitmask(adev->gfx.config.max_cu_per_sh);
 
-       return (~data) & mask;
+       return ~REG_GET_FIELD(data, CC_GC_SHADER_ARRAY_CONFIG, INACTIVE_CUS) & mask;
 }
 
 static void gfx_v8_0_get_cu_info(struct amdgpu_device *adev)
index bc82c79..ebed1f8 100644 (file)
@@ -26,6 +26,4 @@
 
 extern const struct amd_ip_funcs gfx_v8_0_ip_funcs;
 
-void gfx_v8_0_select_se_sh(struct amdgpu_device *adev, u32 se_num, u32 sh_num);
-
 #endif
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c
new file mode 100644 (file)
index 0000000..b13c8aa
--- /dev/null
@@ -0,0 +1,1071 @@
+
+/*
+ * Copyright 2014 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 "amdgpu.h"
+#include "gmc_v6_0.h"
+#include "amdgpu_ucode.h"
+#include "si/sid.h"
+
+static void gmc_v6_0_set_gart_funcs(struct amdgpu_device *adev);
+static void gmc_v6_0_set_irq_funcs(struct amdgpu_device *adev);
+static int gmc_v6_0_wait_for_idle(void *handle);
+
+MODULE_FIRMWARE("radeon/tahiti_mc.bin");
+MODULE_FIRMWARE("radeon/pitcairn_mc.bin");
+MODULE_FIRMWARE("radeon/verde_mc.bin");
+MODULE_FIRMWARE("radeon/oland_mc.bin");
+
+static const u32 crtc_offsets[6] =
+{
+       SI_CRTC0_REGISTER_OFFSET,
+       SI_CRTC1_REGISTER_OFFSET,
+       SI_CRTC2_REGISTER_OFFSET,
+       SI_CRTC3_REGISTER_OFFSET,
+       SI_CRTC4_REGISTER_OFFSET,
+       SI_CRTC5_REGISTER_OFFSET
+};
+
+static void gmc_v6_0_mc_stop(struct amdgpu_device *adev,
+                            struct amdgpu_mode_mc_save *save)
+{
+       u32 blackout;
+
+       if (adev->mode_info.num_crtc)
+               amdgpu_display_stop_mc_access(adev, save);
+
+       gmc_v6_0_wait_for_idle((void *)adev);
+
+       blackout = RREG32(MC_SHARED_BLACKOUT_CNTL);
+       if (REG_GET_FIELD(blackout, mmMC_SHARED_BLACKOUT_CNTL, xxBLACKOUT_MODE) != 1) {
+               /* Block CPU access */
+               WREG32(BIF_FB_EN, 0);
+               /* blackout the MC */
+               blackout = REG_SET_FIELD(blackout,
+                                        mmMC_SHARED_BLACKOUT_CNTL, xxBLACKOUT_MODE, 0);
+               WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1);
+       }
+       /* wait for the MC to settle */
+       udelay(100);
+
+}
+
+static void gmc_v6_0_mc_resume(struct amdgpu_device *adev,
+                              struct amdgpu_mode_mc_save *save)
+{
+       u32 tmp;
+
+       /* unblackout the MC */
+       tmp = RREG32(MC_SHARED_BLACKOUT_CNTL);
+       tmp = REG_SET_FIELD(tmp, mmMC_SHARED_BLACKOUT_CNTL, xxBLACKOUT_MODE, 0);
+       WREG32(MC_SHARED_BLACKOUT_CNTL, tmp);
+       /* allow CPU access */
+       tmp = REG_SET_FIELD(0, mmBIF_FB_EN, xxFB_READ_EN, 1);
+       tmp = REG_SET_FIELD(tmp, mmBIF_FB_EN, xxFB_WRITE_EN, 1);
+       WREG32(BIF_FB_EN, tmp);
+
+       if (adev->mode_info.num_crtc)
+               amdgpu_display_resume_mc_access(adev, save);
+
+}
+
+static int gmc_v6_0_init_microcode(struct amdgpu_device *adev)
+{
+       const char *chip_name;
+       char fw_name[30];
+       int err;
+
+       DRM_DEBUG("\n");
+
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               chip_name = "tahiti";
+               break;
+       case CHIP_PITCAIRN:
+               chip_name = "pitcairn";
+               break;
+       case CHIP_VERDE:
+               chip_name = "verde";
+               break;
+       case CHIP_OLAND:
+               chip_name = "oland";
+               break;
+       case CHIP_HAINAN:
+               chip_name = "hainan";
+               break;
+       default: BUG();
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+       err = request_firmware(&adev->mc.fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+
+       err = amdgpu_ucode_validate(adev->mc.fw);
+
+out:
+       if (err) {
+               dev_err(adev->dev,
+                      "si_mc: Failed to load firmware \"%s\"\n",
+                      fw_name);
+               release_firmware(adev->mc.fw);
+               adev->mc.fw = NULL;
+       }
+       return err;
+}
+
+static int gmc_v6_0_mc_load_microcode(struct amdgpu_device *adev)
+{
+       const __le32 *new_fw_data = NULL;
+       u32 running;
+       const __le32 *new_io_mc_regs = NULL;
+       int i, regs_size, ucode_size;
+       const struct mc_firmware_header_v1_0 *hdr;
+
+       if (!adev->mc.fw)
+               return -EINVAL;
+
+       hdr = (const struct mc_firmware_header_v1_0 *)adev->mc.fw->data;
+
+       amdgpu_ucode_print_mc_hdr(&hdr->header);
+
+       adev->mc.fw_version = le32_to_cpu(hdr->header.ucode_version);
+       regs_size = le32_to_cpu(hdr->io_debug_size_bytes) / (4 * 2);
+       new_io_mc_regs = (const __le32 *)
+               (adev->mc.fw->data + le32_to_cpu(hdr->io_debug_array_offset_bytes));
+       ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
+       new_fw_data = (const __le32 *)
+               (adev->mc.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+
+       running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
+
+       if (running == 0) {
+
+               /* 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, le32_to_cpup(new_io_mc_regs++));
+                       WREG32(MC_SEQ_IO_DEBUG_DATA, le32_to_cpup(new_io_mc_regs++));
+               }
+               /* load the MC ucode */
+               for (i = 0; i < ucode_size; i++) {
+                       WREG32(MC_SEQ_SUP_PGM, le32_to_cpup(new_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 < adev->usec_timeout; i++) {
+                       if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D0)
+                               break;
+                       udelay(1);
+               }
+               for (i = 0; i < adev->usec_timeout; i++) {
+                       if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D1)
+                               break;
+                       udelay(1);
+               }
+
+       }
+
+       return 0;
+}
+
+static void gmc_v6_0_vram_gtt_location(struct amdgpu_device *adev,
+                                      struct amdgpu_mc *mc)
+{
+       if (mc->mc_vram_size > 0xFFC0000000ULL) {
+               dev_warn(adev->dev, "limiting VRAM\n");
+               mc->real_vram_size = 0xFFC0000000ULL;
+               mc->mc_vram_size = 0xFFC0000000ULL;
+       }
+       amdgpu_vram_location(adev, &adev->mc, 0);
+       adev->mc.gtt_base_align = 0;
+       amdgpu_gtt_location(adev, mc);
+}
+
+static void gmc_v6_0_mc_program(struct amdgpu_device *adev)
+{
+       struct amdgpu_mode_mc_save save;
+       u32 tmp;
+       int i, j;
+
+       /* Initialize HDP */
+       for (i = 0, j = 0; i < 32; i++, j += 0x6) {
+               WREG32((0xb05 + j), 0x00000000);
+               WREG32((0xb06 + j), 0x00000000);
+               WREG32((0xb07 + j), 0x00000000);
+               WREG32((0xb08 + j), 0x00000000);
+               WREG32((0xb09 + j), 0x00000000);
+       }
+       WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0);
+
+       gmc_v6_0_mc_stop(adev, &save);
+
+       if (gmc_v6_0_wait_for_idle((void *)adev)) {
+               dev_warn(adev->dev, "Wait for MC idle timedout !\n");
+       }
+
+       WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE);
+       /* Update configuration */
+       WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+              adev->mc.vram_start >> 12);
+       WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+              adev->mc.vram_end >> 12);
+       WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR,
+              adev->vram_scratch.gpu_addr >> 12);
+       tmp = ((adev->mc.vram_end >> 24) & 0xFFFF) << 16;
+       tmp |= ((adev->mc.vram_start >> 24) & 0xFFFF);
+       WREG32(MC_VM_FB_LOCATION, tmp);
+       /* XXX double check these! */
+       WREG32(HDP_NONSURFACE_BASE, (adev->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 (gmc_v6_0_wait_for_idle((void *)adev)) {
+               dev_warn(adev->dev, "Wait for MC idle timedout !\n");
+       }
+       gmc_v6_0_mc_resume(adev, &save);
+       amdgpu_display_set_vga_render_state(adev, false);
+}
+
+static int gmc_v6_0_mc_init(struct amdgpu_device *adev)
+{
+
+       u32 tmp;
+       int chansize, numchan;
+
+       tmp = RREG32(MC_ARB_RAMCFG);
+       if (tmp & CHANSIZE_OVERRIDE) {
+               chansize = 16;
+       } else 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;
+       }
+       adev->mc.vram_width = numchan * chansize;
+       /* Could aper size report 0 ? */
+       adev->mc.aper_base = pci_resource_start(adev->pdev, 0);
+       adev->mc.aper_size = pci_resource_len(adev->pdev, 0);
+       /* size in MB on si */
+       adev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024ULL * 1024ULL;
+       adev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024ULL * 1024ULL;
+       adev->mc.visible_vram_size = adev->mc.aper_size;
+
+       /* unless the user had overridden it, set the gart
+        * size equal to the 1024 or vram, whichever is larger.
+        */
+       if (amdgpu_gart_size == -1)
+               adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
+       else
+               adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
+
+       gmc_v6_0_vram_gtt_location(adev, &adev->mc);
+
+       return 0;
+}
+
+static void gmc_v6_0_gart_flush_gpu_tlb(struct amdgpu_device *adev,
+                                       uint32_t vmid)
+{
+       WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0);
+
+       WREG32(VM_INVALIDATE_REQUEST, 1 << vmid);
+}
+
+static int gmc_v6_0_gart_set_pte_pde(struct amdgpu_device *adev,
+                                    void *cpu_pt_addr,
+                                    uint32_t gpu_page_idx,
+                                    uint64_t addr,
+                                    uint32_t flags)
+{
+       void __iomem *ptr = (void *)cpu_pt_addr;
+       uint64_t value;
+
+       value = addr & 0xFFFFFFFFFFFFF000ULL;
+       value |= flags;
+       writeq(value, ptr + (gpu_page_idx * 8));
+
+       return 0;
+}
+
+static void gmc_v6_0_set_fault_enable_default(struct amdgpu_device *adev,
+                                             bool value)
+{
+       u32 tmp;
+
+       tmp = RREG32(VM_CONTEXT1_CNTL);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxRANGE_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxDUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxPDE0_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxVALID_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxREAD_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       tmp = REG_SET_FIELD(tmp, mmVM_CONTEXT1_CNTL,
+                           xxWRITE_PROTECTION_FAULT_ENABLE_DEFAULT, value);
+       WREG32(VM_CONTEXT1_CNTL, tmp);
+}
+
+static int gmc_v6_0_gart_enable(struct amdgpu_device *adev)
+{
+       int r, i;
+
+       if (adev->gart.robj == NULL) {
+               dev_err(adev->dev, "No VRAM object for PCIE GART.\n");
+               return -EINVAL;
+       }
+       r = amdgpu_gart_table_vram_pin(adev);
+       if (r)
+               return r;
+       /* Setup TLB control */
+       WREG32(MC_VM_MX_L1_TLB_CNTL,
+              (0xA << 7) |
+              ENABLE_L1_TLB |
+              ENABLE_L1_FRAGMENT_PROCESSING |
+              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 |
+              BANK_SELECT(4) |
+              L2_CACHE_BIGK_FRAGMENT_SIZE(4));
+       /* setup context0 */
+       WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, adev->mc.gtt_start >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, adev->mc.gtt_end >> 12);
+       WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, adev->gart.table_addr >> 12);
+       WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
+                       (u32)(adev->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(0x575, 0);
+       WREG32(0x576, 0);
+       WREG32(0x577, 0);
+
+       /* empty context1-15 */
+       /* set vm size, must be a multiple of 4 */
+       WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
+       WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, adev->vm_manager.max_pfn - 1);
+       /* Assign the pt base to something valid for now; the pts used for
+        * the VMs are determined by the application and setup and assigned
+        * on the fly in the vm part of radeon_gart.c
+        */
+       for (i = 1; i < 16; i++) {
+               if (i < 8)
+                       WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + i,
+                              adev->gart.table_addr >> 12);
+               else
+                       WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + i - 8,
+                              adev->gart.table_addr >> 12);
+       }
+
+       /* enable context1-15 */
+       WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR,
+              (u32)(adev->dummy_page.addr >> 12));
+       WREG32(VM_CONTEXT1_CNTL2, 4);
+       WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) |
+                               PAGE_TABLE_BLOCK_SIZE(amdgpu_vm_block_size - 9) |
+                               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);
+
+       gmc_v6_0_gart_flush_gpu_tlb(adev, 0);
+       dev_info(adev->dev, "PCIE GART of %uM enabled (table at 0x%016llX).\n",
+                (unsigned)(adev->mc.gtt_size >> 20),
+                (unsigned long long)adev->gart.table_addr);
+       adev->gart.ready = true;
+       return 0;
+}
+
+static int gmc_v6_0_gart_init(struct amdgpu_device *adev)
+{
+       int r;
+
+       if (adev->gart.robj) {
+               dev_warn(adev->dev, "gmc_v6_0 PCIE GART already initialized\n");
+               return 0;
+       }
+       r = amdgpu_gart_init(adev);
+       if (r)
+               return r;
+       adev->gart.table_size = adev->gart.num_gpu_pages * 8;
+       return amdgpu_gart_table_vram_alloc(adev);
+}
+
+static void gmc_v6_0_gart_disable(struct amdgpu_device *adev)
+{
+       /*unsigned i;
+
+       for (i = 1; i < 16; ++i) {
+               uint32_t reg;
+               if (i < 8)
+                       reg = VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + i ;
+               else
+                       reg = VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + (i - 8);
+               adev->vm_manager.saved_table_addr[i] = RREG32(reg);
+       }*/
+
+       /* 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_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(0));
+       amdgpu_gart_table_vram_unpin(adev);
+}
+
+static void gmc_v6_0_gart_fini(struct amdgpu_device *adev)
+{
+       amdgpu_gart_table_vram_free(adev);
+       amdgpu_gart_fini(adev);
+}
+
+static int gmc_v6_0_vm_init(struct amdgpu_device *adev)
+{
+       /*
+        * number of VMs
+        * VMID 0 is reserved for System
+        * amdgpu graphics/compute will use VMIDs 1-7
+        * amdkfd will use VMIDs 8-15
+        */
+       adev->vm_manager.num_ids = AMDGPU_NUM_OF_VMIDS;
+       amdgpu_vm_manager_init(adev);
+
+       /* base offset of vram pages */
+       if (adev->flags & AMD_IS_APU) {
+               u64 tmp = RREG32(MC_VM_FB_OFFSET);
+               tmp <<= 22;
+               adev->vm_manager.vram_base_offset = tmp;
+       } else
+               adev->vm_manager.vram_base_offset = 0;
+
+       return 0;
+}
+
+static void gmc_v6_0_vm_fini(struct amdgpu_device *adev)
+{
+}
+
+static void gmc_v6_0_vm_decode_fault(struct amdgpu_device *adev,
+                                    u32 status, u32 addr, u32 mc_client)
+{
+       u32 mc_id;
+       u32 vmid = REG_GET_FIELD(status, mmVM_CONTEXT1_PROTECTION_FAULT_STATUS, xxVMID);
+       u32 protections = REG_GET_FIELD(status, mmVM_CONTEXT1_PROTECTION_FAULT_STATUS,
+                                       xxPROTECTIONS);
+       char block[5] = { mc_client >> 24, (mc_client >> 16) & 0xff,
+               (mc_client >> 8) & 0xff, mc_client & 0xff, 0 };
+
+       mc_id = REG_GET_FIELD(status, mmVM_CONTEXT1_PROTECTION_FAULT_STATUS,
+                             xxMEMORY_CLIENT_ID);
+
+       dev_err(adev->dev, "VM fault (0x%02x, vmid %d) at page %u, %s from '%s' (0x%08x) (%d)\n",
+              protections, vmid, addr,
+              REG_GET_FIELD(status, mmVM_CONTEXT1_PROTECTION_FAULT_STATUS,
+                            xxMEMORY_CLIENT_RW) ?
+              "write" : "read", block, mc_client, mc_id);
+}
+
+/*
+static const u32 mc_cg_registers[] = {
+       MC_HUB_MISC_HUB_CG,
+       MC_HUB_MISC_SIP_CG,
+       MC_HUB_MISC_VM_CG,
+       MC_XPB_CLK_GAT,
+       ATC_MISC_CG,
+       MC_CITF_MISC_WR_CG,
+       MC_CITF_MISC_RD_CG,
+       MC_CITF_MISC_VM_CG,
+       VM_L2_CG,
+};
+
+static const u32 mc_cg_ls_en[] = {
+       MC_HUB_MISC_HUB_CG__MEM_LS_ENABLE_MASK,
+       MC_HUB_MISC_SIP_CG__MEM_LS_ENABLE_MASK,
+       MC_HUB_MISC_VM_CG__MEM_LS_ENABLE_MASK,
+       MC_XPB_CLK_GAT__MEM_LS_ENABLE_MASK,
+       ATC_MISC_CG__MEM_LS_ENABLE_MASK,
+       MC_CITF_MISC_WR_CG__MEM_LS_ENABLE_MASK,
+       MC_CITF_MISC_RD_CG__MEM_LS_ENABLE_MASK,
+       MC_CITF_MISC_VM_CG__MEM_LS_ENABLE_MASK,
+       VM_L2_CG__MEM_LS_ENABLE_MASK,
+};
+
+static const u32 mc_cg_en[] = {
+       MC_HUB_MISC_HUB_CG__ENABLE_MASK,
+       MC_HUB_MISC_SIP_CG__ENABLE_MASK,
+       MC_HUB_MISC_VM_CG__ENABLE_MASK,
+       MC_XPB_CLK_GAT__ENABLE_MASK,
+       ATC_MISC_CG__ENABLE_MASK,
+       MC_CITF_MISC_WR_CG__ENABLE_MASK,
+       MC_CITF_MISC_RD_CG__ENABLE_MASK,
+       MC_CITF_MISC_VM_CG__ENABLE_MASK,
+       VM_L2_CG__ENABLE_MASK,
+};
+
+static void gmc_v6_0_enable_mc_ls(struct amdgpu_device *adev,
+                                 bool enable)
+{
+       int i;
+       u32 orig, data;
+
+       for (i = 0; i < ARRAY_SIZE(mc_cg_registers); i++) {
+               orig = data = RREG32(mc_cg_registers[i]);
+               if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_MC_LS))
+                       data |= mc_cg_ls_en[i];
+               else
+                       data &= ~mc_cg_ls_en[i];
+               if (data != orig)
+                       WREG32(mc_cg_registers[i], data);
+       }
+}
+
+static void gmc_v6_0_enable_mc_mgcg(struct amdgpu_device *adev,
+                                   bool enable)
+{
+       int i;
+       u32 orig, data;
+
+       for (i = 0; i < ARRAY_SIZE(mc_cg_registers); i++) {
+               orig = data = RREG32(mc_cg_registers[i]);
+               if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_MC_MGCG))
+                       data |= mc_cg_en[i];
+               else
+                       data &= ~mc_cg_en[i];
+               if (data != orig)
+                       WREG32(mc_cg_registers[i], data);
+       }
+}
+
+static void gmc_v6_0_enable_bif_mgls(struct amdgpu_device *adev,
+                                    bool enable)
+{
+       u32 orig, data;
+
+       orig = data = RREG32_PCIE(ixPCIE_CNTL2);
+
+       if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_BIF_LS)) {
+               data = REG_SET_FIELD(data, PCIE_CNTL2, SLV_MEM_LS_EN, 1);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, MST_MEM_LS_EN, 1);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, REPLAY_MEM_LS_EN, 1);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, SLV_MEM_AGGRESSIVE_LS_EN, 1);
+       } else {
+               data = REG_SET_FIELD(data, PCIE_CNTL2, SLV_MEM_LS_EN, 0);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, MST_MEM_LS_EN, 0);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, REPLAY_MEM_LS_EN, 0);
+               data = REG_SET_FIELD(data, PCIE_CNTL2, SLV_MEM_AGGRESSIVE_LS_EN, 0);
+       }
+
+       if (orig != data)
+               WREG32_PCIE(ixPCIE_CNTL2, data);
+}
+
+static void gmc_v6_0_enable_hdp_mgcg(struct amdgpu_device *adev,
+                                    bool enable)
+{
+       u32 orig, data;
+
+       orig = data = RREG32(HDP_HOST_PATH_CNTL);
+
+       if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_HDP_MGCG))
+               data = REG_SET_FIELD(data, HDP_HOST_PATH_CNTL, CLOCK_GATING_DIS, 0);
+       else
+               data = REG_SET_FIELD(data, HDP_HOST_PATH_CNTL, CLOCK_GATING_DIS, 1);
+
+       if (orig != data)
+               WREG32(HDP_HOST_PATH_CNTL, data);
+}
+
+static void gmc_v6_0_enable_hdp_ls(struct amdgpu_device *adev,
+                                  bool enable)
+{
+       u32 orig, data;
+
+       orig = data = RREG32(HDP_MEM_POWER_LS);
+
+       if (enable && (adev->cg_flags & AMDGPU_CG_SUPPORT_HDP_LS))
+               data = REG_SET_FIELD(data, HDP_MEM_POWER_LS, LS_ENABLE, 1);
+       else
+               data = REG_SET_FIELD(data, HDP_MEM_POWER_LS, LS_ENABLE, 0);
+
+       if (orig != data)
+               WREG32(HDP_MEM_POWER_LS, data);
+}
+*/
+
+static int gmc_v6_0_convert_vram_type(int mc_seq_vram_type)
+{
+       switch (mc_seq_vram_type) {
+       case MC_SEQ_MISC0__MT__GDDR1:
+               return AMDGPU_VRAM_TYPE_GDDR1;
+       case MC_SEQ_MISC0__MT__DDR2:
+               return AMDGPU_VRAM_TYPE_DDR2;
+       case MC_SEQ_MISC0__MT__GDDR3:
+               return AMDGPU_VRAM_TYPE_GDDR3;
+       case MC_SEQ_MISC0__MT__GDDR4:
+               return AMDGPU_VRAM_TYPE_GDDR4;
+       case MC_SEQ_MISC0__MT__GDDR5:
+               return AMDGPU_VRAM_TYPE_GDDR5;
+       case MC_SEQ_MISC0__MT__DDR3:
+               return AMDGPU_VRAM_TYPE_DDR3;
+       default:
+               return AMDGPU_VRAM_TYPE_UNKNOWN;
+       }
+}
+
+static int gmc_v6_0_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       gmc_v6_0_set_gart_funcs(adev);
+       gmc_v6_0_set_irq_funcs(adev);
+
+       if (adev->flags & AMD_IS_APU) {
+               adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
+       } else {
+               u32 tmp = RREG32(MC_SEQ_MISC0);
+               tmp &= MC_SEQ_MISC0__MT__MASK;
+               adev->mc.vram_type = gmc_v6_0_convert_vram_type(tmp);
+       }
+
+       return 0;
+}
+
+static int gmc_v6_0_late_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return amdgpu_irq_get(adev, &adev->mc.vm_fault, 0);
+}
+
+static int gmc_v6_0_sw_init(void *handle)
+{
+       int r;
+       int dma_bits;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault);
+       if (r)
+               return r;
+
+       r = amdgpu_irq_add_id(adev, 147, &adev->mc.vm_fault);
+       if (r)
+               return r;
+
+       adev->vm_manager.max_pfn = amdgpu_vm_size << 18;
+
+       adev->mc.mc_mask = 0xffffffffffULL;
+
+       adev->need_dma32 = false;
+       dma_bits = adev->need_dma32 ? 32 : 40;
+       r = pci_set_dma_mask(adev->pdev, DMA_BIT_MASK(dma_bits));
+       if (r) {
+               adev->need_dma32 = true;
+               dma_bits = 32;
+               dev_warn(adev->dev, "amdgpu: No suitable DMA available.\n");
+       }
+       r = pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(dma_bits));
+       if (r) {
+               pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
+               dev_warn(adev->dev, "amdgpu: No coherent DMA available.\n");
+       }
+
+       r = gmc_v6_0_init_microcode(adev);
+       if (r) {
+               dev_err(adev->dev, "Failed to load mc firmware!\n");
+               return r;
+       }
+
+       r = amdgpu_ttm_global_init(adev);
+       if (r) {
+               return r;
+       }
+
+       r = gmc_v6_0_mc_init(adev);
+       if (r)
+               return r;
+
+       r = amdgpu_bo_init(adev);
+       if (r)
+               return r;
+
+       r = gmc_v6_0_gart_init(adev);
+       if (r)
+               return r;
+
+       if (!adev->vm_manager.enabled) {
+               r = gmc_v6_0_vm_init(adev);
+               if (r) {
+                       dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
+                       return r;
+               }
+               adev->vm_manager.enabled = true;
+       }
+
+       return r;
+}
+
+static int gmc_v6_0_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (adev->vm_manager.enabled) {
+               gmc_v6_0_vm_fini(adev);
+               adev->vm_manager.enabled = false;
+       }
+       gmc_v6_0_gart_fini(adev);
+       amdgpu_gem_force_release(adev);
+       amdgpu_bo_fini(adev);
+
+       return 0;
+}
+
+static int gmc_v6_0_hw_init(void *handle)
+{
+       int r;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       gmc_v6_0_mc_program(adev);
+
+       if (!(adev->flags & AMD_IS_APU)) {
+               r = gmc_v6_0_mc_load_microcode(adev);
+               if (r) {
+                       dev_err(adev->dev, "Failed to load MC firmware!\n");
+                       return r;
+               }
+       }
+
+       r = gmc_v6_0_gart_enable(adev);
+       if (r)
+               return r;
+
+       return r;
+}
+
+static int gmc_v6_0_hw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       amdgpu_irq_put(adev, &adev->mc.vm_fault, 0);
+       gmc_v6_0_gart_disable(adev);
+
+       return 0;
+}
+
+static int gmc_v6_0_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (adev->vm_manager.enabled) {
+               gmc_v6_0_vm_fini(adev);
+               adev->vm_manager.enabled = false;
+       }
+       gmc_v6_0_hw_fini(adev);
+
+       return 0;
+}
+
+static int gmc_v6_0_resume(void *handle)
+{
+       int r;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       r = gmc_v6_0_hw_init(adev);
+       if (r)
+               return r;
+
+       if (!adev->vm_manager.enabled) {
+               r = gmc_v6_0_vm_init(adev);
+               if (r) {
+                       dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
+                       return r;
+               }
+               adev->vm_manager.enabled = true;
+       }
+
+       return r;
+}
+
+static bool gmc_v6_0_is_idle(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 tmp = RREG32(SRBM_STATUS);
+
+       if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
+                  SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK | SRBM_STATUS__VMC_BUSY_MASK))
+               return false;
+
+       return true;
+}
+
+static int gmc_v6_0_wait_for_idle(void *handle)
+{
+       unsigned i;
+       u32 tmp;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               tmp = RREG32(SRBM_STATUS) & (SRBM_STATUS__MCB_BUSY_MASK |
+                                              SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
+                                              SRBM_STATUS__MCC_BUSY_MASK |
+                                              SRBM_STATUS__MCD_BUSY_MASK |
+                                              SRBM_STATUS__VMC_BUSY_MASK);
+               if (!tmp)
+                       return 0;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+
+}
+
+static int gmc_v6_0_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       struct amdgpu_mode_mc_save save;
+       u32 srbm_soft_reset = 0;
+       u32 tmp = RREG32(SRBM_STATUS);
+
+       if (tmp & SRBM_STATUS__VMC_BUSY_MASK)
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
+                                               mmSRBM_SOFT_RESET, xxSOFT_RESET_VMC, 1);
+
+       if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
+                  SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) {
+               if (!(adev->flags & AMD_IS_APU))
+                       srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
+                                                       mmSRBM_SOFT_RESET, xxSOFT_RESET_MC, 1);
+       }
+
+       if (srbm_soft_reset) {
+               gmc_v6_0_mc_stop(adev, &save);
+               if (gmc_v6_0_wait_for_idle(adev)) {
+                       dev_warn(adev->dev, "Wait for GMC idle timed out !\n");
+               }
+
+
+               tmp = RREG32(SRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(adev->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);
+
+               udelay(50);
+
+               gmc_v6_0_mc_resume(adev, &save);
+               udelay(50);
+       }
+
+       return 0;
+}
+
+static int gmc_v6_0_vm_fault_interrupt_state(struct amdgpu_device *adev,
+                                            struct amdgpu_irq_src *src,
+                                            unsigned type,
+                                            enum amdgpu_interrupt_state state)
+{
+       u32 tmp;
+       u32 bits = (VM_CONTEXT1_CNTL__RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
+                   VM_CONTEXT1_CNTL__DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
+                   VM_CONTEXT1_CNTL__PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
+                   VM_CONTEXT1_CNTL__VALID_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
+                   VM_CONTEXT1_CNTL__READ_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
+                   VM_CONTEXT1_CNTL__WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK);
+
+       switch (state) {
+       case AMDGPU_IRQ_STATE_DISABLE:
+               tmp = RREG32(VM_CONTEXT0_CNTL);
+               tmp &= ~bits;
+               WREG32(VM_CONTEXT0_CNTL, tmp);
+               tmp = RREG32(VM_CONTEXT1_CNTL);
+               tmp &= ~bits;
+               WREG32(VM_CONTEXT1_CNTL, tmp);
+               break;
+       case AMDGPU_IRQ_STATE_ENABLE:
+               tmp = RREG32(VM_CONTEXT0_CNTL);
+               tmp |= bits;
+               WREG32(VM_CONTEXT0_CNTL, tmp);
+               tmp = RREG32(VM_CONTEXT1_CNTL);
+               tmp |= bits;
+               WREG32(VM_CONTEXT1_CNTL, tmp);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int gmc_v6_0_process_interrupt(struct amdgpu_device *adev,
+                                     struct amdgpu_irq_src *source,
+                                     struct amdgpu_iv_entry *entry)
+{
+       u32 addr, status;
+
+       addr = RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR);
+       status = RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS);
+       WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1);
+
+       if (!addr && !status)
+               return 0;
+
+       if (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_FIRST)
+               gmc_v6_0_set_fault_enable_default(adev, false);
+
+       dev_err(adev->dev, "GPU fault detected: %d 0x%08x\n",
+               entry->src_id, entry->src_data);
+       dev_err(adev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_ADDR   0x%08X\n",
+               addr);
+       dev_err(adev->dev, "  VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
+               status);
+       gmc_v6_0_vm_decode_fault(adev, status, addr, 0);
+
+       return 0;
+}
+
+static int gmc_v6_0_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int gmc_v6_0_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       return 0;
+}
+
+const struct amd_ip_funcs gmc_v6_0_ip_funcs = {
+       .name = "gmc_v6_0",
+       .early_init = gmc_v6_0_early_init,
+       .late_init = gmc_v6_0_late_init,
+       .sw_init = gmc_v6_0_sw_init,
+       .sw_fini = gmc_v6_0_sw_fini,
+       .hw_init = gmc_v6_0_hw_init,
+       .hw_fini = gmc_v6_0_hw_fini,
+       .suspend = gmc_v6_0_suspend,
+       .resume = gmc_v6_0_resume,
+       .is_idle = gmc_v6_0_is_idle,
+       .wait_for_idle = gmc_v6_0_wait_for_idle,
+       .soft_reset = gmc_v6_0_soft_reset,
+       .set_clockgating_state = gmc_v6_0_set_clockgating_state,
+       .set_powergating_state = gmc_v6_0_set_powergating_state,
+};
+
+static const struct amdgpu_gart_funcs gmc_v6_0_gart_funcs = {
+       .flush_gpu_tlb = gmc_v6_0_gart_flush_gpu_tlb,
+       .set_pte_pde = gmc_v6_0_gart_set_pte_pde,
+};
+
+static const struct amdgpu_irq_src_funcs gmc_v6_0_irq_funcs = {
+       .set = gmc_v6_0_vm_fault_interrupt_state,
+       .process = gmc_v6_0_process_interrupt,
+};
+
+static void gmc_v6_0_set_gart_funcs(struct amdgpu_device *adev)
+{
+       if (adev->gart.gart_funcs == NULL)
+               adev->gart.gart_funcs = &gmc_v6_0_gart_funcs;
+}
+
+static void gmc_v6_0_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->mc.vm_fault.num_types = 1;
+       adev->mc.vm_fault.funcs = &gmc_v6_0_irq_funcs;
+}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.h b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.h
new file mode 100644 (file)
index 0000000..42c4fc6
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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 __GMC_V6_0_H__
+#define __GMC_V6_0_H__
+
+extern const struct amd_ip_funcs gmc_v6_0_ip_funcs;
+
+#endif
index 0b0f086..aa0c4b9 100644 (file)
@@ -183,7 +183,7 @@ static int gmc_v7_0_mc_load_microcode(struct amdgpu_device *adev)
        const struct mc_firmware_header_v1_0 *hdr;
        const __le32 *fw_data = NULL;
        const __le32 *io_mc_regs = NULL;
-       u32 running, blackout = 0;
+       u32 running;
        int i, ucode_size, regs_size;
 
        if (!adev->mc.fw)
@@ -203,11 +203,6 @@ static int gmc_v7_0_mc_load_microcode(struct amdgpu_device *adev)
        running = REG_GET_FIELD(RREG32(mmMC_SEQ_SUP_CNTL), MC_SEQ_SUP_CNTL, RUN);
 
        if (running == 0) {
-               if (running) {
-                       blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
-                       WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout | 1);
-               }
-
                /* reset the engine and set to writable */
                WREG32(mmMC_SEQ_SUP_CNTL, 0x00000008);
                WREG32(mmMC_SEQ_SUP_CNTL, 0x00000010);
@@ -239,9 +234,6 @@ static int gmc_v7_0_mc_load_microcode(struct amdgpu_device *adev)
                                break;
                        udelay(1);
                }
-
-               if (running)
-                       WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout);
        }
 
        return 0;
@@ -393,7 +385,7 @@ static int gmc_v7_0_mc_init(struct amdgpu_device *adev)
         * size equal to the 1024 or vram, whichever is larger.
         */
        if (amdgpu_gart_size == -1)
-               adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
+               adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
        else
                adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
 
@@ -953,6 +945,11 @@ static int gmc_v7_0_sw_init(void *handle)
                return r;
        }
 
+       r = amdgpu_ttm_global_init(adev);
+       if (r) {
+               return r;
+       }
+
        r = gmc_v7_0_mc_init(adev);
        if (r)
                return r;
index 2aee2c6..1b319f5 100644 (file)
@@ -261,7 +261,7 @@ static int gmc_v8_0_mc_load_microcode(struct amdgpu_device *adev)
        const struct mc_firmware_header_v1_0 *hdr;
        const __le32 *fw_data = NULL;
        const __le32 *io_mc_regs = NULL;
-       u32 running, blackout = 0;
+       u32 running;
        int i, ucode_size, regs_size;
 
        if (!adev->mc.fw)
@@ -269,8 +269,10 @@ static int gmc_v8_0_mc_load_microcode(struct amdgpu_device *adev)
 
        /* Skip MC ucode loading on SR-IOV capable boards.
         * vbios does this for us in asic_init in that case.
+        * Skip MC ucode loading on VF, because hypervisor will do that
+        * for this adaptor.
         */
-       if (adev->virtualization.supports_sr_iov)
+       if (amdgpu_sriov_bios(adev))
                return 0;
 
        hdr = (const struct mc_firmware_header_v1_0 *)adev->mc.fw->data;
@@ -287,11 +289,6 @@ static int gmc_v8_0_mc_load_microcode(struct amdgpu_device *adev)
        running = REG_GET_FIELD(RREG32(mmMC_SEQ_SUP_CNTL), MC_SEQ_SUP_CNTL, RUN);
 
        if (running == 0) {
-               if (running) {
-                       blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
-                       WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout | 1);
-               }
-
                /* reset the engine and set to writable */
                WREG32(mmMC_SEQ_SUP_CNTL, 0x00000008);
                WREG32(mmMC_SEQ_SUP_CNTL, 0x00000010);
@@ -323,9 +320,6 @@ static int gmc_v8_0_mc_load_microcode(struct amdgpu_device *adev)
                                break;
                        udelay(1);
                }
-
-               if (running)
-                       WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout);
        }
 
        return 0;
@@ -477,7 +471,7 @@ static int gmc_v8_0_mc_init(struct amdgpu_device *adev)
         * size equal to the 1024 or vram, whichever is larger.
         */
        if (amdgpu_gart_size == -1)
-               adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
+               adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev);
        else
                adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
 
@@ -957,6 +951,11 @@ static int gmc_v8_0_sw_init(void *handle)
                return r;
        }
 
+       r = amdgpu_ttm_global_init(adev);
+       if (r) {
+               return r;
+       }
+
        r = gmc_v8_0_mc_init(adev);
        if (r)
                return r;
@@ -1100,9 +1099,8 @@ static int gmc_v8_0_wait_for_idle(void *handle)
 
 }
 
-static int gmc_v8_0_soft_reset(void *handle)
+static int gmc_v8_0_check_soft_reset(void *handle)
 {
-       struct amdgpu_mode_mc_save save;
        u32 srbm_soft_reset = 0;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        u32 tmp = RREG32(mmSRBM_STATUS);
@@ -1117,13 +1115,42 @@ static int gmc_v8_0_soft_reset(void *handle)
                        srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
                                                        SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
        }
-
        if (srbm_soft_reset) {
-               gmc_v8_0_mc_stop(adev, &save);
-               if (gmc_v8_0_wait_for_idle((void *)adev)) {
-                       dev_warn(adev->dev, "Wait for GMC idle timed out !\n");
-               }
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang = true;
+               adev->mc.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang = false;
+               adev->mc.srbm_soft_reset = 0;
+       }
+       return 0;
+}
 
+static int gmc_v8_0_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+               return 0;
+
+       gmc_v8_0_mc_stop(adev, &adev->mc.save);
+       if (gmc_v8_0_wait_for_idle(adev)) {
+               dev_warn(adev->dev, "Wait for GMC idle timed out !\n");
+       }
+
+       return 0;
+}
+
+static int gmc_v8_0_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+               return 0;
+       srbm_soft_reset = adev->mc.srbm_soft_reset;
+
+       if (srbm_soft_reset) {
+               u32 tmp;
 
                tmp = RREG32(mmSRBM_SOFT_RESET);
                tmp |= srbm_soft_reset;
@@ -1139,14 +1166,22 @@ static int gmc_v8_0_soft_reset(void *handle)
 
                /* Wait a little for things to settle down */
                udelay(50);
-
-               gmc_v8_0_mc_resume(adev, &save);
-               udelay(50);
        }
 
        return 0;
 }
 
+static int gmc_v8_0_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_GMC].hang)
+               return 0;
+
+       gmc_v8_0_mc_resume(adev, &adev->mc.save);
+       return 0;
+}
+
 static int gmc_v8_0_vm_fault_interrupt_state(struct amdgpu_device *adev,
                                             struct amdgpu_irq_src *src,
                                             unsigned type,
@@ -1414,7 +1449,10 @@ const struct amd_ip_funcs gmc_v8_0_ip_funcs = {
        .resume = gmc_v8_0_resume,
        .is_idle = gmc_v8_0_is_idle,
        .wait_for_idle = gmc_v8_0_wait_for_idle,
+       .check_soft_reset = gmc_v8_0_check_soft_reset,
+       .pre_soft_reset = gmc_v8_0_pre_soft_reset,
        .soft_reset = gmc_v8_0_soft_reset,
+       .post_soft_reset = gmc_v8_0_post_soft_reset,
        .set_clockgating_state = gmc_v8_0_set_clockgating_state,
        .set_powergating_state = gmc_v8_0_set_powergating_state,
 };
diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_dpm.c b/drivers/gpu/drm/amd/amdgpu/iceland_dpm.c
deleted file mode 100644 (file)
index 2f078ad..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "iceland_smum.h"
-
-MODULE_FIRMWARE("amdgpu/topaz_smc.bin");
-
-static void iceland_dpm_set_funcs(struct amdgpu_device *adev);
-
-static int iceland_dpm_early_init(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       iceland_dpm_set_funcs(adev);
-
-       return 0;
-}
-
-static int iceland_dpm_init_microcode(struct amdgpu_device *adev)
-{
-       char fw_name[30] = "amdgpu/topaz_smc.bin";
-       int err;
-
-       err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
-       if (err)
-               goto out;
-       err = amdgpu_ucode_validate(adev->pm.fw);
-
-out:
-       if (err) {
-               DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
-               release_firmware(adev->pm.fw);
-               adev->pm.fw = NULL;
-       }
-       return err;
-}
-
-static int iceland_dpm_sw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       ret = iceland_dpm_init_microcode(adev);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int iceland_dpm_sw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       release_firmware(adev->pm.fw);
-       adev->pm.fw = NULL;
-
-       return 0;
-}
-
-static int iceland_dpm_hw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-
-       /* smu init only needs to be called at startup, not resume.
-        * It should be in sw_init, but requires the fw info gathered
-        * in sw_init from other IP modules.
-        */
-       ret = iceland_smu_init(adev);
-       if (ret) {
-               DRM_ERROR("SMU initialization failed\n");
-               goto fail;
-       }
-
-       ret = iceland_smu_start(adev);
-       if (ret) {
-               DRM_ERROR("SMU start failed\n");
-               goto fail;
-       }
-
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-
-fail:
-       adev->firmware.smu_load = false;
-       mutex_unlock(&adev->pm.mutex);
-       return -EINVAL;
-}
-
-static int iceland_dpm_hw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-       /* smu fini only needs to be called at teardown, not suspend.
-        * It should be in sw_fini, but we put it here for symmetry
-        * with smu init.
-        */
-       iceland_smu_fini(adev);
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-}
-
-static int iceland_dpm_suspend(void *handle)
-{
-       return 0;
-}
-
-static int iceland_dpm_resume(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-
-       ret = iceland_smu_start(adev);
-       if (ret) {
-               DRM_ERROR("SMU start failed\n");
-               goto fail;
-       }
-
-fail:
-       mutex_unlock(&adev->pm.mutex);
-       return ret;
-}
-
-static int iceland_dpm_set_clockgating_state(void *handle,
-                       enum amd_clockgating_state state)
-{
-       return 0;
-}
-
-static int iceland_dpm_set_powergating_state(void *handle,
-                       enum amd_powergating_state state)
-{
-       return 0;
-}
-
-const struct amd_ip_funcs iceland_dpm_ip_funcs = {
-       .name = "iceland_dpm",
-       .early_init = iceland_dpm_early_init,
-       .late_init = NULL,
-       .sw_init = iceland_dpm_sw_init,
-       .sw_fini = iceland_dpm_sw_fini,
-       .hw_init = iceland_dpm_hw_init,
-       .hw_fini = iceland_dpm_hw_fini,
-       .suspend = iceland_dpm_suspend,
-       .resume = iceland_dpm_resume,
-       .is_idle = NULL,
-       .wait_for_idle = NULL,
-       .soft_reset = NULL,
-       .set_clockgating_state = iceland_dpm_set_clockgating_state,
-       .set_powergating_state = iceland_dpm_set_powergating_state,
-};
-
-static const struct amdgpu_dpm_funcs iceland_dpm_funcs = {
-       .get_temperature = NULL,
-       .pre_set_power_state = NULL,
-       .set_power_state = NULL,
-       .post_set_power_state = NULL,
-       .display_configuration_changed = NULL,
-       .get_sclk = NULL,
-       .get_mclk = NULL,
-       .print_power_state = NULL,
-       .debugfs_print_current_performance_level = NULL,
-       .force_performance_level = NULL,
-       .vblank_too_short = NULL,
-       .powergate_uvd = NULL,
-};
-
-static void iceland_dpm_set_funcs(struct amdgpu_device *adev)
-{
-       if (NULL == adev->pm.funcs)
-               adev->pm.funcs = &iceland_dpm_funcs;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_smc.c b/drivers/gpu/drm/amd/amdgpu/iceland_smc.c
deleted file mode 100644 (file)
index 2118399..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "ppsmc.h"
-#include "iceland_smum.h"
-#include "smu_ucode_xfer_vi.h"
-#include "amdgpu_ucode.h"
-
-#include "smu/smu_7_1_1_d.h"
-#include "smu/smu_7_1_1_sh_mask.h"
-
-#define ICELAND_SMC_SIZE 0x20000
-
-static int iceland_set_smc_sram_address(struct amdgpu_device *adev,
-                                       uint32_t smc_address, uint32_t limit)
-{
-       uint32_t val;
-
-       if (smc_address & 3)
-               return -EINVAL;
-
-       if ((smc_address + 3) > limit)
-               return -EINVAL;
-
-       WREG32(mmSMC_IND_INDEX_0, smc_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       return 0;
-}
-
-static int iceland_copy_bytes_to_smc(struct amdgpu_device *adev,
-                                    uint32_t smc_start_address,
-                                    const uint8_t *src,
-                                    uint32_t byte_count, uint32_t limit)
-{
-       uint32_t addr;
-       uint32_t data, orig_data;
-       int result = 0;
-       uint32_t extra_shift;
-       unsigned long flags;
-
-       if (smc_start_address & 3)
-               return -EINVAL;
-
-       if ((smc_start_address + byte_count) > limit)
-               return -EINVAL;
-
-       addr = smc_start_address;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       while (byte_count >= 4) {
-               /* Bytes are written into the SMC addres space with the MSB first */
-               data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
-
-               result = iceland_set_smc_sram_address(adev, addr, limit);
-
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-
-               src += 4;
-               byte_count -= 4;
-               addr += 4;
-       }
-
-       if (0 != byte_count) {
-               /* Now write odd bytes left, do a read modify write cycle */
-               data = 0;
-
-               result = iceland_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               orig_data = RREG32(mmSMC_IND_DATA_0);
-               extra_shift = 8 * (4 - byte_count);
-
-               while (byte_count > 0) {
-                       data = (data << 8) + *src++;
-                       byte_count--;
-               }
-
-               data <<= extra_shift;
-               data |= (orig_data & ~((~0UL) << extra_shift));
-
-               result = iceland_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-       }
-
-out:
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-void iceland_start_smc(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-}
-
-void iceland_reset_smc(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-}
-
-static int iceland_program_jump_on_start(struct amdgpu_device *adev)
-{
-       static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
-       iceland_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
-
-       return 0;
-}
-
-void iceland_stop_smc_clock(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-}
-
-void iceland_start_smc_clock(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-}
-
-static bool iceland_is_smc_ram_running(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
-
-       return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
-}
-
-static int wait_smu_response(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32(mmSMC_RESP_0);
-               if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int iceland_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
-{
-       if (!iceland_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send message\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int iceland_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
-                                                  PPSMC_Msg msg)
-{
-       if (!iceland_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-static int iceland_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
-                                                 PPSMC_Msg msg,
-                                                 uint32_t parameter)
-{
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return iceland_send_msg_to_smc(adev, msg);
-}
-
-static int iceland_send_msg_to_smc_with_parameter_without_waiting(
-                                       struct amdgpu_device *adev,
-                                       PPSMC_Msg msg, uint32_t parameter)
-{
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return iceland_send_msg_to_smc_without_waiting(adev, msg);
-}
-
-#if 0 /* not used yet */
-static int iceland_wait_for_smc_inactive(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       if (!iceland_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-               if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-#endif
-
-static int iceland_smu_upload_firmware_image(struct amdgpu_device *adev)
-{
-       const struct smc_firmware_header_v1_0 *hdr;
-       uint32_t ucode_size;
-       uint32_t ucode_start_address;
-       const uint8_t *src;
-       uint32_t val;
-       uint32_t byte_count;
-       uint32_t data;
-       unsigned long flags;
-       int i;
-
-       if (!adev->pm.fw)
-               return -EINVAL;
-
-       /* Skip SMC ucode loading on SR-IOV capable boards.
-        * vbios does this for us in asic_init in that case.
-        */
-       if (adev->virtualization.supports_sr_iov)
-               return 0;
-
-       hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
-       amdgpu_ucode_print_smc_hdr(&hdr->header);
-
-       adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
-       ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
-       ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
-       src = (const uint8_t *)
-               (adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
-
-       if (ucode_size & 3) {
-               DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
-               return -EINVAL;
-       }
-
-       if (ucode_size > ICELAND_SMC_SIZE) {
-               DRM_ERROR("SMC address is beyond the SMC RAM area\n");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixRCU_UC_EVENTS);
-               if (REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done) == 0)
-                       break;
-               udelay(1);
-       }
-       val = RREG32_SMC(ixSMC_SYSCON_MISC_CNTL);
-       WREG32_SMC(ixSMC_SYSCON_MISC_CNTL, val | 1);
-
-       iceland_stop_smc_clock(adev);
-       iceland_reset_smc(adev);
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       byte_count = ucode_size;
-       while (byte_count >= 4) {
-               data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
-               WREG32(mmSMC_IND_DATA_0, data);
-               src += 4;
-               byte_count -= 4;
-       }
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-
-       return 0;
-}
-
-#if 0 /* not used yet */
-static int iceland_read_smc_sram_dword(struct amdgpu_device *adev,
-                                      uint32_t smc_address,
-                                      uint32_t *value,
-                                      uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = iceland_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               *value = RREG32(mmSMC_IND_DATA_0);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int iceland_write_smc_sram_dword(struct amdgpu_device *adev,
-                                       uint32_t smc_address,
-                                       uint32_t value,
-                                       uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = iceland_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               WREG32(mmSMC_IND_DATA_0, value);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int iceland_smu_stop_smc(struct amdgpu_device *adev)
-{
-       iceland_reset_smc(adev);
-       iceland_stop_smc_clock(adev);
-
-       return 0;
-}
-#endif
-
-static int iceland_smu_start_smc(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       iceland_program_jump_on_start(adev);
-       iceland_start_smc_clock(adev);
-       iceland_start_smc(adev);
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixFIRMWARE_FLAGS);
-               if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED) == 1)
-                       break;
-               udelay(1);
-       }
-       return 0;
-}
-
-static enum AMDGPU_UCODE_ID iceland_convert_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case UCODE_ID_SDMA0:
-                       return AMDGPU_UCODE_ID_SDMA0;
-               case UCODE_ID_SDMA1:
-                       return AMDGPU_UCODE_ID_SDMA1;
-               case UCODE_ID_CP_CE:
-                       return AMDGPU_UCODE_ID_CP_CE;
-               case UCODE_ID_CP_PFP:
-                       return AMDGPU_UCODE_ID_CP_PFP;
-               case UCODE_ID_CP_ME:
-                       return AMDGPU_UCODE_ID_CP_ME;
-               case UCODE_ID_CP_MEC:
-               case UCODE_ID_CP_MEC_JT1:
-                       return AMDGPU_UCODE_ID_CP_MEC1;
-               case UCODE_ID_CP_MEC_JT2:
-                       return AMDGPU_UCODE_ID_CP_MEC2;
-               case UCODE_ID_RLC_G:
-                       return AMDGPU_UCODE_ID_RLC_G;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return AMDGPU_UCODE_ID_MAXIMUM;
-       }
-}
-
-static uint32_t iceland_smu_get_mask_for_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case AMDGPU_UCODE_ID_SDMA0:
-                       return UCODE_ID_SDMA0_MASK;
-               case AMDGPU_UCODE_ID_SDMA1:
-                       return UCODE_ID_SDMA1_MASK;
-               case AMDGPU_UCODE_ID_CP_CE:
-                       return UCODE_ID_CP_CE_MASK;
-               case AMDGPU_UCODE_ID_CP_PFP:
-                       return UCODE_ID_CP_PFP_MASK;
-               case AMDGPU_UCODE_ID_CP_ME:
-                       return UCODE_ID_CP_ME_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC1:
-                       return UCODE_ID_CP_MEC_MASK | UCODE_ID_CP_MEC_JT1_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC2:
-                       return UCODE_ID_CP_MEC_MASK;
-               case AMDGPU_UCODE_ID_RLC_G:
-                       return UCODE_ID_RLC_G_MASK;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return 0;
-       }
-}
-
-static int iceland_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
-                                                     uint32_t fw_type,
-                                                     struct SMU_Entry *entry)
-{
-       enum AMDGPU_UCODE_ID id = iceland_convert_fw_type(fw_type);
-       struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
-       const struct gfx_firmware_header_v1_0 *header = NULL;
-       uint64_t gpu_addr;
-       uint32_t data_size;
-
-       if (ucode->fw == NULL)
-               return -EINVAL;
-
-       gpu_addr  = ucode->mc_addr;
-       header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
-       data_size = le32_to_cpu(header->header.ucode_size_bytes);
-
-       entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
-       entry->id = (uint16_t)fw_type;
-       entry->image_addr_high = upper_32_bits(gpu_addr);
-       entry->image_addr_low = lower_32_bits(gpu_addr);
-       entry->meta_data_addr_high = 0;
-       entry->meta_data_addr_low = 0;
-       entry->data_size_byte = data_size;
-       entry->num_register_entries = 0;
-       entry->flags = 0;
-
-       return 0;
-}
-
-static int iceland_smu_request_load_fw(struct amdgpu_device *adev)
-{
-       struct iceland_smu_private_data *private = (struct iceland_smu_private_data *)adev->smu.priv;
-       struct SMU_DRAMData_TOC *toc;
-       uint32_t fw_to_load;
-
-       toc = (struct SMU_DRAMData_TOC *)private->header;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       if (!adev->firmware.smu_load)
-               return 0;
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for RLC\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for CE\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for PFP\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for ME\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA0\n");
-               return -EINVAL;
-       }
-
-       if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA1\n");
-               return -EINVAL;
-       }
-
-       iceland_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
-       iceland_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK |
-                       UCODE_ID_SDMA0_MASK |
-                       UCODE_ID_SDMA1_MASK |
-                       UCODE_ID_CP_CE_MASK |
-                       UCODE_ID_CP_ME_MASK |
-                       UCODE_ID_CP_PFP_MASK |
-                       UCODE_ID_CP_MEC_MASK |
-                       UCODE_ID_CP_MEC_JT1_MASK;
-
-
-       if (iceland_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
-               DRM_ERROR("Fail to request SMU load ucode\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int iceland_smu_check_fw_load_finish(struct amdgpu_device *adev,
-                                           uint32_t fw_type)
-{
-       uint32_t fw_mask = iceland_smu_get_mask_for_fw_type(fw_type);
-       int i;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_27) & fw_mask))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("check firmware loading failed\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-int iceland_smu_start(struct amdgpu_device *adev)
-{
-       int result;
-
-       result = iceland_smu_upload_firmware_image(adev);
-       if (result)
-               return result;
-       result = iceland_smu_start_smc(adev);
-       if (result)
-               return result;
-
-       return iceland_smu_request_load_fw(adev);
-}
-
-static const struct amdgpu_smumgr_funcs iceland_smumgr_funcs = {
-       .check_fw_load_finish = iceland_smu_check_fw_load_finish,
-       .request_smu_load_fw = NULL,
-       .request_smu_specific_fw = NULL,
-};
-
-int iceland_smu_init(struct amdgpu_device *adev)
-{
-       struct iceland_smu_private_data *private;
-       uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
-       uint64_t mc_addr;
-       void *toc_buf_ptr;
-       int ret;
-
-       private = kzalloc(sizeof(struct iceland_smu_private_data), GFP_KERNEL);
-       if (NULL == private)
-               return -ENOMEM;
-
-       /* allocate firmware buffers */
-       if (adev->firmware.smu_load)
-               amdgpu_ucode_init_bo(adev);
-
-       adev->smu.priv = private;
-       adev->smu.fw_flags = 0;
-
-       /* Allocate FW image data structure and header buffer */
-       ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
-                              true, AMDGPU_GEM_DOMAIN_VRAM,
-                              AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                              NULL, NULL, toc_buf);
-       if (ret) {
-               DRM_ERROR("Failed to allocate memory for TOC buffer\n");
-               return -ENOMEM;
-       }
-
-       /* Retrieve GPU address for header buffer and internal buffer */
-       ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
-       if (ret) {
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to reserve the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to pin the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to map the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       amdgpu_bo_unreserve(adev->smu.toc_buf);
-       private->header_addr_low = lower_32_bits(mc_addr);
-       private->header_addr_high = upper_32_bits(mc_addr);
-       private->header = toc_buf_ptr;
-
-       adev->smu.smumgr_funcs = &iceland_smumgr_funcs;
-
-       return 0;
-}
-
-int iceland_smu_fini(struct amdgpu_device *adev)
-{
-       amdgpu_bo_unref(&adev->smu.toc_buf);
-       kfree(adev->smu.priv);
-       adev->smu.priv = NULL;
-       if (adev->firmware.fw_buf)
-               amdgpu_ucode_fini_bo(adev);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/iceland_smum.h b/drivers/gpu/drm/amd/amdgpu/iceland_smum.h
deleted file mode 100644 (file)
index 5983e31..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2014 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 ICELAND_SMUM_H
-#define ICELAND_SMUM_H
-
-#include "ppsmc.h"
-
-extern int iceland_smu_init(struct amdgpu_device *adev);
-extern int iceland_smu_fini(struct amdgpu_device *adev);
-extern int iceland_smu_start(struct amdgpu_device *adev);
-
-struct iceland_smu_private_data
-{
-       uint8_t *header;
-       uint8_t *mec_image;
-       uint32_t header_addr_high;
-       uint32_t header_addr_low;
-};
-
-#endif
index a845e88..f8618a3 100644 (file)
@@ -2845,7 +2845,11 @@ static int kv_dpm_init(struct amdgpu_device *adev)
                pi->caps_tcp_ramping = true;
        }
 
-       pi->caps_sclk_ds = true;
+       if (amdgpu_sclk_deep_sleep_en)
+               pi->caps_sclk_ds = true;
+       else
+               pi->caps_sclk_ds = false;
+
        pi->enable_auto_thermal_throttling = true;
        pi->disable_nb_ps3_in_battery = false;
        if (amdgpu_bapm == 0)
diff --git a/drivers/gpu/drm/amd/amdgpu/r600_dpm.h b/drivers/gpu/drm/amd/amdgpu/r600_dpm.h
new file mode 100644 (file)
index 0000000..055321f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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
+
+/* XXX are these ok? */
+#define R600_TEMP_RANGE_MIN (90 * 1000)
+#define R600_TEMP_RANGE_MAX (120 * 1000)
+
+#define FDO_PWM_MODE_STATIC  1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+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,
+};
+#endif
index a64715d..565dab3 100644 (file)
@@ -190,12 +190,8 @@ out:
  */
 static uint32_t sdma_v2_4_ring_get_rptr(struct amdgpu_ring *ring)
 {
-       u32 rptr;
-
        /* XXX check if swapping is necessary on BE */
-       rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2;
-
-       return rptr;
+       return ring->adev->wb.wb[ring->rptr_offs] >> 2;
 }
 
 /**
@@ -749,24 +745,16 @@ static void sdma_v2_4_vm_copy_pte(struct amdgpu_ib *ib,
                                  uint64_t pe, uint64_t src,
                                  unsigned count)
 {
-       while (count) {
-               unsigned bytes = count * 8;
-               if (bytes > 0x1FFFF8)
-                       bytes = 0x1FFFF8;
-
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
-                       SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
-               ib->ptr[ib->length_dw++] = bytes;
-               ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
-               ib->ptr[ib->length_dw++] = lower_32_bits(src);
-               ib->ptr[ib->length_dw++] = upper_32_bits(src);
-               ib->ptr[ib->length_dw++] = lower_32_bits(pe);
-               ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
-               pe += bytes;
-               src += bytes;
-               count -= bytes / 8;
-       }
+       unsigned bytes = count * 8;
+
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
+               SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
+       ib->ptr[ib->length_dw++] = bytes;
+       ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
+       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+       ib->ptr[ib->length_dw++] = upper_32_bits(src);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
 }
 
 /**
@@ -774,39 +762,27 @@ static void sdma_v2_4_vm_copy_pte(struct amdgpu_ib *ib,
  *
  * @ib: indirect buffer to fill with commands
  * @pe: addr of the page entry
- * @addr: dst addr to write into pe
+ * @value: dst addr to write into pe
  * @count: number of page entries to update
  * @incr: increase next addr by incr bytes
- * @flags: access flags
  *
  * Update PTEs by writing them manually using sDMA (CIK).
  */
-static void sdma_v2_4_vm_write_pte(struct amdgpu_ib *ib,
-                                  const dma_addr_t *pages_addr, uint64_t pe,
-                                  uint64_t addr, unsigned count,
-                                  uint32_t incr, uint32_t flags)
+static void sdma_v2_4_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
+                                  uint64_t value, unsigned count,
+                                  uint32_t incr)
 {
-       uint64_t value;
-       unsigned ndw;
-
-       while (count) {
-               ndw = count * 2;
-               if (ndw > 0xFFFFE)
-                       ndw = 0xFFFFE;
-
-               /* for non-physically contiguous pages (system) */
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
-                       SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
-               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) {
-                       value = amdgpu_vm_map_gart(pages_addr, addr);
-                       addr += incr;
-                       value |= flags;
-                       ib->ptr[ib->length_dw++] = value;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(value);
-               }
+       unsigned ndw = count * 2;
+
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
+               SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
+       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) {
+               ib->ptr[ib->length_dw++] = lower_32_bits(value);
+               ib->ptr[ib->length_dw++] = upper_32_bits(value);
+               value += incr;
        }
 }
 
@@ -822,40 +798,21 @@ static void sdma_v2_4_vm_write_pte(struct amdgpu_ib *ib,
  *
  * Update the page tables using sDMA (CIK).
  */
-static void sdma_v2_4_vm_set_pte_pde(struct amdgpu_ib *ib,
-                                    uint64_t pe,
+static void sdma_v2_4_vm_set_pte_pde(struct amdgpu_ib *ib, uint64_t pe,
                                     uint64_t addr, unsigned count,
                                     uint32_t incr, uint32_t flags)
 {
-       uint64_t value;
-       unsigned ndw;
-
-       while (count) {
-               ndw = count;
-               if (ndw > 0x7FFFF)
-                       ndw = 0x7FFFF;
-
-               if (flags & AMDGPU_PTE_VALID)
-                       value = addr;
-               else
-                       value = 0;
-
-               /* for physically contiguous pages (vram) */
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
-               ib->ptr[ib->length_dw++] = pe; /* dst addr */
-               ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-               ib->ptr[ib->length_dw++] = 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;
-       }
+       /* for physically contiguous pages (vram) */
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe); /* dst addr */
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       ib->ptr[ib->length_dw++] = flags; /* mask */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = lower_32_bits(addr); /* value */
+       ib->ptr[ib->length_dw++] = upper_32_bits(addr);
+       ib->ptr[ib->length_dw++] = incr; /* increment size */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = count; /* number of entries */
 }
 
 /**
@@ -945,6 +902,22 @@ static void sdma_v2_4_ring_emit_vm_flush(struct amdgpu_ring *ring,
                          SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
 }
 
+static unsigned sdma_v2_4_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               7 + 6; /* sdma_v2_4_ring_emit_ib */
+}
+
+static unsigned sdma_v2_4_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               6 + /* sdma_v2_4_ring_emit_hdp_flush */
+               3 + /* sdma_v2_4_ring_emit_hdp_invalidate */
+               6 + /* sdma_v2_4_ring_emit_pipeline_sync */
+               12 + /* sdma_v2_4_ring_emit_vm_flush */
+               10 + 10 + 10; /* sdma_v2_4_ring_emit_fence x3 for user fence, vm fence */
+}
+
 static int sdma_v2_4_early_init(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -1263,6 +1236,8 @@ static const struct amdgpu_ring_funcs sdma_v2_4_ring_funcs = {
        .test_ib = sdma_v2_4_ring_test_ib,
        .insert_nop = sdma_v2_4_ring_insert_nop,
        .pad_ib = sdma_v2_4_ring_pad_ib,
+       .get_emit_ib_size = sdma_v2_4_ring_get_emit_ib_size,
+       .get_dma_frame_size = sdma_v2_4_ring_get_dma_frame_size,
 };
 
 static void sdma_v2_4_set_ring_funcs(struct amdgpu_device *adev)
index 653ce5e..f325fd8 100644 (file)
@@ -335,12 +335,8 @@ out:
  */
 static uint32_t sdma_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
 {
-       u32 rptr;
-
        /* XXX check if swapping is necessary on BE */
-       rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2;
-
-       return rptr;
+       return ring->adev->wb.wb[ring->rptr_offs] >> 2;
 }
 
 /**
@@ -499,31 +495,6 @@ static void sdma_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se
        amdgpu_ring_write(ring, SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(0));
 }
 
-unsigned init_cond_exec(struct amdgpu_ring *ring)
-{
-       unsigned ret;
-       amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_COND_EXE));
-       amdgpu_ring_write(ring, lower_32_bits(ring->cond_exe_gpu_addr));
-       amdgpu_ring_write(ring, upper_32_bits(ring->cond_exe_gpu_addr));
-       amdgpu_ring_write(ring, 1);
-       ret = ring->wptr;/* this is the offset we need patch later */
-       amdgpu_ring_write(ring, 0x55aa55aa);/* insert dummy here and patch it later */
-       return ret;
-}
-
-void patch_cond_exec(struct amdgpu_ring *ring, unsigned offset)
-{
-       unsigned cur;
-       BUG_ON(ring->ring[offset] != 0x55aa55aa);
-
-       cur = ring->wptr - 1;
-       if (likely(cur > offset))
-               ring->ring[offset] = cur - offset;
-       else
-               ring->ring[offset] = (ring->ring_size>>2) - offset + cur;
-}
-
-
 /**
  * sdma_v3_0_gfx_stop - stop the gfx async dma engines
  *
@@ -976,24 +947,16 @@ static void sdma_v3_0_vm_copy_pte(struct amdgpu_ib *ib,
                                  uint64_t pe, uint64_t src,
                                  unsigned count)
 {
-       while (count) {
-               unsigned bytes = count * 8;
-               if (bytes > 0x1FFFF8)
-                       bytes = 0x1FFFF8;
-
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
-                       SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
-               ib->ptr[ib->length_dw++] = bytes;
-               ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
-               ib->ptr[ib->length_dw++] = lower_32_bits(src);
-               ib->ptr[ib->length_dw++] = upper_32_bits(src);
-               ib->ptr[ib->length_dw++] = lower_32_bits(pe);
-               ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-
-               pe += bytes;
-               src += bytes;
-               count -= bytes / 8;
-       }
+       unsigned bytes = count * 8;
+
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
+               SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
+       ib->ptr[ib->length_dw++] = bytes;
+       ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
+       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+       ib->ptr[ib->length_dw++] = upper_32_bits(src);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
 }
 
 /**
@@ -1001,39 +964,27 @@ static void sdma_v3_0_vm_copy_pte(struct amdgpu_ib *ib,
  *
  * @ib: indirect buffer to fill with commands
  * @pe: addr of the page entry
- * @addr: dst addr to write into pe
+ * @value: dst addr to write into pe
  * @count: number of page entries to update
  * @incr: increase next addr by incr bytes
- * @flags: access flags
  *
  * Update PTEs by writing them manually using sDMA (CIK).
  */
-static void sdma_v3_0_vm_write_pte(struct amdgpu_ib *ib,
-                                  const dma_addr_t *pages_addr, uint64_t pe,
-                                  uint64_t addr, unsigned count,
-                                  uint32_t incr, uint32_t flags)
-{
-       uint64_t value;
-       unsigned ndw;
-
-       while (count) {
-               ndw = count * 2;
-               if (ndw > 0xFFFFE)
-                       ndw = 0xFFFFE;
-
-               /* for non-physically contiguous pages (system) */
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
-                       SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
-               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) {
-                       value = amdgpu_vm_map_gart(pages_addr, addr);
-                       addr += incr;
-                       value |= flags;
-                       ib->ptr[ib->length_dw++] = value;
-                       ib->ptr[ib->length_dw++] = upper_32_bits(value);
-               }
+static void sdma_v3_0_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
+                                  uint64_t value, unsigned count,
+                                  uint32_t incr)
+{
+       unsigned ndw = count * 2;
+
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
+               SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       ib->ptr[ib->length_dw++] = ndw;
+       for (; ndw > 0; ndw -= 2, --count, pe += 8) {
+               ib->ptr[ib->length_dw++] = lower_32_bits(value);
+               ib->ptr[ib->length_dw++] = upper_32_bits(value);
+               value += incr;
        }
 }
 
@@ -1049,40 +1000,21 @@ static void sdma_v3_0_vm_write_pte(struct amdgpu_ib *ib,
  *
  * Update the page tables using sDMA (CIK).
  */
-static void sdma_v3_0_vm_set_pte_pde(struct amdgpu_ib *ib,
-                                    uint64_t pe,
+static void sdma_v3_0_vm_set_pte_pde(struct amdgpu_ib *ib, uint64_t pe,
                                     uint64_t addr, unsigned count,
                                     uint32_t incr, uint32_t flags)
 {
-       uint64_t value;
-       unsigned ndw;
-
-       while (count) {
-               ndw = count;
-               if (ndw > 0x7FFFF)
-                       ndw = 0x7FFFF;
-
-               if (flags & AMDGPU_PTE_VALID)
-                       value = addr;
-               else
-                       value = 0;
-
-               /* for physically contiguous pages (vram) */
-               ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
-               ib->ptr[ib->length_dw++] = pe; /* dst addr */
-               ib->ptr[ib->length_dw++] = upper_32_bits(pe);
-               ib->ptr[ib->length_dw++] = 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;
-       }
+       /* for physically contiguous pages (vram) */
+       ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe); /* dst addr */
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       ib->ptr[ib->length_dw++] = flags; /* mask */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = lower_32_bits(addr); /* value */
+       ib->ptr[ib->length_dw++] = upper_32_bits(addr);
+       ib->ptr[ib->length_dw++] = incr; /* increment size */
+       ib->ptr[ib->length_dw++] = 0;
+       ib->ptr[ib->length_dw++] = count; /* number of entries */
 }
 
 /**
@@ -1172,6 +1104,22 @@ static void sdma_v3_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
                          SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
 }
 
+static unsigned sdma_v3_0_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               7 + 6; /* sdma_v3_0_ring_emit_ib */
+}
+
+static unsigned sdma_v3_0_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               6 + /* sdma_v3_0_ring_emit_hdp_flush */
+               3 + /* sdma_v3_0_ring_emit_hdp_invalidate */
+               6 + /* sdma_v3_0_ring_emit_pipeline_sync */
+               12 + /* sdma_v3_0_ring_emit_vm_flush */
+               10 + 10 + 10; /* sdma_v3_0_ring_emit_fence x3 for user fence, vm fence */
+}
+
 static int sdma_v3_0_early_init(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -1320,27 +1268,78 @@ static int sdma_v3_0_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
-static int sdma_v3_0_soft_reset(void *handle)
+static int sdma_v3_0_check_soft_reset(void *handle)
 {
-       u32 srbm_soft_reset = 0;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
        u32 tmp = RREG32(mmSRBM_STATUS2);
 
-       if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK) {
-               /* sdma0 */
-               tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
-               tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
-               WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
+       if ((tmp & SRBM_STATUS2__SDMA_BUSY_MASK) ||
+           (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK)) {
                srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
-       }
-       if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK) {
-               /* sdma1 */
-               tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
-               tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
-               WREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET, tmp);
                srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK;
        }
 
+       if (srbm_soft_reset) {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang = true;
+               adev->sdma.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang = false;
+               adev->sdma.srbm_soft_reset = 0;
+       }
+
+       return 0;
+}
+
+static int sdma_v3_0_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+               return 0;
+
+       srbm_soft_reset = adev->sdma.srbm_soft_reset;
+
+       if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) ||
+           REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) {
+               sdma_v3_0_ctx_switch_enable(adev, false);
+               sdma_v3_0_enable(adev, false);
+       }
+
+       return 0;
+}
+
+static int sdma_v3_0_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+               return 0;
+
+       srbm_soft_reset = adev->sdma.srbm_soft_reset;
+
+       if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) ||
+           REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) {
+               sdma_v3_0_gfx_resume(adev);
+               sdma_v3_0_rlc_resume(adev);
+       }
+
+       return 0;
+}
+
+static int sdma_v3_0_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
+       u32 tmp;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_SDMA].hang)
+               return 0;
+
+       srbm_soft_reset = adev->sdma.srbm_soft_reset;
+
        if (srbm_soft_reset) {
                tmp = RREG32(mmSRBM_SOFT_RESET);
                tmp |= srbm_soft_reset;
@@ -1559,6 +1558,9 @@ const struct amd_ip_funcs sdma_v3_0_ip_funcs = {
        .resume = sdma_v3_0_resume,
        .is_idle = sdma_v3_0_is_idle,
        .wait_for_idle = sdma_v3_0_wait_for_idle,
+       .check_soft_reset = sdma_v3_0_check_soft_reset,
+       .pre_soft_reset = sdma_v3_0_pre_soft_reset,
+       .post_soft_reset = sdma_v3_0_post_soft_reset,
        .soft_reset = sdma_v3_0_soft_reset,
        .set_clockgating_state = sdma_v3_0_set_clockgating_state,
        .set_powergating_state = sdma_v3_0_set_powergating_state,
@@ -1579,6 +1581,8 @@ static const struct amdgpu_ring_funcs sdma_v3_0_ring_funcs = {
        .test_ib = sdma_v3_0_ring_test_ib,
        .insert_nop = sdma_v3_0_ring_insert_nop,
        .pad_ib = sdma_v3_0_ring_pad_ib,
+       .get_emit_ib_size = sdma_v3_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = sdma_v3_0_ring_get_dma_frame_size,
 };
 
 static void sdma_v3_0_set_ring_funcs(struct amdgpu_device *adev)
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
new file mode 100644 (file)
index 0000000..dc9511c
--- /dev/null
@@ -0,0 +1,1965 @@
+/*
+ * Copyright 2015 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 <linux/slab.h>
+#include <linux/module.h>
+#include "drmP.h"
+#include "amdgpu.h"
+#include "amdgpu_atombios.h"
+#include "amdgpu_ih.h"
+#include "amdgpu_uvd.h"
+#include "amdgpu_vce.h"
+#include "atom.h"
+#include "amdgpu_powerplay.h"
+#include "si/sid.h"
+#include "si_ih.h"
+#include "gfx_v6_0.h"
+#include "gmc_v6_0.h"
+#include "si_dma.h"
+#include "dce_v6_0.h"
+#include "si.h"
+
+static const u32 tahiti_golden_registers[] =
+{
+       0x2684, 0x00010000, 0x00018208,
+       0x260c, 0xffffffff, 0x00000000,
+       0x260d, 0xf00fffff, 0x00000400,
+       0x260e, 0x0002021c, 0x00020200,
+       0x031e, 0x00000080, 0x00000000,
+       0x340c, 0x000300c0, 0x00800040,
+       0x360c, 0x000300c0, 0x00800040,
+       0x16ec, 0x000000f0, 0x00000070,
+       0x16f0, 0x00200000, 0x50100000,
+       0x1c0c, 0x31000311, 0x00000011,
+       0x09df, 0x00000003, 0x000007ff,
+       0x0903, 0x000007ff, 0x00000000,
+       0x2285, 0xf000001f, 0x00000007,
+       0x22c9, 0xffffffff, 0x00ffffff,
+       0x22c4, 0x0000ff0f, 0x00000000,
+       0xa293, 0x07ffffff, 0x4e000000,
+       0xa0d4, 0x3f3f3fff, 0x2a00126a,
+       0x000c, 0x000000ff, 0x0040,
+       0x000d, 0x00000040, 0x00004040,
+       0x2440, 0x07ffffff, 0x03000000,
+       0x23a2, 0x01ff1f3f, 0x00000000,
+       0x23a1, 0x01ff1f3f, 0x00000000,
+       0x2418, 0x0000007f, 0x00000020,
+       0x2542, 0x00010000, 0x00010000,
+       0x2b05, 0x00000200, 0x000002fb,
+       0x2b04, 0xffffffff, 0x0000543b,
+       0x2b03, 0xffffffff, 0xa9210876,
+       0x2234, 0xffffffff, 0x000fff40,
+       0x2235, 0x0000001f, 0x00000010,
+       0x0504, 0x20000000, 0x20fffed8,
+       0x0570, 0x000c0fc0, 0x000c0400
+};
+
+static const u32 tahiti_golden_registers2[] =
+{
+       0x0319, 0x00000001, 0x00000001
+};
+
+static const u32 tahiti_golden_rlc_registers[] =
+{
+       0x3109, 0xffffffff, 0x00601005,
+       0x311f, 0xffffffff, 0x10104040,
+       0x3122, 0xffffffff, 0x0100000a,
+       0x30c5, 0xffffffff, 0x00000800,
+       0x30c3, 0xffffffff, 0x800000f4,
+       0x3d2a, 0xffffffff, 0x00000000
+};
+
+static const u32 pitcairn_golden_registers[] =
+{
+       0x2684, 0x00010000, 0x00018208,
+       0x260c, 0xffffffff, 0x00000000,
+       0x260d, 0xf00fffff, 0x00000400,
+       0x260e, 0x0002021c, 0x00020200,
+       0x031e, 0x00000080, 0x00000000,
+       0x340c, 0x000300c0, 0x00800040,
+       0x360c, 0x000300c0, 0x00800040,
+       0x16ec, 0x000000f0, 0x00000070,
+       0x16f0, 0x00200000, 0x50100000,
+       0x1c0c, 0x31000311, 0x00000011,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0903, 0x000007ff, 0x00000000,
+       0x2285, 0xf000001f, 0x00000007,
+       0x22c9, 0xffffffff, 0x00ffffff,
+       0x22c4, 0x0000ff0f, 0x00000000,
+       0xa293, 0x07ffffff, 0x4e000000,
+       0xa0d4, 0x3f3f3fff, 0x2a00126a,
+       0x000c, 0x000000ff, 0x0040,
+       0x000d, 0x00000040, 0x00004040,
+       0x2440, 0x07ffffff, 0x03000000,
+       0x2418, 0x0000007f, 0x00000020,
+       0x2542, 0x00010000, 0x00010000,
+       0x2b05, 0x000003ff, 0x000000f7,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b03, 0xffffffff, 0x32761054,
+       0x2235, 0x0000001f, 0x00000010,
+       0x0570, 0x000c0fc0, 0x000c0400
+};
+
+static const u32 pitcairn_golden_rlc_registers[] =
+{
+       0x3109, 0xffffffff, 0x00601004,
+       0x311f, 0xffffffff, 0x10102020,
+       0x3122, 0xffffffff, 0x01000020,
+       0x30c5, 0xffffffff, 0x00000800,
+       0x30c3, 0xffffffff, 0x800000a4
+};
+
+static const u32 verde_pg_init[] =
+{
+       0xd4f, 0xffffffff, 0x40000,
+       0xd4e, 0xffffffff, 0x200010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x7007,
+       0xd4e, 0xffffffff, 0x300010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x400000,
+       0xd4e, 0xffffffff, 0x100010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x120200,
+       0xd4e, 0xffffffff, 0x500010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x1e1e16,
+       0xd4e, 0xffffffff, 0x600010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x171f1e,
+       0xd4e, 0xffffffff, 0x700010ff,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4f, 0xffffffff, 0x0,
+       0xd4e, 0xffffffff, 0x9ff,
+       0xd40, 0xffffffff, 0x0,
+       0xd41, 0xffffffff, 0x10000800,
+       0xd41, 0xffffffff, 0xf,
+       0xd41, 0xffffffff, 0xf,
+       0xd40, 0xffffffff, 0x4,
+       0xd41, 0xffffffff, 0x1000051e,
+       0xd41, 0xffffffff, 0xffff,
+       0xd41, 0xffffffff, 0xffff,
+       0xd40, 0xffffffff, 0x8,
+       0xd41, 0xffffffff, 0x80500,
+       0xd40, 0xffffffff, 0x12,
+       0xd41, 0xffffffff, 0x9050c,
+       0xd40, 0xffffffff, 0x1d,
+       0xd41, 0xffffffff, 0xb052c,
+       0xd40, 0xffffffff, 0x2a,
+       0xd41, 0xffffffff, 0x1053e,
+       0xd40, 0xffffffff, 0x2d,
+       0xd41, 0xffffffff, 0x10546,
+       0xd40, 0xffffffff, 0x30,
+       0xd41, 0xffffffff, 0xa054e,
+       0xd40, 0xffffffff, 0x3c,
+       0xd41, 0xffffffff, 0x1055f,
+       0xd40, 0xffffffff, 0x3f,
+       0xd41, 0xffffffff, 0x10567,
+       0xd40, 0xffffffff, 0x42,
+       0xd41, 0xffffffff, 0x1056f,
+       0xd40, 0xffffffff, 0x45,
+       0xd41, 0xffffffff, 0x10572,
+       0xd40, 0xffffffff, 0x48,
+       0xd41, 0xffffffff, 0x20575,
+       0xd40, 0xffffffff, 0x4c,
+       0xd41, 0xffffffff, 0x190801,
+       0xd40, 0xffffffff, 0x67,
+       0xd41, 0xffffffff, 0x1082a,
+       0xd40, 0xffffffff, 0x6a,
+       0xd41, 0xffffffff, 0x1b082d,
+       0xd40, 0xffffffff, 0x87,
+       0xd41, 0xffffffff, 0x310851,
+       0xd40, 0xffffffff, 0xba,
+       0xd41, 0xffffffff, 0x891,
+       0xd40, 0xffffffff, 0xbc,
+       0xd41, 0xffffffff, 0x893,
+       0xd40, 0xffffffff, 0xbe,
+       0xd41, 0xffffffff, 0x20895,
+       0xd40, 0xffffffff, 0xc2,
+       0xd41, 0xffffffff, 0x20899,
+       0xd40, 0xffffffff, 0xc6,
+       0xd41, 0xffffffff, 0x2089d,
+       0xd40, 0xffffffff, 0xca,
+       0xd41, 0xffffffff, 0x8a1,
+       0xd40, 0xffffffff, 0xcc,
+       0xd41, 0xffffffff, 0x8a3,
+       0xd40, 0xffffffff, 0xce,
+       0xd41, 0xffffffff, 0x308a5,
+       0xd40, 0xffffffff, 0xd3,
+       0xd41, 0xffffffff, 0x6d08cd,
+       0xd40, 0xffffffff, 0x142,
+       0xd41, 0xffffffff, 0x2000095a,
+       0xd41, 0xffffffff, 0x1,
+       0xd40, 0xffffffff, 0x144,
+       0xd41, 0xffffffff, 0x301f095b,
+       0xd40, 0xffffffff, 0x165,
+       0xd41, 0xffffffff, 0xc094d,
+       0xd40, 0xffffffff, 0x173,
+       0xd41, 0xffffffff, 0xf096d,
+       0xd40, 0xffffffff, 0x184,
+       0xd41, 0xffffffff, 0x15097f,
+       0xd40, 0xffffffff, 0x19b,
+       0xd41, 0xffffffff, 0xc0998,
+       0xd40, 0xffffffff, 0x1a9,
+       0xd41, 0xffffffff, 0x409a7,
+       0xd40, 0xffffffff, 0x1af,
+       0xd41, 0xffffffff, 0xcdc,
+       0xd40, 0xffffffff, 0x1b1,
+       0xd41, 0xffffffff, 0x800,
+       0xd42, 0xffffffff, 0x6c9b2000,
+       0xd44, 0xfc00, 0x2000,
+       0xd51, 0xffffffff, 0xfc0,
+       0xa35, 0x00000100, 0x100
+};
+
+static const u32 verde_golden_rlc_registers[] =
+{
+       0x3109, 0xffffffff, 0x033f1005,
+       0x311f, 0xffffffff, 0x10808020,
+       0x3122, 0xffffffff, 0x00800008,
+       0x30c5, 0xffffffff, 0x00001000,
+       0x30c3, 0xffffffff, 0x80010014
+};
+
+static const u32 verde_golden_registers[] =
+{
+       0x2684, 0x00010000, 0x00018208,
+       0x260c, 0xffffffff, 0x00000000,
+       0x260d, 0xf00fffff, 0x00000400,
+       0x260e, 0x0002021c, 0x00020200,
+       0x031e, 0x00000080, 0x00000000,
+       0x340c, 0x000300c0, 0x00800040,
+       0x340c, 0x000300c0, 0x00800040,
+       0x360c, 0x000300c0, 0x00800040,
+       0x360c, 0x000300c0, 0x00800040,
+       0x16ec, 0x000000f0, 0x00000070,
+       0x16f0, 0x00200000, 0x50100000,
+
+       0x1c0c, 0x31000311, 0x00000011,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0903, 0x000007ff, 0x00000000,
+       0x0903, 0x000007ff, 0x00000000,
+       0x0903, 0x000007ff, 0x00000000,
+       0x2285, 0xf000001f, 0x00000007,
+       0x2285, 0xf000001f, 0x00000007,
+       0x2285, 0xf000001f, 0x00000007,
+       0x2285, 0xffffffff, 0x00ffffff,
+       0x22c4, 0x0000ff0f, 0x00000000,
+
+       0xa293, 0x07ffffff, 0x4e000000,
+       0xa0d4, 0x3f3f3fff, 0x0000124a,
+       0xa0d4, 0x3f3f3fff, 0x0000124a,
+       0xa0d4, 0x3f3f3fff, 0x0000124a,
+       0x000c, 0x000000ff, 0x0040,
+       0x000d, 0x00000040, 0x00004040,
+       0x2440, 0x07ffffff, 0x03000000,
+       0x2440, 0x07ffffff, 0x03000000,
+       0x23a2, 0x01ff1f3f, 0x00000000,
+       0x23a3, 0x01ff1f3f, 0x00000000,
+       0x23a2, 0x01ff1f3f, 0x00000000,
+       0x23a1, 0x01ff1f3f, 0x00000000,
+       0x23a1, 0x01ff1f3f, 0x00000000,
+
+       0x23a1, 0x01ff1f3f, 0x00000000,
+       0x2418, 0x0000007f, 0x00000020,
+       0x2542, 0x00010000, 0x00010000,
+       0x2b01, 0x000003ff, 0x00000003,
+       0x2b05, 0x000003ff, 0x00000003,
+       0x2b05, 0x000003ff, 0x00000003,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b03, 0xffffffff, 0x00001032,
+       0x2b03, 0xffffffff, 0x00001032,
+       0x2b03, 0xffffffff, 0x00001032,
+       0x2235, 0x0000001f, 0x00000010,
+       0x2235, 0x0000001f, 0x00000010,
+       0x2235, 0x0000001f, 0x00000010,
+       0x0570, 0x000c0fc0, 0x000c0400
+};
+
+static const u32 oland_golden_registers[] =
+{
+       0x2684, 0x00010000, 0x00018208,
+       0x260c, 0xffffffff, 0x00000000,
+       0x260d, 0xf00fffff, 0x00000400,
+       0x260e, 0x0002021c, 0x00020200,
+       0x031e, 0x00000080, 0x00000000,
+       0x340c, 0x000300c0, 0x00800040,
+       0x360c, 0x000300c0, 0x00800040,
+       0x16ec, 0x000000f0, 0x00000070,
+       0x16f9, 0x00200000, 0x50100000,
+       0x1c0c, 0x31000311, 0x00000011,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0903, 0x000007ff, 0x00000000,
+       0x2285, 0xf000001f, 0x00000007,
+       0x22c9, 0xffffffff, 0x00ffffff,
+       0x22c4, 0x0000ff0f, 0x00000000,
+       0xa293, 0x07ffffff, 0x4e000000,
+       0xa0d4, 0x3f3f3fff, 0x00000082,
+       0x000c, 0x000000ff, 0x0040,
+       0x000d, 0x00000040, 0x00004040,
+       0x2440, 0x07ffffff, 0x03000000,
+       0x2418, 0x0000007f, 0x00000020,
+       0x2542, 0x00010000, 0x00010000,
+       0x2b05, 0x000003ff, 0x000000f3,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b03, 0xffffffff, 0x00003210,
+       0x2235, 0x0000001f, 0x00000010,
+       0x0570, 0x000c0fc0, 0x000c0400
+};
+
+static const u32 oland_golden_rlc_registers[] =
+{
+       0x3109, 0xffffffff, 0x00601005,
+       0x311f, 0xffffffff, 0x10104040,
+       0x3122, 0xffffffff, 0x0100000a,
+       0x30c5, 0xffffffff, 0x00000800,
+       0x30c3, 0xffffffff, 0x800000f4
+};
+
+static const u32 hainan_golden_registers[] =
+{
+       0x2684, 0x00010000, 0x00018208,
+       0x260c, 0xffffffff, 0x00000000,
+       0x260d, 0xf00fffff, 0x00000400,
+       0x260e, 0x0002021c, 0x00020200,
+       0x4595, 0xff000fff, 0x00000100,
+       0x340c, 0x000300c0, 0x00800040,
+       0x3630, 0xff000fff, 0x00000100,
+       0x360c, 0x000300c0, 0x00800040,
+       0x0ab9, 0x00073ffe, 0x000022a2,
+       0x0903, 0x000007ff, 0x00000000,
+       0x2285, 0xf000001f, 0x00000007,
+       0x22c9, 0xffffffff, 0x00ffffff,
+       0x22c4, 0x0000ff0f, 0x00000000,
+       0xa393, 0x07ffffff, 0x4e000000,
+       0xa0d4, 0x3f3f3fff, 0x00000000,
+       0x000c, 0x000000ff, 0x0040,
+       0x000d, 0x00000040, 0x00004040,
+       0x2440, 0x03e00000, 0x03600000,
+       0x2418, 0x0000007f, 0x00000020,
+       0x2542, 0x00010000, 0x00010000,
+       0x2b05, 0x000003ff, 0x000000f1,
+       0x2b04, 0xffffffff, 0x00000000,
+       0x2b03, 0xffffffff, 0x00003210,
+       0x2235, 0x0000001f, 0x00000010,
+       0x0570, 0x000c0fc0, 0x000c0400
+};
+
+static const u32 hainan_golden_registers2[] =
+{
+       0x263e, 0xffffffff, 0x02010001
+};
+
+static const u32 tahiti_mgcg_cgcg_init[] =
+{
+       0x3100, 0xffffffff, 0xfffffffc,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2698, 0xffffffff, 0x00000100,
+       0x24a9, 0xffffffff, 0x00000100,
+       0x3059, 0xffffffff, 0x00000100,
+       0x25dd, 0xffffffff, 0x00000100,
+       0x2261, 0xffffffff, 0x06000100,
+       0x2286, 0xffffffff, 0x00000100,
+       0x24a8, 0xffffffff, 0x00000100,
+       0x30e0, 0xffffffff, 0x00000100,
+       0x22ca, 0xffffffff, 0x00000100,
+       0x2451, 0xffffffff, 0x00000100,
+       0x2362, 0xffffffff, 0x00000100,
+       0x2363, 0xffffffff, 0x00000100,
+       0x240c, 0xffffffff, 0x00000100,
+       0x240d, 0xffffffff, 0x00000100,
+       0x240e, 0xffffffff, 0x00000100,
+       0x240f, 0xffffffff, 0x00000100,
+       0x2b60, 0xffffffff, 0x00000100,
+       0x2b15, 0xffffffff, 0x00000100,
+       0x225f, 0xffffffff, 0x06000100,
+       0x261a, 0xffffffff, 0x00000100,
+       0x2544, 0xffffffff, 0x00000100,
+       0x2bc1, 0xffffffff, 0x00000100,
+       0x2b81, 0xffffffff, 0x00000100,
+       0x2527, 0xffffffff, 0x00000100,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2458, 0xffffffff, 0x00010000,
+       0x2459, 0xffffffff, 0x00030002,
+       0x245a, 0xffffffff, 0x00040007,
+       0x245b, 0xffffffff, 0x00060005,
+       0x245c, 0xffffffff, 0x00090008,
+       0x245d, 0xffffffff, 0x00020001,
+       0x245e, 0xffffffff, 0x00040003,
+       0x245f, 0xffffffff, 0x00000007,
+       0x2460, 0xffffffff, 0x00060005,
+       0x2461, 0xffffffff, 0x00090008,
+       0x2462, 0xffffffff, 0x00030002,
+       0x2463, 0xffffffff, 0x00050004,
+       0x2464, 0xffffffff, 0x00000008,
+       0x2465, 0xffffffff, 0x00070006,
+       0x2466, 0xffffffff, 0x000a0009,
+       0x2467, 0xffffffff, 0x00040003,
+       0x2468, 0xffffffff, 0x00060005,
+       0x2469, 0xffffffff, 0x00000009,
+       0x246a, 0xffffffff, 0x00080007,
+       0x246b, 0xffffffff, 0x000b000a,
+       0x246c, 0xffffffff, 0x00050004,
+       0x246d, 0xffffffff, 0x00070006,
+       0x246e, 0xffffffff, 0x0008000b,
+       0x246f, 0xffffffff, 0x000a0009,
+       0x2470, 0xffffffff, 0x000d000c,
+       0x2471, 0xffffffff, 0x00060005,
+       0x2472, 0xffffffff, 0x00080007,
+       0x2473, 0xffffffff, 0x0000000b,
+       0x2474, 0xffffffff, 0x000a0009,
+       0x2475, 0xffffffff, 0x000d000c,
+       0x2476, 0xffffffff, 0x00070006,
+       0x2477, 0xffffffff, 0x00090008,
+       0x2478, 0xffffffff, 0x0000000c,
+       0x2479, 0xffffffff, 0x000b000a,
+       0x247a, 0xffffffff, 0x000e000d,
+       0x247b, 0xffffffff, 0x00080007,
+       0x247c, 0xffffffff, 0x000a0009,
+       0x247d, 0xffffffff, 0x0000000d,
+       0x247e, 0xffffffff, 0x000c000b,
+       0x247f, 0xffffffff, 0x000f000e,
+       0x2480, 0xffffffff, 0x00090008,
+       0x2481, 0xffffffff, 0x000b000a,
+       0x2482, 0xffffffff, 0x000c000f,
+       0x2483, 0xffffffff, 0x000e000d,
+       0x2484, 0xffffffff, 0x00110010,
+       0x2485, 0xffffffff, 0x000a0009,
+       0x2486, 0xffffffff, 0x000c000b,
+       0x2487, 0xffffffff, 0x0000000f,
+       0x2488, 0xffffffff, 0x000e000d,
+       0x2489, 0xffffffff, 0x00110010,
+       0x248a, 0xffffffff, 0x000b000a,
+       0x248b, 0xffffffff, 0x000d000c,
+       0x248c, 0xffffffff, 0x00000010,
+       0x248d, 0xffffffff, 0x000f000e,
+       0x248e, 0xffffffff, 0x00120011,
+       0x248f, 0xffffffff, 0x000c000b,
+       0x2490, 0xffffffff, 0x000e000d,
+       0x2491, 0xffffffff, 0x00000011,
+       0x2492, 0xffffffff, 0x0010000f,
+       0x2493, 0xffffffff, 0x00130012,
+       0x2494, 0xffffffff, 0x000d000c,
+       0x2495, 0xffffffff, 0x000f000e,
+       0x2496, 0xffffffff, 0x00100013,
+       0x2497, 0xffffffff, 0x00120011,
+       0x2498, 0xffffffff, 0x00150014,
+       0x2499, 0xffffffff, 0x000e000d,
+       0x249a, 0xffffffff, 0x0010000f,
+       0x249b, 0xffffffff, 0x00000013,
+       0x249c, 0xffffffff, 0x00120011,
+       0x249d, 0xffffffff, 0x00150014,
+       0x249e, 0xffffffff, 0x000f000e,
+       0x249f, 0xffffffff, 0x00110010,
+       0x24a0, 0xffffffff, 0x00000014,
+       0x24a1, 0xffffffff, 0x00130012,
+       0x24a2, 0xffffffff, 0x00160015,
+       0x24a3, 0xffffffff, 0x0010000f,
+       0x24a4, 0xffffffff, 0x00120011,
+       0x24a5, 0xffffffff, 0x00000015,
+       0x24a6, 0xffffffff, 0x00140013,
+       0x24a7, 0xffffffff, 0x00170016,
+       0x2454, 0xffffffff, 0x96940200,
+       0x21c2, 0xffffffff, 0x00900100,
+       0x311e, 0xffffffff, 0x00000080,
+       0x3101, 0xffffffff, 0x0020003f,
+       0xc, 0xffffffff, 0x0000001c,
+       0xd, 0x000f0000, 0x000f0000,
+       0x583, 0xffffffff, 0x00000100,
+       0x409, 0xffffffff, 0x00000100,
+       0x40b, 0x00000101, 0x00000000,
+       0x82a, 0xffffffff, 0x00000104,
+       0x993, 0x000c0000, 0x000c0000,
+       0x992, 0x000c0000, 0x000c0000,
+       0x1579, 0xff000fff, 0x00000100,
+       0x157a, 0x00000001, 0x00000001,
+       0xbd4, 0x00000001, 0x00000001,
+       0xc33, 0xc0000fff, 0x00000104,
+       0x3079, 0x00000001, 0x00000001,
+       0x3430, 0xfffffff0, 0x00000100,
+       0x3630, 0xfffffff0, 0x00000100
+};
+static const u32 pitcairn_mgcg_cgcg_init[] =
+{
+       0x3100, 0xffffffff, 0xfffffffc,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2698, 0xffffffff, 0x00000100,
+       0x24a9, 0xffffffff, 0x00000100,
+       0x3059, 0xffffffff, 0x00000100,
+       0x25dd, 0xffffffff, 0x00000100,
+       0x2261, 0xffffffff, 0x06000100,
+       0x2286, 0xffffffff, 0x00000100,
+       0x24a8, 0xffffffff, 0x00000100,
+       0x30e0, 0xffffffff, 0x00000100,
+       0x22ca, 0xffffffff, 0x00000100,
+       0x2451, 0xffffffff, 0x00000100,
+       0x2362, 0xffffffff, 0x00000100,
+       0x2363, 0xffffffff, 0x00000100,
+       0x240c, 0xffffffff, 0x00000100,
+       0x240d, 0xffffffff, 0x00000100,
+       0x240e, 0xffffffff, 0x00000100,
+       0x240f, 0xffffffff, 0x00000100,
+       0x2b60, 0xffffffff, 0x00000100,
+       0x2b15, 0xffffffff, 0x00000100,
+       0x225f, 0xffffffff, 0x06000100,
+       0x261a, 0xffffffff, 0x00000100,
+       0x2544, 0xffffffff, 0x00000100,
+       0x2bc1, 0xffffffff, 0x00000100,
+       0x2b81, 0xffffffff, 0x00000100,
+       0x2527, 0xffffffff, 0x00000100,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2458, 0xffffffff, 0x00010000,
+       0x2459, 0xffffffff, 0x00030002,
+       0x245a, 0xffffffff, 0x00040007,
+       0x245b, 0xffffffff, 0x00060005,
+       0x245c, 0xffffffff, 0x00090008,
+       0x245d, 0xffffffff, 0x00020001,
+       0x245e, 0xffffffff, 0x00040003,
+       0x245f, 0xffffffff, 0x00000007,
+       0x2460, 0xffffffff, 0x00060005,
+       0x2461, 0xffffffff, 0x00090008,
+       0x2462, 0xffffffff, 0x00030002,
+       0x2463, 0xffffffff, 0x00050004,
+       0x2464, 0xffffffff, 0x00000008,
+       0x2465, 0xffffffff, 0x00070006,
+       0x2466, 0xffffffff, 0x000a0009,
+       0x2467, 0xffffffff, 0x00040003,
+       0x2468, 0xffffffff, 0x00060005,
+       0x2469, 0xffffffff, 0x00000009,
+       0x246a, 0xffffffff, 0x00080007,
+       0x246b, 0xffffffff, 0x000b000a,
+       0x246c, 0xffffffff, 0x00050004,
+       0x246d, 0xffffffff, 0x00070006,
+       0x246e, 0xffffffff, 0x0008000b,
+       0x246f, 0xffffffff, 0x000a0009,
+       0x2470, 0xffffffff, 0x000d000c,
+       0x2480, 0xffffffff, 0x00090008,
+       0x2481, 0xffffffff, 0x000b000a,
+       0x2482, 0xffffffff, 0x000c000f,
+       0x2483, 0xffffffff, 0x000e000d,
+       0x2484, 0xffffffff, 0x00110010,
+       0x2485, 0xffffffff, 0x000a0009,
+       0x2486, 0xffffffff, 0x000c000b,
+       0x2487, 0xffffffff, 0x0000000f,
+       0x2488, 0xffffffff, 0x000e000d,
+       0x2489, 0xffffffff, 0x00110010,
+       0x248a, 0xffffffff, 0x000b000a,
+       0x248b, 0xffffffff, 0x000d000c,
+       0x248c, 0xffffffff, 0x00000010,
+       0x248d, 0xffffffff, 0x000f000e,
+       0x248e, 0xffffffff, 0x00120011,
+       0x248f, 0xffffffff, 0x000c000b,
+       0x2490, 0xffffffff, 0x000e000d,
+       0x2491, 0xffffffff, 0x00000011,
+       0x2492, 0xffffffff, 0x0010000f,
+       0x2493, 0xffffffff, 0x00130012,
+       0x2494, 0xffffffff, 0x000d000c,
+       0x2495, 0xffffffff, 0x000f000e,
+       0x2496, 0xffffffff, 0x00100013,
+       0x2497, 0xffffffff, 0x00120011,
+       0x2498, 0xffffffff, 0x00150014,
+       0x2454, 0xffffffff, 0x96940200,
+       0x21c2, 0xffffffff, 0x00900100,
+       0x311e, 0xffffffff, 0x00000080,
+       0x3101, 0xffffffff, 0x0020003f,
+       0xc, 0xffffffff, 0x0000001c,
+       0xd, 0x000f0000, 0x000f0000,
+       0x583, 0xffffffff, 0x00000100,
+       0x409, 0xffffffff, 0x00000100,
+       0x40b, 0x00000101, 0x00000000,
+       0x82a, 0xffffffff, 0x00000104,
+       0x1579, 0xff000fff, 0x00000100,
+       0x157a, 0x00000001, 0x00000001,
+       0xbd4, 0x00000001, 0x00000001,
+       0xc33, 0xc0000fff, 0x00000104,
+       0x3079, 0x00000001, 0x00000001,
+       0x3430, 0xfffffff0, 0x00000100,
+       0x3630, 0xfffffff0, 0x00000100
+};
+static const u32 verde_mgcg_cgcg_init[] =
+{
+       0x3100, 0xffffffff, 0xfffffffc,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2698, 0xffffffff, 0x00000100,
+       0x24a9, 0xffffffff, 0x00000100,
+       0x3059, 0xffffffff, 0x00000100,
+       0x25dd, 0xffffffff, 0x00000100,
+       0x2261, 0xffffffff, 0x06000100,
+       0x2286, 0xffffffff, 0x00000100,
+       0x24a8, 0xffffffff, 0x00000100,
+       0x30e0, 0xffffffff, 0x00000100,
+       0x22ca, 0xffffffff, 0x00000100,
+       0x2451, 0xffffffff, 0x00000100,
+       0x2362, 0xffffffff, 0x00000100,
+       0x2363, 0xffffffff, 0x00000100,
+       0x240c, 0xffffffff, 0x00000100,
+       0x240d, 0xffffffff, 0x00000100,
+       0x240e, 0xffffffff, 0x00000100,
+       0x240f, 0xffffffff, 0x00000100,
+       0x2b60, 0xffffffff, 0x00000100,
+       0x2b15, 0xffffffff, 0x00000100,
+       0x225f, 0xffffffff, 0x06000100,
+       0x261a, 0xffffffff, 0x00000100,
+       0x2544, 0xffffffff, 0x00000100,
+       0x2bc1, 0xffffffff, 0x00000100,
+       0x2b81, 0xffffffff, 0x00000100,
+       0x2527, 0xffffffff, 0x00000100,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2458, 0xffffffff, 0x00010000,
+       0x2459, 0xffffffff, 0x00030002,
+       0x245a, 0xffffffff, 0x00040007,
+       0x245b, 0xffffffff, 0x00060005,
+       0x245c, 0xffffffff, 0x00090008,
+       0x245d, 0xffffffff, 0x00020001,
+       0x245e, 0xffffffff, 0x00040003,
+       0x245f, 0xffffffff, 0x00000007,
+       0x2460, 0xffffffff, 0x00060005,
+       0x2461, 0xffffffff, 0x00090008,
+       0x2462, 0xffffffff, 0x00030002,
+       0x2463, 0xffffffff, 0x00050004,
+       0x2464, 0xffffffff, 0x00000008,
+       0x2465, 0xffffffff, 0x00070006,
+       0x2466, 0xffffffff, 0x000a0009,
+       0x2467, 0xffffffff, 0x00040003,
+       0x2468, 0xffffffff, 0x00060005,
+       0x2469, 0xffffffff, 0x00000009,
+       0x246a, 0xffffffff, 0x00080007,
+       0x246b, 0xffffffff, 0x000b000a,
+       0x246c, 0xffffffff, 0x00050004,
+       0x246d, 0xffffffff, 0x00070006,
+       0x246e, 0xffffffff, 0x0008000b,
+       0x246f, 0xffffffff, 0x000a0009,
+       0x2470, 0xffffffff, 0x000d000c,
+       0x2480, 0xffffffff, 0x00090008,
+       0x2481, 0xffffffff, 0x000b000a,
+       0x2482, 0xffffffff, 0x000c000f,
+       0x2483, 0xffffffff, 0x000e000d,
+       0x2484, 0xffffffff, 0x00110010,
+       0x2485, 0xffffffff, 0x000a0009,
+       0x2486, 0xffffffff, 0x000c000b,
+       0x2487, 0xffffffff, 0x0000000f,
+       0x2488, 0xffffffff, 0x000e000d,
+       0x2489, 0xffffffff, 0x00110010,
+       0x248a, 0xffffffff, 0x000b000a,
+       0x248b, 0xffffffff, 0x000d000c,
+       0x248c, 0xffffffff, 0x00000010,
+       0x248d, 0xffffffff, 0x000f000e,
+       0x248e, 0xffffffff, 0x00120011,
+       0x248f, 0xffffffff, 0x000c000b,
+       0x2490, 0xffffffff, 0x000e000d,
+       0x2491, 0xffffffff, 0x00000011,
+       0x2492, 0xffffffff, 0x0010000f,
+       0x2493, 0xffffffff, 0x00130012,
+       0x2494, 0xffffffff, 0x000d000c,
+       0x2495, 0xffffffff, 0x000f000e,
+       0x2496, 0xffffffff, 0x00100013,
+       0x2497, 0xffffffff, 0x00120011,
+       0x2498, 0xffffffff, 0x00150014,
+       0x2454, 0xffffffff, 0x96940200,
+       0x21c2, 0xffffffff, 0x00900100,
+       0x311e, 0xffffffff, 0x00000080,
+       0x3101, 0xffffffff, 0x0020003f,
+       0xc, 0xffffffff, 0x0000001c,
+       0xd, 0x000f0000, 0x000f0000,
+       0x583, 0xffffffff, 0x00000100,
+       0x409, 0xffffffff, 0x00000100,
+       0x40b, 0x00000101, 0x00000000,
+       0x82a, 0xffffffff, 0x00000104,
+       0x993, 0x000c0000, 0x000c0000,
+       0x992, 0x000c0000, 0x000c0000,
+       0x1579, 0xff000fff, 0x00000100,
+       0x157a, 0x00000001, 0x00000001,
+       0xbd4, 0x00000001, 0x00000001,
+       0xc33, 0xc0000fff, 0x00000104,
+       0x3079, 0x00000001, 0x00000001,
+       0x3430, 0xfffffff0, 0x00000100,
+       0x3630, 0xfffffff0, 0x00000100
+};
+static const u32 oland_mgcg_cgcg_init[] =
+{
+       0x3100, 0xffffffff, 0xfffffffc,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2698, 0xffffffff, 0x00000100,
+       0x24a9, 0xffffffff, 0x00000100,
+       0x3059, 0xffffffff, 0x00000100,
+       0x25dd, 0xffffffff, 0x00000100,
+       0x2261, 0xffffffff, 0x06000100,
+       0x2286, 0xffffffff, 0x00000100,
+       0x24a8, 0xffffffff, 0x00000100,
+       0x30e0, 0xffffffff, 0x00000100,
+       0x22ca, 0xffffffff, 0x00000100,
+       0x2451, 0xffffffff, 0x00000100,
+       0x2362, 0xffffffff, 0x00000100,
+       0x2363, 0xffffffff, 0x00000100,
+       0x240c, 0xffffffff, 0x00000100,
+       0x240d, 0xffffffff, 0x00000100,
+       0x240e, 0xffffffff, 0x00000100,
+       0x240f, 0xffffffff, 0x00000100,
+       0x2b60, 0xffffffff, 0x00000100,
+       0x2b15, 0xffffffff, 0x00000100,
+       0x225f, 0xffffffff, 0x06000100,
+       0x261a, 0xffffffff, 0x00000100,
+       0x2544, 0xffffffff, 0x00000100,
+       0x2bc1, 0xffffffff, 0x00000100,
+       0x2b81, 0xffffffff, 0x00000100,
+       0x2527, 0xffffffff, 0x00000100,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2458, 0xffffffff, 0x00010000,
+       0x2459, 0xffffffff, 0x00030002,
+       0x245a, 0xffffffff, 0x00040007,
+       0x245b, 0xffffffff, 0x00060005,
+       0x245c, 0xffffffff, 0x00090008,
+       0x245d, 0xffffffff, 0x00020001,
+       0x245e, 0xffffffff, 0x00040003,
+       0x245f, 0xffffffff, 0x00000007,
+       0x2460, 0xffffffff, 0x00060005,
+       0x2461, 0xffffffff, 0x00090008,
+       0x2462, 0xffffffff, 0x00030002,
+       0x2463, 0xffffffff, 0x00050004,
+       0x2464, 0xffffffff, 0x00000008,
+       0x2465, 0xffffffff, 0x00070006,
+       0x2466, 0xffffffff, 0x000a0009,
+       0x2467, 0xffffffff, 0x00040003,
+       0x2468, 0xffffffff, 0x00060005,
+       0x2469, 0xffffffff, 0x00000009,
+       0x246a, 0xffffffff, 0x00080007,
+       0x246b, 0xffffffff, 0x000b000a,
+       0x246c, 0xffffffff, 0x00050004,
+       0x246d, 0xffffffff, 0x00070006,
+       0x246e, 0xffffffff, 0x0008000b,
+       0x246f, 0xffffffff, 0x000a0009,
+       0x2470, 0xffffffff, 0x000d000c,
+       0x2471, 0xffffffff, 0x00060005,
+       0x2472, 0xffffffff, 0x00080007,
+       0x2473, 0xffffffff, 0x0000000b,
+       0x2474, 0xffffffff, 0x000a0009,
+       0x2475, 0xffffffff, 0x000d000c,
+       0x2454, 0xffffffff, 0x96940200,
+       0x21c2, 0xffffffff, 0x00900100,
+       0x311e, 0xffffffff, 0x00000080,
+       0x3101, 0xffffffff, 0x0020003f,
+       0xc, 0xffffffff, 0x0000001c,
+       0xd, 0x000f0000, 0x000f0000,
+       0x583, 0xffffffff, 0x00000100,
+       0x409, 0xffffffff, 0x00000100,
+       0x40b, 0x00000101, 0x00000000,
+       0x82a, 0xffffffff, 0x00000104,
+       0x993, 0x000c0000, 0x000c0000,
+       0x992, 0x000c0000, 0x000c0000,
+       0x1579, 0xff000fff, 0x00000100,
+       0x157a, 0x00000001, 0x00000001,
+       0xbd4, 0x00000001, 0x00000001,
+       0xc33, 0xc0000fff, 0x00000104,
+       0x3079, 0x00000001, 0x00000001,
+       0x3430, 0xfffffff0, 0x00000100,
+       0x3630, 0xfffffff0, 0x00000100
+};
+static const u32 hainan_mgcg_cgcg_init[] =
+{
+       0x3100, 0xffffffff, 0xfffffffc,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2698, 0xffffffff, 0x00000100,
+       0x24a9, 0xffffffff, 0x00000100,
+       0x3059, 0xffffffff, 0x00000100,
+       0x25dd, 0xffffffff, 0x00000100,
+       0x2261, 0xffffffff, 0x06000100,
+       0x2286, 0xffffffff, 0x00000100,
+       0x24a8, 0xffffffff, 0x00000100,
+       0x30e0, 0xffffffff, 0x00000100,
+       0x22ca, 0xffffffff, 0x00000100,
+       0x2451, 0xffffffff, 0x00000100,
+       0x2362, 0xffffffff, 0x00000100,
+       0x2363, 0xffffffff, 0x00000100,
+       0x240c, 0xffffffff, 0x00000100,
+       0x240d, 0xffffffff, 0x00000100,
+       0x240e, 0xffffffff, 0x00000100,
+       0x240f, 0xffffffff, 0x00000100,
+       0x2b60, 0xffffffff, 0x00000100,
+       0x2b15, 0xffffffff, 0x00000100,
+       0x225f, 0xffffffff, 0x06000100,
+       0x261a, 0xffffffff, 0x00000100,
+       0x2544, 0xffffffff, 0x00000100,
+       0x2bc1, 0xffffffff, 0x00000100,
+       0x2b81, 0xffffffff, 0x00000100,
+       0x2527, 0xffffffff, 0x00000100,
+       0x200b, 0xffffffff, 0xe0000000,
+       0x2458, 0xffffffff, 0x00010000,
+       0x2459, 0xffffffff, 0x00030002,
+       0x245a, 0xffffffff, 0x00040007,
+       0x245b, 0xffffffff, 0x00060005,
+       0x245c, 0xffffffff, 0x00090008,
+       0x245d, 0xffffffff, 0x00020001,
+       0x245e, 0xffffffff, 0x00040003,
+       0x245f, 0xffffffff, 0x00000007,
+       0x2460, 0xffffffff, 0x00060005,
+       0x2461, 0xffffffff, 0x00090008,
+       0x2462, 0xffffffff, 0x00030002,
+       0x2463, 0xffffffff, 0x00050004,
+       0x2464, 0xffffffff, 0x00000008,
+       0x2465, 0xffffffff, 0x00070006,
+       0x2466, 0xffffffff, 0x000a0009,
+       0x2467, 0xffffffff, 0x00040003,
+       0x2468, 0xffffffff, 0x00060005,
+       0x2469, 0xffffffff, 0x00000009,
+       0x246a, 0xffffffff, 0x00080007,
+       0x246b, 0xffffffff, 0x000b000a,
+       0x246c, 0xffffffff, 0x00050004,
+       0x246d, 0xffffffff, 0x00070006,
+       0x246e, 0xffffffff, 0x0008000b,
+       0x246f, 0xffffffff, 0x000a0009,
+       0x2470, 0xffffffff, 0x000d000c,
+       0x2471, 0xffffffff, 0x00060005,
+       0x2472, 0xffffffff, 0x00080007,
+       0x2473, 0xffffffff, 0x0000000b,
+       0x2474, 0xffffffff, 0x000a0009,
+       0x2475, 0xffffffff, 0x000d000c,
+       0x2454, 0xffffffff, 0x96940200,
+       0x21c2, 0xffffffff, 0x00900100,
+       0x311e, 0xffffffff, 0x00000080,
+       0x3101, 0xffffffff, 0x0020003f,
+       0xc, 0xffffffff, 0x0000001c,
+       0xd, 0x000f0000, 0x000f0000,
+       0x583, 0xffffffff, 0x00000100,
+       0x409, 0xffffffff, 0x00000100,
+       0x82a, 0xffffffff, 0x00000104,
+       0x993, 0x000c0000, 0x000c0000,
+       0x992, 0x000c0000, 0x000c0000,
+       0xbd4, 0x00000001, 0x00000001,
+       0xc33, 0xc0000fff, 0x00000104,
+       0x3079, 0x00000001, 0x00000001,
+       0x3430, 0xfffffff0, 0x00000100,
+       0x3630, 0xfffffff0, 0x00000100
+};
+
+static u32 si_pcie_rreg(struct amdgpu_device *adev, u32 reg)
+{
+       unsigned long flags;
+       u32 r;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(AMDGPU_PCIE_INDEX, reg);
+       (void)RREG32(AMDGPU_PCIE_INDEX);
+       r = RREG32(AMDGPU_PCIE_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+       return r;
+}
+
+static void si_pcie_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(AMDGPU_PCIE_INDEX, reg);
+       (void)RREG32(AMDGPU_PCIE_INDEX);
+       WREG32(AMDGPU_PCIE_DATA, v);
+       (void)RREG32(AMDGPU_PCIE_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+}
+
+u32 si_pciep_rreg(struct amdgpu_device *adev, u32 reg)
+{
+       unsigned long flags;
+       u32 r;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(PCIE_PORT_INDEX, ((reg) & 0xff));
+       (void)RREG32(PCIE_PORT_INDEX);
+       r = RREG32(PCIE_PORT_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+       return r;
+}
+
+void si_pciep_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(PCIE_PORT_INDEX, ((reg) & 0xff));
+       (void)RREG32(PCIE_PORT_INDEX);
+       WREG32(PCIE_PORT_DATA, (v));
+       (void)RREG32(PCIE_PORT_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+}
+
+static u32 si_smc_rreg(struct amdgpu_device *adev, u32 reg)
+{
+       unsigned long flags;
+       u32 r;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       WREG32(SMC_IND_INDEX_0, (reg));
+       r = RREG32(SMC_IND_DATA_0);
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+       return r;
+}
+
+static void si_smc_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       WREG32(SMC_IND_INDEX_0, (reg));
+       WREG32(SMC_IND_DATA_0, (v));
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+}
+
+static struct amdgpu_allowed_register_entry si_allowed_read_registers[] = {
+       {GRBM_STATUS, false},
+       {GB_ADDR_CONFIG, false},
+       {MC_ARB_RAMCFG, false},
+       {GB_TILE_MODE0, false},
+       {GB_TILE_MODE1, false},
+       {GB_TILE_MODE2, false},
+       {GB_TILE_MODE3, false},
+       {GB_TILE_MODE4, false},
+       {GB_TILE_MODE5, false},
+       {GB_TILE_MODE6, false},
+       {GB_TILE_MODE7, false},
+       {GB_TILE_MODE8, false},
+       {GB_TILE_MODE9, false},
+       {GB_TILE_MODE10, false},
+       {GB_TILE_MODE11, false},
+       {GB_TILE_MODE12, false},
+       {GB_TILE_MODE13, false},
+       {GB_TILE_MODE14, false},
+       {GB_TILE_MODE15, false},
+       {GB_TILE_MODE16, false},
+       {GB_TILE_MODE17, false},
+       {GB_TILE_MODE18, false},
+       {GB_TILE_MODE19, false},
+       {GB_TILE_MODE20, false},
+       {GB_TILE_MODE21, false},
+       {GB_TILE_MODE22, false},
+       {GB_TILE_MODE23, false},
+       {GB_TILE_MODE24, false},
+       {GB_TILE_MODE25, false},
+       {GB_TILE_MODE26, false},
+       {GB_TILE_MODE27, false},
+       {GB_TILE_MODE28, false},
+       {GB_TILE_MODE29, false},
+       {GB_TILE_MODE30, false},
+       {GB_TILE_MODE31, false},
+       {CC_RB_BACKEND_DISABLE, false, true},
+       {GC_USER_RB_BACKEND_DISABLE, false, true},
+       {PA_SC_RASTER_CONFIG, false, true},
+};
+
+static uint32_t si_read_indexed_register(struct amdgpu_device *adev,
+                                         u32 se_num, u32 sh_num,
+                                         u32 reg_offset)
+{
+       uint32_t val;
+
+       mutex_lock(&adev->grbm_idx_mutex);
+       if (se_num != 0xffffffff || sh_num != 0xffffffff)
+               amdgpu_gfx_select_se_sh(adev, se_num, sh_num, 0xffffffff);
+
+       val = RREG32(reg_offset);
+
+       if (se_num != 0xffffffff || sh_num != 0xffffffff)
+               amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff);
+       mutex_unlock(&adev->grbm_idx_mutex);
+       return val;
+}
+
+static int si_read_register(struct amdgpu_device *adev, u32 se_num,
+                            u32 sh_num, u32 reg_offset, u32 *value)
+{
+       uint32_t i;
+
+       *value = 0;
+       for (i = 0; i < ARRAY_SIZE(si_allowed_read_registers); i++) {
+               if (reg_offset != si_allowed_read_registers[i].reg_offset)
+                       continue;
+
+               if (!si_allowed_read_registers[i].untouched)
+                       *value = si_allowed_read_registers[i].grbm_indexed ?
+                                si_read_indexed_register(adev, se_num,
+                                                          sh_num, reg_offset) :
+                                RREG32(reg_offset);
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static bool si_read_disabled_bios(struct amdgpu_device *adev)
+{
+       u32 bus_cntl;
+       u32 d1vga_control = 0;
+       u32 d2vga_control = 0;
+       u32 vga_render_control = 0;
+       u32 rom_cntl;
+       bool r;
+
+       bus_cntl = RREG32(R600_BUS_CNTL);
+       if (adev->mode_info.num_crtc) {
+               d1vga_control = RREG32(AVIVO_D1VGA_CONTROL);
+               d2vga_control = RREG32(AVIVO_D2VGA_CONTROL);
+               vga_render_control = RREG32(VGA_RENDER_CONTROL);
+       }
+       rom_cntl = RREG32(R600_ROM_CNTL);
+
+       /* enable the rom */
+       WREG32(R600_BUS_CNTL, (bus_cntl & ~R600_BIOS_ROM_DIS));
+       if (adev->mode_info.num_crtc) {
+               /* Disable VGA mode */
+               WREG32(AVIVO_D1VGA_CONTROL,
+                      (d1vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE |
+                                         AVIVO_DVGA_CONTROL_TIMING_SELECT)));
+               WREG32(AVIVO_D2VGA_CONTROL,
+                      (d2vga_control & ~(AVIVO_DVGA_CONTROL_MODE_ENABLE |
+                                         AVIVO_DVGA_CONTROL_TIMING_SELECT)));
+               WREG32(VGA_RENDER_CONTROL,
+                      (vga_render_control & C_000300_VGA_VSTATUS_CNTL));
+       }
+       WREG32(R600_ROM_CNTL, rom_cntl | R600_SCK_OVERWRITE);
+
+       r = amdgpu_read_bios(adev);
+
+       /* restore regs */
+       WREG32(R600_BUS_CNTL, bus_cntl);
+       if (adev->mode_info.num_crtc) {
+               WREG32(AVIVO_D1VGA_CONTROL, d1vga_control);
+               WREG32(AVIVO_D2VGA_CONTROL, d2vga_control);
+               WREG32(VGA_RENDER_CONTROL, vga_render_control);
+       }
+       WREG32(R600_ROM_CNTL, rom_cntl);
+       return r;
+}
+
+//xxx: not implemented
+static int si_asic_reset(struct amdgpu_device *adev)
+{
+       return 0;
+}
+
+static void si_vga_set_state(struct amdgpu_device *adev, bool state)
+{
+       uint32_t temp;
+
+       temp = RREG32(CONFIG_CNTL);
+       if (state == false) {
+               temp &= ~(1<<0);
+               temp |= (1<<1);
+       } else {
+               temp &= ~(1<<1);
+       }
+       WREG32(CONFIG_CNTL, temp);
+}
+
+static u32 si_get_xclk(struct amdgpu_device *adev)
+{
+        u32 reference_clock = adev->clock.spll.reference_freq;
+       u32 tmp;
+
+       tmp = RREG32(CG_CLKPIN_CNTL_2);
+       if (tmp & MUX_TCLK_TO_XCLK)
+               return TCLK;
+
+       tmp = RREG32(CG_CLKPIN_CNTL);
+       if (tmp & XTALIN_DIVIDE)
+               return reference_clock / 4;
+
+       return reference_clock;
+}
+
+//xxx:not implemented
+static int si_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk)
+{
+       return 0;
+}
+
+static void si_detect_hw_virtualization(struct amdgpu_device *adev)
+{
+       if (is_virtual_machine()) /* passthrough mode */
+               adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE;
+}
+
+static const struct amdgpu_asic_funcs si_asic_funcs =
+{
+       .read_disabled_bios = &si_read_disabled_bios,
+       .detect_hw_virtualization = si_detect_hw_virtualization,
+       .read_register = &si_read_register,
+       .reset = &si_asic_reset,
+       .set_vga_state = &si_vga_set_state,
+       .get_xclk = &si_get_xclk,
+       .set_uvd_clocks = &si_set_uvd_clocks,
+       .set_vce_clocks = NULL,
+};
+
+static uint32_t si_get_rev_id(struct amdgpu_device *adev)
+{
+       return (RREG32(CC_DRM_ID_STRAPS) & CC_DRM_ID_STRAPS__ATI_REV_ID_MASK)
+               >> CC_DRM_ID_STRAPS__ATI_REV_ID__SHIFT;
+}
+
+static int si_common_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       adev->smc_rreg = &si_smc_rreg;
+       adev->smc_wreg = &si_smc_wreg;
+       adev->pcie_rreg = &si_pcie_rreg;
+       adev->pcie_wreg = &si_pcie_wreg;
+       adev->pciep_rreg = &si_pciep_rreg;
+       adev->pciep_wreg = &si_pciep_wreg;
+       adev->uvd_ctx_rreg = NULL;
+       adev->uvd_ctx_wreg = NULL;
+       adev->didt_rreg = NULL;
+       adev->didt_wreg = NULL;
+
+       adev->asic_funcs = &si_asic_funcs;
+
+       adev->rev_id = si_get_rev_id(adev);
+       adev->external_rev_id = 0xFF;
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               adev->cg_flags =
+                       AMD_CG_SUPPORT_GFX_MGCG |
+                       AMD_CG_SUPPORT_GFX_MGLS |
+                       /*AMD_CG_SUPPORT_GFX_CGCG |*/
+                       AMD_CG_SUPPORT_GFX_CGLS |
+                       AMD_CG_SUPPORT_GFX_CGTS |
+                       AMD_CG_SUPPORT_GFX_CP_LS |
+                       AMD_CG_SUPPORT_MC_MGCG |
+                       AMD_CG_SUPPORT_SDMA_MGCG |
+                       AMD_CG_SUPPORT_BIF_LS |
+                       AMD_CG_SUPPORT_VCE_MGCG |
+                       AMD_CG_SUPPORT_UVD_MGCG |
+                       AMD_CG_SUPPORT_HDP_LS |
+                       AMD_CG_SUPPORT_HDP_MGCG;
+                       adev->pg_flags = 0;
+               break;
+       case CHIP_PITCAIRN:
+               adev->cg_flags =
+                       AMD_CG_SUPPORT_GFX_MGCG |
+                       AMD_CG_SUPPORT_GFX_MGLS |
+                       /*AMD_CG_SUPPORT_GFX_CGCG |*/
+                       AMD_CG_SUPPORT_GFX_CGLS |
+                       AMD_CG_SUPPORT_GFX_CGTS |
+                       AMD_CG_SUPPORT_GFX_CP_LS |
+                       AMD_CG_SUPPORT_GFX_RLC_LS |
+                       AMD_CG_SUPPORT_MC_LS |
+                       AMD_CG_SUPPORT_MC_MGCG |
+                       AMD_CG_SUPPORT_SDMA_MGCG |
+                       AMD_CG_SUPPORT_BIF_LS |
+                       AMD_CG_SUPPORT_VCE_MGCG |
+                       AMD_CG_SUPPORT_UVD_MGCG |
+                       AMD_CG_SUPPORT_HDP_LS |
+                       AMD_CG_SUPPORT_HDP_MGCG;
+               adev->pg_flags = 0;
+               break;
+
+       case CHIP_VERDE:
+               adev->cg_flags =
+                       AMD_CG_SUPPORT_GFX_MGCG |
+                       AMD_CG_SUPPORT_GFX_MGLS |
+                       AMD_CG_SUPPORT_GFX_CGLS |
+                       AMD_CG_SUPPORT_GFX_CGTS |
+                       AMD_CG_SUPPORT_GFX_CGTS_LS |
+                       AMD_CG_SUPPORT_GFX_CP_LS |
+                       AMD_CG_SUPPORT_MC_LS |
+                       AMD_CG_SUPPORT_MC_MGCG |
+                       AMD_CG_SUPPORT_SDMA_MGCG |
+                       AMD_CG_SUPPORT_SDMA_LS |
+                       AMD_CG_SUPPORT_BIF_LS |
+                       AMD_CG_SUPPORT_VCE_MGCG |
+                       AMD_CG_SUPPORT_UVD_MGCG |
+                       AMD_CG_SUPPORT_HDP_LS |
+                       AMD_CG_SUPPORT_HDP_MGCG;
+               adev->pg_flags = 0;
+               //???
+               adev->external_rev_id = adev->rev_id + 0x14;
+               break;
+       case CHIP_OLAND:
+               adev->cg_flags =
+                       AMD_CG_SUPPORT_GFX_MGCG |
+                       AMD_CG_SUPPORT_GFX_MGLS |
+                       /*AMD_CG_SUPPORT_GFX_CGCG |*/
+                       AMD_CG_SUPPORT_GFX_CGLS |
+                       AMD_CG_SUPPORT_GFX_CGTS |
+                       AMD_CG_SUPPORT_GFX_CP_LS |
+                       AMD_CG_SUPPORT_GFX_RLC_LS |
+                       AMD_CG_SUPPORT_MC_LS |
+                       AMD_CG_SUPPORT_MC_MGCG |
+                       AMD_CG_SUPPORT_SDMA_MGCG |
+                       AMD_CG_SUPPORT_BIF_LS |
+                       AMD_CG_SUPPORT_UVD_MGCG |
+                       AMD_CG_SUPPORT_HDP_LS |
+                       AMD_CG_SUPPORT_HDP_MGCG;
+               adev->pg_flags = 0;
+               break;
+       case CHIP_HAINAN:
+               adev->cg_flags =
+                       AMD_CG_SUPPORT_GFX_MGCG |
+                       AMD_CG_SUPPORT_GFX_MGLS |
+                       /*AMD_CG_SUPPORT_GFX_CGCG |*/
+                       AMD_CG_SUPPORT_GFX_CGLS |
+                       AMD_CG_SUPPORT_GFX_CGTS |
+                       AMD_CG_SUPPORT_GFX_CP_LS |
+                       AMD_CG_SUPPORT_GFX_RLC_LS |
+                       AMD_CG_SUPPORT_MC_LS |
+                       AMD_CG_SUPPORT_MC_MGCG |
+                       AMD_CG_SUPPORT_SDMA_MGCG |
+                       AMD_CG_SUPPORT_BIF_LS |
+                       AMD_CG_SUPPORT_HDP_LS |
+                       AMD_CG_SUPPORT_HDP_MGCG;
+               adev->pg_flags = 0;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int si_common_sw_init(void *handle)
+{
+       return 0;
+}
+
+static int si_common_sw_fini(void *handle)
+{
+       return 0;
+}
+
+
+static void si_init_golden_registers(struct amdgpu_device *adev)
+{
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               amdgpu_program_register_sequence(adev,
+                                                tahiti_golden_registers,
+                                                (const u32)ARRAY_SIZE(tahiti_golden_registers));
+               amdgpu_program_register_sequence(adev,
+                                                tahiti_golden_rlc_registers,
+                                                (const u32)ARRAY_SIZE(tahiti_golden_rlc_registers));
+               amdgpu_program_register_sequence(adev,
+                                                tahiti_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(tahiti_mgcg_cgcg_init));
+               amdgpu_program_register_sequence(adev,
+                                                tahiti_golden_registers2,
+                                                (const u32)ARRAY_SIZE(tahiti_golden_registers2));
+               break;
+       case CHIP_PITCAIRN:
+               amdgpu_program_register_sequence(adev,
+                                                pitcairn_golden_registers,
+                                                (const u32)ARRAY_SIZE(pitcairn_golden_registers));
+               amdgpu_program_register_sequence(adev,
+                                                pitcairn_golden_rlc_registers,
+                                                (const u32)ARRAY_SIZE(pitcairn_golden_rlc_registers));
+               amdgpu_program_register_sequence(adev,
+                                                pitcairn_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(pitcairn_mgcg_cgcg_init));
+       case CHIP_VERDE:
+               amdgpu_program_register_sequence(adev,
+                                                verde_golden_registers,
+                                                (const u32)ARRAY_SIZE(verde_golden_registers));
+               amdgpu_program_register_sequence(adev,
+                                                verde_golden_rlc_registers,
+                                                (const u32)ARRAY_SIZE(verde_golden_rlc_registers));
+               amdgpu_program_register_sequence(adev,
+                                                verde_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(verde_mgcg_cgcg_init));
+               amdgpu_program_register_sequence(adev,
+                                                verde_pg_init,
+                                                (const u32)ARRAY_SIZE(verde_pg_init));
+               break;
+       case CHIP_OLAND:
+               amdgpu_program_register_sequence(adev,
+                                                oland_golden_registers,
+                                                (const u32)ARRAY_SIZE(oland_golden_registers));
+               amdgpu_program_register_sequence(adev,
+                                                oland_golden_rlc_registers,
+                                                (const u32)ARRAY_SIZE(oland_golden_rlc_registers));
+               amdgpu_program_register_sequence(adev,
+                                                oland_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(oland_mgcg_cgcg_init));
+       case CHIP_HAINAN:
+               amdgpu_program_register_sequence(adev,
+                                                hainan_golden_registers,
+                                                (const u32)ARRAY_SIZE(hainan_golden_registers));
+               amdgpu_program_register_sequence(adev,
+                                                hainan_golden_registers2,
+                                                (const u32)ARRAY_SIZE(hainan_golden_registers2));
+               amdgpu_program_register_sequence(adev,
+                                                hainan_mgcg_cgcg_init,
+                                                (const u32)ARRAY_SIZE(hainan_mgcg_cgcg_init));
+               break;
+
+
+       default:
+               BUG();
+       }
+}
+
+static void si_pcie_gen3_enable(struct amdgpu_device *adev)
+{
+       struct pci_dev *root = adev->pdev->bus->self;
+       int bridge_pos, gpu_pos;
+       u32 speed_cntl, mask, current_data_rate;
+       int ret, i;
+       u16 tmp16;
+
+       if (pci_is_root_bus(adev->pdev->bus))
+               return;
+
+       if (amdgpu_pcie_gen2 == 0)
+               return;
+
+       if (adev->flags & AMD_IS_APU)
+               return;
+
+       ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask);
+       if (ret != 0)
+               return;
+
+       if (!(mask & (DRM_PCIE_SPEED_50 | DRM_PCIE_SPEED_80)))
+               return;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       current_data_rate = (speed_cntl & LC_CURRENT_DATA_RATE_MASK) >>
+               LC_CURRENT_DATA_RATE_SHIFT;
+       if (mask & DRM_PCIE_SPEED_80) {
+               if (current_data_rate == 2) {
+                       DRM_INFO("PCIE gen 3 link speeds already enabled\n");
+                       return;
+               }
+               DRM_INFO("enabling PCIE gen 3 link speeds, disable with amdgpu.pcie_gen2=0\n");
+       } else if (mask & DRM_PCIE_SPEED_50) {
+               if (current_data_rate == 1) {
+                       DRM_INFO("PCIE gen 2 link speeds already enabled\n");
+                       return;
+               }
+               DRM_INFO("enabling PCIE gen 2 link speeds, disable with amdgpu.pcie_gen2=0\n");
+       }
+
+       bridge_pos = pci_pcie_cap(root);
+       if (!bridge_pos)
+               return;
+
+       gpu_pos = pci_pcie_cap(adev->pdev);
+       if (!gpu_pos)
+               return;
+
+       if (mask & DRM_PCIE_SPEED_80) {
+               if (current_data_rate != 2) {
+                       u16 bridge_cfg, gpu_cfg;
+                       u16 bridge_cfg2, gpu_cfg2;
+                       u32 max_lw, current_lw, tmp;
+
+                       pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &bridge_cfg);
+                       pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL, &gpu_cfg);
+
+                       tmp16 = bridge_cfg | PCI_EXP_LNKCTL_HAWD;
+                       pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL, tmp16);
+
+                       tmp16 = gpu_cfg | PCI_EXP_LNKCTL_HAWD;
+                       pci_write_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL, tmp16);
+
+                       tmp = RREG32_PCIE(PCIE_LC_STATUS1);
+                       max_lw = (tmp & LC_DETECTED_LINK_WIDTH_MASK) >> LC_DETECTED_LINK_WIDTH_SHIFT;
+                       current_lw = (tmp & LC_OPERATING_LINK_WIDTH_MASK) >> LC_OPERATING_LINK_WIDTH_SHIFT;
+
+                       if (current_lw < max_lw) {
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+                               if (tmp & LC_RENEGOTIATION_SUPPORT) {
+                                       tmp &= ~(LC_LINK_WIDTH_MASK | LC_UPCONFIGURE_DIS);
+                                       tmp |= (max_lw << LC_LINK_WIDTH_SHIFT);
+                                       tmp |= LC_UPCONFIGURE_SUPPORT | LC_RENEGOTIATE_EN | LC_RECONFIG_NOW;
+                                       WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, tmp);
+                               }
+                       }
+
+                       for (i = 0; i < 10; i++) {
+                               pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_DEVSTA, &tmp16);
+                               if (tmp16 & PCI_EXP_DEVSTA_TRPND)
+                                       break;
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &bridge_cfg);
+                               pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL, &gpu_cfg);
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, &bridge_cfg2);
+                               pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &gpu_cfg2);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp |= LC_SET_QUIESCE;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp |= LC_REDO_EQ;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+
+                               mdelay(100);
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL, &tmp16);
+                               tmp16 &= ~PCI_EXP_LNKCTL_HAWD;
+                               tmp16 |= (bridge_cfg & PCI_EXP_LNKCTL_HAWD);
+                               pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL, tmp16);
+
+                               pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL, &tmp16);
+                               tmp16 &= ~PCI_EXP_LNKCTL_HAWD;
+                               tmp16 |= (gpu_cfg & PCI_EXP_LNKCTL_HAWD);
+                               pci_write_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL, tmp16);
+
+                               pci_read_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, &tmp16);
+                               tmp16 &= ~((1 << 4) | (7 << 9));
+                               tmp16 |= (bridge_cfg2 & ((1 << 4) | (7 << 9)));
+                               pci_write_config_word(root, bridge_pos + PCI_EXP_LNKCTL2, tmp16);
+
+                               pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &tmp16);
+                               tmp16 &= ~((1 << 4) | (7 << 9));
+                               tmp16 |= (gpu_cfg2 & ((1 << 4) | (7 << 9)));
+                               pci_write_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL2, tmp16);
+
+                               tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL4);
+                               tmp &= ~LC_SET_QUIESCE;
+                               WREG32_PCIE_PORT(PCIE_LC_CNTL4, tmp);
+                       }
+               }
+       }
+
+       speed_cntl |= LC_FORCE_EN_SW_SPEED_CHANGE | LC_FORCE_DIS_HW_SPEED_CHANGE;
+       speed_cntl &= ~LC_FORCE_DIS_SW_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl);
+
+       pci_read_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL2, &tmp16);
+       tmp16 &= ~0xf;
+       if (mask & DRM_PCIE_SPEED_80)
+               tmp16 |= 3;
+       else if (mask & DRM_PCIE_SPEED_50)
+               tmp16 |= 2;
+       else
+               tmp16 |= 1;
+       pci_write_config_word(adev->pdev, gpu_pos + PCI_EXP_LNKCTL2, tmp16);
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+       speed_cntl |= LC_INITIATE_LINK_SPEED_CHANGE;
+       WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, speed_cntl);
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
+               if ((speed_cntl & LC_INITIATE_LINK_SPEED_CHANGE) == 0)
+                       break;
+               udelay(1);
+       }
+}
+
+static inline u32 si_pif_phy0_rreg(struct amdgpu_device *adev, u32 reg)
+{
+       unsigned long flags;
+       u32 r;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(EVERGREEN_PIF_PHY0_INDEX, ((reg) & 0xffff));
+       r = RREG32(EVERGREEN_PIF_PHY0_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+       return r;
+}
+
+static inline void si_pif_phy0_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(EVERGREEN_PIF_PHY0_INDEX, ((reg) & 0xffff));
+       WREG32(EVERGREEN_PIF_PHY0_DATA, (v));
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+}
+
+static inline u32 si_pif_phy1_rreg(struct amdgpu_device *adev, u32 reg)
+{
+       unsigned long flags;
+       u32 r;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(EVERGREEN_PIF_PHY1_INDEX, ((reg) & 0xffff));
+       r = RREG32(EVERGREEN_PIF_PHY1_DATA);
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+       return r;
+}
+
+static inline void si_pif_phy1_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&adev->pcie_idx_lock, flags);
+       WREG32(EVERGREEN_PIF_PHY1_INDEX, ((reg) & 0xffff));
+       WREG32(EVERGREEN_PIF_PHY1_DATA, (v));
+       spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
+}
+static void si_program_aspm(struct amdgpu_device *adev)
+{
+       u32 data, orig;
+       bool disable_l0s = false, disable_l1 = false, disable_plloff_in_l1 = false;
+       bool disable_clkreq = false;
+
+       if (amdgpu_aspm == 0)
+               return;
+
+       if (adev->flags & AMD_IS_APU)
+               return;
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL);
+       data &= ~LC_XMIT_N_FTS_MASK;
+       data |= LC_XMIT_N_FTS(0x24) | LC_XMIT_N_FTS_OVERRIDE_EN;
+       if (orig != data)
+               WREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL, data);
+
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL3);
+       data |= LC_GO_TO_RECOVERY;
+       if (orig != data)
+               WREG32_PCIE_PORT(PCIE_LC_CNTL3, data);
+
+       orig = data = RREG32_PCIE(PCIE_P_CNTL);
+       data |= P_IGNORE_EDB_ERR;
+       if (orig != data)
+               WREG32_PCIE(PCIE_P_CNTL, data);
+
+       orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+       data &= ~(LC_L0S_INACTIVITY_MASK | LC_L1_INACTIVITY_MASK);
+       data |= LC_PMI_TO_L1_DIS;
+       if (!disable_l0s)
+               data |= LC_L0S_INACTIVITY(7);
+
+       if (!disable_l1) {
+               data |= LC_L1_INACTIVITY(7);
+               data &= ~LC_PMI_TO_L1_DIS;
+               if (orig != data)
+                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+
+               if (!disable_plloff_in_l1) {
+                       bool clk_req_support;
+
+                       orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (orig != data)
+                               si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_0, data);
+
+                       orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (orig != data)
+                               si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_1, data);
+
+                       orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_0);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7);
+                       if (orig != data)
+                               si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_0, data);
+
+                       orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_1);
+                       data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
+                       data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7);
+                       if (orig != data)
+                               si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_1, data);
+
+                       if ((adev->family != CHIP_OLAND) && (adev->family != CHIP_HAINAN)) {
+                               orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               if (orig != data)
+                                       si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_0, data);
+
+                               orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               if (orig != data)
+                                       si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_1, data);
+
+                               orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_2);
+                               data &= ~PLL_RAMP_UP_TIME_2_MASK;
+                               if (orig != data)
+                                       si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_2, data);
+
+                               orig = data = si_pif_phy0_rreg(adev,PB0_PIF_PWRDOWN_3);
+                               data &= ~PLL_RAMP_UP_TIME_3_MASK;
+                               if (orig != data)
+                                       si_pif_phy0_wreg(adev,PB0_PIF_PWRDOWN_3, data);
+
+                               orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_0);
+                               data &= ~PLL_RAMP_UP_TIME_0_MASK;
+                               if (orig != data)
+                                       si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_0, data);
+
+                               orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_1);
+                               data &= ~PLL_RAMP_UP_TIME_1_MASK;
+                               if (orig != data)
+                                       si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_1, data);
+
+                               orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_2);
+                               data &= ~PLL_RAMP_UP_TIME_2_MASK;
+                               if (orig != data)
+                                       si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_2, data);
+
+                               orig = data = si_pif_phy1_rreg(adev,PB1_PIF_PWRDOWN_3);
+                               data &= ~PLL_RAMP_UP_TIME_3_MASK;
+                               if (orig != data)
+                                       si_pif_phy1_wreg(adev,PB1_PIF_PWRDOWN_3, data);
+                       }
+                       orig = data = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
+                       data &= ~LC_DYN_LANES_PWR_STATE_MASK;
+                       data |= LC_DYN_LANES_PWR_STATE(3);
+                       if (orig != data)
+                               WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
+
+                       orig = data = si_pif_phy0_rreg(adev,PB0_PIF_CNTL);
+                       data &= ~LS2_EXIT_TIME_MASK;
+                       if ((adev->family == CHIP_OLAND) || (adev->family == CHIP_HAINAN))
+                               data |= LS2_EXIT_TIME(5);
+                       if (orig != data)
+                               si_pif_phy0_wreg(adev,PB0_PIF_CNTL, data);
+
+                       orig = data = si_pif_phy1_rreg(adev,PB1_PIF_CNTL);
+                       data &= ~LS2_EXIT_TIME_MASK;
+                       if ((adev->family == CHIP_OLAND) || (adev->family == CHIP_HAINAN))
+                               data |= LS2_EXIT_TIME(5);
+                       if (orig != data)
+                               si_pif_phy1_wreg(adev,PB1_PIF_CNTL, data);
+
+                       if (!disable_clkreq &&
+                           !pci_is_root_bus(adev->pdev->bus)) {
+                               struct pci_dev *root = adev->pdev->bus->self;
+                               u32 lnkcap;
+
+                               clk_req_support = false;
+                               pcie_capability_read_dword(root, PCI_EXP_LNKCAP, &lnkcap);
+                               if (lnkcap & PCI_EXP_LNKCAP_CLKPM)
+                                       clk_req_support = true;
+                       } else {
+                               clk_req_support = false;
+                       }
+
+                       if (clk_req_support) {
+                               orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL2);
+                               data |= LC_ALLOW_PDWN_IN_L1 | LC_ALLOW_PDWN_IN_L23;
+                               if (orig != data)
+                                       WREG32_PCIE_PORT(PCIE_LC_CNTL2, data);
+
+                               orig = data = RREG32(THM_CLK_CNTL);
+                               data &= ~(CMON_CLK_SEL_MASK | TMON_CLK_SEL_MASK);
+                               data |= CMON_CLK_SEL(1) | TMON_CLK_SEL(1);
+                               if (orig != data)
+                                       WREG32(THM_CLK_CNTL, data);
+
+                               orig = data = RREG32(MISC_CLK_CNTL);
+                               data &= ~(DEEP_SLEEP_CLK_SEL_MASK | ZCLK_SEL_MASK);
+                               data |= DEEP_SLEEP_CLK_SEL(1) | ZCLK_SEL(1);
+                               if (orig != data)
+                                       WREG32(MISC_CLK_CNTL, data);
+
+                               orig = data = RREG32(CG_CLKPIN_CNTL);
+                               data &= ~BCLK_AS_XCLK;
+                               if (orig != data)
+                                       WREG32(CG_CLKPIN_CNTL, data);
+
+                               orig = data = RREG32(CG_CLKPIN_CNTL_2);
+                               data &= ~FORCE_BIF_REFCLK_EN;
+                               if (orig != data)
+                                       WREG32(CG_CLKPIN_CNTL_2, data);
+
+                               orig = data = RREG32(MPLL_BYPASSCLK_SEL);
+                               data &= ~MPLL_CLKOUT_SEL_MASK;
+                               data |= MPLL_CLKOUT_SEL(4);
+                               if (orig != data)
+                                       WREG32(MPLL_BYPASSCLK_SEL, data);
+
+                               orig = data = RREG32(SPLL_CNTL_MODE);
+                               data &= ~SPLL_REFCLK_SEL_MASK;
+                               if (orig != data)
+                                       WREG32(SPLL_CNTL_MODE, data);
+                       }
+               }
+       } else {
+               if (orig != data)
+                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+       }
+
+       orig = data = RREG32_PCIE(PCIE_CNTL2);
+       data |= SLV_MEM_LS_EN | MST_MEM_LS_EN | REPLAY_MEM_LS_EN;
+       if (orig != data)
+               WREG32_PCIE(PCIE_CNTL2, data);
+
+       if (!disable_l0s) {
+               data = RREG32_PCIE_PORT(PCIE_LC_N_FTS_CNTL);
+               if((data & LC_N_FTS_MASK) == LC_N_FTS_MASK) {
+                       data = RREG32_PCIE(PCIE_LC_STATUS1);
+                       if ((data & LC_REVERSE_XMIT) && (data & LC_REVERSE_RCVR)) {
+                               orig = data = RREG32_PCIE_PORT(PCIE_LC_CNTL);
+                               data &= ~LC_L0S_INACTIVITY_MASK;
+                               if (orig != data)
+                                       WREG32_PCIE_PORT(PCIE_LC_CNTL, data);
+                       }
+               }
+       }
+}
+
+static void si_fix_pci_max_read_req_size(struct amdgpu_device *adev)
+{
+       int readrq;
+       u16 v;
+
+       readrq = pcie_get_readrq(adev->pdev);
+       v = ffs(readrq) - 8;
+       if ((v == 0) || (v == 6) || (v == 7))
+               pcie_set_readrq(adev->pdev, 512);
+}
+
+static int si_common_hw_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       si_fix_pci_max_read_req_size(adev);
+       si_init_golden_registers(adev);
+       si_pcie_gen3_enable(adev);
+       si_program_aspm(adev);
+
+       return 0;
+}
+
+static int si_common_hw_fini(void *handle)
+{
+       return 0;
+}
+
+static int si_common_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_common_hw_fini(adev);
+}
+
+static int si_common_resume(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_common_hw_init(adev);
+}
+
+static bool si_common_is_idle(void *handle)
+{
+       return true;
+}
+
+static int si_common_wait_for_idle(void *handle)
+{
+       return 0;
+}
+
+static int si_common_soft_reset(void *handle)
+{
+       return 0;
+}
+
+static int si_common_set_clockgating_state(void *handle,
+                                           enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int si_common_set_powergating_state(void *handle,
+                                           enum amd_powergating_state state)
+{
+       return 0;
+}
+
+const struct amd_ip_funcs si_common_ip_funcs = {
+       .name = "si_common",
+       .early_init = si_common_early_init,
+       .late_init = NULL,
+       .sw_init = si_common_sw_init,
+       .sw_fini = si_common_sw_fini,
+       .hw_init = si_common_hw_init,
+       .hw_fini = si_common_hw_fini,
+       .suspend = si_common_suspend,
+       .resume = si_common_resume,
+       .is_idle = si_common_is_idle,
+       .wait_for_idle = si_common_wait_for_idle,
+       .soft_reset = si_common_soft_reset,
+       .set_clockgating_state = si_common_set_clockgating_state,
+       .set_powergating_state = si_common_set_powergating_state,
+};
+
+static const struct amdgpu_ip_block_version verde_ip_blocks[] =
+{
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &dce_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_dma_ip_funcs,
+       },
+/*     {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 3,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &si_null_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_null_ip_funcs,
+       },
+       */
+};
+
+
+static const struct amdgpu_ip_block_version hainan_ip_blocks[] =
+{
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &si_dma_ip_funcs,
+       },
+};
+
+int si_set_ip_blocks(struct amdgpu_device *adev)
+{
+       switch (adev->asic_type) {
+       case CHIP_VERDE:
+       case CHIP_TAHITI:
+       case CHIP_PITCAIRN:
+       case CHIP_OLAND:
+               adev->ip_blocks = verde_ip_blocks;
+               adev->num_ip_blocks = ARRAY_SIZE(verde_ip_blocks);
+               break;
+       case CHIP_HAINAN:
+               adev->ip_blocks = hainan_ip_blocks;
+               adev->num_ip_blocks = ARRAY_SIZE(hainan_ip_blocks);
+               break;
+       default:
+               BUG();
+       }
+       return 0;
+}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/si.h b/drivers/gpu/drm/amd/amdgpu/si.h
new file mode 100644 (file)
index 0000000..959d7b6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2015 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 __SI_H__
+#define __SI_H__
+
+extern const struct amd_ip_funcs si_common_ip_funcs;
+
+void si_srbm_select(struct amdgpu_device *adev,
+                    u32 me, u32 pipe, u32 queue, u32 vmid);
+int si_set_ip_blocks(struct amdgpu_device *adev);
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dma.c b/drivers/gpu/drm/amd/amdgpu/si_dma.c
new file mode 100644 (file)
index 0000000..de35819
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ * Copyright 2015 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 <drm/drmP.h>
+#include "amdgpu.h"
+#include "amdgpu_trace.h"
+#include "si/sid.h"
+
+const u32 sdma_offsets[SDMA_MAX_INSTANCE] =
+{
+       DMA0_REGISTER_OFFSET,
+       DMA1_REGISTER_OFFSET
+};
+
+static void si_dma_set_ring_funcs(struct amdgpu_device *adev);
+static void si_dma_set_buffer_funcs(struct amdgpu_device *adev);
+static void si_dma_set_vm_pte_funcs(struct amdgpu_device *adev);
+static void si_dma_set_irq_funcs(struct amdgpu_device *adev);
+
+static uint32_t si_dma_ring_get_rptr(struct amdgpu_ring *ring)
+{
+       return ring->adev->wb.wb[ring->rptr_offs>>2];
+}
+
+static uint32_t si_dma_ring_get_wptr(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+       u32 me = (ring == &adev->sdma.instance[0].ring) ? 0 : 1;
+
+       return (RREG32(DMA_RB_WPTR + sdma_offsets[me]) & 0x3fffc) >> 2;
+}
+
+static void si_dma_ring_set_wptr(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+       u32 me = (ring == &adev->sdma.instance[0].ring) ? 0 : 1;
+
+       WREG32(DMA_RB_WPTR + sdma_offsets[me], (ring->wptr << 2) & 0x3fffc);
+}
+
+static void si_dma_ring_emit_ib(struct amdgpu_ring *ring,
+                               struct amdgpu_ib *ib,
+                               unsigned vm_id, bool ctx_switch)
+{
+       /* The indirect buffer packet must end on an 8 DW boundary in the DMA ring.
+        * Pad as necessary with NOPs.
+        */
+       while ((ring->wptr & 7) != 5)
+               amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, DMA_IB_PACKET(DMA_PACKET_INDIRECT_BUFFER, vm_id, 0));
+       amdgpu_ring_write(ring, (ib->gpu_addr & 0xFFFFFFE0));
+       amdgpu_ring_write(ring, (ib->length_dw << 12) | (upper_32_bits(ib->gpu_addr) & 0xFF));
+
+}
+
+static void si_dma_ring_emit_hdp_flush(struct amdgpu_ring *ring)
+{
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, (0xf << 16) | (HDP_MEM_COHERENCY_FLUSH_CNTL));
+       amdgpu_ring_write(ring, 1);
+}
+
+static void si_dma_ring_emit_hdp_invalidate(struct amdgpu_ring *ring)
+{
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, (0xf << 16) | (HDP_DEBUG0));
+       amdgpu_ring_write(ring, 1);
+}
+
+/**
+ * si_dma_ring_emit_fence - emit a fence on the DMA ring
+ *
+ * @ring: amdgpu ring pointer
+ * @fence: amdgpu 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 (VI).
+ */
+static void si_dma_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
+                                     unsigned flags)
+{
+
+       bool write64bit = flags & AMDGPU_FENCE_FLAG_64BIT;
+       /* write the fence */
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_FENCE, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, addr & 0xfffffffc);
+       amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xff));
+       amdgpu_ring_write(ring, seq);
+       /* optionally write high bits as well */
+       if (write64bit) {
+               addr += 4;
+               amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_FENCE, 0, 0, 0, 0));
+               amdgpu_ring_write(ring, addr & 0xfffffffc);
+               amdgpu_ring_write(ring, (upper_32_bits(addr) & 0xff));
+               amdgpu_ring_write(ring, upper_32_bits(seq));
+       }
+       /* generate an interrupt */
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_TRAP, 0, 0, 0, 0));
+}
+
+static void si_dma_stop(struct amdgpu_device *adev)
+{
+       struct amdgpu_ring *ring;
+       u32 rb_cntl;
+       unsigned i;
+
+       for (i = 0; i < adev->sdma.num_instances; i++) {
+               ring = &adev->sdma.instance[i].ring;
+               /* dma0 */
+               rb_cntl = RREG32(DMA_RB_CNTL + sdma_offsets[i]);
+               rb_cntl &= ~DMA_RB_ENABLE;
+               WREG32(DMA_RB_CNTL + sdma_offsets[i], rb_cntl);
+
+               if (adev->mman.buffer_funcs_ring == ring)
+                       amdgpu_ttm_set_active_vram_size(adev, adev->mc.visible_vram_size);
+               ring->ready = false;
+       }
+}
+
+static int si_dma_start(struct amdgpu_device *adev)
+{
+       struct amdgpu_ring *ring;
+       u32 rb_cntl, dma_cntl, ib_cntl, rb_bufsz;
+       int i, r;
+       uint64_t rptr_addr;
+
+       for (i = 0; i < adev->sdma.num_instances; i++) {
+               ring = &adev->sdma.instance[i].ring;
+
+               WREG32(DMA_SEM_INCOMPLETE_TIMER_CNTL + sdma_offsets[i], 0);
+               WREG32(DMA_SEM_WAIT_FAIL_TIMER_CNTL + sdma_offsets[i], 0);
+
+               /* Set ring buffer size in dwords */
+               rb_bufsz = order_base_2(ring->ring_size / 4);
+               rb_cntl = rb_bufsz << 1;
+#ifdef __BIG_ENDIAN
+               rb_cntl |= DMA_RB_SWAP_ENABLE | DMA_RPTR_WRITEBACK_SWAP_ENABLE;
+#endif
+               WREG32(DMA_RB_CNTL + sdma_offsets[i], rb_cntl);
+
+               /* Initialize the ring buffer's read and write pointers */
+               WREG32(DMA_RB_RPTR + sdma_offsets[i], 0);
+               WREG32(DMA_RB_WPTR + sdma_offsets[i], 0);
+
+               rptr_addr = adev->wb.gpu_addr + (ring->rptr_offs * 4);
+
+               WREG32(DMA_RB_RPTR_ADDR_LO + sdma_offsets[i], lower_32_bits(rptr_addr));
+               WREG32(DMA_RB_RPTR_ADDR_HI + sdma_offsets[i], upper_32_bits(rptr_addr) & 0xFF);
+
+               rb_cntl |= DMA_RPTR_WRITEBACK_ENABLE;
+
+               WREG32(DMA_RB_BASE + sdma_offsets[i], ring->gpu_addr >> 8);
+
+               /* enable DMA IBs */
+               ib_cntl = DMA_IB_ENABLE | CMD_VMID_FORCE;
+#ifdef __BIG_ENDIAN
+               ib_cntl |= DMA_IB_SWAP_ENABLE;
+#endif
+               WREG32(DMA_IB_CNTL + sdma_offsets[i], ib_cntl);
+
+               dma_cntl = RREG32(DMA_CNTL + sdma_offsets[i]);
+               dma_cntl &= ~CTXEMPTY_INT_ENABLE;
+               WREG32(DMA_CNTL + sdma_offsets[i], dma_cntl);
+
+               ring->wptr = 0;
+               WREG32(DMA_RB_WPTR + sdma_offsets[i], ring->wptr << 2);
+               WREG32(DMA_RB_CNTL + sdma_offsets[i], rb_cntl | DMA_RB_ENABLE);
+
+               ring->ready = true;
+
+               r = amdgpu_ring_test_ring(ring);
+               if (r) {
+                       ring->ready = false;
+                       return r;
+               }
+
+               if (adev->mman.buffer_funcs_ring == ring)
+                       amdgpu_ttm_set_active_vram_size(adev, adev->mc.real_vram_size);
+       }
+
+       return 0;
+}
+
+/**
+ * si_dma_ring_test_ring - simple async dma engine test
+ *
+ * @ring: amdgpu_ring structure holding ring information
+ *
+ * Test the DMA engine by writing using it to write an
+ * value to memory. (VI).
+ * Returns 0 for success, error for failure.
+ */
+static int si_dma_ring_test_ring(struct amdgpu_ring *ring)
+{
+       struct amdgpu_device *adev = ring->adev;
+       unsigned i;
+       unsigned index;
+       int r;
+       u32 tmp;
+       u64 gpu_addr;
+
+       r = amdgpu_wb_get(adev, &index);
+       if (r) {
+               dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
+               return r;
+       }
+
+       gpu_addr = adev->wb.gpu_addr + (index * 4);
+       tmp = 0xCAFEDEAD;
+       adev->wb.wb[index] = cpu_to_le32(tmp);
+
+       r = amdgpu_ring_alloc(ring, 4);
+       if (r) {
+               DRM_ERROR("amdgpu: dma failed to lock ring %d (%d).\n", ring->idx, r);
+               amdgpu_wb_free(adev, index);
+               return r;
+       }
+
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, 1));
+       amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
+       amdgpu_ring_write(ring, upper_32_bits(gpu_addr) & 0xff);
+       amdgpu_ring_write(ring, 0xDEADBEEF);
+       amdgpu_ring_commit(ring);
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               tmp = le32_to_cpu(adev->wb.wb[index]);
+               if (tmp == 0xDEADBEEF)
+                       break;
+               DRM_UDELAY(1);
+       }
+
+       if (i < adev->usec_timeout) {
+               DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
+       } else {
+               DRM_ERROR("amdgpu: ring %d test failed (0x%08X)\n",
+                         ring->idx, tmp);
+               r = -EINVAL;
+       }
+       amdgpu_wb_free(adev, index);
+
+       return r;
+}
+
+/**
+ * si_dma_ring_test_ib - test an IB on the DMA engine
+ *
+ * @ring: amdgpu_ring structure holding ring information
+ *
+ * Test a simple IB in the DMA ring (VI).
+ * Returns 0 on success, error on failure.
+ */
+static int si_dma_ring_test_ib(struct amdgpu_ring *ring, long timeout)
+{
+       struct amdgpu_device *adev = ring->adev;
+       struct amdgpu_ib ib;
+       struct fence *f = NULL;
+       unsigned index;
+       u32 tmp = 0;
+       u64 gpu_addr;
+       long r;
+
+       r = amdgpu_wb_get(adev, &index);
+       if (r) {
+               dev_err(adev->dev, "(%ld) failed to allocate wb slot\n", r);
+               return r;
+       }
+
+       gpu_addr = adev->wb.gpu_addr + (index * 4);
+       tmp = 0xCAFEDEAD;
+       adev->wb.wb[index] = cpu_to_le32(tmp);
+       memset(&ib, 0, sizeof(ib));
+       r = amdgpu_ib_get(adev, NULL, 256, &ib);
+       if (r) {
+               DRM_ERROR("amdgpu: failed to get ib (%ld).\n", r);
+               goto err0;
+       }
+
+       ib.ptr[0] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, 1);
+       ib.ptr[1] = lower_32_bits(gpu_addr);
+       ib.ptr[2] = upper_32_bits(gpu_addr) & 0xff;
+       ib.ptr[3] = 0xDEADBEEF;
+       ib.length_dw = 4;
+       r = amdgpu_ib_schedule(ring, 1, &ib, NULL, NULL, &f);
+       if (r)
+               goto err1;
+
+       r = fence_wait_timeout(f, false, timeout);
+       if (r == 0) {
+               DRM_ERROR("amdgpu: IB test timed out\n");
+               r = -ETIMEDOUT;
+               goto err1;
+       } else if (r < 0) {
+               DRM_ERROR("amdgpu: fence wait failed (%ld).\n", r);
+               goto err1;
+       }
+       tmp = le32_to_cpu(adev->wb.wb[index]);
+       if (tmp == 0xDEADBEEF) {
+               DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
+               r = 0;
+       } else {
+               DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
+               r = -EINVAL;
+       }
+
+err1:
+       amdgpu_ib_free(adev, &ib, NULL);
+       fence_put(f);
+err0:
+       amdgpu_wb_free(adev, index);
+       return r;
+}
+
+/**
+ * cik_dma_vm_copy_pte - update PTEs by copying them from the GART
+ *
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @src: src addr to copy from
+ * @count: number of page entries to update
+ *
+ * Update PTEs by copying them from the GART using DMA (SI).
+ */
+static void si_dma_vm_copy_pte(struct amdgpu_ib *ib,
+                              uint64_t pe, uint64_t src,
+                              unsigned count)
+{
+       unsigned bytes = count * 8;
+
+       ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
+                                             1, 0, 0, bytes);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = lower_32_bits(src);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+       ib->ptr[ib->length_dw++] = upper_32_bits(src) & 0xff;
+}
+
+/**
+ * si_dma_vm_write_pte - update PTEs by writing them manually
+ *
+ * @ib: indirect buffer to fill with commands
+ * @pe: addr of the page entry
+ * @value: dst addr to write into pe
+ * @count: number of page entries to update
+ * @incr: increase next addr by incr bytes
+ *
+ * Update PTEs by writing them manually using DMA (SI).
+ */
+static void si_dma_vm_write_pte(struct amdgpu_ib *ib, uint64_t pe,
+                               uint64_t value, unsigned count,
+                               uint32_t incr)
+{
+       unsigned ndw = count * 2;
+
+       ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_WRITE, 0, 0, 0, ndw);
+       ib->ptr[ib->length_dw++] = lower_32_bits(pe);
+       ib->ptr[ib->length_dw++] = upper_32_bits(pe);
+       for (; ndw > 0; ndw -= 2) {
+               ib->ptr[ib->length_dw++] = lower_32_bits(value);
+               ib->ptr[ib->length_dw++] = upper_32_bits(value);
+               value += incr;
+       }
+}
+
+/**
+ * si_dma_vm_set_pte_pde - update the page tables using sDMA
+ *
+ * @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 sDMA (CIK).
+ */
+static void si_dma_vm_set_pte_pde(struct amdgpu_ib *ib,
+                                    uint64_t pe,
+                                    uint64_t addr, unsigned count,
+                                    uint32_t incr, uint32_t flags)
+{
+       uint64_t value;
+       unsigned ndw;
+
+       while (count) {
+               ndw = count * 2;
+               if (ndw > 0xFFFFE)
+                       ndw = 0xFFFFE;
+
+               if (flags & AMDGPU_PTE_VALID)
+                       value = addr;
+               else
+                       value = 0;
+
+               /* for physically contiguous pages (vram) */
+               ib->ptr[ib->length_dw++] = DMA_PTE_PDE_PACKET(ndw);
+               ib->ptr[ib->length_dw++] = pe; /* dst addr */
+               ib->ptr[ib->length_dw++] = upper_32_bits(pe) & 0xff;
+               ib->ptr[ib->length_dw++] = 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;
+               pe += ndw * 4;
+               addr += (ndw / 2) * incr;
+               count -= ndw / 2;
+       }
+}
+
+/**
+ * si_dma_pad_ib - pad the IB to the required number of dw
+ *
+ * @ib: indirect buffer to fill with padding
+ *
+ */
+static void si_dma_ring_pad_ib(struct amdgpu_ring *ring, struct amdgpu_ib *ib)
+{
+       while (ib->length_dw & 0x7)
+               ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0);
+}
+
+/**
+ * cik_sdma_ring_emit_pipeline_sync - sync the pipeline
+ *
+ * @ring: amdgpu_ring pointer
+ *
+ * Make sure all previous operations are completed (CIK).
+ */
+static void si_dma_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
+{
+       uint32_t seq = ring->fence_drv.sync_seq;
+       uint64_t addr = ring->fence_drv.gpu_addr;
+
+       /* wait for idle */
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_POLL_REG_MEM, 0, 0, 0, 0) |
+                         (1 << 27)); /* Poll memory */
+       amdgpu_ring_write(ring, lower_32_bits(addr));
+       amdgpu_ring_write(ring, (0xff << 16) | upper_32_bits(addr)); /* retry, addr_hi */
+       amdgpu_ring_write(ring, 0xffffffff); /* mask */
+       amdgpu_ring_write(ring, seq); /* value */
+       amdgpu_ring_write(ring, (3 << 28) | 0x20); /* func(equal) | poll interval */
+}
+
+/**
+ * si_dma_ring_emit_vm_flush - cik vm flush using sDMA
+ *
+ * @ring: amdgpu_ring pointer
+ * @vm: amdgpu_vm pointer
+ *
+ * Update the page table base and flush the VM TLB
+ * using sDMA (VI).
+ */
+static void si_dma_ring_emit_vm_flush(struct amdgpu_ring *ring,
+                                     unsigned vm_id, uint64_t pd_addr)
+{
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0));
+       if (vm_id < 8)
+               amdgpu_ring_write(ring, (0xf << 16) | (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + vm_id));
+       else
+               amdgpu_ring_write(ring, (0xf << 16) | (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + (vm_id - 8)));
+       amdgpu_ring_write(ring, pd_addr >> 12);
+
+       /* bits 0-7 are the VM contexts0-7 */
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST));
+       amdgpu_ring_write(ring, 1 << vm_id);
+
+       /* wait for invalidate to complete */
+       amdgpu_ring_write(ring, DMA_PACKET(DMA_PACKET_POLL_REG_MEM, 0, 0, 0, 0));
+       amdgpu_ring_write(ring, VM_INVALIDATE_REQUEST);
+       amdgpu_ring_write(ring, 0xff << 16); /* retry */
+       amdgpu_ring_write(ring, 1 << vm_id); /* mask */
+       amdgpu_ring_write(ring, 0); /* value */
+       amdgpu_ring_write(ring, (0 << 28) | 0x20); /* func(always) | poll interval */
+}
+
+static unsigned si_dma_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               7 + 3; /* si_dma_ring_emit_ib */
+}
+
+static unsigned si_dma_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               3 + /* si_dma_ring_emit_hdp_flush */
+               3 + /* si_dma_ring_emit_hdp_invalidate */
+               6 + /* si_dma_ring_emit_pipeline_sync */
+               12 + /* si_dma_ring_emit_vm_flush */
+               9 + 9 + 9; /* si_dma_ring_emit_fence x3 for user fence, vm fence */
+}
+
+static int si_dma_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       adev->sdma.num_instances = 2;
+
+       si_dma_set_ring_funcs(adev);
+       si_dma_set_buffer_funcs(adev);
+       si_dma_set_vm_pte_funcs(adev);
+       si_dma_set_irq_funcs(adev);
+
+       return 0;
+}
+
+static int si_dma_sw_init(void *handle)
+{
+       struct amdgpu_ring *ring;
+       int r, i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       /* DMA0 trap event */
+       r = amdgpu_irq_add_id(adev, 224, &adev->sdma.trap_irq);
+       if (r)
+               return r;
+
+       /* DMA1 trap event */
+       r = amdgpu_irq_add_id(adev, 244, &adev->sdma.trap_irq_1);
+       if (r)
+               return r;
+
+       for (i = 0; i < adev->sdma.num_instances; i++) {
+               ring = &adev->sdma.instance[i].ring;
+               ring->ring_obj = NULL;
+               ring->use_doorbell = false;
+               sprintf(ring->name, "sdma%d", i);
+               r = amdgpu_ring_init(adev, ring, 1024,
+                                    DMA_PACKET(DMA_PACKET_NOP, 0, 0, 0, 0), 0xf,
+                                    &adev->sdma.trap_irq,
+                                    (i == 0) ?
+                                    AMDGPU_SDMA_IRQ_TRAP0 : AMDGPU_SDMA_IRQ_TRAP1,
+                                    AMDGPU_RING_TYPE_SDMA);
+               if (r)
+                       return r;
+       }
+
+       return r;
+}
+
+static int si_dma_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       int i;
+
+       for (i = 0; i < adev->sdma.num_instances; i++)
+               amdgpu_ring_fini(&adev->sdma.instance[i].ring);
+
+       return 0;
+}
+
+static int si_dma_hw_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_dma_start(adev);
+}
+
+static int si_dma_hw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       si_dma_stop(adev);
+
+       return 0;
+}
+
+static int si_dma_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_dma_hw_fini(adev);
+}
+
+static int si_dma_resume(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_dma_hw_init(adev);
+}
+
+static bool si_dma_is_idle(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 tmp = RREG32(SRBM_STATUS2);
+
+       if (tmp & (DMA_BUSY_MASK | DMA1_BUSY_MASK))
+           return false;
+
+       return true;
+}
+
+static int si_dma_wait_for_idle(void *handle)
+{
+       unsigned i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (si_dma_is_idle(handle))
+                       return 0;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+}
+
+static int si_dma_soft_reset(void *handle)
+{
+       DRM_INFO("si_dma_soft_reset --- not implemented !!!!!!!\n");
+       return 0;
+}
+
+static int si_dma_set_trap_irq_state(struct amdgpu_device *adev,
+                                       struct amdgpu_irq_src *src,
+                                       unsigned type,
+                                       enum amdgpu_interrupt_state state)
+{
+       u32 sdma_cntl;
+
+       switch (type) {
+       case AMDGPU_SDMA_IRQ_TRAP0:
+               switch (state) {
+               case AMDGPU_IRQ_STATE_DISABLE:
+                       sdma_cntl = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET);
+                       sdma_cntl &= ~TRAP_ENABLE;
+                       WREG32(DMA_CNTL + DMA0_REGISTER_OFFSET, sdma_cntl);
+                       break;
+               case AMDGPU_IRQ_STATE_ENABLE:
+                       sdma_cntl = RREG32(DMA_CNTL + DMA0_REGISTER_OFFSET);
+                       sdma_cntl |= TRAP_ENABLE;
+                       WREG32(DMA_CNTL + DMA0_REGISTER_OFFSET, sdma_cntl);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case AMDGPU_SDMA_IRQ_TRAP1:
+               switch (state) {
+               case AMDGPU_IRQ_STATE_DISABLE:
+                       sdma_cntl = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET);
+                       sdma_cntl &= ~TRAP_ENABLE;
+                       WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, sdma_cntl);
+                       break;
+               case AMDGPU_IRQ_STATE_ENABLE:
+                       sdma_cntl = RREG32(DMA_CNTL + DMA1_REGISTER_OFFSET);
+                       sdma_cntl |= TRAP_ENABLE;
+                       WREG32(DMA_CNTL + DMA1_REGISTER_OFFSET, sdma_cntl);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int si_dma_process_trap_irq(struct amdgpu_device *adev,
+                                     struct amdgpu_irq_src *source,
+                                     struct amdgpu_iv_entry *entry)
+{
+       amdgpu_fence_process(&adev->sdma.instance[0].ring);
+
+       return 0;
+}
+
+static int si_dma_process_trap_irq_1(struct amdgpu_device *adev,
+                                     struct amdgpu_irq_src *source,
+                                     struct amdgpu_iv_entry *entry)
+{
+       amdgpu_fence_process(&adev->sdma.instance[1].ring);
+
+       return 0;
+}
+
+static int si_dma_process_illegal_inst_irq(struct amdgpu_device *adev,
+                                             struct amdgpu_irq_src *source,
+                                             struct amdgpu_iv_entry *entry)
+{
+       DRM_ERROR("Illegal instruction in SDMA command stream\n");
+       schedule_work(&adev->reset_work);
+       return 0;
+}
+
+static int si_dma_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       u32 orig, data, offset;
+       int i;
+       bool enable;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       enable = (state == AMD_CG_STATE_GATE) ? true : false;
+
+       if (enable && (adev->cg_flags & AMD_CG_SUPPORT_SDMA_MGCG)) {
+               for (i = 0; i < adev->sdma.num_instances; i++) {
+                       if (i == 0)
+                               offset = DMA0_REGISTER_OFFSET;
+                       else
+                               offset = DMA1_REGISTER_OFFSET;
+                       orig = data = RREG32(DMA_POWER_CNTL + offset);
+                       data &= ~MEM_POWER_OVERRIDE;
+                       if (data != orig)
+                               WREG32(DMA_POWER_CNTL + offset, data);
+                       WREG32(DMA_CLK_CTRL + offset, 0x00000100);
+               }
+       } else {
+               for (i = 0; i < adev->sdma.num_instances; i++) {
+                       if (i == 0)
+                               offset = DMA0_REGISTER_OFFSET;
+                       else
+                               offset = DMA1_REGISTER_OFFSET;
+                       orig = data = RREG32(DMA_POWER_CNTL + offset);
+                       data |= MEM_POWER_OVERRIDE;
+                       if (data != orig)
+                               WREG32(DMA_POWER_CNTL + offset, data);
+
+                       orig = data = RREG32(DMA_CLK_CTRL + offset);
+                       data = 0xff000000;
+                       if (data != orig)
+                               WREG32(DMA_CLK_CTRL + offset, data);
+               }
+       }
+
+       return 0;
+}
+
+static int si_dma_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       u32 tmp;
+
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       WREG32(DMA_PGFSM_WRITE,  0x00002000);
+       WREG32(DMA_PGFSM_CONFIG, 0x100010ff);
+
+       for (tmp = 0; tmp < 5; tmp++)
+               WREG32(DMA_PGFSM_WRITE, 0);
+
+       return 0;
+}
+
+const struct amd_ip_funcs si_dma_ip_funcs = {
+       .name = "si_dma",
+       .early_init = si_dma_early_init,
+       .late_init = NULL,
+       .sw_init = si_dma_sw_init,
+       .sw_fini = si_dma_sw_fini,
+       .hw_init = si_dma_hw_init,
+       .hw_fini = si_dma_hw_fini,
+       .suspend = si_dma_suspend,
+       .resume = si_dma_resume,
+       .is_idle = si_dma_is_idle,
+       .wait_for_idle = si_dma_wait_for_idle,
+       .soft_reset = si_dma_soft_reset,
+       .set_clockgating_state = si_dma_set_clockgating_state,
+       .set_powergating_state = si_dma_set_powergating_state,
+};
+
+static const struct amdgpu_ring_funcs si_dma_ring_funcs = {
+       .get_rptr = si_dma_ring_get_rptr,
+       .get_wptr = si_dma_ring_get_wptr,
+       .set_wptr = si_dma_ring_set_wptr,
+       .parse_cs = NULL,
+       .emit_ib = si_dma_ring_emit_ib,
+       .emit_fence = si_dma_ring_emit_fence,
+       .emit_pipeline_sync = si_dma_ring_emit_pipeline_sync,
+       .emit_vm_flush = si_dma_ring_emit_vm_flush,
+       .emit_hdp_flush = si_dma_ring_emit_hdp_flush,
+       .emit_hdp_invalidate = si_dma_ring_emit_hdp_invalidate,
+       .test_ring = si_dma_ring_test_ring,
+       .test_ib = si_dma_ring_test_ib,
+       .insert_nop = amdgpu_ring_insert_nop,
+       .pad_ib = si_dma_ring_pad_ib,
+       .get_emit_ib_size = si_dma_ring_get_emit_ib_size,
+       .get_dma_frame_size = si_dma_ring_get_dma_frame_size,
+};
+
+static void si_dma_set_ring_funcs(struct amdgpu_device *adev)
+{
+       int i;
+
+       for (i = 0; i < adev->sdma.num_instances; i++)
+               adev->sdma.instance[i].ring.funcs = &si_dma_ring_funcs;
+}
+
+static const struct amdgpu_irq_src_funcs si_dma_trap_irq_funcs = {
+       .set = si_dma_set_trap_irq_state,
+       .process = si_dma_process_trap_irq,
+};
+
+static const struct amdgpu_irq_src_funcs si_dma_trap_irq_funcs_1 = {
+       .set = si_dma_set_trap_irq_state,
+       .process = si_dma_process_trap_irq_1,
+};
+
+static const struct amdgpu_irq_src_funcs si_dma_illegal_inst_irq_funcs = {
+       .process = si_dma_process_illegal_inst_irq,
+};
+
+static void si_dma_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->sdma.trap_irq.num_types = AMDGPU_SDMA_IRQ_LAST;
+       adev->sdma.trap_irq.funcs = &si_dma_trap_irq_funcs;
+       adev->sdma.trap_irq_1.funcs = &si_dma_trap_irq_funcs_1;
+       adev->sdma.illegal_inst_irq.funcs = &si_dma_illegal_inst_irq_funcs;
+}
+
+/**
+ * si_dma_emit_copy_buffer - copy buffer using the sDMA engine
+ *
+ * @ring: amdgpu_ring structure holding ring information
+ * @src_offset: src GPU address
+ * @dst_offset: dst GPU address
+ * @byte_count: number of bytes to xfer
+ *
+ * Copy GPU buffers using the DMA engine (VI).
+ * Used by the amdgpu ttm implementation to move pages if
+ * registered as the asic copy callback.
+ */
+static void si_dma_emit_copy_buffer(struct amdgpu_ib *ib,
+                                      uint64_t src_offset,
+                                      uint64_t dst_offset,
+                                      uint32_t byte_count)
+{
+       ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_COPY,
+                                             1, 0, 0, byte_count);
+       ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
+       ib->ptr[ib->length_dw++] = lower_32_bits(src_offset);
+       ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset) & 0xff;
+       ib->ptr[ib->length_dw++] = upper_32_bits(src_offset) & 0xff;
+}
+
+/**
+ * si_dma_emit_fill_buffer - fill buffer using the sDMA engine
+ *
+ * @ring: amdgpu_ring structure holding ring information
+ * @src_data: value to write to buffer
+ * @dst_offset: dst GPU address
+ * @byte_count: number of bytes to xfer
+ *
+ * Fill GPU buffers using the DMA engine (VI).
+ */
+static void si_dma_emit_fill_buffer(struct amdgpu_ib *ib,
+                                      uint32_t src_data,
+                                      uint64_t dst_offset,
+                                      uint32_t byte_count)
+{
+       ib->ptr[ib->length_dw++] = DMA_PACKET(DMA_PACKET_CONSTANT_FILL,
+                                             0, 0, 0, byte_count / 4);
+       ib->ptr[ib->length_dw++] = lower_32_bits(dst_offset);
+       ib->ptr[ib->length_dw++] = src_data;
+       ib->ptr[ib->length_dw++] = upper_32_bits(dst_offset) << 16;
+}
+
+
+static const struct amdgpu_buffer_funcs si_dma_buffer_funcs = {
+       .copy_max_bytes = 0xffff8,
+       .copy_num_dw = 5,
+       .emit_copy_buffer = si_dma_emit_copy_buffer,
+
+       .fill_max_bytes = 0xffff8,
+       .fill_num_dw = 4,
+       .emit_fill_buffer = si_dma_emit_fill_buffer,
+};
+
+static void si_dma_set_buffer_funcs(struct amdgpu_device *adev)
+{
+       if (adev->mman.buffer_funcs == NULL) {
+               adev->mman.buffer_funcs = &si_dma_buffer_funcs;
+               adev->mman.buffer_funcs_ring = &adev->sdma.instance[0].ring;
+       }
+}
+
+static const struct amdgpu_vm_pte_funcs si_dma_vm_pte_funcs = {
+       .copy_pte = si_dma_vm_copy_pte,
+       .write_pte = si_dma_vm_write_pte,
+       .set_pte_pde = si_dma_vm_set_pte_pde,
+};
+
+static void si_dma_set_vm_pte_funcs(struct amdgpu_device *adev)
+{
+       unsigned i;
+
+       if (adev->vm_manager.vm_pte_funcs == NULL) {
+               adev->vm_manager.vm_pte_funcs = &si_dma_vm_pte_funcs;
+               for (i = 0; i < adev->sdma.num_instances; i++)
+                       adev->vm_manager.vm_pte_rings[i] =
+                               &adev->sdma.instance[i].ring;
+
+               adev->vm_manager.vm_pte_num_rings = adev->sdma.num_instances;
+       }
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dma.h b/drivers/gpu/drm/amd/amdgpu/si_dma.h
new file mode 100644 (file)
index 0000000..3a3e0c7
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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 __SI_DMA_H__
+#define __SI_DMA_H__
+
+extern const struct amd_ip_funcs si_dma_ip_funcs;
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c
new file mode 100644 (file)
index 0000000..8bd0892
--- /dev/null
@@ -0,0 +1,8006 @@
+/*
+ * Copyright 2013 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 "amdgpu.h"
+#include "amdgpu_pm.h"
+#include "amdgpu_dpm.h"
+#include "amdgpu_atombios.h"
+#include "si/sid.h"
+#include "r600_dpm.h"
+#include "si_dpm.h"
+#include "atom.h"
+#include "../include/pptable.h"
+#include <linux/math64.h>
+#include <linux/seq_file.h>
+#include <linux/firmware.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                 0x20000
+
+#define SCLK_MIN_DEEPSLEEP_FREQ     1350
+
+
+/* sizeof(ATOM_PPLIB_EXTENDEDHEADER) */
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2 12
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3 14
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4 16
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V5 18
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6 20
+#define SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7 22
+
+#define BIOS_SCRATCH_4                                    0x5cd
+
+MODULE_FIRMWARE("radeon/tahiti_smc.bin");
+MODULE_FIRMWARE("radeon/tahiti_k_smc.bin");
+MODULE_FIRMWARE("radeon/pitcairn_smc.bin");
+MODULE_FIRMWARE("radeon/pitcairn_k_smc.bin");
+MODULE_FIRMWARE("radeon/verde_smc.bin");
+MODULE_FIRMWARE("radeon/verde_k_smc.bin");
+MODULE_FIRMWARE("radeon/oland_smc.bin");
+MODULE_FIRMWARE("radeon/oland_k_smc.bin");
+MODULE_FIRMWARE("radeon/hainan_smc.bin");
+MODULE_FIRMWARE("radeon/hainan_k_smc.bin");
+
+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;
+       struct _ATOM_PPLIB_FANTABLE3 fan3;
+};
+
+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;
+       struct _ATOM_PPLIB_SI_CLOCK_INFO si;
+};
+
+static 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,
+};
+
+static 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,
+};
+
+static const struct si_cac_config_reg cac_weights_tahiti[] =
+{
+       { 0x0, 0x0000ffff, 0, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x101, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0xc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x8fc, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x95, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x34e, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x1a1, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0xda, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x46, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x208, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xe7, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x948, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x167, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x31, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x18e, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_tahiti[] =
+{
+       { 0x143, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x8, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+
+};
+
+static const struct si_cac_config_reg cac_override_tahiti[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_tahiti =
+{
+       ((1 << 16) | 27027),
+       6,
+       0,
+       4,
+       95,
+       {
+               0UL,
+               0UL,
+               4521550UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               40
+       },
+       595000000UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_tahiti =
+{
+       { 1159409, 0, 0, 0, 0 },
+       { 777, 0, 0, 0, 0 },
+       2,
+       54000,
+       127000,
+       25,
+       2,
+       10,
+       13,
+       { 27, 31, 35, 39, 43, 47, 54, 61, 67, 74, 81, 88, 95, 0, 0, 0 },
+       { 240888759, 221057860, 235370597, 162287531, 158510299, 131423027, 116673180, 103067515, 87941937, 76209048, 68209175, 64090048, 58301890, 0, 0, 0 },
+       { 12024, 11189, 11451, 8411, 7939, 6666, 5681, 4905, 4241, 3720, 3354, 3122, 2890, 0, 0, 0 },
+       85,
+       false
+};
+
+#if 0
+static const struct si_dte_data dte_data_tahiti_le =
+{
+       { 0x1E8480, 0x7A1200, 0x2160EC0, 0x3938700, 0 },
+       { 0x7D, 0x7D, 0x4E4, 0xB00, 0 },
+       0x5,
+       0xAFC8,
+       0x64,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x78, 0x7C, 0x82, 0x88, 0x8E, 0x94, 0x9A, 0xA0, 0xA6, 0xAC, 0xB0, 0xB4, 0xB8, 0xBC, 0xC0, 0xC4 },
+       { 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700, 0x3938700 },
+       { 0x2AF8, 0x2AF8, 0x29BB, 0x27F9, 0x2637, 0x2475, 0x22B3, 0x20F1, 0x1F2F, 0x1D6D, 0x1734, 0x1414, 0x10F4, 0xDD4, 0xAB4, 0x794 },
+       85,
+       true
+};
+#endif
+
+static const struct si_dte_data dte_data_tahiti_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x7D0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_new_zealand =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0 },
+       { 0x29B, 0x3E9, 0x537, 0x7D2, 0 },
+       0x5,
+       0xAFC8,
+       0x69,
+       0x32,
+       1,
+       0,
+       0x10,
+       { 0x82, 0xA0, 0xB4, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xDAC, 0x1388, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685, 0x685 },
+       85,
+       true
+};
+
+static const struct si_dte_data dte_data_aruba_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_malta =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_cac_config_reg cac_weights_pitcairn[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x8a, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x24d, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x19, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0xc11, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x7f3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x403, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x367, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x4c9, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x45d, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x36d, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x534, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x5da, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x880, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0x201, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x1f, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5de, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x7b, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0xf9, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x66, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x13, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x186, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_pitcairn[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x116, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x155, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x92, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x149, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x101, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x119, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x158, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x95, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x14c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x122, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x125, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x128, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x12b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_pitcairn[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_pitcairn =
+{
+       ((1 << 16) | 27027),
+       5,
+       0,
+       6,
+       100,
+       {
+               51600000UL,
+               1800000UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_pitcairn =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_curacao_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_curacao_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x1D17, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_neptune_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       45000,
+       100,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0x3A2F, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2BD, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_chelsea_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x30A, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_heathrow[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x362, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x315, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_cape_verde[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_cape_verde[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x9b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x107, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x113, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x0001fffe, 1, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x152, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x146, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_cape_verde[] =
+{
+    { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_cape_verde =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_cape_verde =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_venus_xtx =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x71C, 0xAAB, 0xE39, 0x11C7, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xD6D8, 0x88B8, 0x1555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0xBDA, 0x11C7, 0x17B4, 0x1DA1, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0xAFC8, 0x88B8, 0x238E, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_venus_pro =
+{
+       {  0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x11C7, 0x1AAB, 0x238E, 0x2C72, 0x0 },
+       5,
+       55000,
+       0x69,
+       0xA,
+       1,
+       0,
+       0x3,
+       { 0x96, 0xB4, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x895440, 0x3D0900, 0x989680, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       { 0x88B8, 0x88B8, 0x3555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_cac_config_reg cac_weights_oland[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x82, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x153, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x52, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x4F, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x135, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0xAC, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x118, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0xBE, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x110, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x4CD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x37, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x27, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0xC3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x35, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0x28, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x26C, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3B2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x99D, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA3F, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0xA, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x34, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x3BA, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x30, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7A, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x100, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_mars_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x60, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_pro[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x90, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_weights_oland_xt[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x43, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0xAF, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x2A, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x29, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0xA0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x59, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x1A5, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1D6, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0x2A3, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x8FD, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x76, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x8A, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0xA3, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x71, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xA6, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x81, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0x3D2, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0x27C, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xA96, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x5, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0xB, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x15, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x36, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x10, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x120, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x32, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x7E, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0x280, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0x7, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0x3C, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0x203, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0xB4, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_oland[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x4, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg lcac_mars_pro[] =
+{
+       { 0x98, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x98, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x104, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x110, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x14f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x0001fffe, 1, 0x6, SISLANDS_CACCONFIG_CGIND },
+       { 0x8c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x143, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x11f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x164, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x167, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16a, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15e, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x161, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x15b, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x0001fffe, 1, 0x2, SISLANDS_CACCONFIG_CGIND },
+       { 0x16d, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x170, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x173, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x176, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x179, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17c, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x0001fffe, 1, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0x17f, 0x00000001, 0, 0x1, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_cac_config_reg cac_override_oland[] =
+{
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_oland =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_powertune_data powertune_data_mars_pro =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       7,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static const struct si_dte_data dte_data_oland =
+{
+       { 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0 },
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+       0,
+       false
+};
+
+static const struct si_dte_data dte_data_mars_pro =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xF627, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+static const struct si_dte_data dte_data_sun_xt =
+{
+       { 0x1E8480, 0x3D0900, 0x989680, 0x2625A00, 0x0 },
+       { 0x0, 0x0, 0x0, 0x0, 0x0 },
+       5,
+       55000,
+       105,
+       0xA,
+       1,
+       0,
+       0x10,
+       { 0x96, 0xB4, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
+       { 0x895440, 0x3D0900, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680, 0x989680 },
+       { 0xD555, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 },
+       90,
+       true
+};
+
+
+static const struct si_cac_config_reg cac_weights_hainan[] =
+{
+       { 0x0, 0x0000ffff, 0, 0x2d9, SISLANDS_CACCONFIG_CGIND },
+       { 0x0, 0xffff0000, 16, 0x22b, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0x0000ffff, 0, 0x21c, SISLANDS_CACCONFIG_CGIND },
+       { 0x1, 0xffff0000, 16, 0x1dc, SISLANDS_CACCONFIG_CGIND },
+       { 0x2, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0x0000ffff, 0, 0x24e, SISLANDS_CACCONFIG_CGIND },
+       { 0x3, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0x0000ffff, 0, 0x35e, SISLANDS_CACCONFIG_CGIND },
+       { 0x5, 0xffff0000, 16, 0x1143, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0x0000ffff, 0, 0xe17, SISLANDS_CACCONFIG_CGIND },
+       { 0x6, 0xffff0000, 16, 0x441, SISLANDS_CACCONFIG_CGIND },
+       { 0x18f, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0x0000ffff, 0, 0x28b, SISLANDS_CACCONFIG_CGIND },
+       { 0x7, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x8, 0xffff0000, 16, 0xabe, SISLANDS_CACCONFIG_CGIND },
+       { 0x9, 0x0000ffff, 0, 0xf11, SISLANDS_CACCONFIG_CGIND },
+       { 0xa, 0x0000ffff, 0, 0x907, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0x0000ffff, 0, 0xb45, SISLANDS_CACCONFIG_CGIND },
+       { 0xb, 0xffff0000, 16, 0xd1e, SISLANDS_CACCONFIG_CGIND },
+       { 0xc, 0x0000ffff, 0, 0xa2c, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0x0000ffff, 0, 0x62, SISLANDS_CACCONFIG_CGIND },
+       { 0xd, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0xe, 0x0000ffff, 0, 0x1f3, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0x0000ffff, 0, 0x42, SISLANDS_CACCONFIG_CGIND },
+       { 0xf, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x10, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0x0000ffff, 0, 0x709, SISLANDS_CACCONFIG_CGIND },
+       { 0x11, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x12, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x13, 0xffff0000, 16, 0x3a, SISLANDS_CACCONFIG_CGIND },
+       { 0x14, 0x0000ffff, 0, 0x357, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0x0000ffff, 0, 0x9f, SISLANDS_CACCONFIG_CGIND },
+       { 0x15, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x4e, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0x0000ffff, 0, 0x314, SISLANDS_CACCONFIG_CGIND },
+       { 0x16, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x17, 0x0000ffff, 0, 0x6d, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x18, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0x0000ffff, 0, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x19, 0xffff0000, 16, 0x0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1a, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1b, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1c, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1d, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1e, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x1f, 0xffff0000, 16, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x20, 0x0000ffff, 0, 0, SISLANDS_CACCONFIG_CGIND },
+       { 0x6d, 0x0000ffff, 0, 0x1b9, SISLANDS_CACCONFIG_CGIND },
+       { 0xFFFFFFFF }
+};
+
+static const struct si_powertune_data powertune_data_hainan =
+{
+       ((1 << 16) | 0x6993),
+       5,
+       0,
+       9,
+       105,
+       {
+               0UL,
+               0UL,
+               7194395UL,
+               309631529UL,
+               -1270850L,
+               4513710L,
+               100
+       },
+       117830498UL,
+       12,
+       {
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0,
+               0
+       },
+       true
+};
+
+static struct rv7xx_power_info *rv770_get_pi(struct amdgpu_device *adev);
+static struct evergreen_power_info *evergreen_get_pi(struct amdgpu_device *adev);
+static struct ni_power_info *ni_get_pi(struct amdgpu_device *adev);
+static struct  si_ps *si_get_ps(struct amdgpu_ps *rps);
+
+static int si_populate_voltage_value(struct amdgpu_device *adev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_SMC_VOLTAGE_VALUE *voltage);
+static int si_get_std_voltage_value(struct amdgpu_device *adev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage);
+static int si_write_smc_soft_register(struct amdgpu_device *adev,
+                                     u16 reg_offset, u32 value);
+static int si_convert_power_level_to_smc(struct amdgpu_device *adev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level);
+static int si_calculate_sclk_params(struct amdgpu_device *adev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk);
+
+static void si_thermal_start_smc_fan_control(struct amdgpu_device *adev);
+static void si_fan_ctrl_set_default_mode(struct amdgpu_device *adev);
+static void si_dpm_set_dpm_funcs(struct amdgpu_device *adev);
+static void si_dpm_set_irq_funcs(struct amdgpu_device *adev);
+
+static struct si_power_info *si_get_pi(struct amdgpu_device *adev)
+{
+       struct si_power_info *pi = adev->pm.dpm.priv;
+       return pi;
+}
+
+static void si_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
+                                                    u16 v, s32 t, u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+       s64 temperature, t_slope, t_intercept, av, bv, t_ref;
+       s64 tmp;
+
+       i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+       temperature = div64_s64(drm_int2fixp(t), 1000);
+
+       t_slope = div64_s64(drm_int2fixp(coeff->t_slope), 100000000);
+       t_intercept = div64_s64(drm_int2fixp(coeff->t_intercept), 100000000);
+       av = div64_s64(drm_int2fixp(coeff->av), 100000000);
+       bv = div64_s64(drm_int2fixp(coeff->bv), 100000000);
+       t_ref = drm_int2fixp(coeff->t_ref);
+
+       tmp = drm_fixp_mul(t_slope, vddc) + t_intercept;
+       kt = drm_fixp_exp(drm_fixp_mul(tmp, temperature));
+       kt = drm_fixp_div(kt, drm_fixp_exp(drm_fixp_mul(tmp, t_ref)));
+       kv = drm_fixp_mul(av, drm_fixp_exp(drm_fixp_mul(bv, vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v_and_t(struct amdgpu_device *adev,
+                                            const struct ni_leakage_coeffients *coeff,
+                                            u16 v,
+                                            s32 t,
+                                            u32 i_leakage,
+                                            u32 *leakage)
+{
+       si_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+
+static void si_calculate_leakage_for_v_formula(const struct ni_leakage_coeffients *coeff,
+                                              const u32 fixed_kt, u16 v,
+                                              u32 ileakage, u32 *leakage)
+{
+       s64 kt, kv, leakage_w, i_leakage, vddc;
+
+       i_leakage = div64_s64(drm_int2fixp(ileakage), 100);
+       vddc = div64_s64(drm_int2fixp(v), 1000);
+
+       kt = div64_s64(drm_int2fixp(fixed_kt), 100000000);
+       kv = drm_fixp_mul(div64_s64(drm_int2fixp(coeff->av), 100000000),
+                         drm_fixp_exp(drm_fixp_mul(div64_s64(drm_int2fixp(coeff->bv), 100000000), vddc)));
+
+       leakage_w = drm_fixp_mul(drm_fixp_mul(drm_fixp_mul(i_leakage, kt), kv), vddc);
+
+       *leakage = drm_fixp2int(leakage_w * 1000);
+}
+
+static void si_calculate_leakage_for_v(struct amdgpu_device *adev,
+                                      const struct ni_leakage_coeffients *coeff,
+                                      const u32 fixed_kt,
+                                      u16 v,
+                                      u32 i_leakage,
+                                      u32 *leakage)
+{
+       si_calculate_leakage_for_v_formula(coeff, fixed_kt, v, i_leakage, leakage);
+}
+
+
+static void si_update_dte_from_pl2(struct amdgpu_device *adev,
+                                  struct si_dte_data *dte_data)
+{
+       u32 p_limit1 = adev->pm.dpm.tdp_limit;
+       u32 p_limit2 = adev->pm.dpm.near_tdp_limit;
+       u32 k = dte_data->k;
+       u32 t_max = dte_data->max_t;
+       u32 t_split[5] = { 10, 15, 20, 25, 30 };
+       u32 t_0 = dte_data->t0;
+       u32 i;
+
+       if (p_limit2 != 0 && p_limit2 <= p_limit1) {
+               dte_data->tdep_count = 3;
+
+               for (i = 0; i < k; i++) {
+                       dte_data->r[i] =
+                               (t_split[i] * (t_max - t_0/(u32)1000) * (1 << 14)) /
+                               (p_limit2  * (u32)100);
+               }
+
+               dte_data->tdep_r[1] = dte_data->r[4] * 2;
+
+               for (i = 2; i < SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE; i++) {
+                       dte_data->tdep_r[i] = dte_data->r[4];
+               }
+       } else {
+               DRM_ERROR("Invalid PL2! DTE will not be updated.\n");
+       }
+}
+
+static struct rv7xx_power_info *rv770_get_pi(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = adev->pm.dpm.priv;
+
+       return pi;
+}
+
+static struct ni_power_info *ni_get_pi(struct amdgpu_device *adev)
+{
+       struct ni_power_info *pi = adev->pm.dpm.priv;
+
+       return pi;
+}
+
+static struct si_ps *si_get_ps(struct amdgpu_ps *aps)
+{
+       struct  si_ps *ps = aps->ps_priv;
+
+       return ps;
+}
+
+static void si_initialize_powertune_defaults(struct amdgpu_device *adev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       bool update_dte_from_pl2 = false;
+
+       if (adev->asic_type == CHIP_TAHITI) {
+               si_pi->cac_weights = cac_weights_tahiti;
+               si_pi->lcac_config = lcac_tahiti;
+               si_pi->cac_override = cac_override_tahiti;
+               si_pi->powertune_data = &powertune_data_tahiti;
+               si_pi->dte_data = dte_data_tahiti;
+
+               switch (adev->pdev->device) {
+               case 0x6798:
+                       si_pi->dte_data.enable_dte_by_default = true;
+                       break;
+               case 0x6799:
+                       si_pi->dte_data = dte_data_new_zealand;
+                       break;
+               case 0x6790:
+               case 0x6791:
+               case 0x6792:
+               case 0x679E:
+                       si_pi->dte_data = dte_data_aruba_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679B:
+                       si_pi->dte_data = dte_data_malta;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x679A:
+                       si_pi->dte_data = dte_data_tahiti_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       if (si_pi->dte_data.enable_dte_by_default == true)
+                               DRM_ERROR("DTE is not enabled!\n");
+                       break;
+               }
+       } else if (adev->asic_type == CHIP_PITCAIRN) {
+               si_pi->cac_weights = cac_weights_pitcairn;
+               si_pi->lcac_config = lcac_pitcairn;
+               si_pi->cac_override = cac_override_pitcairn;
+               si_pi->powertune_data = &powertune_data_pitcairn;
+
+               switch (adev->pdev->device) {
+               case 0x6810:
+               case 0x6818:
+                       si_pi->dte_data = dte_data_curacao_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6819:
+               case 0x6811:
+                       si_pi->dte_data = dte_data_curacao_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6800:
+               case 0x6806:
+                       si_pi->dte_data = dte_data_neptune_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->dte_data = dte_data_pitcairn;
+                       break;
+               }
+       } else if (adev->asic_type == CHIP_VERDE) {
+               si_pi->lcac_config = lcac_cape_verde;
+               si_pi->cac_override = cac_override_cape_verde;
+               si_pi->powertune_data = &powertune_data_cape_verde;
+
+               switch (adev->pdev->device) {
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+               case 0x6835:
+                       si_pi->cac_weights = cac_weights_cape_verde_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x682C:
+                       si_pi->cac_weights = cac_weights_cape_verde_pro;
+                       si_pi->dte_data = dte_data_sun_xt;
+                       break;
+               case 0x6825:
+               case 0x6827:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6824:
+               case 0x682D:
+                       si_pi->cac_weights = cac_weights_chelsea_xt;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x682F:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               case 0x6820:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xtx;
+                       break;
+               case 0x6821:
+                       si_pi->cac_weights = cac_weights_heathrow;
+                       si_pi->dte_data = dte_data_venus_xt;
+                       break;
+               case 0x6823:
+               case 0x682B:
+               case 0x6822:
+               case 0x682A:
+                       si_pi->cac_weights = cac_weights_chelsea_pro;
+                       si_pi->dte_data = dte_data_venus_pro;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_cape_verde;
+                       si_pi->dte_data = dte_data_cape_verde;
+                       break;
+               }
+       } else if (adev->asic_type == CHIP_OLAND) {
+               si_pi->lcac_config = lcac_mars_pro;
+               si_pi->cac_override = cac_override_oland;
+               si_pi->powertune_data = &powertune_data_mars_pro;
+               si_pi->dte_data = dte_data_mars_pro;
+
+               switch (adev->pdev->device) {
+               case 0x6601:
+               case 0x6621:
+               case 0x6603:
+               case 0x6605:
+                       si_pi->cac_weights = cac_weights_mars_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6600:
+               case 0x6606:
+               case 0x6620:
+               case 0x6604:
+                       si_pi->cac_weights = cac_weights_mars_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6611:
+               case 0x6613:
+               case 0x6608:
+                       si_pi->cac_weights = cac_weights_oland_pro;
+                       update_dte_from_pl2 = true;
+                       break;
+               case 0x6610:
+                       si_pi->cac_weights = cac_weights_oland_xt;
+                       update_dte_from_pl2 = true;
+                       break;
+               default:
+                       si_pi->cac_weights = cac_weights_oland;
+                       si_pi->lcac_config = lcac_oland;
+                       si_pi->cac_override = cac_override_oland;
+                       si_pi->powertune_data = &powertune_data_oland;
+                       si_pi->dte_data = dte_data_oland;
+                       break;
+               }
+       } else if (adev->asic_type == CHIP_HAINAN) {
+               si_pi->cac_weights = cac_weights_hainan;
+               si_pi->lcac_config = lcac_oland;
+               si_pi->cac_override = cac_override_oland;
+               si_pi->powertune_data = &powertune_data_hainan;
+               si_pi->dte_data = dte_data_sun_xt;
+               update_dte_from_pl2 = true;
+       } else {
+               DRM_ERROR("Unknown SI asic revision, failed to initialize PowerTune!\n");
+               return;
+       }
+
+       ni_pi->enable_power_containment = false;
+       ni_pi->enable_cac = false;
+       ni_pi->enable_sq_ramping = false;
+       si_pi->enable_dte = false;
+
+       if (si_pi->powertune_data->enable_powertune_by_default) {
+               ni_pi->enable_power_containment = true;
+               ni_pi->enable_cac = true;
+               if (si_pi->dte_data.enable_dte_by_default) {
+                       si_pi->enable_dte = true;
+                       if (update_dte_from_pl2)
+                               si_update_dte_from_pl2(adev, &si_pi->dte_data);
+
+               }
+               ni_pi->enable_sq_ramping = true;
+       }
+
+       ni_pi->driver_calculate_cac_leakage = true;
+       ni_pi->cac_configuration_required = true;
+
+       if (ni_pi->cac_configuration_required) {
+               ni_pi->support_cac_long_term_average = true;
+               si_pi->dyn_powertune_data.l2_lta_window_size =
+                       si_pi->powertune_data->l2_lta_window_size_default;
+               si_pi->dyn_powertune_data.lts_truncate =
+                       si_pi->powertune_data->lts_truncate_default;
+       } else {
+               ni_pi->support_cac_long_term_average = false;
+               si_pi->dyn_powertune_data.l2_lta_window_size = 0;
+               si_pi->dyn_powertune_data.lts_truncate = 0;
+       }
+
+       si_pi->dyn_powertune_data.disable_uvd_powertune = false;
+}
+
+static u32 si_get_smc_power_scaling_factor(struct amdgpu_device *adev)
+{
+       return 1;
+}
+
+static u32 si_calculate_cac_wintime(struct amdgpu_device *adev)
+{
+       u32 xclk;
+       u32 wintime;
+       u32 cac_window;
+       u32 cac_window_size;
+
+       xclk = amdgpu_asic_get_xclk(adev);
+
+       if (xclk == 0)
+               return 0;
+
+       cac_window = RREG32(CG_CAC_CTRL) & CAC_WINDOW_MASK;
+       cac_window_size = ((cac_window & 0xFFFF0000) >> 16) * (cac_window & 0x0000FFFF);
+
+       wintime = (cac_window_size * 100) / xclk;
+
+       return wintime;
+}
+
+static u32 si_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor)
+{
+       return power_in_watts;
+}
+
+static int si_calculate_adjusted_tdp_limits(struct amdgpu_device *adev,
+                                           bool adjust_polarity,
+                                           u32 tdp_adjustment,
+                                           u32 *tdp_limit,
+                                           u32 *near_tdp_limit)
+{
+       u32 adjustment_delta, max_tdp_limit;
+
+       if (tdp_adjustment > (u32)adev->pm.dpm.tdp_od_limit)
+               return -EINVAL;
+
+       max_tdp_limit = ((100 + 100) * adev->pm.dpm.tdp_limit) / 100;
+
+       if (adjust_polarity) {
+               *tdp_limit = ((100 + tdp_adjustment) * adev->pm.dpm.tdp_limit) / 100;
+               *near_tdp_limit = adev->pm.dpm.near_tdp_limit_adjusted + (*tdp_limit - adev->pm.dpm.tdp_limit);
+       } else {
+               *tdp_limit = ((100 - tdp_adjustment) * adev->pm.dpm.tdp_limit) / 100;
+               adjustment_delta  = adev->pm.dpm.tdp_limit - *tdp_limit;
+               if (adjustment_delta < adev->pm.dpm.near_tdp_limit_adjusted)
+                       *near_tdp_limit = adev->pm.dpm.near_tdp_limit_adjusted - adjustment_delta;
+               else
+                       *near_tdp_limit = 0;
+       }
+
+       if ((*tdp_limit <= 0) || (*tdp_limit > max_tdp_limit))
+               return -EINVAL;
+       if ((*near_tdp_limit <= 0) || (*near_tdp_limit > *tdp_limit))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits(struct amdgpu_device *adev,
+                                     struct amdgpu_ps *amdgpu_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               PP_SIslands_PAPMParameters *papm_parm;
+               struct amdgpu_ppm_table *ppm = adev->pm.dpm.dyn_state.ppm_table;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
+               u32 tdp_limit;
+               u32 near_tdp_limit;
+               int ret;
+
+               if (scaling_factor == 0)
+                       return -EINVAL;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               ret = si_calculate_adjusted_tdp_limits(adev,
+                                                      false, /* ??? */
+                                                      adev->pm.dpm.tdp_adjustment,
+                                                      &tdp_limit,
+                                                      &near_tdp_limit);
+               if (ret)
+                       return ret;
+
+               smc_table->dpm2Params.TDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(near_tdp_limit, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((near_tdp_limit * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = amdgpu_si_copy_bytes_to_smc(adev,
+                                                 (si_pi->state_table_start + offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                                  offsetof(PP_SIslands_DPM2Parameters, TDPLimit)),
+                                                 (u8 *)(&(smc_table->dpm2Params.TDPLimit)),
+                                                 sizeof(u32) * 3,
+                                                 si_pi->sram_end);
+               if (ret)
+                       return ret;
+
+               if (si_pi->enable_ppm) {
+                       papm_parm = &si_pi->papm_parm;
+                       memset(papm_parm, 0, sizeof(PP_SIslands_PAPMParameters));
+                       papm_parm->NearTDPLimitTherm = cpu_to_be32(ppm->dgpu_tdp);
+                       papm_parm->dGPU_T_Limit = cpu_to_be32(ppm->tj_max);
+                       papm_parm->dGPU_T_Warning = cpu_to_be32(95);
+                       papm_parm->dGPU_T_Hysteresis = cpu_to_be32(5);
+                       papm_parm->PlatformPowerLimit = 0xffffffff;
+                       papm_parm->NearTDPLimitPAPM = 0xffffffff;
+
+                       ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->papm_cfg_table_start,
+                                                         (u8 *)papm_parm,
+                                                         sizeof(PP_SIslands_PAPMParameters),
+                                                         si_pi->sram_end);
+                       if (ret)
+                               return ret;
+               }
+       }
+       return 0;
+}
+
+static int si_populate_smc_tdp_limits_2(struct amdgpu_device *adev,
+                                       struct amdgpu_ps *amdgpu_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (ni_pi->enable_power_containment) {
+               SISLANDS_SMC_STATETABLE *smc_table = &si_pi->smc_statetable;
+               u32 scaling_factor = si_get_smc_power_scaling_factor(adev);
+               int ret;
+
+               memset(smc_table, 0, sizeof(SISLANDS_SMC_STATETABLE));
+
+               smc_table->dpm2Params.NearTDPLimit =
+                       cpu_to_be32(si_scale_power_for_smc(adev->pm.dpm.near_tdp_limit_adjusted, scaling_factor) * 1000);
+               smc_table->dpm2Params.SafePowerLimit =
+                       cpu_to_be32(si_scale_power_for_smc((adev->pm.dpm.near_tdp_limit_adjusted * SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, scaling_factor) * 1000);
+
+               ret = amdgpu_si_copy_bytes_to_smc(adev,
+                                                 (si_pi->state_table_start +
+                                                  offsetof(SISLANDS_SMC_STATETABLE, dpm2Params) +
+                                                  offsetof(PP_SIslands_DPM2Parameters, NearTDPLimit)),
+                                                 (u8 *)(&(smc_table->dpm2Params.NearTDPLimit)),
+                                                 sizeof(u32) * 2,
+                                                 si_pi->sram_end);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static u16 si_calculate_power_efficiency_ratio(struct amdgpu_device *adev,
+                                              const u16 prev_std_vddc,
+                                              const u16 curr_std_vddc)
+{
+       u64 margin = (u64)SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN;
+       u64 prev_vddc = (u64)prev_std_vddc;
+       u64 curr_vddc = (u64)curr_std_vddc;
+       u64 pwr_efficiency_ratio, n, d;
+
+       if ((prev_vddc == 0) || (curr_vddc == 0))
+               return 0;
+
+       n = div64_u64((u64)1024 * curr_vddc * curr_vddc * ((u64)1000 + margin), (u64)1000);
+       d = prev_vddc * prev_vddc;
+       pwr_efficiency_ratio = div64_u64(n, d);
+
+       if (pwr_efficiency_ratio > (u64)0xFFFF)
+               return 0;
+
+       return (u16)pwr_efficiency_ratio;
+}
+
+static bool si_should_disable_uvd_powertune(struct amdgpu_device *adev,
+                                           struct amdgpu_ps *amdgpu_state)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (si_pi->dyn_powertune_data.disable_uvd_powertune &&
+           amdgpu_state->vclk && amdgpu_state->dclk)
+               return true;
+
+       return false;
+}
+
+struct evergreen_power_info *evergreen_get_pi(struct amdgpu_device *adev)
+{
+       struct evergreen_power_info *pi = adev->pm.dpm.priv;
+
+       return pi;
+}
+
+static int si_populate_power_containment_values(struct amdgpu_device *adev,
+                                               struct amdgpu_ps *amdgpu_state,
+                                               SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct  si_ps *state = si_get_ps(amdgpu_state);
+       SISLANDS_SMC_VOLTAGE_VALUE vddc;
+       u32 prev_sclk;
+       u32 max_sclk;
+       u32 min_sclk;
+       u16 prev_std_vddc;
+       u16 curr_std_vddc;
+       int i;
+       u16 pwr_efficiency_ratio;
+       u8 max_ps_percent;
+       bool disable_uvd_power_tune;
+       int ret;
+
+       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;
+
+       disable_uvd_power_tune = si_should_disable_uvd_powertune(adev, amdgpu_state);
+
+       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].dpm2.PwrEfficiencyRatio = 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;
+               if (i == 1)
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_M;
+               else
+                       max_ps_percent = SISLANDS_DPM2_MAXPS_PERCENT_H;
+
+               if (prev_sclk > max_sclk)
+                       return -EINVAL;
+
+               if ((max_ps_percent == 0) ||
+                   (prev_sclk == max_sclk) ||
+                   disable_uvd_power_tune)
+                       min_sclk = max_sclk;
+               else if (i == 1)
+                       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;
+
+               ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i-1].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(adev, &vddc, &prev_std_vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+                                               state->performance_levels[i].vddc, &vddc);
+               if (ret)
+                       return ret;
+
+               ret = si_get_std_voltage_value(adev, &vddc, &curr_std_vddc);
+               if (ret)
+                       return ret;
+
+               pwr_efficiency_ratio = si_calculate_power_efficiency_ratio(adev,
+                                                                          prev_std_vddc, curr_std_vddc);
+
+               smc_state->levels[i].dpm2.MaxPS = (u8)((SISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
+               smc_state->levels[i].dpm2.NearTDPDec = SISLANDS_DPM2_NEAR_TDP_DEC;
+               smc_state->levels[i].dpm2.AboveSafeInc = SISLANDS_DPM2_ABOVE_SAFE_INC;
+               smc_state->levels[i].dpm2.BelowSafeInc = SISLANDS_DPM2_BELOW_SAFE_INC;
+               smc_state->levels[i].dpm2.PwrEfficiencyRatio = cpu_to_be16(pwr_efficiency_ratio);
+       }
+
+       return 0;
+}
+
+static int si_populate_sq_ramping_values(struct amdgpu_device *adev,
+                                        struct amdgpu_ps *amdgpu_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct  si_ps *state = si_get_ps(amdgpu_state);
+       u32 sq_power_throttle, 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 (adev->pm.dpm.sq_ramping_threshold == 0)
+               return -EINVAL;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
+               enable_sq_ramping = false;
+
+       if (SISLANDS_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 >= adev->pm.dpm.sq_ramping_threshold) &&
+                   enable_sq_ramping) {
+                       sq_power_throttle |= MAX_POWER(SISLANDS_DPM2_SQ_RAMP_MAX_POWER);
+                       sq_power_throttle |= MIN_POWER(SISLANDS_DPM2_SQ_RAMP_MIN_POWER);
+                       sq_power_throttle2 |= MAX_POWER_DELTA(SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
+                       sq_power_throttle2 |= STI_SIZE(SISLANDS_DPM2_SQ_RAMP_STI_SIZE);
+                       sq_power_throttle2 |= LTI_RATIO(SISLANDS_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 si_enable_power_containment(struct amdgpu_device *adev,
+                                      struct amdgpu_ps *amdgpu_new_state,
+                                      bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_power_containment) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(adev, amdgpu_new_state)) {
+                               smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingActive);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->pc_enabled = false;
+                               } else {
+                                       ni_pi->pc_enabled = true;
+                               }
+                       }
+               } else {
+                       smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_TDPClampingInactive);
+                       if (smc_result != PPSMC_Result_OK)
+                               ret = -EINVAL;
+                       ni_pi->pc_enabled = false;
+               }
+       }
+
+       return ret;
+}
+
+static int si_initialize_smc_dte_tables(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int ret = 0;
+       struct si_dte_data *dte_data = &si_pi->dte_data;
+       Smc_SIslands_DTE_Configuration *dte_tables = NULL;
+       u32 table_size;
+       u8 tdep_count;
+       u32 i;
+
+       if (dte_data == NULL)
+               si_pi->enable_dte = false;
+
+       if (si_pi->enable_dte == false)
+               return 0;
+
+       if (dte_data->k <= 0)
+               return -EINVAL;
+
+       dte_tables = kzalloc(sizeof(Smc_SIslands_DTE_Configuration), GFP_KERNEL);
+       if (dte_tables == NULL) {
+               si_pi->enable_dte = false;
+               return -ENOMEM;
+       }
+
+       table_size = dte_data->k;
+
+       if (table_size > SMC_SISLANDS_DTE_MAX_FILTER_STAGES)
+               table_size = SMC_SISLANDS_DTE_MAX_FILTER_STAGES;
+
+       tdep_count = dte_data->tdep_count;
+       if (tdep_count > SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE)
+               tdep_count = SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE;
+
+       dte_tables->K = cpu_to_be32(table_size);
+       dte_tables->T0 = cpu_to_be32(dte_data->t0);
+       dte_tables->MaxT = cpu_to_be32(dte_data->max_t);
+       dte_tables->WindowSize = dte_data->window_size;
+       dte_tables->temp_select = dte_data->temp_select;
+       dte_tables->DTE_mode = dte_data->dte_mode;
+       dte_tables->Tthreshold = cpu_to_be32(dte_data->t_threshold);
+
+       if (tdep_count > 0)
+               table_size--;
+
+       for (i = 0; i < table_size; i++) {
+               dte_tables->tau[i] = cpu_to_be32(dte_data->tau[i]);
+               dte_tables->R[i]   = cpu_to_be32(dte_data->r[i]);
+       }
+
+       dte_tables->Tdep_count = tdep_count;
+
+       for (i = 0; i < (u32)tdep_count; i++) {
+               dte_tables->T_limits[i] = dte_data->t_limits[i];
+               dte_tables->Tdep_tau[i] = cpu_to_be32(dte_data->tdep_tau[i]);
+               dte_tables->Tdep_R[i] = cpu_to_be32(dte_data->tdep_r[i]);
+       }
+
+       ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->dte_table_start,
+                                         (u8 *)dte_tables,
+                                         sizeof(Smc_SIslands_DTE_Configuration),
+                                         si_pi->sram_end);
+       kfree(dte_tables);
+
+       return ret;
+}
+
+static int si_get_cac_std_voltage_max_min(struct amdgpu_device *adev,
+                                         u16 *max, u16 *min)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct amdgpu_cac_leakage_table *table =
+               &adev->pm.dpm.dyn_state.cac_leakage_table;
+       u32 i;
+       u32 v0_loadline;
+
+       if (table == NULL)
+               return -EINVAL;
+
+       *max = 0;
+       *min = 0xFFFF;
+
+       for (i = 0; i < table->count; i++) {
+               if (table->entries[i].vddc > *max)
+                       *max = table->entries[i].vddc;
+               if (table->entries[i].vddc < *min)
+                       *min = table->entries[i].vddc;
+       }
+
+       if (si_pi->powertune_data->lkge_lut_v0_percent > 100)
+               return -EINVAL;
+
+       v0_loadline = (*min) * (100 - si_pi->powertune_data->lkge_lut_v0_percent) / 100;
+
+       if (v0_loadline > 0xFFFFUL)
+               return -EINVAL;
+
+       *min = (u16)v0_loadline;
+
+       if ((*min > *max) || (*max == 0) || (*min == 0))
+               return -EINVAL;
+
+       return 0;
+}
+
+static u16 si_get_cac_std_voltage_step(u16 max, u16 min)
+{
+       return ((max - min) + (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1)) /
+               SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
+}
+
+static int si_init_dte_leakage_table(struct amdgpu_device *adev,
+                                    PP_SIslands_CacConfig *cac_tables,
+                                    u16 vddc_max, u16 vddc_min, u16 vddc_step,
+                                    u16 t0, u16 t_step)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 leakage;
+       unsigned int i, j;
+       s32 t;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(adev);
+
+       for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++) {
+               t = (1000 * (i * t_step + t0));
+
+               for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+                       voltage = vddc_max - (vddc_step * j);
+
+                       si_calculate_leakage_for_v_and_t(adev,
+                                                        &si_pi->powertune_data->leakage_coefficients,
+                                                        voltage,
+                                                        t,
+                                                        si_pi->dyn_powertune_data.cac_leakage,
+                                                        &leakage);
+
+                       smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+                       if (smc_leakage > 0xFFFF)
+                               smc_leakage = 0xFFFF;
+
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+               }
+       }
+       return 0;
+}
+
+static int si_init_simplified_leakage_table(struct amdgpu_device *adev,
+                                           PP_SIslands_CacConfig *cac_tables,
+                                           u16 vddc_max, u16 vddc_min, u16 vddc_step)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 leakage;
+       unsigned int i, j;
+       u32 smc_leakage;
+       u32 scaling_factor;
+       u16 voltage;
+
+       scaling_factor = si_get_smc_power_scaling_factor(adev);
+
+       for (j = 0; j < SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
+               voltage = vddc_max - (vddc_step * j);
+
+               si_calculate_leakage_for_v(adev,
+                                          &si_pi->powertune_data->leakage_coefficients,
+                                          si_pi->powertune_data->fixed_kt,
+                                          voltage,
+                                          si_pi->dyn_powertune_data.cac_leakage,
+                                          &leakage);
+
+               smc_leakage = si_scale_power_for_smc(leakage, scaling_factor) / 4;
+
+               if (smc_leakage > 0xFFFF)
+                       smc_leakage = 0xFFFF;
+
+               for (i = 0; i < SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES ; i++)
+                       cac_tables->cac_lkge_lut[i][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES-1-j] =
+                               cpu_to_be16((u16)smc_leakage);
+       }
+       return 0;
+}
+
+static int si_initialize_smc_cac_tables(struct amdgpu_device *adev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       PP_SIslands_CacConfig *cac_tables = NULL;
+       u16 vddc_max, vddc_min, vddc_step;
+       u16 t0, t_step;
+       u32 load_line_slope, reg;
+       int ret = 0;
+       u32 ticks_per_us = amdgpu_asic_get_xclk(adev) / 100;
+
+       if (ni_pi->enable_cac == false)
+               return 0;
+
+       cac_tables = kzalloc(sizeof(PP_SIslands_CacConfig), GFP_KERNEL);
+       if (!cac_tables)
+               return -ENOMEM;
+
+       reg = RREG32(CG_CAC_CTRL) & ~CAC_WINDOW_MASK;
+       reg |= CAC_WINDOW(si_pi->powertune_data->cac_window);
+       WREG32(CG_CAC_CTRL, reg);
+
+       si_pi->dyn_powertune_data.cac_leakage = adev->pm.dpm.cac_leakage;
+       si_pi->dyn_powertune_data.dc_pwr_value =
+               si_pi->powertune_data->dc_cac[NISLANDS_DCCAC_LEVEL_0];
+       si_pi->dyn_powertune_data.wintime = si_calculate_cac_wintime(adev);
+       si_pi->dyn_powertune_data.shift_n = si_pi->powertune_data->shift_n_default;
+
+       si_pi->dyn_powertune_data.leakage_minimum_temperature = 80 * 1000;
+
+       ret = si_get_cac_std_voltage_max_min(adev, &vddc_max, &vddc_min);
+       if (ret)
+               goto done_free;
+
+       vddc_step = si_get_cac_std_voltage_step(vddc_max, vddc_min);
+       vddc_min = vddc_max - (vddc_step * (SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES - 1));
+       t_step = 4;
+       t0 = 60;
+
+       if (si_pi->enable_dte || ni_pi->driver_calculate_cac_leakage)
+               ret = si_init_dte_leakage_table(adev, cac_tables,
+                                               vddc_max, vddc_min, vddc_step,
+                                               t0, t_step);
+       else
+               ret = si_init_simplified_leakage_table(adev, cac_tables,
+                                                      vddc_max, vddc_min, vddc_step);
+       if (ret)
+               goto done_free;
+
+       load_line_slope = ((u32)adev->pm.dpm.load_line_slope << SMC_SISLANDS_SCALE_R) / 100;
+
+       cac_tables->l2numWin_TDP = cpu_to_be32(si_pi->dyn_powertune_data.l2_lta_window_size);
+       cac_tables->lts_truncate_n = si_pi->dyn_powertune_data.lts_truncate;
+       cac_tables->SHIFT_N = si_pi->dyn_powertune_data.shift_n;
+       cac_tables->lkge_lut_V0 = cpu_to_be32((u32)vddc_min);
+       cac_tables->lkge_lut_Vstep = cpu_to_be32((u32)vddc_step);
+       cac_tables->R_LL = cpu_to_be32(load_line_slope);
+       cac_tables->WinTime = cpu_to_be32(si_pi->dyn_powertune_data.wintime);
+       cac_tables->calculation_repeats = cpu_to_be32(2);
+       cac_tables->dc_cac = cpu_to_be32(0);
+       cac_tables->log2_PG_LKG_SCALE = 12;
+       cac_tables->cac_temp = si_pi->powertune_data->operating_temp;
+       cac_tables->lkge_lut_T0 = cpu_to_be32((u32)t0);
+       cac_tables->lkge_lut_Tstep = cpu_to_be32((u32)t_step);
+
+       ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->cac_table_start,
+                                         (u8 *)cac_tables,
+                                         sizeof(PP_SIslands_CacConfig),
+                                         si_pi->sram_end);
+
+       if (ret)
+               goto done_free;
+
+       ret = si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_ticks_per_us, ticks_per_us);
+
+done_free:
+       if (ret) {
+               ni_pi->enable_cac = false;
+               ni_pi->enable_power_containment = false;
+       }
+
+       kfree(cac_tables);
+
+       return ret;
+}
+
+static int si_program_cac_config_registers(struct amdgpu_device *adev,
+                                          const struct si_cac_config_reg *cac_config_regs)
+{
+       const struct si_cac_config_reg *config_regs = cac_config_regs;
+       u32 data = 0, offset;
+
+       if (!config_regs)
+               return -EINVAL;
+
+       while (config_regs->offset != 0xFFFFFFFF) {
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               data = RREG32_SMC(offset);
+                       break;
+               default:
+                       data = RREG32(config_regs->offset);
+                       break;
+               }
+
+               data &= ~config_regs->mask;
+               data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+
+               switch (config_regs->type) {
+               case SISLANDS_CACCONFIG_CGIND:
+                       offset = SMC_CG_IND_START + config_regs->offset;
+                       if (offset < SMC_CG_IND_END)
+                               WREG32_SMC(offset, data);
+                       break;
+               default:
+                       WREG32(config_regs->offset, data);
+                       break;
+               }
+               config_regs++;
+       }
+       return 0;
+}
+
+static int si_initialize_hardware_cac_manager(struct amdgpu_device *adev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int ret;
+
+       if ((ni_pi->enable_cac == false) ||
+           (ni_pi->cac_configuration_required == false))
+               return 0;
+
+       ret = si_program_cac_config_registers(adev, si_pi->lcac_config);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(adev, si_pi->cac_override);
+       if (ret)
+               return ret;
+       ret = si_program_cac_config_registers(adev, si_pi->cac_weights);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int si_enable_smc_cac(struct amdgpu_device *adev,
+                            struct amdgpu_ps *amdgpu_new_state,
+                            bool enable)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       PPSMC_Result smc_result;
+       int ret = 0;
+
+       if (ni_pi->enable_cac) {
+               if (enable) {
+                       if (!si_should_disable_uvd_powertune(adev, amdgpu_new_state)) {
+                               if (ni_pi->support_cac_long_term_average) {
+                                       smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_CACLongTermAvgEnable);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ni_pi->support_cac_long_term_average = false;
+                               }
+
+                               smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableCac);
+                               if (smc_result != PPSMC_Result_OK) {
+                                       ret = -EINVAL;
+                                       ni_pi->cac_enabled = false;
+                               } else {
+                                       ni_pi->cac_enabled = true;
+                               }
+
+                               if (si_pi->enable_dte) {
+                                       smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableDTE);
+                                       if (smc_result != PPSMC_Result_OK)
+                                               ret = -EINVAL;
+                               }
+                       }
+               } else if (ni_pi->cac_enabled) {
+                       if (si_pi->enable_dte)
+                               smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableDTE);
+
+                       smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableCac);
+
+                       ni_pi->cac_enabled = false;
+
+                       if (ni_pi->support_cac_long_term_average)
+                               smc_result = amdgpu_si_send_msg_to_smc(adev, PPSMC_CACLongTermAvgDisable);
+               }
+       }
+       return ret;
+}
+
+static int si_init_smc_spll_table(struct amdgpu_device *adev)
+{
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       SMC_SISLANDS_SPLL_DIV_TABLE *spll_table;
+       SISLANDS_SMC_SCLK_VALUE sclk_params;
+       u32 fb_div, p_div;
+       u32 clk_s, clk_v;
+       u32 sclk = 0;
+       int ret = 0;
+       u32 tmp;
+       int i;
+
+       if (si_pi->spll_table_start == 0)
+               return -EINVAL;
+
+       spll_table = kzalloc(sizeof(SMC_SISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
+       if (spll_table == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < 256; i++) {
+               ret = si_calculate_sclk_params(adev, 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_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
+                       ret = -EINVAL;
+               if (fb_div & ~(SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT))
+                       ret = -EINVAL;
+               if (clk_s & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
+                       ret = -EINVAL;
+               if (clk_v & ~(SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
+                       ret = -EINVAL;
+
+               if (ret)
+                       break;
+
+               tmp = ((fb_div << SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
+                       ((p_div << SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
+               spll_table->freq[i] = cpu_to_be32(tmp);
+
+               tmp = ((clk_v << SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
+                       ((clk_s << SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
+               spll_table->ss[i] = cpu_to_be32(tmp);
+
+               sclk += 512;
+       }
+
+
+       if (!ret)
+               ret = amdgpu_si_copy_bytes_to_smc(adev, si_pi->spll_table_start,
+                                                 (u8 *)spll_table,
+                                                 sizeof(SMC_SISLANDS_SPLL_DIV_TABLE),
+                                                 si_pi->sram_end);
+
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       kfree(spll_table);
+
+       return ret;
+}
+
+struct si_dpm_quirk {
+       u32 chip_vendor;
+       u32 chip_device;
+       u32 subsys_vendor;
+       u32 subsys_device;
+       u32 max_sclk;
+       u32 max_mclk;
+};
+
+/* cards with dpm stability problems */
+static struct si_dpm_quirk si_dpm_quirk_list[] = {
+       /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */
+       { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0xe271, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6811, 0x174b, 0x2015, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6810, 0x174b, 0xe271, 85000, 90000 },
+       { PCI_VENDOR_ID_ATI, 0x6811, 0x1462, 0x2015, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6811, 0x1043, 0x2015, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6811, 0x148c, 0x2015, 0, 120000 },
+       { PCI_VENDOR_ID_ATI, 0x6810, 0x1682, 0x9275, 0, 120000 },
+       { 0, 0, 0, 0 },
+};
+
+static u16 si_get_lower_of_leakage_and_vce_voltage(struct amdgpu_device *adev,
+                                                  u16 vce_voltage)
+{
+       u16 highest_leakage = 0;
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int i;
+
+       for (i = 0; i < si_pi->leakage_voltage.count; i++){
+               if (highest_leakage < si_pi->leakage_voltage.entries[i].voltage)
+                       highest_leakage = si_pi->leakage_voltage.entries[i].voltage;
+       }
+
+       if (si_pi->leakage_voltage.count && (highest_leakage < vce_voltage))
+               return highest_leakage;
+
+       return vce_voltage;
+}
+
+static int si_get_vce_clock_voltage(struct amdgpu_device *adev,
+                                   u32 evclk, u32 ecclk, u16 *voltage)
+{
+       u32 i;
+       int ret = -EINVAL;
+       struct amdgpu_vce_clock_voltage_dependency_table *table =
+               &adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
+
+       if (((evclk == 0) && (ecclk == 0)) ||
+           (table && (table->count == 0))) {
+               *voltage = 0;
+               return 0;
+       }
+
+       for (i = 0; i < table->count; i++) {
+               if ((evclk <= table->entries[i].evclk) &&
+                   (ecclk <= table->entries[i].ecclk)) {
+                       *voltage = table->entries[i].v;
+                       ret = 0;
+                       break;
+               }
+       }
+
+       /* if no match return the highest voltage */
+       if (ret)
+               *voltage = table->entries[table->count - 1].v;
+
+       *voltage = si_get_lower_of_leakage_and_vce_voltage(adev, *voltage);
+
+       return ret;
+}
+
+static bool si_dpm_vblank_too_short(struct amdgpu_device *adev)
+{
+
+       u32 vblank_time = amdgpu_dpm_get_vblank_time(adev);
+       /* we never hit the non-gddr5 limit so disable it */
+       u32 switch_limit = adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0;
+
+       if (vblank_time < switch_limit)
+               return true;
+       else
+               return false;
+
+}
+
+static int ni_copy_and_switch_arb_sets(struct amdgpu_device *adev,
+                               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 void ni_update_current_ps(struct amdgpu_device *adev,
+                         struct amdgpu_ps *rps)
+{
+       struct si_ps *new_ps = si_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+
+       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 amdgpu_device *adev,
+                           struct amdgpu_ps *rps)
+{
+       struct si_ps *new_ps = si_get_ps(rps);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+
+       eg_pi->requested_rps = *rps;
+       ni_pi->requested_ps = *new_ps;
+       eg_pi->requested_rps.ps_priv = &ni_pi->requested_ps;
+}
+
+static void ni_set_uvd_clock_before_set_eng_clock(struct amdgpu_device *adev,
+                                          struct amdgpu_ps *new_ps,
+                                          struct amdgpu_ps *old_ps)
+{
+       struct si_ps *new_state = si_get_ps(new_ps);
+       struct si_ps *current_state = si_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->performance_levels[new_state->performance_level_count - 1].sclk >=
+           current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+               return;
+
+       amdgpu_asic_set_uvd_clocks(adev, new_ps->vclk, new_ps->dclk);
+}
+
+static void ni_set_uvd_clock_after_set_eng_clock(struct amdgpu_device *adev,
+                                         struct amdgpu_ps *new_ps,
+                                         struct amdgpu_ps *old_ps)
+{
+       struct si_ps *new_state = si_get_ps(new_ps);
+       struct si_ps *current_state = si_get_ps(old_ps);
+
+       if ((new_ps->vclk == old_ps->vclk) &&
+           (new_ps->dclk == old_ps->dclk))
+               return;
+
+       if (new_state->performance_levels[new_state->performance_level_count - 1].sclk <
+           current_state->performance_levels[current_state->performance_level_count - 1].sclk)
+               return;
+
+       amdgpu_asic_set_uvd_clocks(adev, new_ps->vclk, new_ps->dclk);
+}
+
+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 u32 btc_find_valid_clock(struct amdgpu_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 amdgpu_device *adev,
+                             u32 max_mclk, u32 requested_mclk)
+{
+       return btc_find_valid_clock(&adev->pm.dpm.dyn_state.valid_mclk_values,
+                                   max_mclk, requested_mclk);
+}
+
+static u32 btc_get_valid_sclk(struct amdgpu_device *adev,
+                             u32 max_sclk, u32 requested_sclk)
+{
+       return btc_find_valid_clock(&adev->pm.dpm.dyn_state.valid_sclk_values,
+                                   max_sclk, requested_sclk);
+}
+
+static void btc_get_max_clock_from_voltage_dependency_table(struct amdgpu_clock_voltage_dependency_table *table,
+                                                           u32 *max_clock)
+{
+       u32 i, clock = 0;
+
+       if ((table == NULL) || (table->count == 0)) {
+               *max_clock = clock;
+               return;
+       }
+
+       for (i = 0; i < table->count; i++) {
+               if (clock < table->entries[i].clk)
+                       clock = table->entries[i].clk;
+       }
+       *max_clock = clock;
+}
+
+static void btc_apply_voltage_dependency_rules(struct amdgpu_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 void btc_adjust_clock_combinations(struct amdgpu_device *adev,
+                                         const struct amdgpu_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) > adev->pm.dpm.dyn_state.mclk_sclk_ratio)
+                       pl->sclk = btc_get_valid_sclk(adev,
+                                                     max_limits->sclk,
+                                                     (pl->mclk +
+                                                     (adev->pm.dpm.dyn_state.mclk_sclk_ratio - 1)) /
+                                                     adev->pm.dpm.dyn_state.mclk_sclk_ratio);
+       } else {
+               if ((pl->sclk - pl->mclk) > adev->pm.dpm.dyn_state.sclk_mclk_delta)
+                       pl->mclk = btc_get_valid_mclk(adev,
+                                                     max_limits->mclk,
+                                                     pl->sclk -
+                                                     adev->pm.dpm.dyn_state.sclk_mclk_delta);
+       }
+}
+
+static void btc_apply_voltage_delta_rules(struct amdgpu_device *adev,
+                                         u16 max_vddc, u16 max_vddci,
+                                         u16 *vddc, u16 *vddci)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       u16 new_voltage;
+
+       if ((0 == *vddc) || (0 == *vddci))
+               return;
+
+       if (*vddc > *vddci) {
+               if ((*vddc - *vddci) > adev->pm.dpm.dyn_state.vddc_vddci_delta) {
+                       new_voltage = btc_find_voltage(&eg_pi->vddci_voltage_table,
+                                                      (*vddc - adev->pm.dpm.dyn_state.vddc_vddci_delta));
+                       *vddci = (new_voltage < max_vddci) ? new_voltage : max_vddci;
+               }
+       } else {
+               if ((*vddci - *vddc) > adev->pm.dpm.dyn_state.vddc_vddci_delta) {
+                       new_voltage = btc_find_voltage(&eg_pi->vddc_voltage_table,
+                                                      (*vddci - adev->pm.dpm.dyn_state.vddc_vddci_delta));
+                       *vddc = (new_voltage < max_vddc) ? new_voltage : max_vddc;
+               }
+       }
+}
+
+static enum amdgpu_pcie_gen r600_get_pcie_gen_support(struct amdgpu_device *adev,
+                                              u32 sys_mask,
+                                              enum amdgpu_pcie_gen asic_gen,
+                                              enum amdgpu_pcie_gen default_gen)
+{
+       switch (asic_gen) {
+       case AMDGPU_PCIE_GEN1:
+               return AMDGPU_PCIE_GEN1;
+       case AMDGPU_PCIE_GEN2:
+               return AMDGPU_PCIE_GEN2;
+       case AMDGPU_PCIE_GEN3:
+               return AMDGPU_PCIE_GEN3;
+       default:
+               if ((sys_mask & DRM_PCIE_SPEED_80) && (default_gen == AMDGPU_PCIE_GEN3))
+                       return AMDGPU_PCIE_GEN3;
+               else if ((sys_mask & DRM_PCIE_SPEED_50) && (default_gen == AMDGPU_PCIE_GEN2))
+                       return AMDGPU_PCIE_GEN2;
+               else
+                       return AMDGPU_PCIE_GEN1;
+       }
+       return AMDGPU_PCIE_GEN1;
+}
+
+static 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)));
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static u8 rv770_get_memory_module_index(struct amdgpu_device *adev)
+{
+       return (u8) ((RREG32(BIOS_SCRATCH_4) >> 16) & 0xff);
+}
+
+static void rv770_get_max_vddc(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       u16 vddc;
+
+       if (amdgpu_atombios_get_max_vddc(adev, 0, 0, &vddc))
+               pi->max_vddc = 0;
+       else
+               pi->max_vddc = vddc;
+}
+
+static void rv770_get_engine_memory_ss(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct amdgpu_atom_ss ss;
+
+       pi->sclk_ss = amdgpu_atombios_get_asic_ss_info(adev, &ss,
+                                                      ASIC_INTERNAL_ENGINE_SS, 0);
+       pi->mclk_ss = amdgpu_atombios_get_asic_ss_info(adev, &ss,
+                                                      ASIC_INTERNAL_MEMORY_SS, 0);
+
+       if (pi->sclk_ss || pi->mclk_ss)
+               pi->dynamic_ss = true;
+       else
+               pi->dynamic_ss = false;
+}
+
+
+static void si_apply_state_adjust_rules(struct amdgpu_device *adev,
+                                       struct amdgpu_ps *rps)
+{
+       struct  si_ps *ps = si_get_ps(rps);
+       struct amdgpu_clock_and_voltage_limits *max_limits;
+       bool disable_mclk_switching = false;
+       bool disable_sclk_switching = false;
+       u32 mclk, sclk;
+       u16 vddc, vddci, min_vce_voltage = 0;
+       u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc;
+       u32 max_sclk = 0, max_mclk = 0;
+       int i;
+       struct si_dpm_quirk *p = si_dpm_quirk_list;
+
+       /* Apply dpm quirks */
+       while (p && p->chip_device != 0) {
+               if (adev->pdev->vendor == p->chip_vendor &&
+                   adev->pdev->device == p->chip_device &&
+                   adev->pdev->subsystem_vendor == p->subsys_vendor &&
+                   adev->pdev->subsystem_device == p->subsys_device) {
+                       max_sclk = p->max_sclk;
+                       max_mclk = p->max_mclk;
+                       break;
+               }
+               ++p;
+       }
+       /* limit mclk on all R7 370 parts for stability */
+       if (adev->pdev->device == 0x6811 &&
+           adev->pdev->revision == 0x81)
+               max_mclk = 120000;
+       /* limit sclk/mclk on Jet parts for stability */
+       if (adev->pdev->device == 0x6665 &&
+           adev->pdev->revision == 0xc3) {
+               max_sclk = 75000;
+               max_mclk = 80000;
+       }
+
+       if (rps->vce_active) {
+               rps->evclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].evclk;
+               rps->ecclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].ecclk;
+               si_get_vce_clock_voltage(adev, rps->evclk, rps->ecclk,
+                                        &min_vce_voltage);
+       } else {
+               rps->evclk = 0;
+               rps->ecclk = 0;
+       }
+
+       if ((adev->pm.dpm.new_active_crtc_count > 1) ||
+           si_dpm_vblank_too_short(adev))
+               disable_mclk_switching = true;
+
+       if (rps->vclk || rps->dclk) {
+               disable_mclk_switching = true;
+               disable_sclk_switching = true;
+       }
+
+       if (adev->pm.dpm.ac_power)
+               max_limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+       else
+               max_limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
+
+       for (i = ps->performance_level_count - 2; i >= 0; i--) {
+               if (ps->performance_levels[i].vddc > ps->performance_levels[i+1].vddc)
+                       ps->performance_levels[i].vddc = ps->performance_levels[i+1].vddc;
+       }
+       if (adev->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;
+               }
+       }
+
+       /* limit clocks to max supported clocks based on voltage dependency tables */
+       btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
+                                                       &max_sclk_vddc);
+       btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                                       &max_mclk_vddci);
+       btc_get_max_clock_from_voltage_dependency_table(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                       &max_mclk_vddc);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (max_sclk_vddc) {
+                       if (ps->performance_levels[i].sclk > max_sclk_vddc)
+                               ps->performance_levels[i].sclk = max_sclk_vddc;
+               }
+               if (max_mclk_vddci) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddci)
+                               ps->performance_levels[i].mclk = max_mclk_vddci;
+               }
+               if (max_mclk_vddc) {
+                       if (ps->performance_levels[i].mclk > max_mclk_vddc)
+                               ps->performance_levels[i].mclk = max_mclk_vddc;
+               }
+               if (max_mclk) {
+                       if (ps->performance_levels[i].mclk > max_mclk)
+                               ps->performance_levels[i].mclk = max_mclk;
+               }
+               if (max_sclk) {
+                       if (ps->performance_levels[i].sclk > max_sclk)
+                               ps->performance_levels[i].sclk = max_sclk;
+               }
+       }
+
+       /* XXX validate the min clocks required for display */
+
+       if (disable_mclk_switching) {
+               mclk  = ps->performance_levels[ps->performance_level_count - 1].mclk;
+               vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
+       } else {
+               mclk = ps->performance_levels[0].mclk;
+               vddci = ps->performance_levels[0].vddci;
+       }
+
+       if (disable_sclk_switching) {
+               sclk = ps->performance_levels[ps->performance_level_count - 1].sclk;
+               vddc = ps->performance_levels[ps->performance_level_count - 1].vddc;
+       } else {
+               sclk = ps->performance_levels[0].sclk;
+               vddc = ps->performance_levels[0].vddc;
+       }
+
+       if (rps->vce_active) {
+               if (sclk < adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk)
+                       sclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].sclk;
+               if (mclk < adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].mclk)
+                       mclk = adev->pm.dpm.vce_states[adev->pm.dpm.vce_level].mclk;
+       }
+
+       /* 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;
+
+       if (disable_sclk_switching) {
+               sclk = ps->performance_levels[0].sclk;
+               for (i = 1; i < ps->performance_level_count; i++) {
+                       if (sclk < ps->performance_levels[i].sclk)
+                               sclk = ps->performance_levels[i].sclk;
+               }
+               for (i = 0; i < ps->performance_level_count; i++) {
+                       ps->performance_levels[i].sclk = sclk;
+                       ps->performance_levels[i].vddc = vddc;
+               }
+       } else {
+               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 = 0; i < ps->performance_level_count; i++)
+               btc_adjust_clock_combinations(adev, max_limits,
+                                             &ps->performance_levels[i]);
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               if (ps->performance_levels[i].vddc < min_vce_voltage)
+                       ps->performance_levels[i].vddc = min_vce_voltage;
+               btc_apply_voltage_dependency_rules(&adev->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(&adev->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(&adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                                  ps->performance_levels[i].mclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+               btc_apply_voltage_dependency_rules(&adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk,
+                                                  adev->clock.current_dispclk,
+                                                  max_limits->vddc,  &ps->performance_levels[i].vddc);
+       }
+
+       for (i = 0; i < ps->performance_level_count; i++) {
+               btc_apply_voltage_delta_rules(adev,
+                                             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 > adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
+                       ps->dc_compatible = false;
+       }
+}
+
+#if 0
+static int si_read_smc_soft_register(struct amdgpu_device *adev,
+                                    u16 reg_offset, u32 *value)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       return amdgpu_si_read_smc_sram_dword(adev,
+                                            si_pi->soft_regs_start + reg_offset, value,
+                                            si_pi->sram_end);
+}
+#endif
+
+static int si_write_smc_soft_register(struct amdgpu_device *adev,
+                                     u16 reg_offset, u32 value)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       return amdgpu_si_write_smc_sram_dword(adev,
+                                             si_pi->soft_regs_start + reg_offset,
+                                             value, si_pi->sram_end);
+}
+
+static bool si_is_special_1gb_platform(struct amdgpu_device *adev)
+{
+       bool ret = false;
+       u32 tmp, width, row, column, bank, density;
+       bool is_memory_gddr5, is_special;
+
+       tmp = RREG32(MC_SEQ_MISC0);
+       is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE == ((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT));
+       is_special = (MC_SEQ_MISC0_REV_ID_VALUE == ((tmp & MC_SEQ_MISC0_REV_ID_MASK) >> MC_SEQ_MISC0_REV_ID_SHIFT))
+               & (MC_SEQ_MISC0_VEN_ID_VALUE == ((tmp & MC_SEQ_MISC0_VEN_ID_MASK) >> MC_SEQ_MISC0_VEN_ID_SHIFT));
+
+       WREG32(MC_SEQ_IO_DEBUG_INDEX, 0xb);
+       width = ((RREG32(MC_SEQ_IO_DEBUG_DATA) >> 1) & 1) ? 16 : 32;
+
+       tmp = RREG32(MC_ARB_RAMCFG);
+       row = ((tmp & NOOFROWS_MASK) >> NOOFROWS_SHIFT) + 10;
+       column = ((tmp & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT) + 8;
+       bank = ((tmp & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + 2;
+
+       density = (1 << (row + column - 20 + bank)) * width;
+
+       if ((adev->pdev->device == 0x6819) &&
+           is_memory_gddr5 && is_special && (density == 0x400))
+               ret = true;
+
+       return ret;
+}
+
+static void si_get_leakage_vddc(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u16 vddc, count = 0;
+       int i, ret;
+
+       for (i = 0; i < SISLANDS_MAX_LEAKAGE_COUNT; i++) {
+               ret = amdgpu_atombios_get_leakage_vddc_based_on_leakage_idx(adev, &vddc, SISLANDS_LEAKAGE_INDEX0 + i);
+
+               if (!ret && (vddc > 0) && (vddc != (SISLANDS_LEAKAGE_INDEX0 + i))) {
+                       si_pi->leakage_voltage.entries[count].voltage = vddc;
+                       si_pi->leakage_voltage.entries[count].leakage_index =
+                               SISLANDS_LEAKAGE_INDEX0 + i;
+                       count++;
+               }
+       }
+       si_pi->leakage_voltage.count = count;
+}
+
+static int si_get_leakage_voltage_from_leakage_index(struct amdgpu_device *adev,
+                                                    u32 index, u16 *leakage_voltage)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int i;
+
+       if (leakage_voltage == NULL)
+               return -EINVAL;
+
+       if ((index & 0xff00) != 0xff00)
+               return -EINVAL;
+
+       if ((index & 0xff) > SISLANDS_MAX_LEAKAGE_COUNT + 1)
+               return -EINVAL;
+
+       if (index < SISLANDS_LEAKAGE_INDEX0)
+               return -EINVAL;
+
+       for (i = 0; i < si_pi->leakage_voltage.count; i++) {
+               if (si_pi->leakage_voltage.entries[i].leakage_index == index) {
+                       *leakage_voltage = si_pi->leakage_voltage.entries[i].voltage;
+                       return 0;
+               }
+       }
+       return -EAGAIN;
+}
+
+static void si_set_dpm_event_sources(struct amdgpu_device *adev, u32 sources)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       bool want_thermal_protection;
+       enum amdgpu_dpm_event_src dpm_event_src;
+
+       switch (sources) {
+       case 0:
+       default:
+               want_thermal_protection = false;
+               break;
+       case (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL):
+               want_thermal_protection = true;
+               dpm_event_src = AMDGPU_DPM_EVENT_SRC_DIGITAL;
+               break;
+       case (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
+               want_thermal_protection = true;
+               dpm_event_src = AMDGPU_DPM_EVENT_SRC_EXTERNAL;
+               break;
+       case ((1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
+             (1 << AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL)):
+               want_thermal_protection = true;
+               dpm_event_src = AMDGPU_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 si_enable_auto_throttle_source(struct amdgpu_device *adev,
+                                          enum amdgpu_dpm_auto_throttle_src source,
+                                          bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+       if (enable) {
+               if (!(pi->active_auto_throttle_sources & (1 << source))) {
+                       pi->active_auto_throttle_sources |= 1 << source;
+                       si_set_dpm_event_sources(adev, pi->active_auto_throttle_sources);
+               }
+       } else {
+               if (pi->active_auto_throttle_sources & (1 << source)) {
+                       pi->active_auto_throttle_sources &= ~(1 << source);
+                       si_set_dpm_event_sources(adev, pi->active_auto_throttle_sources);
+               }
+       }
+}
+
+static void si_start_dpm(struct amdgpu_device *adev)
+{
+       WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_stop_dpm(struct amdgpu_device *adev)
+{
+       WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+
+static void si_enable_sclk_control(struct amdgpu_device *adev, 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);
+
+}
+
+#if 0
+static int si_notify_hardware_of_thermal_state(struct amdgpu_device *adev,
+                                              u32 thermal_level)
+{
+       PPSMC_Result ret;
+
+       if (thermal_level == 0) {
+               ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableThermalInterrupt);
+               if (ret == PPSMC_Result_OK)
+                       return 0;
+               else
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+static void si_notify_hardware_vpu_recovery_event(struct amdgpu_device *adev)
+{
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen, true);
+}
+#endif
+
+#if 0
+static int si_notify_hw_of_powersource(struct amdgpu_device *adev, bool ac_power)
+{
+       if (ac_power)
+               return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+#endif
+
+static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
+                                                     PPSMC_Msg msg, u32 parameter)
+{
+       WREG32(SMC_SCRATCH0, parameter);
+       return amdgpu_si_send_msg_to_smc(adev, msg);
+}
+
+static int si_restrict_performance_levels_before_switch(struct amdgpu_device *adev)
+{
+       if (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_dpm_force_performance_level(struct amdgpu_device *adev,
+                                  enum amdgpu_dpm_forced_level level)
+{
+       struct amdgpu_ps *rps = adev->pm.dpm.current_ps;
+       struct  si_ps *ps = si_get_ps(rps);
+       u32 levels = ps->performance_level_count;
+
+       if (level == AMDGPU_DPM_FORCED_LEVEL_HIGH) {
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 1) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == AMDGPU_DPM_FORCED_LEVEL_LOW) {
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, 1) != PPSMC_Result_OK)
+                       return -EINVAL;
+       } else if (level == AMDGPU_DPM_FORCED_LEVEL_AUTO) {
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetForcedLevels, 0) != PPSMC_Result_OK)
+                       return -EINVAL;
+
+               if (si_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SetEnabledLevels, levels) != PPSMC_Result_OK)
+                       return -EINVAL;
+       }
+
+       adev->pm.dpm.forced_level = level;
+
+       return 0;
+}
+
+#if 0
+static int si_set_boot_state(struct amdgpu_device *adev)
+{
+       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_SwitchToInitialState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+#endif
+
+static int si_set_sw_state(struct amdgpu_device *adev)
+{
+       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_SwitchToSwState) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_halt_smc(struct amdgpu_device *adev)
+{
+       if (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_Halt) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (amdgpu_si_wait_for_smc_inactive(adev) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_resume_smc(struct amdgpu_device *adev)
+{
+       if (amdgpu_si_send_msg_to_smc(adev, PPSMC_FlushDataCache) != PPSMC_Result_OK)
+               return -EINVAL;
+
+       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_Resume) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_dpm_start_smc(struct amdgpu_device *adev)
+{
+       amdgpu_si_program_jump_on_start(adev);
+       amdgpu_si_start_smc(adev);
+       amdgpu_si_smc_clock(adev, true);
+}
+
+static void si_dpm_stop_smc(struct amdgpu_device *adev)
+{
+       amdgpu_si_reset_smc(adev);
+       amdgpu_si_smc_clock(adev, false);
+}
+
+static int si_process_firmware_header(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+       int ret;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_stateTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->state_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->soft_regs_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->mc_reg_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_fanTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->fan_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->arb_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->cac_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->dte_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_spllTable,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->spll_table_start = tmp;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev,
+                                           SISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
+                                           SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       si_pi->papm_cfg_table_start = tmp;
+
+       return ret;
+}
+
+static void si_read_clock_registers(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       si_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
+       si_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
+       si_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
+       si_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
+       si_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
+       si_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
+       si_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
+       si_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
+       si_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl = RREG32(MPLL_FUNC_CNTL);
+       si_pi->clock_registers.mpll_func_cntl_1 = RREG32(MPLL_FUNC_CNTL_1);
+       si_pi->clock_registers.mpll_func_cntl_2 = RREG32(MPLL_FUNC_CNTL_2);
+       si_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
+       si_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+
+static void si_enable_thermal_protection(struct amdgpu_device *adev,
+                                         bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
+       else
+               WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
+}
+
+static void si_enable_acpi_power_management(struct amdgpu_device *adev)
+{
+       WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+
+#if 0
+static int si_enter_ulp_state(struct amdgpu_device *adev)
+{
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_SwitchToMinimumPower);
+
+       udelay(25000);
+
+       return 0;
+}
+
+static int si_exit_ulp_state(struct amdgpu_device *adev)
+{
+       int i;
+
+       WREG32(SMC_MESSAGE_0, PPSMC_MSG_ResumeFromMinimumPower);
+
+       udelay(7000);
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (RREG32(SMC_RESP_0) == 1)
+                       break;
+               udelay(1000);
+       }
+
+       return 0;
+}
+#endif
+
+static int si_notify_smc_display_change(struct amdgpu_device *adev,
+                                    bool has_display)
+{
+       PPSMC_Msg msg = has_display ?
+               PPSMC_MSG_HasDisplay : PPSMC_MSG_NoDisplay;
+
+       return (amdgpu_si_send_msg_to_smc(adev, msg) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static void si_program_response_times(struct amdgpu_device *adev)
+{
+       u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out;
+       u32 vddc_dly, acpi_dly, vbi_dly;
+       u32 reference_clock;
+
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
+
+       voltage_response_time = (u32)adev->pm.dpm.voltage_response_time;
+       backbias_response_time = (u32)adev->pm.dpm.backbias_response_time;
+
+       if (voltage_response_time == 0)
+               voltage_response_time = 1000;
+
+       acpi_delay_time = 15000;
+       vbi_time_out = 100000;
+
+       reference_clock = amdgpu_asic_get_xclk(adev);
+
+       vddc_dly = (voltage_response_time  * reference_clock) / 100;
+       acpi_dly = (acpi_delay_time * reference_clock) / 100;
+       vbi_dly  = (vbi_time_out * reference_clock) / 100;
+
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_delay_vreg,  vddc_dly);
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_delay_acpi,  acpi_dly);
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
+}
+
+static void si_program_ds_registers(struct amdgpu_device *adev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       u32 tmp;
+
+       /* DEEP_SLEEP_CLK_SEL field should be 0x10 on tahiti A0 */
+       if (adev->asic_type == CHIP_TAHITI && adev->rev_id == 0x0)
+               tmp = 0x10;
+       else
+               tmp = 0x1;
+
+       if (eg_pi->sclk_deep_sleep) {
+               WREG32_P(MISC_CLK_CNTL, DEEP_SLEEP_CLK_SEL(tmp), ~DEEP_SLEEP_CLK_SEL_MASK);
+               WREG32_P(CG_SPLL_AUTOSCALE_CNTL, AUTOSCALE_ON_SS_CLEAR,
+                        ~AUTOSCALE_ON_SS_CLEAR);
+       }
+}
+
+static void si_program_display_gap(struct amdgpu_device *adev)
+{
+       u32 tmp, pipe;
+       int i;
+
+       tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK);
+       if (adev->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 (adev->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 ((adev->pm.dpm.new_active_crtc_count > 0) &&
+           (!(adev->pm.dpm.new_active_crtcs & (1 << pipe)))) {
+               /* find the first active crtc */
+               for (i = 0; i < adev->mode_info.num_crtc; i++) {
+                       if (adev->pm.dpm.new_active_crtcs & (1 << i))
+                               break;
+               }
+               if (i == adev->mode_info.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);
+       }
+
+       /* Setting this to false forces the performance state to low if the crtcs are disabled.
+        * This can be a problem on PowerXpress systems or if you want to use the card
+        * for offscreen rendering or compute if there are no crtcs enabled.
+        */
+       si_notify_smc_display_change(adev, adev->pm.dpm.new_active_crtc_count > 0);
+}
+
+static void si_enable_spread_spectrum(struct amdgpu_device *adev, bool enable)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+       if (enable) {
+               if (pi->sclk_ss)
+                       WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
+       } else {
+               WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+               WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+       }
+}
+
+static void si_setup_bsp(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       u32 xclk = amdgpu_asic_get_xclk(adev);
+
+       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);
+}
+
+static void si_program_git(struct amdgpu_device *adev)
+{
+       WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK);
+}
+
+static void si_program_tp(struct amdgpu_device *adev)
+{
+       int i;
+       enum r600_td td = R600_TD_DFLT;
+
+       for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
+               WREG32(CG_FFCT_0 + i, (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);
+}
+
+static void si_program_tpp(struct amdgpu_device *adev)
+{
+       WREG32(CG_TPC, R600_TPC_DFLT);
+}
+
+static void si_program_sstp(struct amdgpu_device *adev)
+{
+       WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT)));
+}
+
+static void si_enable_display_gap(struct amdgpu_device *adev)
+{
+       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 si_program_vc(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+       WREG32(CG_FTV, pi->vrc);
+}
+
+static void si_clear_vc(struct amdgpu_device *adev)
+{
+       WREG32(CG_FTV, 0);
+}
+
+static u8 si_get_ddr3_mclk_frequency_ratio(u32 memory_clock)
+{
+       u8 mc_para_index;
+
+       if (memory_clock < 10000)
+               mc_para_index = 0;
+       else if (memory_clock >= 80000)
+               mc_para_index = 0x0f;
+       else
+               mc_para_index = (u8)((memory_clock - 10000) / 5000 + 1);
+       return mc_para_index;
+}
+
+static u8 si_get_mclk_frequency_ratio(u32 memory_clock, bool strobe_mode)
+{
+       u8 mc_para_index;
+
+       if (strobe_mode) {
+               if (memory_clock < 12500)
+                       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);
+       }
+       return mc_para_index;
+}
+
+static u8 si_get_strobe_mode_settings(struct amdgpu_device *adev, u32 mclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       bool strobe_mode = false;
+       u8 result = 0;
+
+       if (mclk <= pi->mclk_strobe_mode_threshold)
+               strobe_mode = true;
+
+       if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+               result = si_get_mclk_frequency_ratio(mclk, strobe_mode);
+       else
+               result = si_get_ddr3_mclk_frequency_ratio(mclk);
+
+       if (strobe_mode)
+               result |= SISLANDS_SMC_STROBE_ENABLE;
+
+       return result;
+}
+
+static int si_upload_firmware(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       amdgpu_si_reset_smc(adev);
+       amdgpu_si_smc_clock(adev, false);
+
+       return amdgpu_si_load_smc_ucode(adev, si_pi->sram_end);
+}
+
+static bool si_validate_phase_shedding_tables(struct amdgpu_device *adev,
+                                             const struct atom_voltage_table *table,
+                                             const struct amdgpu_phase_shedding_limits_table *limits)
+{
+       u32 data, num_bits, num_levels;
+
+       if ((table == NULL) || (limits == NULL))
+               return false;
+
+       data = table->mask_low;
+
+       num_bits = hweight32(data);
+
+       if (num_bits == 0)
+               return false;
+
+       num_levels = (1 << num_bits);
+
+       if (table->count != num_levels)
+               return false;
+
+       if (limits->count != (num_levels - 1))
+               return false;
+
+       return true;
+}
+
+static void si_trim_voltage_table_to_fit_state_table(struct amdgpu_device *adev,
+                                             u32 max_voltage_steps,
+                                             struct atom_voltage_table *voltage_table)
+{
+       unsigned int i, diff;
+
+       if (voltage_table->count <= max_voltage_steps)
+               return;
+
+       diff = voltage_table->count - max_voltage_steps;
+
+       for (i= 0; i < max_voltage_steps; i++)
+               voltage_table->entries[i] = voltage_table->entries[i + diff];
+
+       voltage_table->count = max_voltage_steps;
+}
+
+static int si_get_svi2_voltage_table(struct amdgpu_device *adev,
+                                    struct amdgpu_clock_voltage_dependency_table *voltage_dependency_table,
+                                    struct atom_voltage_table *voltage_table)
+{
+       u32 i;
+
+       if (voltage_dependency_table == NULL)
+               return -EINVAL;
+
+       voltage_table->mask_low = 0;
+       voltage_table->phase_delay = 0;
+
+       voltage_table->count = voltage_dependency_table->count;
+       for (i = 0; i < voltage_table->count; i++) {
+               voltage_table->entries[i].value = voltage_dependency_table->entries[i].v;
+               voltage_table->entries[i].smio_low = 0;
+       }
+
+       return 0;
+}
+
+static int si_construct_voltage_tables(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int ret;
+
+       if (pi->voltage_control) {
+               ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddc_voltage_table);
+               if (ret)
+                       return ret;
+
+               if (eg_pi->vddc_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(adev,
+                                                                SISLANDS_MAX_NO_VREG_STEPS,
+                                                                &eg_pi->vddc_voltage_table);
+       } else if (si_pi->voltage_control_svi2) {
+               ret = si_get_svi2_voltage_table(adev,
+                                               &adev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
+                                               &eg_pi->vddc_voltage_table);
+               if (ret)
+                       return ret;
+       } else {
+               return -EINVAL;
+       }
+
+       if (eg_pi->vddci_control) {
+               ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDCI,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+
+               if (eg_pi->vddci_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(adev,
+                                                                SISLANDS_MAX_NO_VREG_STEPS,
+                                                                &eg_pi->vddci_voltage_table);
+       }
+       if (si_pi->vddci_control_svi2) {
+               ret = si_get_svi2_voltage_table(adev,
+                                               &adev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
+                                               &eg_pi->vddci_voltage_table);
+               if (ret)
+                       return ret;
+       }
+
+       if (pi->mvdd_control) {
+               ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_MVDDC,
+                                                   VOLTAGE_OBJ_GPIO_LUT, &si_pi->mvdd_voltage_table);
+
+               if (ret) {
+                       pi->mvdd_control = false;
+                       return ret;
+               }
+
+               if (si_pi->mvdd_voltage_table.count == 0) {
+                       pi->mvdd_control = false;
+                       return -EINVAL;
+               }
+
+               if (si_pi->mvdd_voltage_table.count > SISLANDS_MAX_NO_VREG_STEPS)
+                       si_trim_voltage_table_to_fit_state_table(adev,
+                                                                SISLANDS_MAX_NO_VREG_STEPS,
+                                                                &si_pi->mvdd_voltage_table);
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = amdgpu_atombios_get_voltage_table(adev, VOLTAGE_TYPE_VDDC,
+                                                   VOLTAGE_OBJ_PHASE_LUT, &si_pi->vddc_phase_shed_table);
+               if (ret)
+                       si_pi->vddc_phase_shed_control = false;
+
+               if ((si_pi->vddc_phase_shed_table.count == 0) ||
+                   (si_pi->vddc_phase_shed_table.count > SISLANDS_MAX_NO_VREG_STEPS))
+                       si_pi->vddc_phase_shed_control = false;
+       }
+
+       return 0;
+}
+
+static void si_populate_smc_voltage_table(struct amdgpu_device *adev,
+                                         const struct atom_voltage_table *voltage_table,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       unsigned int i;
+
+       for (i = 0; i < voltage_table->count; i++)
+               table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
+}
+
+static int si_populate_smc_voltage_tables(struct amdgpu_device *adev,
+                                         SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u8 i;
+
+       if (si_pi->voltage_control_svi2) {
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc,
+                       si_pi->svc_gpio_id);
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd,
+                       si_pi->svd_gpio_id);
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_svi_rework_plat_type,
+                                          2);
+       } else {
+               if (eg_pi->vddc_voltage_table.count) {
+                       si_populate_smc_voltage_table(adev, &eg_pi->vddc_voltage_table, table);
+                       table->voltageMaskTable.lowMask[SISLANDS_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) {
+                       si_populate_smc_voltage_table(adev, &eg_pi->vddci_voltage_table, table);
+
+                       table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDCI] =
+                               cpu_to_be32(eg_pi->vddci_voltage_table.mask_low);
+               }
+
+
+               if (si_pi->mvdd_voltage_table.count) {
+                       si_populate_smc_voltage_table(adev, &si_pi->mvdd_voltage_table, table);
+
+                       table->voltageMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_MVDD] =
+                               cpu_to_be32(si_pi->mvdd_voltage_table.mask_low);
+               }
+
+               if (si_pi->vddc_phase_shed_control) {
+                       if (si_validate_phase_shedding_tables(adev, &si_pi->vddc_phase_shed_table,
+                                                             &adev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
+                               si_populate_smc_voltage_table(adev, &si_pi->vddc_phase_shed_table, table);
+
+                               table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] =
+                                       cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
+
+                               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
+                                                          (u32)si_pi->vddc_phase_shed_table.phase_delay);
+                       } else {
+                               si_pi->vddc_phase_shed_control = false;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_voltage_value(struct amdgpu_device *adev,
+                                    const struct atom_voltage_table *table,
+                                    u16 value, SISLANDS_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 int si_populate_mvdd_value(struct amdgpu_device *adev, u32 mclk,
+                                 SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (pi->mvdd_control) {
+               if (mclk <= pi->mvdd_split_frequency)
+                       voltage->index = 0;
+               else
+                       voltage->index = (u8)(si_pi->mvdd_voltage_table.count) - 1;
+
+               voltage->value = cpu_to_be16(si_pi->mvdd_voltage_table.entries[voltage->index].value);
+       }
+       return 0;
+}
+
+static int si_get_std_voltage_value(struct amdgpu_device *adev,
+                                   SISLANDS_SMC_VOLTAGE_VALUE *voltage,
+                                   u16 *std_voltage)
+{
+       u16 v_index;
+       bool voltage_found = false;
+       *std_voltage = be16_to_cpu(voltage->value);
+
+       if (adev->pm.dpm.dyn_state.cac_leakage_table.entries) {
+               if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE) {
+                       if (adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries == NULL)
+                               return -EINVAL;
+
+                       for (v_index = 0; (u32)v_index < adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                               if (be16_to_cpu(voltage->value) ==
+                                   (u16)adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                                       voltage_found = true;
+                                       if ((u32)v_index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                               *std_voltage =
+                                                       adev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                       else
+                                               *std_voltage =
+                                                       adev->pm.dpm.dyn_state.cac_leakage_table.entries[adev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                       break;
+                               }
+                       }
+
+                       if (!voltage_found) {
+                               for (v_index = 0; (u32)v_index < adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.count; v_index++) {
+                                       if (be16_to_cpu(voltage->value) <=
+                                           (u16)adev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries[v_index].v) {
+                                               voltage_found = true;
+                                               if ((u32)v_index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+                                                       *std_voltage =
+                                                               adev->pm.dpm.dyn_state.cac_leakage_table.entries[v_index].vddc;
+                                               else
+                                                       *std_voltage =
+                                                               adev->pm.dpm.dyn_state.cac_leakage_table.entries[adev->pm.dpm.dyn_state.cac_leakage_table.count-1].vddc;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       if ((u32)voltage->index < adev->pm.dpm.dyn_state.cac_leakage_table.count)
+                               *std_voltage = adev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
+               }
+       }
+
+       return 0;
+}
+
+static int si_populate_std_voltage_value(struct amdgpu_device *adev,
+                                        u16 value, u8 index,
+                                        SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       voltage->index = index;
+       voltage->value = cpu_to_be16(value);
+
+       return 0;
+}
+
+static int si_populate_phase_shedding_value(struct amdgpu_device *adev,
+                                           const struct amdgpu_phase_shedding_limits_table *limits,
+                                           u16 voltage, u32 sclk, u32 mclk,
+                                           SISLANDS_SMC_VOLTAGE_VALUE *smc_voltage)
+{
+       unsigned int i;
+
+       for (i = 0; i < limits->count; i++) {
+               if ((voltage <= limits->entries[i].voltage) &&
+                   (sclk <= limits->entries[i].sclk) &&
+                   (mclk <= limits->entries[i].mclk))
+                       break;
+       }
+
+       smc_voltage->phase_settings = (u8)i;
+
+       return 0;
+}
+
+static int si_init_arb_table_index(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+       int ret;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev, si_pi->arb_table_start,
+                                           &tmp, si_pi->sram_end);
+       if (ret)
+               return ret;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= MC_CG_ARB_FREQ_F1 << 24;
+
+       return amdgpu_si_write_smc_sram_dword(adev, si_pi->arb_table_start,
+                                             tmp, si_pi->sram_end);
+}
+
+static int si_initial_switch_from_arb_f0_to_f1(struct amdgpu_device *adev)
+{
+       return ni_copy_and_switch_arb_sets(adev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int si_reset_to_default(struct amdgpu_device *adev)
+{
+       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_ResetToDefaults) == PPSMC_Result_OK) ?
+               0 : -EINVAL;
+}
+
+static int si_force_switch_to_arb_f0(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+       int ret;
+
+       ret = amdgpu_si_read_smc_sram_dword(adev, si_pi->arb_table_start,
+                                           &tmp, si_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(adev, tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static u32 si_calculate_memory_refresh_rate(struct amdgpu_device *adev,
+                                           u32 engine_clock)
+{
+       u32 dram_rows;
+       u32 dram_refresh_rate;
+       u32 mc_arb_rfsh_rate;
+       u32 tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
+
+       if (tmp >= 4)
+               dram_rows = 16384;
+       else
+               dram_rows = 1 << (tmp + 10);
+
+       dram_refresh_rate = 1 << ((RREG32(MC_SEQ_MISC0) & 0x3) + 3);
+       mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+
+       return mc_arb_rfsh_rate;
+}
+
+static int si_populate_memory_timing_parameters(struct amdgpu_device *adev,
+                                               struct rv7xx_pl *pl,
+                                               SMC_SIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
+       u32 dram_timing;
+       u32 dram_timing2;
+       u32 burst_time;
+
+       arb_regs->mc_arb_rfsh_rate =
+               (u8)si_calculate_memory_refresh_rate(adev, pl->sclk);
+
+       amdgpu_atombios_set_engine_dram_timings(adev,
+                                           pl->sclk,
+                                           pl->mclk);
+
+       dram_timing  = RREG32(MC_ARB_DRAM_TIMING);
+       dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
+       burst_time = RREG32(MC_ARB_BURST_TIME) & STATE0_MASK;
+
+       arb_regs->mc_arb_dram_timing  = cpu_to_be32(dram_timing);
+       arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
+       arb_regs->mc_arb_burst_time = (u8)burst_time;
+
+       return 0;
+}
+
+static int si_do_program_memory_timing_parameters(struct amdgpu_device *adev,
+                                                 struct amdgpu_ps *amdgpu_state,
+                                                 unsigned int first_arb_set)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct  si_ps *state = si_get_ps(amdgpu_state);
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int i, ret = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               ret = si_populate_memory_timing_parameters(adev, &state->performance_levels[i], &arb_regs);
+               if (ret)
+                       break;
+               ret = amdgpu_si_copy_bytes_to_smc(adev,
+                                                 si_pi->arb_table_start +
+                                                 offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                                 sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i),
+                                                 (u8 *)&arb_regs,
+                                                 sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                                 si_pi->sram_end);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int si_program_memory_timing_parameters(struct amdgpu_device *adev,
+                                              struct amdgpu_ps *amdgpu_new_state)
+{
+       return si_do_program_memory_timing_parameters(adev, amdgpu_new_state,
+                                                     SISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+
+static int si_populate_initial_mvdd_value(struct amdgpu_device *adev,
+                                         struct SISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (pi->mvdd_control)
+               return si_populate_voltage_value(adev, &si_pi->mvdd_voltage_table,
+                                                si_pi->mvdd_bootup_value, voltage);
+
+       return 0;
+}
+
+static int si_populate_smc_initial_state(struct amdgpu_device *adev,
+                                        struct amdgpu_ps *amdgpu_initial_state,
+                                        SISLANDS_SMC_STATETABLE *table)
+{
+       struct  si_ps *initial_state = si_get_ps(amdgpu_initial_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 reg;
+       int ret;
+
+       table->initialState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(si_pi->clock_registers.dll_cntl);
+       table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mclk_pwrmgt_cntl);
+       table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_ad_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_dq_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_1);
+       table->initialState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_func_cntl_2);
+       table->initialState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->initialState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_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(si_pi->clock_registers.cg_spll_func_cntl);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_2);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_3);
+       table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_func_cntl_4);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
+               cpu_to_be32(si_pi->clock_registers.cg_spll_spread_spectrum);
+       table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2  =
+               cpu_to_be32(si_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 =
+               SISLANDS_INITIAL_STATE_ARB_INDEX;
+
+       table->initialState.levels[0].ACIndex = 0;
+
+       ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+                                       initial_state->performance_levels[0].vddc,
+                                       &table->initialState.levels[0].vddc);
+
+       if (!ret) {
+               u16 std_vddc;
+
+               ret = si_get_std_voltage_value(adev,
+                                              &table->initialState.levels[0].vddc,
+                                              &std_vddc);
+               if (!ret)
+                       si_populate_std_voltage_value(adev, std_vddc,
+                                                     table->initialState.levels[0].vddc.index,
+                                                     &table->initialState.levels[0].std_vddc);
+       }
+
+       if (eg_pi->vddci_control)
+               si_populate_voltage_value(adev,
+                                         &eg_pi->vddci_voltage_table,
+                                         initial_state->performance_levels[0].vddci,
+                                         &table->initialState.levels[0].vddci);
+
+       if (si_pi->vddc_phase_shed_control)
+               si_populate_phase_shedding_value(adev,
+                                                &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                initial_state->performance_levels[0].vddc,
+                                                initial_state->performance_levels[0].sclk,
+                                                initial_state->performance_levels[0].mclk,
+                                                &table->initialState.levels[0].vddc);
+
+       si_populate_initial_mvdd_value(adev, &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);
+       table->initialState.levels[0].gen2PCIE = (u8)si_pi->boot_pcie_gen;
+
+       if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+               table->initialState.levels[0].strobeMode =
+                       si_get_strobe_mode_settings(adev,
+                                                   initial_state->performance_levels[0].mclk);
+
+               if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
+                       table->initialState.levels[0].mcFlags = SISLANDS_SMC_MC_EDC_RD_FLAG | SISLANDS_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;
+       table->initialState.levels[0].dpm2.PwrEfficiencyRatio = 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 si_populate_smc_acpi_state(struct amdgpu_device *adev,
+                                     SISLANDS_SMC_STATETABLE *table)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32 mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32 mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32 mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32 mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32 reg;
+       int ret;
+
+       table->ACPIState = table->initialState;
+
+       table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (pi->acpi_vddc) {
+               ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+                                               pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(adev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+                       if (!ret)
+                               si_populate_std_voltage_value(adev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = si_pi->acpi_pcie_gen;
+
+               if (si_pi->vddc_phase_shed_control) {
+                       si_populate_phase_shedding_value(adev,
+                                                        &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->acpi_vddc,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+               }
+       } else {
+               ret = si_populate_voltage_value(adev, &eg_pi->vddc_voltage_table,
+                                               pi->min_vddc_in_table, &table->ACPIState.levels[0].vddc);
+               if (!ret) {
+                       u16 std_vddc;
+
+                       ret = si_get_std_voltage_value(adev,
+                                                      &table->ACPIState.levels[0].vddc, &std_vddc);
+
+                       if (!ret)
+                               si_populate_std_voltage_value(adev, std_vddc,
+                                                             table->ACPIState.levels[0].vddc.index,
+                                                             &table->ACPIState.levels[0].std_vddc);
+               }
+               table->ACPIState.levels[0].gen2PCIE = (u8)r600_get_pcie_gen_support(adev,
+                                                                                   si_pi->sys_pcie_mask,
+                                                                                   si_pi->boot_pcie_gen,
+                                                                                   AMDGPU_PCIE_GEN1);
+
+               if (si_pi->vddc_phase_shed_control)
+                       si_populate_phase_shedding_value(adev,
+                                                        &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                        pi->min_vddc_in_table,
+                                                        0,
+                                                        0,
+                                                        &table->ACPIState.levels[0].vddc);
+       }
+
+       if (pi->acpi_vddc) {
+               if (eg_pi->acpi_vddci)
+                       si_populate_voltage_value(adev, &eg_pi->vddci_voltage_table,
+                                                 eg_pi->acpi_vddci,
+                                                 &table->ACPIState.levels[0].vddci);
+       }
+
+       mclk_pwrmgt_cntl |= MRDCK0_RESET | MRDCK1_RESET;
+       mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       dll_cntl &= ~(MRDCK0_BYPASS | MRDCK1_BYPASS);
+
+       spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
+       spll_func_cntl_2 |= SCLK_MUX_SEL(4);
+
+       table->ACPIState.levels[0].mclk.vDLL_CNTL =
+               cpu_to_be32(dll_cntl);
+       table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
+               cpu_to_be32(mclk_pwrmgt_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
+               cpu_to_be32(mpll_ad_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
+               cpu_to_be32(mpll_dq_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL =
+               cpu_to_be32(mpll_func_cntl);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_1 =
+               cpu_to_be32(mpll_func_cntl_1);
+       table->ACPIState.levels[0].mclk.vMPLL_FUNC_CNTL_2 =
+               cpu_to_be32(mpll_func_cntl_2);
+       table->ACPIState.levels[0].mclk.vMPLL_SS =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss1);
+       table->ACPIState.levels[0].mclk.vMPLL_SS2 =
+               cpu_to_be32(si_pi->clock_registers.mpll_ss2);
+
+       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].mclk.mclk_value = 0;
+       table->ACPIState.levels[0].sclk.sclk_value = 0;
+
+       si_populate_mvdd_value(adev, 0, &table->ACPIState.levels[0].mvdd);
+
+       if (eg_pi->dynamic_ac_timing)
+               table->ACPIState.levels[0].ACIndex = 0;
+
+       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;
+       table->ACPIState.levels[0].dpm2.PwrEfficiencyRatio = 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 si_populate_ulv_state(struct amdgpu_device *adev,
+                                SISLANDS_SMC_SWSTATE *state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       u32 sclk_in_sr = 1350; /* ??? */
+       int ret;
+
+       ret = si_convert_power_level_to_smc(adev, &ulv->pl,
+                                           &state->levels[0]);
+       if (!ret) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                       else
+                               state->levels[0].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+               }
+               if (ulv->one_pcie_lane_in_ulv)
+                       state->flags |= PPSMC_SWSTATE_FLAG_PCIE_X1;
+               state->levels[0].arbRefreshState = (u8)(SISLANDS_ULV_STATE_ARB_INDEX);
+               state->levels[0].ACIndex = 1;
+               state->levels[0].std_vddc = state->levels[0].vddc;
+               state->levelCount = 1;
+
+               state->flags |= PPSMC_SWSTATE_FLAG_DC;
+       }
+
+       return ret;
+}
+
+static int si_program_ulv_memory_timing_parameters(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
+       int ret;
+
+       ret = si_populate_memory_timing_parameters(adev, &ulv->pl,
+                                                  &arb_regs);
+       if (ret)
+               return ret;
+
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_ulv_volt_change_delay,
+                                  ulv->volt_change_delay);
+
+       ret = amdgpu_si_copy_bytes_to_smc(adev,
+                                         si_pi->arb_table_start +
+                                         offsetof(SMC_SIslands_MCArbDramTimingRegisters, data) +
+                                         sizeof(SMC_SIslands_MCArbDramTimingRegisterSet) * SISLANDS_ULV_STATE_ARB_INDEX,
+                                         (u8 *)&arb_regs,
+                                         sizeof(SMC_SIslands_MCArbDramTimingRegisterSet),
+                                         si_pi->sram_end);
+
+       return ret;
+}
+
+static void si_get_mvdd_configuration(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+
+       pi->mvdd_split_frequency = 30000;
+}
+
+static int si_init_smc_table(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct amdgpu_ps *amdgpu_boot_state = adev->pm.dpm.boot_ps;
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       SISLANDS_SMC_STATETABLE  *table = &si_pi->smc_statetable;
+       int ret;
+       u32 lane_width;
+       u32 vr_hot_gpio;
+
+       si_populate_smc_voltage_tables(adev, table);
+
+       switch (adev->pm.int_thermal_type) {
+       case THERMAL_TYPE_SI:
+       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 (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) {
+               if ((adev->pdev->device != 0x6818) && (adev->pdev->device != 0x6819))
+                       table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
+       }
+
+       if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+               table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REVERT_GPIO5_POLARITY)
+               table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH;
+
+       if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE) {
+               table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT_PROG_GPIO;
+               vr_hot_gpio = adev->pm.dpm.backbias_response_time;
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_vr_hot_gpio,
+                                          vr_hot_gpio);
+       }
+
+       ret = si_populate_smc_initial_state(adev, amdgpu_boot_state, table);
+       if (ret)
+               return ret;
+
+       ret = si_populate_smc_acpi_state(adev, table);
+       if (ret)
+               return ret;
+
+       table->driverState = table->initialState;
+
+       ret = si_do_program_memory_timing_parameters(adev, amdgpu_boot_state,
+                                                    SISLANDS_INITIAL_STATE_ARB_INDEX);
+       if (ret)
+               return ret;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               ret = si_populate_ulv_state(adev, &table->ULVState);
+               if (ret)
+                       return ret;
+
+               ret = si_program_ulv_memory_timing_parameters(adev);
+               if (ret)
+                       return ret;
+
+               WREG32(CG_ULV_CONTROL, ulv->cg_ulv_control);
+               WREG32(CG_ULV_PARAMETER, ulv->cg_ulv_parameter);
+
+               lane_width = amdgpu_get_pcie_lanes(adev);
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       } else {
+               table->ULVState = table->initialState;
+       }
+
+       return amdgpu_si_copy_bytes_to_smc(adev, si_pi->state_table_start,
+                                          (u8 *)table, sizeof(SISLANDS_SMC_STATETABLE),
+                                          si_pi->sram_end);
+}
+
+static int si_calculate_sclk_params(struct amdgpu_device *adev,
+                                   u32 engine_clock,
+                                   SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct atom_clock_dividers dividers;
+       u32 spll_func_cntl = si_pi->clock_registers.cg_spll_func_cntl;
+       u32 spll_func_cntl_2 = si_pi->clock_registers.cg_spll_func_cntl_2;
+       u32 spll_func_cntl_3 = si_pi->clock_registers.cg_spll_func_cntl_3;
+       u32 spll_func_cntl_4 = si_pi->clock_registers.cg_spll_func_cntl_4;
+       u32 cg_spll_spread_spectrum = si_pi->clock_registers.cg_spll_spread_spectrum;
+       u32 cg_spll_spread_spectrum_2 = si_pi->clock_registers.cg_spll_spread_spectrum_2;
+       u64 tmp;
+       u32 reference_clock = adev->clock.spll.reference_freq;
+       u32 reference_divider;
+       u32 fbdiv;
+       int ret;
+
+       ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+                                            engine_clock, false, &dividers);
+       if (ret)
+               return ret;
+
+       reference_divider = 1 + dividers.ref_div;
+
+       tmp = (u64) engine_clock * reference_divider * dividers.post_div * 16384;
+       do_div(tmp, reference_clock);
+       fbdiv = (u32) tmp;
+
+       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 amdgpu_atom_ss ss;
+               u32 vco_freq = engine_clock * dividers.post_div;
+
+               if (amdgpu_atombios_get_asic_ss_info(adev, &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 si_populate_sclk_value(struct amdgpu_device *adev,
+                                 u32 engine_clock,
+                                 SISLANDS_SMC_SCLK_VALUE *sclk)
+{
+       SISLANDS_SMC_SCLK_VALUE sclk_tmp;
+       int ret;
+
+       ret = si_calculate_sclk_params(adev, 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 si_populate_mclk_value(struct amdgpu_device *adev,
+                                 u32 engine_clock,
+                                 u32 memory_clock,
+                                 SISLANDS_SMC_MCLK_VALUE *mclk,
+                                 bool strobe_mode,
+                                 bool dll_state_on)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32  dll_cntl = si_pi->clock_registers.dll_cntl;
+       u32  mclk_pwrmgt_cntl = si_pi->clock_registers.mclk_pwrmgt_cntl;
+       u32  mpll_ad_func_cntl = si_pi->clock_registers.mpll_ad_func_cntl;
+       u32  mpll_dq_func_cntl = si_pi->clock_registers.mpll_dq_func_cntl;
+       u32  mpll_func_cntl = si_pi->clock_registers.mpll_func_cntl;
+       u32  mpll_func_cntl_1 = si_pi->clock_registers.mpll_func_cntl_1;
+       u32  mpll_func_cntl_2 = si_pi->clock_registers.mpll_func_cntl_2;
+       u32  mpll_ss1 = si_pi->clock_registers.mpll_ss1;
+       u32  mpll_ss2 = si_pi->clock_registers.mpll_ss2;
+       struct atom_mpll_param mpll_param;
+       int ret;
+
+       ret = amdgpu_atombios_get_memory_pll_dividers(adev, memory_clock, strobe_mode, &mpll_param);
+       if (ret)
+               return ret;
+
+       mpll_func_cntl &= ~BWCTRL_MASK;
+       mpll_func_cntl |= BWCTRL(mpll_param.bwcntl);
+
+       mpll_func_cntl_1 &= ~(CLKF_MASK | CLKFRAC_MASK | VCO_MODE_MASK);
+       mpll_func_cntl_1 |= CLKF(mpll_param.clkf) |
+               CLKFRAC(mpll_param.clkfrac) | VCO_MODE(mpll_param.vco_mode);
+
+       mpll_ad_func_cntl &= ~YCLK_POST_DIV_MASK;
+       mpll_ad_func_cntl |= YCLK_POST_DIV(mpll_param.post_div);
+
+       if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+               mpll_dq_func_cntl &= ~(YCLK_SEL_MASK | YCLK_POST_DIV_MASK);
+               mpll_dq_func_cntl |= YCLK_SEL(mpll_param.yclk_sel) |
+                       YCLK_POST_DIV(mpll_param.post_div);
+       }
+
+       if (pi->mclk_ss) {
+               struct amdgpu_atom_ss ss;
+               u32 freq_nom;
+               u32 tmp;
+               u32 reference_clock = adev->clock.mpll.reference_freq;
+
+               if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5)
+                       freq_nom = memory_clock * 4;
+               else
+                       freq_nom = memory_clock * 2;
+
+               tmp = freq_nom / reference_clock;
+               tmp = tmp * tmp;
+               if (amdgpu_atombios_get_asic_ss_info(adev, &ss,
+                                                    ASIC_INTERNAL_MEMORY_SS, freq_nom)) {
+                       u32 clks = reference_clock * 5 / ss.rate;
+                       u32 clkv = (u32)((((131 * ss.percentage * ss.rate) / 100) * tmp) / freq_nom);
+
+                       mpll_ss1 &= ~CLKV_MASK;
+                       mpll_ss1 |= CLKV(clkv);
+
+                       mpll_ss2 &= ~CLKS_MASK;
+                       mpll_ss2 |= CLKS(clks);
+               }
+       }
+
+       mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
+       mclk_pwrmgt_cntl |= DLL_SPEED(mpll_param.dll_speed);
+
+       if (dll_state_on)
+               mclk_pwrmgt_cntl |= MRDCK0_PDNB | MRDCK1_PDNB;
+       else
+               mclk_pwrmgt_cntl &= ~(MRDCK0_PDNB | MRDCK1_PDNB);
+
+       mclk->mclk_value = cpu_to_be32(memory_clock);
+       mclk->vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl);
+       mclk->vMPLL_FUNC_CNTL_1 = cpu_to_be32(mpll_func_cntl_1);
+       mclk->vMPLL_FUNC_CNTL_2 = cpu_to_be32(mpll_func_cntl_2);
+       mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
+       mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
+       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 si_populate_smc_sp(struct amdgpu_device *adev,
+                              struct amdgpu_ps *amdgpu_state,
+                              SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct  si_ps *ps = si_get_ps(amdgpu_state);
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       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 si_convert_power_level_to_smc(struct amdgpu_device *adev,
+                                        struct rv7xx_pl *pl,
+                                        SISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       int ret;
+       bool dll_state_on;
+       u16 std_vddc;
+       bool gmc_pg = false;
+
+       if (eg_pi->pcie_performance_request &&
+           (si_pi->force_pcie_gen != AMDGPU_PCIE_GEN_INVALID))
+               level->gen2PCIE = (u8)si_pi->force_pcie_gen;
+       else
+               level->gen2PCIE = (u8)pl->pcie_gen;
+
+       ret = si_populate_sclk_value(adev, 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 &&
+           (RREG32(DPG_PIPE_STUTTER_CONTROL) & STUTTER_ENABLE) &&
+           (adev->pm.dpm.new_active_crtc_count <= 2)) {
+               level->mcFlags |= SISLANDS_SMC_MC_STUTTER_EN;
+
+               if (gmc_pg)
+                       level->mcFlags |= SISLANDS_SMC_MC_PG_EN;
+       }
+
+       if (adev->mc.vram_type == AMDGPU_VRAM_TYPE_GDDR5) {
+               if (pl->mclk > pi->mclk_edc_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_RD_FLAG;
+
+               if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
+                       level->mcFlags |= SISLANDS_SMC_MC_EDC_WR_FLAG;
+
+               level->strobeMode = si_get_strobe_mode_settings(adev, pl->mclk);
+
+               if (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) {
+                       if (si_get_mclk_frequency_ratio(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;
+               }
+       } else {
+               level->strobeMode = si_get_strobe_mode_settings(adev,
+                                                               pl->mclk);
+
+               dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
+       }
+
+       ret = si_populate_mclk_value(adev,
+                                    pl->sclk,
+                                    pl->mclk,
+                                    &level->mclk,
+                                    (level->strobeMode & SISLANDS_SMC_STROBE_ENABLE) != 0, dll_state_on);
+       if (ret)
+               return ret;
+
+       ret = si_populate_voltage_value(adev,
+                                       &eg_pi->vddc_voltage_table,
+                                       pl->vddc, &level->vddc);
+       if (ret)
+               return ret;
+
+
+       ret = si_get_std_voltage_value(adev, &level->vddc, &std_vddc);
+       if (ret)
+               return ret;
+
+       ret = si_populate_std_voltage_value(adev, std_vddc,
+                                           level->vddc.index, &level->std_vddc);
+       if (ret)
+               return ret;
+
+       if (eg_pi->vddci_control) {
+               ret = si_populate_voltage_value(adev, &eg_pi->vddci_voltage_table,
+                                               pl->vddci, &level->vddci);
+               if (ret)
+                       return ret;
+       }
+
+       if (si_pi->vddc_phase_shed_control) {
+               ret = si_populate_phase_shedding_value(adev,
+                                                      &adev->pm.dpm.dyn_state.phase_shedding_limits_table,
+                                                      pl->vddc,
+                                                      pl->sclk,
+                                                      pl->mclk,
+                                                      &level->vddc);
+               if (ret)
+                       return ret;
+       }
+
+       level->MaxPoweredUpCU = si_pi->max_cu;
+
+       ret = si_populate_mvdd_value(adev, pl->mclk, &level->mvdd);
+
+       return ret;
+}
+
+static int si_populate_smc_t(struct amdgpu_device *adev,
+                            struct amdgpu_ps *amdgpu_state,
+                            SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct  si_ps *state = si_get_ps(amdgpu_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++) {
+               ret = r600_calculate_at(
+                       (50 / SISLANDS_MAX_HARDWARE_POWERLEVELS) * 100 * (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 si_disable_ulv(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported)
+               return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_DisableULV) == PPSMC_Result_OK) ?
+                       0 : -EINVAL;
+
+       return 0;
+}
+
+static bool si_is_state_ulv_compatible(struct amdgpu_device *adev,
+                                      struct amdgpu_ps *amdgpu_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(adev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+       const struct  si_ps *state = si_get_ps(amdgpu_state);
+       int i;
+
+       if (state->performance_levels[0].mclk != ulv->pl.mclk)
+               return false;
+
+       /* XXX validate against display requirements! */
+
+       for (i = 0; i < adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count; i++) {
+               if (adev->clock.current_dispclk <=
+                   adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].clk) {
+                       if (ulv->pl.vddc <
+                           adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[i].v)
+                               return false;
+               }
+       }
+
+       if ((amdgpu_state->vclk != 0) || (amdgpu_state->dclk != 0))
+               return false;
+
+       return true;
+}
+
+static int si_set_power_state_conditionally_enable_ulv(struct amdgpu_device *adev,
+                                                      struct amdgpu_ps *amdgpu_new_state)
+{
+       const struct si_power_info *si_pi = si_get_pi(adev);
+       const struct si_ulv_param *ulv = &si_pi->ulv;
+
+       if (ulv->supported) {
+               if (si_is_state_ulv_compatible(adev, amdgpu_new_state))
+                       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableULV) == PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+       }
+       return 0;
+}
+
+static int si_convert_power_state_to_smc(struct amdgpu_device *adev,
+                                        struct amdgpu_ps *amdgpu_state,
+                                        SISLANDS_SMC_SWSTATE *smc_state)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct ni_power_info *ni_pi = ni_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct  si_ps *state = si_get_ps(amdgpu_state);
+       int i, ret;
+       u32 threshold;
+       u32 sclk_in_sr = 1350; /* ??? */
+
+       if (state->performance_level_count > SISLANDS_MAX_HARDWARE_POWERLEVELS)
+               return -EINVAL;
+
+       threshold = state->performance_levels[state->performance_level_count-1].sclk * 100 / 100;
+
+       if (amdgpu_state->vclk && amdgpu_state->dclk) {
+               eg_pi->uvd_enabled = true;
+               if (eg_pi->smu_uvd_hs)
+                       smc_state->flags |= PPSMC_SWSTATE_FLAG_UVD;
+       } else {
+               eg_pi->uvd_enabled = false;
+       }
+
+       if (state->dc_compatible)
+               smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
+
+       smc_state->levelCount = 0;
+       for (i = 0; i < state->performance_level_count; i++) {
+               if (eg_pi->sclk_deep_sleep) {
+                       if ((i == 0) || si_pi->sclk_deep_sleep_above_low) {
+                               if (sclk_in_sr <= SCLK_MIN_DEEPSLEEP_FREQ)
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_BYPASS;
+                               else
+                                       smc_state->levels[i].stateFlags |= PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE;
+                       }
+               }
+
+               ret = si_convert_power_level_to_smc(adev, &state->performance_levels[i],
+                                                   &smc_state->levels[i]);
+               smc_state->levels[i].arbRefreshState =
+                       (u8)(SISLANDS_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 = SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
+               else
+                       smc_state->levels[i].ACIndex = 0;
+
+               smc_state->levelCount++;
+       }
+
+       si_write_smc_soft_register(adev,
+                                  SI_SMC_SOFT_REGISTER_watermark_threshold,
+                                  threshold / 512);
+
+       si_populate_smc_sp(adev, amdgpu_state, smc_state);
+
+       ret = si_populate_power_containment_values(adev, amdgpu_state, smc_state);
+       if (ret)
+               ni_pi->enable_power_containment = false;
+
+       ret = si_populate_sq_ramping_values(adev, amdgpu_state, smc_state);
+       if (ret)
+               ni_pi->enable_sq_ramping = false;
+
+       return si_populate_smc_t(adev, amdgpu_state, smc_state);
+}
+
+static int si_upload_sw_state(struct amdgpu_device *adev,
+                             struct amdgpu_ps *amdgpu_new_state)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct  si_ps *new_state = si_get_ps(amdgpu_new_state);
+       int ret;
+       u32 address = si_pi->state_table_start +
+               offsetof(SISLANDS_SMC_STATETABLE, driverState);
+       u32 state_size = sizeof(SISLANDS_SMC_SWSTATE) +
+               ((new_state->performance_level_count - 1) *
+                sizeof(SISLANDS_SMC_HW_PERFORMANCE_LEVEL));
+       SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.driverState;
+
+       memset(smc_state, 0, state_size);
+
+       ret = si_convert_power_state_to_smc(adev, amdgpu_new_state, smc_state);
+       if (ret)
+               return ret;
+
+       return amdgpu_si_copy_bytes_to_smc(adev, address, (u8 *)smc_state,
+                                          state_size, si_pi->sram_end);
+}
+
+static int si_upload_ulv_state(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       int ret = 0;
+
+       if (ulv->supported && ulv->pl.vddc) {
+               u32 address = si_pi->state_table_start +
+                       offsetof(SISLANDS_SMC_STATETABLE, ULVState);
+               SISLANDS_SMC_SWSTATE *smc_state = &si_pi->smc_statetable.ULVState;
+               u32 state_size = sizeof(SISLANDS_SMC_SWSTATE);
+
+               memset(smc_state, 0, state_size);
+
+               ret = si_populate_ulv_state(adev, smc_state);
+               if (!ret)
+                       ret = amdgpu_si_copy_bytes_to_smc(adev, address, (u8 *)smc_state,
+                                                         state_size, si_pi->sram_end);
+       }
+
+       return ret;
+}
+
+static int si_upload_smc_data(struct amdgpu_device *adev)
+{
+       struct amdgpu_crtc *amdgpu_crtc = NULL;
+       int i;
+
+       if (adev->pm.dpm.new_active_crtc_count == 0)
+               return 0;
+
+       for (i = 0; i < adev->mode_info.num_crtc; i++) {
+               if (adev->pm.dpm.new_active_crtcs & (1 << i)) {
+                       amdgpu_crtc = adev->mode_info.crtcs[i];
+                       break;
+               }
+       }
+
+       if (amdgpu_crtc == NULL)
+               return 0;
+
+       if (amdgpu_crtc->line_time <= 0)
+               return 0;
+
+       if (si_write_smc_soft_register(adev,
+                                      SI_SMC_SOFT_REGISTER_crtc_index,
+                                      amdgpu_crtc->crtc_id) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(adev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min,
+                                      amdgpu_crtc->wm_high / amdgpu_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       if (si_write_smc_soft_register(adev,
+                                      SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max,
+                                      amdgpu_crtc->wm_low / amdgpu_crtc->line_time) != PPSMC_Result_OK)
+               return 0;
+
+       return 0;
+}
+
+static int si_set_mc_special_registers(struct amdgpu_device *adev,
+                                      struct si_mc_reg_table *table)
+{
+       u8 i, j, k;
+       u32 temp_reg;
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                       return -EINVAL;
+               switch (table->mc_reg_address[i].s1) {
+               case MC_SEQ_MISC1:
+                       temp_reg = RREG32(MC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP;
+                       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_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       temp_reg = RREG32(MC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP;
+                       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 (adev->mc.vram_type != AMDGPU_VRAM_TYPE_GDDR5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+
+                       if (adev->mc.vram_type != AMDGPU_VRAM_TYPE_GDDR5) {
+                               table->mc_reg_address[j].s1 = MC_PMG_AUTO_CMD;
+                               table->mc_reg_address[j].s0 = MC_PMG_AUTO_CMD;
+                               for (k = 0; k < table->num_entries; k++)
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               j++;
+                               if (j >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                                       return -EINVAL;
+                       }
+                       break;
+               case MC_SEQ_RESERVE_M:
+                       temp_reg = RREG32(MC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1;
+                       table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP;
+                       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_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               return -EINVAL;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static bool si_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg)
+{
+       bool result = true;
+       switch (in_reg) {
+       case  MC_SEQ_RAS_TIMING:
+               *out_reg = MC_SEQ_RAS_TIMING_LP;
+               break;
+       case MC_SEQ_CAS_TIMING:
+               *out_reg = MC_SEQ_CAS_TIMING_LP;
+               break;
+       case MC_SEQ_MISC_TIMING:
+               *out_reg = MC_SEQ_MISC_TIMING_LP;
+               break;
+       case MC_SEQ_MISC_TIMING2:
+               *out_reg = MC_SEQ_MISC_TIMING2_LP;
+               break;
+       case MC_SEQ_RD_CTL_D0:
+               *out_reg = MC_SEQ_RD_CTL_D0_LP;
+               break;
+       case MC_SEQ_RD_CTL_D1:
+               *out_reg = MC_SEQ_RD_CTL_D1_LP;
+               break;
+       case MC_SEQ_WR_CTL_D0:
+               *out_reg = MC_SEQ_WR_CTL_D0_LP;
+               break;
+       case MC_SEQ_WR_CTL_D1:
+               *out_reg = MC_SEQ_WR_CTL_D1_LP;
+               break;
+       case MC_PMG_CMD_EMRS:
+               *out_reg = MC_SEQ_PMG_CMD_EMRS_LP;
+               break;
+       case MC_PMG_CMD_MRS:
+               *out_reg = MC_SEQ_PMG_CMD_MRS_LP;
+               break;
+       case MC_PMG_CMD_MRS1:
+               *out_reg = MC_SEQ_PMG_CMD_MRS1_LP;
+               break;
+       case MC_SEQ_PMG_TIMING:
+               *out_reg = MC_SEQ_PMG_TIMING_LP;
+               break;
+       case MC_PMG_CMD_MRS2:
+               *out_reg = MC_SEQ_PMG_CMD_MRS2_LP;
+               break;
+       case MC_SEQ_WR_CTL_2:
+               *out_reg = MC_SEQ_WR_CTL_2_LP;
+               break;
+       default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static void si_set_valid_flag(struct si_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 si_set_s0_mc_reg_index(struct si_mc_reg_table *table)
+{
+       u32 i;
+       u16 address;
+
+       for (i = 0; i < table->last; i++)
+               table->mc_reg_address[i].s0 = si_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
+                       address : table->mc_reg_address[i].s1;
+
+}
+
+static int si_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
+                                     struct si_mc_reg_table *si_table)
+{
+       u8 i, j;
+
+       if (table->last > SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+               return -EINVAL;
+       if (table->num_entries > MAX_AC_TIMING_ENTRIES)
+               return -EINVAL;
+
+       for (i = 0; i < table->last; i++)
+               si_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
+       si_table->last = table->last;
+
+       for (i = 0; i < table->num_entries; i++) {
+               si_table->mc_reg_table_entry[i].mclk_max =
+                       table->mc_reg_table_entry[i].mclk_max;
+               for (j = 0; j < table->last; j++) {
+                       si_table->mc_reg_table_entry[i].mc_data[j] =
+                               table->mc_reg_table_entry[i].mc_data[j];
+               }
+       }
+       si_table->num_entries = table->num_entries;
+
+       return 0;
+}
+
+static int si_initialize_mc_reg_table(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct atom_mc_reg_table *table;
+       struct si_mc_reg_table *si_table = &si_pi->mc_reg_table;
+       u8 module_index = rv770_get_memory_module_index(adev);
+       int ret;
+
+       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));
+       WREG32(MC_SEQ_WR_CTL_2_LP, RREG32(MC_SEQ_WR_CTL_2));
+
+       ret = amdgpu_atombios_init_mc_reg_table(adev, module_index, table);
+       if (ret)
+               goto init_mc_done;
+
+       ret = si_copy_vbios_mc_reg_table(table, si_table);
+       if (ret)
+               goto init_mc_done;
+
+       si_set_s0_mc_reg_index(si_table);
+
+       ret = si_set_mc_special_registers(adev, si_table);
+       if (ret)
+               goto init_mc_done;
+
+       si_set_valid_flag(si_table);
+
+init_mc_done:
+       kfree(table);
+
+       return ret;
+
+}
+
+static void si_populate_mc_reg_addresses(struct amdgpu_device *adev,
+                                        SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 i, j;
+
+       for (i = 0, j = 0; j < si_pi->mc_reg_table.last; j++) {
+               if (si_pi->mc_reg_table.valid_flag & (1 << j)) {
+                       if (i >= SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE)
+                               break;
+                       mc_reg_table->address[i].s0 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               cpu_to_be16(si_pi->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+       mc_reg_table->last = (u8)i;
+}
+
+static void si_convert_mc_registers(const struct si_mc_reg_entry *entry,
+                                   SMC_SIslands_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 si_convert_mc_reg_table_entry_to_smc(struct amdgpu_device *adev,
+                                                struct rv7xx_pl *pl,
+                                                SMC_SIslands_MCRegisterSet *mc_reg_table_data)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 i = 0;
+
+       for (i = 0; i < si_pi->mc_reg_table.num_entries; i++) {
+               if (pl->mclk <= si_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
+                       break;
+       }
+
+       if ((i == si_pi->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+}
+
+static void si_convert_mc_reg_table_to_smc(struct amdgpu_device *adev,
+                                          struct amdgpu_ps *amdgpu_state,
+                                          SMC_SIslands_MCRegisters *mc_reg_table)
+{
+       struct si_ps *state = si_get_ps(amdgpu_state);
+       int i;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               si_convert_mc_reg_table_entry_to_smc(adev,
+                                                    &state->performance_levels[i],
+                                                    &mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
+       }
+}
+
+static int si_populate_mc_reg_table(struct amdgpu_device *adev,
+                                   struct amdgpu_ps *amdgpu_boot_state)
+{
+       struct  si_ps *boot_state = si_get_ps(amdgpu_boot_state);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct si_ulv_param *ulv = &si_pi->ulv;
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_seq_index, 1);
+
+       si_populate_mc_reg_addresses(adev, smc_mc_reg_table);
+
+       si_convert_mc_reg_table_entry_to_smc(adev, &boot_state->performance_levels[0],
+                                            &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_INITIAL_SLOT]);
+
+       si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                               &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ACPI_SLOT],
+                               si_pi->mc_reg_table.last,
+                               si_pi->mc_reg_table.valid_flag);
+
+       if (ulv->supported && ulv->pl.vddc != 0)
+               si_convert_mc_reg_table_entry_to_smc(adev, &ulv->pl,
+                                                    &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT]);
+       else
+               si_convert_mc_registers(&si_pi->mc_reg_table.mc_reg_table_entry[0],
+                                       &smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_ULV_SLOT],
+                                       si_pi->mc_reg_table.last,
+                                       si_pi->mc_reg_table.valid_flag);
+
+       si_convert_mc_reg_table_to_smc(adev, amdgpu_boot_state, smc_mc_reg_table);
+
+       return amdgpu_si_copy_bytes_to_smc(adev, si_pi->mc_reg_table_start,
+                                          (u8 *)smc_mc_reg_table,
+                                          sizeof(SMC_SIslands_MCRegisters), si_pi->sram_end);
+}
+
+static int si_upload_mc_reg_table(struct amdgpu_device *adev,
+                                 struct amdgpu_ps *amdgpu_new_state)
+{
+       struct si_ps *new_state = si_get_ps(amdgpu_new_state);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 address = si_pi->mc_reg_table_start +
+               offsetof(SMC_SIslands_MCRegisters,
+                        data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
+       SMC_SIslands_MCRegisters *smc_mc_reg_table = &si_pi->smc_mc_reg_table;
+
+       memset(smc_mc_reg_table, 0, sizeof(SMC_SIslands_MCRegisters));
+
+       si_convert_mc_reg_table_to_smc(adev, amdgpu_new_state, smc_mc_reg_table);
+
+       return amdgpu_si_copy_bytes_to_smc(adev, address,
+                                          (u8 *)&smc_mc_reg_table->data[SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
+                                          sizeof(SMC_SIslands_MCRegisterSet) * new_state->performance_level_count,
+                                          si_pi->sram_end);
+}
+
+static void si_enable_voltage_control(struct amdgpu_device *adev, bool enable)
+{
+       if (enable)
+               WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
+       else
+               WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+
+static enum amdgpu_pcie_gen si_get_maximum_link_speed(struct amdgpu_device *adev,
+                                                     struct amdgpu_ps *amdgpu_state)
+{
+       struct si_ps *state = si_get_ps(amdgpu_state);
+       int i;
+       u16 pcie_speed, max_speed = 0;
+
+       for (i = 0; i < state->performance_level_count; i++) {
+               pcie_speed = state->performance_levels[i].pcie_gen;
+               if (max_speed < pcie_speed)
+                       max_speed = pcie_speed;
+       }
+       return max_speed;
+}
+
+static u16 si_get_current_pcie_speed(struct amdgpu_device *adev)
+{
+       u32 speed_cntl;
+
+       speed_cntl = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE_MASK;
+       speed_cntl >>= LC_CURRENT_DATA_RATE_SHIFT;
+
+       return (u16)speed_cntl;
+}
+
+static void si_request_link_speed_change_before_state_change(struct amdgpu_device *adev,
+                                                            struct amdgpu_ps *amdgpu_new_state,
+                                                            struct amdgpu_ps *amdgpu_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       enum amdgpu_pcie_gen target_link_speed = si_get_maximum_link_speed(adev, amdgpu_new_state);
+       enum amdgpu_pcie_gen current_link_speed;
+
+       if (si_pi->force_pcie_gen == AMDGPU_PCIE_GEN_INVALID)
+               current_link_speed = si_get_maximum_link_speed(adev, amdgpu_current_state);
+       else
+               current_link_speed = si_pi->force_pcie_gen;
+
+       si_pi->force_pcie_gen = AMDGPU_PCIE_GEN_INVALID;
+       si_pi->pspp_notify_required = false;
+       if (target_link_speed > current_link_speed) {
+               switch (target_link_speed) {
+#if defined(CONFIG_ACPI)
+               case AMDGPU_PCIE_GEN3:
+                       if (amdgpu_acpi_pcie_performance_request(adev, PCIE_PERF_REQ_PECI_GEN3, false) == 0)
+                               break;
+                       si_pi->force_pcie_gen = AMDGPU_PCIE_GEN2;
+                       if (current_link_speed == AMDGPU_PCIE_GEN2)
+                               break;
+               case AMDGPU_PCIE_GEN2:
+                       if (amdgpu_acpi_pcie_performance_request(adev, PCIE_PERF_REQ_PECI_GEN2, false) == 0)
+                               break;
+#endif
+               default:
+                       si_pi->force_pcie_gen = si_get_current_pcie_speed(adev);
+                       break;
+               }
+       } else {
+               if (target_link_speed < current_link_speed)
+                       si_pi->pspp_notify_required = true;
+       }
+}
+
+static void si_notify_link_speed_change_after_state_change(struct amdgpu_device *adev,
+                                                          struct amdgpu_ps *amdgpu_new_state,
+                                                          struct amdgpu_ps *amdgpu_current_state)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       enum amdgpu_pcie_gen target_link_speed = si_get_maximum_link_speed(adev, amdgpu_new_state);
+       u8 request;
+
+       if (si_pi->pspp_notify_required) {
+               if (target_link_speed == AMDGPU_PCIE_GEN3)
+                       request = PCIE_PERF_REQ_PECI_GEN3;
+               else if (target_link_speed == AMDGPU_PCIE_GEN2)
+                       request = PCIE_PERF_REQ_PECI_GEN2;
+               else
+                       request = PCIE_PERF_REQ_PECI_GEN1;
+
+               if ((request == PCIE_PERF_REQ_PECI_GEN1) &&
+                   (si_get_current_pcie_speed(adev) > 0))
+                       return;
+
+#if defined(CONFIG_ACPI)
+               amdgpu_acpi_pcie_performance_request(adev, request, false);
+#endif
+       }
+}
+
+#if 0
+static int si_ds_request(struct amdgpu_device *adev,
+                        bool ds_status_on, u32 count_write)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+
+       if (eg_pi->sclk_deep_sleep) {
+               if (ds_status_on)
+                       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_CancelThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ?
+                               0 : -EINVAL;
+               else
+                       return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_ThrottleOVRDSCLKDS) ==
+                               PPSMC_Result_OK) ? 0 : -EINVAL;
+       }
+       return 0;
+}
+#endif
+
+static void si_set_max_cu_value(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+
+       if (adev->asic_type == CHIP_VERDE) {
+               switch (adev->pdev->device) {
+               case 0x6820:
+               case 0x6825:
+               case 0x6821:
+               case 0x6823:
+               case 0x6827:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x682D:
+               case 0x6824:
+               case 0x682F:
+               case 0x6826:
+                       si_pi->max_cu = 8;
+                       break;
+               case 0x6828:
+               case 0x6830:
+               case 0x6831:
+               case 0x6838:
+               case 0x6839:
+               case 0x683D:
+                       si_pi->max_cu = 10;
+                       break;
+               case 0x683B:
+               case 0x683F:
+               case 0x6829:
+                       si_pi->max_cu = 8;
+                       break;
+               default:
+                       si_pi->max_cu = 0;
+                       break;
+               }
+       } else {
+               si_pi->max_cu = 0;
+       }
+}
+
+static int si_patch_single_dependency_table_based_on_leakage(struct amdgpu_device *adev,
+                                                            struct amdgpu_clock_voltage_dependency_table *table)
+{
+       u32 i;
+       int j;
+       u16 leakage_voltage;
+
+       if (table) {
+               for (i = 0; i < table->count; i++) {
+                       switch (si_get_leakage_voltage_from_leakage_index(adev,
+                                                                         table->entries[i].v,
+                                                                         &leakage_voltage)) {
+                       case 0:
+                               table->entries[i].v = leakage_voltage;
+                               break;
+                       case -EAGAIN:
+                               return -EINVAL;
+                       case -EINVAL:
+                       default:
+                               break;
+                       }
+               }
+
+               for (j = (table->count - 2); j >= 0; j--) {
+                       table->entries[j].v = (table->entries[j].v <= table->entries[j + 1].v) ?
+                               table->entries[j].v : table->entries[j + 1].v;
+               }
+       }
+       return 0;
+}
+
+static int si_patch_dependency_tables_based_on_leakage(struct amdgpu_device *adev)
+{
+       int ret = 0;
+
+       ret = si_patch_single_dependency_table_based_on_leakage(adev,
+                                                               &adev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
+       if (ret)
+               DRM_ERROR("Could not patch vddc_on_sclk leakage table\n");
+       ret = si_patch_single_dependency_table_based_on_leakage(adev,
+                                                               &adev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
+       if (ret)
+               DRM_ERROR("Could not patch vddc_on_mclk leakage table\n");
+       ret = si_patch_single_dependency_table_based_on_leakage(adev,
+                                                               &adev->pm.dpm.dyn_state.vddci_dependency_on_mclk);
+       if (ret)
+               DRM_ERROR("Could not patch vddci_on_mclk leakage table\n");
+       return ret;
+}
+
+static void si_set_pcie_lane_width_in_smc(struct amdgpu_device *adev,
+                                         struct amdgpu_ps *amdgpu_new_state,
+                                         struct amdgpu_ps *amdgpu_current_state)
+{
+       u32 lane_width;
+       u32 new_lane_width =
+               (amdgpu_new_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+       u32 current_lane_width =
+               (amdgpu_current_state->caps & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
+
+       if (new_lane_width != current_lane_width) {
+               amdgpu_set_pcie_lanes(adev, new_lane_width);
+               lane_width = amdgpu_get_pcie_lanes(adev);
+               si_write_smc_soft_register(adev, SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width, lane_width);
+       }
+}
+
+static void si_dpm_setup_asic(struct amdgpu_device *adev)
+{
+       si_read_clock_registers(adev);
+       si_enable_acpi_power_management(adev);
+}
+
+static int si_thermal_enable_alert(struct amdgpu_device *adev,
+                                  bool enable)
+{
+       u32 thermal_int = RREG32(CG_THERMAL_INT);
+
+       if (enable) {
+               PPSMC_Result result;
+
+               thermal_int &= ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
+               WREG32(CG_THERMAL_INT, thermal_int);
+               result = amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_EnableThermalInterrupt);
+               if (result != PPSMC_Result_OK) {
+                       DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
+                       return -EINVAL;
+               }
+       } else {
+               thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
+               WREG32(CG_THERMAL_INT, thermal_int);
+       }
+
+       return 0;
+}
+
+static int si_thermal_set_temperature_range(struct amdgpu_device *adev,
+                                           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);
+
+       adev->pm.dpm.thermal.min_temp = low_temp;
+       adev->pm.dpm.thermal.max_temp = high_temp;
+
+       return 0;
+}
+
+static void si_fan_ctrl_set_static_mode(struct amdgpu_device *adev, u32 mode)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+
+       if (si_pi->fan_ctrl_is_in_default_mode) {
+               tmp = (RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK) >> FDO_PWM_MODE_SHIFT;
+               si_pi->fan_ctrl_default_mode = tmp;
+               tmp = (RREG32(CG_FDO_CTRL2) & TMIN_MASK) >> TMIN_SHIFT;
+               si_pi->t_min = tmp;
+               si_pi->fan_ctrl_is_in_default_mode = false;
+       }
+
+       tmp = RREG32(CG_FDO_CTRL2) & ~TMIN_MASK;
+       tmp |= TMIN(0);
+       WREG32(CG_FDO_CTRL2, tmp);
+
+       tmp = RREG32(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
+       tmp |= FDO_PWM_MODE(mode);
+       WREG32(CG_FDO_CTRL2, tmp);
+}
+
+static int si_thermal_setup_fan_table(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       PP_SIslands_FanTable fan_table = { FDO_MODE_HARDWARE };
+       u32 duty100;
+       u32 t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       u16 fdo_min, slope1, slope2;
+       u32 reference_clock, tmp;
+       int ret;
+       u64 tmp64;
+
+       if (!si_pi->fan_table_start) {
+               adev->pm.dpm.fan.ucode_fan_control = false;
+               return 0;
+       }
+
+       duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+       if (duty100 == 0) {
+               adev->pm.dpm.fan.ucode_fan_control = false;
+               return 0;
+       }
+
+       tmp64 = (u64)adev->pm.dpm.fan.pwm_min * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (u16)tmp64;
+
+       t_diff1 = adev->pm.dpm.fan.t_med - adev->pm.dpm.fan.t_min;
+       t_diff2 = adev->pm.dpm.fan.t_high - adev->pm.dpm.fan.t_med;
+
+       pwm_diff1 = adev->pm.dpm.fan.pwm_med - adev->pm.dpm.fan.pwm_min;
+       pwm_diff2 = adev->pm.dpm.fan.pwm_high - adev->pm.dpm.fan.pwm_med;
+
+       slope1 = (u16)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (u16)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.temp_min = cpu_to_be16((50 + adev->pm.dpm.fan.t_min) / 100);
+       fan_table.temp_med = cpu_to_be16((50 + adev->pm.dpm.fan.t_med) / 100);
+       fan_table.temp_max = cpu_to_be16((50 + adev->pm.dpm.fan.t_max) / 100);
+       fan_table.slope1 = cpu_to_be16(slope1);
+       fan_table.slope2 = cpu_to_be16(slope2);
+       fan_table.fdo_min = cpu_to_be16(fdo_min);
+       fan_table.hys_down = cpu_to_be16(adev->pm.dpm.fan.t_hyst);
+       fan_table.hys_up = cpu_to_be16(1);
+       fan_table.hys_slope = cpu_to_be16(1);
+       fan_table.temp_resp_lim = cpu_to_be16(5);
+       reference_clock = amdgpu_asic_get_xclk(adev);
+
+       fan_table.refresh_period = cpu_to_be32((adev->pm.dpm.fan.cycle_delay *
+                                               reference_clock) / 1600);
+       fan_table.fdo_max = cpu_to_be16((u16)duty100);
+
+       tmp = (RREG32(CG_MULT_THERMAL_CTRL) & TEMP_SEL_MASK) >> TEMP_SEL_SHIFT;
+       fan_table.temp_src = (uint8_t)tmp;
+
+       ret = amdgpu_si_copy_bytes_to_smc(adev,
+                                         si_pi->fan_table_start,
+                                         (u8 *)(&fan_table),
+                                         sizeof(fan_table),
+                                         si_pi->sram_end);
+
+       if (ret) {
+               DRM_ERROR("Failed to load fan table to the SMC.");
+               adev->pm.dpm.fan.ucode_fan_control = false;
+       }
+
+       return ret;
+}
+
+static int si_fan_ctrl_start_smc_fan_control(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       PPSMC_Result ret;
+
+       ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_StartFanControl);
+       if (ret == PPSMC_Result_OK) {
+               si_pi->fan_is_controlled_by_smc = true;
+               return 0;
+       } else {
+               return -EINVAL;
+       }
+}
+
+static int si_fan_ctrl_stop_smc_fan_control(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       PPSMC_Result ret;
+
+       ret = amdgpu_si_send_msg_to_smc(adev, PPSMC_StopFanControl);
+
+       if (ret == PPSMC_Result_OK) {
+               si_pi->fan_is_controlled_by_smc = false;
+               return 0;
+       } else {
+               return -EINVAL;
+       }
+}
+
+static int si_dpm_get_fan_speed_percent(struct amdgpu_device *adev,
+                                     u32 *speed)
+{
+       u32 duty, duty100;
+       u64 tmp64;
+
+       if (adev->pm.no_fan)
+               return -ENOENT;
+
+       duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+       duty = (RREG32(CG_THERMAL_STATUS) & FDO_PWM_DUTY_MASK) >> FDO_PWM_DUTY_SHIFT;
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+       tmp64 = (u64)duty * 100;
+       do_div(tmp64, duty100);
+       *speed = (u32)tmp64;
+
+       if (*speed > 100)
+               *speed = 100;
+
+       return 0;
+}
+
+static int si_dpm_set_fan_speed_percent(struct amdgpu_device *adev,
+                                     u32 speed)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+       u32 duty, duty100;
+       u64 tmp64;
+
+       if (adev->pm.no_fan)
+               return -ENOENT;
+
+       if (si_pi->fan_is_controlled_by_smc)
+               return -EINVAL;
+
+       if (speed > 100)
+               return -EINVAL;
+
+       duty100 = (RREG32(CG_FDO_CTRL1) & FMAX_DUTY100_MASK) >> FMAX_DUTY100_SHIFT;
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+       tmp64 = (u64)speed * duty100;
+       do_div(tmp64, 100);
+       duty = (u32)tmp64;
+
+       tmp = RREG32(CG_FDO_CTRL0) & ~FDO_STATIC_DUTY_MASK;
+       tmp |= FDO_STATIC_DUTY(duty);
+       WREG32(CG_FDO_CTRL0, tmp);
+
+       return 0;
+}
+
+static void si_dpm_set_fan_control_mode(struct amdgpu_device *adev, u32 mode)
+{
+       if (mode) {
+               /* stop auto-manage */
+               if (adev->pm.dpm.fan.ucode_fan_control)
+                       si_fan_ctrl_stop_smc_fan_control(adev);
+               si_fan_ctrl_set_static_mode(adev, mode);
+       } else {
+               /* restart auto-manage */
+               if (adev->pm.dpm.fan.ucode_fan_control)
+                       si_thermal_start_smc_fan_control(adev);
+               else
+                       si_fan_ctrl_set_default_mode(adev);
+       }
+}
+
+static u32 si_dpm_get_fan_control_mode(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+
+       if (si_pi->fan_is_controlled_by_smc)
+               return 0;
+
+       tmp = RREG32(CG_FDO_CTRL2) & FDO_PWM_MODE_MASK;
+       return (tmp >> FDO_PWM_MODE_SHIFT);
+}
+
+#if 0
+static int si_fan_ctrl_get_fan_speed_rpm(struct amdgpu_device *adev,
+                                        u32 *speed)
+{
+       u32 tach_period;
+       u32 xclk = amdgpu_asic_get_xclk(adev);
+
+       if (adev->pm.no_fan)
+               return -ENOENT;
+
+       if (adev->pm.fan_pulses_per_revolution == 0)
+               return -ENOENT;
+
+       tach_period = (RREG32(CG_TACH_STATUS) & TACH_PERIOD_MASK) >> TACH_PERIOD_SHIFT;
+       if (tach_period == 0)
+               return -ENOENT;
+
+       *speed = 60 * xclk * 10000 / tach_period;
+
+       return 0;
+}
+
+static int si_fan_ctrl_set_fan_speed_rpm(struct amdgpu_device *adev,
+                                        u32 speed)
+{
+       u32 tach_period, tmp;
+       u32 xclk = amdgpu_asic_get_xclk(adev);
+
+       if (adev->pm.no_fan)
+               return -ENOENT;
+
+       if (adev->pm.fan_pulses_per_revolution == 0)
+               return -ENOENT;
+
+       if ((speed < adev->pm.fan_min_rpm) ||
+           (speed > adev->pm.fan_max_rpm))
+               return -EINVAL;
+
+       if (adev->pm.dpm.fan.ucode_fan_control)
+               si_fan_ctrl_stop_smc_fan_control(adev);
+
+       tach_period = 60 * xclk * 10000 / (8 * speed);
+       tmp = RREG32(CG_TACH_CTRL) & ~TARGET_PERIOD_MASK;
+       tmp |= TARGET_PERIOD(tach_period);
+       WREG32(CG_TACH_CTRL, tmp);
+
+       si_fan_ctrl_set_static_mode(adev, FDO_PWM_MODE_STATIC_RPM);
+
+       return 0;
+}
+#endif
+
+static void si_fan_ctrl_set_default_mode(struct amdgpu_device *adev)
+{
+       struct si_power_info *si_pi = si_get_pi(adev);
+       u32 tmp;
+
+       if (!si_pi->fan_ctrl_is_in_default_mode) {
+               tmp = RREG32(CG_FDO_CTRL2) & ~FDO_PWM_MODE_MASK;
+               tmp |= FDO_PWM_MODE(si_pi->fan_ctrl_default_mode);
+               WREG32(CG_FDO_CTRL2, tmp);
+
+               tmp = RREG32(CG_FDO_CTRL2) & ~TMIN_MASK;
+               tmp |= TMIN(si_pi->t_min);
+               WREG32(CG_FDO_CTRL2, tmp);
+               si_pi->fan_ctrl_is_in_default_mode = true;
+       }
+}
+
+static void si_thermal_start_smc_fan_control(struct amdgpu_device *adev)
+{
+       if (adev->pm.dpm.fan.ucode_fan_control) {
+               si_fan_ctrl_start_smc_fan_control(adev);
+               si_fan_ctrl_set_static_mode(adev, FDO_PWM_MODE_STATIC);
+       }
+}
+
+static void si_thermal_initialize(struct amdgpu_device *adev)
+{
+       u32 tmp;
+
+       if (adev->pm.fan_pulses_per_revolution) {
+               tmp = RREG32(CG_TACH_CTRL) & ~EDGE_PER_REV_MASK;
+               tmp |= EDGE_PER_REV(adev->pm.fan_pulses_per_revolution -1);
+               WREG32(CG_TACH_CTRL, tmp);
+       }
+
+       tmp = RREG32(CG_FDO_CTRL2) & ~TACH_PWM_RESP_RATE_MASK;
+       tmp |= TACH_PWM_RESP_RATE(0x28);
+       WREG32(CG_FDO_CTRL2, tmp);
+}
+
+static int si_thermal_start_thermal_controller(struct amdgpu_device *adev)
+{
+       int ret;
+
+       si_thermal_initialize(adev);
+       ret = si_thermal_set_temperature_range(adev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+       if (ret)
+               return ret;
+       ret = si_thermal_enable_alert(adev, true);
+       if (ret)
+               return ret;
+       if (adev->pm.dpm.fan.ucode_fan_control) {
+               ret = si_halt_smc(adev);
+               if (ret)
+                       return ret;
+               ret = si_thermal_setup_fan_table(adev);
+               if (ret)
+                       return ret;
+               ret = si_resume_smc(adev);
+               if (ret)
+                       return ret;
+               si_thermal_start_smc_fan_control(adev);
+       }
+
+       return 0;
+}
+
+static void si_thermal_stop_thermal_controller(struct amdgpu_device *adev)
+{
+       if (!adev->pm.no_fan) {
+               si_fan_ctrl_set_default_mode(adev);
+               si_fan_ctrl_stop_smc_fan_control(adev);
+       }
+}
+
+static int si_dpm_enable(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct amdgpu_ps *boot_ps = adev->pm.dpm.boot_ps;
+       int ret;
+
+       if (amdgpu_si_is_smc_running(adev))
+               return -EINVAL;
+       if (pi->voltage_control || si_pi->voltage_control_svi2)
+               si_enable_voltage_control(adev, true);
+       if (pi->mvdd_control)
+               si_get_mvdd_configuration(adev);
+       if (pi->voltage_control || si_pi->voltage_control_svi2) {
+               ret = si_construct_voltage_tables(adev);
+               if (ret) {
+                       DRM_ERROR("si_construct_voltage_tables failed\n");
+                       return ret;
+               }
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_initialize_mc_reg_table(adev);
+               if (ret)
+                       eg_pi->dynamic_ac_timing = false;
+       }
+       if (pi->dynamic_ss)
+               si_enable_spread_spectrum(adev, true);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(adev, true);
+       si_setup_bsp(adev);
+       si_program_git(adev);
+       si_program_tp(adev);
+       si_program_tpp(adev);
+       si_program_sstp(adev);
+       si_enable_display_gap(adev);
+       si_program_vc(adev);
+       ret = si_upload_firmware(adev);
+       if (ret) {
+               DRM_ERROR("si_upload_firmware failed\n");
+               return ret;
+       }
+       ret = si_process_firmware_header(adev);
+       if (ret) {
+               DRM_ERROR("si_process_firmware_header failed\n");
+               return ret;
+       }
+       ret = si_initial_switch_from_arb_f0_to_f1(adev);
+       if (ret) {
+               DRM_ERROR("si_initial_switch_from_arb_f0_to_f1 failed\n");
+               return ret;
+       }
+       ret = si_init_smc_table(adev);
+       if (ret) {
+               DRM_ERROR("si_init_smc_table failed\n");
+               return ret;
+       }
+       ret = si_init_smc_spll_table(adev);
+       if (ret) {
+               DRM_ERROR("si_init_smc_spll_table failed\n");
+               return ret;
+       }
+       ret = si_init_arb_table_index(adev);
+       if (ret) {
+               DRM_ERROR("si_init_arb_table_index failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_populate_mc_reg_table(adev, boot_ps);
+               if (ret) {
+                       DRM_ERROR("si_populate_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = si_initialize_smc_cac_tables(adev);
+       if (ret) {
+               DRM_ERROR("si_initialize_smc_cac_tables failed\n");
+               return ret;
+       }
+       ret = si_initialize_hardware_cac_manager(adev);
+       if (ret) {
+               DRM_ERROR("si_initialize_hardware_cac_manager failed\n");
+               return ret;
+       }
+       ret = si_initialize_smc_dte_tables(adev);
+       if (ret) {
+               DRM_ERROR("si_initialize_smc_dte_tables failed\n");
+               return ret;
+       }
+       ret = si_populate_smc_tdp_limits(adev, boot_ps);
+       if (ret) {
+               DRM_ERROR("si_populate_smc_tdp_limits failed\n");
+               return ret;
+       }
+       ret = si_populate_smc_tdp_limits_2(adev, boot_ps);
+       if (ret) {
+               DRM_ERROR("si_populate_smc_tdp_limits_2 failed\n");
+               return ret;
+       }
+       si_program_response_times(adev);
+       si_program_ds_registers(adev);
+       si_dpm_start_smc(adev);
+       ret = si_notify_smc_display_change(adev, false);
+       if (ret) {
+               DRM_ERROR("si_notify_smc_display_change failed\n");
+               return ret;
+       }
+       si_enable_sclk_control(adev, true);
+       si_start_dpm(adev);
+
+       si_enable_auto_throttle_source(adev, AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+       si_thermal_start_thermal_controller(adev);
+       ni_update_current_ps(adev, boot_ps);
+
+       return 0;
+}
+
+static int si_set_temperature_range(struct amdgpu_device *adev)
+{
+       int ret;
+
+       ret = si_thermal_enable_alert(adev, false);
+       if (ret)
+               return ret;
+       ret = si_thermal_set_temperature_range(adev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
+       if (ret)
+               return ret;
+       ret = si_thermal_enable_alert(adev, true);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static void si_dpm_disable(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct amdgpu_ps *boot_ps = adev->pm.dpm.boot_ps;
+
+       if (!amdgpu_si_is_smc_running(adev))
+               return;
+       si_thermal_stop_thermal_controller(adev);
+       si_disable_ulv(adev);
+       si_clear_vc(adev);
+       if (pi->thermal_protection)
+               si_enable_thermal_protection(adev, false);
+       si_enable_power_containment(adev, boot_ps, false);
+       si_enable_smc_cac(adev, boot_ps, false);
+       si_enable_spread_spectrum(adev, false);
+       si_enable_auto_throttle_source(adev, AMDGPU_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
+       si_stop_dpm(adev);
+       si_reset_to_default(adev);
+       si_dpm_stop_smc(adev);
+       si_force_switch_to_arb_f0(adev);
+
+       ni_update_current_ps(adev, boot_ps);
+}
+
+static int si_dpm_pre_set_power_state(struct amdgpu_device *adev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct amdgpu_ps requested_ps = *adev->pm.dpm.requested_ps;
+       struct amdgpu_ps *new_ps = &requested_ps;
+
+       ni_update_requested_ps(adev, new_ps);
+       si_apply_state_adjust_rules(adev, &eg_pi->requested_rps);
+
+       return 0;
+}
+
+static int si_power_control_set_level(struct amdgpu_device *adev)
+{
+       struct amdgpu_ps *new_ps = adev->pm.dpm.requested_ps;
+       int ret;
+
+       ret = si_restrict_performance_levels_before_switch(adev);
+       if (ret)
+               return ret;
+       ret = si_halt_smc(adev);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits(adev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_populate_smc_tdp_limits_2(adev, new_ps);
+       if (ret)
+               return ret;
+       ret = si_resume_smc(adev);
+       if (ret)
+               return ret;
+       ret = si_set_sw_state(adev);
+       if (ret)
+               return ret;
+       return 0;
+}
+
+static int si_dpm_set_power_state(struct amdgpu_device *adev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct amdgpu_ps *new_ps = &eg_pi->requested_rps;
+       struct amdgpu_ps *old_ps = &eg_pi->current_rps;
+       int ret;
+
+       ret = si_disable_ulv(adev);
+       if (ret) {
+               DRM_ERROR("si_disable_ulv failed\n");
+               return ret;
+       }
+       ret = si_restrict_performance_levels_before_switch(adev);
+       if (ret) {
+               DRM_ERROR("si_restrict_performance_levels_before_switch failed\n");
+               return ret;
+       }
+       if (eg_pi->pcie_performance_request)
+               si_request_link_speed_change_before_state_change(adev, new_ps, old_ps);
+       ni_set_uvd_clock_before_set_eng_clock(adev, new_ps, old_ps);
+       ret = si_enable_power_containment(adev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("si_enable_power_containment failed\n");
+               return ret;
+       }
+       ret = si_enable_smc_cac(adev, new_ps, false);
+       if (ret) {
+               DRM_ERROR("si_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = si_halt_smc(adev);
+       if (ret) {
+               DRM_ERROR("si_halt_smc failed\n");
+               return ret;
+       }
+       ret = si_upload_sw_state(adev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_upload_sw_state failed\n");
+               return ret;
+       }
+       ret = si_upload_smc_data(adev);
+       if (ret) {
+               DRM_ERROR("si_upload_smc_data failed\n");
+               return ret;
+       }
+       ret = si_upload_ulv_state(adev);
+       if (ret) {
+               DRM_ERROR("si_upload_ulv_state failed\n");
+               return ret;
+       }
+       if (eg_pi->dynamic_ac_timing) {
+               ret = si_upload_mc_reg_table(adev, new_ps);
+               if (ret) {
+                       DRM_ERROR("si_upload_mc_reg_table failed\n");
+                       return ret;
+               }
+       }
+       ret = si_program_memory_timing_parameters(adev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_program_memory_timing_parameters failed\n");
+               return ret;
+       }
+       si_set_pcie_lane_width_in_smc(adev, new_ps, old_ps);
+
+       ret = si_resume_smc(adev);
+       if (ret) {
+               DRM_ERROR("si_resume_smc failed\n");
+               return ret;
+       }
+       ret = si_set_sw_state(adev);
+       if (ret) {
+               DRM_ERROR("si_set_sw_state failed\n");
+               return ret;
+       }
+       ni_set_uvd_clock_after_set_eng_clock(adev, new_ps, old_ps);
+       if (eg_pi->pcie_performance_request)
+               si_notify_link_speed_change_after_state_change(adev, new_ps, old_ps);
+       ret = si_set_power_state_conditionally_enable_ulv(adev, new_ps);
+       if (ret) {
+               DRM_ERROR("si_set_power_state_conditionally_enable_ulv failed\n");
+               return ret;
+       }
+       ret = si_enable_smc_cac(adev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("si_enable_smc_cac failed\n");
+               return ret;
+       }
+       ret = si_enable_power_containment(adev, new_ps, true);
+       if (ret) {
+               DRM_ERROR("si_enable_power_containment failed\n");
+               return ret;
+       }
+
+       ret = si_power_control_set_level(adev);
+       if (ret) {
+               DRM_ERROR("si_power_control_set_level failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void si_dpm_post_set_power_state(struct amdgpu_device *adev)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct amdgpu_ps *new_ps = &eg_pi->requested_rps;
+
+       ni_update_current_ps(adev, new_ps);
+}
+
+#if 0
+void si_dpm_reset_asic(struct amdgpu_device *adev)
+{
+       si_restrict_performance_levels_before_switch(adev);
+       si_disable_ulv(adev);
+       si_set_boot_state(adev);
+}
+#endif
+
+static void si_dpm_display_configuration_changed(struct amdgpu_device *adev)
+{
+       si_program_display_gap(adev);
+}
+
+
+static void si_parse_pplib_non_clock_info(struct amdgpu_device *adev,
+                                         struct amdgpu_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)
+               adev->pm.dpm.boot_ps = rps;
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
+               adev->pm.dpm.uvd_ps = rps;
+}
+
+static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
+                                     struct amdgpu_ps *rps, int index,
+                                     union pplib_clock_info *clock_info)
+{
+       struct rv7xx_power_info *pi = rv770_get_pi(adev);
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct si_power_info *si_pi = si_get_pi(adev);
+       struct  si_ps *ps = si_get_ps(rps);
+       u16 leakage_voltage;
+       struct rv7xx_pl *pl = &ps->performance_levels[index];
+       int ret;
+
+       ps->performance_level_count = index + 1;
+
+       pl->sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+       pl->sclk |= clock_info->si.ucEngineClockHigh << 16;
+       pl->mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+       pl->mclk |= clock_info->si.ucMemoryClockHigh << 16;
+
+       pl->vddc = le16_to_cpu(clock_info->si.usVDDC);
+       pl->vddci = le16_to_cpu(clock_info->si.usVDDCI);
+       pl->flags = le32_to_cpu(clock_info->si.ulFlags);
+       pl->pcie_gen = r600_get_pcie_gen_support(adev,
+                                                si_pi->sys_pcie_mask,
+                                                si_pi->boot_pcie_gen,
+                                                clock_info->si.ucPCIEGen);
+
+       /* patch up vddc if necessary */
+       ret = si_get_leakage_voltage_from_leakage_index(adev, pl->vddc,
+                                                       &leakage_voltage);
+       if (ret == 0)
+               pl->vddc = leakage_voltage;
+
+       if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
+               pi->acpi_vddc = pl->vddc;
+               eg_pi->acpi_vddci = pl->vddci;
+               si_pi->acpi_pcie_gen = pl->pcie_gen;
+       }
+
+       if ((rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) &&
+           index == 0) {
+               /* XXX disable for A0 tahiti */
+               si_pi->ulv.supported = false;
+               si_pi->ulv.pl = *pl;
+               si_pi->ulv.one_pcie_lane_in_ulv = false;
+               si_pi->ulv.volt_change_delay = SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT;
+               si_pi->ulv.cg_ulv_parameter = SISLANDS_CGULVPARAMETER_DFLT;
+               si_pi->ulv.cg_ulv_control = SISLANDS_CGULVCONTROL_DFLT;
+       }
+
+       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, mvdd;
+               amdgpu_atombios_get_default_voltages(adev, &vddc, &vddci, &mvdd);
+               pl->mclk = adev->clock.default_mclk;
+               pl->sclk = adev->clock.default_sclk;
+               pl->vddc = vddc;
+               pl->vddci = vddci;
+               si_pi->mvdd_bootup_value = mvdd;
+       }
+
+       if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
+           ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
+               adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
+               adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
+               adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
+               adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
+       }
+}
+
+union pplib_power_state {
+       struct _ATOM_PPLIB_STATE v1;
+       struct _ATOM_PPLIB_STATE_V2 v2;
+};
+
+static int si_parse_power_table(struct amdgpu_device *adev)
+{
+       struct amdgpu_mode_info *mode_info = &adev->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  si_ps *ps;
+
+       if (!amdgpu_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);
+
+       amdgpu_add_thermal_controller(adev);
+
+       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));
+
+       adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) *
+                                 state_array->ucNumEntries, GFP_KERNEL);
+       if (!adev->pm.dpm.ps)
+               return -ENOMEM;
+       power_state_offset = (u8 *)state_array->states;
+       for (i = 0; i < state_array->ucNumEntries; i++) {
+               u8 *idx;
+               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];
+               ps = kzalloc(sizeof(struct  si_ps), GFP_KERNEL);
+               if (ps == NULL) {
+                       kfree(adev->pm.dpm.ps);
+                       return -ENOMEM;
+               }
+               adev->pm.dpm.ps[i].ps_priv = ps;
+               si_parse_pplib_non_clock_info(adev, &adev->pm.dpm.ps[i],
+                                             non_clock_info,
+                                             non_clock_info_array->ucEntrySize);
+               k = 0;
+               idx = (u8 *)&power_state->v2.clockInfoIndex[0];
+               for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
+                       clock_array_index = idx[j];
+                       if (clock_array_index >= clock_info_array->ucNumEntries)
+                               continue;
+                       if (k >= SISLANDS_MAX_HARDWARE_POWERLEVELS)
+                               break;
+                       clock_info = (union pplib_clock_info *)
+                               ((u8 *)&clock_info_array->clockInfo[0] +
+                                (clock_array_index * clock_info_array->ucEntrySize));
+                       si_parse_pplib_clock_info(adev,
+                                                 &adev->pm.dpm.ps[i], k,
+                                                 clock_info);
+                       k++;
+               }
+               power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
+       }
+       adev->pm.dpm.num_ps = state_array->ucNumEntries;
+
+       /* fill in the vce power states */
+       for (i = 0; i < AMDGPU_MAX_VCE_LEVELS; i++) {
+               u32 sclk, mclk;
+               clock_array_index = adev->pm.dpm.vce_states[i].clk_idx;
+               clock_info = (union pplib_clock_info *)
+                       &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
+               sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+               sclk |= clock_info->si.ucEngineClockHigh << 16;
+               mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+               mclk |= clock_info->si.ucMemoryClockHigh << 16;
+               adev->pm.dpm.vce_states[i].sclk = sclk;
+               adev->pm.dpm.vce_states[i].mclk = mclk;
+       }
+
+       return 0;
+}
+
+static int si_dpm_init(struct amdgpu_device *adev)
+{
+       struct rv7xx_power_info *pi;
+       struct evergreen_power_info *eg_pi;
+       struct ni_power_info *ni_pi;
+       struct si_power_info *si_pi;
+       struct atom_clock_dividers dividers;
+       int ret;
+       u32 mask;
+
+       si_pi = kzalloc(sizeof(struct si_power_info), GFP_KERNEL);
+       if (si_pi == NULL)
+               return -ENOMEM;
+       adev->pm.dpm.priv = si_pi;
+       ni_pi = &si_pi->ni;
+       eg_pi = &ni_pi->eg;
+       pi = &eg_pi->rv7xx;
+
+       ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask);
+       if (ret)
+               si_pi->sys_pcie_mask = 0;
+       else
+               si_pi->sys_pcie_mask = mask;
+       si_pi->force_pcie_gen = AMDGPU_PCIE_GEN_INVALID;
+       si_pi->boot_pcie_gen = si_get_current_pcie_speed(adev);
+
+       si_set_max_cu_value(adev);
+
+       rv770_get_max_vddc(adev);
+       si_get_leakage_vddc(adev);
+       si_patch_dependency_tables_based_on_leakage(adev);
+
+       pi->acpi_vddc = 0;
+       eg_pi->acpi_vddci = 0;
+       pi->min_vddc_in_table = 0;
+       pi->max_vddc_in_table = 0;
+
+       ret = amdgpu_get_platform_caps(adev);
+       if (ret)
+               return ret;
+
+       ret = amdgpu_parse_extended_power_table(adev);
+       if (ret)
+               return ret;
+
+       ret = si_parse_power_table(adev);
+       if (ret)
+               return ret;
+
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries =
+               kzalloc(4 * sizeof(struct amdgpu_clock_voltage_dependency_entry), GFP_KERNEL);
+       if (!adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries) {
+               amdgpu_free_extended_power_table(adev);
+               return -ENOMEM;
+       }
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.count = 4;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].clk = 0;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[0].v = 0;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].clk = 36000;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[1].v = 720;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].clk = 54000;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[2].v = 810;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].clk = 72000;
+       adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries[3].v = 900;
+
+       if (adev->pm.dpm.voltage_response_time == 0)
+               adev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
+       if (adev->pm.dpm.backbias_response_time == 0)
+               adev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
+
+       ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_ENGINE_PLL_PARAM,
+                                            0, false, &dividers);
+       if (ret)
+               pi->ref_div = dividers.ref_div + 1;
+       else
+               pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
+
+       eg_pi->smu_uvd_hs = false;
+
+       pi->mclk_strobe_mode_threshold = 40000;
+       if (si_is_special_1gb_platform(adev))
+               pi->mclk_stutter_mode_threshold = 0;
+       else
+               pi->mclk_stutter_mode_threshold = pi->mclk_strobe_mode_threshold;
+       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 =
+               amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                           VOLTAGE_OBJ_GPIO_LUT);
+       if (!pi->voltage_control) {
+               si_pi->voltage_control_svi2 =
+                       amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                   VOLTAGE_OBJ_SVID2);
+               if (si_pi->voltage_control_svi2)
+                       amdgpu_atombios_get_svi2_info(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                                 &si_pi->svd_gpio_id, &si_pi->svc_gpio_id);
+       }
+
+       pi->mvdd_control =
+               amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_MVDDC,
+                                           VOLTAGE_OBJ_GPIO_LUT);
+
+       eg_pi->vddci_control =
+               amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+                                           VOLTAGE_OBJ_GPIO_LUT);
+       if (!eg_pi->vddci_control)
+               si_pi->vddci_control_svi2 =
+                       amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDCI,
+                                                   VOLTAGE_OBJ_SVID2);
+
+       si_pi->vddc_phase_shed_control =
+               amdgpu_atombios_is_voltage_gpio(adev, SET_VOLTAGE_TYPE_ASIC_VDDC,
+                                           VOLTAGE_OBJ_PHASE_LUT);
+
+       rv770_get_engine_memory_ss(adev);
+
+       pi->asi = RV770_ASI_DFLT;
+       pi->pasi = CYPRESS_HASI_DFLT;
+       pi->vrc = SISLANDS_VRC_DFLT;
+
+       pi->gfx_clock_gating = true;
+
+       eg_pi->sclk_deep_sleep = true;
+       si_pi->sclk_deep_sleep_above_low = false;
+
+       if (adev->pm.int_thermal_type != THERMAL_TYPE_NONE)
+               pi->thermal_protection = true;
+       else
+               pi->thermal_protection = false;
+
+       eg_pi->dynamic_ac_timing = true;
+
+       eg_pi->light_sleep = true;
+#if defined(CONFIG_ACPI)
+       eg_pi->pcie_performance_request =
+               amdgpu_acpi_is_pcie_performance_request_supported(adev);
+#else
+       eg_pi->pcie_performance_request = false;
+#endif
+
+       si_pi->sram_end = SMC_RAM_END;
+
+       adev->pm.dpm.dyn_state.mclk_sclk_ratio = 4;
+       adev->pm.dpm.dyn_state.sclk_mclk_delta = 15000;
+       adev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
+       adev->pm.dpm.dyn_state.valid_sclk_values.count = 0;
+       adev->pm.dpm.dyn_state.valid_sclk_values.values = NULL;
+       adev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
+       adev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
+
+       si_initialize_powertune_defaults(adev);
+
+       /* make sure dc limits are valid */
+       if ((adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk == 0) ||
+           (adev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk == 0))
+               adev->pm.dpm.dyn_state.max_clock_voltage_on_dc =
+                       adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
+
+       si_pi->fan_ctrl_is_in_default_mode = true;
+
+       return 0;
+}
+
+static void si_dpm_fini(struct amdgpu_device *adev)
+{
+       int i;
+
+       if (adev->pm.dpm.ps)
+               for (i = 0; i < adev->pm.dpm.num_ps; i++)
+                       kfree(adev->pm.dpm.ps[i].ps_priv);
+       kfree(adev->pm.dpm.ps);
+       kfree(adev->pm.dpm.priv);
+       kfree(adev->pm.dpm.dyn_state.vddc_dependency_on_dispclk.entries);
+       amdgpu_free_extended_power_table(adev);
+}
+
+static void si_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev,
+                                                   struct seq_file *m)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct amdgpu_ps *rps = &eg_pi->current_rps;
+       struct  si_ps *ps = si_get_ps(rps);
+       struct rv7xx_pl *pl;
+       u32 current_index =
+               (RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_INDEX_MASK) >>
+               CURRENT_STATE_INDEX_SHIFT;
+
+       if (current_index >= ps->performance_level_count) {
+               seq_printf(m, "invalid dpm profile %d\n", current_index);
+       } else {
+               pl = &ps->performance_levels[current_index];
+               seq_printf(m, "uvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+               seq_printf(m, "power level %d    sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+                          current_index, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+       }
+}
+
+static int si_dpm_set_interrupt_state(struct amdgpu_device *adev,
+                                     struct amdgpu_irq_src *source,
+                                     unsigned type,
+                                     enum amdgpu_interrupt_state state)
+{
+       u32 cg_thermal_int;
+
+       switch (type) {
+       case AMDGPU_THERMAL_IRQ_LOW_TO_HIGH:
+               switch (state) {
+               case AMDGPU_IRQ_STATE_DISABLE:
+                       cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+                       cg_thermal_int |= THERM_INT_MASK_HIGH;
+                       WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+                       break;
+               case AMDGPU_IRQ_STATE_ENABLE:
+                       cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+                       cg_thermal_int &= ~THERM_INT_MASK_HIGH;
+                       WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case AMDGPU_THERMAL_IRQ_HIGH_TO_LOW:
+               switch (state) {
+               case AMDGPU_IRQ_STATE_DISABLE:
+                       cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+                       cg_thermal_int |= THERM_INT_MASK_LOW;
+                       WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+                       break;
+               case AMDGPU_IRQ_STATE_ENABLE:
+                       cg_thermal_int = RREG32_SMC(CG_THERMAL_INT);
+                       cg_thermal_int &= ~THERM_INT_MASK_LOW;
+                       WREG32_SMC(CG_THERMAL_INT, cg_thermal_int);
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int si_dpm_process_interrupt(struct amdgpu_device *adev,
+                                   struct amdgpu_irq_src *source,
+                                   struct amdgpu_iv_entry *entry)
+{
+       bool queue_thermal = false;
+
+       if (entry == NULL)
+               return -EINVAL;
+
+       switch (entry->src_id) {
+       case 230: /* thermal low to high */
+               DRM_DEBUG("IH: thermal low to high\n");
+               adev->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");
+               adev->pm.dpm.thermal.high_to_low = true;
+               queue_thermal = true;
+               break;
+       default:
+               break;
+       }
+
+       if (queue_thermal)
+               schedule_work(&adev->pm.dpm.thermal.work);
+
+       return 0;
+}
+
+static int si_dpm_late_init(void *handle)
+{
+       int ret;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!amdgpu_dpm)
+               return 0;
+
+       /* init the sysfs and debugfs files late */
+       ret = amdgpu_pm_sysfs_init(adev);
+       if (ret)
+               return ret;
+
+       ret = si_set_temperature_range(adev);
+       if (ret)
+               return ret;
+#if 0 //TODO ?
+       si_dpm_powergate_uvd(adev, true);
+#endif
+       return 0;
+}
+
+/**
+ * si_dpm_init_microcode - load ucode images from disk
+ *
+ * @adev: amdgpu_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 si_dpm_init_microcode(struct amdgpu_device *adev)
+{
+       const char *chip_name;
+       char fw_name[30];
+       int err;
+
+       DRM_DEBUG("\n");
+       switch (adev->asic_type) {
+       case CHIP_TAHITI:
+               chip_name = "tahiti";
+               break;
+       case CHIP_PITCAIRN:
+               if ((adev->pdev->revision == 0x81) ||
+                   (adev->pdev->device == 0x6810) ||
+                   (adev->pdev->device == 0x6811) ||
+                   (adev->pdev->device == 0x6816) ||
+                   (adev->pdev->device == 0x6817) ||
+                   (adev->pdev->device == 0x6806))
+                       chip_name = "pitcairn_k";
+               else
+                       chip_name = "pitcairn";
+               break;
+       case CHIP_VERDE:
+               if ((adev->pdev->revision == 0x81) ||
+                   (adev->pdev->revision == 0x83) ||
+                   (adev->pdev->revision == 0x87) ||
+                   (adev->pdev->device == 0x6820) ||
+                   (adev->pdev->device == 0x6821) ||
+                   (adev->pdev->device == 0x6822) ||
+                   (adev->pdev->device == 0x6823) ||
+                   (adev->pdev->device == 0x682A) ||
+                   (adev->pdev->device == 0x682B))
+                       chip_name = "verde_k";
+               else
+                       chip_name = "verde";
+               break;
+       case CHIP_OLAND:
+               if ((adev->pdev->revision == 0xC7) ||
+                   (adev->pdev->revision == 0x80) ||
+                   (adev->pdev->revision == 0x81) ||
+                   (adev->pdev->revision == 0x83) ||
+                   (adev->pdev->device == 0x6604) ||
+                   (adev->pdev->device == 0x6605))
+                       chip_name = "oland_k";
+               else
+                       chip_name = "oland";
+               break;
+       case CHIP_HAINAN:
+               if ((adev->pdev->revision == 0x81) ||
+                   (adev->pdev->revision == 0x83) ||
+                   (adev->pdev->revision == 0xC3) ||
+                   (adev->pdev->device == 0x6664) ||
+                   (adev->pdev->device == 0x6665) ||
+                   (adev->pdev->device == 0x6667))
+                       chip_name = "hainan_k";
+               else
+                       chip_name = "hainan";
+               break;
+       default: BUG();
+       }
+
+       snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name);
+       err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
+       if (err)
+               goto out;
+       err = amdgpu_ucode_validate(adev->pm.fw);
+
+out:
+       if (err) {
+               DRM_ERROR("si_smc: Failed to load firmware. err = %d\"%s\"\n",
+                         err, fw_name);
+               release_firmware(adev->pm.fw);
+               adev->pm.fw = NULL;
+       }
+       return err;
+
+}
+
+static int si_dpm_sw_init(void *handle)
+{
+       int ret;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       ret = amdgpu_irq_add_id(adev, 230, &adev->pm.dpm.thermal.irq);
+       if (ret)
+               return ret;
+
+       ret = amdgpu_irq_add_id(adev, 231, &adev->pm.dpm.thermal.irq);
+       if (ret)
+               return ret;
+
+       /* default to balanced state */
+       adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
+       adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
+       adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO;
+       adev->pm.default_sclk = adev->clock.default_sclk;
+       adev->pm.default_mclk = adev->clock.default_mclk;
+       adev->pm.current_sclk = adev->clock.default_sclk;
+       adev->pm.current_mclk = adev->clock.default_mclk;
+       adev->pm.int_thermal_type = THERMAL_TYPE_NONE;
+
+       if (amdgpu_dpm == 0)
+               return 0;
+
+       ret = si_dpm_init_microcode(adev);
+       if (ret)
+               return ret;
+
+       INIT_WORK(&adev->pm.dpm.thermal.work, amdgpu_dpm_thermal_work_handler);
+       mutex_lock(&adev->pm.mutex);
+       ret = si_dpm_init(adev);
+       if (ret)
+               goto dpm_failed;
+       adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+       if (amdgpu_dpm == 1)
+               amdgpu_pm_print_power_states(adev);
+       mutex_unlock(&adev->pm.mutex);
+       DRM_INFO("amdgpu: dpm initialized\n");
+
+       return 0;
+
+dpm_failed:
+       si_dpm_fini(adev);
+       mutex_unlock(&adev->pm.mutex);
+       DRM_ERROR("amdgpu: dpm initialization failed\n");
+       return ret;
+}
+
+static int si_dpm_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       mutex_lock(&adev->pm.mutex);
+       amdgpu_pm_sysfs_fini(adev);
+       si_dpm_fini(adev);
+       mutex_unlock(&adev->pm.mutex);
+
+       return 0;
+}
+
+static int si_dpm_hw_init(void *handle)
+{
+       int ret;
+
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!amdgpu_dpm)
+               return 0;
+
+       mutex_lock(&adev->pm.mutex);
+       si_dpm_setup_asic(adev);
+       ret = si_dpm_enable(adev);
+       if (ret)
+               adev->pm.dpm_enabled = false;
+       else
+               adev->pm.dpm_enabled = true;
+       mutex_unlock(&adev->pm.mutex);
+
+       return ret;
+}
+
+static int si_dpm_hw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (adev->pm.dpm_enabled) {
+               mutex_lock(&adev->pm.mutex);
+               si_dpm_disable(adev);
+               mutex_unlock(&adev->pm.mutex);
+       }
+
+       return 0;
+}
+
+static int si_dpm_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (adev->pm.dpm_enabled) {
+               mutex_lock(&adev->pm.mutex);
+               /* disable dpm */
+               si_dpm_disable(adev);
+               /* reset the power state */
+               adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
+               mutex_unlock(&adev->pm.mutex);
+       }
+       return 0;
+}
+
+static int si_dpm_resume(void *handle)
+{
+       int ret;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (adev->pm.dpm_enabled) {
+               /* asic init will reset to the boot state */
+               mutex_lock(&adev->pm.mutex);
+               si_dpm_setup_asic(adev);
+               ret = si_dpm_enable(adev);
+               if (ret)
+                       adev->pm.dpm_enabled = false;
+               else
+                       adev->pm.dpm_enabled = true;
+               mutex_unlock(&adev->pm.mutex);
+               if (adev->pm.dpm_enabled)
+                       amdgpu_pm_compute_clocks(adev);
+       }
+       return 0;
+}
+
+static bool si_dpm_is_idle(void *handle)
+{
+       /* XXX */
+       return true;
+}
+
+static int si_dpm_wait_for_idle(void *handle)
+{
+       /* XXX */
+       return 0;
+}
+
+static int si_dpm_soft_reset(void *handle)
+{
+       return 0;
+}
+
+static int si_dpm_set_clockgating_state(void *handle,
+                                       enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int si_dpm_set_powergating_state(void *handle,
+                                       enum amd_powergating_state state)
+{
+       return 0;
+}
+
+/* get temperature in millidegrees */
+static int si_dpm_get_temp(struct amdgpu_device *adev)
+{
+       u32 temp;
+       int actual_temp = 0;
+
+       temp = (RREG32(CG_MULT_THERMAL_STATUS) & CTF_TEMP_MASK) >>
+               CTF_TEMP_SHIFT;
+
+       if (temp & 0x200)
+               actual_temp = 255;
+       else
+               actual_temp = temp & 0x1ff;
+
+       actual_temp = (actual_temp * 1000);
+
+       return actual_temp;
+}
+
+static u32 si_dpm_get_sclk(struct amdgpu_device *adev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct  si_ps *requested_state = si_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].sclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk;
+}
+
+static u32 si_dpm_get_mclk(struct amdgpu_device *adev, bool low)
+{
+       struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
+       struct  si_ps *requested_state = si_get_ps(&eg_pi->requested_rps);
+
+       if (low)
+               return requested_state->performance_levels[0].mclk;
+       else
+               return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk;
+}
+
+static void si_dpm_print_power_state(struct amdgpu_device *adev,
+                                    struct amdgpu_ps *rps)
+{
+       struct  si_ps *ps = si_get_ps(rps);
+       struct rv7xx_pl *pl;
+       int i;
+
+       amdgpu_dpm_print_class_info(rps->class, rps->class2);
+       amdgpu_dpm_print_cap_info(rps->caps);
+       DRM_INFO("\tuvd    vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
+       for (i = 0; i < ps->performance_level_count; i++) {
+               pl = &ps->performance_levels[i];
+               if (adev->asic_type >= CHIP_TAHITI)
+                       DRM_INFO("\t\tpower level %d    sclk: %u mclk: %u vddc: %u vddci: %u pcie gen: %u\n",
+                                i, pl->sclk, pl->mclk, pl->vddc, pl->vddci, pl->pcie_gen + 1);
+               else
+                       DRM_INFO("\t\tpower level %d    sclk: %u mclk: %u vddc: %u vddci: %u\n",
+                                i, pl->sclk, pl->mclk, pl->vddc, pl->vddci);
+       }
+       amdgpu_dpm_print_ps_status(adev, rps);
+}
+
+static int si_dpm_early_init(void *handle)
+{
+
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       si_dpm_set_dpm_funcs(adev);
+       si_dpm_set_irq_funcs(adev);
+       return 0;
+}
+
+
+const struct amd_ip_funcs si_dpm_ip_funcs = {
+       .name = "si_dpm",
+       .early_init = si_dpm_early_init,
+       .late_init = si_dpm_late_init,
+       .sw_init = si_dpm_sw_init,
+       .sw_fini = si_dpm_sw_fini,
+       .hw_init = si_dpm_hw_init,
+       .hw_fini = si_dpm_hw_fini,
+       .suspend = si_dpm_suspend,
+       .resume = si_dpm_resume,
+       .is_idle = si_dpm_is_idle,
+       .wait_for_idle = si_dpm_wait_for_idle,
+       .soft_reset = si_dpm_soft_reset,
+       .set_clockgating_state = si_dpm_set_clockgating_state,
+       .set_powergating_state = si_dpm_set_powergating_state,
+};
+
+static const struct amdgpu_dpm_funcs si_dpm_funcs = {
+       .get_temperature = &si_dpm_get_temp,
+       .pre_set_power_state = &si_dpm_pre_set_power_state,
+       .set_power_state = &si_dpm_set_power_state,
+       .post_set_power_state = &si_dpm_post_set_power_state,
+       .display_configuration_changed = &si_dpm_display_configuration_changed,
+       .get_sclk = &si_dpm_get_sclk,
+       .get_mclk = &si_dpm_get_mclk,
+       .print_power_state = &si_dpm_print_power_state,
+       .debugfs_print_current_performance_level = &si_dpm_debugfs_print_current_performance_level,
+       .force_performance_level = &si_dpm_force_performance_level,
+       .vblank_too_short = &si_dpm_vblank_too_short,
+       .set_fan_control_mode = &si_dpm_set_fan_control_mode,
+       .get_fan_control_mode = &si_dpm_get_fan_control_mode,
+       .set_fan_speed_percent = &si_dpm_set_fan_speed_percent,
+       .get_fan_speed_percent = &si_dpm_get_fan_speed_percent,
+};
+
+static void si_dpm_set_dpm_funcs(struct amdgpu_device *adev)
+{
+       if (adev->pm.funcs == NULL)
+               adev->pm.funcs = &si_dpm_funcs;
+}
+
+static const struct amdgpu_irq_src_funcs si_dpm_irq_funcs = {
+       .set = si_dpm_set_interrupt_state,
+       .process = si_dpm_process_interrupt,
+};
+
+static void si_dpm_set_irq_funcs(struct amdgpu_device *adev)
+{
+       adev->pm.dpm.thermal.irq.num_types = AMDGPU_THERMAL_IRQ_LAST;
+       adev->pm.dpm.thermal.irq.funcs = &si_dpm_irq_funcs;
+}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.h b/drivers/gpu/drm/amd/amdgpu/si_dpm.h
new file mode 100644 (file)
index 0000000..51ce21c
--- /dev/null
@@ -0,0 +1,1015 @@
+/*
+ * 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 __SI_DPM_H__
+#define __SI_DPM_H__
+
+#include "amdgpu_atombios.h"
+#include "sislands_smc.h"
+
+#define MC_CG_CONFIG                                    0x96f
+#define MC_ARB_CG                                       0x9fa
+#define                CG_ARB_REQ(x)                           ((x) << 0)
+#define                CG_ARB_REQ_MASK                         (0xff << 0)
+
+#define        MC_ARB_DRAM_TIMING_1                            0x9fc
+#define        MC_ARB_DRAM_TIMING_2                            0x9fd
+#define        MC_ARB_DRAM_TIMING_3                            0x9fe
+#define        MC_ARB_DRAM_TIMING2_1                           0x9ff
+#define        MC_ARB_DRAM_TIMING2_2                           0xa00
+#define        MC_ARB_DRAM_TIMING2_3                           0xa01
+
+#define MAX_NO_OF_MVDD_VALUES 2
+#define MAX_NO_VREG_STEPS 32
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+#define RV770_ASI_DFLT                                1000
+#define CYPRESS_HASI_DFLT                               400000
+#define PCIE_PERF_REQ_PECI_GEN1         2
+#define PCIE_PERF_REQ_PECI_GEN2         3
+#define PCIE_PERF_REQ_PECI_GEN3         4
+#define RV770_DEFAULT_VCLK_FREQ  53300 /* 10 khz */
+#define RV770_DEFAULT_DCLK_FREQ  40000 /* 10 khz */
+
+#define SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE 16
+
+#define RV770_SMC_TABLE_ADDRESS 0xB000
+#define RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE    3
+
+#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
+
+#define RV770_SMC_VOLTAGEMASK_VDDC 0
+#define RV770_SMC_VOLTAGEMASK_MVDD 1
+#define RV770_SMC_VOLTAGEMASK_VDDCI 2
+#define RV770_SMC_VOLTAGEMASK_MAX  4
+
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+#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
+
+#define MAX_NO_VREG_STEPS 32
+
+#define NISLANDS_SMC_VOLTAGEMASK_VDDC  0
+#define NISLANDS_SMC_VOLTAGEMASK_MVDD  1
+#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define NISLANDS_SMC_VOLTAGEMASK_MAX   4
+
+#define SISLANDS_MCREGISTERTABLE_INITIAL_SLOT               0
+#define SISLANDS_MCREGISTERTABLE_ACPI_SLOT                  1
+#define SISLANDS_MCREGISTERTABLE_ULV_SLOT                   2
+#define SISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT     3
+
+#define SISLANDS_LEAKAGE_INDEX0     0xff01
+#define SISLANDS_MAX_LEAKAGE_COUNT  4
+
+#define SISLANDS_MAX_HARDWARE_POWERLEVELS 5
+#define SISLANDS_INITIAL_STATE_ARB_INDEX    0
+#define SISLANDS_ACPI_STATE_ARB_INDEX       1
+#define SISLANDS_ULV_STATE_ARB_INDEX        2
+#define SISLANDS_DRIVER_STATE_ARB_INDEX     3
+
+#define SISLANDS_DPM2_MAX_PULSE_SKIP        256
+
+#define SISLANDS_DPM2_NEAR_TDP_DEC          10
+#define SISLANDS_DPM2_ABOVE_SAFE_INC        5
+#define SISLANDS_DPM2_BELOW_SAFE_INC        20
+
+#define SISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT            80
+
+#define SISLANDS_DPM2_MAXPS_PERCENT_H                   99
+#define SISLANDS_DPM2_MAXPS_PERCENT_M                   99
+
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER                 0x3FFF
+#define SISLANDS_DPM2_SQ_RAMP_MIN_POWER                 0x12
+#define SISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA           0x15
+#define SISLANDS_DPM2_SQ_RAMP_STI_SIZE                  0x1E
+#define SISLANDS_DPM2_SQ_RAMP_LTI_RATIO                 0xF
+
+#define SISLANDS_DPM2_PWREFFICIENCYRATIO_MARGIN         10
+
+#define SISLANDS_VRC_DFLT                               0xC000B3
+#define SISLANDS_ULVVOLTAGECHANGEDELAY_DFLT             1687
+#define SISLANDS_CGULVPARAMETER_DFLT                    0x00040035
+#define SISLANDS_CGULVCONTROL_DFLT                      0x1f007550
+
+#define SI_ASI_DFLT                                10000
+#define SI_BSP_DFLT                                0x41EB
+#define SI_BSU_DFLT                                0x2
+#define SI_AH_DFLT                                 5
+#define SI_RLP_DFLT                                25
+#define SI_RMP_DFLT                                65
+#define SI_LHP_DFLT                                40
+#define SI_LMP_DFLT                                15
+#define SI_TD_DFLT                                 0
+#define SI_UTC_DFLT_00                             0x24
+#define SI_UTC_DFLT_01                             0x22
+#define SI_UTC_DFLT_02                             0x22
+#define SI_UTC_DFLT_03                             0x22
+#define SI_UTC_DFLT_04                             0x22
+#define SI_UTC_DFLT_05                             0x22
+#define SI_UTC_DFLT_06                             0x22
+#define SI_UTC_DFLT_07                             0x22
+#define SI_UTC_DFLT_08                             0x22
+#define SI_UTC_DFLT_09                             0x22
+#define SI_UTC_DFLT_10                             0x22
+#define SI_UTC_DFLT_11                             0x22
+#define SI_UTC_DFLT_12                             0x22
+#define SI_UTC_DFLT_13                             0x22
+#define SI_UTC_DFLT_14                             0x22
+#define SI_DTC_DFLT_00                             0x24
+#define SI_DTC_DFLT_01                             0x22
+#define SI_DTC_DFLT_02                             0x22
+#define SI_DTC_DFLT_03                             0x22
+#define SI_DTC_DFLT_04                             0x22
+#define SI_DTC_DFLT_05                             0x22
+#define SI_DTC_DFLT_06                             0x22
+#define SI_DTC_DFLT_07                             0x22
+#define SI_DTC_DFLT_08                             0x22
+#define SI_DTC_DFLT_09                             0x22
+#define SI_DTC_DFLT_10                             0x22
+#define SI_DTC_DFLT_11                             0x22
+#define SI_DTC_DFLT_12                             0x22
+#define SI_DTC_DFLT_13                             0x22
+#define SI_DTC_DFLT_14                             0x22
+#define SI_VRC_DFLT                                0x0000C003
+#define SI_VOLTAGERESPONSETIME_DFLT                1000
+#define SI_BACKBIASRESPONSETIME_DFLT               1000
+#define SI_VRU_DFLT                                0x3
+#define SI_SPLLSTEPTIME_DFLT                       0x1000
+#define SI_SPLLSTEPUNIT_DFLT                       0x3
+#define SI_TPU_DFLT                                0
+#define SI_TPC_DFLT                                0x200
+#define SI_SSTU_DFLT                               0
+#define SI_SST_DFLT                                0x00C8
+#define SI_GICST_DFLT                              0x200
+#define SI_FCT_DFLT                                0x0400
+#define SI_FCTU_DFLT                               0
+#define SI_CTXCGTT3DRPHC_DFLT                      0x20
+#define SI_CTXCGTT3DRSDC_DFLT                      0x40
+#define SI_VDDC3DOORPHC_DFLT                       0x100
+#define SI_VDDC3DOORSDC_DFLT                       0x7
+#define SI_VDDC3DOORSU_DFLT                        0
+#define SI_MPLLLOCKTIME_DFLT                       100
+#define SI_MPLLRESETTIME_DFLT                      150
+#define SI_VCOSTEPPCT_DFLT                          20
+#define SI_ENDINGVCOSTEPPCT_DFLT                    5
+#define SI_REFERENCEDIVIDER_DFLT                    4
+
+#define SI_PM_NUMBER_OF_TC 15
+#define SI_PM_NUMBER_OF_SCLKS 20
+#define SI_PM_NUMBER_OF_MCLKS 4
+#define SI_PM_NUMBER_OF_VOLTAGE_LEVELS 4
+#define SI_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+
+/* XXX are these ok? */
+#define SI_TEMP_RANGE_MIN (90 * 1000)
+#define SI_TEMP_RANGE_MAX (120 * 1000)
+
+#define FDO_PWM_MODE_STATIC  1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+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
+};
+
+enum si_cac_config_reg_type
+{
+       SISLANDS_CACCONFIG_MMR = 0,
+       SISLANDS_CACCONFIG_CGIND,
+       SISLANDS_CACCONFIG_MAX
+};
+
+enum si_power_level {
+       SI_POWER_LEVEL_LOW = 0,
+       SI_POWER_LEVEL_MEDIUM = 1,
+       SI_POWER_LEVEL_HIGH = 2,
+       SI_POWER_LEVEL_CTXSW = 3,
+};
+
+enum si_td {
+       SI_TD_AUTO,
+       SI_TD_UP,
+       SI_TD_DOWN,
+};
+
+enum si_display_watermark {
+       SI_DISPLAY_WATERMARK_LOW = 0,
+       SI_DISPLAY_WATERMARK_HIGH = 1,
+};
+
+enum si_display_gap
+{
+    SI_PM_DISPLAY_GAP_VBLANK_OR_WM = 0,
+    SI_PM_DISPLAY_GAP_VBLANK       = 1,
+    SI_PM_DISPLAY_GAP_WATERMARK    = 2,
+    SI_PM_DISPLAY_GAP_IGNORE       = 3,
+};
+
+extern const struct amd_ip_funcs si_dpm_ip_funcs;
+
+struct ni_leakage_coeffients
+{
+       u32 at;
+       u32 bt;
+       u32 av;
+       u32 bv;
+       s32 t_slope;
+       s32 t_intercept;
+       u32 t_ref;
+};
+
+struct SMC_Evergreen_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_Evergreen_MCRegisterAddress SMC_Evergreen_MCRegisterAddress;
+
+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 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;
+
+struct SMC_NIslands_MCRegisterSet
+{
+    uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet;
+
+struct ni_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct SMC_NIslands_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress;
+
+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 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 at {
+       u32 rlp;
+       u32 rmp;
+       u32 lhp;
+       u32 lmp;
+};
+
+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 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;
+};
+
+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;
+
+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;
+
+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;
+
+struct vddc_table_entry {
+       u16 vddc;
+       u8 vddc_index;
+       u8 high_smio;
+       u32 low_smio;
+};
+
+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 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;
+       u32 rlp;
+       u32 rmp;
+       u32 lhp;
+       u32 lmp;
+       /* 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;
+       enum amdgpu_pcie_gen pcie_gen; /* si+ only */
+};
+
+struct rv7xx_ps {
+       struct rv7xx_pl high;
+       struct rv7xx_pl medium;
+       struct rv7xx_pl low;
+       bool dc_compatible;
+};
+
+struct si_ps {
+       u16 performance_level_count;
+       bool dc_compatible;
+       struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+
+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];
+};
+
+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 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;
+       bool smu_uvd_hs;
+       bool uvd_enabled;
+       /* 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;
+       struct at ats[2];
+       /* smc offsets */
+       u16 mc_reg_table_start;
+       struct amdgpu_ps current_rps;
+       struct rv7xx_ps current_ps;
+       struct amdgpu_ps requested_rps;
+       struct rv7xx_ps requested_ps;
+};
+
+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;
+};
+
+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;
+
+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;
+
+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 si_ps current_ps;
+       struct si_ps requested_ps;
+       /* scratch structs */
+       SMC_NIslands_MCRegisters smc_mc_reg_table;
+       NISLANDS_SMC_STATETABLE smc_statetable;
+};
+
+struct si_cac_config_reg
+{
+       u32 offset;
+       u32 mask;
+       u32 shift;
+       u32 value;
+       enum si_cac_config_reg_type type;
+};
+
+struct si_powertune_data
+{
+       u32 cac_window;
+       u32 l2_lta_window_size_default;
+       u8 lts_truncate_default;
+       u8 shift_n_default;
+       u8 operating_temp;
+       struct ni_leakage_coeffients leakage_coefficients;
+       u32 fixed_kt;
+       u32 lkge_lut_v0_percent;
+       u8 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
+       bool enable_powertune_by_default;
+};
+
+struct si_dyn_powertune_data
+{
+       u32 cac_leakage;
+       s32 leakage_minimum_temperature;
+       u32 wintime;
+       u32 l2_lta_window_size;
+       u8 lts_truncate;
+       u8 shift_n;
+       u8 dc_pwr_value;
+       bool disable_uvd_powertune;
+};
+
+struct si_dte_data
+{
+       u32 tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 r[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+       u32 k;
+       u32 t0;
+       u32 max_t;
+       u8 window_size;
+       u8 temp_select;
+       u8 dte_mode;
+       u8 tdep_count;
+       u8 t_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 tdep_r[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+       u32 t_threshold;
+       bool enable_dte_by_default;
+};
+
+struct si_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 dll_cntl;
+       u32 mclk_pwrmgt_cntl;
+       u32 mpll_ad_func_cntl;
+       u32 mpll_dq_func_cntl;
+       u32 mpll_func_cntl;
+       u32 mpll_func_cntl_1;
+       u32 mpll_func_cntl_2;
+       u32 mpll_ss1;
+       u32 mpll_ss2;
+};
+
+struct si_mc_reg_entry {
+       u32 mclk_max;
+       u32 mc_data[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_mc_reg_table {
+       u8 last;
+       u8 num_entries;
+       u16 valid_flag;
+       struct si_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct si_leakage_voltage_entry
+{
+       u16 voltage;
+       u16 leakage_index;
+};
+
+struct si_leakage_voltage
+{
+       u16 count;
+       struct si_leakage_voltage_entry entries[SISLANDS_MAX_LEAKAGE_COUNT];
+};
+
+
+struct si_ulv_param {
+       bool supported;
+       u32 cg_ulv_control;
+       u32 cg_ulv_parameter;
+       u32 volt_change_delay;
+       struct rv7xx_pl pl;
+       bool one_pcie_lane_in_ulv;
+};
+
+struct si_power_info {
+       /* must be first! */
+       struct ni_power_info ni;
+       struct si_clock_registers clock_registers;
+       struct si_mc_reg_table mc_reg_table;
+       struct atom_voltage_table mvdd_voltage_table;
+       struct atom_voltage_table vddc_phase_shed_table;
+       struct si_leakage_voltage leakage_voltage;
+       u16 mvdd_bootup_value;
+       struct si_ulv_param ulv;
+       u32 max_cu;
+       /* pcie gen */
+       enum amdgpu_pcie_gen force_pcie_gen;
+       enum amdgpu_pcie_gen boot_pcie_gen;
+       enum amdgpu_pcie_gen acpi_pcie_gen;
+       u32 sys_pcie_mask;
+       /* flags */
+       bool enable_dte;
+       bool enable_ppm;
+       bool vddc_phase_shed_control;
+       bool pspp_notify_required;
+       bool sclk_deep_sleep_above_low;
+       bool voltage_control_svi2;
+       bool vddci_control_svi2;
+       /* smc offsets */
+       u32 sram_end;
+       u32 state_table_start;
+       u32 soft_regs_start;
+       u32 mc_reg_table_start;
+       u32 arb_table_start;
+       u32 cac_table_start;
+       u32 dte_table_start;
+       u32 spll_table_start;
+       u32 papm_cfg_table_start;
+       u32 fan_table_start;
+       /* CAC stuff */
+       const struct si_cac_config_reg *cac_weights;
+       const struct si_cac_config_reg *lcac_config;
+       const struct si_cac_config_reg *cac_override;
+       const struct si_powertune_data *powertune_data;
+       struct si_dyn_powertune_data dyn_powertune_data;
+       /* DTE stuff */
+       struct si_dte_data dte_data;
+       /* scratch structs */
+       SMC_SIslands_MCRegisters smc_mc_reg_table;
+       SISLANDS_SMC_STATETABLE smc_statetable;
+       PP_SIslands_PAPMParameters papm_parm;
+       /* SVI2 */
+       u8 svd_gpio_id;
+       u8 svc_gpio_id;
+       /* fan control */
+       bool fan_ctrl_is_in_default_mode;
+       u32 t_min;
+       u32 fan_ctrl_default_mode;
+       bool fan_is_controlled_by_smc;
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.c b/drivers/gpu/drm/amd/amdgpu/si_ih.c
new file mode 100644 (file)
index 0000000..8fae3d4
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Copyright 2015 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 "amdgpu.h"
+#include "amdgpu_ih.h"
+#include "si/sid.h"
+#include "si_ih.h"
+
+static void si_ih_set_interrupt_funcs(struct amdgpu_device *adev);
+
+static void si_ih_enable_interrupts(struct amdgpu_device *adev)
+{
+       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);
+       adev->irq.ih.enabled = true;
+}
+  
+static void si_ih_disable_interrupts(struct amdgpu_device *adev)
+{
+       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);
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+       adev->irq.ih.enabled = false;
+       adev->irq.ih.rptr = 0;
+}
+
+static int si_ih_irq_init(struct amdgpu_device *adev)
+{
+       int rb_bufsz;
+       u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
+       u64 wptr_off;
+
+       si_ih_disable_interrupts(adev);
+       WREG32(INTERRUPT_CNTL2, adev->irq.ih.gpu_addr >> 8);
+       interrupt_cntl = RREG32(INTERRUPT_CNTL);
+       interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
+       interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
+       WREG32(INTERRUPT_CNTL, interrupt_cntl);
+
+       WREG32(IH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
+       rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
+
+       ih_rb_cntl = IH_WPTR_OVERFLOW_ENABLE |
+                    IH_WPTR_OVERFLOW_CLEAR |
+                    (rb_bufsz << 1) |
+                    IH_WPTR_WRITEBACK_ENABLE;
+
+       wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
+       WREG32(IH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
+       WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
+       WREG32(IH_RB_CNTL, ih_rb_cntl);
+       WREG32(IH_RB_RPTR, 0);
+       WREG32(IH_RB_WPTR, 0);
+
+       ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0);
+       if (adev->irq.msi_enabled)
+               ih_cntl |= RPTR_REARM;
+       WREG32(IH_CNTL, ih_cntl);
+
+       pci_set_master(adev->pdev);
+       si_ih_enable_interrupts(adev);
+
+       return 0;
+}
+
+static void si_ih_irq_disable(struct amdgpu_device *adev)
+{
+       si_ih_disable_interrupts(adev);
+       mdelay(1);
+}
+
+static u32 si_ih_get_wptr(struct amdgpu_device *adev)
+{
+       u32 wptr, tmp;
+
+       wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
+
+       if (wptr & IH_RB_WPTR__RB_OVERFLOW_MASK) {
+               wptr &= ~IH_RB_WPTR__RB_OVERFLOW_MASK;
+               dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
+                       wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
+               adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
+               tmp = RREG32(IH_RB_CNTL);
+               tmp |= IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK;
+               WREG32(IH_RB_CNTL, tmp);
+       }
+       return (wptr & adev->irq.ih.ptr_mask);
+}
+
+static void si_ih_decode_iv(struct amdgpu_device *adev,
+                            struct amdgpu_iv_entry *entry)
+{
+       u32 ring_index = adev->irq.ih.rptr >> 2;
+       uint32_t dw[4];
+
+       dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
+       dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
+       dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
+       dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
+
+       entry->src_id = dw[0] & 0xff;
+       entry->src_data = dw[1] & 0xfffffff;
+       entry->ring_id = dw[2] & 0xff;
+       entry->vm_id = (dw[2] >> 8) & 0xff;
+
+       adev->irq.ih.rptr += 16;
+}
+
+static void si_ih_set_rptr(struct amdgpu_device *adev)
+{
+       WREG32(IH_RB_RPTR, adev->irq.ih.rptr);
+}
+
+static int si_ih_early_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       si_ih_set_interrupt_funcs(adev);
+
+       return 0;
+}
+
+static int si_ih_sw_init(void *handle)
+{
+       int r;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       r = amdgpu_ih_ring_init(adev, 64 * 1024, false);
+       if (r)
+               return r;
+
+       return amdgpu_irq_init(adev);
+}
+
+static int si_ih_sw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       amdgpu_irq_fini(adev);
+       amdgpu_ih_ring_fini(adev);
+
+       return 0;
+}
+
+static int si_ih_hw_init(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_ih_irq_init(adev);
+}
+
+static int si_ih_hw_fini(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       si_ih_irq_disable(adev);
+
+       return 0;
+}
+
+static int si_ih_suspend(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_ih_hw_fini(adev);
+}
+
+static int si_ih_resume(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       return si_ih_hw_init(adev);
+}
+
+static bool si_ih_is_idle(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 tmp = RREG32(SRBM_STATUS);
+
+       if (tmp & SRBM_STATUS__IH_BUSY_MASK)
+               return false;
+
+       return true;
+}
+
+static int si_ih_wait_for_idle(void *handle)
+{
+       unsigned i;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               if (si_ih_is_idle(handle))
+                       return 0;
+               udelay(1);
+       }
+       return -ETIMEDOUT;
+}
+
+static int si_ih_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       u32 srbm_soft_reset = 0;
+       u32 tmp = RREG32(SRBM_STATUS);
+
+       if (tmp & SRBM_STATUS__IH_BUSY_MASK)
+               srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_IH_MASK;
+
+       if (srbm_soft_reset) {
+               tmp = RREG32(SRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(adev->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);
+
+               udelay(50);
+       }
+
+       return 0;
+}
+
+static int si_ih_set_clockgating_state(void *handle,
+                                         enum amd_clockgating_state state)
+{
+       return 0;
+}
+
+static int si_ih_set_powergating_state(void *handle,
+                                         enum amd_powergating_state state)
+{
+       return 0;
+}
+
+const struct amd_ip_funcs si_ih_ip_funcs = {
+       .name = "si_ih",
+       .early_init = si_ih_early_init,
+       .late_init = NULL,
+       .sw_init = si_ih_sw_init,
+       .sw_fini = si_ih_sw_fini,
+       .hw_init = si_ih_hw_init,
+       .hw_fini = si_ih_hw_fini,
+       .suspend = si_ih_suspend,
+       .resume = si_ih_resume,
+       .is_idle = si_ih_is_idle,
+       .wait_for_idle = si_ih_wait_for_idle,
+       .soft_reset = si_ih_soft_reset,
+       .set_clockgating_state = si_ih_set_clockgating_state,
+       .set_powergating_state = si_ih_set_powergating_state,
+};
+
+static const struct amdgpu_ih_funcs si_ih_funcs = {
+       .get_wptr = si_ih_get_wptr,
+       .decode_iv = si_ih_decode_iv,
+       .set_rptr = si_ih_set_rptr
+};
+
+static void si_ih_set_interrupt_funcs(struct amdgpu_device *adev)
+{
+       if (adev->irq.ih_funcs == NULL)
+               adev->irq.ih_funcs = &si_ih_funcs;
+}
+
diff --git a/drivers/gpu/drm/amd/amdgpu/si_ih.h b/drivers/gpu/drm/amd/amdgpu/si_ih.h
new file mode 100644 (file)
index 0000000..f3e3a95
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015 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 __SI_IH_H__
+#define __SI_IH_H__
+
+extern const struct amd_ip_funcs si_ih_ip_funcs;
+
+#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/si_smc.c b/drivers/gpu/drm/amd/amdgpu/si_smc.c
new file mode 100644 (file)
index 0000000..668ba99
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * 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 "amdgpu.h"
+#include "si/sid.h"
+#include "ppsmc.h"
+#include "amdgpu_ucode.h"
+#include "sislands_smc.h"
+
+static int si_set_smc_sram_address(struct amdgpu_device *adev,
+                                  u32 smc_address, u32 limit)
+{
+       if (smc_address & 3)
+               return -EINVAL;
+       if ((smc_address + 3) > limit)
+               return -EINVAL;
+
+       WREG32(SMC_IND_INDEX_0, smc_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+
+       return 0;
+}
+
+int amdgpu_si_copy_bytes_to_smc(struct amdgpu_device *adev,
+                               u32 smc_start_address,
+                               const u8 *src, u32 byte_count, u32 limit)
+{
+       unsigned long flags;
+       int ret = 0;
+       u32 data, original_data, addr, extra_shift;
+
+       if (smc_start_address & 3)
+               return -EINVAL;
+       if ((smc_start_address + byte_count) > limit)
+               return -EINVAL;
+
+       addr = smc_start_address;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       while (byte_count >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               ret = si_set_smc_sram_address(adev, addr, limit);
+               if (ret)
+                       goto done;
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       /* RMW for the final bytes */
+       if (byte_count > 0) {
+               data = 0;
+
+               ret = si_set_smc_sram_address(adev, addr, limit);
+               if (ret)
+                       goto done;
+
+               original_data = RREG32(SMC_IND_DATA_0);
+               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 = si_set_smc_sram_address(adev, addr, limit);
+               if (ret)
+                       goto done;
+
+               WREG32(SMC_IND_DATA_0, data);
+       }
+
+done:
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+       return ret;
+}
+
+void amdgpu_si_start_smc(struct amdgpu_device *adev)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+
+       tmp &= ~RST_REG;
+
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+void amdgpu_si_reset_smc(struct amdgpu_device *adev)
+{
+       u32 tmp;
+
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+       RREG32(CB_CGTT_SCLK_CTRL);
+
+       tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL) |
+             RST_REG;
+       WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp);
+}
+
+int amdgpu_si_program_jump_on_start(struct amdgpu_device *adev)
+{
+       static const u8 data[] = { 0x0E, 0x00, 0x40, 0x40 };
+
+       return amdgpu_si_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
+}
+
+void amdgpu_si_smc_clock(struct amdgpu_device *adev, bool enable)
+{
+       u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       if (enable)
+               tmp &= ~CK_DISABLE;
+       else
+               tmp |= CK_DISABLE;
+
+       WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp);
+}
+
+bool amdgpu_si_is_smc_running(struct amdgpu_device *adev)
+{
+       u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL);
+       u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+
+       if (!(rst & RST_REG) && !(clk & CK_DISABLE))
+               return true;
+
+       return false;
+}
+
+PPSMC_Result amdgpu_si_send_msg_to_smc(struct amdgpu_device *adev,
+                                      PPSMC_Msg msg)
+{
+       u32 tmp;
+       int i;
+
+       if (!amdgpu_si_is_smc_running(adev))
+               return PPSMC_Result_Failed;
+
+       WREG32(SMC_MESSAGE_0, msg);
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               tmp = RREG32(SMC_RESP_0);
+               if (tmp != 0)
+                       break;
+               udelay(1);
+       }
+
+       return (PPSMC_Result)RREG32(SMC_RESP_0);
+}
+
+PPSMC_Result amdgpu_si_wait_for_smc_inactive(struct amdgpu_device *adev)
+{
+       u32 tmp;
+       int i;
+
+       if (!amdgpu_si_is_smc_running(adev))
+               return PPSMC_Result_OK;
+
+       for (i = 0; i < adev->usec_timeout; i++) {
+               tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0);
+               if ((tmp & CKEN) == 0)
+                       break;
+               udelay(1);
+       }
+
+       return PPSMC_Result_OK;
+}
+
+int amdgpu_si_load_smc_ucode(struct amdgpu_device *adev, u32 limit)
+{
+       const struct smc_firmware_header_v1_0 *hdr;
+       unsigned long flags;
+       u32 ucode_start_address;
+       u32 ucode_size;
+       const u8 *src;
+       u32 data;
+
+       if (!adev->pm.fw)
+               return -EINVAL;
+
+       hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
+
+       amdgpu_ucode_print_smc_hdr(&hdr->header);
+
+       adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
+       ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
+       ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
+       src = (const u8 *)
+               (adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
+       if (ucode_size & 3)
+               return -EINVAL;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       WREG32(SMC_IND_INDEX_0, ucode_start_address);
+       WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0);
+       while (ucode_size >= 4) {
+               /* SMC address space is BE */
+               data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
+
+               WREG32(SMC_IND_DATA_0, data);
+
+               src += 4;
+               ucode_size -= 4;
+       }
+       WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0);
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+       return 0;
+}
+
+int amdgpu_si_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+                                 u32 *value, u32 limit)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       ret = si_set_smc_sram_address(adev, smc_address, limit);
+       if (ret == 0)
+               *value = RREG32(SMC_IND_DATA_0);
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+       return ret;
+}
+
+int amdgpu_si_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+                                  u32 value, u32 limit)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&adev->smc_idx_lock, flags);
+       ret = si_set_smc_sram_address(adev, smc_address, limit);
+       if (ret == 0)
+               WREG32(SMC_IND_DATA_0, value);
+       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/amd/amdgpu/sislands_smc.h b/drivers/gpu/drm/amd/amdgpu/sislands_smc.h
new file mode 100644 (file)
index 0000000..d2930ec
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2013 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_SISLANDS_SMC_H
+#define PP_SISLANDS_SMC_H
+
+#include "ppsmc.h"
+
+#pragma pack(push, 1)
+
+#define SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+
+struct PP_SIslands_Dpm2PerfLevel
+{
+    uint8_t MaxPS;
+    uint8_t TgtAct;
+    uint8_t MaxPS_StepInc;
+    uint8_t MaxPS_StepDec;
+    uint8_t PSSamplingTime;
+    uint8_t NearTDPDec;
+    uint8_t AboveSafeInc;
+    uint8_t BelowSafeInc;
+    uint8_t PSDeltaLimit;
+    uint8_t PSDeltaWin;
+    uint16_t PwrEfficiencyRatio;
+    uint8_t Reserved[4];
+};
+
+typedef struct PP_SIslands_Dpm2PerfLevel PP_SIslands_Dpm2PerfLevel;
+
+struct PP_SIslands_DPM2Status
+{
+    uint32_t    dpm2Flags;
+    uint8_t     CurrPSkip;
+    uint8_t     CurrPSkipPowerShift;
+    uint8_t     CurrPSkipTDP;
+    uint8_t     CurrPSkipOCP;
+    uint8_t     MaxSPLLIndex;
+    uint8_t     MinSPLLIndex;
+    uint8_t     CurrSPLLIndex;
+    uint8_t     InfSweepMode;
+    uint8_t     InfSweepDir;
+    uint8_t     TDPexceeded;
+    uint8_t     reserved;
+    uint8_t     SwitchDownThreshold;
+    uint32_t    SwitchDownCounter;
+    uint32_t    SysScalingFactor;
+};
+
+typedef struct PP_SIslands_DPM2Status PP_SIslands_DPM2Status;
+
+struct PP_SIslands_DPM2Parameters
+{
+    uint32_t    TDPLimit;
+    uint32_t    NearTDPLimit;
+    uint32_t    SafePowerLimit;
+    uint32_t    PowerBoostLimit;
+    uint32_t    MinLimitDelta;
+};
+typedef struct PP_SIslands_DPM2Parameters PP_SIslands_DPM2Parameters;
+
+struct PP_SIslands_PAPMStatus
+{
+    uint32_t    EstimatedDGPU_T;
+    uint32_t    EstimatedDGPU_P;
+    uint32_t    EstimatedAPU_T;
+    uint32_t    EstimatedAPU_P;
+    uint8_t     dGPU_T_Limit_Exceeded;
+    uint8_t     reserved[3];
+};
+typedef struct PP_SIslands_PAPMStatus PP_SIslands_PAPMStatus;
+
+struct PP_SIslands_PAPMParameters
+{
+    uint32_t    NearTDPLimitTherm;
+    uint32_t    NearTDPLimitPAPM;
+    uint32_t    PlatformPowerLimit;
+    uint32_t    dGPU_T_Limit;
+    uint32_t    dGPU_T_Warning;
+    uint32_t    dGPU_T_Hysteresis;
+};
+typedef struct PP_SIslands_PAPMParameters PP_SIslands_PAPMParameters;
+
+struct SISLANDS_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 SISLANDS_SMC_SCLK_VALUE SISLANDS_SMC_SCLK_VALUE;
+
+struct SISLANDS_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_DQ_FUNC_CNTL;
+    uint32_t    vMCLK_PWRMGT_CNTL;
+    uint32_t    vDLL_CNTL;
+    uint32_t    vMPLL_SS;
+    uint32_t    vMPLL_SS2;
+    uint32_t    mclk_value;
+};
+
+typedef struct SISLANDS_SMC_MCLK_VALUE SISLANDS_SMC_MCLK_VALUE;
+
+struct SISLANDS_SMC_VOLTAGE_VALUE
+{
+    uint16_t    value;
+    uint8_t     index;
+    uint8_t     phase_settings;
+};
+
+typedef struct SISLANDS_SMC_VOLTAGE_VALUE SISLANDS_SMC_VOLTAGE_VALUE;
+
+struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL
+{
+    uint8_t                     ACIndex;
+    uint8_t                     displayWatermark;
+    uint8_t                     gen2PCIE;
+    uint8_t                     UVDWatermark;
+    uint8_t                     VCEWatermark;
+    uint8_t                     strobeMode;
+    uint8_t                     mcFlags;
+    uint8_t                     padding;
+    uint32_t                    aT;
+    uint32_t                    bSP;
+    SISLANDS_SMC_SCLK_VALUE     sclk;
+    SISLANDS_SMC_MCLK_VALUE     mclk;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  mvdd;
+    SISLANDS_SMC_VOLTAGE_VALUE  vddci;
+    SISLANDS_SMC_VOLTAGE_VALUE  std_vddc;
+    uint8_t                     hysteresisUp;
+    uint8_t                     hysteresisDown;
+    uint8_t                     stateFlags;
+    uint8_t                     arbRefreshState;
+    uint32_t                    SQPowerThrottle;
+    uint32_t                    SQPowerThrottle_2;
+    uint32_t                    MaxPoweredUpCU;
+    SISLANDS_SMC_VOLTAGE_VALUE  high_temp_vddc;
+    SISLANDS_SMC_VOLTAGE_VALUE  low_temp_vddc;
+    uint32_t                    reserved[2];
+    PP_SIslands_Dpm2PerfLevel   dpm2;
+};
+
+#define SISLANDS_SMC_STROBE_RATIO    0x0F
+#define SISLANDS_SMC_STROBE_ENABLE   0x10
+
+#define SISLANDS_SMC_MC_EDC_RD_FLAG  0x01
+#define SISLANDS_SMC_MC_EDC_WR_FLAG  0x02
+#define SISLANDS_SMC_MC_RTT_ENABLE   0x04
+#define SISLANDS_SMC_MC_STUTTER_EN   0x08
+#define SISLANDS_SMC_MC_PG_EN        0x10
+
+typedef struct SISLANDS_SMC_HW_PERFORMANCE_LEVEL SISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+
+struct SISLANDS_SMC_SWSTATE
+{
+    uint8_t                             flags;
+    uint8_t                             levelCount;
+    uint8_t                             padding2;
+    uint8_t                             padding3;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   levels[1];
+};
+
+typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
+
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC  0
+#define SISLANDS_SMC_VOLTAGEMASK_MVDD  1
+#define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3
+#define SISLANDS_SMC_VOLTAGEMASK_MAX   4
+
+struct SISLANDS_SMC_VOLTAGEMASKTABLE
+{
+    uint32_t lowMask[SISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+
+typedef struct SISLANDS_SMC_VOLTAGEMASKTABLE SISLANDS_SMC_VOLTAGEMASKTABLE;
+
+#define SISLANDS_MAX_NO_VREG_STEPS 32
+
+struct SISLANDS_SMC_STATETABLE
+{
+    uint8_t                             thermalProtectType;
+    uint8_t                             systemFlags;
+    uint8_t                             maxVDDCIndexInPPTable;
+    uint8_t                             extraFlags;
+    uint32_t                            lowSMIO[SISLANDS_MAX_NO_VREG_STEPS];
+    SISLANDS_SMC_VOLTAGEMASKTABLE       voltageMaskTable;
+    SISLANDS_SMC_VOLTAGEMASKTABLE       phaseMaskTable;
+    PP_SIslands_DPM2Parameters          dpm2Params;
+    SISLANDS_SMC_SWSTATE                initialState;
+    SISLANDS_SMC_SWSTATE                ACPIState;
+    SISLANDS_SMC_SWSTATE                ULVState;
+    SISLANDS_SMC_SWSTATE                driverState;
+    SISLANDS_SMC_HW_PERFORMANCE_LEVEL   dpmLevels[SISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+
+typedef struct SISLANDS_SMC_STATETABLE SISLANDS_SMC_STATETABLE;
+
+#define SI_SMC_SOFT_REGISTER_mclk_chg_timeout         0x0
+#define SI_SMC_SOFT_REGISTER_delay_vreg               0xC
+#define SI_SMC_SOFT_REGISTER_delay_acpi               0x28
+#define SI_SMC_SOFT_REGISTER_seq_index                0x5C
+#define SI_SMC_SOFT_REGISTER_mvdd_chg_time            0x60
+#define SI_SMC_SOFT_REGISTER_mclk_switch_lim          0x70
+#define SI_SMC_SOFT_REGISTER_watermark_threshold      0x78
+#define SI_SMC_SOFT_REGISTER_phase_shedding_delay     0x88
+#define SI_SMC_SOFT_REGISTER_ulv_volt_change_delay    0x8C
+#define SI_SMC_SOFT_REGISTER_mc_block_delay           0x98
+#define SI_SMC_SOFT_REGISTER_ticks_per_us             0xA8
+#define SI_SMC_SOFT_REGISTER_crtc_index               0xC4
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_min 0xC8
+#define SI_SMC_SOFT_REGISTER_mclk_change_block_cp_max 0xCC
+#define SI_SMC_SOFT_REGISTER_non_ulv_pcie_link_width  0xF4
+#define SI_SMC_SOFT_REGISTER_tdr_is_about_to_happen   0xFC
+#define SI_SMC_SOFT_REGISTER_vr_hot_gpio              0x100
+#define SI_SMC_SOFT_REGISTER_svi_rework_plat_type     0x118
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svd   0x11c
+#define SI_SMC_SOFT_REGISTER_svi_rework_gpio_id_svc   0x120
+
+struct PP_SIslands_FanTable
+{
+       uint8_t  fdo_mode;
+       uint8_t  padding;
+       int16_t  temp_min;
+       int16_t  temp_med;
+       int16_t  temp_max;
+       int16_t  slope1;
+       int16_t  slope2;
+       int16_t  fdo_min;
+       int16_t  hys_up;
+       int16_t  hys_down;
+       int16_t  hys_slope;
+       int16_t  temp_resp_lim;
+       int16_t  temp_curr;
+       int16_t  slope_curr;
+       int16_t  pwm_curr;
+       uint32_t refresh_period;
+       int16_t  fdo_max;
+       uint8_t  temp_src;
+       int8_t  padding2;
+};
+
+typedef struct PP_SIslands_FanTable PP_SIslands_FanTable;
+
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 32
+
+#define SMC_SISLANDS_SCALE_I  7
+#define SMC_SISLANDS_SCALE_R 12
+
+struct PP_SIslands_CacConfig
+{
+    uint16_t   cac_lkge_lut[SMC_SISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_SISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
+    uint32_t   lkge_lut_V0;
+    uint32_t   lkge_lut_Vstep;
+    uint32_t   WinTime;
+    uint32_t   R_LL;
+    uint32_t   calculation_repeats;
+    uint32_t   l2numWin_TDP;
+    uint32_t   dc_cac;
+    uint8_t    lts_truncate_n;
+    uint8_t    SHIFT_N;
+    uint8_t    log2_PG_LKG_SCALE;
+    uint8_t    cac_temp;
+    uint32_t   lkge_lut_T0;
+    uint32_t   lkge_lut_Tstep;
+};
+
+typedef struct PP_SIslands_CacConfig PP_SIslands_CacConfig;
+
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE 16
+#define SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+
+struct SMC_SIslands_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMC_SIslands_MCRegisterAddress SMC_SIslands_MCRegisterAddress;
+
+struct SMC_SIslands_MCRegisterSet
+{
+    uint32_t value[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMC_SIslands_MCRegisterSet SMC_SIslands_MCRegisterSet;
+
+struct SMC_SIslands_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMC_SIslands_MCRegisterAddress      address[SMC_SISLANDS_MC_REGISTER_ARRAY_SIZE];
+    SMC_SIslands_MCRegisterSet          data[SMC_SISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMC_SIslands_MCRegisters SMC_SIslands_MCRegisters;
+
+struct SMC_SIslands_MCArbDramTimingRegisterSet
+{
+    uint32_t mc_arb_dram_timing;
+    uint32_t mc_arb_dram_timing2;
+    uint8_t  mc_arb_rfsh_rate;
+    uint8_t  mc_arb_burst_time;
+    uint8_t  padding[2];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisterSet SMC_SIslands_MCArbDramTimingRegisterSet;
+
+struct SMC_SIslands_MCArbDramTimingRegisters
+{
+    uint8_t                                     arb_current;
+    uint8_t                                     reserved[3];
+    SMC_SIslands_MCArbDramTimingRegisterSet     data[16];
+};
+
+typedef struct SMC_SIslands_MCArbDramTimingRegisters SMC_SIslands_MCArbDramTimingRegisters;
+
+struct SMC_SISLANDS_SPLL_DIV_TABLE
+{
+    uint32_t    freq[256];
+    uint32_t    ss[256];
+};
+
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_MASK  0x01ffffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_MASK   0xfe000000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT  25
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_MASK   0x000fffff
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT  0
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_MASK   0xfff00000
+#define SMC_SISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT  20
+
+typedef struct SMC_SISLANDS_SPLL_DIV_TABLE SMC_SISLANDS_SPLL_DIV_TABLE;
+
+#define SMC_SISLANDS_DTE_MAX_FILTER_STAGES 5
+
+#define SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE 16
+
+struct Smc_SIslands_DTE_Configuration
+{
+    uint32_t tau[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t R[SMC_SISLANDS_DTE_MAX_FILTER_STAGES];
+    uint32_t K;
+    uint32_t T0;
+    uint32_t MaxT;
+    uint8_t  WindowSize;
+    uint8_t  Tdep_count;
+    uint8_t  temp_select;
+    uint8_t  DTE_mode;
+    uint8_t  T_limits[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_tau[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tdep_R[SMC_SISLANDS_DTE_MAX_TEMPERATURE_DEPENDENT_ARRAY_SIZE];
+    uint32_t Tthreshold;
+};
+
+typedef struct Smc_SIslands_DTE_Configuration Smc_SIslands_DTE_Configuration;
+
+#define SMC_SISLANDS_DTE_STATUS_FLAG_DTE_ON 1
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x10000
+
+#define SISLANDS_SMC_FIRMWARE_HEADER_version                   0x0
+#define SISLANDS_SMC_FIRMWARE_HEADER_flags                     0x4
+#define SISLANDS_SMC_FIRMWARE_HEADER_softRegisters             0xC
+#define SISLANDS_SMC_FIRMWARE_HEADER_stateTable                0x10
+#define SISLANDS_SMC_FIRMWARE_HEADER_fanTable                  0x14
+#define SISLANDS_SMC_FIRMWARE_HEADER_CacConfigTable            0x18
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable           0x24
+#define SISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x30
+#define SISLANDS_SMC_FIRMWARE_HEADER_spllTable                 0x38
+#define SISLANDS_SMC_FIRMWARE_HEADER_DteConfiguration          0x40
+#define SISLANDS_SMC_FIRMWARE_HEADER_PAPMParameters            0x48
+
+#pragma pack(pop)
+
+int amdgpu_si_copy_bytes_to_smc(struct amdgpu_device *adev,
+                               u32 smc_start_address,
+                               const u8 *src, u32 byte_count, u32 limit);
+void amdgpu_si_start_smc(struct amdgpu_device *adev);
+void amdgpu_si_reset_smc(struct amdgpu_device *adev);
+int amdgpu_si_program_jump_on_start(struct amdgpu_device *adev);
+void amdgpu_si_smc_clock(struct amdgpu_device *adev, bool enable);
+bool amdgpu_si_is_smc_running(struct amdgpu_device *adev);
+PPSMC_Result amdgpu_si_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg);
+PPSMC_Result amdgpu_si_wait_for_smc_inactive(struct amdgpu_device *adev);
+int amdgpu_si_load_smc_ucode(struct amdgpu_device *adev, u32 limit);
+int amdgpu_si_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+                                 u32 *value, u32 limit);
+int amdgpu_si_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
+                                  u32 value, u32 limit);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_dpm.c b/drivers/gpu/drm/amd/amdgpu/tonga_dpm.c
deleted file mode 100644 (file)
index f06f6f4..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "tonga_smum.h"
-
-MODULE_FIRMWARE("amdgpu/tonga_smc.bin");
-
-static void tonga_dpm_set_funcs(struct amdgpu_device *adev);
-
-static int tonga_dpm_early_init(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       tonga_dpm_set_funcs(adev);
-
-       return 0;
-}
-
-static int tonga_dpm_init_microcode(struct amdgpu_device *adev)
-{
-       char fw_name[30] = "amdgpu/tonga_smc.bin";
-       int err;
-       err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
-       if (err)
-               goto out;
-       err = amdgpu_ucode_validate(adev->pm.fw);
-
-out:
-       if (err) {
-               DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
-               release_firmware(adev->pm.fw);
-               adev->pm.fw = NULL;
-       }
-       return err;
-}
-
-static int tonga_dpm_sw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       ret = tonga_dpm_init_microcode(adev);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int tonga_dpm_sw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       release_firmware(adev->pm.fw);
-       adev->pm.fw = NULL;
-
-       return 0;
-}
-
-static int tonga_dpm_hw_init(void *handle)
-{
-       int ret;
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-
-       /* smu init only needs to be called at startup, not resume.
-        * It should be in sw_init, but requires the fw info gathered
-        * in sw_init from other IP modules.
-        */
-       ret = tonga_smu_init(adev);
-       if (ret) {
-               DRM_ERROR("SMU initialization failed\n");
-               goto fail;
-       }
-
-       ret = tonga_smu_start(adev);
-       if (ret) {
-               DRM_ERROR("SMU start failed\n");
-               goto fail;
-       }
-
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-
-fail:
-       adev->firmware.smu_load = false;
-       mutex_unlock(&adev->pm.mutex);
-       return -EINVAL;
-}
-
-static int tonga_dpm_hw_fini(void *handle)
-{
-       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-
-       mutex_lock(&adev->pm.mutex);
-       /* smu fini only needs to be called at teardown, not suspend.
-        * It should be in sw_fini, but we put it here for symmetry
-        * with smu init.
-        */
-       tonga_smu_fini(adev);
-       mutex_unlock(&adev->pm.mutex);
-       return 0;
-}
-
-static int tonga_dpm_suspend(void *handle)
-{
-       return tonga_dpm_hw_fini(handle);
-}
-
-static int tonga_dpm_resume(void *handle)
-{
-       return tonga_dpm_hw_init(handle);
-}
-
-static int tonga_dpm_set_clockgating_state(void *handle,
-                       enum amd_clockgating_state state)
-{
-       return 0;
-}
-
-static int tonga_dpm_set_powergating_state(void *handle,
-                       enum amd_powergating_state state)
-{
-       return 0;
-}
-
-const struct amd_ip_funcs tonga_dpm_ip_funcs = {
-       .name = "tonga_dpm",
-       .early_init = tonga_dpm_early_init,
-       .late_init = NULL,
-       .sw_init = tonga_dpm_sw_init,
-       .sw_fini = tonga_dpm_sw_fini,
-       .hw_init = tonga_dpm_hw_init,
-       .hw_fini = tonga_dpm_hw_fini,
-       .suspend = tonga_dpm_suspend,
-       .resume = tonga_dpm_resume,
-       .is_idle = NULL,
-       .wait_for_idle = NULL,
-       .soft_reset = NULL,
-       .set_clockgating_state = tonga_dpm_set_clockgating_state,
-       .set_powergating_state = tonga_dpm_set_powergating_state,
-};
-
-static const struct amdgpu_dpm_funcs tonga_dpm_funcs = {
-       .get_temperature = NULL,
-       .pre_set_power_state = NULL,
-       .set_power_state = NULL,
-       .post_set_power_state = NULL,
-       .display_configuration_changed = NULL,
-       .get_sclk = NULL,
-       .get_mclk = NULL,
-       .print_power_state = NULL,
-       .debugfs_print_current_performance_level = NULL,
-       .force_performance_level = NULL,
-       .vblank_too_short = NULL,
-       .powergate_uvd = NULL,
-};
-
-static void tonga_dpm_set_funcs(struct amdgpu_device *adev)
-{
-       if (NULL == adev->pm.funcs)
-               adev->pm.funcs = &tonga_dpm_funcs;
-}
index c920558..d127d59 100644 (file)
@@ -373,10 +373,10 @@ static int tonga_ih_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
-static int tonga_ih_soft_reset(void *handle)
+static int tonga_ih_check_soft_reset(void *handle)
 {
-       u32 srbm_soft_reset = 0;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
        u32 tmp = RREG32(mmSRBM_STATUS);
 
        if (tmp & SRBM_STATUS__IH_BUSY_MASK)
@@ -384,6 +384,48 @@ static int tonga_ih_soft_reset(void *handle)
                                                SOFT_RESET_IH, 1);
 
        if (srbm_soft_reset) {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang = true;
+               adev->irq.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang = false;
+               adev->irq.srbm_soft_reset = 0;
+       }
+
+       return 0;
+}
+
+static int tonga_ih_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+               return 0;
+
+       return tonga_ih_hw_fini(adev);
+}
+
+static int tonga_ih_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+               return 0;
+
+       return tonga_ih_hw_init(adev);
+}
+
+static int tonga_ih_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_IH].hang)
+               return 0;
+       srbm_soft_reset = adev->irq.srbm_soft_reset;
+
+       if (srbm_soft_reset) {
+               u32 tmp;
+
                tmp = RREG32(mmSRBM_SOFT_RESET);
                tmp |= srbm_soft_reset;
                dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
@@ -427,7 +469,10 @@ const struct amd_ip_funcs tonga_ih_ip_funcs = {
        .resume = tonga_ih_resume,
        .is_idle = tonga_ih_is_idle,
        .wait_for_idle = tonga_ih_wait_for_idle,
+       .check_soft_reset = tonga_ih_check_soft_reset,
+       .pre_soft_reset = tonga_ih_pre_soft_reset,
        .soft_reset = tonga_ih_soft_reset,
+       .post_soft_reset = tonga_ih_post_soft_reset,
        .set_clockgating_state = tonga_ih_set_clockgating_state,
        .set_powergating_state = tonga_ih_set_powergating_state,
 };
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_smc.c b/drivers/gpu/drm/amd/amdgpu/tonga_smc.c
deleted file mode 100644 (file)
index 940de18..0000000
+++ /dev/null
@@ -1,862 +0,0 @@
-/*
- * Copyright 2014 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 "amdgpu.h"
-#include "tonga_ppsmc.h"
-#include "tonga_smum.h"
-#include "smu_ucode_xfer_vi.h"
-#include "amdgpu_ucode.h"
-
-#include "smu/smu_7_1_2_d.h"
-#include "smu/smu_7_1_2_sh_mask.h"
-
-#define TONGA_SMC_SIZE 0x20000
-
-static int tonga_set_smc_sram_address(struct amdgpu_device *adev, uint32_t smc_address, uint32_t limit)
-{
-       uint32_t val;
-
-       if (smc_address & 3)
-               return -EINVAL;
-
-       if ((smc_address + 3) > limit)
-               return -EINVAL;
-
-       WREG32(mmSMC_IND_INDEX_0, smc_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       return 0;
-}
-
-static int tonga_copy_bytes_to_smc(struct amdgpu_device *adev, uint32_t smc_start_address, const uint8_t *src, uint32_t byte_count, uint32_t limit)
-{
-       uint32_t addr;
-       uint32_t data, orig_data;
-       int result = 0;
-       uint32_t extra_shift;
-       unsigned long flags;
-
-       if (smc_start_address & 3)
-               return -EINVAL;
-
-       if ((smc_start_address + byte_count) > limit)
-               return -EINVAL;
-
-       addr = smc_start_address;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       while (byte_count >= 4) {
-               /* Bytes are written into the SMC addres space with the MSB first */
-               data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
-
-               result = tonga_set_smc_sram_address(adev, addr, limit);
-
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-
-               src += 4;
-               byte_count -= 4;
-               addr += 4;
-       }
-
-       if (0 != byte_count) {
-               /* Now write odd bytes left, do a read modify write cycle */
-               data = 0;
-
-               result = tonga_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               orig_data = RREG32(mmSMC_IND_DATA_0);
-               extra_shift = 8 * (4 - byte_count);
-
-               while (byte_count > 0) {
-                       data = (data << 8) + *src++;
-                       byte_count--;
-               }
-
-               data <<= extra_shift;
-               data |= (orig_data & ~((~0UL) << extra_shift));
-
-               result = tonga_set_smc_sram_address(adev, addr, limit);
-               if (result)
-                       goto out;
-
-               WREG32(mmSMC_IND_DATA_0, data);
-       }
-
-out:
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int tonga_program_jump_on_start(struct amdgpu_device *adev)
-{
-       static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
-       tonga_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
-
-       return 0;
-}
-
-static bool tonga_is_smc_ram_running(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
-
-       return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
-}
-
-static int wait_smu_response(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32(mmSMC_RESP_0);
-               if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int tonga_send_msg_to_smc_offset(struct amdgpu_device *adev)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, 0x20000);
-       WREG32(mmSMC_MESSAGE_0, PPSMC_MSG_Test);
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send message\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int tonga_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
-{
-       if (!tonga_is_smc_ram_running(adev))
-       {
-               return -EINVAL;
-       }
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send message\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int tonga_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
-                                               PPSMC_Msg msg)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-static int tonga_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
-                                               PPSMC_Msg msg,
-                                               uint32_t parameter)
-{
-       if (!tonga_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return tonga_send_msg_to_smc(adev, msg);
-}
-
-static int tonga_send_msg_to_smc_with_parameter_without_waiting(
-                                       struct amdgpu_device *adev,
-                                       PPSMC_Msg msg, uint32_t parameter)
-{
-       if (wait_smu_response(adev)) {
-               DRM_ERROR("Failed to send previous message\n");
-               return -EINVAL;
-       }
-
-       WREG32(mmSMC_MSG_ARG_0, parameter);
-
-       return tonga_send_msg_to_smc_without_waiting(adev, msg);
-}
-
-#if 0 /* not used yet */
-static int tonga_wait_for_smc_inactive(struct amdgpu_device *adev)
-{
-       int i;
-       uint32_t val;
-
-       if (!tonga_is_smc_ram_running(adev))
-               return -EINVAL;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-               if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout)
-               return -EINVAL;
-
-       return 0;
-}
-#endif
-
-static int tonga_smu_upload_firmware_image(struct amdgpu_device *adev)
-{
-       const struct smc_firmware_header_v1_0 *hdr;
-       uint32_t ucode_size;
-       uint32_t ucode_start_address;
-       const uint8_t *src;
-       uint32_t val;
-       uint32_t byte_count;
-       uint32_t *data;
-       unsigned long flags;
-
-       if (!adev->pm.fw)
-               return -EINVAL;
-
-       /* Skip SMC ucode loading on SR-IOV capable boards.
-        * vbios does this for us in asic_init in that case.
-        */
-       if (adev->virtualization.supports_sr_iov)
-               return 0;
-
-       hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
-       amdgpu_ucode_print_smc_hdr(&hdr->header);
-
-       adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
-       ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
-       ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
-       src = (const uint8_t *)
-               (adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
-
-       if (ucode_size & 3) {
-               DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
-               return -EINVAL;
-       }
-
-       if (ucode_size > TONGA_SMC_SIZE) {
-               DRM_ERROR("SMC address is beyond the SMC RAM area\n");
-               return -EINVAL;
-       }
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-
-       byte_count = ucode_size;
-       data = (uint32_t *)src;
-       for (; byte_count >= 4; data++, byte_count -= 4)
-               WREG32(mmSMC_IND_DATA_0, data[0]);
-
-       val = RREG32(mmSMC_IND_ACCESS_CNTL);
-       val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       WREG32(mmSMC_IND_ACCESS_CNTL, val);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-
-       return 0;
-}
-
-#if 0 /* not used yet */
-static int tonga_read_smc_sram_dword(struct amdgpu_device *adev,
-                               uint32_t smc_address,
-                               uint32_t *value,
-                               uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = tonga_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               *value = RREG32(mmSMC_IND_DATA_0);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int tonga_write_smc_sram_dword(struct amdgpu_device *adev,
-                               uint32_t smc_address,
-                               uint32_t value,
-                               uint32_t limit)
-{
-       int result;
-       unsigned long flags;
-
-       spin_lock_irqsave(&adev->smc_idx_lock, flags);
-       result = tonga_set_smc_sram_address(adev, smc_address, limit);
-       if (result == 0)
-               WREG32(mmSMC_IND_DATA_0, value);
-       spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
-       return result;
-}
-
-static int tonga_smu_stop_smc(struct amdgpu_device *adev)
-{
-       uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       return 0;
-}
-#endif
-
-static enum AMDGPU_UCODE_ID tonga_convert_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case UCODE_ID_SDMA0:
-                       return AMDGPU_UCODE_ID_SDMA0;
-               case UCODE_ID_SDMA1:
-                       return AMDGPU_UCODE_ID_SDMA1;
-               case UCODE_ID_CP_CE:
-                       return AMDGPU_UCODE_ID_CP_CE;
-               case UCODE_ID_CP_PFP:
-                       return AMDGPU_UCODE_ID_CP_PFP;
-               case UCODE_ID_CP_ME:
-                       return AMDGPU_UCODE_ID_CP_ME;
-               case UCODE_ID_CP_MEC:
-               case UCODE_ID_CP_MEC_JT1:
-                       return AMDGPU_UCODE_ID_CP_MEC1;
-               case UCODE_ID_CP_MEC_JT2:
-                       return AMDGPU_UCODE_ID_CP_MEC2;
-               case UCODE_ID_RLC_G:
-                       return AMDGPU_UCODE_ID_RLC_G;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return AMDGPU_UCODE_ID_MAXIMUM;
-       }
-}
-
-static int tonga_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
-                                               uint32_t fw_type,
-                                               struct SMU_Entry *entry)
-{
-       enum AMDGPU_UCODE_ID id = tonga_convert_fw_type(fw_type);
-       struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
-       const struct gfx_firmware_header_v1_0 *header = NULL;
-       uint64_t gpu_addr;
-       uint32_t data_size;
-
-       if (ucode->fw == NULL)
-               return -EINVAL;
-
-       gpu_addr  = ucode->mc_addr;
-       header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
-       data_size = le32_to_cpu(header->header.ucode_size_bytes);
-
-       if ((fw_type == UCODE_ID_CP_MEC_JT1) ||
-               (fw_type == UCODE_ID_CP_MEC_JT2)) {
-               gpu_addr += le32_to_cpu(header->jt_offset) << 2;
-               data_size = le32_to_cpu(header->jt_size) << 2;
-       }
-
-       entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
-       entry->id = (uint16_t)fw_type;
-       entry->image_addr_high = upper_32_bits(gpu_addr);
-       entry->image_addr_low = lower_32_bits(gpu_addr);
-       entry->meta_data_addr_high = 0;
-       entry->meta_data_addr_low = 0;
-       entry->data_size_byte = data_size;
-       entry->num_register_entries = 0;
-
-       if (fw_type == UCODE_ID_RLC_G)
-               entry->flags = 1;
-       else
-               entry->flags = 0;
-
-       return 0;
-}
-
-static int tonga_smu_request_load_fw(struct amdgpu_device *adev)
-{
-       struct tonga_smu_private_data *private = (struct tonga_smu_private_data *)adev->smu.priv;
-       struct SMU_DRAMData_TOC *toc;
-       uint32_t fw_to_load;
-
-       WREG32_SMC(ixSOFT_REGISTERS_TABLE_28, 0);
-
-       tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_HI, private->smu_buffer_addr_high);
-       tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_LO, private->smu_buffer_addr_low);
-
-       toc = (struct SMU_DRAMData_TOC *)private->header;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       if (!adev->firmware.smu_load)
-               return 0;
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for RLC\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for CE\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for PFP\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for ME\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT2,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for MEC_JT2\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA0\n");
-               return -EINVAL;
-       }
-
-       if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
-                       &toc->entry[toc->num_entries++])) {
-               DRM_ERROR("Failed to get firmware entry for SDMA1\n");
-               return -EINVAL;
-       }
-
-       tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
-       tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK |
-                       UCODE_ID_SDMA0_MASK |
-                       UCODE_ID_SDMA1_MASK |
-                       UCODE_ID_CP_CE_MASK |
-                       UCODE_ID_CP_ME_MASK |
-                       UCODE_ID_CP_PFP_MASK |
-                       UCODE_ID_CP_MEC_MASK;
-
-       if (tonga_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
-               DRM_ERROR("Fail to request SMU load ucode\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static uint32_t tonga_smu_get_mask_for_fw_type(uint32_t fw_type)
-{
-       switch (fw_type) {
-               case AMDGPU_UCODE_ID_SDMA0:
-                       return UCODE_ID_SDMA0_MASK;
-               case AMDGPU_UCODE_ID_SDMA1:
-                       return UCODE_ID_SDMA1_MASK;
-               case AMDGPU_UCODE_ID_CP_CE:
-                       return UCODE_ID_CP_CE_MASK;
-               case AMDGPU_UCODE_ID_CP_PFP:
-                       return UCODE_ID_CP_PFP_MASK;
-               case AMDGPU_UCODE_ID_CP_ME:
-                       return UCODE_ID_CP_ME_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC1:
-                       return UCODE_ID_CP_MEC_MASK;
-               case AMDGPU_UCODE_ID_CP_MEC2:
-                       return UCODE_ID_CP_MEC_MASK;
-               case AMDGPU_UCODE_ID_RLC_G:
-                       return UCODE_ID_RLC_G_MASK;
-               default:
-                       DRM_ERROR("ucode type is out of range!\n");
-                       return 0;
-       }
-}
-
-static int tonga_smu_check_fw_load_finish(struct amdgpu_device *adev,
-                                       uint32_t fw_type)
-{
-       uint32_t fw_mask = tonga_smu_get_mask_for_fw_type(fw_type);
-       int i;
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_28) & fw_mask))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("check firmware loading failed\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int tonga_smu_start_in_protection_mode(struct amdgpu_device *adev)
-{
-       int result;
-       uint32_t val;
-       int i;
-
-       /* Assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       result = tonga_smu_upload_firmware_image(adev);
-       if (result)
-               return result;
-
-       /* Clear status */
-       WREG32_SMC(ixSMU_STATUS, 0);
-
-       /* Enable clock */
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       /* De-assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       /* Set SMU Auto Start */
-       val = RREG32_SMC(ixSMU_INPUT_DATA);
-       val = REG_SET_FIELD(val, SMU_INPUT_DATA, AUTO_START, 1);
-       WREG32_SMC(ixSMU_INPUT_DATA, val);
-
-       /* Clear firmware interrupt enable flag */
-       WREG32_SMC(ixFIRMWARE_FLAGS, 0);
-
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixRCU_UC_EVENTS);
-               if (REG_GET_FIELD(val, RCU_UC_EVENTS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Interrupt is not enabled by firmware\n");
-               return -EINVAL;
-       }
-
-       /* Call Test SMU message with 0x20000 offset
-        * to trigger SMU start
-        */
-       tonga_send_msg_to_smc_offset(adev);
-
-       /* Wait for done bit to be set */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixSMU_STATUS);
-               if (REG_GET_FIELD(val, SMU_STATUS, SMU_DONE))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Timeout for SMU start\n");
-               return -EINVAL;
-       }
-
-       /* Check pass/failed indicator */
-       val = RREG32_SMC(ixSMU_STATUS);
-       if (!REG_GET_FIELD(val, SMU_STATUS, SMU_PASS)) {
-               DRM_ERROR("SMU Firmware start failed\n");
-               return -EINVAL;
-       }
-
-       /* Wait for firmware to initialize */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixFIRMWARE_FLAGS);
-               if(REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("SMU firmware initialization failed\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int tonga_smu_start_in_non_protection_mode(struct amdgpu_device *adev)
-{
-       int i, result;
-       uint32_t val;
-
-       /* wait for smc boot up */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixRCU_UC_EVENTS);
-               val = REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done);
-               if (val)
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("SMC boot sequence is not completed\n");
-               return -EINVAL;
-       }
-
-       /* Clear firmware interrupt enable flag */
-       WREG32_SMC(ixFIRMWARE_FLAGS, 0);
-
-       /* Assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       result = tonga_smu_upload_firmware_image(adev);
-       if (result)
-               return result;
-
-       /* Set smc instruct start point at 0x0 */
-       tonga_program_jump_on_start(adev);
-
-       /* Enable clock */
-       val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
-       val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
-       WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
-
-       /* De-assert reset */
-       val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
-       val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
-       WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
-
-       /* Wait for firmware to initialize */
-       for (i = 0; i < adev->usec_timeout; i++) {
-               val = RREG32_SMC(ixFIRMWARE_FLAGS);
-               if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
-                       break;
-               udelay(1);
-       }
-
-       if (i == adev->usec_timeout) {
-               DRM_ERROR("Timeout for SMC firmware initialization\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-int tonga_smu_start(struct amdgpu_device *adev)
-{
-       int result;
-       uint32_t val;
-
-       if (!tonga_is_smc_ram_running(adev)) {
-               val = RREG32_SMC(ixSMU_FIRMWARE);
-               if (!REG_GET_FIELD(val, SMU_FIRMWARE, SMU_MODE)) {
-                       result = tonga_smu_start_in_non_protection_mode(adev);
-                       if (result)
-                               return result;
-               } else {
-                       result = tonga_smu_start_in_protection_mode(adev);
-                       if (result)
-                               return result;
-               }
-       }
-
-       return tonga_smu_request_load_fw(adev);
-}
-
-static const struct amdgpu_smumgr_funcs tonga_smumgr_funcs = {
-       .check_fw_load_finish = tonga_smu_check_fw_load_finish,
-       .request_smu_load_fw = NULL,
-       .request_smu_specific_fw = NULL,
-};
-
-int tonga_smu_init(struct amdgpu_device *adev)
-{
-       struct tonga_smu_private_data *private;
-       uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       uint32_t smu_internal_buffer_size = 200*4096;
-       struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
-       struct amdgpu_bo **smu_buf = &adev->smu.smu_buf;
-       uint64_t mc_addr;
-       void *toc_buf_ptr;
-       void *smu_buf_ptr;
-       int ret;
-
-       private = kzalloc(sizeof(struct tonga_smu_private_data), GFP_KERNEL);
-       if (NULL == private)
-               return -ENOMEM;
-
-       /* allocate firmware buffers */
-       if (adev->firmware.smu_load)
-               amdgpu_ucode_init_bo(adev);
-
-       adev->smu.priv = private;
-       adev->smu.fw_flags = 0;
-
-       /* Allocate FW image data structure and header buffer */
-       ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
-                              true, AMDGPU_GEM_DOMAIN_VRAM,
-                              AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                              NULL, NULL, toc_buf);
-       if (ret) {
-               DRM_ERROR("Failed to allocate memory for TOC buffer\n");
-               return -ENOMEM;
-       }
-
-       /* Allocate buffer for SMU internal buffer */
-       ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE,
-                              true, AMDGPU_GEM_DOMAIN_VRAM,
-                              AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED,
-                              NULL, NULL, smu_buf);
-       if (ret) {
-               DRM_ERROR("Failed to allocate memory for SMU internal buffer\n");
-               return -ENOMEM;
-       }
-
-       /* Retrieve GPU address for header buffer and internal buffer */
-       ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
-       if (ret) {
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to reserve the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to pin the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.toc_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to map the TOC buffer\n");
-               return -EINVAL;
-       }
-
-       amdgpu_bo_unreserve(adev->smu.toc_buf);
-       private->header_addr_low = lower_32_bits(mc_addr);
-       private->header_addr_high = upper_32_bits(mc_addr);
-       private->header = toc_buf_ptr;
-
-       ret = amdgpu_bo_reserve(adev->smu.smu_buf, false);
-       if (ret) {
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to reserve the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to pin the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr);
-       if (ret) {
-               amdgpu_bo_unreserve(adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.smu_buf);
-               amdgpu_bo_unref(&adev->smu.toc_buf);
-               DRM_ERROR("Failed to map the SMU internal buffer\n");
-               return -EINVAL;
-       }
-
-       amdgpu_bo_unreserve(adev->smu.smu_buf);
-       private->smu_buffer_addr_low = lower_32_bits(mc_addr);
-       private->smu_buffer_addr_high = upper_32_bits(mc_addr);
-
-       adev->smu.smumgr_funcs = &tonga_smumgr_funcs;
-
-       return 0;
-}
-
-int tonga_smu_fini(struct amdgpu_device *adev)
-{
-       amdgpu_bo_unref(&adev->smu.toc_buf);
-       amdgpu_bo_unref(&adev->smu.smu_buf);
-       kfree(adev->smu.priv);
-       adev->smu.priv = NULL;
-       if (adev->firmware.fw_buf)
-               amdgpu_ucode_fini_bo(adev);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_smum.h b/drivers/gpu/drm/amd/amdgpu/tonga_smum.h
deleted file mode 100644 (file)
index c031ff9..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2014 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 TONGA_SMUMGR_H
-#define TONGA_SMUMGR_H
-
-#include "tonga_ppsmc.h"
-
-int tonga_smu_init(struct amdgpu_device *adev);
-int tonga_smu_fini(struct amdgpu_device *adev);
-int tonga_smu_start(struct amdgpu_device *adev);
-
-struct tonga_smu_private_data
-{
-       uint8_t *header;
-       uint32_t smu_buffer_addr_high;
-       uint32_t smu_buffer_addr_low;
-       uint32_t header_addr_high;
-       uint32_t header_addr_low;
-};
-
-#endif
index 132e613..f6c9415 100644 (file)
@@ -116,7 +116,7 @@ static int uvd_v4_2_sw_init(void *handle)
 
        ring = &adev->uvd.ring;
        sprintf(ring->name, "uvd");
-       r = amdgpu_ring_init(adev, ring, 512, CP_PACKET2, 0xf,
+       r = amdgpu_ring_init(adev, ring, 512, PACKET0(mmUVD_NO_OP, 0), 0xf,
                             &adev->uvd.irq, 0, AMDGPU_RING_TYPE_UVD);
 
        return r;
@@ -526,6 +526,20 @@ static void uvd_v4_2_ring_emit_ib(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, ib->length_dw);
 }
 
+static unsigned uvd_v4_2_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               4; /* uvd_v4_2_ring_emit_ib */
+}
+
+static unsigned uvd_v4_2_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               2 + /* uvd_v4_2_ring_emit_hdp_flush */
+               2 + /* uvd_v4_2_ring_emit_hdp_invalidate */
+               14; /* uvd_v4_2_ring_emit_fence  x1 no user fence */
+}
+
 /**
  * uvd_v4_2_mc_resume - memory controller programming
  *
@@ -756,6 +770,8 @@ static const struct amdgpu_ring_funcs uvd_v4_2_ring_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_uvd_ring_begin_use,
        .end_use = amdgpu_uvd_ring_end_use,
+       .get_emit_ib_size = uvd_v4_2_ring_get_emit_ib_size,
+       .get_dma_frame_size = uvd_v4_2_ring_get_dma_frame_size,
 };
 
 static void uvd_v4_2_set_ring_funcs(struct amdgpu_device *adev)
index 101de13..400c16f 100644 (file)
@@ -112,7 +112,7 @@ static int uvd_v5_0_sw_init(void *handle)
 
        ring = &adev->uvd.ring;
        sprintf(ring->name, "uvd");
-       r = amdgpu_ring_init(adev, ring, 512, CP_PACKET2, 0xf,
+       r = amdgpu_ring_init(adev, ring, 512, PACKET0(mmUVD_NO_OP, 0), 0xf,
                             &adev->uvd.irq, 0, AMDGPU_RING_TYPE_UVD);
 
        return r;
@@ -577,6 +577,20 @@ static void uvd_v5_0_ring_emit_ib(struct amdgpu_ring *ring,
        amdgpu_ring_write(ring, ib->length_dw);
 }
 
+static unsigned uvd_v5_0_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               6; /* uvd_v5_0_ring_emit_ib */
+}
+
+static unsigned uvd_v5_0_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               2 + /* uvd_v5_0_ring_emit_hdp_flush */
+               2 + /* uvd_v5_0_ring_emit_hdp_invalidate */
+               14; /* uvd_v5_0_ring_emit_fence  x1 no user fence */
+}
+
 static bool uvd_v5_0_is_idle(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -807,6 +821,8 @@ static const struct amdgpu_ring_funcs uvd_v5_0_ring_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_uvd_ring_begin_use,
        .end_use = amdgpu_uvd_ring_end_use,
+       .get_emit_ib_size = uvd_v5_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = uvd_v5_0_ring_get_dma_frame_size,
 };
 
 static void uvd_v5_0_set_ring_funcs(struct amdgpu_device *adev)
index 7f21102..e0fd9f2 100644 (file)
@@ -116,7 +116,7 @@ static int uvd_v6_0_sw_init(void *handle)
 
        ring = &adev->uvd.ring;
        sprintf(ring->name, "uvd");
-       r = amdgpu_ring_init(adev, ring, 512, CP_PACKET2, 0xf,
+       r = amdgpu_ring_init(adev, ring, 512, PACKET0(mmUVD_NO_OP, 0), 0xf,
                             &adev->uvd.irq, 0, AMDGPU_RING_TYPE_UVD);
 
        return r;
@@ -396,21 +396,14 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
 
        uvd_v6_0_mc_resume(adev);
 
-       /* Set dynamic clock gating in S/W control mode */
-       if (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG) {
-               uvd_v6_0_set_sw_clock_gating(adev);
-       } else {
-               /* disable clock gating */
-               uint32_t data = RREG32(mmUVD_CGC_CTRL);
-               data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK;
-               WREG32(mmUVD_CGC_CTRL, data);
-       }
+       /* disable clock gating */
+       WREG32_FIELD(UVD_CGC_CTRL, DYN_CLOCK_MODE, 0);
 
        /* disable interupt */
-       WREG32_P(mmUVD_MASTINT_EN, 0, ~UVD_MASTINT_EN__VCPU_EN_MASK);
+       WREG32_FIELD(UVD_MASTINT_EN, VCPU_EN, 0);
 
        /* stall UMC and register bus before resetting VCPU */
-       WREG32_P(mmUVD_LMI_CTRL2, UVD_LMI_CTRL2__STALL_ARB_UMC_MASK, ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
+       WREG32_FIELD(UVD_LMI_CTRL2, STALL_ARB_UMC, 1);
        mdelay(1);
 
        /* put LMI, VCPU, RBC etc... into reset */
@@ -426,7 +419,7 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
        mdelay(5);
 
        /* take UVD block out of reset */
-       WREG32_P(mmSRBM_SOFT_RESET, 0, ~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
+       WREG32_FIELD(SRBM_SOFT_RESET, SOFT_RESET_UVD, 0);
        mdelay(5);
 
        /* initialize UVD memory controller */
@@ -461,7 +454,7 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
        WREG32(mmUVD_VCPU_CNTL, UVD_VCPU_CNTL__CLK_EN_MASK);
 
        /* enable UMC */
-       WREG32_P(mmUVD_LMI_CTRL2, 0, ~UVD_LMI_CTRL2__STALL_ARB_UMC_MASK);
+       WREG32_FIELD(UVD_LMI_CTRL2, STALL_ARB_UMC, 0);
 
        /* boot up the VCPU */
        WREG32(mmUVD_SOFT_RESET, 0);
@@ -481,11 +474,9 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
                        break;
 
                DRM_ERROR("UVD not responding, trying to reset the VCPU!!!\n");
-               WREG32_P(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK,
-                               ~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
+               WREG32_FIELD(UVD_SOFT_RESET, VCPU_SOFT_RESET, 1);
                mdelay(10);
-               WREG32_P(mmUVD_SOFT_RESET, 0,
-                        ~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
+               WREG32_FIELD(UVD_SOFT_RESET, VCPU_SOFT_RESET, 0);
                mdelay(10);
                r = -1;
        }
@@ -502,15 +493,14 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
        /* clear the bit 4 of UVD_STATUS */
        WREG32_P(mmUVD_STATUS, 0, ~(2 << UVD_STATUS__VCPU_REPORT__SHIFT));
 
+       /* force RBC into idle state */
        rb_bufsz = order_base_2(ring->ring_size);
-       tmp = 0;
-       tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BUFSZ, rb_bufsz);
+       tmp = REG_SET_FIELD(0, UVD_RBC_RB_CNTL, RB_BUFSZ, rb_bufsz);
        tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BLKSZ, 1);
        tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_FETCH, 1);
        tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_WPTR_POLL_EN, 0);
        tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_UPDATE, 1);
        tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
-       /* force RBC into idle state */
        WREG32(mmUVD_RBC_RB_CNTL, tmp);
 
        /* set the write pointer delay */
@@ -531,7 +521,7 @@ static int uvd_v6_0_start(struct amdgpu_device *adev)
        ring->wptr = RREG32(mmUVD_RBC_RB_RPTR);
        WREG32(mmUVD_RBC_RB_WPTR, ring->wptr);
 
-       WREG32_P(mmUVD_RBC_RB_CNTL, 0, ~UVD_RBC_RB_CNTL__RB_NO_FETCH_MASK);
+       WREG32_FIELD(UVD_RBC_RB_CNTL, RB_NO_FETCH, 0);
 
        return 0;
 }
@@ -735,6 +725,31 @@ static void uvd_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
        amdgpu_ring_write(ring, 0xE);
 }
 
+static unsigned uvd_v6_0_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               8; /* uvd_v6_0_ring_emit_ib */
+}
+
+static unsigned uvd_v6_0_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               2 + /* uvd_v6_0_ring_emit_hdp_flush */
+               2 + /* uvd_v6_0_ring_emit_hdp_invalidate */
+               10 + /* uvd_v6_0_ring_emit_pipeline_sync */
+               14; /* uvd_v6_0_ring_emit_fence x1 no user fence */
+}
+
+static unsigned uvd_v6_0_ring_get_dma_frame_size_vm(struct amdgpu_ring *ring)
+{
+       return
+               2 + /* uvd_v6_0_ring_emit_hdp_flush */
+               2 + /* uvd_v6_0_ring_emit_hdp_invalidate */
+               10 + /* uvd_v6_0_ring_emit_pipeline_sync */
+               20 + /* uvd_v6_0_ring_emit_vm_flush */
+               14 + 14; /* uvd_v6_0_ring_emit_fence x2 vm fence */
+}
+
 static bool uvd_v6_0_is_idle(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
@@ -748,20 +763,82 @@ static int uvd_v6_0_wait_for_idle(void *handle)
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        for (i = 0; i < adev->usec_timeout; i++) {
-               if (!(RREG32(mmSRBM_STATUS) & SRBM_STATUS__UVD_BUSY_MASK))
+               if (uvd_v6_0_is_idle(handle))
                        return 0;
        }
        return -ETIMEDOUT;
 }
 
-static int uvd_v6_0_soft_reset(void *handle)
+#define AMDGPU_UVD_STATUS_BUSY_MASK    0xfd
+static int uvd_v6_0_check_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
+       u32 tmp = RREG32(mmSRBM_STATUS);
+
+       if (REG_GET_FIELD(tmp, SRBM_STATUS, UVD_RQ_PENDING) ||
+           REG_GET_FIELD(tmp, SRBM_STATUS, UVD_BUSY) ||
+           (RREG32(mmUVD_STATUS) & AMDGPU_UVD_STATUS_BUSY_MASK))
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_UVD, 1);
+
+       if (srbm_soft_reset) {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang = true;
+               adev->uvd.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang = false;
+               adev->uvd.srbm_soft_reset = 0;
+       }
+       return 0;
+}
+static int uvd_v6_0_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+               return 0;
 
        uvd_v6_0_stop(adev);
+       return 0;
+}
+
+static int uvd_v6_0_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+               return 0;
+       srbm_soft_reset = adev->uvd.srbm_soft_reset;
+
+       if (srbm_soft_reset) {
+               u32 tmp;
+
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+
+               udelay(50);
+
+               tmp &= ~srbm_soft_reset;
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+
+               /* Wait a little for things to settle down */
+               udelay(50);
+       }
+
+       return 0;
+}
+
+static int uvd_v6_0_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_UVD].hang)
+               return 0;
 
-       WREG32_P(mmSRBM_SOFT_RESET, SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK,
-                       ~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
        mdelay(5);
 
        return uvd_v6_0_start(adev);
@@ -902,21 +979,15 @@ static int uvd_v6_0_set_clockgating_state(void *handle,
                                          enum amd_clockgating_state state)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-       bool enable = (state == AMD_CG_STATE_GATE) ? true : false;
-       static int curstate = -1;
 
        if (adev->asic_type == CHIP_FIJI ||
-                       adev->asic_type == CHIP_POLARIS10)
-               uvd_v6_set_bypass_mode(adev, enable);
+           adev->asic_type == CHIP_POLARIS10)
+               uvd_v6_set_bypass_mode(adev, state == AMD_CG_STATE_GATE ? true : false);
 
        if (!(adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG))
                return 0;
 
-       if (curstate == state)
-               return 0;
-
-       curstate = state;
-       if (enable) {
+       if (state == AMD_CG_STATE_GATE) {
                /* disable HW gating and enable Sw gating */
                uvd_v6_0_set_sw_clock_gating(adev);
        } else {
@@ -946,6 +1017,8 @@ static int uvd_v6_0_set_powergating_state(void *handle,
        if (!(adev->pg_flags & AMD_PG_SUPPORT_UVD))
                return 0;
 
+       WREG32(mmUVD_POWER_STATUS, UVD_POWER_STATUS__UVD_PG_EN_MASK);
+
        if (state == AMD_PG_STATE_GATE) {
                uvd_v6_0_stop(adev);
                return 0;
@@ -966,7 +1039,10 @@ const struct amd_ip_funcs uvd_v6_0_ip_funcs = {
        .resume = uvd_v6_0_resume,
        .is_idle = uvd_v6_0_is_idle,
        .wait_for_idle = uvd_v6_0_wait_for_idle,
+       .check_soft_reset = uvd_v6_0_check_soft_reset,
+       .pre_soft_reset = uvd_v6_0_pre_soft_reset,
        .soft_reset = uvd_v6_0_soft_reset,
+       .post_soft_reset = uvd_v6_0_post_soft_reset,
        .set_clockgating_state = uvd_v6_0_set_clockgating_state,
        .set_powergating_state = uvd_v6_0_set_powergating_state,
 };
@@ -986,6 +1062,8 @@ static const struct amdgpu_ring_funcs uvd_v6_0_ring_phys_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_uvd_ring_begin_use,
        .end_use = amdgpu_uvd_ring_end_use,
+       .get_emit_ib_size = uvd_v6_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = uvd_v6_0_ring_get_dma_frame_size,
 };
 
 static const struct amdgpu_ring_funcs uvd_v6_0_ring_vm_funcs = {
@@ -1005,6 +1083,8 @@ static const struct amdgpu_ring_funcs uvd_v6_0_ring_vm_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_uvd_ring_begin_use,
        .end_use = amdgpu_uvd_ring_end_use,
+       .get_emit_ib_size = uvd_v6_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = uvd_v6_0_ring_get_dma_frame_size_vm,
 };
 
 static void uvd_v6_0_set_ring_funcs(struct amdgpu_device *adev)
index 80a37a6..76e64ad 100644 (file)
 #include "amdgpu.h"
 #include "amdgpu_vce.h"
 #include "cikd.h"
-
 #include "vce/vce_2_0_d.h"
 #include "vce/vce_2_0_sh_mask.h"
-
+#include "smu/smu_7_0_1_d.h"
+#include "smu/smu_7_0_1_sh_mask.h"
 #include "oss/oss_2_0_d.h"
 #include "oss/oss_2_0_sh_mask.h"
 
 #define VCE_V2_0_FW_SIZE       (256 * 1024)
 #define VCE_V2_0_STACK_SIZE    (64 * 1024)
 #define VCE_V2_0_DATA_SIZE     (23552 * AMDGPU_MAX_VCE_HANDLES)
+#define VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK  0x02
 
 static void vce_v2_0_mc_resume(struct amdgpu_device *adev);
 static void vce_v2_0_set_ring_funcs(struct amdgpu_device *adev);
@@ -96,6 +97,49 @@ static void vce_v2_0_ring_set_wptr(struct amdgpu_ring *ring)
                WREG32(mmVCE_RB_WPTR2, ring->wptr);
 }
 
+static int vce_v2_0_lmi_clean(struct amdgpu_device *adev)
+{
+       int i, j;
+
+       for (i = 0; i < 10; ++i) {
+               for (j = 0; j < 100; ++j) {
+                       uint32_t status = RREG32(mmVCE_LMI_STATUS);
+
+                       if (status & 0x337f)
+                               return 0;
+                       mdelay(10);
+               }
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int vce_v2_0_firmware_loaded(struct amdgpu_device *adev)
+{
+       int i, j;
+
+       for (i = 0; i < 10; ++i) {
+               for (j = 0; j < 100; ++j) {
+                       uint32_t status = RREG32(mmVCE_STATUS);
+
+                       if (status & VCE_STATUS_VCPU_REPORT_FW_LOADED_MASK)
+                               return 0;
+                       mdelay(10);
+               }
+
+               DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
+               WREG32_P(mmVCE_SOFT_RESET,
+                       VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
+                       ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+               mdelay(10);
+               WREG32_P(mmVCE_SOFT_RESET, 0,
+                       ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+               mdelay(10);
+       }
+
+       return -ETIMEDOUT;
+}
+
 /**
  * vce_v2_0_start - start VCE block
  *
@@ -106,7 +150,7 @@ static void vce_v2_0_ring_set_wptr(struct amdgpu_ring *ring)
 static int vce_v2_0_start(struct amdgpu_device *adev)
 {
        struct amdgpu_ring *ring;
-       int i, j, r;
+       int r;
 
        vce_v2_0_mc_resume(adev);
 
@@ -127,36 +171,12 @@ static int vce_v2_0_start(struct amdgpu_device *adev)
        WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
        WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
 
-       WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, ~VCE_VCPU_CNTL__CLK_EN_MASK);
-
-       WREG32_P(mmVCE_SOFT_RESET,
-                VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
-                ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-
+       WREG32_FIELD(VCE_VCPU_CNTL, CLK_EN, 1);
+       WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 1);
        mdelay(100);
+       WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 0);
 
-       WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-
-       for (i = 0; i < 10; ++i) {
-               uint32_t status;
-               for (j = 0; j < 100; ++j) {
-                       status = RREG32(mmVCE_STATUS);
-                       if (status & 2)
-                               break;
-                       mdelay(10);
-               }
-               r = 0;
-               if (status & 2)
-                       break;
-
-               DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
-               WREG32_P(mmVCE_SOFT_RESET, VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
-                               ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-               mdelay(10);
-               WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-               mdelay(10);
-               r = -1;
-       }
+       r = vce_v2_0_firmware_loaded(adev);
 
        /* clear BUSY flag */
        WREG32_P(mmVCE_STATUS, 0, ~1);
@@ -173,6 +193,8 @@ static int vce_v2_0_early_init(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
+       adev->vce.num_rings = 2;
+
        vce_v2_0_set_ring_funcs(adev);
        vce_v2_0_set_irq_funcs(adev);
 
@@ -182,7 +204,7 @@ static int vce_v2_0_early_init(void *handle)
 static int vce_v2_0_sw_init(void *handle)
 {
        struct amdgpu_ring *ring;
-       int r;
+       int r, i;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        /* VCE */
@@ -199,19 +221,14 @@ static int vce_v2_0_sw_init(void *handle)
        if (r)
                return r;
 
-       ring = &adev->vce.ring[0];
-       sprintf(ring->name, "vce0");
-       r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
-                            &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
-       if (r)
-               return r;
-
-       ring = &adev->vce.ring[1];
-       sprintf(ring->name, "vce1");
-       r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
-                            &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
-       if (r)
-               return r;
+       for (i = 0; i < adev->vce.num_rings; i++) {
+               ring = &adev->vce.ring[i];
+               sprintf(ring->name, "vce%d", i);
+               r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
+                                    &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
+               if (r)
+                       return r;
+       }
 
        return r;
 }
@@ -234,29 +251,23 @@ static int vce_v2_0_sw_fini(void *handle)
 
 static int vce_v2_0_hw_init(void *handle)
 {
-       struct amdgpu_ring *ring;
-       int r;
+       int r, i;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
        r = vce_v2_0_start(adev);
+       /* this error mean vcpu not in running state, so just skip ring test, not stop driver initialize */
        if (r)
-/* this error mean vcpu not in running state, so just skip ring test, not stop driver initialize */
                return 0;
 
-       ring = &adev->vce.ring[0];
-       ring->ready = true;
-       r = amdgpu_ring_test_ring(ring);
-       if (r) {
-               ring->ready = false;
-               return r;
-       }
+       for (i = 0; i < adev->vce.num_rings; i++)
+               adev->vce.ring[i].ready = false;
 
-       ring = &adev->vce.ring[1];
-       ring->ready = true;
-       r = amdgpu_ring_test_ring(ring);
-       if (r) {
-               ring->ready = false;
-               return r;
+       for (i = 0; i < adev->vce.num_rings; i++) {
+               r = amdgpu_ring_test_ring(&adev->vce.ring[i]);
+               if (r)
+                       return r;
+               else
+                       adev->vce.ring[i].ready = true;
        }
 
        DRM_INFO("VCE initialized successfully.\n");
@@ -338,47 +349,50 @@ static void vce_v2_0_set_sw_cg(struct amdgpu_device *adev, bool gated)
 
 static void vce_v2_0_set_dyn_cg(struct amdgpu_device *adev, bool gated)
 {
-       u32 orig, tmp;
+       if (vce_v2_0_wait_for_idle(adev)) {
+               DRM_INFO("VCE is busy, Can't set clock gateing");
+               return;
+       }
 
-       if (gated) {
-               if (vce_v2_0_wait_for_idle(adev)) {
-                       DRM_INFO("VCE is busy, Can't set clock gateing");
-                       return;
-               }
-               WREG32_P(mmVCE_VCPU_CNTL, 0, ~VCE_VCPU_CNTL__CLK_EN_MASK);
-               WREG32_P(mmVCE_SOFT_RESET, VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-               mdelay(100);
-               WREG32(mmVCE_STATUS, 0);
-       } else {
-               WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, ~VCE_VCPU_CNTL__CLK_EN_MASK);
-               WREG32_P(mmVCE_SOFT_RESET, VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
-               mdelay(100);
+       WREG32_P(mmVCE_LMI_CTRL2, 0x100, ~0x100);
+
+       if (vce_v2_0_lmi_clean(adev)) {
+               DRM_INFO("LMI is busy, Can't set clock gateing");
+               return;
        }
 
-       tmp = RREG32(mmVCE_CLOCK_GATING_B);
-       tmp &= ~0x00060006;
+       WREG32_P(mmVCE_VCPU_CNTL, 0, ~VCE_VCPU_CNTL__CLK_EN_MASK);
+       WREG32_P(mmVCE_SOFT_RESET,
+                VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
+                ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+       WREG32(mmVCE_STATUS, 0);
+
+       if (gated)
+               WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0);
+       /* LMI_MC/LMI_UMC always set in dynamic, set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {0, 0} */
        if (gated) {
-               tmp |= 0xe10000;
+               /* Force CLOCK OFF , set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {*, 1} */
+               WREG32(mmVCE_CLOCK_GATING_B, 0xe90010);
        } else {
-               tmp |= 0xe1;
-               tmp &= ~0xe10000;
+               /* Force CLOCK ON, set {CGC_*_GATE_MODE, CGC_*_SW_GATE} = {1, 0} */
+               WREG32(mmVCE_CLOCK_GATING_B, 0x800f1);
        }
-       WREG32(mmVCE_CLOCK_GATING_B, tmp);
 
-       orig = tmp = RREG32(mmVCE_UENC_CLOCK_GATING);
-       tmp &= ~0x1fe000;
-       tmp &= ~0xff000000;
-       if (tmp != orig)
-               WREG32(mmVCE_UENC_CLOCK_GATING, tmp);
+       /* Set VCE_UENC_CLOCK_GATING always in dynamic mode {*_FORCE_ON, *_FORCE_OFF} = {0, 0}*/;
+       WREG32(mmVCE_UENC_CLOCK_GATING, 0x40);
 
-       orig = tmp = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
-       tmp &= ~0x3fc;
-       if (tmp != orig)
-               WREG32(mmVCE_UENC_REG_CLOCK_GATING, tmp);
+       /* set VCE_UENC_REG_CLOCK_GATING always in dynamic mode */
+       WREG32(mmVCE_UENC_REG_CLOCK_GATING, 0x00);
 
-       if (gated)
-               WREG32(mmVCE_CGTT_CLK_OVERRIDE, 0);
-       WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+       WREG32_P(mmVCE_LMI_CTRL2, 0, ~0x100);
+       if(!gated) {
+               WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, ~VCE_VCPU_CNTL__CLK_EN_MASK);
+               mdelay(100);
+               WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+
+               vce_v2_0_firmware_loaded(adev);
+               WREG32_P(mmVCE_STATUS, 0, ~VCE_STATUS__JOB_BUSY_MASK);
+       }
 }
 
 static void vce_v2_0_disable_cg(struct amdgpu_device *adev)
@@ -458,9 +472,7 @@ static void vce_v2_0_mc_resume(struct amdgpu_device *adev)
        WREG32(mmVCE_VCPU_CACHE_SIZE2, size);
 
        WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
-
-       WREG32_P(mmVCE_SYS_INT_EN, VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK,
-                ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK);
+       WREG32_FIELD(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN, 1);
 
        vce_v2_0_init_cg(adev);
 }
@@ -474,11 +486,11 @@ static bool vce_v2_0_is_idle(void *handle)
 
 static int vce_v2_0_wait_for_idle(void *handle)
 {
-       unsigned i;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       unsigned i;
 
        for (i = 0; i < adev->usec_timeout; i++) {
-               if (!(RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK))
+               if (vce_v2_0_is_idle(handle))
                        return 0;
        }
        return -ETIMEDOUT;
@@ -488,8 +500,7 @@ static int vce_v2_0_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
-       WREG32_P(mmSRBM_SOFT_RESET, SRBM_SOFT_RESET__SOFT_RESET_VCE_MASK,
-                       ~SRBM_SOFT_RESET__SOFT_RESET_VCE_MASK);
+       WREG32_FIELD(SRBM_SOFT_RESET, SOFT_RESET_VCE, 1);
        mdelay(5);
 
        return vce_v2_0_start(adev);
@@ -516,10 +527,8 @@ static int vce_v2_0_process_interrupt(struct amdgpu_device *adev,
        DRM_DEBUG("IH: VCE\n");
        switch (entry->src_data) {
        case 0:
-               amdgpu_fence_process(&adev->vce.ring[0]);
-               break;
        case 1:
-               amdgpu_fence_process(&adev->vce.ring[1]);
+               amdgpu_fence_process(&adev->vce.ring[entry->src_data]);
                break;
        default:
                DRM_ERROR("Unhandled interrupt: %d %d\n",
@@ -530,11 +539,28 @@ static int vce_v2_0_process_interrupt(struct amdgpu_device *adev,
        return 0;
 }
 
+static void vce_v2_0_set_bypass_mode(struct amdgpu_device *adev, bool enable)
+{
+       u32 tmp = RREG32_SMC(ixGCK_DFS_BYPASS_CNTL);
+
+       if (enable)
+               tmp |= GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK;
+       else
+               tmp &= ~GCK_DFS_BYPASS_CNTL__BYPASSECLK_MASK;
+
+       WREG32_SMC(ixGCK_DFS_BYPASS_CNTL, tmp);
+}
+
+
 static int vce_v2_0_set_clockgating_state(void *handle,
                                          enum amd_clockgating_state state)
 {
        bool gate = false;
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       bool enable = (state == AMD_CG_STATE_GATE) ? true : false;
+
+
+       vce_v2_0_set_bypass_mode(adev, enable);
 
        if (state == AMD_CG_STATE_GATE)
                gate = true;
@@ -596,12 +622,16 @@ static const struct amdgpu_ring_funcs vce_v2_0_ring_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_vce_ring_begin_use,
        .end_use = amdgpu_vce_ring_end_use,
+       .get_emit_ib_size = amdgpu_vce_ring_get_emit_ib_size,
+       .get_dma_frame_size = amdgpu_vce_ring_get_dma_frame_size,
 };
 
 static void vce_v2_0_set_ring_funcs(struct amdgpu_device *adev)
 {
-       adev->vce.ring[0].funcs = &vce_v2_0_ring_funcs;
-       adev->vce.ring[1].funcs = &vce_v2_0_ring_funcs;
+       int i;
+
+       for (i = 0; i < adev->vce.num_rings; i++)
+               adev->vce.ring[i].funcs = &vce_v2_0_ring_funcs;
 }
 
 static const struct amdgpu_irq_src_funcs vce_v2_0_irq_funcs = {
index c271abf..3f6db4e 100644 (file)
@@ -37,6 +37,9 @@
 #include "gca/gfx_8_0_d.h"
 #include "smu/smu_7_1_2_d.h"
 #include "smu/smu_7_1_2_sh_mask.h"
+#include "gca/gfx_8_0_d.h"
+#include "gca/gfx_8_0_sh_mask.h"
+
 
 #define GRBM_GFX_INDEX__VCE_INSTANCE__SHIFT    0x04
 #define GRBM_GFX_INDEX__VCE_INSTANCE_MASK      0x10
@@ -67,8 +70,10 @@ static uint32_t vce_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
 
        if (ring == &adev->vce.ring[0])
                return RREG32(mmVCE_RB_RPTR);
-       else
+       else if (ring == &adev->vce.ring[1])
                return RREG32(mmVCE_RB_RPTR2);
+       else
+               return RREG32(mmVCE_RB_RPTR3);
 }
 
 /**
@@ -84,8 +89,10 @@ static uint32_t vce_v3_0_ring_get_wptr(struct amdgpu_ring *ring)
 
        if (ring == &adev->vce.ring[0])
                return RREG32(mmVCE_RB_WPTR);
-       else
+       else if (ring == &adev->vce.ring[1])
                return RREG32(mmVCE_RB_WPTR2);
+       else
+               return RREG32(mmVCE_RB_WPTR3);
 }
 
 /**
@@ -101,108 +108,80 @@ static void vce_v3_0_ring_set_wptr(struct amdgpu_ring *ring)
 
        if (ring == &adev->vce.ring[0])
                WREG32(mmVCE_RB_WPTR, ring->wptr);
-       else
+       else if (ring == &adev->vce.ring[1])
                WREG32(mmVCE_RB_WPTR2, ring->wptr);
+       else
+               WREG32(mmVCE_RB_WPTR3, ring->wptr);
 }
 
 static void vce_v3_0_override_vce_clock_gating(struct amdgpu_device *adev, bool override)
 {
-       u32 tmp, data;
-
-       tmp = data = RREG32(mmVCE_RB_ARB_CTRL);
-       if (override)
-               data |= VCE_RB_ARB_CTRL__VCE_CGTT_OVERRIDE_MASK;
-       else
-               data &= ~VCE_RB_ARB_CTRL__VCE_CGTT_OVERRIDE_MASK;
-
-       if (tmp != data)
-               WREG32(mmVCE_RB_ARB_CTRL, data);
+       WREG32_FIELD(VCE_RB_ARB_CTRL, VCE_CGTT_OVERRIDE, override ? 1 : 0);
 }
 
 static void vce_v3_0_set_vce_sw_clock_gating(struct amdgpu_device *adev,
                                             bool gated)
 {
-       u32 tmp, data;
+       u32 data;
+
        /* Set Override to disable Clock Gating */
        vce_v3_0_override_vce_clock_gating(adev, true);
 
-       if (!gated) {
-               /* Force CLOCK ON for VCE_CLOCK_GATING_B,
-                * {*_FORCE_ON, *_FORCE_OFF} = {1, 0}
-                * VREG can be FORCE ON or set to Dynamic, but can't be OFF
-                */
-               tmp = data = RREG32(mmVCE_CLOCK_GATING_B);
+       /* This function enables MGCG which is controlled by firmware.
+          With the clocks in the gated state the core is still
+          accessible but the firmware will throttle the clocks on the
+          fly as necessary.
+       */
+       if (gated) {
+               data = RREG32(mmVCE_CLOCK_GATING_B);
                data |= 0x1ff;
                data &= ~0xef0000;
-               if (tmp != data)
-                       WREG32(mmVCE_CLOCK_GATING_B, data);
+               WREG32(mmVCE_CLOCK_GATING_B, data);
 
-               /* Force CLOCK ON for VCE_UENC_CLOCK_GATING,
-                * {*_FORCE_ON, *_FORCE_OFF} = {1, 0}
-                */
-               tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING);
+               data = RREG32(mmVCE_UENC_CLOCK_GATING);
                data |= 0x3ff000;
                data &= ~0xffc00000;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_CLOCK_GATING, data);
+               WREG32(mmVCE_UENC_CLOCK_GATING, data);
 
-               /* set VCE_UENC_CLOCK_GATING_2 */
-               tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING_2);
+               data = RREG32(mmVCE_UENC_CLOCK_GATING_2);
                data |= 0x2;
-               data &= ~0x2;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_CLOCK_GATING_2, data);
+               data &= ~0x00010000;
+               WREG32(mmVCE_UENC_CLOCK_GATING_2, data);
 
-               /* Force CLOCK ON for VCE_UENC_REG_CLOCK_GATING */
-               tmp = data = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
+               data = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
                data |= 0x37f;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_REG_CLOCK_GATING, data);
+               WREG32(mmVCE_UENC_REG_CLOCK_GATING, data);
 
-               /* Force VCE_UENC_DMA_DCLK_CTRL Clock ON */
-               tmp = data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL);
+               data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL);
                data |= VCE_UENC_DMA_DCLK_CTRL__WRDMCLK_FORCEON_MASK |
-                               VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK |
-                               VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK  |
-                               0x8;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data);
+                       VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK |
+                       VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK  |
+                       0x8;
+               WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data);
        } else {
-               /* Force CLOCK OFF for VCE_CLOCK_GATING_B,
-                * {*, *_FORCE_OFF} = {*, 1}
-                * set VREG to Dynamic, as it can't be OFF
-                */
-               tmp = data = RREG32(mmVCE_CLOCK_GATING_B);
+               data = RREG32(mmVCE_CLOCK_GATING_B);
                data &= ~0x80010;
                data |= 0xe70008;
-               if (tmp != data)
-                       WREG32(mmVCE_CLOCK_GATING_B, data);
-               /* Force CLOCK OFF for VCE_UENC_CLOCK_GATING,
-                * Force ClOCK OFF takes precedent over Force CLOCK ON setting.
-                * {*_FORCE_ON, *_FORCE_OFF} = {*, 1}
-                */
-               tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING);
+               WREG32(mmVCE_CLOCK_GATING_B, data);
+
+               data = RREG32(mmVCE_UENC_CLOCK_GATING);
                data |= 0xffc00000;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_CLOCK_GATING, data);
-               /* Set VCE_UENC_CLOCK_GATING_2 */
-               tmp = data = RREG32(mmVCE_UENC_CLOCK_GATING_2);
+               WREG32(mmVCE_UENC_CLOCK_GATING, data);
+
+               data = RREG32(mmVCE_UENC_CLOCK_GATING_2);
                data |= 0x10000;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_CLOCK_GATING_2, data);
-               /* Set VCE_UENC_REG_CLOCK_GATING to dynamic */
-               tmp = data = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
+               WREG32(mmVCE_UENC_CLOCK_GATING_2, data);
+
+               data = RREG32(mmVCE_UENC_REG_CLOCK_GATING);
                data &= ~0xffc00000;
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_REG_CLOCK_GATING, data);
-               /* Set VCE_UENC_DMA_DCLK_CTRL CG always in dynamic mode */
-               tmp = data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL);
+               WREG32(mmVCE_UENC_REG_CLOCK_GATING, data);
+
+               data = RREG32(mmVCE_UENC_DMA_DCLK_CTRL);
                data &= ~(VCE_UENC_DMA_DCLK_CTRL__WRDMCLK_FORCEON_MASK |
-                               VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK |
-                               VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK  |
-                               0x8);
-               if (tmp != data)
-                       WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data);
+                         VCE_UENC_DMA_DCLK_CTRL__RDDMCLK_FORCEON_MASK |
+                         VCE_UENC_DMA_DCLK_CTRL__REGCLK_FORCEON_MASK  |
+                         0x8);
+               WREG32(mmVCE_UENC_DMA_DCLK_CTRL, data);
        }
        vce_v3_0_override_vce_clock_gating(adev, false);
 }
@@ -221,12 +200,9 @@ static int vce_v3_0_firmware_loaded(struct amdgpu_device *adev)
                }
 
                DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
-               WREG32_P(mmVCE_SOFT_RESET,
-                       VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
-                       ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+               WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 1);
                mdelay(10);
-               WREG32_P(mmVCE_SOFT_RESET, 0,
-                       ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+               WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 0);
                mdelay(10);
        }
 
@@ -259,43 +235,34 @@ static int vce_v3_0_start(struct amdgpu_device *adev)
        WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
        WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
 
+       ring = &adev->vce.ring[2];
+       WREG32(mmVCE_RB_RPTR3, ring->wptr);
+       WREG32(mmVCE_RB_WPTR3, ring->wptr);
+       WREG32(mmVCE_RB_BASE_LO3, ring->gpu_addr);
+       WREG32(mmVCE_RB_BASE_HI3, upper_32_bits(ring->gpu_addr));
+       WREG32(mmVCE_RB_SIZE3, ring->ring_size / 4);
+
        mutex_lock(&adev->grbm_idx_mutex);
        for (idx = 0; idx < 2; ++idx) {
                if (adev->vce.harvest_config & (1 << idx))
                        continue;
 
-               if (idx == 0)
-                       WREG32_P(mmGRBM_GFX_INDEX, 0,
-                               ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
-               else
-                       WREG32_P(mmGRBM_GFX_INDEX,
-                               GRBM_GFX_INDEX__VCE_INSTANCE_MASK,
-                               ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
-
+               WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, idx);
                vce_v3_0_mc_resume(adev, idx);
-
-               WREG32_P(mmVCE_STATUS, VCE_STATUS__JOB_BUSY_MASK,
-                        ~VCE_STATUS__JOB_BUSY_MASK);
+               WREG32_FIELD(VCE_STATUS, JOB_BUSY, 1);
 
                if (adev->asic_type >= CHIP_STONEY)
                        WREG32_P(mmVCE_VCPU_CNTL, 1, ~0x200001);
                else
-                       WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK,
-                               ~VCE_VCPU_CNTL__CLK_EN_MASK);
-
-               WREG32_P(mmVCE_SOFT_RESET, 0,
-                       ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+                       WREG32_FIELD(VCE_VCPU_CNTL, CLK_EN, 1);
 
+               WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 0);
                mdelay(100);
 
                r = vce_v3_0_firmware_loaded(adev);
 
                /* clear BUSY flag */
-               WREG32_P(mmVCE_STATUS, 0, ~VCE_STATUS__JOB_BUSY_MASK);
-
-               /* Set Clock-Gating off */
-               if (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)
-                       vce_v3_0_set_vce_sw_clock_gating(adev, false);
+               WREG32_FIELD(VCE_STATUS, JOB_BUSY, 0);
 
                if (r) {
                        DRM_ERROR("VCE not responding, giving up!!!\n");
@@ -304,7 +271,7 @@ static int vce_v3_0_start(struct amdgpu_device *adev)
                }
        }
 
-       WREG32_P(mmGRBM_GFX_INDEX, 0, ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
+       WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, 0);
        mutex_unlock(&adev->grbm_idx_mutex);
 
        return 0;
@@ -319,33 +286,25 @@ static int vce_v3_0_stop(struct amdgpu_device *adev)
                if (adev->vce.harvest_config & (1 << idx))
                        continue;
 
-               if (idx == 0)
-                       WREG32_P(mmGRBM_GFX_INDEX, 0,
-                               ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
-               else
-                       WREG32_P(mmGRBM_GFX_INDEX,
-                               GRBM_GFX_INDEX__VCE_INSTANCE_MASK,
-                               ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
+               WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, idx);
 
                if (adev->asic_type >= CHIP_STONEY)
                        WREG32_P(mmVCE_VCPU_CNTL, 0, ~0x200001);
                else
-                       WREG32_P(mmVCE_VCPU_CNTL, 0,
-                               ~VCE_VCPU_CNTL__CLK_EN_MASK);
+                       WREG32_FIELD(VCE_VCPU_CNTL, CLK_EN, 0);
+
                /* hold on ECPU */
-               WREG32_P(mmVCE_SOFT_RESET,
-                        VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
-                        ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
+               WREG32_FIELD(VCE_SOFT_RESET, ECPU_SOFT_RESET, 1);
 
                /* clear BUSY flag */
-               WREG32_P(mmVCE_STATUS, 0, ~VCE_STATUS__JOB_BUSY_MASK);
+               WREG32_FIELD(VCE_STATUS, JOB_BUSY, 0);
 
                /* Set Clock-Gating off */
                if (adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG)
                        vce_v3_0_set_vce_sw_clock_gating(adev, false);
        }
 
-       WREG32_P(mmGRBM_GFX_INDEX, 0, ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
+       WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, 0);
        mutex_unlock(&adev->grbm_idx_mutex);
 
        return 0;
@@ -399,6 +358,8 @@ static int vce_v3_0_early_init(void *handle)
            (AMDGPU_VCE_HARVEST_VCE0 | AMDGPU_VCE_HARVEST_VCE1))
                return -ENOENT;
 
+       adev->vce.num_rings = 3;
+
        vce_v3_0_set_ring_funcs(adev);
        vce_v3_0_set_irq_funcs(adev);
 
@@ -409,7 +370,7 @@ static int vce_v3_0_sw_init(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
        struct amdgpu_ring *ring;
-       int r;
+       int r, i;
 
        /* VCE */
        r = amdgpu_irq_add_id(adev, 167, &adev->vce.irq);
@@ -425,19 +386,14 @@ static int vce_v3_0_sw_init(void *handle)
        if (r)
                return r;
 
-       ring = &adev->vce.ring[0];
-       sprintf(ring->name, "vce0");
-       r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
-                            &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
-       if (r)
-               return r;
-
-       ring = &adev->vce.ring[1];
-       sprintf(ring->name, "vce1");
-       r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
-                            &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
-       if (r)
-               return r;
+       for (i = 0; i < adev->vce.num_rings; i++) {
+               ring = &adev->vce.ring[i];
+               sprintf(ring->name, "vce%d", i);
+               r = amdgpu_ring_init(adev, ring, 512, VCE_CMD_NO_OP, 0xf,
+                                    &adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
+               if (r)
+                       return r;
+       }
 
        return r;
 }
@@ -467,10 +423,10 @@ static int vce_v3_0_hw_init(void *handle)
        if (r)
                return r;
 
-       adev->vce.ring[0].ready = false;
-       adev->vce.ring[1].ready = false;
+       for (i = 0; i < adev->vce.num_rings; i++)
+               adev->vce.ring[i].ready = false;
 
-       for (i = 0; i < 2; i++) {
+       for (i = 0; i < adev->vce.num_rings; i++) {
                r = amdgpu_ring_test_ring(&adev->vce.ring[i]);
                if (r)
                        return r;
@@ -534,7 +490,7 @@ static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx)
        WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16));
        WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000);
        WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F);
-       WREG32(mmVCE_CLOCK_GATING_B, 0xf7);
+       WREG32(mmVCE_CLOCK_GATING_B, 0x1FF);
 
        WREG32(mmVCE_LMI_CTRL, 0x00398000);
        WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1);
@@ -573,9 +529,7 @@ static void vce_v3_0_mc_resume(struct amdgpu_device *adev, int idx)
        }
 
        WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
-
-       WREG32_P(mmVCE_SYS_INT_EN, VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK,
-                ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK);
+       WREG32_FIELD(VCE_SYS_INT_EN, VCE_SYS_INT_TRAP_INTERRUPT_EN, 1);
 }
 
 static bool vce_v3_0_is_idle(void *handle)
@@ -601,20 +555,108 @@ static int vce_v3_0_wait_for_idle(void *handle)
        return -ETIMEDOUT;
 }
 
+#define  VCE_STATUS_VCPU_REPORT_AUTO_BUSY_MASK  0x00000008L   /* AUTO_BUSY */
+#define  VCE_STATUS_VCPU_REPORT_RB0_BUSY_MASK   0x00000010L   /* RB0_BUSY */
+#define  VCE_STATUS_VCPU_REPORT_RB1_BUSY_MASK   0x00000020L   /* RB1_BUSY */
+#define  AMDGPU_VCE_STATUS_BUSY_MASK (VCE_STATUS_VCPU_REPORT_AUTO_BUSY_MASK | \
+                                     VCE_STATUS_VCPU_REPORT_RB0_BUSY_MASK)
+
+static int vce_v3_0_check_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       u32 srbm_soft_reset = 0;
+
+       /* According to VCE team , we should use VCE_STATUS instead
+        * SRBM_STATUS.VCE_BUSY bit for busy status checking.
+        * GRBM_GFX_INDEX.INSTANCE_INDEX is used to specify which VCE
+        * instance's registers are accessed
+        * (0 for 1st instance, 10 for 2nd instance).
+        *
+        *VCE_STATUS
+        *|UENC|ACPI|AUTO ACTIVE|RB1 |RB0 |RB2 |          |FW_LOADED|JOB |
+        *|----+----+-----------+----+----+----+----------+---------+----|
+        *|bit8|bit7|    bit6   |bit5|bit4|bit3|   bit2   |  bit1   |bit0|
+        *
+        * VCE team suggest use bit 3--bit 6 for busy status check
+        */
+       mutex_lock(&adev->grbm_idx_mutex);
+       WREG32_FIELD(GRBM_GFX_INDEX, INSTANCE_INDEX, 0);
+       if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) {
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1);
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
+       }
+       WREG32_FIELD(GRBM_GFX_INDEX, INSTANCE_INDEX, 0x10);
+       if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) {
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1);
+               srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
+       }
+       WREG32_FIELD(GRBM_GFX_INDEX, INSTANCE_INDEX, 0);
+
+       if (srbm_soft_reset) {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang = true;
+               adev->vce.srbm_soft_reset = srbm_soft_reset;
+       } else {
+               adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang = false;
+               adev->vce.srbm_soft_reset = 0;
+       }
+       mutex_unlock(&adev->grbm_idx_mutex);
+       return 0;
+}
+
 static int vce_v3_0_soft_reset(void *handle)
 {
        struct amdgpu_device *adev = (struct amdgpu_device *)handle;
-       u32 mask = 0;
+       u32 srbm_soft_reset;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+               return 0;
+       srbm_soft_reset = adev->vce.srbm_soft_reset;
+
+       if (srbm_soft_reset) {
+               u32 tmp;
 
-       mask |= (adev->vce.harvest_config & AMDGPU_VCE_HARVEST_VCE0) ? 0 : SRBM_SOFT_RESET__SOFT_RESET_VCE0_MASK;
-       mask |= (adev->vce.harvest_config & AMDGPU_VCE_HARVEST_VCE1) ? 0 : SRBM_SOFT_RESET__SOFT_RESET_VCE1_MASK;
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+               tmp |= srbm_soft_reset;
+               dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+
+               udelay(50);
+
+               tmp &= ~srbm_soft_reset;
+               WREG32(mmSRBM_SOFT_RESET, tmp);
+               tmp = RREG32(mmSRBM_SOFT_RESET);
+
+               /* Wait a little for things to settle down */
+               udelay(50);
+       }
+
+       return 0;
+}
+
+static int vce_v3_0_pre_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+               return 0;
 
-       WREG32_P(mmSRBM_SOFT_RESET, mask,
-                ~(SRBM_SOFT_RESET__SOFT_RESET_VCE0_MASK |
-                  SRBM_SOFT_RESET__SOFT_RESET_VCE1_MASK));
        mdelay(5);
 
-       return vce_v3_0_start(adev);
+       return vce_v3_0_suspend(adev);
+}
+
+
+static int vce_v3_0_post_soft_reset(void *handle)
+{
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+
+       if (!adev->ip_block_status[AMD_IP_BLOCK_TYPE_VCE].hang)
+               return 0;
+
+       mdelay(5);
+
+       return vce_v3_0_resume(adev);
 }
 
 static int vce_v3_0_set_interrupt_state(struct amdgpu_device *adev,
@@ -637,13 +679,12 @@ static int vce_v3_0_process_interrupt(struct amdgpu_device *adev,
 {
        DRM_DEBUG("IH: VCE\n");
 
-       WREG32_P(mmVCE_SYS_INT_STATUS,
-               VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK,
-               ~VCE_SYS_INT_STATUS__VCE_SYS_INT_TRAP_INTERRUPT_INT_MASK);
+       WREG32_FIELD(VCE_SYS_INT_STATUS, VCE_SYS_INT_TRAP_INTERRUPT_INT, 1);
 
        switch (entry->src_data) {
        case 0:
        case 1:
+       case 2:
                amdgpu_fence_process(&adev->vce.ring[entry->src_data]);
                break;
        default:
@@ -655,7 +696,7 @@ static int vce_v3_0_process_interrupt(struct amdgpu_device *adev,
        return 0;
 }
 
-static void vce_v3_set_bypass_mode(struct amdgpu_device *adev, bool enable)
+static void vce_v3_0_set_bypass_mode(struct amdgpu_device *adev, bool enable)
 {
        u32 tmp = RREG32_SMC(ixGCK_DFS_BYPASS_CNTL);
 
@@ -674,8 +715,10 @@ static int vce_v3_0_set_clockgating_state(void *handle,
        bool enable = (state == AMD_CG_STATE_GATE) ? true : false;
        int i;
 
-       if (adev->asic_type == CHIP_POLARIS10)
-               vce_v3_set_bypass_mode(adev, enable);
+       if ((adev->asic_type == CHIP_POLARIS10) ||
+               (adev->asic_type == CHIP_TONGA) ||
+               (adev->asic_type == CHIP_FIJI))
+               vce_v3_0_set_bypass_mode(adev, enable);
 
        if (!(adev->cg_flags & AMD_CG_SUPPORT_VCE_MGCG))
                return 0;
@@ -686,13 +729,7 @@ static int vce_v3_0_set_clockgating_state(void *handle,
                if (adev->vce.harvest_config & (1 << i))
                        continue;
 
-               if (i == 0)
-                       WREG32_P(mmGRBM_GFX_INDEX, 0,
-                                       ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
-               else
-                       WREG32_P(mmGRBM_GFX_INDEX,
-                                       GRBM_GFX_INDEX__VCE_INSTANCE_MASK,
-                                       ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
+               WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, i);
 
                if (enable) {
                        /* initialize VCE_CLOCK_GATING_A: Clock ON/OFF delay */
@@ -711,7 +748,7 @@ static int vce_v3_0_set_clockgating_state(void *handle,
                vce_v3_0_set_vce_sw_clock_gating(adev, enable);
        }
 
-       WREG32_P(mmGRBM_GFX_INDEX, 0, ~GRBM_GFX_INDEX__VCE_INSTANCE_MASK);
+       WREG32_FIELD(GRBM_GFX_INDEX, VCE_INSTANCE, 0);
        mutex_unlock(&adev->grbm_idx_mutex);
 
        return 0;
@@ -739,6 +776,60 @@ static int vce_v3_0_set_powergating_state(void *handle,
                return vce_v3_0_start(adev);
 }
 
+static void vce_v3_0_ring_emit_ib(struct amdgpu_ring *ring,
+               struct amdgpu_ib *ib, unsigned int vm_id, bool ctx_switch)
+{
+       amdgpu_ring_write(ring, VCE_CMD_IB_VM);
+       amdgpu_ring_write(ring, vm_id);
+       amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr));
+       amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
+       amdgpu_ring_write(ring, ib->length_dw);
+}
+
+static void vce_v3_0_emit_vm_flush(struct amdgpu_ring *ring,
+                        unsigned int vm_id, uint64_t pd_addr)
+{
+       amdgpu_ring_write(ring, VCE_CMD_UPDATE_PTB);
+       amdgpu_ring_write(ring, vm_id);
+       amdgpu_ring_write(ring, pd_addr >> 12);
+
+       amdgpu_ring_write(ring, VCE_CMD_FLUSH_TLB);
+       amdgpu_ring_write(ring, vm_id);
+       amdgpu_ring_write(ring, VCE_CMD_END);
+}
+
+static void vce_v3_0_emit_pipeline_sync(struct amdgpu_ring *ring)
+{
+       uint32_t seq = ring->fence_drv.sync_seq;
+       uint64_t addr = ring->fence_drv.gpu_addr;
+
+       amdgpu_ring_write(ring, VCE_CMD_WAIT_GE);
+       amdgpu_ring_write(ring, lower_32_bits(addr));
+       amdgpu_ring_write(ring, upper_32_bits(addr));
+       amdgpu_ring_write(ring, seq);
+}
+
+static unsigned vce_v3_0_ring_get_emit_ib_size(struct amdgpu_ring *ring)
+{
+       return
+               5; /* vce_v3_0_ring_emit_ib */
+}
+
+static unsigned vce_v3_0_ring_get_dma_frame_size(struct amdgpu_ring *ring)
+{
+       return
+               4 + /* vce_v3_0_emit_pipeline_sync */
+               6; /* amdgpu_vce_ring_emit_fence x1 no user fence */
+}
+
+static unsigned vce_v3_0_ring_get_dma_frame_size_vm(struct amdgpu_ring *ring)
+{
+       return
+               6 + /* vce_v3_0_emit_vm_flush */
+               4 + /* vce_v3_0_emit_pipeline_sync */
+               6 + 6; /* amdgpu_vce_ring_emit_fence x2 vm fence */
+}
+
 const struct amd_ip_funcs vce_v3_0_ip_funcs = {
        .name = "vce_v3_0",
        .early_init = vce_v3_0_early_init,
@@ -751,12 +842,15 @@ const struct amd_ip_funcs vce_v3_0_ip_funcs = {
        .resume = vce_v3_0_resume,
        .is_idle = vce_v3_0_is_idle,
        .wait_for_idle = vce_v3_0_wait_for_idle,
+       .check_soft_reset = vce_v3_0_check_soft_reset,
+       .pre_soft_reset = vce_v3_0_pre_soft_reset,
        .soft_reset = vce_v3_0_soft_reset,
+       .post_soft_reset = vce_v3_0_post_soft_reset,
        .set_clockgating_state = vce_v3_0_set_clockgating_state,
        .set_powergating_state = vce_v3_0_set_powergating_state,
 };
 
-static const struct amdgpu_ring_funcs vce_v3_0_ring_funcs = {
+static const struct amdgpu_ring_funcs vce_v3_0_ring_phys_funcs = {
        .get_rptr = vce_v3_0_ring_get_rptr,
        .get_wptr = vce_v3_0_ring_get_wptr,
        .set_wptr = vce_v3_0_ring_set_wptr,
@@ -769,12 +863,42 @@ static const struct amdgpu_ring_funcs vce_v3_0_ring_funcs = {
        .pad_ib = amdgpu_ring_generic_pad_ib,
        .begin_use = amdgpu_vce_ring_begin_use,
        .end_use = amdgpu_vce_ring_end_use,
+       .get_emit_ib_size = vce_v3_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = vce_v3_0_ring_get_dma_frame_size,
+};
+
+static const struct amdgpu_ring_funcs vce_v3_0_ring_vm_funcs = {
+       .get_rptr = vce_v3_0_ring_get_rptr,
+       .get_wptr = vce_v3_0_ring_get_wptr,
+       .set_wptr = vce_v3_0_ring_set_wptr,
+       .parse_cs = NULL,
+       .emit_ib = vce_v3_0_ring_emit_ib,
+       .emit_vm_flush = vce_v3_0_emit_vm_flush,
+       .emit_pipeline_sync = vce_v3_0_emit_pipeline_sync,
+       .emit_fence = amdgpu_vce_ring_emit_fence,
+       .test_ring = amdgpu_vce_ring_test_ring,
+       .test_ib = amdgpu_vce_ring_test_ib,
+       .insert_nop = amdgpu_ring_insert_nop,
+       .pad_ib = amdgpu_ring_generic_pad_ib,
+       .begin_use = amdgpu_vce_ring_begin_use,
+       .end_use = amdgpu_vce_ring_end_use,
+       .get_emit_ib_size = vce_v3_0_ring_get_emit_ib_size,
+       .get_dma_frame_size = vce_v3_0_ring_get_dma_frame_size_vm,
 };
 
 static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev)
 {
-       adev->vce.ring[0].funcs = &vce_v3_0_ring_funcs;
-       adev->vce.ring[1].funcs = &vce_v3_0_ring_funcs;
+       int i;
+
+       if (adev->asic_type >= CHIP_STONEY) {
+               for (i = 0; i < adev->vce.num_rings; i++)
+                       adev->vce.ring[i].funcs = &vce_v3_0_ring_vm_funcs;
+               DRM_INFO("VCE enabled in VM mode\n");
+       } else {
+               for (i = 0; i < adev->vce.num_rings; i++)
+                       adev->vce.ring[i].funcs = &vce_v3_0_ring_phys_funcs;
+               DRM_INFO("VCE enabled in physical mode\n");
+       }
 }
 
 static const struct amdgpu_irq_src_funcs vce_v3_0_irq_funcs = {
index 03a31c5..c0d9aad 100644 (file)
 #if defined(CONFIG_DRM_AMD_ACP)
 #include "amdgpu_acp.h"
 #endif
+#include "dce_virtual.h"
 
+MODULE_FIRMWARE("amdgpu/topaz_smc.bin");
+MODULE_FIRMWARE("amdgpu/tonga_smc.bin");
+MODULE_FIRMWARE("amdgpu/fiji_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_smc.bin");
 MODULE_FIRMWARE("amdgpu/polaris10_smc_sk.bin");
 MODULE_FIRMWARE("amdgpu/polaris11_smc.bin");
@@ -444,18 +448,21 @@ static bool vi_read_bios_from_rom(struct amdgpu_device *adev,
        return true;
 }
 
-static u32 vi_get_virtual_caps(struct amdgpu_device *adev)
+static void vi_detect_hw_virtualization(struct amdgpu_device *adev)
 {
-       u32 caps = 0;
-       u32 reg = RREG32(mmBIF_IOV_FUNC_IDENTIFIER);
-
-       if (REG_GET_FIELD(reg, BIF_IOV_FUNC_IDENTIFIER, IOV_ENABLE))
-               caps |= AMDGPU_VIRT_CAPS_SRIOV_EN;
-
-       if (REG_GET_FIELD(reg, BIF_IOV_FUNC_IDENTIFIER, FUNC_IDENTIFIER))
-               caps |= AMDGPU_VIRT_CAPS_IS_VF;
-
-       return caps;
+       uint32_t reg = RREG32(mmBIF_IOV_FUNC_IDENTIFIER);
+       /* bit0: 0 means pf and 1 means vf */
+       /* bit31: 0 means disable IOV and 1 means enable */
+       if (reg & 1)
+               adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_IS_VF;
+
+       if (reg & 0x80000000)
+               adev->virtualization.virtual_caps |= AMDGPU_SRIOV_CAPS_ENABLE_IOV;
+
+       if (reg == 0) {
+               if (is_virtual_machine()) /* passthrough mode exclus sr-iov mode */
+                       adev->virtualization.virtual_caps |= AMDGPU_PASSTHROUGH_MODE;
+       }
 }
 
 static const struct amdgpu_allowed_register_entry tonga_allowed_read_registers[] = {
@@ -822,6 +829,60 @@ static const struct amdgpu_ip_block_version topaz_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version topaz_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vi_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 7,
+               .minor = 4,
+               .rev = 0,
+               .funcs = &gmc_v7_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 2,
+               .minor = 4,
+               .rev = 0,
+               .funcs = &iceland_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 1,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 2,
+               .minor = 4,
+               .rev = 0,
+               .funcs = &sdma_v2_4_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version tonga_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -890,6 +951,74 @@ static const struct amdgpu_ip_block_version tonga_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version tonga_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vi_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &tonga_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 10,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &sdma_v3_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 5,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &uvd_v5_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v3_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version fiji_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -958,6 +1087,74 @@ static const struct amdgpu_ip_block_version fiji_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version fiji_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vi_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 8,
+               .minor = 5,
+               .rev = 0,
+               .funcs = &gmc_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &tonga_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 10,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &sdma_v3_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &uvd_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v3_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version polaris11_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1026,6 +1223,74 @@ static const struct amdgpu_ip_block_version polaris11_ip_blocks[] =
        },
 };
 
+static const struct amdgpu_ip_block_version polaris11_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vi_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 8,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &gmc_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 3,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &tonga_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 7,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 11,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 3,
+               .minor = 1,
+               .rev = 0,
+               .funcs = &sdma_v3_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 6,
+               .minor = 3,
+               .rev = 0,
+               .funcs = &uvd_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 3,
+               .minor = 4,
+               .rev = 0,
+               .funcs = &vce_v3_0_ip_funcs,
+       },
+};
+
 static const struct amdgpu_ip_block_version cz_ip_blocks[] =
 {
        /* ORDER MATTERS! */
@@ -1103,34 +1368,142 @@ static const struct amdgpu_ip_block_version cz_ip_blocks[] =
 #endif
 };
 
+static const struct amdgpu_ip_block_version cz_ip_blocks_vd[] =
+{
+       /* ORDER MATTERS! */
+       {
+               .type = AMD_IP_BLOCK_TYPE_COMMON,
+               .major = 2,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vi_common_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GMC,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gmc_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_IH,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &cz_ih_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SMC,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &amdgpu_pp_ip_funcs
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_DCE,
+               .major = 11,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &dce_virtual_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_GFX,
+               .major = 8,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &gfx_v8_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_SDMA,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &sdma_v3_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_UVD,
+               .major = 6,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &uvd_v6_0_ip_funcs,
+       },
+       {
+               .type = AMD_IP_BLOCK_TYPE_VCE,
+               .major = 3,
+               .minor = 0,
+               .rev = 0,
+               .funcs = &vce_v3_0_ip_funcs,
+       },
+#if defined(CONFIG_DRM_AMD_ACP)
+       {
+               .type = AMD_IP_BLOCK_TYPE_ACP,
+               .major = 2,
+               .minor = 2,
+               .rev = 0,
+               .funcs = &acp_ip_funcs,
+       },
+#endif
+};
+
 int vi_set_ip_blocks(struct amdgpu_device *adev)
 {
-       switch (adev->asic_type) {
-       case CHIP_TOPAZ:
-               adev->ip_blocks = topaz_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(topaz_ip_blocks);
-               break;
-       case CHIP_FIJI:
-               adev->ip_blocks = fiji_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(fiji_ip_blocks);
-               break;
-       case CHIP_TONGA:
-               adev->ip_blocks = tonga_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(tonga_ip_blocks);
-               break;
-       case CHIP_POLARIS11:
-       case CHIP_POLARIS10:
-               adev->ip_blocks = polaris11_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(polaris11_ip_blocks);
-               break;
-       case CHIP_CARRIZO:
-       case CHIP_STONEY:
-               adev->ip_blocks = cz_ip_blocks;
-               adev->num_ip_blocks = ARRAY_SIZE(cz_ip_blocks);
-               break;
-       default:
-               /* FIXME: not supported yet */
-               return -EINVAL;
+       if (adev->enable_virtual_display) {
+               switch (adev->asic_type) {
+               case CHIP_TOPAZ:
+                       adev->ip_blocks = topaz_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(topaz_ip_blocks_vd);
+                       break;
+               case CHIP_FIJI:
+                       adev->ip_blocks = fiji_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(fiji_ip_blocks_vd);
+                       break;
+               case CHIP_TONGA:
+                       adev->ip_blocks = tonga_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(tonga_ip_blocks_vd);
+                       break;
+               case CHIP_POLARIS11:
+               case CHIP_POLARIS10:
+                       adev->ip_blocks = polaris11_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(polaris11_ip_blocks_vd);
+                       break;
+
+               case CHIP_CARRIZO:
+               case CHIP_STONEY:
+                       adev->ip_blocks = cz_ip_blocks_vd;
+                       adev->num_ip_blocks = ARRAY_SIZE(cz_ip_blocks_vd);
+                       break;
+               default:
+                       /* FIXME: not supported yet */
+                       return -EINVAL;
+               }
+       } else {
+               switch (adev->asic_type) {
+               case CHIP_TOPAZ:
+                       adev->ip_blocks = topaz_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(topaz_ip_blocks);
+                       break;
+               case CHIP_FIJI:
+                       adev->ip_blocks = fiji_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(fiji_ip_blocks);
+                       break;
+               case CHIP_TONGA:
+                       adev->ip_blocks = tonga_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(tonga_ip_blocks);
+                       break;
+               case CHIP_POLARIS11:
+               case CHIP_POLARIS10:
+                       adev->ip_blocks = polaris11_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(polaris11_ip_blocks);
+                       break;
+               case CHIP_CARRIZO:
+               case CHIP_STONEY:
+                       adev->ip_blocks = cz_ip_blocks;
+                       adev->num_ip_blocks = ARRAY_SIZE(cz_ip_blocks);
+                       break;
+               default:
+                       /* FIXME: not supported yet */
+                       return -EINVAL;
+               }
        }
 
        return 0;
@@ -1154,13 +1527,13 @@ static const struct amdgpu_asic_funcs vi_asic_funcs =
 {
        .read_disabled_bios = &vi_read_disabled_bios,
        .read_bios_from_rom = &vi_read_bios_from_rom,
+       .detect_hw_virtualization = vi_detect_hw_virtualization,
        .read_register = &vi_read_register,
        .reset = &vi_asic_reset,
        .set_vga_state = &vi_vga_set_state,
        .get_xclk = &vi_get_xclk,
        .set_uvd_clocks = &vi_set_uvd_clocks,
        .set_vce_clocks = &vi_set_vce_clocks,
-       .get_virtual_caps = &vi_get_virtual_caps,
 };
 
 static int vi_common_early_init(void *handle)
@@ -1248,8 +1621,17 @@ static int vi_common_early_init(void *handle)
                        AMD_CG_SUPPORT_HDP_MGCG |
                        AMD_CG_SUPPORT_HDP_LS |
                        AMD_CG_SUPPORT_SDMA_MGCG |
-                       AMD_CG_SUPPORT_SDMA_LS;
+                       AMD_CG_SUPPORT_SDMA_LS |
+                       AMD_CG_SUPPORT_VCE_MGCG;
+               /* rev0 hardware requires workarounds to support PG */
                adev->pg_flags = 0;
+               if (adev->rev_id != 0x00) {
+                       adev->pg_flags |= AMD_PG_SUPPORT_GFX_PG |
+                               AMD_PG_SUPPORT_GFX_SMG |
+                               AMD_PG_SUPPORT_GFX_PIPELINE |
+                               AMD_PG_SUPPORT_UVD |
+                               AMD_PG_SUPPORT_VCE;
+               }
                adev->external_rev_id = adev->rev_id + 0x1;
                break;
        case CHIP_STONEY:
@@ -1267,14 +1649,24 @@ static int vi_common_early_init(void *handle)
                        AMD_CG_SUPPORT_HDP_MGCG |
                        AMD_CG_SUPPORT_HDP_LS |
                        AMD_CG_SUPPORT_SDMA_MGCG |
-                       AMD_CG_SUPPORT_SDMA_LS;
-               adev->external_rev_id = adev->rev_id + 0x1;
+                       AMD_CG_SUPPORT_SDMA_LS |
+                       AMD_CG_SUPPORT_VCE_MGCG;
+               adev->pg_flags |= AMD_PG_SUPPORT_GFX_PG |
+                       AMD_PG_SUPPORT_GFX_SMG |
+                       AMD_PG_SUPPORT_GFX_PIPELINE |
+                       AMD_PG_SUPPORT_UVD |
+                       AMD_PG_SUPPORT_VCE;
+               adev->external_rev_id = adev->rev_id + 0x61;
                break;
        default:
                /* FIXME: not supported yet */
                return -EINVAL;
        }
 
+       /* in early init stage, vbios code won't work */
+       if (adev->asic_funcs->detect_hw_virtualization)
+               amdgpu_asic_detect_hw_virtualization(adev);
+
        if (amdgpu_smc_load_fw && smc_enabled)
                adev->firmware.smu_load = true;
 
@@ -1418,6 +1810,63 @@ static void vi_update_rom_medium_grain_clock_gating(struct amdgpu_device *adev,
                WREG32_SMC(ixCGTT_ROM_CLK_CTRL0, data);
 }
 
+static int vi_common_set_clockgating_state_by_smu(void *handle,
+                                          enum amd_clockgating_state state)
+{
+       uint32_t msg_id, pp_state;
+       struct amdgpu_device *adev = (struct amdgpu_device *)handle;
+       void *pp_handle = adev->powerplay.pp_handle;
+
+       if (state == AMD_CG_STATE_UNGATE)
+               pp_state = 0;
+       else
+               pp_state = PP_STATE_CG | PP_STATE_LS;
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_MC,
+                      PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_SDMA,
+                      PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_HDP,
+                      PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_BIF,
+                      PP_STATE_SUPPORT_LS,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_BIF,
+                      PP_STATE_SUPPORT_CG,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_DRM,
+                      PP_STATE_SUPPORT_LS,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
+                      PP_BLOCK_SYS_ROM,
+                      PP_STATE_SUPPORT_CG,
+                      pp_state);
+       amd_set_clockgating_by_smu(pp_handle, msg_id);
+
+       return 0;
+}
+
 static int vi_common_set_clockgating_state(void *handle,
                                           enum amd_clockgating_state state)
 {
@@ -1443,6 +1892,10 @@ static int vi_common_set_clockgating_state(void *handle,
                vi_update_hdp_light_sleep(adev,
                                state == AMD_CG_STATE_GATE ? true : false);
                break;
+       case CHIP_TONGA:
+       case CHIP_POLARIS10:
+       case CHIP_POLARIS11:
+               vi_common_set_clockgating_state_by_smu(adev, state);
        default:
                break;
        }
index 062ee16..11746f2 100644 (file)
 #define VCE_CMD_IB_AUTO        0x00000005
 #define VCE_CMD_SEMAPHORE      0x00000006
 
+#define VCE_CMD_IB_VM           0x00000102
+#define VCE_CMD_WAIT_GE         0x00000106
+#define VCE_CMD_UPDATE_PTB      0x00000107
+#define VCE_CMD_FLUSH_TLB       0x00000108
+
+/* mmPA_SC_RASTER_CONFIG mask */
+#define RB_MAP_PKR0(x)                         ((x) << 0)
+#define RB_MAP_PKR0_MASK                       (0x3 << 0)
+#define RB_MAP_PKR1(x)                         ((x) << 2)
+#define RB_MAP_PKR1_MASK                       (0x3 << 2)
+#define RB_XSEL2(x)                            ((x) << 4)
+#define RB_XSEL2_MASK                          (0x3 << 4)
+#define RB_XSEL                                        (1 << 6)
+#define RB_YSEL                                        (1 << 7)
+#define PKR_MAP(x)                             ((x) << 8)
+#define PKR_MAP_MASK                           (0x3 << 8)
+#define PKR_XSEL(x)                            ((x) << 10)
+#define PKR_XSEL_MASK                          (0x3 << 10)
+#define PKR_YSEL(x)                            ((x) << 12)
+#define PKR_YSEL_MASK                          (0x3 << 12)
+#define SC_MAP(x)                              ((x) << 16)
+#define SC_MAP_MASK                            (0x3 << 16)
+#define SC_XSEL(x)                             ((x) << 18)
+#define SC_XSEL_MASK                           (0x3 << 18)
+#define SC_YSEL(x)                             ((x) << 20)
+#define SC_YSEL_MASK                           (0x3 << 20)
+#define SE_MAP(x)                              ((x) << 24)
+#define SE_MAP_MASK                            (0x3 << 24)
+#define SE_XSEL(x)                             ((x) << 26)
+#define SE_XSEL_MASK                           (0x3 << 26)
+#define SE_YSEL(x)                             ((x) << 28)
+#define SE_YSEL_MASK                           (0x3 << 28)
+
+/* mmPA_SC_RASTER_CONFIG_1 mask */
+#define SE_PAIR_MAP(x)                         ((x) << 0)
+#define SE_PAIR_MAP_MASK                       (0x3 << 0)
+#define SE_PAIR_XSEL(x)                                ((x) << 2)
+#define SE_PAIR_XSEL_MASK                      (0x3 << 2)
+#define SE_PAIR_YSEL(x)                                ((x) << 4)
+#define SE_PAIR_YSEL_MASK                      (0x3 << 4)
+
 #endif
index a7d3cb3..453c5d6 100644 (file)
@@ -142,13 +142,15 @@ int kfd_doorbell_mmap(struct kfd_process *process, struct vm_area_struct *vma)
 
        vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
-       pr_debug("mapping doorbell page:\n");
-       pr_debug("     target user address == 0x%08llX\n",
-                       (unsigned long long) vma->vm_start);
-       pr_debug("     physical address    == 0x%08llX\n", address);
-       pr_debug("     vm_flags            == 0x%04lX\n", vma->vm_flags);
-       pr_debug("     size                == 0x%04lX\n",
-                        doorbell_process_allocation());
+       pr_debug("kfd: mapping doorbell page in %s\n"
+                "     target user address == 0x%08llX\n"
+                "     physical address    == 0x%08llX\n"
+                "     vm_flags            == 0x%04lX\n"
+                "     size                == 0x%04lX\n",
+                __func__,
+                (unsigned long long) vma->vm_start, address, vma->vm_flags,
+                doorbell_process_allocation());
+
 
        return io_remap_pfn_range(vma,
                                vma->vm_start,
index 9beae87..d135cd0 100644 (file)
@@ -47,6 +47,9 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev,
        pr_debug("amdkfd: In func %s initializing queue type %d size %d\n",
                        __func__, KFD_QUEUE_TYPE_HIQ, queue_size);
 
+       memset(&prop, 0, sizeof(prop));
+       memset(&nop, 0, sizeof(nop));
+
        nop.opcode = IT_NOP;
        nop.type = PM4_TYPE_3;
        nop.u32all |= PM4_COUNT_ZERO;
@@ -121,7 +124,7 @@ static bool initialize(struct kernel_queue *kq, struct kfd_dev *dev,
        prop.eop_ring_buffer_address = kq->eop_gpu_addr;
        prop.eop_ring_buffer_size = PAGE_SIZE;
 
-       if (init_queue(&kq->queue, prop) != 0)
+       if (init_queue(&kq->queue, &prop) != 0)
                goto err_init_queue;
 
        kq->queue->device = dev;
index 80113c3..4750cab 100644 (file)
@@ -619,7 +619,7 @@ int kfd_init_apertures(struct kfd_process *process);
 /* Queue Context Management */
 struct cik_sdma_rlc_registers *get_sdma_mqd(void *mqd);
 
-int init_queue(struct queue **q, struct queue_properties properties);
+int init_queue(struct queue **q, const struct queue_properties *properties);
 void uninit_queue(struct queue *q);
 void print_queue_properties(struct queue_properties *q);
 void print_queue(struct queue *q);
index 4f3849a..ef7c8de 100644 (file)
@@ -404,58 +404,47 @@ void kfd_unbind_process_from_device(struct kfd_dev *dev, unsigned int pasid)
 {
        struct kfd_process *p;
        struct kfd_process_device *pdd;
-       int idx, i;
 
        BUG_ON(dev == NULL);
 
-       idx = srcu_read_lock(&kfd_processes_srcu);
-
        /*
         * Look for the process that matches the pasid. If there is no such
         * process, we either released it in amdkfd's own notifier, or there
         * is a bug. Unfortunately, there is no way to tell...
         */
-       hash_for_each_rcu(kfd_processes_table, i, p, kfd_processes)
-               if (p->pasid == pasid) {
-
-                       srcu_read_unlock(&kfd_processes_srcu, idx);
-
-                       pr_debug("Unbinding process %d from IOMMU\n", pasid);
+       p = kfd_lookup_process_by_pasid(pasid);
+       if (!p)
+               return;
 
-                       mutex_lock(&p->mutex);
-
-                       if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid))
-                               kfd_dbgmgr_destroy(dev->dbgmgr);
-
-                       pqm_uninit(&p->pqm);
+       pr_debug("Unbinding process %d from IOMMU\n", pasid);
 
-                       pdd = kfd_get_process_device_data(dev, p);
+       if ((dev->dbgmgr) && (dev->dbgmgr->pasid == p->pasid))
+               kfd_dbgmgr_destroy(dev->dbgmgr);
 
-                       if (!pdd) {
-                               mutex_unlock(&p->mutex);
-                               return;
-                       }
+       pqm_uninit(&p->pqm);
 
-                       if (pdd->reset_wavefronts) {
-                               dbgdev_wave_reset_wavefronts(pdd->dev, p);
-                               pdd->reset_wavefronts = false;
-                       }
+       pdd = kfd_get_process_device_data(dev, p);
 
-                       /*
-                        * Just mark pdd as unbound, because we still need it
-                        * to call amd_iommu_unbind_pasid() in when the
-                        * process exits.
-                        * We don't call amd_iommu_unbind_pasid() here
-                        * because the IOMMU called us.
-                        */
-                       pdd->bound = false;
+       if (!pdd) {
+               mutex_unlock(&p->mutex);
+               return;
+       }
 
-                       mutex_unlock(&p->mutex);
+       if (pdd->reset_wavefronts) {
+               dbgdev_wave_reset_wavefronts(pdd->dev, p);
+               pdd->reset_wavefronts = false;
+       }
 
-                       return;
-               }
+       /*
+        * Just mark pdd as unbound, because we still need it
+        * to call amd_iommu_unbind_pasid() in when the
+        * process exits.
+        * We don't call amd_iommu_unbind_pasid() here
+        * because the IOMMU called us.
+        */
+       pdd->bound = false;
 
-       srcu_read_unlock(&kfd_processes_srcu, idx);
+       mutex_unlock(&p->mutex);
 }
 
 struct kfd_process_device *kfd_get_first_process_device_data(struct kfd_process *p)
index 7b69070..e1fb40b 100644 (file)
@@ -129,7 +129,7 @@ static int create_cp_queue(struct process_queue_manager *pqm,
        q_properties->vmid = 0;
        q_properties->queue_id = qid;
 
-       retval = init_queue(q, *q_properties);
+       retval = init_queue(q, q_properties);
        if (retval != 0)
                goto err_init_queue;
 
index 9a0c90b..0ab1970 100644 (file)
@@ -63,7 +63,7 @@ void print_queue(struct queue *q)
        pr_debug("Queue Device Address: 0x%p\n", q->device);
 }
 
-int init_queue(struct queue **q, struct queue_properties properties)
+int init_queue(struct queue **q, const struct queue_properties *properties)
 {
        struct queue *tmp;
 
@@ -73,7 +73,7 @@ int init_queue(struct queue **q, struct queue_properties properties)
        if (!tmp)
                return -ENOMEM;
 
-       memcpy(&tmp->properties, &properties, sizeof(struct queue_properties));
+       memcpy(&tmp->properties, properties, sizeof(struct queue_properties));
 
        *q = tmp;
        return 0;
index 884c96f..1e50647 100644 (file)
@@ -1090,19 +1090,21 @@ static uint32_t kfd_generate_gpu_id(struct kfd_dev *gpu)
 {
        uint32_t hashout;
        uint32_t buf[7];
+       uint64_t local_mem_size;
        int i;
 
        if (!gpu)
                return 0;
 
+       local_mem_size = gpu->kfd2kgd->get_vmem_size(gpu->kgd);
+
        buf[0] = gpu->pdev->devfn;
        buf[1] = gpu->pdev->subsystem_vendor;
        buf[2] = gpu->pdev->subsystem_device;
        buf[3] = gpu->pdev->device;
        buf[4] = gpu->pdev->bus->number;
-       buf[5] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd)
-                       & 0xffffffff);
-       buf[6] = (uint32_t)(gpu->kfd2kgd->get_vmem_size(gpu->kgd) >> 32);
+       buf[5] = lower_32_bits(local_mem_size);
+       buf[6] = upper_32_bits(local_mem_size);
 
        for (i = 0, hashout = 0; i < 7; i++)
                hashout ^= hash_32(buf[i], KFD_GPU_ID_HASH_WIDTH);
index a74a0d2..c934b78 100644 (file)
  * Supported ASIC types
  */
 enum amd_asic_type {
-       CHIP_BONAIRE = 0,
+       CHIP_TAHITI = 0,
+       CHIP_PITCAIRN,
+       CHIP_VERDE,
+       CHIP_OLAND,
+       CHIP_HAINAN,
+       CHIP_BONAIRE,
        CHIP_KAVERI,
        CHIP_KABINI,
        CHIP_HAWAII,
@@ -159,8 +164,14 @@ struct amd_ip_funcs {
        bool (*is_idle)(void *handle);
        /* poll for idle */
        int (*wait_for_idle)(void *handle);
+       /* check soft reset the IP block */
+       int (*check_soft_reset)(void *handle);
+       /* pre soft reset the IP block */
+       int (*pre_soft_reset)(void *handle);
        /* soft reset the IP block */
        int (*soft_reset)(void *handle);
+       /* post soft reset the IP block */
+       int (*post_soft_reset)(void *handle);
        /* enable/disable cg for the IP block */
        int (*set_clockgating_state)(void *handle,
                                     enum amd_clockgating_state state);
diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/clearstate_si.h b/drivers/gpu/drm/amd/include/asic_reg/si/clearstate_si.h
new file mode 100644 (file)
index 0000000..66e39cd
--- /dev/null
@@ -0,0 +1,941 @@
+/*
+ * Copyright 2013 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 si_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
+    0x00000000, // DB_DEPTH_BOUNDS_MIN
+    0x00000000, // DB_DEPTH_BOUNDS_MAX
+    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
+    0x00000000, // TA_BC_BASE_ADDR
+    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
+    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, // COHER_DEST_BASE_2
+    0x00000000, // COHER_DEST_BASE_3
+    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
+};
+static const u32 si_SECT_CONTEXT_def_2[] =
+{
+    0x00000000, // CP_PERFMON_CNTX_CNTL
+    0x00000000, // CP_RINGID
+    0x00000000, // CP_VMID
+    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
+    0xffffffff, // VGT_MAX_VTX_INDX
+    0x00000000, // VGT_MIN_VTX_INDX
+    0x00000000, // VGT_INDX_OFFSET
+    0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX
+    0, // HOLE
+    0x00000000, // CB_BLEND_RED
+    0x00000000, // CB_BLEND_GREEN
+    0x00000000, // CB_BLEND_BLUE
+    0x00000000, // CB_BLEND_ALPHA
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // DB_STENCIL_CONTROL
+    0x00000000, // DB_STENCILREFMASK
+    0x00000000, // DB_STENCILREFMASK_BF
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    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
+    0, // HOLE
+    0x00000000, // SPI_PS_INPUT_ENA
+    0x00000000, // SPI_PS_INPUT_ADDR
+    0x00000000, // SPI_INTERP_CONTROL_0
+    0x00000002, // SPI_PS_IN_CONTROL
+    0, // HOLE
+    0x00000000, // SPI_BARYC_CNTL
+    0, // HOLE
+    0x00000000, // SPI_TMPRING_SIZE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // SPI_WAVE_MGMT_1
+    0x00000000, // SPI_WAVE_MGMT_2
+    0x00000000, // SPI_SHADER_POS_FORMAT
+    0x00000000, // SPI_SHADER_Z_FORMAT
+    0x00000000, // SPI_SHADER_COL_FORMAT
+    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, // 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 si_SECT_CONTEXT_def_3[] =
+{
+    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 si_SECT_CONTEXT_def_4[] =
+{
+    0x00000000, // DB_DEPTH_CONTROL
+    0x00000000, // DB_EQAA
+    0x00000000, // CB_COLOR_CONTROL
+    0x00000000, // DB_SHADER_CONTROL
+    0x00090000, // PA_CL_CLIP_CNTL
+    0x00000004, // 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
+    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
+    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
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    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
+    0x00000000, // VGT_GSVS_RING_OFFSET_1
+    0x00000000, // VGT_GSVS_RING_OFFSET_2
+    0x00000000, // VGT_GSVS_RING_OFFSET_3
+    0x00000000, // VGT_GS_OUT_PRIM_TYPE
+    0x00000000, // IA_ENHANCE
+};
+static const u32 si_SECT_CONTEXT_def_5[] =
+{
+    0x00000000, // VGT_PRIMITIVEID_EN
+};
+static const u32 si_SECT_CONTEXT_def_6[] =
+{
+    0x00000000, // VGT_PRIMITIVEID_RESET
+};
+static const u32 si_SECT_CONTEXT_def_7[] =
+{
+    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
+    0x00000000, // VGT_ESGS_RING_ITEMSIZE
+    0x00000000, // VGT_GSVS_RING_ITEMSIZE
+    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
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_1
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_2
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2
+    0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3
+    0x00000000, // VGT_STRMOUT_VTX_STRIDE_3
+    0, // HOLE
+    0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // VGT_SHADER_STAGES_EN
+    0x00000000, // VGT_LS_HS_CONFIG
+    0x00000000, // VGT_GS_VERT_ITEMSIZE
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_1
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_2
+    0x00000000, // VGT_GS_VERT_ITEMSIZE_3
+    0x00000000, // VGT_TF_PARAM
+    0x00000000, // DB_ALPHA_TO_MASK
+    0, // HOLE
+    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
+    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, // 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
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    0, // HOLE
+    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
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR1_BASE
+    0x00000000, // CB_COLOR1_PITCH
+    0x00000000, // CB_COLOR1_SLICE
+    0x00000000, // CB_COLOR1_VIEW
+    0x00000000, // CB_COLOR1_INFO
+    0x00000000, // CB_COLOR1_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR2_BASE
+    0x00000000, // CB_COLOR2_PITCH
+    0x00000000, // CB_COLOR2_SLICE
+    0x00000000, // CB_COLOR2_VIEW
+    0x00000000, // CB_COLOR2_INFO
+    0x00000000, // CB_COLOR2_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR3_BASE
+    0x00000000, // CB_COLOR3_PITCH
+    0x00000000, // CB_COLOR3_SLICE
+    0x00000000, // CB_COLOR3_VIEW
+    0x00000000, // CB_COLOR3_INFO
+    0x00000000, // CB_COLOR3_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR4_BASE
+    0x00000000, // CB_COLOR4_PITCH
+    0x00000000, // CB_COLOR4_SLICE
+    0x00000000, // CB_COLOR4_VIEW
+    0x00000000, // CB_COLOR4_INFO
+    0x00000000, // CB_COLOR4_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR5_BASE
+    0x00000000, // CB_COLOR5_PITCH
+    0x00000000, // CB_COLOR5_SLICE
+    0x00000000, // CB_COLOR5_VIEW
+    0x00000000, // CB_COLOR5_INFO
+    0x00000000, // CB_COLOR5_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR6_BASE
+    0x00000000, // CB_COLOR6_PITCH
+    0x00000000, // CB_COLOR6_SLICE
+    0x00000000, // CB_COLOR6_VIEW
+    0x00000000, // CB_COLOR6_INFO
+    0x00000000, // CB_COLOR6_ATTRIB
+    0, // HOLE
+    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
+    0, // HOLE
+    0, // HOLE
+    0x00000000, // CB_COLOR7_BASE
+    0x00000000, // CB_COLOR7_PITCH
+    0x00000000, // CB_COLOR7_SLICE
+    0x00000000, // CB_COLOR7_VIEW
+    0x00000000, // CB_COLOR7_INFO
+    0x00000000, // CB_COLOR7_ATTRIB
+    0, // HOLE
+    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
+};
+static const struct cs_extent_def si_SECT_CONTEXT_defs[] =
+{
+    {si_SECT_CONTEXT_def_1, 0x0000a000, 212 },
+    {si_SECT_CONTEXT_def_2, 0x0000a0d8, 272 },
+    {si_SECT_CONTEXT_def_3, 0x0000a1f5, 6 },
+    {si_SECT_CONTEXT_def_4, 0x0000a200, 157 },
+    {si_SECT_CONTEXT_def_5, 0x0000a2a1, 1 },
+    {si_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
+    {si_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
+    { NULL, 0, 0 }
+};
+static const struct cs_section_def si_cs_data[] = {
+    { si_SECT_CONTEXT_defs, SECT_CONTEXT },
+    { NULL, SECT_NONE }
+};
diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h b/drivers/gpu/drm/amd/include/asic_reg/si/si_reg.h
new file mode 100644 (file)
index 0000000..895c8e2
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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 __SI_REG_H__
+#define __SI_REG_H__
+
+/* SI */
+#define SI_DC_GPIO_HPD_MASK                      0x196c
+#define SI_DC_GPIO_HPD_A                         0x196d
+#define SI_DC_GPIO_HPD_EN                        0x196e
+#define SI_DC_GPIO_HPD_Y                         0x196f
+
+#define SI_GRPH_CONTROL                          0x1a01
+#       define SI_GRPH_DEPTH(x)                  (((x) & 0x3) << 0)
+#       define SI_GRPH_DEPTH_8BPP                0
+#       define SI_GRPH_DEPTH_16BPP               1
+#       define SI_GRPH_DEPTH_32BPP               2
+#       define SI_GRPH_NUM_BANKS(x)              (((x) & 0x3) << 2)
+#       define SI_ADDR_SURF_2_BANK               0
+#       define SI_ADDR_SURF_4_BANK               1
+#       define SI_ADDR_SURF_8_BANK               2
+#       define SI_ADDR_SURF_16_BANK              3
+#       define SI_GRPH_Z(x)                      (((x) & 0x3) << 4)
+#       define SI_GRPH_BANK_WIDTH(x)             (((x) & 0x3) << 6)
+#       define SI_ADDR_SURF_BANK_WIDTH_1         0
+#       define SI_ADDR_SURF_BANK_WIDTH_2         1
+#       define SI_ADDR_SURF_BANK_WIDTH_4         2
+#       define SI_ADDR_SURF_BANK_WIDTH_8         3
+#       define SI_GRPH_FORMAT(x)                 (((x) & 0x7) << 8)
+/* 8 BPP */
+#       define SI_GRPH_FORMAT_INDEXED            0
+/* 16 BPP */
+#       define SI_GRPH_FORMAT_ARGB1555           0
+#       define SI_GRPH_FORMAT_ARGB565            1
+#       define SI_GRPH_FORMAT_ARGB4444           2
+#       define SI_GRPH_FORMAT_AI88               3
+#       define SI_GRPH_FORMAT_MONO16             4
+#       define SI_GRPH_FORMAT_BGRA5551           5
+/* 32 BPP */
+#       define SI_GRPH_FORMAT_ARGB8888           0
+#       define SI_GRPH_FORMAT_ARGB2101010        1
+#       define SI_GRPH_FORMAT_32BPP_DIG          2
+#       define SI_GRPH_FORMAT_8B_ARGB2101010     3
+#       define SI_GRPH_FORMAT_BGRA1010102        4
+#       define SI_GRPH_FORMAT_8B_BGRA1010102     5
+#       define SI_GRPH_FORMAT_RGB111110          6
+#       define SI_GRPH_FORMAT_BGR101111          7
+#       define SI_GRPH_BANK_HEIGHT(x)            (((x) & 0x3) << 11)
+#       define SI_ADDR_SURF_BANK_HEIGHT_1        0
+#       define SI_ADDR_SURF_BANK_HEIGHT_2        1
+#       define SI_ADDR_SURF_BANK_HEIGHT_4        2
+#       define SI_ADDR_SURF_BANK_HEIGHT_8        3
+#       define SI_GRPH_TILE_SPLIT(x)             (((x) & 0x7) << 13)
+#       define SI_ADDR_SURF_TILE_SPLIT_64B       0
+#       define SI_ADDR_SURF_TILE_SPLIT_128B      1
+#       define SI_ADDR_SURF_TILE_SPLIT_256B      2
+#       define SI_ADDR_SURF_TILE_SPLIT_512B      3
+#       define SI_ADDR_SURF_TILE_SPLIT_1KB       4
+#       define SI_ADDR_SURF_TILE_SPLIT_2KB       5
+#       define SI_ADDR_SURF_TILE_SPLIT_4KB       6
+#       define SI_GRPH_MACRO_TILE_ASPECT(x)      (((x) & 0x3) << 18)
+#       define SI_ADDR_SURF_MACRO_TILE_ASPECT_1  0
+#       define SI_ADDR_SURF_MACRO_TILE_ASPECT_2  1
+#       define SI_ADDR_SURF_MACRO_TILE_ASPECT_4  2
+#       define SI_ADDR_SURF_MACRO_TILE_ASPECT_8  3
+#       define SI_GRPH_ARRAY_MODE(x)             (((x) & 0x7) << 20)
+#       define SI_GRPH_ARRAY_LINEAR_GENERAL      0
+#       define SI_GRPH_ARRAY_LINEAR_ALIGNED      1
+#       define SI_GRPH_ARRAY_1D_TILED_THIN1      2
+#       define SI_GRPH_ARRAY_2D_TILED_THIN1      4
+#       define SI_GRPH_PIPE_CONFIG(x)           (((x) & 0x1f) << 24)
+#       define SI_ADDR_SURF_P2                  0
+#       define SI_ADDR_SURF_P4_8x16             4
+#       define SI_ADDR_SURF_P4_16x16            5
+#       define SI_ADDR_SURF_P4_16x32            6
+#       define SI_ADDR_SURF_P4_32x32            7
+#       define SI_ADDR_SURF_P8_16x16_8x16       8
+#       define SI_ADDR_SURF_P8_16x32_8x16       9
+#       define SI_ADDR_SURF_P8_32x32_8x16       10
+#       define SI_ADDR_SURF_P8_16x32_16x16      11
+#       define SI_ADDR_SURF_P8_32x32_16x16      12
+#       define SI_ADDR_SURF_P8_32x32_16x32      13
+#       define SI_ADDR_SURF_P8_32x64_32x32      14
+
+#endif
diff --git a/drivers/gpu/drm/amd/include/asic_reg/si/sid.h b/drivers/gpu/drm/amd/include/asic_reg/si/sid.h
new file mode 100644 (file)
index 0000000..c57eff1
--- /dev/null
@@ -0,0 +1,2461 @@
+/*
+ * 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 SI_H
+#define SI_H
+
+#define TAHITI_RB_BITMAP_WIDTH_PER_SH  2
+
+#define TAHITI_GB_ADDR_CONFIG_GOLDEN        0x12011003
+#define VERDE_GB_ADDR_CONFIG_GOLDEN         0x12010002
+#define HAINAN_GB_ADDR_CONFIG_GOLDEN        0x02010001
+
+#define SI_MAX_SH_GPRS                 256
+#define SI_MAX_TEMP_GPRS               16
+#define SI_MAX_SH_THREADS              256
+#define SI_MAX_SH_STACK_ENTRIES        4096
+#define SI_MAX_FRC_EOV_CNT             16384
+#define SI_MAX_BACKENDS                8
+#define SI_MAX_BACKENDS_MASK           0xFF
+#define SI_MAX_BACKENDS_PER_SE_MASK     0x0F
+#define SI_MAX_SIMDS                   12
+#define SI_MAX_SIMDS_MASK              0x0FFF
+#define SI_MAX_SIMDS_PER_SE_MASK        0x00FF
+#define SI_MAX_PIPES                   8
+#define SI_MAX_PIPES_MASK              0xFF
+#define SI_MAX_PIPES_PER_SIMD_MASK      0x3F
+#define SI_MAX_LDS_NUM                 0xFFFF
+#define SI_MAX_TCC                     16
+#define SI_MAX_TCC_MASK                0xFFFF
+
+#define AMDGPU_NUM_OF_VMIDS            8
+
+/* SMC IND accessor regs */
+#define SMC_IND_INDEX_0                              0x80
+#define SMC_IND_DATA_0                               0x81
+
+#define SMC_IND_ACCESS_CNTL                          0x8A
+#       define AUTO_INCREMENT_IND_0                  (1 << 0)
+#define SMC_MESSAGE_0                                0x8B
+#define SMC_RESP_0                                   0x8C
+
+/* CG IND registers are accessed via SMC indirect space + SMC_CG_IND_START */
+#define SMC_CG_IND_START                    0xc0030000
+#define SMC_CG_IND_END                      0xc0040000
+
+#define        CG_CGTT_LOCAL_0                         0x400
+#define        CG_CGTT_LOCAL_1                         0x401
+
+/* SMC IND registers */
+#define        SMC_SYSCON_RESET_CNTL                           0x80000000
+#       define RST_REG                                  (1 << 0)
+#define        SMC_SYSCON_CLOCK_CNTL_0                         0x80000004
+#       define CK_DISABLE                               (1 << 0)
+#       define CKEN                                     (1 << 24)
+
+#define VGA_HDP_CONTROL                                0xCA
+#define                VGA_MEMORY_DISABLE                              (1 << 4)
+
+#define DCCG_DISP_SLOW_SELECT_REG                       0x13F
+#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                               0x180
+#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                             0x181
+#define                SCLK_MUX_SEL(x)                         ((x) << 0)
+#define                SCLK_MUX_SEL_MASK                       (0x1ff << 0)
+#define                SPLL_CTLREQ_CHG                         (1 << 23)
+#define                SCLK_MUX_UPDATE                         (1 << 26)
+#define        CG_SPLL_FUNC_CNTL_3                             0x182
+#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        CG_SPLL_FUNC_CNTL_4                             0x183
+
+#define        SPLL_STATUS                                     0x185
+#define                SPLL_CHG_STATUS                         (1 << 1)
+#define        SPLL_CNTL_MODE                                  0x186
+#define                SPLL_SW_DIR_CONTROL                     (1 << 0)
+#      define SPLL_REFCLK_SEL(x)                       ((x) << 26)
+#      define SPLL_REFCLK_SEL_MASK                     (3 << 26)
+
+#define        CG_SPLL_SPREAD_SPECTRUM                         0x188
+#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                       0x189
+#define                CLK_V(x)                                ((x) << 0)
+#define                CLK_V_MASK                              (0x3ffffff << 0)
+#define                CLK_V_SHIFT                             0
+
+#define        CG_SPLL_AUTOSCALE_CNTL                          0x18b
+#       define AUTOSCALE_ON_SS_CLEAR                    (1 << 9)
+
+/* discrete uvd clocks */
+#define        CG_UPLL_FUNC_CNTL                               0x18d
+#      define UPLL_RESET_MASK                          0x00000001
+#      define UPLL_SLEEP_MASK                          0x00000002
+#      define UPLL_BYPASS_EN_MASK                      0x00000004
+#      define UPLL_CTLREQ_MASK                         0x00000008
+#      define UPLL_VCO_MODE_MASK                       0x00000600
+#      define UPLL_REF_DIV_MASK                        0x003F0000
+#      define UPLL_CTLACK_MASK                         0x40000000
+#      define UPLL_CTLACK2_MASK                        0x80000000
+#define        CG_UPLL_FUNC_CNTL_2                             0x18e
+#      define UPLL_PDIV_A(x)                           ((x) << 0)
+#      define UPLL_PDIV_A_MASK                         0x0000007F
+#      define UPLL_PDIV_B(x)                           ((x) << 8)
+#      define UPLL_PDIV_B_MASK                         0x00007F00
+#      define VCLK_SRC_SEL(x)                          ((x) << 20)
+#      define VCLK_SRC_SEL_MASK                        0x01F00000
+#      define DCLK_SRC_SEL(x)                          ((x) << 25)
+#      define DCLK_SRC_SEL_MASK                        0x3E000000
+#define        CG_UPLL_FUNC_CNTL_3                             0x18f
+#      define UPLL_FB_DIV(x)                           ((x) << 0)
+#      define UPLL_FB_DIV_MASK                         0x01FFFFFF
+#define        CG_UPLL_FUNC_CNTL_4                             0x191
+#      define UPLL_SPARE_ISPARE9                       0x00020000
+#define        CG_UPLL_FUNC_CNTL_5                             0x192
+#      define RESET_ANTI_MUX_MASK                      0x00000200
+#define        CG_UPLL_SPREAD_SPECTRUM                         0x194
+#      define SSEN_MASK                                0x00000001
+
+#define        MPLL_BYPASSCLK_SEL                              0x197
+#      define MPLL_CLKOUT_SEL(x)                       ((x) << 8)
+#      define MPLL_CLKOUT_SEL_MASK                     0xFF00
+
+#define CG_CLKPIN_CNTL                                    0x198
+#       define XTALIN_DIVIDE                              (1 << 1)
+#       define BCLK_AS_XCLK                               (1 << 2)
+#define CG_CLKPIN_CNTL_2                                  0x199
+#       define FORCE_BIF_REFCLK_EN                        (1 << 3)
+#       define MUX_TCLK_TO_XCLK                           (1 << 8)
+
+#define        THM_CLK_CNTL                                    0x19b
+#      define CMON_CLK_SEL(x)                          ((x) << 0)
+#      define CMON_CLK_SEL_MASK                        0xFF
+#      define TMON_CLK_SEL(x)                          ((x) << 8)
+#      define TMON_CLK_SEL_MASK                        0xFF00
+#define        MISC_CLK_CNTL                                   0x19c
+#      define DEEP_SLEEP_CLK_SEL(x)                    ((x) << 0)
+#      define DEEP_SLEEP_CLK_SEL_MASK                  0xFF
+#      define ZCLK_SEL(x)                              ((x) << 8)
+#      define ZCLK_SEL_MASK                            0xFF00
+
+#define        CG_THERMAL_CTRL                                 0x1c0
+#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_STATUS                               0x1c1
+#define                FDO_PWM_DUTY(x)                         ((x) << 9)
+#define                FDO_PWM_DUTY_MASK                       (0xff << 9)
+#define                FDO_PWM_DUTY_SHIFT                      9
+#define        CG_THERMAL_INT                                  0x1c2
+#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_CTRL                                    0x1c4
+#define                TEMP_SEL(x)                                     ((x) << 20)
+#define                TEMP_SEL_MASK                                   (0xff << 20)
+#define                TEMP_SEL_SHIFT                                  20
+#define        CG_MULT_THERMAL_STATUS                                  0x1c5
+#define                ASIC_MAX_TEMP(x)                                ((x) << 0)
+#define                ASIC_MAX_TEMP_MASK                              0x000001ff
+#define                ASIC_MAX_TEMP_SHIFT                             0
+#define                CTF_TEMP(x)                                     ((x) << 9)
+#define                CTF_TEMP_MASK                                   0x0003fe00
+#define                CTF_TEMP_SHIFT                                  9
+
+#define        CG_FDO_CTRL0                                    0x1d5
+#define                FDO_STATIC_DUTY(x)                      ((x) << 0)
+#define                FDO_STATIC_DUTY_MASK                    0x000000FF
+#define                FDO_STATIC_DUTY_SHIFT                   0
+#define        CG_FDO_CTRL1                                    0x1d6
+#define                FMAX_DUTY100(x)                         ((x) << 0)
+#define                FMAX_DUTY100_MASK                       0x000000FF
+#define                FMAX_DUTY100_SHIFT                      0
+#define        CG_FDO_CTRL2                                    0x1d7
+#define                TMIN(x)                                 ((x) << 0)
+#define                TMIN_MASK                               0x000000FF
+#define                TMIN_SHIFT                              0
+#define                FDO_PWM_MODE(x)                         ((x) << 11)
+#define                FDO_PWM_MODE_MASK                       (7 << 11)
+#define                FDO_PWM_MODE_SHIFT                      11
+#define                TACH_PWM_RESP_RATE(x)                   ((x) << 25)
+#define                TACH_PWM_RESP_RATE_MASK                 (0x7f << 25)
+#define                TACH_PWM_RESP_RATE_SHIFT                25
+
+#define CG_TACH_CTRL                                    0x1dc
+#       define EDGE_PER_REV(x)                          ((x) << 0)
+#       define EDGE_PER_REV_MASK                        (0x7 << 0)
+#       define EDGE_PER_REV_SHIFT                       0
+#       define TARGET_PERIOD(x)                         ((x) << 3)
+#       define TARGET_PERIOD_MASK                       0xfffffff8
+#       define TARGET_PERIOD_SHIFT                      3
+#define CG_TACH_STATUS                                  0x1dd
+#       define TACH_PERIOD(x)                           ((x) << 0)
+#       define TACH_PERIOD_MASK                         0xffffffff
+#       define TACH_PERIOD_SHIFT                        0
+
+#define GENERAL_PWRMGT                                  0x1e0
+#       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 SW_SMIO_INDEX(x)                         ((x) << 6)
+#       define SW_SMIO_INDEX_MASK                       (1 << 6)
+#       define SW_SMIO_INDEX_SHIFT                      6
+#       define VOLT_PWRMGT_EN                           (1 << 10)
+#       define DYN_SPREAD_SPECTRUM_EN                   (1 << 23)
+#define CG_TPC                                            0x1e1
+#define SCLK_PWRMGT_CNTL                                  0x1e2
+#       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 TARGET_AND_CURRENT_PROFILE_INDEX                  0x1e6
+#       define CURRENT_STATE_INDEX_MASK                   (0xf << 4)
+#       define CURRENT_STATE_INDEX_SHIFT                  4
+
+#define CG_FTV                                            0x1ef
+
+#define CG_FFCT_0                                         0x1f0
+#       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                                          0x1ff
+#       define BSP(x)                                  ((x) << 0)
+#       define BSP_MASK                                        (0xffff << 0)
+#       define BSU(x)                                  ((x) << 16)
+#       define BSU_MASK                                        (0xf << 16)
+#define CG_AT                                           0x200
+#       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                                          0x201
+#       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                                            0x203
+#       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                               0x20a
+#       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_ULV_CONTROL                                  0x21e
+#define        CG_ULV_PARAMETER                                0x21f
+
+#define        SMC_SCRATCH0                                    0x221
+
+#define        CG_CAC_CTRL                                     0x22e
+#      define CAC_WINDOW(x)                            ((x) << 0)
+#      define CAC_WINDOW_MASK                          0x00ffffff
+
+#define DMIF_ADDR_CONFIG                               0x2F5
+
+#define DMIF_ADDR_CALC                                 0x300
+
+#define        PIPE0_DMIF_BUFFER_CONTROL                         0x0328
+#       define DMIF_BUFFERS_ALLOCATED(x)                  ((x) << 0)
+#       define DMIF_BUFFERS_ALLOCATED_COMPLETED           (1 << 4)
+
+#define        SRBM_STATUS                                     0x394
+#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        SRBM_SOFT_RESET                                 0x398
+#define                SOFT_RESET_BIF                          (1 << 1)
+#define                SOFT_RESET_DC                           (1 << 5)
+#define                SOFT_RESET_DMA1                         (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_DMA                          (1 << 20)
+#define                SOFT_RESET_TST                          (1 << 21)
+#define                SOFT_RESET_REGBB                        (1 << 22)
+#define                SOFT_RESET_ORB                          (1 << 23)
+
+#define        CC_SYS_RB_BACKEND_DISABLE                       0x3A0
+#define        GC_USER_SYS_RB_BACKEND_DISABLE                  0x3A1
+
+#define SRBM_READ_ERROR                                        0x3A6
+#define SRBM_INT_CNTL                                  0x3A8
+#define SRBM_INT_ACK                                   0x3AA
+
+#define        SRBM_STATUS2                                    0x3B1
+#define                DMA_BUSY                                (1 << 5)
+#define                DMA1_BUSY                               (1 << 6)
+
+#define VM_L2_CNTL                                     0x500
+#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                                    0x501
+#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                                    0x502
+#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                                    0x503
+#define                L2_BUSY                                         (1 << 0)
+#define VM_CONTEXT0_CNTL                               0x504
+#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                PAGE_TABLE_BLOCK_SIZE(x)                        (((x) & 0xF) << 24)
+#define VM_CONTEXT1_CNTL                               0x505
+#define VM_CONTEXT0_CNTL2                              0x50C
+#define VM_CONTEXT1_CNTL2                              0x50D
+#define        VM_CONTEXT8_PAGE_TABLE_BASE_ADDR                0x50E
+#define        VM_CONTEXT9_PAGE_TABLE_BASE_ADDR                0x50F
+#define        VM_CONTEXT10_PAGE_TABLE_BASE_ADDR               0x510
+#define        VM_CONTEXT11_PAGE_TABLE_BASE_ADDR               0x511
+#define        VM_CONTEXT12_PAGE_TABLE_BASE_ADDR               0x512
+#define        VM_CONTEXT13_PAGE_TABLE_BASE_ADDR               0x513
+#define        VM_CONTEXT14_PAGE_TABLE_BASE_ADDR               0x514
+#define        VM_CONTEXT15_PAGE_TABLE_BASE_ADDR               0x515
+
+#define        VM_CONTEXT1_PROTECTION_FAULT_ADDR               0x53f
+#define        VM_CONTEXT1_PROTECTION_FAULT_STATUS             0x537
+#define                PROTECTIONS_MASK                        (0xf << 0)
+#define                PROTECTIONS_SHIFT                       0
+               /* bit 0: range
+                * bit 1: pde0
+                * bit 2: valid
+                * bit 3: read
+                * bit 4: write
+                */
+#define                MEMORY_CLIENT_ID_MASK                   (0xff << 12)
+#define                MEMORY_CLIENT_ID_SHIFT                  12
+#define                MEMORY_CLIENT_RW_MASK                   (1 << 24)
+#define                MEMORY_CLIENT_RW_SHIFT                  24
+#define                FAULT_VMID_MASK                         (0xf << 25)
+#define                FAULT_VMID_SHIFT                        25
+
+#define VM_INVALIDATE_REQUEST                          0x51E
+#define VM_INVALIDATE_RESPONSE                         0x51F
+
+#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR      0x546
+#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR      0x547
+
+#define        VM_CONTEXT0_PAGE_TABLE_BASE_ADDR                0x54F
+#define        VM_CONTEXT1_PAGE_TABLE_BASE_ADDR                0x550
+#define        VM_CONTEXT2_PAGE_TABLE_BASE_ADDR                0x551
+#define        VM_CONTEXT3_PAGE_TABLE_BASE_ADDR                0x552
+#define        VM_CONTEXT4_PAGE_TABLE_BASE_ADDR                0x553
+#define        VM_CONTEXT5_PAGE_TABLE_BASE_ADDR                0x554
+#define        VM_CONTEXT6_PAGE_TABLE_BASE_ADDR                0x555
+#define        VM_CONTEXT7_PAGE_TABLE_BASE_ADDR                0x556
+#define        VM_CONTEXT0_PAGE_TABLE_START_ADDR               0x557
+#define        VM_CONTEXT1_PAGE_TABLE_START_ADDR               0x558
+
+#define        VM_CONTEXT0_PAGE_TABLE_END_ADDR                 0x55F
+#define        VM_CONTEXT1_PAGE_TABLE_END_ADDR                 0x560
+
+#define VM_L2_CG                                       0x570
+#define                MC_CG_ENABLE                            (1 << 18)
+#define                MC_LS_ENABLE                            (1 << 19)
+
+#define MC_SHARED_CHMAP                                                0x801
+#define                NOOFCHAN_SHIFT                                  12
+#define                NOOFCHAN_MASK                                   0x0000f000
+#define MC_SHARED_CHREMAP                                      0x802
+
+#define        MC_VM_FB_LOCATION                               0x809
+#define        MC_VM_AGP_TOP                                   0x80A
+#define        MC_VM_AGP_BOT                                   0x80B
+#define        MC_VM_AGP_BASE                                  0x80C
+#define        MC_VM_SYSTEM_APERTURE_LOW_ADDR                  0x80D
+#define        MC_VM_SYSTEM_APERTURE_HIGH_ADDR                 0x80E
+#define        MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR              0x80F
+
+#define        MC_VM_MX_L1_TLB_CNTL                            0x819
+#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_SHARED_BLACKOUT_CNTL                        0x82B
+
+#define MC_HUB_MISC_HUB_CG                             0x82E
+#define MC_HUB_MISC_VM_CG                              0x82F
+
+#define MC_HUB_MISC_SIP_CG                             0x830
+
+#define MC_XPB_CLK_GAT                                 0x91E
+
+#define MC_CITF_MISC_RD_CG                             0x992
+#define MC_CITF_MISC_WR_CG                             0x993
+#define MC_CITF_MISC_VM_CG                             0x994
+
+#define        MC_ARB_RAMCFG                                   0x9D8
+#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                CHANSIZE_OVERRIDE                               (1 << 11)
+#define                NOOFGROUPS_SHIFT                                12
+#define                NOOFGROUPS_MASK                                 0x00001000
+
+#define        MC_ARB_DRAM_TIMING                              0x9DD
+#define        MC_ARB_DRAM_TIMING2                             0x9DE
+
+#define MC_ARB_BURST_TIME                               0xA02
+#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_TRAIN_WAKEUP_CNTL                        0xA3A
+#define                TRAIN_DONE_D0                           (1 << 30)
+#define                TRAIN_DONE_D1                           (1 << 31)
+
+#define MC_SEQ_SUP_CNTL                                0xA32
+#define                RUN_MASK                                (1 << 0)
+#define MC_SEQ_SUP_PGM                                 0xA33
+#define MC_PMG_AUTO_CMD                                0xA34
+
+#define MC_IO_PAD_CNTL_D0                              0xA74
+#define                MEM_FALL_OUT_CMD                        (1 << 8)
+
+#define MC_SEQ_RAS_TIMING                               0xA28
+#define MC_SEQ_CAS_TIMING                               0xA29
+#define MC_SEQ_MISC_TIMING                              0xA2A
+#define MC_SEQ_MISC_TIMING2                             0xA2B
+#define MC_SEQ_PMG_TIMING                               0xA2C
+#define MC_SEQ_RD_CTL_D0                                0xA2D
+#define MC_SEQ_RD_CTL_D1                                0xA2E
+#define MC_SEQ_WR_CTL_D0                                0xA2F
+#define MC_SEQ_WR_CTL_D1                                0xA30
+
+#define MC_SEQ_MISC0                                           0xA80
+#define        MC_SEQ_MISC0_VEN_ID_SHIFT               8
+#define        MC_SEQ_MISC0_VEN_ID_MASK                0x00000f00
+#define        MC_SEQ_MISC0_VEN_ID_VALUE               3
+#define        MC_SEQ_MISC0_REV_ID_SHIFT               12
+#define        MC_SEQ_MISC0_REV_ID_MASK                0x0000f000
+#define        MC_SEQ_MISC0_REV_ID_VALUE               1
+#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                                    0xA81
+#define MC_SEQ_RESERVE_M                                0xA82
+#define MC_PMG_CMD_EMRS                                 0xA83
+
+#define MC_SEQ_IO_DEBUG_INDEX                          0xA91
+#define MC_SEQ_IO_DEBUG_DATA                                   0xA92
+
+#define MC_SEQ_MISC5                                    0xA95
+#define MC_SEQ_MISC6                                    0xA96
+
+#define MC_SEQ_MISC7                                    0xA99
+
+#define MC_SEQ_RAS_TIMING_LP                            0xA9B
+#define MC_SEQ_CAS_TIMING_LP                            0xA9C
+#define MC_SEQ_MISC_TIMING_LP                           0xA9D
+#define MC_SEQ_MISC_TIMING2_LP                          0xA9E
+#define MC_SEQ_WR_CTL_D0_LP                             0xA9F
+#define MC_SEQ_WR_CTL_D1_LP                             0xAA0
+#define MC_SEQ_PMG_CMD_EMRS_LP                          0xAA1
+#define MC_SEQ_PMG_CMD_MRS_LP                           0xAA2
+
+#define MC_PMG_CMD_MRS                                  0xAAB
+
+#define MC_SEQ_RD_CTL_D0_LP                             0xAC7
+#define MC_SEQ_RD_CTL_D1_LP                             0xAC8
+
+#define MC_PMG_CMD_MRS1                                 0xAD1
+#define MC_SEQ_PMG_CMD_MRS1_LP                          0xAD2
+#define MC_SEQ_PMG_TIMING_LP                            0xAD3
+
+#define MC_SEQ_WR_CTL_2                                 0xAD5
+#define MC_SEQ_WR_CTL_2_LP                              0xAD6
+#define MC_PMG_CMD_MRS2                                 0xAD7
+#define MC_SEQ_PMG_CMD_MRS2_LP                          0xAD8
+
+#define        MCLK_PWRMGT_CNTL                                0xAE8
+#       define DLL_SPEED(x)                            ((x) << 0)
+#       define DLL_SPEED_MASK                          (0x1f << 0)
+#       define DLL_READY                                (1 << 6)
+#       define MC_INT_CNTL                              (1 << 7)
+#       define MRDCK0_PDNB                              (1 << 8)
+#       define MRDCK1_PDNB                              (1 << 9)
+#       define MRDCK0_RESET                             (1 << 16)
+#       define MRDCK1_RESET                             (1 << 17)
+#       define DLL_READY_READ                           (1 << 24)
+#define        DLL_CNTL                                        0xAE9
+#       define MRDCK0_BYPASS                            (1 << 24)
+#       define MRDCK1_BYPASS                            (1 << 25)
+
+#define        MPLL_CNTL_MODE                                  0xAEC
+#       define MPLL_MCLK_SEL                            (1 << 11)
+#define        MPLL_FUNC_CNTL                                  0xAED
+#define                BWCTRL(x)                               ((x) << 20)
+#define                BWCTRL_MASK                             (0xff << 20)
+#define        MPLL_FUNC_CNTL_1                                0xAEE
+#define                VCO_MODE(x)                             ((x) << 0)
+#define                VCO_MODE_MASK                           (3 << 0)
+#define                CLKFRAC(x)                              ((x) << 4)
+#define                CLKFRAC_MASK                            (0xfff << 4)
+#define                CLKF(x)                                 ((x) << 16)
+#define                CLKF_MASK                               (0xfff << 16)
+#define        MPLL_FUNC_CNTL_2                                0xAEF
+#define        MPLL_AD_FUNC_CNTL                               0xAF0
+#define                YCLK_POST_DIV(x)                        ((x) << 0)
+#define                YCLK_POST_DIV_MASK                      (7 << 0)
+#define        MPLL_DQ_FUNC_CNTL                               0xAF1
+#define                YCLK_SEL(x)                             ((x) << 4)
+#define                YCLK_SEL_MASK                           (1 << 4)
+
+#define        MPLL_SS1                                        0xAF3
+#define                CLKV(x)                                 ((x) << 0)
+#define                CLKV_MASK                               (0x3ffffff << 0)
+#define        MPLL_SS2                                        0xAF4
+#define                CLKS(x)                                 ((x) << 0)
+#define                CLKS_MASK                               (0xfff << 0)
+
+#define        HDP_HOST_PATH_CNTL                              0xB00
+#define        CLOCK_GATING_DIS                        (1 << 23)
+#define        HDP_NONSURFACE_BASE                             0xB01
+#define        HDP_NONSURFACE_INFO                             0xB02
+#define        HDP_NONSURFACE_SIZE                             0xB03
+
+#define HDP_DEBUG0                                     0xBCC
+
+#define HDP_ADDR_CONFIG                                0xBD2
+#define HDP_MISC_CNTL                                  0xBD3
+#define        HDP_FLUSH_INVALIDATE_CACHE                      (1 << 0)
+#define HDP_MEM_POWER_LS                               0xBD4
+#define        HDP_LS_ENABLE                           (1 << 0)
+
+#define ATC_MISC_CG                                    0xCD4
+
+#define IH_RB_CNTL                                        0xF80
+#       define IH_RB_ENABLE                               (1 << 0)
+#       define IH_IB_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                                        0xF81
+#define IH_RB_RPTR                                        0xF82
+#define IH_RB_WPTR                                        0xF83
+#       define RB_OVERFLOW                                (1 << 0)
+#       define WPTR_OFFSET_MASK                           0x3fffc
+#define IH_RB_WPTR_ADDR_HI                                0xF84
+#define IH_RB_WPTR_ADDR_LO                                0xF85
+#define IH_CNTL                                           0xF86
+#       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                                  0x150A
+
+#define INTERRUPT_CNTL                                    0x151A
+#       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                                   0x151B
+
+#define HDP_MEM_COHERENCY_FLUSH_CNTL                   0x1520
+
+#define        BIF_FB_EN                                               0x1524
+#define                FB_READ_EN                                      (1 << 0)
+#define                FB_WRITE_EN                                     (1 << 1)
+
+#define HDP_REG_COHERENCY_FLUSH_CNTL                   0x1528
+
+/* DCE6 ELD audio interface */
+#define AZ_F0_CODEC_ENDPOINT_INDEX                       0x1780
+#       define AZ_ENDPOINT_REG_INDEX(x)                  (((x) & 0xff) << 0)
+#       define AZ_ENDPOINT_REG_WRITE_EN                  (1 << 8)
+#define AZ_F0_CODEC_ENDPOINT_DATA                        0x1781
+
+#define AZ_F0_CODEC_PIN_CONTROL_CHANNEL_SPEAKER          0x25
+#define                SPEAKER_ALLOCATION(x)                   (((x) & 0x7f) << 0)
+#define                SPEAKER_ALLOCATION_MASK                 (0x7f << 0)
+#define                SPEAKER_ALLOCATION_SHIFT                0
+#define                HDMI_CONNECTION                         (1 << 16)
+#define                DP_CONNECTION                           (1 << 17)
+
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR0        0x28 /* LPCM */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR1        0x29 /* AC3 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR2        0x2A /* MPEG1 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR3        0x2B /* MP3 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR4        0x2C /* MPEG2 */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR5        0x2D /* AAC */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR6        0x2E /* DTS */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR7        0x2F /* ATRAC */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR8        0x30 /* one bit audio - leave at 0 (default) */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR9        0x31 /* Dolby Digital */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR10       0x32 /* DTS-HD */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR11       0x33 /* MAT-MLP */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR12       0x34 /* DTS */
+#define AZ_F0_CODEC_PIN_CONTROL_AUDIO_DESCRIPTOR13       0x35 /* WMA Pro */
+#       define MAX_CHANNELS(x)                            (((x) & 0x7) << 0)
+/* max channels minus one.  7 = 8 channels */
+#       define SUPPORTED_FREQUENCIES(x)                   (((x) & 0xff) << 8)
+#       define DESCRIPTOR_BYTE_2(x)                       (((x) & 0xff) << 16)
+#       define SUPPORTED_FREQUENCIES_STEREO(x)            (((x) & 0xff) << 24) /* LPCM only */
+/* SUPPORTED_FREQUENCIES, SUPPORTED_FREQUENCIES_STEREO
+ * bit0 = 32 kHz
+ * bit1 = 44.1 kHz
+ * bit2 = 48 kHz
+ * bit3 = 88.2 kHz
+ * bit4 = 96 kHz
+ * bit5 = 176.4 kHz
+ * bit6 = 192 kHz
+ */
+
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_LIPSYNC         0x37
+#       define VIDEO_LIPSYNC(x)                           (((x) & 0xff) << 0)
+#       define AUDIO_LIPSYNC(x)                           (((x) & 0xff) << 8)
+/* VIDEO_LIPSYNC, AUDIO_LIPSYNC
+ * 0   = invalid
+ * x   = legal delay value
+ * 255 = sync not supported
+ */
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_HBR             0x38
+#       define HBR_CAPABLE                                (1 << 0) /* enabled by default */
+
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO0               0x3a
+#       define MANUFACTURER_ID(x)                        (((x) & 0xffff) << 0)
+#       define PRODUCT_ID(x)                             (((x) & 0xffff) << 16)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO1               0x3b
+#       define SINK_DESCRIPTION_LEN(x)                   (((x) & 0xff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO2               0x3c
+#       define PORT_ID0(x)                               (((x) & 0xffffffff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO3               0x3d
+#       define PORT_ID1(x)                               (((x) & 0xffffffff) << 0)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO4               0x3e
+#       define DESCRIPTION0(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION1(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION2(x)                           (((x) & 0xff) << 16)
+#       define DESCRIPTION3(x)                           (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO5               0x3f
+#       define DESCRIPTION4(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION5(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION6(x)                           (((x) & 0xff) << 16)
+#       define DESCRIPTION7(x)                           (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO6               0x40
+#       define DESCRIPTION8(x)                           (((x) & 0xff) << 0)
+#       define DESCRIPTION9(x)                           (((x) & 0xff) << 8)
+#       define DESCRIPTION10(x)                          (((x) & 0xff) << 16)
+#       define DESCRIPTION11(x)                          (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO7               0x41
+#       define DESCRIPTION12(x)                          (((x) & 0xff) << 0)
+#       define DESCRIPTION13(x)                          (((x) & 0xff) << 8)
+#       define DESCRIPTION14(x)                          (((x) & 0xff) << 16)
+#       define DESCRIPTION15(x)                          (((x) & 0xff) << 24)
+#define AZ_F0_CODEC_PIN_CONTROL_SINK_INFO8               0x42
+#       define DESCRIPTION16(x)                          (((x) & 0xff) << 0)
+#       define DESCRIPTION17(x)                          (((x) & 0xff) << 8)
+
+#define AZ_F0_CODEC_PIN_CONTROL_HOT_PLUG_CONTROL         0x54
+#       define AUDIO_ENABLED                             (1 << 31)
+
+#define AZ_F0_CODEC_PIN_CONTROL_RESPONSE_CONFIGURATION_DEFAULT  0x56
+#define                PORT_CONNECTIVITY_MASK                          (3 << 30)
+#define                PORT_CONNECTIVITY_SHIFT                         30
+
+#define        DC_LB_MEMORY_SPLIT                                      0x1AC3
+#define                DC_LB_MEMORY_CONFIG(x)                          ((x) << 20)
+
+#define        PRIORITY_A_CNT                                          0x1AC6
+#define                PRIORITY_MARK_MASK                              0x7fff
+#define                PRIORITY_OFF                                    (1 << 16)
+#define                PRIORITY_ALWAYS_ON                              (1 << 20)
+#define        PRIORITY_B_CNT                                          0x1AC7
+
+#define        DPG_PIPE_ARBITRATION_CONTROL3                           0x1B32
+#       define LATENCY_WATERMARK_MASK(x)                       ((x) << 16)
+#define        DPG_PIPE_LATENCY_CONTROL                                0x1B33
+#       define LATENCY_LOW_WATERMARK(x)                                ((x) << 0)
+#       define LATENCY_HIGH_WATERMARK(x)                       ((x) << 16)
+
+/* 0x6bb8, 0x77b8, 0x103b8, 0x10fb8, 0x11bb8, 0x127b8 */
+#define VLINE_STATUS                                    0x1AEE
+#       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)
+/* 0x6bbc, 0x77bc, 0x103bc, 0x10fbc, 0x11bbc, 0x127bc */
+#define VBLANK_STATUS                                   0x1AEF
+#       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)
+
+/* 0x6b40, 0x7740, 0x10340, 0x10f40, 0x11b40, 0x12740 */
+#define INT_MASK                                        0x1AD0
+#       define VBLANK_INT_MASK                          (1 << 0)
+#       define VLINE_INT_MASK                           (1 << 4)
+
+#define DISP_INTERRUPT_STATUS                           0x183D
+#       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                  0x183E
+#       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                 0x183F
+#       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                 0x1840
+#       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                 0x1853
+#       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                 0x1854
+#       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)
+
+/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */
+#define GRPH_INT_STATUS                                 0x1A16
+#       define GRPH_PFLIP_INT_OCCURRED                  (1 << 0)
+#       define GRPH_PFLIP_INT_CLEAR                     (1 << 8)
+/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */
+#define        GRPH_INT_CONTROL                                0x1A17
+#       define GRPH_PFLIP_INT_MASK                      (1 << 0)
+#       define GRPH_PFLIP_INT_TYPE                      (1 << 8)
+
+#define        DAC_AUTODETECT_INT_CONTROL                      0x19F2
+
+#define DC_HPD1_INT_STATUS                              0x1807
+#define DC_HPD2_INT_STATUS                              0x180A
+#define DC_HPD3_INT_STATUS                              0x180D
+#define DC_HPD4_INT_STATUS                              0x1810
+#define DC_HPD5_INT_STATUS                              0x1813
+#define DC_HPD6_INT_STATUS                              0x1816
+#       define DC_HPDx_INT_STATUS                       (1 << 0)
+#       define DC_HPDx_SENSE                            (1 << 1)
+#       define DC_HPDx_RX_INT_STATUS                    (1 << 8)
+
+#define DC_HPD1_INT_CONTROL                             0x1808
+#define DC_HPD2_INT_CONTROL                             0x180B
+#define DC_HPD3_INT_CONTROL                             0x180E
+#define DC_HPD4_INT_CONTROL                             0x1811
+#define DC_HPD5_INT_CONTROL                             0x1814
+#define DC_HPD6_INT_CONTROL                             0x1817
+#       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                                   0x1809
+#define DC_HPD2_CONTROL                                   0x180C
+#define DC_HPD3_CONTROL                                   0x180F
+#define DC_HPD4_CONTROL                                   0x1812
+#define DC_HPD5_CONTROL                                   0x1815
+#define DC_HPD6_CONTROL                                   0x1818
+#       define DC_HPDx_CONNECTION_TIMER(x)                ((x) << 0)
+#       define DC_HPDx_RX_INT_TIMER(x)                    ((x) << 16)
+#       define DC_HPDx_EN                                 (1 << 28)
+
+#define DPG_PIPE_STUTTER_CONTROL                          0x1B35
+#       define STUTTER_ENABLE                             (1 << 0)
+
+/* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
+#define CRTC_STATUS_FRAME_COUNT                         0x1BA6
+
+/* Audio clocks */
+#define DCCG_AUDIO_DTO_SOURCE                           0x05ac
+#       define DCCG_AUDIO_DTO0_SOURCE_SEL(x) ((x) << 0) /* crtc0 - crtc5 */
+#       define DCCG_AUDIO_DTO_SEL            (1 << 4)   /* 0=dto0 1=dto1 */
+
+#define DCCG_AUDIO_DTO0_PHASE                           0x05b0
+#define DCCG_AUDIO_DTO0_MODULE                          0x05b4
+#define DCCG_AUDIO_DTO1_PHASE                           0x05c0
+#define DCCG_AUDIO_DTO1_MODULE                          0x05c4
+
+#define AFMT_AUDIO_SRC_CONTROL                          0x1c4f
+#define                AFMT_AUDIO_SRC_SELECT(x)                (((x) & 7) << 0)
+/* AFMT_AUDIO_SRC_SELECT
+ * 0 = stream0
+ * 1 = stream1
+ * 2 = stream2
+ * 3 = stream3
+ * 4 = stream4
+ * 5 = stream5
+ */
+
+#define        GRBM_CNTL                                       0x2000
+#define                GRBM_READ_TIMEOUT(x)                            ((x) << 0)
+
+#define        GRBM_STATUS2                                    0x2002
+#define                RLC_RQ_PENDING                                  (1 << 0)
+#define                RLC_BUSY                                        (1 << 8)
+#define                TC_BUSY                                         (1 << 9)
+
+#define        GRBM_STATUS                                     0x2004
+#define                CMDFIFO_AVAIL_MASK                              0x0000000F
+#define                RING2_RQ_PENDING                                (1 << 4)
+#define                SRBM_RQ_PENDING                                 (1 << 5)
+#define                RING1_RQ_PENDING                                (1 << 6)
+#define                CF_RQ_PENDING                                   (1 << 7)
+#define                PF_RQ_PENDING                                   (1 << 8)
+#define                GDS_DMA_RQ_PENDING                              (1 << 9)
+#define                GRBM_EE_BUSY                                    (1 << 10)
+#define                DB_CLEAN                                        (1 << 12)
+#define                CB_CLEAN                                        (1 << 13)
+#define                TA_BUSY                                         (1 << 14)
+#define                GDS_BUSY                                        (1 << 15)
+#define                VGT_BUSY                                        (1 << 17)
+#define                IA_BUSY_NO_DMA                                  (1 << 18)
+#define                IA_BUSY                                         (1 << 19)
+#define                SX_BUSY                                         (1 << 20)
+#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                                 0x2005
+#define        GRBM_STATUS_SE1                                 0x2006
+#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                                 0x2008
+#define                SOFT_RESET_CP                                   (1 << 0)
+#define                SOFT_RESET_CB                                   (1 << 1)
+#define                SOFT_RESET_RLC                                  (1 << 2)
+#define                SOFT_RESET_DB                                   (1 << 3)
+#define                SOFT_RESET_GDS                                  (1 << 4)
+#define                SOFT_RESET_PA                                   (1 << 5)
+#define                SOFT_RESET_SC                                   (1 << 6)
+#define                SOFT_RESET_BCI                                  (1 << 7)
+#define                SOFT_RESET_SPI                                  (1 << 8)
+#define                SOFT_RESET_SX                                   (1 << 10)
+#define                SOFT_RESET_TC                                   (1 << 11)
+#define                SOFT_RESET_TA                                   (1 << 12)
+#define                SOFT_RESET_VGT                                  (1 << 14)
+#define                SOFT_RESET_IA                                   (1 << 15)
+
+#define GRBM_GFX_INDEX                                 0x200B
+#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 GRBM_INT_CNTL                                   0x2018
+#       define RDERR_INT_ENABLE                         (1 << 0)
+#       define GUI_IDLE_INT_ENABLE                      (1 << 19)
+
+#define        CP_STRMOUT_CNTL                                 0x213F
+#define        SCRATCH_REG0                                    0x2140
+#define        SCRATCH_REG1                                    0x2141
+#define        SCRATCH_REG2                                    0x2142
+#define        SCRATCH_REG3                                    0x2143
+#define        SCRATCH_REG4                                    0x2144
+#define        SCRATCH_REG5                                    0x2145
+#define        SCRATCH_REG6                                    0x2146
+#define        SCRATCH_REG7                                    0x2147
+
+#define        SCRATCH_UMSK                                    0x2150
+#define        SCRATCH_ADDR                                    0x2151
+
+#define        CP_SEM_WAIT_TIMER                               0x216F
+
+#define        CP_SEM_INCOMPLETE_TIMER_CNTL                    0x2172
+
+#define CP_ME_CNTL                                     0x21B6
+#define                CP_CE_HALT                                      (1 << 24)
+#define                CP_PFP_HALT                                     (1 << 26)
+#define                CP_ME_HALT                                      (1 << 28)
+
+#define        CP_COHER_CNTL2                                  0x217A
+
+#define        CP_RB2_RPTR                                     0x21BE
+#define        CP_RB1_RPTR                                     0x21BF
+#define        CP_RB0_RPTR                                     0x21C0
+#define        CP_RB_WPTR_DELAY                                0x21C1
+
+#define        CP_QUEUE_THRESHOLDS                             0x21D8
+#define                ROQ_IB1_START(x)                                ((x) << 0)
+#define                ROQ_IB2_START(x)                                ((x) << 8)
+#define CP_MEQ_THRESHOLDS                              0x21D9
+#define                MEQ1_START(x)                           ((x) << 0)
+#define                MEQ2_START(x)                           ((x) << 8)
+
+#define        CP_PERFMON_CNTL                                 0x21FF
+
+#define        VGT_VTX_VECT_EJECT_REG                          0x222C
+
+#define        VGT_CACHE_INVALIDATION                          0x2231
+#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_ESGS_RING_SIZE                              0x2232
+#define        VGT_GSVS_RING_SIZE                              0x2233
+
+#define        VGT_GS_VERTEX_REUSE                             0x2235
+
+#define        VGT_PRIMITIVE_TYPE                              0x2256
+#define        VGT_INDEX_TYPE                                  0x2257
+
+#define        VGT_NUM_INDICES                                 0x225C
+#define        VGT_NUM_INSTANCES                               0x225D
+
+#define        VGT_TF_RING_SIZE                                0x2262
+
+#define        VGT_HS_OFFCHIP_PARAM                            0x226C
+
+#define        VGT_TF_MEMORY_BASE                              0x226E
+
+#define CC_GC_SHADER_ARRAY_CONFIG                      0x226F
+#define                INACTIVE_CUS_MASK                       0xFFFF0000
+#define                INACTIVE_CUS_SHIFT                      16
+#define GC_USER_SHADER_ARRAY_CONFIG                    0x2270
+
+#define        PA_CL_ENHANCE                                   0x2285
+#define                CLIP_VTX_REORDER_ENA                            (1 << 0)
+#define                NUM_CLIP_SEQ(x)                                 ((x) << 1)
+
+#define        PA_SU_LINE_STIPPLE_VALUE                        0x2298
+
+#define        PA_SC_LINE_STIPPLE_STATE                        0x22C4
+
+#define        PA_SC_FORCE_EOV_MAX_CNTS                        0x22C9
+#define                FORCE_EOV_MAX_CLK_CNT(x)                        ((x) << 0)
+#define                FORCE_EOV_MAX_REZ_CNT(x)                        ((x) << 16)
+
+#define        PA_SC_FIFO_SIZE                                 0x22F3
+#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                                   0x22FC
+
+#define        SQ_CONFIG                                       0x2300
+
+#define        SQC_CACHES                                      0x2302
+
+#define SQ_POWER_THROTTLE                               0x2396
+#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                              0x2397
+#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
+
+#define        SX_DEBUG_1                                      0x2418
+
+#define        SPI_STATIC_THREAD_MGMT_1                        0x2438
+#define        SPI_STATIC_THREAD_MGMT_2                        0x2439
+#define        SPI_STATIC_THREAD_MGMT_3                        0x243A
+#define        SPI_PS_MAX_WAVE_ID                              0x243B
+
+#define        SPI_CONFIG_CNTL                                 0x2440
+
+#define        SPI_CONFIG_CNTL_1                               0x244F
+#define                VTX_DONE_DELAY(x)                               ((x) << 0)
+#define                INTERP_ONE_PRIM_PER_ROW                         (1 << 4)
+
+#define        CGTS_TCC_DISABLE                                0x2452
+#define        CGTS_USER_TCC_DISABLE                           0x2453
+#define                TCC_DISABLE_MASK                                0xFFFF0000
+#define                TCC_DISABLE_SHIFT                               16
+#define        CGTS_SM_CTRL_REG                                0x2454
+#define                OVERRIDE                                (1 << 21)
+#define                LS_OVERRIDE                             (1 << 22)
+
+#define        SPI_LB_CU_MASK                                  0x24D5
+
+#define        TA_CNTL_AUX                                     0x2542
+
+#define CC_RB_BACKEND_DISABLE                          0x263D
+#define                BACKEND_DISABLE(x)                      ((x) << 16)
+#define GB_ADDR_CONFIG                                 0x263E
+#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                NUM_GPUS(x)                             ((x) << 20)
+#define                NUM_GPUS_MASK                           0x00700000
+#define                NUM_GPUS_SHIFT                          20
+#define                MULTI_GPU_TILE_SIZE(x)                  ((x) << 24)
+#define                MULTI_GPU_TILE_SIZE_MASK                0x03000000
+#define                MULTI_GPU_TILE_SIZE_SHIFT               24
+#define                ROW_SIZE(x)                             ((x) << 28)
+#define                ROW_SIZE_MASK                           0x30000000
+#define                ROW_SIZE_SHIFT                          28
+
+#define        GB_TILE_MODE0                                   0x2644
+#       define MICRO_TILE_MODE(x)                              ((x) << 0)
+#              define  ADDR_SURF_DISPLAY_MICRO_TILING          0
+#              define  ADDR_SURF_THIN_MICRO_TILING             1
+#              define  ADDR_SURF_DEPTH_MICRO_TILING            2
+#       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 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 BANK_WIDTH(x)                                   ((x) << 14)
+#              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) << 16)
+#              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) << 18)
+#              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) << 20)
+#              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        GB_TILE_MODE1                                   0x2645
+#define        GB_TILE_MODE2                                   0x2646
+#define        GB_TILE_MODE3                                   0x2647
+#define        GB_TILE_MODE4                                   0x2648
+#define        GB_TILE_MODE5                                   0x2649
+#define        GB_TILE_MODE6                                   0x264a
+#define        GB_TILE_MODE7                                   0x264b
+#define        GB_TILE_MODE8                                   0x264c
+#define        GB_TILE_MODE9                                   0x264d
+#define        GB_TILE_MODE10                                  0x264e
+#define        GB_TILE_MODE11                                  0x264f
+#define        GB_TILE_MODE12                                  0x2650
+#define        GB_TILE_MODE13                                  0x2651
+#define        GB_TILE_MODE14                                  0x2652
+#define        GB_TILE_MODE15                                  0x2653
+#define        GB_TILE_MODE16                                  0x2654
+#define        GB_TILE_MODE17                                  0x2655
+#define        GB_TILE_MODE18                                  0x2656
+#define        GB_TILE_MODE19                                  0x2657
+#define        GB_TILE_MODE20                                  0x2658
+#define        GB_TILE_MODE21                                  0x2659
+#define        GB_TILE_MODE22                                  0x265a
+#define        GB_TILE_MODE23                                  0x265b
+#define        GB_TILE_MODE24                                  0x265c
+#define        GB_TILE_MODE25                                  0x265d
+#define        GB_TILE_MODE26                                  0x265e
+#define        GB_TILE_MODE27                                  0x265f
+#define        GB_TILE_MODE28                                  0x2660
+#define        GB_TILE_MODE29                                  0x2661
+#define        GB_TILE_MODE30                                  0x2662
+#define        GB_TILE_MODE31                                  0x2663
+
+#define        CB_PERFCOUNTER0_SELECT0                         0x2688
+#define        CB_PERFCOUNTER0_SELECT1                         0x2689
+#define        CB_PERFCOUNTER1_SELECT0                         0x268A
+#define        CB_PERFCOUNTER1_SELECT1                         0x268B
+#define        CB_PERFCOUNTER2_SELECT0                         0x268C
+#define        CB_PERFCOUNTER2_SELECT1                         0x268D
+#define        CB_PERFCOUNTER3_SELECT0                         0x268E
+#define        CB_PERFCOUNTER3_SELECT1                         0x268F
+
+#define        CB_CGTT_SCLK_CTRL                               0x2698
+
+#define        GC_USER_RB_BACKEND_DISABLE                      0x26DF
+#define                BACKEND_DISABLE_MASK                    0x00FF0000
+#define                BACKEND_DISABLE_SHIFT                   16
+
+#define        TCP_CHAN_STEER_LO                               0x2B03
+#define        TCP_CHAN_STEER_HI                               0x2B94
+
+#define        CP_RB0_BASE                                     0x3040
+#define        CP_RB0_CNTL                                     0x3041
+#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                                0x3043
+#define        CP_RB0_RPTR_ADDR_HI                             0x3044
+#define        CP_RB0_WPTR                                     0x3045
+
+#define        CP_PFP_UCODE_ADDR                               0x3054
+#define        CP_PFP_UCODE_DATA                               0x3055
+#define        CP_ME_RAM_RADDR                                 0x3056
+#define        CP_ME_RAM_WADDR                                 0x3057
+#define        CP_ME_RAM_DATA                                  0x3058
+
+#define        CP_CE_UCODE_ADDR                                0x305A
+#define        CP_CE_UCODE_DATA                                0x305B
+
+#define        CP_RB1_BASE                                     0x3060
+#define        CP_RB1_CNTL                                     0x3061
+#define        CP_RB1_RPTR_ADDR                                0x3062
+#define        CP_RB1_RPTR_ADDR_HI                             0x3063
+#define        CP_RB1_WPTR                                     0x3064
+#define        CP_RB2_BASE                                     0x3065
+#define        CP_RB2_CNTL                                     0x3066
+#define        CP_RB2_RPTR_ADDR                                0x3067
+#define        CP_RB2_RPTR_ADDR_HI                             0x3068
+#define        CP_RB2_WPTR                                     0x3069
+#define CP_INT_CNTL_RING0                               0x306A
+#define CP_INT_CNTL_RING1                               0x306B
+#define CP_INT_CNTL_RING2                               0x306C
+#       define CNTX_BUSY_INT_ENABLE                     (1 << 19)
+#       define CNTX_EMPTY_INT_ENABLE                    (1 << 20)
+#       define WAIT_MEM_SEM_INT_ENABLE                  (1 << 21)
+#       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_INT_STATUS_RING0                             0x306D
+#define CP_INT_STATUS_RING1                             0x306E
+#define CP_INT_STATUS_RING2                             0x306F
+#       define WAIT_MEM_SEM_INT_STAT                    (1 << 21)
+#       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_MEM_SLP_CNTL                                 0x3079
+#       define CP_MEM_LS_EN                             (1 << 0)
+
+#define        CP_DEBUG                                        0x307F
+
+#define RLC_CNTL                                          0x30C0
+#       define RLC_ENABLE                                 (1 << 0)
+#define RLC_RL_BASE                                       0x30C1
+#define RLC_RL_SIZE                                       0x30C2
+#define RLC_LB_CNTL                                       0x30C3
+#       define LOAD_BALANCE_ENABLE                        (1 << 0)
+#define RLC_SAVE_AND_RESTORE_BASE                         0x30C4
+#define RLC_LB_CNTR_MAX                                   0x30C5
+#define RLC_LB_CNTR_INIT                                  0x30C6
+
+#define RLC_CLEAR_STATE_RESTORE_BASE                      0x30C8
+
+#define RLC_UCODE_ADDR                                    0x30CB
+#define RLC_UCODE_DATA                                    0x30CC
+
+#define RLC_GPU_CLOCK_COUNT_LSB                           0x30CE
+#define RLC_GPU_CLOCK_COUNT_MSB                           0x30CF
+#define RLC_CAPTURE_GPU_CLOCK_COUNT                       0x30D0
+#define RLC_MC_CNTL                                       0x30D1
+#define RLC_UCODE_CNTL                                    0x30D2
+#define RLC_STAT                                          0x30D3
+#       define RLC_BUSY_STATUS                            (1 << 0)
+#       define GFX_POWER_STATUS                           (1 << 1)
+#       define GFX_CLOCK_STATUS                           (1 << 2)
+#       define GFX_LS_STATUS                              (1 << 3)
+
+#define        RLC_PG_CNTL                                     0x30D7
+#      define GFX_PG_ENABLE                            (1 << 0)
+#      define GFX_PG_SRC                               (1 << 1)
+
+#define        RLC_CGTT_MGCG_OVERRIDE                          0x3100
+#define        RLC_CGCG_CGLS_CTRL                              0x3101
+#      define CGCG_EN                                  (1 << 0)
+#      define CGLS_EN                                  (1 << 1)
+
+#define        RLC_TTOP_D                                      0x3105
+#      define RLC_PUD(x)                               ((x) << 0)
+#      define RLC_PUD_MASK                             (0xff << 0)
+#      define RLC_PDD(x)                               ((x) << 8)
+#      define RLC_PDD_MASK                             (0xff << 8)
+#      define RLC_TTPD(x)                              ((x) << 16)
+#      define RLC_TTPD_MASK                            (0xff << 16)
+#      define RLC_MSD(x)                               ((x) << 24)
+#      define RLC_MSD_MASK                             (0xff << 24)
+
+#define RLC_LB_INIT_CU_MASK                               0x3107
+
+#define        RLC_PG_AO_CU_MASK                               0x310B
+#define        RLC_MAX_PG_CU                                   0x310C
+#      define MAX_PU_CU(x)                             ((x) << 0)
+#      define MAX_PU_CU_MASK                           (0xff << 0)
+#define        RLC_AUTO_PG_CTRL                                0x310C
+#      define AUTO_PG_EN                               (1 << 0)
+#      define GRBM_REG_SGIT(x)                         ((x) << 3)
+#      define GRBM_REG_SGIT_MASK                       (0xffff << 3)
+#      define PG_AFTER_GRBM_REG_ST(x)                  ((x) << 19)
+#      define PG_AFTER_GRBM_REG_ST_MASK                (0x1fff << 19)
+
+#define RLC_SERDES_WR_MASTER_MASK_0                       0x3115
+#define RLC_SERDES_WR_MASTER_MASK_1                       0x3116
+#define RLC_SERDES_WR_CTRL                                0x3117
+
+#define RLC_SERDES_MASTER_BUSY_0                          0x3119
+#define RLC_SERDES_MASTER_BUSY_1                          0x311A
+
+#define RLC_GCPM_GENERAL_3                                0x311E
+
+#define        DB_RENDER_CONTROL                               0xA000
+
+#define DB_DEPTH_INFO                                   0xA00F
+
+#define PA_SC_RASTER_CONFIG                             0xA0D4
+#      define RB_MAP_PKR0(x)                           ((x) << 0)
+#      define RB_MAP_PKR0_MASK                         (0x3 << 0)
+#      define RB_MAP_PKR1(x)                           ((x) << 2)
+#      define RB_MAP_PKR1_MASK                         (0x3 << 2)
+#       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 RB_XSEL2(x)                              ((x) << 4)
+#      define RB_XSEL2_MASK                            (0x3 << 4)
+#      define RB_XSEL                                  (1 << 6)
+#      define RB_YSEL                                  (1 << 7)
+#      define PKR_MAP(x)                               ((x) << 8)
+#      define PKR_MAP_MASK                             (0x3 << 8)
+#       define RASTER_CONFIG_PKR_MAP_0                 0
+#       define RASTER_CONFIG_PKR_MAP_1                 1
+#       define RASTER_CONFIG_PKR_MAP_2                 2
+#       define RASTER_CONFIG_PKR_MAP_3                 3
+#      define PKR_XSEL(x)                              ((x) << 10)
+#      define PKR_XSEL_MASK                            (0x3 << 10)
+#      define PKR_YSEL(x)                              ((x) << 12)
+#      define PKR_YSEL_MASK                            (0x3 << 12)
+#      define SC_MAP(x)                                ((x) << 16)
+#      define SC_MAP_MASK                              (0x3 << 16)
+#      define SC_XSEL(x)                               ((x) << 18)
+#      define SC_XSEL_MASK                             (0x3 << 18)
+#      define SC_YSEL(x)                               ((x) << 20)
+#      define SC_YSEL_MASK                             (0x3 << 20)
+#      define SE_MAP(x)                                ((x) << 24)
+#      define SE_MAP_MASK                              (0x3 << 24)
+#       define RASTER_CONFIG_SE_MAP_0                  0
+#       define RASTER_CONFIG_SE_MAP_1                  1
+#       define RASTER_CONFIG_SE_MAP_2                  2
+#       define RASTER_CONFIG_SE_MAP_3                  3
+#      define SE_XSEL(x)                               ((x) << 26)
+#      define SE_XSEL_MASK                             (0x3 << 26)
+#      define SE_YSEL(x)                               ((x) << 28)
+#      define SE_YSEL_MASK                             (0x3 << 28)
+
+
+#define VGT_EVENT_INITIATOR                             0xA2A4
+#       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 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)
+
+/* PIF PHY0 registers idx/data 0x8/0xc */
+#define PB0_PIF_CNTL                                      0x10
+#       define LS2_EXIT_TIME(x)                           ((x) << 17)
+#       define LS2_EXIT_TIME_MASK                         (0x7 << 17)
+#       define LS2_EXIT_TIME_SHIFT                        17
+#define PB0_PIF_PAIRING                                   0x11
+#       define MULTI_PIF                                  (1 << 25)
+#define PB0_PIF_PWRDOWN_0                                 0x12
+#       define PLL_POWER_STATE_IN_TXS2_0(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_0_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_0(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_0_SHIFT             10
+#       define PLL_RAMP_UP_TIME_0(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_0_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_0_SHIFT                   24
+#define PB0_PIF_PWRDOWN_1                                 0x13
+#       define PLL_POWER_STATE_IN_TXS2_1(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_1_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_1(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_1_SHIFT             10
+#       define PLL_RAMP_UP_TIME_1(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_1_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_1_SHIFT                   24
+
+#define PB0_PIF_PWRDOWN_2                                 0x17
+#       define PLL_POWER_STATE_IN_TXS2_2(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_2_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_2_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_2(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_2_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_2_SHIFT             10
+#       define PLL_RAMP_UP_TIME_2(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_2_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_2_SHIFT                   24
+#define PB0_PIF_PWRDOWN_3                                 0x18
+#       define PLL_POWER_STATE_IN_TXS2_3(x)               ((x) << 7)
+#       define PLL_POWER_STATE_IN_TXS2_3_MASK             (0x7 << 7)
+#       define PLL_POWER_STATE_IN_TXS2_3_SHIFT            7
+#       define PLL_POWER_STATE_IN_OFF_3(x)                ((x) << 10)
+#       define PLL_POWER_STATE_IN_OFF_3_MASK              (0x7 << 10)
+#       define PLL_POWER_STATE_IN_OFF_3_SHIFT             10
+#       define PLL_RAMP_UP_TIME_3(x)                      ((x) << 24)
+#       define PLL_RAMP_UP_TIME_3_MASK                    (0x7 << 24)
+#       define PLL_RAMP_UP_TIME_3_SHIFT                   24
+/* PIF PHY1 registers idx/data 0x10/0x14 */
+#define PB1_PIF_CNTL                                      0x10
+#define PB1_PIF_PAIRING                                   0x11
+#define PB1_PIF_PWRDOWN_0                                 0x12
+#define PB1_PIF_PWRDOWN_1                                 0x13
+
+#define PB1_PIF_PWRDOWN_2                                 0x17
+#define PB1_PIF_PWRDOWN_3                                 0x18
+/* PCIE registers idx/data 0x30/0x34 */
+#define PCIE_CNTL2                                        0x1c /* PCIE */
+#       define SLV_MEM_LS_EN                              (1 << 16)
+#       define SLV_MEM_AGGRESSIVE_LS_EN                   (1 << 17)
+#       define MST_MEM_LS_EN                              (1 << 18)
+#       define REPLAY_MEM_LS_EN                           (1 << 19)
+#define PCIE_LC_STATUS1                                   0x28 /* PCIE */
+#       define LC_REVERSE_RCVR                            (1 << 0)
+#       define LC_REVERSE_XMIT                            (1 << 1)
+#       define LC_OPERATING_LINK_WIDTH_MASK               (0x7 << 2)
+#       define LC_OPERATING_LINK_WIDTH_SHIFT              2
+#       define LC_DETECTED_LINK_WIDTH_MASK                (0x7 << 5)
+#       define LC_DETECTED_LINK_WIDTH_SHIFT               5
+
+#define PCIE_P_CNTL                                       0x40 /* PCIE */
+#       define P_IGNORE_EDB_ERR                           (1 << 6)
+
+/* PCIE PORT registers idx/data 0x38/0x3c */
+#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_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 LC_DYN_LANES_PWR_STATE(x)                  ((x) << 21)
+#       define LC_DYN_LANES_PWR_STATE_MASK                (0x3 << 21)
+#       define LC_DYN_LANES_PWR_STATE_SHIFT               21
+#define PCIE_LC_N_FTS_CNTL                                0xa3 /* PCIE_P */
+#       define LC_XMIT_N_FTS(x)                           ((x) << 0)
+#       define LC_XMIT_N_FTS_MASK                         (0xff << 0)
+#       define LC_XMIT_N_FTS_SHIFT                        0
+#       define LC_XMIT_N_FTS_OVERRIDE_EN                  (1 << 8)
+#       define LC_N_FTS_MASK                              (0xff << 24)
+#define PCIE_LC_SPEED_CNTL                                0xa4 /* PCIE_P */
+#       define LC_GEN2_EN_STRAP                           (1 << 0)
+#       define LC_GEN3_EN_STRAP                           (1 << 1)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_EN           (1 << 2)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_MASK         (0x3 << 3)
+#       define LC_TARGET_LINK_SPEED_OVERRIDE_SHIFT        3
+#       define LC_FORCE_EN_SW_SPEED_CHANGE                (1 << 5)
+#       define LC_FORCE_DIS_SW_SPEED_CHANGE               (1 << 6)
+#       define LC_FORCE_EN_HW_SPEED_CHANGE                (1 << 7)
+#       define LC_FORCE_DIS_HW_SPEED_CHANGE               (1 << 8)
+#       define LC_INITIATE_LINK_SPEED_CHANGE              (1 << 9)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK      (0x3 << 10)
+#       define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT     10
+#       define LC_CURRENT_DATA_RATE_MASK                  (0x3 << 13) /* 0/1/2 = gen1/2/3 */
+#       define LC_CURRENT_DATA_RATE_SHIFT                 13
+#       define LC_CLR_FAILED_SPD_CHANGE_CNT               (1 << 16)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN2               (1 << 18)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN2                (1 << 19)
+#       define LC_OTHER_SIDE_EVER_SENT_GEN3               (1 << 20)
+#       define LC_OTHER_SIDE_SUPPORTS_GEN3                (1 << 21)
+
+#define PCIE_LC_CNTL2                                     0xb1
+#       define LC_ALLOW_PDWN_IN_L1                        (1 << 17)
+#       define LC_ALLOW_PDWN_IN_L23                       (1 << 18)
+
+#define PCIE_LC_CNTL3                                     0xb5 /* PCIE_P */
+#       define LC_GO_TO_RECOVERY                          (1 << 30)
+#define PCIE_LC_CNTL4                                     0xb6 /* PCIE_P */
+#       define LC_REDO_EQ                                 (1 << 5)
+#       define LC_SET_QUIESCE                             (1 << 13)
+
+/*
+ * UVD
+ */
+#define UVD_UDEC_ADDR_CONFIG                           0x3bd3
+#define UVD_UDEC_DB_ADDR_CONFIG                                0x3bd4
+#define UVD_UDEC_DBW_ADDR_CONFIG                       0x3bd5
+#define UVD_RBC_RB_RPTR                                        0x3da4
+#define UVD_RBC_RB_WPTR                                        0x3da5
+#define UVD_STATUS                                     0x3daf
+
+#define        UVD_CGC_CTRL                                    0x3dc2
+#      define DCM                                      (1 << 0)
+#      define CG_DT(x)                                 ((x) << 2)
+#      define CG_DT_MASK                               (0xf << 2)
+#      define CLK_OD(x)                                ((x) << 6)
+#      define CLK_OD_MASK                              (0x1f << 6)
+
+ /* UVD CTX indirect */
+#define        UVD_CGC_MEM_CTRL                                0xC0
+#define        UVD_CGC_CTRL2                                   0xC1
+#      define DYN_OR_EN                                (1 << 0)
+#      define DYN_RR_EN                                (1 << 1)
+#      define G_DIV_ID(x)                              ((x) << 2)
+#      define G_DIV_ID_MASK                            (0x7 << 2)
+
+/*
+ * PM4
+ */
+#define PACKET0(reg, n)        ((RADEON_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 RADEON_PACKET_TYPE3 3
+#define PACKET3(op, n) ((RADEON_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                        GDS_PARTITION_BASE              2
+#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_ALLOC_GDS                               0x1B
+#define        PACKET3_WRITE_GDS_RAM                           0x1C
+#define        PACKET3_ATOMIC_GDS                              0x1D
+#define        PACKET3_ATOMIC                                  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_DRAW_INDEX_IMMD                         0x2E
+#define        PACKET3_NUM_INSTANCES                           0x2F
+#define        PACKET3_DRAW_INDEX_MULTI_AUTO                   0x30
+#define        PACKET3_INDIRECT_BUFFER_CONST                   0x31
+#define        PACKET3_INDIRECT_BUFFER                         0x3F
+#define        PACKET3_STRMOUT_BUFFER_UPDATE                   0x34
+#define        PACKET3_DRAW_INDEX_OFFSET_2                     0x35
+#define        PACKET3_DRAW_INDEX_MULTI_ELEMENT                0x36
+#define        PACKET3_WRITE_DATA                              0x37
+#define                WRITE_DATA_DST_SEL(x)                   ((x) << 8)
+                /* 0 - register
+                * 1 - memory (sync - via GRBM)
+                * 2 - tc/l2
+                * 3 - gds
+                * 4 - reserved
+                * 5 - memory (async - direct)
+                */
+#define                WR_ONE_ADDR                             (1 << 16)
+#define                WR_CONFIRM                              (1 << 20)
+#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_MPEG_INDEX                              0x3A
+#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_ENGINE(x)                  ((x) << 8)
+                /* 0 - me
+                * 1 - pfp
+                */
+#define        PACKET3_MEM_WRITE                               0x3D
+#define        PACKET3_COPY_DATA                               0x40
+#define        PACKET3_CP_DMA                                  0x41
+/* 1. header
+ * 2. SRC_ADDR_LO or DATA [31:0]
+ * 3. CP_SYNC [31] | SRC_SEL [30:29] | ENGINE [27] | DST_SEL [21:20] |
+ *    SRC_ADDR_HI [7:0]
+ * 4. DST_ADDR_LO [31:0]
+ * 5. DST_ADDR_HI [7:0]
+ * 6. COMMAND [30:21] | BYTE_COUNT [20:0]
+ */
+#              define PACKET3_CP_DMA_DST_SEL(x)    ((x) << 20)
+                /* 0 - DST_ADDR
+                * 1 - GDS
+                */
+#              define PACKET3_CP_DMA_ENGINE(x)     ((x) << 27)
+                /* 0 - ME
+                * 1 - PFP
+                */
+#              define PACKET3_CP_DMA_SRC_SEL(x)    ((x) << 29)
+                /* 0 - SRC_ADDR
+                * 1 - GDS
+                * 2 - DATA
+                */
+#              define PACKET3_CP_DMA_CP_SYNC       (1 << 31)
+/* COMMAND */
+#              define PACKET3_CP_DMA_DIS_WC        (1 << 21)
+#              define PACKET3_CP_DMA_CMD_SRC_SWAP(x) ((x) << 22)
+                /* 0 - none
+                * 1 - 8 in 16
+                * 2 - 8 in 32
+                * 3 - 8 in 64
+                */
+#              define PACKET3_CP_DMA_CMD_DST_SWAP(x) ((x) << 24)
+                /* 0 - none
+                * 1 - 8 in 16
+                * 2 - 8 in 32
+                * 3 - 8 in 64
+                */
+#              define PACKET3_CP_DMA_CMD_SAS       (1 << 26)
+                /* 0 - memory
+                * 1 - register
+                */
+#              define PACKET3_CP_DMA_CMD_DAS       (1 << 27)
+                /* 0 - memory
+                * 1 - register
+                */
+#              define PACKET3_CP_DMA_CMD_SAIC      (1 << 28)
+#              define PACKET3_CP_DMA_CMD_DAIC      (1 << 29)
+#              define PACKET3_CP_DMA_CMD_RAW_WAIT  (1 << 30)
+#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_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)
+#              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_ICACHE_ACTION_ENA (1 << 29)
+#define        PACKET3_ME_INITIALIZE                           0x44
+#define                PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16)
+#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
+                * 2 - SAMPLE_PIPELINESTAT
+                * 3 - SAMPLE_STREAMOUTSTAT*
+                * 4 - *S_PARTIAL_FLUSH
+                * 5 - EOP events
+                * 6 - EOS events
+                * 7 - CACHE_FLUSH, CACHE_FLUSH_AND_INV_EVENT
+                */
+#define                INV_L2                                  (1 << 20)
+                /* INV TC L2 cache when EVENT_INDEX = 7 */
+#define        PACKET3_EVENT_WRITE_EOP                         0x47
+#define                DATA_SEL(x)                             ((x) << 29)
+                /* 0 - discard
+                * 1 - send low 32bit data
+                * 2 - send 64bit data
+                * 3 - send 64bit counter value
+                */
+#define                INT_SEL(x)                              ((x) << 24)
+                /* 0 - none
+                * 1 - interrupt only (DATA_SEL = 0)
+                * 2 - interrupt when data write is confirmed
+                */
+#define        PACKET3_EVENT_WRITE_EOS                         0x48
+#define        PACKET3_PREAMBLE_CNTL                           0x4A
+#              define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE     (2 << 28)
+#              define PACKET3_PREAMBLE_END_CLEAR_STATE       (3 << 28)
+#define        PACKET3_ONE_REG_WRITE                           0x57
+#define        PACKET3_LOAD_CONFIG_REG                         0x5F
+#define        PACKET3_LOAD_CONTEXT_REG                        0x60
+#define        PACKET3_LOAD_SH_REG                             0x61
+#define        PACKET3_SET_CONFIG_REG                          0x68
+#define                PACKET3_SET_CONFIG_REG_START                    0x00002000
+#define                PACKET3_SET_CONFIG_REG_END                      0x00002c00
+#define        PACKET3_SET_CONTEXT_REG                         0x69
+#define                PACKET3_SET_CONTEXT_REG_START                   0x000a000
+#define                PACKET3_SET_CONTEXT_REG_END                     0x000a400
+#define        PACKET3_SET_CONTEXT_REG_INDIRECT                0x73
+#define        PACKET3_SET_RESOURCE_INDIRECT                   0x74
+#define        PACKET3_SET_SH_REG                              0x76
+#define                PACKET3_SET_SH_REG_START                        0x00002c00
+#define                PACKET3_SET_SH_REG_END                          0x00003000
+#define        PACKET3_SET_SH_REG_OFFSET                       0x77
+#define        PACKET3_ME_WRITE                                0x7A
+#define        PACKET3_SCRATCH_RAM_WRITE                       0x7D
+#define        PACKET3_SCRATCH_RAM_READ                        0x7E
+#define        PACKET3_CE_WRITE                                0x7F
+#define        PACKET3_LOAD_CONST_RAM                          0x80
+#define        PACKET3_WRITE_CONST_RAM                         0x81
+#define        PACKET3_WRITE_CONST_RAM_OFFSET                  0x82
+#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                      0x87
+#define        PACKET3_WAIT_ON_DE_COUNTER_DIFF                 0x88
+#define        PACKET3_SET_CE_DE_COUNTERS                      0x89
+#define        PACKET3_WAIT_ON_AVAIL_BUFFER                    0x8A
+#define        PACKET3_SWITCH_BUFFER                           0x8B
+
+/* ASYNC DMA - first instance at 0xd000, second at 0xd800 */
+#define DMA0_REGISTER_OFFSET                              0x0 /* not a register */
+#define DMA1_REGISTER_OFFSET                              0x200 /* not a register */
+
+#define DMA_RB_CNTL                                       0x3400
+#       define DMA_RB_ENABLE                              (1 << 0)
+#       define DMA_RB_SIZE(x)                             ((x) << 1) /* log2 */
+#       define DMA_RB_SWAP_ENABLE                         (1 << 9) /* 8IN32 */
+#       define DMA_RPTR_WRITEBACK_ENABLE                  (1 << 12)
+#       define DMA_RPTR_WRITEBACK_SWAP_ENABLE             (1 << 13)  /* 8IN32 */
+#       define DMA_RPTR_WRITEBACK_TIMER(x)                ((x) << 16) /* log2 */
+#define DMA_RB_BASE                                       0x3401
+#define DMA_RB_RPTR                                       0x3402
+#define DMA_RB_WPTR                                       0x3403
+
+#define DMA_RB_RPTR_ADDR_HI                               0x3407
+#define DMA_RB_RPTR_ADDR_LO                               0x3408
+
+#define DMA_IB_CNTL                                       0x3409
+#       define DMA_IB_ENABLE                              (1 << 0)
+#       define DMA_IB_SWAP_ENABLE                         (1 << 4)
+#       define CMD_VMID_FORCE                             (1 << 31)
+#define DMA_IB_RPTR                                       0x340a
+#define DMA_CNTL                                          0x340b
+#       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 CTXEMPTY_INT_ENABLE                        (1 << 28)
+#define DMA_STATUS_REG                                    0x340d
+#       define DMA_IDLE                                   (1 << 0)
+#define DMA_TILING_CONFIG                                0x342e
+
+#define        DMA_POWER_CNTL                                  0x342f
+#       define MEM_POWER_OVERRIDE                       (1 << 8)
+#define        DMA_CLK_CTRL                                    0x3430
+
+#define        DMA_PG                                          0x3435
+#      define PG_CNTL_ENABLE                           (1 << 0)
+#define        DMA_PGFSM_CONFIG                                0x3436
+#define        DMA_PGFSM_WRITE                                 0x3437
+
+#define DMA_PACKET(cmd, b, t, s, n)    ((((cmd) & 0xF) << 28) |        \
+                                        (((b) & 0x1) << 26) |          \
+                                        (((t) & 0x1) << 23) |          \
+                                        (((s) & 0x1) << 22) |          \
+                                        (((n) & 0xFFFFF) << 0))
+
+#define DMA_IB_PACKET(cmd, vmid, n)    ((((cmd) & 0xF) << 28) |        \
+                                        (((vmid) & 0xF) << 20) |       \
+                                        (((n) & 0xFFFFF) << 0))
+
+#define DMA_PTE_PDE_PACKET(n)          ((2 << 28) |                    \
+                                        (1 << 26) |                    \
+                                        (1 << 21) |                    \
+                                        (((n) & 0xFFFFF) << 0))
+
+/* async DMA Packet types */
+#define        DMA_PACKET_WRITE                                  0x2
+#define        DMA_PACKET_COPY                                   0x3
+#define        DMA_PACKET_INDIRECT_BUFFER                        0x4
+#define        DMA_PACKET_SEMAPHORE                              0x5
+#define        DMA_PACKET_FENCE                                  0x6
+#define        DMA_PACKET_TRAP                                   0x7
+#define        DMA_PACKET_SRBM_WRITE                             0x9
+#define        DMA_PACKET_CONSTANT_FILL                          0xd
+#define        DMA_PACKET_POLL_REG_MEM                           0xe
+#define        DMA_PACKET_NOP                                    0xf
+
+#define VCE_STATUS                                     0x20004
+#define VCE_VCPU_CNTL                                  0x20014
+#define                VCE_CLK_EN                              (1 << 0)
+#define VCE_VCPU_CACHE_OFFSET0                         0x20024
+#define VCE_VCPU_CACHE_SIZE0                           0x20028
+#define VCE_VCPU_CACHE_OFFSET1                         0x2002c
+#define VCE_VCPU_CACHE_SIZE1                           0x20030
+#define VCE_VCPU_CACHE_OFFSET2                         0x20034
+#define VCE_VCPU_CACHE_SIZE2                           0x20038
+#define VCE_SOFT_RESET                                 0x20120
+#define        VCE_ECPU_SOFT_RESET                     (1 << 0)
+#define        VCE_FME_SOFT_RESET                      (1 << 2)
+#define VCE_RB_BASE_LO2                                        0x2016c
+#define VCE_RB_BASE_HI2                                        0x20170
+#define VCE_RB_SIZE2                                   0x20174
+#define VCE_RB_RPTR2                                   0x20178
+#define VCE_RB_WPTR2                                   0x2017c
+#define VCE_RB_BASE_LO                                 0x20180
+#define VCE_RB_BASE_HI                                 0x20184
+#define VCE_RB_SIZE                                    0x20188
+#define VCE_RB_RPTR                                    0x2018c
+#define VCE_RB_WPTR                                    0x20190
+#define VCE_CLOCK_GATING_A                             0x202f8
+#define VCE_CLOCK_GATING_B                             0x202fc
+#define VCE_UENC_CLOCK_GATING                          0x205bc
+#define VCE_UENC_REG_CLOCK_GATING                      0x205c0
+#define VCE_FW_REG_STATUS                              0x20e10
+#      define VCE_FW_REG_STATUS_BUSY                   (1 << 0)
+#      define VCE_FW_REG_STATUS_PASS                   (1 << 3)
+#      define VCE_FW_REG_STATUS_DONE                   (1 << 11)
+#define VCE_LMI_FW_START_KEYSEL                                0x20e18
+#define VCE_LMI_FW_PERIODIC_CTRL                       0x20e20
+#define VCE_LMI_CTRL2                                  0x20e74
+#define VCE_LMI_CTRL                                   0x20e98
+#define VCE_LMI_VM_CTRL                                        0x20ea0
+#define VCE_LMI_SWAP_CNTL                              0x20eb4
+#define VCE_LMI_SWAP_CNTL1                             0x20eb8
+#define VCE_LMI_CACHE_CTRL                             0x20ef4
+
+#define VCE_CMD_NO_OP                                  0x00000000
+#define VCE_CMD_END                                    0x00000001
+#define VCE_CMD_IB                                     0x00000002
+#define VCE_CMD_FENCE                                  0x00000003
+#define VCE_CMD_TRAP                                   0x00000004
+#define VCE_CMD_IB_AUTO                                        0x00000005
+#define VCE_CMD_SEMAPHORE                              0x00000006
+
+
+//#dce stupp
+/* display controller offsets used for crtc/cur/lut/grph/viewport/etc. */
+#define SI_CRTC0_REGISTER_OFFSET                0 //(0x6df0 - 0x6df0)/4
+#define SI_CRTC1_REGISTER_OFFSET                0x300 //(0x79f0 - 0x6df0)/4
+#define SI_CRTC2_REGISTER_OFFSET                0x2600 //(0x105f0 - 0x6df0)/4
+#define SI_CRTC3_REGISTER_OFFSET                0x2900 //(0x111f0 - 0x6df0)/4
+#define SI_CRTC4_REGISTER_OFFSET                0x2c00 //(0x11df0 - 0x6df0)/4
+#define SI_CRTC5_REGISTER_OFFSET                0x2f00 //(0x129f0 - 0x6df0)/4
+
+#define CURSOR_WIDTH 64
+#define CURSOR_HEIGHT 64
+#define AMDGPU_MM_INDEX                        0x0000
+#define AMDGPU_MM_DATA                 0x0001
+
+#define VERDE_NUM_CRTC 6
+#define        BLACKOUT_MODE_MASK                      0x00000007
+#define        VGA_RENDER_CONTROL                      0xC0
+#define R_000300_VGA_RENDER_CONTROL             0xC0
+#define C_000300_VGA_VSTATUS_CNTL               0xFFFCFFFF
+#define EVERGREEN_CRTC_STATUS                   0x1BA3
+#define EVERGREEN_CRTC_V_BLANK                  (1 << 0)
+#define EVERGREEN_CRTC_STATUS_POSITION          0x1BA4
+/* CRTC blocks at 0x6df0, 0x79f0, 0x105f0, 0x111f0, 0x11df0, 0x129f0 */
+#define EVERGREEN_CRTC_V_BLANK_START_END                0x1b8d
+#define EVERGREEN_CRTC_CONTROL                          0x1b9c
+#define EVERGREEN_CRTC_MASTER_EN                 (1 << 0)
+#define EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24)
+#define EVERGREEN_CRTC_BLANK_CONTROL                    0x1b9d
+#define EVERGREEN_CRTC_BLANK_DATA_EN             (1 << 8)
+#define EVERGREEN_CRTC_V_BLANK                   (1 << 0)
+#define EVERGREEN_CRTC_STATUS_HV_COUNT                  0x1ba8
+#define EVERGREEN_CRTC_UPDATE_LOCK                      0x1bb5
+#define EVERGREEN_MASTER_UPDATE_LOCK                    0x1bbd
+#define EVERGREEN_MASTER_UPDATE_MODE                    0x1bbe
+#define EVERGREEN_GRPH_UPDATE_LOCK               (1 << 16)
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH     0x1a07
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH   0x1a08
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS          0x1a04
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS        0x1a05
+#define EVERGREEN_GRPH_UPDATE                           0x1a11
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS               0xc4
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH          0xc9
+#define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING    (1 << 2)
+
+#define EVERGREEN_DATA_FORMAT                           0x1ac0
+#       define EVERGREEN_INTERLEAVE_EN                  (1 << 0)
+
+#define MC_SHARED_CHMAP__NOOFCHAN_MASK 0xf000
+#define MC_SHARED_CHMAP__NOOFCHAN__SHIFT 0xc
+
+#define R600_D1GRPH_ARRAY_MODE_LINEAR_GENERAL            (0 << 20)
+#define R600_D1GRPH_ARRAY_MODE_LINEAR_ALIGNED            (1 << 20)
+#define R600_D1GRPH_ARRAY_MODE_1D_TILED_THIN1            (2 << 20)
+#define R600_D1GRPH_ARRAY_MODE_2D_TILED_THIN1            (4 << 20)
+
+#define R700_D1GRPH_PRIMARY_SURFACE_ADDRESS_HIGH                0x1a45
+#define R700_D2GRPH_PRIMARY_SURFACE_ADDRESS_HIGH                0x1845
+
+#define R700_D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH              0x1847
+#define R700_D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH              0x1a47
+
+#define DISP_INTERRUPT_STATUS__LB_D1_VBLANK_INTERRUPT_MASK 0x8
+#define DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VBLANK_INTERRUPT_MASK 0x8
+#define DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VBLANK_INTERRUPT_MASK 0x8
+#define DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VBLANK_INTERRUPT_MASK 0x8
+#define DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VBLANK_INTERRUPT_MASK 0x8
+#define DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VBLANK_INTERRUPT_MASK 0x8
+
+#define DISP_INTERRUPT_STATUS__LB_D1_VLINE_INTERRUPT_MASK 0x4
+#define DISP_INTERRUPT_STATUS_CONTINUE__LB_D2_VLINE_INTERRUPT_MASK 0x4
+#define DISP_INTERRUPT_STATUS_CONTINUE2__LB_D3_VLINE_INTERRUPT_MASK 0x4
+#define DISP_INTERRUPT_STATUS_CONTINUE3__LB_D4_VLINE_INTERRUPT_MASK 0x4
+#define DISP_INTERRUPT_STATUS_CONTINUE4__LB_D5_VLINE_INTERRUPT_MASK 0x4
+#define DISP_INTERRUPT_STATUS_CONTINUE5__LB_D6_VLINE_INTERRUPT_MASK 0x4
+
+#define DISP_INTERRUPT_STATUS__DC_HPD1_INTERRUPT_MASK 0x20000
+#define DISP_INTERRUPT_STATUS_CONTINUE__DC_HPD2_INTERRUPT_MASK 0x20000
+#define DISP_INTERRUPT_STATUS_CONTINUE2__DC_HPD3_INTERRUPT_MASK 0x20000
+#define DISP_INTERRUPT_STATUS_CONTINUE3__DC_HPD4_INTERRUPT_MASK 0x20000
+#define DISP_INTERRUPT_STATUS_CONTINUE4__DC_HPD5_INTERRUPT_MASK 0x20000
+#define DISP_INTERRUPT_STATUS_CONTINUE5__DC_HPD6_INTERRUPT_MASK 0x20000
+
+#define GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_OCCURRED_MASK 0x1
+#define GRPH_INTERRUPT_STATUS__GRPH_PFLIP_INT_CLEAR_MASK 0x100
+
+#define DC_HPD1_INT_CONTROL__DC_HPD1_INT_ACK_MASK 0x1
+
+#define R600_D1GRPH_SWAP_CONTROL                               0x1843
+#define R600_D1GRPH_SWAP_ENDIAN_NONE                    (0 << 0)
+#define R600_D1GRPH_SWAP_ENDIAN_16BIT                   (1 << 0)
+#define R600_D1GRPH_SWAP_ENDIAN_32BIT                   (2 << 0)
+#define R600_D1GRPH_SWAP_ENDIAN_64BIT                   (3 << 0)
+
+#define AVIVO_D1VGA_CONTROL                                    0x00cc
+#       define AVIVO_DVGA_CONTROL_MODE_ENABLE            (1 << 0)
+#       define AVIVO_DVGA_CONTROL_TIMING_SELECT          (1 << 8)
+#       define AVIVO_DVGA_CONTROL_SYNC_POLARITY_SELECT   (1 << 9)
+#       define AVIVO_DVGA_CONTROL_OVERSCAN_TIMING_SELECT (1 << 10)
+#       define AVIVO_DVGA_CONTROL_OVERSCAN_COLOR_EN      (1 << 16)
+#       define AVIVO_DVGA_CONTROL_ROTATE                 (1 << 24)
+#define AVIVO_D2VGA_CONTROL                                    0x00ce
+
+#define R600_BUS_CNTL                                           0x1508
+#       define R600_BIOS_ROM_DIS                                (1 << 1)
+
+#define R600_ROM_CNTL                              0x580
+#       define R600_SCK_OVERWRITE                  (1 << 1)
+#       define R600_SCK_PRESCALE_CRYSTAL_CLK_SHIFT 28
+#       define R600_SCK_PRESCALE_CRYSTAL_CLK_MASK  (0xf << 28)
+
+#define GRPH_INTERRUPT_CONTROL__GRPH_PFLIP_INT_MASK_MASK 0x1
+
+#define FMT_BIT_DEPTH_CONTROL                0x1bf2
+#define FMT_TRUNCATE_EN               (1 << 0)
+#define FMT_TRUNCATE_DEPTH            (1 << 4)
+#define FMT_SPATIAL_DITHER_EN         (1 << 8)
+#define FMT_SPATIAL_DITHER_MODE(x)    ((x) << 9)
+#define FMT_SPATIAL_DITHER_DEPTH      (1 << 12)
+#define FMT_FRAME_RANDOM_ENABLE       (1 << 13)
+#define FMT_RGB_RANDOM_ENABLE         (1 << 14)
+#define FMT_HIGHPASS_RANDOM_ENABLE    (1 << 15)
+#define FMT_TEMPORAL_DITHER_EN        (1 << 16)
+#define FMT_TEMPORAL_DITHER_DEPTH     (1 << 20)
+#define FMT_TEMPORAL_DITHER_OFFSET(x) ((x) << 21)
+#define FMT_TEMPORAL_LEVEL            (1 << 24)
+#define FMT_TEMPORAL_DITHER_RESET     (1 << 25)
+#define FMT_25FRC_SEL(x)              ((x) << 26)
+#define FMT_50FRC_SEL(x)              ((x) << 28)
+#define FMT_75FRC_SEL(x)              ((x) << 30)
+
+#define EVERGREEN_DC_LUT_CONTROL                        0x1a80
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_BLUE              0x1a81
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_GREEN             0x1a82
+#define EVERGREEN_DC_LUT_BLACK_OFFSET_RED               0x1a83
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_BLUE              0x1a84
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_GREEN             0x1a85
+#define EVERGREEN_DC_LUT_WHITE_OFFSET_RED               0x1a86
+#define EVERGREEN_DC_LUT_30_COLOR                       0x1a7c
+#define EVERGREEN_DC_LUT_RW_INDEX                       0x1a79
+#define EVERGREEN_DC_LUT_WRITE_EN_MASK                  0x1a7e
+#define EVERGREEN_DC_LUT_RW_MODE                        0x1a78
+
+#define EVERGREEN_GRPH_ENABLE                           0x1a00
+#define EVERGREEN_GRPH_CONTROL                          0x1a01
+#define EVERGREEN_GRPH_DEPTH(x)                  (((x) & 0x3) << 0)
+#define EVERGREEN_GRPH_DEPTH_8BPP                0
+#define EVERGREEN_GRPH_DEPTH_16BPP               1
+#define EVERGREEN_GRPH_DEPTH_32BPP               2
+#define EVERGREEN_GRPH_NUM_BANKS(x)              (((x) & 0x3) << 2)
+#define EVERGREEN_ADDR_SURF_2_BANK               0
+#define EVERGREEN_ADDR_SURF_4_BANK               1
+#define EVERGREEN_ADDR_SURF_8_BANK               2
+#define EVERGREEN_ADDR_SURF_16_BANK              3
+#define EVERGREEN_GRPH_Z(x)                      (((x) & 0x3) << 4)
+#define EVERGREEN_GRPH_BANK_WIDTH(x)             (((x) & 0x3) << 6)
+#define EVERGREEN_ADDR_SURF_BANK_WIDTH_1         0
+#define EVERGREEN_ADDR_SURF_BANK_WIDTH_2         1
+#define EVERGREEN_ADDR_SURF_BANK_WIDTH_4         2
+#define EVERGREEN_ADDR_SURF_BANK_WIDTH_8         3
+#define EVERGREEN_GRPH_FORMAT(x)                 (((x) & 0x7) << 8)
+
+#define EVERGREEN_GRPH_FORMAT_INDEXED            0
+#define EVERGREEN_GRPH_FORMAT_ARGB1555           0
+#define EVERGREEN_GRPH_FORMAT_ARGB565            1
+#define EVERGREEN_GRPH_FORMAT_ARGB4444           2
+#define EVERGREEN_GRPH_FORMAT_AI88               3
+#define EVERGREEN_GRPH_FORMAT_MONO16             4
+#define EVERGREEN_GRPH_FORMAT_BGRA5551           5
+
+/* 32 BPP */
+#define EVERGREEN_GRPH_FORMAT_ARGB8888           0
+#define EVERGREEN_GRPH_FORMAT_ARGB2101010        1
+#define EVERGREEN_GRPH_FORMAT_32BPP_DIG          2
+#define EVERGREEN_GRPH_FORMAT_8B_ARGB2101010     3
+#define EVERGREEN_GRPH_FORMAT_BGRA1010102        4
+#define EVERGREEN_GRPH_FORMAT_8B_BGRA1010102     5
+#define EVERGREEN_GRPH_FORMAT_RGB111110          6
+#define EVERGREEN_GRPH_FORMAT_BGR101111          7
+#define EVERGREEN_GRPH_BANK_HEIGHT(x)            (((x) & 0x3) << 11)
+#define EVERGREEN_ADDR_SURF_BANK_HEIGHT_1        0
+#define EVERGREEN_ADDR_SURF_BANK_HEIGHT_2        1
+#define EVERGREEN_ADDR_SURF_BANK_HEIGHT_4        2
+#define EVERGREEN_ADDR_SURF_BANK_HEIGHT_8        3
+#define EVERGREEN_GRPH_TILE_SPLIT(x)             (((x) & 0x7) << 13)
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_64B       0
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_128B      1
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_256B      2
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_512B      3
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_1KB       4
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_2KB       5
+#define EVERGREEN_ADDR_SURF_TILE_SPLIT_4KB       6
+#define EVERGREEN_GRPH_MACRO_TILE_ASPECT(x)      (((x) & 0x3) << 18)
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1  0
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2  1
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4  2
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8  3
+#define EVERGREEN_GRPH_ARRAY_MODE(x)             (((x) & 0x7) << 20)
+#define EVERGREEN_GRPH_ARRAY_LINEAR_GENERAL      0
+#define EVERGREEN_GRPH_ARRAY_LINEAR_ALIGNED      1
+#define EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1      2
+#define EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1      4
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1  0
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2  1
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4  2
+#define EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8  3
+
+#define EVERGREEN_GRPH_SWAP_CONTROL                     0x1a03
+#define EVERGREEN_GRPH_ENDIAN_SWAP(x)            (((x) & 0x3) << 0)
+#       define EVERGREEN_GRPH_ENDIAN_NONE               0
+#       define EVERGREEN_GRPH_ENDIAN_8IN16              1
+#       define EVERGREEN_GRPH_ENDIAN_8IN32              2
+#       define EVERGREEN_GRPH_ENDIAN_8IN64              3
+
+#define EVERGREEN_D3VGA_CONTROL                         0xf8
+#define EVERGREEN_D4VGA_CONTROL                         0xf9
+#define EVERGREEN_D5VGA_CONTROL                         0xfa
+#define EVERGREEN_D6VGA_CONTROL                         0xfb
+
+#define EVERGREEN_GRPH_SURFACE_ADDRESS_MASK      0xffffff00
+
+#define EVERGREEN_GRPH_LUT_10BIT_BYPASS_CONTROL         0x1a02
+#define EVERGREEN_LUT_10BIT_BYPASS_EN            (1 << 8)
+
+#define EVERGREEN_GRPH_PITCH                            0x1a06
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH     0x1a07
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH   0x1a08
+#define EVERGREEN_GRPH_SURFACE_OFFSET_X                 0x1a09
+#define EVERGREEN_GRPH_SURFACE_OFFSET_Y                 0x1a0a
+#define EVERGREEN_GRPH_X_START                          0x1a0b
+#define EVERGREEN_GRPH_Y_START                          0x1a0c
+#define EVERGREEN_GRPH_X_END                            0x1a0d
+#define EVERGREEN_GRPH_Y_END                            0x1a0e
+#define EVERGREEN_GRPH_UPDATE                           0x1a11
+#define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING    (1 << 2)
+#define EVERGREEN_GRPH_UPDATE_LOCK               (1 << 16)
+#define EVERGREEN_GRPH_FLIP_CONTROL                     0x1a12
+#define EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN (1 << 0)
+
+#define EVERGREEN_VIEWPORT_START                        0x1b5c
+#define EVERGREEN_VIEWPORT_SIZE                         0x1b5d
+#define EVERGREEN_DESKTOP_HEIGHT                        0x1ac1
+
+/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */
+#define EVERGREEN_CUR_CONTROL                           0x1a66
+#       define EVERGREEN_CURSOR_EN                      (1 << 0)
+#       define EVERGREEN_CURSOR_MODE(x)                 (((x) & 0x3) << 8)
+#       define EVERGREEN_CURSOR_MONO                    0
+#       define EVERGREEN_CURSOR_24_1                    1
+#       define EVERGREEN_CURSOR_24_8_PRE_MULT           2
+#       define EVERGREEN_CURSOR_24_8_UNPRE_MULT         3
+#       define EVERGREEN_CURSOR_2X_MAGNIFY              (1 << 16)
+#       define EVERGREEN_CURSOR_FORCE_MC_ON             (1 << 20)
+#       define EVERGREEN_CURSOR_URGENT_CONTROL(x)       (((x) & 0x7) << 24)
+#       define EVERGREEN_CURSOR_URGENT_ALWAYS           0
+#       define EVERGREEN_CURSOR_URGENT_1_8              1
+#       define EVERGREEN_CURSOR_URGENT_1_4              2
+#       define EVERGREEN_CURSOR_URGENT_3_8              3
+#       define EVERGREEN_CURSOR_URGENT_1_2              4
+#define EVERGREEN_CUR_SURFACE_ADDRESS                   0x1a67
+#       define EVERGREEN_CUR_SURFACE_ADDRESS_MASK       0xfffff000
+#define EVERGREEN_CUR_SIZE                              0x1a68
+#define EVERGREEN_CUR_SURFACE_ADDRESS_HIGH              0x1a69
+#define EVERGREEN_CUR_POSITION                          0x1a6a
+#define EVERGREEN_CUR_HOT_SPOT                          0x1a6b
+#define EVERGREEN_CUR_COLOR1                            0x1a6c
+#define EVERGREEN_CUR_COLOR2                            0x1a6d
+#define EVERGREEN_CUR_UPDATE                            0x1a6e
+#       define EVERGREEN_CURSOR_UPDATE_PENDING          (1 << 0)
+#       define EVERGREEN_CURSOR_UPDATE_TAKEN            (1 << 1)
+#       define EVERGREEN_CURSOR_UPDATE_LOCK             (1 << 16)
+#       define EVERGREEN_CURSOR_DISABLE_MULTIPLE_UPDATE (1 << 24)
+
+
+#define NI_INPUT_CSC_CONTROL                           0x1a35
+#       define NI_INPUT_CSC_GRPH_MODE(x)               (((x) & 0x3) << 0)
+#       define NI_INPUT_CSC_BYPASS                     0
+#       define NI_INPUT_CSC_PROG_COEFF                 1
+#       define NI_INPUT_CSC_PROG_SHARED_MATRIXA        2
+#       define NI_INPUT_CSC_OVL_MODE(x)                (((x) & 0x3) << 4)
+
+#define NI_OUTPUT_CSC_CONTROL                          0x1a3c
+#       define NI_OUTPUT_CSC_GRPH_MODE(x)              (((x) & 0x7) << 0)
+#       define NI_OUTPUT_CSC_BYPASS                    0
+#       define NI_OUTPUT_CSC_TV_RGB                    1
+#       define NI_OUTPUT_CSC_YCBCR_601                 2
+#       define NI_OUTPUT_CSC_YCBCR_709                 3
+#       define NI_OUTPUT_CSC_PROG_COEFF                4
+#       define NI_OUTPUT_CSC_PROG_SHARED_MATRIXB       5
+#       define NI_OUTPUT_CSC_OVL_MODE(x)               (((x) & 0x7) << 4)
+
+#define NI_DEGAMMA_CONTROL                             0x1a58
+#       define NI_GRPH_DEGAMMA_MODE(x)                 (((x) & 0x3) << 0)
+#       define NI_DEGAMMA_BYPASS                       0
+#       define NI_DEGAMMA_SRGB_24                      1
+#       define NI_DEGAMMA_XVYCC_222                    2
+#       define NI_OVL_DEGAMMA_MODE(x)                  (((x) & 0x3) << 4)
+#       define NI_ICON_DEGAMMA_MODE(x)                 (((x) & 0x3) << 8)
+#       define NI_CURSOR_DEGAMMA_MODE(x)               (((x) & 0x3) << 12)
+
+#define NI_GAMUT_REMAP_CONTROL                         0x1a59
+#       define NI_GRPH_GAMUT_REMAP_MODE(x)             (((x) & 0x3) << 0)
+#       define NI_GAMUT_REMAP_BYPASS                   0
+#       define NI_GAMUT_REMAP_PROG_COEFF               1
+#       define NI_GAMUT_REMAP_PROG_SHARED_MATRIXA      2
+#       define NI_GAMUT_REMAP_PROG_SHARED_MATRIXB      3
+#       define NI_OVL_GAMUT_REMAP_MODE(x)              (((x) & 0x3) << 4)
+
+#define NI_REGAMMA_CONTROL                             0x1aa0
+#       define NI_GRPH_REGAMMA_MODE(x)                 (((x) & 0x7) << 0)
+#       define NI_REGAMMA_BYPASS                       0
+#       define NI_REGAMMA_SRGB_24                      1
+#       define NI_REGAMMA_XVYCC_222                    2
+#       define NI_REGAMMA_PROG_A                       3
+#       define NI_REGAMMA_PROG_B                       4
+#       define NI_OVL_REGAMMA_MODE(x)                  (((x) & 0x7) << 4)
+
+
+#define NI_PRESCALE_GRPH_CONTROL                       0x1a2d
+#       define NI_GRPH_PRESCALE_BYPASS                 (1 << 4)
+
+#define NI_PRESCALE_OVL_CONTROL                        0x1a31
+#       define NI_OVL_PRESCALE_BYPASS                  (1 << 4)
+
+#define NI_INPUT_GAMMA_CONTROL                         0x1a10
+#       define NI_GRPH_INPUT_GAMMA_MODE(x)             (((x) & 0x3) << 0)
+#       define NI_INPUT_GAMMA_USE_LUT                  0
+#       define NI_INPUT_GAMMA_BYPASS                   1
+#       define NI_INPUT_GAMMA_SRGB_24                  2
+#       define NI_INPUT_GAMMA_XVYCC_222                3
+#       define NI_OVL_INPUT_GAMMA_MODE(x)              (((x) & 0x3) << 4)
+
+#define IH_RB_WPTR__RB_OVERFLOW_MASK   0x1
+#define IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK 0x80000000
+#define SRBM_STATUS__IH_BUSY_MASK      0x20000
+#define SRBM_SOFT_RESET__SOFT_RESET_IH_MASK    0x400
+
+#define        BLACKOUT_MODE_MASK                      0x00000007
+#define        VGA_RENDER_CONTROL                      0xC0
+#define R_000300_VGA_RENDER_CONTROL             0xC0
+#define C_000300_VGA_VSTATUS_CNTL               0xFFFCFFFF
+#define EVERGREEN_CRTC_STATUS                   0x1BA3
+#define EVERGREEN_CRTC_V_BLANK                  (1 << 0)
+#define EVERGREEN_CRTC_STATUS_POSITION          0x1BA4
+/* CRTC blocks at 0x6df0, 0x79f0, 0x105f0, 0x111f0, 0x11df0, 0x129f0 */
+#define EVERGREEN_CRTC_V_BLANK_START_END                0x1b8d
+#define EVERGREEN_CRTC_CONTROL                          0x1b9c
+#       define EVERGREEN_CRTC_MASTER_EN                 (1 << 0)
+#       define EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24)
+#define EVERGREEN_CRTC_BLANK_CONTROL                    0x1b9d
+#       define EVERGREEN_CRTC_BLANK_DATA_EN             (1 << 8)
+#       define EVERGREEN_CRTC_V_BLANK                   (1 << 0)
+#define EVERGREEN_CRTC_STATUS_HV_COUNT                  0x1ba8
+#define EVERGREEN_CRTC_UPDATE_LOCK                      0x1bb5
+#define EVERGREEN_MASTER_UPDATE_LOCK                    0x1bbd
+#define EVERGREEN_MASTER_UPDATE_MODE                    0x1bbe
+#define EVERGREEN_GRPH_UPDATE_LOCK               (1 << 16)
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH     0x1a07
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH   0x1a08
+#define EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS          0x1a04
+#define EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS        0x1a05
+#define EVERGREEN_GRPH_UPDATE                           0x1a11
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS               0xc4
+#define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH          0xc9
+#define EVERGREEN_GRPH_SURFACE_UPDATE_PENDING    (1 << 2)
+
+#define mmVM_CONTEXT1_CNTL__xxRANGE_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x10
+#define mmVM_CONTEXT1_CNTL__xxRANGE_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0x4
+#define mmVM_CONTEXT1_CNTL__xxDUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x80
+#define mmVM_CONTEXT1_CNTL__xxDUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0x7
+#define mmVM_CONTEXT1_CNTL__xxPDE0_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x400
+#define mmVM_CONTEXT1_CNTL__xxPDE0_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0xa
+#define mmVM_CONTEXT1_CNTL__xxVALID_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x2000
+#define mmVM_CONTEXT1_CNTL__xxVALID_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0xd
+#define mmVM_CONTEXT1_CNTL__xxREAD_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x10000
+#define mmVM_CONTEXT1_CNTL__xxREAD_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0x10
+#define mmVM_CONTEXT1_CNTL__xxWRITE_PROTECTION_FAULT_ENABLE_DEFAULT_MASK 0x80000
+#define mmVM_CONTEXT1_CNTL__xxWRITE_PROTECTION_FAULT_ENABLE_DEFAULT__SHIFT 0x13
+
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxVMID_MASK 0x1e000000
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxVMID__SHIFT 0x19
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxPROTECTIONS_MASK 0xff
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxPROTECTIONS__SHIFT 0x0
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxMEMORY_CLIENT_ID_MASK 0xff000
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxMEMORY_CLIENT_ID__SHIFT 0xc
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxMEMORY_CLIENT_RW_MASK 0x1000000
+#define mmVM_CONTEXT1_PROTECTION_FAULT_STATUS__xxMEMORY_CLIENT_RW__SHIFT 0x18
+
+#define mmMC_SHARED_BLACKOUT_CNTL__xxBLACKOUT_MODE_MASK 0x7
+#define mmMC_SHARED_BLACKOUT_CNTL__xxBLACKOUT_MODE__SHIFT 0x0
+
+#define mmBIF_FB_EN__xxFB_READ_EN_MASK 0x1
+#define mmBIF_FB_EN__xxFB_READ_EN__SHIFT 0x0
+#define mmBIF_FB_EN__xxFB_WRITE_EN_MASK 0x2
+#define mmBIF_FB_EN__xxFB_WRITE_EN__SHIFT 0x1
+
+#define mmSRBM_SOFT_RESET__xxSOFT_RESET_VMC_MASK 0x20000
+#define mmSRBM_SOFT_RESET__xxSOFT_RESET_VMC__SHIFT 0x11
+#define mmSRBM_SOFT_RESET__xxSOFT_RESET_MC_MASK 0x800
+#define mmSRBM_SOFT_RESET__xxSOFT_RESET_MC__SHIFT 0xb
+
+#define VM_CONTEXT1_CNTL__RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x8
+#define VM_CONTEXT1_CNTL__RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0x3
+#define VM_CONTEXT1_CNTL__DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x40
+#define VM_CONTEXT1_CNTL__DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0x6
+#define VM_CONTEXT1_CNTL__PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x200
+#define VM_CONTEXT1_CNTL__PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0x9
+#define VM_CONTEXT1_CNTL__VALID_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x1000
+#define VM_CONTEXT1_CNTL__VALID_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0xc
+#define VM_CONTEXT1_CNTL__READ_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x8000
+#define VM_CONTEXT1_CNTL__READ_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0xf
+#define VM_CONTEXT1_CNTL__WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK 0x40000
+#define VM_CONTEXT1_CNTL__WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT__SHIFT 0x12
+
+#define MC_SEQ_MISC0__MT__MASK 0xf0000000
+#define MC_SEQ_MISC0__MT__GDDR1  0x10000000
+#define MC_SEQ_MISC0__MT__DDR2   0x20000000
+#define MC_SEQ_MISC0__MT__GDDR3  0x30000000
+#define MC_SEQ_MISC0__MT__GDDR4  0x40000000
+#define MC_SEQ_MISC0__MT__GDDR5  0x50000000
+#define MC_SEQ_MISC0__MT__HBM    0x60000000
+#define MC_SEQ_MISC0__MT__DDR3   0xB0000000
+
+#define SRBM_STATUS__MCB_BUSY_MASK 0x200
+#define SRBM_STATUS__MCB_BUSY__SHIFT 0x9
+#define SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK 0x400
+#define SRBM_STATUS__MCB_NON_DISPLAY_BUSY__SHIFT 0xa
+#define SRBM_STATUS__MCC_BUSY_MASK 0x800
+#define SRBM_STATUS__MCC_BUSY__SHIFT 0xb
+#define SRBM_STATUS__MCD_BUSY_MASK 0x1000
+#define SRBM_STATUS__MCD_BUSY__SHIFT 0xc
+#define SRBM_STATUS__VMC_BUSY_MASK 0x100
+#define SRBM_STATUS__VMC_BUSY__SHIFT 0x8
+
+
+#define GRBM_STATUS__GUI_ACTIVE_MASK 0x80000000
+#define CP_INT_CNTL_RING__TIME_STAMP_INT_ENABLE_MASK 0x4000000
+#define CP_INT_CNTL_RING0__PRIV_REG_INT_ENABLE_MASK 0x800000
+#define CP_INT_CNTL_RING0__PRIV_INSTR_INT_ENABLE_MASK 0x400000
+#define PACKET3_SEM_WAIT_ON_SIGNAL    (0x1 << 12)
+#define PACKET3_SEM_SEL_SIGNAL     (0x6 << 29)
+#define PACKET3_SEM_SEL_WAIT       (0x7 << 29)
+
+#define CONFIG_CNTL    0x1509
+#define CC_DRM_ID_STRAPS       0X1559
+#define AMDGPU_PCIE_INDEX      0xc
+#define AMDGPU_PCIE_DATA       0xd
+
+#define DMA_SEM_INCOMPLETE_TIMER_CNTL                     0x3411
+#define DMA_SEM_WAIT_FAIL_TIMER_CNTL                      0x3412
+#define DMA_MODE                                          0x342f
+#define DMA_RB_RPTR_ADDR_HI                               0x3407
+#define DMA_RB_RPTR_ADDR_LO                               0x3408
+#define DMA_BUSY_MASK 0x20
+#define DMA1_BUSY_MASK 0X40
+#define SDMA_MAX_INSTANCE 2
+
+#define PCIE_BUS_CLK    10000
+#define TCLK            (PCIE_BUS_CLK / 10)
+#define CC_DRM_ID_STRAPS__ATI_REV_ID_MASK              0xf0000000
+#define CC_DRM_ID_STRAPS__ATI_REV_ID__SHIFT 0x1c
+#define        PCIE_PORT_INDEX                                 0xe
+#define        PCIE_PORT_DATA                                  0xf
+#define EVERGREEN_PIF_PHY0_INDEX                        0x8
+#define EVERGREEN_PIF_PHY0_DATA                         0xc
+#define EVERGREEN_PIF_PHY1_INDEX                        0x10
+#define EVERGREEN_PIF_PHY1_DATA                                0x14
+
+#define        MC_VM_FB_OFFSET                                 0x81a
+
+#endif
index f3e53b1..19802e9 100644 (file)
@@ -34,6 +34,7 @@
 #define mmUVD_UDEC_ADDR_CONFIG                                                  0x3bd3
 #define mmUVD_UDEC_DB_ADDR_CONFIG                                               0x3bd4
 #define mmUVD_UDEC_DBW_ADDR_CONFIG                                              0x3bd5
+#define mmUVD_NO_OP                                                             0x3bff
 #define mmUVD_SEMA_CNTL                                                         0x3d00
 #define mmUVD_LMI_EXT40_ADDR                                                    0x3d26
 #define mmUVD_CTX_INDEX                                                         0x3d28
index eb4cf53..cc972d2 100644 (file)
@@ -34,6 +34,7 @@
 #define mmUVD_UDEC_ADDR_CONFIG                                                  0x3bd3
 #define mmUVD_UDEC_DB_ADDR_CONFIG                                               0x3bd4
 #define mmUVD_UDEC_DBW_ADDR_CONFIG                                              0x3bd5
+#define mmUVD_NO_OP                                                             0x3bff
 #define mmUVD_LMI_RBC_RB_64BIT_BAR_LOW                                          0x3c69
 #define mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH                                         0x3c68
 #define mmUVD_LMI_RBC_IB_64BIT_BAR_LOW                                          0x3c67
index ec69869..378f4b6 100644 (file)
@@ -35,6 +35,7 @@
 #define mmUVD_UDEC_DB_ADDR_CONFIG                                               0x3bd4
 #define mmUVD_UDEC_DBW_ADDR_CONFIG                                              0x3bd5
 #define mmUVD_POWER_STATUS_U                                                    0x3bfd
+#define mmUVD_NO_OP                                                             0x3bff
 #define mmUVD_LMI_RBC_RB_64BIT_BAR_LOW                                          0x3c69
 #define mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH                                         0x3c68
 #define mmUVD_LMI_RBC_IB_64BIT_BAR_LOW                                          0x3c67
index 3493da5..4a4d379 100644 (file)
@@ -494,6 +494,7 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3
   union
   {
     ATOM_COMPUTE_CLOCK_FREQ  ulClock;         //Input Parameter
+    ULONG ulClockParams;                      //ULONG access for BE
     ATOM_S_MPLL_FB_DIVIDER   ulFbDiv;         //Output Parameter
   };
   UCHAR   ucRefDiv;                           //Output Parameter
@@ -526,6 +527,7 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5
   union
   {
     ATOM_COMPUTE_CLOCK_FREQ  ulClock;         //Input Parameter
+    ULONG ulClockParams;                      //ULONG access for BE
     ATOM_S_MPLL_FB_DIVIDER   ulFbDiv;         //Output Parameter
   };
   UCHAR   ucRefDiv;                           //Output Parameter
old mode 100644 (file)
new mode 100755 (executable)
index b86aba9..df7c18b
@@ -119,6 +119,8 @@ enum cgs_system_info_id {
        CGS_SYSTEM_INFO_PG_FLAGS,
        CGS_SYSTEM_INFO_GFX_CU_INFO,
        CGS_SYSTEM_INFO_GFX_SE_INFO,
+       CGS_SYSTEM_INFO_PCIE_SUB_SYS_ID,
+       CGS_SYSTEM_INFO_PCIE_SUB_SYS_VENDOR_ID,
        CGS_SYSTEM_INFO_ID_MAXIMUM,
 };
 
@@ -159,6 +161,7 @@ struct cgs_clock_limits {
  */
 struct cgs_firmware_info {
        uint16_t                version;
+       uint16_t                fw_version;
        uint16_t                feature_version;
        uint32_t                image_size;
        uint64_t                mc_addr;
diff --git a/drivers/gpu/drm/amd/powerplay/Kconfig b/drivers/gpu/drm/amd/powerplay/Kconfig
deleted file mode 100644 (file)
index af38033..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-config DRM_AMD_POWERPLAY
-       bool  "Enable AMD powerplay component"
-       depends on DRM_AMDGPU
-       default n
-       help
-         select this option will enable AMD powerplay component.
index abbb658..7174f7a 100644 (file)
@@ -31,6 +31,7 @@
 #include "eventmanager.h"
 #include "pp_debug.h"
 
+
 #define PP_CHECK(handle)                                               \
        do {                                                            \
                if ((handle) == NULL || (handle)->pp_valid != PP_VALID) \
@@ -162,12 +163,12 @@ static int pp_hw_fini(void *handle)
        pp_handle = (struct pp_instance *)handle;
        eventmgr = pp_handle->eventmgr;
 
-       if (eventmgr != NULL || eventmgr->pp_eventmgr_fini != NULL)
+       if (eventmgr != NULL && eventmgr->pp_eventmgr_fini != NULL)
                eventmgr->pp_eventmgr_fini(eventmgr);
 
        smumgr = pp_handle->smu_mgr;
 
-       if (smumgr != NULL || smumgr->smumgr_funcs != NULL ||
+       if (smumgr != NULL && smumgr->smumgr_funcs != NULL &&
                smumgr->smumgr_funcs->smu_fini != NULL)
                smumgr->smumgr_funcs->smu_fini(smumgr);
 
@@ -190,11 +191,9 @@ static int pp_sw_reset(void *handle)
 }
 
 
-static int pp_set_clockgating_state(void *handle,
-                                   enum amd_clockgating_state state)
+int amd_set_clockgating_by_smu(void *handle, uint32_t msg_id)
 {
        struct pp_hwmgr  *hwmgr;
-       uint32_t msg_id, pp_state;
 
        if (handle == NULL)
                return -EINVAL;
@@ -208,76 +207,7 @@ static int pp_set_clockgating_state(void *handle,
                return 0;
        }
 
-       if (state == AMD_CG_STATE_UNGATE)
-               pp_state = 0;
-       else
-               pp_state = PP_STATE_CG | PP_STATE_LS;
-
-       /* Enable/disable GFX blocks clock gating through SMU */
-       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
-                       PP_BLOCK_GFX_CG,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
-                       PP_BLOCK_GFX_3D,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
-                       PP_BLOCK_GFX_RLC,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
-                       PP_BLOCK_GFX_CP,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_GFX,
-                       PP_BLOCK_GFX_MG,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-
-       /* Enable/disable System blocks clock gating through SMU */
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_BIF,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_BIF,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_MC,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_ROM,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_DRM,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_HDP,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-       msg_id = PP_CG_MSG_ID(PP_GROUP_SYS,
-                       PP_BLOCK_SYS_SDMA,
-                       PP_STATE_SUPPORT_CG | PP_STATE_SUPPORT_LS,
-                       pp_state);
-       hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
-
-       return 0;
+       return hwmgr->hwmgr_func->update_clock_gatings(hwmgr, &msg_id);
 }
 
 static int pp_set_powergating_state(void *handle,
@@ -361,7 +291,7 @@ const struct amd_ip_funcs pp_ip_funcs = {
        .is_idle = pp_is_idle,
        .wait_for_idle = pp_wait_for_idle,
        .soft_reset = pp_sw_reset,
-       .set_clockgating_state = pp_set_clockgating_state,
+       .set_clockgating_state = NULL,
        .set_powergating_state = pp_set_powergating_state,
 };
 
@@ -537,7 +467,6 @@ int pp_dpm_dispatch_tasks(void *handle, enum amd_pp_event event_id, void *input,
                ret = pem_handle_event(pp_handle->eventmgr, event_id, &data);
                break;
        case AMD_PP_EVENT_READJUST_POWER_STATE:
-               pp_handle->hwmgr->current_ps = pp_handle->hwmgr->boot_ps;
                ret = pem_handle_event(pp_handle->eventmgr, event_id, &data);
                break;
        default:
@@ -576,28 +505,6 @@ enum amd_pm_state_type pp_dpm_get_current_power_state(void *handle)
        }
 }
 
-static void
-pp_debugfs_print_current_performance_level(void *handle,
-                                              struct seq_file *m)
-{
-       struct pp_hwmgr  *hwmgr;
-
-       if (handle == NULL)
-               return;
-
-       hwmgr = ((struct pp_instance *)handle)->hwmgr;
-
-       if (hwmgr == NULL || hwmgr->hwmgr_func == NULL)
-               return;
-
-       if (hwmgr->hwmgr_func->print_current_perforce_level == NULL) {
-               printk(KERN_INFO "%s was not implemented.\n", __func__);
-               return;
-       }
-
-       hwmgr->hwmgr_func->print_current_perforce_level(hwmgr, m);
-}
-
 static int pp_dpm_set_fan_control_mode(void *handle, uint32_t mode)
 {
        struct pp_hwmgr  *hwmgr;
@@ -764,15 +671,12 @@ static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size)
        PP_CHECK_HW(hwmgr);
 
        if (!hwmgr->hardcode_pp_table) {
-               hwmgr->hardcode_pp_table =
-                               kzalloc(hwmgr->soft_pp_table_size, GFP_KERNEL);
+               hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table,
+                                                  hwmgr->soft_pp_table_size,
+                                                  GFP_KERNEL);
 
                if (!hwmgr->hardcode_pp_table)
                        return -ENOMEM;
-
-               /* to avoid powerplay crash when hardcode pptable is empty */
-               memcpy(hwmgr->hardcode_pp_table, hwmgr->soft_pp_table,
-                               hwmgr->soft_pp_table_size);
        }
 
        memcpy(hwmgr->hardcode_pp_table, buf, size);
@@ -897,6 +801,25 @@ static int pp_dpm_set_mclk_od(void *handle, uint32_t value)
        return hwmgr->hwmgr_func->set_mclk_od(hwmgr, value);
 }
 
+static int pp_dpm_read_sensor(void *handle, int idx, int32_t *value)
+{
+       struct pp_hwmgr *hwmgr;
+
+       if (!handle)
+               return -EINVAL;
+
+       hwmgr = ((struct pp_instance *)handle)->hwmgr;
+
+       PP_CHECK_HW(hwmgr);
+
+       if (hwmgr->hwmgr_func->read_sensor == NULL) {
+               printk(KERN_INFO "%s was not implemented.\n", __func__);
+               return 0;
+       }
+
+       return hwmgr->hwmgr_func->read_sensor(hwmgr, idx, value);
+}
+
 const struct amd_powerplay_funcs pp_dpm_funcs = {
        .get_temperature = pp_dpm_get_temperature,
        .load_firmware = pp_dpm_load_fw,
@@ -909,7 +832,6 @@ const struct amd_powerplay_funcs pp_dpm_funcs = {
        .powergate_vce = pp_dpm_powergate_vce,
        .powergate_uvd = pp_dpm_powergate_uvd,
        .dispatch_tasks = pp_dpm_dispatch_tasks,
-       .print_current_performance_level = pp_debugfs_print_current_performance_level,
        .set_fan_control_mode = pp_dpm_set_fan_control_mode,
        .get_fan_control_mode = pp_dpm_get_fan_control_mode,
        .set_fan_speed_percent = pp_dpm_set_fan_speed_percent,
@@ -923,6 +845,7 @@ const struct amd_powerplay_funcs pp_dpm_funcs = {
        .set_sclk_od = pp_dpm_set_sclk_od,
        .get_mclk_od = pp_dpm_get_mclk_od,
        .set_mclk_od = pp_dpm_set_mclk_od,
+       .read_sensor = pp_dpm_read_sensor,
 };
 
 static int amd_pp_instance_init(struct amd_pp_init *pp_init,
index 635fc4b..92b1178 100644 (file)
@@ -262,6 +262,8 @@ static const pem_event_action * const display_config_change_event[] = {
        unblock_adjust_power_state_tasks,
        set_cpu_power_state,
        notify_hw_power_source_tasks,
+       get_2d_performance_state_tasks,
+       set_performance_state_tasks,
        /* updateDALConfigurationTasks,
        variBrightDisplayConfigurationChangeTasks, */
        adjust_power_state_tasks,
index a46225c..4899088 100644 (file)
@@ -70,11 +70,12 @@ int psm_set_states(struct pp_eventmgr *eventmgr, unsigned long *state_id)
        int i;
 
        table_entries = hwmgr->num_ps;
+
        state = hwmgr->ps;
 
        for (i = 0; i < table_entries; i++) {
                if (state->id == *state_id) {
-                       hwmgr->request_ps = state;
+                       memcpy(hwmgr->request_ps, state, hwmgr->ps_size);
                        return 0;
                }
                state = (struct pp_power_state *)((unsigned long)state + hwmgr->ps_size);
@@ -100,13 +101,14 @@ int psm_adjust_power_state_dynamic(struct pp_eventmgr *eventmgr, bool skip)
        if (requested == NULL)
                return 0;
 
+       phm_apply_state_adjust_rules(hwmgr, requested, pcurrent);
+
        if (pcurrent == NULL || (0 != phm_check_states_equal(hwmgr, &pcurrent->hardware, &requested->hardware, &equal)))
                equal = false;
 
        if (!equal || phm_check_smc_update_required_for_display_configuration(hwmgr)) {
-               phm_apply_state_adjust_rules(hwmgr, requested, pcurrent);
                phm_set_power_state(hwmgr, &pcurrent->hardware, &requested->hardware);
-               hwmgr->current_ps = requested;
+               memcpy(hwmgr->current_ps, hwmgr->request_ps, hwmgr->ps_size);
        }
        return 0;
 }
index f7ce4cb..5fff1d6 100644 (file)
@@ -3,14 +3,12 @@
 # It provides the hardware management services for the driver.
 
 HARDWARE_MGR = hwmgr.o processpptables.o functiontables.o \
-              hardwaremanager.o pp_acpi.o cz_hwmgr.o \
-               cz_clockpowergating.o \
-              tonga_processpptables.o ppatomctrl.o \
-               tonga_hwmgr.o pppcielanes.o  tonga_thermal.o\
-               fiji_powertune.o fiji_hwmgr.o tonga_clockpowergating.o \
-               fiji_clockpowergating.o fiji_thermal.o \
-              polaris10_hwmgr.o polaris10_powertune.o polaris10_thermal.o \
-              polaris10_clockpowergating.o
+               hardwaremanager.o pp_acpi.o cz_hwmgr.o \
+               cz_clockpowergating.o pppcielanes.o\
+               process_pptables_v1_0.o ppatomctrl.o \
+               smu7_hwmgr.o smu7_powertune.o smu7_thermal.o \
+               smu7_clockpowergating.o
+
 
 AMD_PP_HWMGR = $(addprefix $(AMD_PP_PATH)/hwmgr/,$(HARDWARE_MGR))
 
index 8cc0df9..7e4fcbb 100644 (file)
@@ -178,7 +178,6 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
        int result;
 
        cz_hwmgr->gfx_ramp_step = 256*25/100;
-
        cz_hwmgr->gfx_ramp_delay = 1; /* by default, we delay 1us */
 
        for (i = 0; i < CZ_MAX_HARDWARE_POWERLEVELS; i++)
@@ -186,33 +185,19 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
 
        cz_hwmgr->mgcg_cgtt_local0 = 0x00000000;
        cz_hwmgr->mgcg_cgtt_local1 = 0x00000000;
-
        cz_hwmgr->clock_slow_down_freq = 25000;
-
        cz_hwmgr->skip_clock_slow_down = 1;
-
        cz_hwmgr->enable_nb_ps_policy = 1; /* disable until UNB is ready, Enabled */
-
        cz_hwmgr->voltage_drop_in_dce_power_gating = 0; /* disable until fully verified */
-
        cz_hwmgr->voting_rights_clients = 0x00C00033;
-
        cz_hwmgr->static_screen_threshold = 8;
-
        cz_hwmgr->ddi_power_gating_disabled = 0;
-
        cz_hwmgr->bapm_enabled = 1;
-
        cz_hwmgr->voltage_drop_threshold = 0;
-
        cz_hwmgr->gfx_power_gating_threshold = 500;
-
        cz_hwmgr->vce_slow_sclk_threshold = 20000;
-
        cz_hwmgr->dce_slow_sclk_threshold = 30000;
-
        cz_hwmgr->disable_driver_thermal_policy = 1;
-
        cz_hwmgr->disable_nb_ps3_in_battery = 0;
 
        phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
@@ -221,9 +206,6 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
        phm_cap_set(hwmgr->platform_descriptor.platformCaps,
                                    PHM_PlatformCaps_NonABMSupportInPPLib);
 
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                          PHM_PlatformCaps_SclkDeepSleep);
-
        phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
                                        PHM_PlatformCaps_DynamicM3Arbiter);
 
@@ -233,9 +215,7 @@ static int cz_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
                                  PHM_PlatformCaps_DynamicPatchPowerState);
 
        cz_hwmgr->thermal_auto_throttling_treshold = 0;
-
        cz_hwmgr->tdr_clock = 0;
-
        cz_hwmgr->disable_gfx_power_gating_in_uvd = 0;
 
        phm_cap_set(hwmgr->platform_descriptor.platformCaps,
@@ -450,19 +430,12 @@ static int cz_construct_boot_state(struct pp_hwmgr *hwmgr)
                        (uint8_t)cz_hwmgr->sys_info.bootup_nb_voltage_index;
 
        cz_hwmgr->boot_power_level.dsDividerIndex = 0;
-
        cz_hwmgr->boot_power_level.ssDividerIndex = 0;
-
        cz_hwmgr->boot_power_level.allowGnbSlow = 1;
-
        cz_hwmgr->boot_power_level.forceNBPstate = 0;
-
        cz_hwmgr->boot_power_level.hysteresis_up = 0;
-
        cz_hwmgr->boot_power_level.numSIMDToPowerDown = 0;
-
        cz_hwmgr->boot_power_level.display_wm = 0;
-
        cz_hwmgr->boot_power_level.vce_wm = 0;
 
        return 0;
@@ -749,7 +722,6 @@ static int cz_tf_update_sclk_limit(struct pp_hwmgr *hwmgr,
                cz_hwmgr->sclk_dpm.soft_max_clk  = table->entries[table->count - 1].clk;
 
        clock = hwmgr->display_config.min_core_set_clock;
-;
        if (clock == 0)
                printk(KERN_INFO "[ powerplay ] min_core_set_clock not set\n");
 
@@ -832,7 +804,7 @@ static int cz_tf_set_watermark_threshold(struct pp_hwmgr *hwmgr,
 
        smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
                                        PPSMC_MSG_SetWatermarkFrequency,
-                                     cz_hwmgr->sclk_dpm.soft_max_clk);
+                                       cz_hwmgr->sclk_dpm.soft_max_clk);
 
        return 0;
 }
@@ -858,9 +830,9 @@ static int cz_tf_enable_nb_dpm(struct pp_hwmgr *hwmgr,
                PP_DBG_LOG("enabling ALL SMU features.\n");
                dpm_features |= NB_DPM_MASK;
                ret = smum_send_msg_to_smc_with_parameter(
-                                                            hwmgr->smumgr,
-                                        PPSMC_MSG_EnableAllSmuFeatures,
-                                                            dpm_features);
+                                                         hwmgr->smumgr,
+                                                         PPSMC_MSG_EnableAllSmuFeatures,
+                                                         dpm_features);
                if (ret == 0)
                        cz_hwmgr->is_nb_dpm_enabled = true;
        }
@@ -1246,7 +1218,7 @@ static int cz_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
 
 static int cz_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
 {
-       if (hwmgr != NULL || hwmgr->backend != NULL) {
+       if (hwmgr != NULL && hwmgr->backend != NULL) {
                kfree(hwmgr->backend);
                kfree(hwmgr);
        }
@@ -1402,10 +1374,12 @@ int cz_dpm_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
                                                   PPSMC_MSG_SetUvdHardMin));
 
                        cz_enable_disable_uvd_dpm(hwmgr, true);
-               } else
+               } else {
                        cz_enable_disable_uvd_dpm(hwmgr, true);
-       } else
+               }
+       } else {
                cz_enable_disable_uvd_dpm(hwmgr, false);
+       }
 
        return 0;
 }
@@ -1564,78 +1538,6 @@ int cz_get_power_state_size(struct pp_hwmgr *hwmgr)
        return sizeof(struct cz_power_state);
 }
 
-static void
-cz_print_current_perforce_level(struct pp_hwmgr *hwmgr, struct seq_file *m)
-{
-       struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend);
-
-       struct phm_clock_voltage_dependency_table *table =
-                               hwmgr->dyn_state.vddc_dependency_on_sclk;
-
-       struct phm_vce_clock_voltage_dependency_table *vce_table =
-               hwmgr->dyn_state.vce_clock_voltage_dependency_table;
-
-       struct phm_uvd_clock_voltage_dependency_table *uvd_table =
-               hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
-
-       uint32_t sclk_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX),
-                                       TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX);
-       uint32_t uvd_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
-                                       TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_UVD_INDEX);
-       uint32_t vce_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
-                                       TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_VCE_INDEX);
-
-       uint32_t sclk, vclk, dclk, ecclk, tmp, activity_percent;
-       uint16_t vddnb, vddgfx;
-       int result;
-
-       if (sclk_index >= NUM_SCLK_LEVELS) {
-               seq_printf(m, "\n invalid sclk dpm profile %d\n", sclk_index);
-       } else {
-               sclk = table->entries[sclk_index].clk;
-               seq_printf(m, "\n index: %u sclk: %u MHz\n", sclk_index, sclk/100);
-       }
-
-       tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_NB_CURRENTVID) &
-               CURRENT_NB_VID_MASK) >> CURRENT_NB_VID__SHIFT;
-       vddnb = cz_convert_8Bit_index_to_voltage(hwmgr, tmp);
-       tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_GFX_CURRENTVID) &
-               CURRENT_GFX_VID_MASK) >> CURRENT_GFX_VID__SHIFT;
-       vddgfx = cz_convert_8Bit_index_to_voltage(hwmgr, (u16)tmp);
-       seq_printf(m, "\n vddnb: %u vddgfx: %u\n", vddnb, vddgfx);
-
-       seq_printf(m, "\n uvd    %sabled\n", cz_hwmgr->uvd_power_gated ? "dis" : "en");
-       if (!cz_hwmgr->uvd_power_gated) {
-               if (uvd_index >= CZ_MAX_HARDWARE_POWERLEVELS) {
-                       seq_printf(m, "\n invalid uvd dpm level %d\n", uvd_index);
-               } else {
-                       vclk = uvd_table->entries[uvd_index].vclk;
-                       dclk = uvd_table->entries[uvd_index].dclk;
-                       seq_printf(m, "\n index: %u uvd vclk: %u MHz dclk: %u MHz\n", uvd_index, vclk/100, dclk/100);
-               }
-       }
-
-       seq_printf(m, "\n vce    %sabled\n", cz_hwmgr->vce_power_gated ? "dis" : "en");
-       if (!cz_hwmgr->vce_power_gated) {
-               if (vce_index >= CZ_MAX_HARDWARE_POWERLEVELS) {
-                       seq_printf(m, "\n invalid vce dpm level %d\n", vce_index);
-               } else {
-                       ecclk = vce_table->entries[vce_index].ecclk;
-                       seq_printf(m, "\n index: %u vce ecclk: %u MHz\n", vce_index, ecclk/100);
-               }
-       }
-
-       result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_GetAverageGraphicsActivity);
-       if (0 == result) {
-               activity_percent = cgs_read_register(hwmgr->device, mmSMU_MP1_SRBM2P_ARG_0);
-               activity_percent = activity_percent > 100 ? 100 : activity_percent;
-       } else {
-               activity_percent = 50;
-       }
-
-       seq_printf(m, "\n [GPU load]: %u %%\n\n", activity_percent);
-}
-
 static void cz_hw_print_display_cfg(
        const struct cc6_settings *cc6_settings)
 {
@@ -1690,13 +1592,10 @@ static int cz_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time,
        struct cz_hwmgr *hw_data = (struct cz_hwmgr *)(hwmgr->backend);
 
        if (separation_time !=
-               hw_data->cc6_settings.cpu_pstate_separation_time
-               || cc6_disable !=
-               hw_data->cc6_settings.cpu_cc6_disable
-               || pstate_disable !=
-               hw_data->cc6_settings.cpu_pstate_disable
-               || pstate_switch_disable !=
-               hw_data->cc6_settings.nb_pstate_switch_disable) {
+           hw_data->cc6_settings.cpu_pstate_separation_time ||
+           cc6_disable != hw_data->cc6_settings.cpu_cc6_disable ||
+           pstate_disable != hw_data->cc6_settings.cpu_pstate_disable ||
+           pstate_switch_disable != hw_data->cc6_settings.nb_pstate_switch_disable) {
 
                hw_data->cc6_settings.cc6_setting_changed = true;
 
@@ -1799,8 +1698,7 @@ static int cz_get_performance_level(struct pp_hwmgr *hwmgr, const struct pp_hw_p
        ps = cast_const_PhwCzPowerState(state);
 
        level_index = index > ps->level - 1 ? ps->level - 1 : index;
-
-       level->coreClock  = ps->levels[level_index].engineClock;
+       level->coreClock = ps->levels[level_index].engineClock;
 
        if (designation == PHM_PerformanceLevelDesignation_PowerContainment) {
                for (i = 1; i < ps->level; i++) {
@@ -1887,6 +1785,107 @@ static int cz_get_max_high_clocks(struct pp_hwmgr *hwmgr, struct amd_pp_simple_c
        return 0;
 }
 
+static int cz_read_sensor(struct pp_hwmgr *hwmgr, int idx, int32_t *value)
+{
+       struct cz_hwmgr *cz_hwmgr = (struct cz_hwmgr *)(hwmgr->backend);
+
+       struct phm_clock_voltage_dependency_table *table =
+                               hwmgr->dyn_state.vddc_dependency_on_sclk;
+
+       struct phm_vce_clock_voltage_dependency_table *vce_table =
+               hwmgr->dyn_state.vce_clock_voltage_dependency_table;
+
+       struct phm_uvd_clock_voltage_dependency_table *uvd_table =
+               hwmgr->dyn_state.uvd_clock_voltage_dependency_table;
+
+       uint32_t sclk_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX),
+                                       TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX);
+       uint32_t uvd_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
+                                       TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_UVD_INDEX);
+       uint32_t vce_index = PHM_GET_FIELD(cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixTARGET_AND_CURRENT_PROFILE_INDEX_2),
+                                       TARGET_AND_CURRENT_PROFILE_INDEX_2, CURR_VCE_INDEX);
+
+       uint32_t sclk, vclk, dclk, ecclk, tmp, activity_percent;
+       uint16_t vddnb, vddgfx;
+       int result;
+
+       switch (idx) {
+       case AMDGPU_PP_SENSOR_GFX_SCLK:
+               if (sclk_index < NUM_SCLK_LEVELS) {
+                       sclk = table->entries[sclk_index].clk;
+                       *value = sclk;
+                       return 0;
+               }
+               return -EINVAL;
+       case AMDGPU_PP_SENSOR_VDDNB:
+               tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_NB_CURRENTVID) &
+                       CURRENT_NB_VID_MASK) >> CURRENT_NB_VID__SHIFT;
+               vddnb = cz_convert_8Bit_index_to_voltage(hwmgr, tmp);
+               *value = vddnb;
+               return 0;
+       case AMDGPU_PP_SENSOR_VDDGFX:
+               tmp = (cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixSMUSVI_GFX_CURRENTVID) &
+                       CURRENT_GFX_VID_MASK) >> CURRENT_GFX_VID__SHIFT;
+               vddgfx = cz_convert_8Bit_index_to_voltage(hwmgr, (u16)tmp);
+               *value = vddgfx;
+               return 0;
+       case AMDGPU_PP_SENSOR_UVD_VCLK:
+               if (!cz_hwmgr->uvd_power_gated) {
+                       if (uvd_index >= CZ_MAX_HARDWARE_POWERLEVELS) {
+                               return -EINVAL;
+                       } else {
+                               vclk = uvd_table->entries[uvd_index].vclk;
+                               *value = vclk;
+                               return 0;
+                       }
+               }
+               *value = 0;
+               return 0;
+       case AMDGPU_PP_SENSOR_UVD_DCLK:
+               if (!cz_hwmgr->uvd_power_gated) {
+                       if (uvd_index >= CZ_MAX_HARDWARE_POWERLEVELS) {
+                               return -EINVAL;
+                       } else {
+                               dclk = uvd_table->entries[uvd_index].dclk;
+                               *value = dclk;
+                               return 0;
+                       }
+               }
+               *value = 0;
+               return 0;
+       case AMDGPU_PP_SENSOR_VCE_ECCLK:
+               if (!cz_hwmgr->vce_power_gated) {
+                       if (vce_index >= CZ_MAX_HARDWARE_POWERLEVELS) {
+                               return -EINVAL;
+                       } else {
+                               ecclk = vce_table->entries[vce_index].ecclk;
+                               *value = ecclk;
+                               return 0;
+                       }
+               }
+               *value = 0;
+               return 0;
+       case AMDGPU_PP_SENSOR_GPU_LOAD:
+               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_GetAverageGraphicsActivity);
+               if (0 == result) {
+                       activity_percent = cgs_read_register(hwmgr->device, mmSMU_MP1_SRBM2P_ARG_0);
+                       activity_percent = activity_percent > 100 ? 100 : activity_percent;
+               } else {
+                       activity_percent = 50;
+               }
+               *value = activity_percent;
+               return 0;
+       case AMDGPU_PP_SENSOR_UVD_POWER:
+               *value = cz_hwmgr->uvd_power_gated ? 0 : 1;
+               return 0;
+       case AMDGPU_PP_SENSOR_VCE_POWER:
+               *value = cz_hwmgr->vce_power_gated ? 0 : 1;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
 static const struct pp_hwmgr_func cz_hwmgr_funcs = {
        .backend_init = cz_hwmgr_backend_init,
        .backend_fini = cz_hwmgr_backend_fini,
@@ -1902,7 +1901,6 @@ static const struct pp_hwmgr_func cz_hwmgr_funcs = {
        .patch_boot_state = cz_dpm_patch_boot_state,
        .get_pp_table_entry = cz_dpm_get_pp_table_entry,
        .get_num_of_pp_table_entries = cz_dpm_get_num_of_pp_table_entries,
-       .print_current_perforce_level = cz_print_current_perforce_level,
        .set_cpu_power_state = cz_set_cpu_power_state,
        .store_cc6_data = cz_store_cc6_data,
        .force_clock_level = cz_force_clock_level,
@@ -1912,6 +1910,7 @@ static const struct pp_hwmgr_func cz_hwmgr_funcs = {
        .get_current_shallow_sleep_clocks = cz_get_current_shallow_sleep_clocks,
        .get_clock_by_type = cz_get_clock_by_type,
        .get_max_high_clocks = cz_get_max_high_clocks,
+       .read_sensor = cz_read_sensor,
 };
 
 int cz_hwmgr_init(struct pp_hwmgr *hwmgr)
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.c
deleted file mode 100644 (file)
index 5afe820..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2015 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 "hwmgr.h"
-#include "fiji_clockpowergating.h"
-#include "fiji_ppsmc.h"
-#include "fiji_hwmgr.h"
-
-int fiji_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-       data->samu_power_gated = false;
-       data->acp_power_gated = false;
-
-       return 0;
-}
-
-int fiji_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (data->uvd_power_gated == bgate)
-               return 0;
-
-       data->uvd_power_gated = bgate;
-
-       if (bgate) {
-               cgs_set_clockgating_state(hwmgr->device,
-                                         AMD_IP_BLOCK_TYPE_UVD,
-                                         AMD_CG_STATE_GATE);
-               fiji_update_uvd_dpm(hwmgr, true);
-       } else {
-               fiji_update_uvd_dpm(hwmgr, false);
-               cgs_set_clockgating_state(hwmgr->device,
-                                         AMD_IP_BLOCK_TYPE_UVD,
-                                         AMD_CG_STATE_UNGATE);
-       }
-
-       return 0;
-}
-
-int fiji_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_set_power_state_input states;
-       const struct pp_power_state  *pcurrent;
-       struct pp_power_state  *requested;
-
-       if (data->vce_power_gated == bgate)
-               return 0;
-
-       data->vce_power_gated = bgate;
-
-       pcurrent = hwmgr->current_ps;
-       requested = hwmgr->request_ps;
-
-       states.pcurrent_state = &(pcurrent->hardware);
-       states.pnew_state = &(requested->hardware);
-
-       fiji_update_vce_dpm(hwmgr, &states);
-       fiji_enable_disable_vce_dpm(hwmgr, !bgate);
-
-       return 0;
-}
-
-int fiji_phm_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (data->samu_power_gated == bgate)
-               return 0;
-
-       data->samu_power_gated = bgate;
-
-       if (bgate)
-               fiji_update_samu_dpm(hwmgr, true);
-       else
-               fiji_update_samu_dpm(hwmgr, false);
-
-       return 0;
-}
-
-int fiji_phm_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (data->acp_power_gated == bgate)
-               return 0;
-
-       data->acp_power_gated = bgate;
-
-       if (bgate)
-               fiji_update_acp_dpm(hwmgr, true);
-       else
-               fiji_update_acp_dpm(hwmgr, false);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_clockpowergating.h
deleted file mode 100644 (file)
index 33af5f5..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2015 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 _FIJI_CLOCK_POWER_GATING_H_
-#define _FIJI_CLOCK_POWER_GATING_H_
-
-#include "fiji_hwmgr.h"
-#include "pp_asicblocks.h"
-
-extern int fiji_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate);
-extern int fiji_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate);
-extern int fiji_phm_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate);
-extern int fiji_phm_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate);
-extern int fiji_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr);
-#endif /* _TONGA_CLOCK_POWER_GATING_H_ */
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_dyn_defaults.h b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_dyn_defaults.h
deleted file mode 100644 (file)
index 32d43e8..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2015 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 FIJI_DYN_DEFAULTS_H
-#define FIJI_DYN_DEFAULTS_H
-
-/** \file
-* Volcanic Islands Dynamic default parameters.
-*/
-
-enum FIJIdpm_TrendDetection
-{
-    FIJIAdpm_TrendDetection_AUTO,
-    FIJIAdpm_TrendDetection_UP,
-    FIJIAdpm_TrendDetection_DOWN
-};
-typedef enum FIJIdpm_TrendDetection FIJIdpm_TrendDetection;
-
-/* We need to fill in the default values!!!!!!!!!!!!!!!!!!!!!!! */
-
-/* Bit vector representing same fields as hardware register. */
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT0    0x3FFFC102  /* CP_Gfx_busy ????
-                                                         * HDP_busy
-                                                         * IH_busy
-                                                         * UVD_busy
-                                                         * VCE_busy
-                                                         * ACP_busy
-                                                         * SAMU_busy
-                                                         * SDMA enabled */
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT1    0x000400  /* FE_Gfx_busy  - Intended for primary usage.   Rest are for flexibility. ????
-                                                       * SH_Gfx_busy
-                                                       * RB_Gfx_busy
-                                                       * VCE_busy */
-
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT2    0xC00080  /* SH_Gfx_busy - Intended for primary usage.   Rest are for flexibility.
-                                                       * FE_Gfx_busy
-                                                       * RB_Gfx_busy
-                                                       * ACP_busy */
-
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT3    0xC00200  /* RB_Gfx_busy - Intended for primary usage.   Rest are for flexibility.
-                                                       * FE_Gfx_busy
-                                                       * SH_Gfx_busy
-                                                       * UVD_busy */
-
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT4    0xC01680  /* UVD_busy
-                                                       * VCE_busy
-                                                       * ACP_busy
-                                                       * SAMU_busy */
-
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT5    0xC00033  /* GFX, HDP */
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT6    0xC00033  /* GFX, HDP */
-#define PPFIJI_VOTINGRIGHTSCLIENTS_DFLT7    0x3FFFC000  /* GFX, HDP */
-
-
-/* thermal protection counter (units). */
-#define PPFIJI_THERMALPROTECTCOUNTER_DFLT            0x200 /* ~19us */
-
-/* static screen threshold unit */
-#define PPFIJI_STATICSCREENTHRESHOLDUNIT_DFLT    0
-
-/* static screen threshold */
-#define PPFIJI_STATICSCREENTHRESHOLD_DFLT        0x00C8
-
-/* gfx idle clock stop threshold */
-#define PPFIJI_GFXIDLECLOCKSTOPTHRESHOLD_DFLT        0x200 /* ~19us with static screen threshold unit of 0 */
-
-/* Fixed reference divider to use when building baby stepping tables. */
-#define PPFIJI_REFERENCEDIVIDER_DFLT                  4
-
-/* ULV voltage change delay time
- * Used to be delay_vreg in N.I. split for S.I.
- * Using N.I. delay_vreg value as default
- * ReferenceClock = 2700
- * VoltageResponseTime = 1000
- * VDDCDelayTime = (VoltageResponseTime * ReferenceClock) / 1600 = 1687
- */
-#define PPFIJI_ULVVOLTAGECHANGEDELAY_DFLT             1687
-
-#define PPFIJI_CGULVPARAMETER_DFLT                     0x00040035
-#define PPFIJI_CGULVCONTROL_DFLT                       0x00007450
-#define PPFIJI_TARGETACTIVITY_DFLT                     30 /* 30%*/
-#define PPFIJI_MCLK_TARGETACTIVITY_DFLT                10 /* 10% */
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.c
deleted file mode 100644 (file)
index 120a9e2..0000000
+++ /dev/null
@@ -1,5599 +0,0 @@
-/*
- * Copyright 2015 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/module.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-#include "linux/delay.h"
-
-#include "hwmgr.h"
-#include "fiji_smumgr.h"
-#include "atombios.h"
-#include "hardwaremanager.h"
-#include "ppatomctrl.h"
-#include "atombios.h"
-#include "cgs_common.h"
-#include "fiji_dyn_defaults.h"
-#include "fiji_powertune.h"
-#include "smu73.h"
-#include "smu/smu_7_1_3_d.h"
-#include "smu/smu_7_1_3_sh_mask.h"
-#include "gmc/gmc_8_1_d.h"
-#include "gmc/gmc_8_1_sh_mask.h"
-#include "bif/bif_5_0_d.h"
-#include "bif/bif_5_0_sh_mask.h"
-#include "dce/dce_10_0_d.h"
-#include "dce/dce_10_0_sh_mask.h"
-#include "pppcielanes.h"
-#include "fiji_hwmgr.h"
-#include "tonga_processpptables.h"
-#include "tonga_pptable.h"
-#include "pp_debug.h"
-#include "pp_acpi.h"
-#include "amd_pcie_helpers.h"
-#include "cgs_linux.h"
-#include "ppinterrupt.h"
-
-#include "fiji_clockpowergating.h"
-#include "fiji_thermal.h"
-
-#define VOLTAGE_SCALE  4
-#define SMC_RAM_END            0x40000
-#define VDDC_VDDCI_DELTA       300
-
-#define MC_SEQ_MISC0_GDDR5_SHIFT 28
-#define MC_SEQ_MISC0_GDDR5_MASK  0xf0000000
-#define MC_SEQ_MISC0_GDDR5_VALUE 5
-
-#define MC_CG_ARB_FREQ_F0           0x0a /* boot-up default */
-#define MC_CG_ARB_FREQ_F1           0x0b
-#define MC_CG_ARB_FREQ_F2           0x0c
-#define MC_CG_ARB_FREQ_F3           0x0d
-
-/* From smc_reg.h */
-#define SMC_CG_IND_START            0xc0030000
-#define SMC_CG_IND_END              0xc0040000  /* First byte after SMC_CG_IND */
-
-#define VOLTAGE_SCALE               4
-#define VOLTAGE_VID_OFFSET_SCALE1   625
-#define VOLTAGE_VID_OFFSET_SCALE2   100
-
-#define VDDC_VDDCI_DELTA            300
-
-#define ixSWRST_COMMAND_1           0x1400103
-#define MC_SEQ_CNTL__CAC_EN_MASK    0x40000000
-
-/** Values for the CG_THERMAL_CTRL::DPM_EVENT_SRC field. */
-enum DPM_EVENT_SRC {
-    DPM_EVENT_SRC_ANALOG = 0,               /* Internal analog trip point */
-    DPM_EVENT_SRC_EXTERNAL = 1,             /* External (GPIO 17) signal */
-    DPM_EVENT_SRC_DIGITAL = 2,              /* Internal digital trip point (DIG_THERM_DPM) */
-    DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,   /* Internal analog or external */
-    DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL = 4   /* Internal digital or external */
-};
-
-
-/* [2.5%,~2.5%] Clock stretched is multiple of 2.5% vs
- * not and [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ]
- */
-static const uint16_t fiji_clock_stretcher_lookup_table[2][4] =
-{ {600, 1050, 3, 0}, {600, 1050, 6, 1} };
-
-/* [FF, SS] type, [] 4 voltage ranges, and
- * [Floor Freq, Boundary Freq, VID min , VID max]
- */
-static const uint32_t fiji_clock_stretcher_ddt_table[2][4][4] =
-{ { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
-  { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} } };
-
-/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%]
- * (coming from PWR_CKS_CNTL.stretch_amount reg spec)
- */
-static const uint8_t fiji_clock_stretch_amount_conversion[2][6] =
-{ {0, 1, 3, 2, 4, 5}, {0, 2, 4, 5, 6, 5} };
-
-static const unsigned long PhwFiji_Magic = (unsigned long)(PHM_VIslands_Magic);
-
-struct fiji_power_state *cast_phw_fiji_power_state(
-                                 struct pp_hw_power_state *hw_ps)
-{
-       PP_ASSERT_WITH_CODE((PhwFiji_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL;);
-
-       return (struct fiji_power_state *)hw_ps;
-}
-
-const struct fiji_power_state *cast_const_phw_fiji_power_state(
-                                const struct pp_hw_power_state *hw_ps)
-{
-       PP_ASSERT_WITH_CODE((PhwFiji_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL;);
-
-       return (const struct fiji_power_state *)hw_ps;
-}
-
-static bool fiji_is_dpm_running(struct pp_hwmgr *hwmgr)
-{
-       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
-                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
-                       ? true : false;
-}
-
-static void fiji_init_dpm_defaults(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_ulv_parm *ulv = &data->ulv;
-
-       ulv->cg_ulv_parameter = PPFIJI_CGULVPARAMETER_DFLT;
-       data->voting_rights_clients0 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT0;
-       data->voting_rights_clients1 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT1;
-       data->voting_rights_clients2 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT2;
-       data->voting_rights_clients3 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT3;
-       data->voting_rights_clients4 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT4;
-       data->voting_rights_clients5 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT5;
-       data->voting_rights_clients6 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT6;
-       data->voting_rights_clients7 = PPFIJI_VOTINGRIGHTSCLIENTS_DFLT7;
-
-       data->static_screen_threshold_unit =
-                       PPFIJI_STATICSCREENTHRESHOLDUNIT_DFLT;
-       data->static_screen_threshold =
-                       PPFIJI_STATICSCREENTHRESHOLD_DFLT;
-
-       /* Unset ABM cap as it moved to DAL.
-        * Add PHM_PlatformCaps_NonABMSupportInPPLib
-        * for re-direct ABM related request to DAL
-        */
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ABM);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_NonABMSupportInPPLib);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DynamicACTiming);
-
-       fiji_initialize_power_tune_defaults(hwmgr);
-
-       data->mclk_stutter_mode_threshold = 60000;
-       data->pcie_gen_performance.max = PP_PCIEGen1;
-       data->pcie_gen_performance.min = PP_PCIEGen3;
-       data->pcie_gen_power_saving.max = PP_PCIEGen1;
-       data->pcie_gen_power_saving.min = PP_PCIEGen3;
-       data->pcie_lane_performance.max = 0;
-       data->pcie_lane_performance.min = 16;
-       data->pcie_lane_power_saving.max = 0;
-       data->pcie_lane_power_saving.min = 16;
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DynamicUVDState);
-}
-
-static int fiji_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr,
-       phm_ppt_v1_voltage_lookup_table *lookup_table,
-       uint16_t virtual_voltage_id, int32_t *sclk)
-{
-       uint8_t entryId;
-       uint8_t voltageId;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       PP_ASSERT_WITH_CODE(lookup_table->count != 0, "Lookup table is empty", return -EINVAL);
-
-       /* search for leakage voltage ID 0xff01 ~ 0xff08 and sckl */
-       for (entryId = 0; entryId < table_info->vdd_dep_on_sclk->count; entryId++) {
-               voltageId = table_info->vdd_dep_on_sclk->entries[entryId].vddInd;
-               if (lookup_table->entries[voltageId].us_vdd == virtual_voltage_id)
-                       break;
-       }
-
-       PP_ASSERT_WITH_CODE(entryId < table_info->vdd_dep_on_sclk->count,
-                       "Can't find requested voltage id in vdd_dep_on_sclk table!",
-                       return -EINVAL;
-                       );
-
-       *sclk = table_info->vdd_dep_on_sclk->entries[entryId].clk;
-
-       return 0;
-}
-
-/**
-* Get Leakage VDDC based on leakage ID.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int fiji_get_evv_voltages(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint16_t    vv_id;
-       uint16_t    vddc = 0;
-       uint16_t    evv_default = 1150;
-       uint16_t    i, j;
-       uint32_t  sclk = 0;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)hwmgr->pptable;
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       int result;
-
-       for (i = 0; i < FIJI_MAX_LEAKAGE_COUNT; i++) {
-               vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
-               if (!fiji_get_sclk_for_voltage_evv(hwmgr,
-                               table_info->vddc_lookup_table, vv_id, &sclk)) {
-                       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_ClockStretcher)) {
-                               for (j = 1; j < sclk_table->count; j++) {
-                                       if (sclk_table->entries[j].clk == sclk &&
-                                                       sclk_table->entries[j].cks_enable == 0) {
-                                               sclk += 5000;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_EnableDriverEVV))
-                               result = atomctrl_calculate_voltage_evv_on_sclk(hwmgr,
-                                               VOLTAGE_TYPE_VDDC, sclk, vv_id, &vddc, i, true);
-                       else
-                               result = -EINVAL;
-
-                       if (result)
-                               result = atomctrl_get_voltage_evv_on_sclk(hwmgr,
-                                               VOLTAGE_TYPE_VDDC, sclk,vv_id, &vddc);
-
-                       /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */
-                       PP_ASSERT_WITH_CODE((vddc < 2000),
-                                       "Invalid VDDC value, greater than 2v!", result = -EINVAL;);
-
-                       if (result)
-                               /* 1.15V is the default safe value for Fiji */
-                               vddc = evv_default;
-
-                       /* the voltage should not be zero nor equal to leakage ID */
-                       if (vddc != 0 && vddc != vv_id) {
-                               data->vddc_leakage.actual_voltage
-                               [data->vddc_leakage.count] = vddc;
-                               data->vddc_leakage.leakage_id
-                               [data->vddc_leakage.count] = vv_id;
-                               data->vddc_leakage.count++;
-                       }
-               }
-       }
-       return 0;
-}
-
-/**
- * Change virtual leakage voltage to actual value.
- *
- * @param     hwmgr  the address of the powerplay hardware manager.
- * @param     pointer to changing voltage
- * @param     pointer to leakage table
- */
-static void fiji_patch_with_vdd_leakage(struct pp_hwmgr *hwmgr,
-               uint16_t *voltage, struct fiji_leakage_voltage *leakage_table)
-{
-       uint32_t index;
-
-       /* search for leakage voltage ID 0xff01 ~ 0xff08 */
-       for (index = 0; index < leakage_table->count; index++) {
-               /* if this voltage matches a leakage voltage ID */
-               /* patch with actual leakage voltage */
-               if (leakage_table->leakage_id[index] == *voltage) {
-                       *voltage = leakage_table->actual_voltage[index];
-                       break;
-               }
-       }
-
-       if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
-               printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n");
-}
-
-/**
-* Patch voltage lookup table by EVV leakages.
-*
-* @param     hwmgr  the address of the powerplay hardware manager.
-* @param     pointer to voltage lookup table
-* @param     pointer to leakage table
-* @return     always 0
-*/
-static int fiji_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *lookup_table,
-               struct fiji_leakage_voltage *leakage_table)
-{
-       uint32_t i;
-
-       for (i = 0; i < lookup_table->count; i++)
-               fiji_patch_with_vdd_leakage(hwmgr,
-                               &lookup_table->entries[i].us_vdd, leakage_table);
-
-       return 0;
-}
-
-static int fiji_patch_clock_voltage_limits_with_vddc_leakage(
-               struct pp_hwmgr *hwmgr, struct fiji_leakage_voltage *leakage_table,
-               uint16_t *vddc)
-{
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       fiji_patch_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);
-       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
-                       table_info->max_clock_voltage_on_dc.vddc;
-       return 0;
-}
-
-static int fiji_patch_voltage_dependency_tables_with_lookup_table(
-               struct pp_hwmgr *hwmgr)
-{
-       uint8_t entryId;
-       uint8_t voltageId;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
-                       table_info->vdd_dep_on_mclk;
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-
-       for (entryId = 0; entryId < sclk_table->count; ++entryId) {
-               voltageId = sclk_table->entries[entryId].vddInd;
-               sclk_table->entries[entryId].vddc =
-                               table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       for (entryId = 0; entryId < mclk_table->count; ++entryId) {
-               voltageId = mclk_table->entries[entryId].vddInd;
-               mclk_table->entries[entryId].vddc =
-                       table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       for (entryId = 0; entryId < mm_table->count; ++entryId) {
-               voltageId = mm_table->entries[entryId].vddcInd;
-               mm_table->entries[entryId].vddc =
-                       table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       return 0;
-
-}
-
-static int fiji_calc_voltage_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       /* Need to determine if we need calculated voltage. */
-       return 0;
-}
-
-static int fiji_calc_mm_voltage_dependency_table(struct pp_hwmgr *hwmgr)
-{
-       /* Need to determine if we need calculated voltage from mm table. */
-       return 0;
-}
-
-static int fiji_sort_lookup_table(struct pp_hwmgr *hwmgr,
-               struct phm_ppt_v1_voltage_lookup_table *lookup_table)
-{
-       uint32_t table_size, i, j;
-       struct phm_ppt_v1_voltage_lookup_record tmp_voltage_lookup_record;
-       table_size = lookup_table->count;
-
-       PP_ASSERT_WITH_CODE(0 != lookup_table->count,
-               "Lookup table is empty", return -EINVAL);
-
-       /* Sorting voltages */
-       for (i = 0; i < table_size - 1; i++) {
-               for (j = i + 1; j > 0; j--) {
-                       if (lookup_table->entries[j].us_vdd <
-                                       lookup_table->entries[j - 1].us_vdd) {
-                               tmp_voltage_lookup_record = lookup_table->entries[j - 1];
-                               lookup_table->entries[j - 1] = lookup_table->entries[j];
-                               lookup_table->entries[j] = tmp_voltage_lookup_record;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_complete_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       int result = 0;
-       int tmp_result;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       tmp_result = fiji_patch_lookup_table_with_leakage(hwmgr,
-                       table_info->vddc_lookup_table, &(data->vddc_leakage));
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = fiji_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
-                       &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = fiji_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = fiji_calc_voltage_dependency_tables(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = fiji_calc_mm_voltage_dependency_table(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = fiji_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
-       if(tmp_result)
-               result = tmp_result;
-
-       return result;
-}
-
-static int fiji_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
-                       table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
-                       table_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table != NULL,
-               "VDD dependency on SCLK table is missing.       \
-               This table is mandatory", return -EINVAL);
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
-               "VDD dependency on SCLK table has to have is missing.   \
-               This table is mandatory", return -EINVAL);
-
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table != NULL,
-               "VDD dependency on MCLK table is missing.       \
-               This table is mandatory", return -EINVAL);
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
-               "VDD dependency on MCLK table has to have is missing.    \
-               This table is mandatory", return -EINVAL);
-
-       data->min_vddc_in_pptable = (uint16_t)allowed_sclk_vdd_table->entries[0].vddc;
-       data->max_vddc_in_pptable =     (uint16_t)allowed_sclk_vdd_table->
-                       entries[allowed_sclk_vdd_table->count - 1].vddc;
-
-       table_info->max_clock_voltage_on_ac.sclk =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
-       table_info->max_clock_voltage_on_ac.mclk =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
-       table_info->max_clock_voltage_on_ac.vddc =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
-       table_info->max_clock_voltage_on_ac.vddci =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
-
-       hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
-               table_info->max_clock_voltage_on_ac.sclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
-               table_info->max_clock_voltage_on_ac.mclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
-               table_info->max_clock_voltage_on_ac.vddc;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddci =
-               table_info->max_clock_voltage_on_ac.vddci;
-
-       return 0;
-}
-
-static uint16_t fiji_get_current_pcie_speed(struct pp_hwmgr *hwmgr)
-{
-       uint32_t speedCntl = 0;
-
-       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
-       speedCntl = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__PCIE,
-                       ixPCIE_LC_SPEED_CNTL);
-       return((uint16_t)PHM_GET_FIELD(speedCntl,
-                       PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
-}
-
-static int fiji_get_current_pcie_lane_number(struct pp_hwmgr *hwmgr)
-{
-       uint32_t link_width;
-
-       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
-       link_width = PHM_READ_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
-                       PCIE_LC_LINK_WIDTH_CNTL, LC_LINK_WIDTH_RD);
-
-       PP_ASSERT_WITH_CODE((7 >= link_width),
-                       "Invalid PCIe lane width!", return 0);
-
-       return decode_pcie_lane_width(link_width);
-}
-
-/** Patch the Boot State to match VBIOS boot clocks and voltage.
-*
-* @param hwmgr Pointer to the hardware manager.
-* @param pPowerState The address of the PowerState instance being created.
-*
-*/
-static int fiji_patch_boot_state(struct pp_hwmgr *hwmgr,
-               struct pp_hw_power_state *hw_ps)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_power_state *ps = (struct fiji_power_state *)hw_ps;
-       ATOM_FIRMWARE_INFO_V2_2 *fw_info;
-       uint16_t size;
-       uint8_t frev, crev;
-       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
-
-       /* First retrieve the Boot clocks and VDDC from the firmware info table.
-        * We assume here that fw_info is unchanged if this call fails.
-        */
-       fw_info = (ATOM_FIRMWARE_INFO_V2_2 *)cgs_atom_get_data_table(
-                       hwmgr->device, index,
-                       &size, &frev, &crev);
-       if (!fw_info)
-               /* During a test, there is no firmware info table. */
-               return 0;
-
-       /* Patch the state. */
-       data->vbios_boot_state.sclk_bootup_value =
-                       le32_to_cpu(fw_info->ulDefaultEngineClock);
-       data->vbios_boot_state.mclk_bootup_value =
-                       le32_to_cpu(fw_info->ulDefaultMemoryClock);
-       data->vbios_boot_state.mvdd_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpMVDDCVoltage);
-       data->vbios_boot_state.vddc_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpVDDCVoltage);
-       data->vbios_boot_state.vddci_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpVDDCIVoltage);
-       data->vbios_boot_state.pcie_gen_bootup_value =
-                       fiji_get_current_pcie_speed(hwmgr);
-       data->vbios_boot_state.pcie_lane_bootup_value =
-                       (uint16_t)fiji_get_current_pcie_lane_number(hwmgr);
-
-       /* set boot power state */
-       ps->performance_levels[0].memory_clock = data->vbios_boot_state.mclk_bootup_value;
-       ps->performance_levels[0].engine_clock = data->vbios_boot_state.sclk_bootup_value;
-       ps->performance_levels[0].pcie_gen = data->vbios_boot_state.pcie_gen_bootup_value;
-       ps->performance_levels[0].pcie_lane = data->vbios_boot_state.pcie_lane_bootup_value;
-
-       return 0;
-}
-
-static int fiji_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
-{
-       return phm_hwmgr_backend_fini(hwmgr);
-}
-
-static int fiji_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data;
-       uint32_t i;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       bool stay_in_boot;
-       int result;
-
-       data = kzalloc(sizeof(struct fiji_hwmgr), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       hwmgr->backend = data;
-
-       data->dll_default_on = false;
-       data->sram_end = SMC_RAM_END;
-
-       for (i = 0; i < SMU73_MAX_LEVELS_GRAPHICS; i++)
-               data->activity_target[i] = FIJI_AT_DFLT;
-
-       data->vddc_vddci_delta = VDDC_VDDCI_DELTA;
-
-       data->mclk_activity_target = PPFIJI_MCLK_TARGETACTIVITY_DFLT;
-       data->mclk_dpm0_activity_target = 0xa;
-
-       data->sclk_dpm_key_disabled = 0;
-       data->mclk_dpm_key_disabled = 0;
-       data->pcie_dpm_key_disabled = 0;
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_UnTabledHardwareInterface);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_TablelessHardwareInterface);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep);
-
-       data->gpio_debug = 0;
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DynamicPatchPowerState);
-
-       /* need to set voltage control types before EVV patching */
-       data->voltage_control = FIJI_VOLTAGE_CONTROL_NONE;
-       data->vddci_control = FIJI_VOLTAGE_CONTROL_NONE;
-       data->mvdd_control = FIJI_VOLTAGE_CONTROL_NONE;
-
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-
-       if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                       VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2))
-               data->voltage_control = FIJI_VOLTAGE_CONTROL_BY_SVID2;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableMVDDControl))
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT))
-                       data->mvdd_control = FIJI_VOLTAGE_CONTROL_BY_GPIO;
-
-       if (data->mvdd_control == FIJI_VOLTAGE_CONTROL_NONE)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableMVDDControl);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ControlVDDCI)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
-                       data->vddci_control = FIJI_VOLTAGE_CONTROL_BY_GPIO;
-               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
-                       data->vddci_control = FIJI_VOLTAGE_CONTROL_BY_SVID2;
-       }
-
-       if (data->vddci_control == FIJI_VOLTAGE_CONTROL_NONE)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ControlVDDCI);
-
-       if (table_info && table_info->cac_dtp_table->usClockStretchAmount)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ClockStretcher);
-
-       fiji_init_dpm_defaults(hwmgr);
-
-       /* Get leakage voltage based on leakage ID. */
-       fiji_get_evv_voltages(hwmgr);
-
-       /* Patch our voltage dependency table with actual leakage voltage
-        * We need to perform leakage translation before it's used by other functions
-        */
-       fiji_complete_dependency_tables(hwmgr);
-
-       /* Parse pptable data read from VBIOS */
-       fiji_set_private_data_based_on_pptable(hwmgr);
-
-       /* ULV Support */
-       data->ulv.ulv_supported = true; /* ULV feature is enabled by default */
-
-       /* Initalize Dynamic State Adjustment Rule Settings */
-       result = tonga_initializa_dynamic_state_adjustment_rule_settings(hwmgr);
-
-       if (!result) {
-               data->uvd_enabled = false;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_EnableSMU7ThermalManagement);
-               data->vddc_phase_shed_control = false;
-       }
-
-       stay_in_boot = phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StayInBootState);
-
-       if (0 == result) {
-               struct cgs_system_info sys_info = {0};
-
-               data->is_tlu_enabled = false;
-               hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
-                               FIJI_MAX_HARDWARE_POWERLEVELS;
-               hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
-               hwmgr->platform_descriptor.minimumClocksReductionPercentage  = 50;
-
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_FanSpeedInTableIsRPM);
-
-               if (table_info->cac_dtp_table->usDefaultTargetOperatingTemp &&
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ucFanControlMode) {
-                       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM =
-                                       hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
-                       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
-                                       hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM;
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempMinLimit =
-                                       table_info->cac_dtp_table->usOperatingTempMinLimit;
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempMaxLimit =
-                                       table_info->cac_dtp_table->usOperatingTempMaxLimit;
-                       hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp =
-                                       table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempStep =
-                                       table_info->cac_dtp_table->usOperatingTempStep;
-                       hwmgr->dyn_state.cac_dtp_table->usTargetOperatingTemp =
-                                       table_info->cac_dtp_table->usTargetOperatingTemp;
-
-                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_ODFuzzyFanControlSupport);
-               }
-
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_GEN_INFO;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_gen_cap = AMDGPU_DEFAULT_PCIE_GEN_MASK;
-               else
-                       data->pcie_gen_cap = (uint32_t)sys_info.value;
-               if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
-                       data->pcie_spc_cap = 20;
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_MLW;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_lane_cap = AMDGPU_DEFAULT_PCIE_MLW_MASK;
-               else
-                       data->pcie_lane_cap = (uint32_t)sys_info.value;
-       } else {
-               /* Ignore return value in here, we are cleaning up a mess. */
-               fiji_hwmgr_backend_fini(hwmgr);
-       }
-
-       return 0;
-}
-
-/**
- * Read clock related registers.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int fiji_read_clock_registers(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       data->clock_registers.vCG_SPLL_FUNC_CNTL =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_FUNC_CNTL);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_2 =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_FUNC_CNTL_2);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_3 =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_FUNC_CNTL_3);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_4 =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_FUNC_CNTL_4);
-       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_SPREAD_SPECTRUM);
-       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2 =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_SPLL_SPREAD_SPECTRUM_2);
-
-       return 0;
-}
-
-/**
- * Find out if memory is GDDR5.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int fiji_get_memory_type(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t temp;
-
-       temp = cgs_read_register(hwmgr->device, mmMC_SEQ_MISC0);
-
-       data->is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE ==
-                       ((temp & MC_SEQ_MISC0_GDDR5_MASK) >>
-                        MC_SEQ_MISC0_GDDR5_SHIFT));
-
-       return 0;
-}
-
-/**
- * Enables Dynamic Power Management by SMC
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int fiji_enable_acpi_power_management(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, STATIC_PM_EN, 1);
-
-       return 0;
-}
-
-/**
- * Initialize PowerGating States for different engines
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int fiji_init_power_gate_state(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-       data->samu_power_gated = false;
-       data->acp_power_gated = false;
-       data->pg_acp_init = true;
-
-       return 0;
-}
-
-static int fiji_init_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       data->low_sclk_interrupt_threshold = 0;
-
-       return 0;
-}
-
-static int fiji_setup_asic_task(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = fiji_read_clock_registers(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to read clock registers!", result = tmp_result);
-
-       tmp_result = fiji_get_memory_type(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to get memory type!", result = tmp_result);
-
-       tmp_result = fiji_enable_acpi_power_management(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable ACPI power management!", result = tmp_result);
-
-       tmp_result = fiji_init_power_gate_state(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to init power gate state!", result = tmp_result);
-
-       tmp_result = tonga_get_mc_microcode_version(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to get MC microcode version!", result = tmp_result);
-
-       tmp_result = fiji_init_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to init sclk threshold!", result = tmp_result);
-
-       return result;
-}
-
-/**
-* Checks if we want to support voltage control
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-*/
-static bool fiji_voltage_control(const struct pp_hwmgr *hwmgr)
-{
-       const struct fiji_hwmgr *data =
-                       (const struct fiji_hwmgr *)(hwmgr->backend);
-
-       return (FIJI_VOLTAGE_CONTROL_NONE != data->voltage_control);
-}
-
-/**
-* Enable voltage control
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int fiji_enable_voltage_control(struct pp_hwmgr *hwmgr)
-{
-       /* enable voltage control */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, VOLT_PWRMGT_EN, 1);
-
-       return 0;
-}
-
-/**
-* Remove repeated voltage values and create table with unique values.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    vol_table  the pointer to changing voltage table
-* @return    0 in success
-*/
-
-static int fiji_trim_voltage_table(struct pp_hwmgr *hwmgr,
-               struct pp_atomctrl_voltage_table *vol_table)
-{
-       uint32_t i, j;
-       uint16_t vvalue;
-       bool found = false;
-       struct pp_atomctrl_voltage_table *table;
-
-       PP_ASSERT_WITH_CODE((NULL != vol_table),
-                       "Voltage Table empty.", return -EINVAL);
-       table = kzalloc(sizeof(struct pp_atomctrl_voltage_table),
-                       GFP_KERNEL);
-
-       if (NULL == table)
-               return -ENOMEM;
-
-       table->mask_low = vol_table->mask_low;
-       table->phase_delay = vol_table->phase_delay;
-
-       for (i = 0; i < vol_table->count; i++) {
-               vvalue = vol_table->entries[i].value;
-               found = false;
-
-               for (j = 0; j < table->count; j++) {
-                       if (vvalue == table->entries[j].value) {
-                               found = true;
-                               break;
-                       }
-               }
-
-               if (!found) {
-                       table->entries[table->count].value = vvalue;
-                       table->entries[table->count].smio_low =
-                                       vol_table->entries[i].smio_low;
-                       table->count++;
-               }
-       }
-
-       memcpy(vol_table, table, sizeof(struct pp_atomctrl_voltage_table));
-       kfree(table);
-
-       return 0;
-}
-
-static int fiji_get_svi2_mvdd_voltage_table(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_clock_voltage_dependency_table *dep_table)
-{
-       uint32_t i;
-       int result;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct pp_atomctrl_voltage_table *vol_table = &(data->mvdd_voltage_table);
-
-       PP_ASSERT_WITH_CODE((0 != dep_table->count),
-                       "Voltage Dependency Table empty.", return -EINVAL);
-
-       vol_table->mask_low = 0;
-       vol_table->phase_delay = 0;
-       vol_table->count = dep_table->count;
-
-       for (i = 0; i < dep_table->count; i++) {
-               vol_table->entries[i].value = dep_table->entries[i].mvdd;
-               vol_table->entries[i].smio_low = 0;
-       }
-
-       result = fiji_trim_voltage_table(hwmgr, vol_table);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to trim MVDD table.", return result);
-
-       return 0;
-}
-
-static int fiji_get_svi2_vddci_voltage_table(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_clock_voltage_dependency_table *dep_table)
-{
-       uint32_t i;
-       int result;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct pp_atomctrl_voltage_table *vol_table = &(data->vddci_voltage_table);
-
-       PP_ASSERT_WITH_CODE((0 != dep_table->count),
-                       "Voltage Dependency Table empty.", return -EINVAL);
-
-       vol_table->mask_low = 0;
-       vol_table->phase_delay = 0;
-       vol_table->count = dep_table->count;
-
-       for (i = 0; i < dep_table->count; i++) {
-               vol_table->entries[i].value = dep_table->entries[i].vddci;
-               vol_table->entries[i].smio_low = 0;
-       }
-
-       result = fiji_trim_voltage_table(hwmgr, vol_table);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to trim VDDCI table.", return result);
-
-       return 0;
-}
-
-static int fiji_get_svi2_vdd_voltage_table(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *lookup_table)
-{
-       int i = 0;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct pp_atomctrl_voltage_table *vol_table = &(data->vddc_voltage_table);
-
-       PP_ASSERT_WITH_CODE((0 != lookup_table->count),
-                       "Voltage Lookup Table empty.", return -EINVAL);
-
-       vol_table->mask_low = 0;
-       vol_table->phase_delay = 0;
-
-       vol_table->count = lookup_table->count;
-
-       for (i = 0; i < vol_table->count; i++) {
-               vol_table->entries[i].value = lookup_table->entries[i].us_vdd;
-               vol_table->entries[i].smio_low = 0;
-       }
-
-       return 0;
-}
-
-/* ---- Voltage Tables ----
- * If the voltage table would be bigger than
- * what will fit into the state table on
- * the SMC keep only the higher entries.
- */
-static void fiji_trim_voltage_table_to_fit_state_table(struct pp_hwmgr *hwmgr,
-               uint32_t max_vol_steps, struct pp_atomctrl_voltage_table *vol_table)
-{
-       unsigned int i, diff;
-
-       if (vol_table->count <= max_vol_steps)
-               return;
-
-       diff = vol_table->count - max_vol_steps;
-
-       for (i = 0; i < max_vol_steps; i++)
-               vol_table->entries[i] = vol_table->entries[i + diff];
-
-       vol_table->count = max_vol_steps;
-
-       return;
-}
-
-/**
-* Create Voltage Tables.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int fiji_construct_voltage_tables(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)hwmgr->pptable;
-       int result;
-
-       if (FIJI_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                               VOLTAGE_TYPE_MVDDC,     VOLTAGE_OBJ_GPIO_LUT,
-                               &(data->mvdd_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve MVDD table.",
-                               return result);
-       } else if (FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
-               result = fiji_get_svi2_mvdd_voltage_table(hwmgr,
-                               table_info->vdd_dep_on_mclk);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 MVDD table from dependancy table.",
-                               return result;);
-       }
-
-       if (FIJI_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT,
-                               &(data->vddci_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve VDDCI table.",
-                               return result);
-       } else if (FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
-               result = fiji_get_svi2_vddci_voltage_table(hwmgr,
-                               table_info->vdd_dep_on_mclk);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 VDDCI table from dependancy table.",
-                               return result);
-       }
-
-       if(FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               result = fiji_get_svi2_vdd_voltage_table(hwmgr,
-                               table_info->vddc_lookup_table);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 VDDC table from lookup table.",
-                               return result);
-       }
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddc_voltage_table.count <= (SMU73_MAX_LEVELS_VDDC)),
-                       "Too many voltage values for VDDC. Trimming to fit state table.",
-                       fiji_trim_voltage_table_to_fit_state_table(hwmgr,
-                                       SMU73_MAX_LEVELS_VDDC, &(data->vddc_voltage_table)));
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddci_voltage_table.count <= (SMU73_MAX_LEVELS_VDDCI)),
-                       "Too many voltage values for VDDCI. Trimming to fit state table.",
-                       fiji_trim_voltage_table_to_fit_state_table(hwmgr,
-                                       SMU73_MAX_LEVELS_VDDCI, &(data->vddci_voltage_table)));
-
-       PP_ASSERT_WITH_CODE(
-                       (data->mvdd_voltage_table.count <= (SMU73_MAX_LEVELS_MVDD)),
-                       "Too many voltage values for MVDD. Trimming to fit state table.",
-                       fiji_trim_voltage_table_to_fit_state_table(hwmgr,
-                                       SMU73_MAX_LEVELS_MVDD, &(data->mvdd_voltage_table)));
-
-       return 0;
-}
-
-static int fiji_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
-{
-       /* Program additional LP registers
-        * that are no longer programmed by VBIOS
-        */
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP,
-                       cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
-
-       return 0;
-}
-
-/**
-* Programs static screed detection parameters
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int fiji_program_static_screen_threshold_parameters(
-               struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* Set static screen threshold unit */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD_UNIT,
-                       data->static_screen_threshold_unit);
-       /* Set static screen threshold */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD,
-                       data->static_screen_threshold);
-
-       return 0;
-}
-
-/**
-* Setup display gap for glitch free memory clock switching.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int fiji_enable_display_gap(struct pp_hwmgr *hwmgr)
-{
-       uint32_t displayGap =
-                       cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixCG_DISPLAY_GAP_CNTL);
-
-       displayGap = PHM_SET_FIELD(displayGap, CG_DISPLAY_GAP_CNTL,
-                       DISP_GAP, DISPLAY_GAP_IGNORE);
-
-       displayGap = PHM_SET_FIELD(displayGap, CG_DISPLAY_GAP_CNTL,
-                       DISP_GAP_MCHG, DISPLAY_GAP_VBLANK);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_DISPLAY_GAP_CNTL, displayGap);
-
-       return 0;
-}
-
-/**
-* Programs activity state transition voting clients
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int fiji_program_voting_clients(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* Clear reset for voting clients before enabling DPM */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 0);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 0);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_0, data->voting_rights_clients0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_1, data->voting_rights_clients1);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_2, data->voting_rights_clients2);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_3, data->voting_rights_clients3);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_4, data->voting_rights_clients4);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_5, data->voting_rights_clients5);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_6, data->voting_rights_clients6);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_7, data->voting_rights_clients7);
-
-       return 0;
-}
-
-static int fiji_clear_voting_clients(struct pp_hwmgr *hwmgr)
-{
-       /* Reset voting clients before disabling DPM */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 1);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 1);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_0, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_1, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_2, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_3, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_4, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_5, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_6, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_7, 0);
-
-       return 0;
-}
-
-/**
-* Get the location of various tables inside the FW image.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int fiji_process_firmware_header(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
-       uint32_t tmp;
-       int result;
-       bool error = false;
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, DpmTable),
-                       &tmp, data->sram_end);
-
-       if (0 == result)
-               data->dpm_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, SoftRegisters),
-                       &tmp, data->sram_end);
-
-       if (!result) {
-               data->soft_regs_start = tmp;
-               smu_data->soft_regs_start = tmp;
-       }
-
-       error |= (0 != result);
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, mcRegisterTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->mc_reg_table_start = tmp;
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, FanTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->fan_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, mcArbDramTimingTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->arb_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU73_Firmware_Header, Version),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               hwmgr->microcode_version_info.SMC = tmp;
-
-       error |= (0 != result);
-
-       return error ? -1 : 0;
-}
-
-/* Copy one arb setting to another and then switch the active set.
- * arb_src and arb_dest is one of the MC_CG_ARB_FREQ_Fx constants.
- */
-static int fiji_copy_and_switch_arb_sets(struct pp_hwmgr *hwmgr,
-               uint32_t arb_src, uint32_t arb_dest)
-{
-       uint32_t mc_arb_dram_timing;
-       uint32_t mc_arb_dram_timing2;
-       uint32_t burst_time;
-       uint32_t mc_cg_config;
-
-       switch (arb_src) {
-       case MC_CG_ARB_FREQ_F0:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
-               break;
-       case MC_CG_ARB_FREQ_F1:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (arb_dest) {
-       case MC_CG_ARB_FREQ_F0:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0, burst_time);
-               break;
-       case MC_CG_ARB_FREQ_F1:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1, burst_time);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       mc_cg_config = cgs_read_register(hwmgr->device, mmMC_CG_CONFIG);
-       mc_cg_config |= 0x0000000F;
-       cgs_write_register(hwmgr->device, mmMC_CG_CONFIG, mc_cg_config);
-       PHM_WRITE_FIELD(hwmgr->device, MC_ARB_CG, CG_ARB_REQ, arb_dest);
-
-       return 0;
-}
-
-/**
-* Call SMC to reset S0/S1 to S1 and Reset SMIO to initial value
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   if success then 0;
-*/
-static int fiji_reset_to_default(struct pp_hwmgr *hwmgr)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_ResetToDefaults);
-}
-
-/**
-* Initial switch from ARB F0->F1
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-* This function is to be called from the SetPowerState table.
-*/
-static int fiji_initial_switch_from_arbf0_to_f1(struct pp_hwmgr *hwmgr)
-{
-       return fiji_copy_and_switch_arb_sets(hwmgr,
-                       MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
-}
-
-static int fiji_force_switch_to_arbf0(struct pp_hwmgr *hwmgr)
-{
-       uint32_t tmp;
-
-       tmp = (cgs_read_ind_register(hwmgr->device,
-                       CGS_IND_REG__SMC, ixSMC_SCRATCH9) &
-                       0x0000ff00) >> 8;
-
-       if (tmp == MC_CG_ARB_FREQ_F0)
-               return 0;
-
-       return fiji_copy_and_switch_arb_sets(hwmgr,
-                       tmp, MC_CG_ARB_FREQ_F0);
-}
-
-static int fiji_reset_single_dpm_table(struct pp_hwmgr *hwmgr,
-               struct fiji_single_dpm_table *dpm_table, uint32_t count)
-{
-       int i;
-       PP_ASSERT_WITH_CODE(count <= MAX_REGULAR_DPM_NUMBER,
-                       "Fatal error, can not set up single DPM table entries "
-                       "to exceed max number!",);
-
-       dpm_table->count = count;
-       for (i = 0; i < MAX_REGULAR_DPM_NUMBER; i++)
-               dpm_table->dpm_levels[i].enabled = false;
-
-       return 0;
-}
-
-static void fiji_setup_pcie_table_entry(
-       struct fiji_single_dpm_table *dpm_table,
-       uint32_t index, uint32_t pcie_gen,
-       uint32_t pcie_lanes)
-{
-       dpm_table->dpm_levels[index].value = pcie_gen;
-       dpm_table->dpm_levels[index].param1 = pcie_lanes;
-       dpm_table->dpm_levels[index].enabled = true;
-}
-
-static int fiji_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
-       uint32_t i, max_entry;
-
-       PP_ASSERT_WITH_CODE((data->use_pcie_performance_levels ||
-                       data->use_pcie_power_saving_levels), "No pcie performance levels!",
-                       return -EINVAL);
-
-       if (data->use_pcie_performance_levels &&
-                       !data->use_pcie_power_saving_levels) {
-               data->pcie_gen_power_saving = data->pcie_gen_performance;
-               data->pcie_lane_power_saving = data->pcie_lane_performance;
-       } else if (!data->use_pcie_performance_levels &&
-                       data->use_pcie_power_saving_levels) {
-               data->pcie_gen_performance = data->pcie_gen_power_saving;
-               data->pcie_lane_performance = data->pcie_lane_power_saving;
-       }
-
-       fiji_reset_single_dpm_table(hwmgr,
-                       &data->dpm_table.pcie_speed_table, SMU73_MAX_LEVELS_LINK);
-
-       if (pcie_table != NULL) {
-               /* max_entry is used to make sure we reserve one PCIE level
-                * for boot level (fix for A+A PSPP issue).
-                * If PCIE table from PPTable have ULV entry + 8 entries,
-                * then ignore the last entry.*/
-               max_entry = (SMU73_MAX_LEVELS_LINK < pcie_table->count) ?
-                               SMU73_MAX_LEVELS_LINK : pcie_table->count;
-               for (i = 1; i < max_entry; i++) {
-                       fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i - 1,
-                                       get_pcie_gen_support(data->pcie_gen_cap,
-                                                       pcie_table->entries[i].gen_speed),
-                                       get_pcie_lane_support(data->pcie_lane_cap,
-                                                       pcie_table->entries[i].lane_width));
-               }
-               data->dpm_table.pcie_speed_table.count = max_entry - 1;
-       } else {
-               /* Hardcode Pcie Table */
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 0,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Min_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 1,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Min_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 2,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 3,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 4,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 5,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-
-               data->dpm_table.pcie_speed_table.count = 6;
-       }
-       /* Populate last level for boot PCIE level, but do not increment count. */
-       fiji_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
-                       data->dpm_table.pcie_speed_table.count,
-                       get_pcie_gen_support(data->pcie_gen_cap,
-                                       PP_Min_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap,
-                                       PP_Max_PCIELane));
-
-       return 0;
-}
-
-/*
- * This function is to initalize all DPM state tables
- * for SMU7 based on the dependency table.
- * Dynamic state patching function will then trim these
- * state tables to the allowed range based
- * on the power policy or external client requests,
- * such as UVD request, etc.
- */
-static int fiji_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i;
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                       table_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(dep_sclk_table != NULL,
-                       "SCLK dependency table is missing. This table is mandatory",
-                       return -EINVAL);
-       PP_ASSERT_WITH_CODE(dep_sclk_table->count >= 1,
-                       "SCLK dependency table has to have is missing. "
-                       "This table is mandatory",
-                       return -EINVAL);
-
-       PP_ASSERT_WITH_CODE(dep_mclk_table != NULL,
-                       "MCLK dependency table is missing. This table is mandatory",
-                       return -EINVAL);
-       PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
-                       "MCLK dependency table has to have is missing. "
-                       "This table is mandatory",
-                       return -EINVAL);
-
-       /* clear the state table to reset everything to default */
-       fiji_reset_single_dpm_table(hwmgr,
-                       &data->dpm_table.sclk_table, SMU73_MAX_LEVELS_GRAPHICS);
-       fiji_reset_single_dpm_table(hwmgr,
-                       &data->dpm_table.mclk_table, SMU73_MAX_LEVELS_MEMORY);
-
-       /* Initialize Sclk DPM table based on allow Sclk values */
-       data->dpm_table.sclk_table.count = 0;
-       for (i = 0; i < dep_sclk_table->count; i++) {
-               if (i == 0 || data->dpm_table.sclk_table.dpm_levels
-                               [data->dpm_table.sclk_table.count - 1].value !=
-                                               dep_sclk_table->entries[i].clk) {
-                       data->dpm_table.sclk_table.dpm_levels
-                       [data->dpm_table.sclk_table.count].value =
-                                       dep_sclk_table->entries[i].clk;
-                       data->dpm_table.sclk_table.dpm_levels
-                       [data->dpm_table.sclk_table.count].enabled =
-                                       (i == 0) ? true : false;
-                       data->dpm_table.sclk_table.count++;
-               }
-       }
-
-       /* Initialize Mclk DPM table based on allow Mclk values */
-       data->dpm_table.mclk_table.count = 0;
-       for (i=0; i<dep_mclk_table->count; i++) {
-               if ( i==0 || data->dpm_table.mclk_table.dpm_levels
-                               [data->dpm_table.mclk_table.count - 1].value !=
-                                               dep_mclk_table->entries[i].clk) {
-                       data->dpm_table.mclk_table.dpm_levels
-                       [data->dpm_table.mclk_table.count].value =
-                                       dep_mclk_table->entries[i].clk;
-                       data->dpm_table.mclk_table.dpm_levels
-                       [data->dpm_table.mclk_table.count].enabled =
-                                       (i == 0) ? true : false;
-                       data->dpm_table.mclk_table.count++;
-               }
-       }
-
-       /* setup PCIE gen speed levels */
-       fiji_setup_default_pcie_table(hwmgr);
-
-       /* save a copy of the default DPM table */
-       memcpy(&(data->golden_dpm_table), &(data->dpm_table),
-                       sizeof(struct fiji_dpm_table));
-
-       return 0;
-}
-
-/**
- * @brief PhwFiji_GetVoltageOrder
- *  Returns index of requested voltage record in lookup(table)
- * @param lookup_table - lookup list to search in
- * @param voltage - voltage to look for
- * @return 0 on success
- */
-uint8_t fiji_get_voltage_index(
-               struct phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t voltage)
-{
-       uint8_t count = (uint8_t) (lookup_table->count);
-       uint8_t i;
-
-       PP_ASSERT_WITH_CODE((NULL != lookup_table),
-                       "Lookup Table empty.", return 0);
-       PP_ASSERT_WITH_CODE((0 != count),
-                       "Lookup Table empty.", return 0);
-
-       for (i = 0; i < lookup_table->count; i++) {
-               /* find first voltage equal or bigger than requested */
-               if (lookup_table->entries[i].us_vdd >= voltage)
-                       return i;
-       }
-       /* voltage is bigger than max voltage in the table */
-       return i - 1;
-}
-
-/**
-* Preparation of vddc and vddgfx CAC tables for SMC.
-*
-* @param    hwmgr  the address of the hardware manager
-* @param    table  the SMC DPM table structure to be populated
-* @return   always 0
-*/
-static int fiji_populate_cac_table(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       uint32_t count;
-       uint8_t index;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
-                       table_info->vddc_lookup_table;
-       /* tables is already swapped, so in order to use the value from it,
-        * we need to swap it back.
-        * We are populating vddc CAC data to BapmVddc table
-        * in split and merged mode
-        */
-       for( count = 0; count<lookup_table->count; count++) {
-               index = fiji_get_voltage_index(lookup_table,
-                               data->vddc_voltage_table.entries[count].value);
-               table->BapmVddcVidLoSidd[count] = (uint8_t) ((6200 -
-                               (lookup_table->entries[index].us_cac_low *
-                                               VOLTAGE_SCALE)) / 25);
-               table->BapmVddcVidHiSidd[count] = (uint8_t) ((6200 -
-                               (lookup_table->entries[index].us_cac_high *
-                                               VOLTAGE_SCALE)) / 25);
-       }
-
-       return 0;
-}
-
-/**
-* Preparation of voltage tables for SMC.
-*
-* @param    hwmgr   the address of the hardware manager
-* @param    table   the SMC DPM table structure to be populated
-* @return   always  0
-*/
-
-int fiji_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       int result;
-
-       result = fiji_populate_cac_table(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate CAC voltage tables to SMC",
-                       return -EINVAL);
-
-       return 0;
-}
-
-static int fiji_populate_ulv_level(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_Ulv *state)
-{
-       int result = 0;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       state->CcPwrDynRm = 0;
-       state->CcPwrDynRm1 = 0;
-
-       state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
-       state->VddcOffsetVid = (uint8_t)( table_info->us_ulv_voltage_offset *
-                       VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1 );
-
-       state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1;
-
-       if (!result) {
-               CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
-               CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
-               CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
-       }
-       return result;
-}
-
-static int fiji_populate_ulv_state(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       return fiji_populate_ulv_level(hwmgr, &table->Ulv);
-}
-
-static int32_t fiji_get_dpm_level_enable_mask_value(
-               struct fiji_single_dpm_table* dpm_table)
-{
-       int32_t i;
-       int32_t mask = 0;
-
-       for (i = dpm_table->count; i > 0; i--) {
-               mask = mask << 1;
-               if (dpm_table->dpm_levels[i - 1].enabled)
-                       mask |= 0x1;
-               else
-                       mask &= 0xFFFFFFFE;
-       }
-       return mask;
-}
-
-static int fiji_populate_smc_link_level(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_dpm_table *dpm_table = &data->dpm_table;
-       int i;
-
-       /* Index (dpm_table->pcie_speed_table.count)
-        * is reserved for PCIE boot level. */
-       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
-               table->LinkLevel[i].PcieGenSpeed  =
-                               (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
-               table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
-                               dpm_table->pcie_speed_table.dpm_levels[i].param1);
-               table->LinkLevel[i].EnabledForActivity = 1;
-               table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
-               table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
-               table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
-       }
-
-       data->smc_state_table.LinkLevelCount =
-                       (uint8_t)dpm_table->pcie_speed_table.count;
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
-
-       return 0;
-}
-
-/**
-* Calculates the SCLK dividers using the provided engine clock
-*
-* @param    hwmgr  the address of the hardware manager
-* @param    clock  the engine clock to use to populate the structure
-* @param    sclk   the SMC SCLK structure to be populated
-*/
-static int fiji_calculate_sclk_params(struct pp_hwmgr *hwmgr,
-               uint32_t clock, struct SMU73_Discrete_GraphicsLevel *sclk)
-{
-       const struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       uint32_t spll_func_cntl            = data->clock_registers.vCG_SPLL_FUNC_CNTL;
-       uint32_t spll_func_cntl_3          = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
-       uint32_t spll_func_cntl_4          = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
-       uint32_t cg_spll_spread_spectrum   = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
-       uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
-       uint32_t ref_clock;
-       uint32_t ref_divider;
-       uint32_t fbdiv;
-       int result;
-
-       /* get the engine clock dividers for this clock value */
-       result = atomctrl_get_engine_pll_dividers_vi(hwmgr, clock,  &dividers);
-
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "Error retrieving Engine Clock dividers from VBIOS.",
-                       return result);
-
-       /* To get FBDIV we need to multiply this by 16384 and divide it by Fref. */
-       ref_clock = atomctrl_get_reference_clock(hwmgr);
-       ref_divider = 1 + dividers.uc_pll_ref_div;
-
-       /* low 14 bits is fraction and high 12 bits is divider */
-       fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
-
-       /* SPLL_FUNC_CNTL setup */
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
-                       SPLL_REF_DIV, dividers.uc_pll_ref_div);
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
-                       SPLL_PDIV_A,  dividers.uc_pll_post_div);
-
-       /* SPLL_FUNC_CNTL_3 setup*/
-       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
-                       SPLL_FB_DIV, fbdiv);
-
-       /* set to use fractional accumulation*/
-       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
-                       SPLL_DITHEN, 1);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
-               struct pp_atomctrl_internal_ss_info ssInfo;
-
-               uint32_t vco_freq = clock * dividers.uc_pll_post_div;
-               if (!atomctrl_get_engine_clock_spread_spectrum(hwmgr,
-                               vco_freq, &ssInfo)) {
-                       /*
-                        * ss_info.speed_spectrum_percentage -- in unit of 0.01%
-                        * ss_info.speed_spectrum_rate -- in unit of khz
-                        *
-                        * clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2
-                        */
-                       uint32_t clk_s = ref_clock * 5 /
-                                       (ref_divider * ssInfo.speed_spectrum_rate);
-                       /* clkv = 2 * D * fbdiv / NS */
-                       uint32_t clk_v = 4 * ssInfo.speed_spectrum_percentage *
-                                       fbdiv / (clk_s * 10000);
-
-                       cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
-                                       CG_SPLL_SPREAD_SPECTRUM, CLKS, clk_s);
-                       cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
-                                       CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
-                       cg_spll_spread_spectrum_2 = PHM_SET_FIELD(cg_spll_spread_spectrum_2,
-                                       CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clk_v);
-               }
-       }
-
-       sclk->SclkFrequency        = clock;
-       sclk->CgSpllFuncCntl3      = spll_func_cntl_3;
-       sclk->CgSpllFuncCntl4      = spll_func_cntl_4;
-       sclk->SpllSpreadSpectrum   = cg_spll_spread_spectrum;
-       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
-       sclk->SclkDid              = (uint8_t)dividers.pll_post_divider;
-
-       return 0;
-}
-
-static uint16_t fiji_find_closest_vddci(struct pp_hwmgr *hwmgr, uint16_t vddci)
-{
-       uint32_t  i;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct pp_atomctrl_voltage_table *vddci_table =
-                       &(data->vddci_voltage_table);
-
-       for (i = 0; i < vddci_table->count; i++) {
-               if (vddci_table->entries[i].value >= vddci)
-                       return vddci_table->entries[i].value;
-       }
-
-       PP_ASSERT_WITH_CODE(false,
-                       "VDDCI is larger than max VDDCI in VDDCI Voltage Table!",
-                       return vddci_table->entries[i-1].value);
-}
-
-static int fiji_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
-               struct phm_ppt_v1_clock_voltage_dependency_table* dep_table,
-               uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
-{
-       uint32_t i;
-       uint16_t vddci;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       *voltage = *mvdd = 0;
-
-       /* clock - voltage dependency table is empty table */
-       if (dep_table->count == 0)
-               return -EINVAL;
-
-       for (i = 0; i < dep_table->count; i++) {
-               /* find first sclk bigger than request */
-               if (dep_table->entries[i].clk >= clock) {
-                       *voltage |= (dep_table->entries[i].vddc *
-                                       VOLTAGE_SCALE) << VDDC_SHIFT;
-                       if (FIJI_VOLTAGE_CONTROL_NONE == data->vddci_control)
-                               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-                       else if (dep_table->entries[i].vddci)
-                               *voltage |= (dep_table->entries[i].vddci *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-                       else {
-                               vddci = fiji_find_closest_vddci(hwmgr,
-                                               (dep_table->entries[i].vddc -
-                                                               (uint16_t)data->vddc_vddci_delta));
-                               *voltage |= (vddci * VOLTAGE_SCALE) <<  VDDCI_SHIFT;
-                       }
-
-                       if (FIJI_VOLTAGE_CONTROL_NONE == data->mvdd_control)
-                               *mvdd = data->vbios_boot_state.mvdd_bootup_value *
-                                       VOLTAGE_SCALE;
-                       else if (dep_table->entries[i].mvdd)
-                               *mvdd = (uint32_t) dep_table->entries[i].mvdd *
-                                       VOLTAGE_SCALE;
-
-                       *voltage |= 1 << PHASES_SHIFT;
-                       return 0;
-               }
-       }
-
-       /* sclk is bigger than max sclk in the dependence table */
-       *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
-
-       if (FIJI_VOLTAGE_CONTROL_NONE == data->vddci_control)
-               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
-                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-       else if (dep_table->entries[i-1].vddci) {
-               vddci = fiji_find_closest_vddci(hwmgr,
-                               (dep_table->entries[i].vddc -
-                                               (uint16_t)data->vddc_vddci_delta));
-               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-       }
-
-       if (FIJI_VOLTAGE_CONTROL_NONE == data->mvdd_control)
-               *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
-       else if (dep_table->entries[i].mvdd)
-               *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
-
-       return 0;
-}
-
-static uint8_t fiji_get_sleep_divider_id_from_clock(uint32_t clock,
-               uint32_t clock_insr)
-{
-       uint8_t i;
-       uint32_t temp;
-       uint32_t min = max(clock_insr, (uint32_t)FIJI_MINIMUM_ENGINE_CLOCK);
-
-       PP_ASSERT_WITH_CODE((clock >= min), "Engine clock can't satisfy stutter requirement!", return 0);
-       for (i = FIJI_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
-               temp = clock >> i;
-
-               if (temp >= min || i == 0)
-                       break;
-       }
-       return i;
-}
-/**
-* Populates single SMC SCLK structure using the provided engine clock
-*
-* @param    hwmgr      the address of the hardware manager
-* @param    clock the engine clock to use to populate the structure
-* @param    sclk        the SMC SCLK structure to be populated
-*/
-
-static int fiji_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
-               uint32_t clock, uint16_t sclk_al_threshold,
-               struct SMU73_Discrete_GraphicsLevel *level)
-{
-       int result;
-       /* PP_Clocks minClocks; */
-       uint32_t threshold, mvdd;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       result = fiji_calculate_sclk_params(hwmgr, clock, level);
-
-       /* populate graphics levels */
-       result = fiji_get_dependency_volt_by_clk(hwmgr,
-                       table_info->vdd_dep_on_sclk, clock,
-                       &level->MinVoltage, &mvdd);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "can not find VDDC voltage value for "
-                       "VDDC engine clock dependency table",
-                       return result);
-
-       level->SclkFrequency = clock;
-       level->ActivityLevel = sclk_al_threshold;
-       level->CcPwrDynRm = 0;
-       level->CcPwrDynRm1 = 0;
-       level->EnabledForActivity = 0;
-       level->EnabledForThrottle = 1;
-       level->UpHyst = 10;
-       level->DownHyst = 0;
-       level->VoltageDownHyst = 0;
-       level->PowerThrottle = 0;
-
-       threshold = clock * data->fast_watermark_threshold / 100;
-
-
-       data->display_timing.min_clock_in_sr = hwmgr->display_config.min_core_set_clock_in_sr;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
-               level->DeepSleepDivId = fiji_get_sleep_divider_id_from_clock(clock,
-                                                               hwmgr->display_config.min_core_set_clock_in_sr);
-
-
-       /* Default to slow, highest DPM level will be
-        * set to PPSMC_DISPLAY_WATERMARK_LOW later.
-        */
-       level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-
-       CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->SclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl3);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl4);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum2);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
-
-       return 0;
-}
-/**
-* Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
-*
-* @param    hwmgr      the address of the hardware manager
-*/
-static int fiji_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_dpm_table *dpm_table = &data->dpm_table;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
-       uint8_t pcie_entry_cnt = (uint8_t) data->dpm_table.pcie_speed_table.count;
-       int result = 0;
-       uint32_t array = data->dpm_table_start +
-                       offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
-       uint32_t array_size = sizeof(struct SMU73_Discrete_GraphicsLevel) *
-                       SMU73_MAX_LEVELS_GRAPHICS;
-       struct SMU73_Discrete_GraphicsLevel *levels =
-                       data->smc_state_table.GraphicsLevel;
-       uint32_t i, max_entry;
-       uint8_t hightest_pcie_level_enabled = 0,
-                       lowest_pcie_level_enabled = 0,
-                       mid_pcie_level_enabled = 0,
-                       count = 0;
-
-       for (i = 0; i < dpm_table->sclk_table.count; i++) {
-               result = fiji_populate_single_graphic_level(hwmgr,
-                               dpm_table->sclk_table.dpm_levels[i].value,
-                               (uint16_t)data->activity_target[i],
-                               &levels[i]);
-               if (result)
-                       return result;
-
-               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
-               if (i > 1)
-                       levels[i].DeepSleepDivId = 0;
-       }
-
-       /* Only enable level 0 for now.*/
-       levels[0].EnabledForActivity = 1;
-
-       /* set highest level watermark to high */
-       levels[dpm_table->sclk_table.count - 1].DisplayWatermark =
-                       PPSMC_DISPLAY_WATERMARK_HIGH;
-
-       data->smc_state_table.GraphicsDpmLevelCount =
-                       (uint8_t)dpm_table->sclk_table.count;
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
-
-       if (pcie_table != NULL) {
-               PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
-                               "There must be 1 or more PCIE levels defined in PPTable.",
-                               return -EINVAL);
-               max_entry = pcie_entry_cnt - 1;
-               for (i = 0; i < dpm_table->sclk_table.count; i++)
-                       levels[i].pcieDpmLevel =
-                                       (uint8_t) ((i < max_entry)? i : max_entry);
-       } else {
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << (hightest_pcie_level_enabled + 1))) != 0 ))
-                       hightest_pcie_level_enabled++;
-
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << lowest_pcie_level_enabled)) == 0 ))
-                       lowest_pcie_level_enabled++;
-
-               while ((count < hightest_pcie_level_enabled) &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << (lowest_pcie_level_enabled + 1 + count))) == 0 ))
-                       count++;
-
-               mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1+ count) <
-                               hightest_pcie_level_enabled?
-                                               (lowest_pcie_level_enabled + 1 + count) :
-                                               hightest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to hightest_pcie_level_enabled */
-               for(i = 2; i < dpm_table->sclk_table.count; i++)
-                       levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to lowest_pcie_level_enabled */
-               levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to mid_pcie_level_enabled */
-               levels[1].pcieDpmLevel = mid_pcie_level_enabled;
-       }
-       /* level count will send to smc once at init smc table and never change */
-       result = fiji_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
-                       (uint32_t)array_size, data->sram_end);
-
-       return result;
-}
-
-/**
- * MCLK Frequency Ratio
- * SEQ_CG_RESP  Bit[31:24] - 0x0
- * Bit[27:24] \96 DDR3 Frequency ratio
- * 0x0 <= 100MHz,       450 < 0x8 <= 500MHz
- * 100 < 0x1 <= 150MHz,       500 < 0x9 <= 550MHz
- * 150 < 0x2 <= 200MHz,       550 < 0xA <= 600MHz
- * 200 < 0x3 <= 250MHz,       600 < 0xB <= 650MHz
- * 250 < 0x4 <= 300MHz,       650 < 0xC <= 700MHz
- * 300 < 0x5 <= 350MHz,       700 < 0xD <= 750MHz
- * 350 < 0x6 <= 400MHz,       750 < 0xE <= 800MHz
- * 400 < 0x7 <= 450MHz,       800 < 0xF
- */
-static uint8_t fiji_get_mclk_frequency_ratio(uint32_t mem_clock)
-{
-       if (mem_clock <= 10000) return 0x0;
-       if (mem_clock <= 15000) return 0x1;
-       if (mem_clock <= 20000) return 0x2;
-       if (mem_clock <= 25000) return 0x3;
-       if (mem_clock <= 30000) return 0x4;
-       if (mem_clock <= 35000) return 0x5;
-       if (mem_clock <= 40000) return 0x6;
-       if (mem_clock <= 45000) return 0x7;
-       if (mem_clock <= 50000) return 0x8;
-       if (mem_clock <= 55000) return 0x9;
-       if (mem_clock <= 60000) return 0xa;
-       if (mem_clock <= 65000) return 0xb;
-       if (mem_clock <= 70000) return 0xc;
-       if (mem_clock <= 75000) return 0xd;
-       if (mem_clock <= 80000) return 0xe;
-       /* mem_clock > 800MHz */
-       return 0xf;
-}
-
-/**
-* Populates the SMC MCLK structure using the provided memory clock
-*
-* @param    hwmgr   the address of the hardware manager
-* @param    clock   the memory clock to use to populate the structure
-* @param    sclk    the SMC SCLK structure to be populated
-*/
-static int fiji_calculate_mclk_params(struct pp_hwmgr *hwmgr,
-    uint32_t clock, struct SMU73_Discrete_MemoryLevel *mclk)
-{
-       struct pp_atomctrl_memory_clock_param mem_param;
-       int result;
-
-       result = atomctrl_get_memory_pll_dividers_vi(hwmgr, clock, &mem_param);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to get Memory PLL Dividers.",);
-
-       /* Save the result data to outpupt memory level structure */
-       mclk->MclkFrequency   = clock;
-       mclk->MclkDivider     = (uint8_t)mem_param.mpll_post_divider;
-       mclk->FreqRange       = fiji_get_mclk_frequency_ratio(clock);
-
-       return result;
-}
-
-static int fiji_populate_single_memory_level(struct pp_hwmgr *hwmgr,
-               uint32_t clock, struct SMU73_Discrete_MemoryLevel *mem_level)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int result = 0;
-
-       if (table_info->vdd_dep_on_mclk) {
-               result = fiji_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_mclk, clock,
-                               &mem_level->MinVoltage, &mem_level->MinMvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find MinVddc voltage value from memory "
-                               "VDDC voltage dependency table", return result);
-       }
-
-       mem_level->EnabledForThrottle = 1;
-       mem_level->EnabledForActivity = 0;
-       mem_level->UpHyst = 0;
-       mem_level->DownHyst = 100;
-       mem_level->VoltageDownHyst = 0;
-       mem_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
-       mem_level->StutterEnable = false;
-
-       mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-
-       /* enable stutter mode if all the follow condition applied
-        * PECI_GetNumberOfActiveDisplays(hwmgr->pPECI,
-        * &(data->DisplayTiming.numExistingDisplays));
-        */
-       data->display_timing.num_existing_displays = 1;
-
-       if ((data->mclk_stutter_mode_threshold) &&
-               (clock <= data->mclk_stutter_mode_threshold) &&
-               (!data->is_uvd_enabled) &&
-               (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
-                               STUTTER_ENABLE) & 0x1))
-               mem_level->StutterEnable = true;
-
-       result = fiji_calculate_mclk_params(hwmgr, clock, mem_level);
-       if (!result) {
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
-       }
-       return result;
-}
-
-/**
-* Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
-*
-* @param    hwmgr      the address of the hardware manager
-*/
-static int fiji_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_dpm_table *dpm_table = &data->dpm_table;
-       int result;
-       /* populate MCLK dpm table to SMU7 */
-       uint32_t array = data->dpm_table_start +
-                       offsetof(SMU73_Discrete_DpmTable, MemoryLevel);
-       uint32_t array_size = sizeof(SMU73_Discrete_MemoryLevel) *
-                       SMU73_MAX_LEVELS_MEMORY;
-       struct SMU73_Discrete_MemoryLevel *levels =
-                       data->smc_state_table.MemoryLevel;
-       uint32_t i;
-
-       for (i = 0; i < dpm_table->mclk_table.count; i++) {
-               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
-                               "can not populate memory level as memory clock is zero",
-                               return -EINVAL);
-               result = fiji_populate_single_memory_level(hwmgr,
-                               dpm_table->mclk_table.dpm_levels[i].value,
-                               &levels[i]);
-               if (result)
-                       return result;
-       }
-
-       /* Only enable level 0 for now. */
-       levels[0].EnabledForActivity = 1;
-
-       /* in order to prevent MC activity from stutter mode to push DPM up.
-        * the UVD change complements this by putting the MCLK in
-        * a higher state by default such that we are not effected by
-        * up threshold or and MCLK DPM latency.
-        */
-       levels[0].ActivityLevel = (uint16_t)data->mclk_dpm0_activity_target;
-       CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
-
-       data->smc_state_table.MemoryDpmLevelCount =
-                       (uint8_t)dpm_table->mclk_table.count;
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
-       /* set highest level watermark to high */
-       levels[dpm_table->mclk_table.count - 1].DisplayWatermark =
-                       PPSMC_DISPLAY_WATERMARK_HIGH;
-
-       /* level count will send to smc once at init smc table and never change */
-       result = fiji_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
-                       (uint32_t)array_size, data->sram_end);
-
-       return result;
-}
-
-/**
-* Populates the SMC MVDD structure using the provided memory clock.
-*
-* @param    hwmgr      the address of the hardware manager
-* @param    mclk        the MCLK value to be used in the decision if MVDD should be high or low.
-* @param    voltage     the SMC VOLTAGE structure to be populated
-*/
-int fiji_populate_mvdd_value(struct pp_hwmgr *hwmgr,
-               uint32_t mclk, SMIO_Pattern *smio_pat)
-{
-       const struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i = 0;
-
-       if (FIJI_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
-               /* find mvdd value which clock is more than request */
-               for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
-                       if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
-                               smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
-                               break;
-                       }
-               }
-               PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
-                               "MVDD Voltage is outside the supported range.",
-                               return -EINVAL);
-       } else
-               return -EINVAL;
-
-       return 0;
-}
-
-static int fiji_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
-               SMU73_Discrete_DpmTable *table)
-{
-       int result = 0;
-       const struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       SMIO_Pattern vol_level;
-       uint32_t mvdd;
-       uint16_t us_mvdd;
-       uint32_t spll_func_cntl    = data->clock_registers.vCG_SPLL_FUNC_CNTL;
-       uint32_t spll_func_cntl_2  = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
-
-       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
-
-       if (!data->sclk_dpm_key_disabled) {
-               /* Get MinVoltage and Frequency from DPM0,
-                * already converted to SMC_UL */
-               table->ACPILevel.SclkFrequency =
-                               data->dpm_table.sclk_table.dpm_levels[0].value;
-               result = fiji_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_sclk,
-                               table->ACPILevel.SclkFrequency,
-                               &table->ACPILevel.MinVoltage, &mvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Cannot find ACPI VDDC voltage value "
-                               "in Clock Dependency Table",);
-       } else {
-               table->ACPILevel.SclkFrequency =
-                               data->vbios_boot_state.sclk_bootup_value;
-               table->ACPILevel.MinVoltage =
-                               data->vbios_boot_state.vddc_bootup_value * VOLTAGE_SCALE;
-       }
-
-       /* get the engine clock dividers for this clock value */
-       result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
-                       table->ACPILevel.SclkFrequency,  &dividers);
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "Error retrieving Engine Clock dividers from VBIOS.",
-                       return result);
-
-       table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
-       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-       table->ACPILevel.DeepSleepDivId = 0;
-
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
-                       SPLL_PWRON, 0);
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
-                       SPLL_RESET, 1);
-       spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2, CG_SPLL_FUNC_CNTL_2,
-                       SCLK_MUX_SEL, 4);
-
-       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
-       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
-       table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
-       table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
-       table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
-       table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
-       table->ACPILevel.CcPwrDynRm = 0;
-       table->ACPILevel.CcPwrDynRm1 = 0;
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
-
-       if (!data->mclk_dpm_key_disabled) {
-               /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
-               table->MemoryACPILevel.MclkFrequency =
-                               data->dpm_table.mclk_table.dpm_levels[0].value;
-               result = fiji_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_mclk,
-                               table->MemoryACPILevel.MclkFrequency,
-                               &table->MemoryACPILevel.MinVoltage, &mvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Cannot find ACPI VDDCI voltage value "
-                               "in Clock Dependency Table",);
-       } else {
-               table->MemoryACPILevel.MclkFrequency =
-                               data->vbios_boot_state.mclk_bootup_value;
-               table->MemoryACPILevel.MinVoltage =
-                               data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE;
-       }
-
-       us_mvdd = 0;
-       if ((FIJI_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
-                       (data->mclk_dpm_key_disabled))
-               us_mvdd = data->vbios_boot_state.mvdd_bootup_value;
-       else {
-               if (!fiji_populate_mvdd_value(hwmgr,
-                               data->dpm_table.mclk_table.dpm_levels[0].value,
-                               &vol_level))
-                       us_mvdd = vol_level.Voltage;
-       }
-
-       table->MemoryACPILevel.MinMvdd =
-                       PP_HOST_TO_SMC_UL(us_mvdd * VOLTAGE_SCALE);
-
-       table->MemoryACPILevel.EnabledForThrottle = 0;
-       table->MemoryACPILevel.EnabledForActivity = 0;
-       table->MemoryACPILevel.UpHyst = 0;
-       table->MemoryACPILevel.DownHyst = 100;
-       table->MemoryACPILevel.VoltageDownHyst = 0;
-       table->MemoryACPILevel.ActivityLevel =
-                       PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
-
-       table->MemoryACPILevel.StutterEnable = false;
-       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
-
-       return result;
-}
-
-static int fiji_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
-               SMU73_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       table->VceLevelCount = (uint8_t)(mm_table->count);
-       table->VceBootLevel = 0;
-
-       for(count = 0; count < table->VceLevelCount; count++) {
-               table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
-               table->VceLevel[count].MinVoltage = 0;
-               table->VceLevel[count].MinVoltage |=
-                               (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->VceLevel[count].MinVoltage |=
-                               ((mm_table->entries[count].vddc - data->vddc_vddci_delta) *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /*retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->VceLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for VCE engine clock",
-                               return result);
-
-               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
-       }
-       return result;
-}
-
-static int fiji_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
-               SMU73_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       table->AcpLevelCount = (uint8_t)(mm_table->count);
-       table->AcpBootLevel = 0;
-
-       for (count = 0; count < table->AcpLevelCount; count++) {
-               table->AcpLevel[count].Frequency = mm_table->entries[count].aclk;
-               table->AcpLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
-                               VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->AcpLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
-                               data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->AcpLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->AcpLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for engine clock", return result);
-
-               table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].MinVoltage);
-       }
-       return result;
-}
-
-static int fiji_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
-               SMU73_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       table->SamuBootLevel = 0;
-       table->SamuLevelCount = (uint8_t)(mm_table->count);
-
-       for (count = 0; count < table->SamuLevelCount; count++) {
-               /* not sure whether we need evclk or not */
-               table->SamuLevel[count].MinVoltage = 0;
-               table->SamuLevel[count].Frequency = mm_table->entries[count].samclock;
-               table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
-                               VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->SamuLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
-                               data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->SamuLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for samu clock", return result);
-
-               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].MinVoltage);
-       }
-       return result;
-}
-
-static int fiji_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
-               int32_t eng_clock, int32_t mem_clock,
-               struct SMU73_Discrete_MCArbDramTimingTableEntry *arb_regs)
-{
-       uint32_t dram_timing;
-       uint32_t dram_timing2;
-       uint32_t burstTime;
-       ULONG state, trrds, trrdl;
-       int result;
-
-       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
-                       eng_clock, mem_clock);
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "Error calling VBIOS to set DRAM_TIMING.", return result);
-
-       dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-       dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-       burstTime = cgs_read_register(hwmgr->device, mmMC_ARB_BURST_TIME);
-
-       state = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, STATE0);
-       trrds = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDS0);
-       trrdl = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDL0);
-
-       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dram_timing);
-       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
-       arb_regs->McArbBurstTime   = (uint8_t)burstTime;
-       arb_regs->TRRDS            = (uint8_t)trrds;
-       arb_regs->TRRDL            = (uint8_t)trrdl;
-
-       return 0;
-}
-
-static int fiji_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct SMU73_Discrete_MCArbDramTimingTable arb_regs;
-       uint32_t i, j;
-       int result = 0;
-
-       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
-               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
-                       result = fiji_populate_memory_timing_parameters(hwmgr,
-                                       data->dpm_table.sclk_table.dpm_levels[i].value,
-                                       data->dpm_table.mclk_table.dpm_levels[j].value,
-                                       &arb_regs.entries[i][j]);
-                       if (result)
-                               break;
-               }
-       }
-
-       if (!result)
-               result = fiji_copy_bytes_to_smc(
-                               hwmgr->smumgr,
-                               data->arb_table_start,
-                               (uint8_t *)&arb_regs,
-                               sizeof(SMU73_Discrete_MCArbDramTimingTable),
-                               data->sram_end);
-       return result;
-}
-
-static int fiji_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       table->UvdLevelCount = (uint8_t)(mm_table->count);
-       table->UvdBootLevel = 0;
-
-       for (count = 0; count < table->UvdLevelCount; count++) {
-               table->UvdLevel[count].MinVoltage = 0;
-               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
-               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
-               table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
-                               VOLTAGE_SCALE) << VDDC_SHIFT;
-               table->UvdLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
-                               data->vddc_vddci_delta) * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->UvdLevel[count].VclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for Vclk clock", return result);
-
-               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->UvdLevel[count].DclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for Dclk clock", return result);
-
-               table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
-
-       }
-       return result;
-}
-
-static int fiji_find_boot_level(struct fiji_single_dpm_table *table,
-               uint32_t value, uint32_t *boot_level)
-{
-       int result = -EINVAL;
-       uint32_t i;
-
-       for (i = 0; i < table->count; i++) {
-               if (value == table->dpm_levels[i].value) {
-                       *boot_level = i;
-                       result = 0;
-               }
-       }
-       return result;
-}
-
-static int fiji_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       int result = 0;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       table->GraphicsBootLevel = 0;
-       table->MemoryBootLevel = 0;
-
-       /* find boot level from dpm table */
-       result = fiji_find_boot_level(&(data->dpm_table.sclk_table),
-                       data->vbios_boot_state.sclk_bootup_value,
-                       (uint32_t *)&(table->GraphicsBootLevel));
-
-       result = fiji_find_boot_level(&(data->dpm_table.mclk_table),
-                       data->vbios_boot_state.mclk_bootup_value,
-                       (uint32_t *)&(table->MemoryBootLevel));
-
-       table->BootVddc  = data->vbios_boot_state.vddc_bootup_value *
-                       VOLTAGE_SCALE;
-       table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
-                       VOLTAGE_SCALE;
-       table->BootMVdd  = data->vbios_boot_state.mvdd_bootup_value *
-                       VOLTAGE_SCALE;
-
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
-
-       return 0;
-}
-
-static int fiji_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint8_t count, level;
-
-       count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
-       for (level = 0; level < count; level++) {
-               if(table_info->vdd_dep_on_sclk->entries[level].clk >=
-                               data->vbios_boot_state.sclk_bootup_value) {
-                       data->smc_state_table.GraphicsBootLevel = level;
-                       break;
-               }
-       }
-
-       count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
-       for (level = 0; level < count; level++) {
-               if(table_info->vdd_dep_on_mclk->entries[level].clk >=
-                               data->vbios_boot_state.mclk_bootup_value) {
-                       data->smc_state_table.MemoryBootLevel = level;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
-{
-       uint32_t ro, efuse, efuse2, clock_freq, volt_without_cks,
-                       volt_with_cks, value;
-       uint16_t clock_freq_u16;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint8_t type, i, j, cks_setting, stretch_amount, stretch_amount2,
-                       volt_offset = 0;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-
-       stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
-
-       /* Read SMU_Eefuse to read and calculate RO and determine
-        * if the part is SS or FF. if RO >= 1660MHz, part is FF.
-        */
-       efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixSMU_EFUSE_0 + (146 * 4));
-       efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixSMU_EFUSE_0 + (148 * 4));
-       efuse &= 0xFF000000;
-       efuse = efuse >> 24;
-       efuse2 &= 0xF;
-
-       if (efuse2 == 1)
-               ro = (2300 - 1350) * efuse / 255 + 1350;
-       else
-               ro = (2500 - 1000) * efuse / 255 + 1000;
-
-       if (ro >= 1660)
-               type = 0;
-       else
-               type = 1;
-
-       /* Populate Stretch amount */
-       data->smc_state_table.ClockStretcherAmount = stretch_amount;
-
-       /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
-       for (i = 0; i < sclk_table->count; i++) {
-               data->smc_state_table.Sclk_CKS_masterEn0_7 |=
-                               sclk_table->entries[i].cks_enable << i;
-               volt_without_cks = (uint32_t)((14041 *
-                       (sclk_table->entries[i].clk/100) / 10000 + 3571 + 75 - ro) * 1000 /
-                       (4026 - (13924 * (sclk_table->entries[i].clk/100) / 10000)));
-               volt_with_cks = (uint32_t)((13946 *
-                       (sclk_table->entries[i].clk/100) / 10000 + 3320 + 45 - ro) * 1000 /
-                       (3664 - (11454 * (sclk_table->entries[i].clk/100) / 10000)));
-               if (volt_without_cks >= volt_with_cks)
-                       volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
-                                       sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
-               data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
-       }
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
-                       STRETCH_ENABLE, 0x0);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
-                       masterReset, 0x1);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
-                       staticEnable, 0x1);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
-                       masterReset, 0x0);
-
-       /* Populate CKS Lookup Table */
-       if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
-               stretch_amount2 = 0;
-       else if (stretch_amount == 3 || stretch_amount == 4)
-               stretch_amount2 = 1;
-       else {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ClockStretcher);
-               PP_ASSERT_WITH_CODE(false,
-                               "Stretch Amount in PPTable not supported\n",
-                               return -EINVAL);
-       }
-
-       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixPWR_CKS_CNTL);
-       value &= 0xFFC2FF87;
-       data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].minFreq =
-                       fiji_clock_stretcher_lookup_table[stretch_amount2][0];
-       data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].maxFreq =
-                       fiji_clock_stretcher_lookup_table[stretch_amount2][1];
-       clock_freq_u16 = (uint16_t)(PP_SMC_TO_HOST_UL(data->smc_state_table.
-                       GraphicsLevel[data->smc_state_table.GraphicsDpmLevelCount - 1].
-                       SclkFrequency) / 100);
-       if (fiji_clock_stretcher_lookup_table[stretch_amount2][0] <
-                       clock_freq_u16 &&
-           fiji_clock_stretcher_lookup_table[stretch_amount2][1] >
-                       clock_freq_u16) {
-               /* Program PWR_CKS_CNTL. CKS_USE_FOR_LOW_FREQ */
-               value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 16;
-               /* Program PWR_CKS_CNTL. CKS_LDO_REFSEL */
-               value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][2]) << 18;
-               /* Program PWR_CKS_CNTL. CKS_STRETCH_AMOUNT */
-               value |= (fiji_clock_stretch_amount_conversion
-                               [fiji_clock_stretcher_lookup_table[stretch_amount2][3]]
-                                [stretch_amount]) << 3;
-       }
-       CONVERT_FROM_HOST_TO_SMC_US(data->smc_state_table.CKS_LOOKUPTable.
-                       CKS_LOOKUPTableEntry[0].minFreq);
-       CONVERT_FROM_HOST_TO_SMC_US(data->smc_state_table.CKS_LOOKUPTable.
-                       CKS_LOOKUPTableEntry[0].maxFreq);
-       data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting =
-                       fiji_clock_stretcher_lookup_table[stretch_amount2][2] & 0x7F;
-       data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting |=
-                       (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 7;
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixPWR_CKS_CNTL, value);
-
-       /* Populate DDT Lookup Table */
-       for (i = 0; i < 4; i++) {
-               /* Assign the minimum and maximum VID stored
-                * in the last row of Clock Stretcher Voltage Table.
-                */
-               data->smc_state_table.ClockStretcherDataTable.
-               ClockStretcherDataTableEntry[i].minVID =
-                               (uint8_t) fiji_clock_stretcher_ddt_table[type][i][2];
-               data->smc_state_table.ClockStretcherDataTable.
-               ClockStretcherDataTableEntry[i].maxVID =
-                               (uint8_t) fiji_clock_stretcher_ddt_table[type][i][3];
-               /* Loop through each SCLK and check the frequency
-                * to see if it lies within the frequency for clock stretcher.
-                */
-               for (j = 0; j < data->smc_state_table.GraphicsDpmLevelCount; j++) {
-                       cks_setting = 0;
-                       clock_freq = PP_SMC_TO_HOST_UL(
-                                       data->smc_state_table.GraphicsLevel[j].SclkFrequency);
-                       /* Check the allowed frequency against the sclk level[j].
-                        *  Sclk's endianness has already been converted,
-                        *  and it's in 10Khz unit,
-                        *  as opposed to Data table, which is in Mhz unit.
-                        */
-                       if (clock_freq >=
-                                       (fiji_clock_stretcher_ddt_table[type][i][0]) * 100) {
-                               cks_setting |= 0x2;
-                               if (clock_freq <
-                                               (fiji_clock_stretcher_ddt_table[type][i][1]) * 100)
-                                       cks_setting |= 0x1;
-                       }
-                       data->smc_state_table.ClockStretcherDataTable.
-                       ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
-               }
-               CONVERT_FROM_HOST_TO_SMC_US(data->smc_state_table.
-                               ClockStretcherDataTable.
-                               ClockStretcherDataTableEntry[i].setting);
-       }
-
-       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
-       value &= 0xFFFFFFFE;
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
-
-       return 0;
-}
-
-/**
-* Populates the SMC VRConfig field in DPM table.
-*
-* @param    hwmgr   the address of the hardware manager
-* @param    table   the SMC DPM table structure to be populated
-* @return   always 0
-*/
-static int fiji_populate_vr_config(struct pp_hwmgr *hwmgr,
-               struct SMU73_Discrete_DpmTable *table)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint16_t config;
-
-       config = VR_MERGED_WITH_VDDC;
-       table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
-
-       /* Set Vddc Voltage Controller */
-       if(FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               config = VR_SVI2_PLANE_1;
-               table->VRConfig |= config;
-       } else {
-               PP_ASSERT_WITH_CODE(false,
-                               "VDDC should be on SVI2 control in merged mode!",);
-       }
-       /* Set Vddci Voltage Controller */
-       if(FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
-               config = VR_SVI2_PLANE_2;  /* only in merged mode */
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       } else if (FIJI_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
-               config = VR_SMIO_PATTERN_1;
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       } else {
-               config = VR_STATIC_VOLTAGE;
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       }
-       /* Set Mvdd Voltage Controller */
-       if(FIJI_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
-               config = VR_SVI2_PLANE_2;
-               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
-       } else if(FIJI_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               config = VR_SMIO_PATTERN_2;
-               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
-       } else {
-               config = VR_STATIC_VOLTAGE;
-               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
-       }
-
-       return 0;
-}
-
-/**
-* Initializes the SMC table and uploads it
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput  the pointer to input data (PowerState)
-* @return   always 0
-*/
-static int fiji_init_smc_table(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct SMU73_Discrete_DpmTable *table = &(data->smc_state_table);
-       const struct fiji_ulv_parm *ulv = &(data->ulv);
-       uint8_t i;
-       struct pp_atomctrl_gpio_pin_assignment gpio_pin;
-
-       result = fiji_setup_default_dpm_tables(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to setup default DPM tables!", return result);
-
-       if(FIJI_VOLTAGE_CONTROL_NONE != data->voltage_control)
-               fiji_populate_smc_voltage_tables(hwmgr, table);
-
-       table->SystemFlags = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition))
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StepVddc))
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
-
-       if (data->is_memory_gddr5)
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
-
-       if (ulv->ulv_supported && table_info->us_ulv_voltage_offset) {
-               result = fiji_populate_ulv_state(hwmgr, table);
-               PP_ASSERT_WITH_CODE(0 == result,
-                               "Failed to initialize ULV state!", return result);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_ULV_PARAMETER, ulv->cg_ulv_parameter);
-       }
-
-       result = fiji_populate_smc_link_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Link Level!", return result);
-
-       result = fiji_populate_all_graphic_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Graphics Level!", return result);
-
-       result = fiji_populate_all_memory_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Memory Level!", return result);
-
-       result = fiji_populate_smc_acpi_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize ACPI Level!", return result);
-
-       result = fiji_populate_smc_vce_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize VCE Level!", return result);
-
-       result = fiji_populate_smc_acp_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize ACP Level!", return result);
-
-       result = fiji_populate_smc_samu_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize SAMU Level!", return result);
-
-       /* Since only the initial state is completely set up at this point
-        * (the other states are just copies of the boot state) we only
-        * need to populate the  ARB settings for the initial state.
-        */
-       result = fiji_program_memory_timing_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to Write ARB settings for the initial state.", return result);
-
-       result = fiji_populate_smc_uvd_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize UVD Level!", return result);
-
-       result = fiji_populate_smc_boot_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Boot Level!", return result);
-
-       result = fiji_populate_smc_initailial_state(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Boot State!", return result);
-
-       result = fiji_populate_bapm_parameters_in_dpm_table(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to populate BAPM Parameters!", return result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ClockStretcher)) {
-               result = fiji_populate_clock_stretcher_data_table(hwmgr);
-               PP_ASSERT_WITH_CODE(0 == result,
-                               "Failed to populate Clock Stretcher Data Table!",
-                               return result);
-       }
-
-       table->GraphicsVoltageChangeEnable  = 1;
-       table->GraphicsThermThrottleEnable  = 1;
-       table->GraphicsInterval = 1;
-       table->VoltageInterval  = 1;
-       table->ThermalInterval  = 1;
-       table->TemperatureLimitHigh =
-                       table_info->cac_dtp_table->usTargetOperatingTemp *
-                       FIJI_Q88_FORMAT_CONVERSION_UNIT;
-       table->TemperatureLimitLow  =
-                       (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
-                       FIJI_Q88_FORMAT_CONVERSION_UNIT;
-       table->MemoryVoltageChangeEnable = 1;
-       table->MemoryInterval = 1;
-       table->VoltageResponseTime = 0;
-       table->PhaseResponseTime = 0;
-       table->MemoryThermThrottleEnable = 1;
-       table->PCIeBootLinkLevel = 0;      /* 0:Gen1 1:Gen2 2:Gen3*/
-       table->PCIeGenInterval = 1;
-       table->VRConfig = 0;
-
-       result = fiji_populate_vr_config(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to populate VRConfig setting!", return result);
-
-       table->ThermGpio = 17;
-       table->SclkStepSize = 0x4000;
-
-       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
-               table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_RegulatorHot);
-       } else {
-               table->VRHotGpio = FIJI_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_RegulatorHot);
-       }
-
-       if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
-                       &gpio_pin)) {
-               table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_AutomaticDCTransition);
-       } else {
-               table->AcDcGpio = FIJI_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_AutomaticDCTransition);
-       }
-
-       /* Thermal Output GPIO */
-       if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
-                       &gpio_pin)) {
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ThermalOutGPIO);
-
-               table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
-
-               /* For porlarity read GPIOPAD_A with assigned Gpio pin
-                * since VBIOS will program this register to set 'inactive state',
-                * driver can then determine 'active state' from this and
-                * program SMU with correct polarity
-                */
-               table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
-                               (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
-
-               /* if required, combine VRHot/PCC with thermal out GPIO */
-               if(phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_RegulatorHot) &&
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_CombinePCCWithThermalSignal))
-                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
-       } else {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ThermalOutGPIO);
-               table->ThermOutGpio = 17;
-               table->ThermOutPolarity = 1;
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
-       }
-
-       for (i = 0; i < SMU73_MAX_ENTRIES_SMIO; i++)
-               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
-       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
-       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
-
-       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
-       result = fiji_copy_bytes_to_smc(hwmgr->smumgr,
-                       data->dpm_table_start +
-                       offsetof(SMU73_Discrete_DpmTable, SystemFlags),
-                       (uint8_t *)&(table->SystemFlags),
-                       sizeof(SMU73_Discrete_DpmTable) - 3 * sizeof(SMU73_PIDController),
-                       data->sram_end);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to upload dpm data to SMC memory!", return result);
-
-       return 0;
-}
-
-/**
-* Initialize the ARB DRAM timing table's index field.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int fiji_init_arb_table_index(struct pp_hwmgr *hwmgr)
-{
-       const struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t tmp;
-       int result;
-
-       /* This is a read-modify-write on the first byte of the ARB table.
-        * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
-        * is the field 'current'.
-        * This solution is ugly, but we never write the whole table only
-        * individual fields in it.
-        * In reality this field should not be in that structure
-        * but in a soft register.
-        */
-       result = fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       data->arb_table_start, &tmp, data->sram_end);
-
-       if (result)
-               return result;
-
-       tmp &= 0x00FFFFFF;
-       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
-
-       return fiji_write_smc_sram_dword(hwmgr->smumgr,
-                       data->arb_table_start,  tmp, data->sram_end);
-}
-
-static int fiji_enable_vrhot_gpio_interrupt(struct pp_hwmgr *hwmgr)
-{
-       if(phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_RegulatorHot))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_EnableVRHotGPIOInterrupt);
-
-       return 0;
-}
-
-static int fiji_enable_sclk_control(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       SCLK_PWRMGT_OFF, 0);
-       return 0;
-}
-
-static int fiji_enable_ulv(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_ulv_parm *ulv = &(data->ulv);
-
-       if (ulv->ulv_supported)
-               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_EnableULV);
-
-       return 0;
-}
-
-static int fiji_disable_ulv(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_ulv_parm *ulv = &(data->ulv);
-
-       if (ulv->ulv_supported)
-               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DisableULV);
-
-       return 0;
-}
-
-static int fiji_enable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep)) {
-               if (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_MASTER_DeepSleep_ON))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to enable Master Deep Sleep switch failed!",
-                                       return -1);
-       } else {
-               if (smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to disable Master Deep Sleep switch failed!",
-                                       return -1);
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_disable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep)) {
-               if (smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to disable Master Deep Sleep switch failed!",
-                                       return -1);
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_enable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t   val, val0, val2;
-       uint32_t   i, cpl_cntl, cpl_threshold, mc_threshold;
-
-       /* enable SCLK dpm */
-       if(!data->sclk_dpm_key_disabled)
-               PP_ASSERT_WITH_CODE(
-               (0 == smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DPM_Enable)),
-               "Failed to enable SCLK DPM during DPM Start Function!",
-               return -1);
-
-       /* enable MCLK dpm */
-       if(0 == data->mclk_dpm_key_disabled) {
-               cpl_threshold = 0;
-               mc_threshold = 0;
-
-               /* Read per MCD tile (0 - 7) */
-               for (i = 0; i < 8; i++) {
-                       PHM_WRITE_FIELD(hwmgr->device, MC_CONFIG_MCD, MC_RD_ENABLE, i);
-                       val = cgs_read_register(hwmgr->device, mmMC_SEQ_RESERVE_0_S) & 0xf0000000;
-                       if (0xf0000000 != val) {
-                               /* count number of MCQ that has channel(s) enabled */
-                               cpl_threshold++;
-                               /* only harvest 3 or full 4 supported */
-                               mc_threshold = val ? 3 : 4;
-                       }
-               }
-               PP_ASSERT_WITH_CODE(0 != cpl_threshold,
-                               "Number of MCQ is zero!", return -EINVAL;);
-
-               mc_threshold = ((mc_threshold & LCAC_MC0_CNTL__MC0_THRESHOLD_MASK) <<
-                               LCAC_MC0_CNTL__MC0_THRESHOLD__SHIFT) |
-                                               LCAC_MC0_CNTL__MC0_ENABLE_MASK;
-               cpl_cntl = ((cpl_threshold & LCAC_CPL_CNTL__CPL_THRESHOLD_MASK) <<
-                               LCAC_CPL_CNTL__CPL_THRESHOLD__SHIFT) |
-                                               LCAC_CPL_CNTL__CPL_ENABLE_MASK;
-               cpl_cntl = (cpl_cntl | (8 << LCAC_CPL_CNTL__CPL_BLOCK_ID__SHIFT));
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_MC0_CNTL, mc_threshold);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_MC1_CNTL, mc_threshold);
-               if (8 == cpl_threshold) {
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC2_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC3_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC4_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC5_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC6_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC7_CNTL, mc_threshold);
-               }
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_CPL_CNTL, cpl_cntl);
-
-               udelay(5);
-
-               mc_threshold = mc_threshold |
-                               (1 << LCAC_MC0_CNTL__MC0_SIGNAL_ID__SHIFT);
-               cpl_cntl = cpl_cntl | (1 << LCAC_CPL_CNTL__CPL_SIGNAL_ID__SHIFT);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_MC0_CNTL, mc_threshold);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_MC1_CNTL, mc_threshold);
-               if (8 == cpl_threshold) {
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC2_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC3_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC4_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC5_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC6_CNTL, mc_threshold);
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixLCAC_MC7_CNTL, mc_threshold);
-               }
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixLCAC_CPL_CNTL, cpl_cntl);
-
-               /* Program CAC_EN per MCD (0-7) Tile */
-               val0 = val = cgs_read_register(hwmgr->device, mmMC_CONFIG_MCD);
-               val &= ~(MC_CONFIG_MCD__MCD0_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD1_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD2_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD3_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD4_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD5_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD6_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MCD7_WR_ENABLE_MASK |
-                               MC_CONFIG_MCD__MC_RD_ENABLE_MASK);
-
-               for (i = 0; i < 8; i++) {
-                       /* Enable MCD i Tile read & write */
-                       val2  = (val | (i << MC_CONFIG_MCD__MC_RD_ENABLE__SHIFT) |
-                                       (1 << i));
-                       cgs_write_register(hwmgr->device, mmMC_CONFIG_MCD, val2);
-                       /* Enbale CAC_ON MCD i Tile */
-                       val2 = cgs_read_register(hwmgr->device, mmMC_SEQ_CNTL);
-                       val2 |= MC_SEQ_CNTL__CAC_EN_MASK;
-                       cgs_write_register(hwmgr->device, mmMC_SEQ_CNTL, val2);
-               }
-               /* Set MC_CONFIG_MCD back to its default setting val0 */
-               cgs_write_register(hwmgr->device, mmMC_CONFIG_MCD, val0);
-
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_Enable)),
-                               "Failed to enable MCLK DPM during DPM Start Function!",
-                               return -1);
-       }
-       return 0;
-}
-
-static int fiji_start_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /*enable general power management */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                       GLOBAL_PWRMGT_EN, 1);
-       /* enable sclk deep sleep */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       DYNAMIC_PM_EN, 1);
-       /* prepare for PCIE DPM */
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       data->soft_regs_start + offsetof(SMU73_SoftRegisters,
-                                       VoltageChangeTimeout), 0x1000);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
-                       SWRST_COMMAND_1, RESETLC, 0x0);
-
-       PP_ASSERT_WITH_CODE(
-                       (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                       PPSMC_MSG_Voltage_Cntl_Enable)),
-                       "Failed to enable voltage DPM during DPM Start Function!",
-                       return -1);
-
-       if (fiji_enable_sclk_mclk_dpm(hwmgr)) {
-               printk(KERN_ERR "Failed to enable Sclk DPM and Mclk DPM!");
-               return -1;
-       }
-
-       /* enable PCIE dpm */
-       if(!data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Enable)),
-                               "Failed to enable pcie DPM during DPM Start Function!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-static int fiji_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* disable SCLK dpm */
-       if (!data->sclk_dpm_key_disabled)
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_DPM_Disable) == 0),
-                               "Failed to disable SCLK DPM!",
-                               return -1);
-
-       /* disable MCLK dpm */
-       if (!data->mclk_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_MCLKDPM_SetEnabledMask, 1) == 0),
-                               "Failed to force MCLK DPM0!",
-                               return -1);
-
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_Disable) == 0),
-                               "Failed to disable MCLK DPM!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-static int fiji_stop_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* disable general power management */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                       GLOBAL_PWRMGT_EN, 0);
-       /* disable sclk deep sleep */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       DYNAMIC_PM_EN, 0);
-
-       /* disable PCIE dpm */
-       if (!data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Disable) == 0),
-                               "Failed to disable pcie DPM during DPM Stop Function!",
-                               return -1);
-       }
-
-       if (fiji_disable_sclk_mclk_dpm(hwmgr)) {
-               printk(KERN_ERR "Failed to disable Sclk DPM and Mclk DPM!");
-               return -1;
-       }
-
-       PP_ASSERT_WITH_CODE(
-                       (smum_send_msg_to_smc(hwmgr->smumgr,
-                                       PPSMC_MSG_Voltage_Cntl_Disable) == 0),
-                       "Failed to disable voltage DPM during DPM Stop Function!",
-                       return -1);
-
-       return 0;
-}
-
-static void fiji_set_dpm_event_sources(struct pp_hwmgr *hwmgr,
-               uint32_t sources)
-{
-       bool protection;
-       enum DPM_EVENT_SRC src;
-
-       switch (sources) {
-       default:
-               printk(KERN_ERR "Unknown throttling event sources.");
-               /* fall through */
-       case 0:
-               protection = false;
-               /* src is unused */
-               break;
-       case (1 << PHM_AutoThrottleSource_Thermal):
-               protection = true;
-               src = DPM_EVENT_SRC_DIGITAL;
-               break;
-       case (1 << PHM_AutoThrottleSource_External):
-               protection = true;
-               src = DPM_EVENT_SRC_EXTERNAL;
-               break;
-       case (1 << PHM_AutoThrottleSource_External) |
-                       (1 << PHM_AutoThrottleSource_Thermal):
-               protection = true;
-               src = DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL;
-               break;
-       }
-       /* Order matters - don't enable thermal protection for the wrong source. */
-       if (protection) {
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_CTRL,
-                               DPM_EVENT_SRC, src);
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                               THERMAL_PROTECTION_DIS,
-                               !phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_ThermalController));
-       } else
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                               THERMAL_PROTECTION_DIS, 1);
-}
-
-static int fiji_enable_auto_throttle_source(struct pp_hwmgr *hwmgr,
-               PHM_AutoThrottleSource source)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (!(data->active_auto_throttle_sources & (1 << source))) {
-               data->active_auto_throttle_sources |= 1 << source;
-               fiji_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
-       }
-       return 0;
-}
-
-static int fiji_enable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
-{
-       return fiji_enable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
-}
-
-static int fiji_disable_auto_throttle_source(struct pp_hwmgr *hwmgr,
-               PHM_AutoThrottleSource source)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (data->active_auto_throttle_sources & (1 << source)) {
-               data->active_auto_throttle_sources &= ~(1 << source);
-               fiji_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
-       }
-       return 0;
-}
-
-static int fiji_disable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
-{
-       return fiji_disable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
-}
-
-static int fiji_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = (!fiji_is_dpm_running(hwmgr))? 0 : -1;
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "DPM is already running right now, no need to enable DPM!",
-                       return 0);
-
-       if (fiji_voltage_control(hwmgr)) {
-               tmp_result = fiji_enable_voltage_control(hwmgr);
-               PP_ASSERT_WITH_CODE(tmp_result == 0,
-                               "Failed to enable voltage control!",
-                               result = tmp_result);
-       }
-
-       if (fiji_voltage_control(hwmgr)) {
-               tmp_result = fiji_construct_voltage_tables(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to contruct voltage tables!",
-                               result = tmp_result);
-       }
-
-       tmp_result = fiji_initialize_mc_reg_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize MC reg table!", result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EngineSpreadSpectrumSupport))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 1);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalController))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 0);
-
-       tmp_result = fiji_program_static_screen_threshold_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program static screen threshold parameters!",
-                       result = tmp_result);
-
-       tmp_result = fiji_enable_display_gap(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable display gap!", result = tmp_result);
-
-       tmp_result = fiji_program_voting_clients(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program voting clients!", result = tmp_result);
-
-       tmp_result = fiji_process_firmware_header(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to process firmware header!", result = tmp_result);
-
-       tmp_result = fiji_initial_switch_from_arbf0_to_f1(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize switch from ArbF0 to F1!",
-                       result = tmp_result);
-
-       tmp_result = fiji_init_smc_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize SMC table!", result = tmp_result);
-
-       tmp_result = fiji_init_arb_table_index(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize ARB table index!", result = tmp_result);
-
-       tmp_result = fiji_populate_pm_fuses(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to populate PM fuses!", result = tmp_result);
-
-       tmp_result = fiji_enable_vrhot_gpio_interrupt(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
-
-       tmp_result = tonga_notify_smc_display_change(hwmgr, false);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to notify no display!", result = tmp_result);
-
-       tmp_result = fiji_enable_sclk_control(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable SCLK control!", result = tmp_result);
-
-       tmp_result = fiji_enable_ulv(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable ULV!", result = tmp_result);
-
-       tmp_result = fiji_enable_deep_sleep_master_switch(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable deep sleep master switch!", result = tmp_result);
-
-       tmp_result = fiji_start_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to start DPM!", result = tmp_result);
-
-       tmp_result = fiji_enable_smc_cac(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable SMC CAC!", result = tmp_result);
-
-       tmp_result = fiji_enable_power_containment(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable power containment!", result = tmp_result);
-
-       tmp_result = fiji_power_control_set_level(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to power control set level!", result = tmp_result);
-
-       tmp_result = fiji_enable_thermal_auto_throttle(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable thermal auto throttle!", result = tmp_result);
-
-       return result;
-}
-
-static int fiji_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = (fiji_is_dpm_running(hwmgr)) ? 0 : -1;
-       PP_ASSERT_WITH_CODE(tmp_result == 0,
-                       "DPM is not running right now, no need to disable DPM!",
-                       return 0);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalController))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 1);
-
-       tmp_result = fiji_disable_power_containment(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable power containment!", result = tmp_result);
-
-       tmp_result = fiji_disable_smc_cac(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable SMC CAC!", result = tmp_result);
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_SPLL_SPREAD_SPECTRUM, SSEN, 0);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 0);
-
-       tmp_result = fiji_disable_thermal_auto_throttle(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable thermal auto throttle!", result = tmp_result);
-
-       tmp_result = fiji_stop_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to stop DPM!", result = tmp_result);
-
-       tmp_result = fiji_disable_deep_sleep_master_switch(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable deep sleep master switch!", result = tmp_result);
-
-       tmp_result = fiji_disable_ulv(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable ULV!", result = tmp_result);
-
-       tmp_result = fiji_clear_voting_clients(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to clear voting clients!", result = tmp_result);
-
-       tmp_result = fiji_reset_to_default(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to reset to default!", result = tmp_result);
-
-       tmp_result = fiji_force_switch_to_arbf0(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to force to switch arbf0!", result = tmp_result);
-
-       return result;
-}
-
-static int fiji_force_dpm_highest(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t level, tmp;
-
-       if (!data->sclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                               (1 << level));
-               }
-       }
-
-       if (!data->mclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                               (1 << level));
-               }
-       }
-
-       if (!data->pcie_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_ForceLevel,
-                                               (1 << level));
-               }
-       }
-       return 0;
-}
-
-static int fiji_upload_dpmlevel_enable_mask(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       phm_apply_dal_min_voltage_request(hwmgr);
-
-       if (!data->sclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-       }
-       return 0;
-}
-
-static int fiji_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (!fiji_is_dpm_running(hwmgr))
-               return -EINVAL;
-
-       if (!data->pcie_dpm_key_disabled) {
-               smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_PCIeDPM_UnForceLevel);
-       }
-
-       return fiji_upload_dpmlevel_enable_mask(hwmgr);
-}
-
-static uint32_t fiji_get_lowest_enabled_level(
-               struct pp_hwmgr *hwmgr, uint32_t mask)
-{
-       uint32_t level = 0;
-
-       while(0 == (mask & (1 << level)))
-               level++;
-
-       return level;
-}
-
-static int fiji_force_dpm_lowest(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data =
-                       (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t level;
-
-       if (!data->sclk_dpm_key_disabled)
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       level = fiji_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                                           (1 << level));
-
-       }
-
-       if (!data->mclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       level = fiji_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                                           (1 << level));
-               }
-       }
-
-       if (!data->pcie_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
-                       level = fiji_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.pcie_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_PCIeDPM_ForceLevel,
-                                                           (1 << level));
-               }
-       }
-
-       return 0;
-
-}
-static int fiji_dpm_force_dpm_level(struct pp_hwmgr *hwmgr,
-                               enum amd_dpm_forced_level level)
-{
-       int ret = 0;
-
-       switch (level) {
-       case AMD_DPM_FORCED_LEVEL_HIGH:
-               ret = fiji_force_dpm_highest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_LOW:
-               ret = fiji_force_dpm_lowest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_AUTO:
-               ret = fiji_unforce_dpm_levels(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       default:
-               break;
-       }
-
-       hwmgr->dpm_level = level;
-
-       return ret;
-}
-
-static int fiji_get_power_state_size(struct pp_hwmgr *hwmgr)
-{
-       return sizeof(struct fiji_power_state);
-}
-
-static int fiji_get_pp_table_entry_callback_func(struct pp_hwmgr *hwmgr,
-               void *state, struct pp_power_state *power_state,
-               void *pp_table, uint32_t classification_flag)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_power_state  *fiji_power_state =
-                       (struct fiji_power_state *)(&(power_state->hardware));
-       struct fiji_performance_level *performance_level;
-       ATOM_Tonga_State *state_entry = (ATOM_Tonga_State *)state;
-       ATOM_Tonga_POWERPLAYTABLE *powerplay_table =
-                       (ATOM_Tonga_POWERPLAYTABLE *)pp_table;
-       ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table =
-                       (ATOM_Tonga_SCLK_Dependency_Table *)
-                       (((unsigned long)powerplay_table) +
-                               le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
-       ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
-                       (ATOM_Tonga_MCLK_Dependency_Table *)
-                       (((unsigned long)powerplay_table) +
-                               le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
-
-       /* The following fields are not initialized here: id orderedList allStatesList */
-       power_state->classification.ui_label =
-                       (le16_to_cpu(state_entry->usClassification) &
-                       ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
-                       ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
-       power_state->classification.flags = classification_flag;
-       /* NOTE: There is a classification2 flag in BIOS that is not being used right now */
-
-       power_state->classification.temporary_state = false;
-       power_state->classification.to_be_deleted = false;
-
-       power_state->validation.disallowOnDC =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
-                                       ATOM_Tonga_DISALLOW_ON_DC));
-
-       power_state->pcie.lanes = 0;
-
-       power_state->display.disableFrameModulation = false;
-       power_state->display.limitRefreshrate = false;
-       power_state->display.enableVariBright =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
-                                       ATOM_Tonga_ENABLE_VARIBRIGHT));
-
-       power_state->validation.supportedPowerLevels = 0;
-       power_state->uvd_clocks.VCLK = 0;
-       power_state->uvd_clocks.DCLK = 0;
-       power_state->temperatures.min = 0;
-       power_state->temperatures.max = 0;
-
-       performance_level = &(fiji_power_state->performance_levels
-                       [fiji_power_state->performance_level_count++]);
-
-       PP_ASSERT_WITH_CODE(
-                       (fiji_power_state->performance_level_count < SMU73_MAX_LEVELS_GRAPHICS),
-                       "Performance levels exceeds SMC limit!",
-                       return -1);
-
-       PP_ASSERT_WITH_CODE(
-                       (fiji_power_state->performance_level_count <=
-                                       hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
-                       "Performance levels exceeds Driver limit!",
-                       return -1);
-
-       /* Performance levels are arranged from low to high. */
-       performance_level->memory_clock = mclk_dep_table->entries
-                       [state_entry->ucMemoryClockIndexLow].ulMclk;
-       performance_level->engine_clock = sclk_dep_table->entries
-                       [state_entry->ucEngineClockIndexLow].ulSclk;
-       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
-                       state_entry->ucPCIEGenLow);
-       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
-                       state_entry->ucPCIELaneHigh);
-
-       performance_level = &(fiji_power_state->performance_levels
-                       [fiji_power_state->performance_level_count++]);
-       performance_level->memory_clock = mclk_dep_table->entries
-                       [state_entry->ucMemoryClockIndexHigh].ulMclk;
-       performance_level->engine_clock = sclk_dep_table->entries
-                       [state_entry->ucEngineClockIndexHigh].ulSclk;
-       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
-                       state_entry->ucPCIEGenHigh);
-       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
-                       state_entry->ucPCIELaneHigh);
-
-       return 0;
-}
-
-static int fiji_get_pp_table_entry(struct pp_hwmgr *hwmgr,
-               unsigned long entry_index, struct pp_power_state *state)
-{
-       int result;
-       struct fiji_power_state *ps;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                       table_info->vdd_dep_on_mclk;
-
-       state->hardware.magic = PHM_VIslands_Magic;
-
-       ps = (struct fiji_power_state *)(&state->hardware);
-
-       result = tonga_get_powerplay_table_entry(hwmgr, entry_index, state,
-                       fiji_get_pp_table_entry_callback_func);
-
-       /* This is the earliest time we have all the dependency table and the VBIOS boot state
-        * as PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot state
-        * if there is only one VDDCI/MCLK level, check if it's the same as VBIOS boot state
-        */
-       if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
-               if (dep_mclk_table->entries[0].clk !=
-                               data->vbios_boot_state.mclk_bootup_value)
-                       printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot MCLK level");
-               if (dep_mclk_table->entries[0].vddci !=
-                               data->vbios_boot_state.vddci_bootup_value)
-                       printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot VDDCI level");
-       }
-
-       /* set DC compatible flag if this state supports DC */
-       if (!state->validation.disallowOnDC)
-               ps->dc_compatible = true;
-
-       if (state->classification.flags & PP_StateClassificationFlag_ACPI)
-               data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
-
-       ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
-       ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
-
-       if (!result) {
-               uint32_t i;
-
-               switch (state->classification.ui_label) {
-               case PP_StateUILabel_Performance:
-                       data->use_pcie_performance_levels = true;
-
-                       for (i = 0; i < ps->performance_level_count; i++) {
-                               if (data->pcie_gen_performance.max <
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.max =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_performance.min >
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.min =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_performance.max <
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.max =
-                                                       ps->performance_levels[i].pcie_lane;
-
-                               if (data->pcie_lane_performance.min >
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.min =
-                                                       ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               case PP_StateUILabel_Battery:
-                       data->use_pcie_power_saving_levels = true;
-
-                       for (i = 0; i < ps->performance_level_count; i++) {
-                               if (data->pcie_gen_power_saving.max <
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.max =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_power_saving.min >
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.min =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_power_saving.max <
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.max =
-                                                       ps->performance_levels[i].pcie_lane;
-
-                               if (data->pcie_lane_power_saving.min >
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.min =
-                                                       ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-static int fiji_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
-                               struct pp_power_state  *request_ps,
-                       const struct pp_power_state *current_ps)
-{
-       struct fiji_power_state *fiji_ps =
-                               cast_phw_fiji_power_state(&request_ps->hardware);
-       uint32_t sclk;
-       uint32_t mclk;
-       struct PP_Clocks minimum_clocks = {0};
-       bool disable_mclk_switching;
-       bool disable_mclk_switching_for_frame_lock;
-       struct cgs_display_info info = {0};
-       const struct phm_clock_and_voltage_limits *max_limits;
-       uint32_t i;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int32_t count;
-       int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
-
-       data->battery_state = (PP_StateUILabel_Battery ==
-                       request_ps->classification.ui_label);
-
-       PP_ASSERT_WITH_CODE(fiji_ps->performance_level_count == 2,
-                                "VI should always have 2 performance levels",);
-
-       max_limits = (PP_PowerSource_AC == hwmgr->power_source) ?
-                       &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
-                       &(hwmgr->dyn_state.max_clock_voltage_on_dc);
-
-       /* Cap clock DPM tables at DC MAX if it is in DC. */
-       if (PP_PowerSource_DC == hwmgr->power_source) {
-               for (i = 0; i < fiji_ps->performance_level_count; i++) {
-                       if (fiji_ps->performance_levels[i].memory_clock > max_limits->mclk)
-                               fiji_ps->performance_levels[i].memory_clock = max_limits->mclk;
-                       if (fiji_ps->performance_levels[i].engine_clock > max_limits->sclk)
-                               fiji_ps->performance_levels[i].engine_clock = max_limits->sclk;
-               }
-       }
-
-       fiji_ps->vce_clks.evclk = hwmgr->vce_arbiter.evclk;
-       fiji_ps->vce_clks.ecclk = hwmgr->vce_arbiter.ecclk;
-
-       fiji_ps->acp_clk = hwmgr->acp_arbiter.acpclk;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       /*TO DO result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
-
-       /* TO DO GetMinClockSettings(hwmgr->pPECI, &minimum_clocks); */
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StablePState)) {
-               max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
-               stable_pstate_sclk = (max_limits->sclk * 75) / 100;
-
-               for (count = table_info->vdd_dep_on_sclk->count - 1;
-                               count >= 0; count--) {
-                       if (stable_pstate_sclk >=
-                                       table_info->vdd_dep_on_sclk->entries[count].clk) {
-                               stable_pstate_sclk =
-                                               table_info->vdd_dep_on_sclk->entries[count].clk;
-                               break;
-                       }
-               }
-
-               if (count < 0)
-                       stable_pstate_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
-
-               stable_pstate_mclk = max_limits->mclk;
-
-               minimum_clocks.engineClock = stable_pstate_sclk;
-               minimum_clocks.memoryClock = stable_pstate_mclk;
-       }
-
-       if (minimum_clocks.engineClock < hwmgr->gfx_arbiter.sclk)
-               minimum_clocks.engineClock = hwmgr->gfx_arbiter.sclk;
-
-       if (minimum_clocks.memoryClock < hwmgr->gfx_arbiter.mclk)
-               minimum_clocks.memoryClock = hwmgr->gfx_arbiter.mclk;
-
-       fiji_ps->sclk_threshold = hwmgr->gfx_arbiter.sclk_threshold;
-
-       if (0 != hwmgr->gfx_arbiter.sclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.sclk_over_drive <=
-                               hwmgr->platform_descriptor.overdriveLimit.engineClock),
-                               "Overdrive sclk exceeds limit",
-                               hwmgr->gfx_arbiter.sclk_over_drive =
-                                               hwmgr->platform_descriptor.overdriveLimit.engineClock);
-
-               if (hwmgr->gfx_arbiter.sclk_over_drive >= hwmgr->gfx_arbiter.sclk)
-                       fiji_ps->performance_levels[1].engine_clock =
-                                       hwmgr->gfx_arbiter.sclk_over_drive;
-       }
-
-       if (0 != hwmgr->gfx_arbiter.mclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.mclk_over_drive <=
-                               hwmgr->platform_descriptor.overdriveLimit.memoryClock),
-                               "Overdrive mclk exceeds limit",
-                               hwmgr->gfx_arbiter.mclk_over_drive =
-                                               hwmgr->platform_descriptor.overdriveLimit.memoryClock);
-
-               if (hwmgr->gfx_arbiter.mclk_over_drive >= hwmgr->gfx_arbiter.mclk)
-                       fiji_ps->performance_levels[1].memory_clock =
-                                       hwmgr->gfx_arbiter.mclk_over_drive;
-       }
-
-       disable_mclk_switching_for_frame_lock = phm_cap_enabled(
-                                   hwmgr->platform_descriptor.platformCaps,
-                                   PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
-
-       disable_mclk_switching = (1 < info.display_count) ||
-                                   disable_mclk_switching_for_frame_lock;
-
-       sclk = fiji_ps->performance_levels[0].engine_clock;
-       mclk = fiji_ps->performance_levels[0].memory_clock;
-
-       if (disable_mclk_switching)
-               mclk = fiji_ps->performance_levels
-               [fiji_ps->performance_level_count - 1].memory_clock;
-
-       if (sclk < minimum_clocks.engineClock)
-               sclk = (minimum_clocks.engineClock > max_limits->sclk) ?
-                               max_limits->sclk : minimum_clocks.engineClock;
-
-       if (mclk < minimum_clocks.memoryClock)
-               mclk = (minimum_clocks.memoryClock > max_limits->mclk) ?
-                               max_limits->mclk : minimum_clocks.memoryClock;
-
-       fiji_ps->performance_levels[0].engine_clock = sclk;
-       fiji_ps->performance_levels[0].memory_clock = mclk;
-
-       fiji_ps->performance_levels[1].engine_clock =
-               (fiji_ps->performance_levels[1].engine_clock >=
-                               fiji_ps->performance_levels[0].engine_clock) ?
-                                               fiji_ps->performance_levels[1].engine_clock :
-                                               fiji_ps->performance_levels[0].engine_clock;
-
-       if (disable_mclk_switching) {
-               if (mclk < fiji_ps->performance_levels[1].memory_clock)
-                       mclk = fiji_ps->performance_levels[1].memory_clock;
-
-               fiji_ps->performance_levels[0].memory_clock = mclk;
-               fiji_ps->performance_levels[1].memory_clock = mclk;
-       } else {
-               if (fiji_ps->performance_levels[1].memory_clock <
-                               fiji_ps->performance_levels[0].memory_clock)
-                       fiji_ps->performance_levels[1].memory_clock =
-                                       fiji_ps->performance_levels[0].memory_clock;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StablePState)) {
-               for (i = 0; i < fiji_ps->performance_level_count; i++) {
-                       fiji_ps->performance_levels[i].engine_clock = stable_pstate_sclk;
-                       fiji_ps->performance_levels[i].memory_clock = stable_pstate_mclk;
-                       fiji_ps->performance_levels[i].pcie_gen = data->pcie_gen_performance.max;
-                       fiji_ps->performance_levels[i].pcie_lane = data->pcie_gen_performance.max;
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       const struct fiji_power_state *fiji_ps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       uint32_t sclk = fiji_ps->performance_levels
-                       [fiji_ps->performance_level_count - 1].engine_clock;
-       struct fiji_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       uint32_t mclk = fiji_ps->performance_levels
-                       [fiji_ps->performance_level_count - 1].memory_clock;
-       uint32_t i;
-       struct cgs_display_info info = {0};
-
-       data->need_update_smu7_dpm_table = 0;
-
-       for (i = 0; i < sclk_table->count; i++) {
-               if (sclk == sclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= sclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
-       else {
-               if(data->display_timing.min_clock_in_sr !=
-                       hwmgr->display_config.min_core_set_clock_in_sr)
-                       data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
-       }
-
-       for (i = 0; i < mclk_table->count; i++) {
-               if (mclk == mclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= mclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
-
-       return 0;
-}
-
-static uint16_t fiji_get_maximum_link_speed(struct pp_hwmgr *hwmgr,
-               const struct fiji_power_state *fiji_ps)
-{
-       uint32_t i;
-       uint32_t sclk, max_sclk = 0;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_dpm_table *dpm_table = &data->dpm_table;
-
-       for (i = 0; i < fiji_ps->performance_level_count; i++) {
-               sclk = fiji_ps->performance_levels[i].engine_clock;
-               if (max_sclk < sclk)
-                       max_sclk = sclk;
-       }
-
-       for (i = 0; i < dpm_table->sclk_table.count; i++) {
-               if (dpm_table->sclk_table.dpm_levels[i].value == max_sclk)
-                       return (uint16_t) ((i >= dpm_table->pcie_speed_table.count) ?
-                                       dpm_table->pcie_speed_table.dpm_levels
-                                       [dpm_table->pcie_speed_table.count - 1].value :
-                                       dpm_table->pcie_speed_table.dpm_levels[i].value);
-       }
-
-       return 0;
-}
-
-static int fiji_request_link_speed_change_before_state_change(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_power_state *fiji_nps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-       const struct fiji_power_state *fiji_cps =
-                       cast_const_phw_fiji_power_state(states->pcurrent_state);
-
-       uint16_t target_link_speed = fiji_get_maximum_link_speed(hwmgr, fiji_nps);
-       uint16_t current_link_speed;
-
-       if (data->force_pcie_gen == PP_PCIEGenInvalid)
-               current_link_speed = fiji_get_maximum_link_speed(hwmgr, fiji_cps);
-       else
-               current_link_speed = data->force_pcie_gen;
-
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-       data->pspp_notify_required = false;
-       if (target_link_speed > current_link_speed) {
-               switch(target_link_speed) {
-               case PP_PCIEGen3:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN3, false))
-                               break;
-                       data->force_pcie_gen = PP_PCIEGen2;
-                       if (current_link_speed == PP_PCIEGen2)
-                               break;
-               case PP_PCIEGen2:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN2, false))
-                               break;
-               default:
-                       data->force_pcie_gen = fiji_get_current_pcie_speed(hwmgr);
-                       break;
-               }
-       } else {
-               if (target_link_speed < current_link_speed)
-                       data->pspp_notify_required = true;
-       }
-
-       return 0;
-}
-
-static int fiji_freeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-               PP_ASSERT_WITH_CODE(fiji_is_dpm_running(hwmgr),
-                                   "Trying to freeze SCLK DPM when DPM is disabled",
-                                   );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_FreezeLevel),
-                               "Failed to freeze SCLK DPM during FreezeSclkMclkDPM Function!",
-                               return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-                DPMTABLE_OD_UPDATE_MCLK)) {
-               PP_ASSERT_WITH_CODE(fiji_is_dpm_running(hwmgr),
-                                   "Trying to freeze MCLK DPM when DPM is disabled",
-                                   );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MCLKDPM_FreezeLevel),
-                               "Failed to freeze MCLK DPM during FreezeSclkMclkDPM Function!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-static int fiji_populate_and_upload_sclk_mclk_dpm_levels(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result = 0;
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       const struct fiji_power_state *fiji_ps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t sclk = fiji_ps->performance_levels
-                       [fiji_ps->performance_level_count - 1].engine_clock;
-       uint32_t mclk = fiji_ps->performance_levels
-                       [fiji_ps->performance_level_count - 1].memory_clock;
-       struct fiji_dpm_table *dpm_table = &data->dpm_table;
-
-       struct fiji_dpm_table *golden_dpm_table = &data->golden_dpm_table;
-       uint32_t dpm_count, clock_percent;
-       uint32_t i;
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
-               dpm_table->sclk_table.dpm_levels
-               [dpm_table->sclk_table.count - 1].value = sclk;
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_OD6PlusinACSupport) ||
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_OD6PlusinDCSupport)) {
-               /* Need to do calculation based on the golden DPM table
-                * as the Heatmap GPU Clock axis is also based on the default values
-                */
-                       PP_ASSERT_WITH_CODE(
-                               (golden_dpm_table->sclk_table.dpm_levels
-                                               [golden_dpm_table->sclk_table.count - 1].value != 0),
-                               "Divide by 0!",
-                               return -1);
-                       dpm_count = dpm_table->sclk_table.count < 2 ?
-                                       0 : dpm_table->sclk_table.count - 2;
-                       for (i = dpm_count; i > 1; i--) {
-                               if (sclk > golden_dpm_table->sclk_table.dpm_levels
-                                               [golden_dpm_table->sclk_table.count-1].value) {
-                                       clock_percent =
-                                               ((sclk - golden_dpm_table->sclk_table.dpm_levels
-                                                       [golden_dpm_table->sclk_table.count-1].value) * 100) /
-                                               golden_dpm_table->sclk_table.dpm_levels
-                                                       [golden_dpm_table->sclk_table.count-1].value;
-
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value +
-                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
-                                                               clock_percent)/100;
-
-                               } else if (golden_dpm_table->sclk_table.dpm_levels
-                                               [dpm_table->sclk_table.count-1].value > sclk) {
-                                       clock_percent =
-                                               ((golden_dpm_table->sclk_table.dpm_levels
-                                               [golden_dpm_table->sclk_table.count - 1].value - sclk) *
-                                                               100) /
-                                               golden_dpm_table->sclk_table.dpm_levels
-                                                       [golden_dpm_table->sclk_table.count-1].value;
-
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value -
-                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
-                                                                       clock_percent) / 100;
-                               } else
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
-               dpm_table->mclk_table.dpm_levels
-                       [dpm_table->mclk_table.count - 1].value = mclk;
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_OD6PlusinACSupport) ||
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_OD6PlusinDCSupport)) {
-
-                       PP_ASSERT_WITH_CODE(
-                                       (golden_dpm_table->mclk_table.dpm_levels
-                                               [golden_dpm_table->mclk_table.count-1].value != 0),
-                                       "Divide by 0!",
-                                       return -1);
-                       dpm_count = dpm_table->mclk_table.count < 2 ?
-                                       0 : dpm_table->mclk_table.count - 2;
-                       for (i = dpm_count; i > 1; i--) {
-                               if (mclk > golden_dpm_table->mclk_table.dpm_levels
-                                               [golden_dpm_table->mclk_table.count-1].value) {
-                                       clock_percent = ((mclk -
-                                                       golden_dpm_table->mclk_table.dpm_levels
-                                                       [golden_dpm_table->mclk_table.count-1].value) * 100) /
-                                                       golden_dpm_table->mclk_table.dpm_levels
-                                                       [golden_dpm_table->mclk_table.count-1].value;
-
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value +
-                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
-                                                                       clock_percent) / 100;
-
-                               } else if (golden_dpm_table->mclk_table.dpm_levels
-                                               [dpm_table->mclk_table.count-1].value > mclk) {
-                                       clock_percent = ((golden_dpm_table->mclk_table.dpm_levels
-                                                       [golden_dpm_table->mclk_table.count-1].value - mclk) * 100) /
-                                                                       golden_dpm_table->mclk_table.dpm_levels
-                                                                       [golden_dpm_table->mclk_table.count-1].value;
-
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value -
-                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
-                                                                       clock_percent) / 100;
-                               } else
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK)) {
-               result = fiji_populate_all_graphic_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
-                               return result);
-       }
-
-       if (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_MCLK + DPMTABLE_UPDATE_MCLK)) {
-               /*populate MCLK dpm table to SMU7 */
-               result = fiji_populate_all_memory_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
-                               return result);
-       }
-
-       return result;
-}
-
-static int fiji_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
-                         struct fiji_single_dpm_table * dpm_table,
-                            uint32_t low_limit, uint32_t high_limit)
-{
-       uint32_t i;
-
-       for (i = 0; i < dpm_table->count; i++) {
-               if ((dpm_table->dpm_levels[i].value < low_limit) ||
-                   (dpm_table->dpm_levels[i].value > high_limit))
-                       dpm_table->dpm_levels[i].enabled = false;
-               else
-                       dpm_table->dpm_levels[i].enabled = true;
-       }
-       return 0;
-}
-
-static int fiji_trim_dpm_states(struct pp_hwmgr *hwmgr,
-               const struct fiji_power_state *fiji_ps)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t high_limit_count;
-
-       PP_ASSERT_WITH_CODE((fiji_ps->performance_level_count >= 1),
-                       "power state did not have any performance level",
-                       return -1);
-
-       high_limit_count = (1 == fiji_ps->performance_level_count) ? 0 : 1;
-
-       fiji_trim_single_dpm_states(hwmgr,
-                       &(data->dpm_table.sclk_table),
-                       fiji_ps->performance_levels[0].engine_clock,
-                       fiji_ps->performance_levels[high_limit_count].engine_clock);
-
-       fiji_trim_single_dpm_states(hwmgr,
-                       &(data->dpm_table.mclk_table),
-                       fiji_ps->performance_levels[0].memory_clock,
-                       fiji_ps->performance_levels[high_limit_count].memory_clock);
-
-       return 0;
-}
-
-static int fiji_generate_dpm_level_enable_mask(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result;
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_power_state *fiji_ps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-
-       result = fiji_trim_dpm_states(hwmgr, fiji_ps);
-       if (result)
-               return result;
-
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&data->dpm_table.sclk_table);
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&data->dpm_table.mclk_table);
-       data->last_mclk_dpm_enable_mask =
-                       data->dpm_level_enable_mask.mclk_dpm_enable_mask;
-
-       if (data->uvd_enabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask & 1)
-                       data->dpm_level_enable_mask.mclk_dpm_enable_mask &= 0xFFFFFFFE;
-       }
-
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
-                       fiji_get_dpm_level_enable_mask_value(&data->dpm_table.pcie_speed_table);
-
-       return 0;
-}
-
-int fiji_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
-                                 (PPSMC_Msg)PPSMC_MSG_UVDDPM_Enable :
-                                 (PPSMC_Msg)PPSMC_MSG_UVDDPM_Disable);
-}
-
-int fiji_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable?
-                       PPSMC_MSG_VCEDPM_Enable :
-                       PPSMC_MSG_VCEDPM_Disable);
-}
-
-int fiji_enable_disable_samu_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable?
-                       PPSMC_MSG_SAMUDPM_Enable :
-                       PPSMC_MSG_SAMUDPM_Disable);
-}
-
-int fiji_enable_disable_acp_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable?
-                       PPSMC_MSG_ACPDPM_Enable :
-                       PPSMC_MSG_ACPDPM_Disable);
-}
-
-int fiji_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               data->smc_state_table.UvdBootLevel = 0;
-               if (table_info->mm_dep_table->count > 0)
-                       data->smc_state_table.UvdBootLevel =
-                                       (uint8_t) (table_info->mm_dep_table->count - 1);
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU73_Discrete_DpmTable, UvdBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0x00FFFFFF;
-               mm_boot_level_value |= data->smc_state_table.UvdBootLevel << 24;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_UVDDPM) ||
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_UVDDPM_SetEnabledMask,
-                                       (uint32_t)(1 << data->smc_state_table.UvdBootLevel));
-       }
-
-       return fiji_enable_disable_uvd_dpm(hwmgr, !bgate);
-}
-
-int fiji_update_vce_dpm(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_power_state *fiji_nps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-       const struct fiji_power_state *fiji_cps =
-                       cast_const_phw_fiji_power_state(states->pcurrent_state);
-
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (fiji_nps->vce_clks.evclk >0 &&
-       (fiji_cps == NULL || fiji_cps->vce_clks.evclk == 0)) {
-               data->smc_state_table.VceBootLevel =
-                               (uint8_t) (table_info->mm_dep_table->count - 1);
-
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU73_Discrete_DpmTable, VceBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFF00FFFF;
-               mm_boot_level_value |= data->smc_state_table.VceBootLevel << 16;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState)) {
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_VCEDPM_SetEnabledMask,
-                                       (uint32_t)1 << data->smc_state_table.VceBootLevel);
-
-                       fiji_enable_disable_vce_dpm(hwmgr, true);
-               } else if (fiji_nps->vce_clks.evclk == 0 &&
-                               fiji_cps != NULL &&
-                               fiji_cps->vce_clks.evclk > 0)
-                       fiji_enable_disable_vce_dpm(hwmgr, false);
-       }
-
-       return 0;
-}
-
-int fiji_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               data->smc_state_table.SamuBootLevel =
-                               (uint8_t) (table_info->mm_dep_table->count - 1);
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU73_Discrete_DpmTable, SamuBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFFFFFF00;
-               mm_boot_level_value |= data->smc_state_table.SamuBootLevel << 0;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SAMUDPM_SetEnabledMask,
-                                       (uint32_t)(1 << data->smc_state_table.SamuBootLevel));
-       }
-
-       return fiji_enable_disable_samu_dpm(hwmgr, !bgate);
-}
-
-int fiji_update_acp_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               data->smc_state_table.AcpBootLevel =
-                               (uint8_t) (table_info->mm_dep_table->count - 1);
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU73_Discrete_DpmTable, AcpBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFFFF00FF;
-               mm_boot_level_value |= data->smc_state_table.AcpBootLevel << 8;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_ACPDPM_SetEnabledMask,
-                                               (uint32_t)(1 << data->smc_state_table.AcpBootLevel));
-       }
-
-       return fiji_enable_disable_acp_dpm(hwmgr, !bgate);
-}
-
-static int fiji_update_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       int result = 0;
-       uint32_t low_sclk_interrupt_threshold = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkThrottleLowNotification)
-               && (hwmgr->gfx_arbiter.sclk_threshold !=
-                               data->low_sclk_interrupt_threshold)) {
-               data->low_sclk_interrupt_threshold =
-                               hwmgr->gfx_arbiter.sclk_threshold;
-               low_sclk_interrupt_threshold =
-                               data->low_sclk_interrupt_threshold;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
-
-               result = fiji_copy_bytes_to_smc(
-                               hwmgr->smumgr,
-                               data->dpm_table_start +
-                               offsetof(SMU73_Discrete_DpmTable,
-                                       LowSclkInterruptThreshold),
-                               (uint8_t *)&low_sclk_interrupt_threshold,
-                               sizeof(uint32_t),
-                               data->sram_end);
-       }
-
-       return result;
-}
-
-static int fiji_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
-               return fiji_program_memory_timing_parameters(hwmgr);
-
-       return 0;
-}
-
-static int fiji_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-
-               PP_ASSERT_WITH_CODE(fiji_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze SCLK DPM when DPM is disabled",
-                                   );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                       "Failed to unfreeze SCLK DPM during UnFreezeSclkMclkDPM Function!",
-                       return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
-
-               PP_ASSERT_WITH_CODE(fiji_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze MCLK DPM when DPM is disabled",
-                                   );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                   "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!",
-                   return -1);
-       }
-
-       data->need_update_smu7_dpm_table = 0;
-
-       return 0;
-}
-
-/* Look up the voltaged based on DAL's requested level.
- * and then send the requested VDDC voltage to SMC
- */
-static void fiji_apply_dal_minimum_voltage_request(struct pp_hwmgr *hwmgr)
-{
-       return;
-}
-
-int fiji_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* Apply minimum voltage based on DAL's request level */
-       fiji_apply_dal_minimum_voltage_request(hwmgr);
-
-       if (0 == data->sclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this,
-                *  we should skip this message.
-                */
-               if (!fiji_is_dpm_running(hwmgr))
-                       printk(KERN_ERR "[ powerplay ] "
-                                       "Trying to set Enable Mask when DPM is disabled \n");
-
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == result),
-                               "Set Sclk Dpm enable Mask failed", return -1);
-               }
-       }
-
-       if (0 == data->mclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this,
-                *  we should skip this message.
-                */
-               if (!fiji_is_dpm_running(hwmgr))
-                       printk(KERN_ERR "[ powerplay ]"
-                                       " Trying to set Enable Mask when DPM is disabled \n");
-
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == result),
-                               "Set Mclk Dpm enable Mask failed", return -1);
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_notify_link_speed_change_after_state_change(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_power_state *fiji_ps =
-                       cast_const_phw_fiji_power_state(states->pnew_state);
-       uint16_t target_link_speed = fiji_get_maximum_link_speed(hwmgr, fiji_ps);
-       uint8_t  request;
-
-       if (data->pspp_notify_required) {
-               if (target_link_speed == PP_PCIEGen3)
-                       request = PCIE_PERF_REQ_GEN3;
-               else if (target_link_speed == PP_PCIEGen2)
-                       request = PCIE_PERF_REQ_GEN2;
-               else
-                       request = PCIE_PERF_REQ_GEN1;
-
-               if(request == PCIE_PERF_REQ_GEN1 &&
-                               fiji_get_current_pcie_speed(hwmgr) > 0)
-                       return 0;
-
-               if (acpi_pcie_perf_request(hwmgr->device, request, false)) {
-                       if (PP_PCIEGen2 == target_link_speed)
-                               printk("PSPP request to switch to Gen2 from Gen3 Failed!");
-                       else
-                               printk("PSPP request to switch to Gen1 from Gen2 Failed!");
-               }
-       }
-
-       return 0;
-}
-
-static int fiji_set_power_state_tasks(struct pp_hwmgr *hwmgr,
-               const void *input)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = fiji_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to find DPM states clocks in DPM table!",
-                       result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result =
-                       fiji_request_link_speed_change_before_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to request link speed change before state change!",
-                               result = tmp_result);
-       }
-
-       tmp_result = fiji_freeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to freeze SCLK MCLK DPM!", result = tmp_result);
-
-       tmp_result = fiji_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to populate and upload SCLK MCLK DPM levels!",
-                       result = tmp_result);
-
-       tmp_result = fiji_generate_dpm_level_enable_mask(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to generate DPM level enabled mask!",
-                       result = tmp_result);
-
-       tmp_result = fiji_update_vce_dpm(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to update VCE DPM!",
-                       result = tmp_result);
-
-       tmp_result = fiji_update_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to update SCLK threshold!",
-                       result = tmp_result);
-
-       tmp_result = fiji_program_mem_timing_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program memory timing parameters!",
-                       result = tmp_result);
-
-       tmp_result = fiji_unfreeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to unfreeze SCLK MCLK DPM!",
-                       result = tmp_result);
-
-       tmp_result = fiji_upload_dpm_level_enable_mask(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to upload DPM level enabled mask!",
-                       result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result =
-                       fiji_notify_link_speed_change_after_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to notify link speed change after state change!",
-                               result = tmp_result);
-       }
-
-       return result;
-}
-
-static int fiji_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct fiji_power_state  *fiji_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       fiji_ps = cast_phw_fiji_power_state(&ps->hardware);
-
-       if (low)
-               return fiji_ps->performance_levels[0].engine_clock;
-       else
-               return fiji_ps->performance_levels
-                               [fiji_ps->performance_level_count-1].engine_clock;
-}
-
-static int fiji_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct fiji_power_state  *fiji_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       fiji_ps = cast_phw_fiji_power_state(&ps->hardware);
-
-       if (low)
-               return fiji_ps->performance_levels[0].memory_clock;
-       else
-               return fiji_ps->performance_levels
-                               [fiji_ps->performance_level_count-1].memory_clock;
-}
-
-static void fiji_print_current_perforce_level(
-               struct pp_hwmgr *hwmgr, struct seq_file *m)
-{
-       uint32_t sclk, mclk, activity_percent = 0;
-       uint32_t offset;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
-
-       sclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
-
-       mclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-       seq_printf(m, "\n [  mclk  ]: %u MHz\n\n [  sclk  ]: %u MHz\n",
-                       mclk / 100, sclk / 100);
-
-       offset = data->soft_regs_start + offsetof(SMU73_SoftRegisters, AverageGraphicsActivity);
-       activity_percent = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset);
-       activity_percent += 0x80;
-       activity_percent >>= 8;
-
-       seq_printf(m, "\n [GPU load]: %u%%\n\n", activity_percent > 100 ? 100 : activity_percent);
-
-       seq_printf(m, "uvd    %sabled\n", data->uvd_power_gated ? "dis" : "en");
-
-       seq_printf(m, "vce    %sabled\n", data->vce_power_gated ? "dis" : "en");
-}
-
-static int fiji_program_display_gap(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t num_active_displays = 0;
-       uint32_t display_gap = cgs_read_ind_register(hwmgr->device,
-                       CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
-       uint32_t display_gap2;
-       uint32_t pre_vbi_time_in_us;
-       uint32_t frame_time_in_us;
-       uint32_t ref_clock;
-       uint32_t refresh_rate = 0;
-       struct cgs_display_info info = {0};
-       struct cgs_mode_info mode_info;
-
-       info.mode_info = &mode_info;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-       num_active_displays = info.display_count;
-
-       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
-                       DISP_GAP, (num_active_displays > 0)?
-                       DISPLAY_GAP_VBLANK_OR_WM : DISPLAY_GAP_IGNORE);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_DISPLAY_GAP_CNTL, display_gap);
-
-       ref_clock = mode_info.ref_clock;
-       refresh_rate = mode_info.refresh_rate;
-
-       if (refresh_rate == 0)
-               refresh_rate = 60;
-
-       frame_time_in_us = 1000000 / refresh_rate;
-
-       pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
-       display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_DISPLAY_GAP_CNTL2, display_gap2);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       data->soft_regs_start +
-                       offsetof(SMU73_SoftRegisters, PreVBlankGap), 0x64);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       data->soft_regs_start +
-                       offsetof(SMU73_SoftRegisters, VBlankTimeout),
-                       (frame_time_in_us - pre_vbi_time_in_us));
-
-       if (num_active_displays == 1)
-               tonga_notify_smc_display_change(hwmgr, true);
-
-       return 0;
-}
-
-int fiji_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
-{
-       return fiji_program_display_gap(hwmgr);
-}
-
-static int fiji_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr,
-               uint16_t us_max_fan_pwm)
-{
-       hwmgr->thermal_controller.
-       advanceFanControlParameters.usMaxFanPWM = us_max_fan_pwm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                       PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm);
-}
-
-static int fiji_set_max_fan_rpm_output(struct pp_hwmgr *hwmgr,
-               uint16_t us_max_fan_rpm)
-{
-       hwmgr->thermal_controller.
-       advanceFanControlParameters.usMaxFanRPM = us_max_fan_rpm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                       PPSMC_MSG_SetFanRpmMax, us_max_fan_rpm);
-}
-
-int fiji_dpm_set_interrupt_state(void *private_data,
-                                        unsigned src_id, unsigned type,
-                                        int enabled)
-{
-       uint32_t cg_thermal_int;
-       struct pp_hwmgr *hwmgr = ((struct pp_eventmgr *)private_data)->hwmgr;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       switch (type) {
-       case AMD_THERMAL_IRQ_LOW_TO_HIGH:
-               if (enabled) {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               } else {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               }
-               break;
-
-       case AMD_THERMAL_IRQ_HIGH_TO_LOW:
-               if (enabled) {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               } else {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               }
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
-int fiji_register_internal_thermal_interrupt(struct pp_hwmgr *hwmgr,
-                                       const void *thermal_interrupt_info)
-{
-       int result;
-       const struct pp_interrupt_registration_info *info =
-                       (const struct pp_interrupt_registration_info *)
-                       thermal_interrupt_info;
-
-       if (info == NULL)
-               return -EINVAL;
-
-       result = cgs_add_irq_source(hwmgr->device, 230, AMD_THERMAL_IRQ_LAST,
-                               fiji_dpm_set_interrupt_state,
-                               info->call_back, info->context);
-
-       if (result)
-               return -EINVAL;
-
-       result = cgs_add_irq_source(hwmgr->device, 231, AMD_THERMAL_IRQ_LAST,
-                               fiji_dpm_set_interrupt_state,
-                               info->call_back, info->context);
-
-       if (result)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int fiji_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-       if (mode) {
-               /* stop auto-manage */
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl))
-                       fiji_fan_ctrl_stop_smc_fan_control(hwmgr);
-               fiji_fan_ctrl_set_static_mode(hwmgr, mode);
-       } else
-               /* restart auto-manage */
-               fiji_fan_ctrl_reset_fan_speed_to_default(hwmgr);
-
-       return 0;
-}
-
-static int fiji_get_fan_control_mode(struct pp_hwmgr *hwmgr)
-{
-       if (hwmgr->fan_ctrl_is_in_default_mode)
-               return hwmgr->fan_ctrl_default_mode;
-       else
-               return PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, FDO_PWM_MODE);
-}
-
-static int fiji_force_clock_level(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, uint32_t mask)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
-               return -EINVAL;
-
-       switch (type) {
-       case PP_SCLK:
-               if (!data->sclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask & mask);
-               break;
-
-       case PP_MCLK:
-               if (!data->mclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask & mask);
-               break;
-
-       case PP_PCIE:
-       {
-               uint32_t tmp = mask & data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-               uint32_t level = 0;
-
-               while (tmp >>= 1)
-                       level++;
-
-               if (!data->pcie_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_PCIeDPM_ForceLevel,
-                                       level);
-               break;
-       }
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static int fiji_print_clock_levels(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, char *buf)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct fiji_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct fiji_single_dpm_table *pcie_table = &(data->dpm_table.pcie_speed_table);
-       int i, now, size = 0;
-       uint32_t clock, pcie_speed;
-
-       switch (type) {
-       case PP_SCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < sclk_table->count; i++) {
-                       if (clock > sclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < sclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, sclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_MCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < mclk_table->count; i++) {
-                       if (clock > mclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < mclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, mclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_PCIE:
-               pcie_speed = fiji_get_current_pcie_speed(hwmgr);
-               for (i = 0; i < pcie_table->count; i++) {
-                       if (pcie_speed != pcie_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < pcie_table->count; i++)
-                       size += sprintf(buf + size, "%d: %s %s\n", i,
-                                       (pcie_table->dpm_levels[i].value == 0) ? "2.5GB, x1" :
-                                       (pcie_table->dpm_levels[i].value == 1) ? "5.0GB, x16" :
-                                       (pcie_table->dpm_levels[i].value == 2) ? "8.0GB, x16" : "",
-                                       (i == now) ? "*" : "");
-               break;
-       default:
-               break;
-       }
-       return size;
-}
-
-static inline bool fiji_are_power_levels_equal(const struct fiji_performance_level *pl1,
-                                                          const struct fiji_performance_level *pl2)
-{
-       return ((pl1->memory_clock == pl2->memory_clock) &&
-                 (pl1->engine_clock == pl2->engine_clock) &&
-                 (pl1->pcie_gen == pl2->pcie_gen) &&
-                 (pl1->pcie_lane == pl2->pcie_lane));
-}
-
-int fiji_check_states_equal(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *pstate1, const struct pp_hw_power_state *pstate2, bool *equal)
-{
-       const struct fiji_power_state *psa = cast_const_phw_fiji_power_state(pstate1);
-       const struct fiji_power_state *psb = cast_const_phw_fiji_power_state(pstate2);
-       int i;
-
-       if (equal == NULL || psa == NULL || psb == NULL)
-               return -EINVAL;
-
-       /* If the two states don't even have the same number of performance levels they cannot be the same state. */
-       if (psa->performance_level_count != psb->performance_level_count) {
-               *equal = false;
-               return 0;
-       }
-
-       for (i = 0; i < psa->performance_level_count; i++) {
-               if (!fiji_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
-                       /* If we have found even one performance level pair that is different the states are different. */
-                       *equal = false;
-                       return 0;
-               }
-       }
-
-       /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
-       *equal = ((psa->uvd_clks.vclk == psb->uvd_clks.vclk) && (psa->uvd_clks.dclk == psb->uvd_clks.dclk));
-       *equal &= ((psa->vce_clks.evclk == psb->vce_clks.evclk) && (psa->vce_clks.ecclk == psb->vce_clks.ecclk));
-       *equal &= (psa->sclk_threshold == psb->sclk_threshold);
-       *equal &= (psa->acp_clk == psb->acp_clk);
-
-       return 0;
-}
-
-bool fiji_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       bool is_update_required = false;
-       struct cgs_display_info info = {0,0,NULL};
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               is_update_required = true;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
-               if(hwmgr->display_config.min_core_set_clock_in_sr != data->display_timing.min_clock_in_sr)
-                       is_update_required = true;
-       }
-
-       return is_update_required;
-}
-
-static int fiji_get_sclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct fiji_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       int value;
-
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return value;
-}
-
-static int fiji_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       struct pp_power_state  *ps;
-       struct fiji_power_state  *fiji_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       fiji_ps = cast_phw_fiji_power_state(&ps->hardware);
-
-       fiji_ps->performance_levels[fiji_ps->performance_level_count - 1].engine_clock =
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
-                       value / 100 +
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return 0;
-}
-
-static int fiji_get_mclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct fiji_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       int value;
-
-       value = (mclk_table->dpm_levels[mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return value;
-}
-
-static int fiji_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct fiji_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       struct pp_power_state  *ps;
-       struct fiji_power_state  *fiji_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       fiji_ps = cast_phw_fiji_power_state(&ps->hardware);
-
-       fiji_ps->performance_levels[fiji_ps->performance_level_count - 1].memory_clock =
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value *
-                       value / 100 +
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return 0;
-}
-
-static const struct pp_hwmgr_func fiji_hwmgr_funcs = {
-       .backend_init = &fiji_hwmgr_backend_init,
-       .backend_fini = &fiji_hwmgr_backend_fini,
-       .asic_setup = &fiji_setup_asic_task,
-       .dynamic_state_management_enable = &fiji_enable_dpm_tasks,
-       .dynamic_state_management_disable = &fiji_disable_dpm_tasks,
-       .force_dpm_level = &fiji_dpm_force_dpm_level,
-       .get_num_of_pp_table_entries = &tonga_get_number_of_powerplay_table_entries,
-       .get_power_state_size = &fiji_get_power_state_size,
-       .get_pp_table_entry = &fiji_get_pp_table_entry,
-       .patch_boot_state = &fiji_patch_boot_state,
-       .apply_state_adjust_rules = &fiji_apply_state_adjust_rules,
-       .power_state_set = &fiji_set_power_state_tasks,
-       .get_sclk = &fiji_dpm_get_sclk,
-       .get_mclk = &fiji_dpm_get_mclk,
-       .print_current_perforce_level = &fiji_print_current_perforce_level,
-       .powergate_uvd = &fiji_phm_powergate_uvd,
-       .powergate_vce = &fiji_phm_powergate_vce,
-       .disable_clock_power_gating = &fiji_phm_disable_clock_power_gating,
-       .notify_smc_display_config_after_ps_adjustment =
-                       &tonga_notify_smc_display_config_after_ps_adjustment,
-       .display_config_changed = &fiji_display_configuration_changed_task,
-       .set_max_fan_pwm_output = fiji_set_max_fan_pwm_output,
-       .set_max_fan_rpm_output = fiji_set_max_fan_rpm_output,
-       .get_temperature = fiji_thermal_get_temperature,
-       .stop_thermal_controller = fiji_thermal_stop_thermal_controller,
-       .get_fan_speed_info = fiji_fan_ctrl_get_fan_speed_info,
-       .get_fan_speed_percent = fiji_fan_ctrl_get_fan_speed_percent,
-       .set_fan_speed_percent = fiji_fan_ctrl_set_fan_speed_percent,
-       .reset_fan_speed_to_default = fiji_fan_ctrl_reset_fan_speed_to_default,
-       .get_fan_speed_rpm = fiji_fan_ctrl_get_fan_speed_rpm,
-       .set_fan_speed_rpm = fiji_fan_ctrl_set_fan_speed_rpm,
-       .uninitialize_thermal_controller = fiji_thermal_ctrl_uninitialize_thermal_controller,
-       .register_internal_thermal_interrupt = fiji_register_internal_thermal_interrupt,
-       .set_fan_control_mode = fiji_set_fan_control_mode,
-       .get_fan_control_mode = fiji_get_fan_control_mode,
-       .check_states_equal = fiji_check_states_equal,
-       .check_smc_update_required_for_display_configuration = fiji_check_smc_update_required_for_display_configuration,
-       .force_clock_level = fiji_force_clock_level,
-       .print_clock_levels = fiji_print_clock_levels,
-       .get_sclk_od = fiji_get_sclk_od,
-       .set_sclk_od = fiji_set_sclk_od,
-       .get_mclk_od = fiji_get_mclk_od,
-       .set_mclk_od = fiji_set_mclk_od,
-};
-
-int fiji_hwmgr_init(struct pp_hwmgr *hwmgr)
-{
-       hwmgr->hwmgr_func = &fiji_hwmgr_funcs;
-       hwmgr->pptable_func = &tonga_pptable_funcs;
-       pp_fiji_thermal_initialize(hwmgr);
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_hwmgr.h
deleted file mode 100644 (file)
index bf67c2a..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright 2015 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 _FIJI_HWMGR_H_
-#define _FIJI_HWMGR_H_
-
-#include "hwmgr.h"
-#include "smu73.h"
-#include "smu73_discrete.h"
-#include "ppatomctrl.h"
-#include "fiji_ppsmc.h"
-#include "pp_endian.h"
-
-#define FIJI_MAX_HARDWARE_POWERLEVELS  2
-#define FIJI_AT_DFLT   30
-
-#define FIJI_VOLTAGE_CONTROL_NONE                   0x0
-#define FIJI_VOLTAGE_CONTROL_BY_GPIO                0x1
-#define FIJI_VOLTAGE_CONTROL_BY_SVID2               0x2
-#define FIJI_VOLTAGE_CONTROL_MERGED                 0x3
-
-#define DPMTABLE_OD_UPDATE_SCLK     0x00000001
-#define DPMTABLE_OD_UPDATE_MCLK     0x00000002
-#define DPMTABLE_UPDATE_SCLK        0x00000004
-#define DPMTABLE_UPDATE_MCLK        0x00000008
-
-struct fiji_performance_level {
-       uint32_t  memory_clock;
-       uint32_t  engine_clock;
-       uint16_t  pcie_gen;
-       uint16_t  pcie_lane;
-};
-
-struct fiji_uvd_clocks {
-       uint32_t  vclk;
-       uint32_t  dclk;
-};
-
-struct fiji_vce_clocks {
-       uint32_t  evclk;
-       uint32_t  ecclk;
-};
-
-struct fiji_power_state {
-    uint32_t                  magic;
-    struct fiji_uvd_clocks    uvd_clks;
-    struct fiji_vce_clocks    vce_clks;
-    uint32_t                  sam_clk;
-    uint32_t                  acp_clk;
-    uint16_t                  performance_level_count;
-    bool                      dc_compatible;
-    uint32_t                  sclk_threshold;
-    struct fiji_performance_level  performance_levels[FIJI_MAX_HARDWARE_POWERLEVELS];
-};
-
-struct fiji_dpm_level {
-       bool    enabled;
-    uint32_t   value;
-    uint32_t   param1;
-};
-
-#define FIJI_MAX_DEEPSLEEP_DIVIDER_ID 5
-#define MAX_REGULAR_DPM_NUMBER 8
-#define FIJI_MINIMUM_ENGINE_CLOCK 2500
-
-struct fiji_single_dpm_table {
-       uint32_t                count;
-       struct fiji_dpm_level   dpm_levels[MAX_REGULAR_DPM_NUMBER];
-};
-
-struct fiji_dpm_table {
-       struct fiji_single_dpm_table  sclk_table;
-       struct fiji_single_dpm_table  mclk_table;
-       struct fiji_single_dpm_table  pcie_speed_table;
-       struct fiji_single_dpm_table  vddc_table;
-       struct fiji_single_dpm_table  vddci_table;
-       struct fiji_single_dpm_table  mvdd_table;
-};
-
-struct fiji_clock_registers {
-       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  vDLL_CNTL;
-       uint32_t  vMCLK_PWRMGT_CNTL;
-       uint32_t  vMPLL_AD_FUNC_CNTL;
-       uint32_t  vMPLL_DQ_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL_1;
-       uint32_t  vMPLL_FUNC_CNTL_2;
-       uint32_t  vMPLL_SS1;
-       uint32_t  vMPLL_SS2;
-};
-
-struct fiji_voltage_smio_registers {
-       uint32_t vS0_VID_LOWER_SMIO_CNTL;
-};
-
-#define FIJI_MAX_LEAKAGE_COUNT  8
-struct fiji_leakage_voltage {
-       uint16_t  count;
-       uint16_t  leakage_id[FIJI_MAX_LEAKAGE_COUNT];
-       uint16_t  actual_voltage[FIJI_MAX_LEAKAGE_COUNT];
-};
-
-struct fiji_vbios_boot_state {
-       uint16_t    mvdd_bootup_value;
-       uint16_t    vddc_bootup_value;
-       uint16_t    vddci_bootup_value;
-       uint32_t    sclk_bootup_value;
-       uint32_t    mclk_bootup_value;
-       uint16_t    pcie_gen_bootup_value;
-       uint16_t    pcie_lane_bootup_value;
-};
-
-struct fiji_bacos {
-       uint32_t                       best_match;
-       uint32_t                       baco_flags;
-       struct fiji_performance_level  performance_level;
-};
-
-/* Ultra Low Voltage parameter structure */
-struct fiji_ulv_parm {
-       bool                           ulv_supported;
-       uint32_t                       cg_ulv_parameter;
-       uint32_t                       ulv_volt_change_delay;
-       struct fiji_performance_level  ulv_power_level;
-};
-
-struct fiji_display_timing {
-       uint32_t  min_clock_in_sr;
-       uint32_t  num_existing_displays;
-};
-
-struct fiji_dpmlevel_enable_mask {
-       uint32_t  uvd_dpm_enable_mask;
-       uint32_t  vce_dpm_enable_mask;
-       uint32_t  acp_dpm_enable_mask;
-       uint32_t  samu_dpm_enable_mask;
-       uint32_t  sclk_dpm_enable_mask;
-       uint32_t  mclk_dpm_enable_mask;
-       uint32_t  pcie_dpm_enable_mask;
-};
-
-struct fiji_pcie_perf_range {
-       uint16_t  max;
-       uint16_t  min;
-};
-
-struct fiji_hwmgr {
-       struct fiji_dpm_table                   dpm_table;
-       struct fiji_dpm_table                   golden_dpm_table;
-
-       uint32_t                                                voting_rights_clients0;
-       uint32_t                                                voting_rights_clients1;
-       uint32_t                                                voting_rights_clients2;
-       uint32_t                                                voting_rights_clients3;
-       uint32_t                                                voting_rights_clients4;
-       uint32_t                                                voting_rights_clients5;
-       uint32_t                                                voting_rights_clients6;
-       uint32_t                                                voting_rights_clients7;
-       uint32_t                                                static_screen_threshold_unit;
-       uint32_t                                                static_screen_threshold;
-       uint32_t                                                voltage_control;
-       uint32_t                                                vddc_vddci_delta;
-
-       uint32_t                                                active_auto_throttle_sources;
-
-       struct fiji_clock_registers            clock_registers;
-       struct fiji_voltage_smio_registers      voltage_smio_registers;
-
-       bool                           is_memory_gddr5;
-       uint16_t                       acpi_vddc;
-       bool                           pspp_notify_required;
-       uint16_t                       force_pcie_gen;
-       uint16_t                       acpi_pcie_gen;
-       uint32_t                       pcie_gen_cap;
-       uint32_t                       pcie_lane_cap;
-       uint32_t                       pcie_spc_cap;
-       struct fiji_leakage_voltage          vddc_leakage;
-       struct fiji_leakage_voltage          Vddci_leakage;
-
-       uint32_t                             mvdd_control;
-       uint32_t                             vddc_mask_low;
-       uint32_t                             mvdd_mask_low;
-       uint16_t                            max_vddc_in_pptable;
-       uint16_t                            min_vddc_in_pptable;
-       uint16_t                            max_vddci_in_pptable;
-       uint16_t                            min_vddci_in_pptable;
-       uint32_t                             mclk_strobe_mode_threshold;
-       uint32_t                             mclk_stutter_mode_threshold;
-       uint32_t                             mclk_edc_enable_threshold;
-       uint32_t                             mclk_edcwr_enable_threshold;
-       bool                                is_uvd_enabled;
-       struct fiji_vbios_boot_state        vbios_boot_state;
-
-       bool                           battery_state;
-       bool                           is_tlu_enabled;
-
-       /* ---- SMC SRAM Address of firmware header tables ---- */
-       uint32_t                             sram_end;
-       uint32_t                             dpm_table_start;
-       uint32_t                             soft_regs_start;
-       uint32_t                             mc_reg_table_start;
-       uint32_t                             fan_table_start;
-       uint32_t                             arb_table_start;
-       struct SMU73_Discrete_DpmTable       smc_state_table;
-       struct SMU73_Discrete_Ulv            ulv_setting;
-
-       /* ---- Stuff originally coming from Evergreen ---- */
-       uint32_t                             vddci_control;
-       struct pp_atomctrl_voltage_table     vddc_voltage_table;
-       struct pp_atomctrl_voltage_table     vddci_voltage_table;
-       struct pp_atomctrl_voltage_table     mvdd_voltage_table;
-
-       uint32_t                             mgcg_cgtt_local2;
-       uint32_t                             mgcg_cgtt_local3;
-       uint32_t                             gpio_debug;
-       uint32_t                             mc_micro_code_feature;
-       uint32_t                             highest_mclk;
-       uint16_t                             acpi_vddci;
-       uint8_t                              mvdd_high_index;
-       uint8_t                              mvdd_low_index;
-       bool                                 dll_default_on;
-       bool                                 performance_request_registered;
-
-       /* ---- Low Power Features ---- */
-       struct fiji_bacos                    bacos;
-       struct fiji_ulv_parm                 ulv;
-
-       /* ---- CAC Stuff ---- */
-       uint32_t                       cac_table_start;
-       bool                           cac_configuration_required;
-       bool                           driver_calculate_cac_leakage;
-       bool                           cac_enabled;
-
-       /* ---- DPM2 Parameters ---- */
-       uint32_t                       power_containment_features;
-       bool                           enable_dte_feature;
-       bool                           enable_tdc_limit_feature;
-       bool                           enable_pkg_pwr_tracking_feature;
-       bool                           disable_uvd_power_tune_feature;
-       const struct fiji_pt_defaults  *power_tune_defaults;
-       struct SMU73_Discrete_PmFuses  power_tune_table;
-       uint32_t                       dte_tj_offset;
-       uint32_t                       fast_watermark_threshold;
-
-       /* ---- Phase Shedding ---- */
-       bool                           vddc_phase_shed_control;
-
-       /* ---- DI/DT ---- */
-       struct fiji_display_timing        display_timing;
-
-       /* ---- Thermal Temperature Setting ---- */
-       struct fiji_dpmlevel_enable_mask     dpm_level_enable_mask;
-       uint32_t                             need_update_smu7_dpm_table;
-       uint32_t                             sclk_dpm_key_disabled;
-       uint32_t                             mclk_dpm_key_disabled;
-       uint32_t                             pcie_dpm_key_disabled;
-       uint32_t                             min_engine_clocks;
-       struct fiji_pcie_perf_range          pcie_gen_performance;
-       struct fiji_pcie_perf_range          pcie_lane_performance;
-       struct fiji_pcie_perf_range          pcie_gen_power_saving;
-       struct fiji_pcie_perf_range          pcie_lane_power_saving;
-       bool                                 use_pcie_performance_levels;
-       bool                                 use_pcie_power_saving_levels;
-       uint32_t                             activity_target[SMU73_MAX_LEVELS_GRAPHICS];
-       uint32_t                             mclk_activity_target;
-       uint32_t                             mclk_dpm0_activity_target;
-       uint32_t                             low_sclk_interrupt_threshold;
-       uint32_t                             last_mclk_dpm_enable_mask;
-       bool                                 uvd_enabled;
-
-       /* ---- Power Gating States ---- */
-       bool                           uvd_power_gated;
-       bool                           vce_power_gated;
-       bool                           samu_power_gated;
-       bool                           acp_power_gated;
-       bool                           pg_acp_init;
-       bool                           frtc_enabled;
-       bool                           frtc_status_changed;
-};
-
-/* To convert to Q8.8 format for firmware */
-#define FIJI_Q88_FORMAT_CONVERSION_UNIT             256
-
-enum Fiji_I2CLineID {
-    Fiji_I2CLineID_DDC1 = 0x90,
-    Fiji_I2CLineID_DDC2 = 0x91,
-    Fiji_I2CLineID_DDC3 = 0x92,
-    Fiji_I2CLineID_DDC4 = 0x93,
-    Fiji_I2CLineID_DDC5 = 0x94,
-    Fiji_I2CLineID_DDC6 = 0x95,
-    Fiji_I2CLineID_SCLSDA = 0x96,
-    Fiji_I2CLineID_DDCVGA = 0x97
-};
-
-#define Fiji_I2C_DDC1DATA          0
-#define Fiji_I2C_DDC1CLK           1
-#define Fiji_I2C_DDC2DATA          2
-#define Fiji_I2C_DDC2CLK           3
-#define Fiji_I2C_DDC3DATA          4
-#define Fiji_I2C_DDC3CLK           5
-#define Fiji_I2C_SDA               40
-#define Fiji_I2C_SCL               41
-#define Fiji_I2C_DDC4DATA          65
-#define Fiji_I2C_DDC4CLK           66
-#define Fiji_I2C_DDC5DATA          0x48
-#define Fiji_I2C_DDC5CLK           0x49
-#define Fiji_I2C_DDC6DATA          0x4a
-#define Fiji_I2C_DDC6CLK           0x4b
-#define Fiji_I2C_DDCVGADATA        0x4c
-#define Fiji_I2C_DDCVGACLK         0x4d
-
-#define FIJI_UNUSED_GPIO_PIN       0x7F
-
-extern int tonga_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr);
-extern int tonga_get_mc_microcode_version (struct pp_hwmgr *hwmgr);
-extern int tonga_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr);
-extern int tonga_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display);
-int fiji_update_vce_dpm(struct pp_hwmgr *hwmgr, const void *input);
-int fiji_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int fiji_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int fiji_update_acp_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int fiji_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable);
-
-#endif /* _FIJI_HWMGR_H_ */
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.c
deleted file mode 100644 (file)
index 4465845..0000000
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * Copyright 2015 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 "hwmgr.h"
-#include "smumgr.h"
-#include "fiji_hwmgr.h"
-#include "fiji_powertune.h"
-#include "fiji_smumgr.h"
-#include "smu73_discrete.h"
-#include "pp_debug.h"
-
-#define VOLTAGE_SCALE  4
-#define POWERTUNE_DEFAULT_SET_MAX    1
-
-const struct fiji_pt_defaults fiji_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
-               /*sviLoadLIneEn,  SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc */
-               {1,               0xF,             0xFD,
-               /* TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase */
-               0x19,        5,               45}
-};
-
-void fiji_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *fiji_hwmgr = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct  phm_ppt_v1_information *table_info =
-                       (struct  phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t tmp = 0;
-
-       if(table_info &&
-                       table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
-                       table_info->cac_dtp_table->usPowerTuneDataSetID)
-               fiji_hwmgr->power_tune_defaults =
-                               &fiji_power_tune_data_set_array
-                               [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
-       else
-               fiji_hwmgr->power_tune_defaults = &fiji_power_tune_data_set_array[0];
-
-       /* Assume disabled */
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CAC);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SQRamping);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DBRamping);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_TDRamping);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_TCPRamping);
-
-       fiji_hwmgr->dte_tj_offset = tmp;
-
-       if (!tmp) {
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_CAC);
-
-               fiji_hwmgr->fast_watermark_threshold = 100;
-
-               if (hwmgr->powercontainment_enabled) {
-                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                   PHM_PlatformCaps_PowerContainment);
-                       tmp = 1;
-                       fiji_hwmgr->enable_dte_feature = tmp ? false : true;
-                       fiji_hwmgr->enable_tdc_limit_feature = tmp ? true : false;
-                       fiji_hwmgr->enable_pkg_pwr_tracking_feature = tmp ? true : false;
-               }
-       }
-}
-
-/* PPGen has the gain setting generated in x * 100 unit
- * This function is to convert the unit to x * 4096(0x1000) unit.
- *  This is the unit expected by SMC firmware
- */
-static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
-{
-       uint32_t tmp;
-       tmp = raw_setting * 4096 / 100;
-       return (uint16_t)tmp;
-}
-
-static void get_scl_sda_value(uint8_t line, uint8_t *scl, uint8_t* sda)
-{
-       switch (line) {
-       case Fiji_I2CLineID_DDC1 :
-               *scl = Fiji_I2C_DDC1CLK;
-               *sda = Fiji_I2C_DDC1DATA;
-               break;
-       case Fiji_I2CLineID_DDC2 :
-               *scl = Fiji_I2C_DDC2CLK;
-               *sda = Fiji_I2C_DDC2DATA;
-               break;
-       case Fiji_I2CLineID_DDC3 :
-               *scl = Fiji_I2C_DDC3CLK;
-               *sda = Fiji_I2C_DDC3DATA;
-               break;
-       case Fiji_I2CLineID_DDC4 :
-               *scl = Fiji_I2C_DDC4CLK;
-               *sda = Fiji_I2C_DDC4DATA;
-               break;
-       case Fiji_I2CLineID_DDC5 :
-               *scl = Fiji_I2C_DDC5CLK;
-               *sda = Fiji_I2C_DDC5DATA;
-               break;
-       case Fiji_I2CLineID_DDC6 :
-               *scl = Fiji_I2C_DDC6CLK;
-               *sda = Fiji_I2C_DDC6DATA;
-               break;
-       case Fiji_I2CLineID_SCLSDA :
-               *scl = Fiji_I2C_SCL;
-               *sda = Fiji_I2C_SDA;
-               break;
-       case Fiji_I2CLineID_DDCVGA :
-               *scl = Fiji_I2C_DDCVGACLK;
-               *sda = Fiji_I2C_DDCVGADATA;
-               break;
-       default:
-               *scl = 0;
-               *sda = 0;
-               break;
-       }
-}
-
-int fiji_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_pt_defaults *defaults = data->power_tune_defaults;
-       SMU73_Discrete_DpmTable  *dpm_table = &(data->smc_state_table);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
-       struct pp_advance_fan_control_parameters *fan_table=
-                       &hwmgr->thermal_controller.advanceFanControlParameters;
-       uint8_t uc_scl, uc_sda;
-
-       /* TDP number of fraction bits are changed from 8 to 7 for Fiji
-        * as requested by SMC team
-        */
-       dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
-                       (uint16_t)(cac_dtp_table->usTDP * 128));
-       dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
-                       (uint16_t)(cac_dtp_table->usTDP * 128));
-
-       PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
-                       "Target Operating Temp is out of Range!",);
-
-       dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
-       dpm_table->GpuTjHyst = 8;
-
-       dpm_table->DTEAmbientTempBase = defaults->DTEAmbientTempBase;
-
-       /* The following are for new Fiji Multi-input fan/thermal control */
-       dpm_table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTargetOperatingTemp * 256);
-       dpm_table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitHotspot * 256);
-       dpm_table->TemperatureLimitLiquid1 = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitLiquid1 * 256);
-       dpm_table->TemperatureLimitLiquid2 = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitLiquid2 * 256);
-       dpm_table->TemperatureLimitVrVddc = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitVrVddc * 256);
-       dpm_table->TemperatureLimitVrMvdd = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitVrMvdd * 256);
-       dpm_table->TemperatureLimitPlx = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitPlx * 256);
-
-       dpm_table->FanGainEdge = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainEdge));
-       dpm_table->FanGainHotspot = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainHotspot));
-       dpm_table->FanGainLiquid = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainLiquid));
-       dpm_table->FanGainVrVddc = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainVrVddc));
-       dpm_table->FanGainVrMvdd = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainVrMvdd));
-       dpm_table->FanGainPlx = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainPlx));
-       dpm_table->FanGainHbm = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainHbm));
-
-       dpm_table->Liquid1_I2C_address = cac_dtp_table->ucLiquid1_I2C_address;
-       dpm_table->Liquid2_I2C_address = cac_dtp_table->ucLiquid2_I2C_address;
-       dpm_table->Vr_I2C_address = cac_dtp_table->ucVr_I2C_address;
-       dpm_table->Plx_I2C_address = cac_dtp_table->ucPlx_I2C_address;
-
-       get_scl_sda_value(cac_dtp_table->ucLiquid_I2C_Line, &uc_scl, &uc_sda);
-       dpm_table->Liquid_I2C_LineSCL = uc_scl;
-       dpm_table->Liquid_I2C_LineSDA = uc_sda;
-
-       get_scl_sda_value(cac_dtp_table->ucVr_I2C_Line, &uc_scl, &uc_sda);
-       dpm_table->Vr_I2C_LineSCL = uc_scl;
-       dpm_table->Vr_I2C_LineSDA = uc_sda;
-
-       get_scl_sda_value(cac_dtp_table->ucPlx_I2C_Line, &uc_scl, &uc_sda);
-       dpm_table->Plx_I2C_LineSCL = uc_scl;
-       dpm_table->Plx_I2C_LineSDA = uc_sda;
-
-       return 0;
-}
-
-static int fiji_populate_svi_load_line(struct pp_hwmgr *hwmgr)
-{
-    struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-    const struct fiji_pt_defaults *defaults = data->power_tune_defaults;
-
-    data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
-    data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
-    data->power_tune_table.SviLoadLineTrimVddC = 3;
-    data->power_tune_table.SviLoadLineOffsetVddC = 0;
-
-    return 0;
-}
-
-static int fiji_populate_tdc_limit(struct pp_hwmgr *hwmgr)
-{
-       uint16_t tdc_limit;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       const struct fiji_pt_defaults *defaults = data->power_tune_defaults;
-
-       /* TDC number of fraction bits are changed from 8 to 7
-        * for Fiji as requested by SMC team
-        */
-       tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
-       data->power_tune_table.TDC_VDDC_PkgLimit =
-                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
-       data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
-                       defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
-       data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
-
-       return 0;
-}
-
-static int fiji_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       const struct fiji_pt_defaults *defaults = data->power_tune_defaults;
-       uint32_t temp;
-
-       if (fiji_read_smc_sram_dword(hwmgr->smumgr,
-                       fuse_table_offset +
-                       offsetof(SMU73_Discrete_PmFuses, TdcWaterfallCtl),
-                       (uint32_t *)&temp, data->sram_end))
-               PP_ASSERT_WITH_CODE(false,
-                               "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
-                               return -EINVAL);
-       else {
-               data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
-               data->power_tune_table.LPMLTemperatureMin =
-                               (uint8_t)((temp >> 16) & 0xff);
-               data->power_tune_table.LPMLTemperatureMax =
-                               (uint8_t)((temp >> 8) & 0xff);
-               data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
-       }
-       return 0;
-}
-
-static int fiji_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
-{
-       int i;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* Currently not used. Set all to zero. */
-       for (i = 0; i < 16; i++)
-               data->power_tune_table.LPMLTemperatureScaler[i] = 0;
-
-       return 0;
-}
-
-static int fiji_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if( (hwmgr->thermal_controller.advanceFanControlParameters.
-                       usFanOutputSensitivity & (1 << 15)) ||
-                       0 == hwmgr->thermal_controller.advanceFanControlParameters.
-                       usFanOutputSensitivity )
-               hwmgr->thermal_controller.advanceFanControlParameters.
-               usFanOutputSensitivity = hwmgr->thermal_controller.
-                       advanceFanControlParameters.usDefaultFanOutputSensitivity;
-
-       data->power_tune_table.FuzzyFan_PwmSetDelta =
-                       PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
-                                       advanceFanControlParameters.usFanOutputSensitivity);
-       return 0;
-}
-
-static int fiji_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
-{
-       int i;
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       /* Currently not used. Set all to zero. */
-       for (i = 0; i < 16; i++)
-               data->power_tune_table.GnbLPML[i] = 0;
-
-       return 0;
-}
-
-static int fiji_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
-{
- /*   int  i, min, max;
-    struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-    uint8_t * pHiVID = data->power_tune_table.BapmVddCVidHiSidd;
-    uint8_t * pLoVID = data->power_tune_table.BapmVddCVidLoSidd;
-
-    min = max = pHiVID[0];
-    for (i = 0; i < 8; i++) {
-        if (0 != pHiVID[i]) {
-            if (min > pHiVID[i])
-                min = pHiVID[i];
-            if (max < pHiVID[i])
-                max = pHiVID[i];
-        }
-
-        if (0 != pLoVID[i]) {
-            if (min > pLoVID[i])
-                min = pLoVID[i];
-            if (max < pLoVID[i])
-                max = pLoVID[i];
-        }
-    }
-
-    PP_ASSERT_WITH_CODE((0 != min) && (0 != max), "BapmVddcVidSidd table does not exist!", return int_Failed);
-    data->power_tune_table.GnbLPMLMaxVid = (uint8_t)max;
-    data->power_tune_table.GnbLPMLMinVid = (uint8_t)min;
-*/
-    return 0;
-}
-
-static int fiji_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint16_t HiSidd = data->power_tune_table.BapmVddCBaseLeakageHiSidd;
-       uint16_t LoSidd = data->power_tune_table.BapmVddCBaseLeakageLoSidd;
-       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
-
-       HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
-       LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
-
-       data->power_tune_table.BapmVddCBaseLeakageHiSidd =
-                       CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
-       data->power_tune_table.BapmVddCBaseLeakageLoSidd =
-                       CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
-
-       return 0;
-}
-
-int fiji_populate_pm_fuses(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       uint32_t pm_fuse_table_offset;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-               if (fiji_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU7_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU73_Firmware_Header, PmFuseTable),
-                               &pm_fuse_table_offset, data->sram_end))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to get pm_fuse_table_offset Failed!",
-                                       return -EINVAL);
-
-               /* DW6 */
-               if (fiji_populate_svi_load_line(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate SviLoadLine Failed!",
-                                       return -EINVAL);
-               /* DW7 */
-               if (fiji_populate_tdc_limit(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate TDCLimit Failed!", return -EINVAL);
-               /* DW8 */
-               if (fiji_populate_dw8(hwmgr, pm_fuse_table_offset))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate TdcWaterfallCtl, "
-                                       "LPMLTemperature Min and Max Failed!",
-                                       return -EINVAL);
-
-               /* DW9-DW12 */
-               if (0 != fiji_populate_temperature_scaler(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate LPMLTemperatureScaler Failed!",
-                                       return -EINVAL);
-
-               /* DW13-DW14 */
-               if(fiji_populate_fuzzy_fan(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate Fuzzy Fan Control parameters Failed!",
-                                       return -EINVAL);
-
-               /* DW15-DW18 */
-               if (fiji_populate_gnb_lpml(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate GnbLPML Failed!",
-                                       return -EINVAL);
-
-               /* DW19 */
-               if (fiji_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate GnbLPML Min and Max Vid Failed!",
-                                       return -EINVAL);
-
-               /* DW20 */
-               if (fiji_populate_bapm_vddc_base_leakage_sidd(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
-                                       "Sidd Failed!", return -EINVAL);
-
-               if (fiji_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
-                               (uint8_t *)&data->power_tune_table,
-                               sizeof(struct SMU73_Discrete_PmFuses), data->sram_end))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to download PmFuseTable Failed!",
-                                       return -EINVAL);
-       }
-       return 0;
-}
-
-int fiji_enable_smc_cac(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CAC)) {
-               int smc_result;
-               smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                               (uint16_t)(PPSMC_MSG_EnableCac));
-               PP_ASSERT_WITH_CODE((0 == smc_result),
-                               "Failed to enable CAC in SMC.", result = -1);
-
-        data->cac_enabled = (0 == smc_result) ? true : false;
-       }
-       return result;
-}
-
-int fiji_disable_smc_cac(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CAC) && data->cac_enabled) {
-               int smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                               (uint16_t)(PPSMC_MSG_DisableCac));
-               PP_ASSERT_WITH_CODE((smc_result == 0),
-                               "Failed to disable CAC in SMC.", result = -1);
-
-               data->cac_enabled = false;
-       }
-       return result;
-}
-
-int fiji_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-
-       if(data->power_containment_features &
-                       POWERCONTAINMENT_FEATURE_PkgPwrLimit)
-               return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_PkgPwrSetLimit, n);
-       return 0;
-}
-
-static int fiji_set_overdriver_target_tdp(struct pp_hwmgr *pHwMgr, uint32_t target_tdp)
-{
-       return smum_send_msg_to_smc_with_parameter(pHwMgr->smumgr,
-                       PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
-}
-
-int fiji_enable_power_containment(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int smc_result;
-       int result = 0;
-
-       data->power_containment_features = 0;
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-               if (data->enable_dte_feature) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_EnableDTE));
-                       PP_ASSERT_WITH_CODE((0 == smc_result),
-                                       "Failed to enable DTE in SMC.", result = -1;);
-                       if (0 == smc_result)
-                               data->power_containment_features |= POWERCONTAINMENT_FEATURE_DTE;
-               }
-
-               if (data->enable_tdc_limit_feature) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_TDCLimitEnable));
-                       PP_ASSERT_WITH_CODE((0 == smc_result),
-                                       "Failed to enable TDCLimit in SMC.", result = -1;);
-                       if (0 == smc_result)
-                               data->power_containment_features |=
-                                               POWERCONTAINMENT_FEATURE_TDCLimit;
-               }
-
-               if (data->enable_pkg_pwr_tracking_feature) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitEnable));
-                       PP_ASSERT_WITH_CODE((0 == smc_result),
-                                       "Failed to enable PkgPwrTracking in SMC.", result = -1;);
-                       if (0 == smc_result) {
-                               struct phm_cac_tdp_table *cac_table =
-                                               table_info->cac_dtp_table;
-                               uint32_t default_limit =
-                                       (uint32_t)(cac_table->usMaximumPowerDeliveryLimit * 256);
-
-                               data->power_containment_features |=
-                                               POWERCONTAINMENT_FEATURE_PkgPwrLimit;
-
-                               if (fiji_set_power_limit(hwmgr, default_limit))
-                                       printk(KERN_ERR "Failed to set Default Power Limit in SMC!");
-                       }
-               }
-       }
-       return result;
-}
-
-int fiji_disable_power_containment(struct pp_hwmgr *hwmgr)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment) &&
-                       data->power_containment_features) {
-               int smc_result;
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_TDCLimit) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_TDCLimitDisable));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable TDCLimit in SMC.",
-                                       result = smc_result);
-               }
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_DTE) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_DisableDTE));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable DTE in SMC.",
-                                       result = smc_result);
-               }
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitDisable));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable PkgPwrTracking in SMC.",
-                                       result = smc_result);
-               }
-               data->power_containment_features = 0;
-       }
-
-       return result;
-}
-
-int fiji_power_control_set_level(struct pp_hwmgr *hwmgr)
-{
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
-       int adjust_percent, target_tdp;
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-               /* adjustment percentage has already been validated */
-               adjust_percent = hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
-                               hwmgr->platform_descriptor.TDPAdjustment :
-                               (-1 * hwmgr->platform_descriptor.TDPAdjustment);
-               /* SMC requested that target_tdp to be 7 bit fraction in DPM table
-                * but message to be 8 bit fraction for messages
-                */
-               target_tdp = ((100 + adjust_percent) * (int)(cac_table->usTDP * 256)) / 100;
-               result = fiji_set_overdriver_target_tdp(hwmgr, (uint32_t)target_tdp);
-       }
-
-       return result;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.h b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_powertune.h
deleted file mode 100644 (file)
index fec7724..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2015 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 FIJI_POWERTUNE_H
-#define FIJI_POWERTUNE_H
-
-enum fiji_pt_config_reg_type {
-       FIJI_CONFIGREG_MMR = 0,
-       FIJI_CONFIGREG_SMC_IND,
-       FIJI_CONFIGREG_DIDT_IND,
-       FIJI_CONFIGREG_CACHE,
-       FIJI_CONFIGREG_MAX
-};
-
-/* PowerContainment Features */
-#define POWERCONTAINMENT_FEATURE_DTE             0x00000001
-#define POWERCONTAINMENT_FEATURE_TDCLimit        0x00000002
-#define POWERCONTAINMENT_FEATURE_PkgPwrLimit     0x00000004
-
-#define DIDT_SQ_CTRL0__UNUSED_0_MASK             0xffffffc0
-#define DIDT_SQ_CTRL0__UNUSED_0__SHIFT           0x6
-#define DIDT_TD_CTRL0__UNUSED_0_MASK             0xffffffc0
-#define DIDT_TD_CTRL0__UNUSED_0__SHIFT           0x6
-#define DIDT_TCP_CTRL0__UNUSED_0_MASK            0xffffffc0
-#define DIDT_TCP_CTRL0__UNUSED_0__SHIFT          0x6
-#define DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK                 0xe0000000
-#define DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001d
-#define DIDT_TD_TUNING_CTRL__UNUSED_0_MASK                 0xe0000000
-#define DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001d
-#define DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK                0xe0000000
-#define DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT              0x0000001d
-
-struct fiji_pt_config_reg {
-       uint32_t                           offset;
-       uint32_t                           mask;
-       uint32_t                           shift;
-       uint32_t                           value;
-       enum fiji_pt_config_reg_type       type;
-};
-
-struct fiji_pt_defaults
-{
-    uint8_t   SviLoadLineEn;
-    uint8_t   SviLoadLineVddC;
-    uint8_t   TDC_VDDC_ThrottleReleaseLimitPerc;
-    uint8_t   TDC_MAWt;
-    uint8_t   TdcWaterfallCtl;
-    uint8_t   DTEAmbientTempBase;
-};
-
-void fiji_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr);
-int fiji_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr);
-int fiji_populate_pm_fuses(struct pp_hwmgr *hwmgr);
-int fiji_enable_smc_cac(struct pp_hwmgr *hwmgr);
-int fiji_disable_smc_cac(struct pp_hwmgr *hwmgr);
-int fiji_enable_power_containment(struct pp_hwmgr *hwmgr);
-int fiji_disable_power_containment(struct pp_hwmgr *hwmgr);
-int fiji_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
-int fiji_power_control_set_level(struct pp_hwmgr *hwmgr);
-
-#endif  /* FIJI_POWERTUNE_H */
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.c
deleted file mode 100644 (file)
index 92976b6..0000000
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * Copyright 2015 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 <asm/div64.h>
-#include "fiji_thermal.h"
-#include "fiji_hwmgr.h"
-#include "fiji_smumgr.h"
-#include "fiji_ppsmc.h"
-#include "smu/smu_7_1_3_d.h"
-#include "smu/smu_7_1_3_sh_mask.h"
-
-int fiji_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
-               struct phm_fan_speed_info *fan_speed_info)
-{
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       fan_speed_info->supports_percent_read = true;
-       fan_speed_info->supports_percent_write = true;
-       fan_speed_info->min_percent = 0;
-       fan_speed_info->max_percent = 100;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_FanSpeedInTableIsRPM) &&
-               hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
-               fan_speed_info->supports_rpm_read = true;
-               fan_speed_info->supports_rpm_write = true;
-               fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM;
-               fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
-       } else {
-               fan_speed_info->min_rpm = 0;
-               fan_speed_info->max_rpm = 0;
-       }
-
-       return 0;
-}
-
-int fiji_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
-               uint32_t *speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-       duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_STATUS, FDO_PWM_DUTY);
-
-       if (duty100 == 0)
-               return -EINVAL;
-
-
-       tmp64 = (uint64_t)duty * 100;
-       do_div(tmp64, duty100);
-       *speed = (uint32_t)tmp64;
-
-       if (*speed > 100)
-               *speed = 100;
-
-       return 0;
-}
-
-int fiji_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
-{
-       uint32_t tach_period;
-       uint32_t crystal_clock_freq;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
-                       (hwmgr->thermal_controller.fanInfo.
-                               ucTachometerPulsesPerRevolution == 0))
-               return 0;
-
-       tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_TACH_STATUS, TACH_PERIOD);
-
-       if (tach_period == 0)
-               return -EINVAL;
-
-       crystal_clock_freq = tonga_get_xclk(hwmgr);
-
-       *speed = 60 * crystal_clock_freq * 10000/ tach_period;
-
-       return 0;
-}
-
-/**
-* Set Fan Speed Control to static mode, so that the user can decide what speed to use.
-* @param    hwmgr  the address of the powerplay hardware manager.
-*           mode    the fan control mode, 0 default, 1 by percent, 5, by RPM
-* @exception Should always succeed.
-*/
-int fiji_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-
-       if (hwmgr->fan_ctrl_is_in_default_mode) {
-               hwmgr->fan_ctrl_default_mode =
-                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,     CGS_IND_REG__SMC,
-                                               CG_FDO_CTRL2, FDO_PWM_MODE);
-               hwmgr->tmin =
-                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                               CG_FDO_CTRL2, TMIN);
-               hwmgr->fan_ctrl_is_in_default_mode = false;
-       }
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, TMIN, 0);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, FDO_PWM_MODE, mode);
-
-       return 0;
-}
-
-/**
-* Reset Fan Speed Control to default mode.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Should always succeed.
-*/
-int fiji_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->fan_ctrl_is_in_default_mode) {
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode);
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, TMIN, hwmgr->tmin);
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       }
-
-       return 0;
-}
-
-int fiji_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ODFuzzyFanControlSupport)) {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY);
-               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_FanSpeedInTableIsRPM))
-                       hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr,
-                                       hwmgr->thermal_controller.
-                                       advanceFanControlParameters.usMaxFanRPM);
-               else
-                       hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr,
-                                       hwmgr->thermal_controller.
-                                       advanceFanControlParameters.usMaxFanPWM);
-
-       } else {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE);
-               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
-       }
-
-       if (!result && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ucTargetTemperature)
-               result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanTemperatureTarget,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ucTargetTemperature);
-
-       return result;
-}
-
-
-int fiji_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl);
-}
-
-/**
-* Set Fan Speed in percent.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (0% - 100%) to be set.
-* @exception Fails is the 100% setting appears to be 0.
-*/
-int fiji_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
-               uint32_t speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       if (speed > 100)
-               speed = 100;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl))
-               fiji_fan_ctrl_stop_smc_fan_control(hwmgr);
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (duty100 == 0)
-               return -EINVAL;
-
-       tmp64 = (uint64_t)speed * duty100;
-       do_div(tmp64, 100);
-       duty = (uint32_t)tmp64;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL0, FDO_STATIC_DUTY, duty);
-
-       return fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-}
-
-/**
-* Reset Fan Speed to default.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Always succeeds.
-*/
-int fiji_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl)) {
-               result = fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-               if (!result)
-                       result = fiji_fan_ctrl_start_smc_fan_control(hwmgr);
-       } else
-               result = fiji_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set Fan Speed in RPM.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (min - max) to be set.
-* @exception Fails is the speed not lie between min and max.
-*/
-int fiji_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
-{
-       uint32_t tach_period;
-       uint32_t crystal_clock_freq;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
-                       (hwmgr->thermal_controller.fanInfo.
-                       ucTachometerPulsesPerRevolution == 0) ||
-                       (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
-                       (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
-               return 0;
-
-       crystal_clock_freq = tonga_get_xclk(hwmgr);
-
-       tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_TACH_STATUS, TACH_PERIOD, tach_period);
-
-       return fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-}
-
-/**
-* Reads the remote temperature from the SIslands thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-int fiji_thermal_get_temperature(struct pp_hwmgr *hwmgr)
-{
-       int temp;
-
-       temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_MULT_THERMAL_STATUS, CTF_TEMP);
-
-       /* Bit 9 means the reading is lower than the lowest usable value. */
-       if (temp & 0x200)
-               temp = FIJI_THERMAL_MAXIMUM_TEMP_READING;
-       else
-               temp = temp & 0x1ff;
-
-       temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       return temp;
-}
-
-/**
-* Set the requested temperature range for high and low alert signals
-*
-* @param    hwmgr The address of the hardware manager.
-* @param    range Temperature range to be programmed for high and low alert signals
-* @exception PP_Result_BadInput if the input data is not valid.
-*/
-static int fiji_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
-               uint32_t low_temp, uint32_t high_temp)
-{
-       uint32_t low = FIJI_THERMAL_MINIMUM_ALERT_TEMP *
-                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-       uint32_t high = FIJI_THERMAL_MAXIMUM_ALERT_TEMP *
-                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       if (low < low_temp)
-               low = low_temp;
-       if (high > high_temp)
-               high = high_temp;
-
-       if (low > high)
-               return -EINVAL;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, DIG_THERM_INTH,
-                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, DIG_THERM_INTL,
-                       (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_CTRL, DIG_THERM_DPM,
-                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-
-       return 0;
-}
-
-/**
-* Programs thermal controller one-time setting registers
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int fiji_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_TACH_CTRL, EDGE_PER_REV,
-                               hwmgr->thermal_controller.fanInfo.
-                               ucTachometerPulsesPerRevolution - 1);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
-
-       return 0;
-}
-
-/**
-* Enable thermal alerts on the RV770 thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int fiji_thermal_enable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK);
-       alert &= ~(FIJI_THERMAL_HIGH_ALERT_MASK | FIJI_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to enable internal thermal interrupts */
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable);
-}
-
-/**
-* Disable thermal alerts on the RV770 thermal controller.
-* @param    hwmgr The address of the hardware manager.
-*/
-static int fiji_thermal_disable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK);
-       alert |= (FIJI_THERMAL_HIGH_ALERT_MASK | FIJI_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to disable internal thermal interrupts */
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable);
-}
-
-/**
-* Uninitialize the thermal controller.
-* Currently just disables alerts.
-* @param    hwmgr The address of the hardware manager.
-*/
-int fiji_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       int result = fiji_thermal_disable_alert(hwmgr);
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               fiji_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set up the fan table to control the fan using the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       struct fiji_hwmgr *data = (struct fiji_hwmgr *)(hwmgr->backend);
-       SMU73_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
-       uint32_t duty100;
-       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
-       uint16_t fdo_min, slope1, slope2;
-       uint32_t reference_clock;
-       int res;
-       uint64_t tmp64;
-
-       if (data->fan_table_start == 0) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (duty100 == 0) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
-                       usPWMMin * duty100;
-       do_div(tmp64, 10000);
-       fdo_min = (uint16_t)tmp64;
-
-       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
-       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
-
-       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
-       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
-
-       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
-       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
-
-       fan_table.TempMin = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMin) / 100);
-       fan_table.TempMed = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMed) / 100);
-       fan_table.TempMax = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMax) / 100);
-
-       fan_table.Slope1 = cpu_to_be16(slope1);
-       fan_table.Slope2 = cpu_to_be16(slope2);
-
-       fan_table.FdoMin = cpu_to_be16(fdo_min);
-
-       fan_table.HystDown = cpu_to_be16(hwmgr->
-                       thermal_controller.advanceFanControlParameters.ucTHyst);
-
-       fan_table.HystUp = cpu_to_be16(1);
-
-       fan_table.HystSlope = cpu_to_be16(1);
-
-       fan_table.TempRespLim = cpu_to_be16(5);
-
-       reference_clock = tonga_get_xclk(hwmgr);
-
-       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
-                       thermal_controller.advanceFanControlParameters.ulCycleDelay *
-                       reference_clock) / 1600);
-
-       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
-
-       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
-                       hwmgr->device, CGS_IND_REG__SMC,
-                       CG_MULT_THERMAL_CTRL, TEMP_SEL);
-
-       res = fiji_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start,
-                       (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
-                       data->sram_end);
-
-       if (!res && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ucMinimumPWMLimit)
-               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanMinPwm,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ucMinimumPWMLimit);
-
-       if (!res && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
-               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanSclkTarget,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ulMinFanSCLKAcousticLimit);
-
-       if (res)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-
-       return 0;
-}
-
-/**
-* Start the fan control on the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_fiji_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-/* If the fantable setup has failed we could have disabled
- * PHM_PlatformCaps_MicrocodeFanControl even after
- * this function was included in the table.
- * Make sure that we still think controlling the fan is OK.
-*/
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl)) {
-               fiji_fan_ctrl_start_smc_fan_control(hwmgr);
-               fiji_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-       }
-
-       return 0;
-}
-
-/**
-* Set temperature range for high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_fiji_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input;
-
-       if (range == NULL)
-               return -EINVAL;
-
-       return fiji_thermal_set_temperature_range(hwmgr, range->min, range->max);
-}
-
-/**
-* Programs one-time setting registers
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from initialize thermal controller routine
-*/
-int tf_fiji_thermal_initialize(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-    return fiji_thermal_initialize(hwmgr);
-}
-
-/**
-* Enable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from enable alert routine
-*/
-int tf_fiji_thermal_enable_alert(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       return fiji_thermal_enable_alert(hwmgr);
-}
-
-/**
-* Disable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from disable alert routine
-*/
-static int tf_fiji_thermal_disable_alert(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       return fiji_thermal_disable_alert(hwmgr);
-}
-
-static const struct phm_master_table_item
-fiji_thermal_start_thermal_controller_master_list[] = {
-       {NULL, tf_fiji_thermal_initialize},
-       {NULL, tf_fiji_thermal_set_temperature_range},
-       {NULL, tf_fiji_thermal_enable_alert},
-/* We should restrict performance levels to low before we halt the SMC.
- * On the other hand we are still in boot state when we do this
- * so it would be pointless.
- * If this assumption changes we have to revisit this table.
- */
-       {NULL, tf_fiji_thermal_setup_fan_table},
-       {NULL, tf_fiji_thermal_start_smc_fan_control},
-       {NULL, NULL}
-};
-
-static const struct phm_master_table_header
-fiji_thermal_start_thermal_controller_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       fiji_thermal_start_thermal_controller_master_list
-};
-
-static const struct phm_master_table_item
-fiji_thermal_set_temperature_range_master_list[] = {
-       {NULL, tf_fiji_thermal_disable_alert},
-       {NULL, tf_fiji_thermal_set_temperature_range},
-       {NULL, tf_fiji_thermal_enable_alert},
-       {NULL, NULL}
-};
-
-static const struct phm_master_table_header
-fiji_thermal_set_temperature_range_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       fiji_thermal_set_temperature_range_master_list
-};
-
-int fiji_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
-               fiji_fan_ctrl_set_default_mode(hwmgr);
-       return 0;
-}
-
-/**
-* Initializes the thermal controller related functions in the Hardware Manager structure.
-* @param    hwmgr The address of the hardware manager.
-* @exception Any error code from the low-level communication.
-*/
-int pp_fiji_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       result = phm_construct_table(hwmgr,
-                       &fiji_thermal_set_temperature_range_master,
-                       &(hwmgr->set_temperature_range));
-
-       if (!result) {
-               result = phm_construct_table(hwmgr,
-                               &fiji_thermal_start_thermal_controller_master,
-                               &(hwmgr->start_thermal_controller));
-               if (result)
-                       phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range));
-       }
-
-       if (!result)
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       return result;
-}
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.h b/drivers/gpu/drm/amd/powerplay/hwmgr/fiji_thermal.h
deleted file mode 100644 (file)
index 8621493..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2015 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 FIJI_THERMAL_H
-#define FIJI_THERMAL_H
-
-#include "hwmgr.h"
-
-#define FIJI_THERMAL_HIGH_ALERT_MASK         0x1
-#define FIJI_THERMAL_LOW_ALERT_MASK          0x2
-
-#define FIJI_THERMAL_MINIMUM_TEMP_READING    -256
-#define FIJI_THERMAL_MAXIMUM_TEMP_READING    255
-
-#define FIJI_THERMAL_MINIMUM_ALERT_TEMP      0
-#define FIJI_THERMAL_MAXIMUM_ALERT_TEMP      255
-
-#define FDO_PWM_MODE_STATIC  1
-#define FDO_PWM_MODE_STATIC_RPM 5
-
-
-extern int tf_fiji_thermal_initialize(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_fiji_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_fiji_thermal_enable_alert(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-
-extern int fiji_thermal_get_temperature(struct pp_hwmgr *hwmgr);
-extern int fiji_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int fiji_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info);
-extern int fiji_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int fiji_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
-extern int fiji_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode);
-extern int fiji_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int fiji_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
-extern int pp_fiji_thermal_initialize(struct pp_hwmgr *hwmgr);
-extern int fiji_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int fiji_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int fiji_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int fiji_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
-extern uint32_t tonga_get_xclk(struct pp_hwmgr *hwmgr);
-
-#endif
-
index 789f98a..14f8c1f 100644 (file)
@@ -24,8 +24,6 @@
 #include "hwmgr.h"
 #include "hardwaremanager.h"
 #include "power_state.h"
-#include "pp_acpi.h"
-#include "amd_acpi.h"
 #include "pp_debug.h"
 
 #define PHM_FUNC_CHECK(hw) \
                        return -EINVAL;                         \
        } while (0)
 
-void phm_init_dynamic_caps(struct pp_hwmgr *hwmgr)
-{
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableVoltageTransition);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableEngineTransition);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMemoryTransition);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMGClockGating);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMGCGTSSM);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableLSClockGating);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_Force3DClockSupport);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableLightSleep);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMCLS);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisablePowerGating);
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableDPM);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableSMUUVDHandshake);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ThermalAutoThrottling);
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_NoOD5Support);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UserMaxClockForMultiDisplays);
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VpuRecoveryInProgress);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDDPM);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEDPM);
-
-       if (acpi_atcs_functions_supported(hwmgr->device, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST) &&
-               acpi_atcs_functions_supported(hwmgr->device, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION))
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
-}
-
 bool phm_is_hw_access_blocked(struct pp_hwmgr *hwmgr)
 {
        return hwmgr->block_hw_access;
index 27e0762..1167205 100644 (file)
 #include "pp_debug.h"
 #include "ppatomctrl.h"
 #include "ppsmc.h"
-
-#define VOLTAGE_SCALE               4
+#include "pp_acpi.h"
+#include "amd_acpi.h"
 
 extern int cz_hwmgr_init(struct pp_hwmgr *hwmgr);
-extern int tonga_hwmgr_init(struct pp_hwmgr *hwmgr);
-extern int fiji_hwmgr_init(struct pp_hwmgr *hwmgr);
-extern int polaris10_hwmgr_init(struct pp_hwmgr *hwmgr);
+
+static int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr);
+static int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr);
+static int fiji_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static int tonga_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+static int topaz_set_asic_special_caps(struct pp_hwmgr *hwmgr);
+
+uint8_t convert_to_vid(uint16_t vddc)
+{
+       return (uint8_t) ((6200 - (vddc * VOLTAGE_SCALE)) / 25);
+}
 
 int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
 {
@@ -56,10 +65,12 @@ int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
        hwmgr->device = pp_init->device;
        hwmgr->chip_family = pp_init->chip_family;
        hwmgr->chip_id = pp_init->chip_id;
-       hwmgr->hw_revision = pp_init->rev_id;
        hwmgr->usec_timeout = AMD_MAX_USEC_TIMEOUT;
        hwmgr->power_source = PP_PowerSource_AC;
-       hwmgr->powercontainment_enabled = pp_init->powercontainment_enabled;
+       hwmgr->pp_table_version = PP_TABLE_V1;
+
+       hwmgr_init_default_caps(hwmgr);
+       hwmgr_set_user_specify_caps(hwmgr);
 
        switch (hwmgr->chip_family) {
        case AMDGPU_FAMILY_CZ:
@@ -67,26 +78,38 @@ int hwmgr_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
                break;
        case AMDGPU_FAMILY_VI:
                switch (hwmgr->chip_id) {
+               case CHIP_TOPAZ:
+                       topaz_set_asic_special_caps(hwmgr);
+                       hwmgr->feature_mask &= ~(PP_SMC_VOLTAGE_CONTROL_MASK |
+                                               PP_VBI_TIME_SUPPORT_MASK |
+                                               PP_ENABLE_GFX_CG_THRU_SMU);
+                       hwmgr->pp_table_version = PP_TABLE_V0;
+                       break;
                case CHIP_TONGA:
-                       tonga_hwmgr_init(hwmgr);
+                       tonga_set_asic_special_caps(hwmgr);
+                       hwmgr->feature_mask &= ~(PP_SMC_VOLTAGE_CONTROL_MASK |
+                                               PP_VBI_TIME_SUPPORT_MASK);
                        break;
                case CHIP_FIJI:
-                       fiji_hwmgr_init(hwmgr);
+                       fiji_set_asic_special_caps(hwmgr);
+                       hwmgr->feature_mask &= ~(PP_SMC_VOLTAGE_CONTROL_MASK |
+                                               PP_VBI_TIME_SUPPORT_MASK |
+                                               PP_ENABLE_GFX_CG_THRU_SMU);
                        break;
                case CHIP_POLARIS11:
                case CHIP_POLARIS10:
-                       polaris10_hwmgr_init(hwmgr);
+                       polaris_set_asic_special_caps(hwmgr);
+                       hwmgr->feature_mask &= ~(PP_UVD_HANDSHAKE_MASK);
                        break;
                default:
                        return -EINVAL;
                }
+               smu7_hwmgr_init(hwmgr);
                break;
        default:
                return -EINVAL;
        }
 
-       phm_init_dynamic_caps(hwmgr);
-
        return 0;
 }
 
@@ -105,6 +128,8 @@ int hwmgr_fini(struct pp_hwmgr *hwmgr)
        kfree(hwmgr->set_temperature_range.function_list);
 
        kfree(hwmgr->ps);
+       kfree(hwmgr->current_ps);
+       kfree(hwmgr->request_ps);
        kfree(hwmgr);
        return 0;
 }
@@ -129,10 +154,17 @@ int hw_init_power_state_table(struct pp_hwmgr *hwmgr)
                                          sizeof(struct pp_power_state);
 
        hwmgr->ps = kzalloc(size * table_entries, GFP_KERNEL);
-
        if (hwmgr->ps == NULL)
                return -ENOMEM;
 
+       hwmgr->request_ps = kzalloc(size, GFP_KERNEL);
+       if (hwmgr->request_ps == NULL)
+               return -ENOMEM;
+
+       hwmgr->current_ps = kzalloc(size, GFP_KERNEL);
+       if (hwmgr->current_ps == NULL)
+               return -ENOMEM;
+
        state = hwmgr->ps;
 
        for (i = 0; i < table_entries; i++) {
@@ -140,7 +172,8 @@ int hw_init_power_state_table(struct pp_hwmgr *hwmgr)
 
                if (state->classification.flags & PP_StateClassificationFlag_Boot) {
                        hwmgr->boot_ps = state;
-                       hwmgr->current_ps = hwmgr->request_ps = state;
+                       memcpy(hwmgr->current_ps, state, size);
+                       memcpy(hwmgr->request_ps, state, size);
                }
 
                state->id = i + 1; /* assigned unique num for every power state id */
@@ -150,6 +183,7 @@ int hw_init_power_state_table(struct pp_hwmgr *hwmgr)
                state = (struct pp_power_state *)((unsigned long)state + size);
        }
 
+
        return 0;
 }
 
@@ -182,30 +216,6 @@ int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index,
        return 0;
 }
 
-int phm_wait_for_register_unequal(struct pp_hwmgr *hwmgr,
-                               uint32_t index, uint32_t value, uint32_t mask)
-{
-       uint32_t i;
-       uint32_t cur_value;
-
-       if (hwmgr == NULL || hwmgr->device == NULL) {
-               printk(KERN_ERR "[ powerplay ] Invalid Hardware Manager!");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < hwmgr->usec_timeout; i++) {
-               cur_value = cgs_read_register(hwmgr->device, index);
-               if ((cur_value & mask) != (value & mask))
-                       break;
-               udelay(1);
-       }
-
-       /* timeout means wrong logic*/
-       if (i == hwmgr->usec_timeout)
-               return -1;
-       return 0;
-}
-
 
 /**
  * Returns once the part of the register indicated by the mask has
@@ -227,21 +237,7 @@ void phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr,
        phm_wait_on_register(hwmgr, indirect_port + 1, mask, value);
 }
 
-void phm_wait_for_indirect_register_unequal(struct pp_hwmgr *hwmgr,
-                                       uint32_t indirect_port,
-                                       uint32_t index,
-                                       uint32_t value,
-                                       uint32_t mask)
-{
-       if (hwmgr == NULL || hwmgr->device == NULL) {
-               printk(KERN_ERR "[ powerplay ] Invalid Hardware Manager!");
-               return;
-       }
 
-       cgs_write_register(hwmgr->device, indirect_port, index);
-       phm_wait_for_register_unequal(hwmgr, indirect_port + 1,
-                                     value, mask);
-}
 
 bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr)
 {
@@ -403,12 +399,9 @@ int phm_reset_single_dpm_table(void *table,
 
        struct vi_dpm_table *dpm_table = (struct vi_dpm_table *)table;
 
-       PP_ASSERT_WITH_CODE(count <= max,
-                       "Fatal error, can not set up single DPM table entries to exceed max number!",
-                          );
+       dpm_table->count = count > max ? max : count;
 
-       dpm_table->count = count;
-       for (i = 0; i < max; i++)
+       for (i = 0; i < dpm_table->count; i++)
                dpm_table->dpm_level[i].enabled = false;
 
        return 0;
@@ -462,6 +455,27 @@ uint8_t phm_get_voltage_index(
        return i - 1;
 }
 
+uint8_t phm_get_voltage_id(pp_atomctrl_voltage_table *voltage_table,
+               uint32_t voltage)
+{
+       uint8_t count = (uint8_t) (voltage_table->count);
+       uint8_t i = 0;
+
+       PP_ASSERT_WITH_CODE((NULL != voltage_table),
+               "Voltage Table empty.", return 0;);
+       PP_ASSERT_WITH_CODE((0 != count),
+               "Voltage Table empty.", return 0;);
+
+       for (i = 0; i < count; i++) {
+               /* find first voltage bigger than requested */
+               if (voltage_table->entries[i].value >= voltage)
+                       return i;
+       }
+
+       /* voltage is bigger than max voltage in the table */
+       return i - 1;
+}
+
 uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_table, uint16_t vddci)
 {
        uint32_t  i;
@@ -549,7 +563,8 @@ int phm_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr
                table_clk_vlt->entries[2].v = 810;
                table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_PERFORMANCE;
                table_clk_vlt->entries[3].v = 900;
-               pptable_info->vddc_dep_on_dal_pwrl = table_clk_vlt;
+               if (pptable_info != NULL)
+                       pptable_info->vddc_dep_on_dal_pwrl = table_clk_vlt;
                hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
        }
 
@@ -615,3 +630,186 @@ void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr)
        printk(KERN_ERR "DAL requested level can not"
                        " found a available voltage in VDDC DPM Table \n");
 }
+
+void hwmgr_init_default_caps(struct pp_hwmgr *hwmgr)
+{
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableVoltageTransition);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableEngineTransition);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMemoryTransition);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMGClockGating);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMGCGTSSM);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableLSClockGating);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_Force3DClockSupport);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableLightSleep);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableMCLS);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisablePowerGating);
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableDPM);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DisableSMUUVDHandshake);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ThermalAutoThrottling);
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_NoOD5Support);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UserMaxClockForMultiDisplays);
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VpuRecoveryInProgress);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDDPM);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_VCEDPM);
+
+       if (acpi_atcs_functions_supported(hwmgr->device, ATCS_FUNCTION_PCIE_PERFORMANCE_REQUEST) &&
+               acpi_atcs_functions_supported(hwmgr->device, ATCS_FUNCTION_PCIE_DEVICE_READY_NOTIFICATION))
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+               PHM_PlatformCaps_DynamicPatchPowerState);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+               PHM_PlatformCaps_EnableSMU7ThermalManagement);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_DynamicPowerManagement);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_SMC);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_DynamicUVDState);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_FanSpeedInTableIsRPM);
+
+       return;
+}
+
+int hwmgr_set_user_specify_caps(struct pp_hwmgr *hwmgr)
+{
+       if (amdgpu_sclk_deep_sleep_en)
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep);
+       else
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep);
+
+       if (amdgpu_powercontainment)
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                           PHM_PlatformCaps_PowerContainment);
+       else
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                           PHM_PlatformCaps_PowerContainment);
+
+       hwmgr->feature_mask = amdgpu_pp_feature_mask;
+
+       return 0;
+}
+
+int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+                               uint32_t sclk, uint16_t id, uint16_t *voltage)
+{
+       uint32_t vol;
+       int ret = 0;
+
+       if (hwmgr->chip_id < CHIP_POLARIS10) {
+               atomctrl_get_voltage_evv_on_sclk(hwmgr, voltage_type, sclk, id, voltage);
+               if (*voltage >= 2000 || *voltage == 0)
+                       *voltage = 1150;
+       } else {
+               ret = atomctrl_get_voltage_evv_on_sclk_ai(hwmgr, voltage_type, sclk, id, &vol);
+               *voltage = (uint16_t)vol/100;
+       }
+       return ret;
+}
+
+int polaris_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+       /* power tune caps Assume disabled */
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_SQRamping);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_DBRamping);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_TDRamping);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_TCPRamping);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                                       PHM_PlatformCaps_CAC);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_RegulatorHot);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_AutomaticDCTransition);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_TablelessHardwareInterface);
+
+       if (hwmgr->chip_id == CHIP_POLARIS11)
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_SPLLShutdownSupport);
+       return 0;
+}
+
+int fiji_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SQRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_DBRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TDRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TCPRamping);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TablelessHardwareInterface);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CAC);
+       return 0;
+}
+
+int tonga_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SQRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_DBRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TDRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TCPRamping);
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                     PHM_PlatformCaps_UVDPowerGating);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                     PHM_PlatformCaps_VCEPowerGating);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                        PHM_PlatformCaps_TablelessHardwareInterface);
+
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CAC);
+
+       return 0;
+}
+
+int topaz_set_asic_special_caps(struct pp_hwmgr *hwmgr)
+{
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SQRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_DBRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TDRamping);
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_TCPRamping);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                        PHM_PlatformCaps_TablelessHardwareInterface);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CAC);
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                   PHM_PlatformCaps_EVV);
+       return 0;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.c
deleted file mode 100644 (file)
index b5edb51..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright 2016 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 "polaris10_clockpowergating.h"
-
-int polaris10_phm_powerdown_uvd(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_uvd_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_UVDPowerOFF);
-       return 0;
-}
-
-int polaris10_phm_powerup_uvd(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_uvd_power_gating(hwmgr)) {
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                 PHM_PlatformCaps_UVDDynamicPowerGating)) {
-                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_UVDPowerON, 1);
-               } else {
-                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_UVDPowerON, 0);
-               }
-       }
-
-       return 0;
-}
-
-int polaris10_phm_powerdown_vce(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_vce_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_VCEPowerOFF);
-       return 0;
-}
-
-int polaris10_phm_powerup_vce(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_vce_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_VCEPowerON);
-       return 0;
-}
-
-int polaris10_phm_powerdown_samu(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SamuPowerGating))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SAMPowerOFF);
-       return 0;
-}
-
-int polaris10_phm_powerup_samu(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SamuPowerGating))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SAMPowerON);
-       return 0;
-}
-
-int polaris10_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-       data->samu_power_gated = false;
-
-       polaris10_phm_powerup_uvd(hwmgr);
-       polaris10_phm_powerup_vce(hwmgr);
-       polaris10_phm_powerup_samu(hwmgr);
-
-       return 0;
-}
-
-int polaris10_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->uvd_power_gated == bgate)
-               return 0;
-
-       data->uvd_power_gated = bgate;
-
-       if (bgate) {
-               cgs_set_clockgating_state(hwmgr->device,
-                               AMD_IP_BLOCK_TYPE_UVD,
-                               AMD_CG_STATE_GATE);
-               polaris10_update_uvd_dpm(hwmgr, true);
-               polaris10_phm_powerdown_uvd(hwmgr);
-       } else {
-               polaris10_phm_powerup_uvd(hwmgr);
-               polaris10_update_uvd_dpm(hwmgr, false);
-               cgs_set_clockgating_state(hwmgr->device,
-                               AMD_IP_BLOCK_TYPE_UVD,
-                               AMD_CG_STATE_UNGATE);
-       }
-
-       return 0;
-}
-
-int polaris10_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->vce_power_gated == bgate)
-               return 0;
-
-       data->vce_power_gated = bgate;
-
-       if (bgate) {
-               cgs_set_clockgating_state(hwmgr->device,
-                               AMD_IP_BLOCK_TYPE_VCE,
-                               AMD_CG_STATE_GATE);
-               polaris10_update_vce_dpm(hwmgr, true);
-               polaris10_phm_powerdown_vce(hwmgr);
-       } else {
-               polaris10_phm_powerup_vce(hwmgr);
-               polaris10_update_vce_dpm(hwmgr, false);
-               cgs_set_clockgating_state(hwmgr->device,
-                               AMD_IP_BLOCK_TYPE_VCE,
-                               AMD_CG_STATE_UNGATE);
-       }
-       return 0;
-}
-
-int polaris10_phm_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->samu_power_gated == bgate)
-               return 0;
-
-       data->samu_power_gated = bgate;
-
-       if (bgate) {
-               polaris10_update_samu_dpm(hwmgr, true);
-               polaris10_phm_powerdown_samu(hwmgr);
-       } else {
-               polaris10_phm_powerup_samu(hwmgr);
-               polaris10_update_samu_dpm(hwmgr, false);
-       }
-
-       return 0;
-}
-
-int polaris10_phm_update_clock_gatings(struct pp_hwmgr *hwmgr,
-                                       const uint32_t *msg_id)
-{
-       PPSMC_Msg msg;
-       uint32_t value;
-
-       switch ((*msg_id & PP_GROUP_MASK) >> PP_GROUP_SHIFT) {
-       case PP_GROUP_GFX:
-               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
-               case PP_BLOCK_GFX_CG:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_CGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                                       ? PPSMC_MSG_EnableClockGatingFeature
-                                       : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_CGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_GFX_3D:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_3DCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if  (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_3DLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_GFX_RLC:
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_RLC_LS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_GFX_CP:
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_CP_LS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_GFX_MG:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = (CG_CPF_MGCG_MASK | CG_RLC_MGCG_MASK |
-                                               CG_GFX_OTHERS_MGCG_MASK);
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               default:
-                       return -1;
-               }
-               break;
-
-       case PP_GROUP_SYS:
-               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
-               case PP_BLOCK_SYS_BIF:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_BIF_MGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       if  (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_BIF_MGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_MC:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_MC_MGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_MC_MGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_DRM:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_DRM_MGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_DRM_MGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_HDP:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_HDP_MGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_HDP_MGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_SDMA:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_SDMA_MGCG_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_SDMA_MGLS_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_ROM:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
-                                               PPSMC_MSG_EnableClockGatingFeature :
-                                               PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_ROM_MASK;
-
-                               if (smum_send_msg_to_smc_with_parameter(
-                                               hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               default:
-                       return -1;
-
-               }
-               break;
-
-       default:
-               return -1;
-
-       }
-
-       return 0;
-}
-
-/* This function is for Polaris11 only for now,
- * Powerplay will only control the static per CU Power Gating.
- * Dynamic per CU Power Gating will be done in gfx.
- */
-int polaris10_phm_enable_per_cu_power_gating(struct pp_hwmgr *hwmgr, bool enable)
-{
-       struct cgs_system_info sys_info = {0};
-       uint32_t active_cus;
-       int result;
-
-       sys_info.size = sizeof(struct cgs_system_info);
-       sys_info.info_id = CGS_SYSTEM_INFO_GFX_CU_INFO;
-
-       result = cgs_query_system_info(hwmgr->device, &sys_info);
-
-       if (result)
-               return -EINVAL;
-       else
-               active_cus = sys_info.value;
-
-       if (enable)
-               return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_GFX_CU_PG_ENABLE, active_cus);
-       else
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_GFX_CU_PG_DISABLE);
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_clockpowergating.h
deleted file mode 100644 (file)
index 88d68cb..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2016 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 _POLARIS10_CLOCK_POWER_GATING_H_
-#define _POLARIS10_CLOCK_POWER_GATING_H_
-
-#include "polaris10_hwmgr.h"
-#include "pp_asicblocks.h"
-
-int polaris10_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_phm_powerdown_uvd(struct pp_hwmgr *hwmgr);
-int polaris10_phm_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_phm_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr);
-int polaris10_phm_update_clock_gatings(struct pp_hwmgr *hwmgr,
-                                       const uint32_t *msg_id);
-int polaris10_phm_enable_per_cu_power_gating(struct pp_hwmgr *hwmgr, bool enable);
-
-#endif /* _POLARIS10_CLOCK_POWER_GATING_H_ */
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_dyn_defaults.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_dyn_defaults.h
deleted file mode 100644 (file)
index f78ffd9..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2015 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 POLARIS10_DYN_DEFAULTS_H
-#define POLARIS10_DYN_DEFAULTS_H
-
-
-enum Polaris10dpm_TrendDetection {
-       Polaris10Adpm_TrendDetection_AUTO,
-       Polaris10Adpm_TrendDetection_UP,
-       Polaris10Adpm_TrendDetection_DOWN
-};
-typedef enum Polaris10dpm_TrendDetection Polaris10dpm_TrendDetection;
-
-/*  We need to fill in the default values */
-
-
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT0              0x3FFFC102
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT1              0x000400
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT2              0xC00080
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT3              0xC00200
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT4              0xC01680
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT5              0xC00033
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT6              0xC00033
-#define PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT7              0x3FFFC000
-
-
-#define PPPOLARIS10_THERMALPROTECTCOUNTER_DFLT            0x200
-#define PPPOLARIS10_STATICSCREENTHRESHOLDUNIT_DFLT        0
-#define PPPOLARIS10_STATICSCREENTHRESHOLD_DFLT            0x00C8
-#define PPPOLARIS10_GFXIDLECLOCKSTOPTHRESHOLD_DFLT        0x200
-#define PPPOLARIS10_REFERENCEDIVIDER_DFLT                  4
-
-#define PPPOLARIS10_ULVVOLTAGECHANGEDELAY_DFLT             1687
-
-#define PPPOLARIS10_CGULVPARAMETER_DFLT                    0x00040035
-#define PPPOLARIS10_CGULVCONTROL_DFLT                      0x00007450
-#define PPPOLARIS10_TARGETACTIVITY_DFLT                     50
-#define PPPOLARIS10_MCLK_TARGETACTIVITY_DFLT                10
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.c
deleted file mode 100644 (file)
index 769636a..0000000
+++ /dev/null
@@ -1,5290 +0,0 @@
-/*
- * Copyright 2015 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/module.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-#include <asm/div64.h>
-#include "linux/delay.h"
-#include "pp_acpi.h"
-#include "hwmgr.h"
-#include "polaris10_hwmgr.h"
-#include "polaris10_powertune.h"
-#include "polaris10_dyn_defaults.h"
-#include "polaris10_smumgr.h"
-#include "pp_debug.h"
-#include "ppatomctrl.h"
-#include "atombios.h"
-#include "tonga_pptable.h"
-#include "pppcielanes.h"
-#include "amd_pcie_helpers.h"
-#include "hardwaremanager.h"
-#include "tonga_processpptables.h"
-#include "cgs_common.h"
-#include "smu74.h"
-#include "smu_ucode_xfer_vi.h"
-#include "smu74_discrete.h"
-#include "smu/smu_7_1_3_d.h"
-#include "smu/smu_7_1_3_sh_mask.h"
-#include "gmc/gmc_8_1_d.h"
-#include "gmc/gmc_8_1_sh_mask.h"
-#include "oss/oss_3_0_d.h"
-#include "gca/gfx_8_0_d.h"
-#include "bif/bif_5_0_d.h"
-#include "bif/bif_5_0_sh_mask.h"
-#include "gmc/gmc_8_1_d.h"
-#include "gmc/gmc_8_1_sh_mask.h"
-#include "bif/bif_5_0_d.h"
-#include "bif/bif_5_0_sh_mask.h"
-#include "dce/dce_10_0_d.h"
-#include "dce/dce_10_0_sh_mask.h"
-
-#include "polaris10_thermal.h"
-#include "polaris10_clockpowergating.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 0x40000
-
-#define SMC_CG_IND_START            0xc0030000
-#define SMC_CG_IND_END              0xc0040000
-
-#define VOLTAGE_SCALE               4
-#define VOLTAGE_VID_OFFSET_SCALE1   625
-#define VOLTAGE_VID_OFFSET_SCALE2   100
-
-#define VDDC_VDDCI_DELTA            200
-
-#define MEM_FREQ_LOW_LATENCY        25000
-#define MEM_FREQ_HIGH_LATENCY       80000
-
-#define MEM_LATENCY_HIGH            45
-#define MEM_LATENCY_LOW             35
-#define MEM_LATENCY_ERR             0xFFFF
-
-#define MC_SEQ_MISC0_GDDR5_SHIFT 28
-#define MC_SEQ_MISC0_GDDR5_MASK  0xf0000000
-#define MC_SEQ_MISC0_GDDR5_VALUE 5
-
-
-#define PCIE_BUS_CLK                10000
-#define TCLK                        (PCIE_BUS_CLK / 10)
-
-
-static const uint16_t polaris10_clock_stretcher_lookup_table[2][4] =
-{ {600, 1050, 3, 0}, {600, 1050, 6, 1} };
-
-/*  [FF, SS] type, [] 4 voltage ranges, and [Floor Freq, Boundary Freq, VID min , VID max] */
-static const uint32_t polaris10_clock_stretcher_ddt_table[2][4][4] =
-{ { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
-  { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} } };
-
-/*  [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%] (coming from PWR_CKS_CNTL.stretch_amount reg spec) */
-static const uint8_t polaris10_clock_stretch_amount_conversion[2][6] =
-{ {0, 1, 3, 2, 4, 5}, {0, 2, 4, 5, 6, 5} };
-
-/** Values for the CG_THERMAL_CTRL::DPM_EVENT_SRC field. */
-enum DPM_EVENT_SRC {
-       DPM_EVENT_SRC_ANALOG = 0,
-       DPM_EVENT_SRC_EXTERNAL = 1,
-       DPM_EVENT_SRC_DIGITAL = 2,
-       DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,
-       DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL = 4
-};
-
-static const unsigned long PhwPolaris10_Magic = (unsigned long)(PHM_VIslands_Magic);
-
-struct polaris10_power_state *cast_phw_polaris10_power_state(
-                                 struct pp_hw_power_state *hw_ps)
-{
-       PP_ASSERT_WITH_CODE((PhwPolaris10_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL);
-
-       return (struct polaris10_power_state *)hw_ps;
-}
-
-const struct polaris10_power_state *cast_const_phw_polaris10_power_state(
-                                const struct pp_hw_power_state *hw_ps)
-{
-       PP_ASSERT_WITH_CODE((PhwPolaris10_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL);
-
-       return (const struct polaris10_power_state *)hw_ps;
-}
-
-static bool polaris10_is_dpm_running(struct pp_hwmgr *hwmgr)
-{
-       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
-                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
-                       ? true : false;
-}
-
-/**
- * Find the MC microcode version and store it in the HwMgr struct
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int phm_get_mc_microcode_version (struct pp_hwmgr *hwmgr)
-{
-       cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
-
-       hwmgr->microcode_version_info.MC = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
-
-       return 0;
-}
-
-uint16_t phm_get_current_pcie_speed(struct pp_hwmgr *hwmgr)
-{
-       uint32_t speedCntl = 0;
-
-       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
-       speedCntl = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__PCIE,
-                       ixPCIE_LC_SPEED_CNTL);
-       return((uint16_t)PHM_GET_FIELD(speedCntl,
-                       PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
-}
-
-int phm_get_current_pcie_lane_number(struct pp_hwmgr *hwmgr)
-{
-       uint32_t link_width;
-
-       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
-       link_width = PHM_READ_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
-                       PCIE_LC_LINK_WIDTH_CNTL, LC_LINK_WIDTH_RD);
-
-       PP_ASSERT_WITH_CODE((7 >= link_width),
-                       "Invalid PCIe lane width!", return 0);
-
-       return decode_pcie_lane_width(link_width);
-}
-
-/**
-* Enable voltage control
-*
-* @param    pHwMgr  the address of the powerplay hardware manager.
-* @return   always PP_Result_OK
-*/
-int polaris10_enable_smc_voltage_controller(struct pp_hwmgr *hwmgr)
-{
-       PP_ASSERT_WITH_CODE(
-               (hwmgr->smumgr->smumgr_funcs->send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Voltage_Cntl_Enable) == 0),
-               "Failed to enable voltage DPM during DPM Start Function!",
-               return 1;
-       );
-
-       return 0;
-}
-
-/**
-* Checks if we want to support voltage control
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-*/
-static bool polaris10_voltage_control(const struct pp_hwmgr *hwmgr)
-{
-       const struct polaris10_hwmgr *data =
-                       (const struct polaris10_hwmgr *)(hwmgr->backend);
-
-       return (POLARIS10_VOLTAGE_CONTROL_NONE != data->voltage_control);
-}
-
-/**
-* Enable voltage control
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_enable_voltage_control(struct pp_hwmgr *hwmgr)
-{
-       /* enable voltage control */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, VOLT_PWRMGT_EN, 1);
-
-       return 0;
-}
-
-/**
-* Create Voltage Tables.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_construct_voltage_tables(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)hwmgr->pptable;
-       int result;
-
-       if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT,
-                               &(data->mvdd_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve MVDD table.",
-                               return result);
-       } else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
-               result = phm_get_svi2_mvdd_voltage_table(&(data->mvdd_voltage_table),
-                               table_info->vdd_dep_on_mclk);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 MVDD table from dependancy table.",
-                               return result;);
-       }
-
-       if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT,
-                               &(data->vddci_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve VDDCI table.",
-                               return result);
-       } else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
-               result = phm_get_svi2_vddci_voltage_table(&(data->vddci_voltage_table),
-                               table_info->vdd_dep_on_mclk);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 VDDCI table from dependancy table.",
-                               return result);
-       }
-
-       if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               result = phm_get_svi2_vdd_voltage_table(&(data->vddc_voltage_table),
-                               table_info->vddc_lookup_table);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to retrieve SVI2 VDDC table from lookup table.",
-                               return result);
-       }
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddc_voltage_table.count <= (SMU74_MAX_LEVELS_VDDC)),
-                       "Too many voltage values for VDDC. Trimming to fit state table.",
-                       phm_trim_voltage_table_to_fit_state_table(SMU74_MAX_LEVELS_VDDC,
-                                                               &(data->vddc_voltage_table)));
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddci_voltage_table.count <= (SMU74_MAX_LEVELS_VDDCI)),
-                       "Too many voltage values for VDDCI. Trimming to fit state table.",
-                       phm_trim_voltage_table_to_fit_state_table(SMU74_MAX_LEVELS_VDDCI,
-                                       &(data->vddci_voltage_table)));
-
-       PP_ASSERT_WITH_CODE(
-                       (data->mvdd_voltage_table.count <= (SMU74_MAX_LEVELS_MVDD)),
-                       "Too many voltage values for MVDD. Trimming to fit state table.",
-                       phm_trim_voltage_table_to_fit_state_table(SMU74_MAX_LEVELS_MVDD,
-                                                          &(data->mvdd_voltage_table)));
-
-       return 0;
-}
-
-/**
-* Programs static screed detection parameters
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_program_static_screen_threshold_parameters(
-                                                       struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* Set static screen threshold unit */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD_UNIT,
-                       data->static_screen_threshold_unit);
-       /* Set static screen threshold */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD,
-                       data->static_screen_threshold);
-
-       return 0;
-}
-
-/**
-* Setup display gap for glitch free memory clock switching.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int polaris10_enable_display_gap(struct pp_hwmgr *hwmgr)
-{
-       uint32_t display_gap =
-                       cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                                       ixCG_DISPLAY_GAP_CNTL);
-
-       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
-                       DISP_GAP, DISPLAY_GAP_IGNORE);
-
-       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
-                       DISP_GAP_MCHG, DISPLAY_GAP_VBLANK);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_DISPLAY_GAP_CNTL, display_gap);
-
-       return 0;
-}
-
-/**
-* Programs activity state transition voting clients
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int polaris10_program_voting_clients(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* Clear reset for voting clients before enabling DPM */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 0);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 0);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_0, data->voting_rights_clients0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_1, data->voting_rights_clients1);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_2, data->voting_rights_clients2);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_3, data->voting_rights_clients3);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_4, data->voting_rights_clients4);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_5, data->voting_rights_clients5);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_6, data->voting_rights_clients6);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_7, data->voting_rights_clients7);
-
-       return 0;
-}
-
-static int polaris10_clear_voting_clients(struct pp_hwmgr *hwmgr)
-{
-       /* Reset voting clients before disabling DPM */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 1);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 1);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_0, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_1, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_2, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_3, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_4, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_5, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_6, 0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_FREQ_TRAN_VOTING_7, 0);
-
-       return 0;
-}
-
-/**
-* Get the location of various tables inside the FW image.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always  0
-*/
-static int polaris10_process_firmware_header(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
-       uint32_t tmp;
-       int result;
-       bool error = false;
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, DpmTable),
-                       &tmp, data->sram_end);
-
-       if (0 == result)
-               data->dpm_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, SoftRegisters),
-                       &tmp, data->sram_end);
-
-       if (!result) {
-               data->soft_regs_start = tmp;
-               smu_data->soft_regs_start = tmp;
-       }
-
-       error |= (0 != result);
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, mcRegisterTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->mc_reg_table_start = tmp;
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, FanTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->fan_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, mcArbDramTimingTable),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               data->arb_table_start = tmp;
-
-       error |= (0 != result);
-
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       SMU7_FIRMWARE_HEADER_LOCATION +
-                       offsetof(SMU74_Firmware_Header, Version),
-                       &tmp, data->sram_end);
-
-       if (!result)
-               hwmgr->microcode_version_info.SMC = tmp;
-
-       error |= (0 != result);
-
-       return error ? -1 : 0;
-}
-
-/* Copy one arb setting to another and then switch the active set.
- * arb_src and arb_dest is one of the MC_CG_ARB_FREQ_Fx constants.
- */
-static int polaris10_copy_and_switch_arb_sets(struct pp_hwmgr *hwmgr,
-               uint32_t arb_src, uint32_t arb_dest)
-{
-       uint32_t mc_arb_dram_timing;
-       uint32_t mc_arb_dram_timing2;
-       uint32_t burst_time;
-       uint32_t mc_cg_config;
-
-       switch (arb_src) {
-       case MC_CG_ARB_FREQ_F0:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
-               break;
-       case MC_CG_ARB_FREQ_F1:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       switch (arb_dest) {
-       case MC_CG_ARB_FREQ_F0:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0, burst_time);
-               break;
-       case MC_CG_ARB_FREQ_F1:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1, burst_time);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       mc_cg_config = cgs_read_register(hwmgr->device, mmMC_CG_CONFIG);
-       mc_cg_config |= 0x0000000F;
-       cgs_write_register(hwmgr->device, mmMC_CG_CONFIG, mc_cg_config);
-       PHM_WRITE_FIELD(hwmgr->device, MC_ARB_CG, CG_ARB_REQ, arb_dest);
-
-       return 0;
-}
-
-static int polaris10_reset_to_default(struct pp_hwmgr *hwmgr)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_ResetToDefaults);
-}
-
-/**
-* Initial switch from ARB F0->F1
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-* This function is to be called from the SetPowerState table.
-*/
-static int polaris10_initial_switch_from_arbf0_to_f1(struct pp_hwmgr *hwmgr)
-{
-       return polaris10_copy_and_switch_arb_sets(hwmgr,
-                       MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
-}
-
-static int polaris10_force_switch_to_arbf0(struct pp_hwmgr *hwmgr)
-{
-       uint32_t tmp;
-
-       tmp = (cgs_read_ind_register(hwmgr->device,
-                       CGS_IND_REG__SMC, ixSMC_SCRATCH9) &
-                       0x0000ff00) >> 8;
-
-       if (tmp == MC_CG_ARB_FREQ_F0)
-               return 0;
-
-       return polaris10_copy_and_switch_arb_sets(hwmgr,
-                       tmp, MC_CG_ARB_FREQ_F0);
-}
-
-static int polaris10_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
-       uint32_t i, max_entry;
-
-       PP_ASSERT_WITH_CODE((data->use_pcie_performance_levels ||
-                       data->use_pcie_power_saving_levels), "No pcie performance levels!",
-                       return -EINVAL);
-
-       if (data->use_pcie_performance_levels &&
-                       !data->use_pcie_power_saving_levels) {
-               data->pcie_gen_power_saving = data->pcie_gen_performance;
-               data->pcie_lane_power_saving = data->pcie_lane_performance;
-       } else if (!data->use_pcie_performance_levels &&
-                       data->use_pcie_power_saving_levels) {
-               data->pcie_gen_performance = data->pcie_gen_power_saving;
-               data->pcie_lane_performance = data->pcie_lane_power_saving;
-       }
-
-       phm_reset_single_dpm_table(&data->dpm_table.pcie_speed_table,
-                                       SMU74_MAX_LEVELS_LINK,
-                                       MAX_REGULAR_DPM_NUMBER);
-
-       if (pcie_table != NULL) {
-               /* max_entry is used to make sure we reserve one PCIE level
-                * for boot level (fix for A+A PSPP issue).
-                * If PCIE table from PPTable have ULV entry + 8 entries,
-                * then ignore the last entry.*/
-               max_entry = (SMU74_MAX_LEVELS_LINK < pcie_table->count) ?
-                               SMU74_MAX_LEVELS_LINK : pcie_table->count;
-               for (i = 1; i < max_entry; i++) {
-                       phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i - 1,
-                                       get_pcie_gen_support(data->pcie_gen_cap,
-                                                       pcie_table->entries[i].gen_speed),
-                                       get_pcie_lane_support(data->pcie_lane_cap,
-                                                       pcie_table->entries[i].lane_width));
-               }
-               data->dpm_table.pcie_speed_table.count = max_entry - 1;
-
-               /* Setup BIF_SCLK levels */
-               for (i = 0; i < max_entry; i++)
-                       data->bif_sclk_table[i] = pcie_table->entries[i].pcie_sclk;
-       } else {
-               /* Hardcode Pcie Table */
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 0,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Min_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 1,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Min_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 2,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 3,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 4,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 5,
-                               get_pcie_gen_support(data->pcie_gen_cap,
-                                               PP_Max_PCIEGen),
-                               get_pcie_lane_support(data->pcie_lane_cap,
-                                               PP_Max_PCIELane));
-
-               data->dpm_table.pcie_speed_table.count = 6;
-       }
-       /* Populate last level for boot PCIE level, but do not increment count. */
-       phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
-                       data->dpm_table.pcie_speed_table.count,
-                       get_pcie_gen_support(data->pcie_gen_cap,
-                                       PP_Min_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap,
-                                       PP_Max_PCIELane));
-
-       return 0;
-}
-
-/*
- * This function is to initalize all DPM state tables
- * for SMU7 based on the dependency table.
- * Dynamic state patching function will then trim these
- * state tables to the allowed range based
- * on the power policy or external client requests,
- * such as UVD request, etc.
- */
-int polaris10_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i;
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                       table_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(dep_sclk_table != NULL,
-                       "SCLK dependency table is missing. This table is mandatory",
-                       return -EINVAL);
-       PP_ASSERT_WITH_CODE(dep_sclk_table->count >= 1,
-                       "SCLK dependency table has to have is missing."
-                       "This table is mandatory",
-                       return -EINVAL);
-
-       PP_ASSERT_WITH_CODE(dep_mclk_table != NULL,
-                       "MCLK dependency table is missing. This table is mandatory",
-                       return -EINVAL);
-       PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
-                       "MCLK dependency table has to have is missing."
-                       "This table is mandatory",
-                       return -EINVAL);
-
-       /* clear the state table to reset everything to default */
-       phm_reset_single_dpm_table(
-                       &data->dpm_table.sclk_table, SMU74_MAX_LEVELS_GRAPHICS, MAX_REGULAR_DPM_NUMBER);
-       phm_reset_single_dpm_table(
-                       &data->dpm_table.mclk_table, SMU74_MAX_LEVELS_MEMORY, MAX_REGULAR_DPM_NUMBER);
-
-
-       /* Initialize Sclk DPM table based on allow Sclk values */
-       data->dpm_table.sclk_table.count = 0;
-       for (i = 0; i < dep_sclk_table->count; i++) {
-               if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count - 1].value !=
-                                               dep_sclk_table->entries[i].clk) {
-
-                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
-                                       dep_sclk_table->entries[i].clk;
-
-                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled =
-                                       (i == 0) ? true : false;
-                       data->dpm_table.sclk_table.count++;
-               }
-       }
-
-       /* Initialize Mclk DPM table based on allow Mclk values */
-       data->dpm_table.mclk_table.count = 0;
-       for (i = 0; i < dep_mclk_table->count; i++) {
-               if (i == 0 || data->dpm_table.mclk_table.dpm_levels
-                               [data->dpm_table.mclk_table.count - 1].value !=
-                                               dep_mclk_table->entries[i].clk) {
-                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
-                                                       dep_mclk_table->entries[i].clk;
-                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled =
-                                                       (i == 0) ? true : false;
-                       data->dpm_table.mclk_table.count++;
-               }
-       }
-
-       /* setup PCIE gen speed levels */
-       polaris10_setup_default_pcie_table(hwmgr);
-
-       /* save a copy of the default DPM table */
-       memcpy(&(data->golden_dpm_table), &(data->dpm_table),
-                       sizeof(struct polaris10_dpm_table));
-
-       return 0;
-}
-
-uint8_t convert_to_vid(uint16_t vddc)
-{
-       return (uint8_t) ((6200 - (vddc * VOLTAGE_SCALE)) / 25);
-}
-
-/**
- * Mvdd table preparation for SMC.
- *
- * @param    *hwmgr The address of the hardware manager.
- * @param    *table The SMC DPM table structure to be populated.
- * @return   0
- */
-static int polaris10_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
-                       SMU74_Discrete_DpmTable *table)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t count, level;
-
-       if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               count = data->mvdd_voltage_table.count;
-               if (count > SMU_MAX_SMIO_LEVELS)
-                       count = SMU_MAX_SMIO_LEVELS;
-               for (level = 0; level < count; level++) {
-                       table->SmioTable2.Pattern[level].Voltage =
-                               PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[count].value * VOLTAGE_SCALE);
-                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
-                       table->SmioTable2.Pattern[level].Smio =
-                               (uint8_t) level;
-                       table->Smio[level] |=
-                               data->mvdd_voltage_table.entries[level].smio_low;
-               }
-               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
-
-               table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count);
-       }
-
-       return 0;
-}
-
-static int polaris10_populate_smc_vddci_table(struct pp_hwmgr *hwmgr,
-                                       struct SMU74_Discrete_DpmTable *table)
-{
-       uint32_t count, level;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       count = data->vddci_voltage_table.count;
-
-       if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
-               if (count > SMU_MAX_SMIO_LEVELS)
-                       count = SMU_MAX_SMIO_LEVELS;
-               for (level = 0; level < count; ++level) {
-                       table->SmioTable1.Pattern[level].Voltage =
-                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[level].value * VOLTAGE_SCALE);
-                       table->SmioTable1.Pattern[level].Smio = (uint8_t) level;
-
-                       table->Smio[level] |= data->vddci_voltage_table.entries[level].smio_low;
-               }
-       }
-
-       table->SmioMask1 = data->vddci_voltage_table.mask_low;
-
-       return 0;
-}
-
-/**
-* Preparation of vddc and vddgfx CAC tables for SMC.
-*
-* @param    hwmgr  the address of the hardware manager
-* @param    table  the SMC DPM table structure to be populated
-* @return   always 0
-*/
-static int polaris10_populate_cac_table(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       uint32_t count;
-       uint8_t index;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
-                       table_info->vddc_lookup_table;
-       /* tables is already swapped, so in order to use the value from it,
-        * we need to swap it back.
-        * We are populating vddc CAC data to BapmVddc table
-        * in split and merged mode
-        */
-       for (count = 0; count < lookup_table->count; count++) {
-               index = phm_get_voltage_index(lookup_table,
-                               data->vddc_voltage_table.entries[count].value);
-               table->BapmVddcVidLoSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_low);
-               table->BapmVddcVidHiSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_mid);
-               table->BapmVddcVidHiSidd2[count] = convert_to_vid(lookup_table->entries[index].us_cac_high);
-       }
-
-       return 0;
-}
-
-/**
-* Preparation of voltage tables for SMC.
-*
-* @param    hwmgr   the address of the hardware manager
-* @param    table   the SMC DPM table structure to be populated
-* @return   always  0
-*/
-
-int polaris10_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       polaris10_populate_smc_vddci_table(hwmgr, table);
-       polaris10_populate_smc_mvdd_table(hwmgr, table);
-       polaris10_populate_cac_table(hwmgr, table);
-
-       return 0;
-}
-
-static int polaris10_populate_ulv_level(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_Ulv *state)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       state->CcPwrDynRm = 0;
-       state->CcPwrDynRm1 = 0;
-
-       state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
-       state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
-                       VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
-
-       state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1;
-
-       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
-       CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
-
-       return 0;
-}
-
-static int polaris10_populate_ulv_state(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       return polaris10_populate_ulv_level(hwmgr, &table->Ulv);
-}
-
-static int polaris10_populate_smc_link_level(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_dpm_table *dpm_table = &data->dpm_table;
-       int i;
-
-       /* Index (dpm_table->pcie_speed_table.count)
-        * is reserved for PCIE boot level. */
-       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
-               table->LinkLevel[i].PcieGenSpeed  =
-                               (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
-               table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
-                               dpm_table->pcie_speed_table.dpm_levels[i].param1);
-               table->LinkLevel[i].EnabledForActivity = 1;
-               table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
-               table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
-               table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
-       }
-
-       data->smc_state_table.LinkLevelCount =
-                       (uint8_t)dpm_table->pcie_speed_table.count;
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
-
-       return 0;
-}
-
-static uint32_t polaris10_get_xclk(struct pp_hwmgr *hwmgr)
-{
-       uint32_t reference_clock, tmp;
-       struct cgs_display_info info = {0};
-       struct cgs_mode_info mode_info;
-
-       info.mode_info = &mode_info;
-
-       tmp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL_2, MUX_TCLK_TO_XCLK);
-
-       if (tmp)
-               return TCLK;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-       reference_clock = mode_info.ref_clock;
-
-       tmp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL, XTALIN_DIVIDE);
-
-       if (0 != tmp)
-               return reference_clock / 4;
-
-       return reference_clock;
-}
-
-/**
-* Calculates the SCLK dividers using the provided engine clock
-*
-* @param    hwmgr  the address of the hardware manager
-* @param    clock  the engine clock to use to populate the structure
-* @param    sclk   the SMC SCLK structure to be populated
-*/
-static int polaris10_calculate_sclk_params(struct pp_hwmgr *hwmgr,
-               uint32_t clock, SMU_SclkSetting *sclk_setting)
-{
-       const struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const SMU74_Discrete_DpmTable *table = &(data->smc_state_table);
-       struct pp_atomctrl_clock_dividers_ai dividers;
-
-       uint32_t ref_clock;
-       uint32_t pcc_target_percent, pcc_target_freq, ss_target_percent, ss_target_freq;
-       uint8_t i;
-       int result;
-       uint64_t temp;
-
-       sclk_setting->SclkFrequency = clock;
-       /* get the engine clock dividers for this clock value */
-       result = atomctrl_get_engine_pll_dividers_ai(hwmgr, clock,  &dividers);
-       if (result == 0) {
-               sclk_setting->Fcw_int = dividers.usSclk_fcw_int;
-               sclk_setting->Fcw_frac = dividers.usSclk_fcw_frac;
-               sclk_setting->Pcc_fcw_int = dividers.usPcc_fcw_int;
-               sclk_setting->PllRange = dividers.ucSclkPllRange;
-               sclk_setting->Sclk_slew_rate = 0x400;
-               sclk_setting->Pcc_up_slew_rate = dividers.usPcc_fcw_slew_frac;
-               sclk_setting->Pcc_down_slew_rate = 0xffff;
-               sclk_setting->SSc_En = dividers.ucSscEnable;
-               sclk_setting->Fcw1_int = dividers.usSsc_fcw1_int;
-               sclk_setting->Fcw1_frac = dividers.usSsc_fcw1_frac;
-               sclk_setting->Sclk_ss_slew_rate = dividers.usSsc_fcw_slew_frac;
-               return result;
-       }
-
-       ref_clock = polaris10_get_xclk(hwmgr);
-
-       for (i = 0; i < NUM_SCLK_RANGE; i++) {
-               if (clock > data->range_table[i].trans_lower_frequency
-               && clock <= data->range_table[i].trans_upper_frequency) {
-                       sclk_setting->PllRange = i;
-                       break;
-               }
-       }
-
-       sclk_setting->Fcw_int = (uint16_t)((clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
-       temp = clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
-       temp <<= 0x10;
-       do_div(temp, ref_clock);
-       sclk_setting->Fcw_frac = temp & 0xffff;
-
-       pcc_target_percent = 10; /*  Hardcode 10% for now. */
-       pcc_target_freq = clock - (clock * pcc_target_percent / 100);
-       sclk_setting->Pcc_fcw_int = (uint16_t)((pcc_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
-
-       ss_target_percent = 2; /*  Hardcode 2% for now. */
-       sclk_setting->SSc_En = 0;
-       if (ss_target_percent) {
-               sclk_setting->SSc_En = 1;
-               ss_target_freq = clock - (clock * ss_target_percent / 100);
-               sclk_setting->Fcw1_int = (uint16_t)((ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
-               temp = ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
-               temp <<= 0x10;
-               do_div(temp, ref_clock);
-               sclk_setting->Fcw1_frac = temp & 0xffff;
-       }
-
-       return 0;
-}
-
-static int polaris10_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
-               struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
-               uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
-{
-       uint32_t i;
-       uint16_t vddci;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       *voltage = *mvdd = 0;
-
-       /* clock - voltage dependency table is empty table */
-       if (dep_table->count == 0)
-               return -EINVAL;
-
-       for (i = 0; i < dep_table->count; i++) {
-               /* find first sclk bigger than request */
-               if (dep_table->entries[i].clk >= clock) {
-                       *voltage |= (dep_table->entries[i].vddc *
-                                       VOLTAGE_SCALE) << VDDC_SHIFT;
-                       if (POLARIS10_VOLTAGE_CONTROL_NONE == data->vddci_control)
-                               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-                       else if (dep_table->entries[i].vddci)
-                               *voltage |= (dep_table->entries[i].vddci *
-                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-                       else {
-                               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
-                                               (dep_table->entries[i].vddc -
-                                                               (uint16_t)data->vddc_vddci_delta));
-                               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-                       }
-
-                       if (POLARIS10_VOLTAGE_CONTROL_NONE == data->mvdd_control)
-                               *mvdd = data->vbios_boot_state.mvdd_bootup_value *
-                                       VOLTAGE_SCALE;
-                       else if (dep_table->entries[i].mvdd)
-                               *mvdd = (uint32_t) dep_table->entries[i].mvdd *
-                                       VOLTAGE_SCALE;
-
-                       *voltage |= 1 << PHASES_SHIFT;
-                       return 0;
-               }
-       }
-
-       /* sclk is bigger than max sclk in the dependence table */
-       *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
-
-       if (POLARIS10_VOLTAGE_CONTROL_NONE == data->vddci_control)
-               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
-                               VOLTAGE_SCALE) << VDDCI_SHIFT;
-       else if (dep_table->entries[i-1].vddci) {
-               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
-                               (dep_table->entries[i].vddc -
-                                               (uint16_t)data->vddc_vddci_delta));
-               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-       }
-
-       if (POLARIS10_VOLTAGE_CONTROL_NONE == data->mvdd_control)
-               *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
-       else if (dep_table->entries[i].mvdd)
-               *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
-
-       return 0;
-}
-
-static const sclkFcwRange_t Range_Table[NUM_SCLK_RANGE] =
-{ {VCO_2_4, POSTDIV_DIV_BY_16,  75, 160, 112},
-  {VCO_3_6, POSTDIV_DIV_BY_16, 112, 224, 160},
-  {VCO_2_4, POSTDIV_DIV_BY_8,   75, 160, 112},
-  {VCO_3_6, POSTDIV_DIV_BY_8,  112, 224, 160},
-  {VCO_2_4, POSTDIV_DIV_BY_4,   75, 160, 112},
-  {VCO_3_6, POSTDIV_DIV_BY_4,  112, 216, 160},
-  {VCO_2_4, POSTDIV_DIV_BY_2,   75, 160, 108},
-  {VCO_3_6, POSTDIV_DIV_BY_2,  112, 216, 160} };
-
-static void polaris10_get_sclk_range_table(struct pp_hwmgr *hwmgr)
-{
-       uint32_t i, ref_clk;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       SMU74_Discrete_DpmTable  *table = &(data->smc_state_table);
-       struct pp_atom_ctrl_sclk_range_table range_table_from_vbios = { { {0} } };
-
-       ref_clk = polaris10_get_xclk(hwmgr);
-
-       if (0 == atomctrl_get_smc_sclk_range_table(hwmgr, &range_table_from_vbios)) {
-               for (i = 0; i < NUM_SCLK_RANGE; i++) {
-                       table->SclkFcwRangeTable[i].vco_setting = range_table_from_vbios.entry[i].ucVco_setting;
-                       table->SclkFcwRangeTable[i].postdiv = range_table_from_vbios.entry[i].ucPostdiv;
-                       table->SclkFcwRangeTable[i].fcw_pcc = range_table_from_vbios.entry[i].usFcw_pcc;
-
-                       table->SclkFcwRangeTable[i].fcw_trans_upper = range_table_from_vbios.entry[i].usFcw_trans_upper;
-                       table->SclkFcwRangeTable[i].fcw_trans_lower = range_table_from_vbios.entry[i].usRcw_trans_lower;
-
-                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
-                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
-                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
-               }
-               return;
-       }
-
-       for (i = 0; i < NUM_SCLK_RANGE; i++) {
-
-               data->range_table[i].trans_lower_frequency = (ref_clk * Range_Table[i].fcw_trans_lower) >> Range_Table[i].postdiv;
-               data->range_table[i].trans_upper_frequency = (ref_clk * Range_Table[i].fcw_trans_upper) >> Range_Table[i].postdiv;
-
-               table->SclkFcwRangeTable[i].vco_setting = Range_Table[i].vco_setting;
-               table->SclkFcwRangeTable[i].postdiv = Range_Table[i].postdiv;
-               table->SclkFcwRangeTable[i].fcw_pcc = Range_Table[i].fcw_pcc;
-
-               table->SclkFcwRangeTable[i].fcw_trans_upper = Range_Table[i].fcw_trans_upper;
-               table->SclkFcwRangeTable[i].fcw_trans_lower = Range_Table[i].fcw_trans_lower;
-
-               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
-               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
-               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
-       }
-}
-
-/**
-* Populates single SMC SCLK structure using the provided engine clock
-*
-* @param    hwmgr      the address of the hardware manager
-* @param    clock the engine clock to use to populate the structure
-* @param    sclk        the SMC SCLK structure to be populated
-*/
-
-static int polaris10_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
-               uint32_t clock, uint16_t sclk_al_threshold,
-               struct SMU74_Discrete_GraphicsLevel *level)
-{
-       int result, i, temp;
-       /* PP_Clocks minClocks; */
-       uint32_t mvdd;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       SMU_SclkSetting curr_sclk_setting = { 0 };
-
-       result = polaris10_calculate_sclk_params(hwmgr, clock, &curr_sclk_setting);
-
-       /* populate graphics levels */
-       result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                       table_info->vdd_dep_on_sclk, clock,
-                       &level->MinVoltage, &mvdd);
-
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "can not find VDDC voltage value for "
-                       "VDDC engine clock dependency table",
-                       return result);
-       level->ActivityLevel = sclk_al_threshold;
-
-       level->CcPwrDynRm = 0;
-       level->CcPwrDynRm1 = 0;
-       level->EnabledForActivity = 0;
-       level->EnabledForThrottle = 1;
-       level->UpHyst = 10;
-       level->DownHyst = 0;
-       level->VoltageDownHyst = 0;
-       level->PowerThrottle = 0;
-
-       /*
-       * TODO: get minimum clocks from dal configaration
-       * PECI_GetMinClockSettings(hwmgr->pPECI, &minClocks);
-       */
-       /* data->DisplayTiming.minClockInSR = minClocks.engineClockInSR; */
-
-       /* get level->DeepSleepDivId
-       if (phm_cap_enabled(hwmgr->platformDescriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
-               level->DeepSleepDivId = PhwFiji_GetSleepDividerIdFromClock(hwmgr, clock, minClocks.engineClockInSR);
-       */
-       PP_ASSERT_WITH_CODE((clock >= POLARIS10_MINIMUM_ENGINE_CLOCK), "Engine clock can't satisfy stutter requirement!", return 0);
-       for (i = POLARIS10_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
-               temp = clock >> i;
-
-               if (temp >= POLARIS10_MINIMUM_ENGINE_CLOCK || i == 0)
-                       break;
-       }
-
-       level->DeepSleepDivId = i;
-
-       /* Default to slow, highest DPM level will be
-        * set to PPSMC_DISPLAY_WATERMARK_LOW later.
-        */
-       if (data->update_up_hyst)
-               level->UpHyst = (uint8_t)data->up_hyst;
-       if (data->update_down_hyst)
-               level->DownHyst = (uint8_t)data->down_hyst;
-
-       level->SclkSetting = curr_sclk_setting;
-
-       CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
-       CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
-       CONVERT_FROM_HOST_TO_SMC_UL(level->SclkSetting.SclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_int);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_frac);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_fcw_int);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_up_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_down_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_int);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_frac);
-       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_ss_slew_rate);
-       return 0;
-}
-
-/**
-* Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
-*
-* @param    hwmgr      the address of the hardware manager
-*/
-static int polaris10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_dpm_table *dpm_table = &data->dpm_table;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
-       uint8_t pcie_entry_cnt = (uint8_t) data->dpm_table.pcie_speed_table.count;
-       int result = 0;
-       uint32_t array = data->dpm_table_start +
-                       offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
-       uint32_t array_size = sizeof(struct SMU74_Discrete_GraphicsLevel) *
-                       SMU74_MAX_LEVELS_GRAPHICS;
-       struct SMU74_Discrete_GraphicsLevel *levels =
-                       data->smc_state_table.GraphicsLevel;
-       uint32_t i, max_entry;
-       uint8_t hightest_pcie_level_enabled = 0,
-               lowest_pcie_level_enabled = 0,
-               mid_pcie_level_enabled = 0,
-               count = 0;
-
-       polaris10_get_sclk_range_table(hwmgr);
-
-       for (i = 0; i < dpm_table->sclk_table.count; i++) {
-
-               result = polaris10_populate_single_graphic_level(hwmgr,
-                               dpm_table->sclk_table.dpm_levels[i].value,
-                               (uint16_t)data->activity_target[i],
-                               &(data->smc_state_table.GraphicsLevel[i]));
-               if (result)
-                       return result;
-
-               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
-               if (i > 1)
-                       levels[i].DeepSleepDivId = 0;
-       }
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_SPLLShutdownSupport))
-               data->smc_state_table.GraphicsLevel[0].SclkSetting.SSc_En = 0;
-
-       data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
-       data->smc_state_table.GraphicsDpmLevelCount =
-                       (uint8_t)dpm_table->sclk_table.count;
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
-
-
-       if (pcie_table != NULL) {
-               PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
-                               "There must be 1 or more PCIE levels defined in PPTable.",
-                               return -EINVAL);
-               max_entry = pcie_entry_cnt - 1;
-               for (i = 0; i < dpm_table->sclk_table.count; i++)
-                       levels[i].pcieDpmLevel =
-                                       (uint8_t) ((i < max_entry) ? i : max_entry);
-       } else {
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << (hightest_pcie_level_enabled + 1))) != 0))
-                       hightest_pcie_level_enabled++;
-
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << lowest_pcie_level_enabled)) == 0))
-                       lowest_pcie_level_enabled++;
-
-               while ((count < hightest_pcie_level_enabled) &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                               (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
-                       count++;
-
-               mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
-                               hightest_pcie_level_enabled ?
-                                               (lowest_pcie_level_enabled + 1 + count) :
-                                               hightest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to hightest_pcie_level_enabled */
-               for (i = 2; i < dpm_table->sclk_table.count; i++)
-                       levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to lowest_pcie_level_enabled */
-               levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to mid_pcie_level_enabled */
-               levels[1].pcieDpmLevel = mid_pcie_level_enabled;
-       }
-       /* level count will send to smc once at init smc table and never change */
-       result = polaris10_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
-                       (uint32_t)array_size, data->sram_end);
-
-       return result;
-}
-
-static int polaris10_populate_single_memory_level(struct pp_hwmgr *hwmgr,
-               uint32_t clock, struct SMU74_Discrete_MemoryLevel *mem_level)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int result = 0;
-       struct cgs_display_info info = {0, 0, NULL};
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (table_info->vdd_dep_on_mclk) {
-               result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                               table_info->vdd_dep_on_mclk, clock,
-                               &mem_level->MinVoltage, &mem_level->MinMvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find MinVddc voltage value from memory "
-                               "VDDC voltage dependency table", return result);
-       }
-
-       mem_level->MclkFrequency = clock;
-       mem_level->EnabledForThrottle = 1;
-       mem_level->EnabledForActivity = 0;
-       mem_level->UpHyst = 0;
-       mem_level->DownHyst = 100;
-       mem_level->VoltageDownHyst = 0;
-       mem_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
-       mem_level->StutterEnable = false;
-       mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-
-       data->display_timing.num_existing_displays = info.display_count;
-
-       if ((data->mclk_stutter_mode_threshold) &&
-               (clock <= data->mclk_stutter_mode_threshold) &&
-               (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
-                               STUTTER_ENABLE) & 0x1))
-               mem_level->StutterEnable = true;
-
-       if (!result) {
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
-               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
-       }
-       return result;
-}
-
-/**
-* Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
-*
-* @param    hwmgr      the address of the hardware manager
-*/
-static int polaris10_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_dpm_table *dpm_table = &data->dpm_table;
-       int result;
-       /* populate MCLK dpm table to SMU7 */
-       uint32_t array = data->dpm_table_start +
-                       offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
-       uint32_t array_size = sizeof(SMU74_Discrete_MemoryLevel) *
-                       SMU74_MAX_LEVELS_MEMORY;
-       struct SMU74_Discrete_MemoryLevel *levels =
-                       data->smc_state_table.MemoryLevel;
-       uint32_t i;
-
-       for (i = 0; i < dpm_table->mclk_table.count; i++) {
-               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
-                               "can not populate memory level as memory clock is zero",
-                               return -EINVAL);
-               result = polaris10_populate_single_memory_level(hwmgr,
-                               dpm_table->mclk_table.dpm_levels[i].value,
-                               &levels[i]);
-               if (i == dpm_table->mclk_table.count - 1) {
-                       levels[i].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
-                       levels[i].EnabledForActivity = 1;
-               }
-               if (result)
-                       return result;
-       }
-
-       /* In order to prevent MC activity from stutter mode to push DPM up,
-        * the UVD change complements this by putting the MCLK in
-        * a higher state by default such that we are not affected by
-        * up threshold or and MCLK DPM latency.
-        */
-       levels[0].ActivityLevel = 0x1f;
-       CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
-
-       data->smc_state_table.MemoryDpmLevelCount =
-                       (uint8_t)dpm_table->mclk_table.count;
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
-
-       /* level count will send to smc once at init smc table and never change */
-       result = polaris10_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
-                       (uint32_t)array_size, data->sram_end);
-
-       return result;
-}
-
-/**
-* Populates the SMC MVDD structure using the provided memory clock.
-*
-* @param    hwmgr      the address of the hardware manager
-* @param    mclk        the MCLK value to be used in the decision if MVDD should be high or low.
-* @param    voltage     the SMC VOLTAGE structure to be populated
-*/
-int polaris10_populate_mvdd_value(struct pp_hwmgr *hwmgr,
-               uint32_t mclk, SMIO_Pattern *smio_pat)
-{
-       const struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i = 0;
-
-       if (POLARIS10_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
-               /* find mvdd value which clock is more than request */
-               for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
-                       if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
-                               smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
-                               break;
-                       }
-               }
-               PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
-                               "MVDD Voltage is outside the supported range.",
-                               return -EINVAL);
-       } else
-               return -EINVAL;
-
-       return 0;
-}
-
-static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
-               SMU74_Discrete_DpmTable *table)
-{
-       int result = 0;
-       uint32_t sclk_frequency;
-       const struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       SMIO_Pattern vol_level;
-       uint32_t mvdd;
-       uint16_t us_mvdd;
-
-       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
-
-
-       /* Get MinVoltage and Frequency from DPM0,
-        * already converted to SMC_UL */
-       sclk_frequency = data->vbios_boot_state.sclk_bootup_value;
-       result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                       table_info->vdd_dep_on_sclk,
-                       sclk_frequency,
-                       &table->ACPILevel.MinVoltage, &mvdd);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Cannot find ACPI VDDC voltage value "
-                       "in Clock Dependency Table",
-                       );
-
-
-       result = polaris10_calculate_sclk_params(hwmgr, sclk_frequency,  &(table->ACPILevel.SclkSetting));
-       PP_ASSERT_WITH_CODE(result == 0, "Error retrieving Engine Clock dividers from VBIOS.", return result);
-
-       table->ACPILevel.DeepSleepDivId = 0;
-       table->ACPILevel.CcPwrDynRm = 0;
-       table->ACPILevel.CcPwrDynRm1 = 0;
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkSetting.SclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_int);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_frac);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_fcw_int);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_up_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_down_slew_rate);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_int);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac);
-       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate);
-
-
-       /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
-       table->MemoryACPILevel.MclkFrequency = data->vbios_boot_state.mclk_bootup_value;
-       result = polaris10_get_dependency_volt_by_clk(hwmgr,
-                       table_info->vdd_dep_on_mclk,
-                       table->MemoryACPILevel.MclkFrequency,
-                       &table->MemoryACPILevel.MinVoltage, &mvdd);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Cannot find ACPI VDDCI voltage value "
-                       "in Clock Dependency Table",
-                       );
-
-       us_mvdd = 0;
-       if ((POLARIS10_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
-                       (data->mclk_dpm_key_disabled))
-               us_mvdd = data->vbios_boot_state.mvdd_bootup_value;
-       else {
-               if (!polaris10_populate_mvdd_value(hwmgr,
-                               data->dpm_table.mclk_table.dpm_levels[0].value,
-                               &vol_level))
-                       us_mvdd = vol_level.Voltage;
-       }
-
-       if (0 == polaris10_populate_mvdd_value(hwmgr, 0, &vol_level))
-               table->MemoryACPILevel.MinMvdd = PP_HOST_TO_SMC_UL(vol_level.Voltage);
-       else
-               table->MemoryACPILevel.MinMvdd = 0;
-
-       table->MemoryACPILevel.StutterEnable = false;
-
-       table->MemoryACPILevel.EnabledForThrottle = 0;
-       table->MemoryACPILevel.EnabledForActivity = 0;
-       table->MemoryACPILevel.UpHyst = 0;
-       table->MemoryACPILevel.DownHyst = 100;
-       table->MemoryACPILevel.VoltageDownHyst = 0;
-       table->MemoryACPILevel.ActivityLevel =
-                       PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
-
-       return result;
-}
-
-static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
-               SMU74_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t vddci;
-
-       table->VceLevelCount = (uint8_t)(mm_table->count);
-       table->VceBootLevel = 0;
-
-       for (count = 0; count < table->VceLevelCount; count++) {
-               table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
-               table->VceLevel[count].MinVoltage = 0;
-               table->VceLevel[count].MinVoltage |=
-                               (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
-
-               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
-                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
-                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
-               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
-                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
-               else
-                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
-
-
-               table->VceLevel[count].MinVoltage |=
-                               (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /*retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->VceLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for VCE engine clock",
-                               return result);
-
-               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
-       }
-       return result;
-}
-
-static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
-               SMU74_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t vddci;
-
-       table->SamuBootLevel = 0;
-       table->SamuLevelCount = (uint8_t)(mm_table->count);
-
-       for (count = 0; count < table->SamuLevelCount; count++) {
-               /* not sure whether we need evclk or not */
-               table->SamuLevel[count].MinVoltage = 0;
-               table->SamuLevel[count].Frequency = mm_table->entries[count].samclock;
-               table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
-                               VOLTAGE_SCALE) << VDDC_SHIFT;
-
-               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
-                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
-                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
-               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
-                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
-               else
-                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
-
-               table->SamuLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->SamuLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for samu clock", return result);
-
-               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].MinVoltage);
-       }
-       return result;
-}
-
-static int polaris10_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
-               int32_t eng_clock, int32_t mem_clock,
-               SMU74_Discrete_MCArbDramTimingTableEntry *arb_regs)
-{
-       uint32_t dram_timing;
-       uint32_t dram_timing2;
-       uint32_t burst_time;
-       int result;
-
-       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
-                       eng_clock, mem_clock);
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "Error calling VBIOS to set DRAM_TIMING.", return result);
-
-       dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-       dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-       burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
-
-
-       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dram_timing);
-       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
-       arb_regs->McArbBurstTime   = (uint8_t)burst_time;
-
-       return 0;
-}
-
-static int polaris10_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct SMU74_Discrete_MCArbDramTimingTable arb_regs;
-       uint32_t i, j;
-       int result = 0;
-
-       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
-               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
-                       result = polaris10_populate_memory_timing_parameters(hwmgr,
-                                       data->dpm_table.sclk_table.dpm_levels[i].value,
-                                       data->dpm_table.mclk_table.dpm_levels[j].value,
-                                       &arb_regs.entries[i][j]);
-                       if (result == 0)
-                               result = atomctrl_set_ac_timing_ai(hwmgr, data->dpm_table.mclk_table.dpm_levels[j].value, j);
-                       if (result != 0)
-                               return result;
-               }
-       }
-
-       result = polaris10_copy_bytes_to_smc(
-                       hwmgr->smumgr,
-                       data->arb_table_start,
-                       (uint8_t *)&arb_regs,
-                       sizeof(SMU74_Discrete_MCArbDramTimingTable),
-                       data->sram_end);
-       return result;
-}
-
-static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       int result = -EINVAL;
-       uint8_t count;
-       struct pp_atomctrl_clock_dividers_vi dividers;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t vddci;
-
-       table->UvdLevelCount = (uint8_t)(mm_table->count);
-       table->UvdBootLevel = 0;
-
-       for (count = 0; count < table->UvdLevelCount; count++) {
-               table->UvdLevel[count].MinVoltage = 0;
-               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
-               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
-               table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
-                               VOLTAGE_SCALE) << VDDC_SHIFT;
-
-               if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
-                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
-                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
-               else if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
-                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
-               else
-                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
-
-               table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
-               table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->UvdLevel[count].VclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for Vclk clock", return result);
-
-               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                               table->UvdLevel[count].DclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for Dclk clock", return result);
-
-               table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
-       }
-
-       return result;
-}
-
-static int polaris10_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       int result = 0;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       table->GraphicsBootLevel = 0;
-       table->MemoryBootLevel = 0;
-
-       /* find boot level from dpm table */
-       result = phm_find_boot_level(&(data->dpm_table.sclk_table),
-                       data->vbios_boot_state.sclk_bootup_value,
-                       (uint32_t *)&(table->GraphicsBootLevel));
-
-       result = phm_find_boot_level(&(data->dpm_table.mclk_table),
-                       data->vbios_boot_state.mclk_bootup_value,
-                       (uint32_t *)&(table->MemoryBootLevel));
-
-       table->BootVddc  = data->vbios_boot_state.vddc_bootup_value *
-                       VOLTAGE_SCALE;
-       table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
-                       VOLTAGE_SCALE;
-       table->BootMVdd  = data->vbios_boot_state.mvdd_bootup_value *
-                       VOLTAGE_SCALE;
-
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
-
-       return 0;
-}
-
-
-static int polaris10_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint8_t count, level;
-
-       count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
-
-       for (level = 0; level < count; level++) {
-               if (table_info->vdd_dep_on_sclk->entries[level].clk >=
-                               data->vbios_boot_state.sclk_bootup_value) {
-                       data->smc_state_table.GraphicsBootLevel = level;
-                       break;
-               }
-       }
-
-       count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
-       for (level = 0; level < count; level++) {
-               if (table_info->vdd_dep_on_mclk->entries[level].clk >=
-                               data->vbios_boot_state.mclk_bootup_value) {
-                       data->smc_state_table.MemoryBootLevel = level;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
-{
-       uint32_t ro, efuse, volt_without_cks, volt_with_cks, value, max, min;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint8_t i, stretch_amount, volt_offset = 0;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-
-       stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
-
-       /* Read SMU_Eefuse to read and calculate RO and determine
-        * if the part is SS or FF. if RO >= 1660MHz, part is FF.
-        */
-       efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixSMU_EFUSE_0 + (67 * 4));
-       efuse &= 0xFF000000;
-       efuse = efuse >> 24;
-
-       if (hwmgr->chip_id == CHIP_POLARIS10) {
-               min = 1000;
-               max = 2300;
-       } else {
-               min = 1100;
-               max = 2100;
-       }
-
-       ro = efuse * (max -min)/255 + min;
-
-       /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
-       for (i = 0; i < sclk_table->count; i++) {
-               data->smc_state_table.Sclk_CKS_masterEn0_7 |=
-                               sclk_table->entries[i].cks_enable << i;
-               if (hwmgr->chip_id == CHIP_POLARIS10) {
-                       volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) * 136418 -(ro - 70) * 1000000) / \
-                                               (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000));
-                       volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 * 3232 - (ro - 65) * 1000000) / \
-                                       (2522480 - sclk_table->entries[i].clk/100 * 115764/100));
-               } else {
-                       volt_without_cks = (uint32_t)((2416794800U + (sclk_table->entries[i].clk/100) * 1476925/10 -(ro - 50) * 1000000) / \
-                                               (2625416 - (sclk_table->entries[i].clk/100) * (12586807/10000)));
-                       volt_with_cks = (uint32_t)((2999656000U - sclk_table->entries[i].clk/100 * 392803 - (ro - 44) * 1000000) / \
-                                       (3422454 - sclk_table->entries[i].clk/100 * (18886376/10000)));
-               }
-
-               if (volt_without_cks >= volt_with_cks)
-                       volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
-                                       sclk_table->entries[i].cks_voffset) * 100 + 624) / 625);
-
-               data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
-       }
-
-       data->smc_state_table.LdoRefSel = (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ? table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 6;
-       /* Populate CKS Lookup Table */
-       if (stretch_amount != 1 && stretch_amount != 2 && stretch_amount != 3 &&
-                       stretch_amount != 4 && stretch_amount != 5) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ClockStretcher);
-               PP_ASSERT_WITH_CODE(false,
-                               "Stretch Amount in PPTable not supported\n",
-                               return -EINVAL);
-       }
-
-       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
-       value &= 0xFFFFFFFE;
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
-
-       return 0;
-}
-
-/**
-* Populates the SMC VRConfig field in DPM table.
-*
-* @param    hwmgr   the address of the hardware manager
-* @param    table   the SMC DPM table structure to be populated
-* @return   always 0
-*/
-static int polaris10_populate_vr_config(struct pp_hwmgr *hwmgr,
-               struct SMU74_Discrete_DpmTable *table)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint16_t config;
-
-       config = VR_MERGED_WITH_VDDC;
-       table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
-
-       /* Set Vddc Voltage Controller */
-       if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               config = VR_SVI2_PLANE_1;
-               table->VRConfig |= config;
-       } else {
-               PP_ASSERT_WITH_CODE(false,
-                               "VDDC should be on SVI2 control in merged mode!",
-                               );
-       }
-       /* Set Vddci Voltage Controller */
-       if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
-               config = VR_SVI2_PLANE_2;  /* only in merged mode */
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       } else if (POLARIS10_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
-               config = VR_SMIO_PATTERN_1;
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       } else {
-               config = VR_STATIC_VOLTAGE;
-               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
-       }
-       /* Set Mvdd Voltage Controller */
-       if (POLARIS10_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
-               config = VR_SVI2_PLANE_2;
-               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start +
-                       offsetof(SMU74_SoftRegisters, AllowMvddSwitch), 0x1);
-       } else {
-               config = VR_STATIC_VOLTAGE;
-               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
-       }
-
-       return 0;
-}
-
-
-int polaris10_populate_avfs_parameters(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       SMU74_Discrete_DpmTable  *table = &(data->smc_state_table);
-       int result = 0;
-       struct pp_atom_ctrl__avfs_parameters avfs_params = {0};
-       AVFS_meanNsigma_t AVFS_meanNsigma = { {0} };
-       AVFS_Sclk_Offset_t AVFS_SclkOffset = { {0} };
-       uint32_t tmp, i;
-       struct pp_smumgr *smumgr = hwmgr->smumgr;
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)hwmgr->pptable;
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-
-
-       if (smu_data->avfs.avfs_btc_status == AVFS_BTC_NOTSUPPORTED)
-               return result;
-
-       result = atomctrl_get_avfs_information(hwmgr, &avfs_params);
-
-       if (0 == result) {
-               table->BTCGB_VDROOP_TABLE[0].a0  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a0);
-               table->BTCGB_VDROOP_TABLE[0].a1  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a1);
-               table->BTCGB_VDROOP_TABLE[0].a2  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a2);
-               table->BTCGB_VDROOP_TABLE[1].a0  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0);
-               table->BTCGB_VDROOP_TABLE[1].a1  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1);
-               table->BTCGB_VDROOP_TABLE[1].a2  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2);
-               table->AVFSGB_VDROOP_TABLE[0].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_m1);
-               table->AVFSGB_VDROOP_TABLE[0].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSON_m2);
-               table->AVFSGB_VDROOP_TABLE[0].b  = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_b);
-               table->AVFSGB_VDROOP_TABLE[0].m1_shift = 24;
-               table->AVFSGB_VDROOP_TABLE[0].m2_shift  = 12;
-               table->AVFSGB_VDROOP_TABLE[1].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1);
-               table->AVFSGB_VDROOP_TABLE[1].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2);
-               table->AVFSGB_VDROOP_TABLE[1].b  = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b);
-               table->AVFSGB_VDROOP_TABLE[1].m1_shift = 24;
-               table->AVFSGB_VDROOP_TABLE[1].m2_shift  = 12;
-               table->MaxVoltage                = PP_HOST_TO_SMC_US(avfs_params.usMaxVoltage_0_25mv);
-               AVFS_meanNsigma.Aconstant[0]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant0);
-               AVFS_meanNsigma.Aconstant[1]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant1);
-               AVFS_meanNsigma.Aconstant[2]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant2);
-               AVFS_meanNsigma.DC_tol_sigma      = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_DC_tol_sigma);
-               AVFS_meanNsigma.Platform_mean     = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_mean);
-               AVFS_meanNsigma.PSM_Age_CompFactor = PP_HOST_TO_SMC_US(avfs_params.usPSM_Age_ComFactor);
-               AVFS_meanNsigma.Platform_sigma     = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_sigma);
-
-               for (i = 0; i < NUM_VFT_COLUMNS; i++) {
-                       AVFS_meanNsigma.Static_Voltage_Offset[i] = (uint8_t)(sclk_table->entries[i].cks_voffset * 100 / 625);
-                       AVFS_SclkOffset.Sclk_Offset[i] = PP_HOST_TO_SMC_US((uint16_t)(sclk_table->entries[i].sclk_offset) / 100);
-               }
-
-               result = polaris10_read_smc_sram_dword(smumgr,
-                               SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsMeanNSigma),
-                               &tmp, data->sram_end);
-
-               polaris10_copy_bytes_to_smc(smumgr,
-                                       tmp,
-                                       (uint8_t *)&AVFS_meanNsigma,
-                                       sizeof(AVFS_meanNsigma_t),
-                                       data->sram_end);
-
-               result = polaris10_read_smc_sram_dword(smumgr,
-                               SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsSclkOffsetTable),
-                               &tmp, data->sram_end);
-               polaris10_copy_bytes_to_smc(smumgr,
-                                       tmp,
-                                       (uint8_t *)&AVFS_SclkOffset,
-                                       sizeof(AVFS_Sclk_Offset_t),
-                                       data->sram_end);
-
-               data->avfs_vdroop_override_setting = (avfs_params.ucEnableGB_VDROOP_TABLE_CKSON << BTCGB0_Vdroop_Enable_SHIFT) |
-                                               (avfs_params.ucEnableGB_VDROOP_TABLE_CKSOFF << BTCGB1_Vdroop_Enable_SHIFT) |
-                                               (avfs_params.ucEnableGB_FUSE_TABLE_CKSON << AVFSGB0_Vdroop_Enable_SHIFT) |
-                                               (avfs_params.ucEnableGB_FUSE_TABLE_CKSOFF << AVFSGB1_Vdroop_Enable_SHIFT);
-               data->apply_avfs_cks_off_voltage = (avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage == 1) ? true : false;
-       }
-       return result;
-}
-
-
-/**
-* Initializes the SMC table and uploads it
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_init_smc_table(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct SMU74_Discrete_DpmTable *table = &(data->smc_state_table);
-       const struct polaris10_ulv_parm *ulv = &(data->ulv);
-       uint8_t i;
-       struct pp_atomctrl_gpio_pin_assignment gpio_pin;
-       pp_atomctrl_clock_dividers_vi dividers;
-
-       result = polaris10_setup_default_dpm_tables(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to setup default DPM tables!", return result);
-
-       if (POLARIS10_VOLTAGE_CONTROL_NONE != data->voltage_control)
-               polaris10_populate_smc_voltage_tables(hwmgr, table);
-
-       table->SystemFlags = 0;
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition))
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StepVddc))
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
-
-       if (data->is_memory_gddr5)
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
-
-       if (ulv->ulv_supported && table_info->us_ulv_voltage_offset) {
-               result = polaris10_populate_ulv_state(hwmgr, table);
-               PP_ASSERT_WITH_CODE(0 == result,
-                               "Failed to initialize ULV state!", return result);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               ixCG_ULV_PARAMETER, PPPOLARIS10_CGULVPARAMETER_DFLT);
-       }
-
-       result = polaris10_populate_smc_link_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Link Level!", return result);
-
-       result = polaris10_populate_all_graphic_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Graphics Level!", return result);
-
-       result = polaris10_populate_all_memory_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Memory Level!", return result);
-
-       result = polaris10_populate_smc_acpi_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize ACPI Level!", return result);
-
-       result = polaris10_populate_smc_vce_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize VCE Level!", return result);
-
-       result = polaris10_populate_smc_samu_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize SAMU Level!", return result);
-
-       /* Since only the initial state is completely set up at this point
-        * (the other states are just copies of the boot state) we only
-        * need to populate the  ARB settings for the initial state.
-        */
-       result = polaris10_program_memory_timing_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to Write ARB settings for the initial state.", return result);
-
-       result = polaris10_populate_smc_uvd_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize UVD Level!", return result);
-
-       result = polaris10_populate_smc_boot_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Boot Level!", return result);
-
-       result = polaris10_populate_smc_initailial_state(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize Boot State!", return result);
-
-       result = polaris10_populate_bapm_parameters_in_dpm_table(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to populate BAPM Parameters!", return result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ClockStretcher)) {
-               result = polaris10_populate_clock_stretcher_data_table(hwmgr);
-               PP_ASSERT_WITH_CODE(0 == result,
-                               "Failed to populate Clock Stretcher Data Table!",
-                               return result);
-       }
-
-       result = polaris10_populate_avfs_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result, "Failed to populate AVFS Parameters!", return result;);
-
-       table->CurrSclkPllRange = 0xff;
-       table->GraphicsVoltageChangeEnable  = 1;
-       table->GraphicsThermThrottleEnable  = 1;
-       table->GraphicsInterval = 1;
-       table->VoltageInterval  = 1;
-       table->ThermalInterval  = 1;
-       table->TemperatureLimitHigh =
-                       table_info->cac_dtp_table->usTargetOperatingTemp *
-                       POLARIS10_Q88_FORMAT_CONVERSION_UNIT;
-       table->TemperatureLimitLow  =
-                       (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
-                       POLARIS10_Q88_FORMAT_CONVERSION_UNIT;
-       table->MemoryVoltageChangeEnable = 1;
-       table->MemoryInterval = 1;
-       table->VoltageResponseTime = 0;
-       table->PhaseResponseTime = 0;
-       table->MemoryThermThrottleEnable = 1;
-       table->PCIeBootLinkLevel = 0;
-       table->PCIeGenInterval = 1;
-       table->VRConfig = 0;
-
-       result = polaris10_populate_vr_config(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to populate VRConfig setting!", return result);
-
-       table->ThermGpio = 17;
-       table->SclkStepSize = 0x4000;
-
-       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
-               table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
-       } else {
-               table->VRHotGpio = POLARIS10_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_RegulatorHot);
-       }
-
-       if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
-                       &gpio_pin)) {
-               table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_AutomaticDCTransition);
-       } else {
-               table->AcDcGpio = POLARIS10_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_AutomaticDCTransition);
-       }
-
-       /* Thermal Output GPIO */
-       if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
-                       &gpio_pin)) {
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ThermalOutGPIO);
-
-               table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
-
-               /* For porlarity read GPIOPAD_A with assigned Gpio pin
-                * since VBIOS will program this register to set 'inactive state',
-                * driver can then determine 'active state' from this and
-                * program SMU with correct polarity
-                */
-               table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A)
-                                       & (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
-
-               /* if required, combine VRHot/PCC with thermal out GPIO */
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_RegulatorHot)
-               && phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_CombinePCCWithThermalSignal))
-                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
-       } else {
-               table->ThermOutGpio = 17;
-               table->ThermOutPolarity = 1;
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
-       }
-
-       /* Populate BIF_SCLK levels into SMC DPM table */
-       for (i = 0; i <= data->dpm_table.pcie_speed_table.count; i++) {
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr, data->bif_sclk_table[i], &dividers);
-               PP_ASSERT_WITH_CODE((result == 0), "Can not find DFS divide id for Sclk", return result);
-
-               if (i == 0)
-                       table->Ulv.BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
-               else
-                       table->LinkLevel[i-1].BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
-       }
-
-       for (i = 0; i < SMU74_MAX_ENTRIES_SMIO; i++)
-               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
-
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->CurrSclkPllRange);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
-       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
-       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
-
-       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
-       result = polaris10_copy_bytes_to_smc(hwmgr->smumgr,
-                       data->dpm_table_start +
-                       offsetof(SMU74_Discrete_DpmTable, SystemFlags),
-                       (uint8_t *)&(table->SystemFlags),
-                       sizeof(SMU74_Discrete_DpmTable) - 3 * sizeof(SMU74_PIDController),
-                       data->sram_end);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to upload dpm data to SMC memory!", return result);
-
-       return 0;
-}
-
-/**
-* Initialize the ARB DRAM timing table's index field.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_init_arb_table_index(struct pp_hwmgr *hwmgr)
-{
-       const struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t tmp;
-       int result;
-
-       /* This is a read-modify-write on the first byte of the ARB table.
-        * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
-        * is the field 'current'.
-        * This solution is ugly, but we never write the whole table only
-        * individual fields in it.
-        * In reality this field should not be in that structure
-        * but in a soft register.
-        */
-       result = polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       data->arb_table_start, &tmp, data->sram_end);
-
-       if (result)
-               return result;
-
-       tmp &= 0x00FFFFFF;
-       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
-
-       return polaris10_write_smc_sram_dword(hwmgr->smumgr,
-                       data->arb_table_start, tmp, data->sram_end);
-}
-
-static int polaris10_enable_vrhot_gpio_interrupt(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_RegulatorHot))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_EnableVRHotGPIOInterrupt);
-
-       return 0;
-}
-
-static int polaris10_enable_sclk_control(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       SCLK_PWRMGT_OFF, 0);
-       return 0;
-}
-
-static int polaris10_enable_ulv(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_ulv_parm *ulv = &(data->ulv);
-
-       if (ulv->ulv_supported)
-               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_EnableULV);
-
-       return 0;
-}
-
-static int polaris10_disable_ulv(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_ulv_parm *ulv = &(data->ulv);
-
-       if (ulv->ulv_supported)
-               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DisableULV);
-
-       return 0;
-}
-
-static int polaris10_enable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep)) {
-               if (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_MASTER_DeepSleep_ON))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to enable Master Deep Sleep switch failed!",
-                                       return -1);
-       } else {
-               if (smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to disable Master Deep Sleep switch failed!",
-                                       return -1);
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_disable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep)) {
-               if (smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to disable Master Deep Sleep switch failed!",
-                                       return -1);
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_enable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t soft_register_value = 0;
-       uint32_t handshake_disables_offset = data->soft_regs_start
-                               + offsetof(SMU74_SoftRegisters, HandshakeDisables);
-
-       /* enable SCLK dpm */
-       if (!data->sclk_dpm_key_disabled)
-               PP_ASSERT_WITH_CODE(
-               (0 == smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DPM_Enable)),
-               "Failed to enable SCLK DPM during DPM Start Function!",
-               return -1);
-
-       /* enable MCLK dpm */
-       if (0 == data->mclk_dpm_key_disabled) {
-/* Disable UVD - SMU handshake for MCLK. */
-               soft_register_value = cgs_read_ind_register(hwmgr->device,
-                                       CGS_IND_REG__SMC, handshake_disables_offset);
-               soft_register_value |= SMU7_UVD_MCLK_HANDSHAKE_DISABLE;
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                               handshake_disables_offset, soft_register_value);
-
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_Enable)),
-                               "Failed to enable MCLK DPM during DPM Start Function!",
-                               return -1);
-
-               PHM_WRITE_FIELD(hwmgr->device, MC_SEQ_CNTL_3, CAC_EN, 0x1);
-
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x5);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x5);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x100005);
-               udelay(10);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x400005);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x400005);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x500005);
-       }
-
-       return 0;
-}
-
-static int polaris10_start_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /*enable general power management */
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                       GLOBAL_PWRMGT_EN, 1);
-
-       /* enable sclk deep sleep */
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       DYNAMIC_PM_EN, 1);
-
-       /* prepare for PCIE DPM */
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       data->soft_regs_start + offsetof(SMU74_SoftRegisters,
-                                       VoltageChangeTimeout), 0x1000);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
-                       SWRST_COMMAND_1, RESETLC, 0x0);
-/*
-       PP_ASSERT_WITH_CODE(
-                       (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                       PPSMC_MSG_Voltage_Cntl_Enable)),
-                       "Failed to enable voltage DPM during DPM Start Function!",
-                       return -1);
-*/
-
-       if (polaris10_enable_sclk_mclk_dpm(hwmgr)) {
-               printk(KERN_ERR "Failed to enable Sclk DPM and Mclk DPM!");
-               return -1;
-       }
-
-       /* enable PCIE dpm */
-       if (0 == data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Enable)),
-                               "Failed to enable pcie DPM during DPM Start Function!",
-                               return -1);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_Falcon_QuickTransition)) {
-               PP_ASSERT_WITH_CODE((0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_EnableACDCGPIOInterrupt)),
-                               "Failed to enable AC DC GPIO Interrupt!",
-                               );
-       }
-
-       return 0;
-}
-
-static int polaris10_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* disable SCLK dpm */
-       if (!data->sclk_dpm_key_disabled)
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_DPM_Disable) == 0),
-                               "Failed to disable SCLK DPM!",
-                               return -1);
-
-       /* disable MCLK dpm */
-       if (!data->mclk_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_Disable) == 0),
-                               "Failed to disable MCLK DPM!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-static int polaris10_stop_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* disable general power management */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                       GLOBAL_PWRMGT_EN, 0);
-       /* disable sclk deep sleep */
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
-                       DYNAMIC_PM_EN, 0);
-
-       /* disable PCIE dpm */
-       if (!data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Disable) == 0),
-                               "Failed to disable pcie DPM during DPM Stop Function!",
-                               return -1);
-       }
-
-       if (polaris10_disable_sclk_mclk_dpm(hwmgr)) {
-               printk(KERN_ERR "Failed to disable Sclk DPM and Mclk DPM!");
-               return -1;
-       }
-
-       return 0;
-}
-
-static void polaris10_set_dpm_event_sources(struct pp_hwmgr *hwmgr, uint32_t sources)
-{
-       bool protection;
-       enum DPM_EVENT_SRC src;
-
-       switch (sources) {
-       default:
-               printk(KERN_ERR "Unknown throttling event sources.");
-               /* fall through */
-       case 0:
-               protection = false;
-               /* src is unused */
-               break;
-       case (1 << PHM_AutoThrottleSource_Thermal):
-               protection = true;
-               src = DPM_EVENT_SRC_DIGITAL;
-               break;
-       case (1 << PHM_AutoThrottleSource_External):
-               protection = true;
-               src = DPM_EVENT_SRC_EXTERNAL;
-               break;
-       case (1 << PHM_AutoThrottleSource_External) |
-                       (1 << PHM_AutoThrottleSource_Thermal):
-               protection = true;
-               src = DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL;
-               break;
-       }
-       /* Order matters - don't enable thermal protection for the wrong source. */
-       if (protection) {
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_CTRL,
-                               DPM_EVENT_SRC, src);
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                               THERMAL_PROTECTION_DIS,
-                               !phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_ThermalController));
-       } else
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
-                               THERMAL_PROTECTION_DIS, 1);
-}
-
-static int polaris10_enable_auto_throttle_source(struct pp_hwmgr *hwmgr,
-               PHM_AutoThrottleSource source)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (!(data->active_auto_throttle_sources & (1 << source))) {
-               data->active_auto_throttle_sources |= 1 << source;
-               polaris10_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
-       }
-       return 0;
-}
-
-static int polaris10_enable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
-{
-       return polaris10_enable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
-}
-
-static int polaris10_disable_auto_throttle_source(struct pp_hwmgr *hwmgr,
-               PHM_AutoThrottleSource source)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->active_auto_throttle_sources & (1 << source)) {
-               data->active_auto_throttle_sources &= ~(1 << source);
-               polaris10_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
-       }
-       return 0;
-}
-
-static int polaris10_disable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
-{
-       return polaris10_disable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
-}
-
-int polaris10_pcie_performance_request(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       data->pcie_performance_request = true;
-
-       return 0;
-}
-
-int polaris10_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-       tmp_result = (!polaris10_is_dpm_running(hwmgr)) ? 0 : -1;
-       PP_ASSERT_WITH_CODE(result == 0,
-                       "DPM is already running right now, no need to enable DPM!",
-                       return 0);
-
-       if (polaris10_voltage_control(hwmgr)) {
-               tmp_result = polaris10_enable_voltage_control(hwmgr);
-               PP_ASSERT_WITH_CODE(tmp_result == 0,
-                               "Failed to enable voltage control!",
-                               result = tmp_result);
-
-               tmp_result = polaris10_construct_voltage_tables(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to contruct voltage tables!",
-                               result = tmp_result);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EngineSpreadSpectrumSupport))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 1);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalController))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 0);
-
-       tmp_result = polaris10_program_static_screen_threshold_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program static screen threshold parameters!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_enable_display_gap(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable display gap!", result = tmp_result);
-
-       tmp_result = polaris10_program_voting_clients(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program voting clients!", result = tmp_result);
-
-       tmp_result = polaris10_process_firmware_header(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to process firmware header!", result = tmp_result);
-
-       tmp_result = polaris10_initial_switch_from_arbf0_to_f1(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize switch from ArbF0 to F1!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_init_smc_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize SMC table!", result = tmp_result);
-
-       tmp_result = polaris10_init_arb_table_index(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to initialize ARB table index!", result = tmp_result);
-
-       tmp_result = polaris10_populate_pm_fuses(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to populate PM fuses!", result = tmp_result);
-
-       tmp_result = polaris10_enable_vrhot_gpio_interrupt(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay);
-
-       tmp_result = polaris10_enable_sclk_control(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable SCLK control!", result = tmp_result);
-
-       tmp_result = polaris10_enable_smc_voltage_controller(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable voltage control!", result = tmp_result);
-
-       tmp_result = polaris10_enable_ulv(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable ULV!", result = tmp_result);
-
-       tmp_result = polaris10_enable_deep_sleep_master_switch(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable deep sleep master switch!", result = tmp_result);
-
-       tmp_result = polaris10_enable_didt_config(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to enable deep sleep master switch!", result = tmp_result);
-
-       tmp_result = polaris10_start_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to start DPM!", result = tmp_result);
-
-       tmp_result = polaris10_enable_smc_cac(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable SMC CAC!", result = tmp_result);
-
-       tmp_result = polaris10_enable_power_containment(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable power containment!", result = tmp_result);
-
-       tmp_result = polaris10_power_control_set_level(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to power control set level!", result = tmp_result);
-
-       tmp_result = polaris10_enable_thermal_auto_throttle(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable thermal auto throttle!", result = tmp_result);
-
-       tmp_result = polaris10_pcie_performance_request(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "pcie performance request failed!", result = tmp_result);
-
-       return result;
-}
-
-int polaris10_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = (polaris10_is_dpm_running(hwmgr)) ? 0 : -1;
-       PP_ASSERT_WITH_CODE(tmp_result == 0,
-                       "DPM is not running right now, no need to disable DPM!",
-                       return 0);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalController))
-               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 1);
-
-       tmp_result = polaris10_disable_power_containment(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable power containment!", result = tmp_result);
-
-       tmp_result = polaris10_disable_smc_cac(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable SMC CAC!", result = tmp_result);
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_SPLL_SPREAD_SPECTRUM, SSEN, 0);
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 0);
-
-       tmp_result = polaris10_disable_thermal_auto_throttle(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable thermal auto throttle!", result = tmp_result);
-
-       tmp_result = polaris10_stop_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to stop DPM!", result = tmp_result);
-
-       tmp_result = polaris10_disable_deep_sleep_master_switch(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable deep sleep master switch!", result = tmp_result);
-
-       tmp_result = polaris10_disable_ulv(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to disable ULV!", result = tmp_result);
-
-       tmp_result = polaris10_clear_voting_clients(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to clear voting clients!", result = tmp_result);
-
-       tmp_result = polaris10_reset_to_default(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to reset to default!", result = tmp_result);
-
-       tmp_result = polaris10_force_switch_to_arbf0(hwmgr);
-       PP_ASSERT_WITH_CODE((tmp_result == 0),
-                       "Failed to force to switch arbf0!", result = tmp_result);
-
-       return result;
-}
-
-int polaris10_reset_asic_tasks(struct pp_hwmgr *hwmgr)
-{
-
-       return 0;
-}
-
-int polaris10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
-{
-       return phm_hwmgr_backend_fini(hwmgr);
-}
-
-int polaris10_set_features_platform_caps(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_DynamicPatchPowerState);
-
-       if (data->mvdd_control == POLARIS10_VOLTAGE_CONTROL_NONE)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_EnableMVDDControl);
-
-       if (data->vddci_control == POLARIS10_VOLTAGE_CONTROL_NONE)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_ControlVDDCI);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                        PHM_PlatformCaps_TablelessHardwareInterface);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableSMU7ThermalManagement);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DynamicPowerManagement);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_UnTabledHardwareInterface);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_TablelessHardwareInterface);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_SMC);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_NonABMSupportInPPLib);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_DynamicUVDState);
-
-       /* power tune caps Assume disabled */
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_SQRamping);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_DBRamping);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_TDRamping);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_TCPRamping);
-
-       if (hwmgr->powercontainment_enabled)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                           PHM_PlatformCaps_PowerContainment);
-       else
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                           PHM_PlatformCaps_PowerContainment);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                                       PHM_PlatformCaps_CAC);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_RegulatorHot);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_AutomaticDCTransition);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_ODFuzzyFanControlSupport);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_FanSpeedInTableIsRPM);
-
-       if (hwmgr->chip_id == CHIP_POLARIS11)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_SPLLShutdownSupport);
-       return 0;
-}
-
-static void polaris10_init_dpm_defaults(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       polaris10_initialize_power_tune_defaults(hwmgr);
-
-       data->pcie_gen_performance.max = PP_PCIEGen1;
-       data->pcie_gen_performance.min = PP_PCIEGen3;
-       data->pcie_gen_power_saving.max = PP_PCIEGen1;
-       data->pcie_gen_power_saving.min = PP_PCIEGen3;
-       data->pcie_lane_performance.max = 0;
-       data->pcie_lane_performance.min = 16;
-       data->pcie_lane_power_saving.max = 0;
-       data->pcie_lane_power_saving.min = 16;
-}
-
-/**
-* Get Leakage VDDC based on leakage ID.
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always 0
-*/
-static int polaris10_get_evv_voltages(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint16_t vv_id;
-       uint32_t vddc = 0;
-       uint16_t i, j;
-       uint32_t sclk = 0;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)hwmgr->pptable;
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       int result;
-
-       for (i = 0; i < POLARIS10_MAX_LEAKAGE_COUNT; i++) {
-               vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
-               if (!phm_get_sclk_for_voltage_evv(hwmgr,
-                               table_info->vddc_lookup_table, vv_id, &sclk)) {
-                       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_ClockStretcher)) {
-                               for (j = 1; j < sclk_table->count; j++) {
-                                       if (sclk_table->entries[j].clk == sclk &&
-                                                       sclk_table->entries[j].cks_enable == 0) {
-                                               sclk += 5000;
-                                               break;
-                                       }
-                               }
-                       }
-
-                       if (atomctrl_get_voltage_evv_on_sclk_ai(hwmgr,
-                                               VOLTAGE_TYPE_VDDC,
-                                               sclk, vv_id, &vddc) != 0) {
-                               printk(KERN_WARNING "failed to retrieving EVV voltage!\n");
-                               continue;
-                       }
-
-                       /* need to make sure vddc is less than 2v or else, it could burn the ASIC.
-                        * real voltage level in unit of 0.01mv */
-                       PP_ASSERT_WITH_CODE((vddc < 200000 && vddc != 0),
-                                       "Invalid VDDC value", result = -EINVAL;);
-
-                       /* the voltage should not be zero nor equal to leakage ID */
-                       if (vddc != 0 && vddc != vv_id) {
-                               data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = (uint16_t)(vddc/100);
-                               data->vddc_leakage.leakage_id[data->vddc_leakage.count] = vv_id;
-                               data->vddc_leakage.count++;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/**
- * Change virtual leakage voltage to actual value.
- *
- * @param     hwmgr  the address of the powerplay hardware manager.
- * @param     pointer to changing voltage
- * @param     pointer to leakage table
- */
-static void polaris10_patch_with_vdd_leakage(struct pp_hwmgr *hwmgr,
-               uint16_t *voltage, struct polaris10_leakage_voltage *leakage_table)
-{
-       uint32_t index;
-
-       /* search for leakage voltage ID 0xff01 ~ 0xff08 */
-       for (index = 0; index < leakage_table->count; index++) {
-               /* if this voltage matches a leakage voltage ID */
-               /* patch with actual leakage voltage */
-               if (leakage_table->leakage_id[index] == *voltage) {
-                       *voltage = leakage_table->actual_voltage[index];
-                       break;
-               }
-       }
-
-       if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
-               printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n");
-}
-
-/**
-* Patch voltage lookup table by EVV leakages.
-*
-* @param     hwmgr  the address of the powerplay hardware manager.
-* @param     pointer to voltage lookup table
-* @param     pointer to leakage table
-* @return     always 0
-*/
-static int polaris10_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *lookup_table,
-               struct polaris10_leakage_voltage *leakage_table)
-{
-       uint32_t i;
-
-       for (i = 0; i < lookup_table->count; i++)
-               polaris10_patch_with_vdd_leakage(hwmgr,
-                               &lookup_table->entries[i].us_vdd, leakage_table);
-
-       return 0;
-}
-
-static int polaris10_patch_clock_voltage_limits_with_vddc_leakage(
-               struct pp_hwmgr *hwmgr, struct polaris10_leakage_voltage *leakage_table,
-               uint16_t *vddc)
-{
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       polaris10_patch_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);
-       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
-                       table_info->max_clock_voltage_on_dc.vddc;
-       return 0;
-}
-
-static int polaris10_patch_voltage_dependency_tables_with_lookup_table(
-               struct pp_hwmgr *hwmgr)
-{
-       uint8_t entryId;
-       uint8_t voltageId;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
-                       table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
-                       table_info->vdd_dep_on_mclk;
-       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
-                       table_info->mm_dep_table;
-
-       for (entryId = 0; entryId < sclk_table->count; ++entryId) {
-               voltageId = sclk_table->entries[entryId].vddInd;
-               sclk_table->entries[entryId].vddc =
-                               table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       for (entryId = 0; entryId < mclk_table->count; ++entryId) {
-               voltageId = mclk_table->entries[entryId].vddInd;
-               mclk_table->entries[entryId].vddc =
-                       table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       for (entryId = 0; entryId < mm_table->count; ++entryId) {
-               voltageId = mm_table->entries[entryId].vddcInd;
-               mm_table->entries[entryId].vddc =
-                       table_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       return 0;
-
-}
-
-static int polaris10_calc_voltage_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       /* Need to determine if we need calculated voltage. */
-       return 0;
-}
-
-static int polaris10_calc_mm_voltage_dependency_table(struct pp_hwmgr *hwmgr)
-{
-       /* Need to determine if we need calculated voltage from mm table. */
-       return 0;
-}
-
-static int polaris10_sort_lookup_table(struct pp_hwmgr *hwmgr,
-               struct phm_ppt_v1_voltage_lookup_table *lookup_table)
-{
-       uint32_t table_size, i, j;
-       struct phm_ppt_v1_voltage_lookup_record tmp_voltage_lookup_record;
-       table_size = lookup_table->count;
-
-       PP_ASSERT_WITH_CODE(0 != lookup_table->count,
-               "Lookup table is empty", return -EINVAL);
-
-       /* Sorting voltages */
-       for (i = 0; i < table_size - 1; i++) {
-               for (j = i + 1; j > 0; j--) {
-                       if (lookup_table->entries[j].us_vdd <
-                                       lookup_table->entries[j - 1].us_vdd) {
-                               tmp_voltage_lookup_record = lookup_table->entries[j - 1];
-                               lookup_table->entries[j - 1] = lookup_table->entries[j];
-                               lookup_table->entries[j] = tmp_voltage_lookup_record;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_complete_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       int result = 0;
-       int tmp_result;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       tmp_result = polaris10_patch_lookup_table_with_leakage(hwmgr,
-                       table_info->vddc_lookup_table, &(data->vddc_leakage));
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = polaris10_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
-                       &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = polaris10_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = polaris10_calc_voltage_dependency_tables(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = polaris10_calc_mm_voltage_dependency_table(hwmgr);
-       if (tmp_result)
-               result = tmp_result;
-
-       tmp_result = polaris10_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
-       if (tmp_result)
-               result = tmp_result;
-
-       return result;
-}
-
-static int polaris10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
-{
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
-                                               table_info->vdd_dep_on_sclk;
-       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
-                                               table_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table != NULL,
-               "VDD dependency on SCLK table is missing.       \
-               This table is mandatory", return -EINVAL);
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
-               "VDD dependency on SCLK table has to have is missing.   \
-               This table is mandatory", return -EINVAL);
-
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table != NULL,
-               "VDD dependency on MCLK table is missing.       \
-               This table is mandatory", return -EINVAL);
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
-               "VDD dependency on MCLK table has to have is missing.    \
-               This table is mandatory", return -EINVAL);
-
-       table_info->max_clock_voltage_on_ac.sclk =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
-       table_info->max_clock_voltage_on_ac.mclk =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
-       table_info->max_clock_voltage_on_ac.vddc =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
-       table_info->max_clock_voltage_on_ac.vddci =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
-
-       hwmgr->dyn_state.max_clock_voltage_on_ac.sclk = table_info->max_clock_voltage_on_ac.sclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.mclk = table_info->max_clock_voltage_on_ac.mclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddc = table_info->max_clock_voltage_on_ac.vddc;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddci =table_info->max_clock_voltage_on_ac.vddci;
-
-       return 0;
-}
-
-int polaris10_patch_voltage_workaround(struct pp_hwmgr *hwmgr)
-{
-       struct phm_ppt_v1_information *table_info =
-                      (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                       table_info->vdd_dep_on_mclk;
-       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
-                       table_info->vddc_lookup_table;
-       uint32_t i;
-
-       if (hwmgr->chip_id == CHIP_POLARIS10 && hwmgr->hw_revision == 0xC7) {
-               if (lookup_table->entries[dep_mclk_table->entries[dep_mclk_table->count-1].vddInd].us_vdd >= 1000)
-                       return 0;
-
-               for (i = 0; i < lookup_table->count; i++) {
-                       if (lookup_table->entries[i].us_vdd < 0xff01 && lookup_table->entries[i].us_vdd >= 1000) {
-                               dep_mclk_table->entries[dep_mclk_table->count-1].vddInd = (uint8_t) i;
-                               return 0;
-                       }
-               }
-       }
-       return 0;
-}
-
-
-int polaris10_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data;
-       struct pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
-       uint32_t temp_reg;
-       int result;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       data = kzalloc(sizeof(struct polaris10_hwmgr), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       hwmgr->backend = data;
-
-       data->dll_default_on = false;
-       data->sram_end = SMC_RAM_END;
-       data->mclk_dpm0_activity_target = 0xa;
-       data->disable_dpm_mask = 0xFF;
-       data->static_screen_threshold = PPPOLARIS10_STATICSCREENTHRESHOLD_DFLT;
-       data->static_screen_threshold_unit = PPPOLARIS10_STATICSCREENTHRESHOLD_DFLT;
-       data->activity_target[0] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[1] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[2] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[3] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[4] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[5] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[6] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-       data->activity_target[7] = PPPOLARIS10_TARGETACTIVITY_DFLT;
-
-       data->voting_rights_clients0 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT0;
-       data->voting_rights_clients1 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT1;
-       data->voting_rights_clients2 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT2;
-       data->voting_rights_clients3 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT3;
-       data->voting_rights_clients4 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT4;
-       data->voting_rights_clients5 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT5;
-       data->voting_rights_clients6 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT6;
-       data->voting_rights_clients7 = PPPOLARIS10_VOTINGRIGHTSCLIENTS_DFLT7;
-
-       data->vddc_vddci_delta = VDDC_VDDCI_DELTA;
-
-       data->mclk_activity_target = PPPOLARIS10_MCLK_TARGETACTIVITY_DFLT;
-
-       /* need to set voltage control types before EVV patching */
-       data->voltage_control = POLARIS10_VOLTAGE_CONTROL_NONE;
-       data->vddci_control = POLARIS10_VOLTAGE_CONTROL_NONE;
-       data->mvdd_control = POLARIS10_VOLTAGE_CONTROL_NONE;
-
-       data->enable_tdc_limit_feature = true;
-       data->enable_pkg_pwr_tracking_feature = true;
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-       data->mclk_stutter_mode_threshold = 40000;
-
-       if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                       VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2))
-               data->voltage_control = POLARIS10_VOLTAGE_CONTROL_BY_SVID2;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableMVDDControl)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT))
-                       data->mvdd_control = POLARIS10_VOLTAGE_CONTROL_BY_GPIO;
-               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2))
-                       data->mvdd_control = POLARIS10_VOLTAGE_CONTROL_BY_SVID2;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ControlVDDCI)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
-                       data->vddci_control = POLARIS10_VOLTAGE_CONTROL_BY_GPIO;
-               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
-                       data->vddci_control = POLARIS10_VOLTAGE_CONTROL_BY_SVID2;
-       }
-
-       if (table_info->cac_dtp_table->usClockStretchAmount != 0)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_ClockStretcher);
-
-       polaris10_set_features_platform_caps(hwmgr);
-
-       polaris10_patch_voltage_workaround(hwmgr);
-       polaris10_init_dpm_defaults(hwmgr);
-
-       /* Get leakage voltage based on leakage ID. */
-       result = polaris10_get_evv_voltages(hwmgr);
-
-       if (result) {
-               printk("Get EVV Voltage Failed.  Abort Driver loading!\n");
-               return -1;
-       }
-
-       polaris10_complete_dependency_tables(hwmgr);
-       polaris10_set_private_data_based_on_pptable(hwmgr);
-
-       /* Initalize Dynamic State Adjustment Rule Settings */
-       result = phm_initializa_dynamic_state_adjustment_rule_settings(hwmgr);
-
-       if (0 == result) {
-               struct cgs_system_info sys_info = {0};
-
-               data->is_tlu_enabled = false;
-
-               hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
-                                                       POLARIS10_MAX_HARDWARE_POWERLEVELS;
-               hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
-               hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
-
-
-               if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_PCC_GPIO_PINID, &gpio_pin_assignment)) {
-                       temp_reg = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL);
-                       switch (gpio_pin_assignment.uc_gpio_pin_bit_shift) {
-                       case 0:
-                               temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x1);
-                               break;
-                       case 1:
-                               temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x2);
-                               break;
-                       case 2:
-                               temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW, 0x1);
-                               break;
-                       case 3:
-                               temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, FORCE_NB_PS1, 0x1);
-                               break;
-                       case 4:
-                               temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, DPM_ENABLED, 0x1);
-                               break;
-                       default:
-                               PP_ASSERT_WITH_CODE(0,
-                               "Failed to setup PCC HW register! Wrong GPIO assigned for VDDC_PCC_GPIO_PINID!",
-                               );
-                               break;
-                       }
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL, temp_reg);
-               }
-
-               if (table_info->cac_dtp_table->usDefaultTargetOperatingTemp != 0 &&
-                       hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode) {
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMinLimit =
-                               (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMaxLimit =
-                               (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMStep = 1;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit = 100;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMinLimit =
-                               (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMStep = 1;
-
-                       table_info->cac_dtp_table->usDefaultTargetOperatingTemp = (table_info->cac_dtp_table->usDefaultTargetOperatingTemp >= 50) ?
-                                                                       (table_info->cac_dtp_table->usDefaultTargetOperatingTemp -50) : 0;
-
-                       table_info->cac_dtp_table->usOperatingTempMaxLimit = table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
-                       table_info->cac_dtp_table->usOperatingTempStep = 1;
-                       table_info->cac_dtp_table->usOperatingTempHyst = 1;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM =
-                                      hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
-
-                       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
-                                      hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM;
-
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempMinLimit =
-                                      table_info->cac_dtp_table->usOperatingTempMinLimit;
-
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempMaxLimit =
-                                      table_info->cac_dtp_table->usOperatingTempMaxLimit;
-
-                       hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp =
-                                      table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
-
-                       hwmgr->dyn_state.cac_dtp_table->usOperatingTempStep =
-                                      table_info->cac_dtp_table->usOperatingTempStep;
-
-                       hwmgr->dyn_state.cac_dtp_table->usTargetOperatingTemp =
-                                      table_info->cac_dtp_table->usTargetOperatingTemp;
-               }
-
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_GEN_INFO;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_gen_cap = AMDGPU_DEFAULT_PCIE_GEN_MASK;
-               else
-                       data->pcie_gen_cap = (uint32_t)sys_info.value;
-               if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
-                       data->pcie_spc_cap = 20;
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_MLW;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_lane_cap = AMDGPU_DEFAULT_PCIE_MLW_MASK;
-               else
-                       data->pcie_lane_cap = (uint32_t)sys_info.value;
-
-               hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
-/* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
-               hwmgr->platform_descriptor.clockStep.engineClock = 500;
-               hwmgr->platform_descriptor.clockStep.memoryClock = 500;
-       } else {
-               /* Ignore return value in here, we are cleaning up a mess. */
-               polaris10_hwmgr_backend_fini(hwmgr);
-       }
-
-       return 0;
-}
-
-static int polaris10_force_dpm_highest(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t level, tmp;
-
-       if (!data->pcie_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_ForceLevel, level);
-               }
-       }
-
-       if (!data->sclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                               (1 << level));
-               }
-       }
-
-       if (!data->mclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++;
-
-                       if (level)
-                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                               (1 << level));
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       phm_apply_dal_min_voltage_request(hwmgr);
-
-       if (!data->sclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-       }
-
-       if (!data->mclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-       }
-
-       return 0;
-}
-
-static int polaris10_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (!polaris10_is_dpm_running(hwmgr))
-               return -EINVAL;
-
-       if (!data->pcie_dpm_key_disabled) {
-               smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_PCIeDPM_UnForceLevel);
-       }
-
-       return polaris10_upload_dpm_level_enable_mask(hwmgr);
-}
-
-static int polaris10_force_dpm_lowest(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data =
-                       (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t level;
-
-       if (!data->sclk_dpm_key_disabled)
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       level = phm_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                                           (1 << level));
-
-       }
-
-       if (!data->mclk_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       level = phm_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                                           (1 << level));
-               }
-       }
-
-       if (!data->pcie_dpm_key_disabled) {
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
-                       level = phm_get_lowest_enabled_level(hwmgr,
-                                                             data->dpm_level_enable_mask.pcie_dpm_enable_mask);
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                           PPSMC_MSG_PCIeDPM_ForceLevel,
-                                                           (level));
-               }
-       }
-
-       return 0;
-
-}
-static int polaris10_force_dpm_level(struct pp_hwmgr *hwmgr,
-                               enum amd_dpm_forced_level level)
-{
-       int ret = 0;
-
-       switch (level) {
-       case AMD_DPM_FORCED_LEVEL_HIGH:
-               ret = polaris10_force_dpm_highest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_LOW:
-               ret = polaris10_force_dpm_lowest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_AUTO:
-               ret = polaris10_unforce_dpm_levels(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       default:
-               break;
-       }
-
-       hwmgr->dpm_level = level;
-
-       return ret;
-}
-
-static int polaris10_get_power_state_size(struct pp_hwmgr *hwmgr)
-{
-       return sizeof(struct polaris10_power_state);
-}
-
-
-static int polaris10_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
-                               struct pp_power_state *request_ps,
-                       const struct pp_power_state *current_ps)
-{
-
-       struct polaris10_power_state *polaris10_ps =
-                               cast_phw_polaris10_power_state(&request_ps->hardware);
-       uint32_t sclk;
-       uint32_t mclk;
-       struct PP_Clocks minimum_clocks = {0};
-       bool disable_mclk_switching;
-       bool disable_mclk_switching_for_frame_lock;
-       struct cgs_display_info info = {0};
-       const struct phm_clock_and_voltage_limits *max_limits;
-       uint32_t i;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int32_t count;
-       int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
-
-       data->battery_state = (PP_StateUILabel_Battery ==
-                       request_ps->classification.ui_label);
-
-       PP_ASSERT_WITH_CODE(polaris10_ps->performance_level_count == 2,
-                                "VI should always have 2 performance levels",
-                               );
-
-       max_limits = (PP_PowerSource_AC == hwmgr->power_source) ?
-                       &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
-                       &(hwmgr->dyn_state.max_clock_voltage_on_dc);
-
-       /* Cap clock DPM tables at DC MAX if it is in DC. */
-       if (PP_PowerSource_DC == hwmgr->power_source) {
-               for (i = 0; i < polaris10_ps->performance_level_count; i++) {
-                       if (polaris10_ps->performance_levels[i].memory_clock > max_limits->mclk)
-                               polaris10_ps->performance_levels[i].memory_clock = max_limits->mclk;
-                       if (polaris10_ps->performance_levels[i].engine_clock > max_limits->sclk)
-                               polaris10_ps->performance_levels[i].engine_clock = max_limits->sclk;
-               }
-       }
-
-       polaris10_ps->vce_clks.evclk = hwmgr->vce_arbiter.evclk;
-       polaris10_ps->vce_clks.ecclk = hwmgr->vce_arbiter.ecclk;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       /*TO DO result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
-
-       /* TO DO GetMinClockSettings(hwmgr->pPECI, &minimum_clocks); */
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StablePState)) {
-               max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
-               stable_pstate_sclk = (max_limits->sclk * 75) / 100;
-
-               for (count = table_info->vdd_dep_on_sclk->count - 1;
-                               count >= 0; count--) {
-                       if (stable_pstate_sclk >=
-                                       table_info->vdd_dep_on_sclk->entries[count].clk) {
-                               stable_pstate_sclk =
-                                               table_info->vdd_dep_on_sclk->entries[count].clk;
-                               break;
-                       }
-               }
-
-               if (count < 0)
-                       stable_pstate_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
-
-               stable_pstate_mclk = max_limits->mclk;
-
-               minimum_clocks.engineClock = stable_pstate_sclk;
-               minimum_clocks.memoryClock = stable_pstate_mclk;
-       }
-
-       if (minimum_clocks.engineClock < hwmgr->gfx_arbiter.sclk)
-               minimum_clocks.engineClock = hwmgr->gfx_arbiter.sclk;
-
-       if (minimum_clocks.memoryClock < hwmgr->gfx_arbiter.mclk)
-               minimum_clocks.memoryClock = hwmgr->gfx_arbiter.mclk;
-
-       polaris10_ps->sclk_threshold = hwmgr->gfx_arbiter.sclk_threshold;
-
-       if (0 != hwmgr->gfx_arbiter.sclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.sclk_over_drive <=
-                               hwmgr->platform_descriptor.overdriveLimit.engineClock),
-                               "Overdrive sclk exceeds limit",
-                               hwmgr->gfx_arbiter.sclk_over_drive =
-                                               hwmgr->platform_descriptor.overdriveLimit.engineClock);
-
-               if (hwmgr->gfx_arbiter.sclk_over_drive >= hwmgr->gfx_arbiter.sclk)
-                       polaris10_ps->performance_levels[1].engine_clock =
-                                       hwmgr->gfx_arbiter.sclk_over_drive;
-       }
-
-       if (0 != hwmgr->gfx_arbiter.mclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.mclk_over_drive <=
-                               hwmgr->platform_descriptor.overdriveLimit.memoryClock),
-                               "Overdrive mclk exceeds limit",
-                               hwmgr->gfx_arbiter.mclk_over_drive =
-                                               hwmgr->platform_descriptor.overdriveLimit.memoryClock);
-
-               if (hwmgr->gfx_arbiter.mclk_over_drive >= hwmgr->gfx_arbiter.mclk)
-                       polaris10_ps->performance_levels[1].memory_clock =
-                                       hwmgr->gfx_arbiter.mclk_over_drive;
-       }
-
-       disable_mclk_switching_for_frame_lock = phm_cap_enabled(
-                                   hwmgr->platform_descriptor.platformCaps,
-                                   PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
-
-
-       disable_mclk_switching = (1 < info.display_count) ||
-                                   disable_mclk_switching_for_frame_lock;
-
-       sclk = polaris10_ps->performance_levels[0].engine_clock;
-       mclk = polaris10_ps->performance_levels[0].memory_clock;
-
-       if (disable_mclk_switching)
-               mclk = polaris10_ps->performance_levels
-               [polaris10_ps->performance_level_count - 1].memory_clock;
-
-       if (sclk < minimum_clocks.engineClock)
-               sclk = (minimum_clocks.engineClock > max_limits->sclk) ?
-                               max_limits->sclk : minimum_clocks.engineClock;
-
-       if (mclk < minimum_clocks.memoryClock)
-               mclk = (minimum_clocks.memoryClock > max_limits->mclk) ?
-                               max_limits->mclk : minimum_clocks.memoryClock;
-
-       polaris10_ps->performance_levels[0].engine_clock = sclk;
-       polaris10_ps->performance_levels[0].memory_clock = mclk;
-
-       polaris10_ps->performance_levels[1].engine_clock =
-               (polaris10_ps->performance_levels[1].engine_clock >=
-                               polaris10_ps->performance_levels[0].engine_clock) ?
-                                               polaris10_ps->performance_levels[1].engine_clock :
-                                               polaris10_ps->performance_levels[0].engine_clock;
-
-       if (disable_mclk_switching) {
-               if (mclk < polaris10_ps->performance_levels[1].memory_clock)
-                       mclk = polaris10_ps->performance_levels[1].memory_clock;
-
-               polaris10_ps->performance_levels[0].memory_clock = mclk;
-               polaris10_ps->performance_levels[1].memory_clock = mclk;
-       } else {
-               if (polaris10_ps->performance_levels[1].memory_clock <
-                               polaris10_ps->performance_levels[0].memory_clock)
-                       polaris10_ps->performance_levels[1].memory_clock =
-                                       polaris10_ps->performance_levels[0].memory_clock;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StablePState)) {
-               for (i = 0; i < polaris10_ps->performance_level_count; i++) {
-                       polaris10_ps->performance_levels[i].engine_clock = stable_pstate_sclk;
-                       polaris10_ps->performance_levels[i].memory_clock = stable_pstate_mclk;
-                       polaris10_ps->performance_levels[i].pcie_gen = data->pcie_gen_performance.max;
-                       polaris10_ps->performance_levels[i].pcie_lane = data->pcie_gen_performance.max;
-               }
-       }
-       return 0;
-}
-
-
-static int polaris10_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct polaris10_power_state  *polaris10_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       polaris10_ps = cast_phw_polaris10_power_state(&ps->hardware);
-
-       if (low)
-               return polaris10_ps->performance_levels[0].memory_clock;
-       else
-               return polaris10_ps->performance_levels
-                               [polaris10_ps->performance_level_count-1].memory_clock;
-}
-
-static int polaris10_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct polaris10_power_state  *polaris10_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       polaris10_ps = cast_phw_polaris10_power_state(&ps->hardware);
-
-       if (low)
-               return polaris10_ps->performance_levels[0].engine_clock;
-       else
-               return polaris10_ps->performance_levels
-                               [polaris10_ps->performance_level_count-1].engine_clock;
-}
-
-static int polaris10_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
-                                       struct pp_hw_power_state *hw_ps)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_power_state *ps = (struct polaris10_power_state *)hw_ps;
-       ATOM_FIRMWARE_INFO_V2_2 *fw_info;
-       uint16_t size;
-       uint8_t frev, crev;
-       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
-
-       /* First retrieve the Boot clocks and VDDC from the firmware info table.
-        * We assume here that fw_info is unchanged if this call fails.
-        */
-       fw_info = (ATOM_FIRMWARE_INFO_V2_2 *)cgs_atom_get_data_table(
-                       hwmgr->device, index,
-                       &size, &frev, &crev);
-       if (!fw_info)
-               /* During a test, there is no firmware info table. */
-               return 0;
-
-       /* Patch the state. */
-       data->vbios_boot_state.sclk_bootup_value =
-                       le32_to_cpu(fw_info->ulDefaultEngineClock);
-       data->vbios_boot_state.mclk_bootup_value =
-                       le32_to_cpu(fw_info->ulDefaultMemoryClock);
-       data->vbios_boot_state.mvdd_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpMVDDCVoltage);
-       data->vbios_boot_state.vddc_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpVDDCVoltage);
-       data->vbios_boot_state.vddci_bootup_value =
-                       le16_to_cpu(fw_info->usBootUpVDDCIVoltage);
-       data->vbios_boot_state.pcie_gen_bootup_value =
-                       phm_get_current_pcie_speed(hwmgr);
-
-       data->vbios_boot_state.pcie_lane_bootup_value =
-                       (uint16_t)phm_get_current_pcie_lane_number(hwmgr);
-
-       /* set boot power state */
-       ps->performance_levels[0].memory_clock = data->vbios_boot_state.mclk_bootup_value;
-       ps->performance_levels[0].engine_clock = data->vbios_boot_state.sclk_bootup_value;
-       ps->performance_levels[0].pcie_gen = data->vbios_boot_state.pcie_gen_bootup_value;
-       ps->performance_levels[0].pcie_lane = data->vbios_boot_state.pcie_lane_bootup_value;
-
-       return 0;
-}
-
-static int polaris10_get_pp_table_entry_callback_func(struct pp_hwmgr *hwmgr,
-               void *state, struct pp_power_state *power_state,
-               void *pp_table, uint32_t classification_flag)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_power_state  *polaris10_power_state =
-                       (struct polaris10_power_state *)(&(power_state->hardware));
-       struct polaris10_performance_level *performance_level;
-       ATOM_Tonga_State *state_entry = (ATOM_Tonga_State *)state;
-       ATOM_Tonga_POWERPLAYTABLE *powerplay_table =
-                       (ATOM_Tonga_POWERPLAYTABLE *)pp_table;
-       PPTable_Generic_SubTable_Header *sclk_dep_table =
-                       (PPTable_Generic_SubTable_Header *)
-                       (((unsigned long)powerplay_table) +
-                               le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
-
-       ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
-                       (ATOM_Tonga_MCLK_Dependency_Table *)
-                       (((unsigned long)powerplay_table) +
-                               le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
-
-       /* The following fields are not initialized here: id orderedList allStatesList */
-       power_state->classification.ui_label =
-                       (le16_to_cpu(state_entry->usClassification) &
-                       ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
-                       ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
-       power_state->classification.flags = classification_flag;
-       /* NOTE: There is a classification2 flag in BIOS that is not being used right now */
-
-       power_state->classification.temporary_state = false;
-       power_state->classification.to_be_deleted = false;
-
-       power_state->validation.disallowOnDC =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
-                                       ATOM_Tonga_DISALLOW_ON_DC));
-
-       power_state->pcie.lanes = 0;
-
-       power_state->display.disableFrameModulation = false;
-       power_state->display.limitRefreshrate = false;
-       power_state->display.enableVariBright =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
-                                       ATOM_Tonga_ENABLE_VARIBRIGHT));
-
-       power_state->validation.supportedPowerLevels = 0;
-       power_state->uvd_clocks.VCLK = 0;
-       power_state->uvd_clocks.DCLK = 0;
-       power_state->temperatures.min = 0;
-       power_state->temperatures.max = 0;
-
-       performance_level = &(polaris10_power_state->performance_levels
-                       [polaris10_power_state->performance_level_count++]);
-
-       PP_ASSERT_WITH_CODE(
-                       (polaris10_power_state->performance_level_count < SMU74_MAX_LEVELS_GRAPHICS),
-                       "Performance levels exceeds SMC limit!",
-                       return -1);
-
-       PP_ASSERT_WITH_CODE(
-                       (polaris10_power_state->performance_level_count <=
-                                       hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
-                       "Performance levels exceeds Driver limit!",
-                       return -1);
-
-       /* Performance levels are arranged from low to high. */
-       performance_level->memory_clock = mclk_dep_table->entries
-                       [state_entry->ucMemoryClockIndexLow].ulMclk;
-       if (sclk_dep_table->ucRevId == 0)
-               performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
-                       [state_entry->ucEngineClockIndexLow].ulSclk;
-       else if (sclk_dep_table->ucRevId == 1)
-               performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
-                       [state_entry->ucEngineClockIndexLow].ulSclk;
-       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
-                       state_entry->ucPCIEGenLow);
-       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
-                       state_entry->ucPCIELaneHigh);
-
-       performance_level = &(polaris10_power_state->performance_levels
-                       [polaris10_power_state->performance_level_count++]);
-       performance_level->memory_clock = mclk_dep_table->entries
-                       [state_entry->ucMemoryClockIndexHigh].ulMclk;
-
-       if (sclk_dep_table->ucRevId == 0)
-               performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
-                       [state_entry->ucEngineClockIndexHigh].ulSclk;
-       else if (sclk_dep_table->ucRevId == 1)
-               performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
-                       [state_entry->ucEngineClockIndexHigh].ulSclk;
-
-       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
-                       state_entry->ucPCIEGenHigh);
-       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
-                       state_entry->ucPCIELaneHigh);
-
-       return 0;
-}
-
-static int polaris10_get_pp_table_entry(struct pp_hwmgr *hwmgr,
-               unsigned long entry_index, struct pp_power_state *state)
-{
-       int result;
-       struct polaris10_power_state *ps;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                       table_info->vdd_dep_on_mclk;
-
-       state->hardware.magic = PHM_VIslands_Magic;
-
-       ps = (struct polaris10_power_state *)(&state->hardware);
-
-       result = tonga_get_powerplay_table_entry(hwmgr, entry_index, state,
-                       polaris10_get_pp_table_entry_callback_func);
-
-       /* This is the earliest time we have all the dependency table and the VBIOS boot state
-        * as PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot state
-        * if there is only one VDDCI/MCLK level, check if it's the same as VBIOS boot state
-        */
-       if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
-               if (dep_mclk_table->entries[0].clk !=
-                               data->vbios_boot_state.mclk_bootup_value)
-                       printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot MCLK level");
-               if (dep_mclk_table->entries[0].vddci !=
-                               data->vbios_boot_state.vddci_bootup_value)
-                       printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot VDDCI level");
-       }
-
-       /* set DC compatible flag if this state supports DC */
-       if (!state->validation.disallowOnDC)
-               ps->dc_compatible = true;
-
-       if (state->classification.flags & PP_StateClassificationFlag_ACPI)
-               data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
-
-       ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
-       ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
-
-       if (!result) {
-               uint32_t i;
-
-               switch (state->classification.ui_label) {
-               case PP_StateUILabel_Performance:
-                       data->use_pcie_performance_levels = true;
-                       for (i = 0; i < ps->performance_level_count; i++) {
-                               if (data->pcie_gen_performance.max <
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.max =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_performance.min >
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.min =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_performance.max <
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.max =
-                                                       ps->performance_levels[i].pcie_lane;
-                               if (data->pcie_lane_performance.min >
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.min =
-                                                       ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               case PP_StateUILabel_Battery:
-                       data->use_pcie_power_saving_levels = true;
-
-                       for (i = 0; i < ps->performance_level_count; i++) {
-                               if (data->pcie_gen_power_saving.max <
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.max =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_power_saving.min >
-                                               ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.min =
-                                                       ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_power_saving.max <
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.max =
-                                                       ps->performance_levels[i].pcie_lane;
-
-                               if (data->pcie_lane_power_saving.min >
-                                               ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.min =
-                                                       ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-static void
-polaris10_print_current_perforce_level(struct pp_hwmgr *hwmgr, struct seq_file *m)
-{
-       uint32_t sclk, mclk, activity_percent;
-       uint32_t offset;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
-
-       sclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
-
-       mclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-       seq_printf(m, "\n [  mclk  ]: %u MHz\n\n [  sclk  ]: %u MHz\n",
-                       mclk / 100, sclk / 100);
-
-       offset = data->soft_regs_start + offsetof(SMU74_SoftRegisters, AverageGraphicsActivity);
-       activity_percent = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset);
-       activity_percent += 0x80;
-       activity_percent >>= 8;
-
-       seq_printf(m, "\n [GPU load]: %u%%\n\n", activity_percent > 100 ? 100 : activity_percent);
-
-       seq_printf(m, "uvd    %sabled\n", data->uvd_power_gated ? "dis" : "en");
-
-       seq_printf(m, "vce    %sabled\n", data->vce_power_gated ? "dis" : "en");
-}
-
-static int polaris10_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       const struct polaris10_power_state *polaris10_ps =
-                       cast_const_phw_polaris10_power_state(states->pnew_state);
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       uint32_t sclk = polaris10_ps->performance_levels
-                       [polaris10_ps->performance_level_count - 1].engine_clock;
-       struct polaris10_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       uint32_t mclk = polaris10_ps->performance_levels
-                       [polaris10_ps->performance_level_count - 1].memory_clock;
-       struct PP_Clocks min_clocks = {0};
-       uint32_t i;
-       struct cgs_display_info info = {0};
-
-       data->need_update_smu7_dpm_table = 0;
-
-       for (i = 0; i < sclk_table->count; i++) {
-               if (sclk == sclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= sclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
-       else {
-       /* TODO: Check SCLK in DAL's minimum clocks
-        * in case DeepSleep divider update is required.
-        */
-               if (data->display_timing.min_clock_in_sr != min_clocks.engineClockInSR &&
-                       (min_clocks.engineClockInSR >= POLARIS10_MINIMUM_ENGINE_CLOCK ||
-                               data->display_timing.min_clock_in_sr >= POLARIS10_MINIMUM_ENGINE_CLOCK))
-                       data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
-       }
-
-       for (i = 0; i < mclk_table->count; i++) {
-               if (mclk == mclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= mclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
-
-       return 0;
-}
-
-static uint16_t polaris10_get_maximum_link_speed(struct pp_hwmgr *hwmgr,
-               const struct polaris10_power_state *polaris10_ps)
-{
-       uint32_t i;
-       uint32_t sclk, max_sclk = 0;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_dpm_table *dpm_table = &data->dpm_table;
-
-       for (i = 0; i < polaris10_ps->performance_level_count; i++) {
-               sclk = polaris10_ps->performance_levels[i].engine_clock;
-               if (max_sclk < sclk)
-                       max_sclk = sclk;
-       }
-
-       for (i = 0; i < dpm_table->sclk_table.count; i++) {
-               if (dpm_table->sclk_table.dpm_levels[i].value == max_sclk)
-                       return (uint16_t) ((i >= dpm_table->pcie_speed_table.count) ?
-                                       dpm_table->pcie_speed_table.dpm_levels
-                                       [dpm_table->pcie_speed_table.count - 1].value :
-                                       dpm_table->pcie_speed_table.dpm_levels[i].value);
-       }
-
-       return 0;
-}
-
-static int polaris10_request_link_speed_change_before_state_change(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_power_state *polaris10_nps =
-                       cast_const_phw_polaris10_power_state(states->pnew_state);
-       const struct polaris10_power_state *polaris10_cps =
-                       cast_const_phw_polaris10_power_state(states->pcurrent_state);
-
-       uint16_t target_link_speed = polaris10_get_maximum_link_speed(hwmgr, polaris10_nps);
-       uint16_t current_link_speed;
-
-       if (data->force_pcie_gen == PP_PCIEGenInvalid)
-               current_link_speed = polaris10_get_maximum_link_speed(hwmgr, polaris10_cps);
-       else
-               current_link_speed = data->force_pcie_gen;
-
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-       data->pspp_notify_required = false;
-
-       if (target_link_speed > current_link_speed) {
-               switch (target_link_speed) {
-               case PP_PCIEGen3:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN3, false))
-                               break;
-                       data->force_pcie_gen = PP_PCIEGen2;
-                       if (current_link_speed == PP_PCIEGen2)
-                               break;
-               case PP_PCIEGen2:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN2, false))
-                               break;
-               default:
-                       data->force_pcie_gen = phm_get_current_pcie_speed(hwmgr);
-                       break;
-               }
-       } else {
-               if (target_link_speed < current_link_speed)
-                       data->pspp_notify_required = true;
-       }
-
-       return 0;
-}
-
-static int polaris10_freeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-               PP_ASSERT_WITH_CODE(polaris10_is_dpm_running(hwmgr),
-                                   "Trying to freeze SCLK DPM when DPM is disabled",
-                               );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_FreezeLevel),
-                               "Failed to freeze SCLK DPM during FreezeSclkMclkDPM Function!",
-                               return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-                DPMTABLE_OD_UPDATE_MCLK)) {
-               PP_ASSERT_WITH_CODE(polaris10_is_dpm_running(hwmgr),
-                                   "Trying to freeze MCLK DPM when DPM is disabled",
-                               );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_MCLKDPM_FreezeLevel),
-                               "Failed to freeze MCLK DPM during FreezeSclkMclkDPM Function!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-static int polaris10_populate_and_upload_sclk_mclk_dpm_levels(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result = 0;
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       const struct polaris10_power_state *polaris10_ps =
-                       cast_const_phw_polaris10_power_state(states->pnew_state);
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t sclk = polaris10_ps->performance_levels
-                       [polaris10_ps->performance_level_count - 1].engine_clock;
-       uint32_t mclk = polaris10_ps->performance_levels
-                       [polaris10_ps->performance_level_count - 1].memory_clock;
-       struct polaris10_dpm_table *dpm_table = &data->dpm_table;
-
-       struct polaris10_dpm_table *golden_dpm_table = &data->golden_dpm_table;
-       uint32_t dpm_count, clock_percent;
-       uint32_t i;
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
-               dpm_table->sclk_table.dpm_levels
-               [dpm_table->sclk_table.count - 1].value = sclk;
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
-                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
-               /* Need to do calculation based on the golden DPM table
-                * as the Heatmap GPU Clock axis is also based on the default values
-                */
-                       PP_ASSERT_WITH_CODE(
-                               (golden_dpm_table->sclk_table.dpm_levels
-                                               [golden_dpm_table->sclk_table.count - 1].value != 0),
-                               "Divide by 0!",
-                               return -1);
-                       dpm_count = dpm_table->sclk_table.count < 2 ? 0 : dpm_table->sclk_table.count - 2;
-
-                       for (i = dpm_count; i > 1; i--) {
-                               if (sclk > golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value) {
-                                       clock_percent =
-                                             ((sclk
-                                               - golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value
-                                               ) * 100)
-                                               / golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value;
-
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value +
-                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
-                                                               clock_percent)/100;
-
-                               } else if (golden_dpm_table->sclk_table.dpm_levels[dpm_table->sclk_table.count-1].value > sclk) {
-                                       clock_percent =
-                                               ((golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count - 1].value
-                                               - sclk) * 100)
-                                               / golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value;
-
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value -
-                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
-                                                                       clock_percent) / 100;
-                               } else
-                                       dpm_table->sclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->sclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
-               dpm_table->mclk_table.dpm_levels
-                       [dpm_table->mclk_table.count - 1].value = mclk;
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
-                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
-
-                       PP_ASSERT_WITH_CODE(
-                                       (golden_dpm_table->mclk_table.dpm_levels
-                                               [golden_dpm_table->mclk_table.count-1].value != 0),
-                                       "Divide by 0!",
-                                       return -1);
-                       dpm_count = dpm_table->mclk_table.count < 2 ? 0 : dpm_table->mclk_table.count - 2;
-                       for (i = dpm_count; i > 1; i--) {
-                               if (golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value < mclk) {
-                                       clock_percent = ((mclk -
-                                       golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value) * 100)
-                                       / golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value;
-
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value +
-                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
-                                                       clock_percent) / 100;
-
-                               } else if (golden_dpm_table->mclk_table.dpm_levels[dpm_table->mclk_table.count-1].value > mclk) {
-                                       clock_percent = (
-                                        (golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value - mclk)
-                                       * 100)
-                                       / golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value;
-
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value -
-                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
-                                                                       clock_percent) / 100;
-                               } else
-                                       dpm_table->mclk_table.dpm_levels[i].value =
-                                                       golden_dpm_table->mclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK)) {
-               result = polaris10_populate_all_graphic_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
-                               return result);
-       }
-
-       if (data->need_update_smu7_dpm_table &
-                       (DPMTABLE_OD_UPDATE_MCLK + DPMTABLE_UPDATE_MCLK)) {
-               /*populate MCLK dpm table to SMU7 */
-               result = polaris10_populate_all_memory_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
-                               return result);
-       }
-
-       return result;
-}
-
-static int polaris10_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
-                         struct polaris10_single_dpm_table *dpm_table,
-                       uint32_t low_limit, uint32_t high_limit)
-{
-       uint32_t i;
-
-       for (i = 0; i < dpm_table->count; i++) {
-               if ((dpm_table->dpm_levels[i].value < low_limit)
-               || (dpm_table->dpm_levels[i].value > high_limit))
-                       dpm_table->dpm_levels[i].enabled = false;
-               else
-                       dpm_table->dpm_levels[i].enabled = true;
-       }
-
-       return 0;
-}
-
-static int polaris10_trim_dpm_states(struct pp_hwmgr *hwmgr,
-               const struct polaris10_power_state *polaris10_ps)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t high_limit_count;
-
-       PP_ASSERT_WITH_CODE((polaris10_ps->performance_level_count >= 1),
-                       "power state did not have any performance level",
-                       return -1);
-
-       high_limit_count = (1 == polaris10_ps->performance_level_count) ? 0 : 1;
-
-       polaris10_trim_single_dpm_states(hwmgr,
-                       &(data->dpm_table.sclk_table),
-                       polaris10_ps->performance_levels[0].engine_clock,
-                       polaris10_ps->performance_levels[high_limit_count].engine_clock);
-
-       polaris10_trim_single_dpm_states(hwmgr,
-                       &(data->dpm_table.mclk_table),
-                       polaris10_ps->performance_levels[0].memory_clock,
-                       polaris10_ps->performance_levels[high_limit_count].memory_clock);
-
-       return 0;
-}
-
-static int polaris10_generate_dpm_level_enable_mask(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result;
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_power_state *polaris10_ps =
-                       cast_const_phw_polaris10_power_state(states->pnew_state);
-
-       result = polaris10_trim_dpm_states(hwmgr, polaris10_ps);
-       if (result)
-               return result;
-
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.sclk_table);
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.mclk_table);
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
-                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.pcie_speed_table);
-
-       return 0;
-}
-
-int polaris10_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
-                       PPSMC_MSG_UVDDPM_Enable :
-                       PPSMC_MSG_UVDDPM_Disable);
-}
-
-int polaris10_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable?
-                       PPSMC_MSG_VCEDPM_Enable :
-                       PPSMC_MSG_VCEDPM_Disable);
-}
-
-int polaris10_enable_disable_samu_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable?
-                       PPSMC_MSG_SAMUDPM_Enable :
-                       PPSMC_MSG_SAMUDPM_Disable);
-}
-
-int polaris10_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               data->smc_state_table.UvdBootLevel = 0;
-               if (table_info->mm_dep_table->count > 0)
-                       data->smc_state_table.UvdBootLevel =
-                                       (uint8_t) (table_info->mm_dep_table->count - 1);
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU74_Discrete_DpmTable, UvdBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0x00FFFFFF;
-               mm_boot_level_value |= data->smc_state_table.UvdBootLevel << 24;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_UVDDPM) ||
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_UVDDPM_SetEnabledMask,
-                                       (uint32_t)(1 << data->smc_state_table.UvdBootLevel));
-       }
-
-       return polaris10_enable_disable_uvd_dpm(hwmgr, !bgate);
-}
-
-int polaris10_update_vce_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                               PHM_PlatformCaps_StablePState))
-                       data->smc_state_table.VceBootLevel =
-                               (uint8_t) (table_info->mm_dep_table->count - 1);
-               else
-                       data->smc_state_table.VceBootLevel = 0;
-
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU74_Discrete_DpmTable, VceBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFF00FFFF;
-               mm_boot_level_value |= data->smc_state_table.VceBootLevel << 16;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_VCEDPM_SetEnabledMask,
-                                       (uint32_t)1 << data->smc_state_table.VceBootLevel);
-       }
-
-       polaris10_enable_disable_vce_dpm(hwmgr, !bgate);
-
-       return 0;
-}
-
-int polaris10_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-
-       if (!bgate) {
-               data->smc_state_table.SamuBootLevel = 0;
-               mm_boot_level_offset = data->dpm_table_start +
-                               offsetof(SMU74_Discrete_DpmTable, SamuBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFFFFFF00;
-               mm_boot_level_value |= data->smc_state_table.SamuBootLevel << 0;
-               cgs_write_ind_register(hwmgr->device,
-                               CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SAMUDPM_SetEnabledMask,
-                                       (uint32_t)(1 << data->smc_state_table.SamuBootLevel));
-       }
-
-       return polaris10_enable_disable_samu_dpm(hwmgr, !bgate);
-}
-
-static int polaris10_update_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       int result = 0;
-       uint32_t low_sclk_interrupt_threshold = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkThrottleLowNotification)
-               && (hwmgr->gfx_arbiter.sclk_threshold !=
-                               data->low_sclk_interrupt_threshold)) {
-               data->low_sclk_interrupt_threshold =
-                               hwmgr->gfx_arbiter.sclk_threshold;
-               low_sclk_interrupt_threshold =
-                               data->low_sclk_interrupt_threshold;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
-
-               result = polaris10_copy_bytes_to_smc(
-                               hwmgr->smumgr,
-                               data->dpm_table_start +
-                               offsetof(SMU74_Discrete_DpmTable,
-                                       LowSclkInterruptThreshold),
-                               (uint8_t *)&low_sclk_interrupt_threshold,
-                               sizeof(uint32_t),
-                               data->sram_end);
-       }
-
-       return result;
-}
-
-static int polaris10_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
-               return polaris10_program_memory_timing_parameters(hwmgr);
-
-       return 0;
-}
-
-static int polaris10_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-
-               PP_ASSERT_WITH_CODE(polaris10_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze SCLK DPM when DPM is disabled",
-                               );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                       "Failed to unfreeze SCLK DPM during UnFreezeSclkMclkDPM Function!",
-                       return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
-
-               PP_ASSERT_WITH_CODE(polaris10_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze MCLK DPM when DPM is disabled",
-                               );
-               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                   "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!",
-                   return -1);
-       }
-
-       data->need_update_smu7_dpm_table = 0;
-
-       return 0;
-}
-
-static int polaris10_notify_link_speed_change_after_state_change(
-               struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states =
-                       (const struct phm_set_power_state_input *)input;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_power_state *polaris10_ps =
-                       cast_const_phw_polaris10_power_state(states->pnew_state);
-       uint16_t target_link_speed = polaris10_get_maximum_link_speed(hwmgr, polaris10_ps);
-       uint8_t  request;
-
-       if (data->pspp_notify_required) {
-               if (target_link_speed == PP_PCIEGen3)
-                       request = PCIE_PERF_REQ_GEN3;
-               else if (target_link_speed == PP_PCIEGen2)
-                       request = PCIE_PERF_REQ_GEN2;
-               else
-                       request = PCIE_PERF_REQ_GEN1;
-
-               if (request == PCIE_PERF_REQ_GEN1 &&
-                               phm_get_current_pcie_speed(hwmgr) > 0)
-                       return 0;
-
-               if (acpi_pcie_perf_request(hwmgr->device, request, false)) {
-                       if (PP_PCIEGen2 == target_link_speed)
-                               printk("PSPP request to switch to Gen2 from Gen3 Failed!");
-                       else
-                               printk("PSPP request to switch to Gen1 from Gen2 Failed!");
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_notify_smc_display(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-               (PPSMC_Msg)PPSMC_MSG_SetVBITimeout, data->frame_time_x2);
-       return (smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay) == 0) ?  0 : -EINVAL;
-}
-
-
-
-static int polaris10_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
-{
-       int tmp_result, result = 0;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       tmp_result = polaris10_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to find DPM states clocks in DPM table!",
-                       result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result =
-                       polaris10_request_link_speed_change_before_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to request link speed change before state change!",
-                               result = tmp_result);
-       }
-
-       tmp_result = polaris10_freeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to freeze SCLK MCLK DPM!", result = tmp_result);
-
-       tmp_result = polaris10_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to populate and upload SCLK MCLK DPM levels!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_generate_dpm_level_enable_mask(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to generate DPM level enabled mask!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_update_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to update SCLK threshold!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_program_mem_timing_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to program memory timing parameters!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_notify_smc_display(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to notify smc display settings!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_unfreeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to unfreeze SCLK MCLK DPM!",
-                       result = tmp_result);
-
-       tmp_result = polaris10_upload_dpm_level_enable_mask(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to upload DPM level enabled mask!",
-                       result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result =
-                       polaris10_notify_link_speed_change_after_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                               "Failed to notify link speed change after state change!",
-                               result = tmp_result);
-       }
-       data->apply_optimized_settings = false;
-       return result;
-}
-
-static int polaris10_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_pwm)
-{
-       hwmgr->thermal_controller.
-       advanceFanControlParameters.usMaxFanPWM = us_max_fan_pwm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                       PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm);
-}
-
-
-int polaris10_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display)
-{
-       PPSMC_Msg msg = has_display ? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay;
-
-       return (smum_send_msg_to_smc(hwmgr->smumgr, msg) == 0) ?  0 : -1;
-}
-
-int polaris10_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr)
-{
-       uint32_t num_active_displays = 0;
-       struct cgs_display_info info = {0};
-       info.mode_info = NULL;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       num_active_displays = info.display_count;
-
-       if (num_active_displays > 1)  /* to do && (pHwMgr->pPECI->displayConfiguration.bMultiMonitorInSync != TRUE)) */
-               polaris10_notify_smc_display_change(hwmgr, false);
-
-
-       return 0;
-}
-
-/**
-* Programs the display gap
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always OK
-*/
-int polaris10_program_display_gap(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t num_active_displays = 0;
-       uint32_t display_gap = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
-       uint32_t display_gap2;
-       uint32_t pre_vbi_time_in_us;
-       uint32_t frame_time_in_us;
-       uint32_t ref_clock;
-       uint32_t refresh_rate = 0;
-       struct cgs_display_info info = {0};
-       struct cgs_mode_info mode_info;
-
-       info.mode_info = &mode_info;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-       num_active_displays = info.display_count;
-
-       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL, DISP_GAP, (num_active_displays > 0) ? DISPLAY_GAP_VBLANK_OR_WM : DISPLAY_GAP_IGNORE);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL, display_gap);
-
-       ref_clock = mode_info.ref_clock;
-       refresh_rate = mode_info.refresh_rate;
-
-       if (0 == refresh_rate)
-               refresh_rate = 60;
-
-       frame_time_in_us = 1000000 / refresh_rate;
-
-       pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
-       data->frame_time_x2 = frame_time_in_us * 2 / 100;
-
-       display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU74_SoftRegisters, PreVBlankGap), 0x64);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU74_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us));
-
-
-       return 0;
-}
-
-
-int polaris10_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
-{
-       return polaris10_program_display_gap(hwmgr);
-}
-
-/**
-*  Set maximum target operating fan output RPM
-*
-* @param    hwmgr:  the address of the powerplay hardware manager.
-* @param    usMaxFanRpm:  max operating fan RPM value.
-* @return   The response that came from the SMC.
-*/
-static int polaris10_set_max_fan_rpm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_rpm)
-{
-       hwmgr->thermal_controller.
-       advanceFanControlParameters.usMaxFanRPM = us_max_fan_rpm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                       PPSMC_MSG_SetFanRpmMax, us_max_fan_rpm);
-}
-
-int polaris10_register_internal_thermal_interrupt(struct pp_hwmgr *hwmgr,
-                                       const void *thermal_interrupt_info)
-{
-       return 0;
-}
-
-bool polaris10_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       bool is_update_required = false;
-       struct cgs_display_info info = {0, 0, NULL};
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               is_update_required = true;
-/* TO DO NEED TO GET DEEP SLEEP CLOCK FROM DAL
-       if (phm_cap_enabled(hwmgr->hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
-               cgs_get_min_clock_settings(hwmgr->device, &min_clocks);
-               if (min_clocks.engineClockInSR != data->display_timing.minClockInSR &&
-                       (min_clocks.engineClockInSR >= POLARIS10_MINIMUM_ENGINE_CLOCK ||
-                               data->display_timing.minClockInSR >= POLARIS10_MINIMUM_ENGINE_CLOCK))
-                       is_update_required = true;
-*/
-       return is_update_required;
-}
-
-static inline bool polaris10_are_power_levels_equal(const struct polaris10_performance_level *pl1,
-                                                          const struct polaris10_performance_level *pl2)
-{
-       return ((pl1->memory_clock == pl2->memory_clock) &&
-                 (pl1->engine_clock == pl2->engine_clock) &&
-                 (pl1->pcie_gen == pl2->pcie_gen) &&
-                 (pl1->pcie_lane == pl2->pcie_lane));
-}
-
-int polaris10_check_states_equal(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *pstate1, const struct pp_hw_power_state *pstate2, bool *equal)
-{
-       const struct polaris10_power_state *psa = cast_const_phw_polaris10_power_state(pstate1);
-       const struct polaris10_power_state *psb = cast_const_phw_polaris10_power_state(pstate2);
-       int i;
-
-       if (pstate1 == NULL || pstate2 == NULL || equal == NULL)
-               return -EINVAL;
-
-       /* If the two states don't even have the same number of performance levels they cannot be the same state. */
-       if (psa->performance_level_count != psb->performance_level_count) {
-               *equal = false;
-               return 0;
-       }
-
-       for (i = 0; i < psa->performance_level_count; i++) {
-               if (!polaris10_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
-                       /* If we have found even one performance level pair that is different the states are different. */
-                       *equal = false;
-                       return 0;
-               }
-       }
-
-       /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
-       *equal = ((psa->uvd_clks.vclk == psb->uvd_clks.vclk) && (psa->uvd_clks.dclk == psb->uvd_clks.dclk));
-       *equal &= ((psa->vce_clks.evclk == psb->vce_clks.evclk) && (psa->vce_clks.ecclk == psb->vce_clks.ecclk));
-       *equal &= (psa->sclk_threshold == psb->sclk_threshold);
-
-       return 0;
-}
-
-int polaris10_upload_mc_firmware(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       uint32_t vbios_version;
-
-       /*  Read MC indirect register offset 0x9F bits [3:0] to see if VBIOS has already loaded a full version of MC ucode or not.*/
-
-       phm_get_mc_microcode_version(hwmgr);
-       vbios_version = hwmgr->microcode_version_info.MC & 0xf;
-       /*  Full version of MC ucode has already been loaded. */
-       if (vbios_version == 0) {
-               data->need_long_memory_training = false;
-               return 0;
-       }
-
-       data->need_long_memory_training = false;
-
-/*
- *     PPMCME_FirmwareDescriptorEntry *pfd = NULL;
-       pfd = &tonga_mcmeFirmware;
-       if (0 == PHM_READ_FIELD(hwmgr->device, MC_SEQ_SUP_CNTL, RUN))
-               polaris10_load_mc_microcode(hwmgr, pfd->dpmThreshold,
-                                       pfd->cfgArray, pfd->cfgSize, pfd->ioDebugArray,
-                                       pfd->ioDebugSize, pfd->ucodeArray, pfd->ucodeSize);
-*/
-       return 0;
-}
-
-/**
- * Read clock related registers.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int polaris10_read_clock_registers(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       data->clock_registers.vCG_SPLL_FUNC_CNTL = cgs_read_ind_register(hwmgr->device,
-                                               CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL)
-                                               & CG_SPLL_FUNC_CNTL__SPLL_BYPASS_EN_MASK;
-
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_2 = cgs_read_ind_register(hwmgr->device,
-                                               CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_2)
-                                               & CG_SPLL_FUNC_CNTL_2__SCLK_MUX_SEL_MASK;
-
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_4 = cgs_read_ind_register(hwmgr->device,
-                                               CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_4)
-                                               & CG_SPLL_FUNC_CNTL_4__SPLL_SPARE_MASK;
-
-       return 0;
-}
-
-/**
- * Find out if memory is GDDR5.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int polaris10_get_memory_type(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t temp;
-
-       temp = cgs_read_register(hwmgr->device, mmMC_SEQ_MISC0);
-
-       data->is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE ==
-                       ((temp & MC_SEQ_MISC0_GDDR5_MASK) >>
-                        MC_SEQ_MISC0_GDDR5_SHIFT));
-
-       return 0;
-}
-
-/**
- * Enables Dynamic Power Management by SMC
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int polaris10_enable_acpi_power_management(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       GENERAL_PWRMGT, STATIC_PM_EN, 1);
-
-       return 0;
-}
-
-/**
- * Initialize PowerGating States for different engines
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-static int polaris10_init_power_gate_state(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-       data->samu_power_gated = false;
-
-       return 0;
-}
-
-static int polaris10_init_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       data->low_sclk_interrupt_threshold = 0;
-
-       return 0;
-}
-
-int polaris10_setup_asic_task(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       polaris10_upload_mc_firmware(hwmgr);
-
-       tmp_result = polaris10_read_clock_registers(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to read clock registers!", result = tmp_result);
-
-       tmp_result = polaris10_get_memory_type(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to get memory type!", result = tmp_result);
-
-       tmp_result = polaris10_enable_acpi_power_management(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable ACPI power management!", result = tmp_result);
-
-       tmp_result = polaris10_init_power_gate_state(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to init power gate state!", result = tmp_result);
-
-       tmp_result = phm_get_mc_microcode_version(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to get MC microcode version!", result = tmp_result);
-
-       tmp_result = polaris10_init_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to init sclk threshold!", result = tmp_result);
-
-       return result;
-}
-
-static int polaris10_force_clock_level(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, uint32_t mask)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
-               return -EINVAL;
-
-       switch (type) {
-       case PP_SCLK:
-               if (!data->sclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask & mask);
-               break;
-       case PP_MCLK:
-               if (!data->mclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask & mask);
-               break;
-       case PP_PCIE:
-       {
-               uint32_t tmp = mask & data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-               uint32_t level = 0;
-
-               while (tmp >>= 1)
-                       level++;
-
-               if (!data->pcie_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_PCIeDPM_ForceLevel,
-                                       level);
-               break;
-       }
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static uint16_t polaris10_get_current_pcie_speed(struct pp_hwmgr *hwmgr)
-{
-       uint32_t speedCntl = 0;
-
-       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
-       speedCntl = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__PCIE,
-                       ixPCIE_LC_SPEED_CNTL);
-       return((uint16_t)PHM_GET_FIELD(speedCntl,
-                       PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
-}
-
-static int polaris10_print_clock_levels(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, char *buf)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct polaris10_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct polaris10_single_dpm_table *pcie_table = &(data->dpm_table.pcie_speed_table);
-       int i, now, size = 0;
-       uint32_t clock, pcie_speed;
-
-       switch (type) {
-       case PP_SCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < sclk_table->count; i++) {
-                       if (clock > sclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < sclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, sclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_MCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < mclk_table->count; i++) {
-                       if (clock > mclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < mclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, mclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_PCIE:
-               pcie_speed = polaris10_get_current_pcie_speed(hwmgr);
-               for (i = 0; i < pcie_table->count; i++) {
-                       if (pcie_speed != pcie_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < pcie_table->count; i++)
-                       size += sprintf(buf + size, "%d: %s %s\n", i,
-                                       (pcie_table->dpm_levels[i].value == 0) ? "2.5GB, x8" :
-                                       (pcie_table->dpm_levels[i].value == 1) ? "5.0GB, x16" :
-                                       (pcie_table->dpm_levels[i].value == 2) ? "8.0GB, x16" : "",
-                                       (i == now) ? "*" : "");
-               break;
-       default:
-               break;
-       }
-       return size;
-}
-
-static int polaris10_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-       if (mode) {
-               /* stop auto-manage */
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl))
-                       polaris10_fan_ctrl_stop_smc_fan_control(hwmgr);
-               polaris10_fan_ctrl_set_static_mode(hwmgr, mode);
-       } else
-               /* restart auto-manage */
-               polaris10_fan_ctrl_reset_fan_speed_to_default(hwmgr);
-
-       return 0;
-}
-
-static int polaris10_get_fan_control_mode(struct pp_hwmgr *hwmgr)
-{
-       if (hwmgr->fan_ctrl_is_in_default_mode)
-               return hwmgr->fan_ctrl_default_mode;
-       else
-               return PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, FDO_PWM_MODE);
-}
-
-static int polaris10_get_sclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct polaris10_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       int value;
-
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return value;
-}
-
-static int polaris10_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       struct pp_power_state  *ps;
-       struct polaris10_power_state  *polaris10_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       polaris10_ps = cast_phw_polaris10_power_state(&ps->hardware);
-
-       polaris10_ps->performance_levels[polaris10_ps->performance_level_count - 1].engine_clock =
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
-                       value / 100 +
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return 0;
-}
-
-static int polaris10_get_mclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct polaris10_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       int value;
-
-       value = (mclk_table->dpm_levels[mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return value;
-}
-
-static int polaris10_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct polaris10_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       struct pp_power_state  *ps;
-       struct polaris10_power_state  *polaris10_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       polaris10_ps = cast_phw_polaris10_power_state(&ps->hardware);
-
-       polaris10_ps->performance_levels[polaris10_ps->performance_level_count - 1].memory_clock =
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value *
-                       value / 100 +
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return 0;
-}
-static const struct pp_hwmgr_func polaris10_hwmgr_funcs = {
-       .backend_init = &polaris10_hwmgr_backend_init,
-       .backend_fini = &polaris10_hwmgr_backend_fini,
-       .asic_setup = &polaris10_setup_asic_task,
-       .dynamic_state_management_enable = &polaris10_enable_dpm_tasks,
-       .apply_state_adjust_rules = polaris10_apply_state_adjust_rules,
-       .force_dpm_level = &polaris10_force_dpm_level,
-       .power_state_set = polaris10_set_power_state_tasks,
-       .get_power_state_size = polaris10_get_power_state_size,
-       .get_mclk = polaris10_dpm_get_mclk,
-       .get_sclk = polaris10_dpm_get_sclk,
-       .patch_boot_state = polaris10_dpm_patch_boot_state,
-       .get_pp_table_entry = polaris10_get_pp_table_entry,
-       .get_num_of_pp_table_entries = tonga_get_number_of_powerplay_table_entries,
-       .print_current_perforce_level = polaris10_print_current_perforce_level,
-       .powerdown_uvd = polaris10_phm_powerdown_uvd,
-       .powergate_uvd = polaris10_phm_powergate_uvd,
-       .powergate_vce = polaris10_phm_powergate_vce,
-       .disable_clock_power_gating = polaris10_phm_disable_clock_power_gating,
-       .update_clock_gatings = polaris10_phm_update_clock_gatings,
-       .notify_smc_display_config_after_ps_adjustment = polaris10_notify_smc_display_config_after_ps_adjustment,
-       .display_config_changed = polaris10_display_configuration_changed_task,
-       .set_max_fan_pwm_output = polaris10_set_max_fan_pwm_output,
-       .set_max_fan_rpm_output = polaris10_set_max_fan_rpm_output,
-       .get_temperature = polaris10_thermal_get_temperature,
-       .stop_thermal_controller = polaris10_thermal_stop_thermal_controller,
-       .get_fan_speed_info = polaris10_fan_ctrl_get_fan_speed_info,
-       .get_fan_speed_percent = polaris10_fan_ctrl_get_fan_speed_percent,
-       .set_fan_speed_percent = polaris10_fan_ctrl_set_fan_speed_percent,
-       .reset_fan_speed_to_default = polaris10_fan_ctrl_reset_fan_speed_to_default,
-       .get_fan_speed_rpm = polaris10_fan_ctrl_get_fan_speed_rpm,
-       .set_fan_speed_rpm = polaris10_fan_ctrl_set_fan_speed_rpm,
-       .uninitialize_thermal_controller = polaris10_thermal_ctrl_uninitialize_thermal_controller,
-       .register_internal_thermal_interrupt = polaris10_register_internal_thermal_interrupt,
-       .check_smc_update_required_for_display_configuration = polaris10_check_smc_update_required_for_display_configuration,
-       .check_states_equal = polaris10_check_states_equal,
-       .set_fan_control_mode = polaris10_set_fan_control_mode,
-       .get_fan_control_mode = polaris10_get_fan_control_mode,
-       .force_clock_level = polaris10_force_clock_level,
-       .print_clock_levels = polaris10_print_clock_levels,
-       .enable_per_cu_power_gating = polaris10_phm_enable_per_cu_power_gating,
-       .get_sclk_od = polaris10_get_sclk_od,
-       .set_sclk_od = polaris10_set_sclk_od,
-       .get_mclk_od = polaris10_get_mclk_od,
-       .set_mclk_od = polaris10_set_mclk_od,
-};
-
-int polaris10_hwmgr_init(struct pp_hwmgr *hwmgr)
-{
-       hwmgr->hwmgr_func = &polaris10_hwmgr_funcs;
-       hwmgr->pptable_func = &tonga_pptable_funcs;
-       pp_polaris10_thermal_initialize(hwmgr);
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_hwmgr.h
deleted file mode 100644 (file)
index 33c3394..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright 2015 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 POLARIS10_HWMGR_H
-#define POLARIS10_HWMGR_H
-
-#include "hwmgr.h"
-#include "smu74.h"
-#include "smu74_discrete.h"
-#include "ppatomctrl.h"
-#include "polaris10_ppsmc.h"
-#include "polaris10_powertune.h"
-
-#define POLARIS10_MAX_HARDWARE_POWERLEVELS     2
-
-#define POLARIS10_VOLTAGE_CONTROL_NONE                   0x0
-#define POLARIS10_VOLTAGE_CONTROL_BY_GPIO                0x1
-#define POLARIS10_VOLTAGE_CONTROL_BY_SVID2               0x2
-#define POLARIS10_VOLTAGE_CONTROL_MERGED                 0x3
-
-#define DPMTABLE_OD_UPDATE_SCLK     0x00000001
-#define DPMTABLE_OD_UPDATE_MCLK     0x00000002
-#define DPMTABLE_UPDATE_SCLK        0x00000004
-#define DPMTABLE_UPDATE_MCLK        0x00000008
-
-struct polaris10_performance_level {
-       uint32_t  memory_clock;
-       uint32_t  engine_clock;
-       uint16_t  pcie_gen;
-       uint16_t  pcie_lane;
-};
-
-struct polaris10_uvd_clocks {
-       uint32_t  vclk;
-       uint32_t  dclk;
-};
-
-struct polaris10_vce_clocks {
-       uint32_t  evclk;
-       uint32_t  ecclk;
-};
-
-struct polaris10_power_state {
-       uint32_t                  magic;
-       struct polaris10_uvd_clocks    uvd_clks;
-       struct polaris10_vce_clocks    vce_clks;
-       uint32_t                  sam_clk;
-       uint16_t                  performance_level_count;
-       bool                      dc_compatible;
-       uint32_t                  sclk_threshold;
-       struct polaris10_performance_level  performance_levels[POLARIS10_MAX_HARDWARE_POWERLEVELS];
-};
-
-struct polaris10_dpm_level {
-       bool    enabled;
-       uint32_t        value;
-       uint32_t        param1;
-};
-
-#define POLARIS10_MAX_DEEPSLEEP_DIVIDER_ID 5
-#define MAX_REGULAR_DPM_NUMBER 8
-#define POLARIS10_MINIMUM_ENGINE_CLOCK 2500
-
-struct polaris10_single_dpm_table {
-       uint32_t                count;
-       struct polaris10_dpm_level      dpm_levels[MAX_REGULAR_DPM_NUMBER];
-};
-
-struct polaris10_dpm_table {
-       struct polaris10_single_dpm_table  sclk_table;
-       struct polaris10_single_dpm_table  mclk_table;
-       struct polaris10_single_dpm_table  pcie_speed_table;
-       struct polaris10_single_dpm_table  vddc_table;
-       struct polaris10_single_dpm_table  vddci_table;
-       struct polaris10_single_dpm_table  mvdd_table;
-};
-
-struct polaris10_clock_registers {
-       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  vDLL_CNTL;
-       uint32_t  vMCLK_PWRMGT_CNTL;
-       uint32_t  vMPLL_AD_FUNC_CNTL;
-       uint32_t  vMPLL_DQ_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL_1;
-       uint32_t  vMPLL_FUNC_CNTL_2;
-       uint32_t  vMPLL_SS1;
-       uint32_t  vMPLL_SS2;
-};
-
-#define DISABLE_MC_LOADMICROCODE   1
-#define DISABLE_MC_CFGPROGRAMMING  2
-
-struct polaris10_voltage_smio_registers {
-       uint32_t vS0_VID_LOWER_SMIO_CNTL;
-};
-
-#define POLARIS10_MAX_LEAKAGE_COUNT  8
-
-struct polaris10_leakage_voltage {
-       uint16_t  count;
-       uint16_t  leakage_id[POLARIS10_MAX_LEAKAGE_COUNT];
-       uint16_t  actual_voltage[POLARIS10_MAX_LEAKAGE_COUNT];
-};
-
-struct polaris10_vbios_boot_state {
-       uint16_t    mvdd_bootup_value;
-       uint16_t    vddc_bootup_value;
-       uint16_t    vddci_bootup_value;
-       uint32_t    sclk_bootup_value;
-       uint32_t    mclk_bootup_value;
-       uint16_t    pcie_gen_bootup_value;
-       uint16_t    pcie_lane_bootup_value;
-};
-
-/* Ultra Low Voltage parameter structure */
-struct polaris10_ulv_parm {
-       bool                           ulv_supported;
-       uint32_t                       cg_ulv_parameter;
-       uint32_t                       ulv_volt_change_delay;
-       struct polaris10_performance_level  ulv_power_level;
-};
-
-struct polaris10_display_timing {
-       uint32_t  min_clock_in_sr;
-       uint32_t  num_existing_displays;
-};
-
-struct polaris10_dpmlevel_enable_mask {
-       uint32_t  uvd_dpm_enable_mask;
-       uint32_t  vce_dpm_enable_mask;
-       uint32_t  acp_dpm_enable_mask;
-       uint32_t  samu_dpm_enable_mask;
-       uint32_t  sclk_dpm_enable_mask;
-       uint32_t  mclk_dpm_enable_mask;
-       uint32_t  pcie_dpm_enable_mask;
-};
-
-struct polaris10_pcie_perf_range {
-       uint16_t  max;
-       uint16_t  min;
-};
-struct polaris10_range_table {
-       uint32_t trans_lower_frequency; /* in 10khz */
-       uint32_t trans_upper_frequency;
-};
-
-struct polaris10_hwmgr {
-       struct polaris10_dpm_table                      dpm_table;
-       struct polaris10_dpm_table                      golden_dpm_table;
-       SMU74_Discrete_DpmTable                         smc_state_table;
-       struct SMU74_Discrete_Ulv            ulv_setting;
-
-       struct polaris10_range_table                range_table[NUM_SCLK_RANGE];
-       uint32_t                                                voting_rights_clients0;
-       uint32_t                                                voting_rights_clients1;
-       uint32_t                                                voting_rights_clients2;
-       uint32_t                                                voting_rights_clients3;
-       uint32_t                                                voting_rights_clients4;
-       uint32_t                                                voting_rights_clients5;
-       uint32_t                                                voting_rights_clients6;
-       uint32_t                                                voting_rights_clients7;
-       uint32_t                                                static_screen_threshold_unit;
-       uint32_t                                                static_screen_threshold;
-       uint32_t                                                voltage_control;
-       uint32_t                                                vddc_vddci_delta;
-
-       uint32_t                                                active_auto_throttle_sources;
-
-       struct polaris10_clock_registers            clock_registers;
-       struct polaris10_voltage_smio_registers      voltage_smio_registers;
-
-       bool                           is_memory_gddr5;
-       uint16_t                       acpi_vddc;
-       bool                           pspp_notify_required;
-       uint16_t                       force_pcie_gen;
-       uint16_t                       acpi_pcie_gen;
-       uint32_t                       pcie_gen_cap;
-       uint32_t                       pcie_lane_cap;
-       uint32_t                       pcie_spc_cap;
-       struct polaris10_leakage_voltage          vddc_leakage;
-       struct polaris10_leakage_voltage          Vddci_leakage;
-
-       uint32_t                             mvdd_control;
-       uint32_t                             vddc_mask_low;
-       uint32_t                             mvdd_mask_low;
-       uint16_t                            max_vddc_in_pptable;
-       uint16_t                            min_vddc_in_pptable;
-       uint16_t                            max_vddci_in_pptable;
-       uint16_t                            min_vddci_in_pptable;
-       uint32_t                             mclk_strobe_mode_threshold;
-       uint32_t                             mclk_stutter_mode_threshold;
-       uint32_t                             mclk_edc_enable_threshold;
-       uint32_t                             mclk_edcwr_enable_threshold;
-       bool                                is_uvd_enabled;
-       struct polaris10_vbios_boot_state        vbios_boot_state;
-
-       bool                           pcie_performance_request;
-       bool                           battery_state;
-       bool                           is_tlu_enabled;
-
-       /* ---- SMC SRAM Address of firmware header tables ---- */
-       uint32_t                             sram_end;
-       uint32_t                             dpm_table_start;
-       uint32_t                             soft_regs_start;
-       uint32_t                             mc_reg_table_start;
-       uint32_t                             fan_table_start;
-       uint32_t                             arb_table_start;
-
-       /* ---- Stuff originally coming from Evergreen ---- */
-       uint32_t                             vddci_control;
-       struct pp_atomctrl_voltage_table     vddc_voltage_table;
-       struct pp_atomctrl_voltage_table     vddci_voltage_table;
-       struct pp_atomctrl_voltage_table     mvdd_voltage_table;
-
-       uint32_t                             mgcg_cgtt_local2;
-       uint32_t                             mgcg_cgtt_local3;
-       uint32_t                             gpio_debug;
-       uint32_t                             mc_micro_code_feature;
-       uint32_t                             highest_mclk;
-       uint16_t                             acpi_vddci;
-       uint8_t                              mvdd_high_index;
-       uint8_t                              mvdd_low_index;
-       bool                                 dll_default_on;
-       bool                                 performance_request_registered;
-
-       /* ---- Low Power Features ---- */
-       struct polaris10_ulv_parm                 ulv;
-
-       /* ---- CAC Stuff ---- */
-       uint32_t                       cac_table_start;
-       bool                           cac_configuration_required;
-       bool                           driver_calculate_cac_leakage;
-       bool                           cac_enabled;
-
-       /* ---- DPM2 Parameters ---- */
-       uint32_t                       power_containment_features;
-       bool                           enable_dte_feature;
-       bool                           enable_tdc_limit_feature;
-       bool                           enable_pkg_pwr_tracking_feature;
-       bool                           disable_uvd_power_tune_feature;
-       const struct polaris10_pt_defaults       *power_tune_defaults;
-       struct SMU74_Discrete_PmFuses  power_tune_table;
-       uint32_t                       dte_tj_offset;
-       uint32_t                       fast_watermark_threshold;
-
-       /* ---- Phase Shedding ---- */
-       bool                           vddc_phase_shed_control;
-
-       /* ---- DI/DT ---- */
-       struct polaris10_display_timing        display_timing;
-       uint32_t                      bif_sclk_table[SMU74_MAX_LEVELS_LINK];
-
-       /* ---- Thermal Temperature Setting ---- */
-       struct polaris10_dpmlevel_enable_mask     dpm_level_enable_mask;
-       uint32_t                                  need_update_smu7_dpm_table;
-       uint32_t                                  sclk_dpm_key_disabled;
-       uint32_t                                  mclk_dpm_key_disabled;
-       uint32_t                                  pcie_dpm_key_disabled;
-       uint32_t                                  min_engine_clocks;
-       struct polaris10_pcie_perf_range          pcie_gen_performance;
-       struct polaris10_pcie_perf_range          pcie_lane_performance;
-       struct polaris10_pcie_perf_range          pcie_gen_power_saving;
-       struct polaris10_pcie_perf_range          pcie_lane_power_saving;
-       bool                                      use_pcie_performance_levels;
-       bool                                      use_pcie_power_saving_levels;
-       uint32_t                                  activity_target[SMU74_MAX_LEVELS_GRAPHICS];
-       uint32_t                                  mclk_activity_target;
-       uint32_t                                  mclk_dpm0_activity_target;
-       uint32_t                                  low_sclk_interrupt_threshold;
-       uint32_t                                  last_mclk_dpm_enable_mask;
-       bool                                      uvd_enabled;
-
-       /* ---- Power Gating States ---- */
-       bool                           uvd_power_gated;
-       bool                           vce_power_gated;
-       bool                           samu_power_gated;
-       bool                           need_long_memory_training;
-
-       /* Application power optimization parameters */
-       bool                               update_up_hyst;
-       bool                               update_down_hyst;
-       uint32_t                           down_hyst;
-       uint32_t                           up_hyst;
-       uint32_t disable_dpm_mask;
-       bool apply_optimized_settings;
-       uint32_t                              avfs_vdroop_override_setting;
-       bool                                  apply_avfs_cks_off_voltage;
-       uint32_t                              frame_time_x2;
-};
-
-/* To convert to Q8.8 format for firmware */
-#define POLARIS10_Q88_FORMAT_CONVERSION_UNIT             256
-
-enum Polaris10_I2CLineID {
-       Polaris10_I2CLineID_DDC1 = 0x90,
-       Polaris10_I2CLineID_DDC2 = 0x91,
-       Polaris10_I2CLineID_DDC3 = 0x92,
-       Polaris10_I2CLineID_DDC4 = 0x93,
-       Polaris10_I2CLineID_DDC5 = 0x94,
-       Polaris10_I2CLineID_DDC6 = 0x95,
-       Polaris10_I2CLineID_SCLSDA = 0x96,
-       Polaris10_I2CLineID_DDCVGA = 0x97
-};
-
-#define POLARIS10_I2C_DDC1DATA          0
-#define POLARIS10_I2C_DDC1CLK           1
-#define POLARIS10_I2C_DDC2DATA          2
-#define POLARIS10_I2C_DDC2CLK           3
-#define POLARIS10_I2C_DDC3DATA          4
-#define POLARIS10_I2C_DDC3CLK           5
-#define POLARIS10_I2C_SDA               40
-#define POLARIS10_I2C_SCL               41
-#define POLARIS10_I2C_DDC4DATA          65
-#define POLARIS10_I2C_DDC4CLK           66
-#define POLARIS10_I2C_DDC5DATA          0x48
-#define POLARIS10_I2C_DDC5CLK           0x49
-#define POLARIS10_I2C_DDC6DATA          0x4a
-#define POLARIS10_I2C_DDC6CLK           0x4b
-#define POLARIS10_I2C_DDCVGADATA        0x4c
-#define POLARIS10_I2C_DDCVGACLK         0x4d
-
-#define POLARIS10_UNUSED_GPIO_PIN       0x7F
-
-int polaris10_hwmgr_init(struct pp_hwmgr *hwmgr);
-
-int polaris10_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int polaris10_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable);
-int polaris10_update_vce_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.c
deleted file mode 100644 (file)
index b9cb240..0000000
+++ /dev/null
@@ -1,988 +0,0 @@
-/*
- * Copyright 2015 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 "hwmgr.h"
-#include "smumgr.h"
-#include "polaris10_hwmgr.h"
-#include "polaris10_powertune.h"
-#include "polaris10_smumgr.h"
-#include "smu74_discrete.h"
-#include "pp_debug.h"
-#include "gca/gfx_8_0_d.h"
-#include "gca/gfx_8_0_sh_mask.h"
-#include "oss/oss_3_0_sh_mask.h"
-
-#define VOLTAGE_SCALE  4
-#define POWERTUNE_DEFAULT_SET_MAX    1
-
-uint32_t DIDTBlock_Info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK;
-
-struct polaris10_pt_config_reg GCCACConfig_Polaris10[] = {
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- *      Offset                             Mask                                                Shift                                               Value       Type
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00060013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00860013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01060013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01860013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02060013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02860013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03060013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03860013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x04060013, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x000E0013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x008E0013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x010E0013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x018E0013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x020E0013, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00100013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00900013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01100013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01900013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02100013, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02900013, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   0xFFFFFFFF  }
-};
-
-struct polaris10_pt_config_reg GCCACConfig_Polaris11[] = {
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- *      Offset                             Mask                                                Shift                                               Value       Type
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00060011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00860011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01060011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01860011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02060011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02860011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03060011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03860011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x04060011, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x000E0011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x008E0011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x010E0011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x018E0011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x020E0011, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00100011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00900011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01100011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01900011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02100011, POLARIS10_CONFIGREG_GC_CAC_IND },
-       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02900011, POLARIS10_CONFIGREG_GC_CAC_IND },
-
-       {   0xFFFFFFFF  }
-};
-
-struct polaris10_pt_config_reg DIDTConfig_Polaris10[] = {
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- *      Offset                             Mask                                                Shift                                               Value       Type
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT,                  0x0073,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT,                  0x00ab,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0084,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT,                  0x005a,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0067,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT,                  0x0084,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0027,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0046,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT,                 0x00aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MIN_POWER_MASK,                      DIDT_SQ_CTRL1__MIN_POWER__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MAX_POWER_MASK,                      DIDT_SQ_CTRL1__MAX_POWER__SHIFT,                    0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__UNUSED_0_MASK,                    DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3853,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_0_MASK,                       DIDT_SQ_CTRL2__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x005a,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_1_MASK,                       DIDT_SQ_CTRL2__UNUSED_1__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_2_MASK,                       DIDT_SQ_CTRL2__UNUSED_2__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__UNUSED_0_MASK,                  DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x3853,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x3153,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__PHASE_OFFSET_MASK,                   DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__UNUSED_0_MASK,                       DIDT_SQ_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT,                  0x000a,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT,                  0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0017,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT,                  0x002f,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0046,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT,                  0x005d,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MIN_POWER_MASK,                      DIDT_TD_CTRL1__MIN_POWER__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MAX_POWER_MASK,                      DIDT_TD_CTRL1__MAX_POWER__SHIFT,                    0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__UNUSED_0_MASK,                    DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0x00ff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3fff,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_0_MASK,                       DIDT_TD_CTRL2__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x000f,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_1_MASK,                       DIDT_TD_CTRL2__UNUSED_1__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_2_MASK,                       DIDT_TD_CTRL2__UNUSED_2__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__UNUSED_0_MASK,                  DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x0dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x0dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0009,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0009,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__UNUSED_0_MASK,                       DIDT_TD_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT,                 0x0004,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT,                 0x0037,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT,                 0x00ff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT,                 0x0054,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MIN_POWER_MASK,                     DIDT_TCP_CTRL1__MIN_POWER__SHIFT,                   0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MAX_POWER_MASK,                     DIDT_TCP_CTRL1__MAX_POWER__SHIFT,                   0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__UNUSED_0_MASK,                   DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK,              DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT,            0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK,               DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT,             0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_0_MASK,                      DIDT_TCP_CTRL2__UNUSED_0__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,      DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,    0x0032,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_1_MASK,                      DIDT_TCP_CTRL2__UNUSED_1__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,      DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_2_MASK,                      DIDT_TCP_CTRL2__UNUSED_2__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,   DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,  DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__UNUSED_0_MASK,                 DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,      DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,    0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,    0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK,                DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT,              0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__UNUSED_0_MASK,                       DIDT_TCP_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   0xFFFFFFFF  }
-};
-
-struct polaris10_pt_config_reg DIDTConfig_Polaris11[] = {
-/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- *      Offset                             Mask                                                Shift                                               Value       Type
- * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- */
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT,                  0x0073,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT,                  0x00ab,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0084,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT,                  0x005a,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0067,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT,                  0x0084,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0027,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0046,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT,                 0x00aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MIN_POWER_MASK,                      DIDT_SQ_CTRL1__MIN_POWER__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MAX_POWER_MASK,                      DIDT_SQ_CTRL1__MAX_POWER__SHIFT,                    0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__UNUSED_0_MASK,                    DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3853,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_0_MASK,                       DIDT_SQ_CTRL2__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x005a,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_1_MASK,                       DIDT_SQ_CTRL2__UNUSED_1__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_2_MASK,                       DIDT_SQ_CTRL2__UNUSED_2__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__UNUSED_0_MASK,                  DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x3853,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x3153,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__PHASE_OFFSET_MASK,                   DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__UNUSED_0_MASK,                       DIDT_SQ_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT,                  0x000a,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT,                  0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0017,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT,                  0x002f,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0046,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT,                  0x005d,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MIN_POWER_MASK,                      DIDT_TD_CTRL1__MIN_POWER__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MAX_POWER_MASK,                      DIDT_TD_CTRL1__MAX_POWER__SHIFT,                    0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__UNUSED_0_MASK,                    DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0x00ff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3fff,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_0_MASK,                       DIDT_TD_CTRL2__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x000f,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_1_MASK,                       DIDT_TD_CTRL2__UNUSED_1__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_2_MASK,                       DIDT_TD_CTRL2__UNUSED_2__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__UNUSED_0_MASK,                  DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x0dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x0dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0008,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0008,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__UNUSED_0_MASK,                       DIDT_TD_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT,                 0x0004,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT,                 0x0037,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT,                 0x00ff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT,                 0x0054,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MIN_POWER_MASK,                     DIDT_TCP_CTRL1__MIN_POWER__SHIFT,                   0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MAX_POWER_MASK,                     DIDT_TCP_CTRL1__MAX_POWER__SHIFT,                   0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__UNUSED_0_MASK,                   DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK,              DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT,            0xffff,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK,               DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT,             0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_0_MASK,                      DIDT_TCP_CTRL2__UNUSED_0__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,      DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,    0x0032,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_1_MASK,                      DIDT_TCP_CTRL2__UNUSED_1__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,      DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_2_MASK,                      DIDT_TCP_CTRL2__UNUSED_2__SHIFT,                    0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,   DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,  DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__UNUSED_0_MASK,                 DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT,               0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,      DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,    0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,    0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,    0x3dde,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK,                DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT,              0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__UNUSED_0_MASK,                       DIDT_TCP_CTRL0__UNUSED_0__SHIFT,                     0x0000,     POLARIS10_CONFIGREG_DIDT_IND },
-       {   0xFFFFFFFF  }
-};
-
-static const struct polaris10_pt_defaults polaris10_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
-       /* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
-        * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT */
-       { 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
-       { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61},
-       { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 } },
-};
-
-void polaris10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *polaris10_hwmgr = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct  phm_ppt_v1_information *table_info =
-                       (struct  phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (table_info &&
-                       table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
-                       table_info->cac_dtp_table->usPowerTuneDataSetID)
-               polaris10_hwmgr->power_tune_defaults =
-                               &polaris10_power_tune_data_set_array
-                               [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
-       else
-               polaris10_hwmgr->power_tune_defaults = &polaris10_power_tune_data_set_array[0];
-
-}
-
-static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
-{
-       uint32_t tmp;
-       tmp = raw_setting * 4096 / 100;
-       return (uint16_t)tmp;
-}
-
-int polaris10_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_pt_defaults *defaults = data->power_tune_defaults;
-       SMU74_Discrete_DpmTable  *dpm_table = &(data->smc_state_table);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
-       struct pp_advance_fan_control_parameters *fan_table=
-                       &hwmgr->thermal_controller.advanceFanControlParameters;
-       int i, j, k;
-       const uint16_t *pdef1;
-       const uint16_t *pdef2;
-
-       dpm_table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
-       dpm_table->TargetTdp  = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
-
-       PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
-                               "Target Operating Temp is out of Range!",
-                               );
-
-       dpm_table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTargetOperatingTemp * 256);
-       dpm_table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
-                       cac_dtp_table->usTemperatureLimitHotspot * 256);
-       dpm_table->FanGainEdge = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainEdge));
-       dpm_table->FanGainHotspot = PP_HOST_TO_SMC_US(
-                       scale_fan_gain_settings(fan_table->usFanGainHotspot));
-
-       pdef1 = defaults->BAPMTI_R;
-       pdef2 = defaults->BAPMTI_RC;
-
-       for (i = 0; i < SMU74_DTE_ITERATIONS; i++) {
-               for (j = 0; j < SMU74_DTE_SOURCES; j++) {
-                       for (k = 0; k < SMU74_DTE_SINKS; k++) {
-                               dpm_table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*pdef1);
-                               dpm_table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*pdef2);
-                               pdef1++;
-                               pdef2++;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int polaris10_populate_svi_load_line(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_pt_defaults *defaults = data->power_tune_defaults;
-
-       data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
-       data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
-       data->power_tune_table.SviLoadLineTrimVddC = 3;
-       data->power_tune_table.SviLoadLineOffsetVddC = 0;
-
-       return 0;
-}
-
-static int polaris10_populate_tdc_limit(struct pp_hwmgr *hwmgr)
-{
-       uint16_t tdc_limit;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       const struct polaris10_pt_defaults *defaults = data->power_tune_defaults;
-
-       tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
-       data->power_tune_table.TDC_VDDC_PkgLimit =
-                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
-       data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
-                       defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
-       data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
-
-       return 0;
-}
-
-static int polaris10_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       const struct polaris10_pt_defaults *defaults = data->power_tune_defaults;
-       uint32_t temp;
-
-       if (polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                       fuse_table_offset +
-                       offsetof(SMU74_Discrete_PmFuses, TdcWaterfallCtl),
-                       (uint32_t *)&temp, data->sram_end))
-               PP_ASSERT_WITH_CODE(false,
-                               "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
-                               return -EINVAL);
-       else {
-               data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
-               data->power_tune_table.LPMLTemperatureMin =
-                               (uint8_t)((temp >> 16) & 0xff);
-               data->power_tune_table.LPMLTemperatureMax =
-                               (uint8_t)((temp >> 8) & 0xff);
-               data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
-       }
-       return 0;
-}
-
-static int polaris10_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
-{
-       int i;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* Currently not used. Set all to zero. */
-       for (i = 0; i < 16; i++)
-               data->power_tune_table.LPMLTemperatureScaler[i] = 0;
-
-       return 0;
-}
-
-static int polaris10_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if ((hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity & (1 << 15))
-               || 0 == hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity)
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
-                       hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity;
-
-       data->power_tune_table.FuzzyFan_PwmSetDelta = PP_HOST_TO_SMC_US(
-                               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity);
-       return 0;
-}
-
-static int polaris10_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
-{
-       int i;
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       /* Currently not used. Set all to zero. */
-       for (i = 0; i < 16; i++)
-               data->power_tune_table.GnbLPML[i] = 0;
-
-       return 0;
-}
-
-static int polaris10_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
-{
-       return 0;
-}
-
-static int polaris10_enable_didt(struct pp_hwmgr *hwmgr, const bool enable)
-{
-
-       uint32_t en = enable ? 1 : 0;
-       int32_t result = 0;
-       uint32_t data;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping)) {
-               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0);
-               data &= ~DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK;
-               data |= ((en << DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0, data);
-               DIDTBlock_Info &= ~SQ_Enable_MASK;
-               DIDTBlock_Info |= en << SQ_Enable_SHIFT;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping)) {
-               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0);
-               data &= ~DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK;
-               data |= ((en << DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0, data);
-               DIDTBlock_Info &= ~DB_Enable_MASK;
-               DIDTBlock_Info |= en << DB_Enable_SHIFT;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping)) {
-               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0);
-               data &= ~DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK;
-               data |= ((en << DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0, data);
-               DIDTBlock_Info &= ~TD_Enable_MASK;
-               DIDTBlock_Info |= en << TD_Enable_SHIFT;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
-               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0);
-               data &= ~DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK;
-               data |= ((en << DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK);
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0, data);
-               DIDTBlock_Info &= ~TCP_Enable_MASK;
-               DIDTBlock_Info |= en << TCP_Enable_SHIFT;
-       }
-
-       if (enable)
-               result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_Didt_Block_Function, DIDTBlock_Info);
-
-       return result;
-}
-
-static int polaris10_program_pt_config_registers(struct pp_hwmgr *hwmgr,
-                               struct polaris10_pt_config_reg *cac_config_regs)
-{
-       struct polaris10_pt_config_reg *config_regs = cac_config_regs;
-       uint32_t cache = 0;
-       uint32_t data = 0;
-
-       PP_ASSERT_WITH_CODE((config_regs != NULL), "Invalid config register table.", return -EINVAL);
-
-       while (config_regs->offset != 0xFFFFFFFF) {
-               if (config_regs->type == POLARIS10_CONFIGREG_CACHE)
-                       cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
-               else {
-                       switch (config_regs->type) {
-                       case POLARIS10_CONFIGREG_SMC_IND:
-                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset);
-                               break;
-
-                       case POLARIS10_CONFIGREG_DIDT_IND:
-                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset);
-                               break;
-
-                       case POLARIS10_CONFIGREG_GC_CAC_IND:
-                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset);
-                               break;
-
-                       default:
-                               data = cgs_read_register(hwmgr->device, config_regs->offset);
-                               break;
-                       }
-
-                       data &= ~config_regs->mask;
-                       data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
-                       data |= cache;
-
-                       switch (config_regs->type) {
-                       case POLARIS10_CONFIGREG_SMC_IND:
-                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset, data);
-                               break;
-
-                       case POLARIS10_CONFIGREG_DIDT_IND:
-                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset, data);
-                               break;
-
-                       case POLARIS10_CONFIGREG_GC_CAC_IND:
-                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset, data);
-                               break;
-
-                       default:
-                               cgs_write_register(hwmgr->device, config_regs->offset, data);
-                               break;
-                       }
-                       cache = 0;
-               }
-
-               config_regs++;
-       }
-
-       return 0;
-}
-
-int polaris10_enable_didt_config(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       uint32_t num_se = 0;
-       uint32_t count, value, value2;
-       struct cgs_system_info sys_info = {0};
-
-       sys_info.size = sizeof(struct cgs_system_info);
-       sys_info.info_id = CGS_SYSTEM_INFO_GFX_SE_INFO;
-       result = cgs_query_system_info(hwmgr->device, &sys_info);
-
-
-       if (result == 0)
-               num_se = sys_info.value;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
-
-               /* TO DO Pre DIDT disable clock gating */
-               value = 0;
-               value2 = cgs_read_register(hwmgr->device, mmGRBM_GFX_INDEX);
-               for (count = 0; count < num_se; count++) {
-                       value = SYS_GRBM_GFX_INDEX_DATA__INSTANCE_BROADCAST_WRITES_MASK
-                               | SYS_GRBM_GFX_INDEX_DATA__SH_BROADCAST_WRITES_MASK
-                               | (count << SYS_GRBM_GFX_INDEX_DATA__SE_INDEX__SHIFT);
-                       cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value);
-
-                       if (hwmgr->chip_id == CHIP_POLARIS10) {
-                               result = polaris10_program_pt_config_registers(hwmgr, GCCACConfig_Polaris10);
-                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
-                               result = polaris10_program_pt_config_registers(hwmgr, DIDTConfig_Polaris10);
-                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
-                       } else if (hwmgr->chip_id == CHIP_POLARIS11) {
-                               result = polaris10_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11);
-                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
-                               result = polaris10_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11);
-                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
-                       }
-               }
-               cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value2);
-
-               result = polaris10_enable_didt(hwmgr, true);
-               PP_ASSERT_WITH_CODE((result == 0), "EnableDiDt failed.", return result);
-
-               /* TO DO Post DIDT enable clock gating */
-       }
-
-       return 0;
-}
-
-int polaris10_disable_didt_config(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) ||
-               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
-               /* TO DO Pre DIDT disable clock gating */
-
-               result = polaris10_enable_didt(hwmgr, false);
-               PP_ASSERT_WITH_CODE((result == 0), "Post DIDT enable clock gating failed.", return result);
-               /* TO DO Post DIDT enable clock gating */
-       }
-
-       return 0;
-}
-
-
-static int polaris10_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint16_t hi_sidd = data->power_tune_table.BapmVddCBaseLeakageHiSidd;
-       uint16_t lo_sidd = data->power_tune_table.BapmVddCBaseLeakageLoSidd;
-       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
-
-       hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
-       lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
-
-       data->power_tune_table.BapmVddCBaseLeakageHiSidd =
-                       CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
-       data->power_tune_table.BapmVddCBaseLeakageLoSidd =
-                       CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
-
-       return 0;
-}
-
-int polaris10_populate_pm_fuses(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       uint32_t pm_fuse_table_offset;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-               if (polaris10_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU7_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU74_Firmware_Header, PmFuseTable),
-                               &pm_fuse_table_offset, data->sram_end))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to get pm_fuse_table_offset Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_populate_svi_load_line(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate SviLoadLine Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_populate_tdc_limit(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate TDCLimit Failed!", return -EINVAL);
-
-               if (polaris10_populate_dw8(hwmgr, pm_fuse_table_offset))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate TdcWaterfallCtl, "
-                                       "LPMLTemperature Min and Max Failed!",
-                                       return -EINVAL);
-
-               if (0 != polaris10_populate_temperature_scaler(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate LPMLTemperatureScaler Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_populate_fuzzy_fan(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate Fuzzy Fan Control parameters Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_populate_gnb_lpml(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate GnbLPML Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate GnbLPML Min and Max Vid Failed!",
-                                       return -EINVAL);
-
-               if (polaris10_populate_bapm_vddc_base_leakage_sidd(hwmgr))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
-                                       "Sidd Failed!", return -EINVAL);
-
-               if (polaris10_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
-                               (uint8_t *)&data->power_tune_table,
-                               (sizeof(struct SMU74_Discrete_PmFuses) - 92), data->sram_end))
-                       PP_ASSERT_WITH_CODE(false,
-                                       "Attempt to download PmFuseTable Failed!",
-                                       return -EINVAL);
-       }
-       return 0;
-}
-
-int polaris10_enable_smc_cac(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CAC)) {
-               int smc_result;
-               smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                               (uint16_t)(PPSMC_MSG_EnableCac));
-               PP_ASSERT_WITH_CODE((0 == smc_result),
-                               "Failed to enable CAC in SMC.", result = -1);
-
-               data->cac_enabled = (0 == smc_result) ? true : false;
-       }
-       return result;
-}
-
-int polaris10_disable_smc_cac(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CAC) && data->cac_enabled) {
-               int smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                               (uint16_t)(PPSMC_MSG_DisableCac));
-               PP_ASSERT_WITH_CODE((smc_result == 0),
-                               "Failed to disable CAC in SMC.", result = -1);
-
-               data->cac_enabled = false;
-       }
-       return result;
-}
-
-int polaris10_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (data->power_containment_features &
-                       POWERCONTAINMENT_FEATURE_PkgPwrLimit)
-               return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_PkgPwrSetLimit, n);
-       return 0;
-}
-
-static int polaris10_set_overdriver_target_tdp(struct pp_hwmgr *pHwMgr, uint32_t target_tdp)
-{
-       return smum_send_msg_to_smc_with_parameter(pHwMgr->smumgr,
-                       PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
-}
-
-int polaris10_enable_power_containment(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int smc_result;
-       int result = 0;
-
-       data->power_containment_features = 0;
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-
-               if (data->enable_tdc_limit_feature) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_TDCLimitEnable));
-                       PP_ASSERT_WITH_CODE((0 == smc_result),
-                                       "Failed to enable TDCLimit in SMC.", result = -1;);
-                       if (0 == smc_result)
-                               data->power_containment_features |=
-                                               POWERCONTAINMENT_FEATURE_TDCLimit;
-               }
-
-               if (data->enable_pkg_pwr_tracking_feature) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitEnable));
-                       PP_ASSERT_WITH_CODE((0 == smc_result),
-                                       "Failed to enable PkgPwrTracking in SMC.", result = -1;);
-                       if (0 == smc_result) {
-                               struct phm_cac_tdp_table *cac_table =
-                                               table_info->cac_dtp_table;
-                               uint32_t default_limit =
-                                       (uint32_t)(cac_table->usMaximumPowerDeliveryLimit * 256);
-
-                               data->power_containment_features |=
-                                               POWERCONTAINMENT_FEATURE_PkgPwrLimit;
-
-                               if (polaris10_set_power_limit(hwmgr, default_limit))
-                                       printk(KERN_ERR "Failed to set Default Power Limit in SMC!");
-                       }
-               }
-       }
-       return result;
-}
-
-int polaris10_disable_power_containment(struct pp_hwmgr *hwmgr)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment) &&
-                       data->power_containment_features) {
-               int smc_result;
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_TDCLimit) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_TDCLimitDisable));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable TDCLimit in SMC.",
-                                       result = smc_result);
-               }
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_DTE) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_DisableDTE));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable DTE in SMC.",
-                                       result = smc_result);
-               }
-
-               if (data->power_containment_features &
-                               POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
-                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
-                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitDisable));
-                       PP_ASSERT_WITH_CODE((smc_result == 0),
-                                       "Failed to disable PkgPwrTracking in SMC.",
-                                       result = smc_result);
-               }
-               data->power_containment_features = 0;
-       }
-
-       return result;
-}
-
-int polaris10_power_control_set_level(struct pp_hwmgr *hwmgr)
-{
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
-       int adjust_percent, target_tdp;
-       int result = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerContainment)) {
-               /* adjustment percentage has already been validated */
-               adjust_percent = hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
-                               hwmgr->platform_descriptor.TDPAdjustment :
-                               (-1 * hwmgr->platform_descriptor.TDPAdjustment);
-               /* SMC requested that target_tdp to be 7 bit fraction in DPM table
-                * but message to be 8 bit fraction for messages
-                */
-               target_tdp = ((100 + adjust_percent) * (int)(cac_table->usTDP * 256)) / 100;
-               result = polaris10_set_overdriver_target_tdp(hwmgr, (uint32_t)target_tdp);
-       }
-
-       return result;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_powertune.h
deleted file mode 100644 (file)
index bc78e28..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2015 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 POLARIS10_POWERTUNE_H
-#define POLARIS10_POWERTUNE_H
-
-enum polaris10_pt_config_reg_type {
-       POLARIS10_CONFIGREG_MMR = 0,
-       POLARIS10_CONFIGREG_SMC_IND,
-       POLARIS10_CONFIGREG_DIDT_IND,
-       POLARIS10_CONFIGREG_GC_CAC_IND,
-       POLARIS10_CONFIGREG_CACHE,
-       POLARIS10_CONFIGREG_MAX
-};
-
-#define DIDT_SQ_CTRL0__UNUSED_0_MASK    0xfffc0000
-#define DIDT_SQ_CTRL0__UNUSED_0__SHIFT  0x12
-#define DIDT_TD_CTRL0__UNUSED_0_MASK    0xfffc0000
-#define DIDT_TD_CTRL0__UNUSED_0__SHIFT  0x12
-#define DIDT_TCP_CTRL0__UNUSED_0_MASK   0xfffc0000
-#define DIDT_TCP_CTRL0__UNUSED_0__SHIFT 0x12
-#define DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK                 0xc0000000
-#define DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001e
-#define DIDT_TD_TUNING_CTRL__UNUSED_0_MASK                 0xc0000000
-#define DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001e
-#define DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK                0xc0000000
-#define DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT              0x0000001e
-
-/* PowerContainment Features */
-#define POWERCONTAINMENT_FEATURE_DTE             0x00000001
-#define POWERCONTAINMENT_FEATURE_TDCLimit        0x00000002
-#define POWERCONTAINMENT_FEATURE_PkgPwrLimit     0x00000004
-
-#define ixGC_CAC_CNTL 0x0000
-#define ixDIDT_SQ_STALL_CTRL 0x0004
-#define  ixDIDT_SQ_TUNING_CTRL 0x0005
-#define ixDIDT_TD_STALL_CTRL 0x0044
-#define ixDIDT_TD_TUNING_CTRL 0x0045
-#define ixDIDT_TCP_STALL_CTRL 0x0064
-#define ixDIDT_TCP_TUNING_CTRL 0x0065
-
-struct polaris10_pt_config_reg {
-       uint32_t                           offset;
-       uint32_t                           mask;
-       uint32_t                           shift;
-       uint32_t                           value;
-       enum polaris10_pt_config_reg_type       type;
-};
-
-struct polaris10_pt_defaults {
-       uint8_t   SviLoadLineEn;
-       uint8_t   SviLoadLineVddC;
-       uint8_t   TDC_VDDC_ThrottleReleaseLimitPerc;
-       uint8_t   TDC_MAWt;
-       uint8_t   TdcWaterfallCtl;
-       uint8_t   DTEAmbientTempBase;
-
-       uint32_t  DisplayCac;
-       uint32_t  BAPM_TEMP_GRADIENT;
-       uint16_t  BAPMTI_R[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
-       uint16_t  BAPMTI_RC[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
-};
-
-void polaris10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr);
-int polaris10_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr);
-int polaris10_populate_pm_fuses(struct pp_hwmgr *hwmgr);
-int polaris10_enable_smc_cac(struct pp_hwmgr *hwmgr);
-int polaris10_disable_smc_cac(struct pp_hwmgr *hwmgr);
-int polaris10_enable_power_containment(struct pp_hwmgr *hwmgr);
-int polaris10_disable_power_containment(struct pp_hwmgr *hwmgr);
-int polaris10_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
-int polaris10_power_control_set_level(struct pp_hwmgr *hwmgr);
-int polaris10_enable_didt_config(struct pp_hwmgr *hwmgr);
-#endif  /* POLARIS10_POWERTUNE_H */
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.c
deleted file mode 100644 (file)
index b206632..0000000
+++ /dev/null
@@ -1,716 +0,0 @@
-/*
- * Copyright 2016 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 <asm/div64.h>
-#include "polaris10_thermal.h"
-#include "polaris10_hwmgr.h"
-#include "polaris10_smumgr.h"
-#include "polaris10_ppsmc.h"
-#include "smu/smu_7_1_3_d.h"
-#include "smu/smu_7_1_3_sh_mask.h"
-
-int polaris10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
-               struct phm_fan_speed_info *fan_speed_info)
-{
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       fan_speed_info->supports_percent_read = true;
-       fan_speed_info->supports_percent_write = true;
-       fan_speed_info->min_percent = 0;
-       fan_speed_info->max_percent = 100;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_FanSpeedInTableIsRPM) &&
-               hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
-               fan_speed_info->supports_rpm_read = true;
-               fan_speed_info->supports_rpm_write = true;
-               fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM;
-               fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
-       } else {
-               fan_speed_info->min_rpm = 0;
-               fan_speed_info->max_rpm = 0;
-       }
-
-       return 0;
-}
-
-int polaris10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
-               uint32_t *speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-       duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_STATUS, FDO_PWM_DUTY);
-
-       if (duty100 == 0)
-               return -EINVAL;
-
-
-       tmp64 = (uint64_t)duty * 100;
-       do_div(tmp64, duty100);
-       *speed = (uint32_t)tmp64;
-
-       if (*speed > 100)
-               *speed = 100;
-
-       return 0;
-}
-
-int polaris10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
-{
-       uint32_t tach_period;
-       uint32_t crystal_clock_freq;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
-                       (hwmgr->thermal_controller.fanInfo.
-                               ucTachometerPulsesPerRevolution == 0))
-               return 0;
-
-       tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_TACH_STATUS, TACH_PERIOD);
-
-       if (tach_period == 0)
-               return -EINVAL;
-
-       crystal_clock_freq = tonga_get_xclk(hwmgr);
-
-       *speed = 60 * crystal_clock_freq * 10000 / tach_period;
-
-       return 0;
-}
-
-/**
-* Set Fan Speed Control to static mode, so that the user can decide what speed to use.
-* @param    hwmgr  the address of the powerplay hardware manager.
-*           mode    the fan control mode, 0 default, 1 by percent, 5, by RPM
-* @exception Should always succeed.
-*/
-int polaris10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-
-       if (hwmgr->fan_ctrl_is_in_default_mode) {
-               hwmgr->fan_ctrl_default_mode =
-                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,     CGS_IND_REG__SMC,
-                                               CG_FDO_CTRL2, FDO_PWM_MODE);
-               hwmgr->tmin =
-                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                               CG_FDO_CTRL2, TMIN);
-               hwmgr->fan_ctrl_is_in_default_mode = false;
-       }
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, TMIN, 0);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, FDO_PWM_MODE, mode);
-
-       return 0;
-}
-
-/**
-* Reset Fan Speed Control to default mode.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Should always succeed.
-*/
-int polaris10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->fan_ctrl_is_in_default_mode) {
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode);
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, TMIN, hwmgr->tmin);
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       }
-
-       return 0;
-}
-
-int polaris10_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ODFuzzyFanControlSupport)) {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY);
-               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_FanSpeedInTableIsRPM))
-                       hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr,
-                                       hwmgr->thermal_controller.
-                                       advanceFanControlParameters.usMaxFanRPM);
-               else
-                       hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr,
-                                       hwmgr->thermal_controller.
-                                       advanceFanControlParameters.usMaxFanPWM);
-
-       } else {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE);
-               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
-       }
-
-       if (!result && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ucTargetTemperature)
-               result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanTemperatureTarget,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ucTargetTemperature);
-
-       return result;
-}
-
-
-int polaris10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl);
-}
-
-/**
-* Set Fan Speed in percent.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (0% - 100%) to be set.
-* @exception Fails is the 100% setting appears to be 0.
-*/
-int polaris10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
-               uint32_t speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       if (speed > 100)
-               speed = 100;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl))
-               polaris10_fan_ctrl_stop_smc_fan_control(hwmgr);
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (duty100 == 0)
-               return -EINVAL;
-
-       tmp64 = (uint64_t)speed * duty100;
-       do_div(tmp64, 100);
-       duty = (uint32_t)tmp64;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL0, FDO_STATIC_DUTY, duty);
-
-       return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-}
-
-/**
-* Reset Fan Speed to default.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Always succeeds.
-*/
-int polaris10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl)) {
-               result = polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-               if (!result)
-                       result = polaris10_fan_ctrl_start_smc_fan_control(hwmgr);
-       } else
-               result = polaris10_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set Fan Speed in RPM.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (min - max) to be set.
-* @exception Fails is the speed not lie between min and max.
-*/
-int polaris10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
-{
-       uint32_t tach_period;
-       uint32_t crystal_clock_freq;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
-                       (hwmgr->thermal_controller.fanInfo.
-                       ucTachometerPulsesPerRevolution == 0) ||
-                       (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
-                       (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
-               return 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl))
-               polaris10_fan_ctrl_stop_smc_fan_control(hwmgr);
-
-       crystal_clock_freq = tonga_get_xclk(hwmgr);
-
-       tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_TACH_STATUS, TACH_PERIOD, tach_period);
-
-       return polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-}
-
-/**
-* Reads the remote temperature from the SIslands thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-int polaris10_thermal_get_temperature(struct pp_hwmgr *hwmgr)
-{
-       int temp;
-
-       temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_MULT_THERMAL_STATUS, CTF_TEMP);
-
-       /* Bit 9 means the reading is lower than the lowest usable value. */
-       if (temp & 0x200)
-               temp = POLARIS10_THERMAL_MAXIMUM_TEMP_READING;
-       else
-               temp = temp & 0x1ff;
-
-       temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       return temp;
-}
-
-/**
-* Set the requested temperature range for high and low alert signals
-*
-* @param    hwmgr The address of the hardware manager.
-* @param    range Temperature range to be programmed for high and low alert signals
-* @exception PP_Result_BadInput if the input data is not valid.
-*/
-static int polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
-               uint32_t low_temp, uint32_t high_temp)
-{
-       uint32_t low = POLARIS10_THERMAL_MINIMUM_ALERT_TEMP *
-                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-       uint32_t high = POLARIS10_THERMAL_MAXIMUM_ALERT_TEMP *
-                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       if (low < low_temp)
-               low = low_temp;
-       if (high > high_temp)
-               high = high_temp;
-
-       if (low > high)
-               return -EINVAL;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, DIG_THERM_INTH,
-                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, DIG_THERM_INTL,
-                       (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_CTRL, DIG_THERM_DPM,
-                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-
-       return 0;
-}
-
-/**
-* Programs thermal controller one-time setting registers
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int polaris10_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_TACH_CTRL, EDGE_PER_REV,
-                               hwmgr->thermal_controller.fanInfo.
-                               ucTachometerPulsesPerRevolution - 1);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
-
-       return 0;
-}
-
-/**
-* Enable thermal alerts on the RV770 thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK);
-       alert &= ~(POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to enable internal thermal interrupts */
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable);
-}
-
-/**
-* Disable thermal alerts on the RV770 thermal controller.
-* @param    hwmgr The address of the hardware manager.
-*/
-static int polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK);
-       alert |= (POLARIS10_THERMAL_HIGH_ALERT_MASK | POLARIS10_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to disable internal thermal interrupts */
-       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable);
-}
-
-/**
-* Uninitialize the thermal controller.
-* Currently just disables alerts.
-* @param    hwmgr The address of the hardware manager.
-*/
-int polaris10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       int result = polaris10_thermal_disable_alert(hwmgr);
-
-       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
-               polaris10_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set up the fan table to control the fan using the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-       SMU74_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
-       uint32_t duty100;
-       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
-       uint16_t fdo_min, slope1, slope2;
-       uint32_t reference_clock;
-       int res;
-       uint64_t tmp64;
-
-       if (data->fan_table_start == 0) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                       CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (duty100 == 0) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
-                       usPWMMin * duty100;
-       do_div(tmp64, 10000);
-       fdo_min = (uint16_t)tmp64;
-
-       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
-       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
-
-       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
-       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
-                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
-
-       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
-       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
-
-       fan_table.TempMin = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMin) / 100);
-       fan_table.TempMed = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMed) / 100);
-       fan_table.TempMax = cpu_to_be16((50 + hwmgr->
-                       thermal_controller.advanceFanControlParameters.usTMax) / 100);
-
-       fan_table.Slope1 = cpu_to_be16(slope1);
-       fan_table.Slope2 = cpu_to_be16(slope2);
-
-       fan_table.FdoMin = cpu_to_be16(fdo_min);
-
-       fan_table.HystDown = cpu_to_be16(hwmgr->
-                       thermal_controller.advanceFanControlParameters.ucTHyst);
-
-       fan_table.HystUp = cpu_to_be16(1);
-
-       fan_table.HystSlope = cpu_to_be16(1);
-
-       fan_table.TempRespLim = cpu_to_be16(5);
-
-       reference_clock = tonga_get_xclk(hwmgr);
-
-       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
-                       thermal_controller.advanceFanControlParameters.ulCycleDelay *
-                       reference_clock) / 1600);
-
-       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
-
-       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
-                       hwmgr->device, CGS_IND_REG__SMC,
-                       CG_MULT_THERMAL_CTRL, TEMP_SEL);
-
-       res = polaris10_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start,
-                       (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
-                       data->sram_end);
-
-       if (!res && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ucMinimumPWMLimit)
-               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanMinPwm,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ucMinimumPWMLimit);
-
-       if (!res && hwmgr->thermal_controller.
-                       advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
-               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                               PPSMC_MSG_SetFanSclkTarget,
-                               hwmgr->thermal_controller.
-                               advanceFanControlParameters.ulMinFanSCLKAcousticLimit);
-
-       if (res)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl);
-
-       return 0;
-}
-
-/**
-* Start the fan control on the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_polaris10_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-/* If the fantable setup has failed we could have disabled
- * PHM_PlatformCaps_MicrocodeFanControl even after
- * this function was included in the table.
- * Make sure that we still think controlling the fan is OK.
-*/
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MicrocodeFanControl)) {
-               polaris10_fan_ctrl_start_smc_fan_control(hwmgr);
-               polaris10_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-       }
-
-       return 0;
-}
-
-/**
-* Set temperature range for high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input;
-
-       if (range == NULL)
-               return -EINVAL;
-
-       return polaris10_thermal_set_temperature_range(hwmgr, range->min, range->max);
-}
-
-/**
-* Programs one-time setting registers
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from initialize thermal controller routine
-*/
-int tf_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-    return polaris10_thermal_initialize(hwmgr);
-}
-
-/**
-* Enable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from enable alert routine
-*/
-int tf_polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       return polaris10_thermal_enable_alert(hwmgr);
-}
-
-/**
-* Disable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from disable alert routine
-*/
-static int tf_polaris10_thermal_disable_alert(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       return polaris10_thermal_disable_alert(hwmgr);
-}
-
-static int tf_polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr,
-               void *input, void *output, void *storage, int result)
-{
-       int ret;
-       struct pp_smumgr *smumgr = (struct pp_smumgr *)(hwmgr->smumgr);
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-       struct polaris10_hwmgr *data = (struct polaris10_hwmgr *)(hwmgr->backend);
-
-       if (smu_data->avfs.avfs_btc_status == AVFS_BTC_NOTSUPPORTED)
-               return 0;
-
-       ret = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                       PPSMC_MSG_SetGBDroopSettings, data->avfs_vdroop_override_setting);
-
-       ret = (smum_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs) == 0) ?
-                       0 : -1;
-
-       if (!ret)
-               /* If this param is not changed, this function could fire unnecessarily */
-               smu_data->avfs.avfs_btc_status = AVFS_BTC_COMPLETED_PREVIOUSLY;
-
-       return ret;
-}
-
-static const struct phm_master_table_item
-polaris10_thermal_start_thermal_controller_master_list[] = {
-       {NULL, tf_polaris10_thermal_initialize},
-       {NULL, tf_polaris10_thermal_set_temperature_range},
-       {NULL, tf_polaris10_thermal_enable_alert},
-       {NULL, tf_polaris10_thermal_avfs_enable},
-/* We should restrict performance levels to low before we halt the SMC.
- * On the other hand we are still in boot state when we do this
- * so it would be pointless.
- * If this assumption changes we have to revisit this table.
- */
-       {NULL, tf_polaris10_thermal_setup_fan_table},
-       {NULL, tf_polaris10_thermal_start_smc_fan_control},
-       {NULL, NULL}
-};
-
-static const struct phm_master_table_header
-polaris10_thermal_start_thermal_controller_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       polaris10_thermal_start_thermal_controller_master_list
-};
-
-static const struct phm_master_table_item
-polaris10_thermal_set_temperature_range_master_list[] = {
-       {NULL, tf_polaris10_thermal_disable_alert},
-       {NULL, tf_polaris10_thermal_set_temperature_range},
-       {NULL, tf_polaris10_thermal_enable_alert},
-       {NULL, NULL}
-};
-
-static const struct phm_master_table_header
-polaris10_thermal_set_temperature_range_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       polaris10_thermal_set_temperature_range_master_list
-};
-
-int polaris10_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
-               polaris10_fan_ctrl_set_default_mode(hwmgr);
-       return 0;
-}
-
-/**
-* Initializes the thermal controller related functions in the Hardware Manager structure.
-* @param    hwmgr The address of the hardware manager.
-* @exception Any error code from the low-level communication.
-*/
-int pp_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       result = phm_construct_table(hwmgr,
-                       &polaris10_thermal_set_temperature_range_master,
-                       &(hwmgr->set_temperature_range));
-
-       if (!result) {
-               result = phm_construct_table(hwmgr,
-                               &polaris10_thermal_start_thermal_controller_master,
-                               &(hwmgr->start_thermal_controller));
-               if (result)
-                       phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range));
-       }
-
-       if (!result)
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       return result;
-}
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.h b/drivers/gpu/drm/amd/powerplay/hwmgr/polaris10_thermal.h
deleted file mode 100644 (file)
index 62f8cbc..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2016 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 _POLARIS10_THERMAL_H_
-#define _POLARIS10_THERMAL_H_
-
-#include "hwmgr.h"
-
-#define POLARIS10_THERMAL_HIGH_ALERT_MASK         0x1
-#define POLARIS10_THERMAL_LOW_ALERT_MASK          0x2
-
-#define POLARIS10_THERMAL_MINIMUM_TEMP_READING    -256
-#define POLARIS10_THERMAL_MAXIMUM_TEMP_READING    255
-
-#define POLARIS10_THERMAL_MINIMUM_ALERT_TEMP      0
-#define POLARIS10_THERMAL_MAXIMUM_ALERT_TEMP      255
-
-#define FDO_PWM_MODE_STATIC  1
-#define FDO_PWM_MODE_STATIC_RPM 5
-
-
-extern int tf_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_polaris10_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_polaris10_thermal_enable_alert(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-
-extern int polaris10_thermal_get_temperature(struct pp_hwmgr *hwmgr);
-extern int polaris10_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int polaris10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info);
-extern int polaris10_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int polaris10_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
-extern int polaris10_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode);
-extern int polaris10_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int polaris10_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
-extern int pp_polaris10_thermal_initialize(struct pp_hwmgr *hwmgr);
-extern int polaris10_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int polaris10_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int polaris10_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int polaris10_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
-extern uint32_t tonga_get_xclk(struct pp_hwmgr *hwmgr);
-
-#endif
-
index 26f3e30..1126bd4 100644 (file)
@@ -22,7 +22,6 @@
  */
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/fb.h>
 
 #include "ppatomctrl.h"
 #include "atombios.h"
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/pptable_v1_0.h b/drivers/gpu/drm/amd/powerplay/hwmgr/pptable_v1_0.h
new file mode 100644 (file)
index 0000000..1e870f5
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * Copyright 2015 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 TONGA_PPTABLE_H
+#define TONGA_PPTABLE_H
+
+/** \file
+ * This is a PowerPlay table header file
+ */
+#pragma pack(push, 1)
+
+#include "hwmgr.h"
+
+#define ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
+#define ATOM_TONGA_PP_FANPARAMETERS_NOFAN                                 0x80    /* No fan is connected to this controller. */
+
+#define ATOM_TONGA_PP_THERMALCONTROLLER_NONE      0
+#define ATOM_TONGA_PP_THERMALCONTROLLER_LM96163   17
+#define ATOM_TONGA_PP_THERMALCONTROLLER_TONGA     21
+#define ATOM_TONGA_PP_THERMALCONTROLLER_FIJI      22
+
+/*
+ * 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.
+ * To keep the number of these types low we should also use the same code for all ASICs (i.e. do not distinguish RV6xx and RV7xx Internal here).
+ * The driver can pick the correct internal controller based on the ASIC.
+ */
+
+#define ATOM_TONGA_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL   0x89    /* ADT7473 Fan Control + Internal Thermal Controller */
+#define ATOM_TONGA_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL   0x8D    /* EMC2103 Fan Control + Internal Thermal Controller */
+
+/*/* ATOM_TONGA_POWERPLAYTABLE::ulPlatformCaps */
+#define ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL              0x1            /* This cap indicates whether vddgfx will be a separated power rail. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY                   0x2            /* This cap indicates whether this is a mobile part and CCC need to show Powerplay page. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE            0x4            /* This cap indicates whether power source notificaiton is done by SBIOS directly. */
+#define ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND      0x8            /* Enable the option to overwrite voltage island feature to be disabled, regardless of VddGfx power rail support. */
+#define ____RETIRE16____                                0x10
+#define ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC                 0x20            /* This cap indicates whether power source notificaiton is done by GPIO directly. */
+#define ____RETIRE64____                                0x40
+#define ____RETIRE128____                               0x80
+#define ____RETIRE256____                              0x100
+#define ____RETIRE512____                              0x200
+#define ____RETIRE1024____                             0x400
+#define ____RETIRE2048____                             0x800
+#define ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL             0x1000            /* This cap indicates dynamic MVDD is required. Uncheck to disable it. */
+#define ____RETIRE2000____                            0x2000
+#define ____RETIRE4000____                            0x4000
+#define ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL            0x8000            /* This cap indicates dynamic VDDCI is required. Uncheck to disable it. */
+#define ____RETIRE10000____                          0x10000
+#define ATOM_TONGA_PP_PLATFORM_CAP_BACO                    0x20000            /* Enable to indicate the driver supports BACO state. */
+
+#define ATOM_TONGA_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17         0x100000     /* Enable to indicate the driver supports thermal2GPIO17. */
+#define ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL  0x1000000     /* Enable to indicate if thermal and PCC are sharing the same GPIO */
+#define ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE       0x2000000
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification */
+#define ATOM_PPLIB_CLASSIFICATION_UI_MASK               0x0007
+#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT              0
+#define ATOM_PPLIB_CLASSIFICATION_UI_NONE               0
+#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY            1
+#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED           3
+#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE        5
+/* 2, 4, 6, 7 are reserved */
+
+#define ATOM_PPLIB_CLASSIFICATION_BOOT                  0x0008
+#define ATOM_PPLIB_CLASSIFICATION_THERMAL               0x0010
+#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE    0x0020
+#define ATOM_PPLIB_CLASSIFICATION_REST                  0x0040
+#define ATOM_PPLIB_CLASSIFICATION_FORCED                0x0080
+#define ATOM_PPLIB_CLASSIFICATION_ACPI                  0x1000
+
+/* ATOM_PPLIB_NONCLOCK_INFO::usClassification2 */
+#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001
+
+#define ATOM_Tonga_DISALLOW_ON_DC                       0x00004000
+#define ATOM_Tonga_ENABLE_VARIBRIGHT                    0x00008000
+
+#define ATOM_Tonga_TABLE_REVISION_TONGA                 7
+
+typedef struct _ATOM_Tonga_POWERPLAYTABLE {
+       ATOM_COMMON_TABLE_HEADER sHeader;
+
+       UCHAR  ucTableRevision;
+       USHORT usTableSize;                                             /*the size of header structure */
+
+       ULONG   ulGoldenPPID;
+       ULONG   ulGoldenRevision;
+       USHORT  usFormatID;
+
+       USHORT  usVoltageTime;                                   /*in microseconds */
+       ULONG   ulPlatformCaps;                                   /*See ATOM_Tonga_CAPS_* */
+
+       ULONG   ulMaxODEngineClock;                        /*For Overdrive.  */
+       ULONG   ulMaxODMemoryClock;                        /*For Overdrive. */
+
+       USHORT  usPowerControlLimit;
+       USHORT  usUlvVoltageOffset;                               /*in mv units */
+
+       USHORT  usStateArrayOffset;                               /*points to ATOM_Tonga_State_Array */
+       USHORT  usFanTableOffset;                                 /*points to ATOM_Tonga_Fan_Table */
+       USHORT  usThermalControllerOffset;                 /*points to ATOM_Tonga_Thermal_Controller */
+       USHORT  usReserv;                                                  /*CustomThermalPolicy removed for Tonga. Keep this filed as reserved. */
+
+       USHORT  usMclkDependencyTableOffset;       /*points to ATOM_Tonga_MCLK_Dependency_Table */
+       USHORT  usSclkDependencyTableOffset;       /*points to ATOM_Tonga_SCLK_Dependency_Table */
+       USHORT  usVddcLookupTableOffset;                   /*points to ATOM_Tonga_Voltage_Lookup_Table */
+       USHORT  usVddgfxLookupTableOffset;              /*points to ATOM_Tonga_Voltage_Lookup_Table */
+
+       USHORT  usMMDependencyTableOffset;                /*points to ATOM_Tonga_MM_Dependency_Table */
+
+       USHORT  usVCEStateTableOffset;                     /*points to ATOM_Tonga_VCE_State_Table; */
+
+       USHORT  usPPMTableOffset;                                 /*points to ATOM_Tonga_PPM_Table */
+       USHORT  usPowerTuneTableOffset;                   /*points to ATOM_PowerTune_Table */
+
+       USHORT  usHardLimitTableOffset;                    /*points to ATOM_Tonga_Hard_Limit_Table */
+
+       USHORT  usPCIETableOffset;                                /*points to ATOM_Tonga_PCIE_Table */
+
+       USHORT  usGPIOTableOffset;                                /*points to ATOM_Tonga_GPIO_Table */
+
+       USHORT  usReserved[6];                                     /*TODO: modify reserved size to fit structure aligning */
+} ATOM_Tonga_POWERPLAYTABLE;
+
+typedef struct _ATOM_Tonga_State {
+       UCHAR  ucEngineClockIndexHigh;
+       UCHAR  ucEngineClockIndexLow;
+
+       UCHAR  ucMemoryClockIndexHigh;
+       UCHAR  ucMemoryClockIndexLow;
+
+       UCHAR  ucPCIEGenLow;
+       UCHAR  ucPCIEGenHigh;
+
+       UCHAR  ucPCIELaneLow;
+       UCHAR  ucPCIELaneHigh;
+
+       USHORT usClassification;
+       ULONG ulCapsAndSettings;
+       USHORT usClassification2;
+       UCHAR  ucUnused[4];
+} ATOM_Tonga_State;
+
+typedef struct _ATOM_Tonga_State_Array {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;             /* Number of entries. */
+       ATOM_Tonga_State entries[1];    /* Dynamically allocate entries. */
+} ATOM_Tonga_State_Array;
+
+typedef struct _ATOM_Tonga_MCLK_Dependency_Record {
+       UCHAR  ucVddcInd;       /* Vddc voltage */
+       USHORT usVddci;
+       USHORT usVddgfxOffset;  /* Offset relative to Vddc voltage */
+       USHORT usMvdd;
+       ULONG ulMclk;
+       USHORT usReserved;
+} ATOM_Tonga_MCLK_Dependency_Record;
+
+typedef struct _ATOM_Tonga_MCLK_Dependency_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                                             /* Number of entries. */
+       ATOM_Tonga_MCLK_Dependency_Record entries[1];                           /* Dynamically allocate entries. */
+} ATOM_Tonga_MCLK_Dependency_Table;
+
+typedef struct _ATOM_Tonga_SCLK_Dependency_Record {
+       UCHAR  ucVddInd;                                                                                        /* Base voltage */
+       USHORT usVddcOffset;                                                                            /* Offset relative to base voltage */
+       ULONG ulSclk;
+       USHORT usEdcCurrent;
+       UCHAR  ucReliabilityTemperature;
+       UCHAR  ucCKSVOffsetandDisable;                                                    /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
+} ATOM_Tonga_SCLK_Dependency_Record;
+
+typedef struct _ATOM_Tonga_SCLK_Dependency_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                                             /* Number of entries. */
+       ATOM_Tonga_SCLK_Dependency_Record entries[1];                            /* Dynamically allocate entries. */
+} ATOM_Tonga_SCLK_Dependency_Table;
+
+typedef struct _ATOM_Polaris_SCLK_Dependency_Record {
+       UCHAR  ucVddInd;                                                                                        /* Base voltage */
+       USHORT usVddcOffset;                                                                            /* Offset relative to base voltage */
+       ULONG ulSclk;
+       USHORT usEdcCurrent;
+       UCHAR  ucReliabilityTemperature;
+       UCHAR  ucCKSVOffsetandDisable;                  /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
+       ULONG  ulSclkOffset;
+} ATOM_Polaris_SCLK_Dependency_Record;
+
+typedef struct _ATOM_Polaris_SCLK_Dependency_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                     /* Number of entries. */
+       ATOM_Polaris_SCLK_Dependency_Record entries[1];                          /* Dynamically allocate entries. */
+} ATOM_Polaris_SCLK_Dependency_Table;
+
+typedef struct _ATOM_Tonga_PCIE_Record {
+       UCHAR ucPCIEGenSpeed;
+       UCHAR usPCIELaneWidth;
+       UCHAR ucReserved[2];
+} ATOM_Tonga_PCIE_Record;
+
+typedef struct _ATOM_Tonga_PCIE_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                                             /* Number of entries. */
+       ATOM_Tonga_PCIE_Record entries[1];                                                      /* Dynamically allocate entries. */
+} ATOM_Tonga_PCIE_Table;
+
+typedef struct _ATOM_Polaris10_PCIE_Record {
+       UCHAR ucPCIEGenSpeed;
+       UCHAR usPCIELaneWidth;
+       UCHAR ucReserved[2];
+       ULONG ulPCIE_Sclk;
+} ATOM_Polaris10_PCIE_Record;
+
+typedef struct _ATOM_Polaris10_PCIE_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                         /* Number of entries. */
+       ATOM_Polaris10_PCIE_Record entries[1];                      /* Dynamically allocate entries. */
+} ATOM_Polaris10_PCIE_Table;
+
+
+typedef struct _ATOM_Tonga_MM_Dependency_Record {
+       UCHAR   ucVddcInd;                                                                                       /* VDDC voltage */
+       USHORT  usVddgfxOffset;                                                                   /* Offset relative to VDDC voltage */
+       ULONG  ulDClk;                                                                                          /* UVD D-clock */
+       ULONG  ulVClk;                                                                                          /* UVD V-clock */
+       ULONG  ulEClk;                                                                                          /* VCE clock */
+       ULONG  ulAClk;                                                                                          /* ACP clock */
+       ULONG  ulSAMUClk;                                                                                       /* SAMU clock */
+} ATOM_Tonga_MM_Dependency_Record;
+
+typedef struct _ATOM_Tonga_MM_Dependency_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                                             /* Number of entries. */
+       ATOM_Tonga_MM_Dependency_Record entries[1];                        /* Dynamically allocate entries. */
+} ATOM_Tonga_MM_Dependency_Table;
+
+typedef struct _ATOM_Tonga_Voltage_Lookup_Record {
+       USHORT usVdd;                                                                                      /* Base voltage */
+       USHORT usCACLow;
+       USHORT usCACMid;
+       USHORT usCACHigh;
+} ATOM_Tonga_Voltage_Lookup_Record;
+
+typedef struct _ATOM_Tonga_Voltage_Lookup_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;                                                                             /* Number of entries. */
+       ATOM_Tonga_Voltage_Lookup_Record entries[1];                            /* Dynamically allocate entries. */
+} ATOM_Tonga_Voltage_Lookup_Table;
+
+typedef struct _ATOM_Tonga_Fan_Table {
+       UCHAR   ucRevId;                                                 /* Change this if the table format changes or version changes so that the other fields are not the same. */
+       UCHAR   ucTHyst;                                                 /* Temperature hysteresis. Integer. */
+       USHORT  usTMin;                                                  /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
+       USHORT  usTMed;                                                  /* The middle temperature where we change slopes. */
+       USHORT  usTHigh;                                                 /* The high point above TMed for adjusting the second slope. */
+       USHORT  usPWMMin;                                                /* The minimum PWM value in percent (0.01% increments). */
+       USHORT  usPWMMed;                                                /* The PWM value (in percent) at TMed. */
+       USHORT  usPWMHigh;                                               /* The PWM value at THigh. */
+       USHORT  usTMax;                                                  /* The max temperature */
+       UCHAR   ucFanControlMode;                                 /* Legacy or Fuzzy Fan mode */
+       USHORT  usFanPWMMax;                                      /* Maximum allowed fan power in percent */
+       USHORT  usFanOutputSensitivity;           /* Sensitivity of fan reaction to temepature changes */
+       USHORT  usFanRPMMax;                                      /* The default value in RPM */
+       ULONG  ulMinFanSCLKAcousticLimit;          /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
+       UCHAR   ucTargetTemperature;                     /* Advanced fan controller target temperature. */
+       UCHAR   ucMinimumPWMLimit;                        /* The minimum PWM that the advanced fan controller can set.  This should be set to the highest PWM that will run the fan at its lowest RPM. */
+       USHORT  usReserved;
+} ATOM_Tonga_Fan_Table;
+
+typedef struct _ATOM_Fiji_Fan_Table {
+       UCHAR   ucRevId;                                                 /* Change this if the table format changes or version changes so that the other fields are not the same. */
+       UCHAR   ucTHyst;                                                 /* Temperature hysteresis. Integer. */
+       USHORT  usTMin;                                                  /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
+       USHORT  usTMed;                                                  /* The middle temperature where we change slopes. */
+       USHORT  usTHigh;                                                 /* The high point above TMed for adjusting the second slope. */
+       USHORT  usPWMMin;                                                /* The minimum PWM value in percent (0.01% increments). */
+       USHORT  usPWMMed;                                                /* The PWM value (in percent) at TMed. */
+       USHORT  usPWMHigh;                                               /* The PWM value at THigh. */
+       USHORT  usTMax;                                                  /* The max temperature */
+       UCHAR   ucFanControlMode;                                 /* Legacy or Fuzzy Fan mode */
+       USHORT  usFanPWMMax;                                      /* Maximum allowed fan power in percent */
+       USHORT  usFanOutputSensitivity;           /* Sensitivity of fan reaction to temepature changes */
+       USHORT  usFanRPMMax;                                      /* The default value in RPM */
+       ULONG  ulMinFanSCLKAcousticLimit;               /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
+       UCHAR   ucTargetTemperature;                     /* Advanced fan controller target temperature. */
+       UCHAR   ucMinimumPWMLimit;                        /* The minimum PWM that the advanced fan controller can set.  This should be set to the highest PWM that will run the fan at its lowest RPM. */
+       USHORT  usFanGainEdge;
+       USHORT  usFanGainHotspot;
+       USHORT  usFanGainLiquid;
+       USHORT  usFanGainVrVddc;
+       USHORT  usFanGainVrMvdd;
+       USHORT  usFanGainPlx;
+       USHORT  usFanGainHbm;
+       USHORT  usReserved;
+} ATOM_Fiji_Fan_Table;
+
+typedef struct _ATOM_Tonga_Thermal_Controller {
+       UCHAR ucRevId;
+       UCHAR ucType;              /* one of ATOM_TONGA_PP_THERMALCONTROLLER_* */
+       UCHAR ucI2cLine;                /* as interpreted by DAL I2C */
+       UCHAR ucI2cAddress;
+       UCHAR ucFanParameters;  /* Fan Control Parameters. */
+       UCHAR ucFanMinRPM;       /* Fan Minimum RPM (hundreds) -- for display purposes only. */
+       UCHAR ucFanMaxRPM;       /* Fan Maximum RPM (hundreds) -- for display purposes only. */
+       UCHAR ucReserved;
+       UCHAR ucFlags;             /* to be defined */
+} ATOM_Tonga_Thermal_Controller;
+
+typedef struct _ATOM_Tonga_VCE_State_Record {
+       UCHAR  ucVCEClockIndex; /*index into usVCEDependencyTableOffset of 'ATOM_Tonga_MM_Dependency_Table' type */
+       UCHAR  ucFlag;          /* 2 bits indicates memory p-states */
+       UCHAR  ucSCLKIndex;             /*index into ATOM_Tonga_SCLK_Dependency_Table */
+       UCHAR  ucMCLKIndex;             /*index into ATOM_Tonga_MCLK_Dependency_Table */
+} ATOM_Tonga_VCE_State_Record;
+
+typedef struct _ATOM_Tonga_VCE_State_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;
+       ATOM_Tonga_VCE_State_Record entries[1];
+} ATOM_Tonga_VCE_State_Table;
+
+typedef struct _ATOM_Tonga_PowerTune_Table {
+       UCHAR  ucRevId;
+       USHORT usTDP;
+       USHORT usConfigurableTDP;
+       USHORT usTDC;
+       USHORT usBatteryPowerLimit;
+       USHORT usSmallPowerLimit;
+       USHORT usLowCACLeakage;
+       USHORT usHighCACLeakage;
+       USHORT usMaximumPowerDeliveryLimit;
+       USHORT usTjMax;
+       USHORT usPowerTuneDataSetID;
+       USHORT usEDCLimit;
+       USHORT usSoftwareShutdownTemp;
+       USHORT usClockStretchAmount;
+       USHORT usReserve[2];
+} ATOM_Tonga_PowerTune_Table;
+
+typedef struct _ATOM_Fiji_PowerTune_Table {
+       UCHAR  ucRevId;
+       USHORT usTDP;
+       USHORT usConfigurableTDP;
+       USHORT usTDC;
+       USHORT usBatteryPowerLimit;
+       USHORT usSmallPowerLimit;
+       USHORT usLowCACLeakage;
+       USHORT usHighCACLeakage;
+       USHORT usMaximumPowerDeliveryLimit;
+       USHORT usTjMax;  /* For Fiji, this is also usTemperatureLimitEdge; */
+       USHORT usPowerTuneDataSetID;
+       USHORT usEDCLimit;
+       USHORT usSoftwareShutdownTemp;
+       USHORT usClockStretchAmount;
+       USHORT usTemperatureLimitHotspot;  /*The following are added for Fiji */
+       USHORT usTemperatureLimitLiquid1;
+       USHORT usTemperatureLimitLiquid2;
+       USHORT usTemperatureLimitVrVddc;
+       USHORT usTemperatureLimitVrMvdd;
+       USHORT usTemperatureLimitPlx;
+       UCHAR  ucLiquid1_I2C_address;  /*Liquid */
+       UCHAR  ucLiquid2_I2C_address;
+       UCHAR  ucLiquid_I2C_Line;
+       UCHAR  ucVr_I2C_address;        /*VR */
+       UCHAR  ucVr_I2C_Line;
+       UCHAR  ucPlx_I2C_address;  /*PLX */
+       UCHAR  ucPlx_I2C_Line;
+       USHORT usReserved;
+} ATOM_Fiji_PowerTune_Table;
+
+#define ATOM_PPM_A_A    1
+#define ATOM_PPM_A_I    2
+typedef struct _ATOM_Tonga_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_Tonga_PPM_Table;
+
+typedef struct _ATOM_Tonga_Hard_Limit_Record {
+       ULONG  ulSCLKLimit;
+       ULONG  ulMCLKLimit;
+       USHORT  usVddcLimit;
+       USHORT  usVddciLimit;
+       USHORT  usVddgfxLimit;
+} ATOM_Tonga_Hard_Limit_Record;
+
+typedef struct _ATOM_Tonga_Hard_Limit_Table {
+       UCHAR ucRevId;
+       UCHAR ucNumEntries;
+       ATOM_Tonga_Hard_Limit_Record entries[1];
+} ATOM_Tonga_Hard_Limit_Table;
+
+typedef struct _ATOM_Tonga_GPIO_Table {
+       UCHAR  ucRevId;
+       UCHAR  ucVRHotTriggeredSclkDpmIndex;            /* If VRHot signal is triggered SCLK will be limited to this DPM level */
+       UCHAR  ucReserve[5];
+} ATOM_Tonga_GPIO_Table;
+
+typedef struct _PPTable_Generic_SubTable_Header {
+       UCHAR  ucRevId;
+} PPTable_Generic_SubTable_Header;
+
+
+#pragma pack(pop)
+
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.c
new file mode 100644 (file)
index 0000000..7de701d
--- /dev/null
@@ -0,0 +1,1325 @@
+/*
+ * Copyright 2015 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/module.h>
+#include <linux/slab.h>
+
+#include "process_pptables_v1_0.h"
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "pp_debug.h"
+#include "hwmgr.h"
+#include "cgs_common.h"
+#include "pptable_v1_0.h"
+
+/**
+ * Private Function used during initialization.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param setIt A flag indication if the capability should be set (TRUE) or reset (FALSE).
+ * @param cap Which capability to set/reset.
+ */
+static void set_hw_cap(struct pp_hwmgr *hwmgr, bool setIt, enum phm_platform_caps cap)
+{
+       if (setIt)
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
+       else
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
+}
+
+
+/**
+ * Private Function used during initialization.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_caps the bit array (from BIOS) of capability bits.
+ * @exception the current implementation always returns 1.
+ */
+static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
+{
+       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE16____),
+               "ATOM_PP_PLATFORM_CAP_ASPM_L1 is not supported!", continue);
+       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE64____),
+               "ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY is not supported!", continue);
+       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE512____),
+               "ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL is not supported!", continue);
+       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE1024____),
+               "ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 is not supported!", continue);
+       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE2048____),
+               "ATOM_PP_PLATFORM_CAP_HTLINKCONTROL is not supported!", continue);
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY),
+                       PHM_PlatformCaps_PowerPlaySupport
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
+                       PHM_PlatformCaps_BiosPowerSourceControl
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC),
+                       PHM_PlatformCaps_AutomaticDCTransition
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL),
+                       PHM_PlatformCaps_EnableMVDDControl
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL),
+                       PHM_PlatformCaps_ControlVDDCI
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL),
+                       PHM_PlatformCaps_ControlVDDGFX
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_BACO),
+                       PHM_PlatformCaps_BACO
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND),
+                       PHM_PlatformCaps_DisableVoltageIsland
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL),
+                       PHM_PlatformCaps_CombinePCCWithThermalSignal
+                 );
+
+       set_hw_cap(
+                       hwmgr,
+                       0 != (powerplay_caps & ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE),
+                       PHM_PlatformCaps_LoadPostProductionFirmware
+                 );
+
+       return 0;
+}
+
+/**
+ * Private Function to get the PowerPlay Table Address.
+ */
+const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
+{
+       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
+
+       u16 size;
+       u8 frev, crev;
+       void *table_address = (void *)hwmgr->soft_pp_table;
+
+       if (!table_address) {
+               table_address = (ATOM_Tonga_POWERPLAYTABLE *)
+                               cgs_atom_get_data_table(hwmgr->device,
+                                               index, &size, &frev, &crev);
+               hwmgr->soft_pp_table = table_address;   /*Cache the result in RAM.*/
+               hwmgr->soft_pp_table_size = size;
+       }
+
+       return table_address;
+}
+
+static int get_vddc_lookup_table(
+               struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_voltage_lookup_table **lookup_table,
+               const ATOM_Tonga_Voltage_Lookup_Table *vddc_lookup_pp_tables,
+               uint32_t max_levels
+               )
+{
+       uint32_t table_size, i;
+       phm_ppt_v1_voltage_lookup_table *table;
+       phm_ppt_v1_voltage_lookup_record *record;
+       ATOM_Tonga_Voltage_Lookup_Record *atom_record;
+
+       PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries),
+               "Invalid CAC Leakage PowerPlay Table!", return 1);
+
+       table_size = sizeof(uint32_t) +
+               sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels;
+
+       table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == table)
+               return -ENOMEM;
+
+       memset(table, 0x00, table_size);
+
+       table->count = vddc_lookup_pp_tables->ucNumEntries;
+
+       for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) {
+               record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       phm_ppt_v1_voltage_lookup_record,
+                                       entries, table, i);
+               atom_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_Voltage_Lookup_Record,
+                                       entries, vddc_lookup_pp_tables, i);
+               record->us_calculated = 0;
+               record->us_vdd = atom_record->usVdd;
+               record->us_cac_low = atom_record->usCACLow;
+               record->us_cac_mid = atom_record->usCACMid;
+               record->us_cac_high = atom_record->usCACHigh;
+       }
+
+       *lookup_table = table;
+
+       return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Initialize Platform Power Management Parameter table
+ * @param hwmgr Pointer to the hardware manager.
+ * @param atom_ppm_table Pointer to PPM table in VBIOS
+ */
+static int get_platform_power_management_table(
+               struct pp_hwmgr *hwmgr,
+               ATOM_Tonga_PPM_Table *atom_ppm_table)
+{
+       struct phm_ppm_table *ptr = kzalloc(sizeof(ATOM_Tonga_PPM_Table), GFP_KERNEL);
+       struct phm_ppt_v1_information *pp_table_information =
+               (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (NULL == ptr)
+               return -ENOMEM;
+
+       ptr->ppm_design
+               = atom_ppm_table->ucPpmDesign;
+       ptr->cpu_core_number
+               = atom_ppm_table->usCpuCoreNumber;
+       ptr->platform_tdp
+               = atom_ppm_table->ulPlatformTDP;
+       ptr->small_ac_platform_tdp
+               = atom_ppm_table->ulSmallACPlatformTDP;
+       ptr->platform_tdc
+               = atom_ppm_table->ulPlatformTDC;
+       ptr->small_ac_platform_tdc
+               = atom_ppm_table->ulSmallACPlatformTDC;
+       ptr->apu_tdp
+               = atom_ppm_table->ulApuTDP;
+       ptr->dgpu_tdp
+               = atom_ppm_table->ulDGpuTDP;
+       ptr->dgpu_ulv_power
+               = atom_ppm_table->ulDGpuUlvPower;
+       ptr->tj_max
+               = atom_ppm_table->ulTjmax;
+
+       pp_table_information->ppm_parameter_table = ptr;
+
+       return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Initialize TDP limits for DPM2
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ */
+static int init_dpm_2_parameters(
+               struct pp_hwmgr *hwmgr,
+               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+               )
+{
+       int result = 0;
+       struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       ATOM_Tonga_PPM_Table *atom_ppm_table;
+       uint32_t disable_ppm = 0;
+       uint32_t disable_power_control = 0;
+
+       pp_table_information->us_ulv_voltage_offset =
+               le16_to_cpu(powerplay_table->usUlvVoltageOffset);
+
+       pp_table_information->ppm_parameter_table = NULL;
+       pp_table_information->vddc_lookup_table = NULL;
+       pp_table_information->vddgfx_lookup_table = NULL;
+       /* TDP limits */
+       hwmgr->platform_descriptor.TDPODLimit =
+               le16_to_cpu(powerplay_table->usPowerControlLimit);
+       hwmgr->platform_descriptor.TDPAdjustment = 0;
+       hwmgr->platform_descriptor.VidAdjustment = 0;
+       hwmgr->platform_descriptor.VidAdjustmentPolarity = 0;
+       hwmgr->platform_descriptor.VidMinLimit = 0;
+       hwmgr->platform_descriptor.VidMaxLimit = 1500000;
+       hwmgr->platform_descriptor.VidStep = 6250;
+
+       disable_power_control = 0;
+       if (0 == disable_power_control) {
+               /* enable TDP overdrive (PowerControl) feature as well if supported */
+               if (hwmgr->platform_descriptor.TDPODLimit != 0)
+                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerControl);
+       }
+
+       if (0 != powerplay_table->usVddcLookupTableOffset) {
+               const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable =
+                       (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
+                       le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
+
+               result = get_vddc_lookup_table(hwmgr,
+                       &pp_table_information->vddc_lookup_table, pVddcCACTable, 16);
+       }
+
+       if (0 != powerplay_table->usVddgfxLookupTableOffset) {
+               const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable =
+                       (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
+                       le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset));
+
+               result = get_vddc_lookup_table(hwmgr,
+                       &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16);
+       }
+
+       disable_ppm = 0;
+       if (0 == disable_ppm) {
+               atom_ppm_table = (ATOM_Tonga_PPM_Table *)
+                       (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset));
+
+               if (0 != powerplay_table->usPPMTableOffset) {
+                       if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) {
+                               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_EnablePlatformPowerManagement);
+                       }
+               }
+       }
+
+       return result;
+}
+
+static int get_valid_clk(
+               struct pp_hwmgr *hwmgr,
+               struct phm_clock_array **clk_table,
+               phm_ppt_v1_clock_voltage_dependency_table const *clk_volt_pp_table
+               )
+{
+       uint32_t table_size, i;
+       struct phm_clock_array *table;
+       phm_ppt_v1_clock_voltage_dependency_record *dep_record;
+
+       PP_ASSERT_WITH_CODE((0 != clk_volt_pp_table->count),
+               "Invalid PowerPlay Table!", return -1);
+
+       table_size = sizeof(uint32_t) +
+               sizeof(uint32_t) * clk_volt_pp_table->count;
+
+       table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == table)
+               return -ENOMEM;
+
+       memset(table, 0x00, table_size);
+
+       table->count = (uint32_t)clk_volt_pp_table->count;
+
+       for (i = 0; i < table->count; i++) {
+               dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                               phm_ppt_v1_clock_voltage_dependency_record,
+                               entries, clk_volt_pp_table, i);
+               table->values[i] = (uint32_t)dep_record->clk;
+       }
+       *clk_table = table;
+
+       return 0;
+}
+
+static int get_hard_limits(
+               struct pp_hwmgr *hwmgr,
+               struct phm_clock_and_voltage_limits *limits,
+               ATOM_Tonga_Hard_Limit_Table const *limitable
+               )
+{
+       PP_ASSERT_WITH_CODE((0 != limitable->ucNumEntries), "Invalid PowerPlay Table!", return -1);
+
+       /* currently we always take entries[0] parameters */
+       limits->sclk = (uint32_t)limitable->entries[0].ulSCLKLimit;
+       limits->mclk = (uint32_t)limitable->entries[0].ulMCLKLimit;
+       limits->vddc = (uint16_t)limitable->entries[0].usVddcLimit;
+       limits->vddci = (uint16_t)limitable->entries[0].usVddciLimit;
+       limits->vddgfx = (uint16_t)limitable->entries[0].usVddgfxLimit;
+
+       return 0;
+}
+
+static int get_mclk_voltage_dependency_table(
+               struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_mclk_dep_table,
+               ATOM_Tonga_MCLK_Dependency_Table const *mclk_dep_table
+               )
+{
+       uint32_t table_size, i;
+       phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
+       phm_ppt_v1_clock_voltage_dependency_record *mclk_table_record;
+       ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
+
+       PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries),
+               "Invalid PowerPlay Table!", return -1);
+
+       table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+               * mclk_dep_table->ucNumEntries;
+
+       mclk_table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == mclk_table)
+               return -ENOMEM;
+
+       memset(mclk_table, 0x00, table_size);
+
+       mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
+
+       for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
+               mclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       phm_ppt_v1_clock_voltage_dependency_record,
+                                               entries, mclk_table, i);
+               mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_MCLK_Dependency_Record,
+                                               entries, mclk_dep_table, i);
+               mclk_table_record->vddInd = mclk_dep_record->ucVddcInd;
+               mclk_table_record->vdd_offset = mclk_dep_record->usVddgfxOffset;
+               mclk_table_record->vddci = mclk_dep_record->usVddci;
+               mclk_table_record->mvdd = mclk_dep_record->usMvdd;
+               mclk_table_record->clk = mclk_dep_record->ulMclk;
+       }
+
+       *pp_tonga_mclk_dep_table = mclk_table;
+
+       return 0;
+}
+
+static int get_sclk_voltage_dependency_table(
+               struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_sclk_dep_table,
+               PPTable_Generic_SubTable_Header const  *sclk_dep_table
+               )
+{
+       uint32_t table_size, i;
+       phm_ppt_v1_clock_voltage_dependency_table *sclk_table;
+       phm_ppt_v1_clock_voltage_dependency_record *sclk_table_record;
+
+       if (sclk_dep_table->ucRevId < 1) {
+               const ATOM_Tonga_SCLK_Dependency_Table *tonga_table =
+                           (ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table;
+               ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record;
+
+               PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries),
+                       "Invalid PowerPlay Table!", return -1);
+
+               table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+                       * tonga_table->ucNumEntries;
+
+               sclk_table = kzalloc(table_size, GFP_KERNEL);
+
+               if (NULL == sclk_table)
+                       return -ENOMEM;
+
+               memset(sclk_table, 0x00, table_size);
+
+               sclk_table->count = (uint32_t)tonga_table->ucNumEntries;
+
+               for (i = 0; i < tonga_table->ucNumEntries; i++) {
+                       sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Tonga_SCLK_Dependency_Record,
+                                               entries, tonga_table, i);
+                       sclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               phm_ppt_v1_clock_voltage_dependency_record,
+                                               entries, sclk_table, i);
+                       sclk_table_record->vddInd = sclk_dep_record->ucVddInd;
+                       sclk_table_record->vdd_offset = sclk_dep_record->usVddcOffset;
+                       sclk_table_record->clk = sclk_dep_record->ulSclk;
+                       sclk_table_record->cks_enable =
+                               (((sclk_dep_record->ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
+                       sclk_table_record->cks_voffset = (sclk_dep_record->ucCKSVOffsetandDisable & 0x7F);
+               }
+       } else {
+               const ATOM_Polaris_SCLK_Dependency_Table *polaris_table =
+                           (ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table;
+               ATOM_Polaris_SCLK_Dependency_Record *sclk_dep_record;
+
+               PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries),
+                       "Invalid PowerPlay Table!", return -1);
+
+               table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
+                       * polaris_table->ucNumEntries;
+
+               sclk_table = kzalloc(table_size, GFP_KERNEL);
+
+               if (NULL == sclk_table)
+                       return -ENOMEM;
+
+               memset(sclk_table, 0x00, table_size);
+
+               sclk_table->count = (uint32_t)polaris_table->ucNumEntries;
+
+               for (i = 0; i < polaris_table->ucNumEntries; i++) {
+                       sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Polaris_SCLK_Dependency_Record,
+                                               entries, polaris_table, i);
+                       sclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               phm_ppt_v1_clock_voltage_dependency_record,
+                                               entries, sclk_table, i);
+                       sclk_table_record->vddInd = sclk_dep_record->ucVddInd;
+                       sclk_table_record->vdd_offset = sclk_dep_record->usVddcOffset;
+                       sclk_table_record->clk = sclk_dep_record->ulSclk;
+                       sclk_table_record->cks_enable =
+                               (((sclk_dep_record->ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
+                       sclk_table_record->cks_voffset = (sclk_dep_record->ucCKSVOffsetandDisable & 0x7F);
+                       sclk_table_record->sclk_offset = sclk_dep_record->ulSclkOffset;
+               }
+       }
+       *pp_tonga_sclk_dep_table = sclk_table;
+
+       return 0;
+}
+
+static int get_pcie_table(
+               struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_pcie_table **pp_tonga_pcie_table,
+               PPTable_Generic_SubTable_Header const *ptable
+               )
+{
+       uint32_t table_size, i, pcie_count;
+       phm_ppt_v1_pcie_table *pcie_table;
+       struct phm_ppt_v1_information *pp_table_information =
+               (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_pcie_record *pcie_record;
+
+       if (ptable->ucRevId < 1) {
+               const ATOM_Tonga_PCIE_Table *atom_pcie_table = (ATOM_Tonga_PCIE_Table *)ptable;
+               ATOM_Tonga_PCIE_Record *atom_pcie_record;
+
+               PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
+                       "Invalid PowerPlay Table!", return -1);
+
+               table_size = sizeof(uint32_t) +
+                       sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
+
+               pcie_table = kzalloc(table_size, GFP_KERNEL);
+
+               if (pcie_table == NULL)
+                       return -ENOMEM;
+
+               memset(pcie_table, 0x00, table_size);
+
+               /*
+               * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
+               * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
+               */
+               pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
+               if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
+                       pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
+               else
+                       printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \
+                       Disregarding the excess entries... \n");
+
+               pcie_table->count = pcie_count;
+               for (i = 0; i < pcie_count; i++) {
+                       pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               phm_ppt_v1_pcie_record,
+                                               entries, pcie_table, i);
+                       atom_pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Tonga_PCIE_Record,
+                                               entries, atom_pcie_table, i);
+                       pcie_record->gen_speed = atom_pcie_record->ucPCIEGenSpeed;
+                       pcie_record->lane_width = atom_pcie_record->usPCIELaneWidth;
+               }
+
+               *pp_tonga_pcie_table = pcie_table;
+       } else {
+               /* Polaris10/Polaris11 and newer. */
+               const ATOM_Polaris10_PCIE_Table *atom_pcie_table = (ATOM_Polaris10_PCIE_Table *)ptable;
+               ATOM_Polaris10_PCIE_Record *atom_pcie_record;
+
+               PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
+                       "Invalid PowerPlay Table!", return -1);
+
+               table_size = sizeof(uint32_t) +
+                       sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
+
+               pcie_table = kzalloc(table_size, GFP_KERNEL);
+
+               if (pcie_table == NULL)
+                       return -ENOMEM;
+
+               memset(pcie_table, 0x00, table_size);
+
+               /*
+               * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
+               * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
+               */
+               pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
+               if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
+                       pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
+               else
+                       printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \
+                       Disregarding the excess entries... \n");
+
+               pcie_table->count = pcie_count;
+
+               for (i = 0; i < pcie_count; i++) {
+                       pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               phm_ppt_v1_pcie_record,
+                                               entries, pcie_table, i);
+                       atom_pcie_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Polaris10_PCIE_Record,
+                                               entries, atom_pcie_table, i);
+                       pcie_record->gen_speed = atom_pcie_record->ucPCIEGenSpeed;
+                       pcie_record->lane_width = atom_pcie_record->usPCIELaneWidth;
+                       pcie_record->pcie_sclk = atom_pcie_record->ulPCIE_Sclk;
+               }
+
+               *pp_tonga_pcie_table = pcie_table;
+       }
+
+       return 0;
+}
+
+static int get_cac_tdp_table(
+               struct pp_hwmgr *hwmgr,
+               struct phm_cac_tdp_table **cac_tdp_table,
+               const PPTable_Generic_SubTable_Header * table
+               )
+{
+       uint32_t table_size;
+       struct phm_cac_tdp_table *tdp_table;
+
+       table_size = sizeof(uint32_t) + sizeof(struct phm_cac_tdp_table);
+       tdp_table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == tdp_table)
+               return -ENOMEM;
+
+       memset(tdp_table, 0x00, table_size);
+
+       hwmgr->dyn_state.cac_dtp_table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == hwmgr->dyn_state.cac_dtp_table) {
+               kfree(tdp_table);
+               return -ENOMEM;
+       }
+
+       memset(hwmgr->dyn_state.cac_dtp_table, 0x00, table_size);
+
+       if (table->ucRevId < 3) {
+               const ATOM_Tonga_PowerTune_Table *tonga_table =
+                       (ATOM_Tonga_PowerTune_Table *)table;
+               tdp_table->usTDP = tonga_table->usTDP;
+               tdp_table->usConfigurableTDP =
+                       tonga_table->usConfigurableTDP;
+               tdp_table->usTDC = tonga_table->usTDC;
+               tdp_table->usBatteryPowerLimit =
+                       tonga_table->usBatteryPowerLimit;
+               tdp_table->usSmallPowerLimit =
+                       tonga_table->usSmallPowerLimit;
+               tdp_table->usLowCACLeakage =
+                       tonga_table->usLowCACLeakage;
+               tdp_table->usHighCACLeakage =
+                       tonga_table->usHighCACLeakage;
+               tdp_table->usMaximumPowerDeliveryLimit =
+                       tonga_table->usMaximumPowerDeliveryLimit;
+               tdp_table->usDefaultTargetOperatingTemp =
+                       tonga_table->usTjMax;
+               tdp_table->usTargetOperatingTemp =
+                       tonga_table->usTjMax; /*Set the initial temp to the same as default */
+               tdp_table->usPowerTuneDataSetID =
+                       tonga_table->usPowerTuneDataSetID;
+               tdp_table->usSoftwareShutdownTemp =
+                       tonga_table->usSoftwareShutdownTemp;
+               tdp_table->usClockStretchAmount =
+                       tonga_table->usClockStretchAmount;
+       } else {   /* Fiji and newer */
+               const ATOM_Fiji_PowerTune_Table *fijitable =
+                       (ATOM_Fiji_PowerTune_Table *)table;
+               tdp_table->usTDP = fijitable->usTDP;
+               tdp_table->usConfigurableTDP = fijitable->usConfigurableTDP;
+               tdp_table->usTDC = fijitable->usTDC;
+               tdp_table->usBatteryPowerLimit = fijitable->usBatteryPowerLimit;
+               tdp_table->usSmallPowerLimit = fijitable->usSmallPowerLimit;
+               tdp_table->usLowCACLeakage = fijitable->usLowCACLeakage;
+               tdp_table->usHighCACLeakage = fijitable->usHighCACLeakage;
+               tdp_table->usMaximumPowerDeliveryLimit =
+                       fijitable->usMaximumPowerDeliveryLimit;
+               tdp_table->usDefaultTargetOperatingTemp =
+                       fijitable->usTjMax;
+               tdp_table->usTargetOperatingTemp =
+                       fijitable->usTjMax; /*Set the initial temp to the same as default */
+               tdp_table->usPowerTuneDataSetID =
+                       fijitable->usPowerTuneDataSetID;
+               tdp_table->usSoftwareShutdownTemp =
+                       fijitable->usSoftwareShutdownTemp;
+               tdp_table->usClockStretchAmount =
+                       fijitable->usClockStretchAmount;
+               tdp_table->usTemperatureLimitHotspot =
+                       fijitable->usTemperatureLimitHotspot;
+               tdp_table->usTemperatureLimitLiquid1 =
+                       fijitable->usTemperatureLimitLiquid1;
+               tdp_table->usTemperatureLimitLiquid2 =
+                       fijitable->usTemperatureLimitLiquid2;
+               tdp_table->usTemperatureLimitVrVddc =
+                       fijitable->usTemperatureLimitVrVddc;
+               tdp_table->usTemperatureLimitVrMvdd =
+                       fijitable->usTemperatureLimitVrMvdd;
+               tdp_table->usTemperatureLimitPlx =
+                       fijitable->usTemperatureLimitPlx;
+               tdp_table->ucLiquid1_I2C_address =
+                       fijitable->ucLiquid1_I2C_address;
+               tdp_table->ucLiquid2_I2C_address =
+                       fijitable->ucLiquid2_I2C_address;
+               tdp_table->ucLiquid_I2C_Line =
+                       fijitable->ucLiquid_I2C_Line;
+               tdp_table->ucVr_I2C_address = fijitable->ucVr_I2C_address;
+               tdp_table->ucVr_I2C_Line = fijitable->ucVr_I2C_Line;
+               tdp_table->ucPlx_I2C_address = fijitable->ucPlx_I2C_address;
+               tdp_table->ucPlx_I2C_Line = fijitable->ucPlx_I2C_Line;
+       }
+
+       *cac_tdp_table = tdp_table;
+
+       return 0;
+}
+
+static int get_mm_clock_voltage_table(
+               struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_mm_clock_voltage_dependency_table **tonga_mm_table,
+               const ATOM_Tonga_MM_Dependency_Table * mm_dependency_table
+               )
+{
+       uint32_t table_size, i;
+       const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record;
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
+       phm_ppt_v1_mm_clock_voltage_dependency_record *mm_table_record;
+
+       PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries),
+               "Invalid PowerPlay Table!", return -1);
+       table_size = sizeof(uint32_t) +
+               sizeof(phm_ppt_v1_mm_clock_voltage_dependency_record)
+               * mm_dependency_table->ucNumEntries;
+       mm_table = kzalloc(table_size, GFP_KERNEL);
+
+       if (NULL == mm_table)
+               return -ENOMEM;
+
+       memset(mm_table, 0x00, table_size);
+
+       mm_table->count = mm_dependency_table->ucNumEntries;
+
+       for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
+               mm_dependency_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Tonga_MM_Dependency_Record,
+                                               entries, mm_dependency_table, i);
+               mm_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       phm_ppt_v1_mm_clock_voltage_dependency_record,
+                                       entries, mm_table, i);
+               mm_table_record->vddcInd = mm_dependency_record->ucVddcInd;
+               mm_table_record->vddgfx_offset = mm_dependency_record->usVddgfxOffset;
+               mm_table_record->aclk = mm_dependency_record->ulAClk;
+               mm_table_record->samclock = mm_dependency_record->ulSAMUClk;
+               mm_table_record->eclk = mm_dependency_record->ulEClk;
+               mm_table_record->vclk = mm_dependency_record->ulVClk;
+               mm_table_record->dclk = mm_dependency_record->ulDClk;
+       }
+
+       *tonga_mm_table = mm_table;
+
+       return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Initialize clock voltage dependency
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ */
+static int init_clock_voltage_dependency(
+               struct pp_hwmgr *hwmgr,
+               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+               )
+{
+       int result = 0;
+       struct phm_ppt_v1_information *pp_table_information =
+               (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table =
+               (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
+       const PPTable_Generic_SubTable_Header *pPowerTuneTable =
+               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
+       const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
+               (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+       const PPTable_Generic_SubTable_Header *sclk_dep_table =
+               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
+       const ATOM_Tonga_Hard_Limit_Table *pHardLimits =
+               (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usHardLimitTableOffset));
+       const PPTable_Generic_SubTable_Header *pcie_table =
+               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
+               le16_to_cpu(powerplay_table->usPCIETableOffset));
+
+       pp_table_information->vdd_dep_on_sclk = NULL;
+       pp_table_information->vdd_dep_on_mclk = NULL;
+       pp_table_information->mm_dep_table = NULL;
+       pp_table_information->pcie_table = NULL;
+
+       if (powerplay_table->usMMDependencyTableOffset != 0)
+               result = get_mm_clock_voltage_table(hwmgr,
+               &pp_table_information->mm_dep_table, mm_dependency_table);
+
+       if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0)
+               result = get_cac_tdp_table(hwmgr,
+               &pp_table_information->cac_dtp_table, pPowerTuneTable);
+
+       if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0)
+               result = get_sclk_voltage_dependency_table(hwmgr,
+               &pp_table_information->vdd_dep_on_sclk, sclk_dep_table);
+
+       if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0)
+               result = get_mclk_voltage_dependency_table(hwmgr,
+               &pp_table_information->vdd_dep_on_mclk, mclk_dep_table);
+
+       if (result == 0 && powerplay_table->usPCIETableOffset != 0)
+               result = get_pcie_table(hwmgr,
+               &pp_table_information->pcie_table, pcie_table);
+
+       if (result == 0 && powerplay_table->usHardLimitTableOffset != 0)
+               result = get_hard_limits(hwmgr,
+               &pp_table_information->max_clock_voltage_on_dc, pHardLimits);
+
+       hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
+               pp_table_information->max_clock_voltage_on_dc.sclk;
+       hwmgr->dyn_state.max_clock_voltage_on_dc.mclk =
+               pp_table_information->max_clock_voltage_on_dc.mclk;
+       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
+               pp_table_information->max_clock_voltage_on_dc.vddc;
+       hwmgr->dyn_state.max_clock_voltage_on_dc.vddci =
+               pp_table_information->max_clock_voltage_on_dc.vddci;
+
+       if (result == 0 && (NULL != pp_table_information->vdd_dep_on_mclk)
+               && (0 != pp_table_information->vdd_dep_on_mclk->count))
+               result = get_valid_clk(hwmgr, &pp_table_information->valid_mclk_values,
+               pp_table_information->vdd_dep_on_mclk);
+
+       if (result == 0 && (NULL != pp_table_information->vdd_dep_on_sclk)
+               && (0 != pp_table_information->vdd_dep_on_sclk->count))
+               result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values,
+               pp_table_information->vdd_dep_on_sclk);
+
+       return result;
+}
+
+/** Retrieves the (signed) Overdrive limits from VBIOS.
+ * The max engine clock, memory clock and max temperature come from the firmware info table.
+ *
+ * The information is placed into the platform descriptor.
+ *
+ * @param hwmgr source of the VBIOS table and owner of the platform descriptor to be updated.
+ * @param powerplay_table the address of the PowerPlay table.
+ *
+ * @return 1 as long as the firmware info table was present and of a supported version.
+ */
+static int init_over_drive_limits(
+               struct pp_hwmgr *hwmgr,
+               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table)
+{
+       hwmgr->platform_descriptor.overdriveLimit.engineClock =
+               le16_to_cpu(powerplay_table->ulMaxODEngineClock);
+       hwmgr->platform_descriptor.overdriveLimit.memoryClock =
+               le16_to_cpu(powerplay_table->ulMaxODMemoryClock);
+
+       hwmgr->platform_descriptor.minOverdriveVDDC = 0;
+       hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
+       hwmgr->platform_descriptor.overdriveVDDCStep = 0;
+
+       if (hwmgr->platform_descriptor.overdriveLimit.engineClock > 0 \
+               && hwmgr->platform_descriptor.overdriveLimit.memoryClock > 0) {
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ACOverdriveSupport);
+       }
+
+       return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Inspect the PowerPlay table for obvious signs of corruption.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ * @exception This implementation always returns 1.
+ */
+static int init_thermal_controller(
+               struct pp_hwmgr *hwmgr,
+               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+               )
+{
+       const PPTable_Generic_SubTable_Header *fan_table;
+       ATOM_Tonga_Thermal_Controller *thermal_controller;
+
+       thermal_controller = (ATOM_Tonga_Thermal_Controller *)
+               (((unsigned long)powerplay_table) +
+               le16_to_cpu(powerplay_table->usThermalControllerOffset));
+       PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset),
+               "Thermal controller table not set!", return -1);
+
+       hwmgr->thermal_controller.ucType = thermal_controller->ucType;
+       hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
+       hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
+
+       hwmgr->thermal_controller.fanInfo.bNoFan =
+               (0 != (thermal_controller->ucFanParameters & ATOM_TONGA_PP_FANPARAMETERS_NOFAN));
+
+       hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
+               thermal_controller->ucFanParameters &
+               ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
+
+       hwmgr->thermal_controller.fanInfo.ulMinRPM
+               = thermal_controller->ucFanMinRPM * 100UL;
+       hwmgr->thermal_controller.fanInfo.ulMaxRPM
+               = thermal_controller->ucFanMaxRPM * 100UL;
+
+       set_hw_cap(
+                       hwmgr,
+                       ATOM_TONGA_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
+                       PHM_PlatformCaps_ThermalController
+                 );
+
+       if (0 == powerplay_table->usFanTableOffset)
+               return 0;
+
+       fan_table = (const PPTable_Generic_SubTable_Header *)
+               (((unsigned long)powerplay_table) +
+               le16_to_cpu(powerplay_table->usFanTableOffset));
+
+       PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset),
+               "Fan table not set!", return -1);
+       PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId),
+               "Unsupported fan table format!", return -1);
+
+       hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay
+               = 100000;
+       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+               PHM_PlatformCaps_MicrocodeFanControl);
+
+       if (fan_table->ucRevId < 8) {
+               const ATOM_Tonga_Fan_Table *tonga_fan_table =
+                       (ATOM_Tonga_Fan_Table *)fan_table;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
+                       = tonga_fan_table->ucTHyst;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMin
+                       = tonga_fan_table->usTMin;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMed
+                       = tonga_fan_table->usTMed;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
+                       = tonga_fan_table->usTHigh;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
+                       = tonga_fan_table->usPWMMin;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
+                       = tonga_fan_table->usPWMMed;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
+                       = tonga_fan_table->usPWMHigh;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+                       = 10900;                  /* hard coded */
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+                       = tonga_fan_table->usTMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
+                       = tonga_fan_table->ucFanControlMode;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
+                       = tonga_fan_table->usFanPWMMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
+                       = 4836;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
+                       = tonga_fan_table->usFanOutputSensitivity;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
+                       = tonga_fan_table->usFanRPMMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
+                       = (tonga_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places.  SMC wants MHz. */
+               hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
+                       = tonga_fan_table->ucTargetTemperature;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
+                       = tonga_fan_table->ucMinimumPWMLimit;
+       } else {
+               const ATOM_Fiji_Fan_Table *fiji_fan_table =
+                       (ATOM_Fiji_Fan_Table *)fan_table;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
+                       = fiji_fan_table->ucTHyst;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMin
+                       = fiji_fan_table->usTMin;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMed
+                       = fiji_fan_table->usTMed;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
+                       = fiji_fan_table->usTHigh;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
+                       = fiji_fan_table->usPWMMin;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
+                       = fiji_fan_table->usPWMMed;
+               hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
+                       = fiji_fan_table->usPWMHigh;
+               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
+                       = fiji_fan_table->usTMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
+                       = fiji_fan_table->ucFanControlMode;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
+                       = fiji_fan_table->usFanPWMMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
+                       = 4836;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
+                       = fiji_fan_table->usFanOutputSensitivity;
+               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
+                       = fiji_fan_table->usFanRPMMax;
+               hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
+                       = (fiji_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places.  SMC wants MHz. */
+               hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
+                       = fiji_fan_table->ucTargetTemperature;
+               hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
+                       = fiji_fan_table->ucMinimumPWMLimit;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge
+                       = fiji_fan_table->usFanGainEdge;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot
+                       = fiji_fan_table->usFanGainHotspot;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid
+                       = fiji_fan_table->usFanGainLiquid;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc
+                       = fiji_fan_table->usFanGainVrVddc;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd
+                       = fiji_fan_table->usFanGainVrMvdd;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx
+                       = fiji_fan_table->usFanGainPlx;
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm
+                       = fiji_fan_table->usFanGainHbm;
+       }
+
+       return 0;
+}
+
+/**
+ * Private Function used during initialization.
+ * Inspect the PowerPlay table for obvious signs of corruption.
+ * @param hwmgr Pointer to the hardware manager.
+ * @param powerplay_table Pointer to the PowerPlay Table.
+ * @exception 2 if the powerplay table is incorrect.
+ */
+static int check_powerplay_tables(
+               struct pp_hwmgr *hwmgr,
+               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
+               )
+{
+       const ATOM_Tonga_State_Array *state_arrays;
+
+       state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) +
+               le16_to_cpu(powerplay_table->usStateArrayOffset));
+
+       PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <=
+               powerplay_table->sHeader.ucTableFormatRevision),
+               "Unsupported PPTable format!", return -1);
+       PP_ASSERT_WITH_CODE((0 != powerplay_table->usStateArrayOffset),
+               "State table is not set!", return -1);
+       PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize),
+               "Invalid PowerPlay Table!", return -1);
+       PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
+               "Invalid PowerPlay Table!", return -1);
+
+       return 0;
+}
+
+int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr)
+{
+       int result = 0;
+       const ATOM_Tonga_POWERPLAYTABLE *powerplay_table;
+
+       hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v1_information), GFP_KERNEL);
+
+       PP_ASSERT_WITH_CODE((NULL != hwmgr->pptable),
+                           "Failed to allocate hwmgr->pptable!", return -ENOMEM);
+
+       memset(hwmgr->pptable, 0x00, sizeof(struct phm_ppt_v1_information));
+
+       powerplay_table = get_powerplay_table(hwmgr);
+
+       PP_ASSERT_WITH_CODE((NULL != powerplay_table),
+               "Missing PowerPlay Table!", return -1);
+
+       result = check_powerplay_tables(hwmgr, powerplay_table);
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "check_powerplay_tables failed", return result);
+
+       result = set_platform_caps(hwmgr,
+                                  le32_to_cpu(powerplay_table->ulPlatformCaps));
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "set_platform_caps failed", return result);
+
+       result = init_thermal_controller(hwmgr, powerplay_table);
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "init_thermal_controller failed", return result);
+
+       result = init_over_drive_limits(hwmgr, powerplay_table);
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "init_over_drive_limits failed", return result);
+
+       result = init_clock_voltage_dependency(hwmgr, powerplay_table);
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "init_clock_voltage_dependency failed", return result);
+
+       result = init_dpm_2_parameters(hwmgr, powerplay_table);
+
+       PP_ASSERT_WITH_CODE((result == 0),
+                           "init_dpm_2_parameters failed", return result);
+
+       return result;
+}
+
+int pp_tables_v1_0_uninitialize(struct pp_hwmgr *hwmgr)
+{
+       struct phm_ppt_v1_information *pp_table_information =
+               (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       kfree(pp_table_information->vdd_dep_on_sclk);
+       pp_table_information->vdd_dep_on_sclk = NULL;
+
+       kfree(pp_table_information->vdd_dep_on_mclk);
+       pp_table_information->vdd_dep_on_mclk = NULL;
+
+       kfree(pp_table_information->valid_mclk_values);
+       pp_table_information->valid_mclk_values = NULL;
+
+       kfree(pp_table_information->valid_sclk_values);
+       pp_table_information->valid_sclk_values = NULL;
+
+       kfree(pp_table_information->vddc_lookup_table);
+       pp_table_information->vddc_lookup_table = NULL;
+
+       kfree(pp_table_information->vddgfx_lookup_table);
+       pp_table_information->vddgfx_lookup_table = NULL;
+
+       kfree(pp_table_information->mm_dep_table);
+       pp_table_information->mm_dep_table = NULL;
+
+       kfree(pp_table_information->cac_dtp_table);
+       pp_table_information->cac_dtp_table = NULL;
+
+       kfree(hwmgr->dyn_state.cac_dtp_table);
+       hwmgr->dyn_state.cac_dtp_table = NULL;
+
+       kfree(pp_table_information->ppm_parameter_table);
+       pp_table_information->ppm_parameter_table = NULL;
+
+       kfree(pp_table_information->pcie_table);
+       pp_table_information->pcie_table = NULL;
+
+       kfree(hwmgr->pptable);
+       hwmgr->pptable = NULL;
+
+       return 0;
+}
+
+const struct pp_table_func pptable_v1_0_funcs = {
+       .pptable_init = pp_tables_v1_0_initialize,
+       .pptable_fini = pp_tables_v1_0_uninitialize,
+};
+
+int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr)
+{
+       ATOM_Tonga_State_Array const *state_arrays;
+       const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+
+       PP_ASSERT_WITH_CODE((NULL != pp_table),
+                       "Missing PowerPlay Table!", return -1);
+       PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >=
+                       ATOM_Tonga_TABLE_REVISION_TONGA),
+                       "Incorrect PowerPlay table revision!", return -1);
+
+       state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
+                       le16_to_cpu(pp_table->usStateArrayOffset));
+
+       return (uint32_t)(state_arrays->ucNumEntries);
+}
+
+/**
+* Private function to convert flags stored in the BIOS to software flags in PowerPlay.
+*/
+static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr,
+               uint16_t classification, uint16_t classification2)
+{
+       uint32_t result = 0;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
+               result |= PP_StateClassificationFlag_Boot;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
+               result |= PP_StateClassificationFlag_Thermal;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
+               result |= PP_StateClassificationFlag_LimitedPowerSource;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
+               result |= PP_StateClassificationFlag_Rest;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
+               result |= PP_StateClassificationFlag_Forced;
+
+       if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
+               result |= PP_StateClassificationFlag_ACPI;
+
+       if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
+               result |= PP_StateClassificationFlag_LimitedPowerSource_2;
+
+       return result;
+}
+
+static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr)
+{
+       const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+       const ATOM_Tonga_VCE_State_Table *vce_state_table =
+                               (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pp_table) + le16_to_cpu(pp_table->usVCEStateTableOffset));
+
+       if (vce_state_table == NULL)
+               return 0;
+
+       return vce_state_table->ucNumEntries;
+}
+
+static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i,
+               struct pp_vce_state *vce_state, void **clock_info, uint32_t *flag)
+{
+       const ATOM_Tonga_VCE_State_Record *vce_state_record;
+       ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record;
+       ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record;
+       ATOM_Tonga_MM_Dependency_Record *mm_dep_record;
+       const ATOM_Tonga_POWERPLAYTABLE *pptable = get_powerplay_table(hwmgr);
+       const ATOM_Tonga_VCE_State_Table *vce_state_table = (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pptable)
+                                                         + le16_to_cpu(pptable->usVCEStateTableOffset));
+       const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table = (ATOM_Tonga_SCLK_Dependency_Table *)(((unsigned long)pptable)
+                                                         + le16_to_cpu(pptable->usSclkDependencyTableOffset));
+       const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = (ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long)pptable)
+                                                         + le16_to_cpu(pptable->usMclkDependencyTableOffset));
+       const ATOM_Tonga_MM_Dependency_Table *mm_dep_table = (ATOM_Tonga_MM_Dependency_Table *)(((unsigned long)pptable)
+                                                         + le16_to_cpu(pptable->usMMDependencyTableOffset));
+
+       PP_ASSERT_WITH_CODE((i < vce_state_table->ucNumEntries),
+                        "Requested state entry ID is out of range!",
+                        return -EINVAL);
+
+       vce_state_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_VCE_State_Record,
+                                       entries, vce_state_table, i);
+       sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_SCLK_Dependency_Record,
+                                       entries, sclk_dep_table,
+                                       vce_state_record->ucSCLKIndex);
+       mm_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_MM_Dependency_Record,
+                                       entries, mm_dep_table,
+                                       vce_state_record->ucVCEClockIndex);
+       *flag = vce_state_record->ucFlag;
+
+       vce_state->evclk = mm_dep_record->ulEClk;
+       vce_state->ecclk = mm_dep_record->ulEClk;
+       vce_state->sclk = sclk_dep_record->ulSclk;
+
+       if (vce_state_record->ucMCLKIndex >= mclk_dep_table->ucNumEntries)
+               mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_MCLK_Dependency_Record,
+                                       entries, mclk_dep_table,
+                                       mclk_dep_table->ucNumEntries - 1);
+       else
+               mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                       ATOM_Tonga_MCLK_Dependency_Record,
+                                       entries, mclk_dep_table,
+                                       vce_state_record->ucMCLKIndex);
+
+       vce_state->mclk = mclk_dep_record->ulMclk;
+       return 0;
+}
+
+/**
+* Create a Power State out of an entry in the PowerPlay table.
+* This function is called by the hardware back-end.
+* @param hwmgr Pointer to the hardware manager.
+* @param entry_index The index of the entry to be extracted from the table.
+* @param power_state The address of the PowerState instance being created.
+* @return -1 if the entry cannot be retrieved.
+*/
+int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr,
+               uint32_t entry_index, struct pp_power_state *power_state,
+               int (*call_back_func)(struct pp_hwmgr *, void *,
+                               struct pp_power_state *, void *, uint32_t))
+{
+       int result = 0;
+       const ATOM_Tonga_State_Array *state_arrays;
+       const ATOM_Tonga_State *state_entry;
+       const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
+       int i, j;
+       uint32_t flags = 0;
+
+       PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1;);
+       power_state->classification.bios_index = entry_index;
+
+       if (pp_table->sHeader.ucTableFormatRevision >=
+                       ATOM_Tonga_TABLE_REVISION_TONGA) {
+               state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
+                               le16_to_cpu(pp_table->usStateArrayOffset));
+
+               PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset),
+                               "Invalid PowerPlay Table State Array Offset.", return -1);
+               PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
+                               "Invalid PowerPlay Table State Array.", return -1);
+               PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
+                               "Invalid PowerPlay Table State Array Entry.", return -1);
+
+               state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR(
+                                               ATOM_Tonga_State, entries,
+                                               state_arrays, entry_index);
+
+               result = call_back_func(hwmgr, (void *)state_entry, power_state,
+                               (void *)pp_table,
+                               make_classification_flags(hwmgr,
+                                       le16_to_cpu(state_entry->usClassification),
+                                       le16_to_cpu(state_entry->usClassification2)));
+       }
+
+       if (!result && (power_state->classification.flags &
+                       PP_StateClassificationFlag_Boot))
+               result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware));
+
+       hwmgr->num_vce_state_tables = i = ppt_get_num_of_vce_state_table_entries_v1_0(hwmgr);
+
+       if ((i != 0) && (i <= PP_MAX_VCE_LEVELS)) {
+               for (j = 0; j < i; j++)
+                       ppt_get_vce_state_table_entry_v1_0(hwmgr, j, &(hwmgr->vce_states[j]), NULL, &flags);
+       }
+
+       return result;
+}
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.h b/drivers/gpu/drm/amd/powerplay/hwmgr/process_pptables_v1_0.h
new file mode 100644 (file)
index 0000000..b9710ab
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 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 _PROCESSPPTABLES_V1_0_H
+#define _PROCESSPPTABLES_V1_0_H
+
+#include "hwmgr.h"
+
+extern const struct pp_table_func pptable_v1_0_funcs;
+extern int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr);
+extern int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t entry_index,
+               struct pp_power_state *power_state, int (*call_back_func)(struct pp_hwmgr *, void *,
+                               struct pp_power_state *, void *, uint32_t));
+
+#endif
+
index 6c321b0..ccf7ebe 100644 (file)
@@ -1523,7 +1523,7 @@ int get_number_of_vce_state_table_entries(
 
 int get_vce_state_table_entry(struct pp_hwmgr *hwmgr,
                                                        unsigned long i,
-                                                       struct PP_VCEState *vce_state,
+                                                       struct pp_vce_state *vce_state,
                                                        void **clock_info,
                                                        unsigned long *flag)
 {
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.c
new file mode 100644 (file)
index 0000000..6eb6db1
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2016 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 "smu7_hwmgr.h"
+#include "smu7_clockpowergating.h"
+#include "smu7_common.h"
+
+static int smu7_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
+                       PPSMC_MSG_UVDDPM_Enable :
+                       PPSMC_MSG_UVDDPM_Disable);
+}
+
+static int smu7_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
+                       PPSMC_MSG_VCEDPM_Enable :
+                       PPSMC_MSG_VCEDPM_Disable);
+}
+
+static int smu7_enable_disable_samu_dpm(struct pp_hwmgr *hwmgr, bool enable)
+{
+       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
+                       PPSMC_MSG_SAMUDPM_Enable :
+                       PPSMC_MSG_SAMUDPM_Disable);
+}
+
+static int smu7_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       if (!bgate)
+               smum_update_smc_table(hwmgr, SMU_UVD_TABLE);
+       return smu7_enable_disable_uvd_dpm(hwmgr, !bgate);
+}
+
+static int smu7_update_vce_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       if (!bgate)
+               smum_update_smc_table(hwmgr, SMU_VCE_TABLE);
+       return smu7_enable_disable_vce_dpm(hwmgr, !bgate);
+}
+
+static int smu7_update_samu_dpm(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       if (!bgate)
+               smum_update_smc_table(hwmgr, SMU_SAMU_TABLE);
+       return smu7_enable_disable_samu_dpm(hwmgr, !bgate);
+}
+
+int smu7_powerdown_uvd(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cf_want_uvd_power_gating(hwmgr))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_UVDPowerOFF);
+       return 0;
+}
+
+int smu7_powerup_uvd(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cf_want_uvd_power_gating(hwmgr)) {
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                 PHM_PlatformCaps_UVDDynamicPowerGating)) {
+                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_UVDPowerON, 1);
+               } else {
+                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_UVDPowerON, 0);
+               }
+       }
+
+       return 0;
+}
+
+int smu7_powerdown_vce(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cf_want_vce_power_gating(hwmgr))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_VCEPowerOFF);
+       return 0;
+}
+
+int smu7_powerup_vce(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cf_want_vce_power_gating(hwmgr))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_VCEPowerON);
+       return 0;
+}
+
+int smu7_powerdown_samu(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SamuPowerGating))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_SAMPowerOFF);
+       return 0;
+}
+
+int smu7_powerup_samu(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SamuPowerGating))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_SAMPowerON);
+       return 0;
+}
+
+int smu7_disable_clock_power_gating(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       data->uvd_power_gated = false;
+       data->vce_power_gated = false;
+       data->samu_power_gated = false;
+
+       smu7_powerup_uvd(hwmgr);
+       smu7_powerup_vce(hwmgr);
+       smu7_powerup_samu(hwmgr);
+
+       return 0;
+}
+
+int smu7_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       data->uvd_power_gated = bgate;
+
+       if (bgate) {
+               cgs_set_clockgating_state(hwmgr->device,
+                               AMD_IP_BLOCK_TYPE_UVD,
+                               AMD_CG_STATE_GATE);
+               smu7_update_uvd_dpm(hwmgr, true);
+               smu7_powerdown_uvd(hwmgr);
+       } else {
+               smu7_powerup_uvd(hwmgr);
+               smu7_update_uvd_dpm(hwmgr, false);
+               cgs_set_clockgating_state(hwmgr->device,
+                               AMD_IP_BLOCK_TYPE_UVD,
+                               AMD_CG_STATE_UNGATE);
+       }
+
+       return 0;
+}
+
+int smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->vce_power_gated == bgate)
+               return 0;
+
+       data->vce_power_gated = bgate;
+
+       if (bgate) {
+               cgs_set_clockgating_state(hwmgr->device,
+                               AMD_IP_BLOCK_TYPE_VCE,
+                               AMD_CG_STATE_GATE);
+               smu7_update_vce_dpm(hwmgr, true);
+               smu7_powerdown_vce(hwmgr);
+       } else {
+               smu7_powerup_vce(hwmgr);
+               smu7_update_vce_dpm(hwmgr, false);
+               cgs_set_clockgating_state(hwmgr->device,
+                               AMD_IP_BLOCK_TYPE_VCE,
+                               AMD_CG_STATE_UNGATE);
+       }
+       return 0;
+}
+
+int smu7_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->samu_power_gated == bgate)
+               return 0;
+
+       data->samu_power_gated = bgate;
+
+       if (bgate) {
+               smu7_update_samu_dpm(hwmgr, true);
+               smu7_powerdown_samu(hwmgr);
+       } else {
+               smu7_powerup_samu(hwmgr);
+               smu7_update_samu_dpm(hwmgr, false);
+       }
+
+       return 0;
+}
+
+int smu7_update_clock_gatings(struct pp_hwmgr *hwmgr,
+                                       const uint32_t *msg_id)
+{
+       PPSMC_Msg msg;
+       uint32_t value;
+
+       if (!(hwmgr->feature_mask & PP_ENABLE_GFX_CG_THRU_SMU))
+               return 0;
+
+       switch ((*msg_id & PP_GROUP_MASK) >> PP_GROUP_SHIFT) {
+       case PP_GROUP_GFX:
+               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
+               case PP_BLOCK_GFX_CG:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_CGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
+                                       ? PPSMC_MSG_EnableClockGatingFeature
+                                       : PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_CGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_GFX_3D:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_3DCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+
+                       if  (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_3DLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_GFX_RLC:
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_RLC_LS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_GFX_CP:
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_GFX_CP_LS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_GFX_MG:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = (CG_CPF_MGCG_MASK | CG_RLC_MGCG_MASK |
+                                               CG_GFX_OTHERS_MGCG_MASK);
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+               break;
+
+       case PP_GROUP_SYS:
+               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
+               case PP_BLOCK_SYS_BIF:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_BIF_MGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       if  (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_BIF_MGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_SYS_MC:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_MC_MGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_MC_MGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_SYS_DRM:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_CG ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_DRM_MGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_DRM_MGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_SYS_HDP:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_HDP_MGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_HDP_MGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_SYS_SDMA:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_SDMA_MGCG_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+
+                       if (PP_STATE_SUPPORT_LS & *msg_id) {
+                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_SDMA_MGLS_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               case PP_BLOCK_SYS_ROM:
+                       if (PP_STATE_SUPPORT_CG & *msg_id) {
+                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG) ?
+                                               PPSMC_MSG_EnableClockGatingFeature :
+                                               PPSMC_MSG_DisableClockGatingFeature;
+                               value = CG_SYS_ROM_MASK;
+
+                               if (smum_send_msg_to_smc_with_parameter(
+                                               hwmgr->smumgr, msg, value))
+                                       return -EINVAL;
+                       }
+                       break;
+
+               default:
+                       return -EINVAL;
+
+               }
+               break;
+
+       default:
+               return -EINVAL;
+
+       }
+
+       return 0;
+}
+
+/* This function is for Polaris11 only for now,
+ * Powerplay will only control the static per CU Power Gating.
+ * Dynamic per CU Power Gating will be done in gfx.
+ */
+int smu7_enable_per_cu_power_gating(struct pp_hwmgr *hwmgr, bool enable)
+{
+       struct cgs_system_info sys_info = {0};
+       uint32_t active_cus;
+       int result;
+
+       sys_info.size = sizeof(struct cgs_system_info);
+       sys_info.info_id = CGS_SYSTEM_INFO_GFX_CU_INFO;
+
+       result = cgs_query_system_info(hwmgr->device, &sys_info);
+
+       if (result)
+               return -EINVAL;
+
+       active_cus = sys_info.value;
+
+       if (enable)
+               return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_GFX_CU_PG_ENABLE, active_cus);
+       else
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_GFX_CU_PG_DISABLE);
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_clockpowergating.h
new file mode 100644 (file)
index 0000000..d52a28c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 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 _SMU7_CLOCK_POWER_GATING_H_
+#define _SMU7_CLOCK__POWER_GATING_H_
+
+#include "smu7_hwmgr.h"
+#include "pp_asicblocks.h"
+
+int smu7_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_powerdown_uvd(struct pp_hwmgr *hwmgr);
+int smu7_powergate_samu(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_powergate_acp(struct pp_hwmgr *hwmgr, bool bgate);
+int smu7_disable_clock_power_gating(struct pp_hwmgr *hwmgr);
+int smu7_update_clock_gatings(struct pp_hwmgr *hwmgr,
+                                       const uint32_t *msg_id);
+int smu7_enable_per_cu_power_gating(struct pp_hwmgr *hwmgr, bool enable);
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_dyn_defaults.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_dyn_defaults.h
new file mode 100644 (file)
index 0000000..f967613
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 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 _SMU7_DYN_DEFAULTS_H
+#define _SMU7_DYN_DEFAULTS_H
+
+
+/*  We need to fill in the default values */
+
+
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT0              0x3FFFC102
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT1              0x000400
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT2              0xC00080
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT3              0xC00200
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT4              0xC01680
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT5              0xC00033
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT6              0xC00033
+#define SMU7_VOTINGRIGHTSCLIENTS_DFLT7              0x3FFFC000
+
+
+#define SMU7_THERMALPROTECTCOUNTER_DFLT            0x200
+#define SMU7_STATICSCREENTHRESHOLDUNIT_DFLT        0
+#define SMU7_STATICSCREENTHRESHOLD_DFLT            0x00C8
+#define SMU7_GFXIDLECLOCKSTOPTHRESHOLD_DFLT        0x200
+#define SMU7_REFERENCEDIVIDER_DFLT                  4
+
+#define SMU7_ULVVOLTAGECHANGEDELAY_DFLT             1687
+
+#define SMU7_CGULVPARAMETER_DFLT                    0x00040035
+#define SMU7_CGULVCONTROL_DFLT                      0x00007450
+#define SMU7_TARGETACTIVITY_DFLT                     50
+#define SMU7_MCLK_TARGETACTIVITY_DFLT                10
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
new file mode 100644 (file)
index 0000000..508245d
--- /dev/null
@@ -0,0 +1,4359 @@
+/*
+ * Copyright 2015 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/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <asm/div64.h>
+#include "linux/delay.h"
+#include "pp_acpi.h"
+#include "pp_debug.h"
+#include "ppatomctrl.h"
+#include "atombios.h"
+#include "pptable_v1_0.h"
+#include "pppcielanes.h"
+#include "amd_pcie_helpers.h"
+#include "hardwaremanager.h"
+#include "process_pptables_v1_0.h"
+#include "cgs_common.h"
+
+#include "smu7_common.h"
+
+#include "hwmgr.h"
+#include "smu7_hwmgr.h"
+#include "smu7_powertune.h"
+#include "smu7_dyn_defaults.h"
+#include "smu7_thermal.h"
+#include "smu7_clockpowergating.h"
+#include "processpptables.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_CG_IND_START            0xc0030000
+#define SMC_CG_IND_END              0xc0040000
+
+#define VOLTAGE_SCALE               4
+#define VOLTAGE_VID_OFFSET_SCALE1   625
+#define VOLTAGE_VID_OFFSET_SCALE2   100
+
+#define MEM_FREQ_LOW_LATENCY        25000
+#define MEM_FREQ_HIGH_LATENCY       80000
+
+#define MEM_LATENCY_HIGH            45
+#define MEM_LATENCY_LOW             35
+#define MEM_LATENCY_ERR             0xFFFF
+
+#define MC_SEQ_MISC0_GDDR5_SHIFT 28
+#define MC_SEQ_MISC0_GDDR5_MASK  0xf0000000
+#define MC_SEQ_MISC0_GDDR5_VALUE 5
+
+#define PCIE_BUS_CLK                10000
+#define TCLK                        (PCIE_BUS_CLK / 10)
+
+
+/** Values for the CG_THERMAL_CTRL::DPM_EVENT_SRC field. */
+enum DPM_EVENT_SRC {
+       DPM_EVENT_SRC_ANALOG = 0,
+       DPM_EVENT_SRC_EXTERNAL = 1,
+       DPM_EVENT_SRC_DIGITAL = 2,
+       DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,
+       DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL = 4
+};
+
+static const unsigned long PhwVIslands_Magic = (unsigned long)(PHM_VIslands_Magic);
+
+struct smu7_power_state *cast_phw_smu7_power_state(
+                                 struct pp_hw_power_state *hw_ps)
+{
+       PP_ASSERT_WITH_CODE((PhwVIslands_Magic == hw_ps->magic),
+                               "Invalid Powerstate Type!",
+                                return NULL);
+
+       return (struct smu7_power_state *)hw_ps;
+}
+
+const struct smu7_power_state *cast_const_phw_smu7_power_state(
+                                const struct pp_hw_power_state *hw_ps)
+{
+       PP_ASSERT_WITH_CODE((PhwVIslands_Magic == hw_ps->magic),
+                               "Invalid Powerstate Type!",
+                                return NULL);
+
+       return (const struct smu7_power_state *)hw_ps;
+}
+
+/**
+ * Find the MC microcode version and store it in the HwMgr struct
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+int smu7_get_mc_microcode_version (struct pp_hwmgr *hwmgr)
+{
+       cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
+
+       hwmgr->microcode_version_info.MC = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
+
+       return 0;
+}
+
+uint16_t smu7_get_current_pcie_speed(struct pp_hwmgr *hwmgr)
+{
+       uint32_t speedCntl = 0;
+
+       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
+       speedCntl = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__PCIE,
+                       ixPCIE_LC_SPEED_CNTL);
+       return((uint16_t)PHM_GET_FIELD(speedCntl,
+                       PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
+}
+
+int smu7_get_current_pcie_lane_number(struct pp_hwmgr *hwmgr)
+{
+       uint32_t link_width;
+
+       /* mmPCIE_PORT_INDEX rename as mmPCIE_INDEX */
+       link_width = PHM_READ_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
+                       PCIE_LC_LINK_WIDTH_CNTL, LC_LINK_WIDTH_RD);
+
+       PP_ASSERT_WITH_CODE((7 >= link_width),
+                       "Invalid PCIe lane width!", return 0);
+
+       return decode_pcie_lane_width(link_width);
+}
+
+/**
+* Enable voltage control
+*
+* @param    pHwMgr  the address of the powerplay hardware manager.
+* @return   always PP_Result_OK
+*/
+int smu7_enable_smc_voltage_controller(struct pp_hwmgr *hwmgr)
+{
+       if (hwmgr->feature_mask & PP_SMC_VOLTAGE_CONTROL_MASK)
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Voltage_Cntl_Enable);
+
+       return 0;
+}
+
+/**
+* Checks if we want to support voltage control
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+*/
+static bool smu7_voltage_control(const struct pp_hwmgr *hwmgr)
+{
+       const struct smu7_hwmgr *data =
+                       (const struct smu7_hwmgr *)(hwmgr->backend);
+
+       return (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control);
+}
+
+/**
+* Enable voltage control
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+static int smu7_enable_voltage_control(struct pp_hwmgr *hwmgr)
+{
+       /* enable voltage control */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       GENERAL_PWRMGT, VOLT_PWRMGT_EN, 1);
+
+       return 0;
+}
+
+static int phm_get_svi2_voltage_table_v0(pp_atomctrl_voltage_table *voltage_table,
+               struct phm_clock_voltage_dependency_table *voltage_dependency_table
+               )
+{
+       uint32_t i;
+
+       PP_ASSERT_WITH_CODE((NULL != voltage_table),
+                       "Voltage Dependency Table empty.", return -EINVAL;);
+
+       voltage_table->mask_low = 0;
+       voltage_table->phase_delay = 0;
+       voltage_table->count = voltage_dependency_table->count;
+
+       for (i = 0; i < voltage_dependency_table->count; i++) {
+               voltage_table->entries[i].value =
+                       voltage_dependency_table->entries[i].v;
+               voltage_table->entries[i].smio_low = 0;
+       }
+
+       return 0;
+}
+
+
+/**
+* Create Voltage Tables.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+static int smu7_construct_voltage_tables(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)hwmgr->pptable;
+       int result = 0;
+       uint32_t tmp;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+               result = atomctrl_get_voltage_table_v3(hwmgr,
+                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT,
+                               &(data->mvdd_voltage_table));
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to retrieve MVDD table.",
+                               return result);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+               if (hwmgr->pp_table_version == PP_TABLE_V1)
+                       result = phm_get_svi2_mvdd_voltage_table(&(data->mvdd_voltage_table),
+                                       table_info->vdd_dep_on_mclk);
+               else if (hwmgr->pp_table_version == PP_TABLE_V0)
+                       result = phm_get_svi2_voltage_table_v0(&(data->mvdd_voltage_table),
+                                       hwmgr->dyn_state.mvdd_dependency_on_mclk);
+
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to retrieve SVI2 MVDD table from dependancy table.",
+                               return result;);
+       }
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+               result = atomctrl_get_voltage_table_v3(hwmgr,
+                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT,
+                               &(data->vddci_voltage_table));
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to retrieve VDDCI table.",
+                               return result);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+               if (hwmgr->pp_table_version == PP_TABLE_V1)
+                       result = phm_get_svi2_vddci_voltage_table(&(data->vddci_voltage_table),
+                                       table_info->vdd_dep_on_mclk);
+               else if (hwmgr->pp_table_version == PP_TABLE_V0)
+                       result = phm_get_svi2_voltage_table_v0(&(data->vddci_voltage_table),
+                                       hwmgr->dyn_state.vddci_dependency_on_mclk);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to retrieve SVI2 VDDCI table from dependancy table.",
+                               return result);
+       }
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+               /* VDDGFX has only SVI2 voltage control */
+               result = phm_get_svi2_vdd_voltage_table(&(data->vddgfx_voltage_table),
+                                       table_info->vddgfx_lookup_table);
+               PP_ASSERT_WITH_CODE((0 == result),
+                       "Failed to retrieve SVI2 VDDGFX table from lookup table.", return result;);
+       }
+
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->voltage_control) {
+               result = atomctrl_get_voltage_table_v3(hwmgr,
+                                       VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_GPIO_LUT,
+                                       &data->vddc_voltage_table);
+               PP_ASSERT_WITH_CODE((0 == result),
+                       "Failed to retrieve VDDC table.", return result;);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+
+               if (hwmgr->pp_table_version == PP_TABLE_V0)
+                       result = phm_get_svi2_voltage_table_v0(&data->vddc_voltage_table,
+                                       hwmgr->dyn_state.vddc_dependency_on_mclk);
+               else if (hwmgr->pp_table_version == PP_TABLE_V1)
+                       result = phm_get_svi2_vdd_voltage_table(&(data->vddc_voltage_table),
+                               table_info->vddc_lookup_table);
+
+               PP_ASSERT_WITH_CODE((0 == result),
+                       "Failed to retrieve SVI2 VDDC table from dependancy table.", return result;);
+       }
+
+       tmp = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_VDDC);
+       PP_ASSERT_WITH_CODE(
+                       (data->vddc_voltage_table.count <= tmp),
+               "Too many voltage values for VDDC. Trimming to fit state table.",
+                       phm_trim_voltage_table_to_fit_state_table(tmp,
+                                               &(data->vddc_voltage_table)));
+
+       tmp = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_VDDGFX);
+       PP_ASSERT_WITH_CODE(
+                       (data->vddgfx_voltage_table.count <= tmp),
+               "Too many voltage values for VDDC. Trimming to fit state table.",
+                       phm_trim_voltage_table_to_fit_state_table(tmp,
+                                               &(data->vddgfx_voltage_table)));
+
+       tmp = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_VDDCI);
+       PP_ASSERT_WITH_CODE(
+                       (data->vddci_voltage_table.count <= tmp),
+               "Too many voltage values for VDDCI. Trimming to fit state table.",
+                       phm_trim_voltage_table_to_fit_state_table(tmp,
+                                       &(data->vddci_voltage_table)));
+
+       tmp = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_MVDD);
+       PP_ASSERT_WITH_CODE(
+                       (data->mvdd_voltage_table.count <= tmp),
+               "Too many voltage values for MVDD. Trimming to fit state table.",
+                       phm_trim_voltage_table_to_fit_state_table(tmp,
+                                               &(data->mvdd_voltage_table)));
+
+       return 0;
+}
+
+/**
+* Programs static screed detection parameters
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+static int smu7_program_static_screen_threshold_parameters(
+                                                       struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /* Set static screen threshold unit */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD_UNIT,
+                       data->static_screen_threshold_unit);
+       /* Set static screen threshold */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD,
+                       data->static_screen_threshold);
+
+       return 0;
+}
+
+/**
+* Setup display gap for glitch free memory clock switching.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always  0
+*/
+static int smu7_enable_display_gap(struct pp_hwmgr *hwmgr)
+{
+       uint32_t display_gap =
+                       cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                                       ixCG_DISPLAY_GAP_CNTL);
+
+       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
+                       DISP_GAP, DISPLAY_GAP_IGNORE);
+
+       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL,
+                       DISP_GAP_MCHG, DISPLAY_GAP_VBLANK);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_DISPLAY_GAP_CNTL, display_gap);
+
+       return 0;
+}
+
+/**
+* Programs activity state transition voting clients
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always  0
+*/
+static int smu7_program_voting_clients(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /* Clear reset for voting clients before enabling DPM */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 0);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 0);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_0, data->voting_rights_clients0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_1, data->voting_rights_clients1);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_2, data->voting_rights_clients2);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_3, data->voting_rights_clients3);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_4, data->voting_rights_clients4);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_5, data->voting_rights_clients5);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_6, data->voting_rights_clients6);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_7, data->voting_rights_clients7);
+
+       return 0;
+}
+
+static int smu7_clear_voting_clients(struct pp_hwmgr *hwmgr)
+{
+       /* Reset voting clients before disabling DPM */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 1);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 1);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_0, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_1, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_2, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_3, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_4, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_5, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_6, 0);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_FREQ_TRAN_VOTING_7, 0);
+
+       return 0;
+}
+
+/* Copy one arb setting to another and then switch the active set.
+ * arb_src and arb_dest is one of the MC_CG_ARB_FREQ_Fx constants.
+ */
+static int smu7_copy_and_switch_arb_sets(struct pp_hwmgr *hwmgr,
+               uint32_t arb_src, uint32_t arb_dest)
+{
+       uint32_t mc_arb_dram_timing;
+       uint32_t mc_arb_dram_timing2;
+       uint32_t burst_time;
+       uint32_t mc_cg_config;
+
+       switch (arb_src) {
+       case MC_CG_ARB_FREQ_F0:
+               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+               break;
+       case MC_CG_ARB_FREQ_F1:
+               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1);
+               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1);
+               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (arb_dest) {
+       case MC_CG_ARB_FREQ_F0:
+               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING, mc_arb_dram_timing);
+               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
+               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0, burst_time);
+               break;
+       case MC_CG_ARB_FREQ_F1:
+               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
+               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
+               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1, burst_time);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mc_cg_config = cgs_read_register(hwmgr->device, mmMC_CG_CONFIG);
+       mc_cg_config |= 0x0000000F;
+       cgs_write_register(hwmgr->device, mmMC_CG_CONFIG, mc_cg_config);
+       PHM_WRITE_FIELD(hwmgr->device, MC_ARB_CG, CG_ARB_REQ, arb_dest);
+
+       return 0;
+}
+
+static int smu7_reset_to_default(struct pp_hwmgr *hwmgr)
+{
+       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_ResetToDefaults);
+}
+
+/**
+* Initial switch from ARB F0->F1
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+* This function is to be called from the SetPowerState table.
+*/
+static int smu7_initial_switch_from_arbf0_to_f1(struct pp_hwmgr *hwmgr)
+{
+       return smu7_copy_and_switch_arb_sets(hwmgr,
+                       MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+
+static int smu7_force_switch_to_arbf0(struct pp_hwmgr *hwmgr)
+{
+       uint32_t tmp;
+
+       tmp = (cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, ixSMC_SCRATCH9) &
+                       0x0000ff00) >> 8;
+
+       if (tmp == MC_CG_ARB_FREQ_F0)
+               return 0;
+
+       return smu7_copy_and_switch_arb_sets(hwmgr,
+                       tmp, MC_CG_ARB_FREQ_F0);
+}
+
+static int smu7_setup_default_pcie_table(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_pcie_table *pcie_table = NULL;
+
+       uint32_t i, max_entry;
+       uint32_t tmp;
+
+       PP_ASSERT_WITH_CODE((data->use_pcie_performance_levels ||
+                       data->use_pcie_power_saving_levels), "No pcie performance levels!",
+                       return -EINVAL);
+
+       if (table_info != NULL)
+               pcie_table = table_info->pcie_table;
+
+       if (data->use_pcie_performance_levels &&
+                       !data->use_pcie_power_saving_levels) {
+               data->pcie_gen_power_saving = data->pcie_gen_performance;
+               data->pcie_lane_power_saving = data->pcie_lane_performance;
+       } else if (!data->use_pcie_performance_levels &&
+                       data->use_pcie_power_saving_levels) {
+               data->pcie_gen_performance = data->pcie_gen_power_saving;
+               data->pcie_lane_performance = data->pcie_lane_power_saving;
+       }
+       tmp = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_LINK);
+       phm_reset_single_dpm_table(&data->dpm_table.pcie_speed_table,
+                                       tmp,
+                                       MAX_REGULAR_DPM_NUMBER);
+
+       if (pcie_table != NULL) {
+               /* max_entry is used to make sure we reserve one PCIE level
+                * for boot level (fix for A+A PSPP issue).
+                * If PCIE table from PPTable have ULV entry + 8 entries,
+                * then ignore the last entry.*/
+               max_entry = (tmp < pcie_table->count) ? tmp : pcie_table->count;
+               for (i = 1; i < max_entry; i++) {
+                       phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i - 1,
+                                       get_pcie_gen_support(data->pcie_gen_cap,
+                                                       pcie_table->entries[i].gen_speed),
+                                       get_pcie_lane_support(data->pcie_lane_cap,
+                                                       pcie_table->entries[i].lane_width));
+               }
+               data->dpm_table.pcie_speed_table.count = max_entry - 1;
+               smum_update_smc_table(hwmgr, SMU_BIF_TABLE);
+       } else {
+               /* Hardcode Pcie Table */
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 0,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Min_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 1,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Min_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 2,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Max_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 3,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Max_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 4,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Max_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+               phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 5,
+                               get_pcie_gen_support(data->pcie_gen_cap,
+                                               PP_Max_PCIEGen),
+                               get_pcie_lane_support(data->pcie_lane_cap,
+                                               PP_Max_PCIELane));
+
+               data->dpm_table.pcie_speed_table.count = 6;
+       }
+       /* Populate last level for boot PCIE level, but do not increment count. */
+       phm_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
+                       data->dpm_table.pcie_speed_table.count,
+                       get_pcie_gen_support(data->pcie_gen_cap,
+                                       PP_Min_PCIEGen),
+                       get_pcie_lane_support(data->pcie_lane_cap,
+                                       PP_Max_PCIELane));
+
+       return 0;
+}
+
+static int smu7_reset_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       memset(&(data->dpm_table), 0x00, sizeof(data->dpm_table));
+
+       phm_reset_single_dpm_table(
+                       &data->dpm_table.sclk_table,
+                               smum_get_mac_definition(hwmgr->smumgr,
+                                       SMU_MAX_LEVELS_GRAPHICS),
+                                       MAX_REGULAR_DPM_NUMBER);
+       phm_reset_single_dpm_table(
+                       &data->dpm_table.mclk_table,
+                       smum_get_mac_definition(hwmgr->smumgr,
+                               SMU_MAX_LEVELS_MEMORY), MAX_REGULAR_DPM_NUMBER);
+
+       phm_reset_single_dpm_table(
+                       &data->dpm_table.vddc_table,
+                               smum_get_mac_definition(hwmgr->smumgr,
+                                       SMU_MAX_LEVELS_VDDC),
+                                       MAX_REGULAR_DPM_NUMBER);
+       phm_reset_single_dpm_table(
+                       &data->dpm_table.vddci_table,
+                       smum_get_mac_definition(hwmgr->smumgr,
+                               SMU_MAX_LEVELS_VDDCI), MAX_REGULAR_DPM_NUMBER);
+
+       phm_reset_single_dpm_table(
+                       &data->dpm_table.mvdd_table,
+                               smum_get_mac_definition(hwmgr->smumgr,
+                                       SMU_MAX_LEVELS_MVDD),
+                                       MAX_REGULAR_DPM_NUMBER);
+       return 0;
+}
+/*
+ * This function is to initialize all DPM state tables
+ * for SMU7 based on the dependency table.
+ * Dynamic state patching function will then trim these
+ * state tables to the allowed range based
+ * on the power policy or external client requests,
+ * such as UVD request, etc.
+ */
+
+static int smu7_setup_dpm_tables_v0(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_clock_voltage_dependency_table *allowed_vdd_sclk_table =
+               hwmgr->dyn_state.vddc_dependency_on_sclk;
+       struct phm_clock_voltage_dependency_table *allowed_vdd_mclk_table =
+               hwmgr->dyn_state.vddc_dependency_on_mclk;
+       struct phm_cac_leakage_table *std_voltage_table =
+               hwmgr->dyn_state.cac_leakage_table;
+       uint32_t i;
+
+       PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table != NULL,
+               "SCLK dependency table is missing. This table is mandatory", return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table->count >= 1,
+               "SCLK dependency table has to have is missing. This table is mandatory", return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
+               "MCLK dependency table is missing. This table is mandatory", return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table->count >= 1,
+               "VMCLK dependency table has to have is missing. This table is mandatory", return -EINVAL);
+
+
+       /* Initialize Sclk DPM table based on allow Sclk values*/
+       data->dpm_table.sclk_table.count = 0;
+
+       for (i = 0; i < allowed_vdd_sclk_table->count; i++) {
+               if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count-1].value !=
+                               allowed_vdd_sclk_table->entries[i].clk) {
+                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
+                               allowed_vdd_sclk_table->entries[i].clk;
+                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled = 1; /*(i==0) ? 1 : 0; to do */
+                       data->dpm_table.sclk_table.count++;
+               }
+       }
+
+       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
+               "MCLK dependency table is missing. This table is mandatory", return -EINVAL);
+       /* Initialize Mclk DPM table based on allow Mclk values */
+       data->dpm_table.mclk_table.count = 0;
+       for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+               if (i == 0 || data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count-1].value !=
+                       allowed_vdd_mclk_table->entries[i].clk) {
+                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
+                               allowed_vdd_mclk_table->entries[i].clk;
+                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled = 1; /*(i==0) ? 1 : 0; */
+                       data->dpm_table.mclk_table.count++;
+               }
+       }
+
+       /* Initialize Vddc DPM table based on allow Vddc values.  And populate corresponding std values. */
+       for (i = 0; i < allowed_vdd_sclk_table->count; i++) {
+               data->dpm_table.vddc_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+               data->dpm_table.vddc_table.dpm_levels[i].param1 = std_voltage_table->entries[i].Leakage;
+               /* param1 is for corresponding std voltage */
+               data->dpm_table.vddc_table.dpm_levels[i].enabled = 1;
+       }
+
+       data->dpm_table.vddc_table.count = allowed_vdd_sclk_table->count;
+       allowed_vdd_mclk_table = hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+       if (NULL != allowed_vdd_mclk_table) {
+               /* Initialize Vddci DPM table based on allow Mclk values */
+               for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+                       data->dpm_table.vddci_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+                       data->dpm_table.vddci_table.dpm_levels[i].enabled = 1;
+               }
+               data->dpm_table.vddci_table.count = allowed_vdd_mclk_table->count;
+       }
+
+       allowed_vdd_mclk_table = hwmgr->dyn_state.mvdd_dependency_on_mclk;
+
+       if (NULL != allowed_vdd_mclk_table) {
+               /*
+                * Initialize MVDD DPM table based on allow Mclk
+                * values
+                */
+               for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
+                       data->dpm_table.mvdd_table.dpm_levels[i].value = allowed_vdd_mclk_table->entries[i].v;
+                       data->dpm_table.mvdd_table.dpm_levels[i].enabled = 1;
+               }
+               data->dpm_table.mvdd_table.count = allowed_vdd_mclk_table->count;
+       }
+
+       return 0;
+}
+
+static int smu7_setup_dpm_tables_v1(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint32_t i;
+
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table;
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+
+       if (table_info == NULL)
+               return -EINVAL;
+
+       dep_sclk_table = table_info->vdd_dep_on_sclk;
+       dep_mclk_table = table_info->vdd_dep_on_mclk;
+
+       PP_ASSERT_WITH_CODE(dep_sclk_table != NULL,
+                       "SCLK dependency table is missing.",
+                       return -EINVAL);
+       PP_ASSERT_WITH_CODE(dep_sclk_table->count >= 1,
+                       "SCLK dependency table count is 0.",
+                       return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(dep_mclk_table != NULL,
+                       "MCLK dependency table is missing.",
+                       return -EINVAL);
+       PP_ASSERT_WITH_CODE(dep_mclk_table->count >= 1,
+                       "MCLK dependency table count is 0",
+                       return -EINVAL);
+
+       /* Initialize Sclk DPM table based on allow Sclk values */
+       data->dpm_table.sclk_table.count = 0;
+       for (i = 0; i < dep_sclk_table->count; i++) {
+               if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count - 1].value !=
+                                               dep_sclk_table->entries[i].clk) {
+
+                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
+                                       dep_sclk_table->entries[i].clk;
+
+                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled =
+                                       (i == 0) ? true : false;
+                       data->dpm_table.sclk_table.count++;
+               }
+       }
+
+       /* Initialize Mclk DPM table based on allow Mclk values */
+       data->dpm_table.mclk_table.count = 0;
+       for (i = 0; i < dep_mclk_table->count; i++) {
+               if (i == 0 || data->dpm_table.mclk_table.dpm_levels
+                               [data->dpm_table.mclk_table.count - 1].value !=
+                                               dep_mclk_table->entries[i].clk) {
+                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
+                                                       dep_mclk_table->entries[i].clk;
+                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled =
+                                                       (i == 0) ? true : false;
+                       data->dpm_table.mclk_table.count++;
+               }
+       }
+
+       return 0;
+}
+
+int smu7_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       smu7_reset_dpm_tables(hwmgr);
+
+       if (hwmgr->pp_table_version == PP_TABLE_V1)
+               smu7_setup_dpm_tables_v1(hwmgr);
+       else if (hwmgr->pp_table_version == PP_TABLE_V0)
+               smu7_setup_dpm_tables_v0(hwmgr);
+
+       smu7_setup_default_pcie_table(hwmgr);
+
+       /* save a copy of the default DPM table */
+       memcpy(&(data->golden_dpm_table), &(data->dpm_table),
+                       sizeof(struct smu7_dpm_table));
+       return 0;
+}
+
+uint32_t smu7_get_xclk(struct pp_hwmgr *hwmgr)
+{
+       uint32_t reference_clock, tmp;
+       struct cgs_display_info info = {0};
+       struct cgs_mode_info mode_info;
+
+       info.mode_info = &mode_info;
+
+       tmp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL_2, MUX_TCLK_TO_XCLK);
+
+       if (tmp)
+               return TCLK;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+       reference_clock = mode_info.ref_clock;
+
+       tmp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL, XTALIN_DIVIDE);
+
+       if (0 != tmp)
+               return reference_clock / 4;
+
+       return reference_clock;
+}
+
+static int smu7_enable_vrhot_gpio_interrupt(struct pp_hwmgr *hwmgr)
+{
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_RegulatorHot))
+               return smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_EnableVRHotGPIOInterrupt);
+
+       return 0;
+}
+
+static int smu7_enable_sclk_control(struct pp_hwmgr *hwmgr)
+{
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+                       SCLK_PWRMGT_OFF, 0);
+       return 0;
+}
+
+static int smu7_enable_ulv(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->ulv_supported)
+               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_EnableULV);
+
+       return 0;
+}
+
+static int smu7_disable_ulv(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->ulv_supported)
+               return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DisableULV);
+
+       return 0;
+}
+
+static int smu7_enable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep)) {
+               if (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_MASTER_DeepSleep_ON))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to enable Master Deep Sleep switch failed!",
+                                       return -EINVAL);
+       } else {
+               if (smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to disable Master Deep Sleep switch failed!",
+                                       return -EINVAL);
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_disable_deep_sleep_master_switch(struct pp_hwmgr *hwmgr)
+{
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep)) {
+               if (smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_MASTER_DeepSleep_OFF)) {
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to disable Master Deep Sleep switch failed!",
+                                       return -EINVAL);
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_disable_handshake_uvd(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t soft_register_value = 0;
+       uint32_t handshake_disables_offset = data->soft_regs_start
+                               + smum_get_offsetof(hwmgr->smumgr,
+                                       SMU_SoftRegisters, HandshakeDisables);
+
+       soft_register_value = cgs_read_ind_register(hwmgr->device,
+                               CGS_IND_REG__SMC, handshake_disables_offset);
+       soft_register_value |= smum_get_mac_definition(hwmgr->smumgr,
+                                       SMU_UVD_MCLK_HANDSHAKE_DISABLE);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       handshake_disables_offset, soft_register_value);
+       return 0;
+}
+
+static int smu7_enable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /* enable SCLK dpm */
+       if (!data->sclk_dpm_key_disabled)
+               PP_ASSERT_WITH_CODE(
+               (0 == smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_DPM_Enable)),
+               "Failed to enable SCLK DPM during DPM Start Function!",
+               return -EINVAL);
+
+       /* enable MCLK dpm */
+       if (0 == data->mclk_dpm_key_disabled) {
+               if (!(hwmgr->feature_mask & PP_UVD_HANDSHAKE_MASK))
+                       smu7_disable_handshake_uvd(hwmgr);
+               PP_ASSERT_WITH_CODE(
+                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                                               PPSMC_MSG_MCLKDPM_Enable)),
+                               "Failed to enable MCLK DPM during DPM Start Function!",
+                               return -EINVAL);
+
+               PHM_WRITE_FIELD(hwmgr->device, MC_SEQ_CNTL_3, CAC_EN, 0x1);
+
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x5);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x5);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x100005);
+               udelay(10);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC0_CNTL, 0x400005);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_MC1_CNTL, 0x400005);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixLCAC_CPL_CNTL, 0x500005);
+       }
+
+       return 0;
+}
+
+static int smu7_start_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /*enable general power management */
+
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+                       GLOBAL_PWRMGT_EN, 1);
+
+       /* enable sclk deep sleep */
+
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+                       DYNAMIC_PM_EN, 1);
+
+       /* prepare for PCIE DPM */
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       data->soft_regs_start +
+                       smum_get_offsetof(hwmgr->smumgr, SMU_SoftRegisters,
+                                               VoltageChangeTimeout), 0x1000);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE,
+                       SWRST_COMMAND_1, RESETLC, 0x0);
+
+       PP_ASSERT_WITH_CODE(
+                       (0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                                       PPSMC_MSG_Voltage_Cntl_Enable)),
+                       "Failed to enable voltage DPM during DPM Start Function!",
+                       return -EINVAL);
+
+
+       if (smu7_enable_sclk_mclk_dpm(hwmgr)) {
+               printk(KERN_ERR "Failed to enable Sclk DPM and Mclk DPM!");
+               return -EINVAL;
+       }
+
+       /* enable PCIE dpm */
+       if (0 == data->pcie_dpm_key_disabled) {
+               PP_ASSERT_WITH_CODE(
+                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                                               PPSMC_MSG_PCIeDPM_Enable)),
+                               "Failed to enable pcie DPM during DPM Start Function!",
+                               return -EINVAL);
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_Falcon_QuickTransition)) {
+               PP_ASSERT_WITH_CODE((0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_EnableACDCGPIOInterrupt)),
+                               "Failed to enable AC DC GPIO Interrupt!",
+                               );
+       }
+
+       return 0;
+}
+
+static int smu7_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /* disable SCLK dpm */
+       if (!data->sclk_dpm_key_disabled)
+               PP_ASSERT_WITH_CODE(
+                               (smum_send_msg_to_smc(hwmgr->smumgr,
+                                               PPSMC_MSG_DPM_Disable) == 0),
+                               "Failed to disable SCLK DPM!",
+                               return -EINVAL);
+
+       /* disable MCLK dpm */
+       if (!data->mclk_dpm_key_disabled) {
+               PP_ASSERT_WITH_CODE(
+                               (smum_send_msg_to_smc(hwmgr->smumgr,
+                                               PPSMC_MSG_MCLKDPM_Disable) == 0),
+                               "Failed to disable MCLK DPM!",
+                               return -EINVAL);
+       }
+
+       return 0;
+}
+
+static int smu7_stop_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       /* disable general power management */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+                       GLOBAL_PWRMGT_EN, 0);
+       /* disable sclk deep sleep */
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL,
+                       DYNAMIC_PM_EN, 0);
+
+       /* disable PCIE dpm */
+       if (!data->pcie_dpm_key_disabled) {
+               PP_ASSERT_WITH_CODE(
+                               (smum_send_msg_to_smc(hwmgr->smumgr,
+                                               PPSMC_MSG_PCIeDPM_Disable) == 0),
+                               "Failed to disable pcie DPM during DPM Stop Function!",
+                               return -EINVAL);
+       }
+
+       if (smu7_disable_sclk_mclk_dpm(hwmgr)) {
+               printk(KERN_ERR "Failed to disable Sclk DPM and Mclk DPM!");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void smu7_set_dpm_event_sources(struct pp_hwmgr *hwmgr, uint32_t sources)
+{
+       bool protection;
+       enum DPM_EVENT_SRC src;
+
+       switch (sources) {
+       default:
+               printk(KERN_ERR "Unknown throttling event sources.");
+               /* fall through */
+       case 0:
+               protection = false;
+               /* src is unused */
+               break;
+       case (1 << PHM_AutoThrottleSource_Thermal):
+               protection = true;
+               src = DPM_EVENT_SRC_DIGITAL;
+               break;
+       case (1 << PHM_AutoThrottleSource_External):
+               protection = true;
+               src = DPM_EVENT_SRC_EXTERNAL;
+               break;
+       case (1 << PHM_AutoThrottleSource_External) |
+                       (1 << PHM_AutoThrottleSource_Thermal):
+               protection = true;
+               src = DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL;
+               break;
+       }
+       /* Order matters - don't enable thermal protection for the wrong source. */
+       if (protection) {
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_CTRL,
+                               DPM_EVENT_SRC, src);
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+                               THERMAL_PROTECTION_DIS,
+                               !phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_ThermalController));
+       } else
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT,
+                               THERMAL_PROTECTION_DIS, 1);
+}
+
+static int smu7_enable_auto_throttle_source(struct pp_hwmgr *hwmgr,
+               PHM_AutoThrottleSource source)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (!(data->active_auto_throttle_sources & (1 << source))) {
+               data->active_auto_throttle_sources |= 1 << source;
+               smu7_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
+       }
+       return 0;
+}
+
+static int smu7_enable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
+{
+       return smu7_enable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
+}
+
+static int smu7_disable_auto_throttle_source(struct pp_hwmgr *hwmgr,
+               PHM_AutoThrottleSource source)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->active_auto_throttle_sources & (1 << source)) {
+               data->active_auto_throttle_sources &= ~(1 << source);
+               smu7_set_dpm_event_sources(hwmgr, data->active_auto_throttle_sources);
+       }
+       return 0;
+}
+
+static int smu7_disable_thermal_auto_throttle(struct pp_hwmgr *hwmgr)
+{
+       return smu7_disable_auto_throttle_source(hwmgr, PHM_AutoThrottleSource_Thermal);
+}
+
+int smu7_pcie_performance_request(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       data->pcie_performance_request = true;
+
+       return 0;
+}
+
+int smu7_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+       int tmp_result = 0;
+       int result = 0;
+
+       tmp_result = (!smum_is_dpm_running(hwmgr)) ? 0 : -1;
+       PP_ASSERT_WITH_CODE(tmp_result == 0,
+                       "DPM is already running right now, no need to enable DPM!",
+                       return 0);
+
+       if (smu7_voltage_control(hwmgr)) {
+               tmp_result = smu7_enable_voltage_control(hwmgr);
+               PP_ASSERT_WITH_CODE(tmp_result == 0,
+                               "Failed to enable voltage control!",
+                               result = tmp_result);
+
+               tmp_result = smu7_construct_voltage_tables(hwmgr);
+               PP_ASSERT_WITH_CODE((0 == tmp_result),
+                               "Failed to contruct voltage tables!",
+                               result = tmp_result);
+       }
+       smum_initialize_mc_reg_table(hwmgr);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_EngineSpreadSpectrumSupport))
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 1);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ThermalController))
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 0);
+
+       tmp_result = smu7_program_static_screen_threshold_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to program static screen threshold parameters!",
+                       result = tmp_result);
+
+       tmp_result = smu7_enable_display_gap(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable display gap!", result = tmp_result);
+
+       tmp_result = smu7_program_voting_clients(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to program voting clients!", result = tmp_result);
+
+       tmp_result = smum_process_firmware_header(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to process firmware header!", result = tmp_result);
+
+       tmp_result = smu7_initial_switch_from_arbf0_to_f1(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to initialize switch from ArbF0 to F1!",
+                       result = tmp_result);
+
+       result = smu7_setup_default_dpm_tables(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to setup default DPM tables!", return result);
+
+       tmp_result = smum_init_smc_table(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to initialize SMC table!", result = tmp_result);
+
+       tmp_result = smu7_enable_vrhot_gpio_interrupt(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable VR hot GPIO interrupt!", result = tmp_result);
+
+       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay);
+
+       tmp_result = smu7_enable_sclk_control(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable SCLK control!", result = tmp_result);
+
+       tmp_result = smu7_enable_smc_voltage_controller(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable voltage control!", result = tmp_result);
+
+       tmp_result = smu7_enable_ulv(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable ULV!", result = tmp_result);
+
+       tmp_result = smu7_enable_deep_sleep_master_switch(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable deep sleep master switch!", result = tmp_result);
+
+       tmp_result = smu7_enable_didt_config(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to enable deep sleep master switch!", result = tmp_result);
+
+       tmp_result = smu7_start_dpm(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to start DPM!", result = tmp_result);
+
+       tmp_result = smu7_enable_smc_cac(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable SMC CAC!", result = tmp_result);
+
+       tmp_result = smu7_enable_power_containment(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable power containment!", result = tmp_result);
+
+       tmp_result = smu7_power_control_set_level(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to power control set level!", result = tmp_result);
+
+       tmp_result = smu7_enable_thermal_auto_throttle(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable thermal auto throttle!", result = tmp_result);
+
+       tmp_result = smu7_pcie_performance_request(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "pcie performance request failed!", result = tmp_result);
+
+       return 0;
+}
+
+int smu7_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
+{
+       int tmp_result, result = 0;
+
+       tmp_result = (smum_is_dpm_running(hwmgr)) ? 0 : -1;
+       PP_ASSERT_WITH_CODE(tmp_result == 0,
+                       "DPM is not running right now, no need to disable DPM!",
+                       return 0);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ThermalController))
+               PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, 1);
+
+       tmp_result = smu7_disable_power_containment(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to disable power containment!", result = tmp_result);
+
+       tmp_result = smu7_disable_smc_cac(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to disable SMC CAC!", result = tmp_result);
+
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_SPLL_SPREAD_SPECTRUM, SSEN, 0);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, 0);
+
+       tmp_result = smu7_disable_thermal_auto_throttle(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to disable thermal auto throttle!", result = tmp_result);
+
+       tmp_result = smu7_stop_dpm(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to stop DPM!", result = tmp_result);
+
+       tmp_result = smu7_disable_deep_sleep_master_switch(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to disable deep sleep master switch!", result = tmp_result);
+
+       tmp_result = smu7_disable_ulv(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to disable ULV!", result = tmp_result);
+
+       tmp_result = smu7_clear_voting_clients(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to clear voting clients!", result = tmp_result);
+
+       tmp_result = smu7_reset_to_default(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to reset to default!", result = tmp_result);
+
+       tmp_result = smu7_force_switch_to_arbf0(hwmgr);
+       PP_ASSERT_WITH_CODE((tmp_result == 0),
+                       "Failed to force to switch arbf0!", result = tmp_result);
+
+       return result;
+}
+
+int smu7_reset_asic_tasks(struct pp_hwmgr *hwmgr)
+{
+
+       return 0;
+}
+
+static void smu7_init_dpm_defaults(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       data->dll_default_on = false;
+       data->mclk_dpm0_activity_target = 0xa;
+       data->mclk_activity_target = SMU7_MCLK_TARGETACTIVITY_DFLT;
+       data->vddc_vddgfx_delta = 300;
+       data->static_screen_threshold = SMU7_STATICSCREENTHRESHOLD_DFLT;
+       data->static_screen_threshold_unit = SMU7_STATICSCREENTHRESHOLDUNIT_DFLT;
+       data->voting_rights_clients0 = SMU7_VOTINGRIGHTSCLIENTS_DFLT0;
+       data->voting_rights_clients1 = SMU7_VOTINGRIGHTSCLIENTS_DFLT1;
+       data->voting_rights_clients2 = SMU7_VOTINGRIGHTSCLIENTS_DFLT2;
+       data->voting_rights_clients3 = SMU7_VOTINGRIGHTSCLIENTS_DFLT3;
+       data->voting_rights_clients4 = SMU7_VOTINGRIGHTSCLIENTS_DFLT4;
+       data->voting_rights_clients5 = SMU7_VOTINGRIGHTSCLIENTS_DFLT5;
+       data->voting_rights_clients6 = SMU7_VOTINGRIGHTSCLIENTS_DFLT6;
+       data->voting_rights_clients7 = SMU7_VOTINGRIGHTSCLIENTS_DFLT7;
+
+       data->mclk_dpm_key_disabled = hwmgr->feature_mask & PP_MCLK_DPM_MASK ? false : true;
+       data->sclk_dpm_key_disabled = hwmgr->feature_mask & PP_SCLK_DPM_MASK ? false : true;
+       data->pcie_dpm_key_disabled = hwmgr->feature_mask & PP_PCIE_DPM_MASK ? false : true;
+       /* need to set voltage control types before EVV patching */
+       data->voltage_control = SMU7_VOLTAGE_CONTROL_NONE;
+       data->vddci_control = SMU7_VOLTAGE_CONTROL_NONE;
+       data->mvdd_control = SMU7_VOLTAGE_CONTROL_NONE;
+       data->enable_tdc_limit_feature = true;
+       data->enable_pkg_pwr_tracking_feature = true;
+       data->force_pcie_gen = PP_PCIEGenInvalid;
+       data->ulv_supported = hwmgr->feature_mask & PP_ULV_MASK ? true : false;
+
+       data->fast_watermark_threshold = 100;
+       if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                       VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2))
+               data->voltage_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ControlVDDGFX)) {
+               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                       VOLTAGE_TYPE_VDDGFX, VOLTAGE_OBJ_SVID2)) {
+                       data->vdd_gfx_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+               }
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_EnableMVDDControl)) {
+               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT))
+                       data->mvdd_control = SMU7_VOLTAGE_CONTROL_BY_GPIO;
+               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                               VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_SVID2))
+                       data->mvdd_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+       }
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->vdd_gfx_control) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ControlVDDGFX);
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ControlVDDCI)) {
+               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
+                       data->vddci_control = SMU7_VOLTAGE_CONTROL_BY_GPIO;
+               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
+                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
+                       data->vddci_control = SMU7_VOLTAGE_CONTROL_BY_SVID2;
+       }
+
+       if (data->mvdd_control == SMU7_VOLTAGE_CONTROL_NONE)
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_EnableMVDDControl);
+
+       if (data->vddci_control == SMU7_VOLTAGE_CONTROL_NONE)
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ControlVDDCI);
+
+       if ((hwmgr->pp_table_version != PP_TABLE_V0)
+               && (table_info->cac_dtp_table->usClockStretchAmount != 0))
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_ClockStretcher);
+
+       data->pcie_gen_performance.max = PP_PCIEGen1;
+       data->pcie_gen_performance.min = PP_PCIEGen3;
+       data->pcie_gen_power_saving.max = PP_PCIEGen1;
+       data->pcie_gen_power_saving.min = PP_PCIEGen3;
+       data->pcie_lane_performance.max = 0;
+       data->pcie_lane_performance.min = 16;
+       data->pcie_lane_power_saving.max = 0;
+       data->pcie_lane_power_saving.min = 16;
+}
+
+/**
+* Get Leakage VDDC based on leakage ID.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+static int smu7_get_evv_voltages(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint16_t vv_id;
+       uint16_t vddc = 0;
+       uint16_t vddgfx = 0;
+       uint16_t i, j;
+       uint32_t sclk = 0;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)hwmgr->pptable;
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table = NULL;
+
+
+       if (table_info != NULL)
+               sclk_table = table_info->vdd_dep_on_sclk;
+
+       for (i = 0; i < SMU7_MAX_LEAKAGE_COUNT; i++) {
+               vv_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
+
+               if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+                       if (0 == phm_get_sclk_for_voltage_evv(hwmgr,
+                                               table_info->vddgfx_lookup_table, vv_id, &sclk)) {
+                               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                                       PHM_PlatformCaps_ClockStretcher)) {
+                                       for (j = 1; j < sclk_table->count; j++) {
+                                               if (sclk_table->entries[j].clk == sclk &&
+                                                               sclk_table->entries[j].cks_enable == 0) {
+                                                       sclk += 5000;
+                                                       break;
+                                               }
+                                       }
+                               }
+                               if (0 == atomctrl_get_voltage_evv_on_sclk
+                                   (hwmgr, VOLTAGE_TYPE_VDDGFX, sclk,
+                                    vv_id, &vddgfx)) {
+                                       /* need to make sure vddgfx is less than 2v or else, it could burn the ASIC. */
+                                       PP_ASSERT_WITH_CODE((vddgfx < 2000 && vddgfx != 0), "Invalid VDDGFX value!", return -EINVAL);
+
+                                       /* the voltage should not be zero nor equal to leakage ID */
+                                       if (vddgfx != 0 && vddgfx != vv_id) {
+                                               data->vddcgfx_leakage.actual_voltage[data->vddcgfx_leakage.count] = vddgfx;
+                                               data->vddcgfx_leakage.leakage_id[data->vddcgfx_leakage.count] = vv_id;
+                                               data->vddcgfx_leakage.count++;
+                                       }
+                               } else {
+                                       printk("Error retrieving EVV voltage value!\n");
+                               }
+                       }
+               } else {
+
+                       if ((hwmgr->pp_table_version == PP_TABLE_V0)
+                               || !phm_get_sclk_for_voltage_evv(hwmgr,
+                                       table_info->vddc_lookup_table, vv_id, &sclk)) {
+                               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_ClockStretcher)) {
+                                       for (j = 1; j < sclk_table->count; j++) {
+                                               if (sclk_table->entries[j].clk == sclk &&
+                                                               sclk_table->entries[j].cks_enable == 0) {
+                                                       sclk += 5000;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                               if (phm_get_voltage_evv_on_sclk(hwmgr,
+                                                       VOLTAGE_TYPE_VDDC,
+                                                       sclk, vv_id, &vddc) == 0) {
+                                       if (vddc >= 2000 || vddc == 0)
+                                               return -EINVAL;
+                               } else {
+                                       printk(KERN_WARNING "failed to retrieving EVV voltage!\n");
+                                       continue;
+                               }
+
+                               /* the voltage should not be zero nor equal to leakage ID */
+                               if (vddc != 0 && vddc != vv_id) {
+                                       data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = (uint16_t)(vddc);
+                                       data->vddc_leakage.leakage_id[data->vddc_leakage.count] = vv_id;
+                                       data->vddc_leakage.count++;
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Change virtual leakage voltage to actual value.
+ *
+ * @param     hwmgr  the address of the powerplay hardware manager.
+ * @param     pointer to changing voltage
+ * @param     pointer to leakage table
+ */
+static void smu7_patch_ppt_v1_with_vdd_leakage(struct pp_hwmgr *hwmgr,
+               uint16_t *voltage, struct smu7_leakage_voltage *leakage_table)
+{
+       uint32_t index;
+
+       /* search for leakage voltage ID 0xff01 ~ 0xff08 */
+       for (index = 0; index < leakage_table->count; index++) {
+               /* if this voltage matches a leakage voltage ID */
+               /* patch with actual leakage voltage */
+               if (leakage_table->leakage_id[index] == *voltage) {
+                       *voltage = leakage_table->actual_voltage[index];
+                       break;
+               }
+       }
+
+       if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
+               printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n");
+}
+
+/**
+* Patch voltage lookup table by EVV leakages.
+*
+* @param     hwmgr  the address of the powerplay hardware manager.
+* @param     pointer to voltage lookup table
+* @param     pointer to leakage table
+* @return     always 0
+*/
+static int smu7_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
+               phm_ppt_v1_voltage_lookup_table *lookup_table,
+               struct smu7_leakage_voltage *leakage_table)
+{
+       uint32_t i;
+
+       for (i = 0; i < lookup_table->count; i++)
+               smu7_patch_ppt_v1_with_vdd_leakage(hwmgr,
+                               &lookup_table->entries[i].us_vdd, leakage_table);
+
+       return 0;
+}
+
+static int smu7_patch_clock_voltage_limits_with_vddc_leakage(
+               struct pp_hwmgr *hwmgr, struct smu7_leakage_voltage *leakage_table,
+               uint16_t *vddc)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       smu7_patch_ppt_v1_with_vdd_leakage(hwmgr, (uint16_t *)vddc, leakage_table);
+       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
+                       table_info->max_clock_voltage_on_dc.vddc;
+       return 0;
+}
+
+static int smu7_patch_voltage_dependency_tables_with_lookup_table(
+               struct pp_hwmgr *hwmgr)
+{
+       uint8_t entry_id;
+       uint8_t voltage_id;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+                       table_info->vdd_dep_on_sclk;
+       struct phm_ppt_v1_clock_voltage_dependency_table *mclk_table =
+                       table_info->vdd_dep_on_mclk;
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+
+       if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+               for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+                       voltage_id = sclk_table->entries[entry_id].vddInd;
+                       sclk_table->entries[entry_id].vddgfx =
+                               table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd;
+               }
+       } else {
+               for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+                       voltage_id = sclk_table->entries[entry_id].vddInd;
+                       sclk_table->entries[entry_id].vddc =
+                               table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+               }
+       }
+
+       for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
+               voltage_id = mclk_table->entries[entry_id].vddInd;
+               mclk_table->entries[entry_id].vddc =
+                       table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+       }
+
+       for (entry_id = 0; entry_id < mm_table->count; ++entry_id) {
+               voltage_id = mm_table->entries[entry_id].vddcInd;
+               mm_table->entries[entry_id].vddc =
+                       table_info->vddc_lookup_table->entries[voltage_id].us_vdd;
+       }
+
+       return 0;
+
+}
+
+static int phm_add_voltage(struct pp_hwmgr *hwmgr,
+                       phm_ppt_v1_voltage_lookup_table *look_up_table,
+                       phm_ppt_v1_voltage_lookup_record *record)
+{
+       uint32_t i;
+
+       PP_ASSERT_WITH_CODE((NULL != look_up_table),
+               "Lookup Table empty.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((0 != look_up_table->count),
+               "Lookup Table empty.", return -EINVAL);
+
+       i = smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_VDDGFX);
+       PP_ASSERT_WITH_CODE((i >= look_up_table->count),
+               "Lookup Table is full.", return -EINVAL);
+
+       /* This is to avoid entering duplicate calculated records. */
+       for (i = 0; i < look_up_table->count; i++) {
+               if (look_up_table->entries[i].us_vdd == record->us_vdd) {
+                       if (look_up_table->entries[i].us_calculated == 1)
+                               return 0;
+                       break;
+               }
+       }
+
+       look_up_table->entries[i].us_calculated = 1;
+       look_up_table->entries[i].us_vdd = record->us_vdd;
+       look_up_table->entries[i].us_cac_low = record->us_cac_low;
+       look_up_table->entries[i].us_cac_mid = record->us_cac_mid;
+       look_up_table->entries[i].us_cac_high = record->us_cac_high;
+       /* Only increment the count when we're appending, not replacing duplicate entry. */
+       if (i == look_up_table->count)
+               look_up_table->count++;
+
+       return 0;
+}
+
+
+static int smu7_calc_voltage_dependency_tables(struct pp_hwmgr *hwmgr)
+{
+       uint8_t entry_id;
+       struct phm_ppt_v1_voltage_lookup_record v_record;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       phm_ppt_v1_clock_voltage_dependency_table *sclk_table = pptable_info->vdd_dep_on_sclk;
+       phm_ppt_v1_clock_voltage_dependency_table *mclk_table = pptable_info->vdd_dep_on_mclk;
+
+       if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+               for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) {
+                       if (sclk_table->entries[entry_id].vdd_offset & (1 << 15))
+                               v_record.us_vdd = sclk_table->entries[entry_id].vddgfx +
+                                       sclk_table->entries[entry_id].vdd_offset - 0xFFFF;
+                       else
+                               v_record.us_vdd = sclk_table->entries[entry_id].vddgfx +
+                                       sclk_table->entries[entry_id].vdd_offset;
+
+                       sclk_table->entries[entry_id].vddc =
+                               v_record.us_cac_low = v_record.us_cac_mid =
+                               v_record.us_cac_high = v_record.us_vdd;
+
+                       phm_add_voltage(hwmgr, pptable_info->vddc_lookup_table, &v_record);
+               }
+
+               for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) {
+                       if (mclk_table->entries[entry_id].vdd_offset & (1 << 15))
+                               v_record.us_vdd = mclk_table->entries[entry_id].vddc +
+                                       mclk_table->entries[entry_id].vdd_offset - 0xFFFF;
+                       else
+                               v_record.us_vdd = mclk_table->entries[entry_id].vddc +
+                                       mclk_table->entries[entry_id].vdd_offset;
+
+                       mclk_table->entries[entry_id].vddgfx = v_record.us_cac_low =
+                               v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
+                       phm_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
+               }
+       }
+       return 0;
+}
+
+static int smu7_calc_mm_voltage_dependency_table(struct pp_hwmgr *hwmgr)
+{
+       uint8_t entry_id;
+       struct phm_ppt_v1_voltage_lookup_record v_record;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
+
+       if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+               for (entry_id = 0; entry_id < mm_table->count; entry_id++) {
+                       if (mm_table->entries[entry_id].vddgfx_offset & (1 << 15))
+                               v_record.us_vdd = mm_table->entries[entry_id].vddc +
+                                       mm_table->entries[entry_id].vddgfx_offset - 0xFFFF;
+                       else
+                               v_record.us_vdd = mm_table->entries[entry_id].vddc +
+                                       mm_table->entries[entry_id].vddgfx_offset;
+
+                       /* Add the calculated VDDGFX to the VDDGFX lookup table */
+                       mm_table->entries[entry_id].vddgfx = v_record.us_cac_low =
+                               v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
+                       phm_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
+               }
+       }
+       return 0;
+}
+
+static int smu7_sort_lookup_table(struct pp_hwmgr *hwmgr,
+               struct phm_ppt_v1_voltage_lookup_table *lookup_table)
+{
+       uint32_t table_size, i, j;
+       struct phm_ppt_v1_voltage_lookup_record tmp_voltage_lookup_record;
+       table_size = lookup_table->count;
+
+       PP_ASSERT_WITH_CODE(0 != lookup_table->count,
+               "Lookup table is empty", return -EINVAL);
+
+       /* Sorting voltages */
+       for (i = 0; i < table_size - 1; i++) {
+               for (j = i + 1; j > 0; j--) {
+                       if (lookup_table->entries[j].us_vdd <
+                                       lookup_table->entries[j - 1].us_vdd) {
+                               tmp_voltage_lookup_record = lookup_table->entries[j - 1];
+                               lookup_table->entries[j - 1] = lookup_table->entries[j];
+                               lookup_table->entries[j] = tmp_voltage_lookup_record;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_complete_dependency_tables(struct pp_hwmgr *hwmgr)
+{
+       int result = 0;
+       int tmp_result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+               tmp_result = smu7_patch_lookup_table_with_leakage(hwmgr,
+                       table_info->vddgfx_lookup_table, &(data->vddcgfx_leakage));
+               if (tmp_result != 0)
+                       result = tmp_result;
+
+               smu7_patch_ppt_v1_with_vdd_leakage(hwmgr,
+                       &table_info->max_clock_voltage_on_dc.vddgfx, &(data->vddcgfx_leakage));
+       } else {
+
+               tmp_result = smu7_patch_lookup_table_with_leakage(hwmgr,
+                               table_info->vddc_lookup_table, &(data->vddc_leakage));
+               if (tmp_result)
+                       result = tmp_result;
+
+               tmp_result = smu7_patch_clock_voltage_limits_with_vddc_leakage(hwmgr,
+                               &(data->vddc_leakage), &table_info->max_clock_voltage_on_dc.vddc);
+               if (tmp_result)
+                       result = tmp_result;
+       }
+
+       tmp_result = smu7_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
+       if (tmp_result)
+               result = tmp_result;
+
+       tmp_result = smu7_calc_voltage_dependency_tables(hwmgr);
+       if (tmp_result)
+               result = tmp_result;
+
+       tmp_result = smu7_calc_mm_voltage_dependency_table(hwmgr);
+       if (tmp_result)
+               result = tmp_result;
+
+       tmp_result = smu7_sort_lookup_table(hwmgr, table_info->vddgfx_lookup_table);
+       if (tmp_result)
+               result = tmp_result;
+
+       tmp_result = smu7_sort_lookup_table(hwmgr, table_info->vddc_lookup_table);
+       if (tmp_result)
+               result = tmp_result;
+
+       return result;
+}
+
+static int smu7_set_private_data_based_on_pptable_v1(struct pp_hwmgr *hwmgr)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
+                                               table_info->vdd_dep_on_sclk;
+       struct phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
+                                               table_info->vdd_dep_on_mclk;
+
+       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table != NULL,
+               "VDD dependency on SCLK table is missing.",
+               return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
+               "VDD dependency on SCLK table has to have is missing.",
+               return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table != NULL,
+               "VDD dependency on MCLK table is missing",
+               return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
+               "VDD dependency on MCLK table has to have is missing.",
+               return -EINVAL);
+
+       table_info->max_clock_voltage_on_ac.sclk =
+               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
+       table_info->max_clock_voltage_on_ac.mclk =
+               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
+       table_info->max_clock_voltage_on_ac.vddc =
+               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
+       table_info->max_clock_voltage_on_ac.vddci =
+               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
+
+       hwmgr->dyn_state.max_clock_voltage_on_ac.sclk = table_info->max_clock_voltage_on_ac.sclk;
+       hwmgr->dyn_state.max_clock_voltage_on_ac.mclk = table_info->max_clock_voltage_on_ac.mclk;
+       hwmgr->dyn_state.max_clock_voltage_on_ac.vddc = table_info->max_clock_voltage_on_ac.vddc;
+       hwmgr->dyn_state.max_clock_voltage_on_ac.vddci = table_info->max_clock_voltage_on_ac.vddci;
+
+       return 0;
+}
+
+int smu7_patch_voltage_workaround(struct pp_hwmgr *hwmgr)
+{
+       struct phm_ppt_v1_information *table_info =
+                      (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+       struct phm_ppt_v1_voltage_lookup_table *lookup_table;
+       uint32_t i;
+       uint32_t hw_revision, sub_vendor_id, sub_sys_id;
+       struct cgs_system_info sys_info = {0};
+
+       if (table_info != NULL) {
+               dep_mclk_table = table_info->vdd_dep_on_mclk;
+               lookup_table = table_info->vddc_lookup_table;
+       } else
+               return 0;
+
+       sys_info.size = sizeof(struct cgs_system_info);
+
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_REV;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       hw_revision = (uint32_t)sys_info.value;
+
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_SUB_SYS_ID;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       sub_sys_id = (uint32_t)sys_info.value;
+
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_SUB_SYS_VENDOR_ID;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       sub_vendor_id = (uint32_t)sys_info.value;
+
+       if (hwmgr->chip_id == CHIP_POLARIS10 && hw_revision == 0xC7 &&
+                       ((sub_sys_id == 0xb37 && sub_vendor_id == 0x1002) ||
+                   (sub_sys_id == 0x4a8 && sub_vendor_id == 0x1043) ||
+                   (sub_sys_id == 0x9480 && sub_vendor_id == 0x1682))) {
+               if (lookup_table->entries[dep_mclk_table->entries[dep_mclk_table->count-1].vddInd].us_vdd >= 1000)
+                       return 0;
+
+               for (i = 0; i < lookup_table->count; i++) {
+                       if (lookup_table->entries[i].us_vdd < 0xff01 && lookup_table->entries[i].us_vdd >= 1000) {
+                               dep_mclk_table->entries[dep_mclk_table->count-1].vddInd = (uint8_t) i;
+                               return 0;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int smu7_thermal_parameter_init(struct pp_hwmgr *hwmgr)
+{
+       struct pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
+       uint32_t temp_reg;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_PCC_GPIO_PINID, &gpio_pin_assignment)) {
+               temp_reg = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL);
+               switch (gpio_pin_assignment.uc_gpio_pin_bit_shift) {
+               case 0:
+                       temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x1);
+                       break;
+               case 1:
+                       temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x2);
+                       break;
+               case 2:
+                       temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, GNB_SLOW, 0x1);
+                       break;
+               case 3:
+                       temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, FORCE_NB_PS1, 0x1);
+                       break;
+               case 4:
+                       temp_reg = PHM_SET_FIELD(temp_reg, CNB_PWRMGT_CNTL, DPM_ENABLED, 0x1);
+                       break;
+               default:
+                       PP_ASSERT_WITH_CODE(0,
+                       "Failed to setup PCC HW register! Wrong GPIO assigned for VDDC_PCC_GPIO_PINID!",
+                       );
+                       break;
+               }
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL, temp_reg);
+       }
+
+       if (table_info == NULL)
+               return 0;
+
+       if (table_info->cac_dtp_table->usDefaultTargetOperatingTemp != 0 &&
+               hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode) {
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMinLimit =
+                       (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMMaxLimit =
+                       (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanPWMStep = 1;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMaxLimit = 100;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMMinLimit =
+                       (uint16_t)hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanRPMStep = 1;
+
+               table_info->cac_dtp_table->usDefaultTargetOperatingTemp = (table_info->cac_dtp_table->usDefaultTargetOperatingTemp >= 50) ?
+                                                               (table_info->cac_dtp_table->usDefaultTargetOperatingTemp - 50) : 0;
+
+               table_info->cac_dtp_table->usOperatingTempMaxLimit = table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
+               table_info->cac_dtp_table->usOperatingTempStep = 1;
+               table_info->cac_dtp_table->usOperatingTempHyst = 1;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM =
+                              hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM;
+
+               hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM =
+                              hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM;
+
+               hwmgr->dyn_state.cac_dtp_table->usOperatingTempMinLimit =
+                              table_info->cac_dtp_table->usOperatingTempMinLimit;
+
+               hwmgr->dyn_state.cac_dtp_table->usOperatingTempMaxLimit =
+                              table_info->cac_dtp_table->usOperatingTempMaxLimit;
+
+               hwmgr->dyn_state.cac_dtp_table->usDefaultTargetOperatingTemp =
+                              table_info->cac_dtp_table->usDefaultTargetOperatingTemp;
+
+               hwmgr->dyn_state.cac_dtp_table->usOperatingTempStep =
+                              table_info->cac_dtp_table->usOperatingTempStep;
+
+               hwmgr->dyn_state.cac_dtp_table->usTargetOperatingTemp =
+                              table_info->cac_dtp_table->usTargetOperatingTemp;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                                               PHM_PlatformCaps_ODFuzzyFanControlSupport);
+       }
+
+       return 0;
+}
+
+/**
+ * Change virtual leakage voltage to actual value.
+ *
+ * @param     hwmgr  the address of the powerplay hardware manager.
+ * @param     pointer to changing voltage
+ * @param     pointer to leakage table
+ */
+static void smu7_patch_ppt_v0_with_vdd_leakage(struct pp_hwmgr *hwmgr,
+               uint32_t *voltage, struct smu7_leakage_voltage *leakage_table)
+{
+       uint32_t index;
+
+       /* search for leakage voltage ID 0xff01 ~ 0xff08 */
+       for (index = 0; index < leakage_table->count; index++) {
+               /* if this voltage matches a leakage voltage ID */
+               /* patch with actual leakage voltage */
+               if (leakage_table->leakage_id[index] == *voltage) {
+                       *voltage = leakage_table->actual_voltage[index];
+                       break;
+               }
+       }
+
+       if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
+               printk(KERN_ERR "Voltage value looks like a Leakage ID but it's not patched \n");
+}
+
+
+static int smu7_patch_vddc(struct pp_hwmgr *hwmgr,
+                             struct phm_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                               &data->vddc_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_vddci(struct pp_hwmgr *hwmgr,
+                              struct phm_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                                       &data->vddci_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_vce_vddc(struct pp_hwmgr *hwmgr,
+                                 struct phm_vce_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                                       &data->vddc_leakage);
+
+       return 0;
+}
+
+
+static int smu7_patch_uvd_vddc(struct pp_hwmgr *hwmgr,
+                                 struct phm_uvd_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                                       &data->vddc_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_vddc_shed_limit(struct pp_hwmgr *hwmgr,
+                                        struct phm_phase_shedding_limits_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].Voltage,
+                                                       &data->vddc_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_samu_vddc(struct pp_hwmgr *hwmgr,
+                                  struct phm_samu_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                                       &data->vddc_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_acp_vddc(struct pp_hwmgr *hwmgr,
+                                 struct phm_acp_clock_voltage_dependency_table *tab)
+{
+       uint16_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab)
+               for (i = 0; i < tab->count; i++)
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &tab->entries[i].v,
+                                       &data->vddc_leakage);
+
+       return 0;
+}
+
+static int smu7_patch_limits_vddc(struct pp_hwmgr *hwmgr,
+                                    struct phm_clock_and_voltage_limits *tab)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab) {
+               smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, (uint32_t *)&tab->vddc,
+                                                       &data->vddc_leakage);
+               smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, (uint32_t *)&tab->vddci,
+                                                       &data->vddci_leakage);
+       }
+
+       return 0;
+}
+
+static int smu7_patch_cac_vddc(struct pp_hwmgr *hwmgr, struct phm_cac_leakage_table *tab)
+{
+       uint32_t i;
+       uint32_t vddc;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (tab) {
+               for (i = 0; i < tab->count; i++) {
+                       vddc = (uint32_t)(tab->entries[i].Vddc);
+                       smu7_patch_ppt_v0_with_vdd_leakage(hwmgr, &vddc, &data->vddc_leakage);
+                       tab->entries[i].Vddc = (uint16_t)vddc;
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_patch_dependency_tables_with_leakage(struct pp_hwmgr *hwmgr)
+{
+       int tmp;
+
+       tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_sclk);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dependency_on_mclk);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_vddc(hwmgr, hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_vddci(hwmgr, hwmgr->dyn_state.vddci_dependency_on_mclk);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_vce_vddc(hwmgr, hwmgr->dyn_state.vce_clock_voltage_dependency_table);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_uvd_vddc(hwmgr, hwmgr->dyn_state.uvd_clock_voltage_dependency_table);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_samu_vddc(hwmgr, hwmgr->dyn_state.samu_clock_voltage_dependency_table);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_acp_vddc(hwmgr, hwmgr->dyn_state.acp_clock_voltage_dependency_table);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_vddc_shed_limit(hwmgr, hwmgr->dyn_state.vddc_phase_shed_limits_table);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_limits_vddc(hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_ac);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_limits_vddc(hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_dc);
+       if (tmp)
+               return -EINVAL;
+
+       tmp = smu7_patch_cac_vddc(hwmgr, hwmgr->dyn_state.cac_leakage_table);
+       if (tmp)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int smu7_set_private_data_based_on_pptable_v0(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       struct phm_clock_voltage_dependency_table *allowed_sclk_vddc_table = hwmgr->dyn_state.vddc_dependency_on_sclk;
+       struct phm_clock_voltage_dependency_table *allowed_mclk_vddc_table = hwmgr->dyn_state.vddc_dependency_on_mclk;
+       struct phm_clock_voltage_dependency_table *allowed_mclk_vddci_table = hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+       PP_ASSERT_WITH_CODE(allowed_sclk_vddc_table != NULL,
+               "VDDC dependency on SCLK table is missing. This table is mandatory\n", return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_sclk_vddc_table->count >= 1,
+               "VDDC dependency on SCLK table has to have is missing. This table is mandatory\n", return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(allowed_mclk_vddc_table != NULL,
+               "VDDC dependency on MCLK table is missing. This table is mandatory\n", return -EINVAL);
+       PP_ASSERT_WITH_CODE(allowed_mclk_vddc_table->count >= 1,
+               "VDD dependency on MCLK table has to have is missing. This table is mandatory\n", return -EINVAL);
+
+       data->min_vddc_in_pptable = (uint16_t)allowed_sclk_vddc_table->entries[0].v;
+       data->max_vddc_in_pptable = (uint16_t)allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+
+       hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
+               allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].clk;
+       hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
+               allowed_mclk_vddc_table->entries[allowed_mclk_vddc_table->count - 1].clk;
+       hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
+               allowed_sclk_vddc_table->entries[allowed_sclk_vddc_table->count - 1].v;
+
+       if (allowed_mclk_vddci_table != NULL && allowed_mclk_vddci_table->count >= 1) {
+               data->min_vddci_in_pptable = (uint16_t)allowed_mclk_vddci_table->entries[0].v;
+               data->max_vddci_in_pptable = (uint16_t)allowed_mclk_vddci_table->entries[allowed_mclk_vddci_table->count - 1].v;
+       }
+
+       if (hwmgr->dyn_state.vddci_dependency_on_mclk != NULL && hwmgr->dyn_state.vddci_dependency_on_mclk->count > 1)
+               hwmgr->dyn_state.max_clock_voltage_on_ac.vddci = hwmgr->dyn_state.vddci_dependency_on_mclk->entries[hwmgr->dyn_state.vddci_dependency_on_mclk->count - 1].v;
+
+       return 0;
+}
+
+int smu7_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data;
+       int result;
+
+       data = kzalloc(sizeof(struct smu7_hwmgr), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       hwmgr->backend = data;
+
+       smu7_patch_voltage_workaround(hwmgr);
+       smu7_init_dpm_defaults(hwmgr);
+
+       /* Get leakage voltage based on leakage ID. */
+       result = smu7_get_evv_voltages(hwmgr);
+
+       if (result) {
+               printk("Get EVV Voltage Failed.  Abort Driver loading!\n");
+               return -EINVAL;
+       }
+
+       if (hwmgr->pp_table_version == PP_TABLE_V1) {
+               smu7_complete_dependency_tables(hwmgr);
+               smu7_set_private_data_based_on_pptable_v1(hwmgr);
+       } else if (hwmgr->pp_table_version == PP_TABLE_V0) {
+               smu7_patch_dependency_tables_with_leakage(hwmgr);
+               smu7_set_private_data_based_on_pptable_v0(hwmgr);
+       }
+
+       /* Initalize Dynamic State Adjustment Rule Settings */
+       result = phm_initializa_dynamic_state_adjustment_rule_settings(hwmgr);
+
+       if (0 == result) {
+               struct cgs_system_info sys_info = {0};
+
+               data->is_tlu_enabled = false;
+
+               hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
+                                                       SMU7_MAX_HARDWARE_POWERLEVELS;
+               hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
+               hwmgr->platform_descriptor.minimumClocksReductionPercentage = 50;
+
+               sys_info.size = sizeof(struct cgs_system_info);
+               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_GEN_INFO;
+               result = cgs_query_system_info(hwmgr->device, &sys_info);
+               if (result)
+                       data->pcie_gen_cap = AMDGPU_DEFAULT_PCIE_GEN_MASK;
+               else
+                       data->pcie_gen_cap = (uint32_t)sys_info.value;
+               if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
+                       data->pcie_spc_cap = 20;
+               sys_info.size = sizeof(struct cgs_system_info);
+               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_MLW;
+               result = cgs_query_system_info(hwmgr->device, &sys_info);
+               if (result)
+                       data->pcie_lane_cap = AMDGPU_DEFAULT_PCIE_MLW_MASK;
+               else
+                       data->pcie_lane_cap = (uint32_t)sys_info.value;
+
+               hwmgr->platform_descriptor.vbiosInterruptId = 0x20000400; /* IRQ_SOURCE1_SW_INT */
+/* The true clock step depends on the frequency, typically 4.5 or 9 MHz. Here we use 5. */
+               hwmgr->platform_descriptor.clockStep.engineClock = 500;
+               hwmgr->platform_descriptor.clockStep.memoryClock = 500;
+               smu7_thermal_parameter_init(hwmgr);
+       } else {
+               /* Ignore return value in here, we are cleaning up a mess. */
+               phm_hwmgr_backend_fini(hwmgr);
+       }
+
+       return 0;
+}
+
+static int smu7_force_dpm_highest(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t level, tmp;
+
+       if (!data->pcie_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+                       level = 0;
+                       tmp = data->dpm_level_enable_mask.pcie_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               level++;
+
+                       if (level)
+                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                               PPSMC_MSG_PCIeDPM_ForceLevel, level);
+               }
+       }
+
+       if (!data->sclk_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+                       level = 0;
+                       tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               level++;
+
+                       if (level)
+                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                               PPSMC_MSG_SCLKDPM_SetEnabledMask,
+                                               (1 << level));
+               }
+       }
+
+       if (!data->mclk_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+                       level = 0;
+                       tmp = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
+                       while (tmp >>= 1)
+                               level++;
+
+                       if (level)
+                               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                               PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                               (1 << level));
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (hwmgr->pp_table_version == PP_TABLE_V1)
+               phm_apply_dal_min_voltage_request(hwmgr);
+/* TO DO  for v0 iceland and Ci*/
+
+       if (!data->sclk_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask)
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
+                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask);
+       }
+
+       if (!data->mclk_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask)
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask);
+       }
+
+       return 0;
+}
+
+static int smu7_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (!smum_is_dpm_running(hwmgr))
+               return -EINVAL;
+
+       if (!data->pcie_dpm_key_disabled) {
+               smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_PCIeDPM_UnForceLevel);
+       }
+
+       return smu7_upload_dpm_level_enable_mask(hwmgr);
+}
+
+static int smu7_force_dpm_lowest(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data =
+                       (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t level;
+
+       if (!data->sclk_dpm_key_disabled)
+               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
+                       level = phm_get_lowest_enabled_level(hwmgr,
+                                                             data->dpm_level_enable_mask.sclk_dpm_enable_mask);
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                                           PPSMC_MSG_SCLKDPM_SetEnabledMask,
+                                                           (1 << level));
+
+       }
+
+       if (!data->mclk_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
+                       level = phm_get_lowest_enabled_level(hwmgr,
+                                                             data->dpm_level_enable_mask.mclk_dpm_enable_mask);
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                                           PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                                           (1 << level));
+               }
+       }
+
+       if (!data->pcie_dpm_key_disabled) {
+               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask) {
+                       level = phm_get_lowest_enabled_level(hwmgr,
+                                                             data->dpm_level_enable_mask.pcie_dpm_enable_mask);
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                                           PPSMC_MSG_PCIeDPM_ForceLevel,
+                                                           (level));
+               }
+       }
+
+       return 0;
+
+}
+static int smu7_force_dpm_level(struct pp_hwmgr *hwmgr,
+                               enum amd_dpm_forced_level level)
+{
+       int ret = 0;
+
+       switch (level) {
+       case AMD_DPM_FORCED_LEVEL_HIGH:
+               ret = smu7_force_dpm_highest(hwmgr);
+               if (ret)
+                       return ret;
+               break;
+       case AMD_DPM_FORCED_LEVEL_LOW:
+               ret = smu7_force_dpm_lowest(hwmgr);
+               if (ret)
+                       return ret;
+               break;
+       case AMD_DPM_FORCED_LEVEL_AUTO:
+               ret = smu7_unforce_dpm_levels(hwmgr);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               break;
+       }
+
+       hwmgr->dpm_level = level;
+
+       return ret;
+}
+
+static int smu7_get_power_state_size(struct pp_hwmgr *hwmgr)
+{
+       return sizeof(struct smu7_power_state);
+}
+
+
+static int smu7_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
+                               struct pp_power_state *request_ps,
+                       const struct pp_power_state *current_ps)
+{
+
+       struct smu7_power_state *smu7_ps =
+                               cast_phw_smu7_power_state(&request_ps->hardware);
+       uint32_t sclk;
+       uint32_t mclk;
+       struct PP_Clocks minimum_clocks = {0};
+       bool disable_mclk_switching;
+       bool disable_mclk_switching_for_frame_lock;
+       struct cgs_display_info info = {0};
+       const struct phm_clock_and_voltage_limits *max_limits;
+       uint32_t i;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       int32_t count;
+       int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
+
+       data->battery_state = (PP_StateUILabel_Battery ==
+                       request_ps->classification.ui_label);
+
+       PP_ASSERT_WITH_CODE(smu7_ps->performance_level_count == 2,
+                                "VI should always have 2 performance levels",
+                               );
+
+       max_limits = (PP_PowerSource_AC == hwmgr->power_source) ?
+                       &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
+                       &(hwmgr->dyn_state.max_clock_voltage_on_dc);
+
+       /* Cap clock DPM tables at DC MAX if it is in DC. */
+       if (PP_PowerSource_DC == hwmgr->power_source) {
+               for (i = 0; i < smu7_ps->performance_level_count; i++) {
+                       if (smu7_ps->performance_levels[i].memory_clock > max_limits->mclk)
+                               smu7_ps->performance_levels[i].memory_clock = max_limits->mclk;
+                       if (smu7_ps->performance_levels[i].engine_clock > max_limits->sclk)
+                               smu7_ps->performance_levels[i].engine_clock = max_limits->sclk;
+               }
+       }
+
+       smu7_ps->vce_clks.evclk = hwmgr->vce_arbiter.evclk;
+       smu7_ps->vce_clks.ecclk = hwmgr->vce_arbiter.ecclk;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+
+       /*TO DO result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
+
+       minimum_clocks.engineClock = hwmgr->display_config.min_core_set_clock;
+       minimum_clocks.memoryClock = hwmgr->display_config.min_mem_set_clock;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState)) {
+               max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
+               stable_pstate_sclk = (max_limits->sclk * 75) / 100;
+
+               for (count = table_info->vdd_dep_on_sclk->count - 1;
+                               count >= 0; count--) {
+                       if (stable_pstate_sclk >=
+                                       table_info->vdd_dep_on_sclk->entries[count].clk) {
+                               stable_pstate_sclk =
+                                               table_info->vdd_dep_on_sclk->entries[count].clk;
+                               break;
+                       }
+               }
+
+               if (count < 0)
+                       stable_pstate_sclk = table_info->vdd_dep_on_sclk->entries[0].clk;
+
+               stable_pstate_mclk = max_limits->mclk;
+
+               minimum_clocks.engineClock = stable_pstate_sclk;
+               minimum_clocks.memoryClock = stable_pstate_mclk;
+       }
+
+       if (minimum_clocks.engineClock < hwmgr->gfx_arbiter.sclk)
+               minimum_clocks.engineClock = hwmgr->gfx_arbiter.sclk;
+
+       if (minimum_clocks.memoryClock < hwmgr->gfx_arbiter.mclk)
+               minimum_clocks.memoryClock = hwmgr->gfx_arbiter.mclk;
+
+       smu7_ps->sclk_threshold = hwmgr->gfx_arbiter.sclk_threshold;
+
+       if (0 != hwmgr->gfx_arbiter.sclk_over_drive) {
+               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.sclk_over_drive <=
+                               hwmgr->platform_descriptor.overdriveLimit.engineClock),
+                               "Overdrive sclk exceeds limit",
+                               hwmgr->gfx_arbiter.sclk_over_drive =
+                                               hwmgr->platform_descriptor.overdriveLimit.engineClock);
+
+               if (hwmgr->gfx_arbiter.sclk_over_drive >= hwmgr->gfx_arbiter.sclk)
+                       smu7_ps->performance_levels[1].engine_clock =
+                                       hwmgr->gfx_arbiter.sclk_over_drive;
+       }
+
+       if (0 != hwmgr->gfx_arbiter.mclk_over_drive) {
+               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.mclk_over_drive <=
+                               hwmgr->platform_descriptor.overdriveLimit.memoryClock),
+                               "Overdrive mclk exceeds limit",
+                               hwmgr->gfx_arbiter.mclk_over_drive =
+                                               hwmgr->platform_descriptor.overdriveLimit.memoryClock);
+
+               if (hwmgr->gfx_arbiter.mclk_over_drive >= hwmgr->gfx_arbiter.mclk)
+                       smu7_ps->performance_levels[1].memory_clock =
+                                       hwmgr->gfx_arbiter.mclk_over_drive;
+       }
+
+       disable_mclk_switching_for_frame_lock = phm_cap_enabled(
+                                   hwmgr->platform_descriptor.platformCaps,
+                                   PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
+
+
+       disable_mclk_switching = (1 < info.display_count) ||
+                                   disable_mclk_switching_for_frame_lock;
+
+       sclk = smu7_ps->performance_levels[0].engine_clock;
+       mclk = smu7_ps->performance_levels[0].memory_clock;
+
+       if (disable_mclk_switching)
+               mclk = smu7_ps->performance_levels
+               [smu7_ps->performance_level_count - 1].memory_clock;
+
+       if (sclk < minimum_clocks.engineClock)
+               sclk = (minimum_clocks.engineClock > max_limits->sclk) ?
+                               max_limits->sclk : minimum_clocks.engineClock;
+
+       if (mclk < minimum_clocks.memoryClock)
+               mclk = (minimum_clocks.memoryClock > max_limits->mclk) ?
+                               max_limits->mclk : minimum_clocks.memoryClock;
+
+       smu7_ps->performance_levels[0].engine_clock = sclk;
+       smu7_ps->performance_levels[0].memory_clock = mclk;
+
+       smu7_ps->performance_levels[1].engine_clock =
+               (smu7_ps->performance_levels[1].engine_clock >=
+                               smu7_ps->performance_levels[0].engine_clock) ?
+                                               smu7_ps->performance_levels[1].engine_clock :
+                                               smu7_ps->performance_levels[0].engine_clock;
+
+       if (disable_mclk_switching) {
+               if (mclk < smu7_ps->performance_levels[1].memory_clock)
+                       mclk = smu7_ps->performance_levels[1].memory_clock;
+
+               smu7_ps->performance_levels[0].memory_clock = mclk;
+               smu7_ps->performance_levels[1].memory_clock = mclk;
+       } else {
+               if (smu7_ps->performance_levels[1].memory_clock <
+                               smu7_ps->performance_levels[0].memory_clock)
+                       smu7_ps->performance_levels[1].memory_clock =
+                                       smu7_ps->performance_levels[0].memory_clock;
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState)) {
+               for (i = 0; i < smu7_ps->performance_level_count; i++) {
+                       smu7_ps->performance_levels[i].engine_clock = stable_pstate_sclk;
+                       smu7_ps->performance_levels[i].memory_clock = stable_pstate_mclk;
+                       smu7_ps->performance_levels[i].pcie_gen = data->pcie_gen_performance.max;
+                       smu7_ps->performance_levels[i].pcie_lane = data->pcie_gen_performance.max;
+               }
+       }
+       return 0;
+}
+
+
+static int smu7_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
+{
+       struct pp_power_state  *ps;
+       struct smu7_power_state  *smu7_ps;
+
+       if (hwmgr == NULL)
+               return -EINVAL;
+
+       ps = hwmgr->request_ps;
+
+       if (ps == NULL)
+               return -EINVAL;
+
+       smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+       if (low)
+               return smu7_ps->performance_levels[0].memory_clock;
+       else
+               return smu7_ps->performance_levels
+                               [smu7_ps->performance_level_count-1].memory_clock;
+}
+
+static int smu7_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
+{
+       struct pp_power_state  *ps;
+       struct smu7_power_state  *smu7_ps;
+
+       if (hwmgr == NULL)
+               return -EINVAL;
+
+       ps = hwmgr->request_ps;
+
+       if (ps == NULL)
+               return -EINVAL;
+
+       smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+       if (low)
+               return smu7_ps->performance_levels[0].engine_clock;
+       else
+               return smu7_ps->performance_levels
+                               [smu7_ps->performance_level_count-1].engine_clock;
+}
+
+static int smu7_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
+                                       struct pp_hw_power_state *hw_ps)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_power_state *ps = (struct smu7_power_state *)hw_ps;
+       ATOM_FIRMWARE_INFO_V2_2 *fw_info;
+       uint16_t size;
+       uint8_t frev, crev;
+       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
+
+       /* First retrieve the Boot clocks and VDDC from the firmware info table.
+        * We assume here that fw_info is unchanged if this call fails.
+        */
+       fw_info = (ATOM_FIRMWARE_INFO_V2_2 *)cgs_atom_get_data_table(
+                       hwmgr->device, index,
+                       &size, &frev, &crev);
+       if (!fw_info)
+               /* During a test, there is no firmware info table. */
+               return 0;
+
+       /* Patch the state. */
+       data->vbios_boot_state.sclk_bootup_value =
+                       le32_to_cpu(fw_info->ulDefaultEngineClock);
+       data->vbios_boot_state.mclk_bootup_value =
+                       le32_to_cpu(fw_info->ulDefaultMemoryClock);
+       data->vbios_boot_state.mvdd_bootup_value =
+                       le16_to_cpu(fw_info->usBootUpMVDDCVoltage);
+       data->vbios_boot_state.vddc_bootup_value =
+                       le16_to_cpu(fw_info->usBootUpVDDCVoltage);
+       data->vbios_boot_state.vddci_bootup_value =
+                       le16_to_cpu(fw_info->usBootUpVDDCIVoltage);
+       data->vbios_boot_state.pcie_gen_bootup_value =
+                       smu7_get_current_pcie_speed(hwmgr);
+
+       data->vbios_boot_state.pcie_lane_bootup_value =
+                       (uint16_t)smu7_get_current_pcie_lane_number(hwmgr);
+
+       /* set boot power state */
+       ps->performance_levels[0].memory_clock = data->vbios_boot_state.mclk_bootup_value;
+       ps->performance_levels[0].engine_clock = data->vbios_boot_state.sclk_bootup_value;
+       ps->performance_levels[0].pcie_gen = data->vbios_boot_state.pcie_gen_bootup_value;
+       ps->performance_levels[0].pcie_lane = data->vbios_boot_state.pcie_lane_bootup_value;
+
+       return 0;
+}
+
+static int smu7_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       unsigned long ret = 0;
+
+       if (hwmgr->pp_table_version == PP_TABLE_V0) {
+               result = pp_tables_get_num_of_entries(hwmgr, &ret);
+               return result ? 0 : ret;
+       } else if (hwmgr->pp_table_version == PP_TABLE_V1) {
+               result = get_number_of_powerplay_table_entries_v1_0(hwmgr);
+               return result;
+       }
+       return 0;
+}
+
+static int smu7_get_pp_table_entry_callback_func_v1(struct pp_hwmgr *hwmgr,
+               void *state, struct pp_power_state *power_state,
+               void *pp_table, uint32_t classification_flag)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_power_state  *smu7_power_state =
+                       (struct smu7_power_state *)(&(power_state->hardware));
+       struct smu7_performance_level *performance_level;
+       ATOM_Tonga_State *state_entry = (ATOM_Tonga_State *)state;
+       ATOM_Tonga_POWERPLAYTABLE *powerplay_table =
+                       (ATOM_Tonga_POWERPLAYTABLE *)pp_table;
+       PPTable_Generic_SubTable_Header *sclk_dep_table =
+                       (PPTable_Generic_SubTable_Header *)
+                       (((unsigned long)powerplay_table) +
+                               le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
+
+       ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
+                       (ATOM_Tonga_MCLK_Dependency_Table *)
+                       (((unsigned long)powerplay_table) +
+                               le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
+
+       /* The following fields are not initialized here: id orderedList allStatesList */
+       power_state->classification.ui_label =
+                       (le16_to_cpu(state_entry->usClassification) &
+                       ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
+                       ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
+       power_state->classification.flags = classification_flag;
+       /* NOTE: There is a classification2 flag in BIOS that is not being used right now */
+
+       power_state->classification.temporary_state = false;
+       power_state->classification.to_be_deleted = false;
+
+       power_state->validation.disallowOnDC =
+                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
+                                       ATOM_Tonga_DISALLOW_ON_DC));
+
+       power_state->pcie.lanes = 0;
+
+       power_state->display.disableFrameModulation = false;
+       power_state->display.limitRefreshrate = false;
+       power_state->display.enableVariBright =
+                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) &
+                                       ATOM_Tonga_ENABLE_VARIBRIGHT));
+
+       power_state->validation.supportedPowerLevels = 0;
+       power_state->uvd_clocks.VCLK = 0;
+       power_state->uvd_clocks.DCLK = 0;
+       power_state->temperatures.min = 0;
+       power_state->temperatures.max = 0;
+
+       performance_level = &(smu7_power_state->performance_levels
+                       [smu7_power_state->performance_level_count++]);
+
+       PP_ASSERT_WITH_CODE(
+                       (smu7_power_state->performance_level_count < smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_GRAPHICS)),
+                       "Performance levels exceeds SMC limit!",
+                       return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(
+                       (smu7_power_state->performance_level_count <=
+                                       hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
+                       "Performance levels exceeds Driver limit!",
+                       return -EINVAL);
+
+       /* Performance levels are arranged from low to high. */
+       performance_level->memory_clock = mclk_dep_table->entries
+                       [state_entry->ucMemoryClockIndexLow].ulMclk;
+       if (sclk_dep_table->ucRevId == 0)
+               performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
+                       [state_entry->ucEngineClockIndexLow].ulSclk;
+       else if (sclk_dep_table->ucRevId == 1)
+               performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
+                       [state_entry->ucEngineClockIndexLow].ulSclk;
+       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
+                       state_entry->ucPCIEGenLow);
+       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
+                       state_entry->ucPCIELaneHigh);
+
+       performance_level = &(smu7_power_state->performance_levels
+                       [smu7_power_state->performance_level_count++]);
+       performance_level->memory_clock = mclk_dep_table->entries
+                       [state_entry->ucMemoryClockIndexHigh].ulMclk;
+
+       if (sclk_dep_table->ucRevId == 0)
+               performance_level->engine_clock = ((ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table)->entries
+                       [state_entry->ucEngineClockIndexHigh].ulSclk;
+       else if (sclk_dep_table->ucRevId == 1)
+               performance_level->engine_clock = ((ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table)->entries
+                       [state_entry->ucEngineClockIndexHigh].ulSclk;
+
+       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap,
+                       state_entry->ucPCIEGenHigh);
+       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap,
+                       state_entry->ucPCIELaneHigh);
+
+       return 0;
+}
+
+static int smu7_get_pp_table_entry_v1(struct pp_hwmgr *hwmgr,
+               unsigned long entry_index, struct pp_power_state *state)
+{
+       int result;
+       struct smu7_power_state *ps;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
+                       table_info->vdd_dep_on_mclk;
+
+       state->hardware.magic = PHM_VIslands_Magic;
+
+       ps = (struct smu7_power_state *)(&state->hardware);
+
+       result = get_powerplay_table_entry_v1_0(hwmgr, entry_index, state,
+                       smu7_get_pp_table_entry_callback_func_v1);
+
+       /* This is the earliest time we have all the dependency table and the VBIOS boot state
+        * as PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot state
+        * if there is only one VDDCI/MCLK level, check if it's the same as VBIOS boot state
+        */
+       if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
+               if (dep_mclk_table->entries[0].clk !=
+                               data->vbios_boot_state.mclk_bootup_value)
+                       printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table "
+                                       "does not match VBIOS boot MCLK level");
+               if (dep_mclk_table->entries[0].vddci !=
+                               data->vbios_boot_state.vddci_bootup_value)
+                       printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table "
+                                       "does not match VBIOS boot VDDCI level");
+       }
+
+       /* set DC compatible flag if this state supports DC */
+       if (!state->validation.disallowOnDC)
+               ps->dc_compatible = true;
+
+       if (state->classification.flags & PP_StateClassificationFlag_ACPI)
+               data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
+
+       ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
+       ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
+
+       if (!result) {
+               uint32_t i;
+
+               switch (state->classification.ui_label) {
+               case PP_StateUILabel_Performance:
+                       data->use_pcie_performance_levels = true;
+                       for (i = 0; i < ps->performance_level_count; i++) {
+                               if (data->pcie_gen_performance.max <
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_performance.max =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_gen_performance.min >
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_performance.min =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_lane_performance.max <
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_performance.max =
+                                                       ps->performance_levels[i].pcie_lane;
+                               if (data->pcie_lane_performance.min >
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_performance.min =
+                                                       ps->performance_levels[i].pcie_lane;
+                       }
+                       break;
+               case PP_StateUILabel_Battery:
+                       data->use_pcie_power_saving_levels = true;
+
+                       for (i = 0; i < ps->performance_level_count; i++) {
+                               if (data->pcie_gen_power_saving.max <
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_power_saving.max =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_gen_power_saving.min >
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_power_saving.min =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_lane_power_saving.max <
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_power_saving.max =
+                                                       ps->performance_levels[i].pcie_lane;
+
+                               if (data->pcie_lane_power_saving.min >
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_power_saving.min =
+                                                       ps->performance_levels[i].pcie_lane;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int smu7_get_pp_table_entry_callback_func_v0(struct pp_hwmgr *hwmgr,
+                                       struct pp_hw_power_state *power_state,
+                                       unsigned int index, const void *clock_info)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_power_state  *ps = cast_phw_smu7_power_state(power_state);
+       const ATOM_PPLIB_CI_CLOCK_INFO *visland_clk_info = clock_info;
+       struct smu7_performance_level *performance_level;
+       uint32_t engine_clock, memory_clock;
+       uint16_t pcie_gen_from_bios;
+
+       engine_clock = visland_clk_info->ucEngineClockHigh << 16 | visland_clk_info->usEngineClockLow;
+       memory_clock = visland_clk_info->ucMemoryClockHigh << 16 | visland_clk_info->usMemoryClockLow;
+
+       if (!(data->mc_micro_code_feature & DISABLE_MC_LOADMICROCODE) && memory_clock > data->highest_mclk)
+               data->highest_mclk = memory_clock;
+
+       performance_level = &(ps->performance_levels
+                       [ps->performance_level_count++]);
+
+       PP_ASSERT_WITH_CODE(
+                       (ps->performance_level_count < smum_get_mac_definition(hwmgr->smumgr, SMU_MAX_LEVELS_GRAPHICS)),
+                       "Performance levels exceeds SMC limit!",
+                       return -EINVAL);
+
+       PP_ASSERT_WITH_CODE(
+                       (ps->performance_level_count <=
+                                       hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
+                       "Performance levels exceeds Driver limit!",
+                       return -EINVAL);
+
+       /* Performance levels are arranged from low to high. */
+       performance_level->memory_clock = memory_clock;
+       performance_level->engine_clock = engine_clock;
+
+       pcie_gen_from_bios = visland_clk_info->ucPCIEGen;
+
+       performance_level->pcie_gen = get_pcie_gen_support(data->pcie_gen_cap, pcie_gen_from_bios);
+       performance_level->pcie_lane = get_pcie_lane_support(data->pcie_lane_cap, visland_clk_info->usPCIELane);
+
+       return 0;
+}
+
+static int smu7_get_pp_table_entry_v0(struct pp_hwmgr *hwmgr,
+               unsigned long entry_index, struct pp_power_state *state)
+{
+       int result;
+       struct smu7_power_state *ps;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_clock_voltage_dependency_table *dep_mclk_table =
+                       hwmgr->dyn_state.vddci_dependency_on_mclk;
+
+       memset(&state->hardware, 0x00, sizeof(struct pp_hw_power_state));
+
+       state->hardware.magic = PHM_VIslands_Magic;
+
+       ps = (struct smu7_power_state *)(&state->hardware);
+
+       result = pp_tables_get_entry(hwmgr, entry_index, state,
+                       smu7_get_pp_table_entry_callback_func_v0);
+
+       /*
+        * This is the earliest time we have all the dependency table
+        * and the VBIOS boot state as
+        * PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot
+        * state if there is only one VDDCI/MCLK level, check if it's
+        * the same as VBIOS boot state
+        */
+       if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
+               if (dep_mclk_table->entries[0].clk !=
+                               data->vbios_boot_state.mclk_bootup_value)
+                       printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table "
+                                       "does not match VBIOS boot MCLK level");
+               if (dep_mclk_table->entries[0].v !=
+                               data->vbios_boot_state.vddci_bootup_value)
+                       printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table "
+                                       "does not match VBIOS boot VDDCI level");
+       }
+
+       /* set DC compatible flag if this state supports DC */
+       if (!state->validation.disallowOnDC)
+               ps->dc_compatible = true;
+
+       if (state->classification.flags & PP_StateClassificationFlag_ACPI)
+               data->acpi_pcie_gen = ps->performance_levels[0].pcie_gen;
+
+       ps->uvd_clks.vclk = state->uvd_clocks.VCLK;
+       ps->uvd_clks.dclk = state->uvd_clocks.DCLK;
+
+       if (!result) {
+               uint32_t i;
+
+               switch (state->classification.ui_label) {
+               case PP_StateUILabel_Performance:
+                       data->use_pcie_performance_levels = true;
+
+                       for (i = 0; i < ps->performance_level_count; i++) {
+                               if (data->pcie_gen_performance.max <
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_performance.max =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_gen_performance.min >
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_performance.min =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_lane_performance.max <
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_performance.max =
+                                                       ps->performance_levels[i].pcie_lane;
+
+                               if (data->pcie_lane_performance.min >
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_performance.min =
+                                                       ps->performance_levels[i].pcie_lane;
+                       }
+                       break;
+               case PP_StateUILabel_Battery:
+                       data->use_pcie_power_saving_levels = true;
+
+                       for (i = 0; i < ps->performance_level_count; i++) {
+                               if (data->pcie_gen_power_saving.max <
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_power_saving.max =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_gen_power_saving.min >
+                                               ps->performance_levels[i].pcie_gen)
+                                       data->pcie_gen_power_saving.min =
+                                                       ps->performance_levels[i].pcie_gen;
+
+                               if (data->pcie_lane_power_saving.max <
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_power_saving.max =
+                                                       ps->performance_levels[i].pcie_lane;
+
+                               if (data->pcie_lane_power_saving.min >
+                                               ps->performance_levels[i].pcie_lane)
+                                       data->pcie_lane_power_saving.min =
+                                                       ps->performance_levels[i].pcie_lane;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+       return 0;
+}
+
+static int smu7_get_pp_table_entry(struct pp_hwmgr *hwmgr,
+               unsigned long entry_index, struct pp_power_state *state)
+{
+       if (hwmgr->pp_table_version == PP_TABLE_V0)
+               return smu7_get_pp_table_entry_v0(hwmgr, entry_index, state);
+       else if (hwmgr->pp_table_version == PP_TABLE_V1)
+               return smu7_get_pp_table_entry_v1(hwmgr, entry_index, state);
+
+       return 0;
+}
+
+static int smu7_read_sensor(struct pp_hwmgr *hwmgr, int idx, int32_t *value)
+{
+       uint32_t sclk, mclk, activity_percent;
+       uint32_t offset;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       switch (idx) {
+       case AMDGPU_PP_SENSOR_GFX_SCLK:
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
+               sclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+               *value = sclk;
+               return 0;
+       case AMDGPU_PP_SENSOR_GFX_MCLK:
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
+               mclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+               *value = mclk;
+               return 0;
+       case AMDGPU_PP_SENSOR_GPU_LOAD:
+               offset = data->soft_regs_start + smum_get_offsetof(hwmgr->smumgr,
+                                                               SMU_SoftRegisters,
+                                                               AverageGraphicsActivity);
+
+               activity_percent = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset);
+               activity_percent += 0x80;
+               activity_percent >>= 8;
+               *value = activity_percent > 100 ? 100 : activity_percent;
+               return 0;
+       case AMDGPU_PP_SENSOR_GPU_TEMP:
+               *value = smu7_thermal_get_temperature(hwmgr);
+               return 0;
+       case AMDGPU_PP_SENSOR_UVD_POWER:
+               *value = data->uvd_power_gated ? 0 : 1;
+               return 0;
+       case AMDGPU_PP_SENSOR_VCE_POWER:
+               *value = data->vce_power_gated ? 0 : 1;
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int smu7_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
+{
+       const struct phm_set_power_state_input *states =
+                       (const struct phm_set_power_state_input *)input;
+       const struct smu7_power_state *smu7_ps =
+                       cast_const_phw_smu7_power_state(states->pnew_state);
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+       uint32_t sclk = smu7_ps->performance_levels
+                       [smu7_ps->performance_level_count - 1].engine_clock;
+       struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+       uint32_t mclk = smu7_ps->performance_levels
+                       [smu7_ps->performance_level_count - 1].memory_clock;
+       struct PP_Clocks min_clocks = {0};
+       uint32_t i;
+       struct cgs_display_info info = {0};
+
+       data->need_update_smu7_dpm_table = 0;
+
+       for (i = 0; i < sclk_table->count; i++) {
+               if (sclk == sclk_table->dpm_levels[i].value)
+                       break;
+       }
+
+       if (i >= sclk_table->count)
+               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
+       else {
+       /* TODO: Check SCLK in DAL's minimum clocks
+        * in case DeepSleep divider update is required.
+        */
+               if (data->display_timing.min_clock_in_sr != min_clocks.engineClockInSR &&
+                       (min_clocks.engineClockInSR >= SMU7_MINIMUM_ENGINE_CLOCK ||
+                               data->display_timing.min_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK))
+                       data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
+       }
+
+       for (i = 0; i < mclk_table->count; i++) {
+               if (mclk == mclk_table->dpm_levels[i].value)
+                       break;
+       }
+
+       if (i >= mclk_table->count)
+               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+
+       if (data->display_timing.num_existing_displays != info.display_count)
+               data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
+
+       return 0;
+}
+
+static uint16_t smu7_get_maximum_link_speed(struct pp_hwmgr *hwmgr,
+               const struct smu7_power_state *smu7_ps)
+{
+       uint32_t i;
+       uint32_t sclk, max_sclk = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+
+       for (i = 0; i < smu7_ps->performance_level_count; i++) {
+               sclk = smu7_ps->performance_levels[i].engine_clock;
+               if (max_sclk < sclk)
+                       max_sclk = sclk;
+       }
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+               if (dpm_table->sclk_table.dpm_levels[i].value == max_sclk)
+                       return (uint16_t) ((i >= dpm_table->pcie_speed_table.count) ?
+                                       dpm_table->pcie_speed_table.dpm_levels
+                                       [dpm_table->pcie_speed_table.count - 1].value :
+                                       dpm_table->pcie_speed_table.dpm_levels[i].value);
+       }
+
+       return 0;
+}
+
+static int smu7_request_link_speed_change_before_state_change(
+               struct pp_hwmgr *hwmgr, const void *input)
+{
+       const struct phm_set_power_state_input *states =
+                       (const struct phm_set_power_state_input *)input;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       const struct smu7_power_state *smu7_nps =
+                       cast_const_phw_smu7_power_state(states->pnew_state);
+       const struct smu7_power_state *polaris10_cps =
+                       cast_const_phw_smu7_power_state(states->pcurrent_state);
+
+       uint16_t target_link_speed = smu7_get_maximum_link_speed(hwmgr, smu7_nps);
+       uint16_t current_link_speed;
+
+       if (data->force_pcie_gen == PP_PCIEGenInvalid)
+               current_link_speed = smu7_get_maximum_link_speed(hwmgr, polaris10_cps);
+       else
+               current_link_speed = data->force_pcie_gen;
+
+       data->force_pcie_gen = PP_PCIEGenInvalid;
+       data->pspp_notify_required = false;
+
+       if (target_link_speed > current_link_speed) {
+               switch (target_link_speed) {
+               case PP_PCIEGen3:
+                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN3, false))
+                               break;
+                       data->force_pcie_gen = PP_PCIEGen2;
+                       if (current_link_speed == PP_PCIEGen2)
+                               break;
+               case PP_PCIEGen2:
+                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN2, false))
+                               break;
+               default:
+                       data->force_pcie_gen = smu7_get_current_pcie_speed(hwmgr);
+                       break;
+               }
+       } else {
+               if (target_link_speed < current_link_speed)
+                       data->pspp_notify_required = true;
+       }
+
+       return 0;
+}
+
+static int smu7_freeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (0 == data->need_update_smu7_dpm_table)
+               return 0;
+
+       if ((0 == data->sclk_dpm_key_disabled) &&
+               (data->need_update_smu7_dpm_table &
+                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to freeze SCLK DPM when DPM is disabled",
+                               );
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_SCLKDPM_FreezeLevel),
+                               "Failed to freeze SCLK DPM during FreezeSclkMclkDPM Function!",
+                               return -EINVAL);
+       }
+
+       if ((0 == data->mclk_dpm_key_disabled) &&
+               (data->need_update_smu7_dpm_table &
+                DPMTABLE_OD_UPDATE_MCLK)) {
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to freeze MCLK DPM when DPM is disabled",
+                               );
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_MCLKDPM_FreezeLevel),
+                               "Failed to freeze MCLK DPM during FreezeSclkMclkDPM Function!",
+                               return -EINVAL);
+       }
+
+       return 0;
+}
+
+static int smu7_populate_and_upload_sclk_mclk_dpm_levels(
+               struct pp_hwmgr *hwmgr, const void *input)
+{
+       int result = 0;
+       const struct phm_set_power_state_input *states =
+                       (const struct phm_set_power_state_input *)input;
+       const struct smu7_power_state *smu7_ps =
+                       cast_const_phw_smu7_power_state(states->pnew_state);
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t sclk = smu7_ps->performance_levels
+                       [smu7_ps->performance_level_count - 1].engine_clock;
+       uint32_t mclk = smu7_ps->performance_levels
+                       [smu7_ps->performance_level_count - 1].memory_clock;
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+
+       struct smu7_dpm_table *golden_dpm_table = &data->golden_dpm_table;
+       uint32_t dpm_count, clock_percent;
+       uint32_t i;
+
+       if (0 == data->need_update_smu7_dpm_table)
+               return 0;
+
+       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
+               dpm_table->sclk_table.dpm_levels
+               [dpm_table->sclk_table.count - 1].value = sclk;
+
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
+                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
+               /* Need to do calculation based on the golden DPM table
+                * as the Heatmap GPU Clock axis is also based on the default values
+                */
+                       PP_ASSERT_WITH_CODE(
+                               (golden_dpm_table->sclk_table.dpm_levels
+                                               [golden_dpm_table->sclk_table.count - 1].value != 0),
+                               "Divide by 0!",
+                               return -EINVAL);
+                       dpm_count = dpm_table->sclk_table.count < 2 ? 0 : dpm_table->sclk_table.count - 2;
+
+                       for (i = dpm_count; i > 1; i--) {
+                               if (sclk > golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value) {
+                                       clock_percent =
+                                             ((sclk
+                                               - golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value
+                                               ) * 100)
+                                               / golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value;
+
+                                       dpm_table->sclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->sclk_table.dpm_levels[i].value +
+                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
+                                                               clock_percent)/100;
+
+                               } else if (golden_dpm_table->sclk_table.dpm_levels[dpm_table->sclk_table.count-1].value > sclk) {
+                                       clock_percent =
+                                               ((golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count - 1].value
+                                               - sclk) * 100)
+                                               / golden_dpm_table->sclk_table.dpm_levels[golden_dpm_table->sclk_table.count-1].value;
+
+                                       dpm_table->sclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->sclk_table.dpm_levels[i].value -
+                                                       (golden_dpm_table->sclk_table.dpm_levels[i].value *
+                                                                       clock_percent) / 100;
+                               } else
+                                       dpm_table->sclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->sclk_table.dpm_levels[i].value;
+                       }
+               }
+       }
+
+       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
+               dpm_table->mclk_table.dpm_levels
+                       [dpm_table->mclk_table.count - 1].value = mclk;
+
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
+                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
+
+                       PP_ASSERT_WITH_CODE(
+                                       (golden_dpm_table->mclk_table.dpm_levels
+                                               [golden_dpm_table->mclk_table.count-1].value != 0),
+                                       "Divide by 0!",
+                                       return -EINVAL);
+                       dpm_count = dpm_table->mclk_table.count < 2 ? 0 : dpm_table->mclk_table.count - 2;
+                       for (i = dpm_count; i > 1; i--) {
+                               if (golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value < mclk) {
+                                       clock_percent = ((mclk -
+                                       golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value) * 100)
+                                       / golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value;
+
+                                       dpm_table->mclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->mclk_table.dpm_levels[i].value +
+                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
+                                                       clock_percent) / 100;
+
+                               } else if (golden_dpm_table->mclk_table.dpm_levels[dpm_table->mclk_table.count-1].value > mclk) {
+                                       clock_percent = (
+                                        (golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value - mclk)
+                                       * 100)
+                                       / golden_dpm_table->mclk_table.dpm_levels[golden_dpm_table->mclk_table.count-1].value;
+
+                                       dpm_table->mclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->mclk_table.dpm_levels[i].value -
+                                                       (golden_dpm_table->mclk_table.dpm_levels[i].value *
+                                                                       clock_percent) / 100;
+                               } else
+                                       dpm_table->mclk_table.dpm_levels[i].value =
+                                                       golden_dpm_table->mclk_table.dpm_levels[i].value;
+                       }
+               }
+       }
+
+       if (data->need_update_smu7_dpm_table &
+                       (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK)) {
+               result = smum_populate_all_graphic_levels(hwmgr);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
+                               return result);
+       }
+
+       if (data->need_update_smu7_dpm_table &
+                       (DPMTABLE_OD_UPDATE_MCLK + DPMTABLE_UPDATE_MCLK)) {
+               /*populate MCLK dpm table to SMU7 */
+               result = smum_populate_all_memory_levels(hwmgr);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
+                               return result);
+       }
+
+       return result;
+}
+
+static int smu7_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
+                         struct smu7_single_dpm_table *dpm_table,
+                       uint32_t low_limit, uint32_t high_limit)
+{
+       uint32_t i;
+
+       for (i = 0; i < dpm_table->count; i++) {
+               if ((dpm_table->dpm_levels[i].value < low_limit)
+               || (dpm_table->dpm_levels[i].value > high_limit))
+                       dpm_table->dpm_levels[i].enabled = false;
+               else
+                       dpm_table->dpm_levels[i].enabled = true;
+       }
+
+       return 0;
+}
+
+static int smu7_trim_dpm_states(struct pp_hwmgr *hwmgr,
+               const struct smu7_power_state *smu7_ps)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t high_limit_count;
+
+       PP_ASSERT_WITH_CODE((smu7_ps->performance_level_count >= 1),
+                       "power state did not have any performance level",
+                       return -EINVAL);
+
+       high_limit_count = (1 == smu7_ps->performance_level_count) ? 0 : 1;
+
+       smu7_trim_single_dpm_states(hwmgr,
+                       &(data->dpm_table.sclk_table),
+                       smu7_ps->performance_levels[0].engine_clock,
+                       smu7_ps->performance_levels[high_limit_count].engine_clock);
+
+       smu7_trim_single_dpm_states(hwmgr,
+                       &(data->dpm_table.mclk_table),
+                       smu7_ps->performance_levels[0].memory_clock,
+                       smu7_ps->performance_levels[high_limit_count].memory_clock);
+
+       return 0;
+}
+
+static int smu7_generate_dpm_level_enable_mask(
+               struct pp_hwmgr *hwmgr, const void *input)
+{
+       int result;
+       const struct phm_set_power_state_input *states =
+                       (const struct phm_set_power_state_input *)input;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       const struct smu7_power_state *smu7_ps =
+                       cast_const_phw_smu7_power_state(states->pnew_state);
+
+       result = smu7_trim_dpm_states(hwmgr, smu7_ps);
+       if (result)
+               return result;
+
+       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.sclk_table);
+       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.mclk_table);
+       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&data->dpm_table.pcie_speed_table);
+
+       return 0;
+}
+
+static int smu7_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (0 == data->need_update_smu7_dpm_table)
+               return 0;
+
+       if ((0 == data->sclk_dpm_key_disabled) &&
+               (data->need_update_smu7_dpm_table &
+               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
+
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to Unfreeze SCLK DPM when DPM is disabled",
+                               );
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
+                       "Failed to unfreeze SCLK DPM during UnFreezeSclkMclkDPM Function!",
+                       return -EINVAL);
+       }
+
+       if ((0 == data->mclk_dpm_key_disabled) &&
+               (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
+
+               PP_ASSERT_WITH_CODE(true == smum_is_dpm_running(hwmgr),
+                               "Trying to Unfreeze MCLK DPM when DPM is disabled",
+                               );
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(hwmgr->smumgr,
+                               PPSMC_MSG_SCLKDPM_UnfreezeLevel),
+                   "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!",
+                   return -EINVAL);
+       }
+
+       data->need_update_smu7_dpm_table = 0;
+
+       return 0;
+}
+
+static int smu7_notify_link_speed_change_after_state_change(
+               struct pp_hwmgr *hwmgr, const void *input)
+{
+       const struct phm_set_power_state_input *states =
+                       (const struct phm_set_power_state_input *)input;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       const struct smu7_power_state *smu7_ps =
+                       cast_const_phw_smu7_power_state(states->pnew_state);
+       uint16_t target_link_speed = smu7_get_maximum_link_speed(hwmgr, smu7_ps);
+       uint8_t  request;
+
+       if (data->pspp_notify_required) {
+               if (target_link_speed == PP_PCIEGen3)
+                       request = PCIE_PERF_REQ_GEN3;
+               else if (target_link_speed == PP_PCIEGen2)
+                       request = PCIE_PERF_REQ_GEN2;
+               else
+                       request = PCIE_PERF_REQ_GEN1;
+
+               if (request == PCIE_PERF_REQ_GEN1 &&
+                               smu7_get_current_pcie_speed(hwmgr) > 0)
+                       return 0;
+
+               if (acpi_pcie_perf_request(hwmgr->device, request, false)) {
+                       if (PP_PCIEGen2 == target_link_speed)
+                               printk("PSPP request to switch to Gen2 from Gen3 Failed!");
+                       else
+                               printk("PSPP request to switch to Gen1 from Gen2 Failed!");
+               }
+       }
+
+       return 0;
+}
+
+static int smu7_notify_smc_display(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (hwmgr->feature_mask & PP_VBI_TIME_SUPPORT_MASK)
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                       (PPSMC_Msg)PPSMC_MSG_SetVBITimeout, data->frame_time_x2);
+       return (smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)PPSMC_HasDisplay) == 0) ?  0 : -EINVAL;
+}
+
+static int smu7_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
+{
+       int tmp_result, result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       tmp_result = smu7_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to find DPM states clocks in DPM table!",
+                       result = tmp_result);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
+               tmp_result =
+                       smu7_request_link_speed_change_before_state_change(hwmgr, input);
+               PP_ASSERT_WITH_CODE((0 == tmp_result),
+                               "Failed to request link speed change before state change!",
+                               result = tmp_result);
+       }
+
+       tmp_result = smu7_freeze_sclk_mclk_dpm(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to freeze SCLK MCLK DPM!", result = tmp_result);
+
+       tmp_result = smu7_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to populate and upload SCLK MCLK DPM levels!",
+                       result = tmp_result);
+
+       tmp_result = smu7_generate_dpm_level_enable_mask(hwmgr, input);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to generate DPM level enabled mask!",
+                       result = tmp_result);
+
+       tmp_result = smum_update_sclk_threshold(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to update SCLK threshold!",
+                       result = tmp_result);
+
+       tmp_result = smu7_notify_smc_display(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to notify smc display settings!",
+                       result = tmp_result);
+
+       tmp_result = smu7_unfreeze_sclk_mclk_dpm(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to unfreeze SCLK MCLK DPM!",
+                       result = tmp_result);
+
+       tmp_result = smu7_upload_dpm_level_enable_mask(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to upload DPM level enabled mask!",
+                       result = tmp_result);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PCIEPerformanceRequest)) {
+               tmp_result =
+                       smu7_notify_link_speed_change_after_state_change(hwmgr, input);
+               PP_ASSERT_WITH_CODE((0 == tmp_result),
+                               "Failed to notify link speed change after state change!",
+                               result = tmp_result);
+       }
+       data->apply_optimized_settings = false;
+       return result;
+}
+
+static int smu7_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_pwm)
+{
+       hwmgr->thermal_controller.
+       advanceFanControlParameters.usMaxFanPWM = us_max_fan_pwm;
+
+       if (phm_is_hw_access_blocked(hwmgr))
+               return 0;
+
+       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                       PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm);
+}
+
+int smu7_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display)
+{
+       PPSMC_Msg msg = has_display ? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay;
+
+       return (smum_send_msg_to_smc(hwmgr->smumgr, msg) == 0) ?  0 : -1;
+}
+
+int smu7_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr)
+{
+       uint32_t num_active_displays = 0;
+       struct cgs_display_info info = {0};
+
+       info.mode_info = NULL;
+       cgs_get_active_displays_info(hwmgr->device, &info);
+
+       num_active_displays = info.display_count;
+
+       if (num_active_displays > 1 && hwmgr->display_config.multi_monitor_in_sync != true)
+               smu7_notify_smc_display_change(hwmgr, false);
+
+       return 0;
+}
+
+/**
+* Programs the display gap
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always OK
+*/
+int smu7_program_display_gap(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t num_active_displays = 0;
+       uint32_t display_gap = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
+       uint32_t display_gap2;
+       uint32_t pre_vbi_time_in_us;
+       uint32_t frame_time_in_us;
+       uint32_t ref_clock;
+       uint32_t refresh_rate = 0;
+       struct cgs_display_info info = {0};
+       struct cgs_mode_info mode_info;
+
+       info.mode_info = &mode_info;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+       num_active_displays = info.display_count;
+
+       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL, DISP_GAP, (num_active_displays > 0) ? DISPLAY_GAP_VBLANK_OR_WM : DISPLAY_GAP_IGNORE);
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL, display_gap);
+
+       ref_clock = mode_info.ref_clock;
+       refresh_rate = mode_info.refresh_rate;
+
+       if (0 == refresh_rate)
+               refresh_rate = 60;
+
+       frame_time_in_us = 1000000 / refresh_rate;
+
+       pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
+       data->frame_time_x2 = frame_time_in_us * 2 / 100;
+
+       display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       data->soft_regs_start + smum_get_offsetof(hwmgr->smumgr,
+                                                       SMU_SoftRegisters,
+                                                       PreVBlankGap), 0x64);
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       data->soft_regs_start + smum_get_offsetof(hwmgr->smumgr,
+                                                       SMU_SoftRegisters,
+                                                       VBlankTimeout),
+                                       (frame_time_in_us - pre_vbi_time_in_us));
+
+       return 0;
+}
+
+int smu7_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
+{
+       return smu7_program_display_gap(hwmgr);
+}
+
+/**
+*  Set maximum target operating fan output RPM
+*
+* @param    hwmgr:  the address of the powerplay hardware manager.
+* @param    usMaxFanRpm:  max operating fan RPM value.
+* @return   The response that came from the SMC.
+*/
+static int smu7_set_max_fan_rpm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_rpm)
+{
+       hwmgr->thermal_controller.
+       advanceFanControlParameters.usMaxFanRPM = us_max_fan_rpm;
+
+       if (phm_is_hw_access_blocked(hwmgr))
+               return 0;
+
+       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                       PPSMC_MSG_SetFanRpmMax, us_max_fan_rpm);
+}
+
+int smu7_register_internal_thermal_interrupt(struct pp_hwmgr *hwmgr,
+                                       const void *thermal_interrupt_info)
+{
+       return 0;
+}
+
+bool smu7_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       bool is_update_required = false;
+       struct cgs_display_info info = {0, 0, NULL};
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+
+       if (data->display_timing.num_existing_displays != info.display_count)
+               is_update_required = true;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
+               if (data->display_timing.min_clock_in_sr != hwmgr->display_config.min_core_set_clock_in_sr &&
+                       (data->display_timing.min_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK ||
+                       hwmgr->display_config.min_core_set_clock_in_sr >= SMU7_MINIMUM_ENGINE_CLOCK))
+                       is_update_required = true;
+       }
+       return is_update_required;
+}
+
+static inline bool smu7_are_power_levels_equal(const struct smu7_performance_level *pl1,
+                                                          const struct smu7_performance_level *pl2)
+{
+       return ((pl1->memory_clock == pl2->memory_clock) &&
+                 (pl1->engine_clock == pl2->engine_clock) &&
+                 (pl1->pcie_gen == pl2->pcie_gen) &&
+                 (pl1->pcie_lane == pl2->pcie_lane));
+}
+
+int smu7_check_states_equal(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *pstate1, const struct pp_hw_power_state *pstate2, bool *equal)
+{
+       const struct smu7_power_state *psa = cast_const_phw_smu7_power_state(pstate1);
+       const struct smu7_power_state *psb = cast_const_phw_smu7_power_state(pstate2);
+       int i;
+
+       if (pstate1 == NULL || pstate2 == NULL || equal == NULL)
+               return -EINVAL;
+
+       /* If the two states don't even have the same number of performance levels they cannot be the same state. */
+       if (psa->performance_level_count != psb->performance_level_count) {
+               *equal = false;
+               return 0;
+       }
+
+       for (i = 0; i < psa->performance_level_count; i++) {
+               if (!smu7_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
+                       /* If we have found even one performance level pair that is different the states are different. */
+                       *equal = false;
+                       return 0;
+               }
+       }
+
+       /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
+       *equal = ((psa->uvd_clks.vclk == psb->uvd_clks.vclk) && (psa->uvd_clks.dclk == psb->uvd_clks.dclk));
+       *equal &= ((psa->vce_clks.evclk == psb->vce_clks.evclk) && (psa->vce_clks.ecclk == psb->vce_clks.ecclk));
+       *equal &= (psa->sclk_threshold == psb->sclk_threshold);
+
+       return 0;
+}
+
+int smu7_upload_mc_firmware(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       uint32_t vbios_version;
+       uint32_t tmp;
+
+       /* Read MC indirect register offset 0x9F bits [3:0] to see
+        * if VBIOS has already loaded a full version of MC ucode
+        * or not.
+        */
+
+       smu7_get_mc_microcode_version(hwmgr);
+       vbios_version = hwmgr->microcode_version_info.MC & 0xf;
+
+       data->need_long_memory_training = false;
+
+       cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX,
+                                                       ixMC_IO_DEBUG_UP_13);
+       tmp = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
+
+       if (tmp & (1 << 23)) {
+               data->mem_latency_high = MEM_LATENCY_HIGH;
+               data->mem_latency_low = MEM_LATENCY_LOW;
+       } else {
+               data->mem_latency_high = 330;
+               data->mem_latency_low = 330;
+       }
+
+       return 0;
+}
+
+static int smu7_read_clock_registers(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       data->clock_registers.vCG_SPLL_FUNC_CNTL         =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL);
+       data->clock_registers.vCG_SPLL_FUNC_CNTL_2       =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_2);
+       data->clock_registers.vCG_SPLL_FUNC_CNTL_3       =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_3);
+       data->clock_registers.vCG_SPLL_FUNC_CNTL_4       =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_4);
+       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM   =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM);
+       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2 =
+               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM_2);
+       data->clock_registers.vDLL_CNTL                  =
+               cgs_read_register(hwmgr->device, mmDLL_CNTL);
+       data->clock_registers.vMCLK_PWRMGT_CNTL          =
+               cgs_read_register(hwmgr->device, mmMCLK_PWRMGT_CNTL);
+       data->clock_registers.vMPLL_AD_FUNC_CNTL         =
+               cgs_read_register(hwmgr->device, mmMPLL_AD_FUNC_CNTL);
+       data->clock_registers.vMPLL_DQ_FUNC_CNTL         =
+               cgs_read_register(hwmgr->device, mmMPLL_DQ_FUNC_CNTL);
+       data->clock_registers.vMPLL_FUNC_CNTL            =
+               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL);
+       data->clock_registers.vMPLL_FUNC_CNTL_1          =
+               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_1);
+       data->clock_registers.vMPLL_FUNC_CNTL_2          =
+               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_2);
+       data->clock_registers.vMPLL_SS1                  =
+               cgs_read_register(hwmgr->device, mmMPLL_SS1);
+       data->clock_registers.vMPLL_SS2                  =
+               cgs_read_register(hwmgr->device, mmMPLL_SS2);
+       return 0;
+
+}
+
+/**
+ * Find out if memory is GDDR5.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+static int smu7_get_memory_type(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t temp;
+
+       temp = cgs_read_register(hwmgr->device, mmMC_SEQ_MISC0);
+
+       data->is_memory_gddr5 = (MC_SEQ_MISC0_GDDR5_VALUE ==
+                       ((temp & MC_SEQ_MISC0_GDDR5_MASK) >>
+                        MC_SEQ_MISC0_GDDR5_SHIFT));
+
+       return 0;
+}
+
+/**
+ * Enables Dynamic Power Management by SMC
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+static int smu7_enable_acpi_power_management(struct pp_hwmgr *hwmgr)
+{
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       GENERAL_PWRMGT, STATIC_PM_EN, 1);
+
+       return 0;
+}
+
+/**
+ * Initialize PowerGating States for different engines
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+static int smu7_init_power_gate_state(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       data->uvd_power_gated = false;
+       data->vce_power_gated = false;
+       data->samu_power_gated = false;
+
+       return 0;
+}
+
+static int smu7_init_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       data->low_sclk_interrupt_threshold = 0;
+       return 0;
+}
+
+int smu7_setup_asic_task(struct pp_hwmgr *hwmgr)
+{
+       int tmp_result, result = 0;
+
+       smu7_upload_mc_firmware(hwmgr);
+
+       tmp_result = smu7_read_clock_registers(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to read clock registers!", result = tmp_result);
+
+       tmp_result = smu7_get_memory_type(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to get memory type!", result = tmp_result);
+
+       tmp_result = smu7_enable_acpi_power_management(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to enable ACPI power management!", result = tmp_result);
+
+       tmp_result = smu7_init_power_gate_state(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to init power gate state!", result = tmp_result);
+
+       tmp_result = smu7_get_mc_microcode_version(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to get MC microcode version!", result = tmp_result);
+
+       tmp_result = smu7_init_sclk_threshold(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == tmp_result),
+                       "Failed to init sclk threshold!", result = tmp_result);
+
+       return result;
+}
+
+static int smu7_force_clock_level(struct pp_hwmgr *hwmgr,
+               enum pp_clock_type type, uint32_t mask)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
+               return -EINVAL;
+
+       switch (type) {
+       case PP_SCLK:
+               if (!data->sclk_dpm_key_disabled)
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
+                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask & mask);
+               break;
+       case PP_MCLK:
+               if (!data->mclk_dpm_key_disabled)
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
+                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask & mask);
+               break;
+       case PP_PCIE:
+       {
+               uint32_t tmp = mask & data->dpm_level_enable_mask.pcie_dpm_enable_mask;
+               uint32_t level = 0;
+
+               while (tmp >>= 1)
+                       level++;
+
+               if (!data->pcie_dpm_key_disabled)
+                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                                       PPSMC_MSG_PCIeDPM_ForceLevel,
+                                       level);
+               break;
+       }
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int smu7_print_clock_levels(struct pp_hwmgr *hwmgr,
+               enum pp_clock_type type, char *buf)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+       struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+       struct smu7_single_dpm_table *pcie_table = &(data->dpm_table.pcie_speed_table);
+       int i, now, size = 0;
+       uint32_t clock, pcie_speed;
+
+       switch (type) {
+       case PP_SCLK:
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
+               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+
+               for (i = 0; i < sclk_table->count; i++) {
+                       if (clock > sclk_table->dpm_levels[i].value)
+                               continue;
+                       break;
+               }
+               now = i;
+
+               for (i = 0; i < sclk_table->count; i++)
+                       size += sprintf(buf + size, "%d: %uMhz %s\n",
+                                       i, sclk_table->dpm_levels[i].value / 100,
+                                       (i == now) ? "*" : "");
+               break;
+       case PP_MCLK:
+               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
+               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
+
+               for (i = 0; i < mclk_table->count; i++) {
+                       if (clock > mclk_table->dpm_levels[i].value)
+                               continue;
+                       break;
+               }
+               now = i;
+
+               for (i = 0; i < mclk_table->count; i++)
+                       size += sprintf(buf + size, "%d: %uMhz %s\n",
+                                       i, mclk_table->dpm_levels[i].value / 100,
+                                       (i == now) ? "*" : "");
+               break;
+       case PP_PCIE:
+               pcie_speed = smu7_get_current_pcie_speed(hwmgr);
+               for (i = 0; i < pcie_table->count; i++) {
+                       if (pcie_speed != pcie_table->dpm_levels[i].value)
+                               continue;
+                       break;
+               }
+               now = i;
+
+               for (i = 0; i < pcie_table->count; i++)
+                       size += sprintf(buf + size, "%d: %s %s\n", i,
+                                       (pcie_table->dpm_levels[i].value == 0) ? "2.5GB, x8" :
+                                       (pcie_table->dpm_levels[i].value == 1) ? "5.0GB, x16" :
+                                       (pcie_table->dpm_levels[i].value == 2) ? "8.0GB, x16" : "",
+                                       (i == now) ? "*" : "");
+               break;
+       default:
+               break;
+       }
+       return size;
+}
+
+static int smu7_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+       if (mode) {
+               /* stop auto-manage */
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl))
+                       smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+               smu7_fan_ctrl_set_static_mode(hwmgr, mode);
+       } else
+               /* restart auto-manage */
+               smu7_fan_ctrl_reset_fan_speed_to_default(hwmgr);
+
+       return 0;
+}
+
+static int smu7_get_fan_control_mode(struct pp_hwmgr *hwmgr)
+{
+       if (hwmgr->fan_ctrl_is_in_default_mode)
+               return hwmgr->fan_ctrl_default_mode;
+       else
+               return PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               CG_FDO_CTRL2, FDO_PWM_MODE);
+}
+
+static int smu7_get_sclk_od(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
+       struct smu7_single_dpm_table *golden_sclk_table =
+                       &(data->golden_dpm_table.sclk_table);
+       int value;
+
+       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
+                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
+                       100 /
+                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+
+       return value;
+}
+
+static int smu7_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *golden_sclk_table =
+                       &(data->golden_dpm_table.sclk_table);
+       struct pp_power_state  *ps;
+       struct smu7_power_state  *smu7_ps;
+
+       if (value > 20)
+               value = 20;
+
+       ps = hwmgr->request_ps;
+
+       if (ps == NULL)
+               return -EINVAL;
+
+       smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+       smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].engine_clock =
+                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
+                       value / 100 +
+                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
+
+       return 0;
+}
+
+static int smu7_get_mclk_od(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
+       struct smu7_single_dpm_table *golden_mclk_table =
+                       &(data->golden_dpm_table.mclk_table);
+       int value;
+
+       value = (mclk_table->dpm_levels[mclk_table->count - 1].value -
+                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) *
+                       100 /
+                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
+
+       return value;
+}
+
+static int smu7_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_single_dpm_table *golden_mclk_table =
+                       &(data->golden_dpm_table.mclk_table);
+       struct pp_power_state  *ps;
+       struct smu7_power_state  *smu7_ps;
+
+       if (value > 20)
+               value = 20;
+
+       ps = hwmgr->request_ps;
+
+       if (ps == NULL)
+               return -EINVAL;
+
+       smu7_ps = cast_phw_smu7_power_state(&ps->hardware);
+
+       smu7_ps->performance_levels[smu7_ps->performance_level_count - 1].memory_clock =
+                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value *
+                       value / 100 +
+                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
+
+       return 0;
+}
+
+
+static int smu7_get_sclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)hwmgr->pptable;
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_sclk_table;
+       int i;
+
+       if (table_info == NULL)
+               return -EINVAL;
+
+       dep_sclk_table = table_info->vdd_dep_on_sclk;
+
+       for (i = 0; i < dep_sclk_table->count; i++) {
+               clocks->clock[i] = dep_sclk_table->entries[i].clk;
+               clocks->count++;
+       }
+       return 0;
+}
+
+static uint32_t smu7_get_mem_latency(struct pp_hwmgr *hwmgr, uint32_t clk)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (clk >= MEM_FREQ_LOW_LATENCY && clk < MEM_FREQ_HIGH_LATENCY)
+               return data->mem_latency_high;
+       else if (clk >= MEM_FREQ_HIGH_LATENCY)
+               return data->mem_latency_low;
+       else
+               return MEM_LATENCY_ERR;
+}
+
+static int smu7_get_mclks(struct pp_hwmgr *hwmgr, struct amd_pp_clocks *clocks)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)hwmgr->pptable;
+       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table;
+       int i;
+
+       if (table_info == NULL)
+               return -EINVAL;
+
+       dep_mclk_table = table_info->vdd_dep_on_mclk;
+
+       for (i = 0; i < dep_mclk_table->count; i++) {
+               clocks->clock[i] = dep_mclk_table->entries[i].clk;
+               clocks->latency[i] = smu7_get_mem_latency(hwmgr,
+                                               dep_mclk_table->entries[i].clk);
+               clocks->count++;
+       }
+       return 0;
+}
+
+static int smu7_get_clock_by_type(struct pp_hwmgr *hwmgr, enum amd_pp_clock_type type,
+                                               struct amd_pp_clocks *clocks)
+{
+       switch (type) {
+       case amd_pp_sys_clock:
+               smu7_get_sclks(hwmgr, clocks);
+               break;
+       case amd_pp_mem_clock:
+               smu7_get_mclks(hwmgr, clocks);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
+       .backend_init = &smu7_hwmgr_backend_init,
+       .backend_fini = &phm_hwmgr_backend_fini,
+       .asic_setup = &smu7_setup_asic_task,
+       .dynamic_state_management_enable = &smu7_enable_dpm_tasks,
+       .apply_state_adjust_rules = smu7_apply_state_adjust_rules,
+       .force_dpm_level = &smu7_force_dpm_level,
+       .power_state_set = smu7_set_power_state_tasks,
+       .get_power_state_size = smu7_get_power_state_size,
+       .get_mclk = smu7_dpm_get_mclk,
+       .get_sclk = smu7_dpm_get_sclk,
+       .patch_boot_state = smu7_dpm_patch_boot_state,
+       .get_pp_table_entry = smu7_get_pp_table_entry,
+       .get_num_of_pp_table_entries = smu7_get_number_of_powerplay_table_entries,
+       .powerdown_uvd = smu7_powerdown_uvd,
+       .powergate_uvd = smu7_powergate_uvd,
+       .powergate_vce = smu7_powergate_vce,
+       .disable_clock_power_gating = smu7_disable_clock_power_gating,
+       .update_clock_gatings = smu7_update_clock_gatings,
+       .notify_smc_display_config_after_ps_adjustment = smu7_notify_smc_display_config_after_ps_adjustment,
+       .display_config_changed = smu7_display_configuration_changed_task,
+       .set_max_fan_pwm_output = smu7_set_max_fan_pwm_output,
+       .set_max_fan_rpm_output = smu7_set_max_fan_rpm_output,
+       .get_temperature = smu7_thermal_get_temperature,
+       .stop_thermal_controller = smu7_thermal_stop_thermal_controller,
+       .get_fan_speed_info = smu7_fan_ctrl_get_fan_speed_info,
+       .get_fan_speed_percent = smu7_fan_ctrl_get_fan_speed_percent,
+       .set_fan_speed_percent = smu7_fan_ctrl_set_fan_speed_percent,
+       .reset_fan_speed_to_default = smu7_fan_ctrl_reset_fan_speed_to_default,
+       .get_fan_speed_rpm = smu7_fan_ctrl_get_fan_speed_rpm,
+       .set_fan_speed_rpm = smu7_fan_ctrl_set_fan_speed_rpm,
+       .uninitialize_thermal_controller = smu7_thermal_ctrl_uninitialize_thermal_controller,
+       .register_internal_thermal_interrupt = smu7_register_internal_thermal_interrupt,
+       .check_smc_update_required_for_display_configuration = smu7_check_smc_update_required_for_display_configuration,
+       .check_states_equal = smu7_check_states_equal,
+       .set_fan_control_mode = smu7_set_fan_control_mode,
+       .get_fan_control_mode = smu7_get_fan_control_mode,
+       .force_clock_level = smu7_force_clock_level,
+       .print_clock_levels = smu7_print_clock_levels,
+       .enable_per_cu_power_gating = smu7_enable_per_cu_power_gating,
+       .get_sclk_od = smu7_get_sclk_od,
+       .set_sclk_od = smu7_set_sclk_od,
+       .get_mclk_od = smu7_get_mclk_od,
+       .set_mclk_od = smu7_set_mclk_od,
+       .get_clock_by_type = smu7_get_clock_by_type,
+       .read_sensor = smu7_read_sensor,
+};
+
+uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
+               uint32_t clock_insr)
+{
+       uint8_t i;
+       uint32_t temp;
+       uint32_t min = max(clock_insr, (uint32_t)SMU7_MINIMUM_ENGINE_CLOCK);
+
+       PP_ASSERT_WITH_CODE((clock >= min), "Engine clock can't satisfy stutter requirement!", return 0);
+       for (i = SMU7_MAX_DEEPSLEEP_DIVIDER_ID;  ; i--) {
+               temp = clock >> i;
+
+               if (temp >= min || i == 0)
+                       break;
+       }
+       return i;
+}
+
+int smu7_hwmgr_init(struct pp_hwmgr *hwmgr)
+{
+       int ret = 0;
+
+       hwmgr->hwmgr_func = &smu7_hwmgr_funcs;
+       if (hwmgr->pp_table_version == PP_TABLE_V0)
+               hwmgr->pptable_func = &pptable_funcs;
+       else if (hwmgr->pp_table_version == PP_TABLE_V1)
+               hwmgr->pptable_func = &pptable_v1_0_funcs;
+
+       pp_smu7_thermal_initialize(hwmgr);
+       return ret;
+}
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.h
new file mode 100644 (file)
index 0000000..27e7f76
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2015 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 _SMU7_HWMGR_H
+#define _SMU7_HWMGR_H
+
+#include "hwmgr.h"
+#include "ppatomctrl.h"
+
+#define SMU7_MAX_HARDWARE_POWERLEVELS   2
+
+#define SMU7_VOLTAGE_CONTROL_NONE                   0x0
+#define SMU7_VOLTAGE_CONTROL_BY_GPIO                0x1
+#define SMU7_VOLTAGE_CONTROL_BY_SVID2               0x2
+#define SMU7_VOLTAGE_CONTROL_MERGED                 0x3
+
+#define DPMTABLE_OD_UPDATE_SCLK     0x00000001
+#define DPMTABLE_OD_UPDATE_MCLK     0x00000002
+#define DPMTABLE_UPDATE_SCLK        0x00000004
+#define DPMTABLE_UPDATE_MCLK        0x00000008
+
+enum gpu_pt_config_reg_type {
+       GPU_CONFIGREG_MMR = 0,
+       GPU_CONFIGREG_SMC_IND,
+       GPU_CONFIGREG_DIDT_IND,
+       GPU_CONFIGREG_GC_CAC_IND,
+       GPU_CONFIGREG_CACHE,
+       GPU_CONFIGREG_MAX
+};
+
+struct gpu_pt_config_reg {
+       uint32_t                           offset;
+       uint32_t                           mask;
+       uint32_t                           shift;
+       uint32_t                           value;
+       enum gpu_pt_config_reg_type       type;
+};
+
+struct smu7_performance_level {
+       uint32_t  memory_clock;
+       uint32_t  engine_clock;
+       uint16_t  pcie_gen;
+       uint16_t  pcie_lane;
+};
+
+struct smu7_thermal_temperature_setting {
+       long temperature_low;
+       long temperature_high;
+       long temperature_shutdown;
+};
+
+struct smu7_uvd_clocks {
+       uint32_t  vclk;
+       uint32_t  dclk;
+};
+
+struct smu7_vce_clocks {
+       uint32_t  evclk;
+       uint32_t  ecclk;
+};
+
+struct smu7_power_state {
+       uint32_t                  magic;
+       struct smu7_uvd_clocks    uvd_clks;
+       struct smu7_vce_clocks    vce_clks;
+       uint32_t                  sam_clk;
+       uint16_t                  performance_level_count;
+       bool                      dc_compatible;
+       uint32_t                  sclk_threshold;
+       struct smu7_performance_level  performance_levels[SMU7_MAX_HARDWARE_POWERLEVELS];
+};
+
+struct smu7_dpm_level {
+       bool    enabled;
+       uint32_t        value;
+       uint32_t        param1;
+};
+
+#define SMU7_MAX_DEEPSLEEP_DIVIDER_ID 5
+#define MAX_REGULAR_DPM_NUMBER 8
+#define SMU7_MINIMUM_ENGINE_CLOCK 2500
+
+struct smu7_single_dpm_table {
+       uint32_t                count;
+       struct smu7_dpm_level   dpm_levels[MAX_REGULAR_DPM_NUMBER];
+};
+
+struct smu7_dpm_table {
+       struct smu7_single_dpm_table  sclk_table;
+       struct smu7_single_dpm_table  mclk_table;
+       struct smu7_single_dpm_table  pcie_speed_table;
+       struct smu7_single_dpm_table  vddc_table;
+       struct smu7_single_dpm_table  vddci_table;
+       struct smu7_single_dpm_table  mvdd_table;
+};
+
+struct smu7_clock_registers {
+       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  vDLL_CNTL;
+       uint32_t  vMCLK_PWRMGT_CNTL;
+       uint32_t  vMPLL_AD_FUNC_CNTL;
+       uint32_t  vMPLL_DQ_FUNC_CNTL;
+       uint32_t  vMPLL_FUNC_CNTL;
+       uint32_t  vMPLL_FUNC_CNTL_1;
+       uint32_t  vMPLL_FUNC_CNTL_2;
+       uint32_t  vMPLL_SS1;
+       uint32_t  vMPLL_SS2;
+};
+
+#define DISABLE_MC_LOADMICROCODE   1
+#define DISABLE_MC_CFGPROGRAMMING  2
+
+struct smu7_voltage_smio_registers {
+       uint32_t vS0_VID_LOWER_SMIO_CNTL;
+};
+
+#define SMU7_MAX_LEAKAGE_COUNT  8
+
+struct smu7_leakage_voltage {
+       uint16_t  count;
+       uint16_t  leakage_id[SMU7_MAX_LEAKAGE_COUNT];
+       uint16_t  actual_voltage[SMU7_MAX_LEAKAGE_COUNT];
+};
+
+struct smu7_vbios_boot_state {
+       uint16_t    mvdd_bootup_value;
+       uint16_t    vddc_bootup_value;
+       uint16_t    vddci_bootup_value;
+       uint16_t    vddgfx_bootup_value;
+       uint32_t    sclk_bootup_value;
+       uint32_t    mclk_bootup_value;
+       uint16_t    pcie_gen_bootup_value;
+       uint16_t    pcie_lane_bootup_value;
+};
+
+struct smu7_display_timing {
+       uint32_t  min_clock_in_sr;
+       uint32_t  num_existing_displays;
+};
+
+struct smu7_dpmlevel_enable_mask {
+       uint32_t  uvd_dpm_enable_mask;
+       uint32_t  vce_dpm_enable_mask;
+       uint32_t  acp_dpm_enable_mask;
+       uint32_t  samu_dpm_enable_mask;
+       uint32_t  sclk_dpm_enable_mask;
+       uint32_t  mclk_dpm_enable_mask;
+       uint32_t  pcie_dpm_enable_mask;
+};
+
+struct smu7_pcie_perf_range {
+       uint16_t  max;
+       uint16_t  min;
+};
+
+struct smu7_hwmgr {
+       struct smu7_dpm_table                   dpm_table;
+       struct smu7_dpm_table                   golden_dpm_table;
+
+       uint32_t                                                voting_rights_clients0;
+       uint32_t                                                voting_rights_clients1;
+       uint32_t                                                voting_rights_clients2;
+       uint32_t                                                voting_rights_clients3;
+       uint32_t                                                voting_rights_clients4;
+       uint32_t                                                voting_rights_clients5;
+       uint32_t                                                voting_rights_clients6;
+       uint32_t                                                voting_rights_clients7;
+       uint32_t                                                static_screen_threshold_unit;
+       uint32_t                                                static_screen_threshold;
+       uint32_t                                                voltage_control;
+       uint32_t                                                vdd_gfx_control;
+       uint32_t                                                vddc_vddgfx_delta;
+       uint32_t                                                active_auto_throttle_sources;
+
+       struct smu7_clock_registers            clock_registers;
+
+       bool                           is_memory_gddr5;
+       uint16_t                       acpi_vddc;
+       bool                           pspp_notify_required;
+       uint16_t                       force_pcie_gen;
+       uint16_t                       acpi_pcie_gen;
+       uint32_t                       pcie_gen_cap;
+       uint32_t                       pcie_lane_cap;
+       uint32_t                       pcie_spc_cap;
+       struct smu7_leakage_voltage          vddc_leakage;
+       struct smu7_leakage_voltage          vddci_leakage;
+       struct smu7_leakage_voltage          vddcgfx_leakage;
+
+       uint32_t                             mvdd_control;
+       uint32_t                             vddc_mask_low;
+       uint32_t                             mvdd_mask_low;
+       uint16_t                            max_vddc_in_pptable;
+       uint16_t                            min_vddc_in_pptable;
+       uint16_t                            max_vddci_in_pptable;
+       uint16_t                            min_vddci_in_pptable;
+       bool                                is_uvd_enabled;
+       struct smu7_vbios_boot_state        vbios_boot_state;
+
+       bool                           pcie_performance_request;
+       bool                           battery_state;
+       bool                           is_tlu_enabled;
+       bool                           disable_handshake;
+       bool                           smc_voltage_control_enabled;
+       bool                           vbi_time_out_support;
+
+       uint32_t                       soft_regs_start;
+       /* ---- Stuff originally coming from Evergreen ---- */
+       uint32_t                             vddci_control;
+       struct pp_atomctrl_voltage_table     vddc_voltage_table;
+       struct pp_atomctrl_voltage_table     vddci_voltage_table;
+       struct pp_atomctrl_voltage_table     mvdd_voltage_table;
+       struct pp_atomctrl_voltage_table     vddgfx_voltage_table;
+
+       uint32_t                             mgcg_cgtt_local2;
+       uint32_t                             mgcg_cgtt_local3;
+       uint32_t                             gpio_debug;
+       uint32_t                             mc_micro_code_feature;
+       uint32_t                             highest_mclk;
+       uint16_t                             acpi_vddci;
+       uint8_t                              mvdd_high_index;
+       uint8_t                              mvdd_low_index;
+       bool                                 dll_default_on;
+       bool                                 performance_request_registered;
+
+       /* ---- Low Power Features ---- */
+       bool                           ulv_supported;
+
+       /* ---- CAC Stuff ---- */
+       uint32_t                       cac_table_start;
+       bool                           cac_configuration_required;
+       bool                           driver_calculate_cac_leakage;
+       bool                           cac_enabled;
+
+       /* ---- DPM2 Parameters ---- */
+       uint32_t                       power_containment_features;
+       bool                           enable_dte_feature;
+       bool                           enable_tdc_limit_feature;
+       bool                           enable_pkg_pwr_tracking_feature;
+       bool                           disable_uvd_power_tune_feature;
+
+
+       uint32_t                       dte_tj_offset;
+       uint32_t                       fast_watermark_threshold;
+
+       /* ---- Phase Shedding ---- */
+       bool                           vddc_phase_shed_control;
+
+       /* ---- DI/DT ---- */
+       struct smu7_display_timing        display_timing;
+
+       /* ---- Thermal Temperature Setting ---- */
+       struct smu7_thermal_temperature_setting  thermal_temp_setting;
+       struct smu7_dpmlevel_enable_mask     dpm_level_enable_mask;
+       uint32_t                                  need_update_smu7_dpm_table;
+       uint32_t                                  sclk_dpm_key_disabled;
+       uint32_t                                  mclk_dpm_key_disabled;
+       uint32_t                                  pcie_dpm_key_disabled;
+       uint32_t                                  min_engine_clocks;
+       struct smu7_pcie_perf_range          pcie_gen_performance;
+       struct smu7_pcie_perf_range          pcie_lane_performance;
+       struct smu7_pcie_perf_range          pcie_gen_power_saving;
+       struct smu7_pcie_perf_range          pcie_lane_power_saving;
+       bool                                      use_pcie_performance_levels;
+       bool                                      use_pcie_power_saving_levels;
+       uint32_t                                  mclk_activity_target;
+       uint32_t                                  mclk_dpm0_activity_target;
+       uint32_t                                  low_sclk_interrupt_threshold;
+       uint32_t                                  last_mclk_dpm_enable_mask;
+       bool                                      uvd_enabled;
+
+       /* ---- Power Gating States ---- */
+       bool                           uvd_power_gated;
+       bool                           vce_power_gated;
+       bool                           samu_power_gated;
+       bool                           need_long_memory_training;
+
+       /* Application power optimization parameters */
+       bool                               update_up_hyst;
+       bool                               update_down_hyst;
+       uint32_t                           down_hyst;
+       uint32_t                           up_hyst;
+       uint32_t disable_dpm_mask;
+       bool apply_optimized_settings;
+
+       uint32_t                              avfs_vdroop_override_setting;
+       bool                                  apply_avfs_cks_off_voltage;
+       uint32_t                              frame_time_x2;
+       uint16_t                              mem_latency_high;
+       uint16_t                              mem_latency_low;
+};
+
+/* To convert to Q8.8 format for firmware */
+#define SMU7_Q88_FORMAT_CONVERSION_UNIT             256
+
+enum SMU7_I2CLineID {
+       SMU7_I2CLineID_DDC1 = 0x90,
+       SMU7_I2CLineID_DDC2 = 0x91,
+       SMU7_I2CLineID_DDC3 = 0x92,
+       SMU7_I2CLineID_DDC4 = 0x93,
+       SMU7_I2CLineID_DDC5 = 0x94,
+       SMU7_I2CLineID_DDC6 = 0x95,
+       SMU7_I2CLineID_SCLSDA = 0x96,
+       SMU7_I2CLineID_DDCVGA = 0x97
+};
+
+#define SMU7_I2C_DDC1DATA          0
+#define SMU7_I2C_DDC1CLK           1
+#define SMU7_I2C_DDC2DATA          2
+#define SMU7_I2C_DDC2CLK           3
+#define SMU7_I2C_DDC3DATA          4
+#define SMU7_I2C_DDC3CLK           5
+#define SMU7_I2C_SDA               40
+#define SMU7_I2C_SCL               41
+#define SMU7_I2C_DDC4DATA          65
+#define SMU7_I2C_DDC4CLK           66
+#define SMU7_I2C_DDC5DATA          0x48
+#define SMU7_I2C_DDC5CLK           0x49
+#define SMU7_I2C_DDC6DATA          0x4a
+#define SMU7_I2C_DDC6CLK           0x4b
+#define SMU7_I2C_DDCVGADATA        0x4c
+#define SMU7_I2C_DDCVGACLK         0x4d
+
+#define SMU7_UNUSED_GPIO_PIN       0x7F
+uint32_t smu7_get_xclk(struct pp_hwmgr *hwmgr);
+uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,
+               uint32_t clock_insr);
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.c
new file mode 100644 (file)
index 0000000..41b634f
--- /dev/null
@@ -0,0 +1,729 @@
+/*
+ * Copyright 2015 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 "hwmgr.h"
+#include "smumgr.h"
+#include "smu7_hwmgr.h"
+#include "smu7_powertune.h"
+#include "pp_debug.h"
+#include "smu7_common.h"
+
+#define VOLTAGE_SCALE  4
+
+static uint32_t DIDTBlock_Info = SQ_IR_MASK | TCP_IR_MASK | TD_PCC_MASK;
+
+static const struct gpu_pt_config_reg GCCACConfig_Polaris10[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ *      Offset                             Mask                                                Shift                                               Value       Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00060013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00860013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01060013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01860013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02060013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02860013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03060013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03860013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x04060013, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x000E0013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x008E0013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x010E0013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x018E0013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x020E0013, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00100013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00900013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01100013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01900013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02100013, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02900013, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   0xFFFFFFFF  }
+};
+
+static const struct gpu_pt_config_reg GCCACConfig_Polaris11[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ *      Offset                             Mask                                                Shift                                               Value       Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00060011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00860011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01060011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01860011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02060011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02860011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03060011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x03860011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x04060011, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x000E0011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x008E0011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x010E0011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x018E0011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x020E0011, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00100011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x00900011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01100011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x01900011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02100011, GPU_CONFIGREG_GC_CAC_IND },
+       {   ixGC_CAC_CNTL,                     0xFFFFFFFF,                                         0,                                                  0x02900011, GPU_CONFIGREG_GC_CAC_IND },
+
+       {   0xFFFFFFFF  }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris10[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ *      Offset                             Mask                                                Shift                                               Value       Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT,                  0x0073,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT,                  0x00ab,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0084,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT,                  0x005a,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0067,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT,                  0x0084,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0027,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0046,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT,                 0x00aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MIN_POWER_MASK,                      DIDT_SQ_CTRL1__MIN_POWER__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MAX_POWER_MASK,                      DIDT_SQ_CTRL1__MAX_POWER__SHIFT,                    0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__UNUSED_0_MASK,                    DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3853,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_0_MASK,                       DIDT_SQ_CTRL2__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x005a,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_1_MASK,                       DIDT_SQ_CTRL2__UNUSED_1__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_2_MASK,                       DIDT_SQ_CTRL2__UNUSED_2__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__UNUSED_0_MASK,                  DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x3853,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x3153,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__PHASE_OFFSET_MASK,                   DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__UNUSED_0_MASK,                       DIDT_SQ_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT,                  0x000a,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT,                  0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0017,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT,                  0x002f,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0046,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT,                  0x005d,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MIN_POWER_MASK,                      DIDT_TD_CTRL1__MIN_POWER__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MAX_POWER_MASK,                      DIDT_TD_CTRL1__MAX_POWER__SHIFT,                    0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__UNUSED_0_MASK,                    DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0x00ff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3fff,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_0_MASK,                       DIDT_TD_CTRL2__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x000f,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_1_MASK,                       DIDT_TD_CTRL2__UNUSED_1__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_2_MASK,                       DIDT_TD_CTRL2__UNUSED_2__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__UNUSED_0_MASK,                  DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x0dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x0dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0009,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0009,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__UNUSED_0_MASK,                       DIDT_TD_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT,                 0x0004,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT,                 0x0037,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT,                 0x00ff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT,                 0x0054,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MIN_POWER_MASK,                     DIDT_TCP_CTRL1__MIN_POWER__SHIFT,                   0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MAX_POWER_MASK,                     DIDT_TCP_CTRL1__MAX_POWER__SHIFT,                   0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__UNUSED_0_MASK,                   DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK,              DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT,            0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK,               DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT,             0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_0_MASK,                      DIDT_TCP_CTRL2__UNUSED_0__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,      DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,    0x0032,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_1_MASK,                      DIDT_TCP_CTRL2__UNUSED_1__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,      DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_2_MASK,                      DIDT_TCP_CTRL2__UNUSED_2__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,   DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,  DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__UNUSED_0_MASK,                 DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,      DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,    0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,    0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK,                DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT,              0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__UNUSED_0_MASK,                       DIDT_TCP_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   0xFFFFFFFF  }
+};
+
+static const struct gpu_pt_config_reg DIDTConfig_Polaris11[] = {
+/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ *      Offset                             Mask                                                Shift                                               Value       Type
+ * ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ */
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT0__SHIFT,                  0x0073,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT1__SHIFT,                  0x00ab,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0084,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT0_3,               DIDT_SQ_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_SQ_WEIGHT0_3__WEIGHT3__SHIFT,                  0x005a,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0067,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT5__SHIFT,                  0x0084,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0027,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT4_7,               DIDT_SQ_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_SQ_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0046,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT8_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT8__SHIFT,                 0x00aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT9_MASK,                   DIDT_SQ_WEIGHT8_11__WEIGHT9__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT10_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT10__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_WEIGHT8_11,              DIDT_SQ_WEIGHT8_11__WEIGHT11_MASK,                  DIDT_SQ_WEIGHT8_11__WEIGHT11__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MIN_POWER_MASK,                      DIDT_SQ_CTRL1__MIN_POWER__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL1,                   DIDT_SQ_CTRL1__MAX_POWER_MASK,                      DIDT_SQ_CTRL1__MAX_POWER__SHIFT,                    0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__UNUSED_0_MASK,                    DIDT_SQ_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL_OCP,                DIDT_SQ_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_SQ_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_SQ_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3853,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_0_MASK,                       DIDT_SQ_CTRL2__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_SQ_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x005a,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_1_MASK,                       DIDT_SQ_CTRL2__UNUSED_1__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_SQ_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL2,                   DIDT_SQ_CTRL2__UNUSED_2_MASK,                       DIDT_SQ_CTRL2__UNUSED_2__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_SQ_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_SQ_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_SQ_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x0ebb,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_STALL_CTRL,              DIDT_SQ_STALL_CTRL__UNUSED_0_MASK,                  DIDT_SQ_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_SQ_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x3853,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_SQ_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x3153,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_TUNING_CTRL,             DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_SQ_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__PHASE_OFFSET_MASK,                   DIDT_SQ_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_SQ_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_SQ_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_SQ_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_SQ_CTRL0,                   DIDT_SQ_CTRL0__UNUSED_0_MASK,                       DIDT_SQ_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT0_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT0__SHIFT,                  0x000a,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT1_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT1__SHIFT,                  0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT2_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT2__SHIFT,                  0x0017,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT0_3,               DIDT_TD_WEIGHT0_3__WEIGHT3_MASK,                    DIDT_TD_WEIGHT0_3__WEIGHT3__SHIFT,                  0x002f,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT4_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT4__SHIFT,                  0x0046,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT5_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT5__SHIFT,                  0x005d,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT6_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT6__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_WEIGHT4_7,               DIDT_TD_WEIGHT4_7__WEIGHT7_MASK,                    DIDT_TD_WEIGHT4_7__WEIGHT7__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MIN_POWER_MASK,                      DIDT_TD_CTRL1__MIN_POWER__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL1,                   DIDT_TD_CTRL1__MAX_POWER_MASK,                      DIDT_TD_CTRL1__MAX_POWER__SHIFT,                    0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__UNUSED_0_MASK,                    DIDT_TD_CTRL_OCP__UNUSED_0__SHIFT,                  0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL_OCP,                DIDT_TD_CTRL_OCP__OCP_MAX_POWER_MASK,               DIDT_TD_CTRL_OCP__OCP_MAX_POWER__SHIFT,             0x00ff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__MAX_POWER_DELTA_MASK,                DIDT_TD_CTRL2__MAX_POWER_DELTA__SHIFT,              0x3fff,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_0_MASK,                       DIDT_TD_CTRL2__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,       DIDT_TD_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,     0x000f,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_1_MASK,                       DIDT_TD_CTRL2__UNUSED_1__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,       DIDT_TD_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL2,                   DIDT_TD_CTRL2__UNUSED_2_MASK,                       DIDT_TD_CTRL2__UNUSED_2__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,    DIDT_TD_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT,  0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,       DIDT_TD_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,     0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,   DIDT_TD_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_STALL_CTRL,              DIDT_TD_STALL_CTRL__UNUSED_0_MASK,                  DIDT_TD_STALL_CTRL__UNUSED_0__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,       DIDT_TD_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,     0x0dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,       DIDT_TD_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,     0x0dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_TUNING_CTRL,             DIDT_TD_TUNING_CTRL__UNUSED_0_MASK,                 DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TD_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TD_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TD_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TD_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0008,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TD_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0008,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TD_CTRL0,                   DIDT_TD_CTRL0__UNUSED_0_MASK,                       DIDT_TD_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT0_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT0__SHIFT,                 0x0004,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT1_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT1__SHIFT,                 0x0037,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT2_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT2__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT0_3,              DIDT_TCP_WEIGHT0_3__WEIGHT3_MASK,                   DIDT_TCP_WEIGHT0_3__WEIGHT3__SHIFT,                 0x00ff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT4_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT4__SHIFT,                 0x0054,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT5_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT5__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT6_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT6__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_WEIGHT4_7,              DIDT_TCP_WEIGHT4_7__WEIGHT7_MASK,                   DIDT_TCP_WEIGHT4_7__WEIGHT7__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MIN_POWER_MASK,                     DIDT_TCP_CTRL1__MIN_POWER__SHIFT,                   0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL1,                  DIDT_TCP_CTRL1__MAX_POWER_MASK,                     DIDT_TCP_CTRL1__MAX_POWER__SHIFT,                   0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__UNUSED_0_MASK,                   DIDT_TCP_CTRL_OCP__UNUSED_0__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL_OCP,               DIDT_TCP_CTRL_OCP__OCP_MAX_POWER_MASK,              DIDT_TCP_CTRL_OCP__OCP_MAX_POWER__SHIFT,            0xffff,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__MAX_POWER_DELTA_MASK,               DIDT_TCP_CTRL2__MAX_POWER_DELTA__SHIFT,             0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_0_MASK,                      DIDT_TCP_CTRL2__UNUSED_0__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE_MASK,      DIDT_TCP_CTRL2__SHORT_TERM_INTERVAL_SIZE__SHIFT,    0x0032,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_1_MASK,                      DIDT_TCP_CTRL2__UNUSED_1__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO_MASK,      DIDT_TCP_CTRL2__LONG_TERM_INTERVAL_RATIO__SHIFT,    0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL2,                  DIDT_TCP_CTRL2__UNUSED_2_MASK,                      DIDT_TCP_CTRL2__UNUSED_2__SHIFT,                    0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE_MASK,   DIDT_TCP_STALL_CTRL__DIDT_STALL_CTRL_ENABLE__SHIFT, 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_HI__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO_MASK,      DIDT_TCP_STALL_CTRL__DIDT_STALL_DELAY_LO__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD_MASK,  DIDT_TCP_STALL_CTRL__DIDT_HI_POWER_THRESHOLD__SHIFT, 0x01aa,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_STALL_CTRL,             DIDT_TCP_STALL_CTRL__UNUSED_0_MASK,                 DIDT_TCP_STALL_CTRL__UNUSED_0__SHIFT,               0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE_MASK,      DIDT_TCP_TUNING_CTRL__DIDT_TUNING_ENABLE__SHIFT,    0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_HI__SHIFT,    0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO_MASK,      DIDT_TCP_TUNING_CTRL__MAX_POWER_DELTA_LO__SHIFT,    0x3dde,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_TUNING_CTRL,            DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK,                DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT,              0x0000,     GPU_CONFIGREG_DIDT_IND },
+
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK,                   DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT,                 0x0001,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__USE_REF_CLOCK_MASK,                  DIDT_TCP_CTRL0__USE_REF_CLOCK__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__PHASE_OFFSET_MASK,                   DIDT_TCP_CTRL0__PHASE_OFFSET__SHIFT,                 0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CTRL_RST_MASK,                  DIDT_TCP_CTRL0__DIDT_CTRL_RST__SHIFT,                0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE_MASK,           DIDT_TCP_CTRL0__DIDT_CLK_EN_OVERRIDE__SHIFT,         0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_HI__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO_MASK,     DIDT_TCP_CTRL0__DIDT_MAX_STALLS_ALLOWED_LO__SHIFT,   0x0010,     GPU_CONFIGREG_DIDT_IND },
+       {   ixDIDT_TCP_CTRL0,                   DIDT_TCP_CTRL0__UNUSED_0_MASK,                       DIDT_TCP_CTRL0__UNUSED_0__SHIFT,                     0x0000,     GPU_CONFIGREG_DIDT_IND },
+       {   0xFFFFFFFF  }
+};
+
+
+static int smu7_enable_didt(struct pp_hwmgr *hwmgr, const bool enable)
+{
+
+       uint32_t en = enable ? 1 : 0;
+       int32_t result = 0;
+       uint32_t data;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping)) {
+               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0);
+               data &= ~DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK;
+               data |= ((en << DIDT_SQ_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_SQ_CTRL0__DIDT_CTRL_EN_MASK);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_SQ_CTRL0, data);
+               DIDTBlock_Info &= ~SQ_Enable_MASK;
+               DIDTBlock_Info |= en << SQ_Enable_SHIFT;
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping)) {
+               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0);
+               data &= ~DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK;
+               data |= ((en << DIDT_DB_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_DB_CTRL0__DIDT_CTRL_EN_MASK);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_DB_CTRL0, data);
+               DIDTBlock_Info &= ~DB_Enable_MASK;
+               DIDTBlock_Info |= en << DB_Enable_SHIFT;
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping)) {
+               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0);
+               data &= ~DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK;
+               data |= ((en << DIDT_TD_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TD_CTRL0__DIDT_CTRL_EN_MASK);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TD_CTRL0, data);
+               DIDTBlock_Info &= ~TD_Enable_MASK;
+               DIDTBlock_Info |= en << TD_Enable_SHIFT;
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
+               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0);
+               data &= ~DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK;
+               data |= ((en << DIDT_TCP_CTRL0__DIDT_CTRL_EN__SHIFT) & DIDT_TCP_CTRL0__DIDT_CTRL_EN_MASK);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, ixDIDT_TCP_CTRL0, data);
+               DIDTBlock_Info &= ~TCP_Enable_MASK;
+               DIDTBlock_Info |= en << TCP_Enable_SHIFT;
+       }
+
+       if (enable)
+               result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_Didt_Block_Function, DIDTBlock_Info);
+
+       return result;
+}
+
+static int smu7_program_pt_config_registers(struct pp_hwmgr *hwmgr,
+                               const struct gpu_pt_config_reg *cac_config_regs)
+{
+       const struct gpu_pt_config_reg *config_regs = cac_config_regs;
+       uint32_t cache = 0;
+       uint32_t data = 0;
+
+       PP_ASSERT_WITH_CODE((config_regs != NULL), "Invalid config register table.", return -EINVAL);
+
+       while (config_regs->offset != 0xFFFFFFFF) {
+               if (config_regs->type == GPU_CONFIGREG_CACHE)
+                       cache |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+               else {
+                       switch (config_regs->type) {
+                       case GPU_CONFIGREG_SMC_IND:
+                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset);
+                               break;
+
+                       case GPU_CONFIGREG_DIDT_IND:
+                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset);
+                               break;
+
+                       case GPU_CONFIGREG_GC_CAC_IND:
+                               data = cgs_read_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset);
+                               break;
+
+                       default:
+                               data = cgs_read_register(hwmgr->device, config_regs->offset);
+                               break;
+                       }
+
+                       data &= ~config_regs->mask;
+                       data |= ((config_regs->value << config_regs->shift) & config_regs->mask);
+                       data |= cache;
+
+                       switch (config_regs->type) {
+                       case GPU_CONFIGREG_SMC_IND:
+                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, config_regs->offset, data);
+                               break;
+
+                       case GPU_CONFIGREG_DIDT_IND:
+                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__DIDT, config_regs->offset, data);
+                               break;
+
+                       case GPU_CONFIGREG_GC_CAC_IND:
+                               cgs_write_ind_register(hwmgr->device, CGS_IND_REG_GC_CAC, config_regs->offset, data);
+                               break;
+
+                       default:
+                               cgs_write_register(hwmgr->device, config_regs->offset, data);
+                               break;
+                       }
+                       cache = 0;
+               }
+
+               config_regs++;
+       }
+
+       return 0;
+}
+
+int smu7_enable_didt_config(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       uint32_t num_se = 0;
+       uint32_t count, value, value2;
+       struct cgs_system_info sys_info = {0};
+
+       sys_info.size = sizeof(struct cgs_system_info);
+       sys_info.info_id = CGS_SYSTEM_INFO_GFX_SE_INFO;
+       result = cgs_query_system_info(hwmgr->device, &sys_info);
+
+
+       if (result == 0)
+               num_se = sys_info.value;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
+
+               /* TO DO Pre DIDT disable clock gating */
+               value = 0;
+               value2 = cgs_read_register(hwmgr->device, mmGRBM_GFX_INDEX);
+               for (count = 0; count < num_se; count++) {
+                       value = SYS_GRBM_GFX_INDEX_DATA__INSTANCE_BROADCAST_WRITES_MASK
+                               | SYS_GRBM_GFX_INDEX_DATA__SH_BROADCAST_WRITES_MASK
+                               | (count << SYS_GRBM_GFX_INDEX_DATA__SE_INDEX__SHIFT);
+                       cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value);
+
+                       if (hwmgr->chip_id == CHIP_POLARIS10) {
+                               result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris10);
+                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
+                               result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris10);
+                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
+                       } else if (hwmgr->chip_id == CHIP_POLARIS11) {
+                               result = smu7_program_pt_config_registers(hwmgr, GCCACConfig_Polaris11);
+                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
+                               result = smu7_program_pt_config_registers(hwmgr, DIDTConfig_Polaris11);
+                               PP_ASSERT_WITH_CODE((result == 0), "DIDT Config failed.", return result);
+                       }
+               }
+               cgs_write_register(hwmgr->device, mmGRBM_GFX_INDEX, value2);
+
+               result = smu7_enable_didt(hwmgr, true);
+               PP_ASSERT_WITH_CODE((result == 0), "EnableDiDt failed.", return result);
+
+               /* TO DO Post DIDT enable clock gating */
+       }
+
+       return 0;
+}
+
+int smu7_disable_didt_config(struct pp_hwmgr *hwmgr)
+{
+       int result;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SQRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DBRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TDRamping) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_TCPRamping)) {
+               /* TO DO Pre DIDT disable clock gating */
+
+               result = smu7_enable_didt(hwmgr, false);
+               PP_ASSERT_WITH_CODE((result == 0), "Post DIDT enable clock gating failed.", return result);
+               /* TO DO Post DIDT enable clock gating */
+       }
+
+       return 0;
+}
+
+int smu7_enable_smc_cac(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int result = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CAC)) {
+               int smc_result;
+               smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                               (uint16_t)(PPSMC_MSG_EnableCac));
+               PP_ASSERT_WITH_CODE((0 == smc_result),
+                               "Failed to enable CAC in SMC.", result = -1);
+
+               data->cac_enabled = (0 == smc_result) ? true : false;
+       }
+       return result;
+}
+
+int smu7_disable_smc_cac(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int result = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CAC) && data->cac_enabled) {
+               int smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                               (uint16_t)(PPSMC_MSG_DisableCac));
+               PP_ASSERT_WITH_CODE((smc_result == 0),
+                               "Failed to disable CAC in SMC.", result = -1);
+
+               data->cac_enabled = false;
+       }
+       return result;
+}
+
+int smu7_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->power_containment_features &
+                       POWERCONTAINMENT_FEATURE_PkgPwrLimit)
+               return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_PkgPwrSetLimit, n);
+       return 0;
+}
+
+static int smu7_set_overdriver_target_tdp(struct pp_hwmgr *pHwMgr, uint32_t target_tdp)
+{
+       return smum_send_msg_to_smc_with_parameter(pHwMgr->smumgr,
+                       PPSMC_MSG_OverDriveSetTargetTdp, target_tdp);
+}
+
+int smu7_enable_power_containment(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       int smc_result;
+       int result = 0;
+       struct phm_cac_tdp_table *cac_table;
+
+       data->power_containment_features = 0;
+       if (hwmgr->pp_table_version == PP_TABLE_V1)
+               cac_table = table_info->cac_dtp_table;
+       else
+               cac_table = hwmgr->dyn_state.cac_dtp_table;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+
+               if (data->enable_tdc_limit_feature) {
+                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                                       (uint16_t)(PPSMC_MSG_TDCLimitEnable));
+                       PP_ASSERT_WITH_CODE((0 == smc_result),
+                                       "Failed to enable TDCLimit in SMC.", result = -1;);
+                       if (0 == smc_result)
+                               data->power_containment_features |=
+                                               POWERCONTAINMENT_FEATURE_TDCLimit;
+               }
+
+               if (data->enable_pkg_pwr_tracking_feature) {
+                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitEnable));
+                       PP_ASSERT_WITH_CODE((0 == smc_result),
+                                       "Failed to enable PkgPwrTracking in SMC.", result = -1;);
+                       if (0 == smc_result) {
+                               uint32_t default_limit =
+                                       (uint32_t)(cac_table->usMaximumPowerDeliveryLimit * 256);
+
+                               data->power_containment_features |=
+                                               POWERCONTAINMENT_FEATURE_PkgPwrLimit;
+
+                               if (smu7_set_power_limit(hwmgr, default_limit))
+                                       printk(KERN_ERR "Failed to set Default Power Limit in SMC!");
+                       }
+               }
+       }
+       return result;
+}
+
+int smu7_disable_power_containment(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int result = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment) &&
+                       data->power_containment_features) {
+               int smc_result;
+
+               if (data->power_containment_features &
+                               POWERCONTAINMENT_FEATURE_TDCLimit) {
+                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                                       (uint16_t)(PPSMC_MSG_TDCLimitDisable));
+                       PP_ASSERT_WITH_CODE((smc_result == 0),
+                                       "Failed to disable TDCLimit in SMC.",
+                                       result = smc_result);
+               }
+
+               if (data->power_containment_features &
+                               POWERCONTAINMENT_FEATURE_DTE) {
+                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                                       (uint16_t)(PPSMC_MSG_DisableDTE));
+                       PP_ASSERT_WITH_CODE((smc_result == 0),
+                                       "Failed to disable DTE in SMC.",
+                                       result = smc_result);
+               }
+
+               if (data->power_containment_features &
+                               POWERCONTAINMENT_FEATURE_PkgPwrLimit) {
+                       smc_result = smum_send_msg_to_smc(hwmgr->smumgr,
+                                       (uint16_t)(PPSMC_MSG_PkgPwrLimitDisable));
+                       PP_ASSERT_WITH_CODE((smc_result == 0),
+                                       "Failed to disable PkgPwrTracking in SMC.",
+                                       result = smc_result);
+               }
+               data->power_containment_features = 0;
+       }
+
+       return result;
+}
+
+int smu7_power_control_set_level(struct pp_hwmgr *hwmgr)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_cac_tdp_table *cac_table;
+
+       int adjust_percent, target_tdp;
+       int result = 0;
+
+       if (hwmgr->pp_table_version == PP_TABLE_V1)
+               cac_table = table_info->cac_dtp_table;
+       else
+               cac_table = hwmgr->dyn_state.cac_dtp_table;
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+               /* adjustment percentage has already been validated */
+               adjust_percent = hwmgr->platform_descriptor.TDPAdjustmentPolarity ?
+                               hwmgr->platform_descriptor.TDPAdjustment :
+                               (-1 * hwmgr->platform_descriptor.TDPAdjustment);
+               /* SMC requested that target_tdp to be 7 bit fraction in DPM table
+                * but message to be 8 bit fraction for messages
+                */
+               target_tdp = ((100 + adjust_percent) * (int)(cac_table->usTDP * 256)) / 100;
+               result = smu7_set_overdriver_target_tdp(hwmgr, (uint32_t)target_tdp);
+       }
+
+       return result;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_powertune.h
new file mode 100644 (file)
index 0000000..22f86b6
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015 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 _SMU7_POWERTUNE_H
+#define _SMU7_POWERTUNE_H
+
+#define DIDT_SQ_CTRL0__UNUSED_0_MASK    0xfffc0000
+#define DIDT_SQ_CTRL0__UNUSED_0__SHIFT  0x12
+#define DIDT_TD_CTRL0__UNUSED_0_MASK    0xfffc0000
+#define DIDT_TD_CTRL0__UNUSED_0__SHIFT  0x12
+#define DIDT_TCP_CTRL0__UNUSED_0_MASK   0xfffc0000
+#define DIDT_TCP_CTRL0__UNUSED_0__SHIFT 0x12
+#define DIDT_SQ_TUNING_CTRL__UNUSED_0_MASK                 0xc0000000
+#define DIDT_SQ_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001e
+#define DIDT_TD_TUNING_CTRL__UNUSED_0_MASK                 0xc0000000
+#define DIDT_TD_TUNING_CTRL__UNUSED_0__SHIFT               0x0000001e
+#define DIDT_TCP_TUNING_CTRL__UNUSED_0_MASK                0xc0000000
+#define DIDT_TCP_TUNING_CTRL__UNUSED_0__SHIFT              0x0000001e
+
+/* PowerContainment Features */
+#define POWERCONTAINMENT_FEATURE_DTE             0x00000001
+#define POWERCONTAINMENT_FEATURE_TDCLimit        0x00000002
+#define POWERCONTAINMENT_FEATURE_PkgPwrLimit     0x00000004
+
+#define ixGC_CAC_CNTL 0x0000
+#define ixDIDT_SQ_STALL_CTRL 0x0004
+#define ixDIDT_SQ_TUNING_CTRL 0x0005
+#define ixDIDT_TD_STALL_CTRL 0x0044
+#define ixDIDT_TD_TUNING_CTRL 0x0045
+#define ixDIDT_TCP_STALL_CTRL 0x0064
+#define ixDIDT_TCP_TUNING_CTRL 0x0065
+
+
+int smu7_enable_smc_cac(struct pp_hwmgr *hwmgr);
+int smu7_disable_smc_cac(struct pp_hwmgr *hwmgr);
+int smu7_enable_power_containment(struct pp_hwmgr *hwmgr);
+int smu7_disable_power_containment(struct pp_hwmgr *hwmgr);
+int smu7_set_power_limit(struct pp_hwmgr *hwmgr, uint32_t n);
+int smu7_power_control_set_level(struct pp_hwmgr *hwmgr);
+int smu7_enable_didt_config(struct pp_hwmgr *hwmgr);
+int smu7_disable_didt_config(struct pp_hwmgr *hwmgr);
+#endif  /* DGPU_POWERTUNE_H */
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.c
new file mode 100644 (file)
index 0000000..fb6c6f6
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2016 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 <asm/div64.h>
+#include "smu7_thermal.h"
+#include "smu7_hwmgr.h"
+#include "smu7_common.h"
+
+int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+               struct phm_fan_speed_info *fan_speed_info)
+{
+       if (hwmgr->thermal_controller.fanInfo.bNoFan)
+               return 0;
+
+       fan_speed_info->supports_percent_read = true;
+       fan_speed_info->supports_percent_write = true;
+       fan_speed_info->min_percent = 0;
+       fan_speed_info->max_percent = 100;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_FanSpeedInTableIsRPM) &&
+               hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
+               fan_speed_info->supports_rpm_read = true;
+               fan_speed_info->supports_rpm_write = true;
+               fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM;
+               fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
+       } else {
+               fan_speed_info->min_rpm = 0;
+               fan_speed_info->max_rpm = 0;
+       }
+
+       return 0;
+}
+
+int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr,
+               uint32_t *speed)
+{
+       uint32_t duty100;
+       uint32_t duty;
+       uint64_t tmp64;
+
+       if (hwmgr->thermal_controller.fanInfo.bNoFan)
+               return 0;
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL1, FMAX_DUTY100);
+       duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_STATUS, FDO_PWM_DUTY);
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+
+       tmp64 = (uint64_t)duty * 100;
+       do_div(tmp64, duty100);
+       *speed = (uint32_t)tmp64;
+
+       if (*speed > 100)
+               *speed = 100;
+
+       return 0;
+}
+
+int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
+{
+       uint32_t tach_period;
+       uint32_t crystal_clock_freq;
+
+       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
+                       (hwmgr->thermal_controller.fanInfo.
+                               ucTachometerPulsesPerRevolution == 0))
+               return 0;
+
+       tach_period = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_TACH_STATUS, TACH_PERIOD);
+
+       if (tach_period == 0)
+               return -EINVAL;
+
+       crystal_clock_freq = smu7_get_xclk(hwmgr);
+
+       *speed = 60 * crystal_clock_freq * 10000 / tach_period;
+
+       return 0;
+}
+
+/**
+* Set Fan Speed Control to static mode, so that the user can decide what speed to use.
+* @param    hwmgr  the address of the powerplay hardware manager.
+*           mode    the fan control mode, 0 default, 1 by percent, 5, by RPM
+* @exception Should always succeed.
+*/
+int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
+{
+
+       if (hwmgr->fan_ctrl_is_in_default_mode) {
+               hwmgr->fan_ctrl_default_mode =
+                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,     CGS_IND_REG__SMC,
+                                               CG_FDO_CTRL2, FDO_PWM_MODE);
+               hwmgr->tmin =
+                               PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                                               CG_FDO_CTRL2, TMIN);
+               hwmgr->fan_ctrl_is_in_default_mode = false;
+       }
+
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL2, TMIN, 0);
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL2, FDO_PWM_MODE, mode);
+
+       return 0;
+}
+
+/**
+* Reset Fan Speed Control to default mode.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @exception Should always succeed.
+*/
+int smu7_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
+{
+       if (!hwmgr->fan_ctrl_is_in_default_mode) {
+               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode);
+               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               CG_FDO_CTRL2, TMIN, hwmgr->tmin);
+               hwmgr->fan_ctrl_is_in_default_mode = true;
+       }
+
+       return 0;
+}
+
+static int smu7_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+       int result;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ODFuzzyFanControlSupport)) {
+               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY);
+               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
+
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_FanSpeedInTableIsRPM))
+                       hwmgr->hwmgr_func->set_max_fan_rpm_output(hwmgr,
+                                       hwmgr->thermal_controller.
+                                       advanceFanControlParameters.usMaxFanRPM);
+               else
+                       hwmgr->hwmgr_func->set_max_fan_pwm_output(hwmgr,
+                                       hwmgr->thermal_controller.
+                                       advanceFanControlParameters.usMaxFanPWM);
+
+       } else {
+               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE);
+               result = smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl);
+       }
+
+       if (!result && hwmgr->thermal_controller.
+                       advanceFanControlParameters.ucTargetTemperature)
+               result = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SetFanTemperatureTarget,
+                               hwmgr->thermal_controller.
+                               advanceFanControlParameters.ucTargetTemperature);
+
+       return result;
+}
+
+
+int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
+{
+       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl);
+}
+
+/**
+* Set Fan Speed in percent.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    speed is the percentage value (0% - 100%) to be set.
+* @exception Fails is the 100% setting appears to be 0.
+*/
+int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr,
+               uint32_t speed)
+{
+       uint32_t duty100;
+       uint32_t duty;
+       uint64_t tmp64;
+
+       if (hwmgr->thermal_controller.fanInfo.bNoFan)
+               return 0;
+
+       if (speed > 100)
+               speed = 100;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MicrocodeFanControl))
+               smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL1, FMAX_DUTY100);
+
+       if (duty100 == 0)
+               return -EINVAL;
+
+       tmp64 = (uint64_t)speed * duty100;
+       do_div(tmp64, 100);
+       duty = (uint32_t)tmp64;
+
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL0, FDO_STATIC_DUTY, duty);
+
+       return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+}
+
+/**
+* Reset Fan Speed to default.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @exception Always succeeds.
+*/
+int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
+{
+       int result;
+
+       if (hwmgr->thermal_controller.fanInfo.bNoFan)
+               return 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MicrocodeFanControl)) {
+               result = smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+               if (!result)
+                       result = smu7_fan_ctrl_start_smc_fan_control(hwmgr);
+       } else
+               result = smu7_fan_ctrl_set_default_mode(hwmgr);
+
+       return result;
+}
+
+/**
+* Set Fan Speed in RPM.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    speed is the percentage value (min - max) to be set.
+* @exception Fails is the speed not lie between min and max.
+*/
+int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
+{
+       uint32_t tach_period;
+       uint32_t crystal_clock_freq;
+
+       if (hwmgr->thermal_controller.fanInfo.bNoFan ||
+                       (hwmgr->thermal_controller.fanInfo.
+                       ucTachometerPulsesPerRevolution == 0) ||
+                       (speed < hwmgr->thermal_controller.fanInfo.ulMinRPM) ||
+                       (speed > hwmgr->thermal_controller.fanInfo.ulMaxRPM))
+               return 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MicrocodeFanControl))
+               smu7_fan_ctrl_stop_smc_fan_control(hwmgr);
+
+       crystal_clock_freq = smu7_get_xclk(hwmgr);
+
+       tach_period = 60 * crystal_clock_freq * 10000 / (8 * speed);
+
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               CG_TACH_STATUS, TACH_PERIOD, tach_period);
+
+       return smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+}
+
+/**
+* Reads the remote temperature from the SIslands thermal controller.
+*
+* @param    hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr)
+{
+       int temp;
+
+       temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_MULT_THERMAL_STATUS, CTF_TEMP);
+
+       /* Bit 9 means the reading is lower than the lowest usable value. */
+       if (temp & 0x200)
+               temp = SMU7_THERMAL_MAXIMUM_TEMP_READING;
+       else
+               temp = temp & 0x1ff;
+
+       temp *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+       return temp;
+}
+
+/**
+* Set the requested temperature range for high and low alert signals
+*
+* @param    hwmgr The address of the hardware manager.
+* @param    range Temperature range to be programmed for high and low alert signals
+* @exception PP_Result_BadInput if the input data is not valid.
+*/
+static int smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+               uint32_t low_temp, uint32_t high_temp)
+{
+       uint32_t low = SMU7_THERMAL_MINIMUM_ALERT_TEMP *
+                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+       uint32_t high = SMU7_THERMAL_MAXIMUM_ALERT_TEMP *
+                       PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+       if (low < low_temp)
+               low = low_temp;
+       if (high > high_temp)
+               high = high_temp;
+
+       if (low > high)
+               return -EINVAL;
+
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, DIG_THERM_INTH,
+                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, DIG_THERM_INTL,
+                       (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_CTRL, DIG_THERM_DPM,
+                       (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
+
+       return 0;
+}
+
+/**
+* Programs thermal controller one-time setting registers
+*
+* @param    hwmgr The address of the hardware manager.
+*/
+static int smu7_thermal_initialize(struct pp_hwmgr *hwmgr)
+{
+       if (hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
+               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                               CG_TACH_CTRL, EDGE_PER_REV,
+                               hwmgr->thermal_controller.fanInfo.
+                               ucTachometerPulsesPerRevolution - 1);
+
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
+
+       return 0;
+}
+
+/**
+* Enable thermal alerts on the RV770 thermal controller.
+*
+* @param    hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr)
+{
+       uint32_t alert;
+
+       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, THERM_INT_MASK);
+       alert &= ~(SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, THERM_INT_MASK, alert);
+
+       /* send message to SMU to enable internal thermal interrupts */
+       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable);
+}
+
+/**
+* Disable thermal alerts on the RV770 thermal controller.
+* @param    hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr)
+{
+       uint32_t alert;
+
+       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, THERM_INT_MASK);
+       alert |= (SMU7_THERMAL_HIGH_ALERT_MASK | SMU7_THERMAL_LOW_ALERT_MASK);
+       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_THERMAL_INT, THERM_INT_MASK, alert);
+
+       /* send message to SMU to disable internal thermal interrupts */
+       return smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable);
+}
+
+/**
+* Uninitialize the thermal controller.
+* Currently just disables alerts.
+* @param    hwmgr The address of the hardware manager.
+*/
+int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+       int result = smu7_thermal_disable_alert(hwmgr);
+
+       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
+               smu7_fan_ctrl_set_default_mode(hwmgr);
+
+       return result;
+}
+
+/**
+* Start the fan control on the SMC.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+static int tf_smu7_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+/* If the fantable setup has failed we could have disabled
+ * PHM_PlatformCaps_MicrocodeFanControl even after
+ * this function was included in the table.
+ * Make sure that we still think controlling the fan is OK.
+*/
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MicrocodeFanControl)) {
+               smu7_fan_ctrl_start_smc_fan_control(hwmgr);
+               smu7_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
+       }
+
+       return 0;
+}
+
+/**
+* Set temperature range for high and low alerts
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+static int tf_smu7_thermal_set_temperature_range(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input;
+
+       if (range == NULL)
+               return -EINVAL;
+
+       return smu7_thermal_set_temperature_range(hwmgr, range->min, range->max);
+}
+
+/**
+* Programs one-time setting registers
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from initialize thermal controller routine
+*/
+static int tf_smu7_thermal_initialize(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       return smu7_thermal_initialize(hwmgr);
+}
+
+/**
+* Enable high and low alerts
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from enable alert routine
+*/
+static int tf_smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       return smu7_thermal_enable_alert(hwmgr);
+}
+
+/**
+* Disable high and low alerts
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from disable alert routine
+*/
+static int tf_smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       return smu7_thermal_disable_alert(hwmgr);
+}
+
+static const struct phm_master_table_item
+phm_thermal_start_thermal_controller_master_list[] = {
+       {NULL, tf_smu7_thermal_initialize},
+       {NULL, tf_smu7_thermal_set_temperature_range},
+       {NULL, tf_smu7_thermal_enable_alert},
+       {NULL, smum_thermal_avfs_enable},
+/* We should restrict performance levels to low before we halt the SMC.
+ * On the other hand we are still in boot state when we do this
+ * so it would be pointless.
+ * If this assumption changes we have to revisit this table.
+ */
+       {NULL, smum_thermal_setup_fan_table},
+       {NULL, tf_smu7_thermal_start_smc_fan_control},
+       {NULL, NULL}
+};
+
+static const struct phm_master_table_header
+phm_thermal_start_thermal_controller_master = {
+       0,
+       PHM_MasterTableFlag_None,
+       phm_thermal_start_thermal_controller_master_list
+};
+
+static const struct phm_master_table_item
+phm_thermal_set_temperature_range_master_list[] = {
+       {NULL, tf_smu7_thermal_disable_alert},
+       {NULL, tf_smu7_thermal_set_temperature_range},
+       {NULL, tf_smu7_thermal_enable_alert},
+       {NULL, NULL}
+};
+
+static const struct phm_master_table_header
+phm_thermal_set_temperature_range_master = {
+       0,
+       PHM_MasterTableFlag_None,
+       phm_thermal_set_temperature_range_master_list
+};
+
+int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
+{
+       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
+               smu7_fan_ctrl_set_default_mode(hwmgr);
+       return 0;
+}
+
+/**
+* Initializes the thermal controller related functions in the Hardware Manager structure.
+* @param    hwmgr The address of the hardware manager.
+* @exception Any error code from the low-level communication.
+*/
+int pp_smu7_thermal_initialize(struct pp_hwmgr *hwmgr)
+{
+       int result;
+
+       result = phm_construct_table(hwmgr,
+                       &phm_thermal_set_temperature_range_master,
+                       &(hwmgr->set_temperature_range));
+
+       if (!result) {
+               result = phm_construct_table(hwmgr,
+                               &phm_thermal_start_thermal_controller_master,
+                               &(hwmgr->start_thermal_controller));
+               if (result)
+                       phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range));
+       }
+
+       if (!result)
+               hwmgr->fan_ctrl_is_in_default_mode = true;
+       return result;
+}
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_thermal.h
new file mode 100644 (file)
index 0000000..6face97
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 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 _SMU7_THERMAL_H_
+#define _SMU7_THERMAL_H_
+
+#include "hwmgr.h"
+
+#define SMU7_THERMAL_HIGH_ALERT_MASK         0x1
+#define SMU7_THERMAL_LOW_ALERT_MASK          0x2
+
+#define SMU7_THERMAL_MINIMUM_TEMP_READING    -256
+#define SMU7_THERMAL_MAXIMUM_TEMP_READING    255
+
+#define SMU7_THERMAL_MINIMUM_ALERT_TEMP      0
+#define SMU7_THERMAL_MAXIMUM_ALERT_TEMP      255
+
+#define FDO_PWM_MODE_STATIC  1
+#define FDO_PWM_MODE_STATIC_RPM 5
+
+extern int smu7_thermal_get_temperature(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info);
+extern int smu7_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed);
+extern int smu7_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode);
+extern int smu7_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed);
+extern int smu7_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
+extern int pp_smu7_thermal_initialize(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr);
+extern int smu7_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed);
+extern int smu7_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed);
+extern int smu7_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_enable_alert(struct pp_hwmgr *hwmgr);
+extern int smu7_thermal_disable_alert(struct pp_hwmgr *hwmgr);
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.c
deleted file mode 100644 (file)
index e58d038..0000000
+++ /dev/null
@@ -1,350 +0,0 @@
-/*
- * Copyright 2015 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 "hwmgr.h"
-#include "tonga_clockpowergating.h"
-#include "tonga_ppsmc.h"
-#include "tonga_hwmgr.h"
-
-int tonga_phm_powerdown_uvd(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_uvd_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                                                    PPSMC_MSG_UVDPowerOFF);
-       return 0;
-}
-
-int tonga_phm_powerup_uvd(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_uvd_power_gating(hwmgr)) {
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                 PHM_PlatformCaps_UVDDynamicPowerGating)) {
-                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                               PPSMC_MSG_UVDPowerON, 1);
-               } else {
-                       return smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                                               PPSMC_MSG_UVDPowerON, 0);
-               }
-       }
-
-       return 0;
-}
-
-int tonga_phm_powerdown_vce(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_vce_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                                                 PPSMC_MSG_VCEPowerOFF);
-       return 0;
-}
-
-int tonga_phm_powerup_vce(struct pp_hwmgr *hwmgr)
-{
-       if (phm_cf_want_vce_power_gating(hwmgr))
-               return smum_send_msg_to_smc(hwmgr->smumgr,
-                                                 PPSMC_MSG_VCEPowerON);
-       return 0;
-}
-
-int tonga_phm_set_asic_block_gating(struct pp_hwmgr *hwmgr, enum PHM_AsicBlock block, enum PHM_ClockGateSetting gating)
-{
-       int ret = 0;
-
-       switch (block) {
-       case PHM_AsicBlock_UVD_MVC:
-       case PHM_AsicBlock_UVD:
-       case PHM_AsicBlock_UVD_HD:
-       case PHM_AsicBlock_UVD_SD:
-               if (gating == PHM_ClockGateSetting_StaticOff)
-                       ret = tonga_phm_powerdown_uvd(hwmgr);
-               else
-                       ret = tonga_phm_powerup_uvd(hwmgr);
-               break;
-       case PHM_AsicBlock_GFX:
-       default:
-               break;
-       }
-
-       return ret;
-}
-
-int tonga_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-
-       tonga_phm_powerup_uvd(hwmgr);
-       tonga_phm_powerup_vce(hwmgr);
-
-       return 0;
-}
-
-int tonga_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       if (data->uvd_power_gated == bgate)
-               return 0;
-
-       data->uvd_power_gated = bgate;
-
-       if (bgate) {
-               cgs_set_clockgating_state(hwmgr->device,
-                                               AMD_IP_BLOCK_TYPE_UVD,
-                                               AMD_CG_STATE_UNGATE);
-               cgs_set_powergating_state(hwmgr->device,
-                                               AMD_IP_BLOCK_TYPE_UVD,
-                                               AMD_PG_STATE_GATE);
-               tonga_update_uvd_dpm(hwmgr, true);
-               tonga_phm_powerdown_uvd(hwmgr);
-       } else {
-               tonga_phm_powerup_uvd(hwmgr);
-               cgs_set_powergating_state(hwmgr->device,
-                                               AMD_IP_BLOCK_TYPE_UVD,
-                                               AMD_PG_STATE_UNGATE);
-               cgs_set_clockgating_state(hwmgr->device,
-                                               AMD_IP_BLOCK_TYPE_UVD,
-                                               AMD_PG_STATE_GATE);
-
-               tonga_update_uvd_dpm(hwmgr, false);
-       }
-
-       return 0;
-}
-
-int tonga_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct phm_set_power_state_input states;
-       const struct pp_power_state  *pcurrent;
-       struct pp_power_state  *requested;
-
-       pcurrent = hwmgr->current_ps;
-       requested = hwmgr->request_ps;
-
-       states.pcurrent_state = &(pcurrent->hardware);
-       states.pnew_state = &(requested->hardware);
-
-       if (phm_cf_want_vce_power_gating(hwmgr)) {
-               if (data->vce_power_gated != bgate) {
-                       if (bgate) {
-                               cgs_set_clockgating_state(
-                                                       hwmgr->device,
-                                                       AMD_IP_BLOCK_TYPE_VCE,
-                                                       AMD_CG_STATE_UNGATE);
-                               cgs_set_powergating_state(
-                                                       hwmgr->device,
-                                                       AMD_IP_BLOCK_TYPE_VCE,
-                                                       AMD_PG_STATE_GATE);
-                               tonga_enable_disable_vce_dpm(hwmgr, false);
-                               data->vce_power_gated = true;
-                       } else {
-                               tonga_phm_powerup_vce(hwmgr);
-                               data->vce_power_gated = false;
-                               cgs_set_powergating_state(
-                                                       hwmgr->device,
-                                                       AMD_IP_BLOCK_TYPE_VCE,
-                                                       AMD_PG_STATE_UNGATE);
-                               cgs_set_clockgating_state(
-                                                       hwmgr->device,
-                                                       AMD_IP_BLOCK_TYPE_VCE,
-                                                       AMD_PG_STATE_GATE);
-
-                               tonga_update_vce_dpm(hwmgr, &states);
-                               tonga_enable_disable_vce_dpm(hwmgr, true);
-                               return 0;
-                       }
-               }
-       } else {
-               tonga_update_vce_dpm(hwmgr, &states);
-               tonga_enable_disable_vce_dpm(hwmgr, true);
-               return 0;
-       }
-
-       if (!data->vce_power_gated)
-               tonga_update_vce_dpm(hwmgr, &states);
-
-       return 0;
-}
-
-int tonga_phm_update_clock_gatings(struct pp_hwmgr *hwmgr,
-                                       const uint32_t *msg_id)
-{
-       PPSMC_Msg msg;
-       uint32_t value;
-
-       switch ((*msg_id & PP_GROUP_MASK) >> PP_GROUP_SHIFT) {
-       case PP_GROUP_GFX:
-               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
-               case PP_BLOCK_GFX_CG:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                                 ? PPSMC_MSG_EnableClockGatingFeature
-                                 : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_CGCG_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                                       ? PPSMC_MSG_EnableClockGatingFeature
-                                       : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_GFX_CGLS_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_GFX_MG:
-                       /* For GFX MGCG, there are three different ones;
-                        * CPF, RLC, and all others.  CPF MGCG will not be used for Tonga.
-                        * For GFX MGLS, Tonga will not support it.
-                        * */
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = (CG_RLC_MGCG_MASK | CG_GFX_OTHERS_MGCG_MASK);
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               default:
-                       return -1;
-               }
-               break;
-
-       case PP_GROUP_SYS:
-               switch ((*msg_id & PP_BLOCK_MASK) >> PP_BLOCK_SHIFT) {
-               case PP_BLOCK_SYS_BIF:
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_BIF_MGLS_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_MC:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_MC_MGCG_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_MC_MGLS_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_HDP:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                                       ? PPSMC_MSG_EnableClockGatingFeature
-                                       : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_HDP_MGCG_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                                       ? PPSMC_MSG_EnableClockGatingFeature
-                                       : PPSMC_MSG_DisableClockGatingFeature;
-
-                               value = CG_SYS_HDP_MGLS_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_SDMA:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_SDMA_MGCG_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-
-                       if (PP_STATE_SUPPORT_LS & *msg_id) {
-                               msg = (*msg_id & PP_STATE_MASK) & PP_STATE_LS
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-
-                               value = CG_SYS_SDMA_MGLS_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               case PP_BLOCK_SYS_ROM:
-                       if (PP_STATE_SUPPORT_CG & *msg_id) {
-                               msg = ((*msg_id & PP_STATE_MASK) & PP_STATE_CG)
-                               ? PPSMC_MSG_EnableClockGatingFeature
-                               : PPSMC_MSG_DisableClockGatingFeature;
-                               value = CG_SYS_ROM_MASK;
-
-                               if (0 != smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, msg, value))
-                                       return -1;
-                       }
-                       break;
-
-               default:
-                       return -1;
-
-               }
-               break;
-
-       default:
-               return -1;
-
-       }
-
-       return 0;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_clockpowergating.h
deleted file mode 100644 (file)
index 8bc38cb..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2015 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 _TONGA_CLOCK_POWER_GATING_H_
-#define _TONGA_CLOCK_POWER_GATING_H_
-
-#include "tonga_hwmgr.h"
-#include "pp_asicblocks.h"
-
-extern int tonga_phm_set_asic_block_gating(struct pp_hwmgr *hwmgr, enum PHM_AsicBlock block, enum PHM_ClockGateSetting gating);
-extern int tonga_phm_powergate_vce(struct pp_hwmgr *hwmgr, bool bgate);
-extern int tonga_phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool bgate);
-extern int tonga_phm_powerdown_uvd(struct pp_hwmgr *hwmgr);
-extern int tonga_phm_disable_clock_power_gating(struct pp_hwmgr *hwmgr);
-extern int tonga_phm_update_clock_gatings(struct pp_hwmgr *hwmgr, const uint32_t *msg_id);
-#endif /* _TONGA_CLOCK_POWER_GATING_H_ */
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_dyn_defaults.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_dyn_defaults.h
deleted file mode 100644 (file)
index 080d69d..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_DYN_DEFAULTS_H
-#define TONGA_DYN_DEFAULTS_H
-
-
-/** \file
- * Volcanic Islands Dynamic default parameters.
- */
-
-enum TONGAdpm_TrendDetection {
-       TONGAdpm_TrendDetection_AUTO,
-       TONGAdpm_TrendDetection_UP,
-       TONGAdpm_TrendDetection_DOWN
-};
-typedef enum TONGAdpm_TrendDetection TONGAdpm_TrendDetection;
-
-/* Bit vector representing same fields as hardware register. */
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT0              0x3FFFC102  /* CP_Gfx_busy */
-/* HDP_busy */
-/* IH_busy */
-/* DRM_busy */
-/* DRMDMA_busy */
-/* UVD_busy */
-/* VCE_busy */
-/* ACP_busy */
-/* SAMU_busy */
-/* AVP_busy  */
-/* SDMA enabled */
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT1              0x000400  /* FE_Gfx_busy  - Intended for primary usage.   Rest are for flexibility. */
-/* SH_Gfx_busy */
-/* RB_Gfx_busy */
-/* VCE_busy */
-
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT2              0xC00080  /* SH_Gfx_busy - Intended for primary usage.   Rest are for flexibility. */
-/* FE_Gfx_busy */
-/* RB_Gfx_busy */
-/* ACP_busy */
-
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT3              0xC00200  /* RB_Gfx_busy - Intended for primary usage.   Rest are for flexibility. */
-/* FE_Gfx_busy */
-/* SH_Gfx_busy */
-/* UVD_busy */
-
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT4              0xC01680  /* UVD_busy */
-/* VCE_busy */
-/* ACP_busy */
-/* SAMU_busy */
-
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT5              0xC00033  /* GFX, HDP, DRMDMA */
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT6              0xC00033  /* GFX, HDP, DRMDMA */
-#define PPTONGA_VOTINGRIGHTSCLIENTS_DFLT7              0x3FFFC000  /* GFX, HDP, DRMDMA */
-
-
-/* thermal protection counter (units).*/
-#define PPTONGA_THERMALPROTECTCOUNTER_DFLT            0x200 /* ~19us */
-
-/* static screen threshold unit */
-#define PPTONGA_STATICSCREENTHRESHOLDUNIT_DFLT        0
-
-/* static screen threshold */
-#define PPTONGA_STATICSCREENTHRESHOLD_DFLT            0x00C8
-
-/* gfx idle clock stop threshold */
-#define PPTONGA_GFXIDLECLOCKSTOPTHRESHOLD_DFLT        0x200 /* ~19us with static screen threshold unit of 0 */
-
-/* Fixed reference divider to use when building baby stepping tables. */
-#define PPTONGA_REFERENCEDIVIDER_DFLT                  4
-
-/*
- * ULV voltage change delay time
- * Used to be delay_vreg in N.I. split for S.I.
- * Using N.I. delay_vreg value as default
- * ReferenceClock = 2700
- * VoltageResponseTime = 1000
- * VDDCDelayTime = (VoltageResponseTime * ReferenceClock) / 1600 = 1687
- */
-
-#define PPTONGA_ULVVOLTAGECHANGEDELAY_DFLT             1687
-
-#define PPTONGA_CGULVPARAMETER_DFLT                    0x00040035
-#define PPTONGA_CGULVCONTROL_DFLT                      0x00007450
-#define PPTONGA_TARGETACTIVITY_DFLT                     30  /*30%  */
-#define PPTONGA_MCLK_TARGETACTIVITY_DFLT                10  /*10%  */
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.c
deleted file mode 100644 (file)
index c7dc111..0000000
+++ /dev/null
@@ -1,6276 +0,0 @@
-/*
- * Copyright 2015 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/module.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-#include "linux/delay.h"
-#include "pp_acpi.h"
-#include "hwmgr.h"
-#include <atombios.h>
-#include "tonga_hwmgr.h"
-#include "pptable.h"
-#include "processpptables.h"
-#include "tonga_processpptables.h"
-#include "tonga_pptable.h"
-#include "pp_debug.h"
-#include "tonga_ppsmc.h"
-#include "cgs_common.h"
-#include "pppcielanes.h"
-#include "tonga_dyn_defaults.h"
-#include "smumgr.h"
-#include "tonga_smumgr.h"
-#include "tonga_clockpowergating.h"
-#include "tonga_thermal.h"
-
-#include "smu/smu_7_1_2_d.h"
-#include "smu/smu_7_1_2_sh_mask.h"
-
-#include "gmc/gmc_8_1_d.h"
-#include "gmc/gmc_8_1_sh_mask.h"
-
-#include "bif/bif_5_0_d.h"
-#include "bif/bif_5_0_sh_mask.h"
-
-#include "dce/dce_10_0_d.h"
-#include "dce/dce_10_0_sh_mask.h"
-
-#include "cgs_linux.h"
-#include "eventmgr.h"
-#include "amd_pcie_helpers.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 PCIE_BUS_CLK                10000
-#define TCLK                        (PCIE_BUS_CLK / 10)
-
-#define SMC_RAM_END 0x40000
-#define SMC_CG_IND_START            0xc0030000
-#define SMC_CG_IND_END              0xc0040000  /* First byte after SMC_CG_IND*/
-
-#define VOLTAGE_SCALE               4
-#define VOLTAGE_VID_OFFSET_SCALE1   625
-#define VOLTAGE_VID_OFFSET_SCALE2   100
-
-#define VDDC_VDDCI_DELTA            200
-#define VDDC_VDDGFX_DELTA           300
-
-#define MC_SEQ_MISC0_GDDR5_SHIFT 28
-#define MC_SEQ_MISC0_GDDR5_MASK  0xf0000000
-#define MC_SEQ_MISC0_GDDR5_VALUE 5
-
-typedef uint32_t PECI_RegistryValue;
-
-/* [2.5%,~2.5%] Clock stretched is multiple of 2.5% vs not and [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ] */
-static const uint16_t PP_ClockStretcherLookupTable[2][4] = {
-       {600, 1050, 3, 0},
-       {600, 1050, 6, 1} };
-
-/* [FF, SS] type, [] 4 voltage ranges, and [Floor Freq, Boundary Freq, VID min , VID max] */
-static const uint32_t PP_ClockStretcherDDTTable[2][4][4] = {
-       { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
-       { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} } };
-
-/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%] (coming from PWR_CKS_CNTL.stretch_amount reg spec) */
-static const uint8_t PP_ClockStretchAmountConversion[2][6] = {
-       {0, 1, 3, 2, 4, 5},
-       {0, 2, 4, 5, 6, 5} };
-
-/* Values for the CG_THERMAL_CTRL::DPM_EVENT_SRC field. */
-enum DPM_EVENT_SRC {
-       DPM_EVENT_SRC_ANALOG = 0,               /* Internal analog trip point */
-       DPM_EVENT_SRC_EXTERNAL = 1,             /* External (GPIO 17) signal */
-       DPM_EVENT_SRC_DIGITAL = 2,              /* Internal digital trip point (DIG_THERM_DPM) */
-       DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3,   /* Internal analog or external */
-       DPM_EVENT_SRC_DIGITAL_OR_EXTERNAL = 4   /* Internal digital or external */
-};
-typedef enum DPM_EVENT_SRC DPM_EVENT_SRC;
-
-static const unsigned long PhwTonga_Magic = (unsigned long)(PHM_VIslands_Magic);
-
-struct tonga_power_state *cast_phw_tonga_power_state(
-                                 struct pp_hw_power_state *hw_ps)
-{
-       if (hw_ps == NULL)
-               return NULL;
-
-       PP_ASSERT_WITH_CODE((PhwTonga_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL);
-
-       return (struct tonga_power_state *)hw_ps;
-}
-
-const struct tonga_power_state *cast_const_phw_tonga_power_state(
-                                const struct pp_hw_power_state *hw_ps)
-{
-       if (hw_ps == NULL)
-               return NULL;
-
-       PP_ASSERT_WITH_CODE((PhwTonga_Magic == hw_ps->magic),
-                               "Invalid Powerstate Type!",
-                                return NULL);
-
-       return (const struct tonga_power_state *)hw_ps;
-}
-
-int tonga_add_voltage(struct pp_hwmgr *hwmgr,
-       phm_ppt_v1_voltage_lookup_table *look_up_table,
-       phm_ppt_v1_voltage_lookup_record *record)
-{
-       uint32_t i;
-       PP_ASSERT_WITH_CODE((NULL != look_up_table),
-               "Lookup Table empty.", return -1;);
-       PP_ASSERT_WITH_CODE((0 != look_up_table->count),
-               "Lookup Table empty.", return -1;);
-       PP_ASSERT_WITH_CODE((SMU72_MAX_LEVELS_VDDGFX >= look_up_table->count),
-               "Lookup Table is full.", return -1;);
-
-       /* This is to avoid entering duplicate calculated records. */
-       for (i = 0; i < look_up_table->count; i++) {
-               if (look_up_table->entries[i].us_vdd == record->us_vdd) {
-                       if (look_up_table->entries[i].us_calculated == 1)
-                               return 0;
-                       else
-                               break;
-               }
-       }
-
-       look_up_table->entries[i].us_calculated = 1;
-       look_up_table->entries[i].us_vdd = record->us_vdd;
-       look_up_table->entries[i].us_cac_low = record->us_cac_low;
-       look_up_table->entries[i].us_cac_mid = record->us_cac_mid;
-       look_up_table->entries[i].us_cac_high = record->us_cac_high;
-       /* Only increment the count when we're appending, not replacing duplicate entry. */
-       if (i == look_up_table->count)
-               look_up_table->count++;
-
-       return 0;
-}
-
-int tonga_notify_smc_display_change(struct pp_hwmgr *hwmgr, bool has_display)
-{
-       PPSMC_Msg msg = has_display? (PPSMC_Msg)PPSMC_HasDisplay : (PPSMC_Msg)PPSMC_NoDisplay;
-
-       return (smum_send_msg_to_smc(hwmgr->smumgr, msg) == 0) ?  0 : -1;
-}
-
-uint8_t tonga_get_voltage_id(pp_atomctrl_voltage_table *voltage_table,
-               uint32_t voltage)
-{
-       uint8_t count = (uint8_t) (voltage_table->count);
-       uint8_t i = 0;
-
-       PP_ASSERT_WITH_CODE((NULL != voltage_table),
-               "Voltage Table empty.", return 0;);
-       PP_ASSERT_WITH_CODE((0 != count),
-               "Voltage Table empty.", return 0;);
-
-       for (i = 0; i < count; i++) {
-               /* find first voltage bigger than requested */
-               if (voltage_table->entries[i].value >= voltage)
-                       return i;
-       }
-
-       /* voltage is bigger than max voltage in the table */
-       return i - 1;
-}
-
-/**
- * @brief PhwTonga_GetVoltageOrder
- *  Returns index of requested voltage record in lookup(table)
- * @param hwmgr - pointer to hardware manager
- * @param lookupTable - lookup list to search in
- * @param voltage - voltage to look for
- * @return 0 on success
- */
-uint8_t tonga_get_voltage_index(phm_ppt_v1_voltage_lookup_table *look_up_table,
-               uint16_t voltage)
-{
-       uint8_t count = (uint8_t) (look_up_table->count);
-       uint8_t i;
-
-       PP_ASSERT_WITH_CODE((NULL != look_up_table), "Lookup Table empty.", return 0;);
-       PP_ASSERT_WITH_CODE((0 != count), "Lookup Table empty.", return 0;);
-
-       for (i = 0; i < count; i++) {
-               /* find first voltage equal or bigger than requested */
-               if (look_up_table->entries[i].us_vdd >= voltage)
-                       return i;
-       }
-
-       /* voltage is bigger than max voltage in the table */
-       return i-1;
-}
-
-bool tonga_is_dpm_running(struct pp_hwmgr *hwmgr)
-{
-       /*
-        * We return the status of Voltage Control instead of checking SCLK/MCLK DPM
-        * because we may have test scenarios that need us intentionly disable SCLK/MCLK DPM,
-        * whereas voltage control is a fundemental change that will not be disabled
-        */
-
-       return (0 == PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                       FEATURE_STATUS, VOLTAGE_CONTROLLER_ON) ? 1 : 0);
-}
-
-/**
- * Re-generate the DPM level mask value
- * @param    hwmgr      the address of the hardware manager
- */
-static uint32_t tonga_get_dpm_level_enable_mask_value(
-                       struct tonga_single_dpm_table * dpm_table)
-{
-       uint32_t i;
-       uint32_t mask_value = 0;
-
-       for (i = dpm_table->count; i > 0; i--) {
-               mask_value = mask_value << 1;
-
-               if (dpm_table->dpm_levels[i-1].enabled)
-                       mask_value |= 0x1;
-               else
-                       mask_value &= 0xFFFFFFFE;
-       }
-       return mask_value;
-}
-
-/**
- * Retrieve DPM default values from registry (if available)
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- */
-void tonga_initialize_dpm_defaults(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       phw_tonga_ulv_parm *ulv = &(data->ulv);
-       uint32_t tmp;
-
-       ulv->ch_ulv_parameter = PPTONGA_CGULVPARAMETER_DFLT;
-       data->voting_rights_clients0 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT0;
-       data->voting_rights_clients1 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT1;
-       data->voting_rights_clients2 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT2;
-       data->voting_rights_clients3 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT3;
-       data->voting_rights_clients4 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT4;
-       data->voting_rights_clients5 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT5;
-       data->voting_rights_clients6 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT6;
-       data->voting_rights_clients7 = PPTONGA_VOTINGRIGHTSCLIENTS_DFLT7;
-
-       data->static_screen_threshold_unit = PPTONGA_STATICSCREENTHRESHOLDUNIT_DFLT;
-       data->static_screen_threshold = PPTONGA_STATICSCREENTHRESHOLD_DFLT;
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_ABM);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_NonABMSupportInPPLib);
-
-       tmp = 0;
-       if (tmp == 0)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DynamicACTiming);
-
-       tmp = 0;
-       if (0 != tmp)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DisableMemoryTransition);
-
-       data->mclk_strobe_mode_threshold = 40000;
-       data->mclk_stutter_mode_threshold = 30000;
-       data->mclk_edc_enable_threshold = 40000;
-       data->mclk_edc_wr_enable_threshold = 40000;
-
-       tmp = 0;
-       if (tmp != 0)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_DisableMCLS);
-
-       data->pcie_gen_performance.max = PP_PCIEGen1;
-       data->pcie_gen_performance.min = PP_PCIEGen3;
-       data->pcie_gen_power_saving.max = PP_PCIEGen1;
-       data->pcie_gen_power_saving.min = PP_PCIEGen3;
-
-       data->pcie_lane_performance.max = 0;
-       data->pcie_lane_performance.min = 16;
-       data->pcie_lane_power_saving.max = 0;
-       data->pcie_lane_power_saving.min = 16;
-
-       tmp = 0;
-
-       if (tmp)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkThrottleLowNotification);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_DynamicUVDState);
-
-}
-
-int tonga_update_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       int result = 0;
-       uint32_t low_sclk_interrupt_threshold = 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkThrottleLowNotification)
-               && (hwmgr->gfx_arbiter.sclk_threshold != data->low_sclk_interrupt_threshold)) {
-               data->low_sclk_interrupt_threshold = hwmgr->gfx_arbiter.sclk_threshold;
-               low_sclk_interrupt_threshold = data->low_sclk_interrupt_threshold;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
-
-               result = tonga_copy_bytes_to_smc(
-                               hwmgr->smumgr,
-                               data->dpm_table_start + offsetof(SMU72_Discrete_DpmTable,
-                               LowSclkInterruptThreshold),
-                               (uint8_t *)&low_sclk_interrupt_threshold,
-                               sizeof(uint32_t),
-                               data->sram_end
-                               );
-       }
-
-       return result;
-}
-
-/**
- * Find SCLK value that is associated with specified virtual_voltage_Id.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @param    virtual_voltage_Id  voltageId to look for.
- * @param    sclk output value .
- * @return   always 0 if success and 2 if association not found
- */
-static int tonga_get_sclk_for_voltage_evv(struct pp_hwmgr *hwmgr,
-       phm_ppt_v1_voltage_lookup_table *lookup_table,
-       uint16_t virtual_voltage_id, uint32_t *sclk)
-{
-       uint8_t entryId;
-       uint8_t voltageId;
-       struct phm_ppt_v1_information *pptable_info =
-                                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       PP_ASSERT_WITH_CODE(lookup_table->count != 0, "Lookup table is empty", return -1);
-
-       /* search for leakage voltage ID 0xff01 ~ 0xff08 and sckl */
-       for (entryId = 0; entryId < pptable_info->vdd_dep_on_sclk->count; entryId++) {
-               voltageId = pptable_info->vdd_dep_on_sclk->entries[entryId].vddInd;
-               if (lookup_table->entries[voltageId].us_vdd == virtual_voltage_id)
-                       break;
-       }
-
-       PP_ASSERT_WITH_CODE(entryId < pptable_info->vdd_dep_on_sclk->count,
-                       "Can't find requested voltage id in vdd_dep_on_sclk table!",
-                       return -1;
-                       );
-
-       *sclk = pptable_info->vdd_dep_on_sclk->entries[entryId].clk;
-
-       return 0;
-}
-
-/**
- * Get Leakage VDDC based on leakage ID.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   2 if vddgfx returned is greater than 2V or if BIOS
- */
-int tonga_get_evv_voltage(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_clock_voltage_dependency_table *sclk_table = pptable_info->vdd_dep_on_sclk;
-       uint16_t    virtual_voltage_id;
-       uint16_t    vddc = 0;
-       uint16_t    vddgfx = 0;
-       uint16_t    i, j;
-       uint32_t  sclk = 0;
-
-       /* retrieve voltage for leakage ID (0xff01 + i) */
-       for (i = 0; i < TONGA_MAX_LEAKAGE_COUNT; i++) {
-               virtual_voltage_id = ATOM_VIRTUAL_VOLTAGE_ID0 + i;
-
-               /* in split mode we should have only vddgfx EVV leakages */
-               if (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) {
-                       if (0 == tonga_get_sclk_for_voltage_evv(hwmgr,
-                                               pptable_info->vddgfx_lookup_table, virtual_voltage_id, &sclk)) {
-                               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                                                       PHM_PlatformCaps_ClockStretcher)) {
-                                       for (j = 1; j < sclk_table->count; j++) {
-                                               if (sclk_table->entries[j].clk == sclk &&
-                                                               sclk_table->entries[j].cks_enable == 0) {
-                                                       sclk += 5000;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               if (0 == atomctrl_get_voltage_evv_on_sclk
-                                   (hwmgr, VOLTAGE_TYPE_VDDGFX, sclk,
-                                    virtual_voltage_id, &vddgfx)) {
-                                       /* need to make sure vddgfx is less than 2v or else, it could burn the ASIC. */
-                                       PP_ASSERT_WITH_CODE((vddgfx < 2000 && vddgfx != 0), "Invalid VDDGFX value!", return -1);
-
-                                       /* the voltage should not be zero nor equal to leakage ID */
-                                       if (vddgfx != 0 && vddgfx != virtual_voltage_id) {
-                                               data->vddcgfx_leakage.actual_voltage[data->vddcgfx_leakage.count] = vddgfx;
-                                               data->vddcgfx_leakage.leakage_id[data->vddcgfx_leakage.count] = virtual_voltage_id;
-                                               data->vddcgfx_leakage.count++;
-                                       }
-                               } else {
-                                       printk("Error retrieving EVV voltage value!\n");
-                               }
-                       }
-               } else {
-                       /*  in merged mode we have only vddc EVV leakages */
-                       if (0 == tonga_get_sclk_for_voltage_evv(hwmgr,
-                                               pptable_info->vddc_lookup_table,
-                                               virtual_voltage_id, &sclk)) {
-                               if (0 == atomctrl_get_voltage_evv_on_sclk
-                                   (hwmgr, VOLTAGE_TYPE_VDDC, sclk,
-                                    virtual_voltage_id, &vddc)) {
-                                       /* need to make sure vddc is less than 2v or else, it could burn the ASIC. */
-                                       PP_ASSERT_WITH_CODE(vddc < 2000, "Invalid VDDC value!", return -1);
-
-                                       /* the voltage should not be zero nor equal to leakage ID */
-                                       if (vddc != 0 && vddc != virtual_voltage_id) {
-                                               data->vddc_leakage.actual_voltage[data->vddc_leakage.count] = vddc;
-                                               data->vddc_leakage.leakage_id[data->vddc_leakage.count] = virtual_voltage_id;
-                                               data->vddc_leakage.count++;
-                                       }
-                               } else {
-                                       printk("Error retrieving EVV voltage value!\n");
-                               }
-                       }
-               }
-       }
-
-       return 0;
-}
-
-int tonga_enable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* enable SCLK dpm */
-       if (0 == data->sclk_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                                  PPSMC_MSG_DPM_Enable)),
-                               "Failed to enable SCLK DPM during DPM Start Function!",
-                               return -1);
-       }
-
-       /* enable MCLK dpm */
-       if (0 == data->mclk_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                            PPSMC_MSG_MCLKDPM_Enable)),
-                               "Failed to enable MCLK DPM during DPM Start Function!",
-                               return -1);
-
-               PHM_WRITE_FIELD(hwmgr->device, MC_SEQ_CNTL_3, CAC_EN, 0x1);
-
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_MC0_CNTL, 0x05);/* CH0,1 read */
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_MC1_CNTL, 0x05);/* CH2,3 read */
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_CPL_CNTL, 0x100005);/*Read */
-
-               udelay(10);
-
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_MC0_CNTL, 0x400005);/* CH0,1 write */
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_MC1_CNTL, 0x400005);/* CH2,3 write */
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixLCAC_CPL_CNTL, 0x500005);/* write */
-
-       }
-
-       return 0;
-}
-
-int tonga_start_dpm(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* enable general power management */
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, 1);
-       /* enable sclk deep sleep */
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL, DYNAMIC_PM_EN, 1);
-
-       /* prepare for PCIE DPM */
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start +
-                       offsetof(SMU72_SoftRegisters, VoltageChangeTimeout), 0x1000);
-
-       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__PCIE, SWRST_COMMAND_1, RESETLC, 0x0);
-
-       PP_ASSERT_WITH_CODE(
-                       (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                       PPSMC_MSG_Voltage_Cntl_Enable)),
-                       "Failed to enable voltage DPM during DPM Start Function!",
-                       return -1);
-
-       if (0 != tonga_enable_sclk_mclk_dpm(hwmgr)) {
-               PP_ASSERT_WITH_CODE(0, "Failed to enable Sclk DPM and Mclk DPM!", return -1);
-       }
-
-       /* enable PCIE dpm */
-       if (0 == data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Enable)),
-                               "Failed to enable pcie DPM during DPM Start Function!",
-                               return -1
-                               );
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_Falcon_QuickTransition)) {
-                                  smum_send_msg_to_smc(hwmgr->smumgr,
-                                   PPSMC_MSG_EnableACDCGPIOInterrupt);
-       }
-
-       return 0;
-}
-
-int tonga_disable_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* disable SCLK dpm */
-       if (0 == data->sclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-               PP_ASSERT_WITH_CODE(
-                               !tonga_is_dpm_running(hwmgr),
-                               "Trying to Disable SCLK DPM when DPM is disabled",
-                               return -1
-                               );
-
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                                 PPSMC_MSG_DPM_Disable)),
-                               "Failed to disable SCLK DPM during DPM stop Function!",
-                               return -1);
-       }
-
-       /* disable MCLK dpm */
-       if (0 == data->mclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this, we should skip this message. */
-               PP_ASSERT_WITH_CODE(
-                               !tonga_is_dpm_running(hwmgr),
-                               "Trying to Disable MCLK DPM when DPM is disabled",
-                               return -1
-                               );
-
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                           PPSMC_MSG_MCLKDPM_Disable)),
-                               "Failed to Disable MCLK DPM during DPM stop Function!",
-                               return -1);
-       }
-
-       return 0;
-}
-
-int tonga_stop_dpm(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, 0);
-       /* disable sclk deep sleep*/
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL, DYNAMIC_PM_EN, 0);
-
-       /* disable PCIE dpm */
-       if (0 == data->pcie_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-               PP_ASSERT_WITH_CODE(
-                               !tonga_is_dpm_running(hwmgr),
-                               "Trying to Disable PCIE DPM when DPM is disabled",
-                               return -1
-                               );
-               PP_ASSERT_WITH_CODE(
-                               (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                               PPSMC_MSG_PCIeDPM_Disable)),
-                               "Failed to disable pcie DPM during DPM stop Function!",
-                               return -1);
-       }
-
-       if (0 != tonga_disable_sclk_mclk_dpm(hwmgr))
-               PP_ASSERT_WITH_CODE(0, "Failed to disable Sclk DPM and Mclk DPM!", return -1);
-
-       /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-       PP_ASSERT_WITH_CODE(
-                       !tonga_is_dpm_running(hwmgr),
-                       "Trying to Disable Voltage CNTL when DPM is disabled",
-                       return -1
-                       );
-
-       PP_ASSERT_WITH_CODE(
-                       (0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                       PPSMC_MSG_Voltage_Cntl_Disable)),
-                       "Failed to disable voltage DPM during DPM stop Function!",
-                       return -1);
-
-       return 0;
-}
-
-int tonga_enable_sclk_control(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, 0);
-
-       return 0;
-}
-
-/**
- * Send a message to the SMC and return a parameter
- *
- * @param    hwmgr:  the address of the powerplay hardware manager.
- * @param    msg: the message to send.
- * @param    parameter: pointer to the received parameter
- * @return   The response that came from the SMC.
- */
-PPSMC_Result tonga_send_msg_to_smc_return_parameter(
-               struct pp_hwmgr *hwmgr,
-               PPSMC_Msg msg,
-               uint32_t *parameter)
-{
-       int result;
-
-       result = smum_send_msg_to_smc(hwmgr->smumgr, msg);
-
-       if ((0 == result) && parameter) {
-               *parameter = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-       }
-
-       return result;
-}
-
-/**
- * force DPM power State
- *
- * @param    hwmgr:  the address of the powerplay hardware manager.
- * @param    n     :  DPM level
- * @return   The response that came from the SMC.
- */
-int tonga_dpm_force_state(struct pp_hwmgr *hwmgr, uint32_t n)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t level_mask = 1 << n;
-
-       /* Checking if DPM is running.  If we discover hang because of this, we should skip this message. */
-       PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                           "Trying to force SCLK when DPM is disabled",
-                           return -1;);
-       if (0 == data->sclk_dpm_key_disabled)
-               return (0 == smum_send_msg_to_smc_with_parameter(
-                                                            hwmgr->smumgr,
-                    (PPSMC_Msg)(PPSMC_MSG_SCLKDPM_SetEnabledMask),
-                                                   level_mask) ? 0 : 1);
-
-       return 0;
-}
-
-/**
- * force DPM power State
- *
- * @param    hwmgr:  the address of the powerplay hardware manager.
- * @param    n     :  DPM level
- * @return   The response that came from the SMC.
- */
-int tonga_dpm_force_state_mclk(struct pp_hwmgr *hwmgr, uint32_t n)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t level_mask = 1 << n;
-
-       /* Checking if DPM is running.  If we discover hang because of this, we should skip this message. */
-       PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                           "Trying to Force MCLK when DPM is disabled",
-                           return -1;);
-       if (0 == data->mclk_dpm_key_disabled)
-               return (0 == smum_send_msg_to_smc_with_parameter(
-                                                               hwmgr->smumgr,
-                                                               (PPSMC_Msg)(PPSMC_MSG_MCLKDPM_SetEnabledMask),
-                                                               level_mask) ? 0 : 1);
-
-       return 0;
-}
-
-/**
- * force DPM power State
- *
- * @param    hwmgr:  the address of the powerplay hardware manager.
- * @param    n     :  DPM level
- * @return   The response that came from the SMC.
- */
-int tonga_dpm_force_state_pcie(struct pp_hwmgr *hwmgr, uint32_t n)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-       PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                           "Trying to Force PCIE level when DPM is disabled",
-                           return -1;);
-       if (0 == data->pcie_dpm_key_disabled)
-               return (0 == smum_send_msg_to_smc_with_parameter(
-                                                            hwmgr->smumgr,
-                          (PPSMC_Msg)(PPSMC_MSG_PCIeDPM_ForceLevel),
-                                                               n) ? 0 : 1);
-
-       return 0;
-}
-
-/**
- * Set the initial state by calling SMC to switch to this state directly
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_set_boot_state(struct pp_hwmgr *hwmgr)
-{
-       /*
-       * SMC only stores one state that SW will ask to switch too,
-       * so we switch the the just uploaded one
-       */
-       return (0 == tonga_disable_sclk_mclk_dpm(hwmgr)) ? 0 : 1;
-}
-
-/**
- * Get the location of various tables inside the FW image.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_process_firmware_header(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_smumgr *tonga_smu = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
-
-       uint32_t tmp;
-       int result;
-       bool error = false;
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, DpmTable),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               data->dpm_table_start = tmp;
-       }
-
-       error |= (0 != result);
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, SoftRegisters),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               data->soft_regs_start = tmp;
-               tonga_smu->ulSoftRegsStart = tmp;
-       }
-
-       error |= (0 != result);
-
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, mcRegisterTable),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               data->mc_reg_table_start = tmp;
-       }
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, FanTable),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               data->fan_table_start = tmp;
-       }
-
-       error |= (0 != result);
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, mcArbDramTimingTable),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               data->arb_table_start = tmp;
-       }
-
-       error |= (0 != result);
-
-
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               SMU72_FIRMWARE_HEADER_LOCATION +
-                               offsetof(SMU72_Firmware_Header, Version),
-                               &tmp, data->sram_end);
-
-       if (0 == result) {
-               hwmgr->microcode_version_info.SMC = tmp;
-       }
-
-       error |= (0 != result);
-
-       return error ? 1 : 0;
-}
-
-/**
- * Read clock related registers.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_read_clock_registers(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       data->clock_registers.vCG_SPLL_FUNC_CNTL         =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_2       =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_2);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_3       =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_3);
-       data->clock_registers.vCG_SPLL_FUNC_CNTL_4       =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_FUNC_CNTL_4);
-       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM   =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM);
-       data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2 =
-               cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_SPLL_SPREAD_SPECTRUM_2);
-       data->clock_registers.vDLL_CNTL                  =
-               cgs_read_register(hwmgr->device, mmDLL_CNTL);
-       data->clock_registers.vMCLK_PWRMGT_CNTL          =
-               cgs_read_register(hwmgr->device, mmMCLK_PWRMGT_CNTL);
-       data->clock_registers.vMPLL_AD_FUNC_CNTL         =
-               cgs_read_register(hwmgr->device, mmMPLL_AD_FUNC_CNTL);
-       data->clock_registers.vMPLL_DQ_FUNC_CNTL         =
-               cgs_read_register(hwmgr->device, mmMPLL_DQ_FUNC_CNTL);
-       data->clock_registers.vMPLL_FUNC_CNTL            =
-               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL);
-       data->clock_registers.vMPLL_FUNC_CNTL_1          =
-               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_1);
-       data->clock_registers.vMPLL_FUNC_CNTL_2          =
-               cgs_read_register(hwmgr->device, mmMPLL_FUNC_CNTL_2);
-       data->clock_registers.vMPLL_SS1                  =
-               cgs_read_register(hwmgr->device, mmMPLL_SS1);
-       data->clock_registers.vMPLL_SS2                  =
-               cgs_read_register(hwmgr->device, mmMPLL_SS2);
-
-       return 0;
-}
-
-/**
- * Find out if memory is GDDR5.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_get_memory_type(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t temp;
-
-       temp = cgs_read_register(hwmgr->device, mmMC_SEQ_MISC0);
-
-       data->is_memory_GDDR5 = (MC_SEQ_MISC0_GDDR5_VALUE ==
-                       ((temp & MC_SEQ_MISC0_GDDR5_MASK) >>
-                        MC_SEQ_MISC0_GDDR5_SHIFT));
-
-       return 0;
-}
-
-/**
- * Enables Dynamic Power Management by SMC
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_enable_acpi_power_management(struct pp_hwmgr *hwmgr)
-{
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT, STATIC_PM_EN, 1);
-
-       return 0;
-}
-
-/**
- * Initialize PowerGating States for different engines
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_init_power_gate_state(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       data->uvd_power_gated = false;
-       data->vce_power_gated = false;
-       data->samu_power_gated = false;
-       data->acp_power_gated = false;
-       data->pg_acp_init = true;
-
-       return 0;
-}
-
-/**
- * Checks if DPM is enabled
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_check_for_dpm_running(struct pp_hwmgr *hwmgr)
-{
-       /*
-        * We return the status of Voltage Control instead of checking SCLK/MCLK DPM
-        * because we may have test scenarios that need us intentionly disable SCLK/MCLK DPM,
-        * whereas voltage control is a fundemental change that will not be disabled
-        */
-       return (!tonga_is_dpm_running(hwmgr) ? 0 : 1);
-}
-
-/**
- * Checks if DPM is stopped
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_check_for_dpm_stopped(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       if (tonga_is_dpm_running(hwmgr)) {
-               /* If HW Virtualization is enabled, dpm_table_start will not have a valid value */
-               if (!data->dpm_table_start) {
-                       return 1;
-               }
-       }
-
-       return 0;
-}
-
-/**
- * Remove repeated voltage values and create table with unique values.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @param    voltage_table  the pointer to changing voltage table
- * @return    1 in success
- */
-
-static int tonga_trim_voltage_table(struct pp_hwmgr *hwmgr,
-                       pp_atomctrl_voltage_table *voltage_table)
-{
-       uint32_t table_size, i, j;
-       uint16_t vvalue;
-       bool bVoltageFound = false;
-       pp_atomctrl_voltage_table *table;
-
-       PP_ASSERT_WITH_CODE((NULL != voltage_table), "Voltage Table empty.", return -1;);
-       table_size = sizeof(pp_atomctrl_voltage_table);
-       table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == table)
-               return -ENOMEM;
-
-       memset(table, 0x00, table_size);
-       table->mask_low = voltage_table->mask_low;
-       table->phase_delay = voltage_table->phase_delay;
-
-       for (i = 0; i < voltage_table->count; i++) {
-               vvalue = voltage_table->entries[i].value;
-               bVoltageFound = false;
-
-               for (j = 0; j < table->count; j++) {
-                       if (vvalue == table->entries[j].value) {
-                               bVoltageFound = true;
-                               break;
-                       }
-               }
-
-               if (!bVoltageFound) {
-                       table->entries[table->count].value = vvalue;
-                       table->entries[table->count].smio_low =
-                               voltage_table->entries[i].smio_low;
-                       table->count++;
-               }
-       }
-
-       memcpy(table, voltage_table, sizeof(pp_atomctrl_voltage_table));
-
-       kfree(table);
-
-       return 0;
-}
-
-static int tonga_get_svi2_vdd_ci_voltage_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_clock_voltage_dependency_table *voltage_dependency_table)
-{
-       uint32_t i;
-       int result;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       pp_atomctrl_voltage_table *vddci_voltage_table = &(data->vddci_voltage_table);
-
-       PP_ASSERT_WITH_CODE((0 != voltage_dependency_table->count),
-                       "Voltage Dependency Table empty.", return -1;);
-
-       vddci_voltage_table->mask_low = 0;
-       vddci_voltage_table->phase_delay = 0;
-       vddci_voltage_table->count = voltage_dependency_table->count;
-
-       for (i = 0; i < voltage_dependency_table->count; i++) {
-               vddci_voltage_table->entries[i].value =
-                       voltage_dependency_table->entries[i].vddci;
-               vddci_voltage_table->entries[i].smio_low = 0;
-       }
-
-       result = tonga_trim_voltage_table(hwmgr, vddci_voltage_table);
-       PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to trim VDDCI table.", return result;);
-
-       return 0;
-}
-
-
-
-static int tonga_get_svi2_vdd_voltage_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *look_up_table,
-               pp_atomctrl_voltage_table *voltage_table)
-{
-       uint8_t i = 0;
-
-       PP_ASSERT_WITH_CODE((0 != look_up_table->count),
-                       "Voltage Lookup Table empty.", return -1;);
-
-       voltage_table->mask_low = 0;
-       voltage_table->phase_delay = 0;
-
-       voltage_table->count = look_up_table->count;
-
-       for (i = 0; i < voltage_table->count; i++) {
-               voltage_table->entries[i].value = look_up_table->entries[i].us_vdd;
-               voltage_table->entries[i].smio_low = 0;
-       }
-
-       return 0;
-}
-
-/*
- * -------------------------------------------------------- Voltage Tables --------------------------------------------------------------------------
- * If the voltage table would be bigger than what will fit into the state table on the SMC keep only the higher entries.
- */
-
-static void tonga_trim_voltage_table_to_fit_state_table(
-               struct pp_hwmgr *hwmgr,
-               uint32_t max_voltage_steps,
-               pp_atomctrl_voltage_table *voltage_table)
-{
-       unsigned int i, diff;
-
-       if (voltage_table->count <= max_voltage_steps) {
-               return;
-       }
-
-       diff = voltage_table->count - max_voltage_steps;
-
-       for (i = 0; i < max_voltage_steps; i++) {
-               voltage_table->entries[i] = voltage_table->entries[i + diff];
-       }
-
-       voltage_table->count = max_voltage_steps;
-
-       return;
-}
-
-/**
- * Create Voltage Tables.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_construct_voltage_tables(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int result;
-
-       /* MVDD has only GPIO voltage control */
-       if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                                       VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT, &(data->mvdd_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to retrieve MVDD table.", return result;);
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->vdd_ci_control) {
-               /* GPIO voltage */
-               result = atomctrl_get_voltage_table_v3(hwmgr,
-                                       VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT, &(data->vddci_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to retrieve VDDCI table.", return result;);
-       } else if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_ci_control) {
-               /* SVI2 voltage */
-               result = tonga_get_svi2_vdd_ci_voltage_table(hwmgr,
-                                       pptable_info->vdd_dep_on_mclk);
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to retrieve SVI2 VDDCI table from dependancy table.", return result;);
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
-               /* VDDGFX has only SVI2 voltage control */
-               result = tonga_get_svi2_vdd_voltage_table(hwmgr,
-                                       pptable_info->vddgfx_lookup_table, &(data->vddgfx_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to retrieve SVI2 VDDGFX table from lookup table.", return result;);
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               /* VDDC has only SVI2 voltage control */
-               result = tonga_get_svi2_vdd_voltage_table(hwmgr,
-                                       pptable_info->vddc_lookup_table, &(data->vddc_voltage_table));
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to retrieve SVI2 VDDC table from lookup table.", return result;);
-       }
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddc_voltage_table.count <= (SMU72_MAX_LEVELS_VDDC)),
-                       "Too many voltage values for VDDC. Trimming to fit state table.",
-                       tonga_trim_voltage_table_to_fit_state_table(hwmgr,
-                       SMU72_MAX_LEVELS_VDDC, &(data->vddc_voltage_table));
-                       );
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddgfx_voltage_table.count <= (SMU72_MAX_LEVELS_VDDGFX)),
-                       "Too many voltage values for VDDGFX. Trimming to fit state table.",
-                       tonga_trim_voltage_table_to_fit_state_table(hwmgr,
-                       SMU72_MAX_LEVELS_VDDGFX, &(data->vddgfx_voltage_table));
-                       );
-
-       PP_ASSERT_WITH_CODE(
-                       (data->vddci_voltage_table.count <= (SMU72_MAX_LEVELS_VDDCI)),
-                       "Too many voltage values for VDDCI. Trimming to fit state table.",
-                       tonga_trim_voltage_table_to_fit_state_table(hwmgr,
-                       SMU72_MAX_LEVELS_VDDCI, &(data->vddci_voltage_table));
-                       );
-
-       PP_ASSERT_WITH_CODE(
-                       (data->mvdd_voltage_table.count <= (SMU72_MAX_LEVELS_MVDD)),
-                       "Too many voltage values for MVDD. Trimming to fit state table.",
-                       tonga_trim_voltage_table_to_fit_state_table(hwmgr,
-                       SMU72_MAX_LEVELS_MVDD, &(data->mvdd_voltage_table));
-                       );
-
-       return 0;
-}
-
-/**
- * Vddc table preparation for SMC.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    table     the SMC DPM table structure to be populated
- * @return   always 0
- */
-static int tonga_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       unsigned int count;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-               table->VddcLevelCount = data->vddc_voltage_table.count;
-               for (count = 0; count < table->VddcLevelCount; count++) {
-                       table->VddcTable[count] =
-                               PP_HOST_TO_SMC_US(data->vddc_voltage_table.entries[count].value * VOLTAGE_SCALE);
-               }
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
-       }
-       return 0;
-}
-
-/**
- * VddGfx table preparation for SMC.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    table     the SMC DPM table structure to be populated
- * @return   always 0
- */
-static int tonga_populate_smc_vdd_gfx_table(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       unsigned int count;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
-               table->VddGfxLevelCount = data->vddgfx_voltage_table.count;
-               for (count = 0; count < data->vddgfx_voltage_table.count; count++) {
-                       table->VddGfxTable[count] =
-                               PP_HOST_TO_SMC_US(data->vddgfx_voltage_table.entries[count].value * VOLTAGE_SCALE);
-               }
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VddGfxLevelCount);
-       }
-       return 0;
-}
-
-/**
- * Vddci table preparation for SMC.
- *
- * @param    *hwmgr The address of the hardware manager.
- * @param    *table The SMC DPM table structure to be populated.
- * @return   0
- */
-static int tonga_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t count;
-
-       table->VddciLevelCount = data->vddci_voltage_table.count;
-       for (count = 0; count < table->VddciLevelCount; count++) {
-               if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_ci_control) {
-                       table->VddciTable[count] =
-                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
-               } else if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->vdd_ci_control) {
-                       table->SmioTable1.Pattern[count].Voltage =
-                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
-                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level. */
-                       table->SmioTable1.Pattern[count].Smio =
-                               (uint8_t) count;
-                       table->Smio[count] |=
-                               data->vddci_voltage_table.entries[count].smio_low;
-                       table->VddciTable[count] =
-                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
-               }
-       }
-
-       table->SmioMask1 = data->vddci_voltage_table.mask_low;
-       CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
-
-       return 0;
-}
-
-/**
- * Mvdd table preparation for SMC.
- *
- * @param    *hwmgr The address of the hardware manager.
- * @param    *table The SMC DPM table structure to be populated.
- * @return   0
- */
-static int tonga_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t count;
-
-       if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               table->MvddLevelCount = data->mvdd_voltage_table.count;
-               for (count = 0; count < table->MvddLevelCount; count++) {
-                       table->SmioTable2.Pattern[count].Voltage =
-                               PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[count].value * VOLTAGE_SCALE);
-                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
-                       table->SmioTable2.Pattern[count].Smio =
-                               (uint8_t) count;
-                       table->Smio[count] |=
-                               data->mvdd_voltage_table.entries[count].smio_low;
-               }
-               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
-       }
-
-       return 0;
-}
-
-/**
- * Convert a voltage value in mv unit to VID number required by SMU firmware
- */
-static uint8_t convert_to_vid(uint16_t vddc)
-{
-       return (uint8_t) ((6200 - (vddc * VOLTAGE_SCALE)) / 25);
-}
-
-
-/**
- * Preparation of vddc and vddgfx CAC tables for SMC.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    table     the SMC DPM table structure to be populated
- * @return   always 0
- */
-static int tonga_populate_cac_tables(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       uint32_t count;
-       uint8_t index;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct phm_ppt_v1_voltage_lookup_table *vddgfx_lookup_table = pptable_info->vddgfx_lookup_table;
-       struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table = pptable_info->vddc_lookup_table;
-
-       /* pTables is already swapped, so in order to use the value from it, we need to swap it back. */
-       uint32_t vddcLevelCount = PP_SMC_TO_HOST_UL(table->VddcLevelCount);
-       uint32_t vddgfxLevelCount = PP_SMC_TO_HOST_UL(table->VddGfxLevelCount);
-
-       for (count = 0; count < vddcLevelCount; count++) {
-               /* We are populating vddc CAC data to BapmVddc table in split and merged mode */
-               index = tonga_get_voltage_index(vddc_lookup_table,
-                       data->vddc_voltage_table.entries[count].value);
-               table->BapmVddcVidLoSidd[count] =
-                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
-               table->BapmVddcVidHiSidd[count] =
-                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
-               table->BapmVddcVidHiSidd2[count] =
-                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
-       }
-
-       if ((data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2)) {
-               /* We are populating vddgfx CAC data to BapmVddgfx table in split mode */
-               for (count = 0; count < vddgfxLevelCount; count++) {
-                       index = tonga_get_voltage_index(vddgfx_lookup_table,
-                               data->vddgfx_voltage_table.entries[count].value);
-                       table->BapmVddGfxVidLoSidd[count] =
-                               convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_low);
-                       table->BapmVddGfxVidHiSidd[count] =
-                               convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_mid);
-                       table->BapmVddGfxVidHiSidd2[count] =
-                               convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_high);
-               }
-       } else {
-               for (count = 0; count < vddcLevelCount; count++) {
-                       index = tonga_get_voltage_index(vddc_lookup_table,
-                               data->vddc_voltage_table.entries[count].value);
-                       table->BapmVddGfxVidLoSidd[count] =
-                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
-                       table->BapmVddGfxVidHiSidd[count] =
-                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
-                       table->BapmVddGfxVidHiSidd2[count] =
-                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
-               }
-       }
-
-       return 0;
-}
-
-
-/**
- * Preparation of voltage tables for SMC.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    table     the SMC DPM table structure to be populated
- * @return   always 0
- */
-
-int tonga_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
-       SMU72_Discrete_DpmTable *table)
-{
-       int result;
-
-       result = tonga_populate_smc_vddc_table(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate VDDC voltage table to SMC", return -1);
-
-       result = tonga_populate_smc_vdd_ci_table(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate VDDCI voltage table to SMC", return -1);
-
-       result = tonga_populate_smc_vdd_gfx_table(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate VDDGFX voltage table to SMC", return -1);
-
-       result = tonga_populate_smc_mvdd_table(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate MVDD voltage table to SMC", return -1);
-
-       result = tonga_populate_cac_tables(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-                       "can not populate CAC voltage tables to SMC", return -1);
-
-       return 0;
-}
-
-/**
- * Populates the SMC VRConfig field in DPM table.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    table     the SMC DPM table structure to be populated
- * @return   always 0
- */
-static int tonga_populate_vr_config(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint16_t config;
-
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
-               /*  Splitted mode */
-               config = VR_SVI2_PLANE_1;
-               table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
-
-               if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-                       config = VR_SVI2_PLANE_2;
-                       table->VRConfig |= config;
-               } else {
-                       printk(KERN_ERR "[ powerplay ] VDDC and VDDGFX should be both on SVI2 control in splitted mode! \n");
-               }
-       } else {
-               /* Merged mode  */
-               config = VR_MERGED_WITH_VDDC;
-               table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
-
-               /* Set Vddc Voltage Controller  */
-               if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
-                       config = VR_SVI2_PLANE_1;
-                       table->VRConfig |= config;
-               } else {
-                       printk(KERN_ERR "[ powerplay ] VDDC should be on SVI2 control in merged mode! \n");
-               }
-       }
-
-       /* Set Vddci Voltage Controller  */
-       if (TONGA_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_ci_control) {
-               config = VR_SVI2_PLANE_2;  /* only in merged mode */
-               table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
-       } else if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->vdd_ci_control) {
-               config = VR_SMIO_PATTERN_1;
-               table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
-       }
-
-       /* Set Mvdd Voltage Controller */
-       if (TONGA_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
-               config = VR_SMIO_PATTERN_2;
-               table->VRConfig |= (config<<VRCONF_MVDD_SHIFT);
-       }
-
-       return 0;
-}
-
-static int tonga_get_dependecy_volt_by_clk(struct pp_hwmgr *hwmgr,
-       phm_ppt_v1_clock_voltage_dependency_table *allowed_clock_voltage_table,
-       uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
-{
-       uint32_t i = 0;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       /* clock - voltage dependency table is empty table */
-       if (allowed_clock_voltage_table->count == 0)
-               return -1;
-
-       for (i = 0; i < allowed_clock_voltage_table->count; i++) {
-               /* find first sclk bigger than request */
-               if (allowed_clock_voltage_table->entries[i].clk >= clock) {
-                       voltage->VddGfx = tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-                                                               allowed_clock_voltage_table->entries[i].vddgfx);
-
-                       voltage->Vddc = tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-                                                               allowed_clock_voltage_table->entries[i].vddc);
-
-                       if (allowed_clock_voltage_table->entries[i].vddci) {
-                               voltage->Vddci = tonga_get_voltage_id(&data->vddci_voltage_table,
-                                                                       allowed_clock_voltage_table->entries[i].vddci);
-                       } else {
-                               voltage->Vddci = tonga_get_voltage_id(&data->vddci_voltage_table,
-                                                                       allowed_clock_voltage_table->entries[i].vddc - data->vddc_vddci_delta);
-                       }
-
-                       if (allowed_clock_voltage_table->entries[i].mvdd) {
-                               *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i].mvdd;
-                       }
-
-                       voltage->Phases = 1;
-                       return 0;
-               }
-       }
-
-       /* sclk is bigger than max sclk in the dependence table */
-       voltage->VddGfx = tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-               allowed_clock_voltage_table->entries[i-1].vddgfx);
-       voltage->Vddc = tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-               allowed_clock_voltage_table->entries[i-1].vddc);
-
-       if (allowed_clock_voltage_table->entries[i-1].vddci) {
-               voltage->Vddci = tonga_get_voltage_id(&data->vddci_voltage_table,
-                       allowed_clock_voltage_table->entries[i-1].vddci);
-       }
-       if (allowed_clock_voltage_table->entries[i-1].mvdd) {
-               *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i-1].mvdd;
-       }
-
-       return 0;
-}
-
-/**
- * Call SMC to reset S0/S1 to S1 and Reset SMIO to initial value
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_reset_to_default(struct pp_hwmgr *hwmgr)
-{
-       return (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_ResetToDefaults) == 0) ? 0 : 1;
-}
-
-int tonga_populate_memory_timing_parameters(
-               struct pp_hwmgr *hwmgr,
-               uint32_t engine_clock,
-               uint32_t memory_clock,
-               struct SMU72_Discrete_MCArbDramTimingTableEntry *arb_regs
-               )
-{
-       uint32_t dramTiming;
-       uint32_t dramTiming2;
-       uint32_t burstTime;
-       int result;
-
-       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
-                               engine_clock, memory_clock);
-
-       PP_ASSERT_WITH_CODE(result == 0,
-               "Error calling VBIOS to set DRAM_TIMING.", return result);
-
-       dramTiming  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-       dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-       burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
-
-       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dramTiming);
-       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
-       arb_regs->McArbBurstTime = (uint8_t)burstTime;
-
-       return 0;
-}
-
-/**
- * Setup parameters for the MC ARB.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- * This function is to be called from the SetPowerState table.
- */
-int tonga_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       int result = 0;
-       SMU72_Discrete_MCArbDramTimingTable  arb_regs;
-       uint32_t i, j;
-
-       memset(&arb_regs, 0x00, sizeof(SMU72_Discrete_MCArbDramTimingTable));
-
-       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
-               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
-                       result = tonga_populate_memory_timing_parameters
-                               (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
-                                data->dpm_table.mclk_table.dpm_levels[j].value,
-                                &arb_regs.entries[i][j]);
-
-                       if (0 != result) {
-                               break;
-                       }
-               }
-       }
-
-       if (0 == result) {
-               result = tonga_copy_bytes_to_smc(
-                               hwmgr->smumgr,
-                               data->arb_table_start,
-                               (uint8_t *)&arb_regs,
-                               sizeof(SMU72_Discrete_MCArbDramTimingTable),
-                               data->sram_end
-                               );
-       }
-
-       return result;
-}
-
-static int tonga_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU72_Discrete_DpmTable *table)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_dpm_table *dpm_table = &data->dpm_table;
-       uint32_t i;
-
-       /* Index (dpm_table->pcie_speed_table.count) is reserved for PCIE boot level. */
-       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
-               table->LinkLevel[i].PcieGenSpeed  =
-                       (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
-               table->LinkLevel[i].PcieLaneCount =
-                       (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
-               table->LinkLevel[i].EnabledForActivity =
-                       1;
-               table->LinkLevel[i].SPC =
-                       (uint8_t)(data->pcie_spc_cap & 0xff);
-               table->LinkLevel[i].DownThreshold =
-                       PP_HOST_TO_SMC_UL(5);
-               table->LinkLevel[i].UpThreshold =
-                       PP_HOST_TO_SMC_UL(30);
-       }
-
-       data->smc_state_table.LinkLevelCount =
-               (uint8_t)dpm_table->pcie_speed_table.count;
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
-               tonga_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
-
-       return 0;
-}
-
-static int tonga_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
-                                       SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-
-       uint8_t count;
-       pp_atomctrl_clock_dividers_vi dividers;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       table->UvdLevelCount = (uint8_t) (mm_table->count);
-       table->UvdBootLevel = 0;
-
-       for (count = 0; count < table->UvdLevelCount; count++) {
-               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
-               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
-               table->UvdLevel[count].MinVoltage.Vddc =
-                       tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-                                               mm_table->entries[count].vddc);
-               table->UvdLevel[count].MinVoltage.VddGfx =
-                       (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) ?
-                       tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-                                               mm_table->entries[count].vddgfx) : 0;
-               table->UvdLevel[count].MinVoltage.Vddci =
-                       tonga_get_voltage_id(&data->vddci_voltage_table,
-                                            mm_table->entries[count].vddc - data->vddc_vddci_delta);
-               table->UvdLevel[count].MinVoltage.Phases = 1;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                                                         table->UvdLevel[count].VclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                                   "can not find divide id for Vclk clock", return result);
-
-               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                                                         table->UvdLevel[count].DclkFrequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                                   "can not find divide id for Dclk clock", return result);
-
-               table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
-               //CONVERT_FROM_HOST_TO_SMC_UL((uint32_t)table->UvdLevel[count].MinVoltage);
-       }
-
-       return result;
-
-}
-
-static int tonga_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
-               SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-
-       uint8_t count;
-       pp_atomctrl_clock_dividers_vi dividers;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       table->VceLevelCount = (uint8_t) (mm_table->count);
-       table->VceBootLevel = 0;
-
-       for (count = 0; count < table->VceLevelCount; count++) {
-               table->VceLevel[count].Frequency =
-                       mm_table->entries[count].eclk;
-               table->VceLevel[count].MinVoltage.Vddc =
-                       tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-                               mm_table->entries[count].vddc);
-               table->VceLevel[count].MinVoltage.VddGfx =
-                       (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) ?
-                       tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-                               mm_table->entries[count].vddgfx) : 0;
-               table->VceLevel[count].MinVoltage.Vddci =
-                       tonga_get_voltage_id(&data->vddci_voltage_table,
-                               mm_table->entries[count].vddc - data->vddc_vddci_delta);
-               table->VceLevel[count].MinVoltage.Phases = 1;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                                       table->VceLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "can not find divide id for VCE engine clock", return result);
-
-               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
-       }
-
-       return result;
-}
-
-static int tonga_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
-               SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-       uint8_t count;
-       pp_atomctrl_clock_dividers_vi dividers;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       table->AcpLevelCount = (uint8_t) (mm_table->count);
-       table->AcpBootLevel = 0;
-
-       for (count = 0; count < table->AcpLevelCount; count++) {
-               table->AcpLevel[count].Frequency =
-                       pptable_info->mm_dep_table->entries[count].aclk;
-               table->AcpLevel[count].MinVoltage.Vddc =
-                       tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-                       mm_table->entries[count].vddc);
-               table->AcpLevel[count].MinVoltage.VddGfx =
-                       (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) ?
-                       tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-                               mm_table->entries[count].vddgfx) : 0;
-               table->AcpLevel[count].MinVoltage.Vddci =
-                       tonga_get_voltage_id(&data->vddci_voltage_table,
-                               mm_table->entries[count].vddc - data->vddc_vddci_delta);
-               table->AcpLevel[count].MinVoltage.Phases = 1;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                       table->AcpLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "can not find divide id for engine clock", return result);
-
-               table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
-       }
-
-       return result;
-}
-
-static int tonga_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
-       SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-       uint8_t count;
-       pp_atomctrl_clock_dividers_vi dividers;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       table->SamuBootLevel = 0;
-       table->SamuLevelCount = (uint8_t) (mm_table->count);
-
-       for (count = 0; count < table->SamuLevelCount; count++) {
-               /* not sure whether we need evclk or not */
-               table->SamuLevel[count].Frequency =
-                       pptable_info->mm_dep_table->entries[count].samclock;
-               table->SamuLevel[count].MinVoltage.Vddc =
-                       tonga_get_voltage_index(pptable_info->vddc_lookup_table,
-                               mm_table->entries[count].vddc);
-               table->SamuLevel[count].MinVoltage.VddGfx =
-                       (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) ?
-                       tonga_get_voltage_index(pptable_info->vddgfx_lookup_table,
-                               mm_table->entries[count].vddgfx) : 0;
-               table->SamuLevel[count].MinVoltage.Vddci =
-                       tonga_get_voltage_id(&data->vddci_voltage_table,
-                               mm_table->entries[count].vddc - data->vddc_vddci_delta);
-               table->SamuLevel[count].MinVoltage.Phases = 1;
-
-               /* retrieve divider value for VBIOS */
-               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
-                                       table->SamuLevel[count].Frequency, &dividers);
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "can not find divide id for samu clock", return result);
-
-               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
-
-               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
-       }
-
-       return result;
-}
-
-/**
- * Populates the SMC MCLK structure using the provided memory clock
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    memory_clock the memory clock to use to populate the structure
- * @param    sclk        the SMC SCLK structure to be populated
- */
-static int tonga_calculate_mclk_params(
-               struct pp_hwmgr *hwmgr,
-               uint32_t memory_clock,
-               SMU72_Discrete_MemoryLevel *mclk,
-               bool strobe_mode,
-               bool dllStateOn
-               )
-{
-       const tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       uint32_t  dll_cntl = data->clock_registers.vDLL_CNTL;
-       uint32_t  mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
-       uint32_t  mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
-       uint32_t  mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
-       uint32_t  mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
-       uint32_t  mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
-       uint32_t  mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
-       uint32_t  mpll_ss1 = data->clock_registers.vMPLL_SS1;
-       uint32_t  mpll_ss2 = data->clock_registers.vMPLL_SS2;
-
-       pp_atomctrl_memory_clock_param mpll_param;
-       int result;
-
-       result = atomctrl_get_memory_pll_dividers_si(hwmgr,
-                               memory_clock, &mpll_param, strobe_mode);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Error retrieving Memory Clock Parameters from VBIOS.", return result);
-
-       /* MPLL_FUNC_CNTL setup*/
-       mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL, mpll_param.bw_ctrl);
-
-       /* MPLL_FUNC_CNTL_1 setup*/
-       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
-                                                       MPLL_FUNC_CNTL_1, CLKF, mpll_param.mpll_fb_divider.cl_kf);
-       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
-                                                       MPLL_FUNC_CNTL_1, CLKFRAC, mpll_param.mpll_fb_divider.clk_frac);
-       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
-                                                       MPLL_FUNC_CNTL_1, VCO_MODE, mpll_param.vco_mode);
-
-       /* MPLL_AD_FUNC_CNTL setup*/
-       mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
-                                                       MPLL_AD_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
-
-       if (data->is_memory_GDDR5) {
-               /* MPLL_DQ_FUNC_CNTL setup*/
-               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
-                                                               MPLL_DQ_FUNC_CNTL, YCLK_SEL, mpll_param.yclk_sel);
-               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
-                                                               MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
-               /*
-                ************************************
-                Fref = Reference Frequency
-                NF = Feedback divider ratio
-                NR = Reference divider ratio
-                Fnom = Nominal VCO output frequency = Fref * NF / NR
-                Fs = Spreading Rate
-                D = Percentage down-spread / 2
-                Fint = Reference input frequency to PFD = Fref / NR
-                NS = Spreading rate divider ratio = int(Fint / (2 * Fs))
-                CLKS = NS - 1 = ISS_STEP_NUM[11:0]
-                NV = D * Fs / Fnom * 4 * ((Fnom/Fref * NR) ^ 2)
-                CLKV = 65536 * NV = ISS_STEP_SIZE[25:0]
-                *************************************
-                */
-               pp_atomctrl_internal_ss_info ss_info;
-               uint32_t freq_nom;
-               uint32_t tmp;
-               uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
-
-               /* for GDDR5 for all modes and DDR3 */
-               if (1 == mpll_param.qdr)
-                       freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
-               else
-                       freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
-
-               /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2  Note: S.I. reference_divider = 1*/
-               tmp = (freq_nom / reference_clock);
-               tmp = tmp * tmp;
-
-               if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
-                       /* ss_info.speed_spectrum_percentage -- in unit of 0.01% */
-                       /* ss.Info.speed_spectrum_rate -- in unit of khz */
-                       /* CLKS = reference_clock / (2 * speed_spectrum_rate * reference_divider) * 10 */
-                       /*     = reference_clock * 5 / speed_spectrum_rate */
-                       uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
-
-                       /* CLKV = 65536 * speed_spectrum_percentage / 2 * spreadSpecrumRate / freq_nom * 4 / 100000 * ((freq_nom / reference_clock) ^ 2) */
-                       /*     = 131 * speed_spectrum_percentage * speed_spectrum_rate / 100 * ((freq_nom / reference_clock) ^ 2) / freq_nom */
-                       uint32_t clkv =
-                               (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
-                                                       ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
-
-                       mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
-                       mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
-               }
-       }
-
-       /* MCLK_PWRMGT_CNTL setup */
-       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
-       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
-       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
-
-
-       /* Save the result data to outpupt memory level structure */
-       mclk->MclkFrequency   = memory_clock;
-       mclk->MpllFuncCntl    = mpll_func_cntl;
-       mclk->MpllFuncCntl_1  = mpll_func_cntl_1;
-       mclk->MpllFuncCntl_2  = mpll_func_cntl_2;
-       mclk->MpllAdFuncCntl  = mpll_ad_func_cntl;
-       mclk->MpllDqFuncCntl  = mpll_dq_func_cntl;
-       mclk->MclkPwrmgtCntl  = mclk_pwrmgt_cntl;
-       mclk->DllCntl         = dll_cntl;
-       mclk->MpllSs1         = mpll_ss1;
-       mclk->MpllSs2         = mpll_ss2;
-
-       return 0;
-}
-
-static uint8_t tonga_get_mclk_frequency_ratio(uint32_t memory_clock,
-               bool strobe_mode)
-{
-       uint8_t mc_para_index;
-
-       if (strobe_mode) {
-               if (memory_clock < 12500) {
-                       mc_para_index = 0x00;
-               } else if (memory_clock > 47500) {
-                       mc_para_index = 0x0f;
-               } else {
-                       mc_para_index = (uint8_t)((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 = (uint8_t)((memory_clock - 60000) / 5000);
-               }
-       }
-
-       return mc_para_index;
-}
-
-static uint8_t tonga_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
-{
-       uint8_t mc_para_index;
-
-       if (memory_clock < 10000) {
-               mc_para_index = 0;
-       } else if (memory_clock >= 80000) {
-               mc_para_index = 0x0f;
-       } else {
-               mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
-       }
-
-       return mc_para_index;
-}
-
-static int tonga_populate_single_memory_level(
-               struct pp_hwmgr *hwmgr,
-               uint32_t memory_clock,
-               SMU72_Discrete_MemoryLevel *memory_level
-               )
-{
-       uint32_t minMvdd = 0;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       int result = 0;
-       bool dllStateOn;
-       struct cgs_display_info info = {0};
-
-
-       if (NULL != pptable_info->vdd_dep_on_mclk) {
-               result = tonga_get_dependecy_volt_by_clk(hwmgr,
-                       pptable_info->vdd_dep_on_mclk, memory_clock, &memory_level->MinVoltage, &minMvdd);
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "can not find MinVddc voltage value from memory VDDC voltage dependency table", return result);
-       }
-
-       if (data->mvdd_control == TONGA_VOLTAGE_CONTROL_NONE) {
-               memory_level->MinMvdd = data->vbios_boot_state.mvdd_bootup_value;
-       } else {
-               memory_level->MinMvdd = minMvdd;
-       }
-       memory_level->EnabledForThrottle = 1;
-       memory_level->EnabledForActivity = 0;
-       memory_level->UpHyst = 0;
-       memory_level->DownHyst = 100;
-       memory_level->VoltageDownHyst = 0;
-
-       /* Indicates maximum activity level for this performance level.*/
-       memory_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
-       memory_level->StutterEnable = 0;
-       memory_level->StrobeEnable = 0;
-       memory_level->EdcReadEnable = 0;
-       memory_level->EdcWriteEnable = 0;
-       memory_level->RttEnable = 0;
-
-       /* default set to low watermark. Highest level will be set to high later.*/
-       memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-       data->display_timing.num_existing_displays = info.display_count;
-
-       if ((data->mclk_stutter_mode_threshold != 0) &&
-           (memory_clock <= data->mclk_stutter_mode_threshold) &&
-           (!data->is_uvd_enabled)
-           && (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL, STUTTER_ENABLE) & 0x1)
-           && (data->display_timing.num_existing_displays <= 2)
-           && (data->display_timing.num_existing_displays != 0))
-               memory_level->StutterEnable = 1;
-
-       /* decide strobe mode*/
-       memory_level->StrobeEnable = (data->mclk_strobe_mode_threshold != 0) &&
-               (memory_clock <= data->mclk_strobe_mode_threshold);
-
-       /* decide EDC mode and memory clock ratio*/
-       if (data->is_memory_GDDR5) {
-               memory_level->StrobeRatio = tonga_get_mclk_frequency_ratio(memory_clock,
-                                       memory_level->StrobeEnable);
-
-               if ((data->mclk_edc_enable_threshold != 0) &&
-                               (memory_clock > data->mclk_edc_enable_threshold)) {
-                       memory_level->EdcReadEnable = 1;
-               }
-
-               if ((data->mclk_edc_wr_enable_threshold != 0) &&
-                               (memory_clock > data->mclk_edc_wr_enable_threshold)) {
-                       memory_level->EdcWriteEnable = 1;
-               }
-
-               if (memory_level->StrobeEnable) {
-                       if (tonga_get_mclk_frequency_ratio(memory_clock, 1) >=
-                                       ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf)) {
-                               dllStateOn = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
-                       } else {
-                               dllStateOn = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
-                       }
-
-               } else {
-                       dllStateOn = data->dll_defaule_on;
-               }
-       } else {
-               memory_level->StrobeRatio =
-                       tonga_get_ddr3_mclk_frequency_ratio(memory_clock);
-               dllStateOn = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
-       }
-
-       result = tonga_calculate_mclk_params(hwmgr,
-               memory_clock, memory_level, memory_level->StrobeEnable, dllStateOn);
-
-       if (0 == result) {
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinMvdd);
-               /* MCLK frequency in units of 10KHz*/
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
-               /* Indicates maximum activity level for this performance level.*/
-               CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
-               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
-       }
-
-       return result;
-}
-
-/**
- * Populates the SMC MVDD structure using the provided memory clock.
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    mclk        the MCLK value to be used in the decision if MVDD should be high or low.
- * @param    voltage     the SMC VOLTAGE structure to be populated
- */
-int tonga_populate_mvdd_value(struct pp_hwmgr *hwmgr, uint32_t mclk, SMIO_Pattern *smio_pattern)
-{
-       const tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i = 0;
-
-       if (TONGA_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
-               /* find mvdd value which clock is more than request */
-               for (i = 0; i < pptable_info->vdd_dep_on_mclk->count; i++) {
-                       if (mclk <= pptable_info->vdd_dep_on_mclk->entries[i].clk) {
-                               /* Always round to higher voltage. */
-                               smio_pattern->Voltage = data->mvdd_voltage_table.entries[i].value;
-                               break;
-                       }
-               }
-
-               PP_ASSERT_WITH_CODE(i < pptable_info->vdd_dep_on_mclk->count,
-                       "MVDD Voltage is outside the supported range.", return -1);
-
-       } else {
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static int tonga_populate_smv_acpi_level(struct pp_hwmgr *hwmgr,
-       SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-       const tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       pp_atomctrl_clock_dividers_vi dividers;
-       SMIO_Pattern voltage_level;
-       uint32_t spll_func_cntl    = data->clock_registers.vCG_SPLL_FUNC_CNTL;
-       uint32_t spll_func_cntl_2  = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
-       uint32_t dll_cntl          = data->clock_registers.vDLL_CNTL;
-       uint32_t mclk_pwrmgt_cntl  = data->clock_registers.vMCLK_PWRMGT_CNTL;
-
-       /* The ACPI state should not do DPM on DC (or ever).*/
-       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
-
-       table->ACPILevel.MinVoltage = data->smc_state_table.GraphicsLevel[0].MinVoltage;
-
-       /* assign zero for now*/
-       table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
-
-       /* get the engine clock dividers for this clock value*/
-       result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
-               table->ACPILevel.SclkFrequency,  &dividers);
-
-       PP_ASSERT_WITH_CODE(result == 0,
-               "Error retrieving Engine Clock dividers from VBIOS.", return result);
-
-       /* divider ID for required SCLK*/
-       table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
-       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-       table->ACPILevel.DeepSleepDivId = 0;
-
-       spll_func_cntl      = PHM_SET_FIELD(spll_func_cntl,
-                                                       CG_SPLL_FUNC_CNTL,   SPLL_PWRON,     0);
-       spll_func_cntl      = PHM_SET_FIELD(spll_func_cntl,
-                                                       CG_SPLL_FUNC_CNTL,   SPLL_RESET,     1);
-       spll_func_cntl_2    = PHM_SET_FIELD(spll_func_cntl_2,
-                                                       CG_SPLL_FUNC_CNTL_2, SCLK_MUX_SEL,   4);
-
-       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
-       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
-       table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
-       table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
-       table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
-       table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
-       table->ACPILevel.CcPwrDynRm = 0;
-       table->ACPILevel.CcPwrDynRm1 = 0;
-
-
-       /* For various features to be enabled/disabled while this level is active.*/
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
-       /* SCLK frequency in units of 10KHz*/
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
-
-       /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
-       table->MemoryACPILevel.MinVoltage = data->smc_state_table.MemoryLevel[0].MinVoltage;
-
-       /*  CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);*/
-
-       if (0 == tonga_populate_mvdd_value(hwmgr, 0, &voltage_level))
-               table->MemoryACPILevel.MinMvdd =
-                       PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
-       else
-               table->MemoryACPILevel.MinMvdd = 0;
-
-       /* Force reset on DLL*/
-       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
-       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
-
-       /* Disable DLL in ACPIState*/
-       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
-       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
-               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
-
-       /* Enable DLL bypass signal*/
-       dll_cntl            = PHM_SET_FIELD(dll_cntl,
-               DLL_CNTL, MRDCK0_BYPASS, 0);
-       dll_cntl            = PHM_SET_FIELD(dll_cntl,
-               DLL_CNTL, MRDCK1_BYPASS, 0);
-
-       table->MemoryACPILevel.DllCntl            =
-               PP_HOST_TO_SMC_UL(dll_cntl);
-       table->MemoryACPILevel.MclkPwrmgtCntl     =
-               PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
-       table->MemoryACPILevel.MpllAdFuncCntl     =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
-       table->MemoryACPILevel.MpllDqFuncCntl     =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
-       table->MemoryACPILevel.MpllFuncCntl       =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
-       table->MemoryACPILevel.MpllFuncCntl_1     =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
-       table->MemoryACPILevel.MpllFuncCntl_2     =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
-       table->MemoryACPILevel.MpllSs1            =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
-       table->MemoryACPILevel.MpllSs2            =
-               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
-
-       table->MemoryACPILevel.EnabledForThrottle = 0;
-       table->MemoryACPILevel.EnabledForActivity = 0;
-       table->MemoryACPILevel.UpHyst = 0;
-       table->MemoryACPILevel.DownHyst = 100;
-       table->MemoryACPILevel.VoltageDownHyst = 0;
-       /* Indicates maximum activity level for this performance level.*/
-       table->MemoryACPILevel.ActivityLevel = PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
-
-       table->MemoryACPILevel.StutterEnable = 0;
-       table->MemoryACPILevel.StrobeEnable = 0;
-       table->MemoryACPILevel.EdcReadEnable = 0;
-       table->MemoryACPILevel.EdcWriteEnable = 0;
-       table->MemoryACPILevel.RttEnable = 0;
-
-       return result;
-}
-
-static int tonga_find_boot_level(struct tonga_single_dpm_table *table, uint32_t value, uint32_t *boot_level)
-{
-       int result = 0;
-       uint32_t i;
-
-       for (i = 0; i < table->count; i++) {
-               if (value == table->dpm_levels[i].value) {
-                       *boot_level = i;
-                       result = 0;
-               }
-       }
-       return result;
-}
-
-static int tonga_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
-                       SMU72_Discrete_DpmTable *table)
-{
-       int result = 0;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       table->GraphicsBootLevel  = 0;        /* 0 == DPM[0] (low), etc. */
-       table->MemoryBootLevel    = 0;        /* 0 == DPM[0] (low), etc. */
-
-       /* find boot level from dpm table*/
-       result = tonga_find_boot_level(&(data->dpm_table.sclk_table),
-       data->vbios_boot_state.sclk_bootup_value,
-       (uint32_t *)&(data->smc_state_table.GraphicsBootLevel));
-
-       if (0 != result) {
-               data->smc_state_table.GraphicsBootLevel = 0;
-               printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \
-                       in dependency table. Using Graphics DPM level 0!");
-               result = 0;
-       }
-
-       result = tonga_find_boot_level(&(data->dpm_table.mclk_table),
-               data->vbios_boot_state.mclk_bootup_value,
-               (uint32_t *)&(data->smc_state_table.MemoryBootLevel));
-
-       if (0 != result) {
-               data->smc_state_table.MemoryBootLevel = 0;
-               printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \
-                       in dependency table. Using Memory DPM level 0!");
-               result = 0;
-       }
-
-       table->BootVoltage.Vddc =
-               tonga_get_voltage_id(&(data->vddc_voltage_table),
-                       data->vbios_boot_state.vddc_bootup_value);
-       table->BootVoltage.VddGfx =
-               tonga_get_voltage_id(&(data->vddgfx_voltage_table),
-                       data->vbios_boot_state.vddgfx_bootup_value);
-       table->BootVoltage.Vddci =
-               tonga_get_voltage_id(&(data->vddci_voltage_table),
-                       data->vbios_boot_state.vddci_bootup_value);
-       table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
-
-       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
-
-       return result;
-}
-
-
-/**
- * Calculates the SCLK dividers using the provided engine clock
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    engine_clock the engine clock to use to populate the structure
- * @param    sclk        the SMC SCLK structure to be populated
- */
-int tonga_calculate_sclk_params(struct pp_hwmgr *hwmgr,
-               uint32_t engine_clock, SMU72_Discrete_GraphicsLevel *sclk)
-{
-       const tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       pp_atomctrl_clock_dividers_vi dividers;
-       uint32_t spll_func_cntl            = data->clock_registers.vCG_SPLL_FUNC_CNTL;
-       uint32_t spll_func_cntl_3          = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
-       uint32_t spll_func_cntl_4          = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
-       uint32_t cg_spll_spread_spectrum   = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
-       uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
-       uint32_t    reference_clock;
-       uint32_t reference_divider;
-       uint32_t fbdiv;
-       int result;
-
-       /* get the engine clock dividers for this clock value*/
-       result = atomctrl_get_engine_pll_dividers_vi(hwmgr, engine_clock,  &dividers);
-
-       PP_ASSERT_WITH_CODE(result == 0,
-               "Error retrieving Engine Clock dividers from VBIOS.", return result);
-
-       /* To get FBDIV we need to multiply this by 16384 and divide it by Fref.*/
-       reference_clock = atomctrl_get_reference_clock(hwmgr);
-
-       reference_divider = 1 + dividers.uc_pll_ref_div;
-
-       /* low 14 bits is fraction and high 12 bits is divider*/
-       fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
-
-       /* SPLL_FUNC_CNTL setup*/
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
-               CG_SPLL_FUNC_CNTL, SPLL_REF_DIV, dividers.uc_pll_ref_div);
-       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
-               CG_SPLL_FUNC_CNTL, SPLL_PDIV_A,  dividers.uc_pll_post_div);
-
-       /* SPLL_FUNC_CNTL_3 setup*/
-       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
-               CG_SPLL_FUNC_CNTL_3, SPLL_FB_DIV, fbdiv);
-
-       /* set to use fractional accumulation*/
-       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
-               CG_SPLL_FUNC_CNTL_3, SPLL_DITHEN, 1);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
-               pp_atomctrl_internal_ss_info ss_info;
-
-               uint32_t vcoFreq = engine_clock * dividers.uc_pll_post_div;
-               if (0 == atomctrl_get_engine_clock_spread_spectrum(hwmgr, vcoFreq, &ss_info)) {
-                       /*
-                       * ss_info.speed_spectrum_percentage -- in unit of 0.01%
-                       * ss_info.speed_spectrum_rate -- in unit of khz
-                       */
-                       /* clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2 */
-                       uint32_t clkS = reference_clock * 5 / (reference_divider * ss_info.speed_spectrum_rate);
-
-                       /* clkv = 2 * D * fbdiv / NS */
-                       uint32_t clkV = 4 * ss_info.speed_spectrum_percentage * fbdiv / (clkS * 10000);
-
-                       cg_spll_spread_spectrum =
-                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, CLKS, clkS);
-                       cg_spll_spread_spectrum =
-                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
-                       cg_spll_spread_spectrum_2 =
-                               PHM_SET_FIELD(cg_spll_spread_spectrum_2, CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clkV);
-               }
-       }
-
-       sclk->SclkFrequency        = engine_clock;
-       sclk->CgSpllFuncCntl3      = spll_func_cntl_3;
-       sclk->CgSpllFuncCntl4      = spll_func_cntl_4;
-       sclk->SpllSpreadSpectrum   = cg_spll_spread_spectrum;
-       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
-       sclk->SclkDid              = (uint8_t)dividers.pll_post_divider;
-
-       return 0;
-}
-
-static uint8_t tonga_get_sleep_divider_id_from_clock(uint32_t engine_clock,
-               uint32_t min_engine_clock_in_sr)
-{
-       uint32_t i, temp;
-       uint32_t min = max(min_engine_clock_in_sr, (uint32_t)TONGA_MINIMUM_ENGINE_CLOCK);
-
-       PP_ASSERT_WITH_CODE((engine_clock >= min),
-                       "Engine clock can't satisfy stutter requirement!", return 0);
-
-       for (i = TONGA_MAX_DEEPSLEEP_DIVIDER_ID;; i--) {
-               temp = engine_clock >> i;
-
-               if(temp >= min || i == 0)
-                       break;
-       }
-       return (uint8_t)i;
-}
-
-/**
- * Populates single SMC SCLK structure using the provided engine clock
- *
- * @param    hwmgr      the address of the hardware manager
- * @param    engine_clock the engine clock to use to populate the structure
- * @param    sclk        the SMC SCLK structure to be populated
- */
-static int tonga_populate_single_graphic_level(struct pp_hwmgr *hwmgr, uint32_t engine_clock, uint16_t sclk_activity_level_threshold, SMU72_Discrete_GraphicsLevel *graphic_level)
-{
-       int result;
-       uint32_t threshold;
-       uint32_t mvdd;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       result = tonga_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
-
-
-       /* populate graphics levels*/
-       result = tonga_get_dependecy_volt_by_clk(hwmgr,
-               pptable_info->vdd_dep_on_sclk, engine_clock,
-               &graphic_level->MinVoltage, &mvdd);
-       PP_ASSERT_WITH_CODE((0 == result),
-               "can not find VDDC voltage value for VDDC       \
-               engine clock dependency table", return result);
-
-       /* SCLK frequency in units of 10KHz*/
-       graphic_level->SclkFrequency = engine_clock;
-
-       /* Indicates maximum activity level for this performance level. 50% for now*/
-       graphic_level->ActivityLevel = sclk_activity_level_threshold;
-
-       graphic_level->CcPwrDynRm = 0;
-       graphic_level->CcPwrDynRm1 = 0;
-       /* this level can be used if activity is high enough.*/
-       graphic_level->EnabledForActivity = 0;
-       /* this level can be used for throttling.*/
-       graphic_level->EnabledForThrottle = 1;
-       graphic_level->UpHyst = 0;
-       graphic_level->DownHyst = 0;
-       graphic_level->VoltageDownHyst = 0;
-       graphic_level->PowerThrottle = 0;
-
-       threshold = engine_clock * data->fast_watemark_threshold / 100;
-/*
-       *get the DAL clock. do it in funture.
-       PECI_GetMinClockSettings(hwmgr->peci, &minClocks);
-       data->display_timing.min_clock_insr = minClocks.engineClockInSR;
-*/
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_SclkDeepSleep))
-               graphic_level->DeepSleepDivId =
-                               tonga_get_sleep_divider_id_from_clock(engine_clock,
-                                               data->display_timing.min_clock_insr);
-
-       /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
-       graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
-
-       if (0 == result) {
-               /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVoltage);*/
-               /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVddcPhases);*/
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SclkFrequency);
-               CONVERT_FROM_HOST_TO_SMC_US(graphic_level->ActivityLevel);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl3);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl4);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum2);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm);
-               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm1);
-       }
-
-       return result;
-}
-
-/**
- * Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
- *
- * @param    hwmgr      the address of the hardware manager
- */
-static int tonga_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       struct tonga_dpm_table *dpm_table = &data->dpm_table;
-       phm_ppt_v1_pcie_table *pcie_table = pptable_info->pcie_table;
-       uint8_t pcie_entry_count = (uint8_t) data->dpm_table.pcie_speed_table.count;
-       int result = 0;
-       uint32_t level_array_adress = data->dpm_table_start +
-               offsetof(SMU72_Discrete_DpmTable, GraphicsLevel);
-       uint32_t level_array_size = sizeof(SMU72_Discrete_GraphicsLevel) *
-               SMU72_MAX_LEVELS_GRAPHICS;   /* 64 -> long; 32 -> int*/
-       SMU72_Discrete_GraphicsLevel *levels = data->smc_state_table.GraphicsLevel;
-       uint32_t i, maxEntry;
-       uint8_t highest_pcie_level_enabled = 0, lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0, count = 0;
-       PECI_RegistryValue reg_value;
-       memset(levels, 0x00, level_array_size);
-
-       for (i = 0; i < dpm_table->sclk_table.count; i++) {
-               result = tonga_populate_single_graphic_level(hwmgr,
-                                       dpm_table->sclk_table.dpm_levels[i].value,
-                                       (uint16_t)data->activity_target[i],
-                                       &(data->smc_state_table.GraphicsLevel[i]));
-
-               if (0 != result)
-                       return result;
-
-               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
-               if (i > 1)
-                       data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
-
-               if (0 == i) {
-                       reg_value = 0;
-                       if (reg_value != 0)
-                               data->smc_state_table.GraphicsLevel[0].UpHyst = (uint8_t)reg_value;
-               }
-
-               if (1 == i) {
-                       reg_value = 0;
-                       if (reg_value != 0)
-                               data->smc_state_table.GraphicsLevel[1].UpHyst = (uint8_t)reg_value;
-               }
-       }
-
-       /* Only enable level 0 for now. */
-       data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
-
-       /* set highest level watermark to high */
-       if (dpm_table->sclk_table.count > 1)
-               data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
-                       PPSMC_DISPLAY_WATERMARK_HIGH;
-
-       data->smc_state_table.GraphicsDpmLevelCount =
-               (uint8_t)dpm_table->sclk_table.count;
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
-               tonga_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
-
-       if (pcie_table != NULL) {
-               PP_ASSERT_WITH_CODE((pcie_entry_count >= 1),
-                       "There must be 1 or more PCIE levels defined in PPTable.", return -1);
-               maxEntry = pcie_entry_count - 1; /* for indexing, we need to decrement by 1.*/
-               for (i = 0; i < dpm_table->sclk_table.count; i++) {
-                       data->smc_state_table.GraphicsLevel[i].pcieDpmLevel =
-                               (uint8_t) ((i < maxEntry) ? i : maxEntry);
-               }
-       } else {
-               if (0 == data->dpm_level_enable_mask.pcie_dpm_enable_mask)
-                       printk(KERN_ERR "[ powerplay ] Pcie Dpm Enablemask is 0!");
-
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                       (1<<(highest_pcie_level_enabled+1))) != 0)) {
-                       highest_pcie_level_enabled++;
-               }
-
-               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                       (1<<lowest_pcie_level_enabled)) == 0)) {
-                       lowest_pcie_level_enabled++;
-               }
-
-               while ((count < highest_pcie_level_enabled) &&
-                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
-                                       (1<<(lowest_pcie_level_enabled+1+count))) == 0)) {
-                       count++;
-               }
-               mid_pcie_level_enabled = (lowest_pcie_level_enabled+1+count) < highest_pcie_level_enabled ?
-                       (lowest_pcie_level_enabled+1+count) : highest_pcie_level_enabled;
-
-
-               /* set pcieDpmLevel to highest_pcie_level_enabled*/
-               for (i = 2; i < dpm_table->sclk_table.count; i++) {
-                       data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
-               }
-
-               /* set pcieDpmLevel to lowest_pcie_level_enabled*/
-               data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
-
-               /* set pcieDpmLevel to mid_pcie_level_enabled*/
-               data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
-       }
-       /* level count will send to smc once at init smc table and never change*/
-       result = tonga_copy_bytes_to_smc(hwmgr->smumgr, level_array_adress, (uint8_t *)levels, (uint32_t)level_array_size, data->sram_end);
-
-       if (0 != result)
-               return result;
-
-       return 0;
-}
-
-/**
- * Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
- *
- * @param    hwmgr      the address of the hardware manager
- */
-
-static int tonga_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_dpm_table *dpm_table = &data->dpm_table;
-       int result;
-       /* populate MCLK dpm table to SMU7 */
-       uint32_t level_array_adress = data->dpm_table_start + offsetof(SMU72_Discrete_DpmTable, MemoryLevel);
-       uint32_t level_array_size = sizeof(SMU72_Discrete_MemoryLevel) * SMU72_MAX_LEVELS_MEMORY;
-       SMU72_Discrete_MemoryLevel *levels = data->smc_state_table.MemoryLevel;
-       uint32_t i;
-
-       memset(levels, 0x00, level_array_size);
-
-       for (i = 0; i < dpm_table->mclk_table.count; i++) {
-               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
-                       "can not populate memory level as memory clock is zero", return -1);
-               result = tonga_populate_single_memory_level(hwmgr, dpm_table->mclk_table.dpm_levels[i].value,
-                       &(data->smc_state_table.MemoryLevel[i]));
-               if (0 != result) {
-                       return result;
-               }
-       }
-
-       /* Only enable level 0 for now.*/
-       data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
-
-       /*
-       * in order to prevent MC activity from stutter mode to push DPM up.
-       * the UVD change complements this by putting the MCLK in a higher state
-       * by default such that we are not effected by up threshold or and MCLK DPM latency.
-       */
-       data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
-       CONVERT_FROM_HOST_TO_SMC_US(data->smc_state_table.MemoryLevel[0].ActivityLevel);
-
-       data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask = tonga_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
-       /* set highest level watermark to high*/
-       data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
-
-       /* level count will send to smc once at init smc table and never change*/
-       result = tonga_copy_bytes_to_smc(hwmgr->smumgr,
-               level_array_adress, (uint8_t *)levels, (uint32_t)level_array_size, data->sram_end);
-
-       if (0 != result) {
-               return result;
-       }
-
-       return 0;
-}
-
-struct TONGA_DLL_SPEED_SETTING {
-       uint16_t            Min;                          /* Minimum Data Rate*/
-       uint16_t            Max;                          /* Maximum Data Rate*/
-       uint32_t                        dll_speed;                     /* The desired DLL_SPEED setting*/
-};
-
-static int tonga_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
-{
-       return 0;
-}
-
-/* ---------------------------------------- ULV related functions ----------------------------------------------------*/
-
-
-static int tonga_reset_single_dpm_table(
-       struct pp_hwmgr *hwmgr,
-       struct tonga_single_dpm_table *dpm_table,
-       uint32_t count)
-{
-       uint32_t i;
-       if (!(count <= MAX_REGULAR_DPM_NUMBER))
-               printk(KERN_ERR "[ powerplay ] Fatal error, can not set up single DPM \
-                       table entries to exceed max number! \n");
-
-       dpm_table->count = count;
-       for (i = 0; i < MAX_REGULAR_DPM_NUMBER; i++) {
-               dpm_table->dpm_levels[i].enabled = false;
-       }
-
-       return 0;
-}
-
-static void tonga_setup_pcie_table_entry(
-       struct tonga_single_dpm_table *dpm_table,
-       uint32_t index, uint32_t pcie_gen,
-       uint32_t pcie_lanes)
-{
-       dpm_table->dpm_levels[index].value = pcie_gen;
-       dpm_table->dpm_levels[index].param1 = pcie_lanes;
-       dpm_table->dpm_levels[index].enabled = true;
-}
-
-static int tonga_setup_default_pcie_tables(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_pcie_table *pcie_table = pptable_info->pcie_table;
-       uint32_t i, maxEntry;
-
-       if (data->use_pcie_performance_levels && !data->use_pcie_power_saving_levels) {
-               data->pcie_gen_power_saving = data->pcie_gen_performance;
-               data->pcie_lane_power_saving = data->pcie_lane_performance;
-       } else if (!data->use_pcie_performance_levels && data->use_pcie_power_saving_levels) {
-               data->pcie_gen_performance = data->pcie_gen_power_saving;
-               data->pcie_lane_performance = data->pcie_lane_power_saving;
-       }
-
-       tonga_reset_single_dpm_table(hwmgr, &data->dpm_table.pcie_speed_table, SMU72_MAX_LEVELS_LINK);
-
-       if (pcie_table != NULL) {
-               /*
-               * maxEntry is used to make sure we reserve one PCIE level for boot level (fix for A+A PSPP issue).
-               * If PCIE table from PPTable have ULV entry + 8 entries, then ignore the last entry.
-               */
-               maxEntry = (SMU72_MAX_LEVELS_LINK < pcie_table->count) ?
-                                               SMU72_MAX_LEVELS_LINK : pcie_table->count;
-               for (i = 1; i < maxEntry; i++) {
-                       tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, i-1,
-                               get_pcie_gen_support(data->pcie_gen_cap, pcie_table->entries[i].gen_speed),
-                               get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               }
-               data->dpm_table.pcie_speed_table.count = maxEntry - 1;
-       } else {
-               /* Hardcode Pcie Table */
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 0,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Min_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 1,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Min_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 2,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Max_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 3,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Max_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 4,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Max_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table, 5,
-                       get_pcie_gen_support(data->pcie_gen_cap, PP_Max_PCIEGen),
-                       get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-               data->dpm_table.pcie_speed_table.count = 6;
-       }
-       /* Populate last level for boot PCIE level, but do not increment count. */
-       tonga_setup_pcie_table_entry(&data->dpm_table.pcie_speed_table,
-               data->dpm_table.pcie_speed_table.count,
-               get_pcie_gen_support(data->pcie_gen_cap, PP_Min_PCIEGen),
-               get_pcie_lane_support(data->pcie_lane_cap, PP_Max_PCIELane));
-
-       return 0;
-
-}
-
-/*
- * This function is to initalize all DPM state tables for SMU7 based on the dependency table.
- * Dynamic state patching function will then trim these state tables to the allowed range based
- * on the power policy or external client requests, such as UVD request, etc.
- */
-static int tonga_setup_default_dpm_tables(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint32_t i;
-
-       phm_ppt_v1_clock_voltage_dependency_table *allowed_vdd_sclk_table =
-               pptable_info->vdd_dep_on_sclk;
-       phm_ppt_v1_clock_voltage_dependency_table *allowed_vdd_mclk_table =
-               pptable_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table != NULL,
-               "SCLK dependency table is missing. This table is mandatory", return -1);
-       PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table->count >= 1,
-               "SCLK dependency table has to have is missing. This table is mandatory", return -1);
-
-       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
-               "MCLK dependency table is missing. This table is mandatory", return -1);
-       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table->count >= 1,
-               "VMCLK dependency table has to have is missing. This table is mandatory", return -1);
-
-       /* clear the state table to reset everything to default */
-       memset(&(data->dpm_table), 0x00, sizeof(data->dpm_table));
-       tonga_reset_single_dpm_table(hwmgr, &data->dpm_table.sclk_table, SMU72_MAX_LEVELS_GRAPHICS);
-       tonga_reset_single_dpm_table(hwmgr, &data->dpm_table.mclk_table, SMU72_MAX_LEVELS_MEMORY);
-       /* tonga_reset_single_dpm_table(hwmgr, &tonga_hwmgr->dpm_table.VddcTable, SMU72_MAX_LEVELS_VDDC); */
-       /* tonga_reset_single_dpm_table(hwmgr, &tonga_hwmgr->dpm_table.vdd_gfx_table, SMU72_MAX_LEVELS_VDDGFX);*/
-       /* tonga_reset_single_dpm_table(hwmgr, &tonga_hwmgr->dpm_table.vdd_ci_table, SMU72_MAX_LEVELS_VDDCI);*/
-       /* tonga_reset_single_dpm_table(hwmgr, &tonga_hwmgr->dpm_table.mvdd_table, SMU72_MAX_LEVELS_MVDD);*/
-
-       PP_ASSERT_WITH_CODE(allowed_vdd_sclk_table != NULL,
-               "SCLK dependency table is missing. This table is mandatory", return -1);
-       /* Initialize Sclk DPM table based on allow Sclk values*/
-       data->dpm_table.sclk_table.count = 0;
-
-       for (i = 0; i < allowed_vdd_sclk_table->count; i++) {
-               if (i == 0 || data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count-1].value !=
-                               allowed_vdd_sclk_table->entries[i].clk) {
-                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].value =
-                               allowed_vdd_sclk_table->entries[i].clk;
-                       data->dpm_table.sclk_table.dpm_levels[data->dpm_table.sclk_table.count].enabled = true; /*(i==0) ? 1 : 0; to do */
-                       data->dpm_table.sclk_table.count++;
-               }
-       }
-
-       PP_ASSERT_WITH_CODE(allowed_vdd_mclk_table != NULL,
-               "MCLK dependency table is missing. This table is mandatory", return -1);
-       /* Initialize Mclk DPM table based on allow Mclk values */
-       data->dpm_table.mclk_table.count = 0;
-       for (i = 0; i < allowed_vdd_mclk_table->count; i++) {
-               if (i == 0 || data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count-1].value !=
-                       allowed_vdd_mclk_table->entries[i].clk) {
-                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].value =
-                               allowed_vdd_mclk_table->entries[i].clk;
-                       data->dpm_table.mclk_table.dpm_levels[data->dpm_table.mclk_table.count].enabled = true; /*(i==0) ? 1 : 0; */
-                       data->dpm_table.mclk_table.count++;
-               }
-       }
-
-       /* setup PCIE gen speed levels*/
-       tonga_setup_default_pcie_tables(hwmgr);
-
-       /* save a copy of the default DPM table*/
-       memcpy(&(data->golden_dpm_table), &(data->dpm_table), sizeof(struct tonga_dpm_table));
-
-       return 0;
-}
-
-int tonga_populate_smc_initial_state(struct pp_hwmgr *hwmgr,
-               const struct tonga_power_state *bootState)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       uint8_t count, level;
-
-       count = (uint8_t) (pptable_info->vdd_dep_on_sclk->count);
-       for (level = 0; level < count; level++) {
-               if (pptable_info->vdd_dep_on_sclk->entries[level].clk >=
-                       bootState->performance_levels[0].engine_clock) {
-                       data->smc_state_table.GraphicsBootLevel = level;
-                       break;
-               }
-       }
-
-       count = (uint8_t) (pptable_info->vdd_dep_on_mclk->count);
-       for (level = 0; level < count; level++) {
-               if (pptable_info->vdd_dep_on_mclk->entries[level].clk >=
-                       bootState->performance_levels[0].memory_clock) {
-                       data->smc_state_table.MemoryBootLevel = level;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-/**
- * Initializes the SMC table and uploads it
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @param    pInput  the pointer to input data (PowerState)
- * @return   always 0
- */
-int tonga_init_smc_table(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       SMU72_Discrete_DpmTable  *table = &(data->smc_state_table);
-       const phw_tonga_ulv_parm *ulv = &(data->ulv);
-       uint8_t i;
-       PECI_RegistryValue reg_value;
-       pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
-
-       result = tonga_setup_default_dpm_tables(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to setup default DPM tables!", return result;);
-       memset(&(data->smc_state_table), 0x00, sizeof(data->smc_state_table));
-       if (TONGA_VOLTAGE_CONTROL_NONE != data->voltage_control) {
-               tonga_populate_smc_voltage_tables(hwmgr, table);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition)) {
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_StepVddc)) {
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
-       }
-
-       if (data->is_memory_GDDR5) {
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
-       }
-
-       i = PHM_READ_FIELD(hwmgr->device, CC_MC_MAX_CHANNEL, NOOFCHAN);
-
-       if (i == 1 || i == 0) {
-               table->SystemFlags |= PPSMC_SYSTEMFLAG_12CHANNEL;
-       }
-
-       if (ulv->ulv_supported && pptable_info->us_ulv_voltage_offset) {
-               PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to initialize ULV state!", return result;);
-
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCG_ULV_PARAMETER, ulv->ch_ulv_parameter);
-       }
-
-       result = tonga_populate_smc_link_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize Link Level!", return result;);
-
-       result = tonga_populate_all_graphic_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize Graphics Level!", return result;);
-
-       result = tonga_populate_all_memory_levels(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize Memory Level!", return result;);
-
-       result = tonga_populate_smv_acpi_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize ACPI Level!", return result;);
-
-       result = tonga_populate_smc_vce_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize VCE Level!", return result;);
-
-       result = tonga_populate_smc_acp_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize ACP Level!", return result;);
-
-       result = tonga_populate_smc_samu_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize SAMU Level!", return result;);
-
-       /* Since only the initial state is completely set up at this point (the other states are just copies of the boot state) we only */
-       /* need to populate the  ARB settings for the initial state. */
-       result = tonga_program_memory_timing_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to Write ARB settings for the initial state.", return result;);
-
-       result = tonga_populate_smc_uvd_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize UVD Level!", return result;);
-
-       result = tonga_populate_smc_boot_level(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize Boot Level!", return result;);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ClockStretcher)) {
-               result = tonga_populate_clock_stretcher_data_table(hwmgr);
-               PP_ASSERT_WITH_CODE(0 == result,
-                       "Failed to populate Clock Stretcher Data Table!", return result;);
-       }
-       table->GraphicsVoltageChangeEnable  = 1;
-       table->GraphicsThermThrottleEnable  = 1;
-       table->GraphicsInterval = 1;
-       table->VoltageInterval  = 1;
-       table->ThermalInterval  = 1;
-       table->TemperatureLimitHigh =
-               pptable_info->cac_dtp_table->usTargetOperatingTemp *
-               TONGA_Q88_FORMAT_CONVERSION_UNIT;
-       table->TemperatureLimitLow =
-               (pptable_info->cac_dtp_table->usTargetOperatingTemp - 1) *
-               TONGA_Q88_FORMAT_CONVERSION_UNIT;
-       table->MemoryVoltageChangeEnable  = 1;
-       table->MemoryInterval  = 1;
-       table->VoltageResponseTime  = 0;
-       table->PhaseResponseTime  = 0;
-       table->MemoryThermThrottleEnable  = 1;
-
-       /*
-       * Cail reads current link status and reports it as cap (we cannot change this due to some previous issues we had)
-       * SMC drops the link status to lowest level after enabling DPM by PowerPlay. After pnp or toggling CF, driver gets reloaded again
-       * but this time Cail reads current link status which was set to low by SMC and reports it as cap to powerplay
-       * To avoid it, we set PCIeBootLinkLevel to highest dpm level
-       */
-       PP_ASSERT_WITH_CODE((1 <= data->dpm_table.pcie_speed_table.count),
-                       "There must be 1 or more PCIE levels defined in PPTable.",
-                       return -1);
-
-       table->PCIeBootLinkLevel = (uint8_t) (data->dpm_table.pcie_speed_table.count);
-
-       table->PCIeGenInterval  = 1;
-
-       result = tonga_populate_vr_config(hwmgr, table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to populate VRConfig setting!", return result);
-
-       table->ThermGpio  = 17;
-       table->SclkStepSize = 0x4000;
-
-       reg_value = 0;
-       if ((0 == reg_value) &&
-               (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID,
-                                               &gpio_pin_assignment))) {
-               table->VRHotGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_RegulatorHot);
-       } else {
-               table->VRHotGpio = TONGA_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_RegulatorHot);
-       }
-
-       /* ACDC Switch GPIO */
-       reg_value = 0;
-       if ((0 == reg_value) &&
-               (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
-                                               &gpio_pin_assignment))) {
-               table->AcDcGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition);
-       } else {
-               table->AcDcGpio = TONGA_UNUSED_GPIO_PIN;
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition);
-       }
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_Falcon_QuickTransition);
-
-       reg_value = 0;
-       if (1 == reg_value) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_AutomaticDCTransition);
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_Falcon_QuickTransition);
-       }
-
-       reg_value = 0;
-       if ((0 == reg_value) && (atomctrl_get_pp_assign_pin(hwmgr,
-                       THERMAL_INT_OUTPUT_GPIO_PINID, &gpio_pin_assignment))) {
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalOutGPIO);
-
-               table->ThermOutGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
-
-               table->ThermOutPolarity =
-                       (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
-                       (1 << gpio_pin_assignment.uc_gpio_pin_bit_shift))) ? 1:0;
-
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
-
-               /* if required, combine VRHot/PCC with thermal out GPIO*/
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_RegulatorHot) &&
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_CombinePCCWithThermalSignal)){
-                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
-               }
-       } else {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ThermalOutGPIO);
-
-               table->ThermOutGpio = 17;
-               table->ThermOutPolarity = 1;
-               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
-       }
-
-       for (i = 0; i < SMU72_MAX_ENTRIES_SMIO; i++) {
-               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
-       }
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
-       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
-       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
-       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
-       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
-
-       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
-       result = tonga_copy_bytes_to_smc(hwmgr->smumgr, data->dpm_table_start +
-                                                                               offsetof(SMU72_Discrete_DpmTable, SystemFlags),
-                                                                               (uint8_t *)&(table->SystemFlags),
-                                                                               sizeof(SMU72_Discrete_DpmTable)-3 * sizeof(SMU72_PIDController),
-                                                                               data->sram_end);
-
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to upload dpm data to SMC memory!", return result;);
-
-       return result;
-}
-
-/* Look up the voltaged based on DAL's requested level. and then send the requested VDDC voltage to SMC*/
-static void tonga_apply_dal_minimum_voltage_request(struct pp_hwmgr *hwmgr)
-{
-       return;
-}
-
-int tonga_upload_dpm_level_enable_mask(struct pp_hwmgr *hwmgr)
-{
-       PPSMC_Result result;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* Apply minimum voltage based on DAL's request level */
-       tonga_apply_dal_minimum_voltage_request(hwmgr);
-
-       if (0 == data->sclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-               if (tonga_is_dpm_running(hwmgr))
-                       printk(KERN_ERR "[ powerplay ] Trying to set Enable Mask when DPM is disabled \n");
-
-               if (0 != data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       result = smum_send_msg_to_smc_with_parameter(
-                                                               hwmgr->smumgr,
-                               (PPSMC_Msg)PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                               data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == result),
-                               "Set Sclk Dpm enable Mask failed", return -1);
-               }
-       }
-
-       if (0 == data->mclk_dpm_key_disabled) {
-               /* Checking if DPM is running.  If we discover hang because of this, we should skip this message.*/
-               if (tonga_is_dpm_running(hwmgr))
-                       printk(KERN_ERR "[ powerplay ] Trying to set Enable Mask when DPM is disabled \n");
-
-               if (0 != data->dpm_level_enable_mask.mclk_dpm_enable_mask) {
-                       result = smum_send_msg_to_smc_with_parameter(
-                                                               hwmgr->smumgr,
-                               (PPSMC_Msg)PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                               data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == result),
-                               "Set Mclk Dpm enable Mask failed", return -1);
-               }
-       }
-
-       return 0;
-}
-
-
-int tonga_force_dpm_highest(struct pp_hwmgr *hwmgr)
-{
-       uint32_t level, tmp;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->pcie_dpm_key_disabled) {
-               /* PCIE */
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask != 0) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++ ;
-
-                       if (0 != level) {
-                               PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state_pcie(hwmgr, level)),
-                                       "force highest pcie dpm state failed!", return -1);
-                       }
-               }
-       }
-
-       if (0 == data->sclk_dpm_key_disabled) {
-               /* SCLK */
-               if (data->dpm_level_enable_mask.sclk_dpm_enable_mask != 0) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.sclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++ ;
-
-                       if (0 != level) {
-                               PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state(hwmgr, level)),
-                                       "force highest sclk dpm state failed!", return -1);
-                               if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
-                                       CGS_IND_REG__SMC, TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX) != level)
-                                       printk(KERN_ERR "[ powerplay ] Target_and_current_Profile_Index. \
-                                               Curr_Sclk_Index does not match the level \n");
-
-                       }
-               }
-       }
-
-       if (0 == data->mclk_dpm_key_disabled) {
-               /* MCLK */
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask != 0) {
-                       level = 0;
-                       tmp = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
-                       while (tmp >>= 1)
-                               level++ ;
-
-                       if (0 != level) {
-                               PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state_mclk(hwmgr, level)),
-                                       "force highest mclk dpm state failed!", return -1);
-                               if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                       TARGET_AND_CURRENT_PROFILE_INDEX, CURR_MCLK_INDEX) != level)
-                                       printk(KERN_ERR "[ powerplay ] Target_and_current_Profile_Index. \
-                                               Curr_Mclk_Index does not match the level \n");
-                       }
-               }
-       }
-
-       return 0;
-}
-
-/**
- * Find the MC microcode version and store it in the HwMgr struct
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_get_mc_microcode_version (struct pp_hwmgr *hwmgr)
-{
-       cgs_write_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_INDEX, 0x9F);
-
-       hwmgr->microcode_version_info.MC = cgs_read_register(hwmgr->device, mmMC_SEQ_IO_DEBUG_DATA);
-
-       return 0;
-}
-
-/**
- * Initialize Dynamic State Adjustment Rule Settings
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- */
-int tonga_initializa_dynamic_state_adjustment_rule_settings(struct pp_hwmgr *hwmgr)
-{
-       uint32_t table_size;
-       struct phm_clock_voltage_dependency_table *table_clk_vlt;
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       hwmgr->dyn_state.mclk_sclk_ratio = 4;
-       hwmgr->dyn_state.sclk_mclk_delta = 15000;      /* 150 MHz */
-       hwmgr->dyn_state.vddc_vddci_delta = 200;       /* 200mV */
-
-       /* initialize vddc_dep_on_dal_pwrl table */
-       table_size = sizeof(uint32_t) + 4 * sizeof(struct phm_clock_voltage_dependency_record);
-       table_clk_vlt = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == table_clk_vlt) {
-               printk(KERN_ERR "[ powerplay ] Can not allocate space for vddc_dep_on_dal_pwrl! \n");
-               return -ENOMEM;
-       } else {
-               table_clk_vlt->count = 4;
-               table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_ULTRALOW;
-               table_clk_vlt->entries[0].v = 0;
-               table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_LOW;
-               table_clk_vlt->entries[1].v = 720;
-               table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_NOMINAL;
-               table_clk_vlt->entries[2].v = 810;
-               table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_PERFORMANCE;
-               table_clk_vlt->entries[3].v = 900;
-               pptable_info->vddc_dep_on_dal_pwrl = table_clk_vlt;
-               hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
-       }
-
-       return 0;
-}
-
-static int tonga_set_private_var_based_on_pptale(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       phm_ppt_v1_clock_voltage_dependency_table *allowed_sclk_vdd_table =
-               pptable_info->vdd_dep_on_sclk;
-       phm_ppt_v1_clock_voltage_dependency_table *allowed_mclk_vdd_table =
-               pptable_info->vdd_dep_on_mclk;
-
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table != NULL,
-               "VDD dependency on SCLK table is missing.       \
-               This table is mandatory", return -1);
-       PP_ASSERT_WITH_CODE(allowed_sclk_vdd_table->count >= 1,
-               "VDD dependency on SCLK table has to have is missing.   \
-               This table is mandatory", return -1);
-
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table != NULL,
-               "VDD dependency on MCLK table is missing.       \
-               This table is mandatory", return -1);
-       PP_ASSERT_WITH_CODE(allowed_mclk_vdd_table->count >= 1,
-               "VDD dependency on MCLK table has to have is missing.    \
-               This table is mandatory", return -1);
-
-       data->min_vddc_in_pp_table = (uint16_t)allowed_sclk_vdd_table->entries[0].vddc;
-       data->max_vddc_in_pp_table = (uint16_t)allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
-
-       pptable_info->max_clock_voltage_on_ac.sclk =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].clk;
-       pptable_info->max_clock_voltage_on_ac.mclk =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].clk;
-       pptable_info->max_clock_voltage_on_ac.vddc =
-               allowed_sclk_vdd_table->entries[allowed_sclk_vdd_table->count - 1].vddc;
-       pptable_info->max_clock_voltage_on_ac.vddci =
-               allowed_mclk_vdd_table->entries[allowed_mclk_vdd_table->count - 1].vddci;
-
-       hwmgr->dyn_state.max_clock_voltage_on_ac.sclk =
-               pptable_info->max_clock_voltage_on_ac.sclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.mclk =
-               pptable_info->max_clock_voltage_on_ac.mclk;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddc =
-               pptable_info->max_clock_voltage_on_ac.vddc;
-       hwmgr->dyn_state.max_clock_voltage_on_ac.vddci =
-               pptable_info->max_clock_voltage_on_ac.vddci;
-
-       return 0;
-}
-
-int tonga_unforce_dpm_levels(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       int result = 1;
-
-       PP_ASSERT_WITH_CODE (!tonga_is_dpm_running(hwmgr),
-                            "Trying to Unforce DPM when DPM is disabled. Returning without sending SMC message.",
-                            return result);
-
-       if (0 == data->pcie_dpm_key_disabled) {
-               PP_ASSERT_WITH_CODE((0 == smum_send_msg_to_smc(
-                                                            hwmgr->smumgr,
-                                       PPSMC_MSG_PCIeDPM_UnForceLevel)),
-                                          "unforce pcie level failed!",
-                                                               return -1);
-       }
-
-       result = tonga_upload_dpm_level_enable_mask(hwmgr);
-
-       return result;
-}
-
-static uint32_t tonga_get_lowest_enable_level(
-                               struct pp_hwmgr *hwmgr, uint32_t level_mask)
-{
-       uint32_t level = 0;
-
-       while (0 == (level_mask & (1 << level)))
-               level++;
-
-       return level;
-}
-
-static int tonga_force_dpm_lowest(struct pp_hwmgr *hwmgr)
-{
-       uint32_t level;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->pcie_dpm_key_disabled) {
-               /* PCIE */
-               if (data->dpm_level_enable_mask.pcie_dpm_enable_mask != 0) {
-                       level = tonga_get_lowest_enable_level(hwmgr,
-                                                             data->dpm_level_enable_mask.pcie_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state_pcie(hwmgr, level)),
-                                           "force lowest pcie dpm state failed!", return -1);
-               }
-       }
-
-       if (0 == data->sclk_dpm_key_disabled) {
-               /* SCLK */
-               if (0 != data->dpm_level_enable_mask.sclk_dpm_enable_mask) {
-                       level = tonga_get_lowest_enable_level(hwmgr,
-                                                             data->dpm_level_enable_mask.sclk_dpm_enable_mask);
-
-                       PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state(hwmgr, level)),
-                                           "force sclk dpm state failed!", return -1);
-
-                       if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
-                                                        CGS_IND_REG__SMC, TARGET_AND_CURRENT_PROFILE_INDEX, CURR_SCLK_INDEX) != level)
-                               printk(KERN_ERR "[ powerplay ] Target_and_current_Profile_Index.        \
-                               Curr_Sclk_Index does not match the level \n");
-               }
-       }
-
-       if (0 == data->mclk_dpm_key_disabled) {
-               /* MCLK */
-               if (data->dpm_level_enable_mask.mclk_dpm_enable_mask != 0) {
-                       level = tonga_get_lowest_enable_level(hwmgr,
-                                                             data->dpm_level_enable_mask.mclk_dpm_enable_mask);
-                       PP_ASSERT_WITH_CODE((0 == tonga_dpm_force_state_mclk(hwmgr, level)),
-                                           "force lowest mclk dpm state failed!", return -1);
-                       if (PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                                        TARGET_AND_CURRENT_PROFILE_INDEX, CURR_MCLK_INDEX) != level)
-                               printk(KERN_ERR "[ powerplay ] Target_and_current_Profile_Index. \
-                                               Curr_Mclk_Index does not match the level \n");
-               }
-       }
-
-       return 0;
-}
-
-static int tonga_patch_voltage_dependency_tables_with_lookup_table(struct pp_hwmgr *hwmgr)
-{
-       uint8_t entryId;
-       uint8_t voltageId;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       phm_ppt_v1_clock_voltage_dependency_table *sclk_table = pptable_info->vdd_dep_on_sclk;
-       phm_ppt_v1_clock_voltage_dependency_table *mclk_table = pptable_info->vdd_dep_on_mclk;
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       if (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) {
-               for (entryId = 0; entryId < sclk_table->count; ++entryId) {
-                       voltageId = sclk_table->entries[entryId].vddInd;
-                       sclk_table->entries[entryId].vddgfx =
-                               pptable_info->vddgfx_lookup_table->entries[voltageId].us_vdd;
-               }
-       } else {
-               for (entryId = 0; entryId < sclk_table->count; ++entryId) {
-                       voltageId = sclk_table->entries[entryId].vddInd;
-                       sclk_table->entries[entryId].vddc =
-                               pptable_info->vddc_lookup_table->entries[voltageId].us_vdd;
-               }
-       }
-
-       for (entryId = 0; entryId < mclk_table->count; ++entryId) {
-               voltageId = mclk_table->entries[entryId].vddInd;
-               mclk_table->entries[entryId].vddc =
-                       pptable_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       for (entryId = 0; entryId < mm_table->count; ++entryId) {
-               voltageId = mm_table->entries[entryId].vddcInd;
-               mm_table->entries[entryId].vddc =
-                       pptable_info->vddc_lookup_table->entries[voltageId].us_vdd;
-       }
-
-       return 0;
-
-}
-
-static int tonga_calc_voltage_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       uint8_t entryId;
-       phm_ppt_v1_voltage_lookup_record v_record;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       phm_ppt_v1_clock_voltage_dependency_table *sclk_table = pptable_info->vdd_dep_on_sclk;
-       phm_ppt_v1_clock_voltage_dependency_table *mclk_table = pptable_info->vdd_dep_on_mclk;
-
-       if (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) {
-               for (entryId = 0; entryId < sclk_table->count; ++entryId) {
-                       if (sclk_table->entries[entryId].vdd_offset & (1 << 15))
-                               v_record.us_vdd = sclk_table->entries[entryId].vddgfx +
-                                       sclk_table->entries[entryId].vdd_offset - 0xFFFF;
-                       else
-                               v_record.us_vdd = sclk_table->entries[entryId].vddgfx +
-                                       sclk_table->entries[entryId].vdd_offset;
-
-                       sclk_table->entries[entryId].vddc =
-                               v_record.us_cac_low = v_record.us_cac_mid =
-                               v_record.us_cac_high = v_record.us_vdd;
-
-                       tonga_add_voltage(hwmgr, pptable_info->vddc_lookup_table, &v_record);
-               }
-
-               for (entryId = 0; entryId < mclk_table->count; ++entryId) {
-                       if (mclk_table->entries[entryId].vdd_offset & (1 << 15))
-                               v_record.us_vdd = mclk_table->entries[entryId].vddc +
-                                       mclk_table->entries[entryId].vdd_offset - 0xFFFF;
-                       else
-                               v_record.us_vdd = mclk_table->entries[entryId].vddc +
-                                       mclk_table->entries[entryId].vdd_offset;
-
-                       mclk_table->entries[entryId].vddgfx = v_record.us_cac_low =
-                               v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
-                       tonga_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
-               }
-       }
-
-       return 0;
-
-}
-
-static int tonga_calc_mm_voltage_dependency_table(struct pp_hwmgr *hwmgr)
-{
-       uint32_t entryId;
-       phm_ppt_v1_voltage_lookup_record v_record;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table = pptable_info->mm_dep_table;
-
-       if (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) {
-               for (entryId = 0; entryId < mm_table->count; entryId++) {
-                       if (mm_table->entries[entryId].vddgfx_offset & (1 << 15))
-                               v_record.us_vdd = mm_table->entries[entryId].vddc +
-                                       mm_table->entries[entryId].vddgfx_offset - 0xFFFF;
-                       else
-                               v_record.us_vdd = mm_table->entries[entryId].vddc +
-                                       mm_table->entries[entryId].vddgfx_offset;
-
-                       /* Add the calculated VDDGFX to the VDDGFX lookup table */
-                       mm_table->entries[entryId].vddgfx = v_record.us_cac_low =
-                               v_record.us_cac_mid = v_record.us_cac_high = v_record.us_vdd;
-                       tonga_add_voltage(hwmgr, pptable_info->vddgfx_lookup_table, &v_record);
-               }
-       }
-       return 0;
-}
-
-
-/**
- * Change virtual leakage voltage to actual value.
- *
- * @param     hwmgr  the address of the powerplay hardware manager.
- * @param     pointer to changing voltage
- * @param     pointer to leakage table
- */
-static void tonga_patch_with_vdd_leakage(struct pp_hwmgr *hwmgr,
-               uint16_t *voltage, phw_tonga_leakage_voltage *pLeakageTable)
-{
-       uint32_t leakage_index;
-
-       /* search for leakage voltage ID 0xff01 ~ 0xff08 */
-       for (leakage_index = 0; leakage_index < pLeakageTable->count; leakage_index++) {
-               /* if this voltage matches a leakage voltage ID */
-               /* patch with actual leakage voltage */
-               if (pLeakageTable->leakage_id[leakage_index] == *voltage) {
-                       *voltage = pLeakageTable->actual_voltage[leakage_index];
-                       break;
-               }
-       }
-
-       if (*voltage > ATOM_VIRTUAL_VOLTAGE_ID0)
-               printk(KERN_ERR "[ powerplay ] Voltage value looks like a Leakage ID but it's not patched \n");
-}
-
-/**
- * Patch voltage lookup table by EVV leakages.
- *
- * @param     hwmgr  the address of the powerplay hardware manager.
- * @param     pointer to voltage lookup table
- * @param     pointer to leakage table
- * @return     always 0
- */
-static int tonga_patch_lookup_table_with_leakage(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *lookup_table,
-               phw_tonga_leakage_voltage *pLeakageTable)
-{
-       uint32_t i;
-
-       for (i = 0; i < lookup_table->count; i++) {
-               tonga_patch_with_vdd_leakage(hwmgr,
-                       &lookup_table->entries[i].us_vdd, pLeakageTable);
-       }
-
-       return 0;
-}
-
-static int tonga_patch_clock_voltage_lomits_with_vddc_leakage(struct pp_hwmgr *hwmgr,
-               phw_tonga_leakage_voltage *pLeakageTable, uint16_t *Vddc)
-{
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       tonga_patch_with_vdd_leakage(hwmgr, (uint16_t *)Vddc, pLeakageTable);
-       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
-               pptable_info->max_clock_voltage_on_dc.vddc;
-
-       return 0;
-}
-
-static int tonga_patch_clock_voltage_limits_with_vddgfx_leakage(
-               struct pp_hwmgr *hwmgr, phw_tonga_leakage_voltage *pLeakageTable,
-               uint16_t *Vddgfx)
-{
-       tonga_patch_with_vdd_leakage(hwmgr, (uint16_t *)Vddgfx, pLeakageTable);
-       return 0;
-}
-
-int tonga_sort_lookup_table(struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table *lookup_table)
-{
-       uint32_t table_size, i, j;
-       phm_ppt_v1_voltage_lookup_record tmp_voltage_lookup_record;
-       table_size = lookup_table->count;
-
-       PP_ASSERT_WITH_CODE(0 != lookup_table->count,
-               "Lookup table is empty", return -1);
-
-       /* Sorting voltages */
-       for (i = 0; i < table_size - 1; i++) {
-               for (j = i + 1; j > 0; j--) {
-                       if (lookup_table->entries[j].us_vdd < lookup_table->entries[j-1].us_vdd) {
-                               tmp_voltage_lookup_record = lookup_table->entries[j-1];
-                               lookup_table->entries[j-1] = lookup_table->entries[j];
-                               lookup_table->entries[j] = tmp_voltage_lookup_record;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int tonga_complete_dependency_tables(struct pp_hwmgr *hwmgr)
-{
-       int result = 0;
-       int tmp_result;
-       tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (data->vdd_gfx_control == TONGA_VOLTAGE_CONTROL_BY_SVID2) {
-               tmp_result = tonga_patch_lookup_table_with_leakage(hwmgr,
-                       pptable_info->vddgfx_lookup_table, &(data->vddcgfx_leakage));
-               if (tmp_result != 0)
-                       result = tmp_result;
-
-               tmp_result = tonga_patch_clock_voltage_limits_with_vddgfx_leakage(hwmgr,
-                       &(data->vddcgfx_leakage), &pptable_info->max_clock_voltage_on_dc.vddgfx);
-               if (tmp_result != 0)
-                       result = tmp_result;
-       } else {
-               tmp_result = tonga_patch_lookup_table_with_leakage(hwmgr,
-                       pptable_info->vddc_lookup_table, &(data->vddc_leakage));
-               if (tmp_result != 0)
-                       result = tmp_result;
-
-               tmp_result = tonga_patch_clock_voltage_lomits_with_vddc_leakage(hwmgr,
-                       &(data->vddc_leakage), &pptable_info->max_clock_voltage_on_dc.vddc);
-               if (tmp_result != 0)
-                       result = tmp_result;
-       }
-
-       tmp_result = tonga_patch_voltage_dependency_tables_with_lookup_table(hwmgr);
-       if (tmp_result != 0)
-               result = tmp_result;
-
-       tmp_result = tonga_calc_voltage_dependency_tables(hwmgr);
-       if (tmp_result != 0)
-               result = tmp_result;
-
-       tmp_result = tonga_calc_mm_voltage_dependency_table(hwmgr);
-       if (tmp_result != 0)
-               result = tmp_result;
-
-       tmp_result = tonga_sort_lookup_table(hwmgr, pptable_info->vddgfx_lookup_table);
-       if (tmp_result != 0)
-               result = tmp_result;
-
-       tmp_result = tonga_sort_lookup_table(hwmgr, pptable_info->vddc_lookup_table);
-       if (tmp_result != 0)
-               result = tmp_result;
-
-       return result;
-}
-
-int tonga_init_sclk_threshold(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       data->low_sclk_interrupt_threshold = 0;
-
-       return 0;
-}
-
-int tonga_setup_asic_task(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = tonga_read_clock_registers(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to read clock registers!", result = tmp_result);
-
-       tmp_result = tonga_get_memory_type(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to get memory type!", result = tmp_result);
-
-       tmp_result = tonga_enable_acpi_power_management(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to enable ACPI power management!", result = tmp_result);
-
-       tmp_result = tonga_init_power_gate_state(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to init power gate state!", result = tmp_result);
-
-       tmp_result = tonga_get_mc_microcode_version(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to get MC microcode version!", result = tmp_result);
-
-       tmp_result = tonga_init_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to init sclk threshold!", result = tmp_result);
-
-       return result;
-}
-
-/**
- * Enable voltage control
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_enable_voltage_control(struct pp_hwmgr *hwmgr)
-{
-       /* enable voltage control */
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, GENERAL_PWRMGT, VOLT_PWRMGT_EN, 1);
-
-       return 0;
-}
-
-/**
- * Checks if we want to support voltage control
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- */
-bool cf_tonga_voltage_control(const struct pp_hwmgr *hwmgr)
-{
-       const struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       return(TONGA_VOLTAGE_CONTROL_NONE != data->voltage_control);
-}
-
-/*---------------------------MC----------------------------*/
-
-uint8_t tonga_get_memory_modile_index(struct pp_hwmgr *hwmgr)
-{
-       return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
-}
-
-bool tonga_check_s0_mc_reg_index(uint16_t inReg, uint16_t *outReg)
-{
-       bool result = true;
-
-       switch (inReg) {
-       case  mmMC_SEQ_RAS_TIMING:
-               *outReg = mmMC_SEQ_RAS_TIMING_LP;
-               break;
-
-       case  mmMC_SEQ_DLL_STBY:
-               *outReg = mmMC_SEQ_DLL_STBY_LP;
-               break;
-
-       case  mmMC_SEQ_G5PDX_CMD0:
-               *outReg = mmMC_SEQ_G5PDX_CMD0_LP;
-               break;
-
-       case  mmMC_SEQ_G5PDX_CMD1:
-               *outReg = mmMC_SEQ_G5PDX_CMD1_LP;
-               break;
-
-       case  mmMC_SEQ_G5PDX_CTRL:
-               *outReg = mmMC_SEQ_G5PDX_CTRL_LP;
-               break;
-
-       case mmMC_SEQ_CAS_TIMING:
-               *outReg = mmMC_SEQ_CAS_TIMING_LP;
-               break;
-
-       case mmMC_SEQ_MISC_TIMING:
-               *outReg = mmMC_SEQ_MISC_TIMING_LP;
-               break;
-
-       case mmMC_SEQ_MISC_TIMING2:
-               *outReg = mmMC_SEQ_MISC_TIMING2_LP;
-               break;
-
-       case mmMC_SEQ_PMG_DVS_CMD:
-               *outReg = mmMC_SEQ_PMG_DVS_CMD_LP;
-               break;
-
-       case mmMC_SEQ_PMG_DVS_CTL:
-               *outReg = mmMC_SEQ_PMG_DVS_CTL_LP;
-               break;
-
-       case mmMC_SEQ_RD_CTL_D0:
-               *outReg = mmMC_SEQ_RD_CTL_D0_LP;
-               break;
-
-       case mmMC_SEQ_RD_CTL_D1:
-               *outReg = mmMC_SEQ_RD_CTL_D1_LP;
-               break;
-
-       case mmMC_SEQ_WR_CTL_D0:
-               *outReg = mmMC_SEQ_WR_CTL_D0_LP;
-               break;
-
-       case mmMC_SEQ_WR_CTL_D1:
-               *outReg = mmMC_SEQ_WR_CTL_D1_LP;
-               break;
-
-       case mmMC_PMG_CMD_EMRS:
-               *outReg = mmMC_SEQ_PMG_CMD_EMRS_LP;
-               break;
-
-       case mmMC_PMG_CMD_MRS:
-               *outReg = mmMC_SEQ_PMG_CMD_MRS_LP;
-               break;
-
-       case mmMC_PMG_CMD_MRS1:
-               *outReg = mmMC_SEQ_PMG_CMD_MRS1_LP;
-               break;
-
-       case mmMC_SEQ_PMG_TIMING:
-               *outReg = mmMC_SEQ_PMG_TIMING_LP;
-               break;
-
-       case mmMC_PMG_CMD_MRS2:
-               *outReg = mmMC_SEQ_PMG_CMD_MRS2_LP;
-               break;
-
-       case mmMC_SEQ_WR_CTL_2:
-               *outReg = mmMC_SEQ_WR_CTL_2_LP;
-               break;
-
-       default:
-               result = false;
-               break;
-       }
-
-       return result;
-}
-
-int tonga_set_s0_mc_reg_index(phw_tonga_mc_reg_table *table)
-{
-       uint32_t i;
-       uint16_t address;
-
-       for (i = 0; i < table->last; i++) {
-               table->mc_reg_address[i].s0 =
-                       tonga_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address)
-                       ? address : table->mc_reg_address[i].s1;
-       }
-       return 0;
-}
-
-int tonga_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table, phw_tonga_mc_reg_table *ni_table)
-{
-       uint8_t i, j;
-
-       PP_ASSERT_WITH_CODE((table->last <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-               "Invalid VramInfo table.", return -1);
-       PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
-               "Invalid VramInfo table.", return -1);
-
-       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;
-}
-
-/**
- * VBIOS omits some information to reduce size, we need to recover them here.
- * 1.   when we see mmMC_SEQ_MISC1, bit[31:16] EMRS1, need to be write to  mmMC_PMG_CMD_EMRS /_LP[15:0].
- *      Bit[15:0] MRS, need to be update mmMC_PMG_CMD_MRS/_LP[15:0]
- * 2.   when we see mmMC_SEQ_RESERVE_M, bit[15:0] EMRS2, need to be write to mmMC_PMG_CMD_MRS1/_LP[15:0].
- * 3.   need to set these data for each clock range
- *
- * @param    hwmgr the address of the powerplay hardware manager.
- * @param    table the address of MCRegTable
- * @return   always 0
- */
-int tonga_set_mc_special_registers(struct pp_hwmgr *hwmgr, phw_tonga_mc_reg_table *table)
-{
-       uint8_t i, j, k;
-       uint32_t temp_reg;
-       const tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       for (i = 0, j = table->last; i < table->last; i++) {
-               PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-                       "Invalid VramInfo table.", return -1);
-               switch (table->mc_reg_address[i].s1) {
-               /*
-               * mmMC_SEQ_MISC1, bit[31:16] EMRS1, need to be write to  mmMC_PMG_CMD_EMRS /_LP[15:0].
-               * Bit[15:0] MRS, need to be update mmMC_PMG_CMD_MRS/_LP[15:0]
-               */
-               case mmMC_SEQ_MISC1:
-                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS);
-                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
-                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
-                       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++;
-                       PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-                               "Invalid VramInfo table.", return -1);
-
-                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
-                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
-                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
-                       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 (!data->is_memory_GDDR5) {
-                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
-                               }
-                       }
-                       j++;
-                       PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-                               "Invalid VramInfo table.", return -1);
-
-                       if (!data->is_memory_GDDR5) {
-                               table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
-                               table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
-                               for (k = 0; k < table->num_entries; k++) {
-                                       table->mc_reg_table_entry[k].mc_data[j] =
-                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
-                               }
-                               j++;
-                               PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-                                       "Invalid VramInfo table.", return -1);
-                       }
-
-                       break;
-
-               case mmMC_SEQ_RESERVE_M:
-                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
-                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
-                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
-                       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++;
-                       PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
-                               "Invalid VramInfo table.", return -1);
-                       break;
-
-               default:
-                       break;
-               }
-
-       }
-
-       table->last = j;
-
-       return 0;
-}
-
-int tonga_set_valid_flag(phw_tonga_mc_reg_table *table)
-{
-       uint8_t 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->validflag |= (1<<i);
-                               break;
-                       }
-               }
-       }
-
-       return 0;
-}
-
-int tonga_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-       pp_atomctrl_mc_reg_table *table;
-       phw_tonga_mc_reg_table *ni_table = &data->tonga_mc_reg_table;
-       uint8_t module_index = tonga_get_memory_modile_index(hwmgr);
-
-       table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
-
-       if (NULL == table)
-               return -ENOMEM;
-
-       /* Program additional LP registers that are no longer programmed by VBIOS */
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
-       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
-
-       memset(table, 0x00, sizeof(pp_atomctrl_mc_reg_table));
-
-       result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
-
-       if (0 == result)
-               result = tonga_copy_vbios_smc_reg_table(table, ni_table);
-
-       if (0 == result) {
-               tonga_set_s0_mc_reg_index(ni_table);
-               result = tonga_set_mc_special_registers(hwmgr, ni_table);
-       }
-
-       if (0 == result)
-               tonga_set_valid_flag(ni_table);
-
-       kfree(table);
-       return result;
-}
-
-/*
-* Copy one arb setting to another and then switch the active set.
-* arbFreqSrc and arbFreqDest is one of the MC_CG_ARB_FREQ_Fx constants.
-*/
-int tonga_copy_and_switch_arb_sets(struct pp_hwmgr *hwmgr,
-               uint32_t arbFreqSrc, uint32_t arbFreqDest)
-{
-       uint32_t mc_arb_dram_timing;
-       uint32_t mc_arb_dram_timing2;
-       uint32_t burst_time;
-       uint32_t mc_cg_config;
-
-       switch (arbFreqSrc) {
-       case MC_CG_ARB_FREQ_F0:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
-               break;
-
-       case MC_CG_ARB_FREQ_F1:
-               mc_arb_dram_timing  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1);
-               mc_arb_dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1);
-               burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1);
-               break;
-
-       default:
-               return -1;
-       }
-
-       switch (arbFreqDest) {
-       case MC_CG_ARB_FREQ_F0:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0, burst_time);
-               break;
-
-       case MC_CG_ARB_FREQ_F1:
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
-               cgs_write_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
-               PHM_WRITE_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE1, burst_time);
-               break;
-
-       default:
-               return -1;
-       }
-
-       mc_cg_config = cgs_read_register(hwmgr->device, mmMC_CG_CONFIG);
-       mc_cg_config |= 0x0000000F;
-       cgs_write_register(hwmgr->device, mmMC_CG_CONFIG, mc_cg_config);
-       PHM_WRITE_FIELD(hwmgr->device, MC_ARB_CG, CG_ARB_REQ, arbFreqDest);
-
-       return 0;
-}
-
-/**
- * Initial switch from ARB F0->F1
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- * This function is to be called from the SetPowerState table.
- */
-int tonga_initial_switch_from_arb_f0_to_f1(struct pp_hwmgr *hwmgr)
-{
-       return tonga_copy_and_switch_arb_sets(hwmgr, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
-}
-
-/**
- * Initialize the ARB DRAM timing table's index field.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_init_arb_table_index(struct pp_hwmgr *hwmgr)
-{
-       const tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t tmp;
-       int result;
-
-       /*
-       * This is a read-modify-write on the first byte of the ARB table.
-       * The first byte in the SMU72_Discrete_MCArbDramTimingTable structure is the field 'current'.
-       * This solution is ugly, but we never write the whole table only individual fields in it.
-       * In reality this field should not be in that structure but in a soft register.
-       */
-       result = tonga_read_smc_sram_dword(hwmgr->smumgr,
-                               data->arb_table_start, &tmp, data->sram_end);
-
-       if (0 != result)
-               return result;
-
-       tmp &= 0x00FFFFFF;
-       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
-
-       return tonga_write_smc_sram_dword(hwmgr->smumgr,
-                       data->arb_table_start,  tmp, data->sram_end);
-}
-
-int tonga_populate_mc_reg_address(struct pp_hwmgr *hwmgr, SMU72_Discrete_MCRegisters *mc_reg_table)
-{
-       const struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       uint32_t i, j;
-
-       for (i = 0, j = 0; j < data->tonga_mc_reg_table.last; j++) {
-               if (data->tonga_mc_reg_table.validflag & 1<<j) {
-                       PP_ASSERT_WITH_CODE(i < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE,
-                               "Index of mc_reg_table->address[] array out of boundary", return -1);
-                       mc_reg_table->address[i].s0 =
-                               PP_HOST_TO_SMC_US(data->tonga_mc_reg_table.mc_reg_address[j].s0);
-                       mc_reg_table->address[i].s1 =
-                               PP_HOST_TO_SMC_US(data->tonga_mc_reg_table.mc_reg_address[j].s1);
-                       i++;
-               }
-       }
-
-       mc_reg_table->last = (uint8_t)i;
-
-       return 0;
-}
-
-/*convert register values from driver to SMC format */
-void tonga_convert_mc_registers(
-       const phw_tonga_mc_reg_entry * pEntry,
-       SMU72_Discrete_MCRegisterSet *pData,
-       uint32_t numEntries, uint32_t validflag)
-{
-       uint32_t i, j;
-
-       for (i = 0, j = 0; j < numEntries; j++) {
-               if (validflag & 1<<j) {
-                       pData->value[i] = PP_HOST_TO_SMC_UL(pEntry->mc_data[j]);
-                       i++;
-               }
-       }
-}
-
-/* find the entry in the memory range table, then populate the value to SMC's tonga_mc_reg_table */
-int tonga_convert_mc_reg_table_entry_to_smc(
-               struct pp_hwmgr *hwmgr,
-               const uint32_t memory_clock,
-               SMU72_Discrete_MCRegisterSet *mc_reg_table_data
-               )
-{
-       const tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t i = 0;
-
-       for (i = 0; i < data->tonga_mc_reg_table.num_entries; i++) {
-               if (memory_clock <=
-                       data->tonga_mc_reg_table.mc_reg_table_entry[i].mclk_max) {
-                       break;
-               }
-       }
-
-       if ((i == data->tonga_mc_reg_table.num_entries) && (i > 0))
-               --i;
-
-       tonga_convert_mc_registers(&data->tonga_mc_reg_table.mc_reg_table_entry[i],
-               mc_reg_table_data, data->tonga_mc_reg_table.last, data->tonga_mc_reg_table.validflag);
-
-       return 0;
-}
-
-int tonga_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
-               SMU72_Discrete_MCRegisters *mc_reg_table)
-{
-       int result = 0;
-       tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       int res;
-       uint32_t i;
-
-       for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
-               res = tonga_convert_mc_reg_table_entry_to_smc(
-                               hwmgr,
-                               data->dpm_table.mclk_table.dpm_levels[i].value,
-                               &mc_reg_table->data[i]
-                               );
-
-               if (0 != res)
-                       result = res;
-       }
-
-       return result;
-}
-
-int tonga_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
-{
-       int result;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       memset(&data->mc_reg_table, 0x00, sizeof(SMU72_Discrete_MCRegisters));
-       result = tonga_populate_mc_reg_address(hwmgr, &(data->mc_reg_table));
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize MCRegTable for the MC register addresses!", return result;);
-
-       result = tonga_convert_mc_reg_table_to_smc(hwmgr, &data->mc_reg_table);
-       PP_ASSERT_WITH_CODE(0 == result,
-               "Failed to initialize MCRegTable for driver state!", return result;);
-
-       return tonga_copy_bytes_to_smc(hwmgr->smumgr, data->mc_reg_table_start,
-                       (uint8_t *)&data->mc_reg_table, sizeof(SMU72_Discrete_MCRegisters), data->sram_end);
-}
-
-/**
- * Programs static screed detection parameters
- *
- * @param   hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_program_static_screen_threshold_parameters(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* Set static screen threshold unit*/
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device,
-               CGS_IND_REG__SMC, CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD_UNIT,
-               data->static_screen_threshold_unit);
-       /* Set static screen threshold*/
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device,
-               CGS_IND_REG__SMC, CG_STATIC_SCREEN_PARAMETER, STATIC_SCREEN_THRESHOLD,
-               data->static_screen_threshold);
-
-       return 0;
-}
-
-/**
- * Setup display gap for glitch free memory clock switching.
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_enable_display_gap(struct pp_hwmgr *hwmgr)
-{
-       uint32_t display_gap = cgs_read_ind_register(hwmgr->device,
-                                                       CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
-
-       display_gap = PHM_SET_FIELD(display_gap,
-                                       CG_DISPLAY_GAP_CNTL, DISP_GAP, DISPLAY_GAP_IGNORE);
-
-       display_gap = PHM_SET_FIELD(display_gap,
-                                       CG_DISPLAY_GAP_CNTL, DISP_GAP_MCHG, DISPLAY_GAP_VBLANK);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_DISPLAY_GAP_CNTL, display_gap);
-
-       return 0;
-}
-
-/**
- * Programs activity state transition voting clients
- *
- * @param    hwmgr  the address of the powerplay hardware manager.
- * @return   always 0
- */
-int tonga_program_voting_clients(struct pp_hwmgr *hwmgr)
-{
-       tonga_hwmgr *data = (tonga_hwmgr *)(hwmgr->backend);
-
-       /* Clear reset for voting clients before enabling DPM */
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-               SCLK_PWRMGT_CNTL, RESET_SCLK_CNT, 0);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-               SCLK_PWRMGT_CNTL, RESET_BUSY_CNT, 0);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_0, data->voting_rights_clients0);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_1, data->voting_rights_clients1);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_2, data->voting_rights_clients2);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_3, data->voting_rights_clients3);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_4, data->voting_rights_clients4);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_5, data->voting_rights_clients5);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_6, data->voting_rights_clients6);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-               ixCG_FREQ_TRAN_VOTING_7, data->voting_rights_clients7);
-
-       return 0;
-}
-
-
-int tonga_enable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = tonga_check_for_dpm_stopped(hwmgr);
-
-       if (cf_tonga_voltage_control(hwmgr)) {
-               tmp_result = tonga_enable_voltage_control(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to enable voltage control!", result = tmp_result);
-
-               tmp_result = tonga_construct_voltage_tables(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == tmp_result),
-                       "Failed to contruct voltage tables!", result = tmp_result);
-       }
-
-       tmp_result = tonga_initialize_mc_reg_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to initialize MC reg table!", result = tmp_result);
-
-       tmp_result = tonga_program_static_screen_threshold_parameters(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to program static screen threshold parameters!", result = tmp_result);
-
-       tmp_result = tonga_enable_display_gap(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to enable display gap!", result = tmp_result);
-
-       tmp_result = tonga_program_voting_clients(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to program voting clients!", result = tmp_result);
-
-       tmp_result = tonga_process_firmware_header(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to process firmware header!", result = tmp_result);
-
-       tmp_result = tonga_initial_switch_from_arb_f0_to_f1(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to initialize switch from ArbF0 to F1!", result = tmp_result);
-
-       tmp_result = tonga_init_smc_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to initialize SMC table!", result = tmp_result);
-
-       tmp_result = tonga_init_arb_table_index(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to initialize ARB table index!", result = tmp_result);
-
-       tmp_result = tonga_populate_initial_mc_reg_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to populate initialize MC Reg table!", result = tmp_result);
-
-       tmp_result = tonga_notify_smc_display_change(hwmgr, false);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to notify no display!", result = tmp_result);
-
-       /* enable SCLK control */
-       tmp_result = tonga_enable_sclk_control(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to enable SCLK control!", result = tmp_result);
-
-       /* enable DPM */
-       tmp_result = tonga_start_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to start DPM!", result = tmp_result);
-
-       return result;
-}
-
-int tonga_disable_dpm_tasks(struct pp_hwmgr *hwmgr)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = tonga_check_for_dpm_running(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "SMC is still running!", return 0);
-
-       tmp_result = tonga_stop_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to stop DPM!", result = tmp_result);
-
-       tmp_result = tonga_reset_to_default(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result),
-               "Failed to reset to default!", result = tmp_result);
-
-       return result;
-}
-
-int tonga_reset_asic_tasks(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       result = tonga_set_boot_state(hwmgr);
-       if (0 != result)
-               printk(KERN_ERR "[ powerplay ] Failed to reset asic via set boot state! \n");
-
-       return result;
-}
-
-int tonga_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
-{
-       return phm_hwmgr_backend_fini(hwmgr);
-}
-
-/**
- * Initializes the Volcanic Islands Hardware Manager
- *
- * @param   hwmgr the address of the powerplay hardware manager.
- * @return   1 if success; otherwise appropriate error code.
- */
-int tonga_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
-{
-       int result = 0;
-       SMU72_Discrete_DpmTable  *table = NULL;
-       tonga_hwmgr *data;
-       pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       phw_tonga_ulv_parm *ulv;
-       struct cgs_system_info sys_info = {0};
-
-       PP_ASSERT_WITH_CODE((NULL != hwmgr),
-               "Invalid Parameter!", return -1;);
-
-       data = kzalloc(sizeof(struct tonga_hwmgr), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       hwmgr->backend = data;
-
-       data->dll_defaule_on = false;
-       data->sram_end = SMC_RAM_END;
-
-       data->activity_target[0] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[1] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[2] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[3] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[4] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[5] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[6] = PPTONGA_TARGETACTIVITY_DFLT;
-       data->activity_target[7] = PPTONGA_TARGETACTIVITY_DFLT;
-
-       data->vddc_vddci_delta = VDDC_VDDCI_DELTA;
-       data->vddc_vddgfx_delta = VDDC_VDDGFX_DELTA;
-       data->mclk_activity_target = PPTONGA_MCLK_TARGETACTIVITY_DFLT;
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_DisableVoltageIsland);
-
-       data->sclk_dpm_key_disabled = 0;
-       data->mclk_dpm_key_disabled = 0;
-       data->pcie_dpm_key_disabled = 0;
-       data->pcc_monitor_enabled = 0;
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_UnTabledHardwareInterface);
-
-       data->gpio_debug = 0;
-       data->engine_clock_data = 0;
-       data->memory_clock_data = 0;
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_DynamicPatchPowerState);
-
-       /* need to set voltage control types before EVV patching*/
-       data->voltage_control = TONGA_VOLTAGE_CONTROL_NONE;
-       data->vdd_ci_control = TONGA_VOLTAGE_CONTROL_NONE;
-       data->vdd_gfx_control = TONGA_VOLTAGE_CONTROL_NONE;
-       data->mvdd_control = TONGA_VOLTAGE_CONTROL_NONE;
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-
-       if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                               VOLTAGE_TYPE_VDDC, VOLTAGE_OBJ_SVID2)) {
-               data->voltage_control = TONGA_VOLTAGE_CONTROL_BY_SVID2;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ControlVDDGFX)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                       VOLTAGE_TYPE_VDDGFX, VOLTAGE_OBJ_SVID2)) {
-                       data->vdd_gfx_control = TONGA_VOLTAGE_CONTROL_BY_SVID2;
-               }
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_NONE == data->vdd_gfx_control) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ControlVDDGFX);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableMVDDControl)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                                       VOLTAGE_TYPE_MVDDC, VOLTAGE_OBJ_GPIO_LUT)) {
-                       data->mvdd_control = TONGA_VOLTAGE_CONTROL_BY_GPIO;
-               }
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_NONE == data->mvdd_control) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_EnableMVDDControl);
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ControlVDDCI)) {
-               if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                                       VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_GPIO_LUT))
-                       data->vdd_ci_control = TONGA_VOLTAGE_CONTROL_BY_GPIO;
-               else if (atomctrl_is_voltage_controled_by_gpio_v3(hwmgr,
-                                               VOLTAGE_TYPE_VDDCI, VOLTAGE_OBJ_SVID2))
-                       data->vdd_ci_control = TONGA_VOLTAGE_CONTROL_BY_SVID2;
-       }
-
-       if (TONGA_VOLTAGE_CONTROL_NONE == data->vdd_ci_control)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_ControlVDDCI);
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_TablelessHardwareInterface);
-
-       if (pptable_info->cac_dtp_table->usClockStretchAmount != 0)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ClockStretcher);
-
-       /* Initializes DPM default values*/
-       tonga_initialize_dpm_defaults(hwmgr);
-
-       /* Get leakage voltage based on leakage ID.*/
-       PP_ASSERT_WITH_CODE((0 == tonga_get_evv_voltage(hwmgr)),
-               "Get EVV Voltage Failed.  Abort Driver loading!", return -1);
-
-       tonga_complete_dependency_tables(hwmgr);
-
-       /* Parse pptable data read from VBIOS*/
-       tonga_set_private_var_based_on_pptale(hwmgr);
-
-       /* ULV Support*/
-       ulv = &(data->ulv);
-       ulv->ulv_supported = false;
-
-       /* Initalize Dynamic State Adjustment Rule Settings*/
-       result = tonga_initializa_dynamic_state_adjustment_rule_settings(hwmgr);
-       if (result)
-               printk(KERN_ERR "[ powerplay ] tonga_initializa_dynamic_state_adjustment_rule_settings failed!\n");
-       data->uvd_enabled = false;
-
-       table = &(data->smc_state_table);
-
-       /*
-       * if ucGPIO_ID=VDDC_PCC_GPIO_PINID in GPIO_LUTable,
-       * Peak Current Control feature is enabled and we should program PCC HW register
-       */
-       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_PCC_GPIO_PINID, &gpio_pin_assignment)) {
-               uint32_t temp_reg = cgs_read_ind_register(hwmgr->device,
-                                                                               CGS_IND_REG__SMC, ixCNB_PWRMGT_CNTL);
-
-               switch (gpio_pin_assignment.uc_gpio_pin_bit_shift) {
-               case 0:
-                       temp_reg = PHM_SET_FIELD(temp_reg,
-                               CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x1);
-                       break;
-               case 1:
-                       temp_reg = PHM_SET_FIELD(temp_reg,
-                               CNB_PWRMGT_CNTL, GNB_SLOW_MODE, 0x2);
-                       break;
-               case 2:
-                       temp_reg = PHM_SET_FIELD(temp_reg,
-                               CNB_PWRMGT_CNTL, GNB_SLOW, 0x1);
-                       break;
-               case 3:
-                       temp_reg = PHM_SET_FIELD(temp_reg,
-                               CNB_PWRMGT_CNTL, FORCE_NB_PS1, 0x1);
-                       break;
-               case 4:
-                       temp_reg = PHM_SET_FIELD(temp_reg,
-                               CNB_PWRMGT_CNTL, DPM_ENABLED, 0x1);
-                       break;
-               default:
-                       printk(KERN_ERR "[ powerplay ] Failed to setup PCC HW register! \
-                               Wrong GPIO assigned for VDDC_PCC_GPIO_PINID! \n");
-                       break;
-               }
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
-                       ixCNB_PWRMGT_CNTL, temp_reg);
-       }
-
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_EnableSMU7ThermalManagement);
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_SMU7);
-
-       data->vddc_phase_shed_control = false;
-
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                     PHM_PlatformCaps_UVDPowerGating);
-       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
-                     PHM_PlatformCaps_VCEPowerGating);
-       sys_info.size = sizeof(struct cgs_system_info);
-       sys_info.info_id = CGS_SYSTEM_INFO_PG_FLAGS;
-       result = cgs_query_system_info(hwmgr->device, &sys_info);
-       if (!result) {
-               if (sys_info.value & AMD_PG_SUPPORT_UVD)
-                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                     PHM_PlatformCaps_UVDPowerGating);
-               if (sys_info.value & AMD_PG_SUPPORT_VCE)
-                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                     PHM_PlatformCaps_VCEPowerGating);
-       }
-
-       if (0 == result) {
-               data->is_tlu_enabled = false;
-               hwmgr->platform_descriptor.hardwareActivityPerformanceLevels =
-                       TONGA_MAX_HARDWARE_POWERLEVELS;
-               hwmgr->platform_descriptor.hardwarePerformanceLevels = 2;
-               hwmgr->platform_descriptor.minimumClocksReductionPercentage  = 50;
-
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_GEN_INFO;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_gen_cap = AMDGPU_DEFAULT_PCIE_GEN_MASK;
-               else
-                       data->pcie_gen_cap = (uint32_t)sys_info.value;
-               if (data->pcie_gen_cap & CAIL_PCIE_LINK_SPEED_SUPPORT_GEN3)
-                       data->pcie_spc_cap = 20;
-               sys_info.size = sizeof(struct cgs_system_info);
-               sys_info.info_id = CGS_SYSTEM_INFO_PCIE_MLW;
-               result = cgs_query_system_info(hwmgr->device, &sys_info);
-               if (result)
-                       data->pcie_lane_cap = AMDGPU_DEFAULT_PCIE_MLW_MASK;
-               else
-                       data->pcie_lane_cap = (uint32_t)sys_info.value;
-       } else {
-               /* Ignore return value in here, we are cleaning up a mess. */
-               tonga_hwmgr_backend_fini(hwmgr);
-       }
-
-       return result;
-}
-
-static int tonga_force_dpm_level(struct pp_hwmgr *hwmgr,
-               enum amd_dpm_forced_level level)
-{
-       int ret = 0;
-
-       switch (level) {
-       case AMD_DPM_FORCED_LEVEL_HIGH:
-               ret = tonga_force_dpm_highest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_LOW:
-               ret = tonga_force_dpm_lowest(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       case AMD_DPM_FORCED_LEVEL_AUTO:
-               ret = tonga_unforce_dpm_levels(hwmgr);
-               if (ret)
-                       return ret;
-               break;
-       default:
-               break;
-       }
-
-       hwmgr->dpm_level = level;
-       return ret;
-}
-
-static int tonga_apply_state_adjust_rules(struct pp_hwmgr *hwmgr,
-                               struct pp_power_state  *prequest_ps,
-                       const struct pp_power_state *pcurrent_ps)
-{
-       struct tonga_power_state *tonga_ps =
-                               cast_phw_tonga_power_state(&prequest_ps->hardware);
-
-       uint32_t sclk;
-       uint32_t mclk;
-       struct PP_Clocks minimum_clocks = {0};
-       bool disable_mclk_switching;
-       bool disable_mclk_switching_for_frame_lock;
-       struct cgs_display_info info = {0};
-       const struct phm_clock_and_voltage_limits *max_limits;
-       uint32_t i;
-       tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       int32_t count;
-       int32_t stable_pstate_sclk = 0, stable_pstate_mclk = 0;
-
-       data->battery_state = (PP_StateUILabel_Battery == prequest_ps->classification.ui_label);
-
-       PP_ASSERT_WITH_CODE(tonga_ps->performance_level_count == 2,
-                                "VI should always have 2 performance levels",
-                                );
-
-       max_limits = (PP_PowerSource_AC == hwmgr->power_source) ?
-                       &(hwmgr->dyn_state.max_clock_voltage_on_ac) :
-                       &(hwmgr->dyn_state.max_clock_voltage_on_dc);
-
-       if (PP_PowerSource_DC == hwmgr->power_source) {
-               for (i = 0; i < tonga_ps->performance_level_count; i++) {
-                       if (tonga_ps->performance_levels[i].memory_clock > max_limits->mclk)
-                               tonga_ps->performance_levels[i].memory_clock = max_limits->mclk;
-                       if (tonga_ps->performance_levels[i].engine_clock > max_limits->sclk)
-                               tonga_ps->performance_levels[i].engine_clock = max_limits->sclk;
-               }
-       }
-
-       tonga_ps->vce_clocks.EVCLK = hwmgr->vce_arbiter.evclk;
-       tonga_ps->vce_clocks.ECCLK = hwmgr->vce_arbiter.ecclk;
-
-       tonga_ps->acp_clk = hwmgr->acp_arbiter.acpclk;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       /*TO DO result = PHM_CheckVBlankTime(hwmgr, &vblankTooShort);*/
-
-       /* TO DO GetMinClockSettings(hwmgr->pPECI, &minimum_clocks); */
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState)) {
-
-               max_limits = &(hwmgr->dyn_state.max_clock_voltage_on_ac);
-               stable_pstate_sclk = (max_limits->sclk * 75) / 100;
-
-               for (count = pptable_info->vdd_dep_on_sclk->count-1; count >= 0; count--) {
-                       if (stable_pstate_sclk >= pptable_info->vdd_dep_on_sclk->entries[count].clk) {
-                               stable_pstate_sclk = pptable_info->vdd_dep_on_sclk->entries[count].clk;
-                               break;
-                       }
-               }
-
-               if (count < 0)
-                       stable_pstate_sclk = pptable_info->vdd_dep_on_sclk->entries[0].clk;
-
-               stable_pstate_mclk = max_limits->mclk;
-
-               minimum_clocks.engineClock = stable_pstate_sclk;
-               minimum_clocks.memoryClock = stable_pstate_mclk;
-       }
-
-       if (minimum_clocks.engineClock < hwmgr->gfx_arbiter.sclk)
-               minimum_clocks.engineClock = hwmgr->gfx_arbiter.sclk;
-
-       if (minimum_clocks.memoryClock < hwmgr->gfx_arbiter.mclk)
-               minimum_clocks.memoryClock = hwmgr->gfx_arbiter.mclk;
-
-       tonga_ps->sclk_threshold = hwmgr->gfx_arbiter.sclk_threshold;
-
-       if (0 != hwmgr->gfx_arbiter.sclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.sclk_over_drive <= hwmgr->platform_descriptor.overdriveLimit.engineClock),
-                                       "Overdrive sclk exceeds limit",
-                                       hwmgr->gfx_arbiter.sclk_over_drive = hwmgr->platform_descriptor.overdriveLimit.engineClock);
-
-               if (hwmgr->gfx_arbiter.sclk_over_drive >= hwmgr->gfx_arbiter.sclk)
-                       tonga_ps->performance_levels[1].engine_clock = hwmgr->gfx_arbiter.sclk_over_drive;
-       }
-
-       if (0 != hwmgr->gfx_arbiter.mclk_over_drive) {
-               PP_ASSERT_WITH_CODE((hwmgr->gfx_arbiter.mclk_over_drive <= hwmgr->platform_descriptor.overdriveLimit.memoryClock),
-                       "Overdrive mclk exceeds limit",
-                       hwmgr->gfx_arbiter.mclk_over_drive = hwmgr->platform_descriptor.overdriveLimit.memoryClock);
-
-               if (hwmgr->gfx_arbiter.mclk_over_drive >= hwmgr->gfx_arbiter.mclk)
-                       tonga_ps->performance_levels[1].memory_clock = hwmgr->gfx_arbiter.mclk_over_drive;
-       }
-
-       disable_mclk_switching_for_frame_lock = phm_cap_enabled(
-                                   hwmgr->platform_descriptor.platformCaps,
-                                   PHM_PlatformCaps_DisableMclkSwitchingForFrameLock);
-
-       disable_mclk_switching = (1 < info.display_count) ||
-                                   disable_mclk_switching_for_frame_lock;
-
-       sclk  = tonga_ps->performance_levels[0].engine_clock;
-       mclk  = tonga_ps->performance_levels[0].memory_clock;
-
-       if (disable_mclk_switching)
-               mclk  = tonga_ps->performance_levels[tonga_ps->performance_level_count - 1].memory_clock;
-
-       if (sclk < minimum_clocks.engineClock)
-               sclk = (minimum_clocks.engineClock > max_limits->sclk) ? max_limits->sclk : minimum_clocks.engineClock;
-
-       if (mclk < minimum_clocks.memoryClock)
-               mclk = (minimum_clocks.memoryClock > max_limits->mclk) ? max_limits->mclk : minimum_clocks.memoryClock;
-
-       tonga_ps->performance_levels[0].engine_clock = sclk;
-       tonga_ps->performance_levels[0].memory_clock = mclk;
-
-       tonga_ps->performance_levels[1].engine_clock =
-               (tonga_ps->performance_levels[1].engine_clock >= tonga_ps->performance_levels[0].engine_clock) ?
-                             tonga_ps->performance_levels[1].engine_clock :
-                             tonga_ps->performance_levels[0].engine_clock;
-
-       if (disable_mclk_switching) {
-               if (mclk < tonga_ps->performance_levels[1].memory_clock)
-                       mclk = tonga_ps->performance_levels[1].memory_clock;
-
-               tonga_ps->performance_levels[0].memory_clock = mclk;
-               tonga_ps->performance_levels[1].memory_clock = mclk;
-       } else {
-               if (tonga_ps->performance_levels[1].memory_clock < tonga_ps->performance_levels[0].memory_clock)
-                       tonga_ps->performance_levels[1].memory_clock = tonga_ps->performance_levels[0].memory_clock;
-       }
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState)) {
-               for (i=0; i < tonga_ps->performance_level_count; i++) {
-                       tonga_ps->performance_levels[i].engine_clock = stable_pstate_sclk;
-                       tonga_ps->performance_levels[i].memory_clock = stable_pstate_mclk;
-                       tonga_ps->performance_levels[i].pcie_gen = data->pcie_gen_performance.max;
-                       tonga_ps->performance_levels[i].pcie_lane = data->pcie_gen_performance.max;
-               }
-       }
-
-       return 0;
-}
-
-int tonga_get_power_state_size(struct pp_hwmgr *hwmgr)
-{
-       return sizeof(struct tonga_power_state);
-}
-
-static int tonga_dpm_get_mclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct tonga_power_state  *tonga_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       tonga_ps = cast_phw_tonga_power_state(&ps->hardware);
-
-       if (low)
-               return tonga_ps->performance_levels[0].memory_clock;
-       else
-               return tonga_ps->performance_levels[tonga_ps->performance_level_count-1].memory_clock;
-}
-
-static int tonga_dpm_get_sclk(struct pp_hwmgr *hwmgr, bool low)
-{
-       struct pp_power_state  *ps;
-       struct tonga_power_state  *tonga_ps;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       tonga_ps = cast_phw_tonga_power_state(&ps->hardware);
-
-       if (low)
-               return tonga_ps->performance_levels[0].engine_clock;
-       else
-               return tonga_ps->performance_levels[tonga_ps->performance_level_count-1].engine_clock;
-}
-
-static uint16_t tonga_get_current_pcie_speed(
-                                                  struct pp_hwmgr *hwmgr)
-{
-       uint32_t speed_cntl = 0;
-
-       speed_cntl = cgs_read_ind_register(hwmgr->device,
-                                                  CGS_IND_REG__PCIE,
-                                                  ixPCIE_LC_SPEED_CNTL);
-       return((uint16_t)PHM_GET_FIELD(speed_cntl,
-                       PCIE_LC_SPEED_CNTL, LC_CURRENT_DATA_RATE));
-}
-
-static int tonga_get_current_pcie_lane_number(
-                                                  struct pp_hwmgr *hwmgr)
-{
-       uint32_t link_width;
-
-       link_width = PHM_READ_INDIRECT_FIELD(hwmgr->device,
-                                                       CGS_IND_REG__PCIE,
-                                                 PCIE_LC_LINK_WIDTH_CNTL,
-                                                       LC_LINK_WIDTH_RD);
-
-       PP_ASSERT_WITH_CODE((7 >= link_width),
-                       "Invalid PCIe lane width!", return 0);
-
-       return decode_pcie_lane_width(link_width);
-}
-
-static int tonga_dpm_patch_boot_state(struct pp_hwmgr *hwmgr,
-                                       struct pp_hw_power_state *hw_ps)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_power_state *ps = (struct tonga_power_state *)hw_ps;
-       ATOM_FIRMWARE_INFO_V2_2 *fw_info;
-       uint16_t size;
-       uint8_t frev, crev;
-       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
-
-       /* First retrieve the Boot clocks and VDDC from the firmware info table.
-        * We assume here that fw_info is unchanged if this call fails.
-        */
-       fw_info = (ATOM_FIRMWARE_INFO_V2_2 *)cgs_atom_get_data_table(
-                       hwmgr->device, index,
-                       &size, &frev, &crev);
-       if (!fw_info)
-               /* During a test, there is no firmware info table. */
-               return 0;
-
-       /* Patch the state. */
-       data->vbios_boot_state.sclk_bootup_value  = le32_to_cpu(fw_info->ulDefaultEngineClock);
-       data->vbios_boot_state.mclk_bootup_value  = le32_to_cpu(fw_info->ulDefaultMemoryClock);
-       data->vbios_boot_state.mvdd_bootup_value  = le16_to_cpu(fw_info->usBootUpMVDDCVoltage);
-       data->vbios_boot_state.vddc_bootup_value  = le16_to_cpu(fw_info->usBootUpVDDCVoltage);
-       data->vbios_boot_state.vddci_bootup_value = le16_to_cpu(fw_info->usBootUpVDDCIVoltage);
-       data->vbios_boot_state.pcie_gen_bootup_value = tonga_get_current_pcie_speed(hwmgr);
-       data->vbios_boot_state.pcie_lane_bootup_value =
-                       (uint16_t)tonga_get_current_pcie_lane_number(hwmgr);
-
-       /* set boot power state */
-       ps->performance_levels[0].memory_clock = data->vbios_boot_state.mclk_bootup_value;
-       ps->performance_levels[0].engine_clock = data->vbios_boot_state.sclk_bootup_value;
-       ps->performance_levels[0].pcie_gen = data->vbios_boot_state.pcie_gen_bootup_value;
-       ps->performance_levels[0].pcie_lane = data->vbios_boot_state.pcie_lane_bootup_value;
-
-       return 0;
-}
-
-static int tonga_get_pp_table_entry_callback_func(struct pp_hwmgr *hwmgr,
-               void *state, struct pp_power_state *power_state,
-               void *pp_table, uint32_t classification_flag)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       struct tonga_power_state  *tonga_ps =
-                       (struct tonga_power_state *)(&(power_state->hardware));
-
-       struct tonga_performance_level *performance_level;
-
-       ATOM_Tonga_State *state_entry = (ATOM_Tonga_State *)state;
-
-       ATOM_Tonga_POWERPLAYTABLE *powerplay_table =
-                       (ATOM_Tonga_POWERPLAYTABLE *)pp_table;
-
-       ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table =
-                       (ATOM_Tonga_SCLK_Dependency_Table *)
-                       (((unsigned long)powerplay_table) +
-                       le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
-
-       ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
-                       (ATOM_Tonga_MCLK_Dependency_Table *)
-                       (((unsigned long)powerplay_table) +
-                       le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
-
-       /* The following fields are not initialized here: id orderedList allStatesList */
-       power_state->classification.ui_label =
-                       (le16_to_cpu(state_entry->usClassification) &
-                       ATOM_PPLIB_CLASSIFICATION_UI_MASK) >>
-                       ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
-       power_state->classification.flags = classification_flag;
-       /* NOTE: There is a classification2 flag in BIOS that is not being used right now */
-
-       power_state->classification.temporary_state = false;
-       power_state->classification.to_be_deleted = false;
-
-       power_state->validation.disallowOnDC =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) & ATOM_Tonga_DISALLOW_ON_DC));
-
-       power_state->pcie.lanes = 0;
-
-       power_state->display.disableFrameModulation = false;
-       power_state->display.limitRefreshrate = false;
-       power_state->display.enableVariBright =
-                       (0 != (le32_to_cpu(state_entry->ulCapsAndSettings) & ATOM_Tonga_ENABLE_VARIBRIGHT));
-
-       power_state->validation.supportedPowerLevels = 0;
-       power_state->uvd_clocks.VCLK = 0;
-       power_state->uvd_clocks.DCLK = 0;
-       power_state->temperatures.min = 0;
-       power_state->temperatures.max = 0;
-
-       performance_level = &(tonga_ps->performance_levels
-                       [tonga_ps->performance_level_count++]);
-
-       PP_ASSERT_WITH_CODE(
-                       (tonga_ps->performance_level_count < SMU72_MAX_LEVELS_GRAPHICS),
-                       "Performance levels exceeds SMC limit!",
-                       return -1);
-
-       PP_ASSERT_WITH_CODE(
-                       (tonga_ps->performance_level_count <=
-                                       hwmgr->platform_descriptor.hardwareActivityPerformanceLevels),
-                       "Performance levels exceeds Driver limit!",
-                       return -1);
-
-       /* Performance levels are arranged from low to high. */
-       performance_level->memory_clock =
-                               le32_to_cpu(mclk_dep_table->entries[state_entry->ucMemoryClockIndexLow].ulMclk);
-
-       performance_level->engine_clock =
-                               le32_to_cpu(sclk_dep_table->entries[state_entry->ucEngineClockIndexLow].ulSclk);
-
-       performance_level->pcie_gen = get_pcie_gen_support(
-                                                       data->pcie_gen_cap,
-                                            state_entry->ucPCIEGenLow);
-
-       performance_level->pcie_lane = get_pcie_lane_support(
-                                                   data->pcie_lane_cap,
-                                          state_entry->ucPCIELaneHigh);
-
-       performance_level =
-                       &(tonga_ps->performance_levels[tonga_ps->performance_level_count++]);
-
-       performance_level->memory_clock =
-                               le32_to_cpu(mclk_dep_table->entries[state_entry->ucMemoryClockIndexHigh].ulMclk);
-
-       performance_level->engine_clock =
-                               le32_to_cpu(sclk_dep_table->entries[state_entry->ucEngineClockIndexHigh].ulSclk);
-
-       performance_level->pcie_gen = get_pcie_gen_support(
-                                                       data->pcie_gen_cap,
-                                           state_entry->ucPCIEGenHigh);
-
-       performance_level->pcie_lane = get_pcie_lane_support(
-                                                   data->pcie_lane_cap,
-                                          state_entry->ucPCIELaneHigh);
-
-       return 0;
-}
-
-static int tonga_get_pp_table_entry(struct pp_hwmgr *hwmgr,
-                   unsigned long entry_index, struct pp_power_state *ps)
-{
-       int result;
-       struct tonga_power_state *tonga_ps;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       struct phm_ppt_v1_information *table_info =
-                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       struct phm_ppt_v1_clock_voltage_dependency_table *dep_mclk_table =
-                                          table_info->vdd_dep_on_mclk;
-
-       ps->hardware.magic = PhwTonga_Magic;
-
-       tonga_ps = cast_phw_tonga_power_state(&(ps->hardware));
-
-       result = tonga_get_powerplay_table_entry(hwmgr, entry_index, ps,
-                       tonga_get_pp_table_entry_callback_func);
-
-       /* This is the earliest time we have all the dependency table and the VBIOS boot state
-        * as PP_Tables_GetPowerPlayTableEntry retrieves the VBIOS boot state
-        * if there is only one VDDCI/MCLK level, check if it's the same as VBIOS boot state
-        */
-       if (dep_mclk_table != NULL && dep_mclk_table->count == 1) {
-               if (dep_mclk_table->entries[0].clk !=
-                               data->vbios_boot_state.mclk_bootup_value)
-                       printk(KERN_ERR "Single MCLK entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot MCLK level");
-               if (dep_mclk_table->entries[0].vddci !=
-                               data->vbios_boot_state.vddci_bootup_value)
-                       printk(KERN_ERR "Single VDDCI entry VDDCI/MCLK dependency table "
-                                       "does not match VBIOS boot VDDCI level");
-       }
-
-       /* set DC compatible flag if this state supports DC */
-       if (!ps->validation.disallowOnDC)
-               tonga_ps->dc_compatible = true;
-
-       if (ps->classification.flags & PP_StateClassificationFlag_ACPI)
-               data->acpi_pcie_gen = tonga_ps->performance_levels[0].pcie_gen;
-       else if (ps->classification.flags & PP_StateClassificationFlag_Boot) {
-               if (data->bacos.best_match == 0xffff) {
-                       /* For V.I. use boot state as base BACO state */
-                       data->bacos.best_match = PP_StateClassificationFlag_Boot;
-                       data->bacos.performance_level = tonga_ps->performance_levels[0];
-               }
-       }
-
-       tonga_ps->uvd_clocks.VCLK = ps->uvd_clocks.VCLK;
-       tonga_ps->uvd_clocks.DCLK = ps->uvd_clocks.DCLK;
-
-       if (!result) {
-               uint32_t i;
-
-               switch (ps->classification.ui_label) {
-               case PP_StateUILabel_Performance:
-                       data->use_pcie_performance_levels = true;
-
-                       for (i = 0; i < tonga_ps->performance_level_count; i++) {
-                               if (data->pcie_gen_performance.max <
-                                               tonga_ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.max =
-                                                       tonga_ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_performance.min >
-                                               tonga_ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_performance.min =
-                                                       tonga_ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_performance.max <
-                                               tonga_ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.max =
-                                                       tonga_ps->performance_levels[i].pcie_lane;
-
-                               if (data->pcie_lane_performance.min >
-                                               tonga_ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_performance.min =
-                                                       tonga_ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               case PP_StateUILabel_Battery:
-                       data->use_pcie_power_saving_levels = true;
-
-                       for (i = 0; i < tonga_ps->performance_level_count; i++) {
-                               if (data->pcie_gen_power_saving.max <
-                                               tonga_ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.max =
-                                                       tonga_ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_gen_power_saving.min >
-                                               tonga_ps->performance_levels[i].pcie_gen)
-                                       data->pcie_gen_power_saving.min =
-                                                       tonga_ps->performance_levels[i].pcie_gen;
-
-                               if (data->pcie_lane_power_saving.max <
-                                               tonga_ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.max =
-                                                       tonga_ps->performance_levels[i].pcie_lane;
-
-                               if (data->pcie_lane_power_saving.min >
-                                               tonga_ps->performance_levels[i].pcie_lane)
-                                       data->pcie_lane_power_saving.min =
-                                                       tonga_ps->performance_levels[i].pcie_lane;
-                       }
-                       break;
-               default:
-                       break;
-               }
-       }
-       return 0;
-}
-
-static void
-tonga_print_current_perforce_level(struct pp_hwmgr *hwmgr, struct seq_file *m)
-{
-       uint32_t sclk, mclk, activity_percent;
-       uint32_t offset;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)(PPSMC_MSG_API_GetSclkFrequency));
-
-       sclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-       smum_send_msg_to_smc(hwmgr->smumgr, (PPSMC_Msg)(PPSMC_MSG_API_GetMclkFrequency));
-
-       mclk = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-       seq_printf(m, "\n [  mclk  ]: %u MHz\n\n [  sclk  ]: %u MHz\n", mclk/100, sclk/100);
-
-       offset = data->soft_regs_start + offsetof(SMU72_SoftRegisters, AverageGraphicsActivity);
-       activity_percent = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, offset);
-       activity_percent += 0x80;
-       activity_percent >>= 8;
-
-       seq_printf(m, "\n [GPU load]: %u%%\n\n", activity_percent > 100 ? 100 : activity_percent);
-
-       seq_printf(m, "uvd    %sabled\n", data->uvd_power_gated ? "dis" : "en");
-
-       seq_printf(m, "vce    %sabled\n", data->vce_power_gated ? "dis" : "en");
-}
-
-static int tonga_find_dpm_states_clocks_in_dpm_table(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       const struct tonga_power_state *tonga_ps = cast_const_phw_tonga_power_state(states->pnew_state);
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *psclk_table = &(data->dpm_table.sclk_table);
-       uint32_t sclk = tonga_ps->performance_levels[tonga_ps->performance_level_count-1].engine_clock;
-       struct tonga_single_dpm_table *pmclk_table = &(data->dpm_table.mclk_table);
-       uint32_t mclk = tonga_ps->performance_levels[tonga_ps->performance_level_count-1].memory_clock;
-       struct PP_Clocks min_clocks = {0};
-       uint32_t i;
-       struct cgs_display_info info = {0};
-
-       data->need_update_smu7_dpm_table = 0;
-
-       for (i = 0; i < psclk_table->count; i++) {
-               if (sclk == psclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= psclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_SCLK;
-       else {
-       /* TODO: Check SCLK in DAL's minimum clocks in case DeepSleep divider update is required.*/
-               if(data->display_timing.min_clock_insr != min_clocks.engineClockInSR)
-                       data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_SCLK;
-       }
-
-       for (i=0; i < pmclk_table->count; i++) {
-               if (mclk == pmclk_table->dpm_levels[i].value)
-                       break;
-       }
-
-       if (i >= pmclk_table->count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_OD_UPDATE_MCLK;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               data->need_update_smu7_dpm_table |= DPMTABLE_UPDATE_MCLK;
-
-       return 0;
-}
-
-static uint16_t tonga_get_maximum_link_speed(struct pp_hwmgr *hwmgr, const struct tonga_power_state *hw_ps)
-{
-       uint32_t i;
-       uint32_t sclk, max_sclk = 0;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_dpm_table *pdpm_table = &data->dpm_table;
-
-       for (i = 0; i < hw_ps->performance_level_count; i++) {
-               sclk = hw_ps->performance_levels[i].engine_clock;
-               if (max_sclk < sclk)
-                       max_sclk = sclk;
-       }
-
-       for (i = 0; i < pdpm_table->sclk_table.count; i++) {
-               if (pdpm_table->sclk_table.dpm_levels[i].value == max_sclk)
-                       return (uint16_t) ((i >= pdpm_table->pcie_speed_table.count) ?
-                                       pdpm_table->pcie_speed_table.dpm_levels[pdpm_table->pcie_speed_table.count-1].value :
-                                       pdpm_table->pcie_speed_table.dpm_levels[i].value);
-       }
-
-       return 0;
-}
-
-static int tonga_request_link_speed_change_before_state_change(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       const struct tonga_power_state *tonga_nps = cast_const_phw_tonga_power_state(states->pnew_state);
-       const struct tonga_power_state *tonga_cps = cast_const_phw_tonga_power_state(states->pcurrent_state);
-
-       uint16_t target_link_speed = tonga_get_maximum_link_speed(hwmgr, tonga_nps);
-       uint16_t current_link_speed;
-
-       if (data->force_pcie_gen == PP_PCIEGenInvalid)
-               current_link_speed = tonga_get_maximum_link_speed(hwmgr, tonga_cps);
-       else
-               current_link_speed = data->force_pcie_gen;
-
-       data->force_pcie_gen = PP_PCIEGenInvalid;
-       data->pspp_notify_required = false;
-       if (target_link_speed > current_link_speed) {
-               switch(target_link_speed) {
-               case PP_PCIEGen3:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN3, false))
-                               break;
-                       data->force_pcie_gen = PP_PCIEGen2;
-                       if (current_link_speed == PP_PCIEGen2)
-                               break;
-               case PP_PCIEGen2:
-                       if (0 == acpi_pcie_perf_request(hwmgr->device, PCIE_PERF_REQ_GEN2, false))
-                               break;
-               default:
-                       data->force_pcie_gen = tonga_get_current_pcie_speed(hwmgr);
-                       break;
-               }
-       } else {
-               if (target_link_speed < current_link_speed)
-                       data->pspp_notify_required = true;
-       }
-
-       return 0;
-}
-
-static int tonga_freeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-               PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                                   "Trying to freeze SCLK DPM when DPM is disabled",
-                       );
-               PP_ASSERT_WITH_CODE(
-                       0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                         PPSMC_MSG_SCLKDPM_FreezeLevel),
-                       "Failed to freeze SCLK DPM during FreezeSclkMclkDPM Function!",
-                       return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-                DPMTABLE_OD_UPDATE_MCLK)) {
-               PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                                   "Trying to freeze MCLK DPM when DPM is disabled",
-                       );
-               PP_ASSERT_WITH_CODE(
-                       0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                                       PPSMC_MSG_MCLKDPM_FreezeLevel),
-                       "Failed to freeze MCLK DPM during FreezeSclkMclkDPM Function!",
-                       return -1);
-       }
-
-       return 0;
-}
-
-static int tonga_populate_and_upload_sclk_mclk_dpm_levels(struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result = 0;
-
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       const struct tonga_power_state *tonga_ps = cast_const_phw_tonga_power_state(states->pnew_state);
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t sclk = tonga_ps->performance_levels[tonga_ps->performance_level_count-1].engine_clock;
-       uint32_t mclk = tonga_ps->performance_levels[tonga_ps->performance_level_count-1].memory_clock;
-       struct tonga_dpm_table *pdpm_table = &data->dpm_table;
-
-       struct tonga_dpm_table *pgolden_dpm_table = &data->golden_dpm_table;
-       uint32_t dpm_count, clock_percent;
-       uint32_t i;
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_SCLK) {
-               pdpm_table->sclk_table.dpm_levels[pdpm_table->sclk_table.count-1].value = sclk;
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
-                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
-               /* Need to do calculation based on the golden DPM table
-                * as the Heatmap GPU Clock axis is also based on the default values
-                */
-                       PP_ASSERT_WITH_CODE(
-                               (pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value != 0),
-                               "Divide by 0!",
-                               return -1);
-                       dpm_count = pdpm_table->sclk_table.count < 2 ? 0 : pdpm_table->sclk_table.count-2;
-                       for (i = dpm_count; i > 1; i--) {
-                               if (sclk > pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value) {
-                                       clock_percent = ((sclk - pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value)*100) /
-                                                       pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value;
-
-                                       pdpm_table->sclk_table.dpm_levels[i].value =
-                                                       pgolden_dpm_table->sclk_table.dpm_levels[i].value +
-                                                       (pgolden_dpm_table->sclk_table.dpm_levels[i].value * clock_percent)/100;
-
-                               } else if (pgolden_dpm_table->sclk_table.dpm_levels[pdpm_table->sclk_table.count-1].value > sclk) {
-                                       clock_percent = ((pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value - sclk)*100) /
-                                                               pgolden_dpm_table->sclk_table.dpm_levels[pgolden_dpm_table->sclk_table.count-1].value;
-
-                                       pdpm_table->sclk_table.dpm_levels[i].value =
-                                                       pgolden_dpm_table->sclk_table.dpm_levels[i].value -
-                                                       (pgolden_dpm_table->sclk_table.dpm_levels[i].value * clock_percent)/100;
-                               } else
-                                       pdpm_table->sclk_table.dpm_levels[i].value =
-                                                       pgolden_dpm_table->sclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK) {
-               pdpm_table->mclk_table.dpm_levels[pdpm_table->mclk_table.count-1].value = mclk;
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinACSupport) ||
-                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_OD6PlusinDCSupport)) {
-
-                       PP_ASSERT_WITH_CODE(
-                                       (pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value != 0),
-                                       "Divide by 0!",
-                                       return -1);
-                       dpm_count = pdpm_table->mclk_table.count < 2? 0 : pdpm_table->mclk_table.count-2;
-                       for (i = dpm_count; i > 1; i--) {
-                               if (mclk > pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value) {
-                                               clock_percent = ((mclk - pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value)*100) /
-                                                                   pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value;
-
-                                               pdpm_table->mclk_table.dpm_levels[i].value =
-                                                                               pgolden_dpm_table->mclk_table.dpm_levels[i].value +
-                                                                               (pgolden_dpm_table->mclk_table.dpm_levels[i].value * clock_percent)/100;
-
-                               } else if (pgolden_dpm_table->mclk_table.dpm_levels[pdpm_table->mclk_table.count-1].value > mclk) {
-                                               clock_percent = ((pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value - mclk)*100) /
-                                                                   pgolden_dpm_table->mclk_table.dpm_levels[pgolden_dpm_table->mclk_table.count-1].value;
-
-                                               pdpm_table->mclk_table.dpm_levels[i].value =
-                                                                       pgolden_dpm_table->mclk_table.dpm_levels[i].value -
-                                                                       (pgolden_dpm_table->mclk_table.dpm_levels[i].value * clock_percent)/100;
-                               } else
-                                       pdpm_table->mclk_table.dpm_levels[i].value = pgolden_dpm_table->mclk_table.dpm_levels[i].value;
-                       }
-               }
-       }
-
-       if (data->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK)) {
-               result = tonga_populate_all_graphic_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                       "Failed to populate SCLK during PopulateNewDPMClocksStates Function!",
-                       return result);
-       }
-
-       if (data->need_update_smu7_dpm_table & (DPMTABLE_OD_UPDATE_MCLK + DPMTABLE_UPDATE_MCLK)) {
-               /*populate MCLK dpm table to SMU7 */
-               result = tonga_populate_all_memory_levels(hwmgr);
-               PP_ASSERT_WITH_CODE((0 == result),
-                               "Failed to populate MCLK during PopulateNewDPMClocksStates Function!",
-                               return result);
-       }
-
-       return result;
-}
-
-static int tonga_trim_single_dpm_states(struct pp_hwmgr *hwmgr,
-                         struct tonga_single_dpm_table * pdpm_table,
-                            uint32_t low_limit, uint32_t high_limit)
-{
-       uint32_t i;
-
-       for (i = 0; i < pdpm_table->count; i++) {
-               if ((pdpm_table->dpm_levels[i].value < low_limit) ||
-                   (pdpm_table->dpm_levels[i].value > high_limit))
-                       pdpm_table->dpm_levels[i].enabled = false;
-               else
-                       pdpm_table->dpm_levels[i].enabled = true;
-       }
-       return 0;
-}
-
-static int tonga_trim_dpm_states(struct pp_hwmgr *hwmgr, const struct tonga_power_state *hw_state)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t high_limit_count;
-
-       PP_ASSERT_WITH_CODE((hw_state->performance_level_count >= 1),
-                               "power state did not have any performance level",
-                                return -1);
-
-       high_limit_count = (1 == hw_state->performance_level_count) ? 0: 1;
-
-       tonga_trim_single_dpm_states(hwmgr,
-                                       &(data->dpm_table.sclk_table),
-                                       hw_state->performance_levels[0].engine_clock,
-                                       hw_state->performance_levels[high_limit_count].engine_clock);
-
-       tonga_trim_single_dpm_states(hwmgr,
-                                               &(data->dpm_table.mclk_table),
-                                               hw_state->performance_levels[0].memory_clock,
-                                               hw_state->performance_levels[high_limit_count].memory_clock);
-
-       return 0;
-}
-
-static int tonga_generate_dpm_level_enable_mask(struct pp_hwmgr *hwmgr, const void *input)
-{
-       int result;
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       const struct tonga_power_state *tonga_ps = cast_const_phw_tonga_power_state(states->pnew_state);
-
-       result = tonga_trim_dpm_states(hwmgr, tonga_ps);
-       if (0 != result)
-               return result;
-
-       data->dpm_level_enable_mask.sclk_dpm_enable_mask = tonga_get_dpm_level_enable_mask_value(&data->dpm_table.sclk_table);
-       data->dpm_level_enable_mask.mclk_dpm_enable_mask = tonga_get_dpm_level_enable_mask_value(&data->dpm_table.mclk_table);
-       data->last_mclk_dpm_enable_mask = data->dpm_level_enable_mask.mclk_dpm_enable_mask;
-       if (data->uvd_enabled)
-               data->dpm_level_enable_mask.mclk_dpm_enable_mask &= 0xFFFFFFFE;
-
-       data->dpm_level_enable_mask.pcie_dpm_enable_mask = tonga_get_dpm_level_enable_mask_value(&data->dpm_table.pcie_speed_table);
-
-       return 0;
-}
-
-int tonga_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
-                                 (PPSMC_Msg)PPSMC_MSG_VCEDPM_Enable :
-                                 (PPSMC_Msg)PPSMC_MSG_VCEDPM_Disable);
-}
-
-int tonga_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable)
-{
-       return smum_send_msg_to_smc(hwmgr->smumgr, enable ?
-                                 (PPSMC_Msg)PPSMC_MSG_UVDDPM_Enable :
-                                 (PPSMC_Msg)PPSMC_MSG_UVDDPM_Disable);
-}
-
-int tonga_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *ptable_information = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (!bgate) {
-               data->smc_state_table.UvdBootLevel = (uint8_t) (ptable_information->mm_dep_table->count - 1);
-               mm_boot_level_offset = data->dpm_table_start + offsetof(SMU72_Discrete_DpmTable, UvdBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0x00FFFFFF;
-               mm_boot_level_value |= data->smc_state_table.UvdBootLevel << 24;
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_UVDDPM) ||
-                   phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                               PPSMC_MSG_UVDDPM_SetEnabledMask,
-                                               (uint32_t)(1 << data->smc_state_table.UvdBootLevel));
-       }
-
-       return tonga_enable_disable_uvd_dpm(hwmgr, !bgate);
-}
-
-int tonga_update_vce_dpm(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       const struct tonga_power_state *tonga_nps = cast_const_phw_tonga_power_state(states->pnew_state);
-       const struct tonga_power_state *tonga_cps = cast_const_phw_tonga_power_state(states->pcurrent_state);
-
-       uint32_t mm_boot_level_offset, mm_boot_level_value;
-       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (tonga_nps->vce_clocks.EVCLK > 0 && (tonga_cps == NULL || tonga_cps->vce_clocks.EVCLK == 0)) {
-               data->smc_state_table.VceBootLevel = (uint8_t) (pptable_info->mm_dep_table->count - 1);
-
-               mm_boot_level_offset = data->dpm_table_start + offsetof(SMU72_Discrete_DpmTable, VceBootLevel);
-               mm_boot_level_offset /= 4;
-               mm_boot_level_offset *= 4;
-               mm_boot_level_value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, mm_boot_level_offset);
-               mm_boot_level_value &= 0xFF00FFFF;
-               mm_boot_level_value |= data->smc_state_table.VceBootLevel << 16;
-               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
-
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_VCEDPM_SetEnabledMask,
-                               (uint32_t)(1 << data->smc_state_table.VceBootLevel));
-
-               tonga_enable_disable_vce_dpm(hwmgr, true);
-       } else if (tonga_nps->vce_clocks.EVCLK == 0 && tonga_cps != NULL && tonga_cps->vce_clocks.EVCLK > 0)
-               tonga_enable_disable_vce_dpm(hwmgr, false);
-
-       return 0;
-}
-
-static int tonga_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       uint32_t address;
-       int32_t result;
-
-       if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
-               return 0;
-
-
-       memset(&data->mc_reg_table, 0, sizeof(SMU72_Discrete_MCRegisters));
-
-       result = tonga_convert_mc_reg_table_to_smc(hwmgr, &(data->mc_reg_table));
-
-       if(result != 0)
-               return result;
-
-
-       address = data->mc_reg_table_start + (uint32_t)offsetof(SMU72_Discrete_MCRegisters, data[0]);
-
-       return  tonga_copy_bytes_to_smc(hwmgr->smumgr, address,
-                                (uint8_t *)&data->mc_reg_table.data[0],
-                               sizeof(SMU72_Discrete_MCRegisterSet) * data->dpm_table.mclk_table.count,
-                               data->sram_end);
-}
-
-static int tonga_program_memory_timing_parameters_conditionally(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       if (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
-               return tonga_program_memory_timing_parameters(hwmgr);
-
-       return 0;
-}
-
-static int tonga_unfreeze_sclk_mclk_dpm(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       if (0 == data->need_update_smu7_dpm_table)
-               return 0;
-
-       if ((0 == data->sclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table &
-               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_UPDATE_SCLK))) {
-
-               PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze SCLK DPM when DPM is disabled",
-                       );
-               PP_ASSERT_WITH_CODE(
-                        0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                        PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                       "Failed to unfreeze SCLK DPM during UnFreezeSclkMclkDPM Function!",
-                       return -1);
-       }
-
-       if ((0 == data->mclk_dpm_key_disabled) &&
-               (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK)) {
-
-               PP_ASSERT_WITH_CODE(!tonga_is_dpm_running(hwmgr),
-                                   "Trying to Unfreeze MCLK DPM when DPM is disabled",
-                               );
-               PP_ASSERT_WITH_CODE(
-                        0 == smum_send_msg_to_smc(hwmgr->smumgr,
-                                        PPSMC_MSG_SCLKDPM_UnfreezeLevel),
-                   "Failed to unfreeze MCLK DPM during UnFreezeSclkMclkDPM Function!",
-                   return -1);
-       }
-
-       data->need_update_smu7_dpm_table = 0;
-
-       return 0;
-}
-
-static int tonga_notify_link_speed_change_after_state_change(struct pp_hwmgr *hwmgr, const void *input)
-{
-       const struct phm_set_power_state_input *states = (const struct phm_set_power_state_input *)input;
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       const struct tonga_power_state *tonga_ps = cast_const_phw_tonga_power_state(states->pnew_state);
-       uint16_t target_link_speed = tonga_get_maximum_link_speed(hwmgr, tonga_ps);
-       uint8_t  request;
-
-       if (data->pspp_notify_required  ||
-           data->pcie_performance_request) {
-               if (target_link_speed == PP_PCIEGen3)
-                       request = PCIE_PERF_REQ_GEN3;
-               else if (target_link_speed == PP_PCIEGen2)
-                       request = PCIE_PERF_REQ_GEN2;
-               else
-                       request = PCIE_PERF_REQ_GEN1;
-
-               if(request == PCIE_PERF_REQ_GEN1 && tonga_get_current_pcie_speed(hwmgr) > 0) {
-                       data->pcie_performance_request = false;
-                       return 0;
-               }
-
-               if (0 != acpi_pcie_perf_request(hwmgr->device, request, false)) {
-                       if (PP_PCIEGen2 == target_link_speed)
-                               printk("PSPP request to switch to Gen2 from Gen3 Failed!");
-                       else
-                               printk("PSPP request to switch to Gen1 from Gen2 Failed!");
-               }
-       }
-
-       data->pcie_performance_request = false;
-       return 0;
-}
-
-static int tonga_set_power_state_tasks(struct pp_hwmgr *hwmgr, const void *input)
-{
-       int tmp_result, result = 0;
-
-       tmp_result = tonga_find_dpm_states_clocks_in_dpm_table(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to find DPM states clocks in DPM table!", result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result = tonga_request_link_speed_change_before_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to request link speed change before state change!", result = tmp_result);
-       }
-
-       tmp_result = tonga_freeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to freeze SCLK MCLK DPM!", result = tmp_result);
-
-       tmp_result = tonga_populate_and_upload_sclk_mclk_dpm_levels(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to populate and upload SCLK MCLK DPM levels!", result = tmp_result);
-
-       tmp_result = tonga_generate_dpm_level_enable_mask(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to generate DPM level enabled mask!", result = tmp_result);
-
-       tmp_result = tonga_update_vce_dpm(hwmgr, input);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to update VCE DPM!", result = tmp_result);
-
-       tmp_result = tonga_update_sclk_threshold(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to update SCLK threshold!", result = tmp_result);
-
-       tmp_result = tonga_update_and_upload_mc_reg_table(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to upload MC reg table!", result = tmp_result);
-
-       tmp_result = tonga_program_memory_timing_parameters_conditionally(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to program memory timing parameters!", result = tmp_result);
-
-       tmp_result = tonga_unfreeze_sclk_mclk_dpm(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to unfreeze SCLK MCLK DPM!", result = tmp_result);
-
-       tmp_result = tonga_upload_dpm_level_enable_mask(hwmgr);
-       PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to upload DPM level enabled mask!", result = tmp_result);
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PCIEPerformanceRequest)) {
-               tmp_result = tonga_notify_link_speed_change_after_state_change(hwmgr, input);
-               PP_ASSERT_WITH_CODE((0 == tmp_result), "Failed to notify link speed change after state change!", result = tmp_result);
-       }
-
-       return result;
-}
-
-/**
-*  Set maximum target operating fan output PWM
-*
-* @param    pHwMgr:  the address of the powerplay hardware manager.
-* @param    usMaxFanPwm:  max operating fan PWM in percents
-* @return   The response that came from the SMC.
-*/
-static int tonga_set_max_fan_pwm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_pwm)
-{
-       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM = us_max_fan_pwm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return (0 == smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_SetFanPwmMax, us_max_fan_pwm) ? 0 : -1);
-}
-
-int tonga_notify_smc_display_config_after_ps_adjustment(struct pp_hwmgr *hwmgr)
-{
-       uint32_t num_active_displays = 0;
-       struct cgs_display_info info = {0};
-       info.mode_info = NULL;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       num_active_displays = info.display_count;
-
-       if (num_active_displays > 1)  /* to do && (pHwMgr->pPECI->displayConfiguration.bMultiMonitorInSync != TRUE)) */
-               tonga_notify_smc_display_change(hwmgr, false);
-       else
-               tonga_notify_smc_display_change(hwmgr, true);
-
-       return 0;
-}
-
-/**
-* Programs the display gap
-*
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @return   always OK
-*/
-int tonga_program_display_gap(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       uint32_t num_active_displays = 0;
-       uint32_t display_gap = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL);
-       uint32_t display_gap2;
-       uint32_t pre_vbi_time_in_us;
-       uint32_t frame_time_in_us;
-       uint32_t ref_clock;
-       uint32_t refresh_rate = 0;
-       struct cgs_display_info info = {0};
-       struct cgs_mode_info mode_info;
-
-       info.mode_info = &mode_info;
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-       num_active_displays = info.display_count;
-
-       display_gap = PHM_SET_FIELD(display_gap, CG_DISPLAY_GAP_CNTL, DISP_GAP, (num_active_displays > 0)? DISPLAY_GAP_VBLANK_OR_WM : DISPLAY_GAP_IGNORE);
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL, display_gap);
-
-       ref_clock = mode_info.ref_clock;
-       refresh_rate = mode_info.refresh_rate;
-
-       if(0 == refresh_rate)
-               refresh_rate = 60;
-
-       frame_time_in_us = 1000000 / refresh_rate;
-
-       pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
-       display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_DISPLAY_GAP_CNTL2, display_gap2);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU72_SoftRegisters, PreVBlankGap), 0x64);
-
-       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, data->soft_regs_start + offsetof(SMU72_SoftRegisters, VBlankTimeout), (frame_time_in_us - pre_vbi_time_in_us));
-
-       if (num_active_displays == 1)
-               tonga_notify_smc_display_change(hwmgr, true);
-
-       return 0;
-}
-
-int tonga_display_configuration_changed_task(struct pp_hwmgr *hwmgr)
-{
-
-       tonga_program_display_gap(hwmgr);
-
-       /* to do PhwTonga_CacUpdateDisplayConfiguration(pHwMgr); */
-       return 0;
-}
-
-/**
-*  Set maximum target operating fan output RPM
-*
-* @param    pHwMgr:  the address of the powerplay hardware manager.
-* @param    usMaxFanRpm:  max operating fan RPM value.
-* @return   The response that came from the SMC.
-*/
-static int tonga_set_max_fan_rpm_output(struct pp_hwmgr *hwmgr, uint16_t us_max_fan_pwm)
-{
-       hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM = us_max_fan_pwm;
-
-       if (phm_is_hw_access_blocked(hwmgr))
-               return 0;
-
-       return (0 == smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_SetFanRpmMax, us_max_fan_pwm) ? 0 : -1);
-}
-
-uint32_t tonga_get_xclk(struct pp_hwmgr *hwmgr)
-{
-       uint32_t reference_clock;
-       uint32_t tc;
-       uint32_t divide;
-
-       ATOM_FIRMWARE_INFO *fw_info;
-       uint16_t size;
-       uint8_t frev, crev;
-       int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
-
-       tc = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL_2, MUX_TCLK_TO_XCLK);
-
-       if (tc)
-               return TCLK;
-
-       fw_info = (ATOM_FIRMWARE_INFO *)cgs_atom_get_data_table(hwmgr->device, index,
-                                                 &size, &frev, &crev);
-
-       if (!fw_info)
-               return 0;
-
-       reference_clock = le16_to_cpu(fw_info->usReferenceClock);
-
-       divide = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_CLKPIN_CNTL, XTALIN_DIVIDE);
-
-       if (0 != divide)
-               return reference_clock / 4;
-
-       return reference_clock;
-}
-
-int tonga_dpm_set_interrupt_state(void *private_data,
-                                        unsigned src_id, unsigned type,
-                                        int enabled)
-{
-       uint32_t cg_thermal_int;
-       struct pp_hwmgr *hwmgr = ((struct pp_eventmgr *)private_data)->hwmgr;
-
-       if (hwmgr == NULL)
-               return -EINVAL;
-
-       switch (type) {
-       case AMD_THERMAL_IRQ_LOW_TO_HIGH:
-               if (enabled) {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               } else {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               }
-               break;
-
-       case AMD_THERMAL_IRQ_HIGH_TO_LOW:
-               if (enabled) {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int |= CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               } else {
-                       cg_thermal_int = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT);
-                       cg_thermal_int &= ~CG_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK;
-                       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixCG_THERMAL_INT, cg_thermal_int);
-               }
-               break;
-       default:
-               break;
-       }
-       return 0;
-}
-
-int tonga_register_internal_thermal_interrupt(struct pp_hwmgr *hwmgr,
-                                       const void *thermal_interrupt_info)
-{
-       int result;
-       const struct pp_interrupt_registration_info *info =
-                       (const struct pp_interrupt_registration_info *)thermal_interrupt_info;
-
-       if (info == NULL)
-               return -EINVAL;
-
-       result = cgs_add_irq_source(hwmgr->device, 230, AMD_THERMAL_IRQ_LAST,
-                               tonga_dpm_set_interrupt_state,
-                               info->call_back, info->context);
-
-       if (result)
-               return -EINVAL;
-
-       result = cgs_add_irq_source(hwmgr->device, 231, AMD_THERMAL_IRQ_LAST,
-                               tonga_dpm_set_interrupt_state,
-                               info->call_back, info->context);
-
-       if (result)
-               return -EINVAL;
-
-       return 0;
-}
-
-bool tonga_check_smc_update_required_for_display_configuration(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       bool is_update_required = false;
-       struct cgs_display_info info = {0,0,NULL};
-
-       cgs_get_active_displays_info(hwmgr->device, &info);
-
-       if (data->display_timing.num_existing_displays != info.display_count)
-               is_update_required = true;
-/* TO DO NEED TO GET DEEP SLEEP CLOCK FROM DAL
-       if (phm_cap_enabled(hwmgr->hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep)) {
-               cgs_get_min_clock_settings(hwmgr->device, &min_clocks);
-               if(min_clocks.engineClockInSR != data->display_timing.minClockInSR)
-                       is_update_required = true;
-*/
-       return is_update_required;
-}
-
-static inline bool tonga_are_power_levels_equal(const struct tonga_performance_level *pl1,
-                                                          const struct tonga_performance_level *pl2)
-{
-       return ((pl1->memory_clock == pl2->memory_clock) &&
-                 (pl1->engine_clock == pl2->engine_clock) &&
-                 (pl1->pcie_gen == pl2->pcie_gen) &&
-                 (pl1->pcie_lane == pl2->pcie_lane));
-}
-
-int tonga_check_states_equal(struct pp_hwmgr *hwmgr, const struct pp_hw_power_state *pstate1, const struct pp_hw_power_state *pstate2, bool *equal)
-{
-       const struct tonga_power_state *psa = cast_const_phw_tonga_power_state(pstate1);
-       const struct tonga_power_state *psb = cast_const_phw_tonga_power_state(pstate2);
-       int i;
-
-       if (equal == NULL || psa == NULL || psb == NULL)
-               return -EINVAL;
-
-       /* If the two states don't even have the same number of performance levels they cannot be the same state. */
-       if (psa->performance_level_count != psb->performance_level_count) {
-               *equal = false;
-               return 0;
-       }
-
-       for (i = 0; i < psa->performance_level_count; i++) {
-               if (!tonga_are_power_levels_equal(&(psa->performance_levels[i]), &(psb->performance_levels[i]))) {
-                       /* If we have found even one performance level pair that is different the states are different. */
-                       *equal = false;
-                       return 0;
-               }
-       }
-
-       /* If all performance levels are the same try to use the UVD clocks to break the tie.*/
-       *equal = ((psa->uvd_clocks.VCLK == psb->uvd_clocks.VCLK) && (psa->uvd_clocks.DCLK == psb->uvd_clocks.DCLK));
-       *equal &= ((psa->vce_clocks.EVCLK == psb->vce_clocks.EVCLK) && (psa->vce_clocks.ECCLK == psb->vce_clocks.ECCLK));
-       *equal &= (psa->sclk_threshold == psb->sclk_threshold);
-       *equal &= (psa->acp_clk == psb->acp_clk);
-
-       return 0;
-}
-
-static int tonga_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-       if (mode) {
-               /* stop auto-manage */
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
-                               PHM_PlatformCaps_MicrocodeFanControl))
-                       tonga_fan_ctrl_stop_smc_fan_control(hwmgr);
-               tonga_fan_ctrl_set_static_mode(hwmgr, mode);
-       } else
-               /* restart auto-manage */
-               tonga_fan_ctrl_reset_fan_speed_to_default(hwmgr);
-
-       return 0;
-}
-
-static int tonga_get_fan_control_mode(struct pp_hwmgr *hwmgr)
-{
-       if (hwmgr->fan_ctrl_is_in_default_mode)
-               return hwmgr->fan_ctrl_default_mode;
-       else
-               return PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                               CG_FDO_CTRL2, FDO_PWM_MODE);
-}
-
-static int tonga_force_clock_level(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, uint32_t mask)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-
-       if (hwmgr->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL)
-               return -EINVAL;
-
-       switch (type) {
-       case PP_SCLK:
-               if (!data->sclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_SCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.sclk_dpm_enable_mask & mask);
-               break;
-       case PP_MCLK:
-               if (!data->mclk_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_MCLKDPM_SetEnabledMask,
-                                       data->dpm_level_enable_mask.mclk_dpm_enable_mask & mask);
-               break;
-       case PP_PCIE:
-       {
-               uint32_t tmp = mask & data->dpm_level_enable_mask.pcie_dpm_enable_mask;
-               uint32_t level = 0;
-
-               while (tmp >>= 1)
-                       level++;
-
-               if (!data->pcie_dpm_key_disabled)
-                       smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
-                                       PPSMC_MSG_PCIeDPM_ForceLevel,
-                                       level);
-               break;
-       }
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static int tonga_print_clock_levels(struct pp_hwmgr *hwmgr,
-               enum pp_clock_type type, char *buf)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct tonga_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct tonga_single_dpm_table *pcie_table = &(data->dpm_table.pcie_speed_table);
-       int i, now, size = 0;
-       uint32_t clock, pcie_speed;
-
-       switch (type) {
-       case PP_SCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetSclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < sclk_table->count; i++) {
-                       if (clock > sclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < sclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, sclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_MCLK:
-               smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_API_GetMclkFrequency);
-               clock = cgs_read_register(hwmgr->device, mmSMC_MSG_ARG_0);
-
-               for (i = 0; i < mclk_table->count; i++) {
-                       if (clock > mclk_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < mclk_table->count; i++)
-                       size += sprintf(buf + size, "%d: %uMhz %s\n",
-                                       i, mclk_table->dpm_levels[i].value / 100,
-                                       (i == now) ? "*" : "");
-               break;
-       case PP_PCIE:
-               pcie_speed = tonga_get_current_pcie_speed(hwmgr);
-               for (i = 0; i < pcie_table->count; i++) {
-                       if (pcie_speed != pcie_table->dpm_levels[i].value)
-                               continue;
-                       break;
-               }
-               now = i;
-
-               for (i = 0; i < pcie_table->count; i++)
-                       size += sprintf(buf + size, "%d: %s %s\n", i,
-                                       (pcie_table->dpm_levels[i].value == 0) ? "2.5GB, x8" :
-                                       (pcie_table->dpm_levels[i].value == 1) ? "5.0GB, x16" :
-                                       (pcie_table->dpm_levels[i].value == 2) ? "8.0GB, x16" : "",
-                                       (i == now) ? "*" : "");
-               break;
-       default:
-               break;
-       }
-       return size;
-}
-
-static int tonga_get_sclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *sclk_table = &(data->dpm_table.sclk_table);
-       struct tonga_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       int value;
-
-       value = (sclk_table->dpm_levels[sclk_table->count - 1].value -
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value) *
-                       100 /
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return value;
-}
-
-static int tonga_set_sclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *golden_sclk_table =
-                       &(data->golden_dpm_table.sclk_table);
-       struct pp_power_state  *ps;
-       struct tonga_power_state  *tonga_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       tonga_ps = cast_phw_tonga_power_state(&ps->hardware);
-
-       tonga_ps->performance_levels[tonga_ps->performance_level_count - 1].engine_clock =
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value *
-                       value / 100 +
-                       golden_sclk_table->dpm_levels[golden_sclk_table->count - 1].value;
-
-       return 0;
-}
-
-static int tonga_get_mclk_od(struct pp_hwmgr *hwmgr)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *mclk_table = &(data->dpm_table.mclk_table);
-       struct tonga_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       int value;
-
-       value = (mclk_table->dpm_levels[mclk_table->count - 1].value -
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value) *
-                       100 /
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return value;
-}
-
-static int tonga_set_mclk_od(struct pp_hwmgr *hwmgr, uint32_t value)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       struct tonga_single_dpm_table *golden_mclk_table =
-                       &(data->golden_dpm_table.mclk_table);
-       struct pp_power_state  *ps;
-       struct tonga_power_state  *tonga_ps;
-
-       if (value > 20)
-               value = 20;
-
-       ps = hwmgr->request_ps;
-
-       if (ps == NULL)
-               return -EINVAL;
-
-       tonga_ps = cast_phw_tonga_power_state(&ps->hardware);
-
-       tonga_ps->performance_levels[tonga_ps->performance_level_count - 1].memory_clock =
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value *
-                       value / 100 +
-                       golden_mclk_table->dpm_levels[golden_mclk_table->count - 1].value;
-
-       return 0;
-}
-
-static const struct pp_hwmgr_func tonga_hwmgr_funcs = {
-       .backend_init = &tonga_hwmgr_backend_init,
-       .backend_fini = &tonga_hwmgr_backend_fini,
-       .asic_setup = &tonga_setup_asic_task,
-       .dynamic_state_management_enable = &tonga_enable_dpm_tasks,
-       .dynamic_state_management_disable = &tonga_disable_dpm_tasks,
-       .apply_state_adjust_rules = tonga_apply_state_adjust_rules,
-       .force_dpm_level = &tonga_force_dpm_level,
-       .power_state_set = tonga_set_power_state_tasks,
-       .get_power_state_size = tonga_get_power_state_size,
-       .get_mclk = tonga_dpm_get_mclk,
-       .get_sclk = tonga_dpm_get_sclk,
-       .patch_boot_state = tonga_dpm_patch_boot_state,
-       .get_pp_table_entry = tonga_get_pp_table_entry,
-       .get_num_of_pp_table_entries = tonga_get_number_of_powerplay_table_entries,
-       .print_current_perforce_level = tonga_print_current_perforce_level,
-       .powerdown_uvd = tonga_phm_powerdown_uvd,
-       .powergate_uvd = tonga_phm_powergate_uvd,
-       .powergate_vce = tonga_phm_powergate_vce,
-       .disable_clock_power_gating = tonga_phm_disable_clock_power_gating,
-       .update_clock_gatings = tonga_phm_update_clock_gatings,
-       .notify_smc_display_config_after_ps_adjustment = tonga_notify_smc_display_config_after_ps_adjustment,
-       .display_config_changed = tonga_display_configuration_changed_task,
-       .set_max_fan_pwm_output = tonga_set_max_fan_pwm_output,
-       .set_max_fan_rpm_output = tonga_set_max_fan_rpm_output,
-       .get_temperature = tonga_thermal_get_temperature,
-       .stop_thermal_controller = tonga_thermal_stop_thermal_controller,
-       .get_fan_speed_info = tonga_fan_ctrl_get_fan_speed_info,
-       .get_fan_speed_percent = tonga_fan_ctrl_get_fan_speed_percent,
-       .set_fan_speed_percent = tonga_fan_ctrl_set_fan_speed_percent,
-       .reset_fan_speed_to_default = tonga_fan_ctrl_reset_fan_speed_to_default,
-       .get_fan_speed_rpm = tonga_fan_ctrl_get_fan_speed_rpm,
-       .set_fan_speed_rpm = tonga_fan_ctrl_set_fan_speed_rpm,
-       .uninitialize_thermal_controller = tonga_thermal_ctrl_uninitialize_thermal_controller,
-       .register_internal_thermal_interrupt = tonga_register_internal_thermal_interrupt,
-       .check_smc_update_required_for_display_configuration = tonga_check_smc_update_required_for_display_configuration,
-       .check_states_equal = tonga_check_states_equal,
-       .set_fan_control_mode = tonga_set_fan_control_mode,
-       .get_fan_control_mode = tonga_get_fan_control_mode,
-       .force_clock_level = tonga_force_clock_level,
-       .print_clock_levels = tonga_print_clock_levels,
-       .get_sclk_od = tonga_get_sclk_od,
-       .set_sclk_od = tonga_set_sclk_od,
-       .get_mclk_od = tonga_get_mclk_od,
-       .set_mclk_od = tonga_set_mclk_od,
-};
-
-int tonga_hwmgr_init(struct pp_hwmgr *hwmgr)
-{
-       hwmgr->hwmgr_func = &tonga_hwmgr_funcs;
-       hwmgr->pptable_func = &tonga_pptable_funcs;
-       pp_tonga_thermal_initialize(hwmgr);
-       return 0;
-}
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_hwmgr.h
deleted file mode 100644 (file)
index 3961884..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_HWMGR_H
-#define TONGA_HWMGR_H
-
-#include "hwmgr.h"
-#include "smu72_discrete.h"
-#include "ppatomctrl.h"
-#include "ppinterrupt.h"
-#include "tonga_powertune.h"
-#include "pp_endian.h"
-
-#define TONGA_MAX_HARDWARE_POWERLEVELS 2
-#define TONGA_DYNCLK_NUMBER_OF_TREND_COEFFICIENTS 15
-
-struct tonga_performance_level {
-       uint32_t        memory_clock;
-       uint32_t        engine_clock;
-       uint16_t    pcie_gen;
-       uint16_t    pcie_lane;
-};
-
-struct _phw_tonga_bacos {
-       uint32_t                          best_match;
-       uint32_t                          baco_flags;
-       struct tonga_performance_level            performance_level;
-};
-typedef struct _phw_tonga_bacos phw_tonga_bacos;
-
-struct _phw_tonga_uvd_clocks {
-       uint32_t   VCLK;
-       uint32_t   DCLK;
-};
-
-typedef struct _phw_tonga_uvd_clocks phw_tonga_uvd_clocks;
-
-struct _phw_tonga_vce_clocks {
-       uint32_t   EVCLK;
-       uint32_t   ECCLK;
-};
-
-typedef struct _phw_tonga_vce_clocks phw_tonga_vce_clocks;
-
-struct tonga_power_state {
-       uint32_t                    magic;
-       phw_tonga_uvd_clocks        uvd_clocks;
-       phw_tonga_vce_clocks        vce_clocks;
-       uint32_t                    sam_clk;
-       uint32_t                    acp_clk;
-       uint16_t                    performance_level_count;
-       bool                        dc_compatible;
-       uint32_t                    sclk_threshold;
-       struct tonga_performance_level performance_levels[TONGA_MAX_HARDWARE_POWERLEVELS];
-};
-
-struct _phw_tonga_dpm_level {
-       bool            enabled;
-       uint32_t    value;
-       uint32_t    param1;
-};
-typedef struct _phw_tonga_dpm_level phw_tonga_dpm_level;
-
-#define TONGA_MAX_DEEPSLEEP_DIVIDER_ID 5
-#define MAX_REGULAR_DPM_NUMBER 8
-#define TONGA_MINIMUM_ENGINE_CLOCK 2500
-
-struct tonga_single_dpm_table {
-       uint32_t count;
-       phw_tonga_dpm_level dpm_levels[MAX_REGULAR_DPM_NUMBER];
-};
-
-struct tonga_dpm_table {
-       struct tonga_single_dpm_table  sclk_table;
-       struct tonga_single_dpm_table  mclk_table;
-       struct tonga_single_dpm_table  pcie_speed_table;
-       struct tonga_single_dpm_table  vddc_table;
-       struct tonga_single_dpm_table  vdd_gfx_table;
-       struct tonga_single_dpm_table  vdd_ci_table;
-       struct tonga_single_dpm_table  mvdd_table;
-};
-typedef struct _phw_tonga_dpm_table phw_tonga_dpm_table;
-
-
-struct _phw_tonga_clock_regisiters {
-       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  vDLL_CNTL;
-       uint32_t  vMCLK_PWRMGT_CNTL;
-       uint32_t  vMPLL_AD_FUNC_CNTL;
-       uint32_t  vMPLL_DQ_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL;
-       uint32_t  vMPLL_FUNC_CNTL_1;
-       uint32_t  vMPLL_FUNC_CNTL_2;
-       uint32_t  vMPLL_SS1;
-       uint32_t  vMPLL_SS2;
-};
-typedef struct _phw_tonga_clock_regisiters phw_tonga_clock_registers;
-
-struct _phw_tonga_voltage_smio_registers {
-       uint32_t vs0_vid_lower_smio_cntl;
-};
-typedef struct _phw_tonga_voltage_smio_registers phw_tonga_voltage_smio_registers;
-
-
-struct _phw_tonga_mc_reg_entry {
-       uint32_t mclk_max;
-       uint32_t mc_data[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
-};
-typedef struct _phw_tonga_mc_reg_entry phw_tonga_mc_reg_entry;
-
-struct _phw_tonga_mc_reg_table {
-       uint8_t   last;               /* number of registers*/
-       uint8_t   num_entries;        /* number of entries in mc_reg_table_entry used*/
-       uint16_t  validflag;          /* indicate the corresponding register is valid or not. 1: valid, 0: invalid. bit0->address[0], bit1->address[1], etc.*/
-       phw_tonga_mc_reg_entry    mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
-       SMU72_Discrete_MCRegisterAddress mc_reg_address[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
-};
-typedef struct _phw_tonga_mc_reg_table phw_tonga_mc_reg_table;
-
-#define DISABLE_MC_LOADMICROCODE   1
-#define DISABLE_MC_CFGPROGRAMMING  2
-
-/*Ultra Low Voltage parameter structure */
-struct _phw_tonga_ulv_parm{
-       bool    ulv_supported;
-       uint32_t   ch_ulv_parameter;
-       uint32_t   ulv_volt_change_delay;
-       struct tonga_performance_level   ulv_power_level;
-};
-typedef struct _phw_tonga_ulv_parm phw_tonga_ulv_parm;
-
-#define TONGA_MAX_LEAKAGE_COUNT  8
-
-struct _phw_tonga_leakage_voltage {
-       uint16_t  count;
-       uint16_t  leakage_id[TONGA_MAX_LEAKAGE_COUNT];
-       uint16_t  actual_voltage[TONGA_MAX_LEAKAGE_COUNT];
-};
-typedef struct _phw_tonga_leakage_voltage phw_tonga_leakage_voltage;
-
-struct _phw_tonga_display_timing {
-       uint32_t min_clock_insr;
-       uint32_t num_existing_displays;
-};
-typedef struct _phw_tonga_display_timing phw_tonga_display_timing;
-
-struct _phw_tonga_dpmlevel_enable_mask {
-       uint32_t uvd_dpm_enable_mask;
-       uint32_t vce_dpm_enable_mask;
-       uint32_t acp_dpm_enable_mask;
-       uint32_t samu_dpm_enable_mask;
-       uint32_t sclk_dpm_enable_mask;
-       uint32_t mclk_dpm_enable_mask;
-       uint32_t pcie_dpm_enable_mask;
-};
-typedef struct _phw_tonga_dpmlevel_enable_mask phw_tonga_dpmlevel_enable_mask;
-
-struct _phw_tonga_pcie_perf_range {
-       uint16_t max;
-       uint16_t min;
-};
-typedef struct _phw_tonga_pcie_perf_range phw_tonga_pcie_perf_range;
-
-struct _phw_tonga_vbios_boot_state {
-       uint16_t                                        mvdd_bootup_value;
-       uint16_t                                        vddc_bootup_value;
-       uint16_t                                        vddci_bootup_value;
-       uint16_t                                        vddgfx_bootup_value;
-       uint32_t                                        sclk_bootup_value;
-       uint32_t                                        mclk_bootup_value;
-       uint16_t                                        pcie_gen_bootup_value;
-       uint16_t                                        pcie_lane_bootup_value;
-};
-typedef struct _phw_tonga_vbios_boot_state phw_tonga_vbios_boot_state;
-
-#define DPMTABLE_OD_UPDATE_SCLK     0x00000001
-#define DPMTABLE_OD_UPDATE_MCLK     0x00000002
-#define DPMTABLE_UPDATE_SCLK        0x00000004
-#define DPMTABLE_UPDATE_MCLK        0x00000008
-
-/* We need to review which fields are needed. */
-/* This is mostly a copy of the RV7xx/Evergreen structure which is close, but not identical to the N.Islands one. */
-struct tonga_hwmgr {
-       struct tonga_dpm_table               dpm_table;
-       struct tonga_dpm_table               golden_dpm_table;
-
-       uint32_t                           voting_rights_clients0;
-       uint32_t                           voting_rights_clients1;
-       uint32_t                           voting_rights_clients2;
-       uint32_t                           voting_rights_clients3;
-       uint32_t                           voting_rights_clients4;
-       uint32_t                           voting_rights_clients5;
-       uint32_t                           voting_rights_clients6;
-       uint32_t                           voting_rights_clients7;
-       uint32_t                           static_screen_threshold_unit;
-       uint32_t                           static_screen_threshold;
-       uint32_t                           voltage_control;
-       uint32_t                           vdd_gfx_control;
-
-       uint32_t                           vddc_vddci_delta;
-       uint32_t                           vddc_vddgfx_delta;
-
-       struct pp_interrupt_registration_info    internal_high_thermal_interrupt_info;
-       struct pp_interrupt_registration_info    internal_low_thermal_interrupt_info;
-       struct pp_interrupt_registration_info    smc_to_host_interrupt_info;
-       uint32_t                          active_auto_throttle_sources;
-
-       struct pp_interrupt_registration_info    external_throttle_interrupt;
-       irq_handler_func_t             external_throttle_callback;
-       void                             *external_throttle_context;
-
-       struct pp_interrupt_registration_info    ctf_interrupt_info;
-       irq_handler_func_t             ctf_callback;
-       void                             *ctf_context;
-
-       phw_tonga_clock_registers         clock_registers;
-       phw_tonga_voltage_smio_registers  voltage_smio_registers;
-
-       bool    is_memory_GDDR5;
-       uint16_t                          acpi_vddc;
-       bool    pspp_notify_required;        /* Flag to indicate if PSPP notification to SBIOS is required */
-       uint16_t                          force_pcie_gen;            /* The forced PCI-E speed if not 0xffff */
-       uint16_t                          acpi_pcie_gen;             /* The PCI-E speed at ACPI time */
-       uint32_t                           pcie_gen_cap;             /* The PCI-E speed capabilities bitmap from CAIL */
-       uint32_t                           pcie_lane_cap;            /* The PCI-E lane capabilities bitmap from CAIL */
-       uint32_t                           pcie_spc_cap;             /* Symbol Per Clock Capabilities from registry */
-       phw_tonga_leakage_voltage       vddc_leakage;            /* The Leakage VDDC supported (based on leakage ID).*/
-       phw_tonga_leakage_voltage       vddcgfx_leakage;         /* The Leakage VDDC supported (based on leakage ID). */
-       phw_tonga_leakage_voltage       vddci_leakage;           /* The Leakage VDDCI supported (based on leakage ID). */
-
-       uint32_t                           mvdd_control;
-       uint32_t                           vddc_mask_low;
-       uint32_t                           mvdd_mask_low;
-       uint16_t                          max_vddc_in_pp_table;        /* the maximum VDDC value in the powerplay table*/
-       uint16_t                          min_vddc_in_pp_table;
-       uint16_t                          max_vddci_in_pp_table;       /* the maximum VDDCI value in the powerplay table */
-       uint16_t                          min_vddci_in_pp_table;
-       uint32_t                           mclk_strobe_mode_threshold;
-       uint32_t                           mclk_stutter_mode_threshold;
-       uint32_t                           mclk_edc_enable_threshold;
-       uint32_t                           mclk_edc_wr_enable_threshold;
-       bool    is_uvd_enabled;
-       bool    is_xdma_enabled;
-       phw_tonga_vbios_boot_state      vbios_boot_state;
-
-       bool                         battery_state;
-       bool                         is_tlu_enabled;
-       bool                         pcie_performance_request;
-
-       /* -------------- SMC SRAM Address of firmware header tables ----------------*/
-       uint32_t                           sram_end;                 /* The first address after the SMC SRAM. */
-       uint32_t                           dpm_table_start;          /* The start of the dpm table in the SMC SRAM. */
-       uint32_t                           soft_regs_start;          /* The start of the soft registers in the SMC SRAM. */
-       uint32_t                           mc_reg_table_start;       /* The start of the mc register table in the SMC SRAM. */
-       uint32_t                           fan_table_start;          /* The start of the fan table in the SMC SRAM. */
-       uint32_t                           arb_table_start;          /* The start of the ARB setting table in the SMC SRAM. */
-       SMU72_Discrete_DpmTable         smc_state_table;             /* The carbon copy of the SMC state table. */
-       SMU72_Discrete_MCRegisters      mc_reg_table;
-       SMU72_Discrete_Ulv              ulv_setting;                 /* The carbon copy of ULV setting. */
-       /* -------------- Stuff originally coming from Evergreen --------------------*/
-       phw_tonga_mc_reg_table                  tonga_mc_reg_table;
-       uint32_t                         vdd_ci_control;
-       pp_atomctrl_voltage_table        vddc_voltage_table;
-       pp_atomctrl_voltage_table        vddci_voltage_table;
-       pp_atomctrl_voltage_table        vddgfx_voltage_table;
-       pp_atomctrl_voltage_table        mvdd_voltage_table;
-
-       uint32_t                           mgcg_cgtt_local2;
-       uint32_t                           mgcg_cgtt_local3;
-       uint32_t                           gpio_debug;
-       uint32_t                                                        mc_micro_code_feature;
-       uint32_t                                                        highest_mclk;
-       uint16_t                          acpi_vdd_ci;
-       uint8_t                           mvdd_high_index;
-       uint8_t                           mvdd_low_index;
-       bool                         dll_defaule_on;
-       bool                         performance_request_registered;
-
-       /* ----------------- Low Power Features ---------------------*/
-       phw_tonga_bacos                                 bacos;
-       phw_tonga_ulv_parm              ulv;
-       /* ----------------- CAC Stuff ---------------------*/
-       uint32_t                                        cac_table_start;
-       bool                         cac_configuration_required;    /* TRUE if PP_CACConfigurationRequired == 1 */
-       bool                         driver_calculate_cac_leakage;  /* TRUE if PP_DriverCalculateCACLeakage == 1 */
-       bool                         cac_enabled;
-       /* ----------------- DPM2 Parameters ---------------------*/
-       uint32_t                                        power_containment_features;
-       bool                         enable_bapm_feature;
-       bool                         enable_tdc_limit_feature;
-       bool                         enable_pkg_pwr_tracking_feature;
-       bool                         disable_uvd_power_tune_feature;
-       phw_tonga_pt_defaults           *power_tune_defaults;
-       SMU72_Discrete_PmFuses           power_tune_table;
-       uint32_t                           ul_dte_tj_offset;             /* Fudge factor in DPM table to correct HW DTE errors */
-       uint32_t                           fast_watemark_threshold;      /* use fast watermark if clock is equal or above this. In percentage of the target high sclk. */
-
-       /* ----------------- Phase Shedding ---------------------*/
-       bool                         vddc_phase_shed_control;
-       /* --------------------- DI/DT --------------------------*/
-       phw_tonga_display_timing       display_timing;
-       /* --------- ReadRegistry data for memory and engine clock margins ---- */
-       uint32_t                           engine_clock_data;
-       uint32_t                           memory_clock_data;
-       /* -------- Thermal Temperature Setting --------------*/
-       phw_tonga_dpmlevel_enable_mask     dpm_level_enable_mask;
-       uint32_t                           need_update_smu7_dpm_table;
-       uint32_t                           sclk_dpm_key_disabled;
-       uint32_t                           mclk_dpm_key_disabled;
-       uint32_t                           pcie_dpm_key_disabled;
-       uint32_t                           min_engine_clocks; /* used to store the previous dal min sclock */
-       phw_tonga_pcie_perf_range       pcie_gen_performance;
-       phw_tonga_pcie_perf_range       pcie_lane_performance;
-       phw_tonga_pcie_perf_range       pcie_gen_power_saving;
-       phw_tonga_pcie_perf_range       pcie_lane_power_saving;
-       bool                            use_pcie_performance_levels;
-       bool                            use_pcie_power_saving_levels;
-       uint32_t                           activity_target[SMU72_MAX_LEVELS_GRAPHICS]; /* percentage value from 0-100, default 50 */
-       uint32_t                           mclk_activity_target;
-       uint32_t                           low_sclk_interrupt_threshold;
-       uint32_t                           last_mclk_dpm_enable_mask;
-       bool                                                            uvd_enabled;
-       uint32_t                           pcc_monitor_enabled;
-
-       /* --------- Power Gating States ------------*/
-       bool                           uvd_power_gated;  /* 1: gated, 0:not gated */
-       bool                           vce_power_gated;  /* 1: gated, 0:not gated */
-       bool                           samu_power_gated; /* 1: gated, 0:not gated */
-       bool                           acp_power_gated;  /* 1: gated, 0:not gated */
-       bool                           pg_acp_init;
-};
-
-typedef struct tonga_hwmgr tonga_hwmgr;
-
-#define TONGA_DPM2_NEAR_TDP_DEC          10
-#define TONGA_DPM2_ABOVE_SAFE_INC        5
-#define TONGA_DPM2_BELOW_SAFE_INC        20
-
-#define TONGA_DPM2_LTA_WINDOW_SIZE       7  /* Log2 of the LTA window size (l2numWin_TDP). Eg. If LTA windows size is 128, then this value should be Log2(128) = 7. */
-
-#define TONGA_DPM2_LTS_TRUNCATE          0
-
-#define TONGA_DPM2_TDP_SAFE_LIMIT_PERCENT            80  /* Maximum 100 */
-
-#define TONGA_DPM2_MAXPS_PERCENT_H                   90  /* Maximum 0xFF */
-#define TONGA_DPM2_MAXPS_PERCENT_M                   90  /* Maximum 0xFF */
-
-#define TONGA_DPM2_PWREFFICIENCYRATIO_MARGIN         50
-
-#define TONGA_DPM2_SQ_RAMP_MAX_POWER                 0x3FFF
-#define TONGA_DPM2_SQ_RAMP_MIN_POWER                 0x12
-#define TONGA_DPM2_SQ_RAMP_MAX_POWER_DELTA           0x15
-#define TONGA_DPM2_SQ_RAMP_SHORT_TERM_INTERVAL_SIZE  0x1E
-#define TONGA_DPM2_SQ_RAMP_LONG_TERM_INTERVAL_RATIO  0xF
-
-#define TONGA_VOLTAGE_CONTROL_NONE                   0x0
-#define TONGA_VOLTAGE_CONTROL_BY_GPIO                0x1
-#define TONGA_VOLTAGE_CONTROL_BY_SVID2               0x2
-#define TONGA_VOLTAGE_CONTROL_MERGED                 0x3
-
-#define TONGA_Q88_FORMAT_CONVERSION_UNIT             256 /*To convert to Q8.8 format for firmware */
-
-#define TONGA_UNUSED_GPIO_PIN                        0x7F
-
-int tonga_hwmgr_init(struct pp_hwmgr *hwmgr);
-int tonga_update_vce_dpm(struct pp_hwmgr *hwmgr, const void *input);
-int tonga_update_uvd_dpm(struct pp_hwmgr *hwmgr, bool bgate);
-int tonga_enable_disable_uvd_dpm(struct pp_hwmgr *hwmgr, bool enable);
-int tonga_enable_disable_vce_dpm(struct pp_hwmgr *hwmgr, bool enable);
-uint32_t tonga_get_xclk(struct pp_hwmgr *hwmgr);
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_powertune.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_powertune.h
deleted file mode 100644 (file)
index 8e6670b..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_POWERTUNE_H
-#define TONGA_POWERTUNE_H
-
-enum _phw_tonga_ptc_config_reg_type {
-       TONGA_CONFIGREG_MMR = 0,
-       TONGA_CONFIGREG_SMC_IND,
-       TONGA_CONFIGREG_DIDT_IND,
-       TONGA_CONFIGREG_CACHE,
-
-       TONGA_CONFIGREG_MAX
-};
-typedef enum _phw_tonga_ptc_config_reg_type phw_tonga_ptc_config_reg_type;
-
-/* PowerContainment Features */
-#define POWERCONTAINMENT_FEATURE_BAPM            0x00000001
-#define POWERCONTAINMENT_FEATURE_TDCLimit        0x00000002
-#define POWERCONTAINMENT_FEATURE_PkgPwrLimit     0x00000004
-
-struct _phw_tonga_pt_config_reg {
-       uint32_t                           Offset;
-       uint32_t                           Mask;
-       uint32_t                           Shift;
-       uint32_t                           Value;
-       phw_tonga_ptc_config_reg_type     Type;
-};
-typedef struct _phw_tonga_pt_config_reg phw_tonga_pt_config_reg;
-
-struct _phw_tonga_pt_defaults {
-       uint8_t   svi_load_line_en;
-       uint8_t   svi_load_line_vddC;
-       uint8_t   tdc_vddc_throttle_release_limit_perc;
-       uint8_t   tdc_mawt;
-       uint8_t   tdc_waterfall_ctl;
-       uint8_t   dte_ambient_temp_base;
-       uint32_t  display_cac;
-       uint32_t  bamp_temp_gradient;
-       uint16_t  bapmti_r[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
-       uint16_t  bapmti_rc[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
-};
-typedef struct _phw_tonga_pt_defaults phw_tonga_pt_defaults;
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_pptable.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_pptable.h
deleted file mode 100644 (file)
index f127198..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_PPTABLE_H
-#define TONGA_PPTABLE_H
-
-/** \file
- * This is a PowerPlay table header file
- */
-#pragma pack(push, 1)
-
-#include "hwmgr.h"
-
-#define ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
-#define ATOM_TONGA_PP_FANPARAMETERS_NOFAN                                 0x80    /* No fan is connected to this controller. */
-
-#define ATOM_TONGA_PP_THERMALCONTROLLER_NONE      0
-#define ATOM_TONGA_PP_THERMALCONTROLLER_LM96163   17
-#define ATOM_TONGA_PP_THERMALCONTROLLER_TONGA     21
-#define ATOM_TONGA_PP_THERMALCONTROLLER_FIJI      22
-
-/*
- * 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.
- * To keep the number of these types low we should also use the same code for all ASICs (i.e. do not distinguish RV6xx and RV7xx Internal here).
- * The driver can pick the correct internal controller based on the ASIC.
- */
-
-#define ATOM_TONGA_PP_THERMALCONTROLLER_ADT7473_WITH_INTERNAL   0x89    /* ADT7473 Fan Control + Internal Thermal Controller */
-#define ATOM_TONGA_PP_THERMALCONTROLLER_EMC2103_WITH_INTERNAL   0x8D    /* EMC2103 Fan Control + Internal Thermal Controller */
-
-/*/* ATOM_TONGA_POWERPLAYTABLE::ulPlatformCaps */
-#define ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL              0x1            /* This cap indicates whether vddgfx will be a separated power rail. */
-#define ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY                   0x2            /* This cap indicates whether this is a mobile part and CCC need to show Powerplay page. */
-#define ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE            0x4            /* This cap indicates whether power source notificaiton is done by SBIOS directly. */
-#define ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND      0x8            /* Enable the option to overwrite voltage island feature to be disabled, regardless of VddGfx power rail support. */
-#define ____RETIRE16____                                0x10
-#define ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC                 0x20            /* This cap indicates whether power source notificaiton is done by GPIO directly. */
-#define ____RETIRE64____                                0x40
-#define ____RETIRE128____                               0x80
-#define ____RETIRE256____                              0x100
-#define ____RETIRE512____                              0x200
-#define ____RETIRE1024____                             0x400
-#define ____RETIRE2048____                             0x800
-#define ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL             0x1000            /* This cap indicates dynamic MVDD is required. Uncheck to disable it. */
-#define ____RETIRE2000____                            0x2000
-#define ____RETIRE4000____                            0x4000
-#define ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL            0x8000            /* This cap indicates dynamic VDDCI is required. Uncheck to disable it. */
-#define ____RETIRE10000____                          0x10000
-#define ATOM_TONGA_PP_PLATFORM_CAP_BACO                    0x20000            /* Enable to indicate the driver supports BACO state. */
-
-#define ATOM_TONGA_PP_PLATFORM_CAP_OUTPUT_THERMAL2GPIO17         0x100000     /* Enable to indicate the driver supports thermal2GPIO17. */
-#define ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL  0x1000000     /* Enable to indicate if thermal and PCC are sharing the same GPIO */
-#define ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE       0x2000000
-
-/* ATOM_PPLIB_NONCLOCK_INFO::usClassification */
-#define ATOM_PPLIB_CLASSIFICATION_UI_MASK               0x0007
-#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT              0
-#define ATOM_PPLIB_CLASSIFICATION_UI_NONE               0
-#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY            1
-#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED           3
-#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE        5
-/* 2, 4, 6, 7 are reserved */
-
-#define ATOM_PPLIB_CLASSIFICATION_BOOT                  0x0008
-#define ATOM_PPLIB_CLASSIFICATION_THERMAL               0x0010
-#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE    0x0020
-#define ATOM_PPLIB_CLASSIFICATION_REST                  0x0040
-#define ATOM_PPLIB_CLASSIFICATION_FORCED                0x0080
-#define ATOM_PPLIB_CLASSIFICATION_ACPI                  0x1000
-
-/* ATOM_PPLIB_NONCLOCK_INFO::usClassification2 */
-#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001
-
-#define ATOM_Tonga_DISALLOW_ON_DC                       0x00004000
-#define ATOM_Tonga_ENABLE_VARIBRIGHT                    0x00008000
-
-#define ATOM_Tonga_TABLE_REVISION_TONGA                 7
-
-typedef struct _ATOM_Tonga_POWERPLAYTABLE {
-       ATOM_COMMON_TABLE_HEADER sHeader;
-
-       UCHAR  ucTableRevision;
-       USHORT usTableSize;                                             /*the size of header structure */
-
-       ULONG   ulGoldenPPID;
-       ULONG   ulGoldenRevision;
-       USHORT  usFormatID;
-
-       USHORT  usVoltageTime;                                   /*in microseconds */
-       ULONG   ulPlatformCaps;                                   /*See ATOM_Tonga_CAPS_* */
-
-       ULONG   ulMaxODEngineClock;                        /*For Overdrive.  */
-       ULONG   ulMaxODMemoryClock;                        /*For Overdrive. */
-
-       USHORT  usPowerControlLimit;
-       USHORT  usUlvVoltageOffset;                               /*in mv units */
-
-       USHORT  usStateArrayOffset;                               /*points to ATOM_Tonga_State_Array */
-       USHORT  usFanTableOffset;                                 /*points to ATOM_Tonga_Fan_Table */
-       USHORT  usThermalControllerOffset;                 /*points to ATOM_Tonga_Thermal_Controller */
-       USHORT  usReserv;                                                  /*CustomThermalPolicy removed for Tonga. Keep this filed as reserved. */
-
-       USHORT  usMclkDependencyTableOffset;       /*points to ATOM_Tonga_MCLK_Dependency_Table */
-       USHORT  usSclkDependencyTableOffset;       /*points to ATOM_Tonga_SCLK_Dependency_Table */
-       USHORT  usVddcLookupTableOffset;                   /*points to ATOM_Tonga_Voltage_Lookup_Table */
-       USHORT  usVddgfxLookupTableOffset;              /*points to ATOM_Tonga_Voltage_Lookup_Table */
-
-       USHORT  usMMDependencyTableOffset;                /*points to ATOM_Tonga_MM_Dependency_Table */
-
-       USHORT  usVCEStateTableOffset;                     /*points to ATOM_Tonga_VCE_State_Table; */
-
-       USHORT  usPPMTableOffset;                                 /*points to ATOM_Tonga_PPM_Table */
-       USHORT  usPowerTuneTableOffset;                   /*points to ATOM_PowerTune_Table */
-
-       USHORT  usHardLimitTableOffset;                    /*points to ATOM_Tonga_Hard_Limit_Table */
-
-       USHORT  usPCIETableOffset;                                /*points to ATOM_Tonga_PCIE_Table */
-
-       USHORT  usGPIOTableOffset;                                /*points to ATOM_Tonga_GPIO_Table */
-
-       USHORT  usReserved[6];                                     /*TODO: modify reserved size to fit structure aligning */
-} ATOM_Tonga_POWERPLAYTABLE;
-
-typedef struct _ATOM_Tonga_State {
-       UCHAR  ucEngineClockIndexHigh;
-       UCHAR  ucEngineClockIndexLow;
-
-       UCHAR  ucMemoryClockIndexHigh;
-       UCHAR  ucMemoryClockIndexLow;
-
-       UCHAR  ucPCIEGenLow;
-       UCHAR  ucPCIEGenHigh;
-
-       UCHAR  ucPCIELaneLow;
-       UCHAR  ucPCIELaneHigh;
-
-       USHORT usClassification;
-       ULONG ulCapsAndSettings;
-       USHORT usClassification2;
-       UCHAR  ucUnused[4];
-} ATOM_Tonga_State;
-
-typedef struct _ATOM_Tonga_State_Array {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;             /* Number of entries. */
-       ATOM_Tonga_State states[1];     /* Dynamically allocate entries. */
-} ATOM_Tonga_State_Array;
-
-typedef struct _ATOM_Tonga_MCLK_Dependency_Record {
-       UCHAR  ucVddcInd;       /* Vddc voltage */
-       USHORT usVddci;
-       USHORT usVddgfxOffset;  /* Offset relative to Vddc voltage */
-       USHORT usMvdd;
-       ULONG ulMclk;
-       USHORT usReserved;
-} ATOM_Tonga_MCLK_Dependency_Record;
-
-typedef struct _ATOM_Tonga_MCLK_Dependency_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                                             /* Number of entries. */
-       ATOM_Tonga_MCLK_Dependency_Record entries[1];                           /* Dynamically allocate entries. */
-} ATOM_Tonga_MCLK_Dependency_Table;
-
-typedef struct _ATOM_Tonga_SCLK_Dependency_Record {
-       UCHAR  ucVddInd;                                                                                        /* Base voltage */
-       USHORT usVddcOffset;                                                                            /* Offset relative to base voltage */
-       ULONG ulSclk;
-       USHORT usEdcCurrent;
-       UCHAR  ucReliabilityTemperature;
-       UCHAR  ucCKSVOffsetandDisable;                                                    /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
-} ATOM_Tonga_SCLK_Dependency_Record;
-
-typedef struct _ATOM_Tonga_SCLK_Dependency_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                                             /* Number of entries. */
-       ATOM_Tonga_SCLK_Dependency_Record entries[1];                            /* Dynamically allocate entries. */
-} ATOM_Tonga_SCLK_Dependency_Table;
-
-typedef struct _ATOM_Polaris_SCLK_Dependency_Record {
-       UCHAR  ucVddInd;                                                                                        /* Base voltage */
-       USHORT usVddcOffset;                                                                            /* Offset relative to base voltage */
-       ULONG ulSclk;
-       USHORT usEdcCurrent;
-       UCHAR  ucReliabilityTemperature;
-       UCHAR  ucCKSVOffsetandDisable;                  /* Bits 0~6: Voltage offset for CKS, Bit 7: Disable/enable for the SCLK level. */
-       ULONG  ulSclkOffset;
-} ATOM_Polaris_SCLK_Dependency_Record;
-
-typedef struct _ATOM_Polaris_SCLK_Dependency_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                     /* Number of entries. */
-       ATOM_Polaris_SCLK_Dependency_Record entries[1];                          /* Dynamically allocate entries. */
-} ATOM_Polaris_SCLK_Dependency_Table;
-
-typedef struct _ATOM_Tonga_PCIE_Record {
-       UCHAR ucPCIEGenSpeed;
-       UCHAR usPCIELaneWidth;
-       UCHAR ucReserved[2];
-} ATOM_Tonga_PCIE_Record;
-
-typedef struct _ATOM_Tonga_PCIE_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                                             /* Number of entries. */
-       ATOM_Tonga_PCIE_Record entries[1];                                                      /* Dynamically allocate entries. */
-} ATOM_Tonga_PCIE_Table;
-
-typedef struct _ATOM_Polaris10_PCIE_Record {
-       UCHAR ucPCIEGenSpeed;
-       UCHAR usPCIELaneWidth;
-       UCHAR ucReserved[2];
-       ULONG ulPCIE_Sclk;
-} ATOM_Polaris10_PCIE_Record;
-
-typedef struct _ATOM_Polaris10_PCIE_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                         /* Number of entries. */
-       ATOM_Polaris10_PCIE_Record entries[1];                      /* Dynamically allocate entries. */
-} ATOM_Polaris10_PCIE_Table;
-
-
-typedef struct _ATOM_Tonga_MM_Dependency_Record {
-       UCHAR   ucVddcInd;                                                                                       /* VDDC voltage */
-       USHORT  usVddgfxOffset;                                                                   /* Offset relative to VDDC voltage */
-       ULONG  ulDClk;                                                                                          /* UVD D-clock */
-       ULONG  ulVClk;                                                                                          /* UVD V-clock */
-       ULONG  ulEClk;                                                                                          /* VCE clock */
-       ULONG  ulAClk;                                                                                          /* ACP clock */
-       ULONG  ulSAMUClk;                                                                                       /* SAMU clock */
-} ATOM_Tonga_MM_Dependency_Record;
-
-typedef struct _ATOM_Tonga_MM_Dependency_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                                             /* Number of entries. */
-       ATOM_Tonga_MM_Dependency_Record entries[1];                        /* Dynamically allocate entries. */
-} ATOM_Tonga_MM_Dependency_Table;
-
-typedef struct _ATOM_Tonga_Voltage_Lookup_Record {
-       USHORT usVdd;                                                                                      /* Base voltage */
-       USHORT usCACLow;
-       USHORT usCACMid;
-       USHORT usCACHigh;
-} ATOM_Tonga_Voltage_Lookup_Record;
-
-typedef struct _ATOM_Tonga_Voltage_Lookup_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;                                                                             /* Number of entries. */
-       ATOM_Tonga_Voltage_Lookup_Record entries[1];                            /* Dynamically allocate entries. */
-} ATOM_Tonga_Voltage_Lookup_Table;
-
-typedef struct _ATOM_Tonga_Fan_Table {
-       UCHAR   ucRevId;                                                 /* Change this if the table format changes or version changes so that the other fields are not the same. */
-       UCHAR   ucTHyst;                                                 /* Temperature hysteresis. Integer. */
-       USHORT  usTMin;                                                  /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
-       USHORT  usTMed;                                                  /* The middle temperature where we change slopes. */
-       USHORT  usTHigh;                                                 /* The high point above TMed for adjusting the second slope. */
-       USHORT  usPWMMin;                                                /* The minimum PWM value in percent (0.01% increments). */
-       USHORT  usPWMMed;                                                /* The PWM value (in percent) at TMed. */
-       USHORT  usPWMHigh;                                               /* The PWM value at THigh. */
-       USHORT  usTMax;                                                  /* The max temperature */
-       UCHAR   ucFanControlMode;                                 /* Legacy or Fuzzy Fan mode */
-       USHORT  usFanPWMMax;                                      /* Maximum allowed fan power in percent */
-       USHORT  usFanOutputSensitivity;           /* Sensitivity of fan reaction to temepature changes */
-       USHORT  usFanRPMMax;                                      /* The default value in RPM */
-       ULONG  ulMinFanSCLKAcousticLimit;          /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
-       UCHAR   ucTargetTemperature;                     /* Advanced fan controller target temperature. */
-       UCHAR   ucMinimumPWMLimit;                        /* The minimum PWM that the advanced fan controller can set.  This should be set to the highest PWM that will run the fan at its lowest RPM. */
-       USHORT  usReserved;
-} ATOM_Tonga_Fan_Table;
-
-typedef struct _ATOM_Fiji_Fan_Table {
-       UCHAR   ucRevId;                                                 /* Change this if the table format changes or version changes so that the other fields are not the same. */
-       UCHAR   ucTHyst;                                                 /* Temperature hysteresis. Integer. */
-       USHORT  usTMin;                                                  /* The temperature, in 0.01 centigrades, below which we just run at a minimal PWM. */
-       USHORT  usTMed;                                                  /* The middle temperature where we change slopes. */
-       USHORT  usTHigh;                                                 /* The high point above TMed for adjusting the second slope. */
-       USHORT  usPWMMin;                                                /* The minimum PWM value in percent (0.01% increments). */
-       USHORT  usPWMMed;                                                /* The PWM value (in percent) at TMed. */
-       USHORT  usPWMHigh;                                               /* The PWM value at THigh. */
-       USHORT  usTMax;                                                  /* The max temperature */
-       UCHAR   ucFanControlMode;                                 /* Legacy or Fuzzy Fan mode */
-       USHORT  usFanPWMMax;                                      /* Maximum allowed fan power in percent */
-       USHORT  usFanOutputSensitivity;           /* Sensitivity of fan reaction to temepature changes */
-       USHORT  usFanRPMMax;                                      /* The default value in RPM */
-       ULONG  ulMinFanSCLKAcousticLimit;               /* Minimum Fan Controller SCLK Frequency Acoustic Limit. */
-       UCHAR   ucTargetTemperature;                     /* Advanced fan controller target temperature. */
-       UCHAR   ucMinimumPWMLimit;                        /* The minimum PWM that the advanced fan controller can set.  This should be set to the highest PWM that will run the fan at its lowest RPM. */
-       USHORT  usFanGainEdge;
-       USHORT  usFanGainHotspot;
-       USHORT  usFanGainLiquid;
-       USHORT  usFanGainVrVddc;
-       USHORT  usFanGainVrMvdd;
-       USHORT  usFanGainPlx;
-       USHORT  usFanGainHbm;
-       USHORT  usReserved;
-} ATOM_Fiji_Fan_Table;
-
-typedef struct _ATOM_Tonga_Thermal_Controller {
-       UCHAR ucRevId;
-       UCHAR ucType;              /* one of ATOM_TONGA_PP_THERMALCONTROLLER_* */
-       UCHAR ucI2cLine;                /* as interpreted by DAL I2C */
-       UCHAR ucI2cAddress;
-       UCHAR ucFanParameters;  /* Fan Control Parameters. */
-       UCHAR ucFanMinRPM;       /* Fan Minimum RPM (hundreds) -- for display purposes only. */
-       UCHAR ucFanMaxRPM;       /* Fan Maximum RPM (hundreds) -- for display purposes only. */
-       UCHAR ucReserved;
-       UCHAR ucFlags;             /* to be defined */
-} ATOM_Tonga_Thermal_Controller;
-
-typedef struct _ATOM_Tonga_VCE_State_Record {
-       UCHAR  ucVCEClockIndex; /*index into usVCEDependencyTableOffset of 'ATOM_Tonga_MM_Dependency_Table' type */
-       UCHAR  ucFlag;          /* 2 bits indicates memory p-states */
-       UCHAR  ucSCLKIndex;             /*index into ATOM_Tonga_SCLK_Dependency_Table */
-       UCHAR  ucMCLKIndex;             /*index into ATOM_Tonga_MCLK_Dependency_Table */
-} ATOM_Tonga_VCE_State_Record;
-
-typedef struct _ATOM_Tonga_VCE_State_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;
-       ATOM_Tonga_VCE_State_Record entries[1];
-} ATOM_Tonga_VCE_State_Table;
-
-typedef struct _ATOM_Tonga_PowerTune_Table {
-       UCHAR  ucRevId;
-       USHORT usTDP;
-       USHORT usConfigurableTDP;
-       USHORT usTDC;
-       USHORT usBatteryPowerLimit;
-       USHORT usSmallPowerLimit;
-       USHORT usLowCACLeakage;
-       USHORT usHighCACLeakage;
-       USHORT usMaximumPowerDeliveryLimit;
-       USHORT usTjMax;
-       USHORT usPowerTuneDataSetID;
-       USHORT usEDCLimit;
-       USHORT usSoftwareShutdownTemp;
-       USHORT usClockStretchAmount;
-       USHORT usReserve[2];
-} ATOM_Tonga_PowerTune_Table;
-
-typedef struct _ATOM_Fiji_PowerTune_Table {
-       UCHAR  ucRevId;
-       USHORT usTDP;
-       USHORT usConfigurableTDP;
-       USHORT usTDC;
-       USHORT usBatteryPowerLimit;
-       USHORT usSmallPowerLimit;
-       USHORT usLowCACLeakage;
-       USHORT usHighCACLeakage;
-       USHORT usMaximumPowerDeliveryLimit;
-       USHORT usTjMax;  /* For Fiji, this is also usTemperatureLimitEdge; */
-       USHORT usPowerTuneDataSetID;
-       USHORT usEDCLimit;
-       USHORT usSoftwareShutdownTemp;
-       USHORT usClockStretchAmount;
-       USHORT usTemperatureLimitHotspot;  /*The following are added for Fiji */
-       USHORT usTemperatureLimitLiquid1;
-       USHORT usTemperatureLimitLiquid2;
-       USHORT usTemperatureLimitVrVddc;
-       USHORT usTemperatureLimitVrMvdd;
-       USHORT usTemperatureLimitPlx;
-       UCHAR  ucLiquid1_I2C_address;  /*Liquid */
-       UCHAR  ucLiquid2_I2C_address;
-       UCHAR  ucLiquid_I2C_Line;
-       UCHAR  ucVr_I2C_address;        /*VR */
-       UCHAR  ucVr_I2C_Line;
-       UCHAR  ucPlx_I2C_address;  /*PLX */
-       UCHAR  ucPlx_I2C_Line;
-       USHORT usReserved;
-} ATOM_Fiji_PowerTune_Table;
-
-#define ATOM_PPM_A_A    1
-#define ATOM_PPM_A_I    2
-typedef struct _ATOM_Tonga_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_Tonga_PPM_Table;
-
-typedef struct _ATOM_Tonga_Hard_Limit_Record {
-       ULONG  ulSCLKLimit;
-       ULONG  ulMCLKLimit;
-       USHORT  usVddcLimit;
-       USHORT  usVddciLimit;
-       USHORT  usVddgfxLimit;
-} ATOM_Tonga_Hard_Limit_Record;
-
-typedef struct _ATOM_Tonga_Hard_Limit_Table {
-       UCHAR ucRevId;
-       UCHAR ucNumEntries;
-       ATOM_Tonga_Hard_Limit_Record entries[1];
-} ATOM_Tonga_Hard_Limit_Table;
-
-typedef struct _ATOM_Tonga_GPIO_Table {
-       UCHAR  ucRevId;
-       UCHAR  ucVRHotTriggeredSclkDpmIndex;            /* If VRHot signal is triggered SCLK will be limited to this DPM level */
-       UCHAR  ucReserve[5];
-} ATOM_Tonga_GPIO_Table;
-
-typedef struct _PPTable_Generic_SubTable_Header {
-       UCHAR  ucRevId;
-} PPTable_Generic_SubTable_Header;
-
-
-#pragma pack(pop)
-
-
-#endif
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.c
deleted file mode 100644 (file)
index cfb647f..0000000
+++ /dev/null
@@ -1,1214 +0,0 @@
-/*
- * Copyright 2015 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/module.h>
-#include <linux/slab.h>
-#include <linux/fb.h>
-
-#include "tonga_processpptables.h"
-#include "ppatomctrl.h"
-#include "atombios.h"
-#include "pp_debug.h"
-#include "hwmgr.h"
-#include "cgs_common.h"
-#include "tonga_pptable.h"
-
-/**
- * Private Function used during initialization.
- * @param hwmgr Pointer to the hardware manager.
- * @param setIt A flag indication if the capability should be set (TRUE) or reset (FALSE).
- * @param cap Which capability to set/reset.
- */
-static void set_hw_cap(struct pp_hwmgr *hwmgr, bool setIt, enum phm_platform_caps cap)
-{
-       if (setIt)
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps, cap);
-       else
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, cap);
-}
-
-
-/**
- * Private Function used during initialization.
- * @param hwmgr Pointer to the hardware manager.
- * @param powerplay_caps the bit array (from BIOS) of capability bits.
- * @exception the current implementation always returns 1.
- */
-static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps)
-{
-       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE16____),
-               "ATOM_PP_PLATFORM_CAP_ASPM_L1 is not supported!", continue);
-       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE64____),
-               "ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY is not supported!", continue);
-       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE512____),
-               "ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL is not supported!", continue);
-       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE1024____),
-               "ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 is not supported!", continue);
-       PP_ASSERT_WITH_CODE((~powerplay_caps & ____RETIRE2048____),
-               "ATOM_PP_PLATFORM_CAP_HTLINKCONTROL is not supported!", continue);
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_POWERPLAY),
-                       PHM_PlatformCaps_PowerPlaySupport
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_SBIOSPOWERSOURCE),
-                       PHM_PlatformCaps_BiosPowerSourceControl
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_HARDWAREDC),
-                       PHM_PlatformCaps_AutomaticDCTransition
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_MVDD_CONTROL),
-                       PHM_PlatformCaps_EnableMVDDControl
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDCI_CONTROL),
-                       PHM_PlatformCaps_ControlVDDCI
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_VDDGFX_CONTROL),
-                       PHM_PlatformCaps_ControlVDDGFX
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_BACO),
-                       PHM_PlatformCaps_BACO
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_CAP_DISABLE_VOLTAGE_ISLAND),
-                       PHM_PlatformCaps_DisableVoltageIsland
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL),
-                       PHM_PlatformCaps_CombinePCCWithThermalSignal
-                 );
-
-       set_hw_cap(
-                       hwmgr,
-                       0 != (powerplay_caps & ATOM_TONGA_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE),
-                       PHM_PlatformCaps_LoadPostProductionFirmware
-                 );
-
-       return 0;
-}
-
-/**
- * Private Function to get the PowerPlay Table Address.
- */
-const void *get_powerplay_table(struct pp_hwmgr *hwmgr)
-{
-       int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
-
-       u16 size;
-       u8 frev, crev;
-       void *table_address = (void *)hwmgr->soft_pp_table;
-
-       if (!table_address) {
-               table_address = (ATOM_Tonga_POWERPLAYTABLE *)
-                               cgs_atom_get_data_table(hwmgr->device,
-                                               index, &size, &frev, &crev);
-               hwmgr->soft_pp_table = table_address;   /*Cache the result in RAM.*/
-               hwmgr->soft_pp_table_size = size;
-       }
-
-       return table_address;
-}
-
-static int get_vddc_lookup_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_voltage_lookup_table **lookup_table,
-               const ATOM_Tonga_Voltage_Lookup_Table   *vddc_lookup_pp_tables,
-               uint32_t        max_levels
-               )
-{
-       uint32_t table_size, i;
-       phm_ppt_v1_voltage_lookup_table *table;
-
-       PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries),
-               "Invalid CAC Leakage PowerPlay Table!", return 1);
-
-       table_size = sizeof(uint32_t) +
-               sizeof(phm_ppt_v1_voltage_lookup_record) * max_levels;
-
-       table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == table)
-               return -ENOMEM;
-
-       memset(table, 0x00, table_size);
-
-       table->count = vddc_lookup_pp_tables->ucNumEntries;
-
-       for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) {
-               table->entries[i].us_calculated = 0;
-               table->entries[i].us_vdd =
-                       vddc_lookup_pp_tables->entries[i].usVdd;
-               table->entries[i].us_cac_low =
-                       vddc_lookup_pp_tables->entries[i].usCACLow;
-               table->entries[i].us_cac_mid =
-                       vddc_lookup_pp_tables->entries[i].usCACMid;
-               table->entries[i].us_cac_high =
-                       vddc_lookup_pp_tables->entries[i].usCACHigh;
-       }
-
-       *lookup_table = table;
-
-       return 0;
-}
-
-/**
- * Private Function used during initialization.
- * Initialize Platform Power Management Parameter table
- * @param hwmgr Pointer to the hardware manager.
- * @param atom_ppm_table Pointer to PPM table in VBIOS
- */
-static int get_platform_power_management_table(
-               struct pp_hwmgr *hwmgr,
-               ATOM_Tonga_PPM_Table *atom_ppm_table)
-{
-       struct phm_ppm_table *ptr = kzalloc(sizeof(ATOM_Tonga_PPM_Table), GFP_KERNEL);
-       struct phm_ppt_v1_information *pp_table_information =
-               (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (NULL == ptr)
-               return -ENOMEM;
-
-       ptr->ppm_design
-               = atom_ppm_table->ucPpmDesign;
-       ptr->cpu_core_number
-               = atom_ppm_table->usCpuCoreNumber;
-       ptr->platform_tdp
-               = atom_ppm_table->ulPlatformTDP;
-       ptr->small_ac_platform_tdp
-               = atom_ppm_table->ulSmallACPlatformTDP;
-       ptr->platform_tdc
-               = atom_ppm_table->ulPlatformTDC;
-       ptr->small_ac_platform_tdc
-               = atom_ppm_table->ulSmallACPlatformTDC;
-       ptr->apu_tdp
-               = atom_ppm_table->ulApuTDP;
-       ptr->dgpu_tdp
-               = atom_ppm_table->ulDGpuTDP;
-       ptr->dgpu_ulv_power
-               = atom_ppm_table->ulDGpuUlvPower;
-       ptr->tj_max
-               = atom_ppm_table->ulTjmax;
-
-       pp_table_information->ppm_parameter_table = ptr;
-
-       return 0;
-}
-
-/**
- * Private Function used during initialization.
- * Initialize TDP limits for DPM2
- * @param hwmgr Pointer to the hardware manager.
- * @param powerplay_table Pointer to the PowerPlay Table.
- */
-static int init_dpm_2_parameters(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
-               )
-{
-       int result = 0;
-       struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable);
-       ATOM_Tonga_PPM_Table *atom_ppm_table;
-       uint32_t disable_ppm = 0;
-       uint32_t disable_power_control = 0;
-
-       pp_table_information->us_ulv_voltage_offset =
-               le16_to_cpu(powerplay_table->usUlvVoltageOffset);
-
-       pp_table_information->ppm_parameter_table = NULL;
-       pp_table_information->vddc_lookup_table = NULL;
-       pp_table_information->vddgfx_lookup_table = NULL;
-       /* TDP limits */
-       hwmgr->platform_descriptor.TDPODLimit =
-               le16_to_cpu(powerplay_table->usPowerControlLimit);
-       hwmgr->platform_descriptor.TDPAdjustment = 0;
-       hwmgr->platform_descriptor.VidAdjustment = 0;
-       hwmgr->platform_descriptor.VidAdjustmentPolarity = 0;
-       hwmgr->platform_descriptor.VidMinLimit = 0;
-       hwmgr->platform_descriptor.VidMaxLimit = 1500000;
-       hwmgr->platform_descriptor.VidStep = 6250;
-
-       disable_power_control = 0;
-       if (0 == disable_power_control) {
-               /* enable TDP overdrive (PowerControl) feature as well if supported */
-               if (hwmgr->platform_descriptor.TDPODLimit != 0)
-                       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_PowerControl);
-       }
-
-       if (0 != powerplay_table->usVddcLookupTableOffset) {
-               const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable =
-                       (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
-                       le16_to_cpu(powerplay_table->usVddcLookupTableOffset));
-
-               result = get_vddc_lookup_table(hwmgr,
-                       &pp_table_information->vddc_lookup_table, pVddcCACTable, 16);
-       }
-
-       if (0 != powerplay_table->usVddgfxLookupTableOffset) {
-               const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable =
-                       (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) +
-                       le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset));
-
-               result = get_vddc_lookup_table(hwmgr,
-                       &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16);
-       }
-
-       disable_ppm = 0;
-       if (0 == disable_ppm) {
-               atom_ppm_table = (ATOM_Tonga_PPM_Table *)
-                       (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset));
-
-               if (0 != powerplay_table->usPPMTableOffset) {
-                       if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) {
-                               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                                       PHM_PlatformCaps_EnablePlatformPowerManagement);
-                       }
-               }
-       }
-
-       return result;
-}
-
-static int get_valid_clk(
-               struct pp_hwmgr *hwmgr,
-               struct phm_clock_array **clk_table,
-               const phm_ppt_v1_clock_voltage_dependency_table  * clk_volt_pp_table
-               )
-{
-       uint32_t table_size, i;
-       struct phm_clock_array *table;
-
-       PP_ASSERT_WITH_CODE((0 != clk_volt_pp_table->count),
-               "Invalid PowerPlay Table!", return -1);
-
-       table_size = sizeof(uint32_t) +
-               sizeof(uint32_t) * clk_volt_pp_table->count;
-
-       table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == table)
-               return -ENOMEM;
-
-       memset(table, 0x00, table_size);
-
-       table->count = (uint32_t)clk_volt_pp_table->count;
-
-       for (i = 0; i < table->count; i++)
-               table->values[i] = (uint32_t)clk_volt_pp_table->entries[i].clk;
-
-       *clk_table = table;
-
-       return 0;
-}
-
-static int get_hard_limits(
-               struct pp_hwmgr *hwmgr,
-               struct phm_clock_and_voltage_limits *limits,
-               const ATOM_Tonga_Hard_Limit_Table * limitable
-               )
-{
-       PP_ASSERT_WITH_CODE((0 != limitable->ucNumEntries), "Invalid PowerPlay Table!", return -1);
-
-       /* currently we always take entries[0] parameters */
-       limits->sclk = (uint32_t)limitable->entries[0].ulSCLKLimit;
-       limits->mclk = (uint32_t)limitable->entries[0].ulMCLKLimit;
-       limits->vddc = (uint16_t)limitable->entries[0].usVddcLimit;
-       limits->vddci = (uint16_t)limitable->entries[0].usVddciLimit;
-       limits->vddgfx = (uint16_t)limitable->entries[0].usVddgfxLimit;
-
-       return 0;
-}
-
-static int get_mclk_voltage_dependency_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_mclk_dep_table,
-               const ATOM_Tonga_MCLK_Dependency_Table * mclk_dep_table
-               )
-{
-       uint32_t table_size, i;
-       phm_ppt_v1_clock_voltage_dependency_table *mclk_table;
-
-       PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries),
-               "Invalid PowerPlay Table!", return -1);
-
-       table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
-               * mclk_dep_table->ucNumEntries;
-
-       mclk_table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == mclk_table)
-               return -ENOMEM;
-
-       memset(mclk_table, 0x00, table_size);
-
-       mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries;
-
-       for (i = 0; i < mclk_dep_table->ucNumEntries; i++) {
-               mclk_table->entries[i].vddInd =
-                       mclk_dep_table->entries[i].ucVddcInd;
-               mclk_table->entries[i].vdd_offset =
-                       mclk_dep_table->entries[i].usVddgfxOffset;
-               mclk_table->entries[i].vddci =
-                       mclk_dep_table->entries[i].usVddci;
-               mclk_table->entries[i].mvdd =
-                       mclk_dep_table->entries[i].usMvdd;
-               mclk_table->entries[i].clk =
-                       mclk_dep_table->entries[i].ulMclk;
-       }
-
-       *pp_tonga_mclk_dep_table = mclk_table;
-
-       return 0;
-}
-
-static int get_sclk_voltage_dependency_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_clock_voltage_dependency_table **pp_tonga_sclk_dep_table,
-               const PPTable_Generic_SubTable_Header *sclk_dep_table
-               )
-{
-       uint32_t table_size, i;
-       phm_ppt_v1_clock_voltage_dependency_table *sclk_table;
-
-       if (sclk_dep_table->ucRevId < 1) {
-               const ATOM_Tonga_SCLK_Dependency_Table *tonga_table =
-                           (ATOM_Tonga_SCLK_Dependency_Table *)sclk_dep_table;
-
-               PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries),
-                       "Invalid PowerPlay Table!", return -1);
-
-               table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
-                       * tonga_table->ucNumEntries;
-
-               sclk_table = kzalloc(table_size, GFP_KERNEL);
-
-               if (NULL == sclk_table)
-                       return -ENOMEM;
-
-               memset(sclk_table, 0x00, table_size);
-
-               sclk_table->count = (uint32_t)tonga_table->ucNumEntries;
-
-               for (i = 0; i < tonga_table->ucNumEntries; i++) {
-                       sclk_table->entries[i].vddInd =
-                               tonga_table->entries[i].ucVddInd;
-                       sclk_table->entries[i].vdd_offset =
-                               tonga_table->entries[i].usVddcOffset;
-                       sclk_table->entries[i].clk =
-                               tonga_table->entries[i].ulSclk;
-                       sclk_table->entries[i].cks_enable =
-                               (((tonga_table->entries[i].ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
-                       sclk_table->entries[i].cks_voffset =
-                               (tonga_table->entries[i].ucCKSVOffsetandDisable & 0x7F);
-               }
-       } else {
-               const ATOM_Polaris_SCLK_Dependency_Table *polaris_table =
-                           (ATOM_Polaris_SCLK_Dependency_Table *)sclk_dep_table;
-
-               PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries),
-                       "Invalid PowerPlay Table!", return -1);
-
-               table_size = sizeof(uint32_t) + sizeof(phm_ppt_v1_clock_voltage_dependency_record)
-                       * polaris_table->ucNumEntries;
-
-               sclk_table = kzalloc(table_size, GFP_KERNEL);
-
-               if (NULL == sclk_table)
-                       return -ENOMEM;
-
-               memset(sclk_table, 0x00, table_size);
-
-               sclk_table->count = (uint32_t)polaris_table->ucNumEntries;
-
-               for (i = 0; i < polaris_table->ucNumEntries; i++) {
-                       sclk_table->entries[i].vddInd =
-                               polaris_table->entries[i].ucVddInd;
-                       sclk_table->entries[i].vdd_offset =
-                               polaris_table->entries[i].usVddcOffset;
-                       sclk_table->entries[i].clk =
-                               polaris_table->entries[i].ulSclk;
-                       sclk_table->entries[i].cks_enable =
-                               (((polaris_table->entries[i].ucCKSVOffsetandDisable & 0x80) >> 7) == 0) ? 1 : 0;
-                       sclk_table->entries[i].cks_voffset =
-                               (polaris_table->entries[i].ucCKSVOffsetandDisable & 0x7F);
-                       sclk_table->entries[i].sclk_offset = polaris_table->entries[i].ulSclkOffset;
-               }
-       }
-       *pp_tonga_sclk_dep_table = sclk_table;
-
-       return 0;
-}
-
-static int get_pcie_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_pcie_table **pp_tonga_pcie_table,
-               const PPTable_Generic_SubTable_Header * pTable
-               )
-{
-       uint32_t table_size, i, pcie_count;
-       phm_ppt_v1_pcie_table *pcie_table;
-       struct phm_ppt_v1_information *pp_table_information =
-               (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       if (pTable->ucRevId < 1) {
-               const ATOM_Tonga_PCIE_Table *atom_pcie_table = (ATOM_Tonga_PCIE_Table *)pTable;
-               PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
-                       "Invalid PowerPlay Table!", return -1);
-
-               table_size = sizeof(uint32_t) +
-                       sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
-
-               pcie_table = kzalloc(table_size, GFP_KERNEL);
-
-               if (pcie_table == NULL)
-                       return -ENOMEM;
-
-               memset(pcie_table, 0x00, table_size);
-
-               /*
-               * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
-               * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
-               */
-               pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
-               if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
-                       pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
-               else
-                       printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \
-                       Disregarding the excess entries... \n");
-
-               pcie_table->count = pcie_count;
-
-               for (i = 0; i < pcie_count; i++) {
-                       pcie_table->entries[i].gen_speed =
-                               atom_pcie_table->entries[i].ucPCIEGenSpeed;
-                       pcie_table->entries[i].lane_width =
-                               atom_pcie_table->entries[i].usPCIELaneWidth;
-               }
-
-               *pp_tonga_pcie_table = pcie_table;
-       } else {
-               /* Polaris10/Polaris11 and newer. */
-               const ATOM_Polaris10_PCIE_Table *atom_pcie_table = (ATOM_Polaris10_PCIE_Table *)pTable;
-               PP_ASSERT_WITH_CODE((atom_pcie_table->ucNumEntries != 0),
-                       "Invalid PowerPlay Table!", return -1);
-
-               table_size = sizeof(uint32_t) +
-                       sizeof(phm_ppt_v1_pcie_record) * atom_pcie_table->ucNumEntries;
-
-               pcie_table = kzalloc(table_size, GFP_KERNEL);
-
-               if (pcie_table == NULL)
-                       return -ENOMEM;
-
-               memset(pcie_table, 0x00, table_size);
-
-               /*
-               * Make sure the number of pcie entries are less than or equal to sclk dpm levels.
-               * Since first PCIE entry is for ULV, #pcie has to be <= SclkLevel + 1.
-               */
-               pcie_count = (pp_table_information->vdd_dep_on_sclk->count) + 1;
-               if ((uint32_t)atom_pcie_table->ucNumEntries <= pcie_count)
-                       pcie_count = (uint32_t)atom_pcie_table->ucNumEntries;
-               else
-                       printk(KERN_ERR "[ powerplay ] Number of Pcie Entries exceed the number of SCLK Dpm Levels! \
-                       Disregarding the excess entries... \n");
-
-               pcie_table->count = pcie_count;
-
-               for (i = 0; i < pcie_count; i++) {
-                       pcie_table->entries[i].gen_speed =
-                               atom_pcie_table->entries[i].ucPCIEGenSpeed;
-                       pcie_table->entries[i].lane_width =
-                               atom_pcie_table->entries[i].usPCIELaneWidth;
-                       pcie_table->entries[i].pcie_sclk =
-                               atom_pcie_table->entries[i].ulPCIE_Sclk;
-               }
-
-               *pp_tonga_pcie_table = pcie_table;
-       }
-
-       return 0;
-}
-
-static int get_cac_tdp_table(
-               struct pp_hwmgr *hwmgr,
-               struct phm_cac_tdp_table **cac_tdp_table,
-               const PPTable_Generic_SubTable_Header * table
-               )
-{
-       uint32_t table_size;
-       struct phm_cac_tdp_table *tdp_table;
-
-       table_size = sizeof(uint32_t) + sizeof(struct phm_cac_tdp_table);
-       tdp_table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == tdp_table)
-               return -ENOMEM;
-
-       memset(tdp_table, 0x00, table_size);
-
-       hwmgr->dyn_state.cac_dtp_table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == hwmgr->dyn_state.cac_dtp_table) {
-               kfree(tdp_table);
-               return -ENOMEM;
-       }
-
-       memset(hwmgr->dyn_state.cac_dtp_table, 0x00, table_size);
-
-       if (table->ucRevId < 3) {
-               const ATOM_Tonga_PowerTune_Table *tonga_table =
-                       (ATOM_Tonga_PowerTune_Table *)table;
-               tdp_table->usTDP = tonga_table->usTDP;
-               tdp_table->usConfigurableTDP =
-                       tonga_table->usConfigurableTDP;
-               tdp_table->usTDC = tonga_table->usTDC;
-               tdp_table->usBatteryPowerLimit =
-                       tonga_table->usBatteryPowerLimit;
-               tdp_table->usSmallPowerLimit =
-                       tonga_table->usSmallPowerLimit;
-               tdp_table->usLowCACLeakage =
-                       tonga_table->usLowCACLeakage;
-               tdp_table->usHighCACLeakage =
-                       tonga_table->usHighCACLeakage;
-               tdp_table->usMaximumPowerDeliveryLimit =
-                       tonga_table->usMaximumPowerDeliveryLimit;
-               tdp_table->usDefaultTargetOperatingTemp =
-                       tonga_table->usTjMax;
-               tdp_table->usTargetOperatingTemp =
-                       tonga_table->usTjMax; /*Set the initial temp to the same as default */
-               tdp_table->usPowerTuneDataSetID =
-                       tonga_table->usPowerTuneDataSetID;
-               tdp_table->usSoftwareShutdownTemp =
-                       tonga_table->usSoftwareShutdownTemp;
-               tdp_table->usClockStretchAmount =
-                       tonga_table->usClockStretchAmount;
-       } else {   /* Fiji and newer */
-               const ATOM_Fiji_PowerTune_Table *fijitable =
-                       (ATOM_Fiji_PowerTune_Table *)table;
-               tdp_table->usTDP = fijitable->usTDP;
-               tdp_table->usConfigurableTDP = fijitable->usConfigurableTDP;
-               tdp_table->usTDC = fijitable->usTDC;
-               tdp_table->usBatteryPowerLimit = fijitable->usBatteryPowerLimit;
-               tdp_table->usSmallPowerLimit = fijitable->usSmallPowerLimit;
-               tdp_table->usLowCACLeakage = fijitable->usLowCACLeakage;
-               tdp_table->usHighCACLeakage = fijitable->usHighCACLeakage;
-               tdp_table->usMaximumPowerDeliveryLimit =
-                       fijitable->usMaximumPowerDeliveryLimit;
-               tdp_table->usDefaultTargetOperatingTemp =
-                       fijitable->usTjMax;
-               tdp_table->usTargetOperatingTemp =
-                       fijitable->usTjMax; /*Set the initial temp to the same as default */
-               tdp_table->usPowerTuneDataSetID =
-                       fijitable->usPowerTuneDataSetID;
-               tdp_table->usSoftwareShutdownTemp =
-                       fijitable->usSoftwareShutdownTemp;
-               tdp_table->usClockStretchAmount =
-                       fijitable->usClockStretchAmount;
-               tdp_table->usTemperatureLimitHotspot =
-                       fijitable->usTemperatureLimitHotspot;
-               tdp_table->usTemperatureLimitLiquid1 =
-                       fijitable->usTemperatureLimitLiquid1;
-               tdp_table->usTemperatureLimitLiquid2 =
-                       fijitable->usTemperatureLimitLiquid2;
-               tdp_table->usTemperatureLimitVrVddc =
-                       fijitable->usTemperatureLimitVrVddc;
-               tdp_table->usTemperatureLimitVrMvdd =
-                       fijitable->usTemperatureLimitVrMvdd;
-               tdp_table->usTemperatureLimitPlx =
-                       fijitable->usTemperatureLimitPlx;
-               tdp_table->ucLiquid1_I2C_address =
-                       fijitable->ucLiquid1_I2C_address;
-               tdp_table->ucLiquid2_I2C_address =
-                       fijitable->ucLiquid2_I2C_address;
-               tdp_table->ucLiquid_I2C_Line =
-                       fijitable->ucLiquid_I2C_Line;
-               tdp_table->ucVr_I2C_address = fijitable->ucVr_I2C_address;
-               tdp_table->ucVr_I2C_Line = fijitable->ucVr_I2C_Line;
-               tdp_table->ucPlx_I2C_address = fijitable->ucPlx_I2C_address;
-               tdp_table->ucPlx_I2C_Line = fijitable->ucPlx_I2C_Line;
-       }
-
-       *cac_tdp_table = tdp_table;
-
-       return 0;
-}
-
-static int get_mm_clock_voltage_table(
-               struct pp_hwmgr *hwmgr,
-               phm_ppt_v1_mm_clock_voltage_dependency_table **tonga_mm_table,
-               const ATOM_Tonga_MM_Dependency_Table * mm_dependency_table
-               )
-{
-       uint32_t table_size, i;
-       const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record;
-       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table;
-
-       PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries),
-               "Invalid PowerPlay Table!", return -1);
-       table_size = sizeof(uint32_t) +
-               sizeof(phm_ppt_v1_mm_clock_voltage_dependency_record)
-               * mm_dependency_table->ucNumEntries;
-       mm_table = kzalloc(table_size, GFP_KERNEL);
-
-       if (NULL == mm_table)
-               return -ENOMEM;
-
-       memset(mm_table, 0x00, table_size);
-
-       mm_table->count = mm_dependency_table->ucNumEntries;
-
-       for (i = 0; i < mm_dependency_table->ucNumEntries; i++) {
-               mm_dependency_record = &mm_dependency_table->entries[i];
-               mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd;
-               mm_table->entries[i].vddgfx_offset = mm_dependency_record->usVddgfxOffset;
-               mm_table->entries[i].aclk = mm_dependency_record->ulAClk;
-               mm_table->entries[i].samclock = mm_dependency_record->ulSAMUClk;
-               mm_table->entries[i].eclk = mm_dependency_record->ulEClk;
-               mm_table->entries[i].vclk = mm_dependency_record->ulVClk;
-               mm_table->entries[i].dclk = mm_dependency_record->ulDClk;
-       }
-
-       *tonga_mm_table = mm_table;
-
-       return 0;
-}
-
-/**
- * Private Function used during initialization.
- * Initialize clock voltage dependency
- * @param hwmgr Pointer to the hardware manager.
- * @param powerplay_table Pointer to the PowerPlay Table.
- */
-static int init_clock_voltage_dependency(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
-               )
-{
-       int result = 0;
-       struct phm_ppt_v1_information *pp_table_information =
-               (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table =
-               (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usMMDependencyTableOffset));
-       const PPTable_Generic_SubTable_Header *pPowerTuneTable =
-               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usPowerTuneTableOffset));
-       const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table =
-               (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usMclkDependencyTableOffset));
-       const PPTable_Generic_SubTable_Header *sclk_dep_table =
-               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usSclkDependencyTableOffset));
-       const ATOM_Tonga_Hard_Limit_Table *pHardLimits =
-               (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usHardLimitTableOffset));
-       const PPTable_Generic_SubTable_Header *pcie_table =
-               (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) +
-               le16_to_cpu(powerplay_table->usPCIETableOffset));
-
-       pp_table_information->vdd_dep_on_sclk = NULL;
-       pp_table_information->vdd_dep_on_mclk = NULL;
-       pp_table_information->mm_dep_table = NULL;
-       pp_table_information->pcie_table = NULL;
-
-       if (powerplay_table->usMMDependencyTableOffset != 0)
-               result = get_mm_clock_voltage_table(hwmgr,
-               &pp_table_information->mm_dep_table, mm_dependency_table);
-
-       if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0)
-               result = get_cac_tdp_table(hwmgr,
-               &pp_table_information->cac_dtp_table, pPowerTuneTable);
-
-       if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0)
-               result = get_sclk_voltage_dependency_table(hwmgr,
-               &pp_table_information->vdd_dep_on_sclk, sclk_dep_table);
-
-       if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0)
-               result = get_mclk_voltage_dependency_table(hwmgr,
-               &pp_table_information->vdd_dep_on_mclk, mclk_dep_table);
-
-       if (result == 0 && powerplay_table->usPCIETableOffset != 0)
-               result = get_pcie_table(hwmgr,
-               &pp_table_information->pcie_table, pcie_table);
-
-       if (result == 0 && powerplay_table->usHardLimitTableOffset != 0)
-               result = get_hard_limits(hwmgr,
-               &pp_table_information->max_clock_voltage_on_dc, pHardLimits);
-
-       hwmgr->dyn_state.max_clock_voltage_on_dc.sclk =
-               pp_table_information->max_clock_voltage_on_dc.sclk;
-       hwmgr->dyn_state.max_clock_voltage_on_dc.mclk =
-               pp_table_information->max_clock_voltage_on_dc.mclk;
-       hwmgr->dyn_state.max_clock_voltage_on_dc.vddc =
-               pp_table_information->max_clock_voltage_on_dc.vddc;
-       hwmgr->dyn_state.max_clock_voltage_on_dc.vddci =
-               pp_table_information->max_clock_voltage_on_dc.vddci;
-
-       if (result == 0 && (NULL != pp_table_information->vdd_dep_on_mclk)
-               && (0 != pp_table_information->vdd_dep_on_mclk->count))
-               result = get_valid_clk(hwmgr, &pp_table_information->valid_mclk_values,
-               pp_table_information->vdd_dep_on_mclk);
-
-       if (result == 0 && (NULL != pp_table_information->vdd_dep_on_sclk)
-               && (0 != pp_table_information->vdd_dep_on_sclk->count))
-               result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values,
-               pp_table_information->vdd_dep_on_sclk);
-
-       return result;
-}
-
-/** Retrieves the (signed) Overdrive limits from VBIOS.
- * The max engine clock, memory clock and max temperature come from the firmware info table.
- *
- * The information is placed into the platform descriptor.
- *
- * @param hwmgr source of the VBIOS table and owner of the platform descriptor to be updated.
- * @param powerplay_table the address of the PowerPlay table.
- *
- * @return 1 as long as the firmware info table was present and of a supported version.
- */
-static int init_over_drive_limits(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table)
-{
-       hwmgr->platform_descriptor.overdriveLimit.engineClock =
-               le16_to_cpu(powerplay_table->ulMaxODEngineClock);
-       hwmgr->platform_descriptor.overdriveLimit.memoryClock =
-               le16_to_cpu(powerplay_table->ulMaxODMemoryClock);
-
-       hwmgr->platform_descriptor.minOverdriveVDDC = 0;
-       hwmgr->platform_descriptor.maxOverdriveVDDC = 0;
-       hwmgr->platform_descriptor.overdriveVDDCStep = 0;
-
-       if (hwmgr->platform_descriptor.overdriveLimit.engineClock > 0 \
-               && hwmgr->platform_descriptor.overdriveLimit.memoryClock > 0) {
-               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-                       PHM_PlatformCaps_ACOverdriveSupport);
-       }
-
-       return 0;
-}
-
-/**
- * Private Function used during initialization.
- * Inspect the PowerPlay table for obvious signs of corruption.
- * @param hwmgr Pointer to the hardware manager.
- * @param powerplay_table Pointer to the PowerPlay Table.
- * @exception This implementation always returns 1.
- */
-static int init_thermal_controller(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
-               )
-{
-       const PPTable_Generic_SubTable_Header *fan_table;
-       ATOM_Tonga_Thermal_Controller *thermal_controller;
-
-       thermal_controller = (ATOM_Tonga_Thermal_Controller *)
-               (((unsigned long)powerplay_table) +
-               le16_to_cpu(powerplay_table->usThermalControllerOffset));
-       PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset),
-               "Thermal controller table not set!", return -1);
-
-       hwmgr->thermal_controller.ucType = thermal_controller->ucType;
-       hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine;
-       hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress;
-
-       hwmgr->thermal_controller.fanInfo.bNoFan =
-               (0 != (thermal_controller->ucFanParameters & ATOM_TONGA_PP_FANPARAMETERS_NOFAN));
-
-       hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution =
-               thermal_controller->ucFanParameters &
-               ATOM_TONGA_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK;
-
-       hwmgr->thermal_controller.fanInfo.ulMinRPM
-               = thermal_controller->ucFanMinRPM * 100UL;
-       hwmgr->thermal_controller.fanInfo.ulMaxRPM
-               = thermal_controller->ucFanMaxRPM * 100UL;
-
-       set_hw_cap(
-                       hwmgr,
-                       ATOM_TONGA_PP_THERMALCONTROLLER_NONE != hwmgr->thermal_controller.ucType,
-                       PHM_PlatformCaps_ThermalController
-                 );
-
-       if (0 == powerplay_table->usFanTableOffset)
-               return 0;
-
-       fan_table = (const PPTable_Generic_SubTable_Header *)
-               (((unsigned long)powerplay_table) +
-               le16_to_cpu(powerplay_table->usFanTableOffset));
-
-       PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset),
-               "Fan table not set!", return -1);
-       PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId),
-               "Unsupported fan table format!", return -1);
-
-       hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay
-               = 100000;
-       phm_cap_set(hwmgr->platform_descriptor.platformCaps,
-               PHM_PlatformCaps_MicrocodeFanControl);
-
-       if (fan_table->ucRevId < 8) {
-               const ATOM_Tonga_Fan_Table *tonga_fan_table =
-                       (ATOM_Tonga_Fan_Table *)fan_table;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
-                       = tonga_fan_table->ucTHyst;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMin
-                       = tonga_fan_table->usTMin;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMed
-                       = tonga_fan_table->usTMed;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
-                       = tonga_fan_table->usTHigh;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
-                       = tonga_fan_table->usPWMMin;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
-                       = tonga_fan_table->usPWMMed;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
-                       = tonga_fan_table->usPWMHigh;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
-                       = 10900;                  /* hard coded */
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
-                       = tonga_fan_table->usTMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
-                       = tonga_fan_table->ucFanControlMode;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
-                       = tonga_fan_table->usFanPWMMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
-                       = 4836;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
-                       = tonga_fan_table->usFanOutputSensitivity;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
-                       = tonga_fan_table->usFanRPMMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
-                       = (tonga_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places.  SMC wants MHz. */
-               hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
-                       = tonga_fan_table->ucTargetTemperature;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
-                       = tonga_fan_table->ucMinimumPWMLimit;
-       } else {
-               const ATOM_Fiji_Fan_Table *fiji_fan_table =
-                       (ATOM_Fiji_Fan_Table *)fan_table;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst
-                       = fiji_fan_table->ucTHyst;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMin
-                       = fiji_fan_table->usTMin;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMed
-                       = fiji_fan_table->usTMed;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTHigh
-                       = fiji_fan_table->usTHigh;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin
-                       = fiji_fan_table->usPWMMin;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed
-                       = fiji_fan_table->usPWMMed;
-               hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh
-                       = fiji_fan_table->usPWMHigh;
-               hwmgr->thermal_controller.advanceFanControlParameters.usTMax
-                       = fiji_fan_table->usTMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucFanControlMode
-                       = fiji_fan_table->ucFanControlMode;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanPWM
-                       = fiji_fan_table->usFanPWMMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity
-                       = 4836;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity
-                       = fiji_fan_table->usFanOutputSensitivity;
-               hwmgr->thermal_controller.advanceFanControlParameters.usDefaultMaxFanRPM
-                       = fiji_fan_table->usFanRPMMax;
-               hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit
-                       = (fiji_fan_table->ulMinFanSCLKAcousticLimit / 100); /* PPTable stores it in 10Khz unit for 2 decimal places.  SMC wants MHz. */
-               hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature
-                       = fiji_fan_table->ucTargetTemperature;
-               hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit
-                       = fiji_fan_table->ucMinimumPWMLimit;
-
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainEdge
-                       = fiji_fan_table->usFanGainEdge;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHotspot
-                       = fiji_fan_table->usFanGainHotspot;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainLiquid
-                       = fiji_fan_table->usFanGainLiquid;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrVddc
-                       = fiji_fan_table->usFanGainVrVddc;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainVrMvdd
-                       = fiji_fan_table->usFanGainVrMvdd;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainPlx
-                       = fiji_fan_table->usFanGainPlx;
-               hwmgr->thermal_controller.advanceFanControlParameters.usFanGainHbm
-                       = fiji_fan_table->usFanGainHbm;
-       }
-
-       return 0;
-}
-
-/**
- * Private Function used during initialization.
- * Inspect the PowerPlay table for obvious signs of corruption.
- * @param hwmgr Pointer to the hardware manager.
- * @param powerplay_table Pointer to the PowerPlay Table.
- * @exception 2 if the powerplay table is incorrect.
- */
-static int check_powerplay_tables(
-               struct pp_hwmgr *hwmgr,
-               const ATOM_Tonga_POWERPLAYTABLE *powerplay_table
-               )
-{
-       const ATOM_Tonga_State_Array *state_arrays;
-
-       state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) +
-               le16_to_cpu(powerplay_table->usStateArrayOffset));
-
-       PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <=
-               powerplay_table->sHeader.ucTableFormatRevision),
-               "Unsupported PPTable format!", return -1);
-       PP_ASSERT_WITH_CODE((0 != powerplay_table->usStateArrayOffset),
-               "State table is not set!", return -1);
-       PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize),
-               "Invalid PowerPlay Table!", return -1);
-       PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
-               "Invalid PowerPlay Table!", return -1);
-
-       return 0;
-}
-
-int tonga_pp_tables_initialize(struct pp_hwmgr *hwmgr)
-{
-       int result = 0;
-       const ATOM_Tonga_POWERPLAYTABLE *powerplay_table;
-
-       hwmgr->pptable = kzalloc(sizeof(struct phm_ppt_v1_information), GFP_KERNEL);
-
-       PP_ASSERT_WITH_CODE((NULL != hwmgr->pptable),
-                           "Failed to allocate hwmgr->pptable!", return -ENOMEM);
-
-       memset(hwmgr->pptable, 0x00, sizeof(struct phm_ppt_v1_information));
-
-       powerplay_table = get_powerplay_table(hwmgr);
-
-       PP_ASSERT_WITH_CODE((NULL != powerplay_table),
-               "Missing PowerPlay Table!", return -1);
-
-       result = check_powerplay_tables(hwmgr, powerplay_table);
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "check_powerplay_tables failed", return result);
-
-       result = set_platform_caps(hwmgr,
-                                  le32_to_cpu(powerplay_table->ulPlatformCaps));
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "set_platform_caps failed", return result);
-
-       result = init_thermal_controller(hwmgr, powerplay_table);
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "init_thermal_controller failed", return result);
-
-       result = init_over_drive_limits(hwmgr, powerplay_table);
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "init_over_drive_limits failed", return result);
-
-       result = init_clock_voltage_dependency(hwmgr, powerplay_table);
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "init_clock_voltage_dependency failed", return result);
-
-       result = init_dpm_2_parameters(hwmgr, powerplay_table);
-
-       PP_ASSERT_WITH_CODE((result == 0),
-                           "init_dpm_2_parameters failed", return result);
-
-       return result;
-}
-
-int tonga_pp_tables_uninitialize(struct pp_hwmgr *hwmgr)
-{
-       struct phm_ppt_v1_information *pp_table_information =
-               (struct phm_ppt_v1_information *)(hwmgr->pptable);
-
-       kfree(pp_table_information->vdd_dep_on_sclk);
-       pp_table_information->vdd_dep_on_sclk = NULL;
-
-       kfree(pp_table_information->vdd_dep_on_mclk);
-       pp_table_information->vdd_dep_on_mclk = NULL;
-
-       kfree(pp_table_information->valid_mclk_values);
-       pp_table_information->valid_mclk_values = NULL;
-
-       kfree(pp_table_information->valid_sclk_values);
-       pp_table_information->valid_sclk_values = NULL;
-
-       kfree(pp_table_information->vddc_lookup_table);
-       pp_table_information->vddc_lookup_table = NULL;
-
-       kfree(pp_table_information->vddgfx_lookup_table);
-       pp_table_information->vddgfx_lookup_table = NULL;
-
-       kfree(pp_table_information->mm_dep_table);
-       pp_table_information->mm_dep_table = NULL;
-
-       kfree(pp_table_information->cac_dtp_table);
-       pp_table_information->cac_dtp_table = NULL;
-
-       kfree(hwmgr->dyn_state.cac_dtp_table);
-       hwmgr->dyn_state.cac_dtp_table = NULL;
-
-       kfree(pp_table_information->ppm_parameter_table);
-       pp_table_information->ppm_parameter_table = NULL;
-
-       kfree(pp_table_information->pcie_table);
-       pp_table_information->pcie_table = NULL;
-
-       kfree(hwmgr->pptable);
-       hwmgr->pptable = NULL;
-
-       return 0;
-}
-
-const struct pp_table_func tonga_pptable_funcs = {
-       .pptable_init = tonga_pp_tables_initialize,
-       .pptable_fini = tonga_pp_tables_uninitialize,
-};
-
-int tonga_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr)
-{
-       const ATOM_Tonga_State_Array * state_arrays;
-       const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
-
-       PP_ASSERT_WITH_CODE((NULL != pp_table),
-                       "Missing PowerPlay Table!", return -1);
-       PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >=
-                       ATOM_Tonga_TABLE_REVISION_TONGA),
-                       "Incorrect PowerPlay table revision!", return -1);
-
-       state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
-                       le16_to_cpu(pp_table->usStateArrayOffset));
-
-       return (uint32_t)(state_arrays->ucNumEntries);
-}
-
-/**
-* Private function to convert flags stored in the BIOS to software flags in PowerPlay.
-*/
-static uint32_t make_classification_flags(struct pp_hwmgr *hwmgr,
-               uint16_t classification, uint16_t classification2)
-{
-       uint32_t result = 0;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_BOOT)
-               result |= PP_StateClassificationFlag_Boot;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_THERMAL)
-               result |= PP_StateClassificationFlag_Thermal;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
-               result |= PP_StateClassificationFlag_LimitedPowerSource;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_REST)
-               result |= PP_StateClassificationFlag_Rest;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_FORCED)
-               result |= PP_StateClassificationFlag_Forced;
-
-       if (classification & ATOM_PPLIB_CLASSIFICATION_ACPI)
-               result |= PP_StateClassificationFlag_ACPI;
-
-       if (classification2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2)
-               result |= PP_StateClassificationFlag_LimitedPowerSource_2;
-
-       return result;
-}
-
-/**
-* Create a Power State out of an entry in the PowerPlay table.
-* This function is called by the hardware back-end.
-* @param hwmgr Pointer to the hardware manager.
-* @param entry_index The index of the entry to be extracted from the table.
-* @param power_state The address of the PowerState instance being created.
-* @return -1 if the entry cannot be retrieved.
-*/
-int tonga_get_powerplay_table_entry(struct pp_hwmgr *hwmgr,
-               uint32_t entry_index, struct pp_power_state *power_state,
-               int (*call_back_func)(struct pp_hwmgr *, void *,
-                               struct pp_power_state *, void *, uint32_t))
-{
-       int result = 0;
-       const ATOM_Tonga_State_Array * state_arrays;
-       const ATOM_Tonga_State *state_entry;
-       const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr);
-
-       PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1;);
-       power_state->classification.bios_index = entry_index;
-
-       if (pp_table->sHeader.ucTableFormatRevision >=
-                       ATOM_Tonga_TABLE_REVISION_TONGA) {
-               state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) +
-                               le16_to_cpu(pp_table->usStateArrayOffset));
-
-               PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset),
-                               "Invalid PowerPlay Table State Array Offset.", return -1);
-               PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries),
-                               "Invalid PowerPlay Table State Array.", return -1);
-               PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries),
-                               "Invalid PowerPlay Table State Array Entry.", return -1);
-
-               state_entry = &(state_arrays->states[entry_index]);
-
-               result = call_back_func(hwmgr, (void *)state_entry, power_state,
-                               (void *)pp_table,
-                               make_classification_flags(hwmgr,
-                                       le16_to_cpu(state_entry->usClassification),
-                                       le16_to_cpu(state_entry->usClassification2)));
-       }
-
-       if (!result && (power_state->classification.flags &
-                       PP_StateClassificationFlag_Boot))
-               result = hwmgr->hwmgr_func->patch_boot_state(hwmgr, &(power_state->hardware));
-
-       return result;
-}
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_processpptables.h
deleted file mode 100644 (file)
index d24b888..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_PROCESSPPTABLES_H
-#define TONGA_PROCESSPPTABLES_H
-
-#include "hwmgr.h"
-
-extern const struct pp_table_func tonga_pptable_funcs;
-extern int tonga_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr);
-extern int tonga_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, uint32_t entry_index,
-               struct pp_power_state *power_state, int (*call_back_func)(struct pp_hwmgr *, void *,
-                               struct pp_power_state *, void *, uint32_t));
-
-#endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.c b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.c
deleted file mode 100644 (file)
index 47ef1ca..0000000
+++ /dev/null
@@ -1,590 +0,0 @@
-/*
- * Copyright 2015 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 <asm/div64.h>
-#include "tonga_thermal.h"
-#include "tonga_hwmgr.h"
-#include "tonga_smumgr.h"
-#include "tonga_ppsmc.h"
-#include "smu/smu_7_1_2_d.h"
-#include "smu/smu_7_1_2_sh_mask.h"
-
-/**
-* Get Fan Speed Control Parameters.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pSpeed is the address of the structure where the result is to be placed.
-* @exception Always succeeds except if we cannot zero out the output structure.
-*/
-int tonga_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info)
-{
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       fan_speed_info->supports_percent_read = true;
-       fan_speed_info->supports_percent_write = true;
-       fan_speed_info->min_percent = 0;
-       fan_speed_info->max_percent = 100;
-
-       if (0 != hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution) {
-               fan_speed_info->supports_rpm_read = true;
-               fan_speed_info->supports_rpm_write = true;
-               fan_speed_info->min_rpm = hwmgr->thermal_controller.fanInfo.ulMinRPM;
-               fan_speed_info->max_rpm = hwmgr->thermal_controller.fanInfo.ulMaxRPM;
-       } else {
-               fan_speed_info->min_rpm = 0;
-               fan_speed_info->max_rpm = 0;
-       }
-
-       return 0;
-}
-
-/**
-* Get Fan Speed in percent.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pSpeed is the address of the structure where the result is to be placed.
-* @exception Fails is the 100% setting appears to be 0.
-*/
-int tonga_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
-       duty = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_STATUS, FDO_PWM_DUTY);
-
-       if (0 == duty100)
-               return -EINVAL;
-
-
-       tmp64 = (uint64_t)duty * 100;
-       do_div(tmp64, duty100);
-       *speed = (uint32_t)tmp64;
-
-       if (*speed > 100)
-               *speed = 100;
-
-       return 0;
-}
-
-/**
-* Get Fan Speed in RPM.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the address of the structure where the result is to be placed.
-* @exception Returns not supported if no fan is found or if pulses per revolution are not set
-*/
-int tonga_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed)
-{
-       return 0;
-}
-
-/**
-* Set Fan Speed Control to static mode, so that the user can decide what speed to use.
-* @param    hwmgr  the address of the powerplay hardware manager.
-*           mode    the fan control mode, 0 default, 1 by percent, 5, by RPM
-* @exception Should always succeed.
-*/
-int tonga_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
-{
-
-       if (hwmgr->fan_ctrl_is_in_default_mode) {
-               hwmgr->fan_ctrl_default_mode = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, FDO_PWM_MODE);
-               hwmgr->tmin = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, TMIN);
-               hwmgr->fan_ctrl_is_in_default_mode = false;
-       }
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, TMIN, 0);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, FDO_PWM_MODE, mode);
-
-       return 0;
-}
-
-/**
-* Reset Fan Speed Control to default mode.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Should always succeed.
-*/
-int tonga_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->fan_ctrl_is_in_default_mode) {
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, FDO_PWM_MODE, hwmgr->fan_ctrl_default_mode);
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, TMIN, hwmgr->tmin);
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       }
-
-       return 0;
-}
-
-int tonga_fan_ctrl_start_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_ODFuzzyFanControlSupport)) {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_FUZZY);
-               result = (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl) == 0) ?  0 : -EINVAL;
-/*
-               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_FanSpeedInTableIsRPM))
-                       hwmgr->set_max_fan_rpm_output(hwmgr, hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanRPM);
-               else
-                       hwmgr->set_max_fan_pwm_output(hwmgr, hwmgr->thermal_controller.advanceFanControlParameters.usMaxFanPWM);
-*/
-       } else {
-               cgs_write_register(hwmgr->device, mmSMC_MSG_ARG_0, FAN_CONTROL_TABLE);
-               result = (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StartFanControl) == 0) ?  0 : -EINVAL;
-       }
-/* TO DO FOR SOME DEVICE ID 0X692b, send this msg return invalid command.
-       if (result == 0 && hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature != 0)
-               result = (0 == smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_SetFanTemperatureTarget, \
-                                                               hwmgr->thermal_controller.advanceFanControlParameters.ucTargetTemperature) ? 0 : -EINVAL);
-*/
-       return result;
-}
-
-
-int tonga_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr)
-{
-       return (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_StopFanControl) == 0) ?  0 : -EINVAL;
-}
-
-/**
-* Set Fan Speed in percent.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (0% - 100%) to be set.
-* @exception Fails is the 100% setting appears to be 0.
-*/
-int tonga_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed)
-{
-       uint32_t duty100;
-       uint32_t duty;
-       uint64_t tmp64;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return -EINVAL;
-
-       if (speed > 100)
-               speed = 100;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
-               tonga_fan_ctrl_stop_smc_fan_control(hwmgr);
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (0 == duty100)
-               return -EINVAL;
-
-       tmp64 = (uint64_t)speed * duty100;
-       do_div(tmp64, 100);
-       duty = (uint32_t)tmp64;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL0, FDO_STATIC_DUTY, duty);
-
-       return tonga_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-}
-
-/**
-* Reset Fan Speed to default.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @exception Always succeeds.
-*/
-int tonga_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               return 0;
-
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl)) {
-               result = tonga_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-               if (0 == result)
-                       result = tonga_fan_ctrl_start_smc_fan_control(hwmgr);
-       } else
-               result = tonga_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set Fan Speed in RPM.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    speed is the percentage value (min - max) to be set.
-* @exception Fails is the speed not lie between min and max.
-*/
-int tonga_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed)
-{
-       return 0;
-}
-
-/**
-* Reads the remote temperature from the SIslands thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-int tonga_thermal_get_temperature(struct pp_hwmgr *hwmgr)
-{
-       int temp;
-
-       temp = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_STATUS, CTF_TEMP);
-
-/* Bit 9 means the reading is lower than the lowest usable value. */
-       if (0 != (0x200 & temp))
-               temp = TONGA_THERMAL_MAXIMUM_TEMP_READING;
-       else
-               temp = (temp & 0x1ff);
-
-       temp = temp * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       return temp;
-}
-
-/**
-* Set the requested temperature range for high and low alert signals
-*
-* @param    hwmgr The address of the hardware manager.
-* @param    range Temperature range to be programmed for high and low alert signals
-* @exception PP_Result_BadInput if the input data is not valid.
-*/
-static int tonga_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, uint32_t low_temp, uint32_t high_temp)
-{
-       uint32_t low = TONGA_THERMAL_MINIMUM_ALERT_TEMP * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-       uint32_t high = TONGA_THERMAL_MAXIMUM_ALERT_TEMP * PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
-
-       if (low < low_temp)
-               low = low_temp;
-       if (high > high_temp)
-               high = high_temp;
-
-       if (low > high)
-               return -EINVAL;
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, DIG_THERM_INTH, (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, DIG_THERM_INTL, (low / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_CTRL, DIG_THERM_DPM, (high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES));
-
-       return 0;
-}
-
-/**
-* Programs thermal controller one-time setting registers
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int tonga_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       if (0 != hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution)
-               PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
-                                               CG_TACH_CTRL, EDGE_PER_REV,
-                                               hwmgr->thermal_controller.fanInfo.ucTachometerPulsesPerRevolution - 1);
-
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL2, TACH_PWM_RESP_RATE, 0x28);
-
-       return 0;
-}
-
-/**
-* Enable thermal alerts on the RV770 thermal controller.
-*
-* @param    hwmgr The address of the hardware manager.
-*/
-static int tonga_thermal_enable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, THERM_INT_MASK);
-       alert &= ~(TONGA_THERMAL_HIGH_ALERT_MASK | TONGA_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to enable internal thermal interrupts */
-       return (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Enable) == 0) ? 0 : -1;
-}
-
-/**
-* Disable thermal alerts on the RV770 thermal controller.
-* @param    hwmgr The address of the hardware manager.
-*/
-static int tonga_thermal_disable_alert(struct pp_hwmgr *hwmgr)
-{
-       uint32_t alert;
-
-       alert = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, THERM_INT_MASK);
-       alert |= (TONGA_THERMAL_HIGH_ALERT_MASK | TONGA_THERMAL_LOW_ALERT_MASK);
-       PHM_WRITE_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_THERMAL_INT, THERM_INT_MASK, alert);
-
-       /* send message to SMU to disable internal thermal interrupts */
-       return (smum_send_msg_to_smc(hwmgr->smumgr, PPSMC_MSG_Thermal_Cntl_Disable) == 0) ? 0 : -1;
-}
-
-/**
-* Uninitialize the thermal controller.
-* Currently just disables alerts.
-* @param    hwmgr The address of the hardware manager.
-*/
-int tonga_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       int result = tonga_thermal_disable_alert(hwmgr);
-
-       if (hwmgr->thermal_controller.fanInfo.bNoFan)
-               tonga_fan_ctrl_set_default_mode(hwmgr);
-
-       return result;
-}
-
-/**
-* Set up the fan table to control the fan using the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-       struct tonga_hwmgr *data = (struct tonga_hwmgr *)(hwmgr->backend);
-       SMU72_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
-       uint32_t duty100;
-       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
-       uint16_t fdo_min, slope1, slope2;
-       uint32_t reference_clock;
-       int res;
-       uint64_t tmp64;
-
-       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
-               return 0;
-
-       if (0 == data->fan_table_start) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
-
-       if (0 == duty100) {
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
-               return 0;
-       }
-
-       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
-       do_div(tmp64, 10000);
-       fdo_min = (uint16_t)tmp64;
-
-       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
-       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
-
-       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
-       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
-
-       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
-       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
-
-       fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
-       fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
-       fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
-
-       fan_table.Slope1 = cpu_to_be16(slope1);
-       fan_table.Slope2 = cpu_to_be16(slope2);
-
-       fan_table.FdoMin = cpu_to_be16(fdo_min);
-
-       fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
-
-       fan_table.HystUp = cpu_to_be16(1);
-
-       fan_table.HystSlope = cpu_to_be16(1);
-
-       fan_table.TempRespLim = cpu_to_be16(5);
-
-       reference_clock = tonga_get_xclk(hwmgr);
-
-       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
-
-       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
-
-       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
-
-       fan_table.FanControl_GL_Flag = 1;
-
-       res = tonga_copy_bytes_to_smc(hwmgr->smumgr, data->fan_table_start, (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), data->sram_end);
-/* TO DO FOR SOME DEVICE ID 0X692b, send this msg return invalid command.
-       if (res == 0 && hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit != 0)
-               res = (0 == smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_SetFanMinPwm, \
-                                               hwmgr->thermal_controller.advanceFanControlParameters.ucMinimumPWMLimit) ? 0 : -1);
-
-       if (res == 0 && hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit != 0)
-               res = (0 == smum_send_msg_to_smc_with_parameter(hwmgr->smumgr, PPSMC_MSG_SetFanSclkTarget, \
-                                       hwmgr->thermal_controller.advanceFanControlParameters.ulMinFanSCLKAcousticLimit) ? 0 : -1);
-
-       if (0 != res)
-               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
-*/
-       return 0;
-}
-
-/**
-* Start the fan control on the SMC.
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_tonga_thermal_start_smc_fan_control(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-/* If the fantable setup has failed we could have disabled PHM_PlatformCaps_MicrocodeFanControl even after this function was included in the table.
- * Make sure that we still think controlling the fan is OK.
-*/
-       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl)) {
-               tonga_fan_ctrl_start_smc_fan_control(hwmgr);
-               tonga_fan_ctrl_set_static_mode(hwmgr, FDO_PWM_MODE_STATIC);
-       }
-
-       return 0;
-}
-
-/**
-* Set temperature range for high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from set temperature range routine
-*/
-int tf_tonga_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-       struct PP_TemperatureRange *range = (struct PP_TemperatureRange *)input;
-
-       if (range == NULL)
-               return -EINVAL;
-
-       return tonga_thermal_set_temperature_range(hwmgr, range->min, range->max);
-}
-
-/**
-* Programs one-time setting registers
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from initialize thermal controller routine
-*/
-int tf_tonga_thermal_initialize(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-    return tonga_thermal_initialize(hwmgr);
-}
-
-/**
-* Enable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from enable alert routine
-*/
-int tf_tonga_thermal_enable_alert(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-       return tonga_thermal_enable_alert(hwmgr);
-}
-
-/**
-* Disable high and low alerts
-* @param    hwmgr  the address of the powerplay hardware manager.
-* @param    pInput the pointer to input data
-* @param    pOutput the pointer to output data
-* @param    pStorage the pointer to temporary storage
-* @param    Result the last failure code
-* @return   result from disable alert routine
-*/
-static int tf_tonga_thermal_disable_alert(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result)
-{
-       return tonga_thermal_disable_alert(hwmgr);
-}
-
-static const struct phm_master_table_item tonga_thermal_start_thermal_controller_master_list[] = {
-       { NULL, tf_tonga_thermal_initialize },
-       { NULL, tf_tonga_thermal_set_temperature_range },
-       { NULL, tf_tonga_thermal_enable_alert },
-/* We should restrict performance levels to low before we halt the SMC.
- * On the other hand we are still in boot state when we do this so it would be pointless.
- * If this assumption changes we have to revisit this table.
- */
-       { NULL, tf_tonga_thermal_setup_fan_table},
-       { NULL, tf_tonga_thermal_start_smc_fan_control},
-       { NULL, NULL }
-};
-
-static const struct phm_master_table_header tonga_thermal_start_thermal_controller_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       tonga_thermal_start_thermal_controller_master_list
-};
-
-static const struct phm_master_table_item tonga_thermal_set_temperature_range_master_list[] = {
-       { NULL, tf_tonga_thermal_disable_alert},
-       { NULL, tf_tonga_thermal_set_temperature_range},
-       { NULL, tf_tonga_thermal_enable_alert},
-       { NULL, NULL }
-};
-
-static const struct phm_master_table_header tonga_thermal_set_temperature_range_master = {
-       0,
-       PHM_MasterTableFlag_None,
-       tonga_thermal_set_temperature_range_master_list
-};
-
-int tonga_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr)
-{
-       if (!hwmgr->thermal_controller.fanInfo.bNoFan)
-               tonga_fan_ctrl_set_default_mode(hwmgr);
-       return 0;
-}
-
-/**
-* Initializes the thermal controller related functions in the Hardware Manager structure.
-* @param    hwmgr The address of the hardware manager.
-* @exception Any error code from the low-level communication.
-*/
-int pp_tonga_thermal_initialize(struct pp_hwmgr *hwmgr)
-{
-       int result;
-
-       result = phm_construct_table(hwmgr, &tonga_thermal_set_temperature_range_master, &(hwmgr->set_temperature_range));
-
-       if (0 == result) {
-               result = phm_construct_table(hwmgr,
-                                               &tonga_thermal_start_thermal_controller_master,
-                                               &(hwmgr->start_thermal_controller));
-               if (0 != result)
-                       phm_destroy_table(hwmgr, &(hwmgr->set_temperature_range));
-       }
-
-       if (0 == result)
-               hwmgr->fan_ctrl_is_in_default_mode = true;
-       return result;
-}
-
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.h b/drivers/gpu/drm/amd/powerplay/hwmgr/tonga_thermal.h
deleted file mode 100644 (file)
index aa335f2..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright 2015 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 TONGA_THERMAL_H
-#define TONGA_THERMAL_H
-
-#include "hwmgr.h"
-
-#define TONGA_THERMAL_HIGH_ALERT_MASK         0x1
-#define TONGA_THERMAL_LOW_ALERT_MASK          0x2
-
-#define TONGA_THERMAL_MINIMUM_TEMP_READING    -256
-#define TONGA_THERMAL_MAXIMUM_TEMP_READING    255
-
-#define TONGA_THERMAL_MINIMUM_ALERT_TEMP      0
-#define TONGA_THERMAL_MAXIMUM_ALERT_TEMP      255
-
-#define FDO_PWM_MODE_STATIC  1
-#define FDO_PWM_MODE_STATIC_RPM 5
-
-
-extern int tf_tonga_thermal_initialize(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_tonga_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-extern int tf_tonga_thermal_enable_alert(struct pp_hwmgr *hwmgr, void *input, void *output, void *storage, int result);
-
-extern int tonga_thermal_get_temperature(struct pp_hwmgr *hwmgr);
-extern int tonga_thermal_stop_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int tonga_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr, struct phm_fan_speed_info *fan_speed_info);
-extern int tonga_fan_ctrl_get_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int tonga_fan_ctrl_set_default_mode(struct pp_hwmgr *hwmgr);
-extern int tonga_fan_ctrl_set_static_mode(struct pp_hwmgr *hwmgr, uint32_t mode);
-extern int tonga_fan_ctrl_set_fan_speed_percent(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int tonga_fan_ctrl_reset_fan_speed_to_default(struct pp_hwmgr *hwmgr);
-extern int pp_tonga_thermal_initialize(struct pp_hwmgr *hwmgr);
-extern int tonga_thermal_ctrl_uninitialize_thermal_controller(struct pp_hwmgr *hwmgr);
-extern int tonga_fan_ctrl_set_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t speed);
-extern int tonga_fan_ctrl_get_fan_speed_rpm(struct pp_hwmgr *hwmgr, uint32_t *speed);
-extern int tonga_fan_ctrl_stop_smc_fan_control(struct pp_hwmgr *hwmgr);
-
-#endif
-
index b764c8c..3fb5e57 100644 (file)
 #include "amd_shared.h"
 #include "cgs_common.h"
 
+enum amd_pp_sensors {
+       AMDGPU_PP_SENSOR_GFX_SCLK = 0,
+       AMDGPU_PP_SENSOR_VDDNB,
+       AMDGPU_PP_SENSOR_VDDGFX,
+       AMDGPU_PP_SENSOR_UVD_VCLK,
+       AMDGPU_PP_SENSOR_UVD_DCLK,
+       AMDGPU_PP_SENSOR_VCE_ECCLK,
+       AMDGPU_PP_SENSOR_GPU_LOAD,
+       AMDGPU_PP_SENSOR_GFX_MCLK,
+       AMDGPU_PP_SENSOR_GPU_TEMP,
+       AMDGPU_PP_SENSOR_VCE_POWER,
+       AMDGPU_PP_SENSOR_UVD_POWER,
+};
 
 enum amd_pp_event {
        AMD_PP_EVENT_INITIALIZE = 0,
@@ -131,9 +144,8 @@ struct amd_pp_init {
        struct cgs_device *device;
        uint32_t chip_family;
        uint32_t chip_id;
-       uint32_t rev_id;
-       bool powercontainment_enabled;
 };
+
 enum amd_pp_display_config_type{
        AMD_PP_DisplayConfigType_None = 0,
        AMD_PP_DisplayConfigType_DP54 ,
@@ -261,6 +273,7 @@ enum amd_pp_clock_type {
 struct amd_pp_clocks {
        uint32_t count;
        uint32_t clock[MAX_NUM_CLOCKS];
+       uint32_t latency[MAX_NUM_CLOCKS];
 };
 
 
@@ -332,8 +345,6 @@ struct amd_powerplay_funcs {
        int (*powergate_uvd)(void *handle, bool gate);
        int (*dispatch_tasks)(void *handle, enum amd_pp_event event_id,
                                   void *input, void *output);
-       void (*print_current_performance_level)(void *handle,
-                                                     struct seq_file *m);
        int (*set_fan_control_mode)(void *handle, uint32_t mode);
        int (*get_fan_control_mode)(void *handle);
        int (*set_fan_speed_percent)(void *handle, uint32_t percent);
@@ -347,6 +358,7 @@ struct amd_powerplay_funcs {
        int (*set_sclk_od)(void *handle, uint32_t value);
        int (*get_mclk_od)(void *handle);
        int (*set_mclk_od)(void *handle, uint32_t value);
+       int (*read_sensor)(void *handle, int idx, int32_t *value);
 };
 
 struct amd_powerplay {
@@ -378,4 +390,6 @@ int amd_powerplay_get_clock_by_type(void *handle,
 int amd_powerplay_get_display_mode_validation_clocks(void *handle,
                struct amd_pp_simple_clock_info *output);
 
+int amd_set_clockgating_by_smu(void *handle, uint32_t msg_id);
+
 #endif /* _AMD_POWERPLAY_H_ */
index 962cb53..d449583 100644 (file)
@@ -341,7 +341,6 @@ extern int phm_powerdown_uvd(struct pp_hwmgr *hwmgr);
 extern int phm_setup_asic(struct pp_hwmgr *hwmgr);
 extern int phm_enable_dynamic_state_management(struct pp_hwmgr *hwmgr);
 extern int phm_disable_dynamic_state_management(struct pp_hwmgr *hwmgr);
-extern void phm_init_dynamic_caps(struct pp_hwmgr *hwmgr);
 extern bool phm_is_hw_access_blocked(struct pp_hwmgr *hwmgr);
 extern int phm_block_hw_access(struct pp_hwmgr *hwmgr, bool block);
 extern int phm_set_power_state(struct pp_hwmgr *hwmgr,
index bf0d2ac..4f0fedd 100644 (file)
 #include "hwmgr_ppt.h"
 #include "ppatomctrl.h"
 #include "hwmgr_ppt.h"
+#include "power_state.h"
 
 struct pp_instance;
 struct pp_hwmgr;
-struct pp_hw_power_state;
-struct pp_power_state;
-struct PP_VCEState;
 struct phm_fan_speed_info;
 struct pp_atomctrl_voltage_table;
 
+extern int amdgpu_powercontainment;
+extern int amdgpu_sclk_deep_sleep_en;
+extern unsigned amdgpu_pp_feature_mask;
+
+#define VOLTAGE_SCALE 4
+
+uint8_t convert_to_vid(uint16_t vddc);
 
 enum DISPLAY_GAP {
        DISPLAY_GAP_VBLANK_OR_WM = 0,   /* Wait for vblank or MCHG watermark. */
@@ -49,7 +54,6 @@ enum DISPLAY_GAP {
 };
 typedef enum DISPLAY_GAP DISPLAY_GAP;
 
-
 struct vi_dpm_level {
        bool enabled;
        uint32_t value;
@@ -71,6 +75,19 @@ enum PP_Result {
 #define PCIE_PERF_REQ_GEN2         3
 #define PCIE_PERF_REQ_GEN3         4
 
+enum PP_FEATURE_MASK {
+       PP_SCLK_DPM_MASK = 0x1,
+       PP_MCLK_DPM_MASK = 0x2,
+       PP_PCIE_DPM_MASK = 0x4,
+       PP_SCLK_DEEP_SLEEP_MASK = 0x8,
+       PP_POWER_CONTAINMENT_MASK = 0x10,
+       PP_UVD_HANDSHAKE_MASK = 0x20,
+       PP_SMC_VOLTAGE_CONTROL_MASK = 0x40,
+       PP_VBI_TIME_SUPPORT_MASK = 0x80,
+       PP_ULV_MASK = 0x100,
+       PP_ENABLE_GFX_CG_THRU_SMU = 0x200
+};
+
 enum PHM_BackEnd_Magic {
        PHM_Dummy_Magic       = 0xAA5555AA,
        PHM_RV770_Magic       = 0xDCBAABCD,
@@ -294,8 +311,6 @@ struct pp_hwmgr_func {
        int (*get_sclk)(struct pp_hwmgr *hwmgr, bool low);
        int (*power_state_set)(struct pp_hwmgr *hwmgr,
                                                const void *state);
-       void (*print_current_perforce_level)(struct pp_hwmgr *hwmgr,
-                                                       struct seq_file *m);
        int (*enable_clock_power_gating)(struct pp_hwmgr *hwmgr);
        int (*notify_smc_display_config_after_ps_adjustment)(struct pp_hwmgr *hwmgr);
        int (*display_config_changed)(struct pp_hwmgr *hwmgr);
@@ -342,6 +357,7 @@ struct pp_hwmgr_func {
        int (*set_sclk_od)(struct pp_hwmgr *hwmgr, uint32_t value);
        int (*get_mclk_od)(struct pp_hwmgr *hwmgr);
        int (*set_mclk_od)(struct pp_hwmgr *hwmgr, uint32_t value);
+       int (*read_sensor)(struct pp_hwmgr *hwmgr, int idx, int32_t *value);
 };
 
 struct pp_table_func {
@@ -351,7 +367,7 @@ struct pp_table_func {
        int (*pptable_get_vce_state_table_entry)(
                                                struct pp_hwmgr *hwmgr,
                                                unsigned long i,
-                                               struct PP_VCEState *vce_state,
+                                               struct pp_vce_state *vce_state,
                                                void **clock_info,
                                                unsigned long *flag);
 };
@@ -570,22 +586,43 @@ struct phm_microcode_version_info {
        uint32_t NB;
 };
 
+#define PP_MAX_VCE_LEVELS 6
+
+enum PP_VCE_LEVEL {
+       PP_VCE_LEVEL_AC_ALL = 0,     /* AC, All cases */
+       PP_VCE_LEVEL_DC_EE = 1,      /* DC, entropy encoding */
+       PP_VCE_LEVEL_DC_LL_LOW = 2,  /* DC, low latency queue, res <= 720 */
+       PP_VCE_LEVEL_DC_LL_HIGH = 3, /* DC, low latency queue, 1080 >= res > 720 */
+       PP_VCE_LEVEL_DC_GP_LOW = 4,  /* DC, general purpose queue, res <= 720 */
+       PP_VCE_LEVEL_DC_GP_HIGH = 5, /* DC, general purpose queue, 1080 >= res > 720 */
+};
+
+
+enum PP_TABLE_VERSION {
+       PP_TABLE_V0 = 0,
+       PP_TABLE_V1,
+       PP_TABLE_V2,
+       PP_TABLE_MAX
+};
+
 /**
  * The main hardware manager structure.
  */
 struct pp_hwmgr {
        uint32_t chip_family;
        uint32_t chip_id;
-       uint32_t hw_revision;
-       uint32_t sub_sys_id;
-       uint32_t sub_vendor_id;
 
+       uint32_t pp_table_version;
        void *device;
        struct pp_smumgr *smumgr;
        const void *soft_pp_table;
        uint32_t soft_pp_table_size;
        void *hardcode_pp_table;
        bool need_pp_table_upload;
+
+       struct pp_vce_state vce_states[PP_MAX_VCE_LEVELS];
+       uint32_t num_vce_state_tables;
+
        enum amd_dpm_forced_level dpm_level;
        bool block_hw_access;
        struct phm_gfx_arbiter gfx_arbiter;
@@ -614,7 +651,6 @@ struct pp_hwmgr {
        uint32_t num_ps;
        struct pp_thermal_controller_info thermal_controller;
        bool fan_ctrl_is_in_default_mode;
-       bool powercontainment_enabled;
        uint32_t fan_ctrl_default_mode;
        uint32_t tmin;
        struct phm_microcode_version_info microcode_version_info;
@@ -624,6 +660,7 @@ struct pp_hwmgr {
        struct pp_power_state    *boot_ps;
        struct pp_power_state    *uvd_ps;
        struct amd_pp_display_configuration display_config;
+       uint32_t feature_mask;
 };
 
 
@@ -637,16 +674,7 @@ extern int hw_init_power_state_table(struct pp_hwmgr *hwmgr);
 extern int phm_wait_on_register(struct pp_hwmgr *hwmgr, uint32_t index,
                                uint32_t value, uint32_t mask);
 
-extern int phm_wait_for_register_unequal(struct pp_hwmgr *hwmgr,
-                               uint32_t index, uint32_t value, uint32_t mask);
 
-extern uint32_t phm_read_indirect_register(struct pp_hwmgr *hwmgr,
-               uint32_t indirect_port, uint32_t index);
-
-extern void phm_write_indirect_register(struct pp_hwmgr *hwmgr,
-               uint32_t indirect_port,
-               uint32_t index,
-               uint32_t value);
 
 extern void phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr,
                                uint32_t indirect_port,
@@ -654,12 +682,7 @@ extern void phm_wait_on_indirect_register(struct pp_hwmgr *hwmgr,
                                uint32_t value,
                                uint32_t mask);
 
-extern void phm_wait_for_indirect_register_unequal(
-                               struct pp_hwmgr *hwmgr,
-                               uint32_t indirect_port,
-                               uint32_t index,
-                               uint32_t value,
-                               uint32_t mask);
+
 
 extern bool phm_cf_want_uvd_power_gating(struct pp_hwmgr *hwmgr);
 extern bool phm_cf_want_vce_power_gating(struct pp_hwmgr *hwmgr);
@@ -673,6 +696,8 @@ extern void phm_trim_voltage_table_to_fit_state_table(uint32_t max_vol_steps, st
 extern int phm_reset_single_dpm_table(void *table, uint32_t count, int max);
 extern void phm_setup_pcie_table_entry(void *table, uint32_t index, uint32_t pcie_gen, uint32_t pcie_lanes);
 extern int32_t phm_get_dpm_level_enable_mask_value(void *table);
+extern uint8_t phm_get_voltage_id(struct pp_atomctrl_voltage_table *voltage_table,
+               uint32_t voltage);
 extern uint8_t phm_get_voltage_index(struct phm_ppt_v1_voltage_lookup_table *lookup_table, uint16_t voltage);
 extern uint16_t phm_find_closest_vddci(struct pp_atomctrl_voltage_table *vddci_table, uint16_t vddci);
 extern int phm_find_boot_level(void *table, uint32_t value, uint32_t *boot_level);
@@ -683,6 +708,10 @@ extern int phm_hwmgr_backend_fini(struct pp_hwmgr *hwmgr);
 extern uint32_t phm_get_lowest_enabled_level(struct pp_hwmgr *hwmgr, uint32_t mask);
 extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr);
 
+extern int smu7_hwmgr_init(struct pp_hwmgr *hwmgr);
+extern int phm_get_voltage_evv_on_sclk(struct pp_hwmgr *hwmgr, uint8_t voltage_type,
+                               uint32_t sclk, uint16_t id, uint16_t *voltage);
+
 #define PHM_ENTIRE_REGISTER_MASK 0xFFFFFFFFU
 
 #define PHM_FIELD_SHIFT(reg, field) reg##__##field##__SHIFT
@@ -697,44 +726,6 @@ extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr);
         PHM_FIELD_SHIFT(reg, field))
 
 
-#define PHM_WAIT_REGISTER_GIVEN_INDEX(hwmgr, index, value, mask)       \
-       phm_wait_on_register(hwmgr, index, value, mask)
-
-#define PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, index, value, mask)       \
-       phm_wait_for_register_unequal(hwmgr, index, value, mask)
-
-#define PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, index, value, mask)        \
-       phm_wait_on_indirect_register(hwmgr, mm##port##_INDEX, index, value, mask)
-
-#define PHM_WAIT_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, index, value, mask)        \
-       phm_wait_for_indirect_register_unequal(hwmgr, mm##port##_INDEX, index, value, mask)
-
-#define PHM_WAIT_VFPF_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, index, value, mask)   \
-       phm_wait_on_indirect_register(hwmgr, mm##port##_INDEX_0, index, value, mask)
-
-#define PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, index, value, mask)   \
-       phm_wait_for_indirect_register_unequal(hwmgr, mm##port##_INDEX_0, index, value, mask)
-
-/* Operations on named registers. */
-
-#define PHM_WAIT_REGISTER(hwmgr, reg, value, mask)     \
-       PHM_WAIT_REGISTER_GIVEN_INDEX(hwmgr, mm##reg, value, mask)
-
-#define PHM_WAIT_REGISTER_UNEQUAL(hwmgr, reg, value, mask)     \
-       PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, mm##reg, value, mask)
-
-#define PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, value, mask)      \
-       PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
-
-#define PHM_WAIT_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, value, mask)      \
-       PHM_WAIT_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
-
-#define PHM_WAIT_VFPF_INDIRECT_REGISTER(hwmgr, port, reg, value, mask) \
-       PHM_WAIT_VFPF_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
-
-#define PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, value, mask) \
-       PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
-
 /* Operations on named fields. */
 
 #define PHM_READ_FIELD(device, reg, field)     \
@@ -762,60 +753,16 @@ extern void phm_apply_dal_min_voltage_request(struct pp_hwmgr *hwmgr);
                        PHM_SET_FIELD(cgs_read_ind_register(device, port, ix##reg),     \
                                reg, field, fieldval))
 
-#define PHM_WAIT_FIELD(hwmgr, reg, field, fieldval)    \
-       PHM_WAIT_REGISTER(hwmgr, reg, (fieldval)        \
-                       << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
-
-#define PHM_WAIT_INDIRECT_FIELD(hwmgr, port, reg, field, fieldval)     \
-       PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, (fieldval) \
-                       << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
-
-#define PHM_WAIT_VFPF_INDIRECT_FIELD(hwmgr, port, reg, field, fieldval)        \
-       PHM_WAIT_VFPF_INDIRECT_REGISTER(hwmgr, port, reg, (fieldval)    \
-                       << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
+#define PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, index, value, mask)        \
+       phm_wait_on_indirect_register(hwmgr, mm##port##_INDEX, index, value, mask)
 
-#define PHM_WAIT_FIELD_UNEQUAL(hwmgr, reg, field, fieldval)    \
-       PHM_WAIT_REGISTER_UNEQUAL(hwmgr, reg, (fieldval)        \
-                       << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
 
-#define PHM_WAIT_INDIRECT_FIELD_UNEQUAL(hwmgr, port, reg, field, fieldval)     \
-       PHM_WAIT_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, (fieldval) \
-                       << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
+#define PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, value, mask)      \
+       PHM_WAIT_INDIRECT_REGISTER_GIVEN_INDEX(hwmgr, port, ix##reg, value, mask)
 
-#define PHM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(hwmgr, port, reg, field, fieldval)        \
-       PHM_WAIT_VFPF_INDIRECT_REGISTER_UNEQUAL(hwmgr, port, reg, (fieldval)    \
+#define PHM_WAIT_INDIRECT_FIELD(hwmgr, port, reg, field, fieldval)     \
+       PHM_WAIT_INDIRECT_REGISTER(hwmgr, port, reg, (fieldval) \
                        << PHM_FIELD_SHIFT(reg, field), PHM_FIELD_MASK(reg, field))
 
-/* Operations on arrays of registers & fields. */
-
-#define PHM_READ_ARRAY_REGISTER(device, reg, offset)   \
-       cgs_read_register(device, mm##reg + (offset))
-
-#define PHM_WRITE_ARRAY_REGISTER(device, reg, offset, value)   \
-       cgs_write_register(device, mm##reg + (offset), value)
-
-#define PHM_WAIT_ARRAY_REGISTER(hwmgr, reg, offset, value, mask)       \
-       PHM_WAIT_REGISTER_GIVEN_INDEX(hwmgr, mm##reg + (offset), value, mask)
-
-#define PHM_WAIT_ARRAY_REGISTER_UNEQUAL(hwmgr, reg, offset, value, mask)       \
-       PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, mm##reg + (offset), value, mask)
-
-#define PHM_READ_ARRAY_FIELD(hwmgr, reg, offset, field) \
-       PHM_GET_FIELD(PHM_READ_ARRAY_REGISTER(hwmgr->device, reg, offset), reg, field)
-
-#define PHM_WRITE_ARRAY_FIELD(hwmgr, reg, offset, field, fieldvalue)   \
-       PHM_WRITE_ARRAY_REGISTER(hwmgr->device, reg, offset,    \
-                       PHM_SET_FIELD(PHM_READ_ARRAY_REGISTER(hwmgr->device, reg, offset),      \
-                               reg, field, fieldvalue))
-
-#define PHM_WAIT_ARRAY_FIELD(hwmgr, reg, offset, field, fieldvalue)    \
-       PHM_WAIT_REGISTER_GIVEN_INDEX(hwmgr, mm##reg + (offset),        \
-                       (fieldvalue) << PHM_FIELD_SHIFT(reg, field),    \
-                       PHM_FIELD_MASK(reg, field))
-
-#define PHM_WAIT_ARRAY_FIELD_UNEQUAL(hwmgr, reg, offset, field, fieldvalue)    \
-       PHM_WAIT_REGISTER_UNEQUAL_GIVEN_INDEX(hwmgr, mm##reg + (offset),        \
-                       (fieldvalue) << PHM_FIELD_SHIFT(reg, field),    \
-                       PHM_FIELD_MASK(reg, field))
 
 #endif /* _HWMGR_H_ */
index f497e7d..0de4436 100644 (file)
@@ -23,8 +23,7 @@
 #ifndef _POLARIS10_PWRVIRUS_H
 #define _POLARIS10_PWRVIRUS_H
 
-#define mmSMC_IND_INDEX_11                              0x01AC
-#define mmSMC_IND_DATA_11                               0x01AD
+
 #define mmCP_HYP_MEC1_UCODE_ADDR       0xf81a
 #define mmCP_HYP_MEC1_UCODE_DATA       0xf81b
 #define mmCP_HYP_MEC2_UCODE_ADDR       0xf81c
index a3f0ce4..9ceaed9 100644 (file)
@@ -158,7 +158,7 @@ struct pp_power_state {
 
 
 /*Structure to hold a VCE state entry*/
-struct PP_VCEState {
+struct pp_vce_state {
        uint32_t evclk;
        uint32_t ecclk;
        uint32_t sclk;
@@ -171,30 +171,28 @@ enum PP_MMProfilingState {
        PP_MMProfilingState_Stopped
 };
 
-struct PP_Clock_Engine_Request {
-       unsigned long clientType;
-       unsigned long ctxid;
+struct pp_clock_engine_request {
+       unsigned long client_type;
+       unsigned long ctx_id;
        uint64_t  context_handle;
        unsigned long sclk;
-       unsigned long sclkHardMin;
+       unsigned long sclk_hard_min;
        unsigned long mclk;
        unsigned long iclk;
        unsigned long evclk;
        unsigned long ecclk;
-       unsigned long ecclkHardMin;
+       unsigned long ecclk_hard_min;
        unsigned long vclk;
        unsigned long dclk;
-       unsigned long samclk;
-       unsigned long acpclk;
-       unsigned long sclkOverdrive;
-       unsigned long mclkOverdrive;
+       unsigned long sclk_over_drive;
+       unsigned long mclk_over_drive;
        unsigned long sclk_threshold;
        unsigned long flag;
        unsigned long vclk_ceiling;
        unsigned long dclk_ceiling;
        unsigned long num_cus;
-       unsigned long pmflag;
-       enum PP_MMProfilingState MMProfilingState;
+       unsigned long pm_flag;
+       enum PP_MMProfilingState mm_profiling_state;
 };
 
 #endif
index d7d83b7..bfdbec1 100644 (file)
@@ -43,5 +43,8 @@
        } while (0)
 
 
+#define GET_FLEXIBLE_ARRAY_MEMBER_ADDR(type, member, ptr, n)   \
+       (type *)((char *)&(ptr)->member + (sizeof(type) * (n)))
+
 #endif /* PP_DEBUG_H */
 
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu71.h b/drivers/gpu/drm/amd/powerplay/inc/smu71.h
new file mode 100644 (file)
index 0000000..71c9b2d
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2016 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 SMU71_H
+#define SMU71_H
+
+#if !defined(SMC_MICROCODE)
+#pragma pack(push, 1)
+#endif
+
+#define SMU__NUM_PCIE_DPM_LEVELS 8
+#define SMU__NUM_SCLK_DPM_STATE 8
+#define SMU__NUM_MCLK_DPM_LEVELS 4
+#define SMU__VARIANT__ICELAND 1
+#define SMU__DGPU_ONLY 1
+#define SMU__DYNAMIC_MCARB_SETTINGS 1
+
+enum SID_OPTION {
+  SID_OPTION_HI,
+  SID_OPTION_LO,
+  SID_OPTION_COUNT
+};
+
+typedef struct {
+  uint32_t high;
+  uint32_t low;
+} data_64_t;
+
+typedef struct {
+  data_64_t high;
+  data_64_t low;
+} data_128_t;
+
+#define SMU7_CONTEXT_ID_SMC        1
+#define SMU7_CONTEXT_ID_VBIOS      2
+
+#define SMU71_MAX_LEVELS_VDDC            8
+#define SMU71_MAX_LEVELS_VDDCI           4
+#define SMU71_MAX_LEVELS_MVDD            4
+#define SMU71_MAX_LEVELS_VDDNB           8
+
+#define SMU71_MAX_LEVELS_GRAPHICS        SMU__NUM_SCLK_DPM_STATE
+#define SMU71_MAX_LEVELS_MEMORY          SMU__NUM_MCLK_DPM_LEVELS
+#define SMU71_MAX_LEVELS_GIO             SMU__NUM_LCLK_DPM_LEVELS
+#define SMU71_MAX_LEVELS_LINK            SMU__NUM_PCIE_DPM_LEVELS
+#define SMU71_MAX_ENTRIES_SMIO           32
+
+#define DPM_NO_LIMIT 0
+#define DPM_NO_UP 1
+#define DPM_GO_DOWN 2
+#define DPM_GO_UP 3
+
+#define SMU7_FIRST_DPM_GRAPHICS_LEVEL    0
+#define SMU7_FIRST_DPM_MEMORY_LEVEL      0
+
+#define GPIO_CLAMP_MODE_VRHOT      1
+#define GPIO_CLAMP_MODE_THERM      2
+#define GPIO_CLAMP_MODE_DC         4
+
+#define SCRATCH_B_TARG_PCIE_INDEX_SHIFT 0
+#define SCRATCH_B_TARG_PCIE_INDEX_MASK  (0x7<<SCRATCH_B_TARG_PCIE_INDEX_SHIFT)
+#define SCRATCH_B_CURR_PCIE_INDEX_SHIFT 3
+#define SCRATCH_B_CURR_PCIE_INDEX_MASK  (0x7<<SCRATCH_B_CURR_PCIE_INDEX_SHIFT)
+#define SCRATCH_B_TARG_UVD_INDEX_SHIFT  6
+#define SCRATCH_B_TARG_UVD_INDEX_MASK   (0x7<<SCRATCH_B_TARG_UVD_INDEX_SHIFT)
+#define SCRATCH_B_CURR_UVD_INDEX_SHIFT  9
+#define SCRATCH_B_CURR_UVD_INDEX_MASK   (0x7<<SCRATCH_B_CURR_UVD_INDEX_SHIFT)
+#define SCRATCH_B_TARG_VCE_INDEX_SHIFT  12
+#define SCRATCH_B_TARG_VCE_INDEX_MASK   (0x7<<SCRATCH_B_TARG_VCE_INDEX_SHIFT)
+#define SCRATCH_B_CURR_VCE_INDEX_SHIFT  15
+#define SCRATCH_B_CURR_VCE_INDEX_MASK   (0x7<<SCRATCH_B_CURR_VCE_INDEX_SHIFT)
+#define SCRATCH_B_TARG_ACP_INDEX_SHIFT  18
+#define SCRATCH_B_TARG_ACP_INDEX_MASK   (0x7<<SCRATCH_B_TARG_ACP_INDEX_SHIFT)
+#define SCRATCH_B_CURR_ACP_INDEX_SHIFT  21
+#define SCRATCH_B_CURR_ACP_INDEX_MASK   (0x7<<SCRATCH_B_CURR_ACP_INDEX_SHIFT)
+#define SCRATCH_B_TARG_SAMU_INDEX_SHIFT 24
+#define SCRATCH_B_TARG_SAMU_INDEX_MASK  (0x7<<SCRATCH_B_TARG_SAMU_INDEX_SHIFT)
+#define SCRATCH_B_CURR_SAMU_INDEX_SHIFT 27
+#define SCRATCH_B_CURR_SAMU_INDEX_MASK  (0x7<<SCRATCH_B_CURR_SAMU_INDEX_SHIFT)
+
+
+#if defined SMU__DGPU_ONLY
+#define SMU71_DTE_ITERATIONS 5
+#define SMU71_DTE_SOURCES 3
+#define SMU71_DTE_SINKS 1
+#define SMU71_NUM_CPU_TES 0
+#define SMU71_NUM_GPU_TES 1
+#define SMU71_NUM_NON_TES 2
+
+#endif
+
+#if defined SMU__FUSION_ONLY
+#define SMU7_DTE_ITERATIONS 5
+#define SMU7_DTE_SOURCES 5
+#define SMU7_DTE_SINKS 3
+#define SMU7_NUM_CPU_TES 2
+#define SMU7_NUM_GPU_TES 1
+#define SMU7_NUM_NON_TES 2
+
+#endif
+
+struct SMU71_PIDController
+{
+    uint32_t Ki;
+    int32_t LFWindupUpperLim;
+    int32_t LFWindupLowerLim;
+    uint32_t StatePrecision;
+    uint32_t LfPrecision;
+    uint32_t LfOffset;
+    uint32_t MaxState;
+    uint32_t MaxLfFraction;
+    uint32_t StateShift;
+};
+
+typedef struct SMU71_PIDController SMU71_PIDController;
+
+struct SMU7_LocalDpmScoreboard
+{
+    uint32_t PercentageBusy;
+
+    int32_t  PIDError;
+    int32_t  PIDIntegral;
+    int32_t  PIDOutput;
+
+    uint32_t SigmaDeltaAccum;
+    uint32_t SigmaDeltaOutput;
+    uint32_t SigmaDeltaLevel;
+
+    uint32_t UtilizationSetpoint;
+
+    uint8_t  TdpClampMode;
+    uint8_t  TdcClampMode;
+    uint8_t  ThermClampMode;
+    uint8_t  VoltageBusy;
+
+    int8_t   CurrLevel;
+    int8_t   TargLevel;
+    uint8_t  LevelChangeInProgress;
+    uint8_t  UpHyst;
+
+    uint8_t  DownHyst;
+    uint8_t  VoltageDownHyst;
+    uint8_t  DpmEnable;
+    uint8_t  DpmRunning;
+
+    uint8_t  DpmForce;
+    uint8_t  DpmForceLevel;
+    uint8_t  DisplayWatermark;
+    uint8_t  McArbIndex;
+
+    uint32_t MinimumPerfSclk;
+
+    uint8_t  AcpiReq;
+    uint8_t  AcpiAck;
+    uint8_t  GfxClkSlow;
+    uint8_t  GpioClampMode;
+
+    uint8_t  FpsFilterWeight;
+    uint8_t  EnabledLevelsChange;
+    uint8_t  DteClampMode;
+    uint8_t  FpsClampMode;
+
+    uint16_t LevelResidencyCounters [SMU71_MAX_LEVELS_GRAPHICS];
+    uint16_t LevelSwitchCounters [SMU71_MAX_LEVELS_GRAPHICS];
+
+    void     (*TargetStateCalculator)(uint8_t);
+    void     (*SavedTargetStateCalculator)(uint8_t);
+
+    uint16_t AutoDpmInterval;
+    uint16_t AutoDpmRange;
+
+    uint8_t  FpsEnabled;
+    uint8_t  MaxPerfLevel;
+    uint8_t  AllowLowClkInterruptToHost;
+    uint8_t  FpsRunning;
+
+    uint32_t MaxAllowedFrequency;
+};
+
+typedef struct SMU7_LocalDpmScoreboard SMU7_LocalDpmScoreboard;
+
+#define SMU7_MAX_VOLTAGE_CLIENTS 12
+
+struct SMU7_VoltageScoreboard
+{
+    uint16_t CurrentVoltage;
+    uint16_t HighestVoltage;
+    uint16_t MaxVid;
+    uint8_t  HighestVidOffset;
+    uint8_t  CurrentVidOffset;
+#if defined (SMU__DGPU_ONLY)
+    uint8_t  CurrentPhases;
+    uint8_t  HighestPhases;
+#else
+    uint8_t  AvsOffset;
+    uint8_t  AvsOffsetApplied;
+#endif
+    uint8_t  ControllerBusy;
+    uint8_t  CurrentVid;
+    uint16_t RequestedVoltage[SMU7_MAX_VOLTAGE_CLIENTS];
+#if defined (SMU__DGPU_ONLY)
+    uint8_t  RequestedPhases[SMU7_MAX_VOLTAGE_CLIENTS];
+#endif
+    uint8_t  EnabledRequest[SMU7_MAX_VOLTAGE_CLIENTS];
+    uint8_t  TargetIndex;
+    uint8_t  Delay;
+    uint8_t  ControllerEnable;
+    uint8_t  ControllerRunning;
+    uint16_t CurrentStdVoltageHiSidd;
+    uint16_t CurrentStdVoltageLoSidd;
+#if defined (SMU__DGPU_ONLY)
+    uint16_t RequestedVddci;
+    uint16_t CurrentVddci;
+    uint16_t HighestVddci;
+    uint8_t  CurrentVddciVid;
+    uint8_t  TargetVddciIndex;
+#endif
+};
+
+typedef struct SMU7_VoltageScoreboard SMU7_VoltageScoreboard;
+
+// -------------------------------------------------------------------------------------------------------------------------
+#define SMU7_MAX_PCIE_LINK_SPEEDS 3 /* 0:Gen1 1:Gen2 2:Gen3 */
+
+struct SMU7_PCIeLinkSpeedScoreboard
+{
+    uint8_t     DpmEnable;
+    uint8_t     DpmRunning;
+    uint8_t     DpmForce;
+    uint8_t     DpmForceLevel;
+
+    uint8_t     CurrentLinkSpeed;
+    uint8_t     EnabledLevelsChange;
+    uint16_t    AutoDpmInterval;
+
+    uint16_t    AutoDpmRange;
+    uint16_t    AutoDpmCount;
+
+    uint8_t     DpmMode;
+    uint8_t     AcpiReq;
+    uint8_t     AcpiAck;
+    uint8_t     CurrentLinkLevel;
+
+};
+
+typedef struct SMU7_PCIeLinkSpeedScoreboard SMU7_PCIeLinkSpeedScoreboard;
+
+// -------------------------------------------------------- CAC table ------------------------------------------------------
+#define SMU7_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16
+#define SMU7_LKGE_LUT_NUM_OF_VOLT_ENTRIES 16
+
+#define SMU7_SCALE_I  7
+#define SMU7_SCALE_R 12
+
+struct SMU7_PowerScoreboard
+{
+    uint16_t   MinVoltage;
+    uint16_t   MaxVoltage;
+
+    uint32_t   AvgGpuPower;
+
+    uint16_t   VddcLeakagePower[SID_OPTION_COUNT];
+    uint16_t   VddcSclkConstantPower[SID_OPTION_COUNT];
+    uint16_t   VddcSclkDynamicPower[SID_OPTION_COUNT];
+    uint16_t   VddcNonSclkDynamicPower[SID_OPTION_COUNT];
+    uint16_t   VddcTotalPower[SID_OPTION_COUNT];
+    uint16_t   VddcTotalCurrent[SID_OPTION_COUNT];
+    uint16_t   VddcLoadVoltage[SID_OPTION_COUNT];
+    uint16_t   VddcNoLoadVoltage[SID_OPTION_COUNT];
+
+    uint16_t   DisplayPhyPower;
+    uint16_t   PciePhyPower;
+
+    uint16_t   VddciTotalPower;
+    uint16_t   Vddr1TotalPower;
+
+    uint32_t   RocPower;
+
+    uint32_t   last_power;
+    uint32_t   enableWinAvg;
+
+    uint32_t   lkg_acc;
+    uint16_t   VoltLkgeScaler;
+    uint16_t   TempLkgeScaler;
+
+    uint32_t   uvd_cac_dclk;
+    uint32_t   uvd_cac_vclk;
+    uint32_t   vce_cac_eclk;
+    uint32_t   samu_cac_samclk;
+    uint32_t   display_cac_dispclk;
+    uint32_t   acp_cac_aclk;
+    uint32_t   unb_cac;
+
+    uint32_t   WinTime;
+
+    uint16_t  GpuPwr_MAWt;
+    uint16_t  FilteredVddcTotalPower;
+
+    uint8_t   CalculationRepeats;
+    uint8_t   WaterfallUp;
+    uint8_t   WaterfallDown;
+    uint8_t   WaterfallLimit;
+};
+
+typedef struct SMU7_PowerScoreboard SMU7_PowerScoreboard;
+
+// --------------------------------------------------------------------------------------------------
+
+struct SMU7_ThermalScoreboard
+{
+   int16_t  GpuLimit;
+   int16_t  GpuHyst;
+   uint16_t CurrGnbTemp;
+   uint16_t FilteredGnbTemp;
+   uint8_t  ControllerEnable;
+   uint8_t  ControllerRunning;
+   uint8_t  WaterfallUp;
+   uint8_t  WaterfallDown;
+   uint8_t  WaterfallLimit;
+   uint8_t  padding[3];
+};
+
+typedef struct SMU7_ThermalScoreboard SMU7_ThermalScoreboard;
+
+// For FeatureEnables:
+#define SMU7_SCLK_DPM_CONFIG_MASK                        0x01
+#define SMU7_VOLTAGE_CONTROLLER_CONFIG_MASK              0x02
+#define SMU7_THERMAL_CONTROLLER_CONFIG_MASK              0x04
+#define SMU7_MCLK_DPM_CONFIG_MASK                        0x08
+#define SMU7_UVD_DPM_CONFIG_MASK                         0x10
+#define SMU7_VCE_DPM_CONFIG_MASK                         0x20
+#define SMU7_ACP_DPM_CONFIG_MASK                         0x40
+#define SMU7_SAMU_DPM_CONFIG_MASK                        0x80
+#define SMU7_PCIEGEN_DPM_CONFIG_MASK                    0x100
+
+#define SMU7_ACP_MCLK_HANDSHAKE_DISABLE                  0x00000001
+#define SMU7_ACP_SCLK_HANDSHAKE_DISABLE                  0x00000002
+#define SMU7_UVD_MCLK_HANDSHAKE_DISABLE                  0x00000100
+#define SMU7_UVD_SCLK_HANDSHAKE_DISABLE                  0x00000200
+#define SMU7_VCE_MCLK_HANDSHAKE_DISABLE                  0x00010000
+#define SMU7_VCE_SCLK_HANDSHAKE_DISABLE                  0x00020000
+
+// All 'soft registers' should be uint32_t.
+struct SMU71_SoftRegisters
+{
+    uint32_t        RefClockFrequency;
+    uint32_t        PmTimerPeriod;
+    uint32_t        FeatureEnables;
+#if defined (SMU__DGPU_ONLY)
+    uint32_t        PreVBlankGap;
+    uint32_t        VBlankTimeout;
+    uint32_t        TrainTimeGap;
+    uint32_t        MvddSwitchTime;
+    uint32_t        LongestAcpiTrainTime;
+    uint32_t        AcpiDelay;
+    uint32_t        G5TrainTime;
+    uint32_t        DelayMpllPwron;
+    uint32_t        VoltageChangeTimeout;
+#endif
+    uint32_t        HandshakeDisables;
+
+    uint8_t         DisplayPhy1Config;
+    uint8_t         DisplayPhy2Config;
+    uint8_t         DisplayPhy3Config;
+    uint8_t         DisplayPhy4Config;
+
+    uint8_t         DisplayPhy5Config;
+    uint8_t         DisplayPhy6Config;
+    uint8_t         DisplayPhy7Config;
+    uint8_t         DisplayPhy8Config;
+
+    uint32_t        AverageGraphicsActivity;
+    uint32_t        AverageMemoryActivity;
+    uint32_t        AverageGioActivity;
+
+    uint8_t         SClkDpmEnabledLevels;
+    uint8_t         MClkDpmEnabledLevels;
+    uint8_t         LClkDpmEnabledLevels;
+    uint8_t         PCIeDpmEnabledLevels;
+
+    uint32_t        DRAM_LOG_ADDR_H;
+    uint32_t        DRAM_LOG_ADDR_L;
+    uint32_t        DRAM_LOG_PHY_ADDR_H;
+    uint32_t        DRAM_LOG_PHY_ADDR_L;
+    uint32_t        DRAM_LOG_BUFF_SIZE;
+    uint32_t        UlvEnterCount;
+    uint32_t        UlvTime;
+    uint32_t        UcodeLoadStatus;
+    uint8_t         DPMFreezeAndForced;
+    uint8_t         Activity_Weight;
+    uint8_t         Reserved8[2];
+    uint32_t        Reserved;
+};
+
+typedef struct SMU71_SoftRegisters SMU71_SoftRegisters;
+
+struct SMU71_Firmware_Header
+{
+    uint32_t Digest[5];
+    uint32_t Version;
+    uint32_t HeaderSize;
+    uint32_t Flags;
+    uint32_t EntryPoint;
+    uint32_t CodeSize;
+    uint32_t ImageSize;
+
+    uint32_t Rtos;
+    uint32_t SoftRegisters;
+    uint32_t DpmTable;
+    uint32_t FanTable;
+    uint32_t CacConfigTable;
+    uint32_t CacStatusTable;
+
+    uint32_t mcRegisterTable;
+
+    uint32_t mcArbDramTimingTable;
+
+    uint32_t PmFuseTable;
+    uint32_t Globals;
+    uint32_t UvdDpmTable;
+    uint32_t AcpDpmTable;
+    uint32_t VceDpmTable;
+    uint32_t SamuDpmTable;
+    uint32_t UlvSettings;
+    uint32_t Reserved[37];
+    uint32_t Signature;
+};
+
+typedef struct SMU71_Firmware_Header SMU71_Firmware_Header;
+
+struct SMU7_HystController_Data
+{
+    uint8_t waterfall_up;
+    uint8_t waterfall_down;
+    uint8_t pstate;
+    uint8_t clamp_mode;
+};
+
+typedef struct SMU7_HystController_Data SMU7_HystController_Data;
+
+#define SMU71_FIRMWARE_HEADER_LOCATION 0x20000
+
+enum  DisplayConfig {
+    PowerDown = 1,
+    DP54x4,
+    DP54x2,
+    DP54x1,
+    DP27x4,
+    DP27x2,
+    DP27x1,
+    HDMI297,
+    HDMI162,
+    LVDS,
+    DP324x4,
+    DP324x2,
+    DP324x1
+};
+
+//#define SX_BLOCK_COUNT 8
+//#define MC_BLOCK_COUNT 1
+//#define CPL_BLOCK_COUNT 27
+
+#if defined SMU__VARIANT__ICELAND
+  #define SX_BLOCK_COUNT 8
+  #define MC_BLOCK_COUNT 1
+  #define CPL_BLOCK_COUNT 29
+#endif
+
+struct SMU7_Local_Cac {
+  uint8_t BlockId;
+  uint8_t SignalId;
+  uint8_t Threshold;
+  uint8_t Padding;
+};
+
+typedef struct SMU7_Local_Cac SMU7_Local_Cac;
+
+struct SMU7_Local_Cac_Table {
+  SMU7_Local_Cac SxLocalCac[SX_BLOCK_COUNT];
+  SMU7_Local_Cac CplLocalCac[CPL_BLOCK_COUNT];
+  SMU7_Local_Cac McLocalCac[MC_BLOCK_COUNT];
+};
+
+typedef struct SMU7_Local_Cac_Table SMU7_Local_Cac_Table;
+
+#if !defined(SMC_MICROCODE)
+#pragma pack(pop)
+#endif
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu71_discrete.h b/drivers/gpu/drm/amd/powerplay/inc/smu71_discrete.h
new file mode 100644 (file)
index 0000000..c0e3936
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright 2016 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 SMU71_DISCRETE_H
+#define SMU71_DISCRETE_H
+
+#include "smu71.h"
+
+#if !defined(SMC_MICROCODE)
+#pragma pack(push, 1)
+#endif
+
+#define VDDC_ON_SVI2  0x1
+#define VDDCI_ON_SVI2 0x2
+#define MVDD_ON_SVI2  0x4
+
+struct SMU71_Discrete_VoltageLevel
+{
+    uint16_t    Voltage;
+    uint16_t    StdVoltageHiSidd;
+    uint16_t    StdVoltageLoSidd;
+    uint8_t     Smio;
+    uint8_t     padding;
+};
+
+typedef struct SMU71_Discrete_VoltageLevel SMU71_Discrete_VoltageLevel;
+
+struct SMU71_Discrete_GraphicsLevel
+{
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+
+    uint32_t    SclkFrequency;
+
+    uint8_t     pcieDpmLevel;
+    uint8_t     DeepSleepDivId;
+    uint16_t    ActivityLevel;
+
+    uint32_t    CgSpllFuncCntl3;
+    uint32_t    CgSpllFuncCntl4;
+    uint32_t    SpllSpreadSpectrum;
+    uint32_t    SpllSpreadSpectrum2;
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+    uint8_t     SclkDid;
+    uint8_t     DisplayWatermark;
+    uint8_t     EnabledForActivity;
+    uint8_t     EnabledForThrottle;
+    uint8_t     UpHyst;
+    uint8_t     DownHyst;
+    uint8_t     VoltageDownHyst;
+    uint8_t     PowerThrottle;
+};
+
+typedef struct SMU71_Discrete_GraphicsLevel SMU71_Discrete_GraphicsLevel;
+
+struct SMU71_Discrete_ACPILevel
+{
+    uint32_t    Flags;
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+    uint32_t    SclkFrequency;
+    uint8_t     SclkDid;
+    uint8_t     DisplayWatermark;
+    uint8_t     DeepSleepDivId;
+    uint8_t     padding;
+    uint32_t    CgSpllFuncCntl;
+    uint32_t    CgSpllFuncCntl2;
+    uint32_t    CgSpllFuncCntl3;
+    uint32_t    CgSpllFuncCntl4;
+    uint32_t    SpllSpreadSpectrum;
+    uint32_t    SpllSpreadSpectrum2;
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+};
+
+typedef struct SMU71_Discrete_ACPILevel SMU71_Discrete_ACPILevel;
+
+struct SMU71_Discrete_Ulv
+{
+    uint32_t    CcPwrDynRm;
+    uint32_t    CcPwrDynRm1;
+    uint16_t    VddcOffset;
+    uint8_t     VddcOffsetVid;
+    uint8_t     VddcPhase;
+    uint32_t    Reserved;
+};
+
+typedef struct SMU71_Discrete_Ulv SMU71_Discrete_Ulv;
+
+struct SMU71_Discrete_MemoryLevel
+{
+    uint32_t    MinVddc;
+    uint32_t    MinVddcPhases;
+    uint32_t    MinVddci;
+    uint32_t    MinMvdd;
+
+    uint32_t    MclkFrequency;
+
+    uint8_t     EdcReadEnable;
+    uint8_t     EdcWriteEnable;
+    uint8_t     RttEnable;
+    uint8_t     StutterEnable;
+
+    uint8_t     StrobeEnable;
+    uint8_t     StrobeRatio;
+    uint8_t     EnabledForThrottle;
+    uint8_t     EnabledForActivity;
+
+    uint8_t     UpHyst;
+    uint8_t     DownHyst;
+    uint8_t     VoltageDownHyst;
+    uint8_t     padding;
+
+    uint16_t    ActivityLevel;
+    uint8_t     DisplayWatermark;
+    uint8_t     padding1;
+
+    uint32_t    MpllFuncCntl;
+    uint32_t    MpllFuncCntl_1;
+    uint32_t    MpllFuncCntl_2;
+    uint32_t    MpllAdFuncCntl;
+    uint32_t    MpllDqFuncCntl;
+    uint32_t    MclkPwrmgtCntl;
+    uint32_t    DllCntl;
+    uint32_t    MpllSs1;
+    uint32_t    MpllSs2;
+};
+
+typedef struct SMU71_Discrete_MemoryLevel SMU71_Discrete_MemoryLevel;
+
+struct SMU71_Discrete_LinkLevel
+{
+    uint8_t     PcieGenSpeed;           ///< 0:PciE-gen1 1:PciE-gen2 2:PciE-gen3
+    uint8_t     PcieLaneCount;          ///< 1=x1, 2=x2, 3=x4, 4=x8, 5=x12, 6=x16
+    uint8_t     EnabledForActivity;
+    uint8_t     SPC;
+    uint32_t    DownThreshold;
+    uint32_t    UpThreshold;
+    uint32_t    Reserved;
+};
+
+typedef struct SMU71_Discrete_LinkLevel SMU71_Discrete_LinkLevel;
+
+
+#ifdef SMU__DYNAMIC_MCARB_SETTINGS
+// MC ARB DRAM Timing registers.
+struct SMU71_Discrete_MCArbDramTimingTableEntry
+{
+    uint32_t McArbDramTiming;
+    uint32_t McArbDramTiming2;
+    uint8_t  McArbBurstTime;
+    uint8_t  padding[3];
+};
+
+typedef struct SMU71_Discrete_MCArbDramTimingTableEntry SMU71_Discrete_MCArbDramTimingTableEntry;
+
+struct SMU71_Discrete_MCArbDramTimingTable
+{
+    SMU71_Discrete_MCArbDramTimingTableEntry entries[SMU__NUM_SCLK_DPM_STATE][SMU__NUM_MCLK_DPM_LEVELS];
+};
+
+typedef struct SMU71_Discrete_MCArbDramTimingTable SMU71_Discrete_MCArbDramTimingTable;
+#endif
+
+// UVD VCLK/DCLK state (level) definition.
+struct SMU71_Discrete_UvdLevel
+{
+    uint32_t VclkFrequency;
+    uint32_t DclkFrequency;
+    uint16_t MinVddc;
+    uint8_t  MinVddcPhases;
+    uint8_t  VclkDivider;
+    uint8_t  DclkDivider;
+    uint8_t  padding[3];
+};
+
+typedef struct SMU71_Discrete_UvdLevel SMU71_Discrete_UvdLevel;
+
+// Clocks for other external blocks (VCE, ACP, SAMU).
+struct SMU71_Discrete_ExtClkLevel
+{
+    uint32_t Frequency;
+    uint16_t MinVoltage;
+    uint8_t  MinPhases;
+    uint8_t  Divider;
+};
+
+typedef struct SMU71_Discrete_ExtClkLevel SMU71_Discrete_ExtClkLevel;
+
+// Everything that we need to keep track of about the current state.
+// Use this instead of copies of the GraphicsLevel and MemoryLevel structures to keep track of state parameters
+// that need to be checked later.
+// We don't need to cache everything about a state, just a few parameters.
+struct SMU71_Discrete_StateInfo
+{
+    uint32_t SclkFrequency;
+    uint32_t MclkFrequency;
+    uint32_t VclkFrequency;
+    uint32_t DclkFrequency;
+    uint32_t SamclkFrequency;
+    uint32_t AclkFrequency;
+    uint32_t EclkFrequency;
+    uint16_t MvddVoltage;
+    uint16_t padding16;
+    uint8_t  DisplayWatermark;
+    uint8_t  McArbIndex;
+    uint8_t  McRegIndex;
+    uint8_t  SeqIndex;
+    uint8_t  SclkDid;
+    int8_t   SclkIndex;
+    int8_t   MclkIndex;
+    uint8_t  PCIeGen;
+
+};
+
+typedef struct SMU71_Discrete_StateInfo SMU71_Discrete_StateInfo;
+
+
+struct SMU71_Discrete_DpmTable
+{
+    // Multi-DPM controller settings
+    SMU71_PIDController                  GraphicsPIDController;
+    SMU71_PIDController                  MemoryPIDController;
+    SMU71_PIDController                  LinkPIDController;
+
+    uint32_t                            SystemFlags;
+
+    // SMIO masks for voltage and phase controls
+    uint32_t                            SmioMaskVddcVid;
+    uint32_t                            SmioMaskVddcPhase;
+    uint32_t                            SmioMaskVddciVid;
+    uint32_t                            SmioMaskMvddVid;
+
+    uint32_t                            VddcLevelCount;
+    uint32_t                            VddciLevelCount;
+    uint32_t                            MvddLevelCount;
+
+    SMU71_Discrete_VoltageLevel          VddcLevel               [SMU71_MAX_LEVELS_VDDC];
+    SMU71_Discrete_VoltageLevel          VddciLevel              [SMU71_MAX_LEVELS_VDDCI];
+    SMU71_Discrete_VoltageLevel          MvddLevel               [SMU71_MAX_LEVELS_MVDD];
+
+    uint8_t                             GraphicsDpmLevelCount;
+    uint8_t                             MemoryDpmLevelCount;
+    uint8_t                             LinkLevelCount;
+    uint8_t                             MasterDeepSleepControl;
+
+    uint32_t                            Reserved[5];
+
+    // State table entries for each DPM state
+    SMU71_Discrete_GraphicsLevel         GraphicsLevel           [SMU71_MAX_LEVELS_GRAPHICS];
+    SMU71_Discrete_MemoryLevel           MemoryACPILevel;
+    SMU71_Discrete_MemoryLevel           MemoryLevel             [SMU71_MAX_LEVELS_MEMORY];
+    SMU71_Discrete_LinkLevel             LinkLevel               [SMU71_MAX_LEVELS_LINK];
+    SMU71_Discrete_ACPILevel             ACPILevel;
+
+    uint32_t                            SclkStepSize;
+    uint32_t                            Smio                    [SMU71_MAX_ENTRIES_SMIO];
+
+    uint8_t                             GraphicsBootLevel;
+    uint8_t                             GraphicsVoltageChangeEnable;
+    uint8_t                             GraphicsThermThrottleEnable;
+    uint8_t                             GraphicsInterval;
+
+    uint8_t                             VoltageInterval;
+    uint8_t                             ThermalInterval;
+    uint16_t                            TemperatureLimitHigh;
+
+    uint16_t                            TemperatureLimitLow;
+    uint8_t                             MemoryBootLevel;
+    uint8_t                             MemoryVoltageChangeEnable;
+
+    uint8_t                             MemoryInterval;
+    uint8_t                             MemoryThermThrottleEnable;
+    uint8_t                             MergedVddci;
+    uint8_t                             padding2;
+
+    uint16_t                            VoltageResponseTime;
+    uint16_t                            PhaseResponseTime;
+
+    uint8_t                             PCIeBootLinkLevel;
+    uint8_t                             PCIeGenInterval;
+    uint8_t                             DTEInterval;
+    uint8_t                             DTEMode;
+
+    uint8_t                             SVI2Enable;
+    uint8_t                             VRHotGpio;
+    uint8_t                             AcDcGpio;
+    uint8_t                             ThermGpio;
+
+    uint32_t                            DisplayCac;
+
+    uint16_t                            MaxPwr;
+    uint16_t                            NomPwr;
+
+    uint16_t                            FpsHighThreshold;
+    uint16_t                            FpsLowThreshold;
+
+    uint16_t                            BAPMTI_R  [SMU71_DTE_ITERATIONS][SMU71_DTE_SOURCES][SMU71_DTE_SINKS];
+    uint16_t                            BAPMTI_RC [SMU71_DTE_ITERATIONS][SMU71_DTE_SOURCES][SMU71_DTE_SINKS];
+
+    uint8_t                             DTEAmbientTempBase;
+    uint8_t                             DTETjOffset;
+    uint8_t                             GpuTjMax;
+    uint8_t                             GpuTjHyst;
+
+    uint16_t                            BootVddc;
+    uint16_t                            BootVddci;
+
+    uint16_t                            BootMVdd;
+    uint16_t                            padding;
+
+    uint32_t                            BAPM_TEMP_GRADIENT;
+
+    uint32_t                            LowSclkInterruptThreshold;
+    uint32_t                            VddGfxReChkWait;
+
+    uint16_t                            PPM_PkgPwrLimit;
+    uint16_t                            PPM_TemperatureLimit;
+
+    uint16_t                            DefaultTdp;
+    uint16_t                            TargetTdp;
+};
+
+typedef struct SMU71_Discrete_DpmTable SMU71_Discrete_DpmTable;
+
+// --------------------------------------------------- AC Timing Parameters ------------------------------------------------
+#define SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE 16
+#define SMU71_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT SMU71_MAX_LEVELS_MEMORY
+
+struct SMU71_Discrete_MCRegisterAddress
+{
+    uint16_t s0;
+    uint16_t s1;
+};
+
+typedef struct SMU71_Discrete_MCRegisterAddress SMU71_Discrete_MCRegisterAddress;
+
+struct SMU71_Discrete_MCRegisterSet
+{
+    uint32_t value[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+typedef struct SMU71_Discrete_MCRegisterSet SMU71_Discrete_MCRegisterSet;
+
+struct SMU71_Discrete_MCRegisters
+{
+    uint8_t                             last;
+    uint8_t                             reserved[3];
+    SMU71_Discrete_MCRegisterAddress     address[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+    SMU71_Discrete_MCRegisterSet         data[SMU71_DISCRETE_MC_REGISTER_ARRAY_SET_COUNT];
+};
+
+typedef struct SMU71_Discrete_MCRegisters SMU71_Discrete_MCRegisters;
+
+
+// --------------------------------------------------- Fan Table -----------------------------------------------------------
+struct SMU71_Discrete_FanTable
+{
+    uint16_t FdoMode;
+    int16_t  TempMin;
+    int16_t  TempMed;
+    int16_t  TempMax;
+    int16_t  Slope1;
+    int16_t  Slope2;
+    int16_t  FdoMin;
+    int16_t  HystUp;
+    int16_t  HystDown;
+    int16_t  HystSlope;
+    int16_t  TempRespLim;
+    int16_t  TempCurr;
+    int16_t  SlopeCurr;
+    int16_t  PwmCurr;
+    uint32_t RefreshPeriod;
+    int16_t  FdoMax;
+    uint8_t  TempSrc;
+    int8_t   Padding;
+};
+
+typedef struct SMU71_Discrete_FanTable SMU71_Discrete_FanTable;
+
+#define SMU7_DISCRETE_GPIO_SCLK_DEBUG             4
+#define SMU7_DISCRETE_GPIO_SCLK_DEBUG_BIT         (0x1 << SMU7_DISCRETE_GPIO_SCLK_DEBUG)
+
+struct SMU71_MclkDpmScoreboard
+{
+
+    uint32_t PercentageBusy;
+
+    int32_t  PIDError;
+    int32_t  PIDIntegral;
+    int32_t  PIDOutput;
+
+    uint32_t SigmaDeltaAccum;
+    uint32_t SigmaDeltaOutput;
+    uint32_t SigmaDeltaLevel;
+
+    uint32_t UtilizationSetpoint;
+
+    uint8_t  TdpClampMode;
+    uint8_t  TdcClampMode;
+    uint8_t  ThermClampMode;
+    uint8_t  VoltageBusy;
+
+    int8_t   CurrLevel;
+    int8_t   TargLevel;
+    uint8_t  LevelChangeInProgress;
+    uint8_t  UpHyst;
+
+    uint8_t  DownHyst;
+    uint8_t  VoltageDownHyst;
+    uint8_t  DpmEnable;
+    uint8_t  DpmRunning;
+
+    uint8_t  DpmForce;
+    uint8_t  DpmForceLevel;
+    uint8_t  DisplayWatermark;
+    uint8_t  McArbIndex;
+
+    uint32_t MinimumPerfMclk;
+
+    uint8_t  AcpiReq;
+    uint8_t  AcpiAck;
+    uint8_t  MclkSwitchInProgress;
+    uint8_t  MclkSwitchCritical;
+
+    uint8_t  TargetMclkIndex;
+    uint8_t  TargetMvddIndex;
+    uint8_t  MclkSwitchResult;
+
+    uint8_t  EnabledLevelsChange;
+
+    uint16_t LevelResidencyCounters [SMU71_MAX_LEVELS_MEMORY];
+    uint16_t LevelSwitchCounters [SMU71_MAX_LEVELS_MEMORY];
+
+    void     (*TargetStateCalculator)(uint8_t);
+    void     (*SavedTargetStateCalculator)(uint8_t);
+
+    uint16_t AutoDpmInterval;
+    uint16_t AutoDpmRange;
+
+    uint16_t  MclkSwitchingTime;
+    uint8_t padding[2];
+};
+
+typedef struct SMU71_MclkDpmScoreboard SMU71_MclkDpmScoreboard;
+
+struct SMU71_UlvScoreboard
+{
+    uint8_t     EnterUlv;
+    uint8_t     ExitUlv;
+    uint8_t     UlvActive;
+    uint8_t     WaitingForUlv;
+    uint8_t     UlvEnable;
+    uint8_t     UlvRunning;
+    uint8_t     UlvMasterEnable;
+    uint8_t     padding;
+    uint32_t    UlvAbortedCount;
+    uint32_t    UlvTimeStamp;
+};
+
+typedef struct SMU71_UlvScoreboard SMU71_UlvScoreboard;
+
+struct SMU71_VddGfxScoreboard
+{
+    uint8_t     VddGfxEnable;
+    uint8_t     VddGfxActive;
+    uint8_t     padding[2];
+
+    uint32_t    VddGfxEnteredCount;
+    uint32_t    VddGfxAbortedCount;
+};
+
+typedef struct SMU71_VddGfxScoreboard SMU71_VddGfxScoreboard;
+
+struct SMU71_AcpiScoreboard {
+  uint32_t SavedInterruptMask[2];
+  uint8_t LastACPIRequest;
+  uint8_t CgBifResp;
+  uint8_t RequestType;
+  uint8_t Padding;
+  SMU71_Discrete_ACPILevel D0Level;
+};
+
+typedef struct SMU71_AcpiScoreboard SMU71_AcpiScoreboard;
+
+
+struct SMU71_Discrete_PmFuses {
+  // dw0-dw1
+  uint8_t BapmVddCVidHiSidd[8];
+
+  // dw2-dw3
+  uint8_t BapmVddCVidLoSidd[8];
+
+  // dw4-dw5
+  uint8_t VddCVid[8];
+
+  // dw6
+  uint8_t SviLoadLineEn;
+  uint8_t SviLoadLineVddC;
+  uint8_t SviLoadLineTrimVddC;
+  uint8_t SviLoadLineOffsetVddC;
+
+  // dw7
+  uint16_t TDC_VDDC_PkgLimit;
+  uint8_t TDC_VDDC_ThrottleReleaseLimitPerc;
+  uint8_t TDC_MAWt;
+
+  // dw8
+  uint8_t TdcWaterfallCtl;
+  uint8_t LPMLTemperatureMin;
+  uint8_t LPMLTemperatureMax;
+  uint8_t Reserved;
+
+  // dw9-dw12
+  uint8_t LPMLTemperatureScaler[16];
+
+  // dw13-dw14
+  int16_t FuzzyFan_ErrorSetDelta;
+  int16_t FuzzyFan_ErrorRateSetDelta;
+  int16_t FuzzyFan_PwmSetDelta;
+  uint16_t Reserved6;
+
+  // dw15
+  uint8_t GnbLPML[16];
+
+  // dw15
+  uint8_t GnbLPMLMaxVid;
+  uint8_t GnbLPMLMinVid;
+  uint8_t Reserved1[2];
+
+  // dw16
+  uint16_t BapmVddCBaseLeakageHiSidd;
+  uint16_t BapmVddCBaseLeakageLoSidd;
+};
+
+typedef struct SMU71_Discrete_PmFuses SMU71_Discrete_PmFuses;
+
+struct SMU71_Discrete_Log_Header_Table {
+  uint32_t    version;
+  uint32_t    asic_id;
+  uint16_t    flags;
+  uint16_t    entry_size;
+  uint32_t    total_size;
+  uint32_t    num_of_entries;
+  uint8_t     type;
+  uint8_t     mode;
+  uint8_t     filler_0[2];
+  uint32_t    filler_1[2];
+};
+
+typedef struct SMU71_Discrete_Log_Header_Table SMU71_Discrete_Log_Header_Table;
+
+struct SMU71_Discrete_Log_Cntl {
+    uint8_t             Enabled;
+    uint8_t             Type;
+    uint8_t             padding[2];
+    uint32_t            BufferSize;
+    uint32_t            SamplesLogged;
+    uint32_t            SampleSize;
+    uint32_t            AddrL;
+    uint32_t            AddrH;
+};
+
+typedef struct SMU71_Discrete_Log_Cntl SMU71_Discrete_Log_Cntl;
+
+#if defined SMU__DGPU_ONLY
+  #define CAC_ACC_NW_NUM_OF_SIGNALS 83
+#endif
+
+
+struct SMU71_Discrete_Cac_Collection_Table {
+  uint32_t temperature;
+  uint32_t cac_acc_nw[CAC_ACC_NW_NUM_OF_SIGNALS];
+  uint32_t filler[4];
+};
+
+typedef struct SMU71_Discrete_Cac_Collection_Table SMU71_Discrete_Cac_Collection_Table;
+
+struct SMU71_Discrete_Cac_Verification_Table {
+  uint32_t VddcTotalPower;
+  uint32_t VddcLeakagePower;
+  uint32_t VddcConstantPower;
+  uint32_t VddcGfxDynamicPower;
+  uint32_t VddcUvdDynamicPower;
+  uint32_t VddcVceDynamicPower;
+  uint32_t VddcAcpDynamicPower;
+  uint32_t VddcPcieDynamicPower;
+  uint32_t VddcDceDynamicPower;
+  uint32_t VddcCurrent;
+  uint32_t VddcVoltage;
+  uint32_t VddciTotalPower;
+  uint32_t VddciLeakagePower;
+  uint32_t VddciConstantPower;
+  uint32_t VddciDynamicPower;
+  uint32_t Vddr1TotalPower;
+  uint32_t Vddr1LeakagePower;
+  uint32_t Vddr1ConstantPower;
+  uint32_t Vddr1DynamicPower;
+  uint32_t spare[8];
+  uint32_t temperature;
+};
+
+typedef struct SMU71_Discrete_Cac_Verification_Table SMU71_Discrete_Cac_Verification_Table;
+
+#if !defined(SMC_MICROCODE)
+#pragma pack(pop)
+#endif
+
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu7_common.h b/drivers/gpu/drm/amd/powerplay/inc/smu7_common.h
new file mode 100644 (file)
index 0000000..65eb630
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 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_COMMON_H
+#define _PP_COMMON_H
+
+#include "smu7_ppsmc.h"
+#include "cgs_common.h"
+
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+
+
+#include "smu74.h"
+#include "smu74_discrete.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+#include "gca/gfx_8_0_d.h"
+#include "gca/gfx_8_0_sh_mask.h"
+
+#include "oss/oss_3_0_d.h"
+#include "oss/oss_3_0_sh_mask.h"
+
+
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h b/drivers/gpu/drm/amd/powerplay/inc/smu7_ppsmc.h
new file mode 100644 (file)
index 0000000..bce0009
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2015 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 DGPU_VI_PP_SMC_H
+#define DGPU_VI_PP_SMC_H
+
+
+#pragma pack(push, 1)
+
+#define PPSMC_MSG_SetGBDroopSettings          ((uint16_t) 0x305)
+
+#define PPSMC_SWSTATE_FLAG_DC                           0x01
+#define PPSMC_SWSTATE_FLAG_UVD                          0x02
+#define PPSMC_SWSTATE_FLAG_VCE                          0x04
+
+#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_SYSTEMFLAG_REGULATOR_HOT_ANALOG           0x20
+
+#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_DPM2FLAGS_TDPCLMP                         0x01
+#define PPSMC_DPM2FLAGS_PWRSHFT                         0x02
+#define PPSMC_DPM2FLAGS_OCP                             0x04
+
+
+#define PPSMC_DISPLAY_WATERMARK_LOW                     0
+#define PPSMC_DISPLAY_WATERMARK_HIGH                    1
+
+
+#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP    0x01
+#define PPSMC_STATEFLAG_POWERBOOST         0x02
+#define PPSMC_STATEFLAG_PSKIP_ON_TDP_FAULT 0x04
+#define PPSMC_STATEFLAG_POWERSHIFT         0x08
+#define PPSMC_STATEFLAG_SLOW_READ_MARGIN   0x10
+#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
+#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS   0x40
+
+
+#define FDO_MODE_HARDWARE 0
+#define FDO_MODE_PIECE_WISE_LINEAR 1
+
+enum FAN_CONTROL {
+       FAN_CONTROL_FUZZY,
+       FAN_CONTROL_TABLE
+};
+
+
+#define PPSMC_Result_OK             ((uint16_t)0x01)
+#define PPSMC_Result_NoMore         ((uint16_t)0x02)
+
+#define PPSMC_Result_NotNow         ((uint16_t)0x03)
+#define PPSMC_Result_Failed         ((uint16_t)0xFF)
+#define PPSMC_Result_UnknownCmd     ((uint16_t)0xFE)
+#define PPSMC_Result_UnknownVT      ((uint16_t)0xFD)
+
+typedef uint16_t PPSMC_Result;
+
+#define PPSMC_isERROR(x) ((uint16_t)0x80 & (x))
+
+
+#define PPSMC_MSG_Halt                      ((uint16_t)0x10)
+#define PPSMC_MSG_Resume                    ((uint16_t)0x11)
+#define PPSMC_MSG_EnableDPMLevel            ((uint16_t)0x12)
+#define PPSMC_MSG_ZeroLevelsDisabled        ((uint16_t)0x13)
+#define PPSMC_MSG_OneLevelsDisabled         ((uint16_t)0x14)
+#define PPSMC_MSG_TwoLevelsDisabled         ((uint16_t)0x15)
+#define PPSMC_MSG_EnableThermalInterrupt    ((uint16_t)0x16)
+#define PPSMC_MSG_RunningOnAC               ((uint16_t)0x17)
+#define PPSMC_MSG_LevelUp                   ((uint16_t)0x18)
+#define PPSMC_MSG_LevelDown                 ((uint16_t)0x19)
+#define PPSMC_MSG_ResetDPMCounters          ((uint16_t)0x1a)
+#define PPSMC_MSG_SwitchToSwState           ((uint16_t)0x20)
+#define PPSMC_MSG_SwitchToSwStateLast       ((uint16_t)0x3f)
+#define PPSMC_MSG_SwitchToInitialState      ((uint16_t)0x40)
+#define PPSMC_MSG_NoForcedLevel             ((uint16_t)0x41)
+#define PPSMC_MSG_ForceHigh                 ((uint16_t)0x42)
+#define PPSMC_MSG_ForceMediumOrHigh         ((uint16_t)0x43)
+#define PPSMC_MSG_SwitchToMinimumPower      ((uint16_t)0x51)
+#define PPSMC_MSG_ResumeFromMinimumPower    ((uint16_t)0x52)
+#define PPSMC_MSG_EnableCac                 ((uint16_t)0x53)
+#define PPSMC_MSG_DisableCac                ((uint16_t)0x54)
+#define PPSMC_DPMStateHistoryStart          ((uint16_t)0x55)
+#define PPSMC_DPMStateHistoryStop           ((uint16_t)0x56)
+#define PPSMC_CACHistoryStart               ((uint16_t)0x57)
+#define PPSMC_CACHistoryStop                ((uint16_t)0x58)
+#define PPSMC_TDPClampingActive             ((uint16_t)0x59)
+#define PPSMC_TDPClampingInactive           ((uint16_t)0x5A)
+#define PPSMC_StartFanControl               ((uint16_t)0x5B)
+#define PPSMC_StopFanControl                ((uint16_t)0x5C)
+#define PPSMC_NoDisplay                     ((uint16_t)0x5D)
+#define PPSMC_HasDisplay                    ((uint16_t)0x5E)
+#define PPSMC_MSG_UVDPowerOFF               ((uint16_t)0x60)
+#define PPSMC_MSG_UVDPowerON                ((uint16_t)0x61)
+#define PPSMC_MSG_EnableULV                 ((uint16_t)0x62)
+#define PPSMC_MSG_DisableULV                ((uint16_t)0x63)
+#define PPSMC_MSG_EnterULV                  ((uint16_t)0x64)
+#define PPSMC_MSG_ExitULV                   ((uint16_t)0x65)
+#define PPSMC_PowerShiftActive              ((uint16_t)0x6A)
+#define PPSMC_PowerShiftInactive            ((uint16_t)0x6B)
+#define PPSMC_OCPActive                     ((uint16_t)0x6C)
+#define PPSMC_OCPInactive                   ((uint16_t)0x6D)
+#define PPSMC_CACLongTermAvgEnable          ((uint16_t)0x6E)
+#define PPSMC_CACLongTermAvgDisable         ((uint16_t)0x6F)
+#define PPSMC_MSG_InferredStateSweep_Start  ((uint16_t)0x70)
+#define PPSMC_MSG_InferredStateSweep_Stop   ((uint16_t)0x71)
+#define PPSMC_MSG_SwitchToLowestInfState    ((uint16_t)0x72)
+#define PPSMC_MSG_SwitchToNonInfState       ((uint16_t)0x73)
+#define PPSMC_MSG_AllStateSweep_Start       ((uint16_t)0x74)
+#define PPSMC_MSG_AllStateSweep_Stop        ((uint16_t)0x75)
+#define PPSMC_MSG_SwitchNextLowerInfState   ((uint16_t)0x76)
+#define PPSMC_MSG_SwitchNextHigherInfState  ((uint16_t)0x77)
+#define PPSMC_MSG_MclkRetrainingTest        ((uint16_t)0x78)
+#define PPSMC_MSG_ForceTDPClamping          ((uint16_t)0x79)
+#define PPSMC_MSG_CollectCAC_PowerCorreln   ((uint16_t)0x7A)
+#define PPSMC_MSG_CollectCAC_WeightCalib    ((uint16_t)0x7B)
+#define PPSMC_MSG_CollectCAC_SQonly         ((uint16_t)0x7C)
+#define PPSMC_MSG_CollectCAC_TemperaturePwr ((uint16_t)0x7D)
+
+#define PPSMC_MSG_ExtremitiesTest_Start     ((uint16_t)0x7E)
+#define PPSMC_MSG_ExtremitiesTest_Stop      ((uint16_t)0x7F)
+#define PPSMC_FlushDataCache                ((uint16_t)0x80)
+#define PPSMC_FlushInstrCache               ((uint16_t)0x81)
+
+#define PPSMC_MSG_SetEnabledLevels          ((uint16_t)0x82)
+#define PPSMC_MSG_SetForcedLevels           ((uint16_t)0x83)
+
+#define PPSMC_MSG_ResetToDefaults           ((uint16_t)0x84)
+
+#define PPSMC_MSG_SetForcedLevelsAndJump      ((uint16_t)0x85)
+#define PPSMC_MSG_SetCACHistoryMode           ((uint16_t)0x86)
+#define PPSMC_MSG_EnableDTE                   ((uint16_t)0x87)
+#define PPSMC_MSG_DisableDTE                  ((uint16_t)0x88)
+
+#define PPSMC_MSG_SmcSpaceSetAddress          ((uint16_t)0x89)
+#define PPSM_MSG_SmcSpaceWriteDWordInc        ((uint16_t)0x8A)
+#define PPSM_MSG_SmcSpaceWriteWordInc         ((uint16_t)0x8B)
+#define PPSM_MSG_SmcSpaceWriteByteInc         ((uint16_t)0x8C)
+
+#define PPSMC_MSG_BREAK                       ((uint16_t)0xF8)
+
+#define PPSMC_MSG_Test                        ((uint16_t) 0x100)
+#define PPSMC_MSG_DPM_Voltage_Pwrmgt          ((uint16_t) 0x101)
+#define PPSMC_MSG_DPM_Config                  ((uint16_t) 0x102)
+#define PPSMC_MSG_PM_Controller_Start         ((uint16_t) 0x103)
+#define PPSMC_MSG_DPM_ForceState              ((uint16_t) 0x104)
+#define PPSMC_MSG_PG_PowerDownSIMD            ((uint16_t) 0x105)
+#define PPSMC_MSG_PG_PowerUpSIMD              ((uint16_t) 0x106)
+#define PPSMC_MSG_PM_Controller_Stop          ((uint16_t) 0x107)
+#define PPSMC_MSG_PG_SIMD_Config              ((uint16_t) 0x108)
+#define PPSMC_MSG_Voltage_Cntl_Enable         ((uint16_t) 0x109)
+#define PPSMC_MSG_Thermal_Cntl_Enable         ((uint16_t) 0x10a)
+#define PPSMC_MSG_Reset_Service               ((uint16_t) 0x10b)
+#define PPSMC_MSG_VCEPowerOFF                 ((uint16_t) 0x10e)
+#define PPSMC_MSG_VCEPowerON                  ((uint16_t) 0x10f)
+#define PPSMC_MSG_DPM_Disable_VCE_HS          ((uint16_t) 0x110)
+#define PPSMC_MSG_DPM_Enable_VCE_HS           ((uint16_t) 0x111)
+#define PPSMC_MSG_DPM_N_LevelsDisabled        ((uint16_t) 0x112)
+#define PPSMC_MSG_DCEPowerOFF                 ((uint16_t) 0x113)
+#define PPSMC_MSG_DCEPowerON                  ((uint16_t) 0x114)
+#define PPSMC_MSG_PCIE_DDIPowerDown           ((uint16_t) 0x117)
+#define PPSMC_MSG_PCIE_DDIPowerUp             ((uint16_t) 0x118)
+#define PPSMC_MSG_PCIE_CascadePLLPowerDown    ((uint16_t) 0x119)
+#define PPSMC_MSG_PCIE_CascadePLLPowerUp      ((uint16_t) 0x11a)
+#define PPSMC_MSG_SYSPLLPowerOff              ((uint16_t) 0x11b)
+#define PPSMC_MSG_SYSPLLPowerOn               ((uint16_t) 0x11c)
+#define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint16_t) 0x11d)
+#define PPSMC_MSG_DCE_AllowVoltageAdjustment  ((uint16_t) 0x11e)
+#define PPSMC_MSG_DISPLAYPHYStatusNotify      ((uint16_t) 0x11f)
+#define PPSMC_MSG_EnableBAPM                  ((uint16_t) 0x120)
+#define PPSMC_MSG_DisableBAPM                 ((uint16_t) 0x121)
+#define PPSMC_MSG_Spmi_Enable                 ((uint16_t) 0x122)
+#define PPSMC_MSG_Spmi_Timer                  ((uint16_t) 0x123)
+#define PPSMC_MSG_LCLK_DPM_Config             ((uint16_t) 0x124)
+#define PPSMC_MSG_VddNB_Request               ((uint16_t) 0x125)
+#define PPSMC_MSG_PCIE_DDIPhyPowerDown        ((uint32_t) 0x126)
+#define PPSMC_MSG_PCIE_DDIPhyPowerUp          ((uint32_t) 0x127)
+#define PPSMC_MSG_MCLKDPM_Config              ((uint16_t) 0x128)
+
+#define PPSMC_MSG_UVDDPM_Config               ((uint16_t) 0x129)
+#define PPSMC_MSG_VCEDPM_Config               ((uint16_t) 0x12A)
+#define PPSMC_MSG_ACPDPM_Config               ((uint16_t) 0x12B)
+#define PPSMC_MSG_SAMUDPM_Config              ((uint16_t) 0x12C)
+#define PPSMC_MSG_UVDDPM_SetEnabledMask       ((uint16_t) 0x12D)
+#define PPSMC_MSG_VCEDPM_SetEnabledMask       ((uint16_t) 0x12E)
+#define PPSMC_MSG_ACPDPM_SetEnabledMask       ((uint16_t) 0x12F)
+#define PPSMC_MSG_SAMUDPM_SetEnabledMask      ((uint16_t) 0x130)
+#define PPSMC_MSG_MCLKDPM_ForceState          ((uint16_t) 0x131)
+#define PPSMC_MSG_MCLKDPM_NoForcedLevel       ((uint16_t) 0x132)
+#define PPSMC_MSG_Thermal_Cntl_Disable        ((uint16_t) 0x133)
+#define PPSMC_MSG_SetTDPLimit                 ((uint16_t) 0x134)
+#define PPSMC_MSG_Voltage_Cntl_Disable        ((uint16_t) 0x135)
+#define PPSMC_MSG_PCIeDPM_Enable              ((uint16_t) 0x136)
+#define PPSMC_MSG_ACPPowerOFF                 ((uint16_t) 0x137)
+#define PPSMC_MSG_ACPPowerON                  ((uint16_t) 0x138)
+#define PPSMC_MSG_SAMPowerOFF                 ((uint16_t) 0x139)
+#define PPSMC_MSG_SAMPowerON                  ((uint16_t) 0x13a)
+#define PPSMC_MSG_SDMAPowerOFF                ((uint16_t) 0x13b)
+#define PPSMC_MSG_SDMAPowerON                 ((uint16_t) 0x13c)
+#define PPSMC_MSG_PCIeDPM_Disable             ((uint16_t) 0x13d)
+#define PPSMC_MSG_IOMMUPowerOFF               ((uint16_t) 0x13e)
+#define PPSMC_MSG_IOMMUPowerON                ((uint16_t) 0x13f)
+#define PPSMC_MSG_NBDPM_Enable                ((uint16_t) 0x140)
+#define PPSMC_MSG_NBDPM_Disable               ((uint16_t) 0x141)
+#define PPSMC_MSG_NBDPM_ForceNominal          ((uint16_t) 0x142)
+#define PPSMC_MSG_NBDPM_ForcePerformance      ((uint16_t) 0x143)
+#define PPSMC_MSG_NBDPM_UnForce               ((uint16_t) 0x144)
+#define PPSMC_MSG_SCLKDPM_SetEnabledMask      ((uint16_t) 0x145)
+#define PPSMC_MSG_MCLKDPM_SetEnabledMask      ((uint16_t) 0x146)
+#define PPSMC_MSG_PCIeDPM_ForceLevel          ((uint16_t) 0x147)
+#define PPSMC_MSG_PCIeDPM_UnForceLevel        ((uint16_t) 0x148)
+#define PPSMC_MSG_EnableACDCGPIOInterrupt     ((uint16_t) 0x149)
+#define PPSMC_MSG_EnableVRHotGPIOInterrupt    ((uint16_t) 0x14a)
+#define PPSMC_MSG_SwitchToAC                  ((uint16_t) 0x14b)
+#define PPSMC_MSG_XDMAPowerOFF                ((uint16_t) 0x14c)
+#define PPSMC_MSG_XDMAPowerON                 ((uint16_t) 0x14d)
+
+#define PPSMC_MSG_DPM_Enable                  ((uint16_t) 0x14e)
+#define PPSMC_MSG_DPM_Disable                 ((uint16_t) 0x14f)
+#define PPSMC_MSG_MCLKDPM_Enable              ((uint16_t) 0x150)
+#define PPSMC_MSG_MCLKDPM_Disable             ((uint16_t) 0x151)
+#define PPSMC_MSG_LCLKDPM_Enable              ((uint16_t) 0x152)
+#define PPSMC_MSG_LCLKDPM_Disable             ((uint16_t) 0x153)
+#define PPSMC_MSG_UVDDPM_Enable               ((uint16_t) 0x154)
+#define PPSMC_MSG_UVDDPM_Disable              ((uint16_t) 0x155)
+#define PPSMC_MSG_SAMUDPM_Enable              ((uint16_t) 0x156)
+#define PPSMC_MSG_SAMUDPM_Disable             ((uint16_t) 0x157)
+#define PPSMC_MSG_ACPDPM_Enable               ((uint16_t) 0x158)
+#define PPSMC_MSG_ACPDPM_Disable              ((uint16_t) 0x159)
+#define PPSMC_MSG_VCEDPM_Enable               ((uint16_t) 0x15a)
+#define PPSMC_MSG_VCEDPM_Disable              ((uint16_t) 0x15b)
+#define PPSMC_MSG_LCLKDPM_SetEnabledMask      ((uint16_t) 0x15c)
+#define PPSMC_MSG_DPM_FPS_Mode                ((uint16_t) 0x15d)
+#define PPSMC_MSG_DPM_Activity_Mode           ((uint16_t) 0x15e)
+#define PPSMC_MSG_VddC_Request                ((uint16_t) 0x15f)
+#define PPSMC_MSG_MCLKDPM_GetEnabledMask      ((uint16_t) 0x160)
+#define PPSMC_MSG_LCLKDPM_GetEnabledMask      ((uint16_t) 0x161)
+#define PPSMC_MSG_SCLKDPM_GetEnabledMask      ((uint16_t) 0x162)
+#define PPSMC_MSG_UVDDPM_GetEnabledMask       ((uint16_t) 0x163)
+#define PPSMC_MSG_SAMUDPM_GetEnabledMask      ((uint16_t) 0x164)
+#define PPSMC_MSG_ACPDPM_GetEnabledMask       ((uint16_t) 0x165)
+#define PPSMC_MSG_VCEDPM_GetEnabledMask       ((uint16_t) 0x166)
+#define PPSMC_MSG_PCIeDPM_SetEnabledMask      ((uint16_t) 0x167)
+#define PPSMC_MSG_PCIeDPM_GetEnabledMask      ((uint16_t) 0x168)
+#define PPSMC_MSG_TDCLimitEnable              ((uint16_t) 0x169)
+#define PPSMC_MSG_TDCLimitDisable             ((uint16_t) 0x16a)
+#define PPSMC_MSG_DPM_AutoRotate_Mode         ((uint16_t) 0x16b)
+#define PPSMC_MSG_DISPCLK_FROM_FCH            ((uint16_t) 0x16c)
+#define PPSMC_MSG_DISPCLK_FROM_DFS            ((uint16_t) 0x16d)
+#define PPSMC_MSG_DPREFCLK_FROM_FCH           ((uint16_t) 0x16e)
+#define PPSMC_MSG_DPREFCLK_FROM_DFS           ((uint16_t) 0x16f)
+#define PPSMC_MSG_PmStatusLogStart            ((uint16_t) 0x170)
+#define PPSMC_MSG_PmStatusLogSample           ((uint16_t) 0x171)
+#define PPSMC_MSG_SCLK_AutoDPM_ON             ((uint16_t) 0x172)
+#define PPSMC_MSG_MCLK_AutoDPM_ON             ((uint16_t) 0x173)
+#define PPSMC_MSG_LCLK_AutoDPM_ON             ((uint16_t) 0x174)
+#define PPSMC_MSG_UVD_AutoDPM_ON              ((uint16_t) 0x175)
+#define PPSMC_MSG_SAMU_AutoDPM_ON             ((uint16_t) 0x176)
+#define PPSMC_MSG_ACP_AutoDPM_ON              ((uint16_t) 0x177)
+#define PPSMC_MSG_VCE_AutoDPM_ON              ((uint16_t) 0x178)
+#define PPSMC_MSG_PCIe_AutoDPM_ON             ((uint16_t) 0x179)
+#define PPSMC_MSG_MASTER_AutoDPM_ON           ((uint16_t) 0x17a)
+#define PPSMC_MSG_MASTER_AutoDPM_OFF          ((uint16_t) 0x17b)
+#define PPSMC_MSG_DYNAMICDISPPHYPOWER         ((uint16_t) 0x17c)
+#define PPSMC_MSG_CAC_COLLECTION_ON           ((uint16_t) 0x17d)
+#define PPSMC_MSG_CAC_COLLECTION_OFF          ((uint16_t) 0x17e)
+#define PPSMC_MSG_CAC_CORRELATION_ON          ((uint16_t) 0x17f)
+#define PPSMC_MSG_CAC_CORRELATION_OFF         ((uint16_t) 0x180)
+#define PPSMC_MSG_PM_STATUS_TO_DRAM_ON        ((uint16_t) 0x181)
+#define PPSMC_MSG_PM_STATUS_TO_DRAM_OFF       ((uint16_t) 0x182)
+#define PPSMC_MSG_ALLOW_LOWSCLK_INTERRUPT     ((uint16_t) 0x184)
+#define PPSMC_MSG_PkgPwrLimitEnable           ((uint16_t) 0x185)
+#define PPSMC_MSG_PkgPwrLimitDisable          ((uint16_t) 0x186)
+#define PPSMC_MSG_PkgPwrSetLimit              ((uint16_t) 0x187)
+#define PPSMC_MSG_OverDriveSetTargetTdp       ((uint16_t) 0x188)
+#define PPSMC_MSG_SCLKDPM_FreezeLevel         ((uint16_t) 0x189)
+#define PPSMC_MSG_SCLKDPM_UnfreezeLevel       ((uint16_t) 0x18A)
+#define PPSMC_MSG_MCLKDPM_FreezeLevel         ((uint16_t) 0x18B)
+#define PPSMC_MSG_MCLKDPM_UnfreezeLevel       ((uint16_t) 0x18C)
+#define PPSMC_MSG_START_DRAM_LOGGING          ((uint16_t) 0x18D)
+#define PPSMC_MSG_STOP_DRAM_LOGGING           ((uint16_t) 0x18E)
+#define PPSMC_MSG_MASTER_DeepSleep_ON         ((uint16_t) 0x18F)
+#define PPSMC_MSG_MASTER_DeepSleep_OFF        ((uint16_t) 0x190)
+#define PPSMC_MSG_Remove_DC_Clamp             ((uint16_t) 0x191)
+#define PPSMC_MSG_DisableACDCGPIOInterrupt    ((uint16_t) 0x192)
+#define PPSMC_MSG_OverrideVoltageControl_SetVddc       ((uint16_t) 0x193)
+#define PPSMC_MSG_OverrideVoltageControl_SetVddci      ((uint16_t) 0x194)
+#define PPSMC_MSG_SetVidOffset_1              ((uint16_t) 0x195)
+#define PPSMC_MSG_SetVidOffset_2              ((uint16_t) 0x207)
+#define PPSMC_MSG_GetVidOffset_1              ((uint16_t) 0x196)
+#define PPSMC_MSG_GetVidOffset_2              ((uint16_t) 0x208)
+#define PPSMC_MSG_THERMAL_OVERDRIVE_Enable    ((uint16_t) 0x197)
+#define PPSMC_MSG_THERMAL_OVERDRIVE_Disable   ((uint16_t) 0x198)
+#define PPSMC_MSG_SetTjMax                    ((uint16_t) 0x199)
+#define PPSMC_MSG_SetFanPwmMax                ((uint16_t) 0x19A)
+#define PPSMC_MSG_WaitForMclkSwitchFinish     ((uint16_t) 0x19B)
+#define PPSMC_MSG_ENABLE_THERMAL_DPM          ((uint16_t) 0x19C)
+#define PPSMC_MSG_DISABLE_THERMAL_DPM         ((uint16_t) 0x19D)
+
+#define PPSMC_MSG_API_GetSclkFrequency        ((uint16_t) 0x200)
+#define PPSMC_MSG_API_GetMclkFrequency        ((uint16_t) 0x201)
+#define PPSMC_MSG_API_GetSclkBusy             ((uint16_t) 0x202)
+#define PPSMC_MSG_API_GetMclkBusy             ((uint16_t) 0x203)
+#define PPSMC_MSG_API_GetAsicPower            ((uint16_t) 0x204)
+#define PPSMC_MSG_SetFanRpmMax                ((uint16_t) 0x205)
+#define PPSMC_MSG_SetFanSclkTarget            ((uint16_t) 0x206)
+#define PPSMC_MSG_SetFanMinPwm                ((uint16_t) 0x209)
+#define PPSMC_MSG_SetFanTemperatureTarget     ((uint16_t) 0x20A)
+
+#define PPSMC_MSG_BACO_StartMonitor           ((uint16_t) 0x240)
+#define PPSMC_MSG_BACO_Cancel                 ((uint16_t) 0x241)
+#define PPSMC_MSG_EnableVddGfx                ((uint16_t) 0x242)
+#define PPSMC_MSG_DisableVddGfx               ((uint16_t) 0x243)
+#define PPSMC_MSG_UcodeAddressLow             ((uint16_t) 0x244)
+#define PPSMC_MSG_UcodeAddressHigh            ((uint16_t) 0x245)
+#define PPSMC_MSG_UcodeLoadStatus             ((uint16_t) 0x246)
+
+#define PPSMC_MSG_DRV_DRAM_ADDR_HI            ((uint16_t) 0x250)
+#define PPSMC_MSG_DRV_DRAM_ADDR_LO            ((uint16_t) 0x251)
+#define PPSMC_MSG_SMU_DRAM_ADDR_HI            ((uint16_t) 0x252)
+#define PPSMC_MSG_SMU_DRAM_ADDR_LO            ((uint16_t) 0x253)
+#define PPSMC_MSG_LoadUcodes                  ((uint16_t) 0x254)
+#define PPSMC_MSG_PowerStateNotify            ((uint16_t) 0x255)
+#define PPSMC_MSG_COND_EXEC_DRAM_ADDR_HI      ((uint16_t) 0x256)
+#define PPSMC_MSG_COND_EXEC_DRAM_ADDR_LO      ((uint16_t) 0x257)
+#define PPSMC_MSG_VBIOS_DRAM_ADDR_HI          ((uint16_t) 0x258)
+#define PPSMC_MSG_VBIOS_DRAM_ADDR_LO          ((uint16_t) 0x259)
+#define PPSMC_MSG_LoadVBios                   ((uint16_t) 0x25A)
+#define PPSMC_MSG_GetUcodeVersion             ((uint16_t) 0x25B)
+#define DMCUSMC_MSG_PSREntry                  ((uint16_t) 0x25C)
+#define DMCUSMC_MSG_PSRExit                   ((uint16_t) 0x25D)
+#define PPSMC_MSG_EnableClockGatingFeature    ((uint16_t) 0x260)
+#define PPSMC_MSG_DisableClockGatingFeature   ((uint16_t) 0x261)
+#define PPSMC_MSG_IsDeviceRunning             ((uint16_t) 0x262)
+#define PPSMC_MSG_LoadMetaData                ((uint16_t) 0x263)
+#define PPSMC_MSG_TMON_AutoCaliberate_Enable  ((uint16_t) 0x264)
+#define PPSMC_MSG_TMON_AutoCaliberate_Disable ((uint16_t) 0x265)
+#define PPSMC_MSG_GetTelemetry1Slope          ((uint16_t) 0x266)
+#define PPSMC_MSG_GetTelemetry1Offset         ((uint16_t) 0x267)
+#define PPSMC_MSG_GetTelemetry2Slope          ((uint16_t) 0x268)
+#define PPSMC_MSG_GetTelemetry2Offset         ((uint16_t) 0x269)
+#define PPSMC_MSG_EnableAvfs                  ((uint16_t) 0x26A)
+#define PPSMC_MSG_DisableAvfs                 ((uint16_t) 0x26B)
+
+#define PPSMC_MSG_PerformBtc                  ((uint16_t) 0x26C)
+#define PPSMC_MSG_VftTableIsValid             ((uint16_t) 0x275)
+#define PPSMC_MSG_UseNewGPIOScheme            ((uint16_t) 0x277)
+#define PPSMC_MSG_GetEnabledPsm               ((uint16_t) 0x400)
+#define PPSMC_MSG_AgmStartPsm                 ((uint16_t) 0x401)
+#define PPSMC_MSG_AgmReadPsm                  ((uint16_t) 0x402)
+#define PPSMC_MSG_AgmResetPsm                 ((uint16_t) 0x403)
+#define PPSMC_MSG_ReadVftCell                 ((uint16_t) 0x404)
+
+#define PPSMC_MSG_GFX_CU_PG_ENABLE            ((uint16_t) 0x280)
+#define PPSMC_MSG_GFX_CU_PG_DISABLE           ((uint16_t) 0x281)
+#define PPSMC_MSG_GetCurrPkgPwr               ((uint16_t) 0x282)
+
+#define PPSMC_MSG_SetGpuPllDfsForSclk         ((uint16_t) 0x300)
+#define PPSMC_MSG_Didt_Block_Function            ((uint16_t) 0x301)
+
+#define PPSMC_MSG_SetVBITimeout               ((uint16_t) 0x306)
+
+#define PPSMC_MSG_SecureSRBMWrite             ((uint16_t) 0x600)
+#define PPSMC_MSG_SecureSRBMRead              ((uint16_t) 0x601)
+#define PPSMC_MSG_SetAddress                  ((uint16_t) 0x800)
+#define PPSMC_MSG_GetData                     ((uint16_t) 0x801)
+#define PPSMC_MSG_SetData                     ((uint16_t) 0x802)
+
+typedef uint16_t PPSMC_Msg;
+
+#define PPSMC_EVENT_STATUS_THERMAL          0x00000001
+#define PPSMC_EVENT_STATUS_REGULATORHOT     0x00000002
+#define PPSMC_EVENT_STATUS_DC               0x00000004
+
+#pragma pack(pop)
+
+#endif
+
index 3c235f0..2139072 100644 (file)
@@ -28,6 +28,7 @@
 
 struct pp_smumgr;
 struct pp_instance;
+struct pp_hwmgr;
 
 #define smu_lower_32_bits(n) ((uint32_t)(n))
 #define smu_upper_32_bits(n) ((uint32_t)(((n)>>16)>>16))
@@ -53,6 +54,45 @@ enum AVFS_BTC_STATUS {
        AVFS_BTC_SMUMSG_ERROR
 };
 
+enum SMU_TABLE {
+       SMU_UVD_TABLE = 0,
+       SMU_VCE_TABLE,
+       SMU_SAMU_TABLE,
+       SMU_BIF_TABLE,
+};
+
+enum SMU_TYPE {
+       SMU_SoftRegisters = 0,
+       SMU_Discrete_DpmTable,
+};
+
+enum SMU_MEMBER {
+       HandshakeDisables = 0,
+       VoltageChangeTimeout,
+       AverageGraphicsActivity,
+       PreVBlankGap,
+       VBlankTimeout,
+       UcodeLoadStatus,
+       UvdBootLevel,
+       VceBootLevel,
+       SamuBootLevel,
+       LowSclkInterruptThreshold,
+};
+
+
+enum SMU_MAC_DEFINITION {
+       SMU_MAX_LEVELS_GRAPHICS = 0,
+       SMU_MAX_LEVELS_MEMORY,
+       SMU_MAX_LEVELS_LINK,
+       SMU_MAX_ENTRIES_SMIO,
+       SMU_MAX_LEVELS_VDDC,
+       SMU_MAX_LEVELS_VDDGFX,
+       SMU_MAX_LEVELS_VDDCI,
+       SMU_MAX_LEVELS_MVDD,
+       SMU_UVD_MCLK_HANDSHAKE_DISABLE,
+};
+
+
 struct pp_smumgr_func {
        int (*smu_init)(struct pp_smumgr *smumgr);
        int (*smu_fini)(struct pp_smumgr *smumgr);
@@ -69,12 +109,23 @@ struct pp_smumgr_func {
        int (*download_pptable_settings)(struct pp_smumgr *smumgr,
                                         void **table);
        int (*upload_pptable_settings)(struct pp_smumgr *smumgr);
+       int (*update_smc_table)(struct pp_hwmgr *hwmgr, uint32_t type);
+       int (*process_firmware_header)(struct pp_hwmgr *hwmgr);
+       int (*update_sclk_threshold)(struct pp_hwmgr *hwmgr);
+       int (*thermal_setup_fan_table)(struct pp_hwmgr *hwmgr);
+       int (*thermal_avfs_enable)(struct pp_hwmgr *hwmgr);
+       int (*init_smc_table)(struct pp_hwmgr *hwmgr);
+       int (*populate_all_graphic_levels)(struct pp_hwmgr *hwmgr);
+       int (*populate_all_memory_levels)(struct pp_hwmgr *hwmgr);
+       int (*initialize_mc_reg_table)(struct pp_hwmgr *hwmgr);
+       uint32_t (*get_offsetof)(uint32_t type, uint32_t member);
+       uint32_t (*get_mac_definition)(uint32_t value);
+       bool (*is_dpm_running)(struct pp_hwmgr *hwmgr);
 };
 
 struct pp_smumgr {
        uint32_t chip_family;
        uint32_t chip_id;
-       uint32_t hw_revision;
        void *device;
        void *backend;
        uint32_t usec_timeout;
@@ -122,6 +173,30 @@ extern int smu_allocate_memory(void *device, uint32_t size,
 
 extern int smu_free_memory(void *device, void *handle);
 
+extern int cz_smum_init(struct pp_smumgr *smumgr);
+extern int iceland_smum_init(struct pp_smumgr *smumgr);
+extern int tonga_smum_init(struct pp_smumgr *smumgr);
+extern int fiji_smum_init(struct pp_smumgr *smumgr);
+extern int polaris10_smum_init(struct pp_smumgr *smumgr);
+
+extern int smum_update_sclk_threshold(struct pp_hwmgr *hwmgr);
+
+extern int smum_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type);
+extern int smum_process_firmware_header(struct pp_hwmgr *hwmgr);
+extern int smum_thermal_avfs_enable(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result);
+extern int smum_thermal_setup_fan_table(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result);
+extern int smum_init_smc_table(struct pp_hwmgr *hwmgr);
+extern int smum_populate_all_graphic_levels(struct pp_hwmgr *hwmgr);
+extern int smum_populate_all_memory_levels(struct pp_hwmgr *hwmgr);
+extern int smum_initialize_mc_reg_table(struct pp_hwmgr *hwmgr);
+extern uint32_t smum_get_offsetof(struct pp_smumgr *smumgr,
+                               uint32_t type, uint32_t member);
+extern uint32_t smum_get_mac_definition(struct pp_smumgr *smumgr, uint32_t value);
+
+extern bool smum_is_dpm_running(struct pp_hwmgr *hwmgr);
+
 #define SMUM_FIELD_SHIFT(reg, field) reg##__##field##__SHIFT
 
 #define SMUM_FIELD_MASK(reg, field) reg##__##field##_MASK
index f10fb64..51ff083 100644 (file)
@@ -2,7 +2,9 @@
 # Makefile for the 'smu manager' sub-component of powerplay.
 # It provides the smu management services for the driver.
 
-SMU_MGR = smumgr.o cz_smumgr.o tonga_smumgr.o fiji_smumgr.o polaris10_smumgr.o
+SMU_MGR = smumgr.o cz_smumgr.o tonga_smumgr.o fiji_smumgr.o fiji_smc.o \
+         polaris10_smumgr.o iceland_smumgr.o polaris10_smc.o tonga_smc.o \
+         smu7_smumgr.o iceland_smc.o
 
 AMD_PP_SMUMGR = $(addprefix $(AMD_PP_PATH)/smumgr/,$(SMU_MGR))
 
index 87c023e..5a44485 100644 (file)
@@ -89,13 +89,8 @@ static int cz_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
        if (result != 0)
                return result;
 
-       result = SMUM_WAIT_FIELD_UNEQUAL(smumgr,
+       return SMUM_WAIT_FIELD_UNEQUAL(smumgr,
                                        SMU_MP1_SRBM2P_RESP_0, CONTENT, 0);
-
-       if (result != 0)
-               return result;
-
-       return 0;
 }
 
 static int cz_set_smc_sram_address(struct pp_smumgr *smumgr,
@@ -106,12 +101,12 @@ static int cz_set_smc_sram_address(struct pp_smumgr *smumgr,
 
        if (0 != (3 & smc_address)) {
                printk(KERN_ERR "[ powerplay ] SMC address must be 4 byte aligned\n");
-               return -1;
+               return -EINVAL;
        }
 
        if (limit <= (smc_address + 3)) {
                printk(KERN_ERR "[ powerplay ] SMC address beyond the SMC RAM area\n");
-               return -1;
+               return -EINVAL;
        }
 
        cgs_write_register(smumgr->device, mmMP0PUB_IND_INDEX_0,
@@ -129,9 +124,10 @@ static int cz_write_smc_sram_dword(struct pp_smumgr *smumgr,
                return -EINVAL;
 
        result = cz_set_smc_sram_address(smumgr, smc_address, limit);
-       cgs_write_register(smumgr->device, mmMP0PUB_IND_DATA_0, value);
+       if (!result)
+               cgs_write_register(smumgr->device, mmMP0PUB_IND_DATA_0, value);
 
-       return 0;
+       return result;
 }
 
 static int cz_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr,
@@ -148,7 +144,6 @@ static int cz_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr,
 static int cz_request_smu_load_fw(struct pp_smumgr *smumgr)
 {
        struct cz_smumgr *cz_smu = (struct cz_smumgr *)(smumgr->backend);
-       int result = 0;
        uint32_t smc_address;
 
        if (!smumgr->reload_fw) {
@@ -177,11 +172,9 @@ static int cz_request_smu_load_fw(struct pp_smumgr *smumgr)
        cz_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_ExecuteJob,
                                cz_smu->toc_entry_power_profiling_index);
 
-       result = cz_send_msg_to_smc_with_parameter(smumgr,
+       return cz_send_msg_to_smc_with_parameter(smumgr,
                                        PPSMC_MSG_ExecuteJob,
                                        cz_smu->toc_entry_initialize_index);
-
-       return result;
 }
 
 static int cz_check_fw_load_finish(struct pp_smumgr *smumgr,
@@ -195,9 +188,6 @@ static int cz_check_fw_load_finish(struct pp_smumgr *smumgr,
        if (smumgr == NULL || smumgr->device == NULL)
                return -EINVAL;
 
-       return cgs_read_register(smumgr->device,
-                                       mmSMU_MP1_SRBM2P_ARG_0);
-
        cgs_write_register(smumgr->device, mmMP0PUB_IND_INDEX, index);
 
        for (i = 0; i < smumgr->usec_timeout; i++) {
@@ -275,7 +265,10 @@ static int cz_start_smu(struct pp_smumgr *smumgr)
        if (smumgr->chip_id == CHIP_STONEY)
                fw_to_check &= ~(UCODE_ID_SDMA1_MASK | UCODE_ID_CP_MEC_JT2_MASK);
 
-       cz_request_smu_load_fw(smumgr);
+       ret = cz_request_smu_load_fw(smumgr);
+       if (ret)
+               printk(KERN_ERR "[ powerplay] SMU firmware load failed\n");
+
        cz_check_fw_load_finish(smumgr, fw_to_check);
 
        ret = cz_load_mec_firmware(smumgr);
@@ -566,10 +559,7 @@ static int cz_smu_construct_toc_for_bootup(struct pp_smumgr *smumgr)
 
        cz_smu_populate_single_ucode_load_task(smumgr,
                                CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false);
-       if (smumgr->chip_id == CHIP_STONEY)
-               cz_smu_populate_single_ucode_load_task(smumgr,
-                               CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false);
-       else
+       if (smumgr->chip_id != CHIP_STONEY)
                cz_smu_populate_single_ucode_load_task(smumgr,
                                CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1, false);
        cz_smu_populate_single_ucode_load_task(smumgr,
@@ -580,10 +570,7 @@ static int cz_smu_construct_toc_for_bootup(struct pp_smumgr *smumgr)
                                CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, false);
        cz_smu_populate_single_ucode_load_task(smumgr,
                                CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
-       if (smumgr->chip_id == CHIP_STONEY)
-               cz_smu_populate_single_ucode_load_task(smumgr,
-                               CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
-       else
+       if (smumgr->chip_id != CHIP_STONEY)
                cz_smu_populate_single_ucode_load_task(smumgr,
                                CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false);
        cz_smu_populate_single_ucode_load_task(smumgr,
@@ -610,19 +597,12 @@ static int cz_smu_construct_toc(struct pp_smumgr *smumgr)
        struct cz_smumgr *cz_smu = (struct cz_smumgr *)smumgr->backend;
 
        cz_smu->toc_entry_used_count = 0;
-
        cz_smu_initialize_toc_empty_job_list(smumgr);
-
        cz_smu_construct_toc_for_rlc_aram_save(smumgr);
-
        cz_smu_construct_toc_for_vddgfx_enter(smumgr);
-
        cz_smu_construct_toc_for_vddgfx_exit(smumgr);
-
        cz_smu_construct_toc_for_power_profiling(smumgr);
-
        cz_smu_construct_toc_for_bootup(smumgr);
-
        cz_smu_construct_toc_for_clock_table(smumgr);
 
        return 0;
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c
new file mode 100644 (file)
index 0000000..76310ac
--- /dev/null
@@ -0,0 +1,2374 @@
+/*
+ * Copyright 2015 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 "fiji_smc.h"
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "fiji_smumgr.h"
+#include "pppcielanes.h"
+#include "smu7_ppsmc.h"
+#include "smu73.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+#include "smu7_smumgr.h"
+
+#define VOLTAGE_SCALE 4
+#define POWERTUNE_DEFAULT_SET_MAX    1
+#define VOLTAGE_VID_OFFSET_SCALE1   625
+#define VOLTAGE_VID_OFFSET_SCALE2   100
+#define VDDC_VDDCI_DELTA            300
+#define MC_CG_ARB_FREQ_F1           0x0b
+
+/* [2.5%,~2.5%] Clock stretched is multiple of 2.5% vs
+ * not and [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ]
+ */
+static const uint16_t fiji_clock_stretcher_lookup_table[2][4] = {
+                               {600, 1050, 3, 0}, {600, 1050, 6, 1} };
+
+/* [FF, SS] type, [] 4 voltage ranges, and
+ * [Floor Freq, Boundary Freq, VID min , VID max]
+ */
+static const uint32_t fiji_clock_stretcher_ddt_table[2][4][4] = {
+       { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
+       { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} } };
+
+/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%]
+ * (coming from PWR_CKS_CNTL.stretch_amount reg spec)
+ */
+static const uint8_t fiji_clock_stretch_amount_conversion[2][6] = {
+                               {0, 1, 3, 2, 4, 5}, {0, 2, 4, 5, 6, 5} };
+
+static const struct fiji_pt_defaults fiji_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+               /*sviLoadLIneEn,  SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc */
+               {1,               0xF,             0xFD,
+               /* TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase */
+               0x19,        5,               45}
+};
+
+/* PPGen has the gain setting generated in x * 100 unit
+ * This function is to convert the unit to x * 4096(0x1000) unit.
+ *  This is the unit expected by SMC firmware
+ */
+static int fiji_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+               struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+               uint32_t clock, uint32_t *voltage, uint32_t *mvdd)
+{
+       uint32_t i;
+       uint16_t vddci;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       *voltage = *mvdd = 0;
+
+
+       /* clock - voltage dependency table is empty table */
+       if (dep_table->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < dep_table->count; i++) {
+               /* find first sclk bigger than request */
+               if (dep_table->entries[i].clk >= clock) {
+                       *voltage |= (dep_table->entries[i].vddc *
+                                       VOLTAGE_SCALE) << VDDC_SHIFT;
+                       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+                               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       else if (dep_table->entries[i].vddci)
+                               *voltage |= (dep_table->entries[i].vddci *
+                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       else {
+                               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               (dep_table->entries[i].vddc -
+                                                               VDDC_VDDCI_DELTA));
+                               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       }
+
+                       if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+                               *mvdd = data->vbios_boot_state.mvdd_bootup_value *
+                                       VOLTAGE_SCALE;
+                       else if (dep_table->entries[i].mvdd)
+                               *mvdd = (uint32_t) dep_table->entries[i].mvdd *
+                                       VOLTAGE_SCALE;
+
+                       *voltage |= 1 << PHASES_SHIFT;
+                       return 0;
+               }
+       }
+
+       /* sclk is bigger than max sclk in the dependence table */
+       *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+       else if (dep_table->entries[i-1].vddci) {
+               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+                               (dep_table->entries[i].vddc -
+                                               VDDC_VDDCI_DELTA));
+               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+       }
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+               *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
+       else if (dep_table->entries[i].mvdd)
+               *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
+
+       return 0;
+}
+
+
+static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
+{
+       uint32_t tmp;
+       tmp = raw_setting * 4096 / 100;
+       return (uint16_t)tmp;
+}
+
+static void get_scl_sda_value(uint8_t line, uint8_t *scl, uint8_t *sda)
+{
+       switch (line) {
+       case SMU7_I2CLineID_DDC1:
+               *scl = SMU7_I2C_DDC1CLK;
+               *sda = SMU7_I2C_DDC1DATA;
+               break;
+       case SMU7_I2CLineID_DDC2:
+               *scl = SMU7_I2C_DDC2CLK;
+               *sda = SMU7_I2C_DDC2DATA;
+               break;
+       case SMU7_I2CLineID_DDC3:
+               *scl = SMU7_I2C_DDC3CLK;
+               *sda = SMU7_I2C_DDC3DATA;
+               break;
+       case SMU7_I2CLineID_DDC4:
+               *scl = SMU7_I2C_DDC4CLK;
+               *sda = SMU7_I2C_DDC4DATA;
+               break;
+       case SMU7_I2CLineID_DDC5:
+               *scl = SMU7_I2C_DDC5CLK;
+               *sda = SMU7_I2C_DDC5DATA;
+               break;
+       case SMU7_I2CLineID_DDC6:
+               *scl = SMU7_I2C_DDC6CLK;
+               *sda = SMU7_I2C_DDC6DATA;
+               break;
+       case SMU7_I2CLineID_SCLSDA:
+               *scl = SMU7_I2C_SCL;
+               *sda = SMU7_I2C_SDA;
+               break;
+       case SMU7_I2CLineID_DDCVGA:
+               *scl = SMU7_I2C_DDCVGACLK;
+               *sda = SMU7_I2C_DDCVGADATA;
+               break;
+       default:
+               *scl = 0;
+               *sda = 0;
+               break;
+       }
+}
+
+static void fiji_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct  phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (table_info &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID)
+               smu_data->power_tune_defaults =
+                               &fiji_power_tune_data_set_array
+                               [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+       else
+               smu_data->power_tune_defaults = &fiji_power_tune_data_set_array[0];
+
+}
+
+static int fiji_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       SMU73_Discrete_DpmTable  *dpm_table = &(smu_data->smc_state_table);
+
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+       struct pp_advance_fan_control_parameters *fan_table =
+                       &hwmgr->thermal_controller.advanceFanControlParameters;
+       uint8_t uc_scl, uc_sda;
+
+       /* TDP number of fraction bits are changed from 8 to 7 for Fiji
+        * as requested by SMC team
+        */
+       dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
+                       (uint16_t)(cac_dtp_table->usTDP * 128));
+       dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
+                       (uint16_t)(cac_dtp_table->usTDP * 128));
+
+       PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+                       "Target Operating Temp is out of Range!",
+                       );
+
+       dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
+       dpm_table->GpuTjHyst = 8;
+
+       dpm_table->DTEAmbientTempBase = defaults->DTEAmbientTempBase;
+
+       /* The following are for new Fiji Multi-input fan/thermal control */
+       dpm_table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTargetOperatingTemp * 256);
+       dpm_table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitHotspot * 256);
+       dpm_table->TemperatureLimitLiquid1 = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitLiquid1 * 256);
+       dpm_table->TemperatureLimitLiquid2 = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitLiquid2 * 256);
+       dpm_table->TemperatureLimitVrVddc = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitVrVddc * 256);
+       dpm_table->TemperatureLimitVrMvdd = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitVrMvdd * 256);
+       dpm_table->TemperatureLimitPlx = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitPlx * 256);
+
+       dpm_table->FanGainEdge = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainEdge));
+       dpm_table->FanGainHotspot = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainHotspot));
+       dpm_table->FanGainLiquid = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainLiquid));
+       dpm_table->FanGainVrVddc = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainVrVddc));
+       dpm_table->FanGainVrMvdd = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainVrMvdd));
+       dpm_table->FanGainPlx = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainPlx));
+       dpm_table->FanGainHbm = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainHbm));
+
+       dpm_table->Liquid1_I2C_address = cac_dtp_table->ucLiquid1_I2C_address;
+       dpm_table->Liquid2_I2C_address = cac_dtp_table->ucLiquid2_I2C_address;
+       dpm_table->Vr_I2C_address = cac_dtp_table->ucVr_I2C_address;
+       dpm_table->Plx_I2C_address = cac_dtp_table->ucPlx_I2C_address;
+
+       get_scl_sda_value(cac_dtp_table->ucLiquid_I2C_Line, &uc_scl, &uc_sda);
+       dpm_table->Liquid_I2C_LineSCL = uc_scl;
+       dpm_table->Liquid_I2C_LineSDA = uc_sda;
+
+       get_scl_sda_value(cac_dtp_table->ucVr_I2C_Line, &uc_scl, &uc_sda);
+       dpm_table->Vr_I2C_LineSCL = uc_scl;
+       dpm_table->Vr_I2C_LineSDA = uc_sda;
+
+       get_scl_sda_value(cac_dtp_table->ucPlx_I2C_Line, &uc_scl, &uc_sda);
+       dpm_table->Plx_I2C_LineSCL = uc_scl;
+       dpm_table->Plx_I2C_LineSDA = uc_sda;
+
+       return 0;
+}
+
+
+static int fiji_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       smu_data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
+       smu_data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
+       smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+       smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+       return 0;
+}
+
+
+static int fiji_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+       uint16_t tdc_limit;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       /* TDC number of fraction bits are changed from 8 to 7
+        * for Fiji as requested by SMC team
+        */
+       tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
+       smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+       smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+                       defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
+       smu_data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
+
+       return 0;
+}
+
+static int fiji_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       const struct fiji_pt_defaults *defaults = smu_data->power_tune_defaults;
+       uint32_t temp;
+
+       if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       fuse_table_offset +
+                       offsetof(SMU73_Discrete_PmFuses, TdcWaterfallCtl),
+                       (uint32_t *)&temp, SMC_RAM_END))
+               PP_ASSERT_WITH_CODE(false,
+                               "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+                               return -EINVAL);
+       else {
+               smu_data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
+               smu_data->power_tune_table.LPMLTemperatureMin =
+                               (uint8_t)((temp >> 16) & 0xff);
+               smu_data->power_tune_table.LPMLTemperatureMax =
+                               (uint8_t)((temp >> 8) & 0xff);
+               smu_data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
+       }
+       return 0;
+}
+
+static int fiji_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+       return 0;
+}
+
+static int fiji_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       if ((hwmgr->thermal_controller.advanceFanControlParameters.
+                       usFanOutputSensitivity & (1 << 15)) ||
+                       0 == hwmgr->thermal_controller.advanceFanControlParameters.
+                       usFanOutputSensitivity)
+               hwmgr->thermal_controller.advanceFanControlParameters.
+               usFanOutputSensitivity = hwmgr->thermal_controller.
+                       advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+       smu_data->power_tune_table.FuzzyFan_PwmSetDelta =
+                       PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
+                                       advanceFanControlParameters.usFanOutputSensitivity);
+       return 0;
+}
+
+static int fiji_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.GnbLPML[i] = 0;
+
+       return 0;
+}
+
+static int fiji_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
+{
+       return 0;
+}
+
+static int fiji_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint16_t HiSidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+       uint16_t LoSidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+       HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+       LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+       smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
+       smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
+
+       return 0;
+}
+
+static int fiji_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+       uint32_t pm_fuse_table_offset;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+               if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU7_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU73_Firmware_Header, PmFuseTable),
+                               &pm_fuse_table_offset, SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to get pm_fuse_table_offset Failed!",
+                                       return -EINVAL);
+
+               /* DW6 */
+               if (fiji_populate_svi_load_line(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate SviLoadLine Failed!",
+                                       return -EINVAL);
+               /* DW7 */
+               if (fiji_populate_tdc_limit(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TDCLimit Failed!", return -EINVAL);
+               /* DW8 */
+               if (fiji_populate_dw8(hwmgr, pm_fuse_table_offset))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TdcWaterfallCtl, "
+                                       "LPMLTemperature Min and Max Failed!",
+                                       return -EINVAL);
+
+               /* DW9-DW12 */
+               if (0 != fiji_populate_temperature_scaler(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate LPMLTemperatureScaler Failed!",
+                                       return -EINVAL);
+
+               /* DW13-DW14 */
+               if (fiji_populate_fuzzy_fan(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate Fuzzy Fan Control parameters Failed!",
+                                       return -EINVAL);
+
+               /* DW15-DW18 */
+               if (fiji_populate_gnb_lpml(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Failed!",
+                                       return -EINVAL);
+
+               /* DW19 */
+               if (fiji_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Min and Max Vid Failed!",
+                                       return -EINVAL);
+
+               /* DW20 */
+               if (fiji_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
+                                       "Sidd Failed!", return -EINVAL);
+
+               if (smu7_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
+                               (uint8_t *)&smu_data->power_tune_table,
+                               sizeof(struct SMU73_Discrete_PmFuses), SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to download PmFuseTable Failed!",
+                                       return -EINVAL);
+       }
+       return 0;
+}
+
+/**
+* Preparation of vddc and vddgfx CAC tables for SMC.
+*
+* @param    hwmgr  the address of the hardware manager
+* @param    table  the SMC DPM table structure to be populated
+* @return   always 0
+*/
+static int fiji_populate_cac_table(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       uint32_t count;
+       uint8_t index;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+                       table_info->vddc_lookup_table;
+       /* tables is already swapped, so in order to use the value from it,
+        * we need to swap it back.
+        * We are populating vddc CAC data to BapmVddc table
+        * in split and merged mode
+        */
+
+       for (count = 0; count < lookup_table->count; count++) {
+               index = phm_get_voltage_index(lookup_table,
+                               data->vddc_voltage_table.entries[count].value);
+               table->BapmVddcVidLoSidd[count] =
+                       convert_to_vid(lookup_table->entries[index].us_cac_low);
+               table->BapmVddcVidHiSidd[count] =
+                       convert_to_vid(lookup_table->entries[index].us_cac_high);
+       }
+
+       return 0;
+}
+
+/**
+* Preparation of voltage tables for SMC.
+*
+* @param    hwmgr   the address of the hardware manager
+* @param    table   the SMC DPM table structure to be populated
+* @return   always  0
+*/
+
+static int fiji_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       int result;
+
+       result = fiji_populate_cac_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "can not populate CAC voltage tables to SMC",
+                       return -EINVAL);
+
+       return 0;
+}
+
+static int fiji_populate_ulv_level(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_Ulv *state)
+{
+       int result = 0;
+
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       state->CcPwrDynRm = 0;
+       state->CcPwrDynRm1 = 0;
+
+       state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+       state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+                       VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+       state->VddcPhase = 1;
+
+       if (!result) {
+               CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+               CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+               CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+       }
+       return result;
+}
+
+static int fiji_populate_ulv_state(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       return fiji_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int fiji_populate_smc_link_level(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       int i;
+
+       /* Index (dpm_table->pcie_speed_table.count)
+        * is reserved for PCIE boot level. */
+       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+               table->LinkLevel[i].PcieGenSpeed  =
+                               (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+               table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
+                               dpm_table->pcie_speed_table.dpm_levels[i].param1);
+               table->LinkLevel[i].EnabledForActivity = 1;
+               table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
+               table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
+               table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
+       }
+
+       smu_data->smc_state_table.LinkLevelCount =
+                       (uint8_t)dpm_table->pcie_speed_table.count;
+       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+       return 0;
+}
+
+
+/**
+* Calculates the SCLK dividers using the provided engine clock
+*
+* @param    hwmgr  the address of the hardware manager
+* @param    clock  the engine clock to use to populate the structure
+* @param    sclk   the SMC SCLK structure to be populated
+*/
+static int fiji_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+               uint32_t clock, struct SMU73_Discrete_GraphicsLevel *sclk)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       uint32_t spll_func_cntl            = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_3          = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       uint32_t spll_func_cntl_4          = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       uint32_t cg_spll_spread_spectrum   = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       uint32_t ref_clock;
+       uint32_t ref_divider;
+       uint32_t fbdiv;
+       int result;
+
+       /* get the engine clock dividers for this clock value */
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr, clock,  &dividers);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+                       "Error retrieving Engine Clock dividers from VBIOS.",
+                       return result);
+
+       /* To get FBDIV we need to multiply this by 16384 and divide it by Fref. */
+       ref_clock = atomctrl_get_reference_clock(hwmgr);
+       ref_divider = 1 + dividers.uc_pll_ref_div;
+
+       /* low 14 bits is fraction and high 12 bits is divider */
+       fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+       /* SPLL_FUNC_CNTL setup */
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                       SPLL_REF_DIV, dividers.uc_pll_ref_div);
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                       SPLL_PDIV_A,  dividers.uc_pll_post_div);
+
+       /* SPLL_FUNC_CNTL_3 setup*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+                       SPLL_FB_DIV, fbdiv);
+
+       /* set to use fractional accumulation*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3, CG_SPLL_FUNC_CNTL_3,
+                       SPLL_DITHEN, 1);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+               struct pp_atomctrl_internal_ss_info ssInfo;
+
+               uint32_t vco_freq = clock * dividers.uc_pll_post_div;
+               if (!atomctrl_get_engine_clock_spread_spectrum(hwmgr,
+                               vco_freq, &ssInfo)) {
+                       /*
+                        * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+                        * ss_info.speed_spectrum_rate -- in unit of khz
+                        *
+                        * clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2
+                        */
+                       uint32_t clk_s = ref_clock * 5 /
+                                       (ref_divider * ssInfo.speed_spectrum_rate);
+                       /* clkv = 2 * D * fbdiv / NS */
+                       uint32_t clk_v = 4 * ssInfo.speed_spectrum_percentage *
+                                       fbdiv / (clk_s * 10000);
+
+                       cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+                                       CG_SPLL_SPREAD_SPECTRUM, CLKS, clk_s);
+                       cg_spll_spread_spectrum = PHM_SET_FIELD(cg_spll_spread_spectrum,
+                                       CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+                       cg_spll_spread_spectrum_2 = PHM_SET_FIELD(cg_spll_spread_spectrum_2,
+                                       CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clk_v);
+               }
+       }
+
+       sclk->SclkFrequency        = clock;
+       sclk->CgSpllFuncCntl3      = spll_func_cntl_3;
+       sclk->CgSpllFuncCntl4      = spll_func_cntl_4;
+       sclk->SpllSpreadSpectrum   = cg_spll_spread_spectrum;
+       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
+       sclk->SclkDid              = (uint8_t)dividers.pll_post_divider;
+
+       return 0;
+}
+
+/**
+* Populates single SMC SCLK structure using the provided engine clock
+*
+* @param    hwmgr      the address of the hardware manager
+* @param    clock the engine clock to use to populate the structure
+* @param    sclk        the SMC SCLK structure to be populated
+*/
+
+static int fiji_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+               uint32_t clock, uint16_t sclk_al_threshold,
+               struct SMU73_Discrete_GraphicsLevel *level)
+{
+       int result;
+       /* PP_Clocks minClocks; */
+       uint32_t threshold, mvdd;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       result = fiji_calculate_sclk_params(hwmgr, clock, level);
+
+       /* populate graphics levels */
+       result = fiji_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_sclk, clock,
+                       (uint32_t *)(&level->MinVoltage), &mvdd);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "can not find VDDC voltage value for "
+                       "VDDC engine clock dependency table",
+                       return result);
+
+       level->SclkFrequency = clock;
+       level->ActivityLevel = sclk_al_threshold;
+       level->CcPwrDynRm = 0;
+       level->CcPwrDynRm1 = 0;
+       level->EnabledForActivity = 0;
+       level->EnabledForThrottle = 1;
+       level->UpHyst = 10;
+       level->DownHyst = 0;
+       level->VoltageDownHyst = 0;
+       level->PowerThrottle = 0;
+
+       threshold = clock * data->fast_watermark_threshold / 100;
+
+       data->display_timing.min_clock_in_sr = hwmgr->display_config.min_core_set_clock_in_sr;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
+               level->DeepSleepDivId = smu7_get_sleep_divider_id_from_clock(clock,
+                                                               hwmgr->display_config.min_core_set_clock_in_sr);
+
+
+       /* Default to slow, highest DPM level will be
+        * set to PPSMC_DISPLAY_WATERMARK_LOW later.
+        */
+       level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl3);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CgSpllFuncCntl4);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->SpllSpreadSpectrum2);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+
+       return 0;
+}
+/**
+* Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
+*
+* @param    hwmgr      the address of the hardware manager
+*/
+int fiji_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+       uint8_t pcie_entry_cnt = (uint8_t) data->dpm_table.pcie_speed_table.count;
+       int result = 0;
+       uint32_t array = smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
+       uint32_t array_size = sizeof(struct SMU73_Discrete_GraphicsLevel) *
+                       SMU73_MAX_LEVELS_GRAPHICS;
+       struct SMU73_Discrete_GraphicsLevel *levels =
+                       smu_data->smc_state_table.GraphicsLevel;
+       uint32_t i, max_entry;
+       uint8_t hightest_pcie_level_enabled = 0,
+                       lowest_pcie_level_enabled = 0,
+                       mid_pcie_level_enabled = 0,
+                       count = 0;
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+               result = fiji_populate_single_graphic_level(hwmgr,
+                               dpm_table->sclk_table.dpm_levels[i].value,
+                               (uint16_t)smu_data->activity_target[i],
+                               &levels[i]);
+               if (result)
+                       return result;
+
+               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+               if (i > 1)
+                       levels[i].DeepSleepDivId = 0;
+       }
+
+       /* Only enable level 0 for now.*/
+       levels[0].EnabledForActivity = 1;
+
+       /* set highest level watermark to high */
+       levels[dpm_table->sclk_table.count - 1].DisplayWatermark =
+                       PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       smu_data->smc_state_table.GraphicsDpmLevelCount =
+                       (uint8_t)dpm_table->sclk_table.count;
+       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+       if (pcie_table != NULL) {
+               PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
+                               "There must be 1 or more PCIE levels defined in PPTable.",
+                               return -EINVAL);
+               max_entry = pcie_entry_cnt - 1;
+               for (i = 0; i < dpm_table->sclk_table.count; i++)
+                       levels[i].pcieDpmLevel =
+                                       (uint8_t) ((i < max_entry) ? i : max_entry);
+       } else {
+               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << (hightest_pcie_level_enabled + 1))) != 0))
+                       hightest_pcie_level_enabled++;
+
+               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << lowest_pcie_level_enabled)) == 0))
+                       lowest_pcie_level_enabled++;
+
+               while ((count < hightest_pcie_level_enabled) &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
+                       count++;
+
+               mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
+                               hightest_pcie_level_enabled ?
+                                               (lowest_pcie_level_enabled + 1 + count) :
+                                               hightest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to hightest_pcie_level_enabled */
+               for (i = 2; i < dpm_table->sclk_table.count; i++)
+                       levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to lowest_pcie_level_enabled */
+               levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to mid_pcie_level_enabled */
+               levels[1].pcieDpmLevel = mid_pcie_level_enabled;
+       }
+       /* level count will send to smc once at init smc table and never change */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
+                       (uint32_t)array_size, SMC_RAM_END);
+
+       return result;
+}
+
+
+/**
+ * MCLK Frequency Ratio
+ * SEQ_CG_RESP  Bit[31:24] - 0x0
+ * Bit[27:24] \96 DDR3 Frequency ratio
+ * 0x0 <= 100MHz,       450 < 0x8 <= 500MHz
+ * 100 < 0x1 <= 150MHz,       500 < 0x9 <= 550MHz
+ * 150 < 0x2 <= 200MHz,       550 < 0xA <= 600MHz
+ * 200 < 0x3 <= 250MHz,       600 < 0xB <= 650MHz
+ * 250 < 0x4 <= 300MHz,       650 < 0xC <= 700MHz
+ * 300 < 0x5 <= 350MHz,       700 < 0xD <= 750MHz
+ * 350 < 0x6 <= 400MHz,       750 < 0xE <= 800MHz
+ * 400 < 0x7 <= 450MHz,       800 < 0xF
+ */
+static uint8_t fiji_get_mclk_frequency_ratio(uint32_t mem_clock)
+{
+       if (mem_clock <= 10000)
+               return 0x0;
+       if (mem_clock <= 15000)
+               return 0x1;
+       if (mem_clock <= 20000)
+               return 0x2;
+       if (mem_clock <= 25000)
+               return 0x3;
+       if (mem_clock <= 30000)
+               return 0x4;
+       if (mem_clock <= 35000)
+               return 0x5;
+       if (mem_clock <= 40000)
+               return 0x6;
+       if (mem_clock <= 45000)
+               return 0x7;
+       if (mem_clock <= 50000)
+               return 0x8;
+       if (mem_clock <= 55000)
+               return 0x9;
+       if (mem_clock <= 60000)
+               return 0xa;
+       if (mem_clock <= 65000)
+               return 0xb;
+       if (mem_clock <= 70000)
+               return 0xc;
+       if (mem_clock <= 75000)
+               return 0xd;
+       if (mem_clock <= 80000)
+               return 0xe;
+       /* mem_clock > 800MHz */
+       return 0xf;
+}
+
+/**
+* Populates the SMC MCLK structure using the provided memory clock
+*
+* @param    hwmgr   the address of the hardware manager
+* @param    clock   the memory clock to use to populate the structure
+* @param    sclk    the SMC SCLK structure to be populated
+*/
+static int fiji_calculate_mclk_params(struct pp_hwmgr *hwmgr,
+    uint32_t clock, struct SMU73_Discrete_MemoryLevel *mclk)
+{
+       struct pp_atomctrl_memory_clock_param mem_param;
+       int result;
+
+       result = atomctrl_get_memory_pll_dividers_vi(hwmgr, clock, &mem_param);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "Failed to get Memory PLL Dividers.",
+                       );
+
+       /* Save the result data to outpupt memory level structure */
+       mclk->MclkFrequency   = clock;
+       mclk->MclkDivider     = (uint8_t)mem_param.mpll_post_divider;
+       mclk->FreqRange       = fiji_get_mclk_frequency_ratio(clock);
+
+       return result;
+}
+
+static int fiji_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+               uint32_t clock, struct SMU73_Discrete_MemoryLevel *mem_level)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       int result = 0;
+       uint32_t mclk_stutter_mode_threshold = 60000;
+
+       if (table_info->vdd_dep_on_mclk) {
+               result = fiji_get_dependency_volt_by_clk(hwmgr,
+                               table_info->vdd_dep_on_mclk, clock,
+                               (uint32_t *)(&mem_level->MinVoltage), &mem_level->MinMvdd);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find MinVddc voltage value from memory "
+                               "VDDC voltage dependency table", return result);
+       }
+
+       mem_level->EnabledForThrottle = 1;
+       mem_level->EnabledForActivity = 0;
+       mem_level->UpHyst = 0;
+       mem_level->DownHyst = 100;
+       mem_level->VoltageDownHyst = 0;
+       mem_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
+       mem_level->StutterEnable = false;
+
+       mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       /* enable stutter mode if all the follow condition applied
+        * PECI_GetNumberOfActiveDisplays(hwmgr->pPECI,
+        * &(data->DisplayTiming.numExistingDisplays));
+        */
+       data->display_timing.num_existing_displays = 1;
+
+       if (mclk_stutter_mode_threshold &&
+               (clock <= mclk_stutter_mode_threshold) &&
+               (!data->is_uvd_enabled) &&
+               (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
+                               STUTTER_ENABLE) & 0x1))
+               mem_level->StutterEnable = true;
+
+       result = fiji_calculate_mclk_params(hwmgr, clock, mem_level);
+       if (!result) {
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
+       }
+       return result;
+}
+
+/**
+* Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
+*
+* @param    hwmgr      the address of the hardware manager
+*/
+int fiji_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       int result;
+       /* populate MCLK dpm table to SMU7 */
+       uint32_t array = smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU73_Discrete_DpmTable, MemoryLevel);
+       uint32_t array_size = sizeof(SMU73_Discrete_MemoryLevel) *
+                       SMU73_MAX_LEVELS_MEMORY;
+       struct SMU73_Discrete_MemoryLevel *levels =
+                       smu_data->smc_state_table.MemoryLevel;
+       uint32_t i;
+
+       for (i = 0; i < dpm_table->mclk_table.count; i++) {
+               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+                               "can not populate memory level as memory clock is zero",
+                               return -EINVAL);
+               result = fiji_populate_single_memory_level(hwmgr,
+                               dpm_table->mclk_table.dpm_levels[i].value,
+                               &levels[i]);
+               if (result)
+                       return result;
+       }
+
+       /* Only enable level 0 for now. */
+       levels[0].EnabledForActivity = 1;
+
+       /* in order to prevent MC activity from stutter mode to push DPM up.
+        * the UVD change complements this by putting the MCLK in
+        * a higher state by default such that we are not effected by
+        * up threshold or and MCLK DPM latency.
+        */
+       levels[0].ActivityLevel = (uint16_t)data->mclk_dpm0_activity_target;
+       CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
+
+       smu_data->smc_state_table.MemoryDpmLevelCount =
+                       (uint8_t)dpm_table->mclk_table.count;
+       data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+       /* set highest level watermark to high */
+       levels[dpm_table->mclk_table.count - 1].DisplayWatermark =
+                       PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       /* level count will send to smc once at init smc table and never change */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
+                       (uint32_t)array_size, SMC_RAM_END);
+
+       return result;
+}
+
+
+/**
+* Populates the SMC MVDD structure using the provided memory clock.
+*
+* @param    hwmgr      the address of the hardware manager
+* @param    mclk        the MCLK value to be used in the decision if MVDD should be high or low.
+* @param    voltage     the SMC VOLTAGE structure to be populated
+*/
+static int fiji_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+               uint32_t mclk, SMIO_Pattern *smio_pat)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint32_t i = 0;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+               /* find mvdd value which clock is more than request */
+               for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+                       if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+                               smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
+                               break;
+                       }
+               }
+               PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+                               "MVDD Voltage is outside the supported range.",
+                               return -EINVAL);
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int fiji_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+               SMU73_Discrete_DpmTable *table)
+{
+       int result = 0;
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       SMIO_Pattern vol_level;
+       uint32_t mvdd;
+       uint16_t us_mvdd;
+       uint32_t spll_func_cntl    = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_2  = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+
+       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (!data->sclk_dpm_key_disabled) {
+               /* Get MinVoltage and Frequency from DPM0,
+                * already converted to SMC_UL */
+               table->ACPILevel.SclkFrequency =
+                               data->dpm_table.sclk_table.dpm_levels[0].value;
+               result = fiji_get_dependency_volt_by_clk(hwmgr,
+                               table_info->vdd_dep_on_sclk,
+                               table->ACPILevel.SclkFrequency,
+                               (uint32_t *)(&table->ACPILevel.MinVoltage), &mvdd);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Cannot find ACPI VDDC voltage value " \
+                               "in Clock Dependency Table",
+                               );
+       } else {
+               table->ACPILevel.SclkFrequency =
+                               data->vbios_boot_state.sclk_bootup_value;
+               table->ACPILevel.MinVoltage =
+                               data->vbios_boot_state.vddc_bootup_value * VOLTAGE_SCALE;
+       }
+
+       /* get the engine clock dividers for this clock value */
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+                       table->ACPILevel.SclkFrequency,  &dividers);
+       PP_ASSERT_WITH_CODE(result == 0,
+                       "Error retrieving Engine Clock dividers from VBIOS.",
+                       return result);
+
+       table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+       table->ACPILevel.DeepSleepDivId = 0;
+
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                       SPLL_PWRON, 0);
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                       SPLL_RESET, 1);
+       spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2, CG_SPLL_FUNC_CNTL_2,
+                       SCLK_MUX_SEL, 4);
+
+       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+       table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       table->ACPILevel.CcPwrDynRm = 0;
+       table->ACPILevel.CcPwrDynRm1 = 0;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+       if (!data->mclk_dpm_key_disabled) {
+               /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+               table->MemoryACPILevel.MclkFrequency =
+                               data->dpm_table.mclk_table.dpm_levels[0].value;
+               result = fiji_get_dependency_volt_by_clk(hwmgr,
+                               table_info->vdd_dep_on_mclk,
+                               table->MemoryACPILevel.MclkFrequency,
+                       (uint32_t *)(&table->MemoryACPILevel.MinVoltage), &mvdd);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "Cannot find ACPI VDDCI voltage value in Clock Dependency Table",
+                               );
+       } else {
+               table->MemoryACPILevel.MclkFrequency =
+                               data->vbios_boot_state.mclk_bootup_value;
+               table->MemoryACPILevel.MinVoltage =
+                               data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE;
+       }
+
+       us_mvdd = 0;
+       if ((SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
+                       (data->mclk_dpm_key_disabled))
+               us_mvdd = data->vbios_boot_state.mvdd_bootup_value;
+       else {
+               if (!fiji_populate_mvdd_value(hwmgr,
+                               data->dpm_table.mclk_table.dpm_levels[0].value,
+                               &vol_level))
+                       us_mvdd = vol_level.Voltage;
+       }
+
+       table->MemoryACPILevel.MinMvdd =
+                       PP_HOST_TO_SMC_UL(us_mvdd * VOLTAGE_SCALE);
+
+       table->MemoryACPILevel.EnabledForThrottle = 0;
+       table->MemoryACPILevel.EnabledForActivity = 0;
+       table->MemoryACPILevel.UpHyst = 0;
+       table->MemoryACPILevel.DownHyst = 100;
+       table->MemoryACPILevel.VoltageDownHyst = 0;
+       table->MemoryACPILevel.ActivityLevel =
+                       PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
+
+       table->MemoryACPILevel.StutterEnable = false;
+       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
+
+       return result;
+}
+
+static int fiji_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+               SMU73_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+
+       table->VceLevelCount = (uint8_t)(mm_table->count);
+       table->VceBootLevel = 0;
+
+       for (count = 0; count < table->VceLevelCount; count++) {
+               table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
+               table->VceLevel[count].MinVoltage = 0;
+               table->VceLevel[count].MinVoltage |=
+                               (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+               table->VceLevel[count].MinVoltage |=
+                               ((mm_table->entries[count].vddc - VDDC_VDDCI_DELTA) *
+                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /*retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->VceLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for VCE engine clock",
+                               return result);
+
+               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
+       }
+       return result;
+}
+
+static int fiji_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+               SMU73_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+
+       table->AcpLevelCount = (uint8_t)(mm_table->count);
+       table->AcpBootLevel = 0;
+
+       for (count = 0; count < table->AcpLevelCount; count++) {
+               table->AcpLevel[count].Frequency = mm_table->entries[count].aclk;
+               table->AcpLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+                               VOLTAGE_SCALE) << VDDC_SHIFT;
+               table->AcpLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
+                               VDDC_VDDCI_DELTA) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->AcpLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->AcpLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for engine clock", return result);
+
+               table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].MinVoltage);
+       }
+       return result;
+}
+
+static int fiji_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
+               SMU73_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+
+       table->SamuBootLevel = 0;
+       table->SamuLevelCount = (uint8_t)(mm_table->count);
+
+       for (count = 0; count < table->SamuLevelCount; count++) {
+               /* not sure whether we need evclk or not */
+               table->SamuLevel[count].MinVoltage = 0;
+               table->SamuLevel[count].Frequency = mm_table->entries[count].samclock;
+               table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+                               VOLTAGE_SCALE) << VDDC_SHIFT;
+               table->SamuLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
+                               VDDC_VDDCI_DELTA) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->SamuLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for samu clock", return result);
+
+               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].MinVoltage);
+       }
+       return result;
+}
+
+static int fiji_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
+               int32_t eng_clock, int32_t mem_clock,
+               struct SMU73_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+       uint32_t dram_timing;
+       uint32_t dram_timing2;
+       uint32_t burstTime;
+       ULONG state, trrds, trrdl;
+       int result;
+
+       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+                       eng_clock, mem_clock);
+       PP_ASSERT_WITH_CODE(result == 0,
+                       "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+       dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+       dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+       burstTime = cgs_read_register(hwmgr->device, mmMC_ARB_BURST_TIME);
+
+       state = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, STATE0);
+       trrds = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDS0);
+       trrdl = PHM_GET_FIELD(burstTime, MC_ARB_BURST_TIME, TRRDL0);
+
+       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dram_timing);
+       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
+       arb_regs->McArbBurstTime   = (uint8_t)burstTime;
+       arb_regs->TRRDS            = (uint8_t)trrds;
+       arb_regs->TRRDL            = (uint8_t)trrdl;
+
+       return 0;
+}
+
+static int fiji_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct SMU73_Discrete_MCArbDramTimingTable arb_regs;
+       uint32_t i, j;
+       int result = 0;
+
+       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+                       result = fiji_populate_memory_timing_parameters(hwmgr,
+                                       data->dpm_table.sclk_table.dpm_levels[i].value,
+                                       data->dpm_table.mclk_table.dpm_levels[j].value,
+                                       &arb_regs.entries[i][j]);
+                       if (result)
+                               break;
+               }
+       }
+
+       if (!result)
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.arb_table_start,
+                               (uint8_t *)&arb_regs,
+                               sizeof(SMU73_Discrete_MCArbDramTimingTable),
+                               SMC_RAM_END);
+       return result;
+}
+
+static int fiji_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+
+       table->UvdLevelCount = (uint8_t)(mm_table->count);
+       table->UvdBootLevel = 0;
+
+       for (count = 0; count < table->UvdLevelCount; count++) {
+               table->UvdLevel[count].MinVoltage = 0;
+               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+               table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+                               VOLTAGE_SCALE) << VDDC_SHIFT;
+               table->UvdLevel[count].MinVoltage |= ((mm_table->entries[count].vddc -
+                               VDDC_VDDCI_DELTA) * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->UvdLevel[count].VclkFrequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for Vclk clock", return result);
+
+               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->UvdLevel[count].DclkFrequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for Dclk clock", return result);
+
+               table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
+
+       }
+       return result;
+}
+
+static int fiji_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       table->GraphicsBootLevel = 0;
+       table->MemoryBootLevel = 0;
+
+       /* find boot level from dpm table */
+       result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+                       data->vbios_boot_state.sclk_bootup_value,
+                       (uint32_t *)&(table->GraphicsBootLevel));
+
+       result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+                       data->vbios_boot_state.mclk_bootup_value,
+                       (uint32_t *)&(table->MemoryBootLevel));
+
+       table->BootVddc  = data->vbios_boot_state.vddc_bootup_value *
+                       VOLTAGE_SCALE;
+       table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
+                       VOLTAGE_SCALE;
+       table->BootMVdd  = data->vbios_boot_state.mvdd_bootup_value *
+                       VOLTAGE_SCALE;
+
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+       return 0;
+}
+
+static int fiji_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint8_t count, level;
+
+       count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
+       for (level = 0; level < count; level++) {
+               if (table_info->vdd_dep_on_sclk->entries[level].clk >=
+                               data->vbios_boot_state.sclk_bootup_value) {
+                       smu_data->smc_state_table.GraphicsBootLevel = level;
+                       break;
+               }
+       }
+
+       count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
+       for (level = 0; level < count; level++) {
+               if (table_info->vdd_dep_on_mclk->entries[level].clk >=
+                               data->vbios_boot_state.mclk_bootup_value) {
+                       smu_data->smc_state_table.MemoryBootLevel = level;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int fiji_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+       uint32_t ro, efuse, efuse2, clock_freq, volt_without_cks,
+                       volt_with_cks, value;
+       uint16_t clock_freq_u16;
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       uint8_t type, i, j, cks_setting, stretch_amount, stretch_amount2,
+                       volt_offset = 0;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+                       table_info->vdd_dep_on_sclk;
+
+       stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+       /* Read SMU_Eefuse to read and calculate RO and determine
+        * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+        */
+       efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixSMU_EFUSE_0 + (146 * 4));
+       efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixSMU_EFUSE_0 + (148 * 4));
+       efuse &= 0xFF000000;
+       efuse = efuse >> 24;
+       efuse2 &= 0xF;
+
+       if (efuse2 == 1)
+               ro = (2300 - 1350) * efuse / 255 + 1350;
+       else
+               ro = (2500 - 1000) * efuse / 255 + 1000;
+
+       if (ro >= 1660)
+               type = 0;
+       else
+               type = 1;
+
+       /* Populate Stretch amount */
+       smu_data->smc_state_table.ClockStretcherAmount = stretch_amount;
+
+       /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+       for (i = 0; i < sclk_table->count; i++) {
+               smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+                               sclk_table->entries[i].cks_enable << i;
+               volt_without_cks = (uint32_t)((14041 *
+                       (sclk_table->entries[i].clk/100) / 10000 + 3571 + 75 - ro) * 1000 /
+                       (4026 - (13924 * (sclk_table->entries[i].clk/100) / 10000)));
+               volt_with_cks = (uint32_t)((13946 *
+                       (sclk_table->entries[i].clk/100) / 10000 + 3320 + 45 - ro) * 1000 /
+                       (3664 - (11454 * (sclk_table->entries[i].clk/100) / 10000)));
+               if (volt_without_cks >= volt_with_cks)
+                       volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+                                       sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
+               smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+       }
+
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       STRETCH_ENABLE, 0x0);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       masterReset, 0x1);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       staticEnable, 0x1);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       masterReset, 0x0);
+
+       /* Populate CKS Lookup Table */
+       if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
+               stretch_amount2 = 0;
+       else if (stretch_amount == 3 || stretch_amount == 4)
+               stretch_amount2 = 1;
+       else {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ClockStretcher);
+               PP_ASSERT_WITH_CODE(false,
+                               "Stretch Amount in PPTable not supported\n",
+                               return -EINVAL);
+       }
+
+       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixPWR_CKS_CNTL);
+       value &= 0xFFC2FF87;
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].minFreq =
+                       fiji_clock_stretcher_lookup_table[stretch_amount2][0];
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].maxFreq =
+                       fiji_clock_stretcher_lookup_table[stretch_amount2][1];
+       clock_freq_u16 = (uint16_t)(PP_SMC_TO_HOST_UL(smu_data->smc_state_table.
+                       GraphicsLevel[smu_data->smc_state_table.GraphicsDpmLevelCount - 1].
+                       SclkFrequency) / 100);
+       if (fiji_clock_stretcher_lookup_table[stretch_amount2][0] <
+                       clock_freq_u16 &&
+           fiji_clock_stretcher_lookup_table[stretch_amount2][1] >
+                       clock_freq_u16) {
+               /* Program PWR_CKS_CNTL. CKS_USE_FOR_LOW_FREQ */
+               value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 16;
+               /* Program PWR_CKS_CNTL. CKS_LDO_REFSEL */
+               value |= (fiji_clock_stretcher_lookup_table[stretch_amount2][2]) << 18;
+               /* Program PWR_CKS_CNTL. CKS_STRETCH_AMOUNT */
+               value |= (fiji_clock_stretch_amount_conversion
+                               [fiji_clock_stretcher_lookup_table[stretch_amount2][3]]
+                                [stretch_amount]) << 3;
+       }
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+                       CKS_LOOKUPTableEntry[0].minFreq);
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+                       CKS_LOOKUPTableEntry[0].maxFreq);
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting =
+                       fiji_clock_stretcher_lookup_table[stretch_amount2][2] & 0x7F;
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting |=
+                       (fiji_clock_stretcher_lookup_table[stretch_amount2][3]) << 7;
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixPWR_CKS_CNTL, value);
+
+       /* Populate DDT Lookup Table */
+       for (i = 0; i < 4; i++) {
+               /* Assign the minimum and maximum VID stored
+                * in the last row of Clock Stretcher Voltage Table.
+                */
+               smu_data->smc_state_table.ClockStretcherDataTable.
+               ClockStretcherDataTableEntry[i].minVID =
+                               (uint8_t) fiji_clock_stretcher_ddt_table[type][i][2];
+               smu_data->smc_state_table.ClockStretcherDataTable.
+               ClockStretcherDataTableEntry[i].maxVID =
+                               (uint8_t) fiji_clock_stretcher_ddt_table[type][i][3];
+               /* Loop through each SCLK and check the frequency
+                * to see if it lies within the frequency for clock stretcher.
+                */
+               for (j = 0; j < smu_data->smc_state_table.GraphicsDpmLevelCount; j++) {
+                       cks_setting = 0;
+                       clock_freq = PP_SMC_TO_HOST_UL(
+                                       smu_data->smc_state_table.GraphicsLevel[j].SclkFrequency);
+                       /* Check the allowed frequency against the sclk level[j].
+                        *  Sclk's endianness has already been converted,
+                        *  and it's in 10Khz unit,
+                        *  as opposed to Data table, which is in Mhz unit.
+                        */
+                       if (clock_freq >=
+                                       (fiji_clock_stretcher_ddt_table[type][i][0]) * 100) {
+                               cks_setting |= 0x2;
+                               if (clock_freq <
+                                               (fiji_clock_stretcher_ddt_table[type][i][1]) * 100)
+                                       cks_setting |= 0x1;
+                       }
+                       smu_data->smc_state_table.ClockStretcherDataTable.
+                       ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
+               }
+               CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.
+                               ClockStretcherDataTable.
+                               ClockStretcherDataTableEntry[i].setting);
+       }
+
+       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
+       value &= 0xFFFFFFFE;
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
+
+       return 0;
+}
+
+/**
+* Populates the SMC VRConfig field in DPM table.
+*
+* @param    hwmgr   the address of the hardware manager
+* @param    table   the SMC DPM table structure to be populated
+* @return   always 0
+*/
+static int fiji_populate_vr_config(struct pp_hwmgr *hwmgr,
+               struct SMU73_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint16_t config;
+
+       config = VR_MERGED_WITH_VDDC;
+       table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
+
+       /* Set Vddc Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+               config = VR_SVI2_PLANE_1;
+               table->VRConfig |= config;
+       } else {
+               PP_ASSERT_WITH_CODE(false,
+                               "VDDC should be on SVI2 control in merged mode!",
+                               );
+       }
+       /* Set Vddci Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+               config = VR_SVI2_PLANE_2;  /* only in merged mode */
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+               config = VR_SMIO_PATTERN_1;
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       } else {
+               config = VR_STATIC_VOLTAGE;
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       }
+       /* Set Mvdd Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+               config = VR_SVI2_PLANE_2;
+               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+               config = VR_SMIO_PATTERN_2;
+               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+       } else {
+               config = VR_STATIC_VOLTAGE;
+               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+       }
+
+       return 0;
+}
+
+static int fiji_init_arb_table_index(struct pp_smumgr *smumgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(smumgr->backend);
+       uint32_t tmp;
+       int result;
+
+       /* This is a read-modify-write on the first byte of the ARB table.
+        * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
+        * is the field 'current'.
+        * This solution is ugly, but we never write the whole table only
+        * individual fields in it.
+        * In reality this field should not be in that structure
+        * but in a soft register.
+        */
+       result = smu7_read_smc_sram_dword(smumgr,
+                       smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+       if (result)
+               return result;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+       return smu7_write_smc_sram_dword(smumgr,
+                       smu_data->smu7_data.arb_table_start,  tmp, SMC_RAM_END);
+}
+
+/**
+* Initializes the SMC table and uploads it
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput  the pointer to input data (PowerState)
+* @return   always 0
+*/
+int fiji_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct SMU73_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+       uint8_t i;
+       struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+
+       fiji_initialize_power_tune_defaults(hwmgr);
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control)
+               fiji_populate_smc_voltage_tables(hwmgr, table);
+
+       table->SystemFlags = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StepVddc))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (data->is_memory_gddr5)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if (data->ulv_supported && table_info->us_ulv_voltage_offset) {
+               result = fiji_populate_ulv_state(hwmgr, table);
+               PP_ASSERT_WITH_CODE(0 == result,
+                               "Failed to initialize ULV state!", return result);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                               ixCG_ULV_PARAMETER, 0x40035);
+       }
+
+       result = fiji_populate_smc_link_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Link Level!", return result);
+
+       result = fiji_populate_all_graphic_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Graphics Level!", return result);
+
+       result = fiji_populate_all_memory_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Memory Level!", return result);
+
+       result = fiji_populate_smc_acpi_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize ACPI Level!", return result);
+
+       result = fiji_populate_smc_vce_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize VCE Level!", return result);
+
+       result = fiji_populate_smc_acp_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize ACP Level!", return result);
+
+       result = fiji_populate_smc_samu_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize SAMU Level!", return result);
+
+       /* Since only the initial state is completely set up at this point
+        * (the other states are just copies of the boot state) we only
+        * need to populate the  ARB settings for the initial state.
+        */
+       result = fiji_program_memory_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to Write ARB settings for the initial state.", return result);
+
+       result = fiji_populate_smc_uvd_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize UVD Level!", return result);
+
+       result = fiji_populate_smc_boot_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Boot Level!", return result);
+
+       result = fiji_populate_smc_initailial_state(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Boot State!", return result);
+
+       result = fiji_populate_bapm_parameters_in_dpm_table(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to populate BAPM Parameters!", return result);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ClockStretcher)) {
+               result = fiji_populate_clock_stretcher_data_table(hwmgr);
+               PP_ASSERT_WITH_CODE(0 == result,
+                               "Failed to populate Clock Stretcher Data Table!",
+                               return result);
+       }
+
+       table->GraphicsVoltageChangeEnable  = 1;
+       table->GraphicsThermThrottleEnable  = 1;
+       table->GraphicsInterval = 1;
+       table->VoltageInterval  = 1;
+       table->ThermalInterval  = 1;
+       table->TemperatureLimitHigh =
+                       table_info->cac_dtp_table->usTargetOperatingTemp *
+                       SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->TemperatureLimitLow  =
+                       (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+                       SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->MemoryVoltageChangeEnable = 1;
+       table->MemoryInterval = 1;
+       table->VoltageResponseTime = 0;
+       table->PhaseResponseTime = 0;
+       table->MemoryThermThrottleEnable = 1;
+       table->PCIeBootLinkLevel = 0;      /* 0:Gen1 1:Gen2 2:Gen3*/
+       table->PCIeGenInterval = 1;
+       table->VRConfig = 0;
+
+       result = fiji_populate_vr_config(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to populate VRConfig setting!", return result);
+
+       table->ThermGpio = 17;
+       table->SclkStepSize = 0x4000;
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+               table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_RegulatorHot);
+       } else {
+               table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_RegulatorHot);
+       }
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+                       &gpio_pin)) {
+               table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_AutomaticDCTransition);
+       } else {
+               table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_AutomaticDCTransition);
+       }
+
+       /* Thermal Output GPIO */
+       if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
+                       &gpio_pin)) {
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ThermalOutGPIO);
+
+               table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
+
+               /* For porlarity read GPIOPAD_A with assigned Gpio pin
+                * since VBIOS will program this register to set 'inactive state',
+                * driver can then determine 'active state' from this and
+                * program SMU with correct polarity
+                */
+               table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
+                               (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+               /* if required, combine VRHot/PCC with thermal out GPIO */
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_RegulatorHot) &&
+                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_CombinePCCWithThermalSignal))
+                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+       } else {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ThermalOutGPIO);
+               table->ThermOutGpio = 17;
+               table->ThermOutPolarity = 1;
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+       }
+
+       for (i = 0; i < SMU73_MAX_ENTRIES_SMIO; i++)
+               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+                       smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU73_Discrete_DpmTable, SystemFlags),
+                       (uint8_t *)&(table->SystemFlags),
+                       sizeof(SMU73_Discrete_DpmTable) - 3 * sizeof(SMU73_PIDController),
+                       SMC_RAM_END);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to upload dpm data to SMC memory!", return result);
+
+       result = fiji_init_arb_table_index(hwmgr->smumgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to upload arb data to SMC memory!", return result);
+
+       result = fiji_populate_pm_fuses(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to  populate PM fuses to SMC memory!", return result);
+       return 0;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+int fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       SMU73_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+       uint32_t duty100;
+       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       uint16_t fdo_min, slope1, slope2;
+       uint32_t reference_clock;
+       int res;
+       uint64_t tmp64;
+
+       if (smu_data->smu7_data.fan_table_start == 0) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL1, FMAX_DUTY100);
+
+       if (duty100 == 0) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
+                       usPWMMin * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (uint16_t)tmp64;
+
+       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.TempMin = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMin) / 100);
+       fan_table.TempMed = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMed) / 100);
+       fan_table.TempMax = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+       fan_table.Slope1 = cpu_to_be16(slope1);
+       fan_table.Slope2 = cpu_to_be16(slope2);
+
+       fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+       fan_table.HystDown = cpu_to_be16(hwmgr->
+                       thermal_controller.advanceFanControlParameters.ucTHyst);
+
+       fan_table.HystUp = cpu_to_be16(1);
+
+       fan_table.HystSlope = cpu_to_be16(1);
+
+       fan_table.TempRespLim = cpu_to_be16(5);
+
+       reference_clock = smu7_get_xclk(hwmgr);
+
+       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
+                       thermal_controller.advanceFanControlParameters.ulCycleDelay *
+                       reference_clock) / 1600);
+
+       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
+                       hwmgr->device, CGS_IND_REG__SMC,
+                       CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+       res = smu7_copy_bytes_to_smc(hwmgr->smumgr, smu_data->smu7_data.fan_table_start,
+                       (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
+                       SMC_RAM_END);
+
+       if (!res && hwmgr->thermal_controller.
+                       advanceFanControlParameters.ucMinimumPWMLimit)
+               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SetFanMinPwm,
+                               hwmgr->thermal_controller.
+                               advanceFanControlParameters.ucMinimumPWMLimit);
+
+       if (!res && hwmgr->thermal_controller.
+                       advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
+               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SetFanSclkTarget,
+                               hwmgr->thermal_controller.
+                               advanceFanControlParameters.ulMinFanSCLKAcousticLimit);
+
+       if (res)
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+
+       return 0;
+}
+
+int fiji_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->need_update_smu7_dpm_table &
+               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+               return fiji_program_memory_timing_parameters(hwmgr);
+
+       return 0;
+}
+
+int fiji_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+
+       int result = 0;
+       uint32_t low_sclk_interrupt_threshold = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkThrottleLowNotification)
+               && (hwmgr->gfx_arbiter.sclk_threshold !=
+                               data->low_sclk_interrupt_threshold)) {
+               data->low_sclk_interrupt_threshold =
+                               hwmgr->gfx_arbiter.sclk_threshold;
+               low_sclk_interrupt_threshold =
+                               data->low_sclk_interrupt_threshold;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU73_Discrete_DpmTable,
+                                       LowSclkInterruptThreshold),
+                               (uint8_t *)&low_sclk_interrupt_threshold,
+                               sizeof(uint32_t),
+                               SMC_RAM_END);
+       }
+       result = fiji_program_mem_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE((result == 0),
+                       "Failed to program memory timing parameters!",
+                       );
+       return result;
+}
+
+uint32_t fiji_get_offsetof(uint32_t type, uint32_t member)
+{
+       switch (type) {
+       case SMU_SoftRegisters:
+               switch (member) {
+               case HandshakeDisables:
+                       return offsetof(SMU73_SoftRegisters, HandshakeDisables);
+               case VoltageChangeTimeout:
+                       return offsetof(SMU73_SoftRegisters, VoltageChangeTimeout);
+               case AverageGraphicsActivity:
+                       return offsetof(SMU73_SoftRegisters, AverageGraphicsActivity);
+               case PreVBlankGap:
+                       return offsetof(SMU73_SoftRegisters, PreVBlankGap);
+               case VBlankTimeout:
+                       return offsetof(SMU73_SoftRegisters, VBlankTimeout);
+               case UcodeLoadStatus:
+                       return offsetof(SMU73_SoftRegisters, UcodeLoadStatus);
+               }
+       case SMU_Discrete_DpmTable:
+               switch (member) {
+               case UvdBootLevel:
+                       return offsetof(SMU73_Discrete_DpmTable, UvdBootLevel);
+               case VceBootLevel:
+                       return offsetof(SMU73_Discrete_DpmTable, VceBootLevel);
+               case SamuBootLevel:
+                       return offsetof(SMU73_Discrete_DpmTable, SamuBootLevel);
+               case LowSclkInterruptThreshold:
+                       return offsetof(SMU73_Discrete_DpmTable, LowSclkInterruptThreshold);
+               }
+       }
+       printk("cant't get the offset of type %x member %x \n", type, member);
+       return 0;
+}
+
+uint32_t fiji_get_mac_definition(uint32_t value)
+{
+       switch (value) {
+       case SMU_MAX_LEVELS_GRAPHICS:
+               return SMU73_MAX_LEVELS_GRAPHICS;
+       case SMU_MAX_LEVELS_MEMORY:
+               return SMU73_MAX_LEVELS_MEMORY;
+       case SMU_MAX_LEVELS_LINK:
+               return SMU73_MAX_LEVELS_LINK;
+       case SMU_MAX_ENTRIES_SMIO:
+               return SMU73_MAX_ENTRIES_SMIO;
+       case SMU_MAX_LEVELS_VDDC:
+               return SMU73_MAX_LEVELS_VDDC;
+       case SMU_MAX_LEVELS_VDDGFX:
+               return SMU73_MAX_LEVELS_VDDGFX;
+       case SMU_MAX_LEVELS_VDDCI:
+               return SMU73_MAX_LEVELS_VDDCI;
+       case SMU_MAX_LEVELS_MVDD:
+               return SMU73_MAX_LEVELS_MVDD;
+       }
+
+       printk("cant't get the mac of %x \n", value);
+       return 0;
+}
+
+
+static int fiji_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       smu_data->smc_state_table.UvdBootLevel = 0;
+       if (table_info->mm_dep_table->count > 0)
+               smu_data->smc_state_table.UvdBootLevel =
+                               (uint8_t) (table_info->mm_dep_table->count - 1);
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start + offsetof(SMU73_Discrete_DpmTable,
+                                               UvdBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0x00FFFFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_UVDDPM) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_UVDDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel));
+       return 0;
+}
+
+static int fiji_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_StablePState))
+               smu_data->smc_state_table.VceBootLevel =
+                       (uint8_t) (table_info->mm_dep_table->count - 1);
+       else
+               smu_data->smc_state_table.VceBootLevel = 0;
+
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                                       offsetof(SMU73_Discrete_DpmTable, VceBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFF00FFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_VCEDPM_SetEnabledMask,
+                               (uint32_t)1 << smu_data->smc_state_table.VceBootLevel);
+       return 0;
+}
+
+static int fiji_update_samu_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+
+
+       smu_data->smc_state_table.SamuBootLevel = 0;
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU73_Discrete_DpmTable, SamuBootLevel);
+
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFFFFFF00;
+       mm_boot_level_value |= smu_data->smc_state_table.SamuBootLevel << 0;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SAMUDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.SamuBootLevel));
+       return 0;
+}
+
+int fiji_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+       switch (type) {
+       case SMU_UVD_TABLE:
+               fiji_update_uvd_smc_table(hwmgr);
+               break;
+       case SMU_VCE_TABLE:
+               fiji_update_vce_smc_table(hwmgr);
+               break;
+       case SMU_SAMU_TABLE:
+               fiji_update_samu_smc_table(hwmgr);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+
+/**
+* Get the location of various tables inside the FW image.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always  0
+*/
+int fiji_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct fiji_smumgr *smu_data = (struct fiji_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t tmp;
+       int result;
+       bool error = false;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, DpmTable),
+                       &tmp, SMC_RAM_END);
+
+       if (0 == result)
+               smu_data->smu7_data.dpm_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, SoftRegisters),
+                       &tmp, SMC_RAM_END);
+
+       if (!result) {
+               data->soft_regs_start = tmp;
+               smu_data->smu7_data.soft_regs_start = tmp;
+       }
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, mcRegisterTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.mc_reg_table_start = tmp;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, FanTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.fan_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, mcArbDramTimingTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.arb_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU73_Firmware_Header, Version),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               hwmgr->microcode_version_info.SMC = tmp;
+
+       error |= (0 != result);
+
+       return error ? -1 : 0;
+}
+
+int fiji_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+
+       /* Program additional LP registers
+        * that are no longer programmed by VBIOS
+        */
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+
+       return 0;
+}
+
+bool fiji_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+                       ? true : false;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.h b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.h
new file mode 100644 (file)
index 0000000..d30d150
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015 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 FIJI_SMC_H
+#define FIJI_SMC_H
+
+#include "smumgr.h"
+#include "smu73.h"
+
+struct fiji_pt_defaults {
+       uint8_t   SviLoadLineEn;
+       uint8_t   SviLoadLineVddC;
+       uint8_t   TDC_VDDC_ThrottleReleaseLimitPerc;
+       uint8_t   TDC_MAWt;
+       uint8_t   TdcWaterfallCtl;
+       uint8_t   DTEAmbientTempBase;
+};
+
+int fiji_populate_all_graphic_levels(struct pp_hwmgr *hwmgr);
+int fiji_populate_all_memory_levels(struct pp_hwmgr *hwmgr);
+int fiji_init_smc_table(struct pp_hwmgr *hwmgr);
+int fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr);
+int fiji_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type);
+int fiji_update_sclk_threshold(struct pp_hwmgr *hwmgr);
+uint32_t fiji_get_offsetof(uint32_t type, uint32_t member);
+uint32_t fiji_get_mac_definition(uint32_t value);
+int fiji_process_firmware_header(struct pp_hwmgr *hwmgr);
+int fiji_initialize_mc_reg_table(struct pp_hwmgr *hwmgr);
+bool fiji_is_dpm_running(struct pp_hwmgr *hwmgr);
+
+#endif
+
old mode 100644 (file)
new mode 100755 (executable)
index 8e52a2e..02fe1df
@@ -38,6 +38,7 @@
 #include "bif/bif_5_0_sh_mask.h"
 #include "pp_debug.h"
 #include "fiji_pwrvirus.h"
+#include "fiji_smc.h"
 
 #define AVFS_EN_MSB                                        1568
 #define AVFS_EN_LSB                                        1568
@@ -57,509 +58,6 @@ static const struct SMU73_Discrete_GraphicsLevel avfs_graphics_level[8] = {
                { 0xf811d047, 0x80380100,   0x01,     0x00,   0x1e00, 0x00000610, 0x87020000, 0x21680000, 0x12000000,   0,      0,   0x0c,   0x01,       0x01,        0x01,      0x00,   0x00,      0x00,     0x00 }
 };
 
-static enum cgs_ucode_id fiji_convert_fw_type_to_cgs(uint32_t fw_type)
-{
-       enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
-
-       switch (fw_type) {
-       case UCODE_ID_SMU:
-               result = CGS_UCODE_ID_SMU;
-               break;
-       case UCODE_ID_SDMA0:
-               result = CGS_UCODE_ID_SDMA0;
-               break;
-       case UCODE_ID_SDMA1:
-               result = CGS_UCODE_ID_SDMA1;
-               break;
-       case UCODE_ID_CP_CE:
-               result = CGS_UCODE_ID_CP_CE;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = CGS_UCODE_ID_CP_PFP;
-               break;
-       case UCODE_ID_CP_ME:
-               result = CGS_UCODE_ID_CP_ME;
-               break;
-       case UCODE_ID_CP_MEC:
-               result = CGS_UCODE_ID_CP_MEC;
-               break;
-       case UCODE_ID_CP_MEC_JT1:
-               result = CGS_UCODE_ID_CP_MEC_JT1;
-               break;
-       case UCODE_ID_CP_MEC_JT2:
-               result = CGS_UCODE_ID_CP_MEC_JT2;
-               break;
-       case UCODE_ID_RLC_G:
-               result = CGS_UCODE_ID_RLC_G;
-               break;
-       default:
-               break;
-       }
-
-       return result;
-}
-/**
-* Set the address for reading/writing the SMC SRAM space.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smc_addr the address in the SMC RAM to access.
-*/
-static int fiji_set_smc_sram_address(struct pp_smumgr *smumgr,
-               uint32_t smc_addr, uint32_t limit)
-{
-       PP_ASSERT_WITH_CODE((0 == (3 & smc_addr)),
-                       "SMC address must be 4 byte aligned.", return -EINVAL;);
-       PP_ASSERT_WITH_CODE((limit > (smc_addr + 3)),
-                       "SMC address is beyond the SMC RAM area.", return -EINVAL;);
-
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_0, smc_addr);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-
-       return 0;
-}
-
-/**
-* Copy bytes from an array into the SMC RAM space.
-*
-* @param    smumgr  the address of the powerplay SMU manager.
-* @param    smcStartAddress the start address in the SMC RAM to copy bytes to.
-* @param    src the byte array to copy the bytes from.
-* @param    byteCount the number of bytes to copy.
-*/
-int fiji_copy_bytes_to_smc(struct pp_smumgr *smumgr,
-               uint32_t smcStartAddress, const uint8_t *src,
-               uint32_t byteCount, uint32_t limit)
-{
-       int result;
-       uint32_t data, originalData;
-       uint32_t addr, extraShift;
-
-       PP_ASSERT_WITH_CODE((0 == (3 & smcStartAddress)),
-                       "SMC address must be 4 byte aligned.", return -EINVAL;);
-       PP_ASSERT_WITH_CODE((limit > (smcStartAddress + byteCount)),
-                       "SMC address is beyond the SMC RAM area.", return -EINVAL;);
-
-       addr = smcStartAddress;
-
-       while (byteCount >= 4) {
-               /* Bytes are written into the SMC addres space with the MSB first. */
-               data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
-
-               result = fiji_set_smc_sram_address(smumgr, addr, limit);
-               if (result)
-                       return result;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data);
-
-               src += 4;
-               byteCount -= 4;
-               addr += 4;
-       }
-
-       if (byteCount) {
-               /* Now write the odd bytes left.
-                * Do a read modify write cycle.
-                */
-               data = 0;
-
-               result = fiji_set_smc_sram_address(smumgr, addr, limit);
-               if (result)
-                       return result;
-
-               originalData = cgs_read_register(smumgr->device, mmSMC_IND_DATA_0);
-               extraShift = 8 * (4 - byteCount);
-
-               while (byteCount > 0) {
-                       /* Bytes are written into the SMC addres
-                        * space with the MSB first.
-                        */
-                       data = (0x100 * data) + *src++;
-                       byteCount--;
-               }
-               data <<= extraShift;
-               data |= (originalData & ~((~0UL) << extraShift));
-
-               result = fiji_set_smc_sram_address(smumgr, addr, limit);
-               if (!result)
-                       return result;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data);
-       }
-       return 0;
-}
-
-int fiji_program_jump_on_start(struct pp_smumgr *smumgr)
-{
-       static const unsigned char data[] = { 0xE0, 0x00, 0x80, 0x40 };
-
-       fiji_copy_bytes_to_smc(smumgr, 0x0, data, 4, sizeof(data) + 1);
-
-       return 0;
-}
-
-/**
-* Return if the SMC is currently running.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-*/
-bool fiji_is_smc_ram_running(struct pp_smumgr *smumgr)
-{
-       return ((0 == SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device,
-                       CGS_IND_REG__SMC,
-                       SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
-                       && (0x20100 <= cgs_read_ind_register(smumgr->device,
-                                       CGS_IND_REG__SMC, ixSMC_PC_C)));
-}
-
-/**
-* Send a message to the SMC, and wait for its response.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   The response that came from the SMC.
-*/
-int fiji_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
-{
-       if (!fiji_is_smc_ram_running(smumgr))
-               return -1;
-
-       if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP)) {
-               printk(KERN_ERR "Failed to send Previous Message.");
-               SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       }
-
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       return 0;
-}
-
-/**
- * Send a message to the SMC with parameter
- * @param    smumgr:  the address of the powerplay hardware manager.
- * @param    msg: the message to send.
- * @param    parameter: the parameter to send
- * @return   The response that came from the SMC.
- */
-int fiji_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr,
-               uint16_t msg, uint32_t parameter)
-{
-       if (!fiji_is_smc_ram_running(smumgr))
-               return -1;
-
-       if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP)) {
-               printk(KERN_ERR "Failed to send Previous Message.");
-               SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       }
-
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       return 0;
-}
-
-
-/**
-* Send a message to the SMC with parameter, do not wait for response
-*
-* @param    smumgr:  the address of the powerplay hardware manager.
-* @param    msg: the message to send.
-* @param    parameter: the parameter to send
-* @return   The response that came from the SMC.
-*/
-int fiji_send_msg_to_smc_with_parameter_without_waiting(
-               struct pp_smumgr *smumgr, uint16_t msg, uint32_t parameter)
-{
-       if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP)) {
-               printk(KERN_ERR "Failed to send Previous Message.");
-               SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       }
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-/**
-* Uploads the SMU firmware from .hex file
-*
-* @param    smumgr  the address of the powerplay SMU manager.
-* @return   0 or -1.
-*/
-
-static int fiji_upload_smu_firmware_image(struct pp_smumgr *smumgr)
-{
-       const uint8_t *src;
-       uint32_t byte_count;
-       uint32_t *data;
-       struct cgs_firmware_info info = {0};
-
-       cgs_get_firmware_info(smumgr->device,
-                       fiji_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
-
-       if (info.image_size & 3) {
-               printk(KERN_ERR "SMC ucode is not 4 bytes aligned\n");
-               return -EINVAL;
-       }
-
-       if (info.image_size > FIJI_SMC_SIZE) {
-               printk(KERN_ERR "SMC address is beyond the SMC RAM area\n");
-               return -EINVAL;
-       }
-
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_0, 0x20000);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
-
-       byte_count = info.image_size;
-       src = (const uint8_t *)info.kptr;
-
-       data = (uint32_t *)src;
-       for (; byte_count >= 4; data++, byte_count -= 4)
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data[0]);
-
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-       return 0;
-}
-
-/**
-* Read a 32bit value from the SMC SRAM space.
-* ALL PARAMETERS ARE IN HOST BYTE ORDER.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smc_addr the address in the SMC RAM to access.
-* @param    value and output parameter for the data read from the SMC SRAM.
-*/
-int fiji_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr,
-               uint32_t *value, uint32_t limit)
-{
-       int     result = fiji_set_smc_sram_address(smumgr, smc_addr, limit);
-
-       if (result)
-               return result;
-
-       *value = cgs_read_register(smumgr->device, mmSMC_IND_DATA_0);
-       return 0;
-}
-
-/**
-* Write a 32bit value to the SMC SRAM space.
-* ALL PARAMETERS ARE IN HOST BYTE ORDER.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smc_addr the address in the SMC RAM to access.
-* @param    value to write to the SMC SRAM.
-*/
-int fiji_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr,
-               uint32_t value, uint32_t limit)
-{
-       int result;
-
-       result = fiji_set_smc_sram_address(smumgr, smc_addr, limit);
-
-       if (result)
-               return result;
-
-       cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, value);
-       return 0;
-}
-
-static uint32_t fiji_get_mask_for_firmware_type(uint32_t fw_type)
-{
-       uint32_t result = 0;
-
-       switch (fw_type) {
-       case UCODE_ID_SDMA0:
-               result = UCODE_ID_SDMA0_MASK;
-               break;
-       case UCODE_ID_SDMA1:
-               result = UCODE_ID_SDMA1_MASK;
-               break;
-       case UCODE_ID_CP_CE:
-               result = UCODE_ID_CP_CE_MASK;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = UCODE_ID_CP_PFP_MASK;
-               break;
-       case UCODE_ID_CP_ME:
-               result = UCODE_ID_CP_ME_MASK;
-               break;
-       case UCODE_ID_CP_MEC_JT1:
-               result = UCODE_ID_CP_MEC_MASK | UCODE_ID_CP_MEC_JT1_MASK;
-               break;
-       case UCODE_ID_CP_MEC_JT2:
-               result = UCODE_ID_CP_MEC_MASK | UCODE_ID_CP_MEC_JT2_MASK;
-               break;
-       case UCODE_ID_RLC_G:
-               result = UCODE_ID_RLC_G_MASK;
-               break;
-       default:
-               printk(KERN_ERR "UCode type is out of range!");
-               result = 0;
-       }
-
-       return result;
-}
-
-/* Populate one firmware image to the data structure */
-static int fiji_populate_single_firmware_entry(struct pp_smumgr *smumgr,
-               uint32_t fw_type, struct SMU_Entry *entry)
-{
-       int result;
-       struct cgs_firmware_info info = {0};
-
-       result = cgs_get_firmware_info(
-                       smumgr->device,
-                       fiji_convert_fw_type_to_cgs(fw_type),
-                       &info);
-
-       if (!result) {
-               entry->version = 0;
-               entry->id = (uint16_t)fw_type;
-               entry->image_addr_high = smu_upper_32_bits(info.mc_addr);
-               entry->image_addr_low = smu_lower_32_bits(info.mc_addr);
-               entry->meta_data_addr_high = 0;
-               entry->meta_data_addr_low = 0;
-               entry->data_size_byte = info.image_size;
-               entry->num_register_entries = 0;
-
-               if (fw_type == UCODE_ID_RLC_G)
-                       entry->flags = 1;
-               else
-                       entry->flags = 0;
-       }
-
-       return result;
-}
-
-static int fiji_request_smu_load_fw(struct pp_smumgr *smumgr)
-{
-       struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend);
-       uint32_t fw_to_load;
-       struct SMU_DRAMData_TOC *toc;
-
-       if (priv->soft_regs_start)
-               cgs_write_ind_register(smumgr->device, CGS_IND_REG__SMC,
-                               priv->soft_regs_start +
-                               offsetof(SMU73_SoftRegisters, UcodeLoadStatus),
-                               0x0);
-
-       toc = (struct SMU_DRAMData_TOC *)priv->header;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_RLC_G, &toc->entry[toc->num_entries++]),
-                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_CE, &toc->entry[toc->num_entries++]),
-                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_PFP, &toc->entry[toc->num_entries++]),
-                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_ME, &toc->entry[toc->num_entries++]),
-                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_MEC, &toc->entry[toc->num_entries++]),
-                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_MEC_JT1, &toc->entry[toc->num_entries++]),
-                                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_CP_MEC_JT2, &toc->entry[toc->num_entries++]),
-                                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_SDMA0, &toc->entry[toc->num_entries++]),
-                                       "Failed to Get Firmware Entry.\n" , return -1 );
-       PP_ASSERT_WITH_CODE(
-                       0 == fiji_populate_single_firmware_entry(smumgr,
-                                       UCODE_ID_SDMA1, &toc->entry[toc->num_entries++]),
-                                       "Failed to Get Firmware Entry.\n" , return -1 );
-
-       fiji_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_HI,
-                       priv->header_buffer.mc_addr_high);
-       fiji_send_msg_to_smc_with_parameter(smumgr,PPSMC_MSG_DRV_DRAM_ADDR_LO,
-                       priv->header_buffer.mc_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK
-                       + UCODE_ID_SDMA0_MASK
-                       + UCODE_ID_SDMA1_MASK
-                       + UCODE_ID_CP_CE_MASK
-                       + UCODE_ID_CP_ME_MASK
-                       + UCODE_ID_CP_PFP_MASK
-                       + UCODE_ID_CP_MEC_MASK
-                       + UCODE_ID_CP_MEC_JT1_MASK
-                       + UCODE_ID_CP_MEC_JT2_MASK;
-
-       if (fiji_send_msg_to_smc_with_parameter(smumgr,
-                       PPSMC_MSG_LoadUcodes, fw_to_load))
-               printk(KERN_ERR "Fail to Request SMU Load uCode");
-
-       return 0;
-}
-
-
-/* Check if the FW has been loaded, SMU will not return
- * if loading has not finished.
- */
-static int fiji_check_fw_load_finish(struct pp_smumgr *smumgr,
-               uint32_t fw_type)
-{
-       struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend);
-       uint32_t mask = fiji_get_mask_for_firmware_type(fw_type);
-
-       /* Check SOFT_REGISTERS_TABLE_28.UcodeLoadStatus */
-       if (smum_wait_on_indirect_register(smumgr, mmSMC_IND_INDEX,
-                       priv->soft_regs_start +
-                       offsetof(SMU73_SoftRegisters, UcodeLoadStatus),
-                       mask, mask)) {
-               printk(KERN_ERR "check firmware loading failed\n");
-               return -EINVAL;
-       }
-       return 0;
-}
-
-
-static int fiji_reload_firmware(struct pp_smumgr *smumgr)
-{
-       return smumgr->smumgr_funcs->start_smu(smumgr);
-}
-
-static bool fiji_is_hw_virtualization_enabled(struct pp_smumgr *smumgr)
-{
-       uint32_t value;
-
-       value = cgs_read_register(smumgr->device, mmBIF_IOV_FUNC_IDENTIFIER);
-       if (value & BIF_IOV_FUNC_IDENTIFIER__IOV_ENABLE_MASK) {
-               /* driver reads on SR-IOV enabled PF: 0x80000000
-                * driver reads on SR-IOV enabled VF: 0x80000001
-                * driver reads on SR-IOV disabled:   0x00000000
-                */
-               return true;
-       }
-       return false;
-}
-
-static int fiji_request_smu_specific_fw_load(struct pp_smumgr *smumgr, uint32_t fw_type)
-{
-       if (fiji_is_hw_virtualization_enabled(smumgr)) {
-               uint32_t masks = fiji_get_mask_for_firmware_type(fw_type);
-               if (fiji_send_msg_to_smc_with_parameter_without_waiting(smumgr,
-                               PPSMC_MSG_LoadUcodes, masks))
-                       printk(KERN_ERR "Fail to Request SMU Load uCode");
-       }
-       /* For non-virtualization cases,
-        * SMU loads all FWs at once in fiji_request_smu_load_fw.
-        */
-       return 0;
-}
-
 static int fiji_start_smu_in_protection_mode(struct pp_smumgr *smumgr)
 {
        int result = 0;
@@ -571,7 +69,7 @@ static int fiji_start_smu_in_protection_mode(struct pp_smumgr *smumgr)
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                        SMC_SYSCON_RESET_CNTL, rst_reg, 1);
 
-       result = fiji_upload_smu_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
        if (result)
                return result;
 
@@ -610,8 +108,8 @@ static int fiji_start_smu_in_protection_mode(struct pp_smumgr *smumgr)
                        SMU_STATUS, SMU_DONE, 0);
 
        /* Check pass/failed indicator */
-       if (1 != SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
-                       SMU_STATUS, SMU_PASS)) {
+       if (SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
+                       SMU_STATUS, SMU_PASS) != 1) {
                PP_ASSERT_WITH_CODE(false,
                                "SMU Firmware start failed!", return -1);
        }
@@ -639,12 +137,12 @@ static int fiji_start_smu_in_non_protection_mode(struct pp_smumgr *smumgr)
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                        SMC_SYSCON_RESET_CNTL, rst_reg, 1);
 
-       result = fiji_upload_smu_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
        if (result)
                return result;
 
        /* Set smc instruct start point at 0x0 */
-       fiji_program_jump_on_start(smumgr);
+       smu7_program_jump_on_start(smumgr);
 
        /* Enable clock */
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
@@ -698,15 +196,15 @@ static int fiji_start_avfs_btc(struct pp_smumgr *smumgr)
 
        priv->avfs.AvfsBtcStatus = AVFS_BTC_STARTED;
        if (priv->avfs.AvfsBtcParam) {
-               if (!fiji_send_msg_to_smc_with_parameter(smumgr,
+               if (!smum_send_msg_to_smc_with_parameter(smumgr,
                                PPSMC_MSG_PerformBtc, priv->avfs.AvfsBtcParam)) {
-                       if (!fiji_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs)) {
+                       if (!smum_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs)) {
                                priv->avfs.AvfsBtcStatus = AVFS_BTC_COMPLETED_UNSAVED;
                                result = 0;
                        } else {
                                printk(KERN_ERR "[AVFS][fiji_start_avfs_btc] Attempt"
                                                " to Enable AVFS Failed!");
-                               fiji_send_msg_to_smc(smumgr, PPSMC_MSG_DisableAvfs);
+                               smum_send_msg_to_smc(smumgr, PPSMC_MSG_DisableAvfs);
                                result = -1;
                        }
                } else {
@@ -736,7 +234,7 @@ int fiji_setup_pm_fuse_for_avfs(struct pp_smumgr *smumgr)
        charz_freq = 0x30750000; /* In 10KHz units 0x00007530 Actual value */
        inversion_voltage = 0x1A04; /* mV Q14.2 0x41A Actual value */
 
-       PP_ASSERT_WITH_CODE(0 == fiji_read_smc_sram_dword(smumgr,
+       PP_ASSERT_WITH_CODE(0 == smu7_read_smc_sram_dword(smumgr,
                        SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU73_Firmware_Header,
                                        PmFuseTable), &table_start, 0x40000),
                        "[AVFS][Fiji_SetupGfxLvlStruct] SMU could not communicate "
@@ -748,13 +246,13 @@ int fiji_setup_pm_fuse_for_avfs(struct pp_smumgr *smumgr)
        inversion_voltage_addr = table_start +
                        offsetof(struct SMU73_Discrete_PmFuses, InversionVoltage);
 
-       result = fiji_copy_bytes_to_smc(smumgr, charz_freq_addr,
+       result = smu7_copy_bytes_to_smc(smumgr, charz_freq_addr,
                        (uint8_t *)(&charz_freq), sizeof(charz_freq), 0x40000);
        PP_ASSERT_WITH_CODE(0 == result,
                        "[AVFS][fiji_setup_pm_fuse_for_avfs] charz_freq could not "
                        "be populated.", return -1;);
 
-       result = fiji_copy_bytes_to_smc(smumgr, inversion_voltage_addr,
+       result = smu7_copy_bytes_to_smc(smumgr, inversion_voltage_addr,
                        (uint8_t *)(&inversion_voltage), sizeof(inversion_voltage), 0x40000);
        PP_ASSERT_WITH_CODE(0 == result, "[AVFS][fiji_setup_pm_fuse_for_avfs] "
                        "charz_freq could not be populated.", return -1;);
@@ -769,7 +267,7 @@ int fiji_setup_graphics_level_structure(struct pp_smumgr *smumgr)
        uint32_t level_addr, vr_config_addr;
        uint32_t level_size = sizeof(avfs_graphics_level);
 
-       PP_ASSERT_WITH_CODE(0 == fiji_read_smc_sram_dword(smumgr,
+       PP_ASSERT_WITH_CODE(0 == smu7_read_smc_sram_dword(smumgr,
                        SMU7_FIRMWARE_HEADER_LOCATION +
                        offsetof(SMU73_Firmware_Header, DpmTable),
                        &table_start, 0x40000),
@@ -784,7 +282,7 @@ int fiji_setup_graphics_level_structure(struct pp_smumgr *smumgr)
        vr_config_addr = table_start +
                        offsetof(SMU73_Discrete_DpmTable, VRConfig);
 
-       PP_ASSERT_WITH_CODE(0 == fiji_copy_bytes_to_smc(smumgr, vr_config_addr,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, vr_config_addr,
                        (uint8_t *)&vr_config, sizeof(int32_t), 0x40000),
                        "[AVFS][Fiji_SetupGfxLvlStruct] Problems copying "
                        "vr_config value over to SMC",
@@ -792,7 +290,7 @@ int fiji_setup_graphics_level_structure(struct pp_smumgr *smumgr)
 
        level_addr = table_start + offsetof(SMU73_Discrete_DpmTable, GraphicsLevel);
 
-       PP_ASSERT_WITH_CODE(0 == fiji_copy_bytes_to_smc(smumgr, level_addr,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, level_addr,
                        (uint8_t *)(&avfs_graphics_level), level_size, 0x40000),
                        "[AVFS][Fiji_SetupGfxLvlStruct] Copying of DPM table failed!",
                        return -1;);
@@ -839,13 +337,13 @@ int fiji_avfs_event_mgr(struct pp_smumgr *smumgr, bool smu_started)
                break;
        case AVFS_BTC_COMPLETED_RESTORED: /*S3 State - Post SMU Start*/
                priv->avfs.AvfsBtcStatus = AVFS_BTC_SMUMSG_ERROR;
-               PP_ASSERT_WITH_CODE(0 == fiji_send_msg_to_smc(smumgr,
-                               PPSMC_MSG_VftTableIsValid),
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(smumgr,
+                               0x666),
                                "[AVFS][fiji_avfs_event_mgr] SMU did not respond "
                                "correctly to VftTableIsValid Msg",
                                return -1;);
                priv->avfs.AvfsBtcStatus = AVFS_BTC_SMUMSG_ERROR;
-               PP_ASSERT_WITH_CODE(0 == fiji_send_msg_to_smc(smumgr,
+               PP_ASSERT_WITH_CODE(0 == smum_send_msg_to_smc(smumgr,
                                PPSMC_MSG_EnableAvfs),
                                "[AVFS][fiji_avfs_event_mgr] SMU did not respond "
                                "correctly to EnableAvfs Message Msg",
@@ -898,7 +396,7 @@ static int fiji_start_smu(struct pp_smumgr *smumgr)
        struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend);
 
        /* Only start SMC if SMC RAM is not running */
-       if (!fiji_is_smc_ram_running(smumgr)) {
+       if (!smu7_is_smc_ram_running(smumgr)) {
                fiji_avfs_event_mgr(smumgr, false);
 
                /* Check if SMU is running in protected mode */
@@ -929,12 +427,12 @@ static int fiji_start_smu(struct pp_smumgr *smumgr)
        /* Setup SoftRegsStart here for register lookup in case
         * DummyBackEnd is used and ProcessFirmwareHeader is not executed
         */
-       fiji_read_smc_sram_dword(smumgr,
+       smu7_read_smc_sram_dword(smumgr,
                        SMU7_FIRMWARE_HEADER_LOCATION +
                        offsetof(SMU73_Firmware_Header, SoftRegisters),
-                       &(priv->soft_regs_start), 0x40000);
+                       &(priv->smu7_data.soft_regs_start), 0x40000);
 
-       result = fiji_request_smu_load_fw(smumgr);
+       result = smu7_request_smu_load_fw(smumgr);
 
        return result;
 }
@@ -963,28 +461,10 @@ static bool fiji_is_hw_avfs_present(struct pp_smumgr *smumgr)
 static int fiji_smu_init(struct pp_smumgr *smumgr)
 {
        struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend);
-       uint64_t mc_addr;
-
-       priv->header_buffer.data_size =
-                       ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       smu_allocate_memory(smumgr->device,
-                       priv->header_buffer.data_size,
-                       CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
-                       PAGE_SIZE,
-                       &mc_addr,
-                       &priv->header_buffer.kaddr,
-                       &priv->header_buffer.handle);
-
-       priv->header = priv->header_buffer.kaddr;
-       priv->header_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
-       priv->header_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
-
-       PP_ASSERT_WITH_CODE((NULL != priv->header),
-                       "Out of memory.",
-                       kfree(smumgr->backend);
-                       cgs_free_gpu_mem(smumgr->device,
-                       (cgs_handle_t)priv->header_buffer.handle);
-                       return -1);
+       int i;
+
+       if (smu7_init(smumgr))
+               return -EINVAL;
 
        priv->avfs.AvfsBtcStatus = AVFS_BTC_BOOT;
        if (fiji_is_hw_avfs_present(smumgr))
@@ -999,37 +479,35 @@ static int fiji_smu_init(struct pp_smumgr *smumgr)
        else
                priv->avfs.AvfsBtcStatus = AVFS_BTC_NOTSUPPORTED;
 
-       priv->acpi_optimization = 1;
+       for (i = 0; i < SMU73_MAX_LEVELS_GRAPHICS; i++)
+               priv->activity_target[i] = 30;
 
        return 0;
 }
 
-static int fiji_smu_fini(struct pp_smumgr *smumgr)
-{
-       struct fiji_smumgr *priv = (struct fiji_smumgr *)(smumgr->backend);
-
-       smu_free_memory(smumgr->device, (void *)priv->header_buffer.handle);
-
-       if (smumgr->backend) {
-               kfree(smumgr->backend);
-               smumgr->backend = NULL;
-       }
-
-       cgs_rel_firmware(smumgr->device, CGS_UCODE_ID_SMU);
-       return 0;
-}
 
 static const struct pp_smumgr_func fiji_smu_funcs = {
        .smu_init = &fiji_smu_init,
-       .smu_fini = &fiji_smu_fini,
+       .smu_fini = &smu7_smu_fini,
        .start_smu = &fiji_start_smu,
-       .check_fw_load_finish = &fiji_check_fw_load_finish,
-       .request_smu_load_fw = &fiji_reload_firmware,
-       .request_smu_load_specific_fw = &fiji_request_smu_specific_fw_load,
-       .send_msg_to_smc = &fiji_send_msg_to_smc,
-       .send_msg_to_smc_with_parameter = &fiji_send_msg_to_smc_with_parameter,
+       .check_fw_load_finish = &smu7_check_fw_load_finish,
+       .request_smu_load_fw = &smu7_reload_firmware,
+       .request_smu_load_specific_fw = NULL,
+       .send_msg_to_smc = &smu7_send_msg_to_smc,
+       .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
        .download_pptable_settings = NULL,
        .upload_pptable_settings = NULL,
+       .update_smc_table = fiji_update_smc_table,
+       .get_offsetof = fiji_get_offsetof,
+       .process_firmware_header = fiji_process_firmware_header,
+       .init_smc_table = fiji_init_smc_table,
+       .update_sclk_threshold = fiji_update_sclk_threshold,
+       .thermal_setup_fan_table = fiji_thermal_setup_fan_table,
+       .populate_all_graphic_levels = fiji_populate_all_graphic_levels,
+       .populate_all_memory_levels = fiji_populate_all_memory_levels,
+       .get_mac_definition = fiji_get_mac_definition,
+       .initialize_mc_reg_table = fiji_initialize_mc_reg_table,
+       .is_dpm_running = fiji_is_dpm_running,
 };
 
 int fiji_smum_init(struct pp_smumgr *smumgr)
index b4eb483..adcbdfb 100644 (file)
 #ifndef _FIJI_SMUMANAGER_H_
 #define _FIJI_SMUMANAGER_H_
 
+#include "smu73_discrete.h"
+#include <pp_endian.h>
+#include "smu7_smumgr.h"
+
+
 
 struct fiji_smu_avfs {
        enum AVFS_BTC_STATUS AvfsBtcStatus;
        uint32_t           AvfsBtcParam;
 };
 
-struct fiji_buffer_entry {
-       uint32_t data_size;
-       uint32_t mc_addr_low;
-       uint32_t mc_addr_high;
-       void *kaddr;
-       unsigned long  handle;
-};
 
 struct fiji_smumgr {
-       uint8_t        *header;
-       uint8_t        *mec_image;
-       uint32_t        soft_regs_start;
+       struct smu7_smumgr                   smu7_data;
+
        struct fiji_smu_avfs avfs;
-       uint32_t        acpi_optimization;
+       struct SMU73_Discrete_DpmTable       smc_state_table;
+       struct SMU73_Discrete_Ulv            ulv_setting;
+       struct SMU73_Discrete_PmFuses  power_tune_table;
+       const struct fiji_pt_defaults  *power_tune_defaults;
+       uint32_t        activity_target[SMU73_MAX_LEVELS_GRAPHICS];
 
-       struct fiji_buffer_entry header_buffer;
 };
 
-int fiji_smum_init(struct pp_smumgr *smumgr);
-int fiji_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smcAddress,
-               uint32_t *value, uint32_t limit);
-int fiji_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr,
-               uint32_t value, uint32_t limit);
-int fiji_copy_bytes_to_smc(struct pp_smumgr *smumgr, uint32_t smcStartAddress,
-               const uint8_t *src,     uint32_t byteCount, uint32_t limit);
+
 
 #endif
 
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c
new file mode 100644 (file)
index 0000000..eda802b
--- /dev/null
@@ -0,0 +1,2576 @@
+/*
+ * Copyright 2015 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 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.
+ * 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.
+ *
+ *
+ */
+
+#include "iceland_smc.h"
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "pppcielanes.h"
+#include "pp_endian.h"
+#include "smu7_ppsmc.h"
+
+#include "smu71_discrete.h"
+
+#include "smu/smu_7_1_1_d.h"
+#include "smu/smu_7_1_1_sh_mask.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+#include "processpptables.h"
+
+#include "iceland_smumgr.h"
+
+#define VOLTAGE_SCALE 4
+#define POWERTUNE_DEFAULT_SET_MAX    1
+#define VOLTAGE_VID_OFFSET_SCALE1   625
+#define VOLTAGE_VID_OFFSET_SCALE2   100
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define VDDC_VDDCI_DELTA            200
+
+#define DEVICE_ID_VI_ICELAND_M_6900    0x6900
+#define DEVICE_ID_VI_ICELAND_M_6901    0x6901
+#define DEVICE_ID_VI_ICELAND_M_6902    0x6902
+#define DEVICE_ID_VI_ICELAND_M_6903    0x6903
+
+static const struct iceland_pt_defaults defaults_iceland = {
+       /*
+        * sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc,
+        * TDC_MAWt, TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT
+        */
+       1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+       { 0x79,  0x253, 0x25D, 0xAE,  0x72,  0x80,  0x83,  0x86,  0x6F,  0xC8,  0xC9,  0xC9,  0x2F,  0x4D,  0x61  },
+       { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 }
+};
+
+/* 35W - XT, XTL */
+static const struct iceland_pt_defaults defaults_icelandxt = {
+       /*
+        * sviLoadLIneEn, SviLoadLineVddC,
+        * TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+        * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac,
+        * BAPM_TEMP_GRADIENT
+        */
+       1, 0xF, 0xFD, 0x19, 5, 45, 0, 0x0,
+       { 0xA7,  0x0, 0x0, 0xB5,  0x0, 0x0, 0x9F,  0x0, 0x0, 0xD6,  0x0, 0x0, 0xD7,  0x0, 0x0},
+       { 0x1EA, 0x0, 0x0, 0x224, 0x0, 0x0, 0x25E, 0x0, 0x0, 0x28E, 0x0, 0x0, 0x2AB, 0x0, 0x0}
+};
+
+/* 25W - PRO, LE */
+static const struct iceland_pt_defaults defaults_icelandpro = {
+       /*
+        * sviLoadLIneEn, SviLoadLineVddC,
+        * TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+        * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac,
+        * BAPM_TEMP_GRADIENT
+        */
+       1, 0xF, 0xFD, 0x19, 5, 45, 0, 0x0,
+       { 0xB7,  0x0, 0x0, 0xC3,  0x0, 0x0, 0xB5,  0x0, 0x0, 0xEA,  0x0, 0x0, 0xE6,  0x0, 0x0},
+       { 0x1EA, 0x0, 0x0, 0x224, 0x0, 0x0, 0x25E, 0x0, 0x0, 0x28E, 0x0, 0x0, 0x2AB, 0x0, 0x0}
+};
+
+static void iceland_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       struct cgs_system_info sys_info = {0};
+       uint32_t dev_id;
+
+       sys_info.size = sizeof(struct cgs_system_info);
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_DEV;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       dev_id = (uint32_t)sys_info.value;
+
+       switch (dev_id) {
+       case DEVICE_ID_VI_ICELAND_M_6900:
+       case DEVICE_ID_VI_ICELAND_M_6903:
+               smu_data->power_tune_defaults = &defaults_icelandxt;
+               break;
+
+       case DEVICE_ID_VI_ICELAND_M_6901:
+       case DEVICE_ID_VI_ICELAND_M_6902:
+               smu_data->power_tune_defaults = &defaults_icelandpro;
+               break;
+       default:
+               smu_data->power_tune_defaults = &defaults_iceland;
+               pr_warning("Unknown V.I. Device ID.\n");
+               break;
+       }
+       return;
+}
+
+static int iceland_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       smu_data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
+       smu_data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddc;
+       smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+       smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+       return 0;
+}
+
+static int iceland_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+       uint16_t tdc_limit;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       tdc_limit = (uint16_t)(hwmgr->dyn_state.cac_dtp_table->usTDC * 256);
+       smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+       smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+                       defaults->tdc_vddc_throttle_release_limit_perc;
+       smu_data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
+
+       return 0;
+}
+
+static int iceland_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+       uint32_t temp;
+
+       if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       fuse_table_offset +
+                       offsetof(SMU71_Discrete_PmFuses, TdcWaterfallCtl),
+                       (uint32_t *)&temp, SMC_RAM_END))
+               PP_ASSERT_WITH_CODE(false,
+                               "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+                               return -EINVAL);
+       else
+               smu_data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
+
+       return 0;
+}
+
+static int iceland_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+       return 0;
+}
+
+static int iceland_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 8; i++)
+               smu_data->power_tune_table.GnbLPML[i] = 0;
+
+       return 0;
+}
+
+static int iceland_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
+{
+       return 0;
+}
+
+static int iceland_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint16_t HiSidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+       uint16_t LoSidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+       struct phm_cac_tdp_table *cac_table = hwmgr->dyn_state.cac_dtp_table;
+
+       HiSidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+       LoSidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+       smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(HiSidd);
+       smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(LoSidd);
+
+       return 0;
+}
+
+static int iceland_populate_bapm_vddc_vid_sidd(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint8_t *hi_vid = smu_data->power_tune_table.BapmVddCVidHiSidd;
+       uint8_t *lo_vid = smu_data->power_tune_table.BapmVddCVidLoSidd;
+
+       PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.cac_leakage_table,
+                           "The CAC Leakage table does not exist!", return -EINVAL);
+       PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count <= 8,
+                           "There should never be more than 8 entries for BapmVddcVid!!!", return -EINVAL);
+       PP_ASSERT_WITH_CODE(hwmgr->dyn_state.cac_leakage_table->count == hwmgr->dyn_state.vddc_dependency_on_sclk->count,
+                           "CACLeakageTable->count and VddcDependencyOnSCLk->count not equal", return -EINVAL);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EVV)) {
+               for (i = 0; (uint32_t) i < hwmgr->dyn_state.cac_leakage_table->count; i++) {
+                       lo_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc1);
+                       hi_vid[i] = convert_to_vid(hwmgr->dyn_state.cac_leakage_table->entries[i].Vddc2);
+               }
+       } else {
+               PP_ASSERT_WITH_CODE(false, "Iceland should always support EVV", return -EINVAL);
+       }
+
+       return 0;
+}
+
+static int iceland_populate_vddc_vid(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint8_t *vid = smu_data->power_tune_table.VddCVid;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       PP_ASSERT_WITH_CODE(data->vddc_voltage_table.count <= 8,
+               "There should never be more than 8 entries for VddcVid!!!",
+               return -EINVAL);
+
+       for (i = 0; i < (int)data->vddc_voltage_table.count; i++) {
+               vid[i] = convert_to_vid(data->vddc_voltage_table.entries[i].value);
+       }
+
+       return 0;
+}
+
+
+
+static int iceland_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t pm_fuse_table_offset;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+               if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, PmFuseTable),
+                               &pm_fuse_table_offset, SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to get pm_fuse_table_offset Failed!",
+                                       return -EINVAL);
+
+               /* DW0 - DW3 */
+               if (iceland_populate_bapm_vddc_vid_sidd(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate bapm vddc vid Failed!",
+                                       return -EINVAL);
+
+               /* DW4 - DW5 */
+               if (iceland_populate_vddc_vid(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate vddc vid Failed!",
+                                       return -EINVAL);
+
+               /* DW6 */
+               if (iceland_populate_svi_load_line(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate SviLoadLine Failed!",
+                                       return -EINVAL);
+               /* DW7 */
+               if (iceland_populate_tdc_limit(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TDCLimit Failed!", return -EINVAL);
+               /* DW8 */
+               if (iceland_populate_dw8(hwmgr, pm_fuse_table_offset))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TdcWaterfallCtl, "
+                                       "LPMLTemperature Min and Max Failed!",
+                                       return -EINVAL);
+
+               /* DW9-DW12 */
+               if (0 != iceland_populate_temperature_scaler(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate LPMLTemperatureScaler Failed!",
+                                       return -EINVAL);
+
+               /* DW13-DW16 */
+               if (iceland_populate_gnb_lpml(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Failed!",
+                                       return -EINVAL);
+
+               /* DW17 */
+               if (iceland_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Min and Max Vid Failed!",
+                                       return -EINVAL);
+
+               /* DW18 */
+               if (iceland_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate BapmVddCBaseLeakage Hi and Lo Sidd Failed!",
+                                       return -EINVAL);
+
+               if (smu7_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
+                               (uint8_t *)&smu_data->power_tune_table,
+                               sizeof(struct SMU71_Discrete_PmFuses), SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to download PmFuseTable Failed!",
+                                       return -EINVAL);
+       }
+       return 0;
+}
+
+static int iceland_get_dependecy_volt_by_clk(struct pp_hwmgr *hwmgr,
+       struct phm_clock_voltage_dependency_table *allowed_clock_voltage_table,
+       uint32_t clock, uint32_t *vol)
+{
+       uint32_t i = 0;
+
+       /* clock - voltage dependency table is empty table */
+       if (allowed_clock_voltage_table->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+               /* find first sclk bigger than request */
+               if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+                       *vol = allowed_clock_voltage_table->entries[i].v;
+                       return 0;
+               }
+       }
+
+       /* sclk is bigger than max sclk in the dependence table */
+       *vol = allowed_clock_voltage_table->entries[i - 1].v;
+
+       return 0;
+}
+
+static int iceland_get_std_voltage_value_sidd(struct pp_hwmgr *hwmgr,
+               pp_atomctrl_voltage_table_entry *tab, uint16_t *hi,
+               uint16_t *lo)
+{
+       uint16_t v_index;
+       bool vol_found = false;
+       *hi = tab->value * VOLTAGE_SCALE;
+       *lo = tab->value * VOLTAGE_SCALE;
+
+       /* SCLK/VDDC Dependency Table has to exist. */
+       PP_ASSERT_WITH_CODE(NULL != hwmgr->dyn_state.vddc_dependency_on_sclk,
+                       "The SCLK/VDDC Dependency Table does not exist.\n",
+                       return -EINVAL);
+
+       if (NULL == hwmgr->dyn_state.cac_leakage_table) {
+               pr_warning("CAC Leakage Table does not exist, using vddc.\n");
+               return 0;
+       }
+
+       /*
+        * Since voltage in the sclk/vddc dependency table is not
+        * necessarily in ascending order because of ELB voltage
+        * patching, loop through entire list to find exact voltage.
+        */
+       for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+               if (tab->value == hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+                       vol_found = true;
+                       if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+                               *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+                               *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage * VOLTAGE_SCALE);
+                       } else {
+                               pr_warning("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index, using maximum index from CAC table.\n");
+                               *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+                               *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+                       }
+                       break;
+               }
+       }
+
+       /*
+        * If voltage is not found in the first pass, loop again to
+        * find the best match, equal or higher value.
+        */
+       if (!vol_found) {
+               for (v_index = 0; (uint32_t)v_index < hwmgr->dyn_state.vddc_dependency_on_sclk->count; v_index++) {
+                       if (tab->value <= hwmgr->dyn_state.vddc_dependency_on_sclk->entries[v_index].v) {
+                               vol_found = true;
+                               if ((uint32_t)v_index < hwmgr->dyn_state.cac_leakage_table->count) {
+                                       *lo = hwmgr->dyn_state.cac_leakage_table->entries[v_index].Vddc * VOLTAGE_SCALE;
+                                       *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[v_index].Leakage) * VOLTAGE_SCALE;
+                               } else {
+                                       pr_warning("Index from SCLK/VDDC Dependency Table exceeds the CAC Leakage Table index in second look up, using maximum index from CAC table.");
+                                       *lo = hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Vddc * VOLTAGE_SCALE;
+                                       *hi = (uint16_t)(hwmgr->dyn_state.cac_leakage_table->entries[hwmgr->dyn_state.cac_leakage_table->count - 1].Leakage * VOLTAGE_SCALE);
+                               }
+                               break;
+                       }
+               }
+
+               if (!vol_found)
+                       pr_warning("Unable to get std_vddc from SCLK/VDDC Dependency Table, using vddc.\n");
+       }
+
+       return 0;
+}
+
+static int iceland_populate_smc_voltage_table(struct pp_hwmgr *hwmgr,
+               pp_atomctrl_voltage_table_entry *tab,
+               SMU71_Discrete_VoltageLevel *smc_voltage_tab)
+{
+       int result;
+
+       result = iceland_get_std_voltage_value_sidd(hwmgr, tab,
+                       &smc_voltage_tab->StdVoltageHiSidd,
+                       &smc_voltage_tab->StdVoltageLoSidd);
+       if (0 != result) {
+               smc_voltage_tab->StdVoltageHiSidd = tab->value * VOLTAGE_SCALE;
+               smc_voltage_tab->StdVoltageLoSidd = tab->value * VOLTAGE_SCALE;
+       }
+
+       smc_voltage_tab->Voltage = PP_HOST_TO_SMC_US(tab->value * VOLTAGE_SCALE);
+       CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageHiSidd);
+       CONVERT_FROM_HOST_TO_SMC_US(smc_voltage_tab->StdVoltageHiSidd);
+
+       return 0;
+}
+
+static int iceland_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
+                       SMU71_Discrete_DpmTable *table)
+{
+       unsigned int count;
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       table->VddcLevelCount = data->vddc_voltage_table.count;
+       for (count = 0; count < table->VddcLevelCount; count++) {
+               result = iceland_populate_smc_voltage_table(hwmgr,
+                               &(data->vddc_voltage_table.entries[count]),
+                               &(table->VddcLevel[count]));
+               PP_ASSERT_WITH_CODE(0 == result, "do not populate SMC VDDC voltage table", return -EINVAL);
+
+               /* GPIO voltage control */
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->voltage_control)
+                       table->VddcLevel[count].Smio |= data->vddc_voltage_table.entries[count].smio_low;
+               else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control)
+                       table->VddcLevel[count].Smio = 0;
+       }
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
+
+       return 0;
+}
+
+static int iceland_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
+                       SMU71_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t count;
+       int result;
+
+       table->VddciLevelCount = data->vddci_voltage_table.count;
+
+       for (count = 0; count < table->VddciLevelCount; count++) {
+               result = iceland_populate_smc_voltage_table(hwmgr,
+                               &(data->vddci_voltage_table.entries[count]),
+                               &(table->VddciLevel[count]));
+               PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC VDDCI voltage table", return -EINVAL);
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       table->VddciLevel[count].Smio |= data->vddci_voltage_table.entries[count].smio_low;
+               else
+                       table->VddciLevel[count].Smio |= 0;
+       }
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
+
+       return 0;
+}
+
+static int iceland_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+                       SMU71_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t count;
+       int result;
+
+       table->MvddLevelCount = data->mvdd_voltage_table.count;
+
+       for (count = 0; count < table->VddciLevelCount; count++) {
+               result = iceland_populate_smc_voltage_table(hwmgr,
+                               &(data->mvdd_voltage_table.entries[count]),
+                               &table->MvddLevel[count]);
+               PP_ASSERT_WITH_CODE(result == 0, "do not populate SMC mvdd voltage table", return -EINVAL);
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control)
+                       table->MvddLevel[count].Smio |= data->mvdd_voltage_table.entries[count].smio_low;
+               else
+                       table->MvddLevel[count].Smio |= 0;
+       }
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
+
+       return 0;
+}
+
+
+static int iceland_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+       SMU71_Discrete_DpmTable *table)
+{
+       int result;
+
+       result = iceland_populate_smc_vddc_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "can not populate VDDC voltage table to SMC", return -EINVAL);
+
+       result = iceland_populate_smc_vdd_ci_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "can not populate VDDCI voltage table to SMC", return -EINVAL);
+
+       result = iceland_populate_smc_mvdd_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "can not populate MVDD voltage table to SMC", return -EINVAL);
+
+       return 0;
+}
+
+static int iceland_populate_ulv_level(struct pp_hwmgr *hwmgr,
+               struct SMU71_Discrete_Ulv *state)
+{
+       uint32_t voltage_response_time, ulv_voltage;
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       state->CcPwrDynRm = 0;
+       state->CcPwrDynRm1 = 0;
+
+       result = pp_tables_get_response_times(hwmgr, &voltage_response_time, &ulv_voltage);
+       PP_ASSERT_WITH_CODE((0 == result), "can not get ULV voltage value", return result;);
+
+       if (ulv_voltage == 0) {
+               data->ulv_supported = false;
+               return 0;
+       }
+
+       if (data->voltage_control != SMU7_VOLTAGE_CONTROL_BY_SVID2) {
+               /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+               if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+                       state->VddcOffset = 0;
+               else
+                       /* used in SMIO Mode. not implemented for now. this is backup only for CI. */
+                       state->VddcOffset = (uint16_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage);
+       } else {
+               /* use minimum voltage if ulv voltage in pptable is bigger than minimum voltage */
+               if (ulv_voltage > hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v)
+                       state->VddcOffsetVid = 0;
+               else  /* used in SVI2 Mode */
+                       state->VddcOffsetVid = (uint8_t)(
+                                       (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[0].v - ulv_voltage)
+                                               * VOLTAGE_VID_OFFSET_SCALE2
+                                               / VOLTAGE_VID_OFFSET_SCALE1);
+       }
+       state->VddcPhase = 1;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+       CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+       return 0;
+}
+
+static int iceland_populate_ulv_state(struct pp_hwmgr *hwmgr,
+                SMU71_Discrete_Ulv *ulv_level)
+{
+       return iceland_populate_ulv_level(hwmgr, ulv_level);
+}
+
+static int iceland_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU71_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t i;
+
+       /* Index (dpm_table->pcie_speed_table.count) is reserved for PCIE boot level. */
+       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+               table->LinkLevel[i].PcieGenSpeed  =
+                       (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+               table->LinkLevel[i].PcieLaneCount =
+                       (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+               table->LinkLevel[i].EnabledForActivity =
+                       1;
+               table->LinkLevel[i].SPC =
+                       (uint8_t)(data->pcie_spc_cap & 0xff);
+               table->LinkLevel[i].DownThreshold =
+                       PP_HOST_TO_SMC_UL(5);
+               table->LinkLevel[i].UpThreshold =
+                       PP_HOST_TO_SMC_UL(30);
+       }
+
+       smu_data->smc_state_table.LinkLevelCount =
+               (uint8_t)dpm_table->pcie_speed_table.count;
+       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+               phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+       return 0;
+}
+
+/**
+ * Calculates the SCLK dividers using the provided engine clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    engine_clock the engine clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int iceland_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+               uint32_t engine_clock, SMU71_Discrete_GraphicsLevel *sclk)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       pp_atomctrl_clock_dividers_vi dividers;
+       uint32_t spll_func_cntl            = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_3          = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       uint32_t spll_func_cntl_4          = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       uint32_t cg_spll_spread_spectrum   = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       uint32_t    reference_clock;
+       uint32_t reference_divider;
+       uint32_t fbdiv;
+       int result;
+
+       /* get the engine clock dividers for this clock value*/
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr, engine_clock,  &dividers);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+       /* To get FBDIV we need to multiply this by 16384 and divide it by Fref.*/
+       reference_clock = atomctrl_get_reference_clock(hwmgr);
+
+       reference_divider = 1 + dividers.uc_pll_ref_div;
+
+       /* low 14 bits is fraction and high 12 bits is divider*/
+       fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+       /* SPLL_FUNC_CNTL setup*/
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+               CG_SPLL_FUNC_CNTL, SPLL_REF_DIV, dividers.uc_pll_ref_div);
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+               CG_SPLL_FUNC_CNTL, SPLL_PDIV_A,  dividers.uc_pll_post_div);
+
+       /* SPLL_FUNC_CNTL_3 setup*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+               CG_SPLL_FUNC_CNTL_3, SPLL_FB_DIV, fbdiv);
+
+       /* set to use fractional accumulation*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+               CG_SPLL_FUNC_CNTL_3, SPLL_DITHEN, 1);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+               pp_atomctrl_internal_ss_info ss_info;
+
+               uint32_t vcoFreq = engine_clock * dividers.uc_pll_post_div;
+               if (0 == atomctrl_get_engine_clock_spread_spectrum(hwmgr, vcoFreq, &ss_info)) {
+                       /*
+                       * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+                       * ss_info.speed_spectrum_rate -- in unit of khz
+                       */
+                       /* clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2 */
+                       uint32_t clkS = reference_clock * 5 / (reference_divider * ss_info.speed_spectrum_rate);
+
+                       /* clkv = 2 * D * fbdiv / NS */
+                       uint32_t clkV = 4 * ss_info.speed_spectrum_percentage * fbdiv / (clkS * 10000);
+
+                       cg_spll_spread_spectrum =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, CLKS, clkS);
+                       cg_spll_spread_spectrum =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+                       cg_spll_spread_spectrum_2 =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum_2, CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clkV);
+               }
+       }
+
+       sclk->SclkFrequency        = engine_clock;
+       sclk->CgSpllFuncCntl3      = spll_func_cntl_3;
+       sclk->CgSpllFuncCntl4      = spll_func_cntl_4;
+       sclk->SpllSpreadSpectrum   = cg_spll_spread_spectrum;
+       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
+       sclk->SclkDid              = (uint8_t)dividers.pll_post_divider;
+
+       return 0;
+}
+
+static int iceland_populate_phase_value_based_on_sclk(struct pp_hwmgr *hwmgr,
+                               const struct phm_phase_shedding_limits_table *pl,
+                                       uint32_t sclk, uint32_t *p_shed)
+{
+       unsigned int i;
+
+       /* use the minimum phase shedding */
+       *p_shed = 1;
+
+       for (i = 0; i < pl->count; i++) {
+               if (sclk < pl->entries[i].Sclk) {
+                       *p_shed = i;
+                       break;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Populates single SMC SCLK structure using the provided engine clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    engine_clock the engine clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int iceland_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+                                               uint32_t engine_clock,
+                               uint16_t sclk_activity_level_threshold,
+                               SMU71_Discrete_GraphicsLevel *graphic_level)
+{
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       result = iceland_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
+
+       /* populate graphics levels*/
+       result = iceland_get_dependecy_volt_by_clk(hwmgr,
+               hwmgr->dyn_state.vddc_dependency_on_sclk, engine_clock,
+               &graphic_level->MinVddc);
+       PP_ASSERT_WITH_CODE((0 == result),
+               "can not find VDDC voltage value for VDDC       \
+               engine clock dependency table", return result);
+
+       /* SCLK frequency in units of 10KHz*/
+       graphic_level->SclkFrequency = engine_clock;
+       graphic_level->MinVddcPhases = 1;
+
+       if (data->vddc_phase_shed_control)
+               iceland_populate_phase_value_based_on_sclk(hwmgr,
+                               hwmgr->dyn_state.vddc_phase_shed_limits_table,
+                               engine_clock,
+                               &graphic_level->MinVddcPhases);
+
+       /* Indicates maximum activity level for this performance level. 50% for now*/
+       graphic_level->ActivityLevel = sclk_activity_level_threshold;
+
+       graphic_level->CcPwrDynRm = 0;
+       graphic_level->CcPwrDynRm1 = 0;
+       /* this level can be used if activity is high enough.*/
+       graphic_level->EnabledForActivity = 0;
+       /* this level can be used for throttling.*/
+       graphic_level->EnabledForThrottle = 1;
+       graphic_level->UpHyst = 0;
+       graphic_level->DownHyst = 100;
+       graphic_level->VoltageDownHyst = 0;
+       graphic_level->PowerThrottle = 0;
+
+       data->display_timing.min_clock_in_sr =
+                       hwmgr->display_config.min_core_set_clock_in_sr;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep))
+               graphic_level->DeepSleepDivId =
+                               smu7_get_sleep_divider_id_from_clock(engine_clock,
+                                               data->display_timing.min_clock_in_sr);
+
+       /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
+       graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       if (0 == result) {
+               graphic_level->MinVddc = PP_HOST_TO_SMC_UL(graphic_level->MinVddc * VOLTAGE_SCALE);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVddcPhases);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_US(graphic_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl3);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl4);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum2);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm1);
+       }
+
+       return result;
+}
+
+/**
+ * Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
+ *
+ * @param    hwmgr      the address of the hardware manager
+ */
+int iceland_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       uint32_t level_array_adress = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU71_Discrete_DpmTable, GraphicsLevel);
+
+       uint32_t level_array_size = sizeof(SMU71_Discrete_GraphicsLevel) *
+                                               SMU71_MAX_LEVELS_GRAPHICS;
+
+       SMU71_Discrete_GraphicsLevel *levels = smu_data->smc_state_table.GraphicsLevel;
+
+       uint32_t i;
+       uint8_t highest_pcie_level_enabled = 0;
+       uint8_t lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0;
+       uint8_t count = 0;
+       int result = 0;
+
+       memset(levels, 0x00, level_array_size);
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+               result = iceland_populate_single_graphic_level(hwmgr,
+                                       dpm_table->sclk_table.dpm_levels[i].value,
+                                       (uint16_t)smu_data->activity_target[i],
+                                       &(smu_data->smc_state_table.GraphicsLevel[i]));
+               if (result != 0)
+                       return result;
+
+               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+               if (i > 1)
+                       smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
+       }
+
+       /* Only enable level 0 for now. */
+       smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+
+       /* set highest level watermark to high */
+       if (dpm_table->sclk_table.count > 1)
+               smu_data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
+                       PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       smu_data->smc_state_table.GraphicsDpmLevelCount =
+               (uint8_t)dpm_table->sclk_table.count;
+       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+               phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+       while ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                               (1 << (highest_pcie_level_enabled + 1))) != 0) {
+               highest_pcie_level_enabled++;
+       }
+
+       while ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+               (1 << lowest_pcie_level_enabled)) == 0) {
+               lowest_pcie_level_enabled++;
+       }
+
+       while ((count < highest_pcie_level_enabled) &&
+                       ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                               (1 << (lowest_pcie_level_enabled + 1 + count))) == 0)) {
+               count++;
+       }
+
+       mid_pcie_level_enabled = (lowest_pcie_level_enabled+1+count) < highest_pcie_level_enabled ?
+               (lowest_pcie_level_enabled+1+count) : highest_pcie_level_enabled;
+
+
+       /* set pcieDpmLevel to highest_pcie_level_enabled*/
+       for (i = 2; i < dpm_table->sclk_table.count; i++) {
+               smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
+       }
+
+       /* set pcieDpmLevel to lowest_pcie_level_enabled*/
+       smu_data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+       /* set pcieDpmLevel to mid_pcie_level_enabled*/
+       smu_data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
+
+       /* level count will send to smc once at init smc table and never change*/
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, level_array_adress,
+                               (uint8_t *)levels, (uint32_t)level_array_size,
+                                                               SMC_RAM_END);
+
+       return result;
+}
+
+/**
+ * Populates the SMC MCLK structure using the provided memory clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    memory_clock the memory clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int iceland_calculate_mclk_params(
+               struct pp_hwmgr *hwmgr,
+               uint32_t memory_clock,
+               SMU71_Discrete_MemoryLevel *mclk,
+               bool strobe_mode,
+               bool dllStateOn
+               )
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       uint32_t  dll_cntl = data->clock_registers.vDLL_CNTL;
+       uint32_t  mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+       uint32_t  mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
+       uint32_t  mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
+       uint32_t  mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
+       uint32_t  mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
+       uint32_t  mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
+       uint32_t  mpll_ss1 = data->clock_registers.vMPLL_SS1;
+       uint32_t  mpll_ss2 = data->clock_registers.vMPLL_SS2;
+
+       pp_atomctrl_memory_clock_param mpll_param;
+       int result;
+
+       result = atomctrl_get_memory_pll_dividers_si(hwmgr,
+                               memory_clock, &mpll_param, strobe_mode);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Error retrieving Memory Clock Parameters from VBIOS.", return result);
+
+       /* MPLL_FUNC_CNTL setup*/
+       mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL, mpll_param.bw_ctrl);
+
+       /* MPLL_FUNC_CNTL_1 setup*/
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                                       MPLL_FUNC_CNTL_1, CLKF, mpll_param.mpll_fb_divider.cl_kf);
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                                       MPLL_FUNC_CNTL_1, CLKFRAC, mpll_param.mpll_fb_divider.clk_frac);
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                                       MPLL_FUNC_CNTL_1, VCO_MODE, mpll_param.vco_mode);
+
+       /* MPLL_AD_FUNC_CNTL setup*/
+       mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
+                                                       MPLL_AD_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+
+       if (data->is_memory_gddr5) {
+               /* MPLL_DQ_FUNC_CNTL setup*/
+               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
+                                                               MPLL_DQ_FUNC_CNTL, YCLK_SEL, mpll_param.yclk_sel);
+               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
+                                                               MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV, mpll_param.mpll_post_divider);
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
+               /*
+                ************************************
+                Fref = Reference Frequency
+                NF = Feedback divider ratio
+                NR = Reference divider ratio
+                Fnom = Nominal VCO output frequency = Fref * NF / NR
+                Fs = Spreading Rate
+                D = Percentage down-spread / 2
+                Fint = Reference input frequency to PFD = Fref / NR
+                NS = Spreading rate divider ratio = int(Fint / (2 * Fs))
+                CLKS = NS - 1 = ISS_STEP_NUM[11:0]
+                NV = D * Fs / Fnom * 4 * ((Fnom/Fref * NR) ^ 2)
+                CLKV = 65536 * NV = ISS_STEP_SIZE[25:0]
+                *************************************
+                */
+               pp_atomctrl_internal_ss_info ss_info;
+               uint32_t freq_nom;
+               uint32_t tmp;
+               uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
+
+               /* for GDDR5 for all modes and DDR3 */
+               if (1 == mpll_param.qdr)
+                       freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
+               else
+                       freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
+
+               /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2  Note: S.I. reference_divider = 1*/
+               tmp = (freq_nom / reference_clock);
+               tmp = tmp * tmp;
+
+               if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
+                       /* ss_info.speed_spectrum_percentage -- in unit of 0.01% */
+                       /* ss.Info.speed_spectrum_rate -- in unit of khz */
+                       /* CLKS = reference_clock / (2 * speed_spectrum_rate * reference_divider) * 10 */
+                       /*     = reference_clock * 5 / speed_spectrum_rate */
+                       uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
+
+                       /* CLKV = 65536 * speed_spectrum_percentage / 2 * spreadSpecrumRate / freq_nom * 4 / 100000 * ((freq_nom / reference_clock) ^ 2) */
+                       /*     = 131 * speed_spectrum_percentage * speed_spectrum_rate / 100 * ((freq_nom / reference_clock) ^ 2) / freq_nom */
+                       uint32_t clkv =
+                               (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
+                                                       ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
+
+                       mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
+                       mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
+               }
+       }
+
+       /* MCLK_PWRMGT_CNTL setup */
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
+
+
+       /* Save the result data to outpupt memory level structure */
+       mclk->MclkFrequency   = memory_clock;
+       mclk->MpllFuncCntl    = mpll_func_cntl;
+       mclk->MpllFuncCntl_1  = mpll_func_cntl_1;
+       mclk->MpllFuncCntl_2  = mpll_func_cntl_2;
+       mclk->MpllAdFuncCntl  = mpll_ad_func_cntl;
+       mclk->MpllDqFuncCntl  = mpll_dq_func_cntl;
+       mclk->MclkPwrmgtCntl  = mclk_pwrmgt_cntl;
+       mclk->DllCntl         = dll_cntl;
+       mclk->MpllSs1         = mpll_ss1;
+       mclk->MpllSs2         = mpll_ss2;
+
+       return 0;
+}
+
+static uint8_t iceland_get_mclk_frequency_ratio(uint32_t memory_clock,
+               bool strobe_mode)
+{
+       uint8_t mc_para_index;
+
+       if (strobe_mode) {
+               if (memory_clock < 12500) {
+                       mc_para_index = 0x00;
+               } else if (memory_clock > 47500) {
+                       mc_para_index = 0x0f;
+               } else {
+                       mc_para_index = (uint8_t)((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 = (uint8_t)((memory_clock - 60000) / 5000);
+               }
+       }
+
+       return mc_para_index;
+}
+
+static uint8_t iceland_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
+{
+       uint8_t mc_para_index;
+
+       if (memory_clock < 10000) {
+               mc_para_index = 0;
+       } else if (memory_clock >= 80000) {
+               mc_para_index = 0x0f;
+       } else {
+               mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
+       }
+
+       return mc_para_index;
+}
+
+static int iceland_populate_phase_value_based_on_mclk(struct pp_hwmgr *hwmgr, const struct phm_phase_shedding_limits_table *pl,
+                                       uint32_t memory_clock, uint32_t *p_shed)
+{
+       unsigned int i;
+
+       *p_shed = 1;
+
+       for (i = 0; i < pl->count; i++) {
+               if (memory_clock < pl->entries[i].Mclk) {
+                       *p_shed = i;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int iceland_populate_single_memory_level(
+               struct pp_hwmgr *hwmgr,
+               uint32_t memory_clock,
+               SMU71_Discrete_MemoryLevel *memory_level
+               )
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int result = 0;
+       bool dll_state_on;
+       struct cgs_display_info info = {0};
+       uint32_t mclk_edc_wr_enable_threshold = 40000;
+       uint32_t mclk_edc_enable_threshold = 40000;
+       uint32_t mclk_strobe_mode_threshold = 40000;
+
+       if (hwmgr->dyn_state.vddc_dependency_on_mclk != NULL) {
+               result = iceland_get_dependecy_volt_by_clk(hwmgr,
+                       hwmgr->dyn_state.vddc_dependency_on_mclk, memory_clock, &memory_level->MinVddc);
+               PP_ASSERT_WITH_CODE((0 == result),
+                       "can not find MinVddc voltage value from memory VDDC voltage dependency table", return result);
+       }
+
+       if (data->vddci_control == SMU7_VOLTAGE_CONTROL_NONE) {
+               memory_level->MinVddci = memory_level->MinVddc;
+       } else if (NULL != hwmgr->dyn_state.vddci_dependency_on_mclk) {
+               result = iceland_get_dependecy_volt_by_clk(hwmgr,
+                               hwmgr->dyn_state.vddci_dependency_on_mclk,
+                               memory_clock,
+                               &memory_level->MinVddci);
+               PP_ASSERT_WITH_CODE((0 == result),
+                       "can not find MinVddci voltage value from memory VDDCI voltage dependency table", return result);
+       }
+
+       memory_level->MinVddcPhases = 1;
+
+       if (data->vddc_phase_shed_control) {
+               iceland_populate_phase_value_based_on_mclk(hwmgr, hwmgr->dyn_state.vddc_phase_shed_limits_table,
+                               memory_clock, &memory_level->MinVddcPhases);
+       }
+
+       memory_level->EnabledForThrottle = 1;
+       memory_level->EnabledForActivity = 0;
+       memory_level->UpHyst = 0;
+       memory_level->DownHyst = 100;
+       memory_level->VoltageDownHyst = 0;
+
+       /* Indicates maximum activity level for this performance level.*/
+       memory_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
+       memory_level->StutterEnable = 0;
+       memory_level->StrobeEnable = 0;
+       memory_level->EdcReadEnable = 0;
+       memory_level->EdcWriteEnable = 0;
+       memory_level->RttEnable = 0;
+
+       /* default set to low watermark. Highest level will be set to high later.*/
+       memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+       data->display_timing.num_existing_displays = info.display_count;
+
+       /* stutter mode not support on iceland */
+
+       /* decide strobe mode*/
+       memory_level->StrobeEnable = (mclk_strobe_mode_threshold != 0) &&
+               (memory_clock <= mclk_strobe_mode_threshold);
+
+       /* decide EDC mode and memory clock ratio*/
+       if (data->is_memory_gddr5) {
+               memory_level->StrobeRatio = iceland_get_mclk_frequency_ratio(memory_clock,
+                                       memory_level->StrobeEnable);
+
+               if ((mclk_edc_enable_threshold != 0) &&
+                               (memory_clock > mclk_edc_enable_threshold)) {
+                       memory_level->EdcReadEnable = 1;
+               }
+
+               if ((mclk_edc_wr_enable_threshold != 0) &&
+                               (memory_clock > mclk_edc_wr_enable_threshold)) {
+                       memory_level->EdcWriteEnable = 1;
+               }
+
+               if (memory_level->StrobeEnable) {
+                       if (iceland_get_mclk_frequency_ratio(memory_clock, 1) >=
+                                       ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf))
+                               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+                       else
+                               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
+               } else
+                       dll_state_on = data->dll_default_on;
+       } else {
+               memory_level->StrobeRatio =
+                       iceland_get_ddr3_mclk_frequency_ratio(memory_clock);
+               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+       }
+
+       result = iceland_calculate_mclk_params(hwmgr,
+               memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+
+       if (0 == result) {
+               memory_level->MinVddc = PP_HOST_TO_SMC_UL(memory_level->MinVddc * VOLTAGE_SCALE);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinVddcPhases);
+               memory_level->MinVddci = PP_HOST_TO_SMC_UL(memory_level->MinVddci * VOLTAGE_SCALE);
+               memory_level->MinMvdd = PP_HOST_TO_SMC_UL(memory_level->MinMvdd * VOLTAGE_SCALE);
+               /* MCLK frequency in units of 10KHz*/
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
+               /* Indicates maximum activity level for this performance level.*/
+               CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
+       }
+
+       return result;
+}
+
+/**
+ * Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
+ *
+ * @param    hwmgr      the address of the hardware manager
+ */
+
+int iceland_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       int result;
+
+       /* populate MCLK dpm table to SMU7 */
+       uint32_t level_array_adress = smu_data->smu7_data.dpm_table_start + offsetof(SMU71_Discrete_DpmTable, MemoryLevel);
+       uint32_t level_array_size = sizeof(SMU71_Discrete_MemoryLevel) * SMU71_MAX_LEVELS_MEMORY;
+       SMU71_Discrete_MemoryLevel *levels = smu_data->smc_state_table.MemoryLevel;
+       uint32_t i;
+
+       memset(levels, 0x00, level_array_size);
+
+       for (i = 0; i < dpm_table->mclk_table.count; i++) {
+               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+                       "can not populate memory level as memory clock is zero", return -EINVAL);
+               result = iceland_populate_single_memory_level(hwmgr, dpm_table->mclk_table.dpm_levels[i].value,
+                       &(smu_data->smc_state_table.MemoryLevel[i]));
+               if (0 != result) {
+                       return result;
+               }
+       }
+
+       /* Only enable level 0 for now.*/
+       smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
+
+       /*
+       * in order to prevent MC activity from stutter mode to push DPM up.
+       * the UVD change complements this by putting the MCLK in a higher state
+       * by default such that we are not effected by up threshold or and MCLK DPM latency.
+       */
+       smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
+
+       smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
+       data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+       /* set highest level watermark to high*/
+       smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       /* level count will send to smc once at init smc table and never change*/
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+               level_array_adress, (uint8_t *)levels, (uint32_t)level_array_size,
+               SMC_RAM_END);
+
+       return result;
+}
+
+static int iceland_populate_mvdd_value(struct pp_hwmgr *hwmgr, uint32_t mclk,
+                                       SMU71_Discrete_VoltageLevel *voltage)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       uint32_t i = 0;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+               /* find mvdd value which clock is more than request */
+               for (i = 0; i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count; i++) {
+                       if (mclk <= hwmgr->dyn_state.mvdd_dependency_on_mclk->entries[i].clk) {
+                               /* Always round to higher voltage. */
+                               voltage->Voltage = data->mvdd_voltage_table.entries[i].value;
+                               break;
+                       }
+               }
+
+               PP_ASSERT_WITH_CODE(i < hwmgr->dyn_state.mvdd_dependency_on_mclk->count,
+                       "MVDD Voltage is outside the supported range.", return -EINVAL);
+
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int iceland_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+       SMU71_Discrete_DpmTable *table)
+{
+       int result = 0;
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       uint32_t vddc_phase_shed_control = 0;
+
+       SMU71_Discrete_VoltageLevel voltage_level;
+       uint32_t spll_func_cntl    = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_2  = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+       uint32_t dll_cntl          = data->clock_registers.vDLL_CNTL;
+       uint32_t mclk_pwrmgt_cntl  = data->clock_registers.vMCLK_PWRMGT_CNTL;
+
+
+       /* The ACPI state should not do DPM on DC (or ever).*/
+       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       if (data->acpi_vddc)
+               table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->acpi_vddc * VOLTAGE_SCALE);
+       else
+               table->ACPILevel.MinVddc = PP_HOST_TO_SMC_UL(data->min_vddc_in_pptable * VOLTAGE_SCALE);
+
+       table->ACPILevel.MinVddcPhases = vddc_phase_shed_control ? 0 : 1;
+       /* assign zero for now*/
+       table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
+
+       /* get the engine clock dividers for this clock value*/
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+               table->ACPILevel.SclkFrequency,  &dividers);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+       /* divider ID for required SCLK*/
+       table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+       table->ACPILevel.DeepSleepDivId = 0;
+
+       spll_func_cntl      = PHM_SET_FIELD(spll_func_cntl,
+                                                       CG_SPLL_FUNC_CNTL,   SPLL_PWRON,     0);
+       spll_func_cntl      = PHM_SET_FIELD(spll_func_cntl,
+                                                       CG_SPLL_FUNC_CNTL,   SPLL_RESET,     1);
+       spll_func_cntl_2    = PHM_SET_FIELD(spll_func_cntl_2,
+                                                       CG_SPLL_FUNC_CNTL_2, SCLK_MUX_SEL,   4);
+
+       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+       table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       table->ACPILevel.CcPwrDynRm = 0;
+       table->ACPILevel.CcPwrDynRm1 = 0;
+
+
+       /* For various features to be enabled/disabled while this level is active.*/
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+       /* SCLK frequency in units of 10KHz*/
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+       /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
+       table->MemoryACPILevel.MinVddc = table->ACPILevel.MinVddc;
+       table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+               table->MemoryACPILevel.MinVddci = table->MemoryACPILevel.MinVddc;
+       else {
+               if (data->acpi_vddci != 0)
+                       table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->acpi_vddci * VOLTAGE_SCALE);
+               else
+                       table->MemoryACPILevel.MinVddci = PP_HOST_TO_SMC_UL(data->min_vddci_in_pptable * VOLTAGE_SCALE);
+       }
+
+       if (0 == iceland_populate_mvdd_value(hwmgr, 0, &voltage_level))
+               table->MemoryACPILevel.MinMvdd =
+                       PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
+       else
+               table->MemoryACPILevel.MinMvdd = 0;
+
+       /* Force reset on DLL*/
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
+
+       /* Disable DLL in ACPIState*/
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
+
+       /* Enable DLL bypass signal*/
+       dll_cntl            = PHM_SET_FIELD(dll_cntl,
+               DLL_CNTL, MRDCK0_BYPASS, 0);
+       dll_cntl            = PHM_SET_FIELD(dll_cntl,
+               DLL_CNTL, MRDCK1_BYPASS, 0);
+
+       table->MemoryACPILevel.DllCntl            =
+               PP_HOST_TO_SMC_UL(dll_cntl);
+       table->MemoryACPILevel.MclkPwrmgtCntl     =
+               PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
+       table->MemoryACPILevel.MpllAdFuncCntl     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
+       table->MemoryACPILevel.MpllDqFuncCntl     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
+       table->MemoryACPILevel.MpllFuncCntl       =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
+       table->MemoryACPILevel.MpllFuncCntl_1     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
+       table->MemoryACPILevel.MpllFuncCntl_2     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
+       table->MemoryACPILevel.MpllSs1            =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
+       table->MemoryACPILevel.MpllSs2            =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
+
+       table->MemoryACPILevel.EnabledForThrottle = 0;
+       table->MemoryACPILevel.EnabledForActivity = 0;
+       table->MemoryACPILevel.UpHyst = 0;
+       table->MemoryACPILevel.DownHyst = 100;
+       table->MemoryACPILevel.VoltageDownHyst = 0;
+       /* Indicates maximum activity level for this performance level.*/
+       table->MemoryACPILevel.ActivityLevel = PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
+
+       table->MemoryACPILevel.StutterEnable = 0;
+       table->MemoryACPILevel.StrobeEnable = 0;
+       table->MemoryACPILevel.EdcReadEnable = 0;
+       table->MemoryACPILevel.EdcWriteEnable = 0;
+       table->MemoryACPILevel.RttEnable = 0;
+
+       return result;
+}
+
+static int iceland_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+                                       SMU71_Discrete_DpmTable *table)
+{
+       return 0;
+}
+
+static int iceland_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+               SMU71_Discrete_DpmTable *table)
+{
+       return 0;
+}
+
+static int iceland_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+               SMU71_Discrete_DpmTable *table)
+{
+       return 0;
+}
+
+static int iceland_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
+       SMU71_Discrete_DpmTable *table)
+{
+       return 0;
+}
+
+static int iceland_populate_memory_timing_parameters(
+               struct pp_hwmgr *hwmgr,
+               uint32_t engine_clock,
+               uint32_t memory_clock,
+               struct SMU71_Discrete_MCArbDramTimingTableEntry *arb_regs
+               )
+{
+       uint32_t dramTiming;
+       uint32_t dramTiming2;
+       uint32_t burstTime;
+       int result;
+
+       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+                               engine_clock, memory_clock);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+       dramTiming  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+       dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+       burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dramTiming);
+       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
+       arb_regs->McArbBurstTime = (uint8_t)burstTime;
+
+       return 0;
+}
+
+/**
+ * Setup parameters for the MC ARB.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ * This function is to be called from the SetPowerState table.
+ */
+static int iceland_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       int result = 0;
+       SMU71_Discrete_MCArbDramTimingTable  arb_regs;
+       uint32_t i, j;
+
+       memset(&arb_regs, 0x00, sizeof(SMU71_Discrete_MCArbDramTimingTable));
+
+       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+                       result = iceland_populate_memory_timing_parameters
+                               (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
+                                data->dpm_table.mclk_table.dpm_levels[j].value,
+                                &arb_regs.entries[i][j]);
+
+                       if (0 != result) {
+                               break;
+                       }
+               }
+       }
+
+       if (0 == result) {
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.arb_table_start,
+                               (uint8_t *)&arb_regs,
+                               sizeof(SMU71_Discrete_MCArbDramTimingTable),
+                               SMC_RAM_END
+                               );
+       }
+
+       return result;
+}
+
+static int iceland_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+                       SMU71_Discrete_DpmTable *table)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       table->GraphicsBootLevel = 0;
+       table->MemoryBootLevel = 0;
+
+       /* find boot level from dpm table*/
+       result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+                       data->vbios_boot_state.sclk_bootup_value,
+                       (uint32_t *)&(smu_data->smc_state_table.GraphicsBootLevel));
+
+       if (0 != result) {
+               smu_data->smc_state_table.GraphicsBootLevel = 0;
+               printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \
+                       in dependency table. Using Graphics DPM level 0!");
+               result = 0;
+       }
+
+       result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+               data->vbios_boot_state.mclk_bootup_value,
+               (uint32_t *)&(smu_data->smc_state_table.MemoryBootLevel));
+
+       if (0 != result) {
+               smu_data->smc_state_table.MemoryBootLevel = 0;
+               printk(KERN_ERR "[ powerplay ] VBIOS did not find boot engine clock value \
+                       in dependency table. Using Memory DPM level 0!");
+               result = 0;
+       }
+
+       table->BootVddc = data->vbios_boot_state.vddc_bootup_value;
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+               table->BootVddci = table->BootVddc;
+       else
+               table->BootVddci = data->vbios_boot_state.vddci_bootup_value;
+
+       table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
+
+       return result;
+}
+
+static int iceland_populate_mc_reg_address(struct pp_smumgr *smumgr,
+                                SMU71_Discrete_MCRegisters *mc_reg_table)
+{
+       const struct iceland_smumgr *smu_data = (struct iceland_smumgr *)smumgr->backend;
+
+       uint32_t i, j;
+
+       for (i = 0, j = 0; j < smu_data->mc_reg_table.last; j++) {
+               if (smu_data->mc_reg_table.validflag & 1<<j) {
+                       PP_ASSERT_WITH_CODE(i < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE,
+                               "Index of mc_reg_table->address[] array out of boundary", return -EINVAL);
+                       mc_reg_table->address[i].s0 =
+                               PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+
+       mc_reg_table->last = (uint8_t)i;
+
+       return 0;
+}
+
+/*convert register values from driver to SMC format */
+static void iceland_convert_mc_registers(
+       const struct iceland_mc_reg_entry *entry,
+       SMU71_Discrete_MCRegisterSet *data,
+       uint32_t num_entries, uint32_t valid_flag)
+{
+       uint32_t i, j;
+
+       for (i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & 1<<j) {
+                       data->value[i] = PP_HOST_TO_SMC_UL(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static int iceland_convert_mc_reg_table_entry_to_smc(
+               struct pp_smumgr *smumgr,
+               const uint32_t memory_clock,
+               SMU71_Discrete_MCRegisterSet *mc_reg_table_data
+               )
+{
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(smumgr->backend);
+       uint32_t i = 0;
+
+       for (i = 0; i < smu_data->mc_reg_table.num_entries; i++) {
+               if (memory_clock <=
+                       smu_data->mc_reg_table.mc_reg_table_entry[i].mclk_max) {
+                       break;
+               }
+       }
+
+       if ((i == smu_data->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       iceland_convert_mc_registers(&smu_data->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, smu_data->mc_reg_table.last,
+                               smu_data->mc_reg_table.validflag);
+
+       return 0;
+}
+
+static int iceland_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
+               SMU71_Discrete_MCRegisters *mc_regs)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int res;
+       uint32_t i;
+
+       for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+               res = iceland_convert_mc_reg_table_entry_to_smc(
+                               hwmgr->smumgr,
+                               data->dpm_table.mclk_table.dpm_levels[i].value,
+                               &mc_regs->data[i]
+                               );
+
+               if (0 != res)
+                       result = res;
+       }
+
+       return result;
+}
+
+static int iceland_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(smumgr->backend);
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t address;
+       int32_t result;
+
+       if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+               return 0;
+
+
+       memset(&smu_data->mc_regs, 0, sizeof(SMU71_Discrete_MCRegisters));
+
+       result = iceland_convert_mc_reg_table_to_smc(hwmgr, &(smu_data->mc_regs));
+
+       if (result != 0)
+               return result;
+
+
+       address = smu_data->smu7_data.mc_reg_table_start + (uint32_t)offsetof(SMU71_Discrete_MCRegisters, data[0]);
+
+       return  smu7_copy_bytes_to_smc(hwmgr->smumgr, address,
+                                (uint8_t *)&smu_data->mc_regs.data[0],
+                               sizeof(SMU71_Discrete_MCRegisterSet) * data->dpm_table.mclk_table.count,
+                               SMC_RAM_END);
+}
+
+static int iceland_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(smumgr->backend);
+
+       memset(&smu_data->mc_regs, 0x00, sizeof(SMU71_Discrete_MCRegisters));
+       result = iceland_populate_mc_reg_address(smumgr, &(smu_data->mc_regs));
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize MCRegTable for the MC register addresses!", return result;);
+
+       result = iceland_convert_mc_reg_table_to_smc(hwmgr, &smu_data->mc_regs);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize MCRegTable for driver state!", return result;);
+
+       return smu7_copy_bytes_to_smc(smumgr, smu_data->smu7_data.mc_reg_table_start,
+                       (uint8_t *)&smu_data->mc_regs, sizeof(SMU71_Discrete_MCRegisters), SMC_RAM_END);
+}
+
+static int iceland_populate_smc_initial_state(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       uint8_t count, level;
+
+       count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_sclk->count);
+
+       for (level = 0; level < count; level++) {
+               if (hwmgr->dyn_state.vddc_dependency_on_sclk->entries[level].clk
+                        >= data->vbios_boot_state.sclk_bootup_value) {
+                       smu_data->smc_state_table.GraphicsBootLevel = level;
+                       break;
+               }
+       }
+
+       count = (uint8_t)(hwmgr->dyn_state.vddc_dependency_on_mclk->count);
+
+       for (level = 0; level < count; level++) {
+               if (hwmgr->dyn_state.vddc_dependency_on_mclk->entries[level].clk
+                       >= data->vbios_boot_state.mclk_bootup_value) {
+                       smu_data->smc_state_table.MemoryBootLevel = level;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int iceland_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       const struct iceland_pt_defaults *defaults = smu_data->power_tune_defaults;
+       SMU71_Discrete_DpmTable  *dpm_table = &(smu_data->smc_state_table);
+       struct phm_cac_tdp_table *cac_dtp_table = hwmgr->dyn_state.cac_dtp_table;
+       struct phm_ppm_table *ppm = hwmgr->dyn_state.ppm_parameter_table;
+       const uint16_t *def1, *def2;
+       int i, j, k;
+
+
+       /*
+        * TDP number of fraction bits are changed from 8 to 7 for Iceland
+        * as requested by SMC team
+        */
+
+       dpm_table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 256));
+       dpm_table->TargetTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
+
+
+       dpm_table->DTETjOffset = 0;
+
+       dpm_table->GpuTjMax = (uint8_t)(data->thermal_temp_setting.temperature_high / PP_TEMPERATURE_UNITS_PER_CENTIGRADES);
+       dpm_table->GpuTjHyst = 8;
+
+       dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
+
+       /* The following are for new Iceland Multi-input fan/thermal control */
+       if (NULL != ppm) {
+               dpm_table->PPM_PkgPwrLimit = (uint16_t)ppm->dgpu_tdp * 256 / 1000;
+               dpm_table->PPM_TemperatureLimit = (uint16_t)ppm->tj_max * 256;
+       } else {
+               dpm_table->PPM_PkgPwrLimit = 0;
+               dpm_table->PPM_TemperatureLimit = 0;
+       }
+
+       CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_PkgPwrLimit);
+       CONVERT_FROM_HOST_TO_SMC_US(dpm_table->PPM_TemperatureLimit);
+
+       dpm_table->BAPM_TEMP_GRADIENT = PP_HOST_TO_SMC_UL(defaults->bamp_temp_gradient);
+       def1 = defaults->bapmti_r;
+       def2 = defaults->bapmti_rc;
+
+       for (i = 0; i < SMU71_DTE_ITERATIONS; i++) {
+               for (j = 0; j < SMU71_DTE_SOURCES; j++) {
+                       for (k = 0; k < SMU71_DTE_SINKS; k++) {
+                               dpm_table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*def1);
+                               dpm_table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*def2);
+                               def1++;
+                               def2++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int iceland_populate_smc_svi2_config(struct pp_hwmgr *hwmgr,
+                                           SMU71_Discrete_DpmTable *tab)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control)
+               tab->SVI2Enable |= VDDC_ON_SVI2;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+               tab->SVI2Enable |= VDDCI_ON_SVI2;
+       else
+               tab->MergedVddci = 1;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control)
+               tab->SVI2Enable |= MVDD_ON_SVI2;
+
+       PP_ASSERT_WITH_CODE(tab->SVI2Enable != (VDDC_ON_SVI2 | VDDCI_ON_SVI2 | MVDD_ON_SVI2) &&
+               (tab->SVI2Enable & VDDC_ON_SVI2), "SVI2 domain configuration is incorrect!", return -EINVAL);
+
+       return 0;
+}
+
+/**
+ * Initializes the SMC table and uploads it
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @param    pInput  the pointer to input data (PowerState)
+ * @return   always 0
+ */
+int iceland_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       SMU71_Discrete_DpmTable  *table = &(smu_data->smc_state_table);
+
+
+       iceland_initialize_power_tune_defaults(hwmgr);
+       memset(&(smu_data->smc_state_table), 0x00, sizeof(smu_data->smc_state_table));
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control) {
+               iceland_populate_smc_voltage_tables(hwmgr, table);
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StepVddc))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (data->is_memory_gddr5)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+
+       if (data->ulv_supported) {
+               result = iceland_populate_ulv_state(hwmgr, &(smu_data->ulv_setting));
+               PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize ULV state!", return result;);
+
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_ULV_PARAMETER, 0x40035);
+       }
+
+       result = iceland_populate_smc_link_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize Link Level!", return result;);
+
+       result = iceland_populate_all_graphic_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize Graphics Level!", return result;);
+
+       result = iceland_populate_all_memory_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize Memory Level!", return result;);
+
+       result = iceland_populate_smc_acpi_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize ACPI Level!", return result;);
+
+       result = iceland_populate_smc_vce_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize VCE Level!", return result;);
+
+       result = iceland_populate_smc_acp_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize ACP Level!", return result;);
+
+       result = iceland_populate_smc_samu_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize SAMU Level!", return result;);
+
+       /* Since only the initial state is completely set up at this point (the other states are just copies of the boot state) we only */
+       /* need to populate the  ARB settings for the initial state. */
+       result = iceland_program_memory_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to Write ARB settings for the initial state.", return result;);
+
+       result = iceland_populate_smc_uvd_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize UVD Level!", return result;);
+
+       table->GraphicsBootLevel = 0;
+       table->MemoryBootLevel = 0;
+
+       result = iceland_populate_smc_boot_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to initialize Boot Level!", return result;);
+
+       result = iceland_populate_smc_initial_state(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result, "Failed to initialize Boot State!", return result);
+
+       result = iceland_populate_bapm_parameters_in_dpm_table(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result, "Failed to populate BAPM Parameters!", return result);
+
+       table->GraphicsVoltageChangeEnable  = 1;
+       table->GraphicsThermThrottleEnable  = 1;
+       table->GraphicsInterval = 1;
+       table->VoltageInterval  = 1;
+       table->ThermalInterval  = 1;
+
+       table->TemperatureLimitHigh =
+               (data->thermal_temp_setting.temperature_high *
+                SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+       table->TemperatureLimitLow =
+               (data->thermal_temp_setting.temperature_low *
+               SMU7_Q88_FORMAT_CONVERSION_UNIT) / PP_TEMPERATURE_UNITS_PER_CENTIGRADES;
+
+       table->MemoryVoltageChangeEnable  = 1;
+       table->MemoryInterval  = 1;
+       table->VoltageResponseTime  = 0;
+       table->PhaseResponseTime  = 0;
+       table->MemoryThermThrottleEnable  = 1;
+       table->PCIeBootLinkLevel = 0;
+       table->PCIeGenInterval = 1;
+
+       result = iceland_populate_smc_svi2_config(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to populate SVI2 setting!", return result);
+
+       table->ThermGpio  = 17;
+       table->SclkStepSize = 0x4000;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcVid);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddcPhase);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskVddciVid);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMaskMvddVid);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+       table->BootVddc = PP_HOST_TO_SMC_US(table->BootVddc * VOLTAGE_SCALE);
+       table->BootVddci = PP_HOST_TO_SMC_US(table->BootVddci * VOLTAGE_SCALE);
+       table->BootMVdd = PP_HOST_TO_SMC_US(table->BootMVdd * VOLTAGE_SCALE);
+
+       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, smu_data->smu7_data.dpm_table_start +
+                                                                               offsetof(SMU71_Discrete_DpmTable, SystemFlags),
+                                                                               (uint8_t *)&(table->SystemFlags),
+                                                                               sizeof(SMU71_Discrete_DpmTable)-3 * sizeof(SMU71_PIDController),
+                                                                               SMC_RAM_END);
+
+       PP_ASSERT_WITH_CODE(0 == result,
+               "Failed to upload dpm data to SMC memory!", return result;);
+
+       /* Upload all ulv setting to SMC memory.(dpm level, dpm level count etc) */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+                       smu_data->smu7_data.ulv_setting_starts,
+                       (uint8_t *)&(smu_data->ulv_setting),
+                       sizeof(SMU71_Discrete_Ulv),
+                       SMC_RAM_END);
+
+
+       result = iceland_populate_initial_mc_reg_table(hwmgr);
+       PP_ASSERT_WITH_CODE((0 == result),
+               "Failed to populate initialize MC Reg table!", return result);
+
+       result = iceland_populate_pm_fuses(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to  populate PM fuses to SMC memory!", return result);
+
+       return 0;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+int iceland_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_smumgr *smu7_data = (struct smu7_smumgr *)(hwmgr->smumgr->backend);
+       SMU71_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+       uint32_t duty100;
+       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       uint16_t fdo_min, slope1, slope2;
+       uint32_t reference_clock;
+       int res;
+       uint64_t tmp64;
+
+       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl))
+               return 0;
+
+       if (0 == smu7_data->fan_table_start) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_FDO_CTRL1, FMAX_DUTY100);
+
+       if (0 == duty100) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (uint16_t)tmp64;
+
+       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed - hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh - hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh - hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
+       fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
+       fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+       fan_table.Slope1 = cpu_to_be16(slope1);
+       fan_table.Slope2 = cpu_to_be16(slope2);
+
+       fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+       fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
+
+       fan_table.HystUp = cpu_to_be16(1);
+
+       fan_table.HystSlope = cpu_to_be16(1);
+
+       fan_table.TempRespLim = cpu_to_be16(5);
+
+       reference_clock = smu7_get_xclk(hwmgr);
+
+       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
+
+       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+       /* fan_table.FanControl_GL_Flag = 1; */
+
+       res = smu7_copy_bytes_to_smc(hwmgr->smumgr, smu7_data->fan_table_start, (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table), SMC_RAM_END);
+
+       return 0;
+}
+
+
+static int iceland_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->need_update_smu7_dpm_table &
+               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+               return iceland_program_memory_timing_parameters(hwmgr);
+
+       return 0;
+}
+
+int iceland_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+
+       int result = 0;
+       uint32_t low_sclk_interrupt_threshold = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkThrottleLowNotification)
+               && (hwmgr->gfx_arbiter.sclk_threshold !=
+                               data->low_sclk_interrupt_threshold)) {
+               data->low_sclk_interrupt_threshold =
+                               hwmgr->gfx_arbiter.sclk_threshold;
+               low_sclk_interrupt_threshold =
+                               data->low_sclk_interrupt_threshold;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU71_Discrete_DpmTable,
+                                       LowSclkInterruptThreshold),
+                               (uint8_t *)&low_sclk_interrupt_threshold,
+                               sizeof(uint32_t),
+                               SMC_RAM_END);
+       }
+
+       result = iceland_update_and_upload_mc_reg_table(hwmgr);
+
+       PP_ASSERT_WITH_CODE((0 == result), "Failed to upload MC reg table!", return result);
+
+       result = iceland_program_mem_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE((result == 0),
+                       "Failed to program memory timing parameters!",
+                       );
+
+       return result;
+}
+
+uint32_t iceland_get_offsetof(uint32_t type, uint32_t member)
+{
+       switch (type) {
+       case SMU_SoftRegisters:
+               switch (member) {
+               case HandshakeDisables:
+                       return offsetof(SMU71_SoftRegisters, HandshakeDisables);
+               case VoltageChangeTimeout:
+                       return offsetof(SMU71_SoftRegisters, VoltageChangeTimeout);
+               case AverageGraphicsActivity:
+                       return offsetof(SMU71_SoftRegisters, AverageGraphicsActivity);
+               case PreVBlankGap:
+                       return offsetof(SMU71_SoftRegisters, PreVBlankGap);
+               case VBlankTimeout:
+                       return offsetof(SMU71_SoftRegisters, VBlankTimeout);
+               case UcodeLoadStatus:
+                       return offsetof(SMU71_SoftRegisters, UcodeLoadStatus);
+               }
+       case SMU_Discrete_DpmTable:
+               switch (member) {
+               case LowSclkInterruptThreshold:
+                       return offsetof(SMU71_Discrete_DpmTable, LowSclkInterruptThreshold);
+               }
+       }
+       printk("cant't get the offset of type %x member %x \n", type, member);
+       return 0;
+}
+
+uint32_t iceland_get_mac_definition(uint32_t value)
+{
+       switch (value) {
+       case SMU_MAX_LEVELS_GRAPHICS:
+               return SMU71_MAX_LEVELS_GRAPHICS;
+       case SMU_MAX_LEVELS_MEMORY:
+               return SMU71_MAX_LEVELS_MEMORY;
+       case SMU_MAX_LEVELS_LINK:
+               return SMU71_MAX_LEVELS_LINK;
+       case SMU_MAX_ENTRIES_SMIO:
+               return SMU71_MAX_ENTRIES_SMIO;
+       case SMU_MAX_LEVELS_VDDC:
+               return SMU71_MAX_LEVELS_VDDC;
+       case SMU_MAX_LEVELS_VDDCI:
+               return SMU71_MAX_LEVELS_VDDCI;
+       case SMU_MAX_LEVELS_MVDD:
+               return SMU71_MAX_LEVELS_MVDD;
+       }
+
+       printk("cant't get the mac of %x \n", value);
+       return 0;
+}
+
+/**
+ * Get the location of various tables inside the FW image.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+int iceland_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_smumgr *smu7_data = (struct smu7_smumgr *)(hwmgr->smumgr->backend);
+
+       uint32_t tmp;
+       int result;
+       bool error = false;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, DpmTable),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               smu7_data->dpm_table_start = tmp;
+       }
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, SoftRegisters),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               data->soft_regs_start = tmp;
+               smu7_data->soft_regs_start = tmp;
+       }
+
+       error |= (0 != result);
+
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, mcRegisterTable),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               smu7_data->mc_reg_table_start = tmp;
+       }
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, FanTable),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               smu7_data->fan_table_start = tmp;
+       }
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, mcArbDramTimingTable),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               smu7_data->arb_table_start = tmp;
+       }
+
+       error |= (0 != result);
+
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, Version),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               hwmgr->microcode_version_info.SMC = tmp;
+       }
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU71_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU71_Firmware_Header, UlvSettings),
+                               &tmp, SMC_RAM_END);
+
+       if (0 == result) {
+               smu7_data->ulv_setting_starts = tmp;
+       }
+
+       error |= (0 != result);
+
+       return error ? 1 : 0;
+}
+
+/*---------------------------MC----------------------------*/
+
+static uint8_t iceland_get_memory_modile_index(struct pp_hwmgr *hwmgr)
+{
+       return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
+}
+
+static bool iceland_check_s0_mc_reg_index(uint16_t in_reg, uint16_t *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+       case  mmMC_SEQ_RAS_TIMING:
+               *out_reg = mmMC_SEQ_RAS_TIMING_LP;
+               break;
+
+       case  mmMC_SEQ_DLL_STBY:
+               *out_reg = mmMC_SEQ_DLL_STBY_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CMD0:
+               *out_reg = mmMC_SEQ_G5PDX_CMD0_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CMD1:
+               *out_reg = mmMC_SEQ_G5PDX_CMD1_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CTRL:
+               *out_reg = mmMC_SEQ_G5PDX_CTRL_LP;
+               break;
+
+       case mmMC_SEQ_CAS_TIMING:
+               *out_reg = mmMC_SEQ_CAS_TIMING_LP;
+               break;
+
+       case mmMC_SEQ_MISC_TIMING:
+               *out_reg = mmMC_SEQ_MISC_TIMING_LP;
+               break;
+
+       case mmMC_SEQ_MISC_TIMING2:
+               *out_reg = mmMC_SEQ_MISC_TIMING2_LP;
+               break;
+
+       case mmMC_SEQ_PMG_DVS_CMD:
+               *out_reg = mmMC_SEQ_PMG_DVS_CMD_LP;
+               break;
+
+       case mmMC_SEQ_PMG_DVS_CTL:
+               *out_reg = mmMC_SEQ_PMG_DVS_CTL_LP;
+               break;
+
+       case mmMC_SEQ_RD_CTL_D0:
+               *out_reg = mmMC_SEQ_RD_CTL_D0_LP;
+               break;
+
+       case mmMC_SEQ_RD_CTL_D1:
+               *out_reg = mmMC_SEQ_RD_CTL_D1_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_D0:
+               *out_reg = mmMC_SEQ_WR_CTL_D0_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_D1:
+               *out_reg = mmMC_SEQ_WR_CTL_D1_LP;
+               break;
+
+       case mmMC_PMG_CMD_EMRS:
+               *out_reg = mmMC_SEQ_PMG_CMD_EMRS_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS1:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS1_LP;
+               break;
+
+       case mmMC_SEQ_PMG_TIMING:
+               *out_reg = mmMC_SEQ_PMG_TIMING_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS2:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS2_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_2:
+               *out_reg = mmMC_SEQ_WR_CTL_2_LP;
+               break;
+
+       default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static int iceland_set_s0_mc_reg_index(struct iceland_mc_reg_table *table)
+{
+       uint32_t i;
+       uint16_t address;
+
+       for (i = 0; i < table->last; i++) {
+               table->mc_reg_address[i].s0 =
+                       iceland_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address)
+                       ? address : table->mc_reg_address[i].s1;
+       }
+       return 0;
+}
+
+static int iceland_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table,
+                                       struct iceland_mc_reg_table *ni_table)
+{
+       uint8_t i, j;
+
+       PP_ASSERT_WITH_CODE((table->last <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+               "Invalid VramInfo table.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
+               "Invalid VramInfo table.", 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;
+}
+
+/**
+ * VBIOS omits some information to reduce size, we need to recover them here.
+ * 1.   when we see mmMC_SEQ_MISC1, bit[31:16] EMRS1, need to be write to  mmMC_PMG_CMD_EMRS /_LP[15:0].
+ *      Bit[15:0] MRS, need to be update mmMC_PMG_CMD_MRS/_LP[15:0]
+ * 2.   when we see mmMC_SEQ_RESERVE_M, bit[15:0] EMRS2, need to be write to mmMC_PMG_CMD_MRS1/_LP[15:0].
+ * 3.   need to set these data for each clock range
+ *
+ * @param    hwmgr the address of the powerplay hardware manager.
+ * @param    table the address of MCRegTable
+ * @return   always 0
+ */
+static int iceland_set_mc_special_registers(struct pp_hwmgr *hwmgr,
+                                       struct iceland_mc_reg_table *table)
+{
+       uint8_t i, j, k;
+       uint32_t temp_reg;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               PP_ASSERT_WITH_CODE((j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                       "Invalid VramInfo table.", return -EINVAL);
+
+               switch (table->mc_reg_address[i].s1) {
+
+               case mmMC_SEQ_MISC1:
+                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
+                       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++;
+                       PP_ASSERT_WITH_CODE((j < SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+
+                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
+                       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 (!data->is_memory_gddr5) {
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                               }
+                       }
+                       j++;
+                       PP_ASSERT_WITH_CODE((j <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+
+                       if (!data->is_memory_gddr5) {
+                               table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
+                               table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
+                               for (k = 0; k < table->num_entries; k++) {
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               }
+                               j++;
+                               PP_ASSERT_WITH_CODE((j <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                                       "Invalid VramInfo table.", return -EINVAL);
+                       }
+
+                       break;
+
+               case mmMC_SEQ_RESERVE_M:
+                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
+                       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++;
+                       PP_ASSERT_WITH_CODE((j <= SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+                       break;
+
+               default:
+                       break;
+               }
+
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static int iceland_set_valid_flag(struct iceland_mc_reg_table *table)
+{
+       uint8_t 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->validflag |= (1<<i);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int iceland_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(hwmgr->smumgr->backend);
+       pp_atomctrl_mc_reg_table *table;
+       struct iceland_mc_reg_table *ni_table = &smu_data->mc_reg_table;
+       uint8_t module_index = iceland_get_memory_modile_index(hwmgr);
+
+       table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
+
+       if (NULL == table)
+               return -ENOMEM;
+
+       /* Program additional LP registers that are no longer programmed by VBIOS */
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP, cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP, cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
+
+       memset(table, 0x00, sizeof(pp_atomctrl_mc_reg_table));
+
+       result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
+
+       if (0 == result)
+               result = iceland_copy_vbios_smc_reg_table(table, ni_table);
+
+       if (0 == result) {
+               iceland_set_s0_mc_reg_index(ni_table);
+               result = iceland_set_mc_special_registers(hwmgr, ni_table);
+       }
+
+       if (0 == result)
+               iceland_set_valid_flag(ni_table);
+
+       kfree(table);
+
+       return result;
+}
+
+bool iceland_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+                       ? true : false;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.h b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.h
new file mode 100644 (file)
index 0000000..13c8dbb
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2015 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 _ICELAND_SMC_H
+#define _ICELAND_SMC_H
+
+#include "smumgr.h"
+
+
+int iceland_populate_all_graphic_levels(struct pp_hwmgr *hwmgr);
+int iceland_populate_all_memory_levels(struct pp_hwmgr *hwmgr);
+int iceland_init_smc_table(struct pp_hwmgr *hwmgr);
+int iceland_thermal_setup_fan_table(struct pp_hwmgr *hwmgr);
+int iceland_update_sclk_threshold(struct pp_hwmgr *hwmgr);
+uint32_t iceland_get_offsetof(uint32_t type, uint32_t member);
+uint32_t iceland_get_mac_definition(uint32_t value);
+int iceland_process_firmware_header(struct pp_hwmgr *hwmgr);
+int iceland_initialize_mc_reg_table(struct pp_hwmgr *hwmgr);
+bool iceland_is_dpm_running(struct pp_hwmgr *hwmgr);
+#endif
+
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.c
new file mode 100644 (file)
index 0000000..eeafefc
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2016 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.
+ *
+ * Author: Huang Rui <ray.huang@amd.com>
+ *
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/gfp.h>
+
+#include "smumgr.h"
+#include "iceland_smumgr.h"
+#include "pp_debug.h"
+#include "smu_ucode_xfer_vi.h"
+#include "ppsmc.h"
+#include "smu/smu_7_1_1_d.h"
+#include "smu/smu_7_1_1_sh_mask.h"
+#include "cgs_common.h"
+#include "iceland_smc.h"
+
+#define ICELAND_SMC_SIZE               0x20000
+
+static int iceland_start_smc(struct pp_smumgr *smumgr)
+{
+       SMUM_WRITE_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
+                                 SMC_SYSCON_RESET_CNTL, rst_reg, 0);
+
+       return 0;
+}
+
+static void iceland_reset_smc(struct pp_smumgr *smumgr)
+{
+       SMUM_WRITE_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
+                                 SMC_SYSCON_RESET_CNTL,
+                                 rst_reg, 1);
+}
+
+
+static void iceland_stop_smc_clock(struct pp_smumgr *smumgr)
+{
+       SMUM_WRITE_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
+                                 SMC_SYSCON_CLOCK_CNTL_0,
+                                 ck_disable, 1);
+}
+
+static void iceland_start_smc_clock(struct pp_smumgr *smumgr)
+{
+       SMUM_WRITE_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
+                                 SMC_SYSCON_CLOCK_CNTL_0,
+                                 ck_disable, 0);
+}
+
+static int iceland_smu_start_smc(struct pp_smumgr *smumgr)
+{
+       /* set smc instruct start point at 0x0 */
+       smu7_program_jump_on_start(smumgr);
+
+       /* enable smc clock */
+       iceland_start_smc_clock(smumgr);
+
+       /* de-assert reset */
+       iceland_start_smc(smumgr);
+
+       SMUM_WAIT_INDIRECT_FIELD(smumgr, SMC_IND, FIRMWARE_FLAGS,
+                                INTERRUPTS_ENABLED, 1);
+
+       return 0;
+}
+
+
+static int iceland_upload_smc_firmware_data(struct pp_smumgr *smumgr,
+                                       uint32_t length, const uint8_t *src,
+                                       uint32_t limit, uint32_t start_addr)
+{
+       uint32_t byte_count = length;
+       uint32_t data;
+
+       PP_ASSERT_WITH_CODE((limit >= byte_count), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_0, start_addr);
+       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
+
+       while (byte_count >= 4) {
+               data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
+               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data);
+               src += 4;
+               byte_count -= 4;
+       }
+
+       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
+
+       PP_ASSERT_WITH_CODE((0 == byte_count), "SMC size must be dividable by 4.", return -EINVAL);
+
+       return 0;
+}
+
+
+static int iceland_smu_upload_firmware_image(struct pp_smumgr *smumgr)
+{
+       uint32_t val;
+       struct cgs_firmware_info info = {0};
+
+       if (smumgr == NULL || smumgr->device == NULL)
+               return -EINVAL;
+
+       /* load SMC firmware */
+       cgs_get_firmware_info(smumgr->device,
+               smu7_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
+
+       if (info.image_size & 3) {
+               pr_err("[ powerplay ] SMC ucode is not 4 bytes aligned\n");
+               return -EINVAL;
+       }
+
+       if (info.image_size > ICELAND_SMC_SIZE) {
+               pr_err("[ powerplay ] SMC address is beyond the SMC RAM area\n");
+               return -EINVAL;
+       }
+
+       /* wait for smc boot up */
+       SMUM_WAIT_INDIRECT_FIELD_UNEQUAL(smumgr, SMC_IND,
+                                        RCU_UC_EVENTS, boot_seq_done, 0);
+
+       /* clear firmware interrupt enable flag */
+       val = cgs_read_ind_register(smumgr->device, CGS_IND_REG__SMC,
+                                   ixSMC_SYSCON_MISC_CNTL);
+       cgs_write_ind_register(smumgr->device, CGS_IND_REG__SMC,
+                              ixSMC_SYSCON_MISC_CNTL, val | 1);
+
+       /* stop smc clock */
+       iceland_stop_smc_clock(smumgr);
+
+       /* reset smc */
+       iceland_reset_smc(smumgr);
+       iceland_upload_smc_firmware_data(smumgr, info.image_size,
+                               (uint8_t *)info.kptr, ICELAND_SMC_SIZE,
+                               info.ucode_start_address);
+
+       return 0;
+}
+
+static int iceland_request_smu_load_specific_fw(struct pp_smumgr *smumgr,
+                                               uint32_t firmwareType)
+{
+       return 0;
+}
+
+static int iceland_start_smu(struct pp_smumgr *smumgr)
+{
+       int result;
+
+       result = iceland_smu_upload_firmware_image(smumgr);
+       if (result)
+               return result;
+       result = iceland_smu_start_smc(smumgr);
+       if (result)
+               return result;
+
+       if (!smu7_is_smc_ram_running(smumgr)) {
+               printk("smu not running, upload firmware again \n");
+               result = iceland_smu_upload_firmware_image(smumgr);
+               if (result)
+                       return result;
+
+               result = iceland_smu_start_smc(smumgr);
+               if (result)
+                       return result;
+       }
+
+       result = smu7_request_smu_load_fw(smumgr);
+
+       return result;
+}
+
+/**
+ * Write a 32bit value to the SMC SRAM space.
+ * ALL PARAMETERS ARE IN HOST BYTE ORDER.
+ * @param    smumgr  the address of the powerplay hardware manager.
+ * @param    smcAddress the address in the SMC RAM to access.
+ * @param    value to write to the SMC SRAM.
+ */
+static int iceland_smu_init(struct pp_smumgr *smumgr)
+{
+       int i;
+       struct iceland_smumgr *smu_data = (struct iceland_smumgr *)(smumgr->backend);
+       if (smu7_init(smumgr))
+               return -EINVAL;
+
+       for (i = 0; i < SMU71_MAX_LEVELS_GRAPHICS; i++)
+               smu_data->activity_target[i] = 30;
+
+       return 0;
+}
+
+static const struct pp_smumgr_func iceland_smu_funcs = {
+       .smu_init = &iceland_smu_init,
+       .smu_fini = &smu7_smu_fini,
+       .start_smu = &iceland_start_smu,
+       .check_fw_load_finish = &smu7_check_fw_load_finish,
+       .request_smu_load_fw = &smu7_reload_firmware,
+       .request_smu_load_specific_fw = &iceland_request_smu_load_specific_fw,
+       .send_msg_to_smc = &smu7_send_msg_to_smc,
+       .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
+       .download_pptable_settings = NULL,
+       .upload_pptable_settings = NULL,
+       .get_offsetof = iceland_get_offsetof,
+       .process_firmware_header = iceland_process_firmware_header,
+       .init_smc_table = iceland_init_smc_table,
+       .update_sclk_threshold = iceland_update_sclk_threshold,
+       .thermal_setup_fan_table = iceland_thermal_setup_fan_table,
+       .populate_all_graphic_levels = iceland_populate_all_graphic_levels,
+       .populate_all_memory_levels = iceland_populate_all_memory_levels,
+       .get_mac_definition = iceland_get_mac_definition,
+       .initialize_mc_reg_table = iceland_initialize_mc_reg_table,
+       .is_dpm_running = iceland_is_dpm_running,
+};
+
+int iceland_smum_init(struct pp_smumgr *smumgr)
+{
+       struct iceland_smumgr *iceland_smu = NULL;
+
+       iceland_smu = kzalloc(sizeof(struct iceland_smumgr), GFP_KERNEL);
+
+       if (iceland_smu == NULL)
+               return -ENOMEM;
+
+       smumgr->backend = iceland_smu;
+       smumgr->smumgr_funcs = &iceland_smu_funcs;
+
+       return 0;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smumgr.h
new file mode 100644 (file)
index 0000000..8eae01b
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 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.
+ *
+ * Author: Huang Rui <ray.huang@amd.com>
+ *
+ */
+
+#ifndef _ICELAND_SMUMGR_H_
+#define _ICELAND_SMUMGR_H_
+
+
+#include "smu7_smumgr.h"
+#include "pp_endian.h"
+#include "smu71_discrete.h"
+
+struct iceland_pt_defaults {
+       uint8_t   svi_load_line_en;
+       uint8_t   svi_load_line_vddc;
+       uint8_t   tdc_vddc_throttle_release_limit_perc;
+       uint8_t   tdc_mawt;
+       uint8_t   tdc_waterfall_ctl;
+       uint8_t   dte_ambient_temp_base;
+       uint32_t  display_cac;
+       uint32_t  bamp_temp_gradient;
+       uint16_t  bapmti_r[SMU71_DTE_ITERATIONS * SMU71_DTE_SOURCES * SMU71_DTE_SINKS];
+       uint16_t  bapmti_rc[SMU71_DTE_ITERATIONS * SMU71_DTE_SOURCES * SMU71_DTE_SINKS];
+};
+
+struct iceland_mc_reg_entry {
+       uint32_t mclk_max;
+       uint32_t mc_data[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct iceland_mc_reg_table {
+       uint8_t   last;               /* number of registers*/
+       uint8_t   num_entries;        /* number of entries in mc_reg_table_entry used*/
+       uint16_t  validflag;          /* indicate the corresponding register is valid or not. 1: valid, 0: invalid. bit0->address[0], bit1->address[1], etc.*/
+       struct iceland_mc_reg_entry    mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMU71_Discrete_MCRegisterAddress mc_reg_address[SMU71_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct iceland_smumgr {
+       struct smu7_smumgr smu7_data;
+       struct SMU71_Discrete_DpmTable       smc_state_table;
+       struct SMU71_Discrete_PmFuses  power_tune_table;
+       struct SMU71_Discrete_Ulv            ulv_setting;
+       const struct iceland_pt_defaults  *power_tune_defaults;
+       SMU71_Discrete_MCRegisters      mc_regs;
+       struct iceland_mc_reg_table mc_reg_table;
+       uint32_t        activity_target[SMU71_MAX_LEVELS_GRAPHICS];
+};
+
+#endif
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c
new file mode 100644 (file)
index 0000000..4ccc0b7
--- /dev/null
@@ -0,0 +1,2287 @@
+/*
+ * Copyright 2015 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 "polaris10_smc.h"
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "polaris10_smumgr.h"
+#include "pppcielanes.h"
+
+#include "smu_ucode_xfer_vi.h"
+#include "smu74_discrete.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+#include "oss/oss_3_0_d.h"
+#include "gca/gfx_8_0_d.h"
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+#include "polaris10_pwrvirus.h"
+#include "smu7_ppsmc.h"
+#include "smu7_smumgr.h"
+
+#define POLARIS10_SMC_SIZE 0x20000
+#define VOLTAGE_VID_OFFSET_SCALE1   625
+#define VOLTAGE_VID_OFFSET_SCALE2   100
+#define POWERTUNE_DEFAULT_SET_MAX    1
+#define VDDC_VDDCI_DELTA            200
+#define MC_CG_ARB_FREQ_F1           0x0b
+
+static const struct polaris10_pt_defaults polaris10_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+       /* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc, TDC_MAWt,
+        * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac, BAPM_TEMP_GRADIENT */
+       { 1, 0xF, 0xFD, 0x19, 5, 45, 0, 0xB0000,
+       { 0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8, 0xC9, 0xC9, 0x2F, 0x4D, 0x61},
+       { 0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203, 0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4 } },
+};
+
+static const sclkFcwRange_t Range_Table[NUM_SCLK_RANGE] = {
+                       {VCO_2_4, POSTDIV_DIV_BY_16,  75, 160, 112},
+                       {VCO_3_6, POSTDIV_DIV_BY_16, 112, 224, 160},
+                       {VCO_2_4, POSTDIV_DIV_BY_8,   75, 160, 112},
+                       {VCO_3_6, POSTDIV_DIV_BY_8,  112, 224, 160},
+                       {VCO_2_4, POSTDIV_DIV_BY_4,   75, 160, 112},
+                       {VCO_3_6, POSTDIV_DIV_BY_4,  112, 216, 160},
+                       {VCO_2_4, POSTDIV_DIV_BY_2,   75, 160, 108},
+                       {VCO_3_6, POSTDIV_DIV_BY_2,  112, 216, 160} };
+
+static int polaris10_get_dependency_volt_by_clk(struct pp_hwmgr *hwmgr,
+               struct phm_ppt_v1_clock_voltage_dependency_table *dep_table,
+               uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
+{
+       uint32_t i;
+       uint16_t vddci;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       *voltage = *mvdd = 0;
+
+       /* clock - voltage dependency table is empty table */
+       if (dep_table->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < dep_table->count; i++) {
+               /* find first sclk bigger than request */
+               if (dep_table->entries[i].clk >= clock) {
+                       *voltage |= (dep_table->entries[i].vddc *
+                                       VOLTAGE_SCALE) << VDDC_SHIFT;
+                       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+                               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       else if (dep_table->entries[i].vddci)
+                               *voltage |= (dep_table->entries[i].vddci *
+                                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       else {
+                               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               (dep_table->entries[i].vddc -
+                                                               (uint16_t)VDDC_VDDCI_DELTA));
+                               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+                       }
+
+                       if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+                               *mvdd = data->vbios_boot_state.mvdd_bootup_value *
+                                       VOLTAGE_SCALE;
+                       else if (dep_table->entries[i].mvdd)
+                               *mvdd = (uint32_t) dep_table->entries[i].mvdd *
+                                       VOLTAGE_SCALE;
+
+                       *voltage |= 1 << PHASES_SHIFT;
+                       return 0;
+               }
+       }
+
+       /* sclk is bigger than max sclk in the dependence table */
+       *voltage |= (dep_table->entries[i - 1].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->vddci_control)
+               *voltage |= (data->vbios_boot_state.vddci_bootup_value *
+                               VOLTAGE_SCALE) << VDDCI_SHIFT;
+       else if (dep_table->entries[i-1].vddci) {
+               vddci = phm_find_closest_vddci(&(data->vddci_voltage_table),
+                               (dep_table->entries[i].vddc -
+                                               (uint16_t)VDDC_VDDCI_DELTA));
+               *voltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+       }
+
+       if (SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control)
+               *mvdd = data->vbios_boot_state.mvdd_bootup_value * VOLTAGE_SCALE;
+       else if (dep_table->entries[i].mvdd)
+               *mvdd = (uint32_t) dep_table->entries[i - 1].mvdd * VOLTAGE_SCALE;
+
+       return 0;
+}
+
+static uint16_t scale_fan_gain_settings(uint16_t raw_setting)
+{
+       uint32_t tmp;
+       tmp = raw_setting * 4096 / 100;
+       return (uint16_t)tmp;
+}
+
+static int polaris10_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+
+       const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+       SMU74_Discrete_DpmTable  *table = &(smu_data->smc_state_table);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+       struct pp_advance_fan_control_parameters *fan_table =
+                       &hwmgr->thermal_controller.advanceFanControlParameters;
+       int i, j, k;
+       const uint16_t *pdef1;
+       const uint16_t *pdef2;
+
+       table->DefaultTdp = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+       table->TargetTdp  = PP_HOST_TO_SMC_US((uint16_t)(cac_dtp_table->usTDP * 128));
+
+       PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+                               "Target Operating Temp is out of Range!",
+                               );
+
+       table->TemperatureLimitEdge = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTargetOperatingTemp * 256);
+       table->TemperatureLimitHotspot = PP_HOST_TO_SMC_US(
+                       cac_dtp_table->usTemperatureLimitHotspot * 256);
+       table->FanGainEdge = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainEdge));
+       table->FanGainHotspot = PP_HOST_TO_SMC_US(
+                       scale_fan_gain_settings(fan_table->usFanGainHotspot));
+
+       pdef1 = defaults->BAPMTI_R;
+       pdef2 = defaults->BAPMTI_RC;
+
+       for (i = 0; i < SMU74_DTE_ITERATIONS; i++) {
+               for (j = 0; j < SMU74_DTE_SOURCES; j++) {
+                       for (k = 0; k < SMU74_DTE_SINKS; k++) {
+                               table->BAPMTI_R[i][j][k] = PP_HOST_TO_SMC_US(*pdef1);
+                               table->BAPMTI_RC[i][j][k] = PP_HOST_TO_SMC_US(*pdef2);
+                               pdef1++;
+                               pdef2++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int polaris10_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       smu_data->power_tune_table.SviLoadLineEn = defaults->SviLoadLineEn;
+       smu_data->power_tune_table.SviLoadLineVddC = defaults->SviLoadLineVddC;
+       smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+       smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+       return 0;
+}
+
+static int polaris10_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+       uint16_t tdc_limit;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 128);
+       smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+       smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+                       defaults->TDC_VDDC_ThrottleReleaseLimitPerc;
+       smu_data->power_tune_table.TDC_MAWt = defaults->TDC_MAWt;
+
+       return 0;
+}
+
+static int polaris10_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       const struct polaris10_pt_defaults *defaults = smu_data->power_tune_defaults;
+       uint32_t temp;
+
+       if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       fuse_table_offset +
+                       offsetof(SMU74_Discrete_PmFuses, TdcWaterfallCtl),
+                       (uint32_t *)&temp, SMC_RAM_END))
+               PP_ASSERT_WITH_CODE(false,
+                               "Attempt to read PmFuses.DW6 (SviLoadLineEn) from SMC Failed!",
+                               return -EINVAL);
+       else {
+               smu_data->power_tune_table.TdcWaterfallCtl = defaults->TdcWaterfallCtl;
+               smu_data->power_tune_table.LPMLTemperatureMin =
+                               (uint8_t)((temp >> 16) & 0xff);
+               smu_data->power_tune_table.LPMLTemperatureMax =
+                               (uint8_t)((temp >> 8) & 0xff);
+               smu_data->power_tune_table.Reserved = (uint8_t)(temp & 0xff);
+       }
+       return 0;
+}
+
+static int polaris10_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+       return 0;
+}
+
+static int polaris10_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+
+/* TO DO move to hwmgr */
+       if ((hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity & (1 << 15))
+               || 0 == hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity)
+               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity =
+                       hwmgr->thermal_controller.advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+       smu_data->power_tune_table.FuzzyFan_PwmSetDelta = PP_HOST_TO_SMC_US(
+                               hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity);
+       return 0;
+}
+
+static int polaris10_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.GnbLPML[i] = 0;
+
+       return 0;
+}
+
+static int polaris10_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
+{
+       return 0;
+}
+
+static int polaris10_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint16_t hi_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+       uint16_t lo_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+       hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+       lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+       smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
+       smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
+
+       return 0;
+}
+
+static int polaris10_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t pm_fuse_table_offset;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+               if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU7_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU74_Firmware_Header, PmFuseTable),
+                               &pm_fuse_table_offset, SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to get pm_fuse_table_offset Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_populate_svi_load_line(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate SviLoadLine Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_populate_tdc_limit(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TDCLimit Failed!", return -EINVAL);
+
+               if (polaris10_populate_dw8(hwmgr, pm_fuse_table_offset))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TdcWaterfallCtl, "
+                                       "LPMLTemperature Min and Max Failed!",
+                                       return -EINVAL);
+
+               if (0 != polaris10_populate_temperature_scaler(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate LPMLTemperatureScaler Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_populate_fuzzy_fan(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate Fuzzy Fan Control parameters Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_populate_gnb_lpml(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate GnbLPML Min and Max Vid Failed!",
+                                       return -EINVAL);
+
+               if (polaris10_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate BapmVddCBaseLeakage Hi and Lo "
+                                       "Sidd Failed!", return -EINVAL);
+
+               if (smu7_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
+                               (uint8_t *)&smu_data->power_tune_table,
+                               (sizeof(struct SMU74_Discrete_PmFuses) - 92), SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to download PmFuseTable Failed!",
+                                       return -EINVAL);
+       }
+       return 0;
+}
+
+/**
+ * Mvdd table preparation for SMC.
+ *
+ * @param    *hwmgr The address of the hardware manager.
+ * @param    *table The SMC DPM table structure to be populated.
+ * @return   0
+ */
+static int polaris10_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+                       SMU74_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t count, level;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+               count = data->mvdd_voltage_table.count;
+               if (count > SMU_MAX_SMIO_LEVELS)
+                       count = SMU_MAX_SMIO_LEVELS;
+               for (level = 0; level < count; level++) {
+                       table->SmioTable2.Pattern[level].Voltage =
+                               PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[count].value * VOLTAGE_SCALE);
+                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
+                       table->SmioTable2.Pattern[level].Smio =
+                               (uint8_t) level;
+                       table->Smio[level] |=
+                               data->mvdd_voltage_table.entries[level].smio_low;
+               }
+               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
+
+               table->MvddLevelCount = (uint32_t) PP_HOST_TO_SMC_UL(count);
+       }
+
+       return 0;
+}
+
+static int polaris10_populate_smc_vddci_table(struct pp_hwmgr *hwmgr,
+                                       struct SMU74_Discrete_DpmTable *table)
+{
+       uint32_t count, level;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       count = data->vddci_voltage_table.count;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+               if (count > SMU_MAX_SMIO_LEVELS)
+                       count = SMU_MAX_SMIO_LEVELS;
+               for (level = 0; level < count; ++level) {
+                       table->SmioTable1.Pattern[level].Voltage =
+                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[level].value * VOLTAGE_SCALE);
+                       table->SmioTable1.Pattern[level].Smio = (uint8_t) level;
+
+                       table->Smio[level] |= data->vddci_voltage_table.entries[level].smio_low;
+               }
+       }
+
+       table->SmioMask1 = data->vddci_voltage_table.mask_low;
+
+       return 0;
+}
+
+/**
+* Preparation of vddc and vddgfx CAC tables for SMC.
+*
+* @param    hwmgr  the address of the hardware manager
+* @param    table  the SMC DPM table structure to be populated
+* @return   always 0
+*/
+static int polaris10_populate_cac_table(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       uint32_t count;
+       uint8_t index;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_voltage_lookup_table *lookup_table =
+                       table_info->vddc_lookup_table;
+       /* tables is already swapped, so in order to use the value from it,
+        * we need to swap it back.
+        * We are populating vddc CAC data to BapmVddc table
+        * in split and merged mode
+        */
+       for (count = 0; count < lookup_table->count; count++) {
+               index = phm_get_voltage_index(lookup_table,
+                               data->vddc_voltage_table.entries[count].value);
+               table->BapmVddcVidLoSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_low);
+               table->BapmVddcVidHiSidd[count] = convert_to_vid(lookup_table->entries[index].us_cac_mid);
+               table->BapmVddcVidHiSidd2[count] = convert_to_vid(lookup_table->entries[index].us_cac_high);
+       }
+
+       return 0;
+}
+
+/**
+* Preparation of voltage tables for SMC.
+*
+* @param    hwmgr   the address of the hardware manager
+* @param    table   the SMC DPM table structure to be populated
+* @return   always  0
+*/
+
+static int polaris10_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       polaris10_populate_smc_vddci_table(hwmgr, table);
+       polaris10_populate_smc_mvdd_table(hwmgr, table);
+       polaris10_populate_cac_table(hwmgr, table);
+
+       return 0;
+}
+
+static int polaris10_populate_ulv_level(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_Ulv *state)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       state->CcPwrDynRm = 0;
+       state->CcPwrDynRm1 = 0;
+
+       state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+       state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+                       VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+       state->VddcPhase = (data->vddc_phase_shed_control) ? 0 : 1;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+       CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+       return 0;
+}
+
+static int polaris10_populate_ulv_state(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       return polaris10_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int polaris10_populate_smc_link_level(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       int i;
+
+       /* Index (dpm_table->pcie_speed_table.count)
+        * is reserved for PCIE boot level. */
+       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+               table->LinkLevel[i].PcieGenSpeed  =
+                               (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+               table->LinkLevel[i].PcieLaneCount = (uint8_t)encode_pcie_lane_width(
+                               dpm_table->pcie_speed_table.dpm_levels[i].param1);
+               table->LinkLevel[i].EnabledForActivity = 1;
+               table->LinkLevel[i].SPC = (uint8_t)(data->pcie_spc_cap & 0xff);
+               table->LinkLevel[i].DownThreshold = PP_HOST_TO_SMC_UL(5);
+               table->LinkLevel[i].UpThreshold = PP_HOST_TO_SMC_UL(30);
+       }
+
+       smu_data->smc_state_table.LinkLevelCount =
+                       (uint8_t)dpm_table->pcie_speed_table.count;
+
+/* To Do move to hwmgr */
+       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+       return 0;
+}
+
+
+static void polaris10_get_sclk_range_table(struct pp_hwmgr *hwmgr,
+                                  SMU74_Discrete_DpmTable  *table)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       uint32_t i, ref_clk;
+
+       struct pp_atom_ctrl_sclk_range_table range_table_from_vbios = { { {0} } };
+
+       ref_clk = smu7_get_xclk(hwmgr);
+
+       if (0 == atomctrl_get_smc_sclk_range_table(hwmgr, &range_table_from_vbios)) {
+               for (i = 0; i < NUM_SCLK_RANGE; i++) {
+                       table->SclkFcwRangeTable[i].vco_setting = range_table_from_vbios.entry[i].ucVco_setting;
+                       table->SclkFcwRangeTable[i].postdiv = range_table_from_vbios.entry[i].ucPostdiv;
+                       table->SclkFcwRangeTable[i].fcw_pcc = range_table_from_vbios.entry[i].usFcw_pcc;
+
+                       table->SclkFcwRangeTable[i].fcw_trans_upper = range_table_from_vbios.entry[i].usFcw_trans_upper;
+                       table->SclkFcwRangeTable[i].fcw_trans_lower = range_table_from_vbios.entry[i].usRcw_trans_lower;
+
+                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+                       CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+               }
+               return;
+       }
+
+       for (i = 0; i < NUM_SCLK_RANGE; i++) {
+               smu_data->range_table[i].trans_lower_frequency = (ref_clk * Range_Table[i].fcw_trans_lower) >> Range_Table[i].postdiv;
+               smu_data->range_table[i].trans_upper_frequency = (ref_clk * Range_Table[i].fcw_trans_upper) >> Range_Table[i].postdiv;
+
+               table->SclkFcwRangeTable[i].vco_setting = Range_Table[i].vco_setting;
+               table->SclkFcwRangeTable[i].postdiv = Range_Table[i].postdiv;
+               table->SclkFcwRangeTable[i].fcw_pcc = Range_Table[i].fcw_pcc;
+
+               table->SclkFcwRangeTable[i].fcw_trans_upper = Range_Table[i].fcw_trans_upper;
+               table->SclkFcwRangeTable[i].fcw_trans_lower = Range_Table[i].fcw_trans_lower;
+
+               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_pcc);
+               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_upper);
+               CONVERT_FROM_HOST_TO_SMC_US(table->SclkFcwRangeTable[i].fcw_trans_lower);
+       }
+}
+
+/**
+* Calculates the SCLK dividers using the provided engine clock
+*
+* @param    hwmgr  the address of the hardware manager
+* @param    clock  the engine clock to use to populate the structure
+* @param    sclk   the SMC SCLK structure to be populated
+*/
+static int polaris10_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+               uint32_t clock, SMU_SclkSetting *sclk_setting)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       const SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+       struct pp_atomctrl_clock_dividers_ai dividers;
+       uint32_t ref_clock;
+       uint32_t pcc_target_percent, pcc_target_freq, ss_target_percent, ss_target_freq;
+       uint8_t i;
+       int result;
+       uint64_t temp;
+
+       sclk_setting->SclkFrequency = clock;
+       /* get the engine clock dividers for this clock value */
+       result = atomctrl_get_engine_pll_dividers_ai(hwmgr, clock,  &dividers);
+       if (result == 0) {
+               sclk_setting->Fcw_int = dividers.usSclk_fcw_int;
+               sclk_setting->Fcw_frac = dividers.usSclk_fcw_frac;
+               sclk_setting->Pcc_fcw_int = dividers.usPcc_fcw_int;
+               sclk_setting->PllRange = dividers.ucSclkPllRange;
+               sclk_setting->Sclk_slew_rate = 0x400;
+               sclk_setting->Pcc_up_slew_rate = dividers.usPcc_fcw_slew_frac;
+               sclk_setting->Pcc_down_slew_rate = 0xffff;
+               sclk_setting->SSc_En = dividers.ucSscEnable;
+               sclk_setting->Fcw1_int = dividers.usSsc_fcw1_int;
+               sclk_setting->Fcw1_frac = dividers.usSsc_fcw1_frac;
+               sclk_setting->Sclk_ss_slew_rate = dividers.usSsc_fcw_slew_frac;
+               return result;
+       }
+
+       ref_clock = smu7_get_xclk(hwmgr);
+
+       for (i = 0; i < NUM_SCLK_RANGE; i++) {
+               if (clock > smu_data->range_table[i].trans_lower_frequency
+               && clock <= smu_data->range_table[i].trans_upper_frequency) {
+                       sclk_setting->PllRange = i;
+                       break;
+               }
+       }
+
+       sclk_setting->Fcw_int = (uint16_t)((clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+       temp = clock << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+       temp <<= 0x10;
+       do_div(temp, ref_clock);
+       sclk_setting->Fcw_frac = temp & 0xffff;
+
+       pcc_target_percent = 10; /*  Hardcode 10% for now. */
+       pcc_target_freq = clock - (clock * pcc_target_percent / 100);
+       sclk_setting->Pcc_fcw_int = (uint16_t)((pcc_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+
+       ss_target_percent = 2; /*  Hardcode 2% for now. */
+       sclk_setting->SSc_En = 0;
+       if (ss_target_percent) {
+               sclk_setting->SSc_En = 1;
+               ss_target_freq = clock - (clock * ss_target_percent / 100);
+               sclk_setting->Fcw1_int = (uint16_t)((ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv) / ref_clock);
+               temp = ss_target_freq << table->SclkFcwRangeTable[sclk_setting->PllRange].postdiv;
+               temp <<= 0x10;
+               do_div(temp, ref_clock);
+               sclk_setting->Fcw1_frac = temp & 0xffff;
+       }
+
+       return 0;
+}
+
+/**
+* Populates single SMC SCLK structure using the provided engine clock
+*
+* @param    hwmgr      the address of the hardware manager
+* @param    clock the engine clock to use to populate the structure
+* @param    sclk        the SMC SCLK structure to be populated
+*/
+
+static int polaris10_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+               uint32_t clock, uint16_t sclk_al_threshold,
+               struct SMU74_Discrete_GraphicsLevel *level)
+{
+       int result;
+       /* PP_Clocks minClocks; */
+       uint32_t mvdd;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       SMU_SclkSetting curr_sclk_setting = { 0 };
+
+       result = polaris10_calculate_sclk_params(hwmgr, clock, &curr_sclk_setting);
+
+       /* populate graphics levels */
+       result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_sclk, clock,
+                       &level->MinVoltage, &mvdd);
+
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "can not find VDDC voltage value for "
+                       "VDDC engine clock dependency table",
+                       return result);
+       level->ActivityLevel = sclk_al_threshold;
+
+       level->CcPwrDynRm = 0;
+       level->CcPwrDynRm1 = 0;
+       level->EnabledForActivity = 0;
+       level->EnabledForThrottle = 1;
+       level->UpHyst = 10;
+       level->DownHyst = 0;
+       level->VoltageDownHyst = 0;
+       level->PowerThrottle = 0;
+       data->display_timing.min_clock_in_sr = hwmgr->display_config.min_core_set_clock_in_sr;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_SclkDeepSleep))
+               level->DeepSleepDivId = smu7_get_sleep_divider_id_from_clock(clock,
+                                                               hwmgr->display_config.min_core_set_clock_in_sr);
+
+       /* Default to slow, highest DPM level will be
+        * set to PPSMC_DISPLAY_WATERMARK_LOW later.
+        */
+       if (data->update_up_hyst)
+               level->UpHyst = (uint8_t)data->up_hyst;
+       if (data->update_down_hyst)
+               level->DownHyst = (uint8_t)data->down_hyst;
+
+       level->SclkSetting = curr_sclk_setting;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(level->MinVoltage);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->CcPwrDynRm1);
+       CONVERT_FROM_HOST_TO_SMC_US(level->ActivityLevel);
+       CONVERT_FROM_HOST_TO_SMC_UL(level->SclkSetting.SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_int);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw_frac);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_fcw_int);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_up_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Pcc_down_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_int);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Fcw1_frac);
+       CONVERT_FROM_HOST_TO_SMC_US(level->SclkSetting.Sclk_ss_slew_rate);
+       return 0;
+}
+
+/**
+* Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
+*
+* @param    hwmgr      the address of the hardware manager
+*/
+int polaris10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+       uint8_t pcie_entry_cnt = (uint8_t) hw_data->dpm_table.pcie_speed_table.count;
+       int result = 0;
+       uint32_t array = smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
+       uint32_t array_size = sizeof(struct SMU74_Discrete_GraphicsLevel) *
+                       SMU74_MAX_LEVELS_GRAPHICS;
+       struct SMU74_Discrete_GraphicsLevel *levels =
+                       smu_data->smc_state_table.GraphicsLevel;
+       uint32_t i, max_entry;
+       uint8_t hightest_pcie_level_enabled = 0,
+               lowest_pcie_level_enabled = 0,
+               mid_pcie_level_enabled = 0,
+               count = 0;
+
+       polaris10_get_sclk_range_table(hwmgr, &(smu_data->smc_state_table));
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+
+               result = polaris10_populate_single_graphic_level(hwmgr,
+                               dpm_table->sclk_table.dpm_levels[i].value,
+                               (uint16_t)smu_data->activity_target[i],
+                               &(smu_data->smc_state_table.GraphicsLevel[i]));
+               if (result)
+                       return result;
+
+               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+               if (i > 1)
+                       levels[i].DeepSleepDivId = 0;
+       }
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_SPLLShutdownSupport))
+               smu_data->smc_state_table.GraphicsLevel[0].SclkSetting.SSc_En = 0;
+
+       smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+       smu_data->smc_state_table.GraphicsDpmLevelCount =
+                       (uint8_t)dpm_table->sclk_table.count;
+       hw_data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+
+       if (pcie_table != NULL) {
+               PP_ASSERT_WITH_CODE((1 <= pcie_entry_cnt),
+                               "There must be 1 or more PCIE levels defined in PPTable.",
+                               return -EINVAL);
+               max_entry = pcie_entry_cnt - 1;
+               for (i = 0; i < dpm_table->sclk_table.count; i++)
+                       levels[i].pcieDpmLevel =
+                                       (uint8_t) ((i < max_entry) ? i : max_entry);
+       } else {
+               while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << (hightest_pcie_level_enabled + 1))) != 0))
+                       hightest_pcie_level_enabled++;
+
+               while (hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << lowest_pcie_level_enabled)) == 0))
+                       lowest_pcie_level_enabled++;
+
+               while ((count < hightest_pcie_level_enabled) &&
+                               ((hw_data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                               (1 << (lowest_pcie_level_enabled + 1 + count))) == 0))
+                       count++;
+
+               mid_pcie_level_enabled = (lowest_pcie_level_enabled + 1 + count) <
+                               hightest_pcie_level_enabled ?
+                                               (lowest_pcie_level_enabled + 1 + count) :
+                                               hightest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to hightest_pcie_level_enabled */
+               for (i = 2; i < dpm_table->sclk_table.count; i++)
+                       levels[i].pcieDpmLevel = hightest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to lowest_pcie_level_enabled */
+               levels[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to mid_pcie_level_enabled */
+               levels[1].pcieDpmLevel = mid_pcie_level_enabled;
+       }
+       /* level count will send to smc once at init smc table and never change */
+       result = smu7_copy_bytes_to_smc(smumgr, array, (uint8_t *)levels,
+                       (uint32_t)array_size, SMC_RAM_END);
+
+       return result;
+}
+
+
+static int polaris10_populate_single_memory_level(struct pp_hwmgr *hwmgr,
+               uint32_t clock, struct SMU74_Discrete_MemoryLevel *mem_level)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       int result = 0;
+       struct cgs_display_info info = {0, 0, NULL};
+       uint32_t mclk_stutter_mode_threshold = 40000;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+
+       if (table_info->vdd_dep_on_mclk) {
+               result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                               table_info->vdd_dep_on_mclk, clock,
+                               &mem_level->MinVoltage, &mem_level->MinMvdd);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find MinVddc voltage value from memory "
+                               "VDDC voltage dependency table", return result);
+       }
+
+       mem_level->MclkFrequency = clock;
+       mem_level->EnabledForThrottle = 1;
+       mem_level->EnabledForActivity = 0;
+       mem_level->UpHyst = 0;
+       mem_level->DownHyst = 100;
+       mem_level->VoltageDownHyst = 0;
+       mem_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
+       mem_level->StutterEnable = false;
+       mem_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       data->display_timing.num_existing_displays = info.display_count;
+
+       if (mclk_stutter_mode_threshold &&
+               (clock <= mclk_stutter_mode_threshold) &&
+               (SMUM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL,
+                               STUTTER_ENABLE) & 0x1))
+               mem_level->StutterEnable = true;
+
+       if (!result) {
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinMvdd);
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_US(mem_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(mem_level->MinVoltage);
+       }
+       return result;
+}
+
+/**
+* Populates all SMC MCLK levels' structure based on the trimmed allowed dpm memory clock states
+*
+* @param    hwmgr      the address of the hardware manager
+*/
+int polaris10_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &hw_data->dpm_table;
+       int result;
+       /* populate MCLK dpm table to SMU7 */
+       uint32_t array = smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
+       uint32_t array_size = sizeof(SMU74_Discrete_MemoryLevel) *
+                       SMU74_MAX_LEVELS_MEMORY;
+       struct SMU74_Discrete_MemoryLevel *levels =
+                       smu_data->smc_state_table.MemoryLevel;
+       uint32_t i;
+
+       for (i = 0; i < dpm_table->mclk_table.count; i++) {
+               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+                               "can not populate memory level as memory clock is zero",
+                               return -EINVAL);
+               result = polaris10_populate_single_memory_level(hwmgr,
+                               dpm_table->mclk_table.dpm_levels[i].value,
+                               &levels[i]);
+               if (i == dpm_table->mclk_table.count - 1) {
+                       levels[i].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+                       levels[i].EnabledForActivity = 1;
+               }
+               if (result)
+                       return result;
+       }
+
+       /* In order to prevent MC activity from stutter mode to push DPM up,
+        * the UVD change complements this by putting the MCLK in
+        * a higher state by default such that we are not affected by
+        * up threshold or and MCLK DPM latency.
+        */
+       levels[0].ActivityLevel = 0x1f;
+       CONVERT_FROM_HOST_TO_SMC_US(levels[0].ActivityLevel);
+
+       smu_data->smc_state_table.MemoryDpmLevelCount =
+                       (uint8_t)dpm_table->mclk_table.count;
+       hw_data->dpm_level_enable_mask.mclk_dpm_enable_mask =
+                       phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+
+       /* level count will send to smc once at init smc table and never change */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, array, (uint8_t *)levels,
+                       (uint32_t)array_size, SMC_RAM_END);
+
+       return result;
+}
+
+/**
+* Populates the SMC MVDD structure using the provided memory clock.
+*
+* @param    hwmgr      the address of the hardware manager
+* @param    mclk        the MCLK value to be used in the decision if MVDD should be high or low.
+* @param    voltage     the SMC VOLTAGE structure to be populated
+*/
+static int polaris10_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+               uint32_t mclk, SMIO_Pattern *smio_pat)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint32_t i = 0;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+               /* find mvdd value which clock is more than request */
+               for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+                       if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+                               smio_pat->Voltage = data->mvdd_voltage_table.entries[i].value;
+                               break;
+                       }
+               }
+               PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+                               "MVDD Voltage is outside the supported range.",
+                               return -EINVAL);
+       } else
+               return -EINVAL;
+
+       return 0;
+}
+
+static int polaris10_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+               SMU74_Discrete_DpmTable *table)
+{
+       int result = 0;
+       uint32_t sclk_frequency;
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       SMIO_Pattern vol_level;
+       uint32_t mvdd;
+       uint16_t us_mvdd;
+
+       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       /* Get MinVoltage and Frequency from DPM0,
+        * already converted to SMC_UL */
+       sclk_frequency = data->vbios_boot_state.sclk_bootup_value;
+       result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_sclk,
+                       sclk_frequency,
+                       &table->ACPILevel.MinVoltage, &mvdd);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "Cannot find ACPI VDDC voltage value "
+                       "in Clock Dependency Table",
+                       );
+
+       result = polaris10_calculate_sclk_params(hwmgr, sclk_frequency,  &(table->ACPILevel.SclkSetting));
+       PP_ASSERT_WITH_CODE(result == 0, "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+       table->ACPILevel.DeepSleepDivId = 0;
+       table->ACPILevel.CcPwrDynRm = 0;
+       table->ACPILevel.CcPwrDynRm1 = 0;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.MinVoltage);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkSetting.SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_int);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw_frac);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_fcw_int);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_up_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Pcc_down_slew_rate);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_int);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Fcw1_frac);
+       CONVERT_FROM_HOST_TO_SMC_US(table->ACPILevel.SclkSetting.Sclk_ss_slew_rate);
+
+
+       /* Get MinVoltage and Frequency from DPM0, already converted to SMC_UL */
+       table->MemoryACPILevel.MclkFrequency = data->vbios_boot_state.mclk_bootup_value;
+       result = polaris10_get_dependency_volt_by_clk(hwmgr,
+                       table_info->vdd_dep_on_mclk,
+                       table->MemoryACPILevel.MclkFrequency,
+                       &table->MemoryACPILevel.MinVoltage, &mvdd);
+       PP_ASSERT_WITH_CODE((0 == result),
+                       "Cannot find ACPI VDDCI voltage value "
+                       "in Clock Dependency Table",
+                       );
+
+       us_mvdd = 0;
+       if ((SMU7_VOLTAGE_CONTROL_NONE == data->mvdd_control) ||
+                       (data->mclk_dpm_key_disabled))
+               us_mvdd = data->vbios_boot_state.mvdd_bootup_value;
+       else {
+               if (!polaris10_populate_mvdd_value(hwmgr,
+                               data->dpm_table.mclk_table.dpm_levels[0].value,
+                               &vol_level))
+                       us_mvdd = vol_level.Voltage;
+       }
+
+       if (0 == polaris10_populate_mvdd_value(hwmgr, 0, &vol_level))
+               table->MemoryACPILevel.MinMvdd = PP_HOST_TO_SMC_UL(vol_level.Voltage);
+       else
+               table->MemoryACPILevel.MinMvdd = 0;
+
+       table->MemoryACPILevel.StutterEnable = false;
+
+       table->MemoryACPILevel.EnabledForThrottle = 0;
+       table->MemoryACPILevel.EnabledForActivity = 0;
+       table->MemoryACPILevel.UpHyst = 0;
+       table->MemoryACPILevel.DownHyst = 100;
+       table->MemoryACPILevel.VoltageDownHyst = 0;
+       table->MemoryACPILevel.ActivityLevel =
+                       PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);
+
+       return result;
+}
+
+static int polaris10_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+               SMU74_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
+
+       table->VceLevelCount = (uint8_t)(mm_table->count);
+       table->VceBootLevel = 0;
+
+       for (count = 0; count < table->VceLevelCount; count++) {
+               table->VceLevel[count].Frequency = mm_table->entries[count].eclk;
+               table->VceLevel[count].MinVoltage = 0;
+               table->VceLevel[count].MinVoltage |=
+                               (mm_table->entries[count].vddc * VOLTAGE_SCALE) << VDDC_SHIFT;
+
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+
+               table->VceLevel[count].MinVoltage |=
+                               (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->VceLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /*retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->VceLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for VCE engine clock",
+                               return result);
+
+               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].MinVoltage);
+       }
+       return result;
+}
+
+
+static int polaris10_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
+               SMU74_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
+
+       table->SamuBootLevel = 0;
+       table->SamuLevelCount = (uint8_t)(mm_table->count);
+
+       for (count = 0; count < table->SamuLevelCount; count++) {
+               /* not sure whether we need evclk or not */
+               table->SamuLevel[count].MinVoltage = 0;
+               table->SamuLevel[count].Frequency = mm_table->entries[count].samclock;
+               table->SamuLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+                               VOLTAGE_SCALE) << VDDC_SHIFT;
+
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               table->SamuLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->SamuLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->SamuLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for samu clock", return result);
+
+               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].MinVoltage);
+       }
+       return result;
+}
+
+static int polaris10_populate_memory_timing_parameters(struct pp_hwmgr *hwmgr,
+               int32_t eng_clock, int32_t mem_clock,
+               SMU74_Discrete_MCArbDramTimingTableEntry *arb_regs)
+{
+       uint32_t dram_timing;
+       uint32_t dram_timing2;
+       uint32_t burst_time;
+       int result;
+
+       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+                       eng_clock, mem_clock);
+       PP_ASSERT_WITH_CODE(result == 0,
+                       "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+       dram_timing = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+       dram_timing2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+       burst_time = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+
+       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dram_timing);
+       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dram_timing2);
+       arb_regs->McArbBurstTime   = (uint8_t)burst_time;
+
+       return 0;
+}
+
+static int polaris10_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct SMU74_Discrete_MCArbDramTimingTable arb_regs;
+       uint32_t i, j;
+       int result = 0;
+
+       for (i = 0; i < hw_data->dpm_table.sclk_table.count; i++) {
+               for (j = 0; j < hw_data->dpm_table.mclk_table.count; j++) {
+                       result = polaris10_populate_memory_timing_parameters(hwmgr,
+                                       hw_data->dpm_table.sclk_table.dpm_levels[i].value,
+                                       hw_data->dpm_table.mclk_table.dpm_levels[j].value,
+                                       &arb_regs.entries[i][j]);
+                       if (result == 0)
+                               result = atomctrl_set_ac_timing_ai(hwmgr, hw_data->dpm_table.mclk_table.dpm_levels[j].value, j);
+                       if (result != 0)
+                               return result;
+               }
+       }
+
+       result = smu7_copy_bytes_to_smc(
+                       hwmgr->smumgr,
+                       smu_data->smu7_data.arb_table_start,
+                       (uint8_t *)&arb_regs,
+                       sizeof(SMU74_Discrete_MCArbDramTimingTable),
+                       SMC_RAM_END);
+       return result;
+}
+
+static int polaris10_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       int result = -EINVAL;
+       uint8_t count;
+       struct pp_atomctrl_clock_dividers_vi dividers;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                       table_info->mm_dep_table;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t vddci;
+
+       table->UvdLevelCount = (uint8_t)(mm_table->count);
+       table->UvdBootLevel = 0;
+
+       for (count = 0; count < table->UvdLevelCount; count++) {
+               table->UvdLevel[count].MinVoltage = 0;
+               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+               table->UvdLevel[count].MinVoltage |= (mm_table->entries[count].vddc *
+                               VOLTAGE_SCALE) << VDDC_SHIFT;
+
+               if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control)
+                       vddci = (uint32_t)phm_find_closest_vddci(&(data->vddci_voltage_table),
+                                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               else if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control)
+                       vddci = mm_table->entries[count].vddc - VDDC_VDDCI_DELTA;
+               else
+                       vddci = (data->vbios_boot_state.vddci_bootup_value * VOLTAGE_SCALE) << VDDCI_SHIFT;
+
+               table->UvdLevel[count].MinVoltage |= (vddci * VOLTAGE_SCALE) << VDDCI_SHIFT;
+               table->UvdLevel[count].MinVoltage |= 1 << PHASES_SHIFT;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->UvdLevel[count].VclkFrequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for Vclk clock", return result);
+
+               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                               table->UvdLevel[count].DclkFrequency, &dividers);
+               PP_ASSERT_WITH_CODE((0 == result),
+                               "can not find divide id for Dclk clock", return result);
+
+               table->UvdLevel[count].DclkDivider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].MinVoltage);
+       }
+
+       return result;
+}
+
+static int polaris10_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       table->GraphicsBootLevel = 0;
+       table->MemoryBootLevel = 0;
+
+       /* find boot level from dpm table */
+       result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+                       data->vbios_boot_state.sclk_bootup_value,
+                       (uint32_t *)&(table->GraphicsBootLevel));
+
+       result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+                       data->vbios_boot_state.mclk_bootup_value,
+                       (uint32_t *)&(table->MemoryBootLevel));
+
+       table->BootVddc  = data->vbios_boot_state.vddc_bootup_value *
+                       VOLTAGE_SCALE;
+       table->BootVddci = data->vbios_boot_state.vddci_bootup_value *
+                       VOLTAGE_SCALE;
+       table->BootMVdd  = data->vbios_boot_state.mvdd_bootup_value *
+                       VOLTAGE_SCALE;
+
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddc);
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootVddci);
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+       return 0;
+}
+
+static int polaris10_populate_smc_initailial_state(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint8_t count, level;
+
+       count = (uint8_t)(table_info->vdd_dep_on_sclk->count);
+
+       for (level = 0; level < count; level++) {
+               if (table_info->vdd_dep_on_sclk->entries[level].clk >=
+                               hw_data->vbios_boot_state.sclk_bootup_value) {
+                       smu_data->smc_state_table.GraphicsBootLevel = level;
+                       break;
+               }
+       }
+
+       count = (uint8_t)(table_info->vdd_dep_on_mclk->count);
+       for (level = 0; level < count; level++) {
+               if (table_info->vdd_dep_on_mclk->entries[level].clk >=
+                               hw_data->vbios_boot_state.mclk_bootup_value) {
+                       smu_data->smc_state_table.MemoryBootLevel = level;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+
+static int polaris10_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+       uint32_t ro, efuse, volt_without_cks, volt_with_cks, value, max, min;
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+
+       uint8_t i, stretch_amount, stretch_amount2, volt_offset = 0;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+                       table_info->vdd_dep_on_sclk;
+
+       stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+       /* Read SMU_Eefuse to read and calculate RO and determine
+        * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+        */
+       efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixSMU_EFUSE_0 + (67 * 4));
+       efuse &= 0xFF000000;
+       efuse = efuse >> 24;
+
+       if (hwmgr->chip_id == CHIP_POLARIS10) {
+               min = 1000;
+               max = 2300;
+       } else {
+               min = 1100;
+               max = 2100;
+       }
+
+       ro = efuse * (max - min) / 255 + min;
+
+       /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+       for (i = 0; i < sclk_table->count; i++) {
+               smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+                               sclk_table->entries[i].cks_enable << i;
+               if (hwmgr->chip_id == CHIP_POLARIS10) {
+                       volt_without_cks = (uint32_t)((2753594000U + (sclk_table->entries[i].clk/100) * 136418 - (ro - 70) * 1000000) / \
+                                               (2424180 - (sclk_table->entries[i].clk/100) * 1132925/1000));
+                       volt_with_cks = (uint32_t)((2797202000U + sclk_table->entries[i].clk/100 * 3232 - (ro - 65) * 1000000) / \
+                                       (2522480 - sclk_table->entries[i].clk/100 * 115764/100));
+               } else {
+                       volt_without_cks = (uint32_t)((2416794800U + (sclk_table->entries[i].clk/100) * 1476925/10 - (ro - 50) * 1000000) / \
+                                               (2625416 - (sclk_table->entries[i].clk/100) * (12586807/10000)));
+                       volt_with_cks = (uint32_t)((2999656000U - sclk_table->entries[i].clk/100 * 392803 - (ro - 44) * 1000000) / \
+                                       (3422454 - sclk_table->entries[i].clk/100 * (18886376/10000)));
+               }
+
+               if (volt_without_cks >= volt_with_cks)
+                       volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+                                       sclk_table->entries[i].cks_voffset) * 100 + 624) / 625);
+
+               smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+       }
+
+       smu_data->smc_state_table.LdoRefSel = (table_info->cac_dtp_table->ucCKS_LDO_REFSEL != 0) ? table_info->cac_dtp_table->ucCKS_LDO_REFSEL : 6;
+       /* Populate CKS Lookup Table */
+       if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
+               stretch_amount2 = 0;
+       else if (stretch_amount == 3 || stretch_amount == 4)
+               stretch_amount2 = 1;
+       else {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ClockStretcher);
+               PP_ASSERT_WITH_CODE(false,
+                               "Stretch Amount in PPTable not supported\n",
+                               return -EINVAL);
+       }
+
+       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL);
+       value &= 0xFFFFFFFE;
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, ixPWR_CKS_CNTL, value);
+
+       return 0;
+}
+
+/**
+* Populates the SMC VRConfig field in DPM table.
+*
+* @param    hwmgr   the address of the hardware manager
+* @param    table   the SMC DPM table structure to be populated
+* @return   always 0
+*/
+static int polaris10_populate_vr_config(struct pp_hwmgr *hwmgr,
+               struct SMU74_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint16_t config;
+
+       config = VR_MERGED_WITH_VDDC;
+       table->VRConfig |= (config << VRCONF_VDDGFX_SHIFT);
+
+       /* Set Vddc Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+               config = VR_SVI2_PLANE_1;
+               table->VRConfig |= config;
+       } else {
+               PP_ASSERT_WITH_CODE(false,
+                               "VDDC should be on SVI2 control in merged mode!",
+                               );
+       }
+       /* Set Vddci Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+               config = VR_SVI2_PLANE_2;  /* only in merged mode */
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+               config = VR_SMIO_PATTERN_1;
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       } else {
+               config = VR_STATIC_VOLTAGE;
+               table->VRConfig |= (config << VRCONF_VDDCI_SHIFT);
+       }
+       /* Set Mvdd Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->mvdd_control) {
+               config = VR_SVI2_PLANE_2;
+               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC, smu_data->smu7_data.soft_regs_start +
+                       offsetof(SMU74_SoftRegisters, AllowMvddSwitch), 0x1);
+       } else {
+               config = VR_STATIC_VOLTAGE;
+               table->VRConfig |= (config << VRCONF_MVDD_SHIFT);
+       }
+
+       return 0;
+}
+
+
+static int polaris10_populate_avfs_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+
+       SMU74_Discrete_DpmTable  *table = &(smu_data->smc_state_table);
+       int result = 0;
+       struct pp_atom_ctrl__avfs_parameters avfs_params = {0};
+       AVFS_meanNsigma_t AVFS_meanNsigma = { {0} };
+       AVFS_Sclk_Offset_t AVFS_SclkOffset = { {0} };
+       uint32_t tmp, i;
+
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)hwmgr->pptable;
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+                       table_info->vdd_dep_on_sclk;
+
+
+       if (smu_data->avfs.avfs_btc_status == AVFS_BTC_NOTSUPPORTED)
+               return result;
+
+       result = atomctrl_get_avfs_information(hwmgr, &avfs_params);
+
+       if (0 == result) {
+               table->BTCGB_VDROOP_TABLE[0].a0  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a0);
+               table->BTCGB_VDROOP_TABLE[0].a1  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a1);
+               table->BTCGB_VDROOP_TABLE[0].a2  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSON_a2);
+               table->BTCGB_VDROOP_TABLE[1].a0  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a0);
+               table->BTCGB_VDROOP_TABLE[1].a1  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a1);
+               table->BTCGB_VDROOP_TABLE[1].a2  = PP_HOST_TO_SMC_UL(avfs_params.ulGB_VDROOP_TABLE_CKSOFF_a2);
+               table->AVFSGB_VDROOP_TABLE[0].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_m1);
+               table->AVFSGB_VDROOP_TABLE[0].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSON_m2);
+               table->AVFSGB_VDROOP_TABLE[0].b  = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSON_b);
+               table->AVFSGB_VDROOP_TABLE[0].m1_shift = 24;
+               table->AVFSGB_VDROOP_TABLE[0].m2_shift  = 12;
+               table->AVFSGB_VDROOP_TABLE[1].m1 = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_m1);
+               table->AVFSGB_VDROOP_TABLE[1].m2 = PP_HOST_TO_SMC_US(avfs_params.usAVFSGB_FUSE_TABLE_CKSOFF_m2);
+               table->AVFSGB_VDROOP_TABLE[1].b  = PP_HOST_TO_SMC_UL(avfs_params.ulAVFSGB_FUSE_TABLE_CKSOFF_b);
+               table->AVFSGB_VDROOP_TABLE[1].m1_shift = 24;
+               table->AVFSGB_VDROOP_TABLE[1].m2_shift  = 12;
+               table->MaxVoltage                = PP_HOST_TO_SMC_US(avfs_params.usMaxVoltage_0_25mv);
+               AVFS_meanNsigma.Aconstant[0]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant0);
+               AVFS_meanNsigma.Aconstant[1]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant1);
+               AVFS_meanNsigma.Aconstant[2]      = PP_HOST_TO_SMC_UL(avfs_params.ulAVFS_meanNsigma_Acontant2);
+               AVFS_meanNsigma.DC_tol_sigma      = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_DC_tol_sigma);
+               AVFS_meanNsigma.Platform_mean     = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_mean);
+               AVFS_meanNsigma.PSM_Age_CompFactor = PP_HOST_TO_SMC_US(avfs_params.usPSM_Age_ComFactor);
+               AVFS_meanNsigma.Platform_sigma     = PP_HOST_TO_SMC_US(avfs_params.usAVFS_meanNsigma_Platform_sigma);
+
+               for (i = 0; i < NUM_VFT_COLUMNS; i++) {
+                       AVFS_meanNsigma.Static_Voltage_Offset[i] = (uint8_t)(sclk_table->entries[i].cks_voffset * 100 / 625);
+                       AVFS_SclkOffset.Sclk_Offset[i] = PP_HOST_TO_SMC_US((uint16_t)(sclk_table->entries[i].sclk_offset) / 100);
+               }
+
+               result = smu7_read_smc_sram_dword(smumgr,
+                               SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsMeanNSigma),
+                               &tmp, SMC_RAM_END);
+
+               smu7_copy_bytes_to_smc(smumgr,
+                                       tmp,
+                                       (uint8_t *)&AVFS_meanNsigma,
+                                       sizeof(AVFS_meanNsigma_t),
+                                       SMC_RAM_END);
+
+               result = smu7_read_smc_sram_dword(smumgr,
+                               SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, AvfsSclkOffsetTable),
+                               &tmp, SMC_RAM_END);
+               smu7_copy_bytes_to_smc(smumgr,
+                                       tmp,
+                                       (uint8_t *)&AVFS_SclkOffset,
+                                       sizeof(AVFS_Sclk_Offset_t),
+                                       SMC_RAM_END);
+
+               data->avfs_vdroop_override_setting = (avfs_params.ucEnableGB_VDROOP_TABLE_CKSON << BTCGB0_Vdroop_Enable_SHIFT) |
+                                               (avfs_params.ucEnableGB_VDROOP_TABLE_CKSOFF << BTCGB1_Vdroop_Enable_SHIFT) |
+                                               (avfs_params.ucEnableGB_FUSE_TABLE_CKSON << AVFSGB0_Vdroop_Enable_SHIFT) |
+                                               (avfs_params.ucEnableGB_FUSE_TABLE_CKSOFF << AVFSGB1_Vdroop_Enable_SHIFT);
+               data->apply_avfs_cks_off_voltage = (avfs_params.ucEnableApplyAVFS_CKS_OFF_Voltage == 1) ? true : false;
+       }
+       return result;
+}
+
+
+/**
+* Initialize the ARB DRAM timing table's index field.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+static int polaris10_init_arb_table_index(struct pp_smumgr *smumgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       uint32_t tmp;
+       int result;
+
+       /* This is a read-modify-write on the first byte of the ARB table.
+        * The first byte in the SMU73_Discrete_MCArbDramTimingTable structure
+        * is the field 'current'.
+        * This solution is ugly, but we never write the whole table only
+        * individual fields in it.
+        * In reality this field should not be in that structure
+        * but in a soft register.
+        */
+       result = smu7_read_smc_sram_dword(smumgr,
+                       smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+       if (result)
+               return result;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+       return smu7_write_smc_sram_dword(smumgr,
+                       smu_data->smu7_data.arb_table_start, tmp, SMC_RAM_END);
+}
+
+static void polaris10_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       struct  phm_ppt_v1_information *table_info =
+                       (struct  phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (table_info &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID)
+               smu_data->power_tune_defaults =
+                               &polaris10_power_tune_data_set_array
+                               [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+       else
+               smu_data->power_tune_defaults = &polaris10_power_tune_data_set_array[0];
+
+}
+
+/**
+* Initializes the SMC table and uploads it
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always 0
+*/
+int polaris10_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct smu7_hwmgr *hw_data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct SMU74_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+       uint8_t i;
+       struct pp_atomctrl_gpio_pin_assignment gpio_pin;
+       pp_atomctrl_clock_dividers_vi dividers;
+
+       polaris10_initialize_power_tune_defaults(hwmgr);
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != hw_data->voltage_control)
+               polaris10_populate_smc_voltage_tables(hwmgr, table);
+
+       table->SystemFlags = 0;
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StepVddc))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (hw_data->is_memory_gddr5)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       if (hw_data->ulv_supported && table_info->us_ulv_voltage_offset) {
+               result = polaris10_populate_ulv_state(hwmgr, table);
+               PP_ASSERT_WITH_CODE(0 == result,
+                               "Failed to initialize ULV state!", return result);
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                               ixCG_ULV_PARAMETER, SMU7_CGULVPARAMETER_DFLT);
+       }
+
+       result = polaris10_populate_smc_link_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Link Level!", return result);
+
+       result = polaris10_populate_all_graphic_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Graphics Level!", return result);
+
+       result = polaris10_populate_all_memory_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Memory Level!", return result);
+
+       result = polaris10_populate_smc_acpi_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize ACPI Level!", return result);
+
+       result = polaris10_populate_smc_vce_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize VCE Level!", return result);
+
+       result = polaris10_populate_smc_samu_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize SAMU Level!", return result);
+
+       /* Since only the initial state is completely set up at this point
+        * (the other states are just copies of the boot state) we only
+        * need to populate the  ARB settings for the initial state.
+        */
+       result = polaris10_program_memory_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to Write ARB settings for the initial state.", return result);
+
+       result = polaris10_populate_smc_uvd_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize UVD Level!", return result);
+
+       result = polaris10_populate_smc_boot_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Boot Level!", return result);
+
+       result = polaris10_populate_smc_initailial_state(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to initialize Boot State!", return result);
+
+       result = polaris10_populate_bapm_parameters_in_dpm_table(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to populate BAPM Parameters!", return result);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ClockStretcher)) {
+               result = polaris10_populate_clock_stretcher_data_table(hwmgr);
+               PP_ASSERT_WITH_CODE(0 == result,
+                               "Failed to populate Clock Stretcher Data Table!",
+                               return result);
+       }
+
+       result = polaris10_populate_avfs_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result, "Failed to populate AVFS Parameters!", return result;);
+
+       table->CurrSclkPllRange = 0xff;
+       table->GraphicsVoltageChangeEnable  = 1;
+       table->GraphicsThermThrottleEnable  = 1;
+       table->GraphicsInterval = 1;
+       table->VoltageInterval  = 1;
+       table->ThermalInterval  = 1;
+       table->TemperatureLimitHigh =
+                       table_info->cac_dtp_table->usTargetOperatingTemp *
+                       SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->TemperatureLimitLow  =
+                       (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+                       SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->MemoryVoltageChangeEnable = 1;
+       table->MemoryInterval = 1;
+       table->VoltageResponseTime = 0;
+       table->PhaseResponseTime = 0;
+       table->MemoryThermThrottleEnable = 1;
+       table->PCIeBootLinkLevel = 0;
+       table->PCIeGenInterval = 1;
+       table->VRConfig = 0;
+
+       result = polaris10_populate_vr_config(hwmgr, table);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to populate VRConfig setting!", return result);
+
+       table->ThermGpio = 17;
+       table->SclkStepSize = 0x4000;
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID, &gpio_pin)) {
+               table->VRHotGpio = gpio_pin.uc_gpio_pin_bit_shift;
+       } else {
+               table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_RegulatorHot);
+       }
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+                       &gpio_pin)) {
+               table->AcDcGpio = gpio_pin.uc_gpio_pin_bit_shift;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_AutomaticDCTransition);
+       } else {
+               table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_AutomaticDCTransition);
+       }
+
+       /* Thermal Output GPIO */
+       if (atomctrl_get_pp_assign_pin(hwmgr, THERMAL_INT_OUTPUT_GPIO_PINID,
+                       &gpio_pin)) {
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ThermalOutGPIO);
+
+               table->ThermOutGpio = gpio_pin.uc_gpio_pin_bit_shift;
+
+               /* For porlarity read GPIOPAD_A with assigned Gpio pin
+                * since VBIOS will program this register to set 'inactive state',
+                * driver can then determine 'active state' from this and
+                * program SMU with correct polarity
+                */
+               table->ThermOutPolarity = (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A)
+                                       & (1 << gpio_pin.uc_gpio_pin_bit_shift))) ? 1:0;
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+               /* if required, combine VRHot/PCC with thermal out GPIO */
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_RegulatorHot)
+               && phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_CombinePCCWithThermalSignal))
+                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+       } else {
+               table->ThermOutGpio = 17;
+               table->ThermOutPolarity = 1;
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+       }
+
+       /* Populate BIF_SCLK levels into SMC DPM table */
+       for (i = 0; i <= hw_data->dpm_table.pcie_speed_table.count; i++) {
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr, smu_data->bif_sclk_table[i], &dividers);
+               PP_ASSERT_WITH_CODE((result == 0), "Can not find DFS divide id for Sclk", return result);
+
+               if (i == 0)
+                       table->Ulv.BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
+               else
+                       table->LinkLevel[i-1].BifSclkDfs = PP_HOST_TO_SMC_US((USHORT)(dividers.pll_post_divider));
+       }
+
+       for (i = 0; i < SMU74_MAX_ENTRIES_SMIO; i++)
+               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->CurrSclkPllRange);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+                       smu_data->smu7_data.dpm_table_start +
+                       offsetof(SMU74_Discrete_DpmTable, SystemFlags),
+                       (uint8_t *)&(table->SystemFlags),
+                       sizeof(SMU74_Discrete_DpmTable) - 3 * sizeof(SMU74_PIDController),
+                       SMC_RAM_END);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to upload dpm data to SMC memory!", return result);
+
+       result = polaris10_init_arb_table_index(hwmgr->smumgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to upload arb data to SMC memory!", return result);
+
+       result = polaris10_populate_pm_fuses(hwmgr);
+       PP_ASSERT_WITH_CODE(0 == result,
+                       "Failed to  populate PM fuses to SMC memory!", return result);
+       return 0;
+}
+
+static int polaris10_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->need_update_smu7_dpm_table &
+               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+               return polaris10_program_memory_timing_parameters(hwmgr);
+
+       return 0;
+}
+
+int polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr)
+{
+       int ret;
+       struct pp_smumgr *smumgr = (struct pp_smumgr *)(hwmgr->smumgr);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (smu_data->avfs.avfs_btc_status == AVFS_BTC_NOTSUPPORTED)
+               return 0;
+
+       ret = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                       PPSMC_MSG_SetGBDroopSettings, data->avfs_vdroop_override_setting);
+
+       ret = (smum_send_msg_to_smc(smumgr, PPSMC_MSG_EnableAvfs) == 0) ?
+                       0 : -1;
+
+       if (!ret)
+               /* If this param is not changed, this function could fire unnecessarily */
+               smu_data->avfs.avfs_btc_status = AVFS_BTC_COMPLETED_PREVIOUSLY;
+
+       return ret;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+int polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       SMU74_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+       uint32_t duty100;
+       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       uint16_t fdo_min, slope1, slope2;
+       uint32_t reference_clock;
+       int res;
+       uint64_t tmp64;
+
+       if (smu_data->smu7_data.fan_table_start == 0) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC,
+                       CG_FDO_CTRL1, FMAX_DUTY100);
+
+       if (duty100 == 0) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.
+                       usPWMMin * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (uint16_t)tmp64;
+
+       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+                       hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.TempMin = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMin) / 100);
+       fan_table.TempMed = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMed) / 100);
+       fan_table.TempMax = cpu_to_be16((50 + hwmgr->
+                       thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+       fan_table.Slope1 = cpu_to_be16(slope1);
+       fan_table.Slope2 = cpu_to_be16(slope2);
+
+       fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+       fan_table.HystDown = cpu_to_be16(hwmgr->
+                       thermal_controller.advanceFanControlParameters.ucTHyst);
+
+       fan_table.HystUp = cpu_to_be16(1);
+
+       fan_table.HystSlope = cpu_to_be16(1);
+
+       fan_table.TempRespLim = cpu_to_be16(5);
+
+       reference_clock = smu7_get_xclk(hwmgr);
+
+       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->
+                       thermal_controller.advanceFanControlParameters.ulCycleDelay *
+                       reference_clock) / 1600);
+
+       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(
+                       hwmgr->device, CGS_IND_REG__SMC,
+                       CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+       res = smu7_copy_bytes_to_smc(hwmgr->smumgr, smu_data->smu7_data.fan_table_start,
+                       (uint8_t *)&fan_table, (uint32_t)sizeof(fan_table),
+                       SMC_RAM_END);
+
+       if (!res && hwmgr->thermal_controller.
+                       advanceFanControlParameters.ucMinimumPWMLimit)
+               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SetFanMinPwm,
+                               hwmgr->thermal_controller.
+                               advanceFanControlParameters.ucMinimumPWMLimit);
+
+       if (!res && hwmgr->thermal_controller.
+                       advanceFanControlParameters.ulMinFanSCLKAcousticLimit)
+               res = smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SetFanSclkTarget,
+                               hwmgr->thermal_controller.
+                               advanceFanControlParameters.ulMinFanSCLKAcousticLimit);
+
+       if (res)
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+
+       return 0;
+}
+
+static int polaris10_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       smu_data->smc_state_table.UvdBootLevel = 0;
+       if (table_info->mm_dep_table->count > 0)
+               smu_data->smc_state_table.UvdBootLevel =
+                               (uint8_t) (table_info->mm_dep_table->count - 1);
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start + offsetof(SMU74_Discrete_DpmTable,
+                                               UvdBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0x00FFFFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_UVDDPM) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_UVDDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel));
+       return 0;
+}
+
+static int polaris10_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_StablePState))
+               smu_data->smc_state_table.VceBootLevel =
+                       (uint8_t) (table_info->mm_dep_table->count - 1);
+       else
+               smu_data->smc_state_table.VceBootLevel = 0;
+
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                                       offsetof(SMU74_Discrete_DpmTable, VceBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFF00FFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_VCEDPM_SetEnabledMask,
+                               (uint32_t)1 << smu_data->smc_state_table.VceBootLevel);
+       return 0;
+}
+
+static int polaris10_update_samu_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+
+
+       smu_data->smc_state_table.SamuBootLevel = 0;
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU74_Discrete_DpmTable, SamuBootLevel);
+
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFFFFFF00;
+       mm_boot_level_value |= smu_data->smc_state_table.SamuBootLevel << 0;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SAMUDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.SamuBootLevel));
+       return 0;
+}
+
+
+static int polaris10_update_bif_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_pcie_table *pcie_table = table_info->pcie_table;
+       int max_entry, i;
+
+       max_entry = (SMU74_MAX_LEVELS_LINK < pcie_table->count) ?
+                                               SMU74_MAX_LEVELS_LINK :
+                                               pcie_table->count;
+       /* Setup BIF_SCLK levels */
+       for (i = 0; i < max_entry; i++)
+               smu_data->bif_sclk_table[i] = pcie_table->entries[i].pcie_sclk;
+       return 0;
+}
+
+int polaris10_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+       switch (type) {
+       case SMU_UVD_TABLE:
+               polaris10_update_uvd_smc_table(hwmgr);
+               break;
+       case SMU_VCE_TABLE:
+               polaris10_update_vce_smc_table(hwmgr);
+               break;
+       case SMU_SAMU_TABLE:
+               polaris10_update_samu_smc_table(hwmgr);
+               break;
+       case SMU_BIF_TABLE:
+               polaris10_update_bif_smc_table(hwmgr);
+       default:
+               break;
+       }
+       return 0;
+}
+
+int polaris10_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+
+       int result = 0;
+       uint32_t low_sclk_interrupt_threshold = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkThrottleLowNotification)
+               && (hwmgr->gfx_arbiter.sclk_threshold !=
+                               data->low_sclk_interrupt_threshold)) {
+               data->low_sclk_interrupt_threshold =
+                               hwmgr->gfx_arbiter.sclk_threshold;
+               low_sclk_interrupt_threshold =
+                               data->low_sclk_interrupt_threshold;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU74_Discrete_DpmTable,
+                                       LowSclkInterruptThreshold),
+                               (uint8_t *)&low_sclk_interrupt_threshold,
+                               sizeof(uint32_t),
+                               SMC_RAM_END);
+       }
+       PP_ASSERT_WITH_CODE((result == 0),
+                       "Failed to update SCLK threshold!", return result);
+
+       result = polaris10_program_mem_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE((result == 0),
+                       "Failed to program memory timing parameters!",
+                       );
+
+       return result;
+}
+
+uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member)
+{
+       switch (type) {
+       case SMU_SoftRegisters:
+               switch (member) {
+               case HandshakeDisables:
+                       return offsetof(SMU74_SoftRegisters, HandshakeDisables);
+               case VoltageChangeTimeout:
+                       return offsetof(SMU74_SoftRegisters, VoltageChangeTimeout);
+               case AverageGraphicsActivity:
+                       return offsetof(SMU74_SoftRegisters, AverageGraphicsActivity);
+               case PreVBlankGap:
+                       return offsetof(SMU74_SoftRegisters, PreVBlankGap);
+               case VBlankTimeout:
+                       return offsetof(SMU74_SoftRegisters, VBlankTimeout);
+               case UcodeLoadStatus:
+                       return offsetof(SMU74_SoftRegisters, UcodeLoadStatus);
+               }
+       case SMU_Discrete_DpmTable:
+               switch (member) {
+               case UvdBootLevel:
+                       return offsetof(SMU74_Discrete_DpmTable, UvdBootLevel);
+               case VceBootLevel:
+                       return offsetof(SMU74_Discrete_DpmTable, VceBootLevel);
+               case SamuBootLevel:
+                       return offsetof(SMU74_Discrete_DpmTable, SamuBootLevel);
+               case LowSclkInterruptThreshold:
+                       return offsetof(SMU74_Discrete_DpmTable, LowSclkInterruptThreshold);
+               }
+       }
+       printk("cant't get the offset of type %x member %x \n", type, member);
+       return 0;
+}
+
+uint32_t polaris10_get_mac_definition(uint32_t value)
+{
+       switch (value) {
+       case SMU_MAX_LEVELS_GRAPHICS:
+               return SMU74_MAX_LEVELS_GRAPHICS;
+       case SMU_MAX_LEVELS_MEMORY:
+               return SMU74_MAX_LEVELS_MEMORY;
+       case SMU_MAX_LEVELS_LINK:
+               return SMU74_MAX_LEVELS_LINK;
+       case SMU_MAX_ENTRIES_SMIO:
+               return SMU74_MAX_ENTRIES_SMIO;
+       case SMU_MAX_LEVELS_VDDC:
+               return SMU74_MAX_LEVELS_VDDC;
+       case SMU_MAX_LEVELS_VDDGFX:
+               return SMU74_MAX_LEVELS_VDDGFX;
+       case SMU_MAX_LEVELS_VDDCI:
+               return SMU74_MAX_LEVELS_VDDCI;
+       case SMU_MAX_LEVELS_MVDD:
+               return SMU74_MAX_LEVELS_MVDD;
+       case SMU_UVD_MCLK_HANDSHAKE_DISABLE:
+               return SMU7_UVD_MCLK_HANDSHAKE_DISABLE;
+       }
+
+       printk("cant't get the mac of %x \n", value);
+       return 0;
+}
+
+/**
+* Get the location of various tables inside the FW image.
+*
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @return   always  0
+*/
+int polaris10_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t tmp;
+       int result;
+       bool error = false;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, DpmTable),
+                       &tmp, SMC_RAM_END);
+
+       if (0 == result)
+               smu_data->smu7_data.dpm_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, SoftRegisters),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.soft_regs_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, mcRegisterTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.mc_reg_table_start = tmp;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, FanTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.fan_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, mcArbDramTimingTable),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.arb_table_start = tmp;
+
+       error |= (0 != result);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       SMU7_FIRMWARE_HEADER_LOCATION +
+                       offsetof(SMU74_Firmware_Header, Version),
+                       &tmp, SMC_RAM_END);
+
+       if (!result)
+               hwmgr->microcode_version_info.SMC = tmp;
+
+       error |= (0 != result);
+
+       return error ? -1 : 0;
+}
+
+bool polaris10_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+                       ? true : false;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.h b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.h
new file mode 100644 (file)
index 0000000..5ade3ce
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 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 POLARIS10_SMC_H
+#define POLARIS10_SMC_H
+
+#include "smumgr.h"
+
+
+int polaris10_populate_all_graphic_levels(struct pp_hwmgr *hwmgr);
+int polaris10_populate_all_memory_levels(struct pp_hwmgr *hwmgr);
+int polaris10_init_smc_table(struct pp_hwmgr *hwmgr);
+int polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr);
+int polaris10_thermal_avfs_enable(struct pp_hwmgr *hwmgr);
+int polaris10_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type);
+int polaris10_update_sclk_threshold(struct pp_hwmgr *hwmgr);
+uint32_t polaris10_get_offsetof(uint32_t type, uint32_t member);
+uint32_t polaris10_get_mac_definition(uint32_t value);
+int polaris10_process_firmware_header(struct pp_hwmgr *hwmgr);
+bool polaris10_is_dpm_running(struct pp_hwmgr *hwmgr);
+
+#endif
+
index 5dba7c5..5c3598a 100644 (file)
 #include "ppatomctrl.h"
 #include "pp_debug.h"
 #include "cgs_common.h"
+#include "polaris10_smc.h"
+#include "smu7_ppsmc.h"
+#include "smu7_smumgr.h"
 
-#define POLARIS10_SMC_SIZE 0x20000
-#define VOLTAGE_SCALE 4
-
-/* Microcode file is stored in this buffer */
-#define BUFFER_SIZE                 80000
-#define MAX_STRING_SIZE             15
-#define BUFFER_SIZETWO              131072  /* 128 *1024 */
-
-#define SMC_RAM_END 0x40000
+#define PPPOLARIS10_TARGETACTIVITY_DFLT                     50
 
 static const SMU74_Discrete_GraphicsLevel avfs_graphics_level_polaris10[8] = {
        /*  Min      pcie   DeepSleep Activity  CgSpll      CgSpll    CcPwr  CcPwr  Sclk         Enabled      Enabled                       Voltage    Power */
@@ -62,572 +57,9 @@ static const SMU74_Discrete_GraphicsLevel avfs_graphics_level_polaris10[8] = {
        { 0xa00fa446, 0x01, 0x00, 0x3200, 0, 0, 0, 0, 0, 0, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, { 0xa0860100, 0x2800, 0, 0x2000, 2, 1, 0x0004, 0x0c02, 0xffff, 0x2700, 0x6433, 0x2100 } }
 };
 
-static const SMU74_Discrete_MemoryLevel avfs_memory_level_polaris10 =
-       {0x100ea446, 0, 0x30750000, 0x01, 0x01, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x1f00, 0x00, 0x00};
-
-/**
-* Set the address for reading/writing the SMC SRAM space.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smcAddress the address in the SMC RAM to access.
-*/
-static int polaris10_set_smc_sram_address(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t limit)
-{
-       PP_ASSERT_WITH_CODE((0 == (3 & smc_addr)), "SMC address must be 4 byte aligned.", return -EINVAL);
-       PP_ASSERT_WITH_CODE((limit > (smc_addr + 3)), "SMC addr is beyond the SMC RAM area.", return -EINVAL);
-
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_11, smc_addr);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0);
-
-       return 0;
-}
-
-/**
-* Copy bytes from SMC RAM space into driver memory.
-*
-* @param    smumgr  the address of the powerplay SMU manager.
-* @param    smc_start_address the start address in the SMC RAM to copy bytes from
-* @param    src the byte array to copy the bytes to.
-* @param    byte_count the number of bytes to copy.
-*/
-int polaris10_copy_bytes_from_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address, uint32_t *dest, uint32_t byte_count, uint32_t limit)
-{
-       uint32_t data;
-       uint32_t addr;
-       uint8_t *dest_byte;
-       uint8_t i, data_byte[4] = {0};
-       uint32_t *pdata = (uint32_t *)&data_byte;
-
-       PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -1;);
-       PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -1);
-
-       addr = smc_start_address;
-
-       while (byte_count >= 4) {
-               polaris10_read_smc_sram_dword(smumgr, addr, &data, limit);
-
-               *dest = PP_SMC_TO_HOST_UL(data);
-
-               dest += 1;
-               byte_count -= 4;
-               addr += 4;
-       }
-
-       if (byte_count) {
-               polaris10_read_smc_sram_dword(smumgr, addr, &data, limit);
-               *pdata = PP_SMC_TO_HOST_UL(data);
-       /* Cast dest into byte type in dest_byte.  This way, we don't overflow if the allocated memory is not 4-byte aligned. */
-               dest_byte = (uint8_t *)dest;
-               for (i = 0; i < byte_count; i++)
-                       dest_byte[i] = data_byte[i];
-       }
-
-       return 0;
-}
-
-/**
-* Copy bytes from an array into the SMC RAM space.
-*
-* @param    pSmuMgr  the address of the powerplay SMU manager.
-* @param    smc_start_address the start address in the SMC RAM to copy bytes to.
-* @param    src the byte array to copy the bytes from.
-* @param    byte_count the number of bytes to copy.
-*/
-int polaris10_copy_bytes_to_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address,
-                               const uint8_t *src, uint32_t byte_count, uint32_t limit)
-{
-       int result;
-       uint32_t data = 0;
-       uint32_t original_data;
-       uint32_t addr = 0;
-       uint32_t extra_shift;
-
-       PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -1);
-       PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -1);
-
-       addr = smc_start_address;
-
-       while (byte_count >= 4) {
-       /* Bytes are written into the SMC addres space with the MSB first. */
-               data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
-
-               result = polaris10_set_smc_sram_address(smumgr, addr, limit);
-
-               if (0 != result)
-                       return result;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, data);
-
-               src += 4;
-               byte_count -= 4;
-               addr += 4;
-       }
-
-       if (0 != byte_count) {
-
-               data = 0;
-
-               result = polaris10_set_smc_sram_address(smumgr, addr, limit);
-
-               if (0 != result)
-                       return result;
-
-
-               original_data = cgs_read_register(smumgr->device, mmSMC_IND_DATA_11);
-
-               extra_shift = 8 * (4 - byte_count);
-
-               while (byte_count > 0) {
-                       /* Bytes are written into the SMC addres space with the MSB first. */
-                       data = (0x100 * data) + *src++;
-                       byte_count--;
-               }
-
-               data <<= extra_shift;
-
-               data |= (original_data & ~((~0UL) << extra_shift));
-
-               result = polaris10_set_smc_sram_address(smumgr, addr, limit);
-
-               if (0 != result)
-                       return result;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, data);
-       }
-
-       return 0;
-}
-
-
-static int polaris10_program_jump_on_start(struct pp_smumgr *smumgr)
-{
-       static const unsigned char data[4] = { 0xE0, 0x00, 0x80, 0x40 };
-
-       polaris10_copy_bytes_to_smc(smumgr, 0x0, data, 4, sizeof(data)+1);
-
-       return 0;
-}
-
-/**
-* Return if the SMC is currently running.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-*/
-bool polaris10_is_smc_ram_running(struct pp_smumgr *smumgr)
-{
-       return ((0 == SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
-       && (0x20100 <= cgs_read_ind_register(smumgr->device, CGS_IND_REG__SMC, ixSMC_PC_C)));
-}
-
-static bool polaris10_is_hw_avfs_present(struct pp_smumgr *smumgr)
-{
-       uint32_t efuse;
-
-       efuse = cgs_read_ind_register(smumgr->device, CGS_IND_REG__SMC, ixSMU_EFUSE_0 + (49*4));
-       efuse &= 0x00000001;
-       if (efuse)
-               return true;
-
-       return false;
-}
-
-/**
-* Send a message to the SMC, and wait for its response.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   The response that came from the SMC.
-*/
-int polaris10_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
-{
-       int ret;
-
-       if (!polaris10_is_smc_ram_running(smumgr))
-               return -1;
-
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP);
-
-       if (ret != 1)
-               printk("\n failed to send pre message %x ret is %d \n",  msg, ret);
-
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP);
-
-       if (ret != 1)
-               printk("\n failed to send message %x ret is %d \n",  msg, ret);
-
-       return 0;
-}
-
-
-/**
-* Send a message to the SMC, and do not wait for its response.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   Always return 0.
-*/
-int polaris10_send_msg_to_smc_without_waiting(struct pp_smumgr *smumgr, uint16_t msg)
-{
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-/**
-* Send a message to the SMC with parameter
-*
-* @param    smumgr:  the address of the powerplay hardware manager.
-* @param    msg: the message to send.
-* @param    parameter: the parameter to send
-* @return   The response that came from the SMC.
-*/
-int polaris10_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr, uint16_t msg, uint32_t parameter)
-{
-       if (!polaris10_is_smc_ram_running(smumgr)) {
-               return -1;
-       }
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-
-       return polaris10_send_msg_to_smc(smumgr, msg);
-}
-
-
-/**
-* Send a message to the SMC with parameter, do not wait for response
-*
-* @param    smumgr:  the address of the powerplay hardware manager.
-* @param    msg: the message to send.
-* @param    parameter: the parameter to send
-* @return   The response that came from the SMC.
-*/
-int polaris10_send_msg_to_smc_with_parameter_without_waiting(struct pp_smumgr *smumgr, uint16_t msg, uint32_t parameter)
-{
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-
-       return polaris10_send_msg_to_smc_without_waiting(smumgr, msg);
-}
-
-int polaris10_send_msg_to_smc_offset(struct pp_smumgr *smumgr)
-{
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, 0x20000);
-
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, PPSMC_MSG_Test);
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP))
-               printk("Failed to send Message.\n");
-
-       return 0;
-}
-
-/**
-* Wait until the SMC is doing nithing. Doing nothing means that the SMC is either turned off or it is sitting on the STOP instruction.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   The response that came from the SMC.
-*/
-int polaris10_wait_for_smc_inactive(struct pp_smumgr *smumgr)
-{
-       /* If the SMC is not even on it qualifies as inactive. */
-       if (!polaris10_is_smc_ram_running(smumgr))
-               return -1;
-
-       SMUM_WAIT_VFPF_INDIRECT_FIELD(smumgr, SMC_IND, SMC_SYSCON_CLOCK_CNTL_0, cken, 0);
-       return 0;
-}
-
-
-/**
-* Upload the SMC firmware to the SMC microcontroller.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    pFirmware the data structure containing the various sections of the firmware.
-*/
-static int polaris10_upload_smc_firmware_data(struct pp_smumgr *smumgr, uint32_t length, uint32_t *src, uint32_t limit)
-{
-       uint32_t byte_count = length;
-
-       PP_ASSERT_WITH_CODE((limit >= byte_count), "SMC address is beyond the SMC RAM area.", return -1);
+static const SMU74_Discrete_MemoryLevel avfs_memory_level_polaris10 = {
+       0x100ea446, 0, 0x30750000, 0x01, 0x01, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x1f00, 0x00, 0x00};
 
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_11, 0x20000);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 1);
-
-       for (; byte_count >= 4; byte_count -= 4)
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, *src++);
-
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0);
-
-       PP_ASSERT_WITH_CODE((0 == byte_count), "SMC size must be dividable by 4.", return -1);
-
-       return 0;
-}
-
-static enum cgs_ucode_id polaris10_convert_fw_type_to_cgs(uint32_t fw_type)
-{
-       enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
-
-       switch (fw_type) {
-       case UCODE_ID_SMU:
-               result = CGS_UCODE_ID_SMU;
-               break;
-       case UCODE_ID_SMU_SK:
-               result = CGS_UCODE_ID_SMU_SK;
-               break;
-       case UCODE_ID_SDMA0:
-               result = CGS_UCODE_ID_SDMA0;
-               break;
-       case UCODE_ID_SDMA1:
-               result = CGS_UCODE_ID_SDMA1;
-               break;
-       case UCODE_ID_CP_CE:
-               result = CGS_UCODE_ID_CP_CE;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = CGS_UCODE_ID_CP_PFP;
-               break;
-       case UCODE_ID_CP_ME:
-               result = CGS_UCODE_ID_CP_ME;
-               break;
-       case UCODE_ID_CP_MEC:
-               result = CGS_UCODE_ID_CP_MEC;
-               break;
-       case UCODE_ID_CP_MEC_JT1:
-               result = CGS_UCODE_ID_CP_MEC_JT1;
-               break;
-       case UCODE_ID_CP_MEC_JT2:
-               result = CGS_UCODE_ID_CP_MEC_JT2;
-               break;
-       case UCODE_ID_RLC_G:
-               result = CGS_UCODE_ID_RLC_G;
-               break;
-       default:
-               break;
-       }
-
-       return result;
-}
-
-static int polaris10_upload_smu_firmware_image(struct pp_smumgr *smumgr)
-{
-       int result = 0;
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-
-       struct cgs_firmware_info info = {0};
-
-       if (smu_data->security_hard_key == 1)
-               cgs_get_firmware_info(smumgr->device,
-                       polaris10_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
-       else
-               cgs_get_firmware_info(smumgr->device,
-                       polaris10_convert_fw_type_to_cgs(UCODE_ID_SMU_SK), &info);
-
-       /* TO DO cgs_init_samu_load_smu(smumgr->device, (uint32_t *)info.kptr, info.image_size, smu_data->post_initial_boot);*/
-       result = polaris10_upload_smc_firmware_data(smumgr, info.image_size, (uint32_t *)info.kptr, POLARIS10_SMC_SIZE);
-
-       return result;
-}
-
-/**
-* Read a 32bit value from the SMC SRAM space.
-* ALL PARAMETERS ARE IN HOST BYTE ORDER.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smcAddress the address in the SMC RAM to access.
-* @param    value and output parameter for the data read from the SMC SRAM.
-*/
-int polaris10_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t *value, uint32_t limit)
-{
-       int result;
-
-       result = polaris10_set_smc_sram_address(smumgr, smc_addr, limit);
-
-       if (result)
-               return result;
-
-       *value = cgs_read_register(smumgr->device, mmSMC_IND_DATA_11);
-       return 0;
-}
-
-/**
-* Write a 32bit value to the SMC SRAM space.
-* ALL PARAMETERS ARE IN HOST BYTE ORDER.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smc_addr the address in the SMC RAM to access.
-* @param    value to write to the SMC SRAM.
-*/
-int polaris10_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t value, uint32_t limit)
-{
-       int result;
-
-       result = polaris10_set_smc_sram_address(smumgr, smc_addr, limit);
-
-       if (result)
-               return result;
-
-       cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, value);
-
-       return 0;
-}
-
-
-int polaris10_smu_fini(struct pp_smumgr *smumgr)
-{
-       if (smumgr->backend) {
-               kfree(smumgr->backend);
-               smumgr->backend = NULL;
-       }
-       cgs_rel_firmware(smumgr->device, CGS_UCODE_ID_SMU);
-       return 0;
-}
-
-/* Convert the firmware type to SMU type mask. For MEC, we need to check all MEC related type */
-static uint32_t polaris10_get_mask_for_firmware_type(uint32_t fw_type)
-{
-       uint32_t result = 0;
-
-       switch (fw_type) {
-       case UCODE_ID_SDMA0:
-               result = UCODE_ID_SDMA0_MASK;
-               break;
-       case UCODE_ID_SDMA1:
-               result = UCODE_ID_SDMA1_MASK;
-               break;
-       case UCODE_ID_CP_CE:
-               result = UCODE_ID_CP_CE_MASK;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = UCODE_ID_CP_PFP_MASK;
-               break;
-       case UCODE_ID_CP_ME:
-               result = UCODE_ID_CP_ME_MASK;
-               break;
-       case UCODE_ID_CP_MEC_JT1:
-       case UCODE_ID_CP_MEC_JT2:
-               result = UCODE_ID_CP_MEC_MASK;
-               break;
-       case UCODE_ID_RLC_G:
-               result = UCODE_ID_RLC_G_MASK;
-               break;
-       default:
-               printk("UCode type is out of range! \n");
-               result = 0;
-       }
-
-       return result;
-}
-
-/* Populate one firmware image to the data structure */
-
-static int polaris10_populate_single_firmware_entry(struct pp_smumgr *smumgr,
-                                               uint32_t fw_type,
-                                               struct SMU_Entry *entry)
-{
-       int result = 0;
-       struct cgs_firmware_info info = {0};
-
-       result = cgs_get_firmware_info(smumgr->device,
-                               polaris10_convert_fw_type_to_cgs(fw_type),
-                               &info);
-
-       if (!result) {
-               entry->version = info.version;
-               entry->id = (uint16_t)fw_type;
-               entry->image_addr_high = smu_upper_32_bits(info.mc_addr);
-               entry->image_addr_low = smu_lower_32_bits(info.mc_addr);
-               entry->meta_data_addr_high = 0;
-               entry->meta_data_addr_low = 0;
-               entry->data_size_byte = info.image_size;
-               entry->num_register_entries = 0;
-       }
-
-       if (fw_type == UCODE_ID_RLC_G)
-               entry->flags = 1;
-       else
-               entry->flags = 0;
-
-       return 0;
-}
-
-static int polaris10_request_smu_load_fw(struct pp_smumgr *smumgr)
-{
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-       uint32_t fw_to_load;
-
-       int result = 0;
-       struct SMU_DRAMData_TOC *toc;
-
-       if (!smumgr->reload_fw) {
-               printk(KERN_INFO "[ powerplay ] skip reloading...\n");
-               return 0;
-       }
-
-       if (smu_data->soft_regs_start)
-               cgs_write_ind_register(smumgr->device, CGS_IND_REG__SMC,
-                                       smu_data->soft_regs_start + offsetof(SMU74_SoftRegisters, UcodeLoadStatus),
-                                       0x0);
-
-       polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_HI, smu_data->smu_buffer.mc_addr_high);
-       polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_LO, smu_data->smu_buffer.mc_addr_low);
-
-       toc = (struct SMU_DRAMData_TOC *)smu_data->header;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_RLC_G, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_CE, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_PFP, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_ME, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_MEC, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_MEC_JT1, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_CP_MEC_JT2, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_SDMA0, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-       PP_ASSERT_WITH_CODE(0 == polaris10_populate_single_firmware_entry(smumgr, UCODE_ID_SDMA1, &toc->entry[toc->num_entries++]), "Failed to Get Firmware Entry.", return -1);
-
-       polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_HI, smu_data->header_buffer.mc_addr_high);
-       polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_LO, smu_data->header_buffer.mc_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK
-                  + UCODE_ID_SDMA0_MASK
-                  + UCODE_ID_SDMA1_MASK
-                  + UCODE_ID_CP_CE_MASK
-                  + UCODE_ID_CP_ME_MASK
-                  + UCODE_ID_CP_PFP_MASK
-                  + UCODE_ID_CP_MEC_MASK;
-
-       if (polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_LoadUcodes, fw_to_load))
-               printk(KERN_ERR "Fail to Request SMU Load uCode");
-
-       return result;
-}
-
-/* Check if the FW has been loaded, SMU will not return if loading has not finished. */
-static int polaris10_check_fw_load_finish(struct pp_smumgr *smumgr, uint32_t fw_type)
-{
-       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-       uint32_t fw_mask = polaris10_get_mask_for_firmware_type(fw_type);
-       uint32_t ret;
-       /* Check SOFT_REGISTERS_TABLE_28.UcodeLoadStatus */
-       ret = smum_wait_on_indirect_register(smumgr, mmSMC_IND_INDEX_11,
-                                       smu_data->soft_regs_start + offsetof(SMU74_SoftRegisters, UcodeLoadStatus),
-                                       fw_mask, fw_mask);
-
-       return ret;
-}
-
-static int polaris10_reload_firmware(struct pp_smumgr *smumgr)
-{
-       return smumgr->smumgr_funcs->start_smu(smumgr);
-}
 
 static int polaris10_setup_pwr_virus(struct pp_smumgr *smumgr)
 {
@@ -669,7 +101,7 @@ static int polaris10_perform_btc(struct pp_smumgr *smumgr)
        struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
 
        if (0 != smu_data->avfs.avfs_btc_param) {
-               if (0 != polaris10_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_PerformBtc, smu_data->avfs.avfs_btc_param)) {
+               if (0 != smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_PerformBtc, smu_data->avfs.avfs_btc_param)) {
                        printk("[AVFS][SmuPolaris10_PerformBtc] PerformBTC SMU msg failed");
                        result = -1;
                }
@@ -697,7 +129,7 @@ int polaris10_setup_graphics_level_structure(struct pp_smumgr *smumgr)
        graphics_level_size = sizeof(avfs_graphics_level_polaris10);
        u16_boot_mvdd = PP_HOST_TO_SMC_US(1300 * VOLTAGE_SCALE);
 
-       PP_ASSERT_WITH_CODE(0 == polaris10_read_smc_sram_dword(smumgr,
+       PP_ASSERT_WITH_CODE(0 == smu7_read_smc_sram_dword(smumgr,
                                SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, DpmTable),
                                &dpm_table_start, 0x40000),
                        "[AVFS][Polaris10_SetupGfxLvlStruct] SMU could not communicate starting address of DPM table",
@@ -708,14 +140,14 @@ int polaris10_setup_graphics_level_structure(struct pp_smumgr *smumgr)
 
        vr_config_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, VRConfig);
 
-       PP_ASSERT_WITH_CODE(0 == polaris10_copy_bytes_to_smc(smumgr, vr_config_address,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, vr_config_address,
                                (uint8_t *)&vr_config, sizeof(uint32_t), 0x40000),
                        "[AVFS][Polaris10_SetupGfxLvlStruct] Problems copying VRConfig value over to SMC",
                        return -1);
 
        graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, GraphicsLevel);
 
-       PP_ASSERT_WITH_CODE(0 == polaris10_copy_bytes_to_smc(smumgr, graphics_level_address,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, graphics_level_address,
                                (uint8_t *)(&avfs_graphics_level_polaris10),
                                graphics_level_size, 0x40000),
                        "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of SCLK DPM table failed!",
@@ -723,7 +155,7 @@ int polaris10_setup_graphics_level_structure(struct pp_smumgr *smumgr)
 
        graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, MemoryLevel);
 
-       PP_ASSERT_WITH_CODE(0 == polaris10_copy_bytes_to_smc(smumgr, graphics_level_address,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, graphics_level_address,
                                (uint8_t *)(&avfs_memory_level_polaris10), sizeof(avfs_memory_level_polaris10), 0x40000),
                                "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of MCLK DPM table failed!",
                        return -1);
@@ -732,7 +164,7 @@ int polaris10_setup_graphics_level_structure(struct pp_smumgr *smumgr)
 
        graphics_level_address = dpm_table_start + offsetof(SMU74_Discrete_DpmTable, BootMVdd);
 
-       PP_ASSERT_WITH_CODE(0 == polaris10_copy_bytes_to_smc(smumgr, graphics_level_address,
+       PP_ASSERT_WITH_CODE(0 == smu7_copy_bytes_to_smc(smumgr, graphics_level_address,
                        (uint8_t *)(&u16_boot_mvdd), sizeof(u16_boot_mvdd), 0x40000),
                        "[AVFS][Polaris10_SetupGfxLvlStruct] Copying of DPM table failed!",
                        return -1);
@@ -793,7 +225,7 @@ static int polaris10_start_smu_in_protection_mode(struct pp_smumgr *smumgr)
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                                        SMC_SYSCON_RESET_CNTL, rst_reg, 1);
 
-       result = polaris10_upload_smu_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
        if (result != 0)
                return result;
 
@@ -812,7 +244,7 @@ static int polaris10_start_smu_in_protection_mode(struct pp_smumgr *smumgr)
 
 
        /* Call Test SMU message with 0x20000 offset to trigger SMU start */
-       polaris10_send_msg_to_smc_offset(smumgr);
+       smu7_send_msg_to_smc_offset(smumgr);
 
        /* Wait done bit to be set */
        /* Check pass/failed indicator */
@@ -853,12 +285,12 @@ static int polaris10_start_smu_in_non_protection_mode(struct pp_smumgr *smumgr)
                                        SMC_SYSCON_RESET_CNTL,
                                        rst_reg, 1);
 
-       result = polaris10_upload_smu_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
        if (result != 0)
                return result;
 
        /* Set smc instruct start point at 0x0 */
-       polaris10_program_jump_on_start(smumgr);
+       smu7_program_jump_on_start(smumgr);
 
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                                        SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
@@ -881,10 +313,10 @@ static int polaris10_start_smu(struct pp_smumgr *smumgr)
        bool SMU_VFT_INTACT;
 
        /* Only start SMC if SMC RAM is not running */
-       if (!polaris10_is_smc_ram_running(smumgr)) {
+       if (!smu7_is_smc_ram_running(smumgr)) {
                SMU_VFT_INTACT = false;
                smu_data->protected_mode = (uint8_t) (SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_MODE));
-               smu_data->security_hard_key = (uint8_t) (SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_SEL));
+               smu_data->smu7_data.security_hard_key = (uint8_t) (SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMU_FIRMWARE, SMU_SEL));
 
                /* Check if SMU is running in protected mode */
                if (smu_data->protected_mode == 0) {
@@ -894,7 +326,7 @@ static int polaris10_start_smu(struct pp_smumgr *smumgr)
 
                        /* If failed, try with different security Key. */
                        if (result != 0) {
-                               smu_data->security_hard_key ^= 1;
+                               smu_data->smu7_data.security_hard_key ^= 1;
                                result = polaris10_start_smu_in_protection_mode(smumgr);
                        }
                }
@@ -906,89 +338,69 @@ static int polaris10_start_smu(struct pp_smumgr *smumgr)
        } else
                SMU_VFT_INTACT = true; /*Driver went offline but SMU was still alive and contains the VFT table */
 
-       smu_data->post_initial_boot = true;
        polaris10_avfs_event_mgr(smumgr, SMU_VFT_INTACT);
        /* Setup SoftRegsStart here for register lookup in case DummyBackEnd is used and ProcessFirmwareHeader is not executed */
-       polaris10_read_smc_sram_dword(smumgr, SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, SoftRegisters),
-                                       &(smu_data->soft_regs_start), 0x40000);
+       smu7_read_smc_sram_dword(smumgr, SMU7_FIRMWARE_HEADER_LOCATION + offsetof(SMU74_Firmware_Header, SoftRegisters),
+                                       &(smu_data->smu7_data.soft_regs_start), 0x40000);
 
-       result = polaris10_request_smu_load_fw(smumgr);
+       result = smu7_request_smu_load_fw(smumgr);
 
        return result;
 }
 
+static bool polaris10_is_hw_avfs_present(struct pp_smumgr *smumgr)
+{
+       uint32_t efuse;
+
+       efuse = cgs_read_ind_register(smumgr->device, CGS_IND_REG__SMC, ixSMU_EFUSE_0 + (49*4));
+       efuse &= 0x00000001;
+       if (efuse)
+               return true;
+
+       return false;
+}
+
 static int polaris10_smu_init(struct pp_smumgr *smumgr)
 {
-       struct polaris10_smumgr *smu_data;
-       uint8_t *internal_buf;
-       uint64_t mc_addr = 0;
-       /* Allocate memory for backend private data */
-       smu_data = (struct polaris10_smumgr *)(smumgr->backend);
-       smu_data->header_buffer.data_size =
-               ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       smu_data->smu_buffer.data_size = 200*4096;
-       smu_data->avfs.avfs_btc_status = AVFS_BTC_NOTSUPPORTED;
-/* Allocate FW image data structure and header buffer and
- * send the header buffer address to SMU */
-       smu_allocate_memory(smumgr->device,
-               smu_data->header_buffer.data_size,
-               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
-               PAGE_SIZE,
-               &mc_addr,
-               &smu_data->header_buffer.kaddr,
-               &smu_data->header_buffer.handle);
-
-       smu_data->header = smu_data->header_buffer.kaddr;
-       smu_data->header_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
-       smu_data->header_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
-
-       PP_ASSERT_WITH_CODE((NULL != smu_data->header),
-               "Out of memory.",
-               kfree(smumgr->backend);
-               cgs_free_gpu_mem(smumgr->device,
-               (cgs_handle_t)smu_data->header_buffer.handle);
-               return -1);
+       struct polaris10_smumgr *smu_data = (struct polaris10_smumgr *)(smumgr->backend);
+       int i;
 
-/* Allocate buffer for SMU internal buffer and send the address to SMU.
- * Iceland SMU does not need internal buffer.*/
-       smu_allocate_memory(smumgr->device,
-               smu_data->smu_buffer.data_size,
-               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
-               PAGE_SIZE,
-               &mc_addr,
-               &smu_data->smu_buffer.kaddr,
-               &smu_data->smu_buffer.handle);
-
-       internal_buf = smu_data->smu_buffer.kaddr;
-       smu_data->smu_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
-       smu_data->smu_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
-
-       PP_ASSERT_WITH_CODE((NULL != internal_buf),
-               "Out of memory.",
-               kfree(smumgr->backend);
-               cgs_free_gpu_mem(smumgr->device,
-               (cgs_handle_t)smu_data->smu_buffer.handle);
-               return -1;);
+       if (smu7_init(smumgr))
+               return -EINVAL;
 
        if (polaris10_is_hw_avfs_present(smumgr))
                smu_data->avfs.avfs_btc_status = AVFS_BTC_BOOT;
        else
                smu_data->avfs.avfs_btc_status = AVFS_BTC_NOTSUPPORTED;
 
+       for (i = 0; i < SMU74_MAX_LEVELS_GRAPHICS; i++)
+               smu_data->activity_target[i] = PPPOLARIS10_TARGETACTIVITY_DFLT;
+
        return 0;
 }
 
-static const struct pp_smumgr_func ellsemere_smu_funcs = {
+static const struct pp_smumgr_func polaris10_smu_funcs = {
        .smu_init = polaris10_smu_init,
-       .smu_fini = polaris10_smu_fini,
+       .smu_fini = smu7_smu_fini,
        .start_smu = polaris10_start_smu,
-       .check_fw_load_finish = polaris10_check_fw_load_finish,
-       .request_smu_load_fw = polaris10_reload_firmware,
+       .check_fw_load_finish = smu7_check_fw_load_finish,
+       .request_smu_load_fw = smu7_reload_firmware,
        .request_smu_load_specific_fw = NULL,
-       .send_msg_to_smc = polaris10_send_msg_to_smc,
-       .send_msg_to_smc_with_parameter = polaris10_send_msg_to_smc_with_parameter,
+       .send_msg_to_smc = smu7_send_msg_to_smc,
+       .send_msg_to_smc_with_parameter = smu7_send_msg_to_smc_with_parameter,
        .download_pptable_settings = NULL,
        .upload_pptable_settings = NULL,
+       .update_smc_table = polaris10_update_smc_table,
+       .get_offsetof = polaris10_get_offsetof,
+       .process_firmware_header = polaris10_process_firmware_header,
+       .init_smc_table = polaris10_init_smc_table,
+       .update_sclk_threshold = polaris10_update_sclk_threshold,
+       .thermal_avfs_enable = polaris10_thermal_avfs_enable,
+       .thermal_setup_fan_table = polaris10_thermal_setup_fan_table,
+       .populate_all_graphic_levels = polaris10_populate_all_graphic_levels,
+       .populate_all_memory_levels = polaris10_populate_all_memory_levels,
+       .get_mac_definition = polaris10_get_mac_definition,
+       .is_dpm_running = polaris10_is_dpm_running,
 };
 
 int polaris10_smum_init(struct pp_smumgr *smumgr)
@@ -998,10 +410,10 @@ int polaris10_smum_init(struct pp_smumgr *smumgr)
        polaris10_smu = kzalloc(sizeof(struct polaris10_smumgr), GFP_KERNEL);
 
        if (polaris10_smu == NULL)
-               return -1;
+               return -EINVAL;
 
        smumgr->backend = polaris10_smu;
-       smumgr->smumgr_funcs = &ellsemere_smu_funcs;
+       smumgr->smumgr_funcs = &polaris10_smu_funcs;
 
        return 0;
 }
index e5377ae..49ebf1d 100644 (file)
 #ifndef _POLARIS10_SMUMANAGER_H
 #define _POLARIS10_SMUMANAGER_H
 
-#include <polaris10_ppsmc.h>
+
 #include <pp_endian.h>
+#include "smu74.h"
+#include "smu74_discrete.h"
+#include "smu7_smumgr.h"
+
+#define SMC_RAM_END 0x40000
 
 struct polaris10_avfs {
        enum AVFS_BTC_STATUS avfs_btc_status;
        uint32_t           avfs_btc_param;
 };
 
-struct polaris10_buffer_entry {
-       uint32_t data_size;
-       uint32_t mc_addr_low;
-       uint32_t mc_addr_high;
-       void *kaddr;
-       unsigned long  handle;
+struct polaris10_pt_defaults {
+       uint8_t   SviLoadLineEn;
+       uint8_t   SviLoadLineVddC;
+       uint8_t   TDC_VDDC_ThrottleReleaseLimitPerc;
+       uint8_t   TDC_MAWt;
+       uint8_t   TdcWaterfallCtl;
+       uint8_t   DTEAmbientTempBase;
+
+       uint32_t  DisplayCac;
+       uint32_t  BAPM_TEMP_GRADIENT;
+       uint16_t  BAPMTI_R[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
+       uint16_t  BAPMTI_RC[SMU74_DTE_ITERATIONS * SMU74_DTE_SOURCES * SMU74_DTE_SINKS];
+};
+
+
+
+struct polaris10_range_table {
+       uint32_t trans_lower_frequency; /* in 10khz */
+       uint32_t trans_upper_frequency;
 };
 
 struct polaris10_smumgr {
-       uint8_t *header;
-       uint8_t *mec_image;
-       struct polaris10_buffer_entry smu_buffer;
-       struct polaris10_buffer_entry header_buffer;
-       uint32_t soft_regs_start;
-       uint8_t *read_rrm_straps;
-       uint32_t read_drm_straps_mc_address_high;
-       uint32_t read_drm_straps_mc_address_low;
-       uint32_t acpi_optimization;
-       bool post_initial_boot;
+       struct smu7_smumgr smu7_data;
        uint8_t protected_mode;
-       uint8_t security_hard_key;
        struct polaris10_avfs  avfs;
+       SMU74_Discrete_DpmTable              smc_state_table;
+       struct SMU74_Discrete_Ulv            ulv_setting;
+       struct SMU74_Discrete_PmFuses  power_tune_table;
+       struct polaris10_range_table                range_table[NUM_SCLK_RANGE];
+       const struct polaris10_pt_defaults       *power_tune_defaults;
+       uint32_t                   activity_target[SMU74_MAX_LEVELS_GRAPHICS];
+       uint32_t                   bif_sclk_table[SMU74_MAX_LEVELS_LINK];
 };
 
 
-int polaris10_smum_init(struct pp_smumgr *smumgr);
-
-int polaris10_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t *value, uint32_t limit);
-int polaris10_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t value, uint32_t limit);
-int polaris10_copy_bytes_to_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address,
-                               const uint8_t *src, uint32_t byte_count, uint32_t limit);
-
 #endif
-
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c
new file mode 100644 (file)
index 0000000..6af744f
--- /dev/null
@@ -0,0 +1,589 @@
+/*
+ * Copyright 2015 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 "smumgr.h"
+#include "smu_ucode_xfer_vi.h"
+#include "smu/smu_7_1_3_d.h"
+#include "smu/smu_7_1_3_sh_mask.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "smu7_ppsmc.h"
+#include "smu7_smumgr.h"
+
+#define SMU7_SMC_SIZE 0x20000
+
+static int smu7_set_smc_sram_address(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t limit)
+{
+       PP_ASSERT_WITH_CODE((0 == (3 & smc_addr)), "SMC address must be 4 byte aligned.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((limit > (smc_addr + 3)), "SMC addr is beyond the SMC RAM area.", return -EINVAL);
+
+       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_11, smc_addr);
+       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0); /* on ci, SMC_IND_ACCESS_CNTL is different */
+       return 0;
+}
+
+
+int smu7_copy_bytes_from_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address, uint32_t *dest, uint32_t byte_count, uint32_t limit)
+{
+       uint32_t data;
+       uint32_t addr;
+       uint8_t *dest_byte;
+       uint8_t i, data_byte[4] = {0};
+       uint32_t *pdata = (uint32_t *)&data_byte;
+
+       PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+               smu7_read_smc_sram_dword(smumgr, addr, &data, limit);
+
+               *dest = PP_SMC_TO_HOST_UL(data);
+
+               dest += 1;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       if (byte_count) {
+               smu7_read_smc_sram_dword(smumgr, addr, &data, limit);
+               *pdata = PP_SMC_TO_HOST_UL(data);
+       /* Cast dest into byte type in dest_byte.  This way, we don't overflow if the allocated memory is not 4-byte aligned. */
+               dest_byte = (uint8_t *)dest;
+               for (i = 0; i < byte_count; i++)
+                       dest_byte[i] = data_byte[i];
+       }
+
+       return 0;
+}
+
+
+int smu7_copy_bytes_to_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address,
+                               const uint8_t *src, uint32_t byte_count, uint32_t limit)
+{
+       int result;
+       uint32_t data = 0;
+       uint32_t original_data;
+       uint32_t addr = 0;
+       uint32_t extra_shift;
+
+       PP_ASSERT_WITH_CODE((0 == (3 & smc_start_address)), "SMC address must be 4 byte aligned.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((limit > (smc_start_address + byte_count)), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+       addr = smc_start_address;
+
+       while (byte_count >= 4) {
+       /* Bytes are written into the SMC addres space with the MSB first. */
+               data = src[0] * 0x1000000 + src[1] * 0x10000 + src[2] * 0x100 + src[3];
+
+               result = smu7_set_smc_sram_address(smumgr, addr, limit);
+
+               if (0 != result)
+                       return result;
+
+               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, data);
+
+               src += 4;
+               byte_count -= 4;
+               addr += 4;
+       }
+
+       if (0 != byte_count) {
+
+               data = 0;
+
+               result = smu7_set_smc_sram_address(smumgr, addr, limit);
+
+               if (0 != result)
+                       return result;
+
+
+               original_data = cgs_read_register(smumgr->device, mmSMC_IND_DATA_11);
+
+               extra_shift = 8 * (4 - byte_count);
+
+               while (byte_count > 0) {
+                       /* Bytes are written into the SMC addres space with the MSB first. */
+                       data = (0x100 * data) + *src++;
+                       byte_count--;
+               }
+
+               data <<= extra_shift;
+
+               data |= (original_data & ~((~0UL) << extra_shift));
+
+               result = smu7_set_smc_sram_address(smumgr, addr, limit);
+
+               if (0 != result)
+                       return result;
+
+               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, data);
+       }
+
+       return 0;
+}
+
+
+int smu7_program_jump_on_start(struct pp_smumgr *smumgr)
+{
+       static const unsigned char data[4] = { 0xE0, 0x00, 0x80, 0x40 };
+
+       smu7_copy_bytes_to_smc(smumgr, 0x0, data, 4, sizeof(data)+1);
+
+       return 0;
+}
+
+bool smu7_is_smc_ram_running(struct pp_smumgr *smumgr)
+{
+       return ((0 == SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC, SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
+       && (0x20100 <= cgs_read_ind_register(smumgr->device, CGS_IND_REG__SMC, ixSMC_PC_C)));
+}
+
+int smu7_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
+{
+       int ret;
+
+       if (!smu7_is_smc_ram_running(smumgr))
+               return -EINVAL;
+
+
+       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
+
+       ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP);
+
+       if (ret != 1)
+               printk("\n failed to send pre message %x ret is %d \n",  msg, ret);
+
+       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
+
+       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
+
+       ret = SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP);
+
+       if (ret != 1)
+               printk("\n failed to send message %x ret is %d \n",  msg, ret);
+
+       return 0;
+}
+
+int smu7_send_msg_to_smc_without_waiting(struct pp_smumgr *smumgr, uint16_t msg)
+{
+       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
+
+       return 0;
+}
+
+int smu7_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr, uint16_t msg, uint32_t parameter)
+{
+       if (!smu7_is_smc_ram_running(smumgr)) {
+               return -EINVAL;
+       }
+
+       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
+
+       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
+
+       return smu7_send_msg_to_smc(smumgr, msg);
+}
+
+int smu7_send_msg_to_smc_with_parameter_without_waiting(struct pp_smumgr *smumgr, uint16_t msg, uint32_t parameter)
+{
+       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
+
+       return smu7_send_msg_to_smc_without_waiting(smumgr, msg);
+}
+
+int smu7_send_msg_to_smc_offset(struct pp_smumgr *smumgr)
+{
+       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, 0x20000);
+
+       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, PPSMC_MSG_Test);
+
+       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
+
+       if (1 != SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP))
+               printk("Failed to send Message.\n");
+
+       return 0;
+}
+
+int smu7_wait_for_smc_inactive(struct pp_smumgr *smumgr)
+{
+       if (!smu7_is_smc_ram_running(smumgr))
+               return -EINVAL;
+
+       SMUM_WAIT_VFPF_INDIRECT_FIELD(smumgr, SMC_IND, SMC_SYSCON_CLOCK_CNTL_0, cken, 0);
+       return 0;
+}
+
+
+enum cgs_ucode_id smu7_convert_fw_type_to_cgs(uint32_t fw_type)
+{
+       enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
+
+       switch (fw_type) {
+       case UCODE_ID_SMU:
+               result = CGS_UCODE_ID_SMU;
+               break;
+       case UCODE_ID_SMU_SK:
+               result = CGS_UCODE_ID_SMU_SK;
+               break;
+       case UCODE_ID_SDMA0:
+               result = CGS_UCODE_ID_SDMA0;
+               break;
+       case UCODE_ID_SDMA1:
+               result = CGS_UCODE_ID_SDMA1;
+               break;
+       case UCODE_ID_CP_CE:
+               result = CGS_UCODE_ID_CP_CE;
+               break;
+       case UCODE_ID_CP_PFP:
+               result = CGS_UCODE_ID_CP_PFP;
+               break;
+       case UCODE_ID_CP_ME:
+               result = CGS_UCODE_ID_CP_ME;
+               break;
+       case UCODE_ID_CP_MEC:
+               result = CGS_UCODE_ID_CP_MEC;
+               break;
+       case UCODE_ID_CP_MEC_JT1:
+               result = CGS_UCODE_ID_CP_MEC_JT1;
+               break;
+       case UCODE_ID_CP_MEC_JT2:
+               result = CGS_UCODE_ID_CP_MEC_JT2;
+               break;
+       case UCODE_ID_RLC_G:
+               result = CGS_UCODE_ID_RLC_G;
+               break;
+       default:
+               break;
+       }
+
+       return result;
+}
+
+
+int smu7_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t *value, uint32_t limit)
+{
+       int result;
+
+       result = smu7_set_smc_sram_address(smumgr, smc_addr, limit);
+
+       if (result)
+               return result;
+
+       *value = cgs_read_register(smumgr->device, mmSMC_IND_DATA_11);
+       return 0;
+}
+
+int smu7_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr, uint32_t value, uint32_t limit)
+{
+       int result;
+
+       result = smu7_set_smc_sram_address(smumgr, smc_addr, limit);
+
+       if (result)
+               return result;
+
+       cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, value);
+
+       return 0;
+}
+
+/* Convert the firmware type to SMU type mask. For MEC, we need to check all MEC related type */
+
+static uint32_t smu7_get_mask_for_firmware_type(uint32_t fw_type)
+{
+       uint32_t result = 0;
+
+       switch (fw_type) {
+       case UCODE_ID_SDMA0:
+               result = UCODE_ID_SDMA0_MASK;
+               break;
+       case UCODE_ID_SDMA1:
+               result = UCODE_ID_SDMA1_MASK;
+               break;
+       case UCODE_ID_CP_CE:
+               result = UCODE_ID_CP_CE_MASK;
+               break;
+       case UCODE_ID_CP_PFP:
+               result = UCODE_ID_CP_PFP_MASK;
+               break;
+       case UCODE_ID_CP_ME:
+               result = UCODE_ID_CP_ME_MASK;
+               break;
+       case UCODE_ID_CP_MEC:
+       case UCODE_ID_CP_MEC_JT1:
+       case UCODE_ID_CP_MEC_JT2:
+               result = UCODE_ID_CP_MEC_MASK;
+               break;
+       case UCODE_ID_RLC_G:
+               result = UCODE_ID_RLC_G_MASK;
+               break;
+       default:
+               printk("UCode type is out of range! \n");
+               result = 0;
+       }
+
+       return result;
+}
+
+static int smu7_populate_single_firmware_entry(struct pp_smumgr *smumgr,
+                                               uint32_t fw_type,
+                                               struct SMU_Entry *entry)
+{
+       int result = 0;
+       struct cgs_firmware_info info = {0};
+
+       result = cgs_get_firmware_info(smumgr->device,
+                               smu7_convert_fw_type_to_cgs(fw_type),
+                               &info);
+
+       if (!result) {
+               entry->version = info.version;
+               entry->id = (uint16_t)fw_type;
+               entry->image_addr_high = smu_upper_32_bits(info.mc_addr);
+               entry->image_addr_low = smu_lower_32_bits(info.mc_addr);
+               entry->meta_data_addr_high = 0;
+               entry->meta_data_addr_low = 0;
+               entry->data_size_byte = info.image_size;
+               entry->num_register_entries = 0;
+       }
+
+       if (fw_type == UCODE_ID_RLC_G)
+               entry->flags = 1;
+       else
+               entry->flags = 0;
+
+       return 0;
+}
+
+int smu7_request_smu_load_fw(struct pp_smumgr *smumgr)
+{
+       struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(smumgr->backend);
+       uint32_t fw_to_load;
+       int result = 0;
+       struct SMU_DRAMData_TOC *toc;
+
+       if (!smumgr->reload_fw) {
+               printk(KERN_INFO "[ powerplay ] skip reloading...\n");
+               return 0;
+       }
+
+       if (smu_data->soft_regs_start)
+               cgs_write_ind_register(smumgr->device, CGS_IND_REG__SMC,
+                                       smu_data->soft_regs_start + smum_get_offsetof(smumgr,
+                                       SMU_SoftRegisters, UcodeLoadStatus),
+                                       0x0);
+
+       if (smumgr->chip_id > CHIP_TOPAZ) { /* add support for Topaz */
+               smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_HI, smu_data->smu_buffer.mc_addr_high);
+               smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_LO, smu_data->smu_buffer.mc_addr_low);
+               fw_to_load = UCODE_ID_RLC_G_MASK
+                          + UCODE_ID_SDMA0_MASK
+                          + UCODE_ID_SDMA1_MASK
+                          + UCODE_ID_CP_CE_MASK
+                          + UCODE_ID_CP_ME_MASK
+                          + UCODE_ID_CP_PFP_MASK
+                          + UCODE_ID_CP_MEC_MASK;
+       } else {
+               fw_to_load = UCODE_ID_RLC_G_MASK
+                          + UCODE_ID_SDMA0_MASK
+                          + UCODE_ID_SDMA1_MASK
+                          + UCODE_ID_CP_CE_MASK
+                          + UCODE_ID_CP_ME_MASK
+                          + UCODE_ID_CP_PFP_MASK
+                          + UCODE_ID_CP_MEC_MASK
+                          + UCODE_ID_CP_MEC_JT1_MASK
+                          + UCODE_ID_CP_MEC_JT2_MASK;
+       }
+
+       toc = (struct SMU_DRAMData_TOC *)smu_data->header;
+       toc->num_entries = 0;
+       toc->structure_version = 1;
+
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_RLC_G, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_CE, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_PFP, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_ME, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_MEC, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_MEC_JT1, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_CP_MEC_JT2, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_SDMA0, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+       PP_ASSERT_WITH_CODE(0 == smu7_populate_single_firmware_entry(smumgr,
+                               UCODE_ID_SDMA1, &toc->entry[toc->num_entries++]),
+                               "Failed to Get Firmware Entry.", return -EINVAL);
+
+       smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_HI, smu_data->header_buffer.mc_addr_high);
+       smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_DRV_DRAM_ADDR_LO, smu_data->header_buffer.mc_addr_low);
+
+       if (smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_LoadUcodes, fw_to_load))
+               printk(KERN_ERR "Fail to Request SMU Load uCode");
+
+       return result;
+}
+
+/* Check if the FW has been loaded, SMU will not return if loading has not finished. */
+int smu7_check_fw_load_finish(struct pp_smumgr *smumgr, uint32_t fw_type)
+{
+       struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(smumgr->backend);
+       uint32_t fw_mask = smu7_get_mask_for_firmware_type(fw_type);
+       uint32_t ret;
+
+       ret = smum_wait_on_indirect_register(smumgr, mmSMC_IND_INDEX_11,
+                                       smu_data->soft_regs_start + smum_get_offsetof(smumgr,
+                                       SMU_SoftRegisters, UcodeLoadStatus),
+                                       fw_mask, fw_mask);
+
+       return ret;
+}
+
+int smu7_reload_firmware(struct pp_smumgr *smumgr)
+{
+       return smumgr->smumgr_funcs->start_smu(smumgr);
+}
+
+static int smu7_upload_smc_firmware_data(struct pp_smumgr *smumgr, uint32_t length, uint32_t *src, uint32_t limit)
+{
+       uint32_t byte_count = length;
+
+       PP_ASSERT_WITH_CODE((limit >= byte_count), "SMC address is beyond the SMC RAM area.", return -EINVAL);
+
+       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_11, 0x20000);
+       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 1);
+
+       for (; byte_count >= 4; byte_count -= 4)
+               cgs_write_register(smumgr->device, mmSMC_IND_DATA_11, *src++);
+
+       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0);
+
+       PP_ASSERT_WITH_CODE((0 == byte_count), "SMC size must be dividable by 4.", return -EINVAL);
+
+       return 0;
+}
+
+
+int smu7_upload_smu_firmware_image(struct pp_smumgr *smumgr)
+{
+       int result = 0;
+       struct smu7_smumgr *smu_data = (struct smu7_smumgr *)(smumgr->backend);
+
+       struct cgs_firmware_info info = {0};
+
+       if (smu_data->security_hard_key == 1)
+               cgs_get_firmware_info(smumgr->device,
+                       smu7_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
+       else
+               cgs_get_firmware_info(smumgr->device,
+                       smu7_convert_fw_type_to_cgs(UCODE_ID_SMU_SK), &info);
+
+       result = smu7_upload_smc_firmware_data(smumgr, info.image_size, (uint32_t *)info.kptr, SMU7_SMC_SIZE);
+
+       return result;
+}
+
+
+int smu7_init(struct pp_smumgr *smumgr)
+{
+       struct smu7_smumgr *smu_data;
+       uint8_t *internal_buf;
+       uint64_t mc_addr = 0;
+
+       /* Allocate memory for backend private data */
+       smu_data = (struct smu7_smumgr *)(smumgr->backend);
+       smu_data->header_buffer.data_size =
+                       ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
+       smu_data->smu_buffer.data_size = 200*4096;
+
+/* Allocate FW image data structure and header buffer and
+ * send the header buffer address to SMU */
+       smu_allocate_memory(smumgr->device,
+               smu_data->header_buffer.data_size,
+               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
+               PAGE_SIZE,
+               &mc_addr,
+               &smu_data->header_buffer.kaddr,
+               &smu_data->header_buffer.handle);
+
+       smu_data->header = smu_data->header_buffer.kaddr;
+       smu_data->header_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
+       smu_data->header_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
+
+       PP_ASSERT_WITH_CODE((NULL != smu_data->header),
+               "Out of memory.",
+               kfree(smumgr->backend);
+               cgs_free_gpu_mem(smumgr->device,
+               (cgs_handle_t)smu_data->header_buffer.handle);
+               return -EINVAL);
+
+       smu_allocate_memory(smumgr->device,
+               smu_data->smu_buffer.data_size,
+               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
+               PAGE_SIZE,
+               &mc_addr,
+               &smu_data->smu_buffer.kaddr,
+               &smu_data->smu_buffer.handle);
+
+       internal_buf = smu_data->smu_buffer.kaddr;
+       smu_data->smu_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
+       smu_data->smu_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
+
+       PP_ASSERT_WITH_CODE((NULL != internal_buf),
+               "Out of memory.",
+               kfree(smumgr->backend);
+               cgs_free_gpu_mem(smumgr->device,
+               (cgs_handle_t)smu_data->smu_buffer.handle);
+               return -EINVAL);
+
+       return 0;
+}
+
+
+int smu7_smu_fini(struct pp_smumgr *smumgr)
+{
+       if (smumgr->backend) {
+               kfree(smumgr->backend);
+               smumgr->backend = NULL;
+       }
+       cgs_rel_firmware(smumgr->device, CGS_UCODE_ID_SMU);
+       return 0;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.h b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.h
new file mode 100644 (file)
index 0000000..76352f2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2015 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 _SMU7_SMUMANAGER_H
+#define _SMU7_SMUMANAGER_H
+
+
+#include <pp_endian.h>
+
+#define SMC_RAM_END 0x40000
+#define mmSMC_IND_INDEX_11                              0x01AC
+#define mmSMC_IND_DATA_11                               0x01AD
+
+struct smu7_buffer_entry {
+       uint32_t data_size;
+       uint32_t mc_addr_low;
+       uint32_t mc_addr_high;
+       void *kaddr;
+       unsigned long  handle;
+};
+
+struct smu7_smumgr {
+       uint8_t *header;
+       uint8_t *mec_image;
+       struct smu7_buffer_entry smu_buffer;
+       struct smu7_buffer_entry header_buffer;
+
+       uint32_t                             soft_regs_start;
+       uint32_t                             dpm_table_start;
+       uint32_t                             mc_reg_table_start;
+       uint32_t                             fan_table_start;
+       uint32_t                             arb_table_start;
+       uint32_t                             ulv_setting_starts;
+       uint8_t                              security_hard_key;
+       uint32_t acpi_optimization;
+};
+
+
+int smu7_copy_bytes_from_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address,
+                               uint32_t *dest, uint32_t byte_count, uint32_t limit);
+int smu7_copy_bytes_to_smc(struct pp_smumgr *smumgr, uint32_t smc_start_address,
+                       const uint8_t *src, uint32_t byte_count, uint32_t limit);
+int smu7_program_jump_on_start(struct pp_smumgr *smumgr);
+bool smu7_is_smc_ram_running(struct pp_smumgr *smumgr);
+int smu7_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg);
+int smu7_send_msg_to_smc_without_waiting(struct pp_smumgr *smumgr, uint16_t msg);
+int smu7_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr, uint16_t msg,
+                                               uint32_t parameter);
+int smu7_send_msg_to_smc_with_parameter_without_waiting(struct pp_smumgr *smumgr,
+                                               uint16_t msg, uint32_t parameter);
+int smu7_send_msg_to_smc_offset(struct pp_smumgr *smumgr);
+int smu7_wait_for_smc_inactive(struct pp_smumgr *smumgr);
+
+enum cgs_ucode_id smu7_convert_fw_type_to_cgs(uint32_t fw_type);
+int smu7_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr,
+                                               uint32_t *value, uint32_t limit);
+int smu7_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smc_addr,
+                                               uint32_t value, uint32_t limit);
+
+int smu7_request_smu_load_fw(struct pp_smumgr *smumgr);
+int smu7_check_fw_load_finish(struct pp_smumgr *smumgr, uint32_t fw_type);
+int smu7_reload_firmware(struct pp_smumgr *smumgr);
+int smu7_upload_smu_firmware_image(struct pp_smumgr *smumgr);
+int smu7_init(struct pp_smumgr *smumgr);
+int smu7_smu_fini(struct pp_smumgr *smumgr);
+
+#endif
\ No newline at end of file
index 7723473..e5812aa 100644 (file)
 #include "smumgr.h"
 #include "cgs_common.h"
 #include "linux/delay.h"
-#include "cz_smumgr.h"
-#include "tonga_smumgr.h"
-#include "fiji_smumgr.h"
-#include "polaris10_smumgr.h"
+
 
 int smum_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
 {
@@ -47,7 +44,6 @@ int smum_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
        smumgr->device = pp_init->device;
        smumgr->chip_family = pp_init->chip_family;
        smumgr->chip_id = pp_init->chip_id;
-       smumgr->hw_revision = pp_init->rev_id;
        smumgr->usec_timeout = AMD_MAX_USEC_TIMEOUT;
        smumgr->reload_fw = 1;
        handle->smu_mgr = smumgr;
@@ -58,6 +54,9 @@ int smum_init(struct amd_pp_init *pp_init, struct pp_instance *handle)
                break;
        case AMDGPU_FAMILY_VI:
                switch (smumgr->chip_id) {
+               case CHIP_TOPAZ:
+                       iceland_smum_init(smumgr);
+                       break;
                case CHIP_TONGA:
                        tonga_smum_init(smumgr);
                        break;
@@ -87,6 +86,57 @@ int smum_fini(struct pp_smumgr *smumgr)
        return 0;
 }
 
+int smum_thermal_avfs_enable(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->thermal_avfs_enable)
+               return hwmgr->smumgr->smumgr_funcs->thermal_avfs_enable(hwmgr);
+
+       return 0;
+}
+
+int smum_thermal_setup_fan_table(struct pp_hwmgr *hwmgr,
+               void *input, void *output, void *storage, int result)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->thermal_setup_fan_table)
+               return hwmgr->smumgr->smumgr_funcs->thermal_setup_fan_table(hwmgr);
+
+       return 0;
+}
+
+int smum_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+
+       if (NULL != hwmgr->smumgr->smumgr_funcs->update_sclk_threshold)
+               return hwmgr->smumgr->smumgr_funcs->update_sclk_threshold(hwmgr);
+
+       return 0;
+}
+
+int smum_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+
+       if (NULL != hwmgr->smumgr->smumgr_funcs->update_smc_table)
+               return hwmgr->smumgr->smumgr_funcs->update_smc_table(hwmgr, type);
+
+       return 0;
+}
+
+uint32_t smum_get_offsetof(struct pp_smumgr *smumgr, uint32_t type, uint32_t member)
+{
+       if (NULL != smumgr->smumgr_funcs->get_offsetof)
+               return smumgr->smumgr_funcs->get_offsetof(type, member);
+
+       return 0;
+}
+
+int smum_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->process_firmware_header)
+               return hwmgr->smumgr->smumgr_funcs->process_firmware_header(hwmgr);
+       return 0;
+}
+
 int smum_get_argument(struct pp_smumgr *smumgr)
 {
        if (NULL != smumgr->smumgr_funcs->get_argument)
@@ -95,13 +145,20 @@ int smum_get_argument(struct pp_smumgr *smumgr)
        return 0;
 }
 
+uint32_t smum_get_mac_definition(struct pp_smumgr *smumgr, uint32_t value)
+{
+       if (NULL != smumgr->smumgr_funcs->get_mac_definition)
+               return smumgr->smumgr_funcs->get_mac_definition(value);
+
+       return 0;
+}
+
 int smum_download_powerplay_table(struct pp_smumgr *smumgr,
                                                                void **table)
 {
        if (NULL != smumgr->smumgr_funcs->download_pptable_settings)
                return smumgr->smumgr_funcs->download_pptable_settings(smumgr,
                                                                        table);
-
        return 0;
 }
 
@@ -268,3 +325,44 @@ int smu_free_memory(void *device, void *handle)
 
        return 0;
 }
+
+int smum_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->init_smc_table)
+               return hwmgr->smumgr->smumgr_funcs->init_smc_table(hwmgr);
+
+       return 0;
+}
+
+int smum_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->populate_all_graphic_levels)
+               return hwmgr->smumgr->smumgr_funcs->populate_all_graphic_levels(hwmgr);
+
+       return 0;
+}
+
+int smum_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->populate_all_memory_levels)
+               return hwmgr->smumgr->smumgr_funcs->populate_all_memory_levels(hwmgr);
+
+       return 0;
+}
+
+/*this interface is needed by island ci/vi */
+int smum_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->initialize_mc_reg_table)
+               return hwmgr->smumgr->smumgr_funcs->initialize_mc_reg_table(hwmgr);
+
+       return 0;
+}
+
+bool smum_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+       if (NULL != hwmgr->smumgr->smumgr_funcs->is_dpm_running)
+               return hwmgr->smumgr->smumgr_funcs->is_dpm_running(hwmgr);
+
+       return true;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c
new file mode 100644 (file)
index 0000000..de2a24d
--- /dev/null
@@ -0,0 +1,3206 @@
+/*
+ * Copyright 2015 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 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.
+ * 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.
+ *
+ *
+ */
+
+#include "tonga_smc.h"
+#include "smu7_dyn_defaults.h"
+
+#include "smu7_hwmgr.h"
+#include "hardwaremanager.h"
+#include "ppatomctrl.h"
+#include "pp_debug.h"
+#include "cgs_common.h"
+#include "atombios.h"
+#include "tonga_smumgr.h"
+#include "pppcielanes.h"
+#include "pp_endian.h"
+#include "smu7_ppsmc.h"
+
+#include "smu72_discrete.h"
+
+#include "smu/smu_7_1_2_d.h"
+#include "smu/smu_7_1_2_sh_mask.h"
+
+#include "gmc/gmc_8_1_d.h"
+#include "gmc/gmc_8_1_sh_mask.h"
+
+#include "bif/bif_5_0_d.h"
+#include "bif/bif_5_0_sh_mask.h"
+
+#include "dce/dce_10_0_d.h"
+#include "dce/dce_10_0_sh_mask.h"
+
+
+#define VOLTAGE_SCALE 4
+#define POWERTUNE_DEFAULT_SET_MAX    1
+#define VOLTAGE_VID_OFFSET_SCALE1   625
+#define VOLTAGE_VID_OFFSET_SCALE2   100
+#define MC_CG_ARB_FREQ_F1           0x0b
+#define VDDC_VDDCI_DELTA            200
+
+
+static const struct tonga_pt_defaults tonga_power_tune_data_set_array[POWERTUNE_DEFAULT_SET_MAX] = {
+/* sviLoadLIneEn, SviLoadLineVddC, TDC_VDDC_ThrottleReleaseLimitPerc,  TDC_MAWt,
+ * TdcWaterfallCtl, DTEAmbientTempBase, DisplayCac,        BAPM_TEMP_GRADIENT
+ */
+       {1,               0xF,             0xFD,                0x19,
+        5,               45,                 0,              0xB0000,
+        {0x79, 0x253, 0x25D, 0xAE, 0x72, 0x80, 0x83, 0x86, 0x6F, 0xC8,
+               0xC9, 0xC9, 0x2F, 0x4D, 0x61},
+        {0x17C, 0x172, 0x180, 0x1BC, 0x1B3, 0x1BD, 0x206, 0x200, 0x203,
+               0x25D, 0x25A, 0x255, 0x2C3, 0x2C5, 0x2B4}
+       },
+};
+
+/* [Fmin, Fmax, LDO_REFSEL, USE_FOR_LOW_FREQ] */
+static const uint16_t tonga_clock_stretcher_lookup_table[2][4] = {
+       {600, 1050, 3, 0},
+       {600, 1050, 6, 1}
+};
+
+/* [FF, SS] type, [] 4 voltage ranges,
+ * and [Floor Freq, Boundary Freq, VID min , VID max]
+ */
+static const uint32_t tonga_clock_stretcher_ddt_table[2][4][4] = {
+       { {265, 529, 120, 128}, {325, 650, 96, 119}, {430, 860, 32, 95}, {0, 0, 0, 31} },
+       { {275, 550, 104, 112}, {319, 638, 96, 103}, {360, 720, 64, 95}, {384, 768, 32, 63} }
+};
+
+/* [Use_For_Low_freq] value, [0%, 5%, 10%, 7.14%, 14.28%, 20%] */
+static const uint8_t tonga_clock_stretch_amount_conversion[2][6] = {
+       {0, 1, 3, 2, 4, 5},
+       {0, 2, 4, 5, 6, 5}
+};
+
+/* PPGen has the gain setting generated in x * 100 unit
+ * This function is to convert the unit to x * 4096(0x1000) unit.
+ *  This is the unit expected by SMC firmware
+ */
+
+
+static int tonga_get_dependecy_volt_by_clk(struct pp_hwmgr *hwmgr,
+       phm_ppt_v1_clock_voltage_dependency_table *allowed_clock_voltage_table,
+       uint32_t clock, SMU_VoltageLevel *voltage, uint32_t *mvdd)
+{
+       uint32_t i = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                          (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       /* clock - voltage dependency table is empty table */
+       if (allowed_clock_voltage_table->count == 0)
+               return -EINVAL;
+
+       for (i = 0; i < allowed_clock_voltage_table->count; i++) {
+               /* find first sclk bigger than request */
+               if (allowed_clock_voltage_table->entries[i].clk >= clock) {
+                       voltage->VddGfx = phm_get_voltage_index(
+                                       pptable_info->vddgfx_lookup_table,
+                               allowed_clock_voltage_table->entries[i].vddgfx);
+                       voltage->Vddc = phm_get_voltage_index(
+                                               pptable_info->vddc_lookup_table,
+                                 allowed_clock_voltage_table->entries[i].vddc);
+
+                       if (allowed_clock_voltage_table->entries[i].vddci)
+                               voltage->Vddci =
+                                       phm_get_voltage_id(&data->vddci_voltage_table, allowed_clock_voltage_table->entries[i].vddci);
+                       else
+                               voltage->Vddci =
+                                       phm_get_voltage_id(&data->vddci_voltage_table,
+                                               allowed_clock_voltage_table->entries[i].vddc - VDDC_VDDCI_DELTA);
+
+
+                       if (allowed_clock_voltage_table->entries[i].mvdd)
+                               *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i].mvdd;
+
+                       voltage->Phases = 1;
+                       return 0;
+               }
+       }
+
+       /* sclk is bigger than max sclk in the dependence table */
+       voltage->VddGfx = phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+               allowed_clock_voltage_table->entries[i-1].vddgfx);
+       voltage->Vddc = phm_get_voltage_index(pptable_info->vddc_lookup_table,
+               allowed_clock_voltage_table->entries[i-1].vddc);
+
+       if (allowed_clock_voltage_table->entries[i-1].vddci)
+               voltage->Vddci = phm_get_voltage_id(&data->vddci_voltage_table,
+                       allowed_clock_voltage_table->entries[i-1].vddci);
+
+       if (allowed_clock_voltage_table->entries[i-1].mvdd)
+               *mvdd = (uint32_t) allowed_clock_voltage_table->entries[i-1].mvdd;
+
+       return 0;
+}
+
+
+/**
+ * Vddc table preparation for SMC.
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    table     the SMC DPM table structure to be populated
+ * @return   always 0
+ */
+static int tonga_populate_smc_vddc_table(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       unsigned int count;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+               table->VddcLevelCount = data->vddc_voltage_table.count;
+               for (count = 0; count < table->VddcLevelCount; count++) {
+                       table->VddcTable[count] =
+                               PP_HOST_TO_SMC_US(data->vddc_voltage_table.entries[count].value * VOLTAGE_SCALE);
+               }
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VddcLevelCount);
+       }
+       return 0;
+}
+
+/**
+ * VddGfx table preparation for SMC.
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    table     the SMC DPM table structure to be populated
+ * @return   always 0
+ */
+static int tonga_populate_smc_vdd_gfx_table(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       unsigned int count;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+               table->VddGfxLevelCount = data->vddgfx_voltage_table.count;
+               for (count = 0; count < data->vddgfx_voltage_table.count; count++) {
+                       table->VddGfxTable[count] =
+                               PP_HOST_TO_SMC_US(data->vddgfx_voltage_table.entries[count].value * VOLTAGE_SCALE);
+               }
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VddGfxLevelCount);
+       }
+       return 0;
+}
+
+/**
+ * Vddci table preparation for SMC.
+ *
+ * @param    *hwmgr The address of the hardware manager.
+ * @param    *table The SMC DPM table structure to be populated.
+ * @return   0
+ */
+static int tonga_populate_smc_vdd_ci_table(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t count;
+
+       table->VddciLevelCount = data->vddci_voltage_table.count;
+       for (count = 0; count < table->VddciLevelCount; count++) {
+               if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+                       table->VddciTable[count] =
+                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+               } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+                       table->SmioTable1.Pattern[count].Voltage =
+                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level. */
+                       table->SmioTable1.Pattern[count].Smio =
+                               (uint8_t) count;
+                       table->Smio[count] |=
+                               data->vddci_voltage_table.entries[count].smio_low;
+                       table->VddciTable[count] =
+                               PP_HOST_TO_SMC_US(data->vddci_voltage_table.entries[count].value * VOLTAGE_SCALE);
+               }
+       }
+
+       table->SmioMask1 = data->vddci_voltage_table.mask_low;
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VddciLevelCount);
+
+       return 0;
+}
+
+/**
+ * Mvdd table preparation for SMC.
+ *
+ * @param    *hwmgr The address of the hardware manager.
+ * @param    *table The SMC DPM table structure to be populated.
+ * @return   0
+ */
+static int tonga_populate_smc_mvdd_table(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t count;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+               table->MvddLevelCount = data->mvdd_voltage_table.count;
+               for (count = 0; count < table->MvddLevelCount; count++) {
+                       table->SmioTable2.Pattern[count].Voltage =
+                               PP_HOST_TO_SMC_US(data->mvdd_voltage_table.entries[count].value * VOLTAGE_SCALE);
+                       /* Index into DpmTable.Smio. Drive bits from Smio entry to get this voltage level.*/
+                       table->SmioTable2.Pattern[count].Smio =
+                               (uint8_t) count;
+                       table->Smio[count] |=
+                               data->mvdd_voltage_table.entries[count].smio_low;
+               }
+               table->SmioMask2 = data->mvdd_voltage_table.mask_low;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->MvddLevelCount);
+       }
+
+       return 0;
+}
+
+/**
+ * Preparation of vddc and vddgfx CAC tables for SMC.
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    table     the SMC DPM table structure to be populated
+ * @return   always 0
+ */
+static int tonga_populate_cac_tables(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       uint32_t count;
+       uint8_t index = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_voltage_lookup_table *vddgfx_lookup_table =
+                                          pptable_info->vddgfx_lookup_table;
+       struct phm_ppt_v1_voltage_lookup_table *vddc_lookup_table =
+                                               pptable_info->vddc_lookup_table;
+
+       /* table is already swapped, so in order to use the value from it
+        * we need to swap it back.
+        */
+       uint32_t vddc_level_count = PP_SMC_TO_HOST_UL(table->VddcLevelCount);
+       uint32_t vddgfx_level_count = PP_SMC_TO_HOST_UL(table->VddGfxLevelCount);
+
+       for (count = 0; count < vddc_level_count; count++) {
+               /* We are populating vddc CAC data to BapmVddc table in split and merged mode */
+               index = phm_get_voltage_index(vddc_lookup_table,
+                       data->vddc_voltage_table.entries[count].value);
+               table->BapmVddcVidLoSidd[count] =
+                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
+               table->BapmVddcVidHiSidd[count] =
+                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
+               table->BapmVddcVidHiSidd2[count] =
+                       convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
+       }
+
+       if ((data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2)) {
+               /* We are populating vddgfx CAC data to BapmVddgfx table in split mode */
+               for (count = 0; count < vddgfx_level_count; count++) {
+                       index = phm_get_voltage_index(vddgfx_lookup_table,
+                               convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_mid));
+                       table->BapmVddGfxVidHiSidd2[count] =
+                               convert_to_vid(vddgfx_lookup_table->entries[index].us_cac_high);
+               }
+       } else {
+               for (count = 0; count < vddc_level_count; count++) {
+                       index = phm_get_voltage_index(vddc_lookup_table,
+                               data->vddc_voltage_table.entries[count].value);
+                       table->BapmVddGfxVidLoSidd[count] =
+                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_low);
+                       table->BapmVddGfxVidHiSidd[count] =
+                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_mid);
+                       table->BapmVddGfxVidHiSidd2[count] =
+                               convert_to_vid(vddc_lookup_table->entries[index].us_cac_high);
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Preparation of voltage tables for SMC.
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    table     the SMC DPM table structure to be populated
+ * @return   always 0
+ */
+
+static int tonga_populate_smc_voltage_tables(struct pp_hwmgr *hwmgr,
+       SMU72_Discrete_DpmTable *table)
+{
+       int result;
+
+       result = tonga_populate_smc_vddc_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+                       "can not populate VDDC voltage table to SMC",
+                       return -EINVAL);
+
+       result = tonga_populate_smc_vdd_ci_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+                       "can not populate VDDCI voltage table to SMC",
+                       return -EINVAL);
+
+       result = tonga_populate_smc_vdd_gfx_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+                       "can not populate VDDGFX voltage table to SMC",
+                       return -EINVAL);
+
+       result = tonga_populate_smc_mvdd_table(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+                       "can not populate MVDD voltage table to SMC",
+                       return -EINVAL);
+
+       result = tonga_populate_cac_tables(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+                       "can not populate CAC voltage tables to SMC",
+                       return -EINVAL);
+
+       return 0;
+}
+
+static int tonga_populate_ulv_level(struct pp_hwmgr *hwmgr,
+               struct SMU72_Discrete_Ulv *state)
+{
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       state->CcPwrDynRm = 0;
+       state->CcPwrDynRm1 = 0;
+
+       state->VddcOffset = (uint16_t) table_info->us_ulv_voltage_offset;
+       state->VddcOffsetVid = (uint8_t)(table_info->us_ulv_voltage_offset *
+                       VOLTAGE_VID_OFFSET_SCALE2 / VOLTAGE_VID_OFFSET_SCALE1);
+
+       state->VddcPhase = 1;
+
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(state->CcPwrDynRm1);
+       CONVERT_FROM_HOST_TO_SMC_US(state->VddcOffset);
+
+       return 0;
+}
+
+static int tonga_populate_ulv_state(struct pp_hwmgr *hwmgr,
+               struct SMU72_Discrete_DpmTable *table)
+{
+       return tonga_populate_ulv_level(hwmgr, &table->Ulv);
+}
+
+static int tonga_populate_smc_link_level(struct pp_hwmgr *hwmgr, SMU72_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t i;
+
+       /* Index (dpm_table->pcie_speed_table.count) is reserved for PCIE boot level. */
+       for (i = 0; i <= dpm_table->pcie_speed_table.count; i++) {
+               table->LinkLevel[i].PcieGenSpeed  =
+                       (uint8_t)dpm_table->pcie_speed_table.dpm_levels[i].value;
+               table->LinkLevel[i].PcieLaneCount =
+                       (uint8_t)encode_pcie_lane_width(dpm_table->pcie_speed_table.dpm_levels[i].param1);
+               table->LinkLevel[i].EnabledForActivity =
+                       1;
+               table->LinkLevel[i].SPC =
+                       (uint8_t)(data->pcie_spc_cap & 0xff);
+               table->LinkLevel[i].DownThreshold =
+                       PP_HOST_TO_SMC_UL(5);
+               table->LinkLevel[i].UpThreshold =
+                       PP_HOST_TO_SMC_UL(30);
+       }
+
+       smu_data->smc_state_table.LinkLevelCount =
+               (uint8_t)dpm_table->pcie_speed_table.count;
+       data->dpm_level_enable_mask.pcie_dpm_enable_mask =
+               phm_get_dpm_level_enable_mask_value(&dpm_table->pcie_speed_table);
+
+       return 0;
+}
+
+/**
+ * Calculates the SCLK dividers using the provided engine clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    engine_clock the engine clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int tonga_calculate_sclk_params(struct pp_hwmgr *hwmgr,
+               uint32_t engine_clock, SMU72_Discrete_GraphicsLevel *sclk)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       pp_atomctrl_clock_dividers_vi dividers;
+       uint32_t spll_func_cntl            = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_3          = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       uint32_t spll_func_cntl_4          = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       uint32_t cg_spll_spread_spectrum   = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       uint32_t cg_spll_spread_spectrum_2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       uint32_t    reference_clock;
+       uint32_t reference_divider;
+       uint32_t fbdiv;
+       int result;
+
+       /* get the engine clock dividers for this clock value*/
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr, engine_clock,  &dividers);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error retrieving Engine Clock dividers from VBIOS.", return result);
+
+       /* To get FBDIV we need to multiply this by 16384 and divide it by Fref.*/
+       reference_clock = atomctrl_get_reference_clock(hwmgr);
+
+       reference_divider = 1 + dividers.uc_pll_ref_div;
+
+       /* low 14 bits is fraction and high 12 bits is divider*/
+       fbdiv = dividers.ul_fb_div.ul_fb_divider & 0x3FFFFFF;
+
+       /* SPLL_FUNC_CNTL setup*/
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+               CG_SPLL_FUNC_CNTL, SPLL_REF_DIV, dividers.uc_pll_ref_div);
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl,
+               CG_SPLL_FUNC_CNTL, SPLL_PDIV_A,  dividers.uc_pll_post_div);
+
+       /* SPLL_FUNC_CNTL_3 setup*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+               CG_SPLL_FUNC_CNTL_3, SPLL_FB_DIV, fbdiv);
+
+       /* set to use fractional accumulation*/
+       spll_func_cntl_3 = PHM_SET_FIELD(spll_func_cntl_3,
+               CG_SPLL_FUNC_CNTL_3, SPLL_DITHEN, 1);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_EngineSpreadSpectrumSupport)) {
+               pp_atomctrl_internal_ss_info ss_info;
+
+               uint32_t vcoFreq = engine_clock * dividers.uc_pll_post_div;
+               if (0 == atomctrl_get_engine_clock_spread_spectrum(hwmgr, vcoFreq, &ss_info)) {
+                       /*
+                       * ss_info.speed_spectrum_percentage -- in unit of 0.01%
+                       * ss_info.speed_spectrum_rate -- in unit of khz
+                       */
+                       /* clks = reference_clock * 10 / (REFDIV + 1) / speed_spectrum_rate / 2 */
+                       uint32_t clkS = reference_clock * 5 / (reference_divider * ss_info.speed_spectrum_rate);
+
+                       /* clkv = 2 * D * fbdiv / NS */
+                       uint32_t clkV = 4 * ss_info.speed_spectrum_percentage * fbdiv / (clkS * 10000);
+
+                       cg_spll_spread_spectrum =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, CLKS, clkS);
+                       cg_spll_spread_spectrum =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum, CG_SPLL_SPREAD_SPECTRUM, SSEN, 1);
+                       cg_spll_spread_spectrum_2 =
+                               PHM_SET_FIELD(cg_spll_spread_spectrum_2, CG_SPLL_SPREAD_SPECTRUM_2, CLKV, clkV);
+               }
+       }
+
+       sclk->SclkFrequency        = engine_clock;
+       sclk->CgSpllFuncCntl3      = spll_func_cntl_3;
+       sclk->CgSpllFuncCntl4      = spll_func_cntl_4;
+       sclk->SpllSpreadSpectrum   = cg_spll_spread_spectrum;
+       sclk->SpllSpreadSpectrum2  = cg_spll_spread_spectrum_2;
+       sclk->SclkDid              = (uint8_t)dividers.pll_post_divider;
+
+       return 0;
+}
+
+/**
+ * Populates single SMC SCLK structure using the provided engine clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    engine_clock the engine clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int tonga_populate_single_graphic_level(struct pp_hwmgr *hwmgr,
+                                               uint32_t engine_clock,
+                               uint16_t sclk_activity_level_threshold,
+                               SMU72_Discrete_GraphicsLevel *graphic_level)
+{
+       int result;
+       uint32_t mvdd;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                           (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       result = tonga_calculate_sclk_params(hwmgr, engine_clock, graphic_level);
+
+       /* populate graphics levels*/
+       result = tonga_get_dependecy_volt_by_clk(hwmgr,
+               pptable_info->vdd_dep_on_sclk, engine_clock,
+               &graphic_level->MinVoltage, &mvdd);
+       PP_ASSERT_WITH_CODE((!result),
+               "can not find VDDC voltage value for VDDC "
+               "engine clock dependency table", return result);
+
+       /* SCLK frequency in units of 10KHz*/
+       graphic_level->SclkFrequency = engine_clock;
+       /* Indicates maximum activity level for this performance level. 50% for now*/
+       graphic_level->ActivityLevel = sclk_activity_level_threshold;
+
+       graphic_level->CcPwrDynRm = 0;
+       graphic_level->CcPwrDynRm1 = 0;
+       /* this level can be used if activity is high enough.*/
+       graphic_level->EnabledForActivity = 0;
+       /* this level can be used for throttling.*/
+       graphic_level->EnabledForThrottle = 1;
+       graphic_level->UpHyst = 0;
+       graphic_level->DownHyst = 0;
+       graphic_level->VoltageDownHyst = 0;
+       graphic_level->PowerThrottle = 0;
+
+       data->display_timing.min_clock_in_sr =
+                       hwmgr->display_config.min_core_set_clock_in_sr;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkDeepSleep))
+               graphic_level->DeepSleepDivId =
+                               smu7_get_sleep_divider_id_from_clock(engine_clock,
+                                               data->display_timing.min_clock_in_sr);
+
+       /* Default to slow, highest DPM level will be set to PPSMC_DISPLAY_WATERMARK_LOW later.*/
+       graphic_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       if (!result) {
+               /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVoltage);*/
+               /* CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->MinVddcPhases);*/
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_US(graphic_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl3);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CgSpllFuncCntl4);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->SpllSpreadSpectrum2);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm);
+               CONVERT_FROM_HOST_TO_SMC_UL(graphic_level->CcPwrDynRm1);
+       }
+
+       return result;
+}
+
+/**
+ * Populates all SMC SCLK levels' structure based on the trimmed allowed dpm engine clock states
+ *
+ * @param    hwmgr      the address of the hardware manager
+ */
+int tonga_populate_all_graphic_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *pptable_info = (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       struct phm_ppt_v1_pcie_table *pcie_table = pptable_info->pcie_table;
+       uint8_t pcie_entry_count = (uint8_t) data->dpm_table.pcie_speed_table.count;
+       uint32_t level_array_address = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU72_Discrete_DpmTable, GraphicsLevel);
+
+       uint32_t level_array_size = sizeof(SMU72_Discrete_GraphicsLevel) *
+                                               SMU72_MAX_LEVELS_GRAPHICS;
+
+       SMU72_Discrete_GraphicsLevel *levels = smu_data->smc_state_table.GraphicsLevel;
+
+       uint32_t i, max_entry;
+       uint8_t highest_pcie_level_enabled = 0;
+       uint8_t lowest_pcie_level_enabled = 0, mid_pcie_level_enabled = 0;
+       uint8_t count = 0;
+       int result = 0;
+
+       memset(levels, 0x00, level_array_size);
+
+       for (i = 0; i < dpm_table->sclk_table.count; i++) {
+               result = tonga_populate_single_graphic_level(hwmgr,
+                                       dpm_table->sclk_table.dpm_levels[i].value,
+                                       (uint16_t)smu_data->activity_target[i],
+                                       &(smu_data->smc_state_table.GraphicsLevel[i]));
+               if (result != 0)
+                       return result;
+
+               /* Making sure only DPM level 0-1 have Deep Sleep Div ID populated. */
+               if (i > 1)
+                       smu_data->smc_state_table.GraphicsLevel[i].DeepSleepDivId = 0;
+       }
+
+       /* Only enable level 0 for now. */
+       smu_data->smc_state_table.GraphicsLevel[0].EnabledForActivity = 1;
+
+       /* set highest level watermark to high */
+       if (dpm_table->sclk_table.count > 1)
+               smu_data->smc_state_table.GraphicsLevel[dpm_table->sclk_table.count-1].DisplayWatermark =
+                       PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       smu_data->smc_state_table.GraphicsDpmLevelCount =
+               (uint8_t)dpm_table->sclk_table.count;
+       data->dpm_level_enable_mask.sclk_dpm_enable_mask =
+               phm_get_dpm_level_enable_mask_value(&dpm_table->sclk_table);
+
+       if (pcie_table != NULL) {
+               PP_ASSERT_WITH_CODE((pcie_entry_count >= 1),
+                       "There must be 1 or more PCIE levels defined in PPTable.",
+                       return -EINVAL);
+               max_entry = pcie_entry_count - 1; /* for indexing, we need to decrement by 1.*/
+               for (i = 0; i < dpm_table->sclk_table.count; i++) {
+                       smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel =
+                               (uint8_t) ((i < max_entry) ? i : max_entry);
+               }
+       } else {
+               if (0 == data->dpm_level_enable_mask.pcie_dpm_enable_mask)
+                       printk(KERN_ERR "[ powerplay ] Pcie Dpm Enablemask is 0 !");
+
+               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                       (1<<(highest_pcie_level_enabled+1))) != 0)) {
+                       highest_pcie_level_enabled++;
+               }
+
+               while (data->dpm_level_enable_mask.pcie_dpm_enable_mask &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                       (1<<lowest_pcie_level_enabled)) == 0)) {
+                       lowest_pcie_level_enabled++;
+               }
+
+               while ((count < highest_pcie_level_enabled) &&
+                               ((data->dpm_level_enable_mask.pcie_dpm_enable_mask &
+                                       (1<<(lowest_pcie_level_enabled+1+count))) == 0)) {
+                       count++;
+               }
+               mid_pcie_level_enabled = (lowest_pcie_level_enabled+1+count) < highest_pcie_level_enabled ?
+                       (lowest_pcie_level_enabled+1+count) : highest_pcie_level_enabled;
+
+
+               /* set pcieDpmLevel to highest_pcie_level_enabled*/
+               for (i = 2; i < dpm_table->sclk_table.count; i++)
+                       smu_data->smc_state_table.GraphicsLevel[i].pcieDpmLevel = highest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to lowest_pcie_level_enabled*/
+               smu_data->smc_state_table.GraphicsLevel[0].pcieDpmLevel = lowest_pcie_level_enabled;
+
+               /* set pcieDpmLevel to mid_pcie_level_enabled*/
+               smu_data->smc_state_table.GraphicsLevel[1].pcieDpmLevel = mid_pcie_level_enabled;
+       }
+       /* level count will send to smc once at init smc table and never change*/
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr, level_array_address,
+                               (uint8_t *)levels, (uint32_t)level_array_size,
+                                                               SMC_RAM_END);
+
+       return result;
+}
+
+/**
+ * Populates the SMC MCLK structure using the provided memory clock
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    memory_clock the memory clock to use to populate the structure
+ * @param    sclk        the SMC SCLK structure to be populated
+ */
+static int tonga_calculate_mclk_params(
+               struct pp_hwmgr *hwmgr,
+               uint32_t memory_clock,
+               SMU72_Discrete_MemoryLevel *mclk,
+               bool strobe_mode,
+               bool dllStateOn
+               )
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       uint32_t dll_cntl = data->clock_registers.vDLL_CNTL;
+       uint32_t mclk_pwrmgt_cntl = data->clock_registers.vMCLK_PWRMGT_CNTL;
+       uint32_t mpll_ad_func_cntl = data->clock_registers.vMPLL_AD_FUNC_CNTL;
+       uint32_t mpll_dq_func_cntl = data->clock_registers.vMPLL_DQ_FUNC_CNTL;
+       uint32_t mpll_func_cntl = data->clock_registers.vMPLL_FUNC_CNTL;
+       uint32_t mpll_func_cntl_1 = data->clock_registers.vMPLL_FUNC_CNTL_1;
+       uint32_t mpll_func_cntl_2 = data->clock_registers.vMPLL_FUNC_CNTL_2;
+       uint32_t mpll_ss1 = data->clock_registers.vMPLL_SS1;
+       uint32_t mpll_ss2 = data->clock_registers.vMPLL_SS2;
+
+       pp_atomctrl_memory_clock_param mpll_param;
+       int result;
+
+       result = atomctrl_get_memory_pll_dividers_si(hwmgr,
+                               memory_clock, &mpll_param, strobe_mode);
+       PP_ASSERT_WITH_CODE(
+                       !result,
+                       "Error retrieving Memory Clock Parameters from VBIOS.",
+                       return result);
+
+       /* MPLL_FUNC_CNTL setup*/
+       mpll_func_cntl = PHM_SET_FIELD(mpll_func_cntl, MPLL_FUNC_CNTL, BWCTRL,
+                                       mpll_param.bw_ctrl);
+
+       /* MPLL_FUNC_CNTL_1 setup*/
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                       MPLL_FUNC_CNTL_1, CLKF,
+                                       mpll_param.mpll_fb_divider.cl_kf);
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                       MPLL_FUNC_CNTL_1, CLKFRAC,
+                                       mpll_param.mpll_fb_divider.clk_frac);
+       mpll_func_cntl_1  = PHM_SET_FIELD(mpll_func_cntl_1,
+                                               MPLL_FUNC_CNTL_1, VCO_MODE,
+                                               mpll_param.vco_mode);
+
+       /* MPLL_AD_FUNC_CNTL setup*/
+       mpll_ad_func_cntl = PHM_SET_FIELD(mpll_ad_func_cntl,
+                                       MPLL_AD_FUNC_CNTL, YCLK_POST_DIV,
+                                       mpll_param.mpll_post_divider);
+
+       if (data->is_memory_gddr5) {
+               /* MPLL_DQ_FUNC_CNTL setup*/
+               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
+                                               MPLL_DQ_FUNC_CNTL, YCLK_SEL,
+                                               mpll_param.yclk_sel);
+               mpll_dq_func_cntl  = PHM_SET_FIELD(mpll_dq_func_cntl,
+                                               MPLL_DQ_FUNC_CNTL, YCLK_POST_DIV,
+                                               mpll_param.mpll_post_divider);
+       }
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_MemorySpreadSpectrumSupport)) {
+               /*
+                ************************************
+                Fref = Reference Frequency
+                NF = Feedback divider ratio
+                NR = Reference divider ratio
+                Fnom = Nominal VCO output frequency = Fref * NF / NR
+                Fs = Spreading Rate
+                D = Percentage down-spread / 2
+                Fint = Reference input frequency to PFD = Fref / NR
+                NS = Spreading rate divider ratio = int(Fint / (2 * Fs))
+                CLKS = NS - 1 = ISS_STEP_NUM[11:0]
+                NV = D * Fs / Fnom * 4 * ((Fnom/Fref * NR) ^ 2)
+                CLKV = 65536 * NV = ISS_STEP_SIZE[25:0]
+                *************************************
+                */
+               pp_atomctrl_internal_ss_info ss_info;
+               uint32_t freq_nom;
+               uint32_t tmp;
+               uint32_t reference_clock = atomctrl_get_mpll_reference_clock(hwmgr);
+
+               /* for GDDR5 for all modes and DDR3 */
+               if (1 == mpll_param.qdr)
+                       freq_nom = memory_clock * 4 * (1 << mpll_param.mpll_post_divider);
+               else
+                       freq_nom = memory_clock * 2 * (1 << mpll_param.mpll_post_divider);
+
+               /* tmp = (freq_nom / reference_clock * reference_divider) ^ 2  Note: S.I. reference_divider = 1*/
+               tmp = (freq_nom / reference_clock);
+               tmp = tmp * tmp;
+
+               if (0 == atomctrl_get_memory_clock_spread_spectrum(hwmgr, freq_nom, &ss_info)) {
+                       /* ss_info.speed_spectrum_percentage -- in unit of 0.01% */
+                       /* ss.Info.speed_spectrum_rate -- in unit of khz */
+                       /* CLKS = reference_clock / (2 * speed_spectrum_rate * reference_divider) * 10 */
+                       /*     = reference_clock * 5 / speed_spectrum_rate */
+                       uint32_t clks = reference_clock * 5 / ss_info.speed_spectrum_rate;
+
+                       /* CLKV = 65536 * speed_spectrum_percentage / 2 * spreadSpecrumRate / freq_nom * 4 / 100000 * ((freq_nom / reference_clock) ^ 2) */
+                       /*     = 131 * speed_spectrum_percentage * speed_spectrum_rate / 100 * ((freq_nom / reference_clock) ^ 2) / freq_nom */
+                       uint32_t clkv =
+                               (uint32_t)((((131 * ss_info.speed_spectrum_percentage *
+                                                       ss_info.speed_spectrum_rate) / 100) * tmp) / freq_nom);
+
+                       mpll_ss1 = PHM_SET_FIELD(mpll_ss1, MPLL_SS1, CLKV, clkv);
+                       mpll_ss2 = PHM_SET_FIELD(mpll_ss2, MPLL_SS2, CLKS, clks);
+               }
+       }
+
+       /* MCLK_PWRMGT_CNTL setup */
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, DLL_SPEED, mpll_param.dll_speed);
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, dllStateOn);
+       mclk_pwrmgt_cntl = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, dllStateOn);
+
+       /* Save the result data to outpupt memory level structure */
+       mclk->MclkFrequency   = memory_clock;
+       mclk->MpllFuncCntl    = mpll_func_cntl;
+       mclk->MpllFuncCntl_1  = mpll_func_cntl_1;
+       mclk->MpllFuncCntl_2  = mpll_func_cntl_2;
+       mclk->MpllAdFuncCntl  = mpll_ad_func_cntl;
+       mclk->MpllDqFuncCntl  = mpll_dq_func_cntl;
+       mclk->MclkPwrmgtCntl  = mclk_pwrmgt_cntl;
+       mclk->DllCntl         = dll_cntl;
+       mclk->MpllSs1         = mpll_ss1;
+       mclk->MpllSs2         = mpll_ss2;
+
+       return 0;
+}
+
+static uint8_t tonga_get_mclk_frequency_ratio(uint32_t memory_clock,
+               bool strobe_mode)
+{
+       uint8_t mc_para_index;
+
+       if (strobe_mode) {
+               if (memory_clock < 12500)
+                       mc_para_index = 0x00;
+               else if (memory_clock > 47500)
+                       mc_para_index = 0x0f;
+               else
+                       mc_para_index = (uint8_t)((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 = (uint8_t)((memory_clock - 60000) / 5000);
+       }
+
+       return mc_para_index;
+}
+
+static uint8_t tonga_get_ddr3_mclk_frequency_ratio(uint32_t memory_clock)
+{
+       uint8_t mc_para_index;
+
+       if (memory_clock < 10000)
+               mc_para_index = 0;
+       else if (memory_clock >= 80000)
+               mc_para_index = 0x0f;
+       else
+               mc_para_index = (uint8_t)((memory_clock - 10000) / 5000 + 1);
+
+       return mc_para_index;
+}
+
+
+static int tonga_populate_single_memory_level(
+               struct pp_hwmgr *hwmgr,
+               uint32_t memory_clock,
+               SMU72_Discrete_MemoryLevel *memory_level
+               )
+{
+       uint32_t mvdd = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                         (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       int result = 0;
+       bool dll_state_on;
+       struct cgs_display_info info = {0};
+       uint32_t mclk_edc_wr_enable_threshold = 40000;
+       uint32_t mclk_stutter_mode_threshold = 30000;
+       uint32_t mclk_edc_enable_threshold = 40000;
+       uint32_t mclk_strobe_mode_threshold = 40000;
+
+       if (NULL != pptable_info->vdd_dep_on_mclk) {
+               result = tonga_get_dependecy_volt_by_clk(hwmgr,
+                               pptable_info->vdd_dep_on_mclk,
+                               memory_clock,
+                               &memory_level->MinVoltage, &mvdd);
+               PP_ASSERT_WITH_CODE(
+                       !result,
+                       "can not find MinVddc voltage value from memory VDDC "
+                       "voltage dependency table",
+                       return result);
+       }
+
+       if (data->mvdd_control == SMU7_VOLTAGE_CONTROL_NONE)
+               memory_level->MinMvdd = data->vbios_boot_state.mvdd_bootup_value;
+       else
+               memory_level->MinMvdd = mvdd;
+
+       memory_level->EnabledForThrottle = 1;
+       memory_level->EnabledForActivity = 0;
+       memory_level->UpHyst = 0;
+       memory_level->DownHyst = 100;
+       memory_level->VoltageDownHyst = 0;
+
+       /* Indicates maximum activity level for this performance level.*/
+       memory_level->ActivityLevel = (uint16_t)data->mclk_activity_target;
+       memory_level->StutterEnable = 0;
+       memory_level->StrobeEnable = 0;
+       memory_level->EdcReadEnable = 0;
+       memory_level->EdcWriteEnable = 0;
+       memory_level->RttEnable = 0;
+
+       /* default set to low watermark. Highest level will be set to high later.*/
+       memory_level->DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+
+       cgs_get_active_displays_info(hwmgr->device, &info);
+       data->display_timing.num_existing_displays = info.display_count;
+
+       if ((mclk_stutter_mode_threshold != 0) &&
+           (memory_clock <= mclk_stutter_mode_threshold) &&
+           (!data->is_uvd_enabled)
+           && (PHM_READ_FIELD(hwmgr->device, DPG_PIPE_STUTTER_CONTROL, STUTTER_ENABLE) & 0x1)
+           && (data->display_timing.num_existing_displays <= 2)
+           && (data->display_timing.num_existing_displays != 0))
+               memory_level->StutterEnable = 1;
+
+       /* decide strobe mode*/
+       memory_level->StrobeEnable = (mclk_strobe_mode_threshold != 0) &&
+               (memory_clock <= mclk_strobe_mode_threshold);
+
+       /* decide EDC mode and memory clock ratio*/
+       if (data->is_memory_gddr5) {
+               memory_level->StrobeRatio = tonga_get_mclk_frequency_ratio(memory_clock,
+                                       memory_level->StrobeEnable);
+
+               if ((mclk_edc_enable_threshold != 0) &&
+                               (memory_clock > mclk_edc_enable_threshold)) {
+                       memory_level->EdcReadEnable = 1;
+               }
+
+               if ((mclk_edc_wr_enable_threshold != 0) &&
+                               (memory_clock > mclk_edc_wr_enable_threshold)) {
+                       memory_level->EdcWriteEnable = 1;
+               }
+
+               if (memory_level->StrobeEnable) {
+                       if (tonga_get_mclk_frequency_ratio(memory_clock, 1) >=
+                                       ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC7) >> 16) & 0xf)) {
+                               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+                       } else {
+                               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC6) >> 1) & 0x1) ? 1 : 0;
+                       }
+
+               } else {
+                       dll_state_on = data->dll_default_on;
+               }
+       } else {
+               memory_level->StrobeRatio =
+                       tonga_get_ddr3_mclk_frequency_ratio(memory_clock);
+               dll_state_on = ((cgs_read_register(hwmgr->device, mmMC_SEQ_MISC5) >> 1) & 0x1) ? 1 : 0;
+       }
+
+       result = tonga_calculate_mclk_params(hwmgr,
+               memory_clock, memory_level, memory_level->StrobeEnable, dll_state_on);
+
+       if (!result) {
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MinMvdd);
+               /* MCLK frequency in units of 10KHz*/
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkFrequency);
+               /* Indicates maximum activity level for this performance level.*/
+               CONVERT_FROM_HOST_TO_SMC_US(memory_level->ActivityLevel);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_1);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllFuncCntl_2);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllAdFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllDqFuncCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MclkPwrmgtCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->DllCntl);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs1);
+               CONVERT_FROM_HOST_TO_SMC_UL(memory_level->MpllSs2);
+       }
+
+       return result;
+}
+
+int tonga_populate_all_memory_levels(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data =
+                       (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       struct smu7_dpm_table *dpm_table = &data->dpm_table;
+       int result;
+
+       /* populate MCLK dpm table to SMU7 */
+       uint32_t level_array_address =
+                               smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU72_Discrete_DpmTable, MemoryLevel);
+       uint32_t level_array_size =
+                               sizeof(SMU72_Discrete_MemoryLevel) *
+                               SMU72_MAX_LEVELS_MEMORY;
+       SMU72_Discrete_MemoryLevel *levels =
+                               smu_data->smc_state_table.MemoryLevel;
+       uint32_t i;
+
+       memset(levels, 0x00, level_array_size);
+
+       for (i = 0; i < dpm_table->mclk_table.count; i++) {
+               PP_ASSERT_WITH_CODE((0 != dpm_table->mclk_table.dpm_levels[i].value),
+                       "can not populate memory level as memory clock is zero",
+                       return -EINVAL);
+               result = tonga_populate_single_memory_level(
+                               hwmgr,
+                               dpm_table->mclk_table.dpm_levels[i].value,
+                               &(smu_data->smc_state_table.MemoryLevel[i]));
+               if (result)
+                       return result;
+       }
+
+       /* Only enable level 0 for now.*/
+       smu_data->smc_state_table.MemoryLevel[0].EnabledForActivity = 1;
+
+       /*
+       * in order to prevent MC activity from stutter mode to push DPM up.
+       * the UVD change complements this by putting the MCLK in a higher state
+       * by default such that we are not effected by up threshold or and MCLK DPM latency.
+       */
+       smu_data->smc_state_table.MemoryLevel[0].ActivityLevel = 0x1F;
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.MemoryLevel[0].ActivityLevel);
+
+       smu_data->smc_state_table.MemoryDpmLevelCount = (uint8_t)dpm_table->mclk_table.count;
+       data->dpm_level_enable_mask.mclk_dpm_enable_mask = phm_get_dpm_level_enable_mask_value(&dpm_table->mclk_table);
+       /* set highest level watermark to high*/
+       smu_data->smc_state_table.MemoryLevel[dpm_table->mclk_table.count-1].DisplayWatermark = PPSMC_DISPLAY_WATERMARK_HIGH;
+
+       /* level count will send to smc once at init smc table and never change*/
+       result = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+               level_array_address, (uint8_t *)levels, (uint32_t)level_array_size,
+               SMC_RAM_END);
+
+       return result;
+}
+
+static int tonga_populate_mvdd_value(struct pp_hwmgr *hwmgr,
+                               uint32_t mclk, SMIO_Pattern *smio_pattern)
+{
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint32_t i = 0;
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->mvdd_control) {
+               /* find mvdd value which clock is more than request */
+               for (i = 0; i < table_info->vdd_dep_on_mclk->count; i++) {
+                       if (mclk <= table_info->vdd_dep_on_mclk->entries[i].clk) {
+                               /* Always round to higher voltage. */
+                               smio_pattern->Voltage =
+                                     data->mvdd_voltage_table.entries[i].value;
+                               break;
+                       }
+               }
+
+               PP_ASSERT_WITH_CODE(i < table_info->vdd_dep_on_mclk->count,
+                       "MVDD Voltage is outside the supported range.",
+                       return -EINVAL);
+       } else {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+static int tonga_populate_smc_acpi_level(struct pp_hwmgr *hwmgr,
+       SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       const struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct pp_atomctrl_clock_dividers_vi dividers;
+
+       SMIO_Pattern voltage_level;
+       uint32_t spll_func_cntl    = data->clock_registers.vCG_SPLL_FUNC_CNTL;
+       uint32_t spll_func_cntl_2  = data->clock_registers.vCG_SPLL_FUNC_CNTL_2;
+       uint32_t dll_cntl          = data->clock_registers.vDLL_CNTL;
+       uint32_t mclk_pwrmgt_cntl  = data->clock_registers.vMCLK_PWRMGT_CNTL;
+
+       /* The ACPI state should not do DPM on DC (or ever).*/
+       table->ACPILevel.Flags &= ~PPSMC_SWSTATE_FLAG_DC;
+
+       table->ACPILevel.MinVoltage =
+                       smu_data->smc_state_table.GraphicsLevel[0].MinVoltage;
+
+       /* assign zero for now*/
+       table->ACPILevel.SclkFrequency = atomctrl_get_reference_clock(hwmgr);
+
+       /* get the engine clock dividers for this clock value*/
+       result = atomctrl_get_engine_pll_dividers_vi(hwmgr,
+               table->ACPILevel.SclkFrequency,  &dividers);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error retrieving Engine Clock dividers from VBIOS.",
+               return result);
+
+       /* divider ID for required SCLK*/
+       table->ACPILevel.SclkDid = (uint8_t)dividers.pll_post_divider;
+       table->ACPILevel.DisplayWatermark = PPSMC_DISPLAY_WATERMARK_LOW;
+       table->ACPILevel.DeepSleepDivId = 0;
+
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                                       SPLL_PWRON, 0);
+       spll_func_cntl = PHM_SET_FIELD(spll_func_cntl, CG_SPLL_FUNC_CNTL,
+                                               SPLL_RESET, 1);
+       spll_func_cntl_2 = PHM_SET_FIELD(spll_func_cntl_2, CG_SPLL_FUNC_CNTL_2,
+                                               SCLK_MUX_SEL, 4);
+
+       table->ACPILevel.CgSpllFuncCntl = spll_func_cntl;
+       table->ACPILevel.CgSpllFuncCntl2 = spll_func_cntl_2;
+       table->ACPILevel.CgSpllFuncCntl3 = data->clock_registers.vCG_SPLL_FUNC_CNTL_3;
+       table->ACPILevel.CgSpllFuncCntl4 = data->clock_registers.vCG_SPLL_FUNC_CNTL_4;
+       table->ACPILevel.SpllSpreadSpectrum = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM;
+       table->ACPILevel.SpllSpreadSpectrum2 = data->clock_registers.vCG_SPLL_SPREAD_SPECTRUM_2;
+       table->ACPILevel.CcPwrDynRm = 0;
+       table->ACPILevel.CcPwrDynRm1 = 0;
+
+
+       /* For various features to be enabled/disabled while this level is active.*/
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.Flags);
+       /* SCLK frequency in units of 10KHz*/
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SclkFrequency);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl3);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CgSpllFuncCntl4);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.SpllSpreadSpectrum2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->ACPILevel.CcPwrDynRm1);
+
+       /* table->MemoryACPILevel.MinVddcPhases = table->ACPILevel.MinVddcPhases;*/
+       table->MemoryACPILevel.MinVoltage =
+                           smu_data->smc_state_table.MemoryLevel[0].MinVoltage;
+
+       /*  CONVERT_FROM_HOST_TO_SMC_UL(table->MemoryACPILevel.MinVoltage);*/
+
+       if (0 == tonga_populate_mvdd_value(hwmgr, 0, &voltage_level))
+               table->MemoryACPILevel.MinMvdd =
+                       PP_HOST_TO_SMC_UL(voltage_level.Voltage * VOLTAGE_SCALE);
+       else
+               table->MemoryACPILevel.MinMvdd = 0;
+
+       /* Force reset on DLL*/
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_RESET, 0x1);
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_RESET, 0x1);
+
+       /* Disable DLL in ACPIState*/
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK0_PDNB, 0);
+       mclk_pwrmgt_cntl    = PHM_SET_FIELD(mclk_pwrmgt_cntl,
+               MCLK_PWRMGT_CNTL, MRDCK1_PDNB, 0);
+
+       /* Enable DLL bypass signal*/
+       dll_cntl            = PHM_SET_FIELD(dll_cntl,
+               DLL_CNTL, MRDCK0_BYPASS, 0);
+       dll_cntl            = PHM_SET_FIELD(dll_cntl,
+               DLL_CNTL, MRDCK1_BYPASS, 0);
+
+       table->MemoryACPILevel.DllCntl            =
+               PP_HOST_TO_SMC_UL(dll_cntl);
+       table->MemoryACPILevel.MclkPwrmgtCntl     =
+               PP_HOST_TO_SMC_UL(mclk_pwrmgt_cntl);
+       table->MemoryACPILevel.MpllAdFuncCntl     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_AD_FUNC_CNTL);
+       table->MemoryACPILevel.MpllDqFuncCntl     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_DQ_FUNC_CNTL);
+       table->MemoryACPILevel.MpllFuncCntl       =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL);
+       table->MemoryACPILevel.MpllFuncCntl_1     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_1);
+       table->MemoryACPILevel.MpllFuncCntl_2     =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_FUNC_CNTL_2);
+       table->MemoryACPILevel.MpllSs1            =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS1);
+       table->MemoryACPILevel.MpllSs2            =
+               PP_HOST_TO_SMC_UL(data->clock_registers.vMPLL_SS2);
+
+       table->MemoryACPILevel.EnabledForThrottle = 0;
+       table->MemoryACPILevel.EnabledForActivity = 0;
+       table->MemoryACPILevel.UpHyst = 0;
+       table->MemoryACPILevel.DownHyst = 100;
+       table->MemoryACPILevel.VoltageDownHyst = 0;
+       /* Indicates maximum activity level for this performance level.*/
+       table->MemoryACPILevel.ActivityLevel =
+                       PP_HOST_TO_SMC_US((uint16_t)data->mclk_activity_target);
+
+       table->MemoryACPILevel.StutterEnable = 0;
+       table->MemoryACPILevel.StrobeEnable = 0;
+       table->MemoryACPILevel.EdcReadEnable = 0;
+       table->MemoryACPILevel.EdcWriteEnable = 0;
+       table->MemoryACPILevel.RttEnable = 0;
+
+       return result;
+}
+
+static int tonga_populate_smc_uvd_level(struct pp_hwmgr *hwmgr,
+                                       SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+
+       uint8_t count;
+       pp_atomctrl_clock_dividers_vi dividers;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                               (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                                               pptable_info->mm_dep_table;
+
+       table->UvdLevelCount = (uint8_t) (mm_table->count);
+       table->UvdBootLevel = 0;
+
+       for (count = 0; count < table->UvdLevelCount; count++) {
+               table->UvdLevel[count].VclkFrequency = mm_table->entries[count].vclk;
+               table->UvdLevel[count].DclkFrequency = mm_table->entries[count].dclk;
+               table->UvdLevel[count].MinVoltage.Vddc =
+                       phm_get_voltage_index(pptable_info->vddc_lookup_table,
+                                               mm_table->entries[count].vddc);
+               table->UvdLevel[count].MinVoltage.VddGfx =
+                       (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+                       phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+                                               mm_table->entries[count].vddgfx) : 0;
+               table->UvdLevel[count].MinVoltage.Vddci =
+                       phm_get_voltage_id(&data->vddci_voltage_table,
+                                            mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               table->UvdLevel[count].MinVoltage.Phases = 1;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(
+                                       hwmgr,
+                                       table->UvdLevel[count].VclkFrequency,
+                                       &dividers);
+
+               PP_ASSERT_WITH_CODE((!result),
+                                   "can not find divide id for Vclk clock",
+                                       return result);
+
+               table->UvdLevel[count].VclkDivider = (uint8_t)dividers.pll_post_divider;
+
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                                                         table->UvdLevel[count].DclkFrequency, &dividers);
+               PP_ASSERT_WITH_CODE((!result),
+                                   "can not find divide id for Dclk clock",
+                                       return result);
+
+               table->UvdLevel[count].DclkDivider =
+                                       (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].VclkFrequency);
+               CONVERT_FROM_HOST_TO_SMC_UL(table->UvdLevel[count].DclkFrequency);
+       }
+
+       return result;
+
+}
+
+static int tonga_populate_smc_vce_level(struct pp_hwmgr *hwmgr,
+               SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+
+       uint8_t count;
+       pp_atomctrl_clock_dividers_vi dividers;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                             (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                                                    pptable_info->mm_dep_table;
+
+       table->VceLevelCount = (uint8_t) (mm_table->count);
+       table->VceBootLevel = 0;
+
+       for (count = 0; count < table->VceLevelCount; count++) {
+               table->VceLevel[count].Frequency =
+                       mm_table->entries[count].eclk;
+               table->VceLevel[count].MinVoltage.Vddc =
+                       phm_get_voltage_index(pptable_info->vddc_lookup_table,
+                               mm_table->entries[count].vddc);
+               table->VceLevel[count].MinVoltage.VddGfx =
+                       (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+                       phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+                               mm_table->entries[count].vddgfx) : 0;
+               table->VceLevel[count].MinVoltage.Vddci =
+                       phm_get_voltage_id(&data->vddci_voltage_table,
+                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               table->VceLevel[count].MinVoltage.Phases = 1;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                                       table->VceLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((!result),
+                               "can not find divide id for VCE engine clock",
+                               return result);
+
+               table->VceLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->VceLevel[count].Frequency);
+       }
+
+       return result;
+}
+
+static int tonga_populate_smc_acp_level(struct pp_hwmgr *hwmgr,
+               SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+       uint8_t count;
+       pp_atomctrl_clock_dividers_vi dividers;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                            (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                                                   pptable_info->mm_dep_table;
+
+       table->AcpLevelCount = (uint8_t) (mm_table->count);
+       table->AcpBootLevel = 0;
+
+       for (count = 0; count < table->AcpLevelCount; count++) {
+               table->AcpLevel[count].Frequency =
+                       pptable_info->mm_dep_table->entries[count].aclk;
+               table->AcpLevel[count].MinVoltage.Vddc =
+                       phm_get_voltage_index(pptable_info->vddc_lookup_table,
+                       mm_table->entries[count].vddc);
+               table->AcpLevel[count].MinVoltage.VddGfx =
+                       (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+                       phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+                               mm_table->entries[count].vddgfx) : 0;
+               table->AcpLevel[count].MinVoltage.Vddci =
+                       phm_get_voltage_id(&data->vddci_voltage_table,
+                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               table->AcpLevel[count].MinVoltage.Phases = 1;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                       table->AcpLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((!result),
+                       "can not find divide id for engine clock", return result);
+
+               table->AcpLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->AcpLevel[count].Frequency);
+       }
+
+       return result;
+}
+
+static int tonga_populate_smc_samu_level(struct pp_hwmgr *hwmgr,
+               SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+       uint8_t count;
+       pp_atomctrl_clock_dividers_vi dividers;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct phm_ppt_v1_information *pptable_info =
+                            (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table =
+                                                   pptable_info->mm_dep_table;
+
+       table->SamuBootLevel = 0;
+       table->SamuLevelCount = (uint8_t) (mm_table->count);
+
+       for (count = 0; count < table->SamuLevelCount; count++) {
+               /* not sure whether we need evclk or not */
+               table->SamuLevel[count].Frequency =
+                       pptable_info->mm_dep_table->entries[count].samclock;
+               table->SamuLevel[count].MinVoltage.Vddc =
+                       phm_get_voltage_index(pptable_info->vddc_lookup_table,
+                               mm_table->entries[count].vddc);
+               table->SamuLevel[count].MinVoltage.VddGfx =
+                       (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) ?
+                       phm_get_voltage_index(pptable_info->vddgfx_lookup_table,
+                               mm_table->entries[count].vddgfx) : 0;
+               table->SamuLevel[count].MinVoltage.Vddci =
+                       phm_get_voltage_id(&data->vddci_voltage_table,
+                               mm_table->entries[count].vddc - VDDC_VDDCI_DELTA);
+               table->SamuLevel[count].MinVoltage.Phases = 1;
+
+               /* retrieve divider value for VBIOS */
+               result = atomctrl_get_dfs_pll_dividers_vi(hwmgr,
+                                       table->SamuLevel[count].Frequency, &dividers);
+               PP_ASSERT_WITH_CODE((!result),
+                       "can not find divide id for samu clock", return result);
+
+               table->SamuLevel[count].Divider = (uint8_t)dividers.pll_post_divider;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(table->SamuLevel[count].Frequency);
+       }
+
+       return result;
+}
+
+static int tonga_populate_memory_timing_parameters(
+               struct pp_hwmgr *hwmgr,
+               uint32_t engine_clock,
+               uint32_t memory_clock,
+               struct SMU72_Discrete_MCArbDramTimingTableEntry *arb_regs
+               )
+{
+       uint32_t dramTiming;
+       uint32_t dramTiming2;
+       uint32_t burstTime;
+       int result;
+
+       result = atomctrl_set_engine_dram_timings_rv770(hwmgr,
+                               engine_clock, memory_clock);
+
+       PP_ASSERT_WITH_CODE(result == 0,
+               "Error calling VBIOS to set DRAM_TIMING.", return result);
+
+       dramTiming  = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING);
+       dramTiming2 = cgs_read_register(hwmgr->device, mmMC_ARB_DRAM_TIMING2);
+       burstTime = PHM_READ_FIELD(hwmgr->device, MC_ARB_BURST_TIME, STATE0);
+
+       arb_regs->McArbDramTiming  = PP_HOST_TO_SMC_UL(dramTiming);
+       arb_regs->McArbDramTiming2 = PP_HOST_TO_SMC_UL(dramTiming2);
+       arb_regs->McArbBurstTime = (uint8_t)burstTime;
+
+       return 0;
+}
+
+/**
+ * Setup parameters for the MC ARB.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ * This function is to be called from the SetPowerState table.
+ */
+static int tonga_program_memory_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       int result = 0;
+       SMU72_Discrete_MCArbDramTimingTable  arb_regs;
+       uint32_t i, j;
+
+       memset(&arb_regs, 0x00, sizeof(SMU72_Discrete_MCArbDramTimingTable));
+
+       for (i = 0; i < data->dpm_table.sclk_table.count; i++) {
+               for (j = 0; j < data->dpm_table.mclk_table.count; j++) {
+                       result = tonga_populate_memory_timing_parameters
+                               (hwmgr, data->dpm_table.sclk_table.dpm_levels[i].value,
+                                data->dpm_table.mclk_table.dpm_levels[j].value,
+                                &arb_regs.entries[i][j]);
+
+                       if (result)
+                               break;
+               }
+       }
+
+       if (!result) {
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.arb_table_start,
+                               (uint8_t *)&arb_regs,
+                               sizeof(SMU72_Discrete_MCArbDramTimingTable),
+                               SMC_RAM_END
+                               );
+       }
+
+       return result;
+}
+
+static int tonga_populate_smc_boot_level(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       table->GraphicsBootLevel = 0;
+       table->MemoryBootLevel = 0;
+
+       /* find boot level from dpm table*/
+       result = phm_find_boot_level(&(data->dpm_table.sclk_table),
+       data->vbios_boot_state.sclk_bootup_value,
+       (uint32_t *)&(smu_data->smc_state_table.GraphicsBootLevel));
+
+       if (result != 0) {
+               smu_data->smc_state_table.GraphicsBootLevel = 0;
+               printk(KERN_ERR "[powerplay] VBIOS did not find boot engine "
+                               "clock value in dependency table. "
+                               "Using Graphics DPM level 0 !");
+               result = 0;
+       }
+
+       result = phm_find_boot_level(&(data->dpm_table.mclk_table),
+               data->vbios_boot_state.mclk_bootup_value,
+               (uint32_t *)&(smu_data->smc_state_table.MemoryBootLevel));
+
+       if (result != 0) {
+               smu_data->smc_state_table.MemoryBootLevel = 0;
+               printk(KERN_ERR "[powerplay] VBIOS did not find boot "
+                               "engine clock value in dependency table."
+                               "Using Memory DPM level 0 !");
+               result = 0;
+       }
+
+       table->BootVoltage.Vddc =
+               phm_get_voltage_id(&(data->vddc_voltage_table),
+                       data->vbios_boot_state.vddc_bootup_value);
+       table->BootVoltage.VddGfx =
+               phm_get_voltage_id(&(data->vddgfx_voltage_table),
+                       data->vbios_boot_state.vddgfx_bootup_value);
+       table->BootVoltage.Vddci =
+               phm_get_voltage_id(&(data->vddci_voltage_table),
+                       data->vbios_boot_state.vddci_bootup_value);
+       table->BootMVdd = data->vbios_boot_state.mvdd_bootup_value;
+
+       CONVERT_FROM_HOST_TO_SMC_US(table->BootMVdd);
+
+       return result;
+}
+
+static int tonga_populate_clock_stretcher_data_table(struct pp_hwmgr *hwmgr)
+{
+       uint32_t ro, efuse, efuse2, clock_freq, volt_without_cks,
+                       volt_with_cks, value;
+       uint16_t clock_freq_u16;
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint8_t type, i, j, cks_setting, stretch_amount, stretch_amount2,
+                       volt_offset = 0;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_ppt_v1_clock_voltage_dependency_table *sclk_table =
+                       table_info->vdd_dep_on_sclk;
+       uint32_t hw_revision, dev_id;
+       struct cgs_system_info sys_info = {0};
+
+       stretch_amount = (uint8_t)table_info->cac_dtp_table->usClockStretchAmount;
+
+       sys_info.size = sizeof(struct cgs_system_info);
+
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_REV;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       hw_revision = (uint32_t)sys_info.value;
+
+       sys_info.info_id = CGS_SYSTEM_INFO_PCIE_DEV;
+       cgs_query_system_info(hwmgr->device, &sys_info);
+       dev_id = (uint32_t)sys_info.value;
+
+       /* Read SMU_Eefuse to read and calculate RO and determine
+        * if the part is SS or FF. if RO >= 1660MHz, part is FF.
+        */
+       efuse = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixSMU_EFUSE_0 + (146 * 4));
+       efuse2 = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixSMU_EFUSE_0 + (148 * 4));
+       efuse &= 0xFF000000;
+       efuse = efuse >> 24;
+       efuse2 &= 0xF;
+
+       if (efuse2 == 1)
+               ro = (2300 - 1350) * efuse / 255 + 1350;
+       else
+               ro = (2500 - 1000) * efuse / 255 + 1000;
+
+       if (ro >= 1660)
+               type = 0;
+       else
+               type = 1;
+
+       /* Populate Stretch amount */
+       smu_data->smc_state_table.ClockStretcherAmount = stretch_amount;
+
+
+       /* Populate Sclk_CKS_masterEn0_7 and Sclk_voltageOffset */
+       for (i = 0; i < sclk_table->count; i++) {
+               smu_data->smc_state_table.Sclk_CKS_masterEn0_7 |=
+                               sclk_table->entries[i].cks_enable << i;
+               if (ASICID_IS_TONGA_P(dev_id, hw_revision)) {
+                       volt_without_cks = (uint32_t)((7732 + 60 - ro - 20838 *
+                               (sclk_table->entries[i].clk/100) / 10000) * 1000 /
+                               (8730 - (5301 * (sclk_table->entries[i].clk/100) / 1000)));
+                       volt_with_cks = (uint32_t)((5250 + 51 - ro - 2404 *
+                               (sclk_table->entries[i].clk/100) / 100000) * 1000 /
+                               (6146 - (3193 * (sclk_table->entries[i].clk/100) / 1000)));
+               } else {
+                       volt_without_cks = (uint32_t)((14041 *
+                               (sclk_table->entries[i].clk/100) / 10000 + 3571 + 75 - ro) * 1000 /
+                               (4026 - (13924 * (sclk_table->entries[i].clk/100) / 10000)));
+                       volt_with_cks = (uint32_t)((13946 *
+                               (sclk_table->entries[i].clk/100) / 10000 + 3320 + 45 - ro) * 1000 /
+                               (3664 - (11454 * (sclk_table->entries[i].clk/100) / 10000)));
+               }
+               if (volt_without_cks >= volt_with_cks)
+                       volt_offset = (uint8_t)(((volt_without_cks - volt_with_cks +
+                                       sclk_table->entries[i].cks_voffset) * 100 / 625) + 1);
+               smu_data->smc_state_table.Sclk_voltageOffset[i] = volt_offset;
+       }
+
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       STRETCH_ENABLE, 0x0);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       masterReset, 0x1);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       staticEnable, 0x1);
+       PHM_WRITE_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, PWR_CKS_ENABLE,
+                       masterReset, 0x0);
+
+       /* Populate CKS Lookup Table */
+       if (stretch_amount == 1 || stretch_amount == 2 || stretch_amount == 5)
+               stretch_amount2 = 0;
+       else if (stretch_amount == 3 || stretch_amount == 4)
+               stretch_amount2 = 1;
+       else {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_ClockStretcher);
+               PP_ASSERT_WITH_CODE(false,
+                               "Stretch Amount in PPTable not supported\n",
+                               return -EINVAL);
+       }
+
+       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixPWR_CKS_CNTL);
+       value &= 0xFFC2FF87;
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].minFreq =
+                       tonga_clock_stretcher_lookup_table[stretch_amount2][0];
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].maxFreq =
+                       tonga_clock_stretcher_lookup_table[stretch_amount2][1];
+       clock_freq_u16 = (uint16_t)(PP_SMC_TO_HOST_UL(smu_data->smc_state_table.
+                       GraphicsLevel[smu_data->smc_state_table.GraphicsDpmLevelCount - 1].
+                       SclkFrequency) / 100);
+       if (tonga_clock_stretcher_lookup_table[stretch_amount2][0] <
+                       clock_freq_u16 &&
+           tonga_clock_stretcher_lookup_table[stretch_amount2][1] >
+                       clock_freq_u16) {
+               /* Program PWR_CKS_CNTL. CKS_USE_FOR_LOW_FREQ */
+               value |= (tonga_clock_stretcher_lookup_table[stretch_amount2][3]) << 16;
+               /* Program PWR_CKS_CNTL. CKS_LDO_REFSEL */
+               value |= (tonga_clock_stretcher_lookup_table[stretch_amount2][2]) << 18;
+               /* Program PWR_CKS_CNTL. CKS_STRETCH_AMOUNT */
+               value |= (tonga_clock_stretch_amount_conversion
+                               [tonga_clock_stretcher_lookup_table[stretch_amount2][3]]
+                                [stretch_amount]) << 3;
+       }
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+                       CKS_LOOKUPTableEntry[0].minFreq);
+       CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.CKS_LOOKUPTable.
+                       CKS_LOOKUPTableEntry[0].maxFreq);
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting =
+                       tonga_clock_stretcher_lookup_table[stretch_amount2][2] & 0x7F;
+       smu_data->smc_state_table.CKS_LOOKUPTable.CKS_LOOKUPTableEntry[0].setting |=
+                       (tonga_clock_stretcher_lookup_table[stretch_amount2][3]) << 7;
+
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixPWR_CKS_CNTL, value);
+
+       /* Populate DDT Lookup Table */
+       for (i = 0; i < 4; i++) {
+               /* Assign the minimum and maximum VID stored
+                * in the last row of Clock Stretcher Voltage Table.
+                */
+               smu_data->smc_state_table.ClockStretcherDataTable.
+               ClockStretcherDataTableEntry[i].minVID =
+                               (uint8_t) tonga_clock_stretcher_ddt_table[type][i][2];
+               smu_data->smc_state_table.ClockStretcherDataTable.
+               ClockStretcherDataTableEntry[i].maxVID =
+                               (uint8_t) tonga_clock_stretcher_ddt_table[type][i][3];
+               /* Loop through each SCLK and check the frequency
+                * to see if it lies within the frequency for clock stretcher.
+                */
+               for (j = 0; j < smu_data->smc_state_table.GraphicsDpmLevelCount; j++) {
+                       cks_setting = 0;
+                       clock_freq = PP_SMC_TO_HOST_UL(
+                                       smu_data->smc_state_table.GraphicsLevel[j].SclkFrequency);
+                       /* Check the allowed frequency against the sclk level[j].
+                        *  Sclk's endianness has already been converted,
+                        *  and it's in 10Khz unit,
+                        *  as opposed to Data table, which is in Mhz unit.
+                        */
+                       if (clock_freq >= tonga_clock_stretcher_ddt_table[type][i][0] * 100) {
+                               cks_setting |= 0x2;
+                               if (clock_freq < tonga_clock_stretcher_ddt_table[type][i][1] * 100)
+                                       cks_setting |= 0x1;
+                       }
+                       smu_data->smc_state_table.ClockStretcherDataTable.
+                       ClockStretcherDataTableEntry[i].setting |= cks_setting << (j * 2);
+               }
+               CONVERT_FROM_HOST_TO_SMC_US(smu_data->smc_state_table.
+                               ClockStretcherDataTable.
+                               ClockStretcherDataTableEntry[i].setting);
+       }
+
+       value = cgs_read_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                                       ixPWR_CKS_CNTL);
+       value &= 0xFFFFFFFE;
+       cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                                       ixPWR_CKS_CNTL, value);
+
+       return 0;
+}
+
+/**
+ * Populates the SMC VRConfig field in DPM table.
+ *
+ * @param    hwmgr      the address of the hardware manager
+ * @param    table     the SMC DPM table structure to be populated
+ * @return   always 0
+ */
+static int tonga_populate_vr_config(struct pp_hwmgr *hwmgr,
+                       SMU72_Discrete_DpmTable *table)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint16_t config;
+
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vdd_gfx_control) {
+               /*  Splitted mode */
+               config = VR_SVI2_PLANE_1;
+               table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
+
+               if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+                       config = VR_SVI2_PLANE_2;
+                       table->VRConfig |= config;
+               } else {
+                       printk(KERN_ERR "[ powerplay ] VDDC and VDDGFX should "
+                               "be both on SVI2 control in splitted mode !\n");
+               }
+       } else {
+               /* Merged mode  */
+               config = VR_MERGED_WITH_VDDC;
+               table->VRConfig |= (config<<VRCONF_VDDGFX_SHIFT);
+
+               /* Set Vddc Voltage Controller  */
+               if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->voltage_control) {
+                       config = VR_SVI2_PLANE_1;
+                       table->VRConfig |= config;
+               } else {
+                       printk(KERN_ERR "[ powerplay ] VDDC should be on "
+                                       "SVI2 control in merged mode !\n");
+               }
+       }
+
+       /* Set Vddci Voltage Controller  */
+       if (SMU7_VOLTAGE_CONTROL_BY_SVID2 == data->vddci_control) {
+               config = VR_SVI2_PLANE_2;  /* only in merged mode */
+               table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+       } else if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->vddci_control) {
+               config = VR_SMIO_PATTERN_1;
+               table->VRConfig |= (config<<VRCONF_VDDCI_SHIFT);
+       }
+
+       /* Set Mvdd Voltage Controller */
+       if (SMU7_VOLTAGE_CONTROL_BY_GPIO == data->mvdd_control) {
+               config = VR_SMIO_PATTERN_2;
+               table->VRConfig |= (config<<VRCONF_MVDD_SHIFT);
+       }
+
+       return 0;
+}
+
+
+/**
+ * Initialize the ARB DRAM timing table's index field.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+static int tonga_init_arb_table_index(struct pp_smumgr *smumgr)
+{
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend);
+       uint32_t tmp;
+       int result;
+
+       /*
+       * This is a read-modify-write on the first byte of the ARB table.
+       * The first byte in the SMU72_Discrete_MCArbDramTimingTable structure
+       * is the field 'current'.
+       * This solution is ugly, but we never write the whole table only
+       * individual fields in it.
+       * In reality this field should not be in that structure
+       * but in a soft register.
+       */
+       result = smu7_read_smc_sram_dword(smumgr,
+                               smu_data->smu7_data.arb_table_start, &tmp, SMC_RAM_END);
+
+       if (result != 0)
+               return result;
+
+       tmp &= 0x00FFFFFF;
+       tmp |= ((uint32_t)MC_CG_ARB_FREQ_F1) << 24;
+
+       return smu7_write_smc_sram_dword(smumgr,
+                       smu_data->smu7_data.arb_table_start, tmp, SMC_RAM_END);
+}
+
+
+static int tonga_populate_bapm_parameters_in_dpm_table(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+       SMU72_Discrete_DpmTable  *dpm_table = &(smu_data->smc_state_table);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       struct phm_cac_tdp_table *cac_dtp_table = table_info->cac_dtp_table;
+       int  i, j, k;
+       const uint16_t *pdef1, *pdef2;
+
+       dpm_table->DefaultTdp = PP_HOST_TO_SMC_US(
+                       (uint16_t)(cac_dtp_table->usTDP * 256));
+       dpm_table->TargetTdp = PP_HOST_TO_SMC_US(
+                       (uint16_t)(cac_dtp_table->usConfigurableTDP * 256));
+
+       PP_ASSERT_WITH_CODE(cac_dtp_table->usTargetOperatingTemp <= 255,
+                       "Target Operating Temp is out of Range !",
+                       );
+
+       dpm_table->GpuTjMax = (uint8_t)(cac_dtp_table->usTargetOperatingTemp);
+       dpm_table->GpuTjHyst = 8;
+
+       dpm_table->DTEAmbientTempBase = defaults->dte_ambient_temp_base;
+
+       dpm_table->BAPM_TEMP_GRADIENT =
+                               PP_HOST_TO_SMC_UL(defaults->bamp_temp_gradient);
+       pdef1 = defaults->bapmti_r;
+       pdef2 = defaults->bapmti_rc;
+
+       for (i = 0; i < SMU72_DTE_ITERATIONS; i++) {
+               for (j = 0; j < SMU72_DTE_SOURCES; j++) {
+                       for (k = 0; k < SMU72_DTE_SINKS; k++) {
+                               dpm_table->BAPMTI_R[i][j][k] =
+                                               PP_HOST_TO_SMC_US(*pdef1);
+                               dpm_table->BAPMTI_RC[i][j][k] =
+                                               PP_HOST_TO_SMC_US(*pdef2);
+                               pdef1++;
+                               pdef2++;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int tonga_populate_svi_load_line(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+
+       smu_data->power_tune_table.SviLoadLineEn = defaults->svi_load_line_en;
+       smu_data->power_tune_table.SviLoadLineVddC = defaults->svi_load_line_vddC;
+       smu_data->power_tune_table.SviLoadLineTrimVddC = 3;
+       smu_data->power_tune_table.SviLoadLineOffsetVddC = 0;
+
+       return 0;
+}
+
+static int tonga_populate_tdc_limit(struct pp_hwmgr *hwmgr)
+{
+       uint16_t tdc_limit;
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       /* TDC number of fraction bits are changed from 8 to 7
+        * for Fiji as requested by SMC team
+        */
+       tdc_limit = (uint16_t)(table_info->cac_dtp_table->usTDC * 256);
+       smu_data->power_tune_table.TDC_VDDC_PkgLimit =
+                       CONVERT_FROM_HOST_TO_SMC_US(tdc_limit);
+       smu_data->power_tune_table.TDC_VDDC_ThrottleReleaseLimitPerc =
+                       defaults->tdc_vddc_throttle_release_limit_perc;
+       smu_data->power_tune_table.TDC_MAWt = defaults->tdc_mawt;
+
+       return 0;
+}
+
+static int tonga_populate_dw8(struct pp_hwmgr *hwmgr, uint32_t fuse_table_offset)
+{
+       struct tonga_smumgr *smu_data =
+                       (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       const struct tonga_pt_defaults *defaults = smu_data->power_tune_defaults;
+       uint32_t temp;
+
+       if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                       fuse_table_offset +
+                       offsetof(SMU72_Discrete_PmFuses, TdcWaterfallCtl),
+                       (uint32_t *)&temp, SMC_RAM_END))
+               PP_ASSERT_WITH_CODE(false,
+                               "Attempt to read PmFuses.DW6 "
+                               "(SviLoadLineEn) from SMC Failed !",
+                               return -EINVAL);
+       else
+               smu_data->power_tune_table.TdcWaterfallCtl = defaults->tdc_waterfall_ctl;
+
+       return 0;
+}
+
+static int tonga_populate_temperature_scaler(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.LPMLTemperatureScaler[i] = 0;
+
+       return 0;
+}
+
+static int tonga_populate_fuzzy_fan(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+
+       if ((hwmgr->thermal_controller.advanceFanControlParameters.
+                       usFanOutputSensitivity & (1 << 15)) ||
+               (hwmgr->thermal_controller.advanceFanControlParameters.usFanOutputSensitivity == 0))
+               hwmgr->thermal_controller.advanceFanControlParameters.
+               usFanOutputSensitivity = hwmgr->thermal_controller.
+                       advanceFanControlParameters.usDefaultFanOutputSensitivity;
+
+       smu_data->power_tune_table.FuzzyFan_PwmSetDelta =
+                       PP_HOST_TO_SMC_US(hwmgr->thermal_controller.
+                                       advanceFanControlParameters.usFanOutputSensitivity);
+       return 0;
+}
+
+static int tonga_populate_gnb_lpml(struct pp_hwmgr *hwmgr)
+{
+       int i;
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+
+       /* Currently not used. Set all to zero. */
+       for (i = 0; i < 16; i++)
+               smu_data->power_tune_table.GnbLPML[i] = 0;
+
+       return 0;
+}
+
+static int tonga_min_max_vgnb_lpml_id_from_bapm_vddc(struct pp_hwmgr *hwmgr)
+{
+       return 0;
+}
+
+static int tonga_populate_bapm_vddc_base_leakage_sidd(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+       uint16_t hi_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd;
+       uint16_t lo_sidd = smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd;
+       struct phm_cac_tdp_table *cac_table = table_info->cac_dtp_table;
+
+       hi_sidd = (uint16_t)(cac_table->usHighCACLeakage / 100 * 256);
+       lo_sidd = (uint16_t)(cac_table->usLowCACLeakage / 100 * 256);
+
+       smu_data->power_tune_table.BapmVddCBaseLeakageHiSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(hi_sidd);
+       smu_data->power_tune_table.BapmVddCBaseLeakageLoSidd =
+                       CONVERT_FROM_HOST_TO_SMC_US(lo_sidd);
+
+       return 0;
+}
+
+static int tonga_populate_pm_fuses(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t pm_fuse_table_offset;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_PowerContainment)) {
+               if (smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, PmFuseTable),
+                               &pm_fuse_table_offset, SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to get pm_fuse_table_offset Failed !",
+                               return -EINVAL);
+
+               /* DW6 */
+               if (tonga_populate_svi_load_line(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate SviLoadLine Failed !",
+                               return -EINVAL);
+               /* DW7 */
+               if (tonga_populate_tdc_limit(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to populate TDCLimit Failed !",
+                                       return -EINVAL);
+               /* DW8 */
+               if (tonga_populate_dw8(hwmgr, pm_fuse_table_offset))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate TdcWaterfallCtl Failed !",
+                               return -EINVAL);
+
+               /* DW9-DW12 */
+               if (tonga_populate_temperature_scaler(hwmgr) != 0)
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate LPMLTemperatureScaler Failed !",
+                               return -EINVAL);
+
+               /* DW13-DW14 */
+               if (tonga_populate_fuzzy_fan(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate Fuzzy Fan "
+                               "Control parameters Failed !",
+                               return -EINVAL);
+
+               /* DW15-DW18 */
+               if (tonga_populate_gnb_lpml(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate GnbLPML Failed !",
+                               return -EINVAL);
+
+               /* DW19 */
+               if (tonga_min_max_vgnb_lpml_id_from_bapm_vddc(hwmgr))
+                       PP_ASSERT_WITH_CODE(false,
+                               "Attempt to populate GnbLPML "
+                               "Min and Max Vid Failed !",
+                               return -EINVAL);
+
+               /* DW20 */
+               if (tonga_populate_bapm_vddc_base_leakage_sidd(hwmgr))
+                       PP_ASSERT_WITH_CODE(
+                               false,
+                               "Attempt to populate BapmVddCBaseLeakage "
+                               "Hi and Lo Sidd Failed !",
+                               return -EINVAL);
+
+               if (smu7_copy_bytes_to_smc(hwmgr->smumgr, pm_fuse_table_offset,
+                               (uint8_t *)&smu_data->power_tune_table,
+                               sizeof(struct SMU72_Discrete_PmFuses), SMC_RAM_END))
+                       PP_ASSERT_WITH_CODE(false,
+                                       "Attempt to download PmFuseTable Failed !",
+                                       return -EINVAL);
+       }
+       return 0;
+}
+
+static int tonga_populate_mc_reg_address(struct pp_smumgr *smumgr,
+                                SMU72_Discrete_MCRegisters *mc_reg_table)
+{
+       const struct tonga_smumgr *smu_data = (struct tonga_smumgr *)smumgr->backend;
+
+       uint32_t i, j;
+
+       for (i = 0, j = 0; j < smu_data->mc_reg_table.last; j++) {
+               if (smu_data->mc_reg_table.validflag & 1<<j) {
+                       PP_ASSERT_WITH_CODE(
+                               i < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE,
+                               "Index of mc_reg_table->address[] array "
+                               "out of boundary",
+                               return -EINVAL);
+                       mc_reg_table->address[i].s0 =
+                               PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s0);
+                       mc_reg_table->address[i].s1 =
+                               PP_HOST_TO_SMC_US(smu_data->mc_reg_table.mc_reg_address[j].s1);
+                       i++;
+               }
+       }
+
+       mc_reg_table->last = (uint8_t)i;
+
+       return 0;
+}
+
+/*convert register values from driver to SMC format */
+static void tonga_convert_mc_registers(
+       const struct tonga_mc_reg_entry *entry,
+       SMU72_Discrete_MCRegisterSet *data,
+       uint32_t num_entries, uint32_t valid_flag)
+{
+       uint32_t i, j;
+
+       for (i = 0, j = 0; j < num_entries; j++) {
+               if (valid_flag & 1<<j) {
+                       data->value[i] = PP_HOST_TO_SMC_UL(entry->mc_data[j]);
+                       i++;
+               }
+       }
+}
+
+static int tonga_convert_mc_reg_table_entry_to_smc(
+               struct pp_smumgr *smumgr,
+               const uint32_t memory_clock,
+               SMU72_Discrete_MCRegisterSet *mc_reg_table_data
+               )
+{
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend);
+       uint32_t i = 0;
+
+       for (i = 0; i < smu_data->mc_reg_table.num_entries; i++) {
+               if (memory_clock <=
+                       smu_data->mc_reg_table.mc_reg_table_entry[i].mclk_max) {
+                       break;
+               }
+       }
+
+       if ((i == smu_data->mc_reg_table.num_entries) && (i > 0))
+               --i;
+
+       tonga_convert_mc_registers(&smu_data->mc_reg_table.mc_reg_table_entry[i],
+                               mc_reg_table_data, smu_data->mc_reg_table.last,
+                               smu_data->mc_reg_table.validflag);
+
+       return 0;
+}
+
+static int tonga_convert_mc_reg_table_to_smc(struct pp_hwmgr *hwmgr,
+               SMU72_Discrete_MCRegisters *mc_regs)
+{
+       int result = 0;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       int res;
+       uint32_t i;
+
+       for (i = 0; i < data->dpm_table.mclk_table.count; i++) {
+               res = tonga_convert_mc_reg_table_entry_to_smc(
+                               hwmgr->smumgr,
+                               data->dpm_table.mclk_table.dpm_levels[i].value,
+                               &mc_regs->data[i]
+                               );
+
+               if (0 != res)
+                       result = res;
+       }
+
+       return result;
+}
+
+static int tonga_update_and_upload_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend);
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       uint32_t address;
+       int32_t result;
+
+       if (0 == (data->need_update_smu7_dpm_table & DPMTABLE_OD_UPDATE_MCLK))
+               return 0;
+
+
+       memset(&smu_data->mc_regs, 0, sizeof(SMU72_Discrete_MCRegisters));
+
+       result = tonga_convert_mc_reg_table_to_smc(hwmgr, &(smu_data->mc_regs));
+
+       if (result != 0)
+               return result;
+
+
+       address = smu_data->smu7_data.mc_reg_table_start +
+                       (uint32_t)offsetof(SMU72_Discrete_MCRegisters, data[0]);
+
+       return  smu7_copy_bytes_to_smc(
+                       hwmgr->smumgr, address,
+                       (uint8_t *)&smu_data->mc_regs.data[0],
+                       sizeof(SMU72_Discrete_MCRegisterSet) *
+                       data->dpm_table.mclk_table.count,
+                       SMC_RAM_END);
+}
+
+static int tonga_populate_initial_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct pp_smumgr *smumgr = hwmgr->smumgr;
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend);
+
+       memset(&smu_data->mc_regs, 0x00, sizeof(SMU72_Discrete_MCRegisters));
+       result = tonga_populate_mc_reg_address(smumgr, &(smu_data->mc_regs));
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize MCRegTable for the MC register addresses !",
+               return result;);
+
+       result = tonga_convert_mc_reg_table_to_smc(hwmgr, &smu_data->mc_regs);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize MCRegTable for driver state !",
+               return result;);
+
+       return smu7_copy_bytes_to_smc(smumgr, smu_data->smu7_data.mc_reg_table_start,
+                       (uint8_t *)&smu_data->mc_regs, sizeof(SMU72_Discrete_MCRegisters), SMC_RAM_END);
+}
+
+static void tonga_initialize_power_tune_defaults(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       struct  phm_ppt_v1_information *table_info =
+                       (struct  phm_ppt_v1_information *)(hwmgr->pptable);
+
+       if (table_info &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID <= POWERTUNE_DEFAULT_SET_MAX &&
+                       table_info->cac_dtp_table->usPowerTuneDataSetID)
+               smu_data->power_tune_defaults =
+                               &tonga_power_tune_data_set_array
+                               [table_info->cac_dtp_table->usPowerTuneDataSetID - 1];
+       else
+               smu_data->power_tune_defaults = &tonga_power_tune_data_set_array[0];
+}
+
+/**
+ * Initializes the SMC table and uploads it
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @param    pInput  the pointer to input data (PowerState)
+ * @return   always 0
+ */
+int tonga_init_smc_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data =
+                       (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       SMU72_Discrete_DpmTable *table = &(smu_data->smc_state_table);
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       uint8_t i;
+       pp_atomctrl_gpio_pin_assignment gpio_pin_assignment;
+
+
+       memset(&(smu_data->smc_state_table), 0x00, sizeof(smu_data->smc_state_table));
+
+       tonga_initialize_power_tune_defaults(hwmgr);
+
+       if (SMU7_VOLTAGE_CONTROL_NONE != data->voltage_control)
+               tonga_populate_smc_voltage_tables(hwmgr, table);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
+
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StepVddc))
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
+
+       if (data->is_memory_gddr5)
+               table->SystemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
+
+       i = PHM_READ_FIELD(hwmgr->device, CC_MC_MAX_CHANNEL, NOOFCHAN);
+
+       if (i == 1 || i == 0)
+               table->SystemFlags |= 0x40;
+
+       if (data->ulv_supported && table_info->us_ulv_voltage_offset) {
+               result = tonga_populate_ulv_state(hwmgr, table);
+               PP_ASSERT_WITH_CODE(!result,
+                       "Failed to initialize ULV state !",
+                       return result;);
+
+               cgs_write_ind_register(hwmgr->device, CGS_IND_REG__SMC,
+                       ixCG_ULV_PARAMETER, 0x40035);
+       }
+
+       result = tonga_populate_smc_link_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize Link Level !", return result);
+
+       result = tonga_populate_all_graphic_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize Graphics Level !", return result);
+
+       result = tonga_populate_all_memory_levels(hwmgr);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize Memory Level !", return result);
+
+       result = tonga_populate_smc_acpi_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize ACPI Level !", return result);
+
+       result = tonga_populate_smc_vce_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize VCE Level !", return result);
+
+       result = tonga_populate_smc_acp_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize ACP Level !", return result);
+
+       result = tonga_populate_smc_samu_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize SAMU Level !", return result);
+
+       /* Since only the initial state is completely set up at this
+       * point (the other states are just copies of the boot state) we only
+       * need to populate the  ARB settings for the initial state.
+       */
+       result = tonga_program_memory_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to Write ARB settings for the initial state.",
+               return result;);
+
+       result = tonga_populate_smc_uvd_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize UVD Level !", return result);
+
+       result = tonga_populate_smc_boot_level(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to initialize Boot Level !", return result);
+
+       tonga_populate_bapm_parameters_in_dpm_table(hwmgr);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to populate BAPM Parameters !", return result);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ClockStretcher)) {
+               result = tonga_populate_clock_stretcher_data_table(hwmgr);
+               PP_ASSERT_WITH_CODE(!result,
+                       "Failed to populate Clock Stretcher Data Table !",
+                       return result;);
+       }
+       table->GraphicsVoltageChangeEnable  = 1;
+       table->GraphicsThermThrottleEnable  = 1;
+       table->GraphicsInterval = 1;
+       table->VoltageInterval  = 1;
+       table->ThermalInterval  = 1;
+       table->TemperatureLimitHigh =
+               table_info->cac_dtp_table->usTargetOperatingTemp *
+               SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->TemperatureLimitLow =
+               (table_info->cac_dtp_table->usTargetOperatingTemp - 1) *
+               SMU7_Q88_FORMAT_CONVERSION_UNIT;
+       table->MemoryVoltageChangeEnable  = 1;
+       table->MemoryInterval  = 1;
+       table->VoltageResponseTime  = 0;
+       table->PhaseResponseTime  = 0;
+       table->MemoryThermThrottleEnable  = 1;
+
+       /*
+       * Cail reads current link status and reports it as cap (we cannot
+       * change this due to some previous issues we had)
+       * SMC drops the link status to lowest level after enabling
+       * DPM by PowerPlay. After pnp or toggling CF, driver gets reloaded again
+       * but this time Cail reads current link status which was set to low by
+       * SMC and reports it as cap to powerplay
+       * To avoid it, we set PCIeBootLinkLevel to highest dpm level
+       */
+       PP_ASSERT_WITH_CODE((1 <= data->dpm_table.pcie_speed_table.count),
+                       "There must be 1 or more PCIE levels defined in PPTable.",
+                       return -EINVAL);
+
+       table->PCIeBootLinkLevel = (uint8_t) (data->dpm_table.pcie_speed_table.count);
+
+       table->PCIeGenInterval  = 1;
+
+       result = tonga_populate_vr_config(hwmgr, table);
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to populate VRConfig setting !", return result);
+
+       table->ThermGpio  = 17;
+       table->SclkStepSize = 0x4000;
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, VDDC_VRHOT_GPIO_PINID,
+                                               &gpio_pin_assignment)) {
+               table->VRHotGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_RegulatorHot);
+       } else {
+               table->VRHotGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_RegulatorHot);
+       }
+
+       if (atomctrl_get_pp_assign_pin(hwmgr, PP_AC_DC_SWITCH_GPIO_PINID,
+                                               &gpio_pin_assignment)) {
+               table->AcDcGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition);
+       } else {
+               table->AcDcGpio = SMU7_UNUSED_GPIO_PIN;
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition);
+       }
+
+       phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+               PHM_PlatformCaps_Falcon_QuickTransition);
+
+       if (0) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_AutomaticDCTransition);
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_Falcon_QuickTransition);
+       }
+
+       if (atomctrl_get_pp_assign_pin(hwmgr,
+                       THERMAL_INT_OUTPUT_GPIO_PINID, &gpio_pin_assignment)) {
+               phm_cap_set(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ThermalOutGPIO);
+
+               table->ThermOutGpio = gpio_pin_assignment.uc_gpio_pin_bit_shift;
+
+               table->ThermOutPolarity =
+                       (0 == (cgs_read_register(hwmgr->device, mmGPIOPAD_A) &
+                       (1 << gpio_pin_assignment.uc_gpio_pin_bit_shift))) ? 1 : 0;
+
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_ONLY;
+
+               /* if required, combine VRHot/PCC with thermal out GPIO*/
+               if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_RegulatorHot) &&
+                       phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_CombinePCCWithThermalSignal)){
+                       table->ThermOutMode = SMU7_THERM_OUT_MODE_THERM_VRHOT;
+               }
+       } else {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_ThermalOutGPIO);
+
+               table->ThermOutGpio = 17;
+               table->ThermOutPolarity = 1;
+               table->ThermOutMode = SMU7_THERM_OUT_MODE_DISABLE;
+       }
+
+       for (i = 0; i < SMU72_MAX_ENTRIES_SMIO; i++)
+               table->Smio[i] = PP_HOST_TO_SMC_UL(table->Smio[i]);
+
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SystemFlags);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->VRConfig);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask1);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SmioMask2);
+       CONVERT_FROM_HOST_TO_SMC_UL(table->SclkStepSize);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitHigh);
+       CONVERT_FROM_HOST_TO_SMC_US(table->TemperatureLimitLow);
+       CONVERT_FROM_HOST_TO_SMC_US(table->VoltageResponseTime);
+       CONVERT_FROM_HOST_TO_SMC_US(table->PhaseResponseTime);
+
+       /* Upload all dpm data to SMC memory.(dpm level, dpm level count etc) */
+       result = smu7_copy_bytes_to_smc(
+                       hwmgr->smumgr,
+                       smu_data->smu7_data.dpm_table_start + offsetof(SMU72_Discrete_DpmTable, SystemFlags),
+                       (uint8_t *)&(table->SystemFlags),
+                       sizeof(SMU72_Discrete_DpmTable) - 3 * sizeof(SMU72_PIDController),
+                       SMC_RAM_END);
+
+       PP_ASSERT_WITH_CODE(!result,
+               "Failed to upload dpm data to SMC memory !", return result;);
+
+       result = tonga_init_arb_table_index(hwmgr->smumgr);
+       PP_ASSERT_WITH_CODE(!result,
+                       "Failed to upload arb data to SMC memory !", return result);
+
+       tonga_populate_pm_fuses(hwmgr);
+       PP_ASSERT_WITH_CODE((!result),
+               "Failed to populate initialize pm fuses !", return result);
+
+       result = tonga_populate_initial_mc_reg_table(hwmgr);
+       PP_ASSERT_WITH_CODE((!result),
+               "Failed to populate initialize MC Reg table !", return result);
+
+       return 0;
+}
+
+/**
+* Set up the fan table to control the fan using the SMC.
+* @param    hwmgr  the address of the powerplay hardware manager.
+* @param    pInput the pointer to input data
+* @param    pOutput the pointer to output data
+* @param    pStorage the pointer to temporary storage
+* @param    Result the last failure code
+* @return   result from set temperature range routine
+*/
+int tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                       (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       SMU72_Discrete_FanTable fan_table = { FDO_MODE_HARDWARE };
+       uint32_t duty100;
+       uint32_t t_diff1, t_diff2, pwm_diff1, pwm_diff2;
+       uint16_t fdo_min, slope1, slope2;
+       uint32_t reference_clock;
+       int res;
+       uint64_t tmp64;
+
+       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_MicrocodeFanControl))
+               return 0;
+
+       if (0 == smu_data->smu7_data.fan_table_start) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       duty100 = PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device,
+                                               CGS_IND_REG__SMC,
+                                               CG_FDO_CTRL1, FMAX_DUTY100);
+
+       if (0 == duty100) {
+               phm_cap_unset(hwmgr->platform_descriptor.platformCaps,
+                               PHM_PlatformCaps_MicrocodeFanControl);
+               return 0;
+       }
+
+       tmp64 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin * duty100;
+       do_div(tmp64, 10000);
+       fdo_min = (uint16_t)tmp64;
+
+       t_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usTMed -
+                  hwmgr->thermal_controller.advanceFanControlParameters.usTMin;
+       t_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usTHigh -
+                 hwmgr->thermal_controller.advanceFanControlParameters.usTMed;
+
+       pwm_diff1 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed -
+                   hwmgr->thermal_controller.advanceFanControlParameters.usPWMMin;
+       pwm_diff2 = hwmgr->thermal_controller.advanceFanControlParameters.usPWMHigh -
+                   hwmgr->thermal_controller.advanceFanControlParameters.usPWMMed;
+
+       slope1 = (uint16_t)((50 + ((16 * duty100 * pwm_diff1) / t_diff1)) / 100);
+       slope2 = (uint16_t)((50 + ((16 * duty100 * pwm_diff2) / t_diff2)) / 100);
+
+       fan_table.TempMin = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMin) / 100);
+       fan_table.TempMed = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMed) / 100);
+       fan_table.TempMax = cpu_to_be16((50 + hwmgr->thermal_controller.advanceFanControlParameters.usTMax) / 100);
+
+       fan_table.Slope1 = cpu_to_be16(slope1);
+       fan_table.Slope2 = cpu_to_be16(slope2);
+
+       fan_table.FdoMin = cpu_to_be16(fdo_min);
+
+       fan_table.HystDown = cpu_to_be16(hwmgr->thermal_controller.advanceFanControlParameters.ucTHyst);
+
+       fan_table.HystUp = cpu_to_be16(1);
+
+       fan_table.HystSlope = cpu_to_be16(1);
+
+       fan_table.TempRespLim = cpu_to_be16(5);
+
+       reference_clock = smu7_get_xclk(hwmgr);
+
+       fan_table.RefreshPeriod = cpu_to_be32((hwmgr->thermal_controller.advanceFanControlParameters.ulCycleDelay * reference_clock) / 1600);
+
+       fan_table.FdoMax = cpu_to_be16((uint16_t)duty100);
+
+       fan_table.TempSrc = (uint8_t)PHM_READ_VFPF_INDIRECT_FIELD(hwmgr->device, CGS_IND_REG__SMC, CG_MULT_THERMAL_CTRL, TEMP_SEL);
+
+       fan_table.FanControl_GL_Flag = 1;
+
+       res = smu7_copy_bytes_to_smc(hwmgr->smumgr,
+                                       smu_data->smu7_data.fan_table_start,
+                                       (uint8_t *)&fan_table,
+                                       (uint32_t)sizeof(fan_table),
+                                       SMC_RAM_END);
+
+       return 0;
+}
+
+
+static int tonga_program_mem_timing_parameters(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       if (data->need_update_smu7_dpm_table &
+               (DPMTABLE_OD_UPDATE_SCLK + DPMTABLE_OD_UPDATE_MCLK))
+               return tonga_program_memory_timing_parameters(hwmgr);
+
+       return 0;
+}
+
+int tonga_update_sclk_threshold(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data =
+                       (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+
+       int result = 0;
+       uint32_t low_sclk_interrupt_threshold = 0;
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_SclkThrottleLowNotification)
+               && (hwmgr->gfx_arbiter.sclk_threshold !=
+                               data->low_sclk_interrupt_threshold)) {
+               data->low_sclk_interrupt_threshold =
+                               hwmgr->gfx_arbiter.sclk_threshold;
+               low_sclk_interrupt_threshold =
+                               data->low_sclk_interrupt_threshold;
+
+               CONVERT_FROM_HOST_TO_SMC_UL(low_sclk_interrupt_threshold);
+
+               result = smu7_copy_bytes_to_smc(
+                               hwmgr->smumgr,
+                               smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU72_Discrete_DpmTable,
+                                       LowSclkInterruptThreshold),
+                               (uint8_t *)&low_sclk_interrupt_threshold,
+                               sizeof(uint32_t),
+                               SMC_RAM_END);
+       }
+
+       result = tonga_update_and_upload_mc_reg_table(hwmgr);
+
+       PP_ASSERT_WITH_CODE((!result),
+                               "Failed to upload MC reg table !",
+                               return result);
+
+       result = tonga_program_mem_timing_parameters(hwmgr);
+       PP_ASSERT_WITH_CODE((result == 0),
+                       "Failed to program memory timing parameters !",
+                       );
+
+       return result;
+}
+
+uint32_t tonga_get_offsetof(uint32_t type, uint32_t member)
+{
+       switch (type) {
+       case SMU_SoftRegisters:
+               switch (member) {
+               case HandshakeDisables:
+                       return offsetof(SMU72_SoftRegisters, HandshakeDisables);
+               case VoltageChangeTimeout:
+                       return offsetof(SMU72_SoftRegisters, VoltageChangeTimeout);
+               case AverageGraphicsActivity:
+                       return offsetof(SMU72_SoftRegisters, AverageGraphicsActivity);
+               case PreVBlankGap:
+                       return offsetof(SMU72_SoftRegisters, PreVBlankGap);
+               case VBlankTimeout:
+                       return offsetof(SMU72_SoftRegisters, VBlankTimeout);
+               case UcodeLoadStatus:
+                       return offsetof(SMU72_SoftRegisters, UcodeLoadStatus);
+               }
+       case SMU_Discrete_DpmTable:
+               switch (member) {
+               case UvdBootLevel:
+                       return offsetof(SMU72_Discrete_DpmTable, UvdBootLevel);
+               case VceBootLevel:
+                       return offsetof(SMU72_Discrete_DpmTable, VceBootLevel);
+               case SamuBootLevel:
+                       return offsetof(SMU72_Discrete_DpmTable, SamuBootLevel);
+               case LowSclkInterruptThreshold:
+                       return offsetof(SMU72_Discrete_DpmTable, LowSclkInterruptThreshold);
+               }
+       }
+       printk("cant't get the offset of type %x member %x\n", type, member);
+       return 0;
+}
+
+uint32_t tonga_get_mac_definition(uint32_t value)
+{
+       switch (value) {
+       case SMU_MAX_LEVELS_GRAPHICS:
+               return SMU72_MAX_LEVELS_GRAPHICS;
+       case SMU_MAX_LEVELS_MEMORY:
+               return SMU72_MAX_LEVELS_MEMORY;
+       case SMU_MAX_LEVELS_LINK:
+               return SMU72_MAX_LEVELS_LINK;
+       case SMU_MAX_ENTRIES_SMIO:
+               return SMU72_MAX_ENTRIES_SMIO;
+       case SMU_MAX_LEVELS_VDDC:
+               return SMU72_MAX_LEVELS_VDDC;
+       case SMU_MAX_LEVELS_VDDGFX:
+               return SMU72_MAX_LEVELS_VDDGFX;
+       case SMU_MAX_LEVELS_VDDCI:
+               return SMU72_MAX_LEVELS_VDDCI;
+       case SMU_MAX_LEVELS_MVDD:
+               return SMU72_MAX_LEVELS_MVDD;
+       }
+       printk("cant't get the mac value %x\n", value);
+
+       return 0;
+}
+
+
+static int tonga_update_uvd_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+       smu_data->smc_state_table.UvdBootLevel = 0;
+       if (table_info->mm_dep_table->count > 0)
+               smu_data->smc_state_table.UvdBootLevel =
+                               (uint8_t) (table_info->mm_dep_table->count - 1);
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU72_Discrete_DpmTable, UvdBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0x00FFFFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.UvdBootLevel << 24;
+       cgs_write_ind_register(hwmgr->device,
+                               CGS_IND_REG__SMC,
+                               mm_boot_level_offset, mm_boot_level_value);
+
+       if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_UVDDPM) ||
+               phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_UVDDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.UvdBootLevel));
+       return 0;
+}
+
+static int tonga_update_vce_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data =
+                               (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+       struct phm_ppt_v1_information *table_info =
+                       (struct phm_ppt_v1_information *)(hwmgr->pptable);
+
+
+       smu_data->smc_state_table.VceBootLevel =
+               (uint8_t) (table_info->mm_dep_table->count - 1);
+
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                                       offsetof(SMU72_Discrete_DpmTable, VceBootLevel);
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFF00FFFF;
+       mm_boot_level_value |= smu_data->smc_state_table.VceBootLevel << 16;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_VCEDPM_SetEnabledMask,
+                               (uint32_t)1 << smu_data->smc_state_table.VceBootLevel);
+       return 0;
+}
+
+static int tonga_update_samu_smc_table(struct pp_hwmgr *hwmgr)
+{
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       uint32_t mm_boot_level_offset, mm_boot_level_value;
+
+       smu_data->smc_state_table.SamuBootLevel = 0;
+       mm_boot_level_offset = smu_data->smu7_data.dpm_table_start +
+                               offsetof(SMU72_Discrete_DpmTable, SamuBootLevel);
+
+       mm_boot_level_offset /= 4;
+       mm_boot_level_offset *= 4;
+       mm_boot_level_value = cgs_read_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset);
+       mm_boot_level_value &= 0xFFFFFF00;
+       mm_boot_level_value |= smu_data->smc_state_table.SamuBootLevel << 0;
+       cgs_write_ind_register(hwmgr->device,
+                       CGS_IND_REG__SMC, mm_boot_level_offset, mm_boot_level_value);
+
+       if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
+                       PHM_PlatformCaps_StablePState))
+               smum_send_msg_to_smc_with_parameter(hwmgr->smumgr,
+                               PPSMC_MSG_SAMUDPM_SetEnabledMask,
+                               (uint32_t)(1 << smu_data->smc_state_table.SamuBootLevel));
+       return 0;
+}
+
+int tonga_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type)
+{
+       switch (type) {
+       case SMU_UVD_TABLE:
+               tonga_update_uvd_smc_table(hwmgr);
+               break;
+       case SMU_VCE_TABLE:
+               tonga_update_vce_smc_table(hwmgr);
+               break;
+       case SMU_SAMU_TABLE:
+               tonga_update_samu_smc_table(hwmgr);
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+
+/**
+ * Get the location of various tables inside the FW image.
+ *
+ * @param    hwmgr  the address of the powerplay hardware manager.
+ * @return   always 0
+ */
+int tonga_process_firmware_header(struct pp_hwmgr *hwmgr)
+{
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+
+       uint32_t tmp;
+       int result;
+       bool error = false;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, DpmTable),
+                               &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.dpm_table_start = tmp;
+
+       error |= (result != 0);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, SoftRegisters),
+                               &tmp, SMC_RAM_END);
+
+       if (!result) {
+               data->soft_regs_start = tmp;
+               smu_data->smu7_data.soft_regs_start = tmp;
+       }
+
+       error |= (result != 0);
+
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, mcRegisterTable),
+                               &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.mc_reg_table_start = tmp;
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, FanTable),
+                               &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.fan_table_start = tmp;
+
+       error |= (result != 0);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, mcArbDramTimingTable),
+                               &tmp, SMC_RAM_END);
+
+       if (!result)
+               smu_data->smu7_data.arb_table_start = tmp;
+
+       error |= (result != 0);
+
+       result = smu7_read_smc_sram_dword(hwmgr->smumgr,
+                               SMU72_FIRMWARE_HEADER_LOCATION +
+                               offsetof(SMU72_Firmware_Header, Version),
+                               &tmp, SMC_RAM_END);
+
+       if (!result)
+               hwmgr->microcode_version_info.SMC = tmp;
+
+       error |= (result != 0);
+
+       return error ? 1 : 0;
+}
+
+/*---------------------------MC----------------------------*/
+
+static uint8_t tonga_get_memory_modile_index(struct pp_hwmgr *hwmgr)
+{
+       return (uint8_t) (0xFF & (cgs_read_register(hwmgr->device, mmBIOS_SCRATCH_4) >> 16));
+}
+
+static bool tonga_check_s0_mc_reg_index(uint16_t in_reg, uint16_t *out_reg)
+{
+       bool result = true;
+
+       switch (in_reg) {
+       case  mmMC_SEQ_RAS_TIMING:
+               *out_reg = mmMC_SEQ_RAS_TIMING_LP;
+               break;
+
+       case  mmMC_SEQ_DLL_STBY:
+               *out_reg = mmMC_SEQ_DLL_STBY_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CMD0:
+               *out_reg = mmMC_SEQ_G5PDX_CMD0_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CMD1:
+               *out_reg = mmMC_SEQ_G5PDX_CMD1_LP;
+               break;
+
+       case  mmMC_SEQ_G5PDX_CTRL:
+               *out_reg = mmMC_SEQ_G5PDX_CTRL_LP;
+               break;
+
+       case mmMC_SEQ_CAS_TIMING:
+               *out_reg = mmMC_SEQ_CAS_TIMING_LP;
+               break;
+
+       case mmMC_SEQ_MISC_TIMING:
+               *out_reg = mmMC_SEQ_MISC_TIMING_LP;
+               break;
+
+       case mmMC_SEQ_MISC_TIMING2:
+               *out_reg = mmMC_SEQ_MISC_TIMING2_LP;
+               break;
+
+       case mmMC_SEQ_PMG_DVS_CMD:
+               *out_reg = mmMC_SEQ_PMG_DVS_CMD_LP;
+               break;
+
+       case mmMC_SEQ_PMG_DVS_CTL:
+               *out_reg = mmMC_SEQ_PMG_DVS_CTL_LP;
+               break;
+
+       case mmMC_SEQ_RD_CTL_D0:
+               *out_reg = mmMC_SEQ_RD_CTL_D0_LP;
+               break;
+
+       case mmMC_SEQ_RD_CTL_D1:
+               *out_reg = mmMC_SEQ_RD_CTL_D1_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_D0:
+               *out_reg = mmMC_SEQ_WR_CTL_D0_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_D1:
+               *out_reg = mmMC_SEQ_WR_CTL_D1_LP;
+               break;
+
+       case mmMC_PMG_CMD_EMRS:
+               *out_reg = mmMC_SEQ_PMG_CMD_EMRS_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS1:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS1_LP;
+               break;
+
+       case mmMC_SEQ_PMG_TIMING:
+               *out_reg = mmMC_SEQ_PMG_TIMING_LP;
+               break;
+
+       case mmMC_PMG_CMD_MRS2:
+               *out_reg = mmMC_SEQ_PMG_CMD_MRS2_LP;
+               break;
+
+       case mmMC_SEQ_WR_CTL_2:
+               *out_reg = mmMC_SEQ_WR_CTL_2_LP;
+               break;
+
+       default:
+               result = false;
+               break;
+       }
+
+       return result;
+}
+
+static int tonga_set_s0_mc_reg_index(struct tonga_mc_reg_table *table)
+{
+       uint32_t i;
+       uint16_t address;
+
+       for (i = 0; i < table->last; i++) {
+               table->mc_reg_address[i].s0 =
+                       tonga_check_s0_mc_reg_index(table->mc_reg_address[i].s1,
+                                                       &address) ?
+                                                       address :
+                                                table->mc_reg_address[i].s1;
+       }
+       return 0;
+}
+
+static int tonga_copy_vbios_smc_reg_table(const pp_atomctrl_mc_reg_table *table,
+                                       struct tonga_mc_reg_table *ni_table)
+{
+       uint8_t i, j;
+
+       PP_ASSERT_WITH_CODE((table->last <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+               "Invalid VramInfo table.", return -EINVAL);
+       PP_ASSERT_WITH_CODE((table->num_entries <= MAX_AC_TIMING_ENTRIES),
+               "Invalid VramInfo table.", 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;
+}
+
+/**
+ * VBIOS omits some information to reduce size, we need to recover them here.
+ * 1.   when we see mmMC_SEQ_MISC1, bit[31:16] EMRS1, need to be write to
+ *      mmMC_PMG_CMD_EMRS /_LP[15:0]. Bit[15:0] MRS, need to be update
+ *      mmMC_PMG_CMD_MRS/_LP[15:0]
+ * 2.   when we see mmMC_SEQ_RESERVE_M, bit[15:0] EMRS2, need to be write to
+ *      mmMC_PMG_CMD_MRS1/_LP[15:0].
+ * 3.   need to set these data for each clock range
+ * @param    hwmgr the address of the powerplay hardware manager.
+ * @param    table the address of MCRegTable
+ * @return   always 0
+ */
+static int tonga_set_mc_special_registers(struct pp_hwmgr *hwmgr,
+                                       struct tonga_mc_reg_table *table)
+{
+       uint8_t i, j, k;
+       uint32_t temp_reg;
+       struct smu7_hwmgr *data = (struct smu7_hwmgr *)(hwmgr->backend);
+
+       for (i = 0, j = table->last; i < table->last; i++) {
+               PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                       "Invalid VramInfo table.", return -EINVAL);
+
+               switch (table->mc_reg_address[i].s1) {
+
+               case mmMC_SEQ_MISC1:
+                       temp_reg = cgs_read_register(hwmgr->device,
+                                                       mmMC_PMG_CMD_EMRS);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_EMRS;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_EMRS_LP;
+                       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++;
+                       PP_ASSERT_WITH_CODE((j < SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+
+                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS_LP;
+                       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 (!data->is_memory_gddr5)
+                                       table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
+                       }
+                       j++;
+                       PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+
+                       if (!data->is_memory_gddr5) {
+                               table->mc_reg_address[j].s1 = mmMC_PMG_AUTO_CMD;
+                               table->mc_reg_address[j].s0 = mmMC_PMG_AUTO_CMD;
+                               for (k = 0; k < table->num_entries; k++)
+                                       table->mc_reg_table_entry[k].mc_data[j] =
+                                               (table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16;
+                               j++;
+                               PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                                       "Invalid VramInfo table.", return -EINVAL);
+                       }
+
+                       break;
+
+               case mmMC_SEQ_RESERVE_M:
+                       temp_reg = cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1);
+                       table->mc_reg_address[j].s1 = mmMC_PMG_CMD_MRS1;
+                       table->mc_reg_address[j].s0 = mmMC_SEQ_PMG_CMD_MRS1_LP;
+                       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++;
+                       PP_ASSERT_WITH_CODE((j <= SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE),
+                               "Invalid VramInfo table.", return -EINVAL);
+                       break;
+
+               default:
+                       break;
+               }
+
+       }
+
+       table->last = j;
+
+       return 0;
+}
+
+static int tonga_set_valid_flag(struct tonga_mc_reg_table *table)
+{
+       uint8_t 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->validflag |= (1<<i);
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int tonga_initialize_mc_reg_table(struct pp_hwmgr *hwmgr)
+{
+       int result;
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(hwmgr->smumgr->backend);
+       pp_atomctrl_mc_reg_table *table;
+       struct tonga_mc_reg_table *ni_table = &smu_data->mc_reg_table;
+       uint8_t module_index = tonga_get_memory_modile_index(hwmgr);
+
+       table = kzalloc(sizeof(pp_atomctrl_mc_reg_table), GFP_KERNEL);
+
+       if (table == NULL)
+               return -ENOMEM;
+
+       /* Program additional LP registers that are no longer programmed by VBIOS */
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RAS_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_CAS_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_CAS_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_DLL_STBY_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_DLL_STBY));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CMD1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_G5PDX_CTRL));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CMD));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_DVS_CTL));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_MISC_TIMING2));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_EMRS_LP,
+                       cgs_read_register(hwmgr->device, mmMC_PMG_CMD_EMRS));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS_LP,
+                       cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D0));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_RD_CTL_D1));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_TIMING_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_PMG_TIMING));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_PMG_CMD_MRS2_LP,
+                       cgs_read_register(hwmgr->device, mmMC_PMG_CMD_MRS2));
+       cgs_write_register(hwmgr->device, mmMC_SEQ_WR_CTL_2_LP,
+                       cgs_read_register(hwmgr->device, mmMC_SEQ_WR_CTL_2));
+
+       memset(table, 0x00, sizeof(pp_atomctrl_mc_reg_table));
+
+       result = atomctrl_initialize_mc_reg_table(hwmgr, module_index, table);
+
+       if (!result)
+               result = tonga_copy_vbios_smc_reg_table(table, ni_table);
+
+       if (!result) {
+               tonga_set_s0_mc_reg_index(ni_table);
+               result = tonga_set_mc_special_registers(hwmgr, ni_table);
+       }
+
+       if (!result)
+               tonga_set_valid_flag(ni_table);
+
+       kfree(table);
+
+       return result;
+}
+
+bool tonga_is_dpm_running(struct pp_hwmgr *hwmgr)
+{
+       return (1 == PHM_READ_INDIRECT_FIELD(hwmgr->device,
+                       CGS_IND_REG__SMC, FEATURE_STATUS, VOLTAGE_CONTROLLER_ON))
+                       ? true : false;
+}
diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.h b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.h
new file mode 100644 (file)
index 0000000..8ae169f
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 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 _TONGA_SMC_H
+#define _TONGA_SMC_H
+
+#include "smumgr.h"
+#include "smu72.h"
+
+
+#define ASICID_IS_TONGA_P(wDID, bRID)   \
+       (((wDID == 0x6930) && ((bRID == 0xF0) || (bRID == 0xF1) || (bRID == 0xFF))) \
+       || ((wDID == 0x6920) && ((bRID == 0) || (bRID == 1))))
+
+
+struct tonga_pt_defaults {
+       uint8_t   svi_load_line_en;
+       uint8_t   svi_load_line_vddC;
+       uint8_t   tdc_vddc_throttle_release_limit_perc;
+       uint8_t   tdc_mawt;
+       uint8_t   tdc_waterfall_ctl;
+       uint8_t   dte_ambient_temp_base;
+       uint32_t  display_cac;
+       uint32_t  bamp_temp_gradient;
+       uint16_t  bapmti_r[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
+       uint16_t  bapmti_rc[SMU72_DTE_ITERATIONS * SMU72_DTE_SOURCES * SMU72_DTE_SINKS];
+};
+
+int tonga_populate_all_graphic_levels(struct pp_hwmgr *hwmgr);
+int tonga_populate_all_memory_levels(struct pp_hwmgr *hwmgr);
+int tonga_init_smc_table(struct pp_hwmgr *hwmgr);
+int tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr);
+int tonga_update_smc_table(struct pp_hwmgr *hwmgr, uint32_t type);
+int tonga_update_sclk_threshold(struct pp_hwmgr *hwmgr);
+uint32_t tonga_get_offsetof(uint32_t type, uint32_t member);
+uint32_t tonga_get_mac_definition(uint32_t value);
+int tonga_process_firmware_header(struct pp_hwmgr *hwmgr);
+int tonga_initialize_mc_reg_table(struct pp_hwmgr *hwmgr);
+bool tonga_is_dpm_running(struct pp_hwmgr *hwmgr);
+#endif
+
index f42c536..5f91240 100644 (file)
 #include "smu/smu_7_1_2_d.h"
 #include "smu/smu_7_1_2_sh_mask.h"
 #include "cgs_common.h"
+#include "tonga_smc.h"
+#include "smu7_smumgr.h"
 
-#define TONGA_SMC_SIZE                 0x20000
-#define BUFFER_SIZE                    80000
-#define MAX_STRING_SIZE                        15
-#define BUFFER_SIZETWO              131072 /*128 *1024*/
-
-/**
-* Set the address for reading/writing the SMC SRAM space.
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    smcAddress the address in the SMC RAM to access.
-*/
-static int tonga_set_smc_sram_address(struct pp_smumgr *smumgr,
-                               uint32_t smcAddress, uint32_t limit)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-       PP_ASSERT_WITH_CODE((0 == (3 & smcAddress)),
-               "SMC address must be 4 byte aligned.",
-               return -1;);
-
-       PP_ASSERT_WITH_CODE((limit > (smcAddress + 3)),
-               "SMC address is beyond the SMC RAM area.",
-               return -1;);
-
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_0, smcAddress);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_11, 0);
-
-       return 0;
-}
-
-/**
-* Copy bytes from an array into the SMC RAM space.
-*
-* @param    smumgr  the address of the powerplay SMU manager.
-* @param    smcStartAddress the start address in the SMC RAM to copy bytes to.
-* @param    src the byte array to copy the bytes from.
-* @param    byteCount the number of bytes to copy.
-*/
-int tonga_copy_bytes_to_smc(struct pp_smumgr *smumgr,
-               uint32_t smcStartAddress, const uint8_t *src,
-               uint32_t byteCount, uint32_t limit)
-{
-       uint32_t addr;
-       uint32_t data, orig_data;
-       int result = 0;
-       uint32_t extra_shift;
-
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-       PP_ASSERT_WITH_CODE((0 == (3 & smcStartAddress)),
-               "SMC address must be 4 byte aligned.",
-               return 0;);
-
-       PP_ASSERT_WITH_CODE((limit > (smcStartAddress + byteCount)),
-               "SMC address is beyond the SMC RAM area.",
-               return 0;);
-
-       addr = smcStartAddress;
-
-       while (byteCount >= 4) {
-               /*
-                * Bytes are written into the
-                * SMC address space with the MSB first
-                */
-               data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
-
-               result = tonga_set_smc_sram_address(smumgr, addr, limit);
-
-               if (result)
-                       goto out;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data);
-
-               src += 4;
-               byteCount -= 4;
-               addr += 4;
-       }
-
-       if (0 != byteCount) {
-               /* Now write odd bytes left, do a read modify write cycle */
-               data = 0;
-
-               result = tonga_set_smc_sram_address(smumgr, addr, limit);
-               if (result)
-                       goto out;
-
-               orig_data = cgs_read_register(smumgr->device,
-                                                       mmSMC_IND_DATA_0);
-               extra_shift = 8 * (4 - byteCount);
-
-               while (byteCount > 0) {
-                       data = (data << 8) + *src++;
-                       byteCount--;
-               }
-
-               data <<= extra_shift;
-               data |= (orig_data & ~((~0UL) << extra_shift));
-
-               result = tonga_set_smc_sram_address(smumgr, addr, limit);
-               if (result)
-                       goto out;
-
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data);
-       }
-
-out:
-       return result;
-}
-
-
-int tonga_program_jump_on_start(struct pp_smumgr *smumgr)
-{
-       static const unsigned char pData[] = { 0xE0, 0x00, 0x80, 0x40 };
-
-       tonga_copy_bytes_to_smc(smumgr, 0x0, pData, 4, sizeof(pData)+1);
-
-       return 0;
-}
-
-/**
-* Return if the SMC is currently running.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-*/
-static int tonga_is_smc_ram_running(struct pp_smumgr *smumgr)
-{
-       return ((0 == SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
-                                       SMC_SYSCON_CLOCK_CNTL_0, ck_disable))
-                       && (0x20100 <= cgs_read_ind_register(smumgr->device,
-                                       CGS_IND_REG__SMC, ixSMC_PC_C)));
-}
-
-static int tonga_send_msg_to_smc_offset(struct pp_smumgr *smumgr)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, 0x20000);
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, PPSMC_MSG_Test);
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       return 0;
-}
-
-/**
-* Send a message to the SMC, and wait for its response.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   The response that came from the SMC.
-*/
-static int tonga_send_msg_to_smc(struct pp_smumgr *smumgr, uint16_t msg)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       if (!tonga_is_smc_ram_running(smumgr))
-               return -1;
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       PP_ASSERT_WITH_CODE(
-               1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
-               "Failed to send Previous Message.",
-               );
-
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       PP_ASSERT_WITH_CODE(
-               1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
-               "Failed to send Message.",
-               );
-
-       return 0;
-}
-
-/*
-* Send a message to the SMC, and do not wait for its response.
-*
-* @param    smumgr  the address of the powerplay hardware manager.
-* @param    msg the message to send.
-* @return   The response that came from the SMC.
-*/
-static int tonga_send_msg_to_smc_without_waiting
-                               (struct pp_smumgr *smumgr, uint16_t msg)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       PP_ASSERT_WITH_CODE(
-               1 == SMUM_READ_FIELD(smumgr->device, SMC_RESP_0, SMC_RESP),
-               "Failed to send Previous Message.",
-               );
-       cgs_write_register(smumgr->device, mmSMC_MESSAGE_0, msg);
-
-       return 0;
-}
-
-/*
-* Send a message to the SMC with parameter
-*
-* @param    smumgr:  the address of the powerplay hardware manager.
-* @param    msg: the message to send.
-* @param    parameter: the parameter to send
-* @return   The response that came from the SMC.
-*/
-static int tonga_send_msg_to_smc_with_parameter(struct pp_smumgr *smumgr,
-                               uint16_t msg, uint32_t parameter)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       if (!tonga_is_smc_ram_running(smumgr))
-               return PPSMC_Result_Failed;
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-
-       return tonga_send_msg_to_smc(smumgr, msg);
-}
-
-/*
-* Send a message to the SMC with parameter, do not wait for response
-*
-* @param    smumgr:  the address of the powerplay hardware manager.
-* @param    msg: the message to send.
-* @param    parameter: the parameter to send
-* @return   The response that came from the SMC.
-*/
-static int tonga_send_msg_to_smc_with_parameter_without_waiting(
-                       struct pp_smumgr *smumgr,
-                       uint16_t msg, uint32_t parameter)
-{
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       SMUM_WAIT_FIELD_UNEQUAL(smumgr, SMC_RESP_0, SMC_RESP, 0);
-
-       cgs_write_register(smumgr->device, mmSMC_MSG_ARG_0, parameter);
-
-       return tonga_send_msg_to_smc_without_waiting(smumgr, msg);
-}
-
-/*
- * Read a 32bit value from the SMC SRAM space.
- * ALL PARAMETERS ARE IN HOST BYTE ORDER.
- * @param    smumgr  the address of the powerplay hardware manager.
- * @param    smcAddress the address in the SMC RAM to access.
- * @param    value and output parameter for the data read from the SMC SRAM.
- */
-int tonga_read_smc_sram_dword(struct pp_smumgr *smumgr,
-                                       uint32_t smcAddress, uint32_t *value,
-                                       uint32_t limit)
-{
-       int result;
-
-       result = tonga_set_smc_sram_address(smumgr, smcAddress, limit);
-
-       if (0 != result)
-               return result;
-
-       *value = cgs_read_register(smumgr->device, mmSMC_IND_DATA_0);
-
-       return 0;
-}
-
-/*
- * Write a 32bit value to the SMC SRAM space.
- * ALL PARAMETERS ARE IN HOST BYTE ORDER.
- * @param    smumgr  the address of the powerplay hardware manager.
- * @param    smcAddress the address in the SMC RAM to access.
- * @param    value to write to the SMC SRAM.
- */
-int tonga_write_smc_sram_dword(struct pp_smumgr *smumgr,
-                                       uint32_t smcAddress, uint32_t value,
-                                       uint32_t limit)
-{
-       int result;
-
-       result = tonga_set_smc_sram_address(smumgr, smcAddress, limit);
-
-       if (0 != result)
-               return result;
-
-       cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, value);
-
-       return 0;
-}
-
-static int tonga_smu_fini(struct pp_smumgr *smumgr)
-{
-       struct tonga_smumgr *priv = (struct tonga_smumgr *)(smumgr->backend);
-
-       smu_free_memory(smumgr->device, (void *)priv->smu_buffer.handle);
-       smu_free_memory(smumgr->device, (void *)priv->header_buffer.handle);
-
-       if (smumgr->backend != NULL) {
-               kfree(smumgr->backend);
-               smumgr->backend = NULL;
-       }
-
-       cgs_rel_firmware(smumgr->device, CGS_UCODE_ID_SMU);
-       return 0;
-}
-
-static enum cgs_ucode_id tonga_convert_fw_type_to_cgs(uint32_t fw_type)
-{
-       enum cgs_ucode_id result = CGS_UCODE_ID_MAXIMUM;
-
-       switch (fw_type) {
-       case UCODE_ID_SMU:
-               result = CGS_UCODE_ID_SMU;
-               break;
-       case UCODE_ID_SDMA0:
-               result = CGS_UCODE_ID_SDMA0;
-               break;
-       case UCODE_ID_SDMA1:
-               result = CGS_UCODE_ID_SDMA1;
-               break;
-       case UCODE_ID_CP_CE:
-               result = CGS_UCODE_ID_CP_CE;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = CGS_UCODE_ID_CP_PFP;
-               break;
-       case UCODE_ID_CP_ME:
-               result = CGS_UCODE_ID_CP_ME;
-               break;
-       case UCODE_ID_CP_MEC:
-               result = CGS_UCODE_ID_CP_MEC;
-               break;
-       case UCODE_ID_CP_MEC_JT1:
-               result = CGS_UCODE_ID_CP_MEC_JT1;
-               break;
-       case UCODE_ID_CP_MEC_JT2:
-               result = CGS_UCODE_ID_CP_MEC_JT2;
-               break;
-       case UCODE_ID_RLC_G:
-               result = CGS_UCODE_ID_RLC_G;
-               break;
-       default:
-               break;
-       }
-
-       return result;
-}
-
-/**
- * Convert the PPIRI firmware type to SMU type mask.
- * For MEC, we need to check all MEC related type
-*/
-static uint16_t tonga_get_mask_for_firmware_type(uint16_t firmwareType)
-{
-       uint16_t result = 0;
-
-       switch (firmwareType) {
-       case UCODE_ID_SDMA0:
-               result = UCODE_ID_SDMA0_MASK;
-               break;
-       case UCODE_ID_SDMA1:
-               result = UCODE_ID_SDMA1_MASK;
-               break;
-       case UCODE_ID_CP_CE:
-               result = UCODE_ID_CP_CE_MASK;
-               break;
-       case UCODE_ID_CP_PFP:
-               result = UCODE_ID_CP_PFP_MASK;
-               break;
-       case UCODE_ID_CP_ME:
-               result = UCODE_ID_CP_ME_MASK;
-               break;
-       case UCODE_ID_CP_MEC:
-       case UCODE_ID_CP_MEC_JT1:
-       case UCODE_ID_CP_MEC_JT2:
-               result = UCODE_ID_CP_MEC_MASK;
-               break;
-       case UCODE_ID_RLC_G:
-               result = UCODE_ID_RLC_G_MASK;
-               break;
-       default:
-               break;
-       }
-
-       return result;
-}
-
-/**
- * Check if the FW has been loaded,
- * SMU will not return if loading has not finished.
-*/
-static int tonga_check_fw_load_finish(struct pp_smumgr *smumgr, uint32_t fwType)
-{
-       uint16_t fwMask = tonga_get_mask_for_firmware_type(fwType);
-
-       if (0 != SMUM_WAIT_VFPF_INDIRECT_REGISTER(smumgr, SMC_IND,
-                               SOFT_REGISTERS_TABLE_28, fwMask, fwMask)) {
-               printk(KERN_ERR "[ powerplay ] check firmware loading failed\n");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-/* Populate one firmware image to the data structure */
-static int tonga_populate_single_firmware_entry(struct pp_smumgr *smumgr,
-                               uint16_t firmware_type,
-                               struct SMU_Entry *pentry)
-{
-       int result;
-       struct cgs_firmware_info info = {0};
-
-       result = cgs_get_firmware_info(
-                               smumgr->device,
-                               tonga_convert_fw_type_to_cgs(firmware_type),
-                               &info);
-
-       if (result == 0) {
-               pentry->version = 0;
-               pentry->id = (uint16_t)firmware_type;
-               pentry->image_addr_high = smu_upper_32_bits(info.mc_addr);
-               pentry->image_addr_low = smu_lower_32_bits(info.mc_addr);
-               pentry->meta_data_addr_high = 0;
-               pentry->meta_data_addr_low = 0;
-               pentry->data_size_byte = info.image_size;
-               pentry->num_register_entries = 0;
-
-               if (firmware_type == UCODE_ID_RLC_G)
-                       pentry->flags = 1;
-               else
-                       pentry->flags = 0;
-       } else {
-               return result;
-       }
-
-       return result;
-}
-
-static int tonga_request_smu_reload_fw(struct pp_smumgr *smumgr)
-{
-       struct tonga_smumgr *tonga_smu =
-               (struct tonga_smumgr *)(smumgr->backend);
-       uint16_t fw_to_load;
-       struct SMU_DRAMData_TOC *toc;
-       /**
-        * First time this gets called during SmuMgr init,
-        * we haven't processed SMU header file yet,
-        * so Soft Register Start offset is unknown.
-        * However, for this case, UcodeLoadStatus is already 0,
-        * so we can skip this if the Soft Registers Start offset is 0.
-        */
-       cgs_write_ind_register(smumgr->device,
-               CGS_IND_REG__SMC, ixSOFT_REGISTERS_TABLE_28, 0);
-
-       tonga_send_msg_to_smc_with_parameter(smumgr,
-               PPSMC_MSG_SMU_DRAM_ADDR_HI,
-               tonga_smu->smu_buffer.mc_addr_high);
-       tonga_send_msg_to_smc_with_parameter(smumgr,
-               PPSMC_MSG_SMU_DRAM_ADDR_LO,
-               tonga_smu->smu_buffer.mc_addr_low);
-
-       toc = (struct SMU_DRAMData_TOC *)tonga_smu->pHeader;
-       toc->num_entries = 0;
-       toc->structure_version = 1;
-
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry(smumgr,
-               UCODE_ID_RLC_G,
-               &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n",
-               return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry(smumgr,
-               UCODE_ID_CP_CE,
-               &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n",
-               return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_CP_PFP, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_CP_ME, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_CP_MEC, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_CP_MEC_JT1, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_CP_MEC_JT2, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_SDMA0, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_populate_single_firmware_entry
-               (smumgr, UCODE_ID_SDMA1, &toc->entry[toc->num_entries++]),
-               "Failed to Get Firmware Entry.\n", return -1);
-
-       tonga_send_msg_to_smc_with_parameter(smumgr,
-               PPSMC_MSG_DRV_DRAM_ADDR_HI,
-               tonga_smu->header_buffer.mc_addr_high);
-       tonga_send_msg_to_smc_with_parameter(smumgr,
-               PPSMC_MSG_DRV_DRAM_ADDR_LO,
-               tonga_smu->header_buffer.mc_addr_low);
-
-       fw_to_load = UCODE_ID_RLC_G_MASK
-                       + UCODE_ID_SDMA0_MASK
-                       + UCODE_ID_SDMA1_MASK
-                       + UCODE_ID_CP_CE_MASK
-                       + UCODE_ID_CP_ME_MASK
-                       + UCODE_ID_CP_PFP_MASK
-                       + UCODE_ID_CP_MEC_MASK;
-
-       PP_ASSERT_WITH_CODE(
-               0 == tonga_send_msg_to_smc_with_parameter_without_waiting(
-               smumgr, PPSMC_MSG_LoadUcodes, fw_to_load),
-               "Fail to Request SMU Load uCode", return 0);
-
-       return 0;
-}
-
-static int tonga_request_smu_load_specific_fw(struct pp_smumgr *smumgr,
-                               uint32_t firmwareType)
-{
-       return 0;
-}
-
-/**
- * Upload the SMC firmware to the SMC microcontroller.
- *
- * @param    smumgr  the address of the powerplay hardware manager.
- * @param    pFirmware the data structure containing the various sections of the firmware.
- */
-static int tonga_smu_upload_firmware_image(struct pp_smumgr *smumgr)
-{
-       const uint8_t *src;
-       uint32_t byte_count;
-       uint32_t *data;
-       struct cgs_firmware_info info = {0};
-
-       if (smumgr == NULL || smumgr->device == NULL)
-               return -EINVAL;
-
-       cgs_get_firmware_info(smumgr->device,
-               tonga_convert_fw_type_to_cgs(UCODE_ID_SMU), &info);
-
-       if (info.image_size & 3) {
-               printk(KERN_ERR "[ powerplay ] SMC ucode is not 4 bytes aligned\n");
-               return -EINVAL;
-       }
-
-       if (info.image_size > TONGA_SMC_SIZE) {
-               printk(KERN_ERR "[ powerplay ] SMC address is beyond the SMC RAM area\n");
-               return -EINVAL;
-       }
-
-       cgs_write_register(smumgr->device, mmSMC_IND_INDEX_0, 0x20000);
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
-
-       byte_count = info.image_size;
-       src = (const uint8_t *)info.kptr;
-
-       data = (uint32_t *)src;
-       for (; byte_count >= 4; data++, byte_count -= 4)
-               cgs_write_register(smumgr->device, mmSMC_IND_DATA_0, data[0]);
-
-       SMUM_WRITE_FIELD(smumgr->device, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
-
-       return 0;
-}
 
 static int tonga_start_in_protection_mode(struct pp_smumgr *smumgr)
 {
@@ -623,7 +45,7 @@ static int tonga_start_in_protection_mode(struct pp_smumgr *smumgr)
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                SMC_SYSCON_RESET_CNTL, rst_reg, 1);
 
-       result = tonga_smu_upload_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
        if (result)
                return result;
 
@@ -653,7 +75,7 @@ static int tonga_start_in_protection_mode(struct pp_smumgr *smumgr)
        /**
         * Call Test SMU message with 0x20000 offset to trigger SMU start
         */
-       tonga_send_msg_to_smc_offset(smumgr);
+       smu7_send_msg_to_smc_offset(smumgr);
 
        /* Wait for done bit to be set */
        SMUM_WAIT_VFPF_INDIRECT_FIELD_UNEQUAL(smumgr, SMC_IND,
@@ -690,13 +112,13 @@ static int tonga_start_in_non_protection_mode(struct pp_smumgr *smumgr)
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                SMC_SYSCON_RESET_CNTL, rst_reg, 1);
 
-       result = tonga_smu_upload_firmware_image(smumgr);
+       result = smu7_upload_smu_firmware_image(smumgr);
 
        if (result != 0)
                return result;
 
        /* Set smc instruct start point at 0x0 */
-       tonga_program_jump_on_start(smumgr);
+       smu7_program_jump_on_start(smumgr);
 
 
        SMUM_WRITE_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
@@ -718,7 +140,7 @@ static int tonga_start_smu(struct pp_smumgr *smumgr)
        int result;
 
        /* Only start SMC if SMC RAM is not running */
-       if (!tonga_is_smc_ram_running(smumgr)) {
+       if (!smu7_is_smc_ram_running(smumgr)) {
                /*Check if SMU is running in protected mode*/
                if (0 == SMUM_READ_VFPF_INDIRECT_FIELD(smumgr->device, CGS_IND_REG__SMC,
                                        SMU_FIRMWARE, SMU_MODE)) {
@@ -732,7 +154,7 @@ static int tonga_start_smu(struct pp_smumgr *smumgr)
                }
        }
 
-       result = tonga_request_smu_reload_fw(smumgr);
+       result = smu7_request_smu_load_fw(smumgr);
 
        return result;
 }
@@ -746,67 +168,41 @@ static int tonga_start_smu(struct pp_smumgr *smumgr)
  */
 static int tonga_smu_init(struct pp_smumgr *smumgr)
 {
-       struct tonga_smumgr *tonga_smu;
-       uint8_t *internal_buf;
-       uint64_t mc_addr = 0;
-       /* Allocate memory for backend private data */
-       tonga_smu = (struct tonga_smumgr *)(smumgr->backend);
-       tonga_smu->header_buffer.data_size =
-               ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
-       tonga_smu->smu_buffer.data_size = 200*4096;
-
-       smu_allocate_memory(smumgr->device,
-               tonga_smu->header_buffer.data_size,
-               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
-               PAGE_SIZE,
-               &mc_addr,
-               &tonga_smu->header_buffer.kaddr,
-               &tonga_smu->header_buffer.handle);
-
-       tonga_smu->pHeader = tonga_smu->header_buffer.kaddr;
-       tonga_smu->header_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
-       tonga_smu->header_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
-
-       PP_ASSERT_WITH_CODE((NULL != tonga_smu->pHeader),
-               "Out of memory.",
-               kfree(smumgr->backend);
-               cgs_free_gpu_mem(smumgr->device,
-               (cgs_handle_t)tonga_smu->header_buffer.handle);
-               return -1);
-
-       smu_allocate_memory(smumgr->device,
-               tonga_smu->smu_buffer.data_size,
-               CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
-               PAGE_SIZE,
-               &mc_addr,
-               &tonga_smu->smu_buffer.kaddr,
-               &tonga_smu->smu_buffer.handle);
-
-       internal_buf = tonga_smu->smu_buffer.kaddr;
-       tonga_smu->smu_buffer.mc_addr_high = smu_upper_32_bits(mc_addr);
-       tonga_smu->smu_buffer.mc_addr_low = smu_lower_32_bits(mc_addr);
-
-       PP_ASSERT_WITH_CODE((NULL != internal_buf),
-               "Out of memory.",
-               kfree(smumgr->backend);
-               cgs_free_gpu_mem(smumgr->device,
-               (cgs_handle_t)tonga_smu->smu_buffer.handle);
-               return -1;);
+       struct tonga_smumgr *smu_data = (struct tonga_smumgr *)(smumgr->backend);
+
+       int i;
+
+       if (smu7_init(smumgr))
+               return -EINVAL;
+
+       for (i = 0; i < SMU72_MAX_LEVELS_GRAPHICS; i++)
+               smu_data->activity_target[i] = 30;
 
        return 0;
 }
 
 static const struct pp_smumgr_func tonga_smu_funcs = {
        .smu_init = &tonga_smu_init,
-       .smu_fini = &tonga_smu_fini,
+       .smu_fini = &smu7_smu_fini,
        .start_smu = &tonga_start_smu,
-       .check_fw_load_finish = &tonga_check_fw_load_finish,
-       .request_smu_load_fw = &tonga_request_smu_reload_fw,
-       .request_smu_load_specific_fw = &tonga_request_smu_load_specific_fw,
-       .send_msg_to_smc = &tonga_send_msg_to_smc,
-       .send_msg_to_smc_with_parameter = &tonga_send_msg_to_smc_with_parameter,
+       .check_fw_load_finish = &smu7_check_fw_load_finish,
+       .request_smu_load_fw = &smu7_request_smu_load_fw,
+       .request_smu_load_specific_fw = NULL,
+       .send_msg_to_smc = &smu7_send_msg_to_smc,
+       .send_msg_to_smc_with_parameter = &smu7_send_msg_to_smc_with_parameter,
        .download_pptable_settings = NULL,
        .upload_pptable_settings = NULL,
+       .update_smc_table = tonga_update_smc_table,
+       .get_offsetof = tonga_get_offsetof,
+       .process_firmware_header = tonga_process_firmware_header,
+       .init_smc_table = tonga_init_smc_table,
+       .update_sclk_threshold = tonga_update_sclk_threshold,
+       .thermal_setup_fan_table = tonga_thermal_setup_fan_table,
+       .populate_all_graphic_levels = tonga_populate_all_graphic_levels,
+       .populate_all_memory_levels = tonga_populate_all_memory_levels,
+       .get_mac_definition = tonga_get_mac_definition,
+       .initialize_mc_reg_table = tonga_initialize_mc_reg_table,
+       .is_dpm_running = tonga_is_dpm_running,
 };
 
 int tonga_smum_init(struct pp_smumgr *smumgr)
index 33c788d..8c4f761 100644 (file)
 #ifndef _TONGA_SMUMGR_H_
 #define _TONGA_SMUMGR_H_
 
-struct tonga_buffer_entry {
-       uint32_t data_size;
-       uint32_t mc_addr_low;
-       uint32_t mc_addr_high;
-       void *kaddr;
-       unsigned long  handle;
+#include "smu72_discrete.h"
+
+#include "smu7_smumgr.h"
+
+struct tonga_mc_reg_entry {
+       uint32_t mclk_max;
+       uint32_t mc_data[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
+};
+
+struct tonga_mc_reg_table {
+       uint8_t   last;               /* number of registers*/
+       uint8_t   num_entries;        /* number of entries in mc_reg_table_entry used*/
+       uint16_t  validflag;          /* indicate the corresponding register is valid or not. 1: valid, 0: invalid. bit0->address[0], bit1->address[1], etc.*/
+       struct tonga_mc_reg_entry    mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
+       SMU72_Discrete_MCRegisterAddress mc_reg_address[SMU72_DISCRETE_MC_REGISTER_ARRAY_SIZE];
 };
 
+
 struct tonga_smumgr {
-       uint8_t *pHeader;
-       uint8_t *pMecImage;
-       uint32_t ulSoftRegsStart;
 
-       struct tonga_buffer_entry header_buffer;
-       struct tonga_buffer_entry smu_buffer;
-};
+       struct smu7_smumgr                   smu7_data;
+       struct SMU72_Discrete_DpmTable       smc_state_table;
+       struct SMU72_Discrete_Ulv            ulv_setting;
+       struct SMU72_Discrete_PmFuses  power_tune_table;
+       const struct tonga_pt_defaults  *power_tune_defaults;
+       SMU72_Discrete_MCRegisters      mc_regs;
+       struct tonga_mc_reg_table mc_reg_table;
 
-extern int tonga_smum_init(struct pp_smumgr *smumgr);
-extern int tonga_copy_bytes_to_smc(struct pp_smumgr *smumgr,
-               uint32_t smcStartAddress, const uint8_t *src,
-               uint32_t byteCount, uint32_t limit);
-extern int tonga_read_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smcAddress,
-               uint32_t *value, uint32_t limit);
-extern int tonga_write_smc_sram_dword(struct pp_smumgr *smumgr, uint32_t smcAddress,
-               uint32_t value, uint32_t limit);
+       uint32_t        activity_target[SMU72_MAX_LEVELS_GRAPHICS];
+
+};
 
 #endif
index ee0a61c..7130b04 100644 (file)
@@ -183,8 +183,6 @@ static void arc_pgu_plane_atomic_update(struct drm_plane *plane,
 }
 
 static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
-       .prepare_fb = NULL,
-       .cleanup_fb = NULL,
        .atomic_update = arc_pgu_plane_atomic_update,
 };
 
index 6d4ff34..28e6471 100644 (file)
@@ -198,8 +198,8 @@ static int arcpgu_probe(struct platform_device *pdev)
        int ret;
 
        drm = drm_dev_alloc(&arcpgu_drm_driver, &pdev->dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        ret = arcpgu_load(drm);
        if (ret)
index d83b46a..fb6a418 100644 (file)
@@ -326,8 +326,8 @@ static int hdlcd_drm_bind(struct device *dev)
                return -ENOMEM;
 
        drm = drm_dev_alloc(&hdlcd_driver, dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        drm->dev_private = hdlcd;
        dev_set_drvdata(dev, drm);
index 82171d2..9280358 100644 (file)
@@ -91,7 +91,8 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state)
 
        drm_atomic_helper_commit_modeset_disables(drm, state);
        drm_atomic_helper_commit_modeset_enables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state, true);
+       drm_atomic_helper_commit_planes(drm, state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
 
        malidp_atomic_commit_hw_done(state);
 
@@ -310,8 +311,8 @@ static int malidp_bind(struct device *dev)
                return ret;
 
        drm = drm_dev_alloc(&malidp_driver, dev);
-       if (!drm) {
-               ret = -ENOMEM;
+       if (IS_ERR(drm)) {
+               ret = PTR_ERR(drm);
                goto alloc_fail;
        }
 
index 95558fd..271d2fb 100644 (file)
@@ -49,6 +49,6 @@ void malidp_de_planes_destroy(struct drm_device *drm);
 int malidp_crtc_init(struct drm_device *drm);
 
 /* often used combination of rotational bits */
-#define MALIDP_ROTATED_MASK    (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))
+#define MALIDP_ROTATED_MASK    (DRM_ROTATE_90 | DRM_ROTATE_270)
 
 #endif  /* __MALIDP_DRV_H__ */
index 725098d..82c193e 100644 (file)
@@ -108,7 +108,7 @@ static int malidp_de_plane_check(struct drm_plane *plane,
                return -EINVAL;
 
        /* packed RGB888 / BGR888 can't be rotated or flipped */
-       if (state->rotation != BIT(DRM_ROTATE_0) &&
+       if (state->rotation != DRM_ROTATE_0 &&
            (state->fb->pixel_format == DRM_FORMAT_RGB888 ||
             state->fb->pixel_format == DRM_FORMAT_BGR888))
                return -EINVAL;
@@ -188,9 +188,9 @@ static void malidp_de_plane_update(struct drm_plane *plane,
        /* setup the rotation and axis flip bits */
        if (plane->state->rotation & DRM_ROTATE_MASK)
                val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET;
-       if (plane->state->rotation & BIT(DRM_REFLECT_X))
+       if (plane->state->rotation & DRM_REFLECT_X)
                val |= LAYER_V_FLIP;
-       if (plane->state->rotation & BIT(DRM_REFLECT_Y))
+       if (plane->state->rotation & DRM_REFLECT_Y)
                val |= LAYER_H_FLIP;
 
        /* set the 'enable layer' bit */
@@ -255,12 +255,12 @@ int malidp_de_planes_init(struct drm_device *drm)
                        goto cleanup;
 
                if (!drm->mode_config.rotation_property) {
-                       unsigned long flags = BIT(DRM_ROTATE_0) |
-                                             BIT(DRM_ROTATE_90) |
-                                             BIT(DRM_ROTATE_180) |
-                                             BIT(DRM_ROTATE_270) |
-                                             BIT(DRM_REFLECT_X) |
-                                             BIT(DRM_REFLECT_Y);
+                       unsigned long flags = DRM_ROTATE_0 |
+                                             DRM_ROTATE_90 |
+                                             DRM_ROTATE_180 |
+                                             DRM_ROTATE_270 |
+                                             DRM_REFLECT_X |
+                                             DRM_REFLECT_Y;
                        drm->mode_config.rotation_property =
                                drm_mode_create_rotation_property(drm, flags);
                }
@@ -268,7 +268,7 @@ int malidp_de_planes_init(struct drm_device *drm)
                if (drm->mode_config.rotation_property && (id != DE_SMART))
                        drm_object_attach_property(&plane->base.base,
                                                   drm->mode_config.rotation_property,
-                                                  BIT(DRM_ROTATE_0));
+                                                  DRM_ROTATE_0);
 
                drm_plane_helper_add(&plane->base,
                                     &malidp_de_plane_helper_funcs);
index f5ebdd6..1e0e68f 100644 (file)
@@ -211,7 +211,7 @@ static struct drm_driver armada_drm_driver = {
        .desc                   = "Armada SoC DRM",
        .date                   = "20120730",
        .driver_features        = DRIVER_GEM | DRIVER_MODESET |
-                                 DRIVER_HAVE_IRQ | DRIVER_PRIME,
+                                 DRIVER_PRIME,
        .ioctls                 = armada_ioctls,
        .fops                   = &armada_drm_fops,
 };
index 7d03c51..ca73ad8 100644 (file)
@@ -7,7 +7,6 @@
  * published by the Free Software Foundation.
  */
 #include <linux/errno.h>
-#include <linux/fb.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 
index ff84316..8067918 100644 (file)
@@ -547,7 +547,7 @@ armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
        exp_info.flags = O_RDWR;
        exp_info.priv = obj;
 
-       return dma_buf_export(&exp_info);
+       return drm_gem_dmabuf_export(dev, &exp_info);
 }
 
 struct drm_gem_object *
index 1ee707e..152b4e7 100644 (file)
@@ -121,7 +121,7 @@ armada_ovl_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
        int ret;
 
        ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip,
-                                           BIT(DRM_ROTATE_0),
+                                           DRM_ROTATE_0,
                                            0, INT_MAX, true, false, &visible);
        if (ret)
                return ret;
index c017a93..7a86e24 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/tty.h>
 #include <linux/sysrq.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/init.h>
 
 
index b29a412..608df4c 100644 (file)
@@ -150,7 +150,8 @@ static int ast_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 {
        struct ast_bo *astbo = ast_bo(bo);
 
-       return drm_vma_node_verify_access(&astbo->gem.vma_node, filp);
+       return drm_vma_node_verify_access(&astbo->gem.vma_node,
+                                         filp->private_data);
 }
 
 static int ast_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
index d4a3d61..5f48431 100644 (file)
@@ -457,7 +457,7 @@ atmel_hlcdc_dc_atomic_complete(struct atmel_hlcdc_dc_commit *commit)
 
        /* Apply the atomic update. */
        drm_atomic_helper_commit_modeset_disables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state, false);
+       drm_atomic_helper_commit_planes(dev, old_state, 0);
        drm_atomic_helper_commit_modeset_enables(dev, old_state);
 
        drm_atomic_helper_wait_for_vblanks(dev, old_state);
@@ -797,8 +797,8 @@ static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev)
        int ret;
 
        ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev);
-       if (!ddev)
-               return -ENOMEM;
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
 
        ret = atmel_hlcdc_dc_load(ddev);
        if (ret)
index 52c527f..9d4c030 100644 (file)
@@ -393,7 +393,7 @@ static void atmel_hlcdc_plane_update_format(struct atmel_hlcdc_plane *plane,
 
        if ((state->base.fb->pixel_format == DRM_FORMAT_YUV422 ||
             state->base.fb->pixel_format == DRM_FORMAT_NV61) &&
-           (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))))
+           (state->base.rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)))
                cfg |= ATMEL_HLCDC_YUV422ROT;
 
        atmel_hlcdc_layer_update_cfg(&plane->layer,
@@ -628,7 +628,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
        /*
         * Swap width and size in case of 90 or 270 degrees rotation
         */
-       if (state->base.rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
+       if (state->base.rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)) {
                tmp = state->crtc_w;
                state->crtc_w = state->crtc_h;
                state->crtc_h = tmp;
@@ -677,7 +677,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
                        return -EINVAL;
 
                switch (state->base.rotation & DRM_ROTATE_MASK) {
-               case BIT(DRM_ROTATE_90):
+               case DRM_ROTATE_90:
                        offset = ((y_offset + state->src_y + patched_src_w - 1) /
                                  ydiv) * fb->pitches[i];
                        offset += ((x_offset + state->src_x) / xdiv) *
@@ -686,7 +686,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
                                          fb->pitches[i];
                        state->pstride[i] = -fb->pitches[i] - state->bpp[i];
                        break;
-               case BIT(DRM_ROTATE_180):
+               case DRM_ROTATE_180:
                        offset = ((y_offset + state->src_y + patched_src_h - 1) /
                                  ydiv) * fb->pitches[i];
                        offset += ((x_offset + state->src_x + patched_src_w - 1) /
@@ -695,7 +695,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
                                           state->bpp[i]) - fb->pitches[i];
                        state->pstride[i] = -2 * state->bpp[i];
                        break;
-               case BIT(DRM_ROTATE_270):
+               case DRM_ROTATE_270:
                        offset = ((y_offset + state->src_y) / ydiv) *
                                 fb->pitches[i];
                        offset += ((x_offset + state->src_x + patched_src_h - 1) /
@@ -705,7 +705,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
                                          (2 * state->bpp[i]);
                        state->pstride[i] = fb->pitches[i] - state->bpp[i];
                        break;
-               case BIT(DRM_ROTATE_0):
+               case DRM_ROTATE_0:
                default:
                        offset = ((y_offset + state->src_y) / ydiv) *
                                 fb->pitches[i];
@@ -755,7 +755,7 @@ static int atmel_hlcdc_plane_atomic_check(struct drm_plane *p,
 }
 
 static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
-                                       const struct drm_plane_state *new_state)
+                                       struct drm_plane_state *new_state)
 {
        /*
         * FIXME: we should avoid this const -> non-const cast but it's
@@ -780,7 +780,7 @@ static int atmel_hlcdc_plane_prepare_fb(struct drm_plane *p,
 }
 
 static void atmel_hlcdc_plane_cleanup_fb(struct drm_plane *p,
-                               const struct drm_plane_state *old_state)
+                                        struct drm_plane_state *old_state)
 {
        /*
         * FIXME: we should avoid this const -> non-const cast but it's
@@ -905,7 +905,7 @@ static void atmel_hlcdc_plane_init_properties(struct atmel_hlcdc_plane *plane,
        if (desc->layout.xstride && desc->layout.pstride)
                drm_object_attach_property(&plane->base.base,
                                plane->base.dev->mode_config.rotation_property,
-                               BIT(DRM_ROTATE_0));
+                               DRM_ROTATE_0);
 
        if (desc->layout.csc) {
                /*
@@ -1056,10 +1056,10 @@ atmel_hlcdc_plane_create_properties(struct drm_device *dev)
 
        dev->mode_config.rotation_property =
                        drm_mode_create_rotation_property(dev,
-                                                         BIT(DRM_ROTATE_0) |
-                                                         BIT(DRM_ROTATE_90) |
-                                                         BIT(DRM_ROTATE_180) |
-                                                         BIT(DRM_ROTATE_270));
+                                                         DRM_ROTATE_0 |
+                                                         DRM_ROTATE_90 |
+                                                         DRM_ROTATE_180 |
+                                                         DRM_ROTATE_270);
        if (!dev->mode_config.rotation_property)
                return ERR_PTR(-ENOMEM);
 
index 19b5ada..32dfe41 100644 (file)
@@ -1,5 +1,4 @@
 #include <linux/io.h>
-#include <linux/fb.h>
 #include <linux/console.h>
 
 #include <drm/drmP.h>
index abace82..534227d 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <drm/drm_fb_helper.h>
 
 #include "bochs.h"
 
@@ -153,7 +154,7 @@ static int bochs_kick_out_firmware_fb(struct pci_dev *pdev)
 
        ap->ranges[0].base = pci_resource_start(pdev, 0);
        ap->ranges[0].size = pci_resource_len(pdev, 0);
-       remove_conflicting_framebuffers(ap, "bochsdrmfb", false);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "bochsdrmfb", false);
        kfree(ap);
 
        return 0;
@@ -162,8 +163,15 @@ static int bochs_kick_out_firmware_fb(struct pci_dev *pdev)
 static int bochs_pci_probe(struct pci_dev *pdev,
                           const struct pci_device_id *ent)
 {
+       unsigned long fbsize;
        int ret;
 
+       fbsize = pci_resource_len(pdev, 0);
+       if (fbsize < 4 * 1024 * 1024) {
+               DRM_ERROR("less than 4 MB video memory, ignoring device\n");
+               return -ENOMEM;
+       }
+
        ret = bochs_kick_out_firmware_fb(pdev);
        if (ret)
                return ret;
index 207a2cb..0b4e5d1 100644 (file)
@@ -178,7 +178,7 @@ static void bochs_encoder_init(struct drm_device *dev)
 }
 
 
-int bochs_connector_get_modes(struct drm_connector *connector)
+static int bochs_connector_get_modes(struct drm_connector *connector)
 {
        int count;
 
index 5c5638a..269cfca 100644 (file)
@@ -128,7 +128,8 @@ static int bochs_bo_verify_access(struct ttm_buffer_object *bo,
 {
        struct bochs_bo *bochsbo = bochs_bo(bo);
 
-       return drm_vma_node_verify_access(&bochsbo->gem.vma_node, filp);
+       return drm_vma_node_verify_access(&bochsbo->gem.vma_node,
+                                         filp->private_data);
 }
 
 static int bochs_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
index b590e67..10e12e7 100644 (file)
@@ -17,6 +17,13 @@ config DRM_ANALOGIX_ANX78XX
          the HDMI output of an application processor to MyDP
          or DisplayPort.
 
+config DRM_DUMB_VGA_DAC
+       tristate "Dumb VGA DAC Bridge support"
+       depends on OF
+       select DRM_KMS_HELPER
+       help
+         Support for RGB to VGA DAC based bridges
+
 config DRM_DW_HDMI
        tristate
        select DRM_KMS_HELPER
index efdb07e..cdf3a3c 100644 (file)
@@ -1,6 +1,7 @@
 ccflags-y := -Iinclude/drm
 
 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
+obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
index ec8fb2e..8ed3906 100644 (file)
@@ -922,15 +922,13 @@ static int adv7511_parse_dt(struct device_node *np,
        return 0;
 }
 
-static const int edid_i2c_addr = 0x7e;
-static const int packet_i2c_addr = 0x70;
-static const int cec_i2c_addr = 0x78;
-
 static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 {
        struct adv7511_link_config link_config;
        struct adv7511 *adv7511;
        struct device *dev = &i2c->dev;
+       unsigned int main_i2c_addr = i2c->addr << 1;
+       unsigned int edid_i2c_addr = main_i2c_addr + 4;
        unsigned int val;
        int ret;
 
@@ -991,8 +989,10 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 
        regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR, edid_i2c_addr);
        regmap_write(adv7511->regmap, ADV7511_REG_PACKET_I2C_ADDR,
-                    packet_i2c_addr);
-       regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR, cec_i2c_addr);
+                    main_i2c_addr - 0xa);
+       regmap_write(adv7511->regmap, ADV7511_REG_CEC_I2C_ADDR,
+                    main_i2c_addr - 2);
+
        adv7511_packet_disable(adv7511, 0xffff);
 
        adv7511->i2c_main = i2c;
index 5eebd15..d7f7b7c 100644 (file)
@@ -149,13 +149,12 @@ void adv7533_uninit_cec(struct adv7511 *adv)
        i2c_unregister_device(adv->i2c_cec);
 }
 
-static const int cec_i2c_addr = 0x78;
-
 int adv7533_init_cec(struct adv7511 *adv)
 {
        int ret;
 
-       adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter, cec_i2c_addr >> 1);
+       adv->i2c_cec = i2c_new_dummy(adv->i2c_main->adapter,
+                                    adv->i2c_main->addr - 1);
        if (!adv->i2c_cec)
                return -ENOMEM;
 
index f9f03bc..a2a8236 100644 (file)
@@ -1001,16 +1001,11 @@ static enum drm_connector_status anx78xx_detect(struct drm_connector *connector,
        return connector_status_connected;
 }
 
-static void anx78xx_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_cleanup(connector);
-}
-
 static const struct drm_connector_funcs anx78xx_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = anx78xx_detect,
-       .destroy = anx78xx_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
index 32715da..6e0447f 100644 (file)
@@ -31,6 +31,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "analogix_dp_core.h"
+#include "analogix_dp_reg.h"
 
 #define to_dp(nm)      container_of(nm, struct analogix_dp_device, nm)
 
@@ -97,133 +98,89 @@ static int analogix_dp_detect_hpd(struct analogix_dp_device *dp)
        return 0;
 }
 
-static unsigned char analogix_dp_calc_edid_check_sum(unsigned char *edid_data)
+int analogix_dp_psr_supported(struct device *dev)
 {
-       int i;
-       unsigned char sum = 0;
-
-       for (i = 0; i < EDID_BLOCK_LENGTH; i++)
-               sum = sum + edid_data[i];
+       struct analogix_dp_device *dp = dev_get_drvdata(dev);
 
-       return sum;
+       return dp->psr_support;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_psr_supported);
 
-static int analogix_dp_read_edid(struct analogix_dp_device *dp)
+int analogix_dp_enable_psr(struct device *dev)
 {
-       unsigned char *edid = dp->edid;
-       unsigned int extend_block = 0;
-       unsigned char sum;
-       unsigned char test_vector;
-       int retval;
+       struct analogix_dp_device *dp = dev_get_drvdata(dev);
+       struct edp_vsc_psr psr_vsc;
 
-       /*
-        * EDID device address is 0x50.
-        * However, if necessary, you must have set upper address
-        * into E-EDID in I2C device, 0x30.
-        */
+       if (!dp->psr_support)
+               return -EINVAL;
 
-       /* Read Extension Flag, Number of 128-byte EDID extension blocks */
-       retval = analogix_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
-                                               EDID_EXTENSION_FLAG,
-                                               &extend_block);
-       if (retval)
-               return retval;
+       /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+       memset(&psr_vsc, 0, sizeof(psr_vsc));
+       psr_vsc.sdp_header.HB0 = 0;
+       psr_vsc.sdp_header.HB1 = 0x7;
+       psr_vsc.sdp_header.HB2 = 0x2;
+       psr_vsc.sdp_header.HB3 = 0x8;
 
-       if (extend_block > 0) {
-               dev_dbg(dp->dev, "EDID data includes a single extension!\n");
-
-               /* Read EDID data */
-               retval = analogix_dp_read_bytes_from_i2c(dp,
-                                               I2C_EDID_DEVICE_ADDR,
-                                               EDID_HEADER_PATTERN,
-                                               EDID_BLOCK_LENGTH,
-                                               &edid[EDID_HEADER_PATTERN]);
-               if (retval != 0) {
-                       dev_err(dp->dev, "EDID Read failed!\n");
-                       return -EIO;
-               }
-               sum = analogix_dp_calc_edid_check_sum(edid);
-               if (sum != 0) {
-                       dev_err(dp->dev, "EDID bad checksum!\n");
-                       return -EIO;
-               }
+       psr_vsc.DB0 = 0;
+       psr_vsc.DB1 = EDP_VSC_PSR_STATE_ACTIVE | EDP_VSC_PSR_CRC_VALUES_VALID;
 
-               /* Read additional EDID data */
-               retval = analogix_dp_read_bytes_from_i2c(dp,
-                               I2C_EDID_DEVICE_ADDR,
-                               EDID_BLOCK_LENGTH,
-                               EDID_BLOCK_LENGTH,
-                               &edid[EDID_BLOCK_LENGTH]);
-               if (retval != 0) {
-                       dev_err(dp->dev, "EDID Read failed!\n");
-                       return -EIO;
-               }
-               sum = analogix_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
-               if (sum != 0) {
-                       dev_err(dp->dev, "EDID bad checksum!\n");
-                       return -EIO;
-               }
+       analogix_dp_send_psr_spd(dp, &psr_vsc);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(analogix_dp_enable_psr);
 
-               analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
-                                               &test_vector);
-               if (test_vector & DP_TEST_LINK_EDID_READ) {
-                       analogix_dp_write_byte_to_dpcd(dp,
-                               DP_TEST_EDID_CHECKSUM,
-                               edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
-                       analogix_dp_write_byte_to_dpcd(dp,
-                               DP_TEST_RESPONSE,
-                               DP_TEST_EDID_CHECKSUM_WRITE);
-               }
-       } else {
-               dev_info(dp->dev, "EDID data does not include any extensions.\n");
-
-               /* Read EDID data */
-               retval = analogix_dp_read_bytes_from_i2c(dp,
-                               I2C_EDID_DEVICE_ADDR, EDID_HEADER_PATTERN,
-                               EDID_BLOCK_LENGTH, &edid[EDID_HEADER_PATTERN]);
-               if (retval != 0) {
-                       dev_err(dp->dev, "EDID Read failed!\n");
-                       return -EIO;
-               }
-               sum = analogix_dp_calc_edid_check_sum(edid);
-               if (sum != 0) {
-                       dev_err(dp->dev, "EDID bad checksum!\n");
-                       return -EIO;
-               }
+int analogix_dp_disable_psr(struct device *dev)
+{
+       struct analogix_dp_device *dp = dev_get_drvdata(dev);
+       struct edp_vsc_psr psr_vsc;
 
-               analogix_dp_read_byte_from_dpcd(dp, DP_TEST_REQUEST,
-                                               &test_vector);
-               if (test_vector & DP_TEST_LINK_EDID_READ) {
-                       analogix_dp_write_byte_to_dpcd(dp,
-                               DP_TEST_EDID_CHECKSUM, edid[EDID_CHECKSUM]);
-                       analogix_dp_write_byte_to_dpcd(dp,
-                               DP_TEST_RESPONSE, DP_TEST_EDID_CHECKSUM_WRITE);
-               }
-       }
+       if (!dp->psr_support)
+               return -EINVAL;
+
+       /* Prepare VSC packet as per EDP 1.4 spec, Table 6.9 */
+       memset(&psr_vsc, 0, sizeof(psr_vsc));
+       psr_vsc.sdp_header.HB0 = 0;
+       psr_vsc.sdp_header.HB1 = 0x7;
+       psr_vsc.sdp_header.HB2 = 0x2;
+       psr_vsc.sdp_header.HB3 = 0x8;
+
+       psr_vsc.DB0 = 0;
+       psr_vsc.DB1 = 0;
 
-       dev_dbg(dp->dev, "EDID Read success!\n");
+       analogix_dp_send_psr_spd(dp, &psr_vsc);
        return 0;
 }
+EXPORT_SYMBOL_GPL(analogix_dp_disable_psr);
 
-static int analogix_dp_handle_edid(struct analogix_dp_device *dp)
+static bool analogix_dp_detect_sink_psr(struct analogix_dp_device *dp)
 {
-       u8 buf[12];
-       int i;
-       int retval;
+       unsigned char psr_version;
 
-       /* Read DPCD DP_DPCD_REV~RECEIVE_PORT1_CAP_1 */
-       retval = analogix_dp_read_bytes_from_dpcd(dp, DP_DPCD_REV, 12, buf);
-       if (retval)
-               return retval;
+       drm_dp_dpcd_readb(&dp->aux, DP_PSR_SUPPORT, &psr_version);
+       dev_dbg(dp->dev, "Panel PSR version : %x\n", psr_version);
 
-       /* Read EDID */
-       for (i = 0; i < 3; i++) {
-               retval = analogix_dp_read_edid(dp);
-               if (!retval)
-                       break;
-       }
+       return (psr_version & DP_PSR_IS_SUPPORTED) ? true : false;
+}
 
-       return retval;
+static void analogix_dp_enable_sink_psr(struct analogix_dp_device *dp)
+{
+       unsigned char psr_en;
+
+       /* Disable psr function */
+       drm_dp_dpcd_readb(&dp->aux, DP_PSR_EN_CFG, &psr_en);
+       psr_en &= ~DP_PSR_ENABLE;
+       drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
+
+       /* Main-Link transmitter remains active during PSR active states */
+       psr_en = DP_PSR_MAIN_LINK_ACTIVE | DP_PSR_CRC_VERIFICATION;
+       drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
+
+       /* Enable psr function */
+       psr_en = DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE |
+                DP_PSR_CRC_VERIFICATION;
+       drm_dp_dpcd_writeb(&dp->aux, DP_PSR_EN_CFG, psr_en);
+
+       analogix_dp_enable_psr_crc(dp);
 }
 
 static void
@@ -232,15 +189,15 @@ analogix_dp_enable_rx_to_enhanced_mode(struct analogix_dp_device *dp,
 {
        u8 data;
 
-       analogix_dp_read_byte_from_dpcd(dp, DP_LANE_COUNT_SET, &data);
+       drm_dp_dpcd_readb(&dp->aux, DP_LANE_COUNT_SET, &data);
 
        if (enable)
-               analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
-                                              DP_LANE_COUNT_ENHANCED_FRAME_EN |
-                                              DPCD_LANE_COUNT_SET(data));
+               drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
+                                  DP_LANE_COUNT_ENHANCED_FRAME_EN |
+                                       DPCD_LANE_COUNT_SET(data));
        else
-               analogix_dp_write_byte_to_dpcd(dp, DP_LANE_COUNT_SET,
-                                              DPCD_LANE_COUNT_SET(data));
+               drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET,
+                                  DPCD_LANE_COUNT_SET(data));
 }
 
 static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
@@ -248,7 +205,7 @@ static int analogix_dp_is_enhanced_mode_available(struct analogix_dp_device *dp)
        u8 data;
        int retval;
 
-       analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+       drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
        retval = DPCD_ENHANCED_FRAME_CAP(data);
 
        return retval;
@@ -267,8 +224,8 @@ static void analogix_dp_training_pattern_dis(struct analogix_dp_device *dp)
 {
        analogix_dp_set_training_pattern(dp, DP_NONE);
 
-       analogix_dp_write_byte_to_dpcd(dp, DP_TRAINING_PATTERN_SET,
-                                      DP_TRAINING_PATTERN_DISABLE);
+       drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+                          DP_TRAINING_PATTERN_DISABLE);
 }
 
 static void
@@ -313,8 +270,8 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
        /* Setup RX configuration */
        buf[0] = dp->link_train.link_rate;
        buf[1] = dp->link_train.lane_count;
-       retval = analogix_dp_write_bytes_to_dpcd(dp, DP_LINK_BW_SET, 2, buf);
-       if (retval)
+       retval = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, 2);
+       if (retval < 0)
                return retval;
 
        /* Set TX pre-emphasis to minimum */
@@ -338,20 +295,22 @@ static int analogix_dp_link_start(struct analogix_dp_device *dp)
        analogix_dp_set_training_pattern(dp, TRAINING_PTN1);
 
        /* Set RX training pattern */
-       retval = analogix_dp_write_byte_to_dpcd(dp,
-                       DP_TRAINING_PATTERN_SET,
-                       DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1);
-       if (retval)
+       retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+                                   DP_LINK_SCRAMBLING_DISABLE |
+                                       DP_TRAINING_PATTERN_1);
+       if (retval < 0)
                return retval;
 
        for (lane = 0; lane < lane_count; lane++)
                buf[lane] = DP_TRAIN_PRE_EMPH_LEVEL_0 |
                            DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
 
-       retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
-                                                lane_count, buf);
+       retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, buf,
+                                  lane_count);
+       if (retval < 0)
+               return retval;
 
-       return retval;
+       return 0;
 }
 
 static unsigned char analogix_dp_get_lane_status(u8 link_status[2], int lane)
@@ -503,25 +462,23 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
 
        lane_count = dp->link_train.lane_count;
 
-       retval =  analogix_dp_read_bytes_from_dpcd(dp,
-                       DP_LANE0_1_STATUS, 2, link_status);
-       if (retval)
+       retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2);
+       if (retval < 0)
                return retval;
 
-       retval =  analogix_dp_read_bytes_from_dpcd(dp,
-                       DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
-       if (retval)
+       retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+                                 adjust_request, 2);
+       if (retval < 0)
                return retval;
 
        if (analogix_dp_clock_recovery_ok(link_status, lane_count) == 0) {
                /* set training pattern 2 for EQ */
                analogix_dp_set_training_pattern(dp, TRAINING_PTN2);
 
-               retval = analogix_dp_write_byte_to_dpcd(dp,
-                               DP_TRAINING_PATTERN_SET,
-                               DP_LINK_SCRAMBLING_DISABLE |
-                               DP_TRAINING_PATTERN_2);
-               if (retval)
+               retval = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+                                           DP_LINK_SCRAMBLING_DISABLE |
+                                               DP_TRAINING_PATTERN_2);
+               if (retval < 0)
                        return retval;
 
                dev_info(dp->dev, "Link Training Clock Recovery success\n");
@@ -559,13 +516,12 @@ static int analogix_dp_process_clock_recovery(struct analogix_dp_device *dp)
                analogix_dp_set_lane_link_training(dp,
                        dp->link_train.training_lane[lane], lane);
 
-       retval = analogix_dp_write_bytes_to_dpcd(dp,
-                       DP_TRAINING_LANE0_SET, lane_count,
-                       dp->link_train.training_lane);
-       if (retval)
+       retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
+                                  dp->link_train.training_lane, lane_count);
+       if (retval < 0)
                return retval;
 
-       return retval;
+       return 0;
 }
 
 static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
@@ -578,9 +534,8 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
 
        lane_count = dp->link_train.lane_count;
 
-       retval = analogix_dp_read_bytes_from_dpcd(dp,
-                       DP_LANE0_1_STATUS, 2, link_status);
-       if (retval)
+       retval = drm_dp_dpcd_read(&dp->aux, DP_LANE0_1_STATUS, link_status, 2);
+       if (retval < 0)
                return retval;
 
        if (analogix_dp_clock_recovery_ok(link_status, lane_count)) {
@@ -588,14 +543,14 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
                return -EIO;
        }
 
-       retval = analogix_dp_read_bytes_from_dpcd(dp,
-                       DP_ADJUST_REQUEST_LANE0_1, 2, adjust_request);
-       if (retval)
+       retval = drm_dp_dpcd_read(&dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+                                 adjust_request, 2);
+       if (retval < 0)
                return retval;
 
-       retval = analogix_dp_read_byte_from_dpcd(dp,
-                       DP_LANE_ALIGN_STATUS_UPDATED, &link_align);
-       if (retval)
+       retval = drm_dp_dpcd_readb(&dp->aux, DP_LANE_ALIGN_STATUS_UPDATED,
+                                  &link_align);
+       if (retval < 0)
                return retval;
 
        analogix_dp_get_adjust_training_lane(dp, adjust_request);
@@ -636,10 +591,12 @@ static int analogix_dp_process_equalizer_training(struct analogix_dp_device *dp)
                analogix_dp_set_lane_link_training(dp,
                        dp->link_train.training_lane[lane], lane);
 
-       retval = analogix_dp_write_bytes_to_dpcd(dp, DP_TRAINING_LANE0_SET,
-                       lane_count, dp->link_train.training_lane);
+       retval = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
+                                  dp->link_train.training_lane, lane_count);
+       if (retval < 0)
+               return retval;
 
-       return retval;
+       return 0;
 }
 
 static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
@@ -653,7 +610,7 @@ static void analogix_dp_get_max_rx_bandwidth(struct analogix_dp_device *dp,
         * For DP rev.1.2, Maximum link rate of Main Link lanes
         * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps, 0x14 = 5.4Gbps
         */
-       analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LINK_RATE, &data);
+       drm_dp_dpcd_readb(&dp->aux, DP_MAX_LINK_RATE, &data);
        *bandwidth = data;
 }
 
@@ -666,7 +623,7 @@ static void analogix_dp_get_max_rx_lane_count(struct analogix_dp_device *dp,
         * For DP rev.1.1, Maximum number of Main Link lanes
         * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
         */
-       analogix_dp_read_byte_from_dpcd(dp, DP_MAX_LANE_COUNT, &data);
+       drm_dp_dpcd_readb(&dp->aux, DP_MAX_LANE_COUNT, &data);
        *lane_count = DPCD_MAX_LANE_COUNT(data);
 }
 
@@ -835,19 +792,15 @@ static void analogix_dp_enable_scramble(struct analogix_dp_device *dp,
        if (enable) {
                analogix_dp_enable_scrambling(dp);
 
-               analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET,
-                                               &data);
-               analogix_dp_write_byte_to_dpcd(dp,
-                       DP_TRAINING_PATTERN_SET,
-                       (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
+               drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data);
+               drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+                                  (u8)(data & ~DP_LINK_SCRAMBLING_DISABLE));
        } else {
                analogix_dp_disable_scrambling(dp);
 
-               analogix_dp_read_byte_from_dpcd(dp, DP_TRAINING_PATTERN_SET,
-                                               &data);
-               analogix_dp_write_byte_to_dpcd(dp,
-                       DP_TRAINING_PATTERN_SET,
-                       (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
+               drm_dp_dpcd_readb(&dp->aux, DP_TRAINING_PATTERN_SET, &data);
+               drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
+                                  (u8)(data | DP_LINK_SCRAMBLING_DISABLE));
        }
 }
 
@@ -921,21 +874,85 @@ static void analogix_dp_commit(struct analogix_dp_device *dp)
 
        /* Enable video */
        analogix_dp_start_video(dp);
+
+       dp->psr_support = analogix_dp_detect_sink_psr(dp);
+       if (dp->psr_support)
+               analogix_dp_enable_sink_psr(dp);
 }
 
-int analogix_dp_get_modes(struct drm_connector *connector)
+/*
+ * This function is a bit of a catch-all for panel preparation, hopefully
+ * simplifying the logic of functions that need to prepare/unprepare the panel
+ * below.
+ *
+ * If @prepare is true, this function will prepare the panel. Conversely, if it
+ * is false, the panel will be unprepared.
+ *
+ * If @is_modeset_prepare is true, the function will disregard the current state
+ * of the panel and either prepare/unprepare the panel based on @prepare. Once
+ * it finishes, it will update dp->panel_is_modeset to reflect the current state
+ * of the panel.
+ */
+static int analogix_dp_prepare_panel(struct analogix_dp_device *dp,
+                                    bool prepare, bool is_modeset_prepare)
 {
-       struct analogix_dp_device *dp = to_dp(connector);
-       struct edid *edid = (struct edid *)dp->edid;
-       int num_modes = 0;
+       int ret = 0;
 
-       if (analogix_dp_handle_edid(dp) == 0) {
-               drm_mode_connector_update_edid_property(&dp->connector, edid);
-               num_modes += drm_add_edid_modes(&dp->connector, edid);
-       }
+       if (!dp->plat_data->panel)
+               return 0;
 
-       if (dp->plat_data->panel)
+       mutex_lock(&dp->panel_lock);
+
+       /*
+        * Exit early if this is a temporary prepare/unprepare and we're already
+        * modeset (since we neither want to prepare twice or unprepare early).
+        */
+       if (dp->panel_is_modeset && !is_modeset_prepare)
+               goto out;
+
+       if (prepare)
+               ret = drm_panel_prepare(dp->plat_data->panel);
+       else
+               ret = drm_panel_unprepare(dp->plat_data->panel);
+
+       if (ret)
+               goto out;
+
+       if (is_modeset_prepare)
+               dp->panel_is_modeset = prepare;
+
+out:
+       mutex_unlock(&dp->panel_lock);
+       return ret;
+}
+
+static int analogix_dp_get_modes(struct drm_connector *connector)
+{
+       struct analogix_dp_device *dp = to_dp(connector);
+       struct edid *edid;
+       int ret, num_modes = 0;
+
+       if (dp->plat_data->panel) {
                num_modes += drm_panel_get_modes(dp->plat_data->panel);
+       } else {
+               ret = analogix_dp_prepare_panel(dp, true, false);
+               if (ret) {
+                       DRM_ERROR("Failed to prepare panel (%d)\n", ret);
+                       return 0;
+               }
+
+               edid = drm_get_edid(connector, &dp->aux.ddc);
+               if (edid) {
+                       drm_mode_connector_update_edid_property(&dp->connector,
+                                                               edid);
+                       num_modes += drm_add_edid_modes(&dp->connector, edid);
+                       kfree(edid);
+               }
+
+               ret = analogix_dp_prepare_panel(dp, false, false);
+               if (ret)
+                       DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
+       }
 
        if (dp->plat_data->get_modes)
                num_modes += dp->plat_data->get_modes(dp->plat_data, connector);
@@ -956,29 +973,37 @@ static const struct drm_connector_helper_funcs analogix_dp_connector_helper_func
        .best_encoder = analogix_dp_best_encoder,
 };
 
-enum drm_connector_status
+static enum drm_connector_status
 analogix_dp_detect(struct drm_connector *connector, bool force)
 {
        struct analogix_dp_device *dp = to_dp(connector);
+       enum drm_connector_status status = connector_status_disconnected;
+       int ret;
 
-       if (analogix_dp_detect_hpd(dp))
+       if (dp->plat_data->panel)
+               return connector_status_connected;
+
+       ret = analogix_dp_prepare_panel(dp, true, false);
+       if (ret) {
+               DRM_ERROR("Failed to prepare panel (%d)\n", ret);
                return connector_status_disconnected;
+       }
 
-       return connector_status_connected;
-}
+       if (!analogix_dp_detect_hpd(dp))
+               status = connector_status_connected;
 
-static void analogix_dp_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
+       ret = analogix_dp_prepare_panel(dp, false, false);
+       if (ret)
+               DRM_ERROR("Failed to unprepare panel (%d)\n", ret);
 
+       return status;
 }
 
 static const struct drm_connector_funcs analogix_dp_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = analogix_dp_detect,
-       .destroy = analogix_dp_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -1035,6 +1060,16 @@ static int analogix_dp_bridge_attach(struct drm_bridge *bridge)
        return 0;
 }
 
+static void analogix_dp_bridge_pre_enable(struct drm_bridge *bridge)
+{
+       struct analogix_dp_device *dp = bridge->driver_private;
+       int ret;
+
+       ret = analogix_dp_prepare_panel(dp, true, true);
+       if (ret)
+               DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+}
+
 static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 {
        struct analogix_dp_device *dp = bridge->driver_private;
@@ -1058,6 +1093,7 @@ static void analogix_dp_bridge_enable(struct drm_bridge *bridge)
 static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 {
        struct analogix_dp_device *dp = bridge->driver_private;
+       int ret;
 
        if (dp->dpms_mode != DRM_MODE_DPMS_ON)
                return;
@@ -1077,6 +1113,10 @@ static void analogix_dp_bridge_disable(struct drm_bridge *bridge)
 
        pm_runtime_put_sync(dp->dev);
 
+       ret = analogix_dp_prepare_panel(dp, false, true);
+       if (ret)
+               DRM_ERROR("failed to setup the panel ret = %d\n", ret);
+
        dp->dpms_mode = DRM_MODE_DPMS_OFF;
 }
 
@@ -1165,9 +1205,9 @@ static void analogix_dp_bridge_nop(struct drm_bridge *bridge)
 }
 
 static const struct drm_bridge_funcs analogix_dp_bridge_funcs = {
+       .pre_enable = analogix_dp_bridge_pre_enable,
        .enable = analogix_dp_bridge_enable,
        .disable = analogix_dp_bridge_disable,
-       .pre_enable = analogix_dp_bridge_nop,
        .post_disable = analogix_dp_bridge_nop,
        .mode_set = analogix_dp_bridge_mode_set,
        .attach = analogix_dp_bridge_attach,
@@ -1231,6 +1271,14 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp)
        return 0;
 }
 
+static ssize_t analogix_dpaux_transfer(struct drm_dp_aux *aux,
+                                      struct drm_dp_aux_msg *msg)
+{
+       struct analogix_dp_device *dp = to_dp(aux);
+
+       return analogix_dp_transfer(dp, msg);
+}
+
 int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
                     struct analogix_dp_plat_data *plat_data)
 {
@@ -1254,6 +1302,9 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
        dp->dev = &pdev->dev;
        dp->dpms_mode = DRM_MODE_DPMS_OFF;
 
+       mutex_init(&dp->panel_lock);
+       dp->panel_is_modeset = false;
+
        /*
         * platform dp driver need containor_of the plat_data to get
         * the driver private data, so we need to store the point of
@@ -1333,13 +1384,6 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
 
        phy_power_on(dp->phy);
 
-       if (dp->plat_data->panel) {
-               if (drm_panel_prepare(dp->plat_data->panel)) {
-                       DRM_ERROR("failed to setup the panel\n");
-                       return -EBUSY;
-               }
-       }
-
        analogix_dp_init_dp(dp);
 
        ret = devm_request_threaded_irq(&pdev->dev, dp->irq,
@@ -1355,6 +1399,14 @@ int analogix_dp_bind(struct device *dev, struct drm_device *drm_dev,
        dp->drm_dev = drm_dev;
        dp->encoder = dp->plat_data->encoder;
 
+       dp->aux.name = "DP-AUX";
+       dp->aux.transfer = analogix_dpaux_transfer;
+       dp->aux.dev = &pdev->dev;
+
+       ret = drm_dp_aux_register(&dp->aux);
+       if (ret)
+               goto err_disable_pm_runtime;
+
        ret = analogix_dp_create_bridge(drm_dev, dp);
        if (ret) {
                DRM_ERROR("failed to create bridge (%d)\n", ret);
index b456380..5c6a288 100644 (file)
 #define MAX_CR_LOOP 5
 #define MAX_EQ_LOOP 5
 
-/* I2C EDID Chip ID, Slave Address */
-#define I2C_EDID_DEVICE_ADDR                   0x50
-#define I2C_E_EDID_DEVICE_ADDR                 0x30
-
-#define EDID_BLOCK_LENGTH                      0x80
-#define EDID_HEADER_PATTERN                    0x00
-#define EDID_EXTENSION_FLAG                    0x7e
-#define EDID_CHECKSUM                          0x7f
-
 /* DP_MAX_LANE_COUNT */
 #define DPCD_ENHANCED_FRAME_CAP(x)             (((x) >> 7) & 0x1)
 #define DPCD_MAX_LANE_COUNT(x)                 ((x) & 0x1f)
@@ -166,6 +157,7 @@ struct analogix_dp_device {
        struct drm_device       *drm_dev;
        struct drm_connector    connector;
        struct drm_bridge       *bridge;
+       struct drm_dp_aux       aux;
        struct clk              *clock;
        unsigned int            irq;
        void __iomem            *reg_base;
@@ -176,7 +168,10 @@ struct analogix_dp_device {
        int                     dpms_mode;
        int                     hpd_gpio;
        bool                    force_hpd;
-       unsigned char           edid[EDID_BLOCK_LENGTH * 2];
+       bool                    psr_support;
+
+       struct mutex            panel_lock;
+       bool                    panel_is_modeset;
 
        struct analogix_dp_plat_data *plat_data;
 };
@@ -206,33 +201,6 @@ void analogix_dp_reset_aux(struct analogix_dp_device *dp);
 void analogix_dp_init_aux(struct analogix_dp_device *dp);
 int analogix_dp_get_plug_in_status(struct analogix_dp_device *dp);
 void analogix_dp_enable_sw_function(struct analogix_dp_device *dp);
-int analogix_dp_start_aux_transaction(struct analogix_dp_device *dp);
-int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
-                                  unsigned int reg_addr,
-                                  unsigned char data);
-int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
-                                   unsigned int reg_addr,
-                                   unsigned char *data);
-int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
-                                   unsigned int reg_addr,
-                                   unsigned int count,
-                                   unsigned char data[]);
-int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
-                                    unsigned int reg_addr,
-                                    unsigned int count,
-                                    unsigned char data[]);
-int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
-                                 unsigned int device_addr,
-                                 unsigned int reg_addr);
-int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
-                                  unsigned int device_addr,
-                                  unsigned int reg_addr,
-                                  unsigned int *data);
-int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
-                                   unsigned int device_addr,
-                                   unsigned int reg_addr,
-                                   unsigned int count,
-                                   unsigned char edid[]);
 void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype);
 void analogix_dp_get_link_bandwidth(struct analogix_dp_device *dp, u32 *bwtype);
 void analogix_dp_set_lane_count(struct analogix_dp_device *dp, u32 count);
@@ -278,4 +246,10 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp);
 void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp);
 void analogix_dp_enable_scrambling(struct analogix_dp_device *dp);
 void analogix_dp_disable_scrambling(struct analogix_dp_device *dp);
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp);
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+                             struct edp_vsc_psr *vsc);
+ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
+                            struct drm_dp_aux_msg *msg);
+
 #endif /* _ANALOGIX_DP_CORE_H */
index 48030f0..cd37ac0 100644 (file)
@@ -585,330 +585,6 @@ int analogix_dp_write_byte_to_dpcd(struct analogix_dp_device *dp,
        return retval;
 }
 
-int analogix_dp_read_byte_from_dpcd(struct analogix_dp_device *dp,
-                                   unsigned int reg_addr,
-                                   unsigned char *data)
-{
-       u32 reg;
-       int i;
-       int retval;
-
-       for (i = 0; i < 3; i++) {
-               /* Clear AUX CH data buffer */
-               reg = BUF_CLR;
-               writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-               /* Select DPCD device address */
-               reg = AUX_ADDR_7_0(reg_addr);
-               writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
-               reg = AUX_ADDR_15_8(reg_addr);
-               writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
-               reg = AUX_ADDR_19_16(reg_addr);
-               writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
-               /*
-                * Set DisplayPort transaction and read 1 byte
-                * If bit 3 is 1, DisplayPort transaction.
-                * If Bit 3 is 0, I2C transaction.
-                */
-               reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-               writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-               /* Start AUX transaction */
-               retval = analogix_dp_start_aux_transaction(dp);
-               if (retval == 0)
-                       break;
-
-               dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-       }
-
-       /* Read data buffer */
-       reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
-       *data = (unsigned char)(reg & 0xff);
-
-       return retval;
-}
-
-int analogix_dp_write_bytes_to_dpcd(struct analogix_dp_device *dp,
-                                   unsigned int reg_addr,
-                                   unsigned int count,
-                                   unsigned char data[])
-{
-       u32 reg;
-       unsigned int start_offset;
-       unsigned int cur_data_count;
-       unsigned int cur_data_idx;
-       int i;
-       int retval = 0;
-
-       /* Clear AUX CH data buffer */
-       reg = BUF_CLR;
-       writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-       start_offset = 0;
-       while (start_offset < count) {
-               /* Buffer size of AUX CH is 16 * 4bytes */
-               if ((count - start_offset) > 16)
-                       cur_data_count = 16;
-               else
-                       cur_data_count = count - start_offset;
-
-               for (i = 0; i < 3; i++) {
-                       /* Select DPCD device address */
-                       reg = AUX_ADDR_7_0(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
-                       reg = AUX_ADDR_15_8(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
-                       reg = AUX_ADDR_19_16(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
-                       for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-                            cur_data_idx++) {
-                               reg = data[start_offset + cur_data_idx];
-                               writel(reg, dp->reg_base +
-                                      ANALOGIX_DP_BUF_DATA_0 +
-                                      4 * cur_data_idx);
-                       }
-
-                       /*
-                        * Set DisplayPort transaction and write
-                        * If bit 3 is 1, DisplayPort transaction.
-                        * If Bit 3 is 0, I2C transaction.
-                        */
-                       reg = AUX_LENGTH(cur_data_count) |
-                               AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-                       /* Start AUX transaction */
-                       retval = analogix_dp_start_aux_transaction(dp);
-                       if (retval == 0)
-                               break;
-
-                       dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-                               __func__);
-               }
-
-               start_offset += cur_data_count;
-       }
-
-       return retval;
-}
-
-int analogix_dp_read_bytes_from_dpcd(struct analogix_dp_device *dp,
-                                    unsigned int reg_addr,
-                                    unsigned int count,
-                                    unsigned char data[])
-{
-       u32 reg;
-       unsigned int start_offset;
-       unsigned int cur_data_count;
-       unsigned int cur_data_idx;
-       int i;
-       int retval = 0;
-
-       /* Clear AUX CH data buffer */
-       reg = BUF_CLR;
-       writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-       start_offset = 0;
-       while (start_offset < count) {
-               /* Buffer size of AUX CH is 16 * 4bytes */
-               if ((count - start_offset) > 16)
-                       cur_data_count = 16;
-               else
-                       cur_data_count = count - start_offset;
-
-               /* AUX CH Request Transaction process */
-               for (i = 0; i < 3; i++) {
-                       /* Select DPCD device address */
-                       reg = AUX_ADDR_7_0(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
-                       reg = AUX_ADDR_15_8(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
-                       reg = AUX_ADDR_19_16(reg_addr + start_offset);
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
-                       /*
-                        * Set DisplayPort transaction and read
-                        * If bit 3 is 1, DisplayPort transaction.
-                        * If Bit 3 is 0, I2C transaction.
-                        */
-                       reg = AUX_LENGTH(cur_data_count) |
-                               AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-                       /* Start AUX transaction */
-                       retval = analogix_dp_start_aux_transaction(dp);
-                       if (retval == 0)
-                               break;
-
-                       dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-                               __func__);
-               }
-
-               for (cur_data_idx = 0; cur_data_idx < cur_data_count;
-                   cur_data_idx++) {
-                       reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
-                                                + 4 * cur_data_idx);
-                       data[start_offset + cur_data_idx] =
-                               (unsigned char)reg;
-               }
-
-               start_offset += cur_data_count;
-       }
-
-       return retval;
-}
-
-int analogix_dp_select_i2c_device(struct analogix_dp_device *dp,
-                                 unsigned int device_addr,
-                                 unsigned int reg_addr)
-{
-       u32 reg;
-       int retval;
-
-       /* Set EDID device address */
-       reg = device_addr;
-       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
-       writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
-       writel(0x0, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
-
-       /* Set offset from base address of EDID device */
-       writel(reg_addr, dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
-
-       /*
-        * Set I2C transaction and write address
-        * If bit 3 is 1, DisplayPort transaction.
-        * If Bit 3 is 0, I2C transaction.
-        */
-       reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
-               AUX_TX_COMM_WRITE;
-       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-       /* Start AUX transaction */
-       retval = analogix_dp_start_aux_transaction(dp);
-       if (retval != 0)
-               dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-
-       return retval;
-}
-
-int analogix_dp_read_byte_from_i2c(struct analogix_dp_device *dp,
-                                  unsigned int device_addr,
-                                  unsigned int reg_addr,
-                                  unsigned int *data)
-{
-       u32 reg;
-       int i;
-       int retval;
-
-       for (i = 0; i < 3; i++) {
-               /* Clear AUX CH data buffer */
-               reg = BUF_CLR;
-               writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-               /* Select EDID device */
-               retval = analogix_dp_select_i2c_device(dp, device_addr,
-                                                      reg_addr);
-               if (retval != 0)
-                       continue;
-
-               /*
-                * Set I2C transaction and read data
-                * If bit 3 is 1, DisplayPort transaction.
-                * If Bit 3 is 0, I2C transaction.
-                */
-               reg = AUX_TX_COMM_I2C_TRANSACTION |
-                       AUX_TX_COMM_READ;
-               writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
-
-               /* Start AUX transaction */
-               retval = analogix_dp_start_aux_transaction(dp);
-               if (retval == 0)
-                       break;
-
-               dev_dbg(dp->dev, "%s: Aux Transaction fail!\n", __func__);
-       }
-
-       /* Read data */
-       if (retval == 0)
-               *data = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0);
-
-       return retval;
-}
-
-int analogix_dp_read_bytes_from_i2c(struct analogix_dp_device *dp,
-                                   unsigned int device_addr,
-                                   unsigned int reg_addr,
-                                   unsigned int count,
-                                   unsigned char edid[])
-{
-       u32 reg;
-       unsigned int i, j;
-       unsigned int cur_data_idx;
-       unsigned int defer = 0;
-       int retval = 0;
-
-       for (i = 0; i < count; i += 16) {
-               for (j = 0; j < 3; j++) {
-                       /* Clear AUX CH data buffer */
-                       reg = BUF_CLR;
-                       writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
-
-                       /* Set normal AUX CH command */
-                       reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
-                       reg &= ~ADDR_ONLY;
-                       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
-
-                       /*
-                        * If Rx sends defer, Tx sends only reads
-                        * request without sending address
-                        */
-                       if (!defer)
-                               retval = analogix_dp_select_i2c_device(dp,
-                                               device_addr, reg_addr + i);
-                       else
-                               defer = 0;
-
-                       if (retval == 0) {
-                               /*
-                                * Set I2C transaction and write data
-                                * If bit 3 is 1, DisplayPort transaction.
-                                * If Bit 3 is 0, I2C transaction.
-                                */
-                               reg = AUX_LENGTH(16) |
-                                       AUX_TX_COMM_I2C_TRANSACTION |
-                                       AUX_TX_COMM_READ;
-                               writel(reg, dp->reg_base +
-                                       ANALOGIX_DP_AUX_CH_CTL_1);
-
-                               /* Start AUX transaction */
-                               retval = analogix_dp_start_aux_transaction(dp);
-                               if (retval == 0)
-                                       break;
-
-                               dev_dbg(dp->dev, "%s: Aux Transaction fail!\n",
-                                       __func__);
-                       }
-                       /* Check if Rx sends defer */
-                       reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
-                       if (reg == AUX_RX_COMM_AUX_DEFER ||
-                           reg == AUX_RX_COMM_I2C_DEFER) {
-                               dev_err(dp->dev, "Defer: %d\n\n", reg);
-                               defer = 1;
-                       }
-               }
-
-               for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
-                       reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0
-                                                + 4 * cur_data_idx);
-                       edid[i + cur_data_idx] = (unsigned char)reg;
-               }
-       }
-
-       return retval;
-}
-
 void analogix_dp_set_link_bandwidth(struct analogix_dp_device *dp, u32 bwtype)
 {
        u32 reg;
@@ -1073,34 +749,22 @@ void analogix_dp_set_lane3_link_training(struct analogix_dp_device *dp,
 
 u32 analogix_dp_get_lane0_link_training(struct analogix_dp_device *dp)
 {
-       u32 reg;
-
-       reg = readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
-       return reg;
+       return readl(dp->reg_base + ANALOGIX_DP_LN0_LINK_TRAINING_CTL);
 }
 
 u32 analogix_dp_get_lane1_link_training(struct analogix_dp_device *dp)
 {
-       u32 reg;
-
-       reg = readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
-       return reg;
+       return readl(dp->reg_base + ANALOGIX_DP_LN1_LINK_TRAINING_CTL);
 }
 
 u32 analogix_dp_get_lane2_link_training(struct analogix_dp_device *dp)
 {
-       u32 reg;
-
-       reg = readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
-       return reg;
+       return readl(dp->reg_base + ANALOGIX_DP_LN2_LINK_TRAINING_CTL);
 }
 
 u32 analogix_dp_get_lane3_link_training(struct analogix_dp_device *dp)
 {
-       u32 reg;
-
-       reg = readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
-       return reg;
+       return readl(dp->reg_base + ANALOGIX_DP_LN3_LINK_TRAINING_CTL);
 }
 
 void analogix_dp_reset_macro(struct analogix_dp_device *dp)
@@ -1322,3 +986,181 @@ void analogix_dp_disable_scrambling(struct analogix_dp_device *dp)
        reg |= SCRAMBLING_DISABLE;
        writel(reg, dp->reg_base + ANALOGIX_DP_TRAINING_PTN_SET);
 }
+
+void analogix_dp_enable_psr_crc(struct analogix_dp_device *dp)
+{
+       writel(PSR_VID_CRC_ENABLE, dp->reg_base + ANALOGIX_DP_CRC_CON);
+}
+
+void analogix_dp_send_psr_spd(struct analogix_dp_device *dp,
+                             struct edp_vsc_psr *vsc)
+{
+       unsigned int val;
+
+       /* don't send info frame */
+       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+       val &= ~IF_EN;
+       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+       /* configure single frame update mode */
+       writel(PSR_FRAME_UP_TYPE_BURST | PSR_CRC_SEL_HARDWARE,
+              dp->reg_base + ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL);
+
+       /* configure VSC HB0~HB3 */
+       writel(vsc->sdp_header.HB0, dp->reg_base + ANALOGIX_DP_SPD_HB0);
+       writel(vsc->sdp_header.HB1, dp->reg_base + ANALOGIX_DP_SPD_HB1);
+       writel(vsc->sdp_header.HB2, dp->reg_base + ANALOGIX_DP_SPD_HB2);
+       writel(vsc->sdp_header.HB3, dp->reg_base + ANALOGIX_DP_SPD_HB3);
+
+       /* configure reused VSC PB0~PB3, magic number from vendor */
+       writel(0x00, dp->reg_base + ANALOGIX_DP_SPD_PB0);
+       writel(0x16, dp->reg_base + ANALOGIX_DP_SPD_PB1);
+       writel(0xCE, dp->reg_base + ANALOGIX_DP_SPD_PB2);
+       writel(0x5D, dp->reg_base + ANALOGIX_DP_SPD_PB3);
+
+       /* configure DB0 / DB1 values */
+       writel(vsc->DB0, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB0);
+       writel(vsc->DB1, dp->reg_base + ANALOGIX_DP_VSC_SHADOW_DB1);
+
+       /* set reuse spd inforframe */
+       val = readl(dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+       val |= REUSE_SPD_EN;
+       writel(val, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3);
+
+       /* mark info frame update */
+       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+       val = (val | IF_UP) & ~IF_EN;
+       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+
+       /* send info frame */
+       val = readl(dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+       val |= IF_EN;
+       writel(val, dp->reg_base + ANALOGIX_DP_PKT_SEND_CTL);
+}
+
+ssize_t analogix_dp_transfer(struct analogix_dp_device *dp,
+                            struct drm_dp_aux_msg *msg)
+{
+       u32 reg;
+       u8 *buffer = msg->buffer;
+       int timeout_loop = 0;
+       unsigned int i;
+       int num_transferred = 0;
+
+       /* Buffer size of AUX CH is 16 bytes */
+       if (WARN_ON(msg->size > 16))
+               return -E2BIG;
+
+       /* Clear AUX CH data buffer */
+       reg = BUF_CLR;
+       writel(reg, dp->reg_base + ANALOGIX_DP_BUFFER_DATA_CTL);
+
+       switch (msg->request & ~DP_AUX_I2C_MOT) {
+       case DP_AUX_I2C_WRITE:
+               reg = AUX_TX_COMM_WRITE | AUX_TX_COMM_I2C_TRANSACTION;
+               if (msg->request & DP_AUX_I2C_MOT)
+                       reg |= AUX_TX_COMM_MOT;
+               break;
+
+       case DP_AUX_I2C_READ:
+               reg = AUX_TX_COMM_READ | AUX_TX_COMM_I2C_TRANSACTION;
+               if (msg->request & DP_AUX_I2C_MOT)
+                       reg |= AUX_TX_COMM_MOT;
+               break;
+
+       case DP_AUX_NATIVE_WRITE:
+               reg = AUX_TX_COMM_WRITE | AUX_TX_COMM_DP_TRANSACTION;
+               break;
+
+       case DP_AUX_NATIVE_READ:
+               reg = AUX_TX_COMM_READ | AUX_TX_COMM_DP_TRANSACTION;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       reg |= AUX_LENGTH(msg->size);
+       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_1);
+
+       /* Select DPCD device address */
+       reg = AUX_ADDR_7_0(msg->address);
+       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_7_0);
+       reg = AUX_ADDR_15_8(msg->address);
+       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_15_8);
+       reg = AUX_ADDR_19_16(msg->address);
+       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_ADDR_19_16);
+
+       if (!(msg->request & DP_AUX_I2C_READ)) {
+               for (i = 0; i < msg->size; i++) {
+                       reg = buffer[i];
+                       writel(reg, dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
+                              4 * i);
+                       num_transferred++;
+               }
+       }
+
+       /* Enable AUX CH operation */
+       reg = AUX_EN;
+
+       /* Zero-sized messages specify address-only transactions. */
+       if (msg->size < 1)
+               reg |= ADDR_ONLY;
+
+       writel(reg, dp->reg_base + ANALOGIX_DP_AUX_CH_CTL_2);
+
+       /* Is AUX CH command reply received? */
+       /* TODO: Wait for an interrupt instead of looping? */
+       reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+       while (!(reg & RPLY_RECEIV)) {
+               timeout_loop++;
+               if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) {
+                       dev_err(dp->dev, "AUX CH command reply failed!\n");
+                       return -ETIMEDOUT;
+               }
+               reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+               usleep_range(10, 11);
+       }
+
+       /* Clear interrupt source for AUX CH command reply */
+       writel(RPLY_RECEIV, dp->reg_base + ANALOGIX_DP_INT_STA);
+
+       /* Clear interrupt source for AUX CH access error */
+       reg = readl(dp->reg_base + ANALOGIX_DP_INT_STA);
+       if (reg & AUX_ERR) {
+               writel(AUX_ERR, dp->reg_base + ANALOGIX_DP_INT_STA);
+               return -EREMOTEIO;
+       }
+
+       /* Check AUX CH error access status */
+       reg = readl(dp->reg_base + ANALOGIX_DP_AUX_CH_STA);
+       if ((reg & AUX_STATUS_MASK)) {
+               dev_err(dp->dev, "AUX CH error happened: %d\n\n",
+                       reg & AUX_STATUS_MASK);
+               return -EREMOTEIO;
+       }
+
+       if (msg->request & DP_AUX_I2C_READ) {
+               for (i = 0; i < msg->size; i++) {
+                       reg = readl(dp->reg_base + ANALOGIX_DP_BUF_DATA_0 +
+                                   4 * i);
+                       buffer[i] = (unsigned char)reg;
+                       num_transferred++;
+               }
+       }
+
+       /* Check if Rx sends defer */
+       reg = readl(dp->reg_base + ANALOGIX_DP_AUX_RX_COMM);
+       if (reg == AUX_RX_COMM_AUX_DEFER)
+               msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+       else if (reg == AUX_RX_COMM_I2C_DEFER)
+               msg->reply = DP_AUX_I2C_REPLY_DEFER;
+       else if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE ||
+                (msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_READ)
+               msg->reply = DP_AUX_I2C_REPLY_ACK;
+       else if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_WRITE ||
+                (msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_NATIVE_READ)
+               msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+
+       return num_transferred;
+}
index cdcc6c5..40200c6 100644 (file)
@@ -22,6 +22,8 @@
 #define ANALOGIX_DP_VIDEO_CTL_8                        0x3C
 #define ANALOGIX_DP_VIDEO_CTL_10               0x44
 
+#define ANALOGIX_DP_SPDIF_AUDIO_CTL_0          0xD8
+
 #define ANALOGIX_DP_PLL_REG_1                  0xfc
 #define ANALOGIX_DP_PLL_REG_2                  0x9e4
 #define ANALOGIX_DP_PLL_REG_3                  0x9e8
 
 #define ANALOGIX_DP_PD                         0x12c
 
+#define ANALOGIX_DP_IF_TYPE                    0x244
+#define ANALOGIX_DP_IF_PKT_DB1                 0x254
+#define ANALOGIX_DP_IF_PKT_DB2                 0x258
+#define ANALOGIX_DP_SPD_HB0                    0x2F8
+#define ANALOGIX_DP_SPD_HB1                    0x2FC
+#define ANALOGIX_DP_SPD_HB2                    0x300
+#define ANALOGIX_DP_SPD_HB3                    0x304
+#define ANALOGIX_DP_SPD_PB0                    0x308
+#define ANALOGIX_DP_SPD_PB1                    0x30C
+#define ANALOGIX_DP_SPD_PB2                    0x310
+#define ANALOGIX_DP_SPD_PB3                    0x314
+#define ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL      0x318
+#define ANALOGIX_DP_VSC_SHADOW_DB0             0x31C
+#define ANALOGIX_DP_VSC_SHADOW_DB1             0x320
+
 #define ANALOGIX_DP_LANE_MAP                   0x35C
 
 #define ANALOGIX_DP_ANALOG_CTL_1               0x370
 
 #define ANALOGIX_DP_SOC_GENERAL_CTL            0x800
 
+#define ANALOGIX_DP_CRC_CON                    0x890
+
 /* ANALOGIX_DP_TX_SW_RESET */
 #define RESET_DP_TX                            (0x1 << 0)
 
 #define VID_CHK_UPDATE_TYPE_SHIFT              (4)
 #define VID_CHK_UPDATE_TYPE_1                  (0x1 << 4)
 #define VID_CHK_UPDATE_TYPE_0                  (0x0 << 4)
+#define REUSE_SPD_EN                           (0x1 << 3)
 
 /* ANALOGIX_DP_VIDEO_CTL_8 */
 #define VID_HRES_TH(x)                         (((x) & 0xf) << 4)
 #define REF_CLK_27M                            (0x0 << 0)
 #define REF_CLK_MASK                           (0x1 << 0)
 
+/* ANALOGIX_DP_PSR_FRAME_UPDATE_CTRL */
+#define PSR_FRAME_UP_TYPE_BURST                        (0x1 << 0)
+#define PSR_FRAME_UP_TYPE_SINGLE               (0x0 << 0)
+#define PSR_CRC_SEL_HARDWARE                   (0x1 << 1)
+#define PSR_CRC_SEL_MANUALLY                   (0x0 << 1)
+
 /* ANALOGIX_DP_LANE_MAP */
 #define LANE3_MAP_LOGIC_LANE_0                 (0x0 << 6)
 #define LANE3_MAP_LOGIC_LANE_1                 (0x1 << 6)
 #define VIDEO_MODE_SLAVE_MODE                  (0x1 << 0)
 #define VIDEO_MODE_MASTER_MODE                 (0x0 << 0)
 
+/* ANALOGIX_DP_PKT_SEND_CTL */
+#define IF_UP                                  (0x1 << 4)
+#define IF_EN                                  (0x1 << 0)
+
+/* ANALOGIX_DP_CRC_CON */
+#define PSR_VID_CRC_FLUSH                      (0x1 << 2)
+#define PSR_VID_CRC_ENABLE                     (0x1 << 0)
+
 #endif /* _ANALOGIX_DP_REG_H */
diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c
new file mode 100644 (file)
index 0000000..afec232
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2015-2016 Free Electrons
+ * Copyright (C) 2015-2016 NextThing Co
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/of_graph.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+struct dumb_vga {
+       struct drm_bridge       bridge;
+       struct drm_connector    connector;
+
+       struct i2c_adapter      *ddc;
+};
+
+static inline struct dumb_vga *
+drm_bridge_to_dumb_vga(struct drm_bridge *bridge)
+{
+       return container_of(bridge, struct dumb_vga, bridge);
+}
+
+static inline struct dumb_vga *
+drm_connector_to_dumb_vga(struct drm_connector *connector)
+{
+       return container_of(connector, struct dumb_vga, connector);
+}
+
+static int dumb_vga_get_modes(struct drm_connector *connector)
+{
+       struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
+       struct edid *edid;
+       int ret;
+
+       if (IS_ERR(vga->ddc))
+               goto fallback;
+
+       edid = drm_get_edid(connector, vga->ddc);
+       if (!edid) {
+               DRM_INFO("EDID readout failed, falling back to standard modes\n");
+               goto fallback;
+       }
+
+       drm_mode_connector_update_edid_property(connector, edid);
+       return drm_add_edid_modes(connector, edid);
+
+fallback:
+       /*
+        * In case we cannot retrieve the EDIDs (broken or missing i2c
+        * bus), fallback on the XGA standards
+        */
+       ret = drm_add_modes_noedid(connector, 1920, 1200);
+
+       /* And prefer a mode pretty much anyone can handle */
+       drm_set_preferred_mode(connector, 1024, 768);
+
+       return ret;
+}
+
+static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
+       .get_modes      = dumb_vga_get_modes,
+};
+
+static enum drm_connector_status
+dumb_vga_connector_detect(struct drm_connector *connector, bool force)
+{
+       struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
+
+       /*
+        * Even if we have an I2C bus, we can't assume that the cable
+        * is disconnected if drm_probe_ddc fails. Some cables don't
+        * wire the DDC pins, or the I2C bus might not be working at
+        * all.
+        */
+       if (!IS_ERR(vga->ddc) && drm_probe_ddc(vga->ddc))
+               return connector_status_connected;
+
+       return connector_status_unknown;
+}
+
+static const struct drm_connector_funcs dumb_vga_con_funcs = {
+       .dpms                   = drm_atomic_helper_connector_dpms,
+       .detect                 = dumb_vga_connector_detect,
+       .fill_modes             = drm_helper_probe_single_connector_modes,
+       .destroy                = drm_connector_cleanup,
+       .reset                  = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_connector_destroy_state,
+};
+
+static int dumb_vga_attach(struct drm_bridge *bridge)
+{
+       struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
+       int ret;
+
+       if (!bridge->encoder) {
+               DRM_ERROR("Missing encoder\n");
+               return -ENODEV;
+       }
+
+       drm_connector_helper_add(&vga->connector,
+                                &dumb_vga_con_helper_funcs);
+       ret = drm_connector_init(bridge->dev, &vga->connector,
+                                &dumb_vga_con_funcs, DRM_MODE_CONNECTOR_VGA);
+       if (ret) {
+               DRM_ERROR("Failed to initialize connector\n");
+               return ret;
+       }
+
+       drm_mode_connector_attach_encoder(&vga->connector,
+                                         bridge->encoder);
+
+       return 0;
+}
+
+static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
+       .attach         = dumb_vga_attach,
+};
+
+static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev)
+{
+       struct device_node *end_node, *phandle, *remote;
+       struct i2c_adapter *ddc;
+
+       end_node = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
+       if (!end_node) {
+               dev_err(dev, "Missing connector endpoint\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       remote = of_graph_get_remote_port_parent(end_node);
+       of_node_put(end_node);
+       if (!remote) {
+               dev_err(dev, "Enable to parse remote node\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
+       of_node_put(remote);
+       if (!phandle)
+               return ERR_PTR(-ENODEV);
+
+       ddc = of_get_i2c_adapter_by_node(phandle);
+       of_node_put(phandle);
+       if (!ddc)
+               return ERR_PTR(-EPROBE_DEFER);
+
+       return ddc;
+}
+
+static int dumb_vga_probe(struct platform_device *pdev)
+{
+       struct dumb_vga *vga;
+       int ret;
+
+       vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL);
+       if (!vga)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, vga);
+
+       vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev);
+       if (IS_ERR(vga->ddc)) {
+               if (PTR_ERR(vga->ddc) == -ENODEV) {
+                       dev_dbg(&pdev->dev,
+                               "No i2c bus specified. Disabling EDID readout\n");
+               } else {
+                       dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
+                       return PTR_ERR(vga->ddc);
+               }
+       }
+
+       vga->bridge.funcs = &dumb_vga_bridge_funcs;
+       vga->bridge.of_node = pdev->dev.of_node;
+
+       ret = drm_bridge_add(&vga->bridge);
+       if (ret && !IS_ERR(vga->ddc))
+               i2c_put_adapter(vga->ddc);
+
+       return ret;
+}
+
+static int dumb_vga_remove(struct platform_device *pdev)
+{
+       struct dumb_vga *vga = platform_get_drvdata(pdev);
+
+       drm_bridge_remove(&vga->bridge);
+
+       if (!IS_ERR(vga->ddc))
+               i2c_put_adapter(vga->ddc);
+
+       return 0;
+}
+
+static const struct of_device_id dumb_vga_match[] = {
+       { .compatible = "dumb-vga-dac" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dumb_vga_match);
+
+static struct platform_driver dumb_vga_driver = {
+       .probe  = dumb_vga_probe,
+       .remove = dumb_vga_remove,
+       .driver         = {
+               .name           = "dumb-vga-dac",
+               .of_match_table = dumb_vga_match,
+       },
+};
+module_platform_driver(dumb_vga_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Dumb VGA DAC bridge driver");
+MODULE_LICENSE("GPL");
index 122bb01..8f2d137 100644 (file)
@@ -640,7 +640,6 @@ static struct platform_driver snd_dw_hdmi_driver = {
        .remove = snd_dw_hdmi_remove,
        .driver = {
                .name = DRIVER_NAME,
-               .owner = THIS_MODULE,
                .pm = PM_OPS,
        },
 };
index 77ab473..ab7023e 100644 (file)
@@ -940,10 +940,11 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
         */
 
        /*
-        * AVI data byte 1 differences: Colorspace in bits 4,5 rather than 5,6,
-        * active aspect present in bit 6 rather than 4.
+        * AVI data byte 1 differences: Colorspace in bits 0,1 rather than 5,6,
+        * scan info in bits 4,5 rather than 0,1 and active aspect present in
+        * bit 6 rather than 4.
         */
-       val = (frame.colorspace & 3) << 4 | (frame.scan_mode & 0x3);
+       val = (frame.scan_mode & 3) << 4 | (frame.colorspace & 3);
        if (frame.active_aspect & 15)
                val |= HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT;
        if (frame.top_bar || frame.bottom_bar)
@@ -1476,12 +1477,6 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
        return mode_status;
 }
 
-static void dw_hdmi_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
 static void dw_hdmi_connector_force(struct drm_connector *connector)
 {
        struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
@@ -1498,7 +1493,7 @@ static const struct drm_connector_funcs dw_hdmi_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = dw_hdmi_connector_detect,
-       .destroy = dw_hdmi_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .force = dw_hdmi_connector_force,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
@@ -1812,9 +1807,6 @@ void dw_hdmi_unbind(struct device *dev, struct device *master, void *data)
        /* Disable all interrupts */
        hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-       hdmi->connector.funcs->destroy(&hdmi->connector);
-       hdmi->encoder->funcs->destroy(hdmi->encoder);
-
        clk_disable_unprepare(hdmi->iahb_clk);
        clk_disable_unprepare(hdmi->isfr_clk);
        i2c_put_adapter(hdmi->ddc);
index 93f3dac..f1a9993 100644 (file)
@@ -245,16 +245,11 @@ static enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
        return connector_status_connected;
 }
 
-static void ptn3460_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_cleanup(connector);
-}
-
 static const struct drm_connector_funcs ptn3460_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = ptn3460_detect,
-       .destroy = ptn3460_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
index 583b8ce..6f7c2f9 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/backlight.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/fb.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -484,16 +483,11 @@ static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
        return connector_status_connected;
 }
 
-static void ps8622_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_cleanup(connector);
-}
-
 static const struct drm_connector_funcs ps8622_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = ps8622_detect,
-       .destroy = ps8622_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
index a09825d..44d476e 100644 (file)
@@ -1165,17 +1165,11 @@ static const struct drm_connector_helper_funcs tc_connector_helper_funcs = {
        .best_encoder = tc_connector_best_encoder,
 };
 
-static void tc_connector_destroy(struct drm_connector *connector)
-{
-       drm_connector_unregister(connector);
-       drm_connector_cleanup(connector);
-}
-
 static const struct drm_connector_funcs tc_connector_funcs = {
        .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = tc_connector_detect,
-       .destroy = tc_connector_destroy,
+       .destroy = drm_connector_cleanup,
        .reset = drm_atomic_helper_connector_reset,
        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
index b05f7ea..6c76d12 100644 (file)
@@ -57,7 +57,7 @@ static int cirrus_kick_out_firmware_fb(struct pci_dev *pdev)
 #ifdef CONFIG_X86
        primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 #endif
-       remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "cirrusdrmfb", primary);
        kfree(ap);
 
        return 0;
index 3b5be72..daecf1a 100644 (file)
@@ -13,8 +13,6 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
-#include <linux/fb.h>
-
 #include "cirrus_drv.h"
 
 static void cirrus_dirty_update(struct cirrus_fbdev *afbdev,
index 1cc9ee6..bb2438d 100644 (file)
@@ -150,7 +150,8 @@ static int cirrus_bo_verify_access(struct ttm_buffer_object *bo, struct file *fi
 {
        struct cirrus_bo *cirrusbo = cirrus_bo(bo);
 
-       return drm_vma_node_verify_access(&cirrusbo->gem.vma_node, filp);
+       return drm_vma_node_verify_access(&cirrusbo->gem.vma_node,
+                                         filp->private_data);
 }
 
 static int cirrus_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
index 605bd24..d621c8a 100644 (file)
@@ -430,9 +430,7 @@ struct drm_agp_head *drm_agp_init(struct drm_device *dev)
  * intact so it can still be used. It is safe to call this if AGP is disabled or
  * was already removed.
  *
- * If DRIVER_MODESET is active, nothing is done to protect the modesetting
- * resources from getting destroyed. Drivers are responsible of cleaning them up
- * during device shutdown.
+ * Cleanup is only done for drivers who have DRIVER_LEGACY set.
  */
 void drm_legacy_agp_clear(struct drm_device *dev)
 {
@@ -440,7 +438,7 @@ void drm_legacy_agp_clear(struct drm_device *dev)
 
        if (!dev->agp)
                return;
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
index 2a3ded4..2373960 100644 (file)
@@ -837,8 +837,9 @@ static int drm_atomic_plane_check(struct drm_plane *plane,
        /* Check whether this plane supports the fb pixel format. */
        ret = drm_plane_check_pixel_format(plane, state->fb->pixel_format);
        if (ret) {
-               DRM_DEBUG_ATOMIC("Invalid pixel format %s\n",
-                                drm_get_format_name(state->fb->pixel_format));
+               char *format_name = drm_get_format_name(state->fb->pixel_format);
+               DRM_DEBUG_ATOMIC("Invalid pixel format %s\n", format_name);
+               kfree(format_name);
                return ret;
        }
 
@@ -1690,7 +1691,7 @@ retry:
                                goto out;
                        }
 
-                       prop = drm_property_find(dev, prop_id);
+                       prop = drm_mode_obj_find_prop_id(obj, prop_id);
                        if (!prop) {
                                drm_mode_object_unreference(obj);
                                ret = -ENOENT;
index 20be86d..c3f8347 100644 (file)
@@ -594,7 +594,7 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
        struct drm_plane_state *plane_state;
        int i, ret = 0;
 
-       ret = drm_atomic_helper_normalize_zpos(dev, state);
+       ret = drm_atomic_normalize_zpos(dev, state);
        if (ret)
                return ret;
 
@@ -749,6 +749,8 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
                /* Right function depends upon target state. */
                if (crtc->state->enable && funcs->prepare)
                        funcs->prepare(crtc);
+               else if (funcs->atomic_disable)
+                       funcs->atomic_disable(crtc, old_crtc_state);
                else if (funcs->disable)
                        funcs->disable(crtc);
                else
@@ -886,8 +888,12 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
                 * Each encoder has at most one connector (since we always steal
                 * it away), so we won't call mode_set hooks twice.
                 */
-               if (funcs && funcs->mode_set)
+               if (funcs && funcs->atomic_mode_set) {
+                       funcs->atomic_mode_set(encoder, new_crtc_state,
+                                              connector->state);
+               } else if (funcs && funcs->mode_set) {
                        funcs->mode_set(encoder, mode, adjusted_mode);
+               }
 
                drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
        }
@@ -1003,29 +1009,46 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
  * drm_atomic_helper_wait_for_fences - wait for fences stashed in plane state
  * @dev: DRM device
  * @state: atomic state object with old state structures
+ * @pre_swap: if true, do an interruptible wait
  *
  * For implicit sync, driver should fish the exclusive fence out from the
  * incoming fb's and stash it in the drm_plane_state.  This is called after
  * drm_atomic_helper_swap_state() so it uses the current plane state (and
  * just uses the atomic state to find the changed planes)
+ *
+ * Returns zero if success or < 0 if fence_wait() fails.
  */
-void drm_atomic_helper_wait_for_fences(struct drm_device *dev,
-                           struct drm_atomic_state *state)
+int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
+                                     struct drm_atomic_state *state,
+                                     bool pre_swap)
 {
        struct drm_plane *plane;
        struct drm_plane_state *plane_state;
-       int i;
+       int i, ret;
 
        for_each_plane_in_state(state, plane, plane_state, i) {
-               if (!plane->state->fence)
+               if (!pre_swap)
+                       plane_state = plane->state;
+
+               if (!plane_state->fence)
                        continue;
 
-               WARN_ON(!plane->state->fb);
+               WARN_ON(!plane_state->fb);
+
+               /*
+                * If waiting for fences pre-swap (ie: nonblock), userspace can
+                * still interrupt the operation. Instead of blocking until the
+                * timer expires, make the wait interruptible.
+                */
+               ret = fence_wait(plane_state->fence, pre_swap);
+               if (ret)
+                       return ret;
 
-               fence_wait(plane->state->fence, false);
-               fence_put(plane->state->fence);
-               plane->state->fence = NULL;
+               fence_put(plane_state->fence);
+               plane_state->fence = NULL;
        }
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_atomic_helper_wait_for_fences);
 
@@ -1142,7 +1165,8 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks);
  *
  *     drm_atomic_helper_commit_modeset_enables(dev, state);
  *
- *     drm_atomic_helper_commit_planes(dev, state, true);
+ *     drm_atomic_helper_commit_planes(dev, state,
+ *                                     DRM_PLANE_COMMIT_ACTIVE_ONLY);
  *
  * for committing the atomic update to hardware.  See the kerneldoc entries for
  * these three functions for more details.
@@ -1153,7 +1177,7 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *state)
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state, false);
+       drm_atomic_helper_commit_planes(dev, state, 0);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
@@ -1172,7 +1196,7 @@ static void commit_tail(struct drm_atomic_state *state)
 
        funcs = dev->mode_config.helper_private;
 
-       drm_atomic_helper_wait_for_fences(dev, state);
+       drm_atomic_helper_wait_for_fences(dev, state, false);
 
        drm_atomic_helper_wait_for_dependencies(state);
 
@@ -1231,6 +1255,12 @@ int drm_atomic_helper_commit(struct drm_device *dev,
        if (ret)
                return ret;
 
+       if (!nonblock) {
+               ret = drm_atomic_helper_wait_for_fences(dev, state, true);
+               if (ret)
+                       return ret;
+       }
+
        /*
         * This is the point of no return - everything below never fails except
         * when the hw goes bonghits. Which means we can commit the new state on
@@ -1631,6 +1661,9 @@ int drm_atomic_helper_prepare_planes(struct drm_device *dev,
 
                funcs = plane->helper_private;
 
+               if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc))
+                       continue;
+
                if (funcs->prepare_fb) {
                        ret = funcs->prepare_fb(plane, plane_state);
                        if (ret)
@@ -1647,18 +1680,20 @@ fail:
                if (j >= i)
                        continue;
 
+               if (!drm_atomic_helper_framebuffer_changed(dev, state, plane_state->crtc))
+                       continue;
+
                funcs = plane->helper_private;
 
                if (funcs->cleanup_fb)
                        funcs->cleanup_fb(plane, plane_state);
-
        }
 
        return ret;
 }
 EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
 
-bool plane_crtc_active(struct drm_plane_state *state)
+static bool plane_crtc_active(const struct drm_plane_state *state)
 {
        return state->crtc && state->crtc->state->active;
 }
@@ -1667,7 +1702,7 @@ bool plane_crtc_active(struct drm_plane_state *state)
  * drm_atomic_helper_commit_planes - commit plane state
  * @dev: DRM device
  * @old_state: atomic state object with old state structures
- * @active_only: Only commit on active CRTC if set
+ * @flags: flags for committing plane state
  *
  * This function commits the new plane state using the plane and atomic helper
  * functions for planes and crtcs. It assumes that the atomic state has already
@@ -1687,25 +1722,34 @@ bool plane_crtc_active(struct drm_plane_state *state)
  * most drivers don't need to be immediately notified of plane updates for a
  * disabled CRTC.
  *
- * Unless otherwise needed, drivers are advised to set the @active_only
- * parameters to true in order not to receive plane update notifications related
- * to a disabled CRTC. This avoids the need to manually ignore plane updates in
+ * Unless otherwise needed, drivers are advised to set the ACTIVE_ONLY flag in
+ * @flags in order not to receive plane update notifications related to a
+ * disabled CRTC. This avoids the need to manually ignore plane updates in
  * driver code when the driver and/or hardware can't or just don't need to deal
  * with updates on disabled CRTCs, for example when supporting runtime PM.
  *
- * The drm_atomic_helper_commit() default implementation only sets @active_only
- * to false to most closely match the behaviour of the legacy helpers. This should
- * not be copied blindly by drivers.
+ * Drivers may set the NO_DISABLE_AFTER_MODESET flag in @flags if the relevant
+ * display controllers require to disable a CRTC's planes when the CRTC is
+ * disabled. This function would skip the ->atomic_disable call for a plane if
+ * the CRTC of the old plane state needs a modesetting operation. Of course,
+ * the drivers need to disable the planes in their CRTC disable callbacks
+ * since no one else would do that.
+ *
+ * The drm_atomic_helper_commit() default implementation doesn't set the
+ * ACTIVE_ONLY flag to most closely match the behaviour of the legacy helpers.
+ * This should not be copied blindly by drivers.
  */
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
                                     struct drm_atomic_state *old_state,
-                                    bool active_only)
+                                    uint32_t flags)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *old_crtc_state;
        struct drm_plane *plane;
        struct drm_plane_state *old_plane_state;
        int i;
+       bool active_only = flags & DRM_PLANE_COMMIT_ACTIVE_ONLY;
+       bool no_disable = flags & DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET;
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
                const struct drm_crtc_helper_funcs *funcs;
@@ -1749,10 +1793,19 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
                /*
                 * Special-case disabling the plane if drivers support it.
                 */
-               if (disabling && funcs->atomic_disable)
+               if (disabling && funcs->atomic_disable) {
+                       struct drm_crtc_state *crtc_state;
+
+                       crtc_state = old_plane_state->crtc->state;
+
+                       if (drm_atomic_crtc_needs_modeset(crtc_state) &&
+                           no_disable)
+                               continue;
+
                        funcs->atomic_disable(plane, old_plane_state);
-               else if (plane->state->crtc || disabling)
+               } else if (plane->state->crtc || disabling) {
                        funcs->atomic_update(plane, old_plane_state);
+               }
        }
 
        for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -1831,12 +1884,12 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
 
 /**
  * drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes
- * @crtc: CRTC
+ * @old_crtc_state: atomic state object with the old CRTC state
  * @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks
  *
  * Disables all planes associated with the given CRTC. This can be
- * used for instance in the CRTC helper disable callback to disable
- * all planes before shutting down the display pipeline.
+ * used for instance in the CRTC helper atomic_disable callback to disable
+ * all planes.
  *
  * If the atomic-parameter is set the function calls the CRTC's
  * atomic_begin hook before and atomic_flush hook after disabling the
@@ -1845,9 +1898,11 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
  * It is a bug to call this function without having implemented the
  * ->atomic_disable() plane hook.
  */
-void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
-                                             bool atomic)
+void
+drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state,
+                                        bool atomic)
 {
+       struct drm_crtc *crtc = old_crtc_state->crtc;
        const struct drm_crtc_helper_funcs *crtc_funcs =
                crtc->helper_private;
        struct drm_plane *plane;
@@ -1855,11 +1910,11 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
        if (atomic && crtc_funcs && crtc_funcs->atomic_begin)
                crtc_funcs->atomic_begin(crtc, NULL);
 
-       drm_for_each_plane(plane, crtc->dev) {
+       drm_atomic_crtc_state_for_each_plane(plane, old_crtc_state) {
                const struct drm_plane_helper_funcs *plane_funcs =
                        plane->helper_private;
 
-               if (plane->state->crtc != crtc || !plane_funcs)
+               if (!plane_funcs)
                        continue;
 
                WARN_ON(!plane_funcs->atomic_disable);
@@ -1894,6 +1949,9 @@ void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
        for_each_plane_in_state(old_state, plane, plane_state, i) {
                const struct drm_plane_helper_funcs *funcs;
 
+               if (!drm_atomic_helper_framebuffer_changed(dev, old_state, plane_state->crtc))
+                       continue;
+
                funcs = plane->helper_private;
 
                if (funcs->cleanup_fb)
@@ -2354,7 +2412,7 @@ int __drm_atomic_helper_set_config(struct drm_mode_set *set,
        primary_state->crtc_h = vdisplay;
        primary_state->src_x = set->x << 16;
        primary_state->src_y = set->y << 16;
-       if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) {
+       if (primary_state->rotation & (DRM_ROTATE_90 | DRM_ROTATE_270)) {
                primary_state->src_w = vdisplay << 16;
                primary_state->src_h = hdisplay << 16;
        } else {
@@ -3039,7 +3097,7 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane)
 
        if (plane->state) {
                plane->state->plane = plane;
-               plane->state->rotation = BIT(DRM_ROTATE_0);
+               plane->state->rotation = DRM_ROTATE_0;
        }
 }
 EXPORT_SYMBOL(drm_atomic_helper_plane_reset);
index 4153e8a..6b14351 100644 (file)
@@ -251,7 +251,7 @@ void drm_master_release(struct drm_file *file_priv)
        if (!drm_is_current_master(file_priv))
                goto out;
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET)) {
+       if (drm_core_check_feature(dev, DRIVER_LEGACY)) {
                /*
                 * Since the master is disappearing, so is the
                 * possibility to lock.
index f3c0942..85172a9 100644 (file)
  */
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
-#include <drm/drm_crtc.h>
+#include <drm/drm_blend.h>
 #include <linux/export.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
 
-#include "drm_internal.h"
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * The basic plane composition model supported by standard plane properties only
+ * has a source rectangle (in logical pixels within the &drm_framebuffer), with
+ * sub-pixel accuracy, which is scaled up to a pixel-aligned destination
+ * rectangle in the visible area of a &drm_crtc. The visible area of a CRTC is
+ * defined by the horizontal and vertical visible pixels (stored in @hdisplay
+ * and @vdisplay) of the requested mode (stored in @mode in the
+ * &drm_crtc_state). These two rectangles are both stored in the
+ * &drm_plane_state.
+ *
+ * For the atomic ioctl the following standard (atomic) properties on the plane object
+ * encode the basic plane composition model:
+ *
+ * SRC_X:
+ *     X coordinate offset for the source rectangle within the
+ *     &drm_framebuffer, in 16.16 fixed point. Must be positive.
+ * SRC_Y:
+ *     Y coordinate offset for the source rectangle within the
+ *     &drm_framebuffer, in 16.16 fixed point. Must be positive.
+ * SRC_W:
+ *     Width for the source rectangle within the &drm_framebuffer, in 16.16
+ *     fixed point. SRC_X plus SRC_W must be within the width of the source
+ *     framebuffer. Must be positive.
+ * SRC_H:
+ *     Height for the source rectangle within the &drm_framebuffer, in 16.16
+ *     fixed point. SRC_Y plus SRC_H must be within the height of the source
+ *     framebuffer. Must be positive.
+ * CRTC_X:
+ *     X coordinate offset for the destination rectangle. Can be negative.
+ * CRTC_Y:
+ *     Y coordinate offset for the destination rectangle. Can be negative.
+ * CRTC_W:
+ *     Width for the destination rectangle. CRTC_X plus CRTC_W can extend past
+ *     the currently visible horizontal area of the &drm_crtc.
+ * CRTC_H:
+ *     Height for the destination rectangle. CRTC_Y plus CRTC_H can extend past
+ *     the currently visible vertical area of the &drm_crtc.
+ * FB_ID:
+ *     Mode object ID of the &drm_framebuffer this plane should scan out.
+ * CRTC_ID:
+ *     Mode object ID of the &drm_crtc this plane should be connected to.
+ *
+ * Note that the source rectangle must fully lie within the bounds of the
+ * &drm_framebuffer. The destination rectangle can lie outside of the visible
+ * area of the current mode of the CRTC. It must be apprpriately clipped by the
+ * driver, which can be done by calling drm_plane_helper_check_update(). Drivers
+ * are also allowed to round the subpixel sampling positions appropriately, but
+ * only to the next full pixel. No pixel outside of the source rectangle may
+ * ever be sampled, which is important when applying more sophisticated
+ * filtering than just a bilinear one when scaling. The filtering mode when
+ * scaling is unspecified.
+ *
+ * On top of this basic transformation additional properties can be exposed by
+ * the driver:
+ *
+ * - Rotation is set up with drm_mode_create_rotation_property(). It adds a
+ *   rotation and reflection step between the source and destination rectangles.
+ *   Without this property the rectangle is only scaled, but not rotated or
+ *   reflected.
+ *
+ * - Z position is set up with drm_plane_create_zpos_immutable_property() and
+ *   drm_plane_create_zpos_property(). It controls the visibility of overlapping
+ *   planes. Without this property the primary plane is always below the cursor
+ *   plane, and ordering between all other planes is undefined.
+ *
+ * Note that all the property extensions described here apply either to the
+ * plane or the CRTC (e.g. for the background color, which currently is not
+ * exposed and assumed to be black).
+ */
+
+/**
+ * drm_mode_create_rotation_property - create a new rotation property
+ * @dev: DRM device
+ * @supported_rotations: bitmask of supported rotations and reflections
+ *
+ * This creates a new property with the selected support for transformations.
+ * The resulting property should be stored in @rotation_property in
+ * &drm_mode_config. It then must be attached to each plane which supports
+ * rotations using drm_object_attach_property().
+ *
+ * FIXME: Probably better if the rotation property is created on each plane,
+ * like the zpos property. Otherwise it's not possible to allow different
+ * rotation modes on different planes.
+ *
+ * Since a rotation by 180° degress is the same as reflecting both along the x
+ * and the y axis the rotation property is somewhat redundant. Drivers can use
+ * drm_rotation_simplify() to normalize values of this property.
+ *
+ * The property exposed to userspace is a bitmask property (see
+ * drm_property_create_bitmask()) called "rotation" and has the following
+ * bitmask enumaration values:
+ *
+ * DRM_ROTATE_0:
+ *     "rotate-0"
+ * DRM_ROTATE_90:
+ *     "rotate-90"
+ * DRM_ROTATE_180:
+ *     "rotate-180"
+ * DRM_ROTATE_270:
+ *     "rotate-270"
+ * DRM_REFLECT_X:
+ *     "reflect-x"
+ * DRM_REFELCT_Y:
+ *     "reflect-y"
+ *
+ * Rotation is the specified amount in degrees in counter clockwise direction,
+ * the X and Y axis are within the source rectangle, i.e.  the X/Y axis before
+ * rotation. After reflection, the rotation is applied to the image sampled from
+ * the source rectangle, before scaling it to fit the destination rectangle.
+ */
+struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
+                                                      unsigned int supported_rotations)
+{
+       static const struct drm_prop_enum_list props[] = {
+               { __builtin_ffs(DRM_ROTATE_0) - 1,   "rotate-0" },
+               { __builtin_ffs(DRM_ROTATE_90) - 1,  "rotate-90" },
+               { __builtin_ffs(DRM_ROTATE_180) - 1, "rotate-180" },
+               { __builtin_ffs(DRM_ROTATE_270) - 1, "rotate-270" },
+               { __builtin_ffs(DRM_REFLECT_X) - 1,  "reflect-x" },
+               { __builtin_ffs(DRM_REFLECT_Y) - 1,  "reflect-y" },
+       };
+
+       return drm_property_create_bitmask(dev, 0, "rotation",
+                                          props, ARRAY_SIZE(props),
+                                          supported_rotations);
+}
+EXPORT_SYMBOL(drm_mode_create_rotation_property);
+
+/**
+ * drm_rotation_simplify() - Try to simplify the rotation
+ * @rotation: Rotation to be simplified
+ * @supported_rotations: Supported rotations
+ *
+ * Attempt to simplify the rotation to a form that is supported.
+ * Eg. if the hardware supports everything except DRM_REFLECT_X
+ * one could call this function like this:
+ *
+ * drm_rotation_simplify(rotation, DRM_ROTATE_0 |
+ *                       DRM_ROTATE_90 | DRM_ROTATE_180 |
+ *                       DRM_ROTATE_270 | DRM_REFLECT_Y);
+ *
+ * to eliminate the DRM_ROTATE_X flag. Depending on what kind of
+ * transforms the hardware supports, this function may not
+ * be able to produce a supported transform, so the caller should
+ * check the result afterwards.
+ */
+unsigned int drm_rotation_simplify(unsigned int rotation,
+                                  unsigned int supported_rotations)
+{
+       if (rotation & ~supported_rotations) {
+               rotation ^= DRM_REFLECT_X | DRM_REFLECT_Y;
+               rotation = (rotation & DRM_REFLECT_MASK) |
+                          BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
+       }
+
+       return rotation;
+}
+EXPORT_SYMBOL(drm_rotation_simplify);
 
 /**
  * drm_plane_create_zpos_property - create mutable zpos property
  * If zpos of some planes cannot be changed (like fixed background or
  * cursor/topmost planes), driver should adjust min/max values and assign those
  * planes immutable zpos property with lower or higher values (for more
- * information, see drm_mode_create_zpos_immutable_property() function). In such
+ * information, see drm_plane_create_zpos_immutable_property() function). In such
  * case driver should also assign proper initial zpos values for all planes in
  * its plane_reset() callback, so the planes will be always sorted properly.
  *
+ * See also drm_atomic_normalize_zpos().
+ *
+ * The property exposed to userspace is called "zpos".
+ *
  * Returns:
  * Zero on success, negative errno on failure.
  */
@@ -88,7 +253,9 @@ EXPORT_SYMBOL(drm_plane_create_zpos_property);
  * support for it in drm core. Using this property driver lets userspace
  * to get the arrangement of the planes for blending operation and notifies
  * it that the hardware (or driver) doesn't support changing of the planes'
- * order.
+ * order. For mutable zpos see drm_plane_create_zpos_property().
+ *
+ * The property exposed to userspace is called "zpos".
  *
  * Returns:
  * Zero on success, negative errno on failure.
@@ -127,20 +294,6 @@ static int drm_atomic_state_zpos_cmp(const void *a, const void *b)
                return sa->plane->base.id - sb->plane->base.id;
 }
 
-/**
- * drm_atomic_helper_crtc_normalize_zpos - calculate normalized zpos values
- * @crtc: crtc with planes, which have to be considered for normalization
- * @crtc_state: new atomic state to apply
- *
- * This function checks new states of all planes assigned to given crtc and
- * calculates normalized zpos value for them. Planes are compared first by their
- * zpos values, then by plane id (if zpos equals). Plane with lowest zpos value
- * is at the bottom. The plane_state->normalized_zpos is then filled with unique
- * values from 0 to number of active planes in crtc minus one.
- *
- * RETURNS
- * Zero for success or -errno
- */
 static int drm_atomic_helper_crtc_normalize_zpos(struct drm_crtc *crtc,
                                          struct drm_crtc_state *crtc_state)
 {
@@ -193,20 +346,25 @@ done:
 }
 
 /**
- * drm_atomic_helper_normalize_zpos - calculate normalized zpos values for all
- *                                   crtcs
+ * drm_atomic_normalize_zpos - calculate normalized zpos values for all crtcs
  * @dev: DRM device
  * @state: atomic state of DRM device
  *
  * This function calculates normalized zpos value for all modified planes in
- * the provided atomic state of DRM device. For more information, see
- * drm_atomic_helper_crtc_normalize_zpos() function.
+ * the provided atomic state of DRM device.
+ *
+ * For every CRTC this function checks new states of all planes assigned to
+ * it and calculates normalized zpos value for these planes. Planes are compared
+ * first by their zpos values, then by plane id (if zpos is equal). The plane
+ * with lowest zpos value is at the bottom. The plane_state->normalized_zpos is
+ * then filled with unique values from 0 to number of active planes in crtc
+ * minus one.
  *
  * RETURNS
  * Zero for success or -errno
  */
-int drm_atomic_helper_normalize_zpos(struct drm_device *dev,
-                                    struct drm_atomic_state *state)
+int drm_atomic_normalize_zpos(struct drm_device *dev,
+                             struct drm_atomic_state *state)
 {
        struct drm_crtc *crtc;
        struct drm_crtc_state *crtc_state;
@@ -236,3 +394,4 @@ int drm_atomic_helper_normalize_zpos(struct drm_device *dev,
        }
        return 0;
 }
+EXPORT_SYMBOL(drm_atomic_normalize_zpos);
index 2555430..0ee052b 100644 (file)
 
 #include <linux/err.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 
-#include <drm/drm_crtc.h>
-
-#include "drm/drmP.h"
+#include <drm/drm_bridge.h>
 
 /**
  * DOC: overview
@@ -98,11 +97,11 @@ EXPORT_SYMBOL(drm_bridge_remove);
  * @dev: DRM device
  * @bridge: bridge control structure
  *
- * called by a kms driver to link one of our encoder/bridge to the given
+ * Called by a kms driver to link one of our encoder/bridge to the given
  * bridge.
  *
  * Note that setting up links between the bridge and our encoder/bridge
- * objects needs to be handled by the kms driver itself
+ * objects needs to be handled by the kms driver itself.
  *
  * RETURNS:
  * Zero on success, error code on failure
@@ -124,6 +123,31 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge)
 }
 EXPORT_SYMBOL(drm_bridge_attach);
 
+/**
+ * drm_bridge_detach - deassociate given bridge from its DRM device
+ *
+ * @bridge: bridge control structure
+ *
+ * Called by a kms driver to unlink the given bridge from its DRM device.
+ *
+ * Note that tearing down links between the bridge and our encoder/bridge
+ * objects needs to be handled by the kms driver itself.
+ */
+void drm_bridge_detach(struct drm_bridge *bridge)
+{
+       if (WARN_ON(!bridge))
+               return;
+
+       if (WARN_ON(!bridge->dev))
+               return;
+
+       if (bridge->funcs->detach)
+               bridge->funcs->detach(bridge);
+
+       bridge->dev = NULL;
+}
+EXPORT_SYMBOL(drm_bridge_detach);
+
 /**
  * DOC: bridge callbacks
  *
index c3a12cd..adb1dd7 100644 (file)
@@ -397,7 +397,7 @@ int drm_legacy_addmap_ioctl(struct drm_device *dev, void *data,
                return -EPERM;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        err = drm_addmap_core(dev, map->offset, map->size, map->type,
@@ -443,7 +443,7 @@ int drm_legacy_getmap_ioctl(struct drm_device *dev, void *data,
        int i;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        idx = map->offset;
@@ -545,7 +545,7 @@ EXPORT_SYMBOL(drm_legacy_rmmap_locked);
 void drm_legacy_rmmap(struct drm_device *dev, struct drm_local_map *map)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -558,7 +558,7 @@ void drm_legacy_master_rmmaps(struct drm_device *dev, struct drm_master *master)
 {
        struct drm_map_list *r_list, *list_temp;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -595,7 +595,7 @@ int drm_legacy_rmmap_ioctl(struct drm_device *dev, void *data,
        int ret;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -755,7 +755,7 @@ int drm_legacy_addbufs_agp(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
@@ -905,14 +905,14 @@ int drm_legacy_addbufs_pci(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist), GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
                return -ENOMEM;
        }
 
-       entry->seglist = kzalloc(count * sizeof(*entry->seglist), GFP_KERNEL);
+       entry->seglist = kcalloc(count, sizeof(*entry->seglist), GFP_KERNEL);
        if (!entry->seglist) {
                kfree(entry->buflist);
                mutex_unlock(&dev->struct_mutex);
@@ -923,8 +923,9 @@ int drm_legacy_addbufs_pci(struct drm_device *dev,
        /* Keep the original pagelist until we know all the allocations
         * have succeeded
         */
-       temp_pagelist = kmalloc((dma->page_count + (count << page_order)) *
-                              sizeof(*dma->pagelist), GFP_KERNEL);
+       temp_pagelist = kmalloc_array(dma->page_count + (count << page_order),
+                                     sizeof(*dma->pagelist),
+                                     GFP_KERNEL);
        if (!temp_pagelist) {
                kfree(entry->buflist);
                kfree(entry->seglist);
@@ -1116,8 +1117,7 @@ static int drm_legacy_addbufs_sg(struct drm_device *dev,
                return -EINVAL;
        }
 
-       entry->buflist = kzalloc(count * sizeof(*entry->buflist),
-                               GFP_KERNEL);
+       entry->buflist = kcalloc(count, sizeof(*entry->buflist), GFP_KERNEL);
        if (!entry->buflist) {
                mutex_unlock(&dev->struct_mutex);
                atomic_dec(&dev->buf_alloc);
@@ -1220,7 +1220,7 @@ int drm_legacy_addbufs(struct drm_device *dev, void *data,
        struct drm_buf_desc *request = data;
        int ret;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1266,7 +1266,7 @@ int drm_legacy_infobufs(struct drm_device *dev, void *data,
        int i;
        int count;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1347,7 +1347,7 @@ int drm_legacy_markbufs(struct drm_device *dev, void *data,
        int order;
        struct drm_buf_entry *entry;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1395,7 +1395,7 @@ int drm_legacy_freebufs(struct drm_device *dev, void *data,
        int idx;
        struct drm_buf *buf;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1450,7 +1450,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data,
        struct drm_buf_map *request = data;
        int i;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA))
@@ -1530,7 +1530,7 @@ int drm_legacy_mapbufs(struct drm_device *dev, void *data,
 int drm_legacy_dma_ioctl(struct drm_device *dev, void *data,
                  struct drm_file *file_priv)
 {
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (dev->driver->dma_ioctl)
diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c
new file mode 100644 (file)
index 0000000..d28ffdd
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_color_mgmt.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * Color management or color space adjustments is supported through a set of 5
+ * properties on the &drm_crtc object. They are set up by calling
+ * drm_crtc_enable_color_mgmt().
+ *
+ * "DEGAMMA_LUT”:
+ *     Blob property to set the degamma lookup table (LUT) mapping pixel data
+ *     from the framebuffer before it is given to the transformation matrix.
+ *     The data is interpreted as an array of struct &drm_color_lut elements.
+ *     Hardware might choose not to use the full precision of the LUT elements
+ *     nor use all the elements of the LUT (for example the hardware might
+ *     choose to interpolate between LUT[0] and LUT[4]).
+ *
+ * “DEGAMMA_LUT_SIZE”:
+ *     Unsinged range property to give the size of the lookup table to be set
+ *     on the DEGAMMA_LUT property (the size depends on the underlying
+ *     hardware). If drivers support multiple LUT sizes then they should
+ *     publish the largest size, and sub-sample smaller sized LUTs (e.g. for
+ *     split-gamma modes) appropriately.
+ *
+ * “CTM”:
+ *     Blob property to set the current transformation matrix (CTM) apply to
+ *     pixel data after the lookup through the degamma LUT and before the
+ *     lookup through the gamma LUT. The data is interpreted as a struct
+ *     &drm_color_ctm.
+ *
+ * “GAMMA_LUT”:
+ *     Blob property to set the gamma lookup table (LUT) mapping pixel data
+ *     after the transformation matrix to data sent to the connector. The
+ *     data is interpreted as an array of struct &drm_color_lut elements.
+ *     Hardware might choose not to use the full precision of the LUT elements
+ *     nor use all the elements of the LUT (for example the hardware might
+ *     choose to interpolate between LUT[0] and LUT[4]).
+ *
+ * “GAMMA_LUT_SIZE”:
+ *     Unsigned range property to give the size of the lookup table to be set
+ *     on the GAMMA_LUT property (the size depends on the underlying hardware).
+ *     If drivers support multiple LUT sizes then they should publish the
+ *     largest size, and sub-sample smaller sized LUTs (e.g. for split-gamma
+ *     modes) appropriately.
+ *
+ * There is also support for a legacy gamma table, which is set up by calling
+ * drm_mode_crtc_set_gamma_size(). Drivers which support both should use
+ * drm_atomic_helper_legacy_gamma_set() to alias the legacy gamma ramp with the
+ * "GAMMA_LUT" property above.
+ */
+
+/**
+ * drm_crtc_enable_color_mgmt - enable color management properties
+ * @crtc: DRM CRTC
+ * @degamma_lut_size: the size of the degamma lut (before CSC)
+ * @has_ctm: whether to attach ctm_property for CSC matrix
+ * @gamma_lut_size: the size of the gamma lut (after CSC)
+ *
+ * This function lets the driver enable the color correction
+ * properties on a CRTC. This includes 3 degamma, csc and gamma
+ * properties that userspace can set and 2 size properties to inform
+ * the userspace of the lut sizes. Each of the properties are
+ * optional. The gamma and degamma properties are only attached if
+ * their size is not 0 and ctm_property is only attached if has_ctm is
+ * true.
+ */
+void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
+                               uint degamma_lut_size,
+                               bool has_ctm,
+                               uint gamma_lut_size)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *config = &dev->mode_config;
+
+       if (degamma_lut_size) {
+               drm_object_attach_property(&crtc->base,
+                                          config->degamma_lut_property, 0);
+               drm_object_attach_property(&crtc->base,
+                                          config->degamma_lut_size_property,
+                                          degamma_lut_size);
+       }
+
+       if (has_ctm)
+               drm_object_attach_property(&crtc->base,
+                                          config->ctm_property, 0);
+
+       if (gamma_lut_size) {
+               drm_object_attach_property(&crtc->base,
+                                          config->gamma_lut_property, 0);
+               drm_object_attach_property(&crtc->base,
+                                          config->gamma_lut_size_property,
+                                          gamma_lut_size);
+       }
+}
+EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
+
+/**
+ * drm_mode_crtc_set_gamma_size - set the gamma table size
+ * @crtc: CRTC to set the gamma table size for
+ * @gamma_size: size of the gamma table
+ *
+ * Drivers which support gamma tables should set this to the supported gamma
+ * table size when initializing the CRTC. Currently the drm core only supports a
+ * fixed gamma table size.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+                                int gamma_size)
+{
+       uint16_t *r_base, *g_base, *b_base;
+       int i;
+
+       crtc->gamma_size = gamma_size;
+
+       crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
+                                   GFP_KERNEL);
+       if (!crtc->gamma_store) {
+               crtc->gamma_size = 0;
+               return -ENOMEM;
+       }
+
+       r_base = crtc->gamma_store;
+       g_base = r_base + gamma_size;
+       b_base = g_base + gamma_size;
+       for (i = 0; i < gamma_size; i++) {
+               r_base[i] = i << 8;
+               g_base[i] = i << 8;
+               b_base[i] = i << 8;
+       }
+
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+
+/**
+ * drm_mode_gamma_set_ioctl - set the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
+ * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_lut *crtc_lut = data;
+       struct drm_crtc *crtc;
+       void *r_base, *g_base, *b_base;
+       int size;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       if (crtc->funcs->gamma_set == NULL) {
+               ret = -ENOSYS;
+               goto out;
+       }
+
+       /* memcpy into gamma store */
+       if (crtc_lut->gamma_size != crtc->gamma_size) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       size = crtc_lut->gamma_size * (sizeof(uint16_t));
+       r_base = crtc->gamma_store;
+       if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       g_base = r_base + size;
+       if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       b_base = g_base + size;
+       if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+
+}
+
+/**
+ * drm_mode_gamma_get_ioctl - get the gamma table
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * Copy the current gamma table into the storage provided. This also provides
+ * the gamma table size the driver expects, which can be used to size the
+ * allocated storage.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_lut *crtc_lut = data;
+       struct drm_crtc *crtc;
+       void *r_base, *g_base, *b_base;
+       int size;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       if (!crtc) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       /* memcpy into gamma store */
+       if (crtc_lut->gamma_size != crtc->gamma_size) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       size = crtc_lut->gamma_size * (sizeof(uint16_t));
+       r_base = crtc->gamma_store;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       g_base = r_base + size;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       b_base = g_base + size;
+       if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
+               ret = -EFAULT;
+               goto out;
+       }
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
new file mode 100644 (file)
index 0000000..2db7fb5
--- /dev/null
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_edid.h>
+
+#include "drm_crtc_internal.h"
+#include "drm_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * In DRM connectors are the general abstraction for display sinks, and include
+ * als fixed panels or anything else that can display pixels in some form. As
+ * opposed to all other KMS objects representing hardware (like CRTC, encoder or
+ * plane abstractions) connectors can be hotplugged and unplugged at runtime.
+ * Hence they are reference-counted using drm_connector_reference() and
+ * drm_connector_unreference().
+ *
+ * KMS driver must create, initialize, register and attach at a struct
+ * &drm_connector for each such sink. The instance is created as other KMS
+ * objects and initialized by setting the following fields.
+ *
+ * The connector is then registered with a call to drm_connector_init() with a
+ * pointer to the connector functions and a connector type, and exposed through
+ * sysfs with a call to drm_connector_register().
+ *
+ * Connectors must be attached to an encoder to be used. For devices that map
+ * connectors to encoders 1:1, the connector should be attached at
+ * initialization time with a call to drm_mode_connector_attach_encoder(). The
+ * driver must also set the struct &drm_connector encoder field to point to the
+ * attached encoder.
+ *
+ * For connectors which are not fixed (like built-in panels) the driver needs to
+ * support hotplug notifications. The simplest way to do that is by using the
+ * probe helpers, see drm_kms_helper_poll_init() for connectors which don't have
+ * hardware support for hotplug interrupts. Connectors with hardware hotplug
+ * support can instead use e.g. drm_helper_hpd_irq_event().
+ */
+
+struct drm_conn_prop_enum_list {
+       int type;
+       const char *name;
+       struct ida ida;
+};
+
+/*
+ * Connector and encoder types.
+ */
+static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
+       { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
+       { DRM_MODE_CONNECTOR_VGA, "VGA" },
+       { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+       { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+       { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+       { DRM_MODE_CONNECTOR_Composite, "Composite" },
+       { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
+       { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+       { DRM_MODE_CONNECTOR_Component, "Component" },
+       { DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
+       { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
+       { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+       { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+       { DRM_MODE_CONNECTOR_TV, "TV" },
+       { DRM_MODE_CONNECTOR_eDP, "eDP" },
+       { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
+       { DRM_MODE_CONNECTOR_DSI, "DSI" },
+       { DRM_MODE_CONNECTOR_DPI, "DPI" },
+};
+
+void drm_connector_ida_init(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_init(&drm_connector_enum_list[i].ida);
+}
+
+void drm_connector_ida_destroy(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
+               ida_destroy(&drm_connector_enum_list[i].ida);
+}
+
+/**
+ * drm_connector_get_cmdline_mode - reads the user's cmdline mode
+ * @connector: connector to quwery
+ *
+ * The kernel supports per-connector configuration of its consoles through
+ * use of the video= parameter. This function parses that option and
+ * extracts the user's specified mode (or enable/disable status) for a
+ * particular connector. This is typically only used during the early fbdev
+ * setup.
+ */
+static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+{
+       struct drm_cmdline_mode *mode = &connector->cmdline_mode;
+       char *option = NULL;
+
+       if (fb_get_options(connector->name, &option))
+               return;
+
+       if (!drm_mode_parse_command_line_for_connector(option,
+                                                      connector,
+                                                      mode))
+               return;
+
+       if (mode->force) {
+               const char *s;
+
+               switch (mode->force) {
+               case DRM_FORCE_OFF:
+                       s = "OFF";
+                       break;
+               case DRM_FORCE_ON_DIGITAL:
+                       s = "ON - dig";
+                       break;
+               default:
+               case DRM_FORCE_ON:
+                       s = "ON";
+                       break;
+               }
+
+               DRM_INFO("forcing %s connector %s\n", connector->name, s);
+               connector->force = mode->force;
+       }
+
+       DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
+                     connector->name,
+                     mode->xres, mode->yres,
+                     mode->refresh_specified ? mode->refresh : 60,
+                     mode->rb ? " reduced blanking" : "",
+                     mode->margins ? " with margins" : "",
+                     mode->interlace ?  " interlaced" : "");
+}
+
+static void drm_connector_free(struct kref *kref)
+{
+       struct drm_connector *connector =
+               container_of(kref, struct drm_connector, base.refcount);
+       struct drm_device *dev = connector->dev;
+
+       drm_mode_object_unregister(dev, &connector->base);
+       connector->funcs->destroy(connector);
+}
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_init(struct drm_device *dev,
+                      struct drm_connector *connector,
+                      const struct drm_connector_funcs *funcs,
+                      int connector_type)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret;
+       struct ida *connector_ida =
+               &drm_connector_enum_list[connector_type].ida;
+
+       drm_modeset_lock_all(dev);
+
+       ret = drm_mode_object_get_reg(dev, &connector->base,
+                                     DRM_MODE_OBJECT_CONNECTOR,
+                                     false, drm_connector_free);
+       if (ret)
+               goto out_unlock;
+
+       connector->base.properties = &connector->properties;
+       connector->dev = dev;
+       connector->funcs = funcs;
+
+       ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
+       if (ret < 0)
+               goto out_put;
+       connector->index = ret;
+       ret = 0;
+
+       connector->connector_type = connector_type;
+       connector->connector_type_id =
+               ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
+       if (connector->connector_type_id < 0) {
+               ret = connector->connector_type_id;
+               goto out_put_id;
+       }
+       connector->name =
+               kasprintf(GFP_KERNEL, "%s-%d",
+                         drm_connector_enum_list[connector_type].name,
+                         connector->connector_type_id);
+       if (!connector->name) {
+               ret = -ENOMEM;
+               goto out_put_type_id;
+       }
+
+       INIT_LIST_HEAD(&connector->probed_modes);
+       INIT_LIST_HEAD(&connector->modes);
+       connector->edid_blob_ptr = NULL;
+       connector->status = connector_status_unknown;
+
+       drm_connector_get_cmdline_mode(connector);
+
+       /* We should add connectors at the end to avoid upsetting the connector
+        * index too much. */
+       list_add_tail(&connector->head, &config->connector_list);
+       config->num_connector++;
+
+       if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
+               drm_object_attach_property(&connector->base,
+                                             config->edid_property,
+                                             0);
+
+       drm_object_attach_property(&connector->base,
+                                     config->dpms_property, 0);
+
+       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
+               drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
+       }
+
+       connector->debugfs_entry = NULL;
+out_put_type_id:
+       if (ret)
+               ida_simple_remove(connector_ida, connector->connector_type_id);
+out_put_id:
+       if (ret)
+               ida_simple_remove(&config->connector_ida, connector->index);
+out_put:
+       if (ret)
+               drm_mode_object_unregister(dev, &connector->base);
+
+out_unlock:
+       drm_modeset_unlock_all(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_connector_init);
+
+/**
+ * drm_mode_connector_attach_encoder - attach a connector to an encoder
+ * @connector: connector to attach
+ * @encoder: encoder to attach @connector to
+ *
+ * This function links up a connector to an encoder. Note that the routing
+ * restrictions between encoders and crtcs are exposed to userspace through the
+ * possible_clones and possible_crtcs bitmasks.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+                                     struct drm_encoder *encoder)
+{
+       int i;
+
+       /*
+        * In the past, drivers have attempted to model the static association
+        * of connector to encoder in simple connector/encoder devices using a
+        * direct assignment of connector->encoder = encoder. This connection
+        * is a logical one and the responsibility of the core, so drivers are
+        * expected not to mess with this.
+        *
+        * Note that the error return should've been enough here, but a large
+        * majority of drivers ignores the return value, so add in a big WARN
+        * to get people's attention.
+        */
+       if (WARN_ON(connector->encoder))
+               return -EINVAL;
+
+       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+               if (connector->encoder_ids[i] == 0) {
+                       connector->encoder_ids[i] = encoder->base.id;
+                       return 0;
+               }
+       }
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
+
+static void drm_mode_remove(struct drm_connector *connector,
+                           struct drm_display_mode *mode)
+{
+       list_del(&mode->head);
+       drm_mode_destroy(connector->dev, mode);
+}
+
+/**
+ * drm_connector_cleanup - cleans up an initialised connector
+ * @connector: connector to cleanup
+ *
+ * Cleans up the connector but doesn't free the object.
+ */
+void drm_connector_cleanup(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_display_mode *mode, *t;
+
+       /* The connector should have been removed from userspace long before
+        * it is finally destroyed.
+        */
+       if (WARN_ON(connector->registered))
+               drm_connector_unregister(connector);
+
+       if (connector->tile_group) {
+               drm_mode_put_tile_group(dev, connector->tile_group);
+               connector->tile_group = NULL;
+       }
+
+       list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
+               drm_mode_remove(connector, mode);
+
+       list_for_each_entry_safe(mode, t, &connector->modes, head)
+               drm_mode_remove(connector, mode);
+
+       ida_simple_remove(&drm_connector_enum_list[connector->connector_type].ida,
+                         connector->connector_type_id);
+
+       ida_simple_remove(&dev->mode_config.connector_ida,
+                         connector->index);
+
+       kfree(connector->display_info.bus_formats);
+       drm_mode_object_unregister(dev, &connector->base);
+       kfree(connector->name);
+       connector->name = NULL;
+       list_del(&connector->head);
+       dev->mode_config.num_connector--;
+
+       WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
+       if (connector->state && connector->funcs->atomic_destroy_state)
+               connector->funcs->atomic_destroy_state(connector,
+                                                      connector->state);
+
+       memset(connector, 0, sizeof(*connector));
+}
+EXPORT_SYMBOL(drm_connector_cleanup);
+
+/**
+ * drm_connector_register - register a connector
+ * @connector: the connector to register
+ *
+ * Register userspace interfaces for a connector
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_register(struct drm_connector *connector)
+{
+       int ret;
+
+       if (connector->registered)
+               return 0;
+
+       ret = drm_sysfs_connector_add(connector);
+       if (ret)
+               return ret;
+
+       ret = drm_debugfs_connector_add(connector);
+       if (ret) {
+               goto err_sysfs;
+       }
+
+       if (connector->funcs->late_register) {
+               ret = connector->funcs->late_register(connector);
+               if (ret)
+                       goto err_debugfs;
+       }
+
+       drm_mode_object_register(connector->dev, &connector->base);
+
+       connector->registered = true;
+       return 0;
+
+err_debugfs:
+       drm_debugfs_connector_remove(connector);
+err_sysfs:
+       drm_sysfs_connector_remove(connector);
+       return ret;
+}
+EXPORT_SYMBOL(drm_connector_register);
+
+/**
+ * drm_connector_unregister - unregister a connector
+ * @connector: the connector to unregister
+ *
+ * Unregister userspace interfaces for a connector
+ */
+void drm_connector_unregister(struct drm_connector *connector)
+{
+       if (!connector->registered)
+               return;
+
+       if (connector->funcs->early_unregister)
+               connector->funcs->early_unregister(connector);
+
+       drm_sysfs_connector_remove(connector);
+       drm_debugfs_connector_remove(connector);
+
+       connector->registered = false;
+}
+EXPORT_SYMBOL(drm_connector_unregister);
+
+void drm_connector_unregister_all(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+
+       /* FIXME: taking the mode config mutex ends up in a clash with sysfs */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+               drm_connector_unregister(connector);
+}
+
+int drm_connector_register_all(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+       int ret;
+
+       /* FIXME: taking the mode config mutex ends up in a clash with
+        * fbcon/backlight registration */
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               ret = drm_connector_register(connector);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       mutex_unlock(&dev->mode_config.mutex);
+       drm_connector_unregister_all(dev);
+       return ret;
+}
+
+/**
+ * drm_get_connector_status_name - return a string for connector status
+ * @status: connector status to compute name of
+ *
+ * In contrast to the other drm_get_*_name functions this one here returns a
+ * const pointer and hence is threadsafe.
+ */
+const char *drm_get_connector_status_name(enum drm_connector_status status)
+{
+       if (status == connector_status_connected)
+               return "connected";
+       else if (status == connector_status_disconnected)
+               return "disconnected";
+       else
+               return "unknown";
+}
+EXPORT_SYMBOL(drm_get_connector_status_name);
+
+static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
+       { SubPixelUnknown, "Unknown" },
+       { SubPixelHorizontalRGB, "Horizontal RGB" },
+       { SubPixelHorizontalBGR, "Horizontal BGR" },
+       { SubPixelVerticalRGB, "Vertical RGB" },
+       { SubPixelVerticalBGR, "Vertical BGR" },
+       { SubPixelNone, "None" },
+};
+
+/**
+ * drm_get_subpixel_order_name - return a string for a given subpixel enum
+ * @order: enum of subpixel_order
+ *
+ * Note you could abuse this and return something out of bounds, but that
+ * would be a caller error.  No unscrubbed user data should make it here.
+ */
+const char *drm_get_subpixel_order_name(enum subpixel_order order)
+{
+       return drm_subpixel_enum_list[order].name;
+}
+EXPORT_SYMBOL(drm_get_subpixel_order_name);
+
+static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
+       { DRM_MODE_DPMS_ON, "On" },
+       { DRM_MODE_DPMS_STANDBY, "Standby" },
+       { DRM_MODE_DPMS_SUSPEND, "Suspend" },
+       { DRM_MODE_DPMS_OFF, "Off" }
+};
+DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
+
+/**
+ * drm_display_info_set_bus_formats - set the supported bus formats
+ * @info: display info to store bus formats in
+ * @formats: array containing the supported bus formats
+ * @num_formats: the number of entries in the fmts array
+ *
+ * Store the supported bus formats in display info structure.
+ * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
+ * a full list of available formats.
+ */
+int drm_display_info_set_bus_formats(struct drm_display_info *info,
+                                    const u32 *formats,
+                                    unsigned int num_formats)
+{
+       u32 *fmts = NULL;
+
+       if (!formats && num_formats)
+               return -EINVAL;
+
+       if (formats && num_formats) {
+               fmts = kmemdup(formats, sizeof(*formats) * num_formats,
+                              GFP_KERNEL);
+               if (!fmts)
+                       return -ENOMEM;
+       }
+
+       kfree(info->bus_formats);
+       info->bus_formats = fmts;
+       info->num_bus_formats = num_formats;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_display_info_set_bus_formats);
+
+/* Optional connector properties. */
+static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
+       { DRM_MODE_SCALE_NONE, "None" },
+       { DRM_MODE_SCALE_FULLSCREEN, "Full" },
+       { DRM_MODE_SCALE_CENTER, "Center" },
+       { DRM_MODE_SCALE_ASPECT, "Full aspect" },
+};
+
+static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
+       { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
+       { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
+       { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
+};
+
+static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+};
+DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
+
+static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
+       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
+};
+DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
+                drm_dvi_i_subconnector_enum_list)
+
+static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
+};
+DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
+
+static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
+       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
+       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
+       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
+};
+DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
+                drm_tv_subconnector_enum_list)
+
+int drm_connector_create_standard_properties(struct drm_device *dev)
+{
+       struct drm_property *prop;
+
+       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "EDID", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.edid_property = prop;
+
+       prop = drm_property_create_enum(dev, 0,
+                                  "DPMS", drm_dpms_enum_list,
+                                  ARRAY_SIZE(drm_dpms_enum_list));
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.dpms_property = prop;
+
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "PATH", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.path_property = prop;
+
+       prop = drm_property_create(dev,
+                                  DRM_MODE_PROP_BLOB |
+                                  DRM_MODE_PROP_IMMUTABLE,
+                                  "TILE", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.tile_property = prop;
+
+       return 0;
+}
+
+/**
+ * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
+ * @dev: DRM device
+ *
+ * Called by a driver the first time a DVI-I connector is made.
+ */
+int drm_mode_create_dvi_i_properties(struct drm_device *dev)
+{
+       struct drm_property *dvi_i_selector;
+       struct drm_property *dvi_i_subconnector;
+
+       if (dev->mode_config.dvi_i_select_subconnector_property)
+               return 0;
+
+       dvi_i_selector =
+               drm_property_create_enum(dev, 0,
+                                   "select subconnector",
+                                   drm_dvi_i_select_enum_list,
+                                   ARRAY_SIZE(drm_dvi_i_select_enum_list));
+       dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
+
+       dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector",
+                                   drm_dvi_i_subconnector_enum_list,
+                                   ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
+       dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
+
+/**
+ * drm_create_tv_properties - create TV specific connector properties
+ * @dev: DRM device
+ * @num_modes: number of different TV formats (modes) supported
+ * @modes: array of pointers to strings containing name of each format
+ *
+ * Called by a driver's TV initialization routine, this function creates
+ * the TV specific connector properties for a given device.  Caller is
+ * responsible for allocating a list of format names and passing them to
+ * this routine.
+ */
+int drm_mode_create_tv_properties(struct drm_device *dev,
+                                 unsigned int num_modes,
+                                 const char * const modes[])
+{
+       struct drm_property *tv_selector;
+       struct drm_property *tv_subconnector;
+       unsigned int i;
+
+       if (dev->mode_config.tv_select_subconnector_property)
+               return 0;
+
+       /*
+        * Basic connector properties
+        */
+       tv_selector = drm_property_create_enum(dev, 0,
+                                         "select subconnector",
+                                         drm_tv_select_enum_list,
+                                         ARRAY_SIZE(drm_tv_select_enum_list));
+       if (!tv_selector)
+               goto nomem;
+
+       dev->mode_config.tv_select_subconnector_property = tv_selector;
+
+       tv_subconnector =
+               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                   "subconnector",
+                                   drm_tv_subconnector_enum_list,
+                                   ARRAY_SIZE(drm_tv_subconnector_enum_list));
+       if (!tv_subconnector)
+               goto nomem;
+       dev->mode_config.tv_subconnector_property = tv_subconnector;
+
+       /*
+        * Other, TV specific properties: margins & TV modes.
+        */
+       dev->mode_config.tv_left_margin_property =
+               drm_property_create_range(dev, 0, "left margin", 0, 100);
+       if (!dev->mode_config.tv_left_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_right_margin_property =
+               drm_property_create_range(dev, 0, "right margin", 0, 100);
+       if (!dev->mode_config.tv_right_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_top_margin_property =
+               drm_property_create_range(dev, 0, "top margin", 0, 100);
+       if (!dev->mode_config.tv_top_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_bottom_margin_property =
+               drm_property_create_range(dev, 0, "bottom margin", 0, 100);
+       if (!dev->mode_config.tv_bottom_margin_property)
+               goto nomem;
+
+       dev->mode_config.tv_mode_property =
+               drm_property_create(dev, DRM_MODE_PROP_ENUM,
+                                   "mode", num_modes);
+       if (!dev->mode_config.tv_mode_property)
+               goto nomem;
+
+       for (i = 0; i < num_modes; i++)
+               drm_property_add_enum(dev->mode_config.tv_mode_property, i,
+                                     i, modes[i]);
+
+       dev->mode_config.tv_brightness_property =
+               drm_property_create_range(dev, 0, "brightness", 0, 100);
+       if (!dev->mode_config.tv_brightness_property)
+               goto nomem;
+
+       dev->mode_config.tv_contrast_property =
+               drm_property_create_range(dev, 0, "contrast", 0, 100);
+       if (!dev->mode_config.tv_contrast_property)
+               goto nomem;
+
+       dev->mode_config.tv_flicker_reduction_property =
+               drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
+       if (!dev->mode_config.tv_flicker_reduction_property)
+               goto nomem;
+
+       dev->mode_config.tv_overscan_property =
+               drm_property_create_range(dev, 0, "overscan", 0, 100);
+       if (!dev->mode_config.tv_overscan_property)
+               goto nomem;
+
+       dev->mode_config.tv_saturation_property =
+               drm_property_create_range(dev, 0, "saturation", 0, 100);
+       if (!dev->mode_config.tv_saturation_property)
+               goto nomem;
+
+       dev->mode_config.tv_hue_property =
+               drm_property_create_range(dev, 0, "hue", 0, 100);
+       if (!dev->mode_config.tv_hue_property)
+               goto nomem;
+
+       return 0;
+nomem:
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(drm_mode_create_tv_properties);
+
+/**
+ * drm_mode_create_scaling_mode_property - create scaling mode property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ */
+int drm_mode_create_scaling_mode_property(struct drm_device *dev)
+{
+       struct drm_property *scaling_mode;
+
+       if (dev->mode_config.scaling_mode_property)
+               return 0;
+
+       scaling_mode =
+               drm_property_create_enum(dev, 0, "scaling mode",
+                               drm_scaling_mode_enum_list,
+                                   ARRAY_SIZE(drm_scaling_mode_enum_list));
+
+       dev->mode_config.scaling_mode_property = scaling_mode;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
+
+/**
+ * drm_mode_create_aspect_ratio_property - create aspect ratio property
+ * @dev: DRM device
+ *
+ * Called by a driver the first time it's needed, must be attached to desired
+ * connectors.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
+{
+       if (dev->mode_config.aspect_ratio_property)
+               return 0;
+
+       dev->mode_config.aspect_ratio_property =
+               drm_property_create_enum(dev, 0, "aspect ratio",
+                               drm_aspect_ratio_enum_list,
+                               ARRAY_SIZE(drm_aspect_ratio_enum_list));
+
+       if (dev->mode_config.aspect_ratio_property == NULL)
+               return -ENOMEM;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
+
+/**
+ * drm_mode_create_suggested_offset_properties - create suggests offset properties
+ * @dev: DRM device
+ *
+ * Create the the suggested x/y offset property for connectors.
+ */
+int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
+{
+       if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
+               return 0;
+
+       dev->mode_config.suggested_x_property =
+               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
+
+       dev->mode_config.suggested_y_property =
+               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
+
+       if (dev->mode_config.suggested_x_property == NULL ||
+           dev->mode_config.suggested_y_property == NULL)
+               return -ENOMEM;
+       return 0;
+}
+EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
+
+/**
+ * drm_mode_connector_set_path_property - set tile property on connector
+ * @connector: connector to set property on.
+ * @path: path to use for property; must not be NULL.
+ *
+ * This creates a property to expose to userspace to specify a
+ * connector path. This is mainly used for DisplayPort MST where
+ * connectors have a topology and we want to allow userspace to give
+ * them more meaningful names.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+                                        const char *path)
+{
+       struct drm_device *dev = connector->dev;
+       int ret;
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->path_blob_ptr,
+                                              strlen(path) + 1,
+                                              path,
+                                              &connector->base,
+                                              dev->mode_config.path_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_path_property);
+
+/**
+ * drm_mode_connector_set_tile_property - set tile property on connector
+ * @connector: connector to set property on.
+ *
+ * This looks up the tile information for a connector, and creates a
+ * property for userspace to parse if it exists. The property is of
+ * the form of 8 integers using ':' as a separator.
+ *
+ * Returns:
+ * Zero on success, errno on failure.
+ */
+int drm_mode_connector_set_tile_property(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       char tile[256];
+       int ret;
+
+       if (!connector->has_tile) {
+               ret  = drm_property_replace_global_blob(dev,
+                                                       &connector->tile_blob_ptr,
+                                                       0,
+                                                       NULL,
+                                                       &connector->base,
+                                                       dev->mode_config.tile_property);
+               return ret;
+       }
+
+       snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
+                connector->tile_group->id, connector->tile_is_single_monitor,
+                connector->num_h_tile, connector->num_v_tile,
+                connector->tile_h_loc, connector->tile_v_loc,
+                connector->tile_h_size, connector->tile_v_size);
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->tile_blob_ptr,
+                                              strlen(tile) + 1,
+                                              tile,
+                                              &connector->base,
+                                              dev->mode_config.tile_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
+
+/**
+ * drm_mode_connector_update_edid_property - update the edid property of a connector
+ * @connector: drm connector
+ * @edid: new value of the edid property
+ *
+ * This function creates a new blob modeset object and assigns its id to the
+ * connector's edid property.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+                                           const struct edid *edid)
+{
+       struct drm_device *dev = connector->dev;
+       size_t size = 0;
+       int ret;
+
+       /* ignore requests to set edid when overridden */
+       if (connector->override_edid)
+               return 0;
+
+       if (edid)
+               size = EDID_LENGTH * (1 + edid->extensions);
+
+       ret = drm_property_replace_global_blob(dev,
+                                              &connector->edid_blob_ptr,
+                                              size,
+                                              edid,
+                                              &connector->base,
+                                              dev->mode_config.edid_property);
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
+
+int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
+                                   struct drm_property *property,
+                                   uint64_t value)
+{
+       int ret = -EINVAL;
+       struct drm_connector *connector = obj_to_connector(obj);
+
+       /* Do DPMS ourselves */
+       if (property == connector->dev->mode_config.dpms_property) {
+               ret = (*connector->funcs->dpms)(connector, (int)value);
+       } else if (connector->funcs->set_property)
+               ret = connector->funcs->set_property(connector, property, value);
+
+       /* store the property value if successful */
+       if (!ret)
+               drm_object_property_set_value(&connector->base, property, value);
+       return ret;
+}
+
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+                                      void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_connector_set_property *conn_set_prop = data;
+       struct drm_mode_obj_set_property obj_set_prop = {
+               .value = conn_set_prop->value,
+               .prop_id = conn_set_prop->prop_id,
+               .obj_id = conn_set_prop->connector_id,
+               .obj_type = DRM_MODE_OBJECT_CONNECTOR
+       };
+
+       /* It does all the locking and checking we need */
+       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
+}
+
+static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
+{
+       /* For atomic drivers only state objects are synchronously updated and
+        * protected by modeset locks, so check those first. */
+       if (connector->state)
+               return connector->state->best_encoder;
+       return connector->encoder;
+}
+
+static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
+                                        const struct drm_file *file_priv)
+{
+       /*
+        * If user-space hasn't configured the driver to expose the stereo 3D
+        * modes, don't expose them.
+        */
+       if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
+               return false;
+
+       return true;
+}
+
+int drm_mode_getconnector(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
+{
+       struct drm_mode_get_connector *out_resp = data;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_display_mode *mode;
+       int mode_count = 0;
+       int encoders_count = 0;
+       int ret = 0;
+       int copied = 0;
+       int i;
+       struct drm_mode_modeinfo u_mode;
+       struct drm_mode_modeinfo __user *mode_ptr;
+       uint32_t __user *encoder_ptr;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       connector = drm_connector_lookup(dev, out_resp->connector_id);
+       if (!connector) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
+
+       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
+               if (connector->encoder_ids[i] != 0)
+                       encoders_count++;
+
+       if (out_resp->count_modes == 0) {
+               connector->funcs->fill_modes(connector,
+                                            dev->mode_config.max_width,
+                                            dev->mode_config.max_height);
+       }
+
+       /* delayed so we get modes regardless of pre-fill_modes state */
+       list_for_each_entry(mode, &connector->modes, head)
+               if (drm_mode_expose_to_userspace(mode, file_priv))
+                       mode_count++;
+
+       out_resp->connector_id = connector->base.id;
+       out_resp->connector_type = connector->connector_type;
+       out_resp->connector_type_id = connector->connector_type_id;
+       out_resp->mm_width = connector->display_info.width_mm;
+       out_resp->mm_height = connector->display_info.height_mm;
+       out_resp->subpixel = connector->display_info.subpixel_order;
+       out_resp->connection = connector->status;
+
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       encoder = drm_connector_get_encoder(connector);
+       if (encoder)
+               out_resp->encoder_id = encoder->base.id;
+       else
+               out_resp->encoder_id = 0;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if ((out_resp->count_modes >= mode_count) && mode_count) {
+               copied = 0;
+               mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
+               list_for_each_entry(mode, &connector->modes, head) {
+                       if (!drm_mode_expose_to_userspace(mode, file_priv))
+                               continue;
+
+                       drm_mode_convert_to_umode(&u_mode, mode);
+                       if (copy_to_user(mode_ptr + copied,
+                                        &u_mode, sizeof(u_mode))) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
+       }
+       out_resp->count_modes = mode_count;
+
+       ret = drm_mode_object_get_properties(&connector->base, file_priv->atomic,
+                       (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
+                       (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
+                       &out_resp->count_props);
+       if (ret)
+               goto out;
+
+       if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
+               copied = 0;
+               encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
+               for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+                       if (connector->encoder_ids[i] != 0) {
+                               if (put_user(connector->encoder_ids[i],
+                                            encoder_ptr + copied)) {
+                                       ret = -EFAULT;
+                                       goto out;
+                               }
+                               copied++;
+                       }
+               }
+       }
+       out_resp->count_encoders = encoders_count;
+
+out:
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+       drm_connector_unreference(connector);
+out_unlock:
+       mutex_unlock(&dev->mode_config.mutex);
+
+       return ret;
+}
+
index 192a5f9..3c4000f 100644 (file)
@@ -54,7 +54,7 @@ struct drm_ctx_list {
 void drm_legacy_ctxbitmap_free(struct drm_device * dev, int ctx_handle)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -92,7 +92,7 @@ static int drm_legacy_ctxbitmap_next(struct drm_device * dev)
 void drm_legacy_ctxbitmap_init(struct drm_device * dev)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        idr_init(&dev->ctx_idr);
@@ -109,7 +109,7 @@ void drm_legacy_ctxbitmap_init(struct drm_device * dev)
 void drm_legacy_ctxbitmap_cleanup(struct drm_device * dev)
 {
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->struct_mutex);
@@ -131,7 +131,7 @@ void drm_legacy_ctxbitmap_flush(struct drm_device *dev, struct drm_file *file)
        struct drm_ctx_list *pos, *tmp;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
 
        mutex_lock(&dev->ctxlist_mutex);
@@ -177,7 +177,7 @@ int drm_legacy_getsareactx(struct drm_device *dev, void *data,
        struct drm_map_list *_entry;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -225,7 +225,7 @@ int drm_legacy_setsareactx(struct drm_device *dev, void *data,
        struct drm_map_list *r_list = NULL;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        mutex_lock(&dev->struct_mutex);
@@ -329,7 +329,7 @@ int drm_legacy_resctx(struct drm_device *dev, void *data,
        int i;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (res->count >= DRM_RESERVED_CONTEXTS) {
@@ -363,7 +363,7 @@ int drm_legacy_addctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        ctx->handle = drm_legacy_ctxbitmap_next(dev);
@@ -410,7 +410,7 @@ int drm_legacy_getctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        /* This is 0, because we don't handle any context flags */
@@ -436,7 +436,7 @@ int drm_legacy_switchctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
@@ -460,7 +460,7 @@ int drm_legacy_newctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
@@ -486,7 +486,7 @@ int drm_legacy_rmctx(struct drm_device *dev, void *data,
        struct drm_ctx *ctx = data;
 
        if (!drm_core_check_feature(dev, DRIVER_KMS_LEGACY_CONTEXT) &&
-           drm_core_check_feature(dev, DRIVER_MODESET))
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        DRM_DEBUG("%d\n", ctx->handle);
index ddebe54..2d7bedf 100644 (file)
 #include <drm/drm_modeset_lock.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_auth.h>
+#include <drm/drm_framebuffer.h>
 
 #include "drm_crtc_internal.h"
 #include "drm_internal.h"
 
-static struct drm_framebuffer *
-internal_framebuffer_create(struct drm_device *dev,
-                           const struct drm_mode_fb_cmd2 *r,
-                           struct drm_file *file_priv);
-
-/* Avoid boilerplate.  I'm tired of typing. */
-#define DRM_ENUM_NAME_FN(fnname, list)                         \
-       const char *fnname(int val)                             \
-       {                                                       \
-               int i;                                          \
-               for (i = 0; i < ARRAY_SIZE(list); i++) {        \
-                       if (list[i].type == val)                \
-                               return list[i].name;            \
-               }                                               \
-               return "(unknown)";                             \
-       }
-
 /*
  * Global properties
  */
-static const struct drm_prop_enum_list drm_dpms_enum_list[] = {
-       { DRM_MODE_DPMS_ON, "On" },
-       { DRM_MODE_DPMS_STANDBY, "Standby" },
-       { DRM_MODE_DPMS_SUSPEND, "Suspend" },
-       { DRM_MODE_DPMS_OFF, "Off" }
-};
-
-DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list)
-
 static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
        { DRM_PLANE_TYPE_OVERLAY, "Overlay" },
        { DRM_PLANE_TYPE_PRIMARY, "Primary" },
@@ -82,320 +57,6 @@ static const struct drm_prop_enum_list drm_plane_type_enum_list[] = {
 /*
  * Optional properties
  */
-static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = {
-       { DRM_MODE_SCALE_NONE, "None" },
-       { DRM_MODE_SCALE_FULLSCREEN, "Full" },
-       { DRM_MODE_SCALE_CENTER, "Center" },
-       { DRM_MODE_SCALE_ASPECT, "Full aspect" },
-};
-
-static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = {
-       { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" },
-       { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" },
-       { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" },
-};
-
-/*
- * Non-global properties, but "required" for certain connectors.
- */
-static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
-       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
-};
-
-DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list)
-
-static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_DVID,      "DVI-D"     }, /* DVI-I  */
-       { DRM_MODE_SUBCONNECTOR_DVIA,      "DVI-A"     }, /* DVI-I  */
-};
-
-DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
-                drm_dvi_i_subconnector_enum_list)
-
-static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
-};
-
-DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list)
-
-static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = {
-       { DRM_MODE_SUBCONNECTOR_Unknown,   "Unknown"   }, /* DVI-I and TV-out */
-       { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SVIDEO,    "SVIDEO"    }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */
-       { DRM_MODE_SUBCONNECTOR_SCART,     "SCART"     }, /* TV-out */
-};
-
-DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
-                drm_tv_subconnector_enum_list)
-
-static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
-       { DRM_MODE_DIRTY_OFF,      "Off"      },
-       { DRM_MODE_DIRTY_ON,       "On"       },
-       { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
-};
-
-struct drm_conn_prop_enum_list {
-       int type;
-       const char *name;
-       struct ida ida;
-};
-
-/*
- * Connector and encoder types.
- */
-static struct drm_conn_prop_enum_list drm_connector_enum_list[] = {
-       { DRM_MODE_CONNECTOR_Unknown, "Unknown" },
-       { DRM_MODE_CONNECTOR_VGA, "VGA" },
-       { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
-       { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
-       { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
-       { DRM_MODE_CONNECTOR_Composite, "Composite" },
-       { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" },
-       { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
-       { DRM_MODE_CONNECTOR_Component, "Component" },
-       { DRM_MODE_CONNECTOR_9PinDIN, "DIN" },
-       { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
-       { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
-       { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
-       { DRM_MODE_CONNECTOR_TV, "TV" },
-       { DRM_MODE_CONNECTOR_eDP, "eDP" },
-       { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
-       { DRM_MODE_CONNECTOR_DSI, "DSI" },
-       { DRM_MODE_CONNECTOR_DPI, "DPI" },
-};
-
-static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
-       { DRM_MODE_ENCODER_NONE, "None" },
-       { DRM_MODE_ENCODER_DAC, "DAC" },
-       { DRM_MODE_ENCODER_TMDS, "TMDS" },
-       { DRM_MODE_ENCODER_LVDS, "LVDS" },
-       { DRM_MODE_ENCODER_TVDAC, "TV" },
-       { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
-       { DRM_MODE_ENCODER_DSI, "DSI" },
-       { DRM_MODE_ENCODER_DPMST, "DP MST" },
-       { DRM_MODE_ENCODER_DPI, "DPI" },
-};
-
-static const struct drm_prop_enum_list drm_subpixel_enum_list[] = {
-       { SubPixelUnknown, "Unknown" },
-       { SubPixelHorizontalRGB, "Horizontal RGB" },
-       { SubPixelHorizontalBGR, "Horizontal BGR" },
-       { SubPixelVerticalRGB, "Vertical RGB" },
-       { SubPixelVerticalBGR, "Vertical BGR" },
-       { SubPixelNone, "None" },
-};
-
-void drm_connector_ida_init(void)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
-               ida_init(&drm_connector_enum_list[i].ida);
-}
-
-void drm_connector_ida_destroy(void)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
-               ida_destroy(&drm_connector_enum_list[i].ida);
-}
-
-/**
- * drm_get_connector_status_name - return a string for connector status
- * @status: connector status to compute name of
- *
- * In contrast to the other drm_get_*_name functions this one here returns a
- * const pointer and hence is threadsafe.
- */
-const char *drm_get_connector_status_name(enum drm_connector_status status)
-{
-       if (status == connector_status_connected)
-               return "connected";
-       else if (status == connector_status_disconnected)
-               return "disconnected";
-       else
-               return "unknown";
-}
-EXPORT_SYMBOL(drm_get_connector_status_name);
-
-/**
- * drm_get_subpixel_order_name - return a string for a given subpixel enum
- * @order: enum of subpixel_order
- *
- * Note you could abuse this and return something out of bounds, but that
- * would be a caller error.  No unscrubbed user data should make it here.
- */
-const char *drm_get_subpixel_order_name(enum subpixel_order order)
-{
-       return drm_subpixel_enum_list[order].name;
-}
-EXPORT_SYMBOL(drm_get_subpixel_order_name);
-
-/*
- * Internal function to assign a slot in the object idr and optionally
- * register the object into the idr.
- */
-static int drm_mode_object_get_reg(struct drm_device *dev,
-                                  struct drm_mode_object *obj,
-                                  uint32_t obj_type,
-                                  bool register_obj,
-                                  void (*obj_free_cb)(struct kref *kref))
-{
-       int ret;
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
-       if (ret >= 0) {
-               /*
-                * Set up the object linking under the protection of the idr
-                * lock so that other users can't see inconsistent state.
-                */
-               obj->id = ret;
-               obj->type = obj_type;
-               if (obj_free_cb) {
-                       obj->free_cb = obj_free_cb;
-                       kref_init(&obj->refcount);
-               }
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
-       return ret < 0 ? ret : 0;
-}
-
-/**
- * drm_mode_object_get - allocate a new modeset identifier
- * @dev: DRM device
- * @obj: object pointer, used to generate unique ID
- * @obj_type: object type
- *
- * Create a unique identifier based on @ptr in @dev's identifier space.  Used
- * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
- * modeset identifiers are _not_ reference counted. Hence don't use this for
- * reference counted modeset objects like framebuffers.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_mode_object_get(struct drm_device *dev,
-                       struct drm_mode_object *obj, uint32_t obj_type)
-{
-       return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
-}
-
-static void drm_mode_object_register(struct drm_device *dev,
-                                    struct drm_mode_object *obj)
-{
-       mutex_lock(&dev->mode_config.idr_mutex);
-       idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);
-       mutex_unlock(&dev->mode_config.idr_mutex);
-}
-
-/**
- * drm_mode_object_unregister - free a modeset identifer
- * @dev: DRM device
- * @object: object to free
- *
- * Free @id from @dev's unique identifier pool.
- * This function can be called multiple times, and guards against
- * multiple removals.
- * These modeset identifiers are _not_ reference counted. Hence don't use this
- * for reference counted modeset objects like framebuffers.
- */
-void drm_mode_object_unregister(struct drm_device *dev,
-                        struct drm_mode_object *object)
-{
-       mutex_lock(&dev->mode_config.idr_mutex);
-       if (object->id) {
-               idr_remove(&dev->mode_config.crtc_idr, object->id);
-               object->id = 0;
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-}
-
-static struct drm_mode_object *_object_find(struct drm_device *dev,
-               uint32_t id, uint32_t type)
-{
-       struct drm_mode_object *obj = NULL;
-
-       mutex_lock(&dev->mode_config.idr_mutex);
-       obj = idr_find(&dev->mode_config.crtc_idr, id);
-       if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
-               obj = NULL;
-       if (obj && obj->id != id)
-               obj = NULL;
-
-       if (obj && obj->free_cb) {
-               if (!kref_get_unless_zero(&obj->refcount))
-                       obj = NULL;
-       }
-       mutex_unlock(&dev->mode_config.idr_mutex);
-
-       return obj;
-}
-
-/**
- * drm_mode_object_find - look up a drm object with static lifetime
- * @dev: drm device
- * @id: id of the mode object
- * @type: type of the mode object
- *
- * This function is used to look up a modeset object. It will acquire a
- * reference for reference counted objects. This reference must be dropped again
- * by callind drm_mode_object_unreference().
- */
-struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
-               uint32_t id, uint32_t type)
-{
-       struct drm_mode_object *obj = NULL;
-
-       obj = _object_find(dev, id, type);
-       return obj;
-}
-EXPORT_SYMBOL(drm_mode_object_find);
-
-/**
- * drm_mode_object_unreference - decr the object refcnt
- * @obj: mode_object
- *
- * This functions decrements the object's refcount if it is a refcounted modeset
- * object. It is a no-op on any other object. This is used to drop references
- * acquired with drm_mode_object_reference().
- */
-void drm_mode_object_unreference(struct drm_mode_object *obj)
-{
-       if (obj->free_cb) {
-               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
-               kref_put(&obj->refcount, obj->free_cb);
-       }
-}
-EXPORT_SYMBOL(drm_mode_object_unreference);
-
-/**
- * drm_mode_object_reference - incr the object refcnt
- * @obj: mode_object
- *
- * This functions increments the object's refcount if it is a refcounted modeset
- * object. It is a no-op on any other object. References should be dropped again
- * by calling drm_mode_object_unreference().
- */
-void drm_mode_object_reference(struct drm_mode_object *obj)
-{
-       if (obj->free_cb) {
-               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
-               kref_get(&obj->refcount);
-       }
-}
-EXPORT_SYMBOL(drm_mode_object_reference);
-
 /**
  * drm_crtc_force_disable - Forcibly turn off a CRTC
  * @crtc: CRTC to turn off
@@ -441,199 +102,6 @@ out:
 }
 EXPORT_SYMBOL(drm_crtc_force_disable_all);
 
-static void drm_framebuffer_free(struct kref *kref)
-{
-       struct drm_framebuffer *fb =
-                       container_of(kref, struct drm_framebuffer, base.refcount);
-       struct drm_device *dev = fb->dev;
-
-       /*
-        * The lookup idr holds a weak reference, which has not necessarily been
-        * removed at this point. Check for that.
-        */
-       drm_mode_object_unregister(dev, &fb->base);
-
-       fb->funcs->destroy(fb);
-}
-
-/**
- * drm_framebuffer_init - initialize a framebuffer
- * @dev: DRM device
- * @fb: framebuffer to be initialized
- * @funcs: ... with these functions
- *
- * Allocates an ID for the framebuffer's parent mode object, sets its mode
- * functions & device file and adds it to the master fd list.
- *
- * IMPORTANT:
- * This functions publishes the fb and makes it available for concurrent access
- * by other users. Which means by this point the fb _must_ be fully set up -
- * since all the fb attributes are invariant over its lifetime, no further
- * locking but only correct reference counting is required.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
-                        const struct drm_framebuffer_funcs *funcs)
-{
-       int ret;
-
-       INIT_LIST_HEAD(&fb->filp_head);
-       fb->dev = dev;
-       fb->funcs = funcs;
-
-       ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB,
-                                     false, drm_framebuffer_free);
-       if (ret)
-               goto out;
-
-       mutex_lock(&dev->mode_config.fb_lock);
-       dev->mode_config.num_fb++;
-       list_add(&fb->head, &dev->mode_config.fb_list);
-       mutex_unlock(&dev->mode_config.fb_lock);
-
-       drm_mode_object_register(dev, &fb->base);
-out:
-       return ret;
-}
-EXPORT_SYMBOL(drm_framebuffer_init);
-
-/**
- * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
- * @dev: drm device
- * @id: id of the fb object
- *
- * If successful, this grabs an additional reference to the framebuffer -
- * callers need to make sure to eventually unreference the returned framebuffer
- * again, using @drm_framebuffer_unreference.
- */
-struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
-                                              uint32_t id)
-{
-       struct drm_mode_object *obj;
-       struct drm_framebuffer *fb = NULL;
-
-       obj = _object_find(dev, id, DRM_MODE_OBJECT_FB);
-       if (obj)
-               fb = obj_to_fb(obj);
-       return fb;
-}
-EXPORT_SYMBOL(drm_framebuffer_lookup);
-
-/**
- * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
- * @fb: fb to unregister
- *
- * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
- * those used for fbdev. Note that the caller must hold a reference of it's own,
- * i.e. the object may not be destroyed through this call (since it'll lead to a
- * locking inversion).
- */
-void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
-{
-       struct drm_device *dev;
-
-       if (!fb)
-               return;
-
-       dev = fb->dev;
-
-       /* Mark fb as reaped and drop idr ref. */
-       drm_mode_object_unregister(dev, &fb->base);
-}
-EXPORT_SYMBOL(drm_framebuffer_unregister_private);
-
-/**
- * drm_framebuffer_cleanup - remove a framebuffer object
- * @fb: framebuffer to remove
- *
- * Cleanup framebuffer. This function is intended to be used from the drivers
- * ->destroy callback. It can also be used to clean up driver private
- * framebuffers embedded into a larger structure.
- *
- * Note that this function does not remove the fb from active usuage - if it is
- * still used anywhere, hilarity can ensue since userspace could call getfb on
- * the id and get back -EINVAL. Obviously no concern at driver unload time.
- *
- * Also, the framebuffer will not be removed from the lookup idr - for
- * user-created framebuffers this will happen in in the rmfb ioctl. For
- * driver-private objects (e.g. for fbdev) drivers need to explicitly call
- * drm_framebuffer_unregister_private.
- */
-void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
-{
-       struct drm_device *dev = fb->dev;
-
-       mutex_lock(&dev->mode_config.fb_lock);
-       list_del(&fb->head);
-       dev->mode_config.num_fb--;
-       mutex_unlock(&dev->mode_config.fb_lock);
-}
-EXPORT_SYMBOL(drm_framebuffer_cleanup);
-
-/**
- * drm_framebuffer_remove - remove and unreference a framebuffer object
- * @fb: framebuffer to remove
- *
- * Scans all the CRTCs and planes in @dev's mode_config.  If they're
- * using @fb, removes it, setting it to NULL. Then drops the reference to the
- * passed-in framebuffer. Might take the modeset locks.
- *
- * Note that this function optimizes the cleanup away if the caller holds the
- * last reference to the framebuffer. It is also guaranteed to not take the
- * modeset locks in this case.
- */
-void drm_framebuffer_remove(struct drm_framebuffer *fb)
-{
-       struct drm_device *dev;
-       struct drm_crtc *crtc;
-       struct drm_plane *plane;
-
-       if (!fb)
-               return;
-
-       dev = fb->dev;
-
-       WARN_ON(!list_empty(&fb->filp_head));
-
-       /*
-        * drm ABI mandates that we remove any deleted framebuffers from active
-        * useage. But since most sane clients only remove framebuffers they no
-        * longer need, try to optimize this away.
-        *
-        * Since we're holding a reference ourselves, observing a refcount of 1
-        * means that we're the last holder and can skip it. Also, the refcount
-        * can never increase from 1 again, so we don't need any barriers or
-        * locks.
-        *
-        * Note that userspace could try to race with use and instate a new
-        * usage _after_ we've cleared all current ones. End result will be an
-        * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
-        * in this manner.
-        */
-       if (drm_framebuffer_read_refcount(fb) > 1) {
-               drm_modeset_lock_all(dev);
-               /* remove from any CRTC */
-               drm_for_each_crtc(crtc, dev) {
-                       if (crtc->primary->fb == fb) {
-                               /* should turn off the crtc */
-                               if (drm_crtc_force_disable(crtc))
-                                       DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
-                       }
-               }
-
-               drm_for_each_plane(plane, dev) {
-                       if (plane->fb == fb)
-                               drm_plane_force_disable(plane);
-               }
-               drm_modeset_unlock_all(dev);
-       }
-
-       drm_framebuffer_unreference(fb);
-}
-EXPORT_SYMBOL(drm_framebuffer_remove);
-
 DEFINE_WW_CLASS(crtc_ww_class);
 
 static unsigned int drm_num_crtcs(struct drm_device *dev)
@@ -683,7 +151,11 @@ static void drm_crtc_unregister_all(struct drm_device *dev)
  * @funcs: callbacks for the new CRTC
  * @name: printf style format string for the CRTC name, or NULL for default name
  *
- * Inits a new object created as base part of a driver crtc object.
+ * Inits a new object created as base part of a driver crtc object. Drivers
+ * should use this function instead of drm_crtc_init(), which is only provided
+ * for backwards compatibility with drivers which do not yet support universal
+ * planes). For really simple hardware which has only 1 plane look at
+ * drm_simple_display_pipe_init() instead.
  *
  * Returns:
  * Zero on success, error code on failure.
@@ -783,4713 +255,650 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
 }
 EXPORT_SYMBOL(drm_crtc_cleanup);
 
-/*
- * drm_mode_remove - remove and free a mode
- * @connector: connector list to modify
- * @mode: mode to remove
- *
- * Remove @mode from @connector's mode list, then free it.
- */
-static void drm_mode_remove(struct drm_connector *connector,
-                           struct drm_display_mode *mode)
+int drm_modeset_register_all(struct drm_device *dev)
 {
-       list_del(&mode->head);
-       drm_mode_destroy(connector->dev, mode);
-}
+       int ret;
 
-/**
- * drm_display_info_set_bus_formats - set the supported bus formats
- * @info: display info to store bus formats in
- * @formats: array containing the supported bus formats
- * @num_formats: the number of entries in the fmts array
- *
- * Store the supported bus formats in display info structure.
- * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for
- * a full list of available formats.
- */
-int drm_display_info_set_bus_formats(struct drm_display_info *info,
-                                    const u32 *formats,
-                                    unsigned int num_formats)
-{
-       u32 *fmts = NULL;
+       ret = drm_plane_register_all(dev);
+       if (ret)
+               goto err_plane;
 
-       if (!formats && num_formats)
-               return -EINVAL;
+       ret = drm_crtc_register_all(dev);
+       if  (ret)
+               goto err_crtc;
 
-       if (formats && num_formats) {
-               fmts = kmemdup(formats, sizeof(*formats) * num_formats,
-                              GFP_KERNEL);
-               if (!fmts)
-                       return -ENOMEM;
-       }
+       ret = drm_encoder_register_all(dev);
+       if (ret)
+               goto err_encoder;
 
-       kfree(info->bus_formats);
-       info->bus_formats = fmts;
-       info->num_bus_formats = num_formats;
+       ret = drm_connector_register_all(dev);
+       if (ret)
+               goto err_connector;
 
        return 0;
+
+err_connector:
+       drm_encoder_unregister_all(dev);
+err_encoder:
+       drm_crtc_unregister_all(dev);
+err_crtc:
+       drm_plane_unregister_all(dev);
+err_plane:
+       return ret;
 }
-EXPORT_SYMBOL(drm_display_info_set_bus_formats);
 
-/**
- * drm_connector_get_cmdline_mode - reads the user's cmdline mode
- * @connector: connector to quwery
- *
- * The kernel supports per-connector configration of its consoles through
- * use of the video= parameter. This function parses that option and
- * extracts the user's specified mode (or enable/disable status) for a
- * particular connector. This is typically only used during the early fbdev
- * setup.
- */
-static void drm_connector_get_cmdline_mode(struct drm_connector *connector)
+void drm_modeset_unregister_all(struct drm_device *dev)
 {
-       struct drm_cmdline_mode *mode = &connector->cmdline_mode;
-       char *option = NULL;
-
-       if (fb_get_options(connector->name, &option))
-               return;
-
-       if (!drm_mode_parse_command_line_for_connector(option,
-                                                      connector,
-                                                      mode))
-               return;
-
-       if (mode->force) {
-               const char *s;
-
-               switch (mode->force) {
-               case DRM_FORCE_OFF:
-                       s = "OFF";
-                       break;
-               case DRM_FORCE_ON_DIGITAL:
-                       s = "ON - dig";
-                       break;
-               default:
-               case DRM_FORCE_ON:
-                       s = "ON";
-                       break;
-               }
-
-               DRM_INFO("forcing %s connector %s\n", connector->name, s);
-               connector->force = mode->force;
-       }
-
-       DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
-                     connector->name,
-                     mode->xres, mode->yres,
-                     mode->refresh_specified ? mode->refresh : 60,
-                     mode->rb ? " reduced blanking" : "",
-                     mode->margins ? " with margins" : "",
-                     mode->interlace ?  " interlaced" : "");
+       drm_connector_unregister_all(dev);
+       drm_encoder_unregister_all(dev);
+       drm_crtc_unregister_all(dev);
+       drm_plane_unregister_all(dev);
 }
 
-static void drm_connector_free(struct kref *kref)
+static int drm_mode_create_standard_properties(struct drm_device *dev)
 {
-       struct drm_connector *connector =
-               container_of(kref, struct drm_connector, base.refcount);
-       struct drm_device *dev = connector->dev;
-
-       drm_mode_object_unregister(dev, &connector->base);
-       connector->funcs->destroy(connector);
-}
+       struct drm_property *prop;
+       int ret;
 
-/**
- * drm_connector_init - Init a preallocated connector
- * @dev: DRM device
- * @connector: the connector to init
- * @funcs: callbacks for this connector
- * @connector_type: user visible type of the connector
- *
- * Initialises a preallocated connector. Connectors should be
- * subclassed as part of driver connector objects.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_init(struct drm_device *dev,
-                      struct drm_connector *connector,
-                      const struct drm_connector_funcs *funcs,
-                      int connector_type)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       int ret;
-       struct ida *connector_ida =
-               &drm_connector_enum_list[connector_type].ida;
-
-       drm_modeset_lock_all(dev);
-
-       ret = drm_mode_object_get_reg(dev, &connector->base,
-                                     DRM_MODE_OBJECT_CONNECTOR,
-                                     false, drm_connector_free);
-       if (ret)
-               goto out_unlock;
-
-       connector->base.properties = &connector->properties;
-       connector->dev = dev;
-       connector->funcs = funcs;
-
-       ret = ida_simple_get(&config->connector_ida, 0, 0, GFP_KERNEL);
-       if (ret < 0)
-               goto out_put;
-       connector->index = ret;
-       ret = 0;
-
-       connector->connector_type = connector_type;
-       connector->connector_type_id =
-               ida_simple_get(connector_ida, 1, 0, GFP_KERNEL);
-       if (connector->connector_type_id < 0) {
-               ret = connector->connector_type_id;
-               goto out_put_id;
-       }
-       connector->name =
-               kasprintf(GFP_KERNEL, "%s-%d",
-                         drm_connector_enum_list[connector_type].name,
-                         connector->connector_type_id);
-       if (!connector->name) {
-               ret = -ENOMEM;
-               goto out_put_type_id;
-       }
-
-       INIT_LIST_HEAD(&connector->probed_modes);
-       INIT_LIST_HEAD(&connector->modes);
-       connector->edid_blob_ptr = NULL;
-       connector->status = connector_status_unknown;
-
-       drm_connector_get_cmdline_mode(connector);
-
-       /* We should add connectors at the end to avoid upsetting the connector
-        * index too much. */
-       list_add_tail(&connector->head, &config->connector_list);
-       config->num_connector++;
-
-       if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
-               drm_object_attach_property(&connector->base,
-                                             config->edid_property,
-                                             0);
-
-       drm_object_attach_property(&connector->base,
-                                     config->dpms_property, 0);
-
-       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
-               drm_object_attach_property(&connector->base, config->prop_crtc_id, 0);
-       }
-
-       connector->debugfs_entry = NULL;
-out_put_type_id:
-       if (ret)
-               ida_remove(connector_ida, connector->connector_type_id);
-out_put_id:
-       if (ret)
-               ida_remove(&config->connector_ida, connector->index);
-out_put:
-       if (ret)
-               drm_mode_object_unregister(dev, &connector->base);
-
-out_unlock:
-       drm_modeset_unlock_all(dev);
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_connector_init);
-
-/**
- * drm_connector_cleanup - cleans up an initialised connector
- * @connector: connector to cleanup
- *
- * Cleans up the connector but doesn't free the object.
- */
-void drm_connector_cleanup(struct drm_connector *connector)
-{
-       struct drm_device *dev = connector->dev;
-       struct drm_display_mode *mode, *t;
-
-       /* The connector should have been removed from userspace long before
-        * it is finally destroyed.
-        */
-       if (WARN_ON(connector->registered))
-               drm_connector_unregister(connector);
-
-       if (connector->tile_group) {
-               drm_mode_put_tile_group(dev, connector->tile_group);
-               connector->tile_group = NULL;
-       }
-
-       list_for_each_entry_safe(mode, t, &connector->probed_modes, head)
-               drm_mode_remove(connector, mode);
-
-       list_for_each_entry_safe(mode, t, &connector->modes, head)
-               drm_mode_remove(connector, mode);
-
-       ida_remove(&drm_connector_enum_list[connector->connector_type].ida,
-                  connector->connector_type_id);
-
-       ida_remove(&dev->mode_config.connector_ida,
-                  connector->index);
-
-       kfree(connector->display_info.bus_formats);
-       drm_mode_object_unregister(dev, &connector->base);
-       kfree(connector->name);
-       connector->name = NULL;
-       list_del(&connector->head);
-       dev->mode_config.num_connector--;
-
-       WARN_ON(connector->state && !connector->funcs->atomic_destroy_state);
-       if (connector->state && connector->funcs->atomic_destroy_state)
-               connector->funcs->atomic_destroy_state(connector,
-                                                      connector->state);
-
-       memset(connector, 0, sizeof(*connector));
-}
-EXPORT_SYMBOL(drm_connector_cleanup);
-
-/**
- * drm_connector_register - register a connector
- * @connector: the connector to register
- *
- * Register userspace interfaces for a connector
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_register(struct drm_connector *connector)
-{
-       int ret;
-
-       if (connector->registered)
-               return 0;
-
-       ret = drm_sysfs_connector_add(connector);
+       ret = drm_connector_create_standard_properties(dev);
        if (ret)
                return ret;
 
-       ret = drm_debugfs_connector_add(connector);
-       if (ret) {
-               goto err_sysfs;
-       }
-
-       if (connector->funcs->late_register) {
-               ret = connector->funcs->late_register(connector);
-               if (ret)
-                       goto err_debugfs;
-       }
-
-       drm_mode_object_register(connector->dev, &connector->base);
-
-       connector->registered = true;
-       return 0;
-
-err_debugfs:
-       drm_debugfs_connector_remove(connector);
-err_sysfs:
-       drm_sysfs_connector_remove(connector);
-       return ret;
-}
-EXPORT_SYMBOL(drm_connector_register);
-
-/**
- * drm_connector_unregister - unregister a connector
- * @connector: the connector to unregister
- *
- * Unregister userspace interfaces for a connector
- */
-void drm_connector_unregister(struct drm_connector *connector)
-{
-       if (!connector->registered)
-               return;
-
-       if (connector->funcs->early_unregister)
-               connector->funcs->early_unregister(connector);
-
-       drm_sysfs_connector_remove(connector);
-       drm_debugfs_connector_remove(connector);
-
-       connector->registered = false;
-}
-EXPORT_SYMBOL(drm_connector_unregister);
-
-static void drm_connector_unregister_all(struct drm_device *dev)
-{
-       struct drm_connector *connector;
-
-       /* FIXME: taking the mode config mutex ends up in a clash with sysfs */
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head)
-               drm_connector_unregister(connector);
-}
-
-static int drm_connector_register_all(struct drm_device *dev)
-{
-       struct drm_connector *connector;
-       int ret;
-
-       /* FIXME: taking the mode config mutex ends up in a clash with
-        * fbcon/backlight registration */
-       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               ret = drm_connector_register(connector);
-               if (ret)
-                       goto err;
-       }
-
-       return 0;
-
-err:
-       mutex_unlock(&dev->mode_config.mutex);
-       drm_connector_unregister_all(dev);
-       return ret;
-}
-
-static int drm_encoder_register_all(struct drm_device *dev)
-{
-       struct drm_encoder *encoder;
-       int ret = 0;
-
-       drm_for_each_encoder(encoder, dev) {
-               if (encoder->funcs->late_register)
-                       ret = encoder->funcs->late_register(encoder);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
+       prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+                                       "type", drm_plane_type_enum_list,
+                                       ARRAY_SIZE(drm_plane_type_enum_list));
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.plane_type_property = prop;
 
-static void drm_encoder_unregister_all(struct drm_device *dev)
-{
-       struct drm_encoder *encoder;
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_X", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_x = prop;
 
-       drm_for_each_encoder(encoder, dev) {
-               if (encoder->funcs->early_unregister)
-                       encoder->funcs->early_unregister(encoder);
-       }
-}
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_Y", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_y = prop;
 
-/**
- * drm_encoder_init - Init a preallocated encoder
- * @dev: drm device
- * @encoder: the encoder to init
- * @funcs: callbacks for this encoder
- * @encoder_type: user visible type of the encoder
- * @name: printf style format string for the encoder name, or NULL for default name
- *
- * Initialises a preallocated encoder. Encoder should be
- * subclassed as part of driver encoder objects.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_encoder_init(struct drm_device *dev,
-                     struct drm_encoder *encoder,
-                     const struct drm_encoder_funcs *funcs,
-                     int encoder_type, const char *name, ...)
-{
-       int ret;
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_W", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_w = prop;
 
-       drm_modeset_lock_all(dev);
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "SRC_H", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_src_h = prop;
 
-       ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
-       if (ret)
-               goto out_unlock;
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_X", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_x = prop;
 
-       encoder->dev = dev;
-       encoder->encoder_type = encoder_type;
-       encoder->funcs = funcs;
-       if (name) {
-               va_list ap;
+       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_Y", INT_MIN, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_y = prop;
 
-               va_start(ap, name);
-               encoder->name = kvasprintf(GFP_KERNEL, name, ap);
-               va_end(ap);
-       } else {
-               encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
-                                         drm_encoder_enum_list[encoder_type].name,
-                                         encoder->base.id);
-       }
-       if (!encoder->name) {
-               ret = -ENOMEM;
-               goto out_put;
-       }
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_W", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_w = prop;
 
-       list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
-       encoder->index = dev->mode_config.num_encoder++;
+       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_H", 0, INT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_h = prop;
 
-out_put:
-       if (ret)
-               drm_mode_object_unregister(dev, &encoder->base);
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "FB_ID", DRM_MODE_OBJECT_FB);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_fb_id = prop;
 
-out_unlock:
-       drm_modeset_unlock_all(dev);
+       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
+                       "CRTC_ID", DRM_MODE_OBJECT_CRTC);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_crtc_id = prop;
 
-       return ret;
-}
-EXPORT_SYMBOL(drm_encoder_init);
+       prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
+                       "ACTIVE");
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_active = prop;
 
-/**
- * drm_encoder_cleanup - cleans up an initialised encoder
- * @encoder: encoder to cleanup
- *
- * Cleans up the encoder but doesn't free the object.
- */
-void drm_encoder_cleanup(struct drm_encoder *encoder)
-{
-       struct drm_device *dev = encoder->dev;
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
+                       "MODE_ID", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.prop_mode_id = prop;
 
-       /* Note that the encoder_list is considered to be static; should we
-        * remove the drm_encoder at runtime we would have to decrement all
-        * the indices on the drm_encoder after us in the encoder_list.
-        */
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "DEGAMMA_LUT", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.degamma_lut_property = prop;
 
-       drm_modeset_lock_all(dev);
-       drm_mode_object_unregister(dev, &encoder->base);
-       kfree(encoder->name);
-       list_del(&encoder->head);
-       dev->mode_config.num_encoder--;
-       drm_modeset_unlock_all(dev);
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_IMMUTABLE,
+                       "DEGAMMA_LUT_SIZE", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.degamma_lut_size_property = prop;
 
-       memset(encoder, 0, sizeof(*encoder));
-}
-EXPORT_SYMBOL(drm_encoder_cleanup);
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "CTM", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.ctm_property = prop;
 
-static unsigned int drm_num_planes(struct drm_device *dev)
-{
-       unsigned int num = 0;
-       struct drm_plane *tmp;
+       prop = drm_property_create(dev,
+                       DRM_MODE_PROP_BLOB,
+                       "GAMMA_LUT", 0);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.gamma_lut_property = prop;
 
-       drm_for_each_plane(tmp, dev) {
-               num++;
-       }
+       prop = drm_property_create_range(dev,
+                       DRM_MODE_PROP_IMMUTABLE,
+                       "GAMMA_LUT_SIZE", 0, UINT_MAX);
+       if (!prop)
+               return -ENOMEM;
+       dev->mode_config.gamma_lut_size_property = prop;
 
-       return num;
+       return 0;
 }
 
 /**
- * drm_universal_plane_init - Initialize a new universal plane object
- * @dev: DRM device
- * @plane: plane object to init
- * @possible_crtcs: bitmask of possible CRTCs
- * @funcs: callbacks for the new plane
- * @formats: array of supported formats (%DRM_FORMAT_*)
- * @format_count: number of elements in @formats
- * @type: type of plane (overlay, primary, cursor)
- * @name: printf style format string for the plane name, or NULL for default name
- *
- * Initializes a plane object of type @type.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
-                            unsigned long possible_crtcs,
-                            const struct drm_plane_funcs *funcs,
-                            const uint32_t *formats, unsigned int format_count,
-                            enum drm_plane_type type,
-                            const char *name, ...)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       int ret;
-
-       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
-       if (ret)
-               return ret;
-
-       drm_modeset_lock_init(&plane->mutex);
-
-       plane->base.properties = &plane->properties;
-       plane->dev = dev;
-       plane->funcs = funcs;
-       plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),
-                                           GFP_KERNEL);
-       if (!plane->format_types) {
-               DRM_DEBUG_KMS("out of memory when allocating plane\n");
-               drm_mode_object_unregister(dev, &plane->base);
-               return -ENOMEM;
-       }
-
-       if (name) {
-               va_list ap;
-
-               va_start(ap, name);
-               plane->name = kvasprintf(GFP_KERNEL, name, ap);
-               va_end(ap);
-       } else {
-               plane->name = kasprintf(GFP_KERNEL, "plane-%d",
-                                       drm_num_planes(dev));
-       }
-       if (!plane->name) {
-               kfree(plane->format_types);
-               drm_mode_object_unregister(dev, &plane->base);
-               return -ENOMEM;
-       }
-
-       memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
-       plane->format_count = format_count;
-       plane->possible_crtcs = possible_crtcs;
-       plane->type = type;
-
-       list_add_tail(&plane->head, &config->plane_list);
-       plane->index = config->num_total_plane++;
-       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-               config->num_overlay_plane++;
-
-       drm_object_attach_property(&plane->base,
-                                  config->plane_type_property,
-                                  plane->type);
-
-       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
-               drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
-               drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_x, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_y, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_w, 0);
-               drm_object_attach_property(&plane->base, config->prop_src_h, 0);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_universal_plane_init);
-
-static int drm_plane_register_all(struct drm_device *dev)
-{
-       struct drm_plane *plane;
-       int ret = 0;
-
-       drm_for_each_plane(plane, dev) {
-               if (plane->funcs->late_register)
-                       ret = plane->funcs->late_register(plane);
-               if (ret)
-                       return ret;
-       }
-
-       return 0;
-}
-
-static void drm_plane_unregister_all(struct drm_device *dev)
-{
-       struct drm_plane *plane;
-
-       drm_for_each_plane(plane, dev) {
-               if (plane->funcs->early_unregister)
-                       plane->funcs->early_unregister(plane);
-       }
-}
-
-/**
- * drm_plane_init - Initialize a legacy plane
- * @dev: DRM device
- * @plane: plane object to init
- * @possible_crtcs: bitmask of possible CRTCs
- * @funcs: callbacks for the new plane
- * @formats: array of supported formats (%DRM_FORMAT_*)
- * @format_count: number of elements in @formats
- * @is_primary: plane type (primary vs overlay)
- *
- * Legacy API to initialize a DRM plane.
- *
- * New drivers should call drm_universal_plane_init() instead.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
-                  unsigned long possible_crtcs,
-                  const struct drm_plane_funcs *funcs,
-                  const uint32_t *formats, unsigned int format_count,
-                  bool is_primary)
-{
-       enum drm_plane_type type;
-
-       type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
-       return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
-                                       formats, format_count, type, NULL);
-}
-EXPORT_SYMBOL(drm_plane_init);
-
-/**
- * drm_plane_cleanup - Clean up the core plane usage
- * @plane: plane to cleanup
- *
- * This function cleans up @plane and removes it from the DRM mode setting
- * core. Note that the function does *not* free the plane structure itself,
- * this is the responsibility of the caller.
- */
-void drm_plane_cleanup(struct drm_plane *plane)
-{
-       struct drm_device *dev = plane->dev;
-
-       drm_modeset_lock_all(dev);
-       kfree(plane->format_types);
-       drm_mode_object_unregister(dev, &plane->base);
-
-       BUG_ON(list_empty(&plane->head));
-
-       /* Note that the plane_list is considered to be static; should we
-        * remove the drm_plane at runtime we would have to decrement all
-        * the indices on the drm_plane after us in the plane_list.
-        */
-
-       list_del(&plane->head);
-       dev->mode_config.num_total_plane--;
-       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-               dev->mode_config.num_overlay_plane--;
-       drm_modeset_unlock_all(dev);
-
-       WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
-       if (plane->state && plane->funcs->atomic_destroy_state)
-               plane->funcs->atomic_destroy_state(plane, plane->state);
-
-       kfree(plane->name);
-
-       memset(plane, 0, sizeof(*plane));
-}
-EXPORT_SYMBOL(drm_plane_cleanup);
-
-/**
- * drm_plane_from_index - find the registered plane at an index
- * @dev: DRM device
- * @idx: index of registered plane to find for
- *
- * Given a plane index, return the registered plane from DRM device's
- * list of planes with matching index.
- */
-struct drm_plane *
-drm_plane_from_index(struct drm_device *dev, int idx)
-{
-       struct drm_plane *plane;
-
-       drm_for_each_plane(plane, dev)
-               if (idx == plane->index)
-                       return plane;
-
-       return NULL;
-}
-EXPORT_SYMBOL(drm_plane_from_index);
-
-/**
- * drm_plane_force_disable - Forcibly disable a plane
- * @plane: plane to disable
- *
- * Forces the plane to be disabled.
- *
- * Used when the plane's current framebuffer is destroyed,
- * and when restoring fbdev mode.
- */
-void drm_plane_force_disable(struct drm_plane *plane)
-{
-       int ret;
-
-       if (!plane->fb)
-               return;
-
-       plane->old_fb = plane->fb;
-       ret = plane->funcs->disable_plane(plane);
-       if (ret) {
-               DRM_ERROR("failed to disable plane with busy fb\n");
-               plane->old_fb = NULL;
-               return;
-       }
-       /* disconnect the plane from the fb and crtc: */
-       drm_framebuffer_unreference(plane->old_fb);
-       plane->old_fb = NULL;
-       plane->fb = NULL;
-       plane->crtc = NULL;
-}
-EXPORT_SYMBOL(drm_plane_force_disable);
-
-int drm_modeset_register_all(struct drm_device *dev)
-{
-       int ret;
-
-       ret = drm_plane_register_all(dev);
-       if (ret)
-               goto err_plane;
-
-       ret = drm_crtc_register_all(dev);
-       if  (ret)
-               goto err_crtc;
-
-       ret = drm_encoder_register_all(dev);
-       if (ret)
-               goto err_encoder;
-
-       ret = drm_connector_register_all(dev);
-       if (ret)
-               goto err_connector;
-
-       return 0;
-
-err_connector:
-       drm_encoder_unregister_all(dev);
-err_encoder:
-       drm_crtc_unregister_all(dev);
-err_crtc:
-       drm_plane_unregister_all(dev);
-err_plane:
-       return ret;
-}
-
-void drm_modeset_unregister_all(struct drm_device *dev)
-{
-       drm_connector_unregister_all(dev);
-       drm_encoder_unregister_all(dev);
-       drm_crtc_unregister_all(dev);
-       drm_plane_unregister_all(dev);
-}
-
-static int drm_mode_create_standard_properties(struct drm_device *dev)
-{
-       struct drm_property *prop;
-
-       /*
-        * Standard properties (apply to all connectors)
-        */
-       prop = drm_property_create(dev, DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "EDID", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.edid_property = prop;
-
-       prop = drm_property_create_enum(dev, 0,
-                                  "DPMS", drm_dpms_enum_list,
-                                  ARRAY_SIZE(drm_dpms_enum_list));
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.dpms_property = prop;
-
-       prop = drm_property_create(dev,
-                                  DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "PATH", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.path_property = prop;
-
-       prop = drm_property_create(dev,
-                                  DRM_MODE_PROP_BLOB |
-                                  DRM_MODE_PROP_IMMUTABLE,
-                                  "TILE", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.tile_property = prop;
-
-       prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                       "type", drm_plane_type_enum_list,
-                                       ARRAY_SIZE(drm_plane_type_enum_list));
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.plane_type_property = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_X", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_x = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_Y", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_y = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_W", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_w = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "SRC_H", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_src_h = prop;
-
-       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_X", INT_MIN, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_x = prop;
-
-       prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_Y", INT_MIN, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_y = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_W", 0, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_w = prop;
-
-       prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_H", 0, INT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_h = prop;
-
-       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
-                       "FB_ID", DRM_MODE_OBJECT_FB);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_fb_id = prop;
-
-       prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
-                       "CRTC_ID", DRM_MODE_OBJECT_CRTC);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_crtc_id = prop;
-
-       prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC,
-                       "ACTIVE");
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_active = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB,
-                       "MODE_ID", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.prop_mode_id = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "DEGAMMA_LUT", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.degamma_lut_property = prop;
-
-       prop = drm_property_create_range(dev,
-                       DRM_MODE_PROP_IMMUTABLE,
-                       "DEGAMMA_LUT_SIZE", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.degamma_lut_size_property = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "CTM", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.ctm_property = prop;
-
-       prop = drm_property_create(dev,
-                       DRM_MODE_PROP_BLOB,
-                       "GAMMA_LUT", 0);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.gamma_lut_property = prop;
-
-       prop = drm_property_create_range(dev,
-                       DRM_MODE_PROP_IMMUTABLE,
-                       "GAMMA_LUT_SIZE", 0, UINT_MAX);
-       if (!prop)
-               return -ENOMEM;
-       dev->mode_config.gamma_lut_size_property = prop;
-
-       return 0;
-}
-
-/**
- * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties
- * @dev: DRM device
- *
- * Called by a driver the first time a DVI-I connector is made.
- */
-int drm_mode_create_dvi_i_properties(struct drm_device *dev)
-{
-       struct drm_property *dvi_i_selector;
-       struct drm_property *dvi_i_subconnector;
-
-       if (dev->mode_config.dvi_i_select_subconnector_property)
-               return 0;
-
-       dvi_i_selector =
-               drm_property_create_enum(dev, 0,
-                                   "select subconnector",
-                                   drm_dvi_i_select_enum_list,
-                                   ARRAY_SIZE(drm_dvi_i_select_enum_list));
-       dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
-
-       dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "subconnector",
-                                   drm_dvi_i_subconnector_enum_list,
-                                   ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
-       dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_dvi_i_properties);
-
-/**
- * drm_create_tv_properties - create TV specific connector properties
- * @dev: DRM device
- * @num_modes: number of different TV formats (modes) supported
- * @modes: array of pointers to strings containing name of each format
- *
- * Called by a driver's TV initialization routine, this function creates
- * the TV specific connector properties for a given device.  Caller is
- * responsible for allocating a list of format names and passing them to
- * this routine.
- */
-int drm_mode_create_tv_properties(struct drm_device *dev,
-                                 unsigned int num_modes,
-                                 const char * const modes[])
-{
-       struct drm_property *tv_selector;
-       struct drm_property *tv_subconnector;
-       unsigned int i;
-
-       if (dev->mode_config.tv_select_subconnector_property)
-               return 0;
-
-       /*
-        * Basic connector properties
-        */
-       tv_selector = drm_property_create_enum(dev, 0,
-                                         "select subconnector",
-                                         drm_tv_select_enum_list,
-                                         ARRAY_SIZE(drm_tv_select_enum_list));
-       if (!tv_selector)
-               goto nomem;
-
-       dev->mode_config.tv_select_subconnector_property = tv_selector;
-
-       tv_subconnector =
-               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "subconnector",
-                                   drm_tv_subconnector_enum_list,
-                                   ARRAY_SIZE(drm_tv_subconnector_enum_list));
-       if (!tv_subconnector)
-               goto nomem;
-       dev->mode_config.tv_subconnector_property = tv_subconnector;
-
-       /*
-        * Other, TV specific properties: margins & TV modes.
-        */
-       dev->mode_config.tv_left_margin_property =
-               drm_property_create_range(dev, 0, "left margin", 0, 100);
-       if (!dev->mode_config.tv_left_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_right_margin_property =
-               drm_property_create_range(dev, 0, "right margin", 0, 100);
-       if (!dev->mode_config.tv_right_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_top_margin_property =
-               drm_property_create_range(dev, 0, "top margin", 0, 100);
-       if (!dev->mode_config.tv_top_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_bottom_margin_property =
-               drm_property_create_range(dev, 0, "bottom margin", 0, 100);
-       if (!dev->mode_config.tv_bottom_margin_property)
-               goto nomem;
-
-       dev->mode_config.tv_mode_property =
-               drm_property_create(dev, DRM_MODE_PROP_ENUM,
-                                   "mode", num_modes);
-       if (!dev->mode_config.tv_mode_property)
-               goto nomem;
-
-       for (i = 0; i < num_modes; i++)
-               drm_property_add_enum(dev->mode_config.tv_mode_property, i,
-                                     i, modes[i]);
-
-       dev->mode_config.tv_brightness_property =
-               drm_property_create_range(dev, 0, "brightness", 0, 100);
-       if (!dev->mode_config.tv_brightness_property)
-               goto nomem;
-
-       dev->mode_config.tv_contrast_property =
-               drm_property_create_range(dev, 0, "contrast", 0, 100);
-       if (!dev->mode_config.tv_contrast_property)
-               goto nomem;
-
-       dev->mode_config.tv_flicker_reduction_property =
-               drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
-       if (!dev->mode_config.tv_flicker_reduction_property)
-               goto nomem;
-
-       dev->mode_config.tv_overscan_property =
-               drm_property_create_range(dev, 0, "overscan", 0, 100);
-       if (!dev->mode_config.tv_overscan_property)
-               goto nomem;
-
-       dev->mode_config.tv_saturation_property =
-               drm_property_create_range(dev, 0, "saturation", 0, 100);
-       if (!dev->mode_config.tv_saturation_property)
-               goto nomem;
-
-       dev->mode_config.tv_hue_property =
-               drm_property_create_range(dev, 0, "hue", 0, 100);
-       if (!dev->mode_config.tv_hue_property)
-               goto nomem;
-
-       return 0;
-nomem:
-       return -ENOMEM;
-}
-EXPORT_SYMBOL(drm_mode_create_tv_properties);
-
-/**
- * drm_mode_create_scaling_mode_property - create scaling mode property
- * @dev: DRM device
- *
- * Called by a driver the first time it's needed, must be attached to desired
- * connectors.
- */
-int drm_mode_create_scaling_mode_property(struct drm_device *dev)
-{
-       struct drm_property *scaling_mode;
-
-       if (dev->mode_config.scaling_mode_property)
-               return 0;
-
-       scaling_mode =
-               drm_property_create_enum(dev, 0, "scaling mode",
-                               drm_scaling_mode_enum_list,
-                                   ARRAY_SIZE(drm_scaling_mode_enum_list));
-
-       dev->mode_config.scaling_mode_property = scaling_mode;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
-
-/**
- * drm_mode_create_aspect_ratio_property - create aspect ratio property
- * @dev: DRM device
- *
- * Called by a driver the first time it's needed, must be attached to desired
- * connectors.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_create_aspect_ratio_property(struct drm_device *dev)
-{
-       if (dev->mode_config.aspect_ratio_property)
-               return 0;
-
-       dev->mode_config.aspect_ratio_property =
-               drm_property_create_enum(dev, 0, "aspect ratio",
-                               drm_aspect_ratio_enum_list,
-                               ARRAY_SIZE(drm_aspect_ratio_enum_list));
-
-       if (dev->mode_config.aspect_ratio_property == NULL)
-               return -ENOMEM;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property);
-
-/**
- * drm_mode_create_dirty_property - create dirty property
- * @dev: DRM device
- *
- * Called by a driver the first time it's needed, must be attached to desired
- * connectors.
- */
-int drm_mode_create_dirty_info_property(struct drm_device *dev)
-{
-       struct drm_property *dirty_info;
-
-       if (dev->mode_config.dirty_info_property)
-               return 0;
-
-       dirty_info =
-               drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
-                                   "dirty",
-                                   drm_dirty_info_enum_list,
-                                   ARRAY_SIZE(drm_dirty_info_enum_list));
-       dev->mode_config.dirty_info_property = dirty_info;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
-
-/**
- * drm_mode_create_suggested_offset_properties - create suggests offset properties
- * @dev: DRM device
- *
- * Create the the suggested x/y offset property for connectors.
- */
-int drm_mode_create_suggested_offset_properties(struct drm_device *dev)
-{
-       if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property)
-               return 0;
-
-       dev->mode_config.suggested_x_property =
-               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff);
-
-       dev->mode_config.suggested_y_property =
-               drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff);
-
-       if (dev->mode_config.suggested_x_property == NULL ||
-           dev->mode_config.suggested_y_property == NULL)
-               return -ENOMEM;
-       return 0;
-}
-EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties);
-
-/**
- * drm_mode_getresources - get graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a set of configuration description structures and return
- * them to the user, including CRTC, connector and framebuffer configuration.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getresources(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_card_res *card_res = data;
-       struct list_head *lh;
-       struct drm_framebuffer *fb;
-       struct drm_connector *connector;
-       struct drm_crtc *crtc;
-       struct drm_encoder *encoder;
-       int ret = 0;
-       int connector_count = 0;
-       int crtc_count = 0;
-       int fb_count = 0;
-       int encoder_count = 0;
-       int copied = 0;
-       uint32_t __user *fb_id;
-       uint32_t __user *crtc_id;
-       uint32_t __user *connector_id;
-       uint32_t __user *encoder_id;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-
-       mutex_lock(&file_priv->fbs_lock);
-       /*
-        * For the non-control nodes we need to limit the list of resources
-        * by IDs in the group list for this node
-        */
-       list_for_each(lh, &file_priv->fbs)
-               fb_count++;
-
-       /* handle this in 4 parts */
-       /* FBs */
-       if (card_res->count_fbs >= fb_count) {
-               copied = 0;
-               fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
-               list_for_each_entry(fb, &file_priv->fbs, filp_head) {
-                       if (put_user(fb->base.id, fb_id + copied)) {
-                               mutex_unlock(&file_priv->fbs_lock);
-                               return -EFAULT;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_fbs = fb_count;
-       mutex_unlock(&file_priv->fbs_lock);
-
-       /* mode_config.mutex protects the connector list against e.g. DP MST
-        * connector hot-adding. CRTC/Plane lists are invariant. */
-       mutex_lock(&dev->mode_config.mutex);
-       drm_for_each_crtc(crtc, dev)
-               crtc_count++;
-
-       drm_for_each_connector(connector, dev)
-               connector_count++;
-
-       drm_for_each_encoder(encoder, dev)
-               encoder_count++;
-
-       card_res->max_height = dev->mode_config.max_height;
-       card_res->min_height = dev->mode_config.min_height;
-       card_res->max_width = dev->mode_config.max_width;
-       card_res->min_width = dev->mode_config.min_width;
-
-       /* CRTCs */
-       if (card_res->count_crtcs >= crtc_count) {
-               copied = 0;
-               crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
-               drm_for_each_crtc(crtc, dev) {
-                       if (put_user(crtc->base.id, crtc_id + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_crtcs = crtc_count;
-
-       /* Encoders */
-       if (card_res->count_encoders >= encoder_count) {
-               copied = 0;
-               encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
-               drm_for_each_encoder(encoder, dev) {
-                       if (put_user(encoder->base.id, encoder_id +
-                                    copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_encoders = encoder_count;
-
-       /* Connectors */
-       if (card_res->count_connectors >= connector_count) {
-               copied = 0;
-               connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
-               drm_for_each_connector(connector, dev) {
-                       if (put_user(connector->base.id,
-                                    connector_id + copied)) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       card_res->count_connectors = connector_count;
-
-out:
-       mutex_unlock(&dev->mode_config.mutex);
-       return ret;
-}
-
-/**
- * drm_mode_getcrtc - get CRTC configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a CRTC configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getcrtc(struct drm_device *dev,
-                    void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_crtc *crtc_resp = data;
-       struct drm_crtc *crtc;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
-       if (!crtc)
-               return -ENOENT;
-
-       drm_modeset_lock_crtc(crtc, crtc->primary);
-       crtc_resp->gamma_size = crtc->gamma_size;
-       if (crtc->primary->fb)
-               crtc_resp->fb_id = crtc->primary->fb->base.id;
-       else
-               crtc_resp->fb_id = 0;
-
-       if (crtc->state) {
-               crtc_resp->x = crtc->primary->state->src_x >> 16;
-               crtc_resp->y = crtc->primary->state->src_y >> 16;
-               if (crtc->state->enable) {
-                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
-                       crtc_resp->mode_valid = 1;
-
-               } else {
-                       crtc_resp->mode_valid = 0;
-               }
-       } else {
-               crtc_resp->x = crtc->x;
-               crtc_resp->y = crtc->y;
-               if (crtc->enabled) {
-                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
-                       crtc_resp->mode_valid = 1;
-
-               } else {
-                       crtc_resp->mode_valid = 0;
-               }
-       }
-       drm_modeset_unlock_crtc(crtc);
-
-       return 0;
-}
-
-static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode,
-                                        const struct drm_file *file_priv)
-{
-       /*
-        * If user-space hasn't configured the driver to expose the stereo 3D
-        * modes, don't expose them.
-        */
-       if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode))
-               return false;
-
-       return true;
-}
-
-static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector)
-{
-       /* For atomic drivers only state objects are synchronously updated and
-        * protected by modeset locks, so check those first. */
-       if (connector->state)
-               return connector->state->best_encoder;
-       return connector->encoder;
-}
-
-/* helper for getconnector and getproperties ioctls */
-static int get_properties(struct drm_mode_object *obj, bool atomic,
-               uint32_t __user *prop_ptr, uint64_t __user *prop_values,
-               uint32_t *arg_count_props)
-{
-       int props_count;
-       int i, ret, copied;
-
-       props_count = obj->properties->count;
-       if (!atomic)
-               props_count -= obj->properties->atomic_count;
-
-       if ((*arg_count_props >= props_count) && props_count) {
-               for (i = 0, copied = 0; copied < props_count; i++) {
-                       struct drm_property *prop = obj->properties->properties[i];
-                       uint64_t val;
-
-                       if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
-                               continue;
-
-                       ret = drm_object_property_get_value(obj, prop, &val);
-                       if (ret)
-                               return ret;
-
-                       if (put_user(prop->base.id, prop_ptr + copied))
-                               return -EFAULT;
-
-                       if (put_user(val, prop_values + copied))
-                               return -EFAULT;
-
-                       copied++;
-               }
-       }
-       *arg_count_props = props_count;
-
-       return 0;
-}
-
-/**
- * drm_mode_getconnector - get connector configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a connector configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getconnector(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_get_connector *out_resp = data;
-       struct drm_connector *connector;
-       struct drm_encoder *encoder;
-       struct drm_display_mode *mode;
-       int mode_count = 0;
-       int encoders_count = 0;
-       int ret = 0;
-       int copied = 0;
-       int i;
-       struct drm_mode_modeinfo u_mode;
-       struct drm_mode_modeinfo __user *mode_ptr;
-       uint32_t __user *encoder_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo));
-
-       mutex_lock(&dev->mode_config.mutex);
-
-       connector = drm_connector_lookup(dev, out_resp->connector_id);
-       if (!connector) {
-               ret = -ENOENT;
-               goto out_unlock;
-       }
-
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
-               if (connector->encoder_ids[i] != 0)
-                       encoders_count++;
-
-       if (out_resp->count_modes == 0) {
-               connector->funcs->fill_modes(connector,
-                                            dev->mode_config.max_width,
-                                            dev->mode_config.max_height);
-       }
-
-       /* delayed so we get modes regardless of pre-fill_modes state */
-       list_for_each_entry(mode, &connector->modes, head)
-               if (drm_mode_expose_to_userspace(mode, file_priv))
-                       mode_count++;
-
-       out_resp->connector_id = connector->base.id;
-       out_resp->connector_type = connector->connector_type;
-       out_resp->connector_type_id = connector->connector_type_id;
-       out_resp->mm_width = connector->display_info.width_mm;
-       out_resp->mm_height = connector->display_info.height_mm;
-       out_resp->subpixel = connector->display_info.subpixel_order;
-       out_resp->connection = connector->status;
-
-       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-       encoder = drm_connector_get_encoder(connector);
-       if (encoder)
-               out_resp->encoder_id = encoder->base.id;
-       else
-               out_resp->encoder_id = 0;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if ((out_resp->count_modes >= mode_count) && mode_count) {
-               copied = 0;
-               mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
-               list_for_each_entry(mode, &connector->modes, head) {
-                       if (!drm_mode_expose_to_userspace(mode, file_priv))
-                               continue;
-
-                       drm_mode_convert_to_umode(&u_mode, mode);
-                       if (copy_to_user(mode_ptr + copied,
-                                        &u_mode, sizeof(u_mode))) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-                       copied++;
-               }
-       }
-       out_resp->count_modes = mode_count;
-
-       ret = get_properties(&connector->base, file_priv->atomic,
-                       (uint32_t __user *)(unsigned long)(out_resp->props_ptr),
-                       (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr),
-                       &out_resp->count_props);
-       if (ret)
-               goto out;
-
-       if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
-               copied = 0;
-               encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
-               for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-                       if (connector->encoder_ids[i] != 0) {
-                               if (put_user(connector->encoder_ids[i],
-                                            encoder_ptr + copied)) {
-                                       ret = -EFAULT;
-                                       goto out;
-                               }
-                               copied++;
-                       }
-               }
-       }
-       out_resp->count_encoders = encoders_count;
-
-out:
-       drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
-       drm_connector_unreference(connector);
-out_unlock:
-       mutex_unlock(&dev->mode_config.mutex);
-
-       return ret;
-}
-
-static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
-{
-       struct drm_connector *connector;
-       struct drm_device *dev = encoder->dev;
-       bool uses_atomic = false;
-
-       /* For atomic drivers only state objects are synchronously updated and
-        * protected by modeset locks, so check those first. */
-       drm_for_each_connector(connector, dev) {
-               if (!connector->state)
-                       continue;
-
-               uses_atomic = true;
-
-               if (connector->state->best_encoder != encoder)
-                       continue;
-
-               return connector->state->crtc;
-       }
-
-       /* Don't return stale data (e.g. pending async disable). */
-       if (uses_atomic)
-               return NULL;
-
-       return encoder->crtc;
-}
-
-/**
- * drm_mode_getencoder - get encoder configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Construct a encoder configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getencoder(struct drm_device *dev, void *data,
-                       struct drm_file *file_priv)
-{
-       struct drm_mode_get_encoder *enc_resp = data;
-       struct drm_encoder *encoder;
-       struct drm_crtc *crtc;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       encoder = drm_encoder_find(dev, enc_resp->encoder_id);
-       if (!encoder)
-               return -ENOENT;
-
-       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
-       crtc = drm_encoder_get_crtc(encoder);
-       if (crtc)
-               enc_resp->crtc_id = crtc->base.id;
-       else
-               enc_resp->crtc_id = 0;
-       drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
-       enc_resp->encoder_type = encoder->encoder_type;
-       enc_resp->encoder_id = encoder->base.id;
-       enc_resp->possible_crtcs = encoder->possible_crtcs;
-       enc_resp->possible_clones = encoder->possible_clones;
-
-       return 0;
-}
-
-/**
- * drm_mode_getplane_res - enumerate all plane resources
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Construct a list of plane ids to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getplane_res(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv)
-{
-       struct drm_mode_get_plane_res *plane_resp = data;
-       struct drm_mode_config *config;
-       struct drm_plane *plane;
-       uint32_t __user *plane_ptr;
-       int copied = 0;
-       unsigned num_planes;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       config = &dev->mode_config;
-
-       if (file_priv->universal_planes)
-               num_planes = config->num_total_plane;
-       else
-               num_planes = config->num_overlay_plane;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if (num_planes &&
-           (plane_resp->count_planes >= num_planes)) {
-               plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
-
-               /* Plane lists are invariant, no locking needed. */
-               drm_for_each_plane(plane, dev) {
-                       /*
-                        * Unless userspace set the 'universal planes'
-                        * capability bit, only advertise overlays.
-                        */
-                       if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
-                           !file_priv->universal_planes)
-                               continue;
-
-                       if (put_user(plane->base.id, plane_ptr + copied))
-                               return -EFAULT;
-                       copied++;
-               }
-       }
-       plane_resp->count_planes = num_planes;
-
-       return 0;
-}
-
-/**
- * drm_mode_getplane - get plane configuration
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Construct a plane configuration structure to return to the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getplane(struct drm_device *dev, void *data,
-                     struct drm_file *file_priv)
-{
-       struct drm_mode_get_plane *plane_resp = data;
-       struct drm_plane *plane;
-       uint32_t __user *format_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       plane = drm_plane_find(dev, plane_resp->plane_id);
-       if (!plane)
-               return -ENOENT;
-
-       drm_modeset_lock(&plane->mutex, NULL);
-       if (plane->crtc)
-               plane_resp->crtc_id = plane->crtc->base.id;
-       else
-               plane_resp->crtc_id = 0;
-
-       if (plane->fb)
-               plane_resp->fb_id = plane->fb->base.id;
-       else
-               plane_resp->fb_id = 0;
-       drm_modeset_unlock(&plane->mutex);
-
-       plane_resp->plane_id = plane->base.id;
-       plane_resp->possible_crtcs = plane->possible_crtcs;
-       plane_resp->gamma_size = 0;
-
-       /*
-        * This ioctl is called twice, once to determine how much space is
-        * needed, and the 2nd time to fill it.
-        */
-       if (plane->format_count &&
-           (plane_resp->count_format_types >= plane->format_count)) {
-               format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
-               if (copy_to_user(format_ptr,
-                                plane->format_types,
-                                sizeof(uint32_t) * plane->format_count)) {
-                       return -EFAULT;
-               }
-       }
-       plane_resp->count_format_types = plane->format_count;
-
-       return 0;
-}
-
-/**
- * drm_plane_check_pixel_format - Check if the plane supports the pixel format
- * @plane: plane to check for format support
- * @format: the pixel format
- *
- * Returns:
- * Zero of @plane has @format in its list of supported pixel formats, -EINVAL
- * otherwise.
- */
-int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
-{
-       unsigned int i;
-
-       for (i = 0; i < plane->format_count; i++) {
-               if (format == plane->format_types[i])
-                       return 0;
-       }
-
-       return -EINVAL;
-}
-
-static int check_src_coords(uint32_t src_x, uint32_t src_y,
-                           uint32_t src_w, uint32_t src_h,
-                           const struct drm_framebuffer *fb)
-{
-       unsigned int fb_width, fb_height;
-
-       fb_width = fb->width << 16;
-       fb_height = fb->height << 16;
-
-       /* Make sure source coordinates are inside the fb. */
-       if (src_w > fb_width ||
-           src_x > fb_width - src_w ||
-           src_h > fb_height ||
-           src_y > fb_height - src_h) {
-               DRM_DEBUG_KMS("Invalid source coordinates "
-                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
-                             src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
-                             src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
-                             src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
-                             src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
-               return -ENOSPC;
-       }
-
-       return 0;
-}
-
-/*
- * setplane_internal - setplane handler for internal callers
- *
- * Note that we assume an extra reference has already been taken on fb.  If the
- * update fails, this reference will be dropped before return; if it succeeds,
- * the previous framebuffer (if any) will be unreferenced instead.
- *
- * src_{x,y,w,h} are provided in 16.16 fixed point format
- */
-static int __setplane_internal(struct drm_plane *plane,
-                              struct drm_crtc *crtc,
-                              struct drm_framebuffer *fb,
-                              int32_t crtc_x, int32_t crtc_y,
-                              uint32_t crtc_w, uint32_t crtc_h,
-                              /* src_{x,y,w,h} values are 16.16 fixed point */
-                              uint32_t src_x, uint32_t src_y,
-                              uint32_t src_w, uint32_t src_h)
-{
-       int ret = 0;
-
-       /* No fb means shut it down */
-       if (!fb) {
-               plane->old_fb = plane->fb;
-               ret = plane->funcs->disable_plane(plane);
-               if (!ret) {
-                       plane->crtc = NULL;
-                       plane->fb = NULL;
-               } else {
-                       plane->old_fb = NULL;
-               }
-               goto out;
-       }
-
-       /* Check whether this plane is usable on this CRTC */
-       if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
-               DRM_DEBUG_KMS("Invalid crtc for plane\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       /* Check whether this plane supports the fb pixel format. */
-       ret = drm_plane_check_pixel_format(plane, fb->pixel_format);
-       if (ret) {
-               DRM_DEBUG_KMS("Invalid pixel format %s\n",
-                             drm_get_format_name(fb->pixel_format));
-               goto out;
-       }
-
-       /* Give drivers some help against integer overflows */
-       if (crtc_w > INT_MAX ||
-           crtc_x > INT_MAX - (int32_t) crtc_w ||
-           crtc_h > INT_MAX ||
-           crtc_y > INT_MAX - (int32_t) crtc_h) {
-               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
-                             crtc_w, crtc_h, crtc_x, crtc_y);
-               ret = -ERANGE;
-               goto out;
-       }
-
-       ret = check_src_coords(src_x, src_y, src_w, src_h, fb);
-       if (ret)
-               goto out;
-
-       plane->old_fb = plane->fb;
-       ret = plane->funcs->update_plane(plane, crtc, fb,
-                                        crtc_x, crtc_y, crtc_w, crtc_h,
-                                        src_x, src_y, src_w, src_h);
-       if (!ret) {
-               plane->crtc = crtc;
-               plane->fb = fb;
-               fb = NULL;
-       } else {
-               plane->old_fb = NULL;
-       }
-
-out:
-       if (fb)
-               drm_framebuffer_unreference(fb);
-       if (plane->old_fb)
-               drm_framebuffer_unreference(plane->old_fb);
-       plane->old_fb = NULL;
-
-       return ret;
-}
-
-static int setplane_internal(struct drm_plane *plane,
-                            struct drm_crtc *crtc,
-                            struct drm_framebuffer *fb,
-                            int32_t crtc_x, int32_t crtc_y,
-                            uint32_t crtc_w, uint32_t crtc_h,
-                            /* src_{x,y,w,h} values are 16.16 fixed point */
-                            uint32_t src_x, uint32_t src_y,
-                            uint32_t src_w, uint32_t src_h)
-{
-       int ret;
-
-       drm_modeset_lock_all(plane->dev);
-       ret = __setplane_internal(plane, crtc, fb,
-                                 crtc_x, crtc_y, crtc_w, crtc_h,
-                                 src_x, src_y, src_w, src_h);
-       drm_modeset_unlock_all(plane->dev);
-
-       return ret;
-}
-
-/**
- * drm_mode_setplane - configure a plane's configuration
- * @dev: DRM device
- * @data: ioctl data*
- * @file_priv: DRM file info
- *
- * Set plane configuration, including placement, fb, scaling, and other factors.
- * Or pass a NULL fb to disable (planes may be disabled without providing a
- * valid crtc).
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_setplane(struct drm_device *dev, void *data,
-                     struct drm_file *file_priv)
-{
-       struct drm_mode_set_plane *plane_req = data;
-       struct drm_plane *plane;
-       struct drm_crtc *crtc = NULL;
-       struct drm_framebuffer *fb = NULL;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       /*
-        * First, find the plane, crtc, and fb objects.  If not available,
-        * we don't bother to call the driver.
-        */
-       plane = drm_plane_find(dev, plane_req->plane_id);
-       if (!plane) {
-               DRM_DEBUG_KMS("Unknown plane ID %d\n",
-                             plane_req->plane_id);
-               return -ENOENT;
-       }
-
-       if (plane_req->fb_id) {
-               fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
-               if (!fb) {
-                       DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
-                                     plane_req->fb_id);
-                       return -ENOENT;
-               }
-
-               crtc = drm_crtc_find(dev, plane_req->crtc_id);
-               if (!crtc) {
-                       DRM_DEBUG_KMS("Unknown crtc ID %d\n",
-                                     plane_req->crtc_id);
-                       return -ENOENT;
-               }
-       }
-
-       /*
-        * setplane_internal will take care of deref'ing either the old or new
-        * framebuffer depending on success.
-        */
-       return setplane_internal(plane, crtc, fb,
-                                plane_req->crtc_x, plane_req->crtc_y,
-                                plane_req->crtc_w, plane_req->crtc_h,
-                                plane_req->src_x, plane_req->src_y,
-                                plane_req->src_w, plane_req->src_h);
-}
-
-/**
- * drm_mode_set_config_internal - helper to call ->set_config
- * @set: modeset config to set
- *
- * This is a little helper to wrap internal calls to the ->set_config driver
- * interface. The only thing it adds is correct refcounting dance.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_set_config_internal(struct drm_mode_set *set)
-{
-       struct drm_crtc *crtc = set->crtc;
-       struct drm_framebuffer *fb;
-       struct drm_crtc *tmp;
-       int ret;
-
-       /*
-        * NOTE: ->set_config can also disable other crtcs (if we steal all
-        * connectors from it), hence we need to refcount the fbs across all
-        * crtcs. Atomic modeset will have saner semantics ...
-        */
-       drm_for_each_crtc(tmp, crtc->dev)
-               tmp->primary->old_fb = tmp->primary->fb;
-
-       fb = set->fb;
-
-       ret = crtc->funcs->set_config(set);
-       if (ret == 0) {
-               crtc->primary->crtc = crtc;
-               crtc->primary->fb = fb;
-       }
-
-       drm_for_each_crtc(tmp, crtc->dev) {
-               if (tmp->primary->fb)
-                       drm_framebuffer_reference(tmp->primary->fb);
-               if (tmp->primary->old_fb)
-                       drm_framebuffer_unreference(tmp->primary->old_fb);
-               tmp->primary->old_fb = NULL;
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_set_config_internal);
-
-/**
- * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode
- * @mode: mode to query
- * @hdisplay: hdisplay value to fill in
- * @vdisplay: vdisplay value to fill in
- *
- * The vdisplay value will be doubled if the specified mode is a stereo mode of
- * the appropriate layout.
- */
-void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
-                           int *hdisplay, int *vdisplay)
-{
-       struct drm_display_mode adjusted;
-
-       drm_mode_copy(&adjusted, mode);
-       drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
-       *hdisplay = adjusted.crtc_hdisplay;
-       *vdisplay = adjusted.crtc_vdisplay;
-}
-EXPORT_SYMBOL(drm_crtc_get_hv_timing);
-
-/**
- * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
- *     CRTC viewport
- * @crtc: CRTC that framebuffer will be displayed on
- * @x: x panning
- * @y: y panning
- * @mode: mode that framebuffer will be displayed under
- * @fb: framebuffer to check size of
- */
-int drm_crtc_check_viewport(const struct drm_crtc *crtc,
-                           int x, int y,
-                           const struct drm_display_mode *mode,
-                           const struct drm_framebuffer *fb)
-
-{
-       int hdisplay, vdisplay;
-
-       drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
-
-       if (crtc->state &&
-           crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) |
-                                             BIT(DRM_ROTATE_270)))
-               swap(hdisplay, vdisplay);
-
-       return check_src_coords(x << 16, y << 16,
-                               hdisplay << 16, vdisplay << 16, fb);
-}
-EXPORT_SYMBOL(drm_crtc_check_viewport);
-
-/**
- * drm_mode_setcrtc - set CRTC configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Build a new CRTC configuration based on user request.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_setcrtc(struct drm_device *dev, void *data,
-                    struct drm_file *file_priv)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_mode_crtc *crtc_req = data;
-       struct drm_crtc *crtc;
-       struct drm_connector **connector_set = NULL, *connector;
-       struct drm_framebuffer *fb = NULL;
-       struct drm_display_mode *mode = NULL;
-       struct drm_mode_set set;
-       uint32_t __user *set_connectors_ptr;
-       int ret;
-       int i;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       /*
-        * Universal plane src offsets are only 16.16, prevent havoc for
-        * drivers using universal plane code internally.
-        */
-       if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
-               return -ERANGE;
-
-       drm_modeset_lock_all(dev);
-       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
-       if (!crtc) {
-               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
-               ret = -ENOENT;
-               goto out;
-       }
-       DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
-
-       if (crtc_req->mode_valid) {
-               /* If we have a mode we need a framebuffer. */
-               /* If we pass -1, set the mode with the currently bound fb */
-               if (crtc_req->fb_id == -1) {
-                       if (!crtc->primary->fb) {
-                               DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
-                               ret = -EINVAL;
-                               goto out;
-                       }
-                       fb = crtc->primary->fb;
-                       /* Make refcounting symmetric with the lookup path. */
-                       drm_framebuffer_reference(fb);
-               } else {
-                       fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
-                       if (!fb) {
-                               DRM_DEBUG_KMS("Unknown FB ID%d\n",
-                                               crtc_req->fb_id);
-                               ret = -ENOENT;
-                               goto out;
-                       }
-               }
-
-               mode = drm_mode_create(dev);
-               if (!mode) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               ret = drm_mode_convert_umode(mode, &crtc_req->mode);
-               if (ret) {
-                       DRM_DEBUG_KMS("Invalid mode\n");
-                       goto out;
-               }
-
-               /*
-                * Check whether the primary plane supports the fb pixel format.
-                * Drivers not implementing the universal planes API use a
-                * default formats list provided by the DRM core which doesn't
-                * match real hardware capabilities. Skip the check in that
-                * case.
-                */
-               if (!crtc->primary->format_default) {
-                       ret = drm_plane_check_pixel_format(crtc->primary,
-                                                          fb->pixel_format);
-                       if (ret) {
-                               DRM_DEBUG_KMS("Invalid pixel format %s\n",
-                                       drm_get_format_name(fb->pixel_format));
-                               goto out;
-                       }
-               }
-
-               ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
-                                             mode, fb);
-               if (ret)
-                       goto out;
-
-       }
-
-       if (crtc_req->count_connectors == 0 && mode) {
-               DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
-               DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
-                         crtc_req->count_connectors);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       if (crtc_req->count_connectors > 0) {
-               u32 out_id;
-
-               /* Avoid unbounded kernel memory allocation */
-               if (crtc_req->count_connectors > config->num_connector) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-
-               connector_set = kmalloc_array(crtc_req->count_connectors,
-                                             sizeof(struct drm_connector *),
-                                             GFP_KERNEL);
-               if (!connector_set) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-
-               for (i = 0; i < crtc_req->count_connectors; i++) {
-                       connector_set[i] = NULL;
-                       set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
-                       if (get_user(out_id, &set_connectors_ptr[i])) {
-                               ret = -EFAULT;
-                               goto out;
-                       }
-
-                       connector = drm_connector_lookup(dev, out_id);
-                       if (!connector) {
-                               DRM_DEBUG_KMS("Connector id %d unknown\n",
-                                               out_id);
-                               ret = -ENOENT;
-                               goto out;
-                       }
-                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
-                                       connector->base.id,
-                                       connector->name);
-
-                       connector_set[i] = connector;
-               }
-       }
-
-       set.crtc = crtc;
-       set.x = crtc_req->x;
-       set.y = crtc_req->y;
-       set.mode = mode;
-       set.connectors = connector_set;
-       set.num_connectors = crtc_req->count_connectors;
-       set.fb = fb;
-       ret = drm_mode_set_config_internal(&set);
-
-out:
-       if (fb)
-               drm_framebuffer_unreference(fb);
-
-       if (connector_set) {
-               for (i = 0; i < crtc_req->count_connectors; i++) {
-                       if (connector_set[i])
-                               drm_connector_unreference(connector_set[i]);
-               }
-       }
-       kfree(connector_set);
-       drm_mode_destroy(dev, mode);
-       drm_modeset_unlock_all(dev);
-       return ret;
-}
-
-/**
- * drm_mode_cursor_universal - translate legacy cursor ioctl call into a
- *     universal plane handler call
- * @crtc: crtc to update cursor for
- * @req: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Legacy cursor ioctl's work directly with driver buffer handles.  To
- * translate legacy ioctl calls into universal plane handler calls, we need to
- * wrap the native buffer handle in a drm_framebuffer.
- *
- * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB
- * buffer with a pitch of 4*width; the universal plane interface should be used
- * directly in cases where the hardware can support other buffer settings and
- * userspace wants to make use of these capabilities.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-static int drm_mode_cursor_universal(struct drm_crtc *crtc,
-                                    struct drm_mode_cursor2 *req,
-                                    struct drm_file *file_priv)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_framebuffer *fb = NULL;
-       struct drm_mode_fb_cmd2 fbreq = {
-               .width = req->width,
-               .height = req->height,
-               .pixel_format = DRM_FORMAT_ARGB8888,
-               .pitches = { req->width * 4 },
-               .handles = { req->handle },
-       };
-       int32_t crtc_x, crtc_y;
-       uint32_t crtc_w = 0, crtc_h = 0;
-       uint32_t src_w = 0, src_h = 0;
-       int ret = 0;
-
-       BUG_ON(!crtc->cursor);
-       WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
-
-       /*
-        * Obtain fb we'll be using (either new or existing) and take an extra
-        * reference to it if fb != null.  setplane will take care of dropping
-        * the reference if the plane update fails.
-        */
-       if (req->flags & DRM_MODE_CURSOR_BO) {
-               if (req->handle) {
-                       fb = internal_framebuffer_create(dev, &fbreq, file_priv);
-                       if (IS_ERR(fb)) {
-                               DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
-                               return PTR_ERR(fb);
-                       }
-                       fb->hot_x = req->hot_x;
-                       fb->hot_y = req->hot_y;
-               } else {
-                       fb = NULL;
-               }
-       } else {
-               fb = crtc->cursor->fb;
-               if (fb)
-                       drm_framebuffer_reference(fb);
-       }
-
-       if (req->flags & DRM_MODE_CURSOR_MOVE) {
-               crtc_x = req->x;
-               crtc_y = req->y;
-       } else {
-               crtc_x = crtc->cursor_x;
-               crtc_y = crtc->cursor_y;
-       }
-
-       if (fb) {
-               crtc_w = fb->width;
-               crtc_h = fb->height;
-               src_w = fb->width << 16;
-               src_h = fb->height << 16;
-       }
-
-       /*
-        * setplane_internal will take care of deref'ing either the old or new
-        * framebuffer depending on success.
-        */
-       ret = __setplane_internal(crtc->cursor, crtc, fb,
-                               crtc_x, crtc_y, crtc_w, crtc_h,
-                               0, 0, src_w, src_h);
-
-       /* Update successful; save new cursor position, if necessary */
-       if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
-               crtc->cursor_x = req->x;
-               crtc->cursor_y = req->y;
-       }
-
-       return ret;
-}
-
-static int drm_mode_cursor_common(struct drm_device *dev,
-                                 struct drm_mode_cursor2 *req,
-                                 struct drm_file *file_priv)
-{
-       struct drm_crtc *crtc;
-       int ret = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
-               return -EINVAL;
-
-       crtc = drm_crtc_find(dev, req->crtc_id);
-       if (!crtc) {
-               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
-               return -ENOENT;
-       }
-
-       /*
-        * If this crtc has a universal cursor plane, call that plane's update
-        * handler rather than using legacy cursor handlers.
-        */
-       drm_modeset_lock_crtc(crtc, crtc->cursor);
-       if (crtc->cursor) {
-               ret = drm_mode_cursor_universal(crtc, req, file_priv);
-               goto out;
-       }
-
-       if (req->flags & DRM_MODE_CURSOR_BO) {
-               if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
-                       ret = -ENXIO;
-                       goto out;
-               }
-               /* Turns off the cursor if handle is 0 */
-               if (crtc->funcs->cursor_set2)
-                       ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
-                                                     req->width, req->height, req->hot_x, req->hot_y);
-               else
-                       ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
-                                                     req->width, req->height);
-       }
-
-       if (req->flags & DRM_MODE_CURSOR_MOVE) {
-               if (crtc->funcs->cursor_move) {
-                       ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
-               } else {
-                       ret = -EFAULT;
-                       goto out;
-               }
-       }
-out:
-       drm_modeset_unlock_crtc(crtc);
-
-       return ret;
-
-}
-
-
-/**
- * drm_mode_cursor_ioctl - set CRTC's cursor configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Set the cursor configuration based on user request.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_cursor_ioctl(struct drm_device *dev,
-                         void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_cursor *req = data;
-       struct drm_mode_cursor2 new_req;
-
-       memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
-       new_req.hot_x = new_req.hot_y = 0;
-
-       return drm_mode_cursor_common(dev, &new_req, file_priv);
-}
-
-/**
- * drm_mode_cursor2_ioctl - set CRTC's cursor configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Set the cursor configuration based on user request. This implements the 2nd
- * version of the cursor ioctl, which allows userspace to additionally specify
- * the hotspot of the pointer.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_cursor2_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_cursor2 *req = data;
-
-       return drm_mode_cursor_common(dev, req, file_priv);
-}
-
-/**
- * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
- * @bpp: bits per pixels
- * @depth: bit depth per pixel
- *
- * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
- * Useful in fbdev emulation code, since that deals in those values.
- */
-uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
-{
-       uint32_t fmt;
-
-       switch (bpp) {
-       case 8:
-               fmt = DRM_FORMAT_C8;
-               break;
-       case 16:
-               if (depth == 15)
-                       fmt = DRM_FORMAT_XRGB1555;
-               else
-                       fmt = DRM_FORMAT_RGB565;
-               break;
-       case 24:
-               fmt = DRM_FORMAT_RGB888;
-               break;
-       case 32:
-               if (depth == 24)
-                       fmt = DRM_FORMAT_XRGB8888;
-               else if (depth == 30)
-                       fmt = DRM_FORMAT_XRGB2101010;
-               else
-                       fmt = DRM_FORMAT_ARGB8888;
-               break;
-       default:
-               DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
-               fmt = DRM_FORMAT_XRGB8888;
-               break;
-       }
-
-       return fmt;
-}
-EXPORT_SYMBOL(drm_mode_legacy_fb_format);
-
-/**
- * drm_mode_addfb - add an FB to the graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Add a new FB to the specified CRTC, given a user request. This is the
- * original addfb ioctl which only supported RGB formats.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_addfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_fb_cmd *or = data;
-       struct drm_mode_fb_cmd2 r = {};
-       int ret;
-
-       /* convert to new format and call new ioctl */
-       r.fb_id = or->fb_id;
-       r.width = or->width;
-       r.height = or->height;
-       r.pitches[0] = or->pitch;
-       r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
-       r.handles[0] = or->handle;
-
-       ret = drm_mode_addfb2(dev, &r, file_priv);
-       if (ret)
-               return ret;
-
-       or->fb_id = r.fb_id;
-
-       return 0;
-}
-
-static int format_check(const struct drm_mode_fb_cmd2 *r)
-{
-       uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
-
-       switch (format) {
-       case DRM_FORMAT_C8:
-       case DRM_FORMAT_RGB332:
-       case DRM_FORMAT_BGR233:
-       case DRM_FORMAT_XRGB4444:
-       case DRM_FORMAT_XBGR4444:
-       case DRM_FORMAT_RGBX4444:
-       case DRM_FORMAT_BGRX4444:
-       case DRM_FORMAT_ARGB4444:
-       case DRM_FORMAT_ABGR4444:
-       case DRM_FORMAT_RGBA4444:
-       case DRM_FORMAT_BGRA4444:
-       case DRM_FORMAT_XRGB1555:
-       case DRM_FORMAT_XBGR1555:
-       case DRM_FORMAT_RGBX5551:
-       case DRM_FORMAT_BGRX5551:
-       case DRM_FORMAT_ARGB1555:
-       case DRM_FORMAT_ABGR1555:
-       case DRM_FORMAT_RGBA5551:
-       case DRM_FORMAT_BGRA5551:
-       case DRM_FORMAT_RGB565:
-       case DRM_FORMAT_BGR565:
-       case DRM_FORMAT_RGB888:
-       case DRM_FORMAT_BGR888:
-       case DRM_FORMAT_XRGB8888:
-       case DRM_FORMAT_XBGR8888:
-       case DRM_FORMAT_RGBX8888:
-       case DRM_FORMAT_BGRX8888:
-       case DRM_FORMAT_ARGB8888:
-       case DRM_FORMAT_ABGR8888:
-       case DRM_FORMAT_RGBA8888:
-       case DRM_FORMAT_BGRA8888:
-       case DRM_FORMAT_XRGB2101010:
-       case DRM_FORMAT_XBGR2101010:
-       case DRM_FORMAT_RGBX1010102:
-       case DRM_FORMAT_BGRX1010102:
-       case DRM_FORMAT_ARGB2101010:
-       case DRM_FORMAT_ABGR2101010:
-       case DRM_FORMAT_RGBA1010102:
-       case DRM_FORMAT_BGRA1010102:
-       case DRM_FORMAT_YUYV:
-       case DRM_FORMAT_YVYU:
-       case DRM_FORMAT_UYVY:
-       case DRM_FORMAT_VYUY:
-       case DRM_FORMAT_AYUV:
-       case DRM_FORMAT_NV12:
-       case DRM_FORMAT_NV21:
-       case DRM_FORMAT_NV16:
-       case DRM_FORMAT_NV61:
-       case DRM_FORMAT_NV24:
-       case DRM_FORMAT_NV42:
-       case DRM_FORMAT_YUV410:
-       case DRM_FORMAT_YVU410:
-       case DRM_FORMAT_YUV411:
-       case DRM_FORMAT_YVU411:
-       case DRM_FORMAT_YUV420:
-       case DRM_FORMAT_YVU420:
-       case DRM_FORMAT_YUV422:
-       case DRM_FORMAT_YVU422:
-       case DRM_FORMAT_YUV444:
-       case DRM_FORMAT_YVU444:
-               return 0;
-       default:
-               DRM_DEBUG_KMS("invalid pixel format %s\n",
-                             drm_get_format_name(r->pixel_format));
-               return -EINVAL;
-       }
-}
-
-static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
-{
-       int ret, hsub, vsub, num_planes, i;
-
-       ret = format_check(r);
-       if (ret) {
-               DRM_DEBUG_KMS("bad framebuffer format %s\n",
-                             drm_get_format_name(r->pixel_format));
-               return ret;
-       }
-
-       hsub = drm_format_horz_chroma_subsampling(r->pixel_format);
-       vsub = drm_format_vert_chroma_subsampling(r->pixel_format);
-       num_planes = drm_format_num_planes(r->pixel_format);
-
-       if (r->width == 0 || r->width % hsub) {
-               DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
-               return -EINVAL;
-       }
-
-       if (r->height == 0 || r->height % vsub) {
-               DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
-               return -EINVAL;
-       }
-
-       for (i = 0; i < num_planes; i++) {
-               unsigned int width = r->width / (i != 0 ? hsub : 1);
-               unsigned int height = r->height / (i != 0 ? vsub : 1);
-               unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
-
-               if (!r->handles[i]) {
-                       DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
-                       return -EINVAL;
-               }
-
-               if ((uint64_t) width * cpp > UINT_MAX)
-                       return -ERANGE;
-
-               if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
-                       return -ERANGE;
-
-               if (r->pitches[i] < width * cpp) {
-                       DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
-                       return -EINVAL;
-               }
-
-               if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
-                       DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
-                                     r->modifier[i], i);
-                       return -EINVAL;
-               }
-
-               /* modifier specific checks: */
-               switch (r->modifier[i]) {
-               case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
-                       /* NOTE: the pitch restriction may be lifted later if it turns
-                        * out that no hw has this restriction:
-                        */
-                       if (r->pixel_format != DRM_FORMAT_NV12 ||
-                                       width % 128 || height % 32 ||
-                                       r->pitches[i] % 128) {
-                               DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
-                               return -EINVAL;
-                       }
-                       break;
-
-               default:
-                       break;
-               }
-       }
-
-       for (i = num_planes; i < 4; i++) {
-               if (r->modifier[i]) {
-                       DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
-                       return -EINVAL;
-               }
-
-               /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
-               if (!(r->flags & DRM_MODE_FB_MODIFIERS))
-                       continue;
-
-               if (r->handles[i]) {
-                       DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
-                       return -EINVAL;
-               }
-
-               if (r->pitches[i]) {
-                       DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
-                       return -EINVAL;
-               }
-
-               if (r->offsets[i]) {
-                       DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-
-static struct drm_framebuffer *
-internal_framebuffer_create(struct drm_device *dev,
-                           const struct drm_mode_fb_cmd2 *r,
-                           struct drm_file *file_priv)
-{
-       struct drm_mode_config *config = &dev->mode_config;
-       struct drm_framebuffer *fb;
-       int ret;
-
-       if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
-               DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if ((config->min_width > r->width) || (r->width > config->max_width)) {
-               DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
-                         r->width, config->min_width, config->max_width);
-               return ERR_PTR(-EINVAL);
-       }
-       if ((config->min_height > r->height) || (r->height > config->max_height)) {
-               DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
-                         r->height, config->min_height, config->max_height);
-               return ERR_PTR(-EINVAL);
-       }
-
-       if (r->flags & DRM_MODE_FB_MODIFIERS &&
-           !dev->mode_config.allow_fb_modifiers) {
-               DRM_DEBUG_KMS("driver does not support fb modifiers\n");
-               return ERR_PTR(-EINVAL);
-       }
-
-       ret = framebuffer_check(r);
-       if (ret)
-               return ERR_PTR(ret);
-
-       fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
-       if (IS_ERR(fb)) {
-               DRM_DEBUG_KMS("could not create framebuffer\n");
-               return fb;
-       }
-
-       return fb;
-}
-
-/**
- * drm_mode_addfb2 - add an FB to the graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Add a new FB to the specified CRTC, given a user request with format. This is
- * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
- * and uses fourcc codes as pixel format specifiers.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_addfb2(struct drm_device *dev,
-                   void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_fb_cmd2 *r = data;
-       struct drm_framebuffer *fb;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       fb = internal_framebuffer_create(dev, r, file_priv);
-       if (IS_ERR(fb))
-               return PTR_ERR(fb);
-
-       DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
-       r->fb_id = fb->base.id;
-
-       /* Transfer ownership to the filp for reaping on close */
-       mutex_lock(&file_priv->fbs_lock);
-       list_add(&fb->filp_head, &file_priv->fbs);
-       mutex_unlock(&file_priv->fbs_lock);
-
-       return 0;
-}
-
-struct drm_mode_rmfb_work {
-       struct work_struct work;
-       struct list_head fbs;
-};
-
-static void drm_mode_rmfb_work_fn(struct work_struct *w)
-{
-       struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
-
-       while (!list_empty(&arg->fbs)) {
-               struct drm_framebuffer *fb =
-                       list_first_entry(&arg->fbs, typeof(*fb), filp_head);
-
-               list_del_init(&fb->filp_head);
-               drm_framebuffer_remove(fb);
-       }
-}
-
-/**
- * drm_mode_rmfb - remove an FB from the configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Remove the FB specified by the user.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_rmfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv)
-{
-       struct drm_framebuffer *fb = NULL;
-       struct drm_framebuffer *fbl = NULL;
-       uint32_t *id = data;
-       int found = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       fb = drm_framebuffer_lookup(dev, *id);
-       if (!fb)
-               return -ENOENT;
-
-       mutex_lock(&file_priv->fbs_lock);
-       list_for_each_entry(fbl, &file_priv->fbs, filp_head)
-               if (fb == fbl)
-                       found = 1;
-       if (!found) {
-               mutex_unlock(&file_priv->fbs_lock);
-               goto fail_unref;
-       }
-
-       list_del_init(&fb->filp_head);
-       mutex_unlock(&file_priv->fbs_lock);
-
-       /* drop the reference we picked up in framebuffer lookup */
-       drm_framebuffer_unreference(fb);
-
-       /*
-        * we now own the reference that was stored in the fbs list
-        *
-        * drm_framebuffer_remove may fail with -EINTR on pending signals,
-        * so run this in a separate stack as there's no way to correctly
-        * handle this after the fb is already removed from the lookup table.
-        */
-       if (drm_framebuffer_read_refcount(fb) > 1) {
-               struct drm_mode_rmfb_work arg;
-
-               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
-               INIT_LIST_HEAD(&arg.fbs);
-               list_add_tail(&fb->filp_head, &arg.fbs);
-
-               schedule_work(&arg.work);
-               flush_work(&arg.work);
-               destroy_work_on_stack(&arg.work);
-       } else
-               drm_framebuffer_unreference(fb);
-
-       return 0;
-
-fail_unref:
-       drm_framebuffer_unreference(fb);
-       return -ENOENT;
-}
-
-/**
- * drm_mode_getfb - get FB info
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Lookup the FB given its ID and return info about it.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_fb_cmd *r = data;
-       struct drm_framebuffer *fb;
-       int ret;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       fb = drm_framebuffer_lookup(dev, r->fb_id);
-       if (!fb)
-               return -ENOENT;
-
-       r->height = fb->height;
-       r->width = fb->width;
-       r->depth = fb->depth;
-       r->bpp = fb->bits_per_pixel;
-       r->pitch = fb->pitches[0];
-       if (fb->funcs->create_handle) {
-               if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
-                   drm_is_control_client(file_priv)) {
-                       ret = fb->funcs->create_handle(fb, file_priv,
-                                                      &r->handle);
-               } else {
-                       /* GET_FB() is an unprivileged ioctl so we must not
-                        * return a buffer-handle to non-master processes! For
-                        * backwards-compatibility reasons, we cannot make
-                        * GET_FB() privileged, so just return an invalid handle
-                        * for non-masters. */
-                       r->handle = 0;
-                       ret = 0;
-               }
-       } else {
-               ret = -ENODEV;
-       }
-
-       drm_framebuffer_unreference(fb);
-
-       return ret;
-}
-
-/**
- * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
- *
- * Lookup the FB and flush out the damaged area supplied by userspace as a clip
- * rectangle list. Generic userspace which does frontbuffer rendering must call
- * this ioctl to flush out the changes on manual-update display outputs, e.g.
- * usb display-link, mipi manual update panels or edp panel self refresh modes.
- *
- * Modesetting drivers which always update the frontbuffer do not need to
- * implement the corresponding ->dirty framebuffer callback.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv)
-{
-       struct drm_clip_rect __user *clips_ptr;
-       struct drm_clip_rect *clips = NULL;
-       struct drm_mode_fb_dirty_cmd *r = data;
-       struct drm_framebuffer *fb;
-       unsigned flags;
-       int num_clips;
-       int ret;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       fb = drm_framebuffer_lookup(dev, r->fb_id);
-       if (!fb)
-               return -ENOENT;
-
-       num_clips = r->num_clips;
-       clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
-
-       if (!num_clips != !clips_ptr) {
-               ret = -EINVAL;
-               goto out_err1;
-       }
-
-       flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
-
-       /* If userspace annotates copy, clips must come in pairs */
-       if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
-               ret = -EINVAL;
-               goto out_err1;
-       }
-
-       if (num_clips && clips_ptr) {
-               if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
-                       ret = -EINVAL;
-                       goto out_err1;
-               }
-               clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
-               if (!clips) {
-                       ret = -ENOMEM;
-                       goto out_err1;
-               }
-
-               ret = copy_from_user(clips, clips_ptr,
-                                    num_clips * sizeof(*clips));
-               if (ret) {
-                       ret = -EFAULT;
-                       goto out_err2;
-               }
-       }
-
-       if (fb->funcs->dirty) {
-               ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
-                                      clips, num_clips);
-       } else {
-               ret = -ENOSYS;
-       }
-
-out_err2:
-       kfree(clips);
-out_err1:
-       drm_framebuffer_unreference(fb);
-
-       return ret;
-}
-
-/**
- * drm_fb_release - remove and free the FBs on this file
- * @priv: drm file for the ioctl
- *
- * Destroy all the FBs associated with @filp.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-void drm_fb_release(struct drm_file *priv)
-{
-       struct drm_framebuffer *fb, *tfb;
-       struct drm_mode_rmfb_work arg;
-
-       INIT_LIST_HEAD(&arg.fbs);
-
-       /*
-        * When the file gets released that means no one else can access the fb
-        * list any more, so no need to grab fpriv->fbs_lock. And we need to
-        * avoid upsetting lockdep since the universal cursor code adds a
-        * framebuffer while holding mutex locks.
-        *
-        * Note that a real deadlock between fpriv->fbs_lock and the modeset
-        * locks is impossible here since no one else but this function can get
-        * at it any more.
-        */
-       list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
-               if (drm_framebuffer_read_refcount(fb) > 1) {
-                       list_move_tail(&fb->filp_head, &arg.fbs);
-               } else {
-                       list_del_init(&fb->filp_head);
-
-                       /* This drops the fpriv->fbs reference. */
-                       drm_framebuffer_unreference(fb);
-               }
-       }
-
-       if (!list_empty(&arg.fbs)) {
-               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
-
-               schedule_work(&arg.work);
-               flush_work(&arg.work);
-               destroy_work_on_stack(&arg.work);
-       }
-}
-
-static bool drm_property_type_valid(struct drm_property *property)
-{
-       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
-               return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
-       return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
-}
-
-/**
- * drm_property_create - create a new property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @num_values: number of pre-defined values
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Note that the DRM core keeps a per-device list of properties and that, if
- * drm_mode_config_cleanup() is called, it will destroy all properties created
- * by the driver.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create(struct drm_device *dev, int flags,
-                                        const char *name, int num_values)
-{
-       struct drm_property *property = NULL;
-       int ret;
-
-       property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
-       if (!property)
-               return NULL;
-
-       property->dev = dev;
-
-       if (num_values) {
-               property->values = kcalloc(num_values, sizeof(uint64_t),
-                                          GFP_KERNEL);
-               if (!property->values)
-                       goto fail;
-       }
-
-       ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
-       if (ret)
-               goto fail;
-
-       property->flags = flags;
-       property->num_values = num_values;
-       INIT_LIST_HEAD(&property->enum_list);
-
-       if (name) {
-               strncpy(property->name, name, DRM_PROP_NAME_LEN);
-               property->name[DRM_PROP_NAME_LEN-1] = '\0';
-       }
-
-       list_add_tail(&property->head, &dev->mode_config.property_list);
-
-       WARN_ON(!drm_property_type_valid(property));
-
-       return property;
-fail:
-       kfree(property->values);
-       kfree(property);
-       return NULL;
-}
-EXPORT_SYMBOL(drm_property_create);
-
-/**
- * drm_property_create_enum - create a new enumeration property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @props: enumeration lists with property values
- * @num_values: number of pre-defined values
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Userspace is only allowed to set one of the predefined values for enumeration
- * properties.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
-                                        const char *name,
-                                        const struct drm_prop_enum_list *props,
-                                        int num_values)
-{
-       struct drm_property *property;
-       int i, ret;
-
-       flags |= DRM_MODE_PROP_ENUM;
-
-       property = drm_property_create(dev, flags, name, num_values);
-       if (!property)
-               return NULL;
-
-       for (i = 0; i < num_values; i++) {
-               ret = drm_property_add_enum(property, i,
-                                     props[i].type,
-                                     props[i].name);
-               if (ret) {
-                       drm_property_destroy(dev, property);
-                       return NULL;
-               }
-       }
-
-       return property;
-}
-EXPORT_SYMBOL(drm_property_create_enum);
-
-/**
- * drm_property_create_bitmask - create a new bitmask property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @props: enumeration lists with property bitflags
- * @num_props: size of the @props array
- * @supported_bits: bitmask of all supported enumeration values
- *
- * This creates a new bitmask drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Compared to plain enumeration properties userspace is allowed to set any
- * or'ed together combination of the predefined property bitflag values
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
-                                        int flags, const char *name,
-                                        const struct drm_prop_enum_list *props,
-                                        int num_props,
-                                        uint64_t supported_bits)
-{
-       struct drm_property *property;
-       int i, ret, index = 0;
-       int num_values = hweight64(supported_bits);
-
-       flags |= DRM_MODE_PROP_BITMASK;
-
-       property = drm_property_create(dev, flags, name, num_values);
-       if (!property)
-               return NULL;
-       for (i = 0; i < num_props; i++) {
-               if (!(supported_bits & (1ULL << props[i].type)))
-                       continue;
-
-               if (WARN_ON(index >= num_values)) {
-                       drm_property_destroy(dev, property);
-                       return NULL;
-               }
-
-               ret = drm_property_add_enum(property, index++,
-                                     props[i].type,
-                                     props[i].name);
-               if (ret) {
-                       drm_property_destroy(dev, property);
-                       return NULL;
-               }
-       }
-
-       return property;
-}
-EXPORT_SYMBOL(drm_property_create_bitmask);
-
-static struct drm_property *property_create_range(struct drm_device *dev,
-                                        int flags, const char *name,
-                                        uint64_t min, uint64_t max)
-{
-       struct drm_property *property;
-
-       property = drm_property_create(dev, flags, name, 2);
-       if (!property)
-               return NULL;
-
-       property->values[0] = min;
-       property->values[1] = max;
-
-       return property;
-}
-
-/**
- * drm_property_create_range - create a new unsigned ranged property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @min: minimum value of the property
- * @max: maximum value of the property
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Userspace is allowed to set any unsigned integer value in the (min, max)
- * range inclusive.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
-                                        const char *name,
-                                        uint64_t min, uint64_t max)
-{
-       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
-                       name, min, max);
-}
-EXPORT_SYMBOL(drm_property_create_range);
-
-/**
- * drm_property_create_signed_range - create a new signed ranged property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @min: minimum value of the property
- * @max: maximum value of the property
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Userspace is allowed to set any signed integer value in the (min, max)
- * range inclusive.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
-                                        int flags, const char *name,
-                                        int64_t min, int64_t max)
-{
-       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
-                       name, I642U64(min), I642U64(max));
-}
-EXPORT_SYMBOL(drm_property_create_signed_range);
-
-/**
- * drm_property_create_object - create a new object property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- * @type: object type from DRM_MODE_OBJECT_* defines
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * Userspace is only allowed to set this to any property value of the given
- * @type. Only useful for atomic properties, which is enforced.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_object(struct drm_device *dev,
-                                        int flags, const char *name, uint32_t type)
-{
-       struct drm_property *property;
-
-       flags |= DRM_MODE_PROP_OBJECT;
-
-       if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC)))
-               return NULL;
-
-       property = drm_property_create(dev, flags, name, 1);
-       if (!property)
-               return NULL;
-
-       property->values[0] = type;
-
-       return property;
-}
-EXPORT_SYMBOL(drm_property_create_object);
-
-/**
- * drm_property_create_bool - create a new boolean property type
- * @dev: drm device
- * @flags: flags specifying the property type
- * @name: name of the property
- *
- * This creates a new generic drm property which can then be attached to a drm
- * object with drm_object_attach_property. The returned property object must be
- * freed with drm_property_destroy.
- *
- * This is implemented as a ranged property with only {0, 1} as valid values.
- *
- * Returns:
- * A pointer to the newly created property on success, NULL on failure.
- */
-struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
-                                        const char *name)
-{
-       return drm_property_create_range(dev, flags, name, 0, 1);
-}
-EXPORT_SYMBOL(drm_property_create_bool);
-
-/**
- * drm_property_add_enum - add a possible value to an enumeration property
- * @property: enumeration property to change
- * @index: index of the new enumeration
- * @value: value of the new enumeration
- * @name: symbolic name of the new enumeration
- *
- * This functions adds enumerations to a property.
- *
- * It's use is deprecated, drivers should use one of the more specific helpers
- * to directly create the property with all enumerations already attached.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_property_add_enum(struct drm_property *property, int index,
-                         uint64_t value, const char *name)
-{
-       struct drm_property_enum *prop_enum;
-
-       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
-                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
-               return -EINVAL;
-
-       /*
-        * Bitmask enum properties have the additional constraint of values
-        * from 0 to 63
-        */
-       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
-                       (value > 63))
-               return -EINVAL;
-
-       if (!list_empty(&property->enum_list)) {
-               list_for_each_entry(prop_enum, &property->enum_list, head) {
-                       if (prop_enum->value == value) {
-                               strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
-                               prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
-                               return 0;
-                       }
-               }
-       }
-
-       prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
-       if (!prop_enum)
-               return -ENOMEM;
-
-       strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
-       prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
-       prop_enum->value = value;
-
-       property->values[index] = value;
-       list_add_tail(&prop_enum->head, &property->enum_list);
-       return 0;
-}
-EXPORT_SYMBOL(drm_property_add_enum);
-
-/**
- * drm_property_destroy - destroy a drm property
- * @dev: drm device
- * @property: property to destry
- *
- * This function frees a property including any attached resources like
- * enumeration values.
- */
-void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
-{
-       struct drm_property_enum *prop_enum, *pt;
-
-       list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) {
-               list_del(&prop_enum->head);
-               kfree(prop_enum);
-       }
-
-       if (property->num_values)
-               kfree(property->values);
-       drm_mode_object_unregister(dev, &property->base);
-       list_del(&property->head);
-       kfree(property);
-}
-EXPORT_SYMBOL(drm_property_destroy);
-
-/**
- * drm_object_attach_property - attach a property to a modeset object
- * @obj: drm modeset object
- * @property: property to attach
- * @init_val: initial value of the property
- *
- * This attaches the given property to the modeset object with the given initial
- * value. Currently this function cannot fail since the properties are stored in
- * a statically sized array.
- */
-void drm_object_attach_property(struct drm_mode_object *obj,
-                               struct drm_property *property,
-                               uint64_t init_val)
-{
-       int count = obj->properties->count;
-
-       if (count == DRM_OBJECT_MAX_PROPERTY) {
-               WARN(1, "Failed to attach object property (type: 0x%x). Please "
-                       "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
-                       "you see this message on the same object type.\n",
-                       obj->type);
-               return;
-       }
-
-       obj->properties->properties[count] = property;
-       obj->properties->values[count] = init_val;
-       obj->properties->count++;
-       if (property->flags & DRM_MODE_PROP_ATOMIC)
-               obj->properties->atomic_count++;
-}
-EXPORT_SYMBOL(drm_object_attach_property);
-
-/**
- * drm_object_property_set_value - set the value of a property
- * @obj: drm mode object to set property value for
- * @property: property to set
- * @val: value the property should be set to
- *
- * This functions sets a given property on a given object. This function only
- * changes the software state of the property, it does not call into the
- * driver's ->set_property callback.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_object_property_set_value(struct drm_mode_object *obj,
-                                 struct drm_property *property, uint64_t val)
-{
-       int i;
-
-       for (i = 0; i < obj->properties->count; i++) {
-               if (obj->properties->properties[i] == property) {
-                       obj->properties->values[i] = val;
-                       return 0;
-               }
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL(drm_object_property_set_value);
-
-/**
- * drm_object_property_get_value - retrieve the value of a property
- * @obj: drm mode object to get property value from
- * @property: property to retrieve
- * @val: storage for the property value
- *
- * This function retrieves the softare state of the given property for the given
- * property. Since there is no driver callback to retrieve the current property
- * value this might be out of sync with the hardware, depending upon the driver
- * and property.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_object_property_get_value(struct drm_mode_object *obj,
-                                 struct drm_property *property, uint64_t *val)
-{
-       int i;
-
-       /* read-only properties bypass atomic mechanism and still store
-        * their value in obj->properties->values[].. mostly to avoid
-        * having to deal w/ EDID and similar props in atomic paths:
-        */
-       if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) &&
-                       !(property->flags & DRM_MODE_PROP_IMMUTABLE))
-               return drm_atomic_get_property(obj, property, val);
-
-       for (i = 0; i < obj->properties->count; i++) {
-               if (obj->properties->properties[i] == property) {
-                       *val = obj->properties->values[i];
-                       return 0;
-               }
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL(drm_object_property_get_value);
-
-/**
- * drm_mode_getproperty_ioctl - get the property metadata
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * This function retrieves the metadata for a given property, like the different
- * possible values for an enum property or the limits for a range property.
- *
- * Blob properties are special
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getproperty_ioctl(struct drm_device *dev,
-                              void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_get_property *out_resp = data;
-       struct drm_property *property;
-       int enum_count = 0;
-       int value_count = 0;
-       int ret = 0, i;
-       int copied;
-       struct drm_property_enum *prop_enum;
-       struct drm_mode_property_enum __user *enum_ptr;
-       uint64_t __user *values_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       drm_modeset_lock_all(dev);
-       property = drm_property_find(dev, out_resp->prop_id);
-       if (!property) {
-               ret = -ENOENT;
-               goto done;
-       }
-
-       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
-                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
-               list_for_each_entry(prop_enum, &property->enum_list, head)
-                       enum_count++;
-       }
-
-       value_count = property->num_values;
-
-       strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
-       out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
-       out_resp->flags = property->flags;
-
-       if ((out_resp->count_values >= value_count) && value_count) {
-               values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
-               for (i = 0; i < value_count; i++) {
-                       if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
-                               ret = -EFAULT;
-                               goto done;
-                       }
-               }
-       }
-       out_resp->count_values = value_count;
-
-       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
-                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
-               if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
-                       copied = 0;
-                       enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
-                       list_for_each_entry(prop_enum, &property->enum_list, head) {
-
-                               if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
-                                       ret = -EFAULT;
-                                       goto done;
-                               }
-
-                               if (copy_to_user(&enum_ptr[copied].name,
-                                                &prop_enum->name, DRM_PROP_NAME_LEN)) {
-                                       ret = -EFAULT;
-                                       goto done;
-                               }
-                               copied++;
-                       }
-               }
-               out_resp->count_enum_blobs = enum_count;
-       }
-
-       /*
-        * NOTE: The idea seems to have been to use this to read all the blob
-        * property values. But nothing ever added them to the corresponding
-        * list, userspace always used the special-purpose get_blob ioctl to
-        * read the value for a blob property. It also doesn't make a lot of
-        * sense to return values here when everything else is just metadata for
-        * the property itself.
-        */
-       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
-               out_resp->count_enum_blobs = 0;
-done:
-       drm_modeset_unlock_all(dev);
-       return ret;
-}
-
-static void drm_property_free_blob(struct kref *kref)
-{
-       struct drm_property_blob *blob =
-               container_of(kref, struct drm_property_blob, base.refcount);
-
-       mutex_lock(&blob->dev->mode_config.blob_lock);
-       list_del(&blob->head_global);
-       mutex_unlock(&blob->dev->mode_config.blob_lock);
-
-       drm_mode_object_unregister(blob->dev, &blob->base);
-
-       kfree(blob);
-}
-
-/**
- * drm_property_create_blob - Create new blob property
- *
- * Creates a new blob property for a specified DRM device, optionally
- * copying data.
- *
- * @dev: DRM device to create property for
- * @length: Length to allocate for blob data
- * @data: If specified, copies data into blob
- *
- * Returns:
- * New blob property with a single reference on success, or an ERR_PTR
- * value on failure.
- */
-struct drm_property_blob *
-drm_property_create_blob(struct drm_device *dev, size_t length,
-                        const void *data)
-{
-       struct drm_property_blob *blob;
-       int ret;
-
-       if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
-               return ERR_PTR(-EINVAL);
-
-       blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
-       if (!blob)
-               return ERR_PTR(-ENOMEM);
-
-       /* This must be explicitly initialised, so we can safely call list_del
-        * on it in the removal handler, even if it isn't in a file list. */
-       INIT_LIST_HEAD(&blob->head_file);
-       blob->length = length;
-       blob->dev = dev;
-
-       if (data)
-               memcpy(blob->data, data, length);
-
-       ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
-                                     true, drm_property_free_blob);
-       if (ret) {
-               kfree(blob);
-               return ERR_PTR(-EINVAL);
-       }
-
-       mutex_lock(&dev->mode_config.blob_lock);
-       list_add_tail(&blob->head_global,
-                     &dev->mode_config.property_blob_list);
-       mutex_unlock(&dev->mode_config.blob_lock);
-
-       return blob;
-}
-EXPORT_SYMBOL(drm_property_create_blob);
-
-/**
- * drm_property_unreference_blob - Unreference a blob property
- *
- * Drop a reference on a blob property. May free the object.
- *
- * @blob: Pointer to blob property
- */
-void drm_property_unreference_blob(struct drm_property_blob *blob)
-{
-       if (!blob)
-               return;
-
-       drm_mode_object_unreference(&blob->base);
-}
-EXPORT_SYMBOL(drm_property_unreference_blob);
-
-/**
- * drm_property_destroy_user_blobs - destroy all blobs created by this client
- * @dev:       DRM device
- * @file_priv: destroy all blobs owned by this file handle
- */
-void drm_property_destroy_user_blobs(struct drm_device *dev,
-                                    struct drm_file *file_priv)
-{
-       struct drm_property_blob *blob, *bt;
-
-       /*
-        * When the file gets released that means no one else can access the
-        * blob list any more, so no need to grab dev->blob_lock.
-        */
-       list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) {
-               list_del_init(&blob->head_file);
-               drm_property_unreference_blob(blob);
-       }
-}
-
-/**
- * drm_property_reference_blob - Take a reference on an existing property
- *
- * Take a new reference on an existing blob property.
- *
- * @blob: Pointer to blob property
- */
-struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob)
-{
-       drm_mode_object_reference(&blob->base);
-       return blob;
-}
-EXPORT_SYMBOL(drm_property_reference_blob);
-
-/**
- * drm_property_lookup_blob - look up a blob property and take a reference
- * @dev: drm device
- * @id: id of the blob property
- *
- * If successful, this takes an additional reference to the blob property.
- * callers need to make sure to eventually unreference the returned property
- * again, using @drm_property_unreference_blob.
- */
-struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
-                                                  uint32_t id)
-{
-       struct drm_mode_object *obj;
-       struct drm_property_blob *blob = NULL;
-
-       obj = _object_find(dev, id, DRM_MODE_OBJECT_BLOB);
-       if (obj)
-               blob = obj_to_blob(obj);
-       return blob;
-}
-EXPORT_SYMBOL(drm_property_lookup_blob);
-
-/**
- * drm_property_replace_global_blob - atomically replace existing blob property
- * @dev: drm device
- * @replace: location of blob property pointer to be replaced
- * @length: length of data for new blob, or 0 for no data
- * @data: content for new blob, or NULL for no data
- * @obj_holds_id: optional object for property holding blob ID
- * @prop_holds_id: optional property holding blob ID
- * @return 0 on success or error on failure
- *
- * This function will atomically replace a global property in the blob list,
- * optionally updating a property which holds the ID of that property. It is
- * guaranteed to be atomic: no caller will be allowed to see intermediate
- * results, and either the entire operation will succeed and clean up the
- * previous property, or it will fail and the state will be unchanged.
- *
- * If length is 0 or data is NULL, no new blob will be created, and the holding
- * property, if specified, will be set to 0.
- *
- * Access to the replace pointer is assumed to be protected by the caller, e.g.
- * by holding the relevant modesetting object lock for its parent.
- *
- * For example, a drm_connector has a 'PATH' property, which contains the ID
- * of a blob property with the value of the MST path information. Calling this
- * function with replace pointing to the connector's path_blob_ptr, length and
- * data set for the new path information, obj_holds_id set to the connector's
- * base object, and prop_holds_id set to the path property name, will perform
- * a completely atomic update. The access to path_blob_ptr is protected by the
- * caller holding a lock on the connector.
- */
-static int drm_property_replace_global_blob(struct drm_device *dev,
-                                            struct drm_property_blob **replace,
-                                            size_t length,
-                                            const void *data,
-                                            struct drm_mode_object *obj_holds_id,
-                                            struct drm_property *prop_holds_id)
-{
-       struct drm_property_blob *new_blob = NULL;
-       struct drm_property_blob *old_blob = NULL;
-       int ret;
-
-       WARN_ON(replace == NULL);
-
-       old_blob = *replace;
-
-       if (length && data) {
-               new_blob = drm_property_create_blob(dev, length, data);
-               if (IS_ERR(new_blob))
-                       return PTR_ERR(new_blob);
-       }
-
-       /* This does not need to be synchronised with blob_lock, as the
-        * get_properties ioctl locks all modesetting objects, and
-        * obj_holds_id must be locked before calling here, so we cannot
-        * have its value out of sync with the list membership modified
-        * below under blob_lock. */
-       if (obj_holds_id) {
-               ret = drm_object_property_set_value(obj_holds_id,
-                                                   prop_holds_id,
-                                                   new_blob ?
-                                                       new_blob->base.id : 0);
-               if (ret != 0)
-                       goto err_created;
-       }
-
-       drm_property_unreference_blob(old_blob);
-       *replace = new_blob;
-
-       return 0;
-
-err_created:
-       drm_property_unreference_blob(new_blob);
-       return ret;
-}
-
-/**
- * drm_mode_getblob_ioctl - get the contents of a blob property value
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * This function retrieves the contents of a blob property. The value stored in
- * an object's blob property is just a normal modeset object id.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_getblob_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_get_blob *out_resp = data;
-       struct drm_property_blob *blob;
-       int ret = 0;
-       void __user *blob_ptr;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       blob = drm_property_lookup_blob(dev, out_resp->blob_id);
-       if (!blob)
-               return -ENOENT;
-
-       if (out_resp->length == blob->length) {
-               blob_ptr = (void __user *)(unsigned long)out_resp->data;
-               if (copy_to_user(blob_ptr, blob->data, blob->length)) {
-                       ret = -EFAULT;
-                       goto unref;
-               }
-       }
-       out_resp->length = blob->length;
-unref:
-       drm_property_unreference_blob(blob);
-
-       return ret;
-}
-
-/**
- * drm_mode_createblob_ioctl - create a new blob property
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * This function creates a new blob property with user-defined values. In order
- * to give us sensible validation and checking when creating, rather than at
- * every potential use, we also require a type to be provided upfront.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_createblob_ioctl(struct drm_device *dev,
-                             void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_create_blob *out_resp = data;
-       struct drm_property_blob *blob;
-       void __user *blob_ptr;
-       int ret = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       blob = drm_property_create_blob(dev, out_resp->length, NULL);
-       if (IS_ERR(blob))
-               return PTR_ERR(blob);
-
-       blob_ptr = (void __user *)(unsigned long)out_resp->data;
-       if (copy_from_user(blob->data, blob_ptr, out_resp->length)) {
-               ret = -EFAULT;
-               goto out_blob;
-       }
-
-       /* Dropping the lock between create_blob and our access here is safe
-        * as only the same file_priv can remove the blob; at this point, it is
-        * not associated with any file_priv. */
-       mutex_lock(&dev->mode_config.blob_lock);
-       out_resp->blob_id = blob->base.id;
-       list_add_tail(&blob->head_file, &file_priv->blobs);
-       mutex_unlock(&dev->mode_config.blob_lock);
-
-       return 0;
-
-out_blob:
-       drm_property_unreference_blob(blob);
-       return ret;
-}
-
-/**
- * drm_mode_destroyblob_ioctl - destroy a user blob property
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Destroy an existing user-defined blob property.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_destroyblob_ioctl(struct drm_device *dev,
-                              void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_destroy_blob *out_resp = data;
-       struct drm_property_blob *blob = NULL, *bt;
-       bool found = false;
-       int ret = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       blob = drm_property_lookup_blob(dev, out_resp->blob_id);
-       if (!blob)
-               return -ENOENT;
-
-       mutex_lock(&dev->mode_config.blob_lock);
-       /* Ensure the property was actually created by this user. */
-       list_for_each_entry(bt, &file_priv->blobs, head_file) {
-               if (bt == blob) {
-                       found = true;
-                       break;
-               }
-       }
-
-       if (!found) {
-               ret = -EPERM;
-               goto err;
-       }
-
-       /* We must drop head_file here, because we may not be the last
-        * reference on the blob. */
-       list_del_init(&blob->head_file);
-       mutex_unlock(&dev->mode_config.blob_lock);
-
-       /* One reference from lookup, and one from the filp. */
-       drm_property_unreference_blob(blob);
-       drm_property_unreference_blob(blob);
-
-       return 0;
-
-err:
-       mutex_unlock(&dev->mode_config.blob_lock);
-       drm_property_unreference_blob(blob);
-
-       return ret;
-}
-
-/**
- * drm_mode_connector_set_path_property - set tile property on connector
- * @connector: connector to set property on.
- * @path: path to use for property; must not be NULL.
- *
- * This creates a property to expose to userspace to specify a
- * connector path. This is mainly used for DisplayPort MST where
- * connectors have a topology and we want to allow userspace to give
- * them more meaningful names.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_connector_set_path_property(struct drm_connector *connector,
-                                        const char *path)
-{
-       struct drm_device *dev = connector->dev;
-       int ret;
-
-       ret = drm_property_replace_global_blob(dev,
-                                              &connector->path_blob_ptr,
-                                              strlen(path) + 1,
-                                              path,
-                                              &connector->base,
-                                              dev->mode_config.path_property);
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_connector_set_path_property);
-
-/**
- * drm_mode_connector_set_tile_property - set tile property on connector
- * @connector: connector to set property on.
- *
- * This looks up the tile information for a connector, and creates a
- * property for userspace to parse if it exists. The property is of
- * the form of 8 integers using ':' as a separator.
- *
- * Returns:
- * Zero on success, errno on failure.
- */
-int drm_mode_connector_set_tile_property(struct drm_connector *connector)
-{
-       struct drm_device *dev = connector->dev;
-       char tile[256];
-       int ret;
-
-       if (!connector->has_tile) {
-               ret  = drm_property_replace_global_blob(dev,
-                                                       &connector->tile_blob_ptr,
-                                                       0,
-                                                       NULL,
-                                                       &connector->base,
-                                                       dev->mode_config.tile_property);
-               return ret;
-       }
-
-       snprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d",
-                connector->tile_group->id, connector->tile_is_single_monitor,
-                connector->num_h_tile, connector->num_v_tile,
-                connector->tile_h_loc, connector->tile_v_loc,
-                connector->tile_h_size, connector->tile_v_size);
-
-       ret = drm_property_replace_global_blob(dev,
-                                              &connector->tile_blob_ptr,
-                                              strlen(tile) + 1,
-                                              tile,
-                                              &connector->base,
-                                              dev->mode_config.tile_property);
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_connector_set_tile_property);
-
-/**
- * drm_mode_connector_update_edid_property - update the edid property of a connector
- * @connector: drm connector
- * @edid: new value of the edid property
- *
- * This function creates a new blob modeset object and assigns its id to the
- * connector's edid property.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_connector_update_edid_property(struct drm_connector *connector,
-                                           const struct edid *edid)
-{
-       struct drm_device *dev = connector->dev;
-       size_t size = 0;
-       int ret;
-
-       /* ignore requests to set edid when overridden */
-       if (connector->override_edid)
-               return 0;
-
-       if (edid)
-               size = EDID_LENGTH * (1 + edid->extensions);
-
-       ret = drm_property_replace_global_blob(dev,
-                                              &connector->edid_blob_ptr,
-                                              size,
-                                              edid,
-                                              &connector->base,
-                                              dev->mode_config.edid_property);
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
-
-/* Some properties could refer to dynamic refcnt'd objects, or things that
- * need special locking to handle lifetime issues (ie. to ensure the prop
- * value doesn't become invalid part way through the property update due to
- * race).  The value returned by reference via 'obj' should be passed back
- * to drm_property_change_valid_put() after the property is set (and the
- * object to which the property is attached has a chance to take it's own
- * reference).
- */
-bool drm_property_change_valid_get(struct drm_property *property,
-                                        uint64_t value, struct drm_mode_object **ref)
-{
-       int i;
-
-       if (property->flags & DRM_MODE_PROP_IMMUTABLE)
-               return false;
-
-       *ref = NULL;
-
-       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
-               if (value < property->values[0] || value > property->values[1])
-                       return false;
-               return true;
-       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
-               int64_t svalue = U642I64(value);
-
-               if (svalue < U642I64(property->values[0]) ||
-                               svalue > U642I64(property->values[1]))
-                       return false;
-               return true;
-       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
-               uint64_t valid_mask = 0;
-
-               for (i = 0; i < property->num_values; i++)
-                       valid_mask |= (1ULL << property->values[i]);
-               return !(value & ~valid_mask);
-       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
-               struct drm_property_blob *blob;
-
-               if (value == 0)
-                       return true;
-
-               blob = drm_property_lookup_blob(property->dev, value);
-               if (blob) {
-                       *ref = &blob->base;
-                       return true;
-               } else {
-                       return false;
-               }
-       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
-               /* a zero value for an object property translates to null: */
-               if (value == 0)
-                       return true;
-
-               *ref = _object_find(property->dev, value, property->values[0]);
-               return *ref != NULL;
-       }
-
-       for (i = 0; i < property->num_values; i++)
-               if (property->values[i] == value)
-                       return true;
-       return false;
-}
-
-void drm_property_change_valid_put(struct drm_property *property,
-               struct drm_mode_object *ref)
-{
-       if (!ref)
-               return;
-
-       if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
-               drm_mode_object_unreference(ref);
-       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
-               drm_property_unreference_blob(obj_to_blob(ref));
-}
-
-/**
- * drm_mode_connector_property_set_ioctl - set the current value of a connector property
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * This function sets the current value for a connectors's property. It also
- * calls into a driver's ->set_property callback to update the hardware state
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
-                                      void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_connector_set_property *conn_set_prop = data;
-       struct drm_mode_obj_set_property obj_set_prop = {
-               .value = conn_set_prop->value,
-               .prop_id = conn_set_prop->prop_id,
-               .obj_id = conn_set_prop->connector_id,
-               .obj_type = DRM_MODE_OBJECT_CONNECTOR
-       };
-
-       /* It does all the locking and checking we need */
-       return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv);
-}
-
-static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
-                                          struct drm_property *property,
-                                          uint64_t value)
-{
-       int ret = -EINVAL;
-       struct drm_connector *connector = obj_to_connector(obj);
-
-       /* Do DPMS ourselves */
-       if (property == connector->dev->mode_config.dpms_property) {
-               ret = (*connector->funcs->dpms)(connector, (int)value);
-       } else if (connector->funcs->set_property)
-               ret = connector->funcs->set_property(connector, property, value);
-
-       /* store the property value if successful */
-       if (!ret)
-               drm_object_property_set_value(&connector->base, property, value);
-       return ret;
-}
-
-static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
-                                     struct drm_property *property,
-                                     uint64_t value)
-{
-       int ret = -EINVAL;
-       struct drm_crtc *crtc = obj_to_crtc(obj);
-
-       if (crtc->funcs->set_property)
-               ret = crtc->funcs->set_property(crtc, property, value);
-       if (!ret)
-               drm_object_property_set_value(obj, property, value);
-
-       return ret;
-}
-
-/**
- * drm_mode_plane_set_obj_prop - set the value of a property
- * @plane: drm plane object to set property value for
- * @property: property to set
- * @value: value the property should be set to
- *
- * This functions sets a given property on a given plane object. This function
- * calls the driver's ->set_property callback and changes the software state of
- * the property if the callback succeeds.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
-                               struct drm_property *property,
-                               uint64_t value)
-{
-       int ret = -EINVAL;
-       struct drm_mode_object *obj = &plane->base;
-
-       if (plane->funcs->set_property)
-               ret = plane->funcs->set_property(plane, property, value);
-       if (!ret)
-               drm_object_property_set_value(obj, property, value);
-
-       return ret;
-}
-EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
-
-/**
- * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
+ * drm_mode_getresources - get graphics configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
- * This function retrieves the current value for an object's property. Compared
- * to the connector specific ioctl this one is extended to also work on crtc and
- * plane objects.
+ * Construct a set of configuration description structures and return
+ * them to the user, including CRTC, connector and framebuffer configuration.
  *
  * Called by the user via ioctl.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
-                                     struct drm_file *file_priv)
+int drm_mode_getresources(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
 {
-       struct drm_mode_obj_get_properties *arg = data;
-       struct drm_mode_object *obj;
+       struct drm_mode_card_res *card_res = data;
+       struct list_head *lh;
+       struct drm_framebuffer *fb;
+       struct drm_connector *connector;
+       struct drm_crtc *crtc;
+       struct drm_encoder *encoder;
        int ret = 0;
+       int connector_count = 0;
+       int crtc_count = 0;
+       int fb_count = 0;
+       int encoder_count = 0;
+       int copied = 0;
+       uint32_t __user *fb_id;
+       uint32_t __user *crtc_id;
+       uint32_t __user *connector_id;
+       uint32_t __user *encoder_id;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       drm_modeset_lock_all(dev);
 
-       obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
-       if (!obj) {
-               ret = -ENOENT;
-               goto out;
+       mutex_lock(&file_priv->fbs_lock);
+       /*
+        * For the non-control nodes we need to limit the list of resources
+        * by IDs in the group list for this node
+        */
+       list_for_each(lh, &file_priv->fbs)
+               fb_count++;
+
+       /* handle this in 4 parts */
+       /* FBs */
+       if (card_res->count_fbs >= fb_count) {
+               copied = 0;
+               fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
+               list_for_each_entry(fb, &file_priv->fbs, filp_head) {
+                       if (put_user(fb->base.id, fb_id + copied)) {
+                               mutex_unlock(&file_priv->fbs_lock);
+                               return -EFAULT;
+                       }
+                       copied++;
+               }
        }
-       if (!obj->properties) {
-               ret = -EINVAL;
-               goto out_unref;
+       card_res->count_fbs = fb_count;
+       mutex_unlock(&file_priv->fbs_lock);
+
+       /* mode_config.mutex protects the connector list against e.g. DP MST
+        * connector hot-adding. CRTC/Plane lists are invariant. */
+       mutex_lock(&dev->mode_config.mutex);
+       drm_for_each_crtc(crtc, dev)
+               crtc_count++;
+
+       drm_for_each_connector(connector, dev)
+               connector_count++;
+
+       drm_for_each_encoder(encoder, dev)
+               encoder_count++;
+
+       card_res->max_height = dev->mode_config.max_height;
+       card_res->min_height = dev->mode_config.min_height;
+       card_res->max_width = dev->mode_config.max_width;
+       card_res->min_width = dev->mode_config.min_width;
+
+       /* CRTCs */
+       if (card_res->count_crtcs >= crtc_count) {
+               copied = 0;
+               crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
+               drm_for_each_crtc(crtc, dev) {
+                       if (put_user(crtc->base.id, crtc_id + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
+       }
+       card_res->count_crtcs = crtc_count;
+
+       /* Encoders */
+       if (card_res->count_encoders >= encoder_count) {
+               copied = 0;
+               encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
+               drm_for_each_encoder(encoder, dev) {
+                       if (put_user(encoder->base.id, encoder_id +
+                                    copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
        }
+       card_res->count_encoders = encoder_count;
 
-       ret = get_properties(obj, file_priv->atomic,
-                       (uint32_t __user *)(unsigned long)(arg->props_ptr),
-                       (uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
-                       &arg->count_props);
+       /* Connectors */
+       if (card_res->count_connectors >= connector_count) {
+               copied = 0;
+               connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
+               drm_for_each_connector(connector, dev) {
+                       if (put_user(connector->base.id,
+                                    connector_id + copied)) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+                       copied++;
+               }
+       }
+       card_res->count_connectors = connector_count;
 
-out_unref:
-       drm_mode_object_unreference(obj);
 out:
-       drm_modeset_unlock_all(dev);
+       mutex_unlock(&dev->mode_config.mutex);
        return ret;
 }
 
 /**
- * drm_mode_obj_set_property_ioctl - set the current value of an object's property
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
+ * drm_mode_getcrtc - get CRTC configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
- * This function sets the current value for an object's property. It also calls
- * into a driver's ->set_property callback to update the hardware state.
- * Compared to the connector specific ioctl this one is extended to also work on
- * crtc and plane objects.
+ * Construct a CRTC configuration structure to return to the user.
  *
  * Called by the user via ioctl.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
-                                   struct drm_file *file_priv)
+int drm_mode_getcrtc(struct drm_device *dev,
+                    void *data, struct drm_file *file_priv)
 {
-       struct drm_mode_obj_set_property *arg = data;
-       struct drm_mode_object *arg_obj;
-       struct drm_mode_object *prop_obj;
-       struct drm_property *property;
-       int i, ret = -EINVAL;
-       struct drm_mode_object *ref;
+       struct drm_mode_crtc *crtc_resp = data;
+       struct drm_crtc *crtc;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
-       drm_modeset_lock_all(dev);
+       crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
+       if (!crtc)
+               return -ENOENT;
 
-       arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
-       if (!arg_obj) {
-               ret = -ENOENT;
-               goto out;
-       }
-       if (!arg_obj->properties)
-               goto out_unref;
+       drm_modeset_lock_crtc(crtc, crtc->primary);
+       crtc_resp->gamma_size = crtc->gamma_size;
+       if (crtc->primary->fb)
+               crtc_resp->fb_id = crtc->primary->fb->base.id;
+       else
+               crtc_resp->fb_id = 0;
 
-       for (i = 0; i < arg_obj->properties->count; i++)
-               if (arg_obj->properties->properties[i]->base.id == arg->prop_id)
-                       break;
+       if (crtc->state) {
+               crtc_resp->x = crtc->primary->state->src_x >> 16;
+               crtc_resp->y = crtc->primary->state->src_y >> 16;
+               if (crtc->state->enable) {
+                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
+                       crtc_resp->mode_valid = 1;
 
-       if (i == arg_obj->properties->count)
-               goto out_unref;
+               } else {
+                       crtc_resp->mode_valid = 0;
+               }
+       } else {
+               crtc_resp->x = crtc->x;
+               crtc_resp->y = crtc->y;
+               if (crtc->enabled) {
+                       drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
+                       crtc_resp->mode_valid = 1;
 
-       prop_obj = drm_mode_object_find(dev, arg->prop_id,
-                                       DRM_MODE_OBJECT_PROPERTY);
-       if (!prop_obj) {
-               ret = -ENOENT;
-               goto out_unref;
-       }
-       property = obj_to_property(prop_obj);
-
-       if (!drm_property_change_valid_get(property, arg->value, &ref))
-               goto out_unref;
-
-       switch (arg_obj->type) {
-       case DRM_MODE_OBJECT_CONNECTOR:
-               ret = drm_mode_connector_set_obj_prop(arg_obj, property,
-                                                     arg->value);
-               break;
-       case DRM_MODE_OBJECT_CRTC:
-               ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
-               break;
-       case DRM_MODE_OBJECT_PLANE:
-               ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj),
-                                                 property, arg->value);
-               break;
+               } else {
+                       crtc_resp->mode_valid = 0;
+               }
        }
+       drm_modeset_unlock_crtc(crtc);
 
-       drm_property_change_valid_put(property, ref);
-
-out_unref:
-       drm_mode_object_unreference(arg_obj);
-out:
-       drm_modeset_unlock_all(dev);
-       return ret;
+       return 0;
 }
 
 /**
- * drm_mode_connector_attach_encoder - attach a connector to an encoder
- * @connector: connector to attach
- * @encoder: encoder to attach @connector to
+ * drm_mode_set_config_internal - helper to call ->set_config
+ * @set: modeset config to set
  *
- * This function links up a connector to an encoder. Note that the routing
- * restrictions between encoders and crtcs are exposed to userspace through the
- * possible_clones and possible_crtcs bitmasks.
+ * This is a little helper to wrap internal calls to the ->set_config driver
+ * interface. The only thing it adds is correct refcounting dance.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int drm_mode_connector_attach_encoder(struct drm_connector *connector,
-                                     struct drm_encoder *encoder)
+int drm_mode_set_config_internal(struct drm_mode_set *set)
 {
-       int i;
+       struct drm_crtc *crtc = set->crtc;
+       struct drm_framebuffer *fb;
+       struct drm_crtc *tmp;
+       int ret;
 
        /*
-        * In the past, drivers have attempted to model the static association
-        * of connector to encoder in simple connector/encoder devices using a
-        * direct assignment of connector->encoder = encoder. This connection
-        * is a logical one and the responsibility of the core, so drivers are
-        * expected not to mess with this.
-        *
-        * Note that the error return should've been enough here, but a large
-        * majority of drivers ignores the return value, so add in a big WARN
-        * to get people's attention.
+        * NOTE: ->set_config can also disable other crtcs (if we steal all
+        * connectors from it), hence we need to refcount the fbs across all
+        * crtcs. Atomic modeset will have saner semantics ...
         */
-       if (WARN_ON(connector->encoder))
-               return -EINVAL;
-
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-               if (connector->encoder_ids[i] == 0) {
-                       connector->encoder_ids[i] = encoder->base.id;
-                       return 0;
-               }
-       }
-       return -ENOMEM;
-}
-EXPORT_SYMBOL(drm_mode_connector_attach_encoder);
-
-/**
- * drm_mode_crtc_set_gamma_size - set the gamma table size
- * @crtc: CRTC to set the gamma table size for
- * @gamma_size: size of the gamma table
- *
- * Drivers which support gamma tables should set this to the supported gamma
- * table size when initializing the CRTC. Currently the drm core only supports a
- * fixed gamma table size.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
-                                int gamma_size)
-{
-       uint16_t *r_base, *g_base, *b_base;
-       int i;
+       drm_for_each_crtc(tmp, crtc->dev)
+               tmp->primary->old_fb = tmp->primary->fb;
 
-       crtc->gamma_size = gamma_size;
+       fb = set->fb;
 
-       crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3,
-                                   GFP_KERNEL);
-       if (!crtc->gamma_store) {
-               crtc->gamma_size = 0;
-               return -ENOMEM;
+       ret = crtc->funcs->set_config(set);
+       if (ret == 0) {
+               crtc->primary->crtc = crtc;
+               crtc->primary->fb = fb;
        }
 
-       r_base = crtc->gamma_store;
-       g_base = r_base + gamma_size;
-       b_base = g_base + gamma_size;
-       for (i = 0; i < gamma_size; i++) {
-               r_base[i] = i << 8;
-               g_base[i] = i << 8;
-               b_base[i] = i << 8;
+       drm_for_each_crtc(tmp, crtc->dev) {
+               if (tmp->primary->fb)
+                       drm_framebuffer_reference(tmp->primary->fb);
+               if (tmp->primary->old_fb)
+                       drm_framebuffer_unreference(tmp->primary->old_fb);
+               tmp->primary->old_fb = NULL;
        }
 
-
-       return 0;
+       return ret;
 }
-EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
+EXPORT_SYMBOL(drm_mode_set_config_internal);
 
 /**
- * drm_mode_gamma_set_ioctl - set the gamma table
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * Set the gamma table of a CRTC to the one passed in by the user. Userspace can
- * inquire the required gamma table size through drm_mode_gamma_get_ioctl.
- *
- * Called by the user via ioctl.
+ * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode
+ * @mode: mode to query
+ * @hdisplay: hdisplay value to fill in
+ * @vdisplay: vdisplay value to fill in
  *
- * Returns:
- * Zero on success, negative errno on failure.
+ * The vdisplay value will be doubled if the specified mode is a stereo mode of
+ * the appropriate layout.
  */
-int drm_mode_gamma_set_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv)
+void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
+                           int *hdisplay, int *vdisplay)
 {
-       struct drm_mode_crtc_lut *crtc_lut = data;
-       struct drm_crtc *crtc;
-       void *r_base, *g_base, *b_base;
-       int size;
-       int ret = 0;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       drm_modeset_lock_all(dev);
-       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
-       if (!crtc) {
-               ret = -ENOENT;
-               goto out;
-       }
-
-       if (crtc->funcs->gamma_set == NULL) {
-               ret = -ENOSYS;
-               goto out;
-       }
-
-       /* memcpy into gamma store */
-       if (crtc_lut->gamma_size != crtc->gamma_size) {
-               ret = -EINVAL;
-               goto out;
-       }
+       struct drm_display_mode adjusted;
 
-       size = crtc_lut->gamma_size * (sizeof(uint16_t));
-       r_base = crtc->gamma_store;
-       if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
+       drm_mode_copy(&adjusted, mode);
+       drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
+       *hdisplay = adjusted.crtc_hdisplay;
+       *vdisplay = adjusted.crtc_vdisplay;
+}
+EXPORT_SYMBOL(drm_crtc_get_hv_timing);
 
-       g_base = r_base + size;
-       if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
+/**
+ * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
+ *     CRTC viewport
+ * @crtc: CRTC that framebuffer will be displayed on
+ * @x: x panning
+ * @y: y panning
+ * @mode: mode that framebuffer will be displayed under
+ * @fb: framebuffer to check size of
+ */
+int drm_crtc_check_viewport(const struct drm_crtc *crtc,
+                           int x, int y,
+                           const struct drm_display_mode *mode,
+                           const struct drm_framebuffer *fb)
 
-       b_base = g_base + size;
-       if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
+{
+       int hdisplay, vdisplay;
 
-       ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size);
+       drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
 
-out:
-       drm_modeset_unlock_all(dev);
-       return ret;
+       if (crtc->state &&
+           crtc->primary->state->rotation & (DRM_ROTATE_90 |
+                                             DRM_ROTATE_270))
+               swap(hdisplay, vdisplay);
 
+       return drm_framebuffer_check_src_coords(x << 16, y << 16,
+                                               hdisplay << 16, vdisplay << 16,
+                                               fb);
 }
+EXPORT_SYMBOL(drm_crtc_check_viewport);
 
 /**
- * drm_mode_gamma_get_ioctl - get the gamma table
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
+ * drm_mode_setcrtc - set CRTC configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
  *
- * Copy the current gamma table into the storage provided. This also provides
- * the gamma table size the driver expects, which can be used to size the
- * allocated storage.
+ * Build a new CRTC configuration based on user request.
  *
  * Called by the user via ioctl.
  *
  * Returns:
  * Zero on success, negative errno on failure.
  */
-int drm_mode_gamma_get_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv)
+int drm_mode_setcrtc(struct drm_device *dev, void *data,
+                    struct drm_file *file_priv)
 {
-       struct drm_mode_crtc_lut *crtc_lut = data;
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_mode_crtc *crtc_req = data;
        struct drm_crtc *crtc;
-       void *r_base, *g_base, *b_base;
-       int size;
-       int ret = 0;
+       struct drm_connector **connector_set = NULL, *connector;
+       struct drm_framebuffer *fb = NULL;
+       struct drm_display_mode *mode = NULL;
+       struct drm_mode_set set;
+       uint32_t __user *set_connectors_ptr;
+       int ret;
+       int i;
 
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return -EINVAL;
 
+       /*
+        * Universal plane src offsets are only 16.16, prevent havoc for
+        * drivers using universal plane code internally.
+        */
+       if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
+               return -ERANGE;
+
        drm_modeset_lock_all(dev);
-       crtc = drm_crtc_find(dev, crtc_lut->crtc_id);
+       crtc = drm_crtc_find(dev, crtc_req->crtc_id);
        if (!crtc) {
+               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
                ret = -ENOENT;
                goto out;
        }
+       DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
 
-       /* memcpy into gamma store */
-       if (crtc_lut->gamma_size != crtc->gamma_size) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       size = crtc_lut->gamma_size * (sizeof(uint16_t));
-       r_base = crtc->gamma_store;
-       if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
-
-       g_base = r_base + size;
-       if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
-
-       b_base = g_base + size;
-       if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) {
-               ret = -EFAULT;
-               goto out;
-       }
-out:
-       drm_modeset_unlock_all(dev);
-       return ret;
-}
-
-/**
- * drm_mode_page_flip_ioctl - schedule an asynchronous fb update
- * @dev: DRM device
- * @data: ioctl data
- * @file_priv: DRM file info
- *
- * This schedules an asynchronous update on a given CRTC, called page flip.
- * Optionally a drm event is generated to signal the completion of the event.
- * Generic drivers cannot assume that a pageflip with changed framebuffer
- * properties (including driver specific metadata like tiling layout) will work,
- * but some drivers support e.g. pixel format changes through the pageflip
- * ioctl.
- *
- * Called by the user via ioctl.
- *
- * Returns:
- * Zero on success, negative errno on failure.
- */
-int drm_mode_page_flip_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv)
-{
-       struct drm_mode_crtc_page_flip *page_flip = data;
-       struct drm_crtc *crtc;
-       struct drm_framebuffer *fb = NULL;
-       struct drm_pending_vblank_event *e = NULL;
-       int ret = -EINVAL;
-
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
-               return -EINVAL;
-
-       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
-           page_flip->reserved != 0)
-               return -EINVAL;
+       if (crtc_req->mode_valid) {
+               /* If we have a mode we need a framebuffer. */
+               /* If we pass -1, set the mode with the currently bound fb */
+               if (crtc_req->fb_id == -1) {
+                       if (!crtc->primary->fb) {
+                               DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
+                               ret = -EINVAL;
+                               goto out;
+                       }
+                       fb = crtc->primary->fb;
+                       /* Make refcounting symmetric with the lookup path. */
+                       drm_framebuffer_reference(fb);
+               } else {
+                       fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
+                       if (!fb) {
+                               DRM_DEBUG_KMS("Unknown FB ID%d\n",
+                                               crtc_req->fb_id);
+                               ret = -ENOENT;
+                               goto out;
+                       }
+               }
 
-       if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
-               return -EINVAL;
+               mode = drm_mode_create(dev);
+               if (!mode) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
 
-       crtc = drm_crtc_find(dev, page_flip->crtc_id);
-       if (!crtc)
-               return -ENOENT;
+               ret = drm_mode_convert_umode(mode, &crtc_req->mode);
+               if (ret) {
+                       DRM_DEBUG_KMS("Invalid mode\n");
+                       goto out;
+               }
 
-       drm_modeset_lock_crtc(crtc, crtc->primary);
-       if (crtc->primary->fb == NULL) {
-               /* The framebuffer is currently unbound, presumably
-                * due to a hotplug event, that userspace has not
-                * yet discovered.
+               /*
+                * Check whether the primary plane supports the fb pixel format.
+                * Drivers not implementing the universal planes API use a
+                * default formats list provided by the DRM core which doesn't
+                * match real hardware capabilities. Skip the check in that
+                * case.
                 */
-               ret = -EBUSY;
-               goto out;
-       }
+               if (!crtc->primary->format_default) {
+                       ret = drm_plane_check_pixel_format(crtc->primary,
+                                                          fb->pixel_format);
+                       if (ret) {
+                               char *format_name = drm_get_format_name(fb->pixel_format);
+                               DRM_DEBUG_KMS("Invalid pixel format %s\n", format_name);
+                               kfree(format_name);
+                               goto out;
+                       }
+               }
 
-       if (crtc->funcs->page_flip == NULL)
-               goto out;
+               ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
+                                             mode, fb);
+               if (ret)
+                       goto out;
 
-       fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
-       if (!fb) {
-               ret = -ENOENT;
-               goto out;
        }
 
-       if (crtc->state) {
-               const struct drm_plane_state *state = crtc->primary->state;
-
-               ret = check_src_coords(state->src_x, state->src_y,
-                                      state->src_w, state->src_h, fb);
-       } else {
-               ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
-       }
-       if (ret)
+       if (crtc_req->count_connectors == 0 && mode) {
+               DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
+               ret = -EINVAL;
                goto out;
+       }
 
-       if (crtc->primary->fb->pixel_format != fb->pixel_format) {
-               DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
+       if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
+               DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
+                         crtc_req->count_connectors);
                ret = -EINVAL;
                goto out;
        }
 
-       if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
-               e = kzalloc(sizeof *e, GFP_KERNEL);
-               if (!e) {
-                       ret = -ENOMEM;
+       if (crtc_req->count_connectors > 0) {
+               u32 out_id;
+
+               /* Avoid unbounded kernel memory allocation */
+               if (crtc_req->count_connectors > config->num_connector) {
+                       ret = -EINVAL;
                        goto out;
                }
-               e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
-               e->event.base.length = sizeof(e->event);
-               e->event.user_data = page_flip->user_data;
-               ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
-               if (ret) {
-                       kfree(e);
+
+               connector_set = kmalloc_array(crtc_req->count_connectors,
+                                             sizeof(struct drm_connector *),
+                                             GFP_KERNEL);
+               if (!connector_set) {
+                       ret = -ENOMEM;
                        goto out;
                }
-       }
 
-       crtc->primary->old_fb = crtc->primary->fb;
-       ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
-       if (ret) {
-               if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)
-                       drm_event_cancel_free(dev, &e->base);
-               /* Keep the old fb, don't unref it. */
-               crtc->primary->old_fb = NULL;
-       } else {
-               crtc->primary->fb = fb;
-               /* Unref only the old framebuffer. */
-               fb = NULL;
+               for (i = 0; i < crtc_req->count_connectors; i++) {
+                       connector_set[i] = NULL;
+                       set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
+                       if (get_user(out_id, &set_connectors_ptr[i])) {
+                               ret = -EFAULT;
+                               goto out;
+                       }
+
+                       connector = drm_connector_lookup(dev, out_id);
+                       if (!connector) {
+                               DRM_DEBUG_KMS("Connector id %d unknown\n",
+                                               out_id);
+                               ret = -ENOENT;
+                               goto out;
+                       }
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
+                                       connector->base.id,
+                                       connector->name);
+
+                       connector_set[i] = connector;
+               }
        }
 
+       set.crtc = crtc;
+       set.x = crtc_req->x;
+       set.y = crtc_req->y;
+       set.mode = mode;
+       set.connectors = connector_set;
+       set.num_connectors = crtc_req->count_connectors;
+       set.fb = fb;
+       ret = drm_mode_set_config_internal(&set);
+
 out:
        if (fb)
                drm_framebuffer_unreference(fb);
-       if (crtc->primary->old_fb)
-               drm_framebuffer_unreference(crtc->primary->old_fb);
-       crtc->primary->old_fb = NULL;
-       drm_modeset_unlock_crtc(crtc);
+
+       if (connector_set) {
+               for (i = 0; i < crtc_req->count_connectors; i++) {
+                       if (connector_set[i])
+                               drm_connector_unreference(connector_set[i]);
+               }
+       }
+       kfree(connector_set);
+       drm_mode_destroy(dev, mode);
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+
+int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
+                              struct drm_property *property,
+                              uint64_t value)
+{
+       int ret = -EINVAL;
+       struct drm_crtc *crtc = obj_to_crtc(obj);
+
+       if (crtc->funcs->set_property)
+               ret = crtc->funcs->set_property(crtc, property, value);
+       if (!ret)
+               drm_object_property_set_value(obj, property, value);
 
        return ret;
 }
@@ -5638,37 +1047,6 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
        return dev->driver->dumb_destroy(file_priv, dev, args->handle);
 }
 
-/**
- * drm_rotation_simplify() - Try to simplify the rotation
- * @rotation: Rotation to be simplified
- * @supported_rotations: Supported rotations
- *
- * Attempt to simplify the rotation to a form that is supported.
- * Eg. if the hardware supports everything except DRM_REFLECT_X
- * one could call this function like this:
- *
- * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) |
- *                       BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) |
- *                       BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y));
- *
- * to eliminate the DRM_ROTATE_X flag. Depending on what kind of
- * transforms the hardware supports, this function may not
- * be able to produce a supported transform, so the caller should
- * check the result afterwards.
- */
-unsigned int drm_rotation_simplify(unsigned int rotation,
-                                  unsigned int supported_rotations)
-{
-       if (rotation & ~supported_rotations) {
-               rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y);
-               rotation = (rotation & DRM_REFLECT_MASK) |
-                          BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4);
-       }
-
-       return rotation;
-}
-EXPORT_SYMBOL(drm_rotation_simplify);
-
 /**
  * drm_mode_config_init - initialize DRM mode_configuration structure
  * @dev: DRM device
@@ -5785,24 +1163,6 @@ void drm_mode_config_cleanup(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_mode_config_cleanup);
 
-struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
-                                                      unsigned int supported_rotations)
-{
-       static const struct drm_prop_enum_list props[] = {
-               { DRM_ROTATE_0,   "rotate-0" },
-               { DRM_ROTATE_90,  "rotate-90" },
-               { DRM_ROTATE_180, "rotate-180" },
-               { DRM_ROTATE_270, "rotate-270" },
-               { DRM_REFLECT_X,  "reflect-x" },
-               { DRM_REFLECT_Y,  "reflect-y" },
-       };
-
-       return drm_property_create_bitmask(dev, 0, "rotation",
-                                          props, ARRAY_SIZE(props),
-                                          supported_rotations);
-}
-EXPORT_SYMBOL(drm_mode_create_rotation_property);
-
 /**
  * DOC: Tile group
  *
@@ -5901,48 +1261,3 @@ struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
        return tg;
 }
 EXPORT_SYMBOL(drm_mode_create_tile_group);
-
-/**
- * drm_crtc_enable_color_mgmt - enable color management properties
- * @crtc: DRM CRTC
- * @degamma_lut_size: the size of the degamma lut (before CSC)
- * @has_ctm: whether to attach ctm_property for CSC matrix
- * @gamma_lut_size: the size of the gamma lut (after CSC)
- *
- * This function lets the driver enable the color correction
- * properties on a CRTC. This includes 3 degamma, csc and gamma
- * properties that userspace can set and 2 size properties to inform
- * the userspace of the lut sizes. Each of the properties are
- * optional. The gamma and degamma properties are only attached if
- * their size is not 0 and ctm_property is only attached if has_ctm is
- * true.
- */
-void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
-                               uint degamma_lut_size,
-                               bool has_ctm,
-                               uint gamma_lut_size)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_mode_config *config = &dev->mode_config;
-
-       if (degamma_lut_size) {
-               drm_object_attach_property(&crtc->base,
-                                          config->degamma_lut_property, 0);
-               drm_object_attach_property(&crtc->base,
-                                          config->degamma_lut_size_property,
-                                          degamma_lut_size);
-       }
-
-       if (has_ctm)
-               drm_object_attach_property(&crtc->base,
-                                          config->ctm_property, 0);
-
-       if (gamma_lut_size) {
-               drm_object_attach_property(&crtc->base,
-                                          config->gamma_lut_property, 0);
-               drm_object_attach_property(&crtc->base,
-                                          config->gamma_lut_size_property,
-                                          gamma_lut_size);
-       }
-}
-EXPORT_SYMBOL(drm_crtc_enable_color_mgmt);
index 604d3ef..5d2cb13 100644 (file)
  * &drm_connector_helper_funcs.
  */
 
-/**
- * drm_helper_move_panel_connectors_to_head() - move panels to the front in the
- *                                             connector list
- * @dev: drm device to operate on
- *
- * Some userspace presumes that the first connected connector is the main
- * display, where it's supposed to display e.g. the login screen. For
- * laptops, this should be the main panel. Use this function to sort all
- * (eDP/LVDS) panels to the front of the connector list, instead of
- * painstakingly trying to initialize them in the right order.
- */
-void drm_helper_move_panel_connectors_to_head(struct drm_device *dev)
-{
-       struct drm_connector *connector, *tmp;
-       struct list_head panel_list;
-
-       INIT_LIST_HEAD(&panel_list);
-
-       list_for_each_entry_safe(connector, tmp,
-                                &dev->mode_config.connector_list, head) {
-               if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
-                   connector->connector_type == DRM_MODE_CONNECTOR_eDP)
-                       list_move_tail(&connector->head, &panel_list);
-       }
-
-       list_splice(&panel_list, &dev->mode_config.connector_list);
-}
-EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
-
 /**
  * drm_helper_encoder_in_use - check if a given encoder is in use
  * @encoder: encoder to check
@@ -912,33 +883,6 @@ int drm_helper_connector_dpms(struct drm_connector *connector, int mode)
 }
 EXPORT_SYMBOL(drm_helper_connector_dpms);
 
-/**
- * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata
- * @fb: drm_framebuffer object to fill out
- * @mode_cmd: metadata from the userspace fb creation request
- *
- * This helper can be used in a drivers fb_create callback to pre-fill the fb's
- * metadata fields.
- */
-void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
-                                   const struct drm_mode_fb_cmd2 *mode_cmd)
-{
-       int i;
-
-       fb->width = mode_cmd->width;
-       fb->height = mode_cmd->height;
-       for (i = 0; i < 4; i++) {
-               fb->pitches[i] = mode_cmd->pitches[i];
-               fb->offsets[i] = mode_cmd->offsets[i];
-               fb->modifier[i] = mode_cmd->modifier[i];
-       }
-       drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
-                                   &fb->bits_per_pixel);
-       fb->pixel_format = mode_cmd->pixel_format;
-       fb->flags = mode_cmd->flags;
-}
-EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
-
 /**
  * drm_helper_resume_force_mode - force-restore mode setting configuration
  * @dev: drm_device which should be restored
diff --git a/drivers/gpu/drm/drm_crtc_helper_internal.h b/drivers/gpu/drm/drm_crtc_helper_internal.h
new file mode 100644 (file)
index 0000000..28295e5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * 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.
+ */
+
+/*
+ * This header file contains mode setting related functions and definitions
+ * which are only used within the drm kms helper module as internal
+ * implementation details and are not exported to drivers.
+ */
+
+#include <drm/drm_dp_helper.h>
+
+/* drm_fb_helper.c */
+#ifdef CONFIG_DRM_FBDEV_EMULATION
+int drm_fb_helper_modinit(void);
+#else
+static inline int drm_fb_helper_modinit(void)
+{
+       return 0;
+}
+#endif
+
+/* drm_dp_aux_dev.c */
+#ifdef CONFIG_DRM_DP_AUX_CHARDEV
+int drm_dp_aux_dev_init(void);
+void drm_dp_aux_dev_exit(void);
+int drm_dp_aux_register_devnode(struct drm_dp_aux *aux);
+void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux);
+#else
+static inline int drm_dp_aux_dev_init(void)
+{
+       return 0;
+}
+
+static inline void drm_dp_aux_dev_exit(void)
+{
+}
+
+static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
+{
+       return 0;
+}
+
+static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
+{
+}
+#endif
index 0c34e6d..c48ba02 100644 (file)
 
 
 /* drm_crtc.c */
-void drm_connector_ida_init(void);
-void drm_connector_ida_destroy(void);
-int drm_mode_object_get(struct drm_device *dev,
-                       struct drm_mode_object *obj, uint32_t obj_type);
-void drm_mode_object_unregister(struct drm_device *dev,
-                               struct drm_mode_object *object);
-bool drm_property_change_valid_get(struct drm_property *property,
-                                  uint64_t value,
-                                  struct drm_mode_object **ref);
-void drm_property_change_valid_put(struct drm_property *property,
-                                  struct drm_mode_object *ref);
-
-int drm_plane_check_pixel_format(const struct drm_plane *plane,
-                                u32 format);
+int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
+                              struct drm_property *property,
+                              uint64_t value);
 int drm_crtc_check_viewport(const struct drm_crtc *crtc,
                            int x, int y,
                            const struct drm_display_mode *mode,
                            const struct drm_framebuffer *fb);
 
 void drm_fb_release(struct drm_file *file_priv);
-void drm_property_destroy_user_blobs(struct drm_device *dev,
-                                    struct drm_file *file_priv);
 
 /* dumb buffer support IOCTLs */
 int drm_mode_create_dumb_ioctl(struct drm_device *dev,
@@ -64,42 +51,32 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
 int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
                                void *data, struct drm_file *file_priv);
 
-/* framebuffer IOCTLs */
-extern int drm_mode_addfb(struct drm_device *dev,
-                         void *data, struct drm_file *file_priv);
-extern int drm_mode_addfb2(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv);
-int drm_mode_rmfb(struct drm_device *dev,
-                        void *data, struct drm_file *file_priv);
-int drm_mode_getfb(struct drm_device *dev,
-                  void *data, struct drm_file *file_priv);
-int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv);
-
 /* IOCTLs */
-int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
-                                     struct drm_file *file_priv);
-int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
-                                   struct drm_file *file_priv);
-
 int drm_mode_getresources(struct drm_device *dev,
                          void *data, struct drm_file *file_priv);
-int drm_mode_getplane_res(struct drm_device *dev, void *data,
-                         struct drm_file *file_priv);
 int drm_mode_getcrtc(struct drm_device *dev,
                     void *data, struct drm_file *file_priv);
-int drm_mode_getconnector(struct drm_device *dev,
-                         void *data, struct drm_file *file_priv);
 int drm_mode_setcrtc(struct drm_device *dev,
                     void *data, struct drm_file *file_priv);
-int drm_mode_getplane(struct drm_device *dev,
-                     void *data, struct drm_file *file_priv);
-int drm_mode_setplane(struct drm_device *dev,
-                     void *data, struct drm_file *file_priv);
-int drm_mode_cursor_ioctl(struct drm_device *dev,
-                         void *data, struct drm_file *file_priv);
-int drm_mode_cursor2_ioctl(struct drm_device *dev,
-                          void *data, struct drm_file *file_priv);
+
+/* drm_color_mgmt.c */
+
+/* IOCTLs */
+int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv);
+int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv);
+
+/* drm_property.c */
+void drm_property_destroy_user_blobs(struct drm_device *dev,
+                                    struct drm_file *file_priv);
+bool drm_property_change_valid_get(struct drm_property *property,
+                                  uint64_t value,
+                                  struct drm_mode_object **ref);
+void drm_property_change_valid_put(struct drm_property *property,
+                                  struct drm_mode_object *ref);
+
+/* IOCTL */
 int drm_mode_getproperty_ioctl(struct drm_device *dev,
                               void *data, struct drm_file *file_priv);
 int drm_mode_getblob_ioctl(struct drm_device *dev,
@@ -108,17 +85,80 @@ int drm_mode_createblob_ioctl(struct drm_device *dev,
                              void *data, struct drm_file *file_priv);
 int drm_mode_destroyblob_ioctl(struct drm_device *dev,
                               void *data, struct drm_file *file_priv);
-int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
-                                         void *data, struct drm_file *file_priv);
+
+/* drm_mode_object.c */
+int drm_mode_object_get_reg(struct drm_device *dev,
+                           struct drm_mode_object *obj,
+                           uint32_t obj_type,
+                           bool register_obj,
+                           void (*obj_free_cb)(struct kref *kref));
+void drm_mode_object_register(struct drm_device *dev,
+                             struct drm_mode_object *obj);
+int drm_mode_object_get(struct drm_device *dev,
+                       struct drm_mode_object *obj, uint32_t obj_type);
+struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
+                                              uint32_t id, uint32_t type);
+void drm_mode_object_unregister(struct drm_device *dev,
+                               struct drm_mode_object *object);
+int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
+                                  uint32_t __user *prop_ptr,
+                                  uint64_t __user *prop_values,
+                                  uint32_t *arg_count_props);
+struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj,
+                                              uint32_t prop_id);
+
+/* IOCTL */
+
+int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
+                                     struct drm_file *file_priv);
+int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
+                                   struct drm_file *file_priv);
+
+/* drm_encoder.c */
+int drm_encoder_register_all(struct drm_device *dev);
+void drm_encoder_unregister_all(struct drm_device *dev);
+
+/* IOCTL */
 int drm_mode_getencoder(struct drm_device *dev,
                        void *data, struct drm_file *file_priv);
-int drm_mode_gamma_get_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv);
-int drm_mode_gamma_set_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv);
 
-int drm_mode_page_flip_ioctl(struct drm_device *dev,
-                            void *data, struct drm_file *file_priv);
+/* drm_connector.c */
+void drm_connector_ida_init(void);
+void drm_connector_ida_destroy(void);
+void drm_connector_unregister_all(struct drm_device *dev);
+int drm_connector_register_all(struct drm_device *dev);
+int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj,
+                                   struct drm_property *property,
+                                   uint64_t value);
+int drm_connector_create_standard_properties(struct drm_device *dev);
+
+/* IOCTL */
+int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+                                         void *data, struct drm_file *file_priv);
+int drm_mode_getconnector(struct drm_device *dev,
+                         void *data, struct drm_file *file_priv);
+
+/* drm_framebuffer.c */
+struct drm_framebuffer *
+drm_internal_framebuffer_create(struct drm_device *dev,
+                               const struct drm_mode_fb_cmd2 *r,
+                               struct drm_file *file_priv);
+void drm_framebuffer_free(struct kref *kref);
+int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
+                                    uint32_t src_w, uint32_t src_h,
+                                    const struct drm_framebuffer *fb);
+
+/* IOCTL */
+int drm_mode_addfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv);
+int drm_mode_addfb2(struct drm_device *dev,
+                   void *data, struct drm_file *file_priv);
+int drm_mode_rmfb(struct drm_device *dev,
+                 void *data, struct drm_file *file_priv);
+int drm_mode_getfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv);
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv);
 
 /* drm_atomic.c */
 int drm_atomic_get_property(struct drm_mode_object *obj,
@@ -129,6 +169,23 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
 int drm_modeset_register_all(struct drm_device *dev);
 void drm_modeset_unregister_all(struct drm_device *dev);
 
-/* drm_blend.c */
-int drm_atomic_helper_normalize_zpos(struct drm_device *dev,
-                                    struct drm_atomic_state *state);
+
+/* drm_plane.c */
+int drm_plane_register_all(struct drm_device *dev);
+void drm_plane_unregister_all(struct drm_device *dev);
+int drm_plane_check_pixel_format(const struct drm_plane *plane,
+                                u32 format);
+
+/* IOCTL */
+int drm_mode_getplane_res(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv);
+int drm_mode_getplane(struct drm_device *dev,
+                     void *data, struct drm_file *file_priv);
+int drm_mode_setplane(struct drm_device *dev,
+                     void *data, struct drm_file *file_priv);
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+                         void *data, struct drm_file *file_priv);
+int drm_mode_cursor2_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv);
+int drm_mode_page_flip_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv);
index ea48180..3f83e2c 100644 (file)
@@ -50,9 +50,8 @@ int drm_legacy_dma_setup(struct drm_device *dev)
        int i;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) ||
-           drm_core_check_feature(dev, DRIVER_MODESET)) {
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return 0;
-       }
 
        dev->buf_use = 0;
        atomic_set(&dev->buf_alloc, 0);
@@ -81,9 +80,8 @@ void drm_legacy_dma_takedown(struct drm_device *dev)
        int i, j;
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_DMA) ||
-           drm_core_check_feature(dev, DRIVER_MODESET)) {
+           !drm_core_check_feature(dev, DRIVER_LEGACY))
                return;
-       }
 
        if (!dma)
                return;
index 734f86a..ec1ed94 100644 (file)
@@ -36,6 +36,8 @@
 #include <drm/drm_crtc.h>
 #include <drm/drmP.h>
 
+#include "drm_crtc_helper_internal.h"
+
 struct drm_dp_aux_dev {
        unsigned index;
        struct drm_dp_aux *aux;
@@ -283,12 +285,7 @@ static int auxdev_wait_atomic_t(atomic_t *p)
        schedule();
        return 0;
 }
-/**
- * drm_dp_aux_unregister_devnode() - unregister a devnode for this aux channel
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 on success or a negative error code on failure.
- */
+
 void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
 {
        struct drm_dp_aux_dev *aux_dev;
@@ -314,14 +311,7 @@ void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
        DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
        kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
 }
-EXPORT_SYMBOL(drm_dp_aux_unregister_devnode);
 
-/**
- * drm_dp_aux_register_devnode() - register a devnode for this aux channel
- * @aux: DisplayPort AUX channel
- *
- * Returns 0 on success or a negative error code on failure.
- */
 int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
 {
        struct drm_dp_aux_dev *aux_dev;
@@ -347,7 +337,6 @@ error:
        drm_dp_aux_unregister_devnode(aux);
        return res;
 }
-EXPORT_SYMBOL(drm_dp_aux_register_devnode);
 
 int drm_dp_aux_dev_init(void)
 {
@@ -369,11 +358,9 @@ out:
        class_destroy(drm_dp_aux_dev_class);
        return res;
 }
-EXPORT_SYMBOL(drm_dp_aux_dev_init);
 
 void drm_dp_aux_dev_exit(void)
 {
        unregister_chrdev(drm_dev_major, "aux");
        class_destroy(drm_dp_aux_dev_class);
 }
-EXPORT_SYMBOL(drm_dp_aux_dev_exit);
index 2bd0644..3e6fe82 100644 (file)
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/i2c.h>
+#include <linux/seq_file.h>
 #include <drm/drm_dp_helper.h>
-#include <drm/drm_dp_aux_dev.h>
 #include <drm/drmP.h>
 
+#include "drm_crtc_helper_internal.h"
+
 /**
  * DOC: dp helpers
  *
@@ -223,7 +225,7 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
                        err = ret;
        }
 
-       DRM_DEBUG_KMS("too many retries, giving up\n");
+       DRM_DEBUG_KMS("Too many retries, giving up. First error: %d\n", err);
        ret = err;
 
 unlock:
@@ -438,6 +440,179 @@ int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
 }
 EXPORT_SYMBOL(drm_dp_link_configure);
 
+/**
+ * drm_dp_downstream_max_clock() - extract branch device max
+ *                                 pixel rate for legacy VGA
+ *                                 converter or max TMDS clock
+ *                                 rate for others
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Returns max clock in kHz on success or 0 if max clock not defined
+ */
+int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                               const u8 port_cap[4])
+{
+       int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+       bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+               DP_DETAILED_CAP_INFO_AVAILABLE;
+
+       if (!detailed_cap_info)
+               return 0;
+
+       switch (type) {
+       case DP_DS_PORT_TYPE_VGA:
+               return port_cap[1] * 8 * 1000;
+       case DP_DS_PORT_TYPE_DVI:
+       case DP_DS_PORT_TYPE_HDMI:
+       case DP_DS_PORT_TYPE_DP_DUALMODE:
+               return port_cap[1] * 2500;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL(drm_dp_downstream_max_clock);
+
+/**
+ * drm_dp_downstream_max_bpc() - extract branch device max
+ *                               bits per component
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ *
+ * Returns max bpc on success or 0 if max bpc not defined
+ */
+int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                             const u8 port_cap[4])
+{
+       int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+       bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+               DP_DETAILED_CAP_INFO_AVAILABLE;
+       int bpc;
+
+       if (!detailed_cap_info)
+               return 0;
+
+       switch (type) {
+       case DP_DS_PORT_TYPE_VGA:
+       case DP_DS_PORT_TYPE_DVI:
+       case DP_DS_PORT_TYPE_HDMI:
+       case DP_DS_PORT_TYPE_DP_DUALMODE:
+               bpc = port_cap[2] & DP_DS_MAX_BPC_MASK;
+
+               switch (bpc) {
+               case DP_DS_8BPC:
+                       return 8;
+               case DP_DS_10BPC:
+                       return 10;
+               case DP_DS_12BPC:
+                       return 12;
+               case DP_DS_16BPC:
+                       return 16;
+               }
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL(drm_dp_downstream_max_bpc);
+
+/**
+ * drm_dp_downstream_id() - identify branch device
+ * @aux: DisplayPort AUX channel
+ * @id: DisplayPort branch device id
+ *
+ * Returns branch device id on success or NULL on failure
+ */
+int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6])
+{
+       return drm_dp_dpcd_read(aux, DP_BRANCH_ID, id, 6);
+}
+EXPORT_SYMBOL(drm_dp_downstream_id);
+
+/**
+ * drm_dp_downstream_debug() - debug DP branch devices
+ * @m: pointer for debugfs file
+ * @dpcd: DisplayPort configuration data
+ * @port_cap: port capabilities
+ * @aux: DisplayPort AUX channel
+ *
+ */
+void drm_dp_downstream_debug(struct seq_file *m,
+                            const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                            const u8 port_cap[4], struct drm_dp_aux *aux)
+{
+       bool detailed_cap_info = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                                DP_DETAILED_CAP_INFO_AVAILABLE;
+       int clk;
+       int bpc;
+       char id[6];
+       int len;
+       uint8_t rev[2];
+       int type = port_cap[0] & DP_DS_PORT_TYPE_MASK;
+       bool branch_device = dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+                            DP_DWN_STRM_PORT_PRESENT;
+
+       seq_printf(m, "\tDP branch device present: %s\n",
+                  branch_device ? "yes" : "no");
+
+       if (!branch_device)
+               return;
+
+       switch (type) {
+       case DP_DS_PORT_TYPE_DP:
+               seq_puts(m, "\t\tType: DisplayPort\n");
+               break;
+       case DP_DS_PORT_TYPE_VGA:
+               seq_puts(m, "\t\tType: VGA\n");
+               break;
+       case DP_DS_PORT_TYPE_DVI:
+               seq_puts(m, "\t\tType: DVI\n");
+               break;
+       case DP_DS_PORT_TYPE_HDMI:
+               seq_puts(m, "\t\tType: HDMI\n");
+               break;
+       case DP_DS_PORT_TYPE_NON_EDID:
+               seq_puts(m, "\t\tType: others without EDID support\n");
+               break;
+       case DP_DS_PORT_TYPE_DP_DUALMODE:
+               seq_puts(m, "\t\tType: DP++\n");
+               break;
+       case DP_DS_PORT_TYPE_WIRELESS:
+               seq_puts(m, "\t\tType: Wireless\n");
+               break;
+       default:
+               seq_puts(m, "\t\tType: N/A\n");
+       }
+
+       drm_dp_downstream_id(aux, id);
+       seq_printf(m, "\t\tID: %s\n", id);
+
+       len = drm_dp_dpcd_read(aux, DP_BRANCH_HW_REV, &rev[0], 1);
+       if (len > 0)
+               seq_printf(m, "\t\tHW: %d.%d\n",
+                          (rev[0] & 0xf0) >> 4, rev[0] & 0xf);
+
+       len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, &rev, 2);
+       if (len > 0)
+               seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]);
+
+       if (detailed_cap_info) {
+               clk = drm_dp_downstream_max_clock(dpcd, port_cap);
+
+               if (clk > 0) {
+                       if (type == DP_DS_PORT_TYPE_VGA)
+                               seq_printf(m, "\t\tMax dot clock: %d kHz\n", clk);
+                       else
+                               seq_printf(m, "\t\tMax TMDS clock: %d kHz\n", clk);
+               }
+
+               bpc = drm_dp_downstream_max_bpc(dpcd, port_cap);
+
+               if (bpc > 0)
+                       seq_printf(m, "\t\tMax bpc: %d\n", bpc);
+       }
+}
+EXPORT_SYMBOL(drm_dp_downstream_debug);
+
 /*
  * I2C-over-AUX implementation
  */
@@ -574,7 +749,17 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                        if (ret == -EBUSY)
                                continue;
 
-                       DRM_DEBUG_KMS("transaction failed: %d\n", ret);
+                       /*
+                        * While timeouts can be errors, they're usually normal
+                        * behavior (for instance, when a driver tries to
+                        * communicate with a non-existant DisplayPort device).
+                        * Avoid spamming the kernel log with timeout errors.
+                        */
+                       if (ret == -ETIMEDOUT)
+                               DRM_DEBUG_KMS_RATELIMITED("transaction timed out\n");
+                       else
+                               DRM_DEBUG_KMS("transaction failed: %d\n", ret);
+
                        return ret;
                }
 
index be27ed3..6efdba4 100644 (file)
@@ -33,7 +33,6 @@
 #include <linux/mount.h>
 #include <linux/slab.h>
 #include <drm/drmP.h>
-#include <drm/drm_core.h>
 #include "drm_crtc_internal.h"
 #include "drm_legacy.h"
 #include "drm_internal.h"
@@ -46,8 +45,8 @@
 unsigned int drm_debug = 0;
 EXPORT_SYMBOL(drm_debug);
 
-MODULE_AUTHOR(CORE_AUTHOR);
-MODULE_DESCRIPTION(CORE_DESC);
+MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
+MODULE_DESCRIPTION("DRM shared core routines");
 MODULE_LICENSE("GPL and additional rights");
 MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug category.\n"
 "\t\tBit 0 (0x01) will enable CORE messages (drm core code)\n"
@@ -63,37 +62,52 @@ static struct idr drm_minors_idr;
 
 static struct dentry *drm_debugfs_root;
 
-void drm_err(const char *format, ...)
+#define DRM_PRINTK_FMT "[" DRM_NAME ":%s]%s %pV"
+
+void drm_dev_printk(const struct device *dev, const char *level,
+                   unsigned int category, const char *function_name,
+                   const char *prefix, const char *format, ...)
 {
        struct va_format vaf;
        va_list args;
 
-       va_start(args, format);
+       if (category != DRM_UT_NONE && !(drm_debug & category))
+               return;
 
+       va_start(args, format);
        vaf.fmt = format;
        vaf.va = &args;
 
-       printk(KERN_ERR "[" DRM_NAME ":%ps] *ERROR* %pV",
-              __builtin_return_address(0), &vaf);
+       if (dev)
+               dev_printk(level, dev, DRM_PRINTK_FMT, function_name, prefix,
+                          &vaf);
+       else
+               printk("%s" DRM_PRINTK_FMT, level, function_name, prefix, &vaf);
 
        va_end(args);
 }
-EXPORT_SYMBOL(drm_err);
+EXPORT_SYMBOL(drm_dev_printk);
 
-void drm_ut_debug_printk(const char *function_name, const char *format, ...)
+void drm_printk(const char *level, unsigned int category,
+               const char *format, ...)
 {
        struct va_format vaf;
        va_list args;
 
+       if (category != DRM_UT_NONE && !(drm_debug & category))
+               return;
+
        va_start(args, format);
        vaf.fmt = format;
        vaf.va = &args;
 
-       printk(KERN_DEBUG "[" DRM_NAME ":%s] %pV", function_name, &vaf);
+       printk("%s" "[" DRM_NAME ":%ps]%s %pV",
+              level, __builtin_return_address(0),
+              strcmp(level, KERN_ERR) == 0 ? " *ERROR*" : "", &vaf);
 
        va_end(args);
 }
-EXPORT_SYMBOL(drm_ut_debug_printk);
+EXPORT_SYMBOL(drm_printk);
 
 /*
  * DRM Minors
@@ -112,7 +126,7 @@ static struct drm_minor **drm_minor_get_slot(struct drm_device *dev,
                                             unsigned int type)
 {
        switch (type) {
-       case DRM_MINOR_LEGACY:
+       case DRM_MINOR_PRIMARY:
                return &dev->primary;
        case DRM_MINOR_RENDER:
                return &dev->render;
@@ -325,6 +339,9 @@ void drm_minor_release(struct drm_minor *minor)
 
 static int drm_dev_set_unique(struct drm_device *dev, const char *name)
 {
+       if (!name)
+               return -EINVAL;
+
        kfree(dev->unique);
        dev->unique = kstrdup(name, GFP_KERNEL);
 
@@ -512,7 +529,7 @@ int drm_dev_init(struct drm_device *dev,
                        goto err_minors;
        }
 
-       ret = drm_minor_alloc(dev, DRM_MINOR_LEGACY);
+       ret = drm_minor_alloc(dev, DRM_MINOR_PRIMARY);
        if (ret)
                goto err_minors;
 
@@ -545,7 +562,7 @@ err_ctxbitmap:
        drm_legacy_ctxbitmap_cleanup(dev);
        drm_ht_remove(&dev->map_hash);
 err_minors:
-       drm_minor_free(dev, DRM_MINOR_LEGACY);
+       drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
        drm_minor_free(dev, DRM_MINOR_CONTROL);
        drm_fs_inode_free(dev->anon_inode);
@@ -575,7 +592,7 @@ EXPORT_SYMBOL(drm_dev_init);
  * own struct should look at using drm_dev_init() instead.
  *
  * RETURNS:
- * Pointer to new DRM device, or NULL if out of memory.
+ * Pointer to new DRM device, or ERR_PTR on failure.
  */
 struct drm_device *drm_dev_alloc(struct drm_driver *driver,
                                 struct device *parent)
@@ -585,12 +602,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
-               return NULL;
+               return ERR_PTR(-ENOMEM);
 
        ret = drm_dev_init(dev, driver, parent);
        if (ret) {
                kfree(dev);
-               return NULL;
+               return ERR_PTR(ret);
        }
 
        return dev;
@@ -608,7 +625,7 @@ static void drm_dev_release(struct kref *ref)
        drm_ht_remove(&dev->map_hash);
        drm_fs_inode_free(dev->anon_inode);
 
-       drm_minor_free(dev, DRM_MINOR_LEGACY);
+       drm_minor_free(dev, DRM_MINOR_PRIMARY);
        drm_minor_free(dev, DRM_MINOR_RENDER);
        drm_minor_free(dev, DRM_MINOR_CONTROL);
 
@@ -684,7 +701,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
        if (ret)
                goto err_minors;
 
-       ret = drm_minor_register(dev, DRM_MINOR_LEGACY);
+       ret = drm_minor_register(dev, DRM_MINOR_PRIMARY);
        if (ret)
                goto err_minors;
 
@@ -701,7 +718,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
        goto out_unlock;
 
 err_minors:
-       drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+       drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
        drm_minor_unregister(dev, DRM_MINOR_RENDER);
        drm_minor_unregister(dev, DRM_MINOR_CONTROL);
 out_unlock:
@@ -741,7 +758,7 @@ void drm_dev_unregister(struct drm_device *dev)
        list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head)
                drm_legacy_rmmap(dev, r_list->map);
 
-       drm_minor_unregister(dev, DRM_MINOR_LEGACY);
+       drm_minor_unregister(dev, DRM_MINOR_PRIMARY);
        drm_minor_unregister(dev, DRM_MINOR_RENDER);
        drm_minor_unregister(dev, DRM_MINOR_CONTROL);
 }
@@ -807,53 +824,48 @@ static const struct file_operations drm_stub_fops = {
        .llseek = noop_llseek,
 };
 
+static void drm_core_exit(void)
+{
+       unregister_chrdev(DRM_MAJOR, "drm");
+       debugfs_remove(drm_debugfs_root);
+       drm_sysfs_destroy();
+       idr_destroy(&drm_minors_idr);
+       drm_connector_ida_destroy();
+       drm_global_release();
+}
+
 static int __init drm_core_init(void)
 {
-       int ret = -ENOMEM;
+       int ret;
 
        drm_global_init();
        drm_connector_ida_init();
        idr_init(&drm_minors_idr);
 
-       if (register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops))
-               goto err_p1;
-
        ret = drm_sysfs_init();
        if (ret < 0) {
-               printk(KERN_ERR "DRM: Error creating drm class.\n");
-               goto err_p2;
+               DRM_ERROR("Cannot create DRM class: %d\n", ret);
+               goto error;
        }
 
        drm_debugfs_root = debugfs_create_dir("dri", NULL);
        if (!drm_debugfs_root) {
-               DRM_ERROR("Cannot create /sys/kernel/debug/dri\n");
-               ret = -1;
-               goto err_p3;
+               ret = -ENOMEM;
+               DRM_ERROR("Cannot create debugfs-root: %d\n", ret);
+               goto error;
        }
 
-       DRM_INFO("Initialized %s %d.%d.%d %s\n",
-                CORE_NAME, CORE_MAJOR, CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE);
+       ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
+       if (ret < 0)
+               goto error;
+
+       DRM_INFO("Initialized\n");
        return 0;
-err_p3:
-       drm_sysfs_destroy();
-err_p2:
-       unregister_chrdev(DRM_MAJOR, "drm");
 
-       idr_destroy(&drm_minors_idr);
-err_p1:
+error:
+       drm_core_exit();
        return ret;
 }
 
-static void __exit drm_core_exit(void)
-{
-       debugfs_remove(drm_debugfs_root);
-       drm_sysfs_destroy();
-
-       unregister_chrdev(DRM_MAJOR, "drm");
-
-       drm_connector_ida_destroy();
-       idr_destroy(&drm_minors_idr);
-}
-
 module_init(drm_core_init);
 module_exit(drm_core_exit);
index 637a0aa..ec77bd3 100644 (file)
@@ -991,7 +991,7 @@ static const struct drm_display_mode edid_cea_modes[] = {
         .vrefresh = 120, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
        /* 64 - 1920x1080@100Hz */
        { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
-                  2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
+                  2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
                   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC),
         .vrefresh = 100, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, },
 };
@@ -3253,16 +3253,12 @@ static void fixup_detailed_cea_mode_clock(struct drm_display_mode *mode)
 }
 
 static void
-parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
+drm_parse_hdmi_vsdb_audio(struct drm_connector *connector, const u8 *db)
 {
        u8 len = cea_db_payload_len(db);
 
-       if (len >= 6) {
+       if (len >= 6)
                connector->eld[5] |= (db[6] >> 7) << 1;  /* Supports_AI */
-               connector->dvi_dual = db[6] & 1;
-       }
-       if (len >= 7)
-               connector->max_tmds_clock = db[7] * 5;
        if (len >= 8) {
                connector->latency_present[0] = db[8] >> 7;
                connector->latency_present[1] = (db[8] >> 6) & 1;
@@ -3276,19 +3272,15 @@ parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
        if (len >= 12)
                connector->audio_latency[1] = db[12];
 
-       DRM_DEBUG_KMS("HDMI: DVI dual %d, "
-                   "max TMDS clock %d, "
-                   "latency present %d %d, "
-                   "video latency %d %d, "
-                   "audio latency %d %d\n",
-                   connector->dvi_dual,
-                   connector->max_tmds_clock,
-             (int) connector->latency_present[0],
-             (int) connector->latency_present[1],
-                   connector->video_latency[0],
-                   connector->video_latency[1],
-                   connector->audio_latency[0],
-                   connector->audio_latency[1]);
+       DRM_DEBUG_KMS("HDMI: latency present %d %d, "
+                     "video latency %d %d, "
+                     "audio latency %d %d\n",
+                     connector->latency_present[0],
+                     connector->latency_present[1],
+                     connector->video_latency[0],
+                     connector->video_latency[1],
+                     connector->audio_latency[0],
+                     connector->audio_latency[1]);
 }
 
 static void
@@ -3358,6 +3350,13 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
 
        memset(eld, 0, sizeof(connector->eld));
 
+       connector->latency_present[0] = false;
+       connector->latency_present[1] = false;
+       connector->video_latency[0] = 0;
+       connector->audio_latency[0] = 0;
+       connector->video_latency[1] = 0;
+       connector->audio_latency[1] = 0;
+
        cea = drm_find_cea_extension(edid);
        if (!cea) {
                DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
@@ -3407,7 +3406,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
                        case VENDOR_BLOCK:
                                /* HDMI Vendor-Specific Data Block */
                                if (cea_db_is_hdmi_vsdb(db))
-                                       parse_hdmi_vsdb(connector, db);
+                                       drm_parse_hdmi_vsdb_audio(connector, db);
                                break;
                        default:
                                break;
@@ -3721,122 +3720,127 @@ bool drm_rgb_quant_range_selectable(struct edid *edid)
 }
 EXPORT_SYMBOL(drm_rgb_quant_range_selectable);
 
-/**
- * drm_assign_hdmi_deep_color_info - detect whether monitor supports
- * hdmi deep color modes and update drm_display_info if so.
- * @edid: monitor EDID information
- * @info: Updated with maximum supported deep color bpc and color format
- *        if deep color supported.
- * @connector: DRM connector, used only for debug output
- *
- * Parse the CEA extension according to CEA-861-B.
- * Return true if HDMI deep color supported, false if not or unknown.
- */
-static bool drm_assign_hdmi_deep_color_info(struct edid *edid,
-                                            struct drm_display_info *info,
-                                            struct drm_connector *connector)
+static void drm_parse_hdmi_deep_color_info(struct drm_connector *connector,
+                                          const u8 *hdmi)
 {
-       u8 *edid_ext, *hdmi;
-       int i;
-       int start_offset, end_offset;
+       struct drm_display_info *info = &connector->display_info;
        unsigned int dc_bpc = 0;
 
-       edid_ext = drm_find_cea_extension(edid);
-       if (!edid_ext)
-               return false;
+       /* HDMI supports at least 8 bpc */
+       info->bpc = 8;
 
-       if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
-               return false;
+       if (cea_db_payload_len(hdmi) < 6)
+               return;
+
+       if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
+               dc_bpc = 10;
+               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
+               DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
+                         connector->name);
+       }
+
+       if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
+               dc_bpc = 12;
+               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
+               DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
+                         connector->name);
+       }
+
+       if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
+               dc_bpc = 16;
+               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
+               DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
+                         connector->name);
+       }
+
+       if (dc_bpc == 0) {
+               DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
+                         connector->name);
+               return;
+       }
+
+       DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
+                 connector->name, dc_bpc);
+       info->bpc = dc_bpc;
 
        /*
-        * Because HDMI identifier is in Vendor Specific Block,
-        * search it from all data blocks of CEA extension.
+        * Deep color support mandates RGB444 support for all video
+        * modes and forbids YCRCB422 support for all video modes per
+        * HDMI 1.3 spec.
         */
-       for_each_cea_db(edid_ext, i, start_offset, end_offset) {
-               if (cea_db_is_hdmi_vsdb(&edid_ext[i])) {
-                       /* HDMI supports at least 8 bpc */
-                       info->bpc = 8;
-
-                       hdmi = &edid_ext[i];
-                       if (cea_db_payload_len(hdmi) < 6)
-                               return false;
-
-                       if (hdmi[6] & DRM_EDID_HDMI_DC_30) {
-                               dc_bpc = 10;
-                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_30;
-                               DRM_DEBUG("%s: HDMI sink does deep color 30.\n",
-                                                 connector->name);
-                       }
+       info->color_formats = DRM_COLOR_FORMAT_RGB444;
 
-                       if (hdmi[6] & DRM_EDID_HDMI_DC_36) {
-                               dc_bpc = 12;
-                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_36;
-                               DRM_DEBUG("%s: HDMI sink does deep color 36.\n",
-                                                 connector->name);
-                       }
+       /* YCRCB444 is optional according to spec. */
+       if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
+               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+               DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
+                         connector->name);
+       }
 
-                       if (hdmi[6] & DRM_EDID_HDMI_DC_48) {
-                               dc_bpc = 16;
-                               info->edid_hdmi_dc_modes |= DRM_EDID_HDMI_DC_48;
-                               DRM_DEBUG("%s: HDMI sink does deep color 48.\n",
-                                                 connector->name);
-                       }
+       /*
+        * Spec says that if any deep color mode is supported at all,
+        * then deep color 36 bit must be supported.
+        */
+       if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
+               DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
+                         connector->name);
+       }
+}
 
-                       if (dc_bpc > 0) {
-                               DRM_DEBUG("%s: Assigning HDMI sink color depth as %d bpc.\n",
-                                                 connector->name, dc_bpc);
-                               info->bpc = dc_bpc;
-
-                               /*
-                                * Deep color support mandates RGB444 support for all video
-                                * modes and forbids YCRCB422 support for all video modes per
-                                * HDMI 1.3 spec.
-                                */
-                               info->color_formats = DRM_COLOR_FORMAT_RGB444;
-
-                               /* YCRCB444 is optional according to spec. */
-                               if (hdmi[6] & DRM_EDID_HDMI_DC_Y444) {
-                                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
-                                       DRM_DEBUG("%s: HDMI sink does YCRCB444 in deep color.\n",
-                                                         connector->name);
-                               }
+static void
+drm_parse_hdmi_vsdb_video(struct drm_connector *connector, const u8 *db)
+{
+       struct drm_display_info *info = &connector->display_info;
+       u8 len = cea_db_payload_len(db);
 
-                               /*
-                                * Spec says that if any deep color mode is supported at all,
-                                * then deep color 36 bit must be supported.
-                                */
-                               if (!(hdmi[6] & DRM_EDID_HDMI_DC_36)) {
-                                       DRM_DEBUG("%s: HDMI sink should do DC_36, but does not!\n",
-                                                         connector->name);
-                               }
+       if (len >= 6)
+               info->dvi_dual = db[6] & 1;
+       if (len >= 7)
+               info->max_tmds_clock = db[7] * 5000;
 
-                               return true;
-                       }
-                       else {
-                               DRM_DEBUG("%s: No deep color support on this HDMI sink.\n",
-                                                 connector->name);
-                       }
-               }
-       }
+       DRM_DEBUG_KMS("HDMI: DVI dual %d, "
+                     "max TMDS clock %d kHz\n",
+                     info->dvi_dual,
+                     info->max_tmds_clock);
 
-       return false;
+       drm_parse_hdmi_deep_color_info(connector, db);
 }
 
-/**
- * drm_add_display_info - pull display info out if present
- * @edid: EDID data
- * @info: display info (attached to connector)
- * @connector: connector whose edid is used to build display info
- *
- * Grab any available display info and stuff it into the drm_display_info
- * structure that's part of the connector.  Useful for tracking bpp and
- * color spaces.
- */
-static void drm_add_display_info(struct edid *edid,
-                                 struct drm_display_info *info,
-                                 struct drm_connector *connector)
+static void drm_parse_cea_ext(struct drm_connector *connector,
+                             struct edid *edid)
 {
-       u8 *edid_ext;
+       struct drm_display_info *info = &connector->display_info;
+       const u8 *edid_ext;
+       int i, start, end;
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               return;
+
+       info->cea_rev = edid_ext[1];
+
+       /* The existence of a CEA block should imply RGB support */
+       info->color_formats = DRM_COLOR_FORMAT_RGB444;
+       if (edid_ext[3] & EDID_CEA_YCRCB444)
+               info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
+       if (edid_ext[3] & EDID_CEA_YCRCB422)
+               info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
+
+       if (cea_db_offsets(edid_ext, &start, &end))
+               return;
+
+       for_each_cea_db(edid_ext, i, start, end) {
+               const u8 *db = &edid_ext[i];
+
+               if (cea_db_is_hdmi_vsdb(db))
+                       drm_parse_hdmi_vsdb_video(connector, db);
+       }
+}
+
+static void drm_add_display_info(struct drm_connector *connector,
+                                struct edid *edid)
+{
+       struct drm_display_info *info = &connector->display_info;
 
        info->width_mm = edid->width_cm * 10;
        info->height_mm = edid->height_cm * 10;
@@ -3844,6 +3848,9 @@ static void drm_add_display_info(struct edid *edid,
        /* driver figures it out in this case */
        info->bpc = 0;
        info->color_formats = 0;
+       info->cea_rev = 0;
+       info->max_tmds_clock = 0;
+       info->dvi_dual = false;
 
        if (edid->revision < 3)
                return;
@@ -3851,21 +3858,7 @@ static void drm_add_display_info(struct edid *edid,
        if (!(edid->input & DRM_EDID_INPUT_DIGITAL))
                return;
 
-       /* Get data from CEA blocks if present */
-       edid_ext = drm_find_cea_extension(edid);
-       if (edid_ext) {
-               info->cea_rev = edid_ext[1];
-
-               /* The existence of a CEA block should imply RGB support */
-               info->color_formats = DRM_COLOR_FORMAT_RGB444;
-               if (edid_ext[3] & EDID_CEA_YCRCB444)
-                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB444;
-               if (edid_ext[3] & EDID_CEA_YCRCB422)
-                       info->color_formats |= DRM_COLOR_FORMAT_YCRCB422;
-       }
-
-       /* HDMI deep color modes supported? Assign to info, if so */
-       drm_assign_hdmi_deep_color_info(edid, info, connector);
+       drm_parse_cea_ext(connector, edid);
 
        /*
         * Digital sink with "DFP 1.x compliant TMDS" according to EDID 1.3?
@@ -4052,7 +4045,9 @@ static int add_displayid_detailed_modes(struct drm_connector *connector,
  * @connector: connector we're probing
  * @edid: EDID data
  *
- * Add the specified modes to the connector's mode list.
+ * Add the specified modes to the connector's mode list. Also fills out the
+ * &drm_display_info structure in @connector with any information which can be
+ * derived from the edid.
  *
  * Return: The number of modes added or 0 if we couldn't find any.
  */
@@ -4099,7 +4094,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
        if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
                edid_fixup_preferred(connector, quirks);
 
-       drm_add_display_info(edid, &connector->display_info, connector);
+       drm_add_display_info(connector, edid);
 
        if (quirks & EDID_QUIRK_FORCE_6BPC)
                connector->display_info.bpc = 6;
diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
new file mode 100644 (file)
index 0000000..5c06771
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include <drm/drmP.h>
+#include <drm/drm_encoder.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * Encoders represent the connecting element between the CRTC (as the overall
+ * pixel pipeline, represented by struct &drm_crtc) and the connectors (as the
+ * generic sink entity, represented by struct &drm_connector). An encoder takes
+ * pixel data from a CRTC and converts it to a format suitable for any attached
+ * connector. Encoders are objects exposed to userspace, originally to allow
+ * userspace to infer cloning and connector/CRTC restrictions. Unfortunately
+ * almost all drivers get this wrong, making the uabi pretty much useless. On
+ * top of that the exposed restrictions are too simple for today's hardware, and
+ * the recommended way to infer restrictions is by using the
+ * DRM_MODE_ATOMIC_TEST_ONLY flag for the atomic IOCTL.
+ *
+ * Otherwise encoders aren't used in the uapi at all (any modeset request from
+ * userspace directly connects a connector with a CRTC), drivers are therefore
+ * free to use them however they wish. Modeset helper libraries make strong use
+ * of encoders to facilitate code sharing. But for more complex settings it is
+ * usually better to move shared code into a separate &drm_bridge. Compared to
+ * encoders, bridges also have the benefit of being purely an internal
+ * abstraction since they are not exposed to userspace at all.
+ *
+ * Encoders are initialized with drm_encoder_init() and cleaned up using
+ * drm_encoder_cleanup().
+ */
+static const struct drm_prop_enum_list drm_encoder_enum_list[] = {
+       { DRM_MODE_ENCODER_NONE, "None" },
+       { DRM_MODE_ENCODER_DAC, "DAC" },
+       { DRM_MODE_ENCODER_TMDS, "TMDS" },
+       { DRM_MODE_ENCODER_LVDS, "LVDS" },
+       { DRM_MODE_ENCODER_TVDAC, "TV" },
+       { DRM_MODE_ENCODER_VIRTUAL, "Virtual" },
+       { DRM_MODE_ENCODER_DSI, "DSI" },
+       { DRM_MODE_ENCODER_DPMST, "DP MST" },
+       { DRM_MODE_ENCODER_DPI, "DPI" },
+};
+
+int drm_encoder_register_all(struct drm_device *dev)
+{
+       struct drm_encoder *encoder;
+       int ret = 0;
+
+       drm_for_each_encoder(encoder, dev) {
+               if (encoder->funcs->late_register)
+                       ret = encoder->funcs->late_register(encoder);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+void drm_encoder_unregister_all(struct drm_device *dev)
+{
+       struct drm_encoder *encoder;
+
+       drm_for_each_encoder(encoder, dev) {
+               if (encoder->funcs->early_unregister)
+                       encoder->funcs->early_unregister(encoder);
+       }
+}
+
+/**
+ * drm_encoder_init - Init a preallocated encoder
+ * @dev: drm device
+ * @encoder: the encoder to init
+ * @funcs: callbacks for this encoder
+ * @encoder_type: user visible type of the encoder
+ * @name: printf style format string for the encoder name, or NULL for default name
+ *
+ * Initialises a preallocated encoder. Encoder should be subclassed as part of
+ * driver encoder objects. At driver unload time drm_encoder_cleanup() should be
+ * called from the driver's destroy hook in &drm_encoder_funcs.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_encoder_init(struct drm_device *dev,
+                    struct drm_encoder *encoder,
+                    const struct drm_encoder_funcs *funcs,
+                    int encoder_type, const char *name, ...)
+{
+       int ret;
+
+       drm_modeset_lock_all(dev);
+
+       ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+       if (ret)
+               goto out_unlock;
+
+       encoder->dev = dev;
+       encoder->encoder_type = encoder_type;
+       encoder->funcs = funcs;
+       if (name) {
+               va_list ap;
+
+               va_start(ap, name);
+               encoder->name = kvasprintf(GFP_KERNEL, name, ap);
+               va_end(ap);
+       } else {
+               encoder->name = kasprintf(GFP_KERNEL, "%s-%d",
+                                         drm_encoder_enum_list[encoder_type].name,
+                                         encoder->base.id);
+       }
+       if (!encoder->name) {
+               ret = -ENOMEM;
+               goto out_put;
+       }
+
+       list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
+       encoder->index = dev->mode_config.num_encoder++;
+
+out_put:
+       if (ret)
+               drm_mode_object_unregister(dev, &encoder->base);
+
+out_unlock:
+       drm_modeset_unlock_all(dev);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_encoder_init);
+
+/**
+ * drm_encoder_cleanup - cleans up an initialised encoder
+ * @encoder: encoder to cleanup
+ *
+ * Cleans up the encoder but doesn't free the object.
+ */
+void drm_encoder_cleanup(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+
+       /* Note that the encoder_list is considered to be static; should we
+        * remove the drm_encoder at runtime we would have to decrement all
+        * the indices on the drm_encoder after us in the encoder_list.
+        */
+
+       drm_modeset_lock_all(dev);
+       drm_mode_object_unregister(dev, &encoder->base);
+       kfree(encoder->name);
+       list_del(&encoder->head);
+       dev->mode_config.num_encoder--;
+       drm_modeset_unlock_all(dev);
+
+       memset(encoder, 0, sizeof(*encoder));
+}
+EXPORT_SYMBOL(drm_encoder_cleanup);
+
+static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
+{
+       struct drm_connector *connector;
+       struct drm_device *dev = encoder->dev;
+       bool uses_atomic = false;
+
+       /* For atomic drivers only state objects are synchronously updated and
+        * protected by modeset locks, so check those first. */
+       drm_for_each_connector(connector, dev) {
+               if (!connector->state)
+                       continue;
+
+               uses_atomic = true;
+
+               if (connector->state->best_encoder != encoder)
+                       continue;
+
+               return connector->state->crtc;
+       }
+
+       /* Don't return stale data (e.g. pending async disable). */
+       if (uses_atomic)
+               return NULL;
+
+       return encoder->crtc;
+}
+
+int drm_mode_getencoder(struct drm_device *dev, void *data,
+                       struct drm_file *file_priv)
+{
+       struct drm_mode_get_encoder *enc_resp = data;
+       struct drm_encoder *encoder;
+       struct drm_crtc *crtc;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       encoder = drm_encoder_find(dev, enc_resp->encoder_id);
+       if (!encoder)
+               return -ENOENT;
+
+       drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+       crtc = drm_encoder_get_crtc(encoder);
+       if (crtc)
+               enc_resp->crtc_id = crtc->base.id;
+       else
+               enc_resp->crtc_id = 0;
+       drm_modeset_unlock(&dev->mode_config.connection_mutex);
+
+       enc_resp->encoder_type = encoder->encoder_type;
+       enc_resp->encoder_id = encoder->base.id;
+       enc_resp->possible_crtcs = encoder->possible_crtcs;
+       enc_resp->possible_clones = encoder->possible_clones;
+
+       return 0;
+}
index 0a06f91..03414bd 100644 (file)
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
+#include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/sysrq.h>
 #include <linux/slab.h>
-#include <linux/fb.h>
 #include <linux/module.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -41,6 +41,8 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 
+#include "drm_crtc_helper_internal.h"
+
 static bool drm_fbdev_emulation = true;
 module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
 MODULE_PARM_DESC(fbdev_emulation,
@@ -335,7 +337,7 @@ retry:
                        goto fail;
                }
 
-               plane_state->rotation = BIT(DRM_ROTATE_0);
+               plane_state->rotation = DRM_ROTATE_0;
 
                plane->old_fb = plane->fb;
                plane_mask |= 1 << drm_plane_index(plane);
@@ -395,7 +397,7 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
                if (dev->mode_config.rotation_property) {
                        drm_mode_plane_set_obj_prop(plane,
                                                    dev->mode_config.rotation_property,
-                                                   BIT(DRM_ROTATE_0));
+                                                   DRM_ROTATE_0);
                }
        }
 
@@ -618,6 +620,16 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
        kfree(helper->crtc_info);
 }
 
+static void drm_fb_helper_resume_worker(struct work_struct *work)
+{
+       struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
+                                                   resume_work);
+
+       console_lock();
+       fb_set_suspend(helper->fbdev, 0);
+       console_unlock();
+}
+
 static void drm_fb_helper_dirty_work(struct work_struct *work)
 {
        struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
@@ -649,6 +661,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
 {
        INIT_LIST_HEAD(&helper->kernel_fb_list);
        spin_lock_init(&helper->dirty_lock);
+       INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
        INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
        helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
        helper->funcs = funcs;
@@ -1024,17 +1037,65 @@ EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
 /**
  * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
  * @fb_helper: driver-allocated fbdev helper
- * @state: desired state, zero to resume, non-zero to suspend
+ * @suspend: whether to suspend or resume
  *
- * A wrapper around fb_set_suspend implemented by fbdev core
+ * A wrapper around fb_set_suspend implemented by fbdev core.
+ * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take
+ * the lock yourself
  */
-void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state)
+void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
 {
        if (fb_helper && fb_helper->fbdev)
-               fb_set_suspend(fb_helper->fbdev, state);
+               fb_set_suspend(fb_helper->fbdev, suspend);
 }
 EXPORT_SYMBOL(drm_fb_helper_set_suspend);
 
+/**
+ * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
+ *                                      takes the console lock
+ * @fb_helper: driver-allocated fbdev helper
+ * @suspend: whether to suspend or resume
+ *
+ * A wrapper around fb_set_suspend() that takes the console lock. If the lock
+ * isn't available on resume, a worker is tasked with waiting for the lock
+ * to become available. The console lock can be pretty contented on resume
+ * due to all the printk activity.
+ *
+ * This function can be called multiple times with the same state since
+ * &fb_info->state is checked to see if fbdev is running or not before locking.
+ *
+ * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.
+ */
+void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+                                       bool suspend)
+{
+       if (!fb_helper || !fb_helper->fbdev)
+               return;
+
+       /* make sure there's no pending/ongoing resume */
+       flush_work(&fb_helper->resume_work);
+
+       if (suspend) {
+               if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
+                       return;
+
+               console_lock();
+
+       } else {
+               if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
+                       return;
+
+               if (!console_trylock()) {
+                       schedule_work(&fb_helper->resume_work);
+                       return;
+               }
+       }
+
+       fb_set_suspend(fb_helper->fbdev, suspend);
+       console_unlock();
+}
+EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
+
 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
                     u16 blue, u16 regno, struct fb_info *info)
 {
@@ -2194,7 +2255,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
  * @fb_helper: the drm_fb_helper
  *
  * Scan the connectors attached to the fb_helper and try to put together a
- * setup after *notification of a change in output configuration.
+ * setup after notification of a change in output configuration.
  *
  * Called at runtime, takes the mode config locks to be able to check/change the
  * modeset configuration. Must be run from process context (which usually means
index 323c238..e84faec 100644 (file)
@@ -92,7 +92,7 @@ static int drm_setup(struct drm_device * dev)
        int ret;
 
        if (dev->driver->firstopen &&
-           !drm_core_check_feature(dev, DRIVER_MODESET)) {
+           drm_core_check_feature(dev, DRIVER_LEGACY)) {
                ret = dev->driver->firstopen(dev);
                if (ret != 0)
                        return ret;
@@ -199,7 +199,6 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
 
        filp->private_data = priv;
        priv->filp = filp;
-       priv->uid = current_euid();
        priv->pid = get_pid(task_pid(current));
        priv->minor = minor;
 
@@ -346,7 +345,7 @@ void drm_lastclose(struct drm_device * dev)
                dev->driver->lastclose(dev);
        DRM_DEBUG("driver lastclose completed\n");
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
                drm_legacy_dev_reinit(dev);
 }
 
@@ -389,7 +388,7 @@ int drm_release(struct inode *inode, struct file *filp)
                  (long)old_encode_dev(file_priv->minor->kdev->devt),
                  dev->open_count);
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
                drm_legacy_lock_release(dev, filp);
 
        if (drm_core_check_feature(dev, DRIVER_HAVE_DMA))
index 0645c85..29c56b4 100644 (file)
@@ -35,20 +35,61 @@ static char printable_char(int c)
        return isascii(c) && isprint(c) ? c : '?';
 }
 
+/**
+ * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description
+ * @bpp: bits per pixels
+ * @depth: bit depth per pixel
+ *
+ * Computes a drm fourcc pixel format code for the given @bpp/@depth values.
+ * Useful in fbdev emulation code, since that deals in those values.
+ */
+uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
+{
+       uint32_t fmt;
+
+       switch (bpp) {
+       case 8:
+               fmt = DRM_FORMAT_C8;
+               break;
+       case 16:
+               if (depth == 15)
+                       fmt = DRM_FORMAT_XRGB1555;
+               else
+                       fmt = DRM_FORMAT_RGB565;
+               break;
+       case 24:
+               fmt = DRM_FORMAT_RGB888;
+               break;
+       case 32:
+               if (depth == 24)
+                       fmt = DRM_FORMAT_XRGB8888;
+               else if (depth == 30)
+                       fmt = DRM_FORMAT_XRGB2101010;
+               else
+                       fmt = DRM_FORMAT_ARGB8888;
+               break;
+       default:
+               DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
+               fmt = DRM_FORMAT_XRGB8888;
+               break;
+       }
+
+       return fmt;
+}
+EXPORT_SYMBOL(drm_mode_legacy_fb_format);
+
 /**
  * drm_get_format_name - return a string for drm fourcc format
  * @format: format to compute name of
  *
- * Note that the buffer used by this function is globally shared and owned by
- * the function itself.
- *
- * FIXME: This isn't really multithreading safe.
+ * Note that the buffer returned by this function is owned by the caller
+ * and will need to be freed using kfree().
  */
-const char *drm_get_format_name(uint32_t format)
+char *drm_get_format_name(uint32_t format)
 {
-       static char buf[32];
+       char *buf = kmalloc(32, GFP_KERNEL);
 
-       snprintf(buf, sizeof(buf),
+       snprintf(buf, 32,
                 "%c%c%c%c %s-endian (0x%08x)",
                 printable_char(format & 0xff),
                 printable_char((format >> 8) & 0xff),
@@ -73,6 +114,8 @@ EXPORT_SYMBOL(drm_get_format_name);
 void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                          int *bpp)
 {
+       char *format_name;
+
        switch (format) {
        case DRM_FORMAT_C8:
        case DRM_FORMAT_RGB332:
@@ -127,8 +170,9 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
                *bpp = 32;
                break;
        default:
-               DRM_DEBUG_KMS("unsupported pixel format %s\n",
-                             drm_get_format_name(format));
+               format_name = drm_get_format_name(format);
+               DRM_DEBUG_KMS("unsupported pixel format %s\n", format_name);
+               kfree(format_name);
                *depth = 0;
                *bpp = 0;
                break;
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
new file mode 100644 (file)
index 0000000..398efd6
--- /dev/null
@@ -0,0 +1,857 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include <drm/drmP.h>
+#include <drm/drm_auth.h>
+#include <drm/drm_framebuffer.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * Frame buffers are abstract memory objects that provide a source of pixels to
+ * scanout to a CRTC. Applications explicitly request the creation of frame
+ * buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque
+ * handle that can be passed to the KMS CRTC control, plane configuration and
+ * page flip functions.
+ *
+ * Frame buffers rely on the underlying memory manager for allocating backing
+ * storage. When creating a frame buffer applications pass a memory handle
+ * (or a list of memory handles for multi-planar formats) through the
+ * struct &drm_mode_fb_cmd2 argument. For drivers using GEM as their userspace
+ * buffer management interface this would be a GEM handle.  Drivers are however
+ * free to use their own backing storage object handles, e.g. vmwgfx directly
+ * exposes special TTM handles to userspace and so expects TTM handles in the
+ * create ioctl and not GEM handles.
+ *
+ * Framebuffers are tracked with struct &drm_framebuffer. They are published
+ * using drm_framebuffer_init() - after calling that function userspace can use
+ * and access the framebuffer object. The helper function
+ * drm_helper_mode_fill_fb_struct() can be used to pre-fill the required
+ * metadata fields.
+ *
+ * The lifetime of a drm framebuffer is controlled with a reference count,
+ * drivers can grab additional references with drm_framebuffer_reference() and
+ * drop them again with drm_framebuffer_unreference(). For driver-private
+ * framebuffers for which the last reference is never dropped (e.g. for the
+ * fbdev framebuffer when the struct struct &drm_framebuffer is embedded into
+ * the fbdev helper struct) drivers can manually clean up a framebuffer at
+ * module unload time with drm_framebuffer_unregister_private(). But doing this
+ * is not recommended, and it's better to have a normal free-standing struct
+ * &drm_framebuffer.
+ */
+
+int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
+                                    uint32_t src_w, uint32_t src_h,
+                                    const struct drm_framebuffer *fb)
+{
+       unsigned int fb_width, fb_height;
+
+       fb_width = fb->width << 16;
+       fb_height = fb->height << 16;
+
+       /* Make sure source coordinates are inside the fb. */
+       if (src_w > fb_width ||
+           src_x > fb_width - src_w ||
+           src_h > fb_height ||
+           src_y > fb_height - src_h) {
+               DRM_DEBUG_KMS("Invalid source coordinates "
+                             "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
+                             src_w >> 16, ((src_w & 0xffff) * 15625) >> 10,
+                             src_h >> 16, ((src_h & 0xffff) * 15625) >> 10,
+                             src_x >> 16, ((src_x & 0xffff) * 15625) >> 10,
+                             src_y >> 16, ((src_y & 0xffff) * 15625) >> 10);
+               return -ENOSPC;
+       }
+
+       return 0;
+}
+
+/**
+ * drm_mode_addfb - add an FB to the graphics configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Add a new FB to the specified CRTC, given a user request. This is the
+ * original addfb ioctl which only supported RGB formats.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_addfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_fb_cmd *or = data;
+       struct drm_mode_fb_cmd2 r = {};
+       int ret;
+
+       /* convert to new format and call new ioctl */
+       r.fb_id = or->fb_id;
+       r.width = or->width;
+       r.height = or->height;
+       r.pitches[0] = or->pitch;
+       r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
+       r.handles[0] = or->handle;
+
+       ret = drm_mode_addfb2(dev, &r, file_priv);
+       if (ret)
+               return ret;
+
+       or->fb_id = r.fb_id;
+
+       return 0;
+}
+
+static int format_check(const struct drm_mode_fb_cmd2 *r)
+{
+       uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
+       char *format_name;
+
+       switch (format) {
+       case DRM_FORMAT_C8:
+       case DRM_FORMAT_RGB332:
+       case DRM_FORMAT_BGR233:
+       case DRM_FORMAT_XRGB4444:
+       case DRM_FORMAT_XBGR4444:
+       case DRM_FORMAT_RGBX4444:
+       case DRM_FORMAT_BGRX4444:
+       case DRM_FORMAT_ARGB4444:
+       case DRM_FORMAT_ABGR4444:
+       case DRM_FORMAT_RGBA4444:
+       case DRM_FORMAT_BGRA4444:
+       case DRM_FORMAT_XRGB1555:
+       case DRM_FORMAT_XBGR1555:
+       case DRM_FORMAT_RGBX5551:
+       case DRM_FORMAT_BGRX5551:
+       case DRM_FORMAT_ARGB1555:
+       case DRM_FORMAT_ABGR1555:
+       case DRM_FORMAT_RGBA5551:
+       case DRM_FORMAT_BGRA5551:
+       case DRM_FORMAT_RGB565:
+       case DRM_FORMAT_BGR565:
+       case DRM_FORMAT_RGB888:
+       case DRM_FORMAT_BGR888:
+       case DRM_FORMAT_XRGB8888:
+       case DRM_FORMAT_XBGR8888:
+       case DRM_FORMAT_RGBX8888:
+       case DRM_FORMAT_BGRX8888:
+       case DRM_FORMAT_ARGB8888:
+       case DRM_FORMAT_ABGR8888:
+       case DRM_FORMAT_RGBA8888:
+       case DRM_FORMAT_BGRA8888:
+       case DRM_FORMAT_XRGB2101010:
+       case DRM_FORMAT_XBGR2101010:
+       case DRM_FORMAT_RGBX1010102:
+       case DRM_FORMAT_BGRX1010102:
+       case DRM_FORMAT_ARGB2101010:
+       case DRM_FORMAT_ABGR2101010:
+       case DRM_FORMAT_RGBA1010102:
+       case DRM_FORMAT_BGRA1010102:
+       case DRM_FORMAT_YUYV:
+       case DRM_FORMAT_YVYU:
+       case DRM_FORMAT_UYVY:
+       case DRM_FORMAT_VYUY:
+       case DRM_FORMAT_AYUV:
+       case DRM_FORMAT_NV12:
+       case DRM_FORMAT_NV21:
+       case DRM_FORMAT_NV16:
+       case DRM_FORMAT_NV61:
+       case DRM_FORMAT_NV24:
+       case DRM_FORMAT_NV42:
+       case DRM_FORMAT_YUV410:
+       case DRM_FORMAT_YVU410:
+       case DRM_FORMAT_YUV411:
+       case DRM_FORMAT_YVU411:
+       case DRM_FORMAT_YUV420:
+       case DRM_FORMAT_YVU420:
+       case DRM_FORMAT_YUV422:
+       case DRM_FORMAT_YVU422:
+       case DRM_FORMAT_YUV444:
+       case DRM_FORMAT_YVU444:
+               return 0;
+       default:
+               format_name = drm_get_format_name(r->pixel_format);
+               DRM_DEBUG_KMS("invalid pixel format %s\n", format_name);
+               kfree(format_name);
+               return -EINVAL;
+       }
+}
+
+static int framebuffer_check(const struct drm_mode_fb_cmd2 *r)
+{
+       int ret, hsub, vsub, num_planes, i;
+
+       ret = format_check(r);
+       if (ret) {
+               char *format_name = drm_get_format_name(r->pixel_format);
+               DRM_DEBUG_KMS("bad framebuffer format %s\n", format_name);
+               kfree(format_name);
+               return ret;
+       }
+
+       hsub = drm_format_horz_chroma_subsampling(r->pixel_format);
+       vsub = drm_format_vert_chroma_subsampling(r->pixel_format);
+       num_planes = drm_format_num_planes(r->pixel_format);
+
+       if (r->width == 0 || r->width % hsub) {
+               DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width);
+               return -EINVAL;
+       }
+
+       if (r->height == 0 || r->height % vsub) {
+               DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < num_planes; i++) {
+               unsigned int width = r->width / (i != 0 ? hsub : 1);
+               unsigned int height = r->height / (i != 0 ? vsub : 1);
+               unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i);
+
+               if (!r->handles[i]) {
+                       DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i);
+                       return -EINVAL;
+               }
+
+               if ((uint64_t) width * cpp > UINT_MAX)
+                       return -ERANGE;
+
+               if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX)
+                       return -ERANGE;
+
+               if (r->pitches[i] < width * cpp) {
+                       DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i);
+                       return -EINVAL;
+               }
+
+               if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) {
+                       DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n",
+                                     r->modifier[i], i);
+                       return -EINVAL;
+               }
+
+               /* modifier specific checks: */
+               switch (r->modifier[i]) {
+               case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE:
+                       /* NOTE: the pitch restriction may be lifted later if it turns
+                        * out that no hw has this restriction:
+                        */
+                       if (r->pixel_format != DRM_FORMAT_NV12 ||
+                                       width % 128 || height % 32 ||
+                                       r->pitches[i] % 128) {
+                               DRM_DEBUG_KMS("bad modifier data for plane %d\n", i);
+                               return -EINVAL;
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+       }
+
+       for (i = num_planes; i < 4; i++) {
+               if (r->modifier[i]) {
+                       DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i);
+                       return -EINVAL;
+               }
+
+               /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */
+               if (!(r->flags & DRM_MODE_FB_MODIFIERS))
+                       continue;
+
+               if (r->handles[i]) {
+                       DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i);
+                       return -EINVAL;
+               }
+
+               if (r->pitches[i]) {
+                       DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i);
+                       return -EINVAL;
+               }
+
+               if (r->offsets[i]) {
+                       DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+struct drm_framebuffer *
+drm_internal_framebuffer_create(struct drm_device *dev,
+                               const struct drm_mode_fb_cmd2 *r,
+                               struct drm_file *file_priv)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       struct drm_framebuffer *fb;
+       int ret;
+
+       if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) {
+               DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if ((config->min_width > r->width) || (r->width > config->max_width)) {
+               DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n",
+                         r->width, config->min_width, config->max_width);
+               return ERR_PTR(-EINVAL);
+       }
+       if ((config->min_height > r->height) || (r->height > config->max_height)) {
+               DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n",
+                         r->height, config->min_height, config->max_height);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (r->flags & DRM_MODE_FB_MODIFIERS &&
+           !dev->mode_config.allow_fb_modifiers) {
+               DRM_DEBUG_KMS("driver does not support fb modifiers\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       ret = framebuffer_check(r);
+       if (ret)
+               return ERR_PTR(ret);
+
+       fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
+       if (IS_ERR(fb)) {
+               DRM_DEBUG_KMS("could not create framebuffer\n");
+               return fb;
+       }
+
+       return fb;
+}
+
+/**
+ * drm_mode_addfb2 - add an FB to the graphics configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Add a new FB to the specified CRTC, given a user request with format. This is
+ * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
+ * and uses fourcc codes as pixel format specifiers.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_addfb2(struct drm_device *dev,
+                   void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_fb_cmd2 *r = data;
+       struct drm_framebuffer *fb;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       fb = drm_internal_framebuffer_create(dev, r, file_priv);
+       if (IS_ERR(fb))
+               return PTR_ERR(fb);
+
+       DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
+       r->fb_id = fb->base.id;
+
+       /* Transfer ownership to the filp for reaping on close */
+       mutex_lock(&file_priv->fbs_lock);
+       list_add(&fb->filp_head, &file_priv->fbs);
+       mutex_unlock(&file_priv->fbs_lock);
+
+       return 0;
+}
+
+struct drm_mode_rmfb_work {
+       struct work_struct work;
+       struct list_head fbs;
+};
+
+static void drm_mode_rmfb_work_fn(struct work_struct *w)
+{
+       struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work);
+
+       while (!list_empty(&arg->fbs)) {
+               struct drm_framebuffer *fb =
+                       list_first_entry(&arg->fbs, typeof(*fb), filp_head);
+
+               list_del_init(&fb->filp_head);
+               drm_framebuffer_remove(fb);
+       }
+}
+
+/**
+ * drm_mode_rmfb - remove an FB from the configuration
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Remove the FB specified by the user.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_rmfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
+{
+       struct drm_framebuffer *fb = NULL;
+       struct drm_framebuffer *fbl = NULL;
+       uint32_t *id = data;
+       int found = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       fb = drm_framebuffer_lookup(dev, *id);
+       if (!fb)
+               return -ENOENT;
+
+       mutex_lock(&file_priv->fbs_lock);
+       list_for_each_entry(fbl, &file_priv->fbs, filp_head)
+               if (fb == fbl)
+                       found = 1;
+       if (!found) {
+               mutex_unlock(&file_priv->fbs_lock);
+               goto fail_unref;
+       }
+
+       list_del_init(&fb->filp_head);
+       mutex_unlock(&file_priv->fbs_lock);
+
+       /* drop the reference we picked up in framebuffer lookup */
+       drm_framebuffer_unreference(fb);
+
+       /*
+        * we now own the reference that was stored in the fbs list
+        *
+        * drm_framebuffer_remove may fail with -EINTR on pending signals,
+        * so run this in a separate stack as there's no way to correctly
+        * handle this after the fb is already removed from the lookup table.
+        */
+       if (drm_framebuffer_read_refcount(fb) > 1) {
+               struct drm_mode_rmfb_work arg;
+
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+               INIT_LIST_HEAD(&arg.fbs);
+               list_add_tail(&fb->filp_head, &arg.fbs);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
+       } else
+               drm_framebuffer_unreference(fb);
+
+       return 0;
+
+fail_unref:
+       drm_framebuffer_unreference(fb);
+       return -ENOENT;
+}
+
+/**
+ * drm_mode_getfb - get FB info
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Lookup the FB given its ID and return info about it.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_getfb(struct drm_device *dev,
+                  void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_fb_cmd *r = data;
+       struct drm_framebuffer *fb;
+       int ret;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       fb = drm_framebuffer_lookup(dev, r->fb_id);
+       if (!fb)
+               return -ENOENT;
+
+       r->height = fb->height;
+       r->width = fb->width;
+       r->depth = fb->depth;
+       r->bpp = fb->bits_per_pixel;
+       r->pitch = fb->pitches[0];
+       if (fb->funcs->create_handle) {
+               if (drm_is_current_master(file_priv) || capable(CAP_SYS_ADMIN) ||
+                   drm_is_control_client(file_priv)) {
+                       ret = fb->funcs->create_handle(fb, file_priv,
+                                                      &r->handle);
+               } else {
+                       /* GET_FB() is an unprivileged ioctl so we must not
+                        * return a buffer-handle to non-master processes! For
+                        * backwards-compatibility reasons, we cannot make
+                        * GET_FB() privileged, so just return an invalid handle
+                        * for non-masters. */
+                       r->handle = 0;
+                       ret = 0;
+               }
+       } else {
+               ret = -ENODEV;
+       }
+
+       drm_framebuffer_unreference(fb);
+
+       return ret;
+}
+
+/**
+ * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB
+ * @dev: drm device for the ioctl
+ * @data: data pointer for the ioctl
+ * @file_priv: drm file for the ioctl call
+ *
+ * Lookup the FB and flush out the damaged area supplied by userspace as a clip
+ * rectangle list. Generic userspace which does frontbuffer rendering must call
+ * this ioctl to flush out the changes on manual-update display outputs, e.g.
+ * usb display-link, mipi manual update panels or edp panel self refresh modes.
+ *
+ * Modesetting drivers which always update the frontbuffer do not need to
+ * implement the corresponding ->dirty framebuffer callback.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_clip_rect __user *clips_ptr;
+       struct drm_clip_rect *clips = NULL;
+       struct drm_mode_fb_dirty_cmd *r = data;
+       struct drm_framebuffer *fb;
+       unsigned flags;
+       int num_clips;
+       int ret;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       fb = drm_framebuffer_lookup(dev, r->fb_id);
+       if (!fb)
+               return -ENOENT;
+
+       num_clips = r->num_clips;
+       clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
+
+       if (!num_clips != !clips_ptr) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
+
+       /* If userspace annotates copy, clips must come in pairs */
+       if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
+               ret = -EINVAL;
+               goto out_err1;
+       }
+
+       if (num_clips && clips_ptr) {
+               if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) {
+                       ret = -EINVAL;
+                       goto out_err1;
+               }
+               clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL);
+               if (!clips) {
+                       ret = -ENOMEM;
+                       goto out_err1;
+               }
+
+               ret = copy_from_user(clips, clips_ptr,
+                                    num_clips * sizeof(*clips));
+               if (ret) {
+                       ret = -EFAULT;
+                       goto out_err2;
+               }
+       }
+
+       if (fb->funcs->dirty) {
+               ret = fb->funcs->dirty(fb, file_priv, flags, r->color,
+                                      clips, num_clips);
+       } else {
+               ret = -ENOSYS;
+       }
+
+out_err2:
+       kfree(clips);
+out_err1:
+       drm_framebuffer_unreference(fb);
+
+       return ret;
+}
+
+/**
+ * drm_fb_release - remove and free the FBs on this file
+ * @priv: drm file for the ioctl
+ *
+ * Destroy all the FBs associated with @filp.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void drm_fb_release(struct drm_file *priv)
+{
+       struct drm_framebuffer *fb, *tfb;
+       struct drm_mode_rmfb_work arg;
+
+       INIT_LIST_HEAD(&arg.fbs);
+
+       /*
+        * When the file gets released that means no one else can access the fb
+        * list any more, so no need to grab fpriv->fbs_lock. And we need to
+        * avoid upsetting lockdep since the universal cursor code adds a
+        * framebuffer while holding mutex locks.
+        *
+        * Note that a real deadlock between fpriv->fbs_lock and the modeset
+        * locks is impossible here since no one else but this function can get
+        * at it any more.
+        */
+       list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
+               if (drm_framebuffer_read_refcount(fb) > 1) {
+                       list_move_tail(&fb->filp_head, &arg.fbs);
+               } else {
+                       list_del_init(&fb->filp_head);
+
+                       /* This drops the fpriv->fbs reference. */
+                       drm_framebuffer_unreference(fb);
+               }
+       }
+
+       if (!list_empty(&arg.fbs)) {
+               INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn);
+
+               schedule_work(&arg.work);
+               flush_work(&arg.work);
+               destroy_work_on_stack(&arg.work);
+       }
+}
+
+void drm_framebuffer_free(struct kref *kref)
+{
+       struct drm_framebuffer *fb =
+                       container_of(kref, struct drm_framebuffer, base.refcount);
+       struct drm_device *dev = fb->dev;
+
+       /*
+        * The lookup idr holds a weak reference, which has not necessarily been
+        * removed at this point. Check for that.
+        */
+       drm_mode_object_unregister(dev, &fb->base);
+
+       fb->funcs->destroy(fb);
+}
+
+/**
+ * drm_framebuffer_init - initialize a framebuffer
+ * @dev: DRM device
+ * @fb: framebuffer to be initialized
+ * @funcs: ... with these functions
+ *
+ * Allocates an ID for the framebuffer's parent mode object, sets its mode
+ * functions & device file and adds it to the master fd list.
+ *
+ * IMPORTANT:
+ * This functions publishes the fb and makes it available for concurrent access
+ * by other users. Which means by this point the fb _must_ be fully set up -
+ * since all the fb attributes are invariant over its lifetime, no further
+ * locking but only correct reference counting is required.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+                        const struct drm_framebuffer_funcs *funcs)
+{
+       int ret;
+
+       INIT_LIST_HEAD(&fb->filp_head);
+       fb->dev = dev;
+       fb->funcs = funcs;
+
+       ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB,
+                                     false, drm_framebuffer_free);
+       if (ret)
+               goto out;
+
+       mutex_lock(&dev->mode_config.fb_lock);
+       dev->mode_config.num_fb++;
+       list_add(&fb->head, &dev->mode_config.fb_list);
+       mutex_unlock(&dev->mode_config.fb_lock);
+
+       drm_mode_object_register(dev, &fb->base);
+out:
+       return ret;
+}
+EXPORT_SYMBOL(drm_framebuffer_init);
+
+/**
+ * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference
+ * @dev: drm device
+ * @id: id of the fb object
+ *
+ * If successful, this grabs an additional reference to the framebuffer -
+ * callers need to make sure to eventually unreference the returned framebuffer
+ * again, using @drm_framebuffer_unreference.
+ */
+struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
+                                              uint32_t id)
+{
+       struct drm_mode_object *obj;
+       struct drm_framebuffer *fb = NULL;
+
+       obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB);
+       if (obj)
+               fb = obj_to_fb(obj);
+       return fb;
+}
+EXPORT_SYMBOL(drm_framebuffer_lookup);
+
+/**
+ * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr
+ * @fb: fb to unregister
+ *
+ * Drivers need to call this when cleaning up driver-private framebuffers, e.g.
+ * those used for fbdev. Note that the caller must hold a reference of it's own,
+ * i.e. the object may not be destroyed through this call (since it'll lead to a
+ * locking inversion).
+ */
+void drm_framebuffer_unregister_private(struct drm_framebuffer *fb)
+{
+       struct drm_device *dev;
+
+       if (!fb)
+               return;
+
+       dev = fb->dev;
+
+       /* Mark fb as reaped and drop idr ref. */
+       drm_mode_object_unregister(dev, &fb->base);
+}
+EXPORT_SYMBOL(drm_framebuffer_unregister_private);
+
+/**
+ * drm_framebuffer_cleanup - remove a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * Cleanup framebuffer. This function is intended to be used from the drivers
+ * ->destroy callback. It can also be used to clean up driver private
+ * framebuffers embedded into a larger structure.
+ *
+ * Note that this function does not remove the fb from active usuage - if it is
+ * still used anywhere, hilarity can ensue since userspace could call getfb on
+ * the id and get back -EINVAL. Obviously no concern at driver unload time.
+ *
+ * Also, the framebuffer will not be removed from the lookup idr - for
+ * user-created framebuffers this will happen in in the rmfb ioctl. For
+ * driver-private objects (e.g. for fbdev) drivers need to explicitly call
+ * drm_framebuffer_unregister_private.
+ */
+void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
+{
+       struct drm_device *dev = fb->dev;
+
+       mutex_lock(&dev->mode_config.fb_lock);
+       list_del(&fb->head);
+       dev->mode_config.num_fb--;
+       mutex_unlock(&dev->mode_config.fb_lock);
+}
+EXPORT_SYMBOL(drm_framebuffer_cleanup);
+
+/**
+ * drm_framebuffer_remove - remove and unreference a framebuffer object
+ * @fb: framebuffer to remove
+ *
+ * Scans all the CRTCs and planes in @dev's mode_config.  If they're
+ * using @fb, removes it, setting it to NULL. Then drops the reference to the
+ * passed-in framebuffer. Might take the modeset locks.
+ *
+ * Note that this function optimizes the cleanup away if the caller holds the
+ * last reference to the framebuffer. It is also guaranteed to not take the
+ * modeset locks in this case.
+ */
+void drm_framebuffer_remove(struct drm_framebuffer *fb)
+{
+       struct drm_device *dev;
+       struct drm_crtc *crtc;
+       struct drm_plane *plane;
+
+       if (!fb)
+               return;
+
+       dev = fb->dev;
+
+       WARN_ON(!list_empty(&fb->filp_head));
+
+       /*
+        * drm ABI mandates that we remove any deleted framebuffers from active
+        * useage. But since most sane clients only remove framebuffers they no
+        * longer need, try to optimize this away.
+        *
+        * Since we're holding a reference ourselves, observing a refcount of 1
+        * means that we're the last holder and can skip it. Also, the refcount
+        * can never increase from 1 again, so we don't need any barriers or
+        * locks.
+        *
+        * Note that userspace could try to race with use and instate a new
+        * usage _after_ we've cleared all current ones. End result will be an
+        * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot
+        * in this manner.
+        */
+       if (drm_framebuffer_read_refcount(fb) > 1) {
+               drm_modeset_lock_all(dev);
+               /* remove from any CRTC */
+               drm_for_each_crtc(crtc, dev) {
+                       if (crtc->primary->fb == fb) {
+                               /* should turn off the crtc */
+                               if (drm_crtc_force_disable(crtc))
+                                       DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
+                       }
+               }
+
+               drm_for_each_plane(plane, dev) {
+                       if (plane->fb == fb)
+                               drm_plane_force_disable(plane);
+               }
+               drm_modeset_unlock_all(dev);
+       }
+
+       drm_framebuffer_unreference(fb);
+}
+EXPORT_SYMBOL(drm_framebuffer_remove);
index 9134ae1..465bacd 100644 (file)
@@ -257,7 +257,7 @@ drm_gem_object_release_handle(int id, void *ptr, void *data)
 
        if (drm_core_check_feature(dev, DRIVER_PRIME))
                drm_gem_remove_prime_handles(obj, file_priv);
-       drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
+       drm_vma_node_revoke(&obj->vma_node, file_priv);
 
        if (dev->driver->gem_close_object)
                dev->driver->gem_close_object(obj, file_priv);
@@ -372,7 +372,7 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
 
        handle = ret;
 
-       ret = drm_vma_node_allow(&obj->vma_node, file_priv->filp);
+       ret = drm_vma_node_allow(&obj->vma_node, file_priv);
        if (ret)
                goto err_remove;
 
@@ -386,7 +386,7 @@ drm_gem_handle_create_tail(struct drm_file *file_priv,
        return 0;
 
 err_revoke:
-       drm_vma_node_revoke(&obj->vma_node, file_priv->filp);
+       drm_vma_node_revoke(&obj->vma_node, file_priv);
 err_remove:
        spin_lock(&file_priv->table_lock);
        idr_remove(&file_priv->object_idr, handle);
@@ -991,7 +991,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
        if (!obj)
                return -EINVAL;
 
-       if (!drm_vma_node_is_allowed(node, filp)) {
+       if (!drm_vma_node_is_allowed(node, priv)) {
                drm_gem_object_unreference_unlocked(obj);
                return -EACCES;
        }
index 3d2e91c..b404287 100644 (file)
@@ -65,30 +65,34 @@ void drm_global_release(void)
 
 int drm_global_item_ref(struct drm_global_reference *ref)
 {
-       int ret;
+       int ret = 0;
        struct drm_global_item *item = &glob[ref->global_type];
 
        mutex_lock(&item->mutex);
        if (item->refcount == 0) {
-               item->object = kzalloc(ref->size, GFP_KERNEL);
-               if (unlikely(item->object == NULL)) {
+               ref->object = kzalloc(ref->size, GFP_KERNEL);
+               if (unlikely(ref->object == NULL)) {
                        ret = -ENOMEM;
-                       goto out_err;
+                       goto error_unlock;
                }
-
-               ref->object = item->object;
                ret = ref->init(ref);
                if (unlikely(ret != 0))
-                       goto out_err;
+                       goto error_free;
 
+               item->object = ref->object;
+       } else {
+               ref->object = item->object;
        }
+
        ++item->refcount;
-       ref->object = item->object;
        mutex_unlock(&item->mutex);
        return 0;
-out_err:
+
+error_free:
+       kfree(ref->object);
+       ref->object = NULL;
+error_unlock:
        mutex_unlock(&item->mutex);
-       item->object = NULL;
        return ret;
 }
 EXPORT_SYMBOL(drm_global_item_ref);
index 7b30b30..dae18e5 100644 (file)
@@ -142,7 +142,7 @@ int drm_ht_just_insert_please(struct drm_open_hash *ht, struct drm_hash_item *it
                              unsigned long add)
 {
        int ret;
-       unsigned long mask = (1 << bits) - 1;
+       unsigned long mask = (1UL << bits) - 1;
        unsigned long first, unshifted_key;
 
        unshifted_key = hash_long(seed, bits);
index 9ae353f..1df2d33 100644 (file)
@@ -80,6 +80,7 @@ int drm_clients_info(struct seq_file *m, void *data)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        struct drm_file *priv;
+       kuid_t uid;
 
        seq_printf(m,
                   "%20s %5s %3s master a %5s %10s\n",
@@ -98,13 +99,14 @@ int drm_clients_info(struct seq_file *m, void *data)
 
                rcu_read_lock(); /* locks pid_task()->comm */
                task = pid_task(priv->pid, PIDTYPE_PID);
+               uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
                seq_printf(m, "%20s %5d %3d   %c    %c %5d %10u\n",
                           task ? task->comm : "<unknown>",
                           pid_vnr(priv->pid),
                           priv->minor->index,
                           drm_is_current_master(priv) ? 'y' : 'n',
                           priv->authenticated ? 'y' : 'n',
-                          from_kuid_munged(seq_user_ns(m), priv->uid),
+                          from_kuid_munged(seq_user_ns(m), uid),
                           priv->magic);
                rcu_read_unlock();
        }
index b86dc9b..e66af28 100644 (file)
@@ -21,6 +21,9 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#define DRM_IF_MAJOR 1
+#define DRM_IF_MINOR 4
+
 /* drm_irq.c */
 extern unsigned int drm_timestamp_monotonic;
 
index a628975..867ab8c 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/export.h>
 
 #include <drm/drmP.h>
-#include <drm/drm_core.h>
 
 #define DRM_IOCTL_VERSION32            DRM_IOWR(0x00, drm_version32_t)
 #define DRM_IOCTL_GET_UNIQUE32         DRM_IOWR(0x01, drm_unique32_t)
@@ -346,6 +345,7 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd,
        struct drm_stats __user *stats;
        int i, err;
 
+       memset(&s32, 0, sizeof(drm_stats32_t));
        stats = compat_alloc_user_space(sizeof(*stats));
        if (!stats)
                return -EFAULT;
index 33af4a5..0ad2c47 100644 (file)
@@ -29,7 +29,6 @@
  */
 
 #include <drm/drmP.h>
-#include <drm/drm_core.h>
 #include <drm/drm_auth.h>
 #include "drm_legacy.h"
 #include "drm_internal.h"
@@ -189,9 +188,8 @@ static int drm_getclient(struct drm_device *dev, void *data,
         */
        if (client->idx == 0) {
                client->auth = file_priv->authenticated;
-               client->pid = pid_vnr(file_priv->pid);
-               client->uid = from_kuid_munged(current_user_ns(),
-                                              file_priv->uid);
+               client->pid = task_pid_vnr(current);
+               client->uid = overflowuid;
                client->magic = 0;
                client->iocs = 0;
 
@@ -228,6 +226,7 @@ static int drm_getstats(struct drm_device *dev, void *data,
 static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
 {
        struct drm_get_cap *req = data;
+       struct drm_crtc *crtc;
 
        req->value = 0;
        switch (req->capability) {
@@ -254,6 +253,13 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_
        case DRM_CAP_ASYNC_PAGE_FLIP:
                req->value = dev->mode_config.async_page_flip;
                break;
+       case DRM_CAP_PAGE_FLIP_TARGET:
+               req->value = 1;
+               drm_for_each_crtc(crtc, dev) {
+                       if (!crtc->funcs->page_flip_target)
+                               req->value = 0;
+               }
+               break;
        case DRM_CAP_CURSOR_WIDTH:
                if (dev->mode_config.cursor_width)
                        req->value = dev->mode_config.cursor_width;
@@ -714,9 +720,9 @@ long drm_ioctl(struct file *filp,
        if (ksize > in_size)
                memset(kdata + in_size, 0, ksize - in_size);
 
-       /* Enforce sane locking for kms driver ioctls. Core ioctls are
+       /* Enforce sane locking for modern driver ioctls. Core ioctls are
         * too messy still. */
-       if ((drm_core_check_feature(dev, DRIVER_MODESET) && is_driver_ioctl) ||
+       if ((!drm_core_check_feature(dev, DRIVER_LEGACY) && is_driver_ioctl) ||
            (ioctl->flags & DRM_UNLOCKED))
                retcode = func(dev, kdata, file_priv);
        else {
index 77f357b..b969a64 100644 (file)
@@ -482,7 +482,7 @@ int drm_irq_install(struct drm_device *dev, int irq)
                return ret;
        }
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
                vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);
 
        /* After installing handler */
@@ -491,7 +491,7 @@ int drm_irq_install(struct drm_device *dev, int irq)
 
        if (ret < 0) {
                dev->irq_enabled = false;
-               if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               if (drm_core_check_feature(dev, DRIVER_LEGACY))
                        vga_client_register(dev->pdev, NULL, NULL, NULL);
                free_irq(irq, dev);
        } else {
@@ -557,7 +557,7 @@ int drm_irq_uninstall(struct drm_device *dev)
 
        DRM_DEBUG("irq=%d\n", dev->irq);
 
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
                vga_client_register(dev->pdev, NULL, NULL, NULL);
 
        if (dev->driver->irq_uninstall)
@@ -592,7 +592,7 @@ int drm_control(struct drm_device *dev, void *data,
 
        if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))
                return 0;
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return 0;
        /* UMS was only ever supported on pci devices. */
        if (WARN_ON(!dev->pdev))
@@ -713,10 +713,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);
  * Negative value on error, failure or if not supported in current
  * video mode:
  *
- * -EINVAL   - Invalid CRTC.
- * -EAGAIN   - Temporary unavailable, e.g., called before initial modeset.
- * -ENOTSUPP - Function not supported in current display mode.
- * -EIO      - Failed, e.g., due to failed scanout position query.
+ * -EINVAL    Invalid CRTC.
+ * -EAGAIN    Temporary unavailable, e.g., called before initial modeset.
+ * -ENOTSUPP  Function not supported in current display mode.
+ * -EIO       Failed, e.g., due to failed scanout position query.
  *
  * Returns or'ed positive status flags on success:
  *
@@ -1008,6 +1008,31 @@ static void send_vblank_event(struct drm_device *dev,
  * period. This helper function implements exactly the required vblank arming
  * behaviour.
  *
+ * NOTE: Drivers using this to send out the event in struct &drm_crtc_state
+ * as part of an atomic commit must ensure that the next vblank happens at
+ * exactly the same time as the atomic commit is committed to the hardware. This
+ * function itself does **not** protect again the next vblank interrupt racing
+ * with either this function call or the atomic commit operation. A possible
+ * sequence could be:
+ *
+ * 1. Driver commits new hardware state into vblank-synchronized registers.
+ * 2. A vblank happens, committing the hardware state. Also the corresponding
+ *    vblank interrupt is fired off and fully processed by the interrupt
+ *    handler.
+ * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event().
+ * 4. The event is only send out for the next vblank, which is wrong.
+ *
+ * An equivalent race can happen when the driver calls
+ * drm_crtc_arm_vblank_event() before writing out the new hardware state.
+ *
+ * The only way to make this work safely is to prevent the vblank from firing
+ * (and the hardware from committing anything else) until the entire atomic
+ * commit sequence has run to completion. If the hardware does not have such a
+ * feature (e.g. using a "go" bit), then it is unsafe to use this functions.
+ * Instead drivers need to manually send out the event from their interrupt
+ * handler by calling drm_crtc_send_vblank_event() and make sure that there's no
+ * possible race with the hardware committing the atomic update.
+ *
  * Caller must hold event lock. Caller must also hold a vblank reference for
  * the event @e, which will be dropped when the next vblank arrives.
  */
@@ -1030,8 +1055,11 @@ EXPORT_SYMBOL(drm_crtc_arm_vblank_event);
  * @crtc: the source CRTC of the vblank event
  * @e: the event to send
  *
- * Updates sequence # and timestamp on event, and sends it to userspace.
- * Caller must hold event lock.
+ * Updates sequence # and timestamp on event for the most recently processed
+ * vblank, and sends it to userspace.  Caller must hold event lock.
+ *
+ * See drm_crtc_arm_vblank_event() for a helper which can be used in certain
+ * situation, especially to send out events for atomic commit operations.
  */
 void drm_crtc_send_vblank_event(struct drm_crtc *crtc,
                                struct drm_pending_vblank_event *e)
@@ -1295,7 +1323,7 @@ void drm_vblank_off(struct drm_device *dev, unsigned int pipe)
                if (e->pipe != pipe)
                        continue;
                DRM_DEBUG("Sending premature vblank event on disable: "
-                         "wanted %d, current %d\n",
+                         "wanted %u, current %u\n",
                          e->event.sequence, seq);
                list_del(&e->base.link);
                drm_vblank_put(dev, pipe);
@@ -1519,7 +1547,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,
                return 0;
 
        /* KMS drivers handle this internally */
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return 0;
 
        pipe = modeset->crtc;
@@ -1585,7 +1613,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
 
        seq = drm_vblank_count_and_time(dev, pipe, &now);
 
-       DRM_DEBUG("event on vblank count %d, current %d, crtc %u\n",
+       DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",
                  vblwait->request.sequence, seq, pipe);
 
        trace_drm_vblank_event_queued(current->pid, pipe,
@@ -1693,7 +1721,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);
        }
 
-       DRM_DEBUG("waiting on vblank count %d, crtc %u\n",
+       DRM_DEBUG("waiting on vblank count %u, crtc %u\n",
                  vblwait->request.sequence, pipe);
        DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,
                    (((drm_vblank_count(dev, pipe) -
@@ -1708,7 +1736,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
                vblwait->reply.tval_sec = now.tv_sec;
                vblwait->reply.tval_usec = now.tv_usec;
 
-               DRM_DEBUG("returning %d to client\n",
+               DRM_DEBUG("returning %u to client\n",
                          vblwait->reply.sequence);
        } else {
                DRM_DEBUG("vblank wait interrupted by signal\n");
@@ -1735,7 +1763,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)
                if ((seq - e->event.sequence) > (1<<23))
                        continue;
 
-               DRM_DEBUG("vblank event on %d, current %d\n",
+               DRM_DEBUG("vblank event on %u, current %u\n",
                          e->event.sequence, seq);
 
                list_del(&e->base.link);
@@ -1826,6 +1854,7 @@ EXPORT_SYMBOL(drm_crtc_handle_vblank);
  */
 u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)
 {
+       WARN_ON_ONCE(dev->max_vblank_count != 0);
        return 0;
 }
 EXPORT_SYMBOL(drm_vblank_no_hw_counter);
index 3187c4b..45db36c 100644 (file)
@@ -27,7 +27,8 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_fb_helper.h>
-#include <drm/drm_dp_aux_dev.h>
+
+#include "drm_crtc_helper_internal.h"
 
 MODULE_AUTHOR("David Airlie, Jesse Barnes");
 MODULE_DESCRIPTION("DRM KMS helper");
index 48ac0eb..c901f3c 100644 (file)
@@ -163,7 +163,7 @@ int drm_legacy_lock(struct drm_device *dev, void *data,
        struct drm_master *master = file_priv->master;
        int ret = 0;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        ++file_priv->lock_count;
@@ -252,7 +252,7 @@ int drm_legacy_unlock(struct drm_device *dev, void *data, struct drm_file *file_
        struct drm_lock *lock = data;
        struct drm_master *master = file_priv->master;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (lock->context == DRM_KERNEL_CONTEXT) {
index af0d471..1160a57 100644 (file)
@@ -998,6 +998,27 @@ int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
 }
 EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
 
+/**
+ * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
+ *    data used by the interface
+ * @dsi: DSI peripheral device
+ * @format: pixel format
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
+{
+       ssize_t err;
+
+       err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
+                                sizeof(format));
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
+
 /**
  * mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for
  *    the Tearing Effect output signal of the display module
@@ -1021,25 +1042,53 @@ int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline)
 EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
 
 /**
- * mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
- *    data used by the interface
+ * mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the
+ *    display
  * @dsi: DSI peripheral device
- * @format: pixel format
+ * @brightness: brightness value
  *
  * Return: 0 on success or a negative error code on failure.
  */
-int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
+int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
+                                       u16 brightness)
 {
+       u8 payload[2] = { brightness & 0xff, brightness >> 8 };
        ssize_t err;
 
-       err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
-                                sizeof(format));
+       err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
+                                payload, sizeof(payload));
        if (err < 0)
                return err;
 
        return 0;
 }
-EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
+EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness);
+
+/**
+ * mipi_dsi_dcs_get_display_brightness() - gets the current brightness value
+ *    of the display
+ * @dsi: DSI peripheral device
+ * @brightness: brightness value
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
+                                       u16 *brightness)
+{
+       ssize_t err;
+
+       err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
+                               brightness, sizeof(*brightness));
+       if (err <= 0) {
+               if (err == 0)
+                       err = -ENODATA;
+
+               return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness);
 
 static int mipi_dsi_drv_probe(struct device *dev)
 {
index cb39f45..11d44a1 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/slab.h>
 #include <linux/seq_file.h>
 #include <linux/export.h>
+#include <linux/interval_tree_generic.h>
 
 /**
  * DOC: Overview
@@ -103,6 +104,72 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_
                                                u64 end,
                                                enum drm_mm_search_flags flags);
 
+#define START(node) ((node)->start)
+#define LAST(node)  ((node)->start + (node)->size - 1)
+
+INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
+                    u64, __subtree_last,
+                    START, LAST, static inline, drm_mm_interval_tree)
+
+struct drm_mm_node *
+drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last)
+{
+       return drm_mm_interval_tree_iter_first(&mm->interval_tree,
+                                              start, last);
+}
+EXPORT_SYMBOL(drm_mm_interval_first);
+
+struct drm_mm_node *
+drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last)
+{
+       return drm_mm_interval_tree_iter_next(node, start, last);
+}
+EXPORT_SYMBOL(drm_mm_interval_next);
+
+static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
+                                         struct drm_mm_node *node)
+{
+       struct drm_mm *mm = hole_node->mm;
+       struct rb_node **link, *rb;
+       struct drm_mm_node *parent;
+
+       node->__subtree_last = LAST(node);
+
+       if (hole_node->allocated) {
+               rb = &hole_node->rb;
+               while (rb) {
+                       parent = rb_entry(rb, struct drm_mm_node, rb);
+                       if (parent->__subtree_last >= node->__subtree_last)
+                               break;
+
+                       parent->__subtree_last = node->__subtree_last;
+                       rb = rb_parent(rb);
+               }
+
+               rb = &hole_node->rb;
+               link = &hole_node->rb.rb_right;
+       } else {
+               rb = NULL;
+               link = &mm->interval_tree.rb_node;
+       }
+
+       while (*link) {
+               rb = *link;
+               parent = rb_entry(rb, struct drm_mm_node, rb);
+               if (parent->__subtree_last < node->__subtree_last)
+                       parent->__subtree_last = node->__subtree_last;
+               if (node->start < parent->start)
+                       link = &parent->rb.rb_left;
+               else
+                       link = &parent->rb.rb_right;
+       }
+
+       rb_link_node(&node->rb, rb, link);
+       rb_insert_augmented(&node->rb,
+                           &mm->interval_tree,
+                           &drm_mm_interval_tree_augment);
+}
+
 static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
                                 struct drm_mm_node *node,
                                 u64 size, unsigned alignment,
@@ -150,9 +217,10 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
        node->color = color;
        node->allocated = 1;
 
-       INIT_LIST_HEAD(&node->hole_stack);
        list_add(&node->node_list, &hole_node->node_list);
 
+       drm_mm_interval_tree_add_node(hole_node, node);
+
        BUG_ON(node->start + node->size > adj_end);
 
        node->hole_follows = 0;
@@ -178,41 +246,54 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
  */
 int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
 {
+       u64 end = node->start + node->size;
        struct drm_mm_node *hole;
-       u64 end;
-       u64 hole_start;
-       u64 hole_end;
+       u64 hole_start, hole_end;
 
-       BUG_ON(node == NULL);
+       if (WARN_ON(node->size == 0))
+               return -EINVAL;
 
        end = node->start + node->size;
 
        /* Find the relevant hole to add our node to */
-       drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
-               if (hole_start > node->start || hole_end < end)
-                       continue;
+       hole = drm_mm_interval_tree_iter_first(&mm->interval_tree,
+                                              node->start, ~(u64)0);
+       if (hole) {
+               if (hole->start < end)
+                       return -ENOSPC;
+       } else {
+               hole = list_entry(&mm->head_node.node_list,
+                                 typeof(*hole), node_list);
+       }
 
-               node->mm = mm;
-               node->allocated = 1;
+       hole = list_last_entry(&hole->node_list, typeof(*hole), node_list);
+       if (!hole->hole_follows)
+               return -ENOSPC;
 
-               INIT_LIST_HEAD(&node->hole_stack);
-               list_add(&node->node_list, &hole->node_list);
+       hole_start = __drm_mm_hole_node_start(hole);
+       hole_end = __drm_mm_hole_node_end(hole);
+       if (hole_start > node->start || hole_end < end)
+               return -ENOSPC;
 
-               if (node->start == hole_start) {
-                       hole->hole_follows = 0;
-                       list_del_init(&hole->hole_stack);
-               }
+       node->mm = mm;
+       node->allocated = 1;
 
-               node->hole_follows = 0;
-               if (end != hole_end) {
-                       list_add(&node->hole_stack, &mm->hole_stack);
-                       node->hole_follows = 1;
-               }
+       list_add(&node->node_list, &hole->node_list);
 
-               return 0;
+       drm_mm_interval_tree_add_node(hole, node);
+
+       if (node->start == hole_start) {
+               hole->hole_follows = 0;
+               list_del(&hole->hole_stack);
        }
 
-       return -ENOSPC;
+       node->hole_follows = 0;
+       if (end != hole_end) {
+               list_add(&node->hole_stack, &mm->hole_stack);
+               node->hole_follows = 1;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL(drm_mm_reserve_node);
 
@@ -239,6 +320,9 @@ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
 {
        struct drm_mm_node *hole_node;
 
+       if (WARN_ON(size == 0))
+               return -EINVAL;
+
        hole_node = drm_mm_search_free_generic(mm, size, alignment,
                                               color, sflags);
        if (!hole_node)
@@ -299,9 +383,10 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
        node->color = color;
        node->allocated = 1;
 
-       INIT_LIST_HEAD(&node->hole_stack);
        list_add(&node->node_list, &hole_node->node_list);
 
+       drm_mm_interval_tree_add_node(hole_node, node);
+
        BUG_ON(node->start < start);
        BUG_ON(node->start < adj_start);
        BUG_ON(node->start + node->size > adj_end);
@@ -340,6 +425,9 @@ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *n
 {
        struct drm_mm_node *hole_node;
 
+       if (WARN_ON(size == 0))
+               return -EINVAL;
+
        hole_node = drm_mm_search_free_in_range_generic(mm,
                                                        size, alignment, color,
                                                        start, end, sflags);
@@ -390,6 +478,7 @@ void drm_mm_remove_node(struct drm_mm_node *node)
        } else
                list_move(&prev_node->hole_stack, &mm->hole_stack);
 
+       drm_mm_interval_tree_remove(node, &mm->interval_tree);
        list_del(&node->node_list);
        node->allocated = 0;
 }
@@ -516,11 +605,13 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
 {
        list_replace(&old->node_list, &new->node_list);
        list_replace(&old->hole_stack, &new->hole_stack);
+       rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree);
        new->hole_follows = old->hole_follows;
        new->mm = old->mm;
        new->start = old->start;
        new->size = old->size;
        new->color = old->color;
+       new->__subtree_last = old->__subtree_last;
 
        old->allocated = 0;
        new->allocated = 1;
@@ -748,7 +839,6 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
 
        /* Clever trick to avoid a special case in the free hole tracking. */
        INIT_LIST_HEAD(&mm->head_node.node_list);
-       INIT_LIST_HEAD(&mm->head_node.hole_stack);
        mm->head_node.hole_follows = 1;
        mm->head_node.scanned_block = 0;
        mm->head_node.scanned_prev_free = 0;
@@ -758,6 +848,8 @@ void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
        mm->head_node.size = start - mm->head_node.start;
        list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
 
+       mm->interval_tree = RB_ROOT;
+
        mm->color_adjust = NULL;
 }
 EXPORT_SYMBOL(drm_mm_init);
diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c
new file mode 100644 (file)
index 0000000..9f17085
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include <drm/drmP.h>
+#include <drm/drm_mode_object.h>
+
+#include "drm_crtc_internal.h"
+
+/*
+ * Internal function to assign a slot in the object idr and optionally
+ * register the object into the idr.
+ */
+int drm_mode_object_get_reg(struct drm_device *dev,
+                           struct drm_mode_object *obj,
+                           uint32_t obj_type,
+                           bool register_obj,
+                           void (*obj_free_cb)(struct kref *kref))
+{
+       int ret;
+
+       mutex_lock(&dev->mode_config.idr_mutex);
+       ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL);
+       if (ret >= 0) {
+               /*
+                * Set up the object linking under the protection of the idr
+                * lock so that other users can't see inconsistent state.
+                */
+               obj->id = ret;
+               obj->type = obj_type;
+               if (obj_free_cb) {
+                       obj->free_cb = obj_free_cb;
+                       kref_init(&obj->refcount);
+               }
+       }
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       return ret < 0 ? ret : 0;
+}
+
+/**
+ * drm_mode_object_get - allocate a new modeset identifier
+ * @dev: DRM device
+ * @obj: object pointer, used to generate unique ID
+ * @obj_type: object type
+ *
+ * Create a unique identifier based on @ptr in @dev's identifier space.  Used
+ * for tracking modes, CRTCs and connectors. Note that despite the _get postfix
+ * modeset identifiers are _not_ reference counted. Hence don't use this for
+ * reference counted modeset objects like framebuffers.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_mode_object_get(struct drm_device *dev,
+                       struct drm_mode_object *obj, uint32_t obj_type)
+{
+       return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL);
+}
+
+void drm_mode_object_register(struct drm_device *dev,
+                             struct drm_mode_object *obj)
+{
+       mutex_lock(&dev->mode_config.idr_mutex);
+       idr_replace(&dev->mode_config.crtc_idr, obj, obj->id);
+       mutex_unlock(&dev->mode_config.idr_mutex);
+}
+
+/**
+ * drm_mode_object_unregister - free a modeset identifer
+ * @dev: DRM device
+ * @object: object to free
+ *
+ * Free @id from @dev's unique identifier pool.
+ * This function can be called multiple times, and guards against
+ * multiple removals.
+ * These modeset identifiers are _not_ reference counted. Hence don't use this
+ * for reference counted modeset objects like framebuffers.
+ */
+void drm_mode_object_unregister(struct drm_device *dev,
+                               struct drm_mode_object *object)
+{
+       mutex_lock(&dev->mode_config.idr_mutex);
+       if (object->id) {
+               idr_remove(&dev->mode_config.crtc_idr, object->id);
+               object->id = 0;
+       }
+       mutex_unlock(&dev->mode_config.idr_mutex);
+}
+
+struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev,
+                                              uint32_t id, uint32_t type)
+{
+       struct drm_mode_object *obj = NULL;
+
+       mutex_lock(&dev->mode_config.idr_mutex);
+       obj = idr_find(&dev->mode_config.crtc_idr, id);
+       if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type)
+               obj = NULL;
+       if (obj && obj->id != id)
+               obj = NULL;
+
+       if (obj && obj->free_cb) {
+               if (!kref_get_unless_zero(&obj->refcount))
+                       obj = NULL;
+       }
+       mutex_unlock(&dev->mode_config.idr_mutex);
+
+       return obj;
+}
+
+/**
+ * drm_mode_object_find - look up a drm object with static lifetime
+ * @dev: drm device
+ * @id: id of the mode object
+ * @type: type of the mode object
+ *
+ * This function is used to look up a modeset object. It will acquire a
+ * reference for reference counted objects. This reference must be dropped again
+ * by callind drm_mode_object_unreference().
+ */
+struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
+               uint32_t id, uint32_t type)
+{
+       struct drm_mode_object *obj = NULL;
+
+       obj = __drm_mode_object_find(dev, id, type);
+       return obj;
+}
+EXPORT_SYMBOL(drm_mode_object_find);
+
+/**
+ * drm_mode_object_unreference - decr the object refcnt
+ * @obj: mode_object
+ *
+ * This function decrements the object's refcount if it is a refcounted modeset
+ * object. It is a no-op on any other object. This is used to drop references
+ * acquired with drm_mode_object_reference().
+ */
+void drm_mode_object_unreference(struct drm_mode_object *obj)
+{
+       if (obj->free_cb) {
+               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
+               kref_put(&obj->refcount, obj->free_cb);
+       }
+}
+EXPORT_SYMBOL(drm_mode_object_unreference);
+
+/**
+ * drm_mode_object_reference - incr the object refcnt
+ * @obj: mode_object
+ *
+ * This function increments the object's refcount if it is a refcounted modeset
+ * object. It is a no-op on any other object. References should be dropped again
+ * by calling drm_mode_object_unreference().
+ */
+void drm_mode_object_reference(struct drm_mode_object *obj)
+{
+       if (obj->free_cb) {
+               DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount));
+               kref_get(&obj->refcount);
+       }
+}
+EXPORT_SYMBOL(drm_mode_object_reference);
+
+/**
+ * drm_object_attach_property - attach a property to a modeset object
+ * @obj: drm modeset object
+ * @property: property to attach
+ * @init_val: initial value of the property
+ *
+ * This attaches the given property to the modeset object with the given initial
+ * value. Currently this function cannot fail since the properties are stored in
+ * a statically sized array.
+ */
+void drm_object_attach_property(struct drm_mode_object *obj,
+                               struct drm_property *property,
+                               uint64_t init_val)
+{
+       int count = obj->properties->count;
+
+       if (count == DRM_OBJECT_MAX_PROPERTY) {
+               WARN(1, "Failed to attach object property (type: 0x%x). Please "
+                       "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time "
+                       "you see this message on the same object type.\n",
+                       obj->type);
+               return;
+       }
+
+       obj->properties->properties[count] = property;
+       obj->properties->values[count] = init_val;
+       obj->properties->count++;
+}
+EXPORT_SYMBOL(drm_object_attach_property);
+
+/**
+ * drm_object_property_set_value - set the value of a property
+ * @obj: drm mode object to set property value for
+ * @property: property to set
+ * @val: value the property should be set to
+ *
+ * This function sets a given property on a given object. This function only
+ * changes the software state of the property, it does not call into the
+ * driver's ->set_property callback.
+ *
+ * Note that atomic drivers should not have any need to call this, the core will
+ * ensure consistency of values reported back to userspace through the
+ * appropriate ->atomic_get_property callback. Only legacy drivers should call
+ * this function to update the tracked value (after clamping and other
+ * restrictions have been applied).
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_object_property_set_value(struct drm_mode_object *obj,
+                                 struct drm_property *property, uint64_t val)
+{
+       int i;
+
+       for (i = 0; i < obj->properties->count; i++) {
+               if (obj->properties->properties[i] == property) {
+                       obj->properties->values[i] = val;
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(drm_object_property_set_value);
+
+/**
+ * drm_object_property_get_value - retrieve the value of a property
+ * @obj: drm mode object to get property value from
+ * @property: property to retrieve
+ * @val: storage for the property value
+ *
+ * This function retrieves the softare state of the given property for the given
+ * property. Since there is no driver callback to retrieve the current property
+ * value this might be out of sync with the hardware, depending upon the driver
+ * and property.
+ *
+ * Atomic drivers should never call this function directly, the core will read
+ * out property values through the various ->atomic_get_property callbacks.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_object_property_get_value(struct drm_mode_object *obj,
+                                 struct drm_property *property, uint64_t *val)
+{
+       int i;
+
+       /* read-only properties bypass atomic mechanism and still store
+        * their value in obj->properties->values[].. mostly to avoid
+        * having to deal w/ EDID and similar props in atomic paths:
+        */
+       if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) &&
+                       !(property->flags & DRM_MODE_PROP_IMMUTABLE))
+               return drm_atomic_get_property(obj, property, val);
+
+       for (i = 0; i < obj->properties->count; i++) {
+               if (obj->properties->properties[i] == property) {
+                       *val = obj->properties->values[i];
+                       return 0;
+               }
+
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL(drm_object_property_get_value);
+
+/* helper for getconnector and getproperties ioctls */
+int drm_mode_object_get_properties(struct drm_mode_object *obj, bool atomic,
+                                  uint32_t __user *prop_ptr,
+                                  uint64_t __user *prop_values,
+                                  uint32_t *arg_count_props)
+{
+       int i, ret, count;
+
+       for (i = 0, count = 0; i < obj->properties->count; i++) {
+               struct drm_property *prop = obj->properties->properties[i];
+               uint64_t val;
+
+               if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic)
+                       continue;
+
+               if (*arg_count_props > count) {
+                       ret = drm_object_property_get_value(obj, prop, &val);
+                       if (ret)
+                               return ret;
+
+                       if (put_user(prop->base.id, prop_ptr + count))
+                               return -EFAULT;
+
+                       if (put_user(val, prop_values + count))
+                               return -EFAULT;
+               }
+
+               count++;
+       }
+       *arg_count_props = count;
+
+       return 0;
+}
+
+/**
+ * drm_mode_obj_get_properties_ioctl - get the current value of a object's property
+ * @dev: DRM device
+ * @data: ioctl data
+ * @file_priv: DRM file info
+ *
+ * This function retrieves the current value for an object's property. Compared
+ * to the connector specific ioctl this one is extended to also work on crtc and
+ * plane objects.
+ *
+ * Called by the user via ioctl.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
+                                     struct drm_file *file_priv)
+{
+       struct drm_mode_obj_get_properties *arg = data;
+       struct drm_mode_object *obj;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+
+       obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
+       if (!obj) {
+               ret = -ENOENT;
+               goto out;
+       }
+       if (!obj->properties) {
+               ret = -EINVAL;
+               goto out_unref;
+       }
+
+       ret = drm_mode_object_get_properties(obj, file_priv->atomic,
+                       (uint32_t __user *)(unsigned long)(arg->props_ptr),
+                       (uint64_t __user *)(unsigned long)(arg->prop_values_ptr),
+                       &arg->count_props);
+
+out_unref:
+       drm_mode_object_unreference(obj);
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+
+struct drm_property *drm_mode_obj_find_prop_id(struct drm_mode_object *obj,
+                                              uint32_t prop_id)
+{
+       int i;
+
+       for (i = 0; i < obj->properties->count; i++)
+               if (obj->properties->properties[i]->base.id == prop_id)
+                       return obj->properties->properties[i];
+
+       return NULL;
+}
+
+int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
+                                   struct drm_file *file_priv)
+{
+       struct drm_mode_obj_set_property *arg = data;
+       struct drm_mode_object *arg_obj;
+       struct drm_property *property;
+       int ret = -EINVAL;
+       struct drm_mode_object *ref;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+
+       arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
+       if (!arg_obj) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       if (!arg_obj->properties)
+               goto out_unref;
+
+       property = drm_mode_obj_find_prop_id(arg_obj, arg->prop_id);
+       if (!property)
+               goto out_unref;
+
+       if (!drm_property_change_valid_get(property, arg->value, &ref))
+               goto out_unref;
+
+       switch (arg_obj->type) {
+       case DRM_MODE_OBJECT_CONNECTOR:
+               ret = drm_mode_connector_set_obj_prop(arg_obj, property,
+                                                     arg->value);
+               break;
+       case DRM_MODE_OBJECT_CRTC:
+               ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value);
+               break;
+       case DRM_MODE_OBJECT_PLANE:
+               ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj),
+                                                 property, arg->value);
+               break;
+       }
+
+       drm_property_change_valid_put(property, ref);
+
+out_unref:
+       drm_mode_object_unreference(arg_obj);
+out:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
index fc5040a..53f07ac 100644 (file)
@@ -657,11 +657,36 @@ void drm_display_mode_to_videomode(const struct drm_display_mode *dmode,
 }
 EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
 
+/**
+ * drm_bus_flags_from_videomode - extract information about pixelclk and
+ * DE polarity from videomode and store it in a separate variable
+ * @vm: videomode structure to use
+ * @bus_flags: information about pixelclk and DE polarity will be stored here
+ *
+ * Sets DRM_BUS_FLAG_DE_(LOW|HIGH) and DRM_BUS_FLAG_PIXDATA_(POS|NEG)EDGE
+ * in @bus_flags according to DISPLAY_FLAGS found in @vm
+ */
+void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags)
+{
+       *bus_flags = 0;
+       if (vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)
+               *bus_flags |= DRM_BUS_FLAG_PIXDATA_POSEDGE;
+       if (vm->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+               *bus_flags |= DRM_BUS_FLAG_PIXDATA_NEGEDGE;
+
+       if (vm->flags & DISPLAY_FLAGS_DE_LOW)
+               *bus_flags |= DRM_BUS_FLAG_DE_LOW;
+       if (vm->flags & DISPLAY_FLAGS_DE_HIGH)
+               *bus_flags |= DRM_BUS_FLAG_DE_HIGH;
+}
+EXPORT_SYMBOL_GPL(drm_bus_flags_from_videomode);
+
 #ifdef CONFIG_OF
 /**
  * of_get_drm_display_mode - get a drm_display_mode from devicetree
  * @np: device_node with the timing specification
  * @dmode: will be set to the return value
+ * @bus_flags: information about pixelclk and DE polarity
  * @index: index into the list of display timings in devicetree
  *
  * This function is expensive and should only be used, if only one mode is to be
@@ -672,7 +697,8 @@ EXPORT_SYMBOL_GPL(drm_display_mode_to_videomode);
  * 0 on success, a negative errno code when no of videomode node was found.
  */
 int of_get_drm_display_mode(struct device_node *np,
-                           struct drm_display_mode *dmode, int index)
+                           struct drm_display_mode *dmode, u32 *bus_flags,
+                           int index)
 {
        struct videomode vm;
        int ret;
@@ -682,6 +708,8 @@ int of_get_drm_display_mode(struct device_node *np,
                return ret;
 
        drm_display_mode_from_videomode(&vm, dmode);
+       if (bus_flags)
+               drm_bus_flags_from_videomode(&vm, bus_flags);
 
        pr_debug("%s: got %dx%d display mode from %s\n",
                of_node_full_name(np), vm.hactive, vm.vactive, np->name);
diff --git a/drivers/gpu/drm/drm_modeset_helper.c b/drivers/gpu/drm/drm_modeset_helper.c
new file mode 100644 (file)
index 0000000..1d45738
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drm_modeset_helper.h>
+#include <drm/drm_plane_helper.h>
+
+/**
+ * DOC: aux kms helpers
+ *
+ * This helper library contains various one-off functions which don't really fit
+ * anywhere else in the DRM modeset helper library.
+ */
+
+/**
+ * drm_helper_move_panel_connectors_to_head() - move panels to the front in the
+ *                                             connector list
+ * @dev: drm device to operate on
+ *
+ * Some userspace presumes that the first connected connector is the main
+ * display, where it's supposed to display e.g. the login screen. For
+ * laptops, this should be the main panel. Use this function to sort all
+ * (eDP/LVDS) panels to the front of the connector list, instead of
+ * painstakingly trying to initialize them in the right order.
+ */
+void drm_helper_move_panel_connectors_to_head(struct drm_device *dev)
+{
+       struct drm_connector *connector, *tmp;
+       struct list_head panel_list;
+
+       INIT_LIST_HEAD(&panel_list);
+
+       list_for_each_entry_safe(connector, tmp,
+                                &dev->mode_config.connector_list, head) {
+               if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
+                   connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+                       list_move_tail(&connector->head, &panel_list);
+       }
+
+       list_splice(&panel_list, &dev->mode_config.connector_list);
+}
+EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head);
+
+/**
+ * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata
+ * @fb: drm_framebuffer object to fill out
+ * @mode_cmd: metadata from the userspace fb creation request
+ *
+ * This helper can be used in a drivers fb_create callback to pre-fill the fb's
+ * metadata fields.
+ */
+void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+                                   const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       int i;
+
+       fb->width = mode_cmd->width;
+       fb->height = mode_cmd->height;
+       for (i = 0; i < 4; i++) {
+               fb->pitches[i] = mode_cmd->pitches[i];
+               fb->offsets[i] = mode_cmd->offsets[i];
+               fb->modifier[i] = mode_cmd->modifier[i];
+       }
+       drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
+                                   &fb->bits_per_pixel);
+       fb->pixel_format = mode_cmd->pixel_format;
+       fb->flags = mode_cmd->flags;
+}
+EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
+
+/*
+ * This is the minimal list of formats that seem to be safe for modeset use
+ * with all current DRM drivers.  Most hardware can actually support more
+ * formats than this and drivers may specify a more accurate list when
+ * creating the primary plane.  However drivers that still call
+ * drm_plane_init() will use this minimal format list as the default.
+ */
+static const uint32_t safe_modeset_formats[] = {
+       DRM_FORMAT_XRGB8888,
+       DRM_FORMAT_ARGB8888,
+};
+
+static struct drm_plane *create_primary_plane(struct drm_device *dev)
+{
+       struct drm_plane *primary;
+       int ret;
+
+       primary = kzalloc(sizeof(*primary), GFP_KERNEL);
+       if (primary == NULL) {
+               DRM_DEBUG_KMS("Failed to allocate primary plane\n");
+               return NULL;
+       }
+
+       /*
+        * Remove the format_default field from drm_plane when dropping
+        * this helper.
+        */
+       primary->format_default = true;
+
+       /* possible_crtc's will be filled in later by crtc_init */
+       ret = drm_universal_plane_init(dev, primary, 0,
+                                      &drm_primary_helper_funcs,
+                                      safe_modeset_formats,
+                                      ARRAY_SIZE(safe_modeset_formats),
+                                      DRM_PLANE_TYPE_PRIMARY, NULL);
+       if (ret) {
+               kfree(primary);
+               primary = NULL;
+       }
+
+       return primary;
+}
+
+/**
+ * drm_crtc_init - Legacy CRTC initialization function
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @funcs: callbacks for the new CRTC
+ *
+ * Initialize a CRTC object with a default helper-provided primary plane and no
+ * cursor plane.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+                 const struct drm_crtc_funcs *funcs)
+{
+       struct drm_plane *primary;
+
+       primary = create_primary_plane(dev);
+       return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs,
+                                        NULL);
+}
+EXPORT_SYMBOL(drm_crtc_init);
index b2f8f10..3ceea9c 100644 (file)
@@ -175,7 +175,7 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,
 {
        struct drm_irq_busid *p = data;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        /* UMS was only ever support on PCI devices. */
@@ -236,8 +236,8 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
        DRM_DEBUG("\n");
 
        dev = drm_dev_alloc(driver, &pdev->dev);
-       if (!dev)
-               return -ENOMEM;
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
 
        ret = pci_enable_device(pdev);
        if (ret)
@@ -263,7 +263,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
 
        /* No locking needed since shadow-attach is single-threaded since it may
         * only be called from the per-driver module init hook. */
-       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+       if (drm_core_check_feature(dev, DRIVER_LEGACY))
                list_add_tail(&dev->legacy_dev_list, &driver->legacy_dev_list);
 
        return 0;
@@ -299,7 +299,7 @@ int drm_pci_init(struct drm_driver *driver, struct pci_driver *pdriver)
 
        DRM_DEBUG("\n");
 
-       if (driver->driver_features & DRIVER_MODESET)
+       if (!(driver->driver_features & DRIVER_LEGACY))
                return pci_register_driver(pdriver);
 
        /* If not using KMS, fall back to stealth mode manual scanning. */
@@ -421,7 +421,7 @@ void drm_pci_exit(struct drm_driver *driver, struct pci_driver *pdriver)
        struct drm_device *dev, *tmp;
        DRM_DEBUG("\n");
 
-       if (driver->driver_features & DRIVER_MODESET) {
+       if (!(driver->driver_features & DRIVER_LEGACY)) {
                pci_unregister_driver(pdriver);
        } else {
                list_for_each_entry_safe(dev, tmp, &driver->legacy_dev_list,
diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c
new file mode 100644 (file)
index 0000000..249c0ae
--- /dev/null
@@ -0,0 +1,906 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_plane.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * A plane represents an image source that can be blended with or overlayed on
+ * top of a CRTC during the scanout process. Planes take their input data from a
+ * &drm_framebuffer object. The plane itself specifies the cropping and scaling
+ * of that image, and where it is placed on the visible are of a display
+ * pipeline, represented by &drm_crtc. A plane can also have additional
+ * properties that specify how the pixels are positioned and blended, like
+ * rotation or Z-position. All these properties are stored in &drm_plane_state.
+ *
+ * To create a plane, a KMS drivers allocates and zeroes an instances of
+ * struct &drm_plane (possibly as part of a larger structure) and registers it
+ * with a call to drm_universal_plane_init().
+ *
+ * Cursor and overlay planes are optional. All drivers should provide one
+ * primary plane per CRTC to avoid surprising userspace too much. See enum
+ * &drm_plane_type for a more in-depth discussion of these special uapi-relevant
+ * plane types. Special planes are associated with their CRTC by calling
+ * drm_crtc_init_with_planes().
+ *
+ * The type of a plane is exposed in the immutable "type" enumeration property,
+ * which has one of the following values: "Overlay", "Primary", "Cursor".
+ */
+
+static unsigned int drm_num_planes(struct drm_device *dev)
+{
+       unsigned int num = 0;
+       struct drm_plane *tmp;
+
+       drm_for_each_plane(tmp, dev) {
+               num++;
+       }
+
+       return num;
+}
+
+/**
+ * drm_universal_plane_init - Initialize a new universal plane object
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (DRM_FORMAT\_\*)
+ * @format_count: number of elements in @formats
+ * @type: type of plane (overlay, primary, cursor)
+ * @name: printf style format string for the plane name, or NULL for default name
+ *
+ * Initializes a plane object of type @type.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                            unsigned long possible_crtcs,
+                            const struct drm_plane_funcs *funcs,
+                            const uint32_t *formats, unsigned int format_count,
+                            enum drm_plane_type type,
+                            const char *name, ...)
+{
+       struct drm_mode_config *config = &dev->mode_config;
+       int ret;
+
+       ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+       if (ret)
+               return ret;
+
+       drm_modeset_lock_init(&plane->mutex);
+
+       plane->base.properties = &plane->properties;
+       plane->dev = dev;
+       plane->funcs = funcs;
+       plane->format_types = kmalloc_array(format_count, sizeof(uint32_t),
+                                           GFP_KERNEL);
+       if (!plane->format_types) {
+               DRM_DEBUG_KMS("out of memory when allocating plane\n");
+               drm_mode_object_unregister(dev, &plane->base);
+               return -ENOMEM;
+       }
+
+       if (name) {
+               va_list ap;
+
+               va_start(ap, name);
+               plane->name = kvasprintf(GFP_KERNEL, name, ap);
+               va_end(ap);
+       } else {
+               plane->name = kasprintf(GFP_KERNEL, "plane-%d",
+                                       drm_num_planes(dev));
+       }
+       if (!plane->name) {
+               kfree(plane->format_types);
+               drm_mode_object_unregister(dev, &plane->base);
+               return -ENOMEM;
+       }
+
+       memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
+       plane->format_count = format_count;
+       plane->possible_crtcs = possible_crtcs;
+       plane->type = type;
+
+       list_add_tail(&plane->head, &config->plane_list);
+       plane->index = config->num_total_plane++;
+       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+               config->num_overlay_plane++;
+
+       drm_object_attach_property(&plane->base,
+                                  config->plane_type_property,
+                                  plane->type);
+
+       if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
+               drm_object_attach_property(&plane->base, config->prop_fb_id, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_id, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_x, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_y, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_w, 0);
+               drm_object_attach_property(&plane->base, config->prop_crtc_h, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_x, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_y, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_w, 0);
+               drm_object_attach_property(&plane->base, config->prop_src_h, 0);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_universal_plane_init);
+
+int drm_plane_register_all(struct drm_device *dev)
+{
+       struct drm_plane *plane;
+       int ret = 0;
+
+       drm_for_each_plane(plane, dev) {
+               if (plane->funcs->late_register)
+                       ret = plane->funcs->late_register(plane);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+void drm_plane_unregister_all(struct drm_device *dev)
+{
+       struct drm_plane *plane;
+
+       drm_for_each_plane(plane, dev) {
+               if (plane->funcs->early_unregister)
+                       plane->funcs->early_unregister(plane);
+       }
+}
+
+/**
+ * drm_plane_init - Initialize a legacy plane
+ * @dev: DRM device
+ * @plane: plane object to init
+ * @possible_crtcs: bitmask of possible CRTCs
+ * @funcs: callbacks for the new plane
+ * @formats: array of supported formats (DRM_FORMAT\_\*)
+ * @format_count: number of elements in @formats
+ * @is_primary: plane type (primary vs overlay)
+ *
+ * Legacy API to initialize a DRM plane.
+ *
+ * New drivers should call drm_universal_plane_init() instead.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                  unsigned long possible_crtcs,
+                  const struct drm_plane_funcs *funcs,
+                  const uint32_t *formats, unsigned int format_count,
+                  bool is_primary)
+{
+       enum drm_plane_type type;
+
+       type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
+       return drm_universal_plane_init(dev, plane, possible_crtcs, funcs,
+                                       formats, format_count, type, NULL);
+}
+EXPORT_SYMBOL(drm_plane_init);
+
+/**
+ * drm_plane_cleanup - Clean up the core plane usage
+ * @plane: plane to cleanup
+ *
+ * This function cleans up @plane and removes it from the DRM mode setting
+ * core. Note that the function does *not* free the plane structure itself,
+ * this is the responsibility of the caller.
+ */
+void drm_plane_cleanup(struct drm_plane *plane)
+{
+       struct drm_device *dev = plane->dev;
+
+       drm_modeset_lock_all(dev);
+       kfree(plane->format_types);
+       drm_mode_object_unregister(dev, &plane->base);
+
+       BUG_ON(list_empty(&plane->head));
+
+       /* Note that the plane_list is considered to be static; should we
+        * remove the drm_plane at runtime we would have to decrement all
+        * the indices on the drm_plane after us in the plane_list.
+        */
+
+       list_del(&plane->head);
+       dev->mode_config.num_total_plane--;
+       if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+               dev->mode_config.num_overlay_plane--;
+       drm_modeset_unlock_all(dev);
+
+       WARN_ON(plane->state && !plane->funcs->atomic_destroy_state);
+       if (plane->state && plane->funcs->atomic_destroy_state)
+               plane->funcs->atomic_destroy_state(plane, plane->state);
+
+       kfree(plane->name);
+
+       memset(plane, 0, sizeof(*plane));
+}
+EXPORT_SYMBOL(drm_plane_cleanup);
+
+/**
+ * drm_plane_from_index - find the registered plane at an index
+ * @dev: DRM device
+ * @idx: index of registered plane to find for
+ *
+ * Given a plane index, return the registered plane from DRM device's
+ * list of planes with matching index.
+ */
+struct drm_plane *
+drm_plane_from_index(struct drm_device *dev, int idx)
+{
+       struct drm_plane *plane;
+
+       drm_for_each_plane(plane, dev)
+               if (idx == plane->index)
+                       return plane;
+
+       return NULL;
+}
+EXPORT_SYMBOL(drm_plane_from_index);
+
+/**
+ * drm_plane_force_disable - Forcibly disable a plane
+ * @plane: plane to disable
+ *
+ * Forces the plane to be disabled.
+ *
+ * Used when the plane's current framebuffer is destroyed,
+ * and when restoring fbdev mode.
+ */
+void drm_plane_force_disable(struct drm_plane *plane)
+{
+       int ret;
+
+       if (!plane->fb)
+               return;
+
+       plane->old_fb = plane->fb;
+       ret = plane->funcs->disable_plane(plane);
+       if (ret) {
+               DRM_ERROR("failed to disable plane with busy fb\n");
+               plane->old_fb = NULL;
+               return;
+       }
+       /* disconnect the plane from the fb and crtc: */
+       drm_framebuffer_unreference(plane->old_fb);
+       plane->old_fb = NULL;
+       plane->fb = NULL;
+       plane->crtc = NULL;
+}
+EXPORT_SYMBOL(drm_plane_force_disable);
+
+/**
+ * drm_mode_plane_set_obj_prop - set the value of a property
+ * @plane: drm plane object to set property value for
+ * @property: property to set
+ * @value: value the property should be set to
+ *
+ * This functions sets a given property on a given plane object. This function
+ * calls the driver's ->set_property callback and changes the software state of
+ * the property if the callback succeeds.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+                               struct drm_property *property,
+                               uint64_t value)
+{
+       int ret = -EINVAL;
+       struct drm_mode_object *obj = &plane->base;
+
+       if (plane->funcs->set_property)
+               ret = plane->funcs->set_property(plane, property, value);
+       if (!ret)
+               drm_object_property_set_value(obj, property, value);
+
+       return ret;
+}
+EXPORT_SYMBOL(drm_mode_plane_set_obj_prop);
+
+int drm_mode_getplane_res(struct drm_device *dev, void *data,
+                         struct drm_file *file_priv)
+{
+       struct drm_mode_get_plane_res *plane_resp = data;
+       struct drm_mode_config *config;
+       struct drm_plane *plane;
+       uint32_t __user *plane_ptr;
+       int copied = 0;
+       unsigned num_planes;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       config = &dev->mode_config;
+
+       if (file_priv->universal_planes)
+               num_planes = config->num_total_plane;
+       else
+               num_planes = config->num_overlay_plane;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if (num_planes &&
+           (plane_resp->count_planes >= num_planes)) {
+               plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
+
+               /* Plane lists are invariant, no locking needed. */
+               drm_for_each_plane(plane, dev) {
+                       /*
+                        * Unless userspace set the 'universal planes'
+                        * capability bit, only advertise overlays.
+                        */
+                       if (plane->type != DRM_PLANE_TYPE_OVERLAY &&
+                           !file_priv->universal_planes)
+                               continue;
+
+                       if (put_user(plane->base.id, plane_ptr + copied))
+                               return -EFAULT;
+                       copied++;
+               }
+       }
+       plane_resp->count_planes = num_planes;
+
+       return 0;
+}
+
+int drm_mode_getplane(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
+{
+       struct drm_mode_get_plane *plane_resp = data;
+       struct drm_plane *plane;
+       uint32_t __user *format_ptr;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       plane = drm_plane_find(dev, plane_resp->plane_id);
+       if (!plane)
+               return -ENOENT;
+
+       drm_modeset_lock(&plane->mutex, NULL);
+       if (plane->crtc)
+               plane_resp->crtc_id = plane->crtc->base.id;
+       else
+               plane_resp->crtc_id = 0;
+
+       if (plane->fb)
+               plane_resp->fb_id = plane->fb->base.id;
+       else
+               plane_resp->fb_id = 0;
+       drm_modeset_unlock(&plane->mutex);
+
+       plane_resp->plane_id = plane->base.id;
+       plane_resp->possible_crtcs = plane->possible_crtcs;
+       plane_resp->gamma_size = 0;
+
+       /*
+        * This ioctl is called twice, once to determine how much space is
+        * needed, and the 2nd time to fill it.
+        */
+       if (plane->format_count &&
+           (plane_resp->count_format_types >= plane->format_count)) {
+               format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
+               if (copy_to_user(format_ptr,
+                                plane->format_types,
+                                sizeof(uint32_t) * plane->format_count)) {
+                       return -EFAULT;
+               }
+       }
+       plane_resp->count_format_types = plane->format_count;
+
+       return 0;
+}
+
+int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format)
+{
+       unsigned int i;
+
+       for (i = 0; i < plane->format_count; i++) {
+               if (format == plane->format_types[i])
+                       return 0;
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * setplane_internal - setplane handler for internal callers
+ *
+ * Note that we assume an extra reference has already been taken on fb.  If the
+ * update fails, this reference will be dropped before return; if it succeeds,
+ * the previous framebuffer (if any) will be unreferenced instead.
+ *
+ * src_{x,y,w,h} are provided in 16.16 fixed point format
+ */
+static int __setplane_internal(struct drm_plane *plane,
+                              struct drm_crtc *crtc,
+                              struct drm_framebuffer *fb,
+                              int32_t crtc_x, int32_t crtc_y,
+                              uint32_t crtc_w, uint32_t crtc_h,
+                              /* src_{x,y,w,h} values are 16.16 fixed point */
+                              uint32_t src_x, uint32_t src_y,
+                              uint32_t src_w, uint32_t src_h)
+{
+       int ret = 0;
+
+       /* No fb means shut it down */
+       if (!fb) {
+               plane->old_fb = plane->fb;
+               ret = plane->funcs->disable_plane(plane);
+               if (!ret) {
+                       plane->crtc = NULL;
+                       plane->fb = NULL;
+               } else {
+                       plane->old_fb = NULL;
+               }
+               goto out;
+       }
+
+       /* Check whether this plane is usable on this CRTC */
+       if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) {
+               DRM_DEBUG_KMS("Invalid crtc for plane\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Check whether this plane supports the fb pixel format. */
+       ret = drm_plane_check_pixel_format(plane, fb->pixel_format);
+       if (ret) {
+               char *format_name = drm_get_format_name(fb->pixel_format);
+               DRM_DEBUG_KMS("Invalid pixel format %s\n", format_name);
+               kfree(format_name);
+               goto out;
+       }
+
+       /* Give drivers some help against integer overflows */
+       if (crtc_w > INT_MAX ||
+           crtc_x > INT_MAX - (int32_t) crtc_w ||
+           crtc_h > INT_MAX ||
+           crtc_y > INT_MAX - (int32_t) crtc_h) {
+               DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
+                             crtc_w, crtc_h, crtc_x, crtc_y);
+               ret = -ERANGE;
+               goto out;
+       }
+
+       ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb);
+       if (ret)
+               goto out;
+
+       plane->old_fb = plane->fb;
+       ret = plane->funcs->update_plane(plane, crtc, fb,
+                                        crtc_x, crtc_y, crtc_w, crtc_h,
+                                        src_x, src_y, src_w, src_h);
+       if (!ret) {
+               plane->crtc = crtc;
+               plane->fb = fb;
+               fb = NULL;
+       } else {
+               plane->old_fb = NULL;
+       }
+
+out:
+       if (fb)
+               drm_framebuffer_unreference(fb);
+       if (plane->old_fb)
+               drm_framebuffer_unreference(plane->old_fb);
+       plane->old_fb = NULL;
+
+       return ret;
+}
+
+static int setplane_internal(struct drm_plane *plane,
+                            struct drm_crtc *crtc,
+                            struct drm_framebuffer *fb,
+                            int32_t crtc_x, int32_t crtc_y,
+                            uint32_t crtc_w, uint32_t crtc_h,
+                            /* src_{x,y,w,h} values are 16.16 fixed point */
+                            uint32_t src_x, uint32_t src_y,
+                            uint32_t src_w, uint32_t src_h)
+{
+       int ret;
+
+       drm_modeset_lock_all(plane->dev);
+       ret = __setplane_internal(plane, crtc, fb,
+                                 crtc_x, crtc_y, crtc_w, crtc_h,
+                                 src_x, src_y, src_w, src_h);
+       drm_modeset_unlock_all(plane->dev);
+
+       return ret;
+}
+
+int drm_mode_setplane(struct drm_device *dev, void *data,
+                     struct drm_file *file_priv)
+{
+       struct drm_mode_set_plane *plane_req = data;
+       struct drm_plane *plane;
+       struct drm_crtc *crtc = NULL;
+       struct drm_framebuffer *fb = NULL;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       /*
+        * First, find the plane, crtc, and fb objects.  If not available,
+        * we don't bother to call the driver.
+        */
+       plane = drm_plane_find(dev, plane_req->plane_id);
+       if (!plane) {
+               DRM_DEBUG_KMS("Unknown plane ID %d\n",
+                             plane_req->plane_id);
+               return -ENOENT;
+       }
+
+       if (plane_req->fb_id) {
+               fb = drm_framebuffer_lookup(dev, plane_req->fb_id);
+               if (!fb) {
+                       DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
+                                     plane_req->fb_id);
+                       return -ENOENT;
+               }
+
+               crtc = drm_crtc_find(dev, plane_req->crtc_id);
+               if (!crtc) {
+                       DRM_DEBUG_KMS("Unknown crtc ID %d\n",
+                                     plane_req->crtc_id);
+                       return -ENOENT;
+               }
+       }
+
+       /*
+        * setplane_internal will take care of deref'ing either the old or new
+        * framebuffer depending on success.
+        */
+       return setplane_internal(plane, crtc, fb,
+                                plane_req->crtc_x, plane_req->crtc_y,
+                                plane_req->crtc_w, plane_req->crtc_h,
+                                plane_req->src_x, plane_req->src_y,
+                                plane_req->src_w, plane_req->src_h);
+}
+
+static int drm_mode_cursor_universal(struct drm_crtc *crtc,
+                                    struct drm_mode_cursor2 *req,
+                                    struct drm_file *file_priv)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_framebuffer *fb = NULL;
+       struct drm_mode_fb_cmd2 fbreq = {
+               .width = req->width,
+               .height = req->height,
+               .pixel_format = DRM_FORMAT_ARGB8888,
+               .pitches = { req->width * 4 },
+               .handles = { req->handle },
+       };
+       int32_t crtc_x, crtc_y;
+       uint32_t crtc_w = 0, crtc_h = 0;
+       uint32_t src_w = 0, src_h = 0;
+       int ret = 0;
+
+       BUG_ON(!crtc->cursor);
+       WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL);
+
+       /*
+        * Obtain fb we'll be using (either new or existing) and take an extra
+        * reference to it if fb != null.  setplane will take care of dropping
+        * the reference if the plane update fails.
+        */
+       if (req->flags & DRM_MODE_CURSOR_BO) {
+               if (req->handle) {
+                       fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv);
+                       if (IS_ERR(fb)) {
+                               DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n");
+                               return PTR_ERR(fb);
+                       }
+                       fb->hot_x = req->hot_x;
+                       fb->hot_y = req->hot_y;
+               } else {
+                       fb = NULL;
+               }
+       } else {
+               fb = crtc->cursor->fb;
+               if (fb)
+                       drm_framebuffer_reference(fb);
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_MOVE) {
+               crtc_x = req->x;
+               crtc_y = req->y;
+       } else {
+               crtc_x = crtc->cursor_x;
+               crtc_y = crtc->cursor_y;
+       }
+
+       if (fb) {
+               crtc_w = fb->width;
+               crtc_h = fb->height;
+               src_w = fb->width << 16;
+               src_h = fb->height << 16;
+       }
+
+       /*
+        * setplane_internal will take care of deref'ing either the old or new
+        * framebuffer depending on success.
+        */
+       ret = __setplane_internal(crtc->cursor, crtc, fb,
+                               crtc_x, crtc_y, crtc_w, crtc_h,
+                               0, 0, src_w, src_h);
+
+       /* Update successful; save new cursor position, if necessary */
+       if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) {
+               crtc->cursor_x = req->x;
+               crtc->cursor_y = req->y;
+       }
+
+       return ret;
+}
+
+static int drm_mode_cursor_common(struct drm_device *dev,
+                                 struct drm_mode_cursor2 *req,
+                                 struct drm_file *file_priv)
+{
+       struct drm_crtc *crtc;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags))
+               return -EINVAL;
+
+       crtc = drm_crtc_find(dev, req->crtc_id);
+       if (!crtc) {
+               DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
+               return -ENOENT;
+       }
+
+       /*
+        * If this crtc has a universal cursor plane, call that plane's update
+        * handler rather than using legacy cursor handlers.
+        */
+       drm_modeset_lock_crtc(crtc, crtc->cursor);
+       if (crtc->cursor) {
+               ret = drm_mode_cursor_universal(crtc, req, file_priv);
+               goto out;
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_BO) {
+               if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) {
+                       ret = -ENXIO;
+                       goto out;
+               }
+               /* Turns off the cursor if handle is 0 */
+               if (crtc->funcs->cursor_set2)
+                       ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle,
+                                                     req->width, req->height, req->hot_x, req->hot_y);
+               else
+                       ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle,
+                                                     req->width, req->height);
+       }
+
+       if (req->flags & DRM_MODE_CURSOR_MOVE) {
+               if (crtc->funcs->cursor_move) {
+                       ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
+               } else {
+                       ret = -EFAULT;
+                       goto out;
+               }
+       }
+out:
+       drm_modeset_unlock_crtc(crtc);
+
+       return ret;
+
+}
+
+
+int drm_mode_cursor_ioctl(struct drm_device *dev,
+                         void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_cursor *req = data;
+       struct drm_mode_cursor2 new_req;
+
+       memcpy(&new_req, req, sizeof(struct drm_mode_cursor));
+       new_req.hot_x = new_req.hot_y = 0;
+
+       return drm_mode_cursor_common(dev, &new_req, file_priv);
+}
+
+/*
+ * Set the cursor configuration based on user request. This implements the 2nd
+ * version of the cursor ioctl, which allows userspace to additionally specify
+ * the hotspot of the pointer.
+ */
+int drm_mode_cursor2_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_cursor2 *req = data;
+
+       return drm_mode_cursor_common(dev, req, file_priv);
+}
+
+int drm_mode_page_flip_ioctl(struct drm_device *dev,
+                            void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_crtc_page_flip_target *page_flip = data;
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *fb = NULL;
+       struct drm_pending_vblank_event *e = NULL;
+       u32 target_vblank = page_flip->sequence;
+       int ret = -EINVAL;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS)
+               return -EINVAL;
+
+       if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET))
+               return -EINVAL;
+
+       /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags
+        * can be specified
+        */
+       if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET)
+               return -EINVAL;
+
+       if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip)
+               return -EINVAL;
+
+       crtc = drm_crtc_find(dev, page_flip->crtc_id);
+       if (!crtc)
+               return -ENOENT;
+
+       if (crtc->funcs->page_flip_target) {
+               u32 current_vblank;
+               int r;
+
+               r = drm_crtc_vblank_get(crtc);
+               if (r)
+                       return r;
+
+               current_vblank = drm_crtc_vblank_count(crtc);
+
+               switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) {
+               case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE:
+                       if ((int)(target_vblank - current_vblank) > 1) {
+                               DRM_DEBUG("Invalid absolute flip target %u, "
+                                         "must be <= %u\n", target_vblank,
+                                         current_vblank + 1);
+                               drm_crtc_vblank_put(crtc);
+                               return -EINVAL;
+                       }
+                       break;
+               case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE:
+                       if (target_vblank != 0 && target_vblank != 1) {
+                               DRM_DEBUG("Invalid relative flip target %u, "
+                                         "must be 0 or 1\n", target_vblank);
+                               drm_crtc_vblank_put(crtc);
+                               return -EINVAL;
+                       }
+                       target_vblank += current_vblank;
+                       break;
+               default:
+                       target_vblank = current_vblank +
+                               !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC);
+                       break;
+               }
+       } else if (crtc->funcs->page_flip == NULL ||
+                  (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) {
+               return -EINVAL;
+       }
+
+       drm_modeset_lock_crtc(crtc, crtc->primary);
+       if (crtc->primary->fb == NULL) {
+               /* The framebuffer is currently unbound, presumably
+                * due to a hotplug event, that userspace has not
+                * yet discovered.
+                */
+               ret = -EBUSY;
+               goto out;
+       }
+
+       fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
+       if (!fb) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       if (crtc->state) {
+               const struct drm_plane_state *state = crtc->primary->state;
+
+               ret = drm_framebuffer_check_src_coords(state->src_x,
+                                                      state->src_y,
+                                                      state->src_w,
+                                                      state->src_h,
+                                                      fb);
+       } else {
+               ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
+       }
+       if (ret)
+               goto out;
+
+       if (crtc->primary->fb->pixel_format != fb->pixel_format) {
+               DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+               e = kzalloc(sizeof *e, GFP_KERNEL);
+               if (!e) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+               e->event.base.length = sizeof(e->event);
+               e->event.user_data = page_flip->user_data;
+               ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base);
+               if (ret) {
+                       kfree(e);
+                       goto out;
+               }
+       }
+
+       crtc->primary->old_fb = crtc->primary->fb;
+       if (crtc->funcs->page_flip_target)
+               ret = crtc->funcs->page_flip_target(crtc, fb, e,
+                                                   page_flip->flags,
+                                                   target_vblank);
+       else
+               ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
+       if (ret) {
+               if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT)
+                       drm_event_cancel_free(dev, &e->base);
+               /* Keep the old fb, don't unref it. */
+               crtc->primary->old_fb = NULL;
+       } else {
+               crtc->primary->fb = fb;
+               /* Unref only the old framebuffer. */
+               fb = NULL;
+       }
+
+out:
+       if (ret && crtc->funcs->page_flip_target)
+               drm_crtc_vblank_put(crtc);
+       if (fb)
+               drm_framebuffer_unreference(fb);
+       if (crtc->primary->old_fb)
+               drm_framebuffer_unreference(crtc->primary->old_fb);
+       crtc->primary->old_fb = NULL;
+       drm_modeset_unlock_crtc(crtc);
+
+       return ret;
+}
index 16c4a7b..7899fc1 100644 (file)
  * the details.
  */
 
-/*
- * This is the minimal list of formats that seem to be safe for modeset use
- * with all current DRM drivers.  Most hardware can actually support more
- * formats than this and drivers may specify a more accurate list when
- * creating the primary plane.  However drivers that still call
- * drm_plane_init() will use this minimal format list as the default.
- */
-static const uint32_t safe_modeset_formats[] = {
-       DRM_FORMAT_XRGB8888,
-       DRM_FORMAT_ARGB8888,
-};
-
 /*
  * Returns the connectors currently associated with a CRTC.  This function
  * should be called twice:  once with a NULL connector list to retrieve
@@ -108,14 +96,9 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
 }
 
 /**
- * drm_plane_helper_check_update() - Check plane update for validity
- * @plane: plane object to update
- * @crtc: owning CRTC of owning plane
- * @fb: framebuffer to flip onto plane
- * @src: source coordinates in 16.16 fixed point
- * @dest: integer destination coordinates
+ * drm_plane_helper_check_state() - Check plane state for validity
+ * @state: plane state to check
  * @clip: integer clipping coordinates
- * @rotation: plane rotation
  * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
  * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
  * @can_position: is it legal to position the plane such that it
@@ -123,10 +106,9 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
  *                only be false for primary planes.
  * @can_update_disabled: can the plane be updated while the crtc
  *                       is disabled?
- * @visible: output parameter indicating whether plane is still visible after
- *           clipping
  *
- * Checks that a desired plane update is valid.  Drivers that provide
+ * Checks that a desired plane update is valid, and updates various
+ * bits of derived state (clipped coordinates etc.). Drivers that provide
  * their own plane handling rather than helper-provided implementations may
  * still wish to call this function to avoid duplication of error checking
  * code.
@@ -134,29 +116,38 @@ static int get_connectors_for_crtc(struct drm_crtc *crtc,
  * RETURNS:
  * Zero if update appears valid, error code on failure
  */
-int drm_plane_helper_check_update(struct drm_plane *plane,
-                                 struct drm_crtc *crtc,
-                                 struct drm_framebuffer *fb,
-                                 struct drm_rect *src,
-                                 struct drm_rect *dest,
-                                 const struct drm_rect *clip,
-                                 unsigned int rotation,
-                                 int min_scale,
-                                 int max_scale,
-                                 bool can_position,
-                                 bool can_update_disabled,
-                                 bool *visible)
+int drm_plane_helper_check_state(struct drm_plane_state *state,
+                                const struct drm_rect *clip,
+                                int min_scale,
+                                int max_scale,
+                                bool can_position,
+                                bool can_update_disabled)
 {
+       struct drm_crtc *crtc = state->crtc;
+       struct drm_framebuffer *fb = state->fb;
+       struct drm_rect *src = &state->src;
+       struct drm_rect *dst = &state->dst;
+       unsigned int rotation = state->rotation;
        int hscale, vscale;
 
+       src->x1 = state->src_x;
+       src->y1 = state->src_y;
+       src->x2 = state->src_x + state->src_w;
+       src->y2 = state->src_y + state->src_h;
+
+       dst->x1 = state->crtc_x;
+       dst->y1 = state->crtc_y;
+       dst->x2 = state->crtc_x + state->crtc_w;
+       dst->y2 = state->crtc_y + state->crtc_h;
+
        if (!fb) {
-               *visible = false;
+               state->visible = false;
                return 0;
        }
 
        /* crtc should only be NULL when disabling (i.e., !fb) */
        if (WARN_ON(!crtc)) {
-               *visible = false;
+               state->visible = false;
                return 0;
        }
 
@@ -168,20 +159,20 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
        drm_rect_rotate(src, fb->width << 16, fb->height << 16, rotation);
 
        /* Check scaling */
-       hscale = drm_rect_calc_hscale(src, dest, min_scale, max_scale);
-       vscale = drm_rect_calc_vscale(src, dest, min_scale, max_scale);
+       hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
+       vscale = drm_rect_calc_vscale(src, dst, min_scale, max_scale);
        if (hscale < 0 || vscale < 0) {
                DRM_DEBUG_KMS("Invalid scaling of plane\n");
-               drm_rect_debug_print("src: ", src, true);
-               drm_rect_debug_print("dst: ", dest, false);
+               drm_rect_debug_print("src: ", &state->src, true);
+               drm_rect_debug_print("dst: ", &state->dst, false);
                return -ERANGE;
        }
 
-       *visible = drm_rect_clip_scaled(src, dest, clip, hscale, vscale);
+       state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
 
        drm_rect_rotate_inv(src, fb->width << 16, fb->height << 16, rotation);
 
-       if (!*visible)
+       if (!state->visible)
                /*
                 * Plane isn't visible; some drivers can handle this
                 * so we just return success here.  Drivers that can't
@@ -191,15 +182,87 @@ int drm_plane_helper_check_update(struct drm_plane *plane,
                 */
                return 0;
 
-       if (!can_position && !drm_rect_equals(dest, clip)) {
+       if (!can_position && !drm_rect_equals(dst, clip)) {
                DRM_DEBUG_KMS("Plane must cover entire CRTC\n");
-               drm_rect_debug_print("dst: ", dest, false);
+               drm_rect_debug_print("dst: ", dst, false);
                drm_rect_debug_print("clip: ", clip, false);
                return -EINVAL;
        }
 
        return 0;
 }
+EXPORT_SYMBOL(drm_plane_helper_check_state);
+
+/**
+ * drm_plane_helper_check_update() - Check plane update for validity
+ * @plane: plane object to update
+ * @crtc: owning CRTC of owning plane
+ * @fb: framebuffer to flip onto plane
+ * @src: source coordinates in 16.16 fixed point
+ * @dst: integer destination coordinates
+ * @clip: integer clipping coordinates
+ * @rotation: plane rotation
+ * @min_scale: minimum @src:@dest scaling factor in 16.16 fixed point
+ * @max_scale: maximum @src:@dest scaling factor in 16.16 fixed point
+ * @can_position: is it legal to position the plane such that it
+ *                doesn't cover the entire crtc?  This will generally
+ *                only be false for primary planes.
+ * @can_update_disabled: can the plane be updated while the crtc
+ *                       is disabled?
+ * @visible: output parameter indicating whether plane is still visible after
+ *           clipping
+ *
+ * Checks that a desired plane update is valid.  Drivers that provide
+ * their own plane handling rather than helper-provided implementations may
+ * still wish to call this function to avoid duplication of error checking
+ * code.
+ *
+ * RETURNS:
+ * Zero if update appears valid, error code on failure
+ */
+int drm_plane_helper_check_update(struct drm_plane *plane,
+                                 struct drm_crtc *crtc,
+                                 struct drm_framebuffer *fb,
+                                 struct drm_rect *src,
+                                 struct drm_rect *dst,
+                                 const struct drm_rect *clip,
+                                 unsigned int rotation,
+                                 int min_scale,
+                                 int max_scale,
+                                 bool can_position,
+                                 bool can_update_disabled,
+                                 bool *visible)
+{
+       struct drm_plane_state state = {
+               .plane = plane,
+               .crtc = crtc,
+               .fb = fb,
+               .src_x = src->x1,
+               .src_y = src->y1,
+               .src_w = drm_rect_width(src),
+               .src_h = drm_rect_height(src),
+               .crtc_x = dst->x1,
+               .crtc_y = dst->y1,
+               .crtc_w = drm_rect_width(dst),
+               .crtc_h = drm_rect_height(dst),
+               .rotation = rotation,
+               .visible = *visible,
+       };
+       int ret;
+
+       ret = drm_plane_helper_check_state(&state, clip,
+                                          min_scale, max_scale,
+                                          can_position,
+                                          can_update_disabled);
+       if (ret)
+               return ret;
+
+       *src = state.src;
+       *dst = state.dst;
+       *visible = state.visible;
+
+       return 0;
+}
 EXPORT_SYMBOL(drm_plane_helper_check_update);
 
 /**
@@ -274,7 +337,7 @@ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc,
 
        ret = drm_plane_helper_check_update(plane, crtc, fb,
                                            &src, &dest, &clip,
-                                           BIT(DRM_ROTATE_0),
+                                           DRM_ROTATE_0,
                                            DRM_PLANE_HELPER_NO_SCALING,
                                            DRM_PLANE_HELPER_NO_SCALING,
                                            false, false, &visible);
@@ -363,60 +426,6 @@ const struct drm_plane_funcs drm_primary_helper_funcs = {
 };
 EXPORT_SYMBOL(drm_primary_helper_funcs);
 
-static struct drm_plane *create_primary_plane(struct drm_device *dev)
-{
-       struct drm_plane *primary;
-       int ret;
-
-       primary = kzalloc(sizeof(*primary), GFP_KERNEL);
-       if (primary == NULL) {
-               DRM_DEBUG_KMS("Failed to allocate primary plane\n");
-               return NULL;
-       }
-
-       /*
-        * Remove the format_default field from drm_plane when dropping
-        * this helper.
-        */
-       primary->format_default = true;
-
-       /* possible_crtc's will be filled in later by crtc_init */
-       ret = drm_universal_plane_init(dev, primary, 0,
-                                      &drm_primary_helper_funcs,
-                                      safe_modeset_formats,
-                                      ARRAY_SIZE(safe_modeset_formats),
-                                      DRM_PLANE_TYPE_PRIMARY, NULL);
-       if (ret) {
-               kfree(primary);
-               primary = NULL;
-       }
-
-       return primary;
-}
-
-/**
- * drm_crtc_init - Legacy CRTC initialization function
- * @dev: DRM device
- * @crtc: CRTC object to init
- * @funcs: callbacks for the new CRTC
- *
- * Initialize a CRTC object with a default helper-provided primary plane and no
- * cursor plane.
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
-                 const struct drm_crtc_funcs *funcs)
-{
-       struct drm_plane *primary;
-
-       primary = create_primary_plane(dev);
-       return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs,
-                                        NULL);
-}
-EXPORT_SYMBOL(drm_crtc_init);
-
 int drm_plane_helper_commit(struct drm_plane *plane,
                            struct drm_plane_state *plane_state,
                            struct drm_framebuffer *old_fb)
index 2c819ef..0262698 100644 (file)
@@ -48,8 +48,8 @@ static int drm_get_platform_dev(struct platform_device *platdev,
        DRM_DEBUG("\n");
 
        dev = drm_dev_alloc(driver, &platdev->dev);
-       if (!dev)
-               return -ENOMEM;
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
 
        dev->platformdev = platdev;
 
index 780589b..b22a94d 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <linux/export.h>
 #include <linux/dma-buf.h>
+#include <linux/rbtree.h>
 #include <drm/drmP.h>
 #include <drm/drm_gem.h>
 
  */
 
 struct drm_prime_member {
-       struct list_head entry;
        struct dma_buf *dma_buf;
        uint32_t handle;
+
+       struct rb_node dmabuf_rb;
+       struct rb_node handle_rb;
 };
 
 struct drm_prime_attachment {
@@ -75,6 +78,7 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
                                    struct dma_buf *dma_buf, uint32_t handle)
 {
        struct drm_prime_member *member;
+       struct rb_node **p, *rb;
 
        member = kmalloc(sizeof(*member), GFP_KERNEL);
        if (!member)
@@ -83,18 +87,56 @@ static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv,
        get_dma_buf(dma_buf);
        member->dma_buf = dma_buf;
        member->handle = handle;
-       list_add(&member->entry, &prime_fpriv->head);
+
+       rb = NULL;
+       p = &prime_fpriv->dmabufs.rb_node;
+       while (*p) {
+               struct drm_prime_member *pos;
+
+               rb = *p;
+               pos = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
+               if (dma_buf > pos->dma_buf)
+                       p = &rb->rb_right;
+               else
+                       p = &rb->rb_left;
+       }
+       rb_link_node(&member->dmabuf_rb, rb, p);
+       rb_insert_color(&member->dmabuf_rb, &prime_fpriv->dmabufs);
+
+       rb = NULL;
+       p = &prime_fpriv->handles.rb_node;
+       while (*p) {
+               struct drm_prime_member *pos;
+
+               rb = *p;
+               pos = rb_entry(rb, struct drm_prime_member, handle_rb);
+               if (handle > pos->handle)
+                       p = &rb->rb_right;
+               else
+                       p = &rb->rb_left;
+       }
+       rb_link_node(&member->handle_rb, rb, p);
+       rb_insert_color(&member->handle_rb, &prime_fpriv->handles);
+
        return 0;
 }
 
 static struct dma_buf *drm_prime_lookup_buf_by_handle(struct drm_prime_file_private *prime_fpriv,
                                                      uint32_t handle)
 {
-       struct drm_prime_member *member;
+       struct rb_node *rb;
+
+       rb = prime_fpriv->handles.rb_node;
+       while (rb) {
+               struct drm_prime_member *member;
 
-       list_for_each_entry(member, &prime_fpriv->head, entry) {
+               member = rb_entry(rb, struct drm_prime_member, handle_rb);
                if (member->handle == handle)
                        return member->dma_buf;
+               else if (member->handle < handle)
+                       rb = rb->rb_right;
+               else
+                       rb = rb->rb_left;
        }
 
        return NULL;
@@ -104,14 +146,23 @@ static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpri
                                       struct dma_buf *dma_buf,
                                       uint32_t *handle)
 {
-       struct drm_prime_member *member;
+       struct rb_node *rb;
+
+       rb = prime_fpriv->dmabufs.rb_node;
+       while (rb) {
+               struct drm_prime_member *member;
 
-       list_for_each_entry(member, &prime_fpriv->head, entry) {
+               member = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
                if (member->dma_buf == dma_buf) {
                        *handle = member->handle;
                        return 0;
+               } else if (member->dma_buf < dma_buf) {
+                       rb = rb->rb_right;
+               } else {
+                       rb = rb->rb_left;
                }
        }
+
        return -ENOENT;
 }
 
@@ -166,13 +217,24 @@ static void drm_gem_map_detach(struct dma_buf *dma_buf,
 void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv,
                                        struct dma_buf *dma_buf)
 {
-       struct drm_prime_member *member, *safe;
+       struct rb_node *rb;
 
-       list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) {
+       rb = prime_fpriv->dmabufs.rb_node;
+       while (rb) {
+               struct drm_prime_member *member;
+
+               member = rb_entry(rb, struct drm_prime_member, dmabuf_rb);
                if (member->dma_buf == dma_buf) {
+                       rb_erase(&member->handle_rb, &prime_fpriv->handles);
+                       rb_erase(&member->dmabuf_rb, &prime_fpriv->dmabufs);
+
                        dma_buf_put(dma_buf);
-                       list_del(&member->entry);
                        kfree(member);
+                       return;
+               } else if (member->dma_buf < dma_buf) {
+                       rb = rb->rb_right;
+               } else {
+                       rb = rb->rb_left;
                }
        }
 }
@@ -221,19 +283,48 @@ static void drm_gem_unmap_dma_buf(struct dma_buf_attachment *attach,
        /* nothing to be done here */
 }
 
+/**
+ * drm_gem_dmabuf_export - dma_buf export implementation for GEM
+ * @dev: parent device for the exported dmabuf
+ * @exp_info: the export information used by dma_buf_export()
+ *
+ * This wraps dma_buf_export() for use by generic GEM drivers that are using
+ * drm_gem_dmabuf_release(). In addition to calling dma_buf_export(), we take
+ * a reference to the drm_device which is released by drm_gem_dmabuf_release().
+ *
+ * Returns the new dmabuf.
+ */
+struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev,
+                                     struct dma_buf_export_info *exp_info)
+{
+       struct dma_buf *dma_buf;
+
+       dma_buf = dma_buf_export(exp_info);
+       if (!IS_ERR(dma_buf))
+               drm_dev_ref(dev);
+
+       return dma_buf;
+}
+EXPORT_SYMBOL(drm_gem_dmabuf_export);
+
 /**
  * drm_gem_dmabuf_release - dma_buf release implementation for GEM
  * @dma_buf: buffer to be released
  *
  * Generic release function for dma_bufs exported as PRIME buffers. GEM drivers
  * must use this in their dma_buf ops structure as the release callback.
+ * drm_gem_dmabuf_release() should be used in conjunction with
+ * drm_gem_dmabuf_export().
  */
 void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
 {
        struct drm_gem_object *obj = dma_buf->priv;
+       struct drm_device *dev = obj->dev;
 
        /* drop the reference on the export fd holds */
        drm_gem_object_unreference_unlocked(obj);
+
+       drm_dev_unref(dev);
 }
 EXPORT_SYMBOL(drm_gem_dmabuf_release);
 
@@ -335,19 +426,22 @@ static const struct dma_buf_ops drm_gem_prime_dmabuf_ops =  {
  * using the PRIME helpers.
  */
 struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
-                                    struct drm_gem_object *obj, int flags)
+                                    struct drm_gem_object *obj,
+                                    int flags)
 {
-       DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
-
-       exp_info.ops = &drm_gem_prime_dmabuf_ops;
-       exp_info.size = obj->size;
-       exp_info.flags = flags;
-       exp_info.priv = obj;
+       struct dma_buf_export_info exp_info = {
+               .exp_name = KBUILD_MODNAME, /* white lie for debug */
+               .owner = dev->driver->fops->owner,
+               .ops = &drm_gem_prime_dmabuf_ops,
+               .size = obj->size,
+               .flags = flags,
+               .priv = obj,
+       };
 
        if (dev->driver->gem_prime_res_obj)
                exp_info.resv = dev->driver->gem_prime_res_obj(obj);
 
-       return dma_buf_export(&exp_info);
+       return drm_gem_dmabuf_export(dev, &exp_info);
 }
 EXPORT_SYMBOL(drm_gem_prime_export);
 
@@ -759,12 +853,13 @@ EXPORT_SYMBOL(drm_prime_gem_destroy);
 
 void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv)
 {
-       INIT_LIST_HEAD(&prime_fpriv->head);
        mutex_init(&prime_fpriv->lock);
+       prime_fpriv->dmabufs = RB_ROOT;
+       prime_fpriv->handles = RB_ROOT;
 }
 
 void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv)
 {
        /* by now drm_gem_release should've made sure the list is empty */
-       WARN_ON(!list_empty(&prime_fpriv->head));
+       WARN_ON(!RB_EMPTY_ROOT(&prime_fpriv->dmabufs));
 }
index a0df377..f6b64d7 100644 (file)
@@ -129,6 +129,7 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev)
 {
        bool poll = false;
        struct drm_connector *connector;
+       unsigned long delay = DRM_OUTPUT_POLL_PERIOD;
 
        WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
 
@@ -141,8 +142,13 @@ void drm_kms_helper_poll_enable_locked(struct drm_device *dev)
                        poll = true;
        }
 
+       if (dev->mode_config.delayed_event) {
+               poll = true;
+               delay = 0;
+       }
+
        if (poll)
-               schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD);
+               schedule_delayed_work(&dev->mode_config.output_poll_work, delay);
 }
 EXPORT_SYMBOL(drm_kms_helper_poll_enable_locked);
 
diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
new file mode 100644 (file)
index 0000000..a4d81cf
--- /dev/null
@@ -0,0 +1,912 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <linux/export.h>
+#include <drm/drmP.h>
+#include <drm/drm_property.h>
+
+#include "drm_crtc_internal.h"
+
+/**
+ * DOC: overview
+ *
+ * Properties as represented by &drm_property are used to extend the modeset
+ * interface exposed to userspace. For the atomic modeset IOCTL properties are
+ * even the only way to transport metadata about the desired new modeset
+ * configuration from userspace to the kernel. Properties have a well-defined
+ * value range, which is enforced by the drm core. See the documentation of the
+ * flags member of struct &drm_property for an overview of the different
+ * property types and ranges.
+ *
+ * Properties don't store the current value directly, but need to be
+ * instatiated by attaching them to a &drm_mode_object with
+ * drm_object_attach_property().
+ *
+ * Property values are only 64bit. To support bigger piles of data (like gamma
+ * tables, color correction matrizes or large structures) a property can instead
+ * point at a &drm_property_blob with that additional data
+ *
+ * Properties are defined by their symbolic name, userspace must keep a
+ * per-object mapping from those names to the property ID used in the atomic
+ * IOCTL and in the get/set property IOCTL.
+ */
+
+static bool drm_property_type_valid(struct drm_property *property)
+{
+       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+               return !(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+       return !!(property->flags & DRM_MODE_PROP_LEGACY_TYPE);
+}
+
+/**
+ * drm_property_create - create a new property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+                                        const char *name, int num_values)
+{
+       struct drm_property *property = NULL;
+       int ret;
+
+       property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
+       if (!property)
+               return NULL;
+
+       property->dev = dev;
+
+       if (num_values) {
+               property->values = kcalloc(num_values, sizeof(uint64_t),
+                                          GFP_KERNEL);
+               if (!property->values)
+                       goto fail;
+       }
+
+       ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+       if (ret)
+               goto fail;
+
+       property->flags = flags;
+       property->num_values = num_values;
+       INIT_LIST_HEAD(&property->enum_list);
+
+       if (name) {
+               strncpy(property->name, name, DRM_PROP_NAME_LEN);
+               property->name[DRM_PROP_NAME_LEN-1] = '\0';
+       }
+
+       list_add_tail(&property->head, &dev->mode_config.property_list);
+
+       WARN_ON(!drm_property_type_valid(property));
+
+       return property;
+fail:
+       kfree(property->values);
+       kfree(property);
+       return NULL;
+}
+EXPORT_SYMBOL(drm_property_create);
+
+/**
+ * drm_property_create_enum - create a new enumeration property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property values
+ * @num_values: number of pre-defined values
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Userspace is only allowed to set one of the predefined values for enumeration
+ * properties.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+                                        const char *name,
+                                        const struct drm_prop_enum_list *props,
+                                        int num_values)
+{
+       struct drm_property *property;
+       int i, ret;
+
+       flags |= DRM_MODE_PROP_ENUM;
+
+       property = drm_property_create(dev, flags, name, num_values);
+       if (!property)
+               return NULL;
+
+       for (i = 0; i < num_values; i++) {
+               ret = drm_property_add_enum(property, i,
+                                     props[i].type,
+                                     props[i].name);
+               if (ret) {
+                       drm_property_destroy(dev, property);
+                       return NULL;
+               }
+       }
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_enum);
+
+/**
+ * drm_property_create_bitmask - create a new bitmask property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @props: enumeration lists with property bitflags
+ * @num_props: size of the @props array
+ * @supported_bits: bitmask of all supported enumeration values
+ *
+ * This creates a new bitmask drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Compared to plain enumeration properties userspace is allowed to set any
+ * or'ed together combination of the predefined property bitflag values
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        const struct drm_prop_enum_list *props,
+                                        int num_props,
+                                        uint64_t supported_bits)
+{
+       struct drm_property *property;
+       int i, ret, index = 0;
+       int num_values = hweight64(supported_bits);
+
+       flags |= DRM_MODE_PROP_BITMASK;
+
+       property = drm_property_create(dev, flags, name, num_values);
+       if (!property)
+               return NULL;
+       for (i = 0; i < num_props; i++) {
+               if (!(supported_bits & (1ULL << props[i].type)))
+                       continue;
+
+               if (WARN_ON(index >= num_values)) {
+                       drm_property_destroy(dev, property);
+                       return NULL;
+               }
+
+               ret = drm_property_add_enum(property, index++,
+                                     props[i].type,
+                                     props[i].name);
+               if (ret) {
+                       drm_property_destroy(dev, property);
+                       return NULL;
+               }
+       }
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_bitmask);
+
+static struct drm_property *property_create_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        uint64_t min, uint64_t max)
+{
+       struct drm_property *property;
+
+       property = drm_property_create(dev, flags, name, 2);
+       if (!property)
+               return NULL;
+
+       property->values[0] = min;
+       property->values[1] = max;
+
+       return property;
+}
+
+/**
+ * drm_property_create_range - create a new unsigned ranged property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @min: minimum value of the property
+ * @max: maximum value of the property
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Userspace is allowed to set any unsigned integer value in the (min, max)
+ * range inclusive.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+                                        const char *name,
+                                        uint64_t min, uint64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_RANGE | flags,
+                       name, min, max);
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
+/**
+ * drm_property_create_signed_range - create a new signed ranged property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @min: minimum value of the property
+ * @max: maximum value of the property
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Userspace is allowed to set any signed integer value in the (min, max)
+ * range inclusive.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+                                        int flags, const char *name,
+                                        int64_t min, int64_t max)
+{
+       return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags,
+                       name, I642U64(min), I642U64(max));
+}
+EXPORT_SYMBOL(drm_property_create_signed_range);
+
+/**
+ * drm_property_create_object - create a new object property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ * @type: object type from DRM_MODE_OBJECT_* defines
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * Userspace is only allowed to set this to any property value of the given
+ * @type. Only useful for atomic properties, which is enforced.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+                                               int flags, const char *name,
+                                               uint32_t type)
+{
+       struct drm_property *property;
+
+       flags |= DRM_MODE_PROP_OBJECT;
+
+       if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC)))
+               return NULL;
+
+       property = drm_property_create(dev, flags, name, 1);
+       if (!property)
+               return NULL;
+
+       property->values[0] = type;
+
+       return property;
+}
+EXPORT_SYMBOL(drm_property_create_object);
+
+/**
+ * drm_property_create_bool - create a new boolean property type
+ * @dev: drm device
+ * @flags: flags specifying the property type
+ * @name: name of the property
+ *
+ * This creates a new generic drm property which can then be attached to a drm
+ * object with drm_object_attach_property. The returned property object must be
+ * freed with drm_property_destroy(), which is done automatically when calling
+ * drm_mode_config_cleanup().
+ *
+ * This is implemented as a ranged property with only {0, 1} as valid values.
+ *
+ * Returns:
+ * A pointer to the newly created property on success, NULL on failure.
+ */
+struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
+                                             const char *name)
+{
+       return drm_property_create_range(dev, flags, name, 0, 1);
+}
+EXPORT_SYMBOL(drm_property_create_bool);
+
+/**
+ * drm_property_add_enum - add a possible value to an enumeration property
+ * @property: enumeration property to change
+ * @index: index of the new enumeration
+ * @value: value of the new enumeration
+ * @name: symbolic name of the new enumeration
+ *
+ * This functions adds enumerations to a property.
+ *
+ * It's use is deprecated, drivers should use one of the more specific helpers
+ * to directly create the property with all enumerations already attached.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_property_add_enum(struct drm_property *property, int index,
+                         uint64_t value, const char *name)
+{
+       struct drm_property_enum *prop_enum;
+
+       if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)))
+               return -EINVAL;
+
+       /*
+        * Bitmask enum properties have the additional constraint of values
+        * from 0 to 63
+        */
+       if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) &&
+                       (value > 63))
+               return -EINVAL;
+
+       if (!list_empty(&property->enum_list)) {
+               list_for_each_entry(prop_enum, &property->enum_list, head) {
+                       if (prop_enum->value == value) {
+                               strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+                               prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+                               return 0;
+                       }
+               }
+       }
+
+       prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL);
+       if (!prop_enum)
+               return -ENOMEM;
+
+       strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN);
+       prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0';
+       prop_enum->value = value;
+
+       property->values[index] = value;
+       list_add_tail(&prop_enum->head, &property->enum_list);
+       return 0;
+}
+EXPORT_SYMBOL(drm_property_add_enum);
+
+/**
+ * drm_property_destroy - destroy a drm property
+ * @dev: drm device
+ * @property: property to destry
+ *
+ * This function frees a property including any attached resources like
+ * enumeration values.
+ */
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property)
+{
+       struct drm_property_enum *prop_enum, *pt;
+
+       list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) {
+               list_del(&prop_enum->head);
+               kfree(prop_enum);
+       }
+
+       if (property->num_values)
+               kfree(property->values);
+       drm_mode_object_unregister(dev, &property->base);
+       list_del(&property->head);
+       kfree(property);
+}
+EXPORT_SYMBOL(drm_property_destroy);
+
+int drm_mode_getproperty_ioctl(struct drm_device *dev,
+                              void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_get_property *out_resp = data;
+       struct drm_property *property;
+       int enum_count = 0;
+       int value_count = 0;
+       int ret = 0, i;
+       int copied;
+       struct drm_property_enum *prop_enum;
+       struct drm_mode_property_enum __user *enum_ptr;
+       uint64_t __user *values_ptr;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       drm_modeset_lock_all(dev);
+       property = drm_property_find(dev, out_resp->prop_id);
+       if (!property) {
+               ret = -ENOENT;
+               goto done;
+       }
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
+               list_for_each_entry(prop_enum, &property->enum_list, head)
+                       enum_count++;
+       }
+
+       value_count = property->num_values;
+
+       strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN);
+       out_resp->name[DRM_PROP_NAME_LEN-1] = 0;
+       out_resp->flags = property->flags;
+
+       if ((out_resp->count_values >= value_count) && value_count) {
+               values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
+               for (i = 0; i < value_count; i++) {
+                       if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
+                               ret = -EFAULT;
+                               goto done;
+                       }
+               }
+       }
+       out_resp->count_values = value_count;
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) ||
+                       drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
+               if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
+                       copied = 0;
+                       enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
+                       list_for_each_entry(prop_enum, &property->enum_list, head) {
+
+                               if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+
+                               if (copy_to_user(&enum_ptr[copied].name,
+                                                &prop_enum->name, DRM_PROP_NAME_LEN)) {
+                                       ret = -EFAULT;
+                                       goto done;
+                               }
+                               copied++;
+                       }
+               }
+               out_resp->count_enum_blobs = enum_count;
+       }
+
+       /*
+        * NOTE: The idea seems to have been to use this to read all the blob
+        * property values. But nothing ever added them to the corresponding
+        * list, userspace always used the special-purpose get_blob ioctl to
+        * read the value for a blob property. It also doesn't make a lot of
+        * sense to return values here when everything else is just metadata for
+        * the property itself.
+        */
+       if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
+               out_resp->count_enum_blobs = 0;
+done:
+       drm_modeset_unlock_all(dev);
+       return ret;
+}
+
+static void drm_property_free_blob(struct kref *kref)
+{
+       struct drm_property_blob *blob =
+               container_of(kref, struct drm_property_blob, base.refcount);
+
+       mutex_lock(&blob->dev->mode_config.blob_lock);
+       list_del(&blob->head_global);
+       mutex_unlock(&blob->dev->mode_config.blob_lock);
+
+       drm_mode_object_unregister(blob->dev, &blob->base);
+
+       kfree(blob);
+}
+
+/**
+ * drm_property_create_blob - Create new blob property
+ * @dev: DRM device to create property for
+ * @length: Length to allocate for blob data
+ * @data: If specified, copies data into blob
+ *
+ * Creates a new blob property for a specified DRM device, optionally
+ * copying data. Note that blob properties are meant to be invariant, hence the
+ * data must be filled out before the blob is used as the value of any property.
+ *
+ * Returns:
+ * New blob property with a single reference on success, or an ERR_PTR
+ * value on failure.
+ */
+struct drm_property_blob *
+drm_property_create_blob(struct drm_device *dev, size_t length,
+                        const void *data)
+{
+       struct drm_property_blob *blob;
+       int ret;
+
+       if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
+               return ERR_PTR(-EINVAL);
+
+       blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+       if (!blob)
+               return ERR_PTR(-ENOMEM);
+
+       /* This must be explicitly initialised, so we can safely call list_del
+        * on it in the removal handler, even if it isn't in a file list. */
+       INIT_LIST_HEAD(&blob->head_file);
+       blob->length = length;
+       blob->dev = dev;
+
+       if (data)
+               memcpy(blob->data, data, length);
+
+       ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
+                                     true, drm_property_free_blob);
+       if (ret) {
+               kfree(blob);
+               return ERR_PTR(-EINVAL);
+       }
+
+       mutex_lock(&dev->mode_config.blob_lock);
+       list_add_tail(&blob->head_global,
+                     &dev->mode_config.property_blob_list);
+       mutex_unlock(&dev->mode_config.blob_lock);
+
+       return blob;
+}
+EXPORT_SYMBOL(drm_property_create_blob);
+
+/**
+ * drm_property_unreference_blob - Unreference a blob property
+ * @blob: Pointer to blob property
+ *
+ * Drop a reference on a blob property. May free the object.
+ */
+void drm_property_unreference_blob(struct drm_property_blob *blob)
+{
+       if (!blob)
+               return;
+
+       drm_mode_object_unreference(&blob->base);
+}
+EXPORT_SYMBOL(drm_property_unreference_blob);
+
+void drm_property_destroy_user_blobs(struct drm_device *dev,
+                                    struct drm_file *file_priv)
+{
+       struct drm_property_blob *blob, *bt;
+
+       /*
+        * When the file gets released that means no one else can access the
+        * blob list any more, so no need to grab dev->blob_lock.
+        */
+       list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) {
+               list_del_init(&blob->head_file);
+               drm_property_unreference_blob(blob);
+       }
+}
+
+/**
+ * drm_property_reference_blob - Take a reference on an existing property
+ * @blob: Pointer to blob property
+ *
+ * Take a new reference on an existing blob property. Returns @blob, which
+ * allows this to be used as a shorthand in assignments.
+ */
+struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob)
+{
+       drm_mode_object_reference(&blob->base);
+       return blob;
+}
+EXPORT_SYMBOL(drm_property_reference_blob);
+
+/**
+ * drm_property_lookup_blob - look up a blob property and take a reference
+ * @dev: drm device
+ * @id: id of the blob property
+ *
+ * If successful, this takes an additional reference to the blob property.
+ * callers need to make sure to eventually unreference the returned property
+ * again, using @drm_property_unreference_blob.
+ *
+ * Return:
+ * NULL on failure, pointer to the blob on success.
+ */
+struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
+                                                  uint32_t id)
+{
+       struct drm_mode_object *obj;
+       struct drm_property_blob *blob = NULL;
+
+       obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB);
+       if (obj)
+               blob = obj_to_blob(obj);
+       return blob;
+}
+EXPORT_SYMBOL(drm_property_lookup_blob);
+
+/**
+ * drm_property_replace_global_blob - replace existing blob property
+ * @dev: drm device
+ * @replace: location of blob property pointer to be replaced
+ * @length: length of data for new blob, or 0 for no data
+ * @data: content for new blob, or NULL for no data
+ * @obj_holds_id: optional object for property holding blob ID
+ * @prop_holds_id: optional property holding blob ID
+ * @return 0 on success or error on failure
+ *
+ * This function will replace a global property in the blob list, optionally
+ * updating a property which holds the ID of that property.
+ *
+ * If length is 0 or data is NULL, no new blob will be created, and the holding
+ * property, if specified, will be set to 0.
+ *
+ * Access to the replace pointer is assumed to be protected by the caller, e.g.
+ * by holding the relevant modesetting object lock for its parent.
+ *
+ * For example, a drm_connector has a 'PATH' property, which contains the ID
+ * of a blob property with the value of the MST path information. Calling this
+ * function with replace pointing to the connector's path_blob_ptr, length and
+ * data set for the new path information, obj_holds_id set to the connector's
+ * base object, and prop_holds_id set to the path property name, will perform
+ * a completely atomic update. The access to path_blob_ptr is protected by the
+ * caller holding a lock on the connector.
+ */
+int drm_property_replace_global_blob(struct drm_device *dev,
+                                    struct drm_property_blob **replace,
+                                    size_t length,
+                                    const void *data,
+                                    struct drm_mode_object *obj_holds_id,
+                                    struct drm_property *prop_holds_id)
+{
+       struct drm_property_blob *new_blob = NULL;
+       struct drm_property_blob *old_blob = NULL;
+       int ret;
+
+       WARN_ON(replace == NULL);
+
+       old_blob = *replace;
+
+       if (length && data) {
+               new_blob = drm_property_create_blob(dev, length, data);
+               if (IS_ERR(new_blob))
+                       return PTR_ERR(new_blob);
+       }
+
+       if (obj_holds_id) {
+               ret = drm_object_property_set_value(obj_holds_id,
+                                                   prop_holds_id,
+                                                   new_blob ?
+                                                       new_blob->base.id : 0);
+               if (ret != 0)
+                       goto err_created;
+       }
+
+       drm_property_unreference_blob(old_blob);
+       *replace = new_blob;
+
+       return 0;
+
+err_created:
+       drm_property_unreference_blob(new_blob);
+       return ret;
+}
+EXPORT_SYMBOL(drm_property_replace_global_blob);
+
+int drm_mode_getblob_ioctl(struct drm_device *dev,
+                          void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_get_blob *out_resp = data;
+       struct drm_property_blob *blob;
+       int ret = 0;
+       void __user *blob_ptr;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       blob = drm_property_lookup_blob(dev, out_resp->blob_id);
+       if (!blob)
+               return -ENOENT;
+
+       if (out_resp->length == blob->length) {
+               blob_ptr = (void __user *)(unsigned long)out_resp->data;
+               if (copy_to_user(blob_ptr, blob->data, blob->length)) {
+                       ret = -EFAULT;
+                       goto unref;
+               }
+       }
+       out_resp->length = blob->length;
+unref:
+       drm_property_unreference_blob(blob);
+
+       return ret;
+}
+
+int drm_mode_createblob_ioctl(struct drm_device *dev,
+                             void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_create_blob *out_resp = data;
+       struct drm_property_blob *blob;
+       void __user *blob_ptr;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       blob = drm_property_create_blob(dev, out_resp->length, NULL);
+       if (IS_ERR(blob))
+               return PTR_ERR(blob);
+
+       blob_ptr = (void __user *)(unsigned long)out_resp->data;
+       if (copy_from_user(blob->data, blob_ptr, out_resp->length)) {
+               ret = -EFAULT;
+               goto out_blob;
+       }
+
+       /* Dropping the lock between create_blob and our access here is safe
+        * as only the same file_priv can remove the blob; at this point, it is
+        * not associated with any file_priv. */
+       mutex_lock(&dev->mode_config.blob_lock);
+       out_resp->blob_id = blob->base.id;
+       list_add_tail(&blob->head_file, &file_priv->blobs);
+       mutex_unlock(&dev->mode_config.blob_lock);
+
+       return 0;
+
+out_blob:
+       drm_property_unreference_blob(blob);
+       return ret;
+}
+
+int drm_mode_destroyblob_ioctl(struct drm_device *dev,
+                              void *data, struct drm_file *file_priv)
+{
+       struct drm_mode_destroy_blob *out_resp = data;
+       struct drm_property_blob *blob = NULL, *bt;
+       bool found = false;
+       int ret = 0;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return -EINVAL;
+
+       blob = drm_property_lookup_blob(dev, out_resp->blob_id);
+       if (!blob)
+               return -ENOENT;
+
+       mutex_lock(&dev->mode_config.blob_lock);
+       /* Ensure the property was actually created by this user. */
+       list_for_each_entry(bt, &file_priv->blobs, head_file) {
+               if (bt == blob) {
+                       found = true;
+                       break;
+               }
+       }
+
+       if (!found) {
+               ret = -EPERM;
+               goto err;
+       }
+
+       /* We must drop head_file here, because we may not be the last
+        * reference on the blob. */
+       list_del_init(&blob->head_file);
+       mutex_unlock(&dev->mode_config.blob_lock);
+
+       /* One reference from lookup, and one from the filp. */
+       drm_property_unreference_blob(blob);
+       drm_property_unreference_blob(blob);
+
+       return 0;
+
+err:
+       mutex_unlock(&dev->mode_config.blob_lock);
+       drm_property_unreference_blob(blob);
+
+       return ret;
+}
+
+/* Some properties could refer to dynamic refcnt'd objects, or things that
+ * need special locking to handle lifetime issues (ie. to ensure the prop
+ * value doesn't become invalid part way through the property update due to
+ * race).  The value returned by reference via 'obj' should be passed back
+ * to drm_property_change_valid_put() after the property is set (and the
+ * object to which the property is attached has a chance to take it's own
+ * reference).
+ */
+bool drm_property_change_valid_get(struct drm_property *property,
+                                  uint64_t value, struct drm_mode_object **ref)
+{
+       int i;
+
+       if (property->flags & DRM_MODE_PROP_IMMUTABLE)
+               return false;
+
+       *ref = NULL;
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) {
+               if (value < property->values[0] || value > property->values[1])
+                       return false;
+               return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) {
+               int64_t svalue = U642I64(value);
+
+               if (svalue < U642I64(property->values[0]) ||
+                               svalue > U642I64(property->values[1]))
+                       return false;
+               return true;
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) {
+               uint64_t valid_mask = 0;
+
+               for (i = 0; i < property->num_values; i++)
+                       valid_mask |= (1ULL << property->values[i]);
+               return !(value & ~valid_mask);
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) {
+               struct drm_property_blob *blob;
+
+               if (value == 0)
+                       return true;
+
+               blob = drm_property_lookup_blob(property->dev, value);
+               if (blob) {
+                       *ref = &blob->base;
+                       return true;
+               } else {
+                       return false;
+               }
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+               /* a zero value for an object property translates to null: */
+               if (value == 0)
+                       return true;
+
+               *ref = __drm_mode_object_find(property->dev, value,
+                                             property->values[0]);
+               return *ref != NULL;
+       }
+
+       for (i = 0; i < property->num_values; i++)
+               if (property->values[i] == value)
+                       return true;
+       return false;
+}
+
+void drm_property_change_valid_put(struct drm_property *property,
+               struct drm_mode_object *ref)
+{
+       if (!ref)
+               return;
+
+       if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) {
+               drm_mode_object_unreference(ref);
+       } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB))
+               drm_property_unreference_blob(obj_to_blob(ref));
+}
index a8e2c86..73e53a8 100644 (file)
@@ -100,7 +100,7 @@ static int drm_calc_scale(int src, int dst)
 {
        int scale = 0;
 
-       if (src < 0 || dst < 0)
+       if (WARN_ON(src < 0 || dst < 0))
                return -EINVAL;
 
        if (dst == 0)
@@ -317,38 +317,38 @@ void drm_rect_rotate(struct drm_rect *r,
 {
        struct drm_rect tmp;
 
-       if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) {
+       if (rotation & (DRM_REFLECT_X | DRM_REFLECT_Y)) {
                tmp = *r;
 
-               if (rotation & BIT(DRM_REFLECT_X)) {
+               if (rotation & DRM_REFLECT_X) {
                        r->x1 = width - tmp.x2;
                        r->x2 = width - tmp.x1;
                }
 
-               if (rotation & BIT(DRM_REFLECT_Y)) {
+               if (rotation & DRM_REFLECT_Y) {
                        r->y1 = height - tmp.y2;
                        r->y2 = height - tmp.y1;
                }
        }
 
        switch (rotation & DRM_ROTATE_MASK) {
-       case BIT(DRM_ROTATE_0):
+       case DRM_ROTATE_0:
                break;
-       case BIT(DRM_ROTATE_90):
+       case DRM_ROTATE_90:
                tmp = *r;
                r->x1 = tmp.y1;
                r->x2 = tmp.y2;
                r->y1 = width - tmp.x2;
                r->y2 = width - tmp.x1;
                break;
-       case BIT(DRM_ROTATE_180):
+       case DRM_ROTATE_180:
                tmp = *r;
                r->x1 = width - tmp.x2;
                r->x2 = width - tmp.x1;
                r->y1 = height - tmp.y2;
                r->y2 = height - tmp.y1;
                break;
-       case BIT(DRM_ROTATE_270):
+       case DRM_ROTATE_270:
                tmp = *r;
                r->x1 = height - tmp.y2;
                r->x2 = height - tmp.y1;
@@ -392,23 +392,23 @@ void drm_rect_rotate_inv(struct drm_rect *r,
        struct drm_rect tmp;
 
        switch (rotation & DRM_ROTATE_MASK) {
-       case BIT(DRM_ROTATE_0):
+       case DRM_ROTATE_0:
                break;
-       case BIT(DRM_ROTATE_90):
+       case DRM_ROTATE_90:
                tmp = *r;
                r->x1 = width - tmp.y2;
                r->x2 = width - tmp.y1;
                r->y1 = tmp.x1;
                r->y2 = tmp.x2;
                break;
-       case BIT(DRM_ROTATE_180):
+       case DRM_ROTATE_180:
                tmp = *r;
                r->x1 = width - tmp.x2;
                r->x2 = width - tmp.x1;
                r->y1 = height - tmp.y2;
                r->y2 = height - tmp.y1;
                break;
-       case BIT(DRM_ROTATE_270):
+       case DRM_ROTATE_270:
                tmp = *r;
                r->x1 = tmp.y1;
                r->x2 = tmp.y2;
@@ -419,15 +419,15 @@ void drm_rect_rotate_inv(struct drm_rect *r,
                break;
        }
 
-       if (rotation & (BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y))) {
+       if (rotation & (DRM_REFLECT_X | DRM_REFLECT_Y)) {
                tmp = *r;
 
-               if (rotation & BIT(DRM_REFLECT_X)) {
+               if (rotation & DRM_REFLECT_X) {
                        r->x1 = width - tmp.x2;
                        r->x2 = width - tmp.x1;
                }
 
-               if (rotation & BIT(DRM_REFLECT_Y)) {
+               if (rotation & DRM_REFLECT_Y) {
                        r->y1 = height - tmp.y2;
                        r->y2 = height - tmp.y1;
                }
index bf70431..275bca4 100644 (file)
@@ -68,7 +68,7 @@ static void drm_sg_cleanup(struct drm_sg_mem * entry)
 void drm_legacy_sg_cleanup(struct drm_device *dev)
 {
        if (drm_core_check_feature(dev, DRIVER_SG) && dev->sg &&
-           !drm_core_check_feature(dev, DRIVER_MODESET)) {
+           drm_core_check_feature(dev, DRIVER_LEGACY)) {
                drm_sg_cleanup(dev->sg);
                dev->sg = NULL;
        }
@@ -88,7 +88,7 @@ int drm_legacy_sg_alloc(struct drm_device *dev, void *data,
 
        DRM_DEBUG("\n");
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_SG))
@@ -201,7 +201,7 @@ int drm_legacy_sg_free(struct drm_device *dev, void *data,
        struct drm_scatter_gather *request = data;
        struct drm_sg_mem *entry;
 
-       if (drm_core_check_feature(dev, DRIVER_MODESET))
+       if (!drm_core_check_feature(dev, DRIVER_LEGACY))
                return -EINVAL;
 
        if (!drm_core_check_feature(dev, DRIVER_SG))
index 0db36d2..7bae08c 100644 (file)
@@ -34,6 +34,12 @@ static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = {
        .destroy = drm_encoder_cleanup,
 };
 
+static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *state)
+{
+       return drm_atomic_add_affected_planes(state->state, crtc);
+}
+
 static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_simple_display_pipe *pipe;
@@ -57,6 +63,7 @@ static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc)
 }
 
 static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
+       .atomic_check = drm_simple_kms_crtc_check,
        .disable = drm_simple_kms_crtc_disable,
        .enable = drm_simple_kms_crtc_enable,
 };
@@ -73,22 +80,9 @@ static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
 static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
                                        struct drm_plane_state *plane_state)
 {
-       struct drm_rect src = {
-               .x1 = plane_state->src_x,
-               .y1 = plane_state->src_y,
-               .x2 = plane_state->src_x + plane_state->src_w,
-               .y2 = plane_state->src_y + plane_state->src_h,
-       };
-       struct drm_rect dest = {
-               .x1 = plane_state->crtc_x,
-               .y1 = plane_state->crtc_y,
-               .x2 = plane_state->crtc_x + plane_state->crtc_w,
-               .y2 = plane_state->crtc_y + plane_state->crtc_h,
-       };
        struct drm_rect clip = { 0 };
        struct drm_simple_display_pipe *pipe;
        struct drm_crtc_state *crtc_state;
-       bool visible;
        int ret;
 
        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
@@ -102,17 +96,15 @@ static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
 
        clip.x2 = crtc_state->adjusted_mode.hdisplay;
        clip.y2 = crtc_state->adjusted_mode.vdisplay;
-       ret = drm_plane_helper_check_update(plane, &pipe->crtc,
-                                           plane_state->fb,
-                                           &src, &dest, &clip,
-                                           plane_state->rotation,
-                                           DRM_PLANE_HELPER_NO_SCALING,
-                                           DRM_PLANE_HELPER_NO_SCALING,
-                                           false, true, &visible);
+
+       ret = drm_plane_helper_check_state(plane_state, &clip,
+                                          DRM_PLANE_HELPER_NO_SCALING,
+                                          DRM_PLANE_HELPER_NO_SCALING,
+                                          false, true);
        if (ret)
                return ret;
 
-       if (!visible)
+       if (!plane_state->visible)
                return -EINVAL;
 
        if (!pipe->funcs || !pipe->funcs->check)
@@ -133,7 +125,33 @@ static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
        pipe->funcs->update(pipe, pstate);
 }
 
+static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
+                                          struct drm_plane_state *state)
+{
+       struct drm_simple_display_pipe *pipe;
+
+       pipe = container_of(plane, struct drm_simple_display_pipe, plane);
+       if (!pipe->funcs || !pipe->funcs->prepare_fb)
+               return 0;
+
+       return pipe->funcs->prepare_fb(pipe, state);
+}
+
+static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
+                                           struct drm_plane_state *state)
+{
+       struct drm_simple_display_pipe *pipe;
+
+       pipe = container_of(plane, struct drm_simple_display_pipe, plane);
+       if (!pipe->funcs || !pipe->funcs->cleanup_fb)
+               return;
+
+       pipe->funcs->cleanup_fb(pipe, state);
+}
+
 static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
+       .prepare_fb = drm_simple_kms_plane_prepare_fb,
+       .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
        .atomic_check = drm_simple_kms_plane_atomic_check,
        .atomic_update = drm_simple_kms_plane_atomic_update,
 };
@@ -147,17 +165,62 @@ static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 };
 
+/**
+ * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
+ * @pipe: simple display pipe object
+ * @bridge: bridge to attach
+ *
+ * Makes it possible to still use the drm_simple_display_pipe helpers when
+ * a DRM bridge has to be used.
+ *
+ * Note that you probably want to initialize the pipe by passing a NULL
+ * connector to drm_simple_display_pipe_init().
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
+                                         struct drm_bridge *bridge)
+{
+       bridge->encoder = &pipe->encoder;
+       pipe->encoder.bridge = bridge;
+       return drm_bridge_attach(pipe->encoder.dev, bridge);
+}
+EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
+
+/**
+ * drm_simple_display_pipe_detach_bridge - Detach the bridge from the display pipe
+ * @pipe: simple display pipe object
+ *
+ * Detaches the drm bridge previously attached with
+ * drm_simple_display_pipe_attach_bridge()
+ */
+void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe)
+{
+       if (WARN_ON(!pipe->encoder.bridge))
+               return;
+
+       drm_bridge_detach(pipe->encoder.bridge);
+       pipe->encoder.bridge = NULL;
+}
+EXPORT_SYMBOL(drm_simple_display_pipe_detach_bridge);
+
 /**
  * drm_simple_display_pipe_init - Initialize a simple display pipeline
  * @dev: DRM device
  * @pipe: simple display pipe object to initialize
  * @funcs: callbacks for the display pipe (optional)
- * @formats: array of supported formats (%DRM_FORMAT_*)
+ * @formats: array of supported formats (DRM_FORMAT\_\*)
  * @format_count: number of elements in @formats
- * @connector: connector to attach and register
+ * @connector: connector to attach and register (optional)
  *
  * Sets up a display pipeline which consist of a really simple
- * plane-crtc-encoder pipe coupled with the provided connector.
+ * plane-crtc-encoder pipe.
+ *
+ * If a connector is supplied, the pipe will be coupled with the provided
+ * connector. You may supply a NULL connector when using drm bridges, that
+ * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
+ *
  * Teardown of a simple display pipe is all handled automatically by the drm
  * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
  * release the memory for the structure themselves.
@@ -196,7 +259,7 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
        encoder->possible_crtcs = 1 << drm_crtc_index(crtc);
        ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs,
                               DRM_MODE_ENCODER_NONE, NULL);
-       if (ret)
+       if (ret || !connector)
                return ret;
 
        return drm_mode_connector_attach_encoder(connector, encoder);
index 32dd821..9a37196 100644 (file)
@@ -19,7 +19,6 @@
 #include <linux/export.h>
 
 #include <drm/drm_sysfs.h>
-#include <drm/drm_core.h>
 #include <drm/drmP.h>
 #include "drm_internal.h"
 
@@ -37,12 +36,7 @@ static char *drm_devnode(struct device *dev, umode_t *mode)
        return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev));
 }
 
-static CLASS_ATTR_STRING(version, S_IRUGO,
-               CORE_NAME " "
-               __stringify(CORE_MAJOR) "."
-               __stringify(CORE_MINOR) "."
-               __stringify(CORE_PATCHLEVEL) " "
-               CORE_DATE);
+static CLASS_ATTR_STRING(version, S_IRUGO, "drm 1.1.0 20060810");
 
 /**
  * drm_sysfs_init - initialize sysfs helpers
index f306c88..20cc33d 100644 (file)
@@ -25,7 +25,6 @@
 #include <drm/drmP.h>
 #include <drm/drm_mm.h>
 #include <drm/drm_vma_manager.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/rbtree.h>
@@ -86,7 +85,6 @@ void drm_vma_offset_manager_init(struct drm_vma_offset_manager *mgr,
                                 unsigned long page_offset, unsigned long size)
 {
        rwlock_init(&mgr->vm_lock);
-       mgr->vm_addr_space_rb = RB_ROOT;
        drm_mm_init(&mgr->vm_addr_space_mm, page_offset, size);
 }
 EXPORT_SYMBOL(drm_vma_offset_manager_init);
@@ -145,16 +143,16 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m
                                                         unsigned long start,
                                                         unsigned long pages)
 {
-       struct drm_vma_offset_node *node, *best;
+       struct drm_mm_node *node, *best;
        struct rb_node *iter;
        unsigned long offset;
 
-       iter = mgr->vm_addr_space_rb.rb_node;
+       iter = mgr->vm_addr_space_mm.interval_tree.rb_node;
        best = NULL;
 
        while (likely(iter)) {
-               node = rb_entry(iter, struct drm_vma_offset_node, vm_rb);
-               offset = node->vm_node.start;
+               node = rb_entry(iter, struct drm_mm_node, rb);
+               offset = node->start;
                if (start >= offset) {
                        iter = iter->rb_right;
                        best = node;
@@ -167,38 +165,17 @@ struct drm_vma_offset_node *drm_vma_offset_lookup_locked(struct drm_vma_offset_m
 
        /* verify that the node spans the requested area */
        if (best) {
-               offset = best->vm_node.start + best->vm_node.size;
+               offset = best->start + best->size;
                if (offset < start + pages)
                        best = NULL;
        }
 
-       return best;
-}
-EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
-
-/* internal helper to link @node into the rb-tree */
-static void _drm_vma_offset_add_rb(struct drm_vma_offset_manager *mgr,
-                                  struct drm_vma_offset_node *node)
-{
-       struct rb_node **iter = &mgr->vm_addr_space_rb.rb_node;
-       struct rb_node *parent = NULL;
-       struct drm_vma_offset_node *iter_node;
-
-       while (likely(*iter)) {
-               parent = *iter;
-               iter_node = rb_entry(*iter, struct drm_vma_offset_node, vm_rb);
+       if (!best)
+               return NULL;
 
-               if (node->vm_node.start < iter_node->vm_node.start)
-                       iter = &(*iter)->rb_left;
-               else if (node->vm_node.start > iter_node->vm_node.start)
-                       iter = &(*iter)->rb_right;
-               else
-                       BUG();
-       }
-
-       rb_link_node(&node->vm_rb, parent, iter);
-       rb_insert_color(&node->vm_rb, &mgr->vm_addr_space_rb);
+       return container_of(best, struct drm_vma_offset_node, vm_node);
 }
+EXPORT_SYMBOL(drm_vma_offset_lookup_locked);
 
 /**
  * drm_vma_offset_add() - Add offset node to manager
@@ -240,8 +217,6 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
        if (ret)
                goto out_unlock;
 
-       _drm_vma_offset_add_rb(mgr, node);
-
 out_unlock:
        write_unlock(&mgr->vm_lock);
        return ret;
@@ -265,7 +240,6 @@ void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
        write_lock(&mgr->vm_lock);
 
        if (drm_mm_node_allocated(&node->vm_node)) {
-               rb_erase(&node->vm_rb, &mgr->vm_addr_space_rb);
                drm_mm_remove_node(&node->vm_node);
                memset(&node->vm_node, 0, sizeof(node->vm_node));
        }
@@ -277,9 +251,9 @@ EXPORT_SYMBOL(drm_vma_offset_remove);
 /**
  * drm_vma_node_allow - Add open-file to list of allowed users
  * @node: Node to modify
- * @filp: Open file to add
+ * @tag: Tag of file to remove
  *
- * Add @filp to the list of allowed open-files for this node. If @filp is
+ * Add @tag to the list of allowed open-files for this node. If @tag is
  * already on this list, the ref-count is incremented.
  *
  * The list of allowed-users is preserved across drm_vma_offset_add() and
@@ -294,7 +268,7 @@ EXPORT_SYMBOL(drm_vma_offset_remove);
  * RETURNS:
  * 0 on success, negative error code on internal failure (out-of-mem)
  */
-int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp)
+int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag)
 {
        struct rb_node **iter;
        struct rb_node *parent = NULL;
@@ -315,10 +289,10 @@ int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp)
                parent = *iter;
                entry = rb_entry(*iter, struct drm_vma_offset_file, vm_rb);
 
-               if (filp == entry->vm_filp) {
+               if (tag == entry->vm_tag) {
                        entry->vm_count++;
                        goto unlock;
-               } else if (filp > entry->vm_filp) {
+               } else if (tag > entry->vm_tag) {
                        iter = &(*iter)->rb_right;
                } else {
                        iter = &(*iter)->rb_left;
@@ -330,7 +304,7 @@ int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp)
                goto unlock;
        }
 
-       new->vm_filp = filp;
+       new->vm_tag = tag;
        new->vm_count = 1;
        rb_link_node(&new->vm_rb, parent, iter);
        rb_insert_color(&new->vm_rb, &node->vm_files);
@@ -346,17 +320,18 @@ EXPORT_SYMBOL(drm_vma_node_allow);
 /**
  * drm_vma_node_revoke - Remove open-file from list of allowed users
  * @node: Node to modify
- * @filp: Open file to remove
+ * @tag: Tag of file to remove
  *
- * Decrement the ref-count of @filp in the list of allowed open-files on @node.
- * If the ref-count drops to zero, remove @filp from the list. You must call
- * this once for every drm_vma_node_allow() on @filp.
+ * Decrement the ref-count of @tag in the list of allowed open-files on @node.
+ * If the ref-count drops to zero, remove @tag from the list. You must call
+ * this once for every drm_vma_node_allow() on @tag.
  *
  * This is locked against concurrent access internally.
  *
- * If @filp is not on the list, nothing is done.
+ * If @tag is not on the list, nothing is done.
  */
-void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp)
+void drm_vma_node_revoke(struct drm_vma_offset_node *node,
+                        struct drm_file *tag)
 {
        struct drm_vma_offset_file *entry;
        struct rb_node *iter;
@@ -366,13 +341,13 @@ void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp)
        iter = node->vm_files.rb_node;
        while (likely(iter)) {
                entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
-               if (filp == entry->vm_filp) {
+               if (tag == entry->vm_tag) {
                        if (!--entry->vm_count) {
                                rb_erase(&entry->vm_rb, &node->vm_files);
                                kfree(entry);
                        }
                        break;
-               } else if (filp > entry->vm_filp) {
+               } else if (tag > entry->vm_tag) {
                        iter = iter->rb_right;
                } else {
                        iter = iter->rb_left;
@@ -386,9 +361,9 @@ EXPORT_SYMBOL(drm_vma_node_revoke);
 /**
  * drm_vma_node_is_allowed - Check whether an open-file is granted access
  * @node: Node to check
- * @filp: Open-file to check for
+ * @tag: Tag of file to remove
  *
- * Search the list in @node whether @filp is currently on the list of allowed
+ * Search the list in @node whether @tag is currently on the list of allowed
  * open-files (see drm_vma_node_allow()).
  *
  * This is locked against concurrent access internally.
@@ -397,7 +372,7 @@ EXPORT_SYMBOL(drm_vma_node_revoke);
  * true iff @filp is on the list
  */
 bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
-                            struct file *filp)
+                            struct drm_file *tag)
 {
        struct drm_vma_offset_file *entry;
        struct rb_node *iter;
@@ -407,9 +382,9 @@ bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
        iter = node->vm_files.rb_node;
        while (likely(iter)) {
                entry = rb_entry(iter, struct drm_vma_offset_file, vm_rb);
-               if (filp == entry->vm_filp)
+               if (tag == entry->vm_tag)
                        break;
-               else if (filp > entry->vm_filp)
+               else if (tag > entry->vm_tag)
                        iter = iter->rb_right;
                else
                        iter = iter->rb_left;
index d8d5564..cb86c7e 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "common.xml.h"
 #include "state.xml.h"
+#include "state_hi.xml.h"
 #include "state_3d.xml.h"
 #include "cmdstream.xml.h"
 
@@ -117,11 +118,6 @@ static void etnaviv_cmd_select_pipe(struct etnaviv_gpu *gpu,
                       VIVS_GL_PIPE_SELECT_PIPE(pipe));
 }
 
-static u32 gpu_va(struct etnaviv_gpu *gpu, struct etnaviv_cmdbuf *buf)
-{
-       return buf->paddr - gpu->memory_base;
-}
-
 static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
        struct etnaviv_cmdbuf *buf, u32 off, u32 len)
 {
@@ -129,7 +125,7 @@ static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu,
        u32 *ptr = buf->vaddr + off;
 
        dev_info(gpu->dev, "virt %p phys 0x%08x free 0x%08x\n",
-                       ptr, gpu_va(gpu, buf) + off, size - len * 4 - off);
+                       ptr, etnaviv_iommu_get_cmdbuf_va(gpu, buf) + off, size - len * 4 - off);
 
        print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
                        ptr, len * 4, 0);
@@ -162,7 +158,7 @@ static u32 etnaviv_buffer_reserve(struct etnaviv_gpu *gpu,
        if (buffer->user_size + cmd_dwords * sizeof(u64) > buffer->size)
                buffer->user_size = 0;
 
-       return gpu_va(gpu, buffer) + buffer->user_size;
+       return etnaviv_iommu_get_cmdbuf_va(gpu, buffer) + buffer->user_size;
 }
 
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
@@ -173,7 +169,41 @@ u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu)
        buffer->user_size = 0;
 
        CMD_WAIT(buffer);
-       CMD_LINK(buffer, 2, gpu_va(gpu, buffer) + buffer->user_size - 4);
+       CMD_LINK(buffer, 2, etnaviv_iommu_get_cmdbuf_va(gpu, buffer) +
+                buffer->user_size - 4);
+
+       return buffer->user_size / 8;
+}
+
+u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr)
+{
+       struct etnaviv_cmdbuf *buffer = gpu->buffer;
+
+       buffer->user_size = 0;
+
+       if (gpu->identity.features & chipFeatures_PIPE_3D) {
+               CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
+                              VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_3D));
+               CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
+                       mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K);
+               CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr);
+               CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
+               CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
+       }
+
+       if (gpu->identity.features & chipFeatures_PIPE_2D) {
+               CMD_LOAD_STATE(buffer, VIVS_GL_PIPE_SELECT,
+                              VIVS_GL_PIPE_SELECT_PIPE(ETNA_PIPE_2D));
+               CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
+                       mtlb_addr | VIVS_MMUv2_CONFIGURATION_MODE_MODE4_K);
+               CMD_LOAD_STATE(buffer, VIVS_MMUv2_SAFE_ADDRESS, safe_addr);
+               CMD_SEM(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
+               CMD_STALL(buffer, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
+       }
+
+       CMD_END(buffer);
+
+       buffer->user_size = ALIGN(buffer->user_size, 8);
 
        return buffer->user_size / 8;
 }
@@ -231,7 +261,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
        if (drm_debug & DRM_UT_DRIVER)
                etnaviv_buffer_dump(gpu, buffer, 0, 0x50);
 
-       link_target = gpu_va(gpu, cmdbuf);
+       link_target = etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf);
        link_dwords = cmdbuf->size / 8;
 
        /*
@@ -246,8 +276,12 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
                extra_dwords = 1;
 
                /* flush command */
-               if (gpu->mmu->need_flush)
-                       extra_dwords += 1;
+               if (gpu->mmu->need_flush) {
+                       if (gpu->mmu->version == ETNAVIV_IOMMU_V1)
+                               extra_dwords += 1;
+                       else
+                               extra_dwords += 3;
+               }
 
                /* pipe switch commands */
                if (gpu->switch_context)
@@ -257,12 +291,23 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
                if (gpu->mmu->need_flush) {
                        /* Add the MMU flush */
-                       CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
-                                      VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
-                                      VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
-                                      VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
-                                      VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
-                                      VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+                       if (gpu->mmu->version == ETNAVIV_IOMMU_V1) {
+                               CMD_LOAD_STATE(buffer, VIVS_GL_FLUSH_MMU,
+                                              VIVS_GL_FLUSH_MMU_FLUSH_FEMMU |
+                                              VIVS_GL_FLUSH_MMU_FLUSH_UNK1 |
+                                              VIVS_GL_FLUSH_MMU_FLUSH_UNK2 |
+                                              VIVS_GL_FLUSH_MMU_FLUSH_PEMMU |
+                                              VIVS_GL_FLUSH_MMU_FLUSH_UNK4);
+                       } else {
+                               CMD_LOAD_STATE(buffer, VIVS_MMUv2_CONFIGURATION,
+                                       VIVS_MMUv2_CONFIGURATION_MODE_MASK |
+                                       VIVS_MMUv2_CONFIGURATION_ADDRESS_MASK |
+                                       VIVS_MMUv2_CONFIGURATION_FLUSH_FLUSH);
+                               CMD_SEM(buffer, SYNC_RECIPIENT_FE,
+                                       SYNC_RECIPIENT_PE);
+                               CMD_STALL(buffer, SYNC_RECIPIENT_FE,
+                                       SYNC_RECIPIENT_PE);
+                       }
 
                        gpu->mmu->need_flush = false;
                }
@@ -301,7 +346,7 @@ void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
 
        if (drm_debug & DRM_UT_DRIVER)
                pr_info("stream link to 0x%08x @ 0x%08x %p\n",
-                       return_target, gpu_va(gpu, cmdbuf), cmdbuf->vaddr);
+                       return_target, etnaviv_iommu_get_cmdbuf_va(gpu, cmdbuf), cmdbuf->vaddr);
 
        if (drm_debug & DRM_UT_DRIVER) {
                print_hex_dump(KERN_INFO, "cmd ", DUMP_PREFIX_OFFSET, 16, 4,
index ffd1b32..aa68766 100644 (file)
@@ -488,8 +488,7 @@ static const struct file_operations fops = {
 };
 
 static struct drm_driver etnaviv_drm_driver = {
-       .driver_features    = DRIVER_HAVE_IRQ |
-                               DRIVER_GEM |
+       .driver_features    = DRIVER_GEM |
                                DRIVER_PRIME |
                                DRIVER_RENDER,
        .open               = etnaviv_open,
@@ -530,10 +529,8 @@ static int etnaviv_bind(struct device *dev)
        int ret;
 
        drm = drm_dev_alloc(&etnaviv_drm_driver, dev);
-       if (!drm)
-               return -ENOMEM;
-
-       drm->platformdev = to_platform_device(dev);
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
        if (!priv) {
index 115c5bc..65e0576 100644 (file)
@@ -96,6 +96,7 @@ struct drm_gem_object *etnaviv_gem_new(struct drm_device *dev,
 int etnaviv_gem_new_userptr(struct drm_device *dev, struct drm_file *file,
        uintptr_t ptr, u32 size, u32 flags, u32 *handle);
 u16 etnaviv_buffer_init(struct etnaviv_gpu *gpu);
+u16 etnaviv_buffer_config_mmuv2(struct etnaviv_gpu *gpu, u32 mtlb_addr, u32 safe_addr);
 void etnaviv_buffer_end(struct etnaviv_gpu *gpu);
 void etnaviv_buffer_queue(struct etnaviv_gpu *gpu, unsigned int event,
        struct etnaviv_cmdbuf *cmdbuf);
index 4a29eea..2bef501 100644 (file)
@@ -175,11 +175,13 @@ void etnaviv_core_dump(struct etnaviv_gpu *gpu)
        etnaviv_core_dump_registers(&iter, gpu);
        etnaviv_core_dump_mmu(&iter, gpu, mmu_size);
        etnaviv_core_dump_mem(&iter, ETDUMP_BUF_RING, gpu->buffer->vaddr,
-                             gpu->buffer->size, gpu->buffer->paddr);
+                             gpu->buffer->size,
+                             etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer));
 
        list_for_each_entry(cmd, &gpu->active_cmd_list, node)
                etnaviv_core_dump_mem(&iter, ETDUMP_BUF_CMD, cmd->vaddr,
-                                     cmd->size, cmd->paddr);
+                                     cmd->size,
+                                     etnaviv_iommu_get_cmdbuf_va(gpu, cmd));
 
        /* Reserve space for the bomap */
        if (n_bomap_pages) {
index b382cf5..b1254f8 100644 (file)
@@ -22,8 +22,6 @@
 #include "etnaviv_gpu.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_mmu.h"
-#include "etnaviv_iommu.h"
-#include "etnaviv_iommu_v2.h"
 #include "common.xml.h"
 #include "state.xml.h"
 #include "state_hi.xml.h"
@@ -329,6 +327,18 @@ static void etnaviv_hw_identify(struct etnaviv_gpu *gpu)
                                gpu->identity.revision = 0x1051;
                        }
                }
+
+               /*
+                * NXP likes to call the GPU on the i.MX6QP GC2000+, but in
+                * reality it's just a re-branded GC3000. We can identify this
+                * core by the upper half of the revision register being all 1.
+                * Fix model/rev here, so all other places can refer to this
+                * core by its real identity.
+                */
+               if (etnaviv_is_model_rev(gpu, GC2000, 0xffff5450)) {
+                       gpu->identity.model = chipModel_GC3000;
+                       gpu->identity.revision &= 0xffff;
+               }
        }
 
        dev_info(gpu->dev, "model: GC%x, revision: %x\n",
@@ -528,6 +538,14 @@ static void etnaviv_gpu_enable_mlcg(struct etnaviv_gpu *gpu)
        gpu_write(gpu, VIVS_PM_MODULE_CONTROLS, pmc);
 }
 
+void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch)
+{
+       gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS, address);
+       gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
+                 VIVS_FE_COMMAND_CONTROL_ENABLE |
+                 VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
+}
+
 static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
 {
        u16 prefetch;
@@ -568,33 +586,20 @@ static void etnaviv_gpu_hw_init(struct etnaviv_gpu *gpu)
                gpu_write(gpu, VIVS_MC_BUS_CONFIG, bus_config);
        }
 
-       /* set base addresses */
-       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
-       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);
-       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base);
-       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base);
-       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
-
-       /* setup the MMU page table pointers */
-       etnaviv_iommu_domain_restore(gpu, gpu->mmu->domain);
+       /* setup the MMU */
+       etnaviv_iommu_restore(gpu);
 
        /* Start command processor */
        prefetch = etnaviv_buffer_init(gpu);
 
        gpu_write(gpu, VIVS_HI_INTR_ENBL, ~0U);
-       gpu_write(gpu, VIVS_FE_COMMAND_ADDRESS,
-                 gpu->buffer->paddr - gpu->memory_base);
-       gpu_write(gpu, VIVS_FE_COMMAND_CONTROL,
-                 VIVS_FE_COMMAND_CONTROL_ENABLE |
-                 VIVS_FE_COMMAND_CONTROL_PREFETCH(prefetch));
+       etnaviv_gpu_start_fe(gpu, etnaviv_iommu_get_cmdbuf_va(gpu, gpu->buffer),
+                            prefetch);
 }
 
 int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
 {
        int ret, i;
-       struct iommu_domain *iommu;
-       enum etnaviv_iommu_version version;
-       bool mmuv2;
 
        ret = pm_runtime_get_sync(gpu->dev);
        if (ret < 0) {
@@ -642,32 +647,10 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
                goto fail;
        }
 
-       /* Setup IOMMU.. eventually we will (I think) do this once per context
-        * and have separate page tables per context.  For now, to keep things
-        * simple and to get something working, just use a single address space:
-        */
-       mmuv2 = gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION;
-       dev_dbg(gpu->dev, "mmuv2: %d\n", mmuv2);
-
-       if (!mmuv2) {
-               iommu = etnaviv_iommu_domain_alloc(gpu);
-               version = ETNAVIV_IOMMU_V1;
-       } else {
-               iommu = etnaviv_iommu_v2_domain_alloc(gpu);
-               version = ETNAVIV_IOMMU_V2;
-       }
-
-       if (!iommu) {
-               dev_err(gpu->dev, "Failed to allocate GPU IOMMU domain\n");
-               ret = -ENOMEM;
-               goto fail;
-       }
-
-       gpu->mmu = etnaviv_iommu_new(gpu, iommu, version);
-       if (!gpu->mmu) {
+       gpu->mmu = etnaviv_iommu_new(gpu);
+       if (IS_ERR(gpu->mmu)) {
                dev_err(gpu->dev, "Failed to instantiate GPU IOMMU\n");
-               iommu_domain_free(iommu);
-               ret = -ENOMEM;
+               ret = PTR_ERR(gpu->mmu);
                goto fail;
        }
 
@@ -678,7 +661,9 @@ int etnaviv_gpu_init(struct etnaviv_gpu *gpu)
                dev_err(gpu->dev, "could not create command buffer\n");
                goto destroy_iommu;
        }
-       if (gpu->buffer->paddr - gpu->memory_base > 0x80000000) {
+
+       if (gpu->mmu->version == ETNAVIV_IOMMU_V1 &&
+           gpu->buffer->paddr - gpu->memory_base > 0x80000000) {
                ret = -EINVAL;
                dev_err(gpu->dev,
                        "command buffer outside valid memory window\n");
@@ -867,45 +852,6 @@ int etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m)
 }
 #endif
 
-/*
- * Power Management:
- */
-static int enable_clk(struct etnaviv_gpu *gpu)
-{
-       if (gpu->clk_core)
-               clk_prepare_enable(gpu->clk_core);
-       if (gpu->clk_shader)
-               clk_prepare_enable(gpu->clk_shader);
-
-       return 0;
-}
-
-static int disable_clk(struct etnaviv_gpu *gpu)
-{
-       if (gpu->clk_core)
-               clk_disable_unprepare(gpu->clk_core);
-       if (gpu->clk_shader)
-               clk_disable_unprepare(gpu->clk_shader);
-
-       return 0;
-}
-
-static int enable_axi(struct etnaviv_gpu *gpu)
-{
-       if (gpu->clk_bus)
-               clk_prepare_enable(gpu->clk_bus);
-
-       return 0;
-}
-
-static int disable_axi(struct etnaviv_gpu *gpu)
-{
-       if (gpu->clk_bus)
-               clk_disable_unprepare(gpu->clk_bus);
-
-       return 0;
-}
-
 /*
  * Hangcheck detection for locked gpu:
  */
@@ -945,7 +891,7 @@ static void recover_worker(struct work_struct *work)
        gpu->completed_fence = gpu->active_fence;
 
        etnaviv_gpu_hw_init(gpu);
-       gpu->switch_context = true;
+       gpu->lastctx = NULL;
        gpu->exec_state = -1;
 
        mutex_unlock(&gpu->lock);
@@ -1178,6 +1124,9 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size,
        if (!cmdbuf)
                return NULL;
 
+       if (gpu->mmu->version == ETNAVIV_IOMMU_V2)
+               size = ALIGN(size, SZ_4K);
+
        cmdbuf->vaddr = dma_alloc_wc(gpu->dev, size, &cmdbuf->paddr,
                                     GFP_KERNEL);
        if (!cmdbuf->vaddr) {
@@ -1193,6 +1142,7 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu, u32 size,
 
 void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf)
 {
+       etnaviv_iommu_put_cmdbuf_va(cmdbuf->gpu, cmdbuf);
        dma_free_wc(cmdbuf->gpu->dev, cmdbuf->size, cmdbuf->vaddr,
                    cmdbuf->paddr);
        kfree(cmdbuf);
@@ -1425,6 +1375,21 @@ static irqreturn_t irq_handler(int irq, void *data)
                        intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR;
                }
 
+               if (intr & VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION) {
+                       int i;
+
+                       dev_err_ratelimited(gpu->dev,
+                               "MMU fault status 0x%08x\n",
+                               gpu_read(gpu, VIVS_MMUv2_STATUS));
+                       for (i = 0; i < 4; i++) {
+                               dev_err_ratelimited(gpu->dev,
+                                       "MMU %d fault addr 0x%08x\n",
+                                       i, gpu_read(gpu,
+                                       VIVS_MMUv2_EXCEPTION_ADDR(i)));
+                       }
+                       intr &= ~VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION;
+               }
+
                while ((event = ffs(intr)) != 0) {
                        struct fence *fence;
 
@@ -1466,39 +1431,72 @@ static int etnaviv_gpu_clk_enable(struct etnaviv_gpu *gpu)
 {
        int ret;
 
-       ret = enable_clk(gpu);
-       if (ret)
-               return ret;
+       if (gpu->clk_bus) {
+               ret = clk_prepare_enable(gpu->clk_bus);
+               if (ret)
+                       return ret;
+       }
 
-       ret = enable_axi(gpu);
-       if (ret) {
-               disable_clk(gpu);
-               return ret;
+       if (gpu->clk_core) {
+               ret = clk_prepare_enable(gpu->clk_core);
+               if (ret)
+                       goto disable_clk_bus;
+       }
+
+       if (gpu->clk_shader) {
+               ret = clk_prepare_enable(gpu->clk_shader);
+               if (ret)
+                       goto disable_clk_core;
        }
 
        return 0;
+
+disable_clk_core:
+       if (gpu->clk_core)
+               clk_disable_unprepare(gpu->clk_core);
+disable_clk_bus:
+       if (gpu->clk_bus)
+               clk_disable_unprepare(gpu->clk_bus);
+
+       return ret;
 }
 
 static int etnaviv_gpu_clk_disable(struct etnaviv_gpu *gpu)
 {
-       int ret;
+       if (gpu->clk_shader)
+               clk_disable_unprepare(gpu->clk_shader);
+       if (gpu->clk_core)
+               clk_disable_unprepare(gpu->clk_core);
+       if (gpu->clk_bus)
+               clk_disable_unprepare(gpu->clk_bus);
 
-       ret = disable_axi(gpu);
-       if (ret)
-               return ret;
+       return 0;
+}
 
-       ret = disable_clk(gpu);
-       if (ret)
-               return ret;
+int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
 
-       return 0;
+       do {
+               u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
+
+               if ((idle & gpu->idle_mask) == gpu->idle_mask)
+                       return 0;
+
+               if (time_is_before_jiffies(timeout)) {
+                       dev_warn(gpu->dev,
+                                "timed out waiting for idle: idle=0x%x\n",
+                                idle);
+                       return -ETIMEDOUT;
+               }
+
+               udelay(5);
+       } while (1);
 }
 
 static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
 {
        if (gpu->buffer) {
-               unsigned long timeout;
-
                /* Replace the last WAIT with END */
                etnaviv_buffer_end(gpu);
 
@@ -1507,22 +1505,7 @@ static int etnaviv_gpu_hw_suspend(struct etnaviv_gpu *gpu)
                 * happen quickly (as the WAIT is only 200 cycles).  If
                 * we fail, just warn and continue.
                 */
-               timeout = jiffies + msecs_to_jiffies(100);
-               do {
-                       u32 idle = gpu_read(gpu, VIVS_HI_IDLE_STATE);
-
-                       if ((idle & gpu->idle_mask) == gpu->idle_mask)
-                               break;
-
-                       if (time_is_before_jiffies(timeout)) {
-                               dev_warn(gpu->dev,
-                                        "timed out waiting for idle: idle=0x%x\n",
-                                        idle);
-                               break;
-                       }
-
-                       udelay(5);
-               } while (1);
+               etnaviv_gpu_wait_idle(gpu, 100);
        }
 
        return etnaviv_gpu_clk_disable(gpu);
@@ -1634,7 +1617,7 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct etnaviv_gpu *gpu;
-       int err = 0;
+       int err;
 
        gpu = devm_kzalloc(dev, sizeof(*gpu), GFP_KERNEL);
        if (!gpu)
@@ -1651,16 +1634,15 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
        /* Get Interrupt: */
        gpu->irq = platform_get_irq(pdev, 0);
        if (gpu->irq < 0) {
-               err = gpu->irq;
-               dev_err(dev, "failed to get irq: %d\n", err);
-               goto fail;
+               dev_err(dev, "failed to get irq: %d\n", gpu->irq);
+               return gpu->irq;
        }
 
        err = devm_request_irq(&pdev->dev, gpu->irq, irq_handler, 0,
                               dev_name(gpu->dev), gpu);
        if (err) {
                dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
-               goto fail;
+               return err;
        }
 
        /* Get Clocks: */
@@ -1694,13 +1676,10 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
        err = component_add(&pdev->dev, &gpu_ops);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register component: %d\n", err);
-               goto fail;
+               return err;
        }
 
        return 0;
-
-fail:
-       return err;
 }
 
 static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
index a69cdd5..73c278d 100644 (file)
@@ -160,6 +160,8 @@ struct etnaviv_cmdbuf {
        dma_addr_t paddr;
        u32 size;
        u32 user_size;
+       /* vram node used if the cmdbuf is mapped through the MMUv2 */
+       struct drm_mm_node vram_node;
        /* fence after which this buffer is to be disposed */
        struct fence *fence;
        /* target exec state */
@@ -214,6 +216,8 @@ struct etnaviv_cmdbuf *etnaviv_gpu_cmdbuf_new(struct etnaviv_gpu *gpu,
 void etnaviv_gpu_cmdbuf_free(struct etnaviv_cmdbuf *cmdbuf);
 int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
 void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);
+int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms);
+void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch);
 
 extern struct platform_driver etnaviv_gpu_driver;
 
index 16353ee..81f1583 100644 (file)
@@ -196,12 +196,19 @@ static struct etnaviv_iommu_ops etnaviv_iommu_ops = {
        .dump = etnaviv_iommuv1_dump,
 };
 
-void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
-       struct iommu_domain *domain)
+void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu)
 {
-       struct etnaviv_iommu_domain *etnaviv_domain = to_etnaviv_domain(domain);
+       struct etnaviv_iommu_domain *etnaviv_domain =
+                       to_etnaviv_domain(gpu->mmu->domain);
        u32 pgtable;
 
+       /* set base addresses */
+       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_RA, gpu->memory_base);
+       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_FE, gpu->memory_base);
+       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_TX, gpu->memory_base);
+       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PEZ, gpu->memory_base);
+       gpu_write(gpu, VIVS_MC_MEMORY_BASE_ADDR_PE, gpu->memory_base);
+
        /* set page table address in MC */
        pgtable = (u32)etnaviv_domain->pgtable.paddr;
 
@@ -212,7 +219,7 @@ void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
        gpu_write(gpu, VIVS_MC_MMU_RA_PAGE_TABLE, pgtable);
 }
 
-struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu)
+struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu)
 {
        struct etnaviv_iommu_domain *etnaviv_domain;
        int ret;
index cf45503..8b51e7c 100644 (file)
 #ifndef __ETNAVIV_IOMMU_H__
 #define __ETNAVIV_IOMMU_H__
 
-#include <linux/iommu.h>
 struct etnaviv_gpu;
 
-struct iommu_domain *etnaviv_iommu_domain_alloc(struct etnaviv_gpu *gpu);
-void etnaviv_iommu_domain_restore(struct etnaviv_gpu *gpu,
-       struct iommu_domain *domain);
-struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
+struct iommu_domain *etnaviv_iommuv1_domain_alloc(struct etnaviv_gpu *gpu);
+void etnaviv_iommuv1_restore(struct etnaviv_gpu *gpu);
+
+struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu);
+void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu);
 
 #endif /* __ETNAVIV_IOMMU_H__ */
index fbb4aed..7e9c4d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com>
+ * Copyright (C) 2016 Etnaviv Project
   *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 as published by
 #include <linux/bitops.h>
 
 #include "etnaviv_gpu.h"
+#include "etnaviv_mmu.h"
 #include "etnaviv_iommu.h"
+#include "state.xml.h"
 #include "state_hi.xml.h"
 
+#define MMUv2_PTE_PRESENT              BIT(0)
+#define MMUv2_PTE_EXCEPTION            BIT(1)
+#define MMUv2_PTE_WRITEABLE            BIT(2)
 
-struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu)
+#define MMUv2_MTLB_MASK                        0xffc00000
+#define MMUv2_MTLB_SHIFT               22
+#define MMUv2_STLB_MASK                        0x003ff000
+#define MMUv2_STLB_SHIFT               12
+
+#define MMUv2_MAX_STLB_ENTRIES         1024
+
+struct etnaviv_iommuv2_domain {
+       struct iommu_domain domain;
+       struct device *dev;
+       void *bad_page_cpu;
+       dma_addr_t bad_page_dma;
+       /* M(aster) TLB aka first level pagetable */
+       u32 *mtlb_cpu;
+       dma_addr_t mtlb_dma;
+       /* S(lave) TLB aka second level pagetable */
+       u32 *stlb_cpu[1024];
+       dma_addr_t stlb_dma[1024];
+};
+
+static struct etnaviv_iommuv2_domain *to_etnaviv_domain(struct iommu_domain *domain)
+{
+       return container_of(domain, struct etnaviv_iommuv2_domain, domain);
+}
+
+static int etnaviv_iommuv2_map(struct iommu_domain *domain, unsigned long iova,
+          phys_addr_t paddr, size_t size, int prot)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       int mtlb_entry, stlb_entry;
+       u32 entry = (u32)paddr | MMUv2_PTE_PRESENT;
+
+       if (size != SZ_4K)
+               return -EINVAL;
+
+       if (prot & IOMMU_WRITE)
+               entry |= MMUv2_PTE_WRITEABLE;
+
+       mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
+       stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;
+
+       etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] = entry;
+
+       return 0;
+}
+
+static size_t etnaviv_iommuv2_unmap(struct iommu_domain *domain,
+       unsigned long iova, size_t size)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       int mtlb_entry, stlb_entry;
+
+       if (size != SZ_4K)
+               return -EINVAL;
+
+       mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
+       stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;
+
+       etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] = MMUv2_PTE_EXCEPTION;
+
+       return SZ_4K;
+}
+
+static phys_addr_t etnaviv_iommuv2_iova_to_phys(struct iommu_domain *domain,
+       dma_addr_t iova)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       int mtlb_entry, stlb_entry;
+
+       mtlb_entry = (iova & MMUv2_MTLB_MASK) >> MMUv2_MTLB_SHIFT;
+       stlb_entry = (iova & MMUv2_STLB_MASK) >> MMUv2_STLB_SHIFT;
+
+       return etnaviv_domain->stlb_cpu[mtlb_entry][stlb_entry] & ~(SZ_4K - 1);
+}
+
+static int etnaviv_iommuv2_init(struct etnaviv_iommuv2_domain *etnaviv_domain)
+{
+       u32 *p;
+       int ret, i, j;
+
+       /* allocate scratch page */
+       etnaviv_domain->bad_page_cpu = dma_alloc_coherent(etnaviv_domain->dev,
+                                                 SZ_4K,
+                                                 &etnaviv_domain->bad_page_dma,
+                                                 GFP_KERNEL);
+       if (!etnaviv_domain->bad_page_cpu) {
+               ret = -ENOMEM;
+               goto fail_mem;
+       }
+       p = etnaviv_domain->bad_page_cpu;
+       for (i = 0; i < SZ_4K / 4; i++)
+               *p++ = 0xdead55aa;
+
+       etnaviv_domain->mtlb_cpu = dma_alloc_coherent(etnaviv_domain->dev,
+                                                 SZ_4K,
+                                                 &etnaviv_domain->mtlb_dma,
+                                                 GFP_KERNEL);
+       if (!etnaviv_domain->mtlb_cpu) {
+               ret = -ENOMEM;
+               goto fail_mem;
+       }
+
+       /* pre-populate STLB pages (may want to switch to on-demand later) */
+       for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
+               etnaviv_domain->stlb_cpu[i] =
+                               dma_alloc_coherent(etnaviv_domain->dev,
+                                                  SZ_4K,
+                                                  &etnaviv_domain->stlb_dma[i],
+                                                  GFP_KERNEL);
+               if (!etnaviv_domain->stlb_cpu[i]) {
+                       ret = -ENOMEM;
+                       goto fail_mem;
+               }
+               p = etnaviv_domain->stlb_cpu[i];
+               for (j = 0; j < SZ_4K / 4; j++)
+                       *p++ = MMUv2_PTE_EXCEPTION;
+
+               etnaviv_domain->mtlb_cpu[i] = etnaviv_domain->stlb_dma[i] |
+                                             MMUv2_PTE_PRESENT;
+       }
+
+       return 0;
+
+fail_mem:
+       if (etnaviv_domain->bad_page_cpu)
+               dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                                 etnaviv_domain->bad_page_cpu,
+                                 etnaviv_domain->bad_page_dma);
+
+       if (etnaviv_domain->mtlb_cpu)
+               dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                                 etnaviv_domain->mtlb_cpu,
+                                 etnaviv_domain->mtlb_dma);
+
+       for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
+               if (etnaviv_domain->stlb_cpu[i])
+                       dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                                         etnaviv_domain->stlb_cpu[i],
+                                         etnaviv_domain->stlb_dma[i]);
+       }
+
+       return ret;
+}
+
+static void etnaviv_iommuv2_domain_free(struct iommu_domain *domain)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       int i;
+
+       dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                         etnaviv_domain->bad_page_cpu,
+                         etnaviv_domain->bad_page_dma);
+
+       dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                         etnaviv_domain->mtlb_cpu,
+                         etnaviv_domain->mtlb_dma);
+
+       for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++) {
+               if (etnaviv_domain->stlb_cpu[i])
+                       dma_free_coherent(etnaviv_domain->dev, SZ_4K,
+                                         etnaviv_domain->stlb_cpu[i],
+                                         etnaviv_domain->stlb_dma[i]);
+       }
+
+       vfree(etnaviv_domain);
+}
+
+static size_t etnaviv_iommuv2_dump_size(struct iommu_domain *domain)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       size_t dump_size = SZ_4K;
+       int i;
+
+       for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++)
+               if (etnaviv_domain->mtlb_cpu[i] & MMUv2_PTE_PRESENT)
+                       dump_size += SZ_4K;
+
+       return dump_size;
+}
+
+static void etnaviv_iommuv2_dump(struct iommu_domain *domain, void *buf)
 {
-       /* TODO */
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(domain);
+       int i;
+
+       memcpy(buf, etnaviv_domain->mtlb_cpu, SZ_4K);
+       buf += SZ_4K;
+       for (i = 0; i < MMUv2_MAX_STLB_ENTRIES; i++, buf += SZ_4K)
+               if (etnaviv_domain->mtlb_cpu[i] & MMUv2_PTE_PRESENT)
+                       memcpy(buf, etnaviv_domain->stlb_cpu[i], SZ_4K);
+}
+
+static struct etnaviv_iommu_ops etnaviv_iommu_ops = {
+       .ops = {
+               .domain_free = etnaviv_iommuv2_domain_free,
+               .map = etnaviv_iommuv2_map,
+               .unmap = etnaviv_iommuv2_unmap,
+               .iova_to_phys = etnaviv_iommuv2_iova_to_phys,
+               .pgsize_bitmap = SZ_4K,
+       },
+       .dump_size = etnaviv_iommuv2_dump_size,
+       .dump = etnaviv_iommuv2_dump,
+};
+
+void etnaviv_iommuv2_restore(struct etnaviv_gpu *gpu)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain =
+                       to_etnaviv_domain(gpu->mmu->domain);
+       u16 prefetch;
+
+       /* If the MMU is already enabled the state is still there. */
+       if (gpu_read(gpu, VIVS_MMUv2_CONTROL) & VIVS_MMUv2_CONTROL_ENABLE)
+               return;
+
+       prefetch = etnaviv_buffer_config_mmuv2(gpu,
+                               (u32)etnaviv_domain->mtlb_dma,
+                               (u32)etnaviv_domain->bad_page_dma);
+       etnaviv_gpu_start_fe(gpu, gpu->buffer->paddr, prefetch);
+       etnaviv_gpu_wait_idle(gpu, 100);
+
+       gpu_write(gpu, VIVS_MMUv2_CONTROL, VIVS_MMUv2_CONTROL_ENABLE);
+}
+struct iommu_domain *etnaviv_iommuv2_domain_alloc(struct etnaviv_gpu *gpu)
+{
+       struct etnaviv_iommuv2_domain *etnaviv_domain;
+       int ret;
+
+       etnaviv_domain = vzalloc(sizeof(*etnaviv_domain));
+       if (!etnaviv_domain)
+               return NULL;
+
+       etnaviv_domain->dev = gpu->dev;
+
+       etnaviv_domain->domain.type = __IOMMU_DOMAIN_PAGING;
+       etnaviv_domain->domain.ops = &etnaviv_iommu_ops.ops;
+       etnaviv_domain->domain.pgsize_bitmap = SZ_4K;
+       etnaviv_domain->domain.geometry.aperture_start = 0;
+       etnaviv_domain->domain.geometry.aperture_end = ~0UL & ~(SZ_4K - 1);
+
+       ret = etnaviv_iommuv2_init(etnaviv_domain);
+       if (ret)
+               goto out_free;
+
+       return &etnaviv_domain->domain;
+
+out_free:
+       vfree(etnaviv_domain);
        return NULL;
 }
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h b/drivers/gpu/drm/etnaviv/etnaviv_iommu_v2.h
deleted file mode 100644 (file)
index 603ea41..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2014 Christian Gmeiner <christian.gmeiner@gmail.com>
-  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published by
- * the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef __ETNAVIV_IOMMU_V2_H__
-#define __ETNAVIV_IOMMU_V2_H__
-
-#include <linux/iommu.h>
-struct etnaviv_gpu;
-
-struct iommu_domain *etnaviv_iommu_v2_domain_alloc(struct etnaviv_gpu *gpu);
-
-#endif /* __ETNAVIV_IOMMU_V2_H__ */
index 29a723f..d3796ed 100644 (file)
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "common.xml.h"
 #include "etnaviv_drv.h"
 #include "etnaviv_gem.h"
 #include "etnaviv_gpu.h"
+#include "etnaviv_iommu.h"
 #include "etnaviv_mmu.h"
 
 static int etnaviv_fault_handler(struct iommu_domain *iommu, struct device *dev,
@@ -101,40 +103,21 @@ static void etnaviv_iommu_remove_mapping(struct etnaviv_iommu *mmu,
        drm_mm_remove_node(&mapping->vram_node);
 }
 
-int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
-       struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
-       struct etnaviv_vram_mapping *mapping)
+static int etnaviv_iommu_find_iova(struct etnaviv_iommu *mmu,
+                                  struct drm_mm_node *node, size_t size)
 {
        struct etnaviv_vram_mapping *free = NULL;
-       struct sg_table *sgt = etnaviv_obj->sgt;
-       struct drm_mm_node *node;
        int ret;
 
-       lockdep_assert_held(&etnaviv_obj->lock);
-
-       mutex_lock(&mmu->lock);
-
-       /* v1 MMU can optimize single entry (contiguous) scatterlists */
-       if (sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
-               u32 iova;
-
-               iova = sg_dma_address(sgt->sgl) - memory_base;
-               if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
-                       mapping->iova = iova;
-                       list_add_tail(&mapping->mmu_node, &mmu->mappings);
-                       mutex_unlock(&mmu->lock);
-                       return 0;
-               }
-       }
+       lockdep_assert_held(&mmu->lock);
 
-       node = &mapping->vram_node;
        while (1) {
                struct etnaviv_vram_mapping *m, *n;
                struct list_head list;
                bool found;
 
                ret = drm_mm_insert_node_in_range(&mmu->mm, node,
-                       etnaviv_obj->base.size, 0, mmu->last_iova, ~0UL,
+                       size, 0, mmu->last_iova, ~0UL,
                        DRM_MM_SEARCH_DEFAULT);
 
                if (ret != -ENOSPC)
@@ -151,7 +134,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
                }
 
                /* Try to retire some entries */
-               drm_mm_init_scan(&mmu->mm, etnaviv_obj->base.size, 0, 0);
+               drm_mm_init_scan(&mmu->mm, size, 0, 0);
 
                found = 0;
                INIT_LIST_HEAD(&list);
@@ -212,6 +195,38 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
                mmu->need_flush = true;
        }
 
+       return ret;
+}
+
+int etnaviv_iommu_map_gem(struct etnaviv_iommu *mmu,
+       struct etnaviv_gem_object *etnaviv_obj, u32 memory_base,
+       struct etnaviv_vram_mapping *mapping)
+{
+       struct sg_table *sgt = etnaviv_obj->sgt;
+       struct drm_mm_node *node;
+       int ret;
+
+       lockdep_assert_held(&etnaviv_obj->lock);
+
+       mutex_lock(&mmu->lock);
+
+       /* v1 MMU can optimize single entry (contiguous) scatterlists */
+       if (mmu->version == ETNAVIV_IOMMU_V1 &&
+           sgt->nents == 1 && !(etnaviv_obj->flags & ETNA_BO_FORCE_MMU)) {
+               u32 iova;
+
+               iova = sg_dma_address(sgt->sgl) - memory_base;
+               if (iova < 0x80000000 - sg_dma_len(sgt->sgl)) {
+                       mapping->iova = iova;
+                       list_add_tail(&mapping->mmu_node, &mmu->mappings);
+                       mutex_unlock(&mmu->lock);
+                       return 0;
+               }
+       }
+
+       node = &mapping->vram_node;
+
+       ret = etnaviv_iommu_find_iova(mmu, node, etnaviv_obj->base.size);
        if (ret < 0) {
                mutex_unlock(&mmu->lock);
                return ret;
@@ -256,30 +271,102 @@ void etnaviv_iommu_destroy(struct etnaviv_iommu *mmu)
        kfree(mmu);
 }
 
-struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu,
-       struct iommu_domain *domain, enum etnaviv_iommu_version version)
+struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu)
 {
+       enum etnaviv_iommu_version version;
        struct etnaviv_iommu *mmu;
 
        mmu = kzalloc(sizeof(*mmu), GFP_KERNEL);
        if (!mmu)
                return ERR_PTR(-ENOMEM);
 
-       mmu->domain = domain;
+       if (!(gpu->identity.minor_features1 & chipMinorFeatures1_MMU_VERSION)) {
+               mmu->domain = etnaviv_iommuv1_domain_alloc(gpu);
+               version = ETNAVIV_IOMMU_V1;
+       } else {
+               mmu->domain = etnaviv_iommuv2_domain_alloc(gpu);
+               version = ETNAVIV_IOMMU_V2;
+       }
+
+       if (!mmu->domain) {
+               dev_err(gpu->dev, "Failed to allocate GPU IOMMU domain\n");
+               kfree(mmu);
+               return ERR_PTR(-ENOMEM);
+       }
+
        mmu->gpu = gpu;
        mmu->version = version;
        mutex_init(&mmu->lock);
        INIT_LIST_HEAD(&mmu->mappings);
 
-       drm_mm_init(&mmu->mm, domain->geometry.aperture_start,
-                   domain->geometry.aperture_end -
-                     domain->geometry.aperture_start + 1);
+       drm_mm_init(&mmu->mm, mmu->domain->geometry.aperture_start,
+                   mmu->domain->geometry.aperture_end -
+                   mmu->domain->geometry.aperture_start + 1);
 
-       iommu_set_fault_handler(domain, etnaviv_fault_handler, gpu->dev);
+       iommu_set_fault_handler(mmu->domain, etnaviv_fault_handler, gpu->dev);
 
        return mmu;
 }
 
+void etnaviv_iommu_restore(struct etnaviv_gpu *gpu)
+{
+       if (gpu->mmu->version == ETNAVIV_IOMMU_V1)
+               etnaviv_iommuv1_restore(gpu);
+       else
+               etnaviv_iommuv2_restore(gpu);
+}
+
+u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu,
+                               struct etnaviv_cmdbuf *buf)
+{
+       struct etnaviv_iommu *mmu = gpu->mmu;
+
+       if (mmu->version == ETNAVIV_IOMMU_V1) {
+               return buf->paddr - gpu->memory_base;
+       } else {
+               int ret;
+
+               if (buf->vram_node.allocated)
+                       return (u32)buf->vram_node.start;
+
+               mutex_lock(&mmu->lock);
+               ret = etnaviv_iommu_find_iova(mmu, &buf->vram_node, buf->size);
+               if (ret < 0) {
+                       mutex_unlock(&mmu->lock);
+                       return 0;
+               }
+               ret = iommu_map(mmu->domain, buf->vram_node.start, buf->paddr,
+                               buf->size, IOMMU_READ);
+               if (ret < 0) {
+                       drm_mm_remove_node(&buf->vram_node);
+                       mutex_unlock(&mmu->lock);
+                       return 0;
+               }
+               /*
+                * At least on GC3000 the FE MMU doesn't properly flush old TLB
+                * entries. Make sure to space the command buffers out in a way
+                * that the FE MMU prefetch won't load invalid entries.
+                */
+               mmu->last_iova = buf->vram_node.start + buf->size + SZ_64K;
+               gpu->mmu->need_flush = true;
+               mutex_unlock(&mmu->lock);
+
+               return (u32)buf->vram_node.start;
+       }
+}
+
+void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu,
+                                struct etnaviv_cmdbuf *buf)
+{
+       struct etnaviv_iommu *mmu = gpu->mmu;
+
+       if (mmu->version == ETNAVIV_IOMMU_V2 && buf->vram_node.allocated) {
+               mutex_lock(&mmu->lock);
+               iommu_unmap(mmu->domain, buf->vram_node.start, buf->size);
+               drm_mm_remove_node(&buf->vram_node);
+               mutex_unlock(&mmu->lock);
+       }
+}
 size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu)
 {
        struct etnaviv_iommu_ops *ops;
index fff215a..e787e49 100644 (file)
@@ -62,10 +62,15 @@ void etnaviv_iommu_unmap_gem(struct etnaviv_iommu *mmu,
        struct etnaviv_vram_mapping *mapping);
 void etnaviv_iommu_destroy(struct etnaviv_iommu *iommu);
 
+u32 etnaviv_iommu_get_cmdbuf_va(struct etnaviv_gpu *gpu,
+                               struct etnaviv_cmdbuf *buf);
+void etnaviv_iommu_put_cmdbuf_va(struct etnaviv_gpu *gpu,
+                                struct etnaviv_cmdbuf *buf);
+
 size_t etnaviv_iommu_dump_size(struct etnaviv_iommu *iommu);
 void etnaviv_iommu_dump(struct etnaviv_iommu *iommu, void *buf);
 
-struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu,
-       struct iommu_domain *domain, enum etnaviv_iommu_version version);
+struct etnaviv_iommu *etnaviv_iommu_new(struct etnaviv_gpu *gpu);
+void etnaviv_iommu_restore(struct etnaviv_gpu *gpu);
 
 #endif /* __ETNAVIV_MMU_H__ */
index 807a3d9..43c73e2 100644 (file)
@@ -8,10 +8,10 @@ http://0x04.net/cgit/index.cgi/rules-ng-ng
 git clone git://0x04.net/rules-ng-ng
 
 The rules-ng-ng source files this header was generated from are:
-- state_hi.xml (  24309 bytes, from 2015-12-12 09:02:53)
-- common.xml   (  18437 bytes, from 2015-12-12 09:02:53)
+- state_hi.xml (  25620 bytes, from 2016-08-19 22:07:37)
+- common.xml   (  20583 bytes, from 2016-06-07 05:22:38)
 
-Copyright (C) 2015
+Copyright (C) 2016
 */
 
 
@@ -78,9 +78,10 @@ Copyright (C) 2015
 #define VIVS_HI_AXI_STATUS_DET_RD_ERR                          0x00000200
 
 #define VIVS_HI_INTR_ACKNOWLEDGE                               0x00000010
-#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK                        0x7fffffff
+#define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK                        0x3fffffff
 #define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT               0
 #define VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC(x)                   (((x) << VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__SHIFT) & VIVS_HI_INTR_ACKNOWLEDGE_INTR_VEC__MASK)
+#define VIVS_HI_INTR_ACKNOWLEDGE_MMU_EXCEPTION                 0x40000000
 #define VIVS_HI_INTR_ACKNOWLEDGE_AXI_BUS_ERROR                 0x80000000
 
 #define VIVS_HI_INTR_ENBL                                      0x00000014
index ac21b40..6ca1f31 100644 (file)
@@ -551,7 +551,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
 {
        struct decon_context *ctx = dev_id;
        u32 val;
-       int win;
 
        if (!test_bit(BIT_CLKS_ENABLED, &ctx->flags))
                goto out;
@@ -560,16 +559,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
        val &= VIDINTCON1_INTFRMDONEPEND | VIDINTCON1_INTFRMPEND;
 
        if (val) {
-               for (win = ctx->first_win; win < WINDOWS_NR ; win++) {
-                       struct exynos_drm_plane *plane = &ctx->planes[win];
-
-                       if (!plane->pending_fb)
-                               continue;
-
-                       exynos_drm_crtc_finish_update(ctx->crtc, plane);
-               }
-
-               /* clear */
                writel(val, ctx->addr + DECON_VIDINTCON1);
                drm_crtc_handle_vblank(&ctx->crtc->base);
        }
index 7f9901b..f4d5a21 100644 (file)
@@ -603,7 +603,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
 {
        struct decon_context *ctx = (struct decon_context *)dev_id;
        u32 val, clear_bit;
-       int win;
 
        val = readl(ctx->regs + VIDINTCON1);
 
@@ -617,14 +616,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id)
 
        if (!ctx->i80_if) {
                drm_crtc_handle_vblank(&ctx->crtc->base);
-               for (win = 0 ; win < WINDOWS_NR ; win++) {
-                       struct exynos_drm_plane *plane = &ctx->planes[win];
-
-                       if (!plane->pending_fb)
-                               continue;
-
-                       exynos_drm_crtc_finish_update(ctx->crtc, plane);
-               }
 
                /* set wait vsync event to zero and wake up queue. */
                if (atomic_read(&ctx->wait_vsync_event)) {
index 4f08505..528229f 100644 (file)
@@ -43,7 +43,7 @@ struct exynos_dp_device {
        struct analogix_dp_plat_data plat_data;
 };
 
-int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
+static int exynos_dp_crtc_clock_enable(struct analogix_dp_plat_data *plat_data,
                                bool enable)
 {
        struct exynos_dp_device *dp = to_dp(plat_data);
index 785ffa6..2530bf5 100644 (file)
@@ -69,8 +69,6 @@ static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       exynos_crtc->event = crtc->state->event;
-
        if (exynos_crtc->ops->atomic_begin)
                exynos_crtc->ops->atomic_begin(exynos_crtc);
 }
@@ -79,9 +77,24 @@ static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
                                     struct drm_crtc_state *old_crtc_state)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+       struct drm_pending_vblank_event *event;
+       unsigned long flags;
 
        if (exynos_crtc->ops->atomic_flush)
                exynos_crtc->ops->atomic_flush(exynos_crtc);
+
+       event = crtc->state->event;
+       if (event) {
+               crtc->state->event = NULL;
+
+               spin_lock_irqsave(&crtc->dev->event_lock, flags);
+               if (drm_crtc_vblank_get(crtc) == 0)
+                       drm_crtc_arm_vblank_event(crtc, event);
+               else
+                       drm_crtc_send_vblank_event(crtc, event);
+               spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
+       }
+
 }
 
 static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
@@ -134,8 +147,6 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
        exynos_crtc->ops = ops;
        exynos_crtc->ctx = ctx;
 
-       init_waitqueue_head(&exynos_crtc->wait_update);
-
        crtc = &exynos_crtc->base;
 
        private->crtc[pipe] = crtc;
@@ -175,32 +186,6 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
                exynos_crtc->ops->disable_vblank(exynos_crtc);
 }
 
-void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
-{
-       wait_event_timeout(exynos_crtc->wait_update,
-                          (atomic_read(&exynos_crtc->pending_update) == 0),
-                          msecs_to_jiffies(50));
-}
-
-void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
-                               struct exynos_drm_plane *exynos_plane)
-{
-       struct drm_crtc *crtc = &exynos_crtc->base;
-       unsigned long flags;
-
-       exynos_plane->pending_fb = NULL;
-
-       if (atomic_dec_and_test(&exynos_crtc->pending_update))
-               wake_up(&exynos_crtc->wait_update);
-
-       spin_lock_irqsave(&crtc->dev->event_lock, flags);
-       if (exynos_crtc->event)
-               drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
-
-       exynos_crtc->event = NULL;
-       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-}
-
 int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
                                       enum exynos_drm_output_type out_type)
 {
@@ -228,20 +213,19 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
 void exynos_drm_crtc_cancel_page_flip(struct drm_crtc *crtc,
                                        struct drm_file *file)
 {
-       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
        struct drm_pending_vblank_event *e;
        unsigned long flags;
 
        spin_lock_irqsave(&crtc->dev->event_lock, flags);
 
-       e = exynos_crtc->event;
-       if (e && e->base.file_priv == file) {
-               exynos_crtc->event = NULL;
-               atomic_dec(&exynos_crtc->pending_update);
-       }
+       e = crtc->state->event;
+       if (e && e->base.file_priv == file)
+               crtc->state->event = NULL;
+       else
+               e = NULL;
 
        spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 
-       if (e && e->base.file_priv == file)
+       if (e)
                drm_event_cancel_free(crtc->dev, &e->base);
 }
index 877d2ef..def78c8 100644 (file)
@@ -45,37 +45,11 @@ struct exynos_atomic_commit {
        u32                     crtcs;
 };
 
-static void exynos_atomic_wait_for_commit(struct drm_atomic_state *state)
-{
-       struct drm_crtc_state *crtc_state;
-       struct drm_crtc *crtc;
-       int i, ret;
-
-       for_each_crtc_in_state(state, crtc, crtc_state, i) {
-               struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-
-               if (!crtc->state->enable)
-                       continue;
-
-               ret = drm_crtc_vblank_get(crtc);
-               if (ret)
-                       continue;
-
-               exynos_drm_crtc_wait_pending_update(exynos_crtc);
-               drm_crtc_vblank_put(crtc);
-       }
-}
-
 static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
 {
        struct drm_device *dev = commit->dev;
        struct exynos_drm_private *priv = dev->dev_private;
        struct drm_atomic_state *state = commit->state;
-       struct drm_plane *plane;
-       struct drm_crtc *crtc;
-       struct drm_plane_state *plane_state;
-       struct drm_crtc_state *crtc_state;
-       int i;
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
@@ -89,25 +63,9 @@ static void exynos_atomic_commit_complete(struct exynos_atomic_commit *commit)
         * have the relevant clocks enabled to perform the update.
         */
 
-       for_each_crtc_in_state(state, crtc, crtc_state, i) {
-               struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-
-               atomic_set(&exynos_crtc->pending_update, 0);
-       }
-
-       for_each_plane_in_state(state, plane, plane_state, i) {
-               struct exynos_drm_crtc *exynos_crtc =
-                                               to_exynos_crtc(plane->crtc);
-
-               if (!plane->crtc)
-                       continue;
-
-               atomic_inc(&exynos_crtc->pending_update);
-       }
-
-       drm_atomic_helper_commit_planes(dev, state, false);
+       drm_atomic_helper_commit_planes(dev, state, 0);
 
-       exynos_atomic_wait_for_commit(state);
+       drm_atomic_helper_wait_for_vblanks(dev, state);
 
        drm_atomic_helper_cleanup_planes(dev, state);
 
index 7f1a49d..d215149 100644 (file)
@@ -86,7 +86,6 @@ struct exynos_drm_plane {
        struct drm_plane base;
        const struct exynos_drm_plane_config *config;
        unsigned int index;
-       struct drm_framebuffer *pending_fb;
 };
 
 #define EXYNOS_DRM_PLANE_CAP_DOUBLE    (1 << 0)
@@ -172,9 +171,6 @@ struct exynos_drm_crtc {
        struct drm_crtc                 base;
        enum exynos_drm_output_type     type;
        unsigned int                    pipe;
-       struct drm_pending_vblank_event *event;
-       wait_queue_head_t               wait_update;
-       atomic_t                        pending_update;
        const struct exynos_drm_crtc_ops        *ops;
        void                            *ctx;
        struct exynos_drm_clk           *pipe_clk;
index d472164..e2e4051 100644 (file)
@@ -198,6 +198,7 @@ struct fimd_context {
        atomic_t                        wait_vsync_event;
        atomic_t                        win_updated;
        atomic_t                        triggering;
+       u32                             clkdiv;
 
        const struct fimd_driver_data *driver_data;
        struct drm_encoder *encoder;
@@ -389,15 +390,18 @@ static void fimd_clear_channels(struct exynos_drm_crtc *crtc)
        pm_runtime_put(ctx->dev);
 }
 
-static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
-               const struct drm_display_mode *mode)
+
+static int fimd_atomic_check(struct exynos_drm_crtc *crtc,
+               struct drm_crtc_state *state)
 {
-       unsigned long ideal_clk;
+       struct drm_display_mode *mode = &state->adjusted_mode;
+       struct fimd_context *ctx = crtc->ctx;
+       unsigned long ideal_clk, lcd_rate;
        u32 clkdiv;
 
        if (mode->clock == 0) {
-               DRM_ERROR("Mode has zero clock value.\n");
-               return 0xff;
+               DRM_INFO("Mode has zero clock value.\n");
+               return -EINVAL;
        }
 
        ideal_clk = mode->clock * 1000;
@@ -410,10 +414,23 @@ static u32 fimd_calc_clkdiv(struct fimd_context *ctx,
                ideal_clk *= 2;
        }
 
+       lcd_rate = clk_get_rate(ctx->lcd_clk);
+       if (2 * lcd_rate < ideal_clk) {
+               DRM_INFO("sclk_fimd clock too low(%lu) for requested pixel clock(%lu)\n",
+                        lcd_rate, ideal_clk);
+               return -EINVAL;
+       }
+
        /* Find the clock divider value that gets us closest to ideal_clk */
-       clkdiv = DIV_ROUND_CLOSEST(clk_get_rate(ctx->lcd_clk), ideal_clk);
+       clkdiv = DIV_ROUND_CLOSEST(lcd_rate, ideal_clk);
+       if (clkdiv >= 0x200) {
+               DRM_INFO("requested pixel clock(%lu) too low\n", ideal_clk);
+               return -EINVAL;
+       }
+
+       ctx->clkdiv = (clkdiv < 0x100) ? clkdiv : 0xff;
 
-       return (clkdiv < 0x100) ? clkdiv : 0xff;
+       return 0;
 }
 
 static void fimd_setup_trigger(struct fimd_context *ctx)
@@ -442,7 +459,7 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
        struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
        const struct fimd_driver_data *driver_data = ctx->driver_data;
        void *timing_base = ctx->regs + driver_data->timing_base;
-       u32 val, clkdiv;
+       u32 val;
 
        if (ctx->suspended)
                return;
@@ -543,9 +560,8 @@ static void fimd_commit(struct exynos_drm_crtc *crtc)
        if (ctx->driver_data->has_clksel)
                val |= VIDCON0_CLKSEL_LCD;
 
-       clkdiv = fimd_calc_clkdiv(ctx, mode);
-       if (clkdiv > 1)
-               val |= VIDCON0_CLKVAL_F(clkdiv - 1) | VIDCON0_CLKDIR;
+       if (ctx->clkdiv > 1)
+               val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
 
        writel(val, ctx->regs + VIDCON0);
 }
@@ -939,14 +955,14 @@ static const struct exynos_drm_crtc_ops fimd_crtc_ops = {
        .update_plane = fimd_update_plane,
        .disable_plane = fimd_disable_plane,
        .atomic_flush = fimd_atomic_flush,
+       .atomic_check = fimd_atomic_check,
        .te_handler = fimd_te_handler,
 };
 
 static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
 {
        struct fimd_context *ctx = (struct fimd_context *)dev_id;
-       u32 val, clear_bit, start, start_s;
-       int win;
+       u32 val, clear_bit;
 
        val = readl(ctx->regs + VIDINTCON1);
 
@@ -961,18 +977,6 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
        if (!ctx->i80_if)
                drm_crtc_handle_vblank(&ctx->crtc->base);
 
-       for (win = 0 ; win < WINDOWS_NR ; win++) {
-               struct exynos_drm_plane *plane = &ctx->planes[win];
-
-               if (!plane->pending_fb)
-                       continue;
-
-               start = readl(ctx->regs + VIDWx_BUF_START(win, 0));
-               start_s = readl(ctx->regs + VIDWx_BUF_START_S(win, 0));
-               if (start == start_s)
-                       exynos_drm_crtc_finish_update(ctx->crtc, plane);
-       }
-
        if (ctx->i80_if) {
                /* Exits triggering mode */
                atomic_set(&ctx->triggering, 0);
index 6eca8bb..aa92dec 100644 (file)
@@ -138,6 +138,18 @@ enum g2d_reg_type {
        MAX_REG_TYPE_NR
 };
 
+enum g2d_flag_bits {
+       /*
+        * If set, suspends the runqueue worker after the currently
+        * processed node is finished.
+        */
+       G2D_BIT_SUSPEND_RUNQUEUE,
+       /*
+        * If set, indicates that the engine is currently busy.
+        */
+       G2D_BIT_ENGINE_BUSY,
+};
+
 /* cmdlist data structure */
 struct g2d_cmdlist {
        u32             head;
@@ -226,7 +238,7 @@ struct g2d_data {
        struct workqueue_struct         *g2d_workq;
        struct work_struct              runqueue_work;
        struct exynos_drm_subdrv        subdrv;
-       bool                            suspended;
+       unsigned long                   flags;
 
        /* cmdlist */
        struct g2d_cmdlist_node         *cmdlist_node;
@@ -246,6 +258,12 @@ struct g2d_data {
        unsigned long                   max_pool;
 };
 
+static inline void g2d_hw_reset(struct g2d_data *g2d)
+{
+       writel(G2D_R | G2D_SFRCLEAR, g2d->regs + G2D_SOFT_RESET);
+       clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
+}
+
 static int g2d_init_cmdlist(struct g2d_data *g2d)
 {
        struct device *dev = g2d->dev;
@@ -803,12 +821,8 @@ static void g2d_dma_start(struct g2d_data *g2d,
        struct g2d_cmdlist_node *node =
                                list_first_entry(&runqueue_node->run_cmdlist,
                                                struct g2d_cmdlist_node, list);
-       int ret;
-
-       ret = pm_runtime_get_sync(g2d->dev);
-       if (ret < 0)
-               return;
 
+       set_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
        writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR);
        writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND);
 }
@@ -831,9 +845,6 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d,
 {
        struct g2d_cmdlist_node *node;
 
-       if (!runqueue_node)
-               return;
-
        mutex_lock(&g2d->cmdlist_mutex);
        /*
         * commands in run_cmdlist have been completed so unmap all gem
@@ -847,29 +858,65 @@ static void g2d_free_runqueue_node(struct g2d_data *g2d,
        kmem_cache_free(g2d->runqueue_slab, runqueue_node);
 }
 
-static void g2d_exec_runqueue(struct g2d_data *g2d)
+/**
+ * g2d_remove_runqueue_nodes - remove items from the list of runqueue nodes
+ * @g2d: G2D state object
+ * @file: if not zero, only remove items with this DRM file
+ *
+ * Has to be called under runqueue lock.
+ */
+static void g2d_remove_runqueue_nodes(struct g2d_data *g2d, struct drm_file* file)
 {
-       g2d->runqueue_node = g2d_get_runqueue_node(g2d);
-       if (g2d->runqueue_node)
-               g2d_dma_start(g2d, g2d->runqueue_node);
+       struct g2d_runqueue_node *node, *n;
+
+       if (list_empty(&g2d->runqueue))
+               return;
+
+       list_for_each_entry_safe(node, n, &g2d->runqueue, list) {
+               if (file && node->filp != file)
+                       continue;
+
+               list_del_init(&node->list);
+               g2d_free_runqueue_node(g2d, node);
+       }
 }
 
 static void g2d_runqueue_worker(struct work_struct *work)
 {
        struct g2d_data *g2d = container_of(work, struct g2d_data,
                                            runqueue_work);
+       struct g2d_runqueue_node *runqueue_node;
+
+       /*
+        * The engine is busy and the completion of the current node is going
+        * to poke the runqueue worker, so nothing to do here.
+        */
+       if (test_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags))
+               return;
 
        mutex_lock(&g2d->runqueue_mutex);
-       pm_runtime_put_sync(g2d->dev);
 
-       complete(&g2d->runqueue_node->complete);
-       if (g2d->runqueue_node->async)
-               g2d_free_runqueue_node(g2d, g2d->runqueue_node);
+       runqueue_node = g2d->runqueue_node;
+       g2d->runqueue_node = NULL;
+
+       if (runqueue_node) {
+               pm_runtime_mark_last_busy(g2d->dev);
+               pm_runtime_put_autosuspend(g2d->dev);
+
+               complete(&runqueue_node->complete);
+               if (runqueue_node->async)
+                       g2d_free_runqueue_node(g2d, runqueue_node);
+       }
+
+       if (!test_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags)) {
+               g2d->runqueue_node = g2d_get_runqueue_node(g2d);
+
+               if (g2d->runqueue_node) {
+                       pm_runtime_get_sync(g2d->dev);
+                       g2d_dma_start(g2d, g2d->runqueue_node);
+               }
+       }
 
-       if (g2d->suspended)
-               g2d->runqueue_node = NULL;
-       else
-               g2d_exec_runqueue(g2d);
        mutex_unlock(&g2d->runqueue_mutex);
 }
 
@@ -918,12 +965,72 @@ static irqreturn_t g2d_irq_handler(int irq, void *dev_id)
                }
        }
 
-       if (pending & G2D_INTP_ACMD_FIN)
+       if (pending & G2D_INTP_ACMD_FIN) {
+               clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
                queue_work(g2d->g2d_workq, &g2d->runqueue_work);
+       }
 
        return IRQ_HANDLED;
 }
 
+/**
+ * g2d_wait_finish - wait for the G2D engine to finish the current runqueue node
+ * @g2d: G2D state object
+ * @file: if not zero, only wait if the current runqueue node belongs
+ *        to the DRM file
+ *
+ * Should the engine not become idle after a 100ms timeout, a hardware
+ * reset is issued.
+ */
+static void g2d_wait_finish(struct g2d_data *g2d, struct drm_file *file)
+{
+       struct device *dev = g2d->dev;
+
+       struct g2d_runqueue_node *runqueue_node = NULL;
+       unsigned int tries = 10;
+
+       mutex_lock(&g2d->runqueue_mutex);
+
+       /* If no node is currently processed, we have nothing to do. */
+       if (!g2d->runqueue_node)
+               goto out;
+
+       runqueue_node = g2d->runqueue_node;
+
+       /* Check if the currently processed item belongs to us. */
+       if (file && runqueue_node->filp != file)
+               goto out;
+
+       mutex_unlock(&g2d->runqueue_mutex);
+
+       /* Wait for the G2D engine to finish. */
+       while (tries-- && (g2d->runqueue_node == runqueue_node))
+               mdelay(10);
+
+       mutex_lock(&g2d->runqueue_mutex);
+
+       if (g2d->runqueue_node != runqueue_node)
+               goto out;
+
+       dev_err(dev, "wait timed out, resetting engine...\n");
+       g2d_hw_reset(g2d);
+
+       /*
+        * After the hardware reset of the engine we are going to loose
+        * the IRQ which triggers the PM runtime put().
+        * So do this manually here.
+        */
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+
+       complete(&runqueue_node->complete);
+       if (runqueue_node->async)
+               g2d_free_runqueue_node(g2d, runqueue_node);
+
+out:
+       mutex_unlock(&g2d->runqueue_mutex);
+}
+
 static int g2d_check_reg_offset(struct device *dev,
                                struct g2d_cmdlist_node *node,
                                int nr, bool for_addr)
@@ -1259,10 +1366,11 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
        runqueue_node->pid = current->pid;
        runqueue_node->filp = file;
        list_add_tail(&runqueue_node->list, &g2d->runqueue);
-       if (!g2d->runqueue_node)
-               g2d_exec_runqueue(g2d);
        mutex_unlock(&g2d->runqueue_mutex);
 
+       /* Let the runqueue know that there is work to do. */
+       queue_work(g2d->g2d_workq, &g2d->runqueue_work);
+
        if (runqueue_node->async)
                goto out;
 
@@ -1339,15 +1447,26 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev,
        if (!g2d)
                return;
 
+       /* Remove the runqueue nodes that belong to us. */
+       mutex_lock(&g2d->runqueue_mutex);
+       g2d_remove_runqueue_nodes(g2d, file);
+       mutex_unlock(&g2d->runqueue_mutex);
+
+       /*
+        * Wait for the runqueue worker to finish its current node.
+        * After this the engine should no longer be accessing any
+        * memory belonging to us.
+        */
+       g2d_wait_finish(g2d, file);
+
+       /*
+        * Even after the engine is idle, there might still be stale cmdlists
+        * (i.e. cmdlisst which we submitted but never executed) around, with
+        * their corresponding GEM/userptr buffers.
+        * Properly unmap these buffers here.
+        */
        mutex_lock(&g2d->cmdlist_mutex);
        list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) {
-               /*
-                * unmap all gem objects not completed.
-                *
-                * P.S. if current process was terminated forcely then
-                * there may be some commands in inuse_cmdlist so unmap
-                * them.
-                */
                g2d_unmap_cmdlist_gem(g2d, node, file);
                list_move_tail(&node->list, &g2d->free_cmdlist);
        }
@@ -1399,7 +1518,11 @@ static int g2d_probe(struct platform_device *pdev)
                goto err_destroy_workqueue;
        }
 
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, 2000);
        pm_runtime_enable(dev);
+       clear_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
+       clear_bit(G2D_BIT_ENGINE_BUSY, &g2d->flags);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
@@ -1440,7 +1563,7 @@ static int g2d_probe(struct platform_device *pdev)
                goto err_put_clk;
        }
 
-       dev_info(dev, "The exynos g2d(ver %d.%d) successfully probed\n",
+       dev_info(dev, "The Exynos G2D (ver %d.%d) successfully probed.\n",
                        G2D_HW_MAJOR_VER, G2D_HW_MINOR_VER);
 
        return 0;
@@ -1458,14 +1581,17 @@ static int g2d_remove(struct platform_device *pdev)
 {
        struct g2d_data *g2d = platform_get_drvdata(pdev);
 
+       /* Suspend operation and wait for engine idle. */
+       set_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
+       g2d_wait_finish(g2d, NULL);
+
        cancel_work_sync(&g2d->runqueue_work);
        exynos_drm_subdrv_unregister(&g2d->subdrv);
 
-       while (g2d->runqueue_node) {
-               g2d_free_runqueue_node(g2d, g2d->runqueue_node);
-               g2d->runqueue_node = g2d_get_runqueue_node(g2d);
-       }
+       /* There should be no locking needed here. */
+       g2d_remove_runqueue_nodes(g2d, NULL);
 
+       pm_runtime_dont_use_autosuspend(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
        g2d_fini_cmdlist(g2d);
@@ -1475,20 +1601,37 @@ static int g2d_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int g2d_runtime_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int g2d_suspend(struct device *dev)
 {
        struct g2d_data *g2d = dev_get_drvdata(dev);
 
-       mutex_lock(&g2d->runqueue_mutex);
-       g2d->suspended = true;
-       mutex_unlock(&g2d->runqueue_mutex);
+       /*
+        * Suspend the runqueue worker operation and wait until the G2D
+        * engine is idle.
+        */
+       set_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
+       g2d_wait_finish(g2d, NULL);
+       flush_work(&g2d->runqueue_work);
 
-       while (g2d->runqueue_node)
-               /* FIXME: good range? */
-               usleep_range(500, 1000);
+       return 0;
+}
 
-       flush_work(&g2d->runqueue_work);
+static int g2d_resume(struct device *dev)
+{
+       struct g2d_data *g2d = dev_get_drvdata(dev);
+
+       clear_bit(G2D_BIT_SUSPEND_RUNQUEUE, &g2d->flags);
+       queue_work(g2d->g2d_workq, &g2d->runqueue_work);
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int g2d_runtime_suspend(struct device *dev)
+{
+       struct g2d_data *g2d = dev_get_drvdata(dev);
 
        clk_disable_unprepare(g2d->gate_clk);
 
@@ -1504,16 +1647,12 @@ static int g2d_runtime_resume(struct device *dev)
        if (ret < 0)
                dev_warn(dev, "failed to enable clock.\n");
 
-       g2d->suspended = false;
-       g2d_exec_runqueue(g2d);
-
        return ret;
 }
 #endif
 
 static const struct dev_pm_ops g2d_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-                               pm_runtime_force_resume)
+       SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume)
        SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL)
 };
 
index 7f32419..c2f17f3 100644 (file)
@@ -238,7 +238,6 @@ static void exynos_plane_atomic_update(struct drm_plane *plane,
                return;
 
        plane->crtc = state->crtc;
-       exynos_plane->pending_fb = state->fb;
 
        if (exynos_crtc->ops->update_plane)
                exynos_crtc->ops->update_plane(exynos_crtc, exynos_plane);
index e8f6c92..57fe514 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/component.h>
+#include <linux/timer.h>
 
 #include <drm/exynos_drm.h>
 
@@ -28,6 +29,9 @@
 #include "exynos_drm_plane.h"
 #include "exynos_drm_vidi.h"
 
+/* VIDI uses fixed refresh rate of 50Hz */
+#define VIDI_REFRESH_TIME (1000 / 50)
+
 /* vidi has totally three virtual windows. */
 #define WINDOWS_NR             3
 
@@ -43,12 +47,9 @@ struct vidi_context {
        struct exynos_drm_plane         planes[WINDOWS_NR];
        struct edid                     *raw_edid;
        unsigned int                    clkdiv;
-       unsigned long                   irq_flags;
        unsigned int                    connected;
-       bool                            vblank_on;
        bool                            suspended;
-       bool                            direct_vblank;
-       struct work_struct              work;
+       struct timer_list               timer;
        struct mutex                    lock;
        int                             pipe;
 };
@@ -102,30 +103,14 @@ static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
        if (ctx->suspended)
                return -EPERM;
 
-       if (!test_and_set_bit(0, &ctx->irq_flags))
-               ctx->vblank_on = true;
-
-       ctx->direct_vblank = true;
-
-       /*
-        * in case of page flip request, vidi_finish_pageflip function
-        * will not be called because direct_vblank is true and then
-        * that function will be called by crtc_ops->update_plane callback
-        */
-       schedule_work(&ctx->work);
+       mod_timer(&ctx->timer,
+               jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
 
        return 0;
 }
 
 static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
 {
-       struct vidi_context *ctx = crtc->ctx;
-
-       if (ctx->suspended)
-               return;
-
-       if (test_and_clear_bit(0, &ctx->irq_flags))
-               ctx->vblank_on = false;
 }
 
 static void vidi_update_plane(struct exynos_drm_crtc *crtc,
@@ -140,9 +125,6 @@ static void vidi_update_plane(struct exynos_drm_crtc *crtc,
 
        addr = exynos_drm_fb_dma_addr(state->fb, 0);
        DRM_DEBUG_KMS("dma_addr = %pad\n", &addr);
-
-       if (ctx->vblank_on)
-               schedule_work(&ctx->work);
 }
 
 static void vidi_enable(struct exynos_drm_crtc *crtc)
@@ -153,17 +135,17 @@ static void vidi_enable(struct exynos_drm_crtc *crtc)
 
        ctx->suspended = false;
 
-       /* if vblank was enabled status, enable it again. */
-       if (test_and_clear_bit(0, &ctx->irq_flags))
-               vidi_enable_vblank(ctx->crtc);
-
        mutex_unlock(&ctx->lock);
+
+       drm_crtc_vblank_on(&crtc->base);
 }
 
 static void vidi_disable(struct exynos_drm_crtc *crtc)
 {
        struct vidi_context *ctx = crtc->ctx;
 
+       drm_crtc_vblank_off(&crtc->base);
+
        mutex_lock(&ctx->lock);
 
        ctx->suspended = true;
@@ -190,37 +172,16 @@ static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
        .update_plane = vidi_update_plane,
 };
 
-static void vidi_fake_vblank_handler(struct work_struct *work)
+static void vidi_fake_vblank_timer(unsigned long arg)
 {
-       struct vidi_context *ctx = container_of(work, struct vidi_context,
-                                       work);
-       int win;
+       struct vidi_context *ctx = (void *)arg;
 
        if (ctx->pipe < 0)
                return;
 
-       /* refresh rate is about 50Hz. */
-       usleep_range(16000, 20000);
-
-       mutex_lock(&ctx->lock);
-
-       if (ctx->direct_vblank) {
-               drm_crtc_handle_vblank(&ctx->crtc->base);
-               ctx->direct_vblank = false;
-               mutex_unlock(&ctx->lock);
-               return;
-       }
-
-       mutex_unlock(&ctx->lock);
-
-       for (win = 0 ; win < WINDOWS_NR ; win++) {
-               struct exynos_drm_plane *plane = &ctx->planes[win];
-
-               if (!plane->pending_fb)
-                       continue;
-
-               exynos_drm_crtc_finish_update(ctx->crtc, plane);
-       }
+       if (drm_crtc_handle_vblank(&ctx->crtc->base))
+               mod_timer(&ctx->timer,
+                       jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
 }
 
 static ssize_t vidi_show_connection(struct device *dev,
@@ -489,6 +450,9 @@ static int vidi_bind(struct device *dev, struct device *master, void *data)
 
 static void vidi_unbind(struct device *dev, struct device *master, void *data)
 {
+       struct vidi_context *ctx = dev_get_drvdata(dev);
+
+       del_timer_sync(&ctx->timer);
 }
 
 static const struct component_ops vidi_component_ops = {
@@ -507,7 +471,7 @@ static int vidi_probe(struct platform_device *pdev)
 
        ctx->pdev = pdev;
 
-       INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
+       setup_timer(&ctx->timer, vidi_fake_vblank_timer, (unsigned long)ctx);
 
        mutex_init(&ctx->lock);
 
index 2275efe..e8fb6ef 100644 (file)
@@ -1669,10 +1669,9 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
        if (ret)
                return ret;
 
-       for (i = 0; i < ARRAY_SIZE(supply); ++i) {
+       for (i = 0; i < ARRAY_SIZE(supply); ++i)
                hdata->regul_bulk[i].supply = supply[i];
-               hdata->regul_bulk[i].consumer = NULL;
-       }
+
        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), hdata->regul_bulk);
        if (ret) {
                if (ret != -EPROBE_DEFER)
@@ -1760,28 +1759,74 @@ static const struct component_ops hdmi_component_ops = {
        .unbind = hdmi_unbind,
 };
 
-static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
+static int hdmi_get_ddc_adapter(struct hdmi_context *hdata)
 {
        const char *compatible_str = "samsung,exynos4210-hdmiddc";
        struct device_node *np;
+       struct i2c_adapter *adpt;
 
        np = of_find_compatible_node(NULL, NULL, compatible_str);
        if (np)
-               return of_get_next_parent(np);
+               np = of_get_next_parent(np);
+       else
+               np = of_parse_phandle(hdata->dev->of_node, "ddc", 0);
+
+       if (!np) {
+               DRM_ERROR("Failed to find ddc node in device tree\n");
+               return -ENODEV;
+       }
 
-       return NULL;
+       adpt = of_find_i2c_adapter_by_node(np);
+       of_node_put(np);
+
+       if (!adpt) {
+               DRM_INFO("Failed to get ddc i2c adapter by node\n");
+               return -EPROBE_DEFER;
+       }
+
+       hdata->ddc_adpt = adpt;
+
+       return 0;
 }
 
-static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
+static int hdmi_get_phy_io(struct hdmi_context *hdata)
 {
        const char *compatible_str = "samsung,exynos4212-hdmiphy";
+       struct device_node *np;
+       int ret = 0;
+
+       np = of_find_compatible_node(NULL, NULL, compatible_str);
+       if (!np) {
+               np = of_parse_phandle(hdata->dev->of_node, "phy", 0);
+               if (!np) {
+                       DRM_ERROR("Failed to find hdmiphy node in device tree\n");
+                       return -ENODEV;
+               }
+       }
+
+       if (hdata->drv_data->is_apb_phy) {
+               hdata->regs_hdmiphy = of_iomap(np, 0);
+               if (!hdata->regs_hdmiphy) {
+                       DRM_ERROR("failed to ioremap hdmi phy\n");
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       } else {
+               hdata->hdmiphy_port = of_find_i2c_device_by_node(np);
+               if (!hdata->hdmiphy_port) {
+                       DRM_INFO("Failed to get hdmi phy i2c client\n");
+                       ret = -EPROBE_DEFER;
+                       goto out;
+               }
+       }
 
-       return of_find_compatible_node(NULL, NULL, compatible_str);
+out:
+       of_node_put(np);
+       return ret;
 }
 
 static int hdmi_probe(struct platform_device *pdev)
 {
-       struct device_node *ddc_node, *phy_node;
        struct device *dev = &pdev->dev;
        struct hdmi_context *hdata;
        struct resource *res;
@@ -1811,52 +1856,13 @@ static int hdmi_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ddc_node = hdmi_legacy_ddc_dt_binding(dev);
-       if (ddc_node)
-               goto out_get_ddc_adpt;
-
-       ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
-       if (!ddc_node) {
-               DRM_ERROR("Failed to find ddc node in device tree\n");
-               return -ENODEV;
-       }
-       of_node_put(dev->of_node);
-
-out_get_ddc_adpt:
-       hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
-       if (!hdata->ddc_adpt) {
-               DRM_ERROR("Failed to get ddc i2c adapter by node\n");
-               return -EPROBE_DEFER;
-       }
-
-       phy_node = hdmi_legacy_phy_dt_binding(dev);
-       if (phy_node)
-               goto out_get_phy_port;
+       ret = hdmi_get_ddc_adapter(hdata);
+       if (ret)
+               return ret;
 
-       phy_node = of_parse_phandle(dev->of_node, "phy", 0);
-       if (!phy_node) {
-               DRM_ERROR("Failed to find hdmiphy node in device tree\n");
-               ret = -ENODEV;
+       ret = hdmi_get_phy_io(hdata);
+       if (ret)
                goto err_ddc;
-       }
-       of_node_put(dev->of_node);
-
-out_get_phy_port:
-       if (hdata->drv_data->is_apb_phy) {
-               hdata->regs_hdmiphy = of_iomap(phy_node, 0);
-               if (!hdata->regs_hdmiphy) {
-                       DRM_ERROR("failed to ioremap hdmi phy\n");
-                       ret = -ENOMEM;
-                       goto err_ddc;
-               }
-       } else {
-               hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
-               if (!hdata->hdmiphy_port) {
-                       DRM_ERROR("Failed to get hdmi phy i2c client\n");
-                       ret = -EPROBE_DEFER;
-                       goto err_ddc;
-               }
-       }
 
        INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
 
index e1d47f9..edb20a3 100644 (file)
@@ -73,6 +73,9 @@ enum mixer_version_id {
 enum mixer_flag_bits {
        MXR_BIT_POWERED,
        MXR_BIT_VSYNC,
+       MXR_BIT_INTERLACE,
+       MXR_BIT_VP_ENABLED,
+       MXR_BIT_HAS_SCLK,
 };
 
 static const uint32_t mixer_formats[] = {
@@ -98,9 +101,6 @@ struct mixer_context {
        struct exynos_drm_plane planes[MIXER_WIN_NR];
        int                     pipe;
        unsigned long           flags;
-       bool                    interlace;
-       bool                    vp_enabled;
-       bool                    has_sclk;
 
        struct mixer_resources  mixer_res;
        enum mixer_version_id   mxr_ver;
@@ -346,7 +346,7 @@ static void mixer_vsync_set_update(struct mixer_context *ctx, bool enable)
        mixer_reg_writemask(res, MXR_STATUS, enable ?
                        MXR_STATUS_SYNC_ENABLE : 0, MXR_STATUS_SYNC_ENABLE);
 
-       if (ctx->vp_enabled)
+       if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
                vp_reg_write(res, VP_SHADOW_UPDATE, enable ?
                        VP_SHADOW_UPDATE_ENABLE : 0);
 }
@@ -357,8 +357,8 @@ static void mixer_cfg_scan(struct mixer_context *ctx, unsigned int height)
        u32 val;
 
        /* choosing between interlace and progressive mode */
-       val = (ctx->interlace ? MXR_CFG_SCAN_INTERLACE :
-                               MXR_CFG_SCAN_PROGRESSIVE);
+       val = test_bit(MXR_BIT_INTERLACE, &ctx->flags) ?
+               MXR_CFG_SCAN_INTERLACE : MXR_CFG_SCAN_PROGRESSIVE;
 
        if (ctx->mxr_ver != MXR_VER_128_0_0_184) {
                /* choosing between proper HD and SD mode */
@@ -436,9 +436,10 @@ static void mixer_cfg_layer(struct mixer_context *ctx, unsigned int win,
                mixer_reg_writemask(res, MXR_LAYER_CFG,
                                    MXR_LAYER_CFG_GRP1_VAL(priority),
                                    MXR_LAYER_CFG_GRP1_MASK);
+
                break;
        case VP_DEFAULT_WIN:
-               if (ctx->vp_enabled) {
+               if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
                        vp_reg_writemask(res, VP_ENABLE, val, VP_ENABLE_ON);
                        mixer_reg_writemask(res, MXR_CFG, val,
                                MXR_CFG_VP_ENABLE);
@@ -501,7 +502,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
        chroma_addr[0] = exynos_drm_fb_dma_addr(fb, 1);
 
        if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
-               ctx->interlace = true;
+               __set_bit(MXR_BIT_INTERLACE, &ctx->flags);
                if (tiled_mode) {
                        luma_addr[1] = luma_addr[0] + 0x40;
                        chroma_addr[1] = chroma_addr[0] + 0x40;
@@ -510,7 +511,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
                        chroma_addr[1] = chroma_addr[0] + fb->pitches[0];
                }
        } else {
-               ctx->interlace = false;
+               __clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
                luma_addr[1] = 0;
                chroma_addr[1] = 0;
        }
@@ -518,7 +519,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
        spin_lock_irqsave(&res->reg_slock, flags);
 
        /* interlace or progressive scan mode */
-       val = (ctx->interlace ? ~0 : 0);
+       val = (test_bit(MXR_BIT_INTERLACE, &ctx->flags) ? ~0 : 0);
        vp_reg_writemask(res, VP_MODE, val, VP_MODE_LINE_SKIP);
 
        /* setup format */
@@ -541,7 +542,7 @@ static void vp_video_buffer(struct mixer_context *ctx,
 
        vp_reg_write(res, VP_DST_WIDTH, state->crtc.w);
        vp_reg_write(res, VP_DST_H_POSITION, state->crtc.x);
-       if (ctx->interlace) {
+       if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) {
                vp_reg_write(res, VP_DST_HEIGHT, state->crtc.h / 2);
                vp_reg_write(res, VP_DST_V_POSITION, state->crtc.y / 2);
        } else {
@@ -636,9 +637,9 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
        src_y_offset = 0;
 
        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-               ctx->interlace = true;
+               __set_bit(MXR_BIT_INTERLACE, &ctx->flags);
        else
-               ctx->interlace = false;
+               __clear_bit(MXR_BIT_INTERLACE, &ctx->flags);
 
        spin_lock_irqsave(&res->reg_slock, flags);
 
@@ -697,10 +698,10 @@ static void mixer_graph_buffer(struct mixer_context *ctx,
 static void vp_win_reset(struct mixer_context *ctx)
 {
        struct mixer_resources *res = &ctx->mixer_res;
-       int tries = 100;
+       unsigned int tries = 100;
 
        vp_reg_write(res, VP_SRESET, VP_SRESET_PROCESSING);
-       for (tries = 100; tries; --tries) {
+       while (tries--) {
                /* waiting until VP_SRESET_PROCESSING is 0 */
                if (~vp_reg_read(res, VP_SRESET) & VP_SRESET_PROCESSING)
                        break;
@@ -733,7 +734,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
        mixer_reg_write(res, MXR_BG_COLOR1, 0x008080);
        mixer_reg_write(res, MXR_BG_COLOR2, 0x008080);
 
-       if (ctx->vp_enabled) {
+       if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
                /* configuration of Video Processor Registers */
                vp_win_reset(ctx);
                vp_default_filter(res);
@@ -742,7 +743,7 @@ static void mixer_win_reset(struct mixer_context *ctx)
        /* disable all layers */
        mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP0_ENABLE);
        mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_GRP1_ENABLE);
-       if (ctx->vp_enabled)
+       if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags))
                mixer_reg_writemask(res, MXR_CFG, 0, MXR_CFG_VP_ENABLE);
 
        spin_unlock_irqrestore(&res->reg_slock, flags);
@@ -753,7 +754,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
        struct mixer_context *ctx = arg;
        struct mixer_resources *res = &ctx->mixer_res;
        u32 val, base, shadow;
-       int win;
 
        spin_lock(&res->reg_slock);
 
@@ -767,7 +767,7 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
                val &= ~MXR_INT_STATUS_VSYNC;
 
                /* interlace scan need to check shadow register */
-               if (ctx->interlace) {
+               if (test_bit(MXR_BIT_INTERLACE, &ctx->flags)) {
                        base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0));
                        shadow = mixer_reg_read(res, MXR_GRAPHIC_BASE_S(0));
                        if (base != shadow)
@@ -780,14 +780,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg)
                }
 
                drm_crtc_handle_vblank(&ctx->crtc->base);
-               for (win = 0 ; win < MIXER_WIN_NR ; win++) {
-                       struct exynos_drm_plane *plane = &ctx->planes[win];
-
-                       if (!plane->pending_fb)
-                               continue;
-
-                       exynos_drm_crtc_finish_update(ctx->crtc, plane);
-               }
        }
 
 out:
@@ -867,7 +859,7 @@ static int vp_resources_init(struct mixer_context *mixer_ctx)
                return -ENODEV;
        }
 
-       if (mixer_ctx->has_sclk) {
+       if (test_bit(MXR_BIT_HAS_SCLK, &mixer_ctx->flags)) {
                mixer_res->sclk_mixer = devm_clk_get(dev, "sclk_mixer");
                if (IS_ERR(mixer_res->sclk_mixer)) {
                        dev_err(dev, "failed to get clock 'sclk_mixer'\n");
@@ -917,7 +909,7 @@ static int mixer_initialize(struct mixer_context *mixer_ctx,
                return ret;
        }
 
-       if (mixer_ctx->vp_enabled) {
+       if (test_bit(MXR_BIT_VP_ENABLED, &mixer_ctx->flags)) {
                /* acquire vp resources: regs, irqs, clocks */
                ret = vp_resources_init(mixer_ctx);
                if (ret) {
@@ -1160,7 +1152,8 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data)
                return ret;
 
        for (i = 0; i < MIXER_WIN_NR; i++) {
-               if (i == VP_DEFAULT_WIN && !ctx->vp_enabled)
+               if (i == VP_DEFAULT_WIN && !test_bit(MXR_BIT_VP_ENABLED,
+                                                    &ctx->flags))
                        continue;
 
                ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
@@ -1215,10 +1208,13 @@ static int mixer_probe(struct platform_device *pdev)
 
        ctx->pdev = pdev;
        ctx->dev = dev;
-       ctx->vp_enabled = drv->is_vp_enabled;
-       ctx->has_sclk = drv->has_sclk;
        ctx->mxr_ver = drv->version;
 
+       if (drv->is_vp_enabled)
+               __set_bit(MXR_BIT_VP_ENABLED, &ctx->flags);
+       if (drv->has_sclk)
+               __set_bit(MXR_BIT_HAS_SCLK, &ctx->flags);
+
        platform_set_drvdata(pdev, ctx);
 
        ret = component_add(&pdev->dev, &mixer_component_ops);
@@ -1244,9 +1240,9 @@ static int __maybe_unused exynos_mixer_suspend(struct device *dev)
 
        clk_disable_unprepare(res->hdmi);
        clk_disable_unprepare(res->mixer);
-       if (ctx->vp_enabled) {
+       if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
                clk_disable_unprepare(res->vp);
-               if (ctx->has_sclk)
+               if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags))
                        clk_disable_unprepare(res->sclk_mixer);
        }
 
@@ -1269,14 +1265,14 @@ static int __maybe_unused exynos_mixer_resume(struct device *dev)
                DRM_ERROR("Failed to prepare_enable the hdmi clk [%d]\n", ret);
                return ret;
        }
-       if (ctx->vp_enabled) {
+       if (test_bit(MXR_BIT_VP_ENABLED, &ctx->flags)) {
                ret = clk_prepare_enable(res->vp);
                if (ret < 0) {
                        DRM_ERROR("Failed to prepare_enable the vp clk [%d]\n",
                                  ret);
                        return ret;
                }
-               if (ctx->has_sclk) {
+               if (test_bit(MXR_BIT_HAS_SCLK, &ctx->flags)) {
                        ret = clk_prepare_enable(res->sclk_mixer);
                        if (ret < 0) {
                                DRM_ERROR("Failed to prepare_enable the " \
index 7882387..0884c45 100644 (file)
@@ -270,7 +270,7 @@ static int fsl_dcu_drm_pm_resume(struct device *dev)
        ret = clk_prepare_enable(fsl_dev->pix_clk);
        if (ret < 0) {
                dev_err(dev, "failed to enable pix clk\n");
-               return ret;
+               goto disable_dcu_clk;
        }
 
        fsl_dcu_drm_init_planes(fsl_dev->drm);
@@ -284,6 +284,10 @@ static int fsl_dcu_drm_pm_resume(struct device *dev)
        enable_irq(fsl_dev->irq);
 
        return 0;
+
+disable_dcu_clk:
+       clk_disable_unprepare(fsl_dev->clk);
+       return ret;
 }
 #endif
 
@@ -330,6 +334,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
        const char *pix_clk_in_name;
        const struct of_device_id *id;
        int ret;
+       u8 div_ratio_shift = 0;
 
        fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
        if (!fsl_dev)
@@ -382,11 +387,14 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
                pix_clk_in = fsl_dev->clk;
        }
 
+       if (of_property_read_bool(dev->of_node, "big-endian"))
+               div_ratio_shift = 24;
+
        pix_clk_in_name = __clk_get_name(pix_clk_in);
        snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
        fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
                        pix_clk_in_name, 0, base + DCU_DIV_RATIO,
-                       0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
+                       div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
        if (IS_ERR(fsl_dev->pix_clk)) {
                dev_err(dev, "failed to register pix clk\n");
                ret = PTR_ERR(fsl_dev->pix_clk);
@@ -402,8 +410,8 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
        fsl_dev->tcon = fsl_tcon_init(dev);
 
        drm = drm_dev_alloc(driver, dev);
-       if (!drm) {
-               ret = -ENOMEM;
+       if (IS_ERR(drm)) {
+               ret = PTR_ERR(drm);
                goto disable_pix_clk;
        }
 
index e50467a..a7e5486 100644 (file)
@@ -169,25 +169,10 @@ static void fsl_dcu_drm_plane_atomic_update(struct drm_plane *plane,
        return;
 }
 
-static void
-fsl_dcu_drm_plane_cleanup_fb(struct drm_plane *plane,
-                            const struct drm_plane_state *new_state)
-{
-}
-
-static int
-fsl_dcu_drm_plane_prepare_fb(struct drm_plane *plane,
-                            const struct drm_plane_state *new_state)
-{
-       return 0;
-}
-
 static const struct drm_plane_helper_funcs fsl_dcu_drm_plane_helper_funcs = {
        .atomic_check = fsl_dcu_drm_plane_atomic_check,
        .atomic_disable = fsl_dcu_drm_plane_atomic_disable,
        .atomic_update = fsl_dcu_drm_plane_atomic_update,
-       .cleanup_fb = fsl_dcu_drm_plane_cleanup_fb,
-       .prepare_fb = fsl_dcu_drm_plane_prepare_fb,
 };
 
 static void fsl_dcu_drm_plane_destroy(struct drm_plane *plane)
index bca09ea..3194e54 100644 (file)
@@ -57,10 +57,7 @@ static int fsl_tcon_init_regmap(struct device *dev,
 
        tcon->regs = devm_regmap_init_mmio(dev, regs,
                                           &fsl_tcon_regmap_config);
-       if (IS_ERR(tcon->regs))
-               return PTR_ERR(tcon->regs);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(tcon->regs);
 }
 
 struct fsl_tcon *fsl_tcon_init(struct device *dev)
index db9f7d0..0d2bb16 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/tty.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/console.h>
 
index 38dc890..ea733ab 100644 (file)
@@ -415,14 +415,6 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector)
        if (ret)
                return ret;
 
-       /* Didn't get an EDID, so
-        * Set wide sync ranges so we get all modes
-        * handed to valid_mode for checking
-        */
-       connector->display_info.min_vfreq = 0;
-       connector->display_info.max_vfreq = 200;
-       connector->display_info.min_hfreq = 0;
-       connector->display_info.max_hfreq = 200;
        if (mode_dev->panel_fixed_mode != NULL) {
                struct drm_display_mode *mode =
                    drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
index 0fcdce0..3a44e70 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/tty.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/console.h>
 
index 907cb51..acb3848 100644 (file)
@@ -335,11 +335,6 @@ static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
        struct drm_display_mode *dup_mode = NULL;
        struct drm_device *dev = connector->dev;
 
-       connector->display_info.min_vfreq = 0;
-       connector->display_info.max_vfreq = 200;
-       connector->display_info.min_hfreq = 0;
-       connector->display_info.max_hfreq = 200;
-
        if (fixed_mode) {
                dev_dbg(dev->dev, "fixed_mode %dx%d\n",
                                fixed_mode->hdisplay, fixed_mode->vdisplay);
index ab696ca..eab6d88 100644 (file)
@@ -163,10 +163,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
        if (bclp > 255)
                return ASLE_BACKLIGHT_FAILED;
 
-       if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) {
-               int max = bd->props.max_brightness;
-               gma_backlight_set(dev, bclp * max / 255);
-       }
+       gma_backlight_set(dev, bclp * bd->props.max_brightness / 255);
 
        asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID;
 
index e55733c..fd7c912 100644 (file)
@@ -530,15 +530,6 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector)
        if (ret)
                return ret;
 
-       /* Didn't get an EDID, so
-        * Set wide sync ranges so we get all modes
-        * handed to valid_mode for checking
-        */
-       connector->display_info.min_vfreq = 0;
-       connector->display_info.max_vfreq = 200;
-       connector->display_info.min_hfreq = 0;
-       connector->display_info.max_hfreq = 200;
-
        if (mode_dev->panel_fixed_mode != NULL) {
                struct drm_display_mode *mode =
                    drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
index 4fca0d6..e536072 100644 (file)
@@ -18,7 +18,6 @@
  */
 
 #include <linux/i2c.h>
-#include <linux/fb.h>
 #include <drm/drmP.h>
 #include "psb_intel_drv.h"
 
index c3707d4..7e7a4d4 100644 (file)
@@ -608,15 +608,17 @@ static void ade_rdma_set(void __iomem *base, struct drm_framebuffer *fb,
                         u32 ch, u32 y, u32 in_h, u32 fmt)
 {
        struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, 0);
+       char *format_name;
        u32 reg_ctrl, reg_addr, reg_size, reg_stride, reg_space, reg_en;
        u32 stride = fb->pitches[0];
        u32 addr = (u32)obj->paddr + y * stride;
 
        DRM_DEBUG_DRIVER("rdma%d: (y=%d, height=%d), stride=%d, paddr=0x%x\n",
                         ch + 1, y, in_h, stride, (u32)obj->paddr);
+       format_name = drm_get_format_name(fb->pixel_format);
        DRM_DEBUG_DRIVER("addr=0x%x, fb:%dx%d, pixel_format=%d(%s)\n",
-                        addr, fb->width, fb->height, fmt,
-                        drm_get_format_name(fb->pixel_format));
+                        addr, fb->width, fb->height, fmt, format_name);
+       kfree(format_name);
 
        /* get reg offset */
        reg_ctrl = RD_CH_CTRL(ch);
@@ -815,19 +817,6 @@ static void ade_disable_channel(struct ade_plane *aplane)
        ade_compositor_routing_disable(base, ch);
 }
 
-static int ade_plane_prepare_fb(struct drm_plane *plane,
-                               const struct drm_plane_state *new_state)
-{
-       /* do nothing */
-       return 0;
-}
-
-static void ade_plane_cleanup_fb(struct drm_plane *plane,
-                                const struct drm_plane_state *old_state)
-{
-       /* do nothing */
-}
-
 static int ade_plane_atomic_check(struct drm_plane *plane,
                                  struct drm_plane_state *state)
 {
@@ -895,8 +884,6 @@ static void ade_plane_atomic_disable(struct drm_plane *plane,
 }
 
 static const struct drm_plane_helper_funcs ade_plane_helper_funcs = {
-       .prepare_fb = ade_plane_prepare_fb,
-       .cleanup_fb = ade_plane_cleanup_fb,
        .atomic_check = ade_plane_atomic_check,
        .atomic_update = ade_plane_atomic_update,
        .atomic_disable = ade_plane_atomic_disable,
index 1edd9bc..90377a6 100644 (file)
@@ -169,7 +169,7 @@ static int kirin_gem_cma_dumb_create(struct drm_file *file,
 
 static struct drm_driver kirin_drm_driver = {
        .driver_features        = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
-                                 DRIVER_ATOMIC | DRIVER_HAVE_IRQ,
+                                 DRIVER_ATOMIC,
        .fops                   = &kirin_drm_fops,
 
        .gem_free_object_unlocked = drm_gem_cma_free_object,
@@ -207,8 +207,8 @@ static int kirin_drm_bind(struct device *dev)
        int ret;
 
        drm_dev = drm_dev_alloc(driver, dev);
-       if (!drm_dev)
-               return -ENOMEM;
+       if (IS_ERR(drm_dev))
+               return PTR_ERR(drm_dev);
 
        drm_dev->platformdev = to_platform_device(dev);
 
index 4d341db..a6c92be 100644 (file)
@@ -22,6 +22,7 @@ config DRM_I2C_SIL164
 config DRM_I2C_NXP_TDA998X
        tristate "NXP Semiconductors TDA998X HDMI encoder"
        default m if DRM_TILCDC
+       select SND_SOC_HDMI_CODEC if SND_SOC
        help
          Support for NXP Semiconductors TDA998X HDMI encoders.
 
index f4315bc..9798d40 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <sound/asoundef.h>
+#include <sound/hdmi-codec.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
 
 #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
 
+struct tda998x_audio_port {
+       u8 format;              /* AFMT_xxx */
+       u8 config;              /* AP value */
+};
+
 struct tda998x_priv {
        struct i2c_client *cec;
        struct i2c_client *hdmi;
@@ -41,7 +47,10 @@ struct tda998x_priv {
        u8 vip_cntrl_0;
        u8 vip_cntrl_1;
        u8 vip_cntrl_2;
-       struct tda998x_encoder_params params;
+       struct tda998x_audio_params audio_params;
+
+       struct platform_device *audio_pdev;
+       struct mutex audio_mutex;
 
        wait_queue_head_t wq_edid;
        volatile int wq_edid_wait;
@@ -53,6 +62,8 @@ struct tda998x_priv {
 
        struct drm_encoder encoder;
        struct drm_connector connector;
+
+       struct tda998x_audio_port audio_port[2];
 };
 
 #define conn_to_tda998x_priv(x) \
@@ -666,26 +677,16 @@ tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
        reg_set(priv, REG_DIP_IF_FLAGS, bit);
 }
 
-static void
-tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p)
+static int tda998x_write_aif(struct tda998x_priv *priv,
+                            struct hdmi_audio_infoframe *cea)
 {
        union hdmi_infoframe frame;
 
-       hdmi_audio_infoframe_init(&frame.audio);
-
-       frame.audio.channels = p->audio_frame[1] & 0x07;
-       frame.audio.channel_allocation = p->audio_frame[4];
-       frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3;
-       frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7;
-
-       /*
-        * L-PCM and IEC61937 compressed audio shall always set sample
-        * frequency to "refer to stream".  For others, see the HDMI
-        * specification.
-        */
-       frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2;
+       frame.audio = *cea;
 
        tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame);
+
+       return 0;
 }
 
 static void
@@ -710,20 +711,21 @@ static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
        }
 }
 
-static void
+static int
 tda998x_configure_audio(struct tda998x_priv *priv,
-               struct drm_display_mode *mode, struct tda998x_encoder_params *p)
+                       struct tda998x_audio_params *params,
+                       unsigned mode_clock)
 {
        u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
        u32 n;
 
        /* Enable audio ports */
-       reg_write(priv, REG_ENA_AP, p->audio_cfg);
-       reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
+       reg_write(priv, REG_ENA_AP, params->config);
 
        /* Set audio input source */
-       switch (p->audio_format) {
+       switch (params->format) {
        case AFMT_SPDIF:
+               reg_write(priv, REG_ENA_ACLK, 0);
                reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
                clksel_aip = AIP_CLKSEL_AIP_SPDIF;
                clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
@@ -731,15 +733,29 @@ tda998x_configure_audio(struct tda998x_priv *priv,
                break;
 
        case AFMT_I2S:
+               reg_write(priv, REG_ENA_ACLK, 1);
                reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
                clksel_aip = AIP_CLKSEL_AIP_I2S;
                clksel_fs = AIP_CLKSEL_FS_ACLK;
-               cts_n = CTS_N_M(3) | CTS_N_K(3);
+               switch (params->sample_width) {
+               case 16:
+                       cts_n = CTS_N_M(3) | CTS_N_K(1);
+                       break;
+               case 18:
+               case 20:
+               case 24:
+                       cts_n = CTS_N_M(3) | CTS_N_K(2);
+                       break;
+               default:
+               case 32:
+                       cts_n = CTS_N_M(3) | CTS_N_K(3);
+                       break;
+               }
                break;
 
        default:
-               BUG();
-               return;
+               dev_err(&priv->hdmi->dev, "Unsupported I2S format\n");
+               return -EINVAL;
        }
 
        reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
@@ -755,11 +771,11 @@ tda998x_configure_audio(struct tda998x_priv *priv,
         * assume 100MHz requires larger divider.
         */
        adiv = AUDIO_DIV_SERCLK_8;
-       if (mode->clock > 100000)
+       if (mode_clock > 100000)
                adiv++;                 /* AUDIO_DIV_SERCLK_16 */
 
        /* S/PDIF asks for a larger divider */
-       if (p->audio_format == AFMT_SPDIF)
+       if (params->format == AFMT_SPDIF)
                adiv++;                 /* AUDIO_DIV_SERCLK_16 or _32 */
 
        reg_write(priv, REG_AUDIO_DIV, adiv);
@@ -768,7 +784,7 @@ tda998x_configure_audio(struct tda998x_priv *priv,
         * This is the approximate value of N, which happens to be
         * the recommended values for non-coherent clocks.
         */
-       n = 128 * p->audio_sample_rate / 1000;
+       n = 128 * params->sample_rate / 1000;
 
        /* Write the CTS and N values */
        buf[0] = 0x44;
@@ -786,20 +802,21 @@ tda998x_configure_audio(struct tda998x_priv *priv,
        reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
        reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
 
-       /* Write the channel status */
-       buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
-       buf[1] = 0x00;
-       buf[2] = IEC958_AES3_CON_FS_NOTID;
-       buf[3] = IEC958_AES4_CON_ORIGFS_NOTID |
-                       IEC958_AES4_CON_MAX_WORDLEN_24;
+       /* Write the channel status
+        * The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because
+        * there is a separate register for each I2S wire.
+        */
+       buf[0] = params->status[0];
+       buf[1] = params->status[1];
+       buf[2] = params->status[3];
+       buf[3] = params->status[4];
        reg_write_range(priv, REG_CH_STAT_B(0), buf, 4);
 
        tda998x_audio_mute(priv, true);
        msleep(20);
        tda998x_audio_mute(priv, false);
 
-       /* Write the audio information packet */
-       tda998x_write_aif(priv, p);
+       return tda998x_write_aif(priv, &params->cea);
 }
 
 /* DRM encoder functions */
@@ -820,7 +837,7 @@ static void tda998x_encoder_set_config(struct tda998x_priv *priv,
                            VIP_CNTRL_2_SWAP_F(p->swap_f) |
                            (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
 
-       priv->params = *p;
+       priv->audio_params = p->audio_params;
 }
 
 static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
@@ -1057,9 +1074,13 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder,
 
                tda998x_write_avi(priv, adjusted_mode);
 
-               if (priv->params.audio_cfg)
-                       tda998x_configure_audio(priv, adjusted_mode,
-                                               &priv->params);
+               if (priv->audio_params.format != AFMT_UNUSED) {
+                       mutex_lock(&priv->audio_mutex);
+                       tda998x_configure_audio(priv,
+                                               &priv->audio_params,
+                                               adjusted_mode->clock);
+                       mutex_unlock(&priv->audio_mutex);
+               }
        }
 }
 
@@ -1159,6 +1180,8 @@ static int tda998x_connector_get_modes(struct drm_connector *connector)
        drm_mode_connector_update_edid_property(connector, edid);
        n = drm_add_edid_modes(connector, edid);
        priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
+       drm_edid_to_eld(connector, edid);
+
        kfree(edid);
 
        return n;
@@ -1180,6 +1203,9 @@ static void tda998x_destroy(struct tda998x_priv *priv)
        cec_write(priv, REG_CEC_RXSHPDINTENA, 0);
        reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD);
 
+       if (priv->audio_pdev)
+               platform_device_unregister(priv->audio_pdev);
+
        if (priv->hdmi->irq)
                free_irq(priv->hdmi->irq, priv);
 
@@ -1189,8 +1215,189 @@ static void tda998x_destroy(struct tda998x_priv *priv)
        i2c_unregister_device(priv->cec);
 }
 
+static int tda998x_audio_hw_params(struct device *dev, void *data,
+                                  struct hdmi_codec_daifmt *daifmt,
+                                  struct hdmi_codec_params *params)
+{
+       struct tda998x_priv *priv = dev_get_drvdata(dev);
+       int i, ret;
+       struct tda998x_audio_params audio = {
+               .sample_width = params->sample_width,
+               .sample_rate = params->sample_rate,
+               .cea = params->cea,
+       };
+
+       if (!priv->encoder.crtc)
+               return -ENODEV;
+
+       memcpy(audio.status, params->iec.status,
+              min(sizeof(audio.status), sizeof(params->iec.status)));
+
+       switch (daifmt->fmt) {
+       case HDMI_I2S:
+               if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
+                   daifmt->bit_clk_master || daifmt->frame_clk_master) {
+                       dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
+                               daifmt->bit_clk_inv, daifmt->frame_clk_inv,
+                               daifmt->bit_clk_master,
+                               daifmt->frame_clk_master);
+                       return -EINVAL;
+               }
+               for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+                       if (priv->audio_port[i].format == AFMT_I2S)
+                               audio.config = priv->audio_port[i].config;
+               audio.format = AFMT_I2S;
+               break;
+       case HDMI_SPDIF:
+               for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
+                       if (priv->audio_port[i].format == AFMT_SPDIF)
+                               audio.config = priv->audio_port[i].config;
+               audio.format = AFMT_SPDIF;
+               break;
+       default:
+               dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+               return -EINVAL;
+       }
+
+       if (audio.config == 0) {
+               dev_err(dev, "%s: No audio configutation found\n", __func__);
+               return -EINVAL;
+       }
+
+       mutex_lock(&priv->audio_mutex);
+       ret = tda998x_configure_audio(priv,
+                                     &audio,
+                                     priv->encoder.crtc->hwmode.clock);
+
+       if (ret == 0)
+               priv->audio_params = audio;
+       mutex_unlock(&priv->audio_mutex);
+
+       return ret;
+}
+
+static void tda998x_audio_shutdown(struct device *dev, void *data)
+{
+       struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+       mutex_lock(&priv->audio_mutex);
+
+       reg_write(priv, REG_ENA_AP, 0);
+
+       priv->audio_params.format = AFMT_UNUSED;
+
+       mutex_unlock(&priv->audio_mutex);
+}
+
+int tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
+{
+       struct tda998x_priv *priv = dev_get_drvdata(dev);
+
+       mutex_lock(&priv->audio_mutex);
+
+       tda998x_audio_mute(priv, enable);
+
+       mutex_unlock(&priv->audio_mutex);
+       return 0;
+}
+
+static int tda998x_audio_get_eld(struct device *dev, void *data,
+                                uint8_t *buf, size_t len)
+{
+       struct tda998x_priv *priv = dev_get_drvdata(dev);
+       struct drm_mode_config *config = &priv->encoder.dev->mode_config;
+       struct drm_connector *connector;
+       int ret = -ENODEV;
+
+       mutex_lock(&config->mutex);
+       list_for_each_entry(connector, &config->connector_list, head) {
+               if (&priv->encoder == connector->encoder) {
+                       memcpy(buf, connector->eld,
+                              min(sizeof(connector->eld), len));
+                       ret = 0;
+               }
+       }
+       mutex_unlock(&config->mutex);
+
+       return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+       .hw_params = tda998x_audio_hw_params,
+       .audio_shutdown = tda998x_audio_shutdown,
+       .digital_mute = tda998x_audio_digital_mute,
+       .get_eld = tda998x_audio_get_eld,
+};
+
+static int tda998x_audio_codec_init(struct tda998x_priv *priv,
+                                   struct device *dev)
+{
+       struct hdmi_codec_pdata codec_data = {
+               .ops = &audio_codec_ops,
+               .max_i2s_channels = 2,
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
+               if (priv->audio_port[i].format == AFMT_I2S &&
+                   priv->audio_port[i].config != 0)
+                       codec_data.i2s = 1;
+               if (priv->audio_port[i].format == AFMT_SPDIF &&
+                   priv->audio_port[i].config != 0)
+                       codec_data.spdif = 1;
+       }
+
+       priv->audio_pdev = platform_device_register_data(
+               dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+               &codec_data, sizeof(codec_data));
+
+       return PTR_ERR_OR_ZERO(priv->audio_pdev);
+}
+
 /* I2C driver functions */
 
+static int tda998x_get_audio_ports(struct tda998x_priv *priv,
+                                  struct device_node *np)
+{
+       const u32 *port_data;
+       u32 size;
+       int i;
+
+       port_data = of_get_property(np, "audio-ports", &size);
+       if (!port_data)
+               return 0;
+
+       size /= sizeof(u32);
+       if (size > 2 * ARRAY_SIZE(priv->audio_port) || size % 2 != 0) {
+               dev_err(&priv->hdmi->dev,
+                       "Bad number of elements in audio-ports dt-property\n");
+               return -EINVAL;
+       }
+
+       size /= 2;
+
+       for (i = 0; i < size; i++) {
+               u8 afmt = be32_to_cpup(&port_data[2*i]);
+               u8 ena_ap = be32_to_cpup(&port_data[2*i+1]);
+
+               if (afmt != AFMT_SPDIF && afmt != AFMT_I2S) {
+                       dev_err(&priv->hdmi->dev,
+                               "Bad audio format %u\n", afmt);
+                       return -EINVAL;
+               }
+
+               priv->audio_port[i].format = afmt;
+               priv->audio_port[i].config = ena_ap;
+       }
+
+       if (priv->audio_port[0].format == priv->audio_port[1].format) {
+               dev_err(&priv->hdmi->dev,
+                       "There can only be on I2S port and one SPDIF port\n");
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
 {
        struct device_node *np = client->dev.of_node;
@@ -1304,7 +1511,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
        if (!np)
                return 0;               /* non-DT */
 
-       /* get the optional video properties */
+       /* get the device tree parameters */
        ret = of_property_read_u32(np, "video-ports", &video);
        if (ret == 0) {
                priv->vip_cntrl_0 = video >> 16;
@@ -1312,8 +1519,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
                priv->vip_cntrl_2 = video;
        }
 
-       return 0;
+       mutex_init(&priv->audio_mutex); /* Protect access from audio thread */
 
+       ret = tda998x_get_audio_ports(priv, np);
+       if (ret)
+               goto fail;
+
+       if (priv->audio_port[0].format != AFMT_UNUSED)
+               tda998x_audio_codec_init(priv, &client->dev);
+
+       return 0;
 fail:
        /* if encoder_init fails, the encoder slave is never registered,
         * so cleanup here:
index 44f4a13..0be55dc 100644 (file)
@@ -56,9 +56,7 @@ static const struct file_operations i810_driver_fops = {
 };
 
 static struct drm_driver driver = {
-       .driver_features =
-           DRIVER_USE_AGP |
-           DRIVER_HAVE_DMA,
+       .driver_features = DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_LEGACY,
        .dev_priv_size = sizeof(drm_i810_buf_priv_t),
        .load = i810_driver_load,
        .lastclose = i810_driver_lastclose,
index 684fc1c..a998c2b 100644 (file)
@@ -3,15 +3,20 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 subdir-ccflags-$(CONFIG_DRM_I915_WERROR) := -Werror
+subdir-ccflags-y += \
+       $(call as-instr,movntdqa (%eax)$(comma)%xmm0,-DCONFIG_AS_MOVNTDQA)
 
 # Please keep these build lists sorted!
 
 # core driver code
 i915-y := i915_drv.o \
          i915_irq.o \
+         i915_memcpy.o \
+         i915_mm.o \
          i915_params.o \
          i915_pci.o \
           i915_suspend.o \
+         i915_sw_fence.o \
          i915_sysfs.o \
          intel_csr.o \
          intel_device_info.o \
@@ -25,7 +30,6 @@ i915-$(CONFIG_DEBUG_FS) += i915_debugfs.o
 i915-y += i915_cmd_parser.o \
          i915_gem_batch_pool.o \
          i915_gem_context.o \
-         i915_gem_debug.o \
          i915_gem_dmabuf.o \
          i915_gem_evict.o \
          i915_gem_execbuffer.o \
@@ -33,6 +37,7 @@ i915-y += i915_cmd_parser.o \
          i915_gem_gtt.o \
          i915_gem.o \
          i915_gem_render_state.o \
+         i915_gem_request.o \
          i915_gem_shrinker.o \
          i915_gem_stolen.o \
          i915_gem_tiling.o \
@@ -40,6 +45,7 @@ i915-y += i915_cmd_parser.o \
          i915_gpu_error.o \
          i915_trace_points.o \
          intel_breadcrumbs.o \
+         intel_engine_cs.o \
          intel_lrc.o \
          intel_mocs.o \
          intel_ringbuffer.o \
@@ -109,6 +115,6 @@ i915-y += intel_gvt.o
 include $(src)/gvt/Makefile
 endif
 
-obj-$(CONFIG_DRM_I915)  += i915.o
+obj-$(CONFIG_DRM_I915) += i915.o
 
 CFLAGS_i915_trace_points.o := -I$(src)
index b0fd6a7..70980f8 100644 (file)
  * The parser always rejects such commands.
  *
  * The majority of the problematic commands fall in the MI_* range, with only a
- * few specific commands on each ring (e.g. PIPE_CONTROL and MI_FLUSH_DW).
+ * few specific commands on each engine (e.g. PIPE_CONTROL and MI_FLUSH_DW).
  *
  * Implementation:
- * Each ring maintains tables of commands and registers which the parser uses in
- * scanning batch buffers submitted to that ring.
+ * Each engine maintains tables of commands and registers which the parser
+ * uses in scanning batch buffers submitted to that engine.
  *
  * Since the set of commands that the parser must check for is significantly
  * smaller than the number of commands supported, the parser tables contain only
  * those commands required by the parser. This generally works because command
  * opcode ranges have standard command length encodings. So for commands that
  * the parser does not need to check, it can easily skip them. This is
- * implemented via a per-ring length decoding vfunc.
+ * implemented via a per-engine length decoding vfunc.
  *
  * Unfortunately, there are a number of commands that do not follow the standard
  * length encoding for their opcode range, primarily amongst the MI_* commands.
  * To handle this, the parser provides a way to define explicit "skip" entries
- * in the per-ring command tables.
+ * in the per-engine command tables.
  *
  * Other command table entries map fairly directly to high level categories
  * mentioned above: rejected, master-only, register whitelist. The parser
  * general bitmasking mechanism.
  */
 
-#define STD_MI_OPCODE_MASK  0xFF800000
-#define STD_3D_OPCODE_MASK  0xFFFF0000
-#define STD_2D_OPCODE_MASK  0xFFC00000
-#define STD_MFX_OPCODE_MASK 0xFFFF0000
+#define STD_MI_OPCODE_SHIFT  (32 - 9)
+#define STD_3D_OPCODE_SHIFT  (32 - 16)
+#define STD_2D_OPCODE_SHIFT  (32 - 10)
+#define STD_MFX_OPCODE_SHIFT (32 - 16)
+#define MIN_OPCODE_SHIFT 16
 
 #define CMD(op, opm, f, lm, fl, ...)                           \
        {                                                       \
                .flags = (fl) | ((f) ? CMD_DESC_FIXED : 0),     \
-               .cmd = { (op), (opm) },                         \
+               .cmd = { (op), ~0u << (opm) },                  \
                .length = { (lm) },                             \
                __VA_ARGS__                                     \
        }
 
 /* Convenience macros to compress the tables */
-#define SMI STD_MI_OPCODE_MASK
-#define S3D STD_3D_OPCODE_MASK
-#define S2D STD_2D_OPCODE_MASK
-#define SMFX STD_MFX_OPCODE_MASK
+#define SMI STD_MI_OPCODE_SHIFT
+#define S3D STD_3D_OPCODE_SHIFT
+#define S2D STD_2D_OPCODE_SHIFT
+#define SMFX STD_MFX_OPCODE_SHIFT
 #define F true
 #define S CMD_DESC_SKIP
 #define R CMD_DESC_REJECT
@@ -350,6 +351,9 @@ static const struct drm_i915_cmd_descriptor hsw_blt_cmds[] = {
        CMD(  MI_LOAD_SCAN_LINES_EXCL,          SMI,   !F,  0x3F,   R  ),
 };
 
+static const struct drm_i915_cmd_descriptor noop_desc =
+       CMD(MI_NOOP, SMI, F, 1, S);
+
 #undef CMD
 #undef SMI
 #undef S3D
@@ -458,6 +462,7 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = {
        REG32(GEN7_GPGPU_DISPATCHDIMX),
        REG32(GEN7_GPGPU_DISPATCHDIMY),
        REG32(GEN7_GPGPU_DISPATCHDIMZ),
+       REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE),
        REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 0),
        REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 1),
        REG64_IDX(GEN7_SO_NUM_PRIMS_WRITTEN, 2),
@@ -473,6 +478,7 @@ static const struct drm_i915_reg_descriptor gen7_render_regs[] = {
        REG32(GEN7_L3SQCREG1),
        REG32(GEN7_L3CNTLREG2),
        REG32(GEN7_L3CNTLREG3),
+       REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
 };
 
 static const struct drm_i915_reg_descriptor hsw_render_regs[] = {
@@ -502,7 +508,10 @@ static const struct drm_i915_reg_descriptor hsw_render_regs[] = {
 };
 
 static const struct drm_i915_reg_descriptor gen7_blt_regs[] = {
+       REG64_IDX(RING_TIMESTAMP, RENDER_RING_BASE),
+       REG64_IDX(RING_TIMESTAMP, BSD_RING_BASE),
        REG32(BCS_SWCTRL),
+       REG64_IDX(RING_TIMESTAMP, BLT_RING_BASE),
 };
 
 static const struct drm_i915_reg_descriptor ivb_master_regs[] = {
@@ -603,7 +612,7 @@ static u32 gen7_blt_get_cmd_length_mask(u32 cmd_header)
        return 0;
 }
 
-static bool validate_cmds_sorted(struct intel_engine_cs *engine,
+static bool validate_cmds_sorted(const struct intel_engine_cs *engine,
                                 const struct drm_i915_cmd_table *cmd_tables,
                                 int cmd_table_count)
 {
@@ -624,8 +633,10 @@ static bool validate_cmds_sorted(struct intel_engine_cs *engine,
                        u32 curr = desc->cmd.value & desc->cmd.mask;
 
                        if (curr < previous) {
-                               DRM_ERROR("CMD: table not sorted ring=%d table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
-                                         engine->id, i, j, curr, previous);
+                               DRM_ERROR("CMD: %s [%d] command table not sorted: "
+                                         "table=%d entry=%d cmd=0x%08X prev=0x%08X\n",
+                                         engine->name, engine->id,
+                                         i, j, curr, previous);
                                ret = false;
                        }
 
@@ -636,7 +647,7 @@ static bool validate_cmds_sorted(struct intel_engine_cs *engine,
        return ret;
 }
 
-static bool check_sorted(int ring_id,
+static bool check_sorted(const struct intel_engine_cs *engine,
                         const struct drm_i915_reg_descriptor *reg_table,
                         int reg_count)
 {
@@ -648,8 +659,10 @@ static bool check_sorted(int ring_id,
                u32 curr = i915_mmio_reg_offset(reg_table[i].addr);
 
                if (curr < previous) {
-                       DRM_ERROR("CMD: table not sorted ring=%d entry=%d reg=0x%08X prev=0x%08X\n",
-                                 ring_id, i, curr, previous);
+                       DRM_ERROR("CMD: %s [%d] register table not sorted: "
+                                 "entry=%d reg=0x%08X prev=0x%08X\n",
+                                 engine->name, engine->id,
+                                 i, curr, previous);
                        ret = false;
                }
 
@@ -666,7 +679,7 @@ static bool validate_regs_sorted(struct intel_engine_cs *engine)
 
        for (i = 0; i < engine->reg_table_count; i++) {
                table = &engine->reg_tables[i];
-               if (!check_sorted(engine->id, table->regs, table->num_regs))
+               if (!check_sorted(engine, table->regs, table->num_regs))
                        return false;
        }
 
@@ -687,12 +700,26 @@ struct cmd_node {
  * non-opcode bits being set. But if we don't include those bits, some 3D
  * commands may hash to the same bucket due to not including opcode bits that
  * make the command unique. For now, we will risk hashing to the same bucket.
- *
- * If we attempt to generate a perfect hash, we should be able to look at bits
- * 31:29 of a command from a batch buffer and use the full mask for that
- * client. The existing INSTR_CLIENT_MASK/SHIFT defines can be used for this.
  */
-#define CMD_HASH_MASK STD_MI_OPCODE_MASK
+static inline u32 cmd_header_key(u32 x)
+{
+       u32 shift;
+
+       switch (x >> INSTR_CLIENT_SHIFT) {
+       default:
+       case INSTR_MI_CLIENT:
+               shift = STD_MI_OPCODE_SHIFT;
+               break;
+       case INSTR_RC_CLIENT:
+               shift = STD_3D_OPCODE_SHIFT;
+               break;
+       case INSTR_BC_CLIENT:
+               shift = STD_2D_OPCODE_SHIFT;
+               break;
+       }
+
+       return x >> shift;
+}
 
 static int init_hash_table(struct intel_engine_cs *engine,
                           const struct drm_i915_cmd_table *cmd_tables,
@@ -716,7 +743,7 @@ static int init_hash_table(struct intel_engine_cs *engine,
 
                        desc_node->desc = desc;
                        hash_add(engine->cmd_hash, &desc_node->node,
-                                desc->cmd.value & CMD_HASH_MASK);
+                                cmd_header_key(desc->cmd.value));
                }
        }
 
@@ -736,23 +763,21 @@ static void fini_hash_table(struct intel_engine_cs *engine)
 }
 
 /**
- * i915_cmd_parser_init_ring() - set cmd parser related fields for a ringbuffer
+ * intel_engine_init_cmd_parser() - set cmd parser related fields for an engine
  * @engine: the engine to initialize
  *
  * Optionally initializes fields related to batch buffer command parsing in the
  * struct intel_engine_cs based on whether the platform requires software
  * command parsing.
- *
- * Return: non-zero if initialization fails
  */
-int i915_cmd_parser_init_ring(struct intel_engine_cs *engine)
+void intel_engine_init_cmd_parser(struct intel_engine_cs *engine)
 {
        const struct drm_i915_cmd_table *cmd_tables;
        int cmd_table_count;
        int ret;
 
        if (!IS_GEN7(engine->i915))
-               return 0;
+               return;
 
        switch (engine->id) {
        case RCS:
@@ -806,36 +831,38 @@ int i915_cmd_parser_init_ring(struct intel_engine_cs *engine)
                engine->get_cmd_length_mask = gen7_bsd_get_cmd_length_mask;
                break;
        default:
-               DRM_ERROR("CMD: cmd_parser_init with unknown ring: %d\n",
-                         engine->id);
-               BUG();
+               MISSING_CASE(engine->id);
+               return;
        }
 
-       BUG_ON(!validate_cmds_sorted(engine, cmd_tables, cmd_table_count));
-       BUG_ON(!validate_regs_sorted(engine));
-
-       WARN_ON(!hash_empty(engine->cmd_hash));
+       if (!validate_cmds_sorted(engine, cmd_tables, cmd_table_count)) {
+               DRM_ERROR("%s: command descriptions are not sorted\n",
+                         engine->name);
+               return;
+       }
+       if (!validate_regs_sorted(engine)) {
+               DRM_ERROR("%s: registers are not sorted\n", engine->name);
+               return;
+       }
 
        ret = init_hash_table(engine, cmd_tables, cmd_table_count);
        if (ret) {
-               DRM_ERROR("CMD: cmd_parser_init failed!\n");
+               DRM_ERROR("%s: initialised failed!\n", engine->name);
                fini_hash_table(engine);
-               return ret;
+               return;
        }
 
        engine->needs_cmd_parser = true;
-
-       return 0;
 }
 
 /**
- * i915_cmd_parser_fini_ring() - clean up cmd parser related fields
+ * intel_engine_cleanup_cmd_parser() - clean up cmd parser related fields
  * @engine: the engine to clean up
  *
  * Releases any resources related to command parsing that may have been
- * initialized for the specified ring.
+ * initialized for the specified engine.
  */
-void i915_cmd_parser_fini_ring(struct intel_engine_cs *engine)
+void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine)
 {
        if (!engine->needs_cmd_parser)
                return;
@@ -850,12 +877,9 @@ find_cmd_in_table(struct intel_engine_cs *engine,
        struct cmd_node *desc_node;
 
        hash_for_each_possible(engine->cmd_hash, desc_node, node,
-                              cmd_header & CMD_HASH_MASK) {
+                              cmd_header_key(cmd_header)) {
                const struct drm_i915_cmd_descriptor *desc = desc_node->desc;
-               u32 masked_cmd = desc->cmd.mask & cmd_header;
-               u32 masked_value = desc->cmd.value & desc->cmd.mask;
-
-               if (masked_cmd == masked_value)
+               if (((cmd_header ^ desc->cmd.value) & desc->cmd.mask) == 0)
                        return desc;
        }
 
@@ -866,18 +890,21 @@ find_cmd_in_table(struct intel_engine_cs *engine,
  * Returns a pointer to a descriptor for the command specified by cmd_header.
  *
  * The caller must supply space for a default descriptor via the default_desc
- * parameter. If no descriptor for the specified command exists in the ring's
+ * parameter. If no descriptor for the specified command exists in the engine's
  * command parser tables, this function fills in default_desc based on the
- * ring's default length encoding and returns default_desc.
+ * engine's default length encoding and returns default_desc.
  */
 static const struct drm_i915_cmd_descriptor*
 find_cmd(struct intel_engine_cs *engine,
         u32 cmd_header,
+        const struct drm_i915_cmd_descriptor *desc,
         struct drm_i915_cmd_descriptor *default_desc)
 {
-       const struct drm_i915_cmd_descriptor *desc;
        u32 mask;
 
+       if (((cmd_header ^ desc->cmd.value) & desc->cmd.mask) == 0)
+               return desc;
+
        desc = find_cmd_in_table(engine, cmd_header);
        if (desc)
                return desc;
@@ -886,152 +913,140 @@ find_cmd(struct intel_engine_cs *engine,
        if (!mask)
                return NULL;
 
-       BUG_ON(!default_desc);
-       default_desc->flags = CMD_DESC_SKIP;
+       default_desc->cmd.value = cmd_header;
+       default_desc->cmd.mask = ~0u << MIN_OPCODE_SHIFT;
        default_desc->length.mask = mask;
-
+       default_desc->flags = CMD_DESC_SKIP;
        return default_desc;
 }
 
 static const struct drm_i915_reg_descriptor *
-find_reg(const struct drm_i915_reg_descriptor *table,
-        int count, u32 addr)
+__find_reg(const struct drm_i915_reg_descriptor *table, int count, u32 addr)
 {
-       int i;
-
-       for (i = 0; i < count; i++) {
-               if (i915_mmio_reg_offset(table[i].addr) == addr)
-                       return &table[i];
+       int start = 0, end = count;
+       while (start < end) {
+               int mid = start + (end - start) / 2;
+               int ret = addr - i915_mmio_reg_offset(table[mid].addr);
+               if (ret < 0)
+                       end = mid;
+               else if (ret > 0)
+                       start = mid + 1;
+               else
+                       return &table[mid];
        }
-
        return NULL;
 }
 
 static const struct drm_i915_reg_descriptor *
-find_reg_in_tables(const struct drm_i915_reg_table *tables,
-                  int count, bool is_master, u32 addr)
+find_reg(const struct intel_engine_cs *engine, bool is_master, u32 addr)
 {
-       int i;
-       const struct drm_i915_reg_table *table;
-       const struct drm_i915_reg_descriptor *reg;
+       const struct drm_i915_reg_table *table = engine->reg_tables;
+       int count = engine->reg_table_count;
 
-       for (i = 0; i < count; i++) {
-               table = &tables[i];
+       do {
                if (!table->master || is_master) {
-                       reg = find_reg(table->regs, table->num_regs,
-                                      addr);
+                       const struct drm_i915_reg_descriptor *reg;
+
+                       reg = __find_reg(table->regs, table->num_regs, addr);
                        if (reg != NULL)
                                return reg;
                }
-       }
+       } while (table++, --count);
 
        return NULL;
 }
 
-static u32 *vmap_batch(struct drm_i915_gem_object *obj,
-                      unsigned start, unsigned len)
-{
-       int i;
-       void *addr = NULL;
-       struct sg_page_iter sg_iter;
-       int first_page = start >> PAGE_SHIFT;
-       int last_page = (len + start + 4095) >> PAGE_SHIFT;
-       int npages = last_page - first_page;
-       struct page **pages;
-
-       pages = drm_malloc_ab(npages, sizeof(*pages));
-       if (pages == NULL) {
-               DRM_DEBUG_DRIVER("Failed to get space for pages\n");
-               goto finish;
-       }
-
-       i = 0;
-       for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents, first_page) {
-               pages[i++] = sg_page_iter_page(&sg_iter);
-               if (i == npages)
-                       break;
-       }
-
-       addr = vmap(pages, i, 0, PAGE_KERNEL);
-       if (addr == NULL) {
-               DRM_DEBUG_DRIVER("Failed to vmap pages\n");
-               goto finish;
-       }
-
-finish:
-       if (pages)
-               drm_free_large(pages);
-       return (u32*)addr;
-}
-
-/* Returns a vmap'd pointer to dest_obj, which the caller must unmap */
-static u32 *copy_batch(struct drm_i915_gem_object *dest_obj,
+/* Returns a vmap'd pointer to dst_obj, which the caller must unmap */
+static u32 *copy_batch(struct drm_i915_gem_object *dst_obj,
                       struct drm_i915_gem_object *src_obj,
                       u32 batch_start_offset,
-                      u32 batch_len)
+                      u32 batch_len,
+                      bool *needs_clflush_after)
 {
-       int needs_clflush = 0;
-       void *src_base, *src;
-       void *dst = NULL;
+       unsigned int src_needs_clflush;
+       unsigned int dst_needs_clflush;
+       void *dst, *src;
        int ret;
 
-       if (batch_len > dest_obj->base.size ||
-           batch_len + batch_start_offset > src_obj->base.size)
-               return ERR_PTR(-E2BIG);
-
-       if (WARN_ON(dest_obj->pages_pin_count == 0))
-               return ERR_PTR(-ENODEV);
-
-       ret = i915_gem_obj_prepare_shmem_read(src_obj, &needs_clflush);
-       if (ret) {
-               DRM_DEBUG_DRIVER("CMD: failed to prepare shadow batch\n");
+       ret = i915_gem_obj_prepare_shmem_read(src_obj, &src_needs_clflush);
+       if (ret)
                return ERR_PTR(ret);
-       }
 
-       src_base = vmap_batch(src_obj, batch_start_offset, batch_len);
-       if (!src_base) {
-               DRM_DEBUG_DRIVER("CMD: Failed to vmap batch\n");
-               ret = -ENOMEM;
+       ret = i915_gem_obj_prepare_shmem_write(dst_obj, &dst_needs_clflush);
+       if (ret) {
+               dst = ERR_PTR(ret);
                goto unpin_src;
        }
 
-       ret = i915_gem_object_set_to_cpu_domain(dest_obj, true);
-       if (ret) {
-               DRM_DEBUG_DRIVER("CMD: Failed to set shadow batch to CPU\n");
-               goto unmap_src;
+       dst = i915_gem_object_pin_map(dst_obj, I915_MAP_WB);
+       if (IS_ERR(dst))
+               goto unpin_dst;
+
+       src = ERR_PTR(-ENODEV);
+       if (src_needs_clflush &&
+           i915_memcpy_from_wc((void *)(uintptr_t)batch_start_offset, NULL, 0)) {
+               src = i915_gem_object_pin_map(src_obj, I915_MAP_WC);
+               if (!IS_ERR(src)) {
+                       i915_memcpy_from_wc(dst,
+                                           src + batch_start_offset,
+                                           ALIGN(batch_len, 16));
+                       i915_gem_object_unpin_map(src_obj);
+               }
        }
-
-       dst = vmap_batch(dest_obj, 0, batch_len);
-       if (!dst) {
-               DRM_DEBUG_DRIVER("CMD: Failed to vmap shadow batch\n");
-               ret = -ENOMEM;
-               goto unmap_src;
+       if (IS_ERR(src)) {
+               void *ptr;
+               int offset, n;
+
+               offset = offset_in_page(batch_start_offset);
+
+               /* We can avoid clflushing partial cachelines before the write
+                * if we only every write full cache-lines. Since we know that
+                * both the source and destination are in multiples of
+                * PAGE_SIZE, we can simply round up to the next cacheline.
+                * We don't care about copying too much here as we only
+                * validate up to the end of the batch.
+                */
+               if (dst_needs_clflush & CLFLUSH_BEFORE)
+                       batch_len = roundup(batch_len,
+                                           boot_cpu_data.x86_clflush_size);
+
+               ptr = dst;
+               for (n = batch_start_offset >> PAGE_SHIFT; batch_len; n++) {
+                       int len = min_t(int, batch_len, PAGE_SIZE - offset);
+
+                       src = kmap_atomic(i915_gem_object_get_page(src_obj, n));
+                       if (src_needs_clflush)
+                               drm_clflush_virt_range(src + offset, len);
+                       memcpy(ptr, src + offset, len);
+                       kunmap_atomic(src);
+
+                       ptr += len;
+                       batch_len -= len;
+                       offset = 0;
+               }
        }
 
-       src = src_base + offset_in_page(batch_start_offset);
-       if (needs_clflush)
-               drm_clflush_virt_range(src, batch_len);
-
-       memcpy(dst, src, batch_len);
+       /* dst_obj is returned with vmap pinned */
+       *needs_clflush_after = dst_needs_clflush & CLFLUSH_AFTER;
 
-unmap_src:
-       vunmap(src_base);
+unpin_dst:
+       i915_gem_obj_finish_shmem_access(dst_obj);
 unpin_src:
-       i915_gem_object_unpin_pages(src_obj);
-
-       return ret ? ERR_PTR(ret) : dst;
+       i915_gem_obj_finish_shmem_access(src_obj);
+       return dst;
 }
 
 /**
- * i915_needs_cmd_parser() - should a given ring use software command parsing?
+ * intel_engine_needs_cmd_parser() - should a given engine use software
+ *                                   command parsing?
  * @engine: the engine in question
  *
  * Only certain platforms require software batch buffer command parsing, and
  * only when enabled via module parameter.
  *
- * Return: true if the ring requires software command parsing
+ * Return: true if the engine requires software command parsing
  */
-bool i915_needs_cmd_parser(struct intel_engine_cs *engine)
+bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine)
 {
        if (!engine->needs_cmd_parser)
                return false;
@@ -1048,6 +1063,9 @@ static bool check_cmd(const struct intel_engine_cs *engine,
                      const bool is_master,
                      bool *oacontrol_set)
 {
+       if (desc->flags & CMD_DESC_SKIP)
+               return true;
+
        if (desc->flags & CMD_DESC_REJECT) {
                DRM_DEBUG_DRIVER("CMD: Rejected command: 0x%08X\n", *cmd);
                return false;
@@ -1072,14 +1090,11 @@ static bool check_cmd(const struct intel_engine_cs *engine,
                     offset += step) {
                        const u32 reg_addr = cmd[offset] & desc->reg.mask;
                        const struct drm_i915_reg_descriptor *reg =
-                               find_reg_in_tables(engine->reg_tables,
-                                                  engine->reg_table_count,
-                                                  is_master,
-                                                  reg_addr);
+                               find_reg(engine, is_master, reg_addr);
 
                        if (!reg) {
-                               DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (ring=%d)\n",
-                                                reg_addr, *cmd, engine->id);
+                               DRM_DEBUG_DRIVER("CMD: Rejected register 0x%08X in command: 0x%08X (exec_id=%d)\n",
+                                                reg_addr, *cmd, engine->exec_id);
                                return false;
                        }
 
@@ -1159,11 +1174,11 @@ static bool check_cmd(const struct intel_engine_cs *engine,
                                desc->bits[i].mask;
 
                        if (dword != desc->bits[i].expected) {
-                               DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (ring=%d)\n",
+                               DRM_DEBUG_DRIVER("CMD: Rejected command 0x%08X for bitmask 0x%08X (exp=0x%08X act=0x%08X) (exec_id=%d)\n",
                                                 *cmd,
                                                 desc->bits[i].mask,
                                                 desc->bits[i].expected,
-                                                dword, engine->id);
+                                                dword, engine->exec_id);
                                return false;
                        }
                }
@@ -1189,23 +1204,26 @@ static bool check_cmd(const struct intel_engine_cs *engine,
  * Return: non-zero if the parser finds violations or otherwise fails; -EACCES
  * if the batch appears legal but should use hardware parsing
  */
-int i915_parse_cmds(struct intel_engine_cs *engine,
-                   struct drm_i915_gem_object *batch_obj,
-                   struct drm_i915_gem_object *shadow_batch_obj,
-                   u32 batch_start_offset,
-                   u32 batch_len,
-                   bool is_master)
+int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+                           struct drm_i915_gem_object *batch_obj,
+                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u32 batch_start_offset,
+                           u32 batch_len,
+                           bool is_master)
 {
-       u32 *cmd, *batch_base, *batch_end;
-       struct drm_i915_cmd_descriptor default_desc = { 0 };
+       u32 *cmd, *batch_end;
+       struct drm_i915_cmd_descriptor default_desc = noop_desc;
+       const struct drm_i915_cmd_descriptor *desc = &default_desc;
        bool oacontrol_set = false; /* OACONTROL tracking. See check_cmd() */
+       bool needs_clflush_after = false;
        int ret = 0;
 
-       batch_base = copy_batch(shadow_batch_obj, batch_obj,
-                               batch_start_offset, batch_len);
-       if (IS_ERR(batch_base)) {
+       cmd = copy_batch(shadow_batch_obj, batch_obj,
+                        batch_start_offset, batch_len,
+                        &needs_clflush_after);
+       if (IS_ERR(cmd)) {
                DRM_DEBUG_DRIVER("CMD: Failed to copy batch\n");
-               return PTR_ERR(batch_base);
+               return PTR_ERR(cmd);
        }
 
        /*
@@ -1213,17 +1231,14 @@ int i915_parse_cmds(struct intel_engine_cs *engine,
         * large or larger and copy_batch() will write MI_NOPs to the extra
         * space. Parsing should be faster in some cases this way.
         */
-       batch_end = batch_base + (batch_len / sizeof(*batch_end));
-
-       cmd = batch_base;
+       batch_end = cmd + (batch_len / sizeof(*batch_end));
        while (cmd < batch_end) {
-               const struct drm_i915_cmd_descriptor *desc;
                u32 length;
 
                if (*cmd == MI_BATCH_BUFFER_END)
                        break;
 
-               desc = find_cmd(engine, *cmd, &default_desc);
+               desc = find_cmd(engine, *cmd, desc, &default_desc);
                if (!desc) {
                        DRM_DEBUG_DRIVER("CMD: Unrecognized command: 0x%08X\n",
                                         *cmd);
@@ -1274,7 +1289,9 @@ int i915_parse_cmds(struct intel_engine_cs *engine,
                ret = -EINVAL;
        }
 
-       vunmap(batch_base);
+       if (ret == 0 && needs_clflush_after)
+               drm_clflush_virt_range(shadow_batch_obj->mapping, batch_len);
+       i915_gem_object_unpin_map(shadow_batch_obj);
 
        return ret;
 }
@@ -1295,7 +1312,7 @@ int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv)
 
        /* If the command parser is not enabled, report 0 - unsupported */
        for_each_engine(engine, dev_priv) {
-               if (i915_needs_cmd_parser(engine)) {
+               if (intel_engine_needs_cmd_parser(engine)) {
                        active = true;
                        break;
                }
index 844fea7..27b0e34 100644 (file)
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
-enum {
-       ACTIVE_LIST,
-       INACTIVE_LIST,
-       PINNED_LIST,
-};
+static inline struct drm_i915_private *node_to_i915(struct drm_info_node *node)
+{
+       return to_i915(node->minor->dev);
+}
 
 /* As the drm_debugfs_init() routines are called before dev->dev_private is
  * allocated we need to hook into the minor for release. */
@@ -63,7 +62,7 @@ drm_add_fake_info_node(struct drm_minor *minor,
 
        node->minor = minor;
        node->dent = ent;
-       node->info_ent = (void *) key;
+       node->info_ent = (void *)key;
 
        mutex_lock(&minor->debugfs_lock);
        list_add(&node->list, &minor->debugfs_list);
@@ -74,12 +73,11 @@ drm_add_fake_info_node(struct drm_minor *minor,
 
 static int i915_capabilities(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       const struct intel_device_info *info = INTEL_INFO(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       const struct intel_device_info *info = INTEL_INFO(dev_priv);
 
-       seq_printf(m, "gen: %d\n", info->gen);
-       seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev));
+       seq_printf(m, "gen: %d\n", INTEL_GEN(dev_priv));
+       seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev_priv));
 #define PRINT_FLAG(x)  seq_printf(m, #x ": %s\n", yesno(info->x))
 #define SEP_SEMICOLON ;
        DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON);
@@ -91,7 +89,7 @@ static int i915_capabilities(struct seq_file *m, void *data)
 
 static char get_active_flag(struct drm_i915_gem_object *obj)
 {
-       return obj->active ? '*' : ' ';
+       return i915_gem_object_is_active(obj) ? '*' : ' ';
 }
 
 static char get_pin_flag(struct drm_i915_gem_object *obj)
@@ -101,7 +99,7 @@ static char get_pin_flag(struct drm_i915_gem_object *obj)
 
 static char get_tiling_flag(struct drm_i915_gem_object *obj)
 {
-       switch (obj->tiling_mode) {
+       switch (i915_gem_object_get_tiling(obj)) {
        default:
        case I915_TILING_NONE: return ' ';
        case I915_TILING_X: return 'X';
@@ -111,7 +109,7 @@ static char get_tiling_flag(struct drm_i915_gem_object *obj)
 
 static char get_global_flag(struct drm_i915_gem_object *obj)
 {
-       return i915_gem_obj_to_ggtt(obj) ? 'g' : ' ';
+       return i915_gem_object_to_ggtt(obj, NULL) ?  'g' : ' ';
 }
 
 static char get_pin_mapped_flag(struct drm_i915_gem_object *obj)
@@ -125,7 +123,7 @@ static u64 i915_gem_obj_total_ggtt_size(struct drm_i915_gem_object *obj)
        struct i915_vma *vma;
 
        list_for_each_entry(vma, &obj->vma_list, obj_link) {
-               if (vma->is_ggtt && drm_mm_node_allocated(&vma->node))
+               if (i915_vma_is_ggtt(vma) && drm_mm_node_allocated(&vma->node))
                        size += vma->node.size;
        }
 
@@ -138,6 +136,7 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
        struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
        struct intel_engine_cs *engine;
        struct i915_vma *vma;
+       unsigned int frontbuffer_bits;
        int pin_count = 0;
        enum intel_engine_id id;
 
@@ -155,30 +154,36 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
                   obj->base.write_domain);
        for_each_engine_id(engine, dev_priv, id)
                seq_printf(m, "%x ",
-                               i915_gem_request_get_seqno(obj->last_read_req[id]));
-       seq_printf(m, "] %x %x%s%s%s",
-                  i915_gem_request_get_seqno(obj->last_write_req),
-                  i915_gem_request_get_seqno(obj->last_fenced_req),
-                  i915_cache_level_str(to_i915(obj->base.dev), obj->cache_level),
+                          i915_gem_active_get_seqno(&obj->last_read[id],
+                                                    &obj->base.dev->struct_mutex));
+       seq_printf(m, "] %x %s%s%s",
+                  i915_gem_active_get_seqno(&obj->last_write,
+                                            &obj->base.dev->struct_mutex),
+                  i915_cache_level_str(dev_priv, obj->cache_level),
                   obj->dirty ? " dirty" : "",
                   obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
        if (obj->base.name)
                seq_printf(m, " (name: %d)", obj->base.name);
        list_for_each_entry(vma, &obj->vma_list, obj_link) {
-               if (vma->pin_count > 0)
+               if (i915_vma_is_pinned(vma))
                        pin_count++;
        }
        seq_printf(m, " (pinned x %d)", pin_count);
        if (obj->pin_display)
                seq_printf(m, " (display)");
-       if (obj->fence_reg != I915_FENCE_REG_NONE)
-               seq_printf(m, " (fence: %d)", obj->fence_reg);
        list_for_each_entry(vma, &obj->vma_list, obj_link) {
+               if (!drm_mm_node_allocated(&vma->node))
+                       continue;
+
                seq_printf(m, " (%sgtt offset: %08llx, size: %08llx",
-                          vma->is_ggtt ? "g" : "pp",
+                          i915_vma_is_ggtt(vma) ? "g" : "pp",
                           vma->node.start, vma->node.size);
-               if (vma->is_ggtt)
+               if (i915_vma_is_ggtt(vma))
                        seq_printf(m, ", type: %u", vma->ggtt_view.type);
+               if (vma->fence)
+                       seq_printf(m, " , fence: %d%s",
+                                  vma->fence->id,
+                                  i915_gem_active_isset(&vma->last_fence) ? "*" : "");
                seq_puts(m, ")");
        }
        if (obj->stolen)
@@ -192,58 +197,15 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
                *t = '\0';
                seq_printf(m, " (%s mappable)", s);
        }
-       if (obj->last_write_req != NULL)
-               seq_printf(m, " (%s)",
-                          i915_gem_request_get_engine(obj->last_write_req)->name);
-       if (obj->frontbuffer_bits)
-               seq_printf(m, " (frontbuffer: 0x%03x)", obj->frontbuffer_bits);
-}
-
-static int i915_gem_object_list_info(struct seq_file *m, void *data)
-{
-       struct drm_info_node *node = m->private;
-       uintptr_t list = (uintptr_t) node->info_ent->data;
-       struct list_head *head;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct i915_vma *vma;
-       u64 total_obj_size, total_gtt_size;
-       int count, ret;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
+       engine = i915_gem_active_get_engine(&obj->last_write,
+                                           &dev_priv->drm.struct_mutex);
+       if (engine)
+               seq_printf(m, " (%s)", engine->name);
 
-       /* FIXME: the user of this interface might want more than just GGTT */
-       switch (list) {
-       case ACTIVE_LIST:
-               seq_puts(m, "Active:\n");
-               head = &ggtt->base.active_list;
-               break;
-       case INACTIVE_LIST:
-               seq_puts(m, "Inactive:\n");
-               head = &ggtt->base.inactive_list;
-               break;
-       default:
-               mutex_unlock(&dev->struct_mutex);
-               return -EINVAL;
-       }
-
-       total_obj_size = total_gtt_size = count = 0;
-       list_for_each_entry(vma, head, vm_link) {
-               seq_printf(m, "   ");
-               describe_obj(m, vma->obj);
-               seq_printf(m, "\n");
-               total_obj_size += vma->obj->base.size;
-               total_gtt_size += vma->node.size;
-               count++;
-       }
-       mutex_unlock(&dev->struct_mutex);
-
-       seq_printf(m, "Total %d objects, %llu bytes, %llu GTT size\n",
-                  count, total_obj_size, total_gtt_size);
-       return 0;
+       frontbuffer_bits = atomic_read(&obj->frontbuffer_bits);
+       if (frontbuffer_bits)
+               seq_printf(m, " (frontbuffer: 0x%03x)", frontbuffer_bits);
 }
 
 static int obj_rank_by_stolen(void *priv,
@@ -263,9 +225,8 @@ static int obj_rank_by_stolen(void *priv,
 
 static int i915_gem_stolen_list_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_i915_gem_object *obj;
        u64 total_obj_size, total_gtt_size;
        LIST_HEAD(stolen);
@@ -311,17 +272,6 @@ static int i915_gem_stolen_list_info(struct seq_file *m, void *data)
        return 0;
 }
 
-#define count_objects(list, member) do { \
-       list_for_each_entry(obj, list, member) { \
-               size += i915_gem_obj_total_ggtt_size(obj); \
-               ++count; \
-               if (obj->map_and_fenceable) { \
-                       mappable_size += i915_gem_obj_ggtt_size(obj); \
-                       ++mappable_count; \
-               } \
-       } \
-} while (0)
-
 struct file_stats {
        struct drm_i915_file_private *file_priv;
        unsigned long count;
@@ -338,46 +288,29 @@ static int per_file_stats(int id, void *ptr, void *data)
 
        stats->count++;
        stats->total += obj->base.size;
-
+       if (!obj->bind_count)
+               stats->unbound += obj->base.size;
        if (obj->base.name || obj->base.dma_buf)
                stats->shared += obj->base.size;
 
-       if (USES_FULL_PPGTT(obj->base.dev)) {
-               list_for_each_entry(vma, &obj->vma_list, obj_link) {
-                       struct i915_hw_ppgtt *ppgtt;
-
-                       if (!drm_mm_node_allocated(&vma->node))
-                               continue;
+       list_for_each_entry(vma, &obj->vma_list, obj_link) {
+               if (!drm_mm_node_allocated(&vma->node))
+                       continue;
 
-                       if (vma->is_ggtt) {
-                               stats->global += obj->base.size;
-                               continue;
-                       }
+               if (i915_vma_is_ggtt(vma)) {
+                       stats->global += vma->node.size;
+               } else {
+                       struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vma->vm);
 
-                       ppgtt = container_of(vma->vm, struct i915_hw_ppgtt, base);
-                       if (ppgtt->file_priv != stats->file_priv)
+                       if (ppgtt->base.file != stats->file_priv)
                                continue;
-
-                       if (obj->active) /* XXX per-vma statistic */
-                               stats->active += obj->base.size;
-                       else
-                               stats->inactive += obj->base.size;
-
-                       return 0;
-               }
-       } else {
-               if (i915_gem_obj_ggtt_bound(obj)) {
-                       stats->global += obj->base.size;
-                       if (obj->active)
-                               stats->active += obj->base.size;
-                       else
-                               stats->inactive += obj->base.size;
-                       return 0;
                }
-       }
 
-       if (!list_empty(&obj->global_list))
-               stats->unbound += obj->base.size;
+               if (i915_vma_is_active(vma))
+                       stats->active += vma->node.size;
+               else
+                       stats->inactive += vma->node.size;
+       }
 
        return 0;
 }
@@ -424,9 +357,9 @@ static int per_file_ctx_stats(int id, void *ptr, void *data)
 
        for (n = 0; n < ARRAY_SIZE(ctx->engine); n++) {
                if (ctx->engine[n].state)
-                       per_file_stats(0, ctx->engine[n].state, data);
-               if (ctx->engine[n].ringbuf)
-                       per_file_stats(0, ctx->engine[n].ringbuf->obj, data);
+                       per_file_stats(0, ctx->engine[n].state->obj, data);
+               if (ctx->engine[n].ring)
+                       per_file_stats(0, ctx->engine[n].ring->vma->obj, data);
        }
 
        return 0;
@@ -435,48 +368,34 @@ static int per_file_ctx_stats(int id, void *ptr, void *data)
 static void print_context_stats(struct seq_file *m,
                                struct drm_i915_private *dev_priv)
 {
+       struct drm_device *dev = &dev_priv->drm;
        struct file_stats stats;
        struct drm_file *file;
 
        memset(&stats, 0, sizeof(stats));
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
+       mutex_lock(&dev->struct_mutex);
        if (dev_priv->kernel_context)
                per_file_ctx_stats(0, dev_priv->kernel_context, &stats);
 
-       list_for_each_entry(file, &dev_priv->drm.filelist, lhead) {
+       list_for_each_entry(file, &dev->filelist, lhead) {
                struct drm_i915_file_private *fpriv = file->driver_priv;
                idr_for_each(&fpriv->context_idr, per_file_ctx_stats, &stats);
        }
-       mutex_unlock(&dev_priv->drm.struct_mutex);
+       mutex_unlock(&dev->struct_mutex);
 
        print_file_stats(m, "[k]contexts", stats);
 }
 
-#define count_vmas(list, member) do { \
-       list_for_each_entry(vma, list, member) { \
-               size += i915_gem_obj_total_ggtt_size(vma->obj); \
-               ++count; \
-               if (vma->obj->map_and_fenceable) { \
-                       mappable_size += i915_gem_obj_ggtt_size(vma->obj); \
-                       ++mappable_count; \
-               } \
-       } \
-} while (0)
-
-static int i915_gem_object_info(struct seq_file *m, void* data)
+static int i915_gem_object_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       u32 count, mappable_count, purgeable_count;
-       u64 size, mappable_size, purgeable_size;
-       unsigned long pin_mapped_count = 0, pin_mapped_purgeable_count = 0;
-       u64 pin_mapped_size = 0, pin_mapped_purgeable_size = 0;
+       u32 count, mapped_count, purgeable_count, dpy_count;
+       u64 size, mapped_size, purgeable_size, dpy_size;
        struct drm_i915_gem_object *obj;
        struct drm_file *file;
-       struct i915_vma *vma;
        int ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -487,70 +406,53 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                   dev_priv->mm.object_count,
                   dev_priv->mm.object_memory);
 
-       size = count = mappable_size = mappable_count = 0;
-       count_objects(&dev_priv->mm.bound_list, global_list);
-       seq_printf(m, "%u [%u] objects, %llu [%llu] bytes in gtt\n",
-                  count, mappable_count, size, mappable_size);
-
-       size = count = mappable_size = mappable_count = 0;
-       count_vmas(&ggtt->base.active_list, vm_link);
-       seq_printf(m, "  %u [%u] active objects, %llu [%llu] bytes\n",
-                  count, mappable_count, size, mappable_size);
+       size = count = 0;
+       mapped_size = mapped_count = 0;
+       purgeable_size = purgeable_count = 0;
+       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
+               size += obj->base.size;
+               ++count;
 
-       size = count = mappable_size = mappable_count = 0;
-       count_vmas(&ggtt->base.inactive_list, vm_link);
-       seq_printf(m, "  %u [%u] inactive objects, %llu [%llu] bytes\n",
-                  count, mappable_count, size, mappable_size);
+               if (obj->madv == I915_MADV_DONTNEED) {
+                       purgeable_size += obj->base.size;
+                       ++purgeable_count;
+               }
 
-       size = count = purgeable_size = purgeable_count = 0;
-       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
-               size += obj->base.size, ++count;
-               if (obj->madv == I915_MADV_DONTNEED)
-                       purgeable_size += obj->base.size, ++purgeable_count;
                if (obj->mapping) {
-                       pin_mapped_count++;
-                       pin_mapped_size += obj->base.size;
-                       if (obj->pages_pin_count == 0) {
-                               pin_mapped_purgeable_count++;
-                               pin_mapped_purgeable_size += obj->base.size;
-                       }
+                       mapped_count++;
+                       mapped_size += obj->base.size;
                }
        }
        seq_printf(m, "%u unbound objects, %llu bytes\n", count, size);
 
-       size = count = mappable_size = mappable_count = 0;
+       size = count = dpy_size = dpy_count = 0;
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               if (obj->fault_mappable) {
-                       size += i915_gem_obj_ggtt_size(obj);
-                       ++count;
-               }
+               size += obj->base.size;
+               ++count;
+
                if (obj->pin_display) {
-                       mappable_size += i915_gem_obj_ggtt_size(obj);
-                       ++mappable_count;
+                       dpy_size += obj->base.size;
+                       ++dpy_count;
                }
+
                if (obj->madv == I915_MADV_DONTNEED) {
                        purgeable_size += obj->base.size;
                        ++purgeable_count;
                }
+
                if (obj->mapping) {
-                       pin_mapped_count++;
-                       pin_mapped_size += obj->base.size;
-                       if (obj->pages_pin_count == 0) {
-                               pin_mapped_purgeable_count++;
-                               pin_mapped_purgeable_size += obj->base.size;
-                       }
+                       mapped_count++;
+                       mapped_size += obj->base.size;
                }
        }
+       seq_printf(m, "%u bound objects, %llu bytes\n",
+                  count, size);
        seq_printf(m, "%u purgeable objects, %llu bytes\n",
                   purgeable_count, purgeable_size);
-       seq_printf(m, "%u pinned mappable objects, %llu bytes\n",
-                  mappable_count, mappable_size);
-       seq_printf(m, "%u fault mappable objects, %llu bytes\n",
-                  count, size);
-       seq_printf(m,
-                  "%lu [%lu] pin mapped objects, %llu [%llu] bytes [purgeable]\n",
-                  pin_mapped_count, pin_mapped_purgeable_count,
-                  pin_mapped_size, pin_mapped_purgeable_size);
+       seq_printf(m, "%u mapped objects, %llu bytes\n",
+                  mapped_count, mapped_size);
+       seq_printf(m, "%u display objects (pinned), %llu bytes\n",
+                  dpy_count, dpy_size);
 
        seq_printf(m, "%llu [%llu] gtt total\n",
                   ggtt->base.total, ggtt->mappable_end - ggtt->base.start);
@@ -563,6 +465,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
        print_context_stats(m, dev_priv);
        list_for_each_entry_reverse(file, &dev->filelist, lhead) {
                struct file_stats stats;
+               struct drm_i915_file_private *file_priv = file->driver_priv;
+               struct drm_i915_gem_request *request;
                struct task_struct *task;
 
                memset(&stats, 0, sizeof(stats));
@@ -576,10 +480,17 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
                 * still alive (e.g. get_pid(current) => fork() => exit()).
                 * Therefore, we need to protect this ->comm access using RCU.
                 */
+               mutex_lock(&dev->struct_mutex);
+               request = list_first_entry_or_null(&file_priv->mm.request_list,
+                                                  struct drm_i915_gem_request,
+                                                  client_list);
                rcu_read_lock();
-               task = pid_task(file->pid, PIDTYPE_PID);
+               task = pid_task(request && request->ctx->pid ?
+                               request->ctx->pid : file->pid,
+                               PIDTYPE_PID);
                print_file_stats(m, task ? task->comm : "<unknown>", stats);
                rcu_read_unlock();
+               mutex_unlock(&dev->struct_mutex);
        }
        mutex_unlock(&dev->filelist_mutex);
 
@@ -589,9 +500,9 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
 static int i915_gem_gtt_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       uintptr_t list = (uintptr_t) node->info_ent->data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(node);
+       struct drm_device *dev = &dev_priv->drm;
+       bool show_pin_display_only = !!node->info_ent->data;
        struct drm_i915_gem_object *obj;
        u64 total_obj_size, total_gtt_size;
        int count, ret;
@@ -602,7 +513,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data)
 
        total_obj_size = total_gtt_size = count = 0;
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               if (list == PINNED_LIST && !i915_gem_obj_is_pinned(obj))
+               if (show_pin_display_only && !obj->pin_display)
                        continue;
 
                seq_puts(m, "   ");
@@ -623,9 +534,8 @@ static int i915_gem_gtt_info(struct seq_file *m, void *data)
 
 static int i915_gem_pageflip_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_crtc *crtc;
        int ret;
 
@@ -672,7 +582,7 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
                                   intel_crtc_get_vblank_counter(crtc));
                        seq_printf(m, "%d prepares\n", atomic_read(&work->pending));
 
-                       if (INTEL_INFO(dev)->gen >= 4)
+                       if (INTEL_GEN(dev_priv) >= 4)
                                addr = I915_HI_DISPBASE(I915_READ(DSPSURF(crtc->plane)));
                        else
                                addr = I915_READ(DSPADDR(crtc->plane));
@@ -693,9 +603,8 @@ static int i915_gem_pageflip_info(struct seq_file *m, void *data)
 
 static int i915_gem_batch_pool_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_i915_gem_object *obj;
        struct intel_engine_cs *engine;
        int total = 0;
@@ -738,9 +647,8 @@ static int i915_gem_batch_pool_info(struct seq_file *m, void *data)
 
 static int i915_gem_request_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
        struct drm_i915_gem_request *req;
        int ret, any;
@@ -754,21 +662,20 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
                int count;
 
                count = 0;
-               list_for_each_entry(req, &engine->request_list, list)
+               list_for_each_entry(req, &engine->request_list, link)
                        count++;
                if (count == 0)
                        continue;
 
                seq_printf(m, "%s requests: %d\n", engine->name, count);
-               list_for_each_entry(req, &engine->request_list, list) {
+               list_for_each_entry(req, &engine->request_list, link) {
+                       struct pid *pid = req->ctx->pid;
                        struct task_struct *task;
 
                        rcu_read_lock();
-                       task = NULL;
-                       if (req->pid)
-                               task = pid_task(req->pid, PIDTYPE_PID);
+                       task = pid ? pid_task(pid, PIDTYPE_PID) : NULL;
                        seq_printf(m, "    %x @ %d: %s [%d]\n",
-                                  req->seqno,
+                                  req->fence.seqno,
                                   (int) (jiffies - req->emitted_jiffies),
                                   task ? task->comm : "<unknown>",
                                   task ? task->pid : -1);
@@ -793,8 +700,6 @@ static void i915_ring_seqno_info(struct seq_file *m,
 
        seq_printf(m, "Current sequence (%s): %x\n",
                   engine->name, intel_engine_get_seqno(engine));
-       seq_printf(m, "Current user interrupts (%s): %lx\n",
-                  engine->name, READ_ONCE(engine->breadcrumbs.irq_wakeups));
 
        spin_lock(&b->lock);
        for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) {
@@ -808,41 +713,25 @@ static void i915_ring_seqno_info(struct seq_file *m,
 
 static int i915_gem_seqno_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_engine_cs *engine;
-       int ret;
-
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
-       intel_runtime_pm_get(dev_priv);
 
        for_each_engine(engine, dev_priv)
                i915_ring_seqno_info(m, engine);
 
-       intel_runtime_pm_put(dev_priv);
-       mutex_unlock(&dev->struct_mutex);
-
        return 0;
 }
 
 
 static int i915_interrupt_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_engine_cs *engine;
-       int ret, i, pipe;
+       int i, pipe;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
        intel_runtime_pm_get(dev_priv);
 
-       if (IS_CHERRYVIEW(dev)) {
+       if (IS_CHERRYVIEW(dev_priv)) {
                seq_printf(m, "Master Interrupt Control:\t%08x\n",
                           I915_READ(GEN8_MASTER_IRQ));
 
@@ -881,7 +770,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                           I915_READ(GEN8_PCU_IIR));
                seq_printf(m, "PCU interrupt enable:\t%08x\n",
                           I915_READ(GEN8_PCU_IER));
-       } else if (INTEL_INFO(dev)->gen >= 8) {
+       } else if (INTEL_GEN(dev_priv) >= 8) {
                seq_printf(m, "Master Interrupt Control:\t%08x\n",
                           I915_READ(GEN8_MASTER_IRQ));
 
@@ -937,7 +826,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                           I915_READ(GEN8_PCU_IIR));
                seq_printf(m, "PCU interrupt enable:\t%08x\n",
                           I915_READ(GEN8_PCU_IER));
-       } else if (IS_VALLEYVIEW(dev)) {
+       } else if (IS_VALLEYVIEW(dev_priv)) {
                seq_printf(m, "Display IER:\t%08x\n",
                           I915_READ(VLV_IER));
                seq_printf(m, "Display IIR:\t%08x\n",
@@ -975,7 +864,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                seq_printf(m, "DPINVGTT:\t%08x\n",
                           I915_READ(DPINVGTT));
 
-       } else if (!HAS_PCH_SPLIT(dev)) {
+       } else if (!HAS_PCH_SPLIT(dev_priv)) {
                seq_printf(m, "Interrupt enable:    %08x\n",
                           I915_READ(IER));
                seq_printf(m, "Interrupt identity:  %08x\n",
@@ -1007,7 +896,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                           I915_READ(GTIMR));
        }
        for_each_engine(engine, dev_priv) {
-               if (INTEL_INFO(dev)->gen >= 6) {
+               if (INTEL_GEN(dev_priv) >= 6) {
                        seq_printf(m,
                                   "Graphics Interrupt mask (%s):       %08x\n",
                                   engine->name, I915_READ_IMR(engine));
@@ -1015,16 +904,14 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
                i915_ring_seqno_info(m, engine);
        }
        intel_runtime_pm_put(dev_priv);
-       mutex_unlock(&dev->struct_mutex);
 
        return 0;
 }
 
 static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        int i, ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -1033,14 +920,14 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
 
        seq_printf(m, "Total fences = %d\n", dev_priv->num_fence_regs);
        for (i = 0; i < dev_priv->num_fence_regs; i++) {
-               struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj;
+               struct i915_vma *vma = dev_priv->fence_regs[i].vma;
 
                seq_printf(m, "Fence %d, pin count = %d, object = ",
                           i, dev_priv->fence_regs[i].pin_count);
-               if (obj == NULL)
+               if (!vma)
                        seq_puts(m, "unused");
                else
-                       describe_obj(m, obj);
+                       describe_obj(m, vma->obj);
                seq_putc(m, '\n');
        }
 
@@ -1051,8 +938,7 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
 static int i915_hws_info(struct seq_file *m, void *data)
 {
        struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(node);
        struct intel_engine_cs *engine;
        const u32 *hws;
        int i;
@@ -1077,33 +963,25 @@ i915_error_state_write(struct file *filp,
                       loff_t *ppos)
 {
        struct i915_error_state_file_priv *error_priv = filp->private_data;
-       struct drm_device *dev = error_priv->dev;
-       int ret;
 
        DRM_DEBUG_DRIVER("Resetting error state\n");
-
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
-
-       i915_destroy_error_state(dev);
-       mutex_unlock(&dev->struct_mutex);
+       i915_destroy_error_state(error_priv->dev);
 
        return cnt;
 }
 
 static int i915_error_state_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
        struct i915_error_state_file_priv *error_priv;
 
        error_priv = kzalloc(sizeof(*error_priv), GFP_KERNEL);
        if (!error_priv)
                return -ENOMEM;
 
-       error_priv->dev = dev;
+       error_priv->dev = &dev_priv->drm;
 
-       i915_error_state_get(dev, error_priv);
+       i915_error_state_get(&dev_priv->drm, error_priv);
 
        file->private_data = error_priv;
 
@@ -1129,7 +1007,8 @@ static ssize_t i915_error_state_read(struct file *file, char __user *userbuf,
        ssize_t ret_count = 0;
        int ret;
 
-       ret = i915_error_state_buf_init(&error_str, to_i915(error_priv->dev), count, *pos);
+       ret = i915_error_state_buf_init(&error_str,
+                                       to_i915(error_priv->dev), count, *pos);
        if (ret)
                return ret;
 
@@ -1162,16 +1041,15 @@ static const struct file_operations i915_error_state_fops = {
 static int
 i915_next_seqno_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
        int ret;
 
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
+       ret = mutex_lock_interruptible(&dev_priv->drm.struct_mutex);
        if (ret)
                return ret;
 
        *val = dev_priv->next_seqno;
-       mutex_unlock(&dev->struct_mutex);
+       mutex_unlock(&dev_priv->drm.struct_mutex);
 
        return 0;
 }
@@ -1179,7 +1057,8 @@ i915_next_seqno_get(void *data, u64 *val)
 static int
 i915_next_seqno_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
+       struct drm_i915_private *dev_priv = data;
+       struct drm_device *dev = &dev_priv->drm;
        int ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -1198,16 +1077,13 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_next_seqno_fops,
 
 static int i915_frequency_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        int ret = 0;
 
        intel_runtime_pm_get(dev_priv);
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-       if (IS_GEN5(dev)) {
+       if (IS_GEN5(dev_priv)) {
                u16 rgvswctl = I915_READ16(MEMSWCTL);
                u16 rgvstat = I915_READ16(MEMSTAT_ILK);
 
@@ -1217,7 +1093,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                           MEMSTAT_VID_SHIFT);
                seq_printf(m, "Current P-state: %d\n",
                           (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
-       } else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
+       } else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
                u32 freq_sts;
 
                mutex_lock(&dev_priv->rps.hw_lock);
@@ -1244,7 +1120,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                           "efficient (RPe) frequency: %d MHz\n",
                           intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq));
                mutex_unlock(&dev_priv->rps.hw_lock);
-       } else if (INTEL_INFO(dev)->gen >= 6) {
+       } else if (INTEL_GEN(dev_priv) >= 6) {
                u32 rp_state_limits;
                u32 gt_perf_status;
                u32 rp_state_cap;
@@ -1256,7 +1132,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                int max_freq;
 
                rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
-               if (IS_BROXTON(dev)) {
+               if (IS_BROXTON(dev_priv)) {
                        rp_state_cap = I915_READ(BXT_RP_STATE_CAP);
                        gt_perf_status = I915_READ(BXT_GT_PERF_STATUS);
                } else {
@@ -1272,11 +1148,11 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
                reqf = I915_READ(GEN6_RPNSWREQ);
-               if (IS_GEN9(dev))
+               if (IS_GEN9(dev_priv))
                        reqf >>= 23;
                else {
                        reqf &= ~GEN6_TURBO_DISABLE;
-                       if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+                       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
                                reqf >>= 24;
                        else
                                reqf >>= 25;
@@ -1294,9 +1170,9 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                rpdownei = I915_READ(GEN6_RP_CUR_DOWN_EI) & GEN6_CURIAVG_MASK;
                rpcurdown = I915_READ(GEN6_RP_CUR_DOWN) & GEN6_CURBSYTAVG_MASK;
                rpprevdown = I915_READ(GEN6_RP_PREV_DOWN) & GEN6_CURBSYTAVG_MASK;
-               if (IS_GEN9(dev))
+               if (IS_GEN9(dev_priv))
                        cagf = (rpstat & GEN9_CAGF_MASK) >> GEN9_CAGF_SHIFT;
-               else if (IS_HASWELL(dev) || IS_BROADWELL(dev))
+               else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
                        cagf = (rpstat & HSW_CAGF_MASK) >> HSW_CAGF_SHIFT;
                else
                        cagf = (rpstat & GEN6_CAGF_MASK) >> GEN6_CAGF_SHIFT;
@@ -1305,7 +1181,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
                mutex_unlock(&dev->struct_mutex);
 
-               if (IS_GEN6(dev) || IS_GEN7(dev)) {
+               if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) {
                        pm_ier = I915_READ(GEN6_PMIER);
                        pm_imr = I915_READ(GEN6_PMIMR);
                        pm_isr = I915_READ(GEN6_PMISR);
@@ -1323,7 +1199,7 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                seq_printf(m, "pm_intr_keep: 0x%08x\n", dev_priv->rps.pm_intr_keep);
                seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
                seq_printf(m, "Render p-state ratio: %d\n",
-                          (gt_perf_status & (IS_GEN9(dev) ? 0x1ff00 : 0xff00)) >> 8);
+                          (gt_perf_status & (IS_GEN9(dev_priv) ? 0x1ff00 : 0xff00)) >> 8);
                seq_printf(m, "Render p-state VID: %d\n",
                           gt_perf_status & 0xff);
                seq_printf(m, "Render p-state limit: %d\n",
@@ -1352,22 +1228,22 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                seq_printf(m, "Down threshold: %d%%\n",
                           dev_priv->rps.down_threshold);
 
-               max_freq = (IS_BROXTON(dev) ? rp_state_cap >> 0 :
+               max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 0 :
                            rp_state_cap >> 16) & 0xff;
-               max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ?
+               max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ?
                             GEN9_FREQ_SCALER : 1);
                seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
                           intel_gpu_freq(dev_priv, max_freq));
 
                max_freq = (rp_state_cap & 0xff00) >> 8;
-               max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ?
+               max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ?
                             GEN9_FREQ_SCALER : 1);
                seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
                           intel_gpu_freq(dev_priv, max_freq));
 
-               max_freq = (IS_BROXTON(dev) ? rp_state_cap >> 16 :
+               max_freq = (IS_BROXTON(dev_priv) ? rp_state_cap >> 16 :
                            rp_state_cap >> 0) & 0xff;
-               max_freq *= (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ?
+               max_freq *= (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ?
                             GEN9_FREQ_SCALER : 1);
                seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
                           intel_gpu_freq(dev_priv, max_freq));
@@ -1381,6 +1257,8 @@ static int i915_frequency_info(struct seq_file *m, void *unused)
                           intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq));
                seq_printf(m, "Min freq: %d MHz\n",
                           intel_gpu_freq(dev_priv, dev_priv->rps.min_freq));
+               seq_printf(m, "Boost freq: %d MHz\n",
+                          intel_gpu_freq(dev_priv, dev_priv->rps.boost_freq));
                seq_printf(m, "Max freq: %d MHz\n",
                           intel_gpu_freq(dev_priv, dev_priv->rps.max_freq));
                seq_printf(m,
@@ -1401,9 +1279,7 @@ out:
 
 static int i915_hangcheck_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_engine_cs *engine;
        u64 acthd[I915_NUM_ENGINES];
        u32 seqno[I915_NUM_ENGINES];
@@ -1411,6 +1287,15 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
        enum intel_engine_id id;
        int j;
 
+       if (test_bit(I915_WEDGED, &dev_priv->gpu_error.flags))
+               seq_printf(m, "Wedged\n");
+       if (test_bit(I915_RESET_IN_PROGRESS, &dev_priv->gpu_error.flags))
+               seq_printf(m, "Reset in progress\n");
+       if (waitqueue_active(&dev_priv->gpu_error.wait_queue))
+               seq_printf(m, "Waiter holding struct mutex\n");
+       if (waitqueue_active(&dev_priv->gpu_error.reset_queue))
+               seq_printf(m, "struct_mutex blocked for reset\n");
+
        if (!i915.enable_hangcheck) {
                seq_printf(m, "Hangcheck disabled\n");
                return 0;
@@ -1419,7 +1304,7 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
        intel_runtime_pm_get(dev_priv);
 
        for_each_engine_id(engine, dev_priv, id) {
-               acthd[id] = intel_ring_get_active_head(engine);
+               acthd[id] = intel_engine_get_active_head(engine);
                seqno[id] = intel_engine_get_seqno(engine);
        }
 
@@ -1440,11 +1325,10 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
                           engine->hangcheck.seqno,
                           seqno[id],
                           engine->last_submitted_seqno);
-               seq_printf(m, "\twaiters? %d\n",
-                          intel_engine_has_waiter(engine));
-               seq_printf(m, "\tuser interrupts = %lx [current %lx]\n",
-                          engine->hangcheck.user_interrupts,
-                          READ_ONCE(engine->breadcrumbs.irq_wakeups));
+               seq_printf(m, "\twaiters? %s, fake irq active? %s\n",
+                          yesno(intel_engine_has_waiter(engine)),
+                          yesno(test_bit(engine->id,
+                                         &dev_priv->gpu_error.missed_irq_rings)));
                seq_printf(m, "\tACTHD = 0x%08llx [current 0x%08llx]\n",
                           (long long)engine->hangcheck.acthd,
                           (long long)acthd[id]);
@@ -1472,9 +1356,8 @@ static int i915_hangcheck_info(struct seq_file *m, void *unused)
 
 static int ironlake_drpc_info(struct seq_file *m)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        u32 rgvmodectl, rstdbyctl;
        u16 crstandvid;
        int ret;
@@ -1540,9 +1423,7 @@ static int ironlake_drpc_info(struct seq_file *m)
 
 static int i915_forcewake_domains(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_uncore_forcewake_domain *fw_domain;
 
        spin_lock_irq(&dev_priv->uncore.lock);
@@ -1558,9 +1439,7 @@ static int i915_forcewake_domains(struct seq_file *m, void *data)
 
 static int vlv_drpc_info(struct seq_file *m)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        u32 rpmodectl1, rcctl1, pw_status;
 
        intel_runtime_pm_get(dev_priv);
@@ -1598,10 +1477,10 @@ static int vlv_drpc_info(struct seq_file *m)
 
 static int gen6_drpc_info(struct seq_file *m)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        u32 rpmodectl1, gt_core_status, rcctl1, rc6vids = 0;
+       u32 gen9_powergate_enable = 0, gen9_powergate_status = 0;
        unsigned forcewake_count;
        int count = 0, ret;
 
@@ -1629,6 +1508,10 @@ static int gen6_drpc_info(struct seq_file *m)
 
        rpmodectl1 = I915_READ(GEN6_RP_CONTROL);
        rcctl1 = I915_READ(GEN6_RC_CONTROL);
+       if (INTEL_GEN(dev_priv) >= 9) {
+               gen9_powergate_enable = I915_READ(GEN9_PG_ENABLE);
+               gen9_powergate_status = I915_READ(GEN9_PWRGT_DOMAIN_STATUS);
+       }
        mutex_unlock(&dev->struct_mutex);
        mutex_lock(&dev_priv->rps.hw_lock);
        sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids);
@@ -1647,6 +1530,12 @@ static int gen6_drpc_info(struct seq_file *m)
                   yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE));
        seq_printf(m, "RC6 Enabled: %s\n",
                   yesno(rcctl1 & GEN6_RC_CTL_RC6_ENABLE));
+       if (INTEL_GEN(dev_priv) >= 9) {
+               seq_printf(m, "Render Well Gating Enabled: %s\n",
+                       yesno(gen9_powergate_enable & GEN9_RENDER_PG_ENABLE));
+               seq_printf(m, "Media Well Gating Enabled: %s\n",
+                       yesno(gen9_powergate_enable & GEN9_MEDIA_PG_ENABLE));
+       }
        seq_printf(m, "Deep RC6 Enabled: %s\n",
                   yesno(rcctl1 & GEN6_RC_CTL_RC6p_ENABLE));
        seq_printf(m, "Deepest RC6 Enabled: %s\n",
@@ -1675,6 +1564,14 @@ static int gen6_drpc_info(struct seq_file *m)
 
        seq_printf(m, "Core Power Down: %s\n",
                   yesno(gt_core_status & GEN6_CORE_CPD_STATE_MASK));
+       if (INTEL_GEN(dev_priv) >= 9) {
+               seq_printf(m, "Render Power Well: %s\n",
+                       (gen9_powergate_status &
+                        GEN9_PWRGT_RENDER_STATUS_MASK) ? "Up" : "Down");
+               seq_printf(m, "Media Power Well: %s\n",
+                       (gen9_powergate_status &
+                        GEN9_PWRGT_MEDIA_STATUS_MASK) ? "Up" : "Down");
+       }
 
        /* Not exactly sure what this is */
        seq_printf(m, "RC6 \"Locked to RPn\" residency since boot: %u\n",
@@ -1692,17 +1589,16 @@ static int gen6_drpc_info(struct seq_file *m)
                   GEN6_DECODE_RC6_VID(((rc6vids >> 8) & 0xff)));
        seq_printf(m, "RC6++ voltage: %dmV\n",
                   GEN6_DECODE_RC6_VID(((rc6vids >> 16) & 0xff)));
-       return 0;
+       return i915_forcewake_domains(m, NULL);
 }
 
 static int i915_drpc_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
 
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                return vlv_drpc_info(m);
-       else if (INTEL_INFO(dev)->gen >= 6)
+       else if (INTEL_GEN(dev_priv) >= 6)
                return gen6_drpc_info(m);
        else
                return ironlake_drpc_info(m);
@@ -1710,9 +1606,7 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
 
 static int i915_frontbuffer_tracking(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
 
        seq_printf(m, "FB tracking busy bits: 0x%08x\n",
                   dev_priv->fb_tracking.busy_bits);
@@ -1725,11 +1619,9 @@ static int i915_frontbuffer_tracking(struct seq_file *m, void *unused)
 
 static int i915_fbc_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
 
-       if (!HAS_FBC(dev)) {
+       if (!HAS_FBC(dev_priv)) {
                seq_puts(m, "FBC unsupported on this chipset\n");
                return 0;
        }
@@ -1743,7 +1635,7 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
                seq_printf(m, "FBC disabled: %s\n",
                           dev_priv->fbc.no_fbc_reason);
 
-       if (INTEL_INFO(dev_priv)->gen >= 7)
+       if (INTEL_GEN(dev_priv) >= 7)
                seq_printf(m, "Compressing: %s\n",
                           yesno(I915_READ(FBC_STATUS2) &
                                 FBC_COMPRESSION_MASK));
@@ -1756,10 +1648,9 @@ static int i915_fbc_status(struct seq_file *m, void *unused)
 
 static int i915_fbc_fc_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
-       if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev))
+       if (INTEL_GEN(dev_priv) < 7 || !HAS_FBC(dev_priv))
                return -ENODEV;
 
        *val = dev_priv->fbc.false_color;
@@ -1769,11 +1660,10 @@ static int i915_fbc_fc_get(void *data, u64 *val)
 
 static int i915_fbc_fc_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
        u32 reg;
 
-       if (INTEL_INFO(dev)->gen < 7 || !HAS_FBC(dev))
+       if (INTEL_GEN(dev_priv) < 7 || !HAS_FBC(dev_priv))
                return -ENODEV;
 
        mutex_lock(&dev_priv->fbc.lock);
@@ -1795,11 +1685,9 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_fbc_fc_fops,
 
 static int i915_ips_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
 
-       if (!HAS_IPS(dev)) {
+       if (!HAS_IPS(dev_priv)) {
                seq_puts(m, "not supported\n");
                return 0;
        }
@@ -1809,7 +1697,7 @@ static int i915_ips_status(struct seq_file *m, void *unused)
        seq_printf(m, "Enabled by kernel parameter: %s\n",
                   yesno(i915.enable_ips));
 
-       if (INTEL_INFO(dev)->gen >= 8) {
+       if (INTEL_GEN(dev_priv) >= 8) {
                seq_puts(m, "Currently: unknown\n");
        } else {
                if (I915_READ(IPS_CTL) & IPS_ENABLE)
@@ -1825,23 +1713,21 @@ static int i915_ips_status(struct seq_file *m, void *unused)
 
 static int i915_sr_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        bool sr_enabled = false;
 
        intel_runtime_pm_get(dev_priv);
 
-       if (HAS_PCH_SPLIT(dev))
+       if (HAS_PCH_SPLIT(dev_priv))
                sr_enabled = I915_READ(WM1_LP_ILK) & WM1_LP_SR_EN;
-       else if (IS_CRESTLINE(dev) || IS_G4X(dev) ||
-                IS_I945G(dev) || IS_I945GM(dev))
+       else if (IS_CRESTLINE(dev_priv) || IS_G4X(dev_priv) ||
+                IS_I945G(dev_priv) || IS_I945GM(dev_priv))
                sr_enabled = I915_READ(FW_BLC_SELF) & FW_BLC_SELF_EN;
-       else if (IS_I915GM(dev))
+       else if (IS_I915GM(dev_priv))
                sr_enabled = I915_READ(INSTPM) & INSTPM_SELF_EN;
-       else if (IS_PINEVIEW(dev))
+       else if (IS_PINEVIEW(dev_priv))
                sr_enabled = I915_READ(DSPFW3) & PINEVIEW_SELF_REFRESH_EN;
-       else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+       else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                sr_enabled = I915_READ(FW_BLC_SELF_VLV) & FW_CSPWRDWNEN;
 
        intel_runtime_pm_put(dev_priv);
@@ -1854,13 +1740,12 @@ static int i915_sr_status(struct seq_file *m, void *unused)
 
 static int i915_emon_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        unsigned long temp, chipset, gfx;
        int ret;
 
-       if (!IS_GEN5(dev))
+       if (!IS_GEN5(dev_priv))
                return -ENODEV;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -1882,27 +1767,23 @@ static int i915_emon_status(struct seq_file *m, void *unused)
 
 static int i915_ring_freq_table(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        int ret = 0;
        int gpu_freq, ia_freq;
        unsigned int max_gpu_freq, min_gpu_freq;
 
-       if (!HAS_CORE_RING_FREQ(dev)) {
+       if (!HAS_LLC(dev_priv)) {
                seq_puts(m, "unsupported on this chipset\n");
                return 0;
        }
 
        intel_runtime_pm_get(dev_priv);
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
        if (ret)
                goto out;
 
-       if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
+       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
                /* Convert GT frequency to 50 HZ units */
                min_gpu_freq =
                        dev_priv->rps.min_freq_softlimit / GEN9_FREQ_SCALER;
@@ -1922,7 +1803,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
                                       &ia_freq);
                seq_printf(m, "%d\t\t%d\t\t\t\t%d\n",
                           intel_gpu_freq(dev_priv, (gpu_freq *
-                               (IS_SKYLAKE(dev) || IS_KABYLAKE(dev) ?
+                               (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv) ?
                                 GEN9_FREQ_SCALER : 1))),
                           ((ia_freq >> 0) & 0xff) * 100,
                           ((ia_freq >> 8) & 0xff) * 100);
@@ -1937,9 +1818,8 @@ out:
 
 static int i915_opregion(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_opregion *opregion = &dev_priv->opregion;
        int ret;
 
@@ -1958,10 +1838,7 @@ out:
 
 static int i915_vbt(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_opregion *opregion = &dev_priv->opregion;
+       struct intel_opregion *opregion = &node_to_i915(m->private)->opregion;
 
        if (opregion->vbt)
                seq_write(m, opregion->vbt, opregion->vbt_size);
@@ -1971,8 +1848,8 @@ static int i915_vbt(struct seq_file *m, void *unused)
 
 static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_framebuffer *fbdev_fb = NULL;
        struct drm_framebuffer *drm_fb;
        int ret;
@@ -1982,8 +1859,8 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
                return ret;
 
 #ifdef CONFIG_DRM_FBDEV_EMULATION
-       if (to_i915(dev)->fbdev) {
-               fbdev_fb = to_intel_framebuffer(to_i915(dev)->fbdev->helper.fb);
+       if (dev_priv->fbdev) {
+               fbdev_fb = to_intel_framebuffer(dev_priv->fbdev->helper.fb);
 
                seq_printf(m, "fbcon size: %d x %d, depth %d, %d bpp, modifier 0x%llx, refcount %d, obj ",
                           fbdev_fb->base.width,
@@ -2019,19 +1896,17 @@ static int i915_gem_framebuffer_info(struct seq_file *m, void *data)
        return 0;
 }
 
-static void describe_ctx_ringbuf(struct seq_file *m,
-                                struct intel_ringbuffer *ringbuf)
+static void describe_ctx_ring(struct seq_file *m, struct intel_ring *ring)
 {
        seq_printf(m, " (ringbuffer, space: %d, head: %u, tail: %u, last head: %d)",
-                  ringbuf->space, ringbuf->head, ringbuf->tail,
-                  ringbuf->last_retired_head);
+                  ring->space, ring->head, ring->tail,
+                  ring->last_retired_head);
 }
 
 static int i915_context_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
        struct i915_gem_context *ctx;
        int ret;
@@ -2042,18 +1917,17 @@ static int i915_context_status(struct seq_file *m, void *unused)
 
        list_for_each_entry(ctx, &dev_priv->context_list, link) {
                seq_printf(m, "HW context %u ", ctx->hw_id);
-               if (IS_ERR(ctx->file_priv)) {
-                       seq_puts(m, "(deleted) ");
-               } else if (ctx->file_priv) {
-                       struct pid *pid = ctx->file_priv->file->pid;
+               if (ctx->pid) {
                        struct task_struct *task;
 
-                       task = get_pid_task(pid, PIDTYPE_PID);
+                       task = get_pid_task(ctx->pid, PIDTYPE_PID);
                        if (task) {
                                seq_printf(m, "(%s [%d]) ",
                                           task->comm, task->pid);
                                put_task_struct(task);
                        }
+               } else if (IS_ERR(ctx->file_priv)) {
+                       seq_puts(m, "(deleted) ");
                } else {
                        seq_puts(m, "(kernel) ");
                }
@@ -2067,9 +1941,9 @@ static int i915_context_status(struct seq_file *m, void *unused)
                        seq_printf(m, "%s: ", engine->name);
                        seq_putc(m, ce->initialised ? 'I' : 'i');
                        if (ce->state)
-                               describe_obj(m, ce->state);
-                       if (ce->ringbuf)
-                               describe_ctx_ringbuf(m, ce->ringbuf);
+                               describe_obj(m, ce->state->obj);
+                       if (ce->ring)
+                               describe_ctx_ring(m, ce->ring);
                        seq_putc(m, '\n');
                }
 
@@ -2085,36 +1959,34 @@ static void i915_dump_lrc_obj(struct seq_file *m,
                              struct i915_gem_context *ctx,
                              struct intel_engine_cs *engine)
 {
-       struct drm_i915_gem_object *ctx_obj = ctx->engine[engine->id].state;
+       struct i915_vma *vma = ctx->engine[engine->id].state;
        struct page *page;
-       uint32_t *reg_state;
        int j;
-       unsigned long ggtt_offset = 0;
 
        seq_printf(m, "CONTEXT: %s %u\n", engine->name, ctx->hw_id);
 
-       if (ctx_obj == NULL) {
-               seq_puts(m, "\tNot allocated\n");
+       if (!vma) {
+               seq_puts(m, "\tFake context\n");
                return;
        }
 
-       if (!i915_gem_obj_ggtt_bound(ctx_obj))
-               seq_puts(m, "\tNot bound in GGTT\n");
-       else
-               ggtt_offset = i915_gem_obj_ggtt_offset(ctx_obj);
+       if (vma->flags & I915_VMA_GLOBAL_BIND)
+               seq_printf(m, "\tBound in GGTT at 0x%08x\n",
+                          i915_ggtt_offset(vma));
 
-       if (i915_gem_object_get_pages(ctx_obj)) {
-               seq_puts(m, "\tFailed to get pages for context object\n");
+       if (i915_gem_object_get_pages(vma->obj)) {
+               seq_puts(m, "\tFailed to get pages for context object\n\n");
                return;
        }
 
-       page = i915_gem_object_get_page(ctx_obj, LRC_STATE_PN);
-       if (!WARN_ON(page == NULL)) {
-               reg_state = kmap_atomic(page);
+       page = i915_gem_object_get_page(vma->obj, LRC_STATE_PN);
+       if (page) {
+               u32 *reg_state = kmap_atomic(page);
 
                for (j = 0; j < 0x600 / sizeof(u32) / 4; j += 4) {
-                       seq_printf(m, "\t[0x%08lx] 0x%08x 0x%08x 0x%08x 0x%08x\n",
-                                  ggtt_offset + 4096 + (j * 4),
+                       seq_printf(m,
+                                  "\t[0x%04x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                                  j * 4,
                                   reg_state[j], reg_state[j + 1],
                                   reg_state[j + 2], reg_state[j + 3]);
                }
@@ -2126,9 +1998,8 @@ static void i915_dump_lrc_obj(struct seq_file *m,
 
 static int i915_dump_lrc(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
        struct i915_gem_context *ctx;
        int ret;
@@ -2153,9 +2024,8 @@ static int i915_dump_lrc(struct seq_file *m, void *unused)
 
 static int i915_execlists(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = (struct drm_info_node *)m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
        u32 status_pointer;
        u8 read_pointer;
@@ -2190,7 +2060,7 @@ static int i915_execlists(struct seq_file *m, void *data)
                status_pointer = I915_READ(RING_CONTEXT_STATUS_PTR(engine));
                seq_printf(m, "\tStatus pointer: 0x%08X\n", status_pointer);
 
-               read_pointer = engine->next_context_status_buffer;
+               read_pointer = GEN8_CSB_READ_PTR(status_pointer);
                write_pointer = GEN8_CSB_WRITE_PTR(status_pointer);
                if (read_pointer > write_pointer)
                        write_pointer += GEN8_CSB_ENTRIES;
@@ -2256,9 +2126,8 @@ static const char *swizzle_string(unsigned swizzle)
 
 static int i915_swizzle_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        int ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -2271,7 +2140,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
        seq_printf(m, "bit6 swizzle for Y-tiling = %s\n",
                   swizzle_string(dev_priv->mm.bit_6_swizzle_y));
 
-       if (IS_GEN3(dev) || IS_GEN4(dev)) {
+       if (IS_GEN3(dev_priv) || IS_GEN4(dev_priv)) {
                seq_printf(m, "DDC = 0x%08x\n",
                           I915_READ(DCC));
                seq_printf(m, "DDC2 = 0x%08x\n",
@@ -2280,7 +2149,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
                           I915_READ16(C0DRB3));
                seq_printf(m, "C1DRB3 = 0x%04x\n",
                           I915_READ16(C1DRB3));
-       } else if (INTEL_INFO(dev)->gen >= 6) {
+       } else if (INTEL_GEN(dev_priv) >= 6) {
                seq_printf(m, "MAD_DIMM_C0 = 0x%08x\n",
                           I915_READ(MAD_DIMM_C0));
                seq_printf(m, "MAD_DIMM_C1 = 0x%08x\n",
@@ -2289,7 +2158,7 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
                           I915_READ(MAD_DIMM_C2));
                seq_printf(m, "TILECTL = 0x%08x\n",
                           I915_READ(TILECTL));
-               if (INTEL_INFO(dev)->gen >= 8)
+               if (INTEL_GEN(dev_priv) >= 8)
                        seq_printf(m, "GAMTARBMODE = 0x%08x\n",
                                   I915_READ(GAMTARBMODE));
                else
@@ -2329,9 +2198,9 @@ static int per_file_ctx(int id, void *ptr, void *data)
        return 0;
 }
 
-static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
+static void gen8_ppgtt_info(struct seq_file *m,
+                           struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_engine_cs *engine;
        struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
        int i;
@@ -2350,9 +2219,9 @@ static void gen8_ppgtt_info(struct seq_file *m, struct drm_device *dev)
        }
 }
 
-static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
+static void gen6_ppgtt_info(struct seq_file *m,
+                           struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_engine_cs *engine;
 
        if (IS_GEN6(dev_priv))
@@ -2384,22 +2253,23 @@ static void gen6_ppgtt_info(struct seq_file *m, struct drm_device *dev)
 
 static int i915_ppgtt_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_file *file;
+       int ret;
 
-       int ret = mutex_lock_interruptible(&dev->struct_mutex);
+       mutex_lock(&dev->filelist_mutex);
+       ret = mutex_lock_interruptible(&dev->struct_mutex);
        if (ret)
-               return ret;
+               goto out_unlock;
+
        intel_runtime_pm_get(dev_priv);
 
-       if (INTEL_INFO(dev)->gen >= 8)
-               gen8_ppgtt_info(m, dev);
-       else if (INTEL_INFO(dev)->gen >= 6)
-               gen6_ppgtt_info(m, dev);
+       if (INTEL_GEN(dev_priv) >= 8)
+               gen8_ppgtt_info(m, dev_priv);
+       else if (INTEL_GEN(dev_priv) >= 6)
+               gen6_ppgtt_info(m, dev_priv);
 
-       mutex_lock(&dev->filelist_mutex);
        list_for_each_entry_reverse(file, &dev->filelist, lhead) {
                struct drm_i915_file_private *file_priv = file->driver_priv;
                struct task_struct *task;
@@ -2407,19 +2277,19 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
                task = get_pid_task(file->pid, PIDTYPE_PID);
                if (!task) {
                        ret = -ESRCH;
-                       goto out_unlock;
+                       goto out_rpm;
                }
                seq_printf(m, "\nproc: %s\n", task->comm);
                put_task_struct(task);
                idr_for_each(&file_priv->context_idr, per_file_ctx,
                             (void *)(unsigned long)m);
        }
-out_unlock:
-       mutex_unlock(&dev->filelist_mutex);
 
+out_rpm:
        intel_runtime_pm_put(dev_priv);
        mutex_unlock(&dev->struct_mutex);
-
+out_unlock:
+       mutex_unlock(&dev->filelist_mutex);
        return ret;
 }
 
@@ -2434,23 +2304,41 @@ static int count_irq_waiters(struct drm_i915_private *i915)
        return count;
 }
 
+static const char *rps_power_to_str(unsigned int power)
+{
+       static const char * const strings[] = {
+               [LOW_POWER] = "low power",
+               [BETWEEN] = "mixed",
+               [HIGH_POWER] = "high power",
+       };
+
+       if (power >= ARRAY_SIZE(strings) || !strings[power])
+               return "unknown";
+
+       return strings[power];
+}
+
 static int i915_rps_boost_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_file *file;
 
        seq_printf(m, "RPS enabled? %d\n", dev_priv->rps.enabled);
        seq_printf(m, "GPU busy? %s [%x]\n",
                   yesno(dev_priv->gt.awake), dev_priv->gt.active_engines);
        seq_printf(m, "CPU waiting? %d\n", count_irq_waiters(dev_priv));
-       seq_printf(m, "Frequency requested %d; min hard:%d, soft:%d; max soft:%d, hard:%d\n",
-                  intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
+       seq_printf(m, "Frequency requested %d\n",
+                  intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq));
+       seq_printf(m, "  min hard:%d, soft:%d; max soft:%d, hard:%d\n",
                   intel_gpu_freq(dev_priv, dev_priv->rps.min_freq),
                   intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit),
                   intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit),
                   intel_gpu_freq(dev_priv, dev_priv->rps.max_freq));
+       seq_printf(m, "  idle:%d, efficient:%d, boost:%d\n",
+                  intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq),
+                  intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq),
+                  intel_gpu_freq(dev_priv, dev_priv->rps.boost_freq));
 
        mutex_lock(&dev->filelist_mutex);
        spin_lock(&dev_priv->rps.client_lock);
@@ -2467,27 +2355,44 @@ static int i915_rps_boost_info(struct seq_file *m, void *data)
                           list_empty(&file_priv->rps.link) ? "" : ", active");
                rcu_read_unlock();
        }
-       seq_printf(m, "Semaphore boosts: %d%s\n",
-                  dev_priv->rps.semaphores.boosts,
-                  list_empty(&dev_priv->rps.semaphores.link) ? "" : ", active");
-       seq_printf(m, "MMIO flip boosts: %d%s\n",
-                  dev_priv->rps.mmioflips.boosts,
-                  list_empty(&dev_priv->rps.mmioflips.link) ? "" : ", active");
-       seq_printf(m, "Kernel boosts: %d\n", dev_priv->rps.boosts);
+       seq_printf(m, "Kernel (anonymous) boosts: %d\n", dev_priv->rps.boosts);
        spin_unlock(&dev_priv->rps.client_lock);
        mutex_unlock(&dev->filelist_mutex);
 
+       if (INTEL_GEN(dev_priv) >= 6 &&
+           dev_priv->rps.enabled &&
+           dev_priv->gt.active_engines) {
+               u32 rpup, rpupei;
+               u32 rpdown, rpdownei;
+
+               intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
+               rpup = I915_READ_FW(GEN6_RP_CUR_UP) & GEN6_RP_EI_MASK;
+               rpupei = I915_READ_FW(GEN6_RP_CUR_UP_EI) & GEN6_RP_EI_MASK;
+               rpdown = I915_READ_FW(GEN6_RP_CUR_DOWN) & GEN6_RP_EI_MASK;
+               rpdownei = I915_READ_FW(GEN6_RP_CUR_DOWN_EI) & GEN6_RP_EI_MASK;
+               intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
+
+               seq_printf(m, "\nRPS Autotuning (current \"%s\" window):\n",
+                          rps_power_to_str(dev_priv->rps.power));
+               seq_printf(m, "  Avg. up: %d%% [above threshold? %d%%]\n",
+                          100 * rpup / rpupei,
+                          dev_priv->rps.up_threshold);
+               seq_printf(m, "  Avg. down: %d%% [below threshold? %d%%]\n",
+                          100 * rpdown / rpdownei,
+                          dev_priv->rps.down_threshold);
+       } else {
+               seq_puts(m, "\nRPS Autotuning inactive\n");
+       }
+
        return 0;
 }
 
 static int i915_llc(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        const bool edram = INTEL_GEN(dev_priv) > 8;
 
-       seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev)));
+       seq_printf(m, "LLC: %s\n", yesno(HAS_LLC(dev_priv)));
        seq_printf(m, "%s: %lluMB\n", edram ? "eDRAM" : "eLLC",
                   intel_uncore_edram_size(dev_priv)/1024/1024);
 
@@ -2496,8 +2401,7 @@ static int i915_llc(struct seq_file *m, void *data)
 
 static int i915_guc_load_status_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_i915_private *dev_priv = to_i915(node->minor->dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
        u32 tmp, i;
 
@@ -2543,6 +2447,7 @@ static void i915_guc_client_info(struct seq_file *m,
                                 struct i915_guc_client *client)
 {
        struct intel_engine_cs *engine;
+       enum intel_engine_id id;
        uint64_t tot = 0;
 
        seq_printf(m, "\tPriority %d, GuC ctx index: %u, PD offset 0x%x\n",
@@ -2553,27 +2458,26 @@ static void i915_guc_client_info(struct seq_file *m,
                client->wq_size, client->wq_offset, client->wq_tail);
 
        seq_printf(m, "\tWork queue full: %u\n", client->no_wq_space);
-       seq_printf(m, "\tFailed to queue: %u\n", client->q_fail);
        seq_printf(m, "\tFailed doorbell: %u\n", client->b_fail);
        seq_printf(m, "\tLast submission result: %d\n", client->retcode);
 
-       for_each_engine(engine, dev_priv) {
+       for_each_engine_id(engine, dev_priv, id) {
+               u64 submissions = client->submissions[id];
+               tot += submissions;
                seq_printf(m, "\tSubmissions: %llu %s\n",
-                               client->submissions[engine->id],
-                               engine->name);
-               tot += client->submissions[engine->id];
+                               submissions, engine->name);
        }
        seq_printf(m, "\tTotal: %llu\n", tot);
 }
 
 static int i915_guc_info(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_guc guc;
        struct i915_guc_client client = {};
        struct intel_engine_cs *engine;
+       enum intel_engine_id id;
        u64 total = 0;
 
        if (!HAS_GUC_SCHED(dev_priv))
@@ -2600,11 +2504,11 @@ static int i915_guc_info(struct seq_file *m, void *data)
        seq_printf(m, "GuC last action error code: %d\n", guc.action_err);
 
        seq_printf(m, "\nGuC submissions:\n");
-       for_each_engine(engine, dev_priv) {
+       for_each_engine_id(engine, dev_priv, id) {
+               u64 submissions = guc.submissions[id];
+               total += submissions;
                seq_printf(m, "\t%-24s: %10llu, last seqno 0x%08x\n",
-                       engine->name, guc.submissions[engine->id],
-                       guc.last_seqno[engine->id]);
-               total += guc.submissions[engine->id];
+                       engine->name, submissions, guc.last_seqno[id]);
        }
        seq_printf(m, "\t%s: %llu\n", "Total", total);
 
@@ -2618,18 +2522,16 @@ static int i915_guc_info(struct seq_file *m, void *data)
 
 static int i915_guc_log_dump(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct drm_i915_gem_object *log_obj = dev_priv->guc.log_obj;
-       u32 *log;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_i915_gem_object *obj;
        int i = 0, pg;
 
-       if (!log_obj)
+       if (!dev_priv->guc.log_vma)
                return 0;
 
-       for (pg = 0; pg < log_obj->base.size / PAGE_SIZE; pg++) {
-               log = kmap_atomic(i915_gem_object_get_page(log_obj, pg));
+       obj = dev_priv->guc.log_vma->obj;
+       for (pg = 0; pg < obj->base.size / PAGE_SIZE; pg++) {
+               u32 *log = kmap_atomic(i915_gem_object_get_page(obj, pg));
 
                for (i = 0; i < PAGE_SIZE / sizeof(u32); i += 4)
                        seq_printf(m, "0x%08x 0x%08x 0x%08x 0x%08x\n",
@@ -2646,15 +2548,13 @@ static int i915_guc_log_dump(struct seq_file *m, void *data)
 
 static int i915_edp_psr_status(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        u32 psrperf = 0;
        u32 stat[3];
        enum pipe pipe;
        bool enabled = false;
 
-       if (!HAS_PSR(dev)) {
+       if (!HAS_PSR(dev_priv)) {
                seq_puts(m, "PSR not supported\n");
                return 0;
        }
@@ -2671,7 +2571,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
        seq_printf(m, "Re-enable work scheduled: %s\n",
                   yesno(work_busy(&dev_priv->psr.work.work)));
 
-       if (HAS_DDI(dev))
+       if (HAS_DDI(dev_priv))
                enabled = I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
        else {
                for_each_pipe(dev_priv, pipe) {
@@ -2688,7 +2588,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
 
        seq_printf(m, "HW Enabled & Active bit: %s", yesno(enabled));
 
-       if (!HAS_DDI(dev))
+       if (!HAS_DDI(dev_priv))
                for_each_pipe(dev_priv, pipe) {
                        if ((stat[pipe] == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
                            (stat[pipe] == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
@@ -2700,7 +2600,7 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
         * VLV/CHV PSR has no kind of performance counter
         * SKL+ Perf counter is reset to 0 everytime DC state is entered
         */
-       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
                psrperf = I915_READ(EDP_PSR_PERF_CNT) &
                        EDP_PSR_PERF_CNT_MASK;
 
@@ -2714,8 +2614,8 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
 
 static int i915_sink_crc(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_connector *connector;
        struct intel_dp *intel_dp = NULL;
        int ret;
@@ -2754,13 +2654,11 @@ out:
 
 static int i915_energy_uJ(struct seq_file *m, void *data)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        u64 power;
        u32 units;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return -ENODEV;
 
        intel_runtime_pm_get(dev_priv);
@@ -2780,9 +2678,8 @@ static int i915_energy_uJ(struct seq_file *m, void *data)
 
 static int i915_runtime_pm_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
 
        if (!HAS_RUNTIME_PM(dev_priv))
                seq_puts(m, "Runtime power management not supported\n");
@@ -2792,22 +2689,20 @@ static int i915_runtime_pm_status(struct seq_file *m, void *unused)
                   yesno(!intel_irqs_enabled(dev_priv)));
 #ifdef CONFIG_PM
        seq_printf(m, "Usage count: %d\n",
-                  atomic_read(&dev->dev->power.usage_count));
+                  atomic_read(&dev_priv->drm.dev->power.usage_count));
 #else
        seq_printf(m, "Device Power Management (CONFIG_PM) disabled\n");
 #endif
        seq_printf(m, "PCI device power state: %s [%d]\n",
-                  pci_power_name(dev_priv->drm.pdev->current_state),
-                  dev_priv->drm.pdev->current_state);
+                  pci_power_name(pdev->current_state),
+                  pdev->current_state);
 
        return 0;
 }
 
 static int i915_power_domain_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct i915_power_domains *power_domains = &dev_priv->power_domains;
        int i;
 
@@ -2840,12 +2735,10 @@ static int i915_power_domain_info(struct seq_file *m, void *unused)
 
 static int i915_dmc_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
        struct intel_csr *csr;
 
-       if (!HAS_CSR(dev)) {
+       if (!HAS_CSR(dev_priv)) {
                seq_puts(m, "not supported\n");
                return 0;
        }
@@ -2863,12 +2756,12 @@ static int i915_dmc_info(struct seq_file *m, void *unused)
        seq_printf(m, "version: %d.%d\n", CSR_VERSION_MAJOR(csr->version),
                   CSR_VERSION_MINOR(csr->version));
 
-       if (IS_SKYLAKE(dev) && csr->version >= CSR_VERSION(1, 6)) {
+       if (IS_SKYLAKE(dev_priv) && csr->version >= CSR_VERSION(1, 6)) {
                seq_printf(m, "DC3 -> DC5 count: %d\n",
                           I915_READ(SKL_CSR_DC3_DC5_COUNT));
                seq_printf(m, "DC5 -> DC6 count: %d\n",
                           I915_READ(SKL_CSR_DC5_DC6_COUNT));
-       } else if (IS_BROXTON(dev) && csr->version >= CSR_VERSION(1, 4)) {
+       } else if (IS_BROXTON(dev_priv) && csr->version >= CSR_VERSION(1, 4)) {
                seq_printf(m, "DC3 -> DC5 count: %d\n",
                           I915_READ(BXT_CSR_DC3_DC5_COUNT));
        }
@@ -2905,8 +2798,8 @@ static void intel_encoder_info(struct seq_file *m,
                               struct intel_crtc *intel_crtc,
                               struct intel_encoder *intel_encoder)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_crtc *crtc = &intel_crtc->base;
        struct intel_connector *intel_connector;
        struct drm_encoder *encoder;
@@ -2932,8 +2825,8 @@ static void intel_encoder_info(struct seq_file *m,
 
 static void intel_crtc_info(struct seq_file *m, struct intel_crtc *intel_crtc)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct drm_crtc *crtc = &intel_crtc->base;
        struct intel_encoder *intel_encoder;
        struct drm_plane_state *plane_state = crtc->primary->state;
@@ -2967,6 +2860,9 @@ static void intel_dp_info(struct seq_file *m,
        seq_printf(m, "\taudio support: %s\n", yesno(intel_dp->has_audio));
        if (intel_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)
                intel_panel_info(m, &intel_connector->panel);
+
+       drm_dp_downstream_debug(m, intel_dp->dpcd, intel_dp->downstream_ports,
+                               &intel_dp->aux);
 }
 
 static void intel_hdmi_info(struct seq_file *m,
@@ -3031,12 +2927,11 @@ static void intel_connector_info(struct seq_file *m,
                intel_seq_print_mode(m, 2, mode);
 }
 
-static bool cursor_active(struct drm_device *dev, int pipe)
+static bool cursor_active(struct drm_i915_private *dev_priv, int pipe)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        u32 state;
 
-       if (IS_845G(dev) || IS_I865G(dev))
+       if (IS_845G(dev_priv) || IS_I865G(dev_priv))
                state = I915_READ(CURCNTR(PIPE_A)) & CURSOR_ENABLE;
        else
                state = I915_READ(CURCNTR(pipe)) & CURSOR_MODE;
@@ -3044,9 +2939,9 @@ static bool cursor_active(struct drm_device *dev, int pipe)
        return state;
 }
 
-static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
+static bool cursor_position(struct drm_i915_private *dev_priv,
+                           int pipe, int *x, int *y)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        u32 pos;
 
        pos = I915_READ(CURPOS(pipe));
@@ -3059,7 +2954,7 @@ static bool cursor_position(struct drm_device *dev, int pipe, int *x, int *y)
        if (pos & (CURSOR_POS_SIGN << CURSOR_Y_SHIFT))
                *y = -*y;
 
-       return cursor_active(dev, pipe);
+       return cursor_active(dev_priv, pipe);
 }
 
 static const char *plane_type(enum drm_plane_type type)
@@ -3089,12 +2984,12 @@ static const char *plane_rotation(unsigned int rotation)
         */
        snprintf(buf, sizeof(buf),
                 "%s%s%s%s%s%s(0x%08x)",
-                (rotation & BIT(DRM_ROTATE_0)) ? "0 " : "",
-                (rotation & BIT(DRM_ROTATE_90)) ? "90 " : "",
-                (rotation & BIT(DRM_ROTATE_180)) ? "180 " : "",
-                (rotation & BIT(DRM_ROTATE_270)) ? "270 " : "",
-                (rotation & BIT(DRM_REFLECT_X)) ? "FLIPX " : "",
-                (rotation & BIT(DRM_REFLECT_Y)) ? "FLIPY " : "",
+                (rotation & DRM_ROTATE_0) ? "0 " : "",
+                (rotation & DRM_ROTATE_90) ? "90 " : "",
+                (rotation & DRM_ROTATE_180) ? "180 " : "",
+                (rotation & DRM_ROTATE_270) ? "270 " : "",
+                (rotation & DRM_REFLECT_X) ? "FLIPX " : "",
+                (rotation & DRM_REFLECT_Y) ? "FLIPY " : "",
                 rotation);
 
        return buf;
@@ -3102,13 +2997,14 @@ static const char *plane_rotation(unsigned int rotation)
 
 static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_plane *intel_plane;
 
        for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) {
                struct drm_plane_state *state;
                struct drm_plane *plane = &intel_plane->base;
+               char *format_name;
 
                if (!plane->state) {
                        seq_puts(m, "plane->state is NULL!\n");
@@ -3117,6 +3013,12 @@ static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc)
 
                state = plane->state;
 
+               if (state->fb) {
+                       format_name = drm_get_format_name(state->fb->pixel_format);
+               } else {
+                       format_name = kstrdup("N/A", GFP_KERNEL);
+               }
+
                seq_printf(m, "\t--Plane id %d: type=%s, crtc_pos=%4dx%4d, crtc_size=%4dx%4d, src_pos=%d.%04ux%d.%04u, src_size=%d.%04ux%d.%04u, format=%s, rotation=%s\n",
                           plane->base.id,
                           plane_type(intel_plane->base.type),
@@ -3130,8 +3032,10 @@ static void intel_plane_info(struct seq_file *m, struct intel_crtc *intel_crtc)
                           ((state->src_w & 0xffff) * 15625) >> 10,
                           (state->src_h >> 16),
                           ((state->src_h & 0xffff) * 15625) >> 10,
-                          state->fb ? drm_get_format_name(state->fb->pixel_format) : "N/A",
+                          format_name,
                           plane_rotation(state->rotation));
+
+               kfree(format_name);
        }
 }
 
@@ -3165,9 +3069,8 @@ static void intel_scaler_info(struct seq_file *m, struct intel_crtc *intel_crtc)
 
 static int i915_display_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_crtc *crtc;
        struct drm_connector *connector;
 
@@ -3191,7 +3094,7 @@ static int i915_display_info(struct seq_file *m, void *unused)
                if (pipe_config->base.active) {
                        intel_crtc_info(m, crtc);
 
-                       active = cursor_position(dev, crtc->pipe, &x, &y);
+                       active = cursor_position(dev_priv, crtc->pipe, &x, &y);
                        seq_printf(m, "\tcursor visible? %s, position (%d, %d), size %dx%d, addr 0x%08x, active? %s\n",
                                   yesno(crtc->cursor_base),
                                   x, y, crtc->base.cursor->state->crtc_w,
@@ -3220,15 +3123,14 @@ static int i915_display_info(struct seq_file *m, void *unused)
 
 static int i915_semaphore_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
-       int num_rings = hweight32(INTEL_INFO(dev)->ring_mask);
+       int num_rings = INTEL_INFO(dev_priv)->num_rings;
        enum intel_engine_id id;
        int j, ret;
 
-       if (!i915_semaphore_is_enabled(dev_priv)) {
+       if (!i915.semaphores) {
                seq_puts(m, "Semaphores are disabled\n");
                return 0;
        }
@@ -3238,11 +3140,11 @@ static int i915_semaphore_status(struct seq_file *m, void *unused)
                return ret;
        intel_runtime_pm_get(dev_priv);
 
-       if (IS_BROADWELL(dev)) {
+       if (IS_BROADWELL(dev_priv)) {
                struct page *page;
                uint64_t *seqno;
 
-               page = i915_gem_object_get_page(dev_priv->semaphore_obj, 0);
+               page = i915_gem_object_get_page(dev_priv->semaphore->obj, 0);
 
                seqno = (uint64_t *)kmap_atomic(page);
                for_each_engine_id(engine, dev_priv, id) {
@@ -3293,9 +3195,8 @@ static int i915_semaphore_status(struct seq_file *m, void *unused)
 
 static int i915_shared_dplls_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        int i;
 
        drm_modeset_lock_all(dev);
@@ -3323,9 +3224,8 @@ static int i915_wa_registers(struct seq_file *m, void *unused)
        int i;
        int ret;
        struct intel_engine_cs *engine;
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct i915_workarounds *workarounds = &dev_priv->workarounds;
        enum intel_engine_id id;
 
@@ -3361,15 +3261,14 @@ static int i915_wa_registers(struct seq_file *m, void *unused)
 
 static int i915_ddb_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct skl_ddb_allocation *ddb;
        struct skl_ddb_entry *entry;
        enum pipe pipe;
        int plane;
 
-       if (INTEL_INFO(dev)->gen < 9)
+       if (INTEL_GEN(dev_priv) < 9)
                return 0;
 
        drm_modeset_lock_all(dev);
@@ -3399,7 +3298,8 @@ static int i915_ddb_info(struct seq_file *m, void *unused)
 }
 
 static void drrs_status_per_crtc(struct seq_file *m,
-               struct drm_device *dev, struct intel_crtc *intel_crtc)
+                                struct drm_device *dev,
+                                struct intel_crtc *intel_crtc)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_drrs *drrs = &dev_priv->drrs;
@@ -3468,8 +3368,8 @@ static void drrs_status_per_crtc(struct seq_file *m,
 
 static int i915_drrs_status(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_crtc *intel_crtc;
        int active_crtc_cnt = 0;
 
@@ -3492,14 +3392,14 @@ static int i915_drrs_status(struct seq_file *m, void *unused)
 
 struct pipe_crc_info {
        const char *name;
-       struct drm_device *dev;
+       struct drm_i915_private *dev_priv;
        enum pipe pipe;
 };
 
 static int i915_dp_mst_info(struct seq_file *m, void *unused)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_encoder *intel_encoder;
        struct intel_digital_port *intel_dig_port;
        struct drm_connector *connector;
@@ -3528,10 +3428,10 @@ static int i915_dp_mst_info(struct seq_file *m, void *unused)
 static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 {
        struct pipe_crc_info *info = inode->i_private;
-       struct drm_i915_private *dev_priv = to_i915(info->dev);
+       struct drm_i915_private *dev_priv = info->dev_priv;
        struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
 
-       if (info->pipe >= INTEL_INFO(info->dev)->num_pipes)
+       if (info->pipe >= INTEL_INFO(dev_priv)->num_pipes)
                return -ENODEV;
 
        spin_lock_irq(&pipe_crc->lock);
@@ -3552,7 +3452,7 @@ static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
 static int i915_pipe_crc_release(struct inode *inode, struct file *filep)
 {
        struct pipe_crc_info *info = inode->i_private;
-       struct drm_i915_private *dev_priv = to_i915(info->dev);
+       struct drm_i915_private *dev_priv = info->dev_priv;
        struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
 
        spin_lock_irq(&pipe_crc->lock);
@@ -3579,8 +3479,7 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count,
                   loff_t *pos)
 {
        struct pipe_crc_info *info = filep->private_data;
-       struct drm_device *dev = info->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = info->dev_priv;
        struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[info->pipe];
        char buf[PIPE_CRC_BUFFER_LEN];
        int n_entries;
@@ -3621,7 +3520,6 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count,
        while (n_entries > 0) {
                struct intel_pipe_crc_entry *entry =
                        &pipe_crc->entries[pipe_crc->tail];
-               int ret;
 
                if (CIRC_CNT(pipe_crc->head, pipe_crc->tail,
                             INTEL_PIPE_CRC_ENTRIES_NR) < 1)
@@ -3638,8 +3536,7 @@ i915_pipe_crc_read(struct file *filep, char __user *user_buf, size_t count,
 
                spin_unlock_irq(&pipe_crc->lock);
 
-               ret = copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN);
-               if (ret == PIPE_CRC_LINE_LEN)
+               if (copy_to_user(user_buf, buf, PIPE_CRC_LINE_LEN))
                        return -EFAULT;
 
                user_buf += PIPE_CRC_LINE_LEN;
@@ -3678,11 +3575,11 @@ static struct pipe_crc_info i915_pipe_crc_data[I915_MAX_PIPES] = {
 static int i915_pipe_crc_create(struct dentry *root, struct drm_minor *minor,
                                enum pipe pipe)
 {
-       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = to_i915(minor->dev);
        struct dentry *ent;
        struct pipe_crc_info *info = &i915_pipe_crc_data[pipe];
 
-       info->dev = dev;
+       info->dev_priv = dev_priv;
        ent = debugfs_create_file(info->name, S_IRUGO, root, info,
                                  &i915_pipe_crc_fops);
        if (!ent)
@@ -3712,8 +3609,7 @@ static const char *pipe_crc_source_name(enum intel_pipe_crc_source source)
 
 static int display_crc_ctl_show(struct seq_file *m, void *data)
 {
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        int i;
 
        for (i = 0; i < I915_MAX_PIPES; i++)
@@ -3725,9 +3621,7 @@ static int display_crc_ctl_show(struct seq_file *m, void *data)
 
 static int display_crc_ctl_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
-
-       return single_open(file, display_crc_ctl_show, dev);
+       return single_open(file, display_crc_ctl_show, inode->i_private);
 }
 
 static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
@@ -3750,9 +3644,11 @@ static int i8xx_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
        return 0;
 }
 
-static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
+static int i9xx_pipe_crc_auto_source(struct drm_i915_private *dev_priv,
+                                    enum pipe pipe,
                                     enum intel_pipe_crc_source *source)
 {
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_encoder *encoder;
        struct intel_crtc *crtc;
        struct intel_digital_port *dig_port;
@@ -3802,16 +3698,15 @@ static int i9xx_pipe_crc_auto_source(struct drm_device *dev, enum pipe pipe,
        return ret;
 }
 
-static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
+static int vlv_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
                                enum pipe pipe,
                                enum intel_pipe_crc_source *source,
                                uint32_t *val)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        bool need_stable_symbols = false;
 
        if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
-               int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
+               int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source);
                if (ret)
                        return ret;
        }
@@ -3829,7 +3724,7 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
                need_stable_symbols = true;
                break;
        case INTEL_PIPE_CRC_SOURCE_DP_D:
-               if (!IS_CHERRYVIEW(dev))
+               if (!IS_CHERRYVIEW(dev_priv))
                        return -EINVAL;
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_VLV;
                need_stable_symbols = true;
@@ -3873,16 +3768,15 @@ static int vlv_pipe_crc_ctl_reg(struct drm_device *dev,
        return 0;
 }
 
-static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
+static int i9xx_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
                                 enum pipe pipe,
                                 enum intel_pipe_crc_source *source,
                                 uint32_t *val)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        bool need_stable_symbols = false;
 
        if (*source == INTEL_PIPE_CRC_SOURCE_AUTO) {
-               int ret = i9xx_pipe_crc_auto_source(dev, pipe, source);
+               int ret = i9xx_pipe_crc_auto_source(dev_priv, pipe, source);
                if (ret)
                        return ret;
        }
@@ -3892,24 +3786,24 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PIPE_I9XX;
                break;
        case INTEL_PIPE_CRC_SOURCE_TV:
-               if (!SUPPORTS_TV(dev))
+               if (!SUPPORTS_TV(dev_priv))
                        return -EINVAL;
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_TV_PRE;
                break;
        case INTEL_PIPE_CRC_SOURCE_DP_B:
-               if (!IS_G4X(dev))
+               if (!IS_G4X(dev_priv))
                        return -EINVAL;
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_B_G4X;
                need_stable_symbols = true;
                break;
        case INTEL_PIPE_CRC_SOURCE_DP_C:
-               if (!IS_G4X(dev))
+               if (!IS_G4X(dev_priv))
                        return -EINVAL;
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_C_G4X;
                need_stable_symbols = true;
                break;
        case INTEL_PIPE_CRC_SOURCE_DP_D:
-               if (!IS_G4X(dev))
+               if (!IS_G4X(dev_priv))
                        return -EINVAL;
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_DP_D_G4X;
                need_stable_symbols = true;
@@ -3933,7 +3827,7 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
        if (need_stable_symbols) {
                uint32_t tmp = I915_READ(PORT_DFT2_G4X);
 
-               WARN_ON(!IS_G4X(dev));
+               WARN_ON(!IS_G4X(dev_priv));
 
                I915_WRITE(PORT_DFT_I9XX,
                           I915_READ(PORT_DFT_I9XX) | DC_BALANCE_RESET);
@@ -3949,10 +3843,9 @@ static int i9xx_pipe_crc_ctl_reg(struct drm_device *dev,
        return 0;
 }
 
-static void vlv_undo_pipe_scramble_reset(struct drm_device *dev,
+static void vlv_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv,
                                         enum pipe pipe)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        uint32_t tmp = I915_READ(PORT_DFT2_G4X);
 
        switch (pipe) {
@@ -3974,10 +3867,9 @@ static void vlv_undo_pipe_scramble_reset(struct drm_device *dev,
 
 }
 
-static void g4x_undo_pipe_scramble_reset(struct drm_device *dev,
+static void g4x_undo_pipe_scramble_reset(struct drm_i915_private *dev_priv,
                                         enum pipe pipe)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        uint32_t tmp = I915_READ(PORT_DFT2_G4X);
 
        if (pipe == PIPE_A)
@@ -4018,9 +3910,10 @@ static int ilk_pipe_crc_ctl_reg(enum intel_pipe_crc_source *source,
        return 0;
 }
 
-static void hsw_trans_edp_pipe_A_crc_wa(struct drm_device *dev, bool enable)
+static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv,
+                                       bool enable)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_crtc *crtc =
                to_intel_crtc(dev_priv->pipe_to_crtc_mapping[PIPE_A]);
        struct intel_crtc_state *pipe_config;
@@ -4054,7 +3947,7 @@ out:
                drm_atomic_state_free(state);
 }
 
-static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
+static int ivb_pipe_crc_ctl_reg(struct drm_i915_private *dev_priv,
                                enum pipe pipe,
                                enum intel_pipe_crc_source *source,
                                uint32_t *val)
@@ -4070,8 +3963,8 @@ static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_SPRITE_IVB;
                break;
        case INTEL_PIPE_CRC_SOURCE_PF:
-               if (IS_HASWELL(dev) && pipe == PIPE_A)
-                       hsw_trans_edp_pipe_A_crc_wa(dev, true);
+               if (IS_HASWELL(dev_priv) && pipe == PIPE_A)
+                       hsw_trans_edp_pipe_A_crc_wa(dev_priv, true);
 
                *val = PIPE_CRC_ENABLE | PIPE_CRC_SOURCE_PF_IVB;
                break;
@@ -4085,13 +3978,14 @@ static int ivb_pipe_crc_ctl_reg(struct drm_device *dev,
        return 0;
 }
 
-static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
+static int pipe_crc_set_source(struct drm_i915_private *dev_priv,
+                              enum pipe pipe,
                               enum intel_pipe_crc_source source)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_device *dev = &dev_priv->drm;
        struct intel_pipe_crc *pipe_crc = &dev_priv->pipe_crc[pipe];
-       struct intel_crtc *crtc = to_intel_crtc(intel_get_crtc_for_pipe(dev,
-                                                                       pipe));
+       struct intel_crtc *crtc =
+                       to_intel_crtc(intel_get_crtc_for_pipe(dev, pipe));
        enum intel_display_power_domain power_domain;
        u32 val = 0; /* shut up gcc */
        int ret;
@@ -4109,16 +4003,16 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
                return -EIO;
        }
 
-       if (IS_GEN2(dev))
+       if (IS_GEN2(dev_priv))
                ret = i8xx_pipe_crc_ctl_reg(&source, &val);
-       else if (INTEL_INFO(dev)->gen < 5)
-               ret = i9xx_pipe_crc_ctl_reg(dev, pipe, &source, &val);
-       else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
-               ret = vlv_pipe_crc_ctl_reg(dev, pipe, &source, &val);
-       else if (IS_GEN5(dev) || IS_GEN6(dev))
+       else if (INTEL_GEN(dev_priv) < 5)
+               ret = i9xx_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val);
+       else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               ret = vlv_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val);
+       else if (IS_GEN5(dev_priv) || IS_GEN6(dev_priv))
                ret = ilk_pipe_crc_ctl_reg(&source, &val);
        else
-               ret = ivb_pipe_crc_ctl_reg(dev, pipe, &source, &val);
+               ret = ivb_pipe_crc_ctl_reg(dev_priv, pipe, &source, &val);
 
        if (ret != 0)
                goto out;
@@ -4182,12 +4076,12 @@ static int pipe_crc_set_source(struct drm_device *dev, enum pipe pipe,
 
                kfree(entries);
 
-               if (IS_G4X(dev))
-                       g4x_undo_pipe_scramble_reset(dev, pipe);
-               else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
-                       vlv_undo_pipe_scramble_reset(dev, pipe);
-               else if (IS_HASWELL(dev) && pipe == PIPE_A)
-                       hsw_trans_edp_pipe_A_crc_wa(dev, false);
+               if (IS_G4X(dev_priv))
+                       g4x_undo_pipe_scramble_reset(dev_priv, pipe);
+               else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+                       vlv_undo_pipe_scramble_reset(dev_priv, pipe);
+               else if (IS_HASWELL(dev_priv) && pipe == PIPE_A)
+                       hsw_trans_edp_pipe_A_crc_wa(dev_priv, false);
 
                hsw_enable_ips(crtc);
        }
@@ -4291,7 +4185,8 @@ display_crc_ctl_parse_source(const char *buf, enum intel_pipe_crc_source *s)
        return -EINVAL;
 }
 
-static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
+static int display_crc_ctl_parse(struct drm_i915_private *dev_priv,
+                                char *buf, size_t len)
 {
 #define N_WORDS 3
        int n_words;
@@ -4322,14 +4217,14 @@ static int display_crc_ctl_parse(struct drm_device *dev, char *buf, size_t len)
                return -EINVAL;
        }
 
-       return pipe_crc_set_source(dev, pipe, source);
+       return pipe_crc_set_source(dev_priv, pipe, source);
 }
 
 static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf,
                                     size_t len, loff_t *offp)
 {
        struct seq_file *m = file->private_data;
-       struct drm_device *dev = m->private;
+       struct drm_i915_private *dev_priv = m->private;
        char *tmpbuf;
        int ret;
 
@@ -4352,7 +4247,7 @@ static ssize_t display_crc_ctl_write(struct file *file, const char __user *ubuf,
        }
        tmpbuf[len] = '\0';
 
-       ret = display_crc_ctl_parse(dev, tmpbuf, len);
+       ret = display_crc_ctl_parse(dev_priv, tmpbuf, len);
 
 out:
        kfree(tmpbuf);
@@ -4373,8 +4268,8 @@ static const struct file_operations i915_display_crc_ctl_fops = {
 };
 
 static ssize_t i915_displayport_test_active_write(struct file *file,
-                                           const char __user *ubuf,
-                                           size_t len, loff_t *offp)
+                                                 const char __user *ubuf,
+                                                 size_t len, loff_t *offp)
 {
        char *input_buffer;
        int status = 0;
@@ -4404,7 +4299,6 @@ static ssize_t i915_displayport_test_active_write(struct file *file,
        DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len);
 
        list_for_each_entry(connector, connector_list, head) {
-
                if (connector->connector_type !=
                    DRM_MODE_CONNECTOR_DisplayPort)
                        continue;
@@ -4442,7 +4336,6 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data)
        struct intel_dp *intel_dp;
 
        list_for_each_entry(connector, connector_list, head) {
-
                if (connector->connector_type !=
                    DRM_MODE_CONNECTOR_DisplayPort)
                        continue;
@@ -4462,11 +4355,12 @@ static int i915_displayport_test_active_show(struct seq_file *m, void *data)
 }
 
 static int i915_displayport_test_active_open(struct inode *inode,
-                                      struct file *file)
+                                            struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       return single_open(file, i915_displayport_test_active_show, dev);
+       return single_open(file, i915_displayport_test_active_show,
+                          &dev_priv->drm);
 }
 
 static const struct file_operations i915_displayport_test_active_fops = {
@@ -4486,7 +4380,6 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data)
        struct intel_dp *intel_dp;
 
        list_for_each_entry(connector, connector_list, head) {
-
                if (connector->connector_type !=
                    DRM_MODE_CONNECTOR_DisplayPort)
                        continue;
@@ -4502,11 +4395,12 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data)
        return 0;
 }
 static int i915_displayport_test_data_open(struct inode *inode,
-                                      struct file *file)
+                                          struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       return single_open(file, i915_displayport_test_data_show, dev);
+       return single_open(file, i915_displayport_test_data_show,
+                          &dev_priv->drm);
 }
 
 static const struct file_operations i915_displayport_test_data_fops = {
@@ -4525,7 +4419,6 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data)
        struct intel_dp *intel_dp;
 
        list_for_each_entry(connector, connector_list, head) {
-
                if (connector->connector_type !=
                    DRM_MODE_CONNECTOR_DisplayPort)
                        continue;
@@ -4544,9 +4437,10 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data)
 static int i915_displayport_test_type_open(struct inode *inode,
                                       struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       return single_open(file, i915_displayport_test_type_show, dev);
+       return single_open(file, i915_displayport_test_type_show,
+                          &dev_priv->drm);
 }
 
 static const struct file_operations i915_displayport_test_type_fops = {
@@ -4559,13 +4453,14 @@ static const struct file_operations i915_displayport_test_type_fops = {
 
 static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
 {
-       struct drm_device *dev = m->private;
+       struct drm_i915_private *dev_priv = m->private;
+       struct drm_device *dev = &dev_priv->drm;
        int level;
        int num_levels;
 
-       if (IS_CHERRYVIEW(dev))
+       if (IS_CHERRYVIEW(dev_priv))
                num_levels = 3;
-       else if (IS_VALLEYVIEW(dev))
+       else if (IS_VALLEYVIEW(dev_priv))
                num_levels = 1;
        else
                num_levels = ilk_wm_max_level(dev) + 1;
@@ -4579,8 +4474,8 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
                 * - WM1+ latency values in 0.5us units
                 * - latencies are in us on gen9/vlv/chv
                 */
-               if (INTEL_INFO(dev)->gen >= 9 || IS_VALLEYVIEW(dev) ||
-                   IS_CHERRYVIEW(dev))
+               if (INTEL_GEN(dev_priv) >= 9 || IS_VALLEYVIEW(dev_priv) ||
+                   IS_CHERRYVIEW(dev_priv))
                        latency *= 10;
                else if (level > 0)
                        latency *= 5;
@@ -4594,14 +4489,13 @@ static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
 
 static int pri_wm_latency_show(struct seq_file *m, void *data)
 {
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        const uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.pri_latency;
+               latencies = dev_priv->wm.pri_latency;
 
        wm_latency_show(m, latencies);
 
@@ -4610,14 +4504,13 @@ static int pri_wm_latency_show(struct seq_file *m, void *data)
 
 static int spr_wm_latency_show(struct seq_file *m, void *data)
 {
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        const uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.spr_latency;
+               latencies = dev_priv->wm.spr_latency;
 
        wm_latency_show(m, latencies);
 
@@ -4626,14 +4519,13 @@ static int spr_wm_latency_show(struct seq_file *m, void *data)
 
 static int cur_wm_latency_show(struct seq_file *m, void *data)
 {
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        const uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.cur_latency;
+               latencies = dev_priv->wm.cur_latency;
 
        wm_latency_show(m, latencies);
 
@@ -4642,48 +4534,49 @@ static int cur_wm_latency_show(struct seq_file *m, void *data)
 
 static int pri_wm_latency_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (INTEL_INFO(dev)->gen < 5)
+       if (INTEL_GEN(dev_priv) < 5)
                return -ENODEV;
 
-       return single_open(file, pri_wm_latency_show, dev);
+       return single_open(file, pri_wm_latency_show, dev_priv);
 }
 
 static int spr_wm_latency_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (HAS_GMCH_DISPLAY(dev))
+       if (HAS_GMCH_DISPLAY(dev_priv))
                return -ENODEV;
 
-       return single_open(file, spr_wm_latency_show, dev);
+       return single_open(file, spr_wm_latency_show, dev_priv);
 }
 
 static int cur_wm_latency_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (HAS_GMCH_DISPLAY(dev))
+       if (HAS_GMCH_DISPLAY(dev_priv))
                return -ENODEV;
 
-       return single_open(file, cur_wm_latency_show, dev);
+       return single_open(file, cur_wm_latency_show, dev_priv);
 }
 
 static ssize_t wm_latency_write(struct file *file, const char __user *ubuf,
                                size_t len, loff_t *offp, uint16_t wm[8])
 {
        struct seq_file *m = file->private_data;
-       struct drm_device *dev = m->private;
+       struct drm_i915_private *dev_priv = m->private;
+       struct drm_device *dev = &dev_priv->drm;
        uint16_t new[8] = { 0 };
        int num_levels;
        int level;
        int ret;
        char tmp[32];
 
-       if (IS_CHERRYVIEW(dev))
+       if (IS_CHERRYVIEW(dev_priv))
                num_levels = 3;
-       else if (IS_VALLEYVIEW(dev))
+       else if (IS_VALLEYVIEW(dev_priv))
                num_levels = 1;
        else
                num_levels = ilk_wm_max_level(dev) + 1;
@@ -4717,14 +4610,13 @@ static ssize_t pri_wm_latency_write(struct file *file, const char __user *ubuf,
                                    size_t len, loff_t *offp)
 {
        struct seq_file *m = file->private_data;
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.pri_latency;
+               latencies = dev_priv->wm.pri_latency;
 
        return wm_latency_write(file, ubuf, len, offp, latencies);
 }
@@ -4733,14 +4625,13 @@ static ssize_t spr_wm_latency_write(struct file *file, const char __user *ubuf,
                                    size_t len, loff_t *offp)
 {
        struct seq_file *m = file->private_data;
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.spr_latency;
+               latencies = dev_priv->wm.spr_latency;
 
        return wm_latency_write(file, ubuf, len, offp, latencies);
 }
@@ -4749,14 +4640,13 @@ static ssize_t cur_wm_latency_write(struct file *file, const char __user *ubuf,
                                    size_t len, loff_t *offp)
 {
        struct seq_file *m = file->private_data;
-       struct drm_device *dev = m->private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = m->private;
        uint16_t *latencies;
 
-       if (INTEL_INFO(dev)->gen >= 9)
+       if (INTEL_GEN(dev_priv) >= 9)
                latencies = dev_priv->wm.skl_latency;
        else
-               latencies = to_i915(dev)->wm.cur_latency;
+               latencies = dev_priv->wm.cur_latency;
 
        return wm_latency_write(file, ubuf, len, offp, latencies);
 }
@@ -4791,8 +4681,7 @@ static const struct file_operations i915_cur_wm_latency_fops = {
 static int
 i915_wedged_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
        *val = i915_terminally_wedged(&dev_priv->gpu_error);
 
@@ -4802,8 +4691,7 @@ i915_wedged_get(void *data, u64 *val)
 static int
 i915_wedged_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
        /*
         * There is no safeguard against this debugfs entry colliding
@@ -4833,8 +4721,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_wedged_fops,
 static int
 i915_ring_missed_irq_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
        *val = dev_priv->gpu_error.missed_irq_rings;
        return 0;
@@ -4843,8 +4730,8 @@ i915_ring_missed_irq_get(void *data, u64 *val)
 static int
 i915_ring_missed_irq_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
+       struct drm_device *dev = &dev_priv->drm;
        int ret;
 
        /* Lock against concurrent debugfs callers */
@@ -4864,8 +4751,7 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_ring_missed_irq_fops,
 static int
 i915_ring_test_irq_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
        *val = dev_priv->gpu_error.test_irq_rings;
 
@@ -4875,8 +4761,7 @@ i915_ring_test_irq_get(void *data, u64 *val)
 static int
 i915_ring_test_irq_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
 
        val &= INTEL_INFO(dev_priv)->ring_mask;
        DRM_DEBUG_DRIVER("Masking interrupts on rings 0x%08llx\n", val);
@@ -4908,8 +4793,8 @@ i915_drop_caches_get(void *data, u64 *val)
 static int
 i915_drop_caches_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
+       struct drm_device *dev = &dev_priv->drm;
        int ret;
 
        DRM_DEBUG("Dropping caches: 0x%08llx\n", val);
@@ -4921,7 +4806,9 @@ i915_drop_caches_set(void *data, u64 val)
                return ret;
 
        if (val & DROP_ACTIVE) {
-               ret = i915_gem_wait_for_idle(dev_priv);
+               ret = i915_gem_wait_for_idle(dev_priv,
+                                            I915_WAIT_INTERRUPTIBLE |
+                                            I915_WAIT_LOCKED);
                if (ret)
                        goto unlock;
        }
@@ -4948,38 +4835,25 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_drop_caches_fops,
 static int
 i915_max_freq_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
+       struct drm_i915_private *dev_priv = data;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return -ENODEV;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-       ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
-       if (ret)
-               return ret;
-
        *val = intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-
        return 0;
 }
 
 static int
 i915_max_freq_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
        u32 hw_max, hw_min;
        int ret;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return -ENODEV;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        DRM_DEBUG_DRIVER("Manually setting max freq to %llu\n", val);
 
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
@@ -5015,38 +4889,25 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_max_freq_fops,
 static int
 i915_min_freq_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
+       struct drm_i915_private *dev_priv = data;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return -ENODEV;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-       ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
-       if (ret)
-               return ret;
-
        *val = intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-
        return 0;
 }
 
 static int
 i915_min_freq_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
        u32 hw_max, hw_min;
        int ret;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return -ENODEV;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        DRM_DEBUG_DRIVER("Manually setting min freq to %llu\n", val);
 
        ret = mutex_lock_interruptible(&dev_priv->rps.hw_lock);
@@ -5061,7 +4922,8 @@ i915_min_freq_set(void *data, u64 val)
        hw_max = dev_priv->rps.max_freq;
        hw_min = dev_priv->rps.min_freq;
 
-       if (val < hw_min || val > hw_max || val > dev_priv->rps.max_freq_softlimit) {
+       if (val < hw_min ||
+           val > hw_max || val > dev_priv->rps.max_freq_softlimit) {
                mutex_unlock(&dev_priv->rps.hw_lock);
                return -EINVAL;
        }
@@ -5082,12 +4944,12 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_min_freq_fops,
 static int
 i915_cache_sharing_get(void *data, u64 *val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
+       struct drm_device *dev = &dev_priv->drm;
        u32 snpcr;
        int ret;
 
-       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+       if (!(IS_GEN6(dev_priv) || IS_GEN7(dev_priv)))
                return -ENODEV;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
@@ -5098,7 +4960,7 @@ i915_cache_sharing_get(void *data, u64 *val)
        snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
 
        intel_runtime_pm_put(dev_priv);
-       mutex_unlock(&dev_priv->drm.struct_mutex);
+       mutex_unlock(&dev->struct_mutex);
 
        *val = (snpcr & GEN6_MBC_SNPCR_MASK) >> GEN6_MBC_SNPCR_SHIFT;
 
@@ -5108,11 +4970,10 @@ i915_cache_sharing_get(void *data, u64 *val)
 static int
 i915_cache_sharing_set(void *data, u64 val)
 {
-       struct drm_device *dev = data;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = data;
        u32 snpcr;
 
-       if (!(IS_GEN6(dev) || IS_GEN7(dev)))
+       if (!(IS_GEN6(dev_priv) || IS_GEN7(dev_priv)))
                return -ENODEV;
 
        if (val > 3)
@@ -5135,18 +4996,9 @@ DEFINE_SIMPLE_ATTRIBUTE(i915_cache_sharing_fops,
                        i915_cache_sharing_get, i915_cache_sharing_set,
                        "%llu\n");
 
-struct sseu_dev_status {
-       unsigned int slice_total;
-       unsigned int subslice_total;
-       unsigned int subslice_per_slice;
-       unsigned int eu_total;
-       unsigned int eu_per_subslice;
-};
-
-static void cherryview_sseu_device_status(struct drm_device *dev,
-                                         struct sseu_dev_status *stat)
+static void cherryview_sseu_device_status(struct drm_i915_private *dev_priv,
+                                         struct sseu_dev_info *sseu)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        int ss_max = 2;
        int ss;
        u32 sig1[ss_max], sig2[ss_max];
@@ -5163,28 +5015,27 @@ static void cherryview_sseu_device_status(struct drm_device *dev,
                        /* skip disabled subslice */
                        continue;
 
-               stat->slice_total = 1;
-               stat->subslice_per_slice++;
+               sseu->slice_mask = BIT(0);
+               sseu->subslice_mask |= BIT(ss);
                eu_cnt = ((sig1[ss] & CHV_EU08_PG_ENABLE) ? 0 : 2) +
                         ((sig1[ss] & CHV_EU19_PG_ENABLE) ? 0 : 2) +
                         ((sig1[ss] & CHV_EU210_PG_ENABLE) ? 0 : 2) +
                         ((sig2[ss] & CHV_EU311_PG_ENABLE) ? 0 : 2);
-               stat->eu_total += eu_cnt;
-               stat->eu_per_subslice = max(stat->eu_per_subslice, eu_cnt);
+               sseu->eu_total += eu_cnt;
+               sseu->eu_per_subslice = max_t(unsigned int,
+                                             sseu->eu_per_subslice, eu_cnt);
        }
-       stat->subslice_total = stat->subslice_per_slice;
 }
 
-static void gen9_sseu_device_status(struct drm_device *dev,
-                                   struct sseu_dev_status *stat)
+static void gen9_sseu_device_status(struct drm_i915_private *dev_priv,
+                                   struct sseu_dev_info *sseu)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        int s_max = 3, ss_max = 4;
        int s, ss;
        u32 s_reg[s_max], eu_reg[2*s_max], eu_mask[2];
 
        /* BXT has a single slice and at most 3 subslices. */
-       if (IS_BROXTON(dev)) {
+       if (IS_BROXTON(dev_priv)) {
                s_max = 1;
                ss_max = 3;
        }
@@ -5205,126 +5056,134 @@ static void gen9_sseu_device_status(struct drm_device *dev,
                     GEN9_PGCTL_SSB_EU311_ACK;
 
        for (s = 0; s < s_max; s++) {
-               unsigned int ss_cnt = 0;
-
                if ((s_reg[s] & GEN9_PGCTL_SLICE_ACK) == 0)
                        /* skip disabled slice */
                        continue;
 
-               stat->slice_total++;
+               sseu->slice_mask |= BIT(s);
 
-               if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev))
-                       ss_cnt = INTEL_INFO(dev)->subslice_per_slice;
+               if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
+                       sseu->subslice_mask =
+                               INTEL_INFO(dev_priv)->sseu.subslice_mask;
 
                for (ss = 0; ss < ss_max; ss++) {
                        unsigned int eu_cnt;
 
-                       if (IS_BROXTON(dev) &&
-                           !(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss))))
-                               /* skip disabled subslice */
-                               continue;
+                       if (IS_BROXTON(dev_priv)) {
+                               if (!(s_reg[s] & (GEN9_PGCTL_SS_ACK(ss))))
+                                       /* skip disabled subslice */
+                                       continue;
 
-                       if (IS_BROXTON(dev))
-                               ss_cnt++;
+                               sseu->subslice_mask |= BIT(ss);
+                       }
 
                        eu_cnt = 2 * hweight32(eu_reg[2*s + ss/2] &
                                               eu_mask[ss%2]);
-                       stat->eu_total += eu_cnt;
-                       stat->eu_per_subslice = max(stat->eu_per_subslice,
-                                                   eu_cnt);
+                       sseu->eu_total += eu_cnt;
+                       sseu->eu_per_subslice = max_t(unsigned int,
+                                                     sseu->eu_per_subslice,
+                                                     eu_cnt);
                }
-
-               stat->subslice_total += ss_cnt;
-               stat->subslice_per_slice = max(stat->subslice_per_slice,
-                                              ss_cnt);
        }
 }
 
-static void broadwell_sseu_device_status(struct drm_device *dev,
-                                        struct sseu_dev_status *stat)
+static void broadwell_sseu_device_status(struct drm_i915_private *dev_priv,
+                                        struct sseu_dev_info *sseu)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int s;
        u32 slice_info = I915_READ(GEN8_GT_SLICE_INFO);
+       int s;
 
-       stat->slice_total = hweight32(slice_info & GEN8_LSLICESTAT_MASK);
+       sseu->slice_mask = slice_info & GEN8_LSLICESTAT_MASK;
 
-       if (stat->slice_total) {
-               stat->subslice_per_slice = INTEL_INFO(dev)->subslice_per_slice;
-               stat->subslice_total = stat->slice_total *
-                                      stat->subslice_per_slice;
-               stat->eu_per_subslice = INTEL_INFO(dev)->eu_per_subslice;
-               stat->eu_total = stat->eu_per_subslice * stat->subslice_total;
+       if (sseu->slice_mask) {
+               sseu->subslice_mask = INTEL_INFO(dev_priv)->sseu.subslice_mask;
+               sseu->eu_per_subslice =
+                               INTEL_INFO(dev_priv)->sseu.eu_per_subslice;
+               sseu->eu_total = sseu->eu_per_subslice *
+                                sseu_subslice_total(sseu);
 
                /* subtract fused off EU(s) from enabled slice(s) */
-               for (s = 0; s < stat->slice_total; s++) {
-                       u8 subslice_7eu = INTEL_INFO(dev)->subslice_7eu[s];
+               for (s = 0; s < fls(sseu->slice_mask); s++) {
+                       u8 subslice_7eu =
+                               INTEL_INFO(dev_priv)->sseu.subslice_7eu[s];
 
-                       stat->eu_total -= hweight8(subslice_7eu);
+                       sseu->eu_total -= hweight8(subslice_7eu);
                }
        }
 }
 
-static int i915_sseu_status(struct seq_file *m, void *unused)
+static void i915_print_sseu_info(struct seq_file *m, bool is_available_info,
+                                const struct sseu_dev_info *sseu)
 {
-       struct drm_info_node *node = (struct drm_info_node *) m->private;
-       struct drm_device *dev = node->minor->dev;
-       struct sseu_dev_status stat;
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       const char *type = is_available_info ? "Available" : "Enabled";
 
-       if (INTEL_INFO(dev)->gen < 8)
-               return -ENODEV;
+       seq_printf(m, "  %s Slice Mask: %04x\n", type,
+                  sseu->slice_mask);
+       seq_printf(m, "  %s Slice Total: %u\n", type,
+                  hweight8(sseu->slice_mask));
+       seq_printf(m, "  %s Subslice Total: %u\n", type,
+                  sseu_subslice_total(sseu));
+       seq_printf(m, "  %s Subslice Mask: %04x\n", type,
+                  sseu->subslice_mask);
+       seq_printf(m, "  %s Subslice Per Slice: %u\n", type,
+                  hweight8(sseu->subslice_mask));
+       seq_printf(m, "  %s EU Total: %u\n", type,
+                  sseu->eu_total);
+       seq_printf(m, "  %s EU Per Subslice: %u\n", type,
+                  sseu->eu_per_subslice);
+
+       if (!is_available_info)
+               return;
+
+       seq_printf(m, "  Has Pooled EU: %s\n", yesno(HAS_POOLED_EU(dev_priv)));
+       if (HAS_POOLED_EU(dev_priv))
+               seq_printf(m, "  Min EU in pool: %u\n", sseu->min_eu_in_pool);
 
-       seq_puts(m, "SSEU Device Info\n");
-       seq_printf(m, "  Available Slice Total: %u\n",
-                  INTEL_INFO(dev)->slice_total);
-       seq_printf(m, "  Available Subslice Total: %u\n",
-                  INTEL_INFO(dev)->subslice_total);
-       seq_printf(m, "  Available Subslice Per Slice: %u\n",
-                  INTEL_INFO(dev)->subslice_per_slice);
-       seq_printf(m, "  Available EU Total: %u\n",
-                  INTEL_INFO(dev)->eu_total);
-       seq_printf(m, "  Available EU Per Subslice: %u\n",
-                  INTEL_INFO(dev)->eu_per_subslice);
-       seq_printf(m, "  Has Pooled EU: %s\n", yesno(HAS_POOLED_EU(dev)));
-       if (HAS_POOLED_EU(dev))
-               seq_printf(m, "  Min EU in pool: %u\n",
-                          INTEL_INFO(dev)->min_eu_in_pool);
        seq_printf(m, "  Has Slice Power Gating: %s\n",
-                  yesno(INTEL_INFO(dev)->has_slice_pg));
+                  yesno(sseu->has_slice_pg));
        seq_printf(m, "  Has Subslice Power Gating: %s\n",
-                  yesno(INTEL_INFO(dev)->has_subslice_pg));
+                  yesno(sseu->has_subslice_pg));
        seq_printf(m, "  Has EU Power Gating: %s\n",
-                  yesno(INTEL_INFO(dev)->has_eu_pg));
+                  yesno(sseu->has_eu_pg));
+}
+
+static int i915_sseu_status(struct seq_file *m, void *unused)
+{
+       struct drm_i915_private *dev_priv = node_to_i915(m->private);
+       struct sseu_dev_info sseu;
+
+       if (INTEL_GEN(dev_priv) < 8)
+               return -ENODEV;
+
+       seq_puts(m, "SSEU Device Info\n");
+       i915_print_sseu_info(m, true, &INTEL_INFO(dev_priv)->sseu);
 
        seq_puts(m, "SSEU Device Status\n");
-       memset(&stat, 0, sizeof(stat));
-       if (IS_CHERRYVIEW(dev)) {
-               cherryview_sseu_device_status(dev, &stat);
-       } else if (IS_BROADWELL(dev)) {
-               broadwell_sseu_device_status(dev, &stat);
-       } else if (INTEL_INFO(dev)->gen >= 9) {
-               gen9_sseu_device_status(dev, &stat);
-       }
-       seq_printf(m, "  Enabled Slice Total: %u\n",
-                  stat.slice_total);
-       seq_printf(m, "  Enabled Subslice Total: %u\n",
-                  stat.subslice_total);
-       seq_printf(m, "  Enabled Subslice Per Slice: %u\n",
-                  stat.subslice_per_slice);
-       seq_printf(m, "  Enabled EU Total: %u\n",
-                  stat.eu_total);
-       seq_printf(m, "  Enabled EU Per Subslice: %u\n",
-                  stat.eu_per_subslice);
+       memset(&sseu, 0, sizeof(sseu));
+
+       intel_runtime_pm_get(dev_priv);
+
+       if (IS_CHERRYVIEW(dev_priv)) {
+               cherryview_sseu_device_status(dev_priv, &sseu);
+       } else if (IS_BROADWELL(dev_priv)) {
+               broadwell_sseu_device_status(dev_priv, &sseu);
+       } else if (INTEL_GEN(dev_priv) >= 9) {
+               gen9_sseu_device_status(dev_priv, &sseu);
+       }
+
+       intel_runtime_pm_put(dev_priv);
+
+       i915_print_sseu_info(m, false, &sseu);
 
        return 0;
 }
 
 static int i915_forcewake_open(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return 0;
 
        intel_runtime_pm_get(dev_priv);
@@ -5335,10 +5194,9 @@ static int i915_forcewake_open(struct inode *inode, struct file *file)
 
 static int i915_forcewake_release(struct inode *inode, struct file *file)
 {
-       struct drm_device *dev = inode->i_private;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = inode->i_private;
 
-       if (INTEL_INFO(dev)->gen < 6)
+       if (INTEL_GEN(dev_priv) < 6)
                return 0;
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
@@ -5355,12 +5213,11 @@ static const struct file_operations i915_forcewake_fops = {
 
 static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
 {
-       struct drm_device *dev = minor->dev;
        struct dentry *ent;
 
        ent = debugfs_create_file("i915_forcewake_user",
                                  S_IRUSR,
-                                 root, dev,
+                                 root, to_i915(minor->dev),
                                  &i915_forcewake_fops);
        if (!ent)
                return -ENOMEM;
@@ -5373,12 +5230,11 @@ static int i915_debugfs_create(struct dentry *root,
                               const char *name,
                               const struct file_operations *fops)
 {
-       struct drm_device *dev = minor->dev;
        struct dentry *ent;
 
        ent = debugfs_create_file(name,
                                  S_IRUGO | S_IWUSR,
-                                 root, dev,
+                                 root, to_i915(minor->dev),
                                  fops);
        if (!ent)
                return -ENOMEM;
@@ -5390,9 +5246,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
        {"i915_capabilities", i915_capabilities, 0},
        {"i915_gem_objects", i915_gem_object_info, 0},
        {"i915_gem_gtt", i915_gem_gtt_info, 0},
-       {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST},
-       {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
-       {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
+       {"i915_gem_pin_display", i915_gem_gtt_info, 0, (void *)1},
        {"i915_gem_stolen", i915_gem_stolen_list_info },
        {"i915_gem_pageflip", i915_gem_pageflip_info, 0},
        {"i915_gem_request", i915_gem_request_info, 0},
@@ -5467,9 +5321,8 @@ static const struct i915_debugfs_files {
        {"i915_dp_test_active", &i915_displayport_test_active_fops}
 };
 
-void intel_display_crc_init(struct drm_device *dev)
+void intel_display_crc_init(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        enum pipe pipe;
 
        for_each_pipe(dev_priv, pipe) {
@@ -5517,7 +5370,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv)
        drm_debugfs_remove_files(i915_debugfs_list,
                                 I915_DEBUGFS_ENTRIES, minor);
 
-       drm_debugfs_remove_files((struct drm_info_list *) &i915_forcewake_fops,
+       drm_debugfs_remove_files((struct drm_info_list *)&i915_forcewake_fops,
                                 1, minor);
 
        for (i = 0; i < ARRAY_SIZE(i915_pipe_crc_data); i++) {
@@ -5529,7 +5382,7 @@ void i915_debugfs_unregister(struct drm_i915_private *dev_priv)
 
        for (i = 0; i < ARRAY_SIZE(i915_debugfs_files); i++) {
                struct drm_info_list *info_list =
-                       (struct drm_info_list *) i915_debugfs_files[i].fops;
+                       (struct drm_info_list *)i915_debugfs_files[i].fops;
 
                drm_debugfs_remove_files(info_list, 1, minor);
        }
@@ -5609,6 +5462,40 @@ static const struct file_operations i915_dpcd_fops = {
        .release = single_release,
 };
 
+static int i915_panel_show(struct seq_file *m, void *data)
+{
+       struct drm_connector *connector = m->private;
+       struct intel_dp *intel_dp =
+               enc_to_intel_dp(&intel_attached_encoder(connector)->base);
+
+       if (connector->status != connector_status_connected)
+               return -ENODEV;
+
+       seq_printf(m, "Panel power up delay: %d\n",
+                  intel_dp->panel_power_up_delay);
+       seq_printf(m, "Panel power down delay: %d\n",
+                  intel_dp->panel_power_down_delay);
+       seq_printf(m, "Backlight on delay: %d\n",
+                  intel_dp->backlight_on_delay);
+       seq_printf(m, "Backlight off delay: %d\n",
+                  intel_dp->backlight_off_delay);
+
+       return 0;
+}
+
+static int i915_panel_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, i915_panel_show, inode->i_private);
+}
+
+static const struct file_operations i915_panel_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_panel_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
 /**
  * i915_debugfs_connector_add - add i915 specific connector debugfs files
  * @connector: pointer to a registered drm_connector
@@ -5628,8 +5515,12 @@ int i915_debugfs_connector_add(struct drm_connector *connector)
 
        if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
            connector->connector_type == DRM_MODE_CONNECTOR_eDP)
-               debugfs_create_file("i915_dpcd", S_IRUGO, root, connector,
-                                   &i915_dpcd_fops);
+               debugfs_create_file("i915_dpcd", S_IRUGO, root,
+                                   connector, &i915_dpcd_fops);
+
+       if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
+               debugfs_create_file("i915_panel_timings", S_IRUGO, root,
+                                   connector, &i915_panel_fops);
 
        return 0;
 }
index 5de36d8..bfb2efd 100644 (file)
@@ -77,7 +77,7 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level,
              const char *fmt, ...)
 {
        static bool shown_bug_once;
-       struct device *dev = dev_priv->drm.dev;
+       struct device *kdev = dev_priv->drm.dev;
        bool is_error = level[1] <= KERN_ERR[1];
        bool is_debug = level[1] == KERN_DEBUG[1];
        struct va_format vaf;
@@ -91,11 +91,11 @@ __i915_printk(struct drm_i915_private *dev_priv, const char *level,
        vaf.fmt = fmt;
        vaf.va = &args;
 
-       dev_printk(level, dev, "[" DRM_NAME ":%ps] %pV",
+       dev_printk(level, kdev, "[" DRM_NAME ":%ps] %pV",
                   __builtin_return_address(0), &vaf);
 
        if (is_error && !shown_bug_once) {
-               dev_notice(dev, "%s", FDO_BUG_MSG);
+               dev_notice(kdev, "%s", FDO_BUG_MSG);
                shown_bug_once = true;
        }
 
@@ -228,31 +228,11 @@ static void intel_detect_pch(struct drm_device *dev)
        pci_dev_put(pch);
 }
 
-bool i915_semaphore_is_enabled(struct drm_i915_private *dev_priv)
-{
-       if (INTEL_GEN(dev_priv) < 6)
-               return false;
-
-       if (i915.semaphores >= 0)
-               return i915.semaphores;
-
-       /* TODO: make semaphores and Execlists play nicely together */
-       if (i915.enable_execlists)
-               return false;
-
-#ifdef CONFIG_INTEL_IOMMU
-       /* Enable semaphores on SNB when IO remapping is off */
-       if (IS_GEN6(dev_priv) && intel_iommu_gfx_mapped)
-               return false;
-#endif
-
-       return true;
-}
-
 static int i915_getparam(struct drm_device *dev, void *data,
                         struct drm_file *file_priv)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        drm_i915_getparam_t *param = data;
        int value;
 
@@ -263,13 +243,10 @@ static int i915_getparam(struct drm_device *dev, void *data,
                /* Reject all old ums/dri params. */
                return -ENODEV;
        case I915_PARAM_CHIPSET_ID:
-               value = dev->pdev->device;
+               value = pdev->device;
                break;
        case I915_PARAM_REVISION:
-               value = dev->pdev->revision;
-               break;
-       case I915_PARAM_HAS_GEM:
-               value = 1;
+               value = pdev->revision;
                break;
        case I915_PARAM_NUM_FENCES_AVAIL:
                value = dev_priv->num_fence_regs;
@@ -277,13 +254,6 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_OVERLAY:
                value = dev_priv->overlay ? 1 : 0;
                break;
-       case I915_PARAM_HAS_PAGEFLIPPING:
-               value = 1;
-               break;
-       case I915_PARAM_HAS_EXECBUF2:
-               /* depends on GEM */
-               value = 1;
-               break;
        case I915_PARAM_HAS_BSD:
                value = intel_engine_initialized(&dev_priv->engine[VCS]);
                break;
@@ -296,67 +266,34 @@ static int i915_getparam(struct drm_device *dev, void *data,
        case I915_PARAM_HAS_BSD2:
                value = intel_engine_initialized(&dev_priv->engine[VCS2]);
                break;
-       case I915_PARAM_HAS_RELAXED_FENCING:
-               value = 1;
-               break;
-       case I915_PARAM_HAS_COHERENT_RINGS:
-               value = 1;
-               break;
        case I915_PARAM_HAS_EXEC_CONSTANTS:
-               value = INTEL_INFO(dev)->gen >= 4;
-               break;
-       case I915_PARAM_HAS_RELAXED_DELTA:
-               value = 1;
-               break;
-       case I915_PARAM_HAS_GEN7_SOL_RESET:
-               value = 1;
+               value = INTEL_GEN(dev_priv) >= 4;
                break;
        case I915_PARAM_HAS_LLC:
-               value = HAS_LLC(dev);
+               value = HAS_LLC(dev_priv);
                break;
        case I915_PARAM_HAS_WT:
-               value = HAS_WT(dev);
+               value = HAS_WT(dev_priv);
                break;
        case I915_PARAM_HAS_ALIASING_PPGTT:
-               value = USES_PPGTT(dev);
-               break;
-       case I915_PARAM_HAS_WAIT_TIMEOUT:
-               value = 1;
+               value = USES_PPGTT(dev_priv);
                break;
        case I915_PARAM_HAS_SEMAPHORES:
-               value = i915_semaphore_is_enabled(dev_priv);
-               break;
-       case I915_PARAM_HAS_PRIME_VMAP_FLUSH:
-               value = 1;
+               value = i915.semaphores;
                break;
        case I915_PARAM_HAS_SECURE_BATCHES:
                value = capable(CAP_SYS_ADMIN);
                break;
-       case I915_PARAM_HAS_PINNED_BATCHES:
-               value = 1;
-               break;
-       case I915_PARAM_HAS_EXEC_NO_RELOC:
-               value = 1;
-               break;
-       case I915_PARAM_HAS_EXEC_HANDLE_LUT:
-               value = 1;
-               break;
        case I915_PARAM_CMD_PARSER_VERSION:
                value = i915_cmd_parser_get_version(dev_priv);
                break;
-       case I915_PARAM_HAS_COHERENT_PHYS_GTT:
-               value = 1;
-               break;
-       case I915_PARAM_MMAP_VERSION:
-               value = 1;
-               break;
        case I915_PARAM_SUBSLICE_TOTAL:
-               value = INTEL_INFO(dev)->subslice_total;
+               value = sseu_subslice_total(&INTEL_INFO(dev_priv)->sseu);
                if (!value)
                        return -ENODEV;
                break;
        case I915_PARAM_EU_TOTAL:
-               value = INTEL_INFO(dev)->eu_total;
+               value = INTEL_INFO(dev_priv)->sseu.eu_total;
                if (!value)
                        return -ENODEV;
                break;
@@ -364,16 +301,43 @@ static int i915_getparam(struct drm_device *dev, void *data,
                value = i915.enable_hangcheck && intel_has_gpu_reset(dev_priv);
                break;
        case I915_PARAM_HAS_RESOURCE_STREAMER:
-               value = HAS_RESOURCE_STREAMER(dev);
-               break;
-       case I915_PARAM_HAS_EXEC_SOFTPIN:
-               value = 1;
+               value = HAS_RESOURCE_STREAMER(dev_priv);
                break;
        case I915_PARAM_HAS_POOLED_EU:
-               value = HAS_POOLED_EU(dev);
+               value = HAS_POOLED_EU(dev_priv);
                break;
        case I915_PARAM_MIN_EU_IN_POOL:
-               value = INTEL_INFO(dev)->min_eu_in_pool;
+               value = INTEL_INFO(dev_priv)->sseu.min_eu_in_pool;
+               break;
+       case I915_PARAM_MMAP_GTT_VERSION:
+               /* Though we've started our numbering from 1, and so class all
+                * earlier versions as 0, in effect their value is undefined as
+                * the ioctl will report EINVAL for the unknown param!
+                */
+               value = i915_gem_mmap_gtt_version();
+               break;
+       case I915_PARAM_MMAP_VERSION:
+               /* Remember to bump this if the version changes! */
+       case I915_PARAM_HAS_GEM:
+       case I915_PARAM_HAS_PAGEFLIPPING:
+       case I915_PARAM_HAS_EXECBUF2: /* depends on GEM */
+       case I915_PARAM_HAS_RELAXED_FENCING:
+       case I915_PARAM_HAS_COHERENT_RINGS:
+       case I915_PARAM_HAS_RELAXED_DELTA:
+       case I915_PARAM_HAS_GEN7_SOL_RESET:
+       case I915_PARAM_HAS_WAIT_TIMEOUT:
+       case I915_PARAM_HAS_PRIME_VMAP_FLUSH:
+       case I915_PARAM_HAS_PINNED_BATCHES:
+       case I915_PARAM_HAS_EXEC_NO_RELOC:
+       case I915_PARAM_HAS_EXEC_HANDLE_LUT:
+       case I915_PARAM_HAS_COHERENT_PHYS_GTT:
+       case I915_PARAM_HAS_EXEC_SOFTPIN:
+               /* For the time being all of these are always true;
+                * if some supported hardware does not have one of these
+                * features this value needs to be provided from
+                * INTEL_INFO(), a feature macro, or similar.
+                */
+               value = 1;
                break;
        default:
                DRM_DEBUG("Unknown parameter %d\n", param->param);
@@ -537,7 +501,7 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
                pr_info("switched on\n");
                dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
                /* i915 resume handler doesn't set to D0 */
-               pci_set_power_state(dev->pdev, PCI_D0);
+               pci_set_power_state(pdev, PCI_D0);
                i915_resume_switcheroo(dev);
                dev->switch_power_state = DRM_SWITCH_POWER_ON;
        } else {
@@ -595,7 +559,6 @@ static void i915_gem_fini(struct drm_device *dev)
        }
 
        mutex_lock(&dev->struct_mutex);
-       i915_gem_reset(dev);
        i915_gem_cleanup_engines(dev);
        i915_gem_context_fini(dev);
        mutex_unlock(&dev->struct_mutex);
@@ -606,6 +569,7 @@ static void i915_gem_fini(struct drm_device *dev)
 static int i915_load_modeset_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        int ret;
 
        if (i915_inject_load_failure())
@@ -622,13 +586,13 @@ static int i915_load_modeset_init(struct drm_device *dev)
         * then we do not take part in VGA arbitration and the
         * vga_client_register() fails with -ENODEV.
         */
-       ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode);
+       ret = vga_client_register(pdev, dev, NULL, i915_vga_set_decode);
        if (ret && ret != -ENODEV)
                goto out;
 
        intel_register_dsm_handler();
 
-       ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, false);
+       ret = vga_switcheroo_register_client(pdev, &i915_switcheroo_ops, false);
        if (ret)
                goto cleanup_vga_client;
 
@@ -680,9 +644,9 @@ cleanup_irq:
 cleanup_csr:
        intel_csr_ucode_fini(dev_priv);
        intel_power_domains_fini(dev_priv);
-       vga_switcheroo_unregister_client(dev->pdev);
+       vga_switcheroo_unregister_client(pdev);
 cleanup_vga_client:
-       vga_client_register(dev->pdev, NULL, NULL, NULL);
+       vga_client_register(pdev, NULL, NULL, NULL);
 out:
        return ret;
 }
@@ -706,7 +670,7 @@ static int i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
        primary =
                pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 
-       ret = remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
+       ret = drm_fb_helper_remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
 
        kfree(ap);
 
@@ -848,6 +812,8 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
        mutex_init(&dev_priv->wm.wm_mutex);
        mutex_init(&dev_priv->pps_mutex);
 
+       i915_memcpy_init_early(dev_priv);
+
        ret = i915_workqueues_init(dev_priv);
        if (ret < 0)
                return ret;
@@ -868,7 +834,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv,
        intel_init_audio_hooks(dev_priv);
        i915_gem_load_init(&dev_priv->drm);
 
-       intel_display_crc_init(&dev_priv->drm);
+       intel_display_crc_init(dev_priv);
 
        intel_device_info_dump(dev_priv);
 
@@ -900,6 +866,7 @@ static void i915_driver_cleanup_early(struct drm_i915_private *dev_priv)
 static int i915_mmio_setup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        int mmio_bar;
        int mmio_size;
 
@@ -916,7 +883,7 @@ static int i915_mmio_setup(struct drm_device *dev)
                mmio_size = 512 * 1024;
        else
                mmio_size = 2 * 1024 * 1024;
-       dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size);
+       dev_priv->regs = pci_iomap(pdev, mmio_bar, mmio_size);
        if (dev_priv->regs == NULL) {
                DRM_ERROR("failed to map registers\n");
 
@@ -932,9 +899,10 @@ static int i915_mmio_setup(struct drm_device *dev)
 static void i915_mmio_cleanup(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
 
        intel_teardown_mchbar(dev);
-       pci_iounmap(dev->pdev, dev_priv->regs);
+       pci_iounmap(pdev, dev_priv->regs);
 }
 
 /**
@@ -999,6 +967,9 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
        i915.enable_ppgtt =
                intel_sanitize_enable_ppgtt(dev_priv, i915.enable_ppgtt);
        DRM_DEBUG_DRIVER("ppgtt mode: %i\n", i915.enable_ppgtt);
+
+       i915.semaphores = intel_sanitize_semaphores(dev_priv, i915.semaphores);
+       DRM_DEBUG_DRIVER("use GPU sempahores? %s\n", yesno(i915.semaphores));
 }
 
 /**
@@ -1010,9 +981,8 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
  */
 static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
 {
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct drm_device *dev = &dev_priv->drm;
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       uint32_t aperture_size;
        int ret;
 
        if (i915_inject_load_failure())
@@ -1022,16 +992,10 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
 
        intel_sanitize_options(dev_priv);
 
-       ret = i915_ggtt_init_hw(dev);
+       ret = i915_ggtt_probe_hw(dev_priv);
        if (ret)
                return ret;
 
-       ret = i915_ggtt_enable_hw(dev);
-       if (ret) {
-               DRM_ERROR("failed to enable GGTT\n");
-               goto out_ggtt;
-       }
-
        /* WARNING: Apparently we must kick fbdev drivers before vgacon,
         * otherwise the vga fbdev driver falls over. */
        ret = i915_kick_out_firmware_fb(dev_priv);
@@ -1046,11 +1010,21 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
                goto out_ggtt;
        }
 
-       pci_set_master(dev->pdev);
+       ret = i915_ggtt_init_hw(dev_priv);
+       if (ret)
+               return ret;
+
+       ret = i915_ggtt_enable_hw(dev_priv);
+       if (ret) {
+               DRM_ERROR("failed to enable GGTT\n");
+               goto out_ggtt;
+       }
+
+       pci_set_master(pdev);
 
        /* overlay on gen2 is broken and can't address above 1G */
        if (IS_GEN2(dev)) {
-               ret = dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30));
+               ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(30));
                if (ret) {
                        DRM_ERROR("failed to set DMA mask\n");
 
@@ -1058,7 +1032,6 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
                }
        }
 
-
        /* 965GM sometimes incorrectly writes to hardware status page (HWS)
         * using 32bit addressing, overwriting memory if HWS is located
         * above 4GB.
@@ -1068,7 +1041,7 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
         * which also needs to be handled carefully.
         */
        if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) {
-               ret = dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32));
+               ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 
                if (ret) {
                        DRM_ERROR("failed to set DMA mask\n");
@@ -1077,19 +1050,6 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
                }
        }
 
-       aperture_size = ggtt->mappable_end;
-
-       ggtt->mappable =
-               io_mapping_create_wc(ggtt->mappable_base,
-                                    aperture_size);
-       if (!ggtt->mappable) {
-               ret = -EIO;
-               goto out_ggtt;
-       }
-
-       ggtt->mtrr = arch_phys_wc_add(ggtt->mappable_base,
-                                             aperture_size);
-
        pm_qos_add_request(&dev_priv->pm_qos, PM_QOS_CPU_DMA_LATENCY,
                           PM_QOS_DEFAULT_VALUE);
 
@@ -1111,14 +1071,14 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
         * stuck interrupts on some machines.
         */
        if (!IS_I945G(dev) && !IS_I945GM(dev)) {
-               if (pci_enable_msi(dev->pdev) < 0)
+               if (pci_enable_msi(pdev) < 0)
                        DRM_DEBUG_DRIVER("can't enable MSI");
        }
 
        return 0;
 
 out_ggtt:
-       i915_ggtt_cleanup_hw(dev);
+       i915_ggtt_cleanup_hw(dev_priv);
 
        return ret;
 }
@@ -1129,16 +1089,13 @@ out_ggtt:
  */
 static void i915_driver_cleanup_hw(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
 
-       if (dev->pdev->msi_enabled)
-               pci_disable_msi(dev->pdev);
+       if (pdev->msi_enabled)
+               pci_disable_msi(pdev);
 
        pm_qos_remove_request(&dev_priv->pm_qos);
-       arch_phys_wc_del(ggtt->mtrr);
-       io_mapping_free(ggtt->mappable);
-       i915_ggtt_cleanup_hw(dev);
+       i915_ggtt_cleanup_hw(dev_priv);
 }
 
 /**
@@ -1164,7 +1121,7 @@ static void i915_driver_register(struct drm_i915_private *dev_priv)
        /* Reveal our presence to userspace */
        if (drm_dev_register(dev, 0) == 0) {
                i915_debugfs_register(dev_priv);
-               i915_setup_sysfs(dev);
+               i915_setup_sysfs(dev_priv);
        } else
                DRM_ERROR("Failed to register driver for userspace access!\n");
 
@@ -1201,7 +1158,7 @@ static void i915_driver_unregister(struct drm_i915_private *dev_priv)
        acpi_video_unregister();
        intel_opregion_unregister(dev_priv);
 
-       i915_teardown_sysfs(&dev_priv->drm);
+       i915_teardown_sysfs(dev_priv);
        i915_debugfs_unregister(dev_priv);
        drm_dev_unregister(&dev_priv->drm);
 
@@ -1310,6 +1267,7 @@ out_free_priv:
 void i915_driver_unload(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
 
        intel_fbdev_fini(dev);
 
@@ -1338,8 +1296,8 @@ void i915_driver_unload(struct drm_device *dev)
        kfree(dev_priv->vbt.lfp_lvds_vbt_mode);
        dev_priv->vbt.lfp_lvds_vbt_mode = NULL;
 
-       vga_switcheroo_unregister_client(dev->pdev);
-       vga_client_register(dev->pdev, NULL, NULL, NULL);
+       vga_switcheroo_unregister_client(pdev);
+       vga_client_register(pdev, NULL, NULL, NULL);
 
        intel_csr_ucode_fini(dev_priv);
 
@@ -1348,7 +1306,7 @@ void i915_driver_unload(struct drm_device *dev)
        i915_destroy_error_state(dev);
 
        /* Flush any outstanding unpin_work. */
-       flush_workqueue(dev_priv->wq);
+       drain_workqueue(dev_priv->wq);
 
        intel_guc_fini(dev);
        i915_gem_fini(dev);
@@ -1436,6 +1394,7 @@ static bool suspend_to_idle(struct drm_i915_private *dev_priv)
 static int i915_drm_suspend(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        pci_power_t opregion_target_state;
        int error;
 
@@ -1452,19 +1411,17 @@ static int i915_drm_suspend(struct drm_device *dev)
 
        drm_kms_helper_poll_disable(dev);
 
-       pci_save_state(dev->pdev);
+       pci_save_state(pdev);
 
        error = i915_gem_suspend(dev);
        if (error) {
-               dev_err(&dev->pdev->dev,
+               dev_err(&pdev->dev,
                        "GEM idle failed, resume might fail\n");
                goto out;
        }
 
        intel_guc_suspend(dev);
 
-       intel_suspend_gt_powersave(dev_priv);
-
        intel_display_suspend(dev);
 
        intel_dp_mst_suspend(dev);
@@ -1500,9 +1457,10 @@ out:
        return error;
 }
 
-static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
+static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation)
 {
-       struct drm_i915_private *dev_priv = to_i915(drm_dev);
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        bool fw_csr;
        int ret;
 
@@ -1536,7 +1494,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
                goto out;
        }
 
-       pci_disable_device(drm_dev->pdev);
+       pci_disable_device(pdev);
        /*
         * During hibernation on some platforms the BIOS may try to access
         * the device even though it's already in D3 and hang the machine. So
@@ -1550,7 +1508,7 @@ static int i915_drm_suspend_late(struct drm_device *drm_dev, bool hibernation)
         * Acer Aspire 1830T
         */
        if (!(hibernation && INTEL_INFO(dev_priv)->gen < 6))
-               pci_set_power_state(drm_dev->pdev, PCI_D3hot);
+               pci_set_power_state(pdev, PCI_D3hot);
 
        dev_priv->suspended_to_idle = suspend_to_idle(dev_priv);
 
@@ -1590,18 +1548,18 @@ static int i915_drm_resume(struct drm_device *dev)
        int ret;
 
        disable_rpm_wakeref_asserts(dev_priv);
+       intel_sanitize_gt_powersave(dev_priv);
 
-       ret = i915_ggtt_enable_hw(dev);
+       ret = i915_ggtt_enable_hw(dev_priv);
        if (ret)
                DRM_ERROR("failed to re-enable GGTT\n");
 
        intel_csr_ucode_resume(dev_priv);
 
-       mutex_lock(&dev->struct_mutex);
-       i915_gem_restore_gtt_mappings(dev);
-       mutex_unlock(&dev->struct_mutex);
+       i915_gem_resume(dev);
 
        i915_restore_state(dev);
+       intel_pps_unlock_regs_wa(dev_priv);
        intel_opregion_setup(dev_priv);
 
        intel_init_pch_refclk(dev);
@@ -1620,7 +1578,7 @@ static int i915_drm_resume(struct drm_device *dev)
        mutex_lock(&dev->struct_mutex);
        if (i915_gem_init_hw(dev)) {
                DRM_ERROR("failed to re-initialize GPU, declaring wedged!\n");
-               atomic_or(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+               i915_gem_set_wedged(dev_priv);
        }
        mutex_unlock(&dev->struct_mutex);
 
@@ -1657,6 +1615,7 @@ static int i915_drm_resume(struct drm_device *dev)
 
        intel_opregion_notify_adapter(dev_priv, PCI_D0);
 
+       intel_autoenable_gt_powersave(dev_priv);
        drm_kms_helper_poll_enable(dev);
 
        enable_rpm_wakeref_asserts(dev_priv);
@@ -1667,6 +1626,7 @@ static int i915_drm_resume(struct drm_device *dev)
 static int i915_drm_resume_early(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        int ret;
 
        /*
@@ -1689,7 +1649,7 @@ static int i915_drm_resume_early(struct drm_device *dev)
         * the device powered we can also remove the following set power state
         * call.
         */
-       ret = pci_set_power_state(dev->pdev, PCI_D0);
+       ret = pci_set_power_state(pdev, PCI_D0);
        if (ret) {
                DRM_ERROR("failed to set PCI D0 power state (%d)\n", ret);
                goto out;
@@ -1708,12 +1668,12 @@ static int i915_drm_resume_early(struct drm_device *dev)
         * depend on the device enable refcount we can't anyway depend on them
         * disabling/enabling the device.
         */
-       if (pci_enable_device(dev->pdev)) {
+       if (pci_enable_device(pdev)) {
                ret = -EIO;
                goto out;
        }
 
-       pci_set_master(dev->pdev);
+       pci_set_master(pdev);
 
        disable_rpm_wakeref_asserts(dev_priv);
 
@@ -1765,8 +1725,10 @@ int i915_resume_switcheroo(struct drm_device *dev)
  * i915_reset - reset chip after a hang
  * @dev: drm device to reset
  *
- * Reset the chip.  Useful if a hang is detected. Returns zero on successful
- * reset or otherwise an error code.
+ * Reset the chip.  Useful if a hang is detected. Marks the device as wedged
+ * on failure.
+ *
+ * Caller must hold the struct_mutex.
  *
  * Procedure is fairly simple:
  *   - reset the chip using the reset reg
@@ -1776,31 +1738,22 @@ int i915_resume_switcheroo(struct drm_device *dev)
  *   - re-init interrupt state
  *   - re-init display
  */
-int i915_reset(struct drm_i915_private *dev_priv)
+void i915_reset(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = &dev_priv->drm;
        struct i915_gpu_error *error = &dev_priv->gpu_error;
-       unsigned reset_counter;
        int ret;
 
-       intel_reset_gt_powersave(dev_priv);
+       lockdep_assert_held(&dev->struct_mutex);
 
-       mutex_lock(&dev->struct_mutex);
+       if (!test_and_clear_bit(I915_RESET_IN_PROGRESS, &error->flags))
+               return;
 
        /* Clear any previous failed attempts at recovery. Time to try again. */
-       atomic_andnot(I915_WEDGED, &error->reset_counter);
-
-       /* Clear the reset-in-progress flag and increment the reset epoch. */
-       reset_counter = atomic_inc_return(&error->reset_counter);
-       if (WARN_ON(__i915_reset_in_progress(reset_counter))) {
-               ret = -EIO;
-               goto error;
-       }
+       __clear_bit(I915_WEDGED, &error->flags);
+       error->reset_count++;
 
        pr_notice("drm/i915: Resetting chip after gpu hang\n");
-
-       i915_gem_reset(dev);
-
        ret = intel_gpu_reset(dev_priv, ALL_ENGINES);
        if (ret) {
                if (ret != -ENODEV)
@@ -1810,6 +1763,7 @@ int i915_reset(struct drm_i915_private *dev_priv)
                goto error;
        }
 
+       i915_gem_reset(dev_priv);
        intel_overlay_reset(dev_priv);
 
        /* Ok, now get things going again... */
@@ -1832,44 +1786,34 @@ int i915_reset(struct drm_i915_private *dev_priv)
                goto error;
        }
 
-       mutex_unlock(&dev->struct_mutex);
-
-       /*
-        * rps/rc6 re-init is necessary to restore state lost after the
-        * reset and the re-install of gt irqs. Skip for ironlake per
-        * previous concerns that it doesn't respond well to some forms
-        * of re-init after reset.
-        */
-       if (INTEL_INFO(dev)->gen > 5)
-               intel_enable_gt_powersave(dev_priv);
-
-       return 0;
+wakeup:
+       wake_up_bit(&error->flags, I915_RESET_IN_PROGRESS);
+       return;
 
 error:
-       atomic_or(I915_WEDGED, &error->reset_counter);
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
+       i915_gem_set_wedged(dev_priv);
+       goto wakeup;
 }
 
-static int i915_pm_suspend(struct device *dev)
+static int i915_pm_suspend(struct device *kdev)
 {
-       struct pci_dev *pdev = to_pci_dev(dev);
-       struct drm_device *drm_dev = pci_get_drvdata(pdev);
+       struct pci_dev *pdev = to_pci_dev(kdev);
+       struct drm_device *dev = pci_get_drvdata(pdev);
 
-       if (!drm_dev) {
-               dev_err(dev, "DRM not initialized, aborting suspend.\n");
+       if (!dev) {
+               dev_err(kdev, "DRM not initialized, aborting suspend.\n");
                return -ENODEV;
        }
 
-       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+       if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       return i915_drm_suspend(drm_dev);
+       return i915_drm_suspend(dev);
 }
 
-static int i915_pm_suspend_late(struct device *dev)
+static int i915_pm_suspend_late(struct device *kdev)
 {
-       struct drm_device *drm_dev = &dev_to_i915(dev)->drm;
+       struct drm_device *dev = &kdev_to_i915(kdev)->drm;
 
        /*
         * We have a suspend ordering issue with the snd-hda driver also
@@ -1880,57 +1824,67 @@ static int i915_pm_suspend_late(struct device *dev)
         * FIXME: This should be solved with a special hdmi sink device or
         * similar so that power domains can be employed.
         */
-       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+       if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       return i915_drm_suspend_late(drm_dev, false);
+       return i915_drm_suspend_late(dev, false);
 }
 
-static int i915_pm_poweroff_late(struct device *dev)
+static int i915_pm_poweroff_late(struct device *kdev)
 {
-       struct drm_device *drm_dev = &dev_to_i915(dev)->drm;
+       struct drm_device *dev = &kdev_to_i915(kdev)->drm;
 
-       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+       if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       return i915_drm_suspend_late(drm_dev, true);
+       return i915_drm_suspend_late(dev, true);
 }
 
-static int i915_pm_resume_early(struct device *dev)
+static int i915_pm_resume_early(struct device *kdev)
 {
-       struct drm_device *drm_dev = &dev_to_i915(dev)->drm;
+       struct drm_device *dev = &kdev_to_i915(kdev)->drm;
 
-       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+       if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       return i915_drm_resume_early(drm_dev);
+       return i915_drm_resume_early(dev);
 }
 
-static int i915_pm_resume(struct device *dev)
+static int i915_pm_resume(struct device *kdev)
 {
-       struct drm_device *drm_dev = &dev_to_i915(dev)->drm;
+       struct drm_device *dev = &kdev_to_i915(kdev)->drm;
 
-       if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF)
+       if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
                return 0;
 
-       return i915_drm_resume(drm_dev);
+       return i915_drm_resume(dev);
 }
 
 /* freeze: before creating the hibernation_image */
-static int i915_pm_freeze(struct device *dev)
+static int i915_pm_freeze(struct device *kdev)
 {
-       return i915_pm_suspend(dev);
+       int ret;
+
+       ret = i915_pm_suspend(kdev);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_freeze(kdev_to_i915(kdev));
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
-static int i915_pm_freeze_late(struct device *dev)
+static int i915_pm_freeze_late(struct device *kdev)
 {
        int ret;
 
-       ret = i915_pm_suspend_late(dev);
+       ret = i915_pm_suspend_late(kdev);
        if (ret)
                return ret;
 
-       ret = i915_gem_freeze_late(dev_to_i915(dev));
+       ret = i915_gem_freeze_late(kdev_to_i915(kdev));
        if (ret)
                return ret;
 
@@ -1938,25 +1892,25 @@ static int i915_pm_freeze_late(struct device *dev)
 }
 
 /* thaw: called after creating the hibernation image, but before turning off. */
-static int i915_pm_thaw_early(struct device *dev)
+static int i915_pm_thaw_early(struct device *kdev)
 {
-       return i915_pm_resume_early(dev);
+       return i915_pm_resume_early(kdev);
 }
 
-static int i915_pm_thaw(struct device *dev)
+static int i915_pm_thaw(struct device *kdev)
 {
-       return i915_pm_resume(dev);
+       return i915_pm_resume(kdev);
 }
 
 /* restore: called after loading the hibernation image. */
-static int i915_pm_restore_early(struct device *dev)
+static int i915_pm_restore_early(struct device *kdev)
 {
-       return i915_pm_resume_early(dev);
+       return i915_pm_resume_early(kdev);
 }
 
-static int i915_pm_restore(struct device *dev)
+static int i915_pm_restore(struct device *kdev)
 {
-       return i915_pm_resume(dev);
+       return i915_pm_resume(kdev);
 }
 
 /*
@@ -2318,9 +2272,9 @@ static int vlv_resume_prepare(struct drm_i915_private *dev_priv,
        return ret;
 }
 
-static int intel_runtime_suspend(struct device *device)
+static int intel_runtime_suspend(struct device *kdev)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
+       struct pci_dev *pdev = to_pci_dev(kdev);
        struct drm_device *dev = pci_get_drvdata(pdev);
        struct drm_i915_private *dev_priv = to_i915(dev);
        int ret;
@@ -2346,7 +2300,7 @@ static int intel_runtime_suspend(struct device *device)
                 * Bump the expiration timestamp, otherwise the suspend won't
                 * be rescheduled.
                 */
-               pm_runtime_mark_last_busy(device);
+               pm_runtime_mark_last_busy(kdev);
 
                return -EAGAIN;
        }
@@ -2425,9 +2379,9 @@ static int intel_runtime_suspend(struct device *device)
        return 0;
 }
 
-static int intel_runtime_resume(struct device *device)
+static int intel_runtime_resume(struct device *kdev)
 {
-       struct pci_dev *pdev = to_pci_dev(device);
+       struct pci_dev *pdev = to_pci_dev(kdev);
        struct drm_device *dev = pci_get_drvdata(pdev);
        struct drm_i915_private *dev_priv = to_i915(dev);
        int ret = 0;
@@ -2467,7 +2421,6 @@ static int intel_runtime_resume(struct device *device)
         * we can do is to hope that things will still work (and disable RPM).
         */
        i915_gem_init_swizzling(dev);
-       gen6_update_ring_freq(dev_priv);
 
        intel_runtime_pm_enable_interrupts(dev_priv);
 
@@ -2623,6 +2576,7 @@ static struct drm_driver driver = {
        .postclose = i915_driver_postclose,
        .set_busid = drm_pci_set_busid,
 
+       .gem_close_object = i915_gem_close_object,
        .gem_free_object = i915_gem_free_object,
        .gem_vm_ops = &i915_gem_vm_ops,
 
index f68c789..8b9ee4e 100644 (file)
@@ -61,6 +61,7 @@
 #include "i915_gem.h"
 #include "i915_gem_gtt.h"
 #include "i915_gem_render_state.h"
+#include "i915_gem_request.h"
 
 #include "intel_gvt.h"
 
@@ -69,7 +70,7 @@
 
 #define DRIVER_NAME            "i915"
 #define DRIVER_DESC            "Intel Graphics"
-#define DRIVER_DATE            "20160711"
+#define DRIVER_DATE            "20160919"
 
 #undef WARN_ON
 /* Many gcc seem to no see through this and fall over :( */
@@ -401,7 +402,7 @@ struct drm_i915_file_private {
                unsigned boosts;
        } rps;
 
-       unsigned int bsd_ring;
+       unsigned int bsd_engine;
 };
 
 /* Used by dp and fdi links */
@@ -431,8 +432,6 @@ void intel_link_compute_m_n(int bpp, int nlanes,
 #define DRIVER_MINOR           6
 #define DRIVER_PATCHLEVEL      0
 
-#define WATCH_LISTS    0
-
 struct opregion_header;
 struct opregion_acpi;
 struct opregion_swsci;
@@ -456,15 +455,21 @@ struct intel_opregion {
 struct intel_overlay;
 struct intel_overlay_error_state;
 
-#define I915_FENCE_REG_NONE -1
-#define I915_MAX_NUM_FENCES 32
-/* 32 fences + sign bit for FENCE_REG_NONE */
-#define I915_MAX_NUM_FENCE_BITS 6
-
 struct drm_i915_fence_reg {
-       struct list_head lru_list;
-       struct drm_i915_gem_object *obj;
+       struct list_head link;
+       struct drm_i915_private *i915;
+       struct i915_vma *vma;
        int pin_count;
+       int id;
+       /**
+        * Whether the tiling parameters for the currently
+        * associated fence register have changed. Note that
+        * for the purposes of tracking tiling changes we also
+        * treat the unfenced register, the register slot that
+        * the object occupies whilst it executes a fenced
+        * command (such as BLT on gen2/3), as a "fence".
+        */
+       bool dirty;
 };
 
 struct sdvo_device_mapping {
@@ -476,130 +481,6 @@ struct sdvo_device_mapping {
        u8 ddc_pin;
 };
 
-struct intel_display_error_state;
-
-struct drm_i915_error_state {
-       struct kref ref;
-       struct timeval time;
-
-       char error_msg[128];
-       bool simulated;
-       int iommu;
-       u32 reset_count;
-       u32 suspend_count;
-
-       /* Generic register state */
-       u32 eir;
-       u32 pgtbl_er;
-       u32 ier;
-       u32 gtier[4];
-       u32 ccid;
-       u32 derrmr;
-       u32 forcewake;
-       u32 error; /* gen6+ */
-       u32 err_int; /* gen7 */
-       u32 fault_data0; /* gen8, gen9 */
-       u32 fault_data1; /* gen8, gen9 */
-       u32 done_reg;
-       u32 gac_eco;
-       u32 gam_ecochk;
-       u32 gab_ctl;
-       u32 gfx_mode;
-       u32 extra_instdone[I915_NUM_INSTDONE_REG];
-       u64 fence[I915_MAX_NUM_FENCES];
-       struct intel_overlay_error_state *overlay;
-       struct intel_display_error_state *display;
-       struct drm_i915_error_object *semaphore_obj;
-
-       struct drm_i915_error_ring {
-               bool valid;
-               /* Software tracked state */
-               bool waiting;
-               int num_waiters;
-               int hangcheck_score;
-               enum intel_ring_hangcheck_action hangcheck_action;
-               int num_requests;
-
-               /* our own tracking of ring head and tail */
-               u32 cpu_ring_head;
-               u32 cpu_ring_tail;
-
-               u32 last_seqno;
-               u32 semaphore_seqno[I915_NUM_ENGINES - 1];
-
-               /* Register state */
-               u32 start;
-               u32 tail;
-               u32 head;
-               u32 ctl;
-               u32 hws;
-               u32 ipeir;
-               u32 ipehr;
-               u32 instdone;
-               u32 bbstate;
-               u32 instpm;
-               u32 instps;
-               u32 seqno;
-               u64 bbaddr;
-               u64 acthd;
-               u32 fault_reg;
-               u64 faddr;
-               u32 rc_psmi; /* sleep state */
-               u32 semaphore_mboxes[I915_NUM_ENGINES - 1];
-
-               struct drm_i915_error_object {
-                       int page_count;
-                       u64 gtt_offset;
-                       u32 *pages[0];
-               } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
-
-               struct drm_i915_error_object *wa_ctx;
-
-               struct drm_i915_error_request {
-                       long jiffies;
-                       u32 seqno;
-                       u32 tail;
-               } *requests;
-
-               struct drm_i915_error_waiter {
-                       char comm[TASK_COMM_LEN];
-                       pid_t pid;
-                       u32 seqno;
-               } *waiters;
-
-               struct {
-                       u32 gfx_mode;
-                       union {
-                               u64 pdp[4];
-                               u32 pp_dir_base;
-                       };
-               } vm_info;
-
-               pid_t pid;
-               char comm[TASK_COMM_LEN];
-       } ring[I915_NUM_ENGINES];
-
-       struct drm_i915_error_buffer {
-               u32 size;
-               u32 name;
-               u32 rseqno[I915_NUM_ENGINES], wseqno;
-               u64 gtt_offset;
-               u32 read_domains;
-               u32 write_domain;
-               s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
-               s32 pinned:2;
-               u32 tiling:2;
-               u32 dirty:1;
-               u32 purgeable:1;
-               u32 userptr:1;
-               s32 ring:4;
-               u32 cache_level:3;
-       } **active_bo, **pinned_bo;
-
-       u32 *active_bo_count, *pinned_bo_count;
-       u32 vm_count;
-};
-
 struct intel_connector;
 struct intel_encoder;
 struct intel_crtc_state;
@@ -629,8 +510,12 @@ struct drm_i915_display_funcs {
                                         struct intel_initial_plane_config *);
        int (*crtc_compute_clock)(struct intel_crtc *crtc,
                                  struct intel_crtc_state *crtc_state);
-       void (*crtc_enable)(struct drm_crtc *crtc);
-       void (*crtc_disable)(struct drm_crtc *crtc);
+       void (*crtc_enable)(struct intel_crtc_state *pipe_config,
+                           struct drm_atomic_state *old_state);
+       void (*crtc_disable)(struct intel_crtc_state *old_crtc_state,
+                            struct drm_atomic_state *old_state);
+       void (*update_crtcs)(struct drm_atomic_state *state,
+                            unsigned int *crtc_vblank_mask);
        void (*audio_codec_enable)(struct drm_connector *connector,
                                   struct intel_encoder *encoder,
                                   const struct drm_display_mode *adjusted_mode);
@@ -694,8 +579,6 @@ struct intel_uncore_funcs {
                                uint16_t val, bool trace);
        void (*mmio_writel)(struct drm_i915_private *dev_priv, i915_reg_t r,
                                uint32_t val, bool trace);
-       void (*mmio_writeq)(struct drm_i915_private *dev_priv, i915_reg_t r,
-                               uint64_t val, bool trace);
 };
 
 struct intel_uncore {
@@ -756,7 +639,7 @@ struct intel_csr {
        func(is_i915g) sep \
        func(is_i945gm) sep \
        func(is_g33) sep \
-       func(need_gfx_hws) sep \
+       func(hws_needs_physical) sep \
        func(is_g4x) sep \
        func(is_pineview) sep \
        func(is_broadwater) sep \
@@ -771,6 +654,19 @@ struct intel_csr {
        func(is_kabylake) sep \
        func(is_preliminary) sep \
        func(has_fbc) sep \
+       func(has_psr) sep \
+       func(has_runtime_pm) sep \
+       func(has_csr) sep \
+       func(has_resource_streamer) sep \
+       func(has_rc6) sep \
+       func(has_rc6p) sep \
+       func(has_dp_mst) sep \
+       func(has_gmbus_irq) sep \
+       func(has_hw_contexts) sep \
+       func(has_logical_ring_contexts) sep \
+       func(has_l3_dpf) sep \
+       func(has_gmch_display) sep \
+       func(has_guc) sep \
        func(has_pipe_cxsr) sep \
        func(has_hotplug) sep \
        func(cursor_needs_physical) sep \
@@ -786,6 +682,24 @@ struct intel_csr {
 #define DEFINE_FLAG(name) u8 name:1
 #define SEP_SEMICOLON ;
 
+struct sseu_dev_info {
+       u8 slice_mask;
+       u8 subslice_mask;
+       u8 eu_total;
+       u8 eu_per_subslice;
+       u8 min_eu_in_pool;
+       /* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */
+       u8 subslice_7eu[3];
+       u8 has_slice_pg:1;
+       u8 has_subslice_pg:1;
+       u8 has_eu_pg:1;
+};
+
+static inline unsigned int sseu_subslice_total(const struct sseu_dev_info *sseu)
+{
+       return hweight8(sseu->slice_mask) * hweight8(sseu->subslice_mask);
+}
+
 struct intel_device_info {
        u32 display_mmio_offset;
        u16 device_id;
@@ -794,7 +708,9 @@ struct intel_device_info {
        u8 gen;
        u16 gen_mask;
        u8 ring_mask; /* Rings supported by the HW */
+       u8 num_rings;
        DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
+       u16 ddb_size; /* in blocks */
        /* Register offsets for the various display pipes and transcoders */
        int pipe_offsets[I915_MAX_TRANSCODERS];
        int trans_offsets[I915_MAX_TRANSCODERS];
@@ -802,17 +718,7 @@ struct intel_device_info {
        int cursor_offsets[I915_MAX_PIPES];
 
        /* Slice/subslice/EU info */
-       u8 slice_total;
-       u8 subslice_total;
-       u8 subslice_per_slice;
-       u8 eu_total;
-       u8 eu_per_subslice;
-       u8 min_eu_in_pool;
-       /* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */
-       u8 subslice_7eu[3];
-       u8 has_slice_pg:1;
-       u8 has_subslice_pg:1;
-       u8 has_eu_pg:1;
+       struct sseu_dev_info sseu;
 
        struct color_luts {
                u16 degamma_lut_size;
@@ -823,6 +729,134 @@ struct intel_device_info {
 #undef DEFINE_FLAG
 #undef SEP_SEMICOLON
 
+struct intel_display_error_state;
+
+struct drm_i915_error_state {
+       struct kref ref;
+       struct timeval time;
+
+       char error_msg[128];
+       bool simulated;
+       int iommu;
+       u32 reset_count;
+       u32 suspend_count;
+       struct intel_device_info device_info;
+
+       /* Generic register state */
+       u32 eir;
+       u32 pgtbl_er;
+       u32 ier;
+       u32 gtier[4];
+       u32 ccid;
+       u32 derrmr;
+       u32 forcewake;
+       u32 error; /* gen6+ */
+       u32 err_int; /* gen7 */
+       u32 fault_data0; /* gen8, gen9 */
+       u32 fault_data1; /* gen8, gen9 */
+       u32 done_reg;
+       u32 gac_eco;
+       u32 gam_ecochk;
+       u32 gab_ctl;
+       u32 gfx_mode;
+       u32 extra_instdone[I915_NUM_INSTDONE_REG];
+       u64 fence[I915_MAX_NUM_FENCES];
+       struct intel_overlay_error_state *overlay;
+       struct intel_display_error_state *display;
+       struct drm_i915_error_object *semaphore;
+
+       struct drm_i915_error_engine {
+               int engine_id;
+               /* Software tracked state */
+               bool waiting;
+               int num_waiters;
+               int hangcheck_score;
+               enum intel_engine_hangcheck_action hangcheck_action;
+               struct i915_address_space *vm;
+               int num_requests;
+
+               /* our own tracking of ring head and tail */
+               u32 cpu_ring_head;
+               u32 cpu_ring_tail;
+
+               u32 last_seqno;
+               u32 semaphore_seqno[I915_NUM_ENGINES - 1];
+
+               /* Register state */
+               u32 start;
+               u32 tail;
+               u32 head;
+               u32 ctl;
+               u32 mode;
+               u32 hws;
+               u32 ipeir;
+               u32 ipehr;
+               u32 instdone;
+               u32 bbstate;
+               u32 instpm;
+               u32 instps;
+               u32 seqno;
+               u64 bbaddr;
+               u64 acthd;
+               u32 fault_reg;
+               u64 faddr;
+               u32 rc_psmi; /* sleep state */
+               u32 semaphore_mboxes[I915_NUM_ENGINES - 1];
+
+               struct drm_i915_error_object {
+                       int page_count;
+                       u64 gtt_offset;
+                       u64 gtt_size;
+                       u32 *pages[0];
+               } *ringbuffer, *batchbuffer, *wa_batchbuffer, *ctx, *hws_page;
+
+               struct drm_i915_error_object *wa_ctx;
+
+               struct drm_i915_error_request {
+                       long jiffies;
+                       pid_t pid;
+                       u32 seqno;
+                       u32 head;
+                       u32 tail;
+               } *requests;
+
+               struct drm_i915_error_waiter {
+                       char comm[TASK_COMM_LEN];
+                       pid_t pid;
+                       u32 seqno;
+               } *waiters;
+
+               struct {
+                       u32 gfx_mode;
+                       union {
+                               u64 pdp[4];
+                               u32 pp_dir_base;
+                       };
+               } vm_info;
+
+               pid_t pid;
+               char comm[TASK_COMM_LEN];
+       } engine[I915_NUM_ENGINES];
+
+       struct drm_i915_error_buffer {
+               u32 size;
+               u32 name;
+               u32 rseqno[I915_NUM_ENGINES], wseqno;
+               u64 gtt_offset;
+               u32 read_domains;
+               u32 write_domain;
+               s32 fence_reg:I915_MAX_NUM_FENCE_BITS;
+               u32 tiling:2;
+               u32 dirty:1;
+               u32 purgeable:1;
+               u32 userptr:1;
+               s32 engine:4;
+               u32 cache_level:3;
+       } *active_bo[I915_NUM_ENGINES], *pinned_bo;
+       u32 active_bo_count[I915_NUM_ENGINES], pinned_bo_count;
+       struct i915_address_space *active_vm[I915_NUM_ENGINES];
+};
+
 enum i915_cache_level {
        I915_CACHE_NONE = 0,
        I915_CACHE_LLC, /* also used for snoopable memory on non-LLC */
@@ -879,6 +913,7 @@ struct i915_gem_context {
        struct drm_i915_private *i915;
        struct drm_i915_file_private *file_priv;
        struct i915_hw_ppgtt *ppgtt;
+       struct pid *pid;
 
        struct i915_ctx_hang_stats hang_stats;
 
@@ -893,9 +928,8 @@ struct i915_gem_context {
        u32 ggtt_alignment;
 
        struct intel_context {
-               struct drm_i915_gem_object *state;
-               struct intel_ringbuffer *ringbuf;
-               struct i915_vma *lrc_vma;
+               struct i915_vma *state;
+               struct intel_ring *ring;
                uint32_t *lrc_reg_state;
                u64 lrc_desc;
                int pin_count;
@@ -909,6 +943,7 @@ struct i915_gem_context {
        struct list_head link;
 
        u8 remap_slice;
+       bool closed:1;
 };
 
 enum fb_op_origin {
@@ -1062,13 +1097,6 @@ struct intel_gmbus {
 
 struct i915_suspend_saved_registers {
        u32 saveDSPARB;
-       u32 saveLVDS;
-       u32 savePP_ON_DELAYS;
-       u32 savePP_OFF_DELAYS;
-       u32 savePP_ON;
-       u32 savePP_OFF;
-       u32 savePP_CONTROL;
-       u32 savePP_DIVISOR;
        u32 saveFBC_CONTROL;
        u32 saveCACHE_MODE_0;
        u32 saveMI_ARB_STATE;
@@ -1157,6 +1185,7 @@ struct intel_gen6_power_mgmt {
        bool interrupts_enabled;
        u32 pm_iir;
 
+       /* PM interrupt bits that should never be masked */
        u32 pm_intr_keep;
 
        /* Frequencies are stored in potentially platform dependent multiples.
@@ -1174,6 +1203,7 @@ struct intel_gen6_power_mgmt {
        u8 max_freq_softlimit;  /* Max frequency permitted by the driver */
        u8 max_freq;            /* Maximum frequency, RP0 if not overclocking */
        u8 min_freq;            /* AKA RPn. Minimum frequency */
+       u8 boost_freq;          /* Frequency to request when wait boosting */
        u8 idle_freq;           /* Frequency to request when we are idle */
        u8 efficient_freq;      /* AKA RPe. Pre-determined balanced frequency */
        u8 rp1_freq;            /* "less than" RP0 power/freqency */
@@ -1191,11 +1221,9 @@ struct intel_gen6_power_mgmt {
        bool client_boost;
 
        bool enabled;
-       struct delayed_work delayed_resume_work;
+       struct delayed_work autoenable_work;
        unsigned boosts;
 
-       struct intel_rps_client semaphores, mmioflips;
-
        /* manual wa residency calculations */
        struct intel_rps_ei up_ei, down_ei;
 
@@ -1320,7 +1348,6 @@ struct i915_gem_mm {
        struct notifier_block oom_notifier;
        struct notifier_block vmap_notifier;
        struct shrinker shrinker;
-       bool shrinker_no_lock_stealing;
 
        /** LRU list of objects with fence regs on them. */
        struct list_head fence_list;
@@ -1332,7 +1359,7 @@ struct i915_gem_mm {
        bool interruptible;
 
        /* the indicator for dispatch video commands on two BSD rings */
-       unsigned int bsd_ring_dispatch_index;
+       atomic_t bsd_engine_dispatch_index;
 
        /** Bit 6 swizzling required for X tiling */
        uint32_t bit_6_swizzle_x;
@@ -1380,9 +1407,10 @@ struct i915_gpu_error {
         * State variable controlling the reset flow and count
         *
         * This is a counter which gets incremented when reset is triggered,
-        * and again when reset has been handled. So odd values (lowest bit set)
-        * means that reset is in progress and even values that
-        * (reset_counter >> 1):th reset was successfully completed.
+        *
+        * Before the reset commences, the I915_RESET_IN_PROGRESS bit is set
+        * meaning that any waiters holding onto the struct_mutex should
+        * relinquish the lock immediately in order for the reset to start.
         *
         * If reset is not completed succesfully, the I915_WEDGE bit is
         * set meaning that hardware is terminally sour and there is no
@@ -1397,10 +1425,11 @@ struct i915_gpu_error {
         * naturally enforces the correct ordering between the bail-out of the
         * waiter and the gpu reset work code.
         */
-       atomic_t reset_counter;
+       unsigned long reset_count;
 
-#define I915_RESET_IN_PROGRESS_FLAG    1
-#define I915_WEDGED                    (1 << 31)
+       unsigned long flags;
+#define I915_RESET_IN_PROGRESS 0
+#define I915_WEDGED            (BITS_PER_LONG - 1)
 
        /**
         * Waitqueue to signal when a hang is detected. Used to for waiters
@@ -1671,7 +1700,7 @@ struct intel_pipe_crc {
 };
 
 struct i915_frontbuffer_tracking {
-       struct mutex lock;
+       spinlock_t lock;
 
        /*
         * Tracking bits for delayed frontbuffer flushing du to gpu activity or
@@ -1706,18 +1735,6 @@ struct i915_virtual_gpu {
        bool active;
 };
 
-struct i915_execbuffer_params {
-       struct drm_device               *dev;
-       struct drm_file                 *file;
-       uint32_t                        dispatch_flags;
-       uint32_t                        args_batch_start_offset;
-       uint64_t                        batch_obj_vm_offset;
-       struct intel_engine_cs *engine;
-       struct drm_i915_gem_object      *batch_obj;
-       struct i915_gem_context            *ctx;
-       struct drm_i915_gem_request     *request;
-};
-
 /* used in computing the new watermarks state */
 struct intel_wm_config {
        unsigned int num_pipes_active;
@@ -1764,13 +1781,15 @@ struct drm_i915_private {
 
        uint32_t psr_mmio_base;
 
+       uint32_t pps_mmio_base;
+
        wait_queue_head_t gmbus_wait_queue;
 
        struct pci_dev *bridge_dev;
        struct i915_gem_context *kernel_context;
        struct intel_engine_cs engine[I915_NUM_ENGINES];
-       struct drm_i915_gem_object *semaphore_obj;
-       uint32_t last_seqno, next_seqno;
+       struct i915_vma *semaphore;
+       u32 next_seqno;
 
        struct drm_dma_handle *status_page_dmah;
        struct resource mch_res;
@@ -1965,11 +1984,11 @@ struct drm_i915_private {
        struct vlv_s0ix_state vlv_s0ix_state;
 
        enum {
-               I915_SKL_SAGV_UNKNOWN = 0,
-               I915_SKL_SAGV_DISABLED,
-               I915_SKL_SAGV_ENABLED,
-               I915_SKL_SAGV_NOT_CONTROLLED
-       } skl_sagv_status;
+               I915_SAGV_UNKNOWN = 0,
+               I915_SAGV_DISABLED,
+               I915_SAGV_ENABLED,
+               I915_SAGV_NOT_CONTROLLED
+       } sagv_status;
 
        struct {
                /*
@@ -2025,12 +2044,8 @@ struct drm_i915_private {
 
        /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */
        struct {
-               int (*execbuf_submit)(struct i915_execbuffer_params *params,
-                                     struct drm_i915_gem_execbuffer2 *args,
-                                     struct list_head *vmas);
-               int (*init_engines)(struct drm_device *dev);
+               void (*resume)(struct drm_i915_private *);
                void (*cleanup_engine)(struct intel_engine_cs *engine);
-               void (*stop_engine)(struct intel_engine_cs *engine);
 
                /**
                 * Is the GPU currently considered idle, or busy executing
@@ -2077,9 +2092,9 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
        return container_of(dev, struct drm_i915_private, drm);
 }
 
-static inline struct drm_i915_private *dev_to_i915(struct device *dev)
+static inline struct drm_i915_private *kdev_to_i915(struct device *kdev)
 {
-       return to_i915(dev_get_drvdata(dev));
+       return to_i915(dev_get_drvdata(kdev));
 }
 
 static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc)
@@ -2102,13 +2117,16 @@ static inline struct drm_i915_private *guc_to_i915(struct intel_guc *guc)
                for_each_if (((id__) = (engine__)->id, \
                              intel_engine_initialized(engine__)))
 
+#define __mask_next_bit(mask) ({                                       \
+       int __idx = ffs(mask) - 1;                                      \
+       mask &= ~BIT(__idx);                                            \
+       __idx;                                                          \
+})
+
 /* Iterator over subset of engines selected by mask */
-#define for_each_engine_masked(engine__, dev_priv__, mask__) \
-       for ((engine__) = &(dev_priv__)->engine[0]; \
-            (engine__) < &(dev_priv__)->engine[I915_NUM_ENGINES]; \
-            (engine__)++) \
-               for_each_if (((mask__) & intel_engine_flag(engine__)) && \
-                            intel_engine_initialized(engine__))
+#define for_each_engine_masked(engine__, dev_priv__, mask__, tmp__) \
+       for (tmp__ = mask__ & INTEL_INFO(dev_priv__)->ring_mask;        \
+            tmp__ ? (engine__ = &(dev_priv__)->engine[__mask_next_bit(tmp__)]), 1 : 0; )
 
 enum hdmi_force_audio {
        HDMI_AUDIO_OFF_DVI = -2,        /* no aux data for HDMI-DVI converter */
@@ -2153,8 +2171,6 @@ struct drm_i915_gem_object_ops {
  */
 #define INTEL_MAX_SPRITE_BITS_PER_PIPE 5
 #define INTEL_FRONTBUFFER_BITS_PER_PIPE 8
-#define INTEL_FRONTBUFFER_BITS \
-       (INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES)
 #define INTEL_FRONTBUFFER_PRIMARY(pipe) \
        (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
 #define INTEL_FRONTBUFFER_CURSOR(pipe) \
@@ -2178,18 +2194,21 @@ struct drm_i915_gem_object {
        struct drm_mm_node *stolen;
        struct list_head global_list;
 
-       struct list_head engine_list[I915_NUM_ENGINES];
        /** Used in execbuf to temporarily hold a ref */
        struct list_head obj_exec_link;
 
        struct list_head batch_pool_link;
 
+       unsigned long flags;
        /**
         * This is set if the object is on the active lists (has pending
         * rendering and so a non-zero seqno), and is not set if it i s on
         * inactive (ready to be unbound) list.
         */
-       unsigned int active:I915_NUM_ENGINES;
+#define I915_BO_ACTIVE_SHIFT 0
+#define I915_BO_ACTIVE_MASK ((1 << I915_NUM_ENGINES) - 1)
+#define __I915_BO_ACTIVE(bo) \
+       ((READ_ONCE((bo)->flags) >> I915_BO_ACTIVE_SHIFT) & I915_BO_ACTIVE_MASK)
 
        /**
         * This is set if the object has been written to since last bound
@@ -2197,37 +2216,11 @@ struct drm_i915_gem_object {
         */
        unsigned int dirty:1;
 
-       /**
-        * Fence register bits (if any) for this object.  Will be set
-        * as needed when mapped into the GTT.
-        * Protected by dev->struct_mutex.
-        */
-       signed int fence_reg:I915_MAX_NUM_FENCE_BITS;
-
        /**
         * Advice: are the backing pages purgeable?
         */
        unsigned int madv:2;
 
-       /**
-        * Current tiling mode for the object.
-        */
-       unsigned int tiling_mode:2;
-       /**
-        * Whether the tiling parameters for the currently associated fence
-        * register have changed. Note that for the purposes of tracking
-        * tiling changes we also treat the unfenced register, the register
-        * slot that the object occupies whilst it executes a fenced
-        * command (such as BLT on gen2/3), as a "fence".
-        */
-       unsigned int fence_dirty:1;
-
-       /**
-        * Is the object at the current location in the gtt mappable and
-        * fenceable? Used to avoid costly recalculations.
-        */
-       unsigned int map_and_fenceable:1;
-
        /**
         * Whether the current gtt mapping needs to be mappable (and isn't just
         * mappable by accident). Track pin and fault separate for a more
@@ -2243,9 +2236,17 @@ struct drm_i915_gem_object {
        unsigned int cache_level:3;
        unsigned int cache_dirty:1;
 
-       unsigned int frontbuffer_bits:INTEL_FRONTBUFFER_BITS;
+       atomic_t frontbuffer_bits;
+       unsigned int frontbuffer_ggtt_origin; /* write once */
+
+       /** Current tiling stride for the object, if it's tiled. */
+       unsigned int tiling_and_stride;
+#define FENCE_MINIMUM_STRIDE 128 /* See i915_tiling_ok() */
+#define TILING_MASK (FENCE_MINIMUM_STRIDE-1)
+#define STRIDE_MASK (~TILING_MASK)
 
-       unsigned int has_wc_mmap;
+       /** Count of VMA actually bound by this object */
+       unsigned int bind_count;
        unsigned int pin_display;
 
        struct sg_table *pages;
@@ -2265,14 +2266,9 @@ struct drm_i915_gem_object {
         * requests on one ring where the write request is older than the
         * read request. This allows for the CPU to read from an active
         * buffer by only waiting for the write to complete.
-        * */
-       struct drm_i915_gem_request *last_read_req[I915_NUM_ENGINES];
-       struct drm_i915_gem_request *last_write_req;
-       /** Breadcrumb of last fenced GPU access to the buffer. */
-       struct drm_i915_gem_request *last_fenced_req;
-
-       /** Current tiling stride for the object, if it's tiled. */
-       uint32_t stride;
+        */
+       struct i915_gem_active last_read[I915_NUM_ENGINES];
+       struct i915_gem_active last_write;
 
        /** References from framebuffers, locks out tiling changes. */
        unsigned long framebuffer_references;
@@ -2280,23 +2276,70 @@ struct drm_i915_gem_object {
        /** Record of address bit 17 of each page at last unbind. */
        unsigned long *bit_17;
 
-       union {
-               /** for phy allocated objects */
-               struct drm_dma_handle *phys_handle;
-
-               struct i915_gem_userptr {
-                       uintptr_t ptr;
-                       unsigned read_only :1;
-                       unsigned workers :4;
+       struct i915_gem_userptr {
+               uintptr_t ptr;
+               unsigned read_only :1;
+               unsigned workers :4;
 #define I915_GEM_USERPTR_MAX_WORKERS 15
 
-                       struct i915_mm_struct *mm;
-                       struct i915_mmu_object *mmu_object;
-                       struct work_struct *work;
-               } userptr;
-       };
+               struct i915_mm_struct *mm;
+               struct i915_mmu_object *mmu_object;
+               struct work_struct *work;
+       } userptr;
+
+       /** for phys allocated objects */
+       struct drm_dma_handle *phys_handle;
 };
-#define to_intel_bo(x) container_of(x, struct drm_i915_gem_object, base)
+
+static inline struct drm_i915_gem_object *
+to_intel_bo(struct drm_gem_object *gem)
+{
+       /* Assert that to_intel_bo(NULL) == NULL */
+       BUILD_BUG_ON(offsetof(struct drm_i915_gem_object, base));
+
+       return container_of(gem, struct drm_i915_gem_object, base);
+}
+
+static inline struct drm_i915_gem_object *
+i915_gem_object_lookup(struct drm_file *file, u32 handle)
+{
+       return to_intel_bo(drm_gem_object_lookup(file, handle));
+}
+
+__deprecated
+extern struct drm_gem_object *
+drm_gem_object_lookup(struct drm_file *file, u32 handle);
+
+__attribute__((nonnull))
+static inline struct drm_i915_gem_object *
+i915_gem_object_get(struct drm_i915_gem_object *obj)
+{
+       drm_gem_object_reference(&obj->base);
+       return obj;
+}
+
+__deprecated
+extern void drm_gem_object_reference(struct drm_gem_object *);
+
+__attribute__((nonnull))
+static inline void
+i915_gem_object_put(struct drm_i915_gem_object *obj)
+{
+       drm_gem_object_unreference(&obj->base);
+}
+
+__deprecated
+extern void drm_gem_object_unreference(struct drm_gem_object *);
+
+__attribute__((nonnull))
+static inline void
+i915_gem_object_put_unlocked(struct drm_i915_gem_object *obj)
+{
+       drm_gem_object_unreference_unlocked(&obj->base);
+}
+
+__deprecated
+extern void drm_gem_object_unreference_unlocked(struct drm_gem_object *);
 
 static inline bool
 i915_gem_object_has_struct_page(const struct drm_i915_gem_object *obj)
@@ -2304,6 +2347,67 @@ i915_gem_object_has_struct_page(const struct drm_i915_gem_object *obj)
        return obj->ops->flags & I915_GEM_OBJECT_HAS_STRUCT_PAGE;
 }
 
+static inline unsigned long
+i915_gem_object_get_active(const struct drm_i915_gem_object *obj)
+{
+       return (obj->flags >> I915_BO_ACTIVE_SHIFT) & I915_BO_ACTIVE_MASK;
+}
+
+static inline bool
+i915_gem_object_is_active(const struct drm_i915_gem_object *obj)
+{
+       return i915_gem_object_get_active(obj);
+}
+
+static inline void
+i915_gem_object_set_active(struct drm_i915_gem_object *obj, int engine)
+{
+       obj->flags |= BIT(engine + I915_BO_ACTIVE_SHIFT);
+}
+
+static inline void
+i915_gem_object_clear_active(struct drm_i915_gem_object *obj, int engine)
+{
+       obj->flags &= ~BIT(engine + I915_BO_ACTIVE_SHIFT);
+}
+
+static inline bool
+i915_gem_object_has_active_engine(const struct drm_i915_gem_object *obj,
+                                 int engine)
+{
+       return obj->flags & BIT(engine + I915_BO_ACTIVE_SHIFT);
+}
+
+static inline unsigned int
+i915_gem_object_get_tiling(struct drm_i915_gem_object *obj)
+{
+       return obj->tiling_and_stride & TILING_MASK;
+}
+
+static inline bool
+i915_gem_object_is_tiled(struct drm_i915_gem_object *obj)
+{
+       return i915_gem_object_get_tiling(obj) != I915_TILING_NONE;
+}
+
+static inline unsigned int
+i915_gem_object_get_stride(struct drm_i915_gem_object *obj)
+{
+       return obj->tiling_and_stride & STRIDE_MASK;
+}
+
+static inline struct i915_vma *i915_vma_get(struct i915_vma *vma)
+{
+       i915_gem_object_get(vma->obj);
+       return vma;
+}
+
+static inline void i915_vma_put(struct i915_vma *vma)
+{
+       lockdep_assert_held(&vma->vm->dev->struct_mutex);
+       i915_gem_object_put(vma->obj);
+}
+
 /*
  * Optimised SGL iterator for GEM objects
  */
@@ -2374,171 +2478,6 @@ static inline struct scatterlist *__sg_next(struct scatterlist *sg)
             (((__iter).curr += PAGE_SIZE) < (__iter).max) ||           \
             ((__iter) = __sgt_iter(__sg_next((__iter).sgp), false), 0))
 
-/**
- * Request queue structure.
- *
- * The request queue allows us to note sequence numbers that have been emitted
- * and may be associated with active buffers to be retired.
- *
- * By keeping this list, we can avoid having to do questionable sequence
- * number comparisons on buffer last_read|write_seqno. It also allows an
- * emission time to be associated with the request for tracking how far ahead
- * of the GPU the submission is.
- *
- * The requests are reference counted, so upon creation they should have an
- * initial reference taken using kref_init
- */
-struct drm_i915_gem_request {
-       struct kref ref;
-
-       /** On Which ring this request was generated */
-       struct drm_i915_private *i915;
-       struct intel_engine_cs *engine;
-       struct intel_signal_node signaling;
-
-        /** GEM sequence number associated with the previous request,
-         * when the HWS breadcrumb is equal to this the GPU is processing
-         * this request.
-         */
-       u32 previous_seqno;
-
-        /** GEM sequence number associated with this request,
-         * when the HWS breadcrumb is equal or greater than this the GPU
-         * has finished processing this request.
-         */
-       u32 seqno;
-
-       /** Position in the ringbuffer of the start of the request */
-       u32 head;
-
-       /**
-        * Position in the ringbuffer of the start of the postfix.
-        * This is required to calculate the maximum available ringbuffer
-        * space without overwriting the postfix.
-        */
-        u32 postfix;
-
-       /** Position in the ringbuffer of the end of the whole request */
-       u32 tail;
-
-       /** Preallocate space in the ringbuffer for the emitting the request */
-       u32 reserved_space;
-
-       /**
-        * Context and ring buffer related to this request
-        * Contexts are refcounted, so when this request is associated with a
-        * context, we must increment the context's refcount, to guarantee that
-        * it persists while any request is linked to it. Requests themselves
-        * are also refcounted, so the request will only be freed when the last
-        * reference to it is dismissed, and the code in
-        * i915_gem_request_free() will then decrement the refcount on the
-        * context.
-        */
-       struct i915_gem_context *ctx;
-       struct intel_ringbuffer *ringbuf;
-
-       /**
-        * Context related to the previous request.
-        * As the contexts are accessed by the hardware until the switch is
-        * completed to a new context, the hardware may still be writing
-        * to the context object after the breadcrumb is visible. We must
-        * not unpin/unbind/prune that object whilst still active and so
-        * we keep the previous context pinned until the following (this)
-        * request is retired.
-        */
-       struct i915_gem_context *previous_context;
-
-       /** Batch buffer related to this request if any (used for
-           error state dump only) */
-       struct drm_i915_gem_object *batch_obj;
-
-       /** Time at which this request was emitted, in jiffies. */
-       unsigned long emitted_jiffies;
-
-       /** global list entry for this request */
-       struct list_head list;
-
-       struct drm_i915_file_private *file_priv;
-       /** file_priv list entry for this request */
-       struct list_head client_list;
-
-       /** process identifier submitting this request */
-       struct pid *pid;
-
-       /**
-        * The ELSP only accepts two elements at a time, so we queue
-        * context/tail pairs on a given queue (ring->execlist_queue) until the
-        * hardware is available. The queue serves a double purpose: we also use
-        * it to keep track of the up to 2 contexts currently in the hardware
-        * (usually one in execution and the other queued up by the GPU): We
-        * only remove elements from the head of the queue when the hardware
-        * informs us that an element has been completed.
-        *
-        * All accesses to the queue are mediated by a spinlock
-        * (ring->execlist_lock).
-        */
-
-       /** Execlist link in the submission queue.*/
-       struct list_head execlist_link;
-
-       /** Execlists no. of times this request has been sent to the ELSP */
-       int elsp_submitted;
-
-       /** Execlists context hardware id. */
-       unsigned ctx_hw_id;
-};
-
-struct drm_i915_gem_request * __must_check
-i915_gem_request_alloc(struct intel_engine_cs *engine,
-                      struct i915_gem_context *ctx);
-void i915_gem_request_free(struct kref *req_ref);
-int i915_gem_request_add_to_client(struct drm_i915_gem_request *req,
-                                  struct drm_file *file);
-
-static inline uint32_t
-i915_gem_request_get_seqno(struct drm_i915_gem_request *req)
-{
-       return req ? req->seqno : 0;
-}
-
-static inline struct intel_engine_cs *
-i915_gem_request_get_engine(struct drm_i915_gem_request *req)
-{
-       return req ? req->engine : NULL;
-}
-
-static inline struct drm_i915_gem_request *
-i915_gem_request_reference(struct drm_i915_gem_request *req)
-{
-       if (req)
-               kref_get(&req->ref);
-       return req;
-}
-
-static inline void
-i915_gem_request_unreference(struct drm_i915_gem_request *req)
-{
-       kref_put(&req->ref, i915_gem_request_free);
-}
-
-static inline void i915_gem_request_assign(struct drm_i915_gem_request **pdst,
-                                          struct drm_i915_gem_request *src)
-{
-       if (src)
-               i915_gem_request_reference(src);
-
-       if (*pdst)
-               i915_gem_request_unreference(*pdst);
-
-       *pdst = src;
-}
-
-/*
- * XXX: i915_gem_request_completed should be here but currently needs the
- * definition of i915_seqno_passed() which is below. It will be moved in
- * a later patch when the call to i915_seqno_passed() is obsoleted...
- */
-
 /*
  * A command that requires special handling by the command parser.
  */
@@ -2626,8 +2565,9 @@ struct drm_i915_cmd_descriptor {
 /*
  * A table of commands requiring special handling by the command parser.
  *
- * Each ring has an array of tables. Each table consists of an array of command
- * descriptors, which must be sorted with command opcodes in ascending order.
+ * Each engine has an array of tables. Each table consists of an array of
+ * command descriptors, which must be sorted with command opcodes in
+ * ascending order.
  */
 struct drm_i915_cmd_table {
        const struct drm_i915_cmd_descriptor *table;
@@ -2645,7 +2585,7 @@ struct drm_i915_cmd_table {
                BUILD_BUG(); \
        __p; \
 })
-#define INTEL_INFO(p)  (&__I915__(p)->info)
+#define INTEL_INFO(p)  (&__I915__(p)->info)
 #define INTEL_GEN(p)   (INTEL_INFO(p)->gen)
 #define INTEL_DEVID(p) (INTEL_INFO(p)->device_id)
 
@@ -2812,10 +2752,10 @@ struct drm_i915_cmd_table {
 #define HAS_EDRAM(dev)         (!!(__I915__(dev)->edram_cap & EDRAM_ENABLED))
 #define HAS_WT(dev)            ((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
                                 HAS_EDRAM(dev))
-#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
+#define HWS_NEEDS_PHYSICAL(dev)        (INTEL_INFO(dev)->hws_needs_physical)
 
-#define HAS_HW_CONTEXTS(dev)   (INTEL_INFO(dev)->gen >= 6)
-#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->gen >= 8)
+#define HAS_HW_CONTEXTS(dev)   (INTEL_INFO(dev)->has_hw_contexts)
+#define HAS_LOGICAL_RING_CONTEXTS(dev) (INTEL_INFO(dev)->has_logical_ring_contexts)
 #define USES_PPGTT(dev)                (i915.enable_ppgtt)
 #define USES_FULL_PPGTT(dev)   (i915.enable_ppgtt >= 2)
 #define USES_FULL_48BIT_PPGTT(dev)     (i915.enable_ppgtt == 3)
@@ -2839,7 +2779,7 @@ struct drm_i915_cmd_table {
  * interrupt source and so prevents the other device from working properly.
  */
 #define HAS_AUX_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
-#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
+#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->has_gmbus_irq)
 
 /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
  * rows, which changed the alignment requirements and fence programming.
@@ -2855,38 +2795,27 @@ struct drm_i915_cmd_table {
 
 #define HAS_IPS(dev)           (IS_HSW_ULT(dev) || IS_BROADWELL(dev))
 
-#define HAS_DP_MST(dev)                (IS_HASWELL(dev) || IS_BROADWELL(dev) || \
-                                INTEL_INFO(dev)->gen >= 9)
+#define HAS_DP_MST(dev)        (INTEL_INFO(dev)->has_dp_mst)
 
 #define HAS_DDI(dev)           (INTEL_INFO(dev)->has_ddi)
 #define HAS_FPGA_DBG_UNCLAIMED(dev)    (INTEL_INFO(dev)->has_fpga_dbg)
-#define HAS_PSR(dev)           (IS_HASWELL(dev) || IS_BROADWELL(dev) || \
-                                IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev) || \
-                                IS_SKYLAKE(dev) || IS_KABYLAKE(dev))
-#define HAS_RUNTIME_PM(dev)    (IS_GEN6(dev) || IS_HASWELL(dev) || \
-                                IS_BROADWELL(dev) || IS_VALLEYVIEW(dev) || \
-                                IS_CHERRYVIEW(dev) || IS_SKYLAKE(dev) || \
-                                IS_KABYLAKE(dev) || IS_BROXTON(dev))
-#define HAS_RC6(dev)           (INTEL_INFO(dev)->gen >= 6)
-#define HAS_RC6p(dev)          (IS_GEN6(dev) || IS_IVYBRIDGE(dev))
-
-#define HAS_CSR(dev)   (IS_GEN9(dev))
+#define HAS_PSR(dev)           (INTEL_INFO(dev)->has_psr)
+#define HAS_RUNTIME_PM(dev)    (INTEL_INFO(dev)->has_runtime_pm)
+#define HAS_RC6(dev)           (INTEL_INFO(dev)->has_rc6)
+#define HAS_RC6p(dev)          (INTEL_INFO(dev)->has_rc6p)
+
+#define HAS_CSR(dev)   (INTEL_INFO(dev)->has_csr)
 
 /*
  * For now, anything with a GuC requires uCode loading, and then supports
  * command submission once loaded. But these are logically independent
  * properties, so we have separate macros to test them.
  */
-#define HAS_GUC(dev)           (IS_GEN9(dev))
+#define HAS_GUC(dev)           (INTEL_INFO(dev)->has_guc)
 #define HAS_GUC_UCODE(dev)     (HAS_GUC(dev))
 #define HAS_GUC_SCHED(dev)     (HAS_GUC(dev))
 
-#define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \
-                                   INTEL_INFO(dev)->gen >= 8)
-
-#define HAS_CORE_RING_FREQ(dev)        (INTEL_INFO(dev)->gen >= 6 && \
-                                !IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) && \
-                                !IS_BROXTON(dev))
+#define HAS_RESOURCE_STREAMER(dev) (INTEL_INFO(dev)->has_resource_streamer)
 
 #define HAS_POOLED_EU(dev)     (INTEL_INFO(dev)->has_pooled_eu)
 
@@ -2914,11 +2843,10 @@ struct drm_i915_cmd_table {
 #define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
 #define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE)
 
-#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || \
-                              IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->has_gmch_display)
 
 /* DPF == dynamic parity feature */
-#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+#define HAS_L3_DPF(dev) (INTEL_INFO(dev)->has_l3_dpf)
 #define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
 
 #define GT_FREQUENCY_MULTIPLIER 50
@@ -2939,7 +2867,9 @@ extern int i915_suspend_switcheroo(struct drm_device *dev, pm_message_t state);
 extern int i915_resume_switcheroo(struct drm_device *dev);
 
 int intel_sanitize_enable_ppgtt(struct drm_i915_private *dev_priv,
-                               int enable_ppgtt);
+                               int enable_ppgtt);
+
+bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value);
 
 /* i915_drv.c */
 void __printf(3, 4)
@@ -2955,7 +2885,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
 #endif
 extern int intel_gpu_reset(struct drm_i915_private *dev_priv, u32 engine_mask);
 extern bool intel_has_gpu_reset(struct drm_i915_private *dev_priv);
-extern int i915_reset(struct drm_i915_private *dev_priv);
+extern void i915_reset(struct drm_i915_private *dev_priv);
 extern int intel_guc_reset(struct drm_i915_private *dev_priv);
 extern void intel_engine_init_hangcheck(struct intel_engine_cs *engine);
 extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
@@ -3116,11 +3046,6 @@ int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
                              struct drm_file *file_priv);
 int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
                             struct drm_file *file_priv);
-void i915_gem_execbuffer_move_to_active(struct list_head *vmas,
-                                       struct drm_i915_gem_request *req);
-int i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
-                                  struct drm_i915_gem_execbuffer2 *args,
-                                  struct list_head *vmas);
 int i915_gem_execbuffer(struct drm_device *dev, void *data,
                        struct drm_file *file_priv);
 int i915_gem_execbuffer2(struct drm_device *dev, void *data,
@@ -3149,6 +3074,7 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
 void i915_gem_load_init(struct drm_device *dev);
 void i915_gem_load_cleanup(struct drm_device *dev);
 void i915_gem_load_init_fences(struct drm_i915_private *dev_priv);
+int i915_gem_freeze(struct drm_i915_private *dev_priv);
 int i915_gem_freeze_late(struct drm_i915_private *dev_priv);
 
 void *i915_gem_object_alloc(struct drm_device *dev);
@@ -3159,47 +3085,28 @@ struct drm_i915_gem_object *i915_gem_object_create(struct drm_device *dev,
                                                  size_t size);
 struct drm_i915_gem_object *i915_gem_object_create_from_data(
                struct drm_device *dev, const void *data, size_t size);
+void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file);
 void i915_gem_free_object(struct drm_gem_object *obj);
-void i915_gem_vma_destroy(struct i915_vma *vma);
-
-/* Flags used by pin/bind&friends. */
-#define PIN_MAPPABLE   (1<<0)
-#define PIN_NONBLOCK   (1<<1)
-#define PIN_GLOBAL     (1<<2)
-#define PIN_OFFSET_BIAS        (1<<3)
-#define PIN_USER       (1<<4)
-#define PIN_UPDATE     (1<<5)
-#define PIN_ZONE_4G    (1<<6)
-#define PIN_HIGH       (1<<7)
-#define PIN_OFFSET_FIXED       (1<<8)
-#define PIN_OFFSET_MASK (~4095)
-int __must_check
-i915_gem_object_pin(struct drm_i915_gem_object *obj,
-                   struct i915_address_space *vm,
-                   uint32_t alignment,
-                   uint64_t flags);
-int __must_check
+
+struct i915_vma * __must_check
 i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
                         const struct i915_ggtt_view *view,
-                        uint32_t alignment,
-                        uint64_t flags);
+                        u64 size,
+                        u64 alignment,
+                        u64 flags);
 
 int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
                  u32 flags);
 void __i915_vma_set_map_and_fenceable(struct i915_vma *vma);
 int __must_check i915_vma_unbind(struct i915_vma *vma);
-/*
- * BEWARE: Do not use the function below unless you can _absolutely_
- * _guarantee_ VMA in question is _not in use_ anywhere.
- */
-int __must_check __i915_vma_unbind_no_wait(struct i915_vma *vma);
+void i915_vma_close(struct i915_vma *vma);
+void i915_vma_destroy(struct i915_vma *vma);
+
+int i915_gem_object_unbind(struct drm_i915_gem_object *obj);
 int i915_gem_object_put_pages(struct drm_i915_gem_object *obj);
 void i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv);
 void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
 
-int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
-                                   int *needs_clflush);
-
 int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
 
 static inline int __sg_page_count(struct scatterlist *sg)
@@ -3259,13 +3166,20 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
        obj->pages_pin_count--;
 }
 
+enum i915_map_type {
+       I915_MAP_WB = 0,
+       I915_MAP_WC,
+};
+
 /**
  * i915_gem_object_pin_map - return a contiguous mapping of the entire object
  * @obj - the object to map into kernel address space
+ * @type - the type of mapping, used to select pgprot_t
  *
  * Calls i915_gem_object_pin_pages() to prevent reaping of the object's
  * pages and then returns a contiguous mapping of the backing storage into
- * the kernel address space.
+ * the kernel address space. Based on the @type of mapping, the PTE will be
+ * set to either WriteBack or WriteCombine (via pgprot_t).
  *
  * The caller must hold the struct_mutex, and is responsible for calling
  * i915_gem_object_unpin_map() when the mapping is no longer required.
@@ -3273,7 +3187,8 @@ static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
  * Returns the pointer through which to access the mapped object, or an
  * ERR_PTR() on error.
  */
-void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj);
+void *__must_check i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
+                                          enum i915_map_type type);
 
 /**
  * i915_gem_object_unpin_map - releases an earlier mapping
@@ -3292,122 +3207,73 @@ static inline void i915_gem_object_unpin_map(struct drm_i915_gem_object *obj)
        i915_gem_object_unpin_pages(obj);
 }
 
+int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
+                                   unsigned int *needs_clflush);
+int i915_gem_obj_prepare_shmem_write(struct drm_i915_gem_object *obj,
+                                    unsigned int *needs_clflush);
+#define CLFLUSH_BEFORE 0x1
+#define CLFLUSH_AFTER 0x2
+#define CLFLUSH_FLAGS (CLFLUSH_BEFORE | CLFLUSH_AFTER)
+
+static inline void
+i915_gem_obj_finish_shmem_access(struct drm_i915_gem_object *obj)
+{
+       i915_gem_object_unpin_pages(obj);
+}
+
 int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
-int i915_gem_object_sync(struct drm_i915_gem_object *obj,
-                        struct intel_engine_cs *to,
-                        struct drm_i915_gem_request **to_req);
 void i915_vma_move_to_active(struct i915_vma *vma,
-                            struct drm_i915_gem_request *req);
+                            struct drm_i915_gem_request *req,
+                            unsigned int flags);
 int i915_gem_dumb_create(struct drm_file *file_priv,
                         struct drm_device *dev,
                         struct drm_mode_create_dumb *args);
 int i915_gem_mmap_gtt(struct drm_file *file_priv, struct drm_device *dev,
                      uint32_t handle, uint64_t *offset);
+int i915_gem_mmap_gtt_version(void);
 
 void i915_gem_track_fb(struct drm_i915_gem_object *old,
                       struct drm_i915_gem_object *new,
                       unsigned frontbuffer_bits);
 
-/**
- * Returns true if seq1 is later than seq2.
- */
-static inline bool
-i915_seqno_passed(uint32_t seq1, uint32_t seq2)
-{
-       return (int32_t)(seq1 - seq2) >= 0;
-}
-
-static inline bool i915_gem_request_started(const struct drm_i915_gem_request *req)
-{
-       return i915_seqno_passed(intel_engine_get_seqno(req->engine),
-                                req->previous_seqno);
-}
-
-static inline bool i915_gem_request_completed(const struct drm_i915_gem_request *req)
-{
-       return i915_seqno_passed(intel_engine_get_seqno(req->engine),
-                                req->seqno);
-}
-
-bool __i915_spin_request(const struct drm_i915_gem_request *request,
-                        int state, unsigned long timeout_us);
-static inline bool i915_spin_request(const struct drm_i915_gem_request *request,
-                                    int state, unsigned long timeout_us)
-{
-       return (i915_gem_request_started(request) &&
-               __i915_spin_request(request, state, timeout_us));
-}
-
-int __must_check i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno);
 int __must_check i915_gem_set_seqno(struct drm_device *dev, u32 seqno);
 
 struct drm_i915_gem_request *
 i915_gem_find_active_request(struct intel_engine_cs *engine);
 
 void i915_gem_retire_requests(struct drm_i915_private *dev_priv);
-void i915_gem_retire_requests_ring(struct intel_engine_cs *engine);
-
-static inline u32 i915_reset_counter(struct i915_gpu_error *error)
-{
-       return atomic_read(&error->reset_counter);
-}
-
-static inline bool __i915_reset_in_progress(u32 reset)
-{
-       return unlikely(reset & I915_RESET_IN_PROGRESS_FLAG);
-}
-
-static inline bool __i915_reset_in_progress_or_wedged(u32 reset)
-{
-       return unlikely(reset & (I915_RESET_IN_PROGRESS_FLAG | I915_WEDGED));
-}
-
-static inline bool __i915_terminally_wedged(u32 reset)
-{
-       return unlikely(reset & I915_WEDGED);
-}
 
 static inline bool i915_reset_in_progress(struct i915_gpu_error *error)
 {
-       return __i915_reset_in_progress(i915_reset_counter(error));
+       return unlikely(test_bit(I915_RESET_IN_PROGRESS, &error->flags));
 }
 
-static inline bool i915_reset_in_progress_or_wedged(struct i915_gpu_error *error)
+static inline bool i915_terminally_wedged(struct i915_gpu_error *error)
 {
-       return __i915_reset_in_progress_or_wedged(i915_reset_counter(error));
+       return unlikely(test_bit(I915_WEDGED, &error->flags));
 }
 
-static inline bool i915_terminally_wedged(struct i915_gpu_error *error)
+static inline bool i915_reset_in_progress_or_wedged(struct i915_gpu_error *error)
 {
-       return __i915_terminally_wedged(i915_reset_counter(error));
+       return i915_reset_in_progress(error) | i915_terminally_wedged(error);
 }
 
 static inline u32 i915_reset_count(struct i915_gpu_error *error)
 {
-       return ((i915_reset_counter(error) & ~I915_WEDGED) + 1) / 2;
+       return READ_ONCE(error->reset_count);
 }
 
-void i915_gem_reset(struct drm_device *dev);
+void i915_gem_reset(struct drm_i915_private *dev_priv);
+void i915_gem_set_wedged(struct drm_i915_private *dev_priv);
 bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, bool force);
 int __must_check i915_gem_init(struct drm_device *dev);
-int i915_gem_init_engines(struct drm_device *dev);
 int __must_check i915_gem_init_hw(struct drm_device *dev);
 void i915_gem_init_swizzling(struct drm_device *dev);
 void i915_gem_cleanup_engines(struct drm_device *dev);
-int __must_check i915_gem_wait_for_idle(struct drm_i915_private *dev_priv);
+int __must_check i915_gem_wait_for_idle(struct drm_i915_private *dev_priv,
+                                       unsigned int flags);
 int __must_check i915_gem_suspend(struct drm_device *dev);
-void __i915_add_request(struct drm_i915_gem_request *req,
-                       struct drm_i915_gem_object *batch_obj,
-                       bool flush_caches);
-#define i915_add_request(req) \
-       __i915_add_request(req, NULL, true)
-#define i915_add_request_no_flush(req) \
-       __i915_add_request(req, NULL, false)
-int __i915_wait_request(struct drm_i915_gem_request *req,
-                       bool interruptible,
-                       s64 *timeout,
-                       struct intel_rps_client *rps);
-int __must_check i915_wait_request(struct drm_i915_gem_request *req);
+void i915_gem_resume(struct drm_device *dev);
 int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 int __must_check
 i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
@@ -3417,22 +3283,20 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
                                  bool write);
 int __must_check
 i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write);
-int __must_check
+struct i915_vma * __must_check
 i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
                                     u32 alignment,
                                     const struct i915_ggtt_view *view);
-void i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj,
-                                             const struct i915_ggtt_view *view);
+void i915_gem_object_unpin_from_display_plane(struct i915_vma *vma);
 int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
                                int align);
 int i915_gem_open(struct drm_device *dev, struct drm_file *file);
 void i915_gem_release(struct drm_device *dev, struct drm_file *file);
 
-uint32_t
-i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode);
-uint32_t
-i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size,
-                           int tiling_mode, bool fenced);
+u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv, u64 size,
+                          int tiling_mode);
+u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size,
+                               int tiling_mode, bool fenced);
 
 int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                                    enum i915_cache_level cache_level);
@@ -3443,86 +3307,82 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
 struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
                                struct drm_gem_object *gem_obj, int flags);
 
-u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
-                                 const struct i915_ggtt_view *view);
-u64 i915_gem_obj_offset(struct drm_i915_gem_object *o,
-                       struct i915_address_space *vm);
-static inline u64
-i915_gem_obj_ggtt_offset(struct drm_i915_gem_object *o)
-{
-       return i915_gem_obj_ggtt_offset_view(o, &i915_ggtt_view_normal);
-}
-
-bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o);
-bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o,
-                                 const struct i915_ggtt_view *view);
-bool i915_gem_obj_bound(struct drm_i915_gem_object *o,
-                       struct i915_address_space *vm);
-
 struct i915_vma *
 i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
-                   struct i915_address_space *vm);
-struct i915_vma *
-i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj,
-                         const struct i915_ggtt_view *view);
+                    struct i915_address_space *vm,
+                    const struct i915_ggtt_view *view);
 
 struct i915_vma *
 i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
-                                 struct i915_address_space *vm);
-struct i915_vma *
-i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj,
-                                      const struct i915_ggtt_view *view);
-
-static inline struct i915_vma *
-i915_gem_obj_to_ggtt(struct drm_i915_gem_object *obj)
-{
-       return i915_gem_obj_to_ggtt_view(obj, &i915_ggtt_view_normal);
-}
-bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj);
+                                 struct i915_address_space *vm,
+                                 const struct i915_ggtt_view *view);
 
-/* Some GGTT VM helpers */
 static inline struct i915_hw_ppgtt *
 i915_vm_to_ppgtt(struct i915_address_space *vm)
 {
        return container_of(vm, struct i915_hw_ppgtt, base);
 }
 
+static inline struct i915_vma *
+i915_gem_object_to_ggtt(struct drm_i915_gem_object *obj,
+                       const struct i915_ggtt_view *view)
+{
+       return i915_gem_obj_to_vma(obj, &to_i915(obj->base.dev)->ggtt.base, view);
+}
 
-static inline bool i915_gem_obj_ggtt_bound(struct drm_i915_gem_object *obj)
+static inline unsigned long
+i915_gem_object_ggtt_offset(struct drm_i915_gem_object *o,
+                           const struct i915_ggtt_view *view)
 {
-       return i915_gem_obj_ggtt_bound_view(obj, &i915_ggtt_view_normal);
+       return i915_ggtt_offset(i915_gem_object_to_ggtt(o, view));
 }
 
-unsigned long
-i915_gem_obj_ggtt_size(struct drm_i915_gem_object *obj);
+/* i915_gem_fence.c */
+int __must_check i915_vma_get_fence(struct i915_vma *vma);
+int __must_check i915_vma_put_fence(struct i915_vma *vma);
 
-static inline int __must_check
-i915_gem_obj_ggtt_pin(struct drm_i915_gem_object *obj,
-                     uint32_t alignment,
-                     unsigned flags)
+/**
+ * i915_vma_pin_fence - pin fencing state
+ * @vma: vma to pin fencing for
+ *
+ * This pins the fencing state (whether tiled or untiled) to make sure the
+ * vma (and its object) is ready to be used as a scanout target. Fencing
+ * status must be synchronize first by calling i915_vma_get_fence():
+ *
+ * The resulting fence pin reference must be released again with
+ * i915_vma_unpin_fence().
+ *
+ * Returns:
+ *
+ * True if the vma has a fence, false otherwise.
+ */
+static inline bool
+i915_vma_pin_fence(struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-
-       return i915_gem_object_pin(obj, &ggtt->base,
-                                  alignment, flags | PIN_GLOBAL);
+       if (vma->fence) {
+               vma->fence->pin_count++;
+               return true;
+       } else
+               return false;
 }
 
-void i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj,
-                                    const struct i915_ggtt_view *view);
+/**
+ * i915_vma_unpin_fence - unpin fencing state
+ * @vma: vma to unpin fencing for
+ *
+ * This releases the fence pin reference acquired through
+ * i915_vma_pin_fence. It will handle both objects with and without an
+ * attached fence correctly, callers do not need to distinguish this.
+ */
 static inline void
-i915_gem_object_ggtt_unpin(struct drm_i915_gem_object *obj)
+i915_vma_unpin_fence(struct i915_vma *vma)
 {
-       i915_gem_object_ggtt_unpin_view(obj, &i915_ggtt_view_normal);
+       if (vma->fence) {
+               GEM_BUG_ON(vma->fence->pin_count <= 0);
+               vma->fence->pin_count--;
+       }
 }
 
-/* i915_gem_fence.c */
-int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj);
-int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj);
-
-bool i915_gem_object_pin_fence(struct drm_i915_gem_object *obj);
-void i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj);
-
 void i915_gem_restore_fences(struct drm_device *dev);
 
 void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
@@ -3533,10 +3393,10 @@ void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj);
 int __must_check i915_gem_context_init(struct drm_device *dev);
 void i915_gem_context_lost(struct drm_i915_private *dev_priv);
 void i915_gem_context_fini(struct drm_device *dev);
-void i915_gem_context_reset(struct drm_device *dev);
 int i915_gem_context_open(struct drm_device *dev, struct drm_file *file);
 void i915_gem_context_close(struct drm_device *dev, struct drm_file *file);
 int i915_switch_context(struct drm_i915_gem_request *req);
+int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv);
 void i915_gem_context_free(struct kref *ctx_ref);
 struct drm_i915_gem_object *
 i915_gem_alloc_context_obj(struct drm_device *dev, size_t size);
@@ -3557,12 +3417,14 @@ i915_gem_context_lookup(struct drm_i915_file_private *file_priv, u32 id)
        return ctx;
 }
 
-static inline void i915_gem_context_reference(struct i915_gem_context *ctx)
+static inline struct i915_gem_context *
+i915_gem_context_get(struct i915_gem_context *ctx)
 {
        kref_get(&ctx->ref);
+       return ctx;
 }
 
-static inline void i915_gem_context_unreference(struct i915_gem_context *ctx)
+static inline void i915_gem_context_put(struct i915_gem_context *ctx)
 {
        lockdep_assert_held(&ctx->i915->drm.struct_mutex);
        kref_put(&ctx->ref, i915_gem_context_free);
@@ -3585,13 +3447,10 @@ int i915_gem_context_reset_stats_ioctl(struct drm_device *dev, void *data,
                                       struct drm_file *file);
 
 /* i915_gem_evict.c */
-int __must_check i915_gem_evict_something(struct drm_device *dev,
-                                         struct i915_address_space *vm,
-                                         int min_size,
-                                         unsigned alignment,
+int __must_check i915_gem_evict_something(struct i915_address_space *vm,
+                                         u64 min_size, u64 alignment,
                                          unsigned cache_level,
-                                         unsigned long start,
-                                         unsigned long end,
+                                         u64 start, u64 end,
                                          unsigned flags);
 int __must_check i915_gem_evict_for_vma(struct i915_vma *target);
 int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle);
@@ -3644,28 +3503,21 @@ static inline bool i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_objec
        struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
 
        return dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_9_10_17 &&
-               obj->tiling_mode != I915_TILING_NONE;
+               i915_gem_object_is_tiled(obj);
 }
 
-/* i915_gem_debug.c */
-#if WATCH_LISTS
-int i915_verify_lists(struct drm_device *dev);
-#else
-#define i915_verify_lists(dev) 0
-#endif
-
 /* i915_debugfs.c */
 #ifdef CONFIG_DEBUG_FS
 int i915_debugfs_register(struct drm_i915_private *dev_priv);
 void i915_debugfs_unregister(struct drm_i915_private *dev_priv);
 int i915_debugfs_connector_add(struct drm_connector *connector);
-void intel_display_crc_init(struct drm_device *dev);
+void intel_display_crc_init(struct drm_i915_private *dev_priv);
 #else
 static inline int i915_debugfs_register(struct drm_i915_private *dev_priv) {return 0;}
 static inline void i915_debugfs_unregister(struct drm_i915_private *dev_priv) {}
 static inline int i915_debugfs_connector_add(struct drm_connector *connector)
 { return 0; }
-static inline void intel_display_crc_init(struct drm_device *dev) {}
+static inline void intel_display_crc_init(struct drm_i915_private *dev_priv) {}
 #endif
 
 /* i915_gpu_error.c */
@@ -3694,23 +3546,23 @@ const char *i915_cache_level_str(struct drm_i915_private *i915, int type);
 
 /* i915_cmd_parser.c */
 int i915_cmd_parser_get_version(struct drm_i915_private *dev_priv);
-int i915_cmd_parser_init_ring(struct intel_engine_cs *engine);
-void i915_cmd_parser_fini_ring(struct intel_engine_cs *engine);
-bool i915_needs_cmd_parser(struct intel_engine_cs *engine);
-int i915_parse_cmds(struct intel_engine_cs *engine,
-                   struct drm_i915_gem_object *batch_obj,
-                   struct drm_i915_gem_object *shadow_batch_obj,
-                   u32 batch_start_offset,
-                   u32 batch_len,
-                   bool is_master);
+void intel_engine_init_cmd_parser(struct intel_engine_cs *engine);
+void intel_engine_cleanup_cmd_parser(struct intel_engine_cs *engine);
+bool intel_engine_needs_cmd_parser(struct intel_engine_cs *engine);
+int intel_engine_cmd_parser(struct intel_engine_cs *engine,
+                           struct drm_i915_gem_object *batch_obj,
+                           struct drm_i915_gem_object *shadow_batch_obj,
+                           u32 batch_start_offset,
+                           u32 batch_len,
+                           bool is_master);
 
 /* i915_suspend.c */
 extern int i915_save_state(struct drm_device *dev);
 extern int i915_restore_state(struct drm_device *dev);
 
 /* i915_sysfs.c */
-void i915_setup_sysfs(struct drm_device *dev_priv);
-void i915_teardown_sysfs(struct drm_device *dev_priv);
+void i915_setup_sysfs(struct drm_i915_private *dev_priv);
+void i915_teardown_sysfs(struct drm_i915_private *dev_priv);
 
 /* intel_i2c.c */
 extern int intel_setup_gmbus(struct drm_device *dev);
@@ -3810,7 +3662,6 @@ extern void intel_set_rps(struct drm_i915_private *dev_priv, u8 val);
 extern void intel_set_memory_cxsr(struct drm_i915_private *dev_priv,
                                  bool enable);
 
-extern bool i915_semaphore_is_enabled(struct drm_i915_private *dev_priv);
 int i915_reg_read_ioctl(struct drm_device *dev, void *data,
                        struct drm_file *file);
 
@@ -3888,9 +3739,16 @@ int intel_freq_opcode(struct drm_i915_private *dev_priv, int val);
  * will be implemented using 2 32-bit writes in an arbitrary order with
  * an arbitrary delay between them. This can cause the hardware to
  * act upon the intermediate value, possibly leading to corruption and
- * machine death. You have been warned.
+ * machine death. For this reason we do not support I915_WRITE64, or
+ * dev_priv->uncore.funcs.mmio_writeq.
+ *
+ * When reading a 64-bit value as two 32-bit values, the delay may cause
+ * the two reads to mismatch, e.g. a timestamp overflowing. Also note that
+ * occasionally a 64-bit register does not actualy support a full readq
+ * and must be read using two 32-bit reads.
+ *
+ * You have been warned.
  */
-#define I915_WRITE64(reg, val) dev_priv->uncore.funcs.mmio_writeq(dev_priv, (reg), (val), true)
 #define I915_READ64(reg)       dev_priv->uncore.funcs.mmio_readq(dev_priv, (reg), true)
 
 #define I915_READ64_2x32(lower_reg, upper_reg) ({                      \
@@ -3933,7 +3791,7 @@ __raw_write(64, q)
 #undef __raw_write
 
 /* These are untraced mmio-accessors that are only valid to be used inside
- * criticial sections inside IRQ handlers where forcewake is explicitly
+ * critical sections inside IRQ handlers where forcewake is explicitly
  * controlled.
  * Think twice, and think again, before using these.
  * Note: Should only be used between intel_uncore_forcewake_irqlock() and
@@ -4005,7 +3863,9 @@ wait_remaining_ms_from_jiffies(unsigned long timestamp_jiffies, int to_wait_ms)
                            schedule_timeout_uninterruptible(remaining_jiffies);
        }
 }
-static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req)
+
+static inline bool
+__i915_request_irq_complete(struct drm_i915_gem_request *req)
 {
        struct intel_engine_cs *engine = req->engine;
 
@@ -4027,7 +3887,7 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req)
         * is woken.
         */
        if (engine->irq_seqno_barrier &&
-           READ_ONCE(engine->breadcrumbs.irq_seqno_bh) == current &&
+           rcu_access_pointer(engine->breadcrumbs.irq_seqno_bh) == current &&
            cmpxchg_relaxed(&engine->breadcrumbs.irq_posted, 1, 0)) {
                struct task_struct *tsk;
 
@@ -4052,7 +3912,7 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req)
                 * irq_posted == false but we are still running).
                 */
                rcu_read_lock();
-               tsk = READ_ONCE(engine->breadcrumbs.irq_seqno_bh);
+               tsk = rcu_dereference(engine->breadcrumbs.irq_seqno_bh);
                if (tsk && tsk != current)
                        /* Note that if the bottom-half is changed as we
                         * are sending the wake-up, the new bottom-half will
@@ -4067,18 +3927,35 @@ static inline bool __i915_request_irq_complete(struct drm_i915_gem_request *req)
                        return true;
        }
 
-       /* We need to check whether any gpu reset happened in between
-        * the request being submitted and now. If a reset has occurred,
-        * the seqno will have been advance past ours and our request
-        * is complete. If we are in the process of handling a reset,
-        * the request is effectively complete as the rendering will
-        * be discarded, but we need to return in order to drop the
-        * struct_mutex.
-        */
-       if (i915_reset_in_progress(&req->i915->gpu_error))
-               return true;
-
        return false;
 }
 
+void i915_memcpy_init_early(struct drm_i915_private *dev_priv);
+bool i915_memcpy_from_wc(void *dst, const void *src, unsigned long len);
+
+/* i915_mm.c */
+int remap_io_mapping(struct vm_area_struct *vma,
+                    unsigned long addr, unsigned long pfn, unsigned long size,
+                    struct io_mapping *iomap);
+
+#define ptr_mask_bits(ptr) ({                                          \
+       unsigned long __v = (unsigned long)(ptr);                       \
+       (typeof(ptr))(__v & PAGE_MASK);                                 \
+})
+
+#define ptr_unpack_bits(ptr, bits) ({                                  \
+       unsigned long __v = (unsigned long)(ptr);                       \
+       (bits) = __v & ~PAGE_MASK;                                      \
+       (typeof(ptr))(__v & PAGE_MASK);                                 \
+})
+
+#define ptr_pack_bits(ptr, bits)                                       \
+       ((typeof(ptr))((unsigned long)(ptr) | (bits)))
+
+#define fetch_and_zero(ptr) ({                                         \
+       typeof(*ptr) __T = *(ptr);                                      \
+       *(ptr) = (typeof(*ptr))0;                                       \
+       __T;                                                            \
+})
+
 #endif
index ccf56c6..947e82c 100644 (file)
 #include <drm/drm_vma_manager.h>
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
+#include "i915_gem_dmabuf.h"
 #include "i915_vgpu.h"
 #include "i915_trace.h"
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 #include "intel_mocs.h"
+#include <linux/reservation.h>
 #include <linux/shmem_fs.h>
 #include <linux/slab.h>
 #include <linux/swap.h>
 
 static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj);
 static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj);
-static void
-i915_gem_object_retire__write(struct drm_i915_gem_object *obj);
-static void
-i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring);
 
 static bool cpu_cache_is_coherent(struct drm_device *dev,
                                  enum i915_cache_level level)
@@ -139,7 +138,6 @@ int i915_mutex_lock_interruptible(struct drm_device *dev)
        if (ret)
                return ret;
 
-       WARN_ON(i915_verify_lists(dev));
        return 0;
 }
 
@@ -156,10 +154,10 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
        pinned = 0;
        mutex_lock(&dev->struct_mutex);
        list_for_each_entry(vma, &ggtt->base.active_list, vm_link)
-               if (vma->pin_count)
+               if (i915_vma_is_pinned(vma))
                        pinned += vma->node.size;
        list_for_each_entry(vma, &ggtt->base.inactive_list, vm_link)
-               if (vma->pin_count)
+               if (i915_vma_is_pinned(vma))
                        pinned += vma->node.size;
        mutex_unlock(&dev->struct_mutex);
 
@@ -281,23 +279,129 @@ static const struct drm_i915_gem_object_ops i915_gem_phys_ops = {
        .release = i915_gem_object_release_phys,
 };
 
-static int
-drop_pages(struct drm_i915_gem_object *obj)
+int i915_gem_object_unbind(struct drm_i915_gem_object *obj)
 {
-       struct i915_vma *vma, *next;
+       struct i915_vma *vma;
+       LIST_HEAD(still_in_list);
        int ret;
 
-       drm_gem_object_reference(&obj->base);
-       list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link)
-               if (i915_vma_unbind(vma))
-                       break;
+       lockdep_assert_held(&obj->base.dev->struct_mutex);
 
-       ret = i915_gem_object_put_pages(obj);
-       drm_gem_object_unreference(&obj->base);
+       /* Closed vma are removed from the obj->vma_list - but they may
+        * still have an active binding on the object. To remove those we
+        * must wait for all rendering to complete to the object (as unbinding
+        * must anyway), and retire the requests.
+        */
+       ret = i915_gem_object_wait_rendering(obj, false);
+       if (ret)
+               return ret;
+
+       i915_gem_retire_requests(to_i915(obj->base.dev));
+
+       while ((vma = list_first_entry_or_null(&obj->vma_list,
+                                              struct i915_vma,
+                                              obj_link))) {
+               list_move_tail(&vma->obj_link, &still_in_list);
+               ret = i915_vma_unbind(vma);
+               if (ret)
+                       break;
+       }
+       list_splice(&still_in_list, &obj->vma_list);
 
        return ret;
 }
 
+/**
+ * Ensures that all rendering to the object has completed and the object is
+ * safe to unbind from the GTT or access from the CPU.
+ * @obj: i915 gem object
+ * @readonly: waiting for just read access or read-write access
+ */
+int
+i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
+                              bool readonly)
+{
+       struct reservation_object *resv;
+       struct i915_gem_active *active;
+       unsigned long active_mask;
+       int idx;
+
+       lockdep_assert_held(&obj->base.dev->struct_mutex);
+
+       if (!readonly) {
+               active = obj->last_read;
+               active_mask = i915_gem_object_get_active(obj);
+       } else {
+               active_mask = 1;
+               active = &obj->last_write;
+       }
+
+       for_each_active(active_mask, idx) {
+               int ret;
+
+               ret = i915_gem_active_wait(&active[idx],
+                                          &obj->base.dev->struct_mutex);
+               if (ret)
+                       return ret;
+       }
+
+       resv = i915_gem_object_get_dmabuf_resv(obj);
+       if (resv) {
+               long err;
+
+               err = reservation_object_wait_timeout_rcu(resv, !readonly, true,
+                                                         MAX_SCHEDULE_TIMEOUT);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+/* A nonblocking variant of the above wait. Must be called prior to
+ * acquiring the mutex for the object, as the object state may change
+ * during this call. A reference must be held by the caller for the object.
+ */
+static __must_check int
+__unsafe_wait_rendering(struct drm_i915_gem_object *obj,
+                       struct intel_rps_client *rps,
+                       bool readonly)
+{
+       struct i915_gem_active *active;
+       unsigned long active_mask;
+       int idx;
+
+       active_mask = __I915_BO_ACTIVE(obj);
+       if (!active_mask)
+               return 0;
+
+       if (!readonly) {
+               active = obj->last_read;
+       } else {
+               active_mask = 1;
+               active = &obj->last_write;
+       }
+
+       for_each_active(active_mask, idx) {
+               int ret;
+
+               ret = i915_gem_active_wait_unlocked(&active[idx],
+                                                   I915_WAIT_INTERRUPTIBLE,
+                                                   NULL, rps);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static struct intel_rps_client *to_rps_client(struct drm_file *file)
+{
+       struct drm_i915_file_private *fpriv = file->driver_priv;
+
+       return &fpriv->rps;
+}
+
 int
 i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
                            int align)
@@ -318,7 +422,11 @@ i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
        if (obj->base.filp == NULL)
                return -EINVAL;
 
-       ret = drop_pages(obj);
+       ret = i915_gem_object_unbind(obj);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_object_put_pages(obj);
        if (ret)
                return ret;
 
@@ -408,7 +516,7 @@ i915_gem_create(struct drm_file *file,
 
        ret = drm_gem_handle_create(file, &obj->base, &handle);
        /* drop reference from allocate - handle holds it now */
-       drm_gem_object_unreference_unlocked(&obj->base);
+       i915_gem_object_put_unlocked(obj);
        if (ret)
                return ret;
 
@@ -502,33 +610,106 @@ __copy_from_user_swizzled(char *gpu_vaddr, int gpu_offset,
  * flush the object from the CPU cache.
  */
 int i915_gem_obj_prepare_shmem_read(struct drm_i915_gem_object *obj,
-                                   int *needs_clflush)
+                                   unsigned int *needs_clflush)
 {
        int ret;
 
        *needs_clflush = 0;
 
-       if (WARN_ON(!i915_gem_object_has_struct_page(obj)))
-               return -EINVAL;
+       if (!i915_gem_object_has_struct_page(obj))
+               return -ENODEV;
+
+       ret = i915_gem_object_wait_rendering(obj, true);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_object_get_pages(obj);
+       if (ret)
+               return ret;
+
+       i915_gem_object_pin_pages(obj);
+
+       i915_gem_object_flush_gtt_write_domain(obj);
 
-       if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU)) {
-               /* If we're not in the cpu read domain, set ourself into the gtt
-                * read domain and manually flush cachelines (if required). This
-                * optimizes for the case when the gpu will dirty the data
-                * anyway again before the next pread happens. */
+       /* If we're not in the cpu read domain, set ourself into the gtt
+        * read domain and manually flush cachelines (if required). This
+        * optimizes for the case when the gpu will dirty the data
+        * anyway again before the next pread happens.
+        */
+       if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU))
                *needs_clflush = !cpu_cache_is_coherent(obj->base.dev,
                                                        obj->cache_level);
-               ret = i915_gem_object_wait_rendering(obj, true);
+
+       if (*needs_clflush && !static_cpu_has(X86_FEATURE_CLFLUSH)) {
+               ret = i915_gem_object_set_to_cpu_domain(obj, false);
                if (ret)
-                       return ret;
+                       goto err_unpin;
+
+               *needs_clflush = 0;
        }
 
+       /* return with the pages pinned */
+       return 0;
+
+err_unpin:
+       i915_gem_object_unpin_pages(obj);
+       return ret;
+}
+
+int i915_gem_obj_prepare_shmem_write(struct drm_i915_gem_object *obj,
+                                    unsigned int *needs_clflush)
+{
+       int ret;
+
+       *needs_clflush = 0;
+       if (!i915_gem_object_has_struct_page(obj))
+               return -ENODEV;
+
+       ret = i915_gem_object_wait_rendering(obj, false);
+       if (ret)
+               return ret;
+
        ret = i915_gem_object_get_pages(obj);
        if (ret)
                return ret;
 
        i915_gem_object_pin_pages(obj);
 
+       i915_gem_object_flush_gtt_write_domain(obj);
+
+       /* If we're not in the cpu write domain, set ourself into the
+        * gtt write domain and manually flush cachelines (as required).
+        * This optimizes for the case when the gpu will use the data
+        * right away and we therefore have to clflush anyway.
+        */
+       if (obj->base.write_domain != I915_GEM_DOMAIN_CPU)
+               *needs_clflush |= cpu_write_needs_clflush(obj) << 1;
+
+       /* Same trick applies to invalidate partially written cachelines read
+        * before writing.
+        */
+       if (!(obj->base.read_domains & I915_GEM_DOMAIN_CPU))
+               *needs_clflush |= !cpu_cache_is_coherent(obj->base.dev,
+                                                        obj->cache_level);
+
+       if (*needs_clflush && !static_cpu_has(X86_FEATURE_CLFLUSH)) {
+               ret = i915_gem_object_set_to_cpu_domain(obj, true);
+               if (ret)
+                       goto err_unpin;
+
+               *needs_clflush = 0;
+       }
+
+       if ((*needs_clflush & CLFLUSH_AFTER) == 0)
+               obj->cache_dirty = true;
+
+       intel_fb_obj_invalidate(obj, ORIGIN_CPU);
+       obj->dirty = 1;
+       /* return with the pages pinned */
+       return 0;
+
+err_unpin:
+       i915_gem_object_unpin_pages(obj);
        return ret;
 }
 
@@ -638,14 +819,24 @@ i915_gem_gtt_pread(struct drm_device *dev,
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
+       struct i915_vma *vma;
        struct drm_mm_node node;
        char __user *user_data;
        uint64_t remain;
        uint64_t offset;
        int ret;
 
-       ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE);
-       if (ret) {
+       vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, PIN_MAPPABLE);
+       if (!IS_ERR(vma)) {
+               node.start = i915_ggtt_offset(vma);
+               node.allocated = false;
+               ret = i915_vma_put_fence(vma);
+               if (ret) {
+                       i915_vma_unpin(vma);
+                       vma = ERR_PTR(ret);
+               }
+       }
+       if (IS_ERR(vma)) {
                ret = insert_mappable_node(dev_priv, &node, PAGE_SIZE);
                if (ret)
                        goto out;
@@ -657,12 +848,6 @@ i915_gem_gtt_pread(struct drm_device *dev,
                }
 
                i915_gem_object_pin_pages(obj);
-       } else {
-               node.start = i915_gem_obj_ggtt_offset(obj);
-               node.allocated = false;
-               ret = i915_gem_object_put_fence(obj);
-               if (ret)
-                       goto out_unpin;
        }
 
        ret = i915_gem_object_set_to_gtt_domain(obj, false);
@@ -707,7 +892,7 @@ i915_gem_gtt_pread(struct drm_device *dev,
                 * and write to user memory which may result into page
                 * faults, and so we cannot perform this under struct_mutex.
                 */
-               if (slow_user_access(ggtt->mappable, page_base,
+               if (slow_user_access(&ggtt->mappable, page_base,
                                     page_offset, user_data,
                                     page_length, false)) {
                        ret = -EFAULT;
@@ -739,7 +924,7 @@ out_unpin:
                i915_gem_object_unpin_pages(obj);
                remove_mappable_node(&node);
        } else {
-               i915_gem_object_ggtt_unpin(obj);
+               i915_vma_unpin(vma);
        }
 out:
        return ret;
@@ -760,19 +945,14 @@ i915_gem_shmem_pread(struct drm_device *dev,
        int needs_clflush = 0;
        struct sg_page_iter sg_iter;
 
-       if (!i915_gem_object_has_struct_page(obj))
-               return -ENODEV;
-
-       user_data = u64_to_user_ptr(args->data_ptr);
-       remain = args->size;
-
-       obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
-
        ret = i915_gem_obj_prepare_shmem_read(obj, &needs_clflush);
        if (ret)
                return ret;
 
+       obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
+       user_data = u64_to_user_ptr(args->data_ptr);
        offset = args->offset;
+       remain = args->size;
 
        for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents,
                         offset >> PAGE_SHIFT) {
@@ -828,7 +1008,7 @@ next_page:
        }
 
 out:
-       i915_gem_object_unpin_pages(obj);
+       i915_gem_obj_finish_shmem_access(obj);
 
        return ret;
 }
@@ -857,25 +1037,27 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
                       args->size))
                return -EFAULT;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
-
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
+               return -ENOENT;
 
        /* Bounds check source.  */
        if (args->offset > obj->base.size ||
            args->size > obj->base.size - args->offset) {
                ret = -EINVAL;
-               goto out;
+               goto err;
        }
 
        trace_i915_gem_object_pread(obj, args->offset, args->size);
 
+       ret = __unsafe_wait_rendering(obj, to_rps_client(file), true);
+       if (ret)
+               goto err;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               goto err;
+
        ret = i915_gem_shmem_pread(dev, obj, args, file);
 
        /* pread for non shmem backed objects */
@@ -886,10 +1068,13 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
                intel_runtime_pm_put(to_i915(dev));
        }
 
-out:
-       drm_gem_object_unreference(&obj->base);
-unlock:
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
+
+       return ret;
+
+err:
+       i915_gem_object_put_unlocked(obj);
        return ret;
 }
 
@@ -919,7 +1104,7 @@ fast_user_write(struct io_mapping *mapping,
 /**
  * This is the fast pwrite path, where we copy the data directly from the
  * user into the GTT, uncached.
- * @dev: drm device pointer
+ * @i915: i915 device private data
  * @obj: i915 gem object
  * @args: pwrite arguments structure
  * @file: drm file pointer
@@ -932,17 +1117,28 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915,
 {
        struct i915_ggtt *ggtt = &i915->ggtt;
        struct drm_device *dev = obj->base.dev;
+       struct i915_vma *vma;
        struct drm_mm_node node;
        uint64_t remain, offset;
        char __user *user_data;
        int ret;
        bool hit_slow_path = false;
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (i915_gem_object_is_tiled(obj))
                return -EFAULT;
 
-       ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_MAPPABLE | PIN_NONBLOCK);
-       if (ret) {
+       vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
+                                      PIN_MAPPABLE | PIN_NONBLOCK);
+       if (!IS_ERR(vma)) {
+               node.start = i915_ggtt_offset(vma);
+               node.allocated = false;
+               ret = i915_vma_put_fence(vma);
+               if (ret) {
+                       i915_vma_unpin(vma);
+                       vma = ERR_PTR(ret);
+               }
+       }
+       if (IS_ERR(vma)) {
                ret = insert_mappable_node(i915, &node, PAGE_SIZE);
                if (ret)
                        goto out;
@@ -954,19 +1150,13 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915,
                }
 
                i915_gem_object_pin_pages(obj);
-       } else {
-               node.start = i915_gem_obj_ggtt_offset(obj);
-               node.allocated = false;
-               ret = i915_gem_object_put_fence(obj);
-               if (ret)
-                       goto out_unpin;
        }
 
        ret = i915_gem_object_set_to_gtt_domain(obj, true);
        if (ret)
                goto out_unpin;
 
-       intel_fb_obj_invalidate(obj, ORIGIN_GTT);
+       intel_fb_obj_invalidate(obj, ORIGIN_CPU);
        obj->dirty = true;
 
        user_data = u64_to_user_ptr(args->data_ptr);
@@ -998,11 +1188,11 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_private *i915,
                 * If the object is non-shmem backed, we retry again with the
                 * path that handles page fault.
                 */
-               if (fast_user_write(ggtt->mappable, page_base,
+               if (fast_user_write(&ggtt->mappable, page_base,
                                    page_offset, user_data, page_length)) {
                        hit_slow_path = true;
                        mutex_unlock(&dev->struct_mutex);
-                       if (slow_user_access(ggtt->mappable,
+                       if (slow_user_access(&ggtt->mappable,
                                             page_base,
                                             page_offset, user_data,
                                             page_length, true)) {
@@ -1033,7 +1223,7 @@ out_flush:
                }
        }
 
-       intel_fb_obj_flush(obj, false, ORIGIN_GTT);
+       intel_fb_obj_flush(obj, false, ORIGIN_CPU);
 out_unpin:
        if (node.allocated) {
                wmb();
@@ -1043,7 +1233,7 @@ out_unpin:
                i915_gem_object_unpin_pages(obj);
                remove_mappable_node(&node);
        } else {
-               i915_gem_object_ggtt_unpin(obj);
+               i915_vma_unpin(vma);
        }
 out:
        return ret;
@@ -1126,41 +1316,17 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
        int shmem_page_offset, page_length, ret = 0;
        int obj_do_bit17_swizzling, page_do_bit17_swizzling;
        int hit_slowpath = 0;
-       int needs_clflush_after = 0;
-       int needs_clflush_before = 0;
+       unsigned int needs_clflush;
        struct sg_page_iter sg_iter;
 
-       user_data = u64_to_user_ptr(args->data_ptr);
-       remain = args->size;
-
-       obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
-
-       if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
-               /* If we're not in the cpu write domain, set ourself into the gtt
-                * write domain and manually flush cachelines (if required). This
-                * optimizes for the case when the gpu will use the data
-                * right away and we therefore have to clflush anyway. */
-               needs_clflush_after = cpu_write_needs_clflush(obj);
-               ret = i915_gem_object_wait_rendering(obj, false);
-               if (ret)
-                       return ret;
-       }
-       /* Same trick applies to invalidate partially written cachelines read
-        * before writing. */
-       if ((obj->base.read_domains & I915_GEM_DOMAIN_CPU) == 0)
-               needs_clflush_before =
-                       !cpu_cache_is_coherent(dev, obj->cache_level);
-
-       ret = i915_gem_object_get_pages(obj);
+       ret = i915_gem_obj_prepare_shmem_write(obj, &needs_clflush);
        if (ret)
                return ret;
 
-       intel_fb_obj_invalidate(obj, ORIGIN_CPU);
-
-       i915_gem_object_pin_pages(obj);
-
+       obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
+       user_data = u64_to_user_ptr(args->data_ptr);
        offset = args->offset;
-       obj->dirty = 1;
+       remain = args->size;
 
        for_each_sg_page(obj->pages->sgl, &sg_iter, obj->pages->nents,
                         offset >> PAGE_SHIFT) {
@@ -1184,7 +1350,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                /* If we don't overwrite a cacheline completely we need to be
                 * careful to have up-to-date data by first clflushing. Don't
                 * overcomplicate things and flush the entire patch. */
-               partial_cacheline_write = needs_clflush_before &&
+               partial_cacheline_write = needs_clflush & CLFLUSH_BEFORE &&
                        ((shmem_page_offset | page_length)
                                & (boot_cpu_data.x86_clflush_size - 1));
 
@@ -1194,7 +1360,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                ret = shmem_pwrite_fast(page, shmem_page_offset, page_length,
                                        user_data, page_do_bit17_swizzling,
                                        partial_cacheline_write,
-                                       needs_clflush_after);
+                                       needs_clflush & CLFLUSH_AFTER);
                if (ret == 0)
                        goto next_page;
 
@@ -1203,7 +1369,7 @@ i915_gem_shmem_pwrite(struct drm_device *dev,
                ret = shmem_pwrite_slow(page, shmem_page_offset, page_length,
                                        user_data, page_do_bit17_swizzling,
                                        partial_cacheline_write,
-                                       needs_clflush_after);
+                                       needs_clflush & CLFLUSH_AFTER);
 
                mutex_lock(&dev->struct_mutex);
 
@@ -1217,7 +1383,7 @@ next_page:
        }
 
 out:
-       i915_gem_object_unpin_pages(obj);
+       i915_gem_obj_finish_shmem_access(obj);
 
        if (hit_slowpath) {
                /*
@@ -1225,17 +1391,15 @@ out:
                 * cachelines in-line while writing and the object moved
                 * out of the cpu write domain while we've dropped the lock.
                 */
-               if (!needs_clflush_after &&
+               if (!(needs_clflush & CLFLUSH_AFTER) &&
                    obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
                        if (i915_gem_clflush_object(obj, obj->pin_display))
-                               needs_clflush_after = true;
+                               needs_clflush |= CLFLUSH_AFTER;
                }
        }
 
-       if (needs_clflush_after)
+       if (needs_clflush & CLFLUSH_AFTER)
                i915_gem_chipset_flush(to_i915(dev));
-       else
-               obj->cache_dirty = true;
 
        intel_fb_obj_flush(obj, false, ORIGIN_CPU);
        return ret;
@@ -1273,27 +1437,29 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
                        return -EFAULT;
        }
 
-       intel_runtime_pm_get(dev_priv);
-
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               goto put_rpm;
-
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
+               return -ENOENT;
 
        /* Bounds check destination. */
        if (args->offset > obj->base.size ||
            args->size > obj->base.size - args->offset) {
                ret = -EINVAL;
-               goto out;
+               goto err;
        }
 
        trace_i915_gem_object_pwrite(obj, args->offset, args->size);
 
+       ret = __unsafe_wait_rendering(obj, to_rps_client(file), false);
+       if (ret)
+               goto err;
+
+       intel_runtime_pm_get(dev_priv);
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               goto err_rpm;
+
        ret = -EFAULT;
        /* We can only do the GTT pwrite on untiled buffers, as otherwise
         * it would end up going through the fenced access, and we'll get
@@ -1312,505 +1478,28 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
        if (ret == -EFAULT || ret == -ENOSPC) {
                if (obj->phys_handle)
                        ret = i915_gem_phys_pwrite(obj, args, file);
-               else if (i915_gem_object_has_struct_page(obj))
-                       ret = i915_gem_shmem_pwrite(dev, obj, args, file);
                else
-                       ret = -ENODEV;
+                       ret = i915_gem_shmem_pwrite(dev, obj, args, file);
        }
 
-out:
-       drm_gem_object_unreference(&obj->base);
-unlock:
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
-put_rpm:
        intel_runtime_pm_put(dev_priv);
 
        return ret;
-}
-
-static int
-i915_gem_check_wedge(unsigned reset_counter, bool interruptible)
-{
-       if (__i915_terminally_wedged(reset_counter))
-               return -EIO;
-
-       if (__i915_reset_in_progress(reset_counter)) {
-               /* Non-interruptible callers can't handle -EAGAIN, hence return
-                * -EIO unconditionally for these. */
-               if (!interruptible)
-                       return -EIO;
-
-               return -EAGAIN;
-       }
 
-       return 0;
+err_rpm:
+       intel_runtime_pm_put(dev_priv);
+err:
+       i915_gem_object_put_unlocked(obj);
+       return ret;
 }
 
-static unsigned long local_clock_us(unsigned *cpu)
+static inline enum fb_op_origin
+write_origin(struct drm_i915_gem_object *obj, unsigned domain)
 {
-       unsigned long t;
-
-       /* Cheaply and approximately convert from nanoseconds to microseconds.
-        * The result and subsequent calculations are also defined in the same
-        * approximate microseconds units. The principal source of timing
-        * error here is from the simple truncation.
-        *
-        * Note that local_clock() is only defined wrt to the current CPU;
-        * the comparisons are no longer valid if we switch CPUs. Instead of
-        * blocking preemption for the entire busywait, we can detect the CPU
-        * switch and use that as indicator of system load and a reason to
-        * stop busywaiting, see busywait_stop().
-        */
-       *cpu = get_cpu();
-       t = local_clock() >> 10;
-       put_cpu();
-
-       return t;
-}
-
-static bool busywait_stop(unsigned long timeout, unsigned cpu)
-{
-       unsigned this_cpu;
-
-       if (time_after(local_clock_us(&this_cpu), timeout))
-               return true;
-
-       return this_cpu != cpu;
-}
-
-bool __i915_spin_request(const struct drm_i915_gem_request *req,
-                        int state, unsigned long timeout_us)
-{
-       unsigned cpu;
-
-       /* When waiting for high frequency requests, e.g. during synchronous
-        * rendering split between the CPU and GPU, the finite amount of time
-        * required to set up the irq and wait upon it limits the response
-        * rate. By busywaiting on the request completion for a short while we
-        * can service the high frequency waits as quick as possible. However,
-        * if it is a slow request, we want to sleep as quickly as possible.
-        * The tradeoff between waiting and sleeping is roughly the time it
-        * takes to sleep on a request, on the order of a microsecond.
-        */
-
-       timeout_us += local_clock_us(&cpu);
-       do {
-               if (i915_gem_request_completed(req))
-                       return true;
-
-               if (signal_pending_state(state, current))
-                       break;
-
-               if (busywait_stop(timeout_us, cpu))
-                       break;
-
-               cpu_relax_lowlatency();
-       } while (!need_resched());
-
-       return false;
-}
-
-/**
- * __i915_wait_request - wait until execution of request has finished
- * @req: duh!
- * @interruptible: do an interruptible wait (normally yes)
- * @timeout: in - how long to wait (NULL forever); out - how much time remaining
- * @rps: RPS client
- *
- * Note: It is of utmost importance that the passed in seqno and reset_counter
- * values have been read by the caller in an smp safe manner. Where read-side
- * locks are involved, it is sufficient to read the reset_counter before
- * unlocking the lock that protects the seqno. For lockless tricks, the
- * reset_counter _must_ be read before, and an appropriate smp_rmb must be
- * inserted.
- *
- * Returns 0 if the request was found within the alloted time. Else returns the
- * errno with remaining time filled in timeout argument.
- */
-int __i915_wait_request(struct drm_i915_gem_request *req,
-                       bool interruptible,
-                       s64 *timeout,
-                       struct intel_rps_client *rps)
-{
-       int state = interruptible ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
-       DEFINE_WAIT(reset);
-       struct intel_wait wait;
-       unsigned long timeout_remain;
-       s64 before = 0; /* Only to silence a compiler warning. */
-       int ret = 0;
-
-       might_sleep();
-
-       if (list_empty(&req->list))
-               return 0;
-
-       if (i915_gem_request_completed(req))
-               return 0;
-
-       timeout_remain = MAX_SCHEDULE_TIMEOUT;
-       if (timeout) {
-               if (WARN_ON(*timeout < 0))
-                       return -EINVAL;
-
-               if (*timeout == 0)
-                       return -ETIME;
-
-               timeout_remain = nsecs_to_jiffies_timeout(*timeout);
-
-               /*
-                * Record current time in case interrupted by signal, or wedged.
-                */
-               before = ktime_get_raw_ns();
-       }
-
-       trace_i915_gem_request_wait_begin(req);
-
-       /* This client is about to stall waiting for the GPU. In many cases
-        * this is undesirable and limits the throughput of the system, as
-        * many clients cannot continue processing user input/output whilst
-        * blocked. RPS autotuning may take tens of milliseconds to respond
-        * to the GPU load and thus incurs additional latency for the client.
-        * We can circumvent that by promoting the GPU frequency to maximum
-        * before we wait. This makes the GPU throttle up much more quickly
-        * (good for benchmarks and user experience, e.g. window animations),
-        * but at a cost of spending more power processing the workload
-        * (bad for battery). Not all clients even want their results
-        * immediately and for them we should just let the GPU select its own
-        * frequency to maximise efficiency. To prevent a single client from
-        * forcing the clocks too high for the whole system, we only allow
-        * each client to waitboost once in a busy period.
-        */
-       if (INTEL_INFO(req->i915)->gen >= 6)
-               gen6_rps_boost(req->i915, rps, req->emitted_jiffies);
-
-       /* Optimistic spin for the next ~jiffie before touching IRQs */
-       if (i915_spin_request(req, state, 5))
-               goto complete;
-
-       set_current_state(state);
-       add_wait_queue(&req->i915->gpu_error.wait_queue, &reset);
-
-       intel_wait_init(&wait, req->seqno);
-       if (intel_engine_add_wait(req->engine, &wait))
-               /* In order to check that we haven't missed the interrupt
-                * as we enabled it, we need to kick ourselves to do a
-                * coherent check on the seqno before we sleep.
-                */
-               goto wakeup;
-
-       for (;;) {
-               if (signal_pending_state(state, current)) {
-                       ret = -ERESTARTSYS;
-                       break;
-               }
-
-               timeout_remain = io_schedule_timeout(timeout_remain);
-               if (timeout_remain == 0) {
-                       ret = -ETIME;
-                       break;
-               }
-
-               if (intel_wait_complete(&wait))
-                       break;
-
-               set_current_state(state);
-
-wakeup:
-               /* Carefully check if the request is complete, giving time
-                * for the seqno to be visible following the interrupt.
-                * We also have to check in case we are kicked by the GPU
-                * reset in order to drop the struct_mutex.
-                */
-               if (__i915_request_irq_complete(req))
-                       break;
-
-               /* Only spin if we know the GPU is processing this request */
-               if (i915_spin_request(req, state, 2))
-                       break;
-       }
-       remove_wait_queue(&req->i915->gpu_error.wait_queue, &reset);
-
-       intel_engine_remove_wait(req->engine, &wait);
-       __set_current_state(TASK_RUNNING);
-complete:
-       trace_i915_gem_request_wait_end(req);
-
-       if (timeout) {
-               s64 tres = *timeout - (ktime_get_raw_ns() - before);
-
-               *timeout = tres < 0 ? 0 : tres;
-
-               /*
-                * Apparently ktime isn't accurate enough and occasionally has a
-                * bit of mismatch in the jiffies<->nsecs<->ktime loop. So patch
-                * things up to make the test happy. We allow up to 1 jiffy.
-                *
-                * This is a regrssion from the timespec->ktime conversion.
-                */
-               if (ret == -ETIME && *timeout < jiffies_to_usecs(1)*1000)
-                       *timeout = 0;
-       }
-
-       if (rps && req->seqno == req->engine->last_submitted_seqno) {
-               /* The GPU is now idle and this client has stalled.
-                * Since no other client has submitted a request in the
-                * meantime, assume that this client is the only one
-                * supplying work to the GPU but is unable to keep that
-                * work supplied because it is waiting. Since the GPU is
-                * then never kept fully busy, RPS autoclocking will
-                * keep the clocks relatively low, causing further delays.
-                * Compensate by giving the synchronous client credit for
-                * a waitboost next time.
-                */
-               spin_lock(&req->i915->rps.client_lock);
-               list_del_init(&rps->link);
-               spin_unlock(&req->i915->rps.client_lock);
-       }
-
-       return ret;
-}
-
-int i915_gem_request_add_to_client(struct drm_i915_gem_request *req,
-                                  struct drm_file *file)
-{
-       struct drm_i915_file_private *file_priv;
-
-       WARN_ON(!req || !file || req->file_priv);
-
-       if (!req || !file)
-               return -EINVAL;
-
-       if (req->file_priv)
-               return -EINVAL;
-
-       file_priv = file->driver_priv;
-
-       spin_lock(&file_priv->mm.lock);
-       req->file_priv = file_priv;
-       list_add_tail(&req->client_list, &file_priv->mm.request_list);
-       spin_unlock(&file_priv->mm.lock);
-
-       req->pid = get_pid(task_pid(current));
-
-       return 0;
-}
-
-static inline void
-i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
-{
-       struct drm_i915_file_private *file_priv = request->file_priv;
-
-       if (!file_priv)
-               return;
-
-       spin_lock(&file_priv->mm.lock);
-       list_del(&request->client_list);
-       request->file_priv = NULL;
-       spin_unlock(&file_priv->mm.lock);
-
-       put_pid(request->pid);
-       request->pid = NULL;
-}
-
-static void i915_gem_request_retire(struct drm_i915_gem_request *request)
-{
-       trace_i915_gem_request_retire(request);
-
-       /* We know the GPU must have read the request to have
-        * sent us the seqno + interrupt, so use the position
-        * of tail of the request to update the last known position
-        * of the GPU head.
-        *
-        * Note this requires that we are always called in request
-        * completion order.
-        */
-       request->ringbuf->last_retired_head = request->postfix;
-
-       list_del_init(&request->list);
-       i915_gem_request_remove_from_client(request);
-
-       if (request->previous_context) {
-               if (i915.enable_execlists)
-                       intel_lr_context_unpin(request->previous_context,
-                                              request->engine);
-       }
-
-       i915_gem_context_unreference(request->ctx);
-       i915_gem_request_unreference(request);
-}
-
-static void
-__i915_gem_request_retire__upto(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       struct drm_i915_gem_request *tmp;
-
-       lockdep_assert_held(&engine->i915->drm.struct_mutex);
-
-       if (list_empty(&req->list))
-               return;
-
-       do {
-               tmp = list_first_entry(&engine->request_list,
-                                      typeof(*tmp), list);
-
-               i915_gem_request_retire(tmp);
-       } while (tmp != req);
-
-       WARN_ON(i915_verify_lists(engine->dev));
-}
-
-/**
- * Waits for a request to be signaled, and cleans up the
- * request and object lists appropriately for that event.
- * @req: request to wait on
- */
-int
-i915_wait_request(struct drm_i915_gem_request *req)
-{
-       struct drm_i915_private *dev_priv = req->i915;
-       bool interruptible;
-       int ret;
-
-       interruptible = dev_priv->mm.interruptible;
-
-       BUG_ON(!mutex_is_locked(&dev_priv->drm.struct_mutex));
-
-       ret = __i915_wait_request(req, interruptible, NULL, NULL);
-       if (ret)
-               return ret;
-
-       /* If the GPU hung, we want to keep the requests to find the guilty. */
-       if (!i915_reset_in_progress(&dev_priv->gpu_error))
-               __i915_gem_request_retire__upto(req);
-
-       return 0;
-}
-
-/**
- * Ensures that all rendering to the object has completed and the object is
- * safe to unbind from the GTT or access from the CPU.
- * @obj: i915 gem object
- * @readonly: waiting for read access or write
- */
-int
-i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj,
-                              bool readonly)
-{
-       int ret, i;
-
-       if (!obj->active)
-               return 0;
-
-       if (readonly) {
-               if (obj->last_write_req != NULL) {
-                       ret = i915_wait_request(obj->last_write_req);
-                       if (ret)
-                               return ret;
-
-                       i = obj->last_write_req->engine->id;
-                       if (obj->last_read_req[i] == obj->last_write_req)
-                               i915_gem_object_retire__read(obj, i);
-                       else
-                               i915_gem_object_retire__write(obj);
-               }
-       } else {
-               for (i = 0; i < I915_NUM_ENGINES; i++) {
-                       if (obj->last_read_req[i] == NULL)
-                               continue;
-
-                       ret = i915_wait_request(obj->last_read_req[i]);
-                       if (ret)
-                               return ret;
-
-                       i915_gem_object_retire__read(obj, i);
-               }
-               GEM_BUG_ON(obj->active);
-       }
-
-       return 0;
-}
-
-static void
-i915_gem_object_retire_request(struct drm_i915_gem_object *obj,
-                              struct drm_i915_gem_request *req)
-{
-       int ring = req->engine->id;
-
-       if (obj->last_read_req[ring] == req)
-               i915_gem_object_retire__read(obj, ring);
-       else if (obj->last_write_req == req)
-               i915_gem_object_retire__write(obj);
-
-       if (!i915_reset_in_progress(&req->i915->gpu_error))
-               __i915_gem_request_retire__upto(req);
-}
-
-/* A nonblocking variant of the above wait. This is a highly dangerous routine
- * as the object state may change during this call.
- */
-static __must_check int
-i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj,
-                                           struct intel_rps_client *rps,
-                                           bool readonly)
-{
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct drm_i915_gem_request *requests[I915_NUM_ENGINES];
-       int ret, i, n = 0;
-
-       BUG_ON(!mutex_is_locked(&dev->struct_mutex));
-       BUG_ON(!dev_priv->mm.interruptible);
-
-       if (!obj->active)
-               return 0;
-
-       if (readonly) {
-               struct drm_i915_gem_request *req;
-
-               req = obj->last_write_req;
-               if (req == NULL)
-                       return 0;
-
-               requests[n++] = i915_gem_request_reference(req);
-       } else {
-               for (i = 0; i < I915_NUM_ENGINES; i++) {
-                       struct drm_i915_gem_request *req;
-
-                       req = obj->last_read_req[i];
-                       if (req == NULL)
-                               continue;
-
-                       requests[n++] = i915_gem_request_reference(req);
-               }
-       }
-
-       mutex_unlock(&dev->struct_mutex);
-       ret = 0;
-       for (i = 0; ret == 0 && i < n; i++)
-               ret = __i915_wait_request(requests[i], true, NULL, rps);
-       mutex_lock(&dev->struct_mutex);
-
-       for (i = 0; i < n; i++) {
-               if (ret == 0)
-                       i915_gem_object_retire_request(obj, requests[i]);
-               i915_gem_request_unreference(requests[i]);
-       }
-
-       return ret;
-}
-
-static struct intel_rps_client *to_rps_client(struct drm_file *file)
-{
-       struct drm_i915_file_private *fpriv = file->driver_priv;
-       return &fpriv->rps;
-}
-
-static enum fb_op_origin
-write_origin(struct drm_i915_gem_object *obj, unsigned domain)
-{
-       return domain == I915_GEM_DOMAIN_GTT && !obj->has_wc_mmap ?
-              ORIGIN_GTT : ORIGIN_CPU;
+       return (domain == I915_GEM_DOMAIN_GTT ?
+               obj->frontbuffer_ggtt_origin : ORIGIN_CPU);
 }
 
 /**
@@ -1831,10 +1520,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        int ret;
 
        /* Only handle setting domains to types used by the CPU. */
-       if (write_domain & I915_GEM_GPU_DOMAINS)
-               return -EINVAL;
-
-       if (read_domains & I915_GEM_GPU_DOMAINS)
+       if ((write_domain | read_domains) & I915_GEM_GPU_DOMAINS)
                return -EINVAL;
 
        /* Having something in the write domain implies it's in the read
@@ -1843,25 +1529,21 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        if (write_domain != 0 && read_domains != write_domain)
                return -EINVAL;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
-
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
+               return -ENOENT;
 
        /* Try to flush the object off the GPU without holding the lock.
         * We will repeat the flush holding the lock in the normal manner
         * to catch cases where we are gazumped.
         */
-       ret = i915_gem_object_wait_rendering__nonblocking(obj,
-                                                         to_rps_client(file),
-                                                         !write_domain);
+       ret = __unsafe_wait_rendering(obj, to_rps_client(file), !write_domain);
        if (ret)
-               goto unref;
+               goto err;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               goto err;
 
        if (read_domains & I915_GEM_DOMAIN_GTT)
                ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0);
@@ -1871,11 +1553,13 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
        if (write_domain != 0)
                intel_fb_obj_invalidate(obj, write_origin(obj, write_domain));
 
-unref:
-       drm_gem_object_unreference(&obj->base);
-unlock:
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
        return ret;
+
+err:
+       i915_gem_object_put_unlocked(obj);
+       return ret;
 }
 
 /**
@@ -1890,26 +1574,23 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_i915_gem_sw_finish *args = data;
        struct drm_i915_gem_object *obj;
-       int ret = 0;
-
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
+       int err = 0;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
+               return -ENOENT;
 
        /* Pinned buffers may be scanout, so flush the cache */
-       if (obj->pin_display)
-               i915_gem_object_flush_cpu_write_domain(obj);
+       if (READ_ONCE(obj->pin_display)) {
+               err = i915_mutex_lock_interruptible(dev);
+               if (!err) {
+                       i915_gem_object_flush_cpu_write_domain(obj);
+                       mutex_unlock(&dev->struct_mutex);
+               }
+       }
 
-       drm_gem_object_unreference(&obj->base);
-unlock:
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
+       i915_gem_object_put_unlocked(obj);
+       return err;
 }
 
 /**
@@ -1937,7 +1618,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                    struct drm_file *file)
 {
        struct drm_i915_gem_mmap *args = data;
-       struct drm_gem_object *obj;
+       struct drm_i915_gem_object *obj;
        unsigned long addr;
 
        if (args->flags & ~(I915_MMAP_WC))
@@ -1946,19 +1627,19 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
        if (args->flags & I915_MMAP_WC && !boot_cpu_has(X86_FEATURE_PAT))
                return -ENODEV;
 
-       obj = drm_gem_object_lookup(file, args->handle);
-       if (obj == NULL)
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
                return -ENOENT;
 
        /* prime objects have no backing filp to GEM mmap
         * pages from.
         */
-       if (!obj->filp) {
-               drm_gem_object_unreference_unlocked(obj);
+       if (!obj->base.filp) {
+               i915_gem_object_put_unlocked(obj);
                return -EINVAL;
        }
 
-       addr = vm_mmap(obj->filp, 0, args->size,
+       addr = vm_mmap(obj->base.filp, 0, args->size,
                       PROT_READ | PROT_WRITE, MAP_SHARED,
                       args->offset);
        if (args->flags & I915_MMAP_WC) {
@@ -1966,7 +1647,7 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                struct vm_area_struct *vma;
 
                if (down_write_killable(&mm->mmap_sem)) {
-                       drm_gem_object_unreference_unlocked(obj);
+                       i915_gem_object_put_unlocked(obj);
                        return -EINTR;
                }
                vma = find_vma(mm, addr);
@@ -1978,9 +1659,9 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
                up_write(&mm->mmap_sem);
 
                /* This may race, but that's ok, it only gets set */
-               WRITE_ONCE(to_intel_bo(obj)->has_wc_mmap, true);
+               WRITE_ONCE(obj->frontbuffer_ggtt_origin, ORIGIN_CPU);
        }
-       drm_gem_object_unreference_unlocked(obj);
+       i915_gem_object_put_unlocked(obj);
        if (IS_ERR((void *)addr))
                return addr;
 
@@ -1989,9 +1670,69 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
        return 0;
 }
 
+static unsigned int tile_row_pages(struct drm_i915_gem_object *obj)
+{
+       u64 size;
+
+       size = i915_gem_object_get_stride(obj);
+       size *= i915_gem_object_get_tiling(obj) == I915_TILING_Y ? 32 : 8;
+
+       return size >> PAGE_SHIFT;
+}
+
+/**
+ * i915_gem_mmap_gtt_version - report the current feature set for GTT mmaps
+ *
+ * A history of the GTT mmap interface:
+ *
+ * 0 - Everything had to fit into the GTT. Both parties of a memcpy had to
+ *     aligned and suitable for fencing, and still fit into the available
+ *     mappable space left by the pinned display objects. A classic problem
+ *     we called the page-fault-of-doom where we would ping-pong between
+ *     two objects that could not fit inside the GTT and so the memcpy
+ *     would page one object in at the expense of the other between every
+ *     single byte.
+ *
+ * 1 - Objects can be any size, and have any compatible fencing (X Y, or none
+ *     as set via i915_gem_set_tiling() [DRM_I915_GEM_SET_TILING]). If the
+ *     object is too large for the available space (or simply too large
+ *     for the mappable aperture!), a view is created instead and faulted
+ *     into userspace. (This view is aligned and sized appropriately for
+ *     fenced access.)
+ *
+ * Restrictions:
+ *
+ *  * snoopable objects cannot be accessed via the GTT. It can cause machine
+ *    hangs on some architectures, corruption on others. An attempt to service
+ *    a GTT page fault from a snoopable object will generate a SIGBUS.
+ *
+ *  * the object must be able to fit into RAM (physical memory, though no
+ *    limited to the mappable aperture).
+ *
+ *
+ * Caveats:
+ *
+ *  * a new GTT page fault will synchronize rendering from the GPU and flush
+ *    all data to system memory. Subsequent access will not be synchronized.
+ *
+ *  * all mappings are revoked on runtime device suspend.
+ *
+ *  * there are only 8, 16 or 32 fence registers to share between all users
+ *    (older machines require fence register for display and blitter access
+ *    as well). Contention of the fence registers will cause the previous users
+ *    to be unmapped and any new access will generate new page faults.
+ *
+ *  * running out of memory while servicing a fault may generate a SIGBUS,
+ *    rather than the expected SIGSEGV.
+ */
+int i915_gem_mmap_gtt_version(void)
+{
+       return 1;
+}
+
 /**
  * i915_gem_fault - fault a page into the GTT
- * @vma: VMA in question
+ * @area: CPU VMA in question
  * @vmf: fault info
  *
  * The fault handler is set up by drm_gem_mmap() when a object is GTT mapped
@@ -2004,122 +1745,120 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
  * from the GTT and/or fence registers to make room.  So performance may
  * suffer if the GTT working set is large or there are few fence registers
  * left.
+ *
+ * The current feature set supported by i915_gem_fault() and thus GTT mmaps
+ * is exposed via I915_PARAM_MMAP_GTT_VERSION (see i915_gem_mmap_gtt_version).
  */
-int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+int i915_gem_fault(struct vm_area_struct *area, struct vm_fault *vmf)
 {
-       struct drm_i915_gem_object *obj = to_intel_bo(vma->vm_private_data);
+#define MIN_CHUNK_PAGES ((1 << 20) >> PAGE_SHIFT) /* 1 MiB */
+       struct drm_i915_gem_object *obj = to_intel_bo(area->vm_private_data);
        struct drm_device *dev = obj->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct i915_ggtt_view view = i915_ggtt_view_normal;
-       pgoff_t page_offset;
-       unsigned long pfn;
-       int ret = 0;
        bool write = !!(vmf->flags & FAULT_FLAG_WRITE);
-
-       intel_runtime_pm_get(dev_priv);
+       struct i915_vma *vma;
+       pgoff_t page_offset;
+       unsigned int flags;
+       int ret;
 
        /* We don't use vmf->pgoff since that has the fake offset */
-       page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+       page_offset = ((unsigned long)vmf->virtual_address - area->vm_start) >>
                PAGE_SHIFT;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               goto out;
-
        trace_i915_gem_object_fault(obj, page_offset, true, write);
 
        /* Try to flush the object off the GPU first without holding the lock.
-        * Upon reacquiring the lock, we will perform our sanity checks and then
+        * Upon acquiring the lock, we will perform our sanity checks and then
         * repeat the flush holding the lock in the normal manner to catch cases
         * where we are gazumped.
         */
-       ret = i915_gem_object_wait_rendering__nonblocking(obj, NULL, !write);
+       ret = __unsafe_wait_rendering(obj, NULL, !write);
        if (ret)
-               goto unlock;
+               goto err;
+
+       intel_runtime_pm_get(dev_priv);
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               goto err_rpm;
 
        /* Access to snoopable pages through the GTT is incoherent. */
        if (obj->cache_level != I915_CACHE_NONE && !HAS_LLC(dev)) {
                ret = -EFAULT;
-               goto unlock;
+               goto err_unlock;
        }
 
-       /* Use a partial view if the object is bigger than the aperture. */
-       if (obj->base.size >= ggtt->mappable_end &&
-           obj->tiling_mode == I915_TILING_NONE) {
-               static const unsigned int chunk_size = 256; // 1 MiB
+       /* If the object is smaller than a couple of partial vma, it is
+        * not worth only creating a single partial vma - we may as well
+        * clear enough space for the full object.
+        */
+       flags = PIN_MAPPABLE;
+       if (obj->base.size > 2 * MIN_CHUNK_PAGES << PAGE_SHIFT)
+               flags |= PIN_NONBLOCK | PIN_NONFAULT;
+
+       /* Now pin it into the GTT as needed */
+       vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, flags);
+       if (IS_ERR(vma)) {
+               struct i915_ggtt_view view;
+               unsigned int chunk_size;
+
+               /* Use a partial view if it is bigger than available space */
+               chunk_size = MIN_CHUNK_PAGES;
+               if (i915_gem_object_is_tiled(obj))
+                       chunk_size = max(chunk_size, tile_row_pages(obj));
 
                memset(&view, 0, sizeof(view));
                view.type = I915_GGTT_VIEW_PARTIAL;
                view.params.partial.offset = rounddown(page_offset, chunk_size);
                view.params.partial.size =
-                       min_t(unsigned int,
-                             chunk_size,
-                             (vma->vm_end - vma->vm_start)/PAGE_SIZE -
+                       min_t(unsigned int, chunk_size,
+                             (area->vm_end - area->vm_start) / PAGE_SIZE -
                              view.params.partial.offset);
-       }
 
-       /* Now pin it into the GTT if needed */
-       ret = i915_gem_object_ggtt_pin(obj, &view, 0, PIN_MAPPABLE);
-       if (ret)
-               goto unlock;
+               /* If the partial covers the entire object, just create a
+                * normal VMA.
+                */
+               if (chunk_size >= obj->base.size >> PAGE_SHIFT)
+                       view.type = I915_GGTT_VIEW_NORMAL;
+
+               /* Userspace is now writing through an untracked VMA, abandon
+                * all hope that the hardware is able to track future writes.
+                */
+               obj->frontbuffer_ggtt_origin = ORIGIN_CPU;
+
+               vma = i915_gem_object_ggtt_pin(obj, &view, 0, 0, PIN_MAPPABLE);
+       }
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto err_unlock;
+       }
 
        ret = i915_gem_object_set_to_gtt_domain(obj, write);
        if (ret)
-               goto unpin;
+               goto err_unpin;
 
-       ret = i915_gem_object_get_fence(obj);
+       ret = i915_vma_get_fence(vma);
        if (ret)
-               goto unpin;
+               goto err_unpin;
 
        /* Finally, remap it using the new GTT offset */
-       pfn = ggtt->mappable_base +
-               i915_gem_obj_ggtt_offset_view(obj, &view);
-       pfn >>= PAGE_SHIFT;
-
-       if (unlikely(view.type == I915_GGTT_VIEW_PARTIAL)) {
-               /* Overriding existing pages in partial view does not cause
-                * us any trouble as TLBs are still valid because the fault
-                * is due to userspace losing part of the mapping or never
-                * having accessed it before (at this partials' range).
-                */
-               unsigned long base = vma->vm_start +
-                                    (view.params.partial.offset << PAGE_SHIFT);
-               unsigned int i;
-
-               for (i = 0; i < view.params.partial.size; i++) {
-                       ret = vm_insert_pfn(vma, base + i * PAGE_SIZE, pfn + i);
-                       if (ret)
-                               break;
-               }
-
-               obj->fault_mappable = true;
-       } else {
-               if (!obj->fault_mappable) {
-                       unsigned long size = min_t(unsigned long,
-                                                  vma->vm_end - vma->vm_start,
-                                                  obj->base.size);
-                       int i;
-
-                       for (i = 0; i < size >> PAGE_SHIFT; i++) {
-                               ret = vm_insert_pfn(vma,
-                                                   (unsigned long)vma->vm_start + i * PAGE_SIZE,
-                                                   pfn + i);
-                               if (ret)
-                                       break;
-                       }
+       ret = remap_io_mapping(area,
+                              area->vm_start + (vma->ggtt_view.params.partial.offset << PAGE_SHIFT),
+                              (ggtt->mappable_base + vma->node.start) >> PAGE_SHIFT,
+                              min_t(u64, vma->size, area->vm_end - area->vm_start),
+                              &ggtt->mappable);
+       if (ret)
+               goto err_unpin;
 
-                       obj->fault_mappable = true;
-               } else
-                       ret = vm_insert_pfn(vma,
-                                           (unsigned long)vmf->virtual_address,
-                                           pfn + page_offset);
-       }
-unpin:
-       i915_gem_object_ggtt_unpin_view(obj, &view);
-unlock:
+       obj->fault_mappable = true;
+err_unpin:
+       __i915_vma_unpin(vma);
+err_unlock:
        mutex_unlock(&dev->struct_mutex);
-out:
+err_rpm:
+       intel_runtime_pm_put(dev_priv);
+err:
        switch (ret) {
        case -EIO:
                /*
@@ -2160,8 +1899,6 @@ out:
                ret = VM_FAULT_SIGBUS;
                break;
        }
-
-       intel_runtime_pm_put(dev_priv);
        return ret;
 }
 
@@ -2215,46 +1952,58 @@ i915_gem_release_all_mmaps(struct drm_i915_private *dev_priv)
                i915_gem_release_mmap(obj);
 }
 
-uint32_t
-i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
+/**
+ * i915_gem_get_ggtt_size - return required global GTT size for an object
+ * @dev_priv: i915 device
+ * @size: object size
+ * @tiling_mode: tiling mode
+ *
+ * Return the required global GTT size for an object, taking into account
+ * potential fence register mapping.
+ */
+u64 i915_gem_get_ggtt_size(struct drm_i915_private *dev_priv,
+                          u64 size, int tiling_mode)
 {
-       uint32_t gtt_size;
+       u64 ggtt_size;
+
+       GEM_BUG_ON(size == 0);
 
-       if (INTEL_INFO(dev)->gen >= 4 ||
+       if (INTEL_GEN(dev_priv) >= 4 ||
            tiling_mode == I915_TILING_NONE)
                return size;
 
        /* Previous chips need a power-of-two fence region when tiling */
-       if (IS_GEN3(dev))
-               gtt_size = 1024*1024;
+       if (IS_GEN3(dev_priv))
+               ggtt_size = 1024*1024;
        else
-               gtt_size = 512*1024;
+               ggtt_size = 512*1024;
 
-       while (gtt_size < size)
-               gtt_size <<= 1;
+       while (ggtt_size < size)
+               ggtt_size <<= 1;
 
-       return gtt_size;
+       return ggtt_size;
 }
 
 /**
- * i915_gem_get_gtt_alignment - return required GTT alignment for an object
- * @dev: drm device
+ * i915_gem_get_ggtt_alignment - return required global GTT alignment
+ * @dev_priv: i915 device
  * @size: object size
  * @tiling_mode: tiling mode
- * @fenced: is fenced alignemned required or not
+ * @fenced: is fenced alignment required or not
  *
- * Return the required GTT alignment for an object, taking into account
+ * Return the required global GTT alignment for an object, taking into account
  * potential fence register mapping.
  */
-uint32_t
-i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size,
-                          int tiling_mode, bool fenced)
+u64 i915_gem_get_ggtt_alignment(struct drm_i915_private *dev_priv, u64 size,
+                               int tiling_mode, bool fenced)
 {
+       GEM_BUG_ON(size == 0);
+
        /*
         * Minimum alignment is 4k (GTT page size), but might be greater
         * if a fence register is needed for the object.
         */
-       if (INTEL_INFO(dev)->gen >= 4 || (!fenced && IS_G33(dev)) ||
+       if (INTEL_GEN(dev_priv) >= 4 || (!fenced && IS_G33(dev_priv)) ||
            tiling_mode == I915_TILING_NONE)
                return 4096;
 
@@ -2262,42 +2011,34 @@ i915_gem_get_gtt_alignment(struct drm_device *dev, uint32_t size,
         * Previous chips need to be aligned to the size of the smallest
         * fence register that can contain the object.
         */
-       return i915_gem_get_gtt_size(dev, size, tiling_mode);
+       return i915_gem_get_ggtt_size(dev_priv, size, tiling_mode);
 }
 
 static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj)
 {
        struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       int ret;
-
-       dev_priv->mm.shrinker_no_lock_stealing = true;
+       int err;
 
-       ret = drm_gem_create_mmap_offset(&obj->base);
-       if (ret != -ENOSPC)
-               goto out;
+       err = drm_gem_create_mmap_offset(&obj->base);
+       if (!err)
+               return 0;
 
-       /* Badly fragmented mmap space? The only way we can recover
-        * space is by destroying unwanted objects. We can't randomly release
-        * mmap_offsets as userspace expects them to be persistent for the
-        * lifetime of the objects. The closest we can is to release the
-        * offsets on purgeable objects by truncating it and marking it purged,
-        * which prevents userspace from ever using that object again.
+       /* We can idle the GPU locklessly to flush stale objects, but in order
+        * to claim that space for ourselves, we need to take the big
+        * struct_mutex to free the requests+objects and allocate our slot.
         */
-       i915_gem_shrink(dev_priv,
-                       obj->base.size >> PAGE_SHIFT,
-                       I915_SHRINK_BOUND |
-                       I915_SHRINK_UNBOUND |
-                       I915_SHRINK_PURGEABLE);
-       ret = drm_gem_create_mmap_offset(&obj->base);
-       if (ret != -ENOSPC)
-               goto out;
+       err = i915_gem_wait_for_idle(dev_priv, I915_WAIT_INTERRUPTIBLE);
+       if (err)
+               return err;
 
-       i915_gem_shrink_all(dev_priv);
-       ret = drm_gem_create_mmap_offset(&obj->base);
-out:
-       dev_priv->mm.shrinker_no_lock_stealing = false;
+       err = i915_mutex_lock_interruptible(&dev_priv->drm);
+       if (!err) {
+               i915_gem_retire_requests(dev_priv);
+               err = drm_gem_create_mmap_offset(&obj->base);
+               mutex_unlock(&dev_priv->drm.struct_mutex);
+       }
 
-       return ret;
+       return err;
 }
 
 static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj)
@@ -2314,32 +2055,15 @@ i915_gem_mmap_gtt(struct drm_file *file,
        struct drm_i915_gem_object *obj;
        int ret;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
-
-       obj = to_intel_bo(drm_gem_object_lookup(file, handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
-
-       if (obj->madv != I915_MADV_WILLNEED) {
-               DRM_DEBUG("Attempting to mmap a purgeable buffer\n");
-               ret = -EFAULT;
-               goto out;
-       }
+       obj = i915_gem_object_lookup(file, handle);
+       if (!obj)
+               return -ENOENT;
 
        ret = i915_gem_object_create_mmap_offset(obj);
-       if (ret)
-               goto out;
+       if (ret == 0)
+               *offset = drm_vma_node_offset_addr(&obj->base.vma_node);
 
-       *offset = drm_vma_node_offset_addr(&obj->base.vma_node);
-
-out:
-       drm_gem_object_unreference(&obj->base);
-unlock:
-       mutex_unlock(&dev->struct_mutex);
+       i915_gem_object_put_unlocked(obj);
        return ret;
 }
 
@@ -2457,7 +2181,7 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
        if (obj->pages_pin_count)
                return -EBUSY;
 
-       BUG_ON(i915_gem_obj_bound_any(obj));
+       GEM_BUG_ON(obj->bind_count);
 
        /* ->put_pages might need to allocate memory for the bit17 swizzle
         * array, hence protect them from being reaped by removing them from gtt
@@ -2465,10 +2189,14 @@ i915_gem_object_put_pages(struct drm_i915_gem_object *obj)
        list_del(&obj->global_list);
 
        if (obj->mapping) {
-               if (is_vmalloc_addr(obj->mapping))
-                       vunmap(obj->mapping);
+               void *ptr;
+
+               ptr = ptr_mask_bits(obj->mapping);
+               if (is_vmalloc_addr(ptr))
+                       vunmap(ptr);
                else
-                       kunmap(kmap_to_page(obj->mapping));
+                       kunmap(kmap_to_page(ptr));
+
                obj->mapping = NULL;
        }
 
@@ -2577,7 +2305,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj)
        if (i915_gem_object_needs_bit17_swizzle(obj))
                i915_gem_object_do_bit_17_swizzle(obj);
 
-       if (obj->tiling_mode != I915_TILING_NONE &&
+       if (i915_gem_object_is_tiled(obj) &&
            dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES)
                i915_gem_object_pin_pages(obj);
 
@@ -2641,7 +2369,8 @@ i915_gem_object_get_pages(struct drm_i915_gem_object *obj)
 }
 
 /* The 'mapping' part of i915_gem_object_pin_map() below */
-static void *i915_gem_object_map(const struct drm_i915_gem_object *obj)
+static void *i915_gem_object_map(const struct drm_i915_gem_object *obj,
+                                enum i915_map_type type)
 {
        unsigned long n_pages = obj->base.size >> PAGE_SHIFT;
        struct sg_table *sgt = obj->pages;
@@ -2650,10 +2379,11 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj)
        struct page *stack_pages[32];
        struct page **pages = stack_pages;
        unsigned long i = 0;
+       pgprot_t pgprot;
        void *addr;
 
        /* A single page can always be kmapped */
-       if (n_pages == 1)
+       if (n_pages == 1 && type == I915_MAP_WB)
                return kmap(sg_page(sgt->sgl));
 
        if (n_pages > ARRAY_SIZE(stack_pages)) {
@@ -2669,7 +2399,15 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj)
        /* Check that we have the expected number of pages */
        GEM_BUG_ON(i != n_pages);
 
-       addr = vmap(pages, n_pages, 0, PAGE_KERNEL);
+       switch (type) {
+       case I915_MAP_WB:
+               pgprot = PAGE_KERNEL;
+               break;
+       case I915_MAP_WC:
+               pgprot = pgprot_writecombine(PAGE_KERNEL_IO);
+               break;
+       }
+       addr = vmap(pages, n_pages, 0, pgprot);
 
        if (pages != stack_pages)
                drm_free_large(pages);
@@ -2678,276 +2416,89 @@ static void *i915_gem_object_map(const struct drm_i915_gem_object *obj)
 }
 
 /* get, pin, and map the pages of the object into kernel space */
-void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj)
+void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj,
+                             enum i915_map_type type)
 {
+       enum i915_map_type has_type;
+       bool pinned;
+       void *ptr;
        int ret;
 
        lockdep_assert_held(&obj->base.dev->struct_mutex);
+       GEM_BUG_ON(!i915_gem_object_has_struct_page(obj));
 
        ret = i915_gem_object_get_pages(obj);
        if (ret)
                return ERR_PTR(ret);
 
        i915_gem_object_pin_pages(obj);
+       pinned = obj->pages_pin_count > 1;
 
-       if (!obj->mapping) {
-               obj->mapping = i915_gem_object_map(obj);
-               if (!obj->mapping) {
-                       i915_gem_object_unpin_pages(obj);
-                       return ERR_PTR(-ENOMEM);
+       ptr = ptr_unpack_bits(obj->mapping, has_type);
+       if (ptr && has_type != type) {
+               if (pinned) {
+                       ret = -EBUSY;
+                       goto err;
                }
-       }
-
-       return obj->mapping;
-}
 
-void i915_vma_move_to_active(struct i915_vma *vma,
-                            struct drm_i915_gem_request *req)
-{
-       struct drm_i915_gem_object *obj = vma->obj;
-       struct intel_engine_cs *engine;
-
-       engine = i915_gem_request_get_engine(req);
-
-       /* Add a reference if we're newly entering the active list. */
-       if (obj->active == 0)
-               drm_gem_object_reference(&obj->base);
-       obj->active |= intel_engine_flag(engine);
-
-       list_move_tail(&obj->engine_list[engine->id], &engine->active_list);
-       i915_gem_request_assign(&obj->last_read_req[engine->id], req);
-
-       list_move_tail(&vma->vm_link, &vma->vm->active_list);
-}
-
-static void
-i915_gem_object_retire__write(struct drm_i915_gem_object *obj)
-{
-       GEM_BUG_ON(obj->last_write_req == NULL);
-       GEM_BUG_ON(!(obj->active & intel_engine_flag(obj->last_write_req->engine)));
-
-       i915_gem_request_assign(&obj->last_write_req, NULL);
-       intel_fb_obj_flush(obj, true, ORIGIN_CS);
-}
-
-static void
-i915_gem_object_retire__read(struct drm_i915_gem_object *obj, int ring)
-{
-       struct i915_vma *vma;
-
-       GEM_BUG_ON(obj->last_read_req[ring] == NULL);
-       GEM_BUG_ON(!(obj->active & (1 << ring)));
-
-       list_del_init(&obj->engine_list[ring]);
-       i915_gem_request_assign(&obj->last_read_req[ring], NULL);
-
-       if (obj->last_write_req && obj->last_write_req->engine->id == ring)
-               i915_gem_object_retire__write(obj);
-
-       obj->active &= ~(1 << ring);
-       if (obj->active)
-               return;
-
-       /* Bump our place on the bound list to keep it roughly in LRU order
-        * so that we don't steal from recently used but inactive objects
-        * (unless we are forced to ofc!)
-        */
-       list_move_tail(&obj->global_list,
-                      &to_i915(obj->base.dev)->mm.bound_list);
-
-       list_for_each_entry(vma, &obj->vma_list, obj_link) {
-               if (!list_empty(&vma->vm_link))
-                       list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
-       }
-
-       i915_gem_request_assign(&obj->last_fenced_req, NULL);
-       drm_gem_object_unreference(&obj->base);
-}
-
-static int
-i915_gem_init_seqno(struct drm_i915_private *dev_priv, u32 seqno)
-{
-       struct intel_engine_cs *engine;
-       int ret;
-
-       /* Carefully retire all requests without writing to the rings */
-       for_each_engine(engine, dev_priv) {
-               ret = intel_engine_idle(engine);
-               if (ret)
-                       return ret;
-       }
-       i915_gem_retire_requests(dev_priv);
+               if (is_vmalloc_addr(ptr))
+                       vunmap(ptr);
+               else
+                       kunmap(kmap_to_page(ptr));
 
-       /* If the seqno wraps around, we need to clear the breadcrumb rbtree */
-       if (!i915_seqno_passed(seqno, dev_priv->next_seqno)) {
-               while (intel_kick_waiters(dev_priv) ||
-                      intel_kick_signalers(dev_priv))
-                       yield();
+               ptr = obj->mapping = NULL;
        }
 
-       /* Finally reset hw state */
-       for_each_engine(engine, dev_priv)
-               intel_ring_init_seqno(engine, seqno);
-
-       return 0;
-}
-
-int i915_gem_set_seqno(struct drm_device *dev, u32 seqno)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
-
-       if (seqno == 0)
-               return -EINVAL;
-
-       /* HWS page needs to be set less than what we
-        * will inject to ring
-        */
-       ret = i915_gem_init_seqno(dev_priv, seqno - 1);
-       if (ret)
-               return ret;
-
-       /* Carefully set the last_seqno value so that wrap
-        * detection still works
-        */
-       dev_priv->next_seqno = seqno;
-       dev_priv->last_seqno = seqno - 1;
-       if (dev_priv->last_seqno == 0)
-               dev_priv->last_seqno--;
-
-       return 0;
-}
-
-int
-i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno)
-{
-       /* reserve 0 for non-seqno */
-       if (dev_priv->next_seqno == 0) {
-               int ret = i915_gem_init_seqno(dev_priv, 0);
-               if (ret)
-                       return ret;
+       if (!ptr) {
+               ptr = i915_gem_object_map(obj, type);
+               if (!ptr) {
+                       ret = -ENOMEM;
+                       goto err;
+               }
 
-               dev_priv->next_seqno = 1;
+               obj->mapping = ptr_pack_bits(ptr, type);
        }
 
-       *seqno = dev_priv->last_seqno = dev_priv->next_seqno++;
-       return 0;
-}
-
-static void i915_gem_mark_busy(const struct intel_engine_cs *engine)
-{
-       struct drm_i915_private *dev_priv = engine->i915;
-
-       dev_priv->gt.active_engines |= intel_engine_flag(engine);
-       if (dev_priv->gt.awake)
-               return;
-
-       intel_runtime_pm_get_noresume(dev_priv);
-       dev_priv->gt.awake = true;
-
-       i915_update_gfx_val(dev_priv);
-       if (INTEL_GEN(dev_priv) >= 6)
-               gen6_rps_busy(dev_priv);
+       return ptr;
 
-       queue_delayed_work(dev_priv->wq,
-                          &dev_priv->gt.retire_work,
-                          round_jiffies_up_relative(HZ));
+err:
+       i915_gem_object_unpin_pages(obj);
+       return ERR_PTR(ret);
 }
 
-/*
- * NB: This function is not allowed to fail. Doing so would mean the the
- * request is not being tracked for completion but the work itself is
- * going to happen on the hardware. This would be a Bad Thing(tm).
- */
-void __i915_add_request(struct drm_i915_gem_request *request,
-                       struct drm_i915_gem_object *obj,
-                       bool flush_caches)
+static void
+i915_gem_object_retire__write(struct i915_gem_active *active,
+                             struct drm_i915_gem_request *request)
 {
-       struct intel_engine_cs *engine;
-       struct intel_ringbuffer *ringbuf;
-       u32 request_start;
-       u32 reserved_tail;
-       int ret;
-
-       if (WARN_ON(request == NULL))
-               return;
-
-       engine = request->engine;
-       ringbuf = request->ringbuf;
-
-       /*
-        * To ensure that this call will not fail, space for its emissions
-        * should already have been reserved in the ring buffer. Let the ring
-        * know that it is time to use that space up.
-        */
-       request_start = intel_ring_get_tail(ringbuf);
-       reserved_tail = request->reserved_space;
-       request->reserved_space = 0;
-
-       /*
-        * Emit any outstanding flushes - execbuf can fail to emit the flush
-        * after having emitted the batchbuffer command. Hence we need to fix
-        * things up similar to emitting the lazy request. The difference here
-        * is that the flush _must_ happen before the next request, no matter
-        * what.
-        */
-       if (flush_caches) {
-               if (i915.enable_execlists)
-                       ret = logical_ring_flush_all_caches(request);
-               else
-                       ret = intel_ring_flush_all_caches(request);
-               /* Not allowed to fail! */
-               WARN(ret, "*_ring_flush_all_caches failed: %d!\n", ret);
-       }
-
-       trace_i915_gem_request_add(request);
+       struct drm_i915_gem_object *obj =
+               container_of(active, struct drm_i915_gem_object, last_write);
 
-       request->head = request_start;
+       intel_fb_obj_flush(obj, true, ORIGIN_CS);
+}
 
-       /* Whilst this request exists, batch_obj will be on the
-        * active_list, and so will hold the active reference. Only when this
-        * request is retired will the the batch_obj be moved onto the
-        * inactive_list and lose its active reference. Hence we do not need
-        * to explicitly hold another reference here.
-        */
-       request->batch_obj = obj;
+static void
+i915_gem_object_retire__read(struct i915_gem_active *active,
+                            struct drm_i915_gem_request *request)
+{
+       int idx = request->engine->id;
+       struct drm_i915_gem_object *obj =
+               container_of(active, struct drm_i915_gem_object, last_read[idx]);
 
-       /* Seal the request and mark it as pending execution. Note that
-        * we may inspect this state, without holding any locks, during
-        * hangcheck. Hence we apply the barrier to ensure that we do not
-        * see a more recent value in the hws than we are tracking.
-        */
-       request->emitted_jiffies = jiffies;
-       request->previous_seqno = engine->last_submitted_seqno;
-       smp_store_mb(engine->last_submitted_seqno, request->seqno);
-       list_add_tail(&request->list, &engine->request_list);
-
-       /* Record the position of the start of the request so that
-        * should we detect the updated seqno part-way through the
-        * GPU processing the request, we never over-estimate the
-        * position of the head.
-        */
-       request->postfix = intel_ring_get_tail(ringbuf);
+       GEM_BUG_ON(!i915_gem_object_has_active_engine(obj, idx));
 
-       if (i915.enable_execlists)
-               ret = engine->emit_request(request);
-       else {
-               ret = engine->add_request(request);
+       i915_gem_object_clear_active(obj, idx);
+       if (i915_gem_object_is_active(obj))
+               return;
 
-               request->tail = intel_ring_get_tail(ringbuf);
-       }
-       /* Not allowed to fail! */
-       WARN(ret, "emit|add_request failed: %d!\n", ret);
-       /* Sanity check that the reserved size was large enough. */
-       ret = intel_ring_get_tail(ringbuf) - request_start;
-       if (ret < 0)
-               ret += ringbuf->size;
-       WARN_ONCE(ret > reserved_tail,
-                 "Not enough space reserved (%d bytes) "
-                 "for adding the request (%d bytes)\n",
-                 reserved_tail, ret);
+       /* Bump our place on the bound list to keep it roughly in LRU order
+        * so that we don't steal from recently used but inactive objects
+        * (unless we are forced to ofc!)
+        */
+       if (obj->bind_count)
+               list_move_tail(&obj->global_list,
+                              &request->i915->mm.bound_list);
 
-       i915_gem_mark_busy(engine);
+       i915_gem_object_put(obj);
 }
 
 static bool i915_context_is_banned(const struct i915_gem_context *ctx)
@@ -2981,101 +2532,6 @@ static void i915_set_reset_status(struct i915_gem_context *ctx,
        }
 }
 
-void i915_gem_request_free(struct kref *req_ref)
-{
-       struct drm_i915_gem_request *req = container_of(req_ref,
-                                                typeof(*req), ref);
-       kmem_cache_free(req->i915->requests, req);
-}
-
-static inline int
-__i915_gem_request_alloc(struct intel_engine_cs *engine,
-                        struct i915_gem_context *ctx,
-                        struct drm_i915_gem_request **req_out)
-{
-       struct drm_i915_private *dev_priv = engine->i915;
-       unsigned reset_counter = i915_reset_counter(&dev_priv->gpu_error);
-       struct drm_i915_gem_request *req;
-       int ret;
-
-       if (!req_out)
-               return -EINVAL;
-
-       *req_out = NULL;
-
-       /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report
-        * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex
-        * and restart.
-        */
-       ret = i915_gem_check_wedge(reset_counter, dev_priv->mm.interruptible);
-       if (ret)
-               return ret;
-
-       req = kmem_cache_zalloc(dev_priv->requests, GFP_KERNEL);
-       if (req == NULL)
-               return -ENOMEM;
-
-       ret = i915_gem_get_seqno(engine->i915, &req->seqno);
-       if (ret)
-               goto err;
-
-       kref_init(&req->ref);
-       req->i915 = dev_priv;
-       req->engine = engine;
-       req->ctx  = ctx;
-       i915_gem_context_reference(req->ctx);
-
-       /*
-        * Reserve space in the ring buffer for all the commands required to
-        * eventually emit this request. This is to guarantee that the
-        * i915_add_request() call can't fail. Note that the reserve may need
-        * to be redone if the request is not actually submitted straight
-        * away, e.g. because a GPU scheduler has deferred it.
-        */
-       req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
-
-       if (i915.enable_execlists)
-               ret = intel_logical_ring_alloc_request_extras(req);
-       else
-               ret = intel_ring_alloc_request_extras(req);
-       if (ret)
-               goto err_ctx;
-
-       *req_out = req;
-       return 0;
-
-err_ctx:
-       i915_gem_context_unreference(ctx);
-err:
-       kmem_cache_free(dev_priv->requests, req);
-       return ret;
-}
-
-/**
- * i915_gem_request_alloc - allocate a request structure
- *
- * @engine: engine that we wish to issue the request on.
- * @ctx: context that the request will be associated with.
- *       This can be NULL if the request is not directly related to
- *       any specific user context, in which case this function will
- *       choose an appropriate context to use.
- *
- * Returns a pointer to the allocated request if successful,
- * or an error code if not.
- */
-struct drm_i915_gem_request *
-i915_gem_request_alloc(struct intel_engine_cs *engine,
-                      struct i915_gem_context *ctx)
-{
-       struct drm_i915_gem_request *req;
-       int err;
-
-       if (ctx == NULL)
-               ctx = engine->i915->kernel_context;
-       err = __i915_gem_request_alloc(engine, ctx, &req);
-       return err ? ERR_PTR(err) : req;
-}
-
 struct drm_i915_gem_request *
 i915_gem_find_active_request(struct intel_engine_cs *engine)
 {
@@ -3089,185 +2545,143 @@ i915_gem_find_active_request(struct intel_engine_cs *engine)
         * extra delay for a recent interrupt is pointless. Hence, we do
         * not need an engine->irq_seqno_barrier() before the seqno reads.
         */
-       list_for_each_entry(request, &engine->request_list, list) {
+       list_for_each_entry(request, &engine->request_list, link) {
                if (i915_gem_request_completed(request))
                        continue;
 
+               if (!i915_sw_fence_done(&request->submit))
+                       break;
+
                return request;
        }
 
        return NULL;
 }
 
-static void i915_gem_reset_engine_status(struct intel_engine_cs *engine)
+static void reset_request(struct drm_i915_gem_request *request)
+{
+       void *vaddr = request->ring->vaddr;
+       u32 head;
+
+       /* As this request likely depends on state from the lost
+        * context, clear out all the user operations leaving the
+        * breadcrumb at the end (so we get the fence notifications).
+        */
+       head = request->head;
+       if (request->postfix < head) {
+               memset(vaddr + head, 0, request->ring->size - head);
+               head = 0;
+       }
+       memset(vaddr + head, 0, request->postfix - head);
+}
+
+static void i915_gem_reset_engine(struct intel_engine_cs *engine)
 {
        struct drm_i915_gem_request *request;
+       struct i915_gem_context *incomplete_ctx;
        bool ring_hung;
 
+       /* Ensure irq handler finishes, and not run again. */
+       tasklet_kill(&engine->irq_tasklet);
+       if (engine->irq_seqno_barrier)
+               engine->irq_seqno_barrier(engine);
+
        request = i915_gem_find_active_request(engine);
-       if (request == NULL)
+       if (!request)
                return;
 
        ring_hung = engine->hangcheck.score >= HANGCHECK_SCORE_RING_HUNG;
-
        i915_set_reset_status(request->ctx, ring_hung);
-       list_for_each_entry_continue(request, &engine->request_list, list)
-               i915_set_reset_status(request->ctx, false);
-}
-
-static void i915_gem_reset_engine_cleanup(struct intel_engine_cs *engine)
-{
-       struct intel_ringbuffer *buffer;
-
-       while (!list_empty(&engine->active_list)) {
-               struct drm_i915_gem_object *obj;
-
-               obj = list_first_entry(&engine->active_list,
-                                      struct drm_i915_gem_object,
-                                      engine_list[engine->id]);
-
-               i915_gem_object_retire__read(obj, engine->id);
-       }
-
-       /*
-        * Clear the execlists queue up before freeing the requests, as those
-        * are the ones that keep the context and ringbuffer backing objects
-        * pinned in place.
-        */
-
-       if (i915.enable_execlists) {
-               /* Ensure irq handler finishes or is cancelled. */
-               tasklet_kill(&engine->irq_tasklet);
-
-               intel_execlists_cancel_requests(engine);
-       }
-
-       /*
-        * We must free the requests after all the corresponding objects have
-        * been moved off active lists. Which is the same order as the normal
-        * retire_requests function does. This is important if object hold
-        * implicit references on things like e.g. ppgtt address spaces through
-        * the request.
-        */
-       while (!list_empty(&engine->request_list)) {
-               struct drm_i915_gem_request *request;
+       if (!ring_hung)
+               return;
 
-               request = list_first_entry(&engine->request_list,
-                                          struct drm_i915_gem_request,
-                                          list);
+       DRM_DEBUG_DRIVER("resetting %s to restart from tail of request 0x%x\n",
+                        engine->name, request->fence.seqno);
 
-               i915_gem_request_retire(request);
-       }
+       /* Setup the CS to resume from the breadcrumb of the hung request */
+       engine->reset_hw(engine, request);
 
-       /* Having flushed all requests from all queues, we know that all
-        * ringbuffers must now be empty. However, since we do not reclaim
-        * all space when retiring the request (to prevent HEADs colliding
-        * with rapid ringbuffer wraparound) the amount of available space
-        * upon reset is less than when we start. Do one more pass over
-        * all the ringbuffers to reset last_retired_head.
+       /* Users of the default context do not rely on logical state
+        * preserved between batches. They have to emit full state on
+        * every batch and so it is safe to execute queued requests following
+        * the hang.
+        *
+        * Other contexts preserve state, now corrupt. We want to skip all
+        * queued requests that reference the corrupt context.
         */
-       list_for_each_entry(buffer, &engine->buffers, link) {
-               buffer->last_retired_head = buffer->tail;
-               intel_ring_update_space(buffer);
-       }
-
-       intel_ring_init_seqno(engine, engine->last_submitted_seqno);
+       incomplete_ctx = request->ctx;
+       if (i915_gem_context_is_default(incomplete_ctx))
+               return;
 
-       engine->i915->gt.active_engines &= ~intel_engine_flag(engine);
+       list_for_each_entry_continue(request, &engine->request_list, link)
+               if (request->ctx == incomplete_ctx)
+                       reset_request(request);
 }
 
-void i915_gem_reset(struct drm_device *dev)
+void i915_gem_reset(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_engine_cs *engine;
 
-       /*
-        * Before we free the objects from the requests, we need to inspect
-        * them for finding the guilty party. As the requests only borrow
-        * their reference to the objects, the inspection must be done first.
-        */
-       for_each_engine(engine, dev_priv)
-               i915_gem_reset_engine_status(engine);
+       i915_gem_retire_requests(dev_priv);
 
        for_each_engine(engine, dev_priv)
-               i915_gem_reset_engine_cleanup(engine);
-       mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0);
+               i915_gem_reset_engine(engine);
 
-       i915_gem_context_reset(dev);
+       i915_gem_restore_fences(&dev_priv->drm);
 
-       i915_gem_restore_fences(dev);
+       if (dev_priv->gt.awake) {
+               intel_sanitize_gt_powersave(dev_priv);
+               intel_enable_gt_powersave(dev_priv);
+               if (INTEL_GEN(dev_priv) >= 6)
+                       gen6_rps_busy(dev_priv);
+       }
+}
 
-       WARN_ON(i915_verify_lists(dev));
+static void nop_submit_request(struct drm_i915_gem_request *request)
+{
 }
 
-/**
- * This function clears the request list as sequence numbers are passed.
- * @engine: engine to retire requests on
- */
-void
-i915_gem_retire_requests_ring(struct intel_engine_cs *engine)
+static void i915_gem_cleanup_engine(struct intel_engine_cs *engine)
 {
-       WARN_ON(i915_verify_lists(engine->dev));
+       engine->submit_request = nop_submit_request;
 
-       /* Retire requests first as we use it above for the early return.
-        * If we retire requests last, we may use a later seqno and so clear
-        * the requests lists without clearing the active list, leading to
-        * confusion.
+       /* Mark all pending requests as complete so that any concurrent
+        * (lockless) lookup doesn't try and wait upon the request as we
+        * reset it.
         */
-       while (!list_empty(&engine->request_list)) {
-               struct drm_i915_gem_request *request;
-
-               request = list_first_entry(&engine->request_list,
-                                          struct drm_i915_gem_request,
-                                          list);
-
-               if (!i915_gem_request_completed(request))
-                       break;
-
-               i915_gem_request_retire(request);
-       }
+       intel_engine_init_seqno(engine, engine->last_submitted_seqno);
 
-       /* Move any buffers on the active list that are no longer referenced
-        * by the ringbuffer to the flushing/inactive lists as appropriate,
-        * before we free the context associated with the requests.
+       /*
+        * Clear the execlists queue up before freeing the requests, as those
+        * are the ones that keep the context and ringbuffer backing objects
+        * pinned in place.
         */
-       while (!list_empty(&engine->active_list)) {
-               struct drm_i915_gem_object *obj;
 
-               obj = list_first_entry(&engine->active_list,
-                                      struct drm_i915_gem_object,
-                                      engine_list[engine->id]);
-
-               if (!list_empty(&obj->last_read_req[engine->id]->list))
-                       break;
-
-               i915_gem_object_retire__read(obj, engine->id);
+       if (i915.enable_execlists) {
+               spin_lock(&engine->execlist_lock);
+               INIT_LIST_HEAD(&engine->execlist_queue);
+               i915_gem_request_put(engine->execlist_port[0].request);
+               i915_gem_request_put(engine->execlist_port[1].request);
+               memset(engine->execlist_port, 0, sizeof(engine->execlist_port));
+               spin_unlock(&engine->execlist_lock);
        }
 
-       WARN_ON(i915_verify_lists(engine->dev));
+       engine->i915->gt.active_engines &= ~intel_engine_flag(engine);
 }
 
-void i915_gem_retire_requests(struct drm_i915_private *dev_priv)
+void i915_gem_set_wedged(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
 
        lockdep_assert_held(&dev_priv->drm.struct_mutex);
+       set_bit(I915_WEDGED, &dev_priv->gpu_error.flags);
 
-       if (dev_priv->gt.active_engines == 0)
-               return;
-
-       GEM_BUG_ON(!dev_priv->gt.awake);
-
-       for_each_engine(engine, dev_priv) {
-               i915_gem_retire_requests_ring(engine);
-               if (list_empty(&engine->request_list))
-                       dev_priv->gt.active_engines &= ~intel_engine_flag(engine);
-       }
+       i915_gem_context_lost(dev_priv);
+       for_each_engine(engine, dev_priv)
+               i915_gem_cleanup_engine(engine);
+       mod_delayed_work(dev_priv->wq, &dev_priv->gt.idle_work, 0);
 
-       if (dev_priv->gt.active_engines == 0)
-               queue_delayed_work(dev_priv->wq,
-                                  &dev_priv->gt.idle_work,
-                                  msecs_to_jiffies(100));
+       i915_gem_retire_requests(dev_priv);
 }
 
 static void
@@ -3287,10 +2701,12 @@ i915_gem_retire_work_handler(struct work_struct *work)
         * We do not need to do this test under locking as in the worst-case
         * we queue the retire worker once too often.
         */
-       if (READ_ONCE(dev_priv->gt.awake))
+       if (READ_ONCE(dev_priv->gt.awake)) {
+               i915_queue_hangcheck(dev_priv);
                queue_delayed_work(dev_priv->wq,
                                   &dev_priv->gt.retire_work,
                                   round_jiffies_up_relative(HZ));
+       }
 }
 
 static void
@@ -3300,7 +2716,6 @@ i915_gem_idle_work_handler(struct work_struct *work)
                container_of(work, typeof(*dev_priv), gt.idle_work.work);
        struct drm_device *dev = &dev_priv->drm;
        struct intel_engine_cs *engine;
-       unsigned int stuck_engines;
        bool rearm_hangcheck;
 
        if (!READ_ONCE(dev_priv->gt.awake))
@@ -3321,303 +2736,98 @@ i915_gem_idle_work_handler(struct work_struct *work)
        }
 
        if (dev_priv->gt.active_engines)
-               goto out_unlock;
-
-       for_each_engine(engine, dev_priv)
-               i915_gem_batch_pool_fini(&engine->batch_pool);
-
-       GEM_BUG_ON(!dev_priv->gt.awake);
-       dev_priv->gt.awake = false;
-       rearm_hangcheck = false;
-
-       stuck_engines = intel_kick_waiters(dev_priv);
-       if (unlikely(stuck_engines)) {
-               DRM_DEBUG_DRIVER("kicked stuck waiters...missed irq\n");
-               dev_priv->gpu_error.missed_irq_rings |= stuck_engines;
-       }
-
-       if (INTEL_GEN(dev_priv) >= 6)
-               gen6_rps_idle(dev_priv);
-       intel_runtime_pm_put(dev_priv);
-out_unlock:
-       mutex_unlock(&dev->struct_mutex);
-
-out_rearm:
-       if (rearm_hangcheck) {
-               GEM_BUG_ON(!dev_priv->gt.awake);
-               i915_queue_hangcheck(dev_priv);
-       }
-}
-
-/**
- * Ensures that an object will eventually get non-busy by flushing any required
- * write domains, emitting any outstanding lazy request and retiring and
- * completed requests.
- * @obj: object to flush
- */
-static int
-i915_gem_object_flush_active(struct drm_i915_gem_object *obj)
-{
-       int i;
-
-       if (!obj->active)
-               return 0;
-
-       for (i = 0; i < I915_NUM_ENGINES; i++) {
-               struct drm_i915_gem_request *req;
-
-               req = obj->last_read_req[i];
-               if (req == NULL)
-                       continue;
-
-               if (i915_gem_request_completed(req))
-                       i915_gem_object_retire__read(obj, i);
-       }
-
-       return 0;
-}
-
-/**
- * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT
- * @dev: drm device pointer
- * @data: ioctl data blob
- * @file: drm file pointer
- *
- * Returns 0 if successful, else an error is returned with the remaining time in
- * the timeout parameter.
- *  -ETIME: object is still busy after timeout
- *  -ERESTARTSYS: signal interrupted the wait
- *  -ENONENT: object doesn't exist
- * Also possible, but rare:
- *  -EAGAIN: GPU wedged
- *  -ENOMEM: damn
- *  -ENODEV: Internal IRQ fail
- *  -E?: The add request failed
- *
- * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any
- * non-zero timeout parameter the wait ioctl will wait for the given number of
- * nanoseconds on an object becoming unbusy. Since the wait itself does so
- * without holding struct_mutex the object may become re-busied before this
- * function completes. A similar but shorter * race condition exists in the busy
- * ioctl
- */
-int
-i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
-{
-       struct drm_i915_gem_wait *args = data;
-       struct drm_i915_gem_object *obj;
-       struct drm_i915_gem_request *req[I915_NUM_ENGINES];
-       int i, n = 0;
-       int ret;
-
-       if (args->flags != 0)
-               return -EINVAL;
-
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
-
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->bo_handle));
-       if (&obj->base == NULL) {
-               mutex_unlock(&dev->struct_mutex);
-               return -ENOENT;
-       }
-
-       /* Need to make sure the object gets inactive eventually. */
-       ret = i915_gem_object_flush_active(obj);
-       if (ret)
-               goto out;
-
-       if (!obj->active)
-               goto out;
-
-       /* Do this after OLR check to make sure we make forward progress polling
-        * on this IOCTL with a timeout == 0 (like busy ioctl)
-        */
-       if (args->timeout_ns == 0) {
-               ret = -ETIME;
-               goto out;
-       }
-
-       drm_gem_object_unreference(&obj->base);
-
-       for (i = 0; i < I915_NUM_ENGINES; i++) {
-               if (obj->last_read_req[i] == NULL)
-                       continue;
-
-               req[n++] = i915_gem_request_reference(obj->last_read_req[i]);
-       }
-
-       mutex_unlock(&dev->struct_mutex);
-
-       for (i = 0; i < n; i++) {
-               if (ret == 0)
-                       ret = __i915_wait_request(req[i], true,
-                                                 args->timeout_ns > 0 ? &args->timeout_ns : NULL,
-                                                 to_rps_client(file));
-               i915_gem_request_unreference(req[i]);
-       }
-       return ret;
-
-out:
-       drm_gem_object_unreference(&obj->base);
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
-}
-
-static int
-__i915_gem_object_sync(struct drm_i915_gem_object *obj,
-                      struct intel_engine_cs *to,
-                      struct drm_i915_gem_request *from_req,
-                      struct drm_i915_gem_request **to_req)
-{
-       struct intel_engine_cs *from;
-       int ret;
-
-       from = i915_gem_request_get_engine(from_req);
-       if (to == from)
-               return 0;
-
-       if (i915_gem_request_completed(from_req))
-               return 0;
-
-       if (!i915_semaphore_is_enabled(to_i915(obj->base.dev))) {
-               struct drm_i915_private *i915 = to_i915(obj->base.dev);
-               ret = __i915_wait_request(from_req,
-                                         i915->mm.interruptible,
-                                         NULL,
-                                         &i915->rps.semaphores);
-               if (ret)
-                       return ret;
-
-               i915_gem_object_retire_request(obj, from_req);
-       } else {
-               int idx = intel_ring_sync_index(from, to);
-               u32 seqno = i915_gem_request_get_seqno(from_req);
-
-               WARN_ON(!to_req);
-
-               if (seqno <= from->semaphore.sync_seqno[idx])
-                       return 0;
-
-               if (*to_req == NULL) {
-                       struct drm_i915_gem_request *req;
+               goto out_unlock;
 
-                       req = i915_gem_request_alloc(to, NULL);
-                       if (IS_ERR(req))
-                               return PTR_ERR(req);
+       for_each_engine(engine, dev_priv)
+               i915_gem_batch_pool_fini(&engine->batch_pool);
 
-                       *to_req = req;
-               }
+       GEM_BUG_ON(!dev_priv->gt.awake);
+       dev_priv->gt.awake = false;
+       rearm_hangcheck = false;
 
-               trace_i915_gem_ring_sync_to(*to_req, from, from_req);
-               ret = to->semaphore.sync_to(*to_req, from, seqno);
-               if (ret)
-                       return ret;
+       if (INTEL_GEN(dev_priv) >= 6)
+               gen6_rps_idle(dev_priv);
+       intel_runtime_pm_put(dev_priv);
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
 
-               /* We use last_read_req because sync_to()
-                * might have just caused seqno wrap under
-                * the radar.
-                */
-               from->semaphore.sync_seqno[idx] =
-                       i915_gem_request_get_seqno(obj->last_read_req[from->id]);
+out_rearm:
+       if (rearm_hangcheck) {
+               GEM_BUG_ON(!dev_priv->gt.awake);
+               i915_queue_hangcheck(dev_priv);
        }
+}
 
-       return 0;
+void i915_gem_close_object(struct drm_gem_object *gem, struct drm_file *file)
+{
+       struct drm_i915_gem_object *obj = to_intel_bo(gem);
+       struct drm_i915_file_private *fpriv = file->driver_priv;
+       struct i915_vma *vma, *vn;
+
+       mutex_lock(&obj->base.dev->struct_mutex);
+       list_for_each_entry_safe(vma, vn, &obj->vma_list, obj_link)
+               if (vma->vm->file == fpriv)
+                       i915_vma_close(vma);
+       mutex_unlock(&obj->base.dev->struct_mutex);
 }
 
 /**
- * i915_gem_object_sync - sync an object to a ring.
- *
- * @obj: object which may be in use on another ring.
- * @to: ring we wish to use the object on. May be NULL.
- * @to_req: request we wish to use the object for. See below.
- *          This will be allocated and returned if a request is
- *          required but not passed in.
- *
- * This code is meant to abstract object synchronization with the GPU.
- * Calling with NULL implies synchronizing the object with the CPU
- * rather than a particular GPU ring. Conceptually we serialise writes
- * between engines inside the GPU. We only allow one engine to write
- * into a buffer at any time, but multiple readers. To ensure each has
- * a coherent view of memory, we must:
- *
- * - If there is an outstanding write request to the object, the new
- *   request must wait for it to complete (either CPU or in hw, requests
- *   on the same ring will be naturally ordered).
- *
- * - If we are a write request (pending_write_domain is set), the new
- *   request must wait for outstanding read requests to complete.
+ * i915_gem_wait_ioctl - implements DRM_IOCTL_I915_GEM_WAIT
+ * @dev: drm device pointer
+ * @data: ioctl data blob
+ * @file: drm file pointer
  *
- * For CPU synchronisation (NULL to) no request is required. For syncing with
- * rings to_req must be non-NULL. However, a request does not have to be
- * pre-allocated. If *to_req is NULL and sync commands will be emitted then a
- * request will be allocated automatically and returned through *to_req. Note
- * that it is not guaranteed that commands will be emitted (because the system
- * might already be idle). Hence there is no need to create a request that
- * might never have any work submitted. Note further that if a request is
- * returned in *to_req, it is the responsibility of the caller to submit
- * that request (after potentially adding more work to it).
+ * Returns 0 if successful, else an error is returned with the remaining time in
+ * the timeout parameter.
+ *  -ETIME: object is still busy after timeout
+ *  -ERESTARTSYS: signal interrupted the wait
+ *  -ENONENT: object doesn't exist
+ * Also possible, but rare:
+ *  -EAGAIN: GPU wedged
+ *  -ENOMEM: damn
+ *  -ENODEV: Internal IRQ fail
+ *  -E?: The add request failed
  *
- * Returns 0 if successful, else propagates up the lower layer error.
+ * The wait ioctl with a timeout of 0 reimplements the busy ioctl. With any
+ * non-zero timeout parameter the wait ioctl will wait for the given number of
+ * nanoseconds on an object becoming unbusy. Since the wait itself does so
+ * without holding struct_mutex the object may become re-busied before this
+ * function completes. A similar but shorter * race condition exists in the busy
+ * ioctl
  */
 int
-i915_gem_object_sync(struct drm_i915_gem_object *obj,
-                    struct intel_engine_cs *to,
-                    struct drm_i915_gem_request **to_req)
+i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file)
 {
-       const bool readonly = obj->base.pending_write_domain == 0;
-       struct drm_i915_gem_request *req[I915_NUM_ENGINES];
-       int ret, i, n;
+       struct drm_i915_gem_wait *args = data;
+       struct intel_rps_client *rps = to_rps_client(file);
+       struct drm_i915_gem_object *obj;
+       unsigned long active;
+       int idx, ret = 0;
 
-       if (!obj->active)
-               return 0;
+       if (args->flags != 0)
+               return -EINVAL;
 
-       if (to == NULL)
-               return i915_gem_object_wait_rendering(obj, readonly);
+       obj = i915_gem_object_lookup(file, args->bo_handle);
+       if (!obj)
+               return -ENOENT;
 
-       n = 0;
-       if (readonly) {
-               if (obj->last_write_req)
-                       req[n++] = obj->last_write_req;
-       } else {
-               for (i = 0; i < I915_NUM_ENGINES; i++)
-                       if (obj->last_read_req[i])
-                               req[n++] = obj->last_read_req[i];
-       }
-       for (i = 0; i < n; i++) {
-               ret = __i915_gem_object_sync(obj, to, req[i], to_req);
+       active = __I915_BO_ACTIVE(obj);
+       for_each_active(active, idx) {
+               s64 *timeout = args->timeout_ns >= 0 ? &args->timeout_ns : NULL;
+               ret = i915_gem_active_wait_unlocked(&obj->last_read[idx],
+                                                   I915_WAIT_INTERRUPTIBLE,
+                                                   timeout, rps);
                if (ret)
-                       return ret;
+                       break;
        }
 
-       return 0;
-}
-
-static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
-{
-       u32 old_write_domain, old_read_domains;
-
-       /* Force a pagefault for domain tracking on next user access */
-       i915_gem_release_mmap(obj);
-
-       if ((obj->base.read_domains & I915_GEM_DOMAIN_GTT) == 0)
-               return;
-
-       old_read_domains = obj->base.read_domains;
-       old_write_domain = obj->base.write_domain;
-
-       obj->base.read_domains &= ~I915_GEM_DOMAIN_GTT;
-       obj->base.write_domain &= ~I915_GEM_DOMAIN_GTT;
-
-       trace_i915_gem_object_change_domain(obj,
-                                           old_read_domains,
-                                           old_write_domain);
+       i915_gem_object_put_unlocked(obj);
+       return ret;
 }
 
 static void __i915_vma_iounmap(struct i915_vma *vma)
 {
-       GEM_BUG_ON(vma->pin_count);
+       GEM_BUG_ON(i915_vma_is_pinned(vma));
 
        if (vma->iomap == NULL)
                return;
@@ -3626,65 +2836,83 @@ static void __i915_vma_iounmap(struct i915_vma *vma)
        vma->iomap = NULL;
 }
 
-static int __i915_vma_unbind(struct i915_vma *vma, bool wait)
+int i915_vma_unbind(struct i915_vma *vma)
 {
        struct drm_i915_gem_object *obj = vma->obj;
-       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
+       unsigned long active;
        int ret;
 
-       if (list_empty(&vma->obj_link))
-               return 0;
-
-       if (!drm_mm_node_allocated(&vma->node)) {
-               i915_gem_vma_destroy(vma);
-               return 0;
-       }
-
-       if (vma->pin_count)
-               return -EBUSY;
+       /* First wait upon any activity as retiring the request may
+        * have side-effects such as unpinning or even unbinding this vma.
+        */
+       active = i915_vma_get_active(vma);
+       if (active) {
+               int idx;
+
+               /* When a closed VMA is retired, it is unbound - eek.
+                * In order to prevent it from being recursively closed,
+                * take a pin on the vma so that the second unbind is
+                * aborted.
+                */
+               __i915_vma_pin(vma);
 
-       BUG_ON(obj->pages == NULL);
+               for_each_active(active, idx) {
+                       ret = i915_gem_active_retire(&vma->last_read[idx],
+                                                  &vma->vm->dev->struct_mutex);
+                       if (ret)
+                               break;
+               }
 
-       if (wait) {
-               ret = i915_gem_object_wait_rendering(obj, false);
+               __i915_vma_unpin(vma);
                if (ret)
                        return ret;
+
+               GEM_BUG_ON(i915_vma_is_active(vma));
        }
 
-       if (vma->is_ggtt && vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) {
-               i915_gem_object_finish_gtt(obj);
+       if (i915_vma_is_pinned(vma))
+               return -EBUSY;
+
+       if (!drm_mm_node_allocated(&vma->node))
+               goto destroy;
+
+       GEM_BUG_ON(obj->bind_count == 0);
+       GEM_BUG_ON(!obj->pages);
 
+       if (i915_vma_is_map_and_fenceable(vma)) {
                /* release the fence reg _after_ flushing */
-               ret = i915_gem_object_put_fence(obj);
+               ret = i915_vma_put_fence(vma);
                if (ret)
                        return ret;
 
+               /* Force a pagefault for domain tracking on next user access */
+               i915_gem_release_mmap(obj);
+
                __i915_vma_iounmap(vma);
+               vma->flags &= ~I915_VMA_CAN_FENCE;
        }
 
-       trace_i915_vma_unbind(vma);
-
-       vma->vm->unbind_vma(vma);
-       vma->bound = 0;
-
-       list_del_init(&vma->vm_link);
-       if (vma->is_ggtt) {
-               if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL) {
-                       obj->map_and_fenceable = false;
-               } else if (vma->ggtt_view.pages) {
-                       sg_free_table(vma->ggtt_view.pages);
-                       kfree(vma->ggtt_view.pages);
-               }
-               vma->ggtt_view.pages = NULL;
+       if (likely(!vma->vm->closed)) {
+               trace_i915_vma_unbind(vma);
+               vma->vm->unbind_vma(vma);
        }
+       vma->flags &= ~(I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
 
        drm_mm_remove_node(&vma->node);
-       i915_gem_vma_destroy(vma);
+       list_move_tail(&vma->vm_link, &vma->vm->unbound_list);
+
+       if (vma->pages != obj->pages) {
+               GEM_BUG_ON(!vma->pages);
+               sg_free_table(vma->pages);
+               kfree(vma->pages);
+       }
+       vma->pages = NULL;
 
        /* Since the unbound list is global, only move to that list if
         * no more VMAs exist. */
-       if (list_empty(&obj->vma_list))
-               list_move_tail(&obj->global_list, &dev_priv->mm.unbound_list);
+       if (--obj->bind_count == 0)
+               list_move_tail(&obj->global_list,
+                              &to_i915(obj->base.dev)->mm.unbound_list);
 
        /* And finally now the object is completely decoupled from this vma,
         * we can drop its hold on the backing storage and allow it to be
@@ -3692,36 +2920,28 @@ static int __i915_vma_unbind(struct i915_vma *vma, bool wait)
         */
        i915_gem_object_unpin_pages(obj);
 
-       return 0;
-}
-
-int i915_vma_unbind(struct i915_vma *vma)
-{
-       return __i915_vma_unbind(vma, true);
-}
+destroy:
+       if (unlikely(i915_vma_is_closed(vma)))
+               i915_vma_destroy(vma);
 
-int __i915_vma_unbind_no_wait(struct i915_vma *vma)
-{
-       return __i915_vma_unbind(vma, false);
+       return 0;
 }
 
-int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv)
+int i915_gem_wait_for_idle(struct drm_i915_private *dev_priv,
+                          unsigned int flags)
 {
        struct intel_engine_cs *engine;
        int ret;
 
-       lockdep_assert_held(&dev_priv->drm.struct_mutex);
-
        for_each_engine(engine, dev_priv) {
                if (engine->last_context == NULL)
                        continue;
 
-               ret = intel_engine_idle(engine);
+               ret = intel_engine_idle(engine, flags);
                if (ret)
                        return ret;
        }
 
-       WARN_ON(i915_verify_lists(dev));
        return 0;
 }
 
@@ -3759,128 +2979,87 @@ static bool i915_gem_valid_gtt_space(struct i915_vma *vma,
 }
 
 /**
- * Finds free space in the GTT aperture and binds the object or a view of it
- * there.
- * @obj: object to bind
- * @vm: address space to bind into
- * @ggtt_view: global gtt view if applicable
- * @alignment: requested alignment
+ * i915_vma_insert - finds a slot for the vma in its address space
+ * @vma: the vma
+ * @size: requested size in bytes (can be larger than the VMA)
+ * @alignment: required alignment
  * @flags: mask of PIN_* flags to use
+ *
+ * First we try to allocate some free space that meets the requirements for
+ * the VMA. Failiing that, if the flags permit, it will evict an old VMA,
+ * preferrably the oldest idle entry to make room for the new VMA.
+ *
+ * Returns:
+ * 0 on success, negative error code otherwise.
  */
-static struct i915_vma *
-i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
-                          struct i915_address_space *vm,
-                          const struct i915_ggtt_view *ggtt_view,
-                          unsigned alignment,
-                          uint64_t flags)
+static int
+i915_vma_insert(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       u32 fence_alignment, unfenced_alignment;
-       u32 search_flag, alloc_flag;
+       struct drm_i915_private *dev_priv = to_i915(vma->vm->dev);
+       struct drm_i915_gem_object *obj = vma->obj;
        u64 start, end;
-       u64 size, fence_size;
-       struct i915_vma *vma;
        int ret;
 
-       if (i915_is_ggtt(vm)) {
-               u32 view_size;
-
-               if (WARN_ON(!ggtt_view))
-                       return ERR_PTR(-EINVAL);
-
-               view_size = i915_ggtt_view_size(obj, ggtt_view);
-
-               fence_size = i915_gem_get_gtt_size(dev,
-                                                  view_size,
-                                                  obj->tiling_mode);
-               fence_alignment = i915_gem_get_gtt_alignment(dev,
-                                                            view_size,
-                                                            obj->tiling_mode,
-                                                            true);
-               unfenced_alignment = i915_gem_get_gtt_alignment(dev,
-                                                               view_size,
-                                                               obj->tiling_mode,
-                                                               false);
-               size = flags & PIN_MAPPABLE ? fence_size : view_size;
-       } else {
-               fence_size = i915_gem_get_gtt_size(dev,
-                                                  obj->base.size,
-                                                  obj->tiling_mode);
-               fence_alignment = i915_gem_get_gtt_alignment(dev,
-                                                            obj->base.size,
-                                                            obj->tiling_mode,
-                                                            true);
-               unfenced_alignment =
-                       i915_gem_get_gtt_alignment(dev,
-                                                  obj->base.size,
-                                                  obj->tiling_mode,
-                                                  false);
-               size = flags & PIN_MAPPABLE ? fence_size : obj->base.size;
-       }
+       GEM_BUG_ON(vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND));
+       GEM_BUG_ON(drm_mm_node_allocated(&vma->node));
+
+       size = max(size, vma->size);
+       if (flags & PIN_MAPPABLE)
+               size = i915_gem_get_ggtt_size(dev_priv, size,
+                                             i915_gem_object_get_tiling(obj));
+
+       alignment = max(max(alignment, vma->display_alignment),
+                       i915_gem_get_ggtt_alignment(dev_priv, size,
+                                                   i915_gem_object_get_tiling(obj),
+                                                   flags & PIN_MAPPABLE));
 
        start = flags & PIN_OFFSET_BIAS ? flags & PIN_OFFSET_MASK : 0;
-       end = vm->total;
+
+       end = vma->vm->total;
        if (flags & PIN_MAPPABLE)
-               end = min_t(u64, end, ggtt->mappable_end);
+               end = min_t(u64, end, dev_priv->ggtt.mappable_end);
        if (flags & PIN_ZONE_4G)
                end = min_t(u64, end, (1ULL << 32) - PAGE_SIZE);
 
-       if (alignment == 0)
-               alignment = flags & PIN_MAPPABLE ? fence_alignment :
-                                               unfenced_alignment;
-       if (flags & PIN_MAPPABLE && alignment & (fence_alignment - 1)) {
-               DRM_DEBUG("Invalid object (view type=%u) alignment requested %u\n",
-                         ggtt_view ? ggtt_view->type : 0,
-                         alignment);
-               return ERR_PTR(-EINVAL);
-       }
-
        /* If binding the object/GGTT view requires more space than the entire
         * aperture has, reject it early before evicting everything in a vain
         * attempt to find space.
         */
        if (size > end) {
-               DRM_DEBUG("Attempting to bind an object (view type=%u) larger than the aperture: size=%llu > %s aperture=%llu\n",
-                         ggtt_view ? ggtt_view->type : 0,
-                         size,
+               DRM_DEBUG("Attempting to bind an object larger than the aperture: request=%llu [object=%zd] > %s aperture=%llu\n",
+                         size, obj->base.size,
                          flags & PIN_MAPPABLE ? "mappable" : "total",
                          end);
-               return ERR_PTR(-E2BIG);
+               return -E2BIG;
        }
 
        ret = i915_gem_object_get_pages(obj);
        if (ret)
-               return ERR_PTR(ret);
+               return ret;
 
        i915_gem_object_pin_pages(obj);
 
-       vma = ggtt_view ? i915_gem_obj_lookup_or_create_ggtt_vma(obj, ggtt_view) :
-                         i915_gem_obj_lookup_or_create_vma(obj, vm);
-
-       if (IS_ERR(vma))
-               goto err_unpin;
-
        if (flags & PIN_OFFSET_FIXED) {
-               uint64_t offset = flags & PIN_OFFSET_MASK;
-
-               if (offset & (alignment - 1) || offset + size > end) {
+               u64 offset = flags & PIN_OFFSET_MASK;
+               if (offset & (alignment - 1) || offset > end - size) {
                        ret = -EINVAL;
-                       goto err_free_vma;
+                       goto err_unpin;
                }
+
                vma->node.start = offset;
                vma->node.size = size;
                vma->node.color = obj->cache_level;
-               ret = drm_mm_reserve_node(&vm->mm, &vma->node);
+               ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node);
                if (ret) {
                        ret = i915_gem_evict_for_vma(vma);
                        if (ret == 0)
-                               ret = drm_mm_reserve_node(&vm->mm, &vma->node);
+                               ret = drm_mm_reserve_node(&vma->vm->mm, &vma->node);
+                       if (ret)
+                               goto err_unpin;
                }
-               if (ret)
-                       goto err_free_vma;
        } else {
+               u32 search_flag, alloc_flag;
+
                if (flags & PIN_HIGH) {
                        search_flag = DRM_MM_SEARCH_BELOW;
                        alloc_flag = DRM_MM_CREATE_TOP;
@@ -3889,47 +3068,45 @@ i915_gem_object_bind_to_vm(struct drm_i915_gem_object *obj,
                        alloc_flag = DRM_MM_CREATE_DEFAULT;
                }
 
+               /* We only allocate in PAGE_SIZE/GTT_PAGE_SIZE (4096) chunks,
+                * so we know that we always have a minimum alignment of 4096.
+                * The drm_mm range manager is optimised to return results
+                * with zero alignment, so where possible use the optimal
+                * path.
+                */
+               if (alignment <= 4096)
+                       alignment = 0;
+
 search_free:
-               ret = drm_mm_insert_node_in_range_generic(&vm->mm, &vma->node,
+               ret = drm_mm_insert_node_in_range_generic(&vma->vm->mm,
+                                                         &vma->node,
                                                          size, alignment,
                                                          obj->cache_level,
                                                          start, end,
                                                          search_flag,
                                                          alloc_flag);
                if (ret) {
-                       ret = i915_gem_evict_something(dev, vm, size, alignment,
+                       ret = i915_gem_evict_something(vma->vm, size, alignment,
                                                       obj->cache_level,
                                                       start, end,
                                                       flags);
                        if (ret == 0)
                                goto search_free;
 
-                       goto err_free_vma;
+                       goto err_unpin;
                }
        }
-       if (WARN_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level))) {
-               ret = -EINVAL;
-               goto err_remove_node;
-       }
-
-       trace_i915_vma_bind(vma, flags);
-       ret = i915_vma_bind(vma, obj->cache_level, flags);
-       if (ret)
-               goto err_remove_node;
+       GEM_BUG_ON(!i915_gem_valid_gtt_space(vma, obj->cache_level));
 
        list_move_tail(&obj->global_list, &dev_priv->mm.bound_list);
-       list_add_tail(&vma->vm_link, &vm->inactive_list);
+       list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
+       obj->bind_count++;
 
-       return vma;
+       return 0;
 
-err_remove_node:
-       drm_mm_remove_node(&vma->node);
-err_free_vma:
-       i915_gem_vma_destroy(vma);
-       vma = ERR_PTR(ret);
 err_unpin:
        i915_gem_object_unpin_pages(obj);
-       return vma;
+       return ret;
 }
 
 bool
@@ -3974,51 +3151,72 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj,
 static void
 i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj)
 {
-       uint32_t old_write_domain;
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
 
        if (obj->base.write_domain != I915_GEM_DOMAIN_GTT)
                return;
 
        /* No actual flushing is required for the GTT write domain.  Writes
-        * to it immediately go to main memory as far as we know, so there's
+        * to it "immediately" go to main memory as far as we know, so there's
         * no chipset flush.  It also doesn't land in render cache.
         *
         * However, we do have to enforce the order so that all writes through
         * the GTT land before any writes to the device, such as updates to
         * the GATT itself.
+        *
+        * We also have to wait a bit for the writes to land from the GTT.
+        * An uncached read (i.e. mmio) seems to be ideal for the round-trip
+        * timing. This issue has only been observed when switching quickly
+        * between GTT writes and CPU reads from inside the kernel on recent hw,
+        * and it appears to only affect discrete GTT blocks (i.e. on LLC
+        * system agents we cannot reproduce this behaviour).
         */
        wmb();
+       if (INTEL_GEN(dev_priv) >= 6 && !HAS_LLC(dev_priv))
+               POSTING_READ(RING_ACTHD(dev_priv->engine[RCS].mmio_base));
 
-       old_write_domain = obj->base.write_domain;
-       obj->base.write_domain = 0;
-
-       intel_fb_obj_flush(obj, false, ORIGIN_GTT);
+       intel_fb_obj_flush(obj, false, write_origin(obj, I915_GEM_DOMAIN_GTT));
 
+       obj->base.write_domain = 0;
        trace_i915_gem_object_change_domain(obj,
                                            obj->base.read_domains,
-                                           old_write_domain);
+                                           I915_GEM_DOMAIN_GTT);
 }
 
 /** Flushes the CPU write domain for the object if it's dirty. */
 static void
 i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj)
 {
-       uint32_t old_write_domain;
-
        if (obj->base.write_domain != I915_GEM_DOMAIN_CPU)
                return;
 
        if (i915_gem_clflush_object(obj, obj->pin_display))
                i915_gem_chipset_flush(to_i915(obj->base.dev));
 
-       old_write_domain = obj->base.write_domain;
-       obj->base.write_domain = 0;
-
        intel_fb_obj_flush(obj, false, ORIGIN_CPU);
 
+       obj->base.write_domain = 0;
        trace_i915_gem_object_change_domain(obj,
                                            obj->base.read_domains,
-                                           old_write_domain);
+                                           I915_GEM_DOMAIN_CPU);
+}
+
+static void i915_gem_object_bump_inactive_ggtt(struct drm_i915_gem_object *obj)
+{
+       struct i915_vma *vma;
+
+       list_for_each_entry(vma, &obj->vma_list, obj_link) {
+               if (!i915_vma_is_ggtt(vma))
+                       continue;
+
+               if (i915_vma_is_active(vma))
+                       continue;
+
+               if (!drm_mm_node_allocated(&vma->node))
+                       continue;
+
+               list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
+       }
 }
 
 /**
@@ -4032,20 +3230,16 @@ i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj)
 int
 i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
        uint32_t old_write_domain, old_read_domains;
-       struct i915_vma *vma;
        int ret;
 
-       if (obj->base.write_domain == I915_GEM_DOMAIN_GTT)
-               return 0;
-
        ret = i915_gem_object_wait_rendering(obj, !write);
        if (ret)
                return ret;
 
+       if (obj->base.write_domain == I915_GEM_DOMAIN_GTT)
+               return 0;
+
        /* Flush and acquire obj->pages so that we are coherent through
         * direct access in memory with previous cached writes through
         * shmemfs and that our cache domain tracking remains valid.
@@ -4086,10 +3280,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
                                            old_write_domain);
 
        /* And bump the LRU for this access */
-       vma = i915_gem_obj_to_ggtt(obj);
-       if (vma && drm_mm_node_allocated(&vma->node) && !obj->active)
-               list_move_tail(&vma->vm_link,
-                              &ggtt->base.inactive_list);
+       i915_gem_object_bump_inactive_ggtt(obj);
 
        return 0;
 }
@@ -4112,9 +3303,7 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
 int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                                    enum i915_cache_level cache_level)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct i915_vma *vma, *next;
-       bool bound = false;
+       struct i915_vma *vma;
        int ret = 0;
 
        if (obj->cache_level == cache_level)
@@ -4125,21 +3314,28 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
         * catch the issue of the CS prefetch crossing page boundaries and
         * reading an invalid PTE on older architectures.
         */
-       list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) {
+restart:
+       list_for_each_entry(vma, &obj->vma_list, obj_link) {
                if (!drm_mm_node_allocated(&vma->node))
                        continue;
 
-               if (vma->pin_count) {
+               if (i915_vma_is_pinned(vma)) {
                        DRM_DEBUG("can not change the cache level of pinned objects\n");
                        return -EBUSY;
                }
 
-               if (!i915_gem_valid_gtt_space(vma, cache_level)) {
-                       ret = i915_vma_unbind(vma);
-                       if (ret)
-                               return ret;
-               } else
-                       bound = true;
+               if (i915_gem_valid_gtt_space(vma, cache_level))
+                       continue;
+
+               ret = i915_vma_unbind(vma);
+               if (ret)
+                       return ret;
+
+               /* As unbinding may affect other elements in the
+                * obj->vma_list (due to side-effects from retiring
+                * an active vma), play safe and restart the iterator.
+                */
+               goto restart;
        }
 
        /* We can reuse the existing drm_mm nodes but need to change the
@@ -4149,7 +3345,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
         * rewrite the PTE in the belief that doing so tramples upon less
         * state and so involves less work.
         */
-       if (bound) {
+       if (obj->bind_count) {
                /* Before we change the PTE, the GPU must not be accessing it.
                 * If we wait upon the object, we know that all the bound
                 * VMA are no longer active.
@@ -4158,7 +3354,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                if (ret)
                        return ret;
 
-               if (!HAS_LLC(dev) && cache_level != I915_CACHE_NONE) {
+               if (!HAS_LLC(obj->base.dev) && cache_level != I915_CACHE_NONE) {
                        /* Access to snoopable pages through the GTT is
                         * incoherent and on some machines causes a hard
                         * lockup. Relinquish the CPU mmaping to force
@@ -4175,9 +3371,11 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
                         * dropped the fence as all snoopable access is
                         * supposed to be linear.
                         */
-                       ret = i915_gem_object_put_fence(obj);
-                       if (ret)
-                               return ret;
+                       list_for_each_entry(vma, &obj->vma_list, obj_link) {
+                               ret = i915_vma_put_fence(vma);
+                               if (ret)
+                                       return ret;
+                       }
                } else {
                        /* We either have incoherent backing store and
                         * so no GTT access or the architecture is fully
@@ -4221,8 +3419,8 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
        struct drm_i915_gem_caching *args = data;
        struct drm_i915_gem_object *obj;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL)
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
                return -ENOENT;
 
        switch (obj->cache_level) {
@@ -4240,7 +3438,7 @@ int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
                break;
        }
 
-       drm_gem_object_unreference_unlocked(&obj->base);
+       i915_gem_object_put_unlocked(obj);
        return 0;
 }
 
@@ -4282,15 +3480,15 @@ int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
        if (ret)
                goto rpm_put;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj) {
                ret = -ENOENT;
                goto unlock;
        }
 
        ret = i915_gem_object_set_cache_level(obj, level);
 
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
 unlock:
        mutex_unlock(&dev->struct_mutex);
 rpm_put:
@@ -4304,11 +3502,12 @@ rpm_put:
  * Can be called from an uninterruptible phase (modesetting) and allows
  * any flushes to be pipelined (for pageflips).
  */
-int
+struct i915_vma *
 i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
                                     u32 alignment,
                                     const struct i915_ggtt_view *view)
 {
+       struct i915_vma *vma;
        u32 old_read_domains, old_write_domain;
        int ret;
 
@@ -4328,19 +3527,31 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
         */
        ret = i915_gem_object_set_cache_level(obj,
                                              HAS_WT(obj->base.dev) ? I915_CACHE_WT : I915_CACHE_NONE);
-       if (ret)
+       if (ret) {
+               vma = ERR_PTR(ret);
                goto err_unpin_display;
+       }
 
        /* As the user may map the buffer once pinned in the display plane
         * (e.g. libkms for the bootup splash), we have to ensure that we
-        * always use map_and_fenceable for all scanout buffers.
+        * always use map_and_fenceable for all scanout buffers. However,
+        * it may simply be too big to fit into mappable, in which case
+        * put it anyway and hope that userspace can cope (but always first
+        * try to preserve the existing ABI).
         */
-       ret = i915_gem_object_ggtt_pin(obj, view, alignment,
-                                      view->type == I915_GGTT_VIEW_NORMAL ?
-                                      PIN_MAPPABLE : 0);
-       if (ret)
+       vma = ERR_PTR(-ENOSPC);
+       if (view->type == I915_GGTT_VIEW_NORMAL)
+               vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment,
+                                              PIN_MAPPABLE | PIN_NONBLOCK);
+       if (IS_ERR(vma))
+               vma = i915_gem_object_ggtt_pin(obj, view, 0, alignment, 0);
+       if (IS_ERR(vma))
                goto err_unpin_display;
 
+       vma->display_alignment = max_t(u64, vma->display_alignment, alignment);
+
+       WARN_ON(obj->pin_display > i915_vma_pin_count(vma));
+
        i915_gem_object_flush_cpu_write_domain(obj);
 
        old_write_domain = obj->base.write_domain;
@@ -4356,23 +3567,28 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
                                            old_read_domains,
                                            old_write_domain);
 
-       return 0;
+       return vma;
 
 err_unpin_display:
        obj->pin_display--;
-       return ret;
+       return vma;
 }
 
 void
-i915_gem_object_unpin_from_display_plane(struct drm_i915_gem_object *obj,
-                                        const struct i915_ggtt_view *view)
+i915_gem_object_unpin_from_display_plane(struct i915_vma *vma)
 {
-       if (WARN_ON(obj->pin_display == 0))
+       if (WARN_ON(vma->obj->pin_display == 0))
                return;
 
-       i915_gem_object_ggtt_unpin_view(obj, view);
+       if (--vma->obj->pin_display == 0)
+               vma->display_alignment = 0;
 
-       obj->pin_display--;
+       /* Bump the LRU to try and avoid premature eviction whilst flipping  */
+       if (!i915_vma_is_active(vma))
+               list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
+
+       i915_vma_unpin(vma);
+       WARN_ON(vma->obj->pin_display > i915_vma_pin_count(vma));
 }
 
 /**
@@ -4389,13 +3605,13 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write)
        uint32_t old_write_domain, old_read_domains;
        int ret;
 
-       if (obj->base.write_domain == I915_GEM_DOMAIN_CPU)
-               return 0;
-
        ret = i915_gem_object_wait_rendering(obj, !write);
        if (ret)
                return ret;
 
+       if (obj->base.write_domain == I915_GEM_DOMAIN_CPU)
+               return 0;
+
        i915_gem_object_flush_gtt_write_domain(obj);
 
        old_write_domain = obj->base.write_domain;
@@ -4470,28 +3686,31 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
                target = request;
        }
        if (target)
-               i915_gem_request_reference(target);
+               i915_gem_request_get(target);
        spin_unlock(&file_priv->mm.lock);
 
        if (target == NULL)
                return 0;
 
-       ret = __i915_wait_request(target, true, NULL, NULL);
-       i915_gem_request_unreference(target);
+       ret = i915_wait_request(target, I915_WAIT_INTERRUPTIBLE, NULL, NULL);
+       i915_gem_request_put(target);
 
        return ret;
 }
 
 static bool
-i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags)
+i915_vma_misplaced(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 {
-       struct drm_i915_gem_object *obj = vma->obj;
+       if (!drm_mm_node_allocated(&vma->node))
+               return false;
 
-       if (alignment &&
-           vma->node.start & (alignment - 1))
+       if (vma->node.size < size)
                return true;
 
-       if (flags & PIN_MAPPABLE && !obj->map_and_fenceable)
+       if (alignment && vma->node.start & (alignment - 1))
+               return true;
+
+       if (flags & PIN_MAPPABLE && !i915_vma_is_map_and_fenceable(vma))
                return true;
 
        if (flags & PIN_OFFSET_BIAS &&
@@ -4508,135 +3727,208 @@ i915_vma_misplaced(struct i915_vma *vma, uint32_t alignment, uint64_t flags)
 void __i915_vma_set_map_and_fenceable(struct i915_vma *vma)
 {
        struct drm_i915_gem_object *obj = vma->obj;
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
        bool mappable, fenceable;
        u32 fence_size, fence_alignment;
 
-       fence_size = i915_gem_get_gtt_size(obj->base.dev,
-                                          obj->base.size,
-                                          obj->tiling_mode);
-       fence_alignment = i915_gem_get_gtt_alignment(obj->base.dev,
-                                                    obj->base.size,
-                                                    obj->tiling_mode,
-                                                    true);
+       fence_size = i915_gem_get_ggtt_size(dev_priv,
+                                           vma->size,
+                                           i915_gem_object_get_tiling(obj));
+       fence_alignment = i915_gem_get_ggtt_alignment(dev_priv,
+                                                     vma->size,
+                                                     i915_gem_object_get_tiling(obj),
+                                                     true);
 
        fenceable = (vma->node.size == fence_size &&
                     (vma->node.start & (fence_alignment - 1)) == 0);
 
        mappable = (vma->node.start + fence_size <=
-                   to_i915(obj->base.dev)->ggtt.mappable_end);
+                   dev_priv->ggtt.mappable_end);
 
-       obj->map_and_fenceable = mappable && fenceable;
+       if (mappable && fenceable)
+               vma->flags |= I915_VMA_CAN_FENCE;
+       else
+               vma->flags &= ~I915_VMA_CAN_FENCE;
 }
 
-static int
-i915_gem_object_do_pin(struct drm_i915_gem_object *obj,
-                      struct i915_address_space *vm,
-                      const struct i915_ggtt_view *ggtt_view,
-                      uint32_t alignment,
-                      uint64_t flags)
+int __i915_vma_do_pin(struct i915_vma *vma,
+                     u64 size, u64 alignment, u64 flags)
 {
-       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       struct i915_vma *vma;
-       unsigned bound;
+       unsigned int bound = vma->flags;
        int ret;
 
-       if (WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base))
-               return -ENODEV;
-
-       if (WARN_ON(flags & (PIN_GLOBAL | PIN_MAPPABLE) && !i915_is_ggtt(vm)))
-               return -EINVAL;
+       GEM_BUG_ON((flags & (PIN_GLOBAL | PIN_USER)) == 0);
+       GEM_BUG_ON((flags & PIN_GLOBAL) && !i915_vma_is_ggtt(vma));
 
-       if (WARN_ON((flags & (PIN_MAPPABLE | PIN_GLOBAL)) == PIN_MAPPABLE))
-               return -EINVAL;
+       if (WARN_ON(bound & I915_VMA_PIN_OVERFLOW)) {
+               ret = -EBUSY;
+               goto err;
+       }
 
-       if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view))
-               return -EINVAL;
+       if ((bound & I915_VMA_BIND_MASK) == 0) {
+               ret = i915_vma_insert(vma, size, alignment, flags);
+               if (ret)
+                       goto err;
+       }
 
-       vma = ggtt_view ? i915_gem_obj_to_ggtt_view(obj, ggtt_view) :
-                         i915_gem_obj_to_vma(obj, vm);
+       ret = i915_vma_bind(vma, vma->obj->cache_level, flags);
+       if (ret)
+               goto err;
 
-       if (vma) {
-               if (WARN_ON(vma->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
-                       return -EBUSY;
+       if ((bound ^ vma->flags) & I915_VMA_GLOBAL_BIND)
+               __i915_vma_set_map_and_fenceable(vma);
 
-               if (i915_vma_misplaced(vma, alignment, flags)) {
-                       WARN(vma->pin_count,
-                            "bo is already pinned in %s with incorrect alignment:"
-                            " offset=%08x %08x, req.alignment=%x, req.map_and_fenceable=%d,"
-                            " obj->map_and_fenceable=%d\n",
-                            ggtt_view ? "ggtt" : "ppgtt",
-                            upper_32_bits(vma->node.start),
-                            lower_32_bits(vma->node.start),
-                            alignment,
-                            !!(flags & PIN_MAPPABLE),
-                            obj->map_and_fenceable);
-                       ret = i915_vma_unbind(vma);
-                       if (ret)
-                               return ret;
+       GEM_BUG_ON(i915_vma_misplaced(vma, size, alignment, flags));
+       return 0;
 
-                       vma = NULL;
-               }
-       }
+err:
+       __i915_vma_unpin(vma);
+       return ret;
+}
 
-       bound = vma ? vma->bound : 0;
-       if (vma == NULL || !drm_mm_node_allocated(&vma->node)) {
-               vma = i915_gem_object_bind_to_vm(obj, vm, ggtt_view, alignment,
-                                                flags);
-               if (IS_ERR(vma))
-                       return PTR_ERR(vma);
-       } else {
-               ret = i915_vma_bind(vma, obj->cache_level, flags);
+struct i915_vma *
+i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
+                        const struct i915_ggtt_view *view,
+                        u64 size,
+                        u64 alignment,
+                        u64 flags)
+{
+       struct i915_address_space *vm = &to_i915(obj->base.dev)->ggtt.base;
+       struct i915_vma *vma;
+       int ret;
+
+       vma = i915_gem_obj_lookup_or_create_vma(obj, vm, view);
+       if (IS_ERR(vma))
+               return vma;
+
+       if (i915_vma_misplaced(vma, size, alignment, flags)) {
+               if (flags & PIN_NONBLOCK &&
+                   (i915_vma_is_pinned(vma) || i915_vma_is_active(vma)))
+                       return ERR_PTR(-ENOSPC);
+
+               WARN(i915_vma_is_pinned(vma),
+                    "bo is already pinned in ggtt with incorrect alignment:"
+                    " offset=%08x, req.alignment=%llx,"
+                    " req.map_and_fenceable=%d, vma->map_and_fenceable=%d\n",
+                    i915_ggtt_offset(vma), alignment,
+                    !!(flags & PIN_MAPPABLE),
+                    i915_vma_is_map_and_fenceable(vma));
+               ret = i915_vma_unbind(vma);
                if (ret)
-                       return ret;
+                       return ERR_PTR(ret);
        }
 
-       if (ggtt_view && ggtt_view->type == I915_GGTT_VIEW_NORMAL &&
-           (bound ^ vma->bound) & GLOBAL_BIND) {
-               __i915_vma_set_map_and_fenceable(vma);
-               WARN_ON(flags & PIN_MAPPABLE && !obj->map_and_fenceable);
-       }
+       ret = i915_vma_pin(vma, size, alignment, flags | PIN_GLOBAL);
+       if (ret)
+               return ERR_PTR(ret);
 
-       vma->pin_count++;
-       return 0;
+       return vma;
 }
 
-int
-i915_gem_object_pin(struct drm_i915_gem_object *obj,
-                   struct i915_address_space *vm,
-                   uint32_t alignment,
-                   uint64_t flags)
+static __always_inline unsigned int __busy_read_flag(unsigned int id)
 {
-       return i915_gem_object_do_pin(obj, vm,
-                                     i915_is_ggtt(vm) ? &i915_ggtt_view_normal : NULL,
-                                     alignment, flags);
+       /* Note that we could alias engines in the execbuf API, but
+        * that would be very unwise as it prevents userspace from
+        * fine control over engine selection. Ahem.
+        *
+        * This should be something like EXEC_MAX_ENGINE instead of
+        * I915_NUM_ENGINES.
+        */
+       BUILD_BUG_ON(I915_NUM_ENGINES > 16);
+       return 0x10000 << id;
 }
 
-int
-i915_gem_object_ggtt_pin(struct drm_i915_gem_object *obj,
-                        const struct i915_ggtt_view *view,
-                        uint32_t alignment,
-                        uint64_t flags)
+static __always_inline unsigned int __busy_write_id(unsigned int id)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
+       /* The uABI guarantees an active writer is also amongst the read
+        * engines. This would be true if we accessed the activity tracking
+        * under the lock, but as we perform the lookup of the object and
+        * its activity locklessly we can not guarantee that the last_write
+        * being active implies that we have set the same engine flag from
+        * last_read - hence we always set both read and write busy for
+        * last_write.
+        */
+       return id | __busy_read_flag(id);
+}
+
+static __always_inline unsigned int
+__busy_set_if_active(const struct i915_gem_active *active,
+                    unsigned int (*flag)(unsigned int id))
+{
+       struct drm_i915_gem_request *request;
 
-       BUG_ON(!view);
+       request = rcu_dereference(active->request);
+       if (!request || i915_gem_request_completed(request))
+               return 0;
 
-       return i915_gem_object_do_pin(obj, &ggtt->base, view,
-                                     alignment, flags | PIN_GLOBAL);
+       /* This is racy. See __i915_gem_active_get_rcu() for an in detail
+        * discussion of how to handle the race correctly, but for reporting
+        * the busy state we err on the side of potentially reporting the
+        * wrong engine as being busy (but we guarantee that the result
+        * is at least self-consistent).
+        *
+        * As we use SLAB_DESTROY_BY_RCU, the request may be reallocated
+        * whilst we are inspecting it, even under the RCU read lock as we are.
+        * This means that there is a small window for the engine and/or the
+        * seqno to have been overwritten. The seqno will always be in the
+        * future compared to the intended, and so we know that if that
+        * seqno is idle (on whatever engine) our request is idle and the
+        * return 0 above is correct.
+        *
+        * The issue is that if the engine is switched, it is just as likely
+        * to report that it is busy (but since the switch happened, we know
+        * the request should be idle). So there is a small chance that a busy
+        * result is actually the wrong engine.
+        *
+        * So why don't we care?
+        *
+        * For starters, the busy ioctl is a heuristic that is by definition
+        * racy. Even with perfect serialisation in the driver, the hardware
+        * state is constantly advancing - the state we report to the user
+        * is stale.
+        *
+        * The critical information for the busy-ioctl is whether the object
+        * is idle as userspace relies on that to detect whether its next
+        * access will stall, or if it has missed submitting commands to
+        * the hardware allowing the GPU to stall. We never generate a
+        * false-positive for idleness, thus busy-ioctl is reliable at the
+        * most fundamental level, and we maintain the guarantee that a
+        * busy object left to itself will eventually become idle (and stay
+        * idle!).
+        *
+        * We allow ourselves the leeway of potentially misreporting the busy
+        * state because that is an optimisation heuristic that is constantly
+        * in flux. Being quickly able to detect the busy/idle state is much
+        * more important than accurate logging of exactly which engines were
+        * busy.
+        *
+        * For accuracy in reporting the engine, we could use
+        *
+        *      result = 0;
+        *      request = __i915_gem_active_get_rcu(active);
+        *      if (request) {
+        *              if (!i915_gem_request_completed(request))
+        *                      result = flag(request->engine->exec_id);
+        *              i915_gem_request_put(request);
+        *      }
+        *
+        * but that still remains susceptible to both hardware and userspace
+        * races. So we accept making the result of that race slightly worse,
+        * given the rarity of the race and its low impact on the result.
+        */
+       return flag(READ_ONCE(request->engine->exec_id));
 }
 
-void
-i915_gem_object_ggtt_unpin_view(struct drm_i915_gem_object *obj,
-                               const struct i915_ggtt_view *view)
+static __always_inline unsigned int
+busy_check_reader(const struct i915_gem_active *active)
 {
-       struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view);
-
-       WARN_ON(vma->pin_count == 0);
-       WARN_ON(!i915_gem_obj_ggtt_bound_view(obj, view));
+       return __busy_set_if_active(active, __busy_read_flag);
+}
 
-       --vma->pin_count;
+static __always_inline unsigned int
+busy_check_writer(const struct i915_gem_active *active)
+{
+       return __busy_set_if_active(active, __busy_write_id);
 }
 
 int
@@ -4645,47 +3937,64 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
 {
        struct drm_i915_gem_busy *args = data;
        struct drm_i915_gem_object *obj;
-       int ret;
+       unsigned long active;
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               return ret;
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
+               return -ENOENT;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL) {
-               ret = -ENOENT;
-               goto unlock;
-       }
+       args->busy = 0;
+       active = __I915_BO_ACTIVE(obj);
+       if (active) {
+               int idx;
 
-       /* Count all active objects as busy, even if they are currently not used
-        * by the gpu. Users of this interface expect objects to eventually
-        * become non-busy without any further actions, therefore emit any
-        * necessary flushes here.
-        */
-       ret = i915_gem_object_flush_active(obj);
-       if (ret)
-               goto unref;
+               /* Yes, the lookups are intentionally racy.
+                *
+                * First, we cannot simply rely on __I915_BO_ACTIVE. We have
+                * to regard the value as stale and as our ABI guarantees
+                * forward progress, we confirm the status of each active
+                * request with the hardware.
+                *
+                * Even though we guard the pointer lookup by RCU, that only
+                * guarantees that the pointer and its contents remain
+                * dereferencable and does *not* mean that the request we
+                * have is the same as the one being tracked by the object.
+                *
+                * Consider that we lookup the request just as it is being
+                * retired and freed. We take a local copy of the pointer,
+                * but before we add its engine into the busy set, the other
+                * thread reallocates it and assigns it to a task on another
+                * engine with a fresh and incomplete seqno. Guarding against
+                * that requires careful serialisation and reference counting,
+                * i.e. using __i915_gem_active_get_request_rcu(). We don't,
+                * instead we expect that if the result is busy, which engines
+                * are busy is not completely reliable - we only guarantee
+                * that the object was busy.
+                */
+               rcu_read_lock();
 
-       args->busy = 0;
-       if (obj->active) {
-               int i;
+               for_each_active(active, idx)
+                       args->busy |= busy_check_reader(&obj->last_read[idx]);
 
-               for (i = 0; i < I915_NUM_ENGINES; i++) {
-                       struct drm_i915_gem_request *req;
+               /* For ABI sanity, we only care that the write engine is in
+                * the set of read engines. This should be ensured by the
+                * ordering of setting last_read/last_write in
+                * i915_vma_move_to_active(), and then in reverse in retire.
+                * However, for good measure, we always report the last_write
+                * request as a busy read as well as being a busy write.
+                *
+                * We don't care that the set of active read/write engines
+                * may change during construction of the result, as it is
+                * equally liable to change before userspace can inspect
+                * the result.
+                */
+               args->busy |= busy_check_writer(&obj->last_write);
 
-                       req = obj->last_read_req[i];
-                       if (req)
-                               args->busy |= 1 << (16 + req->engine->exec_id);
-               }
-               if (obj->last_write_req)
-                       args->busy |= obj->last_write_req->engine->exec_id;
+               rcu_read_unlock();
        }
 
-unref:
-       drm_gem_object_unreference(&obj->base);
-unlock:
-       mutex_unlock(&dev->struct_mutex);
-       return ret;
+       i915_gem_object_put_unlocked(obj);
+       return 0;
 }
 
 int
@@ -4716,19 +4025,14 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
        if (ret)
                return ret;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file_priv, args->handle));
-       if (&obj->base == NULL) {
+       obj = i915_gem_object_lookup(file_priv, args->handle);
+       if (!obj) {
                ret = -ENOENT;
                goto unlock;
        }
 
-       if (i915_gem_obj_is_pinned(obj)) {
-               ret = -EINVAL;
-               goto out;
-       }
-
        if (obj->pages &&
-           obj->tiling_mode != I915_TILING_NONE &&
+           i915_gem_object_is_tiled(obj) &&
            dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
                if (obj->madv == I915_MADV_WILLNEED)
                        i915_gem_object_unpin_pages(obj);
@@ -4745,8 +4049,7 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
 
        args->retained = obj->madv != __I915_MADV_PURGED;
 
-out:
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
 unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
@@ -4759,14 +4062,17 @@ void i915_gem_object_init(struct drm_i915_gem_object *obj,
 
        INIT_LIST_HEAD(&obj->global_list);
        for (i = 0; i < I915_NUM_ENGINES; i++)
-               INIT_LIST_HEAD(&obj->engine_list[i]);
+               init_request_active(&obj->last_read[i],
+                                   i915_gem_object_retire__read);
+       init_request_active(&obj->last_write,
+                           i915_gem_object_retire__write);
        INIT_LIST_HEAD(&obj->obj_exec_link);
        INIT_LIST_HEAD(&obj->vma_list);
        INIT_LIST_HEAD(&obj->batch_pool_link);
 
        obj->ops = ops;
 
-       obj->fence_reg = I915_FENCE_REG_NONE;
+       obj->frontbuffer_ggtt_origin = ORIGIN_GTT;
        obj->madv = I915_MADV_WILLNEED;
 
        i915_gem_info_add_obj(to_i915(obj->base.dev), obj->base.size);
@@ -4871,33 +4177,31 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
 
        trace_i915_gem_object_destroy(obj);
 
+       /* All file-owned VMA should have been released by this point through
+        * i915_gem_close_object(), or earlier by i915_gem_context_close().
+        * However, the object may also be bound into the global GTT (e.g.
+        * older GPUs without per-process support, or for direct access through
+        * the GTT either for the user or for scanout). Those VMA still need to
+        * unbound now.
+        */
        list_for_each_entry_safe(vma, next, &obj->vma_list, obj_link) {
-               int ret;
-
-               vma->pin_count = 0;
-               ret = i915_vma_unbind(vma);
-               if (WARN_ON(ret == -ERESTARTSYS)) {
-                       bool was_interruptible;
-
-                       was_interruptible = dev_priv->mm.interruptible;
-                       dev_priv->mm.interruptible = false;
-
-                       WARN_ON(i915_vma_unbind(vma));
-
-                       dev_priv->mm.interruptible = was_interruptible;
-               }
+               GEM_BUG_ON(!i915_vma_is_ggtt(vma));
+               GEM_BUG_ON(i915_vma_is_active(vma));
+               vma->flags &= ~I915_VMA_PIN_MASK;
+               i915_vma_close(vma);
        }
+       GEM_BUG_ON(obj->bind_count);
 
        /* Stolen objects don't hold a ref, but do hold pin count. Fix that up
         * before progressing. */
        if (obj->stolen)
                i915_gem_object_unpin_pages(obj);
 
-       WARN_ON(obj->frontbuffer_bits);
+       WARN_ON(atomic_read(&obj->frontbuffer_bits));
 
        if (obj->pages && obj->madv == I915_MADV_WILLNEED &&
            dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES &&
-           obj->tiling_mode != I915_TILING_NONE)
+           i915_gem_object_is_tiled(obj))
                i915_gem_object_unpin_pages(obj);
 
        if (WARN_ON(obj->pages_pin_count))
@@ -4905,7 +4209,6 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        if (discard_backing_storage(obj))
                obj->madv = I915_MADV_DONTNEED;
        i915_gem_object_put_pages(obj);
-       i915_gem_object_free_mmap_offset(obj);
 
        BUG_ON(obj->pages);
 
@@ -4924,71 +4227,35 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj)
        intel_runtime_pm_put(dev_priv);
 }
 
-struct i915_vma *i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
-                                    struct i915_address_space *vm)
-{
-       struct i915_vma *vma;
-       list_for_each_entry(vma, &obj->vma_list, obj_link) {
-               if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL &&
-                   vma->vm == vm)
-                       return vma;
-       }
-       return NULL;
-}
-
-struct i915_vma *i915_gem_obj_to_ggtt_view(struct drm_i915_gem_object *obj,
-                                          const struct i915_ggtt_view *view)
-{
-       struct i915_vma *vma;
-
-       GEM_BUG_ON(!view);
-
-       list_for_each_entry(vma, &obj->vma_list, obj_link)
-               if (vma->is_ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view))
-                       return vma;
-       return NULL;
-}
-
-void i915_gem_vma_destroy(struct i915_vma *vma)
-{
-       WARN_ON(vma->node.allocated);
-
-       /* Keep the vma as a placeholder in the execbuffer reservation lists */
-       if (!list_empty(&vma->exec_list))
-               return;
-
-       if (!vma->is_ggtt)
-               i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm));
-
-       list_del(&vma->obj_link);
-
-       kmem_cache_free(to_i915(vma->obj->base.dev)->vmas, vma);
-}
-
-static void
-i915_gem_stop_engines(struct drm_device *dev)
+int i915_gem_suspend(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine;
-
-       for_each_engine(engine, dev_priv)
-               dev_priv->gt.stop_engine(engine);
-}
+       int ret;
 
-int
-i915_gem_suspend(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret = 0;
+       intel_suspend_gt_powersave(dev_priv);
 
        mutex_lock(&dev->struct_mutex);
-       ret = i915_gem_wait_for_idle(dev_priv);
+
+       /* We have to flush all the executing contexts to main memory so
+        * that they can saved in the hibernation image. To ensure the last
+        * context image is coherent, we have to switch away from it. That
+        * leaves the dev_priv->kernel_context still active when
+        * we actually suspend, and its image in memory may not match the GPU
+        * state. Fortunately, the kernel_context is disposable and we do
+        * not rely on its state.
+        */
+       ret = i915_gem_switch_to_kernel_context(dev_priv);
+       if (ret)
+               goto err;
+
+       ret = i915_gem_wait_for_idle(dev_priv,
+                                    I915_WAIT_INTERRUPTIBLE |
+                                    I915_WAIT_LOCKED);
        if (ret)
                goto err;
 
        i915_gem_retire_requests(dev_priv);
 
-       i915_gem_stop_engines(dev);
        i915_gem_context_lost(dev_priv);
        mutex_unlock(&dev->struct_mutex);
 
@@ -5008,6 +4275,22 @@ err:
        return ret;
 }
 
+void i915_gem_resume(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+
+       mutex_lock(&dev->struct_mutex);
+       i915_gem_restore_gtt_mappings(dev);
+
+       /* As we didn't flush the kernel context before suspend, we cannot
+        * guarantee that the context image is complete. So let's just reset
+        * it and start again.
+        */
+       dev_priv->gt.resume(dev_priv);
+
+       mutex_unlock(&dev->struct_mutex);
+}
+
 void i915_gem_init_swizzling(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -5060,53 +4343,6 @@ static void init_unused_rings(struct drm_device *dev)
        }
 }
 
-int i915_gem_init_engines(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
-
-       ret = intel_init_render_ring_buffer(dev);
-       if (ret)
-               return ret;
-
-       if (HAS_BSD(dev)) {
-               ret = intel_init_bsd_ring_buffer(dev);
-               if (ret)
-                       goto cleanup_render_ring;
-       }
-
-       if (HAS_BLT(dev)) {
-               ret = intel_init_blt_ring_buffer(dev);
-               if (ret)
-                       goto cleanup_bsd_ring;
-       }
-
-       if (HAS_VEBOX(dev)) {
-               ret = intel_init_vebox_ring_buffer(dev);
-               if (ret)
-                       goto cleanup_blt_ring;
-       }
-
-       if (HAS_BSD2(dev)) {
-               ret = intel_init_bsd2_ring_buffer(dev);
-               if (ret)
-                       goto cleanup_vebox_ring;
-       }
-
-       return 0;
-
-cleanup_vebox_ring:
-       intel_cleanup_engine(&dev_priv->engine[VECS]);
-cleanup_blt_ring:
-       intel_cleanup_engine(&dev_priv->engine[BCS]);
-cleanup_bsd_ring:
-       intel_cleanup_engine(&dev_priv->engine[VCS]);
-cleanup_render_ring:
-       intel_cleanup_engine(&dev_priv->engine[RCS]);
-
-       return ret;
-}
-
 int
 i915_gem_init_hw(struct drm_device *dev)
 {
@@ -5173,6 +4409,27 @@ out:
        return ret;
 }
 
+bool intel_sanitize_semaphores(struct drm_i915_private *dev_priv, int value)
+{
+       if (INTEL_INFO(dev_priv)->gen < 6)
+               return false;
+
+       /* TODO: make semaphores and Execlists play nicely together */
+       if (i915.enable_execlists)
+               return false;
+
+       if (value >= 0)
+               return value;
+
+#ifdef CONFIG_INTEL_IOMMU
+       /* Enable semaphores on SNB when IO remapping is off */
+       if (INTEL_INFO(dev_priv)->gen == 6 && intel_iommu_gfx_mapped)
+               return false;
+#endif
+
+       return true;
+}
+
 int i915_gem_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -5181,15 +4438,11 @@ int i915_gem_init(struct drm_device *dev)
        mutex_lock(&dev->struct_mutex);
 
        if (!i915.enable_execlists) {
-               dev_priv->gt.execbuf_submit = i915_gem_ringbuffer_submission;
-               dev_priv->gt.init_engines = i915_gem_init_engines;
-               dev_priv->gt.cleanup_engine = intel_cleanup_engine;
-               dev_priv->gt.stop_engine = intel_stop_engine;
+               dev_priv->gt.resume = intel_legacy_submission_resume;
+               dev_priv->gt.cleanup_engine = intel_engine_cleanup;
        } else {
-               dev_priv->gt.execbuf_submit = intel_execlists_submission;
-               dev_priv->gt.init_engines = intel_logical_rings_init;
+               dev_priv->gt.resume = intel_lr_context_resume;
                dev_priv->gt.cleanup_engine = intel_logical_ring_cleanup;
-               dev_priv->gt.stop_engine = intel_logical_ring_stop;
        }
 
        /* This is just a security blanket to placate dragons.
@@ -5201,24 +4454,27 @@ int i915_gem_init(struct drm_device *dev)
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
        i915_gem_init_userptr(dev_priv);
-       i915_gem_init_ggtt(dev);
+
+       ret = i915_gem_init_ggtt(dev_priv);
+       if (ret)
+               goto out_unlock;
 
        ret = i915_gem_context_init(dev);
        if (ret)
                goto out_unlock;
 
-       ret = dev_priv->gt.init_engines(dev);
+       ret = intel_engines_init(dev);
        if (ret)
                goto out_unlock;
 
        ret = i915_gem_init_hw(dev);
        if (ret == -EIO) {
-               /* Allow ring initialisation to fail by marking the GPU as
+               /* Allow engine initialisation to fail by marking the GPU as
                 * wedged. But we only want to do this where the GPU is angry,
                 * for all other failure, such as an allocation failure, bail.
                 */
                DRM_ERROR("Failed to initialize GPU, declaring it wedged\n");
-               atomic_or(I915_WEDGED, &dev_priv->gpu_error.reset_counter);
+               i915_gem_set_wedged(dev_priv);
                ret = 0;
        }
 
@@ -5242,7 +4498,6 @@ i915_gem_cleanup_engines(struct drm_device *dev)
 static void
 init_engine_lists(struct intel_engine_cs *engine)
 {
-       INIT_LIST_HEAD(&engine->active_list);
        INIT_LIST_HEAD(&engine->request_list);
 }
 
@@ -5250,6 +4505,7 @@ void
 i915_gem_load_init_fences(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = &dev_priv->drm;
+       int i;
 
        if (INTEL_INFO(dev_priv)->gen >= 7 && !IS_VALLEYVIEW(dev_priv) &&
            !IS_CHERRYVIEW(dev_priv))
@@ -5265,6 +4521,13 @@ i915_gem_load_init_fences(struct drm_i915_private *dev_priv)
                                I915_READ(vgtif_reg(avail_rs.fence_num));
 
        /* Initialize fence registers to zero */
+       for (i = 0; i < dev_priv->num_fence_regs; i++) {
+               struct drm_i915_fence_reg *fence = &dev_priv->fence_regs[i];
+
+               fence->i915 = dev_priv;
+               fence->id = i;
+               list_add_tail(&fence->link, &dev_priv->mm.fence_list);
+       }
        i915_gem_restore_fences(dev);
 
        i915_gem_detect_bit_6_swizzle(dev);
@@ -5289,18 +4552,17 @@ i915_gem_load_init(struct drm_device *dev)
        dev_priv->requests =
                kmem_cache_create("i915_gem_request",
                                  sizeof(struct drm_i915_gem_request), 0,
-                                 SLAB_HWCACHE_ALIGN,
+                                 SLAB_HWCACHE_ALIGN |
+                                 SLAB_RECLAIM_ACCOUNT |
+                                 SLAB_DESTROY_BY_RCU,
                                  NULL);
 
-       INIT_LIST_HEAD(&dev_priv->vm_list);
        INIT_LIST_HEAD(&dev_priv->context_list);
        INIT_LIST_HEAD(&dev_priv->mm.unbound_list);
        INIT_LIST_HEAD(&dev_priv->mm.bound_list);
        INIT_LIST_HEAD(&dev_priv->mm.fence_list);
        for (i = 0; i < I915_NUM_ENGINES; i++)
                init_engine_lists(&dev_priv->engine[i]);
-       for (i = 0; i < I915_MAX_NUM_FENCES; i++)
-               INIT_LIST_HEAD(&dev_priv->fence_regs[i].lru_list);
        INIT_DELAYED_WORK(&dev_priv->gt.retire_work,
                          i915_gem_retire_work_handler);
        INIT_DELAYED_WORK(&dev_priv->gt.idle_work,
@@ -5310,13 +4572,13 @@ i915_gem_load_init(struct drm_device *dev)
 
        dev_priv->relative_constants_mode = I915_EXEC_CONSTANTS_REL_GENERAL;
 
-       INIT_LIST_HEAD(&dev_priv->mm.fence_list);
-
        init_waitqueue_head(&dev_priv->pending_flip_queue);
 
        dev_priv->mm.interruptible = true;
 
-       mutex_init(&dev_priv->fb_tracking.lock);
+       atomic_set(&dev_priv->mm.bsd_engine_dispatch_index, 0);
+
+       spin_lock_init(&dev_priv->fb_tracking.lock);
 }
 
 void i915_gem_load_cleanup(struct drm_device *dev)
@@ -5326,11 +4588,32 @@ void i915_gem_load_cleanup(struct drm_device *dev)
        kmem_cache_destroy(dev_priv->requests);
        kmem_cache_destroy(dev_priv->vmas);
        kmem_cache_destroy(dev_priv->objects);
+
+       /* And ensure that our DESTROY_BY_RCU slabs are truly destroyed */
+       rcu_barrier();
+}
+
+int i915_gem_freeze(struct drm_i915_private *dev_priv)
+{
+       intel_runtime_pm_get(dev_priv);
+
+       mutex_lock(&dev_priv->drm.struct_mutex);
+       i915_gem_shrink_all(dev_priv);
+       mutex_unlock(&dev_priv->drm.struct_mutex);
+
+       intel_runtime_pm_put(dev_priv);
+
+       return 0;
 }
 
 int i915_gem_freeze_late(struct drm_i915_private *dev_priv)
 {
        struct drm_i915_gem_object *obj;
+       struct list_head *phases[] = {
+               &dev_priv->mm.unbound_list,
+               &dev_priv->mm.bound_list,
+               NULL
+       }, **p;
 
        /* Called just before we write the hibernation image.
         *
@@ -5341,17 +4624,21 @@ int i915_gem_freeze_late(struct drm_i915_private *dev_priv)
         *
         * To make sure the hibernation image contains the latest state,
         * we update that state just before writing out the image.
+        *
+        * To try and reduce the hibernation image, we manually shrink
+        * the objects as well.
         */
 
-       list_for_each_entry(obj, &dev_priv->mm.unbound_list, global_list) {
-               obj->base.read_domains = I915_GEM_DOMAIN_CPU;
-               obj->base.write_domain = I915_GEM_DOMAIN_CPU;
-       }
+       mutex_lock(&dev_priv->drm.struct_mutex);
+       i915_gem_shrink(dev_priv, -1UL, I915_SHRINK_UNBOUND);
 
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               obj->base.read_domains = I915_GEM_DOMAIN_CPU;
-               obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+       for (p = phases; *p; p++) {
+               list_for_each_entry(obj, *p, global_list) {
+                       obj->base.read_domains = I915_GEM_DOMAIN_CPU;
+                       obj->base.write_domain = I915_GEM_DOMAIN_CPU;
+               }
        }
+       mutex_unlock(&dev_priv->drm.struct_mutex);
 
        return 0;
 }
@@ -5359,21 +4646,15 @@ int i915_gem_freeze_late(struct drm_i915_private *dev_priv)
 void i915_gem_release(struct drm_device *dev, struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
+       struct drm_i915_gem_request *request;
 
        /* Clean up our request list when the client is going away, so that
         * later retire_requests won't dereference our soon-to-be-gone
         * file_priv.
         */
        spin_lock(&file_priv->mm.lock);
-       while (!list_empty(&file_priv->mm.request_list)) {
-               struct drm_i915_gem_request *request;
-
-               request = list_first_entry(&file_priv->mm.request_list,
-                                          struct drm_i915_gem_request,
-                                          client_list);
-               list_del(&request->client_list);
+       list_for_each_entry(request, &file_priv->mm.request_list, client_list)
                request->file_priv = NULL;
-       }
        spin_unlock(&file_priv->mm.lock);
 
        if (!list_empty(&file_priv->rps.link)) {
@@ -5402,7 +4683,7 @@ int i915_gem_open(struct drm_device *dev, struct drm_file *file)
        spin_lock_init(&file_priv->mm.lock);
        INIT_LIST_HEAD(&file_priv->mm.request_list);
 
-       file_priv->bsd_ring = -1;
+       file_priv->bsd_engine = -1;
 
        ret = i915_gem_context_open(dev, file);
        if (ret)
@@ -5424,118 +4705,24 @@ void i915_gem_track_fb(struct drm_i915_gem_object *old,
                       struct drm_i915_gem_object *new,
                       unsigned frontbuffer_bits)
 {
+       /* Control of individual bits within the mask are guarded by
+        * the owning plane->mutex, i.e. we can never see concurrent
+        * manipulation of individual bits. But since the bitfield as a whole
+        * is updated using RMW, we need to use atomics in order to update
+        * the bits.
+        */
+       BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES >
+                    sizeof(atomic_t) * BITS_PER_BYTE);
+
        if (old) {
-               WARN_ON(!mutex_is_locked(&old->base.dev->struct_mutex));
-               WARN_ON(!(old->frontbuffer_bits & frontbuffer_bits));
-               old->frontbuffer_bits &= ~frontbuffer_bits;
+               WARN_ON(!(atomic_read(&old->frontbuffer_bits) & frontbuffer_bits));
+               atomic_andnot(frontbuffer_bits, &old->frontbuffer_bits);
        }
 
        if (new) {
-               WARN_ON(!mutex_is_locked(&new->base.dev->struct_mutex));
-               WARN_ON(new->frontbuffer_bits & frontbuffer_bits);
-               new->frontbuffer_bits |= frontbuffer_bits;
-       }
-}
-
-/* All the new VM stuff */
-u64 i915_gem_obj_offset(struct drm_i915_gem_object *o,
-                       struct i915_address_space *vm)
-{
-       struct drm_i915_private *dev_priv = to_i915(o->base.dev);
-       struct i915_vma *vma;
-
-       WARN_ON(vm == &dev_priv->mm.aliasing_ppgtt->base);
-
-       list_for_each_entry(vma, &o->vma_list, obj_link) {
-               if (vma->is_ggtt &&
-                   vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
-                       continue;
-               if (vma->vm == vm)
-                       return vma->node.start;
-       }
-
-       WARN(1, "%s vma for this object not found.\n",
-            i915_is_ggtt(vm) ? "global" : "ppgtt");
-       return -1;
-}
-
-u64 i915_gem_obj_ggtt_offset_view(struct drm_i915_gem_object *o,
-                                 const struct i915_ggtt_view *view)
-{
-       struct i915_vma *vma;
-
-       list_for_each_entry(vma, &o->vma_list, obj_link)
-               if (vma->is_ggtt && i915_ggtt_view_equal(&vma->ggtt_view, view))
-                       return vma->node.start;
-
-       WARN(1, "global vma for this object not found. (view=%u)\n", view->type);
-       return -1;
-}
-
-bool i915_gem_obj_bound(struct drm_i915_gem_object *o,
-                       struct i915_address_space *vm)
-{
-       struct i915_vma *vma;
-
-       list_for_each_entry(vma, &o->vma_list, obj_link) {
-               if (vma->is_ggtt &&
-                   vma->ggtt_view.type != I915_GGTT_VIEW_NORMAL)
-                       continue;
-               if (vma->vm == vm && drm_mm_node_allocated(&vma->node))
-                       return true;
+               WARN_ON(atomic_read(&new->frontbuffer_bits) & frontbuffer_bits);
+               atomic_or(frontbuffer_bits, &new->frontbuffer_bits);
        }
-
-       return false;
-}
-
-bool i915_gem_obj_ggtt_bound_view(struct drm_i915_gem_object *o,
-                                 const struct i915_ggtt_view *view)
-{
-       struct i915_vma *vma;
-
-       list_for_each_entry(vma, &o->vma_list, obj_link)
-               if (vma->is_ggtt &&
-                   i915_ggtt_view_equal(&vma->ggtt_view, view) &&
-                   drm_mm_node_allocated(&vma->node))
-                       return true;
-
-       return false;
-}
-
-bool i915_gem_obj_bound_any(struct drm_i915_gem_object *o)
-{
-       struct i915_vma *vma;
-
-       list_for_each_entry(vma, &o->vma_list, obj_link)
-               if (drm_mm_node_allocated(&vma->node))
-                       return true;
-
-       return false;
-}
-
-unsigned long i915_gem_obj_ggtt_size(struct drm_i915_gem_object *o)
-{
-       struct i915_vma *vma;
-
-       GEM_BUG_ON(list_empty(&o->vma_list));
-
-       list_for_each_entry(vma, &o->vma_list, obj_link) {
-               if (vma->is_ggtt &&
-                   vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL)
-                       return vma->node.size;
-       }
-
-       return 0;
-}
-
-bool i915_gem_obj_is_pinned(struct drm_i915_gem_object *obj)
-{
-       struct i915_vma *vma;
-       list_for_each_entry(vma, &obj->vma_list, obj_link)
-               if (vma->pin_count > 0)
-                       return true;
-
-       return false;
 }
 
 /* Like i915_gem_object_get_page(), but mark the returned page dirty */
@@ -5590,6 +4777,6 @@ i915_gem_object_create_from_data(struct drm_device *dev,
        return obj;
 
 fail:
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        return ERR_PTR(ret);
 }
index 3752d5d..ed98959 100644 (file)
 
 /**
  * i915_gem_batch_pool_init() - initialize a batch buffer pool
- * @dev: the drm device
+ * @engine: the associated request submission engine
  * @pool: the batch buffer pool
  */
-void i915_gem_batch_pool_init(struct drm_device *dev,
+void i915_gem_batch_pool_init(struct intel_engine_cs *engine,
                              struct i915_gem_batch_pool *pool)
 {
        int n;
 
-       pool->dev = dev;
+       pool->engine = engine;
 
        for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++)
                INIT_LIST_HEAD(&pool->cache_list[n]);
@@ -65,18 +65,17 @@ void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool)
 {
        int n;
 
-       WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex));
+       lockdep_assert_held(&pool->engine->i915->drm.struct_mutex);
 
        for (n = 0; n < ARRAY_SIZE(pool->cache_list); n++) {
-               while (!list_empty(&pool->cache_list[n])) {
-                       struct drm_i915_gem_object *obj =
-                               list_first_entry(&pool->cache_list[n],
-                                                struct drm_i915_gem_object,
-                                                batch_pool_link);
-
-                       list_del(&obj->batch_pool_link);
-                       drm_gem_object_unreference(&obj->base);
-               }
+               struct drm_i915_gem_object *obj, *next;
+
+               list_for_each_entry_safe(obj, next,
+                                        &pool->cache_list[n],
+                                        batch_pool_link)
+                       i915_gem_object_put(obj);
+
+               INIT_LIST_HEAD(&pool->cache_list[n]);
        }
 }
 
@@ -102,7 +101,7 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool,
        struct list_head *list;
        int n;
 
-       WARN_ON(!mutex_is_locked(&pool->dev->struct_mutex));
+       lockdep_assert_held(&pool->engine->i915->drm.struct_mutex);
 
        /* Compute a power-of-two bucket, but throw everything greater than
         * 16KiB into the same bucket: i.e. the the buckets hold objects of
@@ -115,13 +114,14 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool,
 
        list_for_each_entry_safe(tmp, next, list, batch_pool_link) {
                /* The batches are strictly LRU ordered */
-               if (tmp->active)
+               if (!i915_gem_active_is_idle(&tmp->last_read[pool->engine->id],
+                                            &tmp->base.dev->struct_mutex))
                        break;
 
                /* While we're looping, do some clean up */
                if (tmp->madv == __I915_MADV_PURGED) {
                        list_del(&tmp->batch_pool_link);
-                       drm_gem_object_unreference(&tmp->base);
+                       i915_gem_object_put(tmp);
                        continue;
                }
 
@@ -134,7 +134,7 @@ i915_gem_batch_pool_get(struct i915_gem_batch_pool *pool,
        if (obj == NULL) {
                int ret;
 
-               obj = i915_gem_object_create(pool->dev, size);
+               obj = i915_gem_object_create(&pool->engine->i915->drm, size);
                if (IS_ERR(obj))
                        return obj;
 
index 848e907..10d5ac4 100644 (file)
 
 #include "i915_drv.h"
 
+struct intel_engine_cs;
+
 struct i915_gem_batch_pool {
-       struct drm_device *dev;
+       struct intel_engine_cs *engine;
        struct list_head cache_list[4];
 };
 
 /* i915_gem_batch_pool.c */
-void i915_gem_batch_pool_init(struct drm_device *dev,
+void i915_gem_batch_pool_init(struct intel_engine_cs *engine,
                              struct i915_gem_batch_pool *pool);
 void i915_gem_batch_pool_fini(struct i915_gem_batch_pool *pool);
 struct drm_i915_gem_object*
index 3c97f0e..df10f4e 100644 (file)
@@ -134,21 +134,6 @@ static int get_context_size(struct drm_i915_private *dev_priv)
        return ret;
 }
 
-static void i915_gem_context_clean(struct i915_gem_context *ctx)
-{
-       struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
-       struct i915_vma *vma, *next;
-
-       if (!ppgtt)
-               return;
-
-       list_for_each_entry_safe(vma, next, &ppgtt->base.inactive_list,
-                                vm_link) {
-               if (WARN_ON(__i915_vma_unbind_no_wait(vma)))
-                       break;
-       }
-}
-
 void i915_gem_context_free(struct kref *ctx_ref)
 {
        struct i915_gem_context *ctx = container_of(ctx_ref, typeof(*ctx), ref);
@@ -156,13 +141,7 @@ void i915_gem_context_free(struct kref *ctx_ref)
 
        lockdep_assert_held(&ctx->i915->drm.struct_mutex);
        trace_i915_context_free(ctx);
-
-       /*
-        * This context is going away and we need to remove all VMAs still
-        * around. This is to handle imported shared objects for which
-        * destructor did not run when their handles were closed.
-        */
-       i915_gem_context_clean(ctx);
+       GEM_BUG_ON(!ctx->closed);
 
        i915_ppgtt_put(ctx->ppgtt);
 
@@ -173,12 +152,13 @@ void i915_gem_context_free(struct kref *ctx_ref)
                        continue;
 
                WARN_ON(ce->pin_count);
-               if (ce->ringbuf)
-                       intel_ringbuffer_free(ce->ringbuf);
+               if (ce->ring)
+                       intel_ring_free(ce->ring);
 
-               drm_gem_object_unreference(&ce->state->base);
+               i915_vma_put(ce->state);
        }
 
+       put_pid(ctx->pid);
        list_del(&ctx->link);
 
        ida_simple_remove(&ctx->i915->context_hw_ida, ctx->hw_id);
@@ -216,7 +196,7 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size)
                ret = i915_gem_object_set_cache_level(obj, I915_CACHE_L3_LLC);
                /* Failure shouldn't ever happen this early */
                if (WARN_ON(ret)) {
-                       drm_gem_object_unreference(&obj->base);
+                       i915_gem_object_put(obj);
                        return ERR_PTR(ret);
                }
        }
@@ -224,6 +204,37 @@ i915_gem_alloc_context_obj(struct drm_device *dev, size_t size)
        return obj;
 }
 
+static void i915_ppgtt_close(struct i915_address_space *vm)
+{
+       struct list_head *phases[] = {
+               &vm->active_list,
+               &vm->inactive_list,
+               &vm->unbound_list,
+               NULL,
+       }, **phase;
+
+       GEM_BUG_ON(vm->closed);
+       vm->closed = true;
+
+       for (phase = phases; *phase; phase++) {
+               struct i915_vma *vma, *vn;
+
+               list_for_each_entry_safe(vma, vn, *phase, vm_link)
+                       if (!i915_vma_is_closed(vma))
+                               i915_vma_close(vma);
+       }
+}
+
+static void context_close(struct i915_gem_context *ctx)
+{
+       GEM_BUG_ON(ctx->closed);
+       ctx->closed = true;
+       if (ctx->ppgtt)
+               i915_ppgtt_close(&ctx->ppgtt->base);
+       ctx->file_priv = ERR_PTR(-EBADF);
+       i915_gem_context_put(ctx);
+}
+
 static int assign_hw_id(struct drm_i915_private *dev_priv, unsigned *out)
 {
        int ret;
@@ -271,13 +282,24 @@ __create_hw_context(struct drm_device *dev,
        ctx->ggtt_alignment = get_context_alignment(dev_priv);
 
        if (dev_priv->hw_context_size) {
-               struct drm_i915_gem_object *obj =
-                               i915_gem_alloc_context_obj(dev, dev_priv->hw_context_size);
+               struct drm_i915_gem_object *obj;
+               struct i915_vma *vma;
+
+               obj = i915_gem_alloc_context_obj(dev,
+                                                dev_priv->hw_context_size);
                if (IS_ERR(obj)) {
                        ret = PTR_ERR(obj);
                        goto err_out;
                }
-               ctx->engine[RCS].state = obj;
+
+               vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL);
+               if (IS_ERR(vma)) {
+                       i915_gem_object_put(obj);
+                       ret = PTR_ERR(vma);
+                       goto err_out;
+               }
+
+               ctx->engine[RCS].state = vma;
        }
 
        /* Default context will never have a file_priv */
@@ -290,6 +312,9 @@ __create_hw_context(struct drm_device *dev,
                ret = DEFAULT_CONTEXT_HANDLE;
 
        ctx->file_priv = file_priv;
+       if (file_priv)
+               ctx->pid = get_task_pid(current, PIDTYPE_PID);
+
        ctx->user_handle = ret;
        /* NB: Mark all slices as needing a remap so that when the context first
         * loads it will restore whatever remap state already exists. If there
@@ -305,7 +330,7 @@ __create_hw_context(struct drm_device *dev,
        return ctx;
 
 err_out:
-       i915_gem_context_unreference(ctx);
+       context_close(ctx);
        return ERR_PTR(ret);
 }
 
@@ -327,13 +352,14 @@ i915_gem_create_context(struct drm_device *dev,
                return ctx;
 
        if (USES_FULL_PPGTT(dev)) {
-               struct i915_hw_ppgtt *ppgtt = i915_ppgtt_create(dev, file_priv);
+               struct i915_hw_ppgtt *ppgtt =
+                       i915_ppgtt_create(to_i915(dev), file_priv);
 
                if (IS_ERR(ppgtt)) {
                        DRM_DEBUG_DRIVER("PPGTT setup failed (%ld)\n",
                                         PTR_ERR(ppgtt));
                        idr_remove(&file_priv->context_idr, ctx->user_handle);
-                       i915_gem_context_unreference(ctx);
+                       context_close(ctx);
                        return ERR_CAST(ppgtt);
                }
 
@@ -388,28 +414,12 @@ static void i915_gem_context_unpin(struct i915_gem_context *ctx,
                struct intel_context *ce = &ctx->engine[engine->id];
 
                if (ce->state)
-                       i915_gem_object_ggtt_unpin(ce->state);
+                       i915_vma_unpin(ce->state);
 
-               i915_gem_context_unreference(ctx);
+               i915_gem_context_put(ctx);
        }
 }
 
-void i915_gem_context_reset(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       lockdep_assert_held(&dev->struct_mutex);
-
-       if (i915.enable_execlists) {
-               struct i915_gem_context *ctx;
-
-               list_for_each_entry(ctx, &dev_priv->context_list, link)
-                       intel_lr_context_reset(dev_priv, ctx);
-       }
-
-       i915_gem_context_lost(dev_priv);
-}
-
 int i915_gem_context_init(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -504,7 +514,7 @@ void i915_gem_context_fini(struct drm_device *dev)
 
        lockdep_assert_held(&dev->struct_mutex);
 
-       i915_gem_context_unreference(dctx);
+       context_close(dctx);
        dev_priv->kernel_context = NULL;
 
        ida_destroy(&dev_priv->context_hw_ida);
@@ -514,8 +524,7 @@ static int context_idr_cleanup(int id, void *p, void *data)
 {
        struct i915_gem_context *ctx = p;
 
-       ctx->file_priv = ERR_PTR(-EBADF);
-       i915_gem_context_unreference(ctx);
+       context_close(ctx);
        return 0;
 }
 
@@ -552,12 +561,13 @@ static inline int
 mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
 {
        struct drm_i915_private *dev_priv = req->i915;
+       struct intel_ring *ring = req->ring;
        struct intel_engine_cs *engine = req->engine;
        u32 flags = hw_flags | MI_MM_SPACE_GTT;
        const int num_rings =
                /* Use an extended w/a on ivb+ if signalling from other rings */
-               i915_semaphore_is_enabled(dev_priv) ?
-               hweight32(INTEL_INFO(dev_priv)->ring_mask) - 1 :
+               i915.semaphores ?
+               INTEL_INFO(dev_priv)->num_rings - 1 :
                0;
        int len, ret;
 
@@ -567,7 +577,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
         * itlb_before_ctx_switch.
         */
        if (IS_GEN6(dev_priv)) {
-               ret = engine->flush(req, I915_GEM_GPU_DOMAINS, 0);
+               ret = engine->emit_flush(req, EMIT_INVALIDATE);
                if (ret)
                        return ret;
        }
@@ -589,64 +599,64 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
 
        /* WaProgramMiArbOnOffAroundMiSetContext:ivb,vlv,hsw,bdw,chv */
        if (INTEL_GEN(dev_priv) >= 7) {
-               intel_ring_emit(engine, MI_ARB_ON_OFF | MI_ARB_DISABLE);
+               intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_DISABLE);
                if (num_rings) {
                        struct intel_engine_cs *signaller;
 
-                       intel_ring_emit(engine,
+                       intel_ring_emit(ring,
                                        MI_LOAD_REGISTER_IMM(num_rings));
                        for_each_engine(signaller, dev_priv) {
                                if (signaller == engine)
                                        continue;
 
-                               intel_ring_emit_reg(engine,
+                               intel_ring_emit_reg(ring,
                                                    RING_PSMI_CTL(signaller->mmio_base));
-                               intel_ring_emit(engine,
+                               intel_ring_emit(ring,
                                                _MASKED_BIT_ENABLE(GEN6_PSMI_SLEEP_MSG_DISABLE));
                        }
                }
        }
 
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_emit(engine, MI_SET_CONTEXT);
-       intel_ring_emit(engine,
-                       i915_gem_obj_ggtt_offset(req->ctx->engine[RCS].state) |
-                       flags);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_emit(ring, MI_SET_CONTEXT);
+       intel_ring_emit(ring,
+                       i915_ggtt_offset(req->ctx->engine[RCS].state) | flags);
        /*
         * w/a: MI_SET_CONTEXT must always be followed by MI_NOOP
         * WaMiSetContext_Hang:snb,ivb,vlv
         */
-       intel_ring_emit(engine, MI_NOOP);
+       intel_ring_emit(ring, MI_NOOP);
 
        if (INTEL_GEN(dev_priv) >= 7) {
                if (num_rings) {
                        struct intel_engine_cs *signaller;
                        i915_reg_t last_reg = {}; /* keep gcc quiet */
 
-                       intel_ring_emit(engine,
+                       intel_ring_emit(ring,
                                        MI_LOAD_REGISTER_IMM(num_rings));
                        for_each_engine(signaller, dev_priv) {
                                if (signaller == engine)
                                        continue;
 
                                last_reg = RING_PSMI_CTL(signaller->mmio_base);
-                               intel_ring_emit_reg(engine, last_reg);
-                               intel_ring_emit(engine,
+                               intel_ring_emit_reg(ring, last_reg);
+                               intel_ring_emit(ring,
                                                _MASKED_BIT_DISABLE(GEN6_PSMI_SLEEP_MSG_DISABLE));
                        }
 
                        /* Insert a delay before the next switch! */
-                       intel_ring_emit(engine,
+                       intel_ring_emit(ring,
                                        MI_STORE_REGISTER_MEM |
                                        MI_SRM_LRM_GLOBAL_GTT);
-                       intel_ring_emit_reg(engine, last_reg);
-                       intel_ring_emit(engine, engine->scratch.gtt_offset);
-                       intel_ring_emit(engine, MI_NOOP);
+                       intel_ring_emit_reg(ring, last_reg);
+                       intel_ring_emit(ring,
+                                       i915_ggtt_offset(engine->scratch));
+                       intel_ring_emit(ring, MI_NOOP);
                }
-               intel_ring_emit(engine, MI_ARB_ON_OFF | MI_ARB_ENABLE);
+               intel_ring_emit(ring, MI_ARB_ON_OFF | MI_ARB_ENABLE);
        }
 
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
        return ret;
 }
@@ -654,7 +664,7 @@ mi_set_context(struct drm_i915_gem_request *req, u32 hw_flags)
 static int remap_l3(struct drm_i915_gem_request *req, int slice)
 {
        u32 *remap_info = req->i915->l3_parity.remap_info[slice];
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int i, ret;
 
        if (!remap_info)
@@ -669,13 +679,13 @@ static int remap_l3(struct drm_i915_gem_request *req, int slice)
         * here because no other code should access these registers other than
         * at initialization time.
         */
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(GEN7_L3LOG_SIZE/4));
        for (i = 0; i < GEN7_L3LOG_SIZE/4; i++) {
-               intel_ring_emit_reg(engine, GEN7_L3LOG(slice, i));
-               intel_ring_emit(engine, remap_info[i]);
+               intel_ring_emit_reg(ring, GEN7_L3LOG(slice, i));
+               intel_ring_emit(ring, remap_info[i]);
        }
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -744,6 +754,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
        struct i915_gem_context *to = req->ctx;
        struct intel_engine_cs *engine = req->engine;
        struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt;
+       struct i915_vma *vma = to->engine[RCS].state;
        struct i915_gem_context *from;
        u32 hw_flags;
        int ret, i;
@@ -751,10 +762,15 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
        if (skip_rcs_switch(ppgtt, engine, to))
                return 0;
 
+       /* Clear this page out of any CPU caches for coherent swap-in/out. */
+       if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
+               ret = i915_gem_object_set_to_gtt_domain(vma->obj, false);
+               if (ret)
+                       return ret;
+       }
+
        /* Trying to pin first makes error handling easier. */
-       ret = i915_gem_obj_ggtt_pin(to->engine[RCS].state,
-                                   to->ggtt_alignment,
-                                   0);
+       ret = i915_vma_pin(vma, 0, to->ggtt_alignment, PIN_GLOBAL);
        if (ret)
                return ret;
 
@@ -767,18 +783,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
         */
        from = engine->last_context;
 
-       /*
-        * Clear this page out of any CPU caches for coherent swap-in/out. Note
-        * that thanks to write = false in this call and us not setting any gpu
-        * write domains when putting a context object onto the active list
-        * (when switching away from it), this won't block.
-        *
-        * XXX: We need a real interface to do this instead of trickery.
-        */
-       ret = i915_gem_object_set_to_gtt_domain(to->engine[RCS].state, false);
-       if (ret)
-               goto unpin_out;
-
        if (needs_pd_load_pre(ppgtt, engine, to)) {
                /* Older GENs and non render rings still want the load first,
                 * "PP_DCLV followed by PP_DIR_BASE register through Load
@@ -787,7 +791,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
                trace_switch_mm(engine, to);
                ret = ppgtt->switch_mm(ppgtt, req);
                if (ret)
-                       goto unpin_out;
+                       goto err;
        }
 
        if (!to->engine[RCS].initialised || i915_gem_context_is_default(to))
@@ -804,7 +808,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
        if (to != from || (hw_flags & MI_FORCE_RESTORE)) {
                ret = mi_set_context(req, hw_flags);
                if (ret)
-                       goto unpin_out;
+                       goto err;
        }
 
        /* The backing object for the context is done after switching to the
@@ -814,8 +818,6 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
         * MI_SET_CONTEXT instead of when the next seqno has completed.
         */
        if (from != NULL) {
-               from->engine[RCS].state->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
-               i915_vma_move_to_active(i915_gem_obj_to_ggtt(from->engine[RCS].state), req);
                /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
                 * whole damn pipeline, we don't need to explicitly mark the
                 * object dirty. The only exception is that the context must be
@@ -823,14 +825,12 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
                 * able to defer doing this until we know the object would be
                 * swapped, but there is no way to do that yet.
                 */
-               from->engine[RCS].state->dirty = 1;
-
-               /* obj is kept alive until the next request by its active ref */
-               i915_gem_object_ggtt_unpin(from->engine[RCS].state);
-               i915_gem_context_unreference(from);
+               i915_vma_move_to_active(from->engine[RCS].state, req, 0);
+               /* state is kept alive until the next request */
+               i915_vma_unpin(from->engine[RCS].state);
+               i915_gem_context_put(from);
        }
-       i915_gem_context_reference(to);
-       engine->last_context = to;
+       engine->last_context = i915_gem_context_get(to);
 
        /* GEN8 does *not* require an explicit reload if the PDPs have been
         * setup, and we do not wish to move them.
@@ -872,8 +872,8 @@ static int do_rcs_switch(struct drm_i915_gem_request *req)
 
        return 0;
 
-unpin_out:
-       i915_gem_object_ggtt_unpin(to->engine[RCS].state);
+err:
+       i915_vma_unpin(vma);
        return ret;
 }
 
@@ -894,8 +894,9 @@ int i915_switch_context(struct drm_i915_gem_request *req)
 {
        struct intel_engine_cs *engine = req->engine;
 
-       WARN_ON(i915.enable_execlists);
        lockdep_assert_held(&req->i915->drm.struct_mutex);
+       if (i915.enable_execlists)
+               return 0;
 
        if (!req->ctx->engine[engine->id].state) {
                struct i915_gem_context *to = req->ctx;
@@ -914,10 +915,9 @@ int i915_switch_context(struct drm_i915_gem_request *req)
                }
 
                if (to != engine->last_context) {
-                       i915_gem_context_reference(to);
                        if (engine->last_context)
-                               i915_gem_context_unreference(engine->last_context);
-                       engine->last_context = to;
+                               i915_gem_context_put(engine->last_context);
+                       engine->last_context = i915_gem_context_get(to);
                }
 
                return 0;
@@ -926,6 +926,33 @@ int i915_switch_context(struct drm_i915_gem_request *req)
        return do_rcs_switch(req);
 }
 
+int i915_gem_switch_to_kernel_context(struct drm_i915_private *dev_priv)
+{
+       struct intel_engine_cs *engine;
+
+       for_each_engine(engine, dev_priv) {
+               struct drm_i915_gem_request *req;
+               int ret;
+
+               if (engine->last_context == NULL)
+                       continue;
+
+               if (engine->last_context == dev_priv->kernel_context)
+                       continue;
+
+               req = i915_gem_request_alloc(engine, dev_priv->kernel_context);
+               if (IS_ERR(req))
+                       return PTR_ERR(req);
+
+               ret = i915_switch_context(req);
+               i915_add_request_no_flush(req);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static bool contexts_enabled(struct drm_device *dev)
 {
        return i915.enable_execlists || to_i915(dev)->hw_context_size;
@@ -985,7 +1012,7 @@ int i915_gem_context_destroy_ioctl(struct drm_device *dev, void *data,
        }
 
        idr_remove(&file_priv->context_idr, ctx->user_handle);
-       i915_gem_context_unreference(ctx);
+       context_close(ctx);
        mutex_unlock(&dev->struct_mutex);
 
        DRM_DEBUG_DRIVER("HW context %d destroyed\n", args->ctx_id);
diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c
deleted file mode 100644 (file)
index a565164..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright © 2008 Intel Corporation
- *
- * 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 AUTHORS OR COPYRIGHT HOLDERS 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:
- *    Keith Packard <keithp@keithp.com>
- *
- */
-
-#include <drm/drmP.h>
-#include <drm/i915_drm.h>
-#include "i915_drv.h"
-
-#if WATCH_LISTS
-int
-i915_verify_lists(struct drm_device *dev)
-{
-       static int warned;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct drm_i915_gem_object *obj;
-       struct intel_engine_cs *engine;
-       int err = 0;
-
-       if (warned)
-               return 0;
-
-       for_each_engine(engine, dev_priv) {
-               list_for_each_entry(obj, &engine->active_list,
-                                   engine_list[engine->id]) {
-                       if (obj->base.dev != dev ||
-                           !atomic_read(&obj->base.refcount.refcount)) {
-                               DRM_ERROR("%s: freed active obj %p\n",
-                                         engine->name, obj);
-                               err++;
-                               break;
-                       } else if (!obj->active ||
-                                  obj->last_read_req[engine->id] == NULL) {
-                               DRM_ERROR("%s: invalid active obj %p\n",
-                                         engine->name, obj);
-                               err++;
-                       } else if (obj->base.write_domain) {
-                               DRM_ERROR("%s: invalid write obj %p (w %x)\n",
-                                         engine->name,
-                                         obj, obj->base.write_domain);
-                               err++;
-                       }
-               }
-       }
-
-       return warned = err;
-}
-#endif /* WATCH_LIST */
index 80bbe43..97c9d68 100644 (file)
  * Authors:
  *     Dave Airlie <airlied@redhat.com>
  */
+
+#include <linux/dma-buf.h>
+#include <linux/reservation.h>
+
 #include <drm/drmP.h>
+
 #include "i915_drv.h"
-#include <linux/dma-buf.h>
 
 static struct drm_i915_gem_object *dma_buf_to_obj(struct dma_buf *buf)
 {
@@ -115,7 +119,7 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
        if (ret)
                return ERR_PTR(ret);
 
-       addr = i915_gem_object_pin_map(obj);
+       addr = i915_gem_object_pin_map(obj, I915_MAP_WB);
        mutex_unlock(&dev->struct_mutex);
 
        return addr;
@@ -218,25 +222,73 @@ static const struct dma_buf_ops i915_dmabuf_ops =  {
        .end_cpu_access = i915_gem_end_cpu_access,
 };
 
+static void export_fences(struct drm_i915_gem_object *obj,
+                         struct dma_buf *dma_buf)
+{
+       struct reservation_object *resv = dma_buf->resv;
+       struct drm_i915_gem_request *req;
+       unsigned long active;
+       int idx;
+
+       active = __I915_BO_ACTIVE(obj);
+       if (!active)
+               return;
+
+       /* Serialise with execbuf to prevent concurrent fence-loops */
+       mutex_lock(&obj->base.dev->struct_mutex);
+
+       /* Mark the object for future fences before racily adding old fences */
+       obj->base.dma_buf = dma_buf;
+
+       ww_mutex_lock(&resv->lock, NULL);
+
+       for_each_active(active, idx) {
+               req = i915_gem_active_get(&obj->last_read[idx],
+                                         &obj->base.dev->struct_mutex);
+               if (!req)
+                       continue;
+
+               if (reservation_object_reserve_shared(resv) == 0)
+                       reservation_object_add_shared_fence(resv, &req->fence);
+
+               i915_gem_request_put(req);
+       }
+
+       req = i915_gem_active_get(&obj->last_write,
+                                 &obj->base.dev->struct_mutex);
+       if (req) {
+               reservation_object_add_excl_fence(resv, &req->fence);
+               i915_gem_request_put(req);
+       }
+
+       ww_mutex_unlock(&resv->lock);
+       mutex_unlock(&obj->base.dev->struct_mutex);
+}
+
 struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
                                      struct drm_gem_object *gem_obj, int flags)
 {
        struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
        DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
+       struct dma_buf *dma_buf;
 
        exp_info.ops = &i915_dmabuf_ops;
        exp_info.size = gem_obj->size;
        exp_info.flags = flags;
        exp_info.priv = gem_obj;
 
-
        if (obj->ops->dmabuf_export) {
                int ret = obj->ops->dmabuf_export(obj);
                if (ret)
                        return ERR_PTR(ret);
        }
 
-       return dma_buf_export(&exp_info);
+       dma_buf = drm_gem_dmabuf_export(dev, &exp_info);
+       if (IS_ERR(dma_buf))
+               return dma_buf;
+
+       export_fences(obj, dma_buf);
+       return dma_buf;
 }
 
 static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj)
@@ -278,8 +330,7 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
                         * Importing dmabuf exported from out own gem increases
                         * refcount on gem itself instead of f_count of dmabuf.
                         */
-                       drm_gem_object_reference(&obj->base);
-                       return &obj->base;
+                       return &i915_gem_object_get(obj)->base;
                }
        }
 
@@ -300,6 +351,16 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
        i915_gem_object_init(obj, &i915_gem_object_dmabuf_ops);
        obj->base.import_attach = attach;
 
+       /* We use GTT as shorthand for a coherent domain, one that is
+        * neither in the GPU cache nor in the CPU cache, where all
+        * writes are immediately visible in memory. (That's not strictly
+        * true, but it's close! There are internal buffers such as the
+        * write-combined buffer or a delay through the chipset for GTT
+        * writes that do require us to treat GTT as a separate cache domain.)
+        */
+       obj->base.read_domains = I915_GEM_DOMAIN_GTT;
+       obj->base.write_domain = 0;
+
        return &obj->base;
 
 fail_detach:
index 3c1280e..5b6f81c 100644 (file)
 #include "intel_drv.h"
 #include "i915_trace.h"
 
-static int switch_to_pinned_context(struct drm_i915_private *dev_priv)
+static bool
+gpu_is_idle(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
 
-       if (i915.enable_execlists)
-               return 0;
-
        for_each_engine(engine, dev_priv) {
-               struct drm_i915_gem_request *req;
-               int ret;
-
-               if (engine->last_context == NULL)
-                       continue;
-
-               if (engine->last_context == dev_priv->kernel_context)
-                       continue;
-
-               req = i915_gem_request_alloc(engine, dev_priv->kernel_context);
-               if (IS_ERR(req))
-                       return PTR_ERR(req);
-
-               ret = i915_switch_context(req);
-               i915_add_request_no_flush(req);
-               if (ret)
-                       return ret;
+               if (intel_engine_is_active(engine))
+                       return false;
        }
 
-       return 0;
+       return true;
 }
 
-
 static bool
-mark_free(struct i915_vma *vma, struct list_head *unwind)
+mark_free(struct i915_vma *vma, unsigned int flags, struct list_head *unwind)
 {
-       if (vma->pin_count)
+       if (i915_vma_is_pinned(vma))
                return false;
 
        if (WARN_ON(!list_empty(&vma->exec_list)))
                return false;
 
+       if (flags & PIN_NONFAULT && vma->obj->fault_mappable)
+               return false;
+
        list_add(&vma->exec_list, unwind);
        return drm_mm_scan_add_block(&vma->node);
 }
 
 /**
  * i915_gem_evict_something - Evict vmas to make room for binding a new one
- * @dev: drm_device
  * @vm: address space to evict from
  * @min_size: size of the desired free space
  * @alignment: alignment constraint of the desired free space
@@ -102,42 +86,37 @@ mark_free(struct i915_vma *vma, struct list_head *unwind)
  * memory in e.g. the shrinker.
  */
 int
-i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm,
-                        int min_size, unsigned alignment, unsigned cache_level,
-                        unsigned long start, unsigned long end,
+i915_gem_evict_something(struct i915_address_space *vm,
+                        u64 min_size, u64 alignment,
+                        unsigned cache_level,
+                        u64 start, u64 end,
                         unsigned flags)
 {
-       struct list_head eviction_list, unwind_list;
-       struct i915_vma *vma;
-       int ret = 0;
-       int pass = 0;
+       struct drm_i915_private *dev_priv = to_i915(vm->dev);
+       struct list_head eviction_list;
+       struct list_head *phases[] = {
+               &vm->inactive_list,
+               &vm->active_list,
+               NULL,
+       }, **phase;
+       struct i915_vma *vma, *next;
+       int ret;
 
-       trace_i915_gem_evict(dev, min_size, alignment, flags);
+       trace_i915_gem_evict(vm, min_size, alignment, flags);
 
        /*
         * The goal is to evict objects and amalgamate space in LRU order.
         * The oldest idle objects reside on the inactive list, which is in
-        * retirement order. The next objects to retire are those on the (per
-        * ring) active list that do not have an outstanding flush. Once the
-        * hardware reports completion (the seqno is updated after the
-        * batchbuffer has been finished) the clean buffer objects would
-        * be retired to the inactive list. Any dirty objects would be added
-        * to the tail of the flushing list. So after processing the clean
-        * active objects we need to emit a MI_FLUSH to retire the flushing
-        * list, hence the retirement order of the flushing list is in
-        * advance of the dirty objects on the active lists.
+        * retirement order. The next objects to retire are those in flight,
+        * on the active list, again in retirement order.
         *
         * The retirement sequence is thus:
         *   1. Inactive objects (already retired)
-        *   2. Clean active objects
-        *   3. Flushing list
-        *   4. Dirty active objects.
+        *   2. Active objects (will stall on unbinding)
         *
         * On each list, the oldest objects lie at the HEAD with the freshest
         * object on the TAIL.
         */
-
-       INIT_LIST_HEAD(&unwind_list);
        if (start != 0 || end != vm->total) {
                drm_mm_init_scan_with_range(&vm->mm, min_size,
                                            alignment, cache_level,
@@ -145,96 +124,86 @@ i915_gem_evict_something(struct drm_device *dev, struct i915_address_space *vm,
        } else
                drm_mm_init_scan(&vm->mm, min_size, alignment, cache_level);
 
-search_again:
-       /* First see if there is a large enough contiguous idle region... */
-       list_for_each_entry(vma, &vm->inactive_list, vm_link) {
-               if (mark_free(vma, &unwind_list))
-                       goto found;
-       }
-
        if (flags & PIN_NONBLOCK)
-               goto none;
+               phases[1] = NULL;
 
-       /* Now merge in the soon-to-be-expired objects... */
-       list_for_each_entry(vma, &vm->active_list, vm_link) {
-               if (mark_free(vma, &unwind_list))
-                       goto found;
-       }
+search_again:
+       INIT_LIST_HEAD(&eviction_list);
+       phase = phases;
+       do {
+               list_for_each_entry(vma, *phase, vm_link)
+                       if (mark_free(vma, flags, &eviction_list))
+                               goto found;
+       } while (*++phase);
 
-none:
        /* Nothing found, clean up and bail out! */
-       while (!list_empty(&unwind_list)) {
-               vma = list_first_entry(&unwind_list,
-                                      struct i915_vma,
-                                      exec_list);
+       list_for_each_entry_safe(vma, next, &eviction_list, exec_list) {
                ret = drm_mm_scan_remove_block(&vma->node);
                BUG_ON(ret);
 
-               list_del_init(&vma->exec_list);
+               INIT_LIST_HEAD(&vma->exec_list);
        }
 
        /* Can we unpin some objects such as idle hw contents,
-        * or pending flips?
+        * or pending flips? But since only the GGTT has global entries
+        * such as scanouts, rinbuffers and contexts, we can skip the
+        * purge when inspecting per-process local address spaces.
         */
-       if (flags & PIN_NONBLOCK)
+       if (!i915_is_ggtt(vm) || flags & PIN_NONBLOCK)
                return -ENOSPC;
 
-       /* Only idle the GPU and repeat the search once */
-       if (pass++ == 0) {
-               struct drm_i915_private *dev_priv = to_i915(dev);
-
-               if (i915_is_ggtt(vm)) {
-                       ret = switch_to_pinned_context(dev_priv);
-                       if (ret)
-                               return ret;
-               }
-
-               ret = i915_gem_wait_for_idle(dev_priv);
-               if (ret)
-                       return ret;
-
-               i915_gem_retire_requests(dev_priv);
-               goto search_again;
+       if (gpu_is_idle(dev_priv)) {
+               /* If we still have pending pageflip completions, drop
+                * back to userspace to give our workqueues time to
+                * acquire our locks and unpin the old scanouts.
+                */
+               return intel_has_pending_fb_unpin(vm->dev) ? -EAGAIN : -ENOSPC;
        }
 
-       /* If we still have pending pageflip completions, drop
-        * back to userspace to give our workqueues time to
-        * acquire our locks and unpin the old scanouts.
+       /* Not everything in the GGTT is tracked via vma (otherwise we
+        * could evict as required with minimal stalling) so we are forced
+        * to idle the GPU and explicitly retire outstanding requests in
+        * the hopes that we can then remove contexts and the like only
+        * bound by their active reference.
         */
-       return intel_has_pending_fb_unpin(dev) ? -EAGAIN : -ENOSPC;
+       ret = i915_gem_switch_to_kernel_context(dev_priv);
+       if (ret)
+               return ret;
+
+       ret = i915_gem_wait_for_idle(dev_priv,
+                                    I915_WAIT_INTERRUPTIBLE |
+                                    I915_WAIT_LOCKED);
+       if (ret)
+               return ret;
+
+       i915_gem_retire_requests(dev_priv);
+       goto search_again;
 
 found:
        /* drm_mm doesn't allow any other other operations while
-        * scanning, therefore store to be evicted objects on a
-        * temporary list. */
-       INIT_LIST_HEAD(&eviction_list);
-       while (!list_empty(&unwind_list)) {
-               vma = list_first_entry(&unwind_list,
-                                      struct i915_vma,
-                                      exec_list);
-               if (drm_mm_scan_remove_block(&vma->node)) {
-                       list_move(&vma->exec_list, &eviction_list);
-                       drm_gem_object_reference(&vma->obj->base);
-                       continue;
-               }
-               list_del_init(&vma->exec_list);
+        * scanning, therefore store to-be-evicted objects on a
+        * temporary list and take a reference for all before
+        * calling unbind (which may remove the active reference
+        * of any of our objects, thus corrupting the list).
+        */
+       list_for_each_entry_safe(vma, next, &eviction_list, exec_list) {
+               if (drm_mm_scan_remove_block(&vma->node))
+                       __i915_vma_pin(vma);
+               else
+                       list_del_init(&vma->exec_list);
        }
 
        /* Unbinding will emit any required flushes */
        while (!list_empty(&eviction_list)) {
-               struct drm_gem_object *obj;
                vma = list_first_entry(&eviction_list,
                                       struct i915_vma,
                                       exec_list);
 
-               obj =  &vma->obj->base;
                list_del_init(&vma->exec_list);
+               __i915_vma_unpin(vma);
                if (ret == 0)
                        ret = i915_vma_unbind(vma);
-
-               drm_gem_object_unreference(obj);
        }
-
        return ret;
 }
 
@@ -256,8 +225,8 @@ i915_gem_evict_for_vma(struct i915_vma *target)
 
                vma = container_of(node, typeof(*vma), node);
 
-               if (vma->pin_count) {
-                       if (!vma->exec_entry || (vma->pin_count > 1))
+               if (i915_vma_is_pinned(vma)) {
+                       if (!vma->exec_entry || i915_vma_pin_count(vma) > 1)
                                /* Object is pinned for some other use */
                                return -EBUSY;
 
@@ -303,22 +272,23 @@ int i915_gem_evict_vm(struct i915_address_space *vm, bool do_idle)
                struct drm_i915_private *dev_priv = to_i915(vm->dev);
 
                if (i915_is_ggtt(vm)) {
-                       ret = switch_to_pinned_context(dev_priv);
+                       ret = i915_gem_switch_to_kernel_context(dev_priv);
                        if (ret)
                                return ret;
                }
 
-               ret = i915_gem_wait_for_idle(dev_priv);
+               ret = i915_gem_wait_for_idle(dev_priv,
+                                            I915_WAIT_INTERRUPTIBLE |
+                                            I915_WAIT_LOCKED);
                if (ret)
                        return ret;
 
                i915_gem_retire_requests(dev_priv);
-
                WARN_ON(!list_empty(&vm->active_list));
        }
 
        list_for_each_entry_safe(vma, next, &vm->inactive_list, vm_link)
-               if (vma->pin_count == 0)
+               if (!i915_vma_is_pinned(vma))
                        WARN_ON(i915_vma_unbind(vma));
 
        return 0;
index 72628ed..7adb4c7 100644 (file)
  *
  */
 
+#include <linux/dma_remapping.h>
+#include <linux/reservation.h>
+#include <linux/uaccess.h>
+
 #include <drm/drmP.h>
 #include <drm/i915_drm.h>
+
 #include "i915_drv.h"
+#include "i915_gem_dmabuf.h"
 #include "i915_trace.h"
 #include "intel_drv.h"
-#include <linux/dma_remapping.h>
-#include <linux/uaccess.h>
+#include "intel_frontbuffer.h"
 
-#define  __EXEC_OBJECT_HAS_PIN (1<<31)
-#define  __EXEC_OBJECT_HAS_FENCE (1<<30)
-#define  __EXEC_OBJECT_NEEDS_MAP (1<<29)
-#define  __EXEC_OBJECT_NEEDS_BIAS (1<<28)
+#define DBG_USE_CPU_RELOC 0 /* -1 force GTT relocs; 1 force CPU relocs */
+
+#define  __EXEC_OBJECT_HAS_PIN         (1<<31)
+#define  __EXEC_OBJECT_HAS_FENCE       (1<<30)
+#define  __EXEC_OBJECT_NEEDS_MAP       (1<<29)
+#define  __EXEC_OBJECT_NEEDS_BIAS      (1<<28)
+#define  __EXEC_OBJECT_INTERNAL_FLAGS (0xf<<28) /* all of the above */
 
 #define BATCH_OFFSET_BIAS (256*1024)
 
+struct i915_execbuffer_params {
+       struct drm_device               *dev;
+       struct drm_file                 *file;
+       struct i915_vma                 *batch;
+       u32                             dispatch_flags;
+       u32                             args_batch_start_offset;
+       struct intel_engine_cs          *engine;
+       struct i915_gem_context         *ctx;
+       struct drm_i915_gem_request     *request;
+};
+
 struct eb_vmas {
+       struct drm_i915_private *i915;
        struct list_head vmas;
        int and;
        union {
@@ -51,7 +71,8 @@ struct eb_vmas {
 };
 
 static struct eb_vmas *
-eb_create(struct drm_i915_gem_execbuffer2 *args)
+eb_create(struct drm_i915_private *i915,
+         struct drm_i915_gem_execbuffer2 *args)
 {
        struct eb_vmas *eb = NULL;
 
@@ -78,6 +99,7 @@ eb_create(struct drm_i915_gem_execbuffer2 *args)
        } else
                eb->and = -args->buffer_count;
 
+       eb->i915 = i915;
        INIT_LIST_HEAD(&eb->vmas);
        return eb;
 }
@@ -89,6 +111,26 @@ eb_reset(struct eb_vmas *eb)
                memset(eb->buckets, 0, (eb->and+1)*sizeof(struct hlist_head));
 }
 
+static struct i915_vma *
+eb_get_batch(struct eb_vmas *eb)
+{
+       struct i915_vma *vma = list_entry(eb->vmas.prev, typeof(*vma), exec_list);
+
+       /*
+        * SNA is doing fancy tricks with compressing batch buffers, which leads
+        * to negative relocation deltas. Usually that works out ok since the
+        * relocate address is still positive, except when the batch is placed
+        * very low in the GTT. Ensure this doesn't happen.
+        *
+        * Note that actual hangs have only been observed on gen7, but for
+        * paranoia do it everywhere.
+        */
+       if ((vma->exec_entry->flags & EXEC_OBJECT_PINNED) == 0)
+               vma->exec_entry->flags |= __EXEC_OBJECT_NEEDS_BIAS;
+
+       return vma;
+}
+
 static int
 eb_lookup_vmas(struct eb_vmas *eb,
               struct drm_i915_gem_exec_object2 *exec,
@@ -122,7 +164,7 @@ eb_lookup_vmas(struct eb_vmas *eb,
                        goto err;
                }
 
-               drm_gem_object_reference(&obj->base);
+               i915_gem_object_get(obj);
                list_add_tail(&obj->obj_exec_link, &objects);
        }
        spin_unlock(&file->table_lock);
@@ -143,8 +185,8 @@ eb_lookup_vmas(struct eb_vmas *eb,
                 * from the (obj, vm) we don't run the risk of creating
                 * duplicated vmas for the same vm.
                 */
-               vma = i915_gem_obj_lookup_or_create_vma(obj, vm);
-               if (IS_ERR(vma)) {
+               vma = i915_gem_obj_lookup_or_create_vma(obj, vm, NULL);
+               if (unlikely(IS_ERR(vma))) {
                        DRM_DEBUG("Failed to lookup VMA\n");
                        ret = PTR_ERR(vma);
                        goto err;
@@ -175,7 +217,7 @@ err:
                                       struct drm_i915_gem_object,
                                       obj_exec_link);
                list_del_init(&obj->obj_exec_link);
-               drm_gem_object_unreference(&obj->base);
+               i915_gem_object_put(obj);
        }
        /*
         * Objects already transfered to the vmas list will be unreferenced by
@@ -208,7 +250,6 @@ static void
 i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma)
 {
        struct drm_i915_gem_exec_object2 *entry;
-       struct drm_i915_gem_object *obj = vma->obj;
 
        if (!drm_mm_node_allocated(&vma->node))
                return;
@@ -216,10 +257,10 @@ i915_gem_execbuffer_unreserve_vma(struct i915_vma *vma)
        entry = vma->exec_entry;
 
        if (entry->flags & __EXEC_OBJECT_HAS_FENCE)
-               i915_gem_object_unpin_fence(obj);
+               i915_vma_unpin_fence(vma);
 
        if (entry->flags & __EXEC_OBJECT_HAS_PIN)
-               vma->pin_count--;
+               __i915_vma_unpin(vma);
 
        entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN);
 }
@@ -234,13 +275,19 @@ static void eb_destroy(struct eb_vmas *eb)
                                       exec_list);
                list_del_init(&vma->exec_list);
                i915_gem_execbuffer_unreserve_vma(vma);
-               drm_gem_object_unreference(&vma->obj->base);
+               i915_vma_put(vma);
        }
        kfree(eb);
 }
 
 static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
 {
+       if (!i915_gem_object_has_struct_page(obj))
+               return false;
+
+       if (DBG_USE_CPU_RELOC)
+               return DBG_USE_CPU_RELOC > 0;
+
        return (HAS_LLC(obj->base.dev) ||
                obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
                obj->cache_level != I915_CACHE_NONE);
@@ -265,144 +312,265 @@ static inline uint64_t gen8_noncanonical_addr(uint64_t address)
 }
 
 static inline uint64_t
-relocation_target(struct drm_i915_gem_relocation_entry *reloc,
+relocation_target(const struct drm_i915_gem_relocation_entry *reloc,
                  uint64_t target_offset)
 {
        return gen8_canonical_addr((int)reloc->delta + target_offset);
 }
 
-static int
-relocate_entry_cpu(struct drm_i915_gem_object *obj,
-                  struct drm_i915_gem_relocation_entry *reloc,
-                  uint64_t target_offset)
+struct reloc_cache {
+       struct drm_i915_private *i915;
+       struct drm_mm_node node;
+       unsigned long vaddr;
+       unsigned int page;
+       bool use_64bit_reloc;
+};
+
+static void reloc_cache_init(struct reloc_cache *cache,
+                            struct drm_i915_private *i915)
 {
-       struct drm_device *dev = obj->base.dev;
-       uint32_t page_offset = offset_in_page(reloc->offset);
-       uint64_t delta = relocation_target(reloc, target_offset);
-       char *vaddr;
-       int ret;
+       cache->page = -1;
+       cache->vaddr = 0;
+       cache->i915 = i915;
+       cache->use_64bit_reloc = INTEL_GEN(cache->i915) >= 8;
+       cache->node.allocated = false;
+}
 
-       ret = i915_gem_object_set_to_cpu_domain(obj, true);
-       if (ret)
-               return ret;
+static inline void *unmask_page(unsigned long p)
+{
+       return (void *)(uintptr_t)(p & PAGE_MASK);
+}
+
+static inline unsigned int unmask_flags(unsigned long p)
+{
+       return p & ~PAGE_MASK;
+}
+
+#define KMAP 0x4 /* after CLFLUSH_FLAGS */
+
+static void reloc_cache_fini(struct reloc_cache *cache)
+{
+       void *vaddr;
 
-       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                               reloc->offset >> PAGE_SHIFT));
-       *(uint32_t *)(vaddr + page_offset) = lower_32_bits(delta);
+       if (!cache->vaddr)
+               return;
 
-       if (INTEL_INFO(dev)->gen >= 8) {
-               page_offset = offset_in_page(page_offset + sizeof(uint32_t));
+       vaddr = unmask_page(cache->vaddr);
+       if (cache->vaddr & KMAP) {
+               if (cache->vaddr & CLFLUSH_AFTER)
+                       mb();
 
-               if (page_offset == 0) {
-                       kunmap_atomic(vaddr);
-                       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                           (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
+               kunmap_atomic(vaddr);
+               i915_gem_obj_finish_shmem_access((struct drm_i915_gem_object *)cache->node.mm);
+       } else {
+               wmb();
+               io_mapping_unmap_atomic((void __iomem *)vaddr);
+               if (cache->node.allocated) {
+                       struct i915_ggtt *ggtt = &cache->i915->ggtt;
+
+                       ggtt->base.clear_range(&ggtt->base,
+                                              cache->node.start,
+                                              cache->node.size,
+                                              true);
+                       drm_mm_remove_node(&cache->node);
+               } else {
+                       i915_vma_unpin((struct i915_vma *)cache->node.mm);
                }
+       }
+}
+
+static void *reloc_kmap(struct drm_i915_gem_object *obj,
+                       struct reloc_cache *cache,
+                       int page)
+{
+       void *vaddr;
+
+       if (cache->vaddr) {
+               kunmap_atomic(unmask_page(cache->vaddr));
+       } else {
+               unsigned int flushes;
+               int ret;
+
+               ret = i915_gem_obj_prepare_shmem_write(obj, &flushes);
+               if (ret)
+                       return ERR_PTR(ret);
+
+               BUILD_BUG_ON(KMAP & CLFLUSH_FLAGS);
+               BUILD_BUG_ON((KMAP | CLFLUSH_FLAGS) & PAGE_MASK);
 
-               *(uint32_t *)(vaddr + page_offset) = upper_32_bits(delta);
+               cache->vaddr = flushes | KMAP;
+               cache->node.mm = (void *)obj;
+               if (flushes)
+                       mb();
        }
 
-       kunmap_atomic(vaddr);
+       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj, page));
+       cache->vaddr = unmask_flags(cache->vaddr) | (unsigned long)vaddr;
+       cache->page = page;
 
-       return 0;
+       return vaddr;
 }
 
-static int
-relocate_entry_gtt(struct drm_i915_gem_object *obj,
-                  struct drm_i915_gem_relocation_entry *reloc,
-                  uint64_t target_offset)
+static void *reloc_iomap(struct drm_i915_gem_object *obj,
+                        struct reloc_cache *cache,
+                        int page)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       uint64_t delta = relocation_target(reloc, target_offset);
-       uint64_t offset;
-       void __iomem *reloc_page;
-       int ret;
+       struct i915_ggtt *ggtt = &cache->i915->ggtt;
+       unsigned long offset;
+       void *vaddr;
+
+       if (cache->node.allocated) {
+               wmb();
+               ggtt->base.insert_page(&ggtt->base,
+                                      i915_gem_object_get_dma_address(obj, page),
+                                      cache->node.start, I915_CACHE_NONE, 0);
+               cache->page = page;
+               return unmask_page(cache->vaddr);
+       }
 
-       ret = i915_gem_object_set_to_gtt_domain(obj, true);
-       if (ret)
-               return ret;
+       if (cache->vaddr) {
+               io_mapping_unmap_atomic(unmask_page(cache->vaddr));
+       } else {
+               struct i915_vma *vma;
+               int ret;
 
-       ret = i915_gem_object_put_fence(obj);
-       if (ret)
-               return ret;
+               if (use_cpu_reloc(obj))
+                       return NULL;
+
+               ret = i915_gem_object_set_to_gtt_domain(obj, true);
+               if (ret)
+                       return ERR_PTR(ret);
+
+               vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0,
+                                              PIN_MAPPABLE | PIN_NONBLOCK);
+               if (IS_ERR(vma)) {
+                       memset(&cache->node, 0, sizeof(cache->node));
+                       ret = drm_mm_insert_node_in_range_generic
+                               (&ggtt->base.mm, &cache->node,
+                                4096, 0, 0,
+                                0, ggtt->mappable_end,
+                                DRM_MM_SEARCH_DEFAULT,
+                                DRM_MM_CREATE_DEFAULT);
+                       if (ret) /* no inactive aperture space, use cpu reloc */
+                               return NULL;
+               } else {
+                       ret = i915_vma_put_fence(vma);
+                       if (ret) {
+                               i915_vma_unpin(vma);
+                               return ERR_PTR(ret);
+                       }
 
-       /* Map the page containing the relocation we're going to perform.  */
-       offset = i915_gem_obj_ggtt_offset(obj);
-       offset += reloc->offset;
-       reloc_page = io_mapping_map_atomic_wc(ggtt->mappable,
-                                             offset & PAGE_MASK);
-       iowrite32(lower_32_bits(delta), reloc_page + offset_in_page(offset));
-
-       if (INTEL_INFO(dev)->gen >= 8) {
-               offset += sizeof(uint32_t);
-
-               if (offset_in_page(offset) == 0) {
-                       io_mapping_unmap_atomic(reloc_page);
-                       reloc_page =
-                               io_mapping_map_atomic_wc(ggtt->mappable,
-                                                        offset);
+                       cache->node.start = vma->node.start;
+                       cache->node.mm = (void *)vma;
                }
+       }
 
-               iowrite32(upper_32_bits(delta),
-                         reloc_page + offset_in_page(offset));
+       offset = cache->node.start;
+       if (cache->node.allocated) {
+               ggtt->base.insert_page(&ggtt->base,
+                                      i915_gem_object_get_dma_address(obj, page),
+                                      offset, I915_CACHE_NONE, 0);
+       } else {
+               offset += page << PAGE_SHIFT;
        }
 
-       io_mapping_unmap_atomic(reloc_page);
+       vaddr = io_mapping_map_atomic_wc(&cache->i915->ggtt.mappable, offset);
+       cache->page = page;
+       cache->vaddr = (unsigned long)vaddr;
 
-       return 0;
+       return vaddr;
 }
 
-static void
-clflush_write32(void *addr, uint32_t value)
+static void *reloc_vaddr(struct drm_i915_gem_object *obj,
+                        struct reloc_cache *cache,
+                        int page)
 {
-       /* This is not a fast path, so KISS. */
-       drm_clflush_virt_range(addr, sizeof(uint32_t));
-       *(uint32_t *)addr = value;
-       drm_clflush_virt_range(addr, sizeof(uint32_t));
+       void *vaddr;
+
+       if (cache->page == page) {
+               vaddr = unmask_page(cache->vaddr);
+       } else {
+               vaddr = NULL;
+               if ((cache->vaddr & KMAP) == 0)
+                       vaddr = reloc_iomap(obj, cache, page);
+               if (!vaddr)
+                       vaddr = reloc_kmap(obj, cache, page);
+       }
+
+       return vaddr;
 }
 
-static int
-relocate_entry_clflush(struct drm_i915_gem_object *obj,
-                      struct drm_i915_gem_relocation_entry *reloc,
-                      uint64_t target_offset)
+static void clflush_write32(u32 *addr, u32 value, unsigned int flushes)
 {
-       struct drm_device *dev = obj->base.dev;
-       uint32_t page_offset = offset_in_page(reloc->offset);
-       uint64_t delta = relocation_target(reloc, target_offset);
-       char *vaddr;
-       int ret;
+       if (unlikely(flushes & (CLFLUSH_BEFORE | CLFLUSH_AFTER))) {
+               if (flushes & CLFLUSH_BEFORE) {
+                       clflushopt(addr);
+                       mb();
+               }
 
-       ret = i915_gem_object_set_to_gtt_domain(obj, true);
-       if (ret)
-               return ret;
+               *addr = value;
+
+               /* Writes to the same cacheline are serialised by the CPU
+                * (including clflush). On the write path, we only require
+                * that it hits memory in an orderly fashion and place
+                * mb barriers at the start and end of the relocation phase
+                * to ensure ordering of clflush wrt to the system.
+                */
+               if (flushes & CLFLUSH_AFTER)
+                       clflushopt(addr);
+       } else
+               *addr = value;
+}
 
-       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                               reloc->offset >> PAGE_SHIFT));
-       clflush_write32(vaddr + page_offset, lower_32_bits(delta));
+static int
+relocate_entry(struct drm_i915_gem_object *obj,
+              const struct drm_i915_gem_relocation_entry *reloc,
+              struct reloc_cache *cache,
+              u64 target_offset)
+{
+       u64 offset = reloc->offset;
+       bool wide = cache->use_64bit_reloc;
+       void *vaddr;
+
+       target_offset = relocation_target(reloc, target_offset);
+repeat:
+       vaddr = reloc_vaddr(obj, cache, offset >> PAGE_SHIFT);
+       if (IS_ERR(vaddr))
+               return PTR_ERR(vaddr);
+
+       clflush_write32(vaddr + offset_in_page(offset),
+                       lower_32_bits(target_offset),
+                       cache->vaddr);
+
+       if (wide) {
+               offset += sizeof(u32);
+               target_offset >>= 32;
+               wide = false;
+               goto repeat;
+       }
 
-       if (INTEL_INFO(dev)->gen >= 8) {
-               page_offset = offset_in_page(page_offset + sizeof(uint32_t));
+       return 0;
+}
 
-               if (page_offset == 0) {
-                       kunmap_atomic(vaddr);
-                       vaddr = kmap_atomic(i915_gem_object_get_dirty_page(obj,
-                           (reloc->offset + sizeof(uint32_t)) >> PAGE_SHIFT));
-               }
+static bool object_is_idle(struct drm_i915_gem_object *obj)
+{
+       unsigned long active = i915_gem_object_get_active(obj);
+       int idx;
 
-               clflush_write32(vaddr + page_offset, upper_32_bits(delta));
+       for_each_active(active, idx) {
+               if (!i915_gem_active_is_idle(&obj->last_read[idx],
+                                            &obj->base.dev->struct_mutex))
+                       return false;
        }
 
-       kunmap_atomic(vaddr);
-
-       return 0;
+       return true;
 }
 
 static int
 i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
                                   struct eb_vmas *eb,
-                                  struct drm_i915_gem_relocation_entry *reloc)
+                                  struct drm_i915_gem_relocation_entry *reloc,
+                                  struct reloc_cache *cache)
 {
        struct drm_device *dev = obj->base.dev;
        struct drm_gem_object *target_obj;
@@ -465,7 +633,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
 
        /* Check that the relocation address is valid... */
        if (unlikely(reloc->offset >
-               obj->base.size - (INTEL_INFO(dev)->gen >= 8 ? 8 : 4))) {
+                    obj->base.size - (cache->use_64bit_reloc ? 8 : 4))) {
                DRM_DEBUG("Relocation beyond object bounds: "
                          "obj %p target %d offset %d size %d.\n",
                          obj, reloc->target_handle,
@@ -482,26 +650,15 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
        }
 
        /* We can't wait for rendering with pagefaults disabled */
-       if (obj->active && pagefault_disabled())
+       if (pagefault_disabled() && !object_is_idle(obj))
                return -EFAULT;
 
-       if (use_cpu_reloc(obj))
-               ret = relocate_entry_cpu(obj, reloc, target_offset);
-       else if (obj->map_and_fenceable)
-               ret = relocate_entry_gtt(obj, reloc, target_offset);
-       else if (static_cpu_has(X86_FEATURE_CLFLUSH))
-               ret = relocate_entry_clflush(obj, reloc, target_offset);
-       else {
-               WARN_ONCE(1, "Impossible case in relocation handling\n");
-               ret = -ENODEV;
-       }
-
+       ret = relocate_entry(obj, reloc, cache, target_offset);
        if (ret)
                return ret;
 
        /* and update the user's relocation entry */
        reloc->presumed_offset = target_offset;
-
        return 0;
 }
 
@@ -513,9 +670,11 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
        struct drm_i915_gem_relocation_entry stack_reloc[N_RELOC(512)];
        struct drm_i915_gem_relocation_entry __user *user_relocs;
        struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
-       int remain, ret;
+       struct reloc_cache cache;
+       int remain, ret = 0;
 
        user_relocs = u64_to_user_ptr(entry->relocs_ptr);
+       reloc_cache_init(&cache, eb->i915);
 
        remain = entry->relocation_count;
        while (remain) {
@@ -525,19 +684,23 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
                        count = ARRAY_SIZE(stack_reloc);
                remain -= count;
 
-               if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0])))
-                       return -EFAULT;
+               if (__copy_from_user_inatomic(r, user_relocs, count*sizeof(r[0]))) {
+                       ret = -EFAULT;
+                       goto out;
+               }
 
                do {
                        u64 offset = r->presumed_offset;
 
-                       ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r);
+                       ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, r, &cache);
                        if (ret)
-                               return ret;
+                               goto out;
 
                        if (r->presumed_offset != offset &&
-                           __put_user(r->presumed_offset, &user_relocs->presumed_offset)) {
-                               return -EFAULT;
+                           __put_user(r->presumed_offset,
+                                      &user_relocs->presumed_offset)) {
+                               ret = -EFAULT;
+                               goto out;
                        }
 
                        user_relocs++;
@@ -545,7 +708,9 @@ i915_gem_execbuffer_relocate_vma(struct i915_vma *vma,
                } while (--count);
        }
 
-       return 0;
+out:
+       reloc_cache_fini(&cache);
+       return ret;
 #undef N_RELOC
 }
 
@@ -555,15 +720,18 @@ i915_gem_execbuffer_relocate_vma_slow(struct i915_vma *vma,
                                      struct drm_i915_gem_relocation_entry *relocs)
 {
        const struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
-       int i, ret;
+       struct reloc_cache cache;
+       int i, ret = 0;
 
+       reloc_cache_init(&cache, eb->i915);
        for (i = 0; i < entry->relocation_count; i++) {
-               ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i]);
+               ret = i915_gem_execbuffer_relocate_entry(vma->obj, eb, &relocs[i], &cache);
                if (ret)
-                       return ret;
+                       break;
        }
+       reloc_cache_fini(&cache);
 
-       return 0;
+       return ret;
 }
 
 static int
@@ -626,23 +794,27 @@ i915_gem_execbuffer_reserve_vma(struct i915_vma *vma,
                        flags |= PIN_HIGH;
        }
 
-       ret = i915_gem_object_pin(obj, vma->vm, entry->alignment, flags);
-       if ((ret == -ENOSPC  || ret == -E2BIG) &&
+       ret = i915_vma_pin(vma,
+                          entry->pad_to_size,
+                          entry->alignment,
+                          flags);
+       if ((ret == -ENOSPC || ret == -E2BIG) &&
            only_mappable_for_reloc(entry->flags))
-               ret = i915_gem_object_pin(obj, vma->vm,
-                                         entry->alignment,
-                                         flags & ~PIN_MAPPABLE);
+               ret = i915_vma_pin(vma,
+                                  entry->pad_to_size,
+                                  entry->alignment,
+                                  flags & ~PIN_MAPPABLE);
        if (ret)
                return ret;
 
        entry->flags |= __EXEC_OBJECT_HAS_PIN;
 
        if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
-               ret = i915_gem_object_get_fence(obj);
+               ret = i915_vma_get_fence(vma);
                if (ret)
                        return ret;
 
-               if (i915_gem_object_pin_fence(obj))
+               if (i915_vma_pin_fence(vma))
                        entry->flags |= __EXEC_OBJECT_HAS_FENCE;
        }
 
@@ -667,7 +839,7 @@ need_reloc_mappable(struct i915_vma *vma)
        if (entry->relocation_count == 0)
                return false;
 
-       if (!vma->is_ggtt)
+       if (!i915_vma_is_ggtt(vma))
                return false;
 
        /* See also use_cpu_reloc() */
@@ -684,14 +856,17 @@ static bool
 eb_vma_misplaced(struct i915_vma *vma)
 {
        struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
-       struct drm_i915_gem_object *obj = vma->obj;
 
-       WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP && !vma->is_ggtt);
+       WARN_ON(entry->flags & __EXEC_OBJECT_NEEDS_MAP &&
+               !i915_vma_is_ggtt(vma));
 
        if (entry->alignment &&
            vma->node.start & (entry->alignment - 1))
                return true;
 
+       if (vma->node.size < entry->pad_to_size)
+               return true;
+
        if (entry->flags & EXEC_OBJECT_PINNED &&
            vma->node.start != entry->offset)
                return true;
@@ -701,7 +876,8 @@ eb_vma_misplaced(struct i915_vma *vma)
                return true;
 
        /* avoid costly ping-pong once a batch bo ended up non-mappable */
-       if (entry->flags & __EXEC_OBJECT_NEEDS_MAP && !obj->map_and_fenceable)
+       if (entry->flags & __EXEC_OBJECT_NEEDS_MAP &&
+           !i915_vma_is_map_and_fenceable(vma))
                return !only_mappable_for_reloc(entry->flags);
 
        if ((entry->flags & EXEC_OBJECT_SUPPORTS_48B_ADDRESS) == 0 &&
@@ -725,8 +901,6 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *engine,
        bool has_fenced_gpu_access = INTEL_GEN(engine->i915) < 4;
        int retry;
 
-       i915_gem_retire_requests_ring(engine);
-
        vm = list_first_entry(vmas, struct i915_vma, exec_list)->vm;
 
        INIT_LIST_HEAD(&ordered_vmas);
@@ -746,7 +920,7 @@ i915_gem_execbuffer_reserve(struct intel_engine_cs *engine,
                        entry->flags &= ~EXEC_OBJECT_NEEDS_FENCE;
                need_fence =
                        entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
-                       obj->tiling_mode != I915_TILING_NONE;
+                       i915_gem_object_is_tiled(obj);
                need_mappable = need_fence || need_reloc_mappable(vma);
 
                if (entry->flags & EXEC_OBJECT_PINNED)
@@ -843,7 +1017,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
                vma = list_first_entry(&eb->vmas, struct i915_vma, exec_list);
                list_del_init(&vma->exec_list);
                i915_gem_execbuffer_unreserve_vma(vma);
-               drm_gem_object_unreference(&vma->obj->base);
+               i915_vma_put(vma);
        }
 
        mutex_unlock(&dev->struct_mutex);
@@ -937,23 +1111,45 @@ err:
        return ret;
 }
 
+static unsigned int eb_other_engines(struct drm_i915_gem_request *req)
+{
+       unsigned int mask;
+
+       mask = ~intel_engine_flag(req->engine) & I915_BO_ACTIVE_MASK;
+       mask <<= I915_BO_ACTIVE_SHIFT;
+
+       return mask;
+}
+
 static int
 i915_gem_execbuffer_move_to_gpu(struct drm_i915_gem_request *req,
                                struct list_head *vmas)
 {
-       const unsigned other_rings = ~intel_engine_flag(req->engine);
+       const unsigned int other_rings = eb_other_engines(req);
        struct i915_vma *vma;
        int ret;
 
        list_for_each_entry(vma, vmas, exec_list) {
                struct drm_i915_gem_object *obj = vma->obj;
+               struct reservation_object *resv;
 
-               if (obj->active & other_rings) {
-                       ret = i915_gem_object_sync(obj, req->engine, &req);
+               if (obj->flags & other_rings) {
+                       ret = i915_gem_request_await_object
+                               (req, obj, obj->base.pending_write_domain);
                        if (ret)
                                return ret;
                }
 
+               resv = i915_gem_object_get_dmabuf_resv(obj);
+               if (resv) {
+                       ret = i915_sw_fence_await_reservation
+                               (&req->submit, resv, &i915_fence_ops,
+                                obj->base.pending_write_domain, 10*HZ,
+                                GFP_KERNEL | __GFP_NOWARN);
+                       if (ret < 0)
+                               return ret;
+               }
+
                if (obj->base.write_domain & I915_GEM_DOMAIN_CPU)
                        i915_gem_clflush_object(obj, false);
        }
@@ -961,10 +1157,8 @@ i915_gem_execbuffer_move_to_gpu(struct drm_i915_gem_request *req,
        /* Unconditionally flush any chipset caches (for streaming writes). */
        i915_gem_chipset_flush(req->engine->i915);
 
-       /* Unconditionally invalidate gpu caches and ensure that we do flush
-        * any residual writes from the previous batch.
-        */
-       return intel_ring_invalidate_all_caches(req);
+       /* Unconditionally invalidate GPU caches and TLBs. */
+       return req->engine->emit_flush(req, EMIT_INVALIDATE);
 }
 
 static bool
@@ -1000,6 +1194,9 @@ validate_exec_list(struct drm_device *dev,
        unsigned invalid_flags;
        int i;
 
+       /* INTERNAL flags must not overlap with external ones */
+       BUILD_BUG_ON(__EXEC_OBJECT_INTERNAL_FLAGS & ~__EXEC_OBJECT_UNKNOWN_FLAGS);
+
        invalid_flags = __EXEC_OBJECT_UNKNOWN_FLAGS;
        if (USES_FULL_PPGTT(dev))
                invalid_flags |= EXEC_OBJECT_NEEDS_GTT;
@@ -1029,6 +1226,14 @@ validate_exec_list(struct drm_device *dev,
                if (exec[i].alignment && !is_power_of_2(exec[i].alignment))
                        return -EINVAL;
 
+               /* pad_to_size was once a reserved field, so sanitize it */
+               if (exec[i].flags & EXEC_OBJECT_PAD_TO_SIZE) {
+                       if (offset_in_page(exec[i].pad_to_size))
+                               return -EINVAL;
+               } else {
+                       exec[i].pad_to_size = 0;
+               }
+
                /* First check for malicious input causing overflow in
                 * the worst case where we need to allocate the entire
                 * relocation tree as a single array.
@@ -1060,12 +1265,9 @@ static struct i915_gem_context *
 i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
                          struct intel_engine_cs *engine, const u32 ctx_id)
 {
-       struct i915_gem_context *ctx = NULL;
+       struct i915_gem_context *ctx;
        struct i915_ctx_hang_stats *hs;
 
-       if (engine->id != RCS && ctx_id != DEFAULT_CONTEXT_HANDLE)
-               return ERR_PTR(-EINVAL);
-
        ctx = i915_gem_context_lookup(file->driver_priv, ctx_id);
        if (IS_ERR(ctx))
                return ctx;
@@ -1079,66 +1281,99 @@ i915_gem_validate_context(struct drm_device *dev, struct drm_file *file,
        return ctx;
 }
 
-void
+void i915_vma_move_to_active(struct i915_vma *vma,
+                            struct drm_i915_gem_request *req,
+                            unsigned int flags)
+{
+       struct drm_i915_gem_object *obj = vma->obj;
+       const unsigned int idx = req->engine->id;
+
+       GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
+
+       obj->dirty = 1; /* be paranoid  */
+
+       /* Add a reference if we're newly entering the active list.
+        * The order in which we add operations to the retirement queue is
+        * vital here: mark_active adds to the start of the callback list,
+        * such that subsequent callbacks are called first. Therefore we
+        * add the active reference first and queue for it to be dropped
+        * *last*.
+        */
+       if (!i915_gem_object_is_active(obj))
+               i915_gem_object_get(obj);
+       i915_gem_object_set_active(obj, idx);
+       i915_gem_active_set(&obj->last_read[idx], req);
+
+       if (flags & EXEC_OBJECT_WRITE) {
+               i915_gem_active_set(&obj->last_write, req);
+
+               intel_fb_obj_invalidate(obj, ORIGIN_CS);
+
+               /* update for the implicit flush after a batch */
+               obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
+       }
+
+       if (flags & EXEC_OBJECT_NEEDS_FENCE)
+               i915_gem_active_set(&vma->last_fence, req);
+
+       i915_vma_set_active(vma, idx);
+       i915_gem_active_set(&vma->last_read[idx], req);
+       list_move_tail(&vma->vm_link, &vma->vm->active_list);
+}
+
+static void eb_export_fence(struct drm_i915_gem_object *obj,
+                           struct drm_i915_gem_request *req,
+                           unsigned int flags)
+{
+       struct reservation_object *resv;
+
+       resv = i915_gem_object_get_dmabuf_resv(obj);
+       if (!resv)
+               return;
+
+       /* Ignore errors from failing to allocate the new fence, we can't
+        * handle an error right now. Worst case should be missed
+        * synchronisation leading to rendering corruption.
+        */
+       ww_mutex_lock(&resv->lock, NULL);
+       if (flags & EXEC_OBJECT_WRITE)
+               reservation_object_add_excl_fence(resv, &req->fence);
+       else if (reservation_object_reserve_shared(resv) == 0)
+               reservation_object_add_shared_fence(resv, &req->fence);
+       ww_mutex_unlock(&resv->lock);
+}
+
+static void
 i915_gem_execbuffer_move_to_active(struct list_head *vmas,
                                   struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = i915_gem_request_get_engine(req);
        struct i915_vma *vma;
 
        list_for_each_entry(vma, vmas, exec_list) {
-               struct drm_i915_gem_exec_object2 *entry = vma->exec_entry;
                struct drm_i915_gem_object *obj = vma->obj;
                u32 old_read = obj->base.read_domains;
                u32 old_write = obj->base.write_domain;
 
-               obj->dirty = 1; /* be paranoid  */
                obj->base.write_domain = obj->base.pending_write_domain;
-               if (obj->base.write_domain == 0)
+               if (obj->base.write_domain)
+                       vma->exec_entry->flags |= EXEC_OBJECT_WRITE;
+               else
                        obj->base.pending_read_domains |= obj->base.read_domains;
                obj->base.read_domains = obj->base.pending_read_domains;
 
-               i915_vma_move_to_active(vma, req);
-               if (obj->base.write_domain) {
-                       i915_gem_request_assign(&obj->last_write_req, req);
-
-                       intel_fb_obj_invalidate(obj, ORIGIN_CS);
-
-                       /* update for the implicit flush after a batch */
-                       obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS;
-               }
-               if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
-                       i915_gem_request_assign(&obj->last_fenced_req, req);
-                       if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
-                               struct drm_i915_private *dev_priv = engine->i915;
-                               list_move_tail(&dev_priv->fence_regs[obj->fence_reg].lru_list,
-                                              &dev_priv->mm.fence_list);
-                       }
-               }
-
+               i915_vma_move_to_active(vma, req, vma->exec_entry->flags);
+               eb_export_fence(obj, req, vma->exec_entry->flags);
                trace_i915_gem_object_change_domain(obj, old_read, old_write);
        }
 }
 
-static void
-i915_gem_execbuffer_retire_commands(struct i915_execbuffer_params *params)
-{
-       /* Unconditionally force add_request to emit a full flush. */
-       params->engine->gpu_caches_dirty = true;
-
-       /* Add a breadcrumb for the completion of the batch buffer */
-       __i915_add_request(params->request, params->batch_obj, true);
-}
-
 static int
-i915_reset_gen7_sol_offsets(struct drm_device *dev,
-                           struct drm_i915_gem_request *req)
+i915_reset_gen7_sol_offsets(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_ring *ring = req->ring;
        int ret, i;
 
-       if (!IS_GEN7(dev) || engine != &dev_priv->engine[RCS]) {
+       if (!IS_GEN7(req->i915) || req->engine->id != RCS) {
                DRM_DEBUG("sol reset is gen7/rcs only\n");
                return -EINVAL;
        }
@@ -1148,21 +1383,21 @@ i915_reset_gen7_sol_offsets(struct drm_device *dev,
                return ret;
 
        for (i = 0; i < 4; i++) {
-               intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1));
-               intel_ring_emit_reg(engine, GEN7_SO_WRITE_OFFSET(i));
-               intel_ring_emit(engine, 0);
+               intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+               intel_ring_emit_reg(ring, GEN7_SO_WRITE_OFFSET(i));
+               intel_ring_emit(ring, 0);
        }
 
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
-static struct drm_i915_gem_object*
+static struct i915_vma *
 i915_gem_execbuffer_parse(struct intel_engine_cs *engine,
                          struct drm_i915_gem_exec_object2 *shadow_exec_entry,
-                         struct eb_vmas *eb,
                          struct drm_i915_gem_object *batch_obj,
+                         struct eb_vmas *eb,
                          u32 batch_start_offset,
                          u32 batch_len,
                          bool is_master)
@@ -1174,51 +1409,44 @@ i915_gem_execbuffer_parse(struct intel_engine_cs *engine,
        shadow_batch_obj = i915_gem_batch_pool_get(&engine->batch_pool,
                                                   PAGE_ALIGN(batch_len));
        if (IS_ERR(shadow_batch_obj))
-               return shadow_batch_obj;
-
-       ret = i915_parse_cmds(engine,
-                             batch_obj,
-                             shadow_batch_obj,
-                             batch_start_offset,
-                             batch_len,
-                             is_master);
-       if (ret)
-               goto err;
-
-       ret = i915_gem_obj_ggtt_pin(shadow_batch_obj, 0, 0);
-       if (ret)
-               goto err;
+               return ERR_CAST(shadow_batch_obj);
+
+       ret = intel_engine_cmd_parser(engine,
+                                     batch_obj,
+                                     shadow_batch_obj,
+                                     batch_start_offset,
+                                     batch_len,
+                                     is_master);
+       if (ret) {
+               if (ret == -EACCES) /* unhandled chained batch */
+                       vma = NULL;
+               else
+                       vma = ERR_PTR(ret);
+               goto out;
+       }
 
-       i915_gem_object_unpin_pages(shadow_batch_obj);
+       vma = i915_gem_object_ggtt_pin(shadow_batch_obj, NULL, 0, 0, 0);
+       if (IS_ERR(vma))
+               goto out;
 
        memset(shadow_exec_entry, 0, sizeof(*shadow_exec_entry));
 
-       vma = i915_gem_obj_to_ggtt(shadow_batch_obj);
        vma->exec_entry = shadow_exec_entry;
        vma->exec_entry->flags = __EXEC_OBJECT_HAS_PIN;
-       drm_gem_object_reference(&shadow_batch_obj->base);
+       i915_gem_object_get(shadow_batch_obj);
        list_add_tail(&vma->exec_list, &eb->vmas);
 
-       shadow_batch_obj->base.pending_read_domains = I915_GEM_DOMAIN_COMMAND;
-
-       return shadow_batch_obj;
-
-err:
+out:
        i915_gem_object_unpin_pages(shadow_batch_obj);
-       if (ret == -EACCES) /* unhandled chained batch */
-               return batch_obj;
-       else
-               return ERR_PTR(ret);
+       return vma;
 }
 
-int
-i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
-                              struct drm_i915_gem_execbuffer2 *args,
-                              struct list_head *vmas)
+static int
+execbuf_submit(struct i915_execbuffer_params *params,
+              struct drm_i915_gem_execbuffer2 *args,
+              struct list_head *vmas)
 {
-       struct drm_device *dev = params->dev;
-       struct intel_engine_cs *engine = params->engine;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = params->request->i915;
        u64 exec_start, exec_len;
        int instp_mode;
        u32 instp_mask;
@@ -1232,34 +1460,31 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
        if (ret)
                return ret;
 
-       WARN(params->ctx->ppgtt && params->ctx->ppgtt->pd_dirty_rings & (1<<engine->id),
-            "%s didn't clear reload\n", engine->name);
-
        instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
        instp_mask = I915_EXEC_CONSTANTS_MASK;
        switch (instp_mode) {
        case I915_EXEC_CONSTANTS_REL_GENERAL:
        case I915_EXEC_CONSTANTS_ABSOLUTE:
        case I915_EXEC_CONSTANTS_REL_SURFACE:
-               if (instp_mode != 0 && engine != &dev_priv->engine[RCS]) {
+               if (instp_mode != 0 && params->engine->id != RCS) {
                        DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
                        return -EINVAL;
                }
 
                if (instp_mode != dev_priv->relative_constants_mode) {
-                       if (INTEL_INFO(dev)->gen < 4) {
+                       if (INTEL_INFO(dev_priv)->gen < 4) {
                                DRM_DEBUG("no rel constants on pre-gen4\n");
                                return -EINVAL;
                        }
 
-                       if (INTEL_INFO(dev)->gen > 5 &&
+                       if (INTEL_INFO(dev_priv)->gen > 5 &&
                            instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
                                DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
                                return -EINVAL;
                        }
 
                        /* The HW changed the meaning on this bit on gen6 */
-                       if (INTEL_INFO(dev)->gen >= 6)
+                       if (INTEL_INFO(dev_priv)->gen >= 6)
                                instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
                }
                break;
@@ -1268,37 +1493,39 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
                return -EINVAL;
        }
 
-       if (engine == &dev_priv->engine[RCS] &&
+       if (params->engine->id == RCS &&
            instp_mode != dev_priv->relative_constants_mode) {
+               struct intel_ring *ring = params->request->ring;
+
                ret = intel_ring_begin(params->request, 4);
                if (ret)
                        return ret;
 
-               intel_ring_emit(engine, MI_NOOP);
-               intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1));
-               intel_ring_emit_reg(engine, INSTPM);
-               intel_ring_emit(engine, instp_mask << 16 | instp_mode);
-               intel_ring_advance(engine);
+               intel_ring_emit(ring, MI_NOOP);
+               intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+               intel_ring_emit_reg(ring, INSTPM);
+               intel_ring_emit(ring, instp_mask << 16 | instp_mode);
+               intel_ring_advance(ring);
 
                dev_priv->relative_constants_mode = instp_mode;
        }
 
        if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
-               ret = i915_reset_gen7_sol_offsets(dev, params->request);
+               ret = i915_reset_gen7_sol_offsets(params->request);
                if (ret)
                        return ret;
        }
 
        exec_len   = args->batch_len;
-       exec_start = params->batch_obj_vm_offset +
+       exec_start = params->batch->node.start +
                     params->args_batch_start_offset;
 
        if (exec_len == 0)
-               exec_len = params->batch_obj->base.size;
+               exec_len = params->batch->size - params->args_batch_start_offset;
 
-       ret = engine->dispatch_execbuffer(params->request,
-                                       exec_start, exec_len,
-                                       params->dispatch_flags);
+       ret = params->engine->emit_bb_start(params->request,
+                                           exec_start, exec_len,
+                                           params->dispatch_flags);
        if (ret)
                return ret;
 
@@ -1311,43 +1538,20 @@ i915_gem_ringbuffer_submission(struct i915_execbuffer_params *params,
 
 /**
  * Find one BSD ring to dispatch the corresponding BSD command.
- * The ring index is returned.
+ * The engine index is returned.
  */
 static unsigned int
-gen8_dispatch_bsd_ring(struct drm_i915_private *dev_priv, struct drm_file *file)
+gen8_dispatch_bsd_engine(struct drm_i915_private *dev_priv,
+                        struct drm_file *file)
 {
        struct drm_i915_file_private *file_priv = file->driver_priv;
 
        /* Check whether the file_priv has already selected one ring. */
-       if ((int)file_priv->bsd_ring < 0) {
-               /* If not, use the ping-pong mechanism to select one. */
-               mutex_lock(&dev_priv->drm.struct_mutex);
-               file_priv->bsd_ring = dev_priv->mm.bsd_ring_dispatch_index;
-               dev_priv->mm.bsd_ring_dispatch_index ^= 1;
-               mutex_unlock(&dev_priv->drm.struct_mutex);
-       }
-
-       return file_priv->bsd_ring;
-}
-
-static struct drm_i915_gem_object *
-eb_get_batch(struct eb_vmas *eb)
-{
-       struct i915_vma *vma = list_entry(eb->vmas.prev, typeof(*vma), exec_list);
+       if ((int)file_priv->bsd_engine < 0)
+               file_priv->bsd_engine = atomic_fetch_xor(1,
+                        &dev_priv->mm.bsd_engine_dispatch_index);
 
-       /*
-        * SNA is doing fancy tricks with compressing batch buffers, which leads
-        * to negative relocation deltas. Usually that works out ok since the
-        * relocate address is still positive, except when the batch is placed
-        * very low in the GTT. Ensure this doesn't happen.
-        *
-        * Note that actual hangs have only been observed on gen7, but for
-        * paranoia do it everywhere.
-        */
-       if ((vma->exec_entry->flags & EXEC_OBJECT_PINNED) == 0)
-               vma->exec_entry->flags |= __EXEC_OBJECT_NEEDS_BIAS;
-
-       return vma->obj;
+       return file_priv->bsd_engine;
 }
 
 #define I915_USER_RINGS (4)
@@ -1360,31 +1564,31 @@ static const enum intel_engine_id user_ring_map[I915_USER_RINGS + 1] = {
        [I915_EXEC_VEBOX]       = VECS
 };
 
-static int
-eb_select_ring(struct drm_i915_private *dev_priv,
-              struct drm_file *file,
-              struct drm_i915_gem_execbuffer2 *args,
-              struct intel_engine_cs **ring)
+static struct intel_engine_cs *
+eb_select_engine(struct drm_i915_private *dev_priv,
+                struct drm_file *file,
+                struct drm_i915_gem_execbuffer2 *args)
 {
        unsigned int user_ring_id = args->flags & I915_EXEC_RING_MASK;
+       struct intel_engine_cs *engine;
 
        if (user_ring_id > I915_USER_RINGS) {
                DRM_DEBUG("execbuf with unknown ring: %u\n", user_ring_id);
-               return -EINVAL;
+               return NULL;
        }
 
        if ((user_ring_id != I915_EXEC_BSD) &&
            ((args->flags & I915_EXEC_BSD_MASK) != 0)) {
                DRM_DEBUG("execbuf with non bsd ring but with invalid "
                          "bsd dispatch flags: %d\n", (int)(args->flags));
-               return -EINVAL;
+               return NULL;
        }
 
        if (user_ring_id == I915_EXEC_BSD && HAS_BSD2(dev_priv)) {
                unsigned int bsd_idx = args->flags & I915_EXEC_BSD_MASK;
 
                if (bsd_idx == I915_EXEC_BSD_DEFAULT) {
-                       bsd_idx = gen8_dispatch_bsd_ring(dev_priv, file);
+                       bsd_idx = gen8_dispatch_bsd_engine(dev_priv, file);
                } else if (bsd_idx >= I915_EXEC_BSD_RING1 &&
                           bsd_idx <= I915_EXEC_BSD_RING2) {
                        bsd_idx >>= I915_EXEC_BSD_SHIFT;
@@ -1392,20 +1596,20 @@ eb_select_ring(struct drm_i915_private *dev_priv,
                } else {
                        DRM_DEBUG("execbuf with unknown bsd ring: %u\n",
                                  bsd_idx);
-                       return -EINVAL;
+                       return NULL;
                }
 
-               *ring = &dev_priv->engine[_VCS(bsd_idx)];
+               engine = &dev_priv->engine[_VCS(bsd_idx)];
        } else {
-               *ring = &dev_priv->engine[user_ring_map[user_ring_id]];
+               engine = &dev_priv->engine[user_ring_map[user_ring_id]];
        }
 
-       if (!intel_engine_initialized(*ring)) {
+       if (!intel_engine_initialized(engine)) {
                DRM_DEBUG("execbuf with invalid ring: %u\n", user_ring_id);
-               return -EINVAL;
+               return NULL;
        }
 
-       return 0;
+       return engine;
 }
 
 static int
@@ -1416,9 +1620,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct drm_i915_gem_request *req = NULL;
        struct eb_vmas *eb;
-       struct drm_i915_gem_object *batch_obj;
        struct drm_i915_gem_exec_object2 shadow_exec_entry;
        struct intel_engine_cs *engine;
        struct i915_gem_context *ctx;
@@ -1447,9 +1649,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        if (args->flags & I915_EXEC_IS_PINNED)
                dispatch_flags |= I915_DISPATCH_PINNED;
 
-       ret = eb_select_ring(dev_priv, file, args, &engine);
-       if (ret)
-               return ret;
+       engine = eb_select_engine(dev_priv, file, args);
+       if (!engine)
+               return -EINVAL;
 
        if (args->buffer_count < 1) {
                DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count);
@@ -1489,7 +1691,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                goto pre_mutex_err;
        }
 
-       i915_gem_context_reference(ctx);
+       i915_gem_context_get(ctx);
 
        if (ctx->ppgtt)
                vm = &ctx->ppgtt->base;
@@ -1498,9 +1700,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
 
        memset(&params_master, 0x00, sizeof(params_master));
 
-       eb = eb_create(args);
+       eb = eb_create(dev_priv, args);
        if (eb == NULL) {
-               i915_gem_context_unreference(ctx);
+               i915_gem_context_put(ctx);
                mutex_unlock(&dev->struct_mutex);
                ret = -ENOMEM;
                goto pre_mutex_err;
@@ -1512,7 +1714,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                goto err;
 
        /* take note of the batch buffer before we might reorder the lists */
-       batch_obj = eb_get_batch(eb);
+       params->batch = eb_get_batch(eb);
 
        /* Move the objects en-masse into the GTT, evicting if necessary. */
        need_relocs = (args->flags & I915_EXEC_NO_RELOC) == 0;
@@ -1536,34 +1738,34 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        }
 
        /* Set the pending read domains for the batch buffer to COMMAND */
-       if (batch_obj->base.pending_write_domain) {
+       if (params->batch->obj->base.pending_write_domain) {
                DRM_DEBUG("Attempting to use self-modifying batch buffer\n");
                ret = -EINVAL;
                goto err;
        }
+       if (args->batch_start_offset > params->batch->size ||
+           args->batch_len > params->batch->size - args->batch_start_offset) {
+               DRM_DEBUG("Attempting to use out-of-bounds batch\n");
+               ret = -EINVAL;
+               goto err;
+       }
 
        params->args_batch_start_offset = args->batch_start_offset;
-       if (i915_needs_cmd_parser(engine) && args->batch_len) {
-               struct drm_i915_gem_object *parsed_batch_obj;
-
-               parsed_batch_obj = i915_gem_execbuffer_parse(engine,
-                                                            &shadow_exec_entry,
-                                                            eb,
-                                                            batch_obj,
-                                                            args->batch_start_offset,
-                                                            args->batch_len,
-                                                            drm_is_current_master(file));
-               if (IS_ERR(parsed_batch_obj)) {
-                       ret = PTR_ERR(parsed_batch_obj);
+       if (intel_engine_needs_cmd_parser(engine) && args->batch_len) {
+               struct i915_vma *vma;
+
+               vma = i915_gem_execbuffer_parse(engine, &shadow_exec_entry,
+                                               params->batch->obj,
+                                               eb,
+                                               args->batch_start_offset,
+                                               args->batch_len,
+                                               drm_is_current_master(file));
+               if (IS_ERR(vma)) {
+                       ret = PTR_ERR(vma);
                        goto err;
                }
 
-               /*
-                * parsed_batch_obj == batch_obj means batch not fully parsed:
-                * Accept, but don't promote to secure.
-                */
-
-               if (parsed_batch_obj != batch_obj) {
+               if (vma) {
                        /*
                         * Batch parsed and accepted:
                         *
@@ -1575,16 +1777,19 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                         */
                        dispatch_flags |= I915_DISPATCH_SECURE;
                        params->args_batch_start_offset = 0;
-                       batch_obj = parsed_batch_obj;
+                       params->batch = vma;
                }
        }
 
-       batch_obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
+       params->batch->obj->base.pending_read_domains |= I915_GEM_DOMAIN_COMMAND;
 
        /* snb/ivb/vlv conflate the "batch in ppgtt" bit with the "non-secure
         * batch" bit. Hence we need to pin secure batches into the global gtt.
         * hsw should have this fixed, but bdw mucks it up again. */
        if (dispatch_flags & I915_DISPATCH_SECURE) {
+               struct drm_i915_gem_object *obj = params->batch->obj;
+               struct i915_vma *vma;
+
                /*
                 * So on first glance it looks freaky that we pin the batch here
                 * outside of the reservation loop. But:
@@ -1595,22 +1800,31 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
                 *   fitting due to fragmentation.
                 * So this is actually safe.
                 */
-               ret = i915_gem_obj_ggtt_pin(batch_obj, 0, 0);
-               if (ret)
+               vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);
+               if (IS_ERR(vma)) {
+                       ret = PTR_ERR(vma);
                        goto err;
+               }
 
-               params->batch_obj_vm_offset = i915_gem_obj_ggtt_offset(batch_obj);
-       } else
-               params->batch_obj_vm_offset = i915_gem_obj_offset(batch_obj, vm);
+               params->batch = vma;
+       }
 
        /* Allocate a request for this batch buffer nice and early. */
-       req = i915_gem_request_alloc(engine, ctx);
-       if (IS_ERR(req)) {
-               ret = PTR_ERR(req);
+       params->request = i915_gem_request_alloc(engine, ctx);
+       if (IS_ERR(params->request)) {
+               ret = PTR_ERR(params->request);
                goto err_batch_unpin;
        }
 
-       ret = i915_gem_request_add_to_client(req, file);
+       /* Whilst this request exists, batch_obj will be on the
+        * active_list, and so will hold the active reference. Only when this
+        * request is retired will the the batch_obj be moved onto the
+        * inactive_list and lose its active reference. Hence we do not need
+        * to explicitly hold another reference here.
+        */
+       params->request->batch = params->batch;
+
+       ret = i915_gem_request_add_to_client(params->request, file);
        if (ret)
                goto err_request;
 
@@ -1624,13 +1838,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
        params->file                    = file;
        params->engine                    = engine;
        params->dispatch_flags          = dispatch_flags;
-       params->batch_obj               = batch_obj;
        params->ctx                     = ctx;
-       params->request                 = req;
 
-       ret = dev_priv->gt.execbuf_submit(params, args, &eb->vmas);
+       ret = execbuf_submit(params, args, &eb->vmas);
 err_request:
-       i915_gem_execbuffer_retire_commands(params);
+       __i915_add_request(params->request, ret == 0);
 
 err_batch_unpin:
        /*
@@ -1640,11 +1852,10 @@ err_batch_unpin:
         * active.
         */
        if (dispatch_flags & I915_DISPATCH_SECURE)
-               i915_gem_object_ggtt_unpin(batch_obj);
-
+               i915_vma_unpin(params->batch);
 err:
        /* the request owns the ref now */
-       i915_gem_context_unreference(ctx);
+       i915_gem_context_put(ctx);
        eb_destroy(eb);
 
        mutex_unlock(&dev->struct_mutex);
index 251d7a9..8df1fa7 100644 (file)
  * CPU ptes into GTT mmaps (not the GTT ptes themselves) as needed.
  */
 
-static void i965_write_fence_reg(struct drm_device *dev, int reg,
-                                struct drm_i915_gem_object *obj)
+#define pipelined 0
+
+static void i965_write_fence_reg(struct drm_i915_fence_reg *fence,
+                                struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        i915_reg_t fence_reg_lo, fence_reg_hi;
        int fence_pitch_shift;
+       u64 val;
 
-       if (INTEL_INFO(dev)->gen >= 6) {
-               fence_reg_lo = FENCE_REG_GEN6_LO(reg);
-               fence_reg_hi = FENCE_REG_GEN6_HI(reg);
+       if (INTEL_INFO(fence->i915)->gen >= 6) {
+               fence_reg_lo = FENCE_REG_GEN6_LO(fence->id);
+               fence_reg_hi = FENCE_REG_GEN6_HI(fence->id);
                fence_pitch_shift = GEN6_FENCE_PITCH_SHIFT;
+
        } else {
-               fence_reg_lo = FENCE_REG_965_LO(reg);
-               fence_reg_hi = FENCE_REG_965_HI(reg);
+               fence_reg_lo = FENCE_REG_965_LO(fence->id);
+               fence_reg_hi = FENCE_REG_965_HI(fence->id);
                fence_pitch_shift = I965_FENCE_PITCH_SHIFT;
        }
 
-       /* To w/a incoherency with non-atomic 64-bit register updates,
-        * we split the 64-bit update into two 32-bit writes. In order
-        * for a partial fence not to be evaluated between writes, we
-        * precede the update with write to turn off the fence register,
-        * and only enable the fence as the last step.
-        *
-        * For extra levels of paranoia, we make sure each step lands
-        * before applying the next step.
-        */
-       I915_WRITE(fence_reg_lo, 0);
-       POSTING_READ(fence_reg_lo);
-
-       if (obj) {
-               u32 size = i915_gem_obj_ggtt_size(obj);
-               uint64_t val;
-
-               /* Adjust fence size to match tiled area */
-               if (obj->tiling_mode != I915_TILING_NONE) {
-                       uint32_t row_size = obj->stride *
-                               (obj->tiling_mode == I915_TILING_Y ? 32 : 8);
-                       size = (size / row_size) * row_size;
-               }
-
-               val = (uint64_t)((i915_gem_obj_ggtt_offset(obj) + size - 4096) &
-                                0xfffff000) << 32;
-               val |= i915_gem_obj_ggtt_offset(obj) & 0xfffff000;
-               val |= (uint64_t)((obj->stride / 128) - 1) << fence_pitch_shift;
-               if (obj->tiling_mode == I915_TILING_Y)
-                       val |= 1 << I965_FENCE_TILING_Y_SHIFT;
+       val = 0;
+       if (vma) {
+               unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
+               bool is_y_tiled = tiling == I915_TILING_Y;
+               unsigned int stride = i915_gem_object_get_stride(vma->obj);
+               u32 row_size = stride * (is_y_tiled ? 32 : 8);
+               u32 size = rounddown((u32)vma->node.size, row_size);
+
+               val = ((vma->node.start + size - 4096) & 0xfffff000) << 32;
+               val |= vma->node.start & 0xfffff000;
+               val |= (u64)((stride / 128) - 1) << fence_pitch_shift;
+               if (is_y_tiled)
+                       val |= BIT(I965_FENCE_TILING_Y_SHIFT);
                val |= I965_FENCE_REG_VALID;
+       }
 
-               I915_WRITE(fence_reg_hi, val >> 32);
-               POSTING_READ(fence_reg_hi);
+       if (!pipelined) {
+               struct drm_i915_private *dev_priv = fence->i915;
 
-               I915_WRITE(fence_reg_lo, val);
+               /* To w/a incoherency with non-atomic 64-bit register updates,
+                * we split the 64-bit update into two 32-bit writes. In order
+                * for a partial fence not to be evaluated between writes, we
+                * precede the update with write to turn off the fence register,
+                * and only enable the fence as the last step.
+                *
+                * For extra levels of paranoia, we make sure each step lands
+                * before applying the next step.
+                */
+               I915_WRITE(fence_reg_lo, 0);
+               POSTING_READ(fence_reg_lo);
+
+               I915_WRITE(fence_reg_hi, upper_32_bits(val));
+               I915_WRITE(fence_reg_lo, lower_32_bits(val));
                POSTING_READ(fence_reg_lo);
-       } else {
-               I915_WRITE(fence_reg_hi, 0);
-               POSTING_READ(fence_reg_hi);
        }
 }
 
-static void i915_write_fence_reg(struct drm_device *dev, int reg,
-                                struct drm_i915_gem_object *obj)
+static void i915_write_fence_reg(struct drm_i915_fence_reg *fence,
+                                struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        u32 val;
 
-       if (obj) {
-               u32 size = i915_gem_obj_ggtt_size(obj);
+       val = 0;
+       if (vma) {
+               unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
+               bool is_y_tiled = tiling == I915_TILING_Y;
+               unsigned int stride = i915_gem_object_get_stride(vma->obj);
                int pitch_val;
                int tile_width;
 
-               WARN((i915_gem_obj_ggtt_offset(obj) & ~I915_FENCE_START_MASK) ||
-                    (size & -size) != size ||
-                    (i915_gem_obj_ggtt_offset(obj) & (size - 1)),
-                    "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08x) aligned\n",
-                    i915_gem_obj_ggtt_offset(obj), obj->map_and_fenceable, size);
+               WARN((vma->node.start & ~I915_FENCE_START_MASK) ||
+                    !is_power_of_2(vma->node.size) ||
+                    (vma->node.start & (vma->node.size - 1)),
+                    "object 0x%08llx [fenceable? %d] not 1M or pot-size (0x%08llx) aligned\n",
+                    vma->node.start,
+                    i915_vma_is_map_and_fenceable(vma),
+                    vma->node.size);
 
-               if (obj->tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))
+               if (is_y_tiled && HAS_128_BYTE_Y_TILING(fence->i915))
                        tile_width = 128;
                else
                        tile_width = 512;
 
                /* Note: pitch better be a power of two tile widths */
-               pitch_val = obj->stride / tile_width;
+               pitch_val = stride / tile_width;
                pitch_val = ffs(pitch_val) - 1;
 
-               val = i915_gem_obj_ggtt_offset(obj);
-               if (obj->tiling_mode == I915_TILING_Y)
-                       val |= 1 << I830_FENCE_TILING_Y_SHIFT;
-               val |= I915_FENCE_SIZE_BITS(size);
+               val = vma->node.start;
+               if (is_y_tiled)
+                       val |= BIT(I830_FENCE_TILING_Y_SHIFT);
+               val |= I915_FENCE_SIZE_BITS(vma->node.size);
                val |= pitch_val << I830_FENCE_PITCH_SHIFT;
                val |= I830_FENCE_REG_VALID;
-       } else
-               val = 0;
+       }
+
+       if (!pipelined) {
+               struct drm_i915_private *dev_priv = fence->i915;
+               i915_reg_t reg = FENCE_REG(fence->id);
 
-       I915_WRITE(FENCE_REG(reg), val);
-       POSTING_READ(FENCE_REG(reg));
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+       }
 }
 
-static void i830_write_fence_reg(struct drm_device *dev, int reg,
-                               struct drm_i915_gem_object *obj)
+static void i830_write_fence_reg(struct drm_i915_fence_reg *fence,
+                                struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       uint32_t val;
+       u32 val;
 
-       if (obj) {
-               u32 size = i915_gem_obj_ggtt_size(obj);
-               uint32_t pitch_val;
+       val = 0;
+       if (vma) {
+               unsigned int tiling = i915_gem_object_get_tiling(vma->obj);
+               bool is_y_tiled = tiling == I915_TILING_Y;
+               unsigned int stride = i915_gem_object_get_stride(vma->obj);
+               u32 pitch_val;
 
-               WARN((i915_gem_obj_ggtt_offset(obj) & ~I830_FENCE_START_MASK) ||
-                    (size & -size) != size ||
-                    (i915_gem_obj_ggtt_offset(obj) & (size - 1)),
-                    "object 0x%08llx not 512K or pot-size 0x%08x aligned\n",
-                    i915_gem_obj_ggtt_offset(obj), size);
+               WARN((vma->node.start & ~I830_FENCE_START_MASK) ||
+                    !is_power_of_2(vma->node.size) ||
+                    (vma->node.start & (vma->node.size - 1)),
+                    "object 0x%08llx not 512K or pot-size 0x%08llx aligned\n",
+                    vma->node.start, vma->node.size);
 
-               pitch_val = obj->stride / 128;
+               pitch_val = stride / 128;
                pitch_val = ffs(pitch_val) - 1;
 
-               val = i915_gem_obj_ggtt_offset(obj);
-               if (obj->tiling_mode == I915_TILING_Y)
-                       val |= 1 << I830_FENCE_TILING_Y_SHIFT;
-               val |= I830_FENCE_SIZE_BITS(size);
+               val = vma->node.start;
+               if (is_y_tiled)
+                       val |= BIT(I830_FENCE_TILING_Y_SHIFT);
+               val |= I830_FENCE_SIZE_BITS(vma->node.size);
                val |= pitch_val << I830_FENCE_PITCH_SHIFT;
                val |= I830_FENCE_REG_VALID;
-       } else
-               val = 0;
+       }
 
-       I915_WRITE(FENCE_REG(reg), val);
-       POSTING_READ(FENCE_REG(reg));
-}
+       if (!pipelined) {
+               struct drm_i915_private *dev_priv = fence->i915;
+               i915_reg_t reg = FENCE_REG(fence->id);
 
-inline static bool i915_gem_object_needs_mb(struct drm_i915_gem_object *obj)
-{
-       return obj && obj->base.read_domains & I915_GEM_DOMAIN_GTT;
+               I915_WRITE(reg, val);
+               POSTING_READ(reg);
+       }
 }
 
-static void i915_gem_write_fence(struct drm_device *dev, int reg,
-                                struct drm_i915_gem_object *obj)
+static void fence_write(struct drm_i915_fence_reg *fence,
+                       struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       /* Ensure that all CPU reads are completed before installing a fence
-        * and all writes before removing the fence.
+       /* Previous access through the fence register is marshalled by
+        * the mb() inside the fault handlers (i915_gem_release_mmaps)
+        * and explicitly managed for internal users.
         */
-       if (i915_gem_object_needs_mb(dev_priv->fence_regs[reg].obj))
-               mb();
-
-       WARN(obj && (!obj->stride || !obj->tiling_mode),
-            "bogus fence setup with stride: 0x%x, tiling mode: %i\n",
-            obj->stride, obj->tiling_mode);
-
-       if (IS_GEN2(dev))
-               i830_write_fence_reg(dev, reg, obj);
-       else if (IS_GEN3(dev))
-               i915_write_fence_reg(dev, reg, obj);
-       else if (INTEL_INFO(dev)->gen >= 4)
-               i965_write_fence_reg(dev, reg, obj);
-
-       /* And similarly be paranoid that no direct access to this region
-        * is reordered to before the fence is installed.
+
+       if (IS_GEN2(fence->i915))
+               i830_write_fence_reg(fence, vma);
+       else if (IS_GEN3(fence->i915))
+               i915_write_fence_reg(fence, vma);
+       else
+               i965_write_fence_reg(fence, vma);
+
+       /* Access through the fenced region afterwards is
+        * ordered by the posting reads whilst writing the registers.
         */
-       if (i915_gem_object_needs_mb(obj))
-               mb();
-}
 
-static inline int fence_number(struct drm_i915_private *dev_priv,
-                              struct drm_i915_fence_reg *fence)
-{
-       return fence - dev_priv->fence_regs;
+       fence->dirty = false;
 }
 
-static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj,
-                                        struct drm_i915_fence_reg *fence,
-                                        bool enable)
+static int fence_update(struct drm_i915_fence_reg *fence,
+                       struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       int reg = fence_number(dev_priv, fence);
-
-       i915_gem_write_fence(obj->base.dev, reg, enable ? obj : NULL);
+       int ret;
 
-       if (enable) {
-               obj->fence_reg = reg;
-               fence->obj = obj;
-               list_move_tail(&fence->lru_list, &dev_priv->mm.fence_list);
-       } else {
-               obj->fence_reg = I915_FENCE_REG_NONE;
-               fence->obj = NULL;
-               list_del_init(&fence->lru_list);
-       }
-       obj->fence_dirty = false;
-}
+       if (vma) {
+               if (!i915_vma_is_map_and_fenceable(vma))
+                       return -EINVAL;
 
-static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj)
-{
-       if (obj->tiling_mode)
-               i915_gem_release_mmap(obj);
+               if (WARN(!i915_gem_object_get_stride(vma->obj) ||
+                        !i915_gem_object_get_tiling(vma->obj),
+                        "bogus fence setup with stride: 0x%x, tiling mode: %i\n",
+                        i915_gem_object_get_stride(vma->obj),
+                        i915_gem_object_get_tiling(vma->obj)))
+                       return -EINVAL;
 
-       /* As we do not have an associated fence register, we will force
-        * a tiling change if we ever need to acquire one.
-        */
-       obj->fence_dirty = false;
-       obj->fence_reg = I915_FENCE_REG_NONE;
-}
+               ret = i915_gem_active_retire(&vma->last_fence,
+                                            &vma->obj->base.dev->struct_mutex);
+               if (ret)
+                       return ret;
+       }
 
-static int
-i915_gem_object_wait_fence(struct drm_i915_gem_object *obj)
-{
-       if (obj->last_fenced_req) {
-               int ret = i915_wait_request(obj->last_fenced_req);
+       if (fence->vma) {
+               ret = i915_gem_active_retire(&fence->vma->last_fence,
+                                     &fence->vma->obj->base.dev->struct_mutex);
                if (ret)
                        return ret;
+       }
+
+       if (fence->vma && fence->vma != vma) {
+               /* Ensure that all userspace CPU access is completed before
+                * stealing the fence.
+                */
+               i915_gem_release_mmap(fence->vma->obj);
+
+               fence->vma->fence = NULL;
+               fence->vma = NULL;
+
+               list_move(&fence->link, &fence->i915->mm.fence_list);
+       }
+
+       fence_write(fence, vma);
+
+       if (vma) {
+               if (fence->vma != vma) {
+                       vma->fence = fence;
+                       fence->vma = vma;
+               }
 
-               i915_gem_request_assign(&obj->last_fenced_req, NULL);
+               list_move_tail(&fence->link, &fence->i915->mm.fence_list);
        }
 
        return 0;
 }
 
 /**
- * i915_gem_object_put_fence - force-remove fence for an object
- * @obj: object to map through a fence reg
+ * i915_vma_put_fence - force-remove fence for a VMA
+ * @vma: vma to map linearly (not through a fence reg)
  *
  * This function force-removes any fence from the given object, which is useful
  * if the kernel wants to do untiled GTT access.
@@ -284,70 +286,40 @@ i915_gem_object_wait_fence(struct drm_i915_gem_object *obj)
  * 0 on success, negative error code on failure.
  */
 int
-i915_gem_object_put_fence(struct drm_i915_gem_object *obj)
+i915_vma_put_fence(struct i915_vma *vma)
 {
-       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-       struct drm_i915_fence_reg *fence;
-       int ret;
+       struct drm_i915_fence_reg *fence = vma->fence;
 
-       ret = i915_gem_object_wait_fence(obj);
-       if (ret)
-               return ret;
-
-       if (obj->fence_reg == I915_FENCE_REG_NONE)
+       if (!fence)
                return 0;
 
-       fence = &dev_priv->fence_regs[obj->fence_reg];
-
-       if (WARN_ON(fence->pin_count))
+       if (fence->pin_count)
                return -EBUSY;
 
-       i915_gem_object_fence_lost(obj);
-       i915_gem_object_update_fence(obj, fence, false);
-
-       return 0;
+       return fence_update(fence, NULL);
 }
 
-static struct drm_i915_fence_reg *
-i915_find_fence_reg(struct drm_device *dev)
+static struct drm_i915_fence_reg *fence_find(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct drm_i915_fence_reg *reg, *avail;
-       int i;
-
-       /* First try to find a free reg */
-       avail = NULL;
-       for (i = 0; i < dev_priv->num_fence_regs; i++) {
-               reg = &dev_priv->fence_regs[i];
-               if (!reg->obj)
-                       return reg;
-
-               if (!reg->pin_count)
-                       avail = reg;
-       }
-
-       if (avail == NULL)
-               goto deadlock;
+       struct drm_i915_fence_reg *fence;
 
-       /* None available, try to steal one or wait for a user to finish */
-       list_for_each_entry(reg, &dev_priv->mm.fence_list, lru_list) {
-               if (reg->pin_count)
+       list_for_each_entry(fence, &dev_priv->mm.fence_list, link) {
+               if (fence->pin_count)
                        continue;
 
-               return reg;
+               return fence;
        }
 
-deadlock:
        /* Wait for completion of pending flips which consume fences */
-       if (intel_has_pending_fb_unpin(dev))
+       if (intel_has_pending_fb_unpin(&dev_priv->drm))
                return ERR_PTR(-EAGAIN);
 
        return ERR_PTR(-EDEADLK);
 }
 
 /**
- * i915_gem_object_get_fence - set up fencing for an object
- * @obj: object to map through a fence reg
+ * i915_vma_get_fence - set up fencing for a vma
+ * @vma: vma to map through a fence reg
  *
  * When mapping objects through the GTT, userspace wants to be able to write
  * to them without having to worry about swizzling if the object is tiled.
@@ -364,103 +336,27 @@ deadlock:
  * 0 on success, negative error code on failure.
  */
 int
-i915_gem_object_get_fence(struct drm_i915_gem_object *obj)
+i915_vma_get_fence(struct i915_vma *vma)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       bool enable = obj->tiling_mode != I915_TILING_NONE;
-       struct drm_i915_fence_reg *reg;
-       int ret;
-
-       /* Have we updated the tiling parameters upon the object and so
-        * will need to serialise the write to the associated fence register?
-        */
-       if (obj->fence_dirty) {
-               ret = i915_gem_object_wait_fence(obj);
-               if (ret)
-                       return ret;
-       }
+       struct drm_i915_fence_reg *fence;
+       struct i915_vma *set = i915_gem_object_is_tiled(vma->obj) ? vma : NULL;
 
        /* Just update our place in the LRU if our fence is getting reused. */
-       if (obj->fence_reg != I915_FENCE_REG_NONE) {
-               reg = &dev_priv->fence_regs[obj->fence_reg];
-               if (!obj->fence_dirty) {
-                       list_move_tail(&reg->lru_list,
-                                      &dev_priv->mm.fence_list);
+       if (vma->fence) {
+               fence = vma->fence;
+               if (!fence->dirty) {
+                       list_move_tail(&fence->link,
+                                      &fence->i915->mm.fence_list);
                        return 0;
                }
-       } else if (enable) {
-               if (WARN_ON(!obj->map_and_fenceable))
-                       return -EINVAL;
-
-               reg = i915_find_fence_reg(dev);
-               if (IS_ERR(reg))
-                       return PTR_ERR(reg);
-
-               if (reg->obj) {
-                       struct drm_i915_gem_object *old = reg->obj;
-
-                       ret = i915_gem_object_wait_fence(old);
-                       if (ret)
-                               return ret;
-
-                       i915_gem_object_fence_lost(old);
-               }
+       } else if (set) {
+               fence = fence_find(to_i915(vma->vm->dev));
+               if (IS_ERR(fence))
+                       return PTR_ERR(fence);
        } else
                return 0;
 
-       i915_gem_object_update_fence(obj, reg, enable);
-
-       return 0;
-}
-
-/**
- * i915_gem_object_pin_fence - pin fencing state
- * @obj: object to pin fencing for
- *
- * This pins the fencing state (whether tiled or untiled) to make sure the
- * object is ready to be used as a scanout target. Fencing status must be
- * synchronize first by calling i915_gem_object_get_fence():
- *
- * The resulting fence pin reference must be released again with
- * i915_gem_object_unpin_fence().
- *
- * Returns:
- *
- * True if the object has a fence, false otherwise.
- */
-bool
-i915_gem_object_pin_fence(struct drm_i915_gem_object *obj)
-{
-       if (obj->fence_reg != I915_FENCE_REG_NONE) {
-               struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-               struct i915_vma *ggtt_vma = i915_gem_obj_to_ggtt(obj);
-
-               WARN_ON(!ggtt_vma ||
-                       dev_priv->fence_regs[obj->fence_reg].pin_count >
-                       ggtt_vma->pin_count);
-               dev_priv->fence_regs[obj->fence_reg].pin_count++;
-               return true;
-       } else
-               return false;
-}
-
-/**
- * i915_gem_object_unpin_fence - unpin fencing state
- * @obj: object to unpin fencing for
- *
- * This releases the fence pin reference acquired through
- * i915_gem_object_pin_fence. It will handle both objects with and without an
- * attached fence correctly, callers do not need to distinguish this.
- */
-void
-i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
-{
-       if (obj->fence_reg != I915_FENCE_REG_NONE) {
-               struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
-               WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count <= 0);
-               dev_priv->fence_regs[obj->fence_reg].pin_count--;
-       }
+       return fence_update(fence, set);
 }
 
 /**
@@ -477,17 +373,16 @@ void i915_gem_restore_fences(struct drm_device *dev)
 
        for (i = 0; i < dev_priv->num_fence_regs; i++) {
                struct drm_i915_fence_reg *reg = &dev_priv->fence_regs[i];
+               struct i915_vma *vma = reg->vma;
 
                /*
                 * Commit delayed tiling changes if we have an object still
                 * attached to the fence, otherwise just clear the fence.
                 */
-               if (reg->obj) {
-                       i915_gem_object_update_fence(reg->obj, reg,
-                                                    reg->obj->tiling_mode);
-               } else {
-                       i915_gem_write_fence(dev, i, NULL);
-               }
+               if (vma && !i915_gem_object_is_tiled(vma->obj))
+                       vma = NULL;
+
+               fence_update(reg, vma);
        }
 }
 
index f38ceff..0bb4232 100644 (file)
@@ -32,6 +32,8 @@
 #include "i915_trace.h"
 #include "intel_drv.h"
 
+#define I915_GFP_DMA (GFP_KERNEL | __GFP_HIGHMEM)
+
 /**
  * DOC: Global GTT views
  *
@@ -173,11 +175,13 @@ static int ppgtt_bind_vma(struct i915_vma *vma,
 {
        u32 pte_flags = 0;
 
+       vma->pages = vma->obj->pages;
+
        /* Currently applicable only to VLV */
        if (vma->obj->gt_ro)
                pte_flags |= PTE_READ_ONLY;
 
-       vma->vm->insert_entries(vma->vm, vma->obj->pages, vma->node.start,
+       vma->vm->insert_entries(vma->vm, vma->pages, vma->node.start,
                                cache_level, pte_flags);
 
        return 0;
@@ -187,7 +191,7 @@ static void ppgtt_unbind_vma(struct i915_vma *vma)
 {
        vma->vm->clear_range(vma->vm,
                             vma->node.start,
-                            vma->obj->base.size,
+                            vma->size,
                             true);
 }
 
@@ -327,16 +331,16 @@ static gen6_pte_t iris_pte_encode(dma_addr_t addr,
 static int __setup_page_dma(struct drm_device *dev,
                            struct i915_page_dma *p, gfp_t flags)
 {
-       struct device *device = &dev->pdev->dev;
+       struct device *kdev = &dev->pdev->dev;
 
        p->page = alloc_page(flags);
        if (!p->page)
                return -ENOMEM;
 
-       p->daddr = dma_map_page(device,
+       p->daddr = dma_map_page(kdev,
                                p->page, 0, 4096, PCI_DMA_BIDIRECTIONAL);
 
-       if (dma_mapping_error(device, p->daddr)) {
+       if (dma_mapping_error(kdev, p->daddr)) {
                __free_page(p->page);
                return -EINVAL;
        }
@@ -346,15 +350,17 @@ static int __setup_page_dma(struct drm_device *dev,
 
 static int setup_page_dma(struct drm_device *dev, struct i915_page_dma *p)
 {
-       return __setup_page_dma(dev, p, GFP_KERNEL);
+       return __setup_page_dma(dev, p, I915_GFP_DMA);
 }
 
 static void cleanup_page_dma(struct drm_device *dev, struct i915_page_dma *p)
 {
+       struct pci_dev *pdev = dev->pdev;
+
        if (WARN_ON(!p->page))
                return;
 
-       dma_unmap_page(&dev->pdev->dev, p->daddr, 4096, PCI_DMA_BIDIRECTIONAL);
+       dma_unmap_page(&pdev->dev, p->daddr, 4096, PCI_DMA_BIDIRECTIONAL);
        __free_page(p->page);
        memset(p, 0, sizeof(*p));
 }
@@ -408,33 +414,18 @@ static void fill_page_dma_32(struct drm_device *dev, struct i915_page_dma *p,
        fill_page_dma(dev, p, v);
 }
 
-static struct i915_page_scratch *alloc_scratch_page(struct drm_device *dev)
+static int
+setup_scratch_page(struct drm_device *dev,
+                  struct i915_page_dma *scratch,
+                  gfp_t gfp)
 {
-       struct i915_page_scratch *sp;
-       int ret;
-
-       sp = kzalloc(sizeof(*sp), GFP_KERNEL);
-       if (sp == NULL)
-               return ERR_PTR(-ENOMEM);
-
-       ret = __setup_page_dma(dev, px_base(sp), GFP_DMA32 | __GFP_ZERO);
-       if (ret) {
-               kfree(sp);
-               return ERR_PTR(ret);
-       }
-
-       set_pages_uc(px_page(sp), 1);
-
-       return sp;
+       return __setup_page_dma(dev, scratch, gfp | __GFP_ZERO);
 }
 
-static void free_scratch_page(struct drm_device *dev,
-                             struct i915_page_scratch *sp)
+static void cleanup_scratch_page(struct drm_device *dev,
+                                struct i915_page_dma *scratch)
 {
-       set_pages_wb(px_page(sp), 1);
-
-       cleanup_px(dev, sp);
-       kfree(sp);
+       cleanup_page_dma(dev, scratch);
 }
 
 static struct i915_page_table *alloc_pt(struct drm_device *dev)
@@ -480,7 +471,7 @@ static void gen8_initialize_pt(struct i915_address_space *vm,
 {
        gen8_pte_t scratch_pte;
 
-       scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = gen8_pte_encode(vm->scratch_page.daddr,
                                      I915_CACHE_LLC, true);
 
        fill_px(vm->dev, pt, scratch_pte);
@@ -491,9 +482,9 @@ static void gen6_initialize_pt(struct i915_address_space *vm,
 {
        gen6_pte_t scratch_pte;
 
-       WARN_ON(px_dma(vm->scratch_page) == 0);
+       WARN_ON(vm->scratch_page.daddr == 0);
 
-       scratch_pte = vm->pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = vm->pte_encode(vm->scratch_page.daddr,
                                     I915_CACHE_LLC, true, 0);
 
        fill32_px(vm->dev, pt, scratch_pte);
@@ -672,6 +663,7 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req,
                          unsigned entry,
                          dma_addr_t addr)
 {
+       struct intel_ring *ring = req->ring;
        struct intel_engine_cs *engine = req->engine;
        int ret;
 
@@ -681,13 +673,13 @@ static int gen8_write_pdp(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1));
-       intel_ring_emit_reg(engine, GEN8_RING_PDP_UDW(engine, entry));
-       intel_ring_emit(engine, upper_32_bits(addr));
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1));
-       intel_ring_emit_reg(engine, GEN8_RING_PDP_LDW(engine, entry));
-       intel_ring_emit(engine, lower_32_bits(addr));
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+       intel_ring_emit_reg(ring, GEN8_RING_PDP_UDW(engine, entry));
+       intel_ring_emit(ring, upper_32_bits(addr));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+       intel_ring_emit_reg(ring, GEN8_RING_PDP_LDW(engine, entry));
+       intel_ring_emit(ring, lower_32_bits(addr));
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -776,7 +768,7 @@ static void gen8_ppgtt_clear_range(struct i915_address_space *vm,
                                   bool use_scratch)
 {
        struct i915_hw_ppgtt *ppgtt = i915_vm_to_ppgtt(vm);
-       gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+       gen8_pte_t scratch_pte = gen8_pte_encode(vm->scratch_page.daddr,
                                                 I915_CACHE_LLC, use_scratch);
 
        if (!USES_FULL_48BIT_PPGTT(vm->dev)) {
@@ -882,9 +874,9 @@ static int gen8_init_scratch(struct i915_address_space *vm)
        struct drm_device *dev = vm->dev;
        int ret;
 
-       vm->scratch_page = alloc_scratch_page(dev);
-       if (IS_ERR(vm->scratch_page))
-               return PTR_ERR(vm->scratch_page);
+       ret = setup_scratch_page(dev, &vm->scratch_page, I915_GFP_DMA);
+       if (ret)
+               return ret;
 
        vm->scratch_pt = alloc_pt(dev);
        if (IS_ERR(vm->scratch_pt)) {
@@ -918,7 +910,7 @@ free_pd:
 free_pt:
        free_pt(dev, vm->scratch_pt);
 free_scratch_page:
-       free_scratch_page(dev, vm->scratch_page);
+       cleanup_scratch_page(dev, &vm->scratch_page);
 
        return ret;
 }
@@ -962,7 +954,7 @@ static void gen8_free_scratch(struct i915_address_space *vm)
                free_pdp(dev, vm->scratch_pdp);
        free_pd(dev, vm->scratch_pd);
        free_pt(dev, vm->scratch_pt);
-       free_scratch_page(dev, vm->scratch_page);
+       cleanup_scratch_page(dev, &vm->scratch_page);
 }
 
 static void gen8_ppgtt_cleanup_3lvl(struct drm_device *dev,
@@ -1459,7 +1451,7 @@ static void gen8_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
        struct i915_address_space *vm = &ppgtt->base;
        uint64_t start = ppgtt->base.start;
        uint64_t length = ppgtt->base.total;
-       gen8_pte_t scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+       gen8_pte_t scratch_pte = gen8_pte_encode(vm->scratch_page.daddr,
                                                 I915_CACHE_LLC, true);
 
        if (!USES_FULL_48BIT_PPGTT(vm->dev)) {
@@ -1576,7 +1568,7 @@ static void gen6_dump_ppgtt(struct i915_hw_ppgtt *ppgtt, struct seq_file *m)
        uint32_t  pte, pde;
        uint32_t start = ppgtt->base.start, length = ppgtt->base.total;
 
-       scratch_pte = vm->pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = vm->pte_encode(vm->scratch_page.daddr,
                                     I915_CACHE_LLC, true, 0);
 
        gen6_for_each_pde(unused, &ppgtt->pd, start, length, pde) {
@@ -1663,11 +1655,12 @@ static uint32_t get_pd_offset(struct i915_hw_ppgtt *ppgtt)
 static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
                         struct drm_i915_gem_request *req)
 {
+       struct intel_ring *ring = req->ring;
        struct intel_engine_cs *engine = req->engine;
        int ret;
 
        /* NB: TLBs must be flushed and invalidated before a switch */
-       ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+       ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH);
        if (ret)
                return ret;
 
@@ -1675,13 +1668,13 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(2));
-       intel_ring_emit_reg(engine, RING_PP_DIR_DCLV(engine));
-       intel_ring_emit(engine, PP_DIR_DCLV_2G);
-       intel_ring_emit_reg(engine, RING_PP_DIR_BASE(engine));
-       intel_ring_emit(engine, get_pd_offset(ppgtt));
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2));
+       intel_ring_emit_reg(ring, RING_PP_DIR_DCLV(engine));
+       intel_ring_emit(ring, PP_DIR_DCLV_2G);
+       intel_ring_emit_reg(ring, RING_PP_DIR_BASE(engine));
+       intel_ring_emit(ring, get_pd_offset(ppgtt));
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -1689,11 +1682,12 @@ static int hsw_mm_switch(struct i915_hw_ppgtt *ppgtt,
 static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
                          struct drm_i915_gem_request *req)
 {
+       struct intel_ring *ring = req->ring;
        struct intel_engine_cs *engine = req->engine;
        int ret;
 
        /* NB: TLBs must be flushed and invalidated before a switch */
-       ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+       ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH);
        if (ret)
                return ret;
 
@@ -1701,17 +1695,17 @@ static int gen7_mm_switch(struct i915_hw_ppgtt *ppgtt,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(2));
-       intel_ring_emit_reg(engine, RING_PP_DIR_DCLV(engine));
-       intel_ring_emit(engine, PP_DIR_DCLV_2G);
-       intel_ring_emit_reg(engine, RING_PP_DIR_BASE(engine));
-       intel_ring_emit(engine, get_pd_offset(ppgtt));
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(2));
+       intel_ring_emit_reg(ring, RING_PP_DIR_DCLV(engine));
+       intel_ring_emit(ring, PP_DIR_DCLV_2G);
+       intel_ring_emit_reg(ring, RING_PP_DIR_BASE(engine));
+       intel_ring_emit(ring, get_pd_offset(ppgtt));
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        /* XXX: RCS is the only one to auto invalidate the TLBs? */
        if (engine->id != RCS) {
-               ret = engine->flush(req, I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS);
+               ret = engine->emit_flush(req, EMIT_INVALIDATE | EMIT_FLUSH);
                if (ret)
                        return ret;
        }
@@ -1799,7 +1793,7 @@ static void gen6_ppgtt_clear_range(struct i915_address_space *vm,
        unsigned first_pte = first_entry % GEN6_PTES;
        unsigned last_pte, i;
 
-       scratch_pte = vm->pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = vm->pte_encode(vm->scratch_page.daddr,
                                     I915_CACHE_LLC, true, 0);
 
        while (num_entries) {
@@ -1945,14 +1939,15 @@ unwind_out:
 static int gen6_init_scratch(struct i915_address_space *vm)
 {
        struct drm_device *dev = vm->dev;
+       int ret;
 
-       vm->scratch_page = alloc_scratch_page(dev);
-       if (IS_ERR(vm->scratch_page))
-               return PTR_ERR(vm->scratch_page);
+       ret = setup_scratch_page(dev, &vm->scratch_page, I915_GFP_DMA);
+       if (ret)
+               return ret;
 
        vm->scratch_pt = alloc_pt(dev);
        if (IS_ERR(vm->scratch_pt)) {
-               free_scratch_page(dev, vm->scratch_page);
+               cleanup_scratch_page(dev, &vm->scratch_page);
                return PTR_ERR(vm->scratch_pt);
        }
 
@@ -1966,7 +1961,7 @@ static void gen6_free_scratch(struct i915_address_space *vm)
        struct drm_device *dev = vm->dev;
 
        free_pt(dev, vm->scratch_pt);
-       free_scratch_page(dev, vm->scratch_page);
+       cleanup_scratch_page(dev, &vm->scratch_page);
 }
 
 static void gen6_ppgtt_cleanup(struct i915_address_space *vm)
@@ -2012,7 +2007,7 @@ alloc:
                                                  0, ggtt->base.total,
                                                  DRM_MM_TOPDOWN);
        if (ret == -ENOSPC && !retried) {
-               ret = i915_gem_evict_something(dev, &ggtt->base,
+               ret = i915_gem_evict_something(&ggtt->base,
                                               GEN6_PD_SIZE, GEN6_PD_ALIGN,
                                               I915_CACHE_NONE,
                                               0, ggtt->base.total,
@@ -2104,11 +2099,12 @@ static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
        return 0;
 }
 
-static int __hw_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+static int __hw_ppgtt_init(struct i915_hw_ppgtt *ppgtt,
+                          struct drm_i915_private *dev_priv)
 {
-       ppgtt->base.dev = dev;
+       ppgtt->base.dev = &dev_priv->drm;
 
-       if (INTEL_INFO(dev)->gen < 8)
+       if (INTEL_INFO(dev_priv)->gen < 8)
                return gen6_ppgtt_init(ppgtt);
        else
                return gen8_ppgtt_init(ppgtt);
@@ -2118,9 +2114,9 @@ static void i915_address_space_init(struct i915_address_space *vm,
                                    struct drm_i915_private *dev_priv)
 {
        drm_mm_init(&vm->mm, vm->start, vm->total);
-       vm->dev = &dev_priv->drm;
        INIT_LIST_HEAD(&vm->active_list);
        INIT_LIST_HEAD(&vm->inactive_list);
+       INIT_LIST_HEAD(&vm->unbound_list);
        list_add_tail(&vm->global_link, &dev_priv->vm_list);
 }
 
@@ -2143,15 +2139,17 @@ static void gtt_write_workarounds(struct drm_device *dev)
                I915_WRITE(GEN8_L3_LRA_1_GPGPU, GEN9_L3_LRA_1_GPGPU_DEFAULT_VALUE_BXT);
 }
 
-static int i915_ppgtt_init(struct drm_device *dev, struct i915_hw_ppgtt *ppgtt)
+static int i915_ppgtt_init(struct i915_hw_ppgtt *ppgtt,
+                          struct drm_i915_private *dev_priv,
+                          struct drm_i915_file_private *file_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret = 0;
+       int ret;
 
-       ret = __hw_ppgtt_init(dev, ppgtt);
+       ret = __hw_ppgtt_init(ppgtt, dev_priv);
        if (ret == 0) {
                kref_init(&ppgtt->ref);
                i915_address_space_init(&ppgtt->base, dev_priv);
+               ppgtt->base.file = file_priv;
        }
 
        return ret;
@@ -2183,7 +2181,8 @@ int i915_ppgtt_init_hw(struct drm_device *dev)
 }
 
 struct i915_hw_ppgtt *
-i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv)
+i915_ppgtt_create(struct drm_i915_private *dev_priv,
+                 struct drm_i915_file_private *fpriv)
 {
        struct i915_hw_ppgtt *ppgtt;
        int ret;
@@ -2192,14 +2191,12 @@ i915_ppgtt_create(struct drm_device *dev, struct drm_i915_file_private *fpriv)
        if (!ppgtt)
                return ERR_PTR(-ENOMEM);
 
-       ret = i915_ppgtt_init(dev, ppgtt);
+       ret = i915_ppgtt_init(ppgtt, dev_priv, fpriv);
        if (ret) {
                kfree(ppgtt);
                return ERR_PTR(ret);
        }
 
-       ppgtt->file_priv = fpriv;
-
        trace_i915_ppgtt_create(&ppgtt->base);
 
        return ppgtt;
@@ -2212,9 +2209,10 @@ void  i915_ppgtt_release(struct kref *kref)
 
        trace_i915_ppgtt_release(&ppgtt->base);
 
-       /* vmas should already be unbound */
+       /* vmas should already be unbound and destroyed */
        WARN_ON(!list_empty(&ppgtt->base.active_list));
        WARN_ON(!list_empty(&ppgtt->base.inactive_list));
+       WARN_ON(!list_empty(&ppgtt->base.unbound_list));
 
        list_del(&ppgtt->base.global_link);
        drm_mm_takedown(&ppgtt->base.mm);
@@ -2223,47 +2221,21 @@ void  i915_ppgtt_release(struct kref *kref)
        kfree(ppgtt);
 }
 
-extern int intel_iommu_gfx_mapped;
 /* Certain Gen5 chipsets require require idling the GPU before
  * unmapping anything from the GTT when VT-d is enabled.
  */
-static bool needs_idle_maps(struct drm_device *dev)
+static bool needs_idle_maps(struct drm_i915_private *dev_priv)
 {
 #ifdef CONFIG_INTEL_IOMMU
        /* Query intel_iommu to see if we need the workaround. Presumably that
         * was loaded first.
         */
-       if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped)
+       if (IS_GEN5(dev_priv) && IS_MOBILE(dev_priv) && intel_iommu_gfx_mapped)
                return true;
 #endif
        return false;
 }
 
-static bool do_idling(struct drm_i915_private *dev_priv)
-{
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       bool ret = dev_priv->mm.interruptible;
-
-       if (unlikely(ggtt->do_idle_maps)) {
-               dev_priv->mm.interruptible = false;
-               if (i915_gem_wait_for_idle(dev_priv)) {
-                       DRM_ERROR("Failed to wait for idle; VT'd may hang.\n");
-                       /* Wait a bit, in hopes it avoids the hang */
-                       udelay(10);
-               }
-       }
-
-       return ret;
-}
-
-static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
-{
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-
-       if (unlikely(ggtt->do_idle_maps))
-               dev_priv->mm.interruptible = interruptible;
-}
-
 void i915_check_and_clear_faults(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
@@ -2332,12 +2304,7 @@ int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
 
 static void gen8_set_pte(void __iomem *addr, gen8_pte_t pte)
 {
-#ifdef writeq
        writeq(pte, addr);
-#else
-       iowrite32((u32)pte, addr);
-       iowrite32(pte >> 32, addr + 4);
-#endif
 }
 
 static void gen8_ggtt_insert_page(struct i915_address_space *vm,
@@ -2530,7 +2497,7 @@ static void gen8_ggtt_clear_range(struct i915_address_space *vm,
                 first_entry, num_entries, max_entries))
                num_entries = max_entries;
 
-       scratch_pte = gen8_pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = gen8_pte_encode(vm->scratch_page.daddr,
                                      I915_CACHE_LLC,
                                      use_scratch);
        for (i = 0; i < num_entries; i++)
@@ -2562,7 +2529,7 @@ static void gen6_ggtt_clear_range(struct i915_address_space *vm,
                 first_entry, num_entries, max_entries))
                num_entries = max_entries;
 
-       scratch_pte = vm->pte_encode(px_dma(vm->scratch_page),
+       scratch_pte = vm->pte_encode(vm->scratch_page.daddr,
                                     I915_CACHE_LLC, use_scratch, 0);
 
        for (i = 0; i < num_entries; i++)
@@ -2641,8 +2608,7 @@ static int ggtt_bind_vma(struct i915_vma *vma,
        if (obj->gt_ro)
                pte_flags |= PTE_READ_ONLY;
 
-       vma->vm->insert_entries(vma->vm, vma->ggtt_view.pages,
-                               vma->node.start,
+       vma->vm->insert_entries(vma->vm, vma->pages, vma->node.start,
                                cache_level, pte_flags);
 
        /*
@@ -2650,7 +2616,7 @@ static int ggtt_bind_vma(struct i915_vma *vma,
         * GLOBAL/LOCAL_BIND, it's all the same ptes. Hence unconditionally
         * upgrade to both bound if we bind either to avoid double-binding.
         */
-       vma->bound |= GLOBAL_BIND | LOCAL_BIND;
+       vma->flags |= I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND;
 
        return 0;
 }
@@ -2672,19 +2638,17 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
                pte_flags |= PTE_READ_ONLY;
 
 
-       if (flags & GLOBAL_BIND) {
+       if (flags & I915_VMA_GLOBAL_BIND) {
                vma->vm->insert_entries(vma->vm,
-                                       vma->ggtt_view.pages,
-                                       vma->node.start,
+                                       vma->pages, vma->node.start,
                                        cache_level, pte_flags);
        }
 
-       if (flags & LOCAL_BIND) {
+       if (flags & I915_VMA_LOCAL_BIND) {
                struct i915_hw_ppgtt *appgtt =
                        to_i915(vma->vm->dev)->mm.aliasing_ppgtt;
                appgtt->base.insert_entries(&appgtt->base,
-                                           vma->ggtt_view.pages,
-                                           vma->node.start,
+                                           vma->pages, vma->node.start,
                                            cache_level, pte_flags);
        }
 
@@ -2693,42 +2657,36 @@ static int aliasing_gtt_bind_vma(struct i915_vma *vma,
 
 static void ggtt_unbind_vma(struct i915_vma *vma)
 {
-       struct drm_device *dev = vma->vm->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct drm_i915_gem_object *obj = vma->obj;
-       const uint64_t size = min_t(uint64_t,
-                                   obj->base.size,
-                                   vma->node.size);
+       struct i915_hw_ppgtt *appgtt = to_i915(vma->vm->dev)->mm.aliasing_ppgtt;
+       const u64 size = min(vma->size, vma->node.size);
 
-       if (vma->bound & GLOBAL_BIND) {
+       if (vma->flags & I915_VMA_GLOBAL_BIND)
                vma->vm->clear_range(vma->vm,
-                                    vma->node.start,
-                                    size,
+                                    vma->node.start, size,
                                     true);
-       }
-
-       if (dev_priv->mm.aliasing_ppgtt && vma->bound & LOCAL_BIND) {
-               struct i915_hw_ppgtt *appgtt = dev_priv->mm.aliasing_ppgtt;
 
+       if (vma->flags & I915_VMA_LOCAL_BIND && appgtt)
                appgtt->base.clear_range(&appgtt->base,
-                                        vma->node.start,
-                                        size,
+                                        vma->node.start, size,
                                         true);
-       }
 }
 
 void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       bool interruptible;
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
+       struct device *kdev = &dev_priv->drm.pdev->dev;
+       struct i915_ggtt *ggtt = &dev_priv->ggtt;
 
-       interruptible = do_idling(dev_priv);
+       if (unlikely(ggtt->do_idle_maps)) {
+               if (i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED)) {
+                       DRM_ERROR("Failed to wait for idle; VT'd may hang.\n");
+                       /* Wait a bit, in hopes it avoids the hang */
+                       udelay(10);
+               }
+       }
 
-       dma_unmap_sg(&dev->pdev->dev, obj->pages->sgl, obj->pages->nents,
+       dma_unmap_sg(kdev, obj->pages->sgl, obj->pages->nents,
                     PCI_DMA_BIDIRECTIONAL);
-
-       undo_idling(dev_priv, interruptible);
 }
 
 static void i915_gtt_color_adjust(struct drm_mm_node *node,
@@ -2739,19 +2697,14 @@ static void i915_gtt_color_adjust(struct drm_mm_node *node,
        if (node->color != color)
                *start += 4096;
 
-       if (!list_empty(&node->node_list)) {
-               node = list_entry(node->node_list.next,
-                                 struct drm_mm_node,
-                                 node_list);
-               if (node->allocated && node->color != color)
-                       *end -= 4096;
-       }
+       node = list_first_entry_or_null(&node->node_list,
+                                       struct drm_mm_node,
+                                       node_list);
+       if (node && node->allocated && node->color != color)
+               *end -= 4096;
 }
 
-static int i915_gem_setup_global_gtt(struct drm_device *dev,
-                                    u64 start,
-                                    u64 mappable_end,
-                                    u64 end)
+int i915_gem_init_ggtt(struct drm_i915_private *dev_priv)
 {
        /* Let GEM Manage all of the aperture.
         *
@@ -2762,48 +2715,15 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
         * aperture.  One page should be enough to keep any prefetching inside
         * of the aperture.
         */
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct drm_mm_node *entry;
-       struct drm_i915_gem_object *obj;
        unsigned long hole_start, hole_end;
+       struct drm_mm_node *entry;
        int ret;
 
-       BUG_ON(mappable_end > end);
-
-       ggtt->base.start = start;
-
-       /* Subtract the guard page before address space initialization to
-        * shrink the range used by drm_mm */
-       ggtt->base.total = end - start - PAGE_SIZE;
-       i915_address_space_init(&ggtt->base, dev_priv);
-       ggtt->base.total += PAGE_SIZE;
-
        ret = intel_vgt_balloon(dev_priv);
        if (ret)
                return ret;
 
-       if (!HAS_LLC(dev))
-               ggtt->base.mm.color_adjust = i915_gtt_color_adjust;
-
-       /* Mark any preallocated objects as occupied */
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               struct i915_vma *vma = i915_gem_obj_to_vma(obj, &ggtt->base);
-
-               DRM_DEBUG_KMS("reserving preallocated space: %llx + %zx\n",
-                             i915_gem_obj_ggtt_offset(obj), obj->base.size);
-
-               WARN_ON(i915_gem_obj_ggtt_bound(obj));
-               ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node);
-               if (ret) {
-                       DRM_DEBUG_KMS("Reservation failed: %i\n", ret);
-                       return ret;
-               }
-               vma->bound |= GLOBAL_BIND;
-               __i915_vma_set_map_and_fenceable(vma);
-               list_add_tail(&vma->vm_link, &ggtt->base.inactive_list);
-       }
-
        /* Clear any non-preallocated blocks */
        drm_mm_for_each_hole(entry, &ggtt->base.mm, hole_start, hole_end) {
                DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
@@ -2813,18 +2733,19 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
        }
 
        /* And finally clear the reserved guard page */
-       ggtt->base.clear_range(&ggtt->base, end - PAGE_SIZE, PAGE_SIZE, true);
+       ggtt->base.clear_range(&ggtt->base,
+                              ggtt->base.total - PAGE_SIZE, PAGE_SIZE,
+                              true);
 
-       if (USES_PPGTT(dev) && !USES_FULL_PPGTT(dev)) {
+       if (USES_PPGTT(dev_priv) && !USES_FULL_PPGTT(dev_priv)) {
                struct i915_hw_ppgtt *ppgtt;
 
                ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
                if (!ppgtt)
                        return -ENOMEM;
 
-               ret = __hw_ppgtt_init(dev, ppgtt);
+               ret = __hw_ppgtt_init(ppgtt, dev_priv);
                if (ret) {
-                       ppgtt->base.cleanup(&ppgtt->base);
                        kfree(ppgtt);
                        return ret;
                }
@@ -2851,35 +2772,21 @@ static int i915_gem_setup_global_gtt(struct drm_device *dev,
        return 0;
 }
 
-/**
- * i915_gem_init_ggtt - Initialize GEM for Global GTT
- * @dev: DRM device
- */
-void i915_gem_init_ggtt(struct drm_device *dev)
-{
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-
-       i915_gem_setup_global_gtt(dev, 0, ggtt->mappable_end, ggtt->base.total);
-}
-
 /**
  * i915_ggtt_cleanup_hw - Clean up GGTT hardware initialization
- * @dev: DRM device
+ * @dev_priv: i915 device
  */
-void i915_ggtt_cleanup_hw(struct drm_device *dev)
+void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
 
        if (dev_priv->mm.aliasing_ppgtt) {
                struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
-
                ppgtt->base.cleanup(&ppgtt->base);
                kfree(ppgtt);
        }
 
-       i915_gem_cleanup_stolen(dev);
+       i915_gem_cleanup_stolen(&dev_priv->drm);
 
        if (drm_mm_initialized(&ggtt->base.mm)) {
                intel_vgt_deballoon(dev_priv);
@@ -2889,6 +2796,9 @@ void i915_ggtt_cleanup_hw(struct drm_device *dev)
        }
 
        ggtt->base.cleanup(&ggtt->base);
+
+       arch_phys_wc_del(ggtt->mtrr);
+       io_mapping_fini(&ggtt->mappable);
 }
 
 static unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
@@ -2969,17 +2879,14 @@ static size_t gen9_get_stolen_size(u16 gen9_gmch_ctl)
                return (gen9_gmch_ctl - 0xf0 + 1) << 22;
 }
 
-static int ggtt_probe_common(struct drm_device *dev,
-                            size_t gtt_size)
+static int ggtt_probe_common(struct i915_ggtt *ggtt, u64 size)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct i915_page_scratch *scratch_page;
-       phys_addr_t ggtt_phys_addr;
+       struct pci_dev *pdev = ggtt->base.dev->pdev;
+       phys_addr_t phys_addr;
+       int ret;
 
        /* For Modern GENs the PTEs and register space are split in the BAR */
-       ggtt_phys_addr = pci_resource_start(dev->pdev, 0) +
-                        (pci_resource_len(dev->pdev, 0) / 2);
+       phys_addr = pci_resource_start(pdev, 0) + pci_resource_len(pdev, 0) / 2;
 
        /*
         * On BXT writes larger than 64 bit to the GTT pagetable range will be
@@ -2988,25 +2895,25 @@ static int ggtt_probe_common(struct drm_device *dev,
         * resort to an uncached mapping. The WC issue is easily caught by the
         * readback check when writing GTT PTE entries.
         */
-       if (IS_BROXTON(dev))
-               ggtt->gsm = ioremap_nocache(ggtt_phys_addr, gtt_size);
+       if (IS_BROXTON(ggtt->base.dev))
+               ggtt->gsm = ioremap_nocache(phys_addr, size);
        else
-               ggtt->gsm = ioremap_wc(ggtt_phys_addr, gtt_size);
+               ggtt->gsm = ioremap_wc(phys_addr, size);
        if (!ggtt->gsm) {
-               DRM_ERROR("Failed to map the gtt page table\n");
+               DRM_ERROR("Failed to map the ggtt page table\n");
                return -ENOMEM;
        }
 
-       scratch_page = alloc_scratch_page(dev);
-       if (IS_ERR(scratch_page)) {
+       ret = setup_scratch_page(ggtt->base.dev,
+                                &ggtt->base.scratch_page,
+                                GFP_DMA32);
+       if (ret) {
                DRM_ERROR("Scratch setup failed\n");
                /* iounmap will also get called at remove, but meh */
                iounmap(ggtt->gsm);
-               return PTR_ERR(scratch_page);
+               return ret;
        }
 
-       ggtt->base.scratch_page = scratch_page;
-
        return 0;
 }
 
@@ -3083,42 +2990,49 @@ static void chv_setup_private_ppat(struct drm_i915_private *dev_priv)
        I915_WRITE(GEN8_PRIVATE_PAT_HI, pat >> 32);
 }
 
+static void gen6_gmch_remove(struct i915_address_space *vm)
+{
+       struct i915_ggtt *ggtt = i915_vm_to_ggtt(vm);
+
+       iounmap(ggtt->gsm);
+       cleanup_scratch_page(vm->dev, &vm->scratch_page);
+}
+
 static int gen8_gmch_probe(struct i915_ggtt *ggtt)
 {
-       struct drm_device *dev = ggtt->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       unsigned int size;
        u16 snb_gmch_ctl;
-       int ret;
 
        /* TODO: We're not aware of mappable constraints on gen8 yet */
-       ggtt->mappable_base = pci_resource_start(dev->pdev, 2);
-       ggtt->mappable_end = pci_resource_len(dev->pdev, 2);
+       ggtt->mappable_base = pci_resource_start(pdev, 2);
+       ggtt->mappable_end = pci_resource_len(pdev, 2);
 
-       if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(39)))
-               pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(39));
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(39)))
+               pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(39));
 
-       pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+       pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
 
-       if (INTEL_INFO(dev)->gen >= 9) {
+       if (INTEL_GEN(dev_priv) >= 9) {
                ggtt->stolen_size = gen9_get_stolen_size(snb_gmch_ctl);
-               ggtt->size = gen8_get_total_gtt_size(snb_gmch_ctl);
-       } else if (IS_CHERRYVIEW(dev)) {
+               size = gen8_get_total_gtt_size(snb_gmch_ctl);
+       } else if (IS_CHERRYVIEW(dev_priv)) {
                ggtt->stolen_size = chv_get_stolen_size(snb_gmch_ctl);
-               ggtt->size = chv_get_total_gtt_size(snb_gmch_ctl);
+               size = chv_get_total_gtt_size(snb_gmch_ctl);
        } else {
                ggtt->stolen_size = gen8_get_stolen_size(snb_gmch_ctl);
-               ggtt->size = gen8_get_total_gtt_size(snb_gmch_ctl);
+               size = gen8_get_total_gtt_size(snb_gmch_ctl);
        }
 
-       ggtt->base.total = (ggtt->size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
+       ggtt->base.total = (size / sizeof(gen8_pte_t)) << PAGE_SHIFT;
 
-       if (IS_CHERRYVIEW(dev) || IS_BROXTON(dev))
+       if (IS_CHERRYVIEW(dev_priv) || IS_BROXTON(dev_priv))
                chv_setup_private_ppat(dev_priv);
        else
                bdw_setup_private_ppat(dev_priv);
 
-       ret = ggtt_probe_common(dev, ggtt->size);
-
+       ggtt->base.cleanup = gen6_gmch_remove;
        ggtt->base.bind_vma = ggtt_bind_vma;
        ggtt->base.unbind_vma = ggtt_unbind_vma;
        ggtt->base.insert_page = gen8_ggtt_insert_page;
@@ -3130,57 +3044,65 @@ static int gen8_gmch_probe(struct i915_ggtt *ggtt)
        if (IS_CHERRYVIEW(dev_priv))
                ggtt->base.insert_entries = gen8_ggtt_insert_entries__BKL;
 
-       return ret;
+       return ggtt_probe_common(ggtt, size);
 }
 
 static int gen6_gmch_probe(struct i915_ggtt *ggtt)
 {
-       struct drm_device *dev = ggtt->base.dev;
+       struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       unsigned int size;
        u16 snb_gmch_ctl;
-       int ret;
 
-       ggtt->mappable_base = pci_resource_start(dev->pdev, 2);
-       ggtt->mappable_end = pci_resource_len(dev->pdev, 2);
+       ggtt->mappable_base = pci_resource_start(pdev, 2);
+       ggtt->mappable_end = pci_resource_len(pdev, 2);
 
        /* 64/512MB is the current min/max we actually know of, but this is just
         * a coarse sanity check.
         */
-       if ((ggtt->mappable_end < (64<<20) || (ggtt->mappable_end > (512<<20)))) {
+       if (ggtt->mappable_end < (64<<20) || ggtt->mappable_end > (512<<20)) {
                DRM_ERROR("Unknown GMADR size (%llx)\n", ggtt->mappable_end);
                return -ENXIO;
        }
 
-       if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40)))
-               pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40));
-       pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
+       if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(40)))
+               pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(40));
+       pci_read_config_word(pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
 
        ggtt->stolen_size = gen6_get_stolen_size(snb_gmch_ctl);
-       ggtt->size = gen6_get_total_gtt_size(snb_gmch_ctl);
-       ggtt->base.total = (ggtt->size / sizeof(gen6_pte_t)) << PAGE_SHIFT;
 
-       ret = ggtt_probe_common(dev, ggtt->size);
+       size = gen6_get_total_gtt_size(snb_gmch_ctl);
+       ggtt->base.total = (size / sizeof(gen6_pte_t)) << PAGE_SHIFT;
 
        ggtt->base.clear_range = gen6_ggtt_clear_range;
        ggtt->base.insert_page = gen6_ggtt_insert_page;
        ggtt->base.insert_entries = gen6_ggtt_insert_entries;
        ggtt->base.bind_vma = ggtt_bind_vma;
        ggtt->base.unbind_vma = ggtt_unbind_vma;
+       ggtt->base.cleanup = gen6_gmch_remove;
+
+       if (HAS_EDRAM(dev_priv))
+               ggtt->base.pte_encode = iris_pte_encode;
+       else if (IS_HASWELL(dev_priv))
+               ggtt->base.pte_encode = hsw_pte_encode;
+       else if (IS_VALLEYVIEW(dev_priv))
+               ggtt->base.pte_encode = byt_pte_encode;
+       else if (INTEL_GEN(dev_priv) >= 7)
+               ggtt->base.pte_encode = ivb_pte_encode;
+       else
+               ggtt->base.pte_encode = snb_pte_encode;
 
-       return ret;
+       return ggtt_probe_common(ggtt, size);
 }
 
-static void gen6_gmch_remove(struct i915_address_space *vm)
+static void i915_gmch_remove(struct i915_address_space *vm)
 {
-       struct i915_ggtt *ggtt = container_of(vm, struct i915_ggtt, base);
-
-       iounmap(ggtt->gsm);
-       free_scratch_page(vm->dev, vm->scratch_page);
+       intel_gmch_remove();
 }
 
 static int i915_gmch_probe(struct i915_ggtt *ggtt)
 {
-       struct drm_device *dev = ggtt->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(ggtt->base.dev);
        int ret;
 
        ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->drm.pdev, NULL);
@@ -3192,12 +3114,13 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt)
        intel_gtt_get(&ggtt->base.total, &ggtt->stolen_size,
                      &ggtt->mappable_base, &ggtt->mappable_end);
 
-       ggtt->do_idle_maps = needs_idle_maps(&dev_priv->drm);
+       ggtt->do_idle_maps = needs_idle_maps(dev_priv);
        ggtt->base.insert_page = i915_ggtt_insert_page;
        ggtt->base.insert_entries = i915_ggtt_insert_entries;
        ggtt->base.clear_range = i915_ggtt_clear_range;
        ggtt->base.bind_vma = ggtt_bind_vma;
        ggtt->base.unbind_vma = ggtt_unbind_vma;
+       ggtt->base.cleanup = i915_gmch_remove;
 
        if (unlikely(ggtt->do_idle_maps))
                DRM_INFO("applying Ironlake quirks for intel_iommu\n");
@@ -3205,65 +3128,40 @@ static int i915_gmch_probe(struct i915_ggtt *ggtt)
        return 0;
 }
 
-static void i915_gmch_remove(struct i915_address_space *vm)
-{
-       intel_gmch_remove();
-}
-
 /**
- * i915_ggtt_init_hw - Initialize GGTT hardware
- * @dev: DRM device
+ * i915_ggtt_probe_hw - Probe GGTT hardware location
+ * @dev_priv: i915 device
  */
-int i915_ggtt_init_hw(struct drm_device *dev)
+int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
        int ret;
 
-       if (INTEL_INFO(dev)->gen <= 5) {
-               ggtt->probe = i915_gmch_probe;
-               ggtt->base.cleanup = i915_gmch_remove;
-       } else if (INTEL_INFO(dev)->gen < 8) {
-               ggtt->probe = gen6_gmch_probe;
-               ggtt->base.cleanup = gen6_gmch_remove;
-
-               if (HAS_EDRAM(dev))
-                       ggtt->base.pte_encode = iris_pte_encode;
-               else if (IS_HASWELL(dev))
-                       ggtt->base.pte_encode = hsw_pte_encode;
-               else if (IS_VALLEYVIEW(dev))
-                       ggtt->base.pte_encode = byt_pte_encode;
-               else if (INTEL_INFO(dev)->gen >= 7)
-                       ggtt->base.pte_encode = ivb_pte_encode;
-               else
-                       ggtt->base.pte_encode = snb_pte_encode;
-       } else {
-               ggtt->probe = gen8_gmch_probe;
-               ggtt->base.cleanup = gen6_gmch_remove;
-       }
-
-       ggtt->base.dev = dev;
-       ggtt->base.is_ggtt = true;
+       ggtt->base.dev = &dev_priv->drm;
 
-       ret = ggtt->probe(ggtt);
+       if (INTEL_GEN(dev_priv) <= 5)
+               ret = i915_gmch_probe(ggtt);
+       else if (INTEL_GEN(dev_priv) < 8)
+               ret = gen6_gmch_probe(ggtt);
+       else
+               ret = gen8_gmch_probe(ggtt);
        if (ret)
                return ret;
 
        if ((ggtt->base.total - 1) >> 32) {
                DRM_ERROR("We never expected a Global GTT with more than 32bits"
-                         "of address space! Found %lldM!\n",
+                         " of address space! Found %lldM!\n",
                          ggtt->base.total >> 20);
                ggtt->base.total = 1ULL << 32;
                ggtt->mappable_end = min(ggtt->mappable_end, ggtt->base.total);
        }
 
-       /*
-        * Initialise stolen early so that we may reserve preallocated
-        * objects for the BIOS to KMS transition.
-        */
-       ret = i915_gem_init_stolen(dev);
-       if (ret)
-               goto out_gtt_cleanup;
+       if (ggtt->mappable_end > ggtt->base.total) {
+               DRM_ERROR("mappable aperture extends past end of GGTT,"
+                         " aperture=%llx, total=%llx\n",
+                         ggtt->mappable_end, ggtt->base.total);
+               ggtt->mappable_end = ggtt->base.total;
+       }
 
        /* GMADR is the PCI mmio aperture into the global GTT. */
        DRM_INFO("Memory usable by graphics device = %lluM\n",
@@ -3276,16 +3174,55 @@ int i915_ggtt_init_hw(struct drm_device *dev)
 #endif
 
        return 0;
+}
+
+/**
+ * i915_ggtt_init_hw - Initialize GGTT hardware
+ * @dev_priv: i915 device
+ */
+int i915_ggtt_init_hw(struct drm_i915_private *dev_priv)
+{
+       struct i915_ggtt *ggtt = &dev_priv->ggtt;
+       int ret;
+
+       INIT_LIST_HEAD(&dev_priv->vm_list);
+
+       /* Subtract the guard page before address space initialization to
+        * shrink the range used by drm_mm.
+        */
+       ggtt->base.total -= PAGE_SIZE;
+       i915_address_space_init(&ggtt->base, dev_priv);
+       ggtt->base.total += PAGE_SIZE;
+       if (!HAS_LLC(dev_priv))
+               ggtt->base.mm.color_adjust = i915_gtt_color_adjust;
+
+       if (!io_mapping_init_wc(&dev_priv->ggtt.mappable,
+                               dev_priv->ggtt.mappable_base,
+                               dev_priv->ggtt.mappable_end)) {
+               ret = -EIO;
+               goto out_gtt_cleanup;
+       }
+
+       ggtt->mtrr = arch_phys_wc_add(ggtt->mappable_base, ggtt->mappable_end);
+
+       /*
+        * Initialise stolen early so that we may reserve preallocated
+        * objects for the BIOS to KMS transition.
+        */
+       ret = i915_gem_init_stolen(&dev_priv->drm);
+       if (ret)
+               goto out_gtt_cleanup;
+
+       return 0;
 
 out_gtt_cleanup:
        ggtt->base.cleanup(&ggtt->base);
-
        return ret;
 }
 
-int i915_ggtt_enable_hw(struct drm_device *dev)
+int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv)
 {
-       if (INTEL_INFO(dev)->gen < 6 && !intel_enable_gtt())
+       if (INTEL_GEN(dev_priv) < 6 && !intel_enable_gtt())
                return -EIO;
 
        return 0;
@@ -3295,8 +3232,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct drm_i915_gem_object *obj;
-       struct i915_vma *vma;
+       struct drm_i915_gem_object *obj, *on;
 
        i915_check_and_clear_faults(dev_priv);
 
@@ -3304,20 +3240,32 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
        ggtt->base.clear_range(&ggtt->base, ggtt->base.start, ggtt->base.total,
                               true);
 
-       /* Cache flush objects bound into GGTT and rebind them. */
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
+       ggtt->base.closed = true; /* skip rewriting PTE on VMA unbind */
+
+       /* clflush objects bound into the GGTT and rebind them. */
+       list_for_each_entry_safe(obj, on,
+                                &dev_priv->mm.bound_list, global_list) {
+               bool ggtt_bound = false;
+               struct i915_vma *vma;
+
                list_for_each_entry(vma, &obj->vma_list, obj_link) {
                        if (vma->vm != &ggtt->base)
                                continue;
 
+                       if (!i915_vma_unbind(vma))
+                               continue;
+
                        WARN_ON(i915_vma_bind(vma, obj->cache_level,
                                              PIN_UPDATE));
+                       ggtt_bound = true;
                }
 
-               if (obj->pin_display)
+               if (ggtt_bound)
                        WARN_ON(i915_gem_object_set_to_gtt_domain(obj, false));
        }
 
+       ggtt->base.closed = false;
+
        if (INTEL_INFO(dev)->gen >= 8) {
                if (IS_CHERRYVIEW(dev) || IS_BROXTON(dev))
                        chv_setup_private_ppat(dev_priv);
@@ -3335,7 +3283,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
 
                        struct i915_hw_ppgtt *ppgtt;
 
-                       if (vm->is_ggtt)
+                       if (i915_is_ggtt(vm))
                                ppgtt = dev_priv->mm.aliasing_ppgtt;
                        else
                                ppgtt = i915_vm_to_ppgtt(vm);
@@ -3348,65 +3296,155 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
        i915_ggtt_flush(dev_priv);
 }
 
+static void
+i915_vma_retire(struct i915_gem_active *active,
+               struct drm_i915_gem_request *rq)
+{
+       const unsigned int idx = rq->engine->id;
+       struct i915_vma *vma =
+               container_of(active, struct i915_vma, last_read[idx]);
+
+       GEM_BUG_ON(!i915_vma_has_active_engine(vma, idx));
+
+       i915_vma_clear_active(vma, idx);
+       if (i915_vma_is_active(vma))
+               return;
+
+       list_move_tail(&vma->vm_link, &vma->vm->inactive_list);
+       if (unlikely(i915_vma_is_closed(vma) && !i915_vma_is_pinned(vma)))
+               WARN_ON(i915_vma_unbind(vma));
+}
+
+void i915_vma_destroy(struct i915_vma *vma)
+{
+       GEM_BUG_ON(vma->node.allocated);
+       GEM_BUG_ON(i915_vma_is_active(vma));
+       GEM_BUG_ON(!i915_vma_is_closed(vma));
+       GEM_BUG_ON(vma->fence);
+
+       list_del(&vma->vm_link);
+       if (!i915_vma_is_ggtt(vma))
+               i915_ppgtt_put(i915_vm_to_ppgtt(vma->vm));
+
+       kmem_cache_free(to_i915(vma->obj->base.dev)->vmas, vma);
+}
+
+void i915_vma_close(struct i915_vma *vma)
+{
+       GEM_BUG_ON(i915_vma_is_closed(vma));
+       vma->flags |= I915_VMA_CLOSED;
+
+       list_del_init(&vma->obj_link);
+       if (!i915_vma_is_active(vma) && !i915_vma_is_pinned(vma))
+               WARN_ON(i915_vma_unbind(vma));
+}
+
 static struct i915_vma *
-__i915_gem_vma_create(struct drm_i915_gem_object *obj,
-                     struct i915_address_space *vm,
-                     const struct i915_ggtt_view *ggtt_view)
+__i915_vma_create(struct drm_i915_gem_object *obj,
+                 struct i915_address_space *vm,
+                 const struct i915_ggtt_view *view)
 {
        struct i915_vma *vma;
+       int i;
 
-       if (WARN_ON(i915_is_ggtt(vm) != !!ggtt_view))
-               return ERR_PTR(-EINVAL);
+       GEM_BUG_ON(vm->closed);
 
        vma = kmem_cache_zalloc(to_i915(obj->base.dev)->vmas, GFP_KERNEL);
        if (vma == NULL)
                return ERR_PTR(-ENOMEM);
 
-       INIT_LIST_HEAD(&vma->vm_link);
-       INIT_LIST_HEAD(&vma->obj_link);
        INIT_LIST_HEAD(&vma->exec_list);
+       for (i = 0; i < ARRAY_SIZE(vma->last_read); i++)
+               init_request_active(&vma->last_read[i], i915_vma_retire);
+       init_request_active(&vma->last_fence, NULL);
+       list_add(&vma->vm_link, &vm->unbound_list);
        vma->vm = vm;
        vma->obj = obj;
-       vma->is_ggtt = i915_is_ggtt(vm);
+       vma->size = obj->base.size;
+
+       if (view) {
+               vma->ggtt_view = *view;
+               if (view->type == I915_GGTT_VIEW_PARTIAL) {
+                       vma->size = view->params.partial.size;
+                       vma->size <<= PAGE_SHIFT;
+               } else if (view->type == I915_GGTT_VIEW_ROTATED) {
+                       vma->size =
+                               intel_rotation_info_size(&view->params.rotated);
+                       vma->size <<= PAGE_SHIFT;
+               }
+       }
 
-       if (i915_is_ggtt(vm))
-               vma->ggtt_view = *ggtt_view;
-       else
+       if (i915_is_ggtt(vm)) {
+               vma->flags |= I915_VMA_GGTT;
+       } else {
                i915_ppgtt_get(i915_vm_to_ppgtt(vm));
+       }
 
        list_add_tail(&vma->obj_link, &obj->vma_list);
-
        return vma;
 }
 
+static inline bool vma_matches(struct i915_vma *vma,
+                              struct i915_address_space *vm,
+                              const struct i915_ggtt_view *view)
+{
+       if (vma->vm != vm)
+               return false;
+
+       if (!i915_vma_is_ggtt(vma))
+               return true;
+
+       if (!view)
+               return vma->ggtt_view.type == 0;
+
+       if (vma->ggtt_view.type != view->type)
+               return false;
+
+       return memcmp(&vma->ggtt_view.params,
+                     &view->params,
+                     sizeof(view->params)) == 0;
+}
+
 struct i915_vma *
-i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
-                                 struct i915_address_space *vm)
+i915_vma_create(struct drm_i915_gem_object *obj,
+               struct i915_address_space *vm,
+               const struct i915_ggtt_view *view)
+{
+       GEM_BUG_ON(view && !i915_is_ggtt(vm));
+       GEM_BUG_ON(i915_gem_obj_to_vma(obj, vm, view));
+
+       return __i915_vma_create(obj, vm, view);
+}
+
+struct i915_vma *
+i915_gem_obj_to_vma(struct drm_i915_gem_object *obj,
+                   struct i915_address_space *vm,
+                   const struct i915_ggtt_view *view)
 {
        struct i915_vma *vma;
 
-       vma = i915_gem_obj_to_vma(obj, vm);
-       if (!vma)
-               vma = __i915_gem_vma_create(obj, vm,
-                                           i915_is_ggtt(vm) ? &i915_ggtt_view_normal : NULL);
+       list_for_each_entry_reverse(vma, &obj->vma_list, obj_link)
+               if (vma_matches(vma, vm, view))
+                       return vma;
 
-       return vma;
+       return NULL;
 }
 
 struct i915_vma *
-i915_gem_obj_lookup_or_create_ggtt_vma(struct drm_i915_gem_object *obj,
-                                      const struct i915_ggtt_view *view)
+i915_gem_obj_lookup_or_create_vma(struct drm_i915_gem_object *obj,
+                                 struct i915_address_space *vm,
+                                 const struct i915_ggtt_view *view)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct i915_vma *vma = i915_gem_obj_to_ggtt_view(obj, view);
+       struct i915_vma *vma;
+
+       GEM_BUG_ON(view && !i915_is_ggtt(vm));
 
+       vma = i915_gem_obj_to_vma(obj, vm, view);
        if (!vma)
-               vma = __i915_gem_vma_create(obj, &ggtt->base, view);
+               vma = __i915_vma_create(obj, vm, view);
 
+       GEM_BUG_ON(i915_vma_is_closed(vma));
        return vma;
-
 }
 
 static struct scatterlist *
@@ -3438,18 +3476,16 @@ rotate_pages(const dma_addr_t *in, unsigned int offset,
 }
 
 static struct sg_table *
-intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
+intel_rotate_fb_obj_pages(const struct intel_rotation_info *rot_info,
                          struct drm_i915_gem_object *obj)
 {
        const size_t n_pages = obj->base.size / PAGE_SIZE;
-       unsigned int size_pages = rot_info->plane[0].width * rot_info->plane[0].height;
-       unsigned int size_pages_uv;
+       unsigned int size = intel_rotation_info_size(rot_info);
        struct sgt_iter sgt_iter;
        dma_addr_t dma_addr;
        unsigned long i;
        dma_addr_t *page_addr_list;
        struct sg_table *st;
-       unsigned int uv_start_page;
        struct scatterlist *sg;
        int ret = -ENOMEM;
 
@@ -3460,18 +3496,12 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
        if (!page_addr_list)
                return ERR_PTR(ret);
 
-       /* Account for UV plane with NV12. */
-       if (rot_info->pixel_format == DRM_FORMAT_NV12)
-               size_pages_uv = rot_info->plane[1].width * rot_info->plane[1].height;
-       else
-               size_pages_uv = 0;
-
        /* Allocate target SG list. */
        st = kmalloc(sizeof(*st), GFP_KERNEL);
        if (!st)
                goto err_st_alloc;
 
-       ret = sg_alloc_table(st, size_pages + size_pages_uv, GFP_KERNEL);
+       ret = sg_alloc_table(st, size, GFP_KERNEL);
        if (ret)
                goto err_sg_alloc;
 
@@ -3484,32 +3514,14 @@ intel_rotate_fb_obj_pages(struct intel_rotation_info *rot_info,
        st->nents = 0;
        sg = st->sgl;
 
-       /* Rotate the pages. */
-       sg = rotate_pages(page_addr_list, 0,
-                         rot_info->plane[0].width, rot_info->plane[0].height,
-                         rot_info->plane[0].width,
-                         st, sg);
-
-       /* Append the UV plane if NV12. */
-       if (rot_info->pixel_format == DRM_FORMAT_NV12) {
-               uv_start_page = size_pages;
-
-               /* Check for tile-row un-alignment. */
-               if (offset_in_page(rot_info->uv_offset))
-                       uv_start_page--;
-
-               rot_info->uv_start_page = uv_start_page;
-
-               sg = rotate_pages(page_addr_list, rot_info->uv_start_page,
-                                 rot_info->plane[1].width, rot_info->plane[1].height,
-                                 rot_info->plane[1].width,
-                                 st, sg);
+       for (i = 0 ; i < ARRAY_SIZE(rot_info->plane); i++) {
+               sg = rotate_pages(page_addr_list, rot_info->plane[i].offset,
+                                 rot_info->plane[i].width, rot_info->plane[i].height,
+                                 rot_info->plane[i].stride, st, sg);
        }
 
-       DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages (%u plane 0)).\n",
-                     obj->base.size, rot_info->plane[0].width,
-                     rot_info->plane[0].height, size_pages + size_pages_uv,
-                     size_pages);
+       DRM_DEBUG_KMS("Created rotated page mapping for object size %zu (%ux%u tiles, %u pages)\n",
+                     obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size);
 
        drm_free_large(page_addr_list);
 
@@ -3520,10 +3532,9 @@ err_sg_alloc:
 err_st_alloc:
        drm_free_large(page_addr_list);
 
-       DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%d) (%ux%u tiles, %u pages (%u plane 0))\n",
-                     obj->base.size, ret, rot_info->plane[0].width,
-                     rot_info->plane[0].height, size_pages + size_pages_uv,
-                     size_pages);
+       DRM_DEBUG_KMS("Failed to create rotated mapping for object size %zu! (%ux%u tiles, %u pages)\n",
+                     obj->base.size, rot_info->plane[0].width, rot_info->plane[0].height, size);
+
        return ERR_PTR(ret);
 }
 
@@ -3573,28 +3584,27 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma)
 {
        int ret = 0;
 
-       if (vma->ggtt_view.pages)
+       if (vma->pages)
                return 0;
 
        if (vma->ggtt_view.type == I915_GGTT_VIEW_NORMAL)
-               vma->ggtt_view.pages = vma->obj->pages;
+               vma->pages = vma->obj->pages;
        else if (vma->ggtt_view.type == I915_GGTT_VIEW_ROTATED)
-               vma->ggtt_view.pages =
+               vma->pages =
                        intel_rotate_fb_obj_pages(&vma->ggtt_view.params.rotated, vma->obj);
        else if (vma->ggtt_view.type == I915_GGTT_VIEW_PARTIAL)
-               vma->ggtt_view.pages =
-                       intel_partial_pages(&vma->ggtt_view, vma->obj);
+               vma->pages = intel_partial_pages(&vma->ggtt_view, vma->obj);
        else
                WARN_ONCE(1, "GGTT view %u not implemented!\n",
                          vma->ggtt_view.type);
 
-       if (!vma->ggtt_view.pages) {
+       if (!vma->pages) {
                DRM_ERROR("Failed to get pages for GGTT view type %u!\n",
                          vma->ggtt_view.type);
                ret = -EINVAL;
-       } else if (IS_ERR(vma->ggtt_view.pages)) {
-               ret = PTR_ERR(vma->ggtt_view.pages);
-               vma->ggtt_view.pages = NULL;
+       } else if (IS_ERR(vma->pages)) {
+               ret = PTR_ERR(vma->pages);
+               vma->pages = NULL;
                DRM_ERROR("Failed to get pages for VMA view type %u (%d)!\n",
                          vma->ggtt_view.type, ret);
        }
@@ -3615,34 +3625,32 @@ i915_get_ggtt_vma_pages(struct i915_vma *vma)
 int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
                  u32 flags)
 {
-       int ret;
        u32 bind_flags;
+       u32 vma_flags;
+       int ret;
 
        if (WARN_ON(flags == 0))
                return -EINVAL;
 
        bind_flags = 0;
        if (flags & PIN_GLOBAL)
-               bind_flags |= GLOBAL_BIND;
+               bind_flags |= I915_VMA_GLOBAL_BIND;
        if (flags & PIN_USER)
-               bind_flags |= LOCAL_BIND;
+               bind_flags |= I915_VMA_LOCAL_BIND;
 
+       vma_flags = vma->flags & (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND);
        if (flags & PIN_UPDATE)
-               bind_flags |= vma->bound;
+               bind_flags |= vma_flags;
        else
-               bind_flags &= ~vma->bound;
-
+               bind_flags &= ~vma_flags;
        if (bind_flags == 0)
                return 0;
 
-       if (vma->bound == 0 && vma->vm->allocate_va_range) {
-               /* XXX: i915_vma_pin() will fix this +- hack */
-               vma->pin_count++;
+       if (vma_flags == 0 && vma->vm->allocate_va_range) {
                trace_i915_va_alloc(vma);
                ret = vma->vm->allocate_va_range(vma->vm,
                                                 vma->node.start,
                                                 vma->node.size);
-               vma->pin_count--;
                if (ret)
                        return ret;
        }
@@ -3651,56 +3659,47 @@ int i915_vma_bind(struct i915_vma *vma, enum i915_cache_level cache_level,
        if (ret)
                return ret;
 
-       vma->bound |= bind_flags;
-
+       vma->flags |= bind_flags;
        return 0;
 }
 
-/**
- * i915_ggtt_view_size - Get the size of a GGTT view.
- * @obj: Object the view is of.
- * @view: The view in question.
- *
- * @return The size of the GGTT view in bytes.
- */
-size_t
-i915_ggtt_view_size(struct drm_i915_gem_object *obj,
-                   const struct i915_ggtt_view *view)
-{
-       if (view->type == I915_GGTT_VIEW_NORMAL) {
-               return obj->base.size;
-       } else if (view->type == I915_GGTT_VIEW_ROTATED) {
-               return intel_rotation_info_size(&view->params.rotated) << PAGE_SHIFT;
-       } else if (view->type == I915_GGTT_VIEW_PARTIAL) {
-               return view->params.partial.size << PAGE_SHIFT;
-       } else {
-               WARN_ONCE(1, "GGTT view %u not implemented!\n", view->type);
-               return obj->base.size;
-       }
-}
-
 void __iomem *i915_vma_pin_iomap(struct i915_vma *vma)
 {
        void __iomem *ptr;
 
+       /* Access through the GTT requires the device to be awake. */
+       assert_rpm_wakelock_held(to_i915(vma->vm->dev));
+
        lockdep_assert_held(&vma->vm->dev->struct_mutex);
-       if (WARN_ON(!vma->obj->map_and_fenceable))
-               return ERR_PTR(-ENODEV);
+       if (WARN_ON(!i915_vma_is_map_and_fenceable(vma)))
+               return IO_ERR_PTR(-ENODEV);
 
-       GEM_BUG_ON(!vma->is_ggtt);
-       GEM_BUG_ON((vma->bound & GLOBAL_BIND) == 0);
+       GEM_BUG_ON(!i915_vma_is_ggtt(vma));
+       GEM_BUG_ON((vma->flags & I915_VMA_GLOBAL_BIND) == 0);
 
        ptr = vma->iomap;
        if (ptr == NULL) {
-               ptr = io_mapping_map_wc(i915_vm_to_ggtt(vma->vm)->mappable,
+               ptr = io_mapping_map_wc(&i915_vm_to_ggtt(vma->vm)->mappable,
                                        vma->node.start,
                                        vma->node.size);
                if (ptr == NULL)
-                       return ERR_PTR(-ENOMEM);
+                       return IO_ERR_PTR(-ENOMEM);
 
                vma->iomap = ptr;
        }
 
-       vma->pin_count++;
+       __i915_vma_pin(vma);
        return ptr;
 }
+
+void i915_vma_unpin_and_release(struct i915_vma **p_vma)
+{
+       struct i915_vma *vma;
+
+       vma = fetch_and_zero(p_vma);
+       if (!vma)
+               return;
+
+       i915_vma_unpin(vma);
+       i915_vma_put(vma);
+}
index aa5f31d..ec78be2 100644 (file)
 
 #include <linux/io-mapping.h>
 
+#include "i915_gem_request.h"
+
+#define I915_FENCE_REG_NONE -1
+#define I915_MAX_NUM_FENCES 32
+/* 32 fences + sign bit for FENCE_REG_NONE */
+#define I915_MAX_NUM_FENCE_BITS 6
+
 struct drm_i915_file_private;
+struct drm_i915_fence_reg;
 
 typedef uint32_t gen6_pte_t;
 typedef uint64_t gen8_pte_t;
@@ -137,12 +145,9 @@ enum i915_ggtt_view_type {
 };
 
 struct intel_rotation_info {
-       unsigned int uv_offset;
-       uint32_t pixel_format;
-       unsigned int uv_start_page;
        struct {
                /* tiles */
-               unsigned int width, height;
+               unsigned int width, height, stride, offset;
        } plane[2];
 };
 
@@ -156,8 +161,6 @@ struct i915_ggtt_view {
                } partial;
                struct intel_rotation_info rotated;
        } params;
-
-       struct sg_table *pages;
 };
 
 extern const struct i915_ggtt_view i915_ggtt_view_normal;
@@ -177,13 +180,38 @@ struct i915_vma {
        struct drm_mm_node node;
        struct drm_i915_gem_object *obj;
        struct i915_address_space *vm;
+       struct drm_i915_fence_reg *fence;
+       struct sg_table *pages;
        void __iomem *iomap;
+       u64 size;
+       u64 display_alignment;
+
+       unsigned int flags;
+       /**
+        * How many users have pinned this object in GTT space. The following
+        * users can each hold at most one reference: pwrite/pread, execbuffer
+        * (objects are not allowed multiple times for the same batchbuffer),
+        * and the framebuffer code. When switching/pageflipping, the
+        * framebuffer code has at most two buffers pinned per crtc.
+        *
+        * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
+        * bits with absolutely no headroom. So use 4 bits.
+        */
+#define I915_VMA_PIN_MASK 0xf
+#define I915_VMA_PIN_OVERFLOW  BIT(5)
 
        /** Flags and address space this VMA is bound to */
-#define GLOBAL_BIND    (1<<0)
-#define LOCAL_BIND     (1<<1)
-       unsigned int bound : 4;
-       bool is_ggtt : 1;
+#define I915_VMA_GLOBAL_BIND   BIT(6)
+#define I915_VMA_LOCAL_BIND    BIT(7)
+#define I915_VMA_BIND_MASK (I915_VMA_GLOBAL_BIND | I915_VMA_LOCAL_BIND | I915_VMA_PIN_OVERFLOW)
+
+#define I915_VMA_GGTT          BIT(8)
+#define I915_VMA_CAN_FENCE     BIT(9)
+#define I915_VMA_CLOSED                BIT(10)
+
+       unsigned int active;
+       struct i915_gem_active last_read[I915_NUM_ENGINES];
+       struct i915_gem_active last_fence;
 
        /**
         * Support different GGTT views into the same object.
@@ -208,20 +236,66 @@ struct i915_vma {
        struct hlist_node exec_node;
        unsigned long exec_handle;
        struct drm_i915_gem_exec_object2 *exec_entry;
-
-       /**
-        * How many users have pinned this object in GTT space. The following
-        * users can each hold at most one reference: pwrite/pread, execbuffer
-        * (objects are not allowed multiple times for the same batchbuffer),
-        * and the framebuffer code. When switching/pageflipping, the
-        * framebuffer code has at most two buffers pinned per crtc.
-        *
-        * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
-        * bits with absolutely no headroom. So use 4 bits. */
-       unsigned int pin_count:4;
-#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
 };
 
+struct i915_vma *
+i915_vma_create(struct drm_i915_gem_object *obj,
+               struct i915_address_space *vm,
+               const struct i915_ggtt_view *view);
+void i915_vma_unpin_and_release(struct i915_vma **p_vma);
+
+static inline bool i915_vma_is_ggtt(const struct i915_vma *vma)
+{
+       return vma->flags & I915_VMA_GGTT;
+}
+
+static inline bool i915_vma_is_map_and_fenceable(const struct i915_vma *vma)
+{
+       return vma->flags & I915_VMA_CAN_FENCE;
+}
+
+static inline bool i915_vma_is_closed(const struct i915_vma *vma)
+{
+       return vma->flags & I915_VMA_CLOSED;
+}
+
+static inline unsigned int i915_vma_get_active(const struct i915_vma *vma)
+{
+       return vma->active;
+}
+
+static inline bool i915_vma_is_active(const struct i915_vma *vma)
+{
+       return i915_vma_get_active(vma);
+}
+
+static inline void i915_vma_set_active(struct i915_vma *vma,
+                                      unsigned int engine)
+{
+       vma->active |= BIT(engine);
+}
+
+static inline void i915_vma_clear_active(struct i915_vma *vma,
+                                        unsigned int engine)
+{
+       vma->active &= ~BIT(engine);
+}
+
+static inline bool i915_vma_has_active_engine(const struct i915_vma *vma,
+                                             unsigned int engine)
+{
+       return vma->active & BIT(engine);
+}
+
+static inline u32 i915_ggtt_offset(const struct i915_vma *vma)
+{
+       GEM_BUG_ON(!i915_vma_is_ggtt(vma));
+       GEM_BUG_ON(!vma->node.allocated);
+       GEM_BUG_ON(upper_32_bits(vma->node.start));
+       GEM_BUG_ON(upper_32_bits(vma->node.start + vma->node.size - 1));
+       return lower_32_bits(vma->node.start);
+}
+
 struct i915_page_dma {
        struct page *page;
        union {
@@ -238,10 +312,6 @@ struct i915_page_dma {
 #define px_page(px) (px_base(px)->page)
 #define px_dma(px) (px_base(px)->daddr)
 
-struct i915_page_scratch {
-       struct i915_page_dma base;
-};
-
 struct i915_page_table {
        struct i915_page_dma base;
 
@@ -272,13 +342,22 @@ struct i915_pml4 {
 struct i915_address_space {
        struct drm_mm mm;
        struct drm_device *dev;
+       /* Every address space belongs to a struct file - except for the global
+        * GTT that is owned by the driver (and so @file is set to NULL). In
+        * principle, no information should leak from one context to another
+        * (or between files/processes etc) unless explicitly shared by the
+        * owner. Tracking the owner is important in order to free up per-file
+        * objects along with the file, to aide resource tracking, and to
+        * assign blame.
+        */
+       struct drm_i915_file_private *file;
        struct list_head global_link;
        u64 start;              /* Start offset always 0 for dri2 */
        u64 total;              /* size addr space maps (ex. 2GB for ggtt) */
 
-       bool is_ggtt;
+       bool closed;
 
-       struct i915_page_scratch *scratch_page;
+       struct i915_page_dma scratch_page;
        struct i915_page_table *scratch_pt;
        struct i915_page_directory *scratch_pd;
        struct i915_page_directory_pointer *scratch_pdp; /* GEN8+ & 48b PPGTT */
@@ -306,6 +385,13 @@ struct i915_address_space {
         */
        struct list_head inactive_list;
 
+       /**
+        * List of vma that have been unbound.
+        *
+        * A reference is not held on the buffer while on this list.
+        */
+       struct list_head unbound_list;
+
        /* FIXME: Need a more generic return type */
        gen6_pte_t (*pte_encode)(dma_addr_t addr,
                                 enum i915_cache_level level,
@@ -338,7 +424,7 @@ struct i915_address_space {
                        u32 flags);
 };
 
-#define i915_is_ggtt(V) ((V)->is_ggtt)
+#define i915_is_ggtt(V) (!(V)->file)
 
 /* The Graphics Translation Table is the way in which GEN hardware translates a
  * Graphics Virtual Address into a Physical Address. In addition to the normal
@@ -349,14 +435,13 @@ struct i915_address_space {
  */
 struct i915_ggtt {
        struct i915_address_space base;
+       struct io_mapping mappable;     /* Mapping to our CPU mappable region */
 
        size_t stolen_size;             /* Total size of stolen memory */
        size_t stolen_usable_size;      /* Total size minus BIOS reserved */
        size_t stolen_reserved_base;
        size_t stolen_reserved_size;
-       size_t size;                    /* Total size of Global GTT */
        u64 mappable_end;               /* End offset that we can CPU map */
-       struct io_mapping *mappable;    /* Mapping to our CPU mappable region */
        phys_addr_t mappable_base;      /* PA of our GMADR */
 
        /** "Graphics Stolen Memory" holds the global PTEs */
@@ -365,8 +450,6 @@ struct i915_ggtt {
        bool do_idle_maps;
 
        int mtrr;
-
-       int (*probe)(struct i915_ggtt *ggtt);
 };
 
 struct i915_hw_ppgtt {
@@ -380,8 +463,6 @@ struct i915_hw_ppgtt {
                struct i915_page_directory pd;          /* GEN6-7 */
        };
 
-       struct drm_i915_file_private *file_priv;
-
        gen6_pte_t __iomem *pd_addr;
 
        int (*enable)(struct i915_hw_ppgtt *ppgtt);
@@ -521,14 +602,15 @@ i915_page_dir_dma_addr(const struct i915_hw_ppgtt *ppgtt, const unsigned n)
                px_dma(ppgtt->base.scratch_pd);
 }
 
-int i915_ggtt_init_hw(struct drm_device *dev);
-int i915_ggtt_enable_hw(struct drm_device *dev);
-void i915_gem_init_ggtt(struct drm_device *dev);
-void i915_ggtt_cleanup_hw(struct drm_device *dev);
+int i915_ggtt_probe_hw(struct drm_i915_private *dev_priv);
+int i915_ggtt_init_hw(struct drm_i915_private *dev_priv);
+int i915_ggtt_enable_hw(struct drm_i915_private *dev_priv);
+int i915_gem_init_ggtt(struct drm_i915_private *dev_priv);
+void i915_ggtt_cleanup_hw(struct drm_i915_private *dev_priv);
 
 int i915_ppgtt_init_hw(struct drm_device *dev);
 void i915_ppgtt_release(struct kref *kref);
-struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_device *dev,
+struct i915_hw_ppgtt *i915_ppgtt_create(struct drm_i915_private *dev_priv,
                                        struct drm_i915_file_private *fpriv);
 static inline void i915_ppgtt_get(struct i915_hw_ppgtt *ppgtt)
 {
@@ -548,23 +630,67 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev);
 int __must_check i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj);
 void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj);
 
-static inline bool
-i915_ggtt_view_equal(const struct i915_ggtt_view *a,
-                     const struct i915_ggtt_view *b)
+/* Flags used by pin/bind&friends. */
+#define PIN_NONBLOCK           BIT(0)
+#define PIN_MAPPABLE           BIT(1)
+#define PIN_ZONE_4G            BIT(2)
+#define PIN_NONFAULT           BIT(3)
+
+#define PIN_MBZ                        BIT(5) /* I915_VMA_PIN_OVERFLOW */
+#define PIN_GLOBAL             BIT(6) /* I915_VMA_GLOBAL_BIND */
+#define PIN_USER               BIT(7) /* I915_VMA_LOCAL_BIND */
+#define PIN_UPDATE             BIT(8)
+
+#define PIN_HIGH               BIT(9)
+#define PIN_OFFSET_BIAS                BIT(10)
+#define PIN_OFFSET_FIXED       BIT(11)
+#define PIN_OFFSET_MASK                (~4095)
+
+int __i915_vma_do_pin(struct i915_vma *vma,
+                     u64 size, u64 alignment, u64 flags);
+static inline int __must_check
+i915_vma_pin(struct i915_vma *vma, u64 size, u64 alignment, u64 flags)
 {
-       if (WARN_ON(!a || !b))
-               return false;
-
-       if (a->type != b->type)
-               return false;
-       if (a->type != I915_GGTT_VIEW_NORMAL)
-               return !memcmp(&a->params, &b->params, sizeof(a->params));
-       return true;
+       BUILD_BUG_ON(PIN_MBZ != I915_VMA_PIN_OVERFLOW);
+       BUILD_BUG_ON(PIN_GLOBAL != I915_VMA_GLOBAL_BIND);
+       BUILD_BUG_ON(PIN_USER != I915_VMA_LOCAL_BIND);
+
+       /* Pin early to prevent the shrinker/eviction logic from destroying
+        * our vma as we insert and bind.
+        */
+       if (likely(((++vma->flags ^ flags) & I915_VMA_BIND_MASK) == 0))
+               return 0;
+
+       return __i915_vma_do_pin(vma, size, alignment, flags);
 }
 
-size_t
-i915_ggtt_view_size(struct drm_i915_gem_object *obj,
-                   const struct i915_ggtt_view *view);
+static inline int i915_vma_pin_count(const struct i915_vma *vma)
+{
+       return vma->flags & I915_VMA_PIN_MASK;
+}
+
+static inline bool i915_vma_is_pinned(const struct i915_vma *vma)
+{
+       return i915_vma_pin_count(vma);
+}
+
+static inline void __i915_vma_pin(struct i915_vma *vma)
+{
+       vma->flags++;
+       GEM_BUG_ON(vma->flags & I915_VMA_PIN_OVERFLOW);
+}
+
+static inline void __i915_vma_unpin(struct i915_vma *vma)
+{
+       GEM_BUG_ON(!i915_vma_is_pinned(vma));
+       vma->flags--;
+}
+
+static inline void i915_vma_unpin(struct i915_vma *vma)
+{
+       GEM_BUG_ON(!drm_mm_node_allocated(&vma->node));
+       __i915_vma_unpin(vma);
+}
 
 /**
  * i915_vma_pin_iomap - calls ioremap_wc to map the GGTT VMA via the aperture
@@ -580,6 +706,7 @@ i915_ggtt_view_size(struct drm_i915_gem_object *obj,
  * Returns a valid iomapped pointer or ERR_PTR.
  */
 void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
+#define IO_ERR_PTR(x) ((void __iomem *)ERR_PTR(x))
 
 /**
  * i915_vma_unpin_iomap - unpins the mapping returned from i915_vma_iomap
@@ -593,9 +720,14 @@ void __iomem *i915_vma_pin_iomap(struct i915_vma *vma);
 static inline void i915_vma_unpin_iomap(struct i915_vma *vma)
 {
        lockdep_assert_held(&vma->vm->dev->struct_mutex);
-       GEM_BUG_ON(vma->pin_count == 0);
        GEM_BUG_ON(vma->iomap == NULL);
-       vma->pin_count--;
+       i915_vma_unpin(vma);
+}
+
+static inline struct page *i915_vma_first_page(struct i915_vma *vma)
+{
+       GEM_BUG_ON(!vma->pages);
+       return sg_page(vma->pages->sgl);
 }
 
 #endif
index f75bbd6..95b7e9a 100644 (file)
 #include "i915_drv.h"
 #include "intel_renderstate.h"
 
+struct render_state {
+       const struct intel_renderstate_rodata *rodata;
+       struct i915_vma *vma;
+       u32 aux_batch_size;
+       u32 aux_batch_offset;
+};
+
 static const struct intel_renderstate_rodata *
-render_state_get_rodata(const int gen)
+render_state_get_rodata(const struct drm_i915_gem_request *req)
 {
-       switch (gen) {
+       switch (INTEL_GEN(req->i915)) {
        case 6:
                return &gen6_null_state;
        case 7:
@@ -45,35 +52,6 @@ render_state_get_rodata(const int gen)
        return NULL;
 }
 
-static int render_state_init(struct render_state *so,
-                            struct drm_i915_private *dev_priv)
-{
-       int ret;
-
-       so->gen = INTEL_GEN(dev_priv);
-       so->rodata = render_state_get_rodata(so->gen);
-       if (so->rodata == NULL)
-               return 0;
-
-       if (so->rodata->batch_items * 4 > 4096)
-               return -EINVAL;
-
-       so->obj = i915_gem_object_create(&dev_priv->drm, 4096);
-       if (IS_ERR(so->obj))
-               return PTR_ERR(so->obj);
-
-       ret = i915_gem_obj_ggtt_pin(so->obj, 4096, 0);
-       if (ret)
-               goto free_gem;
-
-       so->ggtt_offset = i915_gem_obj_ggtt_offset(so->obj);
-       return 0;
-
-free_gem:
-       drm_gem_object_unreference(&so->obj->base);
-       return ret;
-}
-
 /*
  * Macro to add commands to auxiliary batch.
  * This macro only checks for page overflow before inserting the commands,
@@ -94,27 +72,28 @@ free_gem:
 
 static int render_state_setup(struct render_state *so)
 {
-       struct drm_device *dev = so->obj->base.dev;
+       struct drm_device *dev = so->vma->vm->dev;
        const struct intel_renderstate_rodata *rodata = so->rodata;
+       const bool has_64bit_reloc = INTEL_GEN(dev) >= 8;
        unsigned int i = 0, reloc_index = 0;
        struct page *page;
        u32 *d;
        int ret;
 
-       ret = i915_gem_object_set_to_cpu_domain(so->obj, true);
+       ret = i915_gem_object_set_to_cpu_domain(so->vma->obj, true);
        if (ret)
                return ret;
 
-       page = i915_gem_object_get_dirty_page(so->obj, 0);
+       page = i915_gem_object_get_dirty_page(so->vma->obj, 0);
        d = kmap(page);
 
        while (i < rodata->batch_items) {
                u32 s = rodata->batch[i];
 
                if (i * 4  == rodata->reloc[reloc_index]) {
-                       u64 r = s + so->ggtt_offset;
+                       u64 r = s + so->vma->node.start;
                        s = lower_32_bits(r);
-                       if (so->gen >= 8) {
+                       if (has_64bit_reloc) {
                                if (i + 1 >= rodata->batch_items ||
                                    rodata->batch[i + 1] != 0) {
                                        ret = -EINVAL;
@@ -174,7 +153,7 @@ static int render_state_setup(struct render_state *so)
 
        kunmap(page);
 
-       ret = i915_gem_object_set_to_gtt_domain(so->obj, false);
+       ret = i915_gem_object_set_to_gtt_domain(so->vma->obj, false);
        if (ret)
                return ret;
 
@@ -192,67 +171,60 @@ err_out:
 
 #undef OUT_BATCH
 
-void i915_gem_render_state_fini(struct render_state *so)
-{
-       i915_gem_object_ggtt_unpin(so->obj);
-       drm_gem_object_unreference(&so->obj->base);
-}
-
-int i915_gem_render_state_prepare(struct intel_engine_cs *engine,
-                                 struct render_state *so)
+int i915_gem_render_state_init(struct drm_i915_gem_request *req)
 {
+       struct render_state so;
+       struct drm_i915_gem_object *obj;
        int ret;
 
-       if (WARN_ON(engine->id != RCS))
+       if (WARN_ON(req->engine->id != RCS))
                return -ENOENT;
 
-       ret = render_state_init(so, engine->i915);
-       if (ret)
-               return ret;
-
-       if (so->rodata == NULL)
+       so.rodata = render_state_get_rodata(req);
+       if (!so.rodata)
                return 0;
 
-       ret = render_state_setup(so);
-       if (ret) {
-               i915_gem_render_state_fini(so);
-               return ret;
-       }
+       if (so.rodata->batch_items * 4 > 4096)
+               return -EINVAL;
 
-       return 0;
-}
+       obj = i915_gem_object_create(&req->i915->drm, 4096);
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
 
-int i915_gem_render_state_init(struct drm_i915_gem_request *req)
-{
-       struct render_state so;
-       int ret;
+       so.vma = i915_vma_create(obj, &req->i915->ggtt.base, NULL);
+       if (IS_ERR(so.vma)) {
+               ret = PTR_ERR(so.vma);
+               goto err_obj;
+       }
 
-       ret = i915_gem_render_state_prepare(req->engine, &so);
+       ret = i915_vma_pin(so.vma, 0, 0, PIN_GLOBAL);
        if (ret)
-               return ret;
+               goto err_obj;
 
-       if (so.rodata == NULL)
-               return 0;
+       ret = render_state_setup(&so);
+       if (ret)
+               goto err_unpin;
 
-       ret = req->engine->dispatch_execbuffer(req, so.ggtt_offset,
-                                            so.rodata->batch_items * 4,
-                                            I915_DISPATCH_SECURE);
+       ret = req->engine->emit_bb_start(req, so.vma->node.start,
+                                        so.rodata->batch_items * 4,
+                                        I915_DISPATCH_SECURE);
        if (ret)
-               goto out;
+               goto err_unpin;
 
        if (so.aux_batch_size > 8) {
-               ret = req->engine->dispatch_execbuffer(req,
-                                                    (so.ggtt_offset +
-                                                     so.aux_batch_offset),
-                                                    so.aux_batch_size,
-                                                    I915_DISPATCH_SECURE);
+               ret = req->engine->emit_bb_start(req,
+                                                (so.vma->node.start +
+                                                 so.aux_batch_offset),
+                                                so.aux_batch_size,
+                                                I915_DISPATCH_SECURE);
                if (ret)
-                       goto out;
+                       goto err_unpin;
        }
 
-       i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), req);
-
-out:
-       i915_gem_render_state_fini(&so);
+       i915_vma_move_to_active(so.vma, req, 0);
+err_unpin:
+       i915_vma_unpin(so.vma);
+err_obj:
+       i915_gem_object_put(obj);
        return ret;
 }
index 6aaa3a1..18cce3f 100644 (file)
 #ifndef _I915_GEM_RENDER_STATE_H_
 #define _I915_GEM_RENDER_STATE_H_
 
-#include <linux/types.h>
-
-struct intel_renderstate_rodata {
-       const u32 *reloc;
-       const u32 *batch;
-       const u32 batch_items;
-};
-
-struct render_state {
-       const struct intel_renderstate_rodata *rodata;
-       struct drm_i915_gem_object *obj;
-       u64 ggtt_offset;
-       int gen;
-       u32 aux_batch_size;
-       u32 aux_batch_offset;
-};
+struct drm_i915_gem_request;
 
 int i915_gem_render_state_init(struct drm_i915_gem_request *req);
-void i915_gem_render_state_fini(struct render_state *so);
-int i915_gem_render_state_prepare(struct intel_engine_cs *engine,
-                                 struct render_state *so);
 
 #endif /* _I915_GEM_RENDER_STATE_H_ */
diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c
new file mode 100644 (file)
index 0000000..8832f8e
--- /dev/null
@@ -0,0 +1,947 @@
+/*
+ * Copyright © 2008-2015 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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/prefetch.h>
+
+#include "i915_drv.h"
+
+static const char *i915_fence_get_driver_name(struct fence *fence)
+{
+       return "i915";
+}
+
+static const char *i915_fence_get_timeline_name(struct fence *fence)
+{
+       /* Timelines are bound by eviction to a VM. However, since
+        * we only have a global seqno at the moment, we only have
+        * a single timeline. Note that each timeline will have
+        * multiple execution contexts (fence contexts) as we allow
+        * engines within a single timeline to execute in parallel.
+        */
+       return "global";
+}
+
+static bool i915_fence_signaled(struct fence *fence)
+{
+       return i915_gem_request_completed(to_request(fence));
+}
+
+static bool i915_fence_enable_signaling(struct fence *fence)
+{
+       if (i915_fence_signaled(fence))
+               return false;
+
+       intel_engine_enable_signaling(to_request(fence));
+       return true;
+}
+
+static signed long i915_fence_wait(struct fence *fence,
+                                  bool interruptible,
+                                  signed long timeout_jiffies)
+{
+       s64 timeout_ns, *timeout;
+       int ret;
+
+       if (timeout_jiffies != MAX_SCHEDULE_TIMEOUT) {
+               timeout_ns = jiffies_to_nsecs(timeout_jiffies);
+               timeout = &timeout_ns;
+       } else {
+               timeout = NULL;
+       }
+
+       ret = i915_wait_request(to_request(fence),
+                               interruptible, timeout,
+                               NO_WAITBOOST);
+       if (ret == -ETIME)
+               return 0;
+
+       if (ret < 0)
+               return ret;
+
+       if (timeout_jiffies != MAX_SCHEDULE_TIMEOUT)
+               timeout_jiffies = nsecs_to_jiffies(timeout_ns);
+
+       return timeout_jiffies;
+}
+
+static void i915_fence_value_str(struct fence *fence, char *str, int size)
+{
+       snprintf(str, size, "%u", fence->seqno);
+}
+
+static void i915_fence_timeline_value_str(struct fence *fence, char *str,
+                                         int size)
+{
+       snprintf(str, size, "%u",
+                intel_engine_get_seqno(to_request(fence)->engine));
+}
+
+static void i915_fence_release(struct fence *fence)
+{
+       struct drm_i915_gem_request *req = to_request(fence);
+
+       kmem_cache_free(req->i915->requests, req);
+}
+
+const struct fence_ops i915_fence_ops = {
+       .get_driver_name = i915_fence_get_driver_name,
+       .get_timeline_name = i915_fence_get_timeline_name,
+       .enable_signaling = i915_fence_enable_signaling,
+       .signaled = i915_fence_signaled,
+       .wait = i915_fence_wait,
+       .release = i915_fence_release,
+       .fence_value_str = i915_fence_value_str,
+       .timeline_value_str = i915_fence_timeline_value_str,
+};
+
+int i915_gem_request_add_to_client(struct drm_i915_gem_request *req,
+                                  struct drm_file *file)
+{
+       struct drm_i915_private *dev_private;
+       struct drm_i915_file_private *file_priv;
+
+       WARN_ON(!req || !file || req->file_priv);
+
+       if (!req || !file)
+               return -EINVAL;
+
+       if (req->file_priv)
+               return -EINVAL;
+
+       dev_private = req->i915;
+       file_priv = file->driver_priv;
+
+       spin_lock(&file_priv->mm.lock);
+       req->file_priv = file_priv;
+       list_add_tail(&req->client_list, &file_priv->mm.request_list);
+       spin_unlock(&file_priv->mm.lock);
+
+       return 0;
+}
+
+static inline void
+i915_gem_request_remove_from_client(struct drm_i915_gem_request *request)
+{
+       struct drm_i915_file_private *file_priv = request->file_priv;
+
+       if (!file_priv)
+               return;
+
+       spin_lock(&file_priv->mm.lock);
+       list_del(&request->client_list);
+       request->file_priv = NULL;
+       spin_unlock(&file_priv->mm.lock);
+}
+
+void i915_gem_retire_noop(struct i915_gem_active *active,
+                         struct drm_i915_gem_request *request)
+{
+       /* Space left intentionally blank */
+}
+
+static void i915_gem_request_retire(struct drm_i915_gem_request *request)
+{
+       struct i915_gem_active *active, *next;
+
+       trace_i915_gem_request_retire(request);
+       list_del(&request->link);
+
+       /* We know the GPU must have read the request to have
+        * sent us the seqno + interrupt, so use the position
+        * of tail of the request to update the last known position
+        * of the GPU head.
+        *
+        * Note this requires that we are always called in request
+        * completion order.
+        */
+       list_del(&request->ring_link);
+       request->ring->last_retired_head = request->postfix;
+
+       /* Walk through the active list, calling retire on each. This allows
+        * objects to track their GPU activity and mark themselves as idle
+        * when their *last* active request is completed (updating state
+        * tracking lists for eviction, active references for GEM, etc).
+        *
+        * As the ->retire() may free the node, we decouple it first and
+        * pass along the auxiliary information (to avoid dereferencing
+        * the node after the callback).
+        */
+       list_for_each_entry_safe(active, next, &request->active_list, link) {
+               /* In microbenchmarks or focusing upon time inside the kernel,
+                * we may spend an inordinate amount of time simply handling
+                * the retirement of requests and processing their callbacks.
+                * Of which, this loop itself is particularly hot due to the
+                * cache misses when jumping around the list of i915_gem_active.
+                * So we try to keep this loop as streamlined as possible and
+                * also prefetch the next i915_gem_active to try and hide
+                * the likely cache miss.
+                */
+               prefetchw(next);
+
+               INIT_LIST_HEAD(&active->link);
+               RCU_INIT_POINTER(active->request, NULL);
+
+               active->retire(active, request);
+       }
+
+       i915_gem_request_remove_from_client(request);
+
+       if (request->previous_context) {
+               if (i915.enable_execlists)
+                       intel_lr_context_unpin(request->previous_context,
+                                              request->engine);
+       }
+
+       i915_gem_context_put(request->ctx);
+       i915_gem_request_put(request);
+}
+
+void i915_gem_request_retire_upto(struct drm_i915_gem_request *req)
+{
+       struct intel_engine_cs *engine = req->engine;
+       struct drm_i915_gem_request *tmp;
+
+       lockdep_assert_held(&req->i915->drm.struct_mutex);
+       GEM_BUG_ON(list_empty(&req->link));
+
+       do {
+               tmp = list_first_entry(&engine->request_list,
+                                      typeof(*tmp), link);
+
+               i915_gem_request_retire(tmp);
+       } while (tmp != req);
+}
+
+static int i915_gem_check_wedge(struct drm_i915_private *dev_priv)
+{
+       struct i915_gpu_error *error = &dev_priv->gpu_error;
+
+       if (i915_terminally_wedged(error))
+               return -EIO;
+
+       if (i915_reset_in_progress(error)) {
+               /* Non-interruptible callers can't handle -EAGAIN, hence return
+                * -EIO unconditionally for these.
+                */
+               if (!dev_priv->mm.interruptible)
+                       return -EIO;
+
+               return -EAGAIN;
+       }
+
+       return 0;
+}
+
+static int i915_gem_init_seqno(struct drm_i915_private *dev_priv, u32 seqno)
+{
+       struct intel_engine_cs *engine;
+       int ret;
+
+       /* Carefully retire all requests without writing to the rings */
+       for_each_engine(engine, dev_priv) {
+               ret = intel_engine_idle(engine,
+                                       I915_WAIT_INTERRUPTIBLE |
+                                       I915_WAIT_LOCKED);
+               if (ret)
+                       return ret;
+       }
+       i915_gem_retire_requests(dev_priv);
+
+       /* If the seqno wraps around, we need to clear the breadcrumb rbtree */
+       if (!i915_seqno_passed(seqno, dev_priv->next_seqno)) {
+               while (intel_kick_waiters(dev_priv) ||
+                      intel_kick_signalers(dev_priv))
+                       yield();
+       }
+
+       /* Finally reset hw state */
+       for_each_engine(engine, dev_priv)
+               intel_engine_init_seqno(engine, seqno);
+
+       return 0;
+}
+
+int i915_gem_set_seqno(struct drm_device *dev, u32 seqno)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       int ret;
+
+       if (seqno == 0)
+               return -EINVAL;
+
+       /* HWS page needs to be set less than what we
+        * will inject to ring
+        */
+       ret = i915_gem_init_seqno(dev_priv, seqno - 1);
+       if (ret)
+               return ret;
+
+       dev_priv->next_seqno = seqno;
+       return 0;
+}
+
+static int i915_gem_get_seqno(struct drm_i915_private *dev_priv, u32 *seqno)
+{
+       /* reserve 0 for non-seqno */
+       if (unlikely(dev_priv->next_seqno == 0)) {
+               int ret;
+
+               ret = i915_gem_init_seqno(dev_priv, 0);
+               if (ret)
+                       return ret;
+
+               dev_priv->next_seqno = 1;
+       }
+
+       *seqno = dev_priv->next_seqno++;
+       return 0;
+}
+
+static int __i915_sw_fence_call
+submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state)
+{
+       struct drm_i915_gem_request *request =
+               container_of(fence, typeof(*request), submit);
+
+       /* Will be called from irq-context when using foreign DMA fences */
+
+       switch (state) {
+       case FENCE_COMPLETE:
+               request->engine->last_submitted_seqno = request->fence.seqno;
+               request->engine->submit_request(request);
+               break;
+
+       case FENCE_FREE:
+               break;
+       }
+
+       return NOTIFY_DONE;
+}
+
+/**
+ * i915_gem_request_alloc - allocate a request structure
+ *
+ * @engine: engine that we wish to issue the request on.
+ * @ctx: context that the request will be associated with.
+ *       This can be NULL if the request is not directly related to
+ *       any specific user context, in which case this function will
+ *       choose an appropriate context to use.
+ *
+ * Returns a pointer to the allocated request if successful,
+ * or an error code if not.
+ */
+struct drm_i915_gem_request *
+i915_gem_request_alloc(struct intel_engine_cs *engine,
+                      struct i915_gem_context *ctx)
+{
+       struct drm_i915_private *dev_priv = engine->i915;
+       struct drm_i915_gem_request *req;
+       u32 seqno;
+       int ret;
+
+       /* ABI: Before userspace accesses the GPU (e.g. execbuffer), report
+        * EIO if the GPU is already wedged, or EAGAIN to drop the struct_mutex
+        * and restart.
+        */
+       ret = i915_gem_check_wedge(dev_priv);
+       if (ret)
+               return ERR_PTR(ret);
+
+       /* Move the oldest request to the slab-cache (if not in use!) */
+       req = list_first_entry_or_null(&engine->request_list,
+                                      typeof(*req), link);
+       if (req && i915_gem_request_completed(req))
+               i915_gem_request_retire(req);
+
+       /* Beware: Dragons be flying overhead.
+        *
+        * We use RCU to look up requests in flight. The lookups may
+        * race with the request being allocated from the slab freelist.
+        * That is the request we are writing to here, may be in the process
+        * of being read by __i915_gem_active_get_rcu(). As such,
+        * we have to be very careful when overwriting the contents. During
+        * the RCU lookup, we change chase the request->engine pointer,
+        * read the request->fence.seqno and increment the reference count.
+        *
+        * The reference count is incremented atomically. If it is zero,
+        * the lookup knows the request is unallocated and complete. Otherwise,
+        * it is either still in use, or has been reallocated and reset
+        * with fence_init(). This increment is safe for release as we check
+        * that the request we have a reference to and matches the active
+        * request.
+        *
+        * Before we increment the refcount, we chase the request->engine
+        * pointer. We must not call kmem_cache_zalloc() or else we set
+        * that pointer to NULL and cause a crash during the lookup. If
+        * we see the request is completed (based on the value of the
+        * old engine and seqno), the lookup is complete and reports NULL.
+        * If we decide the request is not completed (new engine or seqno),
+        * then we grab a reference and double check that it is still the
+        * active request - which it won't be and restart the lookup.
+        *
+        * Do not use kmem_cache_zalloc() here!
+        */
+       req = kmem_cache_alloc(dev_priv->requests, GFP_KERNEL);
+       if (!req)
+               return ERR_PTR(-ENOMEM);
+
+       ret = i915_gem_get_seqno(dev_priv, &seqno);
+       if (ret)
+               goto err;
+
+       spin_lock_init(&req->lock);
+       fence_init(&req->fence,
+                  &i915_fence_ops,
+                  &req->lock,
+                  engine->fence_context,
+                  seqno);
+
+       i915_sw_fence_init(&req->submit, submit_notify);
+
+       INIT_LIST_HEAD(&req->active_list);
+       req->i915 = dev_priv;
+       req->engine = engine;
+       req->ctx = i915_gem_context_get(ctx);
+
+       /* No zalloc, must clear what we need by hand */
+       req->previous_context = NULL;
+       req->file_priv = NULL;
+       req->batch = NULL;
+
+       /*
+        * Reserve space in the ring buffer for all the commands required to
+        * eventually emit this request. This is to guarantee that the
+        * i915_add_request() call can't fail. Note that the reserve may need
+        * to be redone if the request is not actually submitted straight
+        * away, e.g. because a GPU scheduler has deferred it.
+        */
+       req->reserved_space = MIN_SPACE_FOR_ADD_REQUEST;
+
+       if (i915.enable_execlists)
+               ret = intel_logical_ring_alloc_request_extras(req);
+       else
+               ret = intel_ring_alloc_request_extras(req);
+       if (ret)
+               goto err_ctx;
+
+       /* Record the position of the start of the request so that
+        * should we detect the updated seqno part-way through the
+        * GPU processing the request, we never over-estimate the
+        * position of the head.
+        */
+       req->head = req->ring->tail;
+
+       return req;
+
+err_ctx:
+       i915_gem_context_put(ctx);
+err:
+       kmem_cache_free(dev_priv->requests, req);
+       return ERR_PTR(ret);
+}
+
+static int
+i915_gem_request_await_request(struct drm_i915_gem_request *to,
+                              struct drm_i915_gem_request *from)
+{
+       int idx, ret;
+
+       GEM_BUG_ON(to == from);
+
+       if (to->engine == from->engine)
+               return 0;
+
+       idx = intel_engine_sync_index(from->engine, to->engine);
+       if (from->fence.seqno <= from->engine->semaphore.sync_seqno[idx])
+               return 0;
+
+       trace_i915_gem_ring_sync_to(to, from);
+       if (!i915.semaphores) {
+               if (!i915_spin_request(from, TASK_INTERRUPTIBLE, 2)) {
+                       ret = i915_sw_fence_await_dma_fence(&to->submit,
+                                                           &from->fence, 0,
+                                                           GFP_KERNEL);
+                       if (ret < 0)
+                               return ret;
+               }
+       } else {
+               ret = to->engine->semaphore.sync_to(to, from);
+               if (ret)
+                       return ret;
+       }
+
+       from->engine->semaphore.sync_seqno[idx] = from->fence.seqno;
+       return 0;
+}
+
+/**
+ * i915_gem_request_await_object - set this request to (async) wait upon a bo
+ *
+ * @to: request we are wishing to use
+ * @obj: object which may be in use on another ring.
+ *
+ * This code is meant to abstract object synchronization with the GPU.
+ * Conceptually we serialise writes between engines inside the GPU.
+ * We only allow one engine to write into a buffer at any time, but
+ * multiple readers. To ensure each has a coherent view of memory, we must:
+ *
+ * - If there is an outstanding write request to the object, the new
+ *   request must wait for it to complete (either CPU or in hw, requests
+ *   on the same ring will be naturally ordered).
+ *
+ * - If we are a write request (pending_write_domain is set), the new
+ *   request must wait for outstanding read requests to complete.
+ *
+ * Returns 0 if successful, else propagates up the lower layer error.
+ */
+int
+i915_gem_request_await_object(struct drm_i915_gem_request *to,
+                             struct drm_i915_gem_object *obj,
+                             bool write)
+{
+       struct i915_gem_active *active;
+       unsigned long active_mask;
+       int idx;
+
+       if (write) {
+               active_mask = i915_gem_object_get_active(obj);
+               active = obj->last_read;
+       } else {
+               active_mask = 1;
+               active = &obj->last_write;
+       }
+
+       for_each_active(active_mask, idx) {
+               struct drm_i915_gem_request *request;
+               int ret;
+
+               request = i915_gem_active_peek(&active[idx],
+                                              &obj->base.dev->struct_mutex);
+               if (!request)
+                       continue;
+
+               ret = i915_gem_request_await_request(to, request);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void i915_gem_mark_busy(const struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *dev_priv = engine->i915;
+
+       dev_priv->gt.active_engines |= intel_engine_flag(engine);
+       if (dev_priv->gt.awake)
+               return;
+
+       intel_runtime_pm_get_noresume(dev_priv);
+       dev_priv->gt.awake = true;
+
+       intel_enable_gt_powersave(dev_priv);
+       i915_update_gfx_val(dev_priv);
+       if (INTEL_GEN(dev_priv) >= 6)
+               gen6_rps_busy(dev_priv);
+
+       queue_delayed_work(dev_priv->wq,
+                          &dev_priv->gt.retire_work,
+                          round_jiffies_up_relative(HZ));
+}
+
+/*
+ * NB: This function is not allowed to fail. Doing so would mean the the
+ * request is not being tracked for completion but the work itself is
+ * going to happen on the hardware. This would be a Bad Thing(tm).
+ */
+void __i915_add_request(struct drm_i915_gem_request *request, bool flush_caches)
+{
+       struct intel_engine_cs *engine = request->engine;
+       struct intel_ring *ring = request->ring;
+       struct drm_i915_gem_request *prev;
+       u32 request_start;
+       u32 reserved_tail;
+       int ret;
+
+       trace_i915_gem_request_add(request);
+
+       /*
+        * To ensure that this call will not fail, space for its emissions
+        * should already have been reserved in the ring buffer. Let the ring
+        * know that it is time to use that space up.
+        */
+       request_start = ring->tail;
+       reserved_tail = request->reserved_space;
+       request->reserved_space = 0;
+
+       /*
+        * Emit any outstanding flushes - execbuf can fail to emit the flush
+        * after having emitted the batchbuffer command. Hence we need to fix
+        * things up similar to emitting the lazy request. The difference here
+        * is that the flush _must_ happen before the next request, no matter
+        * what.
+        */
+       if (flush_caches) {
+               ret = engine->emit_flush(request, EMIT_FLUSH);
+
+               /* Not allowed to fail! */
+               WARN(ret, "engine->emit_flush() failed: %d!\n", ret);
+       }
+
+       /* Record the position of the start of the breadcrumb so that
+        * should we detect the updated seqno part-way through the
+        * GPU processing the request, we never over-estimate the
+        * position of the ring's HEAD.
+        */
+       request->postfix = ring->tail;
+
+       /* Not allowed to fail! */
+       ret = engine->emit_request(request);
+       WARN(ret, "(%s)->emit_request failed: %d!\n", engine->name, ret);
+
+       /* Sanity check that the reserved size was large enough. */
+       ret = ring->tail - request_start;
+       if (ret < 0)
+               ret += ring->size;
+       WARN_ONCE(ret > reserved_tail,
+                 "Not enough space reserved (%d bytes) "
+                 "for adding the request (%d bytes)\n",
+                 reserved_tail, ret);
+
+       /* Seal the request and mark it as pending execution. Note that
+        * we may inspect this state, without holding any locks, during
+        * hangcheck. Hence we apply the barrier to ensure that we do not
+        * see a more recent value in the hws than we are tracking.
+        */
+
+       prev = i915_gem_active_raw(&engine->last_request,
+                                  &request->i915->drm.struct_mutex);
+       if (prev)
+               i915_sw_fence_await_sw_fence(&request->submit, &prev->submit,
+                                            &request->submitq);
+
+       request->emitted_jiffies = jiffies;
+       request->previous_seqno = engine->last_pending_seqno;
+       engine->last_pending_seqno = request->fence.seqno;
+       i915_gem_active_set(&engine->last_request, request);
+       list_add_tail(&request->link, &engine->request_list);
+       list_add_tail(&request->ring_link, &ring->request_list);
+
+       i915_gem_mark_busy(engine);
+
+       local_bh_disable();
+       i915_sw_fence_commit(&request->submit);
+       local_bh_enable(); /* Kick the execlists tasklet if just scheduled */
+}
+
+static void reset_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&q->lock, flags);
+       if (list_empty(&wait->task_list))
+               __add_wait_queue(q, wait);
+       spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static unsigned long local_clock_us(unsigned int *cpu)
+{
+       unsigned long t;
+
+       /* Cheaply and approximately convert from nanoseconds to microseconds.
+        * The result and subsequent calculations are also defined in the same
+        * approximate microseconds units. The principal source of timing
+        * error here is from the simple truncation.
+        *
+        * Note that local_clock() is only defined wrt to the current CPU;
+        * the comparisons are no longer valid if we switch CPUs. Instead of
+        * blocking preemption for the entire busywait, we can detect the CPU
+        * switch and use that as indicator of system load and a reason to
+        * stop busywaiting, see busywait_stop().
+        */
+       *cpu = get_cpu();
+       t = local_clock() >> 10;
+       put_cpu();
+
+       return t;
+}
+
+static bool busywait_stop(unsigned long timeout, unsigned int cpu)
+{
+       unsigned int this_cpu;
+
+       if (time_after(local_clock_us(&this_cpu), timeout))
+               return true;
+
+       return this_cpu != cpu;
+}
+
+bool __i915_spin_request(const struct drm_i915_gem_request *req,
+                        int state, unsigned long timeout_us)
+{
+       unsigned int cpu;
+
+       /* When waiting for high frequency requests, e.g. during synchronous
+        * rendering split between the CPU and GPU, the finite amount of time
+        * required to set up the irq and wait upon it limits the response
+        * rate. By busywaiting on the request completion for a short while we
+        * can service the high frequency waits as quick as possible. However,
+        * if it is a slow request, we want to sleep as quickly as possible.
+        * The tradeoff between waiting and sleeping is roughly the time it
+        * takes to sleep on a request, on the order of a microsecond.
+        */
+
+       timeout_us += local_clock_us(&cpu);
+       do {
+               if (i915_gem_request_completed(req))
+                       return true;
+
+               if (signal_pending_state(state, current))
+                       break;
+
+               if (busywait_stop(timeout_us, cpu))
+                       break;
+
+               cpu_relax_lowlatency();
+       } while (!need_resched());
+
+       return false;
+}
+
+/**
+ * i915_wait_request - wait until execution of request has finished
+ * @req: duh!
+ * @flags: how to wait
+ * @timeout: in - how long to wait (NULL forever); out - how much time remaining
+ * @rps: client to charge for RPS boosting
+ *
+ * Note: It is of utmost importance that the passed in seqno and reset_counter
+ * values have been read by the caller in an smp safe manner. Where read-side
+ * locks are involved, it is sufficient to read the reset_counter before
+ * unlocking the lock that protects the seqno. For lockless tricks, the
+ * reset_counter _must_ be read before, and an appropriate smp_rmb must be
+ * inserted.
+ *
+ * Returns 0 if the request was found within the alloted time. Else returns the
+ * errno with remaining time filled in timeout argument.
+ */
+int i915_wait_request(struct drm_i915_gem_request *req,
+                     unsigned int flags,
+                     s64 *timeout,
+                     struct intel_rps_client *rps)
+{
+       const int state = flags & I915_WAIT_INTERRUPTIBLE ?
+               TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE;
+       DEFINE_WAIT(reset);
+       struct intel_wait wait;
+       unsigned long timeout_remain;
+       int ret = 0;
+
+       might_sleep();
+#if IS_ENABLED(CONFIG_LOCKDEP)
+       GEM_BUG_ON(!!lockdep_is_held(&req->i915->drm.struct_mutex) !=
+                  !!(flags & I915_WAIT_LOCKED));
+#endif
+
+       if (i915_gem_request_completed(req))
+               return 0;
+
+       timeout_remain = MAX_SCHEDULE_TIMEOUT;
+       if (timeout) {
+               if (WARN_ON(*timeout < 0))
+                       return -EINVAL;
+
+               if (*timeout == 0)
+                       return -ETIME;
+
+               /* Record current time in case interrupted, or wedged */
+               timeout_remain = nsecs_to_jiffies_timeout(*timeout);
+               *timeout += ktime_get_raw_ns();
+       }
+
+       trace_i915_gem_request_wait_begin(req);
+
+       /* This client is about to stall waiting for the GPU. In many cases
+        * this is undesirable and limits the throughput of the system, as
+        * many clients cannot continue processing user input/output whilst
+        * blocked. RPS autotuning may take tens of milliseconds to respond
+        * to the GPU load and thus incurs additional latency for the client.
+        * We can circumvent that by promoting the GPU frequency to maximum
+        * before we wait. This makes the GPU throttle up much more quickly
+        * (good for benchmarks and user experience, e.g. window animations),
+        * but at a cost of spending more power processing the workload
+        * (bad for battery). Not all clients even want their results
+        * immediately and for them we should just let the GPU select its own
+        * frequency to maximise efficiency. To prevent a single client from
+        * forcing the clocks too high for the whole system, we only allow
+        * each client to waitboost once in a busy period.
+        */
+       if (IS_RPS_CLIENT(rps) && INTEL_GEN(req->i915) >= 6)
+               gen6_rps_boost(req->i915, rps, req->emitted_jiffies);
+
+       /* Optimistic short spin before touching IRQs */
+       if (i915_spin_request(req, state, 5))
+               goto complete;
+
+       set_current_state(state);
+       if (flags & I915_WAIT_LOCKED)
+               add_wait_queue(&req->i915->gpu_error.wait_queue, &reset);
+
+       intel_wait_init(&wait, req->fence.seqno);
+       if (intel_engine_add_wait(req->engine, &wait))
+               /* In order to check that we haven't missed the interrupt
+                * as we enabled it, we need to kick ourselves to do a
+                * coherent check on the seqno before we sleep.
+                */
+               goto wakeup;
+
+       for (;;) {
+               if (signal_pending_state(state, current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               timeout_remain = io_schedule_timeout(timeout_remain);
+               if (timeout_remain == 0) {
+                       ret = -ETIME;
+                       break;
+               }
+
+               if (intel_wait_complete(&wait))
+                       break;
+
+               set_current_state(state);
+
+wakeup:
+               /* Carefully check if the request is complete, giving time
+                * for the seqno to be visible following the interrupt.
+                * We also have to check in case we are kicked by the GPU
+                * reset in order to drop the struct_mutex.
+                */
+               if (__i915_request_irq_complete(req))
+                       break;
+
+               /* If the GPU is hung, and we hold the lock, reset the GPU
+                * and then check for completion. On a full reset, the engine's
+                * HW seqno will be advanced passed us and we are complete.
+                * If we do a partial reset, we have to wait for the GPU to
+                * resume and update the breadcrumb.
+                *
+                * If we don't hold the mutex, we can just wait for the worker
+                * to come along and update the breadcrumb (either directly
+                * itself, or indirectly by recovering the GPU).
+                */
+               if (flags & I915_WAIT_LOCKED &&
+                   i915_reset_in_progress(&req->i915->gpu_error)) {
+                       __set_current_state(TASK_RUNNING);
+                       i915_reset(req->i915);
+                       reset_wait_queue(&req->i915->gpu_error.wait_queue,
+                                        &reset);
+                       continue;
+               }
+
+               /* Only spin if we know the GPU is processing this request */
+               if (i915_spin_request(req, state, 2))
+                       break;
+       }
+
+       intel_engine_remove_wait(req->engine, &wait);
+       if (flags & I915_WAIT_LOCKED)
+               remove_wait_queue(&req->i915->gpu_error.wait_queue, &reset);
+       __set_current_state(TASK_RUNNING);
+
+complete:
+       trace_i915_gem_request_wait_end(req);
+
+       if (timeout) {
+               *timeout -= ktime_get_raw_ns();
+               if (*timeout < 0)
+                       *timeout = 0;
+
+               /*
+                * Apparently ktime isn't accurate enough and occasionally has a
+                * bit of mismatch in the jiffies<->nsecs<->ktime loop. So patch
+                * things up to make the test happy. We allow up to 1 jiffy.
+                *
+                * This is a regrssion from the timespec->ktime conversion.
+                */
+               if (ret == -ETIME && *timeout < jiffies_to_usecs(1)*1000)
+                       *timeout = 0;
+       }
+
+       if (IS_RPS_USER(rps) &&
+           req->fence.seqno == req->engine->last_submitted_seqno) {
+               /* The GPU is now idle and this client has stalled.
+                * Since no other client has submitted a request in the
+                * meantime, assume that this client is the only one
+                * supplying work to the GPU but is unable to keep that
+                * work supplied because it is waiting. Since the GPU is
+                * then never kept fully busy, RPS autoclocking will
+                * keep the clocks relatively low, causing further delays.
+                * Compensate by giving the synchronous client credit for
+                * a waitboost next time.
+                */
+               spin_lock(&req->i915->rps.client_lock);
+               list_del_init(&rps->link);
+               spin_unlock(&req->i915->rps.client_lock);
+       }
+
+       return ret;
+}
+
+static bool engine_retire_requests(struct intel_engine_cs *engine)
+{
+       struct drm_i915_gem_request *request, *next;
+
+       list_for_each_entry_safe(request, next, &engine->request_list, link) {
+               if (!i915_gem_request_completed(request))
+                       return false;
+
+               i915_gem_request_retire(request);
+       }
+
+       return true;
+}
+
+void i915_gem_retire_requests(struct drm_i915_private *dev_priv)
+{
+       struct intel_engine_cs *engine;
+       unsigned int tmp;
+
+       lockdep_assert_held(&dev_priv->drm.struct_mutex);
+
+       if (dev_priv->gt.active_engines == 0)
+               return;
+
+       GEM_BUG_ON(!dev_priv->gt.awake);
+
+       for_each_engine_masked(engine, dev_priv, dev_priv->gt.active_engines, tmp)
+               if (engine_retire_requests(engine))
+                       dev_priv->gt.active_engines &= ~intel_engine_flag(engine);
+
+       if (dev_priv->gt.active_engines == 0)
+               queue_delayed_work(dev_priv->wq,
+                                  &dev_priv->gt.idle_work,
+                                  msecs_to_jiffies(100));
+}
diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h
new file mode 100644 (file)
index 0000000..974bd7b
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * Copyright © 2008-2015 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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 I915_GEM_REQUEST_H
+#define I915_GEM_REQUEST_H
+
+#include <linux/fence.h>
+
+#include "i915_gem.h"
+#include "i915_sw_fence.h"
+
+struct intel_wait {
+       struct rb_node node;
+       struct task_struct *tsk;
+       u32 seqno;
+};
+
+struct intel_signal_node {
+       struct rb_node node;
+       struct intel_wait wait;
+};
+
+/**
+ * Request queue structure.
+ *
+ * The request queue allows us to note sequence numbers that have been emitted
+ * and may be associated with active buffers to be retired.
+ *
+ * By keeping this list, we can avoid having to do questionable sequence
+ * number comparisons on buffer last_read|write_seqno. It also allows an
+ * emission time to be associated with the request for tracking how far ahead
+ * of the GPU the submission is.
+ *
+ * When modifying this structure be very aware that we perform a lockless
+ * RCU lookup of it that may race against reallocation of the struct
+ * from the slab freelist. We intentionally do not zero the structure on
+ * allocation so that the lookup can use the dangling pointers (and is
+ * cogniscent that those pointers may be wrong). Instead, everything that
+ * needs to be initialised must be done so explicitly.
+ *
+ * The requests are reference counted.
+ */
+struct drm_i915_gem_request {
+       struct fence fence;
+       spinlock_t lock;
+
+       /** On Which ring this request was generated */
+       struct drm_i915_private *i915;
+
+       /**
+        * Context and ring buffer related to this request
+        * Contexts are refcounted, so when this request is associated with a
+        * context, we must increment the context's refcount, to guarantee that
+        * it persists while any request is linked to it. Requests themselves
+        * are also refcounted, so the request will only be freed when the last
+        * reference to it is dismissed, and the code in
+        * i915_gem_request_free() will then decrement the refcount on the
+        * context.
+        */
+       struct i915_gem_context *ctx;
+       struct intel_engine_cs *engine;
+       struct intel_ring *ring;
+       struct intel_signal_node signaling;
+
+       struct i915_sw_fence submit;
+       wait_queue_t submitq;
+
+       /** GEM sequence number associated with the previous request,
+        * when the HWS breadcrumb is equal to this the GPU is processing
+        * this request.
+        */
+       u32 previous_seqno;
+
+       /** Position in the ring of the start of the request */
+       u32 head;
+
+       /**
+        * Position in the ring of the start of the postfix.
+        * This is required to calculate the maximum available ring space
+        * without overwriting the postfix.
+        */
+       u32 postfix;
+
+       /** Position in the ring of the end of the whole request */
+       u32 tail;
+
+       /** Position in the ring of the end of any workarounds after the tail */
+       u32 wa_tail;
+
+       /** Preallocate space in the ring for the emitting the request */
+       u32 reserved_space;
+
+       /**
+        * Context related to the previous request.
+        * As the contexts are accessed by the hardware until the switch is
+        * completed to a new context, the hardware may still be writing
+        * to the context object after the breadcrumb is visible. We must
+        * not unpin/unbind/prune that object whilst still active and so
+        * we keep the previous context pinned until the following (this)
+        * request is retired.
+        */
+       struct i915_gem_context *previous_context;
+
+       /** Batch buffer related to this request if any (used for
+        * error state dump only).
+        */
+       struct i915_vma *batch;
+       struct list_head active_list;
+
+       /** Time at which this request was emitted, in jiffies. */
+       unsigned long emitted_jiffies;
+
+       /** engine->request_list entry for this request */
+       struct list_head link;
+
+       /** ring->request_list entry for this request */
+       struct list_head ring_link;
+
+       struct drm_i915_file_private *file_priv;
+       /** file_priv list entry for this request */
+       struct list_head client_list;
+
+       /** Link in the execlist submission queue, guarded by execlist_lock. */
+       struct list_head execlist_link;
+};
+
+extern const struct fence_ops i915_fence_ops;
+
+static inline bool fence_is_i915(struct fence *fence)
+{
+       return fence->ops == &i915_fence_ops;
+}
+
+struct drm_i915_gem_request * __must_check
+i915_gem_request_alloc(struct intel_engine_cs *engine,
+                      struct i915_gem_context *ctx);
+int i915_gem_request_add_to_client(struct drm_i915_gem_request *req,
+                                  struct drm_file *file);
+void i915_gem_request_retire_upto(struct drm_i915_gem_request *req);
+
+static inline u32
+i915_gem_request_get_seqno(struct drm_i915_gem_request *req)
+{
+       return req ? req->fence.seqno : 0;
+}
+
+static inline struct intel_engine_cs *
+i915_gem_request_get_engine(struct drm_i915_gem_request *req)
+{
+       return req ? req->engine : NULL;
+}
+
+static inline struct drm_i915_gem_request *
+to_request(struct fence *fence)
+{
+       /* We assume that NULL fence/request are interoperable */
+       BUILD_BUG_ON(offsetof(struct drm_i915_gem_request, fence) != 0);
+       GEM_BUG_ON(fence && !fence_is_i915(fence));
+       return container_of(fence, struct drm_i915_gem_request, fence);
+}
+
+static inline struct drm_i915_gem_request *
+i915_gem_request_get(struct drm_i915_gem_request *req)
+{
+       return to_request(fence_get(&req->fence));
+}
+
+static inline struct drm_i915_gem_request *
+i915_gem_request_get_rcu(struct drm_i915_gem_request *req)
+{
+       return to_request(fence_get_rcu(&req->fence));
+}
+
+static inline void
+i915_gem_request_put(struct drm_i915_gem_request *req)
+{
+       fence_put(&req->fence);
+}
+
+static inline void i915_gem_request_assign(struct drm_i915_gem_request **pdst,
+                                          struct drm_i915_gem_request *src)
+{
+       if (src)
+               i915_gem_request_get(src);
+
+       if (*pdst)
+               i915_gem_request_put(*pdst);
+
+       *pdst = src;
+}
+
+int
+i915_gem_request_await_object(struct drm_i915_gem_request *to,
+                             struct drm_i915_gem_object *obj,
+                             bool write);
+
+void __i915_add_request(struct drm_i915_gem_request *req, bool flush_caches);
+#define i915_add_request(req) \
+       __i915_add_request(req, true)
+#define i915_add_request_no_flush(req) \
+       __i915_add_request(req, false)
+
+struct intel_rps_client;
+#define NO_WAITBOOST ERR_PTR(-1)
+#define IS_RPS_CLIENT(p) (!IS_ERR(p))
+#define IS_RPS_USER(p) (!IS_ERR_OR_NULL(p))
+
+int i915_wait_request(struct drm_i915_gem_request *req,
+                     unsigned int flags,
+                     s64 *timeout,
+                     struct intel_rps_client *rps)
+       __attribute__((nonnull(1)));
+#define I915_WAIT_INTERRUPTIBLE        BIT(0)
+#define I915_WAIT_LOCKED       BIT(1) /* struct_mutex held, handle GPU reset */
+
+static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine);
+
+/**
+ * Returns true if seq1 is later than seq2.
+ */
+static inline bool i915_seqno_passed(u32 seq1, u32 seq2)
+{
+       return (s32)(seq1 - seq2) >= 0;
+}
+
+static inline bool
+i915_gem_request_started(const struct drm_i915_gem_request *req)
+{
+       return i915_seqno_passed(intel_engine_get_seqno(req->engine),
+                                req->previous_seqno);
+}
+
+static inline bool
+i915_gem_request_completed(const struct drm_i915_gem_request *req)
+{
+       return i915_seqno_passed(intel_engine_get_seqno(req->engine),
+                                req->fence.seqno);
+}
+
+bool __i915_spin_request(const struct drm_i915_gem_request *request,
+                        int state, unsigned long timeout_us);
+static inline bool i915_spin_request(const struct drm_i915_gem_request *request,
+                                    int state, unsigned long timeout_us)
+{
+       return (i915_gem_request_started(request) &&
+               __i915_spin_request(request, state, timeout_us));
+}
+
+/* We treat requests as fences. This is not be to confused with our
+ * "fence registers" but pipeline synchronisation objects ala GL_ARB_sync.
+ * We use the fences to synchronize access from the CPU with activity on the
+ * GPU, for example, we should not rewrite an object's PTE whilst the GPU
+ * is reading them. We also track fences at a higher level to provide
+ * implicit synchronisation around GEM objects, e.g. set-domain will wait
+ * for outstanding GPU rendering before marking the object ready for CPU
+ * access, or a pageflip will wait until the GPU is complete before showing
+ * the frame on the scanout.
+ *
+ * In order to use a fence, the object must track the fence it needs to
+ * serialise with. For example, GEM objects want to track both read and
+ * write access so that we can perform concurrent read operations between
+ * the CPU and GPU engines, as well as waiting for all rendering to
+ * complete, or waiting for the last GPU user of a "fence register". The
+ * object then embeds a #i915_gem_active to track the most recent (in
+ * retirement order) request relevant for the desired mode of access.
+ * The #i915_gem_active is updated with i915_gem_active_set() to track the
+ * most recent fence request, typically this is done as part of
+ * i915_vma_move_to_active().
+ *
+ * When the #i915_gem_active completes (is retired), it will
+ * signal its completion to the owner through a callback as well as mark
+ * itself as idle (i915_gem_active.request == NULL). The owner
+ * can then perform any action, such as delayed freeing of an active
+ * resource including itself.
+ */
+struct i915_gem_active;
+
+typedef void (*i915_gem_retire_fn)(struct i915_gem_active *,
+                                  struct drm_i915_gem_request *);
+
+struct i915_gem_active {
+       struct drm_i915_gem_request __rcu *request;
+       struct list_head link;
+       i915_gem_retire_fn retire;
+};
+
+void i915_gem_retire_noop(struct i915_gem_active *,
+                         struct drm_i915_gem_request *request);
+
+/**
+ * init_request_active - prepares the activity tracker for use
+ * @active - the active tracker
+ * @func - a callback when then the tracker is retired (becomes idle),
+ *         can be NULL
+ *
+ * init_request_active() prepares the embedded @active struct for use as
+ * an activity tracker, that is for tracking the last known active request
+ * associated with it. When the last request becomes idle, when it is retired
+ * after completion, the optional callback @func is invoked.
+ */
+static inline void
+init_request_active(struct i915_gem_active *active,
+                   i915_gem_retire_fn retire)
+{
+       INIT_LIST_HEAD(&active->link);
+       active->retire = retire ?: i915_gem_retire_noop;
+}
+
+/**
+ * i915_gem_active_set - updates the tracker to watch the current request
+ * @active - the active tracker
+ * @request - the request to watch
+ *
+ * i915_gem_active_set() watches the given @request for completion. Whilst
+ * that @request is busy, the @active reports busy. When that @request is
+ * retired, the @active tracker is updated to report idle.
+ */
+static inline void
+i915_gem_active_set(struct i915_gem_active *active,
+                   struct drm_i915_gem_request *request)
+{
+       list_move(&active->link, &request->active_list);
+       rcu_assign_pointer(active->request, request);
+}
+
+static inline struct drm_i915_gem_request *
+__i915_gem_active_peek(const struct i915_gem_active *active)
+{
+       /* Inside the error capture (running with the driver in an unknown
+        * state), we want to bend the rules slightly (a lot).
+        *
+        * Work is in progress to make it safer, in the meantime this keeps
+        * the known issue from spamming the logs.
+        */
+       return rcu_dereference_protected(active->request, 1);
+}
+
+/**
+ * i915_gem_active_raw - return the active request
+ * @active - the active tracker
+ *
+ * i915_gem_active_raw() returns the current request being tracked, or NULL.
+ * It does not obtain a reference on the request for the caller, so the caller
+ * must hold struct_mutex.
+ */
+static inline struct drm_i915_gem_request *
+i915_gem_active_raw(const struct i915_gem_active *active, struct mutex *mutex)
+{
+       return rcu_dereference_protected(active->request,
+                                        lockdep_is_held(mutex));
+}
+
+/**
+ * i915_gem_active_peek - report the active request being monitored
+ * @active - the active tracker
+ *
+ * i915_gem_active_peek() returns the current request being tracked if
+ * still active, or NULL. It does not obtain a reference on the request
+ * for the caller, so the caller must hold struct_mutex.
+ */
+static inline struct drm_i915_gem_request *
+i915_gem_active_peek(const struct i915_gem_active *active, struct mutex *mutex)
+{
+       struct drm_i915_gem_request *request;
+
+       request = i915_gem_active_raw(active, mutex);
+       if (!request || i915_gem_request_completed(request))
+               return NULL;
+
+       return request;
+}
+
+/**
+ * i915_gem_active_get - return a reference to the active request
+ * @active - the active tracker
+ *
+ * i915_gem_active_get() returns a reference to the active request, or NULL
+ * if the active tracker is idle. The caller must hold struct_mutex.
+ */
+static inline struct drm_i915_gem_request *
+i915_gem_active_get(const struct i915_gem_active *active, struct mutex *mutex)
+{
+       return i915_gem_request_get(i915_gem_active_peek(active, mutex));
+}
+
+/**
+ * __i915_gem_active_get_rcu - return a reference to the active request
+ * @active - the active tracker
+ *
+ * __i915_gem_active_get() returns a reference to the active request, or NULL
+ * if the active tracker is idle. The caller must hold the RCU read lock, but
+ * the returned pointer is safe to use outside of RCU.
+ */
+static inline struct drm_i915_gem_request *
+__i915_gem_active_get_rcu(const struct i915_gem_active *active)
+{
+       /* Performing a lockless retrieval of the active request is super
+        * tricky. SLAB_DESTROY_BY_RCU merely guarantees that the backing
+        * slab of request objects will not be freed whilst we hold the
+        * RCU read lock. It does not guarantee that the request itself
+        * will not be freed and then *reused*. Viz,
+        *
+        * Thread A                     Thread B
+        *
+        * req = active.request
+        *                              retire(req) -> free(req);
+        *                              (req is now first on the slab freelist)
+        *                              active.request = NULL
+        *
+        *                              req = new submission on a new object
+        * ref(req)
+        *
+        * To prevent the request from being reused whilst the caller
+        * uses it, we take a reference like normal. Whilst acquiring
+        * the reference we check that it is not in a destroyed state
+        * (refcnt == 0). That prevents the request being reallocated
+        * whilst the caller holds on to it. To check that the request
+        * was not reallocated as we acquired the reference we have to
+        * check that our request remains the active request across
+        * the lookup, in the same manner as a seqlock. The visibility
+        * of the pointer versus the reference counting is controlled
+        * by using RCU barriers (rcu_dereference and rcu_assign_pointer).
+        *
+        * In the middle of all that, we inspect whether the request is
+        * complete. Retiring is lazy so the request may be completed long
+        * before the active tracker is updated. Querying whether the
+        * request is complete is far cheaper (as it involves no locked
+        * instructions setting cachelines to exclusive) than acquiring
+        * the reference, so we do it first. The RCU read lock ensures the
+        * pointer dereference is valid, but does not ensure that the
+        * seqno nor HWS is the right one! However, if the request was
+        * reallocated, that means the active tracker's request was complete.
+        * If the new request is also complete, then both are and we can
+        * just report the active tracker is idle. If the new request is
+        * incomplete, then we acquire a reference on it and check that
+        * it remained the active request.
+        *
+        * It is then imperative that we do not zero the request on
+        * reallocation, so that we can chase the dangling pointers!
+        * See i915_gem_request_alloc().
+        */
+       do {
+               struct drm_i915_gem_request *request;
+
+               request = rcu_dereference(active->request);
+               if (!request || i915_gem_request_completed(request))
+                       return NULL;
+
+               /* An especially silly compiler could decide to recompute the
+                * result of i915_gem_request_completed, more specifically
+                * re-emit the load for request->fence.seqno. A race would catch
+                * a later seqno value, which could flip the result from true to
+                * false. Which means part of the instructions below might not
+                * be executed, while later on instructions are executed. Due to
+                * barriers within the refcounting the inconsistency can't reach
+                * past the call to i915_gem_request_get_rcu, but not executing
+                * that while still executing i915_gem_request_put() creates
+                * havoc enough.  Prevent this with a compiler barrier.
+                */
+               barrier();
+
+               request = i915_gem_request_get_rcu(request);
+
+               /* What stops the following rcu_access_pointer() from occurring
+                * before the above i915_gem_request_get_rcu()? If we were
+                * to read the value before pausing to get the reference to
+                * the request, we may not notice a change in the active
+                * tracker.
+                *
+                * The rcu_access_pointer() is a mere compiler barrier, which
+                * means both the CPU and compiler are free to perform the
+                * memory read without constraint. The compiler only has to
+                * ensure that any operations after the rcu_access_pointer()
+                * occur afterwards in program order. This means the read may
+                * be performed earlier by an out-of-order CPU, or adventurous
+                * compiler.
+                *
+                * The atomic operation at the heart of
+                * i915_gem_request_get_rcu(), see fence_get_rcu(), is
+                * atomic_inc_not_zero() which is only a full memory barrier
+                * when successful. That is, if i915_gem_request_get_rcu()
+                * returns the request (and so with the reference counted
+                * incremented) then the following read for rcu_access_pointer()
+                * must occur after the atomic operation and so confirm
+                * that this request is the one currently being tracked.
+                *
+                * The corresponding write barrier is part of
+                * rcu_assign_pointer().
+                */
+               if (!request || request == rcu_access_pointer(active->request))
+                       return rcu_pointer_handoff(request);
+
+               i915_gem_request_put(request);
+       } while (1);
+}
+
+/**
+ * i915_gem_active_get_unlocked - return a reference to the active request
+ * @active - the active tracker
+ *
+ * i915_gem_active_get_unlocked() returns a reference to the active request,
+ * or NULL if the active tracker is idle. The reference is obtained under RCU,
+ * so no locking is required by the caller.
+ *
+ * The reference should be freed with i915_gem_request_put().
+ */
+static inline struct drm_i915_gem_request *
+i915_gem_active_get_unlocked(const struct i915_gem_active *active)
+{
+       struct drm_i915_gem_request *request;
+
+       rcu_read_lock();
+       request = __i915_gem_active_get_rcu(active);
+       rcu_read_unlock();
+
+       return request;
+}
+
+/**
+ * i915_gem_active_isset - report whether the active tracker is assigned
+ * @active - the active tracker
+ *
+ * i915_gem_active_isset() returns true if the active tracker is currently
+ * assigned to a request. Due to the lazy retiring, that request may be idle
+ * and this may report stale information.
+ */
+static inline bool
+i915_gem_active_isset(const struct i915_gem_active *active)
+{
+       return rcu_access_pointer(active->request);
+}
+
+/**
+ * i915_gem_active_is_idle - report whether the active tracker is idle
+ * @active - the active tracker
+ *
+ * i915_gem_active_is_idle() returns true if the active tracker is currently
+ * unassigned or if the request is complete (but not yet retired). Requires
+ * the caller to hold struct_mutex (but that can be relaxed if desired).
+ */
+static inline bool
+i915_gem_active_is_idle(const struct i915_gem_active *active,
+                       struct mutex *mutex)
+{
+       return !i915_gem_active_peek(active, mutex);
+}
+
+/**
+ * i915_gem_active_wait - waits until the request is completed
+ * @active - the active request on which to wait
+ *
+ * i915_gem_active_wait() waits until the request is completed before
+ * returning. Note that it does not guarantee that the request is
+ * retired first, see i915_gem_active_retire().
+ *
+ * i915_gem_active_wait() returns immediately if the active
+ * request is already complete.
+ */
+static inline int __must_check
+i915_gem_active_wait(const struct i915_gem_active *active, struct mutex *mutex)
+{
+       struct drm_i915_gem_request *request;
+
+       request = i915_gem_active_peek(active, mutex);
+       if (!request)
+               return 0;
+
+       return i915_wait_request(request,
+                                I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED,
+                                NULL, NULL);
+}
+
+/**
+ * i915_gem_active_wait_unlocked - waits until the request is completed
+ * @active - the active request on which to wait
+ * @flags - how to wait
+ * @timeout - how long to wait at most
+ * @rps - userspace client to charge for a waitboost
+ *
+ * i915_gem_active_wait_unlocked() waits until the request is completed before
+ * returning, without requiring any locks to be held. Note that it does not
+ * retire any requests before returning.
+ *
+ * This function relies on RCU in order to acquire the reference to the active
+ * request without holding any locks. See __i915_gem_active_get_rcu() for the
+ * glory details on how that is managed. Once the reference is acquired, we
+ * can then wait upon the request, and afterwards release our reference,
+ * free of any locking.
+ *
+ * This function wraps i915_wait_request(), see it for the full details on
+ * the arguments.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+static inline int
+i915_gem_active_wait_unlocked(const struct i915_gem_active *active,
+                             unsigned int flags,
+                             s64 *timeout,
+                             struct intel_rps_client *rps)
+{
+       struct drm_i915_gem_request *request;
+       int ret = 0;
+
+       request = i915_gem_active_get_unlocked(active);
+       if (request) {
+               ret = i915_wait_request(request, flags, timeout, rps);
+               i915_gem_request_put(request);
+       }
+
+       return ret;
+}
+
+/**
+ * i915_gem_active_retire - waits until the request is retired
+ * @active - the active request on which to wait
+ *
+ * i915_gem_active_retire() waits until the request is completed,
+ * and then ensures that at least the retirement handler for this
+ * @active tracker is called before returning. If the @active
+ * tracker is idle, the function returns immediately.
+ */
+static inline int __must_check
+i915_gem_active_retire(struct i915_gem_active *active,
+                      struct mutex *mutex)
+{
+       struct drm_i915_gem_request *request;
+       int ret;
+
+       request = i915_gem_active_raw(active, mutex);
+       if (!request)
+               return 0;
+
+       ret = i915_wait_request(request,
+                               I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED,
+                               NULL, NULL);
+       if (ret)
+               return ret;
+
+       list_del_init(&active->link);
+       RCU_INIT_POINTER(active->request, NULL);
+
+       active->retire(active, request);
+
+       return 0;
+}
+
+/* Convenience functions for peeking at state inside active's request whilst
+ * guarded by the struct_mutex.
+ */
+
+static inline uint32_t
+i915_gem_active_get_seqno(const struct i915_gem_active *active,
+                         struct mutex *mutex)
+{
+       return i915_gem_request_get_seqno(i915_gem_active_peek(active, mutex));
+}
+
+static inline struct intel_engine_cs *
+i915_gem_active_get_engine(const struct i915_gem_active *active,
+                          struct mutex *mutex)
+{
+       return i915_gem_request_get_engine(i915_gem_active_peek(active, mutex));
+}
+
+#define for_each_active(mask, idx) \
+       for (; mask ? idx = ffs(mask) - 1, 1 : 0; mask &= ~BIT(idx))
+
+#endif /* I915_GEM_REQUEST_H */
index 6f10b42..1c237d0 100644 (file)
@@ -48,19 +48,15 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task)
 #endif
 }
 
-static int num_vma_bound(struct drm_i915_gem_object *obj)
+static bool any_vma_pinned(struct drm_i915_gem_object *obj)
 {
        struct i915_vma *vma;
-       int count = 0;
 
-       list_for_each_entry(vma, &obj->vma_list, obj_link) {
-               if (drm_mm_node_allocated(&vma->node))
-                       count++;
-               if (vma->pin_count)
-                       count++;
-       }
+       list_for_each_entry(vma, &obj->vma_list, obj_link)
+               if (i915_vma_is_pinned(vma))
+                       return true;
 
-       return count;
+       return false;
 }
 
 static bool swap_available(void)
@@ -82,7 +78,10 @@ static bool can_release_pages(struct drm_i915_gem_object *obj)
         * to the GPU, simply unbinding from the GPU is not going to succeed
         * in releasing our pin count on the pages themselves.
         */
-       if (obj->pages_pin_count != num_vma_bound(obj))
+       if (obj->pages_pin_count > obj->bind_count)
+               return false;
+
+       if (any_vma_pinned(obj))
                return false;
 
        /* We can only return physical pages to the system if we can either
@@ -163,17 +162,16 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
         */
        for (phase = phases; phase->list; phase++) {
                struct list_head still_in_list;
+               struct drm_i915_gem_object *obj;
 
                if ((flags & phase->bit) == 0)
                        continue;
 
                INIT_LIST_HEAD(&still_in_list);
-               while (count < target && !list_empty(phase->list)) {
-                       struct drm_i915_gem_object *obj;
-                       struct i915_vma *vma, *v;
-
-                       obj = list_first_entry(phase->list,
-                                              typeof(*obj), global_list);
+               while (count < target &&
+                      (obj = list_first_entry_or_null(phase->list,
+                                                      typeof(*obj),
+                                                      global_list))) {
                        list_move_tail(&obj->global_list, &still_in_list);
 
                        if (flags & I915_SHRINK_PURGEABLE &&
@@ -184,24 +182,21 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
                            !is_vmalloc_addr(obj->mapping))
                                continue;
 
-                       if ((flags & I915_SHRINK_ACTIVE) == 0 && obj->active)
+                       if ((flags & I915_SHRINK_ACTIVE) == 0 &&
+                           i915_gem_object_is_active(obj))
                                continue;
 
                        if (!can_release_pages(obj))
                                continue;
 
-                       drm_gem_object_reference(&obj->base);
+                       i915_gem_object_get(obj);
 
                        /* For the unbound phase, this should be a no-op! */
-                       list_for_each_entry_safe(vma, v,
-                                                &obj->vma_list, obj_link)
-                               if (i915_vma_unbind(vma))
-                                       break;
-
+                       i915_gem_object_unbind(obj);
                        if (i915_gem_object_put_pages(obj) == 0)
                                count += obj->base.size >> PAGE_SHIFT;
 
-                       drm_gem_object_unreference(&obj->base);
+                       i915_gem_object_put(obj);
                }
                list_splice(&still_in_list, phase->list);
        }
@@ -210,6 +205,8 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
                intel_runtime_pm_put(dev_priv);
 
        i915_gem_retire_requests(dev_priv);
+       /* expedite the RCU grace period to free some request slabs */
+       synchronize_rcu_expedited();
 
        return count;
 }
@@ -230,10 +227,15 @@ i915_gem_shrink(struct drm_i915_private *dev_priv,
  */
 unsigned long i915_gem_shrink_all(struct drm_i915_private *dev_priv)
 {
-       return i915_gem_shrink(dev_priv, -1UL,
-                              I915_SHRINK_BOUND |
-                              I915_SHRINK_UNBOUND |
-                              I915_SHRINK_ACTIVE);
+       unsigned long freed;
+
+       freed = i915_gem_shrink(dev_priv, -1UL,
+                               I915_SHRINK_BOUND |
+                               I915_SHRINK_UNBOUND |
+                               I915_SHRINK_ACTIVE);
+       rcu_barrier(); /* wait until our RCU delayed slab frees are completed */
+
+       return freed;
 }
 
 static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
@@ -242,9 +244,6 @@ static bool i915_gem_shrinker_lock(struct drm_device *dev, bool *unlock)
                if (!mutex_is_locked_by(&dev->struct_mutex, current))
                        return false;
 
-               if (to_i915(dev)->mm.shrinker_no_lock_stealing)
-                       return false;
-
                *unlock = false;
        } else
                *unlock = true;
@@ -273,7 +272,7 @@ i915_gem_shrinker_count(struct shrinker *shrinker, struct shrink_control *sc)
                        count += obj->base.size >> PAGE_SHIFT;
 
        list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               if (!obj->active && can_release_pages(obj))
+               if (!i915_gem_object_is_active(obj) && can_release_pages(obj))
                        count += obj->base.size >> PAGE_SHIFT;
        }
 
@@ -321,17 +320,22 @@ i915_gem_shrinker_lock_uninterruptible(struct drm_i915_private *dev_priv,
                                       struct shrinker_lock_uninterruptible *slu,
                                       int timeout_ms)
 {
-       unsigned long timeout = msecs_to_jiffies(timeout_ms) + 1;
+       unsigned long timeout = jiffies + msecs_to_jiffies_timeout(timeout_ms);
+
+       do {
+               if (i915_gem_wait_for_idle(dev_priv, 0) == 0 &&
+                   i915_gem_shrinker_lock(&dev_priv->drm, &slu->unlock))
+                       break;
 
-       while (!i915_gem_shrinker_lock(&dev_priv->drm, &slu->unlock)) {
                schedule_timeout_killable(1);
                if (fatal_signal_pending(current))
                        return false;
-               if (--timeout == 0) {
+
+               if (time_after(jiffies, timeout)) {
                        pr_err("Unable to lock GPU to purge memory.\n");
                        return false;
                }
-       }
+       } while (1);
 
        slu->was_interruptible = dev_priv->mm.interruptible;
        dev_priv->mm.interruptible = false;
@@ -410,7 +414,7 @@ i915_gem_shrinker_vmap(struct notifier_block *nb, unsigned long event, void *ptr
                return NOTIFY_DONE;
 
        /* Force everything onto the inactive lists */
-       ret = i915_gem_wait_for_idle(dev_priv);
+       ret = i915_gem_wait_for_idle(dev_priv, I915_WAIT_LOCKED);
        if (ret)
                goto out;
 
index 66be299..59989e8 100644 (file)
@@ -92,6 +92,7 @@ void i915_gem_stolen_remove_node(struct drm_i915_private *dev_priv,
 static unsigned long i915_stolen_to_physical(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
        struct resource *r;
        u32 base;
@@ -111,33 +112,44 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
        if (INTEL_INFO(dev)->gen >= 3) {
                u32 bsm;
 
-               pci_read_config_dword(dev->pdev, INTEL_BSM, &bsm);
+               pci_read_config_dword(pdev, INTEL_BSM, &bsm);
 
                base = bsm & INTEL_BSM_MASK;
        } else if (IS_I865G(dev)) {
+               u32 tseg_size = 0;
                u16 toud = 0;
+               u8 tmp;
 
-               /*
-                * FIXME is the graphics stolen memory region
-                * always at TOUD? Ie. is it always the last
-                * one to be allocated by the BIOS?
-                */
-               pci_bus_read_config_word(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
+                                        I845_ESMRAMC, &tmp);
+
+               if (tmp & TSEG_ENABLE) {
+                       switch (tmp & I845_TSEG_SIZE_MASK) {
+                       case I845_TSEG_SIZE_512K:
+                               tseg_size = KB(512);
+                               break;
+                       case I845_TSEG_SIZE_1M:
+                               tseg_size = MB(1);
+                               break;
+                       }
+               }
+
+               pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0),
                                         I865_TOUD, &toud);
 
-               base = toud << 16;
+               base = (toud << 16) + tseg_size;
        } else if (IS_I85X(dev)) {
                u32 tseg_size = 0;
                u32 tom;
                u8 tmp;
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
                                         I85X_ESMRAMC, &tmp);
 
                if (tmp & TSEG_ENABLE)
                        tseg_size = MB(1);
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 1),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 1),
                                         I85X_DRB3, &tmp);
                tom = tmp * MB(32);
 
@@ -147,7 +159,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
                u32 tom;
                u8 tmp;
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
                                         I845_ESMRAMC, &tmp);
 
                if (tmp & TSEG_ENABLE) {
@@ -161,7 +173,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
                        }
                }
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
                                         I830_DRB3, &tmp);
                tom = tmp * MB(32);
 
@@ -171,7 +183,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
                u32 tom;
                u8 tmp;
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
                                         I830_ESMRAMC, &tmp);
 
                if (tmp & TSEG_ENABLE) {
@@ -181,7 +193,7 @@ static unsigned long i915_stolen_to_physical(struct drm_device *dev)
                                tseg_size = KB(512);
                }
 
-               pci_bus_read_config_byte(dev->pdev->bus, PCI_DEVFN(0, 0),
+               pci_bus_read_config_byte(pdev->bus, PCI_DEVFN(0, 0),
                                         I830_DRB3, &tmp);
                tom = tmp * MB(32);
 
@@ -685,7 +697,7 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
        if (gtt_offset == I915_GTT_OFFSET_NONE)
                return obj;
 
-       vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base);
+       vma = i915_gem_obj_lookup_or_create_vma(obj, &ggtt->base, NULL);
        if (IS_ERR(vma)) {
                ret = PTR_ERR(vma);
                goto err;
@@ -698,24 +710,25 @@ i915_gem_object_create_stolen_for_preallocated(struct drm_device *dev,
         */
        vma->node.start = gtt_offset;
        vma->node.size = size;
-       if (drm_mm_initialized(&ggtt->base.mm)) {
-               ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node);
-               if (ret) {
-                       DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
-                       goto err;
-               }
 
-               vma->bound |= GLOBAL_BIND;
-               __i915_vma_set_map_and_fenceable(vma);
-               list_add_tail(&vma->vm_link, &ggtt->base.inactive_list);
+       ret = drm_mm_reserve_node(&ggtt->base.mm, &vma->node);
+       if (ret) {
+               DRM_DEBUG_KMS("failed to allocate stolen GTT space\n");
+               goto err;
        }
 
+       vma->pages = obj->pages;
+       vma->flags |= I915_VMA_GLOBAL_BIND;
+       __i915_vma_set_map_and_fenceable(vma);
+       list_move_tail(&vma->vm_link, &ggtt->base.inactive_list);
+       obj->bind_count++;
+
        list_add_tail(&obj->global_list, &dev_priv->mm.bound_list);
        i915_gem_object_pin_pages(obj);
 
        return obj;
 
 err:
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        return NULL;
 }
index 8030199..a14b1e3 100644 (file)
@@ -68,6 +68,9 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
        if (tiling_mode == I915_TILING_NONE)
                return true;
 
+       if (tiling_mode > I915_TILING_LAST)
+               return false;
+
        if (IS_GEN2(dev) ||
            (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)))
                tile_width = 128;
@@ -113,36 +116,58 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
        return true;
 }
 
-/* Is the current GTT allocation valid for the change in tiling? */
-static bool
-i915_gem_object_fence_ok(struct drm_i915_gem_object *obj, int tiling_mode)
+static bool i915_vma_fence_prepare(struct i915_vma *vma, int tiling_mode)
 {
+       struct drm_i915_private *dev_priv = to_i915(vma->vm->dev);
        u32 size;
 
-       if (tiling_mode == I915_TILING_NONE)
-               return true;
-
-       if (INTEL_INFO(obj->base.dev)->gen >= 4)
+       if (!i915_vma_is_map_and_fenceable(vma))
                return true;
 
-       if (IS_GEN3(obj->base.dev)) {
-               if (i915_gem_obj_ggtt_offset(obj) & ~I915_FENCE_START_MASK)
+       if (INTEL_GEN(dev_priv) == 3) {
+               if (vma->node.start & ~I915_FENCE_START_MASK)
                        return false;
        } else {
-               if (i915_gem_obj_ggtt_offset(obj) & ~I830_FENCE_START_MASK)
+               if (vma->node.start & ~I830_FENCE_START_MASK)
                        return false;
        }
 
-       size = i915_gem_get_gtt_size(obj->base.dev, obj->base.size, tiling_mode);
-       if (i915_gem_obj_ggtt_size(obj) != size)
+       size = i915_gem_get_ggtt_size(dev_priv, vma->size, tiling_mode);
+       if (vma->node.size < size)
                return false;
 
-       if (i915_gem_obj_ggtt_offset(obj) & (size - 1))
+       if (vma->node.start & (size - 1))
                return false;
 
        return true;
 }
 
+/* Make the current GTT allocation valid for the change in tiling. */
+static int
+i915_gem_object_fence_prepare(struct drm_i915_gem_object *obj, int tiling_mode)
+{
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
+       struct i915_vma *vma;
+       int ret;
+
+       if (tiling_mode == I915_TILING_NONE)
+               return 0;
+
+       if (INTEL_GEN(dev_priv) >= 4)
+               return 0;
+
+       list_for_each_entry(vma, &obj->vma_list, obj_link) {
+               if (i915_vma_fence_prepare(vma, tiling_mode))
+                       continue;
+
+               ret = i915_vma_unbind(vma);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 /**
  * i915_gem_set_tiling - IOCTL handler to set tiling mode
  * @dev: DRM device
@@ -164,15 +189,18 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
        struct drm_i915_gem_set_tiling *args = data;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_i915_gem_object *obj;
-       int ret = 0;
+       int err = 0;
+
+       /* Make sure we don't cross-contaminate obj->tiling_and_stride */
+       BUILD_BUG_ON(I915_TILING_LAST & STRIDE_MASK);
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL)
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
                return -ENOENT;
 
        if (!i915_tiling_ok(dev,
                            args->stride, obj->base.size, args->tiling_mode)) {
-               drm_gem_object_unreference_unlocked(&obj->base);
+               i915_gem_object_put_unlocked(obj);
                return -EINVAL;
        }
 
@@ -180,7 +208,7 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
 
        mutex_lock(&dev->struct_mutex);
        if (obj->pin_display || obj->framebuffer_references) {
-               ret = -EBUSY;
+               err = -EBUSY;
                goto err;
        }
 
@@ -213,8 +241,8 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                }
        }
 
-       if (args->tiling_mode != obj->tiling_mode ||
-           args->stride != obj->stride) {
+       if (args->tiling_mode != i915_gem_object_get_tiling(obj) ||
+           args->stride != i915_gem_object_get_stride(obj)) {
                /* We need to rebind the object if its current allocation
                 * no longer meets the alignment restrictions for its new
                 * tiling mode. Otherwise we can just leave it alone, but
@@ -227,34 +255,36 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
                 * has to also include the unfenced register the GPU uses
                 * whilst executing a fenced command for an untiled object.
                 */
-               if (obj->map_and_fenceable &&
-                   !i915_gem_object_fence_ok(obj, args->tiling_mode))
-                       ret = i915_vma_unbind(i915_gem_obj_to_ggtt(obj));
 
-               if (ret == 0) {
+               err = i915_gem_object_fence_prepare(obj, args->tiling_mode);
+               if (!err) {
+                       struct i915_vma *vma;
+
                        if (obj->pages &&
                            obj->madv == I915_MADV_WILLNEED &&
                            dev_priv->quirks & QUIRK_PIN_SWIZZLED_PAGES) {
                                if (args->tiling_mode == I915_TILING_NONE)
                                        i915_gem_object_unpin_pages(obj);
-                               if (obj->tiling_mode == I915_TILING_NONE)
+                               if (!i915_gem_object_is_tiled(obj))
                                        i915_gem_object_pin_pages(obj);
                        }
 
-                       obj->fence_dirty =
-                               obj->last_fenced_req ||
-                               obj->fence_reg != I915_FENCE_REG_NONE;
+                       list_for_each_entry(vma, &obj->vma_list, obj_link) {
+                               if (!vma->fence)
+                                       continue;
 
-                       obj->tiling_mode = args->tiling_mode;
-                       obj->stride = args->stride;
+                               vma->fence->dirty = true;
+                       }
+                       obj->tiling_and_stride =
+                               args->stride | args->tiling_mode;
 
                        /* Force the fence to be reacquired for GTT access */
                        i915_gem_release_mmap(obj);
                }
        }
        /* we have to maintain this existing ABI... */
-       args->stride = obj->stride;
-       args->tiling_mode = obj->tiling_mode;
+       args->stride = i915_gem_object_get_stride(obj);
+       args->tiling_mode = i915_gem_object_get_tiling(obj);
 
        /* Try to preallocate memory required to save swizzling on put-pages */
        if (i915_gem_object_needs_bit17_swizzle(obj)) {
@@ -268,12 +298,12 @@ i915_gem_set_tiling(struct drm_device *dev, void *data,
        }
 
 err:
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
 
        intel_runtime_pm_put(dev_priv);
 
-       return ret;
+       return err;
 }
 
 /**
@@ -297,14 +327,12 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_i915_gem_object *obj;
 
-       obj = to_intel_bo(drm_gem_object_lookup(file, args->handle));
-       if (&obj->base == NULL)
+       obj = i915_gem_object_lookup(file, args->handle);
+       if (!obj)
                return -ENOENT;
 
-       mutex_lock(&dev->struct_mutex);
-
-       args->tiling_mode = obj->tiling_mode;
-       switch (obj->tiling_mode) {
+       args->tiling_mode = READ_ONCE(obj->tiling_and_stride) & TILING_MASK;
+       switch (args->tiling_mode) {
        case I915_TILING_X:
                args->swizzle_mode = dev_priv->mm.bit_6_swizzle_x;
                break;
@@ -328,8 +356,6 @@ i915_gem_get_tiling(struct drm_device *dev, void *data,
        if (args->swizzle_mode == I915_BIT_6_SWIZZLE_9_10_17)
                args->swizzle_mode = I915_BIT_6_SWIZZLE_9_10;
 
-       drm_gem_object_unreference(&obj->base);
-       mutex_unlock(&dev->struct_mutex);
-
+       i915_gem_object_put_unlocked(obj);
        return 0;
 }
index 2314c88..e537930 100644 (file)
@@ -63,33 +63,12 @@ struct i915_mmu_object {
 
 static void wait_rendering(struct drm_i915_gem_object *obj)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_gem_request *requests[I915_NUM_ENGINES];
-       int i, n;
-
-       if (!obj->active)
-               return;
-
-       n = 0;
-       for (i = 0; i < I915_NUM_ENGINES; i++) {
-               struct drm_i915_gem_request *req;
-
-               req = obj->last_read_req[i];
-               if (req == NULL)
-                       continue;
-
-               requests[n++] = i915_gem_request_reference(req);
-       }
-
-       mutex_unlock(&dev->struct_mutex);
-
-       for (i = 0; i < n; i++)
-               __i915_wait_request(requests[i], false, NULL, NULL);
-
-       mutex_lock(&dev->struct_mutex);
+       unsigned long active = __I915_BO_ACTIVE(obj);
+       int idx;
 
-       for (i = 0; i < n; i++)
-               i915_gem_request_unreference(requests[i]);
+       for_each_active(active, idx)
+               i915_gem_active_wait_unlocked(&obj->last_read[idx],
+                                             0, NULL, NULL);
 }
 
 static void cancel_userptr(struct work_struct *work)
@@ -98,28 +77,19 @@ static void cancel_userptr(struct work_struct *work)
        struct drm_i915_gem_object *obj = mo->obj;
        struct drm_device *dev = obj->base.dev;
 
+       wait_rendering(obj);
+
        mutex_lock(&dev->struct_mutex);
        /* Cancel any active worker and force us to re-evaluate gup */
        obj->userptr.work = NULL;
 
        if (obj->pages != NULL) {
-               struct drm_i915_private *dev_priv = to_i915(dev);
-               struct i915_vma *vma, *tmp;
-               bool was_interruptible;
-
-               wait_rendering(obj);
-
-               was_interruptible = dev_priv->mm.interruptible;
-               dev_priv->mm.interruptible = false;
-
-               list_for_each_entry_safe(vma, tmp, &obj->vma_list, obj_link)
-                       WARN_ON(i915_vma_unbind(vma));
+               /* We are inside a kthread context and can't be interrupted */
+               WARN_ON(i915_gem_object_unbind(obj));
                WARN_ON(i915_gem_object_put_pages(obj));
-
-               dev_priv->mm.interruptible = was_interruptible;
        }
 
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
 }
 
@@ -572,12 +542,10 @@ __i915_gem_userptr_get_pages_worker(struct work_struct *_work)
                        }
                }
                obj->userptr.work = ERR_PTR(ret);
-               if (ret)
-                       __i915_gem_userptr_set_active(obj, false);
        }
 
        obj->userptr.workers--;
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
 
        release_pages(pvec, pinned, 0);
@@ -622,8 +590,7 @@ __i915_gem_userptr_get_pages_schedule(struct drm_i915_gem_object *obj,
        obj->userptr.work = &work->work;
        obj->userptr.workers++;
 
-       work->obj = obj;
-       drm_gem_object_reference(&obj->base);
+       work->obj = i915_gem_object_get(obj);
 
        work->task = current;
        get_task_struct(work->task);
@@ -659,15 +626,14 @@ i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
         * to the vma (discard or cloning) which should prevent the more
         * egregious cases from causing harm.
         */
-       if (IS_ERR(obj->userptr.work)) {
-               /* active flag will have been dropped already by the worker */
-               ret = PTR_ERR(obj->userptr.work);
-               obj->userptr.work = NULL;
-               return ret;
-       }
-       if (obj->userptr.work)
+
+       if (obj->userptr.work) {
                /* active flag should still be held for the pending work */
-               return -EAGAIN;
+               if (IS_ERR(obj->userptr.work))
+                       return PTR_ERR(obj->userptr.work);
+               else
+                       return -EAGAIN;
+       }
 
        /* Let the mmu-notifier know that we have begun and need cancellation */
        ret = __i915_gem_userptr_set_active(obj, true);
@@ -846,7 +812,7 @@ i915_gem_userptr_ioctl(struct drm_device *dev, void *data, struct drm_file *file
                ret = drm_gem_handle_create(file, &obj->base, &handle);
 
        /* drop reference from allocate - handle holds it now */
-       drm_gem_object_unreference_unlocked(&obj->base);
+       i915_gem_object_put_unlocked(obj);
        if (ret)
                return ret;
 
index 9d73d22..334f15d 100644 (file)
@@ -30,9 +30,9 @@
 #include <generated/utsrelease.h>
 #include "i915_drv.h"
 
-static const char *ring_str(int ring)
+static const char *engine_str(int engine)
 {
-       switch (ring) {
+       switch (engine) {
        case RCS: return "render";
        case VCS: return "bsd";
        case BCS: return "blt";
@@ -42,16 +42,6 @@ static const char *ring_str(int ring)
        }
 }
 
-static const char *pin_flag(int pinned)
-{
-       if (pinned > 0)
-               return " P";
-       else if (pinned < 0)
-               return " p";
-       else
-               return "";
-}
-
 static const char *tiling_flag(int tiling)
 {
        switch (tiling) {
@@ -189,7 +179,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
 {
        int i;
 
-       err_printf(m, "  %s [%d]:\n", name, count);
+       err_printf(m, "%s [%d]:\n", name, count);
 
        while (count--) {
                err_printf(m, "    %08x_%08x %8u %02x %02x [ ",
@@ -202,13 +192,12 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
                        err_printf(m, "%02x ", err->rseqno[i]);
 
                err_printf(m, "] %02x", err->wseqno);
-               err_puts(m, pin_flag(err->pinned));
                err_puts(m, tiling_flag(err->tiling));
                err_puts(m, dirty_flag(err->dirty));
                err_puts(m, purgeable_flag(err->purgeable));
                err_puts(m, err->userptr ? " userptr" : "");
-               err_puts(m, err->ring != -1 ? " " : "");
-               err_puts(m, ring_str(err->ring));
+               err_puts(m, err->engine != -1 ? " " : "");
+               err_puts(m, engine_str(err->engine));
                err_puts(m, i915_cache_level_str(m->i915, err->cache_level));
 
                if (err->name)
@@ -221,7 +210,7 @@ static void print_error_buffers(struct drm_i915_error_state_buf *m,
        }
 }
 
-static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a)
+static const char *hangcheck_action_to_str(enum intel_engine_hangcheck_action a)
 {
        switch (a) {
        case HANGCHECK_IDLE:
@@ -239,70 +228,74 @@ static const char *hangcheck_action_to_str(enum intel_ring_hangcheck_action a)
        return "unknown";
 }
 
-static void i915_ring_error_state(struct drm_i915_error_state_buf *m,
-                                 struct drm_device *dev,
-                                 struct drm_i915_error_state *error,
-                                 int ring_idx)
+static void error_print_engine(struct drm_i915_error_state_buf *m,
+                              struct drm_i915_error_engine *ee)
 {
-       struct drm_i915_error_ring *ring = &error->ring[ring_idx];
-
-       if (!ring->valid)
-               return;
-
-       err_printf(m, "%s command stream:\n", ring_str(ring_idx));
-       err_printf(m, "  START: 0x%08x\n", ring->start);
-       err_printf(m, "  HEAD:  0x%08x\n", ring->head);
-       err_printf(m, "  TAIL:  0x%08x\n", ring->tail);
-       err_printf(m, "  CTL:   0x%08x\n", ring->ctl);
-       err_printf(m, "  HWS:   0x%08x\n", ring->hws);
-       err_printf(m, "  ACTHD: 0x%08x %08x\n", (u32)(ring->acthd>>32), (u32)ring->acthd);
-       err_printf(m, "  IPEIR: 0x%08x\n", ring->ipeir);
-       err_printf(m, "  IPEHR: 0x%08x\n", ring->ipehr);
-       err_printf(m, "  INSTDONE: 0x%08x\n", ring->instdone);
-       if (INTEL_INFO(dev)->gen >= 4) {
-               err_printf(m, "  BBADDR: 0x%08x %08x\n", (u32)(ring->bbaddr>>32), (u32)ring->bbaddr);
-               err_printf(m, "  BB_STATE: 0x%08x\n", ring->bbstate);
-               err_printf(m, "  INSTPS: 0x%08x\n", ring->instps);
+       err_printf(m, "%s command stream:\n", engine_str(ee->engine_id));
+       err_printf(m, "  START: 0x%08x\n", ee->start);
+       err_printf(m, "  HEAD:  0x%08x\n", ee->head);
+       err_printf(m, "  TAIL:  0x%08x\n", ee->tail);
+       err_printf(m, "  CTL:   0x%08x\n", ee->ctl);
+       err_printf(m, "  MODE:  0x%08x\n", ee->mode);
+       err_printf(m, "  HWS:   0x%08x\n", ee->hws);
+       err_printf(m, "  ACTHD: 0x%08x %08x\n",
+                  (u32)(ee->acthd>>32), (u32)ee->acthd);
+       err_printf(m, "  IPEIR: 0x%08x\n", ee->ipeir);
+       err_printf(m, "  IPEHR: 0x%08x\n", ee->ipehr);
+       err_printf(m, "  INSTDONE: 0x%08x\n", ee->instdone);
+       if (ee->batchbuffer) {
+               u64 start = ee->batchbuffer->gtt_offset;
+               u64 end = start + ee->batchbuffer->gtt_size;
+
+               err_printf(m, "  batch: [0x%08x_%08x, 0x%08x_%08x]\n",
+                          upper_32_bits(start), lower_32_bits(start),
+                          upper_32_bits(end), lower_32_bits(end));
        }
-       err_printf(m, "  INSTPM: 0x%08x\n", ring->instpm);
-       err_printf(m, "  FADDR: 0x%08x %08x\n", upper_32_bits(ring->faddr),
-                  lower_32_bits(ring->faddr));
-       if (INTEL_INFO(dev)->gen >= 6) {
-               err_printf(m, "  RC PSMI: 0x%08x\n", ring->rc_psmi);
-               err_printf(m, "  FAULT_REG: 0x%08x\n", ring->fault_reg);
+       if (INTEL_GEN(m->i915) >= 4) {
+               err_printf(m, "  BBADDR: 0x%08x_%08x\n",
+                          (u32)(ee->bbaddr>>32), (u32)ee->bbaddr);
+               err_printf(m, "  BB_STATE: 0x%08x\n", ee->bbstate);
+               err_printf(m, "  INSTPS: 0x%08x\n", ee->instps);
+       }
+       err_printf(m, "  INSTPM: 0x%08x\n", ee->instpm);
+       err_printf(m, "  FADDR: 0x%08x %08x\n", upper_32_bits(ee->faddr),
+                  lower_32_bits(ee->faddr));
+       if (INTEL_GEN(m->i915) >= 6) {
+               err_printf(m, "  RC PSMI: 0x%08x\n", ee->rc_psmi);
+               err_printf(m, "  FAULT_REG: 0x%08x\n", ee->fault_reg);
                err_printf(m, "  SYNC_0: 0x%08x [last synced 0x%08x]\n",
-                          ring->semaphore_mboxes[0],
-                          ring->semaphore_seqno[0]);
+                          ee->semaphore_mboxes[0],
+                          ee->semaphore_seqno[0]);
                err_printf(m, "  SYNC_1: 0x%08x [last synced 0x%08x]\n",
-                          ring->semaphore_mboxes[1],
-                          ring->semaphore_seqno[1]);
-               if (HAS_VEBOX(dev)) {
+                          ee->semaphore_mboxes[1],
+                          ee->semaphore_seqno[1]);
+               if (HAS_VEBOX(m->i915)) {
                        err_printf(m, "  SYNC_2: 0x%08x [last synced 0x%08x]\n",
-                                  ring->semaphore_mboxes[2],
-                                  ring->semaphore_seqno[2]);
+                                  ee->semaphore_mboxes[2],
+                                  ee->semaphore_seqno[2]);
                }
        }
-       if (USES_PPGTT(dev)) {
-               err_printf(m, "  GFX_MODE: 0x%08x\n", ring->vm_info.gfx_mode);
+       if (USES_PPGTT(m->i915)) {
+               err_printf(m, "  GFX_MODE: 0x%08x\n", ee->vm_info.gfx_mode);
 
-               if (INTEL_INFO(dev)->gen >= 8) {
+               if (INTEL_GEN(m->i915) >= 8) {
                        int i;
                        for (i = 0; i < 4; i++)
                                err_printf(m, "  PDP%d: 0x%016llx\n",
-                                          i, ring->vm_info.pdp[i]);
+                                          i, ee->vm_info.pdp[i]);
                } else {
                        err_printf(m, "  PP_DIR_BASE: 0x%08x\n",
-                                  ring->vm_info.pp_dir_base);
+                                  ee->vm_info.pp_dir_base);
                }
        }
-       err_printf(m, "  seqno: 0x%08x\n", ring->seqno);
-       err_printf(m, "  last_seqno: 0x%08x\n", ring->last_seqno);
-       err_printf(m, "  waiting: %s\n", yesno(ring->waiting));
-       err_printf(m, "  ring->head: 0x%08x\n", ring->cpu_ring_head);
-       err_printf(m, "  ring->tail: 0x%08x\n", ring->cpu_ring_tail);
+       err_printf(m, "  seqno: 0x%08x\n", ee->seqno);
+       err_printf(m, "  last_seqno: 0x%08x\n", ee->last_seqno);
+       err_printf(m, "  waiting: %s\n", yesno(ee->waiting));
+       err_printf(m, "  ring->head: 0x%08x\n", ee->cpu_ring_head);
+       err_printf(m, "  ring->tail: 0x%08x\n", ee->cpu_ring_tail);
        err_printf(m, "  hangcheck: %s [%d]\n",
-                  hangcheck_action_to_str(ring->hangcheck_action),
-                  ring->hangcheck_score);
+                  hangcheck_action_to_str(ee->hangcheck_action),
+                  ee->hangcheck_score);
 }
 
 void i915_error_printf(struct drm_i915_error_state_buf *e, const char *f, ...)
@@ -328,11 +321,22 @@ static void print_error_obj(struct drm_i915_error_state_buf *m,
        }
 }
 
+static void err_print_capabilities(struct drm_i915_error_state_buf *m,
+                                  const struct intel_device_info *info)
+{
+#define PRINT_FLAG(x)  err_printf(m, #x ": %s\n", yesno(info->x))
+#define SEP_SEMICOLON ;
+       DEV_INFO_FOR_EACH_FLAG(PRINT_FLAG, SEP_SEMICOLON);
+#undef PRINT_FLAG
+#undef SEP_SEMICOLON
+}
+
 int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                            const struct i915_error_state_file_priv *error_priv)
 {
        struct drm_device *dev = error_priv->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct drm_i915_error_state *error = error_priv->error;
        struct drm_i915_error_object *obj;
        int i, j, offset, elt;
@@ -347,27 +351,28 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
        err_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec,
                   error->time.tv_usec);
        err_printf(m, "Kernel: " UTS_RELEASE "\n");
+       err_print_capabilities(m, &error->device_info);
        max_hangcheck_score = 0;
-       for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
-               if (error->ring[i].hangcheck_score > max_hangcheck_score)
-                       max_hangcheck_score = error->ring[i].hangcheck_score;
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               if (error->engine[i].hangcheck_score > max_hangcheck_score)
+                       max_hangcheck_score = error->engine[i].hangcheck_score;
        }
-       for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
-               if (error->ring[i].hangcheck_score == max_hangcheck_score &&
-                   error->ring[i].pid != -1) {
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               if (error->engine[i].hangcheck_score == max_hangcheck_score &&
+                   error->engine[i].pid != -1) {
                        err_printf(m, "Active process (on ring %s): %s [%d]\n",
-                                  ring_str(i),
-                                  error->ring[i].comm,
-                                  error->ring[i].pid);
+                                  engine_str(i),
+                                  error->engine[i].comm,
+                                  error->engine[i].pid);
                }
        }
        err_printf(m, "Reset count: %u\n", error->reset_count);
        err_printf(m, "Suspend count: %u\n", error->suspend_count);
-       err_printf(m, "PCI ID: 0x%04x\n", dev->pdev->device);
-       err_printf(m, "PCI Revision: 0x%02x\n", dev->pdev->revision);
+       err_printf(m, "PCI ID: 0x%04x\n", pdev->device);
+       err_printf(m, "PCI Revision: 0x%02x\n", pdev->revision);
        err_printf(m, "PCI Subsystem: %04x:%04x\n",
-                  dev->pdev->subsystem_vendor,
-                  dev->pdev->subsystem_device);
+                  pdev->subsystem_vendor,
+                  pdev->subsystem_device);
        err_printf(m, "IOMMU enabled?: %d\n", error->iommu);
 
        if (HAS_CSR(dev)) {
@@ -414,36 +419,55 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
        if (IS_GEN7(dev))
                err_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
 
-       for (i = 0; i < ARRAY_SIZE(error->ring); i++)
-               i915_ring_error_state(m, dev, error, i);
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               if (error->engine[i].engine_id != -1)
+                       error_print_engine(m, &error->engine[i]);
+       }
+
+       for (i = 0; i < ARRAY_SIZE(error->active_vm); i++) {
+               char buf[128];
+               int len, first = 1;
+
+               if (!error->active_vm[i])
+                       break;
 
-       for (i = 0; i < error->vm_count; i++) {
-               err_printf(m, "vm[%d]\n", i);
+               len = scnprintf(buf, sizeof(buf), "Active (");
+               for (j = 0; j < ARRAY_SIZE(error->engine); j++) {
+                       if (error->engine[j].vm != error->active_vm[i])
+                               continue;
 
-               print_error_buffers(m, "Active",
+                       len += scnprintf(buf + len, sizeof(buf), "%s%s",
+                                        first ? "" : ", ",
+                                        dev_priv->engine[j].name);
+                       first = 0;
+               }
+               scnprintf(buf + len, sizeof(buf), ")");
+               print_error_buffers(m, buf,
                                    error->active_bo[i],
                                    error->active_bo_count[i]);
-
-               print_error_buffers(m, "Pinned",
-                                   error->pinned_bo[i],
-                                   error->pinned_bo_count[i]);
        }
 
-       for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
-               obj = error->ring[i].batchbuffer;
+       print_error_buffers(m, "Pinned (global)",
+                           error->pinned_bo,
+                           error->pinned_bo_count);
+
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               struct drm_i915_error_engine *ee = &error->engine[i];
+
+               obj = ee->batchbuffer;
                if (obj) {
                        err_puts(m, dev_priv->engine[i].name);
-                       if (error->ring[i].pid != -1)
+                       if (ee->pid != -1)
                                err_printf(m, " (submitted by %s [%d])",
-                                          error->ring[i].comm,
-                                          error->ring[i].pid);
+                                          ee->comm,
+                                          ee->pid);
                        err_printf(m, " --- gtt_offset = 0x%08x %08x\n",
                                   upper_32_bits(obj->gtt_offset),
                                   lower_32_bits(obj->gtt_offset));
                        print_error_obj(m, obj);
                }
 
-               obj = error->ring[i].wa_batchbuffer;
+               obj = ee->wa_batchbuffer;
                if (obj) {
                        err_printf(m, "%s (w/a) --- gtt_offset = 0x%08x\n",
                                   dev_priv->engine[i].name,
@@ -451,38 +475,43 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                        print_error_obj(m, obj);
                }
 
-               if (error->ring[i].num_requests) {
+               if (ee->num_requests) {
                        err_printf(m, "%s --- %d requests\n",
                                   dev_priv->engine[i].name,
-                                  error->ring[i].num_requests);
-                       for (j = 0; j < error->ring[i].num_requests; j++) {
-                               err_printf(m, "  seqno 0x%08x, emitted %ld, tail 0x%08x\n",
-                                          error->ring[i].requests[j].seqno,
-                                          error->ring[i].requests[j].jiffies,
-                                          error->ring[i].requests[j].tail);
+                                  ee->num_requests);
+                       for (j = 0; j < ee->num_requests; j++) {
+                               err_printf(m, "  pid %d, seqno 0x%08x, emitted %ld, head 0x%08x, tail 0x%08x\n",
+                                          ee->requests[j].pid,
+                                          ee->requests[j].seqno,
+                                          ee->requests[j].jiffies,
+                                          ee->requests[j].head,
+                                          ee->requests[j].tail);
                        }
                }
 
-               if (error->ring[i].num_waiters) {
+               if (IS_ERR(ee->waiters)) {
+                       err_printf(m, "%s --- ? waiters [unable to acquire spinlock]\n",
+                                  dev_priv->engine[i].name);
+               } else if (ee->num_waiters) {
                        err_printf(m, "%s --- %d waiters\n",
                                   dev_priv->engine[i].name,
-                                  error->ring[i].num_waiters);
-                       for (j = 0; j < error->ring[i].num_waiters; j++) {
+                                  ee->num_waiters);
+                       for (j = 0; j < ee->num_waiters; j++) {
                                err_printf(m, " seqno 0x%08x for %s [%d]\n",
-                                          error->ring[i].waiters[j].seqno,
-                                          error->ring[i].waiters[j].comm,
-                                          error->ring[i].waiters[j].pid);
+                                          ee->waiters[j].seqno,
+                                          ee->waiters[j].comm,
+                                          ee->waiters[j].pid);
                        }
                }
 
-               if ((obj = error->ring[i].ringbuffer)) {
+               if ((obj = ee->ringbuffer)) {
                        err_printf(m, "%s --- ringbuffer = 0x%08x\n",
                                   dev_priv->engine[i].name,
                                   lower_32_bits(obj->gtt_offset));
                        print_error_obj(m, obj);
                }
 
-               if ((obj = error->ring[i].hws_page)) {
+               if ((obj = ee->hws_page)) {
                        u64 hws_offset = obj->gtt_offset;
                        u32 *hws_page = &obj->pages[0][0];
 
@@ -504,7 +533,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                        }
                }
 
-               obj = error->ring[i].wa_ctx;
+               obj = ee->wa_ctx;
                if (obj) {
                        u64 wa_ctx_offset = obj->gtt_offset;
                        u32 *wa_ctx_page = &obj->pages[0][0];
@@ -526,7 +555,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                        }
                }
 
-               if ((obj = error->ring[i].ctx)) {
+               if ((obj = ee->ctx)) {
                        err_printf(m, "%s --- HW Context = 0x%08x\n",
                                   dev_priv->engine[i].name,
                                   lower_32_bits(obj->gtt_offset));
@@ -534,7 +563,7 @@ int i915_error_state_to_str(struct drm_i915_error_state_buf *m,
                }
        }
 
-       if ((obj = error->semaphore_obj)) {
+       if ((obj = error->semaphore)) {
                err_printf(m, "Semaphore page = 0x%08x\n",
                           lower_32_bits(obj->gtt_offset));
                for (elt = 0; elt < PAGE_SIZE/16; elt += 4) {
@@ -611,26 +640,27 @@ static void i915_error_state_free(struct kref *error_ref)
                                                          typeof(*error), ref);
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
-               i915_error_object_free(error->ring[i].batchbuffer);
-               i915_error_object_free(error->ring[i].wa_batchbuffer);
-               i915_error_object_free(error->ring[i].ringbuffer);
-               i915_error_object_free(error->ring[i].hws_page);
-               i915_error_object_free(error->ring[i].ctx);
-               i915_error_object_free(error->ring[i].wa_ctx);
-               kfree(error->ring[i].requests);
-               kfree(error->ring[i].waiters);
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               struct drm_i915_error_engine *ee = &error->engine[i];
+
+               i915_error_object_free(ee->batchbuffer);
+               i915_error_object_free(ee->wa_batchbuffer);
+               i915_error_object_free(ee->ringbuffer);
+               i915_error_object_free(ee->hws_page);
+               i915_error_object_free(ee->ctx);
+               i915_error_object_free(ee->wa_ctx);
+
+               kfree(ee->requests);
+               if (!IS_ERR_OR_NULL(ee->waiters))
+                       kfree(ee->waiters);
        }
 
-       i915_error_object_free(error->semaphore_obj);
+       i915_error_object_free(error->semaphore);
 
-       for (i = 0; i < error->vm_count; i++)
+       for (i = 0; i < ARRAY_SIZE(error->active_bo); i++)
                kfree(error->active_bo[i]);
-
-       kfree(error->active_bo);
-       kfree(error->active_bo_count);
        kfree(error->pinned_bo);
-       kfree(error->pinned_bo_count);
+
        kfree(error->overlay);
        kfree(error->display);
        kfree(error);
@@ -638,46 +668,45 @@ static void i915_error_state_free(struct kref *error_ref)
 
 static struct drm_i915_error_object *
 i915_error_object_create(struct drm_i915_private *dev_priv,
-                        struct drm_i915_gem_object *src,
-                        struct i915_address_space *vm)
+                        struct i915_vma *vma)
 {
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
+       struct drm_i915_gem_object *src;
        struct drm_i915_error_object *dst;
-       struct i915_vma *vma = NULL;
        int num_pages;
        bool use_ggtt;
        int i = 0;
        u64 reloc_offset;
 
-       if (src == NULL || src->pages == NULL)
+       if (!vma)
+               return NULL;
+
+       src = vma->obj;
+       if (!src->pages)
                return NULL;
 
        num_pages = src->base.size >> PAGE_SHIFT;
 
        dst = kmalloc(sizeof(*dst) + num_pages * sizeof(u32 *), GFP_ATOMIC);
-       if (dst == NULL)
+       if (!dst)
                return NULL;
 
-       if (i915_gem_obj_bound(src, vm))
-               dst->gtt_offset = i915_gem_obj_offset(src, vm);
-       else
-               dst->gtt_offset = -1;
+       dst->gtt_offset = vma->node.start;
+       dst->gtt_size = vma->node.size;
 
        reloc_offset = dst->gtt_offset;
-       if (i915_is_ggtt(vm))
-               vma = i915_gem_obj_to_ggtt(src);
        use_ggtt = (src->cache_level == I915_CACHE_NONE &&
-                  vma && (vma->bound & GLOBAL_BIND) &&
+                  (vma->flags & I915_VMA_GLOBAL_BIND) &&
                   reloc_offset + num_pages * PAGE_SIZE <= ggtt->mappable_end);
 
        /* Cannot access stolen address directly, try to use the aperture */
        if (src->stolen) {
                use_ggtt = true;
 
-               if (!(vma && vma->bound & GLOBAL_BIND))
+               if (!(vma->flags & I915_VMA_GLOBAL_BIND))
                        goto unwind;
 
-               reloc_offset = i915_gem_obj_ggtt_offset(src);
+               reloc_offset = vma->node.start;
                if (reloc_offset + num_pages * PAGE_SIZE > ggtt->mappable_end)
                        goto unwind;
        }
@@ -705,7 +734,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
                         * captures what the GPU read.
                         */
 
-                       s = io_mapping_map_atomic_wc(ggtt->mappable,
+                       s = io_mapping_map_atomic_wc(&ggtt->mappable,
                                                     reloc_offset);
                        memcpy_fromio(d, s, PAGE_SIZE);
                        io_mapping_unmap_atomic(s);
@@ -737,8 +766,24 @@ unwind:
        kfree(dst);
        return NULL;
 }
-#define i915_error_ggtt_object_create(dev_priv, src) \
-       i915_error_object_create((dev_priv), (src), &(dev_priv)->ggtt.base)
+
+/* The error capture is special as tries to run underneath the normal
+ * locking rules - so we use the raw version of the i915_gem_active lookup.
+ */
+static inline uint32_t
+__active_get_seqno(struct i915_gem_active *active)
+{
+       return i915_gem_request_get_seqno(__i915_gem_active_peek(active));
+}
+
+static inline int
+__active_get_engine_id(struct i915_gem_active *active)
+{
+       struct intel_engine_cs *engine;
+
+       engine = i915_gem_request_get_engine(__i915_gem_active_peek(active));
+       return engine ? engine->id : -1;
+}
 
 static void capture_bo(struct drm_i915_error_buffer *err,
                       struct i915_vma *vma)
@@ -748,32 +793,34 @@ static void capture_bo(struct drm_i915_error_buffer *err,
 
        err->size = obj->base.size;
        err->name = obj->base.name;
+
        for (i = 0; i < I915_NUM_ENGINES; i++)
-               err->rseqno[i] = i915_gem_request_get_seqno(obj->last_read_req[i]);
-       err->wseqno = i915_gem_request_get_seqno(obj->last_write_req);
+               err->rseqno[i] = __active_get_seqno(&obj->last_read[i]);
+       err->wseqno = __active_get_seqno(&obj->last_write);
+       err->engine = __active_get_engine_id(&obj->last_write);
+
        err->gtt_offset = vma->node.start;
        err->read_domains = obj->base.read_domains;
        err->write_domain = obj->base.write_domain;
-       err->fence_reg = obj->fence_reg;
-       err->pinned = 0;
-       if (i915_gem_obj_is_pinned(obj))
-               err->pinned = 1;
-       err->tiling = obj->tiling_mode;
+       err->fence_reg = vma->fence ? vma->fence->id : -1;
+       err->tiling = i915_gem_object_get_tiling(obj);
        err->dirty = obj->dirty;
        err->purgeable = obj->madv != I915_MADV_WILLNEED;
        err->userptr = obj->userptr.mm != NULL;
-       err->ring = obj->last_write_req ?
-                       i915_gem_request_get_engine(obj->last_write_req)->id : -1;
        err->cache_level = obj->cache_level;
 }
 
-static u32 capture_active_bo(struct drm_i915_error_buffer *err,
-                            int count, struct list_head *head)
+static u32 capture_error_bo(struct drm_i915_error_buffer *err,
+                           int count, struct list_head *head,
+                           bool pinned_only)
 {
        struct i915_vma *vma;
        int i = 0;
 
        list_for_each_entry(vma, head, vm_link) {
+               if (pinned_only && !i915_vma_is_pinned(vma))
+                       continue;
+
                capture_bo(err++, vma);
                if (++i == count)
                        break;
@@ -782,28 +829,6 @@ static u32 capture_active_bo(struct drm_i915_error_buffer *err,
        return i;
 }
 
-static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
-                            int count, struct list_head *head,
-                            struct i915_address_space *vm)
-{
-       struct drm_i915_gem_object *obj;
-       struct drm_i915_error_buffer * const first = err;
-       struct drm_i915_error_buffer * const last = err + count;
-
-       list_for_each_entry(obj, head, global_list) {
-               struct i915_vma *vma;
-
-               if (err == last)
-                       break;
-
-               list_for_each_entry(vma, &obj->vma_list, obj_link)
-                       if (vma->vm == vm && vma->pin_count > 0)
-                               capture_bo(err++, vma);
-       }
-
-       return err - first;
-}
-
 /* Generate a semi-unique error code. The code is not meant to have meaning, The
  * code's only purpose is to try to prevent false duplicated bug reports by
  * grossly estimating a GPU error state.
@@ -815,7 +840,7 @@ static u32 capture_pinned_bo(struct drm_i915_error_buffer *err,
  */
 static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv,
                                         struct drm_i915_error_state *error,
-                                        int *ring_id)
+                                        int *engine_id)
 {
        uint32_t error_code = 0;
        int i;
@@ -826,11 +851,11 @@ static uint32_t i915_error_generate_code(struct drm_i915_private *dev_priv,
         * strictly a client bug. Use instdone to differentiate those some.
         */
        for (i = 0; i < I915_NUM_ENGINES; i++) {
-               if (error->ring[i].hangcheck_action == HANGCHECK_HUNG) {
-                       if (ring_id)
-                               *ring_id = i;
+               if (error->engine[i].hangcheck_action == HANGCHECK_HUNG) {
+                       if (engine_id)
+                               *engine_id = i;
 
-                       return error->ring[i].ipehr ^ error->ring[i].instdone;
+                       return error->engine[i].ipehr ^ error->engine[i].instdone;
                }
        }
 
@@ -855,22 +880,17 @@ static void i915_gem_record_fences(struct drm_i915_private *dev_priv,
 }
 
 
-static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv,
-                                       struct drm_i915_error_state *error,
+static void gen8_record_semaphore_state(struct drm_i915_error_state *error,
                                        struct intel_engine_cs *engine,
-                                       struct drm_i915_error_ring *ering)
+                                       struct drm_i915_error_engine *ee)
 {
+       struct drm_i915_private *dev_priv = engine->i915;
        struct intel_engine_cs *to;
        enum intel_engine_id id;
 
-       if (!i915_semaphore_is_enabled(dev_priv))
+       if (!error->semaphore)
                return;
 
-       if (!error->semaphore_obj)
-               error->semaphore_obj =
-                       i915_error_ggtt_object_create(dev_priv,
-                                                     dev_priv->semaphore_obj);
-
        for_each_engine_id(to, dev_priv, id) {
                int idx;
                u16 signal_offset;
@@ -879,44 +899,52 @@ static void gen8_record_semaphore_state(struct drm_i915_private *dev_priv,
                if (engine == to)
                        continue;
 
-               signal_offset = (GEN8_SIGNAL_OFFSET(engine, id) & (PAGE_SIZE - 1))
-                               / 4;
-               tmp = error->semaphore_obj->pages[0];
-               idx = intel_ring_sync_index(engine, to);
+               signal_offset =
+                       (GEN8_SIGNAL_OFFSET(engine, id) & (PAGE_SIZE - 1)) / 4;
+               tmp = error->semaphore->pages[0];
+               idx = intel_engine_sync_index(engine, to);
 
-               ering->semaphore_mboxes[idx] = tmp[signal_offset];
-               ering->semaphore_seqno[idx] = engine->semaphore.sync_seqno[idx];
+               ee->semaphore_mboxes[idx] = tmp[signal_offset];
+               ee->semaphore_seqno[idx] = engine->semaphore.sync_seqno[idx];
        }
 }
 
-static void gen6_record_semaphore_state(struct drm_i915_private *dev_priv,
-                                       struct intel_engine_cs *engine,
-                                       struct drm_i915_error_ring *ering)
+static void gen6_record_semaphore_state(struct intel_engine_cs *engine,
+                                       struct drm_i915_error_engine *ee)
 {
-       ering->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(engine->mmio_base));
-       ering->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(engine->mmio_base));
-       ering->semaphore_seqno[0] = engine->semaphore.sync_seqno[0];
-       ering->semaphore_seqno[1] = engine->semaphore.sync_seqno[1];
+       struct drm_i915_private *dev_priv = engine->i915;
+
+       ee->semaphore_mboxes[0] = I915_READ(RING_SYNC_0(engine->mmio_base));
+       ee->semaphore_mboxes[1] = I915_READ(RING_SYNC_1(engine->mmio_base));
+       ee->semaphore_seqno[0] = engine->semaphore.sync_seqno[0];
+       ee->semaphore_seqno[1] = engine->semaphore.sync_seqno[1];
 
        if (HAS_VEBOX(dev_priv)) {
-               ering->semaphore_mboxes[2] =
+               ee->semaphore_mboxes[2] =
                        I915_READ(RING_SYNC_2(engine->mmio_base));
-               ering->semaphore_seqno[2] = engine->semaphore.sync_seqno[2];
+               ee->semaphore_seqno[2] = engine->semaphore.sync_seqno[2];
        }
 }
 
-static void engine_record_waiters(struct intel_engine_cs *engine,
-                                 struct drm_i915_error_ring *ering)
+static void error_record_engine_waiters(struct intel_engine_cs *engine,
+                                       struct drm_i915_error_engine *ee)
 {
        struct intel_breadcrumbs *b = &engine->breadcrumbs;
        struct drm_i915_error_waiter *waiter;
        struct rb_node *rb;
        int count;
 
-       ering->num_waiters = 0;
-       ering->waiters = NULL;
+       ee->num_waiters = 0;
+       ee->waiters = NULL;
+
+       if (RB_EMPTY_ROOT(&b->waiters))
+               return;
+
+       if (!spin_trylock(&b->lock)) {
+               ee->waiters = ERR_PTR(-EDEADLK);
+               return;
+       }
 
-       spin_lock(&b->lock);
        count = 0;
        for (rb = rb_first(&b->waiters); rb != NULL; rb = rb_next(rb))
                count++;
@@ -930,9 +958,13 @@ static void engine_record_waiters(struct intel_engine_cs *engine,
        if (!waiter)
                return;
 
-       ering->waiters = waiter;
+       if (!spin_trylock(&b->lock)) {
+               kfree(waiter);
+               ee->waiters = ERR_PTR(-EDEADLK);
+               return;
+       }
 
-       spin_lock(&b->lock);
+       ee->waiters = waiter;
        for (rb = rb_first(&b->waiters); rb; rb = rb_next(rb)) {
                struct intel_wait *w = container_of(rb, typeof(*w), node);
 
@@ -941,57 +973,59 @@ static void engine_record_waiters(struct intel_engine_cs *engine,
                waiter->seqno = w->seqno;
                waiter++;
 
-               if (++ering->num_waiters == count)
+               if (++ee->num_waiters == count)
                        break;
        }
        spin_unlock(&b->lock);
 }
 
-static void i915_record_ring_state(struct drm_i915_private *dev_priv,
-                                  struct drm_i915_error_state *error,
-                                  struct intel_engine_cs *engine,
-                                  struct drm_i915_error_ring *ering)
+static void error_record_engine_registers(struct drm_i915_error_state *error,
+                                         struct intel_engine_cs *engine,
+                                         struct drm_i915_error_engine *ee)
 {
+       struct drm_i915_private *dev_priv = engine->i915;
+
        if (INTEL_GEN(dev_priv) >= 6) {
-               ering->rc_psmi = I915_READ(RING_PSMI_CTL(engine->mmio_base));
-               ering->fault_reg = I915_READ(RING_FAULT_REG(engine));
+               ee->rc_psmi = I915_READ(RING_PSMI_CTL(engine->mmio_base));
+               ee->fault_reg = I915_READ(RING_FAULT_REG(engine));
                if (INTEL_GEN(dev_priv) >= 8)
-                       gen8_record_semaphore_state(dev_priv, error, engine,
-                                                   ering);
+                       gen8_record_semaphore_state(error, engine, ee);
                else
-                       gen6_record_semaphore_state(dev_priv, engine, ering);
+                       gen6_record_semaphore_state(engine, ee);
        }
 
        if (INTEL_GEN(dev_priv) >= 4) {
-               ering->faddr = I915_READ(RING_DMA_FADD(engine->mmio_base));
-               ering->ipeir = I915_READ(RING_IPEIR(engine->mmio_base));
-               ering->ipehr = I915_READ(RING_IPEHR(engine->mmio_base));
-               ering->instdone = I915_READ(RING_INSTDONE(engine->mmio_base));
-               ering->instps = I915_READ(RING_INSTPS(engine->mmio_base));
-               ering->bbaddr = I915_READ(RING_BBADDR(engine->mmio_base));
+               ee->faddr = I915_READ(RING_DMA_FADD(engine->mmio_base));
+               ee->ipeir = I915_READ(RING_IPEIR(engine->mmio_base));
+               ee->ipehr = I915_READ(RING_IPEHR(engine->mmio_base));
+               ee->instdone = I915_READ(RING_INSTDONE(engine->mmio_base));
+               ee->instps = I915_READ(RING_INSTPS(engine->mmio_base));
+               ee->bbaddr = I915_READ(RING_BBADDR(engine->mmio_base));
                if (INTEL_GEN(dev_priv) >= 8) {
-                       ering->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(engine->mmio_base)) << 32;
-                       ering->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(engine->mmio_base)) << 32;
+                       ee->faddr |= (u64) I915_READ(RING_DMA_FADD_UDW(engine->mmio_base)) << 32;
+                       ee->bbaddr |= (u64) I915_READ(RING_BBADDR_UDW(engine->mmio_base)) << 32;
                }
-               ering->bbstate = I915_READ(RING_BBSTATE(engine->mmio_base));
+               ee->bbstate = I915_READ(RING_BBSTATE(engine->mmio_base));
        } else {
-               ering->faddr = I915_READ(DMA_FADD_I8XX);
-               ering->ipeir = I915_READ(IPEIR);
-               ering->ipehr = I915_READ(IPEHR);
-               ering->instdone = I915_READ(GEN2_INSTDONE);
+               ee->faddr = I915_READ(DMA_FADD_I8XX);
+               ee->ipeir = I915_READ(IPEIR);
+               ee->ipehr = I915_READ(IPEHR);
+               ee->instdone = I915_READ(GEN2_INSTDONE);
        }
 
-       ering->waiting = intel_engine_has_waiter(engine);
-       ering->instpm = I915_READ(RING_INSTPM(engine->mmio_base));
-       ering->acthd = intel_ring_get_active_head(engine);
-       ering->seqno = intel_engine_get_seqno(engine);
-       ering->last_seqno = engine->last_submitted_seqno;
-       ering->start = I915_READ_START(engine);
-       ering->head = I915_READ_HEAD(engine);
-       ering->tail = I915_READ_TAIL(engine);
-       ering->ctl = I915_READ_CTL(engine);
-
-       if (I915_NEED_GFX_HWS(dev_priv)) {
+       ee->waiting = intel_engine_has_waiter(engine);
+       ee->instpm = I915_READ(RING_INSTPM(engine->mmio_base));
+       ee->acthd = intel_engine_get_active_head(engine);
+       ee->seqno = intel_engine_get_seqno(engine);
+       ee->last_seqno = engine->last_submitted_seqno;
+       ee->start = I915_READ_START(engine);
+       ee->head = I915_READ_HEAD(engine);
+       ee->tail = I915_READ_TAIL(engine);
+       ee->ctl = I915_READ_CTL(engine);
+       if (INTEL_GEN(dev_priv) > 2)
+               ee->mode = I915_READ_MODE(engine);
+
+       if (!HWS_NEEDS_PHYSICAL(dev_priv)) {
                i915_reg_t mmio;
 
                if (IS_GEN7(dev_priv)) {
@@ -1017,107 +1051,150 @@ static void i915_record_ring_state(struct drm_i915_private *dev_priv,
                        mmio = RING_HWS_PGA(engine->mmio_base);
                }
 
-               ering->hws = I915_READ(mmio);
+               ee->hws = I915_READ(mmio);
        }
 
-       ering->hangcheck_score = engine->hangcheck.score;
-       ering->hangcheck_action = engine->hangcheck.action;
+       ee->hangcheck_score = engine->hangcheck.score;
+       ee->hangcheck_action = engine->hangcheck.action;
 
        if (USES_PPGTT(dev_priv)) {
                int i;
 
-               ering->vm_info.gfx_mode = I915_READ(RING_MODE_GEN7(engine));
+               ee->vm_info.gfx_mode = I915_READ(RING_MODE_GEN7(engine));
 
                if (IS_GEN6(dev_priv))
-                       ering->vm_info.pp_dir_base =
+                       ee->vm_info.pp_dir_base =
                                I915_READ(RING_PP_DIR_BASE_READ(engine));
                else if (IS_GEN7(dev_priv))
-                       ering->vm_info.pp_dir_base =
+                       ee->vm_info.pp_dir_base =
                                I915_READ(RING_PP_DIR_BASE(engine));
                else if (INTEL_GEN(dev_priv) >= 8)
                        for (i = 0; i < 4; i++) {
-                               ering->vm_info.pdp[i] =
+                               ee->vm_info.pdp[i] =
                                        I915_READ(GEN8_RING_PDP_UDW(engine, i));
-                               ering->vm_info.pdp[i] <<= 32;
-                               ering->vm_info.pdp[i] |=
+                               ee->vm_info.pdp[i] <<= 32;
+                               ee->vm_info.pdp[i] |=
                                        I915_READ(GEN8_RING_PDP_LDW(engine, i));
                        }
        }
 }
 
-
-static void i915_gem_record_active_context(struct intel_engine_cs *engine,
-                                          struct drm_i915_error_state *error,
-                                          struct drm_i915_error_ring *ering)
+static void engine_record_requests(struct intel_engine_cs *engine,
+                                  struct drm_i915_gem_request *first,
+                                  struct drm_i915_error_engine *ee)
 {
-       struct drm_i915_private *dev_priv = engine->i915;
-       struct drm_i915_gem_object *obj;
+       struct drm_i915_gem_request *request;
+       int count;
 
-       /* Currently render ring is the only HW context user */
-       if (engine->id != RCS || !error->ccid)
+       count = 0;
+       request = first;
+       list_for_each_entry_from(request, &engine->request_list, link)
+               count++;
+       if (!count)
                return;
 
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               if (!i915_gem_obj_ggtt_bound(obj))
-                       continue;
+       ee->requests = kcalloc(count, sizeof(*ee->requests), GFP_ATOMIC);
+       if (!ee->requests)
+               return;
 
-               if ((error->ccid & PAGE_MASK) == i915_gem_obj_ggtt_offset(obj)) {
-                       ering->ctx = i915_error_ggtt_object_create(dev_priv, obj);
+       ee->num_requests = count;
+
+       count = 0;
+       request = first;
+       list_for_each_entry_from(request, &engine->request_list, link) {
+               struct drm_i915_error_request *erq;
+
+               if (count >= ee->num_requests) {
+                       /*
+                        * If the ring request list was changed in
+                        * between the point where the error request
+                        * list was created and dimensioned and this
+                        * point then just exit early to avoid crashes.
+                        *
+                        * We don't need to communicate that the
+                        * request list changed state during error
+                        * state capture and that the error state is
+                        * slightly incorrect as a consequence since we
+                        * are typically only interested in the request
+                        * list state at the point of error state
+                        * capture, not in any changes happening during
+                        * the capture.
+                        */
                        break;
                }
+
+               erq = &ee->requests[count++];
+               erq->seqno = request->fence.seqno;
+               erq->jiffies = request->emitted_jiffies;
+               erq->head = request->head;
+               erq->tail = request->tail;
+
+               rcu_read_lock();
+               erq->pid = request->ctx->pid ? pid_nr(request->ctx->pid) : 0;
+               rcu_read_unlock();
        }
+       ee->num_requests = count;
 }
 
 static void i915_gem_record_rings(struct drm_i915_private *dev_priv,
                                  struct drm_i915_error_state *error)
 {
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
-       struct drm_i915_gem_request *request;
-       int i, count;
+       int i;
+
+       error->semaphore =
+               i915_error_object_create(dev_priv, dev_priv->semaphore);
 
        for (i = 0; i < I915_NUM_ENGINES; i++) {
                struct intel_engine_cs *engine = &dev_priv->engine[i];
+               struct drm_i915_error_engine *ee = &error->engine[i];
+               struct drm_i915_gem_request *request;
 
-               error->ring[i].pid = -1;
+               ee->pid = -1;
+               ee->engine_id = -1;
 
                if (!intel_engine_initialized(engine))
                        continue;
 
-               error->ring[i].valid = true;
+               ee->engine_id = i;
 
-               i915_record_ring_state(dev_priv, error, engine, &error->ring[i]);
-               engine_record_waiters(engine, &error->ring[i]);
+               error_record_engine_registers(error, engine, ee);
+               error_record_engine_waiters(engine, ee);
 
                request = i915_gem_find_active_request(engine);
                if (request) {
-                       struct i915_address_space *vm;
-                       struct intel_ringbuffer *rb;
+                       struct intel_ring *ring;
+                       struct pid *pid;
 
-                       vm = request->ctx->ppgtt ?
+                       ee->vm = request->ctx->ppgtt ?
                                &request->ctx->ppgtt->base : &ggtt->base;
 
                        /* We need to copy these to an anonymous buffer
                         * as the simplest method to avoid being overwritten
                         * by userspace.
                         */
-                       error->ring[i].batchbuffer =
+                       ee->batchbuffer =
                                i915_error_object_create(dev_priv,
-                                                        request->batch_obj,
-                                                        vm);
+                                                        request->batch);
 
                        if (HAS_BROKEN_CS_TLB(dev_priv))
-                               error->ring[i].wa_batchbuffer =
-                                       i915_error_ggtt_object_create(dev_priv,
-                                                            engine->scratch.obj);
+                               ee->wa_batchbuffer =
+                                       i915_error_object_create(dev_priv,
+                                                                engine->scratch);
+
+                       ee->ctx =
+                               i915_error_object_create(dev_priv,
+                                                        request->ctx->engine[i].state);
 
-                       if (request->pid) {
+                       pid = request->ctx->pid;
+                       if (pid) {
                                struct task_struct *task;
 
                                rcu_read_lock();
-                               task = pid_task(request->pid, PIDTYPE_PID);
+                               task = pid_task(pid, PIDTYPE_PID);
                                if (task) {
-                                       strcpy(error->ring[i].comm, task->comm);
-                                       error->ring[i].pid = task->pid;
+                                       strcpy(ee->comm, task->comm);
+                                       ee->pid = task->pid;
                                }
                                rcu_read_unlock();
                        }
@@ -1125,153 +1202,106 @@ static void i915_gem_record_rings(struct drm_i915_private *dev_priv,
                        error->simulated |=
                                request->ctx->flags & CONTEXT_NO_ERROR_CAPTURE;
 
-                       rb = request->ringbuf;
-                       error->ring[i].cpu_ring_head = rb->head;
-                       error->ring[i].cpu_ring_tail = rb->tail;
-                       error->ring[i].ringbuffer =
-                               i915_error_ggtt_object_create(dev_priv,
-                                                             rb->obj);
-               }
-
-               error->ring[i].hws_page =
-                       i915_error_ggtt_object_create(dev_priv,
-                                                     engine->status_page.obj);
+                       ring = request->ring;
+                       ee->cpu_ring_head = ring->head;
+                       ee->cpu_ring_tail = ring->tail;
+                       ee->ringbuffer =
+                               i915_error_object_create(dev_priv, ring->vma);
 
-               if (engine->wa_ctx.obj) {
-                       error->ring[i].wa_ctx =
-                               i915_error_ggtt_object_create(dev_priv,
-                                                             engine->wa_ctx.obj);
+                       engine_record_requests(engine, request, ee);
                }
 
-               i915_gem_record_active_context(engine, error, &error->ring[i]);
-
-               count = 0;
-               list_for_each_entry(request, &engine->request_list, list)
-                       count++;
-
-               error->ring[i].num_requests = count;
-               error->ring[i].requests =
-                       kcalloc(count, sizeof(*error->ring[i].requests),
-                               GFP_ATOMIC);
-               if (error->ring[i].requests == NULL) {
-                       error->ring[i].num_requests = 0;
-                       continue;
-               }
-
-               count = 0;
-               list_for_each_entry(request, &engine->request_list, list) {
-                       struct drm_i915_error_request *erq;
-
-                       if (count >= error->ring[i].num_requests) {
-                               /*
-                                * If the ring request list was changed in
-                                * between the point where the error request
-                                * list was created and dimensioned and this
-                                * point then just exit early to avoid crashes.
-                                *
-                                * We don't need to communicate that the
-                                * request list changed state during error
-                                * state capture and that the error state is
-                                * slightly incorrect as a consequence since we
-                                * are typically only interested in the request
-                                * list state at the point of error state
-                                * capture, not in any changes happening during
-                                * the capture.
-                                */
-                               break;
-                       }
+               ee->hws_page =
+                       i915_error_object_create(dev_priv,
+                                                engine->status_page.vma);
 
-                       erq = &error->ring[i].requests[count++];
-                       erq->seqno = request->seqno;
-                       erq->jiffies = request->emitted_jiffies;
-                       erq->tail = request->postfix;
-               }
+               ee->wa_ctx =
+                       i915_error_object_create(dev_priv, engine->wa_ctx.vma);
        }
 }
 
-/* FIXME: Since pin count/bound list is global, we duplicate what we capture per
- * VM.
- */
 static void i915_gem_capture_vm(struct drm_i915_private *dev_priv,
                                struct drm_i915_error_state *error,
                                struct i915_address_space *vm,
-                               const int ndx)
+                               int idx)
 {
-       struct drm_i915_error_buffer *active_bo = NULL, *pinned_bo = NULL;
-       struct drm_i915_gem_object *obj;
+       struct drm_i915_error_buffer *active_bo;
        struct i915_vma *vma;
-       int i;
+       int count;
 
-       i = 0;
+       count = 0;
        list_for_each_entry(vma, &vm->active_list, vm_link)
-               i++;
-       error->active_bo_count[ndx] = i;
-
-       list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
-               list_for_each_entry(vma, &obj->vma_list, obj_link)
-                       if (vma->vm == vm && vma->pin_count > 0)
-                               i++;
-       }
-       error->pinned_bo_count[ndx] = i - error->active_bo_count[ndx];
-
-       if (i) {
-               active_bo = kcalloc(i, sizeof(*active_bo), GFP_ATOMIC);
-               if (active_bo)
-                       pinned_bo = active_bo + error->active_bo_count[ndx];
-       }
+               count++;
 
+       active_bo = NULL;
+       if (count)
+               active_bo = kcalloc(count, sizeof(*active_bo), GFP_ATOMIC);
        if (active_bo)
-               error->active_bo_count[ndx] =
-                       capture_active_bo(active_bo,
-                                         error->active_bo_count[ndx],
-                                         &vm->active_list);
-
-       if (pinned_bo)
-               error->pinned_bo_count[ndx] =
-                       capture_pinned_bo(pinned_bo,
-                                         error->pinned_bo_count[ndx],
-                                         &dev_priv->mm.bound_list, vm);
-       error->active_bo[ndx] = active_bo;
-       error->pinned_bo[ndx] = pinned_bo;
+               count = capture_error_bo(active_bo, count, &vm->active_list, false);
+       else
+               count = 0;
+
+       error->active_vm[idx] = vm;
+       error->active_bo[idx] = active_bo;
+       error->active_bo_count[idx] = count;
 }
 
-static void i915_gem_capture_buffers(struct drm_i915_private *dev_priv,
-                                    struct drm_i915_error_state *error)
+static void i915_capture_active_buffers(struct drm_i915_private *dev_priv,
+                                       struct drm_i915_error_state *error)
 {
-       struct i915_address_space *vm;
-       int cnt = 0, i = 0;
-
-       list_for_each_entry(vm, &dev_priv->vm_list, global_link)
-               cnt++;
-
-       error->active_bo = kcalloc(cnt, sizeof(*error->active_bo), GFP_ATOMIC);
-       error->pinned_bo = kcalloc(cnt, sizeof(*error->pinned_bo), GFP_ATOMIC);
-       error->active_bo_count = kcalloc(cnt, sizeof(*error->active_bo_count),
-                                        GFP_ATOMIC);
-       error->pinned_bo_count = kcalloc(cnt, sizeof(*error->pinned_bo_count),
-                                        GFP_ATOMIC);
-
-       if (error->active_bo == NULL ||
-           error->pinned_bo == NULL ||
-           error->active_bo_count == NULL ||
-           error->pinned_bo_count == NULL) {
-               kfree(error->active_bo);
-               kfree(error->active_bo_count);
-               kfree(error->pinned_bo);
-               kfree(error->pinned_bo_count);
-
-               error->active_bo = NULL;
-               error->active_bo_count = NULL;
-               error->pinned_bo = NULL;
-               error->pinned_bo_count = NULL;
-       } else {
-               list_for_each_entry(vm, &dev_priv->vm_list, global_link)
-                       i915_gem_capture_vm(dev_priv, error, vm, i++);
+       int cnt = 0, i, j;
 
-               error->vm_count = cnt;
+       BUILD_BUG_ON(ARRAY_SIZE(error->engine) > ARRAY_SIZE(error->active_bo));
+       BUILD_BUG_ON(ARRAY_SIZE(error->active_bo) != ARRAY_SIZE(error->active_vm));
+       BUILD_BUG_ON(ARRAY_SIZE(error->active_bo) != ARRAY_SIZE(error->active_bo_count));
+
+       /* Scan each engine looking for unique active contexts/vm */
+       for (i = 0; i < ARRAY_SIZE(error->engine); i++) {
+               struct drm_i915_error_engine *ee = &error->engine[i];
+               bool found;
+
+               if (!ee->vm)
+                       continue;
+
+               found = false;
+               for (j = 0; j < i && !found; j++)
+                       found = error->engine[j].vm == ee->vm;
+               if (!found)
+                       i915_gem_capture_vm(dev_priv, error, ee->vm, cnt++);
        }
 }
 
+static void i915_capture_pinned_buffers(struct drm_i915_private *dev_priv,
+                                       struct drm_i915_error_state *error)
+{
+       struct i915_address_space *vm = &dev_priv->ggtt.base;
+       struct drm_i915_error_buffer *bo;
+       struct i915_vma *vma;
+       int count_inactive, count_active;
+
+       count_inactive = 0;
+       list_for_each_entry(vma, &vm->active_list, vm_link)
+               count_inactive++;
+
+       count_active = 0;
+       list_for_each_entry(vma, &vm->inactive_list, vm_link)
+               count_active++;
+
+       bo = NULL;
+       if (count_inactive + count_active)
+               bo = kcalloc(count_inactive + count_active,
+                            sizeof(*bo), GFP_ATOMIC);
+       if (!bo)
+               return;
+
+       count_inactive = capture_error_bo(bo, count_inactive,
+                                         &vm->active_list, true);
+       count_active = capture_error_bo(bo + count_inactive, count_active,
+                                       &vm->inactive_list, true);
+       error->pinned_bo_count = count_inactive + count_active;
+       error->pinned_bo = bo;
+}
+
 /* Capture all registers which don't fit into another category. */
 static void i915_capture_reg_state(struct drm_i915_private *dev_priv,
                                   struct drm_i915_error_state *error)
@@ -1352,20 +1382,20 @@ static void i915_error_capture_msg(struct drm_i915_private *dev_priv,
                                   const char *error_msg)
 {
        u32 ecode;
-       int ring_id = -1, len;
+       int engine_id = -1, len;
 
-       ecode = i915_error_generate_code(dev_priv, error, &ring_id);
+       ecode = i915_error_generate_code(dev_priv, error, &engine_id);
 
        len = scnprintf(error->error_msg, sizeof(error->error_msg),
                        "GPU HANG: ecode %d:%d:0x%08x",
-                       INTEL_GEN(dev_priv), ring_id, ecode);
+                       INTEL_GEN(dev_priv), engine_id, ecode);
 
-       if (ring_id != -1 && error->ring[ring_id].pid != -1)
+       if (engine_id != -1 && error->engine[engine_id].pid != -1)
                len += scnprintf(error->error_msg + len,
                                 sizeof(error->error_msg) - len,
                                 ", in %s [%d]",
-                                error->ring[ring_id].comm,
-                                error->ring[ring_id].pid);
+                                error->engine[engine_id].comm,
+                                error->engine[engine_id].pid);
 
        scnprintf(error->error_msg + len, sizeof(error->error_msg) - len,
                  ", reason: %s, action: %s",
@@ -1382,6 +1412,10 @@ static void i915_capture_gen_state(struct drm_i915_private *dev_priv,
 #endif
        error->reset_count = i915_reset_count(&dev_priv->gpu_error);
        error->suspend_count = dev_priv->suspend_count;
+
+       memcpy(&error->device_info,
+              INTEL_INFO(dev_priv),
+              sizeof(error->device_info));
 }
 
 /**
@@ -1415,9 +1449,10 @@ void i915_capture_error_state(struct drm_i915_private *dev_priv,
 
        i915_capture_gen_state(dev_priv, error);
        i915_capture_reg_state(dev_priv, error);
-       i915_gem_capture_buffers(dev_priv, error);
        i915_gem_record_fences(dev_priv, error);
        i915_gem_record_rings(dev_priv, error);
+       i915_capture_active_buffers(dev_priv, error);
+       i915_capture_pinned_buffers(dev_priv, error);
 
        do_gettimeofday(&error->time);
 
index cf5a65b..a47e1e4 100644 (file)
 #define HOST2GUC_INTERRUPT             _MMIO(0xc4c8)
 #define   HOST2GUC_TRIGGER               (1<<0)
 
-#define DRBMISC1                       0x1984
-#define   DOORBELL_ENABLE                (1<<0)
-
 #define GEN8_DRBREGL(x)                        _MMIO(0x1000 + (x) * 8)
 #define   GEN8_DRB_VALID                 (1<<0)
 #define GEN8_DRBREGU(x)                        _MMIO(0x1000 + (x) * 8 + 4)
index 2112e02..3106dcc 100644 (file)
@@ -59,7 +59,7 @@
  * WQ_TYPE_INORDER is needed to support legacy submission via GuC, which
  * represents in-order queue. The kernel driver packs ring tail pointer and an
  * ELSP context descriptor dword into Work Item.
- * See guc_add_workqueue_item()
+ * See guc_wq_item_append()
  *
  */
 
@@ -114,10 +114,8 @@ static int host2guc_action(struct intel_guc *guc, u32 *data, u32 len)
                if (ret != -ETIMEDOUT)
                        ret = -EIO;
 
-               DRM_ERROR("GUC: host2guc action 0x%X failed. ret=%d "
-                               "status=0x%08X response=0x%08X\n",
-                               data[0], ret, status,
-                               I915_READ(SOFT_SCRATCH(15)));
+               DRM_WARN("Action 0x%X failed; ret=%d status=0x%08X response=0x%08X\n",
+                        data[0], ret, status, I915_READ(SOFT_SCRATCH(15)));
 
                dev_priv->guc.action_fail += 1;
                dev_priv->guc.action_err = ret;
@@ -183,7 +181,7 @@ static int guc_update_doorbell_id(struct intel_guc *guc,
                                  struct i915_guc_client *client,
                                  u16 new_id)
 {
-       struct sg_table *sg = guc->ctx_pool_obj->pages;
+       struct sg_table *sg = guc->ctx_pool_vma->pages;
        void *doorbell_bitmap = guc->doorbell_bitmap;
        struct guc_doorbell_info *doorbell;
        struct guc_context_desc desc;
@@ -290,7 +288,7 @@ static uint32_t select_doorbell_cacheline(struct intel_guc *guc)
 /*
  * Initialise the process descriptor shared with the GuC firmware.
  */
-static void guc_init_proc_desc(struct intel_guc *guc,
+static void guc_proc_desc_init(struct intel_guc *guc,
                               struct i915_guc_client *client)
 {
        struct guc_process_desc *desc;
@@ -322,15 +320,15 @@ static void guc_init_proc_desc(struct intel_guc *guc,
  * write queue, etc).
  */
 
-static void guc_init_ctx_desc(struct intel_guc *guc,
+static void guc_ctx_desc_init(struct intel_guc *guc,
                              struct i915_guc_client *client)
 {
-       struct drm_i915_gem_object *client_obj = client->client_obj;
        struct drm_i915_private *dev_priv = guc_to_i915(guc);
        struct intel_engine_cs *engine;
        struct i915_gem_context *ctx = client->owner;
        struct guc_context_desc desc;
        struct sg_table *sg;
+       unsigned int tmp;
        u32 gfx_addr;
 
        memset(&desc, 0, sizeof(desc));
@@ -340,10 +338,10 @@ static void guc_init_ctx_desc(struct intel_guc *guc,
        desc.priority = client->priority;
        desc.db_id = client->doorbell_id;
 
-       for_each_engine(engine, dev_priv) {
+       for_each_engine_masked(engine, dev_priv, client->engines, tmp) {
                struct intel_context *ce = &ctx->engine[engine->id];
-               struct guc_execlist_context *lrc = &desc.lrc[engine->guc_id];
-               struct drm_i915_gem_object *obj;
+               uint32_t guc_engine_id = engine->guc_id;
+               struct guc_execlist_context *lrc = &desc.lrc[guc_engine_id];
 
                /* TODO: We have a design issue to be solved here. Only when we
                 * receive the first batch, we know which engine is used by the
@@ -358,30 +356,29 @@ static void guc_init_ctx_desc(struct intel_guc *guc,
                lrc->context_desc = lower_32_bits(ce->lrc_desc);
 
                /* The state page is after PPHWSP */
-               gfx_addr = i915_gem_obj_ggtt_offset(ce->state);
-               lrc->ring_lcra = gfx_addr + LRC_STATE_PN * PAGE_SIZE;
+               lrc->ring_lcra =
+                       i915_ggtt_offset(ce->state) + LRC_STATE_PN * PAGE_SIZE;
                lrc->context_id = (client->ctx_index << GUC_ELC_CTXID_OFFSET) |
-                               (engine->guc_id << GUC_ELC_ENGINE_OFFSET);
-
-               obj = ce->ringbuf->obj;
-               gfx_addr = i915_gem_obj_ggtt_offset(obj);
+                               (guc_engine_id << GUC_ELC_ENGINE_OFFSET);
 
-               lrc->ring_begin = gfx_addr;
-               lrc->ring_end = gfx_addr + obj->base.size - 1;
-               lrc->ring_next_free_location = gfx_addr;
+               lrc->ring_begin = i915_ggtt_offset(ce->ring->vma);
+               lrc->ring_end = lrc->ring_begin + ce->ring->size - 1;
+               lrc->ring_next_free_location = lrc->ring_begin;
                lrc->ring_current_tail_pointer_value = 0;
 
-               desc.engines_used |= (1 << engine->guc_id);
+               desc.engines_used |= (1 << guc_engine_id);
        }
 
+       DRM_DEBUG_DRIVER("Host engines 0x%x => GuC engines used 0x%x\n",
+                       client->engines, desc.engines_used);
        WARN_ON(desc.engines_used == 0);
 
        /*
         * The doorbell, process descriptor, and workqueue are all parts
         * of the client object, which the GuC will reference via the GGTT
         */
-       gfx_addr = i915_gem_obj_ggtt_offset(client_obj);
-       desc.db_trigger_phy = sg_dma_address(client_obj->pages->sgl) +
+       gfx_addr = i915_ggtt_offset(client->vma);
+       desc.db_trigger_phy = sg_dma_address(client->vma->pages->sgl) +
                                client->doorbell_offset;
        desc.db_trigger_cpu = (uintptr_t)client->client_base +
                                client->doorbell_offset;
@@ -397,12 +394,12 @@ static void guc_init_ctx_desc(struct intel_guc *guc,
        desc.desc_private = (uintptr_t)client;
 
        /* Pool context is pinned already */
-       sg = guc->ctx_pool_obj->pages;
+       sg = guc->ctx_pool_vma->pages;
        sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc),
                             sizeof(desc) * client->ctx_index);
 }
 
-static void guc_fini_ctx_desc(struct intel_guc *guc,
+static void guc_ctx_desc_fini(struct intel_guc *guc,
                              struct i915_guc_client *client)
 {
        struct guc_context_desc desc;
@@ -410,13 +407,13 @@ static void guc_fini_ctx_desc(struct intel_guc *guc,
 
        memset(&desc, 0, sizeof(desc));
 
-       sg = guc->ctx_pool_obj->pages;
+       sg = guc->ctx_pool_vma->pages;
        sg_pcopy_from_buffer(sg->sgl, sg->nents, &desc, sizeof(desc),
                             sizeof(desc) * client->ctx_index);
 }
 
 /**
- * i915_guc_wq_check_space() - check that the GuC can accept a request
+ * i915_guc_wq_reserve() - reserve space in the GuC's workqueue
  * @request:   request associated with the commands
  *
  * Return:     0 if space is available
@@ -424,39 +421,56 @@ static void guc_fini_ctx_desc(struct intel_guc *guc,
  *
  * This function must be called (and must return 0) before a request
  * is submitted to the GuC via i915_guc_submit() below. Once a result
- * of 0 has been returned, it remains valid until (but only until)
- * the next call to submit().
+ * of 0 has been returned, it must be balanced by a corresponding
+ * call to submit().
  *
- * This precheck allows the caller to determine in advance that space
+ * Reservation allows the caller to determine in advance that space
  * will be available for the next submission before committing resources
  * to it, and helps avoid late failures with complicated recovery paths.
  */
-int i915_guc_wq_check_space(struct drm_i915_gem_request *request)
+int i915_guc_wq_reserve(struct drm_i915_gem_request *request)
 {
        const size_t wqi_size = sizeof(struct guc_wq_item);
        struct i915_guc_client *gc = request->i915->guc.execbuf_client;
-       struct guc_process_desc *desc;
+       struct guc_process_desc *desc = gc->client_base + gc->proc_desc_offset;
        u32 freespace;
+       int ret;
 
-       GEM_BUG_ON(gc == NULL);
+       spin_lock(&gc->wq_lock);
+       freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size);
+       freespace -= gc->wq_rsvd;
+       if (likely(freespace >= wqi_size)) {
+               gc->wq_rsvd += wqi_size;
+               ret = 0;
+       } else {
+               gc->no_wq_space++;
+               ret = -EAGAIN;
+       }
+       spin_unlock(&gc->wq_lock);
 
-       desc = gc->client_base + gc->proc_desc_offset;
+       return ret;
+}
 
-       freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size);
-       if (likely(freespace >= wqi_size))
-               return 0;
+void i915_guc_wq_unreserve(struct drm_i915_gem_request *request)
+{
+       const size_t wqi_size = sizeof(struct guc_wq_item);
+       struct i915_guc_client *gc = request->i915->guc.execbuf_client;
 
-       gc->no_wq_space += 1;
+       GEM_BUG_ON(READ_ONCE(gc->wq_rsvd) < wqi_size);
 
-       return -EAGAIN;
+       spin_lock(&gc->wq_lock);
+       gc->wq_rsvd -= wqi_size;
+       spin_unlock(&gc->wq_lock);
 }
 
-static void guc_add_workqueue_item(struct i915_guc_client *gc,
-                                  struct drm_i915_gem_request *rq)
+/* Construct a Work Item and append it to the GuC's Work Queue */
+static void guc_wq_item_append(struct i915_guc_client *gc,
+                              struct drm_i915_gem_request *rq)
 {
        /* wqi_len is in DWords, and does not include the one-word header */
        const size_t wqi_size = sizeof(struct guc_wq_item);
        const u32 wqi_len = wqi_size/sizeof(u32) - 1;
+       struct intel_engine_cs *engine = rq->engine;
        struct guc_process_desc *desc;
        struct guc_wq_item *wqi;
        void *base;
@@ -464,7 +478,7 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc,
 
        desc = gc->client_base + gc->proc_desc_offset;
 
-       /* Free space is guaranteed, see i915_guc_wq_check_space() above */
+       /* Free space is guaranteed, see i915_guc_wq_reserve() above */
        freespace = CIRC_SPACE(gc->wq_tail, desc->head, gc->wq_size);
        GEM_BUG_ON(freespace < wqi_size);
 
@@ -482,31 +496,32 @@ static void guc_add_workqueue_item(struct i915_guc_client *gc,
         * workqueue buffer dw by dw.
         */
        BUILD_BUG_ON(wqi_size != 16);
+       GEM_BUG_ON(gc->wq_rsvd < wqi_size);
 
        /* postincrement WQ tail for next time */
        wq_off = gc->wq_tail;
+       GEM_BUG_ON(wq_off & (wqi_size - 1));
        gc->wq_tail += wqi_size;
        gc->wq_tail &= gc->wq_size - 1;
-       GEM_BUG_ON(wq_off & (wqi_size - 1));
+       gc->wq_rsvd -= wqi_size;
 
        /* WQ starts from the page after doorbell / process_desc */
        wq_page = (wq_off + GUC_DB_SIZE) >> PAGE_SHIFT;
        wq_off &= PAGE_SIZE - 1;
-       base = kmap_atomic(i915_gem_object_get_page(gc->client_obj, wq_page));
+       base = kmap_atomic(i915_gem_object_get_page(gc->vma->obj, wq_page));
        wqi = (struct guc_wq_item *)((char *)base + wq_off);
 
        /* Now fill in the 4-word work queue item */
        wqi->header = WQ_TYPE_INORDER |
                        (wqi_len << WQ_LEN_SHIFT) |
-                       (rq->engine->guc_id << WQ_TARGET_SHIFT) |
+                       (engine->guc_id << WQ_TARGET_SHIFT) |
                        WQ_NO_WCFLUSH_WAIT;
 
        /* The GuC wants only the low-order word of the context descriptor */
-       wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx,
-                                                            rq->engine);
+       wqi->context_desc = (u32)intel_lr_context_descriptor(rq->ctx, engine);
 
        wqi->ring_tail = tail << WQ_RING_TAIL_SHIFT;
-       wqi->fence_id = rq->seqno;
+       wqi->fence_id = rq->fence.seqno;
 
        kunmap_atomic(base);
 }
@@ -553,8 +568,8 @@ static int guc_ring_doorbell(struct i915_guc_client *gc)
                if (db_ret.db_status == GUC_DOORBELL_DISABLED)
                        break;
 
-               DRM_ERROR("Cookie mismatch. Expected %d, returned %d\n",
-                         db_cmp.cookie, db_ret.cookie);
+               DRM_WARN("Cookie mismatch. Expected %d, found %d\n",
+                        db_cmp.cookie, db_ret.cookie);
 
                /* update the cookie to newly read cookie from GuC */
                db_cmp.cookie = db_ret.cookie;
@@ -573,26 +588,26 @@ static int guc_ring_doorbell(struct i915_guc_client *gc)
  * Return:     0 on success, otherwise an errno.
  *             (Note: nonzero really shouldn't happen!)
  *
- * The caller must have already called i915_guc_wq_check_space() above
- * with a result of 0 (success) since the last request submission. This
- * guarantees that there is space in the work queue for the new request,
- * so enqueuing the item cannot fail.
+ * The caller must have already called i915_guc_wq_reserve() above with
+ * a result of 0 (success), guaranteeing that there is space in the work
+ * queue for the new request, so enqueuing the item cannot fail.
  *
  * Bad Things Will Happen if the caller violates this protocol e.g. calls
- * submit() when check() says there's no space, or calls submit() multiple
- * times with no intervening check().
+ * submit() when _reserve() says there's no space, or calls _submit()
+ * a different number of times from (successful) calls to _reserve().
  *
  * The only error here arises if the doorbell hardware isn't functioning
  * as expected, which really shouln't happen.
  */
-int i915_guc_submit(struct drm_i915_gem_request *rq)
+static void i915_guc_submit(struct drm_i915_gem_request *rq)
 {
        unsigned int engine_id = rq->engine->id;
        struct intel_guc *guc = &rq->i915->guc;
        struct i915_guc_client *client = guc->execbuf_client;
        int b_ret;
 
-       guc_add_workqueue_item(client, rq);
+       spin_lock(&client->wq_lock);
+       guc_wq_item_append(client, rq);
        b_ret = guc_ring_doorbell(client);
 
        client->submissions[engine_id] += 1;
@@ -601,9 +616,8 @@ int i915_guc_submit(struct drm_i915_gem_request *rq)
                client->b_fail += 1;
 
        guc->submissions[engine_id] += 1;
-       guc->last_seqno[engine_id] = rq->seqno;
-
-       return b_ret;
+       guc->last_seqno[engine_id] = rq->fence.seqno;
+       spin_unlock(&client->wq_lock);
 }
 
 /*
@@ -613,55 +627,48 @@ int i915_guc_submit(struct drm_i915_gem_request *rq)
  */
 
 /**
- * gem_allocate_guc_obj() - Allocate gem object for GuC usage
- * @dev_priv:  driver private data structure
- * @size:      size of object
+ * guc_allocate_vma() - Allocate a GGTT VMA for GuC usage
+ * @guc:       the guc
+ * @size:      size of area to allocate (both virtual space and memory)
  *
- * This is a wrapper to create a gem obj. In order to use it inside GuC, the
- * object needs to be pinned lifetime. Also we must pin it to gtt space other
- * than [0, GUC_WOPCM_TOP) because this range is reserved inside GuC.
+ * This is a wrapper to create an object for use with the GuC. In order to
+ * use it inside the GuC, an object needs to be pinned lifetime, so we allocate
+ * both some backing storage and a range inside the Global GTT. We must pin
+ * it in the GGTT somewhere other than than [0, GUC_WOPCM_TOP) because that
+ * range is reserved inside GuC.
  *
- * Return:     A drm_i915_gem_object if successful, otherwise NULL.
+ * Return:     A i915_vma if successful, otherwise an ERR_PTR.
  */
-static struct drm_i915_gem_object *
-gem_allocate_guc_obj(struct drm_i915_private *dev_priv, u32 size)
+static struct i915_vma *guc_allocate_vma(struct intel_guc *guc, u32 size)
 {
+       struct drm_i915_private *dev_priv = guc_to_i915(guc);
        struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
+       int ret;
 
        obj = i915_gem_object_create(&dev_priv->drm, size);
        if (IS_ERR(obj))
-               return NULL;
+               return ERR_CAST(obj);
 
-       if (i915_gem_object_get_pages(obj)) {
-               drm_gem_object_unreference(&obj->base);
-               return NULL;
-       }
+       vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL);
+       if (IS_ERR(vma))
+               goto err;
 
-       if (i915_gem_obj_ggtt_pin(obj, PAGE_SIZE,
-                       PIN_OFFSET_BIAS | GUC_WOPCM_TOP)) {
-               drm_gem_object_unreference(&obj->base);
-               return NULL;
+       ret = i915_vma_pin(vma, 0, PAGE_SIZE,
+                          PIN_GLOBAL | PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+       if (ret) {
+               vma = ERR_PTR(ret);
+               goto err;
        }
 
        /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */
        I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE);
 
-       return obj;
-}
+       return vma;
 
-/**
- * gem_release_guc_obj() - Release gem object allocated for GuC usage
- * @obj:       gem obj to be released
- */
-static void gem_release_guc_obj(struct drm_i915_gem_object *obj)
-{
-       if (!obj)
-               return;
-
-       if (i915_gem_obj_is_pinned(obj))
-               i915_gem_object_ggtt_unpin(obj);
-
-       drm_gem_object_unreference(&obj->base);
+err:
+       i915_gem_object_put(obj);
+       return vma;
 }
 
 static void
@@ -688,61 +695,74 @@ guc_client_free(struct drm_i915_private *dev_priv,
                kunmap(kmap_to_page(client->client_base));
        }
 
-       gem_release_guc_obj(client->client_obj);
+       i915_vma_unpin_and_release(&client->vma);
 
        if (client->ctx_index != GUC_INVALID_CTX_ID) {
-               guc_fini_ctx_desc(guc, client);
+               guc_ctx_desc_fini(guc, client);
                ida_simple_remove(&guc->ctx_ids, client->ctx_index);
        }
 
        kfree(client);
 }
 
+/* Check that a doorbell register is in the expected state */
+static bool guc_doorbell_check(struct intel_guc *guc, uint16_t db_id)
+{
+       struct drm_i915_private *dev_priv = guc_to_i915(guc);
+       i915_reg_t drbreg = GEN8_DRBREGL(db_id);
+       uint32_t value = I915_READ(drbreg);
+       bool enabled = (value & GUC_DOORBELL_ENABLED) != 0;
+       bool expected = test_bit(db_id, guc->doorbell_bitmap);
+
+       if (enabled == expected)
+               return true;
+
+       DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) 0x%x, should be %s\n",
+                        db_id, drbreg.reg, value,
+                        expected ? "active" : "inactive");
+
+       return false;
+}
+
 /*
- * Borrow the first client to set up & tear down every doorbell
+ * Borrow the first client to set up & tear down each unused doorbell
  * in turn, to ensure that all doorbell h/w is (re)initialised.
  */
 static void guc_init_doorbell_hw(struct intel_guc *guc)
 {
-       struct drm_i915_private *dev_priv = guc_to_i915(guc);
        struct i915_guc_client *client = guc->execbuf_client;
-       uint16_t db_id, i;
-       int err;
+       uint16_t db_id;
+       int i, err;
 
+       /* Save client's original doorbell selection */
        db_id = client->doorbell_id;
 
        for (i = 0; i < GUC_MAX_DOORBELLS; ++i) {
-               i915_reg_t drbreg = GEN8_DRBREGL(i);
-               u32 value = I915_READ(drbreg);
+               /* Skip if doorbell is OK */
+               if (guc_doorbell_check(guc, i))
+                       continue;
 
                err = guc_update_doorbell_id(guc, client, i);
-
-               /* Report update failure or unexpectedly active doorbell */
-               if (err || (i != db_id && (value & GUC_DOORBELL_ENABLED)))
-                       DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) was 0x%x, err %d\n",
-                                         i, drbreg.reg, value, err);
+               if (err)
+                       DRM_DEBUG_DRIVER("Doorbell %d update failed, err %d\n",
+                                       i, err);
        }
 
        /* Restore to original value */
        err = guc_update_doorbell_id(guc, client, db_id);
        if (err)
-               DRM_ERROR("Failed to restore doorbell to %d, err %d\n",
-                       db_id, err);
+               DRM_WARN("Failed to restore doorbell to %d, err %d\n",
+                        db_id, err);
 
-       for (i = 0; i < GUC_MAX_DOORBELLS; ++i) {
-               i915_reg_t drbreg = GEN8_DRBREGL(i);
-               u32 value = I915_READ(drbreg);
-
-               if (i != db_id && (value & GUC_DOORBELL_ENABLED))
-                       DRM_DEBUG_DRIVER("Doorbell %d (reg 0x%x) finally 0x%x\n",
-                                         i, drbreg.reg, value);
-
-       }
+       /* Read back & verify all doorbell registers */
+       for (i = 0; i < GUC_MAX_DOORBELLS; ++i)
+               (void)guc_doorbell_check(guc, i);
 }
 
 /**
  * guc_client_alloc() - Allocate an i915_guc_client
  * @dev_priv:  driver private data structure
+ * @engines:   The set of engines to enable for this client
  * @priority:  four levels priority _CRITICAL, _HIGH, _NORMAL and _LOW
  *             The kernel client to replace ExecList submission is created with
  *             NORMAL priority. Priority of a client for scheduler can be HIGH,
@@ -754,22 +774,24 @@ static void guc_init_doorbell_hw(struct intel_guc *guc)
  */
 static struct i915_guc_client *
 guc_client_alloc(struct drm_i915_private *dev_priv,
+                uint32_t engines,
                 uint32_t priority,
                 struct i915_gem_context *ctx)
 {
        struct i915_guc_client *client;
        struct intel_guc *guc = &dev_priv->guc;
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
        uint16_t db_id;
 
        client = kzalloc(sizeof(*client), GFP_KERNEL);
        if (!client)
                return NULL;
 
-       client->doorbell_id = GUC_INVALID_DOORBELL_ID;
-       client->priority = priority;
        client->owner = ctx;
        client->guc = guc;
+       client->engines = engines;
+       client->priority = priority;
+       client->doorbell_id = GUC_INVALID_DOORBELL_ID;
 
        client->ctx_index = (uint32_t)ida_simple_get(&guc->ctx_ids, 0,
                        GUC_MAX_GPU_CONTEXTS, GFP_KERNEL);
@@ -779,13 +801,15 @@ guc_client_alloc(struct drm_i915_private *dev_priv,
        }
 
        /* The first page is doorbell/proc_desc. Two followed pages are wq. */
-       obj = gem_allocate_guc_obj(dev_priv, GUC_DB_SIZE + GUC_WQ_SIZE);
-       if (!obj)
+       vma = guc_allocate_vma(guc, GUC_DB_SIZE + GUC_WQ_SIZE);
+       if (IS_ERR(vma))
                goto err;
 
        /* We'll keep just the first (doorbell/proc) page permanently kmap'd. */
-       client->client_obj = obj;
-       client->client_base = kmap(i915_gem_object_get_page(obj, 0));
+       client->vma = vma;
+       client->client_base = kmap(i915_vma_first_page(vma));
+
+       spin_lock_init(&client->wq_lock);
        client->wq_offset = GUC_DB_SIZE;
        client->wq_size = GUC_WQ_SIZE;
 
@@ -806,29 +830,26 @@ guc_client_alloc(struct drm_i915_private *dev_priv,
        else
                client->proc_desc_offset = (GUC_DB_SIZE / 2);
 
-       guc_init_proc_desc(guc, client);
-       guc_init_ctx_desc(guc, client);
+       guc_proc_desc_init(guc, client);
+       guc_ctx_desc_init(guc, client);
        if (guc_init_doorbell(guc, client, db_id))
                goto err;
 
-       DRM_DEBUG_DRIVER("new priority %u client %p: ctx_index %u\n",
-               priority, client, client->ctx_index);
+       DRM_DEBUG_DRIVER("new priority %u client %p for engine(s) 0x%x: ctx_index %u\n",
+               priority, client, client->engines, client->ctx_index);
        DRM_DEBUG_DRIVER("doorbell id %u, cacheline offset 0x%x\n",
                client->doorbell_id, client->doorbell_offset);
 
        return client;
 
 err:
-       DRM_ERROR("FAILED to create priority %u GuC client!\n", priority);
-
        guc_client_free(dev_priv, client);
        return NULL;
 }
 
-static void guc_create_log(struct intel_guc *guc)
+static void guc_log_create(struct intel_guc *guc)
 {
-       struct drm_i915_private *dev_priv = guc_to_i915(guc);
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
        unsigned long offset;
        uint32_t size, flags;
 
@@ -844,16 +865,16 @@ static void guc_create_log(struct intel_guc *guc)
                GUC_LOG_ISR_PAGES + 1 +
                GUC_LOG_CRASH_PAGES + 1) << PAGE_SHIFT;
 
-       obj = guc->log_obj;
-       if (!obj) {
-               obj = gem_allocate_guc_obj(dev_priv, size);
-               if (!obj) {
+       vma = guc->log_vma;
+       if (!vma) {
+               vma = guc_allocate_vma(guc, size);
+               if (IS_ERR(vma)) {
                        /* logging will be off */
                        i915.guc_log_level = -1;
                        return;
                }
 
-               guc->log_obj = obj;
+               guc->log_vma = vma;
        }
 
        /* each allocated unit is a page */
@@ -862,11 +883,11 @@ static void guc_create_log(struct intel_guc *guc)
                (GUC_LOG_ISR_PAGES << GUC_LOG_ISR_SHIFT) |
                (GUC_LOG_CRASH_PAGES << GUC_LOG_CRASH_SHIFT);
 
-       offset = i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT; /* in pages */
+       offset = i915_ggtt_offset(vma) >> PAGE_SHIFT; /* in pages */
        guc->log_flags = (offset << GUC_LOG_BUF_ADDR_SHIFT) | flags;
 }
 
-static void init_guc_policies(struct guc_policies *policies)
+static void guc_policies_init(struct guc_policies *policies)
 {
        struct guc_policy *policy;
        u32 p, i;
@@ -888,10 +909,10 @@ static void init_guc_policies(struct guc_policies *policies)
        policies->is_valid = 1;
 }
 
-static void guc_create_ads(struct intel_guc *guc)
+static void guc_addon_create(struct intel_guc *guc)
 {
        struct drm_i915_private *dev_priv = guc_to_i915(guc);
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
        struct guc_ads *ads;
        struct guc_policies *policies;
        struct guc_mmio_reg_state *reg_state;
@@ -904,16 +925,16 @@ static void guc_create_ads(struct intel_guc *guc)
                        sizeof(struct guc_mmio_reg_state) +
                        GUC_S3_SAVE_SPACE_PAGES * PAGE_SIZE;
 
-       obj = guc->ads_obj;
-       if (!obj) {
-               obj = gem_allocate_guc_obj(dev_priv, PAGE_ALIGN(size));
-               if (!obj)
+       vma = guc->ads_vma;
+       if (!vma) {
+               vma = guc_allocate_vma(guc, PAGE_ALIGN(size));
+               if (IS_ERR(vma))
                        return;
 
-               guc->ads_obj = obj;
+               guc->ads_vma = vma;
        }
 
-       page = i915_gem_object_get_page(obj, 0);
+       page = i915_vma_first_page(vma);
        ads = kmap(page);
 
        /*
@@ -924,17 +945,17 @@ static void guc_create_ads(struct intel_guc *guc)
         * to find it.
         */
        engine = &dev_priv->engine[RCS];
-       ads->golden_context_lrca = engine->status_page.gfx_addr;
+       ads->golden_context_lrca = engine->status_page.ggtt_offset;
 
        for_each_engine(engine, dev_priv)
                ads->eng_state_size[engine->guc_id] = intel_lr_context_size(engine);
 
        /* GuC scheduling policies */
        policies = (void *)ads + sizeof(struct guc_ads);
-       init_guc_policies(policies);
+       guc_policies_init(policies);
 
-       ads->scheduler_policies = i915_gem_obj_ggtt_offset(obj) +
-                       sizeof(struct guc_ads);
+       ads->scheduler_policies =
+               i915_ggtt_offset(vma) + sizeof(struct guc_ads);
 
        /* MMIO reg state */
        reg_state = (void *)policies + sizeof(struct guc_policies);
@@ -966,6 +987,7 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv)
        const size_t poolsize = GUC_MAX_GPU_CONTEXTS * ctxsize;
        const size_t gemsize = round_up(poolsize, PAGE_SIZE);
        struct intel_guc *guc = &dev_priv->guc;
+       struct i915_vma *vma;
 
        /* Wipe bitmap & delete client in case of reinitialisation */
        bitmap_clear(guc->doorbell_bitmap, 0, GUC_MAX_DOORBELLS);
@@ -974,16 +996,17 @@ int i915_guc_submission_init(struct drm_i915_private *dev_priv)
        if (!i915.enable_guc_submission)
                return 0; /* not enabled  */
 
-       if (guc->ctx_pool_obj)
+       if (guc->ctx_pool_vma)
                return 0; /* already allocated */
 
-       guc->ctx_pool_obj = gem_allocate_guc_obj(dev_priv, gemsize);
-       if (!guc->ctx_pool_obj)
-               return -ENOMEM;
+       vma = guc_allocate_vma(guc, gemsize);
+       if (IS_ERR(vma))
+               return PTR_ERR(vma);
 
+       guc->ctx_pool_vma = vma;
        ida_init(&guc->ctx_ids);
-       guc_create_log(guc);
-       guc_create_ads(guc);
+       guc_log_create(guc);
+       guc_addon_create(guc);
 
        return 0;
 }
@@ -992,13 +1015,16 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
 {
        struct intel_guc *guc = &dev_priv->guc;
        struct i915_guc_client *client;
+       struct intel_engine_cs *engine;
+       struct drm_i915_gem_request *request;
 
        /* client for execbuf submission */
        client = guc_client_alloc(dev_priv,
+                                 INTEL_INFO(dev_priv)->ring_mask,
                                  GUC_CTX_PRIORITY_KMD_NORMAL,
                                  dev_priv->kernel_context);
        if (!client) {
-               DRM_ERROR("Failed to create execbuf guc_client\n");
+               DRM_ERROR("Failed to create normal GuC client!\n");
                return -ENOMEM;
        }
 
@@ -1006,6 +1032,18 @@ int i915_guc_submission_enable(struct drm_i915_private *dev_priv)
        host2guc_sample_forcewake(guc, client);
        guc_init_doorbell_hw(guc);
 
+       /* Take over from manual control of ELSP (execlists) */
+       for_each_engine(engine, dev_priv) {
+               engine->submit_request = i915_guc_submit;
+
+               /* Replay the current set of previously submitted requests */
+               list_for_each_entry(request, &engine->request_list, link) {
+                       client->wq_rsvd += sizeof(struct guc_wq_item);
+                       if (i915_sw_fence_done(&request->submit))
+                               i915_guc_submit(request);
+               }
+       }
+
        return 0;
 }
 
@@ -1013,6 +1051,12 @@ void i915_guc_submission_disable(struct drm_i915_private *dev_priv)
 {
        struct intel_guc *guc = &dev_priv->guc;
 
+       if (!guc->execbuf_client)
+               return;
+
+       /* Revert back to manual ELSP submission */
+       intel_execlists_enable_submission(dev_priv);
+
        guc_client_free(dev_priv, guc->execbuf_client);
        guc->execbuf_client = NULL;
 }
@@ -1021,16 +1065,12 @@ void i915_guc_submission_fini(struct drm_i915_private *dev_priv)
 {
        struct intel_guc *guc = &dev_priv->guc;
 
-       gem_release_guc_obj(dev_priv->guc.ads_obj);
-       guc->ads_obj = NULL;
-
-       gem_release_guc_obj(dev_priv->guc.log_obj);
-       guc->log_obj = NULL;
+       i915_vma_unpin_and_release(&guc->ads_vma);
+       i915_vma_unpin_and_release(&guc->log_vma);
 
-       if (guc->ctx_pool_obj)
+       if (guc->ctx_pool_vma)
                ida_destroy(&guc->ctx_ids);
-       gem_release_guc_obj(guc->ctx_pool_obj);
-       guc->ctx_pool_obj = NULL;
+       i915_vma_unpin_and_release(&guc->ctx_pool_vma);
 }
 
 /**
@@ -1053,7 +1093,7 @@ int intel_guc_suspend(struct drm_device *dev)
        /* any value greater than GUC_POWER_D0 */
        data[1] = GUC_POWER_D1;
        /* first page is shared data with GuC */
-       data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state);
+       data[2] = i915_ggtt_offset(ctx->engine[RCS].state);
 
        return host2guc_action(guc, data, ARRAY_SIZE(data));
 }
@@ -1078,7 +1118,7 @@ int intel_guc_resume(struct drm_device *dev)
        data[0] = HOST2GUC_ACTION_EXIT_S_STATE;
        data[1] = GUC_POWER_D0;
        /* first page is shared data with GuC */
-       data[2] = i915_gem_obj_ggtt_offset(ctx->engine[RCS].state);
+       data[2] = i915_ggtt_offset(ctx->engine[RCS].state);
 
        return host2guc_action(guc, data, ARRAY_SIZE(data));
 }
index 1c2aec3..3fc286c 100644 (file)
@@ -350,6 +350,9 @@ void gen6_reset_rps_interrupts(struct drm_i915_private *dev_priv)
 
 void gen6_enable_rps_interrupts(struct drm_i915_private *dev_priv)
 {
+       if (READ_ONCE(dev_priv->rps.interrupts_enabled))
+               return;
+
        spin_lock_irq(&dev_priv->irq_lock);
        WARN_ON_ONCE(dev_priv->rps.pm_iir);
        WARN_ON_ONCE(I915_READ(gen6_pm_iir(dev_priv)) & dev_priv->pm_rps_events);
@@ -368,10 +371,13 @@ u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask)
 
 void gen6_disable_rps_interrupts(struct drm_i915_private *dev_priv)
 {
+       if (!READ_ONCE(dev_priv->rps.interrupts_enabled))
+               return;
+
        spin_lock_irq(&dev_priv->irq_lock);
        dev_priv->rps.interrupts_enabled = false;
 
-       I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0));
+       I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0u));
 
        __gen6_disable_pm_irq(dev_priv, dev_priv->pm_rps_events);
        I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) &
@@ -656,12 +662,6 @@ static void i915_enable_asle_pipestat(struct drm_i915_private *dev_priv)
  *   of horizontal active on the first line of vertical active
  */
 
-static u32 i8xx_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
-{
-       /* Gen2 doesn't have a hardware frame counter */
-       return 0;
-}
-
 /* Called from drm generic code, passed a 'crtc', which
  * we use as a pipe index
  */
@@ -978,10 +978,8 @@ static void ironlake_rps_change_irq_handler(struct drm_i915_private *dev_priv)
 static void notify_ring(struct intel_engine_cs *engine)
 {
        smp_store_mb(engine->breadcrumbs.irq_posted, true);
-       if (intel_engine_wakeup(engine)) {
+       if (intel_engine_wakeup(engine))
                trace_i915_gem_request_notify(engine);
-               engine->breadcrumbs.irq_wakeups++;
-       }
 }
 
 static void vlv_c0_read(struct drm_i915_private *dev_priv,
@@ -1105,9 +1103,10 @@ static void gen6_pm_rps_work(struct work_struct *work)
        new_delay = dev_priv->rps.cur_freq;
        min = dev_priv->rps.min_freq_softlimit;
        max = dev_priv->rps.max_freq_softlimit;
-
-       if (client_boost) {
-               new_delay = dev_priv->rps.max_freq_softlimit;
+       if (client_boost || any_waiters(dev_priv))
+               max = dev_priv->rps.max_freq;
+       if (client_boost && new_delay < dev_priv->rps.boost_freq) {
+               new_delay = dev_priv->rps.boost_freq;
                adj = 0;
        } else if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
                if (adj > 0)
@@ -1122,7 +1121,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
                        new_delay = dev_priv->rps.efficient_freq;
                        adj = 0;
                }
-       } else if (any_waiters(dev_priv)) {
+       } else if (client_boost || any_waiters(dev_priv)) {
                adj = 0;
        } else if (pm_iir & GEN6_PM_RP_DOWN_TIMEOUT) {
                if (dev_priv->rps.cur_freq > dev_priv->rps.efficient_freq)
@@ -2504,57 +2503,52 @@ static void i915_reset_and_wakeup(struct drm_i915_private *dev_priv)
        char *error_event[] = { I915_ERROR_UEVENT "=1", NULL };
        char *reset_event[] = { I915_RESET_UEVENT "=1", NULL };
        char *reset_done_event[] = { I915_ERROR_UEVENT "=0", NULL };
-       int ret;
 
        kobject_uevent_env(kobj, KOBJ_CHANGE, error_event);
 
+       DRM_DEBUG_DRIVER("resetting chip\n");
+       kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
+
        /*
-        * Note that there's only one work item which does gpu resets, so we
-        * need not worry about concurrent gpu resets potentially incrementing
-        * error->reset_counter twice. We only need to take care of another
-        * racing irq/hangcheck declaring the gpu dead for a second time. A
-        * quick check for that is good enough: schedule_work ensures the
-        * correct ordering between hang detection and this work item, and since
-        * the reset in-progress bit is only ever set by code outside of this
-        * work we don't need to worry about any other races.
+        * In most cases it's guaranteed that we get here with an RPM
+        * reference held, for example because there is a pending GPU
+        * request that won't finish until the reset is done. This
+        * isn't the case at least when we get here by doing a
+        * simulated reset via debugs, so get an RPM reference.
         */
-       if (i915_reset_in_progress(&dev_priv->gpu_error)) {
-               DRM_DEBUG_DRIVER("resetting chip\n");
-               kobject_uevent_env(kobj, KOBJ_CHANGE, reset_event);
-
-               /*
-                * In most cases it's guaranteed that we get here with an RPM
-                * reference held, for example because there is a pending GPU
-                * request that won't finish until the reset is done. This
-                * isn't the case at least when we get here by doing a
-                * simulated reset via debugs, so get an RPM reference.
-                */
-               intel_runtime_pm_get(dev_priv);
-
-               intel_prepare_reset(dev_priv);
+       intel_runtime_pm_get(dev_priv);
+       intel_prepare_reset(dev_priv);
 
+       do {
                /*
                 * All state reset _must_ be completed before we update the
                 * reset counter, for otherwise waiters might miss the reset
                 * pending state and not properly drop locks, resulting in
                 * deadlocks with the reset work.
                 */
-               ret = i915_reset(dev_priv);
+               if (mutex_trylock(&dev_priv->drm.struct_mutex)) {
+                       i915_reset(dev_priv);
+                       mutex_unlock(&dev_priv->drm.struct_mutex);
+               }
 
-               intel_finish_reset(dev_priv);
+               /* We need to wait for anyone holding the lock to wakeup */
+       } while (wait_on_bit_timeout(&dev_priv->gpu_error.flags,
+                                    I915_RESET_IN_PROGRESS,
+                                    TASK_UNINTERRUPTIBLE,
+                                    HZ));
 
-               intel_runtime_pm_put(dev_priv);
+       intel_finish_reset(dev_priv);
+       intel_runtime_pm_put(dev_priv);
 
-               if (ret == 0)
-                       kobject_uevent_env(kobj,
-                                          KOBJ_CHANGE, reset_done_event);
+       if (!test_bit(I915_WEDGED, &dev_priv->gpu_error.flags))
+               kobject_uevent_env(kobj,
+                                  KOBJ_CHANGE, reset_done_event);
 
-               /*
-                * Note: The wake_up also serves as a memory barrier so that
-                * waiters see the update value of the reset counter atomic_t.
-                */
-               wake_up_all(&dev_priv->gpu_error.reset_queue);
-       }
+       /*
+        * Note: The wake_up also serves as a memory barrier so that
+        * waiters see the updated value of the dev_priv->gpu_error.
+        */
+       wake_up_all(&dev_priv->gpu_error.reset_queue);
 }
 
 static void i915_report_and_clear_eir(struct drm_i915_private *dev_priv)
@@ -2673,25 +2667,26 @@ void i915_handle_error(struct drm_i915_private *dev_priv,
        i915_capture_error_state(dev_priv, engine_mask, error_msg);
        i915_report_and_clear_eir(dev_priv);
 
-       if (engine_mask) {
-               atomic_or(I915_RESET_IN_PROGRESS_FLAG,
-                               &dev_priv->gpu_error.reset_counter);
+       if (!engine_mask)
+               return;
 
-               /*
-                * Wakeup waiting processes so that the reset function
-                * i915_reset_and_wakeup doesn't deadlock trying to grab
-                * various locks. By bumping the reset counter first, the woken
-                * processes will see a reset in progress and back off,
-                * releasing their locks and then wait for the reset completion.
-                * We must do this for _all_ gpu waiters that might hold locks
-                * that the reset work needs to acquire.
-                *
-                * Note: The wake_up serves as the required memory barrier to
-                * ensure that the waiters see the updated value of the reset
-                * counter atomic_t.
-                */
-               i915_error_wake_up(dev_priv);
-       }
+       if (test_and_set_bit(I915_RESET_IN_PROGRESS,
+                            &dev_priv->gpu_error.flags))
+               return;
+
+       /*
+        * Wakeup waiting processes so that the reset function
+        * i915_reset_and_wakeup doesn't deadlock trying to grab
+        * various locks. By bumping the reset counter first, the woken
+        * processes will see a reset in progress and back off,
+        * releasing their locks and then wait for the reset completion.
+        * We must do this for _all_ gpu waiters that might hold locks
+        * that the reset work needs to acquire.
+        *
+        * Note: The wake_up also provides a memory barrier to ensure that the
+        * waiters see the updated value of the reset flags.
+        */
+       i915_error_wake_up(dev_priv);
 
        i915_reset_and_wakeup(dev_priv);
 }
@@ -2803,13 +2798,6 @@ static void gen8_disable_vblank(struct drm_device *dev, unsigned int pipe)
        spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
-static bool
-ring_idle(struct intel_engine_cs *engine, u32 seqno)
-{
-       return i915_seqno_passed(seqno,
-                                READ_ONCE(engine->last_submitted_seqno));
-}
-
 static bool
 ipehr_is_semaphore_wait(struct intel_engine_cs *engine, u32 ipehr)
 {
@@ -2834,7 +2822,7 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *engine, u32 ipehr,
                        if (engine == signaller)
                                continue;
 
-                       if (offset == signaller->semaphore.signal_ggtt[engine->id])
+                       if (offset == signaller->semaphore.signal_ggtt[engine->hw_id])
                                return signaller;
                }
        } else {
@@ -2844,21 +2832,22 @@ semaphore_wait_to_signaller_ring(struct intel_engine_cs *engine, u32 ipehr,
                        if(engine == signaller)
                                continue;
 
-                       if (sync_bits == signaller->semaphore.mbox.wait[engine->id])
+                       if (sync_bits == signaller->semaphore.mbox.wait[engine->hw_id])
                                return signaller;
                }
        }
 
-       DRM_ERROR("No signaller ring found for ring %i, ipehr 0x%08x, offset 0x%016llx\n",
-                 engine->id, ipehr, offset);
+       DRM_DEBUG_DRIVER("No signaller ring found for %s, ipehr 0x%08x, offset 0x%016llx\n",
+                        engine->name, ipehr, offset);
 
-       return NULL;
+       return ERR_PTR(-ENODEV);
 }
 
 static struct intel_engine_cs *
 semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
 {
        struct drm_i915_private *dev_priv = engine->i915;
+       void __iomem *vaddr;
        u32 cmd, ipehr, head;
        u64 offset = 0;
        int i, backwards;
@@ -2897,6 +2886,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
         */
        head = I915_READ_HEAD(engine) & HEAD_ADDR;
        backwards = (INTEL_GEN(dev_priv) >= 8) ? 5 : 4;
+       vaddr = (void __iomem *)engine->buffer->vaddr;
 
        for (i = backwards; i; --i) {
                /*
@@ -2907,7 +2897,7 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
                head &= engine->buffer->size - 1;
 
                /* This here seems to blow up */
-               cmd = ioread32(engine->buffer->virtual_start + head);
+               cmd = ioread32(vaddr + head);
                if (cmd == ipehr)
                        break;
 
@@ -2917,11 +2907,11 @@ semaphore_waits_for(struct intel_engine_cs *engine, u32 *seqno)
        if (!i)
                return NULL;
 
-       *seqno = ioread32(engine->buffer->virtual_start + head + 4) + 1;
+       *seqno = ioread32(vaddr + head + 4) + 1;
        if (INTEL_GEN(dev_priv) >= 8) {
-               offset = ioread32(engine->buffer->virtual_start + head + 12);
+               offset = ioread32(vaddr + head + 12);
                offset <<= 32;
-               offset = ioread32(engine->buffer->virtual_start + head + 8);
+               offset |= ioread32(vaddr + head + 8);
        }
        return semaphore_wait_to_signaller_ring(engine, ipehr, offset);
 }
@@ -2938,6 +2928,9 @@ static int semaphore_passed(struct intel_engine_cs *engine)
        if (signaller == NULL)
                return -1;
 
+       if (IS_ERR(signaller))
+               return 0;
+
        /* Prevent pathological recursion due to driver bugs */
        if (signaller->hangcheck.deadlock >= I915_NUM_ENGINES)
                return -1;
@@ -2990,7 +2983,7 @@ static bool subunits_stuck(struct intel_engine_cs *engine)
        return stuck;
 }
 
-static enum intel_ring_hangcheck_action
+static enum intel_engine_hangcheck_action
 head_stuck(struct intel_engine_cs *engine, u64 acthd)
 {
        if (acthd != engine->hangcheck.acthd) {
@@ -3008,11 +3001,11 @@ head_stuck(struct intel_engine_cs *engine, u64 acthd)
        return HANGCHECK_HUNG;
 }
 
-static enum intel_ring_hangcheck_action
-ring_stuck(struct intel_engine_cs *engine, u64 acthd)
+static enum intel_engine_hangcheck_action
+engine_stuck(struct intel_engine_cs *engine, u64 acthd)
 {
        struct drm_i915_private *dev_priv = engine->i915;
-       enum intel_ring_hangcheck_action ha;
+       enum intel_engine_hangcheck_action ha;
        u32 tmp;
 
        ha = head_stuck(engine, acthd);
@@ -3054,22 +3047,6 @@ ring_stuck(struct intel_engine_cs *engine, u64 acthd)
        return HANGCHECK_HUNG;
 }
 
-static unsigned long kick_waiters(struct intel_engine_cs *engine)
-{
-       struct drm_i915_private *i915 = engine->i915;
-       unsigned long irq_count = READ_ONCE(engine->breadcrumbs.irq_wakeups);
-
-       if (engine->hangcheck.user_interrupts == irq_count &&
-           !test_and_set_bit(engine->id, &i915->gpu_error.missed_irq_rings)) {
-               if (!test_bit(engine->id, &i915->gpu_error.test_irq_rings))
-                       DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
-                                 engine->name);
-
-               intel_engine_enable_fake_irq(engine);
-       }
-
-       return irq_count;
-}
 /*
  * This is called when the chip hasn't reported back with completed
  * batchbuffers in a long time. We keep track per ring seqno progress and
@@ -3107,7 +3084,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                bool busy = intel_engine_has_waiter(engine);
                u64 acthd;
                u32 seqno;
-               unsigned user_interrupts;
+               u32 submit;
 
                semaphore_clear_deadlocks(dev_priv);
 
@@ -3121,29 +3098,22 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                if (engine->irq_seqno_barrier)
                        engine->irq_seqno_barrier(engine);
 
-               acthd = intel_ring_get_active_head(engine);
+               acthd = intel_engine_get_active_head(engine);
                seqno = intel_engine_get_seqno(engine);
-
-               /* Reset stuck interrupts between batch advances */
-               user_interrupts = 0;
+               submit = READ_ONCE(engine->last_submitted_seqno);
 
                if (engine->hangcheck.seqno == seqno) {
-                       if (ring_idle(engine, seqno)) {
+                       if (i915_seqno_passed(seqno, submit)) {
                                engine->hangcheck.action = HANGCHECK_IDLE;
-                               if (busy) {
-                                       /* Safeguard against driver failure */
-                                       user_interrupts = kick_waiters(engine);
-                                       engine->hangcheck.score += BUSY;
-                               }
                        } else {
                                /* We always increment the hangcheck score
-                                * if the ring is busy and still processing
+                                * if the engine is busy and still processing
                                 * the same request, so that no single request
                                 * can run indefinitely (such as a chain of
                                 * batches). The only time we do not increment
                                 * the hangcheck score on this ring, if this
-                                * ring is in a legitimate wait for another
-                                * ring. In that case the waiting ring is a
+                                * engine is in a legitimate wait for another
+                                * engine. In that case the waiting engine is a
                                 * victim and we want to be sure we catch the
                                 * right culprit. Then every time we do kick
                                 * the ring, add a small increment to the
@@ -3151,8 +3121,8 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                                 * being repeatedly kicked and so responsible
                                 * for stalling the machine.
                                 */
-                               engine->hangcheck.action = ring_stuck(engine,
-                                                                     acthd);
+                               engine->hangcheck.action =
+                                       engine_stuck(engine, acthd);
 
                                switch (engine->hangcheck.action) {
                                case HANGCHECK_IDLE:
@@ -3195,12 +3165,12 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
 
                engine->hangcheck.seqno = seqno;
                engine->hangcheck.acthd = acthd;
-               engine->hangcheck.user_interrupts = user_interrupts;
                busy_count += busy;
        }
 
        if (hung) {
                char msg[80];
+               unsigned int tmp;
                int len;
 
                /* If some rings hung but others were still busy, only
@@ -3210,7 +3180,7 @@ static void i915_hangcheck_elapsed(struct work_struct *work)
                        hung &= ~stuck;
                len = scnprintf(msg, sizeof(msg),
                                "%s on ", stuck == hung ? "No progress" : "Hang");
-               for_each_engine_masked(engine, dev_priv, hung)
+               for_each_engine_masked(engine, dev_priv, hung, tmp)
                        len += scnprintf(msg + len, sizeof(msg) - len,
                                         "%s, ", engine->name);
                msg[len-2] = '\0';
@@ -4536,14 +4506,15 @@ void intel_irq_init(struct drm_i915_private *dev_priv)
                dev_priv->rps.pm_intr_keep |= GEN6_PM_RP_UP_EI_EXPIRED;
 
        if (INTEL_INFO(dev_priv)->gen >= 8)
-               dev_priv->rps.pm_intr_keep |= GEN8_PMINTR_REDIRECT_TO_NON_DISP;
+               dev_priv->rps.pm_intr_keep |= GEN8_PMINTR_REDIRECT_TO_GUC;
 
        INIT_DELAYED_WORK(&dev_priv->gpu_error.hangcheck_work,
                          i915_hangcheck_elapsed);
 
        if (IS_GEN2(dev_priv)) {
+               /* Gen2 doesn't have a hardware frame counter */
                dev->max_vblank_count = 0;
-               dev->driver->get_vblank_counter = i8xx_get_vblank_counter;
+               dev->driver->get_vblank_counter = drm_vblank_no_hw_counter;
        } else if (IS_G4X(dev_priv) || INTEL_INFO(dev_priv)->gen >= 5) {
                dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
                dev->driver->get_vblank_counter = g4x_get_vblank_counter;
diff --git a/drivers/gpu/drm/i915/i915_memcpy.c b/drivers/gpu/drm/i915/i915_memcpy.c
new file mode 100644 (file)
index 0000000..49a0794
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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/kernel.h>
+#include <asm/fpu/api.h>
+
+#include "i915_drv.h"
+
+static DEFINE_STATIC_KEY_FALSE(has_movntdqa);
+
+#ifdef CONFIG_AS_MOVNTDQA
+static void __memcpy_ntdqa(void *dst, const void *src, unsigned long len)
+{
+       kernel_fpu_begin();
+
+       len >>= 4;
+       while (len >= 4) {
+               asm("movntdqa   (%0), %%xmm0\n"
+                   "movntdqa 16(%0), %%xmm1\n"
+                   "movntdqa 32(%0), %%xmm2\n"
+                   "movntdqa 48(%0), %%xmm3\n"
+                   "movaps %%xmm0,   (%1)\n"
+                   "movaps %%xmm1, 16(%1)\n"
+                   "movaps %%xmm2, 32(%1)\n"
+                   "movaps %%xmm3, 48(%1)\n"
+                   :: "r" (src), "r" (dst) : "memory");
+               src += 64;
+               dst += 64;
+               len -= 4;
+       }
+       while (len--) {
+               asm("movntdqa (%0), %%xmm0\n"
+                   "movaps %%xmm0, (%1)\n"
+                   :: "r" (src), "r" (dst) : "memory");
+               src += 16;
+               dst += 16;
+       }
+
+       kernel_fpu_end();
+}
+#endif
+
+/**
+ * i915_memcpy_from_wc: perform an accelerated *aligned* read from WC
+ * @dst: destination pointer
+ * @src: source pointer
+ * @len: how many bytes to copy
+ *
+ * i915_memcpy_from_wc copies @len bytes from @src to @dst using
+ * non-temporal instructions where available. Note that all arguments
+ * (@src, @dst) must be aligned to 16 bytes and @len must be a multiple
+ * of 16.
+ *
+ * To test whether accelerated reads from WC are supported, use
+ * i915_memcpy_from_wc(NULL, NULL, 0);
+ *
+ * Returns true if the copy was successful, false if the preconditions
+ * are not met.
+ */
+bool i915_memcpy_from_wc(void *dst, const void *src, unsigned long len)
+{
+       if (unlikely(((unsigned long)dst | (unsigned long)src | len) & 15))
+               return false;
+
+#ifdef CONFIG_AS_MOVNTDQA
+       if (static_branch_likely(&has_movntdqa)) {
+               if (likely(len))
+                       __memcpy_ntdqa(dst, src, len);
+               return true;
+       }
+#endif
+
+       return false;
+}
+
+void i915_memcpy_init_early(struct drm_i915_private *dev_priv)
+{
+       if (static_cpu_has(X86_FEATURE_XMM4_1))
+               static_branch_enable(&has_movntdqa);
+}
diff --git a/drivers/gpu/drm/i915/i915_mm.c b/drivers/gpu/drm/i915/i915_mm.c
new file mode 100644 (file)
index 0000000..e4935dd
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2014 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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/mm.h>
+#include <linux/io-mapping.h>
+
+#include <asm/pgtable.h>
+
+#include "i915_drv.h"
+
+struct remap_pfn {
+       struct mm_struct *mm;
+       unsigned long pfn;
+       pgprot_t prot;
+};
+
+static int remap_pfn(pte_t *pte, pgtable_t token,
+                    unsigned long addr, void *data)
+{
+       struct remap_pfn *r = data;
+
+       /* Special PTE are not associated with any struct page */
+       set_pte_at(r->mm, addr, pte, pte_mkspecial(pfn_pte(r->pfn, r->prot)));
+       r->pfn++;
+
+       return 0;
+}
+
+/**
+ * remap_io_mapping - remap an IO mapping to userspace
+ * @vma: user vma to map to
+ * @addr: target user address to start at
+ * @pfn: physical address of kernel memory
+ * @size: size of map area
+ * @iomap: the source io_mapping
+ *
+ *  Note: this is only safe if the mm semaphore is held when called.
+ */
+int remap_io_mapping(struct vm_area_struct *vma,
+                    unsigned long addr, unsigned long pfn, unsigned long size,
+                    struct io_mapping *iomap)
+{
+       struct remap_pfn r;
+       int err;
+
+       GEM_BUG_ON((vma->vm_flags &
+                   (VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP)) !=
+                  (VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP));
+
+       /* We rely on prevalidation of the io-mapping to skip track_pfn(). */
+       r.mm = vma->vm_mm;
+       r.pfn = pfn;
+       r.prot = __pgprot((pgprot_val(iomap->prot) & _PAGE_CACHE_MASK) |
+                         (pgprot_val(vma->vm_page_prot) & ~_PAGE_CACHE_MASK));
+
+       err = apply_to_page_range(r.mm, addr, size, remap_pfn, &r);
+       if (unlikely(err)) {
+               zap_vma_ptes(vma, addr, (r.pfn - pfn) << PAGE_SHIFT);
+               return err;
+       }
+
+       return 0;
+}
index b6e404c..768ad89 100644 (file)
@@ -45,6 +45,7 @@ struct i915_params i915 __read_mostly = {
        .fastboot = 0,
        .prefault_disable = 0,
        .load_detect_test = 0,
+       .force_reset_modeset_test = 0,
        .reset = true,
        .invert_brightness = 0,
        .disable_display = 0,
@@ -161,6 +162,11 @@ MODULE_PARM_DESC(load_detect_test,
        "Force-enable the VGA load detect code for testing (default:false). "
        "For developers only.");
 
+module_param_named_unsafe(force_reset_modeset_test, i915.force_reset_modeset_test, bool, 0600);
+MODULE_PARM_DESC(force_reset_modeset_test,
+       "Force a modeset during gpu reset for testing (default:false). "
+       "For developers only.");
+
 module_param_named_unsafe(invert_brightness, i915.invert_brightness, int, 0600);
 MODULE_PARM_DESC(invert_brightness,
        "Invert backlight brightness "
index 0ad020b..3a0dd78 100644 (file)
@@ -57,6 +57,7 @@ struct i915_params {
        bool fastboot;
        bool prefault_disable;
        bool load_detect_test;
+       bool force_reset_modeset_test;
        bool reset;
        bool disable_display;
        bool verbose_state_checks;
index 949c016..687c768 100644 (file)
 #define CHV_COLORS \
        .color = { .degamma_lut_size = 65, .gamma_lut_size = 257 }
 
+#define GEN2_FEATURES \
+       .gen = 2, .num_pipes = 1, \
+       .has_overlay = 1, .overlay_needs_physical = 1, \
+       .has_gmch_display = 1, \
+       .hws_needs_physical = 1, \
+       .ring_mask = RENDER_RING, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_i830_info = {
-       .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
-       .has_overlay = 1, .overlay_needs_physical = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN2_FEATURES,
+       .is_mobile = 1, .cursor_needs_physical = 1,
+       .num_pipes = 2, /* legal, last one wins */
 };
 
 static const struct intel_device_info intel_845g_info = {
-       .gen = 2, .num_pipes = 1,
-       .has_overlay = 1, .overlay_needs_physical = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN2_FEATURES,
 };
 
 static const struct intel_device_info intel_i85x_info = {
-       .gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2,
+       GEN2_FEATURES,
+       .is_i85x = 1, .is_mobile = 1,
+       .num_pipes = 2, /* legal, last one wins */
        .cursor_needs_physical = 1,
-       .has_overlay = 1, .overlay_needs_physical = 1,
        .has_fbc = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_i865g_info = {
-       .gen = 2, .num_pipes = 1,
-       .has_overlay = 1, .overlay_needs_physical = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN2_FEATURES,
 };
 
+#define GEN3_FEATURES \
+       .gen = 3, .num_pipes = 2, \
+       .has_gmch_display = 1, \
+       .ring_mask = RENDER_RING, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_i915g_info = {
-       .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2,
+       GEN3_FEATURES,
+       .is_i915g = 1, .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 static const struct intel_device_info intel_i915gm_info = {
-       .gen = 3, .is_mobile = 1, .num_pipes = 2,
+       GEN3_FEATURES,
+       .is_mobile = 1,
        .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .supports_tv = 1,
        .has_fbc = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 static const struct intel_device_info intel_i945g_info = {
-       .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
+       GEN3_FEATURES,
+       .has_hotplug = 1, .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 static const struct intel_device_info intel_i945gm_info = {
-       .gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
+       GEN3_FEATURES,
+       .is_i945gm = 1, .is_mobile = 1,
        .has_hotplug = 1, .cursor_needs_physical = 1,
        .has_overlay = 1, .overlay_needs_physical = 1,
        .supports_tv = 1,
        .has_fbc = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 
+#define GEN4_FEATURES \
+       .gen = 4, .num_pipes = 2, \
+       .has_hotplug = 1, \
+       .has_gmch_display = 1, \
+       .ring_mask = RENDER_RING, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_i965g_info = {
-       .gen = 4, .is_broadwater = 1, .num_pipes = 2,
-       .has_hotplug = 1,
+       GEN4_FEATURES,
+       .is_broadwater = 1,
        .has_overlay = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 
 static const struct intel_device_info intel_i965gm_info = {
-       .gen = 4, .is_crestline = 1, .num_pipes = 2,
-       .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1,
+       GEN4_FEATURES,
+       .is_crestline = 1,
+       .is_mobile = 1, .has_fbc = 1,
        .has_overlay = 1,
        .supports_tv = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       .hws_needs_physical = 1,
 };
 
 static const struct intel_device_info intel_g33_info = {
-       .gen = 3, .is_g33 = 1, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       GEN3_FEATURES,
+       .is_g33 = 1,
+       .has_hotplug = 1,
        .has_overlay = 1,
-       .ring_mask = RENDER_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_g45_info = {
-       .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2,
-       .has_pipe_cxsr = 1, .has_hotplug = 1,
+       GEN4_FEATURES,
+       .is_g4x = 1,
+       .has_pipe_cxsr = 1,
        .ring_mask = RENDER_RING | BSD_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_gm45_info = {
-       .gen = 4, .is_g4x = 1, .num_pipes = 2,
-       .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1,
-       .has_pipe_cxsr = 1, .has_hotplug = 1,
+       GEN4_FEATURES,
+       .is_g4x = 1,
+       .is_mobile = 1, .has_fbc = 1,
+       .has_pipe_cxsr = 1,
        .supports_tv = 1,
        .ring_mask = RENDER_RING | BSD_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
 static const struct intel_device_info intel_pineview_info = {
-       .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       GEN3_FEATURES,
+       .is_g33 = 1, .is_pineview = 1, .is_mobile = 1,
+       .has_hotplug = 1,
        .has_overlay = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
 };
 
+#define GEN5_FEATURES \
+       .gen = 5, .num_pipes = 2, \
+       .has_hotplug = 1, \
+       .has_gmbus_irq = 1, \
+       .ring_mask = RENDER_RING | BSD_RING, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_ironlake_d_info = {
-       .gen = 5, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .ring_mask = RENDER_RING | BSD_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN5_FEATURES,
 };
 
 static const struct intel_device_info intel_ironlake_m_info = {
-       .gen = 5, .is_mobile = 1, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .has_fbc = 1,
-       .ring_mask = RENDER_RING | BSD_RING,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN5_FEATURES,
+       .is_mobile = 1,
 };
 
+#define GEN6_FEATURES \
+       .gen = 6, .num_pipes = 2, \
+       .has_hotplug = 1, \
+       .has_fbc = 1, \
+       .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
+       .has_llc = 1, \
+       .has_rc6 = 1, \
+       .has_rc6p = 1, \
+       .has_gmbus_irq = 1, \
+       .has_hw_contexts = 1, \
+       GEN_DEFAULT_PIPEOFFSETS, \
+       CURSOR_OFFSETS
+
 static const struct intel_device_info intel_sandybridge_d_info = {
-       .gen = 6, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .has_fbc = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
-       .has_llc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN6_FEATURES,
 };
 
 static const struct intel_device_info intel_sandybridge_m_info = {
-       .gen = 6, .is_mobile = 1, .num_pipes = 2,
-       .need_gfx_hws = 1, .has_hotplug = 1,
-       .has_fbc = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING,
-       .has_llc = 1,
-       GEN_DEFAULT_PIPEOFFSETS,
-       CURSOR_OFFSETS,
+       GEN6_FEATURES,
+       .is_mobile = 1,
 };
 
 #define GEN7_FEATURES  \
        .gen = 7, .num_pipes = 3, \
-       .need_gfx_hws = 1, .has_hotplug = 1, \
+       .has_hotplug = 1, \
        .has_fbc = 1, \
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
        .has_llc = 1, \
+       .has_rc6 = 1, \
+       .has_rc6p = 1, \
+       .has_gmbus_irq = 1, \
+       .has_hw_contexts = 1, \
        GEN_DEFAULT_PIPEOFFSETS, \
        IVB_CURSOR_OFFSETS
 
 static const struct intel_device_info intel_ivybridge_d_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
+       .has_l3_dpf = 1,
 };
 
 static const struct intel_device_info intel_ivybridge_m_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        .is_mobile = 1,
+       .has_l3_dpf = 1,
 };
 
 static const struct intel_device_info intel_ivybridge_q_info = {
        GEN7_FEATURES,
        .is_ivybridge = 1,
        .num_pipes = 0, /* legal, last one wins */
+       .has_l3_dpf = 1,
 };
 
 #define VLV_FEATURES  \
        .gen = 7, .num_pipes = 2, \
-       .need_gfx_hws = 1, .has_hotplug = 1, \
+       .has_psr = 1, \
+       .has_runtime_pm = 1, \
+       .has_rc6 = 1, \
+       .has_gmbus_irq = 1, \
+       .has_hw_contexts = 1, \
+       .has_gmch_display = 1, \
+       .has_hotplug = 1, \
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
        .display_mmio_offset = VLV_DISPLAY_BASE, \
        GEN_DEFAULT_PIPEOFFSETS, \
        CURSOR_OFFSETS
 
-static const struct intel_device_info intel_valleyview_m_info = {
-       VLV_FEATURES,
-       .is_valleyview = 1,
-       .is_mobile = 1,
-};
-
-static const struct intel_device_info intel_valleyview_d_info = {
+static const struct intel_device_info intel_valleyview_info = {
        VLV_FEATURES,
        .is_valleyview = 1,
 };
@@ -263,54 +272,50 @@ static const struct intel_device_info intel_valleyview_d_info = {
        GEN7_FEATURES, \
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING, \
        .has_ddi = 1, \
-       .has_fpga_dbg = 1
-
-static const struct intel_device_info intel_haswell_d_info = {
-       HSW_FEATURES,
-       .is_haswell = 1,
-};
-
-static const struct intel_device_info intel_haswell_m_info = {
+       .has_fpga_dbg = 1, \
+       .has_psr = 1, \
+       .has_resource_streamer = 1, \
+       .has_dp_mst = 1, \
+       .has_rc6p = 0 /* RC6p removed-by HSW */, \
+       .has_runtime_pm = 1
+
+static const struct intel_device_info intel_haswell_info = {
        HSW_FEATURES,
        .is_haswell = 1,
-       .is_mobile = 1,
+       .has_l3_dpf = 1,
 };
 
 #define BDW_FEATURES \
        HSW_FEATURES, \
-       BDW_COLORS
+       BDW_COLORS, \
+       .has_logical_ring_contexts = 1
 
-static const struct intel_device_info intel_broadwell_d_info = {
+static const struct intel_device_info intel_broadwell_info = {
        BDW_FEATURES,
        .gen = 8,
        .is_broadwell = 1,
 };
 
-static const struct intel_device_info intel_broadwell_m_info = {
-       BDW_FEATURES,
-       .gen = 8, .is_mobile = 1,
-       .is_broadwell = 1,
-};
-
-static const struct intel_device_info intel_broadwell_gt3d_info = {
+static const struct intel_device_info intel_broadwell_gt3_info = {
        BDW_FEATURES,
        .gen = 8,
        .is_broadwell = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
 };
 
-static const struct intel_device_info intel_broadwell_gt3m_info = {
-       BDW_FEATURES,
-       .gen = 8, .is_mobile = 1,
-       .is_broadwell = 1,
-       .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-};
-
 static const struct intel_device_info intel_cherryview_info = {
        .gen = 8, .num_pipes = 3,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .is_cherryview = 1,
+       .has_psr = 1,
+       .has_runtime_pm = 1,
+       .has_resource_streamer = 1,
+       .has_rc6 = 1,
+       .has_gmbus_irq = 1,
+       .has_hw_contexts = 1,
+       .has_logical_ring_contexts = 1,
+       .has_gmch_display = 1,
        .display_mmio_offset = VLV_DISPLAY_BASE,
        GEN_CHV_PIPEOFFSETS,
        CURSOR_OFFSETS,
@@ -321,25 +326,41 @@ static const struct intel_device_info intel_skylake_info = {
        BDW_FEATURES,
        .is_skylake = 1,
        .gen = 9,
+       .has_csr = 1,
+       .has_guc = 1,
+       .ddb_size = 896,
 };
 
 static const struct intel_device_info intel_skylake_gt3_info = {
        BDW_FEATURES,
        .is_skylake = 1,
        .gen = 9,
+       .has_csr = 1,
+       .has_guc = 1,
+       .ddb_size = 896,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
 };
 
 static const struct intel_device_info intel_broxton_info = {
        .is_broxton = 1,
        .gen = 9,
-       .need_gfx_hws = 1, .has_hotplug = 1,
+       .has_hotplug = 1,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
        .num_pipes = 3,
        .has_ddi = 1,
        .has_fpga_dbg = 1,
        .has_fbc = 1,
+       .has_runtime_pm = 1,
        .has_pooled_eu = 0,
+       .has_csr = 1,
+       .has_resource_streamer = 1,
+       .has_rc6 = 1,
+       .has_dp_mst = 1,
+       .has_gmbus_irq = 1,
+       .has_hw_contexts = 1,
+       .has_logical_ring_contexts = 1,
+       .has_guc = 1,
+       .ddb_size = 512,
        GEN_DEFAULT_PIPEOFFSETS,
        IVB_CURSOR_OFFSETS,
        BDW_COLORS,
@@ -349,12 +370,18 @@ static const struct intel_device_info intel_kabylake_info = {
        BDW_FEATURES,
        .is_kabylake = 1,
        .gen = 9,
+       .has_csr = 1,
+       .has_guc = 1,
+       .ddb_size = 896,
 };
 
 static const struct intel_device_info intel_kabylake_gt3_info = {
        BDW_FEATURES,
        .is_kabylake = 1,
        .gen = 9,
+       .has_csr = 1,
+       .has_guc = 1,
+       .ddb_size = 896,
        .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
 };
 
@@ -386,14 +413,10 @@ static const struct pci_device_id pciidlist[] = {
        INTEL_IVB_Q_IDS(&intel_ivybridge_q_info), /* must be first IVB */
        INTEL_IVB_M_IDS(&intel_ivybridge_m_info),
        INTEL_IVB_D_IDS(&intel_ivybridge_d_info),
-       INTEL_HSW_D_IDS(&intel_haswell_d_info),
-       INTEL_HSW_M_IDS(&intel_haswell_m_info),
-       INTEL_VLV_M_IDS(&intel_valleyview_m_info),
-       INTEL_VLV_D_IDS(&intel_valleyview_d_info),
-       INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info),
-       INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info),
-       INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info),
-       INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info),
+       INTEL_HSW_IDS(&intel_haswell_info),
+       INTEL_VLV_IDS(&intel_valleyview_info),
+       INTEL_BDW_GT12_IDS(&intel_broadwell_info),
+       INTEL_BDW_GT3_IDS(&intel_broadwell_gt3_info),
        INTEL_CHV_IDS(&intel_cherryview_info),
        INTEL_SKL_GT1_IDS(&intel_skylake_info),
        INTEL_SKL_GT2_IDS(&intel_skylake_info),
index bf2cad3..70d9616 100644 (file)
@@ -186,13 +186,13 @@ static inline bool i915_mmio_reg_valid(i915_reg_t reg)
 #define  GEN9_GRDOM_GUC                        (1 << 5)
 #define  GEN8_GRDOM_MEDIA2             (1 << 7)
 
-#define RING_PP_DIR_BASE(ring)         _MMIO((ring)->mmio_base+0x228)
-#define RING_PP_DIR_BASE_READ(ring)    _MMIO((ring)->mmio_base+0x518)
-#define RING_PP_DIR_DCLV(ring)         _MMIO((ring)->mmio_base+0x220)
+#define RING_PP_DIR_BASE(engine)       _MMIO((engine)->mmio_base+0x228)
+#define RING_PP_DIR_BASE_READ(engine)  _MMIO((engine)->mmio_base+0x518)
+#define RING_PP_DIR_DCLV(engine)       _MMIO((engine)->mmio_base+0x220)
 #define   PP_DIR_DCLV_2G               0xffffffff
 
-#define GEN8_RING_PDP_UDW(ring, n)     _MMIO((ring)->mmio_base+0x270 + (n) * 8 + 4)
-#define GEN8_RING_PDP_LDW(ring, n)     _MMIO((ring)->mmio_base+0x270 + (n) * 8)
+#define GEN8_RING_PDP_UDW(engine, n)   _MMIO((engine)->mmio_base+0x270 + (n) * 8 + 4)
+#define GEN8_RING_PDP_LDW(engine, n)   _MMIO((engine)->mmio_base+0x270 + (n) * 8)
 
 #define GEN8_R_PWR_CLK_STATE           _MMIO(0x20C8)
 #define   GEN8_RPCS_ENABLE             (1 << 31)
@@ -1648,7 +1648,7 @@ enum skl_disp_power_wells {
 #define   ARB_MODE_BWGTLB_DISABLE (1<<9)
 #define   ARB_MODE_SWIZZLE_BDW (1<<1)
 #define RENDER_HWS_PGA_GEN7    _MMIO(0x04080)
-#define RING_FAULT_REG(ring)   _MMIO(0x4094 + 0x100*(ring)->id)
+#define RING_FAULT_REG(engine) _MMIO(0x4094 + 0x100*(engine)->hw_id)
 #define   RING_FAULT_GTTSEL_MASK (1<<11)
 #define   RING_FAULT_SRCID(x)  (((x) >> 3) & 0xff)
 #define   RING_FAULT_FAULT_TYPE(x) (((x) >> 1) & 0x3)
@@ -1846,7 +1846,7 @@ enum skl_disp_power_wells {
 
 #define GFX_MODE       _MMIO(0x2520)
 #define GFX_MODE_GEN7  _MMIO(0x229c)
-#define RING_MODE_GEN7(ring)   _MMIO((ring)->mmio_base+0x29c)
+#define RING_MODE_GEN7(engine) _MMIO((engine)->mmio_base+0x29c)
 #define   GFX_RUN_LIST_ENABLE          (1<<15)
 #define   GFX_INTERRUPT_STEERING       (1<<14)
 #define   GFX_TLB_INVALIDATE_EXPLICIT  (1<<13)
@@ -3660,8 +3660,17 @@ enum {
 #define   VIDEO_DIP_ENABLE_SPD_HSW     (1 << 0)
 
 /* Panel power sequencing */
-#define PP_STATUS      _MMIO(0x61200)
-#define   PP_ON                (1 << 31)
+#define PPS_BASE                       0x61200
+#define VLV_PPS_BASE                   (VLV_DISPLAY_BASE + PPS_BASE)
+#define PCH_PPS_BASE                   0xC7200
+
+#define _MMIO_PPS(pps_idx, reg)                _MMIO(dev_priv->pps_mmio_base - \
+                                             PPS_BASE + (reg) +        \
+                                             (pps_idx) * 0x100)
+
+#define _PP_STATUS                     0x61200
+#define PP_STATUS(pps_idx)             _MMIO_PPS(pps_idx, _PP_STATUS)
+#define   PP_ON                                (1 << 31)
 /*
  * Indicates that all dependencies of the panel are on:
  *
@@ -3669,14 +3678,14 @@ enum {
  * - pipe enabled
  * - LVDS/DVOB/DVOC on
  */
-#define   PP_READY             (1 << 30)
-#define   PP_SEQUENCE_NONE     (0 << 28)
-#define   PP_SEQUENCE_POWER_UP (1 << 28)
-#define   PP_SEQUENCE_POWER_DOWN (2 << 28)
-#define   PP_SEQUENCE_MASK     (3 << 28)
-#define   PP_SEQUENCE_SHIFT    28
-#define   PP_CYCLE_DELAY_ACTIVE        (1 << 27)
-#define   PP_SEQUENCE_STATE_MASK 0x0000000f
+#define   PP_READY                     (1 << 30)
+#define   PP_SEQUENCE_NONE             (0 << 28)
+#define   PP_SEQUENCE_POWER_UP         (1 << 28)
+#define   PP_SEQUENCE_POWER_DOWN       (2 << 28)
+#define   PP_SEQUENCE_MASK             (3 << 28)
+#define   PP_SEQUENCE_SHIFT            28
+#define   PP_CYCLE_DELAY_ACTIVE                (1 << 27)
+#define   PP_SEQUENCE_STATE_MASK       0x0000000f
 #define   PP_SEQUENCE_STATE_OFF_IDLE   (0x0 << 0)
 #define   PP_SEQUENCE_STATE_OFF_S0_1   (0x1 << 0)
 #define   PP_SEQUENCE_STATE_OFF_S0_2   (0x2 << 0)
@@ -3686,11 +3695,46 @@ enum {
 #define   PP_SEQUENCE_STATE_ON_S1_2    (0xa << 0)
 #define   PP_SEQUENCE_STATE_ON_S1_3    (0xb << 0)
 #define   PP_SEQUENCE_STATE_RESET      (0xf << 0)
-#define PP_CONTROL     _MMIO(0x61204)
-#define   POWER_TARGET_ON      (1 << 0)
-#define PP_ON_DELAYS   _MMIO(0x61208)
-#define PP_OFF_DELAYS  _MMIO(0x6120c)
-#define PP_DIVISOR     _MMIO(0x61210)
+
+#define _PP_CONTROL                    0x61204
+#define PP_CONTROL(pps_idx)            _MMIO_PPS(pps_idx, _PP_CONTROL)
+#define  PANEL_UNLOCK_REGS             (0xabcd << 16)
+#define  PANEL_UNLOCK_MASK             (0xffff << 16)
+#define  BXT_POWER_CYCLE_DELAY_MASK    0x1f0
+#define  BXT_POWER_CYCLE_DELAY_SHIFT   4
+#define  EDP_FORCE_VDD                 (1 << 3)
+#define  EDP_BLC_ENABLE                        (1 << 2)
+#define  PANEL_POWER_RESET             (1 << 1)
+#define  PANEL_POWER_OFF               (0 << 0)
+#define  PANEL_POWER_ON                        (1 << 0)
+
+#define _PP_ON_DELAYS                  0x61208
+#define PP_ON_DELAYS(pps_idx)          _MMIO_PPS(pps_idx, _PP_ON_DELAYS)
+#define  PANEL_PORT_SELECT_SHIFT       30
+#define  PANEL_PORT_SELECT_MASK                (3 << 30)
+#define  PANEL_PORT_SELECT_LVDS                (0 << 30)
+#define  PANEL_PORT_SELECT_DPA         (1 << 30)
+#define  PANEL_PORT_SELECT_DPC         (2 << 30)
+#define  PANEL_PORT_SELECT_DPD         (3 << 30)
+#define  PANEL_PORT_SELECT_VLV(port)   ((port) << 30)
+#define  PANEL_POWER_UP_DELAY_MASK     0x1fff0000
+#define  PANEL_POWER_UP_DELAY_SHIFT    16
+#define  PANEL_LIGHT_ON_DELAY_MASK     0x1fff
+#define  PANEL_LIGHT_ON_DELAY_SHIFT    0
+
+#define _PP_OFF_DELAYS                 0x6120C
+#define PP_OFF_DELAYS(pps_idx)         _MMIO_PPS(pps_idx, _PP_OFF_DELAYS)
+#define  PANEL_POWER_DOWN_DELAY_MASK   0x1fff0000
+#define  PANEL_POWER_DOWN_DELAY_SHIFT  16
+#define  PANEL_LIGHT_OFF_DELAY_MASK    0x1fff
+#define  PANEL_LIGHT_OFF_DELAY_SHIFT   0
+
+#define _PP_DIVISOR                    0x61210
+#define PP_DIVISOR(pps_idx)            _MMIO_PPS(pps_idx, _PP_DIVISOR)
+#define  PP_REFERENCE_DIVIDER_MASK     0xffffff00
+#define  PP_REFERENCE_DIVIDER_SHIFT    8
+#define  PANEL_POWER_CYCLE_DELAY_MASK  0x1f
+#define  PANEL_POWER_CYCLE_DELAY_SHIFT 0
 
 /* Panel fitting */
 #define PFIT_CONTROL   _MMIO(dev_priv->info.display_mmio_offset + 0x61230)
@@ -6133,6 +6177,7 @@ enum {
 # define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC     ((1<<10) | (1<<26))
 # define GEN9_RHWO_OPTIMIZATION_DISABLE                (1<<14)
 #define COMMON_SLICE_CHICKEN2                  _MMIO(0x7014)
+# define GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE (1<<12)
 # define GEN8_SBE_DISABLE_REPLAY_BUF_OPTIMIZATION (1<<8)
 # define GEN8_CSC2_SBE_VUE_CACHE_CONSERVATIVE  (1<<0)
 
@@ -6749,77 +6794,6 @@ enum {
 #define PCH_LVDS       _MMIO(0xe1180)
 #define  LVDS_DETECTED (1 << 1)
 
-/* vlv has 2 sets of panel control regs. */
-#define _PIPEA_PP_STATUS         (VLV_DISPLAY_BASE + 0x61200)
-#define _PIPEA_PP_CONTROL        (VLV_DISPLAY_BASE + 0x61204)
-#define _PIPEA_PP_ON_DELAYS      (VLV_DISPLAY_BASE + 0x61208)
-#define  PANEL_PORT_SELECT_VLV(port)   ((port) << 30)
-#define _PIPEA_PP_OFF_DELAYS     (VLV_DISPLAY_BASE + 0x6120c)
-#define _PIPEA_PP_DIVISOR        (VLV_DISPLAY_BASE + 0x61210)
-
-#define _PIPEB_PP_STATUS         (VLV_DISPLAY_BASE + 0x61300)
-#define _PIPEB_PP_CONTROL        (VLV_DISPLAY_BASE + 0x61304)
-#define _PIPEB_PP_ON_DELAYS      (VLV_DISPLAY_BASE + 0x61308)
-#define _PIPEB_PP_OFF_DELAYS     (VLV_DISPLAY_BASE + 0x6130c)
-#define _PIPEB_PP_DIVISOR        (VLV_DISPLAY_BASE + 0x61310)
-
-#define VLV_PIPE_PP_STATUS(pipe)       _MMIO_PIPE(pipe, _PIPEA_PP_STATUS, _PIPEB_PP_STATUS)
-#define VLV_PIPE_PP_CONTROL(pipe)      _MMIO_PIPE(pipe, _PIPEA_PP_CONTROL, _PIPEB_PP_CONTROL)
-#define VLV_PIPE_PP_ON_DELAYS(pipe)    _MMIO_PIPE(pipe, _PIPEA_PP_ON_DELAYS, _PIPEB_PP_ON_DELAYS)
-#define VLV_PIPE_PP_OFF_DELAYS(pipe)   _MMIO_PIPE(pipe, _PIPEA_PP_OFF_DELAYS, _PIPEB_PP_OFF_DELAYS)
-#define VLV_PIPE_PP_DIVISOR(pipe)      _MMIO_PIPE(pipe, _PIPEA_PP_DIVISOR, _PIPEB_PP_DIVISOR)
-
-#define _PCH_PP_STATUS         0xc7200
-#define _PCH_PP_CONTROL                0xc7204
-#define  PANEL_UNLOCK_REGS     (0xabcd << 16)
-#define  PANEL_UNLOCK_MASK     (0xffff << 16)
-#define  BXT_POWER_CYCLE_DELAY_MASK    (0x1f0)
-#define  BXT_POWER_CYCLE_DELAY_SHIFT   4
-#define  EDP_FORCE_VDD         (1 << 3)
-#define  EDP_BLC_ENABLE                (1 << 2)
-#define  PANEL_POWER_RESET     (1 << 1)
-#define  PANEL_POWER_OFF       (0 << 0)
-#define  PANEL_POWER_ON                (1 << 0)
-#define _PCH_PP_ON_DELAYS      0xc7208
-#define  PANEL_PORT_SELECT_MASK        (3 << 30)
-#define  PANEL_PORT_SELECT_LVDS        (0 << 30)
-#define  PANEL_PORT_SELECT_DPA (1 << 30)
-#define  PANEL_PORT_SELECT_DPC (2 << 30)
-#define  PANEL_PORT_SELECT_DPD (3 << 30)
-#define  PANEL_POWER_UP_DELAY_MASK     (0x1fff0000)
-#define  PANEL_POWER_UP_DELAY_SHIFT    16
-#define  PANEL_LIGHT_ON_DELAY_MASK     (0x1fff)
-#define  PANEL_LIGHT_ON_DELAY_SHIFT    0
-
-#define _PCH_PP_OFF_DELAYS             0xc720c
-#define  PANEL_POWER_DOWN_DELAY_MASK   (0x1fff0000)
-#define  PANEL_POWER_DOWN_DELAY_SHIFT  16
-#define  PANEL_LIGHT_OFF_DELAY_MASK    (0x1fff)
-#define  PANEL_LIGHT_OFF_DELAY_SHIFT   0
-
-#define _PCH_PP_DIVISOR                        0xc7210
-#define  PP_REFERENCE_DIVIDER_MASK     (0xffffff00)
-#define  PP_REFERENCE_DIVIDER_SHIFT    8
-#define  PANEL_POWER_CYCLE_DELAY_MASK  (0x1f)
-#define  PANEL_POWER_CYCLE_DELAY_SHIFT 0
-
-#define PCH_PP_STATUS                  _MMIO(_PCH_PP_STATUS)
-#define PCH_PP_CONTROL                 _MMIO(_PCH_PP_CONTROL)
-#define PCH_PP_ON_DELAYS               _MMIO(_PCH_PP_ON_DELAYS)
-#define PCH_PP_OFF_DELAYS              _MMIO(_PCH_PP_OFF_DELAYS)
-#define PCH_PP_DIVISOR                 _MMIO(_PCH_PP_DIVISOR)
-
-/* BXT PPS changes - 2nd set of PPS registers */
-#define _BXT_PP_STATUS2        0xc7300
-#define _BXT_PP_CONTROL2       0xc7304
-#define _BXT_PP_ON_DELAYS2     0xc7308
-#define _BXT_PP_OFF_DELAYS2    0xc730c
-
-#define BXT_PP_STATUS(n)       _MMIO_PIPE(n, _PCH_PP_STATUS, _BXT_PP_STATUS2)
-#define BXT_PP_CONTROL(n)      _MMIO_PIPE(n, _PCH_PP_CONTROL, _BXT_PP_CONTROL2)
-#define BXT_PP_ON_DELAYS(n)    _MMIO_PIPE(n, _PCH_PP_ON_DELAYS, _BXT_PP_ON_DELAYS2)
-#define BXT_PP_OFF_DELAYS(n)   _MMIO_PIPE(n, _PCH_PP_OFF_DELAYS, _BXT_PP_OFF_DELAYS2)
-
 #define _PCH_DP_B              0xe4100
 #define PCH_DP_B               _MMIO(_PCH_DP_B)
 #define _PCH_DPB_AUX_CH_CTL    0xe4110
@@ -6959,6 +6933,9 @@ enum {
 #define  ECOBUS                                        _MMIO(0xa180)
 #define    FORCEWAKE_MT_ENABLE                 (1<<5)
 #define  VLV_SPAREG2H                          _MMIO(0xA194)
+#define  GEN9_PWRGT_DOMAIN_STATUS              _MMIO(0xA2A0)
+#define   GEN9_PWRGT_MEDIA_STATUS_MASK         (1 << 0)
+#define   GEN9_PWRGT_RENDER_STATUS_MASK                (1 << 1)
 
 #define  GTFIFODBG                             _MMIO(0x120000)
 #define    GT_FIFO_SBDEDICATE_FREE_ENTRY_CHV   (0x1f << 20)
@@ -7059,12 +7036,13 @@ enum {
 #define GEN6_RP_UP_THRESHOLD                   _MMIO(0xA02C)
 #define GEN6_RP_DOWN_THRESHOLD                 _MMIO(0xA030)
 #define GEN6_RP_CUR_UP_EI                      _MMIO(0xA050)
-#define   GEN6_CURICONT_MASK                   0xffffff
+#define   GEN6_RP_EI_MASK                      0xffffff
+#define   GEN6_CURICONT_MASK                   GEN6_RP_EI_MASK
 #define GEN6_RP_CUR_UP                         _MMIO(0xA054)
-#define   GEN6_CURBSYTAVG_MASK                 0xffffff
+#define   GEN6_CURBSYTAVG_MASK                 GEN6_RP_EI_MASK
 #define GEN6_RP_PREV_UP                                _MMIO(0xA058)
 #define GEN6_RP_CUR_DOWN_EI                    _MMIO(0xA05C)
-#define   GEN6_CURIAVG_MASK                    0xffffff
+#define   GEN6_CURIAVG_MASK                    GEN6_RP_EI_MASK
 #define GEN6_RP_CUR_DOWN                       _MMIO(0xA060)
 #define GEN6_RP_PREV_DOWN                      _MMIO(0xA064)
 #define GEN6_RP_UP_EI                          _MMIO(0xA068)
@@ -7089,7 +7067,7 @@ enum {
 #define VLV_RCEDATA                            _MMIO(0xA0BC)
 #define GEN6_RC6pp_THRESHOLD                   _MMIO(0xA0C0)
 #define GEN6_PMINTRMSK                         _MMIO(0xA168)
-#define   GEN8_PMINTR_REDIRECT_TO_NON_DISP     (1<<31)
+#define   GEN8_PMINTR_REDIRECT_TO_GUC            (1<<31)
 #define GEN8_MISC_CTRL0                                _MMIO(0xA180)
 #define VLV_PWRDWNUPCTL                                _MMIO(0xA294)
 #define GEN9_MEDIA_PG_IDLE_HYSTERESIS          _MMIO(0xA0C4)
@@ -7499,6 +7477,7 @@ enum {
 #define _DDI_BUF_TRANS_A               0x64E00
 #define _DDI_BUF_TRANS_B               0x64E60
 #define DDI_BUF_TRANS_LO(port, i)      _MMIO(_PORT(port, _DDI_BUF_TRANS_A, _DDI_BUF_TRANS_B) + (i) * 8)
+#define  DDI_BUF_BALANCE_LEG_ENABLE    (1 << 31)
 #define DDI_BUF_TRANS_HI(port, i)      _MMIO(_PORT(port, _DDI_BUF_TRANS_A, _DDI_BUF_TRANS_B) + (i) * 8 + 4)
 
 /* Sideband Interface (SBI) is programmed indirectly, via
index 5cfe4c7..a0af170 100644 (file)
@@ -37,25 +37,6 @@ static void i915_save_display(struct drm_device *dev)
        if (INTEL_INFO(dev)->gen <= 4)
                dev_priv->regfile.saveDSPARB = I915_READ(DSPARB);
 
-       /* LVDS state */
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
-               dev_priv->regfile.saveLVDS = I915_READ(PCH_LVDS);
-       else if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev))
-               dev_priv->regfile.saveLVDS = I915_READ(LVDS);
-
-       /* Panel power sequencer */
-       if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->regfile.savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
-               dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS);
-               dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS);
-               dev_priv->regfile.savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR);
-       } else if (INTEL_INFO(dev)->gen <= 4) {
-               dev_priv->regfile.savePP_CONTROL = I915_READ(PP_CONTROL);
-               dev_priv->regfile.savePP_ON_DELAYS = I915_READ(PP_ON_DELAYS);
-               dev_priv->regfile.savePP_OFF_DELAYS = I915_READ(PP_OFF_DELAYS);
-               dev_priv->regfile.savePP_DIVISOR = I915_READ(PP_DIVISOR);
-       }
-
        /* save FBC interval */
        if (HAS_FBC(dev) && INTEL_INFO(dev)->gen <= 4 && !IS_G4X(dev))
                dev_priv->regfile.saveFBC_CONTROL = I915_READ(FBC_CONTROL);
@@ -64,33 +45,11 @@ static void i915_save_display(struct drm_device *dev)
 static void i915_restore_display(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
-       u32 mask = 0xffffffff;
 
        /* Display arbitration */
        if (INTEL_INFO(dev)->gen <= 4)
                I915_WRITE(DSPARB, dev_priv->regfile.saveDSPARB);
 
-       mask = ~LVDS_PORT_EN;
-
-       /* LVDS state */
-       if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
-               I915_WRITE(PCH_LVDS, dev_priv->regfile.saveLVDS & mask);
-       else if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev))
-               I915_WRITE(LVDS, dev_priv->regfile.saveLVDS & mask);
-
-       /* Panel power sequencer */
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(PCH_PP_ON_DELAYS, dev_priv->regfile.savePP_ON_DELAYS);
-               I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS);
-               I915_WRITE(PCH_PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR);
-               I915_WRITE(PCH_PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
-       } else if (INTEL_INFO(dev)->gen <= 4) {
-               I915_WRITE(PP_ON_DELAYS, dev_priv->regfile.savePP_ON_DELAYS);
-               I915_WRITE(PP_OFF_DELAYS, dev_priv->regfile.savePP_OFF_DELAYS);
-               I915_WRITE(PP_DIVISOR, dev_priv->regfile.savePP_DIVISOR);
-               I915_WRITE(PP_CONTROL, dev_priv->regfile.savePP_CONTROL);
-       }
-
        /* only restore FBC info on the platform that supports FBC*/
        intel_fbc_global_disable(dev_priv);
 
@@ -104,6 +63,7 @@ static void i915_restore_display(struct drm_device *dev)
 int i915_save_state(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        int i;
 
        mutex_lock(&dev->struct_mutex);
@@ -111,7 +71,7 @@ int i915_save_state(struct drm_device *dev)
        i915_save_display(dev);
 
        if (IS_GEN4(dev))
-               pci_read_config_word(dev->pdev, GCDGMBUS,
+               pci_read_config_word(pdev, GCDGMBUS,
                                     &dev_priv->regfile.saveGCDGMBUS);
 
        /* Cache mode state */
@@ -149,6 +109,7 @@ int i915_save_state(struct drm_device *dev)
 int i915_restore_state(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        int i;
 
        mutex_lock(&dev->struct_mutex);
@@ -156,7 +117,7 @@ int i915_restore_state(struct drm_device *dev)
        i915_gem_restore_fences(dev);
 
        if (IS_GEN4(dev))
-               pci_write_config_word(dev->pdev, GCDGMBUS,
+               pci_write_config_word(pdev, GCDGMBUS,
                                      dev_priv->regfile.saveGCDGMBUS);
        i915_restore_display(dev);
 
diff --git a/drivers/gpu/drm/i915/i915_sw_fence.c b/drivers/gpu/drm/i915/i915_sw_fence.c
new file mode 100644 (file)
index 0000000..1e5cbc5
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * (C) Copyright 2016 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include <linux/slab.h>
+#include <linux/fence.h>
+#include <linux/reservation.h>
+
+#include "i915_sw_fence.h"
+
+static DEFINE_SPINLOCK(i915_sw_fence_lock);
+
+static int __i915_sw_fence_notify(struct i915_sw_fence *fence,
+                                 enum i915_sw_fence_notify state)
+{
+       i915_sw_fence_notify_t fn;
+
+       fn = (i915_sw_fence_notify_t)(fence->flags & I915_SW_FENCE_MASK);
+       return fn(fence, state);
+}
+
+static void i915_sw_fence_free(struct kref *kref)
+{
+       struct i915_sw_fence *fence = container_of(kref, typeof(*fence), kref);
+
+       WARN_ON(atomic_read(&fence->pending) > 0);
+
+       if (fence->flags & I915_SW_FENCE_MASK)
+               __i915_sw_fence_notify(fence, FENCE_FREE);
+       else
+               kfree(fence);
+}
+
+static void i915_sw_fence_put(struct i915_sw_fence *fence)
+{
+       kref_put(&fence->kref, i915_sw_fence_free);
+}
+
+static struct i915_sw_fence *i915_sw_fence_get(struct i915_sw_fence *fence)
+{
+       kref_get(&fence->kref);
+       return fence;
+}
+
+static void __i915_sw_fence_wake_up_all(struct i915_sw_fence *fence,
+                                       struct list_head *continuation)
+{
+       wait_queue_head_t *x = &fence->wait;
+       wait_queue_t *pos, *next;
+       unsigned long flags;
+
+       atomic_set_release(&fence->pending, -1); /* 0 -> -1 [done] */
+
+       /*
+        * To prevent unbounded recursion as we traverse the graph of
+        * i915_sw_fences, we move the task_list from this, the next ready
+        * fence, to the tail of the original fence's task_list
+        * (and so added to the list to be woken).
+        */
+
+       spin_lock_irqsave_nested(&x->lock, flags, 1 + !!continuation);
+       if (continuation) {
+               list_for_each_entry_safe(pos, next, &x->task_list, task_list) {
+                       if (pos->func == autoremove_wake_function)
+                               pos->func(pos, TASK_NORMAL, 0, continuation);
+                       else
+                               list_move_tail(&pos->task_list, continuation);
+               }
+       } else {
+               LIST_HEAD(extra);
+
+               do {
+                       list_for_each_entry_safe(pos, next,
+                                                &x->task_list, task_list)
+                               pos->func(pos, TASK_NORMAL, 0, &extra);
+
+                       if (list_empty(&extra))
+                               break;
+
+                       list_splice_tail_init(&extra, &x->task_list);
+               } while (1);
+       }
+       spin_unlock_irqrestore(&x->lock, flags);
+}
+
+static void __i915_sw_fence_complete(struct i915_sw_fence *fence,
+                                    struct list_head *continuation)
+{
+       if (!atomic_dec_and_test(&fence->pending))
+               return;
+
+       if (fence->flags & I915_SW_FENCE_MASK &&
+           __i915_sw_fence_notify(fence, FENCE_COMPLETE) != NOTIFY_DONE)
+               return;
+
+       __i915_sw_fence_wake_up_all(fence, continuation);
+}
+
+static void i915_sw_fence_complete(struct i915_sw_fence *fence)
+{
+       if (WARN_ON(i915_sw_fence_done(fence)))
+               return;
+
+       __i915_sw_fence_complete(fence, NULL);
+}
+
+static void i915_sw_fence_await(struct i915_sw_fence *fence)
+{
+       WARN_ON(atomic_inc_return(&fence->pending) <= 1);
+}
+
+void i915_sw_fence_init(struct i915_sw_fence *fence, i915_sw_fence_notify_t fn)
+{
+       BUG_ON((unsigned long)fn & ~I915_SW_FENCE_MASK);
+
+       init_waitqueue_head(&fence->wait);
+       kref_init(&fence->kref);
+       atomic_set(&fence->pending, 1);
+       fence->flags = (unsigned long)fn;
+}
+
+void i915_sw_fence_commit(struct i915_sw_fence *fence)
+{
+       i915_sw_fence_complete(fence);
+       i915_sw_fence_put(fence);
+}
+
+static int i915_sw_fence_wake(wait_queue_t *wq, unsigned mode, int flags, void *key)
+{
+       list_del(&wq->task_list);
+       __i915_sw_fence_complete(wq->private, key);
+       i915_sw_fence_put(wq->private);
+       return 0;
+}
+
+static bool __i915_sw_fence_check_if_after(struct i915_sw_fence *fence,
+                                   const struct i915_sw_fence * const signaler)
+{
+       wait_queue_t *wq;
+
+       if (__test_and_set_bit(I915_SW_FENCE_CHECKED_BIT, &fence->flags))
+               return false;
+
+       if (fence == signaler)
+               return true;
+
+       list_for_each_entry(wq, &fence->wait.task_list, task_list) {
+               if (wq->func != i915_sw_fence_wake)
+                       continue;
+
+               if (__i915_sw_fence_check_if_after(wq->private, signaler))
+                       return true;
+       }
+
+       return false;
+}
+
+static void __i915_sw_fence_clear_checked_bit(struct i915_sw_fence *fence)
+{
+       wait_queue_t *wq;
+
+       if (!__test_and_clear_bit(I915_SW_FENCE_CHECKED_BIT, &fence->flags))
+               return;
+
+       list_for_each_entry(wq, &fence->wait.task_list, task_list) {
+               if (wq->func != i915_sw_fence_wake)
+                       continue;
+
+               __i915_sw_fence_clear_checked_bit(wq->private);
+       }
+}
+
+static bool i915_sw_fence_check_if_after(struct i915_sw_fence *fence,
+                                 const struct i915_sw_fence * const signaler)
+{
+       unsigned long flags;
+       bool err;
+
+       if (!IS_ENABLED(CONFIG_I915_SW_FENCE_CHECK_DAG))
+               return false;
+
+       spin_lock_irqsave(&i915_sw_fence_lock, flags);
+       err = __i915_sw_fence_check_if_after(fence, signaler);
+       __i915_sw_fence_clear_checked_bit(fence);
+       spin_unlock_irqrestore(&i915_sw_fence_lock, flags);
+
+       return err;
+}
+
+int i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence,
+                                struct i915_sw_fence *signaler,
+                                wait_queue_t *wq)
+{
+       unsigned long flags;
+       int pending;
+
+       if (i915_sw_fence_done(signaler))
+               return 0;
+
+       /* The dependency graph must be acyclic. */
+       if (unlikely(i915_sw_fence_check_if_after(fence, signaler)))
+               return -EINVAL;
+
+       INIT_LIST_HEAD(&wq->task_list);
+       wq->flags = 0;
+       wq->func = i915_sw_fence_wake;
+       wq->private = i915_sw_fence_get(fence);
+
+       i915_sw_fence_await(fence);
+
+       spin_lock_irqsave(&signaler->wait.lock, flags);
+       if (likely(!i915_sw_fence_done(signaler))) {
+               __add_wait_queue_tail(&signaler->wait, wq);
+               pending = 1;
+       } else {
+               i915_sw_fence_wake(wq, 0, 0, NULL);
+               pending = 0;
+       }
+       spin_unlock_irqrestore(&signaler->wait.lock, flags);
+
+       return pending;
+}
+
+struct dma_fence_cb {
+       struct fence_cb base;
+       struct i915_sw_fence *fence;
+       struct fence *dma;
+       struct timer_list timer;
+};
+
+static void timer_i915_sw_fence_wake(unsigned long data)
+{
+       struct dma_fence_cb *cb = (struct dma_fence_cb *)data;
+
+       printk(KERN_WARNING "asynchronous wait on fence %s:%s:%x timed out\n",
+              cb->dma->ops->get_driver_name(cb->dma),
+              cb->dma->ops->get_timeline_name(cb->dma),
+              cb->dma->seqno);
+       fence_put(cb->dma);
+       cb->dma = NULL;
+
+       i915_sw_fence_commit(cb->fence);
+       cb->timer.function = NULL;
+}
+
+static void dma_i915_sw_fence_wake(struct fence *dma, struct fence_cb *data)
+{
+       struct dma_fence_cb *cb = container_of(data, typeof(*cb), base);
+
+       del_timer_sync(&cb->timer);
+       if (cb->timer.function)
+               i915_sw_fence_commit(cb->fence);
+       fence_put(cb->dma);
+
+       kfree(cb);
+}
+
+int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence,
+                                 struct fence *dma,
+                                 unsigned long timeout,
+                                 gfp_t gfp)
+{
+       struct dma_fence_cb *cb;
+       int ret;
+
+       if (fence_is_signaled(dma))
+               return 0;
+
+       cb = kmalloc(sizeof(*cb), gfp);
+       if (!cb) {
+               if (!gfpflags_allow_blocking(gfp))
+                       return -ENOMEM;
+
+               return fence_wait(dma, false);
+       }
+
+       cb->fence = i915_sw_fence_get(fence);
+       i915_sw_fence_await(fence);
+
+       cb->dma = NULL;
+       __setup_timer(&cb->timer,
+                     timer_i915_sw_fence_wake, (unsigned long)cb,
+                     TIMER_IRQSAFE);
+       if (timeout) {
+               cb->dma = fence_get(dma);
+               mod_timer(&cb->timer, round_jiffies_up(jiffies + timeout));
+       }
+
+       ret = fence_add_callback(dma, &cb->base, dma_i915_sw_fence_wake);
+       if (ret == 0) {
+               ret = 1;
+       } else {
+               dma_i915_sw_fence_wake(dma, &cb->base);
+               if (ret == -ENOENT) /* fence already signaled */
+                       ret = 0;
+       }
+
+       return ret;
+}
+
+int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
+                                   struct reservation_object *resv,
+                                   const struct fence_ops *exclude,
+                                   bool write,
+                                   unsigned long timeout,
+                                   gfp_t gfp)
+{
+       struct fence *excl;
+       int ret = 0, pending;
+
+       if (write) {
+               struct fence **shared;
+               unsigned int count, i;
+
+               ret = reservation_object_get_fences_rcu(resv,
+                                                       &excl, &count, &shared);
+               if (ret)
+                       return ret;
+
+               for (i = 0; i < count; i++) {
+                       if (shared[i]->ops == exclude)
+                               continue;
+
+                       pending = i915_sw_fence_await_dma_fence(fence,
+                                                               shared[i],
+                                                               timeout,
+                                                               gfp);
+                       if (pending < 0) {
+                               ret = pending;
+                               break;
+                       }
+
+                       ret |= pending;
+               }
+
+               for (i = 0; i < count; i++)
+                       fence_put(shared[i]);
+               kfree(shared);
+       } else {
+               excl = reservation_object_get_excl_rcu(resv);
+       }
+
+       if (ret >= 0 && excl && excl->ops != exclude) {
+               pending = i915_sw_fence_await_dma_fence(fence,
+                                                       excl,
+                                                       timeout,
+                                                       gfp);
+               if (pending < 0)
+                       ret = pending;
+               else
+                       ret |= pending;
+       }
+
+       fence_put(excl);
+
+       return ret;
+}
diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h
new file mode 100644 (file)
index 0000000..3731416
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * i915_sw_fence.h - library routines for N:M synchronisation points
+ *
+ * Copyright (C) 2016 Intel Corporation
+ *
+ * This file is released under the GPLv2.
+ *
+ */
+
+#ifndef _I915_SW_FENCE_H_
+#define _I915_SW_FENCE_H_
+
+#include <linux/gfp.h>
+#include <linux/kref.h>
+#include <linux/notifier.h> /* for NOTIFY_DONE */
+#include <linux/wait.h>
+
+struct completion;
+struct fence;
+struct fence_ops;
+struct reservation_object;
+
+struct i915_sw_fence {
+       wait_queue_head_t wait;
+       unsigned long flags;
+       struct kref kref;
+       atomic_t pending;
+};
+
+#define I915_SW_FENCE_CHECKED_BIT      0 /* used internally for DAG checking */
+#define I915_SW_FENCE_PRIVATE_BIT      1 /* available for use by owner */
+#define I915_SW_FENCE_MASK             (~3)
+
+enum i915_sw_fence_notify {
+       FENCE_COMPLETE,
+       FENCE_FREE
+};
+
+typedef int (*i915_sw_fence_notify_t)(struct i915_sw_fence *,
+                                     enum i915_sw_fence_notify state);
+#define __i915_sw_fence_call __aligned(4)
+
+void i915_sw_fence_init(struct i915_sw_fence *fence, i915_sw_fence_notify_t fn);
+void i915_sw_fence_commit(struct i915_sw_fence *fence);
+
+int i915_sw_fence_await_sw_fence(struct i915_sw_fence *fence,
+                                struct i915_sw_fence *after,
+                                wait_queue_t *wq);
+int i915_sw_fence_await_dma_fence(struct i915_sw_fence *fence,
+                                 struct fence *dma,
+                                 unsigned long timeout,
+                                 gfp_t gfp);
+int i915_sw_fence_await_reservation(struct i915_sw_fence *fence,
+                                   struct reservation_object *resv,
+                                   const struct fence_ops *exclude,
+                                   bool write,
+                                   unsigned long timeout,
+                                   gfp_t gfp);
+
+static inline bool i915_sw_fence_done(const struct i915_sw_fence *fence)
+{
+       return atomic_read(&fence->pending) < 0;
+}
+
+#endif /* _I915_SW_FENCE_H_ */
index d61829e..1012eee 100644 (file)
 #include "intel_drv.h"
 #include "i915_drv.h"
 
-#define dev_to_drm_minor(d) dev_get_drvdata((d))
+static inline struct drm_i915_private *kdev_minor_to_i915(struct device *kdev)
+{
+       struct drm_minor *minor = dev_get_drvdata(kdev);
+       return to_i915(minor->dev);
+}
 
 #ifdef CONFIG_PM
-static u32 calc_residency(struct drm_device *dev,
+static u32 calc_residency(struct drm_i915_private *dev_priv,
                          i915_reg_t reg)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        u64 raw_time; /* 32b value may overflow during fixed point math */
        u64 units = 128ULL, div = 100000ULL;
        u32 ret;
@@ -49,13 +52,13 @@ static u32 calc_residency(struct drm_device *dev,
        intel_runtime_pm_get(dev_priv);
 
        /* On VLV and CHV, residency time is in CZ units rather than 1.28us */
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
                units = 1;
                div = dev_priv->czclk_freq;
 
                if (I915_READ(VLV_COUNTER_CONTROL) & VLV_COUNT_RANGE_HIGH)
                        units <<= 8;
-       } else if (IS_BROXTON(dev)) {
+       } else if (IS_BROXTON(dev_priv)) {
                units = 1;
                div = 1200;             /* 833.33ns */
        }
@@ -76,32 +79,32 @@ show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf)
 static ssize_t
 show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = dev_get_drvdata(kdev);
-       u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       u32 rc6_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6);
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency);
 }
 
 static ssize_t
 show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = dev_to_drm_minor(kdev);
-       u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       u32 rc6p_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6p);
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6p_residency);
 }
 
 static ssize_t
 show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = dev_to_drm_minor(kdev);
-       u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       u32 rc6pp_residency = calc_residency(dev_priv, GEN6_GT_GFX_RC6pp);
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6pp_residency);
 }
 
 static ssize_t
 show_media_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *dminor = dev_get_drvdata(kdev);
-       u32 rc6_residency = calc_residency(dminor->dev, VLV_GT_MEDIA_RC6);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       u32 rc6_residency = calc_residency(dev_priv, VLV_GT_MEDIA_RC6);
        return snprintf(buf, PAGE_SIZE, "%u\n", rc6_residency);
 }
 
@@ -144,9 +147,9 @@ static struct attribute_group media_rc6_attr_group = {
 };
 #endif
 
-static int l3_access_valid(struct drm_device *dev, loff_t offset)
+static int l3_access_valid(struct drm_i915_private *dev_priv, loff_t offset)
 {
-       if (!HAS_L3_DPF(dev))
+       if (!HAS_L3_DPF(dev_priv))
                return -EPERM;
 
        if (offset % 4 != 0)
@@ -163,22 +166,21 @@ i915_l3_read(struct file *filp, struct kobject *kobj,
             struct bin_attribute *attr, char *buf,
             loff_t offset, size_t count)
 {
-       struct device *dev = kobj_to_dev(kobj);
-       struct drm_minor *dminor = dev_to_drm_minor(dev);
-       struct drm_device *drm_dev = dminor->dev;
-       struct drm_i915_private *dev_priv = to_i915(drm_dev);
+       struct device *kdev = kobj_to_dev(kobj);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       struct drm_device *dev = &dev_priv->drm;
        int slice = (int)(uintptr_t)attr->private;
        int ret;
 
        count = round_down(count, 4);
 
-       ret = l3_access_valid(drm_dev, offset);
+       ret = l3_access_valid(dev_priv, offset);
        if (ret)
                return ret;
 
        count = min_t(size_t, GEN7_L3LOG_SIZE - offset, count);
 
-       ret = i915_mutex_lock_interruptible(drm_dev);
+       ret = i915_mutex_lock_interruptible(dev);
        if (ret)
                return ret;
 
@@ -189,7 +191,7 @@ i915_l3_read(struct file *filp, struct kobject *kobj,
        else
                memset(buf, 0, count);
 
-       mutex_unlock(&drm_dev->struct_mutex);
+       mutex_unlock(&dev->struct_mutex);
 
        return count;
 }
@@ -199,30 +201,29 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
              struct bin_attribute *attr, char *buf,
              loff_t offset, size_t count)
 {
-       struct device *dev = kobj_to_dev(kobj);
-       struct drm_minor *dminor = dev_to_drm_minor(dev);
-       struct drm_device *drm_dev = dminor->dev;
-       struct drm_i915_private *dev_priv = to_i915(drm_dev);
+       struct device *kdev = kobj_to_dev(kobj);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       struct drm_device *dev = &dev_priv->drm;
        struct i915_gem_context *ctx;
        u32 *temp = NULL; /* Just here to make handling failures easy */
        int slice = (int)(uintptr_t)attr->private;
        int ret;
 
-       if (!HAS_HW_CONTEXTS(drm_dev))
+       if (!HAS_HW_CONTEXTS(dev_priv))
                return -ENXIO;
 
-       ret = l3_access_valid(drm_dev, offset);
+       ret = l3_access_valid(dev_priv, offset);
        if (ret)
                return ret;
 
-       ret = i915_mutex_lock_interruptible(drm_dev);
+       ret = i915_mutex_lock_interruptible(dev);
        if (ret)
                return ret;
 
        if (!dev_priv->l3_parity.remap_info[slice]) {
                temp = kzalloc(GEN7_L3LOG_SIZE, GFP_KERNEL);
                if (!temp) {
-                       mutex_unlock(&drm_dev->struct_mutex);
+                       mutex_unlock(&dev->struct_mutex);
                        return -ENOMEM;
                }
        }
@@ -240,7 +241,7 @@ i915_l3_write(struct file *filp, struct kobject *kobj,
        list_for_each_entry(ctx, &dev_priv->context_list, link)
                ctx->remap_slice |= (1<<slice);
 
-       mutex_unlock(&drm_dev->struct_mutex);
+       mutex_unlock(&dev->struct_mutex);
 
        return count;
 }
@@ -266,13 +267,9 @@ static struct bin_attribute dpf_attrs_1 = {
 static ssize_t gt_act_freq_mhz_show(struct device *kdev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
        int ret;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        intel_runtime_pm_get(dev_priv);
 
        mutex_lock(&dev_priv->rps.hw_lock);
@@ -300,59 +297,70 @@ static ssize_t gt_act_freq_mhz_show(struct device *kdev,
 static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
                                    struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       intel_gpu_freq(dev_priv,
+                                      dev_priv->rps.cur_freq));
+}
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+static ssize_t gt_boost_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
+{
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
 
-       intel_runtime_pm_get(dev_priv);
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       intel_gpu_freq(dev_priv,
+                                      dev_priv->rps.boost_freq));
+}
+
+static ssize_t gt_boost_freq_mhz_store(struct device *kdev,
+                                      struct device_attribute *attr,
+                                      const char *buf, size_t count)
+{
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       u32 val;
+       ssize_t ret;
+
+       ret = kstrtou32(buf, 0, &val);
+       if (ret)
+               return ret;
+
+       /* Validate against (static) hardware limits */
+       val = intel_freq_opcode(dev_priv, val);
+       if (val < dev_priv->rps.min_freq || val > dev_priv->rps.max_freq)
+               return -EINVAL;
 
        mutex_lock(&dev_priv->rps.hw_lock);
-       ret = intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq);
+       dev_priv->rps.boost_freq = val;
        mutex_unlock(&dev_priv->rps.hw_lock);
 
-       intel_runtime_pm_put(dev_priv);
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+       return count;
 }
 
 static ssize_t vlv_rpe_freq_mhz_show(struct device *kdev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
 
-       return snprintf(buf, PAGE_SIZE,
-                       "%d\n",
-                       intel_gpu_freq(dev_priv, dev_priv->rps.efficient_freq));
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       intel_gpu_freq(dev_priv,
+                                      dev_priv->rps.efficient_freq));
 }
 
 static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-       mutex_lock(&dev_priv->rps.hw_lock);
-       ret = intel_gpu_freq(dev_priv, dev_priv->rps.max_freq_softlimit);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       intel_gpu_freq(dev_priv,
+                                      dev_priv->rps.max_freq_softlimit));
 }
 
 static ssize_t gt_max_freq_mhz_store(struct device *kdev,
                                     struct device_attribute *attr,
                                     const char *buf, size_t count)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
        u32 val;
        ssize_t ret;
 
@@ -360,8 +368,6 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        intel_runtime_pm_get(dev_priv);
 
        mutex_lock(&dev_priv->rps.hw_lock);
@@ -400,27 +406,18 @@ static ssize_t gt_max_freq_mhz_store(struct device *kdev,
 
 static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       int ret;
-
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
 
-       mutex_lock(&dev_priv->rps.hw_lock);
-       ret = intel_gpu_freq(dev_priv, dev_priv->rps.min_freq_softlimit);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       intel_gpu_freq(dev_priv,
+                                      dev_priv->rps.min_freq_softlimit));
 }
 
 static ssize_t gt_min_freq_mhz_store(struct device *kdev,
                                     struct device_attribute *attr,
                                     const char *buf, size_t count)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
        u32 val;
        ssize_t ret;
 
@@ -428,8 +425,6 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
        if (ret)
                return ret;
 
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
        intel_runtime_pm_get(dev_priv);
 
        mutex_lock(&dev_priv->rps.hw_lock);
@@ -465,6 +460,7 @@ static ssize_t gt_min_freq_mhz_store(struct device *kdev,
 
 static DEVICE_ATTR(gt_act_freq_mhz, S_IRUGO, gt_act_freq_mhz_show, NULL);
 static DEVICE_ATTR(gt_cur_freq_mhz, S_IRUGO, gt_cur_freq_mhz_show, NULL);
+static DEVICE_ATTR(gt_boost_freq_mhz, S_IRUGO, gt_boost_freq_mhz_show, gt_boost_freq_mhz_store);
 static DEVICE_ATTR(gt_max_freq_mhz, S_IRUGO | S_IWUSR, gt_max_freq_mhz_show, gt_max_freq_mhz_store);
 static DEVICE_ATTR(gt_min_freq_mhz, S_IRUGO | S_IWUSR, gt_min_freq_mhz_show, gt_min_freq_mhz_store);
 
@@ -478,9 +474,7 @@ static DEVICE_ATTR(gt_RPn_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
 /* For now we have a static number of RP states */
 static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
 {
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
        u32 val;
 
        if (attr == &dev_attr_gt_RP0_freq_mhz)
@@ -498,6 +492,7 @@ static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr
 static const struct attribute *gen6_attrs[] = {
        &dev_attr_gt_act_freq_mhz.attr,
        &dev_attr_gt_cur_freq_mhz.attr,
+       &dev_attr_gt_boost_freq_mhz.attr,
        &dev_attr_gt_max_freq_mhz.attr,
        &dev_attr_gt_min_freq_mhz.attr,
        &dev_attr_gt_RP0_freq_mhz.attr,
@@ -509,6 +504,7 @@ static const struct attribute *gen6_attrs[] = {
 static const struct attribute *vlv_attrs[] = {
        &dev_attr_gt_act_freq_mhz.attr,
        &dev_attr_gt_cur_freq_mhz.attr,
+       &dev_attr_gt_boost_freq_mhz.attr,
        &dev_attr_gt_max_freq_mhz.attr,
        &dev_attr_gt_min_freq_mhz.attr,
        &dev_attr_gt_RP0_freq_mhz.attr,
@@ -524,8 +520,8 @@ static ssize_t error_state_read(struct file *filp, struct kobject *kobj,
 {
 
        struct device *kdev = kobj_to_dev(kobj);
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
+       struct drm_device *dev = &dev_priv->drm;
        struct i915_error_state_file_priv error_priv;
        struct drm_i915_error_state_buf error_str;
        ssize_t ret_count = 0;
@@ -559,18 +555,10 @@ static ssize_t error_state_write(struct file *file, struct kobject *kobj,
                                 loff_t off, size_t count)
 {
        struct device *kdev = kobj_to_dev(kobj);
-       struct drm_minor *minor = dev_to_drm_minor(kdev);
-       struct drm_device *dev = minor->dev;
-       int ret;
+       struct drm_i915_private *dev_priv = kdev_minor_to_i915(kdev);
 
        DRM_DEBUG_DRIVER("Resetting error state\n");
-
-       ret = mutex_lock_interruptible(&dev->struct_mutex);
-       if (ret)
-               return ret;
-
-       i915_destroy_error_state(dev);
-       mutex_unlock(&dev->struct_mutex);
+       i915_destroy_error_state(&dev_priv->drm);
 
        return count;
 }
@@ -583,37 +571,38 @@ static struct bin_attribute error_state_attr = {
        .write = error_state_write,
 };
 
-void i915_setup_sysfs(struct drm_device *dev)
+void i915_setup_sysfs(struct drm_i915_private *dev_priv)
 {
+       struct device *kdev = dev_priv->drm.primary->kdev;
        int ret;
 
 #ifdef CONFIG_PM
-       if (HAS_RC6(dev)) {
-               ret = sysfs_merge_group(&dev->primary->kdev->kobj,
+       if (HAS_RC6(dev_priv)) {
+               ret = sysfs_merge_group(&kdev->kobj,
                                        &rc6_attr_group);
                if (ret)
                        DRM_ERROR("RC6 residency sysfs setup failed\n");
        }
-       if (HAS_RC6p(dev)) {
-               ret = sysfs_merge_group(&dev->primary->kdev->kobj,
+       if (HAS_RC6p(dev_priv)) {
+               ret = sysfs_merge_group(&kdev->kobj,
                                        &rc6p_attr_group);
                if (ret)
                        DRM_ERROR("RC6p residency sysfs setup failed\n");
        }
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
-               ret = sysfs_merge_group(&dev->primary->kdev->kobj,
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+               ret = sysfs_merge_group(&kdev->kobj,
                                        &media_rc6_attr_group);
                if (ret)
                        DRM_ERROR("Media RC6 residency sysfs setup failed\n");
        }
 #endif
-       if (HAS_L3_DPF(dev)) {
-               ret = device_create_bin_file(dev->primary->kdev, &dpf_attrs);
+       if (HAS_L3_DPF(dev_priv)) {
+               ret = device_create_bin_file(kdev, &dpf_attrs);
                if (ret)
                        DRM_ERROR("l3 parity sysfs setup failed\n");
 
-               if (NUM_L3_SLICES(dev) > 1) {
-                       ret = device_create_bin_file(dev->primary->kdev,
+               if (NUM_L3_SLICES(dev_priv) > 1) {
+                       ret = device_create_bin_file(kdev,
                                                     &dpf_attrs_1);
                        if (ret)
                                DRM_ERROR("l3 parity slice 1 setup failed\n");
@@ -621,30 +610,32 @@ void i915_setup_sysfs(struct drm_device *dev)
        }
 
        ret = 0;
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
-               ret = sysfs_create_files(&dev->primary->kdev->kobj, vlv_attrs);
-       else if (INTEL_INFO(dev)->gen >= 6)
-               ret = sysfs_create_files(&dev->primary->kdev->kobj, gen6_attrs);
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               ret = sysfs_create_files(&kdev->kobj, vlv_attrs);
+       else if (INTEL_GEN(dev_priv) >= 6)
+               ret = sysfs_create_files(&kdev->kobj, gen6_attrs);
        if (ret)
                DRM_ERROR("RPS sysfs setup failed\n");
 
-       ret = sysfs_create_bin_file(&dev->primary->kdev->kobj,
+       ret = sysfs_create_bin_file(&kdev->kobj,
                                    &error_state_attr);
        if (ret)
                DRM_ERROR("error_state sysfs setup failed\n");
 }
 
-void i915_teardown_sysfs(struct drm_device *dev)
+void i915_teardown_sysfs(struct drm_i915_private *dev_priv)
 {
-       sysfs_remove_bin_file(&dev->primary->kdev->kobj, &error_state_attr);
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
-               sysfs_remove_files(&dev->primary->kdev->kobj, vlv_attrs);
+       struct device *kdev = dev_priv->drm.primary->kdev;
+
+       sysfs_remove_bin_file(&kdev->kobj, &error_state_attr);
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               sysfs_remove_files(&kdev->kobj, vlv_attrs);
        else
-               sysfs_remove_files(&dev->primary->kdev->kobj, gen6_attrs);
-       device_remove_bin_file(dev->primary->kdev,  &dpf_attrs_1);
-       device_remove_bin_file(dev->primary->kdev,  &dpf_attrs);
+               sysfs_remove_files(&kdev->kobj, gen6_attrs);
+       device_remove_bin_file(kdev,  &dpf_attrs_1);
+       device_remove_bin_file(kdev,  &dpf_attrs);
 #ifdef CONFIG_PM
-       sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6_attr_group);
-       sysfs_unmerge_group(&dev->primary->kdev->kobj, &rc6p_attr_group);
+       sysfs_unmerge_group(&kdev->kobj, &rc6_attr_group);
+       sysfs_unmerge_group(&kdev->kobj, &rc6p_attr_group);
 #endif
 }
index 534154e..1787980 100644 (file)
@@ -394,25 +394,27 @@ DEFINE_EVENT(i915_gem_object, i915_gem_object_destroy,
 );
 
 TRACE_EVENT(i915_gem_evict,
-           TP_PROTO(struct drm_device *dev, u32 size, u32 align, unsigned flags),
-           TP_ARGS(dev, size, align, flags),
+           TP_PROTO(struct i915_address_space *vm, u32 size, u32 align, unsigned int flags),
+           TP_ARGS(vm, size, align, flags),
 
            TP_STRUCT__entry(
                             __field(u32, dev)
+                            __field(struct i915_address_space *, vm)
                             __field(u32, size)
                             __field(u32, align)
-                            __field(unsigned, flags)
+                            __field(unsigned int, flags)
                            ),
 
            TP_fast_assign(
-                          __entry->dev = dev->primary->index;
+                          __entry->dev = vm->dev->primary->index;
+                          __entry->vm = vm;
                           __entry->size = size;
                           __entry->align = align;
                           __entry->flags = flags;
                          ),
 
-           TP_printk("dev=%d, size=%d, align=%d %s",
-                     __entry->dev, __entry->size, __entry->align,
+           TP_printk("dev=%d, vm=%p, size=%d, align=%d %s",
+                     __entry->dev, __entry->vm, __entry->size, __entry->align,
                      __entry->flags & PIN_MAPPABLE ? ", mappable" : "")
 );
 
@@ -449,10 +451,9 @@ TRACE_EVENT(i915_gem_evict_vm,
 );
 
 TRACE_EVENT(i915_gem_ring_sync_to,
-           TP_PROTO(struct drm_i915_gem_request *to_req,
-                    struct intel_engine_cs *from,
-                    struct drm_i915_gem_request *req),
-           TP_ARGS(to_req, from, req),
+           TP_PROTO(struct drm_i915_gem_request *to,
+                    struct drm_i915_gem_request *from),
+           TP_ARGS(to, from),
 
            TP_STRUCT__entry(
                             __field(u32, dev)
@@ -463,9 +464,9 @@ TRACE_EVENT(i915_gem_ring_sync_to,
 
            TP_fast_assign(
                           __entry->dev = from->i915->drm.primary->index;
-                          __entry->sync_from = from->id;
-                          __entry->sync_to = to_req->engine->id;
-                          __entry->seqno = i915_gem_request_get_seqno(req);
+                          __entry->sync_from = from->engine->id;
+                          __entry->sync_to = to->engine->id;
+                          __entry->seqno = from->fence.seqno;
                           ),
 
            TP_printk("dev=%u, sync-from=%u, sync-to=%u, seqno=%u",
@@ -488,9 +489,9 @@ TRACE_EVENT(i915_gem_ring_dispatch,
            TP_fast_assign(
                           __entry->dev = req->i915->drm.primary->index;
                           __entry->ring = req->engine->id;
-                          __entry->seqno = req->seqno;
+                          __entry->seqno = req->fence.seqno;
                           __entry->flags = flags;
-                          intel_engine_enable_signaling(req);
+                          fence_enable_sw_signaling(&req->fence);
                           ),
 
            TP_printk("dev=%u, ring=%u, seqno=%u, flags=%x",
@@ -533,7 +534,7 @@ DECLARE_EVENT_CLASS(i915_gem_request,
            TP_fast_assign(
                           __entry->dev = req->i915->drm.primary->index;
                           __entry->ring = req->engine->id;
-                          __entry->seqno = req->seqno;
+                          __entry->seqno = req->fence.seqno;
                           ),
 
            TP_printk("dev=%u, ring=%u, seqno=%u",
@@ -595,7 +596,7 @@ TRACE_EVENT(i915_gem_request_wait_begin,
            TP_fast_assign(
                           __entry->dev = req->i915->drm.primary->index;
                           __entry->ring = req->engine->id;
-                          __entry->seqno = req->seqno;
+                          __entry->seqno = req->fence.seqno;
                           __entry->blocking =
                                     mutex_is_locked(&req->i915->drm.struct_mutex);
                           ),
index b81cfb3..dae340c 100644 (file)
@@ -94,6 +94,7 @@ static struct _balloon_info_ bl_info;
 
 /**
  * intel_vgt_deballoon - deballoon reserved graphics address trunks
+ * @dev_priv: i915 device private data
  *
  * This function is called to deallocate the ballooned-out graphic memory, when
  * driver is unloaded or when ballooning fails.
@@ -135,7 +136,7 @@ static int vgt_balloon_space(struct drm_mm *mm,
 
 /**
  * intel_vgt_balloon - balloon out reserved graphics address trunks
- * @dev: drm device
+ * @dev_priv: i915 device private data
  *
  * This function is called at the initialization stage, to balloon out the
  * graphic address space allocated to other vGPUs, by marking these spaces as
@@ -152,27 +153,27 @@ static int vgt_balloon_space(struct drm_mm *mm,
  * host point of view, the graphic address space is partitioned by multiple
  * vGPUs in different VMs. ::
  *
- *                        vGPU1 view         Host view
- *             0 ------> +-----------+     +-----------+
- *               ^       |###########|     |   vGPU3   |
- *               |       |###########|     +-----------+
- *               |       |###########|     |   vGPU2   |
- *               |       +-----------+     +-----------+
- *        mappable GM    | available | ==> |   vGPU1   |
- *               |       +-----------+     +-----------+
- *               |       |###########|     |           |
- *               v       |###########|     |   Host    |
- *               +=======+===========+     +===========+
- *               ^       |###########|     |   vGPU3   |
- *               |       |###########|     +-----------+
- *               |       |###########|     |   vGPU2   |
- *               |       +-----------+     +-----------+
- *      unmappable GM    | available | ==> |   vGPU1   |
- *               |       +-----------+     +-----------+
- *               |       |###########|     |           |
- *               |       |###########|     |   Host    |
- *               v       |###########|     |           |
- * total GM size ------> +-----------+     +-----------+
+ *                         vGPU1 view         Host view
+ *              0 ------> +-----------+     +-----------+
+ *                ^       |###########|     |   vGPU3   |
+ *                |       |###########|     +-----------+
+ *                |       |###########|     |   vGPU2   |
+ *                |       +-----------+     +-----------+
+ *         mappable GM    | available | ==> |   vGPU1   |
+ *                |       +-----------+     +-----------+
+ *                |       |###########|     |           |
+ *                v       |###########|     |   Host    |
+ *                +=======+===========+     +===========+
+ *                ^       |###########|     |   vGPU3   |
+ *                |       |###########|     +-----------+
+ *                |       |###########|     |   vGPU2   |
+ *                |       +-----------+     +-----------+
+ *       unmappable GM    | available | ==> |   vGPU1   |
+ *                |       +-----------+     +-----------+
+ *                |       |###########|     |           |
+ *                |       |###########|     |   Host    |
+ *                v       |###########|     |           |
+ *  total GM size ------> +-----------+     +-----------+
  *
  * Returns:
  * zero on success, non-zero if configuration invalid or ballooning failed
index 7de7721..b82de30 100644 (file)
@@ -55,7 +55,7 @@ intel_create_plane_state(struct drm_plane *plane)
                return NULL;
 
        state->base.plane = plane;
-       state->base.rotation = BIT(DRM_ROTATE_0);
+       state->base.rotation = DRM_ROTATE_0;
        state->ckey.flags = I915_SET_COLORKEY_NONE;
 
        return state;
@@ -134,20 +134,6 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
 
        crtc_state = to_intel_crtc_state(drm_crtc_state);
 
-       /*
-        * The original src/dest coordinates are stored in state->base, but
-        * we want to keep another copy internal to our driver that we can
-        * clip/modify ourselves.
-        */
-       intel_state->src.x1 = state->src_x;
-       intel_state->src.y1 = state->src_y;
-       intel_state->src.x2 = state->src_x + state->src_w;
-       intel_state->src.y2 = state->src_y + state->src_h;
-       intel_state->dst.x1 = state->crtc_x;
-       intel_state->dst.y1 = state->crtc_y;
-       intel_state->dst.x2 = state->crtc_x + state->crtc_w;
-       intel_state->dst.y2 = state->crtc_y + state->crtc_h;
-
        /* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */
        intel_state->clip.x1 = 0;
        intel_state->clip.y1 = 0;
@@ -157,6 +143,7 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
                crtc_state->base.enable ? crtc_state->pipe_src_h : 0;
 
        if (state->fb && intel_rotation_90_or_270(state->rotation)) {
+               char *format_name;
                if (!(state->fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
                        state->fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED)) {
                        DRM_DEBUG_KMS("Y/Yf tiling required for 90/270!\n");
@@ -171,8 +158,9 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
                switch (state->fb->pixel_format) {
                case DRM_FORMAT_C8:
                case DRM_FORMAT_RGB565:
-                       DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n",
-                                       drm_get_format_name(state->fb->pixel_format));
+                       format_name = drm_get_format_name(state->fb->pixel_format);
+                       DRM_DEBUG_KMS("Unsupported pixel format %s for 90/270!\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
 
                default:
@@ -180,7 +168,7 @@ static int intel_plane_atomic_check(struct drm_plane *plane,
                }
        }
 
-       intel_state->visible = false;
+       intel_state->base.visible = false;
        ret = intel_plane->check_plane(plane, crtc_state, intel_state);
        if (ret)
                return ret;
@@ -196,7 +184,7 @@ static void intel_plane_atomic_update(struct drm_plane *plane,
                to_intel_plane_state(plane->state);
        struct drm_crtc *crtc = plane->state->crtc ?: old_state->crtc;
 
-       if (intel_state->visible)
+       if (intel_state->base.visible)
                intel_plane->update_plane(plane,
                                          to_intel_crtc_state(crtc->state),
                                          intel_state);
index d32f586..6c70a5b 100644 (file)
  * related registers. (The notable exception is the power management, not
  * covered here.)
  *
- * The struct i915_audio_component is used to interact between the graphics
- * and audio drivers. The struct i915_audio_component_ops *ops in it is
+ * The struct &i915_audio_component is used to interact between the graphics
+ * and audio drivers. The struct &i915_audio_component_ops @ops in it is
  * defined in graphics driver and called in audio driver. The
- * struct i915_audio_component_audio_ops *audio_ops is called from i915 driver.
+ * struct &i915_audio_component_audio_ops @audio_ops is called from i915 driver.
  */
 
 static const struct {
@@ -359,9 +359,7 @@ static void ilk_audio_codec_disable(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
-       struct intel_digital_port *intel_dig_port =
-               enc_to_dig_port(&encoder->base);
-       enum port port = intel_dig_port->port;
+       enum port port = enc_to_dig_port(&encoder->base)->port;
        enum pipe pipe = intel_crtc->pipe;
        uint32_t tmp, eldv;
        i915_reg_t aud_config, aud_cntrl_st2;
@@ -407,13 +405,10 @@ static void ilk_audio_codec_enable(struct drm_connector *connector,
 {
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
-       struct intel_digital_port *intel_dig_port =
-               enc_to_dig_port(&encoder->base);
-       enum port port = intel_dig_port->port;
+       enum port port = enc_to_dig_port(&encoder->base)->port;
        enum pipe pipe = intel_crtc->pipe;
        uint8_t *eld = connector->eld;
-       uint32_t eldv;
-       uint32_t tmp;
+       uint32_t tmp, eldv;
        int len, i;
        i915_reg_t hdmiw_hdmiedid, aud_config, aud_cntl_st, aud_cntrl_st2;
 
@@ -581,26 +576,26 @@ void intel_init_audio_hooks(struct drm_i915_private *dev_priv)
        }
 }
 
-static void i915_audio_component_get_power(struct device *dev)
+static void i915_audio_component_get_power(struct device *kdev)
 {
-       intel_display_power_get(dev_to_i915(dev), POWER_DOMAIN_AUDIO);
+       intel_display_power_get(kdev_to_i915(kdev), POWER_DOMAIN_AUDIO);
 }
 
-static void i915_audio_component_put_power(struct device *dev)
+static void i915_audio_component_put_power(struct device *kdev)
 {
-       intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO);
+       intel_display_power_put(kdev_to_i915(kdev), POWER_DOMAIN_AUDIO);
 }
 
-static void i915_audio_component_codec_wake_override(struct device *dev,
+static void i915_audio_component_codec_wake_override(struct device *kdev,
                                                     bool enable)
 {
-       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
        u32 tmp;
 
        if (!IS_SKYLAKE(dev_priv) && !IS_KABYLAKE(dev_priv))
                return;
 
-       i915_audio_component_get_power(dev);
+       i915_audio_component_get_power(kdev);
 
        /*
         * Enable/disable generating the codec wake signal, overriding the
@@ -618,13 +613,13 @@ static void i915_audio_component_codec_wake_override(struct device *dev,
                usleep_range(1000, 1500);
        }
 
-       i915_audio_component_put_power(dev);
+       i915_audio_component_put_power(kdev);
 }
 
 /* Get CDCLK in kHz  */
-static int i915_audio_component_get_cdclk_freq(struct device *dev)
+static int i915_audio_component_get_cdclk_freq(struct device *kdev)
 {
-       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
 
        if (WARN_ON_ONCE(!HAS_DDI(dev_priv)))
                return -ENODEV;
@@ -632,10 +627,10 @@ static int i915_audio_component_get_cdclk_freq(struct device *dev)
        return dev_priv->cdclk_freq;
 }
 
-static int i915_audio_component_sync_audio_rate(struct device *dev,
+static int i915_audio_component_sync_audio_rate(struct device *kdev,
                                                int port, int rate)
 {
-       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
        struct intel_encoder *intel_encoder;
        struct intel_crtc *crtc;
        struct drm_display_mode *mode;
@@ -652,7 +647,7 @@ static int i915_audio_component_sync_audio_rate(struct device *dev,
            !IS_HASWELL(dev_priv))
                return 0;
 
-       i915_audio_component_get_power(dev);
+       i915_audio_component_get_power(kdev);
        mutex_lock(&dev_priv->av_mutex);
        /* 1. get the pipe */
        intel_encoder = dev_priv->dig_port_map[port];
@@ -703,15 +698,15 @@ static int i915_audio_component_sync_audio_rate(struct device *dev,
 
  unlock:
        mutex_unlock(&dev_priv->av_mutex);
-       i915_audio_component_put_power(dev);
+       i915_audio_component_put_power(kdev);
        return err;
 }
 
-static int i915_audio_component_get_eld(struct device *dev, int port,
+static int i915_audio_component_get_eld(struct device *kdev, int port,
                                        bool *enabled,
                                        unsigned char *buf, int max_bytes)
 {
-       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(kdev);
        struct intel_encoder *intel_encoder;
        struct intel_digital_port *intel_dig_port;
        const u8 *eld;
@@ -745,11 +740,11 @@ static const struct i915_audio_component_ops i915_audio_component_ops = {
        .get_eld        = i915_audio_component_get_eld,
 };
 
-static int i915_audio_component_bind(struct device *i915_dev,
-                                    struct device *hda_dev, void *data)
+static int i915_audio_component_bind(struct device *i915_kdev,
+                                    struct device *hda_kdev, void *data)
 {
        struct i915_audio_component *acomp = data;
-       struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev);
        int i;
 
        if (WARN_ON(acomp->ops || acomp->dev))
@@ -757,7 +752,7 @@ static int i915_audio_component_bind(struct device *i915_dev,
 
        drm_modeset_lock_all(&dev_priv->drm);
        acomp->ops = &i915_audio_component_ops;
-       acomp->dev = i915_dev;
+       acomp->dev = i915_kdev;
        BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS);
        for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++)
                acomp->aud_sample_rate[i] = 0;
@@ -767,11 +762,11 @@ static int i915_audio_component_bind(struct device *i915_dev,
        return 0;
 }
 
-static void i915_audio_component_unbind(struct device *i915_dev,
-                                       struct device *hda_dev, void *data)
+static void i915_audio_component_unbind(struct device *i915_kdev,
+                                       struct device *hda_kdev, void *data)
 {
        struct i915_audio_component *acomp = data;
-       struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+       struct drm_i915_private *dev_priv = kdev_to_i915(i915_kdev);
 
        drm_modeset_lock_all(&dev_priv->drm);
        acomp->ops = NULL;
index b074f3d..495611b 100644 (file)
 
 #include "i915_drv.h"
 
+static void intel_breadcrumbs_hangcheck(unsigned long data)
+{
+       struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
+       struct intel_breadcrumbs *b = &engine->breadcrumbs;
+
+       if (!b->irq_enabled)
+               return;
+
+       if (time_before(jiffies, b->timeout)) {
+               mod_timer(&b->hangcheck, b->timeout);
+               return;
+       }
+
+       DRM_DEBUG("Hangcheck timer elapsed... %s idle\n", engine->name);
+       set_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
+       mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
+
+       /* Ensure that even if the GPU hangs, we get woken up.
+        *
+        * However, note that if no one is waiting, we never notice
+        * a gpu hang. Eventually, we will have to wait for a resource
+        * held by the GPU and so trigger a hangcheck. In the most
+        * pathological case, this will be upon memory starvation! To
+        * prevent this, we also queue the hangcheck from the retire
+        * worker.
+        */
+       i915_queue_hangcheck(engine->i915);
+}
+
+static unsigned long wait_timeout(void)
+{
+       return round_jiffies_up(jiffies + DRM_I915_HANGCHECK_JIFFIES);
+}
+
 static void intel_breadcrumbs_fake_irq(unsigned long data)
 {
        struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
@@ -37,10 +71,8 @@ static void intel_breadcrumbs_fake_irq(unsigned long data)
         * every jiffie in order to kick the oldest waiter to do the
         * coherent seqno check.
         */
-       rcu_read_lock();
        if (intel_engine_wakeup(engine))
                mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
-       rcu_read_unlock();
 }
 
 static void irq_enable(struct intel_engine_cs *engine)
@@ -91,17 +123,13 @@ static void __intel_breadcrumbs_enable_irq(struct intel_breadcrumbs *b)
        }
 
        if (!b->irq_enabled ||
-           test_bit(engine->id, &i915->gpu_error.missed_irq_rings))
+           test_bit(engine->id, &i915->gpu_error.missed_irq_rings)) {
                mod_timer(&b->fake_irq, jiffies + 1);
-
-       /* Ensure that even if the GPU hangs, we get woken up.
-        *
-        * However, note that if no one is waiting, we never notice
-        * a gpu hang. Eventually, we will have to wait for a resource
-        * held by the GPU and so trigger a hangcheck. In the most
-        * pathological case, this will be upon memory starvation!
-        */
-       i915_queue_hangcheck(i915);
+       } else {
+               /* Ensure we never sleep indefinitely */
+               GEM_BUG_ON(!time_after(b->timeout, jiffies));
+               mod_timer(&b->hangcheck, b->timeout);
+       }
 }
 
 static void __intel_breadcrumbs_disable_irq(struct intel_breadcrumbs *b)
@@ -204,7 +232,7 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine,
        }
        rb_link_node(&wait->node, parent, p);
        rb_insert_color(&wait->node, &b->waiters);
-       GEM_BUG_ON(!first && !b->irq_seqno_bh);
+       GEM_BUG_ON(!first && !rcu_access_pointer(b->irq_seqno_bh));
 
        if (completed) {
                struct rb_node *next = rb_next(completed);
@@ -212,8 +240,9 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine,
                GEM_BUG_ON(!next && !first);
                if (next && next != &wait->node) {
                        GEM_BUG_ON(first);
+                       b->timeout = wait_timeout();
                        b->first_wait = to_wait(next);
-                       smp_store_mb(b->irq_seqno_bh, b->first_wait->tsk);
+                       rcu_assign_pointer(b->irq_seqno_bh, b->first_wait->tsk);
                        /* As there is a delay between reading the current
                         * seqno, processing the completed tasks and selecting
                         * the next waiter, we may have missed the interrupt
@@ -238,8 +267,9 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine,
 
        if (first) {
                GEM_BUG_ON(rb_first(&b->waiters) != &wait->node);
+               b->timeout = wait_timeout();
                b->first_wait = wait;
-               smp_store_mb(b->irq_seqno_bh, wait->tsk);
+               rcu_assign_pointer(b->irq_seqno_bh, wait->tsk);
                /* After assigning ourselves as the new bottom-half, we must
                 * perform a cursory check to prevent a missed interrupt.
                 * Either we miss the interrupt whilst programming the hardware,
@@ -250,7 +280,7 @@ static bool __intel_engine_add_wait(struct intel_engine_cs *engine,
                 */
                __intel_breadcrumbs_enable_irq(b);
        }
-       GEM_BUG_ON(!b->irq_seqno_bh);
+       GEM_BUG_ON(!rcu_access_pointer(b->irq_seqno_bh));
        GEM_BUG_ON(!b->first_wait);
        GEM_BUG_ON(rb_first(&b->waiters) != &b->first_wait->node);
 
@@ -270,11 +300,6 @@ bool intel_engine_add_wait(struct intel_engine_cs *engine,
        return first;
 }
 
-void intel_engine_enable_fake_irq(struct intel_engine_cs *engine)
-{
-       mod_timer(&engine->breadcrumbs.fake_irq, jiffies + 1);
-}
-
 static inline bool chain_wakeup(struct rb_node *rb, int priority)
 {
        return rb && to_wait(rb)->tsk->prio <= priority;
@@ -310,7 +335,7 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine,
                const int priority = wakeup_priority(b, wait->tsk);
                struct rb_node *next;
 
-               GEM_BUG_ON(b->irq_seqno_bh != wait->tsk);
+               GEM_BUG_ON(rcu_access_pointer(b->irq_seqno_bh) != wait->tsk);
 
                /* We are the current bottom-half. Find the next candidate,
                 * the first waiter in the queue on the remaining oldest
@@ -352,14 +377,15 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine,
                         * the interrupt, or if we have to handle an
                         * exception rather than a seqno completion.
                         */
+                       b->timeout = wait_timeout();
                        b->first_wait = to_wait(next);
-                       smp_store_mb(b->irq_seqno_bh, b->first_wait->tsk);
+                       rcu_assign_pointer(b->irq_seqno_bh, b->first_wait->tsk);
                        if (b->first_wait->seqno != wait->seqno)
                                __intel_breadcrumbs_enable_irq(b);
-                       wake_up_process(b->irq_seqno_bh);
+                       wake_up_process(b->first_wait->tsk);
                } else {
                        b->first_wait = NULL;
-                       WRITE_ONCE(b->irq_seqno_bh, NULL);
+                       rcu_assign_pointer(b->irq_seqno_bh, NULL);
                        __intel_breadcrumbs_disable_irq(b);
                }
        } else {
@@ -373,7 +399,7 @@ out_unlock:
        GEM_BUG_ON(b->first_wait == wait);
        GEM_BUG_ON(rb_first(&b->waiters) !=
                   (b->first_wait ? &b->first_wait->node : NULL));
-       GEM_BUG_ON(!b->irq_seqno_bh ^ RB_EMPTY_ROOT(&b->waiters));
+       GEM_BUG_ON(!rcu_access_pointer(b->irq_seqno_bh) ^ RB_EMPTY_ROOT(&b->waiters));
        spin_unlock(&b->lock);
 }
 
@@ -437,6 +463,10 @@ static int intel_breadcrumbs_signaler(void *arg)
                        intel_engine_remove_wait(engine,
                                                 &request->signaling.wait);
 
+                       local_bh_disable();
+                       fence_signal(&request->fence);
+                       local_bh_enable(); /* kick start the tasklets */
+
                        /* Find the next oldest signal. Note that as we have
                         * not been holding the lock, another client may
                         * have installed an even older signal than the one
@@ -452,7 +482,7 @@ static int intel_breadcrumbs_signaler(void *arg)
                        rb_erase(&request->signaling.node, &b->signals);
                        spin_unlock(&b->lock);
 
-                       i915_gem_request_unreference(request);
+                       i915_gem_request_put(request);
                } else {
                        if (kthread_should_stop())
                                break;
@@ -472,18 +502,14 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
        struct rb_node *parent, **p;
        bool first, wakeup;
 
-       if (unlikely(READ_ONCE(request->signaling.wait.tsk)))
-               return;
-
-       spin_lock(&b->lock);
-       if (unlikely(request->signaling.wait.tsk)) {
-               wakeup = false;
-               goto unlock;
-       }
+       /* locked by fence_enable_sw_signaling() */
+       assert_spin_locked(&request->lock);
 
        request->signaling.wait.tsk = b->signaler;
-       request->signaling.wait.seqno = request->seqno;
-       i915_gem_request_reference(request);
+       request->signaling.wait.seqno = request->fence.seqno;
+       i915_gem_request_get(request);
+
+       spin_lock(&b->lock);
 
        /* First add ourselves into the list of waiters, but register our
         * bottom-half as the signaller thread. As per usual, only the oldest
@@ -504,8 +530,8 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
        p = &b->signals.rb_node;
        while (*p) {
                parent = *p;
-               if (i915_seqno_passed(request->seqno,
-                                     to_signaler(parent)->seqno)) {
+               if (i915_seqno_passed(request->fence.seqno,
+                                     to_signaler(parent)->fence.seqno)) {
                        p = &parent->rb_right;
                        first = false;
                } else {
@@ -517,7 +543,6 @@ void intel_engine_enable_signaling(struct drm_i915_gem_request *request)
        if (first)
                smp_store_mb(b->first_signal, request);
 
-unlock:
        spin_unlock(&b->lock);
 
        if (wakeup)
@@ -533,6 +558,9 @@ int intel_engine_init_breadcrumbs(struct intel_engine_cs *engine)
        setup_timer(&b->fake_irq,
                    intel_breadcrumbs_fake_irq,
                    (unsigned long)engine);
+       setup_timer(&b->hangcheck,
+                   intel_breadcrumbs_hangcheck,
+                   (unsigned long)engine);
 
        /* Spawn a thread to provide a common bottom-half for all signals.
         * As this is an asynchronous interface we cannot steal the current
@@ -550,6 +578,36 @@ int intel_engine_init_breadcrumbs(struct intel_engine_cs *engine)
        return 0;
 }
 
+static void cancel_fake_irq(struct intel_engine_cs *engine)
+{
+       struct intel_breadcrumbs *b = &engine->breadcrumbs;
+
+       del_timer_sync(&b->hangcheck);
+       del_timer_sync(&b->fake_irq);
+       clear_bit(engine->id, &engine->i915->gpu_error.missed_irq_rings);
+}
+
+void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine)
+{
+       struct intel_breadcrumbs *b = &engine->breadcrumbs;
+
+       cancel_fake_irq(engine);
+       spin_lock(&b->lock);
+
+       __intel_breadcrumbs_disable_irq(b);
+       if (intel_engine_has_waiter(engine)) {
+               b->timeout = wait_timeout();
+               __intel_breadcrumbs_enable_irq(b);
+               if (READ_ONCE(b->irq_posted))
+                       wake_up_process(b->first_wait->tsk);
+       } else {
+               /* sanitize the IMR and unmask any auxiliary interrupts */
+               irq_disable(engine);
+       }
+
+       spin_unlock(&b->lock);
+}
+
 void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine)
 {
        struct intel_breadcrumbs *b = &engine->breadcrumbs;
@@ -557,7 +615,7 @@ void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine)
        if (!IS_ERR_OR_NULL(b->signaler))
                kthread_stop(b->signaler);
 
-       del_timer_sync(&b->fake_irq);
+       cancel_fake_irq(engine);
 }
 
 unsigned int intel_kick_waiters(struct drm_i915_private *i915)
@@ -570,11 +628,9 @@ unsigned int intel_kick_waiters(struct drm_i915_private *i915)
         * RCU lock, i.e. as we call wake_up_process() we must be holding the
         * rcu_read_lock().
         */
-       rcu_read_lock();
        for_each_engine(engine, i915)
                if (unlikely(intel_engine_wakeup(engine)))
                        mask |= intel_engine_flag(engine);
-       rcu_read_unlock();
 
        return mask;
 }
index bc0fef3..95a7277 100644 (file)
@@ -100,13 +100,14 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int i, pipe = intel_crtc->pipe;
        uint16_t coeffs[9] = { 0, };
+       struct intel_crtc_state *intel_crtc_state = to_intel_crtc_state(crtc_state);
 
        if (crtc_state->ctm) {
                struct drm_color_ctm *ctm =
                        (struct drm_color_ctm *)crtc_state->ctm->data;
                uint64_t input[9] = { 0, };
 
-               if (intel_crtc->config->limited_color_range) {
+               if (intel_crtc_state->limited_color_range) {
                        ctm_mult_by_limited(input, ctm->matrix);
                } else {
                        for (i = 0; i < ARRAY_SIZE(input); i++)
@@ -158,7 +159,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
                 * into consideration.
                 */
                for (i = 0; i < 3; i++) {
-                       if (intel_crtc->config->limited_color_range)
+                       if (intel_crtc_state->limited_color_range)
                                coeffs[i * 3 + i] =
                                        I9XX_CSC_COEFF_LIMITED_RANGE;
                        else
@@ -182,7 +183,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
        if (INTEL_INFO(dev)->gen > 6) {
                uint16_t postoff = 0;
 
-               if (intel_crtc->config->limited_color_range)
+               if (intel_crtc_state->limited_color_range)
                        postoff = (16 * (1 << 12) / 255) & 0x1fff;
 
                I915_WRITE(PIPE_CSC_POSTOFF_HI(pipe), postoff);
@@ -193,7 +194,7 @@ static void i9xx_load_csc_matrix(struct drm_crtc_state *crtc_state)
        } else {
                uint32_t mode = CSC_MODE_YUV_TO_RGB;
 
-               if (intel_crtc->config->limited_color_range)
+               if (intel_crtc_state->limited_color_range)
                        mode |= CSC_BLACK_SCREEN_OFFSET;
 
                I915_WRITE(PIPE_CSC_MODE(pipe), mode);
@@ -263,7 +264,8 @@ void intel_color_set_csc(struct drm_crtc_state *crtc_state)
 
 /* Loads the legacy palette/gamma unit for the CRTC. */
 static void i9xx_load_luts_internal(struct drm_crtc *crtc,
-                                   struct drm_property_blob *blob)
+                                   struct drm_property_blob *blob,
+                                   struct intel_crtc_state *crtc_state)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -272,7 +274,7 @@ static void i9xx_load_luts_internal(struct drm_crtc *crtc,
        int i;
 
        if (HAS_GMCH_DISPLAY(dev)) {
-               if (intel_crtc_has_type(intel_crtc->config, INTEL_OUTPUT_DSI))
+               if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DSI))
                        assert_dsi_pll_enabled(dev_priv);
                else
                        assert_pll_enabled(dev_priv, pipe);
@@ -305,7 +307,8 @@ static void i9xx_load_luts_internal(struct drm_crtc *crtc,
 
 static void i9xx_load_luts(struct drm_crtc_state *crtc_state)
 {
-       i9xx_load_luts_internal(crtc_state->crtc, crtc_state->gamma_lut);
+       i9xx_load_luts_internal(crtc_state->crtc, crtc_state->gamma_lut,
+                               to_intel_crtc_state(crtc_state));
 }
 
 /* Loads the legacy palette/gamma unit for the CRTC on Haswell. */
@@ -323,7 +326,7 @@ static void haswell_load_luts(struct drm_crtc_state *crtc_state)
         * Workaround : Do not read or write the pipe palette/gamma data while
         * GAMMA_MODE is configured for split gamma and IPS_CTL has IPS enabled.
         */
-       if (IS_HASWELL(dev) && intel_crtc->config->ips_enabled &&
+       if (IS_HASWELL(dev) && intel_crtc_state->ips_enabled &&
            (intel_crtc_state->gamma_mode == GAMMA_MODE_MODE_SPLIT)) {
                hsw_disable_ips(intel_crtc);
                reenable_ips = true;
@@ -436,7 +439,8 @@ static void cherryview_load_luts(struct drm_crtc_state *state)
                /* Turn off degamma/gamma on CGM block. */
                I915_WRITE(CGM_PIPE_MODE(pipe),
                           (state->ctm ? CGM_PIPE_MODE_CSC : 0));
-               i9xx_load_luts_internal(crtc, state->gamma_lut);
+               i9xx_load_luts_internal(crtc, state->gamma_lut,
+                                       to_intel_crtc_state(state));
                return;
        }
 
@@ -479,7 +483,7 @@ static void cherryview_load_luts(struct drm_crtc_state *state)
         * Also program a linear LUT in the legacy block (behind the
         * CGM block).
         */
-       i9xx_load_luts_internal(crtc, NULL);
+       i9xx_load_luts_internal(crtc, NULL, to_intel_crtc_state(state));
 }
 
 void intel_color_load_luts(struct drm_crtc_state *crtc_state)
index 827b6ef..dfbcf16 100644 (file)
@@ -143,13 +143,15 @@ static void hsw_crt_get_config(struct intel_encoder *encoder,
 
 /* Note: The caller is required to filter out dpms modes not supported by the
  * platform. */
-static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
+static void intel_crt_set_dpms(struct intel_encoder *encoder,
+                              struct intel_crtc_state *crtc_state,
+                              int mode)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crt *crt = intel_encoder_to_crt(encoder);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode;
        u32 adpa;
 
        if (INTEL_INFO(dev)->gen >= 5)
@@ -193,23 +195,45 @@ static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
        I915_WRITE(crt->adpa_reg, adpa);
 }
 
-static void intel_disable_crt(struct intel_encoder *encoder)
+static void intel_disable_crt(struct intel_encoder *encoder,
+                             struct intel_crtc_state *old_crtc_state,
+                             struct drm_connector_state *old_conn_state)
 {
-       intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF);
+       intel_crt_set_dpms(encoder, old_crtc_state, DRM_MODE_DPMS_OFF);
 }
 
-static void pch_disable_crt(struct intel_encoder *encoder)
+static void pch_disable_crt(struct intel_encoder *encoder,
+                           struct intel_crtc_state *old_crtc_state,
+                           struct drm_connector_state *old_conn_state)
 {
 }
 
-static void pch_post_disable_crt(struct intel_encoder *encoder)
+static void pch_post_disable_crt(struct intel_encoder *encoder,
+                                struct intel_crtc_state *old_crtc_state,
+                                struct drm_connector_state *old_conn_state)
 {
-       intel_disable_crt(encoder);
+       intel_disable_crt(encoder, old_crtc_state, old_conn_state);
 }
 
-static void intel_enable_crt(struct intel_encoder *encoder)
+static void hsw_post_disable_crt(struct intel_encoder *encoder,
+                                struct intel_crtc_state *old_crtc_state,
+                                struct drm_connector_state *old_conn_state)
 {
-       intel_crt_set_dpms(encoder, DRM_MODE_DPMS_ON);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+
+       pch_post_disable_crt(encoder, old_crtc_state, old_conn_state);
+
+       lpt_disable_pch_transcoder(dev_priv);
+       lpt_disable_iclkip(dev_priv);
+
+       intel_ddi_fdi_post_disable(encoder, old_crtc_state, old_conn_state);
+}
+
+static void intel_enable_crt(struct intel_encoder *encoder,
+                            struct intel_crtc_state *pipe_config,
+                            struct drm_connector_state *conn_state)
+{
+       intel_crt_set_dpms(encoder, pipe_config, DRM_MODE_DPMS_ON);
 }
 
 static enum drm_mode_status
@@ -253,7 +277,8 @@ intel_crt_mode_valid(struct drm_connector *connector,
 }
 
 static bool intel_crt_compute_config(struct intel_encoder *encoder,
-                                    struct intel_crtc_state *pipe_config)
+                                    struct intel_crtc_state *pipe_config,
+                                    struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
 
@@ -894,6 +919,7 @@ void intel_crt_init(struct drm_device *dev)
        if (HAS_DDI(dev)) {
                crt->base.get_config = hsw_crt_get_config;
                crt->base.get_hw_state = intel_ddi_get_hw_state;
+               crt->base.post_disable = hsw_post_disable_crt;
        } else {
                crt->base.get_config = intel_crt_get_config;
                crt->base.get_hw_state = intel_crt_get_hw_state;
index c3b33a1..1ea0e1f 100644 (file)
  * onwards to drive newly added DMC (Display microcontroller) in display
  * engine to save and restore the state of display engine when it enter into
  * low-power state and comes back to normal.
- *
- * Firmware loading status will be one of the below states: FW_UNINITIALIZED,
- * FW_LOADED, FW_FAILED.
- *
- * Once the firmware is written into the registers status will be moved from
- * FW_UNINITIALIZED to FW_LOADED and for any erroneous condition status will
- * be moved to FW_FAILED.
  */
 
 #define I915_CSR_KBL "i915/kbl_dmc_ver1_01.bin"
index 1a7efac..15d47c8 100644 (file)
@@ -301,45 +301,34 @@ static const struct bxt_ddi_buf_trans bxt_ddi_translations_hdmi[] = {
        { 154, 0x9A, 1, 128, true },    /* 9:   1200            0   */
 };
 
-static void bxt_ddi_vswing_sequence(struct drm_i915_private *dev_priv,
-                                   u32 level, enum port port, int type);
-
-static void ddi_get_encoder_port(struct intel_encoder *intel_encoder,
-                                struct intel_digital_port **dig_port,
-                                enum port *port)
+enum port intel_ddi_get_encoder_port(struct intel_encoder *encoder)
 {
-       struct drm_encoder *encoder = &intel_encoder->base;
-
-       switch (intel_encoder->type) {
+       switch (encoder->type) {
        case INTEL_OUTPUT_DP_MST:
-               *dig_port = enc_to_mst(encoder)->primary;
-               *port = (*dig_port)->port;
-               break;
-       default:
-               WARN(1, "Invalid DDI encoder type %d\n", intel_encoder->type);
-               /* fallthrough and treat as unknown */
+               return enc_to_mst(&encoder->base)->primary->port;
        case INTEL_OUTPUT_DP:
        case INTEL_OUTPUT_EDP:
        case INTEL_OUTPUT_HDMI:
        case INTEL_OUTPUT_UNKNOWN:
-               *dig_port = enc_to_dig_port(encoder);
-               *port = (*dig_port)->port;
-               break;
+               return enc_to_dig_port(&encoder->base)->port;
        case INTEL_OUTPUT_ANALOG:
-               *dig_port = NULL;
-               *port = PORT_E;
-               break;
+               return PORT_E;
+       default:
+               MISSING_CASE(encoder->type);
+               return PORT_A;
        }
 }
 
-enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
+static const struct ddi_buf_trans *
+bdw_get_buf_trans_edp(struct drm_i915_private *dev_priv, int *n_entries)
 {
-       struct intel_digital_port *dig_port;
-       enum port port;
-
-       ddi_get_encoder_port(intel_encoder, &dig_port, &port);
-
-       return port;
+       if (dev_priv->vbt.edp.low_vswing) {
+               *n_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
+               return bdw_ddi_translations_edp;
+       } else {
+               *n_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
+               return bdw_ddi_translations_dp;
+       }
 }
 
 static const struct ddi_buf_trans *
@@ -424,37 +413,22 @@ static int intel_ddi_hdmi_level(struct drm_i915_private *dev_priv, enum port por
 
 /*
  * Starting with Haswell, DDI port buffers must be programmed with correct
- * values in advance. The buffer values are different for FDI and DP modes,
- * but the HDMI/DVI fields are shared among those. So we program the DDI
- * in either FDI or DP modes only, as HDMI connections will work with both
- * of those
+ * values in advance. This function programs the correct values for
+ * DP/eDP/FDI use cases.
  */
-void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
+void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        u32 iboost_bit = 0;
-       int i, n_hdmi_entries, n_dp_entries, n_edp_entries,
-           size;
-       int hdmi_level;
-       enum port port;
+       int i, n_dp_entries, n_edp_entries, size;
+       enum port port = intel_ddi_get_encoder_port(encoder);
        const struct ddi_buf_trans *ddi_translations_fdi;
        const struct ddi_buf_trans *ddi_translations_dp;
        const struct ddi_buf_trans *ddi_translations_edp;
-       const struct ddi_buf_trans *ddi_translations_hdmi;
        const struct ddi_buf_trans *ddi_translations;
 
-       port = intel_ddi_get_encoder_port(encoder);
-       hdmi_level = intel_ddi_hdmi_level(dev_priv, port);
-
-       if (IS_BROXTON(dev_priv)) {
-               if (encoder->type != INTEL_OUTPUT_HDMI)
-                       return;
-
-               /* Vswing programming for HDMI */
-               bxt_ddi_vswing_sequence(dev_priv, hdmi_level, port,
-                                       INTEL_OUTPUT_HDMI);
+       if (IS_BROXTON(dev_priv))
                return;
-       }
 
        if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
                ddi_translations_fdi = NULL;
@@ -462,12 +436,10 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
                                skl_get_buf_trans_dp(dev_priv, &n_dp_entries);
                ddi_translations_edp =
                                skl_get_buf_trans_edp(dev_priv, &n_edp_entries);
-               ddi_translations_hdmi =
-                               skl_get_buf_trans_hdmi(dev_priv, &n_hdmi_entries);
+
                /* If we're boosting the current, set bit 31 of trans1 */
-               if (dev_priv->vbt.ddi_port_info[port].hdmi_boost_level ||
-                   dev_priv->vbt.ddi_port_info[port].dp_boost_level)
-                       iboost_bit = 1<<31;
+               if (dev_priv->vbt.ddi_port_info[port].dp_boost_level)
+                       iboost_bit = DDI_BUF_BALANCE_LEG_ENABLE;
 
                if (WARN_ON(encoder->type == INTEL_OUTPUT_EDP &&
                            port != PORT_A && port != PORT_E &&
@@ -476,35 +448,20 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
        } else if (IS_BROADWELL(dev_priv)) {
                ddi_translations_fdi = bdw_ddi_translations_fdi;
                ddi_translations_dp = bdw_ddi_translations_dp;
-
-               if (dev_priv->vbt.edp.low_vswing) {
-                       ddi_translations_edp = bdw_ddi_translations_edp;
-                       n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
-               } else {
-                       ddi_translations_edp = bdw_ddi_translations_dp;
-                       n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
-               }
-
-               ddi_translations_hdmi = bdw_ddi_translations_hdmi;
-
+               ddi_translations_edp = bdw_get_buf_trans_edp(dev_priv, &n_edp_entries);
                n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
-               n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
        } else if (IS_HASWELL(dev_priv)) {
                ddi_translations_fdi = hsw_ddi_translations_fdi;
                ddi_translations_dp = hsw_ddi_translations_dp;
                ddi_translations_edp = hsw_ddi_translations_dp;
-               ddi_translations_hdmi = hsw_ddi_translations_hdmi;
                n_dp_entries = n_edp_entries = ARRAY_SIZE(hsw_ddi_translations_dp);
-               n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi);
        } else {
                WARN(1, "ddi translation table missing\n");
                ddi_translations_edp = bdw_ddi_translations_dp;
                ddi_translations_fdi = bdw_ddi_translations_fdi;
                ddi_translations_dp = bdw_ddi_translations_dp;
-               ddi_translations_hdmi = bdw_ddi_translations_hdmi;
                n_edp_entries = ARRAY_SIZE(bdw_ddi_translations_edp);
                n_dp_entries = ARRAY_SIZE(bdw_ddi_translations_dp);
-               n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
        }
 
        switch (encoder->type) {
@@ -513,7 +470,6 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
                size = n_edp_entries;
                break;
        case INTEL_OUTPUT_DP:
-       case INTEL_OUTPUT_HDMI:
                ddi_translations = ddi_translations_dp;
                size = n_dp_entries;
                break;
@@ -531,14 +487,48 @@ void intel_prepare_ddi_buffer(struct intel_encoder *encoder)
                I915_WRITE(DDI_BUF_TRANS_HI(port, i),
                           ddi_translations[i].trans2);
        }
+}
+
+/*
+ * Starting with Haswell, DDI port buffers must be programmed with correct
+ * values in advance. This function programs the correct values for
+ * HDMI/DVI use cases.
+ */
+static void intel_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       u32 iboost_bit = 0;
+       int n_hdmi_entries, hdmi_level;
+       enum port port = intel_ddi_get_encoder_port(encoder);
+       const struct ddi_buf_trans *ddi_translations_hdmi;
 
-       if (encoder->type != INTEL_OUTPUT_HDMI)
+       if (IS_BROXTON(dev_priv))
                return;
 
+       hdmi_level = intel_ddi_hdmi_level(dev_priv, port);
+
+       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+               ddi_translations_hdmi = skl_get_buf_trans_hdmi(dev_priv, &n_hdmi_entries);
+
+               /* If we're boosting the current, set bit 31 of trans1 */
+               if (dev_priv->vbt.ddi_port_info[port].hdmi_boost_level)
+                       iboost_bit = DDI_BUF_BALANCE_LEG_ENABLE;
+       } else if (IS_BROADWELL(dev_priv)) {
+               ddi_translations_hdmi = bdw_ddi_translations_hdmi;
+               n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
+       } else if (IS_HASWELL(dev_priv)) {
+               ddi_translations_hdmi = hsw_ddi_translations_hdmi;
+               n_hdmi_entries = ARRAY_SIZE(hsw_ddi_translations_hdmi);
+       } else {
+               WARN(1, "ddi translation table missing\n");
+               ddi_translations_hdmi = bdw_ddi_translations_hdmi;
+               n_hdmi_entries = ARRAY_SIZE(bdw_ddi_translations_hdmi);
+       }
+
        /* Entry 9 is for HDMI: */
-       I915_WRITE(DDI_BUF_TRANS_LO(port, i),
+       I915_WRITE(DDI_BUF_TRANS_LO(port, 9),
                   ddi_translations_hdmi[hdmi_level].trans1 | iboost_bit);
-       I915_WRITE(DDI_BUF_TRANS_HI(port, i),
+       I915_WRITE(DDI_BUF_TRANS_HI(port, 9),
                   ddi_translations_hdmi[hdmi_level].trans2);
 }
 
@@ -556,6 +546,27 @@ static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
        DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port));
 }
 
+static uint32_t hsw_pll_to_ddi_pll_sel(struct intel_shared_dpll *pll)
+{
+       switch (pll->id) {
+       case DPLL_ID_WRPLL1:
+               return PORT_CLK_SEL_WRPLL1;
+       case DPLL_ID_WRPLL2:
+               return PORT_CLK_SEL_WRPLL2;
+       case DPLL_ID_SPLL:
+               return PORT_CLK_SEL_SPLL;
+       case DPLL_ID_LCPLL_810:
+               return PORT_CLK_SEL_LCPLL_810;
+       case DPLL_ID_LCPLL_1350:
+               return PORT_CLK_SEL_LCPLL_1350;
+       case DPLL_ID_LCPLL_2700:
+               return PORT_CLK_SEL_LCPLL_2700;
+       default:
+               MISSING_CASE(pll->id);
+               return PORT_CLK_SEL_NONE;
+       }
+}
+
 /* Starting with Haswell, different DDI ports can work in FDI mode for
  * connection to the PCH-located connectors. For this, it is necessary to train
  * both the DDI port and PCH receiver for the desired DDI buffer settings.
@@ -571,11 +582,11 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *encoder;
-       u32 temp, i, rx_ctl_val;
+       u32 temp, i, rx_ctl_val, ddi_pll_sel;
 
        for_each_encoder_on_crtc(dev, crtc, encoder) {
                WARN_ON(encoder->type != INTEL_OUTPUT_ANALOG);
-               intel_prepare_ddi_buffer(encoder);
+               intel_prepare_dp_ddi_buffers(encoder);
        }
 
        /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the
@@ -602,8 +613,9 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
        I915_WRITE(FDI_RX_CTL(PIPE_A), rx_ctl_val);
 
        /* Configure Port Clock Select */
-       I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->config->ddi_pll_sel);
-       WARN_ON(intel_crtc->config->ddi_pll_sel != PORT_CLK_SEL_SPLL);
+       ddi_pll_sel = hsw_pll_to_ddi_pll_sel(intel_crtc->config->shared_dpll);
+       I915_WRITE(PORT_CLK_SEL(PORT_E), ddi_pll_sel);
+       WARN_ON(ddi_pll_sel != PORT_CLK_SEL_SPLL);
 
        /* Start the training iterating through available voltages and emphasis,
         * testing each value twice. */
@@ -880,7 +892,7 @@ static void skl_ddi_clock_get(struct intel_encoder *encoder,
        int link_clock = 0;
        uint32_t dpll_ctl1, dpll;
 
-       dpll = pipe_config->ddi_pll_sel;
+       dpll = intel_get_shared_dpll_id(dev_priv, pipe_config->shared_dpll);
 
        dpll_ctl1 = I915_READ(DPLL_CTRL1);
 
@@ -928,7 +940,7 @@ static void hsw_ddi_clock_get(struct intel_encoder *encoder,
        int link_clock = 0;
        u32 val, pll;
 
-       val = pipe_config->ddi_pll_sel;
+       val = hsw_pll_to_ddi_pll_sel(pipe_config->shared_dpll);
        switch (val & PORT_CLK_SEL_MASK) {
        case PORT_CLK_SEL_LCPLL_810:
                link_clock = 81000;
@@ -1136,7 +1148,6 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
-       struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        enum pipe pipe = intel_crtc->pipe;
@@ -1202,29 +1213,15 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
                        temp |= TRANS_DDI_MODE_SELECT_HDMI;
                else
                        temp |= TRANS_DDI_MODE_SELECT_DVI;
-
        } else if (type == INTEL_OUTPUT_ANALOG) {
                temp |= TRANS_DDI_MODE_SELECT_FDI;
                temp |= (intel_crtc->config->fdi_lanes - 1) << 1;
-
        } else if (type == INTEL_OUTPUT_DP ||
                   type == INTEL_OUTPUT_EDP) {
-               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
-
-               if (intel_dp->is_mst) {
-                       temp |= TRANS_DDI_MODE_SELECT_DP_MST;
-               } else
-                       temp |= TRANS_DDI_MODE_SELECT_DP_SST;
-
+               temp |= TRANS_DDI_MODE_SELECT_DP_SST;
                temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count);
        } else if (type == INTEL_OUTPUT_DP_MST) {
-               struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp;
-
-               if (intel_dp->is_mst) {
-                       temp |= TRANS_DDI_MODE_SELECT_DP_MST;
-               } else
-                       temp |= TRANS_DDI_MODE_SELECT_DP_SST;
-
+               temp |= TRANS_DDI_MODE_SELECT_DP_MST;
                temp |= DDI_PORT_WIDTH(intel_crtc->config->lane_count);
        } else {
                WARN(1, "Invalid encoder type %d for pipe %c\n",
@@ -1611,13 +1608,15 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
 }
 
 void intel_ddi_clk_select(struct intel_encoder *encoder,
-                         const struct intel_crtc_state *pipe_config)
+                         struct intel_shared_dpll *pll)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum port port = intel_ddi_get_encoder_port(encoder);
 
+       if (WARN_ON(!pll))
+               return;
+
        if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
-               uint32_t dpll = pipe_config->ddi_pll_sel;
                uint32_t val;
 
                /* DDI -> PLL mapping  */
@@ -1625,65 +1624,91 @@ void intel_ddi_clk_select(struct intel_encoder *encoder,
 
                val &= ~(DPLL_CTRL2_DDI_CLK_OFF(port) |
                        DPLL_CTRL2_DDI_CLK_SEL_MASK(port));
-               val |= (DPLL_CTRL2_DDI_CLK_SEL(dpll, port) |
+               val |= (DPLL_CTRL2_DDI_CLK_SEL(pll->id, port) |
                        DPLL_CTRL2_DDI_SEL_OVERRIDE(port));
 
                I915_WRITE(DPLL_CTRL2, val);
 
        } else if (INTEL_INFO(dev_priv)->gen < 9) {
-               WARN_ON(pipe_config->ddi_pll_sel == PORT_CLK_SEL_NONE);
-               I915_WRITE(PORT_CLK_SEL(port), pipe_config->ddi_pll_sel);
+               I915_WRITE(PORT_CLK_SEL(port), hsw_pll_to_ddi_pll_sel(pll));
        }
 }
 
-static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
+static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder,
+                                   int link_rate, uint32_t lane_count,
+                                   struct intel_shared_dpll *pll,
+                                   bool link_mst)
 {
-       struct drm_encoder *encoder = &intel_encoder->base;
-       struct drm_i915_private *dev_priv = to_i915(encoder->dev);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
-       enum port port = intel_ddi_get_encoder_port(intel_encoder);
-       int type = intel_encoder->type;
-
-       if (type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-
-               intel_dp_dual_mode_set_tmds_output(intel_hdmi, true);
-       }
-
-       intel_prepare_ddi_buffer(intel_encoder);
+       struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       enum port port = intel_ddi_get_encoder_port(encoder);
 
-       if (type == INTEL_OUTPUT_EDP) {
-               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       intel_dp_set_link_params(intel_dp, link_rate, lane_count,
+                                link_mst);
+       if (encoder->type == INTEL_OUTPUT_EDP)
                intel_edp_panel_on(intel_dp);
-       }
-
-       intel_ddi_clk_select(intel_encoder, crtc->config);
 
-       if (type == INTEL_OUTPUT_DP || type == INTEL_OUTPUT_EDP) {
-               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       intel_ddi_clk_select(encoder, pll);
+       intel_prepare_dp_ddi_buffers(encoder);
+       intel_ddi_init_dp_buf_reg(encoder);
+       intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+       intel_dp_start_link_train(intel_dp);
+       if (port != PORT_A || INTEL_GEN(dev_priv) >= 9)
+               intel_dp_stop_link_train(intel_dp);
+}
 
-               intel_dp_set_link_params(intel_dp, crtc->config);
+static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
+                                     bool has_hdmi_sink,
+                                     struct drm_display_mode *adjusted_mode,
+                                     struct intel_shared_dpll *pll)
+{
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct drm_encoder *drm_encoder = &encoder->base;
+       enum port port = intel_ddi_get_encoder_port(encoder);
+       int level = intel_ddi_hdmi_level(dev_priv, port);
 
-               intel_ddi_init_dp_buf_reg(intel_encoder);
+       intel_dp_dual_mode_set_tmds_output(intel_hdmi, true);
+       intel_ddi_clk_select(encoder, pll);
+       intel_prepare_hdmi_ddi_buffers(encoder);
+       if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
+               skl_ddi_set_iboost(encoder, level);
+       else if (IS_BROXTON(dev_priv))
+               bxt_ddi_vswing_sequence(dev_priv, level, port,
+                                       INTEL_OUTPUT_HDMI);
 
-               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
-               intel_dp_start_link_train(intel_dp);
-               if (port != PORT_A || INTEL_INFO(dev_priv)->gen >= 9)
-                       intel_dp_stop_link_train(intel_dp);
-       } else if (type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-               int level = intel_ddi_hdmi_level(dev_priv, port);
+       intel_hdmi->set_infoframes(drm_encoder,
+                                  has_hdmi_sink,
+                                  adjusted_mode);
+}
 
-               if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
-                       skl_ddi_set_iboost(intel_encoder, level);
+static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder,
+                                struct intel_crtc_state *pipe_config,
+                                struct drm_connector_state *conn_state)
+{
+       struct drm_encoder *encoder = &intel_encoder->base;
+       struct intel_crtc *crtc = to_intel_crtc(encoder->crtc);
+       int type = intel_encoder->type;
 
-               intel_hdmi->set_infoframes(encoder,
-                                          crtc->config->has_hdmi_sink,
-                                          &crtc->config->base.adjusted_mode);
+       if (type == INTEL_OUTPUT_DP || type == INTEL_OUTPUT_EDP) {
+               intel_ddi_pre_enable_dp(intel_encoder,
+                                       crtc->config->port_clock,
+                                       crtc->config->lane_count,
+                                       crtc->config->shared_dpll,
+                                       intel_crtc_has_type(crtc->config,
+                                                           INTEL_OUTPUT_DP_MST));
+       }
+       if (type == INTEL_OUTPUT_HDMI) {
+               intel_ddi_pre_enable_hdmi(intel_encoder,
+                                         crtc->config->has_hdmi_sink,
+                                         &crtc->config->base.adjusted_mode,
+                                         crtc->config->shared_dpll);
        }
 }
 
-static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
+static void intel_ddi_post_disable(struct intel_encoder *intel_encoder,
+                                  struct intel_crtc_state *old_crtc_state,
+                                  struct drm_connector_state *old_conn_state)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_device *dev = encoder->dev;
@@ -1693,6 +1718,8 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
        uint32_t val;
        bool wait = false;
 
+       /* old_crtc_state and old_conn_state are NULL when called from DP_MST */
+
        val = I915_READ(DDI_BUF_CTL(port));
        if (val & DDI_BUF_CTL_ENABLE) {
                val &= ~DDI_BUF_CTL_ENABLE;
@@ -1728,7 +1755,42 @@ static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
        }
 }
 
-static void intel_enable_ddi(struct intel_encoder *intel_encoder)
+void intel_ddi_fdi_post_disable(struct intel_encoder *intel_encoder,
+                               struct intel_crtc_state *old_crtc_state,
+                               struct drm_connector_state *old_conn_state)
+{
+       struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
+       uint32_t val;
+
+       /*
+        * Bspec lists this as both step 13 (before DDI_BUF_CTL disable)
+        * and step 18 (after clearing PORT_CLK_SEL). Based on a BUN,
+        * step 13 is the correct place for it. Step 18 is where it was
+        * originally before the BUN.
+        */
+       val = I915_READ(FDI_RX_CTL(PIPE_A));
+       val &= ~FDI_RX_ENABLE;
+       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
+
+       intel_ddi_post_disable(intel_encoder, old_crtc_state, old_conn_state);
+
+       val = I915_READ(FDI_RX_MISC(PIPE_A));
+       val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
+       val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
+       I915_WRITE(FDI_RX_MISC(PIPE_A), val);
+
+       val = I915_READ(FDI_RX_CTL(PIPE_A));
+       val &= ~FDI_PCDCLK;
+       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
+
+       val = I915_READ(FDI_RX_CTL(PIPE_A));
+       val &= ~FDI_RX_PLL_ENABLE;
+       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
+}
+
+static void intel_enable_ddi(struct intel_encoder *intel_encoder,
+                            struct intel_crtc_state *pipe_config,
+                            struct drm_connector_state *conn_state)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_crtc *crtc = encoder->crtc;
@@ -1757,7 +1819,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
 
                intel_edp_backlight_on(intel_dp);
                intel_psr_enable(intel_dp);
-               intel_edp_drrs_enable(intel_dp);
+               intel_edp_drrs_enable(intel_dp, pipe_config);
        }
 
        if (intel_crtc->config->has_audio) {
@@ -1766,7 +1828,9 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
        }
 }
 
-static void intel_disable_ddi(struct intel_encoder *intel_encoder)
+static void intel_disable_ddi(struct intel_encoder *intel_encoder,
+                             struct intel_crtc_state *old_crtc_state,
+                             struct drm_connector_state *old_conn_state)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_crtc *crtc = encoder->crtc;
@@ -1783,7 +1847,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
        if (type == INTEL_OUTPUT_EDP) {
                struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
-               intel_edp_drrs_disable(intel_dp);
+               intel_edp_drrs_disable(intel_dp, old_crtc_state);
                intel_psr_disable(intel_dp);
                intel_edp_backlight_off(intel_dp);
        }
@@ -2072,7 +2136,9 @@ bxt_ddi_phy_calc_lane_lat_optim_mask(struct intel_encoder *encoder,
        }
 }
 
-static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder)
+static void bxt_ddi_pre_pll_enable(struct intel_encoder *encoder,
+                                  struct intel_crtc_state *pipe_config,
+                                  struct drm_connector_state *conn_state)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct drm_i915_private *dev_priv = to_i915(dport->base.base.dev);
@@ -2144,7 +2210,7 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
 
        val = DP_TP_CTL_ENABLE |
              DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
-       if (intel_dp->is_mst)
+       if (intel_dp->link_mst)
                val |= DP_TP_CTL_MODE_MST;
        else {
                val |= DP_TP_CTL_MODE_SST;
@@ -2161,38 +2227,6 @@ void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp)
        udelay(600);
 }
 
-void intel_ddi_fdi_disable(struct drm_crtc *crtc)
-{
-       struct drm_i915_private *dev_priv = to_i915(crtc->dev);
-       struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
-       uint32_t val;
-
-       /*
-        * Bspec lists this as both step 13 (before DDI_BUF_CTL disable)
-        * and step 18 (after clearing PORT_CLK_SEL). Based on a BUN,
-        * step 13 is the correct place for it. Step 18 is where it was
-        * originally before the BUN.
-        */
-       val = I915_READ(FDI_RX_CTL(PIPE_A));
-       val &= ~FDI_RX_ENABLE;
-       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
-
-       intel_ddi_post_disable(intel_encoder);
-
-       val = I915_READ(FDI_RX_MISC(PIPE_A));
-       val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
-       val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
-       I915_WRITE(FDI_RX_MISC(PIPE_A), val);
-
-       val = I915_READ(FDI_RX_CTL(PIPE_A));
-       val &= ~FDI_PCDCLK;
-       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
-
-       val = I915_READ(FDI_RX_CTL(PIPE_A));
-       val &= ~FDI_RX_PLL_ENABLE;
-       I915_WRITE(FDI_RX_CTL(PIPE_A), val);
-}
-
 void intel_ddi_get_config(struct intel_encoder *encoder,
                          struct intel_crtc_state *pipe_config)
 {
@@ -2292,7 +2326,8 @@ void intel_ddi_get_config(struct intel_encoder *encoder,
 }
 
 static bool intel_ddi_compute_config(struct intel_encoder *encoder,
-                                    struct intel_crtc_state *pipe_config)
+                                    struct intel_crtc_state *pipe_config,
+                                    struct drm_connector_state *conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        int type = encoder->type;
@@ -2305,9 +2340,9 @@ static bool intel_ddi_compute_config(struct intel_encoder *encoder,
                pipe_config->cpu_transcoder = TRANSCODER_EDP;
 
        if (type == INTEL_OUTPUT_HDMI)
-               ret = intel_hdmi_compute_config(encoder, pipe_config);
+               ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state);
        else
-               ret = intel_dp_compute_config(encoder, pipe_config);
+               ret = intel_dp_compute_config(encoder, pipe_config, conn_state);
 
        if (IS_BROXTON(dev_priv) && ret)
                pipe_config->lane_lat_optim_mask =
@@ -2358,6 +2393,45 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port)
        return connector;
 }
 
+struct intel_shared_dpll *
+intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock)
+{
+       struct intel_connector *connector = intel_dp->attached_connector;
+       struct intel_encoder *encoder = connector->encoder;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+       struct intel_shared_dpll *pll = NULL;
+       struct intel_shared_dpll_config tmp_pll_config;
+       enum intel_dpll_id dpll_id;
+
+       if (IS_BROXTON(dev_priv)) {
+               dpll_id =  (enum intel_dpll_id)dig_port->port;
+               /*
+                * Select the required PLL. This works for platforms where
+                * there is no shared DPLL.
+                */
+               pll = &dev_priv->shared_dplls[dpll_id];
+               if (WARN_ON(pll->active_mask)) {
+
+                       DRM_ERROR("Shared DPLL in use. active_mask:%x\n",
+                                 pll->active_mask);
+                       return NULL;
+               }
+               tmp_pll_config = pll->config;
+               if (!bxt_ddi_dp_set_dpll_hw_state(clock,
+                                                 &pll->config.hw_state)) {
+                       DRM_ERROR("Could not setup DPLL\n");
+                       pll->config = tmp_pll_config;
+                       return NULL;
+               }
+       } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+               pll = skl_find_link_pll(dev_priv, clock);
+       } else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
+               pll = hsw_ddi_dp_get_dpll(encoder, clock);
+       }
+       return pll;
+}
+
 void intel_ddi_init(struct drm_device *dev, enum port port)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
index cba137f..73b6858 100644 (file)
@@ -46,71 +46,70 @@ void intel_device_info_dump(struct drm_i915_private *dev_priv)
 
 static void cherryview_sseu_info_init(struct drm_i915_private *dev_priv)
 {
-       struct intel_device_info *info = mkwrite_device_info(dev_priv);
+       struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu;
        u32 fuse, eu_dis;
 
        fuse = I915_READ(CHV_FUSE_GT);
 
-       info->slice_total = 1;
+       sseu->slice_mask = BIT(0);
 
        if (!(fuse & CHV_FGT_DISABLE_SS0)) {
-               info->subslice_per_slice++;
+               sseu->subslice_mask |= BIT(0);
                eu_dis = fuse & (CHV_FGT_EU_DIS_SS0_R0_MASK |
                                 CHV_FGT_EU_DIS_SS0_R1_MASK);
-               info->eu_total += 8 - hweight32(eu_dis);
+               sseu->eu_total += 8 - hweight32(eu_dis);
        }
 
        if (!(fuse & CHV_FGT_DISABLE_SS1)) {
-               info->subslice_per_slice++;
+               sseu->subslice_mask |= BIT(1);
                eu_dis = fuse & (CHV_FGT_EU_DIS_SS1_R0_MASK |
                                 CHV_FGT_EU_DIS_SS1_R1_MASK);
-               info->eu_total += 8 - hweight32(eu_dis);
+               sseu->eu_total += 8 - hweight32(eu_dis);
        }
 
-       info->subslice_total = info->subslice_per_slice;
        /*
         * CHV expected to always have a uniform distribution of EU
         * across subslices.
        */
-       info->eu_per_subslice = info->subslice_total ?
-                               info->eu_total / info->subslice_total :
+       sseu->eu_per_subslice = sseu_subslice_total(sseu) ?
+                               sseu->eu_total / sseu_subslice_total(sseu) :
                                0;
        /*
         * CHV supports subslice power gating on devices with more than
         * one subslice, and supports EU power gating on devices with
         * more than one EU pair per subslice.
        */
-       info->has_slice_pg = 0;
-       info->has_subslice_pg = (info->subslice_total > 1);
-       info->has_eu_pg = (info->eu_per_subslice > 2);
+       sseu->has_slice_pg = 0;
+       sseu->has_subslice_pg = sseu_subslice_total(sseu) > 1;
+       sseu->has_eu_pg = (sseu->eu_per_subslice > 2);
 }
 
 static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
 {
        struct intel_device_info *info = mkwrite_device_info(dev_priv);
+       struct sseu_dev_info *sseu = &info->sseu;
        int s_max = 3, ss_max = 4, eu_max = 8;
        int s, ss;
-       u32 fuse2, s_enable, ss_disable, eu_disable;
+       u32 fuse2, eu_disable;
        u8 eu_mask = 0xff;
 
        fuse2 = I915_READ(GEN8_FUSE2);
-       s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
-       ss_disable = (fuse2 & GEN9_F2_SS_DIS_MASK) >> GEN9_F2_SS_DIS_SHIFT;
+       sseu->slice_mask = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
 
-       info->slice_total = hweight32(s_enable);
        /*
         * The subslice disable field is global, i.e. it applies
         * to each of the enabled slices.
        */
-       info->subslice_per_slice = ss_max - hweight32(ss_disable);
-       info->subslice_total = info->slice_total * info->subslice_per_slice;
+       sseu->subslice_mask = (1 << ss_max) - 1;
+       sseu->subslice_mask &= ~((fuse2 & GEN9_F2_SS_DIS_MASK) >>
+                                GEN9_F2_SS_DIS_SHIFT);
 
        /*
         * Iterate through enabled slices and subslices to
         * count the total enabled EU.
        */
        for (s = 0; s < s_max; s++) {
-               if (!(s_enable & BIT(s)))
+               if (!(sseu->slice_mask & BIT(s)))
                        /* skip disabled slice */
                        continue;
 
@@ -118,7 +117,7 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
                for (ss = 0; ss < ss_max; ss++) {
                        int eu_per_ss;
 
-                       if (ss_disable & BIT(ss))
+                       if (!(sseu->subslice_mask & BIT(ss)))
                                /* skip disabled subslice */
                                continue;
 
@@ -131,9 +130,9 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
                         * subslices if they are unbalanced.
                         */
                        if (eu_per_ss == 7)
-                               info->subslice_7eu[s] |= BIT(ss);
+                               sseu->subslice_7eu[s] |= BIT(ss);
 
-                       info->eu_total += eu_per_ss;
+                       sseu->eu_total += eu_per_ss;
                }
        }
 
@@ -144,9 +143,9 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
         * recovery. BXT is expected to be perfectly uniform in EU
         * distribution.
        */
-       info->eu_per_subslice = info->subslice_total ?
-                               DIV_ROUND_UP(info->eu_total,
-                                            info->subslice_total) : 0;
+       sseu->eu_per_subslice = sseu_subslice_total(sseu) ?
+                               DIV_ROUND_UP(sseu->eu_total,
+                                            sseu_subslice_total(sseu)) : 0;
        /*
         * SKL supports slice power gating on devices with more than
         * one slice, and supports EU power gating on devices with
@@ -155,15 +154,15 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
         * supports EU power gating on devices with more than one EU
         * pair per subslice.
        */
-       info->has_slice_pg =
+       sseu->has_slice_pg =
                (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) &&
-               info->slice_total > 1;
-       info->has_subslice_pg =
-               IS_BROXTON(dev_priv) && info->subslice_total > 1;
-       info->has_eu_pg = info->eu_per_subslice > 2;
+               hweight8(sseu->slice_mask) > 1;
+       sseu->has_subslice_pg =
+               IS_BROXTON(dev_priv) && sseu_subslice_total(sseu) > 1;
+       sseu->has_eu_pg = sseu->eu_per_subslice > 2;
 
        if (IS_BROXTON(dev_priv)) {
-#define IS_SS_DISABLED(_ss_disable, ss)    (_ss_disable & BIT(ss))
+#define IS_SS_DISABLED(ss)     (!(sseu->subslice_mask & BIT(ss)))
                /*
                 * There is a HW issue in 2x6 fused down parts that requires
                 * Pooled EU to be enabled as a WA. The pool configuration
@@ -171,19 +170,18 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
                 * doesn't affect if the device has all 3 subslices enabled.
                 */
                /* WaEnablePooledEuFor2x6:bxt */
-               info->has_pooled_eu = ((info->subslice_per_slice == 3) ||
-                                      (info->subslice_per_slice == 2 &&
+               info->has_pooled_eu = ((hweight8(sseu->subslice_mask) == 3) ||
+                                      (hweight8(sseu->subslice_mask) == 2 &&
                                        INTEL_REVID(dev_priv) < BXT_REVID_C0));
 
-               info->min_eu_in_pool = 0;
+               sseu->min_eu_in_pool = 0;
                if (info->has_pooled_eu) {
-                       if (IS_SS_DISABLED(ss_disable, 0) ||
-                           IS_SS_DISABLED(ss_disable, 2))
-                               info->min_eu_in_pool = 3;
-                       else if (IS_SS_DISABLED(ss_disable, 1))
-                               info->min_eu_in_pool = 6;
+                       if (IS_SS_DISABLED(2) || IS_SS_DISABLED(0))
+                               sseu->min_eu_in_pool = 3;
+                       else if (IS_SS_DISABLED(1))
+                               sseu->min_eu_in_pool = 6;
                        else
-                               info->min_eu_in_pool = 9;
+                               sseu->min_eu_in_pool = 9;
                }
 #undef IS_SS_DISABLED
        }
@@ -191,14 +189,20 @@ static void gen9_sseu_info_init(struct drm_i915_private *dev_priv)
 
 static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
 {
-       struct intel_device_info *info = mkwrite_device_info(dev_priv);
+       struct sseu_dev_info *sseu = &mkwrite_device_info(dev_priv)->sseu;
        const int s_max = 3, ss_max = 3, eu_max = 8;
        int s, ss;
-       u32 fuse2, eu_disable[s_max], s_enable, ss_disable;
+       u32 fuse2, eu_disable[s_max];
 
        fuse2 = I915_READ(GEN8_FUSE2);
-       s_enable = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
-       ss_disable = (fuse2 & GEN8_F2_SS_DIS_MASK) >> GEN8_F2_SS_DIS_SHIFT;
+       sseu->slice_mask = (fuse2 & GEN8_F2_S_ENA_MASK) >> GEN8_F2_S_ENA_SHIFT;
+       /*
+        * The subslice disable field is global, i.e. it applies
+        * to each of the enabled slices.
+        */
+       sseu->subslice_mask = BIT(ss_max) - 1;
+       sseu->subslice_mask &= ~((fuse2 & GEN8_F2_SS_DIS_MASK) >>
+                                GEN8_F2_SS_DIS_SHIFT);
 
        eu_disable[0] = I915_READ(GEN8_EU_DISABLE0) & GEN8_EU_DIS0_S0_MASK;
        eu_disable[1] = (I915_READ(GEN8_EU_DISABLE0) >> GEN8_EU_DIS0_S1_SHIFT) |
@@ -208,28 +212,19 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
                        ((I915_READ(GEN8_EU_DISABLE2) & GEN8_EU_DIS2_S2_MASK) <<
                         (32 - GEN8_EU_DIS1_S2_SHIFT));
 
-       info->slice_total = hweight32(s_enable);
-
-       /*
-        * The subslice disable field is global, i.e. it applies
-        * to each of the enabled slices.
-        */
-       info->subslice_per_slice = ss_max - hweight32(ss_disable);
-       info->subslice_total = info->slice_total * info->subslice_per_slice;
-
        /*
         * Iterate through enabled slices and subslices to
         * count the total enabled EU.
         */
        for (s = 0; s < s_max; s++) {
-               if (!(s_enable & (0x1 << s)))
+               if (!(sseu->slice_mask & BIT(s)))
                        /* skip disabled slice */
                        continue;
 
                for (ss = 0; ss < ss_max; ss++) {
                        u32 n_disabled;
 
-                       if (ss_disable & (0x1 << ss))
+                       if (!(sseu->subslice_mask & BIT(ss)))
                                /* skip disabled subslice */
                                continue;
 
@@ -239,9 +234,9 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
                         * Record which subslices have 7 EUs.
                         */
                        if (eu_max - n_disabled == 7)
-                               info->subslice_7eu[s] |= 1 << ss;
+                               sseu->subslice_7eu[s] |= 1 << ss;
 
-                       info->eu_total += eu_max - n_disabled;
+                       sseu->eu_total += eu_max - n_disabled;
                }
        }
 
@@ -250,16 +245,17 @@ static void broadwell_sseu_info_init(struct drm_i915_private *dev_priv)
         * subslices with the exception that any one EU in any one subslice may
         * be fused off for die recovery.
         */
-       info->eu_per_subslice = info->subslice_total ?
-               DIV_ROUND_UP(info->eu_total, info->subslice_total) : 0;
+       sseu->eu_per_subslice = sseu_subslice_total(sseu) ?
+                               DIV_ROUND_UP(sseu->eu_total,
+                                            sseu_subslice_total(sseu)) : 0;
 
        /*
         * BDW supports slice power gating on devices with more than
         * one slice.
         */
-       info->has_slice_pg = (info->slice_total > 1);
-       info->has_subslice_pg = 0;
-       info->has_eu_pg = 0;
+       sseu->has_slice_pg = hweight8(sseu->slice_mask) > 1;
+       sseu->has_subslice_pg = 0;
+       sseu->has_eu_pg = 0;
 }
 
 /*
@@ -374,15 +370,19 @@ void intel_device_info_runtime_init(struct drm_i915_private *dev_priv)
        if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1))
                info->has_snoop = false;
 
-       DRM_DEBUG_DRIVER("slice total: %u\n", info->slice_total);
-       DRM_DEBUG_DRIVER("subslice total: %u\n", info->subslice_total);
-       DRM_DEBUG_DRIVER("subslice per slice: %u\n", info->subslice_per_slice);
-       DRM_DEBUG_DRIVER("EU total: %u\n", info->eu_total);
-       DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->eu_per_subslice);
+       DRM_DEBUG_DRIVER("slice mask: %04x\n", info->sseu.slice_mask);
+       DRM_DEBUG_DRIVER("slice total: %u\n", hweight8(info->sseu.slice_mask));
+       DRM_DEBUG_DRIVER("subslice total: %u\n",
+                        sseu_subslice_total(&info->sseu));
+       DRM_DEBUG_DRIVER("subslice mask %04x\n", info->sseu.subslice_mask);
+       DRM_DEBUG_DRIVER("subslice per slice: %u\n",
+                        hweight8(info->sseu.subslice_mask));
+       DRM_DEBUG_DRIVER("EU total: %u\n", info->sseu.eu_total);
+       DRM_DEBUG_DRIVER("EU per subslice: %u\n", info->sseu.eu_per_subslice);
        DRM_DEBUG_DRIVER("has slice power gating: %s\n",
-                        info->has_slice_pg ? "y" : "n");
+                        info->sseu.has_slice_pg ? "y" : "n");
        DRM_DEBUG_DRIVER("has subslice power gating: %s\n",
-                        info->has_subslice_pg ? "y" : "n");
+                        info->sseu.has_subslice_pg ? "y" : "n");
        DRM_DEBUG_DRIVER("has EU power gating: %s\n",
-                        info->has_eu_pg ? "y" : "n");
+                        info->sseu.has_eu_pg ? "y" : "n");
 }
index 175595f..fbcfed6 100644 (file)
@@ -34,6 +34,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drmP.h>
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 #include "i915_gem_dmabuf.h"
@@ -1201,8 +1202,8 @@ void assert_panel_unlocked(struct drm_i915_private *dev_priv,
        if (HAS_PCH_SPLIT(dev)) {
                u32 port_sel;
 
-               pp_reg = PCH_PP_CONTROL;
-               port_sel = I915_READ(PCH_PP_ON_DELAYS) & PANEL_PORT_SELECT_MASK;
+               pp_reg = PP_CONTROL(0);
+               port_sel = I915_READ(PP_ON_DELAYS(0)) & PANEL_PORT_SELECT_MASK;
 
                if (port_sel == PANEL_PORT_SELECT_LVDS &&
                    I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT)
@@ -1210,10 +1211,10 @@ void assert_panel_unlocked(struct drm_i915_private *dev_priv,
                /* XXX: else fix for eDP */
        } else if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
                /* presumably write lock depends on pipe, not port select */
-               pp_reg = VLV_PIPE_PP_CONTROL(pipe);
+               pp_reg = PP_CONTROL(pipe);
                panel_pipe = pipe;
        } else {
-               pp_reg = PP_CONTROL;
+               pp_reg = PP_CONTROL(0);
                if (I915_READ(LVDS) & LVDS_PIPEB_SELECT)
                        panel_pipe = PIPE_B;
        }
@@ -1906,7 +1907,7 @@ static void ironlake_disable_pch_transcoder(struct drm_i915_private *dev_priv,
        }
 }
 
-static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
+void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
 {
        u32 val;
 
@@ -1958,12 +1959,12 @@ static void intel_enable_pipe(struct intel_crtc *crtc)
         * a plane.  On ILK+ the pipe PLLs are integrated, so we don't
         * need the check.
         */
-       if (HAS_GMCH_DISPLAY(dev_priv))
+       if (HAS_GMCH_DISPLAY(dev_priv)) {
                if (intel_crtc_has_type(crtc->config, INTEL_OUTPUT_DSI))
                        assert_dsi_pll_enabled(dev_priv);
                else
                        assert_pll_enabled(dev_priv, pipe);
-       else {
+       else {
                if (crtc->config->has_pch_encoder) {
                        /* if driving the PCH, we need FDI enabled */
                        assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder);
@@ -2146,33 +2147,6 @@ intel_fill_fb_ggtt_view(struct i915_ggtt_view *view,
        }
 }
 
-static void
-intel_fill_fb_info(struct drm_i915_private *dev_priv,
-                  struct drm_framebuffer *fb)
-{
-       struct intel_rotation_info *info = &to_intel_framebuffer(fb)->rot_info;
-       unsigned int tile_size, tile_width, tile_height, cpp;
-
-       tile_size = intel_tile_size(dev_priv);
-
-       cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-       intel_tile_dims(dev_priv, &tile_width, &tile_height,
-                       fb->modifier[0], cpp);
-
-       info->plane[0].width = DIV_ROUND_UP(fb->pitches[0], tile_width * cpp);
-       info->plane[0].height = DIV_ROUND_UP(fb->height, tile_height);
-
-       if (info->pixel_format == DRM_FORMAT_NV12) {
-               cpp = drm_format_plane_cpp(fb->pixel_format, 1);
-               intel_tile_dims(dev_priv, &tile_width, &tile_height,
-                               fb->modifier[1], cpp);
-
-               info->uv_offset = fb->offsets[1];
-               info->plane[1].width = DIV_ROUND_UP(fb->pitches[1], tile_width * cpp);
-               info->plane[1].height = DIV_ROUND_UP(fb->height / 2, tile_height);
-       }
-}
-
 static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
 {
        if (INTEL_INFO(dev_priv)->gen >= 9)
@@ -2205,16 +2179,15 @@ static unsigned int intel_surf_alignment(const struct drm_i915_private *dev_priv
        }
 }
 
-int
-intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
-                          unsigned int rotation)
+struct i915_vma *
+intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation)
 {
        struct drm_device *dev = fb->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        struct i915_ggtt_view view;
+       struct i915_vma *vma;
        u32 alignment;
-       int ret;
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
@@ -2239,75 +2212,112 @@ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
         */
        intel_runtime_pm_get(dev_priv);
 
-       ret = i915_gem_object_pin_to_display_plane(obj, alignment,
-                                                  &view);
-       if (ret)
-               goto err_pm;
-
-       /* Install a fence for tiled scan-out. Pre-i965 always needs a
-        * fence, whereas 965+ only requires a fence if using
-        * framebuffer compression.  For simplicity, we always install
-        * a fence as the cost is not that onerous.
-        */
-       if (view.type == I915_GGTT_VIEW_NORMAL) {
-               ret = i915_gem_object_get_fence(obj);
-               if (ret == -EDEADLK) {
-                       /*
-                        * -EDEADLK means there are no free fences
-                        * no pending flips.
-                        *
-                        * This is propagated to atomic, but it uses
-                        * -EDEADLK to force a locking recovery, so
-                        * change the returned error to -EBUSY.
-                        */
-                       ret = -EBUSY;
-                       goto err_unpin;
-               } else if (ret)
-                       goto err_unpin;
+       vma = i915_gem_object_pin_to_display_plane(obj, alignment, &view);
+       if (IS_ERR(vma))
+               goto err;
 
-               i915_gem_object_pin_fence(obj);
+       if (i915_vma_is_map_and_fenceable(vma)) {
+               /* Install a fence for tiled scan-out. Pre-i965 always needs a
+                * fence, whereas 965+ only requires a fence if using
+                * framebuffer compression.  For simplicity, we always, when
+                * possible, install a fence as the cost is not that onerous.
+                *
+                * If we fail to fence the tiled scanout, then either the
+                * modeset will reject the change (which is highly unlikely as
+                * the affected systems, all but one, do not have unmappable
+                * space) or we will not be able to enable full powersaving
+                * techniques (also likely not to apply due to various limits
+                * FBC and the like impose on the size of the buffer, which
+                * presumably we violated anyway with this unmappable buffer).
+                * Anyway, it is presumably better to stumble onwards with
+                * something and try to run the system in a "less than optimal"
+                * mode that matches the user configuration.
+                */
+               if (i915_vma_get_fence(vma) == 0)
+                       i915_vma_pin_fence(vma);
        }
 
+err:
        intel_runtime_pm_put(dev_priv);
-       return 0;
-
-err_unpin:
-       i915_gem_object_unpin_from_display_plane(obj, &view);
-err_pm:
-       intel_runtime_pm_put(dev_priv);
-       return ret;
+       return vma;
 }
 
 void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation)
 {
        struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        struct i915_ggtt_view view;
+       struct i915_vma *vma;
 
        WARN_ON(!mutex_is_locked(&obj->base.dev->struct_mutex));
 
        intel_fill_fb_ggtt_view(&view, fb, rotation);
+       vma = i915_gem_object_to_ggtt(obj, &view);
 
-       if (view.type == I915_GGTT_VIEW_NORMAL)
-               i915_gem_object_unpin_fence(obj);
+       i915_vma_unpin_fence(vma);
+       i915_gem_object_unpin_from_display_plane(vma);
+}
 
-       i915_gem_object_unpin_from_display_plane(obj, &view);
+static int intel_fb_pitch(const struct drm_framebuffer *fb, int plane,
+                         unsigned int rotation)
+{
+       if (intel_rotation_90_or_270(rotation))
+               return to_intel_framebuffer(fb)->rotated[plane].pitch;
+       else
+               return fb->pitches[plane];
+}
+
+/*
+ * Convert the x/y offsets into a linear offset.
+ * Only valid with 0/180 degree rotation, which is fine since linear
+ * offset is only used with linear buffers on pre-hsw and tiled buffers
+ * with gen2/3, and 90/270 degree rotations isn't supported on any of them.
+ */
+u32 intel_fb_xy_to_linear(int x, int y,
+                         const struct intel_plane_state *state,
+                         int plane)
+{
+       const struct drm_framebuffer *fb = state->base.fb;
+       unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+       unsigned int pitch = fb->pitches[plane];
+
+       return y * pitch + x * cpp;
+}
+
+/*
+ * Add the x/y offsets derived from fb->offsets[] to the user
+ * specified plane src x/y offsets. The resulting x/y offsets
+ * specify the start of scanout from the beginning of the gtt mapping.
+ */
+void intel_add_fb_offsets(int *x, int *y,
+                         const struct intel_plane_state *state,
+                         int plane)
+
+{
+       const struct intel_framebuffer *intel_fb = to_intel_framebuffer(state->base.fb);
+       unsigned int rotation = state->base.rotation;
+
+       if (intel_rotation_90_or_270(rotation)) {
+               *x += intel_fb->rotated[plane].x;
+               *y += intel_fb->rotated[plane].y;
+       } else {
+               *x += intel_fb->normal[plane].x;
+               *y += intel_fb->normal[plane].y;
+       }
 }
 
 /*
- * Adjust the tile offset by moving the difference into
- * the x/y offsets.
- *
  * Input tile dimensions and pitch must already be
  * rotated to match x and y, and in pixel units.
  */
-static u32 intel_adjust_tile_offset(int *x, int *y,
-                                   unsigned int tile_width,
-                                   unsigned int tile_height,
-                                   unsigned int tile_size,
-                                   unsigned int pitch_tiles,
-                                   u32 old_offset,
-                                   u32 new_offset)
-{
+static u32 _intel_adjust_tile_offset(int *x, int *y,
+                                    unsigned int tile_width,
+                                    unsigned int tile_height,
+                                    unsigned int tile_size,
+                                    unsigned int pitch_tiles,
+                                    u32 old_offset,
+                                    u32 new_offset)
+{
+       unsigned int pitch_pixels = pitch_tiles * tile_width;
        unsigned int tiles;
 
        WARN_ON(old_offset & (tile_size - 1));
@@ -2319,6 +2329,54 @@ static u32 intel_adjust_tile_offset(int *x, int *y,
        *y += tiles / pitch_tiles * tile_height;
        *x += tiles % pitch_tiles * tile_width;
 
+       /* minimize x in case it got needlessly big */
+       *y += *x / pitch_pixels * tile_height;
+       *x %= pitch_pixels;
+
+       return new_offset;
+}
+
+/*
+ * Adjust the tile offset by moving the difference into
+ * the x/y offsets.
+ */
+static u32 intel_adjust_tile_offset(int *x, int *y,
+                                   const struct intel_plane_state *state, int plane,
+                                   u32 old_offset, u32 new_offset)
+{
+       const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev);
+       const struct drm_framebuffer *fb = state->base.fb;
+       unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+       unsigned int rotation = state->base.rotation;
+       unsigned int pitch = intel_fb_pitch(fb, plane, rotation);
+
+       WARN_ON(new_offset > old_offset);
+
+       if (fb->modifier[plane] != DRM_FORMAT_MOD_NONE) {
+               unsigned int tile_size, tile_width, tile_height;
+               unsigned int pitch_tiles;
+
+               tile_size = intel_tile_size(dev_priv);
+               intel_tile_dims(dev_priv, &tile_width, &tile_height,
+                               fb->modifier[plane], cpp);
+
+               if (intel_rotation_90_or_270(rotation)) {
+                       pitch_tiles = pitch / tile_height;
+                       swap(tile_width, tile_height);
+               } else {
+                       pitch_tiles = pitch / (tile_width * cpp);
+               }
+
+               _intel_adjust_tile_offset(x, y, tile_width, tile_height,
+                                         tile_size, pitch_tiles,
+                                         old_offset, new_offset);
+       } else {
+               old_offset += *y * pitch + *x * cpp;
+
+               *y = (old_offset - new_offset) / pitch;
+               *x = ((old_offset - new_offset) - *y * pitch) / cpp;
+       }
+
        return new_offset;
 }
 
@@ -2329,18 +2387,24 @@ static u32 intel_adjust_tile_offset(int *x, int *y,
  * In the 90/270 rotated case, x and y are assumed
  * to be already rotated to match the rotated GTT view, and
  * pitch is the tile_height aligned framebuffer height.
+ *
+ * This function is used when computing the derived information
+ * under intel_framebuffer, so using any of that information
+ * here is not allowed. Anything under drm_framebuffer can be
+ * used. This is why the user has to pass in the pitch since it
+ * is specified in the rotated orientation.
  */
-u32 intel_compute_tile_offset(int *x, int *y,
-                             const struct drm_framebuffer *fb, int plane,
-                             unsigned int pitch,
-                             unsigned int rotation)
+static u32 _intel_compute_tile_offset(const struct drm_i915_private *dev_priv,
+                                     int *x, int *y,
+                                     const struct drm_framebuffer *fb, int plane,
+                                     unsigned int pitch,
+                                     unsigned int rotation,
+                                     u32 alignment)
 {
-       const struct drm_i915_private *dev_priv = to_i915(fb->dev);
        uint64_t fb_modifier = fb->modifier[plane];
        unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
-       u32 offset, offset_aligned, alignment;
+       u32 offset, offset_aligned;
 
-       alignment = intel_surf_alignment(dev_priv, fb_modifier);
        if (alignment)
                alignment--;
 
@@ -2368,9 +2432,9 @@ u32 intel_compute_tile_offset(int *x, int *y,
                offset = (tile_rows * pitch_tiles + tiles) * tile_size;
                offset_aligned = offset & ~alignment;
 
-               intel_adjust_tile_offset(x, y, tile_width, tile_height,
-                                        tile_size, pitch_tiles,
-                                        offset, offset_aligned);
+               _intel_adjust_tile_offset(x, y, tile_width, tile_height,
+                                         tile_size, pitch_tiles,
+                                         offset, offset_aligned);
        } else {
                offset = *y * pitch + *x * cpp;
                offset_aligned = offset & ~alignment;
@@ -2382,6 +2446,177 @@ u32 intel_compute_tile_offset(int *x, int *y,
        return offset_aligned;
 }
 
+u32 intel_compute_tile_offset(int *x, int *y,
+                             const struct intel_plane_state *state,
+                             int plane)
+{
+       const struct drm_i915_private *dev_priv = to_i915(state->base.plane->dev);
+       const struct drm_framebuffer *fb = state->base.fb;
+       unsigned int rotation = state->base.rotation;
+       int pitch = intel_fb_pitch(fb, plane, rotation);
+       u32 alignment;
+
+       /* AUX_DIST needs only 4K alignment */
+       if (fb->pixel_format == DRM_FORMAT_NV12 && plane == 1)
+               alignment = 4096;
+       else
+               alignment = intel_surf_alignment(dev_priv, fb->modifier[plane]);
+
+       return _intel_compute_tile_offset(dev_priv, x, y, fb, plane, pitch,
+                                         rotation, alignment);
+}
+
+/* Convert the fb->offset[] linear offset into x/y offsets */
+static void intel_fb_offset_to_xy(int *x, int *y,
+                                 const struct drm_framebuffer *fb, int plane)
+{
+       unsigned int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+       unsigned int pitch = fb->pitches[plane];
+       u32 linear_offset = fb->offsets[plane];
+
+       *y = linear_offset / pitch;
+       *x = linear_offset % pitch / cpp;
+}
+
+static unsigned int intel_fb_modifier_to_tiling(uint64_t fb_modifier)
+{
+       switch (fb_modifier) {
+       case I915_FORMAT_MOD_X_TILED:
+               return I915_TILING_X;
+       case I915_FORMAT_MOD_Y_TILED:
+               return I915_TILING_Y;
+       default:
+               return I915_TILING_NONE;
+       }
+}
+
+static int
+intel_fill_fb_info(struct drm_i915_private *dev_priv,
+                  struct drm_framebuffer *fb)
+{
+       struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
+       struct intel_rotation_info *rot_info = &intel_fb->rot_info;
+       u32 gtt_offset_rotated = 0;
+       unsigned int max_size = 0;
+       uint32_t format = fb->pixel_format;
+       int i, num_planes = drm_format_num_planes(format);
+       unsigned int tile_size = intel_tile_size(dev_priv);
+
+       for (i = 0; i < num_planes; i++) {
+               unsigned int width, height;
+               unsigned int cpp, size;
+               u32 offset;
+               int x, y;
+
+               cpp = drm_format_plane_cpp(format, i);
+               width = drm_format_plane_width(fb->width, format, i);
+               height = drm_format_plane_height(fb->height, format, i);
+
+               intel_fb_offset_to_xy(&x, &y, fb, i);
+
+               /*
+                * The fence (if used) is aligned to the start of the object
+                * so having the framebuffer wrap around across the edge of the
+                * fenced region doesn't really work. We have no API to configure
+                * the fence start offset within the object (nor could we probably
+                * on gen2/3). So it's just easier if we just require that the
+                * fb layout agrees with the fence layout. We already check that the
+                * fb stride matches the fence stride elsewhere.
+                */
+               if (i915_gem_object_is_tiled(intel_fb->obj) &&
+                   (x + width) * cpp > fb->pitches[i]) {
+                       DRM_DEBUG("bad fb plane %d offset: 0x%x\n",
+                                 i, fb->offsets[i]);
+                       return -EINVAL;
+               }
+
+               /*
+                * First pixel of the framebuffer from
+                * the start of the normal gtt mapping.
+                */
+               intel_fb->normal[i].x = x;
+               intel_fb->normal[i].y = y;
+
+               offset = _intel_compute_tile_offset(dev_priv, &x, &y,
+                                                   fb, 0, fb->pitches[i],
+                                                   DRM_ROTATE_0, tile_size);
+               offset /= tile_size;
+
+               if (fb->modifier[i] != DRM_FORMAT_MOD_NONE) {
+                       unsigned int tile_width, tile_height;
+                       unsigned int pitch_tiles;
+                       struct drm_rect r;
+
+                       intel_tile_dims(dev_priv, &tile_width, &tile_height,
+                                       fb->modifier[i], cpp);
+
+                       rot_info->plane[i].offset = offset;
+                       rot_info->plane[i].stride = DIV_ROUND_UP(fb->pitches[i], tile_width * cpp);
+                       rot_info->plane[i].width = DIV_ROUND_UP(x + width, tile_width);
+                       rot_info->plane[i].height = DIV_ROUND_UP(y + height, tile_height);
+
+                       intel_fb->rotated[i].pitch =
+                               rot_info->plane[i].height * tile_height;
+
+                       /* how many tiles does this plane need */
+                       size = rot_info->plane[i].stride * rot_info->plane[i].height;
+                       /*
+                        * If the plane isn't horizontally tile aligned,
+                        * we need one more tile.
+                        */
+                       if (x != 0)
+                               size++;
+
+                       /* rotate the x/y offsets to match the GTT view */
+                       r.x1 = x;
+                       r.y1 = y;
+                       r.x2 = x + width;
+                       r.y2 = y + height;
+                       drm_rect_rotate(&r,
+                                       rot_info->plane[i].width * tile_width,
+                                       rot_info->plane[i].height * tile_height,
+                                       DRM_ROTATE_270);
+                       x = r.x1;
+                       y = r.y1;
+
+                       /* rotate the tile dimensions to match the GTT view */
+                       pitch_tiles = intel_fb->rotated[i].pitch / tile_height;
+                       swap(tile_width, tile_height);
+
+                       /*
+                        * We only keep the x/y offsets, so push all of the
+                        * gtt offset into the x/y offsets.
+                        */
+                       _intel_adjust_tile_offset(&x, &y, tile_size,
+                                                 tile_width, tile_height, pitch_tiles,
+                                                 gtt_offset_rotated * tile_size, 0);
+
+                       gtt_offset_rotated += rot_info->plane[i].width * rot_info->plane[i].height;
+
+                       /*
+                        * First pixel of the framebuffer from
+                        * the start of the rotated gtt mapping.
+                        */
+                       intel_fb->rotated[i].x = x;
+                       intel_fb->rotated[i].y = y;
+               } else {
+                       size = DIV_ROUND_UP((y + height) * fb->pitches[i] +
+                                           x * cpp, tile_size);
+               }
+
+               /* how many tiles in total needed in the bo */
+               max_size = max(max_size, offset + size);
+       }
+
+       if (max_size * tile_size > to_intel_framebuffer(fb)->obj->base.size) {
+               DRM_DEBUG("fb too big for bo (need %u bytes, have %zu bytes)\n",
+                         max_size * tile_size, to_intel_framebuffer(fb)->obj->base.size);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int i9xx_format_to_fourcc(int format)
 {
        switch (format) {
@@ -2465,9 +2700,8 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
                return false;
        }
 
-       obj->tiling_mode = plane_config->tiling;
-       if (obj->tiling_mode == I915_TILING_X)
-               obj->stride = fb->pitches[0];
+       if (plane_config->tiling == I915_TILING_X)
+               obj->tiling_and_stride = fb->pitches[0] | I915_TILING_X;
 
        mode_cmd.pixel_format = fb->pixel_format;
        mode_cmd.width = fb->width;
@@ -2488,7 +2722,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
        return true;
 
 out_unref_obj:
-       drm_gem_object_unreference(&obj->base);
+       i915_gem_object_put(obj);
        mutex_unlock(&dev->struct_mutex);
        return false;
 }
@@ -2552,7 +2786,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
                        continue;
 
                obj = intel_fb_obj(fb);
-               if (i915_gem_obj_ggtt_offset(obj) == plane_config->base) {
+               if (i915_gem_object_ggtt_offset(obj, NULL) == plane_config->base) {
                        drm_framebuffer_reference(fb);
                        goto valid_fb;
                }
@@ -2565,7 +2799,7 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
         * simplest solution is to just disable the primary plane now and
         * pretend the BIOS never had it enabled.
         */
-       to_intel_plane_state(plane_state)->visible = false;
+       to_intel_plane_state(plane_state)->base.visible = false;
        crtc_state->plane_mask &= ~(1 << drm_plane_index(primary));
        intel_pre_disable_primary_noatomic(&intel_crtc->base);
        intel_plane->disable_plane(primary, &intel_crtc->base);
@@ -2583,24 +2817,188 @@ valid_fb:
        plane_state->crtc_w = fb->width;
        plane_state->crtc_h = fb->height;
 
-       intel_state->src.x1 = plane_state->src_x;
-       intel_state->src.y1 = plane_state->src_y;
-       intel_state->src.x2 = plane_state->src_x + plane_state->src_w;
-       intel_state->src.y2 = plane_state->src_y + plane_state->src_h;
-       intel_state->dst.x1 = plane_state->crtc_x;
-       intel_state->dst.y1 = plane_state->crtc_y;
-       intel_state->dst.x2 = plane_state->crtc_x + plane_state->crtc_w;
-       intel_state->dst.y2 = plane_state->crtc_y + plane_state->crtc_h;
+       intel_state->base.src.x1 = plane_state->src_x;
+       intel_state->base.src.y1 = plane_state->src_y;
+       intel_state->base.src.x2 = plane_state->src_x + plane_state->src_w;
+       intel_state->base.src.y2 = plane_state->src_y + plane_state->src_h;
+       intel_state->base.dst.x1 = plane_state->crtc_x;
+       intel_state->base.dst.y1 = plane_state->crtc_y;
+       intel_state->base.dst.x2 = plane_state->crtc_x + plane_state->crtc_w;
+       intel_state->base.dst.y2 = plane_state->crtc_y + plane_state->crtc_h;
 
        obj = intel_fb_obj(fb);
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (i915_gem_object_is_tiled(obj))
                dev_priv->preserve_bios_swizzle = true;
 
        drm_framebuffer_reference(fb);
        primary->fb = primary->state->fb = fb;
        primary->crtc = primary->state->crtc = &intel_crtc->base;
        intel_crtc->base.state->plane_mask |= (1 << drm_plane_index(primary));
-       obj->frontbuffer_bits |= to_intel_plane(primary)->frontbuffer_bit;
+       atomic_or(to_intel_plane(primary)->frontbuffer_bit,
+                 &obj->frontbuffer_bits);
+}
+
+static int skl_max_plane_width(const struct drm_framebuffer *fb, int plane,
+                              unsigned int rotation)
+{
+       int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+
+       switch (fb->modifier[plane]) {
+       case DRM_FORMAT_MOD_NONE:
+       case I915_FORMAT_MOD_X_TILED:
+               switch (cpp) {
+               case 8:
+                       return 4096;
+               case 4:
+               case 2:
+               case 1:
+                       return 8192;
+               default:
+                       MISSING_CASE(cpp);
+                       break;
+               }
+               break;
+       case I915_FORMAT_MOD_Y_TILED:
+       case I915_FORMAT_MOD_Yf_TILED:
+               switch (cpp) {
+               case 8:
+                       return 2048;
+               case 4:
+                       return 4096;
+               case 2:
+               case 1:
+                       return 8192;
+               default:
+                       MISSING_CASE(cpp);
+                       break;
+               }
+               break;
+       default:
+               MISSING_CASE(fb->modifier[plane]);
+       }
+
+       return 2048;
+}
+
+static int skl_check_main_surface(struct intel_plane_state *plane_state)
+{
+       const struct drm_i915_private *dev_priv = to_i915(plane_state->base.plane->dev);
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int rotation = plane_state->base.rotation;
+       int x = plane_state->base.src.x1 >> 16;
+       int y = plane_state->base.src.y1 >> 16;
+       int w = drm_rect_width(&plane_state->base.src) >> 16;
+       int h = drm_rect_height(&plane_state->base.src) >> 16;
+       int max_width = skl_max_plane_width(fb, 0, rotation);
+       int max_height = 4096;
+       u32 alignment, offset, aux_offset = plane_state->aux.offset;
+
+       if (w > max_width || h > max_height) {
+               DRM_DEBUG_KMS("requested Y/RGB source size %dx%d too big (limit %dx%d)\n",
+                             w, h, max_width, max_height);
+               return -EINVAL;
+       }
+
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+       offset = intel_compute_tile_offset(&x, &y, plane_state, 0);
+
+       alignment = intel_surf_alignment(dev_priv, fb->modifier[0]);
+
+       /*
+        * AUX surface offset is specified as the distance from the
+        * main surface offset, and it must be non-negative. Make
+        * sure that is what we will get.
+        */
+       if (offset > aux_offset)
+               offset = intel_adjust_tile_offset(&x, &y, plane_state, 0,
+                                                 offset, aux_offset & ~(alignment - 1));
+
+       /*
+        * When using an X-tiled surface, the plane blows up
+        * if the x offset + width exceed the stride.
+        *
+        * TODO: linear and Y-tiled seem fine, Yf untested,
+        */
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED) {
+               int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
+
+               while ((x + w) * cpp > fb->pitches[0]) {
+                       if (offset == 0) {
+                               DRM_DEBUG_KMS("Unable to find suitable display surface offset\n");
+                               return -EINVAL;
+                       }
+
+                       offset = intel_adjust_tile_offset(&x, &y, plane_state, 0,
+                                                         offset, offset - alignment);
+               }
+       }
+
+       plane_state->main.offset = offset;
+       plane_state->main.x = x;
+       plane_state->main.y = y;
+
+       return 0;
+}
+
+static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int rotation = plane_state->base.rotation;
+       int max_width = skl_max_plane_width(fb, 1, rotation);
+       int max_height = 4096;
+       int x = plane_state->base.src.x1 >> 17;
+       int y = plane_state->base.src.y1 >> 17;
+       int w = drm_rect_width(&plane_state->base.src) >> 17;
+       int h = drm_rect_height(&plane_state->base.src) >> 17;
+       u32 offset;
+
+       intel_add_fb_offsets(&x, &y, plane_state, 1);
+       offset = intel_compute_tile_offset(&x, &y, plane_state, 1);
+
+       /* FIXME not quite sure how/if these apply to the chroma plane */
+       if (w > max_width || h > max_height) {
+               DRM_DEBUG_KMS("CbCr source size %dx%d too big (limit %dx%d)\n",
+                             w, h, max_width, max_height);
+               return -EINVAL;
+       }
+
+       plane_state->aux.offset = offset;
+       plane_state->aux.x = x;
+       plane_state->aux.y = y;
+
+       return 0;
+}
+
+int skl_check_plane_surface(struct intel_plane_state *plane_state)
+{
+       const struct drm_framebuffer *fb = plane_state->base.fb;
+       unsigned int rotation = plane_state->base.rotation;
+       int ret;
+
+       /* Rotate src coordinates to match rotated GTT view */
+       if (intel_rotation_90_or_270(rotation))
+               drm_rect_rotate(&plane_state->base.src,
+                               fb->width, fb->height, DRM_ROTATE_270);
+
+       /*
+        * Handle the AUX surface first since
+        * the main surface setup depends on it.
+        */
+       if (fb->pixel_format == DRM_FORMAT_NV12) {
+               ret = skl_check_nv12_aux_surface(plane_state);
+               if (ret)
+                       return ret;
+       } else {
+               plane_state->aux.offset = ~0xfff;
+               plane_state->aux.x = 0;
+               plane_state->aux.y = 0;
+       }
+
+       ret = skl_check_main_surface(plane_state);
+       if (ret)
+               return ret;
+
+       return 0;
 }
 
 static void i9xx_update_primary_plane(struct drm_plane *primary,
@@ -2617,9 +3015,8 @@ static void i9xx_update_primary_plane(struct drm_plane *primary,
        u32 dspcntr;
        i915_reg_t reg = DSPCNTR(plane);
        unsigned int rotation = plane_state->base.rotation;
-       int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-       int x = plane_state->src.x1 >> 16;
-       int y = plane_state->src.y1 >> 16;
+       int x = plane_state->base.src.x1 >> 16;
+       int y = plane_state->base.src.y1 >> 16;
 
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
@@ -2670,37 +3067,31 @@ static void i9xx_update_primary_plane(struct drm_plane *primary,
                BUG();
        }
 
-       if (INTEL_INFO(dev)->gen >= 4 &&
-           obj->tiling_mode != I915_TILING_NONE)
+       if (INTEL_GEN(dev_priv) >= 4 &&
+           fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                dspcntr |= DISPPLANE_TILED;
 
        if (IS_G4X(dev))
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
-       linear_offset = y * fb->pitches[0] + x * cpp;
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
 
-       if (INTEL_INFO(dev)->gen >= 4) {
+       if (INTEL_INFO(dev)->gen >= 4)
                intel_crtc->dspaddr_offset =
-                       intel_compute_tile_offset(&x, &y, fb, 0,
-                                                 fb->pitches[0], rotation);
-               linear_offset -= intel_crtc->dspaddr_offset;
-       } else {
-               intel_crtc->dspaddr_offset = linear_offset;
-       }
+                       intel_compute_tile_offset(&x, &y, plane_state, 0);
 
-       if (rotation == BIT(DRM_ROTATE_180)) {
+       if (rotation == DRM_ROTATE_180) {
                dspcntr |= DISPPLANE_ROTATE_180;
 
                x += (crtc_state->pipe_src_w - 1);
                y += (crtc_state->pipe_src_h - 1);
-
-               /* Finding the last pixel of the last line of the display
-               data and adding to linear_offset*/
-               linear_offset +=
-                       (crtc_state->pipe_src_h - 1) * fb->pitches[0] +
-                       (crtc_state->pipe_src_w - 1) * cpp;
        }
 
+       linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
+
+       if (INTEL_INFO(dev)->gen < 4)
+               intel_crtc->dspaddr_offset = linear_offset;
+
        intel_crtc->adjusted_x = x;
        intel_crtc->adjusted_y = y;
 
@@ -2709,11 +3100,12 @@ static void i9xx_update_primary_plane(struct drm_plane *primary,
        I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
        if (INTEL_INFO(dev)->gen >= 4) {
                I915_WRITE(DSPSURF(plane),
-                          i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+                          intel_fb_gtt_offset(fb, rotation) +
+                          intel_crtc->dspaddr_offset);
                I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
                I915_WRITE(DSPLINOFF(plane), linear_offset);
        } else
-               I915_WRITE(DSPADDR(plane), i915_gem_obj_ggtt_offset(obj) + linear_offset);
+               I915_WRITE(DSPADDR(plane), i915_gem_object_ggtt_offset(obj, NULL) + linear_offset);
        POSTING_READ(reg);
 }
 
@@ -2741,15 +3133,13 @@ static void ironlake_update_primary_plane(struct drm_plane *primary,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        int plane = intel_crtc->plane;
        u32 linear_offset;
        u32 dspcntr;
        i915_reg_t reg = DSPCNTR(plane);
        unsigned int rotation = plane_state->base.rotation;
-       int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-       int x = plane_state->src.x1 >> 16;
-       int y = plane_state->src.y1 >> 16;
+       int x = plane_state->base.src.x1 >> 16;
+       int y = plane_state->base.src.y1 >> 16;
 
        dspcntr = DISPPLANE_GAMMA_ENABLE;
        dspcntr |= DISPLAY_PLANE_ENABLE;
@@ -2780,32 +3170,28 @@ static void ironlake_update_primary_plane(struct drm_plane *primary,
                BUG();
        }
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                dspcntr |= DISPPLANE_TILED;
 
        if (!IS_HASWELL(dev) && !IS_BROADWELL(dev))
                dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 
-       linear_offset = y * fb->pitches[0] + x * cpp;
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+
        intel_crtc->dspaddr_offset =
-               intel_compute_tile_offset(&x, &y, fb, 0,
-                                         fb->pitches[0], rotation);
-       linear_offset -= intel_crtc->dspaddr_offset;
-       if (rotation == BIT(DRM_ROTATE_180)) {
+               intel_compute_tile_offset(&x, &y, plane_state, 0);
+
+       if (rotation == DRM_ROTATE_180) {
                dspcntr |= DISPPLANE_ROTATE_180;
 
                if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
                        x += (crtc_state->pipe_src_w - 1);
                        y += (crtc_state->pipe_src_h - 1);
-
-                       /* Finding the last pixel of the last line of the display
-                       data and adding to linear_offset*/
-                       linear_offset +=
-                               (crtc_state->pipe_src_h - 1) * fb->pitches[0] +
-                               (crtc_state->pipe_src_w - 1) * cpp;
                }
        }
 
+       linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
+
        intel_crtc->adjusted_x = x;
        intel_crtc->adjusted_y = y;
 
@@ -2813,7 +3199,8 @@ static void ironlake_update_primary_plane(struct drm_plane *primary,
 
        I915_WRITE(DSPSTRIDE(plane), fb->pitches[0]);
        I915_WRITE(DSPSURF(plane),
-                  i915_gem_obj_ggtt_offset(obj) + intel_crtc->dspaddr_offset);
+                  intel_fb_gtt_offset(fb, rotation) +
+                  intel_crtc->dspaddr_offset);
        if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                I915_WRITE(DSPOFFSET(plane), (y << 16) | x);
        } else {
@@ -2835,32 +3222,21 @@ u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv,
        }
 }
 
-u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
-                          struct drm_i915_gem_object *obj,
-                          unsigned int plane)
+u32 intel_fb_gtt_offset(struct drm_framebuffer *fb,
+                       unsigned int rotation)
 {
+       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        struct i915_ggtt_view view;
        struct i915_vma *vma;
-       u64 offset;
 
-       intel_fill_fb_ggtt_view(&view, intel_plane->base.state->fb,
-                               intel_plane->base.state->rotation);
+       intel_fill_fb_ggtt_view(&view, fb, rotation);
 
-       vma = i915_gem_obj_to_ggtt_view(obj, &view);
+       vma = i915_gem_object_to_ggtt(obj, &view);
        if (WARN(!vma, "ggtt vma for display object not found! (view=%u)\n",
-               view.type))
+                view.type))
                return -1;
 
-       offset = vma->node.start;
-
-       if (plane == 1) {
-               offset += vma->ggtt_view.params.rotated.uv_start_page *
-                         PAGE_SIZE;
-       }
-
-       WARN_ON(upper_32_bits(offset));
-
-       return lower_32_bits(offset);
+       return i915_ggtt_offset(vma);
 }
 
 static void skl_detach_scaler(struct intel_crtc *intel_crtc, int id)
@@ -2890,6 +3266,28 @@ static void skl_detach_scalers(struct intel_crtc *intel_crtc)
        }
 }
 
+u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane,
+                    unsigned int rotation)
+{
+       const struct drm_i915_private *dev_priv = to_i915(fb->dev);
+       u32 stride = intel_fb_pitch(fb, plane, rotation);
+
+       /*
+        * The stride is either expressed as a multiple of 64 bytes chunks for
+        * linear buffers or in number of tiles for tiled buffers.
+        */
+       if (intel_rotation_90_or_270(rotation)) {
+               int cpp = drm_format_plane_cpp(fb->pixel_format, plane);
+
+               stride /= intel_tile_height(dev_priv, fb->modifier[0], cpp);
+       } else {
+               stride /= intel_fb_stride_alignment(dev_priv, fb->modifier[0],
+                                                   fb->pixel_format);
+       }
+
+       return stride;
+}
+
 u32 skl_plane_ctl_format(uint32_t pixel_format)
 {
        switch (pixel_format) {
@@ -2952,17 +3350,17 @@ u32 skl_plane_ctl_tiling(uint64_t fb_modifier)
 u32 skl_plane_ctl_rotation(unsigned int rotation)
 {
        switch (rotation) {
-       case BIT(DRM_ROTATE_0):
+       case DRM_ROTATE_0:
                break;
        /*
         * DRM_ROTATE_ is counter clockwise to stay compatible with Xrandr
         * while i915 HW rotation is clockwise, thats why this swapping.
         */
-       case BIT(DRM_ROTATE_90):
+       case DRM_ROTATE_90:
                return PLANE_CTL_ROTATE_270;
-       case BIT(DRM_ROTATE_180):
+       case DRM_ROTATE_180:
                return PLANE_CTL_ROTATE_180;
-       case BIT(DRM_ROTATE_270):
+       case DRM_ROTATE_270:
                return PLANE_CTL_ROTATE_90;
        default:
                MISSING_CASE(rotation);
@@ -2979,22 +3377,21 @@ static void skylake_update_primary_plane(struct drm_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+       const struct skl_wm_values *wm = &dev_priv->wm.skl_results;
        int pipe = intel_crtc->pipe;
-       u32 plane_ctl, stride_div, stride;
-       u32 tile_height, plane_offset, plane_size;
+       u32 plane_ctl;
        unsigned int rotation = plane_state->base.rotation;
-       int x_offset, y_offset;
-       u32 surf_addr;
+       u32 stride = skl_plane_stride(fb, 0, rotation);
+       u32 surf_addr = plane_state->main.offset;
        int scaler_id = plane_state->scaler_id;
-       int src_x = plane_state->src.x1 >> 16;
-       int src_y = plane_state->src.y1 >> 16;
-       int src_w = drm_rect_width(&plane_state->src) >> 16;
-       int src_h = drm_rect_height(&plane_state->src) >> 16;
-       int dst_x = plane_state->dst.x1;
-       int dst_y = plane_state->dst.y1;
-       int dst_w = drm_rect_width(&plane_state->dst);
-       int dst_h = drm_rect_height(&plane_state->dst);
+       int src_x = plane_state->main.x;
+       int src_y = plane_state->main.y;
+       int src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       int src_h = drm_rect_height(&plane_state->base.src) >> 16;
+       int dst_x = plane_state->base.dst.x1;
+       int dst_y = plane_state->base.dst.y1;
+       int dst_w = drm_rect_width(&plane_state->base.dst);
+       int dst_h = drm_rect_height(&plane_state->base.dst);
 
        plane_ctl = PLANE_CTL_ENABLE |
                    PLANE_CTL_PIPE_GAMMA_ENABLE |
@@ -3005,36 +3402,24 @@ static void skylake_update_primary_plane(struct drm_plane *plane,
        plane_ctl |= PLANE_CTL_PLANE_GAMMA_DISABLE;
        plane_ctl |= skl_plane_ctl_rotation(rotation);
 
-       stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
-                                              fb->pixel_format);
-       surf_addr = intel_plane_obj_offset(to_intel_plane(plane), obj, 0);
+       /* Sizes are 0 based */
+       src_w--;
+       src_h--;
+       dst_w--;
+       dst_h--;
 
-       WARN_ON(drm_rect_width(&plane_state->src) == 0);
+       intel_crtc->dspaddr_offset = surf_addr;
 
-       if (intel_rotation_90_or_270(rotation)) {
-               int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-
-               /* stride = Surface height in tiles */
-               tile_height = intel_tile_height(dev_priv, fb->modifier[0], cpp);
-               stride = DIV_ROUND_UP(fb->height, tile_height);
-               x_offset = stride * tile_height - src_y - src_h;
-               y_offset = src_x;
-               plane_size = (src_w - 1) << 16 | (src_h - 1);
-       } else {
-               stride = fb->pitches[0] / stride_div;
-               x_offset = src_x;
-               y_offset = src_y;
-               plane_size = (src_h - 1) << 16 | (src_w - 1);
-       }
-       plane_offset = y_offset << 16 | x_offset;
+       intel_crtc->adjusted_x = src_x;
+       intel_crtc->adjusted_y = src_y;
 
-       intel_crtc->adjusted_x = x_offset;
-       intel_crtc->adjusted_y = y_offset;
+       if (wm->dirty_pipes & drm_crtc_mask(&intel_crtc->base))
+               skl_write_plane_wm(intel_crtc, wm, 0);
 
        I915_WRITE(PLANE_CTL(pipe, 0), plane_ctl);
-       I915_WRITE(PLANE_OFFSET(pipe, 0), plane_offset);
-       I915_WRITE(PLANE_SIZE(pipe, 0), plane_size);
+       I915_WRITE(PLANE_OFFSET(pipe, 0), (src_y << 16) | src_x);
        I915_WRITE(PLANE_STRIDE(pipe, 0), stride);
+       I915_WRITE(PLANE_SIZE(pipe, 0), (src_h << 16) | src_w);
 
        if (scaler_id >= 0) {
                uint32_t ps_ctrl = 0;
@@ -3051,7 +3436,8 @@ static void skylake_update_primary_plane(struct drm_plane *plane,
                I915_WRITE(PLANE_POS(pipe, 0), (dst_y << 16) | dst_x);
        }
 
-       I915_WRITE(PLANE_SURF(pipe, 0), surf_addr);
+       I915_WRITE(PLANE_SURF(pipe, 0),
+                  intel_fb_gtt_offset(fb, rotation) + surf_addr);
 
        POSTING_READ(PLANE_SURF(pipe, 0));
 }
@@ -3061,7 +3447,15 @@ static void skylake_disable_primary_plane(struct drm_plane *primary,
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       int pipe = to_intel_crtc(crtc)->pipe;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+
+       /*
+        * We only populate skl_results on watermark updates, and if the
+        * plane's visiblity isn't actually changing neither is its watermarks.
+        */
+       if (!crtc->primary->state->visible)
+               skl_write_plane_wm(intel_crtc, &dev_priv->wm.skl_results, 0);
 
        I915_WRITE(PLANE_CTL(pipe, 0), 0);
        I915_WRITE(PLANE_SURF(pipe, 0), 0);
@@ -3096,7 +3490,7 @@ static void intel_update_primary_planes(struct drm_device *dev)
                struct intel_plane_state *plane_state =
                        to_intel_plane_state(plane->base.state);
 
-               if (plane_state->visible)
+               if (plane_state->base.visible)
                        plane->update_plane(&plane->base,
                                            to_intel_crtc_state(crtc->state),
                                            plane_state);
@@ -3135,6 +3529,12 @@ __intel_display_resume(struct drm_device *dev,
        return ret;
 }
 
+static bool gpu_reset_clobbers_display(struct drm_i915_private *dev_priv)
+{
+       return intel_has_gpu_reset(dev_priv) &&
+               INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv);
+}
+
 void intel_prepare_reset(struct drm_i915_private *dev_priv)
 {
        struct drm_device *dev = &dev_priv->drm;
@@ -3142,10 +3542,6 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv)
        struct drm_atomic_state *state;
        int ret;
 
-       /* no reset support for gen2 */
-       if (IS_GEN2(dev_priv))
-               return;
-
        /*
         * Need mode_config.mutex so that we don't
         * trample ongoing ->detect() and whatnot.
@@ -3161,7 +3557,8 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv)
        }
 
        /* reset doesn't touch the display, but flips might get nuked anyway, */
-       if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv))
+       if (!i915.force_reset_modeset_test &&
+           !gpu_reset_clobbers_display(dev_priv))
                return;
 
        /*
@@ -3204,24 +3601,28 @@ void intel_finish_reset(struct drm_i915_private *dev_priv)
         */
        intel_complete_page_flips(dev_priv);
 
-       /* no reset support for gen2 */
-       if (IS_GEN2(dev_priv))
-               return;
+       dev_priv->modeset_restore_state = NULL;
 
        dev_priv->modeset_restore_state = NULL;
 
        /* reset doesn't touch the display */
-       if (INTEL_GEN(dev_priv) >= 5 || IS_G4X(dev_priv)) {
-               /*
-                * Flips in the rings have been nuked by the reset,
-                * so update the base address of all primary
-                * planes to the the last fb to make sure we're
-                * showing the correct fb after a reset.
-                *
-                * FIXME: Atomic will make this obsolete since we won't schedule
-                * CS-based flips (which might get lost in gpu resets) any more.
-                */
-               intel_update_primary_planes(dev);
+       if (!gpu_reset_clobbers_display(dev_priv)) {
+               if (!state) {
+                       /*
+                        * Flips in the rings have been nuked by the reset,
+                        * so update the base address of all primary
+                        * planes to the the last fb to make sure we're
+                        * showing the correct fb after a reset.
+                        *
+                        * FIXME: Atomic will make this obsolete since we won't schedule
+                        * CS-based flips (which might get lost in gpu resets) any more.
+                        */
+                       intel_update_primary_planes(dev);
+               } else {
+                       ret = __intel_display_resume(dev, state);
+                       if (ret)
+                               DRM_ERROR("Restoring old state failed with %i\n", ret);
+               }
        } else {
                /*
                 * The display has been reset as well,
@@ -3230,6 +3631,7 @@ void intel_finish_reset(struct drm_i915_private *dev_priv)
                intel_runtime_pm_disable_interrupts(dev_priv);
                intel_runtime_pm_enable_interrupts(dev_priv);
 
+               intel_pps_unlock_regs_wa(dev_priv);
                intel_modeset_init_hw(dev);
 
                spin_lock_irq(&dev_priv->irq_lock);
@@ -3249,15 +3651,26 @@ void intel_finish_reset(struct drm_i915_private *dev_priv)
        mutex_unlock(&dev->mode_config.mutex);
 }
 
+static bool abort_flip_on_reset(struct intel_crtc *crtc)
+{
+       struct i915_gpu_error *error = &to_i915(crtc->base.dev)->gpu_error;
+
+       if (i915_reset_in_progress(error))
+               return true;
+
+       if (crtc->reset_count != i915_reset_count(error))
+               return true;
+
+       return false;
+}
+
 static bool intel_crtc_has_pending_flip(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       unsigned reset_counter;
        bool pending;
 
-       reset_counter = i915_reset_counter(&to_i915(dev)->gpu_error);
-       if (intel_crtc->reset_counter != reset_counter)
+       if (abort_flip_on_reset(intel_crtc))
                return false;
 
        spin_lock_irq(&dev->event_lock);
@@ -3900,7 +4313,7 @@ static int intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
        return 0;
 }
 
-static void lpt_disable_iclkip(struct drm_i915_private *dev_priv)
+void lpt_disable_iclkip(struct drm_i915_private *dev_priv)
 {
        u32 temp;
 
@@ -4323,7 +4736,7 @@ int skl_update_scaler_crtc(struct intel_crtc_state *state)
                      intel_crtc->pipe, SKL_CRTC_INDEX);
 
        return skl_update_scaler(state, !state->base.active, SKL_CRTC_INDEX,
-               &state->scaler_state.scaler_id, BIT(DRM_ROTATE_0),
+               &state->scaler_state.scaler_id, DRM_ROTATE_0,
                state->pipe_src_w, state->pipe_src_h,
                adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_vdisplay);
 }
@@ -4348,7 +4761,7 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
        struct drm_framebuffer *fb = plane_state->base.fb;
        int ret;
 
-       bool force_detach = !fb || !plane_state->visible;
+       bool force_detach = !fb || !plane_state->base.visible;
 
        DRM_DEBUG_KMS("Updating scaler for [PLANE:%d:%s] scaler_user index %u.%u\n",
                      intel_plane->base.base.id, intel_plane->base.name,
@@ -4358,10 +4771,10 @@ static int skl_update_scaler_plane(struct intel_crtc_state *crtc_state,
                                drm_plane_index(&intel_plane->base),
                                &plane_state->scaler_id,
                                plane_state->base.rotation,
-                               drm_rect_width(&plane_state->src) >> 16,
-                               drm_rect_height(&plane_state->src) >> 16,
-                               drm_rect_width(&plane_state->dst),
-                               drm_rect_height(&plane_state->dst));
+                               drm_rect_width(&plane_state->base.src) >> 16,
+                               drm_rect_height(&plane_state->base.src) >> 16,
+                               drm_rect_width(&plane_state->base.dst),
+                               drm_rect_height(&plane_state->base.dst));
 
        if (ret || plane_state->scaler_id < 0)
                return ret;
@@ -4639,12 +5052,11 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state)
        struct drm_atomic_state *old_state = old_crtc_state->base.state;
        struct intel_crtc_state *pipe_config =
                to_intel_crtc_state(crtc->base.state);
-       struct drm_device *dev = crtc->base.dev;
        struct drm_plane *primary = crtc->base.primary;
        struct drm_plane_state *old_pri_state =
                drm_atomic_get_existing_plane_state(old_state, primary);
 
-       intel_frontbuffer_flip(dev, pipe_config->fb_bits);
+       intel_frontbuffer_flip(to_i915(crtc->base.dev), pipe_config->fb_bits);
 
        crtc->wm.cxsr_allowed = true;
 
@@ -4659,9 +5071,9 @@ static void intel_post_plane_update(struct intel_crtc_state *old_crtc_state)
 
                intel_fbc_post_update(crtc);
 
-               if (primary_state->visible &&
+               if (primary_state->base.visible &&
                    (needs_modeset(&pipe_config->base) ||
-                    !old_primary_state->visible))
+                    !old_primary_state->base.visible))
                        intel_post_enable_primary(&crtc->base);
        }
 }
@@ -4687,8 +5099,8 @@ static void intel_pre_plane_update(struct intel_crtc_state *old_crtc_state)
 
                intel_fbc_pre_update(crtc, pipe_config, primary_state);
 
-               if (old_primary_state->visible &&
-                   (modeset || !primary_state->visible))
+               if (old_primary_state->base.visible &&
+                   (modeset || !primary_state->base.visible))
                        intel_pre_disable_primary(&crtc->base);
        }
 
@@ -4767,18 +5179,140 @@ static void intel_crtc_disable_planes(struct drm_crtc *crtc, unsigned plane_mask
         * to compute the mask of flip planes precisely. For the time being
         * consider this a flip to a NULL plane.
         */
-       intel_frontbuffer_flip(dev, INTEL_FRONTBUFFER_ALL_MASK(pipe));
+       intel_frontbuffer_flip(to_i915(dev), INTEL_FRONTBUFFER_ALL_MASK(pipe));
 }
 
-static void ironlake_crtc_enable(struct drm_crtc *crtc)
+static void intel_encoders_pre_pll_enable(struct drm_crtc *crtc,
+                                         struct intel_crtc_state *crtc_state,
+                                         struct drm_atomic_state *old_state)
 {
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct drm_connector_state *conn_state = conn->state;
+               struct intel_encoder *encoder =
+                       to_intel_encoder(conn_state->best_encoder);
+
+               if (conn_state->crtc != crtc)
+                       continue;
+
+               if (encoder->pre_pll_enable)
+                       encoder->pre_pll_enable(encoder, crtc_state, conn_state);
+       }
+}
+
+static void intel_encoders_pre_enable(struct drm_crtc *crtc,
+                                     struct intel_crtc_state *crtc_state,
+                                     struct drm_atomic_state *old_state)
+{
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct drm_connector_state *conn_state = conn->state;
+               struct intel_encoder *encoder =
+                       to_intel_encoder(conn_state->best_encoder);
+
+               if (conn_state->crtc != crtc)
+                       continue;
+
+               if (encoder->pre_enable)
+                       encoder->pre_enable(encoder, crtc_state, conn_state);
+       }
+}
+
+static void intel_encoders_enable(struct drm_crtc *crtc,
+                                 struct intel_crtc_state *crtc_state,
+                                 struct drm_atomic_state *old_state)
+{
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct drm_connector_state *conn_state = conn->state;
+               struct intel_encoder *encoder =
+                       to_intel_encoder(conn_state->best_encoder);
+
+               if (conn_state->crtc != crtc)
+                       continue;
+
+               encoder->enable(encoder, crtc_state, conn_state);
+               intel_opregion_notify_encoder(encoder, true);
+       }
+}
+
+static void intel_encoders_disable(struct drm_crtc *crtc,
+                                  struct intel_crtc_state *old_crtc_state,
+                                  struct drm_atomic_state *old_state)
+{
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct intel_encoder *encoder =
+                       to_intel_encoder(old_conn_state->best_encoder);
+
+               if (old_conn_state->crtc != crtc)
+                       continue;
+
+               intel_opregion_notify_encoder(encoder, false);
+               encoder->disable(encoder, old_crtc_state, old_conn_state);
+       }
+}
+
+static void intel_encoders_post_disable(struct drm_crtc *crtc,
+                                       struct intel_crtc_state *old_crtc_state,
+                                       struct drm_atomic_state *old_state)
+{
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct intel_encoder *encoder =
+                       to_intel_encoder(old_conn_state->best_encoder);
+
+               if (old_conn_state->crtc != crtc)
+                       continue;
+
+               if (encoder->post_disable)
+                       encoder->post_disable(encoder, old_crtc_state, old_conn_state);
+       }
+}
+
+static void intel_encoders_post_pll_disable(struct drm_crtc *crtc,
+                                           struct intel_crtc_state *old_crtc_state,
+                                           struct drm_atomic_state *old_state)
+{
+       struct drm_connector_state *old_conn_state;
+       struct drm_connector *conn;
+       int i;
+
+       for_each_connector_in_state(old_state, conn, old_conn_state, i) {
+               struct intel_encoder *encoder =
+                       to_intel_encoder(old_conn_state->best_encoder);
+
+               if (old_conn_state->crtc != crtc)
+                       continue;
+
+               if (encoder->post_pll_disable)
+                       encoder->post_pll_disable(encoder, old_crtc_state, old_conn_state);
+       }
+}
+
+static void ironlake_crtc_enable(struct intel_crtc_state *pipe_config,
+                                struct drm_atomic_state *old_state)
+{
+       struct drm_crtc *crtc = pipe_config->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
-       struct intel_crtc_state *pipe_config =
-               to_intel_crtc_state(crtc->state);
 
        if (WARN_ON(intel_crtc->active))
                return;
@@ -4816,9 +5350,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
 
        intel_crtc->active = true;
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_enable)
-                       encoder->pre_enable(encoder);
+       intel_encoders_pre_enable(crtc, pipe_config, old_state);
 
        if (intel_crtc->config->has_pch_encoder) {
                /* Note: FDI PLL enabling _must_ be done before we enable the
@@ -4848,8 +5380,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        assert_vblank_disabled(crtc);
        drm_crtc_vblank_on(crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->enable(encoder);
+       intel_encoders_enable(crtc, pipe_config, old_state);
 
        if (HAS_PCH_CPT(dev))
                cpt_verify_modeset(dev, intel_crtc->pipe);
@@ -4867,16 +5398,15 @@ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
        return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A;
 }
 
-static void haswell_crtc_enable(struct drm_crtc *crtc)
+static void haswell_crtc_enable(struct intel_crtc_state *pipe_config,
+                               struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = pipe_config->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe, hsw_workaround_pipe;
        enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder;
-       struct intel_crtc_state *pipe_config =
-               to_intel_crtc_state(crtc->state);
 
        if (WARN_ON(intel_crtc->active))
                return;
@@ -4885,9 +5415,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
                                                      false);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_pll_enable)
-                       encoder->pre_pll_enable(encoder);
+       intel_encoders_pre_pll_enable(crtc, pipe_config, old_state);
 
        if (intel_crtc->config->shared_dpll)
                intel_enable_shared_dpll(intel_crtc);
@@ -4925,10 +5453,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        else
                intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               if (encoder->pre_enable)
-                       encoder->pre_enable(encoder);
-       }
+       intel_encoders_pre_enable(crtc, pipe_config, old_state);
 
        if (intel_crtc->config->has_pch_encoder)
                dev_priv->display.fdi_link_train(crtc);
@@ -4969,10 +5494,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        assert_vblank_disabled(crtc);
        drm_crtc_vblank_on(crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               encoder->enable(encoder);
-               intel_opregion_notify_encoder(encoder, true);
-       }
+       intel_encoders_enable(crtc, pipe_config, old_state);
 
        if (intel_crtc->config->has_pch_encoder) {
                intel_wait_for_vblank(dev, pipe);
@@ -5006,12 +5528,13 @@ static void ironlake_pfit_disable(struct intel_crtc *crtc, bool force)
        }
 }
 
-static void ironlake_crtc_disable(struct drm_crtc *crtc)
+static void ironlake_crtc_disable(struct intel_crtc_state *old_crtc_state,
+                                 struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = old_crtc_state->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
 
        /*
@@ -5024,8 +5547,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
                intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, false);
        }
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->disable(encoder);
+       intel_encoders_disable(crtc, old_crtc_state, old_state);
 
        drm_crtc_vblank_off(crtc);
        assert_vblank_disabled(crtc);
@@ -5037,9 +5559,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        if (intel_crtc->config->has_pch_encoder)
                ironlake_fdi_disable(crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->post_disable)
-                       encoder->post_disable(encoder);
+       intel_encoders_post_disable(crtc, old_crtc_state, old_state);
 
        if (intel_crtc->config->has_pch_encoder) {
                ironlake_disable_pch_transcoder(dev_priv, pipe);
@@ -5069,22 +5589,20 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        intel_set_pch_fifo_underrun_reporting(dev_priv, pipe, true);
 }
 
-static void haswell_crtc_disable(struct drm_crtc *crtc)
+static void haswell_crtc_disable(struct intel_crtc_state *old_crtc_state,
+                                struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = old_crtc_state->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
        enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder;
 
        if (intel_crtc->config->has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
                                                      false);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
-               intel_opregion_notify_encoder(encoder, false);
-               encoder->disable(encoder);
-       }
+       intel_encoders_disable(crtc, old_crtc_state, old_state);
 
        drm_crtc_vblank_off(crtc);
        assert_vblank_disabled(crtc);
@@ -5107,18 +5625,11 @@ static void haswell_crtc_disable(struct drm_crtc *crtc)
        if (!transcoder_is_dsi(cpu_transcoder))
                intel_ddi_disable_pipe_clock(intel_crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->post_disable)
-                       encoder->post_disable(encoder);
-
-       if (intel_crtc->config->has_pch_encoder) {
-               lpt_disable_pch_transcoder(dev_priv);
-               lpt_disable_iclkip(dev_priv);
-               intel_ddi_fdi_disable(crtc);
+       intel_encoders_post_disable(crtc, old_crtc_state, old_state);
 
+       if (old_crtc_state->has_pch_encoder)
                intel_set_pch_fifo_underrun_reporting(dev_priv, TRANSCODER_A,
                                                      true);
-       }
 }
 
 static void i9xx_pfit_enable(struct intel_crtc *crtc)
@@ -6174,14 +6685,13 @@ static void valleyview_modeset_commit_cdclk(struct drm_atomic_state *old_state)
        intel_display_power_put(dev_priv, POWER_DOMAIN_PIPE_A);
 }
 
-static void valleyview_crtc_enable(struct drm_crtc *crtc)
+static void valleyview_crtc_enable(struct intel_crtc_state *pipe_config,
+                                  struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = pipe_config->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
-       struct intel_crtc_state *pipe_config =
-               to_intel_crtc_state(crtc->state);
        int pipe = intel_crtc->pipe;
 
        if (WARN_ON(intel_crtc->active))
@@ -6206,9 +6716,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
 
        intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_pll_enable)
-                       encoder->pre_pll_enable(encoder);
+       intel_encoders_pre_pll_enable(crtc, pipe_config, old_state);
 
        if (IS_CHERRYVIEW(dev)) {
                chv_prepare_pll(intel_crtc, intel_crtc->config);
@@ -6218,9 +6726,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
                vlv_enable_pll(intel_crtc, intel_crtc->config);
        }
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_enable)
-                       encoder->pre_enable(encoder);
+       intel_encoders_pre_enable(crtc, pipe_config, old_state);
 
        i9xx_pfit_enable(intel_crtc);
 
@@ -6232,8 +6738,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
        assert_vblank_disabled(crtc);
        drm_crtc_vblank_on(crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->enable(encoder);
+       intel_encoders_enable(crtc, pipe_config, old_state);
 }
 
 static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
@@ -6245,14 +6750,13 @@ static void i9xx_set_pll_dividers(struct intel_crtc *crtc)
        I915_WRITE(FP1(crtc->pipe), crtc->config->dpll_hw_state.fp1);
 }
 
-static void i9xx_crtc_enable(struct drm_crtc *crtc)
+static void i9xx_crtc_enable(struct intel_crtc_state *pipe_config,
+                            struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = pipe_config->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
-       struct intel_crtc_state *pipe_config =
-               to_intel_crtc_state(crtc->state);
        enum pipe pipe = intel_crtc->pipe;
 
        if (WARN_ON(intel_crtc->active))
@@ -6273,9 +6777,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        if (!IS_GEN2(dev))
                intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, true);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->pre_enable)
-                       encoder->pre_enable(encoder);
+       intel_encoders_pre_enable(crtc, pipe_config, old_state);
 
        i9xx_enable_pll(intel_crtc);
 
@@ -6289,8 +6791,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        assert_vblank_disabled(crtc);
        drm_crtc_vblank_on(crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->enable(encoder);
+       intel_encoders_enable(crtc, pipe_config, old_state);
 }
 
 static void i9xx_pfit_disable(struct intel_crtc *crtc)
@@ -6308,12 +6809,13 @@ static void i9xx_pfit_disable(struct intel_crtc *crtc)
        I915_WRITE(PFIT_CONTROL, 0);
 }
 
-static void i9xx_crtc_disable(struct drm_crtc *crtc)
+static void i9xx_crtc_disable(struct intel_crtc_state *old_crtc_state,
+                             struct drm_atomic_state *old_state)
 {
+       struct drm_crtc *crtc = old_crtc_state->base.crtc;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
 
        /*
@@ -6323,8 +6825,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        if (IS_GEN2(dev))
                intel_wait_for_vblank(dev, pipe);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               encoder->disable(encoder);
+       intel_encoders_disable(crtc, old_crtc_state, old_state);
 
        drm_crtc_vblank_off(crtc);
        assert_vblank_disabled(crtc);
@@ -6333,9 +6834,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
 
        i9xx_pfit_disable(intel_crtc);
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->post_disable)
-                       encoder->post_disable(encoder);
+       intel_encoders_post_disable(crtc, old_crtc_state, old_state);
 
        if (!intel_crtc_has_type(intel_crtc->config, INTEL_OUTPUT_DSI)) {
                if (IS_CHERRYVIEW(dev))
@@ -6346,9 +6845,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
                        i9xx_disable_pll(intel_crtc);
        }
 
-       for_each_encoder_on_crtc(dev, crtc, encoder)
-               if (encoder->post_pll_disable)
-                       encoder->post_pll_disable(encoder);
+       intel_encoders_post_pll_disable(crtc, old_crtc_state, old_state);
 
        if (!IS_GEN2(dev))
                intel_set_cpu_fifo_underrun_reporting(dev_priv, pipe, false);
@@ -6361,20 +6858,34 @@ static void intel_crtc_disable_noatomic(struct drm_crtc *crtc)
        struct drm_i915_private *dev_priv = to_i915(crtc->dev);
        enum intel_display_power_domain domain;
        unsigned long domains;
+       struct drm_atomic_state *state;
+       struct intel_crtc_state *crtc_state;
+       int ret;
 
        if (!intel_crtc->active)
                return;
 
-       if (to_intel_plane_state(crtc->primary->state)->visible) {
+       if (to_intel_plane_state(crtc->primary->state)->base.visible) {
                WARN_ON(intel_crtc->flip_work);
 
                intel_pre_disable_primary_noatomic(crtc);
 
                intel_crtc_disable_planes(crtc, 1 << drm_plane_index(crtc->primary));
-               to_intel_plane_state(crtc->primary->state)->visible = false;
+               to_intel_plane_state(crtc->primary->state)->base.visible = false;
        }
 
-       dev_priv->display.crtc_disable(crtc);
+       state = drm_atomic_state_alloc(crtc->dev);
+       state->acquire_ctx = crtc->dev->mode_config.acquire_ctx;
+
+       /* Everything's already locked, -EDEADLK can't happen. */
+       crtc_state = intel_atomic_get_crtc_state(state, intel_crtc);
+       ret = drm_atomic_add_affected_connectors(state, crtc);
+
+       WARN_ON(IS_ERR(crtc_state) || ret);
+
+       dev_priv->display.crtc_disable(crtc_state, state);
+
+       drm_atomic_state_free(state);
 
        DRM_DEBUG_KMS("[CRTC:%d:%s] hw state adjusted, was enabled, now disabled\n",
                      crtc->base.id, crtc->name);
@@ -6889,9 +7400,10 @@ static int i9xx_misc_get_display_clock_speed(struct drm_device *dev)
 
 static int pnv_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        u16 gcfgc = 0;
 
-       pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+       pci_read_config_word(pdev, GCFGC, &gcfgc);
 
        switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
        case GC_DISPLAY_CLOCK_267_MHZ_PNV:
@@ -6913,9 +7425,10 @@ static int pnv_get_display_clock_speed(struct drm_device *dev)
 
 static int i915gm_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        u16 gcfgc = 0;
 
-       pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+       pci_read_config_word(pdev, GCFGC, &gcfgc);
 
        if (gcfgc & GC_LOW_FREQUENCY_ENABLE)
                return 133333;
@@ -6937,6 +7450,7 @@ static int i865_get_display_clock_speed(struct drm_device *dev)
 
 static int i85x_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        u16 hpllcc = 0;
 
        /*
@@ -6944,10 +7458,10 @@ static int i85x_get_display_clock_speed(struct drm_device *dev)
         * encoding is different :(
         * FIXME is this the right way to detect 852GM/852GMV?
         */
-       if (dev->pdev->revision == 0x1)
+       if (pdev->revision == 0x1)
                return 133333;
 
-       pci_bus_read_config_word(dev->pdev->bus,
+       pci_bus_read_config_word(pdev->bus,
                                 PCI_DEVFN(0, 3), HPLLCC, &hpllcc);
 
        /* Assume that the hardware is in the high speed state.  This
@@ -7048,10 +7562,11 @@ static unsigned int intel_hpll_vco(struct drm_device *dev)
 
 static int gm45_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        unsigned int cdclk_sel, vco = intel_hpll_vco(dev);
        uint16_t tmp = 0;
 
-       pci_read_config_word(dev->pdev, GCFGC, &tmp);
+       pci_read_config_word(pdev, GCFGC, &tmp);
 
        cdclk_sel = (tmp >> 12) & 0x1;
 
@@ -7070,6 +7585,7 @@ static int gm45_get_display_clock_speed(struct drm_device *dev)
 
 static int i965gm_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        static const uint8_t div_3200[] = { 16, 10,  8 };
        static const uint8_t div_4000[] = { 20, 12, 10 };
        static const uint8_t div_5333[] = { 24, 16, 14 };
@@ -7077,7 +7593,7 @@ static int i965gm_get_display_clock_speed(struct drm_device *dev)
        unsigned int cdclk_sel, vco = intel_hpll_vco(dev);
        uint16_t tmp = 0;
 
-       pci_read_config_word(dev->pdev, GCFGC, &tmp);
+       pci_read_config_word(pdev, GCFGC, &tmp);
 
        cdclk_sel = ((tmp >> 8) & 0x1f) - 1;
 
@@ -7107,6 +7623,7 @@ fail:
 
 static int g33_get_display_clock_speed(struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
        static const uint8_t div_3200[] = { 12, 10,  8,  7, 5, 16 };
        static const uint8_t div_4000[] = { 14, 12, 10,  8, 6, 20 };
        static const uint8_t div_4800[] = { 20, 14, 12, 10, 8, 24 };
@@ -7115,7 +7632,7 @@ static int g33_get_display_clock_speed(struct drm_device *dev)
        unsigned int cdclk_sel, vco = intel_hpll_vco(dev);
        uint16_t tmp = 0;
 
-       pci_read_config_word(dev->pdev, GCFGC, &tmp);
+       pci_read_config_word(pdev, GCFGC, &tmp);
 
        cdclk_sel = (tmp >> 4) & 0x7;
 
@@ -8995,6 +9512,24 @@ static void ironlake_compute_dpll(struct intel_crtc *intel_crtc,
        if (intel_crtc_has_dp_encoder(crtc_state))
                dpll |= DPLL_SDVO_HIGH_SPEED;
 
+       /*
+        * The high speed IO clock is only really required for
+        * SDVO/HDMI/DP, but we also enable it for CRT to make it
+        * possible to share the DPLL between CRT and HDMI. Enabling
+        * the clock needlessly does no real harm, except use up a
+        * bit of power potentially.
+        *
+        * We'll limit this to IVB with 3 pipes, since it has only two
+        * DPLLs and so DPLL sharing is the only way to get three pipes
+        * driving PCH ports at the same time. On SNB we could do this,
+        * and potentially avoid enabling the second DPLL, but it's not
+        * clear if it''s a win or loss power wise. No point in doing
+        * this on ILK at all since it has a fixed DPLL<->pipe mapping.
+        */
+       if (INTEL_INFO(dev_priv)->num_pipes == 3 &&
+           intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG))
+               dpll |= DPLL_SDVO_HIGH_SPEED;
+
        /* compute bitmask from p1 value */
        dpll |= (1 << (crtc_state->dpll.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
        /* also FPA1 */
@@ -9281,7 +9816,7 @@ skylake_get_initial_plane_config(struct intel_crtc *crtc,
        return;
 
 error:
-       kfree(fb);
+       kfree(intel_fb);
 }
 
 static void ironlake_get_pfit_config(struct intel_crtc *crtc,
@@ -9487,7 +10022,7 @@ static void assert_can_disable_lcpll(struct drm_i915_private *dev_priv)
        I915_STATE_WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL enabled\n");
        I915_STATE_WARN(I915_READ(WRPLL_CTL(0)) & WRPLL_PLL_ENABLE, "WRPLL1 enabled\n");
        I915_STATE_WARN(I915_READ(WRPLL_CTL(1)) & WRPLL_PLL_ENABLE, "WRPLL2 enabled\n");
-       I915_STATE_WARN(I915_READ(PCH_PP_STATUS) & PP_ON, "Panel power on\n");
+       I915_STATE_WARN(I915_READ(PP_STATUS(0)) & PP_ON, "Panel power on\n");
        I915_STATE_WARN(I915_READ(BLC_PWM_CPU_CTL2) & BLM_PWM_ENABLE,
             "CPU PWM1 enabled\n");
        if (IS_HASWELL(dev))
@@ -9526,7 +10061,7 @@ static void hsw_write_dcomp(struct drm_i915_private *dev_priv, uint32_t val)
                mutex_lock(&dev_priv->rps.hw_lock);
                if (sandybridge_pcode_write(dev_priv, GEN6_PCODE_WRITE_D_COMP,
                                            val))
-                       DRM_ERROR("Failed to write to D_COMP\n");
+                       DRM_DEBUG_KMS("Failed to write to D_COMP\n");
                mutex_unlock(&dev_priv->rps.hw_lock);
        } else {
                I915_WRITE(D_COMP_BDW, val);
@@ -9934,15 +10469,12 @@ static void bxt_get_ddi_pll(struct drm_i915_private *dev_priv,
 
        switch (port) {
        case PORT_A:
-               pipe_config->ddi_pll_sel = SKL_DPLL0;
                id = DPLL_ID_SKL_DPLL0;
                break;
        case PORT_B:
-               pipe_config->ddi_pll_sel = SKL_DPLL1;
                id = DPLL_ID_SKL_DPLL1;
                break;
        case PORT_C:
-               pipe_config->ddi_pll_sel = SKL_DPLL2;
                id = DPLL_ID_SKL_DPLL2;
                break;
        default:
@@ -9961,25 +10493,10 @@ static void skylake_get_ddi_pll(struct drm_i915_private *dev_priv,
        u32 temp;
 
        temp = I915_READ(DPLL_CTRL2) & DPLL_CTRL2_DDI_CLK_SEL_MASK(port);
-       pipe_config->ddi_pll_sel = temp >> (port * 3 + 1);
+       id = temp >> (port * 3 + 1);
 
-       switch (pipe_config->ddi_pll_sel) {
-       case SKL_DPLL0:
-               id = DPLL_ID_SKL_DPLL0;
-               break;
-       case SKL_DPLL1:
-               id = DPLL_ID_SKL_DPLL1;
-               break;
-       case SKL_DPLL2:
-               id = DPLL_ID_SKL_DPLL2;
-               break;
-       case SKL_DPLL3:
-               id = DPLL_ID_SKL_DPLL3;
-               break;
-       default:
-               MISSING_CASE(pipe_config->ddi_pll_sel);
+       if (WARN_ON(id < SKL_DPLL0 || id > SKL_DPLL3))
                return;
-       }
 
        pipe_config->shared_dpll = intel_get_shared_dpll_by_id(dev_priv, id);
 }
@@ -9989,10 +10506,9 @@ static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv,
                                struct intel_crtc_state *pipe_config)
 {
        enum intel_dpll_id id;
+       uint32_t ddi_pll_sel = I915_READ(PORT_CLK_SEL(port));
 
-       pipe_config->ddi_pll_sel = I915_READ(PORT_CLK_SEL(port));
-
-       switch (pipe_config->ddi_pll_sel) {
+       switch (ddi_pll_sel) {
        case PORT_CLK_SEL_WRPLL1:
                id = DPLL_ID_WRPLL1;
                break;
@@ -10012,7 +10528,7 @@ static void haswell_get_ddi_pll(struct drm_i915_private *dev_priv,
                id = DPLL_ID_LCPLL_2700;
                break;
        default:
-               MISSING_CASE(pipe_config->ddi_pll_sel);
+               MISSING_CASE(ddi_pll_sel);
                /* fall through */
        case PORT_CLK_SEL_NONE:
                return;
@@ -10245,7 +10761,7 @@ static void i845_update_cursor(struct drm_crtc *crtc, u32 base,
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t cntl = 0, size = 0;
 
-       if (plane_state && plane_state->visible) {
+       if (plane_state && plane_state->base.visible) {
                unsigned int width = plane_state->base.crtc_w;
                unsigned int height = plane_state->base.crtc_h;
                unsigned int stride = roundup_pow_of_two(width) * 4;
@@ -10306,10 +10822,14 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       const struct skl_wm_values *wm = &dev_priv->wm.skl_results;
        int pipe = intel_crtc->pipe;
        uint32_t cntl = 0;
 
-       if (plane_state && plane_state->visible) {
+       if (INTEL_GEN(dev_priv) >= 9 && wm->dirty_pipes & drm_crtc_mask(crtc))
+               skl_write_cursor_wm(intel_crtc, wm);
+
+       if (plane_state && plane_state->base.visible) {
                cntl = MCURSOR_GAMMA_ENABLE;
                switch (plane_state->base.crtc_w) {
                        case 64:
@@ -10330,7 +10850,7 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base,
                if (HAS_DDI(dev))
                        cntl |= CURSOR_PIPE_CSC_ENABLE;
 
-               if (plane_state->base.rotation == BIT(DRM_ROTATE_180))
+               if (plane_state->base.rotation == DRM_ROTATE_180)
                        cntl |= CURSOR_ROTATE_180;
        }
 
@@ -10376,7 +10896,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
 
                /* ILK+ do this automagically */
                if (HAS_GMCH_DISPLAY(dev) &&
-                   plane_state->base.rotation == BIT(DRM_ROTATE_180)) {
+                   plane_state->base.rotation == DRM_ROTATE_180) {
                        base += (plane_state->base.crtc_h *
                                 plane_state->base.crtc_w - 1) * 4;
                }
@@ -10509,7 +11029,7 @@ intel_framebuffer_create_for_mode(struct drm_device *dev,
 
        fb = intel_framebuffer_create(dev, &mode_cmd, obj);
        if (IS_ERR(fb))
-               drm_gem_object_unreference_unlocked(&obj->base);
+               i915_gem_object_put_unlocked(obj);
 
        return fb;
 }
@@ -11020,13 +11540,13 @@ static void intel_unpin_work_fn(struct work_struct *__work)
 
        mutex_lock(&dev->struct_mutex);
        intel_unpin_fb_obj(work->old_fb, primary->state->rotation);
-       drm_gem_object_unreference(&work->pending_flip_obj->base);
-
-       if (work->flip_queued_req)
-               i915_gem_request_assign(&work->flip_queued_req, NULL);
+       i915_gem_object_put(work->pending_flip_obj);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_frontbuffer_flip_complete(dev, to_intel_plane(primary)->frontbuffer_bit);
+       i915_gem_request_put(work->flip_queued_req);
+
+       intel_frontbuffer_flip_complete(to_i915(dev),
+                                       to_intel_plane(primary)->frontbuffer_bit);
        intel_fbc_post_update(crtc);
        drm_framebuffer_unreference(work->old_fb);
 
@@ -11047,10 +11567,8 @@ static bool __pageflip_finished_cs(struct intel_crtc *crtc,
 {
        struct drm_device *dev = crtc->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       unsigned reset_counter;
 
-       reset_counter = i915_reset_counter(&dev_priv->gpu_error);
-       if (crtc->reset_counter != reset_counter)
+       if (abort_flip_on_reset(crtc))
                return true;
 
        /*
@@ -11191,7 +11709,7 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
                                 struct drm_i915_gem_request *req,
                                 uint32_t flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        u32 flip_mask;
        int ret;
@@ -11207,13 +11725,13 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
                flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
        else
                flip_mask = MI_WAIT_FOR_PLANE_A_FLIP;
-       intel_ring_emit(engine, MI_WAIT_FOR_EVENT | flip_mask);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_emit(engine, MI_DISPLAY_FLIP |
+       intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
-       intel_ring_emit(engine, fb->pitches[0]);
-       intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset);
-       intel_ring_emit(engine, 0); /* aux display base address, unused */
+       intel_ring_emit(ring, fb->pitches[0]);
+       intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset);
+       intel_ring_emit(ring, 0); /* aux display base address, unused */
 
        return 0;
 }
@@ -11225,7 +11743,7 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
                                 struct drm_i915_gem_request *req,
                                 uint32_t flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        u32 flip_mask;
        int ret;
@@ -11238,13 +11756,13 @@ static int intel_gen3_queue_flip(struct drm_device *dev,
                flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
        else
                flip_mask = MI_WAIT_FOR_PLANE_A_FLIP;
-       intel_ring_emit(engine, MI_WAIT_FOR_EVENT | flip_mask);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_emit(engine, MI_DISPLAY_FLIP_I915 |
+       intel_ring_emit(ring, MI_WAIT_FOR_EVENT | flip_mask);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
-       intel_ring_emit(engine, fb->pitches[0]);
-       intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset);
-       intel_ring_emit(engine, MI_NOOP);
+       intel_ring_emit(ring, fb->pitches[0]);
+       intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset);
+       intel_ring_emit(ring, MI_NOOP);
 
        return 0;
 }
@@ -11256,7 +11774,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
                                 struct drm_i915_gem_request *req,
                                 uint32_t flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t pf, pipesrc;
@@ -11270,11 +11788,11 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
         * Display Registers (which do not change across a page-flip)
         * so we need only reprogram the base address.
         */
-       intel_ring_emit(engine, MI_DISPLAY_FLIP |
+       intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
-       intel_ring_emit(engine, fb->pitches[0]);
-       intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset |
-                       obj->tiling_mode);
+       intel_ring_emit(ring, fb->pitches[0]);
+       intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset |
+                       intel_fb_modifier_to_tiling(fb->modifier[0]));
 
        /* XXX Enabling the panel-fitter across page-flip is so far
         * untested on non-native modes, so ignore it for now.
@@ -11282,7 +11800,7 @@ static int intel_gen4_queue_flip(struct drm_device *dev,
         */
        pf = 0;
        pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
-       intel_ring_emit(engine, pf | pipesrc);
+       intel_ring_emit(ring, pf | pipesrc);
 
        return 0;
 }
@@ -11294,7 +11812,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
                                 struct drm_i915_gem_request *req,
                                 uint32_t flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t pf, pipesrc;
@@ -11304,10 +11822,11 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_DISPLAY_FLIP |
+       intel_ring_emit(ring, MI_DISPLAY_FLIP |
                        MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
-       intel_ring_emit(engine, fb->pitches[0] | obj->tiling_mode);
-       intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset);
+       intel_ring_emit(ring, fb->pitches[0] |
+                       intel_fb_modifier_to_tiling(fb->modifier[0]));
+       intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset);
 
        /* Contrary to the suggestions in the documentation,
         * "Enable Panel Fitter" does not seem to be required when page
@@ -11317,7 +11836,7 @@ static int intel_gen6_queue_flip(struct drm_device *dev,
         */
        pf = 0;
        pipesrc = I915_READ(PIPESRC(intel_crtc->pipe)) & 0x0fff0fff;
-       intel_ring_emit(engine, pf | pipesrc);
+       intel_ring_emit(ring, pf | pipesrc);
 
        return 0;
 }
@@ -11329,7 +11848,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
                                 struct drm_i915_gem_request *req,
                                 uint32_t flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        uint32_t plane_bit = 0;
        int len, ret;
@@ -11350,7 +11869,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
        }
 
        len = 4;
-       if (engine->id == RCS) {
+       if (req->engine->id == RCS) {
                len += 6;
                /*
                 * On Gen 8, SRM is now taking an extra dword to accommodate
@@ -11388,30 +11907,32 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
         * for the RCS also doesn't appear to drop events. Setting the DERRMR
         * to zero does lead to lockups within MI_DISPLAY_FLIP.
         */
-       if (engine->id == RCS) {
-               intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(1));
-               intel_ring_emit_reg(engine, DERRMR);
-               intel_ring_emit(engine, ~(DERRMR_PIPEA_PRI_FLIP_DONE |
+       if (req->engine->id == RCS) {
+               intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+               intel_ring_emit_reg(ring, DERRMR);
+               intel_ring_emit(ring, ~(DERRMR_PIPEA_PRI_FLIP_DONE |
                                          DERRMR_PIPEB_PRI_FLIP_DONE |
                                          DERRMR_PIPEC_PRI_FLIP_DONE));
                if (IS_GEN8(dev))
-                       intel_ring_emit(engine, MI_STORE_REGISTER_MEM_GEN8 |
+                       intel_ring_emit(ring, MI_STORE_REGISTER_MEM_GEN8 |
                                              MI_SRM_LRM_GLOBAL_GTT);
                else
-                       intel_ring_emit(engine, MI_STORE_REGISTER_MEM |
+                       intel_ring_emit(ring, MI_STORE_REGISTER_MEM |
                                              MI_SRM_LRM_GLOBAL_GTT);
-               intel_ring_emit_reg(engine, DERRMR);
-               intel_ring_emit(engine, engine->scratch.gtt_offset + 256);
+               intel_ring_emit_reg(ring, DERRMR);
+               intel_ring_emit(ring,
+                               i915_ggtt_offset(req->engine->scratch) + 256);
                if (IS_GEN8(dev)) {
-                       intel_ring_emit(engine, 0);
-                       intel_ring_emit(engine, MI_NOOP);
+                       intel_ring_emit(ring, 0);
+                       intel_ring_emit(ring, MI_NOOP);
                }
        }
 
-       intel_ring_emit(engine, MI_DISPLAY_FLIP_I915 | plane_bit);
-       intel_ring_emit(engine, (fb->pitches[0] | obj->tiling_mode));
-       intel_ring_emit(engine, intel_crtc->flip_work->gtt_offset);
-       intel_ring_emit(engine, (MI_NOOP));
+       intel_ring_emit(ring, MI_DISPLAY_FLIP_I915 | plane_bit);
+       intel_ring_emit(ring, fb->pitches[0] |
+                       intel_fb_modifier_to_tiling(fb->modifier[0]));
+       intel_ring_emit(ring, intel_crtc->flip_work->gtt_offset);
+       intel_ring_emit(ring, (MI_NOOP));
 
        return 0;
 }
@@ -11446,7 +11967,8 @@ static bool use_mmio_flip(struct intel_engine_cs *engine,
        if (resv && !reservation_object_test_signaled_rcu(resv, false))
                return true;
 
-       return engine != i915_gem_request_get_engine(obj->last_write_req);
+       return engine != i915_gem_active_get_engine(&obj->last_write,
+                                                   &obj->base.dev->struct_mutex);
 }
 
 static void skl_do_mmio_flip(struct intel_crtc *intel_crtc,
@@ -11457,7 +11979,7 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_framebuffer *fb = intel_crtc->base.primary->fb;
        const enum pipe pipe = intel_crtc->pipe;
-       u32 ctl, stride, tile_height;
+       u32 ctl, stride = skl_plane_stride(fb, 0, rotation);
 
        ctl = I915_READ(PLANE_CTL(pipe, 0));
        ctl &= ~PLANE_CTL_TILED_MASK;
@@ -11477,20 +11999,6 @@ static void skl_do_mmio_flip(struct intel_crtc *intel_crtc,
                MISSING_CASE(fb->modifier[0]);
        }
 
-       /*
-        * The stride is either expressed as a multiple of 64 bytes chunks for
-        * linear buffers or in number of tiles for tiled buffers.
-        */
-       if (intel_rotation_90_or_270(rotation)) {
-               /* stride = Surface height in tiles */
-               tile_height = intel_tile_height(dev_priv, fb->modifier[0], 0);
-               stride = DIV_ROUND_UP(fb->height, tile_height);
-       } else {
-               stride = fb->pitches[0] /
-                       intel_fb_stride_alignment(dev_priv, fb->modifier[0],
-                                                 fb->pixel_format);
-       }
-
        /*
         * Both PLANE_CTL and PLANE_STRIDE are not updated on vblank but on
         * PLANE_SURF updates, the update is then guaranteed to be atomic.
@@ -11507,15 +12015,13 @@ static void ilk_do_mmio_flip(struct intel_crtc *intel_crtc,
 {
        struct drm_device *dev = intel_crtc->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_framebuffer *intel_fb =
-               to_intel_framebuffer(intel_crtc->base.primary->fb);
-       struct drm_i915_gem_object *obj = intel_fb->obj;
+       struct drm_framebuffer *fb = intel_crtc->base.primary->fb;
        i915_reg_t reg = DSPCNTR(intel_crtc->plane);
        u32 dspcntr;
 
        dspcntr = I915_READ(reg);
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                dspcntr |= DISPPLANE_TILED;
        else
                dspcntr &= ~DISPPLANE_TILED;
@@ -11538,9 +12044,8 @@ static void intel_mmio_flip_work_func(struct work_struct *w)
        struct reservation_object *resv;
 
        if (work->flip_queued_req)
-               WARN_ON(__i915_wait_request(work->flip_queued_req,
-                                           false, NULL,
-                                           &dev_priv->rps.mmioflips));
+               WARN_ON(i915_wait_request(work->flip_queued_req,
+                                         0, NULL, NO_WAITBOOST));
 
        /* For framebuffer backed by dmabuf, wait for fence */
        resv = i915_gem_object_get_dmabuf_resv(obj);
@@ -11651,7 +12156,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        struct intel_flip_work *work;
        struct intel_engine_cs *engine;
        bool mmio_flip;
-       struct drm_i915_gem_request *request = NULL;
+       struct drm_i915_gem_request *request;
+       struct i915_vma *vma;
        int ret;
 
        /*
@@ -11717,22 +12223,18 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        /* Reference the objects for the scheduled work. */
        drm_framebuffer_reference(work->old_fb);
-       drm_gem_object_reference(&obj->base);
 
        crtc->primary->fb = fb;
        update_state_fb(crtc->primary);
 
-       intel_fbc_pre_update(intel_crtc, intel_crtc->config,
-                            to_intel_plane_state(primary->state));
-
-       work->pending_flip_obj = obj;
+       work->pending_flip_obj = i915_gem_object_get(obj);
 
        ret = i915_mutex_lock_interruptible(dev);
        if (ret)
                goto cleanup;
 
-       intel_crtc->reset_counter = i915_reset_counter(&dev_priv->gpu_error);
-       if (__i915_reset_in_progress_or_wedged(intel_crtc->reset_counter)) {
+       intel_crtc->reset_count = i915_reset_count(&dev_priv->gpu_error);
+       if (i915_reset_in_progress_or_wedged(&dev_priv->gpu_error)) {
                ret = -EIO;
                goto cleanup;
        }
@@ -11744,13 +12246,14 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
                engine = &dev_priv->engine[BCS];
-               if (obj->tiling_mode != intel_fb_obj(work->old_fb)->tiling_mode)
+               if (fb->modifier[0] != old_fb->modifier[0])
                        /* vlv: DISPLAY_FLIP fails to change tiling */
                        engine = NULL;
        } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) {
                engine = &dev_priv->engine[BCS];
        } else if (INTEL_INFO(dev)->gen >= 7) {
-               engine = i915_gem_request_get_engine(obj->last_write_req);
+               engine = i915_gem_active_get_engine(&obj->last_write,
+                                                   &obj->base.dev->struct_mutex);
                if (engine == NULL || engine->id != RCS)
                        engine = &dev_priv->engine[BCS];
        } else {
@@ -11759,47 +12262,52 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 
        mmio_flip = use_mmio_flip(engine, obj);
 
-       /* When using CS flips, we want to emit semaphores between rings.
-        * However, when using mmio flips we will create a task to do the
-        * synchronisation, so all we want here is to pin the framebuffer
-        * into the display plane and skip any waits.
-        */
-       if (!mmio_flip) {
-               ret = i915_gem_object_sync(obj, engine, &request);
-               if (!ret && !request) {
-                       request = i915_gem_request_alloc(engine, NULL);
-                       ret = PTR_ERR_OR_ZERO(request);
-               }
-
-               if (ret)
-                       goto cleanup_pending;
-       }
-
-       ret = intel_pin_and_fence_fb_obj(fb, primary->state->rotation);
-       if (ret)
+       vma = intel_pin_and_fence_fb_obj(fb, primary->state->rotation);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
                goto cleanup_pending;
+       }
 
-       work->gtt_offset = intel_plane_obj_offset(to_intel_plane(primary),
-                                                 obj, 0);
+       work->gtt_offset = intel_fb_gtt_offset(fb, primary->state->rotation);
        work->gtt_offset += intel_crtc->dspaddr_offset;
        work->rotation = crtc->primary->state->rotation;
 
+       /*
+        * There's the potential that the next frame will not be compatible with
+        * FBC, so we want to call pre_update() before the actual page flip.
+        * The problem is that pre_update() caches some information about the fb
+        * object, so we want to do this only after the object is pinned. Let's
+        * be on the safe side and do this immediately before scheduling the
+        * flip.
+        */
+       intel_fbc_pre_update(intel_crtc, intel_crtc->config,
+                            to_intel_plane_state(primary->state));
+
        if (mmio_flip) {
                INIT_WORK(&work->mmio_work, intel_mmio_flip_work_func);
 
-               i915_gem_request_assign(&work->flip_queued_req,
-                                       obj->last_write_req);
-
+               work->flip_queued_req = i915_gem_active_get(&obj->last_write,
+                                                           &obj->base.dev->struct_mutex);
                schedule_work(&work->mmio_work);
        } else {
-               i915_gem_request_assign(&work->flip_queued_req, request);
+               request = i915_gem_request_alloc(engine, engine->last_context);
+               if (IS_ERR(request)) {
+                       ret = PTR_ERR(request);
+                       goto cleanup_unpin;
+               }
+
+               ret = i915_gem_request_await_object(request, obj, false);
+               if (ret)
+                       goto cleanup_request;
+
                ret = dev_priv->display.queue_flip(dev, crtc, fb, obj, request,
                                                   page_flip_flags);
                if (ret)
-                       goto cleanup_unpin;
+                       goto cleanup_request;
 
                intel_mark_page_flip_active(intel_crtc, work);
 
+               work->flip_queued_req = i915_gem_request_get(request);
                i915_add_request_no_flush(request);
        }
 
@@ -11807,25 +12315,25 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                          to_intel_plane(primary)->frontbuffer_bit);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_frontbuffer_flip_prepare(dev,
+       intel_frontbuffer_flip_prepare(to_i915(dev),
                                       to_intel_plane(primary)->frontbuffer_bit);
 
        trace_i915_flip_request(intel_crtc->plane, obj);
 
        return 0;
 
+cleanup_request:
+       i915_add_request_no_flush(request);
 cleanup_unpin:
        intel_unpin_fb_obj(fb, crtc->primary->state->rotation);
 cleanup_pending:
-       if (!IS_ERR_OR_NULL(request))
-               i915_add_request_no_flush(request);
        atomic_dec(&intel_crtc->unpin_work_count);
        mutex_unlock(&dev->struct_mutex);
 cleanup:
        crtc->primary->fb = old_fb;
        update_state_fb(crtc->primary);
 
-       drm_gem_object_unreference_unlocked(&obj->base);
+       i915_gem_object_put_unlocked(obj);
        drm_framebuffer_unreference(work->old_fb);
 
        spin_lock_irq(&dev->event_lock);
@@ -11893,7 +12401,7 @@ static bool intel_wm_need_update(struct drm_plane *plane,
        struct intel_plane_state *cur = to_intel_plane_state(plane->state);
 
        /* Update watermarks on tiling or size changes. */
-       if (new->visible != cur->visible)
+       if (new->base.visible != cur->base.visible)
                return true;
 
        if (!cur->base.fb || !new->base.fb)
@@ -11901,10 +12409,10 @@ static bool intel_wm_need_update(struct drm_plane *plane,
 
        if (cur->base.fb->modifier[0] != new->base.fb->modifier[0] ||
            cur->base.rotation != new->base.rotation ||
-           drm_rect_width(&new->src) != drm_rect_width(&cur->src) ||
-           drm_rect_height(&new->src) != drm_rect_height(&cur->src) ||
-           drm_rect_width(&new->dst) != drm_rect_width(&cur->dst) ||
-           drm_rect_height(&new->dst) != drm_rect_height(&cur->dst))
+           drm_rect_width(&new->base.src) != drm_rect_width(&cur->base.src) ||
+           drm_rect_height(&new->base.src) != drm_rect_height(&cur->base.src) ||
+           drm_rect_width(&new->base.dst) != drm_rect_width(&cur->base.dst) ||
+           drm_rect_height(&new->base.dst) != drm_rect_height(&cur->base.dst))
                return true;
 
        return false;
@@ -11912,10 +12420,10 @@ static bool intel_wm_need_update(struct drm_plane *plane,
 
 static bool needs_scaling(struct intel_plane_state *state)
 {
-       int src_w = drm_rect_width(&state->src) >> 16;
-       int src_h = drm_rect_height(&state->src) >> 16;
-       int dst_w = drm_rect_width(&state->dst);
-       int dst_h = drm_rect_height(&state->dst);
+       int src_w = drm_rect_width(&state->base.src) >> 16;
+       int src_h = drm_rect_height(&state->base.src) >> 16;
+       int dst_w = drm_rect_width(&state->base.dst);
+       int dst_h = drm_rect_height(&state->base.dst);
 
        return (src_w != dst_w || src_h != dst_h);
 }
@@ -11946,8 +12454,8 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
                        return ret;
        }
 
-       was_visible = old_plane_state->visible;
-       visible = to_intel_plane_state(plane_state)->visible;
+       was_visible = old_plane_state->base.visible;
+       visible = to_intel_plane_state(plane_state)->base.visible;
 
        if (!was_crtc_enabled && WARN_ON(was_visible))
                was_visible = false;
@@ -11963,7 +12471,7 @@ int intel_plane_atomic_calc_changes(struct drm_crtc_state *crtc_state,
         * only combine the results from all planes in the current place?
         */
        if (!is_crtc_enabled)
-               to_intel_plane_state(plane_state)->visible = visible = false;
+               to_intel_plane_state(plane_state)->base.visible = visible = false;
 
        if (!was_visible && !visible)
                return 0;
@@ -12167,22 +12675,22 @@ static void
 connected_sink_compute_bpp(struct intel_connector *connector,
                           struct intel_crtc_state *pipe_config)
 {
+       const struct drm_display_info *info = &connector->base.display_info;
        int bpp = pipe_config->pipe_bpp;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s] checking for sink bpp constrains\n",
-               connector->base.base.id,
-               connector->base.name);
+                     connector->base.base.id,
+                     connector->base.name);
 
        /* Don't use an invalid EDID bpc value */
-       if (connector->base.display_info.bpc &&
-           connector->base.display_info.bpc * 3 < bpp) {
+       if (info->bpc != 0 && info->bpc * 3 < bpp) {
                DRM_DEBUG_KMS("clamping display bpp (was %d) to EDID reported max of %d\n",
-                             bpp, connector->base.display_info.bpc*3);
-               pipe_config->pipe_bpp = connector->base.display_info.bpc*3;
+                             bpp, info->bpc * 3);
+               pipe_config->pipe_bpp = info->bpc * 3;
        }
 
        /* Clamp bpp to 8 on screens without EDID 1.4 */
-       if (connector->base.display_info.bpc == 0 && bpp > 24) {
+       if (info->bpc == 0 && bpp > 24) {
                DRM_DEBUG_KMS("clamping display bpp (was %d) to default limit of 24\n",
                              bpp);
                pipe_config->pipe_bpp = 24;
@@ -12301,10 +12809,9 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
        DRM_DEBUG_KMS("double wide: %i\n", pipe_config->double_wide);
 
        if (IS_BROXTON(dev)) {
-               DRM_DEBUG_KMS("ddi_pll_sel: %u; dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x,"
+               DRM_DEBUG_KMS("dpll_hw_state: ebb0: 0x%x, ebb4: 0x%x,"
                              "pll0: 0x%x, pll1: 0x%x, pll2: 0x%x, pll3: 0x%x, "
                              "pll6: 0x%x, pll8: 0x%x, pll9: 0x%x, pll10: 0x%x, pcsdw12: 0x%x\n",
-                             pipe_config->ddi_pll_sel,
                              pipe_config->dpll_hw_state.ebb0,
                              pipe_config->dpll_hw_state.ebb4,
                              pipe_config->dpll_hw_state.pll0,
@@ -12317,15 +12824,13 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                              pipe_config->dpll_hw_state.pll10,
                              pipe_config->dpll_hw_state.pcsdw12);
        } else if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
-               DRM_DEBUG_KMS("ddi_pll_sel: %u; dpll_hw_state: "
+               DRM_DEBUG_KMS("dpll_hw_state: "
                              "ctrl1: 0x%x, cfgcr1: 0x%x, cfgcr2: 0x%x\n",
-                             pipe_config->ddi_pll_sel,
                              pipe_config->dpll_hw_state.ctrl1,
                              pipe_config->dpll_hw_state.cfgcr1,
                              pipe_config->dpll_hw_state.cfgcr2);
        } else if (HAS_DDI(dev)) {
-               DRM_DEBUG_KMS("ddi_pll_sel: 0x%x; dpll_hw_state: wrpll: 0x%x spll: 0x%x\n",
-                             pipe_config->ddi_pll_sel,
+               DRM_DEBUG_KMS("dpll_hw_state: wrpll: 0x%x spll: 0x%x\n",
                              pipe_config->dpll_hw_state.wrpll,
                              pipe_config->dpll_hw_state.spll);
        } else {
@@ -12339,6 +12844,7 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
 
        DRM_DEBUG_KMS("planes on this crtc\n");
        list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+               char *format_name;
                intel_plane = to_intel_plane(plane);
                if (intel_plane->pipe != crtc->pipe)
                        continue;
@@ -12351,19 +12857,23 @@ static void intel_dump_pipe_config(struct intel_crtc *crtc,
                        continue;
                }
 
+               format_name = drm_get_format_name(fb->pixel_format);
+
                DRM_DEBUG_KMS("[PLANE:%d:%s] enabled",
                              plane->base.id, plane->name);
                DRM_DEBUG_KMS("\tFB:%d, fb = %ux%u format = %s",
-                             fb->base.id, fb->width, fb->height,
-                             drm_get_format_name(fb->pixel_format));
+                             fb->base.id, fb->width, fb->height, format_name);
                DRM_DEBUG_KMS("\tscaler:%d src %dx%d+%d+%d dst %dx%d+%d+%d\n",
                              state->scaler_id,
-                             state->src.x1 >> 16, state->src.y1 >> 16,
-                             drm_rect_width(&state->src) >> 16,
-                             drm_rect_height(&state->src) >> 16,
-                             state->dst.x1, state->dst.y1,
-                             drm_rect_width(&state->dst),
-                             drm_rect_height(&state->dst));
+                             state->base.src.x1 >> 16,
+                             state->base.src.y1 >> 16,
+                             drm_rect_width(&state->base.src) >> 16,
+                             drm_rect_height(&state->base.src) >> 16,
+                             state->base.dst.x1, state->base.dst.y1,
+                             drm_rect_width(&state->base.dst),
+                             drm_rect_height(&state->base.dst));
+
+               kfree(format_name);
        }
 }
 
@@ -12372,6 +12882,7 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state)
        struct drm_device *dev = state->dev;
        struct drm_connector *connector;
        unsigned int used_ports = 0;
+       unsigned int used_mst_ports = 0;
 
        /*
         * Walk the connector list instead of the encoder
@@ -12408,11 +12919,20 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state)
                                return false;
 
                        used_ports |= port_mask;
+                       break;
+               case INTEL_OUTPUT_DP_MST:
+                       used_mst_ports |=
+                               1 << enc_to_mst(&encoder->base)->primary->port;
+                       break;
                default:
                        break;
                }
        }
 
+       /* can't mix MST and SST/HDMI on the same port */
+       if (used_ports & used_mst_ports)
+               return false;
+
        return true;
 }
 
@@ -12423,7 +12943,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
        struct intel_crtc_scaler_state scaler_state;
        struct intel_dpll_hw_state dpll_hw_state;
        struct intel_shared_dpll *shared_dpll;
-       uint32_t ddi_pll_sel;
        bool force_thru;
 
        /* FIXME: before the switch to atomic started, a new pipe_config was
@@ -12435,7 +12954,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
        scaler_state = crtc_state->scaler_state;
        shared_dpll = crtc_state->shared_dpll;
        dpll_hw_state = crtc_state->dpll_hw_state;
-       ddi_pll_sel = crtc_state->ddi_pll_sel;
        force_thru = crtc_state->pch_pfit.force_thru;
 
        memset(crtc_state, 0, sizeof *crtc_state);
@@ -12444,7 +12962,6 @@ clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
        crtc_state->scaler_state = scaler_state;
        crtc_state->shared_dpll = shared_dpll;
        crtc_state->dpll_hw_state = dpll_hw_state;
-       crtc_state->ddi_pll_sel = ddi_pll_sel;
        crtc_state->pch_pfit.force_thru = force_thru;
 }
 
@@ -12532,7 +13049,7 @@ encoder_retry:
 
                encoder = to_intel_encoder(connector_state->best_encoder);
 
-               if (!(encoder->compute_config(encoder, pipe_config))) {
+               if (!(encoder->compute_config(encoder, pipe_config, connector_state))) {
                        DRM_DEBUG_KMS("Encoder config failure\n");
                        goto fail;
                }
@@ -12620,12 +13137,6 @@ static bool intel_fuzzy_clock_check(int clock1, int clock2)
        return false;
 }
 
-#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \
-       list_for_each_entry((intel_crtc), \
-                           &(dev)->mode_config.crtc_list, \
-                           base.head) \
-               for_each_if (mask & (1 <<(intel_crtc)->pipe))
-
 static bool
 intel_compare_m_n(unsigned int m, unsigned int n,
                  unsigned int m2, unsigned int n2,
@@ -12873,8 +13384,6 @@ intel_pipe_config_compare(struct drm_device *dev,
 
        PIPE_CONF_CHECK_I(double_wide);
 
-       PIPE_CONF_CHECK_X(ddi_pll_sel);
-
        PIPE_CONF_CHECK_P(shared_dpll);
        PIPE_CONF_CHECK_X(dpll_hw_state.dpll);
        PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md);
@@ -12956,16 +13465,23 @@ static void verify_wm_state(struct drm_crtc *crtc,
                          hw_entry->start, hw_entry->end);
        }
 
-       /* cursor */
-       hw_entry = &hw_ddb.plane[pipe][PLANE_CURSOR];
-       sw_entry = &sw_ddb->plane[pipe][PLANE_CURSOR];
-
-       if (!skl_ddb_entry_equal(hw_entry, sw_entry)) {
-               DRM_ERROR("mismatch in DDB state pipe %c cursor "
-                         "(expected (%u,%u), found (%u,%u))\n",
-                         pipe_name(pipe),
-                         sw_entry->start, sw_entry->end,
-                         hw_entry->start, hw_entry->end);
+       /*
+        * cursor
+        * If the cursor plane isn't active, we may not have updated it's ddb
+        * allocation. In that case since the ddb allocation will be updated
+        * once the plane becomes visible, we can skip this check
+        */
+       if (intel_crtc->cursor_addr) {
+               hw_entry = &hw_ddb.plane[pipe][PLANE_CURSOR];
+               sw_entry = &sw_ddb->plane[pipe][PLANE_CURSOR];
+
+               if (!skl_ddb_entry_equal(hw_entry, sw_entry)) {
+                       DRM_ERROR("mismatch in DDB state pipe %c cursor "
+                                 "(expected (%u,%u), found (%u,%u))\n",
+                                 pipe_name(pipe),
+                                 sw_entry->start, sw_entry->end,
+                                 hw_entry->start, hw_entry->end);
+               }
        }
 }
 
@@ -13580,8 +14096,9 @@ static int intel_atomic_prepare_commit(struct drm_device *dev,
                        if (!intel_plane_state->wait_req)
                                continue;
 
-                       ret = __i915_wait_request(intel_plane_state->wait_req,
-                                                 true, NULL, NULL);
+                       ret = i915_wait_request(intel_plane_state->wait_req,
+                                               I915_WAIT_INTERRUPTIBLE,
+                                               NULL, NULL);
                        if (ret) {
                                /* Any hang should be swallowed by the wait */
                                WARN_ON(ret == -EIO);
@@ -13671,6 +14188,111 @@ static bool needs_vblank_wait(struct intel_crtc_state *crtc_state)
        return false;
 }
 
+static void intel_update_crtc(struct drm_crtc *crtc,
+                             struct drm_atomic_state *state,
+                             struct drm_crtc_state *old_crtc_state,
+                             unsigned int *crtc_vblank_mask)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_crtc_state *pipe_config = to_intel_crtc_state(crtc->state);
+       bool modeset = needs_modeset(crtc->state);
+
+       if (modeset) {
+               update_scanline_offset(intel_crtc);
+               dev_priv->display.crtc_enable(pipe_config, state);
+       } else {
+               intel_pre_plane_update(to_intel_crtc_state(old_crtc_state));
+       }
+
+       if (drm_atomic_get_existing_plane_state(state, crtc->primary)) {
+               intel_fbc_enable(
+                   intel_crtc, pipe_config,
+                   to_intel_plane_state(crtc->primary->state));
+       }
+
+       drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
+
+       if (needs_vblank_wait(pipe_config))
+               *crtc_vblank_mask |= drm_crtc_mask(crtc);
+}
+
+static void intel_update_crtcs(struct drm_atomic_state *state,
+                              unsigned int *crtc_vblank_mask)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       int i;
+
+       for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
+               if (!crtc->state->active)
+                       continue;
+
+               intel_update_crtc(crtc, state, old_crtc_state,
+                                 crtc_vblank_mask);
+       }
+}
+
+static void skl_update_crtcs(struct drm_atomic_state *state,
+                            unsigned int *crtc_vblank_mask)
+{
+       struct drm_device *dev = state->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_crtc_state;
+       struct skl_ddb_allocation *new_ddb = &intel_state->wm_results.ddb;
+       struct skl_ddb_allocation *cur_ddb = &dev_priv->wm.skl_hw.ddb;
+       unsigned int updated = 0;
+       bool progress;
+       enum pipe pipe;
+
+       /*
+        * Whenever the number of active pipes changes, we need to make sure we
+        * update the pipes in the right order so that their ddb allocations
+        * never overlap with eachother inbetween CRTC updates. Otherwise we'll
+        * cause pipe underruns and other bad stuff.
+        */
+       do {
+               int i;
+               progress = false;
+
+               for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
+                       bool vbl_wait = false;
+                       unsigned int cmask = drm_crtc_mask(crtc);
+                       pipe = to_intel_crtc(crtc)->pipe;
+
+                       if (updated & cmask || !crtc->state->active)
+                               continue;
+                       if (skl_ddb_allocation_overlaps(state, cur_ddb, new_ddb,
+                                                       pipe))
+                               continue;
+
+                       updated |= cmask;
+
+                       /*
+                        * If this is an already active pipe, it's DDB changed,
+                        * and this isn't the last pipe that needs updating
+                        * then we need to wait for a vblank to pass for the
+                        * new ddb allocation to take effect.
+                        */
+                       if (!skl_ddb_allocation_equals(cur_ddb, new_ddb, pipe) &&
+                           !crtc->state->active_changed &&
+                           intel_state->wm_results.dirty_pipes != updated)
+                               vbl_wait = true;
+
+                       intel_update_crtc(crtc, state, old_crtc_state,
+                                         crtc_vblank_mask);
+
+                       if (vbl_wait)
+                               intel_wait_for_vblank(dev, pipe);
+
+                       progress = true;
+               }
+       } while (progress);
+}
+
 static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
@@ -13693,8 +14315,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
                if (!intel_plane_state->wait_req)
                        continue;
 
-               ret = __i915_wait_request(intel_plane_state->wait_req,
-                                         true, NULL, NULL);
+               ret = i915_wait_request(intel_plane_state->wait_req,
+                                       0, NULL, NULL);
                /* EIO should be eaten, and we can't get interrupted in the
                 * worker, and blocking commits have waited already. */
                WARN_ON(ret);
@@ -13730,7 +14352,7 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 
                if (old_crtc_state->active) {
                        intel_crtc_disable_planes(crtc, old_crtc_state->plane_mask);
-                       dev_priv->display.crtc_disable(crtc);
+                       dev_priv->display.crtc_disable(to_intel_crtc_state(old_crtc_state), state);
                        intel_crtc->active = false;
                        intel_fbc_disable(intel_crtc);
                        intel_disable_shared_dpll(intel_crtc);
@@ -13763,23 +14385,15 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
                 * SKL workaround: bspec recommends we disable the SAGV when we
                 * have more then one pipe enabled
                 */
-               if (IS_SKYLAKE(dev_priv) && !skl_can_enable_sagv(state))
-                       skl_disable_sagv(dev_priv);
+               if (!intel_can_enable_sagv(state))
+                       intel_disable_sagv(dev_priv);
 
                intel_modeset_verify_disabled(dev);
        }
 
-       /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+       /* Complete the events for pipes that have now been disabled */
        for_each_crtc_in_state(state, crtc, old_crtc_state, i) {
-               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
                bool modeset = needs_modeset(crtc->state);
-               struct intel_crtc_state *pipe_config =
-                       to_intel_crtc_state(crtc->state);
-
-               if (modeset && crtc->state->active) {
-                       update_scanline_offset(to_intel_crtc(crtc));
-                       dev_priv->display.crtc_enable(crtc);
-               }
 
                /* Complete events for now disable pipes here. */
                if (modeset && !crtc->state->active && crtc->state->event) {
@@ -13789,21 +14403,11 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
 
                        crtc->state->event = NULL;
                }
-
-               if (!modeset)
-                       intel_pre_plane_update(to_intel_crtc_state(old_crtc_state));
-
-               if (crtc->state->active &&
-                   drm_atomic_get_existing_plane_state(state, crtc->primary))
-                       intel_fbc_enable(intel_crtc, pipe_config, to_intel_plane_state(crtc->primary->state));
-
-               if (crtc->state->active)
-                       drm_atomic_helper_commit_planes_on_crtc(old_crtc_state);
-
-               if (pipe_config->base.active && needs_vblank_wait(pipe_config))
-                       crtc_vblank_mask |= 1 << i;
        }
 
+       /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+       dev_priv->display.update_crtcs(state, &crtc_vblank_mask);
+
        /* FIXME: We should call drm_atomic_helper_commit_hw_done() here
         * already, but still need the state for the delayed optimization. To
         * fix this:
@@ -13839,9 +14443,8 @@ static void intel_atomic_commit_tail(struct drm_atomic_state *state)
                intel_modeset_verify_crtc(crtc, old_crtc_state, crtc->state);
        }
 
-       if (IS_SKYLAKE(dev_priv) && intel_state->modeset &&
-           skl_can_enable_sagv(state))
-               skl_enable_sagv(dev_priv);
+       if (intel_state->modeset && intel_can_enable_sagv(state))
+               intel_enable_sagv(dev_priv);
 
        drm_atomic_helper_commit_hw_done(state);
 
@@ -13882,19 +14485,12 @@ static void intel_atomic_track_fbs(struct drm_atomic_state *state)
 {
        struct drm_plane_state *old_plane_state;
        struct drm_plane *plane;
-       struct drm_i915_gem_object *obj, *old_obj;
-       struct intel_plane *intel_plane;
        int i;
 
-       mutex_lock(&state->dev->struct_mutex);
-       for_each_plane_in_state(state, plane, old_plane_state, i) {
-               obj = intel_fb_obj(plane->state->fb);
-               old_obj = intel_fb_obj(old_plane_state->fb);
-               intel_plane = to_intel_plane(plane);
-
-               i915_gem_track_fb(old_obj, obj, intel_plane->frontbuffer_bit);
-       }
-       mutex_unlock(&state->dev->struct_mutex);
+       for_each_plane_in_state(state, plane, old_plane_state, i)
+               i915_gem_track_fb(intel_fb_obj(old_plane_state->fb),
+                                 intel_fb_obj(plane->state->fb),
+                                 to_intel_plane(plane)->frontbuffer_bit);
 }
 
 /**
@@ -13990,8 +14586,6 @@ out:
                drm_atomic_state_free(state);
 }
 
-#undef for_each_intel_crtc_masked
-
 /*
  * FIXME: Remove this once i915 is fully DRIVER_ATOMIC by calling
  *        drm_atomic_helper_legacy_gamma_set() directly.
@@ -14060,7 +14654,7 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
  */
 int
 intel_prepare_plane_fb(struct drm_plane *plane,
-                      const struct drm_plane_state *new_state)
+                      struct drm_plane_state *new_state)
 {
        struct drm_device *dev = plane->dev;
        struct drm_framebuffer *fb = new_state->fb;
@@ -14119,15 +14713,17 @@ intel_prepare_plane_fb(struct drm_plane *plane,
                if (ret)
                        DRM_DEBUG_KMS("failed to attach phys object\n");
        } else {
-               ret = intel_pin_and_fence_fb_obj(fb, new_state->rotation);
+               struct i915_vma *vma;
+
+               vma = intel_pin_and_fence_fb_obj(fb, new_state->rotation);
+               if (IS_ERR(vma))
+                       ret = PTR_ERR(vma);
        }
 
        if (ret == 0) {
-               struct intel_plane_state *plane_state =
-                       to_intel_plane_state(new_state);
-
-               i915_gem_request_assign(&plane_state->wait_req,
-                                       obj->last_write_req);
+               to_intel_plane_state(new_state)->wait_req =
+                       i915_gem_active_get(&obj->last_write,
+                                           &obj->base.dev->struct_mutex);
        }
 
        return ret;
@@ -14144,10 +14740,11 @@ intel_prepare_plane_fb(struct drm_plane *plane,
  */
 void
 intel_cleanup_plane_fb(struct drm_plane *plane,
-                      const struct drm_plane_state *old_state)
+                      struct drm_plane_state *old_state)
 {
        struct drm_device *dev = plane->dev;
        struct intel_plane_state *old_intel_state;
+       struct intel_plane_state *intel_state = to_intel_plane_state(plane->state);
        struct drm_i915_gem_object *old_obj = intel_fb_obj(old_state->fb);
        struct drm_i915_gem_object *obj = intel_fb_obj(plane->state->fb);
 
@@ -14160,6 +14757,7 @@ intel_cleanup_plane_fb(struct drm_plane *plane,
            !INTEL_INFO(dev)->cursor_needs_physical))
                intel_unpin_fb_obj(old_state->fb, old_state->rotation);
 
+       i915_gem_request_assign(&intel_state->wait_req, NULL);
        i915_gem_request_assign(&old_intel_state->wait_req, NULL);
 }
 
@@ -14194,13 +14792,14 @@ intel_check_primary_plane(struct drm_plane *plane,
                          struct intel_crtc_state *crtc_state,
                          struct intel_plane_state *state)
 {
+       struct drm_i915_private *dev_priv = to_i915(plane->dev);
        struct drm_crtc *crtc = state->base.crtc;
-       struct drm_framebuffer *fb = state->base.fb;
        int min_scale = DRM_PLANE_HELPER_NO_SCALING;
        int max_scale = DRM_PLANE_HELPER_NO_SCALING;
        bool can_position = false;
+       int ret;
 
-       if (INTEL_INFO(plane->dev)->gen >= 9) {
+       if (INTEL_GEN(dev_priv) >= 9) {
                /* use scaler when colorkey is not required */
                if (state->ckey.flags == I915_SET_COLORKEY_NONE) {
                        min_scale = 1;
@@ -14209,22 +14808,35 @@ intel_check_primary_plane(struct drm_plane *plane,
                can_position = true;
        }
 
-       return drm_plane_helper_check_update(plane, crtc, fb, &state->src,
-                                            &state->dst, &state->clip,
-                                            state->base.rotation,
-                                            min_scale, max_scale,
-                                            can_position, true,
-                                            &state->visible);
+       ret = drm_plane_helper_check_state(&state->base,
+                                          &state->clip,
+                                          min_scale, max_scale,
+                                          can_position, true);
+       if (ret)
+               return ret;
+
+       if (!state->base.fb)
+               return 0;
+
+       if (INTEL_GEN(dev_priv) >= 9) {
+               ret = skl_check_plane_surface(state);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 
 static void intel_begin_crtc_commit(struct drm_crtc *crtc,
                                    struct drm_crtc_state *old_crtc_state)
 {
        struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_crtc_state *old_intel_state =
                to_intel_crtc_state(old_crtc_state);
        bool modeset = needs_modeset(crtc->state);
+       enum pipe pipe = intel_crtc->pipe;
 
        /* Perform vblank evasion around commit operation */
        intel_pipe_update_start(intel_crtc);
@@ -14239,8 +14851,12 @@ static void intel_begin_crtc_commit(struct drm_crtc *crtc,
 
        if (to_intel_crtc_state(crtc->state)->update_pipe)
                intel_update_pipe_config(intel_crtc, old_intel_state);
-       else if (INTEL_INFO(dev)->gen >= 9)
+       else if (INTEL_GEN(dev_priv) >= 9) {
                skl_detach_scalers(intel_crtc);
+
+               I915_WRITE(PIPE_WM_LINETIME(pipe),
+                          dev_priv->wm.skl_hw.wm_linetime[pipe]);
+       }
 }
 
 static void intel_finish_crtc_commit(struct drm_crtc *crtc,
@@ -14374,11 +14990,11 @@ fail:
 void intel_create_rotation_property(struct drm_device *dev, struct intel_plane *plane)
 {
        if (!dev->mode_config.rotation_property) {
-               unsigned long flags = BIT(DRM_ROTATE_0) |
-                       BIT(DRM_ROTATE_180);
+               unsigned long flags = DRM_ROTATE_0 |
+                       DRM_ROTATE_180;
 
                if (INTEL_INFO(dev)->gen >= 9)
-                       flags |= BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270);
+                       flags |= DRM_ROTATE_90 | DRM_ROTATE_270;
 
                dev->mode_config.rotation_property =
                        drm_mode_create_rotation_property(dev, flags);
@@ -14394,19 +15010,17 @@ intel_check_cursor_plane(struct drm_plane *plane,
                         struct intel_crtc_state *crtc_state,
                         struct intel_plane_state *state)
 {
-       struct drm_crtc *crtc = crtc_state->base.crtc;
        struct drm_framebuffer *fb = state->base.fb;
        struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        enum pipe pipe = to_intel_plane(plane)->pipe;
        unsigned stride;
        int ret;
 
-       ret = drm_plane_helper_check_update(plane, crtc, fb, &state->src,
-                                           &state->dst, &state->clip,
-                                           state->base.rotation,
-                                           DRM_PLANE_HELPER_NO_SCALING,
-                                           DRM_PLANE_HELPER_NO_SCALING,
-                                           true, true, &state->visible);
+       ret = drm_plane_helper_check_state(&state->base,
+                                          &state->clip,
+                                          DRM_PLANE_HELPER_NO_SCALING,
+                                          DRM_PLANE_HELPER_NO_SCALING,
+                                          true, true);
        if (ret)
                return ret;
 
@@ -14443,7 +15057,7 @@ intel_check_cursor_plane(struct drm_plane *plane,
         * Refuse the put the cursor into that compromised position.
         */
        if (IS_CHERRYVIEW(plane->dev) && pipe == PIPE_C &&
-           state->visible && state->base.crtc_x < 0) {
+           state->base.visible && state->base.crtc_x < 0) {
                DRM_DEBUG_KMS("CHV cursor C not allowed to straddle the left screen edge\n");
                return -EINVAL;
        }
@@ -14475,7 +15089,7 @@ intel_update_cursor_plane(struct drm_plane *plane,
        if (!obj)
                addr = 0;
        else if (!INTEL_INFO(dev)->cursor_needs_physical)
-               addr = i915_gem_obj_ggtt_offset(obj);
+               addr = i915_gem_object_ggtt_offset(obj, NULL);
        else
                addr = obj->phys_handle->busaddr;
 
@@ -14521,8 +15135,8 @@ static struct drm_plane *intel_cursor_plane_create(struct drm_device *dev,
                if (!dev->mode_config.rotation_property)
                        dev->mode_config.rotation_property =
                                drm_mode_create_rotation_property(dev,
-                                                       BIT(DRM_ROTATE_0) |
-                                                       BIT(DRM_ROTATE_180));
+                                                       DRM_ROTATE_0 |
+                                                       DRM_ROTATE_180);
                if (dev->mode_config.rotation_property)
                        drm_object_attach_property(&cursor->base.base,
                                dev->mode_config.rotation_property,
@@ -14728,12 +15342,50 @@ static bool intel_crt_present(struct drm_device *dev)
        return true;
 }
 
+void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv)
+{
+       int pps_num;
+       int pps_idx;
+
+       if (HAS_DDI(dev_priv))
+               return;
+       /*
+        * This w/a is needed at least on CPT/PPT, but to be sure apply it
+        * everywhere where registers can be write protected.
+        */
+       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               pps_num = 2;
+       else
+               pps_num = 1;
+
+       for (pps_idx = 0; pps_idx < pps_num; pps_idx++) {
+               u32 val = I915_READ(PP_CONTROL(pps_idx));
+
+               val = (val & ~PANEL_UNLOCK_MASK) | PANEL_UNLOCK_REGS;
+               I915_WRITE(PP_CONTROL(pps_idx), val);
+       }
+}
+
+static void intel_pps_init(struct drm_i915_private *dev_priv)
+{
+       if (HAS_PCH_SPLIT(dev_priv) || IS_BROXTON(dev_priv))
+               dev_priv->pps_mmio_base = PCH_PPS_BASE;
+       else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               dev_priv->pps_mmio_base = VLV_PPS_BASE;
+       else
+               dev_priv->pps_mmio_base = PPS_BASE;
+
+       intel_pps_unlock_regs_wa(dev_priv);
+}
+
 static void intel_setup_outputs(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_encoder *encoder;
        bool dpd_is_edp = false;
 
+       intel_pps_init(dev_priv);
+
        /*
         * intel_edp_init_connector() depends on this completing first, to
         * prevent the registeration of both eDP and LVDS and the incorrect
@@ -14921,7 +15573,7 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
        drm_framebuffer_cleanup(fb);
        mutex_lock(&dev->struct_mutex);
        WARN_ON(!intel_fb->obj->framebuffer_references--);
-       drm_gem_object_unreference(&intel_fb->obj->base);
+       i915_gem_object_put(intel_fb->obj);
        mutex_unlock(&dev->struct_mutex);
        kfree(intel_fb);
 }
@@ -15001,24 +15653,27 @@ static int intel_framebuffer_init(struct drm_device *dev,
                                  struct drm_i915_gem_object *obj)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
-       unsigned int aligned_height;
+       unsigned int tiling = i915_gem_object_get_tiling(obj);
        int ret;
        u32 pitch_limit, stride_alignment;
+       char *format_name;
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
        if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) {
-               /* Enforce that fb modifier and tiling mode match, but only for
-                * X-tiled. This is needed for FBC. */
-               if (!!(obj->tiling_mode == I915_TILING_X) !=
-                   !!(mode_cmd->modifier[0] == I915_FORMAT_MOD_X_TILED)) {
+               /*
+                * If there's a fence, enforce that
+                * the fb modifier and tiling mode match.
+                */
+               if (tiling != I915_TILING_NONE &&
+                   tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) {
                        DRM_DEBUG("tiling_mode doesn't match fb modifier\n");
                        return -EINVAL;
                }
        } else {
-               if (obj->tiling_mode == I915_TILING_X)
+               if (tiling == I915_TILING_X) {
                        mode_cmd->modifier[0] = I915_FORMAT_MOD_X_TILED;
-               else if (obj->tiling_mode == I915_TILING_Y) {
+               } else if (tiling == I915_TILING_Y) {
                        DRM_DEBUG("No Y tiling for legacy addfb\n");
                        return -EINVAL;
                }
@@ -15042,6 +15697,16 @@ static int intel_framebuffer_init(struct drm_device *dev,
                return -EINVAL;
        }
 
+       /*
+        * gen2/3 display engine uses the fence if present,
+        * so the tiling mode must match the fb modifier exactly.
+        */
+       if (INTEL_INFO(dev_priv)->gen < 4 &&
+           tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) {
+               DRM_DEBUG("tiling_mode must match fb modifier exactly on gen2/3\n");
+               return -EINVAL;
+       }
+
        stride_alignment = intel_fb_stride_alignment(dev_priv,
                                                     mode_cmd->modifier[0],
                                                     mode_cmd->pixel_format);
@@ -15061,10 +15726,15 @@ static int intel_framebuffer_init(struct drm_device *dev,
                return -EINVAL;
        }
 
-       if (mode_cmd->modifier[0] == I915_FORMAT_MOD_X_TILED &&
-           mode_cmd->pitches[0] != obj->stride) {
+       /*
+        * If there's a fence, enforce that
+        * the fb pitch and fence stride match.
+        */
+       if (tiling != I915_TILING_NONE &&
+           mode_cmd->pitches[0] != i915_gem_object_get_stride(obj)) {
                DRM_DEBUG("pitch (%d) must match tiling stride (%d)\n",
-                         mode_cmd->pitches[0], obj->stride);
+                         mode_cmd->pitches[0],
+                         i915_gem_object_get_stride(obj));
                return -EINVAL;
        }
 
@@ -15077,16 +15747,18 @@ static int intel_framebuffer_init(struct drm_device *dev,
                break;
        case DRM_FORMAT_XRGB1555:
                if (INTEL_INFO(dev)->gen > 3) {
-                       DRM_DEBUG("unsupported pixel format: %s\n",
-                                 drm_get_format_name(mode_cmd->pixel_format));
+                       format_name = drm_get_format_name(mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
                }
                break;
        case DRM_FORMAT_ABGR8888:
                if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev) &&
                    INTEL_INFO(dev)->gen < 9) {
-                       DRM_DEBUG("unsupported pixel format: %s\n",
-                                 drm_get_format_name(mode_cmd->pixel_format));
+                       format_name = drm_get_format_name(mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
                }
                break;
@@ -15094,15 +15766,17 @@ static int intel_framebuffer_init(struct drm_device *dev,
        case DRM_FORMAT_XRGB2101010:
        case DRM_FORMAT_XBGR2101010:
                if (INTEL_INFO(dev)->gen < 4) {
-                       DRM_DEBUG("unsupported pixel format: %s\n",
-                                 drm_get_format_name(mode_cmd->pixel_format));
+                       format_name = drm_get_format_name(mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
                }
                break;
        case DRM_FORMAT_ABGR2101010:
                if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev)) {
-                       DRM_DEBUG("unsupported pixel format: %s\n",
-                                 drm_get_format_name(mode_cmd->pixel_format));
+                       format_name = drm_get_format_name(mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
                }
                break;
@@ -15111,14 +15785,16 @@ static int intel_framebuffer_init(struct drm_device *dev,
        case DRM_FORMAT_YVYU:
        case DRM_FORMAT_VYUY:
                if (INTEL_INFO(dev)->gen < 5) {
-                       DRM_DEBUG("unsupported pixel format: %s\n",
-                                 drm_get_format_name(mode_cmd->pixel_format));
+                       format_name = drm_get_format_name(mode_cmd->pixel_format);
+                       DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+                       kfree(format_name);
                        return -EINVAL;
                }
                break;
        default:
-               DRM_DEBUG("unsupported pixel format: %s\n",
-                         drm_get_format_name(mode_cmd->pixel_format));
+               format_name = drm_get_format_name(mode_cmd->pixel_format);
+               DRM_DEBUG("unsupported pixel format: %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
@@ -15126,17 +15802,12 @@ static int intel_framebuffer_init(struct drm_device *dev,
        if (mode_cmd->offsets[0] != 0)
                return -EINVAL;
 
-       aligned_height = intel_fb_align_height(dev, mode_cmd->height,
-                                              mode_cmd->pixel_format,
-                                              mode_cmd->modifier[0]);
-       /* FIXME drm helper for size checks (especially planar formats)? */
-       if (obj->base.size < aligned_height * mode_cmd->pitches[0])
-               return -EINVAL;
-
        drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
        intel_fb->obj = obj;
 
-       intel_fill_fb_info(dev_priv, &intel_fb->base);
+       ret = intel_fill_fb_info(dev_priv, &intel_fb->base);
+       if (ret)
+               return ret;
 
        ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
        if (ret) {
@@ -15158,13 +15829,13 @@ intel_user_framebuffer_create(struct drm_device *dev,
        struct drm_i915_gem_object *obj;
        struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd;
 
-       obj = to_intel_bo(drm_gem_object_lookup(filp, mode_cmd.handles[0]));
-       if (&obj->base == NULL)
+       obj = i915_gem_object_lookup(filp, mode_cmd.handles[0]);
+       if (!obj)
                return ERR_PTR(-ENOENT);
 
        fb = intel_framebuffer_create(dev, &mode_cmd, obj);
        if (IS_ERR(fb))
-               drm_gem_object_unreference_unlocked(&obj->base);
+               i915_gem_object_put_unlocked(obj);
 
        return fb;
 }
@@ -15347,6 +16018,11 @@ void intel_init_display_hooks(struct drm_i915_private *dev_priv)
                        skl_modeset_calc_cdclk;
        }
 
+       if (dev_priv->info.gen >= 9)
+               dev_priv->display.update_crtcs = skl_update_crtcs;
+       else
+               dev_priv->display.update_crtcs = intel_update_crtcs;
+
        switch (INTEL_INFO(dev_priv)->gen) {
        case 2:
                dev_priv->display.queue_flip = intel_gen2_queue_flip;
@@ -15548,15 +16224,16 @@ static void intel_init_quirks(struct drm_device *dev)
 static void i915_disable_vga(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        u8 sr1;
        i915_reg_t vga_reg = i915_vgacntrl_reg(dev);
 
        /* WaEnableVGAAccessThroughIOPort:ctg,elk,ilk,snb,ivb,vlv,hsw */
-       vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
+       vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO);
        outb(SR01, VGA_SR_INDEX);
        sr1 = inb(VGA_SR_DATA);
        outb(sr1 | 1<<5, VGA_SR_DATA);
-       vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
+       vga_put(pdev, VGA_RSRC_LEGACY_IO);
        udelay(300);
 
        I915_WRITE(vga_reg, VGA_DISP_DISABLE);
@@ -15572,7 +16249,6 @@ void intel_modeset_init_hw(struct drm_device *dev)
        dev_priv->atomic_cdclk_freq = dev_priv->cdclk_freq;
 
        intel_init_clock_gating(dev);
-       intel_enable_gt_powersave(dev_priv);
 }
 
 /*
@@ -15839,15 +16515,22 @@ static bool intel_crtc_has_encoders(struct intel_crtc *crtc)
        return false;
 }
 
-static bool intel_encoder_has_connectors(struct intel_encoder *encoder)
+static struct intel_connector *intel_encoder_find_connector(struct intel_encoder *encoder)
 {
        struct drm_device *dev = encoder->base.dev;
        struct intel_connector *connector;
 
        for_each_connector_on_encoder(dev, &encoder->base, connector)
-               return true;
+               return connector;
 
-       return false;
+       return NULL;
+}
+
+static bool has_pch_trancoder(struct drm_i915_private *dev_priv,
+                             enum transcoder pch_transcoder)
+{
+       return HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv) ||
+               (HAS_PCH_LPT_H(dev_priv) && pch_transcoder == TRANSCODER_A);
 }
 
 static void intel_sanitize_crtc(struct intel_crtc *crtc)
@@ -15893,7 +16576,7 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
                 * Temporarily change the plane mapping and disable everything
                 * ...  */
                plane = crtc->plane;
-               to_intel_plane_state(crtc->base.primary->state)->visible = true;
+               to_intel_plane_state(crtc->base.primary->state)->base.visible = true;
                crtc->plane = !plane;
                intel_crtc_disable_noatomic(&crtc->base);
                crtc->plane = plane;
@@ -15928,14 +16611,23 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc)
                 * worst a fifo underrun happens which also sets this to false.
                 */
                crtc->cpu_fifo_underrun_disabled = true;
-               crtc->pch_fifo_underrun_disabled = true;
+               /*
+                * We track the PCH trancoder underrun reporting state
+                * within the crtc. With crtc for pipe A housing the underrun
+                * reporting state for PCH transcoder A, crtc for pipe B housing
+                * it for PCH transcoder B, etc. LPT-H has only PCH transcoder A,
+                * and marking underrun reporting as disabled for the non-existing
+                * PCH transcoders B and C would prevent enabling the south
+                * error interrupt (see cpt_can_enable_serr_int()).
+                */
+               if (has_pch_trancoder(dev_priv, (enum transcoder)crtc->pipe))
+                       crtc->pch_fifo_underrun_disabled = true;
        }
 }
 
 static void intel_sanitize_encoder(struct intel_encoder *encoder)
 {
        struct intel_connector *connector;
-       struct drm_device *dev = encoder->base.dev;
 
        /* We need to check both for a crtc link (meaning that the
         * encoder is active and trying to read from a pipe) and the
@@ -15943,7 +16635,8 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
        bool has_active_crtc = encoder->base.crtc &&
                to_intel_crtc(encoder->base.crtc)->active;
 
-       if (intel_encoder_has_connectors(encoder) && !has_active_crtc) {
+       connector = intel_encoder_find_connector(encoder);
+       if (connector && !has_active_crtc) {
                DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n",
                              encoder->base.base.id,
                              encoder->base.name);
@@ -15952,12 +16645,14 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
                 * fallout from our resume register restoring. Disable
                 * the encoder manually again. */
                if (encoder->base.crtc) {
+                       struct drm_crtc_state *crtc_state = encoder->base.crtc->state;
+
                        DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
                                      encoder->base.base.id,
                                      encoder->base.name);
-                       encoder->disable(encoder);
+                       encoder->disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
                        if (encoder->post_disable)
-                               encoder->post_disable(encoder);
+                               encoder->post_disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
                }
                encoder->base.crtc = NULL;
 
@@ -15965,12 +16660,9 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
                 * a bug in one of the get_hw_state functions. Or someplace else
                 * in our code, like the register restore mess on resume. Clamp
                 * things to off as a safer default. */
-               for_each_intel_connector(dev, connector) {
-                       if (connector->encoder != encoder)
-                               continue;
-                       connector->base.dpms = DRM_MODE_DPMS_OFF;
-                       connector->base.encoder = NULL;
-               }
+
+               connector->base.dpms = DRM_MODE_DPMS_OFF;
+               connector->base.encoder = NULL;
        }
        /* Enabled encoders without active connectors will be fixed in
         * the crtc fixup. */
@@ -16020,10 +16712,10 @@ static void readout_plane_state(struct intel_crtc *crtc)
        struct intel_plane_state *plane_state =
                to_intel_plane_state(primary->state);
 
-       plane_state->visible = crtc->active &&
+       plane_state->base.visible = crtc->active &&
                primary_get_hw_state(to_intel_plane(primary));
 
-       if (plane_state->visible)
+       if (plane_state->base.visible)
                crtc->base.state->plane_mask |= 1 << drm_plane_index(primary);
 }
 
@@ -16282,7 +16974,6 @@ void intel_modeset_gem_init(struct drm_device *dev)
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *c;
        struct drm_i915_gem_object *obj;
-       int ret;
 
        intel_init_gt_powersave(dev_priv);
 
@@ -16296,15 +16987,17 @@ void intel_modeset_gem_init(struct drm_device *dev)
         * for this.
         */
        for_each_crtc(dev, c) {
+               struct i915_vma *vma;
+
                obj = intel_fb_obj(c->primary->fb);
                if (obj == NULL)
                        continue;
 
                mutex_lock(&dev->struct_mutex);
-               ret = intel_pin_and_fence_fb_obj(c->primary->fb,
+               vma = intel_pin_and_fence_fb_obj(c->primary->fb,
                                                 c->primary->state->rotation);
                mutex_unlock(&dev->struct_mutex);
-               if (ret) {
+               if (IS_ERR(vma)) {
                        DRM_ERROR("failed to pin boot fb on pipe %d\n",
                                  to_intel_crtc(c)->pipe);
                        drm_framebuffer_unreference(c->primary->fb);
index 21b04c3..14a3cf0 100644 (file)
@@ -190,6 +190,29 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes)
        return (max_link_clock * max_lanes * 8) / 10;
 }
 
+static int
+intel_dp_downstream_max_dotclock(struct intel_dp *intel_dp)
+{
+       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+       struct intel_encoder *encoder = &intel_dig_port->base;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       int max_dotclk = dev_priv->max_dotclk_freq;
+       int ds_max_dotclk;
+
+       int type = intel_dp->downstream_ports[0] & DP_DS_PORT_TYPE_MASK;
+
+       if (type != DP_DS_PORT_TYPE_VGA)
+               return max_dotclk;
+
+       ds_max_dotclk = drm_dp_downstream_max_clock(intel_dp->dpcd,
+                                                   intel_dp->downstream_ports);
+
+       if (ds_max_dotclk != 0)
+               max_dotclk = min(max_dotclk, ds_max_dotclk);
+
+       return max_dotclk;
+}
+
 static enum drm_mode_status
 intel_dp_mode_valid(struct drm_connector *connector,
                    struct drm_display_mode *mode)
@@ -199,7 +222,9 @@ intel_dp_mode_valid(struct drm_connector *connector,
        struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
        int target_clock = mode->clock;
        int max_rate, mode_rate, max_lanes, max_link_clock;
-       int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+       int max_dotclk;
+
+       max_dotclk = intel_dp_downstream_max_dotclock(intel_dp);
 
        if (is_edp(intel_dp) && fixed_mode) {
                if (mode->hdisplay > fixed_mode->hdisplay)
@@ -256,6 +281,8 @@ intel_dp_init_panel_power_sequencer(struct drm_device *dev,
 static void
 intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                                              struct intel_dp *intel_dp);
+static void
+intel_dp_pps_init(struct drm_device *dev, struct intel_dp *intel_dp);
 
 static void pps_lock(struct intel_dp *intel_dp)
 {
@@ -463,13 +490,13 @@ typedef bool (*vlv_pipe_check)(struct drm_i915_private *dev_priv,
 static bool vlv_pipe_has_pp_on(struct drm_i915_private *dev_priv,
                               enum pipe pipe)
 {
-       return I915_READ(VLV_PIPE_PP_STATUS(pipe)) & PP_ON;
+       return I915_READ(PP_STATUS(pipe)) & PP_ON;
 }
 
 static bool vlv_pipe_has_vdd_on(struct drm_i915_private *dev_priv,
                                enum pipe pipe)
 {
-       return I915_READ(VLV_PIPE_PP_CONTROL(pipe)) & EDP_FORCE_VDD;
+       return I915_READ(PP_CONTROL(pipe)) & EDP_FORCE_VDD;
 }
 
 static bool vlv_pipe_any(struct drm_i915_private *dev_priv,
@@ -486,7 +513,7 @@ vlv_initial_pps_pipe(struct drm_i915_private *dev_priv,
        enum pipe pipe;
 
        for (pipe = PIPE_A; pipe <= PIPE_B; pipe++) {
-               u32 port_sel = I915_READ(VLV_PIPE_PP_ON_DELAYS(pipe)) &
+               u32 port_sel = I915_READ(PP_ON_DELAYS(pipe)) &
                        PANEL_PORT_SELECT_MASK;
 
                if (port_sel != PANEL_PORT_SELECT_VLV(port))
@@ -583,30 +610,21 @@ static void intel_pps_get_registers(struct drm_i915_private *dev_priv,
                                    struct intel_dp *intel_dp,
                                    struct pps_registers *regs)
 {
+       int pps_idx = 0;
+
        memset(regs, 0, sizeof(*regs));
 
-       if (IS_BROXTON(dev_priv)) {
-               int idx = bxt_power_sequencer_idx(intel_dp);
-
-               regs->pp_ctrl = BXT_PP_CONTROL(idx);
-               regs->pp_stat = BXT_PP_STATUS(idx);
-               regs->pp_on = BXT_PP_ON_DELAYS(idx);
-               regs->pp_off = BXT_PP_OFF_DELAYS(idx);
-       } else if (HAS_PCH_SPLIT(dev_priv)) {
-               regs->pp_ctrl = PCH_PP_CONTROL;
-               regs->pp_stat = PCH_PP_STATUS;
-               regs->pp_on = PCH_PP_ON_DELAYS;
-               regs->pp_off = PCH_PP_OFF_DELAYS;
-               regs->pp_div = PCH_PP_DIVISOR;
-       } else {
-               enum pipe pipe = vlv_power_sequencer_pipe(intel_dp);
+       if (IS_BROXTON(dev_priv))
+               pps_idx = bxt_power_sequencer_idx(intel_dp);
+       else if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+               pps_idx = vlv_power_sequencer_pipe(intel_dp);
 
-               regs->pp_ctrl = VLV_PIPE_PP_CONTROL(pipe);
-               regs->pp_stat = VLV_PIPE_PP_STATUS(pipe);
-               regs->pp_on = VLV_PIPE_PP_ON_DELAYS(pipe);
-               regs->pp_off = VLV_PIPE_PP_OFF_DELAYS(pipe);
-               regs->pp_div = VLV_PIPE_PP_DIVISOR(pipe);
-       }
+       regs->pp_ctrl = PP_CONTROL(pps_idx);
+       regs->pp_stat = PP_STATUS(pps_idx);
+       regs->pp_on = PP_ON_DELAYS(pps_idx);
+       regs->pp_off = PP_OFF_DELAYS(pps_idx);
+       if (!IS_BROXTON(dev_priv))
+               regs->pp_div = PP_DIVISOR(pps_idx);
 }
 
 static i915_reg_t
@@ -651,8 +669,8 @@ static int edp_notify_handler(struct notifier_block *this, unsigned long code,
                i915_reg_t pp_ctrl_reg, pp_div_reg;
                u32 pp_div;
 
-               pp_ctrl_reg = VLV_PIPE_PP_CONTROL(pipe);
-               pp_div_reg  = VLV_PIPE_PP_DIVISOR(pipe);
+               pp_ctrl_reg = PP_CONTROL(pipe);
+               pp_div_reg  = PP_DIVISOR(pipe);
                pp_div = I915_READ(pp_div_reg);
                pp_div &= PP_REFERENCE_DIVIDER_MASK;
 
@@ -1041,10 +1059,10 @@ intel_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                if (WARN_ON(txsize > 20))
                        return -E2BIG;
 
+               WARN_ON(!msg->buffer != !msg->size);
+
                if (msg->buffer)
                        memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
-               else
-                       WARN_ON(msg->size);
 
                ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
                if (ret > 0) {
@@ -1250,7 +1268,7 @@ intel_dp_aux_fini(struct intel_dp *intel_dp)
 }
 
 static void
-intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
+intel_dp_aux_init(struct intel_dp *intel_dp)
 {
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        enum port port = intel_dig_port->port;
@@ -1426,6 +1444,44 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp)
        DRM_DEBUG_KMS("common rates: %s\n", str);
 }
 
+static void intel_dp_print_hw_revision(struct intel_dp *intel_dp)
+{
+       uint8_t rev;
+       int len;
+
+       if ((drm_debug & DRM_UT_KMS) == 0)
+               return;
+
+       if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+             DP_DWN_STRM_PORT_PRESENT))
+               return;
+
+       len = drm_dp_dpcd_read(&intel_dp->aux, DP_BRANCH_HW_REV, &rev, 1);
+       if (len < 0)
+               return;
+
+       DRM_DEBUG_KMS("sink hw revision: %d.%d\n", (rev & 0xf0) >> 4, rev & 0xf);
+}
+
+static void intel_dp_print_sw_revision(struct intel_dp *intel_dp)
+{
+       uint8_t rev[2];
+       int len;
+
+       if ((drm_debug & DRM_UT_KMS) == 0)
+               return;
+
+       if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+             DP_DWN_STRM_PORT_PRESENT))
+               return;
+
+       len = drm_dp_dpcd_read(&intel_dp->aux, DP_BRANCH_SW_REV, &rev, 2);
+       if (len < 0)
+               return;
+
+       DRM_DEBUG_KMS("sink sw revision: %d.%d\n", rev[0], rev[1]);
+}
+
 static int rate_to_index(int find, const int *rates)
 {
        int i = 0;
@@ -1447,7 +1503,7 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp)
        if (WARN_ON(len <= 0))
                return 162000;
 
-       return rates[rate_to_index(0, rates) - 1];
+       return rates[len - 1];
 }
 
 int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
@@ -1468,9 +1524,24 @@ void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
        }
 }
 
+static int intel_dp_compute_bpp(struct intel_dp *intel_dp,
+                               struct intel_crtc_state *pipe_config)
+{
+       int bpp, bpc;
+
+       bpp = pipe_config->pipe_bpp;
+       bpc = drm_dp_downstream_max_bpc(intel_dp->dpcd, intel_dp->downstream_ports);
+
+       if (bpc > 0)
+               bpp = min(bpp, 3*bpc);
+
+       return bpp;
+}
+
 bool
 intel_dp_compute_config(struct intel_encoder *encoder,
-                       struct intel_crtc_state *pipe_config)
+                       struct intel_crtc_state *pipe_config,
+                       struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1533,7 +1604,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 
        /* Walk through all bpp values. Luckily they're all nicely spaced with 2
         * bpc in between. */
-       bpp = pipe_config->pipe_bpp;
+       bpp = intel_dp_compute_bpp(intel_dp, pipe_config);
        if (is_edp(intel_dp)) {
 
                /* Get bpp from vbt only for panels that dont have bpp in edid */
@@ -1647,22 +1718,28 @@ found:
 }
 
 void intel_dp_set_link_params(struct intel_dp *intel_dp,
-                             const struct intel_crtc_state *pipe_config)
+                             int link_rate, uint8_t lane_count,
+                             bool link_mst)
 {
-       intel_dp->link_rate = pipe_config->port_clock;
-       intel_dp->lane_count = pipe_config->lane_count;
+       intel_dp->link_rate = link_rate;
+       intel_dp->lane_count = lane_count;
+       intel_dp->link_mst = link_mst;
 }
 
-static void intel_dp_prepare(struct intel_encoder *encoder)
+static void intel_dp_prepare(struct intel_encoder *encoder,
+                            struct intel_crtc_state *pipe_config)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        enum port port = dp_to_dig_port(intel_dp)->port;
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+       const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
 
-       intel_dp_set_link_params(intel_dp, crtc->config);
+       intel_dp_set_link_params(intel_dp, pipe_config->port_clock,
+                                pipe_config->lane_count,
+                                intel_crtc_has_type(pipe_config,
+                                                    INTEL_OUTPUT_DP_MST));
 
        /*
         * There are four kinds of DP registers:
@@ -1688,7 +1765,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder)
 
        /* Handle DP bits in common between all three register formats */
        intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
-       intel_dp->DP |= DP_PORT_WIDTH(crtc->config->lane_count);
+       intel_dp->DP |= DP_PORT_WIDTH(pipe_config->lane_count);
 
        /* Split out the IBX/CPU vs CPT settings */
 
@@ -1716,7 +1793,7 @@ static void intel_dp_prepare(struct intel_encoder *encoder)
                I915_WRITE(TRANS_DP_CTL(crtc->pipe), trans_dp);
        } else {
                if (!HAS_PCH_SPLIT(dev) && !IS_VALLEYVIEW(dev) &&
-                   !IS_CHERRYVIEW(dev) && crtc->config->limited_color_range)
+                   !IS_CHERRYVIEW(dev) && pipe_config->limited_color_range)
                        intel_dp->DP |= DP_COLOR_RANGE_16_235;
 
                if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
@@ -1835,7 +1912,8 @@ static  u32 ironlake_get_pp_control(struct intel_dp *intel_dp)
        lockdep_assert_held(&dev_priv->pps_mutex);
 
        control = I915_READ(_pp_ctrl_reg(intel_dp));
-       if (!IS_BROXTON(dev)) {
+       if (WARN_ON(!HAS_DDI(dev_priv) &&
+                   (control & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS)) {
                control &= ~PANEL_UNLOCK_MASK;
                control |= PANEL_UNLOCK_REGS;
        }
@@ -1956,7 +2034,7 @@ static void edp_panel_vdd_off_sync(struct intel_dp *intel_dp)
        DRM_DEBUG_KMS("PP_STATUS: 0x%08x PP_CONTROL: 0x%08x\n",
        I915_READ(pp_stat_reg), I915_READ(pp_ctrl_reg));
 
-       if ((pp & POWER_TARGET_ON) == 0)
+       if ((pp & PANEL_POWER_ON) == 0)
                intel_dp->panel_power_off_time = ktime_get_boottime();
 
        power_domain = intel_display_port_aux_power_domain(intel_encoder);
@@ -2043,7 +2121,7 @@ static void edp_panel_on(struct intel_dp *intel_dp)
                POSTING_READ(pp_ctrl_reg);
        }
 
-       pp |= POWER_TARGET_ON;
+       pp |= PANEL_POWER_ON;
        if (!IS_GEN5(dev))
                pp |= PANEL_POWER_RESET;
 
@@ -2095,7 +2173,7 @@ static void edp_panel_off(struct intel_dp *intel_dp)
        pp = ironlake_get_pp_control(intel_dp);
        /* We need to switch off panel power _and_ force vdd, for otherwise some
         * panels get very unhappy and cease to work. */
-       pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
+       pp &= ~(PANEL_POWER_ON | PANEL_POWER_RESET | EDP_FORCE_VDD |
                EDP_BLC_ENABLE);
 
        pp_ctrl_reg = _pp_ctrl_reg(intel_dp);
@@ -2254,10 +2332,10 @@ static void assert_edp_pll(struct drm_i915_private *dev_priv, bool state)
 #define assert_edp_pll_enabled(d) assert_edp_pll((d), true)
 #define assert_edp_pll_disabled(d) assert_edp_pll((d), false)
 
-static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
+static void ironlake_edp_pll_on(struct intel_dp *intel_dp,
+                               struct intel_crtc_state *pipe_config)
 {
-       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
-       struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
        struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
 
        assert_pipe_disabled(dev_priv, crtc->pipe);
@@ -2265,11 +2343,11 @@ static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
        assert_edp_pll_disabled(dev_priv);
 
        DRM_DEBUG_KMS("enabling eDP PLL for clock %d\n",
-                     crtc->config->port_clock);
+                     pipe_config->port_clock);
 
        intel_dp->DP &= ~DP_PLL_FREQ_MASK;
 
-       if (crtc->config->port_clock == 162000)
+       if (pipe_config->port_clock == 162000)
                intel_dp->DP |= DP_PLL_FREQ_162MHZ;
        else
                intel_dp->DP |= DP_PLL_FREQ_270MHZ;
@@ -2478,16 +2556,17 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
        }
 }
 
-static void intel_disable_dp(struct intel_encoder *encoder)
+static void intel_disable_dp(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
-       struct drm_device *dev = encoder->base.dev;
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 
-       if (crtc->config->has_audio)
+       if (old_crtc_state->has_audio)
                intel_audio_codec_disable(encoder);
 
-       if (HAS_PSR(dev) && !HAS_DDI(dev))
+       if (HAS_PSR(dev_priv) && !HAS_DDI(dev_priv))
                intel_psr_disable(intel_dp);
 
        /* Make sure the panel is off before trying to change the mode. But also
@@ -2498,11 +2577,13 @@ static void intel_disable_dp(struct intel_encoder *encoder)
        intel_edp_panel_off(intel_dp);
 
        /* disable the port before the pipe on g4x */
-       if (INTEL_INFO(dev)->gen < 5)
+       if (INTEL_GEN(dev_priv) < 5)
                intel_dp_link_down(intel_dp);
 }
 
-static void ilk_post_disable_dp(struct intel_encoder *encoder)
+static void ilk_post_disable_dp(struct intel_encoder *encoder,
+                               struct intel_crtc_state *old_crtc_state,
+                               struct drm_connector_state *old_conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        enum port port = dp_to_dig_port(intel_dp)->port;
@@ -2514,14 +2595,18 @@ static void ilk_post_disable_dp(struct intel_encoder *encoder)
                ironlake_edp_pll_off(intel_dp);
 }
 
-static void vlv_post_disable_dp(struct intel_encoder *encoder)
+static void vlv_post_disable_dp(struct intel_encoder *encoder,
+                               struct intel_crtc_state *old_crtc_state,
+                               struct drm_connector_state *old_conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
        intel_dp_link_down(intel_dp);
 }
 
-static void chv_post_disable_dp(struct intel_encoder *encoder)
+static void chv_post_disable_dp(struct intel_encoder *encoder,
+                               struct intel_crtc_state *old_crtc_state,
+                               struct drm_connector_state *old_conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
@@ -2547,6 +2632,10 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
        struct drm_i915_private *dev_priv = to_i915(dev);
        enum port port = intel_dig_port->port;
 
+       if (dp_train_pat & DP_TRAINING_PATTERN_MASK)
+               DRM_DEBUG_KMS("Using DP training pattern TPS%d\n",
+                             dp_train_pat & DP_TRAINING_PATTERN_MASK);
+
        if (HAS_DDI(dev)) {
                uint32_t temp = I915_READ(DP_TP_CTL(port));
 
@@ -2588,7 +2677,7 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
                        *DP |= DP_LINK_TRAIN_PAT_2_CPT;
                        break;
                case DP_TRAINING_PATTERN_3:
-                       DRM_ERROR("DP training pattern 3 not supported\n");
+                       DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n");
                        *DP |= DP_LINK_TRAIN_PAT_2_CPT;
                        break;
                }
@@ -2613,7 +2702,7 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
                        if (IS_CHERRYVIEW(dev)) {
                                *DP |= DP_LINK_TRAIN_PAT_3_CHV;
                        } else {
-                               DRM_ERROR("DP training pattern 3 not supported\n");
+                               DRM_DEBUG_KMS("TPS3 not supported, using TPS2 instead\n");
                                *DP |= DP_LINK_TRAIN_PAT_2;
                        }
                        break;
@@ -2621,19 +2710,15 @@ _intel_dp_set_link_train(struct intel_dp *intel_dp,
        }
 }
 
-static void intel_dp_enable_port(struct intel_dp *intel_dp)
+static void intel_dp_enable_port(struct intel_dp *intel_dp,
+                                struct intel_crtc_state *old_crtc_state)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc =
-               to_intel_crtc(dp_to_dig_port(intel_dp)->base.base.crtc);
 
        /* enable with pattern 1 (as per spec) */
-       _intel_dp_set_link_train(intel_dp, &intel_dp->DP,
-                                DP_TRAINING_PATTERN_1);
 
-       I915_WRITE(intel_dp->output_reg, intel_dp->DP);
-       POSTING_READ(intel_dp->output_reg);
+       intel_dp_program_link_training_pattern(intel_dp, DP_TRAINING_PATTERN_1);
 
        /*
         * Magic for VLV/CHV. We _must_ first set up the register
@@ -2642,14 +2727,15 @@ static void intel_dp_enable_port(struct intel_dp *intel_dp)
         * fail when the power sequencer is freshly used for this port.
         */
        intel_dp->DP |= DP_PORT_EN;
-       if (crtc->config->has_audio)
+       if (old_crtc_state->has_audio)
                intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
 
        I915_WRITE(intel_dp->output_reg, intel_dp->DP);
        POSTING_READ(intel_dp->output_reg);
 }
 
-static void intel_enable_dp(struct intel_encoder *encoder)
+static void intel_enable_dp(struct intel_encoder *encoder,
+                           struct intel_crtc_state *pipe_config)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
@@ -2666,7 +2752,7 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
                vlv_init_panel_power_sequencer(intel_dp);
 
-       intel_dp_enable_port(intel_dp);
+       intel_dp_enable_port(intel_dp, pipe_config);
 
        edp_panel_vdd_on(intel_dp);
        edp_panel_on(intel_dp);
@@ -2678,7 +2764,7 @@ static void intel_enable_dp(struct intel_encoder *encoder)
                unsigned int lane_mask = 0x0;
 
                if (IS_CHERRYVIEW(dev))
-                       lane_mask = intel_dp_unused_lane_mask(crtc->config->lane_count);
+                       lane_mask = intel_dp_unused_lane_mask(pipe_config->lane_count);
 
                vlv_wait_port_ready(dev_priv, dp_to_dig_port(intel_dp),
                                    lane_mask);
@@ -2688,22 +2774,26 @@ static void intel_enable_dp(struct intel_encoder *encoder)
        intel_dp_start_link_train(intel_dp);
        intel_dp_stop_link_train(intel_dp);
 
-       if (crtc->config->has_audio) {
+       if (pipe_config->has_audio) {
                DRM_DEBUG_DRIVER("Enabling DP audio on pipe %c\n",
                                 pipe_name(pipe));
                intel_audio_codec_enable(encoder);
        }
 }
 
-static void g4x_enable_dp(struct intel_encoder *encoder)
+static void g4x_enable_dp(struct intel_encoder *encoder,
+                         struct intel_crtc_state *pipe_config,
+                         struct drm_connector_state *conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
-       intel_enable_dp(encoder);
+       intel_enable_dp(encoder, pipe_config);
        intel_edp_backlight_on(intel_dp);
 }
 
-static void vlv_enable_dp(struct intel_encoder *encoder)
+static void vlv_enable_dp(struct intel_encoder *encoder,
+                         struct intel_crtc_state *pipe_config,
+                         struct drm_connector_state *conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
 
@@ -2711,16 +2801,18 @@ static void vlv_enable_dp(struct intel_encoder *encoder)
        intel_psr_enable(intel_dp);
 }
 
-static void g4x_pre_enable_dp(struct intel_encoder *encoder)
+static void g4x_pre_enable_dp(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
        enum port port = dp_to_dig_port(intel_dp)->port;
 
-       intel_dp_prepare(encoder);
+       intel_dp_prepare(encoder, pipe_config);
 
        /* Only ilk+ has port A */
        if (port == PORT_A)
-               ironlake_edp_pll_on(intel_dp);
+               ironlake_edp_pll_on(intel_dp, pipe_config);
 }
 
 static void vlv_detach_power_sequencer(struct intel_dp *intel_dp)
@@ -2728,7 +2820,7 @@ static void vlv_detach_power_sequencer(struct intel_dp *intel_dp)
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct drm_i915_private *dev_priv = to_i915(intel_dig_port->base.base.dev);
        enum pipe pipe = intel_dp->pps_pipe;
-       i915_reg_t pp_on_reg = VLV_PIPE_PP_ON_DELAYS(pipe);
+       i915_reg_t pp_on_reg = PP_ON_DELAYS(pipe);
 
        edp_panel_vdd_off_sync(intel_dp);
 
@@ -2826,38 +2918,48 @@ static void vlv_init_panel_power_sequencer(struct intel_dp *intel_dp)
        intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
 }
 
-static void vlv_pre_enable_dp(struct intel_encoder *encoder)
+static void vlv_pre_enable_dp(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        vlv_phy_pre_encoder_enable(encoder);
 
-       intel_enable_dp(encoder);
+       intel_enable_dp(encoder, pipe_config);
 }
 
-static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder)
+static void vlv_dp_pre_pll_enable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config,
+                                 struct drm_connector_state *conn_state)
 {
-       intel_dp_prepare(encoder);
+       intel_dp_prepare(encoder, pipe_config);
 
        vlv_phy_pre_pll_enable(encoder);
 }
 
-static void chv_pre_enable_dp(struct intel_encoder *encoder)
+static void chv_pre_enable_dp(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        chv_phy_pre_encoder_enable(encoder);
 
-       intel_enable_dp(encoder);
+       intel_enable_dp(encoder, pipe_config);
 
        /* Second common lane will stay alive on its own now */
        chv_phy_release_cl2_override(encoder);
 }
 
-static void chv_dp_pre_pll_enable(struct intel_encoder *encoder)
+static void chv_dp_pre_pll_enable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config,
+                                 struct drm_connector_state *conn_state)
 {
-       intel_dp_prepare(encoder);
+       intel_dp_prepare(encoder, pipe_config);
 
        chv_phy_pre_pll_enable(encoder);
 }
 
-static void chv_dp_post_pll_disable(struct intel_encoder *encoder)
+static void chv_dp_post_pll_disable(struct intel_encoder *encoder,
+                                   struct intel_crtc_state *pipe_config,
+                                   struct drm_connector_state *conn_state)
 {
        chv_phy_post_pll_disable(encoder);
 }
@@ -3395,84 +3497,67 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 }
 
 static bool
-intel_dp_get_dpcd(struct intel_dp *intel_dp)
+intel_dp_read_dpcd(struct intel_dp *intel_dp)
 {
-       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-       struct drm_device *dev = dig_port->base.base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
        if (drm_dp_dpcd_read(&intel_dp->aux, 0x000, intel_dp->dpcd,
                             sizeof(intel_dp->dpcd)) < 0)
                return false; /* aux transfer failed */
 
        DRM_DEBUG_KMS("DPCD: %*ph\n", (int) sizeof(intel_dp->dpcd), intel_dp->dpcd);
 
-       if (intel_dp->dpcd[DP_DPCD_REV] == 0)
-               return false; /* DPCD not present */
+       return intel_dp->dpcd[DP_DPCD_REV] != 0;
+}
 
-       if (drm_dp_dpcd_read(&intel_dp->aux, DP_SINK_COUNT,
-                            &intel_dp->sink_count, 1) < 0)
-               return false;
+static bool
+intel_edp_init_dpcd(struct intel_dp *intel_dp)
+{
+       struct drm_i915_private *dev_priv =
+               to_i915(dp_to_dig_port(intel_dp)->base.base.dev);
 
-       /*
-        * Sink count can change between short pulse hpd hence
-        * a member variable in intel_dp will track any changes
-        * between short pulse interrupts.
-        */
-       intel_dp->sink_count = DP_GET_SINK_COUNT(intel_dp->sink_count);
+       /* this function is meant to be called only once */
+       WARN_ON(intel_dp->dpcd[DP_DPCD_REV] != 0);
 
-       /*
-        * SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
-        * a dongle is present but no display. Unless we require to know
-        * if a dongle is present or not, we don't need to update
-        * downstream port information. So, an early return here saves
-        * time from performing other operations which are not required.
-        */
-       if (!is_edp(intel_dp) && !intel_dp->sink_count)
+       if (!intel_dp_read_dpcd(intel_dp))
                return false;
 
-       /* Check if the panel supports PSR */
-       memset(intel_dp->psr_dpcd, 0, sizeof(intel_dp->psr_dpcd));
-       if (is_edp(intel_dp)) {
-               drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT,
-                                intel_dp->psr_dpcd,
-                                sizeof(intel_dp->psr_dpcd));
-               if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
-                       dev_priv->psr.sink_support = true;
-                       DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
-               }
+       if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
+               dev_priv->no_aux_handshake = intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
+                       DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
 
-               if (INTEL_INFO(dev)->gen >= 9 &&
-                       (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) {
-                       uint8_t frame_sync_cap;
-
-                       dev_priv->psr.sink_support = true;
-                       drm_dp_dpcd_read(&intel_dp->aux,
-                                        DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
-                                        &frame_sync_cap, 1);
-                       dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : false;
-                       /* PSR2 needs frame sync as well */
-                       dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync;
-                       DRM_DEBUG_KMS("PSR2 %s on sink",
-                               dev_priv->psr.psr2_support ? "supported" : "not supported");
-               }
-
-               /* Read the eDP Display control capabilities registers */
-               memset(intel_dp->edp_dpcd, 0, sizeof(intel_dp->edp_dpcd));
-               if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) &&
-                               (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
-                                               intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd)) ==
-                                                               sizeof(intel_dp->edp_dpcd)))
-                       DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd),
-                                       intel_dp->edp_dpcd);
-       }
-
-       DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n",
-                     yesno(intel_dp_source_supports_hbr2(intel_dp)),
-                     yesno(drm_dp_tps3_supported(intel_dp->dpcd)));
+       /* Check if the panel supports PSR */
+       drm_dp_dpcd_read(&intel_dp->aux, DP_PSR_SUPPORT,
+                        intel_dp->psr_dpcd,
+                        sizeof(intel_dp->psr_dpcd));
+       if (intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED) {
+               dev_priv->psr.sink_support = true;
+               DRM_DEBUG_KMS("Detected EDP PSR Panel.\n");
+       }
+
+       if (INTEL_GEN(dev_priv) >= 9 &&
+           (intel_dp->psr_dpcd[0] & DP_PSR2_IS_SUPPORTED)) {
+               uint8_t frame_sync_cap;
+
+               dev_priv->psr.sink_support = true;
+               drm_dp_dpcd_read(&intel_dp->aux,
+                                DP_SINK_DEVICE_AUX_FRAME_SYNC_CAP,
+                                &frame_sync_cap, 1);
+               dev_priv->psr.aux_frame_sync = frame_sync_cap ? true : false;
+               /* PSR2 needs frame sync as well */
+               dev_priv->psr.psr2_support = dev_priv->psr.aux_frame_sync;
+               DRM_DEBUG_KMS("PSR2 %s on sink",
+                             dev_priv->psr.psr2_support ? "supported" : "not supported");
+       }
+
+       /* Read the eDP Display control capabilities registers */
+       if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) &&
+           drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
+                            intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd) ==
+                            sizeof(intel_dp->edp_dpcd)))
+               DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd),
+                             intel_dp->edp_dpcd);
 
        /* Intermediate frequency support */
-       if (is_edp(intel_dp) && (intel_dp->edp_dpcd[0] >= 0x03)) { /* eDp v1.4 or higher */
+       if (intel_dp->edp_dpcd[0] >= 0x03) { /* eDp v1.4 or higher */
                __le16 sink_rates[DP_MAX_SUPPORTED_RATES];
                int i;
 
@@ -3491,7 +3576,36 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp)
                intel_dp->num_sink_rates = i;
        }
 
-       intel_dp_print_rates(intel_dp);
+       return true;
+}
+
+
+static bool
+intel_dp_get_dpcd(struct intel_dp *intel_dp)
+{
+       if (!intel_dp_read_dpcd(intel_dp))
+               return false;
+
+       if (drm_dp_dpcd_read(&intel_dp->aux, DP_SINK_COUNT,
+                            &intel_dp->sink_count, 1) < 0)
+               return false;
+
+       /*
+        * Sink count can change between short pulse hpd hence
+        * a member variable in intel_dp will track any changes
+        * between short pulse interrupts.
+        */
+       intel_dp->sink_count = DP_GET_SINK_COUNT(intel_dp->sink_count);
+
+       /*
+        * SINK_COUNT == 0 and DOWNSTREAM_PORT_PRESENT == 1 implies that
+        * a dongle is present but no display. Unless we require to know
+        * if a dongle is present or not, we don't need to update
+        * downstream port information. So, an early return here saves
+        * time from performing other operations which are not required.
+        */
+       if (!is_edp(intel_dp) && !intel_dp->sink_count)
+               return false;
 
        if (!(intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
              DP_DWN_STRM_PORT_PRESENT))
@@ -3526,7 +3640,7 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
 }
 
 static bool
-intel_dp_probe_mst(struct intel_dp *intel_dp)
+intel_dp_can_mst(struct intel_dp *intel_dp)
 {
        u8 buf[1];
 
@@ -3539,18 +3653,30 @@ intel_dp_probe_mst(struct intel_dp *intel_dp)
        if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
                return false;
 
-       if (drm_dp_dpcd_read(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
-               if (buf[0] & DP_MST_CAP) {
-                       DRM_DEBUG_KMS("Sink is MST capable\n");
-                       intel_dp->is_mst = true;
-               } else {
-                       DRM_DEBUG_KMS("Sink is not MST capable\n");
-                       intel_dp->is_mst = false;
-               }
-       }
+       if (drm_dp_dpcd_read(&intel_dp->aux, DP_MSTM_CAP, buf, 1) != 1)
+               return false;
+
+       return buf[0] & DP_MST_CAP;
+}
+
+static void
+intel_dp_configure_mst(struct intel_dp *intel_dp)
+{
+       if (!i915.enable_dp_mst)
+               return;
+
+       if (!intel_dp->can_mst)
+               return;
 
-       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
-       return intel_dp->is_mst;
+       intel_dp->is_mst = intel_dp_can_mst(intel_dp);
+
+       if (intel_dp->is_mst)
+               DRM_DEBUG_KMS("Sink is MST capable\n");
+       else
+               DRM_DEBUG_KMS("Sink is not MST capable\n");
+
+       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                       intel_dp->is_mst);
 }
 
 static int intel_dp_sink_crc_stop(struct intel_dp *intel_dp)
@@ -3909,7 +4035,7 @@ static bool
 intel_dp_short_pulse(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
-       u8 sink_irq_vector;
+       u8 sink_irq_vector = 0;
        u8 old_sink_count = intel_dp->sink_count;
        bool ret;
 
@@ -3936,7 +4062,8 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
 
        /* Try to read the source of the interrupt */
        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
-           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
+           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) &&
+           sink_irq_vector != 0) {
                /* Clear interrupt source */
                drm_dp_dpcd_writeb(&intel_dp->aux,
                                   DP_DEVICE_SERVICE_IRQ_VECTOR,
@@ -3980,6 +4107,9 @@ intel_dp_detect_dpcd(struct intel_dp *intel_dp)
                connector_status_connected : connector_status_disconnected;
        }
 
+       if (intel_dp_can_mst(intel_dp))
+               return connector_status_connected;
+
        /* If no HPD, poke DDC gently */
        if (drm_probe_ddc(&intel_dp->aux.ddc))
                return connector_status_connected;
@@ -4148,7 +4278,7 @@ static bool bxt_digital_port_connected(struct drm_i915_private *dev_priv,
  *
  * Return %true if @port is connected, %false otherwise.
  */
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
+static bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
                                         struct intel_digital_port *port)
 {
        if (HAS_PCH_IBX(dev_priv))
@@ -4207,7 +4337,7 @@ intel_dp_unset_edid(struct intel_dp *intel_dp)
        intel_dp->has_audio = false;
 }
 
-static void
+static enum drm_connector_status
 intel_dp_long_pulse(struct intel_connector *intel_connector)
 {
        struct drm_connector *connector = &intel_connector->base;
@@ -4217,8 +4347,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
        struct drm_device *dev = connector->dev;
        enum drm_connector_status status;
        enum intel_display_power_domain power_domain;
-       bool ret;
-       u8 sink_irq_vector;
+       u8 sink_irq_vector = 0;
 
        power_domain = intel_display_port_aux_power_domain(intel_encoder);
        intel_display_power_get(to_i915(dev), power_domain);
@@ -4232,7 +4361,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
        else
                status = connector_status_disconnected;
 
-       if (status != connector_status_connected) {
+       if (status == connector_status_disconnected) {
                intel_dp->compliance_test_active = 0;
                intel_dp->compliance_test_type = 0;
                intel_dp->compliance_test_data = 0;
@@ -4252,10 +4381,20 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
        if (intel_encoder->type != INTEL_OUTPUT_EDP)
                intel_encoder->type = INTEL_OUTPUT_DP;
 
+       DRM_DEBUG_KMS("Display Port TPS3 support: source %s, sink %s\n",
+                     yesno(intel_dp_source_supports_hbr2(intel_dp)),
+                     yesno(drm_dp_tps3_supported(intel_dp->dpcd)));
+
+       intel_dp_print_rates(intel_dp);
+
        intel_dp_probe_oui(intel_dp);
 
-       ret = intel_dp_probe_mst(intel_dp);
-       if (ret) {
+       intel_dp_print_hw_revision(intel_dp);
+       intel_dp_print_sw_revision(intel_dp);
+
+       intel_dp_configure_mst(intel_dp);
+
+       if (intel_dp->is_mst) {
                /*
                 * If we are in MST mode then this connector
                 * won't appear connected or have anything
@@ -4284,13 +4423,14 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
        intel_dp->aux.i2c_defer_count = 0;
 
        intel_dp_set_edid(intel_dp);
-
-       status = connector_status_connected;
+       if (is_edp(intel_dp) || intel_connector->detect_edid)
+               status = connector_status_connected;
        intel_dp->detect_done = true;
 
        /* Try to read the source of the interrupt */
        if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
-           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) {
+           intel_dp_get_sink_irq(intel_dp, &sink_irq_vector) &&
+           sink_irq_vector != 0) {
                /* Clear interrupt source */
                drm_dp_dpcd_writeb(&intel_dp->aux,
                                   DP_DEVICE_SERVICE_IRQ_VECTOR,
@@ -4303,12 +4443,11 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
        }
 
 out:
-       if ((status != connector_status_connected) &&
-           (intel_dp->is_mst == false))
+       if (status != connector_status_connected && !intel_dp->is_mst)
                intel_dp_unset_edid(intel_dp);
 
        intel_display_power_put(to_i915(dev), power_domain);
-       return;
+       return status;
 }
 
 static enum drm_connector_status
@@ -4317,7 +4456,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
        struct intel_dp *intel_dp = intel_attached_dp(connector);
        struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
        struct intel_encoder *intel_encoder = &intel_dig_port->base;
-       struct intel_connector *intel_connector = to_intel_connector(connector);
+       enum drm_connector_status status = connector->status;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                      connector->base.id, connector->name);
@@ -4332,14 +4471,11 @@ intel_dp_detect(struct drm_connector *connector, bool force)
 
        /* If full detect is not performed yet, do a full detect */
        if (!intel_dp->detect_done)
-               intel_dp_long_pulse(intel_dp->attached_connector);
+               status = intel_dp_long_pulse(intel_dp->attached_connector);
 
        intel_dp->detect_done = false;
 
-       if (is_edp(intel_dp) || intel_connector->detect_edid)
-               return connector_status_connected;
-       else
-               return connector_status_disconnected;
+       return status;
 }
 
 static void
@@ -4630,13 +4766,8 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder)
 
        pps_lock(intel_dp);
 
-       /*
-        * Read out the current power sequencer assignment,
-        * in case the BIOS did something with it.
-        */
-       if (IS_VALLEYVIEW(encoder->dev) || IS_CHERRYVIEW(encoder->dev))
-               vlv_initial_power_sequencer_setup(intel_dp);
-
+       /* Reinit the power sequencer, in case BIOS did something with it. */
+       intel_dp_pps_init(encoder->dev, intel_dp);
        intel_edp_panel_vdd_sanitize(intel_dp);
 
        pps_unlock(intel_dp);
@@ -4696,36 +4827,34 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
                      port_name(intel_dig_port->port),
                      long_hpd ? "long" : "short");
 
+       if (long_hpd) {
+               intel_dp->detect_done = false;
+               return IRQ_NONE;
+       }
+
        power_domain = intel_display_port_aux_power_domain(intel_encoder);
        intel_display_power_get(dev_priv, power_domain);
 
-       if (long_hpd) {
-               intel_dp_long_pulse(intel_dp->attached_connector);
-               if (intel_dp->is_mst)
-                       ret = IRQ_HANDLED;
-               goto put_power;
-
-       } else {
-               if (intel_dp->is_mst) {
-                       if (intel_dp_check_mst_status(intel_dp) == -EINVAL) {
-                               /*
-                                * If we were in MST mode, and device is not
-                                * there, get out of MST mode
-                                */
-                               DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n",
-                                             intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
-                               intel_dp->is_mst = false;
-                               drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
-                                                               intel_dp->is_mst);
-                               goto put_power;
-                       }
+       if (intel_dp->is_mst) {
+               if (intel_dp_check_mst_status(intel_dp) == -EINVAL) {
+                       /*
+                        * If we were in MST mode, and device is not
+                        * there, get out of MST mode
+                        */
+                       DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n",
+                                     intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
+                       intel_dp->is_mst = false;
+                       drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr,
+                                                       intel_dp->is_mst);
+                       intel_dp->detect_done = false;
+                       goto put_power;
                }
+       }
 
-               if (!intel_dp->is_mst) {
-                       if (!intel_dp_short_pulse(intel_dp)) {
-                               intel_dp_long_pulse(intel_dp->attached_connector);
-                               goto put_power;
-                       }
+       if (!intel_dp->is_mst) {
+               if (!intel_dp_short_pulse(intel_dp)) {
+                       intel_dp->detect_done = false;
+                       goto put_power;
                }
        }
 
@@ -4984,9 +5113,21 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
                      I915_READ(regs.pp_div));
 }
 
+static void intel_dp_pps_init(struct drm_device *dev,
+                             struct intel_dp *intel_dp)
+{
+       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
+               vlv_initial_power_sequencer_setup(intel_dp);
+       } else {
+               intel_dp_init_panel_power_sequencer(dev, intel_dp);
+               intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
+       }
+}
+
 /**
  * intel_dp_set_drrs_state - program registers for RR switch to take effect
- * @dev: DRM device
+ * @dev_priv: i915 device
+ * @crtc_state: a pointer to the active intel_crtc_state
  * @refresh_rate: RR to be programmed
  *
  * This function gets called when refresh rate (RR) has to be changed from
@@ -4996,14 +5137,14 @@ intel_dp_init_panel_power_sequencer_registers(struct drm_device *dev,
  *
  * The caller of this function needs to take a lock on dev_priv->drrs.
  */
-static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
+static void intel_dp_set_drrs_state(struct drm_i915_private *dev_priv,
+                                   struct intel_crtc_state *crtc_state,
+                                   int refresh_rate)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_encoder *encoder;
        struct intel_digital_port *dig_port = NULL;
        struct intel_dp *intel_dp = dev_priv->drrs.dp;
-       struct intel_crtc_state *config = NULL;
-       struct intel_crtc *intel_crtc = NULL;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc_state->base.crtc);
        enum drrs_refresh_rate_type index = DRRS_HIGH_RR;
 
        if (refresh_rate <= 0) {
@@ -5030,8 +5171,6 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
                return;
        }
 
-       config = intel_crtc->config;
-
        if (dev_priv->drrs.type < SEAMLESS_DRRS_SUPPORT) {
                DRM_DEBUG_KMS("Only Seamless DRRS supported.\n");
                return;
@@ -5047,12 +5186,12 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
                return;
        }
 
-       if (!intel_crtc->active) {
+       if (!crtc_state->base.active) {
                DRM_DEBUG_KMS("eDP encoder disabled. CRTC not Active\n");
                return;
        }
 
-       if (INTEL_INFO(dev)->gen >= 8 && !IS_CHERRYVIEW(dev)) {
+       if (INTEL_GEN(dev_priv) >= 8 && !IS_CHERRYVIEW(dev_priv)) {
                switch (index) {
                case DRRS_HIGH_RR:
                        intel_dp_set_m_n(intel_crtc, M1_N1);
@@ -5064,18 +5203,18 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
                default:
                        DRM_ERROR("Unsupported refreshrate type\n");
                }
-       } else if (INTEL_INFO(dev)->gen > 6) {
-               i915_reg_t reg = PIPECONF(intel_crtc->config->cpu_transcoder);
+       } else if (INTEL_GEN(dev_priv) > 6) {
+               i915_reg_t reg = PIPECONF(crtc_state->cpu_transcoder);
                u32 val;
 
                val = I915_READ(reg);
                if (index > DRRS_HIGH_RR) {
-                       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+                       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                                val |= PIPECONF_EDP_RR_MODE_SWITCH_VLV;
                        else
                                val |= PIPECONF_EDP_RR_MODE_SWITCH;
                } else {
-                       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev))
+                       if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
                                val &= ~PIPECONF_EDP_RR_MODE_SWITCH_VLV;
                        else
                                val &= ~PIPECONF_EDP_RR_MODE_SWITCH;
@@ -5091,18 +5230,17 @@ static void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
 /**
  * intel_edp_drrs_enable - init drrs struct if supported
  * @intel_dp: DP struct
+ * @crtc_state: A pointer to the active crtc state.
  *
  * Initializes frontbuffer_bits and drrs.dp
  */
-void intel_edp_drrs_enable(struct intel_dp *intel_dp)
+void intel_edp_drrs_enable(struct intel_dp *intel_dp,
+                          struct intel_crtc_state *crtc_state)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-       struct drm_crtc *crtc = dig_port->base.base.crtc;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       if (!intel_crtc->config->has_drrs) {
+       if (!crtc_state->has_drrs) {
                DRM_DEBUG_KMS("Panel doesn't support DRRS\n");
                return;
        }
@@ -5124,17 +5262,16 @@ unlock:
 /**
  * intel_edp_drrs_disable - Disable DRRS
  * @intel_dp: DP struct
+ * @old_crtc_state: Pointer to old crtc_state.
  *
  */
-void intel_edp_drrs_disable(struct intel_dp *intel_dp)
+void intel_edp_drrs_disable(struct intel_dp *intel_dp,
+                           struct intel_crtc_state *old_crtc_state)
 {
        struct drm_device *dev = intel_dp_to_dev(intel_dp);
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-       struct drm_crtc *crtc = dig_port->base.base.crtc;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       if (!intel_crtc->config->has_drrs)
+       if (!old_crtc_state->has_drrs)
                return;
 
        mutex_lock(&dev_priv->drrs.mutex);
@@ -5144,9 +5281,8 @@ void intel_edp_drrs_disable(struct intel_dp *intel_dp)
        }
 
        if (dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR)
-               intel_dp_set_drrs_state(&dev_priv->drm,
-                                       intel_dp->attached_connector->panel.
-                                       fixed_mode->vrefresh);
+               intel_dp_set_drrs_state(dev_priv, old_crtc_state,
+                       intel_dp->attached_connector->panel.fixed_mode->vrefresh);
 
        dev_priv->drrs.dp = NULL;
        mutex_unlock(&dev_priv->drrs.mutex);
@@ -5175,10 +5311,12 @@ static void intel_edp_drrs_downclock_work(struct work_struct *work)
        if (dev_priv->drrs.busy_frontbuffer_bits)
                goto unlock;
 
-       if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR)
-               intel_dp_set_drrs_state(&dev_priv->drm,
-                                       intel_dp->attached_connector->panel.
-                                       downclock_mode->vrefresh);
+       if (dev_priv->drrs.refresh_rate_type != DRRS_LOW_RR) {
+               struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc;
+
+               intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config,
+                       intel_dp->attached_connector->panel.downclock_mode->vrefresh);
+       }
 
 unlock:
        mutex_unlock(&dev_priv->drrs.mutex);
@@ -5186,7 +5324,7 @@ unlock:
 
 /**
  * intel_edp_drrs_invalidate - Disable Idleness DRRS
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * This function gets called everytime rendering on the given planes start.
@@ -5194,10 +5332,9 @@ unlock:
  *
  * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits.
  */
-void intel_edp_drrs_invalidate(struct drm_device *dev,
-               unsigned frontbuffer_bits)
+void intel_edp_drrs_invalidate(struct drm_i915_private *dev_priv,
+                              unsigned int frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *crtc;
        enum pipe pipe;
 
@@ -5220,16 +5357,15 @@ void intel_edp_drrs_invalidate(struct drm_device *dev,
 
        /* invalidate means busy screen hence upclock */
        if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR)
-               intel_dp_set_drrs_state(&dev_priv->drm,
-                                       dev_priv->drrs.dp->attached_connector->panel.
-                                       fixed_mode->vrefresh);
+               intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config,
+                       dev_priv->drrs.dp->attached_connector->panel.fixed_mode->vrefresh);
 
        mutex_unlock(&dev_priv->drrs.mutex);
 }
 
 /**
  * intel_edp_drrs_flush - Restart Idleness DRRS
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * This function gets called every time rendering on the given planes has
@@ -5239,10 +5375,9 @@ void intel_edp_drrs_invalidate(struct drm_device *dev,
  *
  * Dirty frontbuffers relevant to DRRS are tracked in busy_frontbuffer_bits.
  */
-void intel_edp_drrs_flush(struct drm_device *dev,
-               unsigned frontbuffer_bits)
+void intel_edp_drrs_flush(struct drm_i915_private *dev_priv,
+                         unsigned int frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *crtc;
        enum pipe pipe;
 
@@ -5265,9 +5400,8 @@ void intel_edp_drrs_flush(struct drm_device *dev,
 
        /* flush means busy screen hence upclock */
        if (frontbuffer_bits && dev_priv->drrs.refresh_rate_type == DRRS_LOW_RR)
-               intel_dp_set_drrs_state(&dev_priv->drm,
-                                       dev_priv->drrs.dp->attached_connector->panel.
-                                       fixed_mode->vrefresh);
+               intel_dp_set_drrs_state(dev_priv, to_intel_crtc(crtc)->config,
+                               dev_priv->drrs.dp->attached_connector->panel.fixed_mode->vrefresh);
 
        /*
         * flush also means no more activity hence schedule downclock, if all
@@ -5400,27 +5534,15 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
        pps_lock(intel_dp);
 
        intel_dp_init_panel_power_timestamps(intel_dp);
-
-       if (IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) {
-               vlv_initial_power_sequencer_setup(intel_dp);
-       } else {
-               intel_dp_init_panel_power_sequencer(dev, intel_dp);
-               intel_dp_init_panel_power_sequencer_registers(dev, intel_dp);
-       }
-
+       intel_dp_pps_init(dev, intel_dp);
        intel_edp_panel_vdd_sanitize(intel_dp);
 
        pps_unlock(intel_dp);
 
        /* Cache DPCD and EDID for edp. */
-       has_dpcd = intel_dp_get_dpcd(intel_dp);
+       has_dpcd = intel_edp_init_dpcd(intel_dp);
 
-       if (has_dpcd) {
-               if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
-                       dev_priv->no_aux_handshake =
-                               intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
-                               DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
-       } else {
+       if (!has_dpcd) {
                /* if this fails, presume the device is a ghost */
                DRM_INFO("failed to retrieve link info, disabling eDP\n");
                goto out_vdd_off;
@@ -5576,7 +5698,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
        connector->interlace_allowed = true;
        connector->doublescan_allowed = 0;
 
-       intel_dp_aux_init(intel_dp, intel_connector);
+       intel_dp_aux_init(intel_dp);
 
        INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
                          edp_panel_vdd_work);
index 60fb39c..c438b02 100644 (file)
 
 #include "intel_drv.h"
 
+static void
+intel_dp_dump_link_status(const uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+
+       DRM_DEBUG_KMS("ln0_1:0x%x ln2_3:0x%x align:0x%x sink:0x%x adj_req0_1:0x%x adj_req2_3:0x%x",
+                     link_status[0], link_status[1], link_status[2],
+                     link_status[3], link_status[4], link_status[5]);
+}
+
 static void
 intel_get_adjust_train(struct intel_dp *intel_dp,
                       const uint8_t link_status[DP_LINK_STATUS_SIZE])
@@ -103,13 +112,24 @@ intel_dp_update_link_train(struct intel_dp *intel_dp)
        return ret == intel_dp->lane_count;
 }
 
+static bool intel_dp_link_max_vswing_reached(struct intel_dp *intel_dp)
+{
+       int lane;
+
+       for (lane = 0; lane < intel_dp->lane_count; lane++)
+               if ((intel_dp->train_set[lane] &
+                    DP_TRAIN_MAX_SWING_REACHED) == 0)
+                       return false;
+
+       return true;
+}
+
 /* Enable corresponding port and start training pattern 1 */
-static void
+static bool
 intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 {
-       int i;
        uint8_t voltage;
-       int voltage_tries, loop_tries;
+       int voltage_tries, max_vswing_tries;
        uint8_t link_config[2];
        uint8_t link_bw, rate_select;
 
@@ -125,6 +145,7 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
        if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
                link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
        drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
+
        if (intel_dp->num_sink_rates)
                drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
                                  &rate_select, 1);
@@ -140,60 +161,54 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
                                       DP_TRAINING_PATTERN_1 |
                                       DP_LINK_SCRAMBLING_DISABLE)) {
                DRM_ERROR("failed to enable link training\n");
-               return;
+               return false;
        }
 
-       voltage = 0xff;
-       voltage_tries = 0;
-       loop_tries = 0;
+       voltage_tries = 1;
+       max_vswing_tries = 0;
        for (;;) {
                uint8_t link_status[DP_LINK_STATUS_SIZE];
 
                drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
+
                if (!intel_dp_get_link_status(intel_dp, link_status)) {
                        DRM_ERROR("failed to get link status\n");
-                       break;
+                       return false;
                }
 
                if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
                        DRM_DEBUG_KMS("clock recovery OK\n");
-                       break;
+                       return true;
                }
 
-               /* Check to see if we've tried the max voltage */
-               for (i = 0; i < intel_dp->lane_count; i++)
-                       if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
-                               break;
-               if (i == intel_dp->lane_count) {
-                       ++loop_tries;
-                       if (loop_tries == 5) {
-                               DRM_ERROR("too many full retries, give up\n");
-                               break;
-                       }
-                       intel_dp_reset_link_train(intel_dp,
-                                                 DP_TRAINING_PATTERN_1 |
-                                                 DP_LINK_SCRAMBLING_DISABLE);
-                       voltage_tries = 0;
-                       continue;
+               if (voltage_tries == 5) {
+                       DRM_DEBUG_KMS("Same voltage tried 5 times\n");
+                       return false;
+               }
+
+               if (max_vswing_tries == 1) {
+                       DRM_DEBUG_KMS("Max Voltage Swing reached\n");
+                       return false;
                }
 
-               /* Check to see if we've tried the same voltage 5 times */
-               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
-                       ++voltage_tries;
-                       if (voltage_tries == 5) {
-                               DRM_ERROR("too many voltage retries, give up\n");
-                               break;
-                       }
-               } else
-                       voltage_tries = 0;
                voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
                /* Update training set as requested by target */
                intel_get_adjust_train(intel_dp, link_status);
                if (!intel_dp_update_link_train(intel_dp)) {
                        DRM_ERROR("failed to update link training\n");
-                       break;
+                       return false;
                }
+
+               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) ==
+                   voltage)
+                       ++voltage_tries;
+               else
+                       voltage_tries = 1;
+
+               if (intel_dp_link_max_vswing_reached(intel_dp))
+                       ++max_vswing_tries;
+
        }
 }
 
@@ -229,12 +244,12 @@ static u32 intel_dp_training_pattern(struct intel_dp *intel_dp)
        return training_pattern;
 }
 
-static void
+static bool
 intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 {
-       bool channel_eq = false;
-       int tries, cr_tries;
+       int tries;
        u32 training_pattern;
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
 
        training_pattern = intel_dp_training_pattern(intel_dp);
 
@@ -243,19 +258,11 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
                                     training_pattern |
                                     DP_LINK_SCRAMBLING_DISABLE)) {
                DRM_ERROR("failed to start channel equalization\n");
-               return;
+               return false;
        }
 
-       tries = 0;
-       cr_tries = 0;
-       channel_eq = false;
-       for (;;) {
-               uint8_t link_status[DP_LINK_STATUS_SIZE];
-
-               if (cr_tries > 5) {
-                       DRM_ERROR("failed to train DP, aborting\n");
-                       break;
-               }
+       intel_dp->channel_eq_status = false;
+       for (tries = 0; tries < 5; tries++) {
 
                drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
                if (!intel_dp_get_link_status(intel_dp, link_status)) {
@@ -266,44 +273,38 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
                /* Make sure clock is still ok */
                if (!drm_dp_clock_recovery_ok(link_status,
                                              intel_dp->lane_count)) {
-                       intel_dp_link_training_clock_recovery(intel_dp);
-                       intel_dp_set_link_train(intel_dp,
-                                               training_pattern |
-                                               DP_LINK_SCRAMBLING_DISABLE);
-                       cr_tries++;
-                       continue;
+                       intel_dp_dump_link_status(link_status);
+                       DRM_DEBUG_KMS("Clock recovery check failed, cannot "
+                                     "continue channel equalization\n");
+                       break;
                }
 
                if (drm_dp_channel_eq_ok(link_status,
                                         intel_dp->lane_count)) {
-                       channel_eq = true;
+                       intel_dp->channel_eq_status = true;
+                       DRM_DEBUG_KMS("Channel EQ done. DP Training "
+                                     "successful\n");
                        break;
                }
 
-               /* Try 5 times, then try clock recovery if that fails */
-               if (tries > 5) {
-                       intel_dp_link_training_clock_recovery(intel_dp);
-                       intel_dp_set_link_train(intel_dp,
-                                               training_pattern |
-                                               DP_LINK_SCRAMBLING_DISABLE);
-                       tries = 0;
-                       cr_tries++;
-                       continue;
-               }
-
                /* Update training set as requested by target */
                intel_get_adjust_train(intel_dp, link_status);
                if (!intel_dp_update_link_train(intel_dp)) {
                        DRM_ERROR("failed to update link training\n");
                        break;
                }
-               ++tries;
+       }
+
+       /* Try 5 times, else fail and try at lower BW */
+       if (tries == 5) {
+               intel_dp_dump_link_status(link_status);
+               DRM_DEBUG_KMS("Channel equalization failed 5 times\n");
        }
 
        intel_dp_set_idle_link_train(intel_dp);
 
-       if (channel_eq)
-               DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
+       return intel_dp->channel_eq_status;
+
 }
 
 void intel_dp_stop_link_train(struct intel_dp *intel_dp)
index 68a005d..54a9d76 100644 (file)
 #include <drm/drm_edid.h>
 
 static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
-                                       struct intel_crtc_state *pipe_config)
+                                       struct intel_crtc_state *pipe_config,
+                                       struct drm_connector_state *conn_state)
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
        struct intel_dp *intel_dp = &intel_dig_port->dp;
        struct drm_atomic_state *state;
-       int bpp, i;
+       int bpp;
        int lane_count, slots;
        const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
-       struct drm_connector *drm_connector;
-       struct intel_connector *connector, *found = NULL;
-       struct drm_connector_state *connector_state;
        int mst_pbn;
 
        pipe_config->dp_encoder_is_mst = true;
@@ -54,7 +52,6 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
         */
        lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
 
-
        pipe_config->lane_count = lane_count;
 
        pipe_config->pipe_bpp = 24;
@@ -62,20 +59,6 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
 
        state = pipe_config->base.state;
 
-       for_each_connector_in_state(state, drm_connector, connector_state, i) {
-               connector = to_intel_connector(drm_connector);
-
-               if (connector_state->best_encoder == &encoder->base) {
-                       found = connector;
-                       break;
-               }
-       }
-
-       if (!found) {
-               DRM_ERROR("can't find connector\n");
-               return false;
-       }
-
        mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock, bpp);
 
        pipe_config->pbn = mst_pbn;
@@ -92,16 +75,20 @@ static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
 
 }
 
-static void intel_mst_disable_dp(struct intel_encoder *encoder)
+static void intel_mst_disable_dp(struct intel_encoder *encoder,
+                                struct intel_crtc_state *old_crtc_state,
+                                struct drm_connector_state *old_conn_state)
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
        struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct intel_connector *connector =
+               to_intel_connector(old_conn_state->connector);
        int ret;
 
        DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
 
-       drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->connector->port);
+       drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
 
        ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
        if (ret) {
@@ -109,11 +96,15 @@ static void intel_mst_disable_dp(struct intel_encoder *encoder)
        }
 }
 
-static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
+static void intel_mst_post_disable_dp(struct intel_encoder *encoder,
+                                     struct intel_crtc_state *old_crtc_state,
+                                     struct drm_connector_state *old_conn_state)
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
        struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct intel_connector *connector =
+               to_intel_connector(old_conn_state->connector);
 
        DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
 
@@ -122,59 +113,51 @@ static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
        /* and this can also fail */
        drm_dp_update_payload_part2(&intel_dp->mst_mgr);
 
-       drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->connector->port);
+       drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port);
 
        intel_dp->active_mst_links--;
 
        intel_mst->connector = NULL;
        if (intel_dp->active_mst_links == 0) {
-               intel_dig_port->base.post_disable(&intel_dig_port->base);
+               intel_dig_port->base.post_disable(&intel_dig_port->base,
+                                                 NULL, NULL);
+
                intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
        }
 }
 
-static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
+static void intel_mst_pre_enable_dp(struct intel_encoder *encoder,
+                                   struct intel_crtc_state *pipe_config,
+                                   struct drm_connector_state *conn_state)
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
        struct intel_dp *intel_dp = &intel_dig_port->dp;
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum port port = intel_dig_port->port;
+       struct intel_connector *connector =
+               to_intel_connector(conn_state->connector);
        int ret;
        uint32_t temp;
-       struct intel_connector *found = NULL, *connector;
        int slots;
-       struct drm_crtc *crtc = encoder->base.crtc;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-       for_each_intel_connector(dev, connector) {
-               if (connector->base.state->best_encoder == &encoder->base) {
-                       found = connector;
-                       break;
-               }
-       }
-
-       if (!found) {
-               DRM_ERROR("can't find connector\n");
-               return;
-       }
 
        /* MST encoders are bound to a crtc, not to a connector,
         * force the mapping here for get_hw_state.
         */
-       found->encoder = encoder;
+       connector->encoder = encoder;
+       intel_mst->connector = connector;
 
        DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
 
-       intel_mst->connector = found;
-
        if (intel_dp->active_mst_links == 0) {
-               intel_prepare_ddi_buffer(&intel_dig_port->base);
-
-               intel_ddi_clk_select(&intel_dig_port->base, intel_crtc->config);
+               intel_ddi_clk_select(&intel_dig_port->base,
+                                    pipe_config->shared_dpll);
 
-               intel_dp_set_link_params(intel_dp, intel_crtc->config);
+               intel_prepare_dp_ddi_buffers(&intel_dig_port->base);
+               intel_dp_set_link_params(intel_dp,
+                                        pipe_config->port_clock,
+                                        pipe_config->lane_count,
+                                        true);
 
                intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
 
@@ -185,8 +168,8 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
        }
 
        ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
-                                      intel_mst->connector->port,
-                                      intel_crtc->config->pbn, &slots);
+                                      connector->port,
+                                      pipe_config->pbn, &slots);
        if (ret == false) {
                DRM_ERROR("failed to allocate vcpi\n");
                return;
@@ -200,13 +183,14 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
        ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
 }
 
-static void intel_mst_enable_dp(struct intel_encoder *encoder)
+static void intel_mst_enable_dp(struct intel_encoder *encoder,
+                               struct intel_crtc_state *pipe_config,
+                               struct drm_connector_state *conn_state)
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
        struct intel_dp *intel_dp = &intel_dig_port->dp;
-       struct drm_device *dev = intel_dig_port->base.base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum port port = intel_dig_port->port;
        int ret;
 
@@ -239,9 +223,8 @@ static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
 {
        struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
        struct intel_digital_port *intel_dig_port = intel_mst->primary;
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        enum transcoder cpu_transcoder = pipe_config->cpu_transcoder;
        u32 temp, flags = 0;
 
index 5c1f2d2..1c59ca5 100644 (file)
 
 #include "intel_drv.h"
 
+struct intel_shared_dpll *
+skl_find_link_pll(struct drm_i915_private *dev_priv, int clock)
+{
+       struct intel_shared_dpll *pll = NULL;
+       struct intel_dpll_hw_state dpll_hw_state;
+       enum intel_dpll_id i;
+       bool found = false;
+
+       if (!skl_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state))
+               return pll;
+
+       for (i = DPLL_ID_SKL_DPLL1; i <= DPLL_ID_SKL_DPLL3; i++) {
+               pll = &dev_priv->shared_dplls[i];
+
+               /* Only want to check enabled timings first */
+               if (pll->config.crtc_mask == 0)
+                       continue;
+
+               if (memcmp(&dpll_hw_state, &pll->config.hw_state,
+                          sizeof(pll->config.hw_state)) == 0) {
+                       found = true;
+                       break;
+               }
+       }
+
+       /* Ok no matching timings, maybe there's a free one? */
+       for (i = DPLL_ID_SKL_DPLL1;
+            ((found == false) && (i <= DPLL_ID_SKL_DPLL3)); i++) {
+               pll = &dev_priv->shared_dplls[i];
+               if (pll->config.crtc_mask == 0) {
+                       pll->config.hw_state = dpll_hw_state;
+                       break;
+               }
+       }
+
+       return pll;
+}
+
 struct intel_shared_dpll *
 intel_get_shared_dpll_by_id(struct drm_i915_private *dev_priv,
                            enum intel_dpll_id id)
@@ -452,26 +490,6 @@ static bool hsw_ddi_spll_get_hw_state(struct drm_i915_private *dev_priv,
        return val & SPLL_PLL_ENABLE;
 }
 
-static uint32_t hsw_pll_to_ddi_pll_sel(struct intel_shared_dpll *pll)
-{
-       switch (pll->id) {
-       case DPLL_ID_WRPLL1:
-               return PORT_CLK_SEL_WRPLL1;
-       case DPLL_ID_WRPLL2:
-               return PORT_CLK_SEL_WRPLL2;
-       case DPLL_ID_SPLL:
-               return PORT_CLK_SEL_SPLL;
-       case DPLL_ID_LCPLL_810:
-               return PORT_CLK_SEL_LCPLL_810;
-       case DPLL_ID_LCPLL_1350:
-               return PORT_CLK_SEL_LCPLL_1350;
-       case DPLL_ID_LCPLL_2700:
-               return PORT_CLK_SEL_LCPLL_2700;
-       default:
-               return PORT_CLK_SEL_NONE;
-       }
-}
-
 #define LC_FREQ 2700
 #define LC_FREQ_2K U64_C(LC_FREQ * 2000)
 
@@ -687,11 +705,65 @@ hsw_ddi_calculate_wrpll(int clock /* in Hz */,
        *r2_out = best.r2;
 }
 
+static struct intel_shared_dpll *hsw_ddi_hdmi_get_dpll(int clock,
+                                                      struct intel_crtc *crtc,
+                                                      struct intel_crtc_state *crtc_state)
+{
+       struct intel_shared_dpll *pll;
+       uint32_t val;
+       unsigned int p, n2, r2;
+
+       hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
+
+       val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL |
+             WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
+             WRPLL_DIVIDER_POST(p);
+
+       crtc_state->dpll_hw_state.wrpll = val;
+
+       pll = intel_find_shared_dpll(crtc, crtc_state,
+                                    DPLL_ID_WRPLL1, DPLL_ID_WRPLL2);
+
+       if (!pll)
+               return NULL;
+
+       return pll;
+}
+
+struct intel_shared_dpll *hsw_ddi_dp_get_dpll(struct intel_encoder *encoder,
+                                             int clock)
+{
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_shared_dpll *pll;
+       enum intel_dpll_id pll_id;
+
+       switch (clock / 2) {
+       case 81000:
+               pll_id = DPLL_ID_LCPLL_810;
+               break;
+       case 135000:
+               pll_id = DPLL_ID_LCPLL_1350;
+               break;
+       case 270000:
+               pll_id = DPLL_ID_LCPLL_2700;
+               break;
+       default:
+               DRM_DEBUG_KMS("Invalid clock for DP: %d\n", clock);
+               return NULL;
+       }
+
+       pll = intel_get_shared_dpll_by_id(dev_priv, pll_id);
+
+       if (!pll)
+               return NULL;
+
+       return pll;
+}
+
 static struct intel_shared_dpll *
 hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
             struct intel_encoder *encoder)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
        struct intel_shared_dpll *pll;
        int clock = crtc_state->port_clock;
 
@@ -699,41 +771,12 @@ hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
               sizeof(crtc_state->dpll_hw_state));
 
        if (encoder->type == INTEL_OUTPUT_HDMI) {
-               uint32_t val;
-               unsigned p, n2, r2;
-
-               hsw_ddi_calculate_wrpll(clock * 1000, &r2, &n2, &p);
-
-               val = WRPLL_PLL_ENABLE | WRPLL_PLL_LCPLL |
-                     WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
-                     WRPLL_DIVIDER_POST(p);
-
-               crtc_state->dpll_hw_state.wrpll = val;
-
-               pll = intel_find_shared_dpll(crtc, crtc_state,
-                                            DPLL_ID_WRPLL1, DPLL_ID_WRPLL2);
+               pll = hsw_ddi_hdmi_get_dpll(clock, crtc, crtc_state);
 
        } else if (encoder->type == INTEL_OUTPUT_DP ||
                   encoder->type == INTEL_OUTPUT_DP_MST ||
                   encoder->type == INTEL_OUTPUT_EDP) {
-               enum intel_dpll_id pll_id;
-
-               switch (clock / 2) {
-               case 81000:
-                       pll_id = DPLL_ID_LCPLL_810;
-                       break;
-               case 135000:
-                       pll_id = DPLL_ID_LCPLL_1350;
-                       break;
-               case 270000:
-                       pll_id = DPLL_ID_LCPLL_2700;
-                       break;
-               default:
-                       DRM_DEBUG_KMS("Invalid clock for DP: %d\n", clock);
-                       return NULL;
-               }
-
-               pll = intel_get_shared_dpll_by_id(dev_priv, pll_id);
+               pll = hsw_ddi_dp_get_dpll(encoder, clock);
 
        } else if (encoder->type == INTEL_OUTPUT_ANALOG) {
                if (WARN_ON(crtc_state->port_clock / 2 != 135000))
@@ -751,14 +794,11 @@ hsw_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
        if (!pll)
                return NULL;
 
-       crtc_state->ddi_pll_sel = hsw_pll_to_ddi_pll_sel(pll);
-
        intel_reference_shared_dpll(pll, crtc_state);
 
        return pll;
 }
 
-
 static const struct intel_shared_dpll_funcs hsw_ddi_wrpll_funcs = {
        .enable = hsw_ddi_wrpll_enable,
        .disable = hsw_ddi_wrpll_disable,
@@ -1194,75 +1234,110 @@ skip_remaining_dividers:
        return true;
 }
 
-static struct intel_shared_dpll *
-skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
-            struct intel_encoder *encoder)
+static bool skl_ddi_hdmi_pll_dividers(struct intel_crtc *crtc,
+                                     struct intel_crtc_state *crtc_state,
+                                     int clock)
 {
-       struct intel_shared_dpll *pll;
        uint32_t ctrl1, cfgcr1, cfgcr2;
-       int clock = crtc_state->port_clock;
+       struct skl_wrpll_params wrpll_params = { 0, };
 
        /*
         * See comment in intel_dpll_hw_state to understand why we always use 0
         * as the DPLL id in this function.
         */
-
        ctrl1 = DPLL_CTRL1_OVERRIDE(0);
 
-       if (encoder->type == INTEL_OUTPUT_HDMI) {
-               struct skl_wrpll_params wrpll_params = { 0, };
+       ctrl1 |= DPLL_CTRL1_HDMI_MODE(0);
+
+       if (!skl_ddi_calculate_wrpll(clock * 1000, &wrpll_params))
+               return false;
 
-               ctrl1 |= DPLL_CTRL1_HDMI_MODE(0);
+       cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE |
+               DPLL_CFGCR1_DCO_FRACTION(wrpll_params.dco_fraction) |
+               wrpll_params.dco_integer;
 
-               if (!skl_ddi_calculate_wrpll(clock * 1000, &wrpll_params))
-                       return NULL;
+       cfgcr2 = DPLL_CFGCR2_QDIV_RATIO(wrpll_params.qdiv_ratio) |
+               DPLL_CFGCR2_QDIV_MODE(wrpll_params.qdiv_mode) |
+               DPLL_CFGCR2_KDIV(wrpll_params.kdiv) |
+               DPLL_CFGCR2_PDIV(wrpll_params.pdiv) |
+               wrpll_params.central_freq;
+
+       memset(&crtc_state->dpll_hw_state, 0,
+              sizeof(crtc_state->dpll_hw_state));
+
+       crtc_state->dpll_hw_state.ctrl1 = ctrl1;
+       crtc_state->dpll_hw_state.cfgcr1 = cfgcr1;
+       crtc_state->dpll_hw_state.cfgcr2 = cfgcr2;
+       return true;
+}
+
+
+bool skl_ddi_dp_set_dpll_hw_state(int clock,
+                                 struct intel_dpll_hw_state *dpll_hw_state)
+{
+       uint32_t ctrl1;
+
+       /*
+        * See comment in intel_dpll_hw_state to understand why we always use 0
+        * as the DPLL id in this function.
+        */
+       ctrl1 = DPLL_CTRL1_OVERRIDE(0);
+       switch (clock / 2) {
+       case 81000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0);
+               break;
+       case 135000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0);
+               break;
+       case 270000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0);
+               break;
+               /* eDP 1.4 rates */
+       case 162000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1620, 0);
+               break;
+       case 108000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0);
+               break;
+       case 216000:
+               ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2160, 0);
+               break;
+       }
+
+       dpll_hw_state->ctrl1 = ctrl1;
+       return true;
+}
+
+static struct intel_shared_dpll *
+skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
+            struct intel_encoder *encoder)
+{
+       struct intel_shared_dpll *pll;
+       int clock = crtc_state->port_clock;
+       bool bret;
+       struct intel_dpll_hw_state dpll_hw_state;
 
-               cfgcr1 = DPLL_CFGCR1_FREQ_ENABLE |
-                        DPLL_CFGCR1_DCO_FRACTION(wrpll_params.dco_fraction) |
-                        wrpll_params.dco_integer;
+       memset(&dpll_hw_state, 0, sizeof(dpll_hw_state));
 
-               cfgcr2 = DPLL_CFGCR2_QDIV_RATIO(wrpll_params.qdiv_ratio) |
-                        DPLL_CFGCR2_QDIV_MODE(wrpll_params.qdiv_mode) |
-                        DPLL_CFGCR2_KDIV(wrpll_params.kdiv) |
-                        DPLL_CFGCR2_PDIV(wrpll_params.pdiv) |
-                        wrpll_params.central_freq;
+       if (encoder->type == INTEL_OUTPUT_HDMI) {
+               bret = skl_ddi_hdmi_pll_dividers(crtc, crtc_state, clock);
+               if (!bret) {
+                       DRM_DEBUG_KMS("Could not get HDMI pll dividers.\n");
+                       return NULL;
+               }
        } else if (encoder->type == INTEL_OUTPUT_DP ||
                   encoder->type == INTEL_OUTPUT_DP_MST ||
                   encoder->type == INTEL_OUTPUT_EDP) {
-               switch (crtc_state->port_clock / 2) {
-               case 81000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_810, 0);
-                       break;
-               case 135000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1350, 0);
-                       break;
-               case 270000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2700, 0);
-                       break;
-               /* eDP 1.4 rates */
-               case 162000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1620, 0);
-                       break;
-               case 108000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_1080, 0);
-                       break;
-               case 216000:
-                       ctrl1 |= DPLL_CTRL1_LINK_RATE(DPLL_CTRL1_LINK_RATE_2160, 0);
-                       break;
+               bret = skl_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state);
+               if (!bret) {
+                       DRM_DEBUG_KMS("Could not set DP dpll HW state.\n");
+                       return NULL;
                }
-
-               cfgcr1 = cfgcr2 = 0;
+               crtc_state->dpll_hw_state = dpll_hw_state;
        } else {
                return NULL;
        }
 
-       memset(&crtc_state->dpll_hw_state, 0,
-              sizeof(crtc_state->dpll_hw_state));
-
-       crtc_state->dpll_hw_state.ctrl1 = ctrl1;
-       crtc_state->dpll_hw_state.cfgcr1 = cfgcr1;
-       crtc_state->dpll_hw_state.cfgcr2 = cfgcr2;
-
        if (encoder->type == INTEL_OUTPUT_EDP)
                pll = intel_find_shared_dpll(crtc, crtc_state,
                                             DPLL_ID_SKL_DPLL0,
@@ -1274,8 +1349,6 @@ skl_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
        if (!pll)
                return NULL;
 
-       crtc_state->ddi_pll_sel = pll->id;
-
        intel_reference_shared_dpll(pll, crtc_state);
 
        return pll;
@@ -1484,6 +1557,8 @@ struct bxt_clk_div {
        uint32_t m2_frac;
        bool m2_frac_en;
        uint32_t n;
+
+       int vco;
 };
 
 /* pre-calculated values for DP linkrates */
@@ -1497,57 +1572,60 @@ static const struct bxt_clk_div bxt_dp_clk_val[] = {
        {432000, 3, 1, 32, 1677722, 1, 1}
 };
 
-static struct intel_shared_dpll *
-bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
-            struct intel_encoder *encoder)
+static bool
+bxt_ddi_hdmi_pll_dividers(struct intel_crtc *intel_crtc,
+                         struct intel_crtc_state *crtc_state, int clock,
+                         struct bxt_clk_div *clk_div)
 {
-       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
-       struct intel_shared_dpll *pll;
-       enum intel_dpll_id i;
-       struct intel_digital_port *intel_dig_port;
-       struct bxt_clk_div clk_div = {0};
-       int vco = 0;
-       uint32_t prop_coef, int_coef, gain_ctl, targ_cnt;
-       uint32_t lanestagger;
-       int clock = crtc_state->port_clock;
+       struct dpll best_clock;
 
-       if (encoder->type == INTEL_OUTPUT_HDMI) {
-               struct dpll best_clock;
+       /* Calculate HDMI div */
+       /*
+        * FIXME: tie the following calculation into
+        * i9xx_crtc_compute_clock
+        */
+       if (!bxt_find_best_dpll(crtc_state, clock, &best_clock)) {
+               DRM_DEBUG_DRIVER("no PLL dividers found for clock %d pipe %c\n",
+                                clock, pipe_name(intel_crtc->pipe));
+               return false;
+       }
 
-               /* Calculate HDMI div */
-               /*
-                * FIXME: tie the following calculation into
-                * i9xx_crtc_compute_clock
-                */
-               if (!bxt_find_best_dpll(crtc_state, clock, &best_clock)) {
-                       DRM_DEBUG_DRIVER("no PLL dividers found for clock %d pipe %c\n",
-                                        clock, pipe_name(crtc->pipe));
-                       return NULL;
-               }
+       clk_div->p1 = best_clock.p1;
+       clk_div->p2 = best_clock.p2;
+       WARN_ON(best_clock.m1 != 2);
+       clk_div->n = best_clock.n;
+       clk_div->m2_int = best_clock.m2 >> 22;
+       clk_div->m2_frac = best_clock.m2 & ((1 << 22) - 1);
+       clk_div->m2_frac_en = clk_div->m2_frac != 0;
 
-               clk_div.p1 = best_clock.p1;
-               clk_div.p2 = best_clock.p2;
-               WARN_ON(best_clock.m1 != 2);
-               clk_div.n = best_clock.n;
-               clk_div.m2_int = best_clock.m2 >> 22;
-               clk_div.m2_frac = best_clock.m2 & ((1 << 22) - 1);
-               clk_div.m2_frac_en = clk_div.m2_frac != 0;
+       clk_div->vco = best_clock.vco;
 
-               vco = best_clock.vco;
-       } else if (encoder->type == INTEL_OUTPUT_DP ||
-                  encoder->type == INTEL_OUTPUT_EDP) {
-               int i;
+       return true;
+}
 
-               clk_div = bxt_dp_clk_val[0];
-               for (i = 0; i < ARRAY_SIZE(bxt_dp_clk_val); ++i) {
-                       if (bxt_dp_clk_val[i].clock == clock) {
-                               clk_div = bxt_dp_clk_val[i];
-                               break;
-                       }
+static void bxt_ddi_dp_pll_dividers(int clock, struct bxt_clk_div *clk_div)
+{
+       int i;
+
+       *clk_div = bxt_dp_clk_val[0];
+       for (i = 0; i < ARRAY_SIZE(bxt_dp_clk_val); ++i) {
+               if (bxt_dp_clk_val[i].clock == clock) {
+                       *clk_div = bxt_dp_clk_val[i];
+                       break;
                }
-               vco = clock * 10 / 2 * clk_div.p1 * clk_div.p2;
        }
 
+       clk_div->vco = clock * 10 / 2 * clk_div->p1 * clk_div->p2;
+}
+
+static bool bxt_ddi_set_dpll_hw_state(int clock,
+                         struct bxt_clk_div *clk_div,
+                         struct intel_dpll_hw_state *dpll_hw_state)
+{
+       int vco = clk_div->vco;
+       uint32_t prop_coef, int_coef, gain_ctl, targ_cnt;
+       uint32_t lanestagger;
+
        if (vco >= 6200000 && vco <= 6700000) {
                prop_coef = 4;
                int_coef = 9;
@@ -1566,12 +1644,9 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
                targ_cnt = 9;
        } else {
                DRM_ERROR("Invalid VCO\n");
-               return NULL;
+               return false;
        }
 
-       memset(&crtc_state->dpll_hw_state, 0,
-              sizeof(crtc_state->dpll_hw_state));
-
        if (clock > 270000)
                lanestagger = 0x18;
        else if (clock > 135000)
@@ -1583,35 +1658,86 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
        else
                lanestagger = 0x02;
 
-       crtc_state->dpll_hw_state.ebb0 =
-               PORT_PLL_P1(clk_div.p1) | PORT_PLL_P2(clk_div.p2);
-       crtc_state->dpll_hw_state.pll0 = clk_div.m2_int;
-       crtc_state->dpll_hw_state.pll1 = PORT_PLL_N(clk_div.n);
-       crtc_state->dpll_hw_state.pll2 = clk_div.m2_frac;
+       dpll_hw_state->ebb0 = PORT_PLL_P1(clk_div->p1) | PORT_PLL_P2(clk_div->p2);
+       dpll_hw_state->pll0 = clk_div->m2_int;
+       dpll_hw_state->pll1 = PORT_PLL_N(clk_div->n);
+       dpll_hw_state->pll2 = clk_div->m2_frac;
 
-       if (clk_div.m2_frac_en)
-               crtc_state->dpll_hw_state.pll3 =
-                       PORT_PLL_M2_FRAC_ENABLE;
+       if (clk_div->m2_frac_en)
+               dpll_hw_state->pll3 = PORT_PLL_M2_FRAC_ENABLE;
 
-       crtc_state->dpll_hw_state.pll6 =
-               prop_coef | PORT_PLL_INT_COEFF(int_coef);
-       crtc_state->dpll_hw_state.pll6 |=
-               PORT_PLL_GAIN_CTL(gain_ctl);
+       dpll_hw_state->pll6 = prop_coef | PORT_PLL_INT_COEFF(int_coef);
+       dpll_hw_state->pll6 |= PORT_PLL_GAIN_CTL(gain_ctl);
 
-       crtc_state->dpll_hw_state.pll8 = targ_cnt;
+       dpll_hw_state->pll8 = targ_cnt;
 
-       crtc_state->dpll_hw_state.pll9 = 5 << PORT_PLL_LOCK_THRESHOLD_SHIFT;
+       dpll_hw_state->pll9 = 5 << PORT_PLL_LOCK_THRESHOLD_SHIFT;
 
-       crtc_state->dpll_hw_state.pll10 =
+       dpll_hw_state->pll10 =
                PORT_PLL_DCO_AMP(PORT_PLL_DCO_AMP_DEFAULT)
                | PORT_PLL_DCO_AMP_OVR_EN_H;
 
-       crtc_state->dpll_hw_state.ebb4 = PORT_PLL_10BIT_CLK_ENABLE;
+       dpll_hw_state->ebb4 = PORT_PLL_10BIT_CLK_ENABLE;
+
+       dpll_hw_state->pcsdw12 = LANESTAGGER_STRAP_OVRD | lanestagger;
 
-       crtc_state->dpll_hw_state.pcsdw12 =
-               LANESTAGGER_STRAP_OVRD | lanestagger;
+       return true;
+}
 
-       intel_dig_port = enc_to_dig_port(&encoder->base);
+bool bxt_ddi_dp_set_dpll_hw_state(int clock,
+                         struct intel_dpll_hw_state *dpll_hw_state)
+{
+       struct bxt_clk_div clk_div = {0};
+
+       bxt_ddi_dp_pll_dividers(clock, &clk_div);
+
+       return bxt_ddi_set_dpll_hw_state(clock, &clk_div, dpll_hw_state);
+}
+
+static bool
+bxt_ddi_hdmi_set_dpll_hw_state(struct intel_crtc *intel_crtc,
+                              struct intel_crtc_state *crtc_state, int clock,
+                              struct intel_dpll_hw_state *dpll_hw_state)
+{
+       struct bxt_clk_div clk_div = { };
+
+       bxt_ddi_hdmi_pll_dividers(intel_crtc, crtc_state, clock, &clk_div);
+
+       return bxt_ddi_set_dpll_hw_state(clock, &clk_div, dpll_hw_state);
+}
+
+static struct intel_shared_dpll *
+bxt_get_dpll(struct intel_crtc *crtc,
+               struct intel_crtc_state *crtc_state,
+               struct intel_encoder *encoder)
+{
+       struct intel_dpll_hw_state dpll_hw_state = { };
+       struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+       struct intel_digital_port *intel_dig_port;
+       struct intel_shared_dpll *pll;
+       int i, clock = crtc_state->port_clock;
+
+       if (encoder->type == INTEL_OUTPUT_HDMI &&
+           !bxt_ddi_hdmi_set_dpll_hw_state(crtc, crtc_state, clock,
+                                           &dpll_hw_state))
+               return NULL;
+
+       if ((encoder->type == INTEL_OUTPUT_DP ||
+            encoder->type == INTEL_OUTPUT_EDP) &&
+           !bxt_ddi_dp_set_dpll_hw_state(clock, &dpll_hw_state))
+               return NULL;
+
+       memset(&crtc_state->dpll_hw_state, 0,
+              sizeof(crtc_state->dpll_hw_state));
+
+       crtc_state->dpll_hw_state = dpll_hw_state;
+
+       if (encoder->type == INTEL_OUTPUT_DP_MST) {
+               struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
+
+               intel_dig_port = intel_mst->primary;
+       } else
+               intel_dig_port = enc_to_dig_port(&encoder->base);
 
        /* 1:1 mapping between ports and PLLs */
        i = (enum intel_dpll_id) intel_dig_port->port;
@@ -1622,9 +1748,6 @@ bxt_get_dpll(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state,
 
        intel_reference_shared_dpll(pll, crtc_state);
 
-       /* shared DPLL id 0 is DPLL A */
-       crtc_state->ddi_pll_sel = pll->id;
-
        return pll;
 }
 
index 89c5ada..f438535 100644 (file)
@@ -160,5 +160,20 @@ void intel_disable_shared_dpll(struct intel_crtc *crtc);
 void intel_shared_dpll_commit(struct drm_atomic_state *state);
 void intel_shared_dpll_init(struct drm_device *dev);
 
+/* BXT dpll related functions */
+bool bxt_ddi_dp_set_dpll_hw_state(int clock,
+                         struct intel_dpll_hw_state *dpll_hw_state);
+
+
+/* SKL dpll related functions */
+bool skl_ddi_dp_set_dpll_hw_state(int clock,
+                                 struct intel_dpll_hw_state *dpll_hw_state);
+struct intel_shared_dpll *skl_find_link_pll(struct drm_i915_private *dev_priv,
+                                           int clock);
+
+
+/* HSW dpll related functions */
+struct intel_shared_dpll *hsw_ddi_dp_get_dpll(struct intel_encoder *encoder,
+                                             int clock);
 
 #endif /* _INTEL_DPLL_MGR_H_ */
index ff399b9..a19ec06 100644 (file)
  */
 #define _wait_for(COND, US, W) ({ \
        unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1;   \
-       int ret__ = 0;                                                  \
-       while (!(COND)) {                                               \
-               if (time_after(jiffies, timeout__)) {                   \
-                       if (!(COND))                                    \
-                               ret__ = -ETIMEDOUT;                     \
+       int ret__;                                                      \
+       for (;;) {                                                      \
+               bool expired__ = time_after(jiffies, timeout__);        \
+               if (COND) {                                             \
+                       ret__ = 0;                                      \
+                       break;                                          \
+               }                                                       \
+               if (expired__) {                                        \
+                       ret__ = -ETIMEDOUT;                             \
                        break;                                          \
                }                                                       \
                if ((W) && drm_can_sleep()) {                           \
@@ -178,11 +182,22 @@ struct intel_framebuffer {
        struct drm_framebuffer base;
        struct drm_i915_gem_object *obj;
        struct intel_rotation_info rot_info;
+
+       /* for each plane in the normal GTT view */
+       struct {
+               unsigned int x, y;
+       } normal[2];
+       /* for each plane in the rotated GTT view */
+       struct {
+               unsigned int x, y;
+               unsigned int pitch; /* pixels */
+       } rotated[2];
 };
 
 struct intel_fbdev {
        struct drm_fb_helper helper;
        struct intel_framebuffer *fb;
+       struct i915_vma *vma;
        async_cookie_t cookie;
        int preferred_bpp;
 };
@@ -194,14 +209,26 @@ struct intel_encoder {
        unsigned int cloneable;
        void (*hot_plug)(struct intel_encoder *);
        bool (*compute_config)(struct intel_encoder *,
-                              struct intel_crtc_state *);
-       void (*pre_pll_enable)(struct intel_encoder *);
-       void (*pre_enable)(struct intel_encoder *);
-       void (*enable)(struct intel_encoder *);
-       void (*mode_set)(struct intel_encoder *intel_encoder);
-       void (*disable)(struct intel_encoder *);
-       void (*post_disable)(struct intel_encoder *);
-       void (*post_pll_disable)(struct intel_encoder *);
+                              struct intel_crtc_state *,
+                              struct drm_connector_state *);
+       void (*pre_pll_enable)(struct intel_encoder *,
+                              struct intel_crtc_state *,
+                              struct drm_connector_state *);
+       void (*pre_enable)(struct intel_encoder *,
+                          struct intel_crtc_state *,
+                          struct drm_connector_state *);
+       void (*enable)(struct intel_encoder *,
+                      struct intel_crtc_state *,
+                      struct drm_connector_state *);
+       void (*disable)(struct intel_encoder *,
+                       struct intel_crtc_state *,
+                       struct drm_connector_state *);
+       void (*post_disable)(struct intel_encoder *,
+                            struct intel_crtc_state *,
+                            struct drm_connector_state *);
+       void (*post_pll_disable)(struct intel_encoder *,
+                                struct intel_crtc_state *,
+                                struct drm_connector_state *);
        /* Read out the current hw state of this connector, returning true if
         * the encoder is active. If the encoder is enabled it also set the pipe
         * it is connected to in the pipe parameter. */
@@ -236,6 +263,7 @@ struct intel_panel {
                bool enabled;
                bool combination_mode;  /* gen 2/4 only */
                bool active_low_pwm;
+               bool alternate_pwm_increment;   /* lpt+ */
 
                /* PWM chip */
                bool util_pin_active_low;       /* bxt+ */
@@ -338,10 +366,16 @@ struct intel_atomic_state {
 
 struct intel_plane_state {
        struct drm_plane_state base;
-       struct drm_rect src;
-       struct drm_rect dst;
        struct drm_rect clip;
-       bool visible;
+
+       struct {
+               u32 offset;
+               int x, y;
+       } main;
+       struct {
+               u32 offset;
+               int x, y;
+       } aux;
 
        /*
         * scaler_id
@@ -561,12 +595,6 @@ struct intel_crtc_state {
        /* Selected dpll when shared or NULL. */
        struct intel_shared_dpll *shared_dpll;
 
-       /*
-        * - PORT_CLK_SEL for DDI ports on HSW/BDW.
-        * - enum skl_dpll on SKL
-        */
-       uint32_t ddi_pll_sel;
-
        /* Actual register state of the dpll, for shared dpll cross-checking. */
        struct intel_dpll_hw_state dpll_hw_state;
 
@@ -683,8 +711,8 @@ struct intel_crtc {
 
        struct intel_crtc_state *config;
 
-       /* reset counter value when the last flip was submitted */
-       unsigned int reset_counter;
+       /* global reset count when the last flip was submitted */
+       unsigned int reset_count;
 
        /* Access to these should be protected by dev_priv->irq_lock. */
        bool cpu_fifo_underrun_disabled;
@@ -852,8 +880,10 @@ struct intel_dp {
        int link_rate;
        uint8_t lane_count;
        uint8_t sink_count;
+       bool link_mst;
        bool has_audio;
        bool detect_done;
+       bool channel_eq_status;
        enum hdmi_force_audio force_audio;
        bool limited_color_range;
        bool color_range_auto;
@@ -1106,8 +1136,11 @@ void intel_crt_reset(struct drm_encoder *encoder);
 
 /* intel_ddi.c */
 void intel_ddi_clk_select(struct intel_encoder *encoder,
-                         const struct intel_crtc_state *pipe_config);
-void intel_prepare_ddi_buffer(struct intel_encoder *encoder);
+                         struct intel_shared_dpll *pll);
+void intel_ddi_fdi_post_disable(struct intel_encoder *intel_encoder,
+                               struct intel_crtc_state *old_crtc_state,
+                               struct drm_connector_state *old_conn_state);
+void intel_prepare_dp_ddi_buffers(struct intel_encoder *encoder);
 void hsw_fdi_link_train(struct drm_crtc *crtc);
 void intel_ddi_init(struct drm_device *dev, enum port port);
 enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder);
@@ -1122,7 +1155,6 @@ bool intel_ddi_pll_select(struct intel_crtc *crtc,
 void intel_ddi_set_pipe_settings(struct drm_crtc *crtc);
 void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp);
 bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector);
-void intel_ddi_fdi_disable(struct drm_crtc *crtc);
 void intel_ddi_get_config(struct intel_encoder *encoder,
                          struct intel_crtc_state *pipe_config);
 struct intel_encoder *
@@ -1133,22 +1165,12 @@ void intel_ddi_clock_get(struct intel_encoder *encoder,
                         struct intel_crtc_state *pipe_config);
 void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
 uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
-
-/* intel_frontbuffer.c */
-void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
-                            enum fb_op_origin origin);
-void intel_frontbuffer_flip_prepare(struct drm_device *dev,
-                                   unsigned frontbuffer_bits);
-void intel_frontbuffer_flip_complete(struct drm_device *dev,
-                                    unsigned frontbuffer_bits);
-void intel_frontbuffer_flip(struct drm_device *dev,
-                           unsigned frontbuffer_bits);
+struct intel_shared_dpll *intel_ddi_get_link_dpll(struct intel_dp *intel_dp,
+                                                 int clock);
 unsigned int intel_fb_align_height(struct drm_device *dev,
                                   unsigned int height,
                                   uint32_t pixel_format,
                                   uint64_t fb_format_modifier);
-void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire,
-                       enum fb_op_origin origin);
 u32 intel_fb_stride_alignment(const struct drm_i915_private *dev_priv,
                              uint64_t fb_modifier, uint32_t pixel_format);
 
@@ -1164,14 +1186,22 @@ void skl_set_preferred_cdclk_vco(struct drm_i915_private *dev_priv, int vco);
 void intel_update_rawclk(struct drm_i915_private *dev_priv);
 int vlv_get_cck_clock(struct drm_i915_private *dev_priv,
                      const char *name, u32 reg, int ref_freq);
+void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv);
+void lpt_disable_iclkip(struct drm_i915_private *dev_priv);
 extern const struct drm_plane_funcs intel_plane_funcs;
 void intel_init_display_hooks(struct drm_i915_private *dev_priv);
+unsigned int intel_fb_xy_to_linear(int x, int y,
+                                  const struct intel_plane_state *state,
+                                  int plane);
+void intel_add_fb_offsets(int *x, int *y,
+                         const struct intel_plane_state *state, int plane);
 unsigned int intel_rotation_info_size(const struct intel_rotation_info *rot_info);
 bool intel_has_pending_fb_unpin(struct drm_device *dev);
 void intel_mark_busy(struct drm_i915_private *dev_priv);
 void intel_mark_idle(struct drm_i915_private *dev_priv);
 void intel_crtc_restore_mode(struct drm_crtc *crtc);
 int intel_display_suspend(struct drm_device *dev);
+void intel_pps_unlock_regs_wa(struct drm_i915_private *dev_priv);
 void intel_encoder_destroy(struct drm_encoder *encoder);
 int intel_connector_init(struct intel_connector *);
 struct intel_connector *intel_connector_alloc(void);
@@ -1227,8 +1257,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
 void intel_release_load_detect_pipe(struct drm_connector *connector,
                                    struct intel_load_detect_pipe *old,
                                    struct drm_modeset_acquire_ctx *ctx);
-int intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
-                              unsigned int rotation);
+struct i915_vma *
+intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation);
 void intel_unpin_fb_obj(struct drm_framebuffer *fb, unsigned int rotation);
 struct drm_framebuffer *
 __intel_framebuffer_create(struct drm_device *dev,
@@ -1238,9 +1268,9 @@ void intel_finish_page_flip_cs(struct drm_i915_private *dev_priv, int pipe);
 void intel_finish_page_flip_mmio(struct drm_i915_private *dev_priv, int pipe);
 void intel_check_page_flip(struct drm_i915_private *dev_priv, int pipe);
 int intel_prepare_plane_fb(struct drm_plane *plane,
-                          const struct drm_plane_state *new_state);
+                          struct drm_plane_state *new_state);
 void intel_cleanup_plane_fb(struct drm_plane *plane,
-                           const struct drm_plane_state *old_state);
+                           struct drm_plane_state *old_state);
 int intel_plane_atomic_get_property(struct drm_plane *plane,
                                    const struct drm_plane_state *state,
                                    struct drm_property *property,
@@ -1258,7 +1288,7 @@ unsigned int intel_tile_height(const struct drm_i915_private *dev_priv,
 static inline bool
 intel_rotation_90_or_270(unsigned int rotation)
 {
-       return rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270));
+       return rotation & (DRM_ROTATE_90 | DRM_ROTATE_270);
 }
 
 void intel_create_rotation_property(struct drm_device *dev,
@@ -1290,9 +1320,7 @@ void assert_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, bool state);
 #define assert_pipe_enabled(d, p) assert_pipe(d, p, true)
 #define assert_pipe_disabled(d, p) assert_pipe(d, p, false)
 u32 intel_compute_tile_offset(int *x, int *y,
-                             const struct drm_framebuffer *fb, int plane,
-                             unsigned int pitch,
-                             unsigned int rotation);
+                             const struct intel_plane_state *state, int plane);
 void intel_prepare_reset(struct drm_i915_private *dev_priv);
 void intel_finish_reset(struct drm_i915_private *dev_priv);
 void hsw_enable_pc8(struct drm_i915_private *dev_priv);
@@ -1335,13 +1363,14 @@ void intel_mode_from_pipe_config(struct drm_display_mode *mode,
 int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state);
 int skl_max_scale(struct intel_crtc *crtc, struct intel_crtc_state *crtc_state);
 
-u32 intel_plane_obj_offset(struct intel_plane *intel_plane,
-                          struct drm_i915_gem_object *obj,
-                          unsigned int plane);
+u32 intel_fb_gtt_offset(struct drm_framebuffer *fb, unsigned int rotation);
 
 u32 skl_plane_ctl_format(uint32_t pixel_format);
 u32 skl_plane_ctl_tiling(uint64_t fb_modifier);
 u32 skl_plane_ctl_rotation(unsigned int rotation);
+u32 skl_plane_stride(const struct drm_framebuffer *fb, int plane,
+                    unsigned int rotation);
+int skl_check_plane_surface(struct intel_plane_state *plane_state);
 
 /* intel_csr.c */
 void intel_csr_ucode_init(struct drm_i915_private *);
@@ -1355,7 +1384,8 @@ bool intel_dp_init(struct drm_device *dev, i915_reg_t output_reg, enum port port
 bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
                             struct intel_connector *intel_connector);
 void intel_dp_set_link_params(struct intel_dp *intel_dp,
-                             const struct intel_crtc_state *pipe_config);
+                             int link_rate, uint8_t lane_count,
+                             bool link_mst);
 void intel_dp_start_link_train(struct intel_dp *intel_dp);
 void intel_dp_stop_link_train(struct intel_dp *intel_dp);
 void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
@@ -1364,7 +1394,8 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder);
 void intel_dp_encoder_destroy(struct drm_encoder *encoder);
 int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc);
 bool intel_dp_compute_config(struct intel_encoder *encoder,
-                            struct intel_crtc_state *pipe_config);
+                            struct intel_crtc_state *pipe_config,
+                            struct drm_connector_state *conn_state);
 bool intel_dp_is_edp(struct drm_device *dev, enum port port);
 enum irqreturn intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port,
                                  bool long_hpd);
@@ -1382,13 +1413,14 @@ void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
 void intel_power_sequencer_reset(struct drm_i915_private *dev_priv);
 uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes);
 void intel_plane_destroy(struct drm_plane *plane);
-void intel_edp_drrs_enable(struct intel_dp *intel_dp);
-void intel_edp_drrs_disable(struct intel_dp *intel_dp);
-void intel_edp_drrs_invalidate(struct drm_device *dev,
-               unsigned frontbuffer_bits);
-void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
-bool intel_digital_port_connected(struct drm_i915_private *dev_priv,
-                                        struct intel_digital_port *port);
+void intel_edp_drrs_enable(struct intel_dp *intel_dp,
+                          struct intel_crtc_state *crtc_state);
+void intel_edp_drrs_disable(struct intel_dp *intel_dp,
+                          struct intel_crtc_state *crtc_state);
+void intel_edp_drrs_invalidate(struct drm_i915_private *dev_priv,
+                              unsigned int frontbuffer_bits);
+void intel_edp_drrs_flush(struct drm_i915_private *dev_priv,
+                         unsigned int frontbuffer_bits);
 
 void
 intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
@@ -1488,7 +1520,8 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
                               struct intel_connector *intel_connector);
 struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
 bool intel_hdmi_compute_config(struct intel_encoder *encoder,
-                              struct intel_crtc_state *pipe_config);
+                              struct intel_crtc_state *pipe_config,
+                              struct drm_connector_state *conn_state);
 void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable);
 
 
@@ -1561,13 +1594,13 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
 /* intel_psr.c */
 void intel_psr_enable(struct intel_dp *intel_dp);
 void intel_psr_disable(struct intel_dp *intel_dp);
-void intel_psr_invalidate(struct drm_device *dev,
+void intel_psr_invalidate(struct drm_i915_private *dev_priv,
                          unsigned frontbuffer_bits);
-void intel_psr_flush(struct drm_device *dev,
+void intel_psr_flush(struct drm_i915_private *dev_priv,
                     unsigned frontbuffer_bits,
                     enum fb_op_origin origin);
 void intel_psr_init(struct drm_device *dev);
-void intel_psr_single_frame_update(struct drm_device *dev,
+void intel_psr_single_frame_update(struct drm_i915_private *dev_priv,
                                   unsigned frontbuffer_bits);
 
 /* intel_runtime_pm.c */
@@ -1667,13 +1700,6 @@ enable_rpm_wakeref_asserts(struct drm_i915_private *dev_priv)
        atomic_dec(&dev_priv->pm.wakeref_count);
 }
 
-/* TODO: convert users of these to rely instead on proper RPM refcounting */
-#define DISABLE_RPM_WAKEREF_ASSERTS(dev_priv)  \
-       disable_rpm_wakeref_asserts(dev_priv)
-
-#define ENABLE_RPM_WAKEREF_ASSERTS(dev_priv)   \
-       enable_rpm_wakeref_asserts(dev_priv)
-
 void intel_runtime_pm_get(struct drm_i915_private *dev_priv);
 bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv);
 void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv);
@@ -1699,11 +1725,11 @@ void intel_gpu_ips_init(struct drm_i915_private *dev_priv);
 void intel_gpu_ips_teardown(void);
 void intel_init_gt_powersave(struct drm_i915_private *dev_priv);
 void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv);
+void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv);
 void intel_enable_gt_powersave(struct drm_i915_private *dev_priv);
+void intel_autoenable_gt_powersave(struct drm_i915_private *dev_priv);
 void intel_disable_gt_powersave(struct drm_i915_private *dev_priv);
 void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv);
-void intel_reset_gt_powersave(struct drm_i915_private *dev_priv);
-void gen6_update_ring_freq(struct drm_i915_private *dev_priv);
 void gen6_rps_busy(struct drm_i915_private *dev_priv);
 void gen6_rps_reset_ei(struct drm_i915_private *dev_priv);
 void gen6_rps_idle(struct drm_i915_private *dev_priv);
@@ -1716,9 +1742,21 @@ void ilk_wm_get_hw_state(struct drm_device *dev);
 void skl_wm_get_hw_state(struct drm_device *dev);
 void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv,
                          struct skl_ddb_allocation *ddb /* out */);
-bool skl_can_enable_sagv(struct drm_atomic_state *state);
-int skl_enable_sagv(struct drm_i915_private *dev_priv);
-int skl_disable_sagv(struct drm_i915_private *dev_priv);
+bool intel_can_enable_sagv(struct drm_atomic_state *state);
+int intel_enable_sagv(struct drm_i915_private *dev_priv);
+int intel_disable_sagv(struct drm_i915_private *dev_priv);
+bool skl_ddb_allocation_equals(const struct skl_ddb_allocation *old,
+                              const struct skl_ddb_allocation *new,
+                              enum pipe pipe);
+bool skl_ddb_allocation_overlaps(struct drm_atomic_state *state,
+                                const struct skl_ddb_allocation *old,
+                                const struct skl_ddb_allocation *new,
+                                enum pipe pipe);
+void skl_write_cursor_wm(struct intel_crtc *intel_crtc,
+                        const struct skl_wm_values *wm);
+void skl_write_plane_wm(struct intel_crtc *intel_crtc,
+                       const struct skl_wm_values *wm,
+                       int plane);
 uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_config);
 bool ilk_disable_lp_wm(struct drm_device *dev);
 int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6);
index de8e9fb..b2e3d3a 100644 (file)
@@ -312,7 +312,8 @@ static inline bool is_cmd_mode(struct intel_dsi *intel_dsi)
 }
 
 static bool intel_dsi_compute_config(struct intel_encoder *encoder,
-                                    struct intel_crtc_state *pipe_config)
+                                    struct intel_crtc_state *pipe_config,
+                                    struct drm_connector_state *conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_dsi *intel_dsi = container_of(encoder, struct intel_dsi,
@@ -533,14 +534,15 @@ static void intel_dsi_enable(struct intel_encoder *encoder)
        intel_panel_enable_backlight(intel_dsi->attached_connector);
 }
 
-static void intel_dsi_prepare(struct intel_encoder *intel_encoder);
+static void intel_dsi_prepare(struct intel_encoder *intel_encoder,
+                             struct intel_crtc_state *pipe_config);
 
-static void intel_dsi_pre_enable(struct intel_encoder *encoder)
+static void intel_dsi_pre_enable(struct intel_encoder *encoder,
+                                struct intel_crtc_state *pipe_config,
+                                struct drm_connector_state *conn_state)
 {
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        enum port port;
 
        DRM_DEBUG_KMS("\n");
@@ -550,9 +552,9 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
         * lock. It needs to be fully powered down to fix it.
         */
        intel_disable_dsi_pll(encoder);
-       intel_enable_dsi_pll(encoder, crtc->config);
+       intel_enable_dsi_pll(encoder, pipe_config);
 
-       intel_dsi_prepare(encoder);
+       intel_dsi_prepare(encoder, pipe_config);
 
        /* Panel Enable over CRC PMIC */
        if (intel_dsi->gpio_panel)
@@ -582,7 +584,9 @@ static void intel_dsi_pre_enable(struct intel_encoder *encoder)
        intel_dsi_enable(encoder);
 }
 
-static void intel_dsi_enable_nop(struct intel_encoder *encoder)
+static void intel_dsi_enable_nop(struct intel_encoder *encoder,
+                                struct intel_crtc_state *pipe_config,
+                                struct drm_connector_state *conn_state)
 {
        DRM_DEBUG_KMS("\n");
 
@@ -592,7 +596,9 @@ static void intel_dsi_enable_nop(struct intel_encoder *encoder)
         */
 }
 
-static void intel_dsi_pre_disable(struct intel_encoder *encoder)
+static void intel_dsi_pre_disable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
        struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
        enum port port;
@@ -694,7 +700,9 @@ static void intel_dsi_clear_device_ready(struct intel_encoder *encoder)
        intel_disable_dsi_pll(encoder);
 }
 
-static void intel_dsi_post_disable(struct intel_encoder *encoder)
+static void intel_dsi_post_disable(struct intel_encoder *encoder,
+                                  struct intel_crtc_state *pipe_config,
+                                  struct drm_connector_state *conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base);
@@ -819,6 +827,7 @@ static void bxt_dsi_get_pipe_config(struct intel_encoder *encoder,
        u16 crtc_htotal_sw, crtc_hsync_start_sw, crtc_hsync_end_sw,
                                crtc_hblank_start_sw, crtc_hblank_end_sw;
 
+       /* FIXME: hw readout should not depend on SW state */
        intel_crtc = to_intel_crtc(encoder->base.crtc);
        adjusted_mode_sw = &intel_crtc->config->base.adjusted_mode;
 
@@ -1104,14 +1113,15 @@ static u32 pixel_format_to_reg(enum mipi_dsi_pixel_format fmt)
        }
 }
 
-static void intel_dsi_prepare(struct intel_encoder *intel_encoder)
+static void intel_dsi_prepare(struct intel_encoder *intel_encoder,
+                             struct intel_crtc_state *pipe_config)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
        struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);
-       const struct drm_display_mode *adjusted_mode = &intel_crtc->config->base.adjusted_mode;
+       const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
        enum port port;
        unsigned int bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format);
        u32 val, tmp;
@@ -1348,7 +1358,7 @@ static int intel_dsi_set_property(struct drm_connector *connector,
                intel_connector->panel.fitting_mode = val;
        }
 
-       crtc = intel_attached_encoder(connector)->base.crtc;
+       crtc = connector->state->crtc;
        if (crtc && crtc->state->enable) {
                /*
                 * If the CRTC is enabled, the display will be changed
index b9e5a63..2e452c5 100644 (file)
@@ -174,7 +174,9 @@ static void intel_dvo_get_config(struct intel_encoder *encoder,
        pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
 }
 
-static void intel_disable_dvo(struct intel_encoder *encoder)
+static void intel_disable_dvo(struct intel_encoder *encoder,
+                             struct intel_crtc_state *old_crtc_state,
+                             struct drm_connector_state *old_conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_dvo *intel_dvo = enc_to_dvo(encoder);
@@ -186,17 +188,18 @@ static void intel_disable_dvo(struct intel_encoder *encoder)
        I915_READ(dvo_reg);
 }
 
-static void intel_enable_dvo(struct intel_encoder *encoder)
+static void intel_enable_dvo(struct intel_encoder *encoder,
+                            struct intel_crtc_state *pipe_config,
+                            struct drm_connector_state *conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_dvo *intel_dvo = enc_to_dvo(encoder);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
        i915_reg_t dvo_reg = intel_dvo->dev.dvo_reg;
        u32 temp = I915_READ(dvo_reg);
 
        intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev,
-                                        &crtc->config->base.mode,
-                                        &crtc->config->base.adjusted_mode);
+                                        &pipe_config->base.mode,
+                                        &pipe_config->base.adjusted_mode);
 
        I915_WRITE(dvo_reg, temp | DVO_ENABLE);
        I915_READ(dvo_reg);
@@ -235,7 +238,8 @@ intel_dvo_mode_valid(struct drm_connector *connector,
 }
 
 static bool intel_dvo_compute_config(struct intel_encoder *encoder,
-                                    struct intel_crtc_state *pipe_config)
+                                    struct intel_crtc_state *pipe_config,
+                                    struct drm_connector_state *conn_state)
 {
        struct intel_dvo *intel_dvo = enc_to_dvo(encoder);
        const struct drm_display_mode *fixed_mode =
@@ -253,12 +257,13 @@ static bool intel_dvo_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_dvo_pre_enable(struct intel_encoder *encoder)
+static void intel_dvo_pre_enable(struct intel_encoder *encoder,
+                                struct intel_crtc_state *pipe_config,
+                                struct drm_connector_state *conn_state)
 {
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
        struct intel_dvo *intel_dvo = enc_to_dvo(encoder);
        int pipe = crtc->pipe;
        u32 dvo_val;
diff --git a/drivers/gpu/drm/i915/intel_engine_cs.c b/drivers/gpu/drm/i915/intel_engine_cs.c
new file mode 100644 (file)
index 0000000..025e232
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright © 2016 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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 "i915_drv.h"
+#include "intel_ringbuffer.h"
+#include "intel_lrc.h"
+
+static const struct engine_info {
+       const char *name;
+       unsigned exec_id;
+       enum intel_engine_hw_id hw_id;
+       u32 mmio_base;
+       unsigned irq_shift;
+       int (*init_legacy)(struct intel_engine_cs *engine);
+       int (*init_execlists)(struct intel_engine_cs *engine);
+} intel_engines[] = {
+       [RCS] = {
+               .name = "render ring",
+               .exec_id = I915_EXEC_RENDER,
+               .hw_id = RCS_HW,
+               .mmio_base = RENDER_RING_BASE,
+               .irq_shift = GEN8_RCS_IRQ_SHIFT,
+               .init_execlists = logical_render_ring_init,
+               .init_legacy = intel_init_render_ring_buffer,
+       },
+       [BCS] = {
+               .name = "blitter ring",
+               .exec_id = I915_EXEC_BLT,
+               .hw_id = BCS_HW,
+               .mmio_base = BLT_RING_BASE,
+               .irq_shift = GEN8_BCS_IRQ_SHIFT,
+               .init_execlists = logical_xcs_ring_init,
+               .init_legacy = intel_init_blt_ring_buffer,
+       },
+       [VCS] = {
+               .name = "bsd ring",
+               .exec_id = I915_EXEC_BSD,
+               .hw_id = VCS_HW,
+               .mmio_base = GEN6_BSD_RING_BASE,
+               .irq_shift = GEN8_VCS1_IRQ_SHIFT,
+               .init_execlists = logical_xcs_ring_init,
+               .init_legacy = intel_init_bsd_ring_buffer,
+       },
+       [VCS2] = {
+               .name = "bsd2 ring",
+               .exec_id = I915_EXEC_BSD,
+               .hw_id = VCS2_HW,
+               .mmio_base = GEN8_BSD2_RING_BASE,
+               .irq_shift = GEN8_VCS2_IRQ_SHIFT,
+               .init_execlists = logical_xcs_ring_init,
+               .init_legacy = intel_init_bsd2_ring_buffer,
+       },
+       [VECS] = {
+               .name = "video enhancement ring",
+               .exec_id = I915_EXEC_VEBOX,
+               .hw_id = VECS_HW,
+               .mmio_base = VEBOX_RING_BASE,
+               .irq_shift = GEN8_VECS_IRQ_SHIFT,
+               .init_execlists = logical_xcs_ring_init,
+               .init_legacy = intel_init_vebox_ring_buffer,
+       },
+};
+
+static struct intel_engine_cs *
+intel_engine_setup(struct drm_i915_private *dev_priv,
+                  enum intel_engine_id id)
+{
+       const struct engine_info *info = &intel_engines[id];
+       struct intel_engine_cs *engine = &dev_priv->engine[id];
+
+       engine->id = id;
+       engine->i915 = dev_priv;
+       engine->name = info->name;
+       engine->exec_id = info->exec_id;
+       engine->hw_id = engine->guc_id = info->hw_id;
+       engine->mmio_base = info->mmio_base;
+       engine->irq_shift = info->irq_shift;
+
+       return engine;
+}
+
+/**
+ * intel_engines_init() - allocate, populate and init the Engine Command Streamers
+ * @dev: DRM device.
+ *
+ * Return: non-zero if the initialization failed.
+ */
+int intel_engines_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_device_info *device_info = mkwrite_device_info(dev_priv);
+       unsigned int mask = 0;
+       int (*init)(struct intel_engine_cs *engine);
+       unsigned int i;
+       int ret;
+
+       WARN_ON(INTEL_INFO(dev_priv)->ring_mask == 0);
+       WARN_ON(INTEL_INFO(dev_priv)->ring_mask &
+               GENMASK(sizeof(mask) * BITS_PER_BYTE - 1, I915_NUM_ENGINES));
+
+       for (i = 0; i < ARRAY_SIZE(intel_engines); i++) {
+               if (!HAS_ENGINE(dev_priv, i))
+                       continue;
+
+               if (i915.enable_execlists)
+                       init = intel_engines[i].init_execlists;
+               else
+                       init = intel_engines[i].init_legacy;
+
+               if (!init)
+                       continue;
+
+               ret = init(intel_engine_setup(dev_priv, i));
+               if (ret)
+                       goto cleanup;
+
+               mask |= ENGINE_MASK(i);
+       }
+
+       /*
+        * Catch failures to update intel_engines table when the new engines
+        * are added to the driver by a warning and disabling the forgotten
+        * engines.
+        */
+       if (WARN_ON(mask != INTEL_INFO(dev_priv)->ring_mask))
+               device_info->ring_mask = mask;
+
+       device_info->num_rings = hweight32(mask);
+
+       return 0;
+
+cleanup:
+       for (i = 0; i < I915_NUM_ENGINES; i++) {
+               if (i915.enable_execlists)
+                       intel_logical_ring_cleanup(&dev_priv->engine[i]);
+               else
+                       intel_engine_cleanup(&dev_priv->engine[i]);
+       }
+
+       return ret;
+}
+
+void intel_engine_init_seqno(struct intel_engine_cs *engine, u32 seqno)
+{
+       struct drm_i915_private *dev_priv = engine->i915;
+
+       /* Our semaphore implementation is strictly monotonic (i.e. we proceed
+        * so long as the semaphore value in the register/page is greater
+        * than the sync value), so whenever we reset the seqno,
+        * so long as we reset the tracking semaphore value to 0, it will
+        * always be before the next request's seqno. If we don't reset
+        * the semaphore value, then when the seqno moves backwards all
+        * future waits will complete instantly (causing rendering corruption).
+        */
+       if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) {
+               I915_WRITE(RING_SYNC_0(engine->mmio_base), 0);
+               I915_WRITE(RING_SYNC_1(engine->mmio_base), 0);
+               if (HAS_VEBOX(dev_priv))
+                       I915_WRITE(RING_SYNC_2(engine->mmio_base), 0);
+       }
+       if (dev_priv->semaphore) {
+               struct page *page = i915_vma_first_page(dev_priv->semaphore);
+               void *semaphores;
+
+               /* Semaphores are in noncoherent memory, flush to be safe */
+               semaphores = kmap(page);
+               memset(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0),
+                      0, I915_NUM_ENGINES * gen8_semaphore_seqno_size);
+               drm_clflush_virt_range(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0),
+                                      I915_NUM_ENGINES * gen8_semaphore_seqno_size);
+               kunmap(page);
+       }
+       memset(engine->semaphore.sync_seqno, 0,
+              sizeof(engine->semaphore.sync_seqno));
+
+       intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
+       if (engine->irq_seqno_barrier)
+               engine->irq_seqno_barrier(engine);
+       engine->last_submitted_seqno = seqno;
+
+       engine->hangcheck.seqno = seqno;
+
+       /* After manually advancing the seqno, fake the interrupt in case
+        * there are any waiters for that seqno.
+        */
+       intel_engine_wakeup(engine);
+}
+
+void intel_engine_init_hangcheck(struct intel_engine_cs *engine)
+{
+       memset(&engine->hangcheck, 0, sizeof(engine->hangcheck));
+}
+
+static void intel_engine_init_requests(struct intel_engine_cs *engine)
+{
+       init_request_active(&engine->last_request, NULL);
+       INIT_LIST_HEAD(&engine->request_list);
+}
+
+/**
+ * intel_engines_setup_common - setup engine state not requiring hw access
+ * @engine: Engine to setup.
+ *
+ * Initializes @engine@ structure members shared between legacy and execlists
+ * submission modes which do not require hardware access.
+ *
+ * Typically done early in the submission mode specific engine setup stage.
+ */
+void intel_engine_setup_common(struct intel_engine_cs *engine)
+{
+       INIT_LIST_HEAD(&engine->execlist_queue);
+       spin_lock_init(&engine->execlist_lock);
+
+       engine->fence_context = fence_context_alloc(1);
+
+       intel_engine_init_requests(engine);
+       intel_engine_init_hangcheck(engine);
+       i915_gem_batch_pool_init(engine, &engine->batch_pool);
+
+       intel_engine_init_cmd_parser(engine);
+}
+
+int intel_engine_create_scratch(struct intel_engine_cs *engine, int size)
+{
+       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
+       int ret;
+
+       WARN_ON(engine->scratch);
+
+       obj = i915_gem_object_create_stolen(&engine->i915->drm, size);
+       if (!obj)
+               obj = i915_gem_object_create(&engine->i915->drm, size);
+       if (IS_ERR(obj)) {
+               DRM_ERROR("Failed to allocate scratch page\n");
+               return PTR_ERR(obj);
+       }
+
+       vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto err_unref;
+       }
+
+       ret = i915_vma_pin(vma, 0, 4096, PIN_GLOBAL | PIN_HIGH);
+       if (ret)
+               goto err_unref;
+
+       engine->scratch = vma;
+       DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n",
+                        engine->name, i915_ggtt_offset(vma));
+       return 0;
+
+err_unref:
+       i915_gem_object_put(obj);
+       return ret;
+}
+
+static void intel_engine_cleanup_scratch(struct intel_engine_cs *engine)
+{
+       i915_vma_unpin_and_release(&engine->scratch);
+}
+
+/**
+ * intel_engines_init_common - initialize cengine state which might require hw access
+ * @engine: Engine to initialize.
+ *
+ * Initializes @engine@ structure members shared between legacy and execlists
+ * submission modes which do require hardware access.
+ *
+ * Typcally done at later stages of submission mode specific engine setup.
+ *
+ * Returns zero on success or an error code on failure.
+ */
+int intel_engine_init_common(struct intel_engine_cs *engine)
+{
+       int ret;
+
+       ret = intel_engine_init_breadcrumbs(engine);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/**
+ * intel_engines_cleanup_common - cleans up the engine state created by
+ *                                the common initiailizers.
+ * @engine: Engine to cleanup.
+ *
+ * This cleans up everything created by the common helpers.
+ */
+void intel_engine_cleanup_common(struct intel_engine_cs *engine)
+{
+       intel_engine_cleanup_scratch(engine);
+
+       intel_engine_fini_breadcrumbs(engine);
+       intel_engine_cleanup_cmd_parser(engine);
+       i915_gem_batch_pool_fini(&engine->batch_pool);
+}
index 3836a1c..faa6762 100644 (file)
@@ -190,9 +190,13 @@ static void g4x_fbc_activate(struct drm_i915_private *dev_priv)
                dpfc_ctl |= DPFC_CTL_LIMIT_2X;
        else
                dpfc_ctl |= DPFC_CTL_LIMIT_1X;
-       dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fb.fence_reg;
 
-       I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset);
+       if (params->fb.fence_reg != I915_FENCE_REG_NONE) {
+               dpfc_ctl |= DPFC_CTL_FENCE_EN | params->fb.fence_reg;
+               I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset);
+       } else {
+               I915_WRITE(DPFC_FENCE_YOFF, 0);
+       }
 
        /* enable it... */
        I915_WRITE(DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
@@ -244,21 +248,29 @@ static void ilk_fbc_activate(struct drm_i915_private *dev_priv)
                dpfc_ctl |= DPFC_CTL_LIMIT_1X;
                break;
        }
-       dpfc_ctl |= DPFC_CTL_FENCE_EN;
-       if (IS_GEN5(dev_priv))
-               dpfc_ctl |= params->fb.fence_reg;
+
+       if (params->fb.fence_reg != I915_FENCE_REG_NONE) {
+               dpfc_ctl |= DPFC_CTL_FENCE_EN;
+               if (IS_GEN5(dev_priv))
+                       dpfc_ctl |= params->fb.fence_reg;
+               if (IS_GEN6(dev_priv)) {
+                       I915_WRITE(SNB_DPFC_CTL_SA,
+                                  SNB_CPU_FENCE_ENABLE | params->fb.fence_reg);
+                       I915_WRITE(DPFC_CPU_FENCE_OFFSET,
+                                  params->crtc.fence_y_offset);
+               }
+       } else {
+               if (IS_GEN6(dev_priv)) {
+                       I915_WRITE(SNB_DPFC_CTL_SA, 0);
+                       I915_WRITE(DPFC_CPU_FENCE_OFFSET, 0);
+               }
+       }
 
        I915_WRITE(ILK_DPFC_FENCE_YOFF, params->crtc.fence_y_offset);
        I915_WRITE(ILK_FBC_RT_BASE, params->fb.ggtt_offset | ILK_FBC_RT_VALID);
        /* enable it... */
        I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
 
-       if (IS_GEN6(dev_priv)) {
-               I915_WRITE(SNB_DPFC_CTL_SA,
-                          SNB_CPU_FENCE_ENABLE | params->fb.fence_reg);
-               I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset);
-       }
-
        intel_fbc_recompress(dev_priv);
 }
 
@@ -305,7 +317,15 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv)
                break;
        }
 
-       dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
+       if (params->fb.fence_reg != I915_FENCE_REG_NONE) {
+               dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
+               I915_WRITE(SNB_DPFC_CTL_SA,
+                          SNB_CPU_FENCE_ENABLE | params->fb.fence_reg);
+               I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset);
+       } else {
+               I915_WRITE(SNB_DPFC_CTL_SA,0);
+               I915_WRITE(DPFC_CPU_FENCE_OFFSET, 0);
+       }
 
        if (dev_priv->fbc.false_color)
                dpfc_ctl |= FBC_CTL_FALSE_COLOR;
@@ -324,10 +344,6 @@ static void gen7_fbc_activate(struct drm_i915_private *dev_priv)
 
        I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
 
-       I915_WRITE(SNB_DPFC_CTL_SA,
-                  SNB_CPU_FENCE_ENABLE | params->fb.fence_reg);
-       I915_WRITE(DPFC_CPU_FENCE_OFFSET, params->crtc.fence_y_offset);
-
        intel_fbc_recompress(dev_priv);
 }
 
@@ -494,7 +510,7 @@ static bool multiple_pipes_ok(struct intel_crtc *crtc,
        if (!no_fbc_on_multiple_pipes(dev_priv))
                return true;
 
-       if (plane_state->visible)
+       if (plane_state->base.visible)
                fbc->visible_pipes_mask |= (1 << pipe);
        else
                fbc->visible_pipes_mask &= ~(1 << pipe);
@@ -709,6 +725,14 @@ static bool intel_fbc_hw_tracking_covers_screen(struct intel_crtc *crtc)
        return effective_w <= max_w && effective_h <= max_h;
 }
 
+/* XXX replace me when we have VMA tracking for intel_plane_state */
+static int get_fence_id(struct drm_framebuffer *fb)
+{
+       struct i915_vma *vma = i915_gem_object_to_ggtt(intel_fb_obj(fb), NULL);
+
+       return vma && vma->fence ? vma->fence->id : I915_FENCE_REG_NONE;
+}
+
 static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
                                         struct intel_crtc_state *crtc_state,
                                         struct intel_plane_state *plane_state)
@@ -725,9 +749,9 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
                        ilk_pipe_pixel_rate(crtc_state);
 
        cache->plane.rotation = plane_state->base.rotation;
-       cache->plane.src_w = drm_rect_width(&plane_state->src) >> 16;
-       cache->plane.src_h = drm_rect_height(&plane_state->src) >> 16;
-       cache->plane.visible = plane_state->visible;
+       cache->plane.src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       cache->plane.src_h = drm_rect_height(&plane_state->base.src) >> 16;
+       cache->plane.visible = plane_state->base.visible;
 
        if (!cache->plane.visible)
                return;
@@ -737,11 +761,11 @@ static void intel_fbc_update_state_cache(struct intel_crtc *crtc,
        /* FIXME: We lack the proper locking here, so only run this on the
         * platforms that need. */
        if (IS_GEN(dev_priv, 5, 6))
-               cache->fb.ilk_ggtt_offset = i915_gem_obj_ggtt_offset(obj);
+               cache->fb.ilk_ggtt_offset = i915_gem_object_ggtt_offset(obj, NULL);
        cache->fb.pixel_format = fb->pixel_format;
        cache->fb.stride = fb->pitches[0];
-       cache->fb.fence_reg = obj->fence_reg;
-       cache->fb.tiling_mode = obj->tiling_mode;
+       cache->fb.fence_reg = get_fence_id(fb);
+       cache->fb.tiling_mode = i915_gem_object_get_tiling(obj);
 }
 
 static bool intel_fbc_can_activate(struct intel_crtc *crtc)
@@ -768,6 +792,10 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
 
        /* The use of a CPU fence is mandatory in order to detect writes
         * by the CPU to the scanout and trigger updates to the FBC.
+        *
+        * Note that is possible for a tiled surface to be unmappable (and
+        * so have no fence associated with it) due to aperture constaints
+        * at the time of pinning.
         */
        if (cache->fb.tiling_mode != I915_TILING_X ||
            cache->fb.fence_reg == I915_FENCE_REG_NONE) {
@@ -775,7 +803,7 @@ static bool intel_fbc_can_activate(struct intel_crtc *crtc)
                return false;
        }
        if (INTEL_INFO(dev_priv)->gen <= 4 && !IS_G4X(dev_priv) &&
-           cache->plane.rotation != BIT(DRM_ROTATE_0)) {
+           cache->plane.rotation != DRM_ROTATE_0) {
                fbc->no_fbc_reason = "rotation unsupported";
                return false;
        }
@@ -1050,7 +1078,7 @@ void intel_fbc_choose_crtc(struct drm_i915_private *dev_priv,
                struct intel_plane_state *intel_plane_state =
                        to_intel_plane_state(plane_state);
 
-               if (!intel_plane_state->visible)
+               if (!intel_plane_state->base.visible)
                        continue;
 
                for_each_crtc_in_state(state, crtc, crtc_state, j) {
@@ -1075,6 +1103,8 @@ out:
 /**
  * intel_fbc_enable: tries to enable FBC on the CRTC
  * @crtc: the CRTC
+ * @crtc_state: corresponding &drm_crtc_state for @crtc
+ * @plane_state: corresponding &drm_plane_state for the primary plane of @crtc
  *
  * This function checks if the given CRTC was chosen for FBC, then enables it if
  * possible. Notice that it doesn't activate FBC. It is valid to call
@@ -1163,11 +1193,8 @@ void intel_fbc_disable(struct intel_crtc *crtc)
                return;
 
        mutex_lock(&fbc->lock);
-       if (fbc->crtc == crtc) {
-               WARN_ON(!fbc->enabled);
-               WARN_ON(fbc->active);
+       if (fbc->crtc == crtc)
                __intel_fbc_disable(dev_priv);
-       }
        mutex_unlock(&fbc->lock);
 
        cancel_work_sync(&fbc->work.work);
@@ -1212,7 +1239,7 @@ void intel_fbc_init_pipe_state(struct drm_i915_private *dev_priv)
 
        for_each_intel_crtc(&dev_priv->drm, crtc)
                if (intel_crtc_active(&crtc->base) &&
-                   to_intel_plane_state(crtc->base.primary->state)->visible)
+                   to_intel_plane_state(crtc->base.primary->state)->base.visible)
                        dev_priv->fbc.visible_pipes_mask |= (1 << crtc->pipe);
 }
 
index 3e3632c..b7098f9 100644 (file)
@@ -34,7 +34,6 @@
 #include <linux/tty.h>
 #include <linux/sysrq.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/vga_switcheroo.h>
 
@@ -42,6 +41,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
@@ -159,7 +159,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
 
        fb = __intel_framebuffer_create(dev, &mode_cmd, obj);
        if (IS_ERR(fb)) {
-               drm_gem_object_unreference(&obj->base);
+               i915_gem_object_put(obj);
                ret = PTR_ERR(fb);
                goto out;
        }
@@ -183,13 +183,13 @@ static int intelfb_create(struct drm_fb_helper *helper,
        struct intel_framebuffer *intel_fb = ifbdev->fb;
        struct drm_device *dev = helper->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct i915_ggtt *ggtt = &dev_priv->ggtt;
        struct fb_info *info;
        struct drm_framebuffer *fb;
        struct i915_vma *vma;
-       struct drm_i915_gem_object *obj;
        bool prealloc = false;
-       void *vaddr;
+       void __iomem *vaddr;
        int ret;
 
        if (intel_fb &&
@@ -215,17 +215,17 @@ static int intelfb_create(struct drm_fb_helper *helper,
                sizes->fb_height = intel_fb->base.height;
        }
 
-       obj = intel_fb->obj;
-
        mutex_lock(&dev->struct_mutex);
 
        /* Pin the GGTT vma for our access via info->screen_base.
         * This also validates that any existing fb inherited from the
         * BIOS is suitable for own access.
         */
-       ret = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0));
-       if (ret)
+       vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
                goto out_unlock;
+       }
 
        info = drm_fb_helper_alloc_fbi(helper);
        if (IS_ERR(info)) {
@@ -245,13 +245,11 @@ static int intelfb_create(struct drm_fb_helper *helper,
        info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
        info->fbops = &intelfb_ops;
 
-       vma = i915_gem_obj_to_ggtt(obj);
-
        /* setup aperture base/size for vesafb takeover */
        info->apertures->ranges[0].base = dev->mode_config.fb_base;
        info->apertures->ranges[0].size = ggtt->mappable_end;
 
-       info->fix.smem_start = dev->mode_config.fb_base + vma->node.start;
+       info->fix.smem_start = dev->mode_config.fb_base + i915_ggtt_offset(vma);
        info->fix.smem_len = vma->node.size;
 
        vaddr = i915_vma_pin_iomap(vma);
@@ -273,23 +271,23 @@ static int intelfb_create(struct drm_fb_helper *helper,
         * If the object is stolen however, it will be full of whatever
         * garbage was left in there.
         */
-       if (ifbdev->fb->obj->stolen && !prealloc)
+       if (intel_fb->obj->stolen && !prealloc)
                memset_io(info->screen_base, 0, info->screen_size);
 
        /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
 
-       DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08llx, bo %p\n",
-                     fb->width, fb->height,
-                     i915_gem_obj_ggtt_offset(obj), obj);
+       DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x\n",
+                     fb->width, fb->height, i915_ggtt_offset(vma));
+       ifbdev->vma = vma;
 
        mutex_unlock(&dev->struct_mutex);
-       vga_switcheroo_client_fb_set(dev->pdev, info);
+       vga_switcheroo_client_fb_set(pdev, info);
        return 0;
 
 out_destroy_fbi:
        drm_fb_helper_release_fbi(helper);
 out_unpin:
-       intel_unpin_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0));
+       intel_unpin_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0);
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
        return ret;
@@ -554,7 +552,7 @@ static void intel_fbdev_destroy(struct intel_fbdev *ifbdev)
 
        if (ifbdev->fb) {
                mutex_lock(&ifbdev->helper.dev->struct_mutex);
-               intel_unpin_fb_obj(&ifbdev->fb->base, BIT(DRM_ROTATE_0));
+               intel_unpin_fb_obj(&ifbdev->fb->base, DRM_ROTATE_0);
                mutex_unlock(&ifbdev->helper.dev->struct_mutex);
 
                drm_framebuffer_remove(&ifbdev->fb->base);
@@ -768,7 +766,7 @@ void intel_fbdev_fini(struct drm_device *dev)
        if (!ifbdev)
                return;
 
-       flush_work(&dev_priv->fbdev_suspend_work);
+       cancel_work_sync(&dev_priv->fbdev_suspend_work);
        if (!current_is_async())
                intel_fbdev_sync(ifbdev);
 
index ac85357..966de4c 100644 (file)
 #include <drm/drmP.h>
 
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 #include "i915_drv.h"
 
-/**
- * intel_fb_obj_invalidate - invalidate frontbuffer object
- * @obj: GEM object to invalidate
- * @origin: which operation caused the invalidation
- *
- * This function gets called every time rendering on the given object starts and
- * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must
- * be invalidated. For ORIGIN_CS any subsequent invalidation will be delayed
- * until the rendering completes or a flip on this frontbuffer plane is
- * scheduled.
- */
-void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
-                            enum fb_op_origin origin)
+void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
+                              enum fb_op_origin origin,
+                              unsigned int frontbuffer_bits)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
-       if (!obj->frontbuffer_bits)
-               return;
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
 
        if (origin == ORIGIN_CS) {
-               mutex_lock(&dev_priv->fb_tracking.lock);
-               dev_priv->fb_tracking.busy_bits
-                       |= obj->frontbuffer_bits;
-               dev_priv->fb_tracking.flip_bits
-                       &= ~obj->frontbuffer_bits;
-               mutex_unlock(&dev_priv->fb_tracking.lock);
+               spin_lock(&dev_priv->fb_tracking.lock);
+               dev_priv->fb_tracking.busy_bits |= frontbuffer_bits;
+               dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits;
+               spin_unlock(&dev_priv->fb_tracking.lock);
        }
 
-       intel_psr_invalidate(dev, obj->frontbuffer_bits);
-       intel_edp_drrs_invalidate(dev, obj->frontbuffer_bits);
-       intel_fbc_invalidate(dev_priv, obj->frontbuffer_bits, origin);
+       intel_psr_invalidate(dev_priv, frontbuffer_bits);
+       intel_edp_drrs_invalidate(dev_priv, frontbuffer_bits);
+       intel_fbc_invalidate(dev_priv, frontbuffer_bits, origin);
 }
 
 /**
  * intel_frontbuffer_flush - flush frontbuffer
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  * @origin: which operation caused the flush
  *
@@ -113,64 +96,45 @@ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
  *
  * Can be called without any locks held.
  */
-static void intel_frontbuffer_flush(struct drm_device *dev,
+static void intel_frontbuffer_flush(struct drm_i915_private *dev_priv,
                                    unsigned frontbuffer_bits,
                                    enum fb_op_origin origin)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
        /* Delay flushing when rings are still busy.*/
-       mutex_lock(&dev_priv->fb_tracking.lock);
+       spin_lock(&dev_priv->fb_tracking.lock);
        frontbuffer_bits &= ~dev_priv->fb_tracking.busy_bits;
-       mutex_unlock(&dev_priv->fb_tracking.lock);
+       spin_unlock(&dev_priv->fb_tracking.lock);
 
        if (!frontbuffer_bits)
                return;
 
-       intel_edp_drrs_flush(dev, frontbuffer_bits);
-       intel_psr_flush(dev, frontbuffer_bits, origin);
+       intel_edp_drrs_flush(dev_priv, frontbuffer_bits);
+       intel_psr_flush(dev_priv, frontbuffer_bits, origin);
        intel_fbc_flush(dev_priv, frontbuffer_bits, origin);
 }
 
-/**
- * intel_fb_obj_flush - flush frontbuffer object
- * @obj: GEM object to flush
- * @retire: set when retiring asynchronous rendering
- * @origin: which operation caused the flush
- *
- * This function gets called every time rendering on the given object has
- * completed and frontbuffer caching can be started again. If @retire is true
- * then any delayed flushes will be unblocked.
- */
-void intel_fb_obj_flush(struct drm_i915_gem_object *obj,
-                       bool retire, enum fb_op_origin origin)
+void __intel_fb_obj_flush(struct drm_i915_gem_object *obj,
+                         bool retire,
+                         enum fb_op_origin origin,
+                         unsigned int frontbuffer_bits)
 {
-       struct drm_device *dev = obj->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       unsigned frontbuffer_bits;
-
-       WARN_ON(!mutex_is_locked(&dev->struct_mutex));
-
-       if (!obj->frontbuffer_bits)
-               return;
-
-       frontbuffer_bits = obj->frontbuffer_bits;
+       struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
 
        if (retire) {
-               mutex_lock(&dev_priv->fb_tracking.lock);
+               spin_lock(&dev_priv->fb_tracking.lock);
                /* Filter out new bits since rendering started. */
                frontbuffer_bits &= dev_priv->fb_tracking.busy_bits;
-
                dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
-               mutex_unlock(&dev_priv->fb_tracking.lock);
+               spin_unlock(&dev_priv->fb_tracking.lock);
        }
 
-       intel_frontbuffer_flush(dev, frontbuffer_bits, origin);
+       if (frontbuffer_bits)
+               intel_frontbuffer_flush(dev_priv, frontbuffer_bits, origin);
 }
 
 /**
  * intel_frontbuffer_flip_prepare - prepare asynchronous frontbuffer flip
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * This function gets called after scheduling a flip on @obj. The actual
@@ -180,23 +144,21 @@ void intel_fb_obj_flush(struct drm_i915_gem_object *obj,
  *
  * Can be called without any locks held.
  */
-void intel_frontbuffer_flip_prepare(struct drm_device *dev,
+void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv,
                                    unsigned frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       mutex_lock(&dev_priv->fb_tracking.lock);
+       spin_lock(&dev_priv->fb_tracking.lock);
        dev_priv->fb_tracking.flip_bits |= frontbuffer_bits;
        /* Remove stale busy bits due to the old buffer. */
        dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
-       mutex_unlock(&dev_priv->fb_tracking.lock);
+       spin_unlock(&dev_priv->fb_tracking.lock);
 
-       intel_psr_single_frame_update(dev, frontbuffer_bits);
+       intel_psr_single_frame_update(dev_priv, frontbuffer_bits);
 }
 
 /**
  * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flip
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * This function gets called after the flip has been latched and will complete
@@ -204,23 +166,23 @@ void intel_frontbuffer_flip_prepare(struct drm_device *dev,
  *
  * Can be called without any locks held.
  */
-void intel_frontbuffer_flip_complete(struct drm_device *dev,
+void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv,
                                     unsigned frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       mutex_lock(&dev_priv->fb_tracking.lock);
+       spin_lock(&dev_priv->fb_tracking.lock);
        /* Mask any cancelled flips. */
        frontbuffer_bits &= dev_priv->fb_tracking.flip_bits;
        dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits;
-       mutex_unlock(&dev_priv->fb_tracking.lock);
+       spin_unlock(&dev_priv->fb_tracking.lock);
 
-       intel_frontbuffer_flush(dev, frontbuffer_bits, ORIGIN_FLIP);
+       if (frontbuffer_bits)
+               intel_frontbuffer_flush(dev_priv,
+                                       frontbuffer_bits, ORIGIN_FLIP);
 }
 
 /**
  * intel_frontbuffer_flip - synchronous frontbuffer flip
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * This function gets called after scheduling a flip on @obj. This is for
@@ -229,15 +191,13 @@ void intel_frontbuffer_flip_complete(struct drm_device *dev,
  *
  * Can be called without any locks held.
  */
-void intel_frontbuffer_flip(struct drm_device *dev,
+void intel_frontbuffer_flip(struct drm_i915_private *dev_priv,
                            unsigned frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-
-       mutex_lock(&dev_priv->fb_tracking.lock);
+       spin_lock(&dev_priv->fb_tracking.lock);
        /* Remove stale busy bits due to the old buffer. */
        dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
-       mutex_unlock(&dev_priv->fb_tracking.lock);
+       spin_unlock(&dev_priv->fb_tracking.lock);
 
-       intel_frontbuffer_flush(dev, frontbuffer_bits, ORIGIN_FLIP);
+       intel_frontbuffer_flush(dev_priv, frontbuffer_bits, ORIGIN_FLIP);
 }
diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.h b/drivers/gpu/drm/i915/intel_frontbuffer.h
new file mode 100644 (file)
index 0000000..76ceb53
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2014-2016 Intel Corporation
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS 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 __INTEL_FRONTBUFFER_H__
+#define __INTEL_FRONTBUFFER_H__
+
+struct drm_i915_private;
+struct drm_i915_gem_object;
+
+void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv,
+                                   unsigned frontbuffer_bits);
+void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv,
+                                    unsigned frontbuffer_bits);
+void intel_frontbuffer_flip(struct drm_i915_private *dev_priv,
+                           unsigned frontbuffer_bits);
+
+void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
+                              enum fb_op_origin origin,
+                              unsigned int frontbuffer_bits);
+void __intel_fb_obj_flush(struct drm_i915_gem_object *obj,
+                         bool retire,
+                         enum fb_op_origin origin,
+                         unsigned int frontbuffer_bits);
+
+/**
+ * intel_fb_obj_invalidate - invalidate frontbuffer object
+ * @obj: GEM object to invalidate
+ * @origin: which operation caused the invalidation
+ *
+ * This function gets called every time rendering on the given object starts and
+ * frontbuffer caching (fbc, low refresh rate for DRRS, panel self refresh) must
+ * be invalidated. For ORIGIN_CS any subsequent invalidation will be delayed
+ * until the rendering completes or a flip on this frontbuffer plane is
+ * scheduled.
+ */
+static inline void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
+                                          enum fb_op_origin origin)
+{
+       unsigned int frontbuffer_bits;
+
+       frontbuffer_bits = atomic_read(&obj->frontbuffer_bits);
+       if (!frontbuffer_bits)
+               return;
+
+       __intel_fb_obj_invalidate(obj, origin, frontbuffer_bits);
+}
+
+/**
+ * intel_fb_obj_flush - flush frontbuffer object
+ * @obj: GEM object to flush
+ * @retire: set when retiring asynchronous rendering
+ * @origin: which operation caused the flush
+ *
+ * This function gets called every time rendering on the given object has
+ * completed and frontbuffer caching can be started again. If @retire is true
+ * then any delayed flushes will be unblocked.
+ */
+static inline void intel_fb_obj_flush(struct drm_i915_gem_object *obj,
+                                     bool retire,
+                                     enum fb_op_origin origin)
+{
+       unsigned int frontbuffer_bits;
+
+       frontbuffer_bits = atomic_read(&obj->frontbuffer_bits);
+       if (!frontbuffer_bits)
+               return;
+
+       __intel_fb_obj_flush(obj, retire, origin, frontbuffer_bits);
+}
+
+#endif /* __INTEL_FRONTBUFFER_H__ */
index 3e3e743..5cdf7aa 100644 (file)
@@ -63,26 +63,27 @@ struct drm_i915_gem_request;
  *   retcode: errno from last guc_submit()
  */
 struct i915_guc_client {
-       struct drm_i915_gem_object *client_obj;
+       struct i915_vma *vma;
        void *client_base;              /* first page (only) of above   */
        struct i915_gem_context *owner;
        struct intel_guc *guc;
+
+       uint32_t engines;               /* bitmap of (host) engine ids  */
        uint32_t priority;
        uint32_t ctx_index;
-
        uint32_t proc_desc_offset;
+
        uint32_t doorbell_offset;
        uint32_t cookie;
        uint16_t doorbell_id;
-       uint16_t padding;               /* Maintain alignment           */
+       uint16_t padding[3];            /* Maintain alignment           */
 
+       spinlock_t wq_lock;
        uint32_t wq_offset;
        uint32_t wq_size;
        uint32_t wq_tail;
-       uint32_t unused;                /* Was 'wq_head'                */
-
+       uint32_t wq_rsvd;
        uint32_t no_wq_space;
-       uint32_t q_fail;                /* No longer used               */
        uint32_t b_fail;
        int retcode;
 
@@ -125,11 +126,10 @@ struct intel_guc_fw {
 struct intel_guc {
        struct intel_guc_fw guc_fw;
        uint32_t log_flags;
-       struct drm_i915_gem_object *log_obj;
-
-       struct drm_i915_gem_object *ads_obj;
+       struct i915_vma *log_vma;
 
-       struct drm_i915_gem_object *ctx_pool_obj;
+       struct i915_vma *ads_vma;
+       struct i915_vma *ctx_pool_vma;
        struct ida ctx_ids;
 
        struct i915_guc_client *execbuf_client;
@@ -159,8 +159,8 @@ extern int intel_guc_resume(struct drm_device *dev);
 /* i915_guc_submission.c */
 int i915_guc_submission_init(struct drm_i915_private *dev_priv);
 int i915_guc_submission_enable(struct drm_i915_private *dev_priv);
-int i915_guc_wq_check_space(struct drm_i915_gem_request *rq);
-int i915_guc_submit(struct drm_i915_gem_request *rq);
+int i915_guc_wq_reserve(struct drm_i915_gem_request *rq);
+void i915_guc_wq_unreserve(struct drm_i915_gem_request *request);
 void i915_guc_submission_disable(struct drm_i915_private *dev_priv);
 void i915_guc_submission_fini(struct drm_i915_private *dev_priv);
 
index 944786d..e40db2d 100644 (file)
  *
  *     +-------------------------------+
  *     |        guc_css_header         |
+ *     |                               |
  *     | contains major/minor version  |
  *     +-------------------------------+
  *     |             uCode             |
  *
  * 1. Header, uCode and RSA are must-have components.
  * 2. All firmware components, if they present, are in the sequence illustrated
- * in the layout table above.
+ *    in the layout table above.
  * 3. Length info of each component can be found in header, in dwords.
  * 4. Modulus and exponent key are not required by driver. They may not appear
- * in fw. So driver will load a truncated firmware in this case.
+ *    in fw. So driver will load a truncated firmware in this case.
  */
 
 struct guc_css_header {
index 605c696..6fd39ef 100644 (file)
  *
  */
 
-#define I915_SKL_GUC_UCODE "i915/skl_guc_ver6_1.bin"
+#define SKL_FW_MAJOR 6
+#define SKL_FW_MINOR 1
+
+#define BXT_FW_MAJOR 8
+#define BXT_FW_MINOR 7
+
+#define KBL_FW_MAJOR 9
+#define KBL_FW_MINOR 14
+
+#define GUC_FW_PATH(platform, major, minor) \
+       "i915/" __stringify(platform) "_guc_ver" __stringify(major) "_" __stringify(minor) ".bin"
+
+#define I915_SKL_GUC_UCODE GUC_FW_PATH(skl, SKL_FW_MAJOR, SKL_FW_MINOR)
 MODULE_FIRMWARE(I915_SKL_GUC_UCODE);
 
-#define I915_BXT_GUC_UCODE "i915/bxt_guc_ver8_7.bin"
+#define I915_BXT_GUC_UCODE GUC_FW_PATH(bxt, BXT_FW_MAJOR, BXT_FW_MINOR)
 MODULE_FIRMWARE(I915_BXT_GUC_UCODE);
 
-#define I915_KBL_GUC_UCODE "i915/kbl_guc_ver9_14.bin"
+#define I915_KBL_GUC_UCODE GUC_FW_PATH(kbl, KBL_FW_MAJOR, KBL_FW_MINOR)
 MODULE_FIRMWARE(I915_KBL_GUC_UCODE);
 
 /* User-friendly representation of an enum */
@@ -85,7 +97,7 @@ const char *intel_guc_fw_status_repr(enum intel_guc_fw_status status)
        }
 };
 
-static void direct_interrupts_to_host(struct drm_i915_private *dev_priv)
+static void guc_interrupts_release(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
        int irqs;
@@ -102,7 +114,7 @@ static void direct_interrupts_to_host(struct drm_i915_private *dev_priv)
        I915_WRITE(GUC_WD_VECS_IER, 0);
 }
 
-static void direct_interrupts_to_guc(struct drm_i915_private *dev_priv)
+static void guc_interrupts_capture(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
        int irqs;
@@ -122,13 +134,28 @@ static void direct_interrupts_to_guc(struct drm_i915_private *dev_priv)
        I915_WRITE(GUC_WD_VECS_IER, ~irqs);
 
        /*
-        * If GuC has routed PM interrupts to itself, don't keep it.
-        * and keep other interrupts those are unmasked by GuC.
-       */
+        * The REDIRECT_TO_GUC bit of the PMINTRMSK register directs all
+        * (unmasked) PM interrupts to the GuC. All other bits of this
+        * register *disable* generation of a specific interrupt.
+        *
+        * 'pm_intr_keep' indicates bits that are NOT to be set when
+        * writing to the PM interrupt mask register, i.e. interrupts
+        * that must not be disabled.
+        *
+        * If the GuC is handling these interrupts, then we must not let
+        * the PM code disable ANY interrupt that the GuC is expecting.
+        * So for each ENABLED (0) bit in this register, we must SET the
+        * bit in pm_intr_keep so that it's left enabled for the GuC.
+        *
+        * OTOH the REDIRECT_TO_GUC bit is initially SET in pm_intr_keep
+        * (so interrupts go to the DISPLAY unit at first); but here we
+        * need to CLEAR that bit, which will result in the register bit
+        * being left SET!
+        */
        tmp = I915_READ(GEN6_PMINTRMSK);
-       if (tmp & GEN8_PMINTR_REDIRECT_TO_NON_DISP) {
-               dev_priv->rps.pm_intr_keep |= ~(tmp & ~GEN8_PMINTR_REDIRECT_TO_NON_DISP);
-               dev_priv->rps.pm_intr_keep &= ~GEN8_PMINTR_REDIRECT_TO_NON_DISP;
+       if (tmp & GEN8_PMINTR_REDIRECT_TO_GUC) {
+               dev_priv->rps.pm_intr_keep |= ~tmp;
+               dev_priv->rps.pm_intr_keep &= ~GEN8_PMINTR_REDIRECT_TO_GUC;
        }
 }
 
@@ -140,17 +167,24 @@ static u32 get_gttype(struct drm_i915_private *dev_priv)
 
 static u32 get_core_family(struct drm_i915_private *dev_priv)
 {
-       switch (INTEL_INFO(dev_priv)->gen) {
+       u32 gen = INTEL_GEN(dev_priv);
+
+       switch (gen) {
        case 9:
                return GFXCORE_FAMILY_GEN9;
 
        default:
-               DRM_ERROR("GUC: unsupported core family\n");
+               WARN(1, "GEN%d does not support GuC operation!\n", gen);
                return GFXCORE_FAMILY_UNKNOWN;
        }
 }
 
-static void set_guc_init_params(struct drm_i915_private *dev_priv)
+/*
+ * Initialise the GuC parameter block before starting the firmware
+ * transfer. These parameters are read by the firmware on startup
+ * and cannot be changed thereafter.
+ */
+static void guc_params_init(struct drm_i915_private *dev_priv)
 {
        struct intel_guc *guc = &dev_priv->guc;
        u32 params[GUC_CTL_MAX_DWORDS];
@@ -181,16 +215,15 @@ static void set_guc_init_params(struct drm_i915_private *dev_priv)
                        i915.guc_log_level << GUC_LOG_VERBOSITY_SHIFT;
        }
 
-       if (guc->ads_obj) {
-               u32 ads = (u32)i915_gem_obj_ggtt_offset(guc->ads_obj)
-                               >> PAGE_SHIFT;
+       if (guc->ads_vma) {
+               u32 ads = i915_ggtt_offset(guc->ads_vma) >> PAGE_SHIFT;
                params[GUC_CTL_DEBUG] |= ads << GUC_ADS_ADDR_SHIFT;
                params[GUC_CTL_DEBUG] |= GUC_ADS_ENABLED;
        }
 
        /* If GuC submission is enabled, set up additional parameters here */
        if (i915.enable_guc_submission) {
-               u32 pgs = i915_gem_obj_ggtt_offset(dev_priv->guc.ctx_pool_obj);
+               u32 pgs = i915_ggtt_offset(dev_priv->guc.ctx_pool_vma);
                u32 ctx_in_16 = GUC_MAX_GPU_CONTEXTS / 16;
 
                pgs >>= PAGE_SHIFT;
@@ -238,12 +271,12 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv,
  * Note that GuC needs the CSS header plus uKernel code to be copied by the
  * DMA engine in one operation, whereas the RSA signature is loaded via MMIO.
  */
-static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv)
+static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv,
+                             struct i915_vma *vma)
 {
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
-       struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj;
        unsigned long offset;
-       struct sg_table *sg = fw_obj->pages;
+       struct sg_table *sg = vma->pages;
        u32 status, rsa[UOS_RSA_SCRATCH_MAX_COUNT];
        int i, ret = 0;
 
@@ -260,7 +293,7 @@ static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv)
        I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size);
 
        /* Set the source address for the new blob */
-       offset = i915_gem_obj_ggtt_offset(fw_obj) + guc_fw->header_offset;
+       offset = i915_ggtt_offset(vma) + guc_fw->header_offset;
        I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset));
        I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF);
 
@@ -315,6 +348,7 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
 {
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
        struct drm_device *dev = &dev_priv->drm;
+       struct i915_vma *vma;
        int ret;
 
        ret = i915_gem_object_set_to_gtt_domain(guc_fw->guc_fw_obj, false);
@@ -323,10 +357,10 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
                return ret;
        }
 
-       ret = i915_gem_obj_ggtt_pin(guc_fw->guc_fw_obj, 0, 0);
-       if (ret) {
-               DRM_DEBUG_DRIVER("pin failed %d\n", ret);
-               return ret;
+       vma = i915_gem_object_ggtt_pin(guc_fw->guc_fw_obj, NULL, 0, 0, 0);
+       if (IS_ERR(vma)) {
+               DRM_DEBUG_DRIVER("pin failed %d\n", (int)PTR_ERR(vma));
+               return PTR_ERR(vma);
        }
 
        /* Invalidate GuC TLB to let GuC take the latest updates to GTT. */
@@ -349,7 +383,9 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
        }
 
        /* WaC6DisallowByGfxPause*/
-       I915_WRITE(GEN6_GFXPAUSE, 0x30FFF);
+       if (IS_SKL_REVID(dev, 0, SKL_REVID_C0) ||
+           IS_BXT_REVID(dev, 0, BXT_REVID_B0))
+               I915_WRITE(GEN6_GFXPAUSE, 0x30FFF);
 
        if (IS_BROXTON(dev))
                I915_WRITE(GEN9LP_GT_PM_CONFIG, GT_DOORBELL_ENABLE);
@@ -361,13 +397,13 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
                I915_WRITE(GEN7_MISCCPCTL, (GEN8_DOP_CLOCK_GATE_GUC_ENABLE |
                                            I915_READ(GEN7_MISCCPCTL)));
 
-               /* allows for 5us before GT can go to RC6 */
+               /* allows for 5us (in 10ns units) before GT can go to RC6 */
                I915_WRITE(GUC_ARAT_C6DIS, 0x1FF);
        }
 
-       set_guc_init_params(dev_priv);
+       guc_params_init(dev_priv);
 
-       ret = guc_ucode_xfer_dma(dev_priv);
+       ret = guc_ucode_xfer_dma(dev_priv, vma);
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 
@@ -375,12 +411,12 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv)
         * We keep the object pages for reuse during resume. But we can unpin it
         * now that DMA has completed, so it doesn't continue to take up space.
         */
-       i915_gem_object_ggtt_unpin(guc_fw->guc_fw_obj);
+       i915_vma_unpin(vma);
 
        return ret;
 }
 
-static int i915_reset_guc(struct drm_i915_private *dev_priv)
+static int guc_hw_reset(struct drm_i915_private *dev_priv)
 {
        int ret;
        u32 guc_status;
@@ -433,7 +469,7 @@ int intel_guc_setup(struct drm_device *dev)
                goto fail;
        } else if (*fw_path == '\0') {
                /* Device has a GuC but we don't know what f/w to load? */
-               DRM_INFO("No GuC firmware known for this platform\n");
+               WARN(1, "No GuC firmware known for this platform!\n");
                err = -ENODEV;
                goto fail;
        }
@@ -447,7 +483,7 @@ int intel_guc_setup(struct drm_device *dev)
                goto fail;
        }
 
-       direct_interrupts_to_host(dev_priv);
+       guc_interrupts_release(dev_priv);
 
        guc_fw->guc_fw_load_status = GUC_FIRMWARE_PENDING;
 
@@ -470,11 +506,9 @@ int intel_guc_setup(struct drm_device *dev)
                 * Always reset the GuC just before (re)loading, so
                 * that the state and timing are fairly predictable
                 */
-               err = i915_reset_guc(dev_priv);
-               if (err) {
-                       DRM_ERROR("GuC reset failed: %d\n", err);
+               err = guc_hw_reset(dev_priv);
+               if (err)
                        goto fail;
-               }
 
                err = guc_ucode_xfer(dev_priv);
                if (!err)
@@ -497,7 +531,7 @@ int intel_guc_setup(struct drm_device *dev)
                err = i915_guc_submission_enable(dev_priv);
                if (err)
                        goto fail;
-               direct_interrupts_to_guc(dev_priv);
+               guc_interrupts_capture(dev_priv);
        }
 
        return 0;
@@ -506,7 +540,7 @@ fail:
        if (guc_fw->guc_fw_load_status == GUC_FIRMWARE_PENDING)
                guc_fw->guc_fw_load_status = GUC_FIRMWARE_FAIL;
 
-       direct_interrupts_to_host(dev_priv);
+       guc_interrupts_release(dev_priv);
        i915_guc_submission_disable(dev_priv);
        i915_guc_submission_fini(dev_priv);
 
@@ -532,15 +566,15 @@ fail:
        else if (err == 0)
                DRM_INFO("GuC firmware load skipped\n");
        else if (ret != -EIO)
-               DRM_INFO("GuC firmware load failed: %d\n", err);
+               DRM_NOTE("GuC firmware load failed: %d\n", err);
        else
-               DRM_ERROR("GuC firmware load failed: %d\n", err);
+               DRM_WARN("GuC firmware load failed: %d\n", err);
 
        if (i915.enable_guc_submission) {
                if (fw_path == NULL)
                        DRM_INFO("GuC submission without firmware not supported\n");
                if (ret == 0)
-                       DRM_INFO("Falling back from GuC submission to execlist mode\n");
+                       DRM_NOTE("Falling back from GuC submission to execlist mode\n");
                else
                        DRM_ERROR("GuC init failed: %d\n", ret);
        }
@@ -551,6 +585,7 @@ fail:
 
 static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 {
+       struct pci_dev *pdev = dev->pdev;
        struct drm_i915_gem_object *obj;
        const struct firmware *fw;
        struct guc_css_header *css;
@@ -560,7 +595,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
        DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n",
                intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status));
 
-       err = request_firmware(&fw, guc_fw->guc_fw_path, &dev->pdev->dev);
+       err = request_firmware(&fw, guc_fw->guc_fw_path, &pdev->dev);
        if (err)
                goto fail;
        if (!fw)
@@ -571,7 +606,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 
        /* Check the size of the blob before examining buffer contents */
        if (fw->size < sizeof(struct guc_css_header)) {
-               DRM_ERROR("Firmware header is missing\n");
+               DRM_NOTE("Firmware header is missing\n");
                goto fail;
        }
 
@@ -583,7 +618,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
                css->key_size_dw - css->exponent_size_dw) * sizeof(u32);
 
        if (guc_fw->header_size != sizeof(struct guc_css_header)) {
-               DRM_ERROR("CSS header definition mismatch\n");
+               DRM_NOTE("CSS header definition mismatch\n");
                goto fail;
        }
 
@@ -593,7 +628,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 
        /* now RSA */
        if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) {
-               DRM_ERROR("RSA key size is bad\n");
+               DRM_NOTE("RSA key size is bad\n");
                goto fail;
        }
        guc_fw->rsa_offset = guc_fw->ucode_offset + guc_fw->ucode_size;
@@ -602,14 +637,14 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
        /* At least, it should have header, uCode and RSA. Size of all three. */
        size = guc_fw->header_size + guc_fw->ucode_size + guc_fw->rsa_size;
        if (fw->size < size) {
-               DRM_ERROR("Missing firmware components\n");
+               DRM_NOTE("Missing firmware components\n");
                goto fail;
        }
 
        /* Header and uCode will be loaded to WOPCM. Size of the two. */
        size = guc_fw->header_size + guc_fw->ucode_size;
        if (size > guc_wopcm_size(to_i915(dev))) {
-               DRM_ERROR("Firmware is too large to fit in WOPCM\n");
+               DRM_NOTE("Firmware is too large to fit in WOPCM\n");
                goto fail;
        }
 
@@ -624,7 +659,7 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
 
        if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted ||
            guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) {
-               DRM_ERROR("GuC firmware version %d.%d, required %d.%d\n",
+               DRM_NOTE("GuC firmware version %d.%d, required %d.%d\n",
                        guc_fw->guc_fw_major_found, guc_fw->guc_fw_minor_found,
                        guc_fw->guc_fw_major_wanted, guc_fw->guc_fw_minor_wanted);
                err = -ENOEXEC;
@@ -654,15 +689,15 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw)
        return;
 
 fail:
+       DRM_WARN("Failed to fetch valid GuC firmware from %s (error %d)\n",
+                guc_fw->guc_fw_path, err);
        DRM_DEBUG_DRIVER("GuC fw fetch status FAIL; err %d, fw %p, obj %p\n",
                err, fw, guc_fw->guc_fw_obj);
-       DRM_ERROR("Failed to fetch GuC firmware from %s (error %d)\n",
-                 guc_fw->guc_fw_path, err);
 
        mutex_lock(&dev->struct_mutex);
        obj = guc_fw->guc_fw_obj;
        if (obj)
-               drm_gem_object_unreference(&obj->base);
+               i915_gem_object_put(obj);
        guc_fw->guc_fw_obj = NULL;
        mutex_unlock(&dev->struct_mutex);
 
@@ -695,16 +730,16 @@ void intel_guc_init(struct drm_device *dev)
                fw_path = NULL;
        } else if (IS_SKYLAKE(dev)) {
                fw_path = I915_SKL_GUC_UCODE;
-               guc_fw->guc_fw_major_wanted = 6;
-               guc_fw->guc_fw_minor_wanted = 1;
+               guc_fw->guc_fw_major_wanted = SKL_FW_MAJOR;
+               guc_fw->guc_fw_minor_wanted = SKL_FW_MINOR;
        } else if (IS_BROXTON(dev)) {
                fw_path = I915_BXT_GUC_UCODE;
-               guc_fw->guc_fw_major_wanted = 8;
-               guc_fw->guc_fw_minor_wanted = 7;
+               guc_fw->guc_fw_major_wanted = BXT_FW_MAJOR;
+               guc_fw->guc_fw_minor_wanted = BXT_FW_MINOR;
        } else if (IS_KABYLAKE(dev)) {
                fw_path = I915_KBL_GUC_UCODE;
-               guc_fw->guc_fw_major_wanted = 9;
-               guc_fw->guc_fw_minor_wanted = 14;
+               guc_fw->guc_fw_major_wanted = KBL_FW_MAJOR;
+               guc_fw->guc_fw_minor_wanted = KBL_FW_MINOR;
        } else {
                fw_path = "";   /* unknown device */
        }
@@ -738,12 +773,12 @@ void intel_guc_fini(struct drm_device *dev)
        struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw;
 
        mutex_lock(&dev->struct_mutex);
-       direct_interrupts_to_host(dev_priv);
+       guc_interrupts_release(dev_priv);
        i915_guc_submission_disable(dev_priv);
        i915_guc_submission_fini(dev_priv);
 
        if (guc_fw->guc_fw_obj)
-               drm_gem_object_unreference(&guc_fw->guc_fw_obj->base);
+               i915_gem_object_put(guc_fw->guc_fw_obj);
        guc_fw->guc_fw_obj = NULL;
        mutex_unlock(&dev->struct_mutex);
 
index 4df9f38..f40a35f 100644 (file)
@@ -985,7 +985,9 @@ static void intel_enable_hdmi_audio(struct intel_encoder *encoder)
        intel_audio_codec_enable(encoder);
 }
 
-static void g4x_enable_hdmi(struct intel_encoder *encoder)
+static void g4x_enable_hdmi(struct intel_encoder *encoder,
+                           struct intel_crtc_state *pipe_config,
+                           struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1006,7 +1008,9 @@ static void g4x_enable_hdmi(struct intel_encoder *encoder)
                intel_enable_hdmi_audio(encoder);
 }
 
-static void ibx_enable_hdmi(struct intel_encoder *encoder)
+static void ibx_enable_hdmi(struct intel_encoder *encoder,
+                           struct intel_crtc_state *pipe_config,
+                           struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1055,7 +1059,9 @@ static void ibx_enable_hdmi(struct intel_encoder *encoder)
                intel_enable_hdmi_audio(encoder);
 }
 
-static void cpt_enable_hdmi(struct intel_encoder *encoder)
+static void cpt_enable_hdmi(struct intel_encoder *encoder,
+                           struct intel_crtc_state *pipe_config,
+                           struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1108,11 +1114,15 @@ static void cpt_enable_hdmi(struct intel_encoder *encoder)
                intel_enable_hdmi_audio(encoder);
 }
 
-static void vlv_enable_hdmi(struct intel_encoder *encoder)
+static void vlv_enable_hdmi(struct intel_encoder *encoder,
+                           struct intel_crtc_state *pipe_config,
+                           struct drm_connector_state *conn_state)
 {
 }
 
-static void intel_disable_hdmi(struct intel_encoder *encoder)
+static void intel_disable_hdmi(struct intel_encoder *encoder,
+                              struct intel_crtc_state *old_crtc_state,
+                              struct drm_connector_state *old_conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1164,17 +1174,21 @@ static void intel_disable_hdmi(struct intel_encoder *encoder)
        intel_dp_dual_mode_set_tmds_output(intel_hdmi, false);
 }
 
-static void g4x_disable_hdmi(struct intel_encoder *encoder)
+static void g4x_disable_hdmi(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
 
        if (crtc->config->has_audio)
                intel_audio_codec_disable(encoder);
 
-       intel_disable_hdmi(encoder);
+       intel_disable_hdmi(encoder, old_crtc_state, old_conn_state);
 }
 
-static void pch_disable_hdmi(struct intel_encoder *encoder)
+static void pch_disable_hdmi(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
        struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
 
@@ -1182,9 +1196,11 @@ static void pch_disable_hdmi(struct intel_encoder *encoder)
                intel_audio_codec_disable(encoder);
 }
 
-static void pch_post_disable_hdmi(struct intel_encoder *encoder)
+static void pch_post_disable_hdmi(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
-       intel_disable_hdmi(encoder);
+       intel_disable_hdmi(encoder, old_crtc_state, old_conn_state);
 }
 
 static int intel_hdmi_source_max_tmds_clock(struct drm_i915_private *dev_priv)
@@ -1204,10 +1220,17 @@ static int hdmi_port_clock_limit(struct intel_hdmi *hdmi,
        int max_tmds_clock = intel_hdmi_source_max_tmds_clock(to_i915(dev));
 
        if (respect_downstream_limits) {
+               struct intel_connector *connector = hdmi->attached_connector;
+               const struct drm_display_info *info = &connector->base.display_info;
+
                if (hdmi->dp_dual_mode.max_tmds_clock)
                        max_tmds_clock = min(max_tmds_clock,
                                             hdmi->dp_dual_mode.max_tmds_clock);
-               if (!hdmi->has_hdmi_sink)
+
+               if (info->max_tmds_clock)
+                       max_tmds_clock = min(max_tmds_clock,
+                                            info->max_tmds_clock);
+               else if (!hdmi->has_hdmi_sink)
                        max_tmds_clock = min(max_tmds_clock, 165000);
        }
 
@@ -1285,7 +1308,8 @@ static bool hdmi_12bpc_possible(struct intel_crtc_state *crtc_state)
 }
 
 bool intel_hdmi_compute_config(struct intel_encoder *encoder,
-                              struct intel_crtc_state *pipe_config)
+                              struct intel_crtc_state *pipe_config,
+                              struct drm_connector_state *conn_state)
 {
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct drm_device *dev = encoder->base.dev;
@@ -1422,24 +1446,22 @@ intel_hdmi_dp_dual_mode_detect(struct drm_connector *connector, bool has_edid)
 }
 
 static bool
-intel_hdmi_set_edid(struct drm_connector *connector, bool force)
+intel_hdmi_set_edid(struct drm_connector *connector)
 {
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
        struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
-       struct edid *edid = NULL;
+       struct edid *edid;
        bool connected = false;
 
-       if (force) {
-               intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
+       intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
 
-               edid = drm_get_edid(connector,
-                                   intel_gmbus_get_adapter(dev_priv,
-                                   intel_hdmi->ddc_bus));
+       edid = drm_get_edid(connector,
+                           intel_gmbus_get_adapter(dev_priv,
+                           intel_hdmi->ddc_bus));
 
-               intel_hdmi_dp_dual_mode_detect(connector, edid != NULL);
+       intel_hdmi_dp_dual_mode_detect(connector, edid != NULL);
 
-               intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
-       }
+       intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS);
 
        to_intel_connector(connector)->detect_edid = edid;
        if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
@@ -1465,37 +1487,16 @@ static enum drm_connector_status
 intel_hdmi_detect(struct drm_connector *connector, bool force)
 {
        enum drm_connector_status status;
-       struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
        struct drm_i915_private *dev_priv = to_i915(connector->dev);
-       bool live_status = false;
-       unsigned int try;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
                      connector->base.id, connector->name);
 
        intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS);
 
-       for (try = 0; !live_status && try < 9; try++) {
-               if (try)
-                       msleep(10);
-               live_status = intel_digital_port_connected(dev_priv,
-                               hdmi_to_dig_port(intel_hdmi));
-       }
-
-       if (!live_status) {
-               DRM_DEBUG_KMS("HDMI live status down\n");
-               /*
-                * Live status register is not reliable on all intel platforms.
-                * So consider live_status only for certain platforms, for
-                * others, read EDID to determine presence of sink.
-                */
-               if (INTEL_INFO(dev_priv)->gen < 7 || IS_IVYBRIDGE(dev_priv))
-                       live_status = true;
-       }
-
        intel_hdmi_unset_edid(connector);
 
-       if (intel_hdmi_set_edid(connector, live_status)) {
+       if (intel_hdmi_set_edid(connector)) {
                struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
 
                hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
@@ -1521,7 +1522,7 @@ intel_hdmi_force(struct drm_connector *connector)
        if (connector->status != connector_status_connected)
                return;
 
-       intel_hdmi_set_edid(connector, true);
+       intel_hdmi_set_edid(connector);
        hdmi_to_dig_port(intel_hdmi)->base.type = INTEL_OUTPUT_HDMI;
 }
 
@@ -1638,7 +1639,9 @@ done:
        return 0;
 }
 
-static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
+static void intel_hdmi_pre_enable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config,
+                                 struct drm_connector_state *conn_state)
 {
        struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
@@ -1651,7 +1654,9 @@ static void intel_hdmi_pre_enable(struct intel_encoder *encoder)
                                   adjusted_mode);
 }
 
-static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
+static void vlv_hdmi_pre_enable(struct intel_encoder *encoder,
+                               struct intel_crtc_state *pipe_config,
+                               struct drm_connector_state *conn_state)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct intel_hdmi *intel_hdmi = &dport->hdmi;
@@ -1671,37 +1676,47 @@ static void vlv_hdmi_pre_enable(struct intel_encoder *encoder)
                                   intel_crtc->config->has_hdmi_sink,
                                   adjusted_mode);
 
-       g4x_enable_hdmi(encoder);
+       g4x_enable_hdmi(encoder, pipe_config, conn_state);
 
        vlv_wait_port_ready(dev_priv, dport, 0x0);
 }
 
-static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
+static void vlv_hdmi_pre_pll_enable(struct intel_encoder *encoder,
+                                   struct intel_crtc_state *pipe_config,
+                                   struct drm_connector_state *conn_state)
 {
        intel_hdmi_prepare(encoder);
 
        vlv_phy_pre_pll_enable(encoder);
 }
 
-static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder)
+static void chv_hdmi_pre_pll_enable(struct intel_encoder *encoder,
+                                   struct intel_crtc_state *pipe_config,
+                                   struct drm_connector_state *conn_state)
 {
        intel_hdmi_prepare(encoder);
 
        chv_phy_pre_pll_enable(encoder);
 }
 
-static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder)
+static void chv_hdmi_post_pll_disable(struct intel_encoder *encoder,
+                                     struct intel_crtc_state *old_crtc_state,
+                                     struct drm_connector_state *old_conn_state)
 {
        chv_phy_post_pll_disable(encoder);
 }
 
-static void vlv_hdmi_post_disable(struct intel_encoder *encoder)
+static void vlv_hdmi_post_disable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
        /* Reset lanes to avoid HDMI flicker (VLV w/a) */
        vlv_phy_reset_lanes(encoder);
 }
 
-static void chv_hdmi_post_disable(struct intel_encoder *encoder)
+static void chv_hdmi_post_disable(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -1714,7 +1729,9 @@ static void chv_hdmi_post_disable(struct intel_encoder *encoder)
        mutex_unlock(&dev_priv->sb_lock);
 }
 
-static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
+static void chv_hdmi_pre_enable(struct intel_encoder *encoder,
+                               struct intel_crtc_state *pipe_config,
+                               struct drm_connector_state *conn_state)
 {
        struct intel_digital_port *dport = enc_to_dig_port(&encoder->base);
        struct intel_hdmi *intel_hdmi = &dport->hdmi;
@@ -1734,7 +1751,7 @@ static void chv_hdmi_pre_enable(struct intel_encoder *encoder)
                                   intel_crtc->config->has_hdmi_sink,
                                   adjusted_mode);
 
-       g4x_enable_hdmi(encoder);
+       g4x_enable_hdmi(encoder, pipe_config, conn_state);
 
        vlv_wait_port_ready(dev_priv, dport, 0x0);
 
index f48957e..334d47b 100644 (file)
@@ -477,7 +477,8 @@ void intel_hpd_init(struct drm_i915_private *dev_priv)
        spin_unlock_irq(&dev_priv->irq_lock);
 }
 
-void i915_hpd_poll_init_work(struct work_struct *work) {
+static void i915_hpd_poll_init_work(struct work_struct *work)
+{
        struct drm_i915_private *dev_priv =
                container_of(work, struct drm_i915_private,
                             hotplug.poll_init_work);
@@ -525,7 +526,6 @@ void i915_hpd_poll_init_work(struct work_struct *work) {
 /**
  * intel_hpd_poll_init - enables/disables polling for connectors with hpd
  * @dev_priv: i915 device instance
- * @enabled: Whether to enable or disable polling
  *
  * This function enables polling for all connectors, regardless of whether or
  * not they support hotplug detection. Under certain conditions HPD may not be
index 1f266d7..79aab9a 100644 (file)
@@ -255,67 +255,59 @@ intel_gpio_setup(struct intel_gmbus *bus, unsigned int pin)
        algo->data = bus;
 }
 
-static int
-gmbus_wait_hw_status(struct drm_i915_private *dev_priv,
-                    u32 gmbus2_status,
-                    u32 gmbus4_irq_en)
+static int gmbus_wait(struct drm_i915_private *dev_priv, u32 status, u32 irq_en)
 {
-       int i;
-       u32 gmbus2 = 0;
        DEFINE_WAIT(wait);
-
-       if (!HAS_GMBUS_IRQ(dev_priv))
-               gmbus4_irq_en = 0;
+       u32 gmbus2;
+       int ret;
 
        /* Important: The hw handles only the first bit, so set only one! Since
         * we also need to check for NAKs besides the hw ready/idle signal, we
-        * need to wake up periodically and check that ourselves. */
-       I915_WRITE(GMBUS4, gmbus4_irq_en);
-
-       for (i = 0; i < msecs_to_jiffies_timeout(50); i++) {
-               prepare_to_wait(&dev_priv->gmbus_wait_queue, &wait,
-                               TASK_UNINTERRUPTIBLE);
+        * need to wake up periodically and check that ourselves.
+        */
+       if (!HAS_GMBUS_IRQ(dev_priv))
+               irq_en = 0;
 
-               gmbus2 = I915_READ_NOTRACE(GMBUS2);
-               if (gmbus2 & (GMBUS_SATOER | gmbus2_status))
-                       break;
+       add_wait_queue(&dev_priv->gmbus_wait_queue, &wait);
+       I915_WRITE_FW(GMBUS4, irq_en);
 
-               schedule_timeout(1);
-       }
-       finish_wait(&dev_priv->gmbus_wait_queue, &wait);
+       status |= GMBUS_SATOER;
+       ret = wait_for_us((gmbus2 = I915_READ_FW(GMBUS2)) & status, 2);
+       if (ret)
+               ret = wait_for((gmbus2 = I915_READ_FW(GMBUS2)) & status, 50);
 
-       I915_WRITE(GMBUS4, 0);
+       I915_WRITE_FW(GMBUS4, 0);
+       remove_wait_queue(&dev_priv->gmbus_wait_queue, &wait);
 
        if (gmbus2 & GMBUS_SATOER)
                return -ENXIO;
-       if (gmbus2 & gmbus2_status)
-               return 0;
-       return -ETIMEDOUT;
+
+       return ret;
 }
 
 static int
 gmbus_wait_idle(struct drm_i915_private *dev_priv)
 {
+       DEFINE_WAIT(wait);
+       u32 irq_enable;
        int ret;
 
-       if (!HAS_GMBUS_IRQ(dev_priv))
-               return intel_wait_for_register(dev_priv,
-                                              GMBUS2, GMBUS_ACTIVE, 0,
-                                              10);
-
        /* Important: The hw handles only the first bit, so set only one! */
-       I915_WRITE(GMBUS4, GMBUS_IDLE_EN);
+       irq_enable = 0;
+       if (HAS_GMBUS_IRQ(dev_priv))
+               irq_enable = GMBUS_IDLE_EN;
 
-       ret = wait_event_timeout(dev_priv->gmbus_wait_queue,
-                                (I915_READ_NOTRACE(GMBUS2) & GMBUS_ACTIVE) == 0,
-                                msecs_to_jiffies_timeout(10));
+       add_wait_queue(&dev_priv->gmbus_wait_queue, &wait);
+       I915_WRITE_FW(GMBUS4, irq_enable);
 
-       I915_WRITE(GMBUS4, 0);
+       ret = intel_wait_for_register_fw(dev_priv,
+                                        GMBUS2, GMBUS_ACTIVE, 0,
+                                        10);
 
-       if (ret)
-               return 0;
-       else
-               return -ETIMEDOUT;
+       I915_WRITE_FW(GMBUS4, 0);
+       remove_wait_queue(&dev_priv->gmbus_wait_queue, &wait);
+
+       return ret;
 }
 
 static int
@@ -323,22 +315,21 @@ gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv,
                      unsigned short addr, u8 *buf, unsigned int len,
                      u32 gmbus1_index)
 {
-       I915_WRITE(GMBUS1,
-                  gmbus1_index |
-                  GMBUS_CYCLE_WAIT |
-                  (len << GMBUS_BYTE_COUNT_SHIFT) |
-                  (addr << GMBUS_SLAVE_ADDR_SHIFT) |
-                  GMBUS_SLAVE_READ | GMBUS_SW_RDY);
+       I915_WRITE_FW(GMBUS1,
+                     gmbus1_index |
+                     GMBUS_CYCLE_WAIT |
+                     (len << GMBUS_BYTE_COUNT_SHIFT) |
+                     (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+                     GMBUS_SLAVE_READ | GMBUS_SW_RDY);
        while (len) {
                int ret;
                u32 val, loop = 0;
 
-               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
-                                          GMBUS_HW_RDY_EN);
+               ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN);
                if (ret)
                        return ret;
 
-               val = I915_READ(GMBUS3);
+               val = I915_READ_FW(GMBUS3);
                do {
                        *buf++ = val & 0xff;
                        val >>= 8;
@@ -385,12 +376,12 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
                len -= 1;
        }
 
-       I915_WRITE(GMBUS3, val);
-       I915_WRITE(GMBUS1,
-                  GMBUS_CYCLE_WAIT |
-                  (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
-                  (addr << GMBUS_SLAVE_ADDR_SHIFT) |
-                  GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
+       I915_WRITE_FW(GMBUS3, val);
+       I915_WRITE_FW(GMBUS1,
+                     GMBUS_CYCLE_WAIT |
+                     (chunk_size << GMBUS_BYTE_COUNT_SHIFT) |
+                     (addr << GMBUS_SLAVE_ADDR_SHIFT) |
+                     GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
        while (len) {
                int ret;
 
@@ -399,10 +390,9 @@ gmbus_xfer_write_chunk(struct drm_i915_private *dev_priv,
                        val |= *buf++ << (8 * loop);
                } while (--len && ++loop < 4);
 
-               I915_WRITE(GMBUS3, val);
+               I915_WRITE_FW(GMBUS3, val);
 
-               ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_RDY,
-                                          GMBUS_HW_RDY_EN);
+               ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN);
                if (ret)
                        return ret;
        }
@@ -460,13 +450,13 @@ gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
 
        /* GMBUS5 holds 16-bit index */
        if (gmbus5)
-               I915_WRITE(GMBUS5, gmbus5);
+               I915_WRITE_FW(GMBUS5, gmbus5);
 
        ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index);
 
        /* Clear GMBUS5 after each index transfer */
        if (gmbus5)
-               I915_WRITE(GMBUS5, 0);
+               I915_WRITE_FW(GMBUS5, 0);
 
        return ret;
 }
@@ -478,11 +468,15 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
                                               struct intel_gmbus,
                                               adapter);
        struct drm_i915_private *dev_priv = bus->dev_priv;
+       const unsigned int fw =
+               intel_uncore_forcewake_for_reg(dev_priv, GMBUS0,
+                                              FW_REG_READ | FW_REG_WRITE);
        int i = 0, inc, try = 0;
        int ret = 0;
 
+       intel_uncore_forcewake_get(dev_priv, fw);
 retry:
-       I915_WRITE(GMBUS0, bus->reg0);
+       I915_WRITE_FW(GMBUS0, bus->reg0);
 
        for (; i < num; i += inc) {
                inc = 1;
@@ -496,8 +490,8 @@ retry:
                }
 
                if (!ret)
-                       ret = gmbus_wait_hw_status(dev_priv, GMBUS_HW_WAIT_PHASE,
-                                                  GMBUS_HW_WAIT_EN);
+                       ret = gmbus_wait(dev_priv,
+                                        GMBUS_HW_WAIT_PHASE, GMBUS_HW_WAIT_EN);
                if (ret == -ETIMEDOUT)
                        goto timeout;
                else if (ret)
@@ -508,7 +502,7 @@ retry:
         * a STOP on the very first cycle. To simplify the code we
         * unconditionally generate the STOP condition with an additional gmbus
         * cycle. */
-       I915_WRITE(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY);
+       I915_WRITE_FW(GMBUS1, GMBUS_CYCLE_STOP | GMBUS_SW_RDY);
 
        /* Mark the GMBUS interface as disabled after waiting for idle.
         * We will re-enable it at the start of the next xfer,
@@ -519,7 +513,7 @@ retry:
                         adapter->name);
                ret = -ETIMEDOUT;
        }
-       I915_WRITE(GMBUS0, 0);
+       I915_WRITE_FW(GMBUS0, 0);
        ret = ret ?: i;
        goto out;
 
@@ -548,9 +542,9 @@ clear_err:
         * of resetting the GMBUS controller and so clearing the
         * BUS_ERROR raised by the slave's NAK.
         */
-       I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT);
-       I915_WRITE(GMBUS1, 0);
-       I915_WRITE(GMBUS0, 0);
+       I915_WRITE_FW(GMBUS1, GMBUS_SW_CLR_INT);
+       I915_WRITE_FW(GMBUS1, 0);
+       I915_WRITE_FW(GMBUS0, 0);
 
        DRM_DEBUG_KMS("GMBUS [%s] NAK for addr: %04x %c(%d)\n",
                         adapter->name, msgs[i].addr,
@@ -573,7 +567,7 @@ clear_err:
 timeout:
        DRM_DEBUG_KMS("GMBUS [%s] timed out, falling back to bit banging on pin %d\n",
                      bus->adapter.name, bus->reg0 & 0xff);
-       I915_WRITE(GMBUS0, 0);
+       I915_WRITE_FW(GMBUS0, 0);
 
        /*
         * Hardware may not support GMBUS over these pins? Try GPIO bitbanging
@@ -582,6 +576,7 @@ timeout:
        ret = -EAGAIN;
 
 out:
+       intel_uncore_forcewake_put(dev_priv, fw);
        return ret;
 }
 
@@ -633,6 +628,7 @@ static const struct i2c_algorithm gmbus_algorithm = {
 int intel_setup_gmbus(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct intel_gmbus *bus;
        unsigned int pin;
        int ret;
@@ -663,7 +659,7 @@ int intel_setup_gmbus(struct drm_device *dev)
                         "i915 gmbus %s",
                         get_gmbus_pin(dev_priv, pin)->name);
 
-               bus->adapter.dev.parent = &dev->pdev->dev;
+               bus->adapter.dev.parent = &pdev->dev;
                bus->dev_priv = dev_priv;
 
                bus->adapter.algo = &gmbus_algorithm;
index 414ddda..0adb879 100644 (file)
 #define GEN8_CTX_STATUS_COMPLETE       (1 << 4)
 #define GEN8_CTX_STATUS_LITE_RESTORE   (1 << 15)
 
+#define GEN8_CTX_STATUS_COMPLETED_MASK \
+        (GEN8_CTX_STATUS_ACTIVE_IDLE | \
+         GEN8_CTX_STATUS_PREEMPTED | \
+         GEN8_CTX_STATUS_ELEMENT_SWITCH)
+
 #define CTX_LRI_HEADER_0               0x01
 #define CTX_CONTEXT_CONTROL            0x02
 #define CTX_RING_HEAD                  0x04
@@ -221,10 +226,16 @@ enum {
 /* Typical size of the average request (2 pipecontrols and a MI_BB) */
 #define EXECLISTS_REQUEST_SIZE 64 /* bytes */
 
+#define WA_TAIL_DWORDS 2
+
 static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
                                            struct intel_engine_cs *engine);
 static int intel_lr_context_pin(struct i915_gem_context *ctx,
                                struct intel_engine_cs *engine);
+static void execlists_init_reg_state(u32 *reg_state,
+                                    struct i915_gem_context *ctx,
+                                    struct intel_engine_cs *engine,
+                                    struct intel_ring *ring);
 
 /**
  * intel_sanitize_enable_execlists() - sanitize i915.enable_execlists
@@ -263,12 +274,10 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
 
-       if (IS_GEN8(dev_priv) || IS_GEN9(dev_priv))
-               engine->idle_lite_restore_wa = ~0;
-
-       engine->disable_lite_restore_wa = (IS_SKL_REVID(dev_priv, 0, SKL_REVID_B0) ||
-                                       IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) &&
-                                       (engine->id == VCS || engine->id == VCS2);
+       engine->disable_lite_restore_wa =
+               (IS_SKL_REVID(dev_priv, 0, SKL_REVID_B0) ||
+                IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) &&
+               (engine->id == VCS || engine->id == VCS2);
 
        engine->ctx_desc_template = GEN8_CTX_VALID;
        if (IS_GEN8(dev_priv))
@@ -288,7 +297,6 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine)
 /**
  * intel_lr_context_descriptor_update() - calculate & cache the descriptor
  *                                       descriptor for a pinned context
- *
  * @ctx: Context to work on
  * @engine: Engine the descriptor will be used with
  *
@@ -297,12 +305,13 @@ logical_ring_init_platform_invariants(struct intel_engine_cs *engine)
  * expensive to calculate, we'll just do it once and cache the result,
  * which remains valid until the context is unpinned.
  *
- * This is what a descriptor looks like, from LSB to MSB:
- *    bits  0-11:    flags, GEN8_CTX_* (cached in ctx_desc_template)
- *    bits 12-31:    LRCA, GTT address of (the HWSP of) this context
- *    bits 32-52:    ctx ID, a globally unique tag
- *    bits 53-54:    mbz, reserved for use by hardware
- *    bits 55-63:    group ID, currently unused and set to 0
+ * This is what a descriptor looks like, from LSB to MSB::
+ *
+ *      bits  0-11:    flags, GEN8_CTX_* (cached in ctx_desc_template)
+ *      bits 12-31:    LRCA, GTT address of (the HWSP of) this context
+ *      bits 32-52:    ctx ID, a globally unique tag
+ *      bits 53-54:    mbz, reserved for use by hardware
+ *      bits 55-63:    group ID, currently unused and set to 0
  */
 static void
 intel_lr_context_descriptor_update(struct i915_gem_context *ctx,
@@ -315,7 +324,7 @@ intel_lr_context_descriptor_update(struct i915_gem_context *ctx,
 
        desc = ctx->desc_template;                              /* bits  3-4  */
        desc |= engine->ctx_desc_template;                      /* bits  0-11 */
-       desc |= ce->lrc_vma->node.start + LRC_PPHWSP_PN * PAGE_SIZE;
+       desc |= i915_ggtt_offset(ce->state) + LRC_PPHWSP_PN * PAGE_SIZE;
                                                                /* bits 12-31 */
        desc |= (u64)ctx->hw_id << GEN8_CTX_ID_SHIFT;           /* bits 32-52 */
 
@@ -328,34 +337,18 @@ uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx,
        return ctx->engine[engine->id].lrc_desc;
 }
 
-static void execlists_elsp_write(struct drm_i915_gem_request *rq0,
-                                struct drm_i915_gem_request *rq1)
+static inline void
+execlists_context_status_change(struct drm_i915_gem_request *rq,
+                               unsigned long status)
 {
+       /*
+        * Only used when GVT-g is enabled now. When GVT-g is disabled,
+        * The compiler should eliminate this function as dead-code.
+        */
+       if (!IS_ENABLED(CONFIG_DRM_I915_GVT))
+               return;
 
-       struct intel_engine_cs *engine = rq0->engine;
-       struct drm_i915_private *dev_priv = rq0->i915;
-       uint64_t desc[2];
-
-       if (rq1) {
-               desc[1] = intel_lr_context_descriptor(rq1->ctx, rq1->engine);
-               rq1->elsp_submitted++;
-       } else {
-               desc[1] = 0;
-       }
-
-       desc[0] = intel_lr_context_descriptor(rq0->ctx, rq0->engine);
-       rq0->elsp_submitted++;
-
-       /* You must always write both descriptors in the order below. */
-       I915_WRITE_FW(RING_ELSP(engine), upper_32_bits(desc[1]));
-       I915_WRITE_FW(RING_ELSP(engine), lower_32_bits(desc[1]));
-
-       I915_WRITE_FW(RING_ELSP(engine), upper_32_bits(desc[0]));
-       /* The context is automatically loaded after the following */
-       I915_WRITE_FW(RING_ELSP(engine), lower_32_bits(desc[0]));
-
-       /* ELSP is a wo register, use another nearby reg for posting */
-       POSTING_READ_FW(RING_EXECLIST_STATUS_LO(engine));
+       atomic_notifier_call_chain(&rq->ctx->status_notifier, status, rq);
 }
 
 static void
@@ -367,13 +360,13 @@ execlists_update_context_pdps(struct i915_hw_ppgtt *ppgtt, u32 *reg_state)
        ASSIGN_CTX_PDP(ppgtt, reg_state, 0);
 }
 
-static void execlists_update_context(struct drm_i915_gem_request *rq)
+static u64 execlists_update_context(struct drm_i915_gem_request *rq)
 {
-       struct intel_engine_cs *engine = rq->engine;
+       struct intel_context *ce = &rq->ctx->engine[rq->engine->id];
        struct i915_hw_ppgtt *ppgtt = rq->ctx->ppgtt;
-       uint32_t *reg_state = rq->ctx->engine[engine->id].lrc_reg_state;
+       u32 *reg_state = ce->lrc_reg_state;
 
-       reg_state[CTX_RING_TAIL+1] = rq->tail;
+       reg_state[CTX_RING_TAIL+1] = intel_ring_offset(rq->ring, rq->tail);
 
        /* True 32b PPGTT with dynamic page allocation: update PDP
         * registers and point the unallocated PDPs to scratch page.
@@ -382,321 +375,236 @@ static void execlists_update_context(struct drm_i915_gem_request *rq)
         */
        if (ppgtt && !USES_FULL_48BIT_PPGTT(ppgtt->base.dev))
                execlists_update_context_pdps(ppgtt, reg_state);
+
+       return ce->lrc_desc;
 }
 
-static void execlists_submit_requests(struct drm_i915_gem_request *rq0,
-                                     struct drm_i915_gem_request *rq1)
+static void execlists_submit_ports(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = rq0->i915;
-       unsigned int fw_domains = rq0->engine->fw_domains;
-
-       execlists_update_context(rq0);
+       struct drm_i915_private *dev_priv = engine->i915;
+       struct execlist_port *port = engine->execlist_port;
+       u32 __iomem *elsp =
+               dev_priv->regs + i915_mmio_reg_offset(RING_ELSP(engine));
+       u64 desc[2];
 
-       if (rq1)
-               execlists_update_context(rq1);
+       if (!port[0].count)
+               execlists_context_status_change(port[0].request,
+                                               INTEL_CONTEXT_SCHEDULE_IN);
+       desc[0] = execlists_update_context(port[0].request);
+       engine->preempt_wa = port[0].count++; /* bdw only? fixed on skl? */
 
-       spin_lock_irq(&dev_priv->uncore.lock);
-       intel_uncore_forcewake_get__locked(dev_priv, fw_domains);
+       if (port[1].request) {
+               GEM_BUG_ON(port[1].count);
+               execlists_context_status_change(port[1].request,
+                                               INTEL_CONTEXT_SCHEDULE_IN);
+               desc[1] = execlists_update_context(port[1].request);
+               port[1].count = 1;
+       } else {
+               desc[1] = 0;
+       }
+       GEM_BUG_ON(desc[0] == desc[1]);
 
-       execlists_elsp_write(rq0, rq1);
+       /* You must always write both descriptors in the order below. */
+       writel(upper_32_bits(desc[1]), elsp);
+       writel(lower_32_bits(desc[1]), elsp);
 
-       intel_uncore_forcewake_put__locked(dev_priv, fw_domains);
-       spin_unlock_irq(&dev_priv->uncore.lock);
+       writel(upper_32_bits(desc[0]), elsp);
+       /* The context is automatically loaded after the following */
+       writel(lower_32_bits(desc[0]), elsp);
 }
 
-static inline void execlists_context_status_change(
-               struct drm_i915_gem_request *rq,
-               unsigned long status)
+static bool ctx_single_port_submission(const struct i915_gem_context *ctx)
 {
-       /*
-        * Only used when GVT-g is enabled now. When GVT-g is disabled,
-        * The compiler should eliminate this function as dead-code.
-        */
-       if (!IS_ENABLED(CONFIG_DRM_I915_GVT))
-               return;
-
-       atomic_notifier_call_chain(&rq->ctx->status_notifier, status, rq);
+       return (IS_ENABLED(CONFIG_DRM_I915_GVT) &&
+               ctx->execlists_force_single_submission);
 }
 
-static void execlists_context_unqueue(struct intel_engine_cs *engine)
+static bool can_merge_ctx(const struct i915_gem_context *prev,
+                         const struct i915_gem_context *next)
 {
-       struct drm_i915_gem_request *req0 = NULL, *req1 = NULL;
-       struct drm_i915_gem_request *cursor, *tmp;
+       if (prev != next)
+               return false;
 
-       assert_spin_locked(&engine->execlist_lock);
+       if (ctx_single_port_submission(prev))
+               return false;
 
-       /*
-        * If irqs are not active generate a warning as batches that finish
-        * without the irqs may get lost and a GPU Hang may occur.
-        */
-       WARN_ON(!intel_irqs_enabled(engine->i915));
-
-       /* Try to read in pairs */
-       list_for_each_entry_safe(cursor, tmp, &engine->execlist_queue,
-                                execlist_link) {
-               if (!req0) {
-                       req0 = cursor;
-               } else if (req0->ctx == cursor->ctx) {
-                       /* Same ctx: ignore first request, as second request
-                        * will update tail past first request's workload */
-                       cursor->elsp_submitted = req0->elsp_submitted;
-                       list_del(&req0->execlist_link);
-                       i915_gem_request_unreference(req0);
-                       req0 = cursor;
-               } else {
-                       if (IS_ENABLED(CONFIG_DRM_I915_GVT)) {
-                               /*
-                                * req0 (after merged) ctx requires single
-                                * submission, stop picking
-                                */
-                               if (req0->ctx->execlists_force_single_submission)
-                                       break;
-                               /*
-                                * req0 ctx doesn't require single submission,
-                                * but next req ctx requires, stop picking
-                                */
-                               if (cursor->ctx->execlists_force_single_submission)
-                                       break;
-                       }
-                       req1 = cursor;
-                       WARN_ON(req1->elsp_submitted);
-                       break;
-               }
-       }
+       return true;
+}
 
-       if (unlikely(!req0))
-               return;
+static void execlists_dequeue(struct intel_engine_cs *engine)
+{
+       struct drm_i915_gem_request *cursor, *last;
+       struct execlist_port *port = engine->execlist_port;
+       bool submit = false;
+
+       last = port->request;
+       if (last)
+               /* WaIdleLiteRestore:bdw,skl
+                * Apply the wa NOOPs to prevent ring:HEAD == req:TAIL
+                * as we resubmit the request. See gen8_emit_request()
+                * for where we prepare the padding after the end of the
+                * request.
+                */
+               last->tail = last->wa_tail;
 
-       execlists_context_status_change(req0, INTEL_CONTEXT_SCHEDULE_IN);
+       GEM_BUG_ON(port[1].request);
 
-       if (req1)
-               execlists_context_status_change(req1,
-                                               INTEL_CONTEXT_SCHEDULE_IN);
+       /* Hardware submission is through 2 ports. Conceptually each port
+        * has a (RING_START, RING_HEAD, RING_TAIL) tuple. RING_START is
+        * static for a context, and unique to each, so we only execute
+        * requests belonging to a single context from each ring. RING_HEAD
+        * is maintained by the CS in the context image, it marks the place
+        * where it got up to last time, and through RING_TAIL we tell the CS
+        * where we want to execute up to this time.
+        *
+        * In this list the requests are in order of execution. Consecutive
+        * requests from the same context are adjacent in the ringbuffer. We
+        * can combine these requests into a single RING_TAIL update:
+        *
+        *              RING_HEAD...req1...req2
+        *                                    ^- RING_TAIL
+        * since to execute req2 the CS must first execute req1.
+        *
+        * Our goal then is to point each port to the end of a consecutive
+        * sequence of requests as being the most optimal (fewest wake ups
+        * and context switches) submission.
+        */
 
-       if (req0->elsp_submitted & engine->idle_lite_restore_wa) {
-               /*
-                * WaIdleLiteRestore: make sure we never cause a lite restore
-                * with HEAD==TAIL.
+       spin_lock(&engine->execlist_lock);
+       list_for_each_entry(cursor, &engine->execlist_queue, execlist_link) {
+               /* Can we combine this request with the current port? It has to
+                * be the same context/ringbuffer and not have any exceptions
+                * (e.g. GVT saying never to combine contexts).
                 *
-                * Apply the wa NOOPS to prevent ring:HEAD == req:TAIL as we
-                * resubmit the request. See gen8_emit_request() for where we
-                * prepare the padding after the end of the request.
+                * If we can combine the requests, we can execute both by
+                * updating the RING_TAIL to point to the end of the second
+                * request, and so we never need to tell the hardware about
+                * the first.
                 */
-               struct intel_ringbuffer *ringbuf;
+               if (last && !can_merge_ctx(cursor->ctx, last->ctx)) {
+                       /* If we are on the second port and cannot combine
+                        * this request with the last, then we are done.
+                        */
+                       if (port != engine->execlist_port)
+                               break;
+
+                       /* If GVT overrides us we only ever submit port[0],
+                        * leaving port[1] empty. Note that we also have
+                        * to be careful that we don't queue the same
+                        * context (even though a different request) to
+                        * the second port.
+                        */
+                       if (ctx_single_port_submission(cursor->ctx))
+                               break;
+
+                       GEM_BUG_ON(last->ctx == cursor->ctx);
+
+                       i915_gem_request_assign(&port->request, last);
+                       port++;
+               }
+               last = cursor;
+               submit = true;
+       }
+       if (submit) {
+               /* Decouple all the requests submitted from the queue */
+               engine->execlist_queue.next = &cursor->execlist_link;
+               cursor->execlist_link.prev = &engine->execlist_queue;
 
-               ringbuf = req0->ctx->engine[engine->id].ringbuf;
-               req0->tail += 8;
-               req0->tail &= ringbuf->size - 1;
+               i915_gem_request_assign(&port->request, last);
        }
+       spin_unlock(&engine->execlist_lock);
 
-       execlists_submit_requests(req0, req1);
+       if (submit)
+               execlists_submit_ports(engine);
 }
 
-static unsigned int
-execlists_check_remove_request(struct intel_engine_cs *engine, u32 ctx_id)
+static bool execlists_elsp_idle(struct intel_engine_cs *engine)
 {
-       struct drm_i915_gem_request *head_req;
-
-       assert_spin_locked(&engine->execlist_lock);
-
-       head_req = list_first_entry_or_null(&engine->execlist_queue,
-                                           struct drm_i915_gem_request,
-                                           execlist_link);
-
-       if (WARN_ON(!head_req || (head_req->ctx_hw_id != ctx_id)))
-               return 0;
-
-       WARN(head_req->elsp_submitted == 0, "Never submitted head request\n");
-
-       if (--head_req->elsp_submitted > 0)
-               return 0;
-
-       execlists_context_status_change(head_req, INTEL_CONTEXT_SCHEDULE_OUT);
-
-       list_del(&head_req->execlist_link);
-       i915_gem_request_unreference(head_req);
-
-       return 1;
+       return !engine->execlist_port[0].request;
 }
 
-static u32
-get_context_status(struct intel_engine_cs *engine, unsigned int read_pointer,
-                  u32 *context_id)
+static bool execlists_elsp_ready(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = engine->i915;
-       u32 status;
+       int port;
 
-       read_pointer %= GEN8_CSB_ENTRIES;
+       port = 1; /* wait for a free slot */
+       if (engine->disable_lite_restore_wa || engine->preempt_wa)
+               port = 0; /* wait for GPU to be idle before continuing */
 
-       status = I915_READ_FW(RING_CONTEXT_STATUS_BUF_LO(engine, read_pointer));
-
-       if (status & GEN8_CTX_STATUS_IDLE_ACTIVE)
-               return 0;
-
-       *context_id = I915_READ_FW(RING_CONTEXT_STATUS_BUF_HI(engine,
-                                                             read_pointer));
-
-       return status;
+       return !engine->execlist_port[port].request;
 }
 
-/**
- * intel_lrc_irq_handler() - handle Context Switch interrupts
- * @data: tasklet handler passed in unsigned long
- *
+/*
  * Check the unread Context Status Buffers and manage the submission of new
  * contexts to the ELSP accordingly.
  */
 static void intel_lrc_irq_handler(unsigned long data)
 {
        struct intel_engine_cs *engine = (struct intel_engine_cs *)data;
+       struct execlist_port *port = engine->execlist_port;
        struct drm_i915_private *dev_priv = engine->i915;
-       u32 status_pointer;
-       unsigned int read_pointer, write_pointer;
-       u32 csb[GEN8_CSB_ENTRIES][2];
-       unsigned int csb_read = 0, i;
-       unsigned int submit_contexts = 0;
 
        intel_uncore_forcewake_get(dev_priv, engine->fw_domains);
 
-       status_pointer = I915_READ_FW(RING_CONTEXT_STATUS_PTR(engine));
-
-       read_pointer = engine->next_context_status_buffer;
-       write_pointer = GEN8_CSB_WRITE_PTR(status_pointer);
-       if (read_pointer > write_pointer)
-               write_pointer += GEN8_CSB_ENTRIES;
-
-       while (read_pointer < write_pointer) {
-               if (WARN_ON_ONCE(csb_read == GEN8_CSB_ENTRIES))
-                       break;
-               csb[csb_read][0] = get_context_status(engine, ++read_pointer,
-                                                     &csb[csb_read][1]);
-               csb_read++;
-       }
-
-       engine->next_context_status_buffer = write_pointer % GEN8_CSB_ENTRIES;
-
-       /* Update the read pointer to the old write pointer. Manual ringbuffer
-        * management ftw </sarcasm> */
-       I915_WRITE_FW(RING_CONTEXT_STATUS_PTR(engine),
-                     _MASKED_FIELD(GEN8_CSB_READ_PTR_MASK,
-                                   engine->next_context_status_buffer << 8));
-
-       intel_uncore_forcewake_put(dev_priv, engine->fw_domains);
-
-       spin_lock(&engine->execlist_lock);
+       if (!execlists_elsp_idle(engine)) {
+               u32 __iomem *csb_mmio =
+                       dev_priv->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_PTR(engine));
+               u32 __iomem *buf =
+                       dev_priv->regs + i915_mmio_reg_offset(RING_CONTEXT_STATUS_BUF_LO(engine, 0));
+               unsigned int csb, head, tail;
+
+               csb = readl(csb_mmio);
+               head = GEN8_CSB_READ_PTR(csb);
+               tail = GEN8_CSB_WRITE_PTR(csb);
+               if (tail < head)
+                       tail += GEN8_CSB_ENTRIES;
+               while (head < tail) {
+                       unsigned int idx = ++head % GEN8_CSB_ENTRIES;
+                       unsigned int status = readl(buf + 2 * idx);
+
+                       if (!(status & GEN8_CTX_STATUS_COMPLETED_MASK))
+                               continue;
+
+                       GEM_BUG_ON(port[0].count == 0);
+                       if (--port[0].count == 0) {
+                               GEM_BUG_ON(status & GEN8_CTX_STATUS_PREEMPTED);
+                               execlists_context_status_change(port[0].request,
+                                                               INTEL_CONTEXT_SCHEDULE_OUT);
+
+                               i915_gem_request_put(port[0].request);
+                               port[0] = port[1];
+                               memset(&port[1], 0, sizeof(port[1]));
+
+                               engine->preempt_wa = false;
+                       }
 
-       for (i = 0; i < csb_read; i++) {
-               if (unlikely(csb[i][0] & GEN8_CTX_STATUS_PREEMPTED)) {
-                       if (csb[i][0] & GEN8_CTX_STATUS_LITE_RESTORE) {
-                               if (execlists_check_remove_request(engine, csb[i][1]))
-                                       WARN(1, "Lite Restored request removed from queue\n");
-                       } else
-                               WARN(1, "Preemption without Lite Restore\n");
+                       GEM_BUG_ON(port[0].count == 0 &&
+                                  !(status & GEN8_CTX_STATUS_ACTIVE_IDLE));
                }
 
-               if (csb[i][0] & (GEN8_CTX_STATUS_ACTIVE_IDLE |
-                   GEN8_CTX_STATUS_ELEMENT_SWITCH))
-                       submit_contexts +=
-                               execlists_check_remove_request(engine, csb[i][1]);
-       }
-
-       if (submit_contexts) {
-               if (!engine->disable_lite_restore_wa ||
-                   (csb[i][0] & GEN8_CTX_STATUS_ACTIVE_IDLE))
-                       execlists_context_unqueue(engine);
+               writel(_MASKED_FIELD(GEN8_CSB_READ_PTR_MASK,
+                                    GEN8_CSB_WRITE_PTR(csb) << 8),
+                      csb_mmio);
        }
 
-       spin_unlock(&engine->execlist_lock);
+       if (execlists_elsp_ready(engine))
+               execlists_dequeue(engine);
 
-       if (unlikely(submit_contexts > 2))
-               DRM_ERROR("More than two context complete events?\n");
+       intel_uncore_forcewake_put(dev_priv, engine->fw_domains);
 }
 
-static void execlists_context_queue(struct drm_i915_gem_request *request)
+static void execlists_submit_request(struct drm_i915_gem_request *request)
 {
        struct intel_engine_cs *engine = request->engine;
-       struct drm_i915_gem_request *cursor;
-       int num_elements = 0;
-
-       spin_lock_bh(&engine->execlist_lock);
+       unsigned long flags;
 
-       list_for_each_entry(cursor, &engine->execlist_queue, execlist_link)
-               if (++num_elements > 2)
-                       break;
+       spin_lock_irqsave(&engine->execlist_lock, flags);
 
-       if (num_elements > 2) {
-               struct drm_i915_gem_request *tail_req;
-
-               tail_req = list_last_entry(&engine->execlist_queue,
-                                          struct drm_i915_gem_request,
-                                          execlist_link);
-
-               if (request->ctx == tail_req->ctx) {
-                       WARN(tail_req->elsp_submitted != 0,
-                               "More than 2 already-submitted reqs queued\n");
-                       list_del(&tail_req->execlist_link);
-                       i915_gem_request_unreference(tail_req);
-               }
-       }
-
-       i915_gem_request_reference(request);
        list_add_tail(&request->execlist_link, &engine->execlist_queue);
-       request->ctx_hw_id = request->ctx->hw_id;
-       if (num_elements == 0)
-               execlists_context_unqueue(engine);
+       if (execlists_elsp_idle(engine))
+               tasklet_hi_schedule(&engine->irq_tasklet);
 
-       spin_unlock_bh(&engine->execlist_lock);
-}
-
-static int logical_ring_invalidate_all_caches(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       uint32_t flush_domains;
-       int ret;
-
-       flush_domains = 0;
-       if (engine->gpu_caches_dirty)
-               flush_domains = I915_GEM_GPU_DOMAINS;
-
-       ret = engine->emit_flush(req, I915_GEM_GPU_DOMAINS, flush_domains);
-       if (ret)
-               return ret;
-
-       engine->gpu_caches_dirty = false;
-       return 0;
-}
-
-static int execlists_move_to_gpu(struct drm_i915_gem_request *req,
-                                struct list_head *vmas)
-{
-       const unsigned other_rings = ~intel_engine_flag(req->engine);
-       struct i915_vma *vma;
-       uint32_t flush_domains = 0;
-       bool flush_chipset = false;
-       int ret;
-
-       list_for_each_entry(vma, vmas, exec_list) {
-               struct drm_i915_gem_object *obj = vma->obj;
-
-               if (obj->active & other_rings) {
-                       ret = i915_gem_object_sync(obj, req->engine, &req);
-                       if (ret)
-                               return ret;
-               }
-
-               if (obj->base.write_domain & I915_GEM_DOMAIN_CPU)
-                       flush_chipset |= i915_gem_clflush_object(obj, false);
-
-               flush_domains |= obj->base.write_domain;
-       }
-
-       if (flush_domains & I915_GEM_DOMAIN_GTT)
-               wmb();
-
-       /* Unconditionally invalidate gpu caches and ensure that we do flush
-        * any residual writes from the previous batch.
-        */
-       return logical_ring_invalidate_all_caches(req);
+       spin_unlock_irqrestore(&engine->execlist_lock, flags);
 }
 
 int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request)
@@ -717,7 +625,11 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
                        return ret;
        }
 
-       request->ringbuf = ce->ringbuf;
+       request->ring = ce->ring;
+
+       ret = intel_lr_context_pin(request->ctx, engine);
+       if (ret)
+               return ret;
 
        if (i915.enable_guc_submission) {
                /*
@@ -725,23 +637,19 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
                 * going any further, as the i915_add_request() call
                 * later on mustn't fail ...
                 */
-               ret = i915_guc_wq_check_space(request);
+               ret = i915_guc_wq_reserve(request);
                if (ret)
-                       return ret;
+                       goto err_unpin;
        }
 
-       ret = intel_lr_context_pin(request->ctx, engine);
-       if (ret)
-               return ret;
-
        ret = intel_ring_begin(request, 0);
        if (ret)
-               goto err_unpin;
+               goto err_unreserve;
 
        if (!ce->initialised) {
                ret = engine->init_context(request);
                if (ret)
-                       goto err_unpin;
+                       goto err_unreserve;
 
                ce->initialised = true;
        }
@@ -756,13 +664,16 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
        request->reserved_space -= EXECLISTS_REQUEST_SIZE;
        return 0;
 
+err_unreserve:
+       if (i915.enable_guc_submission)
+               i915_guc_wq_unreserve(request);
 err_unpin:
        intel_lr_context_unpin(request->ctx, engine);
        return ret;
 }
 
 /*
- * intel_logical_ring_advance_and_submit() - advance the tail and submit the workload
+ * intel_logical_ring_advance() - advance the tail and prepare for submission
  * @request: Request to advance the logical ringbuffer of.
  *
  * The tail is updated in our logical ringbuffer struct, not in the actual context. What
@@ -771,13 +682,13 @@ err_unpin:
  * point, the tail *inside* the context is updated and the ELSP written to.
  */
 static int
-intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request)
+intel_logical_ring_advance(struct drm_i915_gem_request *request)
 {
-       struct intel_ringbuffer *ringbuf = request->ringbuf;
+       struct intel_ring *ring = request->ring;
        struct intel_engine_cs *engine = request->engine;
 
-       intel_logical_ring_advance(ringbuf);
-       request->tail = ringbuf->tail;
+       intel_ring_advance(ring);
+       request->tail = ring->tail;
 
        /*
         * Here we add two extra NOOPs as padding to avoid
@@ -785,9 +696,10 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request)
         *
         * Caller must reserve WA_TAIL_DWORDS for us!
         */
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
+       request->wa_tail = ring->tail;
 
        /* We keep the previous context alive until we retire the following
         * request. This ensures that any the context object is still pinned
@@ -797,168 +709,14 @@ intel_logical_ring_advance_and_submit(struct drm_i915_gem_request *request)
         */
        request->previous_context = engine->last_context;
        engine->last_context = request->ctx;
-
-       if (i915.enable_guc_submission)
-               i915_guc_submit(request);
-       else
-               execlists_context_queue(request);
-
-       return 0;
-}
-
-/**
- * execlists_submission() - submit a batchbuffer for execution, Execlists style
- * @params: execbuffer call parameters.
- * @args: execbuffer call arguments.
- * @vmas: list of vmas.
- *
- * This is the evil twin version of i915_gem_ringbuffer_submission. It abstracts
- * away the submission details of the execbuffer ioctl call.
- *
- * Return: non-zero if the submission fails.
- */
-int intel_execlists_submission(struct i915_execbuffer_params *params,
-                              struct drm_i915_gem_execbuffer2 *args,
-                              struct list_head *vmas)
-{
-       struct drm_device       *dev = params->dev;
-       struct intel_engine_cs *engine = params->engine;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_ringbuffer *ringbuf = params->ctx->engine[engine->id].ringbuf;
-       u64 exec_start;
-       int instp_mode;
-       u32 instp_mask;
-       int ret;
-
-       instp_mode = args->flags & I915_EXEC_CONSTANTS_MASK;
-       instp_mask = I915_EXEC_CONSTANTS_MASK;
-       switch (instp_mode) {
-       case I915_EXEC_CONSTANTS_REL_GENERAL:
-       case I915_EXEC_CONSTANTS_ABSOLUTE:
-       case I915_EXEC_CONSTANTS_REL_SURFACE:
-               if (instp_mode != 0 && engine != &dev_priv->engine[RCS]) {
-                       DRM_DEBUG("non-0 rel constants mode on non-RCS\n");
-                       return -EINVAL;
-               }
-
-               if (instp_mode != dev_priv->relative_constants_mode) {
-                       if (instp_mode == I915_EXEC_CONSTANTS_REL_SURFACE) {
-                               DRM_DEBUG("rel surface constants mode invalid on gen5+\n");
-                               return -EINVAL;
-                       }
-
-                       /* The HW changed the meaning on this bit on gen6 */
-                       instp_mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
-               }
-               break;
-       default:
-               DRM_DEBUG("execbuf with unknown constants: %d\n", instp_mode);
-               return -EINVAL;
-       }
-
-       if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
-               DRM_DEBUG("sol reset is gen7 only\n");
-               return -EINVAL;
-       }
-
-       ret = execlists_move_to_gpu(params->request, vmas);
-       if (ret)
-               return ret;
-
-       if (engine == &dev_priv->engine[RCS] &&
-           instp_mode != dev_priv->relative_constants_mode) {
-               ret = intel_ring_begin(params->request, 4);
-               if (ret)
-                       return ret;
-
-               intel_logical_ring_emit(ringbuf, MI_NOOP);
-               intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(1));
-               intel_logical_ring_emit_reg(ringbuf, INSTPM);
-               intel_logical_ring_emit(ringbuf, instp_mask << 16 | instp_mode);
-               intel_logical_ring_advance(ringbuf);
-
-               dev_priv->relative_constants_mode = instp_mode;
-       }
-
-       exec_start = params->batch_obj_vm_offset +
-                    args->batch_start_offset;
-
-       ret = engine->emit_bb_start(params->request, exec_start, params->dispatch_flags);
-       if (ret)
-               return ret;
-
-       trace_i915_gem_ring_dispatch(params->request, params->dispatch_flags);
-
-       i915_gem_execbuffer_move_to_active(vmas, params->request);
-
-       return 0;
-}
-
-void intel_execlists_cancel_requests(struct intel_engine_cs *engine)
-{
-       struct drm_i915_gem_request *req, *tmp;
-       LIST_HEAD(cancel_list);
-
-       WARN_ON(!mutex_is_locked(&engine->i915->drm.struct_mutex));
-
-       spin_lock_bh(&engine->execlist_lock);
-       list_replace_init(&engine->execlist_queue, &cancel_list);
-       spin_unlock_bh(&engine->execlist_lock);
-
-       list_for_each_entry_safe(req, tmp, &cancel_list, execlist_link) {
-               list_del(&req->execlist_link);
-               i915_gem_request_unreference(req);
-       }
-}
-
-void intel_logical_ring_stop(struct intel_engine_cs *engine)
-{
-       struct drm_i915_private *dev_priv = engine->i915;
-       int ret;
-
-       if (!intel_engine_initialized(engine))
-               return;
-
-       ret = intel_engine_idle(engine);
-       if (ret)
-               DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
-                         engine->name, ret);
-
-       /* TODO: Is this correct with Execlists enabled? */
-       I915_WRITE_MODE(engine, _MASKED_BIT_ENABLE(STOP_RING));
-       if (intel_wait_for_register(dev_priv,
-                                   RING_MI_MODE(engine->mmio_base),
-                                   MODE_IDLE, MODE_IDLE,
-                                   1000)) {
-               DRM_ERROR("%s :timed out trying to stop ring\n", engine->name);
-               return;
-       }
-       I915_WRITE_MODE(engine, _MASKED_BIT_DISABLE(STOP_RING));
-}
-
-int logical_ring_flush_all_caches(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       int ret;
-
-       if (!engine->gpu_caches_dirty)
-               return 0;
-
-       ret = engine->emit_flush(req, 0, I915_GEM_GPU_DOMAINS);
-       if (ret)
-               return ret;
-
-       engine->gpu_caches_dirty = false;
        return 0;
 }
 
 static int intel_lr_context_pin(struct i915_gem_context *ctx,
                                struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = ctx->i915;
        struct intel_context *ce = &ctx->engine[engine->id];
        void *vaddr;
-       u32 *lrc_reg_state;
        int ret;
 
        lockdep_assert_held(&ctx->i915->drm.struct_mutex);
@@ -966,41 +724,42 @@ static int intel_lr_context_pin(struct i915_gem_context *ctx,
        if (ce->pin_count++)
                return 0;
 
-       ret = i915_gem_obj_ggtt_pin(ce->state, GEN8_LR_CONTEXT_ALIGN,
-                                   PIN_OFFSET_BIAS | GUC_WOPCM_TOP);
+       ret = i915_vma_pin(ce->state, 0, GEN8_LR_CONTEXT_ALIGN,
+                          PIN_OFFSET_BIAS | GUC_WOPCM_TOP | PIN_GLOBAL);
        if (ret)
                goto err;
 
-       vaddr = i915_gem_object_pin_map(ce->state);
+       vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB);
        if (IS_ERR(vaddr)) {
                ret = PTR_ERR(vaddr);
-               goto unpin_ctx_obj;
+               goto unpin_vma;
        }
 
-       lrc_reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
-
-       ret = intel_pin_and_map_ringbuffer_obj(dev_priv, ce->ringbuf);
+       ret = intel_ring_pin(ce->ring);
        if (ret)
                goto unpin_map;
 
-       i915_gem_context_reference(ctx);
-       ce->lrc_vma = i915_gem_obj_to_ggtt(ce->state);
        intel_lr_context_descriptor_update(ctx, engine);
 
-       lrc_reg_state[CTX_RING_BUFFER_START+1] = ce->ringbuf->vma->node.start;
-       ce->lrc_reg_state = lrc_reg_state;
-       ce->state->dirty = true;
+       ce->lrc_reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
+       ce->lrc_reg_state[CTX_RING_BUFFER_START+1] =
+               i915_ggtt_offset(ce->ring->vma);
+
+       ce->state->obj->dirty = true;
 
        /* Invalidate GuC TLB. */
-       if (i915.enable_guc_submission)
+       if (i915.enable_guc_submission) {
+               struct drm_i915_private *dev_priv = ctx->i915;
                I915_WRITE(GEN8_GTCR, GEN8_GTCR_INVALIDATE);
+       }
 
+       i915_gem_context_get(ctx);
        return 0;
 
 unpin_map:
-       i915_gem_object_unpin_map(ce->state);
-unpin_ctx_obj:
-       i915_gem_object_ggtt_unpin(ce->state);
+       i915_gem_object_unpin_map(ce->state->obj);
+unpin_vma:
+       __i915_vma_unpin(ce->state);
 err:
        ce->pin_count = 0;
        return ret;
@@ -1017,30 +776,24 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx,
        if (--ce->pin_count)
                return;
 
-       intel_unpin_ringbuffer_obj(ce->ringbuf);
+       intel_ring_unpin(ce->ring);
 
-       i915_gem_object_unpin_map(ce->state);
-       i915_gem_object_ggtt_unpin(ce->state);
+       i915_gem_object_unpin_map(ce->state->obj);
+       i915_vma_unpin(ce->state);
 
-       ce->lrc_vma = NULL;
-       ce->lrc_desc = 0;
-       ce->lrc_reg_state = NULL;
-
-       i915_gem_context_unreference(ctx);
+       i915_gem_context_put(ctx);
 }
 
 static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
 {
        int ret, i;
-       struct intel_engine_cs *engine = req->engine;
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
+       struct intel_ring *ring = req->ring;
        struct i915_workarounds *w = &req->i915->workarounds;
 
        if (w->count == 0)
                return 0;
 
-       engine->gpu_caches_dirty = true;
-       ret = logical_ring_flush_all_caches(req);
+       ret = req->engine->emit_flush(req, EMIT_BARRIER);
        if (ret)
                return ret;
 
@@ -1048,17 +801,16 @@ static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
        if (ret)
                return ret;
 
-       intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(w->count));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(w->count));
        for (i = 0; i < w->count; i++) {
-               intel_logical_ring_emit_reg(ringbuf, w->reg[i].addr);
-               intel_logical_ring_emit(ringbuf, w->reg[i].value);
+               intel_ring_emit_reg(ring, w->reg[i].addr);
+               intel_ring_emit(ring, w->reg[i].value);
        }
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
+       intel_ring_emit(ring, MI_NOOP);
 
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_advance(ring);
 
-       engine->gpu_caches_dirty = true;
-       ret = logical_ring_flush_all_caches(req);
+       ret = req->engine->emit_flush(req, EMIT_BARRIER);
        if (ret)
                return ret;
 
@@ -1094,7 +846,7 @@ static int intel_logical_ring_workarounds_emit(struct drm_i915_gem_request *req)
  * code duplication.
  */
 static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine,
-                                               uint32_t *const batch,
+                                               uint32_t *batch,
                                                uint32_t index)
 {
        struct drm_i915_private *dev_priv = engine->i915;
@@ -1113,7 +865,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine,
        wa_ctx_emit(batch, index, (MI_STORE_REGISTER_MEM_GEN8 |
                                   MI_SRM_LRM_GLOBAL_GTT));
        wa_ctx_emit_reg(batch, index, GEN8_L3SQCREG4);
-       wa_ctx_emit(batch, index, engine->scratch.gtt_offset + 256);
+       wa_ctx_emit(batch, index, i915_ggtt_offset(engine->scratch) + 256);
        wa_ctx_emit(batch, index, 0);
 
        wa_ctx_emit(batch, index, MI_LOAD_REGISTER_IMM(1));
@@ -1131,7 +883,7 @@ static inline int gen8_emit_flush_coherentl3_wa(struct intel_engine_cs *engine,
        wa_ctx_emit(batch, index, (MI_LOAD_REGISTER_MEM_GEN8 |
                                   MI_SRM_LRM_GLOBAL_GTT));
        wa_ctx_emit_reg(batch, index, GEN8_L3SQCREG4);
-       wa_ctx_emit(batch, index, engine->scratch.gtt_offset + 256);
+       wa_ctx_emit(batch, index, i915_ggtt_offset(engine->scratch) + 256);
        wa_ctx_emit(batch, index, 0);
 
        return index;
@@ -1156,37 +908,24 @@ static inline int wa_ctx_end(struct i915_wa_ctx_bb *wa_ctx,
        return 0;
 }
 
-/**
- * gen8_init_indirectctx_bb() - initialize indirect ctx batch with WA
- *
- * @engine: only applicable for RCS
- * @wa_ctx: structure representing wa_ctx
- *  offset: specifies start of the batch, should be cache-aligned. This is updated
- *    with the offset value received as input.
- *  size: size of the batch in DWORDS but HW expects in terms of cachelines
- * @batch: page in which WA are loaded
- * @offset: This field specifies the start of the batch, it should be
- *  cache-aligned otherwise it is adjusted accordingly.
- *  Typically we only have one indirect_ctx and per_ctx batch buffer which are
- *  initialized at the beginning and shared across all contexts but this field
- *  helps us to have multiple batches at different offsets and select them based
- *  on a criteria. At the moment this batch always start at the beginning of the page
- *  and at this point we don't have multiple wa_ctx batch buffers.
- *
- *  The number of WA applied are not known at the beginning; we use this field
- *  to return the no of DWORDS written.
+/*
+ * Typically we only have one indirect_ctx and per_ctx batch buffer which are
+ * initialized at the beginning and shared across all contexts but this field
+ * helps us to have multiple batches at different offsets and select them based
+ * on a criteria. At the moment this batch always start at the beginning of the page
+ * and at this point we don't have multiple wa_ctx batch buffers.
  *
- *  It is to be noted that this batch does not contain MI_BATCH_BUFFER_END
- *  so it adds NOOPs as padding to make it cacheline aligned.
- *  MI_BATCH_BUFFER_END will be added to perctx batch and both of them together
- *  makes a complete batch buffer.
+ * The number of WA applied are not known at the beginning; we use this field
+ * to return the no of DWORDS written.
  *
- * Return: non-zero if we exceed the PAGE_SIZE limit.
+ * It is to be noted that this batch does not contain MI_BATCH_BUFFER_END
+ * so it adds NOOPs as padding to make it cacheline aligned.
+ * MI_BATCH_BUFFER_END will be added to perctx batch and both of them together
+ * makes a complete batch buffer.
  */
-
 static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine,
                                    struct i915_wa_ctx_bb *wa_ctx,
-                                   uint32_t *const batch,
+                                   uint32_t *batch,
                                    uint32_t *offset)
 {
        uint32_t scratch_addr;
@@ -1205,7 +944,7 @@ static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine,
 
        /* WaClearSlmSpaceAtContextSwitch:bdw,chv */
        /* Actual scratch location is at 128 bytes offset */
-       scratch_addr = engine->scratch.gtt_offset + 2*CACHELINE_BYTES;
+       scratch_addr = i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES;
 
        wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6));
        wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 |
@@ -1230,26 +969,18 @@ static int gen8_init_indirectctx_bb(struct intel_engine_cs *engine,
        return wa_ctx_end(wa_ctx, *offset = index, CACHELINE_DWORDS);
 }
 
-/**
- * gen8_init_perctx_bb() - initialize per ctx batch with WA
- *
- * @engine: only applicable for RCS
- * @wa_ctx: structure representing wa_ctx
- *  offset: specifies start of the batch, should be cache-aligned.
- *  size: size of the batch in DWORDS but HW expects in terms of cachelines
- * @batch: page in which WA are loaded
- * @offset: This field specifies the start of this batch.
- *   This batch is started immediately after indirect_ctx batch. Since we ensure
- *   that indirect_ctx ends on a cacheline this batch is aligned automatically.
+/*
+ *  This batch is started immediately after indirect_ctx batch. Since we ensure
+ *  that indirect_ctx ends on a cacheline this batch is aligned automatically.
  *
- *   The number of DWORDS written are returned using this field.
+ *  The number of DWORDS written are returned using this field.
  *
  *  This batch is terminated with MI_BATCH_BUFFER_END and so we need not add padding
  *  to align it with cacheline as padding after MI_BATCH_BUFFER_END is redundant.
  */
 static int gen8_init_perctx_bb(struct intel_engine_cs *engine,
                               struct i915_wa_ctx_bb *wa_ctx,
-                              uint32_t *const batch,
+                              uint32_t *batch,
                               uint32_t *offset)
 {
        uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS);
@@ -1264,7 +995,7 @@ static int gen8_init_perctx_bb(struct intel_engine_cs *engine,
 
 static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
                                    struct i915_wa_ctx_bb *wa_ctx,
-                                   uint32_t *const batch,
+                                   uint32_t *batch,
                                    uint32_t *offset)
 {
        int ret;
@@ -1282,11 +1013,18 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
                return ret;
        index = ret;
 
+       /* WaDisableGatherAtSetShaderCommonSlice:skl,bxt,kbl */
+       wa_ctx_emit(batch, index, MI_LOAD_REGISTER_IMM(1));
+       wa_ctx_emit_reg(batch, index, COMMON_SLICE_CHICKEN2);
+       wa_ctx_emit(batch, index, _MASKED_BIT_DISABLE(
+                           GEN9_DISABLE_GATHER_AT_SET_SHADER_COMMON_SLICE));
+       wa_ctx_emit(batch, index, MI_NOOP);
+
        /* WaClearSlmSpaceAtContextSwitch:kbl */
        /* Actual scratch location is at 128 bytes offset */
        if (IS_KBL_REVID(dev_priv, 0, KBL_REVID_A0)) {
-               uint32_t scratch_addr
-                       = engine->scratch.gtt_offset + 2*CACHELINE_BYTES;
+               u32 scratch_addr =
+                       i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES;
 
                wa_ctx_emit(batch, index, GFX_OP_PIPE_CONTROL(6));
                wa_ctx_emit(batch, index, (PIPE_CONTROL_FLUSH_L3 |
@@ -1332,7 +1070,7 @@ static int gen9_init_indirectctx_bb(struct intel_engine_cs *engine,
 
 static int gen9_init_perctx_bb(struct intel_engine_cs *engine,
                               struct i915_wa_ctx_bb *wa_ctx,
-                              uint32_t *const batch,
+                              uint32_t *batch,
                               uint32_t *offset)
 {
        uint32_t index = wa_ctx_start(wa_ctx, *offset, CACHELINE_DWORDS);
@@ -1378,44 +1116,44 @@ static int gen9_init_perctx_bb(struct intel_engine_cs *engine,
 
 static int lrc_setup_wa_ctx_obj(struct intel_engine_cs *engine, u32 size)
 {
-       int ret;
+       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
+       int err;
 
-       engine->wa_ctx.obj = i915_gem_object_create(&engine->i915->drm,
-                                                   PAGE_ALIGN(size));
-       if (IS_ERR(engine->wa_ctx.obj)) {
-               DRM_DEBUG_DRIVER("alloc LRC WA ctx backing obj failed.\n");
-               ret = PTR_ERR(engine->wa_ctx.obj);
-               engine->wa_ctx.obj = NULL;
-               return ret;
-       }
+       obj = i915_gem_object_create(&engine->i915->drm, PAGE_ALIGN(size));
+       if (IS_ERR(obj))
+               return PTR_ERR(obj);
 
-       ret = i915_gem_obj_ggtt_pin(engine->wa_ctx.obj, PAGE_SIZE, 0);
-       if (ret) {
-               DRM_DEBUG_DRIVER("pin LRC WA ctx backing obj failed: %d\n",
-                                ret);
-               drm_gem_object_unreference(&engine->wa_ctx.obj->base);
-               return ret;
+       vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL);
+       if (IS_ERR(vma)) {
+               err = PTR_ERR(vma);
+               goto err;
        }
 
+       err = i915_vma_pin(vma, 0, PAGE_SIZE, PIN_GLOBAL | PIN_HIGH);
+       if (err)
+               goto err;
+
+       engine->wa_ctx.vma = vma;
        return 0;
+
+err:
+       i915_gem_object_put(obj);
+       return err;
 }
 
 static void lrc_destroy_wa_ctx_obj(struct intel_engine_cs *engine)
 {
-       if (engine->wa_ctx.obj) {
-               i915_gem_object_ggtt_unpin(engine->wa_ctx.obj);
-               drm_gem_object_unreference(&engine->wa_ctx.obj->base);
-               engine->wa_ctx.obj = NULL;
-       }
+       i915_vma_unpin_and_release(&engine->wa_ctx.vma);
 }
 
 static int intel_init_workaround_bb(struct intel_engine_cs *engine)
 {
-       int ret;
+       struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx;
        uint32_t *batch;
        uint32_t offset;
        struct page *page;
-       struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx;
+       int ret;
 
        WARN_ON(engine->id != RCS);
 
@@ -1427,7 +1165,7 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine)
        }
 
        /* some WA perform writes to scratch page, ensure it is valid */
-       if (engine->scratch.obj == NULL) {
+       if (!engine->scratch) {
                DRM_ERROR("scratch page not allocated for %s\n", engine->name);
                return -EINVAL;
        }
@@ -1438,7 +1176,7 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine)
                return ret;
        }
 
-       page = i915_gem_object_get_dirty_page(wa_ctx->obj, 0);
+       page = i915_gem_object_get_dirty_page(wa_ctx->vma->obj, 0);
        batch = kmap_atomic(page);
        offset = 0;
 
@@ -1485,55 +1223,37 @@ static void lrc_init_hws(struct intel_engine_cs *engine)
        struct drm_i915_private *dev_priv = engine->i915;
 
        I915_WRITE(RING_HWS_PGA(engine->mmio_base),
-                  (u32)engine->status_page.gfx_addr);
+                  engine->status_page.ggtt_offset);
        POSTING_READ(RING_HWS_PGA(engine->mmio_base));
 }
 
 static int gen8_init_common_ring(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
-       unsigned int next_context_status_buffer_hw;
+       int ret;
+
+       ret = intel_mocs_init_engine(engine);
+       if (ret)
+               return ret;
 
        lrc_init_hws(engine);
 
-       I915_WRITE_IMR(engine,
-                      ~(engine->irq_enable_mask | engine->irq_keep_mask));
+       intel_engine_reset_breadcrumbs(engine);
+
        I915_WRITE(RING_HWSTAM(engine->mmio_base), 0xffffffff);
 
        I915_WRITE(RING_MODE_GEN7(engine),
                   _MASKED_BIT_DISABLE(GFX_REPLAY_MODE) |
                   _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE));
-       POSTING_READ(RING_MODE_GEN7(engine));
-
-       /*
-        * Instead of resetting the Context Status Buffer (CSB) read pointer to
-        * zero, we need to read the write pointer from hardware and use its
-        * value because "this register is power context save restored".
-        * Effectively, these states have been observed:
-        *
-        *      | Suspend-to-idle (freeze) | Suspend-to-RAM (mem) |
-        * BDW  | CSB regs not reset       | CSB regs reset       |
-        * CHT  | CSB regs not reset       | CSB regs not reset   |
-        * SKL  |         ?                |         ?            |
-        * BXT  |         ?                |         ?            |
-        */
-       next_context_status_buffer_hw =
-               GEN8_CSB_WRITE_PTR(I915_READ(RING_CONTEXT_STATUS_PTR(engine)));
-
-       /*
-        * When the CSB registers are reset (also after power-up / gpu reset),
-        * CSB write pointer is set to all 1's, which is not valid, use '5' in
-        * this special case, so the first element read is CSB[0].
-        */
-       if (next_context_status_buffer_hw == GEN8_CSB_PTR_MASK)
-               next_context_status_buffer_hw = (GEN8_CSB_ENTRIES - 1);
 
-       engine->next_context_status_buffer = next_context_status_buffer_hw;
        DRM_DEBUG_DRIVER("Execlists enabled for %s\n", engine->name);
 
        intel_engine_init_hangcheck(engine);
 
-       return intel_mocs_init_engine(engine);
+       if (!execlists_elsp_idle(engine))
+               execlists_submit_ports(engine);
+
+       return 0;
 }
 
 static int gen8_init_render_ring(struct intel_engine_cs *engine)
@@ -1569,11 +1289,57 @@ static int gen9_init_render_ring(struct intel_engine_cs *engine)
        return init_workarounds_ring(engine);
 }
 
+static void reset_common_ring(struct intel_engine_cs *engine,
+                             struct drm_i915_gem_request *request)
+{
+       struct drm_i915_private *dev_priv = engine->i915;
+       struct execlist_port *port = engine->execlist_port;
+       struct intel_context *ce = &request->ctx->engine[engine->id];
+
+       /* We want a simple context + ring to execute the breadcrumb update.
+        * We cannot rely on the context being intact across the GPU hang,
+        * so clear it and rebuild just what we need for the breadcrumb.
+        * All pending requests for this context will be zapped, and any
+        * future request will be after userspace has had the opportunity
+        * to recreate its own state.
+        */
+       execlists_init_reg_state(ce->lrc_reg_state,
+                                request->ctx, engine, ce->ring);
+
+       /* Move the RING_HEAD onto the breadcrumb, past the hanging batch */
+       ce->lrc_reg_state[CTX_RING_BUFFER_START+1] =
+               i915_ggtt_offset(ce->ring->vma);
+       ce->lrc_reg_state[CTX_RING_HEAD+1] = request->postfix;
+
+       request->ring->head = request->postfix;
+       request->ring->last_retired_head = -1;
+       intel_ring_update_space(request->ring);
+
+       if (i915.enable_guc_submission)
+               return;
+
+       /* Catch up with any missed context-switch interrupts */
+       I915_WRITE(RING_CONTEXT_STATUS_PTR(engine), _MASKED_FIELD(0xffff, 0));
+       if (request->ctx != port[0].request->ctx) {
+               i915_gem_request_put(port[0].request);
+               port[0] = port[1];
+               memset(&port[1], 0, sizeof(port[1]));
+       }
+
+       /* CS is stopped, and we will resubmit both ports on resume */
+       GEM_BUG_ON(request->ctx != port[0].request->ctx);
+       port[0].count = 0;
+       port[1].count = 0;
+
+       /* Reset WaIdleLiteRestore:bdw,skl as well */
+       request->tail = request->wa_tail - WA_TAIL_DWORDS * sizeof(u32);
+}
+
 static int intel_logical_ring_emit_pdps(struct drm_i915_gem_request *req)
 {
        struct i915_hw_ppgtt *ppgtt = req->ctx->ppgtt;
+       struct intel_ring *ring = req->ring;
        struct intel_engine_cs *engine = req->engine;
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
        const int num_lri_cmds = GEN8_LEGACY_PDPES * 2;
        int i, ret;
 
@@ -1581,28 +1347,27 @@ static int intel_logical_ring_emit_pdps(struct drm_i915_gem_request *req)
        if (ret)
                return ret;
 
-       intel_logical_ring_emit(ringbuf, MI_LOAD_REGISTER_IMM(num_lri_cmds));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(num_lri_cmds));
        for (i = GEN8_LEGACY_PDPES - 1; i >= 0; i--) {
                const dma_addr_t pd_daddr = i915_page_dir_dma_addr(ppgtt, i);
 
-               intel_logical_ring_emit_reg(ringbuf,
-                                           GEN8_RING_PDP_UDW(engine, i));
-               intel_logical_ring_emit(ringbuf, upper_32_bits(pd_daddr));
-               intel_logical_ring_emit_reg(ringbuf,
-                                           GEN8_RING_PDP_LDW(engine, i));
-               intel_logical_ring_emit(ringbuf, lower_32_bits(pd_daddr));
+               intel_ring_emit_reg(ring, GEN8_RING_PDP_UDW(engine, i));
+               intel_ring_emit(ring, upper_32_bits(pd_daddr));
+               intel_ring_emit_reg(ring, GEN8_RING_PDP_LDW(engine, i));
+               intel_ring_emit(ring, lower_32_bits(pd_daddr));
        }
 
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int gen8_emit_bb_start(struct drm_i915_gem_request *req,
-                             u64 offset, unsigned dispatch_flags)
+                             u64 offset, u32 len,
+                             unsigned int dispatch_flags)
 {
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
+       struct intel_ring *ring = req->ring;
        bool ppgtt = !(dispatch_flags & I915_DISPATCH_SECURE);
        int ret;
 
@@ -1629,14 +1394,14 @@ static int gen8_emit_bb_start(struct drm_i915_gem_request *req,
                return ret;
 
        /* FIXME(BDW): Address space and security selectors. */
-       intel_logical_ring_emit(ringbuf, MI_BATCH_BUFFER_START_GEN8 |
-                               (ppgtt<<8) |
-                               (dispatch_flags & I915_DISPATCH_RS ?
-                                MI_BATCH_RESOURCE_STREAMER : 0));
-       intel_logical_ring_emit(ringbuf, lower_32_bits(offset));
-       intel_logical_ring_emit(ringbuf, upper_32_bits(offset));
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 |
+                       (ppgtt<<8) |
+                       (dispatch_flags & I915_DISPATCH_RS ?
+                        MI_BATCH_RESOURCE_STREAMER : 0));
+       intel_ring_emit(ring, lower_32_bits(offset));
+       intel_ring_emit(ring, upper_32_bits(offset));
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -1655,14 +1420,10 @@ static void gen8_logical_ring_disable_irq(struct intel_engine_cs *engine)
        I915_WRITE_IMR(engine, ~engine->irq_keep_mask);
 }
 
-static int gen8_emit_flush(struct drm_i915_gem_request *request,
-                          u32 invalidate_domains,
-                          u32 unused)
+static int gen8_emit_flush(struct drm_i915_gem_request *request, u32 mode)
 {
-       struct intel_ringbuffer *ringbuf = request->ringbuf;
-       struct intel_engine_cs *engine = ringbuf->engine;
-       struct drm_i915_private *dev_priv = request->i915;
-       uint32_t cmd;
+       struct intel_ring *ring = request->ring;
+       u32 cmd;
        int ret;
 
        ret = intel_ring_begin(request, 4);
@@ -1678,30 +1439,30 @@ static int gen8_emit_flush(struct drm_i915_gem_request *request,
         */
        cmd |= MI_FLUSH_DW_STORE_INDEX | MI_FLUSH_DW_OP_STOREDW;
 
-       if (invalidate_domains & I915_GEM_GPU_DOMAINS) {
+       if (mode & EMIT_INVALIDATE) {
                cmd |= MI_INVALIDATE_TLB;
-               if (engine == &dev_priv->engine[VCS])
+               if (request->engine->id == VCS)
                        cmd |= MI_INVALIDATE_BSD;
        }
 
-       intel_logical_ring_emit(ringbuf, cmd);
-       intel_logical_ring_emit(ringbuf,
-                               I915_GEM_HWS_SCRATCH_ADDR |
-                               MI_FLUSH_DW_USE_GTT);
-       intel_logical_ring_emit(ringbuf, 0); /* upper addr */
-       intel_logical_ring_emit(ringbuf, 0); /* value */
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, cmd);
+       intel_ring_emit(ring,
+                       I915_GEM_HWS_SCRATCH_ADDR |
+                       MI_FLUSH_DW_USE_GTT);
+       intel_ring_emit(ring, 0); /* upper addr */
+       intel_ring_emit(ring, 0); /* value */
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
-                                 u32 invalidate_domains,
-                                 u32 flush_domains)
+                                 u32 mode)
 {
-       struct intel_ringbuffer *ringbuf = request->ringbuf;
-       struct intel_engine_cs *engine = ringbuf->engine;
-       u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
+       struct intel_ring *ring = request->ring;
+       struct intel_engine_cs *engine = request->engine;
+       u32 scratch_addr =
+               i915_ggtt_offset(engine->scratch) + 2 * CACHELINE_BYTES;
        bool vf_flush_wa = false, dc_flush_wa = false;
        u32 flags = 0;
        int ret;
@@ -1709,14 +1470,14 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
 
        flags |= PIPE_CONTROL_CS_STALL;
 
-       if (flush_domains) {
+       if (mode & EMIT_FLUSH) {
                flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DC_FLUSH_ENABLE;
                flags |= PIPE_CONTROL_FLUSH_ENABLE;
        }
 
-       if (invalidate_domains) {
+       if (mode & EMIT_INVALIDATE) {
                flags |= PIPE_CONTROL_TLB_INVALIDATE;
                flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
                flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
@@ -1751,40 +1512,40 @@ static int gen8_emit_flush_render(struct drm_i915_gem_request *request,
                return ret;
 
        if (vf_flush_wa) {
-               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
+               intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
        }
 
        if (dc_flush_wa) {
-               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
-               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_DC_FLUSH_ENABLE);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
+               intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+               intel_ring_emit(ring, PIPE_CONTROL_DC_FLUSH_ENABLE);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
        }
 
-       intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
-       intel_logical_ring_emit(ringbuf, flags);
-       intel_logical_ring_emit(ringbuf, scratch_addr);
-       intel_logical_ring_emit(ringbuf, 0);
-       intel_logical_ring_emit(ringbuf, 0);
-       intel_logical_ring_emit(ringbuf, 0);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+       intel_ring_emit(ring, flags);
+       intel_ring_emit(ring, scratch_addr);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
 
        if (dc_flush_wa) {
-               intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
-               intel_logical_ring_emit(ringbuf, PIPE_CONTROL_CS_STALL);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
-               intel_logical_ring_emit(ringbuf, 0);
+               intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+               intel_ring_emit(ring, PIPE_CONTROL_CS_STALL);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, 0);
        }
 
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -1809,11 +1570,10 @@ static void bxt_a_seqno_barrier(struct intel_engine_cs *engine)
  * used as a workaround for not being allowed to do lite
  * restore with HEAD==TAIL (WaIdleLiteRestore).
  */
-#define WA_TAIL_DWORDS 2
 
 static int gen8_emit_request(struct drm_i915_gem_request *request)
 {
-       struct intel_ringbuffer *ringbuf = request->ringbuf;
+       struct intel_ring *ring = request->ring;
        int ret;
 
        ret = intel_ring_begin(request, 6 + WA_TAIL_DWORDS);
@@ -1823,21 +1583,20 @@ static int gen8_emit_request(struct drm_i915_gem_request *request)
        /* w/a: bit 5 needs to be zero for MI_FLUSH_DW address. */
        BUILD_BUG_ON(I915_GEM_HWS_INDEX_ADDR & (1 << 5));
 
-       intel_logical_ring_emit(ringbuf,
-                               (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW);
-       intel_logical_ring_emit(ringbuf,
-                               intel_hws_seqno_address(request->engine) |
-                               MI_FLUSH_DW_USE_GTT);
-       intel_logical_ring_emit(ringbuf, 0);
-       intel_logical_ring_emit(ringbuf, request->seqno);
-       intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT);
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       return intel_logical_ring_advance_and_submit(request);
+       intel_ring_emit(ring, (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW);
+       intel_ring_emit(ring,
+                       intel_hws_seqno_address(request->engine) |
+                       MI_FLUSH_DW_USE_GTT);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, request->fence.seqno);
+       intel_ring_emit(ring, MI_USER_INTERRUPT);
+       intel_ring_emit(ring, MI_NOOP);
+       return intel_logical_ring_advance(request);
 }
 
 static int gen8_emit_request_render(struct drm_i915_gem_request *request)
 {
-       struct intel_ringbuffer *ringbuf = request->ringbuf;
+       struct intel_ring *ring = request->ring;
        int ret;
 
        ret = intel_ring_begin(request, 8 + WA_TAIL_DWORDS);
@@ -1851,50 +1610,19 @@ static int gen8_emit_request_render(struct drm_i915_gem_request *request)
         * need a prior CS_STALL, which is emitted by the flush
         * following the batch.
         */
-       intel_logical_ring_emit(ringbuf, GFX_OP_PIPE_CONTROL(6));
-       intel_logical_ring_emit(ringbuf,
-                               (PIPE_CONTROL_GLOBAL_GTT_IVB |
-                                PIPE_CONTROL_CS_STALL |
-                                PIPE_CONTROL_QW_WRITE));
-       intel_logical_ring_emit(ringbuf,
-                               intel_hws_seqno_address(request->engine));
-       intel_logical_ring_emit(ringbuf, 0);
-       intel_logical_ring_emit(ringbuf, i915_gem_request_get_seqno(request));
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+       intel_ring_emit(ring,
+                       (PIPE_CONTROL_GLOBAL_GTT_IVB |
+                        PIPE_CONTROL_CS_STALL |
+                        PIPE_CONTROL_QW_WRITE));
+       intel_ring_emit(ring, intel_hws_seqno_address(request->engine));
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, i915_gem_request_get_seqno(request));
        /* We're thrashing one dword of HWS. */
-       intel_logical_ring_emit(ringbuf, 0);
-       intel_logical_ring_emit(ringbuf, MI_USER_INTERRUPT);
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       return intel_logical_ring_advance_and_submit(request);
-}
-
-static int intel_lr_context_render_state_init(struct drm_i915_gem_request *req)
-{
-       struct render_state so;
-       int ret;
-
-       ret = i915_gem_render_state_prepare(req->engine, &so);
-       if (ret)
-               return ret;
-
-       if (so.rodata == NULL)
-               return 0;
-
-       ret = req->engine->emit_bb_start(req, so.ggtt_offset,
-                                      I915_DISPATCH_SECURE);
-       if (ret)
-               goto out;
-
-       ret = req->engine->emit_bb_start(req,
-                                      (so.ggtt_offset + so.aux_batch_offset),
-                                      I915_DISPATCH_SECURE);
-       if (ret)
-               goto out;
-
-       i915_vma_move_to_active(i915_gem_obj_to_ggtt(so.obj), req);
-
-out:
-       i915_gem_render_state_fini(&so);
-       return ret;
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, MI_USER_INTERRUPT);
+       intel_ring_emit(ring, MI_NOOP);
+       return intel_logical_ring_advance(request);
 }
 
 static int gen8_init_rcs_context(struct drm_i915_gem_request *req)
@@ -1913,14 +1641,12 @@ static int gen8_init_rcs_context(struct drm_i915_gem_request *req)
        if (ret)
                DRM_ERROR("MOCS failed to program: expect performance issues.\n");
 
-       return intel_lr_context_render_state_init(req);
+       return i915_gem_render_state_init(req);
 }
 
 /**
  * intel_logical_ring_cleanup() - deallocate the Engine Command Streamer
- *
  * @engine: Engine Command Streamer.
- *
  */
 void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
 {
@@ -1939,39 +1665,42 @@ void intel_logical_ring_cleanup(struct intel_engine_cs *engine)
        dev_priv = engine->i915;
 
        if (engine->buffer) {
-               intel_logical_ring_stop(engine);
                WARN_ON((I915_READ_MODE(engine) & MODE_IDLE) == 0);
        }
 
        if (engine->cleanup)
                engine->cleanup(engine);
 
-       i915_cmd_parser_fini_ring(engine);
-       i915_gem_batch_pool_fini(&engine->batch_pool);
-
-       intel_engine_fini_breadcrumbs(engine);
+       intel_engine_cleanup_common(engine);
 
-       if (engine->status_page.obj) {
-               i915_gem_object_unpin_map(engine->status_page.obj);
-               engine->status_page.obj = NULL;
+       if (engine->status_page.vma) {
+               i915_gem_object_unpin_map(engine->status_page.vma->obj);
+               engine->status_page.vma = NULL;
        }
        intel_lr_context_unpin(dev_priv->kernel_context, engine);
 
-       engine->idle_lite_restore_wa = 0;
-       engine->disable_lite_restore_wa = false;
-       engine->ctx_desc_template = 0;
-
        lrc_destroy_wa_ctx_obj(engine);
        engine->i915 = NULL;
 }
 
+void intel_execlists_enable_submission(struct drm_i915_private *dev_priv)
+{
+       struct intel_engine_cs *engine;
+
+       for_each_engine(engine, dev_priv)
+               engine->submit_request = execlists_submit_request;
+}
+
 static void
 logical_ring_default_vfuncs(struct intel_engine_cs *engine)
 {
        /* Default vfuncs which can be overriden by each engine. */
        engine->init_hw = gen8_init_common_ring;
-       engine->emit_request = gen8_emit_request;
+       engine->reset_hw = reset_common_ring;
        engine->emit_flush = gen8_emit_flush;
+       engine->emit_request = gen8_emit_request;
+       engine->submit_request = execlists_submit_request;
+
        engine->irq_enable = gen8_logical_ring_enable_irq;
        engine->irq_disable = gen8_logical_ring_disable_irq;
        engine->emit_bb_start = gen8_emit_bb_start;
@@ -1980,41 +1709,71 @@ logical_ring_default_vfuncs(struct intel_engine_cs *engine)
 }
 
 static inline void
-logical_ring_default_irqs(struct intel_engine_cs *engine, unsigned shift)
+logical_ring_default_irqs(struct intel_engine_cs *engine)
 {
+       unsigned shift = engine->irq_shift;
        engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << shift;
        engine->irq_keep_mask = GT_CONTEXT_SWITCH_INTERRUPT << shift;
 }
 
 static int
-lrc_setup_hws(struct intel_engine_cs *engine,
-             struct drm_i915_gem_object *dctx_obj)
+lrc_setup_hws(struct intel_engine_cs *engine, struct i915_vma *vma)
 {
+       const int hws_offset = LRC_PPHWSP_PN * PAGE_SIZE;
        void *hws;
 
        /* The HWSP is part of the default context object in LRC mode. */
-       engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(dctx_obj) +
-                                      LRC_PPHWSP_PN * PAGE_SIZE;
-       hws = i915_gem_object_pin_map(dctx_obj);
+       hws = i915_gem_object_pin_map(vma->obj, I915_MAP_WB);
        if (IS_ERR(hws))
                return PTR_ERR(hws);
-       engine->status_page.page_addr = hws + LRC_PPHWSP_PN * PAGE_SIZE;
-       engine->status_page.obj = dctx_obj;
+
+       engine->status_page.page_addr = hws + hws_offset;
+       engine->status_page.ggtt_offset = i915_ggtt_offset(vma) + hws_offset;
+       engine->status_page.vma = vma;
 
        return 0;
 }
 
+static void
+logical_ring_setup(struct intel_engine_cs *engine)
+{
+       struct drm_i915_private *dev_priv = engine->i915;
+       enum forcewake_domains fw_domains;
+
+       intel_engine_setup_common(engine);
+
+       /* Intentionally left blank. */
+       engine->buffer = NULL;
+
+       fw_domains = intel_uncore_forcewake_for_reg(dev_priv,
+                                                   RING_ELSP(engine),
+                                                   FW_REG_WRITE);
+
+       fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
+                                                    RING_CONTEXT_STATUS_PTR(engine),
+                                                    FW_REG_READ | FW_REG_WRITE);
+
+       fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
+                                                    RING_CONTEXT_STATUS_BUF_BASE(engine),
+                                                    FW_REG_READ);
+
+       engine->fw_domains = fw_domains;
+
+       tasklet_init(&engine->irq_tasklet,
+                    intel_lrc_irq_handler, (unsigned long)engine);
+
+       logical_ring_init_platform_invariants(engine);
+       logical_ring_default_vfuncs(engine);
+       logical_ring_default_irqs(engine);
+}
+
 static int
 logical_ring_init(struct intel_engine_cs *engine)
 {
        struct i915_gem_context *dctx = engine->i915->kernel_context;
        int ret;
 
-       ret = intel_engine_init_breadcrumbs(engine);
-       if (ret)
-               goto error;
-
-       ret = i915_cmd_parser_init_ring(engine);
+       ret = intel_engine_init_common(engine);
        if (ret)
                goto error;
 
@@ -2044,11 +1803,13 @@ error:
        return ret;
 }
 
-static int logical_render_ring_init(struct intel_engine_cs *engine)
+int logical_render_ring_init(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
        int ret;
 
+       logical_ring_setup(engine);
+
        if (HAS_L3_DPF(dev_priv))
                engine->irq_keep_mask |= GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
 
@@ -2058,11 +1819,10 @@ static int logical_render_ring_init(struct intel_engine_cs *engine)
        else
                engine->init_hw = gen8_init_render_ring;
        engine->init_context = gen8_init_rcs_context;
-       engine->cleanup = intel_fini_pipe_control;
        engine->emit_flush = gen8_emit_flush_render;
        engine->emit_request = gen8_emit_request_render;
 
-       ret = intel_init_pipe_control(engine, 4096);
+       ret = intel_engine_create_scratch(engine, 4096);
        if (ret)
                return ret;
 
@@ -2085,160 +1845,11 @@ static int logical_render_ring_init(struct intel_engine_cs *engine)
        return ret;
 }
 
-static const struct logical_ring_info {
-       const char *name;
-       unsigned exec_id;
-       unsigned guc_id;
-       u32 mmio_base;
-       unsigned irq_shift;
-       int (*init)(struct intel_engine_cs *engine);
-} logical_rings[] = {
-       [RCS] = {
-               .name = "render ring",
-               .exec_id = I915_EXEC_RENDER,
-               .guc_id = GUC_RENDER_ENGINE,
-               .mmio_base = RENDER_RING_BASE,
-               .irq_shift = GEN8_RCS_IRQ_SHIFT,
-               .init = logical_render_ring_init,
-       },
-       [BCS] = {
-               .name = "blitter ring",
-               .exec_id = I915_EXEC_BLT,
-               .guc_id = GUC_BLITTER_ENGINE,
-               .mmio_base = BLT_RING_BASE,
-               .irq_shift = GEN8_BCS_IRQ_SHIFT,
-               .init = logical_ring_init,
-       },
-       [VCS] = {
-               .name = "bsd ring",
-               .exec_id = I915_EXEC_BSD,
-               .guc_id = GUC_VIDEO_ENGINE,
-               .mmio_base = GEN6_BSD_RING_BASE,
-               .irq_shift = GEN8_VCS1_IRQ_SHIFT,
-               .init = logical_ring_init,
-       },
-       [VCS2] = {
-               .name = "bsd2 ring",
-               .exec_id = I915_EXEC_BSD,
-               .guc_id = GUC_VIDEO_ENGINE2,
-               .mmio_base = GEN8_BSD2_RING_BASE,
-               .irq_shift = GEN8_VCS2_IRQ_SHIFT,
-               .init = logical_ring_init,
-       },
-       [VECS] = {
-               .name = "video enhancement ring",
-               .exec_id = I915_EXEC_VEBOX,
-               .guc_id = GUC_VIDEOENHANCE_ENGINE,
-               .mmio_base = VEBOX_RING_BASE,
-               .irq_shift = GEN8_VECS_IRQ_SHIFT,
-               .init = logical_ring_init,
-       },
-};
-
-static struct intel_engine_cs *
-logical_ring_setup(struct drm_i915_private *dev_priv, enum intel_engine_id id)
-{
-       const struct logical_ring_info *info = &logical_rings[id];
-       struct intel_engine_cs *engine = &dev_priv->engine[id];
-       enum forcewake_domains fw_domains;
-
-       engine->id = id;
-       engine->name = info->name;
-       engine->exec_id = info->exec_id;
-       engine->guc_id = info->guc_id;
-       engine->mmio_base = info->mmio_base;
-
-       engine->i915 = dev_priv;
-
-       /* Intentionally left blank. */
-       engine->buffer = NULL;
-
-       fw_domains = intel_uncore_forcewake_for_reg(dev_priv,
-                                                   RING_ELSP(engine),
-                                                   FW_REG_WRITE);
-
-       fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
-                                                    RING_CONTEXT_STATUS_PTR(engine),
-                                                    FW_REG_READ | FW_REG_WRITE);
-
-       fw_domains |= intel_uncore_forcewake_for_reg(dev_priv,
-                                                    RING_CONTEXT_STATUS_BUF_BASE(engine),
-                                                    FW_REG_READ);
-
-       engine->fw_domains = fw_domains;
-
-       INIT_LIST_HEAD(&engine->active_list);
-       INIT_LIST_HEAD(&engine->request_list);
-       INIT_LIST_HEAD(&engine->buffers);
-       INIT_LIST_HEAD(&engine->execlist_queue);
-       spin_lock_init(&engine->execlist_lock);
-
-       tasklet_init(&engine->irq_tasklet,
-                    intel_lrc_irq_handler, (unsigned long)engine);
-
-       logical_ring_init_platform_invariants(engine);
-       logical_ring_default_vfuncs(engine);
-       logical_ring_default_irqs(engine, info->irq_shift);
-
-       intel_engine_init_hangcheck(engine);
-       i915_gem_batch_pool_init(&dev_priv->drm, &engine->batch_pool);
-
-       return engine;
-}
-
-/**
- * intel_logical_rings_init() - allocate, populate and init the Engine Command Streamers
- * @dev: DRM device.
- *
- * This function inits the engines for an Execlists submission style (the
- * equivalent in the legacy ringbuffer submission world would be
- * i915_gem_init_engines). It does it only for those engines that are present in
- * the hardware.
- *
- * Return: non-zero if the initialization failed.
- */
-int intel_logical_rings_init(struct drm_device *dev)
+int logical_xcs_ring_init(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       unsigned int mask = 0;
-       unsigned int i;
-       int ret;
-
-       WARN_ON(INTEL_INFO(dev_priv)->ring_mask &
-               GENMASK(sizeof(mask) * BITS_PER_BYTE - 1, I915_NUM_ENGINES));
-
-       for (i = 0; i < ARRAY_SIZE(logical_rings); i++) {
-               if (!HAS_ENGINE(dev_priv, i))
-                       continue;
-
-               if (!logical_rings[i].init)
-                       continue;
+       logical_ring_setup(engine);
 
-               ret = logical_rings[i].init(logical_ring_setup(dev_priv, i));
-               if (ret)
-                       goto cleanup;
-
-               mask |= ENGINE_MASK(i);
-       }
-
-       /*
-        * Catch failures to update logical_rings table when the new engines
-        * are added to the driver by a warning and disabling the forgotten
-        * engines.
-        */
-       if (WARN_ON(mask != INTEL_INFO(dev_priv)->ring_mask)) {
-               struct intel_device_info *info =
-                       (struct intel_device_info *)&dev_priv->info;
-               info->ring_mask = mask;
-       }
-
-       return 0;
-
-cleanup:
-       for (i = 0; i < I915_NUM_ENGINES; i++)
-               intel_logical_ring_cleanup(&dev_priv->engine[i]);
-
-       return ret;
+       return logical_ring_init(engine);
 }
 
 static u32
@@ -2259,24 +1870,24 @@ make_rpcs(struct drm_i915_private *dev_priv)
         * must make an explicit request through RPCS for full
         * enablement.
        */
-       if (INTEL_INFO(dev_priv)->has_slice_pg) {
+       if (INTEL_INFO(dev_priv)->sseu.has_slice_pg) {
                rpcs |= GEN8_RPCS_S_CNT_ENABLE;
-               rpcs |= INTEL_INFO(dev_priv)->slice_total <<
+               rpcs |= hweight8(INTEL_INFO(dev_priv)->sseu.slice_mask) <<
                        GEN8_RPCS_S_CNT_SHIFT;
                rpcs |= GEN8_RPCS_ENABLE;
        }
 
-       if (INTEL_INFO(dev_priv)->has_subslice_pg) {
+       if (INTEL_INFO(dev_priv)->sseu.has_subslice_pg) {
                rpcs |= GEN8_RPCS_SS_CNT_ENABLE;
-               rpcs |= INTEL_INFO(dev_priv)->subslice_per_slice <<
+               rpcs |= hweight8(INTEL_INFO(dev_priv)->sseu.subslice_mask) <<
                        GEN8_RPCS_SS_CNT_SHIFT;
                rpcs |= GEN8_RPCS_ENABLE;
        }
 
-       if (INTEL_INFO(dev_priv)->has_eu_pg) {
-               rpcs |= INTEL_INFO(dev_priv)->eu_per_subslice <<
+       if (INTEL_INFO(dev_priv)->sseu.has_eu_pg) {
+               rpcs |= INTEL_INFO(dev_priv)->sseu.eu_per_subslice <<
                        GEN8_RPCS_EU_MIN_SHIFT;
-               rpcs |= INTEL_INFO(dev_priv)->eu_per_subslice <<
+               rpcs |= INTEL_INFO(dev_priv)->sseu.eu_per_subslice <<
                        GEN8_RPCS_EU_MAX_SHIFT;
                rpcs |= GEN8_RPCS_ENABLE;
        }
@@ -2305,38 +1916,13 @@ static u32 intel_lr_indirect_ctx_offset(struct intel_engine_cs *engine)
        return indirect_ctx_offset;
 }
 
-static int
-populate_lr_context(struct i915_gem_context *ctx,
-                   struct drm_i915_gem_object *ctx_obj,
-                   struct intel_engine_cs *engine,
-                   struct intel_ringbuffer *ringbuf)
+static void execlists_init_reg_state(u32 *reg_state,
+                                    struct i915_gem_context *ctx,
+                                    struct intel_engine_cs *engine,
+                                    struct intel_ring *ring)
 {
-       struct drm_i915_private *dev_priv = ctx->i915;
-       struct i915_hw_ppgtt *ppgtt = ctx->ppgtt;
-       void *vaddr;
-       u32 *reg_state;
-       int ret;
-
-       if (!ppgtt)
-               ppgtt = dev_priv->mm.aliasing_ppgtt;
-
-       ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true);
-       if (ret) {
-               DRM_DEBUG_DRIVER("Could not set to CPU domain\n");
-               return ret;
-       }
-
-       vaddr = i915_gem_object_pin_map(ctx_obj);
-       if (IS_ERR(vaddr)) {
-               ret = PTR_ERR(vaddr);
-               DRM_DEBUG_DRIVER("Could not map object pages! (%d)\n", ret);
-               return ret;
-       }
-       ctx_obj->dirty = true;
-
-       /* The second page of the context object contains some fields which must
-        * be set up prior to the first execution. */
-       reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
+       struct drm_i915_private *dev_priv = engine->i915;
+       struct i915_hw_ppgtt *ppgtt = ctx->ppgtt ?: dev_priv->mm.aliasing_ppgtt;
 
        /* A context is actually a big batch buffer with several MI_LOAD_REGISTER_IMM
         * commands followed by (reg, value) pairs. The values we are setting here are
@@ -2350,19 +1936,16 @@ populate_lr_context(struct i915_gem_context *ctx,
                       _MASKED_BIT_ENABLE(CTX_CTRL_INHIBIT_SYN_CTX_SWITCH |
                                          CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT |
                                          (HAS_RESOURCE_STREAMER(dev_priv) ?
-                                           CTX_CTRL_RS_CTX_ENABLE : 0)));
+                                          CTX_CTRL_RS_CTX_ENABLE : 0)));
        ASSIGN_CTX_REG(reg_state, CTX_RING_HEAD, RING_HEAD(engine->mmio_base),
                       0);
        ASSIGN_CTX_REG(reg_state, CTX_RING_TAIL, RING_TAIL(engine->mmio_base),
                       0);
-       /* Ring buffer start address is not known until the buffer is pinned.
-        * It is written to the context image in execlists_update_context()
-        */
        ASSIGN_CTX_REG(reg_state, CTX_RING_BUFFER_START,
                       RING_START(engine->mmio_base), 0);
        ASSIGN_CTX_REG(reg_state, CTX_RING_BUFFER_CONTROL,
                       RING_CTL(engine->mmio_base),
-                      ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID);
+                      ((ring->size - PAGE_SIZE) & RING_NR_PAGES) | RING_VALID);
        ASSIGN_CTX_REG(reg_state, CTX_BB_HEAD_U,
                       RING_BBADDR_UDW(engine->mmio_base), 0);
        ASSIGN_CTX_REG(reg_state, CTX_BB_HEAD_L,
@@ -2383,9 +1966,9 @@ populate_lr_context(struct i915_gem_context *ctx,
                               RING_INDIRECT_CTX(engine->mmio_base), 0);
                ASSIGN_CTX_REG(reg_state, CTX_RCS_INDIRECT_CTX_OFFSET,
                               RING_INDIRECT_CTX_OFFSET(engine->mmio_base), 0);
-               if (engine->wa_ctx.obj) {
+               if (engine->wa_ctx.vma) {
                        struct i915_ctx_workarounds *wa_ctx = &engine->wa_ctx;
-                       uint32_t ggtt_offset = i915_gem_obj_ggtt_offset(wa_ctx->obj);
+                       u32 ggtt_offset = i915_ggtt_offset(wa_ctx->vma);
 
                        reg_state[CTX_RCS_INDIRECT_CTX+1] =
                                (ggtt_offset + wa_ctx->indirect_ctx.offset * sizeof(uint32_t)) |
@@ -2440,6 +2023,36 @@ populate_lr_context(struct i915_gem_context *ctx,
                ASSIGN_CTX_REG(reg_state, CTX_R_PWR_CLK_STATE, GEN8_R_PWR_CLK_STATE,
                               make_rpcs(dev_priv));
        }
+}
+
+static int
+populate_lr_context(struct i915_gem_context *ctx,
+                   struct drm_i915_gem_object *ctx_obj,
+                   struct intel_engine_cs *engine,
+                   struct intel_ring *ring)
+{
+       void *vaddr;
+       int ret;
+
+       ret = i915_gem_object_set_to_cpu_domain(ctx_obj, true);
+       if (ret) {
+               DRM_DEBUG_DRIVER("Could not set to CPU domain\n");
+               return ret;
+       }
+
+       vaddr = i915_gem_object_pin_map(ctx_obj, I915_MAP_WB);
+       if (IS_ERR(vaddr)) {
+               ret = PTR_ERR(vaddr);
+               DRM_DEBUG_DRIVER("Could not map object pages! (%d)\n", ret);
+               return ret;
+       }
+       ctx_obj->dirty = true;
+
+       /* The second page of the context object contains some fields which must
+        * be set up prior to the first execution. */
+
+       execlists_init_reg_state(vaddr + LRC_STATE_PN * PAGE_SIZE,
+                                ctx, engine, ring);
 
        i915_gem_object_unpin_map(ctx_obj);
 
@@ -2484,26 +2097,14 @@ uint32_t intel_lr_context_size(struct intel_engine_cs *engine)
        return ret;
 }
 
-/**
- * execlists_context_deferred_alloc() - create the LRC specific bits of a context
- * @ctx: LR context to create.
- * @engine: engine to be used with the context.
- *
- * This function can be called more than once, with different engines, if we plan
- * to use the context with them. The context backing objects and the ringbuffers
- * (specially the ringbuffer backing objects) suck a lot of memory up, and that's why
- * the creation is a deferred call: it's better to make sure first that we need to use
- * a given ring with the context.
- *
- * Return: non-zero on error.
- */
 static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
                                            struct intel_engine_cs *engine)
 {
        struct drm_i915_gem_object *ctx_obj;
        struct intel_context *ce = &ctx->engine[engine->id];
+       struct i915_vma *vma;
        uint32_t context_size;
-       struct intel_ringbuffer *ringbuf;
+       struct intel_ring *ring;
        int ret;
 
        WARN_ON(ce->state);
@@ -2519,60 +2120,63 @@ static int execlists_context_deferred_alloc(struct i915_gem_context *ctx,
                return PTR_ERR(ctx_obj);
        }
 
-       ringbuf = intel_engine_create_ringbuffer(engine, ctx->ring_size);
-       if (IS_ERR(ringbuf)) {
-               ret = PTR_ERR(ringbuf);
+       vma = i915_vma_create(ctx_obj, &ctx->i915->ggtt.base, NULL);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto error_deref_obj;
+       }
+
+       ring = intel_engine_create_ring(engine, ctx->ring_size);
+       if (IS_ERR(ring)) {
+               ret = PTR_ERR(ring);
                goto error_deref_obj;
        }
 
-       ret = populate_lr_context(ctx, ctx_obj, engine, ringbuf);
+       ret = populate_lr_context(ctx, ctx_obj, engine, ring);
        if (ret) {
                DRM_DEBUG_DRIVER("Failed to populate LRC: %d\n", ret);
-               goto error_ringbuf;
+               goto error_ring_free;
        }
 
-       ce->ringbuf = ringbuf;
-       ce->state = ctx_obj;
+       ce->ring = ring;
+       ce->state = vma;
        ce->initialised = engine->init_context == NULL;
 
        return 0;
 
-error_ringbuf:
-       intel_ringbuffer_free(ringbuf);
+error_ring_free:
+       intel_ring_free(ring);
 error_deref_obj:
-       drm_gem_object_unreference(&ctx_obj->base);
-       ce->ringbuf = NULL;
-       ce->state = NULL;
+       i915_gem_object_put(ctx_obj);
        return ret;
 }
 
-void intel_lr_context_reset(struct drm_i915_private *dev_priv,
-                           struct i915_gem_context *ctx)
+void intel_lr_context_resume(struct drm_i915_private *dev_priv)
 {
+       struct i915_gem_context *ctx = dev_priv->kernel_context;
        struct intel_engine_cs *engine;
 
        for_each_engine(engine, dev_priv) {
                struct intel_context *ce = &ctx->engine[engine->id];
-               struct drm_i915_gem_object *ctx_obj = ce->state;
                void *vaddr;
                uint32_t *reg_state;
 
-               if (!ctx_obj)
+               if (!ce->state)
                        continue;
 
-               vaddr = i915_gem_object_pin_map(ctx_obj);
+               vaddr = i915_gem_object_pin_map(ce->state->obj, I915_MAP_WB);
                if (WARN_ON(IS_ERR(vaddr)))
                        continue;
 
                reg_state = vaddr + LRC_STATE_PN * PAGE_SIZE;
-               ctx_obj->dirty = true;
 
                reg_state[CTX_RING_HEAD+1] = 0;
                reg_state[CTX_RING_TAIL+1] = 0;
 
-               i915_gem_object_unpin_map(ctx_obj);
+               ce->state->obj->dirty = true;
+               i915_gem_object_unpin_map(ce->state->obj);
 
-               ce->ringbuf->head = 0;
-               ce->ringbuf->tail = 0;
+               ce->ring->head = 0;
+               ce->ring->tail = 0;
        }
 }
index 2b8255c..4fed816 100644 (file)
 #define GEN8_LR_CONTEXT_ALIGN 4096
 
 /* Execlists regs */
-#define RING_ELSP(ring)                                _MMIO((ring)->mmio_base + 0x230)
-#define RING_EXECLIST_STATUS_LO(ring)          _MMIO((ring)->mmio_base + 0x234)
-#define RING_EXECLIST_STATUS_HI(ring)          _MMIO((ring)->mmio_base + 0x234 + 4)
-#define RING_CONTEXT_CONTROL(ring)             _MMIO((ring)->mmio_base + 0x244)
+#define RING_ELSP(engine)                      _MMIO((engine)->mmio_base + 0x230)
+#define RING_EXECLIST_STATUS_LO(engine)                _MMIO((engine)->mmio_base + 0x234)
+#define RING_EXECLIST_STATUS_HI(engine)                _MMIO((engine)->mmio_base + 0x234 + 4)
+#define RING_CONTEXT_CONTROL(engine)           _MMIO((engine)->mmio_base + 0x244)
 #define          CTX_CTRL_INHIBIT_SYN_CTX_SWITCH       (1 << 3)
 #define          CTX_CTRL_ENGINE_CTX_RESTORE_INHIBIT   (1 << 0)
 #define   CTX_CTRL_RS_CTX_ENABLE                (1 << 1)
-#define RING_CONTEXT_STATUS_BUF_BASE(ring)     _MMIO((ring)->mmio_base + 0x370)
-#define RING_CONTEXT_STATUS_BUF_LO(ring, i)    _MMIO((ring)->mmio_base + 0x370 + (i) * 8)
-#define RING_CONTEXT_STATUS_BUF_HI(ring, i)    _MMIO((ring)->mmio_base + 0x370 + (i) * 8 + 4)
-#define RING_CONTEXT_STATUS_PTR(ring)          _MMIO((ring)->mmio_base + 0x3a0)
+#define RING_CONTEXT_STATUS_BUF_BASE(engine)   _MMIO((engine)->mmio_base + 0x370)
+#define RING_CONTEXT_STATUS_BUF_LO(engine, i)  _MMIO((engine)->mmio_base + 0x370 + (i) * 8)
+#define RING_CONTEXT_STATUS_BUF_HI(engine, i)  _MMIO((engine)->mmio_base + 0x370 + (i) * 8 + 4)
+#define RING_CONTEXT_STATUS_PTR(engine)                _MMIO((engine)->mmio_base + 0x3a0)
 
 /* The docs specify that the write pointer wraps around after 5h, "After status
  * is written out to the last available status QW at offset 5h, this pointer
@@ -67,35 +67,10 @@ int intel_logical_ring_alloc_request_extras(struct drm_i915_gem_request *request
 int intel_logical_ring_reserve_space(struct drm_i915_gem_request *request);
 void intel_logical_ring_stop(struct intel_engine_cs *engine);
 void intel_logical_ring_cleanup(struct intel_engine_cs *engine);
-int intel_logical_rings_init(struct drm_device *dev);
+int logical_render_ring_init(struct intel_engine_cs *engine);
+int logical_xcs_ring_init(struct intel_engine_cs *engine);
 
-int logical_ring_flush_all_caches(struct drm_i915_gem_request *req);
-/**
- * intel_logical_ring_advance() - advance the ringbuffer tail
- * @ringbuf: Ringbuffer to advance.
- *
- * The tail is only updated in our logical ringbuffer struct.
- */
-static inline void intel_logical_ring_advance(struct intel_ringbuffer *ringbuf)
-{
-       ringbuf->tail &= ringbuf->size - 1;
-}
-/**
- * intel_logical_ring_emit() - write a DWORD to the ringbuffer.
- * @ringbuf: Ringbuffer to write to.
- * @data: DWORD to write.
- */
-static inline void intel_logical_ring_emit(struct intel_ringbuffer *ringbuf,
-                                          u32 data)
-{
-       iowrite32(data, ringbuf->virtual_start + ringbuf->tail);
-       ringbuf->tail += 4;
-}
-static inline void intel_logical_ring_emit_reg(struct intel_ringbuffer *ringbuf,
-                                              i915_reg_t reg)
-{
-       intel_logical_ring_emit(ringbuf, i915_mmio_reg_offset(reg));
-}
+int intel_engines_init(struct drm_device *dev);
 
 /* Logical Ring Contexts */
 
@@ -112,19 +87,13 @@ void intel_lr_context_unpin(struct i915_gem_context *ctx,
 
 struct drm_i915_private;
 
-void intel_lr_context_reset(struct drm_i915_private *dev_priv,
-                           struct i915_gem_context *ctx);
+void intel_lr_context_resume(struct drm_i915_private *dev_priv);
 uint64_t intel_lr_context_descriptor(struct i915_gem_context *ctx,
                                     struct intel_engine_cs *engine);
 
 /* Execlists */
 int intel_sanitize_enable_execlists(struct drm_i915_private *dev_priv,
                                    int enable_execlists);
-struct i915_execbuffer_params;
-int intel_execlists_submission(struct i915_execbuffer_params *params,
-                              struct drm_i915_gem_execbuffer2 *args,
-                              struct list_head *vmas);
-
-void intel_execlists_cancel_requests(struct intel_engine_cs *engine);
+void intel_execlists_enable_submission(struct drm_i915_private *dev_priv);
 
 #endif /* _INTEL_LRC_H_ */
index 4955047..e1d47d5 100644 (file)
@@ -48,6 +48,20 @@ struct intel_lvds_connector {
        struct notifier_block lid_notifier;
 };
 
+struct intel_lvds_pps {
+       /* 100us units */
+       int t1_t2;
+       int t3;
+       int t4;
+       int t5;
+       int tx;
+
+       int divider;
+
+       int port;
+       bool powerdown_on_reset;
+};
+
 struct intel_lvds_encoder {
        struct intel_encoder base;
 
@@ -55,6 +69,9 @@ struct intel_lvds_encoder {
        i915_reg_t reg;
        u32 a3_power;
 
+       struct intel_lvds_pps init_pps;
+       u32 init_lvds_val;
+
        struct intel_lvds_connector *attached_connector;
 };
 
@@ -136,28 +153,108 @@ static void intel_lvds_get_config(struct intel_encoder *encoder,
        pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
 }
 
-static void intel_pre_enable_lvds(struct intel_encoder *encoder)
+static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
+                                       struct intel_lvds_pps *pps)
+{
+       u32 val;
+
+       pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
+
+       val = I915_READ(PP_ON_DELAYS(0));
+       pps->port = (val & PANEL_PORT_SELECT_MASK) >>
+                   PANEL_PORT_SELECT_SHIFT;
+       pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >>
+                    PANEL_POWER_UP_DELAY_SHIFT;
+       pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >>
+                 PANEL_LIGHT_ON_DELAY_SHIFT;
+
+       val = I915_READ(PP_OFF_DELAYS(0));
+       pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >>
+                 PANEL_POWER_DOWN_DELAY_SHIFT;
+       pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >>
+                 PANEL_LIGHT_OFF_DELAY_SHIFT;
+
+       val = I915_READ(PP_DIVISOR(0));
+       pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >>
+                      PP_REFERENCE_DIVIDER_SHIFT;
+       val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >>
+             PANEL_POWER_CYCLE_DELAY_SHIFT;
+       /*
+        * Remove the BSpec specified +1 (100ms) offset that accounts for a
+        * too short power-cycle delay due to the asynchronous programming of
+        * the register.
+        */
+       if (val)
+               val--;
+       /* Convert from 100ms to 100us units */
+       pps->t4 = val * 1000;
+
+       if (INTEL_INFO(dev_priv)->gen <= 4 &&
+           pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
+               DRM_DEBUG_KMS("Panel power timings uninitialized, "
+                             "setting defaults\n");
+               /* Set T2 to 40ms and T5 to 200ms in 100 usec units */
+               pps->t1_t2 = 40 * 10;
+               pps->t5 = 200 * 10;
+               /* Set T3 to 35ms and Tx to 200ms in 100 usec units */
+               pps->t3 = 35 * 10;
+               pps->tx = 200 * 10;
+       }
+
+       DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
+                        "divider %d port %d powerdown_on_reset %d\n",
+                        pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
+                        pps->divider, pps->port, pps->powerdown_on_reset);
+}
+
+static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
+                                  struct intel_lvds_pps *pps)
+{
+       u32 val;
+
+       val = I915_READ(PP_CONTROL(0));
+       WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
+       if (pps->powerdown_on_reset)
+               val |= PANEL_POWER_RESET;
+       I915_WRITE(PP_CONTROL(0), val);
+
+       I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) |
+                                   (pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) |
+                                   (pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT));
+       I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) |
+                                    (pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT));
+
+       val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT;
+       val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) <<
+              PANEL_POWER_CYCLE_DELAY_SHIFT;
+       I915_WRITE(PP_DIVISOR(0), val);
+}
+
+static void intel_pre_enable_lvds(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *pipe_config,
+                                 struct drm_connector_state *conn_state)
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
-       struct drm_device *dev = encoder->base.dev;
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+       struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
        int pipe = crtc->pipe;
        u32 temp;
 
-       if (HAS_PCH_SPLIT(dev)) {
+       if (HAS_PCH_SPLIT(dev_priv)) {
                assert_fdi_rx_pll_disabled(dev_priv, pipe);
                assert_shared_dpll_disabled(dev_priv,
-                                           crtc->config->shared_dpll);
+                                           pipe_config->shared_dpll);
        } else {
                assert_pll_disabled(dev_priv, pipe);
        }
 
-       temp = I915_READ(lvds_encoder->reg);
+       intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
+
+       temp = lvds_encoder->init_lvds_val;
        temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
 
-       if (HAS_PCH_CPT(dev)) {
+       if (HAS_PCH_CPT(dev_priv)) {
                temp &= ~PORT_TRANS_SEL_MASK;
                temp |= PORT_TRANS_SEL_CPT(pipe);
        } else {
@@ -170,7 +267,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
 
        /* set the corresponsding LVDS_BORDER bit */
        temp &= ~LVDS_BORDER_ENABLE;
-       temp |= crtc->config->gmch_pfit.lvds_border_bits;
+       temp |= pipe_config->gmch_pfit.lvds_border_bits;
        /* Set the B0-B3 data pairs corresponding to whether we're going to
         * set the DPLLs for dual-channel mode or not.
         */
@@ -193,7 +290,7 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
        if (IS_GEN4(dev_priv)) {
                /* Bspec wording suggests that LVDS port dithering only exists
                 * for 18bpp panels. */
-               if (crtc->config->dither && crtc->config->pipe_bpp == 18)
+               if (pipe_config->dither && pipe_config->pipe_bpp == 18)
                        temp |= LVDS_ENABLE_DITHER;
                else
                        temp &= ~LVDS_ENABLE_DITHER;
@@ -210,57 +307,45 @@ static void intel_pre_enable_lvds(struct intel_encoder *encoder)
 /**
  * Sets the power state for the panel.
  */
-static void intel_enable_lvds(struct intel_encoder *encoder)
+static void intel_enable_lvds(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
                &lvds_encoder->attached_connector->base;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       i915_reg_t ctl_reg, stat_reg;
-
-       if (HAS_PCH_SPLIT(dev)) {
-               ctl_reg = PCH_PP_CONTROL;
-               stat_reg = PCH_PP_STATUS;
-       } else {
-               ctl_reg = PP_CONTROL;
-               stat_reg = PP_STATUS;
-       }
 
        I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
 
-       I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
+       I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
        POSTING_READ(lvds_encoder->reg);
-       if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, PP_ON, 1000))
+       if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
                DRM_ERROR("timed out waiting for panel to power on\n");
 
        intel_panel_enable_backlight(intel_connector);
 }
 
-static void intel_disable_lvds(struct intel_encoder *encoder)
+static void intel_disable_lvds(struct intel_encoder *encoder,
+                              struct intel_crtc_state *old_crtc_state,
+                              struct drm_connector_state *old_conn_state)
 {
-       struct drm_device *dev = encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       i915_reg_t ctl_reg, stat_reg;
-
-       if (HAS_PCH_SPLIT(dev)) {
-               ctl_reg = PCH_PP_CONTROL;
-               stat_reg = PCH_PP_STATUS;
-       } else {
-               ctl_reg = PP_CONTROL;
-               stat_reg = PP_STATUS;
-       }
+       struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
 
-       I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
-       if (intel_wait_for_register(dev_priv, stat_reg, PP_ON, 0, 1000))
+       I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
+       if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000))
                DRM_ERROR("timed out waiting for panel to power off\n");
 
        I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
        POSTING_READ(lvds_encoder->reg);
 }
 
-static void gmch_disable_lvds(struct intel_encoder *encoder)
+static void gmch_disable_lvds(struct intel_encoder *encoder,
+                             struct intel_crtc_state *old_crtc_state,
+                             struct drm_connector_state *old_conn_state)
+
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
@@ -268,10 +353,12 @@ static void gmch_disable_lvds(struct intel_encoder *encoder)
 
        intel_panel_disable_backlight(intel_connector);
 
-       intel_disable_lvds(encoder);
+       intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
 }
 
-static void pch_disable_lvds(struct intel_encoder *encoder)
+static void pch_disable_lvds(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
        struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
        struct intel_connector *intel_connector =
@@ -280,9 +367,11 @@ static void pch_disable_lvds(struct intel_encoder *encoder)
        intel_panel_disable_backlight(intel_connector);
 }
 
-static void pch_post_disable_lvds(struct intel_encoder *encoder)
+static void pch_post_disable_lvds(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
-       intel_disable_lvds(encoder);
+       intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
 }
 
 static enum drm_mode_status
@@ -304,7 +393,8 @@ intel_lvds_mode_valid(struct drm_connector *connector,
 }
 
 static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
-                                     struct intel_crtc_state *pipe_config)
+                                     struct intel_crtc_state *pipe_config,
+                                     struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = intel_encoder->base.dev;
        struct intel_lvds_encoder *lvds_encoder =
@@ -900,17 +990,6 @@ void intel_lvds_init(struct drm_device *dev)
        int pipe;
        u8 pin;
 
-       /*
-        * Unlock registers and just leave them unlocked. Do this before
-        * checking quirk lists to avoid bogus WARNINGs.
-        */
-       if (HAS_PCH_SPLIT(dev)) {
-               I915_WRITE(PCH_PP_CONTROL,
-                          I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
-       } else if (INTEL_INFO(dev_priv)->gen < 5) {
-               I915_WRITE(PP_CONTROL,
-                          I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
-       }
        if (!intel_lvds_supported(dev))
                return;
 
@@ -943,18 +1022,6 @@ void intel_lvds_init(struct drm_device *dev)
                DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
        }
 
-        /* Set the Panel Power On/Off timings if uninitialized. */
-       if (INTEL_INFO(dev_priv)->gen < 5 &&
-           I915_READ(PP_ON_DELAYS) == 0 && I915_READ(PP_OFF_DELAYS) == 0) {
-               /* Set T2 to 40ms and T5 to 200ms */
-               I915_WRITE(PP_ON_DELAYS, 0x019007d0);
-
-               /* Set T3 to 35ms and Tx to 200ms */
-               I915_WRITE(PP_OFF_DELAYS, 0x015e07d0);
-
-               DRM_DEBUG_KMS("Panel power timings uninitialized, setting defaults\n");
-       }
-
        lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
        if (!lvds_encoder)
                return;
@@ -1020,6 +1087,10 @@ void intel_lvds_init(struct drm_device *dev)
                                      dev->mode_config.scaling_mode_property,
                                      DRM_MODE_SCALE_ASPECT);
        intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
+
+       intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
+       lvds_encoder->init_lvds_val = lvds;
+
        /*
         * LVDS discovery:
         * 1) check for EDID on DDC
@@ -1054,17 +1125,6 @@ void intel_lvds_init(struct drm_device *dev)
        }
        lvds_connector->base.edid = edid;
 
-       if (IS_ERR_OR_NULL(edid)) {
-               /* Didn't get an EDID, so
-                * Set wide sync ranges so we get all modes
-                * handed to valid_mode for checking
-                */
-               connector->display_info.min_vfreq = 0;
-               connector->display_info.max_vfreq = 200;
-               connector->display_info.min_hfreq = 0;
-               connector->display_info.max_hfreq = 200;
-       }
-
        list_for_each_entry(scan, &connector->probed_modes, head) {
                if (scan->type & DRM_MODE_TYPE_PREFERRED) {
                        DRM_DEBUG_KMS("using preferred mode from EDID: ");
index 927825f..80bb924 100644 (file)
@@ -97,7 +97,8 @@ struct drm_i915_mocs_table {
  *       end.
  */
 static const struct drm_i915_mocs_entry skylake_mocs_table[] = {
-       { /* 0x00000009 */
+       [I915_MOCS_UNCACHED] = {
+         /* 0x00000009 */
          .control_value = LE_CACHEABILITY(LE_UC) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
                           LE_LRUM(0) | LE_AOM(0) | LE_RSC(0) | LE_SCC(0) |
@@ -106,7 +107,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = {
          /* 0x0010 */
          .l3cc_value =    L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC),
        },
-       {
+       [I915_MOCS_PTE] = {
          /* 0x00000038 */
          .control_value = LE_CACHEABILITY(LE_PAGETABLE) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
@@ -115,7 +116,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = {
          /* 0x0030 */
          .l3cc_value =    L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
        },
-       {
+       [I915_MOCS_CACHED] = {
          /* 0x0000003b */
          .control_value = LE_CACHEABILITY(LE_WB) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
@@ -128,7 +129,7 @@ static const struct drm_i915_mocs_entry skylake_mocs_table[] = {
 
 /* NOTE: the LE_TGT_CACHE is not used on Broxton */
 static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
-       {
+       [I915_MOCS_UNCACHED] = {
          /* 0x00000009 */
          .control_value = LE_CACHEABILITY(LE_UC) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
@@ -138,7 +139,7 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
          /* 0x0010 */
          .l3cc_value =    L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_UC),
        },
-       {
+       [I915_MOCS_PTE] = {
          /* 0x00000038 */
          .control_value = LE_CACHEABILITY(LE_PAGETABLE) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
@@ -148,7 +149,7 @@ static const struct drm_i915_mocs_entry broxton_mocs_table[] = {
          /* 0x0030 */
          .l3cc_value =    L3_ESC(0) | L3_SCC(0) | L3_CACHEABILITY(L3_WB),
        },
-       {
+       [I915_MOCS_CACHED] = {
          /* 0x00000039 */
          .control_value = LE_CACHEABILITY(LE_UC) |
                           LE_TGT_CACHE(LE_TC_LLC_ELLC) |
@@ -203,9 +204,9 @@ static bool get_mocs_settings(struct drm_i915_private *dev_priv,
        return result;
 }
 
-static i915_reg_t mocs_register(enum intel_engine_id ring, int index)
+static i915_reg_t mocs_register(enum intel_engine_id engine_id, int index)
 {
-       switch (ring) {
+       switch (engine_id) {
        case RCS:
                return GEN9_GFX_MOCS(index);
        case VCS:
@@ -217,7 +218,7 @@ static i915_reg_t mocs_register(enum intel_engine_id ring, int index)
        case VCS2:
                return GEN9_MFX1_MOCS(index);
        default:
-               MISSING_CASE(ring);
+               MISSING_CASE(engine_id);
                return INVALID_MMIO_REG;
        }
 }
@@ -275,7 +276,7 @@ int intel_mocs_init_engine(struct intel_engine_cs *engine)
 static int emit_mocs_control_table(struct drm_i915_gem_request *req,
                                   const struct drm_i915_mocs_table *table)
 {
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
+       struct intel_ring *ring = req->ring;
        enum intel_engine_id engine = req->engine->id;
        unsigned int index;
        int ret;
@@ -287,14 +288,11 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_logical_ring_emit(ringbuf,
-                               MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES));
 
        for (index = 0; index < table->size; index++) {
-               intel_logical_ring_emit_reg(ringbuf,
-                                           mocs_register(engine, index));
-               intel_logical_ring_emit(ringbuf,
-                                       table->table[index].control_value);
+               intel_ring_emit_reg(ring, mocs_register(engine, index));
+               intel_ring_emit(ring, table->table[index].control_value);
        }
 
        /*
@@ -306,14 +304,12 @@ static int emit_mocs_control_table(struct drm_i915_gem_request *req,
         * that value to all the used entries.
         */
        for (; index < GEN9_NUM_MOCS_ENTRIES; index++) {
-               intel_logical_ring_emit_reg(ringbuf,
-                                           mocs_register(engine, index));
-               intel_logical_ring_emit(ringbuf,
-                                       table->table[0].control_value);
+               intel_ring_emit_reg(ring, mocs_register(engine, index));
+               intel_ring_emit(ring, table->table[0].control_value);
        }
 
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -340,7 +336,7 @@ static inline u32 l3cc_combine(const struct drm_i915_mocs_table *table,
 static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
                                const struct drm_i915_mocs_table *table)
 {
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
+       struct intel_ring *ring = req->ring;
        unsigned int i;
        int ret;
 
@@ -351,19 +347,18 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_logical_ring_emit(ringbuf,
+       intel_ring_emit(ring,
                        MI_LOAD_REGISTER_IMM(GEN9_NUM_MOCS_ENTRIES / 2));
 
        for (i = 0; i < table->size/2; i++) {
-               intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
-               intel_logical_ring_emit(ringbuf,
-                                       l3cc_combine(table, 2*i, 2*i+1));
+               intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i));
+               intel_ring_emit(ring, l3cc_combine(table, 2*i, 2*i+1));
        }
 
        if (table->size & 0x01) {
                /* Odd table size - 1 left over */
-               intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
-               intel_logical_ring_emit(ringbuf, l3cc_combine(table, 2*i, 0));
+               intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i));
+               intel_ring_emit(ring, l3cc_combine(table, 2*i, 0));
                i++;
        }
 
@@ -373,12 +368,12 @@ static int emit_mocs_l3cc_table(struct drm_i915_gem_request *req,
         * they are reserved by the hardware.
         */
        for (; i < GEN9_NUM_MOCS_ENTRIES / 2; i++) {
-               intel_logical_ring_emit_reg(ringbuf, GEN9_LNCFCMOCS(i));
-               intel_logical_ring_emit(ringbuf, l3cc_combine(table, 0, 0));
+               intel_ring_emit_reg(ring, GEN9_LNCFCMOCS(i));
+               intel_ring_emit(ring, l3cc_combine(table, 0, 0));
        }
 
-       intel_logical_ring_emit(ringbuf, MI_NOOP);
-       intel_logical_ring_advance(ringbuf);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
index 4640299..a8bd9f7 100644 (file)
@@ -54,6 +54,6 @@
 
 int intel_rcs_context_init_mocs(struct drm_i915_gem_request *req);
 void intel_mocs_init_l3cc_table(struct drm_device *dev);
-int intel_mocs_init_engine(struct intel_engine_cs *ring);
+int intel_mocs_init_engine(struct intel_engine_cs *engine);
 
 #endif
index f2584d0..951e834 100644 (file)
@@ -25,7 +25,6 @@
 
 #include <linux/slab.h>
 #include <linux/i2c.h>
-#include <linux/fb.h>
 #include <drm/drm_edid.h>
 #include <drm/drmP.h>
 #include "intel_drv.h"
index 3212d88..a24bc8c 100644 (file)
@@ -30,6 +30,7 @@
 #include "i915_drv.h"
 #include "i915_reg.h"
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 
 /* Limits for overlay size. According to intel doc, the real limits are:
  * Y width: 4095, UV width (planar): 2047, Y height: 2047,
@@ -170,8 +171,8 @@ struct overlay_registers {
 struct intel_overlay {
        struct drm_i915_private *i915;
        struct intel_crtc *crtc;
-       struct drm_i915_gem_object *vid_bo;
-       struct drm_i915_gem_object *old_vid_bo;
+       struct i915_vma *vma;
+       struct i915_vma *old_vma;
        bool active;
        bool pfit_active;
        u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
@@ -183,8 +184,7 @@ struct intel_overlay {
        u32 flip_addr;
        struct drm_i915_gem_object *reg_bo;
        /* flip handling */
-       struct drm_i915_gem_request *last_flip_req;
-       void (*flip_tail)(struct intel_overlay *);
+       struct i915_gem_active last_flip;
 };
 
 static struct overlay_registers __iomem *
@@ -196,7 +196,7 @@ intel_overlay_map_regs(struct intel_overlay *overlay)
        if (OVERLAY_NEEDS_PHYSICAL(dev_priv))
                regs = (struct overlay_registers __iomem *)overlay->reg_bo->phys_handle->vaddr;
        else
-               regs = io_mapping_map_wc(dev_priv->ggtt.mappable,
+               regs = io_mapping_map_wc(&dev_priv->ggtt.mappable,
                                         overlay->flip_addr,
                                         PAGE_SIZE);
 
@@ -210,37 +210,46 @@ static void intel_overlay_unmap_regs(struct intel_overlay *overlay,
                io_mapping_unmap(regs);
 }
 
-static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
+static void intel_overlay_submit_request(struct intel_overlay *overlay,
                                         struct drm_i915_gem_request *req,
-                                        void (*tail)(struct intel_overlay *))
+                                        i915_gem_retire_fn retire)
 {
-       int ret;
-
-       WARN_ON(overlay->last_flip_req);
-       i915_gem_request_assign(&overlay->last_flip_req, req);
+       GEM_BUG_ON(i915_gem_active_peek(&overlay->last_flip,
+                                       &overlay->i915->drm.struct_mutex));
+       overlay->last_flip.retire = retire;
+       i915_gem_active_set(&overlay->last_flip, req);
        i915_add_request(req);
+}
 
-       overlay->flip_tail = tail;
-       ret = i915_wait_request(overlay->last_flip_req);
-       if (ret)
-               return ret;
+static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
+                                        struct drm_i915_gem_request *req,
+                                        i915_gem_retire_fn retire)
+{
+       intel_overlay_submit_request(overlay, req, retire);
+       return i915_gem_active_retire(&overlay->last_flip,
+                                     &overlay->i915->drm.struct_mutex);
+}
 
-       i915_gem_request_assign(&overlay->last_flip_req, NULL);
-       return 0;
+static struct drm_i915_gem_request *alloc_request(struct intel_overlay *overlay)
+{
+       struct drm_i915_private *dev_priv = overlay->i915;
+       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
+
+       return i915_gem_request_alloc(engine, dev_priv->kernel_context);
 }
 
 /* overlay needs to be disable in OCMD reg */
 static int intel_overlay_on(struct intel_overlay *overlay)
 {
        struct drm_i915_private *dev_priv = overlay->i915;
-       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
        struct drm_i915_gem_request *req;
+       struct intel_ring *ring;
        int ret;
 
        WARN_ON(overlay->active);
        WARN_ON(IS_I830(dev_priv) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE));
 
-       req = i915_gem_request_alloc(engine, NULL);
+       req = alloc_request(overlay);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
@@ -252,11 +261,12 @@ static int intel_overlay_on(struct intel_overlay *overlay)
 
        overlay->active = true;
 
-       intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_ON);
-       intel_ring_emit(engine, overlay->flip_addr | OFC_UPDATE);
-       intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       ring = req->ring;
+       intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_ON);
+       intel_ring_emit(ring, overlay->flip_addr | OFC_UPDATE);
+       intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return intel_overlay_do_wait_request(overlay, req, NULL);
 }
@@ -266,8 +276,8 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
                                  bool load_polyphase_filter)
 {
        struct drm_i915_private *dev_priv = overlay->i915;
-       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
        struct drm_i915_gem_request *req;
+       struct intel_ring *ring;
        u32 flip_addr = overlay->flip_addr;
        u32 tmp;
        int ret;
@@ -282,7 +292,7 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
        if (tmp & (1 << 17))
                DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
 
-       req = i915_gem_request_alloc(engine, NULL);
+       req = alloc_request(overlay);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
@@ -292,38 +302,48 @@ static int intel_overlay_continue(struct intel_overlay *overlay,
                return ret;
        }
 
-       intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
-       intel_ring_emit(engine, flip_addr);
-       intel_ring_advance(engine);
+       ring = req->ring;
+       intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
+       intel_ring_emit(ring, flip_addr);
+       intel_ring_advance(ring);
 
-       WARN_ON(overlay->last_flip_req);
-       i915_gem_request_assign(&overlay->last_flip_req, req);
-       i915_add_request(req);
+       intel_overlay_submit_request(overlay, req, NULL);
 
        return 0;
 }
 
-static void intel_overlay_release_old_vid_tail(struct intel_overlay *overlay)
+static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active,
+                                              struct drm_i915_gem_request *req)
 {
-       struct drm_i915_gem_object *obj = overlay->old_vid_bo;
+       struct intel_overlay *overlay =
+               container_of(active, typeof(*overlay), last_flip);
+       struct i915_vma *vma;
 
-       i915_gem_object_ggtt_unpin(obj);
-       drm_gem_object_unreference(&obj->base);
+       vma = fetch_and_zero(&overlay->old_vma);
+       if (WARN_ON(!vma))
+               return;
 
-       overlay->old_vid_bo = NULL;
+       i915_gem_track_fb(vma->obj, NULL,
+                         INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
+
+       i915_gem_object_unpin_from_display_plane(vma);
+       i915_vma_put(vma);
 }
 
-static void intel_overlay_off_tail(struct intel_overlay *overlay)
+static void intel_overlay_off_tail(struct i915_gem_active *active,
+                                  struct drm_i915_gem_request *req)
 {
-       struct drm_i915_gem_object *obj = overlay->vid_bo;
+       struct intel_overlay *overlay =
+               container_of(active, typeof(*overlay), last_flip);
+       struct i915_vma *vma;
 
        /* never have the overlay hw on without showing a frame */
-       if (WARN_ON(!obj))
+       vma = fetch_and_zero(&overlay->vma);
+       if (WARN_ON(!vma))
                return;
 
-       i915_gem_object_ggtt_unpin(obj);
-       drm_gem_object_unreference(&obj->base);
-       overlay->vid_bo = NULL;
+       i915_gem_object_unpin_from_display_plane(vma);
+       i915_vma_put(vma);
 
        overlay->crtc->overlay = NULL;
        overlay->crtc = NULL;
@@ -334,8 +354,8 @@ static void intel_overlay_off_tail(struct intel_overlay *overlay)
 static int intel_overlay_off(struct intel_overlay *overlay)
 {
        struct drm_i915_private *dev_priv = overlay->i915;
-       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
        struct drm_i915_gem_request *req;
+       struct intel_ring *ring;
        u32 flip_addr = overlay->flip_addr;
        int ret;
 
@@ -347,7 +367,7 @@ static int intel_overlay_off(struct intel_overlay *overlay)
         * of the hw. Do it in both cases */
        flip_addr |= OFC_UPDATE;
 
-       req = i915_gem_request_alloc(engine, NULL);
+       req = alloc_request(overlay);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
@@ -357,46 +377,36 @@ static int intel_overlay_off(struct intel_overlay *overlay)
                return ret;
        }
 
+       ring = req->ring;
        /* wait for overlay to go idle */
-       intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
-       intel_ring_emit(engine, flip_addr);
-       intel_ring_emit(engine, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
+       intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
+       intel_ring_emit(ring, flip_addr);
+       intel_ring_emit(ring, MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
        /* turn overlay off */
        if (IS_I830(dev_priv)) {
                /* Workaround: Don't disable the overlay fully, since otherwise
                 * it dies on the next OVERLAY_ON cmd. */
-               intel_ring_emit(engine, MI_NOOP);
-               intel_ring_emit(engine, MI_NOOP);
-               intel_ring_emit(engine, MI_NOOP);
+               intel_ring_emit(ring, MI_NOOP);
+               intel_ring_emit(ring, MI_NOOP);
+               intel_ring_emit(ring, MI_NOOP);
        } else {
-               intel_ring_emit(engine, MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
-               intel_ring_emit(engine, flip_addr);
-               intel_ring_emit(engine,
+               intel_ring_emit(ring, MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
+               intel_ring_emit(ring, flip_addr);
+               intel_ring_emit(ring,
                                MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
        }
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
-       return intel_overlay_do_wait_request(overlay, req, intel_overlay_off_tail);
+       return intel_overlay_do_wait_request(overlay, req,
+                                            intel_overlay_off_tail);
 }
 
 /* recover from an interruption due to a signal
  * We have to be careful not to repeat work forever an make forward progess. */
 static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
 {
-       int ret;
-
-       if (overlay->last_flip_req == NULL)
-               return 0;
-
-       ret = i915_wait_request(overlay->last_flip_req);
-       if (ret)
-               return ret;
-
-       if (overlay->flip_tail)
-               overlay->flip_tail(overlay);
-
-       i915_gem_request_assign(&overlay->last_flip_req, NULL);
-       return 0;
+       return i915_gem_active_retire(&overlay->last_flip,
+                                     &overlay->i915->drm.struct_mutex);
 }
 
 /* Wait for pending overlay flip and release old frame.
@@ -406,7 +416,6 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
 static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
 {
        struct drm_i915_private *dev_priv = overlay->i915;
-       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
        int ret;
 
        lockdep_assert_held(&dev_priv->drm.struct_mutex);
@@ -414,14 +423,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
        /* Only wait if there is actually an old frame to release to
         * guarantee forward progress.
         */
-       if (!overlay->old_vid_bo)
+       if (!overlay->old_vma)
                return 0;
 
        if (I915_READ(ISR) & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT) {
                /* synchronous slowpath */
                struct drm_i915_gem_request *req;
+               struct intel_ring *ring;
 
-               req = i915_gem_request_alloc(engine, NULL);
+               req = alloc_request(overlay);
                if (IS_ERR(req))
                        return PTR_ERR(req);
 
@@ -431,22 +441,19 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
                        return ret;
                }
 
-               intel_ring_emit(engine,
+               ring = req->ring;
+               intel_ring_emit(ring,
                                MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
-               intel_ring_emit(engine, MI_NOOP);
-               intel_ring_advance(engine);
+               intel_ring_emit(ring, MI_NOOP);
+               intel_ring_advance(ring);
 
                ret = intel_overlay_do_wait_request(overlay, req,
                                                    intel_overlay_release_old_vid_tail);
                if (ret)
                        return ret;
-       }
+       } else
+               intel_overlay_release_old_vid_tail(&overlay->last_flip, NULL);
 
-       intel_overlay_release_old_vid_tail(overlay);
-
-
-       i915_gem_track_fb(overlay->old_vid_bo, NULL,
-                         INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
        return 0;
 }
 
@@ -459,7 +466,6 @@ void intel_overlay_reset(struct drm_i915_private *dev_priv)
 
        intel_overlay_release_old_vid(overlay);
 
-       overlay->last_flip_req = NULL;
        overlay->old_xscale = 0;
        overlay->old_yscale = 0;
        overlay->crtc = NULL;
@@ -740,6 +746,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
        struct drm_i915_private *dev_priv = overlay->i915;
        u32 swidth, swidthsw, sheight, ostride;
        enum pipe pipe = overlay->crtc->pipe;
+       struct i915_vma *vma;
 
        lockdep_assert_held(&dev_priv->drm.struct_mutex);
        WARN_ON(!drm_modeset_is_locked(&dev_priv->drm.mode_config.connection_mutex));
@@ -748,12 +755,12 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
        if (ret != 0)
                return ret;
 
-       ret = i915_gem_object_pin_to_display_plane(new_bo, 0,
+       vma = i915_gem_object_pin_to_display_plane(new_bo, 0,
                                                   &i915_ggtt_view_normal);
-       if (ret != 0)
-               return ret;
+       if (IS_ERR(vma))
+               return PTR_ERR(vma);
 
-       ret = i915_gem_object_put_fence(new_bo);
+       ret = i915_vma_put_fence(vma);
        if (ret)
                goto out_unpin;
 
@@ -794,7 +801,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
        swidth = params->src_w;
        swidthsw = calc_swidthsw(dev_priv, params->offset_Y, tmp_width);
        sheight = params->src_h;
-       iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_Y, &regs->OBUF_0Y);
+       iowrite32(i915_ggtt_offset(vma) + params->offset_Y, &regs->OBUF_0Y);
        ostride = params->stride_Y;
 
        if (params->format & I915_OVERLAY_YUV_PLANAR) {
@@ -808,8 +815,10 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
                                      params->src_w/uv_hscale);
                swidthsw |= max_t(u32, tmp_U, tmp_V) << 16;
                sheight |= (params->src_h/uv_vscale) << 16;
-               iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_U, &regs->OBUF_0U);
-               iowrite32(i915_gem_obj_ggtt_offset(new_bo) + params->offset_V, &regs->OBUF_0V);
+               iowrite32(i915_ggtt_offset(vma) + params->offset_U,
+                         &regs->OBUF_0U);
+               iowrite32(i915_ggtt_offset(vma) + params->offset_V,
+                         &regs->OBUF_0V);
                ostride |= params->stride_UV << 16;
        }
 
@@ -830,19 +839,18 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
        if (ret)
                goto out_unpin;
 
-       i915_gem_track_fb(overlay->vid_bo, new_bo,
+       i915_gem_track_fb(overlay->vma->obj, new_bo,
                          INTEL_FRONTBUFFER_OVERLAY(pipe));
 
-       overlay->old_vid_bo = overlay->vid_bo;
-       overlay->vid_bo = new_bo;
+       overlay->old_vma = overlay->vma;
+       overlay->vma = vma;
 
-       intel_frontbuffer_flip(&dev_priv->drm,
-                              INTEL_FRONTBUFFER_OVERLAY(pipe));
+       intel_frontbuffer_flip(dev_priv, INTEL_FRONTBUFFER_OVERLAY(pipe));
 
        return 0;
 
 out_unpin:
-       i915_gem_object_ggtt_unpin(new_bo);
+       i915_gem_object_unpin_from_display_plane(vma);
        return ret;
 }
 
@@ -870,12 +878,7 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
        iowrite32(0, &regs->OCMD);
        intel_overlay_unmap_regs(overlay, regs);
 
-       ret = intel_overlay_off(overlay);
-       if (ret != 0)
-               return ret;
-
-       intel_overlay_off_tail(overlay);
-       return 0;
+       return intel_overlay_off(overlay);
 }
 
 static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
@@ -1122,9 +1125,8 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data,
        }
        crtc = to_intel_crtc(drmmode_crtc);
 
-       new_bo = to_intel_bo(drm_gem_object_lookup(file_priv,
-                                                  put_image_rec->bo_handle));
-       if (&new_bo->base == NULL) {
+       new_bo = i915_gem_object_lookup(file_priv, put_image_rec->bo_handle);
+       if (!new_bo) {
                ret = -ENOENT;
                goto out_free;
        }
@@ -1132,7 +1134,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data,
        drm_modeset_lock_all(dev);
        mutex_lock(&dev->struct_mutex);
 
-       if (new_bo->tiling_mode) {
+       if (i915_gem_object_is_tiled(new_bo)) {
                DRM_DEBUG_KMS("buffer used for overlay image can not be tiled\n");
                ret = -EINVAL;
                goto out_unlock;
@@ -1220,7 +1222,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data,
 out_unlock:
        mutex_unlock(&dev->struct_mutex);
        drm_modeset_unlock_all(dev);
-       drm_gem_object_unreference_unlocked(&new_bo->base);
+       i915_gem_object_put_unlocked(new_bo);
 out_free:
        kfree(params);
 
@@ -1371,6 +1373,7 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv)
        struct intel_overlay *overlay;
        struct drm_i915_gem_object *reg_bo;
        struct overlay_registers __iomem *regs;
+       struct i915_vma *vma = NULL;
        int ret;
 
        if (!HAS_OVERLAY(dev_priv))
@@ -1404,12 +1407,14 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv)
                }
                overlay->flip_addr = reg_bo->phys_handle->busaddr;
        } else {
-               ret = i915_gem_obj_ggtt_pin(reg_bo, PAGE_SIZE, PIN_MAPPABLE);
-               if (ret) {
+               vma = i915_gem_object_ggtt_pin(reg_bo, NULL,
+                                              0, PAGE_SIZE, PIN_MAPPABLE);
+               if (IS_ERR(vma)) {
                        DRM_ERROR("failed to pin overlay register bo\n");
+                       ret = PTR_ERR(vma);
                        goto out_free_bo;
                }
-               overlay->flip_addr = i915_gem_obj_ggtt_offset(reg_bo);
+               overlay->flip_addr = i915_ggtt_offset(vma);
 
                ret = i915_gem_object_set_to_gtt_domain(reg_bo, true);
                if (ret) {
@@ -1441,10 +1446,10 @@ void intel_setup_overlay(struct drm_i915_private *dev_priv)
        return;
 
 out_unpin_bo:
-       if (!OVERLAY_NEEDS_PHYSICAL(dev_priv))
-               i915_gem_object_ggtt_unpin(reg_bo);
+       if (vma)
+               i915_vma_unpin(vma);
 out_free_bo:
-       drm_gem_object_unreference(&reg_bo->base);
+       i915_gem_object_put(reg_bo);
 out_free:
        mutex_unlock(&dev_priv->drm.struct_mutex);
        kfree(overlay);
@@ -1461,7 +1466,7 @@ void intel_cleanup_overlay(struct drm_i915_private *dev_priv)
         * hardware should be off already */
        WARN_ON(dev_priv->overlay->active);
 
-       drm_gem_object_unreference_unlocked(&dev_priv->overlay->reg_bo->base);
+       i915_gem_object_put_unlocked(dev_priv->overlay->reg_bo);
        kfree(dev_priv->overlay);
 }
 
@@ -1484,7 +1489,7 @@ intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
                regs = (struct overlay_registers __iomem *)
                        overlay->reg_bo->phys_handle->vaddr;
        else
-               regs = io_mapping_map_atomic_wc(dev_priv->ggtt.mappable,
+               regs = io_mapping_map_atomic_wc(&dev_priv->ggtt.mappable,
                                                overlay->flip_addr);
 
        return regs;
index 96c65d7..be4b4d5 100644 (file)
@@ -841,7 +841,7 @@ static void lpt_enable_backlight(struct intel_connector *connector)
 {
        struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
        struct intel_panel *panel = &connector->panel;
-       u32 pch_ctl1, pch_ctl2;
+       u32 pch_ctl1, pch_ctl2, schicken;
 
        pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
        if (pch_ctl1 & BLM_PCH_PWM_ENABLE) {
@@ -850,6 +850,22 @@ static void lpt_enable_backlight(struct intel_connector *connector)
                I915_WRITE(BLC_PWM_PCH_CTL1, pch_ctl1);
        }
 
+       if (HAS_PCH_LPT(dev_priv)) {
+               schicken = I915_READ(SOUTH_CHICKEN2);
+               if (panel->backlight.alternate_pwm_increment)
+                       schicken |= LPT_PWM_GRANULARITY;
+               else
+                       schicken &= ~LPT_PWM_GRANULARITY;
+               I915_WRITE(SOUTH_CHICKEN2, schicken);
+       } else {
+               schicken = I915_READ(SOUTH_CHICKEN1);
+               if (panel->backlight.alternate_pwm_increment)
+                       schicken |= SPT_PWM_GRANULARITY;
+               else
+                       schicken &= ~SPT_PWM_GRANULARITY;
+               I915_WRITE(SOUTH_CHICKEN1, schicken);
+       }
+
        pch_ctl2 = panel->backlight.max << 16;
        I915_WRITE(BLC_PWM_PCH_CTL2, pch_ctl2);
 
@@ -1242,10 +1258,10 @@ static u32 bxt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
  */
 static u32 spt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
 {
-       struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       struct intel_panel *panel = &connector->panel;
        u32 mul;
 
-       if (I915_READ(SOUTH_CHICKEN1) & SPT_PWM_GRANULARITY)
+       if (panel->backlight.alternate_pwm_increment)
                mul = 128;
        else
                mul = 16;
@@ -1261,9 +1277,10 @@ static u32 spt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
 static u32 lpt_hz_to_pwm(struct intel_connector *connector, u32 pwm_freq_hz)
 {
        struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+       struct intel_panel *panel = &connector->panel;
        u32 mul, clock;
 
-       if (I915_READ(SOUTH_CHICKEN2) & LPT_PWM_GRANULARITY)
+       if (panel->backlight.alternate_pwm_increment)
                mul = 16;
        else
                mul = 128;
@@ -1414,6 +1431,13 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
        struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
        struct intel_panel *panel = &connector->panel;
        u32 pch_ctl1, pch_ctl2, val;
+       bool alt;
+
+       if (HAS_PCH_LPT(dev_priv))
+               alt = I915_READ(SOUTH_CHICKEN2) & LPT_PWM_GRANULARITY;
+       else
+               alt = I915_READ(SOUTH_CHICKEN1) & SPT_PWM_GRANULARITY;
+       panel->backlight.alternate_pwm_increment = alt;
 
        pch_ctl1 = I915_READ(BLC_PWM_PCH_CTL1);
        panel->backlight.active_low_pwm = pch_ctl1 & BLM_PCH_POLARITY;
@@ -1430,10 +1454,11 @@ static int lpt_setup_backlight(struct intel_connector *connector, enum pipe unus
        panel->backlight.min = get_backlight_min_vbt(connector);
 
        val = lpt_get_backlight(connector);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
-       panel->backlight.enabled = (pch_ctl1 & BLM_PCH_PWM_ENABLE) &&
-               panel->backlight.level != 0;
+       panel->backlight.enabled = pch_ctl1 & BLM_PCH_PWM_ENABLE;
 
        return 0;
 }
@@ -1459,11 +1484,13 @@ static int pch_setup_backlight(struct intel_connector *connector, enum pipe unus
        panel->backlight.min = get_backlight_min_vbt(connector);
 
        val = pch_get_backlight(connector);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
        cpu_ctl2 = I915_READ(BLC_PWM_CPU_CTL2);
        panel->backlight.enabled = (cpu_ctl2 & BLM_PWM_ENABLE) &&
-               (pch_ctl1 & BLM_PCH_PWM_ENABLE) && panel->backlight.level != 0;
+               (pch_ctl1 & BLM_PCH_PWM_ENABLE);
 
        return 0;
 }
@@ -1498,9 +1525,11 @@ static int i9xx_setup_backlight(struct intel_connector *connector, enum pipe unu
        panel->backlight.min = get_backlight_min_vbt(connector);
 
        val = i9xx_get_backlight(connector);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
-       panel->backlight.enabled = panel->backlight.level != 0;
+       panel->backlight.enabled = val != 0;
 
        return 0;
 }
@@ -1530,10 +1559,11 @@ static int i965_setup_backlight(struct intel_connector *connector, enum pipe unu
        panel->backlight.min = get_backlight_min_vbt(connector);
 
        val = i9xx_get_backlight(connector);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
-       panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) &&
-               panel->backlight.level != 0;
+       panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE;
 
        return 0;
 }
@@ -1562,10 +1592,11 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe
        panel->backlight.min = get_backlight_min_vbt(connector);
 
        val = _vlv_get_backlight(dev_priv, pipe);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
-       panel->backlight.enabled = (ctl2 & BLM_PWM_ENABLE) &&
-               panel->backlight.level != 0;
+       panel->backlight.enabled = ctl2 & BLM_PWM_ENABLE;
 
        return 0;
 }
@@ -1607,10 +1638,11 @@ bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
                return -ENODEV;
 
        val = bxt_get_backlight(connector);
-       panel->backlight.level = intel_panel_compute_brightness(connector, val);
+       val = intel_panel_compute_brightness(connector, val);
+       panel->backlight.level = clamp(val, panel->backlight.min,
+                                      panel->backlight.max);
 
-       panel->backlight.enabled = (pwm_ctl & BXT_BLC_PWM_ENABLE) &&
-               panel->backlight.level != 0;
+       panel->backlight.enabled = pwm_ctl & BXT_BLC_PWM_ENABLE;
 
        return 0;
 }
index 2d24813..a2f751c 100644 (file)
@@ -340,6 +340,11 @@ void intel_set_memory_cxsr(struct drm_i915_private *dev_priv, bool enable)
                I915_WRITE(FW_BLC_SELF, val);
                POSTING_READ(FW_BLC_SELF);
        } else if (IS_I915GM(dev)) {
+               /*
+                * FIXME can't find a bit like this for 915G, and
+                * and yet it does have the related watermark in
+                * FW_BLC_SELF. What's going on?
+                */
                val = enable ? _MASKED_BIT_ENABLE(INSTPM_SELF_EN) :
                               _MASKED_BIT_DISABLE(INSTPM_SELF_EN);
                I915_WRITE(INSTPM, val);
@@ -960,7 +965,7 @@ static uint16_t vlv_compute_wm_level(struct intel_plane *plane,
        if (dev_priv->wm.pri_latency[level] == 0)
                return USHRT_MAX;
 
-       if (!state->visible)
+       if (!state->base.visible)
                return 0;
 
        cpp = drm_format_plane_cpp(state->base.fb->pixel_format, 0);
@@ -1002,7 +1007,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc)
                if (plane->base.type == DRM_PLANE_TYPE_CURSOR)
                        continue;
 
-               if (state->visible) {
+               if (state->base.visible) {
                        wm_state->num_active_planes++;
                        total_rate += drm_format_plane_cpp(state->base.fb->pixel_format, 0);
                }
@@ -1018,7 +1023,7 @@ static void vlv_compute_fifo(struct intel_crtc *crtc)
                        continue;
                }
 
-               if (!state->visible) {
+               if (!state->base.visible) {
                        plane->wm.fifo_size = 0;
                        continue;
                }
@@ -1118,7 +1123,7 @@ static void vlv_compute_wm(struct intel_crtc *crtc)
                struct intel_plane_state *state =
                        to_intel_plane_state(plane->base.state);
 
-               if (!state->visible)
+               if (!state->base.visible)
                        continue;
 
                /* normal watermarks */
@@ -1580,7 +1585,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
                obj = intel_fb_obj(enabled->primary->state->fb);
 
                /* self-refresh seems busted with untiled */
-               if (obj->tiling_mode == I915_TILING_NONE)
+               if (!i915_gem_object_is_tiled(obj))
                        enabled = NULL;
        }
 
@@ -1604,6 +1609,9 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
                unsigned long line_time_us;
                int entries;
 
+               if (IS_I915GM(dev) || IS_I945GM(dev))
+                       cpp = 4;
+
                line_time_us = max(htotal * 1000 / clock, 1);
 
                /* Use ns/us then divide to preserve precision */
@@ -1618,7 +1626,7 @@ static void i9xx_update_wm(struct drm_crtc *unused_crtc)
                if (IS_I945G(dev) || IS_I945GM(dev))
                        I915_WRITE(FW_BLC_SELF,
                                   FW_BLC_SELF_FIFO_MASK | (srwm & 0xff));
-               else if (IS_I915GM(dev))
+               else
                        I915_WRITE(FW_BLC_SELF, srwm & 0x3f);
        }
 
@@ -1767,7 +1775,7 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate,
                drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0;
        uint32_t method1, method2;
 
-       if (!cstate->base.active || !pstate->visible)
+       if (!cstate->base.active || !pstate->base.visible)
                return 0;
 
        method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value);
@@ -1777,7 +1785,7 @@ static uint32_t ilk_compute_pri_wm(const struct intel_crtc_state *cstate,
 
        method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate),
                                 cstate->base.adjusted_mode.crtc_htotal,
-                                drm_rect_width(&pstate->dst),
+                                drm_rect_width(&pstate->base.dst),
                                 cpp, mem_value);
 
        return min(method1, method2);
@@ -1795,13 +1803,13 @@ static uint32_t ilk_compute_spr_wm(const struct intel_crtc_state *cstate,
                drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0;
        uint32_t method1, method2;
 
-       if (!cstate->base.active || !pstate->visible)
+       if (!cstate->base.active || !pstate->base.visible)
                return 0;
 
        method1 = ilk_wm_method1(ilk_pipe_pixel_rate(cstate), cpp, mem_value);
        method2 = ilk_wm_method2(ilk_pipe_pixel_rate(cstate),
                                 cstate->base.adjusted_mode.crtc_htotal,
-                                drm_rect_width(&pstate->dst),
+                                drm_rect_width(&pstate->base.dst),
                                 cpp, mem_value);
        return min(method1, method2);
 }
@@ -1820,7 +1828,7 @@ static uint32_t ilk_compute_cur_wm(const struct intel_crtc_state *cstate,
         * this is necessary to avoid flickering.
         */
        int cpp = 4;
-       int width = pstate->visible ? pstate->base.crtc_w : 64;
+       int width = pstate->base.visible ? pstate->base.crtc_w : 64;
 
        if (!cstate->base.active)
                return 0;
@@ -1838,10 +1846,10 @@ static uint32_t ilk_compute_fbc_wm(const struct intel_crtc_state *cstate,
        int cpp = pstate->base.fb ?
                drm_format_plane_cpp(pstate->base.fb->pixel_format, 0) : 0;
 
-       if (!cstate->base.active || !pstate->visible)
+       if (!cstate->base.active || !pstate->base.visible)
                return 0;
 
-       return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->dst), cpp);
+       return ilk_wm_fbc(pri_val, drm_rect_width(&pstate->base.dst), cpp);
 }
 
 static unsigned int ilk_display_fifo_size(const struct drm_device *dev)
@@ -2118,33 +2126,35 @@ static void intel_read_wm_latency(struct drm_device *dev, uint16_t wm[8])
                wm[7] = (val >> GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT) &
                                GEN9_MEM_LATENCY_LEVEL_MASK;
 
+               /*
+                * If a level n (n > 1) has a 0us latency, all levels m (m >= n)
+                * need to be disabled. We make sure to sanitize the values out
+                * of the punit to satisfy this requirement.
+                */
+               for (level = 1; level <= max_level; level++) {
+                       if (wm[level] == 0) {
+                               for (i = level + 1; i <= max_level; i++)
+                                       wm[i] = 0;
+                               break;
+                       }
+               }
+
                /*
                 * WaWmMemoryReadLatency:skl
                 *
                 * punit doesn't take into account the read latency so we need
-                * to add 2us to the various latency levels we retrieve from
-                * the punit.
-                *   - W0 is a bit special in that it's the only level that
-                *   can't be disabled if we want to have display working, so
-                *   we always add 2us there.
-                *   - For levels >=1, punit returns 0us latency when they are
-                *   disabled, so we respect that and don't add 2us then
-                *
-                * Additionally, if a level n (n > 1) has a 0us latency, all
-                * levels m (m >= n) need to be disabled. We make sure to
-                * sanitize the values out of the punit to satisfy this
-                * requirement.
+                * to add 2us to the various latency levels we retrieve from the
+                * punit when level 0 response data us 0us.
                 */
-               wm[0] += 2;
-               for (level = 1; level <= max_level; level++)
-                       if (wm[level] != 0)
+               if (wm[0] == 0) {
+                       wm[0] += 2;
+                       for (level = 1; level <= max_level; level++) {
+                               if (wm[level] == 0)
+                                       break;
                                wm[level] += 2;
-                       else {
-                               for (i = level + 1; i <= max_level; i++)
-                                       wm[i] = 0;
-
-                               break;
                        }
+               }
+
        } else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
                uint64_t sskpd = I915_READ64(MCH_SSKPD);
 
@@ -2358,10 +2368,10 @@ static int ilk_compute_pipe_wm(struct intel_crtc_state *cstate)
 
        pipe_wm->pipe_enabled = cstate->base.active;
        if (sprstate) {
-               pipe_wm->sprites_enabled = sprstate->visible;
-               pipe_wm->sprites_scaled = sprstate->visible &&
-                       (drm_rect_width(&sprstate->dst) != drm_rect_width(&sprstate->src) >> 16 ||
-                        drm_rect_height(&sprstate->dst) != drm_rect_height(&sprstate->src) >> 16);
+               pipe_wm->sprites_enabled = sprstate->base.visible;
+               pipe_wm->sprites_scaled = sprstate->base.visible &&
+                       (drm_rect_width(&sprstate->base.dst) != drm_rect_width(&sprstate->base.src) >> 16 ||
+                        drm_rect_height(&sprstate->base.dst) != drm_rect_height(&sprstate->base.src) >> 16);
        }
 
        usable_level = max_level;
@@ -2845,13 +2855,6 @@ bool ilk_disable_lp_wm(struct drm_device *dev)
        return _ilk_disable_lp_wm(dev_priv, WM_DIRTY_LP_ALL);
 }
 
-/*
- * On gen9, we need to allocate Display Data Buffer (DDB) portions to the
- * different active planes.
- */
-
-#define SKL_DDB_SIZE           896     /* in blocks */
-#define BXT_DDB_SIZE           512
 #define SKL_SAGV_BLOCK_TIME    30 /* µs */
 
 /*
@@ -2876,6 +2879,19 @@ skl_wm_plane_id(const struct intel_plane *plane)
        }
 }
 
+static bool
+intel_has_sagv(struct drm_i915_private *dev_priv)
+{
+       if (IS_KABYLAKE(dev_priv))
+               return true;
+
+       if (IS_SKYLAKE(dev_priv) &&
+           dev_priv->sagv_status != I915_SAGV_NOT_CONTROLLED)
+               return true;
+
+       return false;
+}
+
 /*
  * SAGV dynamically adjusts the system agent voltage and clock frequencies
  * depending on power and performance requirements. The display engine access
@@ -2888,12 +2904,14 @@ skl_wm_plane_id(const struct intel_plane *plane)
  *  - We're not using an interlaced display configuration
  */
 int
-skl_enable_sagv(struct drm_i915_private *dev_priv)
+intel_enable_sagv(struct drm_i915_private *dev_priv)
 {
        int ret;
 
-       if (dev_priv->skl_sagv_status == I915_SKL_SAGV_NOT_CONTROLLED ||
-           dev_priv->skl_sagv_status == I915_SKL_SAGV_ENABLED)
+       if (!intel_has_sagv(dev_priv))
+               return 0;
+
+       if (dev_priv->sagv_status == I915_SAGV_ENABLED)
                return 0;
 
        DRM_DEBUG_KMS("Enabling the SAGV\n");
@@ -2909,21 +2927,21 @@ skl_enable_sagv(struct drm_i915_private *dev_priv)
         * Some skl systems, pre-release machines in particular,
         * don't actually have an SAGV.
         */
-       if (ret == -ENXIO) {
+       if (IS_SKYLAKE(dev_priv) && ret == -ENXIO) {
                DRM_DEBUG_DRIVER("No SAGV found on system, ignoring\n");
-               dev_priv->skl_sagv_status = I915_SKL_SAGV_NOT_CONTROLLED;
+               dev_priv->sagv_status = I915_SAGV_NOT_CONTROLLED;
                return 0;
        } else if (ret < 0) {
                DRM_ERROR("Failed to enable the SAGV\n");
                return ret;
        }
 
-       dev_priv->skl_sagv_status = I915_SKL_SAGV_ENABLED;
+       dev_priv->sagv_status = I915_SAGV_ENABLED;
        return 0;
 }
 
 static int
-skl_do_sagv_disable(struct drm_i915_private *dev_priv)
+intel_do_sagv_disable(struct drm_i915_private *dev_priv)
 {
        int ret;
        uint32_t temp = GEN9_SAGV_DISABLE;
@@ -2937,19 +2955,21 @@ skl_do_sagv_disable(struct drm_i915_private *dev_priv)
 }
 
 int
-skl_disable_sagv(struct drm_i915_private *dev_priv)
+intel_disable_sagv(struct drm_i915_private *dev_priv)
 {
        int ret, result;
 
-       if (dev_priv->skl_sagv_status == I915_SKL_SAGV_NOT_CONTROLLED ||
-           dev_priv->skl_sagv_status == I915_SKL_SAGV_DISABLED)
+       if (!intel_has_sagv(dev_priv))
+               return 0;
+
+       if (dev_priv->sagv_status == I915_SAGV_DISABLED)
                return 0;
 
        DRM_DEBUG_KMS("Disabling the SAGV\n");
        mutex_lock(&dev_priv->rps.hw_lock);
 
        /* bspec says to keep retrying for at least 1 ms */
-       ret = wait_for(result = skl_do_sagv_disable(dev_priv), 1);
+       ret = wait_for(result = intel_do_sagv_disable(dev_priv), 1);
        mutex_unlock(&dev_priv->rps.hw_lock);
 
        if (ret == -ETIMEDOUT) {
@@ -2961,20 +2981,20 @@ skl_disable_sagv(struct drm_i915_private *dev_priv)
         * Some skl systems, pre-release machines in particular,
         * don't actually have an SAGV.
         */
-       if (result == -ENXIO) {
+       if (IS_SKYLAKE(dev_priv) && result == -ENXIO) {
                DRM_DEBUG_DRIVER("No SAGV found on system, ignoring\n");
-               dev_priv->skl_sagv_status = I915_SKL_SAGV_NOT_CONTROLLED;
+               dev_priv->sagv_status = I915_SAGV_NOT_CONTROLLED;
                return 0;
        } else if (result < 0) {
                DRM_ERROR("Failed to disable the SAGV\n");
                return result;
        }
 
-       dev_priv->skl_sagv_status = I915_SKL_SAGV_DISABLED;
+       dev_priv->sagv_status = I915_SAGV_DISABLED;
        return 0;
 }
 
-bool skl_can_enable_sagv(struct drm_atomic_state *state)
+bool intel_can_enable_sagv(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -2983,6 +3003,9 @@ bool skl_can_enable_sagv(struct drm_atomic_state *state)
        enum pipe pipe;
        int level, plane;
 
+       if (!intel_has_sagv(dev_priv))
+               return false;
+
        /*
         * SKL workaround: bspec recommends we disable the SAGV when we have
         * more then one pipe enabled
@@ -3049,10 +3072,8 @@ skl_ddb_get_pipe_allocation_limits(struct drm_device *dev,
        else
                *num_active = hweight32(dev_priv->active_crtcs);
 
-       if (IS_BROXTON(dev))
-               ddb_size = BXT_DDB_SIZE;
-       else
-               ddb_size = SKL_DDB_SIZE;
+       ddb_size = INTEL_INFO(dev_priv)->ddb_size;
+       WARN_ON(ddb_size == 0);
 
        ddb_size -= 4; /* 4 blocks for bypass path allocation */
 
@@ -3144,14 +3165,14 @@ skl_plane_downscale_amount(const struct intel_plane_state *pstate)
        uint32_t downscale_h, downscale_w;
        uint32_t src_w, src_h, dst_w, dst_h;
 
-       if (WARN_ON(!pstate->visible))
+       if (WARN_ON(!pstate->base.visible))
                return DRM_PLANE_HELPER_NO_SCALING;
 
        /* n.b., src is 16.16 fixed point, dst is whole integer */
-       src_w = drm_rect_width(&pstate->src);
-       src_h = drm_rect_height(&pstate->src);
-       dst_w = drm_rect_width(&pstate->dst);
-       dst_h = drm_rect_height(&pstate->dst);
+       src_w = drm_rect_width(&pstate->base.src);
+       src_h = drm_rect_height(&pstate->base.src);
+       dst_w = drm_rect_width(&pstate->base.dst);
+       dst_h = drm_rect_height(&pstate->base.dst);
        if (intel_rotation_90_or_270(pstate->base.rotation))
                swap(dst_w, dst_h);
 
@@ -3173,15 +3194,15 @@ skl_plane_relative_data_rate(const struct intel_crtc_state *cstate,
        uint32_t width = 0, height = 0;
        unsigned format = fb ? fb->pixel_format : DRM_FORMAT_XRGB8888;
 
-       if (!intel_pstate->visible)
+       if (!intel_pstate->base.visible)
                return 0;
        if (pstate->plane->type == DRM_PLANE_TYPE_CURSOR)
                return 0;
        if (y && format != DRM_FORMAT_NV12)
                return 0;
 
-       width = drm_rect_width(&intel_pstate->src) >> 16;
-       height = drm_rect_height(&intel_pstate->src) >> 16;
+       width = drm_rect_width(&intel_pstate->base.src) >> 16;
+       height = drm_rect_height(&intel_pstate->base.src) >> 16;
 
        if (intel_rotation_90_or_270(pstate->rotation))
                swap(width, height);
@@ -3280,8 +3301,8 @@ skl_ddb_min_alloc(const struct drm_plane_state *pstate,
            fb->modifier[0] != I915_FORMAT_MOD_Yf_TILED)
                return 8;
 
-       src_w = drm_rect_width(&intel_pstate->src) >> 16;
-       src_h = drm_rect_height(&intel_pstate->src) >> 16;
+       src_w = drm_rect_width(&intel_pstate->base.src) >> 16;
+       src_h = drm_rect_height(&intel_pstate->base.src) >> 16;
 
        if (intel_rotation_90_or_270(pstate->rotation))
                swap(src_w, src_h);
@@ -3372,7 +3393,7 @@ skl_allocate_pipe_ddb(struct intel_crtc_state *cstate,
                if (intel_plane->pipe != pipe)
                        continue;
 
-               if (!to_intel_plane_state(pstate)->visible) {
+               if (!to_intel_plane_state(pstate)->base.visible) {
                        minimum[id] = 0;
                        y_minimum[id] = 0;
                        continue;
@@ -3473,29 +3494,14 @@ static uint32_t skl_wm_method1(uint32_t pixel_rate, uint8_t cpp, uint32_t latenc
 }
 
 static uint32_t skl_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal,
-                              uint32_t horiz_pixels, uint8_t cpp,
-                              uint64_t tiling, uint32_t latency)
+                              uint32_t latency, uint32_t plane_blocks_per_line)
 {
        uint32_t ret;
-       uint32_t plane_bytes_per_line, plane_blocks_per_line;
        uint32_t wm_intermediate_val;
 
        if (latency == 0)
                return UINT_MAX;
 
-       plane_bytes_per_line = horiz_pixels * cpp;
-
-       if (tiling == I915_FORMAT_MOD_Y_TILED ||
-           tiling == I915_FORMAT_MOD_Yf_TILED) {
-               plane_bytes_per_line *= 4;
-               plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512);
-               plane_blocks_per_line /= 4;
-       } else if (tiling == DRM_FORMAT_MOD_NONE) {
-               plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512) + 1;
-       } else {
-               plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512);
-       }
-
        wm_intermediate_val = latency * pixel_rate;
        ret = DIV_ROUND_UP(wm_intermediate_val, pipe_htotal * 1000) *
                                plane_blocks_per_line;
@@ -3511,7 +3517,7 @@ static uint32_t skl_adjusted_plane_pixel_rate(const struct intel_crtc_state *cst
        uint64_t pixel_rate;
 
        /* Shouldn't reach here on disabled planes... */
-       if (WARN_ON(!pstate->visible))
+       if (WARN_ON(!pstate->base.visible))
                return 0;
 
        /*
@@ -3546,14 +3552,15 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
        uint8_t cpp;
        uint32_t width = 0, height = 0;
        uint32_t plane_pixel_rate;
+       uint32_t y_tile_minimum, y_min_scanlines;
 
-       if (latency == 0 || !cstate->base.active || !intel_pstate->visible) {
+       if (latency == 0 || !cstate->base.active || !intel_pstate->base.visible) {
                *enabled = false;
                return 0;
        }
 
-       width = drm_rect_width(&intel_pstate->src) >> 16;
-       height = drm_rect_height(&intel_pstate->src) >> 16;
+       width = drm_rect_width(&intel_pstate->base.src) >> 16;
+       height = drm_rect_height(&intel_pstate->base.src) >> 16;
 
        if (intel_rotation_90_or_270(pstate->rotation))
                swap(width, height);
@@ -3561,38 +3568,51 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
        cpp = drm_format_plane_cpp(fb->pixel_format, 0);
        plane_pixel_rate = skl_adjusted_plane_pixel_rate(cstate, intel_pstate);
 
+       if (intel_rotation_90_or_270(pstate->rotation)) {
+               int cpp = (fb->pixel_format == DRM_FORMAT_NV12) ?
+                       drm_format_plane_cpp(fb->pixel_format, 1) :
+                       drm_format_plane_cpp(fb->pixel_format, 0);
+
+               switch (cpp) {
+               case 1:
+                       y_min_scanlines = 16;
+                       break;
+               case 2:
+                       y_min_scanlines = 8;
+                       break;
+               default:
+                       WARN(1, "Unsupported pixel depth for rotation");
+               case 4:
+                       y_min_scanlines = 4;
+                       break;
+               }
+       } else {
+               y_min_scanlines = 4;
+       }
+
+       plane_bytes_per_line = width * cpp;
+       if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
+           fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) {
+               plane_blocks_per_line =
+                     DIV_ROUND_UP(plane_bytes_per_line * y_min_scanlines, 512);
+               plane_blocks_per_line /= y_min_scanlines;
+       } else if (fb->modifier[0] == DRM_FORMAT_MOD_NONE) {
+               plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512)
+                                       + 1;
+       } else {
+               plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512);
+       }
+
        method1 = skl_wm_method1(plane_pixel_rate, cpp, latency);
        method2 = skl_wm_method2(plane_pixel_rate,
                                 cstate->base.adjusted_mode.crtc_htotal,
-                                width,
-                                cpp,
-                                fb->modifier[0],
-                                latency);
+                                latency,
+                                plane_blocks_per_line);
 
-       plane_bytes_per_line = width * cpp;
-       plane_blocks_per_line = DIV_ROUND_UP(plane_bytes_per_line, 512);
+       y_tile_minimum = plane_blocks_per_line * y_min_scanlines;
 
        if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
            fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) {
-               uint32_t min_scanlines = 4;
-               uint32_t y_tile_minimum;
-               if (intel_rotation_90_or_270(pstate->rotation)) {
-                       int cpp = (fb->pixel_format == DRM_FORMAT_NV12) ?
-                               drm_format_plane_cpp(fb->pixel_format, 1) :
-                               drm_format_plane_cpp(fb->pixel_format, 0);
-
-                       switch (cpp) {
-                       case 1:
-                               min_scanlines = 16;
-                               break;
-                       case 2:
-                               min_scanlines = 8;
-                               break;
-                       case 8:
-                               WARN(1, "Unsupported pixel depth for rotation");
-                       }
-               }
-               y_tile_minimum = plane_blocks_per_line * min_scanlines;
                selected_result = max(method2, y_tile_minimum);
        } else {
                if ((ddb_allocation / plane_blocks_per_line) >= 1)
@@ -3606,10 +3626,12 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv,
 
        if (level >= 1 && level <= 7) {
                if (fb->modifier[0] == I915_FORMAT_MOD_Y_TILED ||
-                   fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED)
-                       res_lines += 4;
-               else
+                   fb->modifier[0] == I915_FORMAT_MOD_Yf_TILED) {
+                       res_blocks += y_tile_minimum;
+                       res_lines += y_min_scanlines;
+               } else {
                        res_blocks++;
+               }
        }
 
        if (res_blocks >= ddb_allocation || res_lines > 31) {
@@ -3828,183 +3850,82 @@ static void skl_ddb_entry_write(struct drm_i915_private *dev_priv,
                I915_WRITE(reg, 0);
 }
 
-static void skl_write_wm_values(struct drm_i915_private *dev_priv,
-                               const struct skl_wm_values *new)
+void skl_write_plane_wm(struct intel_crtc *intel_crtc,
+                       const struct skl_wm_values *wm,
+                       int plane)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct intel_crtc *crtc;
-
-       for_each_intel_crtc(dev, crtc) {
-               int i, level, max_level = ilk_wm_max_level(dev);
-               enum pipe pipe = crtc->pipe;
-
-               if ((new->dirty_pipes & drm_crtc_mask(&crtc->base)) == 0)
-                       continue;
-               if (!crtc->active)
-                       continue;
-
-               I915_WRITE(PIPE_WM_LINETIME(pipe), new->wm_linetime[pipe]);
-
-               for (level = 0; level <= max_level; level++) {
-                       for (i = 0; i < intel_num_planes(crtc); i++)
-                               I915_WRITE(PLANE_WM(pipe, i, level),
-                                          new->plane[pipe][i][level]);
-                       I915_WRITE(CUR_WM(pipe, level),
-                                  new->plane[pipe][PLANE_CURSOR][level]);
-               }
-               for (i = 0; i < intel_num_planes(crtc); i++)
-                       I915_WRITE(PLANE_WM_TRANS(pipe, i),
-                                  new->plane_trans[pipe][i]);
-               I915_WRITE(CUR_WM_TRANS(pipe),
-                          new->plane_trans[pipe][PLANE_CURSOR]);
-
-               for (i = 0; i < intel_num_planes(crtc); i++) {
-                       skl_ddb_entry_write(dev_priv,
-                                           PLANE_BUF_CFG(pipe, i),
-                                           &new->ddb.plane[pipe][i]);
-                       skl_ddb_entry_write(dev_priv,
-                                           PLANE_NV12_BUF_CFG(pipe, i),
-                                           &new->ddb.y_plane[pipe][i]);
-               }
+       struct drm_crtc *crtc = &intel_crtc->base;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       int level, max_level = ilk_wm_max_level(dev);
+       enum pipe pipe = intel_crtc->pipe;
 
-               skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe),
-                                   &new->ddb.plane[pipe][PLANE_CURSOR]);
+       for (level = 0; level <= max_level; level++) {
+               I915_WRITE(PLANE_WM(pipe, plane, level),
+                          wm->plane[pipe][plane][level]);
        }
-}
+       I915_WRITE(PLANE_WM_TRANS(pipe, plane), wm->plane_trans[pipe][plane]);
 
-/*
- * When setting up a new DDB allocation arrangement, we need to correctly
- * sequence the times at which the new allocations for the pipes are taken into
- * account or we'll have pipes fetching from space previously allocated to
- * another pipe.
- *
- * Roughly the sequence looks like:
- *  1. re-allocate the pipe(s) with the allocation being reduced and not
- *     overlapping with a previous light-up pipe (another way to put it is:
- *     pipes with their new allocation strickly included into their old ones).
- *  2. re-allocate the other pipes that get their allocation reduced
- *  3. allocate the pipes having their allocation increased
- *
- * Steps 1. and 2. are here to take care of the following case:
- * - Initially DDB looks like this:
- *     |   B    |   C    |
- * - enable pipe A.
- * - pipe B has a reduced DDB allocation that overlaps with the old pipe C
- *   allocation
- *     |  A  |  B  |  C  |
- *
- * We need to sequence the re-allocation: C, B, A (and not B, C, A).
- */
+       skl_ddb_entry_write(dev_priv, PLANE_BUF_CFG(pipe, plane),
+                           &wm->ddb.plane[pipe][plane]);
+       skl_ddb_entry_write(dev_priv, PLANE_NV12_BUF_CFG(pipe, plane),
+                           &wm->ddb.y_plane[pipe][plane]);
+}
 
-static void
-skl_wm_flush_pipe(struct drm_i915_private *dev_priv, enum pipe pipe, int pass)
+void skl_write_cursor_wm(struct intel_crtc *intel_crtc,
+                        const struct skl_wm_values *wm)
 {
-       int plane;
-
-       DRM_DEBUG_KMS("flush pipe %c (pass %d)\n", pipe_name(pipe), pass);
+       struct drm_crtc *crtc = &intel_crtc->base;
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       int level, max_level = ilk_wm_max_level(dev);
+       enum pipe pipe = intel_crtc->pipe;
 
-       for_each_plane(dev_priv, pipe, plane) {
-               I915_WRITE(PLANE_SURF(pipe, plane),
-                          I915_READ(PLANE_SURF(pipe, plane)));
+       for (level = 0; level <= max_level; level++) {
+               I915_WRITE(CUR_WM(pipe, level),
+                          wm->plane[pipe][PLANE_CURSOR][level]);
        }
-       I915_WRITE(CURBASE(pipe), I915_READ(CURBASE(pipe)));
+       I915_WRITE(CUR_WM_TRANS(pipe), wm->plane_trans[pipe][PLANE_CURSOR]);
+
+       skl_ddb_entry_write(dev_priv, CUR_BUF_CFG(pipe),
+                           &wm->ddb.plane[pipe][PLANE_CURSOR]);
 }
 
-static bool
-skl_ddb_allocation_included(const struct skl_ddb_allocation *old,
-                           const struct skl_ddb_allocation *new,
-                           enum pipe pipe)
+bool skl_ddb_allocation_equals(const struct skl_ddb_allocation *old,
+                              const struct skl_ddb_allocation *new,
+                              enum pipe pipe)
 {
-       uint16_t old_size, new_size;
-
-       old_size = skl_ddb_entry_size(&old->pipe[pipe]);
-       new_size = skl_ddb_entry_size(&new->pipe[pipe]);
-
-       return old_size != new_size &&
-              new->pipe[pipe].start >= old->pipe[pipe].start &&
-              new->pipe[pipe].end <= old->pipe[pipe].end;
+       return new->pipe[pipe].start == old->pipe[pipe].start &&
+              new->pipe[pipe].end == old->pipe[pipe].end;
 }
 
-static void skl_flush_wm_values(struct drm_i915_private *dev_priv,
-                               struct skl_wm_values *new_values)
+static inline bool skl_ddb_entries_overlap(const struct skl_ddb_entry *a,
+                                          const struct skl_ddb_entry *b)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct skl_ddb_allocation *cur_ddb, *new_ddb;
-       bool reallocated[I915_MAX_PIPES] = {};
-       struct intel_crtc *crtc;
-       enum pipe pipe;
-
-       new_ddb = &new_values->ddb;
-       cur_ddb = &dev_priv->wm.skl_hw.ddb;
-
-       /*
-        * First pass: flush the pipes with the new allocation contained into
-        * the old space.
-        *
-        * We'll wait for the vblank on those pipes to ensure we can safely
-        * re-allocate the freed space without this pipe fetching from it.
-        */
-       for_each_intel_crtc(dev, crtc) {
-               if (!crtc->active)
-                       continue;
-
-               pipe = crtc->pipe;
-
-               if (!skl_ddb_allocation_included(cur_ddb, new_ddb, pipe))
-                       continue;
-
-               skl_wm_flush_pipe(dev_priv, pipe, 1);
-               intel_wait_for_vblank(dev, pipe);
-
-               reallocated[pipe] = true;
-       }
-
+       return a->start < b->end && b->start < a->end;
+}
 
-       /*
-        * Second pass: flush the pipes that are having their allocation
-        * reduced, but overlapping with a previous allocation.
-        *
-        * Here as well we need to wait for the vblank to make sure the freed
-        * space is not used anymore.
-        */
-       for_each_intel_crtc(dev, crtc) {
-               if (!crtc->active)
-                       continue;
+bool skl_ddb_allocation_overlaps(struct drm_atomic_state *state,
+                                const struct skl_ddb_allocation *old,
+                                const struct skl_ddb_allocation *new,
+                                enum pipe pipe)
+{
+       struct drm_device *dev = state->dev;
+       struct intel_crtc *intel_crtc;
+       enum pipe otherp;
 
-               pipe = crtc->pipe;
+       for_each_intel_crtc(dev, intel_crtc) {
+               otherp = intel_crtc->pipe;
 
-               if (reallocated[pipe])
+               if (otherp == pipe)
                        continue;
 
-               if (skl_ddb_entry_size(&new_ddb->pipe[pipe]) <
-                   skl_ddb_entry_size(&cur_ddb->pipe[pipe])) {
-                       skl_wm_flush_pipe(dev_priv, pipe, 2);
-                       intel_wait_for_vblank(dev, pipe);
-                       reallocated[pipe] = true;
-               }
+               if (skl_ddb_entries_overlap(&new->pipe[pipe],
+                                           &old->pipe[otherp]))
+                       return true;
        }
 
-       /*
-        * Third pass: flush the pipes that got more space allocated.
-        *
-        * We don't need to actively wait for the update here, next vblank
-        * will just get more DDB space with the correct WM values.
-        */
-       for_each_intel_crtc(dev, crtc) {
-               if (!crtc->active)
-                       continue;
-
-               pipe = crtc->pipe;
-
-               /*
-                * At this point, only the pipes more space than before are
-                * left to re-allocate.
-                */
-               if (reallocated[pipe])
-                       continue;
-
-               skl_wm_flush_pipe(dev_priv, pipe, 3);
-       }
+       return false;
 }
 
 static int skl_update_pipe_wm(struct drm_crtc_state *cstate,
@@ -4041,6 +3962,41 @@ pipes_modified(struct drm_atomic_state *state)
        return ret;
 }
 
+int
+skl_ddb_add_affected_planes(struct intel_crtc_state *cstate)
+{
+       struct drm_atomic_state *state = cstate->base.state;
+       struct drm_device *dev = state->dev;
+       struct drm_crtc *crtc = cstate->base.crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_i915_private *dev_priv = to_i915(dev);
+       struct intel_atomic_state *intel_state = to_intel_atomic_state(state);
+       struct skl_ddb_allocation *new_ddb = &intel_state->wm_results.ddb;
+       struct skl_ddb_allocation *cur_ddb = &dev_priv->wm.skl_hw.ddb;
+       struct drm_plane_state *plane_state;
+       struct drm_plane *plane;
+       enum pipe pipe = intel_crtc->pipe;
+       int id;
+
+       WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc));
+
+       drm_for_each_plane_mask(plane, dev, crtc->state->plane_mask) {
+               id = skl_wm_plane_id(to_intel_plane(plane));
+
+               if (skl_ddb_entry_equal(&cur_ddb->plane[pipe][id],
+                                       &new_ddb->plane[pipe][id]) &&
+                   skl_ddb_entry_equal(&cur_ddb->y_plane[pipe][id],
+                                       &new_ddb->y_plane[pipe][id]))
+                       continue;
+
+               plane_state = drm_atomic_get_plane_state(state, plane);
+               if (IS_ERR(plane_state))
+                       return PTR_ERR(plane_state);
+       }
+
+       return 0;
+}
+
 static int
 skl_compute_ddb(struct drm_atomic_state *state)
 {
@@ -4105,7 +4061,7 @@ skl_compute_ddb(struct drm_atomic_state *state)
                if (ret)
                        return ret;
 
-               ret = drm_atomic_add_affected_planes(state, &intel_crtc->base);
+               ret = skl_ddb_add_affected_planes(cstate);
                if (ret)
                        return ret;
        }
@@ -4206,7 +4162,7 @@ static void skl_update_wm(struct drm_crtc *crtc)
        struct skl_wm_values *hw_vals = &dev_priv->wm.skl_hw;
        struct intel_crtc_state *cstate = to_intel_crtc_state(crtc->state);
        struct skl_pipe_wm *pipe_wm = &cstate->wm.skl.optimal;
-       int pipe;
+       enum pipe pipe = intel_crtc->pipe;
 
        if ((results->dirty_pipes & drm_crtc_mask(crtc)) == 0)
                return;
@@ -4215,15 +4171,22 @@ static void skl_update_wm(struct drm_crtc *crtc)
 
        mutex_lock(&dev_priv->wm.wm_mutex);
 
-       skl_write_wm_values(dev_priv, results);
-       skl_flush_wm_values(dev_priv, results);
-
        /*
-        * Store the new configuration (but only for the pipes that have
-        * changed; the other values weren't recomputed).
+        * If this pipe isn't active already, we're going to be enabling it
+        * very soon. Since it's safe to update a pipe's ddb allocation while
+        * the pipe's shut off, just do so here. Already active pipes will have
+        * their watermarks updated once we update their planes.
         */
-       for_each_pipe_masked(dev_priv, pipe, results->dirty_pipes)
-               skl_copy_wm_for_pipe(hw_vals, results, pipe);
+       if (crtc->state->active_changed) {
+               int plane;
+
+               for (plane = 0; plane < intel_num_planes(intel_crtc); plane++)
+                       skl_write_plane_wm(intel_crtc, results, plane);
+
+               skl_write_cursor_wm(intel_crtc, results);
+       }
+
+       skl_copy_wm_for_pipe(hw_vals, results, pipe);
 
        mutex_unlock(&dev_priv->wm.wm_mutex);
 }
@@ -5103,7 +5066,7 @@ void gen6_rps_boost(struct drm_i915_private *dev_priv,
         */
        if (!(dev_priv->gt.awake &&
              dev_priv->rps.enabled &&
-             dev_priv->rps.cur_freq < dev_priv->rps.max_freq_softlimit))
+             dev_priv->rps.cur_freq < dev_priv->rps.boost_freq))
                return;
 
        /* Force a RPS boost (and don't count it against the client) if
@@ -5294,35 +5257,31 @@ int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enable_rc6)
 
 static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv)
 {
-       uint32_t rp_state_cap;
-       u32 ddcc_status = 0;
-       int ret;
-
        /* All of these values are in units of 50MHz */
-       dev_priv->rps.cur_freq          = 0;
+
        /* static values from HW: RP0 > RP1 > RPn (min_freq) */
        if (IS_BROXTON(dev_priv)) {
-               rp_state_cap = I915_READ(BXT_RP_STATE_CAP);
+               u32 rp_state_cap = I915_READ(BXT_RP_STATE_CAP);
                dev_priv->rps.rp0_freq = (rp_state_cap >> 16) & 0xff;
                dev_priv->rps.rp1_freq = (rp_state_cap >>  8) & 0xff;
                dev_priv->rps.min_freq = (rp_state_cap >>  0) & 0xff;
        } else {
-               rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+               u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
                dev_priv->rps.rp0_freq = (rp_state_cap >>  0) & 0xff;
                dev_priv->rps.rp1_freq = (rp_state_cap >>  8) & 0xff;
                dev_priv->rps.min_freq = (rp_state_cap >> 16) & 0xff;
        }
-
        /* hw_max = RP0 until we check for overclocking */
-       dev_priv->rps.max_freq          = dev_priv->rps.rp0_freq;
+       dev_priv->rps.max_freq = dev_priv->rps.rp0_freq;
 
        dev_priv->rps.efficient_freq = dev_priv->rps.rp1_freq;
        if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv) ||
            IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
-               ret = sandybridge_pcode_read(dev_priv,
-                                       HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL,
-                                       &ddcc_status);
-               if (0 == ret)
+               u32 ddcc_status = 0;
+
+               if (sandybridge_pcode_read(dev_priv,
+                                          HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL,
+                                          &ddcc_status) == 0)
                        dev_priv->rps.efficient_freq =
                                clamp_t(u8,
                                        ((ddcc_status >> 8) & 0xff),
@@ -5332,29 +5291,26 @@ static void gen6_init_rps_frequencies(struct drm_i915_private *dev_priv)
 
        if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
                /* Store the frequency values in 16.66 MHZ units, which is
-                  the natural hardware unit for SKL */
+                * the natural hardware unit for SKL
+                */
                dev_priv->rps.rp0_freq *= GEN9_FREQ_SCALER;
                dev_priv->rps.rp1_freq *= GEN9_FREQ_SCALER;
                dev_priv->rps.min_freq *= GEN9_FREQ_SCALER;
                dev_priv->rps.max_freq *= GEN9_FREQ_SCALER;
                dev_priv->rps.efficient_freq *= GEN9_FREQ_SCALER;
        }
+}
 
-       dev_priv->rps.idle_freq = dev_priv->rps.min_freq;
+static void reset_rps(struct drm_i915_private *dev_priv,
+                     void (*set)(struct drm_i915_private *, u8))
+{
+       u8 freq = dev_priv->rps.cur_freq;
 
-       /* Preserve min/max settings in case of re-init */
-       if (dev_priv->rps.max_freq_softlimit == 0)
-               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+       /* force a reset */
+       dev_priv->rps.power = -1;
+       dev_priv->rps.cur_freq = -1;
 
-       if (dev_priv->rps.min_freq_softlimit == 0) {
-               if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
-                       dev_priv->rps.min_freq_softlimit =
-                               max_t(int, dev_priv->rps.efficient_freq,
-                                     intel_freq_opcode(dev_priv, 450));
-               else
-                       dev_priv->rps.min_freq_softlimit =
-                               dev_priv->rps.min_freq;
-       }
+       set(dev_priv, freq);
 }
 
 /* See the Gen9_GT_PM_Programming_Guide doc for the below */
@@ -5362,8 +5318,6 @@ static void gen9_enable_rps(struct drm_i915_private *dev_priv)
 {
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
-       gen6_init_rps_frequencies(dev_priv);
-
        /* WaGsvDisableTurbo: Workaround to disable turbo on BXT A* */
        if (IS_BXT_REVID(dev_priv, 0, BXT_REVID_A1)) {
                /*
@@ -5393,8 +5347,7 @@ static void gen9_enable_rps(struct drm_i915_private *dev_priv)
        /* Leaning on the below call to gen6_set_rps to program/setup the
         * Up/Down EI & threshold registers, as well as the RP_CONTROL,
         * RP_INTERRUPT_LIMITS & RPNSWREQ registers */
-       dev_priv->rps.power = HIGH_POWER; /* force a reset */
-       gen6_set_rps(dev_priv, dev_priv->rps.idle_freq);
+       reset_rps(dev_priv, gen6_set_rps);
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
@@ -5481,9 +5434,6 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv)
        /* 2a: Disable RC states. */
        I915_WRITE(GEN6_RC_CONTROL, 0);
 
-       /* Initialize rps frequencies */
-       gen6_init_rps_frequencies(dev_priv);
-
        /* 2b: Program RC6 thresholds.*/
        I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16);
        I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); /* 12500 * 1280ns */
@@ -5540,8 +5490,7 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv)
 
        /* 6: Ring frequency + overclocking (our driver does this later */
 
-       dev_priv->rps.power = HIGH_POWER; /* force a reset */
-       gen6_set_rps(dev_priv, dev_priv->rps.idle_freq);
+       reset_rps(dev_priv, gen6_set_rps);
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
@@ -5549,7 +5498,7 @@ static void gen8_enable_rps(struct drm_i915_private *dev_priv)
 static void gen6_enable_rps(struct drm_i915_private *dev_priv)
 {
        struct intel_engine_cs *engine;
-       u32 rc6vids, pcu_mbox = 0, rc6_mask = 0;
+       u32 rc6vids, rc6_mask = 0;
        u32 gtfifodbg;
        int rc6_mode;
        int ret;
@@ -5573,9 +5522,6 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv)
 
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
-       /* Initialize rps frequencies */
-       gen6_init_rps_frequencies(dev_priv);
-
        /* disable the counters and set deterministic thresholds */
        I915_WRITE(GEN6_RC_CONTROL, 0);
 
@@ -5626,16 +5572,7 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv)
        if (ret)
                DRM_DEBUG_DRIVER("Failed to set the min frequency\n");
 
-       ret = sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &pcu_mbox);
-       if (!ret && (pcu_mbox & (1<<31))) { /* OC supported */
-               DRM_DEBUG_DRIVER("Overclocking supported. Max: %dMHz, Overclock max: %dMHz\n",
-                                (dev_priv->rps.max_freq_softlimit & 0xff) * 50,
-                                (pcu_mbox & 0xff) * 50);
-               dev_priv->rps.max_freq = pcu_mbox & 0xff;
-       }
-
-       dev_priv->rps.power = HIGH_POWER; /* force a reset */
-       gen6_set_rps(dev_priv, dev_priv->rps.idle_freq);
+       reset_rps(dev_priv, gen6_set_rps);
 
        rc6vids = 0;
        ret = sandybridge_pcode_read(dev_priv, GEN6_PCODE_READ_RC6VIDS, &rc6vids);
@@ -5654,7 +5591,7 @@ static void gen6_enable_rps(struct drm_i915_private *dev_priv)
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
 
-static void __gen6_update_ring_freq(struct drm_i915_private *dev_priv)
+static void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
 {
        int min_freq = 15;
        unsigned int gpu_freq;
@@ -5738,23 +5675,13 @@ static void __gen6_update_ring_freq(struct drm_i915_private *dev_priv)
        }
 }
 
-void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
-{
-       if (!HAS_CORE_RING_FREQ(dev_priv))
-               return;
-
-       mutex_lock(&dev_priv->rps.hw_lock);
-       __gen6_update_ring_freq(dev_priv);
-       mutex_unlock(&dev_priv->rps.hw_lock);
-}
-
 static int cherryview_rps_max_freq(struct drm_i915_private *dev_priv)
 {
        u32 val, rp0;
 
        val = vlv_punit_read(dev_priv, FB_GFX_FMAX_AT_VMAX_FUSE);
 
-       switch (INTEL_INFO(dev_priv)->eu_total) {
+       switch (INTEL_INFO(dev_priv)->sseu.eu_total) {
        case 8:
                /* (2 * 4) config */
                rp0 = (val >> FB_GFX_FMAX_AT_VMAX_2SS4EU_FUSE_SHIFT);
@@ -5892,8 +5819,6 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv)
        u32 pcbr;
        int pctx_size = 24*1024;
 
-       mutex_lock(&dev_priv->drm.struct_mutex);
-
        pcbr = I915_READ(VLV_PCBR);
        if (pcbr) {
                /* BIOS set it up already, grab the pre-alloc'd space */
@@ -5929,7 +5854,6 @@ static void valleyview_setup_pctx(struct drm_i915_private *dev_priv)
 out:
        DRM_DEBUG_DRIVER("PCBR: 0x%08x\n", I915_READ(VLV_PCBR));
        dev_priv->vlv_pctx = pctx;
-       mutex_unlock(&dev_priv->drm.struct_mutex);
 }
 
 static void valleyview_cleanup_pctx(struct drm_i915_private *dev_priv)
@@ -5937,7 +5861,7 @@ static void valleyview_cleanup_pctx(struct drm_i915_private *dev_priv)
        if (WARN_ON(!dev_priv->vlv_pctx))
                return;
 
-       drm_gem_object_unreference_unlocked(&dev_priv->vlv_pctx->base);
+       i915_gem_object_put_unlocked(dev_priv->vlv_pctx);
        dev_priv->vlv_pctx = NULL;
 }
 
@@ -5960,8 +5884,6 @@ static void valleyview_init_gt_powersave(struct drm_i915_private *dev_priv)
 
        vlv_init_gpll_ref_freq(dev_priv);
 
-       mutex_lock(&dev_priv->rps.hw_lock);
-
        val = vlv_punit_read(dev_priv, PUNIT_REG_GPU_FREQ_STS);
        switch ((val >> 6) & 3) {
        case 0:
@@ -5997,17 +5919,6 @@ static void valleyview_init_gt_powersave(struct drm_i915_private *dev_priv)
        DRM_DEBUG_DRIVER("min GPU freq: %d MHz (%u)\n",
                         intel_gpu_freq(dev_priv, dev_priv->rps.min_freq),
                         dev_priv->rps.min_freq);
-
-       dev_priv->rps.idle_freq = dev_priv->rps.min_freq;
-
-       /* Preserve min/max settings in case of re-init */
-       if (dev_priv->rps.max_freq_softlimit == 0)
-               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
-
-       if (dev_priv->rps.min_freq_softlimit == 0)
-               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
-
-       mutex_unlock(&dev_priv->rps.hw_lock);
 }
 
 static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv)
@@ -6018,8 +5929,6 @@ static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv)
 
        vlv_init_gpll_ref_freq(dev_priv);
 
-       mutex_lock(&dev_priv->rps.hw_lock);
-
        mutex_lock(&dev_priv->sb_lock);
        val = vlv_cck_read(dev_priv, CCK_FUSE_REG);
        mutex_unlock(&dev_priv->sb_lock);
@@ -6061,17 +5970,6 @@ static void cherryview_init_gt_powersave(struct drm_i915_private *dev_priv)
                   dev_priv->rps.rp1_freq |
                   dev_priv->rps.min_freq) & 1,
                  "Odd GPU freq values\n");
-
-       dev_priv->rps.idle_freq = dev_priv->rps.min_freq;
-
-       /* Preserve min/max settings in case of re-init */
-       if (dev_priv->rps.max_freq_softlimit == 0)
-               dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
-
-       if (dev_priv->rps.min_freq_softlimit == 0)
-               dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
-
-       mutex_unlock(&dev_priv->rps.hw_lock);
 }
 
 static void valleyview_cleanup_gt_powersave(struct drm_i915_private *dev_priv)
@@ -6162,16 +6060,7 @@ static void cherryview_enable_rps(struct drm_i915_private *dev_priv)
        DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE));
        DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val);
 
-       dev_priv->rps.cur_freq = (val >> 8) & 0xff;
-       DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n",
-                        intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
-                        dev_priv->rps.cur_freq);
-
-       DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
-                        intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq),
-                        dev_priv->rps.idle_freq);
-
-       valleyview_set_rps(dev_priv, dev_priv->rps.idle_freq);
+       reset_rps(dev_priv, valleyview_set_rps);
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
@@ -6251,16 +6140,7 @@ static void valleyview_enable_rps(struct drm_i915_private *dev_priv)
        DRM_DEBUG_DRIVER("GPLL enabled? %s\n", yesno(val & GPLLENABLE));
        DRM_DEBUG_DRIVER("GPU status: 0x%08x\n", val);
 
-       dev_priv->rps.cur_freq = (val >> 8) & 0xff;
-       DRM_DEBUG_DRIVER("current GPU freq: %d MHz (%u)\n",
-                        intel_gpu_freq(dev_priv, dev_priv->rps.cur_freq),
-                        dev_priv->rps.cur_freq);
-
-       DRM_DEBUG_DRIVER("setting GPU freq to %d MHz (%u)\n",
-                        intel_gpu_freq(dev_priv, dev_priv->rps.idle_freq),
-                        dev_priv->rps.idle_freq);
-
-       valleyview_set_rps(dev_priv, dev_priv->rps.idle_freq);
+       reset_rps(dev_priv, valleyview_set_rps);
 
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
@@ -6589,19 +6469,11 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
  */
 bool i915_gpu_busy(void)
 {
-       struct drm_i915_private *dev_priv;
-       struct intel_engine_cs *engine;
        bool ret = false;
 
        spin_lock_irq(&mchdev_lock);
-       if (!i915_mch_dev)
-               goto out_unlock;
-       dev_priv = i915_mch_dev;
-
-       for_each_engine(engine, dev_priv)
-               ret |= !list_empty(&engine->request_list);
-
-out_unlock:
+       if (i915_mch_dev)
+               ret = i915_mch_dev->gt.awake;
        spin_unlock_irq(&mchdev_lock);
 
        return ret;
@@ -6757,10 +6629,51 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
                intel_runtime_pm_get(dev_priv);
        }
 
+       mutex_lock(&dev_priv->drm.struct_mutex);
+       mutex_lock(&dev_priv->rps.hw_lock);
+
+       /* Initialize RPS limits (for userspace) */
        if (IS_CHERRYVIEW(dev_priv))
                cherryview_init_gt_powersave(dev_priv);
        else if (IS_VALLEYVIEW(dev_priv))
                valleyview_init_gt_powersave(dev_priv);
+       else if (INTEL_GEN(dev_priv) >= 6)
+               gen6_init_rps_frequencies(dev_priv);
+
+       /* Derive initial user preferences/limits from the hardware limits */
+       dev_priv->rps.idle_freq = dev_priv->rps.min_freq;
+       dev_priv->rps.cur_freq = dev_priv->rps.idle_freq;
+
+       dev_priv->rps.max_freq_softlimit = dev_priv->rps.max_freq;
+       dev_priv->rps.min_freq_softlimit = dev_priv->rps.min_freq;
+
+       if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
+               dev_priv->rps.min_freq_softlimit =
+                       max_t(int,
+                             dev_priv->rps.efficient_freq,
+                             intel_freq_opcode(dev_priv, 450));
+
+       /* After setting max-softlimit, find the overclock max freq */
+       if (IS_GEN6(dev_priv) ||
+           IS_IVYBRIDGE(dev_priv) || IS_HASWELL(dev_priv)) {
+               u32 params = 0;
+
+               sandybridge_pcode_read(dev_priv, GEN6_READ_OC_PARAMS, &params);
+               if (params & BIT(31)) { /* OC supported */
+                       DRM_DEBUG_DRIVER("Overclocking supported, max: %dMHz, overclock: %dMHz\n",
+                                        (dev_priv->rps.max_freq & 0xff) * 50,
+                                        (params & 0xff) * 50);
+                       dev_priv->rps.max_freq = params & 0xff;
+               }
+       }
+
+       /* Finally allow us to boost to max by default */
+       dev_priv->rps.boost_freq = dev_priv->rps.max_freq;
+
+       mutex_unlock(&dev_priv->rps.hw_lock);
+       mutex_unlock(&dev_priv->drm.struct_mutex);
+
+       intel_autoenable_gt_powersave(dev_priv);
 }
 
 void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv)
@@ -6772,13 +6685,6 @@ void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv)
                intel_runtime_pm_put(dev_priv);
 }
 
-static void gen6_suspend_rps(struct drm_i915_private *dev_priv)
-{
-       flush_delayed_work(&dev_priv->rps.delayed_resume_work);
-
-       gen6_disable_rps_interrupts(dev_priv);
-}
-
 /**
  * intel_suspend_gt_powersave - suspend PM work and helper threads
  * @dev_priv: i915 device
@@ -6792,60 +6698,76 @@ void intel_suspend_gt_powersave(struct drm_i915_private *dev_priv)
        if (INTEL_GEN(dev_priv) < 6)
                return;
 
-       gen6_suspend_rps(dev_priv);
+       if (cancel_delayed_work_sync(&dev_priv->rps.autoenable_work))
+               intel_runtime_pm_put(dev_priv);
+
+       /* gen6_rps_idle() will be called later to disable interrupts */
+}
+
+void intel_sanitize_gt_powersave(struct drm_i915_private *dev_priv)
+{
+       dev_priv->rps.enabled = true; /* force disabling */
+       intel_disable_gt_powersave(dev_priv);
 
-       /* Force GPU to min freq during suspend */
-       gen6_rps_idle(dev_priv);
+       gen6_reset_rps_interrupts(dev_priv);
 }
 
 void intel_disable_gt_powersave(struct drm_i915_private *dev_priv)
 {
-       if (IS_IRONLAKE_M(dev_priv)) {
-               ironlake_disable_drps(dev_priv);
-       } else if (INTEL_INFO(dev_priv)->gen >= 6) {
-               intel_suspend_gt_powersave(dev_priv);
+       if (!READ_ONCE(dev_priv->rps.enabled))
+               return;
 
-               mutex_lock(&dev_priv->rps.hw_lock);
-               if (INTEL_INFO(dev_priv)->gen >= 9) {
-                       gen9_disable_rc6(dev_priv);
-                       gen9_disable_rps(dev_priv);
-               } else if (IS_CHERRYVIEW(dev_priv))
-                       cherryview_disable_rps(dev_priv);
-               else if (IS_VALLEYVIEW(dev_priv))
-                       valleyview_disable_rps(dev_priv);
-               else
-                       gen6_disable_rps(dev_priv);
+       mutex_lock(&dev_priv->rps.hw_lock);
 
-               dev_priv->rps.enabled = false;
-               mutex_unlock(&dev_priv->rps.hw_lock);
+       if (INTEL_GEN(dev_priv) >= 9) {
+               gen9_disable_rc6(dev_priv);
+               gen9_disable_rps(dev_priv);
+       } else if (IS_CHERRYVIEW(dev_priv)) {
+               cherryview_disable_rps(dev_priv);
+       } else if (IS_VALLEYVIEW(dev_priv)) {
+               valleyview_disable_rps(dev_priv);
+       } else if (INTEL_GEN(dev_priv) >= 6) {
+               gen6_disable_rps(dev_priv);
+       }  else if (IS_IRONLAKE_M(dev_priv)) {
+               ironlake_disable_drps(dev_priv);
        }
+
+       dev_priv->rps.enabled = false;
+       mutex_unlock(&dev_priv->rps.hw_lock);
 }
 
-static void intel_gen6_powersave_work(struct work_struct *work)
+void intel_enable_gt_powersave(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv =
-               container_of(work, struct drm_i915_private,
-                            rps.delayed_resume_work.work);
+       /* We shouldn't be disabling as we submit, so this should be less
+        * racy than it appears!
+        */
+       if (READ_ONCE(dev_priv->rps.enabled))
+               return;
 
-       mutex_lock(&dev_priv->rps.hw_lock);
+       /* Powersaving is controlled by the host when inside a VM */
+       if (intel_vgpu_active(dev_priv))
+               return;
 
-       gen6_reset_rps_interrupts(dev_priv);
+       mutex_lock(&dev_priv->rps.hw_lock);
 
        if (IS_CHERRYVIEW(dev_priv)) {
                cherryview_enable_rps(dev_priv);
        } else if (IS_VALLEYVIEW(dev_priv)) {
                valleyview_enable_rps(dev_priv);
-       } else if (INTEL_INFO(dev_priv)->gen >= 9) {
+       } else if (INTEL_GEN(dev_priv) >= 9) {
                gen9_enable_rc6(dev_priv);
                gen9_enable_rps(dev_priv);
                if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
-                       __gen6_update_ring_freq(dev_priv);
+                       gen6_update_ring_freq(dev_priv);
        } else if (IS_BROADWELL(dev_priv)) {
                gen8_enable_rps(dev_priv);
-               __gen6_update_ring_freq(dev_priv);
-       } else {
+               gen6_update_ring_freq(dev_priv);
+       } else if (INTEL_GEN(dev_priv) >= 6) {
                gen6_enable_rps(dev_priv);
-               __gen6_update_ring_freq(dev_priv);
+               gen6_update_ring_freq(dev_priv);
+       } else if (IS_IRONLAKE_M(dev_priv)) {
+               ironlake_enable_drps(dev_priv);
+               intel_init_emon(dev_priv);
        }
 
        WARN_ON(dev_priv->rps.max_freq < dev_priv->rps.min_freq);
@@ -6855,25 +6777,52 @@ static void intel_gen6_powersave_work(struct work_struct *work)
        WARN_ON(dev_priv->rps.efficient_freq > dev_priv->rps.max_freq);
 
        dev_priv->rps.enabled = true;
+       mutex_unlock(&dev_priv->rps.hw_lock);
+}
+
+static void __intel_autoenable_gt_powersave(struct work_struct *work)
+{
+       struct drm_i915_private *dev_priv =
+               container_of(work, typeof(*dev_priv), rps.autoenable_work.work);
+       struct intel_engine_cs *rcs;
+       struct drm_i915_gem_request *req;
 
-       gen6_enable_rps_interrupts(dev_priv);
+       if (READ_ONCE(dev_priv->rps.enabled))
+               goto out;
 
-       mutex_unlock(&dev_priv->rps.hw_lock);
+       rcs = &dev_priv->engine[RCS];
+       if (rcs->last_context)
+               goto out;
 
+       if (!rcs->init_context)
+               goto out;
+
+       mutex_lock(&dev_priv->drm.struct_mutex);
+
+       req = i915_gem_request_alloc(rcs, dev_priv->kernel_context);
+       if (IS_ERR(req))
+               goto unlock;
+
+       if (!i915.enable_execlists && i915_switch_context(req) == 0)
+               rcs->init_context(req);
+
+       /* Mark the device busy, calling intel_enable_gt_powersave() */
+       i915_add_request_no_flush(req);
+
+unlock:
+       mutex_unlock(&dev_priv->drm.struct_mutex);
+out:
        intel_runtime_pm_put(dev_priv);
 }
 
-void intel_enable_gt_powersave(struct drm_i915_private *dev_priv)
+void intel_autoenable_gt_powersave(struct drm_i915_private *dev_priv)
 {
-       /* Powersaving is controlled by the host when inside a VM */
-       if (intel_vgpu_active(dev_priv))
+       if (READ_ONCE(dev_priv->rps.enabled))
                return;
 
        if (IS_IRONLAKE_M(dev_priv)) {
                ironlake_enable_drps(dev_priv);
-               mutex_lock(&dev_priv->drm.struct_mutex);
                intel_init_emon(dev_priv);
-               mutex_unlock(&dev_priv->drm.struct_mutex);
        } else if (INTEL_INFO(dev_priv)->gen >= 6) {
                /*
                 * PCU communication is slow and this doesn't need to be
@@ -6887,21 +6836,13 @@ void intel_enable_gt_powersave(struct drm_i915_private *dev_priv)
                 * paths, so the _noresume version is enough (and in case of
                 * runtime resume it's necessary).
                 */
-               if (schedule_delayed_work(&dev_priv->rps.delayed_resume_work,
-                                          round_jiffies_up_relative(HZ)))
+               if (queue_delayed_work(dev_priv->wq,
+                                      &dev_priv->rps.autoenable_work,
+                                      round_jiffies_up_relative(HZ)))
                        intel_runtime_pm_get_noresume(dev_priv);
        }
 }
 
-void intel_reset_gt_powersave(struct drm_i915_private *dev_priv)
-{
-       if (INTEL_INFO(dev_priv)->gen < 6)
-               return;
-
-       gen6_suspend_rps(dev_priv);
-       dev_priv->rps.enabled = false;
-}
-
 static void ibx_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -8046,7 +7987,7 @@ static void __intel_rps_boost_work(struct work_struct *work)
        if (!i915_gem_request_completed(req))
                gen6_rps_boost(req->i915, NULL, req->emitted_jiffies);
 
-       i915_gem_request_unreference(req);
+       i915_gem_request_put(req);
        kfree(boost);
 }
 
@@ -8064,8 +8005,7 @@ void intel_queue_rps_boost_for_request(struct drm_i915_gem_request *req)
        if (boost == NULL)
                return;
 
-       i915_gem_request_reference(req);
-       boost->req = req;
+       boost->req = i915_gem_request_get(req);
 
        INIT_WORK(&boost->work, __intel_rps_boost_work);
        queue_work(req->i915->wq, &boost->work);
@@ -8078,11 +8018,9 @@ void intel_pm_setup(struct drm_device *dev)
        mutex_init(&dev_priv->rps.hw_lock);
        spin_lock_init(&dev_priv->rps.client_lock);
 
-       INIT_DELAYED_WORK(&dev_priv->rps.delayed_resume_work,
-                         intel_gen6_powersave_work);
+       INIT_DELAYED_WORK(&dev_priv->rps.autoenable_work,
+                         __intel_autoenable_gt_powersave);
        INIT_LIST_HEAD(&dev_priv->rps.clients);
-       INIT_LIST_HEAD(&dev_priv->rps.semaphores.link);
-       INIT_LIST_HEAD(&dev_priv->rps.mmioflips.link);
 
        dev_priv->pm.suspended = false;
        atomic_set(&dev_priv->pm.wakeref_count, 0);
index cf171b4..108ba1e 100644 (file)
@@ -645,9 +645,8 @@ unlock:
        mutex_unlock(&dev_priv->psr.lock);
 }
 
-static void intel_psr_exit(struct drm_device *dev)
+static void intel_psr_exit(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_dp *intel_dp = dev_priv->psr.enabled;
        struct drm_crtc *crtc = dp_to_dig_port(intel_dp)->base.base.crtc;
        enum pipe pipe = to_intel_crtc(crtc)->pipe;
@@ -656,7 +655,7 @@ static void intel_psr_exit(struct drm_device *dev)
        if (!dev_priv->psr.active)
                return;
 
-       if (HAS_DDI(dev)) {
+       if (HAS_DDI(dev_priv)) {
                val = I915_READ(EDP_PSR_CTL);
 
                WARN_ON(!(val & EDP_PSR_ENABLE));
@@ -691,7 +690,7 @@ static void intel_psr_exit(struct drm_device *dev)
 
 /**
  * intel_psr_single_frame_update - Single Frame Update
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * Some platforms support a single frame update feature that is used to
@@ -699,10 +698,9 @@ static void intel_psr_exit(struct drm_device *dev)
  * So far it is only implemented for Valleyview and Cherryview because
  * hardware requires this to be done before a page flip.
  */
-void intel_psr_single_frame_update(struct drm_device *dev,
+void intel_psr_single_frame_update(struct drm_i915_private *dev_priv,
                                   unsigned frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *crtc;
        enum pipe pipe;
        u32 val;
@@ -711,7 +709,7 @@ void intel_psr_single_frame_update(struct drm_device *dev,
         * Single frame update is already supported on BDW+ but it requires
         * many W/A and it isn't really needed.
         */
-       if (!IS_VALLEYVIEW(dev) && !IS_CHERRYVIEW(dev))
+       if (!IS_VALLEYVIEW(dev_priv) && !IS_CHERRYVIEW(dev_priv))
                return;
 
        mutex_lock(&dev_priv->psr.lock);
@@ -737,7 +735,7 @@ void intel_psr_single_frame_update(struct drm_device *dev,
 
 /**
  * intel_psr_invalidate - Invalidade PSR
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  *
  * Since the hardware frontbuffer tracking has gaps we need to integrate
@@ -747,10 +745,9 @@ void intel_psr_single_frame_update(struct drm_device *dev,
  *
  * Dirty frontbuffers relevant to PSR are tracked in busy_frontbuffer_bits."
  */
-void intel_psr_invalidate(struct drm_device *dev,
+void intel_psr_invalidate(struct drm_i915_private *dev_priv,
                          unsigned frontbuffer_bits)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *crtc;
        enum pipe pipe;
 
@@ -767,14 +764,14 @@ void intel_psr_invalidate(struct drm_device *dev,
        dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits;
 
        if (frontbuffer_bits)
-               intel_psr_exit(dev);
+               intel_psr_exit(dev_priv);
 
        mutex_unlock(&dev_priv->psr.lock);
 }
 
 /**
  * intel_psr_flush - Flush PSR
- * @dev: DRM device
+ * @dev_priv: i915 device
  * @frontbuffer_bits: frontbuffer plane tracking bits
  * @origin: which operation caused the flush
  *
@@ -785,10 +782,9 @@ void intel_psr_invalidate(struct drm_device *dev,
  *
  * Dirty frontbuffers relevant to PSR are tracked in busy_frontbuffer_bits.
  */
-void intel_psr_flush(struct drm_device *dev,
+void intel_psr_flush(struct drm_i915_private *dev_priv,
                     unsigned frontbuffer_bits, enum fb_op_origin origin)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
        struct drm_crtc *crtc;
        enum pipe pipe;
 
@@ -806,7 +802,7 @@ void intel_psr_flush(struct drm_device *dev,
 
        /* By definition flush = invalidate + flush */
        if (frontbuffer_bits)
-               intel_psr_exit(dev);
+               intel_psr_exit(dev_priv);
 
        if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
                if (!work_busy(&dev_priv->psr.work.work))
index 5bd6985..08f6fea 100644 (file)
 #ifndef _INTEL_RENDERSTATE_H
 #define _INTEL_RENDERSTATE_H
 
-#include "i915_drv.h"
+#include <linux/types.h>
 
-extern const struct intel_renderstate_rodata gen6_null_state;
-extern const struct intel_renderstate_rodata gen7_null_state;
-extern const struct intel_renderstate_rodata gen8_null_state;
-extern const struct intel_renderstate_rodata gen9_null_state;
+struct intel_renderstate_rodata {
+       const u32 *reloc;
+       const u32 *batch;
+       const u32 batch_items;
+};
 
 #define RO_RENDERSTATE(_g)                                             \
        const struct intel_renderstate_rodata gen ## _g ## _null_state = { \
@@ -38,4 +39,9 @@ extern const struct intel_renderstate_rodata gen9_null_state;
                .batch_items = sizeof(gen ## _g ## _null_state_batch)/4, \
        }
 
+extern const struct intel_renderstate_rodata gen6_null_state;
+extern const struct intel_renderstate_rodata gen7_null_state;
+extern const struct intel_renderstate_rodata gen8_null_state;
+extern const struct intel_renderstate_rodata gen9_null_state;
+
 #endif /* INTEL_RENDERSTATE_H */
index 1d3161b..ed9955d 100644 (file)
@@ -47,57 +47,44 @@ int __intel_ring_space(int head, int tail, int size)
        return space - I915_RING_FREE_SPACE;
 }
 
-void intel_ring_update_space(struct intel_ringbuffer *ringbuf)
+void intel_ring_update_space(struct intel_ring *ring)
 {
-       if (ringbuf->last_retired_head != -1) {
-               ringbuf->head = ringbuf->last_retired_head;
-               ringbuf->last_retired_head = -1;
+       if (ring->last_retired_head != -1) {
+               ring->head = ring->last_retired_head;
+               ring->last_retired_head = -1;
        }
 
-       ringbuf->space = __intel_ring_space(ringbuf->head & HEAD_ADDR,
-                                           ringbuf->tail, ringbuf->size);
-}
-
-static void __intel_ring_advance(struct intel_engine_cs *engine)
-{
-       struct intel_ringbuffer *ringbuf = engine->buffer;
-       ringbuf->tail &= ringbuf->size - 1;
-       engine->write_tail(engine, ringbuf->tail);
+       ring->space = __intel_ring_space(ring->head & HEAD_ADDR,
+                                        ring->tail, ring->size);
 }
 
 static int
-gen2_render_ring_flush(struct drm_i915_gem_request *req,
-                      u32      invalidate_domains,
-                      u32      flush_domains)
+gen2_render_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        u32 cmd;
        int ret;
 
        cmd = MI_FLUSH;
-       if (((invalidate_domains|flush_domains) & I915_GEM_DOMAIN_RENDER) == 0)
-               cmd |= MI_NO_WRITE_FLUSH;
 
-       if (invalidate_domains & I915_GEM_DOMAIN_SAMPLER)
+       if (mode & EMIT_INVALIDATE)
                cmd |= MI_READ_FLUSH;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, cmd);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, cmd);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-gen4_render_ring_flush(struct drm_i915_gem_request *req,
-                      u32      invalidate_domains,
-                      u32      flush_domains)
+gen4_render_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        u32 cmd;
        int ret;
 
@@ -129,23 +116,20 @@ gen4_render_ring_flush(struct drm_i915_gem_request *req,
         * are flushed at any MI_FLUSH.
         */
 
-       cmd = MI_FLUSH | MI_NO_WRITE_FLUSH;
-       if ((invalidate_domains|flush_domains) & I915_GEM_DOMAIN_RENDER)
-               cmd &= ~MI_NO_WRITE_FLUSH;
-       if (invalidate_domains & I915_GEM_DOMAIN_INSTRUCTION)
+       cmd = MI_FLUSH;
+       if (mode & EMIT_INVALIDATE) {
                cmd |= MI_EXE_FLUSH;
-
-       if (invalidate_domains & I915_GEM_DOMAIN_COMMAND &&
-           (IS_G4X(req->i915) || IS_GEN5(req->i915)))
-               cmd |= MI_INVALIDATE_ISP;
+               if (IS_G4X(req->i915) || IS_GEN5(req->i915))
+                       cmd |= MI_INVALIDATE_ISP;
+       }
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, cmd);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, cmd);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -190,45 +174,46 @@ gen4_render_ring_flush(struct drm_i915_gem_request *req,
 static int
 intel_emit_post_sync_nonzero_flush(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
-       u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
+       struct intel_ring *ring = req->ring;
+       u32 scratch_addr =
+               i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES;
        int ret;
 
        ret = intel_ring_begin(req, 6);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(5));
-       intel_ring_emit(engine, PIPE_CONTROL_CS_STALL |
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+       intel_ring_emit(ring, PIPE_CONTROL_CS_STALL |
                        PIPE_CONTROL_STALL_AT_SCOREBOARD);
-       intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
-       intel_ring_emit(engine, 0); /* low dword */
-       intel_ring_emit(engine, 0); /* high dword */
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
+       intel_ring_emit(ring, 0); /* low dword */
+       intel_ring_emit(ring, 0); /* high dword */
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        ret = intel_ring_begin(req, 6);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(5));
-       intel_ring_emit(engine, PIPE_CONTROL_QW_WRITE);
-       intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+       intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE);
+       intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-gen6_render_ring_flush(struct drm_i915_gem_request *req,
-                      u32 invalidate_domains, u32 flush_domains)
+gen6_render_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
+       u32 scratch_addr =
+               i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES;
        u32 flags = 0;
-       u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        /* Force SNB workarounds for PIPE_CONTROL flushes */
@@ -240,7 +225,7 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req,
         * number of bits based on the write domains has little performance
         * impact.
         */
-       if (flush_domains) {
+       if (mode & EMIT_FLUSH) {
                flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
                /*
@@ -249,7 +234,7 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req,
                 */
                flags |= PIPE_CONTROL_CS_STALL;
        }
-       if (invalidate_domains) {
+       if (mode & EMIT_INVALIDATE) {
                flags |= PIPE_CONTROL_TLB_INVALIDATE;
                flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
                flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
@@ -266,11 +251,11 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4));
-       intel_ring_emit(engine, flags);
-       intel_ring_emit(engine, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
-       intel_ring_emit(engine, 0);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
+       intel_ring_emit(ring, flags);
+       intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -278,30 +263,31 @@ gen6_render_ring_flush(struct drm_i915_gem_request *req,
 static int
 gen7_render_ring_cs_stall_wa(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 4);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4));
-       intel_ring_emit(engine, PIPE_CONTROL_CS_STALL |
-                             PIPE_CONTROL_STALL_AT_SCOREBOARD);
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, 0);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
+       intel_ring_emit(ring,
+                       PIPE_CONTROL_CS_STALL |
+                       PIPE_CONTROL_STALL_AT_SCOREBOARD);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-gen7_render_ring_flush(struct drm_i915_gem_request *req,
-                      u32 invalidate_domains, u32 flush_domains)
+gen7_render_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
+       u32 scratch_addr =
+               i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES;
        u32 flags = 0;
-       u32 scratch_addr = engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        /*
@@ -318,13 +304,13 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req,
         * number of bits based on the write domains has little performance
         * impact.
         */
-       if (flush_domains) {
+       if (mode & EMIT_FLUSH) {
                flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DC_FLUSH_ENABLE;
                flags |= PIPE_CONTROL_FLUSH_ENABLE;
        }
-       if (invalidate_domains) {
+       if (mode & EMIT_INVALIDATE) {
                flags |= PIPE_CONTROL_TLB_INVALIDATE;
                flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
                flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
@@ -350,11 +336,11 @@ gen7_render_ring_flush(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(4));
-       intel_ring_emit(engine, flags);
-       intel_ring_emit(engine, scratch_addr);
-       intel_ring_emit(engine, 0);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
+       intel_ring_emit(ring, flags);
+       intel_ring_emit(ring, scratch_addr);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -363,41 +349,41 @@ static int
 gen8_emit_pipe_control(struct drm_i915_gem_request *req,
                       u32 flags, u32 scratch_addr)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 6);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(6));
-       intel_ring_emit(engine, flags);
-       intel_ring_emit(engine, scratch_addr);
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, 0);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+       intel_ring_emit(ring, flags);
+       intel_ring_emit(ring, scratch_addr);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, 0);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-gen8_render_ring_flush(struct drm_i915_gem_request *req,
-                      u32 invalidate_domains, u32 flush_domains)
+gen8_render_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
+       u32 scratch_addr =
+               i915_ggtt_offset(req->engine->scratch) + 2 * CACHELINE_BYTES;
        u32 flags = 0;
-       u32 scratch_addr = req->engine->scratch.gtt_offset + 2 * CACHELINE_BYTES;
        int ret;
 
        flags |= PIPE_CONTROL_CS_STALL;
 
-       if (flush_domains) {
+       if (mode & EMIT_FLUSH) {
                flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
                flags |= PIPE_CONTROL_DC_FLUSH_ENABLE;
                flags |= PIPE_CONTROL_FLUSH_ENABLE;
        }
-       if (invalidate_domains) {
+       if (mode & EMIT_INVALIDATE) {
                flags |= PIPE_CONTROL_TLB_INVALIDATE;
                flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
                flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
@@ -419,14 +405,7 @@ gen8_render_ring_flush(struct drm_i915_gem_request *req,
        return gen8_emit_pipe_control(req, flags, scratch_addr);
 }
 
-static void ring_write_tail(struct intel_engine_cs *engine,
-                           u32 value)
-{
-       struct drm_i915_private *dev_priv = engine->i915;
-       I915_WRITE_TAIL(engine, value);
-}
-
-u64 intel_ring_get_active_head(struct intel_engine_cs *engine)
+u64 intel_engine_get_active_head(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
        u64 acthd;
@@ -488,7 +467,7 @@ static void intel_ring_setup_status_page(struct intel_engine_cs *engine)
                mmio = RING_HWS_PGA(engine->mmio_base);
        }
 
-       I915_WRITE(mmio, (u32)engine->status_page.gfx_addr);
+       I915_WRITE(mmio, engine->status_page.ggtt_offset);
        POSTING_READ(mmio);
 
        /*
@@ -519,7 +498,7 @@ static bool stop_ring(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
 
-       if (!IS_GEN2(dev_priv)) {
+       if (INTEL_GEN(dev_priv) > 2) {
                I915_WRITE_MODE(engine, _MASKED_BIT_ENABLE(STOP_RING));
                if (intel_wait_for_register(dev_priv,
                                            RING_MI_MODE(engine->mmio_base),
@@ -539,9 +518,9 @@ static bool stop_ring(struct intel_engine_cs *engine)
 
        I915_WRITE_CTL(engine, 0);
        I915_WRITE_HEAD(engine, 0);
-       engine->write_tail(engine, 0);
+       I915_WRITE_TAIL(engine, 0);
 
-       if (!IS_GEN2(dev_priv)) {
+       if (INTEL_GEN(dev_priv) > 2) {
                (void)I915_READ_CTL(engine);
                I915_WRITE_MODE(engine, _MASKED_BIT_DISABLE(STOP_RING));
        }
@@ -549,16 +528,10 @@ static bool stop_ring(struct intel_engine_cs *engine)
        return (I915_READ_HEAD(engine) & HEAD_ADDR) == 0;
 }
 
-void intel_engine_init_hangcheck(struct intel_engine_cs *engine)
-{
-       memset(&engine->hangcheck, 0, sizeof(engine->hangcheck));
-}
-
 static int init_ring_common(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
-       struct intel_ringbuffer *ringbuf = engine->buffer;
-       struct drm_i915_gem_object *obj = ringbuf->obj;
+       struct intel_ring *ring = engine->buffer;
        int ret = 0;
 
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
@@ -586,10 +559,12 @@ static int init_ring_common(struct intel_engine_cs *engine)
                }
        }
 
-       if (I915_NEED_GFX_HWS(dev_priv))
-               intel_ring_setup_status_page(engine);
-       else
+       if (HWS_NEEDS_PHYSICAL(dev_priv))
                ring_setup_phys_status_page(engine);
+       else
+               intel_ring_setup_status_page(engine);
+
+       intel_engine_reset_breadcrumbs(engine);
 
        /* Enforce ordering by reading HEAD register back */
        I915_READ_HEAD(engine);
@@ -598,40 +573,39 @@ static int init_ring_common(struct intel_engine_cs *engine)
         * registers with the above sequence (the readback of the HEAD registers
         * also enforces ordering), otherwise the hw might lose the new ring
         * register values. */
-       I915_WRITE_START(engine, i915_gem_obj_ggtt_offset(obj));
+       I915_WRITE_START(engine, i915_ggtt_offset(ring->vma));
 
        /* WaClearRingBufHeadRegAtInit:ctg,elk */
        if (I915_READ_HEAD(engine))
                DRM_DEBUG("%s initialization failed [head=%08x], fudging\n",
                          engine->name, I915_READ_HEAD(engine));
-       I915_WRITE_HEAD(engine, 0);
-       (void)I915_READ_HEAD(engine);
+
+       intel_ring_update_space(ring);
+       I915_WRITE_HEAD(engine, ring->head);
+       I915_WRITE_TAIL(engine, ring->tail);
+       (void)I915_READ_TAIL(engine);
 
        I915_WRITE_CTL(engine,
-                       ((ringbuf->size - PAGE_SIZE) & RING_NR_PAGES)
+                       ((ring->size - PAGE_SIZE) & RING_NR_PAGES)
                        | RING_VALID);
 
        /* If the head is still not zero, the ring is dead */
-       if (wait_for((I915_READ_CTL(engine) & RING_VALID) != 0 &&
-                    I915_READ_START(engine) == i915_gem_obj_ggtt_offset(obj) &&
-                    (I915_READ_HEAD(engine) & HEAD_ADDR) == 0, 50)) {
+       if (intel_wait_for_register_fw(dev_priv, RING_CTL(engine->mmio_base),
+                                      RING_VALID, RING_VALID,
+                                      50)) {
                DRM_ERROR("%s initialization failed "
-                         "ctl %08x (valid? %d) head %08x tail %08x start %08x [expected %08lx]\n",
+                         "ctl %08x (valid? %d) head %08x [%08x] tail %08x [%08x] start %08x [expected %08x]\n",
                          engine->name,
                          I915_READ_CTL(engine),
                          I915_READ_CTL(engine) & RING_VALID,
-                         I915_READ_HEAD(engine), I915_READ_TAIL(engine),
+                         I915_READ_HEAD(engine), ring->head,
+                         I915_READ_TAIL(engine), ring->tail,
                          I915_READ_START(engine),
-                         (unsigned long)i915_gem_obj_ggtt_offset(obj));
+                         i915_ggtt_offset(ring->vma));
                ret = -EIO;
                goto out;
        }
 
-       ringbuf->last_retired_head = -1;
-       ringbuf->head = I915_READ_HEAD(engine);
-       ringbuf->tail = I915_READ_TAIL(engine) & TAIL_ADDR;
-       intel_ring_update_space(ringbuf);
-
        intel_engine_init_hangcheck(engine);
 
 out:
@@ -640,59 +614,25 @@ out:
        return ret;
 }
 
-void intel_fini_pipe_control(struct intel_engine_cs *engine)
+static void reset_ring_common(struct intel_engine_cs *engine,
+                             struct drm_i915_gem_request *request)
 {
-       if (engine->scratch.obj == NULL)
-               return;
-
-       i915_gem_object_ggtt_unpin(engine->scratch.obj);
-       drm_gem_object_unreference(&engine->scratch.obj->base);
-       engine->scratch.obj = NULL;
-}
-
-int intel_init_pipe_control(struct intel_engine_cs *engine, int size)
-{
-       struct drm_i915_gem_object *obj;
-       int ret;
-
-       WARN_ON(engine->scratch.obj);
-
-       obj = i915_gem_object_create_stolen(&engine->i915->drm, size);
-       if (!obj)
-               obj = i915_gem_object_create(&engine->i915->drm, size);
-       if (IS_ERR(obj)) {
-               DRM_ERROR("Failed to allocate scratch page\n");
-               ret = PTR_ERR(obj);
-               goto err;
-       }
-
-       ret = i915_gem_obj_ggtt_pin(obj, 4096, PIN_HIGH);
-       if (ret)
-               goto err_unref;
+       struct intel_ring *ring = request->ring;
 
-       engine->scratch.obj = obj;
-       engine->scratch.gtt_offset = i915_gem_obj_ggtt_offset(obj);
-       DRM_DEBUG_DRIVER("%s pipe control offset: 0x%08x\n",
-                        engine->name, engine->scratch.gtt_offset);
-       return 0;
-
-err_unref:
-       drm_gem_object_unreference(&engine->scratch.obj->base);
-err:
-       return ret;
+       ring->head = request->postfix;
+       ring->last_retired_head = -1;
 }
 
 static int intel_ring_workarounds_emit(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct i915_workarounds *w = &req->i915->workarounds;
        int ret, i;
 
        if (w->count == 0)
                return 0;
 
-       engine->gpu_caches_dirty = true;
-       ret = intel_ring_flush_all_caches(req);
+       ret = req->engine->emit_flush(req, EMIT_BARRIER);
        if (ret)
                return ret;
 
@@ -700,17 +640,16 @@ static int intel_ring_workarounds_emit(struct drm_i915_gem_request *req)
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_LOAD_REGISTER_IMM(w->count));
+       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(w->count));
        for (i = 0; i < w->count; i++) {
-               intel_ring_emit_reg(engine, w->reg[i].addr);
-               intel_ring_emit(engine, w->reg[i].value);
+               intel_ring_emit_reg(ring, w->reg[i].addr);
+               intel_ring_emit(ring, w->reg[i].value);
        }
-       intel_ring_emit(engine, MI_NOOP);
+       intel_ring_emit(ring, MI_NOOP);
 
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
-       engine->gpu_caches_dirty = true;
-       ret = intel_ring_flush_all_caches(req);
+       ret = req->engine->emit_flush(req, EMIT_BARRIER);
        if (ret)
                return ret;
 
@@ -1022,7 +961,7 @@ static int skl_tune_iz_hashing(struct intel_engine_cs *engine)
                 * Only consider slices where one, and only one, subslice has 7
                 * EUs
                 */
-               if (!is_power_of_2(dev_priv->info.subslice_7eu[i]))
+               if (!is_power_of_2(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]))
                        continue;
 
                /*
@@ -1031,7 +970,7 @@ static int skl_tune_iz_hashing(struct intel_engine_cs *engine)
                 *
                 * ->    0 <= ss <= 3;
                 */
-               ss = ffs(dev_priv->info.subslice_7eu[i]) - 1;
+               ss = ffs(INTEL_INFO(dev_priv)->sseu.subslice_7eu[i]) - 1;
                vals[i] = 3 - ss;
        }
 
@@ -1329,191 +1268,194 @@ static void render_ring_cleanup(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
 
-       if (dev_priv->semaphore_obj) {
-               i915_gem_object_ggtt_unpin(dev_priv->semaphore_obj);
-               drm_gem_object_unreference(&dev_priv->semaphore_obj->base);
-               dev_priv->semaphore_obj = NULL;
-       }
-
-       intel_fini_pipe_control(engine);
+       i915_vma_unpin_and_release(&dev_priv->semaphore);
 }
 
-static int gen8_rcs_signal(struct drm_i915_gem_request *signaller_req,
-                          unsigned int num_dwords)
+static int gen8_rcs_signal(struct drm_i915_gem_request *req)
 {
-#define MBOX_UPDATE_DWORDS 8
-       struct intel_engine_cs *signaller = signaller_req->engine;
-       struct drm_i915_private *dev_priv = signaller_req->i915;
+       struct intel_ring *ring = req->ring;
+       struct drm_i915_private *dev_priv = req->i915;
        struct intel_engine_cs *waiter;
        enum intel_engine_id id;
        int ret, num_rings;
 
-       num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask);
-       num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS;
-#undef MBOX_UPDATE_DWORDS
-
-       ret = intel_ring_begin(signaller_req, num_dwords);
+       num_rings = INTEL_INFO(dev_priv)->num_rings;
+       ret = intel_ring_begin(req, (num_rings-1) * 8);
        if (ret)
                return ret;
 
        for_each_engine_id(waiter, dev_priv, id) {
-               u64 gtt_offset = signaller->semaphore.signal_ggtt[id];
+               u64 gtt_offset = req->engine->semaphore.signal_ggtt[id];
                if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID)
                        continue;
 
-               intel_ring_emit(signaller, GFX_OP_PIPE_CONTROL(6));
-               intel_ring_emit(signaller, PIPE_CONTROL_GLOBAL_GTT_IVB |
-                                          PIPE_CONTROL_QW_WRITE |
-                                          PIPE_CONTROL_CS_STALL);
-               intel_ring_emit(signaller, lower_32_bits(gtt_offset));
-               intel_ring_emit(signaller, upper_32_bits(gtt_offset));
-               intel_ring_emit(signaller, signaller_req->seqno);
-               intel_ring_emit(signaller, 0);
-               intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL |
-                                          MI_SEMAPHORE_TARGET(waiter->hw_id));
-               intel_ring_emit(signaller, 0);
+               intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+               intel_ring_emit(ring,
+                               PIPE_CONTROL_GLOBAL_GTT_IVB |
+                               PIPE_CONTROL_QW_WRITE |
+                               PIPE_CONTROL_CS_STALL);
+               intel_ring_emit(ring, lower_32_bits(gtt_offset));
+               intel_ring_emit(ring, upper_32_bits(gtt_offset));
+               intel_ring_emit(ring, req->fence.seqno);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring,
+                               MI_SEMAPHORE_SIGNAL |
+                               MI_SEMAPHORE_TARGET(waiter->hw_id));
+               intel_ring_emit(ring, 0);
        }
+       intel_ring_advance(ring);
 
        return 0;
 }
 
-static int gen8_xcs_signal(struct drm_i915_gem_request *signaller_req,
-                          unsigned int num_dwords)
+static int gen8_xcs_signal(struct drm_i915_gem_request *req)
 {
-#define MBOX_UPDATE_DWORDS 6
-       struct intel_engine_cs *signaller = signaller_req->engine;
-       struct drm_i915_private *dev_priv = signaller_req->i915;
+       struct intel_ring *ring = req->ring;
+       struct drm_i915_private *dev_priv = req->i915;
        struct intel_engine_cs *waiter;
        enum intel_engine_id id;
        int ret, num_rings;
 
-       num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask);
-       num_dwords += (num_rings-1) * MBOX_UPDATE_DWORDS;
-#undef MBOX_UPDATE_DWORDS
-
-       ret = intel_ring_begin(signaller_req, num_dwords);
+       num_rings = INTEL_INFO(dev_priv)->num_rings;
+       ret = intel_ring_begin(req, (num_rings-1) * 6);
        if (ret)
                return ret;
 
        for_each_engine_id(waiter, dev_priv, id) {
-               u64 gtt_offset = signaller->semaphore.signal_ggtt[id];
+               u64 gtt_offset = req->engine->semaphore.signal_ggtt[id];
                if (gtt_offset == MI_SEMAPHORE_SYNC_INVALID)
                        continue;
 
-               intel_ring_emit(signaller, (MI_FLUSH_DW + 1) |
-                                          MI_FLUSH_DW_OP_STOREDW);
-               intel_ring_emit(signaller, lower_32_bits(gtt_offset) |
-                                          MI_FLUSH_DW_USE_GTT);
-               intel_ring_emit(signaller, upper_32_bits(gtt_offset));
-               intel_ring_emit(signaller, signaller_req->seqno);
-               intel_ring_emit(signaller, MI_SEMAPHORE_SIGNAL |
-                                          MI_SEMAPHORE_TARGET(waiter->hw_id));
-               intel_ring_emit(signaller, 0);
+               intel_ring_emit(ring,
+                               (MI_FLUSH_DW + 1) | MI_FLUSH_DW_OP_STOREDW);
+               intel_ring_emit(ring,
+                               lower_32_bits(gtt_offset) |
+                               MI_FLUSH_DW_USE_GTT);
+               intel_ring_emit(ring, upper_32_bits(gtt_offset));
+               intel_ring_emit(ring, req->fence.seqno);
+               intel_ring_emit(ring,
+                               MI_SEMAPHORE_SIGNAL |
+                               MI_SEMAPHORE_TARGET(waiter->hw_id));
+               intel_ring_emit(ring, 0);
        }
+       intel_ring_advance(ring);
 
        return 0;
 }
 
-static int gen6_signal(struct drm_i915_gem_request *signaller_req,
-                      unsigned int num_dwords)
+static int gen6_signal(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *signaller = signaller_req->engine;
-       struct drm_i915_private *dev_priv = signaller_req->i915;
-       struct intel_engine_cs *useless;
-       enum intel_engine_id id;
+       struct intel_ring *ring = req->ring;
+       struct drm_i915_private *dev_priv = req->i915;
+       struct intel_engine_cs *engine;
        int ret, num_rings;
 
-#define MBOX_UPDATE_DWORDS 3
-       num_rings = hweight32(INTEL_INFO(dev_priv)->ring_mask);
-       num_dwords += round_up((num_rings-1) * MBOX_UPDATE_DWORDS, 2);
-#undef MBOX_UPDATE_DWORDS
-
-       ret = intel_ring_begin(signaller_req, num_dwords);
+       num_rings = INTEL_INFO(dev_priv)->num_rings;
+       ret = intel_ring_begin(req, round_up((num_rings-1) * 3, 2));
        if (ret)
                return ret;
 
-       for_each_engine_id(useless, dev_priv, id) {
-               i915_reg_t mbox_reg = signaller->semaphore.mbox.signal[id];
+       for_each_engine(engine, dev_priv) {
+               i915_reg_t mbox_reg;
 
+               if (!(BIT(engine->hw_id) & GEN6_SEMAPHORES_MASK))
+                       continue;
+
+               mbox_reg = req->engine->semaphore.mbox.signal[engine->hw_id];
                if (i915_mmio_reg_valid(mbox_reg)) {
-                       intel_ring_emit(signaller, MI_LOAD_REGISTER_IMM(1));
-                       intel_ring_emit_reg(signaller, mbox_reg);
-                       intel_ring_emit(signaller, signaller_req->seqno);
+                       intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
+                       intel_ring_emit_reg(ring, mbox_reg);
+                       intel_ring_emit(ring, req->fence.seqno);
                }
        }
 
        /* If num_dwords was rounded, make sure the tail pointer is correct */
        if (num_rings % 2 == 0)
-               intel_ring_emit(signaller, MI_NOOP);
+               intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
+
+       return 0;
+}
+
+static void i9xx_submit_request(struct drm_i915_gem_request *request)
+{
+       struct drm_i915_private *dev_priv = request->i915;
+
+       I915_WRITE_TAIL(request->engine,
+                       intel_ring_offset(request->ring, request->tail));
+}
+
+static int i9xx_emit_request(struct drm_i915_gem_request *req)
+{
+       struct intel_ring *ring = req->ring;
+       int ret;
+
+       ret = intel_ring_begin(req, 4);
+       if (ret)
+               return ret;
+
+       intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
+       intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
+       intel_ring_emit(ring, req->fence.seqno);
+       intel_ring_emit(ring, MI_USER_INTERRUPT);
+       intel_ring_advance(ring);
+
+       req->tail = ring->tail;
 
        return 0;
 }
 
 /**
- * gen6_add_request - Update the semaphore mailbox registers
+ * gen6_sema_emit_request - Update the semaphore mailbox registers
  *
  * @request - request to write to the ring
  *
  * Update the mailbox registers in the *other* rings with the current seqno.
  * This acts like a signal in the canonical semaphore.
  */
-static int
-gen6_add_request(struct drm_i915_gem_request *req)
+static int gen6_sema_emit_request(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
        int ret;
 
-       if (engine->semaphore.signal)
-               ret = engine->semaphore.signal(req, 4);
-       else
-               ret = intel_ring_begin(req, 4);
-
+       ret = req->engine->semaphore.signal(req);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_STORE_DWORD_INDEX);
-       intel_ring_emit(engine,
-                       I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
-       intel_ring_emit(engine, req->seqno);
-       intel_ring_emit(engine, MI_USER_INTERRUPT);
-       __intel_ring_advance(engine);
-
-       return 0;
+       return i9xx_emit_request(req);
 }
 
-static int
-gen8_render_add_request(struct drm_i915_gem_request *req)
+static int gen8_render_emit_request(struct drm_i915_gem_request *req)
 {
        struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
-       if (engine->semaphore.signal)
-               ret = engine->semaphore.signal(req, 8);
-       else
-               ret = intel_ring_begin(req, 8);
+       if (engine->semaphore.signal) {
+               ret = engine->semaphore.signal(req);
+               if (ret)
+                       return ret;
+       }
+
+       ret = intel_ring_begin(req, 8);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, GFX_OP_PIPE_CONTROL(6));
-       intel_ring_emit(engine, (PIPE_CONTROL_GLOBAL_GTT_IVB |
-                                PIPE_CONTROL_CS_STALL |
-                                PIPE_CONTROL_QW_WRITE));
-       intel_ring_emit(engine, intel_hws_seqno_address(req->engine));
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, i915_gem_request_get_seqno(req));
+       intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(6));
+       intel_ring_emit(ring, (PIPE_CONTROL_GLOBAL_GTT_IVB |
+                              PIPE_CONTROL_CS_STALL |
+                              PIPE_CONTROL_QW_WRITE));
+       intel_ring_emit(ring, intel_hws_seqno_address(engine));
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, i915_gem_request_get_seqno(req));
        /* We're thrashing one dword of HWS. */
-       intel_ring_emit(engine, 0);
-       intel_ring_emit(engine, MI_USER_INTERRUPT);
-       intel_ring_emit(engine, MI_NOOP);
-       __intel_ring_advance(engine);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, MI_USER_INTERRUPT);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
-       return 0;
-}
+       req->tail = ring->tail;
 
-static inline bool i915_gem_has_seqno_wrapped(struct drm_i915_private *dev_priv,
-                                             u32 seqno)
-{
-       return dev_priv->last_seqno < seqno;
+       return 0;
 }
 
 /**
@@ -1525,82 +1467,71 @@ static inline bool i915_gem_has_seqno_wrapped(struct drm_i915_private *dev_priv,
  */
 
 static int
-gen8_ring_sync(struct drm_i915_gem_request *waiter_req,
-              struct intel_engine_cs *signaller,
-              u32 seqno)
+gen8_ring_sync_to(struct drm_i915_gem_request *req,
+                 struct drm_i915_gem_request *signal)
 {
-       struct intel_engine_cs *waiter = waiter_req->engine;
-       struct drm_i915_private *dev_priv = waiter_req->i915;
-       u64 offset = GEN8_WAIT_OFFSET(waiter, signaller->id);
+       struct intel_ring *ring = req->ring;
+       struct drm_i915_private *dev_priv = req->i915;
+       u64 offset = GEN8_WAIT_OFFSET(req->engine, signal->engine->id);
        struct i915_hw_ppgtt *ppgtt;
        int ret;
 
-       ret = intel_ring_begin(waiter_req, 4);
+       ret = intel_ring_begin(req, 4);
        if (ret)
                return ret;
 
-       intel_ring_emit(waiter, MI_SEMAPHORE_WAIT |
-                               MI_SEMAPHORE_GLOBAL_GTT |
-                               MI_SEMAPHORE_SAD_GTE_SDD);
-       intel_ring_emit(waiter, seqno);
-       intel_ring_emit(waiter, lower_32_bits(offset));
-       intel_ring_emit(waiter, upper_32_bits(offset));
-       intel_ring_advance(waiter);
+       intel_ring_emit(ring,
+                       MI_SEMAPHORE_WAIT |
+                       MI_SEMAPHORE_GLOBAL_GTT |
+                       MI_SEMAPHORE_SAD_GTE_SDD);
+       intel_ring_emit(ring, signal->fence.seqno);
+       intel_ring_emit(ring, lower_32_bits(offset));
+       intel_ring_emit(ring, upper_32_bits(offset));
+       intel_ring_advance(ring);
 
        /* When the !RCS engines idle waiting upon a semaphore, they lose their
         * pagetables and we must reload them before executing the batch.
         * We do this on the i915_switch_context() following the wait and
         * before the dispatch.
         */
-       ppgtt = waiter_req->ctx->ppgtt;
-       if (ppgtt && waiter_req->engine->id != RCS)
-               ppgtt->pd_dirty_rings |= intel_engine_flag(waiter_req->engine);
+       ppgtt = req->ctx->ppgtt;
+       if (ppgtt && req->engine->id != RCS)
+               ppgtt->pd_dirty_rings |= intel_engine_flag(req->engine);
        return 0;
 }
 
 static int
-gen6_ring_sync(struct drm_i915_gem_request *waiter_req,
-              struct intel_engine_cs *signaller,
-              u32 seqno)
+gen6_ring_sync_to(struct drm_i915_gem_request *req,
+                 struct drm_i915_gem_request *signal)
 {
-       struct intel_engine_cs *waiter = waiter_req->engine;
+       struct intel_ring *ring = req->ring;
        u32 dw1 = MI_SEMAPHORE_MBOX |
                  MI_SEMAPHORE_COMPARE |
                  MI_SEMAPHORE_REGISTER;
-       u32 wait_mbox = signaller->semaphore.mbox.wait[waiter->id];
+       u32 wait_mbox = signal->engine->semaphore.mbox.wait[req->engine->hw_id];
        int ret;
 
-       /* Throughout all of the GEM code, seqno passed implies our current
-        * seqno is >= the last seqno executed. However for hardware the
-        * comparison is strictly greater than.
-        */
-       seqno -= 1;
-
        WARN_ON(wait_mbox == MI_SEMAPHORE_SYNC_INVALID);
 
-       ret = intel_ring_begin(waiter_req, 4);
+       ret = intel_ring_begin(req, 4);
        if (ret)
                return ret;
 
-       /* If seqno wrap happened, omit the wait with no-ops */
-       if (likely(!i915_gem_has_seqno_wrapped(waiter_req->i915, seqno))) {
-               intel_ring_emit(waiter, dw1 | wait_mbox);
-               intel_ring_emit(waiter, seqno);
-               intel_ring_emit(waiter, 0);
-               intel_ring_emit(waiter, MI_NOOP);
-       } else {
-               intel_ring_emit(waiter, MI_NOOP);
-               intel_ring_emit(waiter, MI_NOOP);
-               intel_ring_emit(waiter, MI_NOOP);
-               intel_ring_emit(waiter, MI_NOOP);
-       }
-       intel_ring_advance(waiter);
+       intel_ring_emit(ring, dw1 | wait_mbox);
+       /* Throughout all of the GEM code, seqno passed implies our current
+        * seqno is >= the last seqno executed. However for hardware the
+        * comparison is strictly greater than.
+        */
+       intel_ring_emit(ring, signal->fence.seqno - 1);
+       intel_ring_emit(ring, 0);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static void
-gen5_seqno_barrier(struct intel_engine_cs *ring)
+gen5_seqno_barrier(struct intel_engine_cs *engine)
 {
        /* MI_STORE are internally buffered by the GPU and not flushed
         * either by MI_FLUSH or SyncFlush or any other combination of
@@ -1693,40 +1624,18 @@ i8xx_irq_disable(struct intel_engine_cs *engine)
 }
 
 static int
-bsd_ring_flush(struct drm_i915_gem_request *req,
-              u32     invalidate_domains,
-              u32     flush_domains)
+bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_FLUSH);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
-       return 0;
-}
-
-static int
-i9xx_add_request(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       int ret;
-
-       ret = intel_ring_begin(req, 4);
-       if (ret)
-               return ret;
-
-       intel_ring_emit(engine, MI_STORE_DWORD_INDEX);
-       intel_ring_emit(engine,
-                       I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
-       intel_ring_emit(engine, req->seqno);
-       intel_ring_emit(engine, MI_USER_INTERRUPT);
-       __intel_ring_advance(engine);
-
+       intel_ring_emit(ring, MI_FLUSH);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
        return 0;
 }
 
@@ -1788,24 +1697,24 @@ gen8_irq_disable(struct intel_engine_cs *engine)
 }
 
 static int
-i965_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                        u64 offset, u32 length,
-                        unsigned dispatch_flags)
+i965_emit_bb_start(struct drm_i915_gem_request *req,
+                  u64 offset, u32 length,
+                  unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine,
+       intel_ring_emit(ring,
                        MI_BATCH_BUFFER_START |
                        MI_BATCH_GTT |
                        (dispatch_flags & I915_DISPATCH_SECURE ?
                         0 : MI_BATCH_NON_SECURE_I965));
-       intel_ring_emit(engine, offset);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, offset);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -1815,12 +1724,12 @@ i965_dispatch_execbuffer(struct drm_i915_gem_request *req,
 #define I830_TLB_ENTRIES (2)
 #define I830_WA_SIZE max(I830_TLB_ENTRIES*4096, I830_BATCH_LIMIT)
 static int
-i830_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                        u64 offset, u32 len,
-                        unsigned dispatch_flags)
+i830_emit_bb_start(struct drm_i915_gem_request *req,
+                  u64 offset, u32 len,
+                  unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
-       u32 cs_offset = engine->scratch.gtt_offset;
+       struct intel_ring *ring = req->ring;
+       u32 cs_offset = i915_ggtt_offset(req->engine->scratch);
        int ret;
 
        ret = intel_ring_begin(req, 6);
@@ -1828,13 +1737,13 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req,
                return ret;
 
        /* Evict the invalid PTE TLBs */
-       intel_ring_emit(engine, COLOR_BLT_CMD | BLT_WRITE_RGBA);
-       intel_ring_emit(engine, BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096);
-       intel_ring_emit(engine, I830_TLB_ENTRIES << 16 | 4); /* load each page */
-       intel_ring_emit(engine, cs_offset);
-       intel_ring_emit(engine, 0xdeadbeef);
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, COLOR_BLT_CMD | BLT_WRITE_RGBA);
+       intel_ring_emit(ring, BLT_DEPTH_32 | BLT_ROP_COLOR_COPY | 4096);
+       intel_ring_emit(ring, I830_TLB_ENTRIES << 16 | 4); /* load each page */
+       intel_ring_emit(ring, cs_offset);
+       intel_ring_emit(ring, 0xdeadbeef);
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        if ((dispatch_flags & I915_DISPATCH_PINNED) == 0) {
                if (len > I830_BATCH_LIMIT)
@@ -1848,17 +1757,17 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req,
                 * stable batch scratch bo area (so that the CS never
                 * stumbles over its tlb invalidation bug) ...
                 */
-               intel_ring_emit(engine, SRC_COPY_BLT_CMD | BLT_WRITE_RGBA);
-               intel_ring_emit(engine,
+               intel_ring_emit(ring, SRC_COPY_BLT_CMD | BLT_WRITE_RGBA);
+               intel_ring_emit(ring,
                                BLT_DEPTH_32 | BLT_ROP_SRC_COPY | 4096);
-               intel_ring_emit(engine, DIV_ROUND_UP(len, 4096) << 16 | 4096);
-               intel_ring_emit(engine, cs_offset);
-               intel_ring_emit(engine, 4096);
-               intel_ring_emit(engine, offset);
+               intel_ring_emit(ring, DIV_ROUND_UP(len, 4096) << 16 | 4096);
+               intel_ring_emit(ring, cs_offset);
+               intel_ring_emit(ring, 4096);
+               intel_ring_emit(ring, offset);
 
-               intel_ring_emit(engine, MI_FLUSH);
-               intel_ring_emit(engine, MI_NOOP);
-               intel_ring_advance(engine);
+               intel_ring_emit(ring, MI_FLUSH);
+               intel_ring_emit(ring, MI_NOOP);
+               intel_ring_advance(ring);
 
                /* ... and execute it. */
                offset = cs_offset;
@@ -1868,30 +1777,30 @@ i830_dispatch_execbuffer(struct drm_i915_gem_request *req,
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_BATCH_BUFFER_START | MI_BATCH_GTT);
-       intel_ring_emit(engine, offset | (dispatch_flags & I915_DISPATCH_SECURE ?
-                                         0 : MI_BATCH_NON_SECURE));
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT);
+       intel_ring_emit(ring, offset | (dispatch_flags & I915_DISPATCH_SECURE ?
+                                       0 : MI_BATCH_NON_SECURE));
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-i915_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                        u64 offset, u32 len,
-                        unsigned dispatch_flags)
+i915_emit_bb_start(struct drm_i915_gem_request *req,
+                  u64 offset, u32 len,
+                  unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine, MI_BATCH_BUFFER_START | MI_BATCH_GTT);
-       intel_ring_emit(engine, offset | (dispatch_flags & I915_DISPATCH_SECURE ?
-                                         0 : MI_BATCH_NON_SECURE));
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, MI_BATCH_BUFFER_START | MI_BATCH_GTT);
+       intel_ring_emit(ring, offset | (dispatch_flags & I915_DISPATCH_SECURE ?
+                                       0 : MI_BATCH_NON_SECURE));
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -1909,79 +1818,79 @@ static void cleanup_phys_status_page(struct intel_engine_cs *engine)
 
 static void cleanup_status_page(struct intel_engine_cs *engine)
 {
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
 
-       obj = engine->status_page.obj;
-       if (obj == NULL)
+       vma = fetch_and_zero(&engine->status_page.vma);
+       if (!vma)
                return;
 
-       kunmap(sg_page(obj->pages->sgl));
-       i915_gem_object_ggtt_unpin(obj);
-       drm_gem_object_unreference(&obj->base);
-       engine->status_page.obj = NULL;
+       i915_vma_unpin(vma);
+       i915_gem_object_unpin_map(vma->obj);
+       i915_vma_put(vma);
 }
 
 static int init_status_page(struct intel_engine_cs *engine)
 {
-       struct drm_i915_gem_object *obj = engine->status_page.obj;
-
-       if (obj == NULL) {
-               unsigned flags;
-               int ret;
+       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
+       unsigned int flags;
+       int ret;
 
-               obj = i915_gem_object_create(&engine->i915->drm, 4096);
-               if (IS_ERR(obj)) {
-                       DRM_ERROR("Failed to allocate status page\n");
-                       return PTR_ERR(obj);
-               }
+       obj = i915_gem_object_create(&engine->i915->drm, 4096);
+       if (IS_ERR(obj)) {
+               DRM_ERROR("Failed to allocate status page\n");
+               return PTR_ERR(obj);
+       }
 
-               ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
-               if (ret)
-                       goto err_unref;
-
-               flags = 0;
-               if (!HAS_LLC(engine->i915))
-                       /* On g33, we cannot place HWS above 256MiB, so
-                        * restrict its pinning to the low mappable arena.
-                        * Though this restriction is not documented for
-                        * gen4, gen5, or byt, they also behave similarly
-                        * and hang if the HWS is placed at the top of the
-                        * GTT. To generalise, it appears that all !llc
-                        * platforms have issues with us placing the HWS
-                        * above the mappable region (even though we never
-                        * actualy map it).
-                        */
-                       flags |= PIN_MAPPABLE;
-               ret = i915_gem_obj_ggtt_pin(obj, 4096, flags);
-               if (ret) {
-err_unref:
-                       drm_gem_object_unreference(&obj->base);
-                       return ret;
-               }
+       ret = i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
+       if (ret)
+               goto err;
 
-               engine->status_page.obj = obj;
+       vma = i915_vma_create(obj, &engine->i915->ggtt.base, NULL);
+       if (IS_ERR(vma)) {
+               ret = PTR_ERR(vma);
+               goto err;
        }
 
-       engine->status_page.gfx_addr = i915_gem_obj_ggtt_offset(obj);
-       engine->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
-       memset(engine->status_page.page_addr, 0, PAGE_SIZE);
+       flags = PIN_GLOBAL;
+       if (!HAS_LLC(engine->i915))
+               /* On g33, we cannot place HWS above 256MiB, so
+                * restrict its pinning to the low mappable arena.
+                * Though this restriction is not documented for
+                * gen4, gen5, or byt, they also behave similarly
+                * and hang if the HWS is placed at the top of the
+                * GTT. To generalise, it appears that all !llc
+                * platforms have issues with us placing the HWS
+                * above the mappable region (even though we never
+                * actualy map it).
+                */
+               flags |= PIN_MAPPABLE;
+       ret = i915_vma_pin(vma, 0, 4096, flags);
+       if (ret)
+               goto err;
 
-       DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n",
-                       engine->name, engine->status_page.gfx_addr);
+       engine->status_page.vma = vma;
+       engine->status_page.ggtt_offset = i915_ggtt_offset(vma);
+       engine->status_page.page_addr =
+               i915_gem_object_pin_map(obj, I915_MAP_WB);
 
+       DRM_DEBUG_DRIVER("%s hws offset: 0x%08x\n",
+                        engine->name, i915_ggtt_offset(vma));
        return 0;
+
+err:
+       i915_gem_object_put(obj);
+       return ret;
 }
 
 static int init_phys_status_page(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv = engine->i915;
 
-       if (!dev_priv->status_page_dmah) {
-               dev_priv->status_page_dmah =
-                       drm_pci_alloc(&dev_priv->drm, PAGE_SIZE, PAGE_SIZE);
-               if (!dev_priv->status_page_dmah)
-                       return -ENOMEM;
-       }
+       dev_priv->status_page_dmah =
+               drm_pci_alloc(&dev_priv->drm, PAGE_SIZE, PAGE_SIZE);
+       if (!dev_priv->status_page_dmah)
+               return -ENOMEM;
 
        engine->status_page.page_addr = dev_priv->status_page_dmah->vaddr;
        memset(engine->status_page.page_addr, 0, PAGE_SIZE);
@@ -1989,115 +1898,105 @@ static int init_phys_status_page(struct intel_engine_cs *engine)
        return 0;
 }
 
-void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf)
-{
-       GEM_BUG_ON(ringbuf->vma == NULL);
-       GEM_BUG_ON(ringbuf->virtual_start == NULL);
-
-       if (HAS_LLC(ringbuf->obj->base.dev) && !ringbuf->obj->stolen)
-               i915_gem_object_unpin_map(ringbuf->obj);
-       else
-               i915_vma_unpin_iomap(ringbuf->vma);
-       ringbuf->virtual_start = NULL;
-
-       i915_gem_object_ggtt_unpin(ringbuf->obj);
-       ringbuf->vma = NULL;
-}
-
-int intel_pin_and_map_ringbuffer_obj(struct drm_i915_private *dev_priv,
-                                    struct intel_ringbuffer *ringbuf)
+int intel_ring_pin(struct intel_ring *ring)
 {
-       struct drm_i915_gem_object *obj = ringbuf->obj;
        /* Ring wraparound at offset 0 sometimes hangs. No idea why. */
-       unsigned flags = PIN_OFFSET_BIAS | 4096;
+       unsigned int flags = PIN_GLOBAL | PIN_OFFSET_BIAS | 4096;
+       enum i915_map_type map;
+       struct i915_vma *vma = ring->vma;
        void *addr;
        int ret;
 
-       if (HAS_LLC(dev_priv) && !obj->stolen) {
-               ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE, flags);
-               if (ret)
-                       return ret;
+       GEM_BUG_ON(ring->vaddr);
 
-               ret = i915_gem_object_set_to_cpu_domain(obj, true);
-               if (ret)
-                       goto err_unpin;
+       map = HAS_LLC(ring->engine->i915) ? I915_MAP_WB : I915_MAP_WC;
 
-               addr = i915_gem_object_pin_map(obj);
-               if (IS_ERR(addr)) {
-                       ret = PTR_ERR(addr);
-                       goto err_unpin;
-               }
-       } else {
-               ret = i915_gem_obj_ggtt_pin(obj, PAGE_SIZE,
-                                           flags | PIN_MAPPABLE);
-               if (ret)
-                       return ret;
+       if (vma->obj->stolen)
+               flags |= PIN_MAPPABLE;
 
-               ret = i915_gem_object_set_to_gtt_domain(obj, true);
-               if (ret)
-                       goto err_unpin;
+       if (!(vma->flags & I915_VMA_GLOBAL_BIND)) {
+               if (flags & PIN_MAPPABLE || map == I915_MAP_WC)
+                       ret = i915_gem_object_set_to_gtt_domain(vma->obj, true);
+               else
+                       ret = i915_gem_object_set_to_cpu_domain(vma->obj, true);
+               if (unlikely(ret))
+                       return ret;
+       }
 
-               /* Access through the GTT requires the device to be awake. */
-               assert_rpm_wakelock_held(dev_priv);
+       ret = i915_vma_pin(vma, 0, PAGE_SIZE, flags);
+       if (unlikely(ret))
+               return ret;
 
-               addr = i915_vma_pin_iomap(i915_gem_obj_to_ggtt(obj));
-               if (IS_ERR(addr)) {
-                       ret = PTR_ERR(addr);
-                       goto err_unpin;
-               }
-       }
+       if (i915_vma_is_map_and_fenceable(vma))
+               addr = (void __force *)i915_vma_pin_iomap(vma);
+       else
+               addr = i915_gem_object_pin_map(vma->obj, map);
+       if (IS_ERR(addr))
+               goto err;
 
-       ringbuf->virtual_start = addr;
-       ringbuf->vma = i915_gem_obj_to_ggtt(obj);
+       ring->vaddr = addr;
        return 0;
 
-err_unpin:
-       i915_gem_object_ggtt_unpin(obj);
-       return ret;
+err:
+       i915_vma_unpin(vma);
+       return PTR_ERR(addr);
 }
 
-static void intel_destroy_ringbuffer_obj(struct intel_ringbuffer *ringbuf)
+void intel_ring_unpin(struct intel_ring *ring)
 {
-       drm_gem_object_unreference(&ringbuf->obj->base);
-       ringbuf->obj = NULL;
+       GEM_BUG_ON(!ring->vma);
+       GEM_BUG_ON(!ring->vaddr);
+
+       if (i915_vma_is_map_and_fenceable(ring->vma))
+               i915_vma_unpin_iomap(ring->vma);
+       else
+               i915_gem_object_unpin_map(ring->vma->obj);
+       ring->vaddr = NULL;
+
+       i915_vma_unpin(ring->vma);
 }
 
-static int intel_alloc_ringbuffer_obj(struct drm_device *dev,
-                                     struct intel_ringbuffer *ringbuf)
+static struct i915_vma *
+intel_ring_create_vma(struct drm_i915_private *dev_priv, int size)
 {
        struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
 
-       obj = NULL;
-       if (!HAS_LLC(dev))
-               obj = i915_gem_object_create_stolen(dev, ringbuf->size);
-       if (obj == NULL)
-               obj = i915_gem_object_create(dev, ringbuf->size);
+       obj = i915_gem_object_create_stolen(&dev_priv->drm, size);
+       if (!obj)
+               obj = i915_gem_object_create(&dev_priv->drm, size);
        if (IS_ERR(obj))
-               return PTR_ERR(obj);
+               return ERR_CAST(obj);
 
        /* mark ring buffers as read-only from GPU side by default */
        obj->gt_ro = 1;
 
-       ringbuf->obj = obj;
+       vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL);
+       if (IS_ERR(vma))
+               goto err;
 
-       return 0;
+       return vma;
+
+err:
+       i915_gem_object_put(obj);
+       return vma;
 }
 
-struct intel_ringbuffer *
-intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size)
+struct intel_ring *
+intel_engine_create_ring(struct intel_engine_cs *engine, int size)
 {
-       struct intel_ringbuffer *ring;
-       int ret;
+       struct intel_ring *ring;
+       struct i915_vma *vma;
+
+       GEM_BUG_ON(!is_power_of_2(size));
 
        ring = kzalloc(sizeof(*ring), GFP_KERNEL);
-       if (ring == NULL) {
-               DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s\n",
-                                engine->name);
+       if (!ring)
                return ERR_PTR(-ENOMEM);
-       }
 
        ring->engine = engine;
-       list_add(&ring->link, &engine->buffers);
+
+       INIT_LIST_HEAD(&ring->request_list);
 
        ring->size = size;
        /* Workaround an erratum on the i830 which causes a hang if
@@ -2111,23 +2010,20 @@ intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size)
        ring->last_retired_head = -1;
        intel_ring_update_space(ring);
 
-       ret = intel_alloc_ringbuffer_obj(&engine->i915->drm, ring);
-       if (ret) {
-               DRM_DEBUG_DRIVER("Failed to allocate ringbuffer %s: %d\n",
-                                engine->name, ret);
-               list_del(&ring->link);
+       vma = intel_ring_create_vma(engine->i915, size);
+       if (IS_ERR(vma)) {
                kfree(ring);
-               return ERR_PTR(ret);
+               return ERR_CAST(vma);
        }
+       ring->vma = vma;
 
        return ring;
 }
 
 void
-intel_ringbuffer_free(struct intel_ringbuffer *ring)
+intel_ring_free(struct intel_ring *ring)
 {
-       intel_destroy_ringbuffer_obj(ring);
-       list_del(&ring->link);
+       i915_vma_put(ring->vma);
        kfree(ring);
 }
 
@@ -2143,7 +2039,12 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
                return 0;
 
        if (ce->state) {
-               ret = i915_gem_obj_ggtt_pin(ce->state, ctx->ggtt_alignment, 0);
+               ret = i915_gem_object_set_to_gtt_domain(ce->state->obj, false);
+               if (ret)
+                       goto error;
+
+               ret = i915_vma_pin(ce->state, 0, ctx->ggtt_alignment,
+                                  PIN_GLOBAL | PIN_HIGH);
                if (ret)
                        goto error;
        }
@@ -2158,7 +2059,7 @@ static int intel_ring_context_pin(struct i915_gem_context *ctx,
        if (ctx == ctx->i915->kernel_context)
                ce->initialised = true;
 
-       i915_gem_context_reference(ctx);
+       i915_gem_context_get(ctx);
        return 0;
 
 error:
@@ -2177,30 +2078,25 @@ static void intel_ring_context_unpin(struct i915_gem_context *ctx,
                return;
 
        if (ce->state)
-               i915_gem_object_ggtt_unpin(ce->state);
+               i915_vma_unpin(ce->state);
 
-       i915_gem_context_unreference(ctx);
+       i915_gem_context_put(ctx);
 }
 
-static int intel_init_ring_buffer(struct drm_device *dev,
-                                 struct intel_engine_cs *engine)
+static int intel_init_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_ringbuffer *ringbuf;
+       struct drm_i915_private *dev_priv = engine->i915;
+       struct intel_ring *ring;
        int ret;
 
        WARN_ON(engine->buffer);
 
-       engine->i915 = dev_priv;
-       INIT_LIST_HEAD(&engine->active_list);
-       INIT_LIST_HEAD(&engine->request_list);
-       INIT_LIST_HEAD(&engine->execlist_queue);
-       INIT_LIST_HEAD(&engine->buffers);
-       i915_gem_batch_pool_init(dev, &engine->batch_pool);
+       intel_engine_setup_common(engine);
+
        memset(engine->semaphore.sync_seqno, 0,
               sizeof(engine->semaphore.sync_seqno));
 
-       ret = intel_engine_init_breadcrumbs(engine);
+       ret = intel_engine_init_common(engine);
        if (ret)
                goto error;
 
@@ -2215,44 +2111,38 @@ static int intel_init_ring_buffer(struct drm_device *dev,
        if (ret)
                goto error;
 
-       ringbuf = intel_engine_create_ringbuffer(engine, 32 * PAGE_SIZE);
-       if (IS_ERR(ringbuf)) {
-               ret = PTR_ERR(ringbuf);
+       ring = intel_engine_create_ring(engine, 32 * PAGE_SIZE);
+       if (IS_ERR(ring)) {
+               ret = PTR_ERR(ring);
                goto error;
        }
-       engine->buffer = ringbuf;
 
-       if (I915_NEED_GFX_HWS(dev_priv)) {
-               ret = init_status_page(engine);
+       if (HWS_NEEDS_PHYSICAL(dev_priv)) {
+               WARN_ON(engine->id != RCS);
+               ret = init_phys_status_page(engine);
                if (ret)
                        goto error;
        } else {
-               WARN_ON(engine->id != RCS);
-               ret = init_phys_status_page(engine);
+               ret = init_status_page(engine);
                if (ret)
                        goto error;
        }
 
-       ret = intel_pin_and_map_ringbuffer_obj(dev_priv, ringbuf);
+       ret = intel_ring_pin(ring);
        if (ret) {
-               DRM_ERROR("Failed to pin and map ringbuffer %s: %d\n",
-                               engine->name, ret);
-               intel_destroy_ringbuffer_obj(ringbuf);
+               intel_ring_free(ring);
                goto error;
        }
-
-       ret = i915_cmd_parser_init_ring(engine);
-       if (ret)
-               goto error;
+       engine->buffer = ring;
 
        return 0;
 
 error:
-       intel_cleanup_engine(engine);
+       intel_engine_cleanup(engine);
        return ret;
 }
 
-void intel_cleanup_engine(struct intel_engine_cs *engine)
+void intel_engine_cleanup(struct intel_engine_cs *engine)
 {
        struct drm_i915_private *dev_priv;
 
@@ -2262,49 +2152,39 @@ void intel_cleanup_engine(struct intel_engine_cs *engine)
        dev_priv = engine->i915;
 
        if (engine->buffer) {
-               intel_stop_engine(engine);
-               WARN_ON(!IS_GEN2(dev_priv) && (I915_READ_MODE(engine) & MODE_IDLE) == 0);
+               WARN_ON(INTEL_GEN(dev_priv) > 2 &&
+                       (I915_READ_MODE(engine) & MODE_IDLE) == 0);
 
-               intel_unpin_ringbuffer_obj(engine->buffer);
-               intel_ringbuffer_free(engine->buffer);
+               intel_ring_unpin(engine->buffer);
+               intel_ring_free(engine->buffer);
                engine->buffer = NULL;
        }
 
        if (engine->cleanup)
                engine->cleanup(engine);
 
-       if (I915_NEED_GFX_HWS(dev_priv)) {
-               cleanup_status_page(engine);
-       } else {
+       if (HWS_NEEDS_PHYSICAL(dev_priv)) {
                WARN_ON(engine->id != RCS);
                cleanup_phys_status_page(engine);
+       } else {
+               cleanup_status_page(engine);
        }
 
-       i915_cmd_parser_fini_ring(engine);
-       i915_gem_batch_pool_fini(&engine->batch_pool);
-       intel_engine_fini_breadcrumbs(engine);
+       intel_engine_cleanup_common(engine);
 
        intel_ring_context_unpin(dev_priv->kernel_context, engine);
 
        engine->i915 = NULL;
 }
 
-int intel_engine_idle(struct intel_engine_cs *engine)
+void intel_legacy_submission_resume(struct drm_i915_private *dev_priv)
 {
-       struct drm_i915_gem_request *req;
+       struct intel_engine_cs *engine;
 
-       /* Wait upon the last request to be completed */
-       if (list_empty(&engine->request_list))
-               return 0;
-
-       req = list_entry(engine->request_list.prev,
-                        struct drm_i915_gem_request,
-                        list);
-
-       /* Make sure we do not trigger any retires */
-       return __i915_wait_request(req,
-                                  req->i915->mm.interruptible,
-                                  NULL, NULL);
+       for_each_engine(engine, dev_priv) {
+               engine->buffer->head = engine->buffer->tail;
+               engine->buffer->last_retired_head = -1;
+       }
 }
 
 int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
@@ -2317,7 +2197,7 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
         */
        request->reserved_space += LEGACY_REQUEST_SIZE;
 
-       request->ringbuf = request->engine->buffer;
+       request->ring = request->engine->buffer;
 
        ret = intel_ring_begin(request, 0);
        if (ret)
@@ -2329,12 +2209,12 @@ int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request)
 
 static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
 {
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        struct drm_i915_gem_request *target;
+       int ret;
 
-       intel_ring_update_space(ringbuf);
-       if (ringbuf->space >= bytes)
+       intel_ring_update_space(ring);
+       if (ring->space >= bytes)
                return 0;
 
        /*
@@ -2348,35 +2228,37 @@ static int wait_for_space(struct drm_i915_gem_request *req, int bytes)
         */
        GEM_BUG_ON(!req->reserved_space);
 
-       list_for_each_entry(target, &engine->request_list, list) {
+       list_for_each_entry(target, &ring->request_list, ring_link) {
                unsigned space;
 
-               /*
-                * The request queue is per-engine, so can contain requests
-                * from multiple ringbuffers. Here, we must ignore any that
-                * aren't from the ringbuffer we're considering.
-                */
-               if (target->ringbuf != ringbuf)
-                       continue;
-
                /* Would completion of this request free enough space? */
-               space = __intel_ring_space(target->postfix, ringbuf->tail,
-                                          ringbuf->size);
+               space = __intel_ring_space(target->postfix, ring->tail,
+                                          ring->size);
                if (space >= bytes)
                        break;
        }
 
-       if (WARN_ON(&target->list == &engine->request_list))
+       if (WARN_ON(&target->ring_link == &ring->request_list))
                return -ENOSPC;
 
-       return i915_wait_request(target);
+       ret = i915_wait_request(target,
+                               I915_WAIT_INTERRUPTIBLE | I915_WAIT_LOCKED,
+                               NULL, NO_WAITBOOST);
+       if (ret)
+               return ret;
+
+       i915_gem_request_retire_upto(target);
+
+       intel_ring_update_space(ring);
+       GEM_BUG_ON(ring->space < bytes);
+       return 0;
 }
 
 int intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords)
 {
-       struct intel_ringbuffer *ringbuf = req->ringbuf;
-       int remain_actual = ringbuf->size - ringbuf->tail;
-       int remain_usable = ringbuf->effective_size - ringbuf->tail;
+       struct intel_ring *ring = req->ring;
+       int remain_actual = ring->size - ring->tail;
+       int remain_usable = ring->effective_size - ring->tail;
        int bytes = num_dwords * sizeof(u32);
        int total_bytes, wait_bytes;
        bool need_wrap = false;
@@ -2403,37 +2285,33 @@ int intel_ring_begin(struct drm_i915_gem_request *req, int num_dwords)
                wait_bytes = total_bytes;
        }
 
-       if (wait_bytes > ringbuf->space) {
+       if (wait_bytes > ring->space) {
                int ret = wait_for_space(req, wait_bytes);
                if (unlikely(ret))
                        return ret;
-
-               intel_ring_update_space(ringbuf);
-               if (unlikely(ringbuf->space < wait_bytes))
-                       return -EAGAIN;
        }
 
        if (unlikely(need_wrap)) {
-               GEM_BUG_ON(remain_actual > ringbuf->space);
-               GEM_BUG_ON(ringbuf->tail + remain_actual > ringbuf->size);
+               GEM_BUG_ON(remain_actual > ring->space);
+               GEM_BUG_ON(ring->tail + remain_actual > ring->size);
 
                /* Fill the tail with MI_NOOP */
-               memset(ringbuf->virtual_start + ringbuf->tail,
-                      0, remain_actual);
-               ringbuf->tail = 0;
-               ringbuf->space -= remain_actual;
+               memset(ring->vaddr + ring->tail, 0, remain_actual);
+               ring->tail = 0;
+               ring->space -= remain_actual;
        }
 
-       ringbuf->space -= bytes;
-       GEM_BUG_ON(ringbuf->space < 0);
+       ring->space -= bytes;
+       GEM_BUG_ON(ring->space < 0);
        return 0;
 }
 
 /* Align the ring tail to a cacheline boundary */
 int intel_ring_cacheline_align(struct drm_i915_gem_request *req)
 {
-       struct intel_engine_cs *engine = req->engine;
-       int num_dwords = (engine->buffer->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
+       struct intel_ring *ring = req->ring;
+       int num_dwords =
+               (ring->tail & (CACHELINE_BYTES - 1)) / sizeof(uint32_t);
        int ret;
 
        if (num_dwords == 0)
@@ -2445,61 +2323,16 @@ int intel_ring_cacheline_align(struct drm_i915_gem_request *req)
                return ret;
 
        while (num_dwords--)
-               intel_ring_emit(engine, MI_NOOP);
+               intel_ring_emit(ring, MI_NOOP);
 
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
-void intel_ring_init_seqno(struct intel_engine_cs *engine, u32 seqno)
-{
-       struct drm_i915_private *dev_priv = engine->i915;
-
-       /* Our semaphore implementation is strictly monotonic (i.e. we proceed
-        * so long as the semaphore value in the register/page is greater
-        * than the sync value), so whenever we reset the seqno,
-        * so long as we reset the tracking semaphore value to 0, it will
-        * always be before the next request's seqno. If we don't reset
-        * the semaphore value, then when the seqno moves backwards all
-        * future waits will complete instantly (causing rendering corruption).
-        */
-       if (IS_GEN6(dev_priv) || IS_GEN7(dev_priv)) {
-               I915_WRITE(RING_SYNC_0(engine->mmio_base), 0);
-               I915_WRITE(RING_SYNC_1(engine->mmio_base), 0);
-               if (HAS_VEBOX(dev_priv))
-                       I915_WRITE(RING_SYNC_2(engine->mmio_base), 0);
-       }
-       if (dev_priv->semaphore_obj) {
-               struct drm_i915_gem_object *obj = dev_priv->semaphore_obj;
-               struct page *page = i915_gem_object_get_dirty_page(obj, 0);
-               void *semaphores = kmap(page);
-               memset(semaphores + GEN8_SEMAPHORE_OFFSET(engine->id, 0),
-                      0, I915_NUM_ENGINES * gen8_semaphore_seqno_size);
-               kunmap(page);
-       }
-       memset(engine->semaphore.sync_seqno, 0,
-              sizeof(engine->semaphore.sync_seqno));
-
-       intel_write_status_page(engine, I915_GEM_HWS_INDEX, seqno);
-       if (engine->irq_seqno_barrier)
-               engine->irq_seqno_barrier(engine);
-       engine->last_submitted_seqno = seqno;
-
-       engine->hangcheck.seqno = seqno;
-
-       /* After manually advancing the seqno, fake the interrupt in case
-        * there are any waiters for that seqno.
-        */
-       rcu_read_lock();
-       intel_engine_wakeup(engine);
-       rcu_read_unlock();
-}
-
-static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine,
-                                    u32 value)
+static void gen6_bsd_submit_request(struct drm_i915_gem_request *request)
 {
-       struct drm_i915_private *dev_priv = engine->i915;
+       struct drm_i915_private *dev_priv = request->i915;
 
        intel_uncore_forcewake_get(dev_priv, FORCEWAKE_ALL);
 
@@ -2523,8 +2356,7 @@ static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine,
                DRM_ERROR("timed out waiting for the BSD ring to wake up\n");
 
        /* Now that the ring is fully powered up, update the tail */
-       I915_WRITE_FW(RING_TAIL(engine->mmio_base), value);
-       POSTING_READ_FW(RING_TAIL(engine->mmio_base));
+       i9xx_submit_request(request);
 
        /* Let the ring send IDLE messages to the GT again,
         * and so let it sleep to conserve power when idle.
@@ -2535,10 +2367,9 @@ static void gen6_bsd_ring_write_tail(struct intel_engine_cs *engine,
        intel_uncore_forcewake_put(dev_priv, FORCEWAKE_ALL);
 }
 
-static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req,
-                              u32 invalidate, u32 flush)
+static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        uint32_t cmd;
        int ret;
 
@@ -2563,30 +2394,29 @@ static int gen6_bsd_ring_flush(struct drm_i915_gem_request *req,
         * operation is complete. This bit is only valid when the
         * Post-Sync Operation field is a value of 1h or 3h."
         */
-       if (invalidate & I915_GEM_GPU_DOMAINS)
+       if (mode & EMIT_INVALIDATE)
                cmd |= MI_INVALIDATE_TLB | MI_INVALIDATE_BSD;
 
-       intel_ring_emit(engine, cmd);
-       intel_ring_emit(engine,
-                       I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT);
+       intel_ring_emit(ring, cmd);
+       intel_ring_emit(ring, I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT);
        if (INTEL_GEN(req->i915) >= 8) {
-               intel_ring_emit(engine, 0); /* upper addr */
-               intel_ring_emit(engine, 0); /* value */
+               intel_ring_emit(ring, 0); /* upper addr */
+               intel_ring_emit(ring, 0); /* value */
        } else  {
-               intel_ring_emit(engine, 0);
-               intel_ring_emit(engine, MI_NOOP);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, MI_NOOP);
        }
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
        return 0;
 }
 
 static int
-gen8_ring_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                             u64 offset, u32 len,
-                             unsigned dispatch_flags)
+gen8_emit_bb_start(struct drm_i915_gem_request *req,
+                  u64 offset, u32 len,
+                  unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
-       bool ppgtt = USES_PPGTT(engine->dev) &&
+       struct intel_ring *ring = req->ring;
+       bool ppgtt = USES_PPGTT(req->i915) &&
                        !(dispatch_flags & I915_DISPATCH_SECURE);
        int ret;
 
@@ -2595,71 +2425,70 @@ gen8_ring_dispatch_execbuffer(struct drm_i915_gem_request *req,
                return ret;
 
        /* FIXME(BDW): Address space and security selectors. */
-       intel_ring_emit(engine, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8) |
+       intel_ring_emit(ring, MI_BATCH_BUFFER_START_GEN8 | (ppgtt<<8) |
                        (dispatch_flags & I915_DISPATCH_RS ?
                         MI_BATCH_RESOURCE_STREAMER : 0));
-       intel_ring_emit(engine, lower_32_bits(offset));
-       intel_ring_emit(engine, upper_32_bits(offset));
-       intel_ring_emit(engine, MI_NOOP);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, lower_32_bits(offset));
+       intel_ring_emit(ring, upper_32_bits(offset));
+       intel_ring_emit(ring, MI_NOOP);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-hsw_ring_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                            u64 offset, u32 len,
-                            unsigned dispatch_flags)
+hsw_emit_bb_start(struct drm_i915_gem_request *req,
+                 u64 offset, u32 len,
+                 unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine,
+       intel_ring_emit(ring,
                        MI_BATCH_BUFFER_START |
                        (dispatch_flags & I915_DISPATCH_SECURE ?
                         0 : MI_BATCH_PPGTT_HSW | MI_BATCH_NON_SECURE_HSW) |
                        (dispatch_flags & I915_DISPATCH_RS ?
                         MI_BATCH_RESOURCE_STREAMER : 0));
        /* bit0-7 is the length on GEN6+ */
-       intel_ring_emit(engine, offset);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, offset);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 static int
-gen6_ring_dispatch_execbuffer(struct drm_i915_gem_request *req,
-                             u64 offset, u32 len,
-                             unsigned dispatch_flags)
+gen6_emit_bb_start(struct drm_i915_gem_request *req,
+                  u64 offset, u32 len,
+                  unsigned int dispatch_flags)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        int ret;
 
        ret = intel_ring_begin(req, 2);
        if (ret)
                return ret;
 
-       intel_ring_emit(engine,
+       intel_ring_emit(ring,
                        MI_BATCH_BUFFER_START |
                        (dispatch_flags & I915_DISPATCH_SECURE ?
                         0 : MI_BATCH_NON_SECURE_I965));
        /* bit0-7 is the length on GEN6+ */
-       intel_ring_emit(engine, offset);
-       intel_ring_advance(engine);
+       intel_ring_emit(ring, offset);
+       intel_ring_advance(ring);
 
        return 0;
 }
 
 /* Blitter support (SandyBridge+) */
 
-static int gen6_ring_flush(struct drm_i915_gem_request *req,
-                          u32 invalidate, u32 flush)
+static int gen6_ring_flush(struct drm_i915_gem_request *req, u32 mode)
 {
-       struct intel_engine_cs *engine = req->engine;
+       struct intel_ring *ring = req->ring;
        uint32_t cmd;
        int ret;
 
@@ -2684,19 +2513,19 @@ static int gen6_ring_flush(struct drm_i915_gem_request *req,
         * operation is complete. This bit is only valid when the
         * Post-Sync Operation field is a value of 1h or 3h."
         */
-       if (invalidate & I915_GEM_DOMAIN_RENDER)
+       if (mode & EMIT_INVALIDATE)
                cmd |= MI_INVALIDATE_TLB;
-       intel_ring_emit(engine, cmd);
-       intel_ring_emit(engine,
+       intel_ring_emit(ring, cmd);
+       intel_ring_emit(ring,
                        I915_GEM_HWS_SCRATCH_ADDR | MI_FLUSH_DW_USE_GTT);
        if (INTEL_GEN(req->i915) >= 8) {
-               intel_ring_emit(engine, 0); /* upper addr */
-               intel_ring_emit(engine, 0); /* value */
+               intel_ring_emit(ring, 0); /* upper addr */
+               intel_ring_emit(ring, 0); /* value */
        } else  {
-               intel_ring_emit(engine, 0);
-               intel_ring_emit(engine, MI_NOOP);
+               intel_ring_emit(ring, 0);
+               intel_ring_emit(ring, MI_NOOP);
        }
-       intel_ring_advance(engine);
+       intel_ring_advance(ring);
 
        return 0;
 }
@@ -2707,38 +2536,39 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv,
        struct drm_i915_gem_object *obj;
        int ret, i;
 
-       if (!i915_semaphore_is_enabled(dev_priv))
+       if (!i915.semaphores)
                return;
 
-       if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore_obj) {
+       if (INTEL_GEN(dev_priv) >= 8 && !dev_priv->semaphore) {
+               struct i915_vma *vma;
+
                obj = i915_gem_object_create(&dev_priv->drm, 4096);
-               if (IS_ERR(obj)) {
-                       DRM_ERROR("Failed to allocate semaphore bo. Disabling semaphores\n");
-                       i915.semaphores = 0;
-               } else {
-                       i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
-                       ret = i915_gem_obj_ggtt_pin(obj, 0, PIN_NONBLOCK);
-                       if (ret != 0) {
-                               drm_gem_object_unreference(&obj->base);
-                               DRM_ERROR("Failed to pin semaphore bo. Disabling semaphores\n");
-                               i915.semaphores = 0;
-                       } else {
-                               dev_priv->semaphore_obj = obj;
-                       }
-               }
-       }
+               if (IS_ERR(obj))
+                       goto err;
 
-       if (!i915_semaphore_is_enabled(dev_priv))
-               return;
+               vma = i915_vma_create(obj, &dev_priv->ggtt.base, NULL);
+               if (IS_ERR(vma))
+                       goto err_obj;
+
+               ret = i915_gem_object_set_to_gtt_domain(obj, false);
+               if (ret)
+                       goto err_obj;
+
+               ret = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
+               if (ret)
+                       goto err_obj;
+
+               dev_priv->semaphore = vma;
+       }
 
        if (INTEL_GEN(dev_priv) >= 8) {
-               u64 offset = i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj);
+               u32 offset = i915_ggtt_offset(dev_priv->semaphore);
 
-               engine->semaphore.sync_to = gen8_ring_sync;
+               engine->semaphore.sync_to = gen8_ring_sync_to;
                engine->semaphore.signal = gen8_xcs_signal;
 
                for (i = 0; i < I915_NUM_ENGINES; i++) {
-                       u64 ring_offset;
+                       u32 ring_offset;
 
                        if (i != engine->id)
                                ring_offset = offset + GEN8_SEMAPHORE_OFFSET(engine->id, i);
@@ -2748,7 +2578,7 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv,
                        engine->semaphore.signal_ggtt[i] = ring_offset;
                }
        } else if (INTEL_GEN(dev_priv) >= 6) {
-               engine->semaphore.sync_to = gen6_ring_sync;
+               engine->semaphore.sync_to = gen6_ring_sync_to;
                engine->semaphore.signal = gen6_signal;
 
                /*
@@ -2758,52 +2588,62 @@ static void intel_ring_init_semaphores(struct drm_i915_private *dev_priv,
                 * initialized as INVALID.  Gen8 will initialize the
                 * sema between VCS2 and RCS later.
                 */
-               for (i = 0; i < I915_NUM_ENGINES; i++) {
+               for (i = 0; i < GEN6_NUM_SEMAPHORES; i++) {
                        static const struct {
                                u32 wait_mbox;
                                i915_reg_t mbox_reg;
-                       } sem_data[I915_NUM_ENGINES][I915_NUM_ENGINES] = {
-                               [RCS] = {
-                                       [VCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_RV,  .mbox_reg = GEN6_VRSYNC },
-                                       [BCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_RB,  .mbox_reg = GEN6_BRSYNC },
-                                       [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC },
+                       } sem_data[GEN6_NUM_SEMAPHORES][GEN6_NUM_SEMAPHORES] = {
+                               [RCS_HW] = {
+                                       [VCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_RV,  .mbox_reg = GEN6_VRSYNC },
+                                       [BCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_RB,  .mbox_reg = GEN6_BRSYNC },
+                                       [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_RVE, .mbox_reg = GEN6_VERSYNC },
                                },
-                               [VCS] = {
-                                       [RCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VR,  .mbox_reg = GEN6_RVSYNC },
-                                       [BCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VB,  .mbox_reg = GEN6_BVSYNC },
-                                       [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC },
+                               [VCS_HW] = {
+                                       [RCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VR,  .mbox_reg = GEN6_RVSYNC },
+                                       [BCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VB,  .mbox_reg = GEN6_BVSYNC },
+                                       [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_VVE, .mbox_reg = GEN6_VEVSYNC },
                                },
-                               [BCS] = {
-                                       [RCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_BR,  .mbox_reg = GEN6_RBSYNC },
-                                       [VCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_BV,  .mbox_reg = GEN6_VBSYNC },
-                                       [VECS] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC },
+                               [BCS_HW] = {
+                                       [RCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_BR,  .mbox_reg = GEN6_RBSYNC },
+                                       [VCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_BV,  .mbox_reg = GEN6_VBSYNC },
+                                       [VECS_HW] = { .wait_mbox = MI_SEMAPHORE_SYNC_BVE, .mbox_reg = GEN6_VEBSYNC },
                                },
-                               [VECS] = {
-                                       [RCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC },
-                                       [VCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC },
-                                       [BCS] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC },
+                               [VECS_HW] = {
+                                       [RCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VER, .mbox_reg = GEN6_RVESYNC },
+                                       [VCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VEV, .mbox_reg = GEN6_VVESYNC },
+                                       [BCS_HW] =  { .wait_mbox = MI_SEMAPHORE_SYNC_VEB, .mbox_reg = GEN6_BVESYNC },
                                },
                        };
                        u32 wait_mbox;
                        i915_reg_t mbox_reg;
 
-                       if (i == engine->id || i == VCS2) {
+                       if (i == engine->hw_id) {
                                wait_mbox = MI_SEMAPHORE_SYNC_INVALID;
                                mbox_reg = GEN6_NOSYNC;
                        } else {
-                               wait_mbox = sem_data[engine->id][i].wait_mbox;
-                               mbox_reg = sem_data[engine->id][i].mbox_reg;
+                               wait_mbox = sem_data[engine->hw_id][i].wait_mbox;
+                               mbox_reg = sem_data[engine->hw_id][i].mbox_reg;
                        }
 
                        engine->semaphore.mbox.wait[i] = wait_mbox;
                        engine->semaphore.mbox.signal[i] = mbox_reg;
                }
        }
+
+       return;
+
+err_obj:
+       i915_gem_object_put(obj);
+err:
+       DRM_DEBUG_DRIVER("Failed to allocate space for semaphores, disabling\n");
+       i915.semaphores = 0;
 }
 
 static void intel_ring_init_irq(struct drm_i915_private *dev_priv,
                                struct intel_engine_cs *engine)
 {
+       engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT << engine->irq_shift;
+
        if (INTEL_GEN(dev_priv) >= 8) {
                engine->irq_enable = gen8_irq_enable;
                engine->irq_disable = gen8_irq_disable;
@@ -2828,83 +2668,76 @@ static void intel_ring_init_irq(struct drm_i915_private *dev_priv,
 static void intel_ring_default_vfuncs(struct drm_i915_private *dev_priv,
                                      struct intel_engine_cs *engine)
 {
+       intel_ring_init_irq(dev_priv, engine);
+       intel_ring_init_semaphores(dev_priv, engine);
+
        engine->init_hw = init_ring_common;
-       engine->write_tail = ring_write_tail;
+       engine->reset_hw = reset_ring_common;
 
-       engine->add_request = i9xx_add_request;
-       if (INTEL_GEN(dev_priv) >= 6)
-               engine->add_request = gen6_add_request;
+       engine->emit_request = i9xx_emit_request;
+       if (i915.semaphores)
+               engine->emit_request = gen6_sema_emit_request;
+       engine->submit_request = i9xx_submit_request;
 
        if (INTEL_GEN(dev_priv) >= 8)
-               engine->dispatch_execbuffer = gen8_ring_dispatch_execbuffer;
+               engine->emit_bb_start = gen8_emit_bb_start;
        else if (INTEL_GEN(dev_priv) >= 6)
-               engine->dispatch_execbuffer = gen6_ring_dispatch_execbuffer;
+               engine->emit_bb_start = gen6_emit_bb_start;
        else if (INTEL_GEN(dev_priv) >= 4)
-               engine->dispatch_execbuffer = i965_dispatch_execbuffer;
+               engine->emit_bb_start = i965_emit_bb_start;
        else if (IS_I830(dev_priv) || IS_845G(dev_priv))
-               engine->dispatch_execbuffer = i830_dispatch_execbuffer;
+               engine->emit_bb_start = i830_emit_bb_start;
        else
-               engine->dispatch_execbuffer = i915_dispatch_execbuffer;
-
-       intel_ring_init_irq(dev_priv, engine);
-       intel_ring_init_semaphores(dev_priv, engine);
+               engine->emit_bb_start = i915_emit_bb_start;
 }
 
-int intel_init_render_ring_buffer(struct drm_device *dev)
+int intel_init_render_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine = &dev_priv->engine[RCS];
+       struct drm_i915_private *dev_priv = engine->i915;
        int ret;
 
-       engine->name = "render ring";
-       engine->id = RCS;
-       engine->exec_id = I915_EXEC_RENDER;
-       engine->hw_id = 0;
-       engine->mmio_base = RENDER_RING_BASE;
-
        intel_ring_default_vfuncs(dev_priv, engine);
 
-       engine->irq_enable_mask = GT_RENDER_USER_INTERRUPT;
        if (HAS_L3_DPF(dev_priv))
                engine->irq_keep_mask = GT_RENDER_L3_PARITY_ERROR_INTERRUPT;
 
        if (INTEL_GEN(dev_priv) >= 8) {
                engine->init_context = intel_rcs_ctx_init;
-               engine->add_request = gen8_render_add_request;
-               engine->flush = gen8_render_ring_flush;
-               if (i915_semaphore_is_enabled(dev_priv))
+               engine->emit_request = gen8_render_emit_request;
+               engine->emit_flush = gen8_render_ring_flush;
+               if (i915.semaphores)
                        engine->semaphore.signal = gen8_rcs_signal;
        } else if (INTEL_GEN(dev_priv) >= 6) {
                engine->init_context = intel_rcs_ctx_init;
-               engine->flush = gen7_render_ring_flush;
+               engine->emit_flush = gen7_render_ring_flush;
                if (IS_GEN6(dev_priv))
-                       engine->flush = gen6_render_ring_flush;
+                       engine->emit_flush = gen6_render_ring_flush;
        } else if (IS_GEN5(dev_priv)) {
-               engine->flush = gen4_render_ring_flush;
+               engine->emit_flush = gen4_render_ring_flush;
        } else {
                if (INTEL_GEN(dev_priv) < 4)
-                       engine->flush = gen2_render_ring_flush;
+                       engine->emit_flush = gen2_render_ring_flush;
                else
-                       engine->flush = gen4_render_ring_flush;
+                       engine->emit_flush = gen4_render_ring_flush;
                engine->irq_enable_mask = I915_USER_INTERRUPT;
        }
 
        if (IS_HASWELL(dev_priv))
-               engine->dispatch_execbuffer = hsw_ring_dispatch_execbuffer;
+               engine->emit_bb_start = hsw_emit_bb_start;
 
        engine->init_hw = init_render_ring;
        engine->cleanup = render_ring_cleanup;
 
-       ret = intel_init_ring_buffer(dev, engine);
+       ret = intel_init_ring_buffer(engine);
        if (ret)
                return ret;
 
        if (INTEL_GEN(dev_priv) >= 6) {
-               ret = intel_init_pipe_control(engine, 4096);
+               ret = intel_engine_create_scratch(engine, 4096);
                if (ret)
                        return ret;
        } else if (HAS_BROKEN_CS_TLB(dev_priv)) {
-               ret = intel_init_pipe_control(engine, I830_WA_SIZE);
+               ret = intel_engine_create_scratch(engine, I830_WA_SIZE);
                if (ret)
                        return ret;
        }
@@ -2912,166 +2745,71 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
        return 0;
 }
 
-int intel_init_bsd_ring_buffer(struct drm_device *dev)
+int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine = &dev_priv->engine[VCS];
-
-       engine->name = "bsd ring";
-       engine->id = VCS;
-       engine->exec_id = I915_EXEC_BSD;
-       engine->hw_id = 1;
+       struct drm_i915_private *dev_priv = engine->i915;
 
        intel_ring_default_vfuncs(dev_priv, engine);
 
        if (INTEL_GEN(dev_priv) >= 6) {
-               engine->mmio_base = GEN6_BSD_RING_BASE;
                /* gen6 bsd needs a special wa for tail updates */
                if (IS_GEN6(dev_priv))
-                       engine->write_tail = gen6_bsd_ring_write_tail;
-               engine->flush = gen6_bsd_ring_flush;
-               if (INTEL_GEN(dev_priv) >= 8)
-                       engine->irq_enable_mask =
-                               GT_RENDER_USER_INTERRUPT << GEN8_VCS1_IRQ_SHIFT;
-               else
+                       engine->submit_request = gen6_bsd_submit_request;
+               engine->emit_flush = gen6_bsd_ring_flush;
+               if (INTEL_GEN(dev_priv) < 8)
                        engine->irq_enable_mask = GT_BSD_USER_INTERRUPT;
        } else {
                engine->mmio_base = BSD_RING_BASE;
-               engine->flush = bsd_ring_flush;
+               engine->emit_flush = bsd_ring_flush;
                if (IS_GEN5(dev_priv))
                        engine->irq_enable_mask = ILK_BSD_USER_INTERRUPT;
                else
                        engine->irq_enable_mask = I915_BSD_USER_INTERRUPT;
        }
 
-       return intel_init_ring_buffer(dev, engine);
+       return intel_init_ring_buffer(engine);
 }
 
 /**
  * Initialize the second BSD ring (eg. Broadwell GT3, Skylake GT3)
  */
-int intel_init_bsd2_ring_buffer(struct drm_device *dev)
+int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine = &dev_priv->engine[VCS2];
-
-       engine->name = "bsd2 ring";
-       engine->id = VCS2;
-       engine->exec_id = I915_EXEC_BSD;
-       engine->hw_id = 4;
-       engine->mmio_base = GEN8_BSD2_RING_BASE;
+       struct drm_i915_private *dev_priv = engine->i915;
 
        intel_ring_default_vfuncs(dev_priv, engine);
 
-       engine->flush = gen6_bsd_ring_flush;
-       engine->irq_enable_mask =
-                       GT_RENDER_USER_INTERRUPT << GEN8_VCS2_IRQ_SHIFT;
+       engine->emit_flush = gen6_bsd_ring_flush;
 
-       return intel_init_ring_buffer(dev, engine);
+       return intel_init_ring_buffer(engine);
 }
 
-int intel_init_blt_ring_buffer(struct drm_device *dev)
+int intel_init_blt_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine = &dev_priv->engine[BCS];
-
-       engine->name = "blitter ring";
-       engine->id = BCS;
-       engine->exec_id = I915_EXEC_BLT;
-       engine->hw_id = 2;
-       engine->mmio_base = BLT_RING_BASE;
+       struct drm_i915_private *dev_priv = engine->i915;
 
        intel_ring_default_vfuncs(dev_priv, engine);
 
-       engine->flush = gen6_ring_flush;
-       if (INTEL_GEN(dev_priv) >= 8)
-               engine->irq_enable_mask =
-                       GT_RENDER_USER_INTERRUPT << GEN8_BCS_IRQ_SHIFT;
-       else
+       engine->emit_flush = gen6_ring_flush;
+       if (INTEL_GEN(dev_priv) < 8)
                engine->irq_enable_mask = GT_BLT_USER_INTERRUPT;
 
-       return intel_init_ring_buffer(dev, engine);
+       return intel_init_ring_buffer(engine);
 }
 
-int intel_init_vebox_ring_buffer(struct drm_device *dev)
+int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine)
 {
-       struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_engine_cs *engine = &dev_priv->engine[VECS];
-
-       engine->name = "video enhancement ring";
-       engine->id = VECS;
-       engine->exec_id = I915_EXEC_VEBOX;
-       engine->hw_id = 3;
-       engine->mmio_base = VEBOX_RING_BASE;
+       struct drm_i915_private *dev_priv = engine->i915;
 
        intel_ring_default_vfuncs(dev_priv, engine);
 
-       engine->flush = gen6_ring_flush;
+       engine->emit_flush = gen6_ring_flush;
 
-       if (INTEL_GEN(dev_priv) >= 8) {
-               engine->irq_enable_mask =
-                       GT_RENDER_USER_INTERRUPT << GEN8_VECS_IRQ_SHIFT;
-       } else {
+       if (INTEL_GEN(dev_priv) < 8) {
                engine->irq_enable_mask = PM_VEBOX_USER_INTERRUPT;
                engine->irq_enable = hsw_vebox_irq_enable;
                engine->irq_disable = hsw_vebox_irq_disable;
        }
 
-       return intel_init_ring_buffer(dev, engine);
-}
-
-int
-intel_ring_flush_all_caches(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       int ret;
-
-       if (!engine->gpu_caches_dirty)
-               return 0;
-
-       ret = engine->flush(req, 0, I915_GEM_GPU_DOMAINS);
-       if (ret)
-               return ret;
-
-       trace_i915_gem_ring_flush(req, 0, I915_GEM_GPU_DOMAINS);
-
-       engine->gpu_caches_dirty = false;
-       return 0;
-}
-
-int
-intel_ring_invalidate_all_caches(struct drm_i915_gem_request *req)
-{
-       struct intel_engine_cs *engine = req->engine;
-       uint32_t flush_domains;
-       int ret;
-
-       flush_domains = 0;
-       if (engine->gpu_caches_dirty)
-               flush_domains = I915_GEM_GPU_DOMAINS;
-
-       ret = engine->flush(req, I915_GEM_GPU_DOMAINS, flush_domains);
-       if (ret)
-               return ret;
-
-       trace_i915_gem_ring_flush(req, I915_GEM_GPU_DOMAINS, flush_domains);
-
-       engine->gpu_caches_dirty = false;
-       return 0;
-}
-
-void
-intel_stop_engine(struct intel_engine_cs *engine)
-{
-       int ret;
-
-       if (!intel_engine_initialized(engine))
-               return;
-
-       ret = intel_engine_idle(engine);
-       if (ret)
-               DRM_ERROR("failed to quiesce %s whilst cleaning up: %d\n",
-                         engine->name, ret);
-
-       stop_ring(engine);
+       return intel_init_ring_buffer(engine);
 }
index 12cb7ed..ec0b4a0 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <linux/hashtable.h>
 #include "i915_gem_batch_pool.h"
+#include "i915_gem_request.h"
 
 #define I915_CMD_HASH_ORDER 9
 
  */
 #define I915_RING_FREE_SPACE 64
 
-struct  intel_hw_status_page {
-       u32             *page_addr;
-       unsigned int    gfx_addr;
-       struct          drm_i915_gem_object *obj;
+struct intel_hw_status_page {
+       struct i915_vma *vma;
+       u32 *page_addr;
+       u32 ggtt_offset;
 };
 
-#define I915_READ_TAIL(ring) I915_READ(RING_TAIL((ring)->mmio_base))
-#define I915_WRITE_TAIL(ring, val) I915_WRITE(RING_TAIL((ring)->mmio_base), val)
+#define I915_READ_TAIL(engine) I915_READ(RING_TAIL((engine)->mmio_base))
+#define I915_WRITE_TAIL(engine, val) I915_WRITE(RING_TAIL((engine)->mmio_base), val)
 
-#define I915_READ_START(ring) I915_READ(RING_START((ring)->mmio_base))
-#define I915_WRITE_START(ring, val) I915_WRITE(RING_START((ring)->mmio_base), val)
+#define I915_READ_START(engine) I915_READ(RING_START((engine)->mmio_base))
+#define I915_WRITE_START(engine, val) I915_WRITE(RING_START((engine)->mmio_base), val)
 
-#define I915_READ_HEAD(ring)  I915_READ(RING_HEAD((ring)->mmio_base))
-#define I915_WRITE_HEAD(ring, val) I915_WRITE(RING_HEAD((ring)->mmio_base), val)
+#define I915_READ_HEAD(engine)  I915_READ(RING_HEAD((engine)->mmio_base))
+#define I915_WRITE_HEAD(engine, val) I915_WRITE(RING_HEAD((engine)->mmio_base), val)
 
-#define I915_READ_CTL(ring) I915_READ(RING_CTL((ring)->mmio_base))
-#define I915_WRITE_CTL(ring, val) I915_WRITE(RING_CTL((ring)->mmio_base), val)
+#define I915_READ_CTL(engine) I915_READ(RING_CTL((engine)->mmio_base))
+#define I915_WRITE_CTL(engine, val) I915_WRITE(RING_CTL((engine)->mmio_base), val)
 
-#define I915_READ_IMR(ring) I915_READ(RING_IMR((ring)->mmio_base))
-#define I915_WRITE_IMR(ring, val) I915_WRITE(RING_IMR((ring)->mmio_base), val)
+#define I915_READ_IMR(engine) I915_READ(RING_IMR((engine)->mmio_base))
+#define I915_WRITE_IMR(engine, val) I915_WRITE(RING_IMR((engine)->mmio_base), val)
 
-#define I915_READ_MODE(ring) I915_READ(RING_MI_MODE((ring)->mmio_base))
-#define I915_WRITE_MODE(ring, val) I915_WRITE(RING_MI_MODE((ring)->mmio_base), val)
+#define I915_READ_MODE(engine) I915_READ(RING_MI_MODE((engine)->mmio_base))
+#define I915_WRITE_MODE(engine, val) I915_WRITE(RING_MI_MODE((engine)->mmio_base), val)
 
 /* seqno size is actually only a uint32, but since we plan to use MI_FLUSH_DW to
  * do the writes, and that must have qw aligned offsets, simply pretend it's 8b.
@@ -56,13 +57,13 @@ struct  intel_hw_status_page {
 #define GEN8_SEMAPHORE_OFFSET(__from, __to)                         \
        (((__from) * I915_NUM_ENGINES  + (__to)) * gen8_semaphore_seqno_size)
 #define GEN8_SIGNAL_OFFSET(__ring, to)                      \
-       (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \
+       (dev_priv->semaphore->node.start + \
         GEN8_SEMAPHORE_OFFSET((__ring)->id, (to)))
 #define GEN8_WAIT_OFFSET(__ring, from)                      \
-       (i915_gem_obj_ggtt_offset(dev_priv->semaphore_obj) + \
+       (dev_priv->semaphore->node.start + \
         GEN8_SEMAPHORE_OFFSET(from, (__ring)->id))
 
-enum intel_ring_hangcheck_action {
+enum intel_engine_hangcheck_action {
        HANGCHECK_IDLE = 0,
        HANGCHECK_WAIT,
        HANGCHECK_ACTIVE,
@@ -72,23 +73,22 @@ enum intel_ring_hangcheck_action {
 
 #define HANGCHECK_SCORE_RING_HUNG 31
 
-struct intel_ring_hangcheck {
+struct intel_engine_hangcheck {
        u64 acthd;
-       unsigned long user_interrupts;
        u32 seqno;
        int score;
-       enum intel_ring_hangcheck_action action;
+       enum intel_engine_hangcheck_action action;
        int deadlock;
        u32 instdone[I915_NUM_INSTDONE_REG];
 };
 
-struct intel_ringbuffer {
-       struct drm_i915_gem_object *obj;
-       void __iomem *virtual_start;
+struct intel_ring {
        struct i915_vma *vma;
+       void *vaddr;
 
        struct intel_engine_cs *engine;
-       struct list_head link;
+
+       struct list_head request_list;
 
        u32 head;
        u32 tail;
@@ -121,12 +121,12 @@ struct drm_i915_reg_table;
  *    an option for future use.
  *  size: size of the batch in DWORDS
  */
-struct  i915_ctx_workarounds {
+struct i915_ctx_workarounds {
        struct i915_wa_ctx_bb {
                u32 offset;
                u32 size;
        } indirect_ctx, per_ctx;
-       struct drm_i915_gem_object *obj;
+       struct i915_vma *vma;
 };
 
 struct drm_i915_gem_request;
@@ -144,11 +144,18 @@ struct intel_engine_cs {
 #define I915_NUM_ENGINES 5
 #define _VCS(n) (VCS + (n))
        unsigned int exec_id;
-       unsigned int hw_id;
-       unsigned int guc_id; /* XXX same as hw_id? */
+       enum intel_engine_hw_id {
+               RCS_HW = 0,
+               VCS_HW,
+               BCS_HW,
+               VECS_HW,
+               VCS2_HW
+       } hw_id;
+       enum intel_engine_hw_id guc_id; /* XXX same as hw_id? */
+       u64 fence_context;
        u32             mmio_base;
-       struct intel_ringbuffer *buffer;
-       struct list_head buffers;
+       unsigned int irq_shift;
+       struct intel_ring *buffer;
 
        /* Rather than have every client wait upon all user interrupts,
         * with the herd waking after every interrupt and each doing the
@@ -167,8 +174,7 @@ struct intel_engine_cs {
         * the overhead of waking that client is much preferred.
         */
        struct intel_breadcrumbs {
-               struct task_struct *irq_seqno_bh; /* bh for user interrupts */
-               unsigned long irq_wakeups;
+               struct task_struct __rcu *irq_seqno_bh; /* bh for interrupts */
                bool irq_posted;
 
                spinlock_t lock; /* protects the lists of requests */
@@ -178,6 +184,9 @@ struct intel_engine_cs {
                struct task_struct *signaler; /* used for fence signalling */
                struct drm_i915_gem_request *first_signal;
                struct timer_list fake_irq; /* used after a missed interrupt */
+               struct timer_list hangcheck; /* detect missed interrupts */
+
+               unsigned long timeout;
 
                bool irq_enabled : 1;
                bool rpm_wakelock : 1;
@@ -192,36 +201,48 @@ struct intel_engine_cs {
 
        struct intel_hw_status_page status_page;
        struct i915_ctx_workarounds wa_ctx;
+       struct i915_vma *scratch;
 
        u32             irq_keep_mask; /* always keep these interrupts */
        u32             irq_enable_mask; /* bitmask to enable ring interrupt */
-       void            (*irq_enable)(struct intel_engine_cs *ring);
-       void            (*irq_disable)(struct intel_engine_cs *ring);
+       void            (*irq_enable)(struct intel_engine_cs *engine);
+       void            (*irq_disable)(struct intel_engine_cs *engine);
 
-       int             (*init_hw)(struct intel_engine_cs *ring);
+       int             (*init_hw)(struct intel_engine_cs *engine);
+       void            (*reset_hw)(struct intel_engine_cs *engine,
+                                   struct drm_i915_gem_request *req);
 
        int             (*init_context)(struct drm_i915_gem_request *req);
 
-       void            (*write_tail)(struct intel_engine_cs *ring,
-                                     u32 value);
-       int __must_check (*flush)(struct drm_i915_gem_request *req,
-                                 u32   invalidate_domains,
-                                 u32   flush_domains);
-       int             (*add_request)(struct drm_i915_gem_request *req);
+       int             (*emit_flush)(struct drm_i915_gem_request *request,
+                                     u32 mode);
+#define EMIT_INVALIDATE        BIT(0)
+#define EMIT_FLUSH     BIT(1)
+#define EMIT_BARRIER   (EMIT_INVALIDATE | EMIT_FLUSH)
+       int             (*emit_bb_start)(struct drm_i915_gem_request *req,
+                                        u64 offset, u32 length,
+                                        unsigned int dispatch_flags);
+#define I915_DISPATCH_SECURE BIT(0)
+#define I915_DISPATCH_PINNED BIT(1)
+#define I915_DISPATCH_RS     BIT(2)
+       int             (*emit_request)(struct drm_i915_gem_request *req);
+
+       /* Pass the request to the hardware queue (e.g. directly into
+        * the legacy ringbuffer or to the end of an execlist).
+        *
+        * This is called from an atomic context with irqs disabled; must
+        * be irq safe.
+        */
+       void            (*submit_request)(struct drm_i915_gem_request *req);
+
        /* Some chipsets are not quite as coherent as advertised and need
         * an expensive kick to force a true read of the up-to-date seqno.
         * However, the up-to-date seqno is not always required and the last
         * seen value is good enough. Note that the seqno will always be
         * monotonic, even if not coherent.
         */
-       void            (*irq_seqno_barrier)(struct intel_engine_cs *ring);
-       int             (*dispatch_execbuffer)(struct drm_i915_gem_request *req,
-                                              u64 offset, u32 length,
-                                              unsigned dispatch_flags);
-#define I915_DISPATCH_SECURE 0x1
-#define I915_DISPATCH_PINNED 0x2
-#define I915_DISPATCH_RS     0x4
-       void            (*cleanup)(struct intel_engine_cs *ring);
+       void            (*irq_seqno_barrier)(struct intel_engine_cs *engine);
+       void            (*cleanup)(struct intel_engine_cs *engine);
 
        /* GEN8 signal/wait table - never trust comments!
         *        signal to     signal to    signal to   signal to      signal to
@@ -264,51 +285,36 @@ struct intel_engine_cs {
                u32     sync_seqno[I915_NUM_ENGINES-1];
 
                union {
+#define GEN6_SEMAPHORE_LAST    VECS_HW
+#define GEN6_NUM_SEMAPHORES    (GEN6_SEMAPHORE_LAST + 1)
+#define GEN6_SEMAPHORES_MASK   GENMASK(GEN6_SEMAPHORE_LAST, 0)
                        struct {
                                /* our mbox written by others */
-                               u32             wait[I915_NUM_ENGINES];
+                               u32             wait[GEN6_NUM_SEMAPHORES];
                                /* mboxes this ring signals to */
-                               i915_reg_t      signal[I915_NUM_ENGINES];
+                               i915_reg_t      signal[GEN6_NUM_SEMAPHORES];
                        } mbox;
                        u64             signal_ggtt[I915_NUM_ENGINES];
                };
 
                /* AKA wait() */
-               int     (*sync_to)(struct drm_i915_gem_request *to_req,
-                                  struct intel_engine_cs *from,
-                                  u32 seqno);
-               int     (*signal)(struct drm_i915_gem_request *signaller_req,
-                                 /* num_dwords needed by caller */
-                                 unsigned int num_dwords);
+               int     (*sync_to)(struct drm_i915_gem_request *req,
+                                  struct drm_i915_gem_request *signal);
+               int     (*signal)(struct drm_i915_gem_request *req);
        } semaphore;
 
        /* Execlists */
        struct tasklet_struct irq_tasklet;
        spinlock_t execlist_lock; /* used inside tasklet, use spin_lock_bh */
+       struct execlist_port {
+               struct drm_i915_gem_request *request;
+               unsigned int count;
+       } execlist_port[2];
        struct list_head execlist_queue;
        unsigned int fw_domains;
-       unsigned int next_context_status_buffer;
-       unsigned int idle_lite_restore_wa;
        bool disable_lite_restore_wa;
+       bool preempt_wa;
        u32 ctx_desc_template;
-       int             (*emit_request)(struct drm_i915_gem_request *request);
-       int             (*emit_flush)(struct drm_i915_gem_request *request,
-                                     u32 invalidate_domains,
-                                     u32 flush_domains);
-       int             (*emit_bb_start)(struct drm_i915_gem_request *req,
-                                        u64 offset, unsigned dispatch_flags);
-
-       /**
-        * List of objects currently involved in rendering from the
-        * ringbuffer.
-        *
-        * Includes buffers having the contents of their GPU caches
-        * flushed, not necessarily primitives.  last_read_req
-        * represents when the rendering involved will be completed.
-        *
-        * A reference is held on the buffer while on this list.
-        */
-       struct list_head active_list;
 
        /**
         * List of breadcrumbs associated with GPU requests currently
@@ -322,23 +328,24 @@ struct intel_engine_cs {
         * inspecting request list.
         */
        u32 last_submitted_seqno;
+       u32 last_pending_seqno;
 
-       bool gpu_caches_dirty;
+       /* An RCU guarded pointer to the last request. No reference is
+        * held to the request, users must carefully acquire a reference to
+        * the request using i915_gem_active_get_rcu(), or hold the
+        * struct_mutex.
+        */
+       struct i915_gem_active last_request;
 
        struct i915_gem_context *last_context;
 
-       struct intel_ring_hangcheck hangcheck;
-
-       struct {
-               struct drm_i915_gem_object *obj;
-               u32 gtt_offset;
-       } scratch;
+       struct intel_engine_hangcheck hangcheck;
 
        bool needs_cmd_parser;
 
        /*
         * Table of commands the command parser needs to know about
-        * for this ring.
+        * for this engine.
         */
        DECLARE_HASHTABLE(cmd_hash, I915_CMD_HASH_ORDER);
 
@@ -352,11 +359,11 @@ struct intel_engine_cs {
         * Returns the bitmask for the length field of the specified command.
         * Return 0 for an unrecognized/invalid command.
         *
-        * If the command parser finds an entry for a command in the ring's
+        * If the command parser finds an entry for a command in the engine's
         * cmd_tables, it gets the command's length based on the table entry.
-        * If not, it calls this function to determine the per-ring length field
-        * encoding for the command (i.e. certain opcode ranges use certain bits
-        * to encode the command length in the header).
+        * If not, it calls this function to determine the per-engine length
+        * field encoding for the command (i.e. different opcode ranges use
+        * certain bits to encode the command length in the header).
         */
        u32 (*get_cmd_length_mask)(u32 cmd_header);
 };
@@ -374,8 +381,8 @@ intel_engine_flag(const struct intel_engine_cs *engine)
 }
 
 static inline u32
-intel_ring_sync_index(struct intel_engine_cs *engine,
-                     struct intel_engine_cs *other)
+intel_engine_sync_index(struct intel_engine_cs *engine,
+                       struct intel_engine_cs *other)
 {
        int idx;
 
@@ -437,55 +444,76 @@ intel_write_status_page(struct intel_engine_cs *engine,
 #define I915_GEM_HWS_SCRATCH_INDEX     0x40
 #define I915_GEM_HWS_SCRATCH_ADDR (I915_GEM_HWS_SCRATCH_INDEX << MI_STORE_DWORD_INDEX_SHIFT)
 
-struct intel_ringbuffer *
-intel_engine_create_ringbuffer(struct intel_engine_cs *engine, int size);
-int intel_pin_and_map_ringbuffer_obj(struct drm_i915_private *dev_priv,
-                                    struct intel_ringbuffer *ringbuf);
-void intel_unpin_ringbuffer_obj(struct intel_ringbuffer *ringbuf);
-void intel_ringbuffer_free(struct intel_ringbuffer *ring);
+struct intel_ring *
+intel_engine_create_ring(struct intel_engine_cs *engine, int size);
+int intel_ring_pin(struct intel_ring *ring);
+void intel_ring_unpin(struct intel_ring *ring);
+void intel_ring_free(struct intel_ring *ring);
+
+void intel_engine_stop(struct intel_engine_cs *engine);
+void intel_engine_cleanup(struct intel_engine_cs *engine);
 
-void intel_stop_engine(struct intel_engine_cs *engine);
-void intel_cleanup_engine(struct intel_engine_cs *engine);
+void intel_legacy_submission_resume(struct drm_i915_private *dev_priv);
 
 int intel_ring_alloc_request_extras(struct drm_i915_gem_request *request);
 
 int __must_check intel_ring_begin(struct drm_i915_gem_request *req, int n);
 int __must_check intel_ring_cacheline_align(struct drm_i915_gem_request *req);
-static inline void intel_ring_emit(struct intel_engine_cs *engine,
-                                  u32 data)
+
+static inline void intel_ring_emit(struct intel_ring *ring, u32 data)
 {
-       struct intel_ringbuffer *ringbuf = engine->buffer;
-       iowrite32(data, ringbuf->virtual_start + ringbuf->tail);
-       ringbuf->tail += 4;
+       *(uint32_t *)(ring->vaddr + ring->tail) = data;
+       ring->tail += 4;
 }
-static inline void intel_ring_emit_reg(struct intel_engine_cs *engine,
-                                      i915_reg_t reg)
+
+static inline void intel_ring_emit_reg(struct intel_ring *ring, i915_reg_t reg)
 {
-       intel_ring_emit(engine, i915_mmio_reg_offset(reg));
+       intel_ring_emit(ring, i915_mmio_reg_offset(reg));
 }
-static inline void intel_ring_advance(struct intel_engine_cs *engine)
+
+static inline void intel_ring_advance(struct intel_ring *ring)
 {
-       struct intel_ringbuffer *ringbuf = engine->buffer;
-       ringbuf->tail &= ringbuf->size - 1;
+       /* Dummy function.
+        *
+        * This serves as a placeholder in the code so that the reader
+        * can compare against the preceding intel_ring_begin() and
+        * check that the number of dwords emitted matches the space
+        * reserved for the command packet (i.e. the value passed to
+        * intel_ring_begin()).
+        */
+}
+
+static inline u32 intel_ring_offset(struct intel_ring *ring, u32 value)
+{
+       /* Don't write ring->size (equivalent to 0) as that hangs some GPUs. */
+       return value & (ring->size - 1);
 }
+
 int __intel_ring_space(int head, int tail, int size);
-void intel_ring_update_space(struct intel_ringbuffer *ringbuf);
+void intel_ring_update_space(struct intel_ring *ring);
 
-int __must_check intel_engine_idle(struct intel_engine_cs *engine);
-void intel_ring_init_seqno(struct intel_engine_cs *engine, u32 seqno);
-int intel_ring_flush_all_caches(struct drm_i915_gem_request *req);
-int intel_ring_invalidate_all_caches(struct drm_i915_gem_request *req);
+void intel_engine_init_seqno(struct intel_engine_cs *engine, u32 seqno);
 
-int intel_init_pipe_control(struct intel_engine_cs *engine, int size);
-void intel_fini_pipe_control(struct intel_engine_cs *engine);
+void intel_engine_setup_common(struct intel_engine_cs *engine);
+int intel_engine_init_common(struct intel_engine_cs *engine);
+int intel_engine_create_scratch(struct intel_engine_cs *engine, int size);
+void intel_engine_cleanup_common(struct intel_engine_cs *engine);
 
-int intel_init_render_ring_buffer(struct drm_device *dev);
-int intel_init_bsd_ring_buffer(struct drm_device *dev);
-int intel_init_bsd2_ring_buffer(struct drm_device *dev);
-int intel_init_blt_ring_buffer(struct drm_device *dev);
-int intel_init_vebox_ring_buffer(struct drm_device *dev);
+static inline int intel_engine_idle(struct intel_engine_cs *engine,
+                                   unsigned int flags)
+{
+       /* Wait upon the last request to be completed */
+       return i915_gem_active_wait_unlocked(&engine->last_request,
+                                            flags, NULL, NULL);
+}
+
+int intel_init_render_ring_buffer(struct intel_engine_cs *engine);
+int intel_init_bsd_ring_buffer(struct intel_engine_cs *engine);
+int intel_init_bsd2_ring_buffer(struct intel_engine_cs *engine);
+int intel_init_blt_ring_buffer(struct intel_engine_cs *engine);
+int intel_init_vebox_ring_buffer(struct intel_engine_cs *engine);
 
-u64 intel_ring_get_active_head(struct intel_engine_cs *engine);
+u64 intel_engine_get_active_head(struct intel_engine_cs *engine);
 static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine)
 {
        return intel_read_status_page(engine, I915_GEM_HWS_INDEX);
@@ -493,11 +521,6 @@ static inline u32 intel_engine_get_seqno(struct intel_engine_cs *engine)
 
 int init_workarounds_ring(struct intel_engine_cs *engine);
 
-static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf)
-{
-       return ringbuf->tail;
-}
-
 /*
  * Arbitrary size for largest possible 'add request' sequence. The code paths
  * are complex and variable. Empirical measurement shows that the worst case
@@ -509,21 +532,10 @@ static inline u32 intel_ring_get_tail(struct intel_ringbuffer *ringbuf)
 
 static inline u32 intel_hws_seqno_address(struct intel_engine_cs *engine)
 {
-       return engine->status_page.gfx_addr + I915_GEM_HWS_INDEX_ADDR;
+       return engine->status_page.ggtt_offset + I915_GEM_HWS_INDEX_ADDR;
 }
 
 /* intel_breadcrumbs.c -- user interrupt bottom-half for waiters */
-struct intel_wait {
-       struct rb_node node;
-       struct task_struct *tsk;
-       u32 seqno;
-};
-
-struct intel_signal_node {
-       struct rb_node node;
-       struct intel_wait wait;
-};
-
 int intel_engine_init_breadcrumbs(struct intel_engine_cs *engine);
 
 static inline void intel_wait_init(struct intel_wait *wait, u32 seqno)
@@ -543,31 +555,43 @@ void intel_engine_remove_wait(struct intel_engine_cs *engine,
                              struct intel_wait *wait);
 void intel_engine_enable_signaling(struct drm_i915_gem_request *request);
 
-static inline bool intel_engine_has_waiter(struct intel_engine_cs *engine)
+static inline bool intel_engine_has_waiter(const struct intel_engine_cs *engine)
 {
-       return READ_ONCE(engine->breadcrumbs.irq_seqno_bh);
+       return rcu_access_pointer(engine->breadcrumbs.irq_seqno_bh);
 }
 
-static inline bool intel_engine_wakeup(struct intel_engine_cs *engine)
+static inline bool intel_engine_wakeup(const struct intel_engine_cs *engine)
 {
        bool wakeup = false;
-       struct task_struct *tsk = READ_ONCE(engine->breadcrumbs.irq_seqno_bh);
+
        /* Note that for this not to dangerously chase a dangling pointer,
-        * the caller is responsible for ensure that the task remain valid for
-        * wake_up_process() i.e. that the RCU grace period cannot expire.
+        * we must hold the rcu_read_lock here.
         *
         * Also note that tsk is likely to be in !TASK_RUNNING state so an
         * early test for tsk->state != TASK_RUNNING before wake_up_process()
         * is unlikely to be beneficial.
         */
-       if (tsk)
-               wakeup = wake_up_process(tsk);
+       if (intel_engine_has_waiter(engine)) {
+               struct task_struct *tsk;
+
+               rcu_read_lock();
+               tsk = rcu_dereference(engine->breadcrumbs.irq_seqno_bh);
+               if (tsk)
+                       wakeup = wake_up_process(tsk);
+               rcu_read_unlock();
+       }
+
        return wakeup;
 }
 
-void intel_engine_enable_fake_irq(struct intel_engine_cs *engine);
+void intel_engine_reset_breadcrumbs(struct intel_engine_cs *engine);
 void intel_engine_fini_breadcrumbs(struct intel_engine_cs *engine);
 unsigned int intel_kick_waiters(struct drm_i915_private *i915);
 unsigned int intel_kick_signalers(struct drm_i915_private *i915);
 
+static inline bool intel_engine_is_active(struct intel_engine_cs *engine)
+{
+       return i915_gem_active_isset(&engine->last_request);
+}
+
 #endif /* _INTEL_RINGBUFFER_H_ */
index 1c603bb..6c11168 100644 (file)
@@ -287,6 +287,7 @@ void intel_display_set_init_power(struct drm_i915_private *dev_priv,
  */
 static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
 {
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct drm_device *dev = &dev_priv->drm;
 
        /*
@@ -299,9 +300,9 @@ static void hsw_power_well_post_enable(struct drm_i915_private *dev_priv)
         * sure vgacon can keep working normally without triggering interrupts
         * and error messages.
         */
-       vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
+       vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO);
        outb(inb(VGA_MSR_READ), VGA_MSR_WRITE);
-       vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
+       vga_put(pdev, VGA_RSRC_LEGACY_IO);
 
        if (IS_BROADWELL(dev))
                gen8_irq_power_well_post_enable(dev_priv,
@@ -318,7 +319,7 @@ static void hsw_power_well_pre_disable(struct drm_i915_private *dev_priv)
 static void skl_power_well_post_enable(struct drm_i915_private *dev_priv,
                                       struct i915_power_well *power_well)
 {
-       struct drm_device *dev = &dev_priv->drm;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
 
        /*
         * After we re-enable the power well, if we touch VGA register 0x3d5
@@ -331,9 +332,9 @@ static void skl_power_well_post_enable(struct drm_i915_private *dev_priv,
         * and error messages.
         */
        if (power_well->data == SKL_DISP_PW_2) {
-               vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
+               vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO);
                outb(inb(VGA_MSR_READ), VGA_MSR_WRITE);
-               vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
+               vga_put(pdev, VGA_RSRC_LEGACY_IO);
 
                gen8_irq_power_well_post_enable(dev_priv,
                                                1 << PIPE_C | 1 << PIPE_B);
@@ -592,6 +593,8 @@ void bxt_disable_dc9(struct drm_i915_private *dev_priv)
        DRM_DEBUG_KMS("Disabling DC9\n");
 
        gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
+
+       intel_pps_unlock_regs_wa(dev_priv);
 }
 
 static void assert_csr_loaded(struct drm_i915_private *dev_priv)
@@ -854,7 +857,7 @@ static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
                                           struct i915_power_well *power_well)
 {
        enum skl_disp_power_wells power_well_id = power_well->data;
-       struct i915_power_well *cmn_a_well;
+       struct i915_power_well *cmn_a_well = NULL;
 
        if (power_well_id == BXT_DPIO_CMN_BC) {
                /*
@@ -867,7 +870,7 @@ static void bxt_dpio_cmn_power_well_enable(struct drm_i915_private *dev_priv,
 
        bxt_ddi_phy_init(dev_priv, bxt_power_well_to_phy(power_well));
 
-       if (power_well_id == BXT_DPIO_CMN_BC)
+       if (cmn_a_well)
                intel_power_well_put(dev_priv, cmn_a_well);
 }
 
@@ -1121,6 +1124,8 @@ static void vlv_display_power_well_init(struct drm_i915_private *dev_priv)
        }
 
        i915_redisable_vga_power_on(&dev_priv->drm);
+
+       intel_pps_unlock_regs_wa(dev_priv);
 }
 
 static void vlv_display_power_well_deinit(struct drm_i915_private *dev_priv)
@@ -2284,7 +2289,7 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv)
  */
 void intel_power_domains_fini(struct drm_i915_private *dev_priv)
 {
-       struct device *device = &dev_priv->drm.pdev->dev;
+       struct device *kdev = &dev_priv->drm.pdev->dev;
 
        /*
         * The i915.ko module is still not prepared to be loaded when
@@ -2306,7 +2311,7 @@ void intel_power_domains_fini(struct drm_i915_private *dev_priv)
         * the platform doesn't support runtime PM.
         */
        if (!HAS_RUNTIME_PM(dev_priv))
-               pm_runtime_put(device);
+               pm_runtime_put(kdev);
 }
 
 static void intel_power_domains_sync_hw(struct drm_i915_private *dev_priv)
@@ -2647,10 +2652,10 @@ void intel_power_domains_suspend(struct drm_i915_private *dev_priv)
  */
 void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct device *device = &dev->pdev->dev;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct device *kdev = &pdev->dev;
 
-       pm_runtime_get_sync(device);
+       pm_runtime_get_sync(kdev);
 
        atomic_inc(&dev_priv->pm.wakeref_count);
        assert_rpm_wakelock_held(dev_priv);
@@ -2668,11 +2673,11 @@ void intel_runtime_pm_get(struct drm_i915_private *dev_priv)
  */
 bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct device *device = &dev->pdev->dev;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct device *kdev = &pdev->dev;
 
        if (IS_ENABLED(CONFIG_PM)) {
-               int ret = pm_runtime_get_if_in_use(device);
+               int ret = pm_runtime_get_if_in_use(kdev);
 
                /*
                 * In cases runtime PM is disabled by the RPM core and we get
@@ -2710,11 +2715,11 @@ bool intel_runtime_pm_get_if_in_use(struct drm_i915_private *dev_priv)
  */
 void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct device *device = &dev->pdev->dev;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct device *kdev = &pdev->dev;
 
        assert_rpm_wakelock_held(dev_priv);
-       pm_runtime_get_noresume(device);
+       pm_runtime_get_noresume(kdev);
 
        atomic_inc(&dev_priv->pm.wakeref_count);
 }
@@ -2729,15 +2734,15 @@ void intel_runtime_pm_get_noresume(struct drm_i915_private *dev_priv)
  */
 void intel_runtime_pm_put(struct drm_i915_private *dev_priv)
 {
-       struct drm_device *dev = &dev_priv->drm;
-       struct device *device = &dev->pdev->dev;
+       struct pci_dev *pdev = dev_priv->drm.pdev;
+       struct device *kdev = &pdev->dev;
 
        assert_rpm_wakelock_held(dev_priv);
        if (atomic_dec_and_test(&dev_priv->pm.wakeref_count))
                atomic_inc(&dev_priv->pm.atomic_seq);
 
-       pm_runtime_mark_last_busy(device);
-       pm_runtime_put_autosuspend(device);
+       pm_runtime_mark_last_busy(kdev);
+       pm_runtime_put_autosuspend(kdev);
 }
 
 /**
@@ -2752,11 +2757,12 @@ void intel_runtime_pm_put(struct drm_i915_private *dev_priv)
  */
 void intel_runtime_pm_enable(struct drm_i915_private *dev_priv)
 {
+       struct pci_dev *pdev = dev_priv->drm.pdev;
        struct drm_device *dev = &dev_priv->drm;
-       struct device *device = &dev->pdev->dev;
+       struct device *kdev = &pdev->dev;
 
-       pm_runtime_set_autosuspend_delay(device, 10000); /* 10s */
-       pm_runtime_mark_last_busy(device);
+       pm_runtime_set_autosuspend_delay(kdev, 10000); /* 10s */
+       pm_runtime_mark_last_busy(kdev);
 
        /*
         * Take a permanent reference to disable the RPM functionality and drop
@@ -2765,10 +2771,10 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv)
         * platforms without RPM support.
         */
        if (!HAS_RUNTIME_PM(dev)) {
-               pm_runtime_dont_use_autosuspend(device);
-               pm_runtime_get_sync(device);
+               pm_runtime_dont_use_autosuspend(kdev);
+               pm_runtime_get_sync(kdev);
        } else {
-               pm_runtime_use_autosuspend(device);
+               pm_runtime_use_autosuspend(kdev);
        }
 
        /*
@@ -2776,6 +2782,5 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv)
         * We drop that here and will reacquire it during unloading in
         * intel_power_domains_fini().
         */
-       pm_runtime_put_autosuspend(device);
+       pm_runtime_put_autosuspend(kdev);
 }
-
index e378f35..c551024 100644 (file)
@@ -1003,24 +1003,22 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo,
 }
 
 static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo,
-                                        const struct drm_display_mode *adjusted_mode)
+                                        struct intel_crtc_state *pipe_config)
 {
        uint8_t sdvo_data[HDMI_INFOFRAME_SIZE(AVI)];
-       struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        union hdmi_infoframe frame;
        int ret;
        ssize_t len;
 
        ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
-                                                      adjusted_mode);
+                                                      &pipe_config->base.adjusted_mode);
        if (ret < 0) {
                DRM_ERROR("couldn't fill AVI infoframe\n");
                return false;
        }
 
        if (intel_sdvo->rgb_quant_range_selectable) {
-               if (intel_crtc->config->limited_color_range)
+               if (pipe_config->limited_color_range)
                        frame.avi.quantization_range =
                                HDMI_QUANTIZATION_RANGE_LIMITED;
                else
@@ -1125,7 +1123,8 @@ static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config)
 }
 
 static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
-                                     struct intel_crtc_state *pipe_config)
+                                     struct intel_crtc_state *pipe_config,
+                                     struct drm_connector_state *conn_state)
 {
        struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
        struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
@@ -1192,22 +1191,21 @@ static bool intel_sdvo_compute_config(struct intel_encoder *encoder,
        return true;
 }
 
-static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
+static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder,
+                                 struct intel_crtc_state *crtc_state,
+                                 struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = intel_encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
-       struct intel_crtc *crtc = to_intel_crtc(intel_encoder->base.crtc);
-       const struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode;
-       struct drm_display_mode *mode = &crtc->config->base.mode;
+       struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+       const struct drm_display_mode *adjusted_mode = &crtc_state->base.adjusted_mode;
+       struct drm_display_mode *mode = &crtc_state->base.mode;
        struct intel_sdvo *intel_sdvo = to_sdvo(intel_encoder);
        u32 sdvox;
        struct intel_sdvo_in_out_map in_out;
        struct intel_sdvo_dtd input_dtd, output_dtd;
        int rate;
 
-       if (!mode)
-               return;
-
        /* First, set the input mapping for the first input to our controlled
         * output. This is only correct if we're a single-input device, in
         * which case the first input is the output from the appropriate SDVO
@@ -1240,11 +1238,11 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
        if (!intel_sdvo_set_target_input(intel_sdvo))
                return;
 
-       if (crtc->config->has_hdmi_sink) {
+       if (crtc_state->has_hdmi_sink) {
                intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
                intel_sdvo_set_colorimetry(intel_sdvo,
                                           SDVO_COLORIMETRY_RGB256);
-               intel_sdvo_set_avi_infoframe(intel_sdvo, adjusted_mode);
+               intel_sdvo_set_avi_infoframe(intel_sdvo, crtc_state);
        } else
                intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI);
 
@@ -1260,7 +1258,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
                DRM_INFO("Setting input timings on %s failed\n",
                         SDVO_NAME(intel_sdvo));
 
-       switch (crtc->config->pixel_multiplier) {
+       switch (crtc_state->pixel_multiplier) {
        default:
                WARN(1, "unknown pixel multiplier specified\n");
        case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
@@ -1275,7 +1273,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
                /* The real mode polarity is set by the SDVO commands, using
                 * struct intel_sdvo_dtd. */
                sdvox = SDVO_VSYNC_ACTIVE_HIGH | SDVO_HSYNC_ACTIVE_HIGH;
-               if (!HAS_PCH_SPLIT(dev) && crtc->config->limited_color_range)
+               if (!HAS_PCH_SPLIT(dev) && crtc_state->limited_color_range)
                        sdvox |= HDMI_COLOR_RANGE_16_235;
                if (INTEL_INFO(dev)->gen < 5)
                        sdvox |= SDVO_BORDER_ENABLE;
@@ -1301,7 +1299,7 @@ static void intel_sdvo_pre_enable(struct intel_encoder *intel_encoder)
        } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
                /* done in crtc_mode_set as it lives inside the dpll register */
        } else {
-               sdvox |= (crtc->config->pixel_multiplier - 1)
+               sdvox |= (crtc_state->pixel_multiplier - 1)
                        << SDVO_PORT_MULTIPLY_SHIFT;
        }
 
@@ -1434,7 +1432,9 @@ static void intel_sdvo_get_config(struct intel_encoder *encoder,
             pipe_config->pixel_multiplier, encoder_pixel_multiplier);
 }
 
-static void intel_disable_sdvo(struct intel_encoder *encoder)
+static void intel_disable_sdvo(struct intel_encoder *encoder,
+                              struct intel_crtc_state *old_crtc_state,
+                              struct drm_connector_state *conn_state)
 {
        struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
        struct intel_sdvo *intel_sdvo = to_sdvo(encoder);
@@ -1477,16 +1477,22 @@ static void intel_disable_sdvo(struct intel_encoder *encoder)
        }
 }
 
-static void pch_disable_sdvo(struct intel_encoder *encoder)
+static void pch_disable_sdvo(struct intel_encoder *encoder,
+                            struct intel_crtc_state *old_crtc_state,
+                            struct drm_connector_state *old_conn_state)
 {
 }
 
-static void pch_post_disable_sdvo(struct intel_encoder *encoder)
+static void pch_post_disable_sdvo(struct intel_encoder *encoder,
+                                 struct intel_crtc_state *old_crtc_state,
+                                 struct drm_connector_state *old_conn_state)
 {
-       intel_disable_sdvo(encoder);
+       intel_disable_sdvo(encoder, old_crtc_state, old_conn_state);
 }
 
-static void intel_enable_sdvo(struct intel_encoder *encoder)
+static void intel_enable_sdvo(struct intel_encoder *encoder,
+                             struct intel_crtc_state *pipe_config,
+                             struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -2930,10 +2936,12 @@ static bool
 intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo,
                          struct drm_device *dev)
 {
+       struct pci_dev *pdev = dev->pdev;
+
        sdvo->ddc.owner = THIS_MODULE;
        sdvo->ddc.class = I2C_CLASS_DDC;
        snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy");
-       sdvo->ddc.dev.parent = &dev->pdev->dev;
+       sdvo->ddc.dev.parent = &pdev->dev;
        sdvo->ddc.algo_data = sdvo;
        sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
 
index 7c08e4f..73a521f 100644 (file)
@@ -36,6 +36,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_plane_helper.h>
 #include "intel_drv.h"
+#include "intel_frontbuffer.h"
 #include <drm/i915_drm.h>
 #include "i915_drv.h"
 
@@ -202,23 +203,24 @@ skl_update_plane(struct drm_plane *drm_plane,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_plane *intel_plane = to_intel_plane(drm_plane);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
+       const struct skl_wm_values *wm = &dev_priv->wm.skl_results;
+       struct drm_crtc *crtc = crtc_state->base.crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        const int pipe = intel_plane->pipe;
        const int plane = intel_plane->plane + 1;
-       u32 plane_ctl, stride_div, stride;
+       u32 plane_ctl;
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-       u32 surf_addr;
-       u32 tile_height, plane_offset, plane_size;
+       u32 surf_addr = plane_state->main.offset;
        unsigned int rotation = plane_state->base.rotation;
-       int x_offset, y_offset;
-       int crtc_x = plane_state->dst.x1;
-       int crtc_y = plane_state->dst.y1;
-       uint32_t crtc_w = drm_rect_width(&plane_state->dst);
-       uint32_t crtc_h = drm_rect_height(&plane_state->dst);
-       uint32_t x = plane_state->src.x1 >> 16;
-       uint32_t y = plane_state->src.y1 >> 16;
-       uint32_t src_w = drm_rect_width(&plane_state->src) >> 16;
-       uint32_t src_h = drm_rect_height(&plane_state->src) >> 16;
+       u32 stride = skl_plane_stride(fb, 0, rotation);
+       int crtc_x = plane_state->base.dst.x1;
+       int crtc_y = plane_state->base.dst.y1;
+       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
+       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
+       uint32_t x = plane_state->main.x;
+       uint32_t y = plane_state->main.y;
+       uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16;
 
        plane_ctl = PLANE_CTL_ENABLE |
                PLANE_CTL_PIPE_GAMMA_ENABLE |
@@ -229,14 +231,8 @@ skl_update_plane(struct drm_plane *drm_plane,
 
        plane_ctl |= skl_plane_ctl_rotation(rotation);
 
-       stride_div = intel_fb_stride_alignment(dev_priv, fb->modifier[0],
-                                              fb->pixel_format);
-
-       /* Sizes are 0 based */
-       src_w--;
-       src_h--;
-       crtc_w--;
-       crtc_h--;
+       if (wm->dirty_pipes & drm_crtc_mask(crtc))
+               skl_write_plane_wm(intel_crtc, wm, plane);
 
        if (key->flags) {
                I915_WRITE(PLANE_KEYVAL(pipe, plane), key->min_value);
@@ -249,28 +245,15 @@ skl_update_plane(struct drm_plane *drm_plane,
        else if (key->flags & I915_SET_COLORKEY_SOURCE)
                plane_ctl |= PLANE_CTL_KEY_ENABLE_SOURCE;
 
-       surf_addr = intel_plane_obj_offset(intel_plane, obj, 0);
-
-       if (intel_rotation_90_or_270(rotation)) {
-               int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
-
-               /* stride: Surface height in tiles */
-               tile_height = intel_tile_height(dev_priv, fb->modifier[0], cpp);
-               stride = DIV_ROUND_UP(fb->height, tile_height);
-               plane_size = (src_w << 16) | src_h;
-               x_offset = stride * tile_height - y - (src_h + 1);
-               y_offset = x;
-       } else {
-               stride = fb->pitches[0] / stride_div;
-               plane_size = (src_h << 16) | src_w;
-               x_offset = x;
-               y_offset = y;
-       }
-       plane_offset = y_offset << 16 | x_offset;
+       /* Sizes are 0 based */
+       src_w--;
+       src_h--;
+       crtc_w--;
+       crtc_h--;
 
-       I915_WRITE(PLANE_OFFSET(pipe, plane), plane_offset);
+       I915_WRITE(PLANE_OFFSET(pipe, plane), (y << 16) | x);
        I915_WRITE(PLANE_STRIDE(pipe, plane), stride);
-       I915_WRITE(PLANE_SIZE(pipe, plane), plane_size);
+       I915_WRITE(PLANE_SIZE(pipe, plane), (src_h << 16) | src_w);
 
        /* program plane scaler */
        if (plane_state->scaler_id >= 0) {
@@ -295,7 +278,8 @@ skl_update_plane(struct drm_plane *drm_plane,
        }
 
        I915_WRITE(PLANE_CTL(pipe, plane), plane_ctl);
-       I915_WRITE(PLANE_SURF(pipe, plane), surf_addr);
+       I915_WRITE(PLANE_SURF(pipe, plane),
+                  intel_fb_gtt_offset(fb, rotation) + surf_addr);
        POSTING_READ(PLANE_SURF(pipe, plane));
 }
 
@@ -308,6 +292,14 @@ skl_disable_plane(struct drm_plane *dplane, struct drm_crtc *crtc)
        const int pipe = intel_plane->pipe;
        const int plane = intel_plane->plane + 1;
 
+       /*
+        * We only populate skl_results on watermark updates, and if the
+        * plane's visiblity isn't actually changing neither is its watermarks.
+        */
+       if (!dplane->state->visible)
+               skl_write_plane_wm(to_intel_crtc(crtc),
+                                  &dev_priv->wm.skl_results, plane);
+
        I915_WRITE(PLANE_CTL(pipe, plane), 0);
 
        I915_WRITE(PLANE_SURF(pipe, plane), 0);
@@ -362,22 +354,20 @@ vlv_update_plane(struct drm_plane *dplane,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_plane *intel_plane = to_intel_plane(dplane);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        int pipe = intel_plane->pipe;
        int plane = intel_plane->plane;
        u32 sprctl;
        u32 sprsurf_offset, linear_offset;
        unsigned int rotation = dplane->state->rotation;
-       int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-       int crtc_x = plane_state->dst.x1;
-       int crtc_y = plane_state->dst.y1;
-       uint32_t crtc_w = drm_rect_width(&plane_state->dst);
-       uint32_t crtc_h = drm_rect_height(&plane_state->dst);
-       uint32_t x = plane_state->src.x1 >> 16;
-       uint32_t y = plane_state->src.y1 >> 16;
-       uint32_t src_w = drm_rect_width(&plane_state->src) >> 16;
-       uint32_t src_h = drm_rect_height(&plane_state->src) >> 16;
+       int crtc_x = plane_state->base.dst.x1;
+       int crtc_y = plane_state->base.dst.y1;
+       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
+       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
+       uint32_t x = plane_state->base.src.x1 >> 16;
+       uint32_t y = plane_state->base.src.y1 >> 16;
+       uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16;
 
        sprctl = SP_ENABLE;
 
@@ -430,7 +420,7 @@ vlv_update_plane(struct drm_plane *dplane,
         */
        sprctl |= SP_GAMMA_ENABLE;
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                sprctl |= SP_TILED;
 
        /* Sizes are 0 based */
@@ -439,19 +429,18 @@ vlv_update_plane(struct drm_plane *dplane,
        crtc_w--;
        crtc_h--;
 
-       linear_offset = y * fb->pitches[0] + x * cpp;
-       sprsurf_offset = intel_compute_tile_offset(&x, &y, fb, 0,
-                                                  fb->pitches[0], rotation);
-       linear_offset -= sprsurf_offset;
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+       sprsurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0);
 
-       if (rotation == BIT(DRM_ROTATE_180)) {
+       if (rotation == DRM_ROTATE_180) {
                sprctl |= SP_ROTATE_180;
 
                x += src_w;
                y += src_h;
-               linear_offset += src_h * fb->pitches[0] + src_w * cpp;
        }
 
+       linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
+
        if (key->flags) {
                I915_WRITE(SPKEYMINVAL(pipe, plane), key->min_value);
                I915_WRITE(SPKEYMAXVAL(pipe, plane), key->max_value);
@@ -467,7 +456,7 @@ vlv_update_plane(struct drm_plane *dplane,
        I915_WRITE(SPSTRIDE(pipe, plane), fb->pitches[0]);
        I915_WRITE(SPPOS(pipe, plane), (crtc_y << 16) | crtc_x);
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                I915_WRITE(SPTILEOFF(pipe, plane), (y << 16) | x);
        else
                I915_WRITE(SPLINOFF(pipe, plane), linear_offset);
@@ -476,8 +465,8 @@ vlv_update_plane(struct drm_plane *dplane,
 
        I915_WRITE(SPSIZE(pipe, plane), (crtc_h << 16) | crtc_w);
        I915_WRITE(SPCNTR(pipe, plane), sprctl);
-       I915_WRITE(SPSURF(pipe, plane), i915_gem_obj_ggtt_offset(obj) +
-                  sprsurf_offset);
+       I915_WRITE(SPSURF(pipe, plane),
+                  intel_fb_gtt_offset(fb, rotation) + sprsurf_offset);
        POSTING_READ(SPSURF(pipe, plane));
 }
 
@@ -505,21 +494,19 @@ ivb_update_plane(struct drm_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_plane *intel_plane = to_intel_plane(plane);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        enum pipe pipe = intel_plane->pipe;
        u32 sprctl, sprscale = 0;
        u32 sprsurf_offset, linear_offset;
        unsigned int rotation = plane_state->base.rotation;
-       int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-       int crtc_x = plane_state->dst.x1;
-       int crtc_y = plane_state->dst.y1;
-       uint32_t crtc_w = drm_rect_width(&plane_state->dst);
-       uint32_t crtc_h = drm_rect_height(&plane_state->dst);
-       uint32_t x = plane_state->src.x1 >> 16;
-       uint32_t y = plane_state->src.y1 >> 16;
-       uint32_t src_w = drm_rect_width(&plane_state->src) >> 16;
-       uint32_t src_h = drm_rect_height(&plane_state->src) >> 16;
+       int crtc_x = plane_state->base.dst.x1;
+       int crtc_y = plane_state->base.dst.y1;
+       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
+       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
+       uint32_t x = plane_state->base.src.x1 >> 16;
+       uint32_t y = plane_state->base.src.y1 >> 16;
+       uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16;
 
        sprctl = SPRITE_ENABLE;
 
@@ -552,7 +539,7 @@ ivb_update_plane(struct drm_plane *plane,
         */
        sprctl |= SPRITE_GAMMA_ENABLE;
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                sprctl |= SPRITE_TILED;
 
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
@@ -572,22 +559,21 @@ ivb_update_plane(struct drm_plane *plane,
        if (crtc_w != src_w || crtc_h != src_h)
                sprscale = SPRITE_SCALE_ENABLE | (src_w << 16) | src_h;
 
-       linear_offset = y * fb->pitches[0] + x * cpp;
-       sprsurf_offset = intel_compute_tile_offset(&x, &y, fb, 0,
-                                                  fb->pitches[0], rotation);
-       linear_offset -= sprsurf_offset;
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+       sprsurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0);
 
-       if (rotation == BIT(DRM_ROTATE_180)) {
+       if (rotation == DRM_ROTATE_180) {
                sprctl |= SPRITE_ROTATE_180;
 
                /* HSW and BDW does this automagically in hardware */
                if (!IS_HASWELL(dev) && !IS_BROADWELL(dev)) {
                        x += src_w;
                        y += src_h;
-                       linear_offset += src_h * fb->pitches[0] + src_w * cpp;
                }
        }
 
+       linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
+
        if (key->flags) {
                I915_WRITE(SPRKEYVAL(pipe), key->min_value);
                I915_WRITE(SPRKEYMAX(pipe), key->max_value);
@@ -606,7 +592,7 @@ ivb_update_plane(struct drm_plane *plane,
         * register */
        if (IS_HASWELL(dev) || IS_BROADWELL(dev))
                I915_WRITE(SPROFFSET(pipe), (y << 16) | x);
-       else if (obj->tiling_mode != I915_TILING_NONE)
+       else if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                I915_WRITE(SPRTILEOFF(pipe), (y << 16) | x);
        else
                I915_WRITE(SPRLINOFF(pipe), linear_offset);
@@ -616,7 +602,7 @@ ivb_update_plane(struct drm_plane *plane,
                I915_WRITE(SPRSCALE(pipe), sprscale);
        I915_WRITE(SPRCTL(pipe), sprctl);
        I915_WRITE(SPRSURF(pipe),
-                  i915_gem_obj_ggtt_offset(obj) + sprsurf_offset);
+                  intel_fb_gtt_offset(fb, rotation) + sprsurf_offset);
        POSTING_READ(SPRSURF(pipe));
 }
 
@@ -646,21 +632,19 @@ ilk_update_plane(struct drm_plane *plane,
        struct drm_i915_private *dev_priv = to_i915(dev);
        struct intel_plane *intel_plane = to_intel_plane(plane);
        struct drm_framebuffer *fb = plane_state->base.fb;
-       struct drm_i915_gem_object *obj = intel_fb_obj(fb);
        int pipe = intel_plane->pipe;
        u32 dvscntr, dvsscale;
        u32 dvssurf_offset, linear_offset;
        unsigned int rotation = plane_state->base.rotation;
-       int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
        const struct drm_intel_sprite_colorkey *key = &plane_state->ckey;
-       int crtc_x = plane_state->dst.x1;
-       int crtc_y = plane_state->dst.y1;
-       uint32_t crtc_w = drm_rect_width(&plane_state->dst);
-       uint32_t crtc_h = drm_rect_height(&plane_state->dst);
-       uint32_t x = plane_state->src.x1 >> 16;
-       uint32_t y = plane_state->src.y1 >> 16;
-       uint32_t src_w = drm_rect_width(&plane_state->src) >> 16;
-       uint32_t src_h = drm_rect_height(&plane_state->src) >> 16;
+       int crtc_x = plane_state->base.dst.x1;
+       int crtc_y = plane_state->base.dst.y1;
+       uint32_t crtc_w = drm_rect_width(&plane_state->base.dst);
+       uint32_t crtc_h = drm_rect_height(&plane_state->base.dst);
+       uint32_t x = plane_state->base.src.x1 >> 16;
+       uint32_t y = plane_state->base.src.y1 >> 16;
+       uint32_t src_w = drm_rect_width(&plane_state->base.src) >> 16;
+       uint32_t src_h = drm_rect_height(&plane_state->base.src) >> 16;
 
        dvscntr = DVS_ENABLE;
 
@@ -693,7 +677,7 @@ ilk_update_plane(struct drm_plane *plane,
         */
        dvscntr |= DVS_GAMMA_ENABLE;
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                dvscntr |= DVS_TILED;
 
        if (IS_GEN6(dev))
@@ -709,19 +693,18 @@ ilk_update_plane(struct drm_plane *plane,
        if (crtc_w != src_w || crtc_h != src_h)
                dvsscale = DVS_SCALE_ENABLE | (src_w << 16) | src_h;
 
-       linear_offset = y * fb->pitches[0] + x * cpp;
-       dvssurf_offset = intel_compute_tile_offset(&x, &y, fb, 0,
-                                                  fb->pitches[0], rotation);
-       linear_offset -= dvssurf_offset;
+       intel_add_fb_offsets(&x, &y, plane_state, 0);
+       dvssurf_offset = intel_compute_tile_offset(&x, &y, plane_state, 0);
 
-       if (rotation == BIT(DRM_ROTATE_180)) {
+       if (rotation == DRM_ROTATE_180) {
                dvscntr |= DVS_ROTATE_180;
 
                x += src_w;
                y += src_h;
-               linear_offset += src_h * fb->pitches[0] + src_w * cpp;
        }
 
+       linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
+
        if (key->flags) {
                I915_WRITE(DVSKEYVAL(pipe), key->min_value);
                I915_WRITE(DVSKEYMAX(pipe), key->max_value);
@@ -736,7 +719,7 @@ ilk_update_plane(struct drm_plane *plane,
        I915_WRITE(DVSSTRIDE(pipe), fb->pitches[0]);
        I915_WRITE(DVSPOS(pipe), (crtc_y << 16) | crtc_x);
 
-       if (obj->tiling_mode != I915_TILING_NONE)
+       if (fb->modifier[0] == I915_FORMAT_MOD_X_TILED)
                I915_WRITE(DVSTILEOFF(pipe), (y << 16) | x);
        else
                I915_WRITE(DVSLINOFF(pipe), linear_offset);
@@ -745,7 +728,7 @@ ilk_update_plane(struct drm_plane *plane,
        I915_WRITE(DVSSCALE(pipe), dvsscale);
        I915_WRITE(DVSCNTR(pipe), dvscntr);
        I915_WRITE(DVSSURF(pipe),
-                  i915_gem_obj_ggtt_offset(obj) + dvssurf_offset);
+                  intel_fb_gtt_offset(fb, rotation) + dvssurf_offset);
        POSTING_READ(DVSSURF(pipe));
 }
 
@@ -778,15 +761,26 @@ intel_check_sprite_plane(struct drm_plane *plane,
        int crtc_x, crtc_y;
        unsigned int crtc_w, crtc_h;
        uint32_t src_x, src_y, src_w, src_h;
-       struct drm_rect *src = &state->src;
-       struct drm_rect *dst = &state->dst;
+       struct drm_rect *src = &state->base.src;
+       struct drm_rect *dst = &state->base.dst;
        const struct drm_rect *clip = &state->clip;
        int hscale, vscale;
        int max_scale, min_scale;
        bool can_scale;
+       int ret;
+
+       src->x1 = state->base.src_x;
+       src->y1 = state->base.src_y;
+       src->x2 = state->base.src_x + state->base.src_w;
+       src->y2 = state->base.src_y + state->base.src_h;
+
+       dst->x1 = state->base.crtc_x;
+       dst->y1 = state->base.crtc_y;
+       dst->x2 = state->base.crtc_x + state->base.crtc_w;
+       dst->y2 = state->base.crtc_y + state->base.crtc_h;
 
        if (!fb) {
-               state->visible = false;
+               state->base.visible = false;
                return 0;
        }
 
@@ -834,14 +828,14 @@ intel_check_sprite_plane(struct drm_plane *plane,
        vscale = drm_rect_calc_vscale_relaxed(src, dst, min_scale, max_scale);
        BUG_ON(vscale < 0);
 
-       state->visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
+       state->base.visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
 
        crtc_x = dst->x1;
        crtc_y = dst->y1;
        crtc_w = drm_rect_width(dst);
        crtc_h = drm_rect_height(dst);
 
-       if (state->visible) {
+       if (state->base.visible) {
                /* check again in case clipping clamped the results */
                hscale = drm_rect_calc_hscale(src, dst, min_scale, max_scale);
                if (hscale < 0) {
@@ -898,12 +892,12 @@ intel_check_sprite_plane(struct drm_plane *plane,
                                crtc_w &= ~1;
 
                        if (crtc_w == 0)
-                               state->visible = false;
+                               state->base.visible = false;
                }
        }
 
        /* Check size restrictions when scaling */
-       if (state->visible && (src_w != crtc_w || src_h != crtc_h)) {
+       if (state->base.visible && (src_w != crtc_w || src_h != crtc_h)) {
                unsigned int width_bytes;
                int cpp = drm_format_plane_cpp(fb->pixel_format, 0);
 
@@ -912,10 +906,10 @@ intel_check_sprite_plane(struct drm_plane *plane,
                /* FIXME interlacing min height is 6 */
 
                if (crtc_w < 3 || crtc_h < 3)
-                       state->visible = false;
+                       state->base.visible = false;
 
                if (src_w < 3 || src_h < 3)
-                       state->visible = false;
+                       state->base.visible = false;
 
                width_bytes = ((src_x * cpp) & 63) + src_w * cpp;
 
@@ -926,7 +920,7 @@ intel_check_sprite_plane(struct drm_plane *plane,
                }
        }
 
-       if (state->visible) {
+       if (state->base.visible) {
                src->x1 = src_x << 16;
                src->x2 = (src_x + src_w) << 16;
                src->y1 = src_y << 16;
@@ -938,6 +932,12 @@ intel_check_sprite_plane(struct drm_plane *plane,
        dst->y1 = crtc_y;
        dst->y2 = crtc_y + crtc_h;
 
+       if (INTEL_GEN(dev) >= 9) {
+               ret = skl_check_plane_surface(state);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
index 49136ad..d960e48 100644 (file)
@@ -838,7 +838,9 @@ intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
 }
 
 static void
-intel_enable_tv(struct intel_encoder *encoder)
+intel_enable_tv(struct intel_encoder *encoder,
+               struct intel_crtc_state *pipe_config,
+               struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -851,7 +853,9 @@ intel_enable_tv(struct intel_encoder *encoder)
 }
 
 static void
-intel_disable_tv(struct intel_encoder *encoder)
+intel_disable_tv(struct intel_encoder *encoder,
+                struct intel_crtc_state *old_crtc_state,
+                struct drm_connector_state *old_conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
@@ -908,7 +912,8 @@ intel_tv_get_config(struct intel_encoder *encoder,
 
 static bool
 intel_tv_compute_config(struct intel_encoder *encoder,
-                       struct intel_crtc_state *pipe_config)
+                       struct intel_crtc_state *pipe_config,
+                       struct drm_connector_state *conn_state)
 {
        struct intel_tv *intel_tv = enc_to_tv(encoder);
        const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
@@ -1010,7 +1015,9 @@ static void set_color_conversion(struct drm_i915_private *dev_priv,
                   color_conversion->av);
 }
 
-static void intel_tv_pre_enable(struct intel_encoder *encoder)
+static void intel_tv_pre_enable(struct intel_encoder *encoder,
+                               struct intel_crtc_state *pipe_config,
+                               struct drm_connector_state *conn_state)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = to_i915(dev);
index ff80a81..ee2306a 100644 (file)
@@ -435,7 +435,7 @@ void intel_uncore_sanitize(struct drm_i915_private *dev_priv)
        i915.enable_rc6 = sanitize_rc6_option(dev_priv, i915.enable_rc6);
 
        /* BIOS often leaves RC6 enabled, but disable it for hw init */
-       intel_disable_gt_powersave(dev_priv);
+       intel_sanitize_gt_powersave(dev_priv);
 }
 
 static void __intel_uncore_forcewake_get(struct drm_i915_private *dev_priv,
@@ -796,10 +796,9 @@ __unclaimed_reg_debug(struct drm_i915_private *dev_priv,
                      const bool read,
                      const bool before)
 {
-       if (WARN(check_for_unclaimed_mmio(dev_priv),
-                "Unclaimed register detected %s %s register 0x%x\n",
-                before ? "before" : "after",
-                read ? "reading" : "writing to",
+       if (WARN(check_for_unclaimed_mmio(dev_priv) && !before,
+                "Unclaimed %s register 0x%x\n",
+                read ? "read from" : "write to",
                 i915_mmio_reg_offset(reg)))
                i915.mmio_debug--; /* Only report the first N failures */
 }
@@ -1018,11 +1017,9 @@ gen5_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, bool
 __gen5_write(8)
 __gen5_write(16)
 __gen5_write(32)
-__gen5_write(64)
 __gen2_write(8)
 __gen2_write(16)
 __gen2_write(32)
-__gen2_write(64)
 
 #undef __gen5_write
 #undef __gen2_write
@@ -1112,23 +1109,18 @@ gen9_write##x(struct drm_i915_private *dev_priv, i915_reg_t reg, u##x val, \
 __gen9_write(8)
 __gen9_write(16)
 __gen9_write(32)
-__gen9_write(64)
 __chv_write(8)
 __chv_write(16)
 __chv_write(32)
-__chv_write(64)
 __gen8_write(8)
 __gen8_write(16)
 __gen8_write(32)
-__gen8_write(64)
 __hsw_write(8)
 __hsw_write(16)
 __hsw_write(32)
-__hsw_write(64)
 __gen6_write(8)
 __gen6_write(16)
 __gen6_write(32)
-__gen6_write(64)
 
 #undef __gen9_write
 #undef __chv_write
@@ -1158,7 +1150,6 @@ static void vgpu_write##x(struct drm_i915_private *dev_priv, \
 __vgpu_write(8)
 __vgpu_write(16)
 __vgpu_write(32)
-__vgpu_write(64)
 
 #undef __vgpu_write
 #undef VGPU_WRITE_FOOTER
@@ -1169,7 +1160,6 @@ do { \
        dev_priv->uncore.funcs.mmio_writeb = x##_write8; \
        dev_priv->uncore.funcs.mmio_writew = x##_write16; \
        dev_priv->uncore.funcs.mmio_writel = x##_write32; \
-       dev_priv->uncore.funcs.mmio_writeq = x##_write64; \
 } while (0)
 
 #define ASSIGN_READ_MMIO_VFUNCS(x) \
@@ -1597,8 +1587,10 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv,
        if (engine_mask == ALL_ENGINES) {
                hw_mask = GEN6_GRDOM_FULL;
        } else {
+               unsigned int tmp;
+
                hw_mask = 0;
-               for_each_engine_masked(engine, dev_priv, engine_mask)
+               for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
                        hw_mask |= hw_engine_mask[engine->id];
        }
 
@@ -1618,8 +1610,10 @@ static int gen6_reset_engines(struct drm_i915_private *dev_priv,
  * @timeout_ms: timeout in millisecond
  *
  * This routine waits until the target register @reg contains the expected
- * @value after applying the @mask, i.e. it waits until
- *   (I915_READ_FW(@reg) & @mask) == @value
+ * @value after applying the @mask, i.e. it waits until ::
+ *
+ *     (I915_READ_FW(reg) & mask) == value
+ *
  * Otherwise, the wait will timeout after @timeout_ms milliseconds.
  *
  * Note that this routine assumes the caller holds forcewake asserted, it is
@@ -1652,8 +1646,10 @@ int intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
  * @timeout_ms: timeout in millisecond
  *
  * This routine waits until the target register @reg contains the expected
- * @value after applying the @mask, i.e. it waits until
- *   (I915_READ(@reg) & @mask) == @value
+ * @value after applying the @mask, i.e. it waits until ::
+ *
+ *     (I915_READ(reg) & mask) == value
+ *
  * Otherwise, the wait will timeout after @timeout_ms milliseconds.
  *
  * Returns 0 if the register matches the desired condition, or -ETIMEOUT.
@@ -1710,15 +1706,16 @@ static int gen8_reset_engines(struct drm_i915_private *dev_priv,
                              unsigned engine_mask)
 {
        struct intel_engine_cs *engine;
+       unsigned int tmp;
 
-       for_each_engine_masked(engine, dev_priv, engine_mask)
+       for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
                if (gen8_request_engine_reset(engine))
                        goto not_ready;
 
        return gen6_reset_engines(dev_priv, engine_mask);
 
 not_ready:
-       for_each_engine_masked(engine, dev_priv, engine_mask)
+       for_each_engine_masked(engine, dev_priv, engine_mask, tmp)
                gen8_unrequest_engine_reset(engine);
 
        return -EIO;
index 7bf90e9..98df09c 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/component.h>
 #include <linux/device.h>
 #include <linux/dma-buf.h>
-#include <linux/fb.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/reservation.h>
@@ -58,12 +57,6 @@ static int legacyfb_depth = 16;
 module_param(legacyfb_depth, int, 0444);
 #endif
 
-unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc)
-{
-       return drm_crtc_index(crtc->crtc);
-}
-EXPORT_SYMBOL_GPL(imx_drm_crtc_id);
-
 static void imx_drm_driver_lastclose(struct drm_device *drm)
 {
        struct imx_drm_device *imxdrm = drm->dev_private;
@@ -71,43 +64,6 @@ static void imx_drm_driver_lastclose(struct drm_device *drm)
        drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
 }
 
-static int imx_drm_driver_unload(struct drm_device *drm)
-{
-       struct imx_drm_device *imxdrm = drm->dev_private;
-
-       drm_kms_helper_poll_fini(drm);
-
-       if (imxdrm->fbhelper)
-               drm_fbdev_cma_fini(imxdrm->fbhelper);
-
-       component_unbind_all(drm->dev, drm);
-
-       drm_vblank_cleanup(drm);
-       drm_mode_config_cleanup(drm);
-
-       platform_set_drvdata(drm->platformdev, NULL);
-
-       return 0;
-}
-
-int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
-{
-       return drm_crtc_vblank_get(imx_drm_crtc->crtc);
-}
-EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get);
-
-void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc)
-{
-       drm_crtc_vblank_put(imx_drm_crtc->crtc);
-}
-EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put);
-
-void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc)
-{
-       drm_crtc_handle_vblank(imx_drm_crtc->crtc);
-}
-EXPORT_SYMBOL_GPL(imx_drm_handle_vblank);
-
 static int imx_drm_enable_vblank(struct drm_device *drm, unsigned int pipe)
 {
        struct imx_drm_device *imxdrm = drm->dev_private;
@@ -195,54 +151,49 @@ static int imx_drm_atomic_check(struct drm_device *dev,
        return ret;
 }
 
+static int imx_drm_atomic_commit(struct drm_device *dev,
+                                struct drm_atomic_state *state,
+                                bool nonblock)
+{
+       struct drm_plane_state *plane_state;
+       struct drm_plane *plane;
+       struct dma_buf *dma_buf;
+       int i;
+
+       /*
+        * If the plane fb has an dma-buf attached, fish out the exclusive
+        * fence for the atomic helper to wait on.
+        */
+       for_each_plane_in_state(state, plane, plane_state, i) {
+               if ((plane->state->fb != plane_state->fb) && plane_state->fb) {
+                       dma_buf = drm_fb_cma_get_gem_obj(plane_state->fb,
+                                                        0)->base.dma_buf;
+                       if (!dma_buf)
+                               continue;
+                       plane_state->fence =
+                               reservation_object_get_excl_rcu(dma_buf->resv);
+               }
+       }
+
+       return drm_atomic_helper_commit(dev, state, nonblock);
+}
+
 static const struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
        .fb_create = drm_fb_cma_create,
        .output_poll_changed = imx_drm_output_poll_changed,
        .atomic_check = imx_drm_atomic_check,
-       .atomic_commit = drm_atomic_helper_commit,
+       .atomic_commit = imx_drm_atomic_commit,
 };
 
 static void imx_drm_atomic_commit_tail(struct drm_atomic_state *state)
 {
        struct drm_device *dev = state->dev;
-       struct drm_crtc *crtc;
-       struct drm_crtc_state *crtc_state;
-       struct drm_plane_state *plane_state;
-       struct drm_gem_cma_object *cma_obj;
-       struct fence *excl;
-       unsigned shared_count;
-       struct fence **shared;
-       unsigned int i, j;
-       int ret;
-
-       /* Wait for fences. */
-       for_each_crtc_in_state(state, crtc, crtc_state, i) {
-               plane_state = crtc->primary->state;
-               if (plane_state->fb) {
-                       cma_obj = drm_fb_cma_get_gem_obj(plane_state->fb, 0);
-                       if (cma_obj->base.dma_buf) {
-                               ret = reservation_object_get_fences_rcu(
-                                       cma_obj->base.dma_buf->resv, &excl,
-                                       &shared_count, &shared);
-                               if (unlikely(ret))
-                                       DRM_ERROR("failed to get fences "
-                                                 "for buffer\n");
-
-                               if (excl) {
-                                       fence_wait(excl, false);
-                                       fence_put(excl);
-                               }
-                               for (j = 0; j < shared_count; i++) {
-                                       fence_wait(shared[j], false);
-                                       fence_put(shared[j]);
-                               }
-                       }
-               }
-       }
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state, true);
+       drm_atomic_helper_commit_planes(dev, state,
+                               DRM_PLANE_COMMIT_ACTIVE_ONLY |
+                               DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
@@ -257,111 +208,6 @@ static struct drm_mode_config_helper_funcs imx_drm_mode_config_helpers = {
        .atomic_commit_tail = imx_drm_atomic_commit_tail,
 };
 
-/*
- * Main DRM initialisation. This binds, initialises and registers
- * with DRM the subcomponents of the driver.
- */
-static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
-{
-       struct imx_drm_device *imxdrm;
-       struct drm_connector *connector;
-       int ret;
-
-       imxdrm = devm_kzalloc(drm->dev, sizeof(*imxdrm), GFP_KERNEL);
-       if (!imxdrm)
-               return -ENOMEM;
-
-       imxdrm->drm = drm;
-
-       drm->dev_private = imxdrm;
-
-       /*
-        * enable drm irq mode.
-        * - with irq_enabled = true, we can use the vblank feature.
-        *
-        * P.S. note that we wouldn't use drm irq handler but
-        *      just specific driver own one instead because
-        *      drm framework supports only one irq handler and
-        *      drivers can well take care of their interrupts
-        */
-       drm->irq_enabled = true;
-
-       /*
-        * set max width and height as default value(4096x4096).
-        * this value would be used to check framebuffer size limitation
-        * at drm_mode_addfb().
-        */
-       drm->mode_config.min_width = 64;
-       drm->mode_config.min_height = 64;
-       drm->mode_config.max_width = 4096;
-       drm->mode_config.max_height = 4096;
-       drm->mode_config.funcs = &imx_drm_mode_config_funcs;
-       drm->mode_config.helper_private = &imx_drm_mode_config_helpers;
-
-       drm_mode_config_init(drm);
-
-       ret = drm_vblank_init(drm, MAX_CRTC);
-       if (ret)
-               goto err_kms;
-
-       platform_set_drvdata(drm->platformdev, drm);
-
-       /* Now try and bind all our sub-components */
-       ret = component_bind_all(drm->dev, drm);
-       if (ret)
-               goto err_vblank;
-
-       /*
-        * All components are now added, we can publish the connector sysfs
-        * entries to userspace.  This will generate hotplug events and so
-        * userspace will expect to be able to access DRM at this point.
-        */
-       list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
-               ret = drm_connector_register(connector);
-               if (ret) {
-                       dev_err(drm->dev,
-                               "[CONNECTOR:%d:%s] drm_connector_register failed: %d\n",
-                               connector->base.id,
-                               connector->name, ret);
-                       goto err_unbind;
-               }
-       }
-
-       drm_mode_config_reset(drm);
-
-       /*
-        * All components are now initialised, so setup the fb helper.
-        * The fb helper takes copies of key hardware information, so the
-        * crtcs/connectors/encoders must not change after this point.
-        */
-#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
-       if (legacyfb_depth != 16 && legacyfb_depth != 32) {
-               dev_warn(drm->dev, "Invalid legacyfb_depth.  Defaulting to 16bpp\n");
-               legacyfb_depth = 16;
-       }
-       imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
-                               drm->mode_config.num_crtc, MAX_CRTC);
-       if (IS_ERR(imxdrm->fbhelper)) {
-               ret = PTR_ERR(imxdrm->fbhelper);
-               imxdrm->fbhelper = NULL;
-               goto err_unbind;
-       }
-#endif
-
-       drm_kms_helper_poll_init(drm);
-
-       return 0;
-
-err_unbind:
-       component_unbind_all(drm->dev, drm);
-err_vblank:
-       drm_vblank_cleanup(drm);
-err_kms:
-       drm_mode_config_cleanup(drm);
-
-       return ret;
-}
-
 /*
  * imx_drm_add_crtc - add a new crtc
  */
@@ -454,8 +300,6 @@ static const struct drm_ioctl_desc imx_drm_ioctls[] = {
 static struct drm_driver imx_drm_driver = {
        .driver_features        = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
                                  DRIVER_ATOMIC,
-       .load                   = imx_drm_driver_load,
-       .unload                 = imx_drm_driver_unload,
        .lastclose              = imx_drm_driver_lastclose,
        .gem_free_object_unlocked = drm_gem_cma_free_object,
        .gem_vm_ops             = &drm_gem_cma_vm_ops,
@@ -508,12 +352,122 @@ static int compare_of(struct device *dev, void *data)
 
 static int imx_drm_bind(struct device *dev)
 {
-       return drm_platform_init(&imx_drm_driver, to_platform_device(dev));
+       struct drm_device *drm;
+       struct imx_drm_device *imxdrm;
+       int ret;
+
+       drm = drm_dev_alloc(&imx_drm_driver, dev);
+       if (!drm)
+               return -ENOMEM;
+
+       imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL);
+       if (!imxdrm) {
+               ret = -ENOMEM;
+               goto err_unref;
+       }
+
+       imxdrm->drm = drm;
+       drm->dev_private = imxdrm;
+
+       /*
+        * enable drm irq mode.
+        * - with irq_enabled = true, we can use the vblank feature.
+        *
+        * P.S. note that we wouldn't use drm irq handler but
+        *      just specific driver own one instead because
+        *      drm framework supports only one irq handler and
+        *      drivers can well take care of their interrupts
+        */
+       drm->irq_enabled = true;
+
+       /*
+        * set max width and height as default value(4096x4096).
+        * this value would be used to check framebuffer size limitation
+        * at drm_mode_addfb().
+        */
+       drm->mode_config.min_width = 64;
+       drm->mode_config.min_height = 64;
+       drm->mode_config.max_width = 4096;
+       drm->mode_config.max_height = 4096;
+       drm->mode_config.funcs = &imx_drm_mode_config_funcs;
+       drm->mode_config.helper_private = &imx_drm_mode_config_helpers;
+
+       drm_mode_config_init(drm);
+
+       ret = drm_vblank_init(drm, MAX_CRTC);
+       if (ret)
+               goto err_kms;
+
+       dev_set_drvdata(dev, drm);
+
+       /* Now try and bind all our sub-components */
+       ret = component_bind_all(dev, drm);
+       if (ret)
+               goto err_vblank;
+
+       drm_mode_config_reset(drm);
+
+       /*
+        * All components are now initialised, so setup the fb helper.
+        * The fb helper takes copies of key hardware information, so the
+        * crtcs/connectors/encoders must not change after this point.
+        */
+#if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION)
+       if (legacyfb_depth != 16 && legacyfb_depth != 32) {
+               dev_warn(dev, "Invalid legacyfb_depth.  Defaulting to 16bpp\n");
+               legacyfb_depth = 16;
+       }
+       imxdrm->fbhelper = drm_fbdev_cma_init(drm, legacyfb_depth,
+                               drm->mode_config.num_crtc, MAX_CRTC);
+       if (IS_ERR(imxdrm->fbhelper)) {
+               ret = PTR_ERR(imxdrm->fbhelper);
+               imxdrm->fbhelper = NULL;
+               goto err_unbind;
+       }
+#endif
+
+       drm_kms_helper_poll_init(drm);
+
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               goto err_fbhelper;
+
+       return 0;
+
+err_fbhelper:
+       drm_kms_helper_poll_fini(drm);
+       if (imxdrm->fbhelper)
+               drm_fbdev_cma_fini(imxdrm->fbhelper);
+err_unbind:
+       component_unbind_all(drm->dev, drm);
+err_vblank:
+       drm_vblank_cleanup(drm);
+err_kms:
+       drm_mode_config_cleanup(drm);
+err_unref:
+       drm_dev_unref(drm);
+
+       return ret;
 }
 
 static void imx_drm_unbind(struct device *dev)
 {
-       drm_put_dev(dev_get_drvdata(dev));
+       struct drm_device *drm = dev_get_drvdata(dev);
+       struct imx_drm_device *imxdrm = drm->dev_private;
+
+       drm_dev_unregister(drm);
+
+       drm_kms_helper_poll_fini(drm);
+
+       if (imxdrm->fbhelper)
+               drm_fbdev_cma_fini(imxdrm->fbhelper);
+
+       drm_mode_config_cleanup(drm);
+
+       component_unbind_all(drm->dev, drm);
+       dev_set_drvdata(dev, NULL);
+
+       drm_dev_unref(drm);
 }
 
 static const struct component_master_ops imx_drm_ops = {
index 07d33e4..5a91cb1 100644 (file)
@@ -13,8 +13,6 @@ struct drm_plane;
 struct imx_drm_crtc;
 struct platform_device;
 
-unsigned int imx_drm_crtc_id(struct imx_drm_crtc *crtc);
-
 struct imx_crtc_state {
        struct drm_crtc_state                   base;
        u32                                     bus_format;
@@ -44,10 +42,6 @@ int imx_drm_init_drm(struct platform_device *pdev,
                int preferred_bpp);
 int imx_drm_exit_drm(void);
 
-int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc);
-void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc);
-void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc);
-
 void imx_drm_mode_config_init(struct drm_device *drm);
 
 struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
index b03919e..3ce391c 100644 (file)
@@ -57,7 +57,11 @@ struct imx_ldb_channel {
        struct imx_ldb *ldb;
        struct drm_connector connector;
        struct drm_encoder encoder;
+
+       /* Defines what is connected to the ldb, only one at a time */
        struct drm_panel *panel;
+       struct drm_bridge *bridge;
+
        struct device_node *child;
        struct i2c_adapter *ddc;
        int chno;
@@ -66,6 +70,7 @@ struct imx_ldb_channel {
        struct drm_display_mode mode;
        int mode_valid;
        u32 bus_format;
+       u32 bus_flags;
 };
 
 static inline struct imx_ldb_channel *con_to_imx_ldb_ch(struct drm_connector *c)
@@ -251,11 +256,13 @@ static void imx_ldb_encoder_enable(struct drm_encoder *encoder)
        drm_panel_enable(imx_ldb_ch->panel);
 }
 
-static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
-                        struct drm_display_mode *orig_mode,
-                        struct drm_display_mode *mode)
+static void
+imx_ldb_encoder_atomic_mode_set(struct drm_encoder *encoder,
+                               struct drm_crtc_state *crtc_state,
+                               struct drm_connector_state *connector_state)
 {
        struct imx_ldb_channel *imx_ldb_ch = enc_to_imx_ldb_ch(encoder);
+       struct drm_display_mode *mode = &crtc_state->adjusted_mode;
        struct imx_ldb *ldb = imx_ldb_ch->ldb;
        int dual = ldb->ldb_ctrl & LDB_SPLIT_MODE_EN;
        unsigned long serial_clk;
@@ -297,17 +304,11 @@ static void imx_ldb_encoder_mode_set(struct drm_encoder *encoder,
        }
 
        if (!bus_format) {
-               struct drm_connector *connector;
-
-               drm_for_each_connector(connector, encoder->dev) {
-                       struct drm_display_info *di = &connector->display_info;
+               struct drm_connector *connector = connector_state->connector;
+               struct drm_display_info *di = &connector->display_info;
 
-                       if (connector->encoder == encoder &&
-                           di->num_bus_formats) {
-                               bus_format = di->bus_formats[0];
-                               break;
-                       }
-               }
+               if (di->num_bus_formats)
+                       bus_format = di->bus_formats[0];
        }
        imx_ldb_ch_set_bus_format(imx_ldb_ch, bus_format);
 }
@@ -379,8 +380,13 @@ static int imx_ldb_encoder_atomic_check(struct drm_encoder *encoder,
        u32 bus_format = imx_ldb_ch->bus_format;
 
        /* Bus format description in DT overrides connector display info. */
-       if (!bus_format && di->num_bus_formats)
+       if (!bus_format && di->num_bus_formats) {
                bus_format = di->bus_formats[0];
+               imx_crtc_state->bus_flags = di->bus_flags;
+       } else {
+               bus_format = imx_ldb_ch->bus_format;
+               imx_crtc_state->bus_flags = imx_ldb_ch->bus_flags;
+       }
        switch (bus_format) {
        case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
                imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB666_1X18;
@@ -420,7 +426,7 @@ static const struct drm_encoder_funcs imx_ldb_encoder_funcs = {
 };
 
 static const struct drm_encoder_helper_funcs imx_ldb_encoder_helper_funcs = {
-       .mode_set = imx_ldb_encoder_mode_set,
+       .atomic_mode_set = imx_ldb_encoder_atomic_mode_set,
        .enable = imx_ldb_encoder_enable,
        .disable = imx_ldb_encoder_disable,
        .atomic_check = imx_ldb_encoder_atomic_check,
@@ -466,10 +472,30 @@ static int imx_ldb_register(struct drm_device *drm,
        drm_encoder_init(drm, encoder, &imx_ldb_encoder_funcs,
                         DRM_MODE_ENCODER_LVDS, NULL);
 
-       drm_connector_helper_add(&imx_ldb_ch->connector,
-                       &imx_ldb_connector_helper_funcs);
-       drm_connector_init(drm, &imx_ldb_ch->connector,
-                          &imx_ldb_connector_funcs, DRM_MODE_CONNECTOR_LVDS);
+       if (imx_ldb_ch->bridge) {
+               imx_ldb_ch->bridge->encoder = encoder;
+
+               imx_ldb_ch->encoder.bridge = imx_ldb_ch->bridge;
+               ret = drm_bridge_attach(drm, imx_ldb_ch->bridge);
+               if (ret) {
+                       DRM_ERROR("Failed to initialize bridge with drm\n");
+                       return ret;
+               }
+       } else {
+               /*
+                * We want to add the connector whenever there is no bridge
+                * that brings its own, not only when there is a panel. For
+                * historical reasons, the ldb driver can also work without
+                * a panel.
+                */
+               drm_connector_helper_add(&imx_ldb_ch->connector,
+                               &imx_ldb_connector_helper_funcs);
+               drm_connector_init(drm, &imx_ldb_ch->connector,
+                               &imx_ldb_connector_funcs,
+                               DRM_MODE_CONNECTOR_LVDS);
+               drm_mode_connector_attach_encoder(&imx_ldb_ch->connector,
+                               encoder);
+       }
 
        if (imx_ldb_ch->panel) {
                ret = drm_panel_attach(imx_ldb_ch->panel,
@@ -478,8 +504,6 @@ static int imx_ldb_register(struct drm_device *drm,
                        return ret;
        }
 
-       drm_mode_connector_attach_encoder(&imx_ldb_ch->connector, encoder);
-
        return 0;
 }
 
@@ -548,6 +572,46 @@ static const struct of_device_id imx_ldb_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_ldb_dt_ids);
 
+static int imx_ldb_panel_ddc(struct device *dev,
+               struct imx_ldb_channel *channel, struct device_node *child)
+{
+       struct device_node *ddc_node;
+       const u8 *edidp;
+       int ret;
+
+       ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0);
+       if (ddc_node) {
+               channel->ddc = of_find_i2c_adapter_by_node(ddc_node);
+               of_node_put(ddc_node);
+               if (!channel->ddc) {
+                       dev_warn(dev, "failed to get ddc i2c adapter\n");
+                       return -EPROBE_DEFER;
+               }
+       }
+
+       if (!channel->ddc) {
+               /* if no DDC available, fallback to hardcoded EDID */
+               dev_dbg(dev, "no ddc available\n");
+
+               edidp = of_get_property(child, "edid",
+                                       &channel->edid_len);
+               if (edidp) {
+                       channel->edid = kmemdup(edidp,
+                                               channel->edid_len,
+                                               GFP_KERNEL);
+               } else if (!channel->panel) {
+                       /* fallback to display-timings node */
+                       ret = of_get_drm_display_mode(child,
+                                                     &channel->mode,
+                                                     &channel->bus_flags,
+                                                     OF_USE_NATIVE_MODE);
+                       if (!ret)
+                               channel->mode_valid = 1;
+               }
+       }
+       return 0;
+}
+
 static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 {
        struct drm_device *drm = data;
@@ -555,7 +619,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
        const struct of_device_id *of_id =
                        of_match_device(imx_ldb_dt_ids, dev);
        struct device_node *child;
-       const u8 *edidp;
        struct imx_ldb *imx_ldb;
        int dual;
        int ret;
@@ -605,7 +668,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 
        for_each_child_of_node(np, child) {
                struct imx_ldb_channel *channel;
-               struct device_node *ddc_node;
                struct device_node *ep;
                int bus_format;
 
@@ -638,46 +700,25 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
 
                        remote = of_graph_get_remote_port_parent(ep);
                        of_node_put(ep);
-                       if (remote)
+                       if (remote) {
                                channel->panel = of_drm_find_panel(remote);
-                       else
+                               channel->bridge = of_drm_find_bridge(remote);
+                       } else
                                return -EPROBE_DEFER;
                        of_node_put(remote);
-                       if (!channel->panel) {
-                               dev_err(dev, "panel not found: %s\n",
+
+                       if (!channel->panel && !channel->bridge) {
+                               dev_err(dev, "panel/bridge not found: %s\n",
                                        remote->full_name);
                                return -EPROBE_DEFER;
                        }
                }
 
-               ddc_node = of_parse_phandle(child, "ddc-i2c-bus", 0);
-               if (ddc_node) {
-                       channel->ddc = of_find_i2c_adapter_by_node(ddc_node);
-                       of_node_put(ddc_node);
-                       if (!channel->ddc) {
-                               dev_warn(dev, "failed to get ddc i2c adapter\n");
-                               return -EPROBE_DEFER;
-                       }
-               }
-
-               if (!channel->ddc) {
-                       /* if no DDC available, fallback to hardcoded EDID */
-                       dev_dbg(dev, "no ddc available\n");
-
-                       edidp = of_get_property(child, "edid",
-                                               &channel->edid_len);
-                       if (edidp) {
-                               channel->edid = kmemdup(edidp,
-                                                       channel->edid_len,
-                                                       GFP_KERNEL);
-                       } else if (!channel->panel) {
-                               /* fallback to display-timings node */
-                               ret = of_get_drm_display_mode(child,
-                                                             &channel->mode,
-                                                             OF_USE_NATIVE_MODE);
-                               if (!ret)
-                                       channel->mode_valid = 1;
-                       }
+               /* panel ddc only if there is no bridge */
+               if (!channel->bridge) {
+                       ret = imx_ldb_panel_ddc(dev, channel, child);
+                       if (ret)
+                               return ret;
                }
 
                bus_format = of_get_bus_format(dev, child);
@@ -716,11 +757,10 @@ static void imx_ldb_unbind(struct device *dev, struct device *master,
        for (i = 0; i < 2; i++) {
                struct imx_ldb_channel *channel = &imx_ldb->channel[i];
 
-               if (!channel->connector.funcs)
-                       continue;
-
-               channel->connector.funcs->destroy(&channel->connector);
-               channel->encoder.funcs->destroy(&channel->encoder);
+               if (channel->bridge)
+                       drm_bridge_detach(channel->bridge);
+               if (channel->panel)
+                       drm_panel_detach(channel->panel);
 
                kfree(channel->edid);
                i2c_put_adapter(channel->ddc);
index 5e87594..8fc0888 100644 (file)
@@ -685,9 +685,6 @@ static void imx_tve_unbind(struct device *dev, struct device *master,
 {
        struct imx_tve *tve = dev_get_drvdata(dev);
 
-       tve->connector.funcs->destroy(&tve->connector);
-       tve->encoder.funcs->destroy(&tve->encoder);
-
        if (!IS_ERR(tve->dac_reg))
                regulator_disable(tve->dac_reg);
 }
index 462056e..4e1ae3f 100644 (file)
@@ -21,7 +21,6 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
-#include <linux/fb.h>
 #include <linux/clk.h>
 #include <linux/errno.h>
 #include <drm/drm_gem_cma_helper.h>
@@ -61,7 +60,8 @@ static void ipu_crtc_enable(struct drm_crtc *crtc)
        ipu_di_enable(ipu_crtc->di);
 }
 
-static void ipu_crtc_disable(struct drm_crtc *crtc)
+static void ipu_crtc_atomic_disable(struct drm_crtc *crtc,
+                                   struct drm_crtc_state *old_crtc_state)
 {
        struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
        struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
@@ -77,6 +77,9 @@ static void ipu_crtc_disable(struct drm_crtc *crtc)
        }
        spin_unlock_irq(&crtc->dev->event_lock);
 
+       /* always disable planes on the CRTC */
+       drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, true);
+
        drm_crtc_vblank_off(crtc);
 }
 
@@ -123,9 +126,14 @@ static void imx_drm_crtc_destroy_state(struct drm_crtc *crtc,
        kfree(to_imx_crtc_state(state));
 }
 
+static void imx_drm_crtc_destroy(struct drm_crtc *crtc)
+{
+       imx_drm_remove_crtc(to_ipu_crtc(crtc)->imx_crtc);
+}
+
 static const struct drm_crtc_funcs ipu_crtc_funcs = {
        .set_config = drm_atomic_helper_set_config,
-       .destroy = drm_crtc_cleanup,
+       .destroy = imx_drm_crtc_destroy,
        .page_flip = drm_atomic_helper_page_flip,
        .reset = imx_drm_crtc_reset,
        .atomic_duplicate_state = imx_drm_crtc_duplicate_state,
@@ -136,7 +144,7 @@ static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
 {
        struct ipu_crtc *ipu_crtc = dev_id;
 
-       imx_drm_handle_vblank(ipu_crtc->imx_crtc);
+       drm_crtc_handle_vblank(&ipu_crtc->base);
 
        return IRQ_HANDLED;
 }
@@ -246,7 +254,7 @@ static const struct drm_crtc_helper_funcs ipu_helper_funcs = {
        .mode_set_nofb = ipu_crtc_mode_set_nofb,
        .atomic_check = ipu_crtc_atomic_check,
        .atomic_begin = ipu_crtc_atomic_begin,
-       .disable = ipu_crtc_disable,
+       .atomic_disable = ipu_crtc_atomic_disable,
        .enable = ipu_crtc_enable,
 };
 
@@ -414,8 +422,6 @@ static void ipu_drm_unbind(struct device *dev, struct device *master,
 {
        struct ipu_crtc *ipu_crtc = dev_get_drvdata(dev);
 
-       imx_drm_remove_crtc(ipu_crtc->imx_crtc);
-
        ipu_put_resources(ipu_crtc);
        if (ipu_crtc->plane[1])
                ipu_plane_put_resources(ipu_crtc->plane[1]);
index 29423e7..ce22d0a 100644 (file)
@@ -213,8 +213,12 @@ static void ipu_plane_enable(struct ipu_plane *ipu_plane)
                ipu_dp_enable_channel(ipu_plane->dp);
 }
 
-static void ipu_plane_disable(struct ipu_plane *ipu_plane)
+static int ipu_disable_plane(struct drm_plane *plane)
 {
+       struct ipu_plane *ipu_plane = to_ipu_plane(plane);
+
+       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
        ipu_idmac_wait_busy(ipu_plane->ipu_ch, 50);
 
        if (ipu_plane->dp)
@@ -223,15 +227,6 @@ static void ipu_plane_disable(struct ipu_plane *ipu_plane)
        ipu_dmfc_disable_channel(ipu_plane->dmfc);
        if (ipu_plane->dp)
                ipu_dp_disable(ipu_plane->ipu);
-}
-
-static int ipu_disable_plane(struct drm_plane *plane)
-{
-       struct ipu_plane *ipu_plane = to_ipu_plane(plane);
-
-       DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
-       ipu_plane_disable(ipu_plane);
 
        return 0;
 }
@@ -242,7 +237,6 @@ static void ipu_plane_destroy(struct drm_plane *plane)
 
        DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
 
-       ipu_disable_plane(plane);
        drm_plane_cleanup(plane);
        kfree(ipu_plane);
 }
@@ -320,8 +314,10 @@ static int ipu_plane_atomic_check(struct drm_plane *plane,
 
        /*
         * We support resizing active plane or changing its format by
-        * forcing CRTC mode change and disabling-enabling plane in plane's
-        * ->atomic_update callback.
+        * forcing CRTC mode change in plane's ->atomic_check callback
+        * and disabling all affected active planes in CRTC's ->atomic_disable
+        * callback.  The planes will be reenabled in plane's ->atomic_update
+        * callback.
         */
        if (old_fb && (state->src_w != old_state->src_w ||
                              state->src_h != old_state->src_h ||
@@ -395,12 +391,10 @@ static void ipu_plane_atomic_update(struct drm_plane *plane,
        if (old_state->fb) {
                struct drm_crtc_state *crtc_state = state->crtc->state;
 
-               if (!crtc_state->mode_changed) {
+               if (!drm_atomic_crtc_needs_modeset(crtc_state)) {
                        ipu_plane_atomic_set_base(ipu_plane, old_state);
                        return;
                }
-
-               ipu_disable_plane(plane);
        }
 
        switch (ipu_plane->dp_flow) {
index 1dad297..d796ada 100644 (file)
@@ -33,6 +33,7 @@ struct imx_parallel_display {
        void *edid;
        int edid_len;
        u32 bus_format;
+       u32 bus_flags;
        struct drm_display_mode mode;
        struct drm_panel *panel;
        struct drm_bridge *bridge;
@@ -80,6 +81,7 @@ static int imx_pd_connector_get_modes(struct drm_connector *connector)
                        return -EINVAL;
 
                ret = of_get_drm_display_mode(np, &imxpd->mode,
+                                             &imxpd->bus_flags,
                                              OF_USE_NATIVE_MODE);
                if (ret)
                        return ret;
@@ -125,11 +127,13 @@ static int imx_pd_encoder_atomic_check(struct drm_encoder *encoder,
        struct drm_display_info *di = &conn_state->connector->display_info;
        struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
 
-       imx_crtc_state->bus_flags = di->bus_flags;
-       if (!imxpd->bus_format && di->num_bus_formats)
+       if (!imxpd->bus_format && di->num_bus_formats) {
+               imx_crtc_state->bus_flags = di->bus_flags;
                imx_crtc_state->bus_format = di->bus_formats[0];
-       else
+       } else {
+               imx_crtc_state->bus_flags = imxpd->bus_flags;
                imx_crtc_state->bus_format = imxpd->bus_format;
+       }
        imx_crtc_state->di_hsync_pin = 2;
        imx_crtc_state->di_vsync_pin = 3;
 
@@ -289,8 +293,10 @@ static void imx_pd_unbind(struct device *dev, struct device *master,
 {
        struct imx_parallel_display *imxpd = dev_get_drvdata(dev);
 
-       imxpd->encoder.funcs->destroy(&imxpd->encoder);
-       imxpd->connector.funcs->destroy(&imxpd->connector);
+       if (imxpd->bridge)
+               drm_bridge_detach(imxpd->bridge);
+       if (imxpd->panel)
+               drm_panel_detach(imxpd->panel);
 
        kfree(imxpd->edid);
 }
index 8f62671..019b7ca 100644 (file)
@@ -103,7 +103,8 @@ static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
-                          unsigned int h, unsigned int vrefresh)
+                          unsigned int h, unsigned int vrefresh,
+                          unsigned int bpc)
 {
        if (w != 0 && h != 0)
                writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
index 5fb80cb..0df05f9 100644 (file)
@@ -106,7 +106,8 @@ static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
-                           unsigned int height, unsigned int vrefresh)
+                           unsigned int height, unsigned int vrefresh,
+                           unsigned int bpc)
 {
        unsigned int threshold;
        unsigned int reg;
index 24aa3ba..01a21dd 100644 (file)
@@ -31,7 +31,7 @@
  * struct mtk_drm_crtc - MediaTek specific crtc structure.
  * @base: crtc object.
  * @enabled: records whether crtc_enable succeeded
- * @planes: array of 4 mtk_drm_plane structures, one for each overlay plane
+ * @planes: array of 4 drm_plane structures, one for each overlay plane
  * @pending_planes: whether any plane has pending changes to be applied
  * @config_regs: memory mapped mmsys configuration register space
  * @mutex: handle to one of the ten disp_mutex streams
@@ -45,7 +45,7 @@ struct mtk_drm_crtc {
        bool                            pending_needs_vblank;
        struct drm_pending_vblank_event *event;
 
-       struct mtk_drm_plane            planes[OVL_LAYER_NR];
+       struct drm_plane                planes[OVL_LAYER_NR];
        bool                            pending_planes;
 
        void __iomem                    *config_regs;
@@ -112,8 +112,7 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
        struct mtk_crtc_state *state;
 
        if (crtc->state) {
-               if (crtc->state->mode_blob)
-                       drm_property_unreference_blob(crtc->state->mode_blob);
+               __drm_atomic_helper_crtc_destroy_state(crtc->state);
 
                state = to_mtk_crtc_state(crtc->state);
                memset(state, 0, sizeof(*state));
@@ -222,7 +221,9 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
 static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 {
        struct drm_crtc *crtc = &mtk_crtc->base;
-       unsigned int width, height, vrefresh;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       unsigned int width, height, vrefresh, bpc = MTK_MAX_BPC;
        int ret;
        int i;
 
@@ -234,6 +235,19 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
        height = crtc->state->adjusted_mode.vdisplay;
        vrefresh = crtc->state->adjusted_mode.vrefresh;
 
+       drm_for_each_encoder(encoder, crtc->dev) {
+               if (encoder->crtc != crtc)
+                       continue;
+
+               drm_for_each_connector(connector, crtc->dev) {
+                       if (connector->encoder != encoder)
+                               continue;
+                       if (connector->display_info.bpc != 0 &&
+                           bpc > connector->display_info.bpc)
+                               bpc = connector->display_info.bpc;
+               }
+       }
+
        ret = pm_runtime_get_sync(crtc->dev->dev);
        if (ret < 0) {
                DRM_ERROR("Failed to enable power domain: %d\n", ret);
@@ -266,13 +280,13 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
        for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
                struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 
-               mtk_ddp_comp_config(comp, width, height, vrefresh);
+               mtk_ddp_comp_config(comp, width, height, vrefresh, bpc);
                mtk_ddp_comp_start(comp);
        }
 
        /* Initially configure all planes */
        for (i = 0; i < OVL_LAYER_NR; i++) {
-               struct drm_plane *plane = &mtk_crtc->planes[i].base;
+               struct drm_plane *plane = &mtk_crtc->planes[i];
                struct mtk_plane_state *plane_state;
 
                plane_state = to_mtk_plane_state(plane->state);
@@ -351,7 +365,7 @@ static void mtk_drm_crtc_disable(struct drm_crtc *crtc)
 
        /* Set all pending plane state to disabled */
        for (i = 0; i < OVL_LAYER_NR; i++) {
-               struct drm_plane *plane = &mtk_crtc->planes[i].base;
+               struct drm_plane *plane = &mtk_crtc->planes[i];
                struct mtk_plane_state *plane_state;
 
                plane_state = to_mtk_plane_state(plane->state);
@@ -397,7 +411,7 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
        if (mtk_crtc->event)
                mtk_crtc->pending_needs_vblank = true;
        for (i = 0; i < OVL_LAYER_NR; i++) {
-               struct drm_plane *plane = &mtk_crtc->planes[i].base;
+               struct drm_plane *plane = &mtk_crtc->planes[i];
                struct mtk_plane_state *plane_state;
 
                plane_state = to_mtk_plane_state(plane->state);
@@ -409,6 +423,9 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
        }
        if (pending_planes)
                mtk_crtc->pending_planes = true;
+       if (crtc->state->color_mgmt_changed)
+               for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+                       mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], crtc->state);
 }
 
 static const struct drm_crtc_funcs mtk_crtc_funcs = {
@@ -418,6 +435,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
        .reset                  = mtk_drm_crtc_reset,
        .atomic_duplicate_state = mtk_drm_crtc_duplicate_state,
        .atomic_destroy_state   = mtk_drm_crtc_destroy_state,
+       .gamma_set              = drm_atomic_helper_legacy_gamma_set,
 };
 
 static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
@@ -464,14 +482,14 @@ void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
        if (state->pending_config) {
                mtk_ddp_comp_config(ovl, state->pending_width,
                                    state->pending_height,
-                                   state->pending_vrefresh);
+                                   state->pending_vrefresh, 0);
 
                state->pending_config = false;
        }
 
        if (mtk_crtc->pending_planes) {
                for (i = 0; i < OVL_LAYER_NR; i++) {
-                       struct drm_plane *plane = &mtk_crtc->planes[i].base;
+                       struct drm_plane *plane = &mtk_crtc->planes[i];
                        struct mtk_plane_state *plane_state;
 
                        plane_state = to_mtk_plane_state(plane->state);
@@ -559,16 +577,17 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
                                (zpos == 1) ? DRM_PLANE_TYPE_CURSOR :
                                                DRM_PLANE_TYPE_OVERLAY;
                ret = mtk_plane_init(drm_dev, &mtk_crtc->planes[zpos],
-                                    BIT(pipe), type, zpos);
+                                    BIT(pipe), type);
                if (ret)
                        goto unprepare;
        }
 
-       ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0].base,
-                               &mtk_crtc->planes[1].base, pipe);
+       ret = mtk_drm_crtc_init(drm_dev, mtk_crtc, &mtk_crtc->planes[0],
+                               &mtk_crtc->planes[1], pipe);
        if (ret < 0)
                goto unprepare;
-
+       drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
+       drm_crtc_enable_color_mgmt(&mtk_crtc->base, 0, false, MTK_LUT_SIZE);
        priv->crtc[pipe] = &mtk_crtc->base;
        priv->num_pipes++;
 
index 81e5566..a1550fa 100644 (file)
 #include "mtk_drm_plane.h"
 
 #define OVL_LAYER_NR   4
+#define MTK_LUT_SIZE   512
+#define MTK_MAX_BPC    10
+#define MTK_MIN_BPC    3
 
 int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
 void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
-void mtk_drm_crtc_check_flush(struct drm_crtc *crtc);
 void mtk_drm_crtc_commit(struct drm_crtc *crtc);
 void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl);
 int mtk_drm_crtc_create(struct drm_device *drm_dev,
index 3970fcf..df33b3c 100644 (file)
 #include "mtk_drm_drv.h"
 #include "mtk_drm_plane.h"
 #include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_crtc.h"
 
 #define DISP_OD_EN                             0x0000
 #define DISP_OD_INTEN                          0x0008
 #define DISP_OD_INTSTA                         0x000c
 #define DISP_OD_CFG                            0x0020
 #define DISP_OD_SIZE                           0x0030
+#define DISP_DITHER_5                          0x0114
+#define DISP_DITHER_7                          0x011c
+#define DISP_DITHER_15                         0x013c
+#define DISP_DITHER_16                         0x0140
 
 #define DISP_REG_UFO_START                     0x0000
 
 #define DISP_COLOR_WIDTH                       0x0c50
 #define DISP_COLOR_HEIGHT                      0x0c54
 
-#define        OD_RELAY_MODE           BIT(0)
+#define DISP_AAL_EN                            0x0000
+#define DISP_AAL_SIZE                          0x0030
 
-#define        UFO_BYPASS              BIT(2)
+#define DISP_GAMMA_EN                          0x0000
+#define DISP_GAMMA_CFG                         0x0020
+#define DISP_GAMMA_SIZE                                0x0030
+#define DISP_GAMMA_LUT                         0x0700
 
-#define        COLOR_BYPASS_ALL        BIT(7)
-#define        COLOR_SEQ_SEL           BIT(13)
+#define LUT_10BIT_MASK                         0x03ff
+
+#define COLOR_BYPASS_ALL                       BIT(7)
+#define COLOR_SEQ_SEL                          BIT(13)
+
+#define OD_RELAYMODE                           BIT(0)
+
+#define UFO_BYPASS                             BIT(2)
+
+#define AAL_EN                                 BIT(0)
+
+#define GAMMA_EN                               BIT(0)
+#define GAMMA_LUT_EN                           BIT(1)
+
+#define DISP_DITHERING                         BIT(2)
+#define DITHER_LSB_ERR_SHIFT_R(x)              (((x) & 0x7) << 28)
+#define DITHER_OVFLW_BIT_R(x)                  (((x) & 0x7) << 24)
+#define DITHER_ADD_LSHIFT_R(x)                 (((x) & 0x7) << 20)
+#define DITHER_ADD_RSHIFT_R(x)                 (((x) & 0x7) << 16)
+#define DITHER_NEW_BIT_MODE                    BIT(0)
+#define DITHER_LSB_ERR_SHIFT_B(x)              (((x) & 0x7) << 28)
+#define DITHER_OVFLW_BIT_B(x)                  (((x) & 0x7) << 24)
+#define DITHER_ADD_LSHIFT_B(x)                 (((x) & 0x7) << 20)
+#define DITHER_ADD_RSHIFT_B(x)                 (((x) & 0x7) << 16)
+#define DITHER_LSB_ERR_SHIFT_G(x)              (((x) & 0x7) << 12)
+#define DITHER_OVFLW_BIT_G(x)                  (((x) & 0x7) << 8)
+#define DITHER_ADD_LSHIFT_G(x)                 (((x) & 0x7) << 4)
+#define DITHER_ADD_RSHIFT_G(x)                 (((x) & 0x7) << 0)
+
+void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
+                   unsigned int CFG)
+{
+       /* If bpc equal to 0, the dithering function didn't be enabled */
+       if (bpc == 0)
+               return;
+
+       if (bpc >= MTK_MIN_BPC) {
+               writel(0, comp->regs + DISP_DITHER_5);
+               writel(0, comp->regs + DISP_DITHER_7);
+               writel(DITHER_LSB_ERR_SHIFT_R(MTK_MAX_BPC - bpc) |
+                      DITHER_ADD_LSHIFT_R(MTK_MAX_BPC - bpc) |
+                      DITHER_NEW_BIT_MODE,
+                      comp->regs + DISP_DITHER_15);
+               writel(DITHER_LSB_ERR_SHIFT_B(MTK_MAX_BPC - bpc) |
+                      DITHER_ADD_LSHIFT_B(MTK_MAX_BPC - bpc) |
+                      DITHER_LSB_ERR_SHIFT_G(MTK_MAX_BPC - bpc) |
+                      DITHER_ADD_LSHIFT_G(MTK_MAX_BPC - bpc),
+                      comp->regs + DISP_DITHER_16);
+               writel(DISP_DITHERING, comp->regs + CFG);
+       }
+}
 
 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
-                            unsigned int h, unsigned int vrefresh)
+                            unsigned int h, unsigned int vrefresh,
+                            unsigned int bpc)
 {
        writel(w, comp->regs + DISP_COLOR_WIDTH);
        writel(h, comp->regs + DISP_COLOR_HEIGHT);
@@ -60,14 +119,16 @@ static void mtk_color_start(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
-                         unsigned int h, unsigned int vrefresh)
+                         unsigned int h, unsigned int vrefresh,
+                         unsigned int bpc)
 {
        writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
+       writel(OD_RELAYMODE, comp->regs + OD_RELAYMODE);
+       mtk_dither_set(comp, bpc, DISP_OD_CFG);
 }
 
 static void mtk_od_start(struct mtk_ddp_comp *comp)
 {
-       writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
        writel(1, comp->regs + DISP_OD_EN);
 }
 
@@ -76,6 +137,78 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
        writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
 }
 
+static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
+                          unsigned int h, unsigned int vrefresh,
+                          unsigned int bpc)
+{
+       writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
+}
+
+static void mtk_aal_start(struct mtk_ddp_comp *comp)
+{
+       writel(AAL_EN, comp->regs + DISP_AAL_EN);
+}
+
+static void mtk_aal_stop(struct mtk_ddp_comp *comp)
+{
+       writel_relaxed(0x0, comp->regs + DISP_AAL_EN);
+}
+
+static void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
+                            unsigned int h, unsigned int vrefresh,
+                            unsigned int bpc)
+{
+       writel(h << 16 | w, comp->regs + DISP_GAMMA_SIZE);
+       mtk_dither_set(comp, bpc, DISP_GAMMA_CFG);
+}
+
+static void mtk_gamma_start(struct mtk_ddp_comp *comp)
+{
+       writel(GAMMA_EN, comp->regs  + DISP_GAMMA_EN);
+}
+
+static void mtk_gamma_stop(struct mtk_ddp_comp *comp)
+{
+       writel_relaxed(0x0, comp->regs  + DISP_GAMMA_EN);
+}
+
+static void mtk_gamma_set(struct mtk_ddp_comp *comp,
+                         struct drm_crtc_state *state)
+{
+       unsigned int i, reg;
+       struct drm_color_lut *lut;
+       void __iomem *lut_base;
+       u32 word;
+
+       if (state->gamma_lut) {
+               reg = readl(comp->regs + DISP_GAMMA_CFG);
+               reg = reg | GAMMA_LUT_EN;
+               writel(reg, comp->regs + DISP_GAMMA_CFG);
+               lut_base = comp->regs + DISP_GAMMA_LUT;
+               lut = (struct drm_color_lut *)state->gamma_lut->data;
+               for (i = 0; i < MTK_LUT_SIZE; i++) {
+                       word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) +
+                               (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) +
+                               ((lut[i].blue >> 6) & LUT_10BIT_MASK);
+                       writel(word, (lut_base + i * 4));
+               }
+       }
+}
+
+static const struct mtk_ddp_comp_funcs ddp_aal = {
+       .gamma_set = mtk_gamma_set,
+       .config = mtk_aal_config,
+       .start = mtk_aal_start,
+       .stop = mtk_aal_stop,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_gamma = {
+       .gamma_set = mtk_gamma_set,
+       .config = mtk_gamma_config,
+       .start = mtk_gamma_start,
+       .stop = mtk_gamma_stop,
+};
+
 static const struct mtk_ddp_comp_funcs ddp_color = {
        .config = mtk_color_config,
        .start = mtk_color_start,
@@ -112,13 +245,13 @@ struct mtk_ddp_comp_match {
 };
 
 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
-       [DDP_COMPONENT_AAL]     = { MTK_DISP_AAL,       0, NULL },
+       [DDP_COMPONENT_AAL]     = { MTK_DISP_AAL,       0, &ddp_aal },
        [DDP_COMPONENT_COLOR0]  = { MTK_DISP_COLOR,     0, &ddp_color },
        [DDP_COMPONENT_COLOR1]  = { MTK_DISP_COLOR,     1, &ddp_color },
        [DDP_COMPONENT_DPI0]    = { MTK_DPI,            0, NULL },
        [DDP_COMPONENT_DSI0]    = { MTK_DSI,            0, NULL },
        [DDP_COMPONENT_DSI1]    = { MTK_DSI,            1, NULL },
-       [DDP_COMPONENT_GAMMA]   = { MTK_DISP_GAMMA,     0, NULL },
+       [DDP_COMPONENT_GAMMA]   = { MTK_DISP_GAMMA,     0, &ddp_gamma },
        [DDP_COMPONENT_OD]      = { MTK_DISP_OD,        0, &ddp_od },
        [DDP_COMPONENT_OVL0]    = { MTK_DISP_OVL,       0, NULL },
        [DDP_COMPONENT_OVL1]    = { MTK_DISP_OVL,       1, NULL },
index 6b13ba9..22a33ee 100644 (file)
@@ -21,6 +21,7 @@ struct device_node;
 struct drm_crtc;
 struct drm_device;
 struct mtk_plane_state;
+struct drm_crtc_state;
 
 enum mtk_ddp_comp_type {
        MTK_DISP_OVL,
@@ -64,7 +65,7 @@ struct mtk_ddp_comp;
 
 struct mtk_ddp_comp_funcs {
        void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
-                      unsigned int h, unsigned int vrefresh);
+                      unsigned int h, unsigned int vrefresh, unsigned int bpc);
        void (*start)(struct mtk_ddp_comp *comp);
        void (*stop)(struct mtk_ddp_comp *comp);
        void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
@@ -73,6 +74,8 @@ struct mtk_ddp_comp_funcs {
        void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
        void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
                             struct mtk_plane_state *state);
+       void (*gamma_set)(struct mtk_ddp_comp *comp,
+                         struct drm_crtc_state *state);
 };
 
 struct mtk_ddp_comp {
@@ -86,10 +89,10 @@ struct mtk_ddp_comp {
 
 static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
                                       unsigned int w, unsigned int h,
-                                      unsigned int vrefresh)
+                                      unsigned int vrefresh, unsigned int bpc)
 {
        if (comp->funcs && comp->funcs->config)
-               comp->funcs->config(comp, w, h, vrefresh);
+               comp->funcs->config(comp, w, h, vrefresh, bpc);
 }
 
 static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
@@ -139,6 +142,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
                comp->funcs->layer_config(comp, idx, state);
 }
 
+static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp,
+                                    struct drm_crtc_state *state)
+{
+       if (comp->funcs && comp->funcs->gamma_set)
+               comp->funcs->gamma_set(comp, state);
+}
+
 int mtk_ddp_comp_get_id(struct device_node *node,
                        enum mtk_ddp_comp_type comp_type);
 int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
@@ -146,5 +156,7 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
                      const struct mtk_ddp_comp_funcs *funcs);
 int mtk_ddp_comp_register(struct drm_device *drm, struct mtk_ddp_comp *comp);
 void mtk_ddp_comp_unregister(struct drm_device *drm, struct mtk_ddp_comp *comp);
+void mtk_dither_set(struct mtk_ddp_comp *comp, unsigned int bpc,
+                   unsigned int CFG);
 
 #endif /* MTK_DRM_DDP_COMP_H */
index eebb7d8..cf83f65 100644 (file)
@@ -61,10 +61,27 @@ static void mtk_atomic_complete(struct mtk_drm_private *private,
 
        mtk_atomic_wait_for_fences(state);
 
+       /*
+        * Mediatek drm supports runtime PM, so plane registers cannot be
+        * written when their crtc is disabled.
+        *
+        * The comment for drm_atomic_helper_commit states:
+        *     For drivers supporting runtime PM the recommended sequence is
+        *
+        *     drm_atomic_helper_commit_modeset_disables(dev, state);
+        *     drm_atomic_helper_commit_modeset_enables(dev, state);
+        *     drm_atomic_helper_commit_planes(dev, state,
+        *                                     DRM_PLANE_COMMIT_ACTIVE_ONLY);
+        *
+        * See the kerneldoc entries for these three functions for more details.
+        */
        drm_atomic_helper_commit_modeset_disables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state, false);
        drm_atomic_helper_commit_modeset_enables(drm, state);
+       drm_atomic_helper_commit_planes(drm, state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
+
        drm_atomic_helper_wait_for_vblanks(drm, state);
+
        drm_atomic_helper_cleanup_planes(drm, state);
        drm_atomic_state_free(state);
 }
@@ -277,8 +294,8 @@ static int mtk_drm_bind(struct device *dev)
        int ret;
 
        drm = drm_dev_alloc(&mtk_drm_driver, dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        drm->dev_private = private;
        private->drm = drm;
index 3995765..c461a23 100644 (file)
@@ -30,57 +30,12 @@ static const u32 formats[] = {
        DRM_FORMAT_RGB565,
 };
 
-static void mtk_plane_enable(struct mtk_drm_plane *mtk_plane, bool enable,
-                            dma_addr_t addr, struct drm_rect *dest)
-{
-       struct drm_plane *plane = &mtk_plane->base;
-       struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
-       unsigned int pitch, format;
-       int x, y;
-
-       if (WARN_ON(!plane->state || (enable && !plane->state->fb)))
-               return;
-
-       if (plane->state->fb) {
-               pitch = plane->state->fb->pitches[0];
-               format = plane->state->fb->pixel_format;
-       } else {
-               pitch = 0;
-               format = DRM_FORMAT_RGBA8888;
-       }
-
-       x = plane->state->crtc_x;
-       y = plane->state->crtc_y;
-
-       if (x < 0) {
-               addr -= x * 4;
-               x = 0;
-       }
-
-       if (y < 0) {
-               addr -= y * pitch;
-               y = 0;
-       }
-
-       state->pending.enable = enable;
-       state->pending.pitch = pitch;
-       state->pending.format = format;
-       state->pending.addr = addr;
-       state->pending.x = x;
-       state->pending.y = y;
-       state->pending.width = dest->x2 - dest->x1;
-       state->pending.height = dest->y2 - dest->y1;
-       wmb(); /* Make sure the above parameters are set before update */
-       state->pending.dirty = true;
-}
-
 static void mtk_plane_reset(struct drm_plane *plane)
 {
        struct mtk_plane_state *state;
 
        if (plane->state) {
-               if (plane->state->fb)
-                       drm_framebuffer_unreference(plane->state->fb);
+               __drm_atomic_helper_plane_destroy_state(plane->state);
 
                state = to_mtk_plane_state(plane->state);
                memset(state, 0, sizeof(*state));
@@ -134,20 +89,6 @@ static int mtk_plane_atomic_check(struct drm_plane *plane,
 {
        struct drm_framebuffer *fb = state->fb;
        struct drm_crtc_state *crtc_state;
-       bool visible;
-       struct drm_rect dest = {
-               .x1 = state->crtc_x,
-               .y1 = state->crtc_y,
-               .x2 = state->crtc_x + state->crtc_w,
-               .y2 = state->crtc_y + state->crtc_h,
-       };
-       struct drm_rect src = {
-               /* 16.16 fixed point */
-               .x1 = state->src_x,
-               .y1 = state->src_y,
-               .x2 = state->src_x + state->src_w,
-               .y2 = state->src_y + state->src_h,
-       };
        struct drm_rect clip = { 0, };
 
        if (!fb)
@@ -168,40 +109,45 @@ static int mtk_plane_atomic_check(struct drm_plane *plane,
        clip.x2 = crtc_state->mode.hdisplay;
        clip.y2 = crtc_state->mode.vdisplay;
 
-       return drm_plane_helper_check_update(plane, state->crtc, fb,
-                                            &src, &dest, &clip,
-                                            state->rotation,
-                                            DRM_PLANE_HELPER_NO_SCALING,
-                                            DRM_PLANE_HELPER_NO_SCALING,
-                                            true, true, &visible);
+       return drm_plane_helper_check_state(state, &clip,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           DRM_PLANE_HELPER_NO_SCALING,
+                                           true, true);
 }
 
 static void mtk_plane_atomic_update(struct drm_plane *plane,
                                    struct drm_plane_state *old_state)
 {
        struct mtk_plane_state *state = to_mtk_plane_state(plane->state);
-       struct drm_crtc *crtc = state->base.crtc;
+       struct drm_crtc *crtc = plane->state->crtc;
+       struct drm_framebuffer *fb = plane->state->fb;
        struct drm_gem_object *gem;
        struct mtk_drm_gem_obj *mtk_gem;
-       struct mtk_drm_plane *mtk_plane = to_mtk_plane(plane);
-       struct drm_rect dest = {
-               .x1 = state->base.crtc_x,
-               .y1 = state->base.crtc_y,
-               .x2 = state->base.crtc_x + state->base.crtc_w,
-               .y2 = state->base.crtc_y + state->base.crtc_h,
-       };
-       struct drm_rect clip = { 0, };
+       unsigned int pitch, format;
+       dma_addr_t addr;
 
-       if (!crtc)
+       if (!crtc || WARN_ON(!fb))
                return;
 
-       clip.x2 = state->base.crtc->state->mode.hdisplay;
-       clip.y2 = state->base.crtc->state->mode.vdisplay;
-       drm_rect_intersect(&dest, &clip);
-
-       gem = mtk_fb_get_gem_obj(state->base.fb);
+       gem = mtk_fb_get_gem_obj(fb);
        mtk_gem = to_mtk_gem_obj(gem);
-       mtk_plane_enable(mtk_plane, true, mtk_gem->dma_addr, &dest);
+       addr = mtk_gem->dma_addr;
+       pitch = fb->pitches[0];
+       format = fb->pixel_format;
+
+       addr += (plane->state->src.x1 >> 16) * drm_format_plane_cpp(format, 0);
+       addr += (plane->state->src.y1 >> 16) * pitch;
+
+       state->pending.enable = true;
+       state->pending.pitch = pitch;
+       state->pending.format = format;
+       state->pending.addr = addr;
+       state->pending.x = plane->state->dst.x1;
+       state->pending.y = plane->state->dst.y1;
+       state->pending.width = drm_rect_width(&plane->state->dst);
+       state->pending.height = drm_rect_height(&plane->state->dst);
+       wmb(); /* Make sure the above parameters are set before update */
+       state->pending.dirty = true;
 }
 
 static void mtk_plane_atomic_disable(struct drm_plane *plane,
@@ -220,13 +166,12 @@ static const struct drm_plane_helper_funcs mtk_plane_helper_funcs = {
        .atomic_disable = mtk_plane_atomic_disable,
 };
 
-int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
-                  unsigned long possible_crtcs, enum drm_plane_type type,
-                  unsigned int zpos)
+int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                  unsigned long possible_crtcs, enum drm_plane_type type)
 {
        int err;
 
-       err = drm_universal_plane_init(dev, &mtk_plane->base, possible_crtcs,
+       err = drm_universal_plane_init(dev, plane, possible_crtcs,
                                       &mtk_plane_funcs, formats,
                                       ARRAY_SIZE(formats), type, NULL);
        if (err) {
@@ -234,8 +179,7 @@ int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
                return err;
        }
 
-       drm_plane_helper_add(&mtk_plane->base, &mtk_plane_helper_funcs);
-       mtk_plane->idx = zpos;
+       drm_plane_helper_add(plane, &mtk_plane_helper_funcs);
 
        return 0;
 }
index 72a7b3e..6a20b49 100644 (file)
 #include <drm/drm_crtc.h>
 #include <linux/types.h>
 
-struct mtk_drm_plane {
-       struct drm_plane                base;
-       unsigned int                    idx;
-};
-
 struct mtk_plane_pending_state {
        bool                            config;
        bool                            enable;
@@ -41,19 +36,13 @@ struct mtk_plane_state {
        struct mtk_plane_pending_state  pending;
 };
 
-static inline struct mtk_drm_plane *to_mtk_plane(struct drm_plane *plane)
-{
-       return container_of(plane, struct mtk_drm_plane, base);
-}
-
 static inline struct mtk_plane_state *
 to_mtk_plane_state(struct drm_plane_state *state)
 {
        return container_of(state, struct mtk_plane_state, base);
 }
 
-int mtk_plane_init(struct drm_device *dev, struct mtk_drm_plane *mtk_plane,
-                  unsigned long possible_crtcs, enum drm_plane_type type,
-                  unsigned int zpos);
+int mtk_plane_init(struct drm_device *dev, struct drm_plane *plane,
+                  unsigned long possible_crtcs, enum drm_plane_type type);
 
 #endif
index 334562d..71227de 100644 (file)
@@ -1086,20 +1086,20 @@ static int mtk_hdmi_output_init(struct mtk_hdmi *hdmi)
        return 0;
 }
 
-void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi)
+static void mtk_hdmi_audio_enable(struct mtk_hdmi *hdmi)
 {
        mtk_hdmi_aud_enable_packet(hdmi, true);
        hdmi->audio_enable = true;
 }
 
-void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi)
+static void mtk_hdmi_audio_disable(struct mtk_hdmi *hdmi)
 {
        mtk_hdmi_aud_enable_packet(hdmi, false);
        hdmi->audio_enable = false;
 }
 
-int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi,
-                            struct hdmi_audio_param *param)
+static int mtk_hdmi_audio_set_param(struct mtk_hdmi *hdmi,
+                                   struct hdmi_audio_param *param)
 {
        if (!hdmi->audio_enable) {
                dev_err(hdmi->dev, "hdmi audio is in disable state!\n");
@@ -1624,7 +1624,8 @@ static void mtk_hdmi_audio_shutdown(struct device *dev, void *data)
        mtk_hdmi_audio_disable(hdmi);
 }
 
-int mtk_hdmi_audio_digital_mute(struct device *dev, void *data, bool enable)
+static int
+mtk_hdmi_audio_digital_mute(struct device *dev, void *data, bool enable)
 {
        struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
 
index 5e2f131..25b2a1a 100644 (file)
@@ -58,7 +58,7 @@ static const struct file_operations mga_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features =
-           DRIVER_USE_AGP | DRIVER_PCI_DMA |
+           DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_LEGACY |
            DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
        .dev_priv_size = sizeof(drm_mga_buf_priv_t),
        .load = mga_driver_load,
index 2b4b125..1443b3a 100644 (file)
@@ -56,7 +56,7 @@ static void mgag200_kick_out_firmware_fb(struct pci_dev *pdev)
 #ifdef CONFIG_X86
        primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 #endif
-       remove_conflicting_framebuffers(ap, "mgag200drmfb", primary);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "mgag200drmfb", primary);
        kfree(ap);
 }
 
index d9b04b0..88dd221 100644 (file)
@@ -15,8 +15,6 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_crtc_helper.h>
 
-#include <linux/fb.h>
-
 #include "mgag200_drv.h"
 
 static void mga_dirty_update(struct mga_fbdev *mfbdev,
@@ -185,8 +183,10 @@ static int mgag200fb_create(struct drm_fb_helper *helper,
        }
 
        sysram = vmalloc(size);
-       if (!sysram)
+       if (!sysram) {
+               ret = -ENOMEM;
                goto err_sysram;
+       }
 
        info = drm_fb_helper_alloc_fbi(helper);
        if (IS_ERR(info)) {
index 13798b3..e79cbc2 100644 (file)
@@ -135,7 +135,7 @@ static int mga_vram_init(struct mga_device *mdev)
        aper->ranges[0].base = mdev->mc.vram_base;
        aper->ranges[0].size = mdev->mc.vram_window;
 
-       remove_conflicting_framebuffers(aper, "mgafb", true);
+       drm_fb_helper_remove_conflicting_framebuffers(aper, "mgafb", true);
        kfree(aper);
 
        if (!devm_request_mem_region(mdev->dev->dev, mdev->mc.vram_base, mdev->mc.vram_window,
index 68268e5..919b35f 100644 (file)
@@ -150,7 +150,8 @@ static int mgag200_bo_verify_access(struct ttm_buffer_object *bo, struct file *f
 {
        struct mgag200_bo *mgabo = mgag200_bo(bo);
 
-       return drm_vma_node_verify_access(&mgabo->gem.vma_node, filp);
+       return drm_vma_node_verify_access(&mgabo->gem.vma_node,
+                                         filp->private_data);
 }
 
 static int mgag200_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
index 7c7a031..d96b2b6 100644 (file)
@@ -11,6 +11,7 @@ config DRM_MSM
        select TMPFS
        select QCOM_SCM
        select SND_SOC_HDMI_CODEC if SND_SOC
+       select SYNC_FILE
        default y
        help
          DRM/KMS driver for MSM/snapdragon.
index 9737207..a968cad 100644 (file)
@@ -422,11 +422,28 @@ static const struct {
 
 static int msm_hdmi_get_gpio(struct device_node *of_node, const char *name)
 {
-       int gpio = of_get_named_gpio(of_node, name, 0);
+       int gpio;
+
+       /* try with the gpio names as in the table (downstream bindings) */
+       gpio = of_get_named_gpio(of_node, name, 0);
        if (gpio < 0) {
                char name2[32];
-               snprintf(name2, sizeof(name2), "%s-gpio", name);
+
+               /* try with the gpio names as in the upstream bindings */
+               snprintf(name2, sizeof(name2), "%s-gpios", name);
                gpio = of_get_named_gpio(of_node, name2, 0);
+               if (gpio < 0) {
+                       char name3[32];
+
+                       /*
+                        * try again after stripping out the "qcom,hdmi-tx"
+                        * prefix. This is mainly to match "hpd-gpios" used
+                        * in the upstream bindings
+                        */
+                       if (sscanf(name2, "qcom,hdmi-tx-%s", name3))
+                               gpio = of_get_named_gpio(of_node, name3, 0);
+               }
+
                if (gpio < 0) {
                        DBG("failed to get gpio: %s (%d)", name, gpio);
                        gpio = -1;
index de9007e..73e2021 100644 (file)
@@ -243,7 +243,6 @@ void msm_hdmi_i2c_destroy(struct i2c_adapter *i2c)
 
 struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi)
 {
-       struct drm_device *dev = hdmi->dev;
        struct hdmi_i2c_adapter *hdmi_i2c;
        struct i2c_adapter *i2c = NULL;
        int ret;
@@ -267,10 +266,8 @@ struct i2c_adapter *msm_hdmi_i2c_init(struct hdmi *hdmi)
        i2c->algo = &msm_hdmi_i2c_algorithm;
 
        ret = i2c_add_adapter(i2c);
-       if (ret) {
-               dev_err(dev->dev, "failed to register hdmi i2c: %d\n", ret);
+       if (ret)
                goto fail;
-       }
 
        return i2c;
 
index 7b39e89..571a91e 100644 (file)
@@ -228,18 +228,21 @@ static struct device_node *mdp4_detect_lcdc_panel(struct drm_device *dev)
        struct device_node *endpoint, *panel_node;
        struct device_node *np = dev->dev->of_node;
 
-       endpoint = of_graph_get_next_endpoint(np, NULL);
+       /*
+        * LVDS/LCDC is the first port described in the list of ports in the
+        * MDP4 DT node.
+        */
+       endpoint = of_graph_get_endpoint_by_regs(np, 0, -1);
        if (!endpoint) {
-               DBG("no endpoint in MDP4 to fetch LVDS panel\n");
+               DBG("no LVDS remote endpoint\n");
                return NULL;
        }
 
-       /* don't proceed if we have an endpoint but no panel_node tied to it */
        panel_node = of_graph_get_remote_port_parent(endpoint);
        if (!panel_node) {
-               dev_err(dev->dev, "no valid panel node\n");
+               DBG("no valid panel node in LVDS endpoint\n");
                of_node_put(endpoint);
-               return ERR_PTR(-ENODEV);
+               return NULL;
        }
 
        of_node_put(endpoint);
@@ -262,14 +265,12 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms,
        switch (intf_type) {
        case DRM_MODE_ENCODER_LVDS:
                /*
-                * bail out early if:
-                * - there is no panel node (no need to initialize lcdc
-                *   encoder and lvds connector), or
-                * - panel node is a bad pointer
+                * bail out early if there is no panel node (no need to
+                * initialize LCDC encoder and LVDS connector)
                 */
                panel_node = mdp4_detect_lcdc_panel(dev);
-               if (IS_ERR_OR_NULL(panel_node))
-                       return PTR_ERR(panel_node);
+               if (!panel_node)
+                       return 0;
 
                encoder = mdp4_lcdc_encoder_init(dev, panel_node);
                if (IS_ERR(encoder)) {
index bc3d8e7..a06b064 100644 (file)
@@ -93,7 +93,7 @@ static const struct drm_encoder_funcs mdp4_lcdc_encoder_funcs = {
 };
 
 /* this should probably be a helper: */
-struct drm_connector *get_connector(struct drm_encoder *encoder)
+static struct drm_connector *get_connector(struct drm_encoder *encoder)
 {
        struct drm_device *dev = encoder->dev;
        struct drm_connector *connector;
index 9f96dfe..3903dbc 100644 (file)
@@ -81,7 +81,7 @@ static void mdp4_plane_install_properties(struct drm_plane *plane,
        // XXX
 }
 
-int mdp4_plane_set_property(struct drm_plane *plane,
+static int mdp4_plane_set_property(struct drm_plane *plane,
                struct drm_property *property, uint64_t val)
 {
        // XXX
@@ -99,7 +99,7 @@ static const struct drm_plane_funcs mdp4_plane_funcs = {
 };
 
 static int mdp4_plane_prepare_fb(struct drm_plane *plane,
-               const struct drm_plane_state *new_state)
+                                struct drm_plane_state *new_state)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
@@ -113,7 +113,7 @@ static int mdp4_plane_prepare_fb(struct drm_plane *plane,
 }
 
 static void mdp4_plane_cleanup_fb(struct drm_plane *plane,
-               const struct drm_plane_state *old_state)
+                                 struct drm_plane_state *old_state)
 {
        struct mdp4_plane *mdp4_plane = to_mdp4_plane(plane);
        struct mdp4_kms *mdp4_kms = get_kms(plane);
index 432c098..951c002 100644 (file)
@@ -78,12 +78,12 @@ static void mdp5_plane_install_rotation_property(struct drm_device *dev,
        if (!dev->mode_config.rotation_property)
                dev->mode_config.rotation_property =
                        drm_mode_create_rotation_property(dev,
-                       BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y));
+                               DRM_ROTATE_0 | DRM_REFLECT_X | DRM_REFLECT_Y);
 
        if (dev->mode_config.rotation_property)
                drm_object_attach_property(&plane->base,
                        dev->mode_config.rotation_property,
-                       0);
+                       DRM_ROTATE_0);
 }
 
 /* helper to install properties which are common to planes and crtcs */
@@ -250,7 +250,7 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
 };
 
 static int mdp5_plane_prepare_fb(struct drm_plane *plane,
-               const struct drm_plane_state *new_state)
+                                struct drm_plane_state *new_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
@@ -264,7 +264,7 @@ static int mdp5_plane_prepare_fb(struct drm_plane *plane,
 }
 
 static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
-               const struct drm_plane_state *old_state)
+                                 struct drm_plane_state *old_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
@@ -309,8 +309,8 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane,
                        return -EINVAL;
                }
 
-               hflip = !!(state->rotation & BIT(DRM_REFLECT_X));
-               vflip = !!(state->rotation & BIT(DRM_REFLECT_Y));
+               hflip = !!(state->rotation & DRM_REFLECT_X);
+               vflip = !!(state->rotation & DRM_REFLECT_Y);
                if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) ||
                        (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) {
                        dev_err(plane->dev->dev,
@@ -743,8 +743,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
        config |= get_scale_config(format, src_h, crtc_h, false);
        DBG("scale config = %x", config);
 
-       hflip = !!(pstate->rotation & BIT(DRM_REFLECT_X));
-       vflip = !!(pstate->rotation & BIT(DRM_REFLECT_Y));
+       hflip = !!(pstate->rotation & DRM_REFLECT_X);
+       vflip = !!(pstate->rotation & DRM_REFLECT_Y);
 
        spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
 
index 4a8a6f1..73bae38 100644 (file)
@@ -112,13 +112,13 @@ static void complete_commit(struct msm_commit *c, bool async)
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_kms *kms = priv->kms;
 
-       drm_atomic_helper_wait_for_fences(dev, state);
+       drm_atomic_helper_wait_for_fences(dev, state, false);
 
        kms->funcs->prepare_commit(kms, state);
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state, false);
+       drm_atomic_helper_commit_planes(dev, state, 0);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
index 8a02370..fb5c0b0 100644 (file)
  * MSM driver version:
  * - 1.0.0 - initial interface
  * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers
+ * - 1.2.0 - adds explicit fence support for submit ioctl
  */
 #define MSM_VERSION_MAJOR      1
-#define MSM_VERSION_MINOR      1
+#define MSM_VERSION_MINOR      2
 #define MSM_VERSION_PATCHLEVEL 0
 
 static void msm_fb_output_poll_changed(struct drm_device *dev)
@@ -347,9 +348,9 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
        int ret;
 
        ddev = drm_dev_alloc(drv, dev);
-       if (!ddev) {
+       if (IS_ERR(ddev)) {
                dev_err(dev, "failed to allocate drm_device\n");
-               return -ENOMEM;
+               return PTR_ERR(ddev);
        }
 
        platform_set_drvdata(pdev, ddev);
index 85f3047..b6ac27e 100644 (file)
@@ -593,18 +593,16 @@ int msm_gem_cpu_prep(struct drm_gem_object *obj, uint32_t op, ktime_t *timeout)
 {
        struct msm_gem_object *msm_obj = to_msm_bo(obj);
        bool write = !!(op & MSM_PREP_WRITE);
-
-       if (op & MSM_PREP_NOSYNC) {
-               if (!reservation_object_test_signaled_rcu(msm_obj->resv, write))
-                       return -EBUSY;
-       } else {
-               int ret;
-
-               ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write,
-                               true, timeout_to_jiffies(timeout));
-               if (ret <= 0)
-                       return ret == 0 ? -ETIMEDOUT : ret;
-       }
+       unsigned long remain =
+               op & MSM_PREP_NOSYNC ? 0 : timeout_to_jiffies(timeout);
+       long ret;
+
+       ret = reservation_object_wait_timeout_rcu(msm_obj->resv, write,
+                                                 true,  remain);
+       if (ret == 0)
+               return remain == 0 ? -EBUSY : -ETIMEDOUT;
+       else if (ret < 0)
+               return ret;
 
        /* TODO cache maintenance */
 
index 880d6a9..b6a0f37 100644 (file)
@@ -15,6 +15,8 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/sync_file.h>
+
 #include "msm_drv.h"
 #include "msm_gpu.h"
 #include "msm_gem.h"
@@ -378,6 +380,9 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        struct msm_file_private *ctx = file->driver_priv;
        struct msm_gem_submit *submit;
        struct msm_gpu *gpu = priv->gpu;
+       struct fence *in_fence = NULL;
+       struct sync_file *sync_file = NULL;
+       int out_fence_fd = -1;
        unsigned i;
        int ret;
 
@@ -387,13 +392,23 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        /* for now, we just have 3d pipe.. eventually this would need to
         * be more clever to dispatch to appropriate gpu module:
         */
-       if (args->pipe != MSM_PIPE_3D0)
+       if (MSM_PIPE_ID(args->flags) != MSM_PIPE_3D0)
+               return -EINVAL;
+
+       if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS)
                return -EINVAL;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
        if (ret)
                return ret;
 
+       if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
+               out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
+               if (out_fence_fd < 0) {
+                       ret = out_fence_fd;
+                       goto out_unlock;
+               }
+       }
        priv->struct_mutex_task = current;
 
        submit = submit_create(dev, gpu, args->nr_bos, args->nr_cmds);
@@ -410,9 +425,32 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
        if (ret)
                goto out;
 
-       ret = submit_fence_sync(submit);
-       if (ret)
-               goto out;
+       if (args->flags & MSM_SUBMIT_FENCE_FD_IN) {
+               in_fence = sync_file_get_fence(args->fence_fd);
+
+               if (!in_fence) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               /* TODO if we get an array-fence due to userspace merging multiple
+                * fences, we need a way to determine if all the backing fences
+                * are from our own context..
+                */
+
+               if (in_fence->context != gpu->fctx->context) {
+                       ret = fence_wait(in_fence, true);
+                       if (ret)
+                               goto out;
+               }
+
+       }
+
+       if (!(args->fence & MSM_SUBMIT_NO_IMPLICIT)) {
+               ret = submit_fence_sync(submit);
+               if (ret)
+                       goto out;
+       }
 
        ret = submit_pin_objects(submit);
        if (ret)
@@ -478,15 +516,39 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data,
 
        submit->nr_cmds = i;
 
-       ret = msm_gpu_submit(gpu, submit, ctx);
+       submit->fence = msm_fence_alloc(gpu->fctx);
+       if (IS_ERR(submit->fence)) {
+               ret = PTR_ERR(submit->fence);
+               submit->fence = NULL;
+               goto out;
+       }
+
+       if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
+               sync_file = sync_file_create(submit->fence);
+               if (!sync_file) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       msm_gpu_submit(gpu, submit, ctx);
 
        args->fence = submit->fence->seqno;
 
+       if (args->flags & MSM_SUBMIT_FENCE_FD_OUT) {
+               fd_install(out_fence_fd, sync_file->file);
+               args->fence_fd = out_fence_fd;
+       }
+
 out:
+       if (in_fence)
+               fence_put(in_fence);
        submit_cleanup(submit);
        if (ret)
                msm_gem_submit_free(submit);
 out_unlock:
+       if (ret && (out_fence_fd >= 0))
+               put_unused_fd(out_fence_fd);
        priv->struct_mutex_task = NULL;
        mutex_unlock(&dev->struct_mutex);
        return ret;
index 36ed53e..5bb0983 100644 (file)
@@ -509,22 +509,15 @@ void msm_gpu_retire(struct msm_gpu *gpu)
 }
 
 /* add bo's to gpu's ring, and kick gpu: */
-int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
+void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                struct msm_file_private *ctx)
 {
        struct drm_device *dev = gpu->dev;
        struct msm_drm_private *priv = dev->dev_private;
-       int i, ret;
+       int i;
 
        WARN_ON(!mutex_is_locked(&dev->struct_mutex));
 
-       submit->fence = msm_fence_alloc(gpu->fctx);
-       if (IS_ERR(submit->fence)) {
-               ret = PTR_ERR(submit->fence);
-               submit->fence = NULL;
-               return ret;
-       }
-
        inactive_cancel(gpu);
 
        list_add_tail(&submit->node, &gpu->submit_list);
@@ -557,8 +550,6 @@ int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
        priv->lastctx = ctx;
 
        hangcheck_timer_reset(gpu);
-
-       return 0;
 }
 
 /*
index c902283..d61d98a 100644 (file)
@@ -163,7 +163,7 @@ int msm_gpu_perfcntr_sample(struct msm_gpu *gpu, uint32_t *activetime,
                uint32_t *totaltime, uint32_t ncntrs, uint32_t *cntrs);
 
 void msm_gpu_retire(struct msm_gpu *gpu);
-int msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
+void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
                struct msm_file_private *ctx);
 
 int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev,
index 864323b..343b865 100644 (file)
@@ -1152,7 +1152,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
        if (ret)
                goto out;
 
-       ret = ttm_bo_move_ttm(bo, true, intr, no_wait_gpu, new_mem);
+       ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, new_mem);
 out:
        ttm_bo_mem_put(bo, &tmp_mem);
        return ret;
@@ -1180,7 +1180,7 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
        if (ret)
                return ret;
 
-       ret = ttm_bo_move_ttm(bo, true, intr, no_wait_gpu, &tmp_mem);
+       ret = ttm_bo_move_ttm(bo, intr, no_wait_gpu, &tmp_mem);
        if (ret)
                goto out;
 
@@ -1298,7 +1298,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
        /* Fallback to software copy. */
        ret = ttm_bo_wait(bo, intr, no_wait_gpu);
        if (ret == 0)
-               ret = ttm_bo_move_memcpy(bo, evict, intr, no_wait_gpu, new_mem);
+               ret = ttm_bo_move_memcpy(bo, intr, no_wait_gpu, new_mem);
 
 out:
        if (drm->device.info.family < NV_DEVICE_INFO_V0_TESLA) {
@@ -1316,7 +1316,8 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 {
        struct nouveau_bo *nvbo = nouveau_bo(bo);
 
-       return drm_vma_node_verify_access(&nvbo->gem.vma_node, filp);
+       return drm_vma_node_verify_access(&nvbo->gem.vma_node,
+                                         filp->private_data);
 }
 
 static int
index 66c1280..3100fd8 100644 (file)
@@ -351,7 +351,7 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
        boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 #endif
        if (nouveau_modeset != 2)
-               remove_conflicting_framebuffers(aper, "nouveaufb", boot);
+               drm_fb_helper_remove_conflicting_framebuffers(aper, "nouveaufb", boot);
        kfree(aper);
 
        ret = nvkm_device_pci_new(pdev, nouveau_config, nouveau_debug,
@@ -1067,8 +1067,8 @@ nouveau_platform_device_create(const struct nvkm_device_tegra_func *func,
                goto err_free;
 
        drm = drm_dev_alloc(&driver_platform, &pdev->dev);
-       if (!drm) {
-               err = -ENOMEM;
+       if (IS_ERR(drm)) {
+               err = PTR_ERR(drm);
                goto err_free;
        }
 
index d1f248f..9f56927 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/tty.h>
 #include <linux/sysrq.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/init.h>
 #include <linux/screen_info.h>
 #include <linux/vga_switcheroo.h>
index 0eae8af..b1f3b81 100644 (file)
@@ -13,7 +13,6 @@
 
 #include <linux/backlight.h>
 #include <linux/delay.h>
-#include <linux/fb.h>
 #include <linux/gpio/consumer.h>
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
index fc4c238..9f3d6f4 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/spi/spi.h>
-#include <linux/fb.h>
 #include <linux/gpio/consumer.h>
 #include <linux/of_gpio.h>
 
index 157c512..3557a4c 100644 (file)
@@ -28,7 +28,6 @@
 #include <linux/jiffies.h>
 #include <linux/sched.h>
 #include <linux/backlight.h>
-#include <linux/fb.h>
 #include <linux/gpio/consumer.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
index e256d87..dfd4e96 100644 (file)
@@ -125,16 +125,15 @@ u32 dss_of_port_get_port_number(struct device_node *port)
 
 static struct device_node *omapdss_of_get_remote_port(const struct device_node *node)
 {
-       struct device_node *np, *np_parent;
+       struct device_node *np;
 
        np = of_parse_phandle(node, "remote-endpoint", 0);
        if (!np)
                return NULL;
 
-       np_parent = of_get_next_parent(np);
-       of_node_put(np);
+       np = of_get_next_parent(np);
 
-       return np_parent;
+       return np;
 }
 
 struct device_node *
index 26c6134..e1cfba5 100644 (file)
@@ -96,7 +96,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit)
        dispc_runtime_get();
 
        drm_atomic_helper_commit_modeset_disables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state, false);
+       drm_atomic_helper_commit_planes(dev, old_state, 0);
        drm_atomic_helper_commit_modeset_enables(dev, old_state);
 
        omap_atomic_wait_for_completion(dev, old_state);
@@ -295,9 +295,9 @@ static int omap_modeset_init_properties(struct drm_device *dev)
        if (priv->has_dmm) {
                dev->mode_config.rotation_property =
                        drm_mode_create_rotation_property(dev,
-                               BIT(DRM_ROTATE_0) | BIT(DRM_ROTATE_90) |
-                               BIT(DRM_ROTATE_180) | BIT(DRM_ROTATE_270) |
-                               BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y));
+                               DRM_ROTATE_0 | DRM_ROTATE_90 |
+                               DRM_ROTATE_180 | DRM_ROTATE_270 |
+                               DRM_REFLECT_X | DRM_REFLECT_Y);
                if (!dev->mode_config.rotation_property)
                        return -ENOMEM;
        }
index 31f5178..5f3337f 100644 (file)
@@ -179,24 +179,24 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
                                        (uint32_t)win->rotation);
                        /* fallthru to default to no rotation */
                case 0:
-               case BIT(DRM_ROTATE_0):
+               case DRM_ROTATE_0:
                        orient = 0;
                        break;
-               case BIT(DRM_ROTATE_90):
+               case DRM_ROTATE_90:
                        orient = MASK_XY_FLIP | MASK_X_INVERT;
                        break;
-               case BIT(DRM_ROTATE_180):
+               case DRM_ROTATE_180:
                        orient = MASK_X_INVERT | MASK_Y_INVERT;
                        break;
-               case BIT(DRM_ROTATE_270):
+               case DRM_ROTATE_270:
                        orient = MASK_XY_FLIP | MASK_Y_INVERT;
                        break;
                }
 
-               if (win->rotation & BIT(DRM_REFLECT_X))
+               if (win->rotation & DRM_REFLECT_X)
                        orient ^= MASK_X_INVERT;
 
-               if (win->rotation & BIT(DRM_REFLECT_Y))
+               if (win->rotation & DRM_REFLECT_Y)
                        orient ^= MASK_Y_INVERT;
 
                /* adjust x,y offset for flip/invert: */
@@ -213,7 +213,7 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
        } else {
                switch (win->rotation & DRM_ROTATE_MASK) {
                case 0:
-               case BIT(DRM_ROTATE_0):
+               case DRM_ROTATE_0:
                        /* OK */
                        break;
 
index 5252ab7..66ac8c4 100644 (file)
@@ -60,7 +60,7 @@ to_omap_plane_state(struct drm_plane_state *state)
 }
 
 static int omap_plane_prepare_fb(struct drm_plane *plane,
-                                const struct drm_plane_state *new_state)
+                                struct drm_plane_state *new_state)
 {
        if (!new_state->fb)
                return 0;
@@ -69,7 +69,7 @@ static int omap_plane_prepare_fb(struct drm_plane *plane,
 }
 
 static void omap_plane_cleanup_fb(struct drm_plane *plane,
-                                 const struct drm_plane_state *old_state)
+                                 struct drm_plane_state *old_state)
 {
        if (old_state->fb)
                omap_framebuffer_unpin(old_state->fb);
@@ -109,8 +109,8 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
        win.src_y = state->src_y >> 16;
 
        switch (state->rotation & DRM_ROTATE_MASK) {
-       case BIT(DRM_ROTATE_90):
-       case BIT(DRM_ROTATE_270):
+       case DRM_ROTATE_90:
+       case DRM_ROTATE_270:
                win.src_w = state->src_h >> 16;
                win.src_h = state->src_w >> 16;
                break;
@@ -149,7 +149,7 @@ static void omap_plane_atomic_disable(struct drm_plane *plane,
        struct omap_plane_state *omap_state = to_omap_plane_state(plane->state);
        struct omap_plane *omap_plane = to_omap_plane(plane);
 
-       plane->state->rotation = BIT(DRM_ROTATE_0);
+       plane->state->rotation = DRM_ROTATE_0;
        omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
                           ? 0 : omap_plane->id;
 
@@ -178,7 +178,7 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
                return -EINVAL;
 
        if (state->fb) {
-               if (state->rotation != BIT(DRM_ROTATE_0) &&
+               if (state->rotation != DRM_ROTATE_0 &&
                    !omap_framebuffer_supports_rotation(state->fb))
                        return -EINVAL;
        }
@@ -269,7 +269,7 @@ static void omap_plane_reset(struct drm_plane *plane)
         */
        omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY
                           ? 0 : omap_plane->id;
-       omap_state->base.rotation = BIT(DRM_ROTATE_0);
+       omap_state->base.rotation = DRM_ROTATE_0;
 
        plane->state = &omap_state->base;
        plane->state->plane = plane;
index 1500ab9..62aba97 100644 (file)
@@ -18,6 +18,17 @@ config DRM_PANEL_SIMPLE
          that it can be automatically turned off when the panel goes into a
          low power state.
 
+config DRM_PANEL_JDI_LT070ME05000
+       tristate "JDI LT070ME05000 WUXGA DSI panel"
+       depends on OF
+       depends on DRM_MIPI_DSI
+       depends on BACKLIGHT_CLASS_DEVICE
+       help
+         Say Y here if you want to enable support for JDI DSI video mode
+         panel as found in Google Nexus 7 (2013) devices.
+         The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses
+         24 bit per pixel.
+
 config DRM_PANEL_SAMSUNG_LD9040
        tristate "Samsung LD9040 RGB/SPI panel"
        depends on OF && SPI
index f277eed..a5c7ec0 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_DRM_PANEL_SIMPLE) += panel-simple.o
+obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o
 obj-$(CONFIG_DRM_PANEL_LG_LG4573) += panel-lg-lg4573.o
 obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o
 obj-$(CONFIG_DRM_PANEL_SAMSUNG_LD9040) += panel-samsung-ld9040.o
diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
new file mode 100644 (file)
index 0000000..5b2340e
--- /dev/null
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2016 InforceComputing
+ * Author: Vinay Simha BN <simhavcs@gmail.com>
+ *
+ * Copyright (C) 2016 Linaro Ltd
+ * Author: Sumit Semwal <sumit.semwal@linaro.org>
+ *
+ * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a
+ * JDI model LT070ME05000, and its data sheet is at:
+ * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/backlight.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include <video/mipi_display.h>
+
+static const char * const regulator_names[] = {
+       "vddp",
+       "iovcc"
+};
+
+struct jdi_panel {
+       struct drm_panel base;
+       struct mipi_dsi_device *dsi;
+
+       struct regulator_bulk_data supplies[ARRAY_SIZE(regulator_names)];
+
+       struct gpio_desc *enable_gpio;
+       struct gpio_desc *reset_gpio;
+       struct gpio_desc *dcdc_en_gpio;
+       struct backlight_device *backlight;
+
+       bool prepared;
+       bool enabled;
+
+       const struct drm_display_mode *mode;
+};
+
+static inline struct jdi_panel *to_jdi_panel(struct drm_panel *panel)
+{
+       return container_of(panel, struct jdi_panel, base);
+}
+
+static int jdi_panel_init(struct jdi_panel *jdi)
+{
+       struct mipi_dsi_device *dsi = jdi->dsi;
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_soft_reset(dsi);
+       if (ret < 0)
+               return ret;
+
+       usleep_range(10000, 20000);
+
+       ret = mipi_dsi_dcs_set_pixel_format(dsi, MIPI_DCS_PIXEL_FMT_24BIT << 4);
+       if (ret < 0) {
+               dev_err(dev, "failed to set pixel format: %d\n", ret);
+               return ret;
+       }
+
+       ret = mipi_dsi_dcs_set_column_address(dsi, 0, jdi->mode->hdisplay - 1);
+       if (ret < 0) {
+               dev_err(dev, "failed to set column address: %d\n", ret);
+               return ret;
+       }
+
+       ret = mipi_dsi_dcs_set_page_address(dsi, 0, jdi->mode->vdisplay - 1);
+       if (ret < 0) {
+               dev_err(dev, "failed to set page address: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * BIT(5) BCTRL = 1 Backlight Control Block On, Brightness registers
+        *                  are active
+        * BIT(3) BL = 1    Backlight Control On
+        * BIT(2) DD = 0    Display Dimming is Off
+        */
+       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY,
+                                (u8[]){ 0x24 }, 1);
+       if (ret < 0) {
+               dev_err(dev, "failed to write control display: %d\n", ret);
+               return ret;
+       }
+
+       /* CABC off */
+       ret = mipi_dsi_dcs_write(dsi, MIPI_DCS_WRITE_POWER_SAVE,
+                                (u8[]){ 0x00 }, 1);
+       if (ret < 0) {
+               dev_err(dev, "failed to set cabc off: %d\n", ret);
+               return ret;
+       }
+
+       ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+       if (ret < 0) {
+               dev_err(dev, "failed to set exit sleep mode: %d\n", ret);
+               return ret;
+       }
+
+       msleep(120);
+
+       ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x00}, 2);
+       if (ret < 0) {
+               dev_err(dev, "failed to set mcap: %d\n", ret);
+               return ret;
+       }
+
+       mdelay(10);
+
+       /* Interface setting, video mode */
+       ret = mipi_dsi_generic_write(dsi, (u8[])
+                                    {0xB3, 0x26, 0x08, 0x00, 0x20, 0x00}, 6);
+       if (ret < 0) {
+               dev_err(dev, "failed to set display interface setting: %d\n"
+                       , ret);
+               return ret;
+       }
+
+       mdelay(20);
+
+       ret = mipi_dsi_generic_write(dsi, (u8[]){0xB0, 0x03}, 2);
+       if (ret < 0) {
+               dev_err(dev, "failed to set default values for mcap: %d\n"
+                       , ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int jdi_panel_on(struct jdi_panel *jdi)
+{
+       struct mipi_dsi_device *dsi = jdi->dsi;
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_set_display_on(dsi);
+       if (ret < 0)
+               dev_err(dev, "failed to set display on: %d\n", ret);
+
+       return ret;
+}
+
+static void jdi_panel_off(struct jdi_panel *jdi)
+{
+       struct mipi_dsi_device *dsi = jdi->dsi;
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+
+       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_set_display_off(dsi);
+       if (ret < 0)
+               dev_err(dev, "failed to set display off: %d\n", ret);
+
+       ret = mipi_dsi_dcs_enter_sleep_mode(dsi);
+       if (ret < 0)
+               dev_err(dev, "failed to enter sleep mode: %d\n", ret);
+
+       msleep(100);
+}
+
+static int jdi_panel_disable(struct drm_panel *panel)
+{
+       struct jdi_panel *jdi = to_jdi_panel(panel);
+
+       if (!jdi->enabled)
+               return 0;
+
+       jdi->backlight->props.power = FB_BLANK_POWERDOWN;
+       backlight_update_status(jdi->backlight);
+
+       jdi->enabled = false;
+
+       return 0;
+}
+
+static int jdi_panel_unprepare(struct drm_panel *panel)
+{
+       struct jdi_panel *jdi = to_jdi_panel(panel);
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+
+       if (!jdi->prepared)
+               return 0;
+
+       jdi_panel_off(jdi);
+
+       ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
+       if (ret < 0)
+               dev_err(dev, "regulator disable failed, %d\n", ret);
+
+       gpiod_set_value(jdi->enable_gpio, 0);
+
+       gpiod_set_value(jdi->reset_gpio, 1);
+
+       gpiod_set_value(jdi->dcdc_en_gpio, 0);
+
+       jdi->prepared = false;
+
+       return 0;
+}
+
+static int jdi_panel_prepare(struct drm_panel *panel)
+{
+       struct jdi_panel *jdi = to_jdi_panel(panel);
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+
+       if (jdi->prepared)
+               return 0;
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
+       if (ret < 0) {
+               dev_err(dev, "regulator enable failed, %d\n", ret);
+               return ret;
+       }
+
+       msleep(20);
+
+       gpiod_set_value(jdi->dcdc_en_gpio, 1);
+       usleep_range(10, 20);
+
+       gpiod_set_value(jdi->reset_gpio, 0);
+       usleep_range(10, 20);
+
+       gpiod_set_value(jdi->enable_gpio, 1);
+       usleep_range(10, 20);
+
+       ret = jdi_panel_init(jdi);
+       if (ret < 0) {
+               dev_err(dev, "failed to init panel: %d\n", ret);
+               goto poweroff;
+       }
+
+       ret = jdi_panel_on(jdi);
+       if (ret < 0) {
+               dev_err(dev, "failed to set panel on: %d\n", ret);
+               goto poweroff;
+       }
+
+       jdi->prepared = true;
+
+       return 0;
+
+poweroff:
+       ret = regulator_bulk_disable(ARRAY_SIZE(jdi->supplies), jdi->supplies);
+       if (ret < 0)
+               dev_err(dev, "regulator disable failed, %d\n", ret);
+
+       gpiod_set_value(jdi->enable_gpio, 0);
+
+       gpiod_set_value(jdi->reset_gpio, 1);
+
+       gpiod_set_value(jdi->dcdc_en_gpio, 0);
+
+       return ret;
+}
+
+static int jdi_panel_enable(struct drm_panel *panel)
+{
+       struct jdi_panel *jdi = to_jdi_panel(panel);
+
+       if (jdi->enabled)
+               return 0;
+
+       jdi->backlight->props.power = FB_BLANK_UNBLANK;
+       backlight_update_status(jdi->backlight);
+
+       jdi->enabled = true;
+
+       return 0;
+}
+
+static const struct drm_display_mode default_mode = {
+               .clock = 155493,
+               .hdisplay = 1200,
+               .hsync_start = 1200 + 48,
+               .hsync_end = 1200 + 48 + 32,
+               .htotal = 1200 + 48 + 32 + 60,
+               .vdisplay = 1920,
+               .vsync_start = 1920 + 3,
+               .vsync_end = 1920 + 3 + 5,
+               .vtotal = 1920 + 3 + 5 + 6,
+               .vrefresh = 60,
+               .flags = 0,
+};
+
+static int jdi_panel_get_modes(struct drm_panel *panel)
+{
+       struct drm_display_mode *mode;
+       struct jdi_panel *jdi = to_jdi_panel(panel);
+       struct device *dev = &jdi->dsi->dev;
+
+       mode = drm_mode_duplicate(panel->drm, &default_mode);
+       if (!mode) {
+               dev_err(dev, "failed to add mode %ux%ux@%u\n",
+                       default_mode.hdisplay, default_mode.vdisplay,
+                       default_mode.vrefresh);
+               return -ENOMEM;
+       }
+
+       drm_mode_set_name(mode);
+
+       drm_mode_probed_add(panel->connector, mode);
+
+       panel->connector->display_info.width_mm = 95;
+       panel->connector->display_info.height_mm = 151;
+
+       return 1;
+}
+
+static int dsi_dcs_bl_get_brightness(struct backlight_device *bl)
+{
+       struct mipi_dsi_device *dsi = bl_get_data(bl);
+       int ret;
+       u16 brightness = bl->props.brightness;
+
+       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness);
+       if (ret < 0)
+               return ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       return brightness & 0xff;
+}
+
+static int dsi_dcs_bl_update_status(struct backlight_device *bl)
+{
+       struct mipi_dsi_device *dsi = bl_get_data(bl);
+       int ret;
+
+       dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
+
+       ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness);
+       if (ret < 0)
+               return ret;
+
+       dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+       return 0;
+}
+
+static const struct backlight_ops dsi_bl_ops = {
+       .update_status = dsi_dcs_bl_update_status,
+       .get_brightness = dsi_dcs_bl_get_brightness,
+};
+
+static struct backlight_device *
+drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi)
+{
+       struct device *dev = &dsi->dev;
+       struct backlight_properties props;
+
+       memset(&props, 0, sizeof(props));
+       props.type = BACKLIGHT_RAW;
+       props.brightness = 255;
+       props.max_brightness = 255;
+
+       return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
+                                             &dsi_bl_ops, &props);
+}
+
+static const struct drm_panel_funcs jdi_panel_funcs = {
+       .disable = jdi_panel_disable,
+       .unprepare = jdi_panel_unprepare,
+       .prepare = jdi_panel_prepare,
+       .enable = jdi_panel_enable,
+       .get_modes = jdi_panel_get_modes,
+};
+
+static const struct of_device_id jdi_of_match[] = {
+       { .compatible = "jdi,lt070me05000", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, jdi_of_match);
+
+static int jdi_panel_add(struct jdi_panel *jdi)
+{
+       struct device *dev = &jdi->dsi->dev;
+       int ret;
+       unsigned int i;
+
+       jdi->mode = &default_mode;
+
+       for (i = 0; i < ARRAY_SIZE(jdi->supplies); i++)
+               jdi->supplies[i].supply = regulator_names[i];
+
+       ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(jdi->supplies),
+                                     jdi->supplies);
+       if (ret < 0) {
+               dev_err(dev, "failed to init regulator, ret=%d\n", ret);
+               return ret;
+       }
+
+       jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+       if (IS_ERR(jdi->enable_gpio)) {
+               ret = PTR_ERR(jdi->enable_gpio);
+               dev_err(dev, "cannot get enable-gpio %d\n", ret);
+               return ret;
+       }
+
+       jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+       if (IS_ERR(jdi->reset_gpio)) {
+               ret = PTR_ERR(jdi->reset_gpio);
+               dev_err(dev, "cannot get reset-gpios %d\n", ret);
+               return ret;
+       }
+
+       jdi->dcdc_en_gpio = devm_gpiod_get(dev, "dcdc-en", GPIOD_OUT_LOW);
+       if (IS_ERR(jdi->dcdc_en_gpio)) {
+               ret = PTR_ERR(jdi->dcdc_en_gpio);
+               dev_err(dev, "cannot get dcdc-en-gpio %d\n", ret);
+               return ret;
+       }
+
+       jdi->backlight = drm_panel_create_dsi_backlight(jdi->dsi);
+       if (IS_ERR(jdi->backlight)) {
+               ret = PTR_ERR(jdi->backlight);
+               dev_err(dev, "failed to register backlight %d\n", ret);
+               return ret;
+       }
+
+       drm_panel_init(&jdi->base);
+       jdi->base.funcs = &jdi_panel_funcs;
+       jdi->base.dev = &jdi->dsi->dev;
+
+       ret = drm_panel_add(&jdi->base);
+
+       return ret;
+}
+
+static void jdi_panel_del(struct jdi_panel *jdi)
+{
+       if (jdi->base.dev)
+               drm_panel_remove(&jdi->base);
+}
+
+static int jdi_panel_probe(struct mipi_dsi_device *dsi)
+{
+       struct jdi_panel *jdi;
+       int ret;
+
+       dsi->lanes = 4;
+       dsi->format = MIPI_DSI_FMT_RGB888;
+       dsi->mode_flags =  MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
+                          MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+       jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL);
+       if (!jdi)
+               return -ENOMEM;
+
+       mipi_dsi_set_drvdata(dsi, jdi);
+
+       jdi->dsi = dsi;
+
+       ret = jdi_panel_add(jdi);
+       if (ret < 0)
+               return ret;
+
+       return mipi_dsi_attach(dsi);
+}
+
+static int jdi_panel_remove(struct mipi_dsi_device *dsi)
+{
+       struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+       int ret;
+
+       ret = jdi_panel_disable(&jdi->base);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to disable panel: %d\n", ret);
+
+       ret = mipi_dsi_detach(dsi);
+       if (ret < 0)
+               dev_err(&dsi->dev, "failed to detach from DSI host: %d\n",
+                       ret);
+
+       drm_panel_detach(&jdi->base);
+       jdi_panel_del(jdi);
+
+       return 0;
+}
+
+static void jdi_panel_shutdown(struct mipi_dsi_device *dsi)
+{
+       struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi);
+
+       jdi_panel_disable(&jdi->base);
+}
+
+static struct mipi_dsi_driver jdi_panel_driver = {
+       .driver = {
+               .name = "panel-jdi-lt070me05000",
+               .of_match_table = jdi_of_match,
+       },
+       .probe = jdi_panel_probe,
+       .remove = jdi_panel_remove,
+       .shutdown = jdi_panel_shutdown,
+};
+module_mipi_dsi_driver(jdi_panel_driver);
+
+MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
+MODULE_AUTHOR("Vinay Simha BN <simhavcs@gmail.com>");
+MODULE_DESCRIPTION("JDI LT070ME05000 WUXGA");
+MODULE_LICENSE("GPL v2");
index 85143d1..113db3c 100644 (file)
@@ -849,6 +849,34 @@ static const struct panel_desc innolux_at070tn92 = {
        .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
 };
 
+static const struct display_timing innolux_g101ice_l01_timing = {
+       .pixelclock = { 60400000, 71100000, 74700000 },
+       .hactive = { 1280, 1280, 1280 },
+       .hfront_porch = { 41, 80, 100 },
+       .hback_porch = { 40, 79, 99 },
+       .hsync_len = { 1, 1, 1 },
+       .vactive = { 800, 800, 800 },
+       .vfront_porch = { 5, 11, 14 },
+       .vback_porch = { 4, 11, 14 },
+       .vsync_len = { 1, 1, 1 },
+       .flags = DISPLAY_FLAGS_DE_HIGH,
+};
+
+static const struct panel_desc innolux_g101ice_l01 = {
+       .timings = &innolux_g101ice_l01_timing,
+       .num_timings = 1,
+       .bpc = 8,
+       .size = {
+               .width = 217,
+               .height = 135,
+       },
+       .delay = {
+               .enable = 200,
+               .disable = 200,
+       },
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+};
+
 static const struct drm_display_mode innolux_g121i1_l01_mode = {
        .clock = 71000,
        .hdisplay = 1280,
@@ -1186,7 +1214,7 @@ static const struct panel_desc olimex_lcd_olinuxino_43ts = {
                .width = 105,
                .height = 67,
        },
-       .bus_format = MEDIA_BUS_FMT_RGB666_1X18,
+       .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
 };
 
 /*
@@ -1245,6 +1273,7 @@ static const struct panel_desc ortustech_com43h4m85ulc = {
                .height = 93,
        },
        .bus_format = MEDIA_BUS_FMT_RGB888_1X24,
+       .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_POSEDGE,
 };
 
 static const struct drm_display_mode qd43003c0_40_mode = {
@@ -1384,6 +1413,11 @@ static const struct panel_desc sharp_lq123p1jx31 = {
                .width = 259,
                .height = 173,
        },
+       .delay = {
+               .prepare = 110,
+               .enable = 50,
+               .unprepare = 550,
+       },
 };
 
 static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = {
@@ -1430,6 +1464,11 @@ static const struct panel_desc starry_kr122ea0sra = {
                .width = 263,
                .height = 164,
        },
+       .delay = {
+               .prepare = 10 + 200,
+               .enable = 50,
+               .unprepare = 10 + 500,
+       },
 };
 
 static const struct drm_display_mode tpk_f07a_0102_mode = {
@@ -1574,6 +1613,9 @@ static const struct of_device_id platform_of_match[] = {
        }, {
                .compatible = "innolux,at070tn92",
                .data = &innolux_at070tn92,
+       }, {
+               .compatible ="innolux,g101ice-l01",
+               .data = &innolux_g101ice_l01
        }, {
                .compatible ="innolux,g121i1-l01",
                .data = &innolux_g121i1_l01
index 3aef127..a61c0d4 100644 (file)
@@ -211,6 +211,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc)
        struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc);
 
        drm_crtc_cleanup(crtc);
+       qxl_bo_unref(&qxl_crtc->cursor_bo);
        kfree(qxl_crtc);
 }
 
@@ -296,6 +297,52 @@ qxl_hide_cursor(struct qxl_device *qdev)
        return 0;
 }
 
+static int qxl_crtc_apply_cursor(struct drm_crtc *crtc)
+{
+       struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct qxl_device *qdev = dev->dev_private;
+       struct qxl_cursor_cmd *cmd;
+       struct qxl_release *release;
+       int ret = 0;
+
+       if (!qcrtc->cursor_bo)
+               return 0;
+
+       ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd),
+                                        QXL_RELEASE_CURSOR_CMD,
+                                        &release, NULL);
+       if (ret)
+               return ret;
+
+       ret = qxl_release_list_add(release, qcrtc->cursor_bo);
+       if (ret)
+               goto out_free_release;
+
+       ret = qxl_release_reserve_list(release, false);
+       if (ret)
+               goto out_free_release;
+
+       cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
+       cmd->type = QXL_CURSOR_SET;
+       cmd->u.set.position.x = qcrtc->cur_x + qcrtc->hot_spot_x;
+       cmd->u.set.position.y = qcrtc->cur_y + qcrtc->hot_spot_y;
+
+       cmd->u.set.shape = qxl_bo_physical_address(qdev, qcrtc->cursor_bo, 0);
+
+       cmd->u.set.visible = 1;
+       qxl_release_unmap(qdev, release, &cmd->release_info);
+
+       qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
+       qxl_release_fence_buffer_objects(release);
+
+       return ret;
+
+out_free_release:
+       qxl_release_free(qdev, release);
+       return ret;
+}
+
 static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
                                struct drm_file *file_priv,
                                uint32_t handle,
@@ -400,7 +447,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
        }
        drm_gem_object_unreference_unlocked(obj);
 
-       qxl_bo_unref(&cursor_bo);
+       qxl_bo_unref (&qcrtc->cursor_bo);
+       qcrtc->cursor_bo = cursor_bo;
 
        return ret;
 
@@ -655,6 +703,12 @@ static int qxl_crtc_mode_set(struct drm_crtc *crtc,
                           bo->surf.stride, bo->surf.format);
                qxl_io_create_primary(qdev, 0, bo);
                bo->is_primary = true;
+
+               ret = qxl_crtc_apply_cursor(crtc);
+               if (ret) {
+                       DRM_ERROR("could not set cursor after modeset");
+                       ret = 0;
+               }
        }
 
        if (bo->is_primary) {
index ffe8853..9b728ed 100644 (file)
@@ -57,11 +57,8 @@ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
 static int
 alloc_drawable(struct qxl_device *qdev, struct qxl_release **release)
 {
-       int ret;
-       ret = qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable),
-                                        QXL_RELEASE_DRAWABLE, release,
-                                        NULL);
-       return ret;
+       return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable),
+                                         QXL_RELEASE_DRAWABLE, release, NULL);
 }
 
 static void
index 8e633ca..5f3e5ad 100644 (file)
@@ -137,6 +137,7 @@ struct qxl_crtc {
        int cur_y;
        int hot_spot_x;
        int hot_spot_y;
+       struct qxl_bo *cursor_bo;
 };
 
 struct qxl_output {
index 28c1423..2cd879a 100644 (file)
@@ -24,7 +24,6 @@
  *     David Airlie
  */
 #include <linux/module.h>
-#include <linux/fb.h>
 
 #include "drmP.h"
 #include "drm/drm.h"
index 5e1d789..fa5440d 100644 (file)
@@ -61,7 +61,7 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
        if (domain == QXL_GEM_DOMAIN_VRAM)
                qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
        if (domain == QXL_GEM_DOMAIN_SURFACE)
-               qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag;
+               qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV | pflag;
        if (domain == QXL_GEM_DOMAIN_CPU)
                qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag;
        if (!c)
@@ -151,7 +151,7 @@ void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev,
 
        if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
                map = qdev->vram_mapping;
-       else if (bo->tbo.mem.mem_type == TTM_PL_PRIV0)
+       else if (bo->tbo.mem.mem_type == TTM_PL_PRIV)
                map = qdev->surface_mapping;
        else
                goto fallback;
@@ -191,7 +191,7 @@ void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev,
 
        if (bo->tbo.mem.mem_type == TTM_PL_VRAM)
                map = qdev->vram_mapping;
-       else if (bo->tbo.mem.mem_type == TTM_PL_PRIV0)
+       else if (bo->tbo.mem.mem_type == TTM_PL_PRIV)
                map = qdev->surface_mapping;
        else
                goto fallback;
@@ -311,7 +311,7 @@ int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo)
 
 int qxl_surf_evict(struct qxl_device *qdev)
 {
-       return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
+       return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV);
 }
 
 int qxl_vram_evict(struct qxl_device *qdev)
index f599cd0..cd83f05 100644 (file)
@@ -203,12 +203,9 @@ qxl_release_free(struct qxl_device *qdev,
 static int qxl_release_bo_alloc(struct qxl_device *qdev,
                                struct qxl_bo **bo)
 {
-       int ret;
        /* pin releases bo's they are too messy to evict */
-       ret = qxl_bo_create(qdev, PAGE_SIZE, false, true,
-                           QXL_GEM_DOMAIN_VRAM, NULL,
-                           bo);
-       return ret;
+       return qxl_bo_create(qdev, PAGE_SIZE, false, true,
+                            QXL_GEM_DOMAIN_VRAM, NULL, bo);
 }
 
 int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo)
index d50c967..e26c82d 100644 (file)
@@ -168,7 +168,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
                man->default_caching = TTM_PL_FLAG_CACHED;
                break;
        case TTM_PL_VRAM:
-       case TTM_PL_PRIV0:
+       case TTM_PL_PRIV:
                /* "On-card" video ram */
                man->func = &ttm_bo_manager_func;
                man->gpu_offset = 0;
@@ -210,7 +210,8 @@ static int qxl_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 {
        struct qxl_bo *qbo = to_qxl_bo(bo);
 
-       return drm_vma_node_verify_access(&qbo->gem_base.vma_node, filp);
+       return drm_vma_node_verify_access(&qbo->gem_base.vma_node,
+                                         filp->private_data);
 }
 
 static int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
@@ -235,7 +236,7 @@ static int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
                mem->bus.base = qdev->vram_base;
                mem->bus.offset = mem->start << PAGE_SHIFT;
                break;
-       case TTM_PL_PRIV0:
+       case TTM_PL_PRIV:
                mem->bus.is_iomem = true;
                mem->bus.base = qdev->surfaceram_base;
                mem->bus.offset = mem->start << PAGE_SHIFT;
@@ -361,8 +362,8 @@ static int qxl_bo_move(struct ttm_buffer_object *bo,
                qxl_move_null(bo, new_mem);
                return 0;
        }
-       return ttm_bo_move_memcpy(bo, evict, interruptible,
-                                 no_wait_gpu, new_mem);
+       return ttm_bo_move_memcpy(bo, interruptible, no_wait_gpu,
+                                 new_mem);
 }
 
 static void qxl_bo_move_notify(struct ttm_buffer_object *bo,
@@ -376,7 +377,7 @@ static void qxl_bo_move_notify(struct ttm_buffer_object *bo,
        qbo = to_qxl_bo(bo);
        qdev = qbo->gem_base.dev->dev_private;
 
-       if (bo->mem.mem_type == TTM_PL_PRIV0 && qbo->surface_id)
+       if (bo->mem.mem_type == TTM_PL_PRIV && qbo->surface_id)
                qxl_surface_evict(qdev, qbo, new_mem ? true : false);
 }
 
@@ -422,7 +423,7 @@ int qxl_ttm_init(struct qxl_device *qdev)
                DRM_ERROR("Failed initializing VRAM heap.\n");
                return r;
        }
-       r = ttm_bo_init_mm(&qdev->mman.bdev, TTM_PL_PRIV0,
+       r = ttm_bo_init_mm(&qdev->mman.bdev, TTM_PL_PRIV,
                           qdev->surfaceram_size / PAGE_SIZE);
        if (r) {
                DRM_ERROR("Failed initializing Surfaces heap.\n");
@@ -445,7 +446,7 @@ int qxl_ttm_init(struct qxl_device *qdev)
 void qxl_ttm_fini(struct qxl_device *qdev)
 {
        ttm_bo_clean_mm(&qdev->mman.bdev, TTM_PL_VRAM);
-       ttm_bo_clean_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
+       ttm_bo_clean_mm(&qdev->mman.bdev, TTM_PL_PRIV);
        ttm_bo_device_release(&qdev->mman.bdev);
        qxl_ttm_global_fini(qdev);
        DRM_INFO("qxl: ttm finalized\n");
@@ -489,7 +490,7 @@ static int qxl_ttm_debugfs_init(struct qxl_device *qdev)
                if (i == 0)
                        qxl_mem_types_list[i].data = qdev->mman.bdev.man[TTM_PL_VRAM].priv;
                else
-                       qxl_mem_types_list[i].data = qdev->mman.bdev.man[TTM_PL_PRIV0].priv;
+                       qxl_mem_types_list[i].data = qdev->mman.bdev.man[TTM_PL_PRIV].priv;
 
        }
        return qxl_debugfs_add_files(qdev, qxl_mem_types_list, i);
index c57b4de..a982be5 100644 (file)
@@ -56,7 +56,7 @@ static const struct file_operations r128_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features =
-           DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
+           DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | DRIVER_LEGACY |
            DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,
        .dev_priv_size = sizeof(drm_r128_buf_priv_t),
        .load = r128_driver_load,
index 1dcf390..74f99ba 100644 (file)
@@ -1156,6 +1156,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
        u32 tmp, viewport_w, viewport_h;
        int r;
        bool bypass_lut = false;
+       char *format_name;
 
        /* no fb bound */
        if (!atomic && !crtc->primary->fb) {
@@ -1259,8 +1260,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
                bypass_lut = true;
                break;
        default:
-               DRM_ERROR("Unsupported screen format %s\n",
-                         drm_get_format_name(target_fb->pixel_format));
+               format_name = drm_get_format_name(target_fb->pixel_format);
+               DRM_ERROR("Unsupported screen format %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
@@ -1435,8 +1437,8 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
        WREG32(EVERGREEN_VIEWPORT_SIZE + radeon_crtc->crtc_offset,
               (viewport_w << 16) | viewport_h);
 
-       /* set pageflip to happen only at start of vblank interval (front porch) */
-       WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 3);
+       /* set pageflip to happen anywhere in vblank interval */
+       WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0);
 
        if (!atomic && fb && fb != crtc->primary->fb) {
                radeon_fb = to_radeon_framebuffer(fb);
@@ -1471,6 +1473,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
        u32 viewport_w, viewport_h;
        int r;
        bool bypass_lut = false;
+       char *format_name;
 
        /* no fb bound */
        if (!atomic && !crtc->primary->fb) {
@@ -1560,8 +1563,9 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc,
                bypass_lut = true;
                break;
        default:
-               DRM_ERROR("Unsupported screen format %s\n",
-                         drm_get_format_name(target_fb->pixel_format));
+               format_name = drm_get_format_name(target_fb->pixel_format);
+               DRM_ERROR("Unsupported screen format %s\n", format_name);
+               kfree(format_name);
                return -EINVAL;
        }
 
index cead089..432cb46 100644 (file)
@@ -389,22 +389,21 @@ bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
 {
        struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
        u8 msg[DP_DPCD_SIZE];
-       int ret, i;
+       int ret;
 
-       for (i = 0; i < 7; i++) {
-               ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
-                                      DP_DPCD_SIZE);
-               if (ret == DP_DPCD_SIZE) {
-                       memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
+       ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_DPCD_REV, msg,
+                              DP_DPCD_SIZE);
+       if (ret == DP_DPCD_SIZE) {
+               memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
 
-                       DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
-                                     dig_connector->dpcd);
+               DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
+                             dig_connector->dpcd);
 
-                       radeon_dp_probe_oui(radeon_connector);
+               radeon_dp_probe_oui(radeon_connector);
 
-                       return true;
-               }
+               return true;
        }
+
        dig_connector->dpcd[0] = 0;
        return false;
 }
index 0c1b9ff..f6ff41a 100644 (file)
@@ -1871,7 +1871,7 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
 {
        const __be32 *fw_data = NULL;
        const __le32 *new_fw_data = NULL;
-       u32 running, blackout = 0, tmp;
+       u32 running, tmp;
        u32 *io_mc_regs = NULL;
        const __le32 *new_io_mc_regs = NULL;
        int i, regs_size, ucode_size;
@@ -1912,11 +1912,6 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
        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);
@@ -1964,9 +1959,6 @@ int ci_mc_load_microcode(struct radeon_device *rdev)
                                break;
                        udelay(1);
                }
-
-               if (running)
-                       WREG32(MC_SHARED_BLACKOUT_CNTL, blackout);
        }
 
        return 0;
@@ -4201,11 +4193,7 @@ u32 cik_gfx_get_rptr(struct radeon_device *rdev,
 u32 cik_gfx_get_wptr(struct radeon_device *rdev,
                     struct radeon_ring *ring)
 {
-       u32 wptr;
-
-       wptr = RREG32(CP_RB0_WPTR);
-
-       return wptr;
+       return RREG32(CP_RB0_WPTR);
 }
 
 void cik_gfx_set_wptr(struct radeon_device *rdev,
@@ -8215,7 +8203,7 @@ static void cik_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index cead228..48db935 100644 (file)
 #define UVD_UDEC_ADDR_CONFIG           0xef4c
 #define UVD_UDEC_DB_ADDR_CONFIG                0xef50
 #define UVD_UDEC_DBW_ADDR_CONFIG       0xef54
+#define UVD_NO_OP                      0xeffc
 
 #define UVD_LMI_EXT40_ADDR             0xf498
 #define UVD_GP_SCRATCH4                        0xf4e0
index db275b7..0b6b576 100644 (file)
@@ -2878,9 +2878,8 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s
        for (i = 0; i < rdev->num_crtc; i++) {
                if (save->crtc_enabled[i]) {
                        tmp = RREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i]);
-                       if ((tmp & 0x7) != 3) {
+                       if ((tmp & 0x7) != 0) {
                                tmp &= ~0x7;
-                               tmp |= 0x3;
                                WREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i], tmp);
                        }
                        tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]);
@@ -5580,7 +5579,7 @@ static void evergreen_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index c8e3d39..f3d88ca 100644 (file)
 #define UVD_UDEC_ADDR_CONFIG                           0xef4c
 #define UVD_UDEC_DB_ADDR_CONFIG                                0xef50
 #define UVD_UDEC_DBW_ADDR_CONFIG                       0xef54
+#define UVD_NO_OP                                      0xeffc
 #define UVD_RBC_RB_RPTR                                        0xf690
 #define UVD_RBC_RB_WPTR                                        0xf694
 #define UVD_STATUS                                     0xf6bc
index 4a3d7ca..103fc86 100644 (file)
@@ -2062,7 +2062,7 @@ static void cayman_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index 47eb49b..3c9fec8 100644 (file)
 #define UVD_UDEC_ADDR_CONFIG                           0xEF4C
 #define UVD_UDEC_DB_ADDR_CONFIG                                0xEF50
 #define UVD_UDEC_DBW_ADDR_CONFIG                       0xEF54
+#define UVD_NO_OP                                      0xEFFC
 #define UVD_RBC_RB_RPTR                                        0xF690
 #define UVD_RBC_RB_WPTR                                        0xF694
 #define UVD_STATUS                                     0xf6bc
index f25994b..f5e84f4 100644 (file)
@@ -1071,11 +1071,7 @@ u32 r100_gfx_get_rptr(struct radeon_device *rdev,
 u32 r100_gfx_get_wptr(struct radeon_device *rdev,
                      struct radeon_ring *ring)
 {
-       u32 wptr;
-
-       wptr = RREG32(RADEON_CP_RB_WPTR);
-
-       return wptr;
+       return RREG32(RADEON_CP_RB_WPTR);
 }
 
 void r100_gfx_set_wptr(struct radeon_device *rdev,
index 9247e7d..a951881 100644 (file)
@@ -2631,11 +2631,7 @@ u32 r600_gfx_get_rptr(struct radeon_device *rdev,
 u32 r600_gfx_get_wptr(struct radeon_device *rdev,
                      struct radeon_ring *ring)
 {
-       u32 wptr;
-
-       wptr = RREG32(R600_CP_RB_WPTR);
-
-       return wptr;
+       return RREG32(R600_CP_RB_WPTR);
 }
 
 void r600_gfx_set_wptr(struct radeon_device *rdev,
@@ -3097,7 +3093,7 @@ static void r600_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index 1e8495c..2e00a52 100644 (file)
 #define UVD_GPCOM_VCPU_DATA0                           0xef10
 #define UVD_GPCOM_VCPU_DATA1                           0xef14
 #define UVD_ENGINE_CNTL                                        0xef18
+#define UVD_NO_OP                                      0xeffc
 
 #define UVD_SEMA_CNTL                                  0xf400
 #define UVD_RB_ARB_CTRL                                        0xf480
index 5633ee3..1b0dcad 100644 (file)
@@ -742,6 +742,7 @@ struct radeon_flip_work {
        struct work_struct              unpin_work;
        struct radeon_device            *rdev;
        int                             crtc_id;
+       u32                             target_vblank;
        uint64_t                        base;
        struct drm_pending_vblank_event *event;
        struct radeon_bo                *old_rbo;
index 31c9a92..6efbd65 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/acpi.h>
 #include <linux/slab.h>
 #include <linux/power_supply.h>
+#include <linux/pm_runtime.h>
 #include <acpi/video.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include "radeon_acpi.h"
 #include "atom.h"
 
+#if defined(CONFIG_VGA_SWITCHEROO)
+bool radeon_atpx_dgpu_req_power_for_displays(void);
+#else
+static inline bool radeon_atpx_dgpu_req_power_for_displays(void) { return false; }
+#endif
+
 #define ACPI_AC_CLASS           "ac_adapter"
 
 extern void radeon_pm_acpi_event_handler(struct radeon_device *rdev);
@@ -394,6 +401,16 @@ int radeon_atif_handler(struct radeon_device *rdev,
 #endif
                }
        }
+       if (req.pending & ATIF_DGPU_DISPLAY_EVENT) {
+               if ((rdev->flags & RADEON_IS_PX) &&
+                   radeon_atpx_dgpu_req_power_for_displays()) {
+                       pm_runtime_get_sync(rdev->ddev->dev);
+                       /* Just fire off a uevent and let userspace tell us what to do */
+                       drm_helper_hpd_irq_event(rdev->ddev);
+                       pm_runtime_mark_last_busy(rdev->ddev->dev);
+                       pm_runtime_put_autosuspend(rdev->ddev->dev);
+               }
+       }
        /* TODO: check other events */
 
        /* We've handled the event, stop the notifier chain. The ACPI interface
index ddef0d4..2fdcd04 100644 (file)
@@ -29,6 +29,7 @@ struct radeon_atpx {
        acpi_handle handle;
        struct radeon_atpx_functions functions;
        bool is_hybrid;
+       bool dgpu_req_power_for_displays;
 };
 
 static struct radeon_atpx_priv {
@@ -72,6 +73,10 @@ bool radeon_is_atpx_hybrid(void) {
        return radeon_atpx_priv.atpx.is_hybrid;
 }
 
+bool radeon_atpx_dgpu_req_power_for_displays(void) {
+       return radeon_atpx_priv.atpx.dgpu_req_power_for_displays;
+}
+
 /**
  * radeon_atpx_call - call an ATPX method
  *
index b79f3b0..50e96d2 100644 (file)
@@ -198,12 +198,12 @@ int radeon_get_monitor_bpc(struct drm_connector *connector)
                }
 
                /* Any defined maximum tmds clock limit we must not exceed? */
-               if (connector->max_tmds_clock > 0) {
+               if (connector->display_info.max_tmds_clock > 0) {
                        /* mode_clock is clock in kHz for mode to be modeset on this connector */
                        mode_clock = radeon_connector->pixelclock_for_modeset;
 
                        /* Maximum allowable input clock in kHz */
-                       max_tmds_clock = connector->max_tmds_clock * 1000;
+                       max_tmds_clock = connector->display_info.max_tmds_clock;
 
                        DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n",
                                          connector->name, mode_clock, max_tmds_clock);
index a00dd2f..eb92aef 100644 (file)
@@ -639,7 +639,7 @@ void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
  * Used at driver startup.
  * Returns true if virtual or false if not.
  */
-static bool radeon_device_is_virtual(void)
+bool radeon_device_is_virtual(void)
 {
 #ifdef CONFIG_X86
        return boot_cpu_has(X86_FEATURE_HYPERVISOR);
@@ -661,8 +661,9 @@ bool radeon_card_posted(struct radeon_device *rdev)
 {
        uint32_t reg;
 
-       /* for pass through, always force asic_init */
-       if (radeon_device_is_virtual())
+       /* for pass through, always force asic_init for CI */
+       if (rdev->family >= CHIP_BONAIRE &&
+           radeon_device_is_virtual())
                return false;
 
        /* required for EFI mode on macbook2,1 which uses an r5xx asic */
@@ -1956,14 +1957,3 @@ static void radeon_debugfs_remove_files(struct radeon_device *rdev)
        }
 #endif
 }
-
-#if defined(CONFIG_DEBUG_FS)
-int radeon_debugfs_init(struct drm_minor *minor)
-{
-       return 0;
-}
-
-void radeon_debugfs_cleanup(struct drm_minor *minor)
-{
-}
-#endif
index c3206fb..b8ab30a 100644 (file)
@@ -321,16 +321,30 @@ void radeon_crtc_handle_vblank(struct radeon_device *rdev, int crtc_id)
        update_pending = radeon_page_flip_pending(rdev, crtc_id);
 
        /* Has the pageflip already completed in crtc, or is it certain
-        * to complete in this vblank?
+        * to complete in this vblank? GET_DISTANCE_TO_VBLANKSTART provides
+        * distance to start of "fudged earlier" vblank in vpos, distance to
+        * start of real vblank in hpos. vpos >= 0 && hpos < 0 means we are in
+        * the last few scanlines before start of real vblank, where the vblank
+        * irq can fire, so we have sampled update_pending a bit too early and
+        * know the flip will complete at leading edge of the upcoming real
+        * vblank. On pre-AVIVO hardware, flips also complete inside the real
+        * vblank, not only at leading edge, so if update_pending for hpos >= 0
+        *  == inside real vblank, the flip will complete almost immediately.
+        * Note that this method of completion handling is still not 100% race
+        * free, as we could execute before the radeon_flip_work_func managed
+        * to run and set the RADEON_FLIP_SUBMITTED status, thereby we no-op,
+        * but the flip still gets programmed into hw and completed during
+        * vblank, leading to a delayed emission of the flip completion event.
+        * This applies at least to pre-AVIVO hardware, where flips are always
+        * completing inside vblank, not only at leading edge of vblank.
         */
        if (update_pending &&
-           (DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev,
-                                                              crtc_id,
-                                                              USE_REAL_VBLANKSTART,
-                                                              &vpos, &hpos, NULL, NULL,
-                                                              &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
-           ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
-            (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) {
+           (DRM_SCANOUTPOS_VALID &
+            radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id,
+                                       GET_DISTANCE_TO_VBLANKSTART,
+                                       &vpos, &hpos, NULL, NULL,
+                                       &rdev->mode_info.crtcs[crtc_id]->base.hwmode)) &&
+           ((vpos >= 0 && hpos < 0) || (hpos >= 0 && !ASIC_IS_AVIVO(rdev)))) {
                /* crtc didn't flip in this target vblank interval,
                 * but flip is pending in crtc. Based on the current
                 * scanout position we know that the current frame is
@@ -400,14 +414,13 @@ static void radeon_flip_work_func(struct work_struct *__work)
        struct radeon_flip_work *work =
                container_of(__work, struct radeon_flip_work, flip_work);
        struct radeon_device *rdev = work->rdev;
+       struct drm_device *dev = rdev->ddev;
        struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[work->crtc_id];
 
        struct drm_crtc *crtc = &radeon_crtc->base;
        unsigned long flags;
        int r;
-       int vpos, hpos, stat, min_udelay = 0;
-       unsigned repcnt = 4;
-       struct drm_vblank_crtc *vblank = &crtc->dev->vblank[work->crtc_id];
+       int vpos, hpos;
 
        down_read(&rdev->exclusive_lock);
        if (work->fence) {
@@ -438,59 +451,28 @@ static void radeon_flip_work_func(struct work_struct *__work)
                work->fence = NULL;
        }
 
+       /* Wait until we're out of the vertical blank period before the one
+        * targeted by the flip. Always wait on pre DCE4 to avoid races with
+        * flip completion handling from vblank irq, as these old asics don't
+        * have reliable pageflip completion interrupts.
+        */
+       while (radeon_crtc->enabled &&
+               (radeon_get_crtc_scanoutpos(dev, work->crtc_id, 0,
+                                           &vpos, &hpos, NULL, NULL,
+                                           &crtc->hwmode)
+               & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
+               (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
+               (!ASIC_IS_AVIVO(rdev) ||
+               ((int) (work->target_vblank -
+               dev->driver->get_vblank_counter(dev, work->crtc_id)) > 0)))
+               usleep_range(1000, 2000);
+
        /* We borrow the event spin lock for protecting flip_status */
        spin_lock_irqsave(&crtc->dev->event_lock, flags);
 
        /* set the proper interrupt */
        radeon_irq_kms_pflip_irq_get(rdev, radeon_crtc->crtc_id);
 
-       /* If this happens to execute within the "virtually extended" vblank
-        * interval before the start of the real vblank interval then it needs
-        * to delay programming the mmio flip until the real vblank is entered.
-        * This prevents completing a flip too early due to the way we fudge
-        * our vblank counter and vblank timestamps in order to work around the
-        * problem that the hw fires vblank interrupts before actual start of
-        * vblank (when line buffer refilling is done for a frame). It
-        * complements the fudging logic in radeon_get_crtc_scanoutpos() for
-        * timestamping and radeon_get_vblank_counter_kms() for vblank counts.
-        *
-        * In practice this won't execute very often unless on very fast
-        * machines because the time window for this to happen is very small.
-        */
-       while (radeon_crtc->enabled && --repcnt) {
-               /* GET_DISTANCE_TO_VBLANKSTART returns distance to real vblank
-                * start in hpos, and to the "fudged earlier" vblank start in
-                * vpos.
-                */
-               stat = radeon_get_crtc_scanoutpos(rdev->ddev, work->crtc_id,
-                                                 GET_DISTANCE_TO_VBLANKSTART,
-                                                 &vpos, &hpos, NULL, NULL,
-                                                 &crtc->hwmode);
-
-               if ((stat & (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE)) !=
-                   (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_ACCURATE) ||
-                   !(vpos >= 0 && hpos <= 0))
-                       break;
-
-               /* Sleep at least until estimated real start of hw vblank */
-               min_udelay = (-hpos + 1) * max(vblank->linedur_ns / 1000, 5);
-               if (min_udelay > vblank->framedur_ns / 2000) {
-                       /* Don't wait ridiculously long - something is wrong */
-                       repcnt = 0;
-                       break;
-               }
-               spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
-               usleep_range(min_udelay, 2 * min_udelay);
-               spin_lock_irqsave(&crtc->dev->event_lock, flags);
-       };
-
-       if (!repcnt)
-               DRM_DEBUG_DRIVER("Delay problem on crtc %d: min_udelay %d, "
-                                "framedur %d, linedur %d, stat %d, vpos %d, "
-                                "hpos %d\n", work->crtc_id, min_udelay,
-                                vblank->framedur_ns / 1000,
-                                vblank->linedur_ns / 1000, stat, vpos, hpos);
-
        /* do the flip (mmio) */
        radeon_page_flip(rdev, radeon_crtc->crtc_id, work->base, work->async);
 
@@ -499,10 +481,11 @@ static void radeon_flip_work_func(struct work_struct *__work)
        up_read(&rdev->exclusive_lock);
 }
 
-static int radeon_crtc_page_flip(struct drm_crtc *crtc,
-                                struct drm_framebuffer *fb,
-                                struct drm_pending_vblank_event *event,
-                                uint32_t page_flip_flags)
+static int radeon_crtc_page_flip_target(struct drm_crtc *crtc,
+                                       struct drm_framebuffer *fb,
+                                       struct drm_pending_vblank_event *event,
+                                       uint32_t page_flip_flags,
+                                       uint32_t target)
 {
        struct drm_device *dev = crtc->dev;
        struct radeon_device *rdev = dev->dev_private;
@@ -599,12 +582,8 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
                base &= ~7;
        }
        work->base = base;
-
-       r = drm_crtc_vblank_get(crtc);
-       if (r) {
-               DRM_ERROR("failed to get vblank before flip\n");
-               goto pflip_cleanup;
-       }
+       work->target_vblank = target - drm_crtc_vblank_count(crtc) +
+               dev->driver->get_vblank_counter(dev, work->crtc_id);
 
        /* We borrow the event spin lock for protecting flip_work */
        spin_lock_irqsave(&crtc->dev->event_lock, flags);
@@ -613,7 +592,7 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
                DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
                spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
                r = -EBUSY;
-               goto vblank_cleanup;
+               goto pflip_cleanup;
        }
        radeon_crtc->flip_status = RADEON_FLIP_PENDING;
        radeon_crtc->flip_work = work;
@@ -626,9 +605,6 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
        queue_work(radeon_crtc->flip_queue, &work->flip_work);
        return 0;
 
-vblank_cleanup:
-       drm_crtc_vblank_put(crtc);
-
 pflip_cleanup:
        if (unlikely(radeon_bo_reserve(new_rbo, false) != 0)) {
                DRM_ERROR("failed to reserve new rbo in error path\n");
@@ -697,7 +673,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
        .gamma_set = radeon_crtc_gamma_set,
        .set_config = radeon_crtc_set_config,
        .destroy = radeon_crtc_destroy,
-       .page_flip = radeon_crtc_page_flip,
+       .page_flip_target = radeon_crtc_page_flip_target,
 };
 
 static void radeon_crtc_init(struct drm_device *dev, int index)
index db64e00..2d46564 100644 (file)
@@ -164,7 +164,6 @@ radeon_dp_aux_transfer_native(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg
        }
 
        if (tmp & AUX_SW_RX_TIMEOUT) {
-               DRM_DEBUG_KMS("dp_aux_ch timed out\n");
                ret = -ETIMEDOUT;
                goto done;
        }
index c01a7c6..91c8f43 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/vga_switcheroo.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_fb_helper.h>
 
 #include "drm_crtc_helper.h"
 #include "radeon_kfd.h"
  *   2.44.0 - SET_APPEND_CNT packet3 support
  *   2.45.0 - Allow setting shader registers using DMA/COPY packet3 on SI
  *   2.46.0 - Add PFP_SYNC_ME support on evergreen
+ *   2.47.0 - Add UVD_NO_OP register support
  */
 #define KMS_DRIVER_MAJOR       2
-#define KMS_DRIVER_MINOR       46
+#define KMS_DRIVER_MINOR       47
 #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);
@@ -154,11 +156,6 @@ void radeon_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
 extern long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd,
                                    unsigned long arg);
 
-#if defined(CONFIG_DEBUG_FS)
-int radeon_debugfs_init(struct drm_minor *minor);
-void radeon_debugfs_cleanup(struct drm_minor *minor);
-#endif
-
 /* atpx handler */
 #if defined(CONFIG_VGA_SWITCHEROO)
 void radeon_register_atpx_handler(void);
@@ -309,6 +306,8 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
 
 static struct drm_driver kms_driver;
 
+bool radeon_device_is_virtual(void);
+
 static int radeon_kick_out_firmware_fb(struct pci_dev *pdev)
 {
        struct apertures_struct *ap;
@@ -324,7 +323,7 @@ static int radeon_kick_out_firmware_fb(struct pci_dev *pdev)
 #ifdef CONFIG_X86
        primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
 #endif
-       remove_conflicting_framebuffers(ap, "radeondrmfb", primary);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "radeondrmfb", primary);
        kfree(ap);
 
        return 0;
@@ -362,6 +361,17 @@ radeon_pci_remove(struct pci_dev *pdev)
        drm_put_dev(dev);
 }
 
+static void
+radeon_pci_shutdown(struct pci_dev *pdev)
+{
+       /* if we are running in a VM, make sure the device
+        * torn down properly on reboot/shutdown.
+        * unfortunately we can't detect certain
+        * hypervisors so just do this all the time.
+        */
+       radeon_pci_remove(pdev);
+}
+
 static int radeon_pmops_suspend(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
@@ -373,6 +383,14 @@ static int radeon_pmops_resume(struct device *dev)
 {
        struct pci_dev *pdev = to_pci_dev(dev);
        struct drm_device *drm_dev = pci_get_drvdata(pdev);
+
+       /* GPU comes up enabled by the bios on resume */
+       if (radeon_is_px(drm_dev)) {
+               pm_runtime_disable(dev);
+               pm_runtime_set_active(dev);
+               pm_runtime_enable(dev);
+       }
+
        return radeon_resume_kms(drm_dev, true, true);
 }
 
@@ -529,10 +547,6 @@ static struct drm_driver kms_driver = {
        .disable_vblank = radeon_disable_vblank_kms,
        .get_vblank_timestamp = radeon_get_vblank_timestamp_kms,
        .get_scanout_position = radeon_get_crtc_scanoutpos,
-#if defined(CONFIG_DEBUG_FS)
-       .debugfs_init = radeon_debugfs_init,
-       .debugfs_cleanup = radeon_debugfs_cleanup,
-#endif
        .irq_preinstall = radeon_driver_irq_preinstall_kms,
        .irq_postinstall = radeon_driver_irq_postinstall_kms,
        .irq_uninstall = radeon_driver_irq_uninstall_kms,
@@ -574,6 +588,7 @@ static struct pci_driver radeon_kms_pci_driver = {
        .id_table = pciidlist,
        .probe = radeon_pci_probe,
        .remove = radeon_pci_remove,
+       .shutdown = radeon_pci_shutdown,
        .driver.pm = &radeon_pm_ops,
 };
 
index 0e3143a..0daad44 100644 (file)
@@ -25,7 +25,7 @@
  */
 #include <linux/module.h>
 #include <linux/slab.h>
-#include <linux/fb.h>
+#include <linux/pm_runtime.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -47,8 +47,35 @@ struct radeon_fbdev {
        struct radeon_device *rdev;
 };
 
+static int
+radeonfb_open(struct fb_info *info, int user)
+{
+       struct radeon_fbdev *rfbdev = info->par;
+       struct radeon_device *rdev = rfbdev->rdev;
+       int ret = pm_runtime_get_sync(rdev->ddev->dev);
+       if (ret < 0 && ret != -EACCES) {
+               pm_runtime_mark_last_busy(rdev->ddev->dev);
+               pm_runtime_put_autosuspend(rdev->ddev->dev);
+               return ret;
+       }
+       return 0;
+}
+
+static int
+radeonfb_release(struct fb_info *info, int user)
+{
+       struct radeon_fbdev *rfbdev = info->par;
+       struct radeon_device *rdev = rfbdev->rdev;
+
+       pm_runtime_mark_last_busy(rdev->ddev->dev);
+       pm_runtime_put_autosuspend(rdev->ddev->dev);
+       return 0;
+}
+
 static struct fb_ops radeonfb_ops = {
        .owner = THIS_MODULE,
+       .fb_open = radeonfb_open,
+       .fb_release = radeonfb_release,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
        .fb_fillrect = drm_fb_helper_cfb_fillrect,
@@ -383,7 +410,7 @@ void radeon_fbdev_fini(struct radeon_device *rdev)
 void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
 {
        if (rdev->mode_info.rfbdev)
-               fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state);
+               drm_fb_helper_set_suspend(&rdev->mode_info.rfbdev->helper, state);
 }
 
 bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
index 9590bcd..021aa00 100644 (file)
@@ -938,10 +938,8 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                         "Radeon i2c hw bus %s", name);
                i2c->adapter.algo = &radeon_i2c_algo;
                ret = i2c_add_adapter(&i2c->adapter);
-               if (ret) {
-                       DRM_ERROR("Failed to register hw i2c %s\n", name);
+               if (ret)
                        goto out_free;
-               }
        } else if (rec->hw_capable &&
                   radeon_hw_i2c &&
                   ASIC_IS_DCE3(rdev)) {
@@ -950,10 +948,8 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
                         "Radeon i2c hw bus %s", name);
                i2c->adapter.algo = &radeon_atom_i2c_algo;
                ret = i2c_add_adapter(&i2c->adapter);
-               if (ret) {
-                       DRM_ERROR("Failed to register hw i2c %s\n", name);
+               if (ret)
                        goto out_free;
-               }
        } else {
                /* set the radeon bit adapter */
                snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
index 835563c..4388dde 100644 (file)
@@ -641,11 +641,11 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
        if (rdev->family >= CHIP_CAYMAN) {
                struct radeon_fpriv *fpriv;
                struct radeon_vm *vm;
-               int r;
 
                fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
                if (unlikely(!fpriv)) {
-                       return -ENOMEM;
+                       r = -ENOMEM;
+                       goto out_suspend;
                }
 
                if (rdev->accel_working) {
@@ -653,14 +653,14 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
                        r = radeon_vm_init(rdev, vm);
                        if (r) {
                                kfree(fpriv);
-                               return r;
+                               goto out_suspend;
                        }
 
                        r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false);
                        if (r) {
                                radeon_vm_fini(rdev, vm);
                                kfree(fpriv);
-                               return r;
+                               goto out_suspend;
                        }
 
                        /* map the ib pool buffer read only into
@@ -674,15 +674,16 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
                        if (r) {
                                radeon_vm_fini(rdev, vm);
                                kfree(fpriv);
-                               return r;
+                               goto out_suspend;
                        }
                }
                file_priv->driver_priv = fpriv;
        }
 
+out_suspend:
        pm_runtime_mark_last_busy(dev->dev);
        pm_runtime_put_autosuspend(dev->dev);
-       return 0;
+       return r;
 }
 
 /**
@@ -717,6 +718,8 @@ void radeon_driver_postclose_kms(struct drm_device *dev,
                kfree(fpriv);
                file_priv->driver_priv = NULL;
        }
+       pm_runtime_mark_last_busy(dev->dev);
+       pm_runtime_put_autosuspend(dev->dev);
 }
 
 /**
@@ -733,6 +736,8 @@ void radeon_driver_preclose_kms(struct drm_device *dev,
 {
        struct radeon_device *rdev = dev->dev_private;
 
+       pm_runtime_get_sync(dev->dev);
+
        mutex_lock(&rdev->gem.mutex);
        if (rdev->hyperz_filp == file_priv)
                rdev->hyperz_filp = NULL;
index c2e0a1c..4552682 100644 (file)
@@ -237,7 +237,8 @@ static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
 
        if (radeon_ttm_tt_has_userptr(bo->ttm))
                return -EPERM;
-       return drm_vma_node_verify_access(&rbo->gem_base.vma_node, filp);
+       return drm_vma_node_verify_access(&rbo->gem_base.vma_node,
+                                         filp->private_data);
 }
 
 static void radeon_move_null(struct ttm_buffer_object *bo,
@@ -346,7 +347,7 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
        if (unlikely(r)) {
                goto out_cleanup;
        }
-       r = ttm_bo_move_ttm(bo, true, interruptible, no_wait_gpu, new_mem);
+       r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, new_mem);
 out_cleanup:
        ttm_bo_mem_put(bo, &tmp_mem);
        return r;
@@ -379,7 +380,7 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
        if (unlikely(r)) {
                return r;
        }
-       r = ttm_bo_move_ttm(bo, true, interruptible, no_wait_gpu, &tmp_mem);
+       r = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, &tmp_mem);
        if (unlikely(r)) {
                goto out_cleanup;
        }
@@ -444,8 +445,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
 
        if (r) {
 memcpy:
-               r = ttm_bo_move_memcpy(bo, evict, interruptible,
-                                      no_wait_gpu, new_mem);
+               r = ttm_bo_move_memcpy(bo, interruptible, no_wait_gpu, new_mem);
                if (r) {
                        return r;
                }
index 73dfe01..0cd0e7b 100644 (file)
@@ -669,6 +669,7 @@ static int radeon_uvd_cs_reg(struct radeon_cs_parser *p,
                                return r;
                        break;
                case UVD_ENGINE_CNTL:
+               case UVD_NO_OP:
                        break;
                default:
                        DRM_ERROR("Invalid reg 0x%X!\n",
@@ -753,8 +754,10 @@ static int radeon_uvd_send_msg(struct radeon_device *rdev,
        ib.ptr[3] = addr >> 32;
        ib.ptr[4] = PACKET0(UVD_GPCOM_VCPU_CMD, 0);
        ib.ptr[5] = 0;
-       for (i = 6; i < 16; ++i)
-               ib.ptr[i] = PACKET2(0);
+       for (i = 6; i < 16; i += 2) {
+               ib.ptr[i] = PACKET0(UVD_NO_OP, 0);
+               ib.ptr[i+1] = 0;
+       }
        ib.length_dw = 16;
 
        r = radeon_ib_schedule(rdev, &ib, NULL, false);
index 1c120a4..729ae58 100644 (file)
@@ -1738,7 +1738,7 @@ static void rv770_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index 9ef2064..0271f4c 100644 (file)
 #define UVD_UDEC_TILING_CONFIG                          0xef40
 #define UVD_UDEC_DB_TILING_CONFIG                       0xef44
 #define UVD_UDEC_DBW_TILING_CONFIG                      0xef48
+#define UVD_NO_OP                                      0xeffc
 
 #define        GC_USER_SHADER_PIPE_CONFIG                      0x8954
 #define                INACTIVE_QD_PIPES(x)                            ((x) << 8)
index 2523ca9..7ee9aaf 100644 (file)
@@ -1547,7 +1547,7 @@ int si_mc_load_microcode(struct radeon_device *rdev)
 {
        const __be32 *fw_data = NULL;
        const __le32 *new_fw_data = NULL;
-       u32 running, blackout = 0;
+       u32 running;
        u32 *io_mc_regs = NULL;
        const __le32 *new_io_mc_regs = NULL;
        int i, regs_size, ucode_size;
@@ -1598,11 +1598,6 @@ int si_mc_load_microcode(struct radeon_device *rdev)
        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);
@@ -1641,9 +1636,6 @@ int si_mc_load_microcode(struct radeon_device *rdev)
                                break;
                        udelay(1);
                }
-
-               if (running)
-                       WREG32(MC_SHARED_BLACKOUT_CNTL, blackout);
        }
 
        return 0;
@@ -6928,7 +6920,7 @@ static void si_uvd_resume(struct radeon_device *rdev)
                return;
 
        ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX];
-       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, RADEON_CP_PACKET2);
+       r = radeon_ring_init(rdev, ring, ring->ring_size, 0, PACKET0(UVD_NO_OP, 0));
        if (r) {
                dev_err(rdev->dev, "failed initializing UVD ring (%d).\n", r);
                return;
index 1f78ec2..89bdf20 100644 (file)
@@ -4112,7 +4112,7 @@ static int si_populate_smc_voltage_tables(struct radeon_device *rdev,
                                                              &rdev->pm.dpm.dyn_state.phase_shedding_limits_table)) {
                                si_populate_smc_voltage_table(rdev, &si_pi->vddc_phase_shed_table, table);
 
-                               table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC] =
+                               table->phaseMaskTable.lowMask[SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING] =
                                        cpu_to_be32(si_pi->vddc_phase_shed_table.mask_low);
 
                                si_write_smc_soft_register(rdev, SI_SMC_SOFT_REGISTER_phase_shedding_delay,
index d1a7b58..eb220ee 100644 (file)
 #define UVD_UDEC_ADDR_CONFIG                           0xEF4C
 #define UVD_UDEC_DB_ADDR_CONFIG                                0xEF50
 #define UVD_UDEC_DBW_ADDR_CONFIG                       0xEF54
+#define UVD_NO_OP                                      0xEFFC
 #define UVD_RBC_RB_RPTR                                        0xF690
 #define UVD_RBC_RB_WPTR                                        0xF694
 #define UVD_STATUS                                     0xf6bc
index 3c77983..966e3a5 100644 (file)
@@ -194,6 +194,7 @@ typedef struct SISLANDS_SMC_SWSTATE SISLANDS_SMC_SWSTATE;
 #define SISLANDS_SMC_VOLTAGEMASK_VDDC  0
 #define SISLANDS_SMC_VOLTAGEMASK_MVDD  1
 #define SISLANDS_SMC_VOLTAGEMASK_VDDCI 2
+#define SISLANDS_SMC_VOLTAGEMASK_VDDC_PHASE_SHEDDING 3
 #define SISLANDS_SMC_VOLTAGEMASK_MAX   4
 
 struct SISLANDS_SMC_VOLTAGEMASKTABLE
index 899ef7a..73c971e 100644 (file)
@@ -316,8 +316,8 @@ static int rcar_du_probe(struct platform_device *pdev)
        rcdu->info = of_match_device(rcar_du_of_table, rcdu->dev)->data;
 
        ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev);
-       if (!ddev)
-               return -ENOMEM;
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
 
        rcdu->ddev = ddev;
        ddev->dev_private = rcdu;
index f03eb55..bd9c3bb 100644 (file)
@@ -257,7 +257,8 @@ static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
        /* Apply the atomic update. */
        drm_atomic_helper_commit_modeset_disables(dev, old_state);
        drm_atomic_helper_commit_modeset_enables(dev, old_state);
-       drm_atomic_helper_commit_planes(dev, old_state, true);
+       drm_atomic_helper_commit_planes(dev, old_state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
 
        drm_atomic_helper_wait_for_vblanks(dev, old_state);
 
index 05d0713..9746365 100644 (file)
@@ -3,7 +3,7 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 
 rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \
-               rockchip_drm_gem.o rockchip_drm_vop.o
+               rockchip_drm_gem.o rockchip_drm_psr.o rockchip_drm_vop.o
 rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o
 
 obj-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
index 89aadbf..8548e82 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/bridge/analogix_dp.h>
 
 #include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
 #define RK3288_GRF_SOC_CON6            0x25c
@@ -41,6 +42,8 @@
 
 #define HIWORD_UPDATE(val, mask)       (val | (mask) << 16)
 
+#define PSR_WAIT_LINE_FLAG_TIMEOUT_MS  100
+
 #define to_dp(nm)      container_of(nm, struct rockchip_dp_device, nm)
 
 /**
@@ -68,11 +71,65 @@ struct rockchip_dp_device {
        struct regmap            *grf;
        struct reset_control     *rst;
 
+       struct work_struct       psr_work;
+       spinlock_t               psr_lock;
+       unsigned int             psr_state;
+
        const struct rockchip_dp_chip_data *data;
 
        struct analogix_dp_plat_data plat_data;
 };
 
+static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
+{
+       struct rockchip_dp_device *dp = to_dp(encoder);
+       unsigned long flags;
+
+       if (!analogix_dp_psr_supported(dp->dev))
+               return;
+
+       dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
+
+       spin_lock_irqsave(&dp->psr_lock, flags);
+       if (enabled)
+               dp->psr_state = EDP_VSC_PSR_STATE_ACTIVE;
+       else
+               dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+
+       schedule_work(&dp->psr_work);
+       spin_unlock_irqrestore(&dp->psr_lock, flags);
+}
+
+static void analogix_dp_psr_work(struct work_struct *work)
+{
+       struct rockchip_dp_device *dp =
+                               container_of(work, typeof(*dp), psr_work);
+       struct drm_crtc *crtc = dp->encoder.crtc;
+       int psr_state = dp->psr_state;
+       int vact_end;
+       int ret;
+       unsigned long flags;
+
+       if (!crtc)
+               return;
+
+       vact_end = crtc->mode.vtotal - crtc->mode.vsync_start + crtc->mode.vdisplay;
+
+       ret = rockchip_drm_wait_line_flag(dp->encoder.crtc, vact_end,
+                                         PSR_WAIT_LINE_FLAG_TIMEOUT_MS);
+       if (ret) {
+               dev_err(dp->dev, "line flag interrupt did not arrive\n");
+               return;
+       }
+
+       spin_lock_irqsave(&dp->psr_lock, flags);
+       if (psr_state == EDP_VSC_PSR_STATE_ACTIVE)
+               analogix_dp_enable_psr(dp->dev);
+       else
+               analogix_dp_disable_psr(dp->dev);
+       spin_unlock_irqrestore(&dp->psr_lock, flags);
+}
+
 static int rockchip_dp_pre_init(struct rockchip_dp_device *dp)
 {
        reset_control_assert(dp->rst);
@@ -87,6 +144,8 @@ static int rockchip_dp_poweron(struct analogix_dp_plat_data *plat_data)
        struct rockchip_dp_device *dp = to_dp(plat_data);
        int ret;
 
+       cancel_work_sync(&dp->psr_work);
+
        ret = clk_prepare_enable(dp->pclk);
        if (ret < 0) {
                dev_err(dp->dev, "failed to enable pclk %d\n", ret);
@@ -342,12 +401,22 @@ static int rockchip_dp_bind(struct device *dev, struct device *master,
        dp->plat_data.power_off = rockchip_dp_powerdown;
        dp->plat_data.get_modes = rockchip_dp_get_modes;
 
+       spin_lock_init(&dp->psr_lock);
+       dp->psr_state = ~EDP_VSC_PSR_STATE_ACTIVE;
+       INIT_WORK(&dp->psr_work, analogix_dp_psr_work);
+
+       rockchip_drm_psr_register(&dp->encoder, analogix_dp_psr_set);
+
        return analogix_dp_bind(dev, dp->drm_dev, &dp->plat_data);
 }
 
 static void rockchip_dp_unbind(struct device *dev, struct device *master,
                               void *data)
 {
+       struct rockchip_dp_device *dp = dev_get_drvdata(dev);
+
+       rockchip_drm_psr_unregister(&dp->encoder);
+
        return analogix_dp_unbind(dev, master, data);
 }
 
@@ -381,10 +450,8 @@ static int rockchip_dp_probe(struct platform_device *pdev)
 
                panel = of_drm_find_panel(panel_node);
                of_node_put(panel_node);
-               if (!panel) {
-                       DRM_ERROR("failed to find panel\n");
+               if (!panel)
                        return -EPROBE_DEFER;
-               }
        }
 
        dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
@@ -445,7 +512,6 @@ static struct platform_driver rockchip_dp_driver = {
        .remove = rockchip_dp_remove,
        .driver = {
                   .name = "rockchip-dp",
-                  .owner = THIS_MODULE,
                   .pm = &rockchip_dp_pm_ops,
                   .of_match_table = of_match_ptr(rockchip_dp_dt_ids),
        },
index a822d49..8c8cbe8 100644 (file)
@@ -143,8 +143,8 @@ static int rockchip_drm_bind(struct device *dev)
        int ret;
 
        drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
-       if (!drm_dev)
-               return -ENOMEM;
+       if (IS_ERR(drm_dev))
+               return PTR_ERR(drm_dev);
 
        dev_set_drvdata(dev, drm_dev);
 
@@ -156,6 +156,9 @@ static int rockchip_drm_bind(struct device *dev)
 
        drm_dev->dev_private = private;
 
+       INIT_LIST_HEAD(&private->psr_list);
+       spin_lock_init(&private->psr_list_lock);
+
        drm_mode_config_init(drm_dev);
 
        rockchip_drm_mode_config_init(drm_dev);
@@ -306,7 +309,7 @@ static struct drm_driver rockchip_drm_driver = {
 };
 
 #ifdef CONFIG_PM_SLEEP
-void rockchip_drm_fb_suspend(struct drm_device *drm)
+static void rockchip_drm_fb_suspend(struct drm_device *drm)
 {
        struct rockchip_drm_private *priv = drm->dev_private;
 
@@ -315,7 +318,7 @@ void rockchip_drm_fb_suspend(struct drm_device *drm)
        console_unlock();
 }
 
-void rockchip_drm_fb_resume(struct drm_device *drm)
+static void rockchip_drm_fb_resume(struct drm_device *drm)
 {
        struct rockchip_drm_private *priv = drm->dev_private;
 
index ea39329..fb6226c 100644 (file)
@@ -39,7 +39,6 @@ struct drm_connector;
 struct rockchip_crtc_funcs {
        int (*enable_vblank)(struct drm_crtc *crtc);
        void (*disable_vblank)(struct drm_crtc *crtc);
-       void (*wait_for_update)(struct drm_crtc *crtc);
 };
 
 struct rockchip_crtc_state {
@@ -61,6 +60,9 @@ struct rockchip_drm_private {
        struct drm_gem_object *fbdev_bo;
        const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
        struct drm_atomic_state *state;
+
+       struct list_head psr_list;
+       spinlock_t psr_list_lock;
 };
 
 int rockchip_register_crtc_funcs(struct drm_crtc *crtc,
@@ -70,4 +72,7 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
                                   struct device *dev);
 void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
                                    struct device *dev);
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+                               unsigned int mstimeout);
+
 #endif /* _ROCKCHIP_DRM_DRV_H_ */
index 55c5273..0f6eda0 100644 (file)
@@ -22,6 +22,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_fb.h"
 #include "rockchip_drm_gem.h"
+#include "rockchip_drm_psr.h"
 
 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb)
 
@@ -63,9 +64,20 @@ static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb,
                                     rockchip_fb->obj[0], handle);
 }
 
+static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb,
+                                struct drm_file *file,
+                                unsigned int flags, unsigned int color,
+                                struct drm_clip_rect *clips,
+                                unsigned int num_clips)
+{
+       rockchip_drm_psr_flush_all(fb->dev);
+       return 0;
+}
+
 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = {
        .destroy        = rockchip_drm_fb_destroy,
        .create_handle  = rockchip_drm_fb_create_handle,
+       .dirty          = rockchip_drm_fb_dirty,
 };
 
 static struct rockchip_drm_fb *
@@ -162,68 +174,6 @@ static void rockchip_drm_output_poll_changed(struct drm_device *dev)
                drm_fb_helper_hotplug_event(fb_helper);
 }
 
-static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc)
-{
-       struct rockchip_drm_private *priv = crtc->dev->dev_private;
-       int pipe = drm_crtc_index(crtc);
-       const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe];
-
-       if (crtc_funcs && crtc_funcs->wait_for_update)
-               crtc_funcs->wait_for_update(crtc);
-}
-
-/*
- * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066
- * have hardware counters for neither vblanks nor scanlines, which results in
- * a race where:
- *                             | <-- HW vsync irq and reg take effect
- *            plane_commit --> |
- *     get_vblank and wait --> |
- *                             | <-- handle_vblank, vblank->count + 1
- *              cleanup_fb --> |
- *             iommu crash --> |
- *                             | <-- HW vsync irq and reg take effect
- *
- * This function is equivalent but uses rockchip_crtc_wait_for_update() instead
- * of waiting for vblank_count to change.
- */
-static void
-rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state)
-{
-       struct drm_crtc_state *old_crtc_state;
-       struct drm_crtc *crtc;
-       int i, ret;
-
-       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
-               /* No one cares about the old state, so abuse it for tracking
-                * and store whether we hold a vblank reference (and should do a
-                * vblank wait) in the ->enable boolean.
-                */
-               old_crtc_state->enable = false;
-
-               if (!crtc->state->active)
-                       continue;
-
-               if (!drm_atomic_helper_framebuffer_changed(dev,
-                               old_state, crtc))
-                       continue;
-
-               ret = drm_crtc_vblank_get(crtc);
-               if (ret != 0)
-                       continue;
-
-               old_crtc_state->enable = true;
-       }
-
-       for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
-               if (!old_crtc_state->enable)
-                       continue;
-
-               rockchip_crtc_wait_for_update(crtc);
-               drm_crtc_vblank_put(crtc);
-       }
-}
-
 static void
 rockchip_atomic_commit_tail(struct drm_atomic_state *state)
 {
@@ -233,11 +183,12 @@ rockchip_atomic_commit_tail(struct drm_atomic_state *state)
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state, true);
+       drm_atomic_helper_commit_planes(dev, state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
 
        drm_atomic_helper_commit_hw_done(state);
 
-       rockchip_atomic_wait_for_complete(dev, state);
+       drm_atomic_helper_wait_for_vblanks(dev, state);
 
        drm_atomic_helper_cleanup_planes(dev, state);
 }
index 207e01d..a16c69f 100644 (file)
@@ -20,6 +20,7 @@
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_fbdev.h"
 
 #define PREFERRED_BPP          32
 #define to_drm_private(x) \
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.c b/drivers/gpu/drm/rockchip/rockchip_drm_psr.c
new file mode 100644 (file)
index 0000000..a553e18
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_psr.h"
+
+#define PSR_FLUSH_TIMEOUT      msecs_to_jiffies(100)
+
+enum psr_state {
+       PSR_FLUSH,
+       PSR_ENABLE,
+       PSR_DISABLE,
+};
+
+struct psr_drv {
+       struct list_head        list;
+       struct drm_encoder      *encoder;
+
+       spinlock_t              lock;
+       bool                    active;
+       enum psr_state          state;
+
+       struct timer_list       flush_timer;
+
+       void (*set)(struct drm_encoder *encoder, bool enable);
+};
+
+static struct psr_drv *find_psr_by_crtc(struct drm_crtc *crtc)
+{
+       struct rockchip_drm_private *drm_drv = crtc->dev->dev_private;
+       struct psr_drv *psr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+       list_for_each_entry(psr, &drm_drv->psr_list, list) {
+               if (psr->encoder->crtc == crtc)
+                       goto out;
+       }
+       psr = ERR_PTR(-ENODEV);
+
+out:
+       spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+       return psr;
+}
+
+static void psr_set_state_locked(struct psr_drv *psr, enum psr_state state)
+{
+       /*
+        * Allowed finite state machine:
+        *
+        *   PSR_ENABLE  < = = = = = >  PSR_FLUSH
+        *       | ^                        |
+        *       | |                        |
+        *       v |                        |
+        *   PSR_DISABLE < - - - - - - - - -
+        */
+       if (state == psr->state || !psr->active)
+               return;
+
+       /* Already disabled in flush, change the state, but not the hardware */
+       if (state == PSR_DISABLE && psr->state == PSR_FLUSH) {
+               psr->state = state;
+               return;
+       }
+
+       psr->state = state;
+
+       /* Actually commit the state change to hardware */
+       switch (psr->state) {
+       case PSR_ENABLE:
+               psr->set(psr->encoder, true);
+               break;
+
+       case PSR_DISABLE:
+       case PSR_FLUSH:
+               psr->set(psr->encoder, false);
+               break;
+       }
+}
+
+static void psr_set_state(struct psr_drv *psr, enum psr_state state)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&psr->lock, flags);
+       psr_set_state_locked(psr, state);
+       spin_unlock_irqrestore(&psr->lock, flags);
+}
+
+static void psr_flush_handler(unsigned long data)
+{
+       struct psr_drv *psr = (struct psr_drv *)data;
+       unsigned long flags;
+
+       /* If the state has changed since we initiated the flush, do nothing */
+       spin_lock_irqsave(&psr->lock, flags);
+       if (psr->state == PSR_FLUSH)
+               psr_set_state_locked(psr, PSR_ENABLE);
+       spin_unlock_irqrestore(&psr->lock, flags);
+}
+
+/**
+ * rockchip_drm_psr_activate - activate PSR on the given pipe
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_activate(struct drm_crtc *crtc)
+{
+       struct psr_drv *psr = find_psr_by_crtc(crtc);
+       unsigned long flags;
+
+       if (IS_ERR(psr))
+               return PTR_ERR(psr);
+
+       spin_lock_irqsave(&psr->lock, flags);
+       psr->active = true;
+       spin_unlock_irqrestore(&psr->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_activate);
+
+/**
+ * rockchip_drm_psr_deactivate - deactivate PSR on the given pipe
+ * @crtc: CRTC to obtain the PSR encoder
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_deactivate(struct drm_crtc *crtc)
+{
+       struct psr_drv *psr = find_psr_by_crtc(crtc);
+       unsigned long flags;
+
+       if (IS_ERR(psr))
+               return PTR_ERR(psr);
+
+       spin_lock_irqsave(&psr->lock, flags);
+       psr->active = false;
+       spin_unlock_irqrestore(&psr->lock, flags);
+       del_timer_sync(&psr->flush_timer);
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_deactivate);
+
+static void rockchip_drm_do_flush(struct psr_drv *psr)
+{
+       mod_timer(&psr->flush_timer,
+                 round_jiffies_up(jiffies + PSR_FLUSH_TIMEOUT));
+       psr_set_state(psr, PSR_FLUSH);
+}
+
+/**
+ * rockchip_drm_psr_flush - flush a single pipe
+ * @crtc: CRTC of the pipe to flush
+ *
+ * Returns:
+ * 0 on success, -errno on fail
+ */
+int rockchip_drm_psr_flush(struct drm_crtc *crtc)
+{
+       struct psr_drv *psr = find_psr_by_crtc(crtc);
+       if (IS_ERR(psr))
+               return PTR_ERR(psr);
+
+       rockchip_drm_do_flush(psr);
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush);
+
+/**
+ * rockchip_drm_psr_flush_all - force to flush all registered PSR encoders
+ * @dev: drm device
+ *
+ * Disable the PSR function for all registered encoders, and then enable the
+ * PSR function back after PSR_FLUSH_TIMEOUT. If encoder PSR state have been
+ * changed during flush time, then keep the state no change after flush
+ * timeout.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_flush_all(struct drm_device *dev)
+{
+       struct rockchip_drm_private *drm_drv = dev->dev_private;
+       struct psr_drv *psr;
+       unsigned long flags;
+
+       spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+       list_for_each_entry(psr, &drm_drv->psr_list, list)
+               rockchip_drm_do_flush(psr);
+       spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_flush_all);
+
+/**
+ * rockchip_drm_psr_register - register encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+                       void (*psr_set)(struct drm_encoder *, bool enable))
+{
+       struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+       struct psr_drv *psr;
+       unsigned long flags;
+
+       if (!encoder || !psr_set)
+               return -EINVAL;
+
+       psr = kzalloc(sizeof(struct psr_drv), GFP_KERNEL);
+       if (!psr)
+               return -ENOMEM;
+
+       setup_timer(&psr->flush_timer, psr_flush_handler, (unsigned long)psr);
+       spin_lock_init(&psr->lock);
+
+       psr->active = true;
+       psr->state = PSR_DISABLE;
+       psr->encoder = encoder;
+       psr->set = psr_set;
+
+       spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+       list_add_tail(&psr->list, &drm_drv->psr_list);
+       spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_psr_register);
+
+/**
+ * rockchip_drm_psr_unregister - unregister encoder to psr driver
+ * @encoder: encoder that obtain the PSR function
+ * @psr_set: call back to set PSR state
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder)
+{
+       struct rockchip_drm_private *drm_drv = encoder->dev->dev_private;
+       struct psr_drv *psr, *n;
+       unsigned long flags;
+
+       spin_lock_irqsave(&drm_drv->psr_list_lock, flags);
+       list_for_each_entry_safe(psr, n, &drm_drv->psr_list, list) {
+               if (psr->encoder == encoder) {
+                       del_timer(&psr->flush_timer);
+                       list_del(&psr->list);
+                       kfree(psr);
+               }
+       }
+       spin_unlock_irqrestore(&drm_drv->psr_list_lock, flags);
+}
+EXPORT_SYMBOL(rockchip_drm_psr_unregister);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_psr.h b/drivers/gpu/drm/rockchip/rockchip_drm_psr.h
new file mode 100644 (file)
index 0000000..b420cf1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Yakir Yang <ykk@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ROCKCHIP_DRM_PSR___
+#define __ROCKCHIP_DRM_PSR___
+
+void rockchip_drm_psr_flush_all(struct drm_device *dev);
+int rockchip_drm_psr_flush(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_activate(struct drm_crtc *crtc);
+int rockchip_drm_psr_deactivate(struct drm_crtc *crtc);
+
+int rockchip_drm_psr_register(struct drm_encoder *encoder,
+                       void (*psr_set)(struct drm_encoder *, bool enable));
+void rockchip_drm_psr_unregister(struct drm_encoder *encoder);
+
+#endif /* __ROCKCHIP_DRM_PSR__ */
index 91305eb..c7eba30 100644 (file)
 #include <drm/drm_atomic.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_flip_work.h>
 #include <drm/drm_plane_helper.h>
 
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/iopoll.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 #include "rockchip_drm_fb.h"
+#include "rockchip_drm_psr.h"
 #include "rockchip_drm_vop.h"
 
-#define __REG_SET_RELAXED(x, off, mask, shift, v) \
-               vop_mask_write_relaxed(x, off, (mask) << shift, (v) << shift)
-#define __REG_SET_NORMAL(x, off, mask, shift, v) \
-               vop_mask_write(x, off, (mask) << shift, (v) << shift)
+#define __REG_SET_RELAXED(x, off, mask, shift, v, write_mask) \
+               vop_mask_write(x, off, mask, shift, v, write_mask, true)
+
+#define __REG_SET_NORMAL(x, off, mask, shift, v, write_mask) \
+               vop_mask_write(x, off, mask, shift, v, write_mask, false)
 
 #define REG_SET(x, base, reg, v, mode) \
-               __REG_SET_##mode(x, base + reg.offset, reg.mask, reg.shift, v)
+               __REG_SET_##mode(x, base + reg.offset, \
+                                reg.mask, reg.shift, v, reg.write_mask)
 #define REG_SET_MASK(x, base, reg, mask, v, mode) \
-               __REG_SET_##mode(x, base + reg.offset, mask, reg.shift, v)
+               __REG_SET_##mode(x, base + reg.offset, \
+                                mask, reg.shift, v, reg.write_mask)
 
 #define VOP_WIN_SET(x, win, name, v) \
                REG_SET(x, win->base, win->phy->name, v, RELAXED)
 
 #define to_vop(x) container_of(x, struct vop, crtc)
 #define to_vop_win(x) container_of(x, struct vop_win, base)
-#define to_vop_plane_state(x) container_of(x, struct vop_plane_state, base)
 
-struct vop_plane_state {
-       struct drm_plane_state base;
-       int format;
-       struct drm_rect src;
-       struct drm_rect dest;
-       dma_addr_t yrgb_mst;
-       bool enable;
+enum vop_pending {
+       VOP_PENDING_FB_UNREF,
 };
 
 struct vop_win {
        struct drm_plane base;
        const struct vop_win_data *data;
        struct vop *vop;
-
-       /* protected by dev->event_lock */
-       bool enable;
-       dma_addr_t yrgb_mst;
 };
 
 struct vop {
@@ -113,11 +109,15 @@ struct vop {
        struct mutex vsync_mutex;
        bool vsync_work_pending;
        struct completion dsp_hold_completion;
-       struct completion wait_update_complete;
 
        /* protected by dev->event_lock */
        struct drm_pending_vblank_event *event;
 
+       struct drm_flip_work fb_unref_work;
+       unsigned long pending;
+
+       struct completion line_flag_completion;
+
        const struct vop_data *data;
 
        uint32_t *regsbak;
@@ -164,27 +164,25 @@ static inline uint32_t vop_read_reg(struct vop *vop, uint32_t base,
 }
 
 static inline void vop_mask_write(struct vop *vop, uint32_t offset,
-                                 uint32_t mask, uint32_t v)
+                                 uint32_t mask, uint32_t shift, uint32_t v,
+                                 bool write_mask, bool relaxed)
 {
-       if (mask) {
-               uint32_t cached_val = vop->regsbak[offset >> 2];
-
-               cached_val = (cached_val & ~mask) | v;
-               writel(cached_val, vop->regs + offset);
-               vop->regsbak[offset >> 2] = cached_val;
-       }
-}
+       if (!mask)
+               return;
 
-static inline void vop_mask_write_relaxed(struct vop *vop, uint32_t offset,
-                                         uint32_t mask, uint32_t v)
-{
-       if (mask) {
+       if (write_mask) {
+               v = ((v << shift) & 0xffff) | (mask << (shift + 16));
+       } else {
                uint32_t cached_val = vop->regsbak[offset >> 2];
 
-               cached_val = (cached_val & ~mask) | v;
-               writel_relaxed(cached_val, vop->regs + offset);
-               vop->regsbak[offset >> 2] = cached_val;
+               v = (cached_val & ~(mask << shift)) | ((v & mask) << shift);
+               vop->regsbak[offset >> 2] = v;
        }
+
+       if (relaxed)
+               writel_relaxed(v, vop->regs + offset);
+       else
+               writel(v, vop->regs + offset);
 }
 
 static inline uint32_t vop_get_intr_type(struct vop *vop,
@@ -240,7 +238,7 @@ static enum vop_data_format vop_convert_format(uint32_t format)
        case DRM_FORMAT_NV24:
                return VOP_FMT_YUV444SP;
        default:
-               DRM_ERROR("unsupport format[%08x]\n", format);
+               DRM_ERROR("unsupported format[%08x]\n", format);
                return -EINVAL;
        }
 }
@@ -317,7 +315,7 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
        int vskiplines = 0;
 
        if (dst_w > 3840) {
-               DRM_ERROR("Maximum destination width (3840) exceeded\n");
+               DRM_DEV_ERROR(vop->dev, "Maximum dst width (3840) exceeded\n");
                return;
        }
 
@@ -355,11 +353,11 @@ static void scl_vop_cal_scl_fac(struct vop *vop, const struct vop_win_data *win,
        VOP_SCL_SET_EXT(vop, win, lb_mode, lb_mode);
        if (lb_mode == LB_RGB_3840X2) {
                if (yrgb_ver_scl_mode != SCALE_NONE) {
-                       DRM_ERROR("ERROR : not allow yrgb ver scale\n");
+                       DRM_DEV_ERROR(vop->dev, "not allow yrgb ver scale\n");
                        return;
                }
                if (cbcr_ver_scl_mode != SCALE_NONE) {
-                       DRM_ERROR("ERROR : not allow cbcr ver scale\n");
+                       DRM_DEV_ERROR(vop->dev, "not allow cbcr ver scale\n");
                        return;
                }
                vsu_mode = SCALE_UP_BIL;
@@ -411,6 +409,7 @@ static void vop_dsp_hold_valid_irq_enable(struct vop *vop)
 
        spin_lock_irqsave(&vop->irq_lock, flags);
 
+       VOP_INTR_SET_TYPE(vop, clear, DSP_HOLD_VALID_INTR, 1);
        VOP_INTR_SET_TYPE(vop, enable, DSP_HOLD_VALID_INTR, 1);
 
        spin_unlock_irqrestore(&vop->irq_lock, flags);
@@ -430,7 +429,73 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
        spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
-static void vop_enable(struct drm_crtc *crtc)
+/*
+ * (1) each frame starts at the start of the Vsync pulse which is signaled by
+ *     the "FRAME_SYNC" interrupt.
+ * (2) the active data region of each frame ends at dsp_vact_end
+ * (3) we should program this same number (dsp_vact_end) into dsp_line_frag_num,
+ *      to get "LINE_FLAG" interrupt at the end of the active on screen data.
+ *
+ * VOP_INTR_CTRL0.dsp_line_frag_num = VOP_DSP_VACT_ST_END.dsp_vact_end
+ * Interrupts
+ * LINE_FLAG -------------------------------+
+ * FRAME_SYNC ----+                         |
+ *                |                         |
+ *                v                         v
+ *                | Vsync | Vbp |  Vactive  | Vfp |
+ *                        ^     ^           ^     ^
+ *                        |     |           |     |
+ *                        |     |           |     |
+ * dsp_vs_end ------------+     |           |     |   VOP_DSP_VTOTAL_VS_END
+ * dsp_vact_start --------------+           |     |   VOP_DSP_VACT_ST_END
+ * dsp_vact_end ----------------------------+     |   VOP_DSP_VACT_ST_END
+ * dsp_total -------------------------------------+   VOP_DSP_VTOTAL_VS_END
+ */
+static bool vop_line_flag_irq_is_enabled(struct vop *vop)
+{
+       uint32_t line_flag_irq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       line_flag_irq = VOP_INTR_GET_TYPE(vop, enable, LINE_FLAG_INTR);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+
+       return !!line_flag_irq;
+}
+
+static void vop_line_flag_irq_enable(struct vop *vop, int line_num)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       VOP_CTRL_SET(vop, line_flag_num[0], line_num);
+       VOP_INTR_SET_TYPE(vop, clear, LINE_FLAG_INTR, 1);
+       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 1);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static void vop_line_flag_irq_disable(struct vop *vop)
+{
+       unsigned long flags;
+
+       if (WARN_ON(!vop->is_enabled))
+               return;
+
+       spin_lock_irqsave(&vop->irq_lock, flags);
+
+       VOP_INTR_SET_TYPE(vop, enable, LINE_FLAG_INTR, 0);
+
+       spin_unlock_irqrestore(&vop->irq_lock, flags);
+}
+
+static int vop_enable(struct drm_crtc *crtc)
 {
        struct vop *vop = to_vop(crtc);
        int ret;
@@ -438,26 +503,20 @@ static void vop_enable(struct drm_crtc *crtc)
        ret = pm_runtime_get_sync(vop->dev);
        if (ret < 0) {
                dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
-               return;
+               goto err_put_pm_runtime;
        }
 
        ret = clk_enable(vop->hclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to enable hclk - %d\n", ret);
-               return;
-       }
+       if (WARN_ON(ret < 0))
+               goto err_put_pm_runtime;
 
        ret = clk_enable(vop->dclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to enable dclk - %d\n", ret);
+       if (WARN_ON(ret < 0))
                goto err_disable_hclk;
-       }
 
        ret = clk_enable(vop->aclk);
-       if (ret < 0) {
-               dev_err(vop->dev, "failed to enable aclk - %d\n", ret);
+       if (WARN_ON(ret < 0))
                goto err_disable_dclk;
-       }
 
        /*
         * Slave iommu shares power, irq and clock with vop.  It was associated
@@ -487,7 +546,7 @@ static void vop_enable(struct drm_crtc *crtc)
 
        drm_crtc_vblank_on(crtc);
 
-       return;
+       return 0;
 
 err_disable_aclk:
        clk_disable(vop->aclk);
@@ -495,6 +554,9 @@ err_disable_dclk:
        clk_disable(vop->dclk);
 err_disable_hclk:
        clk_disable(vop->hclk);
+err_put_pm_runtime:
+       pm_runtime_put_sync(vop->dev);
+       return ret;
 }
 
 static void vop_crtc_disable(struct drm_crtc *crtc)
@@ -504,6 +566,8 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
 
        WARN_ON(vop->event);
 
+       rockchip_drm_psr_deactivate(&vop->crtc);
+
        /*
         * We need to make sure that all windows are disabled before we
         * disable that crtc. Otherwise we might try to scan from a destroyed
@@ -568,22 +632,6 @@ static void vop_plane_destroy(struct drm_plane *plane)
        drm_plane_cleanup(plane);
 }
 
-static int vop_plane_prepare_fb(struct drm_plane *plane,
-                               const struct drm_plane_state *new_state)
-{
-       if (plane->state->fb)
-               drm_framebuffer_reference(plane->state->fb);
-
-       return 0;
-}
-
-static void vop_plane_cleanup_fb(struct drm_plane *plane,
-                                const struct drm_plane_state *old_state)
-{
-       if (old_state->fb)
-               drm_framebuffer_unreference(old_state->fb);
-}
-
 static int vop_plane_atomic_check(struct drm_plane *plane,
                           struct drm_plane_state *state)
 {
@@ -591,12 +639,8 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
        struct drm_crtc_state *crtc_state;
        struct drm_framebuffer *fb = state->fb;
        struct vop_win *vop_win = to_vop_win(plane);
-       struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
        const struct vop_win_data *win = vop_win->data;
-       bool visible;
        int ret;
-       struct drm_rect *dest = &vop_plane_state->dest;
-       struct drm_rect *src = &vop_plane_state->src;
        struct drm_rect clip;
        int min_scale = win->phy->scl ? FRAC_16_16(1, 8) :
                                        DRM_PLANE_HELPER_NO_SCALING;
@@ -604,62 +648,43 @@ static int vop_plane_atomic_check(struct drm_plane *plane,
                                        DRM_PLANE_HELPER_NO_SCALING;
 
        if (!crtc || !fb)
-               goto out_disable;
+               return 0;
 
        crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
        if (WARN_ON(!crtc_state))
                return -EINVAL;
 
-       src->x1 = state->src_x;
-       src->y1 = state->src_y;
-       src->x2 = state->src_x + state->src_w;
-       src->y2 = state->src_y + state->src_h;
-       dest->x1 = state->crtc_x;
-       dest->y1 = state->crtc_y;
-       dest->x2 = state->crtc_x + state->crtc_w;
-       dest->y2 = state->crtc_y + state->crtc_h;
-
        clip.x1 = 0;
        clip.y1 = 0;
        clip.x2 = crtc_state->adjusted_mode.hdisplay;
        clip.y2 = crtc_state->adjusted_mode.vdisplay;
 
-       ret = drm_plane_helper_check_update(plane, crtc, state->fb,
-                                           src, dest, &clip,
-                                           state->rotation,
-                                           min_scale,
-                                           max_scale,
-                                           true, true, &visible);
+       ret = drm_plane_helper_check_state(state, &clip,
+                                          min_scale, max_scale,
+                                          true, true);
        if (ret)
                return ret;
 
-       if (!visible)
-               goto out_disable;
+       if (!state->visible)
+               return 0;
 
-       vop_plane_state->format = vop_convert_format(fb->pixel_format);
-       if (vop_plane_state->format < 0)
-               return vop_plane_state->format;
+       ret = vop_convert_format(fb->pixel_format);
+       if (ret < 0)
+               return ret;
 
        /*
         * Src.x1 can be odd when do clip, but yuv plane start point
         * need align with 2 pixel.
         */
-       if (is_yuv_support(fb->pixel_format) && ((src->x1 >> 16) % 2))
+       if (is_yuv_support(fb->pixel_format) && ((state->src.x1 >> 16) % 2))
                return -EINVAL;
 
-       vop_plane_state->enable = true;
-
-       return 0;
-
-out_disable:
-       vop_plane_state->enable = false;
        return 0;
 }
 
 static void vop_plane_atomic_disable(struct drm_plane *plane,
                                     struct drm_plane_state *old_state)
 {
-       struct vop_plane_state *vop_plane_state = to_vop_plane_state(old_state);
        struct vop_win *vop_win = to_vop_win(plane);
        const struct vop_win_data *win = vop_win->data;
        struct vop *vop = to_vop(old_state->crtc);
@@ -667,18 +692,11 @@ static void vop_plane_atomic_disable(struct drm_plane *plane,
        if (!old_state->crtc)
                return;
 
-       spin_lock_irq(&plane->dev->event_lock);
-       vop_win->enable = false;
-       vop_win->yrgb_mst = 0;
-       spin_unlock_irq(&plane->dev->event_lock);
-
        spin_lock(&vop->reg_lock);
 
        VOP_WIN_SET(vop, win, enable, 0);
 
        spin_unlock(&vop->reg_lock);
-
-       vop_plane_state->enable = false;
 }
 
 static void vop_plane_atomic_update(struct drm_plane *plane,
@@ -687,21 +705,21 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        struct drm_plane_state *state = plane->state;
        struct drm_crtc *crtc = state->crtc;
        struct vop_win *vop_win = to_vop_win(plane);
-       struct vop_plane_state *vop_plane_state = to_vop_plane_state(state);
        const struct vop_win_data *win = vop_win->data;
        struct vop *vop = to_vop(state->crtc);
        struct drm_framebuffer *fb = state->fb;
        unsigned int actual_w, actual_h;
        unsigned int dsp_stx, dsp_sty;
        uint32_t act_info, dsp_info, dsp_st;
-       struct drm_rect *src = &vop_plane_state->src;
-       struct drm_rect *dest = &vop_plane_state->dest;
+       struct drm_rect *src = &state->src;
+       struct drm_rect *dest = &state->dst;
        struct drm_gem_object *obj, *uv_obj;
        struct rockchip_gem_object *rk_obj, *rk_uv_obj;
        unsigned long offset;
        dma_addr_t dma_addr;
        uint32_t val;
        bool rb_swap;
+       int format;
 
        /*
         * can't update plane when vop is disabled.
@@ -712,7 +730,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
        if (WARN_ON(!vop->is_enabled))
                return;
 
-       if (!vop_plane_state->enable) {
+       if (!state->visible) {
                vop_plane_atomic_disable(plane, old_state);
                return;
        }
@@ -733,18 +751,15 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 
        offset = (src->x1 >> 16) * drm_format_plane_cpp(fb->pixel_format, 0);
        offset += (src->y1 >> 16) * fb->pitches[0];
-       vop_plane_state->yrgb_mst = rk_obj->dma_addr + offset + fb->offsets[0];
+       dma_addr = rk_obj->dma_addr + offset + fb->offsets[0];
 
-       spin_lock_irq(&plane->dev->event_lock);
-       vop_win->enable = true;
-       vop_win->yrgb_mst = vop_plane_state->yrgb_mst;
-       spin_unlock_irq(&plane->dev->event_lock);
+       format = vop_convert_format(fb->pixel_format);
 
        spin_lock(&vop->reg_lock);
 
-       VOP_WIN_SET(vop, win, format, vop_plane_state->format);
+       VOP_WIN_SET(vop, win, format, format);
        VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2);
-       VOP_WIN_SET(vop, win, yrgb_mst, vop_plane_state->yrgb_mst);
+       VOP_WIN_SET(vop, win, yrgb_mst, dma_addr);
        if (is_yuv_support(fb->pixel_format)) {
                int hsub = drm_format_horz_chroma_subsampling(fb->pixel_format);
                int vsub = drm_format_vert_chroma_subsampling(fb->pixel_format);
@@ -791,68 +806,18 @@ static void vop_plane_atomic_update(struct drm_plane *plane,
 }
 
 static const struct drm_plane_helper_funcs plane_helper_funcs = {
-       .prepare_fb = vop_plane_prepare_fb,
-       .cleanup_fb = vop_plane_cleanup_fb,
        .atomic_check = vop_plane_atomic_check,
        .atomic_update = vop_plane_atomic_update,
        .atomic_disable = vop_plane_atomic_disable,
 };
 
-static void vop_atomic_plane_reset(struct drm_plane *plane)
-{
-       struct vop_plane_state *vop_plane_state =
-                                       to_vop_plane_state(plane->state);
-
-       if (plane->state && plane->state->fb)
-               drm_framebuffer_unreference(plane->state->fb);
-
-       kfree(vop_plane_state);
-       vop_plane_state = kzalloc(sizeof(*vop_plane_state), GFP_KERNEL);
-       if (!vop_plane_state)
-               return;
-
-       plane->state = &vop_plane_state->base;
-       plane->state->plane = plane;
-}
-
-static struct drm_plane_state *
-vop_atomic_plane_duplicate_state(struct drm_plane *plane)
-{
-       struct vop_plane_state *old_vop_plane_state;
-       struct vop_plane_state *vop_plane_state;
-
-       if (WARN_ON(!plane->state))
-               return NULL;
-
-       old_vop_plane_state = to_vop_plane_state(plane->state);
-       vop_plane_state = kmemdup(old_vop_plane_state,
-                                 sizeof(*vop_plane_state), GFP_KERNEL);
-       if (!vop_plane_state)
-               return NULL;
-
-       __drm_atomic_helper_plane_duplicate_state(plane,
-                                                 &vop_plane_state->base);
-
-       return &vop_plane_state->base;
-}
-
-static void vop_atomic_plane_destroy_state(struct drm_plane *plane,
-                                          struct drm_plane_state *state)
-{
-       struct vop_plane_state *vop_state = to_vop_plane_state(state);
-
-       __drm_atomic_helper_plane_destroy_state(state);
-
-       kfree(vop_state);
-}
-
 static const struct drm_plane_funcs vop_plane_funcs = {
        .update_plane   = drm_atomic_helper_update_plane,
        .disable_plane  = drm_atomic_helper_disable_plane,
        .destroy = vop_plane_destroy,
-       .reset = vop_atomic_plane_reset,
-       .atomic_duplicate_state = vop_atomic_plane_duplicate_state,
-       .atomic_destroy_state = vop_atomic_plane_destroy_state,
+       .reset = drm_atomic_helper_plane_reset,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
 };
 
 static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
@@ -865,6 +830,7 @@ static int vop_crtc_enable_vblank(struct drm_crtc *crtc)
 
        spin_lock_irqsave(&vop->irq_lock, flags);
 
+       VOP_INTR_SET_TYPE(vop, clear, FS_INTR, 1);
        VOP_INTR_SET_TYPE(vop, enable, FS_INTR, 1);
 
        spin_unlock_irqrestore(&vop->irq_lock, flags);
@@ -887,18 +853,9 @@ static void vop_crtc_disable_vblank(struct drm_crtc *crtc)
        spin_unlock_irqrestore(&vop->irq_lock, flags);
 }
 
-static void vop_crtc_wait_for_update(struct drm_crtc *crtc)
-{
-       struct vop *vop = to_vop(crtc);
-
-       reinit_completion(&vop->wait_update_complete);
-       WARN_ON(!wait_for_completion_timeout(&vop->wait_update_complete, 100));
-}
-
 static const struct rockchip_crtc_funcs private_crtc_funcs = {
        .enable_vblank = vop_crtc_enable_vblank,
        .disable_vblank = vop_crtc_disable_vblank,
-       .wait_for_update = vop_crtc_wait_for_update,
 };
 
 static bool vop_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -928,11 +885,17 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
        u16 vsync_len = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
        u16 vact_st = adjusted_mode->vtotal - adjusted_mode->vsync_start;
        u16 vact_end = vact_st + vdisplay;
-       uint32_t val;
+       uint32_t pin_pol, val;
+       int ret;
 
        WARN_ON(vop->event);
 
-       vop_enable(crtc);
+       ret = vop_enable(crtc);
+       if (ret) {
+               DRM_DEV_ERROR(vop->dev, "Failed to enable vop (%d)\n", ret);
+               return;
+       }
+
        /*
         * If dclk rate is zero, mean that scanout is stop,
         * we don't need wait any more.
@@ -969,25 +932,31 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
                vop_dsp_hold_valid_irq_disable(vop);
        }
 
-       val = 0x8;
-       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
-       val |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
-       VOP_CTRL_SET(vop, pin_pol, val);
+       pin_pol = 0x8;
+       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1;
+       pin_pol |= (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : (1 << 1);
+       VOP_CTRL_SET(vop, pin_pol, pin_pol);
+
        switch (s->output_type) {
        case DRM_MODE_CONNECTOR_LVDS:
                VOP_CTRL_SET(vop, rgb_en, 1);
+               VOP_CTRL_SET(vop, rgb_pin_pol, pin_pol);
                break;
        case DRM_MODE_CONNECTOR_eDP:
+               VOP_CTRL_SET(vop, edp_pin_pol, pin_pol);
                VOP_CTRL_SET(vop, edp_en, 1);
                break;
        case DRM_MODE_CONNECTOR_HDMIA:
+               VOP_CTRL_SET(vop, hdmi_pin_pol, pin_pol);
                VOP_CTRL_SET(vop, hdmi_en, 1);
                break;
        case DRM_MODE_CONNECTOR_DSI:
+               VOP_CTRL_SET(vop, mipi_pin_pol, pin_pol);
                VOP_CTRL_SET(vop, mipi_en, 1);
                break;
        default:
-               DRM_ERROR("unsupport connector_type[%d]\n", s->output_type);
+               DRM_DEV_ERROR(vop->dev, "unsupported connector_type [%d]\n",
+                             s->output_type);
        }
        VOP_CTRL_SET(vop, out_mode, s->output_mode);
 
@@ -1006,12 +975,44 @@ static void vop_crtc_enable(struct drm_crtc *crtc)
        clk_set_rate(vop->dclk, adjusted_mode->clock * 1000);
 
        VOP_CTRL_SET(vop, standby, 0);
+
+       rockchip_drm_psr_activate(&vop->crtc);
+}
+
+static bool vop_fs_irq_is_pending(struct vop *vop)
+{
+       return VOP_INTR_GET_TYPE(vop, status, FS_INTR);
+}
+
+static void vop_wait_for_irq_handler(struct vop *vop)
+{
+       bool pending;
+       int ret;
+
+       /*
+        * Spin until frame start interrupt status bit goes low, which means
+        * that interrupt handler was invoked and cleared it. The timeout of
+        * 10 msecs is really too long, but it is just a safety measure if
+        * something goes really wrong. The wait will only happen in the very
+        * unlikely case of a vblank happening exactly at the same time and
+        * shouldn't exceed microseconds range.
+        */
+       ret = readx_poll_timeout_atomic(vop_fs_irq_is_pending, vop, pending,
+                                       !pending, 0, 10 * 1000);
+       if (ret)
+               DRM_DEV_ERROR(vop->dev, "VOP vblank IRQ stuck for 10 ms\n");
+
+       synchronize_irq(vop->irq);
 }
 
 static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
                                  struct drm_crtc_state *old_crtc_state)
 {
+       struct drm_atomic_state *old_state = old_crtc_state->state;
+       struct drm_plane_state *old_plane_state;
        struct vop *vop = to_vop(crtc);
+       struct drm_plane *plane;
+       int i;
 
        if (WARN_ON(!vop->is_enabled))
                return;
@@ -1021,12 +1022,13 @@ static void vop_crtc_atomic_flush(struct drm_crtc *crtc,
        vop_cfg_done(vop);
 
        spin_unlock(&vop->reg_lock);
-}
 
-static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
-                                 struct drm_crtc_state *old_crtc_state)
-{
-       struct vop *vop = to_vop(crtc);
+       /*
+        * There is a (rather unlikely) possiblity that a vblank interrupt
+        * fired before we set the cfg_done bit. To avoid spuriously
+        * signalling flip completion we need to wait for it to finish.
+        */
+       vop_wait_for_irq_handler(vop);
 
        spin_lock_irq(&crtc->dev->event_lock);
        if (crtc->state->event) {
@@ -1037,6 +1039,25 @@ static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
                crtc->state->event = NULL;
        }
        spin_unlock_irq(&crtc->dev->event_lock);
+
+       for_each_plane_in_state(old_state, plane, old_plane_state, i) {
+               if (!old_plane_state->fb)
+                       continue;
+
+               if (old_plane_state->fb == plane->state->fb)
+                       continue;
+
+               drm_framebuffer_reference(old_plane_state->fb);
+               drm_flip_work_queue(&vop->fb_unref_work, old_plane_state->fb);
+               set_bit(VOP_PENDING_FB_UNREF, &vop->pending);
+               WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+       }
+}
+
+static void vop_crtc_atomic_begin(struct drm_crtc *crtc,
+                                 struct drm_crtc_state *old_crtc_state)
+{
+       rockchip_drm_psr_flush(crtc);
 }
 
 static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = {
@@ -1093,16 +1114,13 @@ static const struct drm_crtc_funcs vop_crtc_funcs = {
        .atomic_destroy_state = vop_crtc_destroy_state,
 };
 
-static bool vop_win_pending_is_complete(struct vop_win *vop_win)
+static void vop_fb_unref_worker(struct drm_flip_work *work, void *val)
 {
-       dma_addr_t yrgb_mst;
-
-       if (!vop_win->enable)
-               return VOP_WIN_GET(vop_win->vop, vop_win->data, enable) == 0;
-
-       yrgb_mst = VOP_WIN_GET_YRGBADDR(vop_win->vop, vop_win->data);
+       struct vop *vop = container_of(work, struct vop, fb_unref_work);
+       struct drm_framebuffer *fb = val;
 
-       return yrgb_mst == vop_win->yrgb_mst;
+       drm_crtc_vblank_put(&vop->crtc);
+       drm_framebuffer_unreference(fb);
 }
 
 static void vop_handle_vblank(struct vop *vop)
@@ -1110,25 +1128,17 @@ static void vop_handle_vblank(struct vop *vop)
        struct drm_device *drm = vop->drm_dev;
        struct drm_crtc *crtc = &vop->crtc;
        unsigned long flags;
-       int i;
-
-       for (i = 0; i < vop->data->win_size; i++) {
-               if (!vop_win_pending_is_complete(&vop->win[i]))
-                       return;
-       }
 
        spin_lock_irqsave(&drm->event_lock, flags);
        if (vop->event) {
-
                drm_crtc_send_vblank_event(crtc, vop->event);
                drm_crtc_vblank_put(crtc);
                vop->event = NULL;
-
        }
        spin_unlock_irqrestore(&drm->event_lock, flags);
 
-       if (!completion_done(&vop->wait_update_complete))
-               complete(&vop->wait_update_complete);
+       if (test_and_clear_bit(VOP_PENDING_FB_UNREF, &vop->pending))
+               drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq);
 }
 
 static irqreturn_t vop_isr(int irq, void *data)
@@ -1162,6 +1172,12 @@ static irqreturn_t vop_isr(int irq, void *data)
                ret = IRQ_HANDLED;
        }
 
+       if (active_irqs & LINE_FLAG_INTR) {
+               complete(&vop->line_flag_completion);
+               active_irqs &= ~LINE_FLAG_INTR;
+               ret = IRQ_HANDLED;
+       }
+
        if (active_irqs & FS_INTR) {
                drm_crtc_handle_vblank(crtc);
                vop_handle_vblank(vop);
@@ -1171,7 +1187,8 @@ static irqreturn_t vop_isr(int irq, void *data)
 
        /* Unhandled irqs are spurious. */
        if (active_irqs)
-               DRM_ERROR("Unknown VOP IRQs: %#02x\n", active_irqs);
+               DRM_DEV_ERROR(vop->dev, "Unknown VOP IRQs: %#02x\n",
+                             active_irqs);
 
        return ret;
 }
@@ -1206,7 +1223,8 @@ static int vop_create_crtc(struct vop *vop)
                                               win_data->phy->nformats,
                                               win_data->type, NULL);
                if (ret) {
-                       DRM_ERROR("failed to initialize plane\n");
+                       DRM_DEV_ERROR(vop->dev, "failed to init plane %d\n",
+                                     ret);
                        goto err_cleanup_planes;
                }
 
@@ -1244,7 +1262,8 @@ static int vop_create_crtc(struct vop *vop)
                                               win_data->phy->nformats,
                                               win_data->type, NULL);
                if (ret) {
-                       DRM_ERROR("failed to initialize overlay plane\n");
+                       DRM_DEV_ERROR(vop->dev, "failed to init overlay %d\n",
+                                     ret);
                        goto err_cleanup_crtc;
                }
                drm_plane_helper_add(&vop_win->base, &plane_helper_funcs);
@@ -1252,14 +1271,17 @@ static int vop_create_crtc(struct vop *vop)
 
        port = of_get_child_by_name(dev->of_node, "port");
        if (!port) {
-               DRM_ERROR("no port node found in %s\n",
-                         dev->of_node->full_name);
+               DRM_DEV_ERROR(vop->dev, "no port node found in %s\n",
+                             dev->of_node->full_name);
                ret = -ENOENT;
                goto err_cleanup_crtc;
        }
 
+       drm_flip_work_init(&vop->fb_unref_work, "fb_unref",
+                          vop_fb_unref_worker);
+
        init_completion(&vop->dsp_hold_completion);
-       init_completion(&vop->wait_update_complete);
+       init_completion(&vop->line_flag_completion);
        crtc->port = port;
        rockchip_register_crtc_funcs(crtc, &private_crtc_funcs);
 
@@ -1300,6 +1322,7 @@ static void vop_destroy_crtc(struct vop *vop)
         * references the CRTC.
         */
        drm_crtc_cleanup(crtc);
+       drm_flip_work_cleanup(&vop->fb_unref_work);
 }
 
 static int vop_initial(struct vop *vop)
@@ -1416,6 +1439,49 @@ static void vop_win_init(struct vop *vop)
        }
 }
 
+/**
+ * rockchip_drm_wait_line_flag - acqiure the give line flag event
+ * @crtc: CRTC to enable line flag
+ * @line_num: interested line number
+ * @mstimeout: millisecond for timeout
+ *
+ * Driver would hold here until the interested line flag interrupt have
+ * happened or timeout to wait.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int rockchip_drm_wait_line_flag(struct drm_crtc *crtc, unsigned int line_num,
+                               unsigned int mstimeout)
+{
+       struct vop *vop = to_vop(crtc);
+       unsigned long jiffies_left;
+
+       if (!crtc || !vop->is_enabled)
+               return -ENODEV;
+
+       if (line_num > crtc->mode.vtotal || mstimeout <= 0)
+               return -EINVAL;
+
+       if (vop_line_flag_irq_is_enabled(vop))
+               return -EBUSY;
+
+       reinit_completion(&vop->line_flag_completion);
+       vop_line_flag_irq_enable(vop, line_num);
+
+       jiffies_left = wait_for_completion_timeout(&vop->line_flag_completion,
+                                                  msecs_to_jiffies(mstimeout));
+       vop_line_flag_irq_disable(vop);
+
+       if (jiffies_left == 0) {
+               dev_err(vop->dev, "Timeout waiting for IRQ\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_drm_wait_line_flag);
+
 static int vop_bind(struct device *dev, struct device *master, void *data)
 {
        struct platform_device *pdev = to_platform_device(dev);
@@ -1481,10 +1547,15 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
 
        ret = vop_create_crtc(vop);
        if (ret)
-               return ret;
+               goto err_enable_irq;
 
        pm_runtime_enable(&pdev->dev);
+
        return 0;
+
+err_enable_irq:
+       enable_irq(vop->irq); /* To balance out the disable_irq above */
+       return ret;
 }
 
 static void vop_unbind(struct device *dev, struct device *master, void *data)
index 071ff0b..1dbc526 100644 (file)
@@ -33,6 +33,7 @@ struct vop_reg {
        uint32_t offset;
        uint32_t shift;
        uint32_t mask;
+       bool write_mask;
 };
 
 struct vop_ctrl {
@@ -48,6 +49,10 @@ struct vop_ctrl {
        struct vop_reg dither_down;
        struct vop_reg dither_up;
        struct vop_reg pin_pol;
+       struct vop_reg rgb_pin_pol;
+       struct vop_reg hdmi_pin_pol;
+       struct vop_reg edp_pin_pol;
+       struct vop_reg mipi_pin_pol;
 
        struct vop_reg htotal_pw;
        struct vop_reg hact_st_end;
@@ -56,6 +61,8 @@ struct vop_ctrl {
        struct vop_reg hpost_st_end;
        struct vop_reg vpost_st_end;
 
+       struct vop_reg line_flag_num[2];
+
        struct vop_reg cfg_done;
 };
 
index 919992c..35c51f3 100644 (file)
 #define VOP_REG(off, _mask, s) \
                {.offset = off, \
                 .mask = _mask, \
-                .shift = s,}
+                .shift = s, \
+                .write_mask = false,}
+
+#define VOP_REG_MASK(off, _mask, s) \
+               {.offset = off, \
+                .mask = _mask, \
+                .shift = s, \
+                .write_mask = true,}
 
 static const uint32_t formats_win_full[] = {
        DRM_FORMAT_XRGB8888,
@@ -50,6 +57,89 @@ static const uint32_t formats_win_lite[] = {
        DRM_FORMAT_BGR565,
 };
 
+static const struct vop_scl_regs rk3036_win_scl = {
+       .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
+       .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
+       .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
+       .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
+};
+
+static const struct vop_win_phy rk3036_win0_data = {
+       .scl = &rk3036_win_scl,
+       .data_formats = formats_win_full,
+       .nformats = ARRAY_SIZE(formats_win_full),
+       .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
+       .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
+       .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
+       .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
+       .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
+       .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
+       .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
+       .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
+       .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
+       .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+};
+
+static const struct vop_win_phy rk3036_win1_data = {
+       .data_formats = formats_win_lite,
+       .nformats = ARRAY_SIZE(formats_win_lite),
+       .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
+       .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
+       .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
+       .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
+       .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
+       .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
+       .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
+       .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
+};
+
+static const struct vop_win_data rk3036_vop_win_data[] = {
+       { .base = 0x00, .phy = &rk3036_win0_data,
+         .type = DRM_PLANE_TYPE_PRIMARY },
+       { .base = 0x00, .phy = &rk3036_win1_data,
+         .type = DRM_PLANE_TYPE_CURSOR },
+};
+
+static const int rk3036_vop_intrs[] = {
+       DSP_HOLD_VALID_INTR,
+       FS_INTR,
+       LINE_FLAG_INTR,
+       BUS_ERROR_INTR,
+};
+
+static const struct vop_intr rk3036_intr = {
+       .intrs = rk3036_vop_intrs,
+       .nintrs = ARRAY_SIZE(rk3036_vop_intrs),
+       .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
+       .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
+       .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),
+};
+
+static const struct vop_ctrl rk3036_ctrl_data = {
+       .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30),
+       .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
+       .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
+       .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+       .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
+       .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+       .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
+       .line_flag_num[0] = VOP_REG(RK3036_INT_STATUS, 0xfff, 12),
+       .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
+};
+
+static const struct vop_reg_data rk3036_vop_init_reg_table[] = {
+       {RK3036_DSP_CTRL1, 0x00000000},
+};
+
+static const struct vop_data rk3036_vop = {
+       .init_table = rk3036_vop_init_reg_table,
+       .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table),
+       .ctrl = &rk3036_ctrl_data,
+       .intr = &rk3036_intr,
+       .win = rk3036_vop_win_data,
+       .win_size = ARRAY_SIZE(rk3036_vop_win_data),
+};
+
 static const struct vop_scl_extension rk3288_win_full_scl_ext = {
        .cbcr_vsd_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 31),
        .cbcr_vsu_mode = VOP_REG(RK3288_WIN0_CTRL1, 0x1, 30),
@@ -133,6 +223,7 @@ static const struct vop_ctrl rk3288_ctrl_data = {
        .vact_st_end = VOP_REG(RK3288_DSP_VACT_ST_END, 0x1fff1fff, 0),
        .hpost_st_end = VOP_REG(RK3288_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
        .vpost_st_end = VOP_REG(RK3288_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+       .line_flag_num[0] = VOP_REG(RK3288_INTR_CTRL0, 0x1fff, 12),
        .cfg_done = VOP_REG(RK3288_REG_CFG_DONE, 0x1, 0),
 };
 
@@ -190,93 +281,104 @@ static const struct vop_data rk3288_vop = {
        .win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
-static const struct vop_scl_regs rk3036_win_scl = {
-       .scale_yrgb_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 0x0),
-       .scale_yrgb_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_YRGB, 0xffff, 16),
-       .scale_cbcr_x = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 0x0),
-       .scale_cbcr_y = VOP_REG(RK3036_WIN0_SCL_FACTOR_CBR, 0xffff, 16),
-};
-
-static const struct vop_win_phy rk3036_win0_data = {
-       .scl = &rk3036_win_scl,
-       .data_formats = formats_win_full,
-       .nformats = ARRAY_SIZE(formats_win_full),
-       .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 0),
-       .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 3),
-       .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 15),
-       .act_info = VOP_REG(RK3036_WIN0_ACT_INFO, 0x1fff1fff, 0),
-       .dsp_info = VOP_REG(RK3036_WIN0_DSP_INFO, 0x0fff0fff, 0),
-       .dsp_st = VOP_REG(RK3036_WIN0_DSP_ST, 0x1fff1fff, 0),
-       .yrgb_mst = VOP_REG(RK3036_WIN0_YRGB_MST, 0xffffffff, 0),
-       .uv_mst = VOP_REG(RK3036_WIN0_CBR_MST, 0xffffffff, 0),
-       .yrgb_vir = VOP_REG(RK3036_WIN0_VIR, 0xffff, 0),
-       .uv_vir = VOP_REG(RK3036_WIN0_VIR, 0x1fff, 16),
+static const struct vop_ctrl rk3399_ctrl_data = {
+       .standby = VOP_REG(RK3399_SYS_CTRL, 0x1, 22),
+       .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23),
+       .rgb_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 12),
+       .hdmi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 13),
+       .edp_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 14),
+       .mipi_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 15),
+       .dither_down = VOP_REG(RK3399_DSP_CTRL1, 0xf, 1),
+       .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6),
+       .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19),
+       .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0),
+       .rgb_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 16),
+       .hdmi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 20),
+       .edp_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 24),
+       .mipi_pin_pol = VOP_REG(RK3399_DSP_CTRL1, 0xf, 28),
+       .htotal_pw = VOP_REG(RK3399_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
+       .hact_st_end = VOP_REG(RK3399_DSP_HACT_ST_END, 0x1fff1fff, 0),
+       .vtotal_pw = VOP_REG(RK3399_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
+       .vact_st_end = VOP_REG(RK3399_DSP_VACT_ST_END, 0x1fff1fff, 0),
+       .hpost_st_end = VOP_REG(RK3399_POST_DSP_HACT_INFO, 0x1fff1fff, 0),
+       .vpost_st_end = VOP_REG(RK3399_POST_DSP_VACT_INFO, 0x1fff1fff, 0),
+       .line_flag_num[0] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 0),
+       .line_flag_num[1] = VOP_REG(RK3399_LINE_FLAG, 0xffff, 16),
+       .cfg_done = VOP_REG_MASK(RK3399_REG_CFG_DONE, 0x1, 0),
 };
 
-static const struct vop_win_phy rk3036_win1_data = {
-       .data_formats = formats_win_lite,
-       .nformats = ARRAY_SIZE(formats_win_lite),
-       .enable = VOP_REG(RK3036_SYS_CTRL, 0x1, 1),
-       .format = VOP_REG(RK3036_SYS_CTRL, 0x7, 6),
-       .rb_swap = VOP_REG(RK3036_SYS_CTRL, 0x1, 19),
-       .act_info = VOP_REG(RK3036_WIN1_ACT_INFO, 0x1fff1fff, 0),
-       .dsp_info = VOP_REG(RK3036_WIN1_DSP_INFO, 0x0fff0fff, 0),
-       .dsp_st = VOP_REG(RK3036_WIN1_DSP_ST, 0x1fff1fff, 0),
-       .yrgb_mst = VOP_REG(RK3036_WIN1_MST, 0xffffffff, 0),
-       .yrgb_vir = VOP_REG(RK3036_WIN1_VIR, 0xffff, 0),
-};
-
-static const struct vop_win_data rk3036_vop_win_data[] = {
-       { .base = 0x00, .phy = &rk3036_win0_data,
-         .type = DRM_PLANE_TYPE_PRIMARY },
-       { .base = 0x00, .phy = &rk3036_win1_data,
-         .type = DRM_PLANE_TYPE_CURSOR },
-};
-
-static const int rk3036_vop_intrs[] = {
-       DSP_HOLD_VALID_INTR,
+static const int rk3399_vop_intrs[] = {
        FS_INTR,
+       0, 0,
        LINE_FLAG_INTR,
+       0,
        BUS_ERROR_INTR,
+       0, 0, 0, 0, 0, 0, 0,
+       DSP_HOLD_VALID_INTR,
 };
 
-static const struct vop_intr rk3036_intr = {
-       .intrs = rk3036_vop_intrs,
-       .nintrs = ARRAY_SIZE(rk3036_vop_intrs),
-       .status = VOP_REG(RK3036_INT_STATUS, 0xf, 0),
-       .enable = VOP_REG(RK3036_INT_STATUS, 0xf, 4),
-       .clear = VOP_REG(RK3036_INT_STATUS, 0xf, 8),
+static const struct vop_intr rk3399_vop_intr = {
+       .intrs = rk3399_vop_intrs,
+       .nintrs = ARRAY_SIZE(rk3399_vop_intrs),
+       .status = VOP_REG_MASK(RK3399_INTR_STATUS0, 0xffff, 0),
+       .enable = VOP_REG_MASK(RK3399_INTR_EN0, 0xffff, 0),
+       .clear = VOP_REG_MASK(RK3399_INTR_CLEAR0, 0xffff, 0),
 };
 
-static const struct vop_ctrl rk3036_ctrl_data = {
-       .standby = VOP_REG(RK3036_SYS_CTRL, 0x1, 30),
-       .out_mode = VOP_REG(RK3036_DSP_CTRL0, 0xf, 0),
-       .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
-       .htotal_pw = VOP_REG(RK3036_DSP_HTOTAL_HS_END, 0x1fff1fff, 0),
-       .hact_st_end = VOP_REG(RK3036_DSP_HACT_ST_END, 0x1fff1fff, 0),
-       .vtotal_pw = VOP_REG(RK3036_DSP_VTOTAL_VS_END, 0x1fff1fff, 0),
-       .vact_st_end = VOP_REG(RK3036_DSP_VACT_ST_END, 0x1fff1fff, 0),
-       .cfg_done = VOP_REG(RK3036_REG_CFG_DONE, 0x1, 0),
+static const struct vop_reg_data rk3399_init_reg_table[] = {
+       {RK3399_SYS_CTRL, 0x2000f800},
+       {RK3399_DSP_CTRL0, 0x00000000},
+       {RK3399_WIN0_CTRL0, 0x00000080},
+       {RK3399_WIN1_CTRL0, 0x00000080},
+       /* TODO: Win2/3 support multiple area function, but we haven't found
+        * a suitable way to use it yet, so let's just use them as other windows
+        * with only area 0 enabled.
+        */
+       {RK3399_WIN2_CTRL0, 0x00000010},
+       {RK3399_WIN3_CTRL0, 0x00000010},
 };
 
-static const struct vop_reg_data rk3036_vop_init_reg_table[] = {
-       {RK3036_DSP_CTRL1, 0x00000000},
+static const struct vop_data rk3399_vop_big = {
+       .init_table = rk3399_init_reg_table,
+       .table_size = ARRAY_SIZE(rk3399_init_reg_table),
+       .intr = &rk3399_vop_intr,
+       .ctrl = &rk3399_ctrl_data,
+       /*
+        * rk3399 vop big windows register layout is same as rk3288.
+        */
+       .win = rk3288_vop_win_data,
+       .win_size = ARRAY_SIZE(rk3288_vop_win_data),
 };
 
-static const struct vop_data rk3036_vop = {
-       .init_table = rk3036_vop_init_reg_table,
-       .table_size = ARRAY_SIZE(rk3036_vop_init_reg_table),
-       .ctrl = &rk3036_ctrl_data,
-       .intr = &rk3036_intr,
-       .win = rk3036_vop_win_data,
-       .win_size = ARRAY_SIZE(rk3036_vop_win_data),
+static const struct vop_win_data rk3399_vop_lit_win_data[] = {
+       { .base = 0x00, .phy = &rk3288_win01_data,
+         .type = DRM_PLANE_TYPE_PRIMARY },
+       { .base = 0x00, .phy = &rk3288_win23_data,
+         .type = DRM_PLANE_TYPE_CURSOR},
+};
+
+static const struct vop_data rk3399_vop_lit = {
+       .init_table = rk3399_init_reg_table,
+       .table_size = ARRAY_SIZE(rk3399_init_reg_table),
+       .intr = &rk3399_vop_intr,
+       .ctrl = &rk3399_ctrl_data,
+       /*
+        * rk3399 vop lit windows register layout is same as rk3288,
+        * but cut off the win1 and win3 windows.
+        */
+       .win = rk3399_vop_lit_win_data,
+       .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data),
 };
 
 static const struct of_device_id vop_driver_dt_match[] = {
-       { .compatible = "rockchip,rk3288-vop",
-         .data = &rk3288_vop },
        { .compatible = "rockchip,rk3036-vop",
          .data = &rk3036_vop },
+       { .compatible = "rockchip,rk3288-vop",
+         .data = &rk3288_vop },
+       { .compatible = "rockchip,rk3399-vop-big",
+         .data = &rk3399_vop_big },
+       { .compatible = "rockchip,rk3399-vop-lit",
+         .data = &rk3399_vop_lit },
        {},
 };
 MODULE_DEVICE_TABLE(of, vop_driver_dt_match);
@@ -305,7 +407,6 @@ static struct platform_driver vop_platform_driver = {
        .remove = vop_remove,
        .driver = {
                .name = "rockchip-vop",
-               .owner = THIS_MODULE,
                .of_match_table = of_match_ptr(vop_driver_dt_match),
        },
 };
index d4b46cb..cd19726 100644 (file)
 #define RK3036_HWC_LUT_ADDR            0x800
 /* rk3036 register definition end */
 
+/* rk3399 register definition */
+#define RK3399_REG_CFG_DONE            0x00000
+#define RK3399_VERSION_INFO            0x00004
+#define RK3399_SYS_CTRL                        0x00008
+#define RK3399_SYS_CTRL1               0x0000c
+#define RK3399_DSP_CTRL0               0x00010
+#define RK3399_DSP_CTRL1               0x00014
+#define RK3399_DSP_BG                  0x00018
+#define RK3399_MCU_CTRL                        0x0001c
+#define RK3399_WB_CTRL0                        0x00020
+#define RK3399_WB_CTRL1                        0x00024
+#define RK3399_WB_YRGB_MST             0x00028
+#define RK3399_WB_CBR_MST              0x0002c
+#define RK3399_WIN0_CTRL0              0x00030
+#define RK3399_WIN0_CTRL1              0x00034
+#define RK3399_WIN0_COLOR_KEY          0x00038
+#define RK3399_WIN0_VIR                        0x0003c
+#define RK3399_WIN0_YRGB_MST           0x00040
+#define RK3399_WIN0_CBR_MST            0x00044
+#define RK3399_WIN0_ACT_INFO           0x00048
+#define RK3399_WIN0_DSP_INFO           0x0004c
+#define RK3399_WIN0_DSP_ST             0x00050
+#define RK3399_WIN0_SCL_FACTOR_YRGB    0x00054
+#define RK3399_WIN0_SCL_FACTOR_CBR     0x00058
+#define RK3399_WIN0_SCL_OFFSET         0x0005c
+#define RK3399_WIN0_SRC_ALPHA_CTRL     0x00060
+#define RK3399_WIN0_DST_ALPHA_CTRL     0x00064
+#define RK3399_WIN0_FADING_CTRL                0x00068
+#define RK3399_WIN0_CTRL2              0x0006c
+#define RK3399_WIN1_CTRL0              0x00070
+#define RK3399_WIN1_CTRL1              0x00074
+#define RK3399_WIN1_COLOR_KEY          0x00078
+#define RK3399_WIN1_VIR                        0x0007c
+#define RK3399_WIN1_YRGB_MST           0x00080
+#define RK3399_WIN1_CBR_MST            0x00084
+#define RK3399_WIN1_ACT_INFO           0x00088
+#define RK3399_WIN1_DSP_INFO           0x0008c
+#define RK3399_WIN1_DSP_ST             0x00090
+#define RK3399_WIN1_SCL_FACTOR_YRGB    0x00094
+#define RK3399_WIN1_SCL_FACTOR_CBR     0x00098
+#define RK3399_WIN1_SCL_OFFSET         0x0009c
+#define RK3399_WIN1_SRC_ALPHA_CTRL     0x000a0
+#define RK3399_WIN1_DST_ALPHA_CTRL     0x000a4
+#define RK3399_WIN1_FADING_CTRL                0x000a8
+#define RK3399_WIN1_CTRL2              0x000ac
+#define RK3399_WIN2_CTRL0              0x000b0
+#define RK3399_WIN2_CTRL1              0x000b4
+#define RK3399_WIN2_VIR0_1             0x000b8
+#define RK3399_WIN2_VIR2_3             0x000bc
+#define RK3399_WIN2_MST0               0x000c0
+#define RK3399_WIN2_DSP_INFO0          0x000c4
+#define RK3399_WIN2_DSP_ST0            0x000c8
+#define RK3399_WIN2_COLOR_KEY          0x000cc
+#define RK3399_WIN2_MST1               0x000d0
+#define RK3399_WIN2_DSP_INFO1          0x000d4
+#define RK3399_WIN2_DSP_ST1            0x000d8
+#define RK3399_WIN2_SRC_ALPHA_CTRL     0x000dc
+#define RK3399_WIN2_MST2               0x000e0
+#define RK3399_WIN2_DSP_INFO2          0x000e4
+#define RK3399_WIN2_DSP_ST2            0x000e8
+#define RK3399_WIN2_DST_ALPHA_CTRL     0x000ec
+#define RK3399_WIN2_MST3               0x000f0
+#define RK3399_WIN2_DSP_INFO3          0x000f4
+#define RK3399_WIN2_DSP_ST3            0x000f8
+#define RK3399_WIN2_FADING_CTRL                0x000fc
+#define RK3399_WIN3_CTRL0              0x00100
+#define RK3399_WIN3_CTRL1              0x00104
+#define RK3399_WIN3_VIR0_1             0x00108
+#define RK3399_WIN3_VIR2_3             0x0010c
+#define RK3399_WIN3_MST0               0x00110
+#define RK3399_WIN3_DSP_INFO0          0x00114
+#define RK3399_WIN3_DSP_ST0            0x00118
+#define RK3399_WIN3_COLOR_KEY          0x0011c
+#define RK3399_WIN3_MST1               0x00120
+#define RK3399_WIN3_DSP_INFO1          0x00124
+#define RK3399_WIN3_DSP_ST1            0x00128
+#define RK3399_WIN3_SRC_ALPHA_CTRL     0x0012c
+#define RK3399_WIN3_MST2               0x00130
+#define RK3399_WIN3_DSP_INFO2          0x00134
+#define RK3399_WIN3_DSP_ST2            0x00138
+#define RK3399_WIN3_DST_ALPHA_CTRL     0x0013c
+#define RK3399_WIN3_MST3               0x00140
+#define RK3399_WIN3_DSP_INFO3          0x00144
+#define RK3399_WIN3_DSP_ST3            0x00148
+#define RK3399_WIN3_FADING_CTRL                0x0014c
+#define RK3399_HWC_CTRL0               0x00150
+#define RK3399_HWC_CTRL1               0x00154
+#define RK3399_HWC_MST                 0x00158
+#define RK3399_HWC_DSP_ST              0x0015c
+#define RK3399_HWC_SRC_ALPHA_CTRL      0x00160
+#define RK3399_HWC_DST_ALPHA_CTRL      0x00164
+#define RK3399_HWC_FADING_CTRL         0x00168
+#define RK3399_HWC_RESERVED1           0x0016c
+#define RK3399_POST_DSP_HACT_INFO      0x00170
+#define RK3399_POST_DSP_VACT_INFO      0x00174
+#define RK3399_POST_SCL_FACTOR_YRGB    0x00178
+#define RK3399_POST_RESERVED           0x0017c
+#define RK3399_POST_SCL_CTRL           0x00180
+#define RK3399_POST_DSP_VACT_INFO_F1   0x00184
+#define RK3399_DSP_HTOTAL_HS_END       0x00188
+#define RK3399_DSP_HACT_ST_END         0x0018c
+#define RK3399_DSP_VTOTAL_VS_END       0x00190
+#define RK3399_DSP_VACT_ST_END         0x00194
+#define RK3399_DSP_VS_ST_END_F1                0x00198
+#define RK3399_DSP_VACT_ST_END_F1      0x0019c
+#define RK3399_PWM_CTRL                        0x001a0
+#define RK3399_PWM_PERIOD_HPR          0x001a4
+#define RK3399_PWM_DUTY_LPR            0x001a8
+#define RK3399_PWM_CNT                 0x001ac
+#define RK3399_BCSH_COLOR_BAR          0x001b0
+#define RK3399_BCSH_BCS                        0x001b4
+#define RK3399_BCSH_H                  0x001b8
+#define RK3399_BCSH_CTRL               0x001bc
+#define RK3399_CABC_CTRL0              0x001c0
+#define RK3399_CABC_CTRL1              0x001c4
+#define RK3399_CABC_CTRL2              0x001c8
+#define RK3399_CABC_CTRL3              0x001cc
+#define RK3399_CABC_GAUSS_LINE0_0      0x001d0
+#define RK3399_CABC_GAUSS_LINE0_1      0x001d4
+#define RK3399_CABC_GAUSS_LINE1_0      0x001d8
+#define RK3399_CABC_GAUSS_LINE1_1      0x001dc
+#define RK3399_CABC_GAUSS_LINE2_0      0x001e0
+#define RK3399_CABC_GAUSS_LINE2_1      0x001e4
+#define RK3399_FRC_LOWER01_0           0x001e8
+#define RK3399_FRC_LOWER01_1           0x001ec
+#define RK3399_FRC_LOWER10_0           0x001f0
+#define RK3399_FRC_LOWER10_1           0x001f4
+#define RK3399_FRC_LOWER11_0           0x001f8
+#define RK3399_FRC_LOWER11_1           0x001fc
+#define RK3399_AFBCD0_CTRL             0x00200
+#define RK3399_AFBCD0_HDR_PTR          0x00204
+#define RK3399_AFBCD0_PIC_SIZE         0x00208
+#define RK3399_AFBCD0_STATUS           0x0020c
+#define RK3399_AFBCD1_CTRL             0x00220
+#define RK3399_AFBCD1_HDR_PTR          0x00224
+#define RK3399_AFBCD1_PIC_SIZE         0x00228
+#define RK3399_AFBCD1_STATUS           0x0022c
+#define RK3399_AFBCD2_CTRL             0x00240
+#define RK3399_AFBCD2_HDR_PTR          0x00244
+#define RK3399_AFBCD2_PIC_SIZE         0x00248
+#define RK3399_AFBCD2_STATUS           0x0024c
+#define RK3399_AFBCD3_CTRL             0x00260
+#define RK3399_AFBCD3_HDR_PTR          0x00264
+#define RK3399_AFBCD3_PIC_SIZE         0x00268
+#define RK3399_AFBCD3_STATUS           0x0026c
+#define RK3399_INTR_EN0                        0x00280
+#define RK3399_INTR_CLEAR0             0x00284
+#define RK3399_INTR_STATUS0            0x00288
+#define RK3399_INTR_RAW_STATUS0                0x0028c
+#define RK3399_INTR_EN1                        0x00290
+#define RK3399_INTR_CLEAR1             0x00294
+#define RK3399_INTR_STATUS1            0x00298
+#define RK3399_INTR_RAW_STATUS1                0x0029c
+#define RK3399_LINE_FLAG               0x002a0
+#define RK3399_VOP_STATUS              0x002a4
+#define RK3399_BLANKING_VALUE          0x002a8
+#define RK3399_MCU_BYPASS_PORT         0x002ac
+#define RK3399_WIN0_DSP_BG             0x002b0
+#define RK3399_WIN1_DSP_BG             0x002b4
+#define RK3399_WIN2_DSP_BG             0x002b8
+#define RK3399_WIN3_DSP_BG             0x002bc
+#define RK3399_YUV2YUV_WIN             0x002c0
+#define RK3399_YUV2YUV_POST            0x002c4
+#define RK3399_AUTO_GATING_EN          0x002cc
+#define RK3399_WIN0_CSC_COE            0x003a0
+#define RK3399_WIN1_CSC_COE            0x003c0
+#define RK3399_WIN2_CSC_COE            0x003e0
+#define RK3399_WIN3_CSC_COE            0x00400
+#define RK3399_HWC_CSC_COE             0x00420
+#define RK3399_BCSH_R2Y_CSC_COE                0x00440
+#define RK3399_BCSH_Y2R_CSC_COE                0x00460
+#define RK3399_POST_YUV2YUV_Y2R_COE    0x00480
+#define RK3399_POST_YUV2YUV_3X3_COE    0x004a0
+#define RK3399_POST_YUV2YUV_R2Y_COE    0x004c0
+#define RK3399_WIN0_YUV2YUV_Y2R                0x004e0
+#define RK3399_WIN0_YUV2YUV_3X3                0x00500
+#define RK3399_WIN0_YUV2YUV_R2Y                0x00520
+#define RK3399_WIN1_YUV2YUV_Y2R                0x00540
+#define RK3399_WIN1_YUV2YUV_3X3                0x00560
+#define RK3399_WIN1_YUV2YUV_R2Y                0x00580
+#define RK3399_WIN2_YUV2YUV_Y2R                0x005a0
+#define RK3399_WIN2_YUV2YUV_3X3                0x005c0
+#define RK3399_WIN2_YUV2YUV_R2Y                0x005e0
+#define RK3399_WIN3_YUV2YUV_Y2R                0x00600
+#define RK3399_WIN3_YUV2YUV_3X3                0x00620
+#define RK3399_WIN3_YUV2YUV_R2Y                0x00640
+#define RK3399_WIN2_LUT_ADDR           0x01000
+#define RK3399_WIN3_LUT_ADDR           0x01400
+#define RK3399_HWC_LUT_ADDR            0x01800
+#define RK3399_CABC_GAMMA_LUT_ADDR     0x01c00
+#define RK3399_GAMMA_LUT_ADDR          0x02000
+/* rk3399 register definition end */
+
 #endif /* _ROCKCHIP_VOP_REG_H */
index 21aed1f..3b80713 100644 (file)
@@ -50,7 +50,7 @@ static const struct file_operations savage_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features =
-           DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_PCI_DMA,
+           DRIVER_USE_AGP | DRIVER_HAVE_DMA | DRIVER_PCI_DMA | DRIVER_LEGACY,
        .dev_priv_size = sizeof(drm_savage_buf_priv_t),
        .load = savage_driver_load,
        .firstopen = savage_driver_firstopen,
index c01ad0a..3dc0d8f 100644 (file)
@@ -1001,15 +1001,9 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_
                cmdbuf->cmd_addr = kcmd_addr;
        }
        if (cmdbuf->vb_size) {
-               kvb_addr = kmalloc(cmdbuf->vb_size, GFP_KERNEL);
-               if (kvb_addr == NULL) {
-                       ret = -ENOMEM;
-                       goto done;
-               }
-
-               if (copy_from_user(kvb_addr, cmdbuf->vb_addr,
-                                      cmdbuf->vb_size)) {
-                       ret = -EFAULT;
+               kvb_addr = memdup_user(cmdbuf->vb_addr, cmdbuf->vb_size);
+               if (IS_ERR(kvb_addr)) {
+                       ret = PTR_ERR(kvb_addr);
                        goto done;
                }
                cmdbuf->vb_addr = kvb_addr;
index 79bce76..ae98398 100644 (file)
@@ -102,7 +102,7 @@ static void sis_driver_postclose(struct drm_device *dev, struct drm_file *file)
 }
 
 static struct drm_driver driver = {
-       .driver_features = DRIVER_USE_AGP,
+       .driver_features = DRIVER_USE_AGP | DRIVER_LEGACY,
        .load = sis_driver_load,
        .unload = sis_driver_unload,
        .open = sis_driver_open,
index 494ab25..acd7286 100644 (file)
@@ -1,6 +1,6 @@
 config DRM_STI
-       tristate "DRM Support for STMicroelectronics SoC stiH41x Series"
-       depends on DRM && (SOC_STIH415 || SOC_STIH416 || ARCH_MULTIPLATFORM)
+       tristate "DRM Support for STMicroelectronics SoC stiH4xx Series"
+       depends on DRM && (ARCH_STI || ARCH_MULTIPLATFORM)
        select RESET_CONTROLLER
        select DRM_KMS_HELPER
        select DRM_GEM_CMA_HELPER
@@ -9,4 +9,4 @@ config DRM_STI
        select FW_LOADER
        select SND_SOC_HDMI_CODEC if SND_SOC
        help
-         Choose this option to enable DRM on STM stiH41x chipset
+         Choose this option to enable DRM on STM stiH4xx chipset
index b805762..d20f7c0 100644 (file)
@@ -9,7 +9,6 @@ sti-drm-y := \
        sti_crtc.o \
        sti_plane.o \
        sti_hdmi.o \
-       sti_hdmi_tx3g0c55phy.o \
        sti_hdmi_tx3g4c28phy.o \
        sti_dvo.o \
        sti_awg_utils.o \
index 134201e..f62041f 100644 (file)
@@ -25,7 +25,7 @@
 /*
  * stiH407 compositor properties
  */
-struct sti_compositor_data stih407_compositor_data = {
+static const struct sti_compositor_data stih407_compositor_data = {
        .nb_subdev = 8,
        .subdev_desc = {
                        {STI_CURSOR_SUBDEV, (int)STI_CURSOR, 0x000},
@@ -39,38 +39,18 @@ struct sti_compositor_data stih407_compositor_data = {
        },
 };
 
-/*
- * stiH416 compositor properties
- * Note:
- * on stih416 MIXER_AUX has a different base address from MIXER_MAIN
- * Moreover, GDPx is different for Main and Aux Mixer. So this subdev map does
- * not fit for stiH416 if we want to enable the MIXER_AUX.
- */
-struct sti_compositor_data stih416_compositor_data = {
-       .nb_subdev = 3,
-       .subdev_desc = {
-                       {STI_GPD_SUBDEV, (int)STI_GDP_0, 0x100},
-                       {STI_GPD_SUBDEV, (int)STI_GDP_1, 0x200},
-                       {STI_MIXER_MAIN_SUBDEV, STI_MIXER_MAIN, 0xC00}
-       },
-};
-
-int sti_compositor_debufs_init(struct sti_compositor *compo,
-                              struct drm_minor *minor)
+int sti_compositor_debugfs_init(struct sti_compositor *compo,
+                               struct drm_minor *minor)
 {
-       int ret = 0, i;
+       unsigned int i;
 
-       for (i = 0; compo->vid[i]; i++) {
-               ret = vid_debugfs_init(compo->vid[i], minor);
-               if (ret)
-                       return ret;
-       }
+       for (i = 0; i < STI_MAX_VID; i++)
+               if (compo->vid[i])
+                       vid_debugfs_init(compo->vid[i], minor);
 
-       for (i = 0; compo->mixer[i]; i++) {
-               ret = sti_mixer_debugfs_init(compo->mixer[i], minor);
-               if (ret)
-                       return ret;
-       }
+       for (i = 0; i < STI_MAX_MIXER; i++)
+               if (compo->mixer[i])
+                       sti_mixer_debugfs_init(compo->mixer[i], minor);
 
        return 0;
 }
@@ -183,9 +163,6 @@ static const struct component_ops sti_compositor_ops = {
 
 static const struct of_device_id compositor_of_match[] = {
        {
-               .compatible = "st,stih416-compositor",
-               .data = &stih416_compositor_data,
-       }, {
                .compatible = "st,stih407-compositor",
                .data = &stih407_compositor_data,
        }, {
@@ -201,6 +178,7 @@ static int sti_compositor_probe(struct platform_device *pdev)
        struct device_node *vtg_np;
        struct sti_compositor *compo;
        struct resource *res;
+       unsigned int i;
 
        compo = devm_kzalloc(dev, sizeof(*compo), GFP_KERNEL);
        if (!compo) {
@@ -208,7 +186,8 @@ static int sti_compositor_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
        compo->dev = dev;
-       compo->vtg_vblank_nb.notifier_call = sti_crtc_vblank_cb;
+       for (i = 0; i < STI_MAX_MIXER; i++)
+               compo->vtg_vblank_nb[i].notifier_call = sti_crtc_vblank_cb;
 
        /* populate data structure depending on compatibility */
        BUG_ON(!of_match_node(compositor_of_match, np)->data);
@@ -266,12 +245,12 @@ static int sti_compositor_probe(struct platform_device *pdev)
 
        vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);
        if (vtg_np)
-               compo->vtg_main = of_vtg_find(vtg_np);
+               compo->vtg[STI_MIXER_MAIN] = of_vtg_find(vtg_np);
        of_node_put(vtg_np);
 
        vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 1);
        if (vtg_np)
-               compo->vtg_aux = of_vtg_find(vtg_np);
+               compo->vtg[STI_MIXER_AUX] = of_vtg_find(vtg_np);
        of_node_put(vtg_np);
 
        platform_set_drvdata(pdev, compo);
index 24444ef..2952a2d 100644 (file)
@@ -60,9 +60,8 @@ struct sti_compositor_data {
  * @rst_aux: reset control of the aux path
  * @mixer: array of mixers
  * @vid: array of vids
- * @vtg_main: vtg for main data path
- * @vtg_aux: vtg for auxillary data path
- * @vtg_vblank_nb: callback for VTG VSYNC notification
+ * @vtg: array of vtgs
+ * @vtg_vblank_nb: array of callbacks for VTG VSYNC notification
  */
 struct sti_compositor {
        struct device *dev;
@@ -76,12 +75,11 @@ struct sti_compositor {
        struct reset_control *rst_aux;
        struct sti_mixer *mixer[STI_MAX_MIXER];
        struct sti_vid *vid[STI_MAX_VID];
-       struct sti_vtg *vtg_main;
-       struct sti_vtg *vtg_aux;
-       struct notifier_block vtg_vblank_nb;
+       struct sti_vtg *vtg[STI_MAX_MIXER];
+       struct notifier_block vtg_vblank_nb[STI_MAX_MIXER];
 };
 
-int sti_compositor_debufs_init(struct sti_compositor *compo,
-                              struct drm_minor *minor);
+int sti_compositor_debugfs_init(struct sti_compositor *compo,
+                               struct drm_minor *minor);
 
 #endif
index c7d734d..e992bed 100644 (file)
@@ -86,8 +86,7 @@ sti_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode)
                goto pix_error;
        }
 
-       sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ?
-                       compo->vtg_main : compo->vtg_aux, &crtc->mode);
+       sti_vtg_set_config(compo->vtg[mixer->id], &crtc->mode);
 
        if (sti_mixer_active_video_area(mixer, &crtc->mode)) {
                DRM_ERROR("Can't set active video area\n");
@@ -166,6 +165,10 @@ static void sti_crtc_atomic_flush(struct drm_crtc *crtc,
 
                switch (plane->status) {
                case STI_PLANE_UPDATED:
+                       /* ignore update for other CRTC */
+                       if (p->state->crtc != crtc)
+                               continue;
+
                        /* update planes tag as updated */
                        DRM_DEBUG_DRIVER("update plane %s\n",
                                         sti_plane_to_str(plane));
@@ -244,8 +247,7 @@ static int sti_crtc_set_property(struct drm_crtc *crtc,
 int sti_crtc_vblank_cb(struct notifier_block *nb,
                       unsigned long event, void *data)
 {
-       struct sti_compositor *compo =
-               container_of(nb, struct sti_compositor, vtg_vblank_nb);
+       struct sti_compositor *compo;
        struct drm_crtc *crtc = data;
        struct sti_mixer *mixer;
        unsigned long flags;
@@ -254,6 +256,7 @@ int sti_crtc_vblank_cb(struct notifier_block *nb,
 
        priv = crtc->dev->dev_private;
        pipe = drm_crtc_index(crtc);
+       compo = container_of(nb, struct sti_compositor, vtg_vblank_nb[pipe]);
        mixer = compo->mixer[pipe];
 
        if ((event != VTG_TOP_FIELD_EVENT) &&
@@ -295,14 +298,13 @@ int sti_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct sti_private *dev_priv = dev->dev_private;
        struct sti_compositor *compo = dev_priv->compo;
-       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe];
        struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc;
+       struct sti_vtg *vtg = compo->vtg[pipe];
 
        DRM_DEBUG_DRIVER("\n");
 
-       if (sti_vtg_register_client(pipe == STI_MIXER_MAIN ?
-                       compo->vtg_main : compo->vtg_aux,
-                       vtg_vblank_nb, crtc)) {
+       if (sti_vtg_register_client(vtg, vtg_vblank_nb, crtc)) {
                DRM_ERROR("Cannot register VTG notifier\n");
                return -EINVAL;
        }
@@ -314,13 +316,13 @@ void sti_crtc_disable_vblank(struct drm_device *drm_dev, unsigned int pipe)
 {
        struct sti_private *priv = drm_dev->dev_private;
        struct sti_compositor *compo = priv->compo;
-       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb;
+       struct notifier_block *vtg_vblank_nb = &compo->vtg_vblank_nb[pipe];
        struct drm_crtc *crtc = &compo->mixer[pipe]->drm_crtc;
+       struct sti_vtg *vtg = compo->vtg[pipe];
 
        DRM_DEBUG_DRIVER("\n");
 
-       if (sti_vtg_unregister_client(pipe == STI_MIXER_MAIN ?
-                       compo->vtg_main : compo->vtg_aux, vtg_vblank_nb))
+       if (sti_vtg_unregister_client(vtg, vtg_vblank_nb))
                DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
 
        /* free the resources of the pending requests */
@@ -336,7 +338,7 @@ static int sti_crtc_late_register(struct drm_crtc *crtc)
        struct sti_compositor *compo = dev_get_drvdata(mixer->dev);
 
        if (drm_crtc_index(crtc) == 0)
-               return sti_compositor_debufs_init(compo, crtc->dev->primary);
+               return sti_compositor_debugfs_init(compo, crtc->dev->primary);
 
        return 0;
 }
index 3b53f7f..cca75bd 100644 (file)
@@ -309,15 +309,15 @@ static void sti_cursor_atomic_disable(struct drm_plane *drm_plane,
 {
        struct sti_plane *plane = to_sti_plane(drm_plane);
 
-       if (!drm_plane->crtc) {
+       if (!oldstate->crtc) {
                DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
                                 drm_plane->base.id);
                return;
        }
 
        DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
-                        drm_plane->crtc->base.id,
-                        sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
+                        oldstate->crtc->base.id,
+                        sti_mixer_to_str(to_sti_mixer(oldstate->crtc)),
                         drm_plane->base.id, sti_plane_to_str(plane));
 
        plane->status = STI_PLANE_DISABLING;
@@ -345,7 +345,7 @@ static int sti_cursor_late_register(struct drm_plane *drm_plane)
        return cursor_debugfs_init(cursor, drm_plane->dev->primary);
 }
 
-struct drm_plane_funcs sti_cursor_plane_helpers_funcs = {
+static const struct drm_plane_funcs sti_cursor_plane_helpers_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = sti_cursor_destroy,
index 96bd3d0..2784919 100644 (file)
@@ -140,7 +140,7 @@ err:
        return ret;
 }
 
-void sti_drm_dbg_cleanup(struct drm_minor *minor)
+static void sti_drm_dbg_cleanup(struct drm_minor *minor)
 {
        drm_debugfs_remove_files(sti_drm_dbg_list,
                                 ARRAY_SIZE(sti_drm_dbg_list), minor);
@@ -178,7 +178,7 @@ static void sti_atomic_complete(struct sti_private *private,
         */
 
        drm_atomic_helper_commit_modeset_disables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state, false);
+       drm_atomic_helper_commit_planes(drm, state, 0);
        drm_atomic_helper_commit_modeset_enables(drm, state);
 
        drm_atomic_helper_wait_for_vblanks(drm, state);
@@ -282,7 +282,7 @@ static const struct file_operations sti_driver_fops = {
 };
 
 static struct drm_driver sti_driver = {
-       .driver_features = DRIVER_HAVE_IRQ | DRIVER_MODESET |
+       .driver_features = DRIVER_MODESET |
            DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC,
        .gem_free_object_unlocked = drm_gem_cma_free_object,
        .gem_vm_ops = &drm_gem_cma_vm_ops,
@@ -365,8 +365,8 @@ static int sti_bind(struct device *dev)
        int ret;
 
        ddev = drm_dev_alloc(&sti_driver, dev);
-       if (!ddev)
-               return -ENOMEM;
+       if (IS_ERR(ddev))
+               return PTR_ERR(ddev);
 
        ddev->platformdev = to_platform_device(dev);
 
index 00881eb..e8c1ed0 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_panel.h>
 
 #include "sti_awg_utils.h"
+#include "sti_drv.h"
 #include "sti_mixer.h"
 
 /* DVO registers */
@@ -106,7 +107,7 @@ struct sti_dvo_connector {
        container_of(x, struct sti_dvo_connector, drm_connector)
 
 #define BLANKING_LEVEL 16
-int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code)
+static int dvo_awg_generate_code(struct sti_dvo *dvo, u8 *ram_size, u32 *ram_code)
 {
        struct drm_display_mode *mode = &dvo->mode;
        struct dvo_config *config = dvo->config;
index b8d942c..81df309 100644 (file)
@@ -460,6 +460,7 @@ static void sti_gdp_disable(struct sti_gdp *gdp)
                clk_disable_unprepare(gdp->clk_pix);
 
        gdp->plane.status = STI_PLANE_DISABLED;
+       gdp->vtg = NULL;
 }
 
 /**
@@ -473,8 +474,8 @@ static void sti_gdp_disable(struct sti_gdp *gdp)
  * RETURNS:
  * 0 on success.
  */
-int sti_gdp_field_cb(struct notifier_block *nb,
-               unsigned long event, void *data)
+static int sti_gdp_field_cb(struct notifier_block *nb,
+                           unsigned long event, void *data)
 {
        struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb);
 
@@ -611,7 +612,6 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane,
        struct drm_crtc *crtc = state->crtc;
        struct sti_compositor *compo = dev_get_drvdata(gdp->dev);
        struct drm_framebuffer *fb =  state->fb;
-       bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
        struct drm_crtc_state *crtc_state;
        struct sti_mixer *mixer;
        struct drm_display_mode *mode;
@@ -628,8 +628,8 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane,
        mode = &crtc_state->mode;
        dst_x = state->crtc_x;
        dst_y = state->crtc_y;
-       dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
-       dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x);
+       dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y);
        /* src_x are in 16.16 format */
        src_x = state->src_x >> 16;
        src_y = state->src_y >> 16;
@@ -648,10 +648,9 @@ static int sti_gdp_atomic_check(struct drm_plane *drm_plane,
                return -EINVAL;
        }
 
-       if (first_prepare) {
+       if (!gdp->vtg) {
                /* Register gdp callback */
-               gdp->vtg = mixer->id == STI_MIXER_MAIN ?
-                                       compo->vtg_main : compo->vtg_aux;
+               gdp->vtg = compo->vtg[mixer->id];
                if (sti_vtg_register_client(gdp->vtg,
                                            &gdp->vtg_field_nb, crtc)) {
                        DRM_ERROR("Cannot register VTG notifier\n");
@@ -719,7 +718,7 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
        u32 dma_updated_top;
        u32 dma_updated_btm;
        int format;
-       unsigned int depth, bpp;
+       unsigned int bpp;
        u32 ydo, xdo, yds, xds;
 
        if (!crtc || !fb)
@@ -728,8 +727,8 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
        mode = &crtc->mode;
        dst_x = state->crtc_x;
        dst_y = state->crtc_y;
-       dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
-       dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x);
+       dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y);
        /* src_x are in 16.16 format */
        src_x = state->src_x >> 16;
        src_y = state->src_y >> 16;
@@ -758,9 +757,9 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
                         (unsigned long)cma_obj->paddr);
 
        /* pixel memory location */
-       drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
+       bpp = drm_format_plane_cpp(fb->pixel_format, 0);
        top_field->gam_gdp_pml = (u32)cma_obj->paddr + fb->offsets[0];
-       top_field->gam_gdp_pml += src_x * (bpp >> 3);
+       top_field->gam_gdp_pml += src_x * bpp;
        top_field->gam_gdp_pml += src_y * fb->pitches[0];
 
        /* output parameters (clamped / cropped) */
@@ -810,7 +809,7 @@ static void sti_gdp_atomic_update(struct drm_plane *drm_plane,
        if (!curr_list) {
                /* First update or invalid node should directly write in the
                 * hw register */
-               DRM_DEBUG_DRIVER("%s first update (or invalid node)",
+               DRM_DEBUG_DRIVER("%s first update (or invalid node)\n",
                                 sti_plane_to_str(plane));
 
                writel(gdp->is_curr_top ?
@@ -846,15 +845,15 @@ static void sti_gdp_atomic_disable(struct drm_plane *drm_plane,
 {
        struct sti_plane *plane = to_sti_plane(drm_plane);
 
-       if (!drm_plane->crtc) {
+       if (!oldstate->crtc) {
                DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
                                 drm_plane->base.id);
                return;
        }
 
        DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
-                        drm_plane->crtc->base.id,
-                        sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
+                        oldstate->crtc->base.id,
+                        sti_mixer_to_str(to_sti_mixer(oldstate->crtc)),
                         drm_plane->base.id, sti_plane_to_str(plane));
 
        plane->status = STI_PLANE_DISABLING;
@@ -882,7 +881,7 @@ static int sti_gdp_late_register(struct drm_plane *drm_plane)
        return gdp_debugfs_init(gdp, drm_plane->dev->primary);
 }
 
-struct drm_plane_funcs sti_gdp_plane_helpers_funcs = {
+static const struct drm_plane_funcs sti_gdp_plane_helpers_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = sti_gdp_destroy,
index 8505569..e7c243f 100644 (file)
 #define SCALE_CTRL_CR_DFLT              0x00DB0249
 
 /* Video DACs control */
-#define VIDEO_DACS_CONTROL_MASK         0x0FFF
-#define VIDEO_DACS_CONTROL_SYSCFG2535   0x085C /* for stih416 */
-#define DAC_CFG_HD_OFF_SHIFT            5
-#define DAC_CFG_HD_OFF_MASK             (0x7 << DAC_CFG_HD_OFF_SHIFT)
-#define VIDEO_DACS_CONTROL_SYSCFG5072   0x0120 /* for stih407 */
 #define DAC_CFG_HD_HZUVW_OFF_MASK       BIT(1)
 
-
 /* Upsampler values for the alternative 2X Filter */
 #define SAMPLER_COEF_NB                 8
 #define HDA_ANA_SRC_Y_CFG_ALT_2X        0x01130000
@@ -300,28 +294,14 @@ static bool hda_get_mode_idx(struct drm_display_mode mode, int *idx)
  */
 static void hda_enable_hd_dacs(struct sti_hda *hda, bool enable)
 {
-       u32 mask;
-
        if (hda->video_dacs_ctrl) {
                u32 val;
 
-               switch ((u32)hda->video_dacs_ctrl & VIDEO_DACS_CONTROL_MASK) {
-               case VIDEO_DACS_CONTROL_SYSCFG2535:
-                       mask = DAC_CFG_HD_OFF_MASK;
-                       break;
-               case VIDEO_DACS_CONTROL_SYSCFG5072:
-                       mask = DAC_CFG_HD_HZUVW_OFF_MASK;
-                       break;
-               default:
-                       DRM_INFO("Video DACS control register not supported!");
-                       return;
-               }
-
                val = readl(hda->video_dacs_ctrl);
                if (enable)
-                       val &= ~mask;
+                       val &= ~DAC_CFG_HD_HZUVW_OFF_MASK;
                else
-                       val |= mask;
+                       val |= DAC_CFG_HD_HZUVW_OFF_MASK;
 
                writel(val, hda->video_dacs_ctrl);
        }
@@ -352,24 +332,11 @@ static void hda_dbg_awg_microcode(struct seq_file *s, void __iomem *reg)
 static void hda_dbg_video_dacs_ctrl(struct seq_file *s, void __iomem *reg)
 {
        u32 val = readl(reg);
-       u32 mask;
-
-       switch ((u32)reg & VIDEO_DACS_CONTROL_MASK) {
-       case VIDEO_DACS_CONTROL_SYSCFG2535:
-               mask = DAC_CFG_HD_OFF_MASK;
-               break;
-       case VIDEO_DACS_CONTROL_SYSCFG5072:
-               mask = DAC_CFG_HD_HZUVW_OFF_MASK;
-               break;
-       default:
-               DRM_DEBUG_DRIVER("Warning: DACS ctrl register not supported!");
-               return;
-       }
 
        seq_puts(s, "\n");
        seq_printf(s, "\n  %-25s 0x%08X", "VIDEO_DACS_CONTROL", val);
        seq_puts(s, "\tHD DACs ");
-       seq_puts(s, val & mask ? "disabled" : "enabled");
+       seq_puts(s, val & DAC_CFG_HD_HZUVW_OFF_MASK ? "disabled" : "enabled");
 }
 
 static int hda_dbg_show(struct seq_file *s, void *data)
index fedc17f..376b076 100644 (file)
@@ -22,7 +22,6 @@
 
 #include "sti_hdmi.h"
 #include "sti_hdmi_tx3g4c28phy.h"
-#include "sti_hdmi_tx3g0c55phy.h"
 #include "sti_vtg.h"
 
 #define HDMI_CFG                        0x0000
@@ -203,7 +202,7 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
 
        /* Audio FIFO underrun IRQ */
        if (hdmi->irq_status & HDMI_INT_AUDIO_FIFO_XRUN)
-               DRM_INFO("Warning: audio FIFO underrun occurs!");
+               DRM_INFO("Warning: audio FIFO underrun occurs!\n");
 
        return IRQ_HANDLED;
 }
@@ -569,7 +568,7 @@ static void hdmi_swreset(struct sti_hdmi *hdmi)
 
        /* Wait reset completed */
        wait_event_interruptible_timeout(hdmi->wait_event,
-                                        hdmi->event_received == true,
+                                        hdmi->event_received,
                                         msecs_to_jiffies
                                         (HDMI_TIMEOUT_SWRESET));
 
@@ -1054,6 +1053,7 @@ static int sti_hdmi_late_register(struct drm_connector *connector)
 }
 
 static const struct drm_connector_funcs sti_hdmi_connector_funcs = {
+       .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = sti_hdmi_connector_detect,
        .destroy = drm_connector_cleanup,
@@ -1181,7 +1181,7 @@ static void hdmi_audio_shutdown(struct device *dev, void *data)
                    HDMI_AUD_CFG_ONE_BIT_INVALID;
        hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
 
-       hdmi->audio.enabled = 0;
+       hdmi->audio.enabled = false;
        hdmi_audio_infoframe_config(hdmi);
 }
 
@@ -1213,7 +1213,7 @@ static int hdmi_audio_hw_params(struct device *dev,
                return -EINVAL;
        }
 
-       audio.enabled = 1;
+       audio.enabled = true;
 
        ret = hdmi_audio_configure(hdmi, &audio);
        if (ret < 0)
@@ -1265,7 +1265,7 @@ static int sti_hdmi_register_audio_driver(struct device *dev,
 
        DRM_DEBUG_DRIVER("\n");
 
-       hdmi->audio.enabled = 0;
+       hdmi->audio.enabled = false;
 
        hdmi->audio_pdev = platform_device_register_data(
                dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
@@ -1373,9 +1373,6 @@ static const struct component_ops sti_hdmi_ops = {
 
 static const struct of_device_id hdmi_of_match[] = {
        {
-               .compatible = "st,stih416-hdmi",
-               .data = &tx3g0c55phy_ops,
-       }, {
                .compatible = "st,stih407-hdmi",
                .data = &tx3g4c28phy_ops,
        }, {
@@ -1422,22 +1419,6 @@ static int sti_hdmi_probe(struct platform_device *pdev)
                goto release_adapter;
        }
 
-       if (of_device_is_compatible(np, "st,stih416-hdmi")) {
-               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                                                  "syscfg");
-               if (!res) {
-                       DRM_ERROR("Invalid syscfg resource\n");
-                       ret = -ENOMEM;
-                       goto release_adapter;
-               }
-               hdmi->syscfg = devm_ioremap_nocache(dev, res->start,
-                                                   resource_size(res));
-               if (!hdmi->syscfg) {
-                       ret = -ENOMEM;
-                       goto release_adapter;
-               }
-       }
-
        hdmi->phy_ops = (struct hdmi_phy_ops *)
                of_match_node(hdmi_of_match, np)->data;
 
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.c
deleted file mode 100644 (file)
index 49ae8e4..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics SA 2014
- * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
- * License terms:  GNU General Public License (GPL), version 2
- */
-
-#include "sti_hdmi_tx3g0c55phy.h"
-
-#define HDMI_SRZ_PLL_CFG                0x0504
-#define HDMI_SRZ_TAP_1                  0x0508
-#define HDMI_SRZ_TAP_2                  0x050C
-#define HDMI_SRZ_TAP_3                  0x0510
-#define HDMI_SRZ_CTRL                   0x0514
-
-#define HDMI_SRZ_PLL_CFG_POWER_DOWN     BIT(0)
-#define HDMI_SRZ_PLL_CFG_VCOR_SHIFT     1
-#define HDMI_SRZ_PLL_CFG_VCOR_425MHZ    0
-#define HDMI_SRZ_PLL_CFG_VCOR_850MHZ    1
-#define HDMI_SRZ_PLL_CFG_VCOR_1700MHZ   2
-#define HDMI_SRZ_PLL_CFG_VCOR_3000MHZ   3
-#define HDMI_SRZ_PLL_CFG_VCOR_MASK      3
-#define HDMI_SRZ_PLL_CFG_VCOR(x)        (x << HDMI_SRZ_PLL_CFG_VCOR_SHIFT)
-#define HDMI_SRZ_PLL_CFG_NDIV_SHIFT     8
-#define HDMI_SRZ_PLL_CFG_NDIV_MASK      (0x1F << HDMI_SRZ_PLL_CFG_NDIV_SHIFT)
-#define HDMI_SRZ_PLL_CFG_MODE_SHIFT     16
-#define HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ  0x1
-#define HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ  0x4
-#define HDMI_SRZ_PLL_CFG_MODE_27_MHZ    0x5
-#define HDMI_SRZ_PLL_CFG_MODE_33_75_MHZ 0x6
-#define HDMI_SRZ_PLL_CFG_MODE_40_5_MHZ  0x7
-#define HDMI_SRZ_PLL_CFG_MODE_54_MHZ    0x8
-#define HDMI_SRZ_PLL_CFG_MODE_67_5_MHZ  0x9
-#define HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ 0xA
-#define HDMI_SRZ_PLL_CFG_MODE_81_MHZ    0xB
-#define HDMI_SRZ_PLL_CFG_MODE_82_5_MHZ  0xC
-#define HDMI_SRZ_PLL_CFG_MODE_108_MHZ   0xD
-#define HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ 0xE
-#define HDMI_SRZ_PLL_CFG_MODE_165_MHZ   0xF
-#define HDMI_SRZ_PLL_CFG_MODE_MASK      0xF
-#define HDMI_SRZ_PLL_CFG_MODE(x)        (x << HDMI_SRZ_PLL_CFG_MODE_SHIFT)
-
-#define HDMI_SRZ_CTRL_POWER_DOWN        (1 << 0)
-#define HDMI_SRZ_CTRL_EXTERNAL_DATA_EN  (1 << 1)
-
-/* sysconf registers */
-#define HDMI_REJECTION_PLL_CONFIGURATION 0x0858        /* SYSTEM_CONFIG2534 */
-#define HDMI_REJECTION_PLL_STATUS        0x0948        /* SYSTEM_CONFIG2594 */
-
-#define REJECTION_PLL_HDMI_ENABLE_SHIFT 0
-#define REJECTION_PLL_HDMI_ENABLE_MASK  (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT)
-#define REJECTION_PLL_HDMI_PDIV_SHIFT   24
-#define REJECTION_PLL_HDMI_PDIV_MASK    (0x7 << REJECTION_PLL_HDMI_PDIV_SHIFT)
-#define REJECTION_PLL_HDMI_NDIV_SHIFT   16
-#define REJECTION_PLL_HDMI_NDIV_MASK    (0xFF << REJECTION_PLL_HDMI_NDIV_SHIFT)
-#define REJECTION_PLL_HDMI_MDIV_SHIFT   8
-#define REJECTION_PLL_HDMI_MDIV_MASK    (0xFF << REJECTION_PLL_HDMI_MDIV_SHIFT)
-
-#define REJECTION_PLL_HDMI_REJ_PLL_LOCK BIT(0)
-
-#define HDMI_TIMEOUT_PLL_LOCK  50   /*milliseconds */
-
-/**
- * pll mode structure
- *
- * A pointer to an array of these structures is passed to a TMDS (HDMI) output
- * via the control interface to provide board and SoC specific
- * configurations of the HDMI PHY. Each entry in the array specifies a hardware
- * specific configuration for a given TMDS clock frequency range. The array
- * should be terminated with an entry that has all fields set to zero.
- *
- * @min: Lower bound of TMDS clock frequency this entry applies to
- * @max: Upper bound of TMDS clock frequency this entry applies to
- * @mode: SoC specific register configuration
- */
-struct pllmode {
-       u32 min;
-       u32 max;
-       u32 mode;
-};
-
-#define NB_PLL_MODE 7
-static struct pllmode pllmodes[NB_PLL_MODE] = {
-       {13500000, 13513500, HDMI_SRZ_PLL_CFG_MODE_13_5_MHZ},
-       {25174800, 25200000, HDMI_SRZ_PLL_CFG_MODE_25_2_MHZ},
-       {27000000, 27027000, HDMI_SRZ_PLL_CFG_MODE_27_MHZ},
-       {54000000, 54054000, HDMI_SRZ_PLL_CFG_MODE_54_MHZ},
-       {72000000, 74250000, HDMI_SRZ_PLL_CFG_MODE_74_25_MHZ},
-       {108000000, 108108000, HDMI_SRZ_PLL_CFG_MODE_108_MHZ},
-       {148351648, 297000000, HDMI_SRZ_PLL_CFG_MODE_148_5_MHZ}
-};
-
-#define NB_HDMI_PHY_CONFIG 5
-static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
-       {0, 40000000, {0x00101010, 0x00101010, 0x00101010, 0x02} },
-       {40000000, 140000000, {0x00111111, 0x00111111, 0x00111111, 0x02} },
-       {140000000, 160000000, {0x00131313, 0x00101010, 0x00101010, 0x02} },
-       {160000000, 250000000, {0x00131313, 0x00111111, 0x00111111, 0x03FE} },
-       {250000000, 300000000, {0x00151515, 0x00101010, 0x00101010, 0x03FE} },
-};
-
-#define PLL_CHANGE_DELAY       1 /* ms */
-
-/**
- * Disable the pll rejection
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * return true if the pll has been disabled
- */
-static bool disable_pll_rejection(struct sti_hdmi *hdmi)
-{
-       u32 val;
-
-       DRM_DEBUG_DRIVER("\n");
-
-       val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
-       val &= ~REJECTION_PLL_HDMI_ENABLE_MASK;
-       writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
-
-       msleep(PLL_CHANGE_DELAY);
-       val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
-
-       return !(val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
-}
-
-/**
- * Enable the old BCH/rejection PLL is now reused to provide the CLKPXPLL
- * clock input to the new PHY PLL that generates the serializer clock
- * (TMDS*10) and the TMDS clock which is now fed back into the HDMI
- * formatter instead of the TMDS clock line from ClockGenB.
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * return true if pll has been correctly set
- */
-static bool enable_pll_rejection(struct sti_hdmi *hdmi)
-{
-       unsigned int inputclock;
-       u32 mdiv, ndiv, pdiv, val;
-
-       DRM_DEBUG_DRIVER("\n");
-
-       if (!disable_pll_rejection(hdmi))
-               return false;
-
-       inputclock = hdmi->mode.clock * 1000;
-
-       DRM_DEBUG_DRIVER("hdmi rejection pll input clock = %dHz\n", inputclock);
-
-
-       /* Power up the HDMI rejection PLL
-        * Note: On this SoC (stiH416) we are forced to have the input clock
-        * be equal to the HDMI pixel clock.
-        *
-        * The values here have been suggested by validation however they are
-        * still provisional and subject to change.
-        *
-        * PLLout = (Fin*Mdiv) / ((2 * Ndiv) / 2^Pdiv)
-        */
-       if (inputclock < 50000000) {
-               /*
-                * For slower clocks we need to multiply more to keep the
-                * internal VCO frequency within the physical specification
-                * of the PLL.
-                */
-               pdiv = 4;
-               ndiv = 240;
-               mdiv = 30;
-       } else {
-               pdiv = 2;
-               ndiv = 60;
-               mdiv = 30;
-       }
-
-       val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
-
-       val &= ~(REJECTION_PLL_HDMI_PDIV_MASK |
-               REJECTION_PLL_HDMI_NDIV_MASK |
-               REJECTION_PLL_HDMI_MDIV_MASK |
-               REJECTION_PLL_HDMI_ENABLE_MASK);
-
-       val |=  (pdiv << REJECTION_PLL_HDMI_PDIV_SHIFT) |
-               (ndiv << REJECTION_PLL_HDMI_NDIV_SHIFT) |
-               (mdiv << REJECTION_PLL_HDMI_MDIV_SHIFT) |
-               (0x1 << REJECTION_PLL_HDMI_ENABLE_SHIFT);
-
-       writel(val, hdmi->syscfg + HDMI_REJECTION_PLL_CONFIGURATION);
-
-       msleep(PLL_CHANGE_DELAY);
-       val = readl(hdmi->syscfg + HDMI_REJECTION_PLL_STATUS);
-
-       return (val & REJECTION_PLL_HDMI_REJ_PLL_LOCK);
-}
-
-/**
- * Start hdmi phy macro cell tx3g0c55
- *
- * @hdmi: pointer on the hdmi internal structure
- *
- * Return false if an error occur
- */
-static bool sti_hdmi_tx3g0c55phy_start(struct sti_hdmi *hdmi)
-{
-       u32 ckpxpll = hdmi->mode.clock * 1000;
-       u32 val, tmdsck, freqvco, pllctrl = 0;
-       unsigned int i;
-
-       if (!enable_pll_rejection(hdmi))
-               return false;
-
-       DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
-
-       /* Assuming no pixel repetition and 24bits color */
-       tmdsck = ckpxpll;
-       pllctrl = 2 << HDMI_SRZ_PLL_CFG_NDIV_SHIFT;
-
-       /*
-        * Setup the PLL mode parameter based on the ckpxpll. If we haven't got
-        * a clock frequency supported by one of the specific PLL modes then we
-        * will end up using the generic mode (0) which only supports a 10x
-        * multiplier, hence only 24bit color.
-        */
-       for (i = 0; i < NB_PLL_MODE; i++) {
-               if (ckpxpll >= pllmodes[i].min && ckpxpll <= pllmodes[i].max)
-                       pllctrl |= HDMI_SRZ_PLL_CFG_MODE(pllmodes[i].mode);
-       }
-
-       freqvco = tmdsck * 10;
-       if (freqvco <= 425000000UL)
-               pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_425MHZ);
-       else if (freqvco <= 850000000UL)
-               pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_850MHZ);
-       else if (freqvco <= 1700000000UL)
-               pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_1700MHZ);
-       else if (freqvco <= 2970000000UL)
-               pllctrl |= HDMI_SRZ_PLL_CFG_VCOR(HDMI_SRZ_PLL_CFG_VCOR_3000MHZ);
-       else {
-               DRM_ERROR("PHY serializer clock out of range\n");
-               goto err;
-       }
-
-       /*
-        * Configure and power up the PHY PLL
-        */
-       hdmi->event_received = false;
-       DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
-       hdmi_write(hdmi, pllctrl, HDMI_SRZ_PLL_CFG);
-
-       /* wait PLL interrupt */
-       wait_event_interruptible_timeout(hdmi->wait_event,
-                                        hdmi->event_received == true,
-                                        msecs_to_jiffies
-                                        (HDMI_TIMEOUT_PLL_LOCK));
-
-       if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
-               DRM_ERROR("hdmi phy pll not locked\n");
-               goto err;
-       }
-
-       DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
-
-       /*
-        * To configure the source termination and pre-emphasis appropriately
-        * for different high speed TMDS clock frequencies a phy configuration
-        * table must be provided, tailored to the SoC and board combination.
-        */
-       for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
-               if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
-                   (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
-                       val = hdmiphy_config[i].config[0];
-                       hdmi_write(hdmi, val, HDMI_SRZ_TAP_1);
-                       val = hdmiphy_config[i].config[1];
-                       hdmi_write(hdmi, val, HDMI_SRZ_TAP_2);
-                       val = hdmiphy_config[i].config[2];
-                       hdmi_write(hdmi, val, HDMI_SRZ_TAP_3);
-                       val = hdmiphy_config[i].config[3];
-                       val |= HDMI_SRZ_CTRL_EXTERNAL_DATA_EN;
-                       val &= ~HDMI_SRZ_CTRL_POWER_DOWN;
-                       hdmi_write(hdmi, val, HDMI_SRZ_CTRL);
-
-                       DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x 0x%x\n",
-                                        hdmiphy_config[i].config[0],
-                                        hdmiphy_config[i].config[1],
-                                        hdmiphy_config[i].config[2],
-                                        hdmiphy_config[i].config[3]);
-                       return true;
-               }
-       }
-
-       /*
-        * Default, power up the serializer with no pre-emphasis or source
-        * termination.
-        */
-       hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_1);
-       hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_2);
-       hdmi_write(hdmi, 0x0, HDMI_SRZ_TAP_3);
-       hdmi_write(hdmi, HDMI_SRZ_CTRL_EXTERNAL_DATA_EN, HDMI_SRZ_CTRL);
-
-       return true;
-
-err:
-       disable_pll_rejection(hdmi);
-
-       return false;
-}
-
-/**
- * Stop hdmi phy macro cell tx3g0c55
- *
- * @hdmi: pointer on the hdmi internal structure
- */
-static void sti_hdmi_tx3g0c55phy_stop(struct sti_hdmi *hdmi)
-{
-       DRM_DEBUG_DRIVER("\n");
-
-       hdmi->event_received = false;
-
-       hdmi_write(hdmi, HDMI_SRZ_CTRL_POWER_DOWN, HDMI_SRZ_CTRL);
-       hdmi_write(hdmi, HDMI_SRZ_PLL_CFG_POWER_DOWN, HDMI_SRZ_PLL_CFG);
-
-       /* wait PLL interrupt */
-       wait_event_interruptible_timeout(hdmi->wait_event,
-                                        hdmi->event_received == true,
-                                        msecs_to_jiffies
-                                        (HDMI_TIMEOUT_PLL_LOCK));
-
-       if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
-               DRM_ERROR("hdmi phy pll not well disabled\n");
-
-       disable_pll_rejection(hdmi);
-}
-
-struct hdmi_phy_ops tx3g0c55phy_ops = {
-       .start = sti_hdmi_tx3g0c55phy_start,
-       .stop = sti_hdmi_tx3g0c55phy_stop,
-};
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h b/drivers/gpu/drm/sti/sti_hdmi_tx3g0c55phy.h
deleted file mode 100644 (file)
index 068237b..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) STMicroelectronics SA 2014
- * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
- * License terms:  GNU General Public License (GPL), version 2
- */
-
-#ifndef _STI_HDMI_TX3G0C55PHY_H_
-#define _STI_HDMI_TX3G0C55PHY_H_
-
-#include "sti_hdmi.h"
-
-extern struct hdmi_phy_ops tx3g0c55phy_ops;
-
-#endif
index b5ee783..f88130f 100644 (file)
@@ -17,6 +17,7 @@
 #include "sti_hqvdp_lut.h"
 #include "sti_plane.h"
 #include "sti_vtg.h"
+#include "sti_drv.h"
 
 /* Firmware name */
 #define HQVDP_FMW_NAME          "hqvdp-stih407.bin"
@@ -770,6 +771,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp)
                DRM_ERROR("XP70 could not revert to idle\n");
 
        hqvdp->plane.status = STI_PLANE_DISABLED;
+       hqvdp->xp70_initialized = false;
 }
 
 /**
@@ -783,7 +785,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp)
  * RETURNS:
  * 0 on success.
  */
-int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
+static int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)
 {
        struct sti_hqvdp *hqvdp = container_of(nb, struct sti_hqvdp, vtg_nb);
        int btm_cmd_offset, top_cmd_offest;
@@ -1012,7 +1014,6 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
        struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);
        struct drm_crtc *crtc = state->crtc;
        struct drm_framebuffer *fb = state->fb;
-       bool first_prepare = plane->status == STI_PLANE_DISABLED ? true : false;
        struct drm_crtc_state *crtc_state;
        struct drm_display_mode *mode;
        int dst_x, dst_y, dst_w, dst_h;
@@ -1026,8 +1027,8 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
        mode = &crtc_state->mode;
        dst_x = state->crtc_x;
        dst_y = state->crtc_y;
-       dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
-       dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x);
+       dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y);
        /* src_x are in 16.16 format */
        src_x = state->src_x >> 16;
        src_y = state->src_y >> 16;
@@ -1063,7 +1064,7 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
                return -EINVAL;
        }
 
-       if (first_prepare) {
+       if (!hqvdp->xp70_initialized) {
                /* Start HQVDP XP70 coprocessor */
                sti_hqvdp_start_xp70(hqvdp);
 
@@ -1115,8 +1116,8 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
        mode = &crtc->mode;
        dst_x = state->crtc_x;
        dst_y = state->crtc_y;
-       dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
-       dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x);
+       dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y);
        /* src_x are in 16.16 format */
        src_x = state->src_x >> 16;
        src_y = state->src_y >> 16;
@@ -1214,15 +1215,15 @@ static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane,
 {
        struct sti_plane *plane = to_sti_plane(drm_plane);
 
-       if (!drm_plane->crtc) {
+       if (!oldstate->crtc) {
                DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
                                 drm_plane->base.id);
                return;
        }
 
        DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
-                        drm_plane->crtc->base.id,
-                        sti_mixer_to_str(to_sti_mixer(drm_plane->crtc)),
+                        oldstate->crtc->base.id,
+                        sti_mixer_to_str(to_sti_mixer(oldstate->crtc)),
                         drm_plane->base.id, sti_plane_to_str(plane));
 
        plane->status = STI_PLANE_DISABLING;
@@ -1250,7 +1251,7 @@ static int sti_hqvdp_late_register(struct drm_plane *drm_plane)
        return hqvdp_debugfs_init(hqvdp, drm_plane->dev->primary);
 }
 
-struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = {
+static const struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = {
        .update_plane = drm_atomic_helper_update_plane,
        .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = sti_hqvdp_destroy,
@@ -1289,7 +1290,7 @@ static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,
        return &hqvdp->plane.drm_plane;
 }
 
-int sti_hqvdp_bind(struct device *dev, struct device *master, void *data)
+static int sti_hqvdp_bind(struct device *dev, struct device *master, void *data)
 {
        struct sti_hqvdp *hqvdp = dev_get_drvdata(dev);
        struct drm_device *drm_dev = data;
index 7d9aea8..4ddc58f 100644 (file)
@@ -16,12 +16,6 @@ static unsigned int bkg_color = 0x000000;
 MODULE_PARM_DESC(bkgcolor, "Value of the background color 0xRRGGBB");
 module_param_named(bkgcolor, bkg_color, int, 0644);
 
-/* Identity: G=Y , B=Cb , R=Cr */
-static const u32 mixerColorSpaceMatIdentity[] = {
-       0x10000000, 0x00000000, 0x10000000, 0x00001000,
-       0x00000000, 0x00000000, 0x00000000, 0x00000000
-};
-
 /* regs offset */
 #define GAM_MIXER_CTL      0x00
 #define GAM_MIXER_BKC      0x04
@@ -358,22 +352,12 @@ int sti_mixer_set_plane_status(struct sti_mixer *mixer,
        return 0;
 }
 
-void sti_mixer_set_matrix(struct sti_mixer *mixer)
-{
-       unsigned int i;
-
-       for (i = 0; i < ARRAY_SIZE(mixerColorSpaceMatIdentity); i++)
-               sti_mixer_reg_write(mixer, GAM_MIXER_MX0 + (i * 4),
-                                   mixerColorSpaceMatIdentity[i]);
-}
-
 struct sti_mixer *sti_mixer_create(struct device *dev,
                                   struct drm_device *drm_dev,
                                   int id,
                                   void __iomem *baseaddr)
 {
        struct sti_mixer *mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
-       struct device_node *np = dev->of_node;
 
        dev_dbg(dev, "%s\n", __func__);
        if (!mixer) {
@@ -384,9 +368,6 @@ struct sti_mixer *sti_mixer_create(struct device *dev,
        mixer->dev = dev;
        mixer->id = id;
 
-       if (of_device_is_compatible(np, "st,stih416-compositor"))
-               sti_mixer_set_matrix(mixer);
-
        DRM_DEBUG_DRIVER("%s created. Regs=%p\n",
                         sti_mixer_to_str(mixer), mixer->regs);
 
index e25995b..ad46d35 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_crtc_helper.h>
 
 #include "sti_crtc.h"
+#include "sti_drv.h"
 #include "sti_vtg.h"
 
 /* glue registers */
@@ -209,13 +210,11 @@ static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd)
  * @tvout: tvout structure
  * @reg: register to set
  * @main_path: main or auxiliary path
- * @sel_input_logic_inverted: need to invert the logic
  * @sel_input: selected_input (main/aux + conv)
  */
 static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
                                    int reg,
                                    bool main_path,
-                                   bool sel_input_logic_inverted,
                                    enum sti_tvout_video_out_type video_out)
 {
        u32 sel_input;
@@ -236,8 +235,7 @@ static void tvout_vip_set_sel_input(struct sti_tvout *tvout,
        }
 
        /* on stih407 chip the sel_input bypass mode logic is inverted */
-       if (sel_input_logic_inverted)
-               sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
+       sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK;
 
        val &= ~TVO_VIP_SEL_INPUT_MASK;
        val |= sel_input;
@@ -295,8 +293,6 @@ static void tvout_preformatter_set_matrix(struct sti_tvout *tvout,
  */
 static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
 {
-       struct device_node *node = tvout->dev->of_node;
-       bool sel_input_logic_inverted = false;
        u32 tvo_in_vid_format;
        int val, tmp;
 
@@ -334,16 +330,11 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
        /* Set round mode (rounded to 8-bit per component) */
        tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED);
 
-       if (of_device_is_compatible(node, "st,stih407-tvout")) {
-               /* Set input video format */
-               tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format,
-                                        TVO_IN_FMT_SIGNED);
-               sel_input_logic_inverted = true;
-       }
+       /* Set input video format */
+       tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 
        /* Input selection */
        tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path,
-                               sel_input_logic_inverted,
                                STI_TVOUT_VIDEO_OUT_RGB);
 }
 
@@ -356,8 +347,6 @@ static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path)
  */
 static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
 {
-       struct device_node *node = tvout->dev->of_node;
-       bool sel_input_logic_inverted = false;
        u32 tvo_in_vid_format;
 
        dev_dbg(tvout->dev, "%s\n", __func__);
@@ -390,16 +379,12 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
        /* set round mode (rounded to 8-bit per component) */
        tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED);
 
-       if (of_device_is_compatible(node, "st,stih407-tvout")) {
-               /* set input video format */
-               tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format,
-                                       TVO_IN_FMT_SIGNED);
-               sel_input_logic_inverted = true;
-       }
+       /* set input video format */
+       tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 
        /* input selection */
        tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path,
-                       sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB);
+                               STI_TVOUT_VIDEO_OUT_RGB);
 }
 
 /**
@@ -411,8 +396,6 @@ static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path)
  */
 static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
 {
-       struct device_node *node = tvout->dev->of_node;
-       bool sel_input_logic_inverted = false;
        u32 tvo_in_vid_format;
        int val;
 
@@ -448,16 +431,11 @@ static void tvout_hda_start(struct sti_tvout *tvout, bool main_path)
        /* set round mode (rounded to 10-bit per component) */
        tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED);
 
-       if (of_device_is_compatible(node, "st,stih407-tvout")) {
-               /* set input video format */
-               tvout_vip_set_in_vid_fmt(tvout,
-                       tvo_in_vid_format, TVO_IN_FMT_SIGNED);
-               sel_input_logic_inverted = true;
-       }
+       /* Set input video format */
+       tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, TVO_IN_FMT_SIGNED);
 
        /* Input selection */
        tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path,
-                               sel_input_logic_inverted,
                                STI_TVOUT_VIDEO_OUT_YUV);
 
        /* power up HD DAC */
@@ -905,7 +883,6 @@ static int sti_tvout_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id tvout_of_match[] = {
-       { .compatible = "st,stih416-tvout", },
        { .compatible = "st,stih407-tvout", },
        { /* end node */ }
 };
index 47634a0..2ad5989 100644 (file)
@@ -142,8 +142,8 @@ void sti_vid_commit(struct sti_vid *vid,
        struct drm_display_mode *mode = &crtc->mode;
        int dst_x = state->crtc_x;
        int dst_y = state->crtc_y;
-       int dst_w = clamp_val(state->crtc_w, 0, mode->crtc_hdisplay - dst_x);
-       int dst_h = clamp_val(state->crtc_h, 0, mode->crtc_vdisplay - dst_y);
+       int dst_w = clamp_val(state->crtc_w, 0, mode->hdisplay - dst_x);
+       int dst_h = clamp_val(state->crtc_h, 0, mode->vdisplay - dst_y);
        int src_h = state->src_h >> 16;
        u32 val, ydo, xdo, yds, xds;
 
index b1eb0d7..cf7fe8a 100644 (file)
@@ -12,6 +12,8 @@
 
 #include <drm/drmP.h>
 
+#include "sti_drv.h"
+
 /* registers offset */
 #define VTAC_CONFIG                     0x00
 #define VTAC_RX_FIFO_CONFIG             0x04
index 0bdc385..a8882bd 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <drm/drmP.h>
 
+#include "sti_drv.h"
 #include "sti_vtg.h"
 
 #define VTG_MODE_MASTER         0
@@ -72,7 +73,7 @@
 #define AWG_DELAY_ED        (-8)
 #define AWG_DELAY_SD        (-7)
 
-LIST_HEAD(vtg_lookup);
+static LIST_HEAD(vtg_lookup);
 
 /*
  * STI VTG register offset structure
index 58cd551..d625a82 100644 (file)
@@ -9,5 +9,5 @@ sun4i-tcon-y += sun4i_dotclock.o
 
 obj-$(CONFIG_DRM_SUN4I)                += sun4i-drm.o sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)                += sun4i_backend.o
-
+obj-$(CONFIG_DRM_SUN4I)                += sun6i_drc.o
 obj-$(CONFIG_DRM_SUN4I)                += sun4i_tv.o
index 3ab5604..32c0584 100644 (file)
@@ -83,8 +83,13 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend,
 }
 EXPORT_SYMBOL(sun4i_backend_layer_enable);
 
-static int sun4i_backend_drm_format_to_layer(u32 format, u32 *mode)
+static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
+                                            u32 format, u32 *mode)
 {
+       if ((plane->type == DRM_PLANE_TYPE_PRIMARY) &&
+           (format == DRM_FORMAT_ARGB8888))
+               format = DRM_FORMAT_XRGB8888;
+
        switch (format) {
        case DRM_FORMAT_ARGB8888:
                *mode = SUN4I_BACKEND_LAY_FBFMT_ARGB8888;
@@ -164,7 +169,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
        DRM_DEBUG_DRIVER("Switching display backend interlaced mode %s\n",
                         interlaced ? "on" : "off");
 
-       ret = sun4i_backend_drm_format_to_layer(fb->pixel_format, &val);
+       ret = sun4i_backend_drm_format_to_layer(plane, fb->pixel_format, &val);
        if (ret) {
                DRM_DEBUG_DRIVER("Invalid format\n");
                return val;
@@ -217,6 +222,51 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
 }
 EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
 
+static int sun4i_backend_init_sat(struct device *dev) {
+       struct sun4i_backend *backend = dev_get_drvdata(dev);
+       int ret;
+
+       backend->sat_reset = devm_reset_control_get(dev, "sat");
+       if (IS_ERR(backend->sat_reset)) {
+               dev_err(dev, "Couldn't get the SAT reset line\n");
+               return PTR_ERR(backend->sat_reset);
+       }
+
+       ret = reset_control_deassert(backend->sat_reset);
+       if (ret) {
+               dev_err(dev, "Couldn't deassert the SAT reset line\n");
+               return ret;
+       }
+
+       backend->sat_clk = devm_clk_get(dev, "sat");
+       if (IS_ERR(backend->sat_clk)) {
+               dev_err(dev, "Couldn't get our SAT clock\n");
+               ret = PTR_ERR(backend->sat_clk);
+               goto err_assert_reset;
+       }
+
+       ret = clk_prepare_enable(backend->sat_clk);
+       if (ret) {
+               dev_err(dev, "Couldn't enable the SAT clock\n");
+               return ret;
+       }
+
+       return 0;
+
+err_assert_reset:
+       reset_control_assert(backend->sat_reset);
+       return ret;
+}
+
+static int sun4i_backend_free_sat(struct device *dev) {
+       struct sun4i_backend *backend = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(backend->sat_clk);
+       reset_control_assert(backend->sat_reset);
+
+       return 0;
+}
+
 static struct regmap_config sun4i_backend_regmap_config = {
        .reg_bits       = 32,
        .val_bits       = 32,
@@ -243,10 +293,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(regs)) {
-               dev_err(dev, "Couldn't map the backend registers\n");
+       if (IS_ERR(regs))
                return PTR_ERR(regs);
-       }
 
        backend->regs = devm_regmap_init_mmio(dev, regs,
                                              &sun4i_backend_regmap_config);
@@ -291,6 +339,15 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
        }
        clk_prepare_enable(backend->ram_clk);
 
+       if (of_device_is_compatible(dev->of_node,
+                                   "allwinner,sun8i-a33-display-backend")) {
+               ret = sun4i_backend_init_sat(dev);
+               if (ret) {
+                       dev_err(dev, "Couldn't init SAT resources\n");
+                       goto err_disable_ram_clk;
+               }
+       }
+
        /* Reset the registers */
        for (i = 0x800; i < 0x1000; i += 4)
                regmap_write(backend->regs, i, 0);
@@ -306,6 +363,8 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
 
        return 0;
 
+err_disable_ram_clk:
+       clk_disable_unprepare(backend->ram_clk);
 err_disable_mod_clk:
        clk_disable_unprepare(backend->mod_clk);
 err_disable_bus_clk:
@@ -320,6 +379,10 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master,
 {
        struct sun4i_backend *backend = dev_get_drvdata(dev);
 
+       if (of_device_is_compatible(dev->of_node,
+                                   "allwinner,sun8i-a33-display-backend"))
+               sun4i_backend_free_sat(dev);
+
        clk_disable_unprepare(backend->ram_clk);
        clk_disable_unprepare(backend->mod_clk);
        clk_disable_unprepare(backend->bus_clk);
@@ -345,6 +408,7 @@ static int sun4i_backend_remove(struct platform_device *pdev)
 
 static const struct of_device_id sun4i_backend_of_table[] = {
        { .compatible = "allwinner,sun5i-a13-display-backend" },
+       { .compatible = "allwinner,sun8i-a33-display-backend" },
        { }
 };
 MODULE_DEVICE_TABLE(of, sun4i_backend_of_table);
index 7070bb3..83e63cc 100644 (file)
@@ -52,8 +52,8 @@
 #define SUN4I_BACKEND_LAYFB_L32ADD_REG(l)      (0x850 + (0x4 * (l)))
 
 #define SUN4I_BACKEND_LAYFB_H4ADD_REG          0x860
-#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l)               GENMASK(3 + ((l) * 8), 0)
-#define SUN4I_BACKEND_LAYFB_H4ADD(l, val)                      ((val) << ((l) * 8))
+#define SUN4I_BACKEND_LAYFB_H4ADD_MSK(l)               GENMASK(3 + ((l) * 8), (l) * 8)
+#define SUN4I_BACKEND_LAYFB_H4ADD(l, val)              ((val) << ((l) * 8))
 
 #define SUN4I_BACKEND_REGBUFFCTL_REG           0x870
 #define SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS          BIT(1)
@@ -146,6 +146,9 @@ struct sun4i_backend {
        struct clk              *bus_clk;
        struct clk              *mod_clk;
        struct clk              *ram_clk;
+
+       struct clk              *sat_clk;
+       struct reset_control    *sat_reset;
 };
 
 void sun4i_backend_apply_color_correction(struct sun4i_backend *backend);
index 5b34631..d401156 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/regmap.h>
 
 #include "sun4i_tcon.h"
+#include "sun4i_dotclock.h"
 
 struct sun4i_dclk {
        struct clk_hw   hw;
@@ -61,7 +62,7 @@ static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
        regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
 
        val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
-       val &= SUN4I_TCON0_DCLK_DIV_WIDTH;
+       val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;
 
        if (!val)
                val = 1;
@@ -76,7 +77,7 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
        u8 best_div = 1;
        int i;
 
-       for (i = 6; i < 127; i++) {
+       for (i = 6; i <= 127; i++) {
                unsigned long ideal = rate * i;
                unsigned long rounded;
 
@@ -89,7 +90,8 @@ static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
                        goto out;
                }
 
-               if ((rounded < ideal) && (rounded > best_parent)) {
+               if (abs(rate - rounded / i) <
+                   abs(rate - best_parent / best_div)) {
                        best_parent = rounded;
                        best_div = i;
                }
index 7092daa..0da9862 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_helper.h>
 
 #include "sun4i_crtc.h"
 #include "sun4i_drv.h"
@@ -109,7 +110,7 @@ static void sun4i_remove_framebuffers(void)
        ap->ranges[0].base = 0;
        ap->ranges[0].size = ~0;
 
-       remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "sun4i-drm-fb", false);
        kfree(ap);
 }
 
@@ -120,8 +121,8 @@ static int sun4i_drv_bind(struct device *dev)
        int ret;
 
        drm = drm_dev_alloc(&sun4i_drv_driver, dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
        if (!drv) {
@@ -199,13 +200,14 @@ static const struct component_master_ops sun4i_drv_master_ops = {
 
 static bool sun4i_drv_node_is_frontend(struct device_node *node)
 {
-       return of_device_is_compatible(node,
-                                      "allwinner,sun5i-a13-display-frontend");
+       return of_device_is_compatible(node, "allwinner,sun5i-a13-display-frontend") ||
+               of_device_is_compatible(node, "allwinner,sun8i-a33-display-frontend");
 }
 
 static bool sun4i_drv_node_is_tcon(struct device_node *node)
 {
-       return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon");
+       return of_device_is_compatible(node, "allwinner,sun5i-a13-tcon") ||
+               of_device_is_compatible(node, "allwinner,sun8i-a33-tcon");
 }
 
 static int compare_of(struct device *dev, void *data)
@@ -257,8 +259,8 @@ static int sun4i_drv_add_endpoints(struct device *dev,
                }
 
                /*
-                * If the node is our TCON, the first port is used for our
-                * panel, and will not be part of the
+                * If the node is our TCON, the first port is used for
+                * panel or bridges, and will not be part of the
                 * component framework.
                 */
                if (sun4i_drv_node_is_tcon(node)) {
@@ -320,6 +322,7 @@ static int sun4i_drv_remove(struct platform_device *pdev)
 
 static const struct of_device_id sun4i_drv_of_table[] = {
        { .compatible = "allwinner,sun5i-a13-display-engine" },
+       { .compatible = "allwinner,sun8i-a33-display-engine" },
        { }
 };
 MODULE_DEVICE_TABLE(of, sun4i_drv_of_table);
index 70688fe..8b6ce61 100644 (file)
@@ -15,6 +15,7 @@
 #include <drm/drmP.h>
 
 #include "sun4i_drv.h"
+#include "sun4i_framebuffer.h"
 
 static void sun4i_de_output_poll_changed(struct drm_device *drm)
 {
index 068ab80..f0035bf 100644 (file)
 #include "sun4i_drv.h"
 #include "sun4i_layer.h"
 
-#define SUN4I_NUM_LAYERS       2
+struct sun4i_plane_desc {
+              enum drm_plane_type     type;
+              u8                      pipe;
+              const uint32_t          *formats;
+              uint32_t                nformats;
+};
 
 static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
                                            struct drm_plane_state *state)
@@ -65,14 +70,35 @@ static const struct drm_plane_funcs sun4i_backend_layer_funcs = {
        .update_plane           = drm_atomic_helper_update_plane,
 };
 
-static const uint32_t sun4i_backend_layer_formats[] = {
+static const uint32_t sun4i_backend_layer_formats_primary[] = {
        DRM_FORMAT_ARGB8888,
+       DRM_FORMAT_RGB888,
        DRM_FORMAT_XRGB8888,
+};
+
+static const uint32_t sun4i_backend_layer_formats_overlay[] = {
+       DRM_FORMAT_ARGB8888,
        DRM_FORMAT_RGB888,
+       DRM_FORMAT_XRGB8888,
+};
+
+static const struct sun4i_plane_desc sun4i_backend_planes[] = {
+       {
+               .type = DRM_PLANE_TYPE_PRIMARY,
+               .pipe = 0,
+               .formats = sun4i_backend_layer_formats_primary,
+               .nformats = ARRAY_SIZE(sun4i_backend_layer_formats_primary),
+       },
+       {
+               .type = DRM_PLANE_TYPE_OVERLAY,
+               .pipe = 1,
+               .formats = sun4i_backend_layer_formats_overlay,
+               .nformats = ARRAY_SIZE(sun4i_backend_layer_formats_overlay),
+       },
 };
 
 static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
-                                               enum drm_plane_type type)
+                                               const struct sun4i_plane_desc *plane)
 {
        struct sun4i_drv *drv = drm->dev_private;
        struct sun4i_layer *layer;
@@ -84,10 +110,8 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
 
        ret = drm_universal_plane_init(drm, &layer->plane, BIT(0),
                                       &sun4i_backend_layer_funcs,
-                                      sun4i_backend_layer_formats,
-                                      ARRAY_SIZE(sun4i_backend_layer_formats),
-                                      type,
-                                      NULL);
+                                      plane->formats, plane->nformats,
+                                      plane->type, NULL);
        if (ret) {
                dev_err(drm->dev, "Couldn't initialize layer\n");
                return ERR_PTR(ret);
@@ -97,7 +121,7 @@ static struct sun4i_layer *sun4i_layer_init_one(struct drm_device *drm,
                             &sun4i_backend_layer_helper_funcs);
        layer->drv = drv;
 
-       if (type == DRM_PLANE_TYPE_PRIMARY)
+       if (plane->type == DRM_PLANE_TYPE_PRIMARY)
                drv->primary = &layer->plane;
 
        return layer;
@@ -109,8 +133,8 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
        struct sun4i_layer **layers;
        int i;
 
-       layers = devm_kcalloc(drm->dev, SUN4I_NUM_LAYERS, sizeof(**layers),
-                             GFP_KERNEL);
+       layers = devm_kcalloc(drm->dev, ARRAY_SIZE(sun4i_backend_planes),
+                             sizeof(**layers), GFP_KERNEL);
        if (!layers)
                return ERR_PTR(-ENOMEM);
 
@@ -135,13 +159,11 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
         * SoCs that support it, sprites could fill the need for more
         * layers.
         */
-       for (i = 0; i < SUN4I_NUM_LAYERS; i++) {
-               enum drm_plane_type type = (i == 0)
-                                        ? DRM_PLANE_TYPE_PRIMARY
-                                        : DRM_PLANE_TYPE_OVERLAY;
+       for (i = 0; i < ARRAY_SIZE(sun4i_backend_planes); i++) {
+               const struct sun4i_plane_desc *plane = &sun4i_backend_planes[i];
                struct sun4i_layer *layer = layers[i];
 
-               layer = sun4i_layer_init_one(drm, type);
+               layer = sun4i_layer_init_one(drm, plane);
                if (IS_ERR(layer)) {
                        dev_err(drm->dev, "Couldn't initialize %s plane\n",
                                i ? "overlay" : "primary");
@@ -149,10 +171,10 @@ struct sun4i_layer **sun4i_layers_init(struct drm_device *drm)
                };
 
                DRM_DEBUG_DRIVER("Assigning %s plane to pipe %d\n",
-                                i ? "overlay" : "primary", i);
+                                i ? "overlay" : "primary", plane->pipe);
                regmap_update_bits(drv->backend->regs, SUN4I_BACKEND_ATTCTL_REG0(i),
                                   SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL_MASK,
-                                  SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(i));
+                                  SUN4I_BACKEND_ATTCTL_REG0_LAY_PIPESEL(plane->pipe));
 
                layer->id = i;
        };
index f5bbac6..c3ff10f 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "sun4i_drv.h"
 #include "sun4i_tcon.h"
+#include "sun4i_rgb.h"
 
 struct sun4i_rgb {
        struct drm_connector    connector;
@@ -151,7 +152,14 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
 
        DRM_DEBUG_DRIVER("Enabling RGB output\n");
 
-       drm_panel_enable(tcon->panel);
+       if (!IS_ERR(tcon->panel)) {
+               drm_panel_prepare(tcon->panel);
+               drm_panel_enable(tcon->panel);
+       }
+
+       /* encoder->bridge can be NULL; drm_bridge_enable checks for it */
+       drm_bridge_enable(encoder->bridge);
+
        sun4i_tcon_channel_enable(tcon, 0);
 }
 
@@ -164,7 +172,14 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
        DRM_DEBUG_DRIVER("Disabling RGB output\n");
 
        sun4i_tcon_channel_disable(tcon, 0);
-       drm_panel_disable(tcon->panel);
+
+       /* encoder->bridge can be NULL; drm_bridge_disable checks for it */
+       drm_bridge_disable(encoder->bridge);
+
+       if (!IS_ERR(tcon->panel)) {
+               drm_panel_disable(tcon->panel);
+               drm_panel_unprepare(tcon->panel);
+       }
 }
 
 static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
@@ -203,17 +218,22 @@ int sun4i_rgb_init(struct drm_device *drm)
 {
        struct sun4i_drv *drv = drm->dev_private;
        struct sun4i_tcon *tcon = drv->tcon;
+       struct drm_encoder *encoder;
        struct sun4i_rgb *rgb;
        int ret;
 
-       /* If we don't have a panel, there's no point in going on */
-       if (IS_ERR(tcon->panel))
-               return -ENODEV;
-
        rgb = devm_kzalloc(drm->dev, sizeof(*rgb), GFP_KERNEL);
        if (!rgb)
                return -ENOMEM;
        rgb->drv = drv;
+       encoder = &rgb->encoder;
+
+       tcon->panel = sun4i_tcon_find_panel(tcon->dev->of_node);
+       encoder->bridge = sun4i_tcon_find_bridge(tcon->dev->of_node);
+       if (IS_ERR(tcon->panel) && IS_ERR(encoder->bridge)) {
+               dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n");
+               return 0;
+       }
 
        drm_encoder_helper_add(&rgb->encoder,
                               &sun4i_rgb_enc_helper_funcs);
@@ -230,19 +250,38 @@ int sun4i_rgb_init(struct drm_device *drm)
        /* The RGB encoder can only work with the TCON channel 0 */
        rgb->encoder.possible_crtcs = BIT(0);
 
-       drm_connector_helper_add(&rgb->connector,
-                                &sun4i_rgb_con_helper_funcs);
-       ret = drm_connector_init(drm, &rgb->connector,
-                                &sun4i_rgb_con_funcs,
-                                DRM_MODE_CONNECTOR_Unknown);
-       if (ret) {
-               dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
-               goto err_cleanup_connector;
+       if (!IS_ERR(tcon->panel)) {
+               drm_connector_helper_add(&rgb->connector,
+                                        &sun4i_rgb_con_helper_funcs);
+               ret = drm_connector_init(drm, &rgb->connector,
+                                        &sun4i_rgb_con_funcs,
+                                        DRM_MODE_CONNECTOR_Unknown);
+               if (ret) {
+                       dev_err(drm->dev, "Couldn't initialise the rgb connector\n");
+                       goto err_cleanup_connector;
+               }
+
+               drm_mode_connector_attach_encoder(&rgb->connector,
+                                                 &rgb->encoder);
+
+               ret = drm_panel_attach(tcon->panel, &rgb->connector);
+               if (ret) {
+                       dev_err(drm->dev, "Couldn't attach our panel\n");
+                       goto err_cleanup_connector;
+               }
        }
 
-       drm_mode_connector_attach_encoder(&rgb->connector, &rgb->encoder);
+       if (!IS_ERR(encoder->bridge)) {
+               encoder->bridge->encoder = &rgb->encoder;
 
-       drm_panel_attach(tcon->panel, &rgb->connector);
+               ret = drm_bridge_attach(drm, encoder->bridge);
+               if (ret) {
+                       dev_err(drm->dev, "Couldn't attach our bridge\n");
+                       goto err_cleanup_connector;
+               }
+       } else {
+               encoder->bridge = NULL;
+       }
 
        return 0;
 
index 652385f..cadacb5 100644 (file)
@@ -59,11 +59,13 @@ void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
                regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
                                   SUN4I_TCON0_CTL_TCON_ENABLE, 0);
                clk_disable_unprepare(tcon->dclk);
-       } else if (channel == 1) {
-               regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
-                                  SUN4I_TCON1_CTL_TCON_ENABLE, 0);
-               clk_disable_unprepare(tcon->sclk1);
+               return;
        }
+
+       WARN_ON(!tcon->has_channel_1);
+       regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+                          SUN4I_TCON1_CTL_TCON_ENABLE, 0);
+       clk_disable_unprepare(tcon->sclk1);
 }
 EXPORT_SYMBOL(sun4i_tcon_channel_disable);
 
@@ -75,12 +77,14 @@ void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
                                   SUN4I_TCON0_CTL_TCON_ENABLE,
                                   SUN4I_TCON0_CTL_TCON_ENABLE);
                clk_prepare_enable(tcon->dclk);
-       } else if (channel == 1) {
-               regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
-                                  SUN4I_TCON1_CTL_TCON_ENABLE,
-                                  SUN4I_TCON1_CTL_TCON_ENABLE);
-               clk_prepare_enable(tcon->sclk1);
+               return;
        }
+
+       WARN_ON(!tcon->has_channel_1);
+       regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+                          SUN4I_TCON1_CTL_TCON_ENABLE,
+                          SUN4I_TCON1_CTL_TCON_ENABLE);
+       clk_prepare_enable(tcon->sclk1);
 }
 EXPORT_SYMBOL(sun4i_tcon_channel_enable);
 
@@ -198,6 +202,8 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
        u8 clk_delay;
        u32 val;
 
+       WARN_ON(!tcon->has_channel_1);
+
        /* Adjust clock delay */
        clk_delay = sun4i_tcon_get_clk_delay(mode, 1);
        regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
@@ -321,10 +327,12 @@ static int sun4i_tcon_init_clocks(struct device *dev,
                return PTR_ERR(tcon->sclk0);
        }
 
-       tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
-       if (IS_ERR(tcon->sclk1)) {
-               dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
-               return PTR_ERR(tcon->sclk1);
+       if (tcon->has_channel_1) {
+               tcon->sclk1 = devm_clk_get(dev, "tcon-ch1");
+               if (IS_ERR(tcon->sclk1)) {
+                       dev_err(dev, "Couldn't get the TCON channel 1 clock\n");
+                       return PTR_ERR(tcon->sclk1);
+               }
        }
 
        return sun4i_dclk_create(dev, tcon);
@@ -374,10 +382,8 @@ static int sun4i_tcon_init_regmap(struct device *dev,
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(regs)) {
-               dev_err(dev, "Couldn't map the TCON registers\n");
+       if (IS_ERR(regs))
                return PTR_ERR(regs);
-       }
 
        tcon->regs = devm_regmap_init_mmio(dev, regs,
                                           &sun4i_tcon_regmap_config);
@@ -398,7 +404,7 @@ static int sun4i_tcon_init_regmap(struct device *dev,
        return 0;
 }
 
-static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
+struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
 {
        struct device_node *port, *remote, *child;
        struct device_node *end_node = NULL;
@@ -432,6 +438,40 @@ static struct drm_panel *sun4i_tcon_find_panel(struct device_node *node)
        return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER);
 }
 
+struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node)
+{
+       struct device_node *port, *remote, *child;
+       struct device_node *end_node = NULL;
+
+       /* Inputs are listed first, then outputs */
+       port = of_graph_get_port_by_id(node, 1);
+
+       /*
+        * Our first output is the RGB interface where the panel will
+        * be connected.
+        */
+       for_each_child_of_node(port, child) {
+               u32 reg;
+
+               of_property_read_u32(child, "reg", &reg);
+               if (reg == 0)
+                       end_node = child;
+       }
+
+       if (!end_node) {
+               DRM_DEBUG_DRIVER("Missing bridge endpoint\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       remote = of_graph_get_remote_port_parent(end_node);
+       if (!remote) {
+               DRM_DEBUG_DRIVER("Enable to parse remote node\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER);
+}
+
 static int sun4i_tcon_bind(struct device *dev, struct device *master,
                           void *data)
 {
@@ -446,9 +486,15 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
        dev_set_drvdata(dev, tcon);
        drv->tcon = tcon;
        tcon->drm = drm;
+       tcon->dev = dev;
 
-       if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon"))
+       if (of_device_is_compatible(dev->of_node, "allwinner,sun5i-a13-tcon")) {
                tcon->has_mux = true;
+               tcon->has_channel_1 = true;
+       } else {
+               tcon->has_mux = false;
+               tcon->has_channel_1 = false;
+       }
 
        tcon->lcd_rst = devm_reset_control_get(dev, "lcd");
        if (IS_ERR(tcon->lcd_rst)) {
@@ -484,12 +530,6 @@ static int sun4i_tcon_bind(struct device *dev, struct device *master,
                goto err_free_clocks;
        }
 
-       tcon->panel = sun4i_tcon_find_panel(dev->of_node);
-       if (IS_ERR(tcon->panel)) {
-               dev_info(dev, "No panel found... RGB output disabled\n");
-               return 0;
-       }
-
        ret = sun4i_rgb_init(drm);
        if (ret < 0)
                goto err_free_clocks;
@@ -519,19 +559,22 @@ static struct component_ops sun4i_tcon_ops = {
 static int sun4i_tcon_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
+       struct drm_bridge *bridge;
        struct drm_panel *panel;
 
        /*
-        * The panel is not ready.
+        * Neither the bridge or the panel is ready.
         * Defer the probe.
         */
        panel = sun4i_tcon_find_panel(node);
+       bridge = sun4i_tcon_find_bridge(node);
 
        /*
         * If we don't have a panel endpoint, just go on
         */
-       if (PTR_ERR(panel) == -EPROBE_DEFER) {
-               DRM_DEBUG_DRIVER("Still waiting for our panel. Deferring...\n");
+       if ((PTR_ERR(panel) == -EPROBE_DEFER) &&
+           (PTR_ERR(bridge) == -EPROBE_DEFER)) {
+               DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n");
                return -EPROBE_DEFER;
        }
 
@@ -547,6 +590,7 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
 
 static const struct of_device_id sun4i_tcon_of_table[] = {
        { .compatible = "allwinner,sun5i-a13-tcon" },
+       { .compatible = "allwinner,sun8i-a33-tcon" },
        { }
 };
 MODULE_DEVICE_TABLE(of, sun4i_tcon_of_table);
index 0e0b11d..12bd489 100644 (file)
 #define SUN4I_TCON_MAX_CHANNELS                2
 
 struct sun4i_tcon {
+       struct device                   *dev;
        struct drm_device               *drm;
        struct regmap                   *regs;
 
@@ -163,8 +164,13 @@ struct sun4i_tcon {
        bool                            has_mux;
 
        struct drm_panel                *panel;
+
+       bool                            has_channel_1;
 };
 
+struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
+struct drm_panel *sun4i_tcon_find_panel(struct device_node *node);
+
 /* Global Control */
 void sun4i_tcon_disable(struct sun4i_tcon *tcon);
 void sun4i_tcon_enable(struct sun4i_tcon *tcon);
index b841478..1dd3d9e 100644 (file)
@@ -161,10 +161,10 @@ struct tv_mode {
        bool            dac3_en;
        bool            dac_bit25_en;
 
-       struct color_gains              *color_gains;
-       struct burst_levels             *burst_levels;
-       struct video_levels             *video_levels;
-       struct resync_parameters        *resync_params;
+       const struct color_gains        *color_gains;
+       const struct burst_levels       *burst_levels;
+       const struct video_levels       *video_levels;
+       const struct resync_parameters  *resync_params;
 };
 
 struct sun4i_tv {
@@ -178,39 +178,39 @@ struct sun4i_tv {
        struct sun4i_drv        *drv;
 };
 
-struct video_levels ntsc_video_levels = {
+static const struct video_levels ntsc_video_levels = {
        .black = 282,   .blank = 240,
 };
 
-struct video_levels pal_video_levels = {
+static const struct video_levels pal_video_levels = {
        .black = 252,   .blank = 252,
 };
 
-struct burst_levels ntsc_burst_levels = {
+static const struct burst_levels ntsc_burst_levels = {
        .cb = 79,       .cr = 0,
 };
 
-struct burst_levels pal_burst_levels = {
+static const struct burst_levels pal_burst_levels = {
        .cb = 40,       .cr = 40,
 };
 
-struct color_gains ntsc_color_gains = {
+static const struct color_gains ntsc_color_gains = {
        .cb = 160,      .cr = 160,
 };
 
-struct color_gains pal_color_gains = {
+static const struct color_gains pal_color_gains = {
        .cb = 224,      .cr = 224,
 };
 
-struct resync_parameters ntsc_resync_parameters = {
+static const struct resync_parameters ntsc_resync_parameters = {
        .field = false, .line = 14,     .pixel = 12,
 };
 
-struct resync_parameters pal_resync_parameters = {
+static const struct resync_parameters pal_resync_parameters = {
        .field = true,  .line = 13,     .pixel = 12,
 };
 
-struct tv_mode tv_modes[] = {
+static const struct tv_mode tv_modes[] = {
        {
                .name           = "NTSC",
                .mode           = SUN4I_TVE_CFG0_RES_480i,
@@ -289,13 +289,13 @@ drm_connector_to_sun4i_tv(struct drm_connector *connector)
  * So far, it doesn't seem to be preserved when the mode is passed by
  * to mode_set for some reason.
  */
-static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
+static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode)
 {
        int i;
 
        /* First try to identify the mode by name */
        for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
-               struct tv_mode *tv_mode = &tv_modes[i];
+               const struct tv_mode *tv_mode = &tv_modes[i];
 
                DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
                                 mode->name, tv_mode->name);
@@ -306,7 +306,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
 
        /* Then by number of lines */
        for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
-               struct tv_mode *tv_mode = &tv_modes[i];
+               const struct tv_mode *tv_mode = &tv_modes[i];
 
                DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
                                 mode->name, tv_mode->name,
@@ -319,7 +319,7 @@ static struct tv_mode *sun4i_tv_find_tv_by_mode(struct drm_display_mode *mode)
        return NULL;
 }
 
-static void sun4i_tv_mode_to_drm_mode(struct tv_mode *tv_mode,
+static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
                                      struct drm_display_mode *mode)
 {
        DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);
@@ -386,7 +386,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
        struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
        struct sun4i_drv *drv = tv->drv;
        struct sun4i_tcon *tcon = drv->tcon;
-       struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
+       const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
 
        sun4i_tcon1_mode_set(tcon, mode);
 
@@ -507,8 +507,14 @@ static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
        int i;
 
        for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
-               struct drm_display_mode *mode = drm_mode_create(connector->dev);
-               struct tv_mode *tv_mode = &tv_modes[i];
+               struct drm_display_mode *mode;
+               const struct tv_mode *tv_mode = &tv_modes[i];
+
+               mode = drm_mode_create(connector->dev);
+               if (!mode) {
+                       DRM_ERROR("Failed to create a new display mode\n");
+                       return 0;
+               }
 
                strcpy(mode->name, tv_mode->name);
 
diff --git a/drivers/gpu/drm/sun4i/sun6i_drc.c b/drivers/gpu/drm/sun4i/sun6i_drc.c
new file mode 100644 (file)
index 0000000..bf6d846
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 Free Electrons
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+struct sun6i_drc {
+       struct clk              *bus_clk;
+       struct clk              *mod_clk;
+       struct reset_control    *reset;
+};
+
+static int sun6i_drc_bind(struct device *dev, struct device *master,
+                        void *data)
+{
+       struct sun6i_drc *drc;
+       int ret;
+
+       drc = devm_kzalloc(dev, sizeof(*drc), GFP_KERNEL);
+       if (!drc)
+               return -ENOMEM;
+       dev_set_drvdata(dev, drc);
+
+       drc->reset = devm_reset_control_get(dev, NULL);
+       if (IS_ERR(drc->reset)) {
+               dev_err(dev, "Couldn't get our reset line\n");
+               return PTR_ERR(drc->reset);
+       }
+
+       ret = reset_control_deassert(drc->reset);
+       if (ret) {
+               dev_err(dev, "Couldn't deassert our reset line\n");
+               return ret;
+       }
+
+       drc->bus_clk = devm_clk_get(dev, "ahb");
+       if (IS_ERR(drc->bus_clk)) {
+               dev_err(dev, "Couldn't get our bus clock\n");
+               ret = PTR_ERR(drc->bus_clk);
+               goto err_assert_reset;
+       }
+       clk_prepare_enable(drc->bus_clk);
+
+       drc->mod_clk = devm_clk_get(dev, "mod");
+       if (IS_ERR(drc->mod_clk)) {
+               dev_err(dev, "Couldn't get our mod clock\n");
+               ret = PTR_ERR(drc->mod_clk);
+               goto err_disable_bus_clk;
+       }
+       clk_prepare_enable(drc->mod_clk);
+
+       return 0;
+
+err_disable_bus_clk:
+       clk_disable_unprepare(drc->bus_clk);
+err_assert_reset:
+       reset_control_assert(drc->reset);
+       return ret;
+}
+
+static void sun6i_drc_unbind(struct device *dev, struct device *master,
+                           void *data)
+{
+       struct sun6i_drc *drc = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(drc->mod_clk);
+       clk_disable_unprepare(drc->bus_clk);
+       reset_control_assert(drc->reset);
+}
+
+static struct component_ops sun6i_drc_ops = {
+       .bind   = sun6i_drc_bind,
+       .unbind = sun6i_drc_unbind,
+};
+
+static int sun6i_drc_probe(struct platform_device *pdev)
+{
+       return component_add(&pdev->dev, &sun6i_drc_ops);
+}
+
+static int sun6i_drc_remove(struct platform_device *pdev)
+{
+       component_del(&pdev->dev, &sun6i_drc_ops);
+
+       return 0;
+}
+
+static const struct of_device_id sun6i_drc_of_table[] = {
+       { .compatible = "allwinner,sun8i-a33-drc" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sun6i_drc_of_table);
+
+static struct platform_driver sun6i_drc_platform_driver = {
+       .probe          = sun6i_drc_probe,
+       .remove         = sun6i_drc_remove,
+       .driver         = {
+               .name           = "sun6i-drc",
+               .of_match_table = sun6i_drc_of_table,
+       },
+};
+module_platform_driver(sun6i_drc_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A31 Dynamic Range Control (DRC) Driver");
+MODULE_LICENSE("GPL");
index fab5ebc..f418892 100644 (file)
@@ -56,6 +56,7 @@ static const struct file_operations tdfx_driver_fops = {
 };
 
 static struct drm_driver driver = {
+       .driver_features = DRIVER_LEGACY,
        .set_busid = drm_pci_set_busid,
        .fops = &tdfx_driver_fops,
        .name = DRIVER_NAME,
index 8495bd0..4010d69 100644 (file)
@@ -480,17 +480,6 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = {
        .atomic_destroy_state = tegra_plane_atomic_destroy_state,
 };
 
-static int tegra_plane_prepare_fb(struct drm_plane *plane,
-                                 const struct drm_plane_state *new_state)
-{
-       return 0;
-}
-
-static void tegra_plane_cleanup_fb(struct drm_plane *plane,
-                                  const struct drm_plane_state *old_fb)
-{
-}
-
 static int tegra_plane_state_add(struct tegra_plane *plane,
                                 struct drm_plane_state *state)
 {
@@ -591,7 +580,14 @@ static void tegra_plane_atomic_update(struct drm_plane *plane,
                struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
 
                window.base[i] = bo->paddr + fb->offsets[i];
-               window.stride[i] = fb->pitches[i];
+
+               /*
+                * Tegra uses a shared stride for UV planes. Framebuffers are
+                * already checked for this in the tegra_plane_atomic_check()
+                * function, so it's safe to ignore the V-plane pitch here.
+                */
+               if (i < 2)
+                       window.stride[i] = fb->pitches[i];
        }
 
        tegra_dc_setup_window(dc, p->index, &window);
@@ -624,8 +620,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane,
 }
 
 static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = {
-       .prepare_fb = tegra_plane_prepare_fb,
-       .cleanup_fb = tegra_plane_cleanup_fb,
        .atomic_check = tegra_plane_atomic_check,
        .atomic_update = tegra_plane_atomic_update,
        .atomic_disable = tegra_plane_atomic_disable,
@@ -796,8 +790,6 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = {
 };
 
 static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
-       .prepare_fb = tegra_plane_prepare_fb,
-       .cleanup_fb = tegra_plane_cleanup_fb,
        .atomic_check = tegra_cursor_atomic_check,
        .atomic_update = tegra_cursor_atomic_update,
        .atomic_disable = tegra_cursor_atomic_disable,
@@ -866,8 +858,6 @@ static const uint32_t tegra_overlay_plane_formats[] = {
 };
 
 static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = {
-       .prepare_fb = tegra_plane_prepare_fb,
-       .cleanup_fb = tegra_plane_cleanup_fb,
        .atomic_check = tegra_plane_atomic_check,
        .atomic_update = tegra_plane_atomic_update,
        .atomic_disable = tegra_plane_atomic_disable,
index 755264d..8ab47b5 100644 (file)
@@ -57,7 +57,8 @@ static void tegra_atomic_complete(struct tegra_drm *tegra,
 
        drm_atomic_helper_commit_modeset_disables(drm, state);
        drm_atomic_helper_commit_modeset_enables(drm, state);
-       drm_atomic_helper_commit_planes(drm, state, true);
+       drm_atomic_helper_commit_planes(drm, state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
 
        drm_atomic_helper_wait_for_vblanks(drm, state);
 
@@ -982,8 +983,8 @@ static int host1x_drm_probe(struct host1x_device *dev)
        int err;
 
        drm = drm_dev_alloc(driver, &dev->dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
 
        dev_set_drvdata(&dev->dev, drm);
 
index aa60d99..95e622e 100644 (file)
@@ -613,7 +613,7 @@ struct dma_buf *tegra_gem_prime_export(struct drm_device *drm,
        exp_info.flags = flags;
        exp_info.priv = gem;
 
-       return dma_buf_export(&exp_info);
+       return drm_gem_dmabuf_export(drm, &exp_info);
 }
 
 struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
index deeca48..6f67517 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \
                                         tilcdc_slave_compat.dtb.o
 
 tilcdc-y := \
+       tilcdc_plane.o \
        tilcdc_crtc.o \
        tilcdc_tfp410.o \
        tilcdc_panel.o \
index 107c8bd..52ebe8f 100644 (file)
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "drm_flip_work.h"
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_flip_work.h>
 #include <drm/drm_plane_helper.h>
+#include <linux/workqueue.h>
 
 #include "tilcdc_drv.h"
 #include "tilcdc_regs.h"
 struct tilcdc_crtc {
        struct drm_crtc base;
 
+       struct drm_plane primary;
        const struct tilcdc_panel_info *info;
        struct drm_pending_vblank_event *event;
-       int dpms;
+       bool enabled;
        wait_queue_head_t frame_done_wq;
        bool frame_done;
        spinlock_t irq_lock;
 
+       unsigned int lcd_fck_rate;
+
        ktime_t last_vblank;
 
        struct drm_framebuffer *curr_fb;
@@ -67,6 +74,7 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
        struct drm_gem_cma_object *gem;
        unsigned int depth, bpp;
        dma_addr_t start, end;
+       u64 dma_base_and_ceiling;
 
        drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
        gem = drm_fb_cma_get_gem_obj(fb, 0);
@@ -77,8 +85,13 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
 
        end = start + (crtc->mode.vdisplay * fb->pitches[0]);
 
-       tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, start);
-       tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, end);
+       /* Write LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG
+        * with a single insruction, if available. This should make it more
+        * unlikely that LCDC would fetch the DMA addresses in the middle of
+        * an update.
+        */
+       dma_base_and_ceiling = (u64)(end - 1) << 32 | start;
+       tilcdc_write64(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, dma_base_and_ceiling);
 
        if (tilcdc_crtc->curr_fb)
                drm_flip_work_queue(&tilcdc_crtc->unref_work,
@@ -87,6 +100,43 @@ static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
        tilcdc_crtc->curr_fb = fb;
 }
 
+static void tilcdc_crtc_enable_irqs(struct drm_device *dev)
+{
+       struct tilcdc_drm_private *priv = dev->dev_private;
+
+       tilcdc_clear_irqstatus(dev, 0xffffffff);
+
+       if (priv->rev == 1) {
+               tilcdc_set(dev, LCDC_RASTER_CTRL_REG,
+                       LCDC_V1_UNDERFLOW_INT_ENA);
+               tilcdc_set(dev, LCDC_DMA_CTRL_REG,
+                       LCDC_V1_END_OF_FRAME_INT_ENA);
+       } else {
+               tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG,
+                       LCDC_V2_UNDERFLOW_INT_ENA |
+                       LCDC_V2_END_OF_FRAME0_INT_ENA |
+                       LCDC_FRAME_DONE | LCDC_SYNC_LOST);
+       }
+}
+
+static void tilcdc_crtc_disable_irqs(struct drm_device *dev)
+{
+       struct tilcdc_drm_private *priv = dev->dev_private;
+
+       /* disable irqs that we might have enabled: */
+       if (priv->rev == 1) {
+               tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
+                       LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);
+               tilcdc_clear(dev, LCDC_DMA_CTRL_REG,
+                       LCDC_V1_END_OF_FRAME_INT_ENA);
+       } else {
+               tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
+                       LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
+                       LCDC_V2_END_OF_FRAME0_INT_ENA |
+                       LCDC_FRAME_DONE | LCDC_SYNC_LOST);
+       }
+}
+
 static void reset(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -100,66 +150,112 @@ static void reset(struct drm_crtc *crtc)
        tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET);
 }
 
-static void start(struct drm_crtc *crtc)
+static void tilcdc_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
+       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
+
+       WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
+
+       if (tilcdc_crtc->enabled)
+               return;
+
+       pm_runtime_get_sync(dev->dev);
 
        reset(crtc);
 
+       tilcdc_crtc_enable_irqs(dev);
+
        tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE);
        tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY));
        tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
+
+       drm_crtc_vblank_on(crtc);
+
+       tilcdc_crtc->enabled = true;
 }
 
-static void stop(struct drm_crtc *crtc)
+void tilcdc_crtc_disable(struct drm_crtc *crtc)
 {
+       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
        struct drm_device *dev = crtc->dev;
+       struct tilcdc_drm_private *priv = dev->dev_private;
+
+       WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
+
+       if (!tilcdc_crtc->enabled)
+               return;
 
+       tilcdc_crtc->frame_done = false;
        tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE);
+
+       /*
+        * if necessary wait for framedone irq which will still come
+        * before putting things to sleep..
+        */
+       if (priv->rev == 2) {
+               int ret = wait_event_timeout(tilcdc_crtc->frame_done_wq,
+                                            tilcdc_crtc->frame_done,
+                                            msecs_to_jiffies(500));
+               if (ret == 0)
+                       dev_err(dev->dev, "%s: timeout waiting for framedone\n",
+                               __func__);
+       }
+
+       drm_crtc_vblank_off(crtc);
+
+       tilcdc_crtc_disable_irqs(dev);
+
+       pm_runtime_put_sync(dev->dev);
+
+       if (tilcdc_crtc->next_fb) {
+               drm_flip_work_queue(&tilcdc_crtc->unref_work,
+                                   tilcdc_crtc->next_fb);
+               tilcdc_crtc->next_fb = NULL;
+       }
+
+       if (tilcdc_crtc->curr_fb) {
+               drm_flip_work_queue(&tilcdc_crtc->unref_work,
+                                   tilcdc_crtc->curr_fb);
+               tilcdc_crtc->curr_fb = NULL;
+       }
+
+       drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
+       tilcdc_crtc->last_vblank = ktime_set(0, 0);
+
+       tilcdc_crtc->enabled = false;
+}
+
+static bool tilcdc_crtc_is_on(struct drm_crtc *crtc)
+{
+       return crtc->state && crtc->state->enable && crtc->state->active;
 }
 
 static void tilcdc_crtc_destroy(struct drm_crtc *crtc)
 {
        struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
+       struct tilcdc_drm_private *priv = crtc->dev->dev_private;
+
+       drm_modeset_lock_crtc(crtc, NULL);
+       tilcdc_crtc_disable(crtc);
+       drm_modeset_unlock_crtc(crtc);
 
-       tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       flush_workqueue(priv->wq);
 
        of_node_put(crtc->port);
        drm_crtc_cleanup(crtc);
        drm_flip_work_cleanup(&tilcdc_crtc->unref_work);
 }
 
-static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb)
-{
-       struct drm_device *dev = crtc->dev;
-       unsigned int depth, bpp;
-
-       drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
-
-       if (fb->pitches[0] != crtc->mode.hdisplay * bpp / 8) {
-               dev_err(dev->dev,
-                       "Invalid pitch: fb and crtc widths must be the same");
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
+int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
                struct drm_framebuffer *fb,
-               struct drm_pending_vblank_event *event,
-               uint32_t page_flip_flags)
+               struct drm_pending_vblank_event *event)
 {
        struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
        struct drm_device *dev = crtc->dev;
-       int r;
        unsigned long flags;
-       s64 tdiff;
-       ktime_t next_vblank;
 
-       r = tilcdc_verify_fb(crtc, fb);
-       if (r)
-               return r;
+       WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
        if (tilcdc_crtc->event) {
                dev_err(dev->dev, "already pending page flip!\n");
@@ -170,82 +266,31 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc,
 
        crtc->primary->fb = fb;
 
-       pm_runtime_get_sync(dev->dev);
-
        spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags);
 
-       next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
-               1000000 / crtc->hwmode.vrefresh);
+       if (crtc->hwmode.vrefresh && ktime_to_ns(tilcdc_crtc->last_vblank)) {
+               ktime_t next_vblank;
+               s64 tdiff;
+
+               next_vblank = ktime_add_us(tilcdc_crtc->last_vblank,
+                       1000000 / crtc->hwmode.vrefresh);
 
-       tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));
+               tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get()));
 
-       if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US)
+               if (tdiff < TILCDC_VBLANK_SAFETY_THRESHOLD_US)
+                       tilcdc_crtc->next_fb = fb;
+       }
+
+       if (tilcdc_crtc->next_fb != fb)
                set_scanout(crtc, fb);
-       else
-               tilcdc_crtc->next_fb = fb;
 
        tilcdc_crtc->event = event;
 
        spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags);
 
-       pm_runtime_put_sync(dev->dev);
-
        return 0;
 }
 
-void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
-       struct drm_device *dev = crtc->dev;
-       struct tilcdc_drm_private *priv = dev->dev_private;
-
-       /* we really only care about on or off: */
-       if (mode != DRM_MODE_DPMS_ON)
-               mode = DRM_MODE_DPMS_OFF;
-
-       if (tilcdc_crtc->dpms == mode)
-               return;
-
-       tilcdc_crtc->dpms = mode;
-
-       if (mode == DRM_MODE_DPMS_ON) {
-               pm_runtime_get_sync(dev->dev);
-               start(crtc);
-       } else {
-               tilcdc_crtc->frame_done = false;
-               stop(crtc);
-
-               /*
-                * if necessary wait for framedone irq which will still come
-                * before putting things to sleep..
-                */
-               if (priv->rev == 2) {
-                       int ret = wait_event_timeout(
-                                       tilcdc_crtc->frame_done_wq,
-                                       tilcdc_crtc->frame_done,
-                                       msecs_to_jiffies(50));
-                       if (ret == 0)
-                               dev_err(dev->dev, "timeout waiting for framedone\n");
-               }
-
-               pm_runtime_put_sync(dev->dev);
-
-               if (tilcdc_crtc->next_fb) {
-                       drm_flip_work_queue(&tilcdc_crtc->unref_work,
-                                           tilcdc_crtc->next_fb);
-                       tilcdc_crtc->next_fb = NULL;
-               }
-
-               if (tilcdc_crtc->curr_fb) {
-                       drm_flip_work_queue(&tilcdc_crtc->unref_work,
-                                           tilcdc_crtc->curr_fb);
-                       tilcdc_crtc->curr_fb = NULL;
-               }
-
-               drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq);
-       }
-}
-
 static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc,
                const struct drm_display_mode *mode,
                struct drm_display_mode *adjusted_mode)
@@ -275,41 +320,54 @@ static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc,
        return true;
 }
 
-static void tilcdc_crtc_prepare(struct drm_crtc *crtc)
+static void tilcdc_crtc_set_clk(struct drm_crtc *crtc)
 {
-       tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-}
+       struct drm_device *dev = crtc->dev;
+       struct tilcdc_drm_private *priv = dev->dev_private;
+       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
+       const unsigned clkdiv = 2; /* using a fixed divider of 2 */
+       int ret;
 
-static void tilcdc_crtc_commit(struct drm_crtc *crtc)
-{
-       tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+       /* mode.clock is in KHz, set_rate wants parameter in Hz */
+       ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv);
+       if (ret < 0) {
+               dev_err(dev->dev, "failed to set display clock rate to: %d\n",
+                       crtc->mode.clock);
+               return;
+       }
+
+       tilcdc_crtc->lcd_fck_rate = clk_get_rate(priv->clk);
+
+       DBG("lcd_clk=%u, mode clock=%d, div=%u",
+           tilcdc_crtc->lcd_fck_rate, crtc->mode.clock, clkdiv);
+
+       /* Configure the LCD clock divisor. */
+       tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) |
+                    LCDC_RASTER_MODE);
+
+       if (priv->rev == 2)
+               tilcdc_set(dev, LCDC_CLK_ENABLE_REG,
+                               LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN |
+                               LCDC_V2_CORE_CLK_EN);
 }
 
-static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
-               struct drm_display_mode *mode,
-               struct drm_display_mode *adjusted_mode,
-               int x, int y,
-               struct drm_framebuffer *old_fb)
+static void tilcdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
        struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
        struct drm_device *dev = crtc->dev;
        struct tilcdc_drm_private *priv = dev->dev_private;
        const struct tilcdc_panel_info *info = tilcdc_crtc->info;
        uint32_t reg, hbp, hfp, hsw, vbp, vfp, vsw;
-       int ret;
+       struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       struct drm_framebuffer *fb = crtc->primary->state->fb;
 
-       ret = tilcdc_crtc_mode_valid(crtc, mode);
-       if (WARN_ON(ret))
-               return ret;
+       WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
 
        if (WARN_ON(!info))
-               return -EINVAL;
-
-       ret = tilcdc_verify_fb(crtc, crtc->primary->fb);
-       if (ret)
-               return ret;
+               return;
 
-       pm_runtime_get_sync(dev->dev);
+       if (WARN_ON(!fb))
+               return;
 
        /* Configure the Burst Size and fifo threshold of DMA: */
        reg = tilcdc_read(dev, LCDC_DMA_CTRL_REG) & ~0x00000770;
@@ -330,7 +388,8 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
                reg |= LCDC_DMA_BURST_SIZE(LCDC_DMA_BURST_16);
                break;
        default:
-               return -EINVAL;
+               dev_err(dev->dev, "invalid burst size\n");
+               return;
        }
        reg |= (info->fifo_th << 8);
        tilcdc_write(dev, LCDC_DMA_CTRL_REG, reg);
@@ -344,9 +403,9 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        vsw = mode->vsync_end - mode->vsync_start;
 
        DBG("%dx%d, hbp=%u, hfp=%u, hsw=%u, vbp=%u, vfp=%u, vsw=%u",
-                       mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw);
+           mode->hdisplay, mode->vdisplay, hbp, hfp, hsw, vbp, vfp, vsw);
 
-       /* Configure the AC Bias Period and Number of Transitions per Interrupt: */
+       /* Set AC Bias Period and Number of Transitions per Interrupt: */
        reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00;
        reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) |
                LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt);
@@ -381,7 +440,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        /*
         * be sure to set Bit 10 for the V2 LCDC controller,
         * otherwise limited to 1024 pixels width, stopping
-        * 1920x1080 being suppoted.
+        * 1920x1080 being supported.
         */
        if (priv->rev == 2) {
                if ((mode->vdisplay - 1) & 0x400) {
@@ -396,14 +455,15 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        /* Configure display type: */
        reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) &
                ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE |
-                       LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK | 0x000ff000);
+                 LCDC_V2_TFT_24BPP_MODE | LCDC_V2_TFT_24BPP_UNPACK |
+                 0x000ff000 /* Palette Loading Delay bits */);
        reg |= LCDC_TFT_MODE; /* no monochrome/passive support */
        if (info->tft_alt_mode)
                reg |= LCDC_TFT_ALT_ENABLE;
        if (priv->rev == 2) {
                unsigned int depth, bpp;
 
-               drm_fb_get_bpp_depth(crtc->primary->fb->pixel_format, &depth, &bpp);
+               drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
                switch (bpp) {
                case 16:
                        break;
@@ -415,7 +475,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
                        break;
                default:
                        dev_err(dev->dev, "invalid pixel format\n");
-                       return -EINVAL;
+                       return;
                }
        }
        reg |= info->fdd < 12;
@@ -436,12 +496,7 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        else
                tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_SYNC_EDGE);
 
-       /*
-        * use value from adjusted_mode here as this might have been
-        * changed as part of the fixup for slave encoders to solve the
-        * issue where tilcdc timings are not VESA compliant
-        */
-       if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
                tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC);
        else
                tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG, LCDC_INVERT_HSYNC);
@@ -456,51 +511,56 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        else
                tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER);
 
-       drm_framebuffer_reference(crtc->primary->fb);
-
-       set_scanout(crtc, crtc->primary->fb);
+       drm_framebuffer_reference(fb);
 
-       tilcdc_crtc_update_clk(crtc);
+       set_scanout(crtc, fb);
 
-       pm_runtime_put_sync(dev->dev);
+       tilcdc_crtc_set_clk(crtc);
 
-       return 0;
+       crtc->hwmode = crtc->state->adjusted_mode;
 }
 
-static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-               struct drm_framebuffer *old_fb)
+static int tilcdc_crtc_atomic_check(struct drm_crtc *crtc,
+                                   struct drm_crtc_state *state)
 {
-       struct drm_device *dev = crtc->dev;
-       int r;
-
-       r = tilcdc_verify_fb(crtc, crtc->primary->fb);
-       if (r)
-               return r;
-
-       drm_framebuffer_reference(crtc->primary->fb);
+       struct drm_display_mode *mode = &state->mode;
+       int ret;
 
-       pm_runtime_get_sync(dev->dev);
+       /* If we are not active we don't care */
+       if (!state->active)
+               return 0;
 
-       set_scanout(crtc, crtc->primary->fb);
+       if (state->state->planes[0].ptr != crtc->primary ||
+           state->state->planes[0].state == NULL ||
+           state->state->planes[0].state->crtc != crtc) {
+               dev_dbg(crtc->dev->dev, "CRTC primary plane must be present");
+               return -EINVAL;
+       }
 
-       pm_runtime_put_sync(dev->dev);
+       ret = tilcdc_crtc_mode_valid(crtc, mode);
+       if (ret) {
+               dev_dbg(crtc->dev->dev, "Mode \"%s\" not valid", mode->name);
+               return -EINVAL;
+       }
 
        return 0;
 }
 
 static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
-               .destroy        = tilcdc_crtc_destroy,
-               .set_config     = drm_crtc_helper_set_config,
-               .page_flip      = tilcdc_crtc_page_flip,
+       .destroy        = tilcdc_crtc_destroy,
+       .set_config     = drm_atomic_helper_set_config,
+       .page_flip      = drm_atomic_helper_page_flip,
+       .reset          = drm_atomic_helper_crtc_reset,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
 static const struct drm_crtc_helper_funcs tilcdc_crtc_helper_funcs = {
-               .dpms           = tilcdc_crtc_dpms,
                .mode_fixup     = tilcdc_crtc_mode_fixup,
-               .prepare        = tilcdc_crtc_prepare,
-               .commit         = tilcdc_crtc_commit,
-               .mode_set       = tilcdc_crtc_mode_set,
-               .mode_set_base  = tilcdc_crtc_mode_set_base,
+               .enable         = tilcdc_crtc_enable,
+               .disable        = tilcdc_crtc_disable,
+               .atomic_check   = tilcdc_crtc_atomic_check,
+               .mode_set_nofb  = tilcdc_crtc_mode_set_nofb,
 };
 
 int tilcdc_crtc_max_width(struct drm_crtc *crtc)
@@ -622,46 +682,23 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,
 
 void tilcdc_crtc_update_clk(struct drm_crtc *crtc)
 {
-       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
        struct drm_device *dev = crtc->dev;
        struct tilcdc_drm_private *priv = dev->dev_private;
-       int dpms = tilcdc_crtc->dpms;
-       unsigned long lcd_clk;
-       const unsigned clkdiv = 2; /* using a fixed divider of 2 */
-       int ret;
+       struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc);
 
-       pm_runtime_get_sync(dev->dev);
+       drm_modeset_lock_crtc(crtc, NULL);
+       if (tilcdc_crtc->lcd_fck_rate != clk_get_rate(priv->clk)) {
+               if (tilcdc_crtc_is_on(crtc)) {
+                       pm_runtime_get_sync(dev->dev);
+                       tilcdc_crtc_disable(crtc);
 
-       if (dpms == DRM_MODE_DPMS_ON)
-               tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+                       tilcdc_crtc_set_clk(crtc);
 
-       /* mode.clock is in KHz, set_rate wants parameter in Hz */
-       ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv);
-       if (ret < 0) {
-               dev_err(dev->dev, "failed to set display clock rate to: %d\n",
-                               crtc->mode.clock);
-               goto out;
+                       tilcdc_crtc_enable(crtc);
+                       pm_runtime_put_sync(dev->dev);
+               }
        }
-
-       lcd_clk = clk_get_rate(priv->clk);
-
-       DBG("lcd_clk=%lu, mode clock=%d, div=%u",
-               lcd_clk, crtc->mode.clock, clkdiv);
-
-       /* Configure the LCD clock divisor. */
-       tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) |
-                       LCDC_RASTER_MODE);
-
-       if (priv->rev == 2)
-               tilcdc_set(dev, LCDC_CLK_ENABLE_REG,
-                               LCDC_V2_DMA_CLK_EN | LCDC_V2_LIDD_CLK_EN |
-                               LCDC_V2_CORE_CLK_EN);
-
-       if (dpms == DRM_MODE_DPMS_ON)
-               tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
-
-out:
-       pm_runtime_put_sync(dev->dev);
+       drm_modeset_unlock_crtc(crtc);
 }
 
 #define SYNC_LOST_COUNT_LIMIT 50
@@ -718,30 +755,34 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc)
                        tilcdc_crtc->frame_intact = true;
        }
 
+       if (stat & LCDC_FIFO_UNDERFLOW)
+               dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow",
+                                   __func__, stat);
+
+       /* For revision 2 only */
        if (priv->rev == 2) {
                if (stat & LCDC_FRAME_DONE) {
                        tilcdc_crtc->frame_done = true;
                        wake_up(&tilcdc_crtc->frame_done_wq);
                }
-               tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0);
-       }
 
-       if (stat & LCDC_SYNC_LOST) {
-               dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost",
-                                   __func__, stat);
-               tilcdc_crtc->frame_intact = false;
-               if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) {
-                       dev_err(dev->dev,
-                               "%s(0x%08x): Sync lost flood detected, disabling the interrupt",
-                               __func__, stat);
-                       tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
-                                    LCDC_SYNC_LOST);
+               if (stat & LCDC_SYNC_LOST) {
+                       dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost",
+                                           __func__, stat);
+                       tilcdc_crtc->frame_intact = false;
+                       if (tilcdc_crtc->sync_lost_count++ >
+                           SYNC_LOST_COUNT_LIMIT) {
+                               dev_err(dev->dev, "%s(0x%08x): Sync lost flood detected, disabling the interrupt", __func__, stat);
+                               tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
+                                            LCDC_SYNC_LOST);
+                       }
                }
-       }
 
-       if (stat & LCDC_FIFO_UNDERFLOW)
-               dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow",
-                                   __func__, stat);
+               /* Indicate to LCDC that the interrupt service routine has
+                * completed, see 13.3.6.1.6 in AM335x TRM.
+                */
+               tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0);
+       }
 
        return IRQ_HANDLED;
 }
@@ -761,7 +802,10 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
 
        crtc = &tilcdc_crtc->base;
 
-       tilcdc_crtc->dpms = DRM_MODE_DPMS_OFF;
+       ret = tilcdc_plane_init(dev, &tilcdc_crtc->primary);
+       if (ret < 0)
+               goto fail;
+
        init_waitqueue_head(&tilcdc_crtc->frame_done_wq);
 
        drm_flip_work_init(&tilcdc_crtc->unref_work,
@@ -769,7 +813,11 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev)
 
        spin_lock_init(&tilcdc_crtc->irq_lock);
 
-       ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs);
+       ret = drm_crtc_init_with_planes(dev, crtc,
+                                       &tilcdc_crtc->primary,
+                                       NULL,
+                                       &tilcdc_crtc_funcs,
+                                       "tilcdc crtc");
        if (ret < 0)
                goto fail;
 
index d278093..a694977 100644 (file)
@@ -20,6 +20,8 @@
 #include <linux/component.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/suspend.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 
 #include "tilcdc_drv.h"
 #include "tilcdc_regs.h"
 
 static LIST_HEAD(module_list);
 
+static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 };
+
+static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565,
+                                              DRM_FORMAT_BGR888,
+                                              DRM_FORMAT_XBGR8888 };
+
+static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565,
+                                             DRM_FORMAT_RGB888,
+                                             DRM_FORMAT_XRGB8888 };
+
+static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565,
+                                            DRM_FORMAT_RGB888,
+                                            DRM_FORMAT_XRGB8888 };
+
 void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
                const struct tilcdc_module_ops *funcs)
 {
@@ -59,9 +75,84 @@ static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
        drm_fbdev_cma_hotplug_event(priv->fbdev);
 }
 
+static int tilcdc_atomic_check(struct drm_device *dev,
+                              struct drm_atomic_state *state)
+{
+       int ret;
+
+       ret = drm_atomic_helper_check_modeset(dev, state);
+       if (ret)
+               return ret;
+
+       ret = drm_atomic_helper_check_planes(dev, state);
+       if (ret)
+               return ret;
+
+       /*
+        * tilcdc ->atomic_check can update ->mode_changed if pixel format
+        * changes, hence will we check modeset changes again.
+        */
+       ret = drm_atomic_helper_check_modeset(dev, state);
+       if (ret)
+               return ret;
+
+       return ret;
+}
+
+static int tilcdc_commit(struct drm_device *dev,
+                 struct drm_atomic_state *state,
+                 bool async)
+{
+       int ret;
+
+       ret = drm_atomic_helper_prepare_planes(dev, state);
+       if (ret)
+               return ret;
+
+       drm_atomic_helper_swap_state(state, true);
+
+       /*
+        * Everything below can be run asynchronously without the need to grab
+        * any modeset locks at all under one condition: It must be guaranteed
+        * that the asynchronous work has either been cancelled (if the driver
+        * supports it, which at least requires that the framebuffers get
+        * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
+        * before the new state gets committed on the software side with
+        * drm_atomic_helper_swap_state().
+        *
+        * This scheme allows new atomic state updates to be prepared and
+        * checked in parallel to the asynchronous completion of the previous
+        * update. Which is important since compositors need to figure out the
+        * composition of the next frame right after having submitted the
+        * current layout.
+        */
+
+       /* Keep HW on while we commit the state. */
+       pm_runtime_get_sync(dev->dev);
+
+       drm_atomic_helper_commit_modeset_disables(dev, state);
+
+       drm_atomic_helper_commit_planes(dev, state, 0);
+
+       drm_atomic_helper_commit_modeset_enables(dev, state);
+
+       /* Now HW should remain on if need becase the crtc is enabled */
+       pm_runtime_put_sync(dev->dev);
+
+       drm_atomic_helper_wait_for_vblanks(dev, state);
+
+       drm_atomic_helper_cleanup_planes(dev, state);
+
+       drm_atomic_state_free(state);
+
+       return 0;
+}
+
 static const struct drm_mode_config_funcs mode_config_funcs = {
        .fb_create = tilcdc_fb_create,
        .output_poll_changed = tilcdc_fb_output_poll_changed,
+       .atomic_check = tilcdc_atomic_check,
+       .atomic_commit = tilcdc_commit,
 };
 
 static int modeset_init(struct drm_device *dev)
@@ -93,12 +184,9 @@ static int cpufreq_transition(struct notifier_block *nb,
 {
        struct tilcdc_drm_private *priv = container_of(nb,
                        struct tilcdc_drm_private, freq_transition);
-       if (val == CPUFREQ_POSTCHANGE) {
-               if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) {
-                       priv->lcd_fck_rate = clk_get_rate(priv->clk);
-                       tilcdc_crtc_update_clk(priv->crtc);
-               }
-       }
+
+       if (val == CPUFREQ_POSTCHANGE)
+               tilcdc_crtc_update_clk(priv->crtc);
 
        return 0;
 }
@@ -112,8 +200,6 @@ static int tilcdc_unload(struct drm_device *dev)
 {
        struct tilcdc_drm_private *priv = dev->dev_private;
 
-       tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF);
-
        tilcdc_remove_external_encoders(dev);
 
        drm_fbdev_cma_fini(priv->fbdev);
@@ -121,9 +207,7 @@ static int tilcdc_unload(struct drm_device *dev)
        drm_mode_config_cleanup(dev);
        drm_vblank_cleanup(dev);
 
-       pm_runtime_get_sync(dev->dev);
        drm_irq_uninstall(dev);
-       pm_runtime_put_sync(dev->dev);
 
 #ifdef CONFIG_CPU_FREQ
        cpufreq_unregister_notifier(&priv->freq_transition,
@@ -146,24 +230,17 @@ static int tilcdc_unload(struct drm_device *dev)
        return 0;
 }
 
-static size_t tilcdc_num_regs(void);
-
 static int tilcdc_load(struct drm_device *dev, unsigned long flags)
 {
        struct platform_device *pdev = dev->platformdev;
        struct device_node *node = pdev->dev.of_node;
        struct tilcdc_drm_private *priv;
-       struct tilcdc_module *mod;
        struct resource *res;
        u32 bpp = 0;
        int ret;
 
        priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
-       if (priv)
-               priv->saved_register =
-                       devm_kcalloc(dev->dev, tilcdc_num_regs(),
-                                    sizeof(*priv->saved_register), GFP_KERNEL);
-       if (!priv || !priv->saved_register) {
+       if (!priv) {
                dev_err(dev->dev, "failed to allocate private data\n");
                return -ENOMEM;
        }
@@ -201,7 +278,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        }
 
 #ifdef CONFIG_CPU_FREQ
-       priv->lcd_fck_rate = clk_get_rate(priv->clk);
        priv->freq_transition.notifier_call = cpufreq_transition;
        ret = cpufreq_register_notifier(&priv->freq_transition,
                        CPUFREQ_TRANSITION_NOTIFIER);
@@ -249,6 +325,37 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
 
        pm_runtime_put_sync(dev->dev);
 
+       if (priv->rev == 1) {
+               DBG("Revision 1 LCDC supports only RGB565 format");
+               priv->pixelformats = tilcdc_rev1_formats;
+               priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats);
+               bpp = 16;
+       } else {
+               const char *str = "\0";
+
+               of_property_read_string(node, "blue-and-red-wiring", &str);
+               if (0 == strcmp(str, "crossed")) {
+                       DBG("Configured for crossed blue and red wires");
+                       priv->pixelformats = tilcdc_crossed_formats;
+                       priv->num_pixelformats =
+                               ARRAY_SIZE(tilcdc_crossed_formats);
+                       bpp = 32; /* Choose bpp with RGB support for fbdef */
+               } else if (0 == strcmp(str, "straight")) {
+                       DBG("Configured for straight blue and red wires");
+                       priv->pixelformats = tilcdc_straight_formats;
+                       priv->num_pixelformats =
+                               ARRAY_SIZE(tilcdc_straight_formats);
+                       bpp = 16; /* Choose bpp with RGB support for fbdef */
+               } else {
+                       DBG("Blue and red wiring '%s' unknown, use legacy mode",
+                           str);
+                       priv->pixelformats = tilcdc_legacy_formats;
+                       priv->num_pixelformats =
+                               ARRAY_SIZE(tilcdc_legacy_formats);
+                       bpp = 16; /* This is just a guess */
+               }
+       }
+
        ret = modeset_init(dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to initialize mode setting\n");
@@ -262,7 +369,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
                if (ret < 0)
                        goto fail_mode_config_cleanup;
 
-               ret = tilcdc_add_external_encoders(dev, &bpp);
+               ret = tilcdc_add_external_encoders(dev);
                if (ret < 0)
                        goto fail_component_cleanup;
        }
@@ -279,22 +386,14 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
                goto fail_external_cleanup;
        }
 
-       pm_runtime_get_sync(dev->dev);
        ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
-       pm_runtime_put_sync(dev->dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to install IRQ handler\n");
                goto fail_vblank_cleanup;
        }
 
-       list_for_each_entry(mod, &module_list, list) {
-               DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp);
-               bpp = mod->preferred_bpp;
-               if (bpp > 0)
-                       break;
-       }
+       drm_mode_config_reset(dev);
 
-       drm_helper_disable_unused_functions(dev);
        priv->fbdev = drm_fbdev_cma_init(dev, bpp,
                        dev->mode_config.num_crtc,
                        dev->mode_config.num_connector);
@@ -308,20 +407,18 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        return 0;
 
 fail_irq_uninstall:
-       pm_runtime_get_sync(dev->dev);
        drm_irq_uninstall(dev);
-       pm_runtime_put_sync(dev->dev);
 
 fail_vblank_cleanup:
        drm_vblank_cleanup(dev);
 
-fail_mode_config_cleanup:
-       drm_mode_config_cleanup(dev);
-
 fail_component_cleanup:
        if (priv->is_componentized)
                component_unbind_all(dev->dev, dev);
 
+fail_mode_config_cleanup:
+       drm_mode_config_cleanup(dev);
+
 fail_external_cleanup:
        tilcdc_remove_external_encoders(dev);
 
@@ -361,45 +458,6 @@ static irqreturn_t tilcdc_irq(int irq, void *arg)
        return tilcdc_crtc_irq(priv->crtc);
 }
 
-static void tilcdc_irq_preinstall(struct drm_device *dev)
-{
-       tilcdc_clear_irqstatus(dev, 0xffffffff);
-}
-
-static int tilcdc_irq_postinstall(struct drm_device *dev)
-{
-       struct tilcdc_drm_private *priv = dev->dev_private;
-
-       /* enable FIFO underflow irq: */
-       if (priv->rev == 1) {
-               tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA);
-       } else {
-               tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG,
-                          LCDC_V2_UNDERFLOW_INT_ENA |
-                          LCDC_V2_END_OF_FRAME0_INT_ENA |
-                          LCDC_FRAME_DONE | LCDC_SYNC_LOST);
-       }
-
-       return 0;
-}
-
-static void tilcdc_irq_uninstall(struct drm_device *dev)
-{
-       struct tilcdc_drm_private *priv = dev->dev_private;
-
-       /* disable irqs that we might have enabled: */
-       if (priv->rev == 1) {
-               tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
-                               LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);
-               tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA);
-       } else {
-               tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG,
-                       LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
-                       LCDC_V2_END_OF_FRAME0_INT_ENA |
-                       LCDC_FRAME_DONE | LCDC_SYNC_LOST);
-       }
-}
-
 static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        return 0;
@@ -410,7 +468,7 @@ static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe)
        return;
 }
 
-#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP)
+#if defined(CONFIG_DEBUG_FS)
 static const struct {
        const char *name;
        uint8_t  rev;
@@ -441,15 +499,6 @@ static const struct {
 #undef REG
 };
 
-static size_t tilcdc_num_regs(void)
-{
-       return ARRAY_SIZE(registers);
-}
-#else
-static size_t tilcdc_num_regs(void)
-{
-       return 0;
-}
 #endif
 
 #ifdef CONFIG_DEBUG_FS
@@ -537,14 +586,11 @@ static const struct file_operations fops = {
 
 static struct drm_driver tilcdc_driver = {
        .driver_features    = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET |
-                              DRIVER_PRIME),
+                              DRIVER_PRIME | DRIVER_ATOMIC),
        .load               = tilcdc_load,
        .unload             = tilcdc_unload,
        .lastclose          = tilcdc_lastclose,
        .irq_handler        = tilcdc_irq,
-       .irq_preinstall     = tilcdc_irq_preinstall,
-       .irq_postinstall    = tilcdc_irq_postinstall,
-       .irq_uninstall      = tilcdc_irq_uninstall,
        .get_vblank_counter = drm_vblank_no_hw_counter,
        .enable_vblank      = tilcdc_enable_vblank,
        .disable_vblank     = tilcdc_disable_vblank,
@@ -584,28 +630,12 @@ static int tilcdc_pm_suspend(struct device *dev)
 {
        struct drm_device *ddev = dev_get_drvdata(dev);
        struct tilcdc_drm_private *priv = ddev->dev_private;
-       unsigned i, n = 0;
 
-       drm_kms_helper_poll_disable(ddev);
+       priv->saved_state = drm_atomic_helper_suspend(ddev);
 
        /* Select sleep pin state */
        pinctrl_pm_select_sleep_state(dev);
 
-       if (pm_runtime_suspended(dev)) {
-               priv->ctx_valid = false;
-               return 0;
-       }
-
-       /* Disable the LCDC controller, to avoid locking up the PRCM */
-       tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF);
-
-       /* Save register state: */
-       for (i = 0; i < ARRAY_SIZE(registers); i++)
-               if (registers[i].save && (priv->rev >= registers[i].rev))
-                       priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg);
-
-       priv->ctx_valid = true;
-
        return 0;
 }
 
@@ -613,23 +643,15 @@ static int tilcdc_pm_resume(struct device *dev)
 {
        struct drm_device *ddev = dev_get_drvdata(dev);
        struct tilcdc_drm_private *priv = ddev->dev_private;
-       unsigned i, n = 0;
+       int ret = 0;
 
        /* Select default pin state */
        pinctrl_pm_select_default_state(dev);
 
-       if (priv->ctx_valid == true) {
-               /* Restore register state: */
-               for (i = 0; i < ARRAY_SIZE(registers); i++)
-                       if (registers[i].save &&
-                           (priv->rev >= registers[i].rev))
-                               tilcdc_write(ddev, registers[i].reg,
-                                            priv->saved_register[n++]);
-       }
-
-       drm_kms_helper_poll_enable(ddev);
+       if (priv->saved_state)
+               ret = drm_atomic_helper_resume(ddev, priv->saved_state);
 
-       return 0;
+       return ret;
 }
 #endif
 
@@ -648,6 +670,12 @@ static int tilcdc_bind(struct device *dev)
 
 static void tilcdc_unbind(struct device *dev)
 {
+       struct drm_device *ddev = dev_get_drvdata(dev);
+
+       /* Check if a subcomponent has already triggered the unloading. */
+       if (!ddev->dev_private)
+               return;
+
        drm_put_dev(dev_get_drvdata(dev));
 }
 
@@ -680,17 +708,15 @@ static int tilcdc_pdev_probe(struct platform_device *pdev)
 
 static int tilcdc_pdev_remove(struct platform_device *pdev)
 {
-       struct drm_device *ddev = dev_get_drvdata(&pdev->dev);
-       struct tilcdc_drm_private *priv = ddev->dev_private;
-
-       /* Check if a subcomponent has already triggered the unloading. */
-       if (!priv)
-               return 0;
+       int ret;
 
-       if (priv->is_componentized)
-               component_master_del(&pdev->dev, &tilcdc_comp_ops);
-       else
+       ret = tilcdc_get_external_components(&pdev->dev, NULL);
+       if (ret < 0)
+               return ret;
+       else if (ret == 0)
                drm_put_dev(platform_get_drvdata(pdev));
+       else
+               component_master_del(&pdev->dev, &tilcdc_comp_ops);
 
        return 0;
 }
index c1de18b..9780c37 100644 (file)
@@ -65,13 +65,15 @@ struct tilcdc_drm_private {
         */
        uint32_t max_width;
 
-       /* register contents saved across suspend/resume: */
-       u32 *saved_register;
-       bool ctx_valid;
+       /* Supported pixel formats */
+       const uint32_t *pixelformats;
+       uint32_t num_pixelformats;
+
+       /* The context for pm susped/resume cycle is stored here */
+       struct drm_atomic_state *saved_state;
 
 #ifdef CONFIG_CPU_FREQ
        struct notifier_block freq_transition;
-       unsigned int lcd_fck_rate;
 #endif
 
        struct workqueue_struct *wq;
@@ -113,7 +115,6 @@ struct tilcdc_module {
        const char *name;
        struct list_head list;
        const struct tilcdc_module_ops *funcs;
-       unsigned int preferred_bpp;
 };
 
 void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
@@ -171,6 +172,11 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,
                                        bool simulate_vesa_sync);
 int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode);
 int tilcdc_crtc_max_width(struct drm_crtc *crtc);
-void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode);
+void tilcdc_crtc_disable(struct drm_crtc *crtc);
+int tilcdc_crtc_update_fb(struct drm_crtc *crtc,
+               struct drm_framebuffer *fb,
+               struct drm_pending_vblank_event *event);
+
+int tilcdc_plane_init(struct drm_device *dev, struct drm_plane *plane);
 
 #endif /* __TILCDC_DRV_H__ */
index 03acb4f..68e8950 100644 (file)
@@ -52,7 +52,7 @@ static int tilcdc_external_mode_valid(struct drm_connector *connector,
        return MODE_OK;
 }
 
-static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp,
+static int tilcdc_add_external_encoder(struct drm_device *dev,
                                       struct drm_connector *connector)
 {
        struct tilcdc_drm_private *priv = dev->dev_private;
@@ -64,7 +64,6 @@ static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp,
        /* Only tda998x is supported at the moment. */
        tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true);
        tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x);
-       *bpp = panel_info_tda998x.bpp;
 
        connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs),
                                       GFP_KERNEL);
@@ -94,7 +93,7 @@ static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp,
        return 0;
 }
 
-int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp)
+int tilcdc_add_external_encoders(struct drm_device *dev)
 {
        struct tilcdc_drm_private *priv = dev->dev_private;
        struct drm_connector *connector;
@@ -108,7 +107,7 @@ int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp)
                        if (connector == priv->connectors[i])
                                found = true;
                if (!found) {
-                       ret = tilcdc_add_external_encoder(dev, bpp, connector);
+                       ret = tilcdc_add_external_encoder(dev, connector);
                        if (ret)
                                return ret;
                }
@@ -138,14 +137,23 @@ static int dev_match_of(struct device *dev, void *data)
 int tilcdc_get_external_components(struct device *dev,
                                   struct component_match **match)
 {
+       struct device_node *node;
        struct device_node *ep = NULL;
        int count = 0;
 
-       while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
-               struct device_node *node;
+       /* Avoid error print by of_graph_get_next_endpoint() if there
+        * is no ports present.
+        */
+       node = of_get_child_by_name(dev->of_node, "ports");
+       if (!node)
+               node = of_get_child_by_name(dev->of_node, "port");
+       if (!node)
+               return 0;
+       of_node_put(node);
 
+       while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) {
                node = of_graph_get_remote_port_parent(ep);
-               if (!node && !of_device_is_available(node)) {
+               if (!node || !of_device_is_available(node)) {
                        of_node_put(node);
                        continue;
                }
index 6aabe27..c700e0c 100644 (file)
@@ -18,7 +18,7 @@
 #ifndef __TILCDC_EXTERNAL_H__
 #define __TILCDC_EXTERNAL_H__
 
-int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp);
+int tilcdc_add_external_encoders(struct drm_device *dev);
 void tilcdc_remove_external_encoders(struct drm_device *dev);
 int tilcdc_get_external_components(struct device *dev,
                                   struct component_match **match);
index ff7774c..2134bb2 100644 (file)
 #include <video/display_timing.h>
 #include <video/of_display_timing.h>
 #include <video/videomode.h>
+#include <drm/drm_atomic_helper.h>
 
 #include "tilcdc_drv.h"
+#include "tilcdc_panel.h"
 
 struct panel_module {
        struct tilcdc_module base;
@@ -64,9 +66,7 @@ static void panel_encoder_dpms(struct drm_encoder *encoder, int mode)
 
 static void panel_encoder_prepare(struct drm_encoder *encoder)
 {
-       struct panel_encoder *panel_encoder = to_panel_encoder(encoder);
        panel_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-       tilcdc_crtc_set_panel_info(encoder->crtc, panel_encoder->mod->info);
 }
 
 static void panel_encoder_commit(struct drm_encoder *encoder)
@@ -196,9 +196,12 @@ static struct drm_encoder *panel_connector_best_encoder(
 
 static const struct drm_connector_funcs panel_connector_funcs = {
        .destroy            = panel_connector_destroy,
-       .dpms               = drm_helper_connector_dpms,
+       .dpms               = drm_atomic_helper_connector_dpms,
        .detect             = panel_connector_detect,
        .fill_modes         = drm_helper_probe_single_connector_modes,
+       .reset              = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static const struct drm_connector_helper_funcs panel_connector_helper_funcs = {
@@ -268,6 +271,9 @@ static int panel_modeset_init(struct tilcdc_module *mod, struct drm_device *dev)
        priv->encoders[priv->num_encoders++] = encoder;
        priv->connectors[priv->num_connectors++] = connector;
 
+       tilcdc_crtc_set_panel_info(priv->crtc,
+                                  to_panel_encoder(encoder)->mod->info);
+
        return 0;
 }
 
@@ -392,8 +398,6 @@ static int panel_probe(struct platform_device *pdev)
                goto fail_timings;
        }
 
-       mod->preferred_bpp = panel_mod->info->bpp;
-
        return 0;
 
 fail_timings:
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_plane.c b/drivers/gpu/drm/tilcdc/tilcdc_plane.c
new file mode 100644 (file)
index 0000000..74c65fa
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 Texas Instruments
+ * Author: Jyri Sarha <jsarha@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <drm/drmP.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <uapi/drm/drm_fourcc.h>
+
+#include "tilcdc_drv.h"
+
+static struct drm_plane_funcs tilcdc_plane_funcs = {
+       .update_plane   = drm_atomic_helper_update_plane,
+       .disable_plane  = drm_atomic_helper_disable_plane,
+       .destroy        = drm_plane_cleanup,
+       .set_property   = drm_atomic_helper_plane_set_property,
+       .reset          = drm_atomic_helper_plane_reset,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int tilcdc_plane_atomic_check(struct drm_plane *plane,
+                                    struct drm_plane_state *state)
+{
+       struct drm_crtc_state *crtc_state;
+       struct drm_plane_state *old_state = plane->state;
+       unsigned int depth, bpp;
+
+       if (!state->crtc)
+               return 0;
+
+       if (WARN_ON(!state->fb))
+               return -EINVAL;
+
+       if (state->crtc_x || state->crtc_y) {
+               dev_err(plane->dev->dev, "%s: crtc position must be zero.",
+                       __func__);
+               return -EINVAL;
+       }
+
+       crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+                                                       state->crtc);
+       /* we should have a crtc state if the plane is attached to a crtc */
+       if (WARN_ON(!crtc_state))
+               return 0;
+
+       if (crtc_state->mode.hdisplay != state->crtc_w ||
+           crtc_state->mode.vdisplay != state->crtc_h) {
+               dev_err(plane->dev->dev,
+                       "%s: Size must match mode (%dx%d == %dx%d)", __func__,
+                       crtc_state->mode.hdisplay, crtc_state->mode.vdisplay,
+                       state->crtc_w, state->crtc_h);
+               return -EINVAL;
+       }
+
+       drm_fb_get_bpp_depth(state->fb->pixel_format, &depth, &bpp);
+       if (state->fb->pitches[0] != crtc_state->mode.hdisplay * bpp / 8) {
+               dev_err(plane->dev->dev,
+                       "Invalid pitch: fb and crtc widths must be the same");
+               return -EINVAL;
+       }
+
+       if (state->fb && old_state->fb &&
+           state->fb->pixel_format != old_state->fb->pixel_format) {
+               dev_dbg(plane->dev->dev,
+                       "%s(): pixel format change requires mode_change\n",
+                       __func__);
+               crtc_state->mode_changed = true;
+       }
+
+       return 0;
+}
+
+static void tilcdc_plane_atomic_update(struct drm_plane *plane,
+                                      struct drm_plane_state *old_state)
+{
+       struct drm_plane_state *state = plane->state;
+
+       if (!state->crtc)
+               return;
+
+       if (WARN_ON(!state->fb || !state->crtc->state))
+               return;
+
+       tilcdc_crtc_update_fb(state->crtc,
+                             state->fb,
+                             state->crtc->state->event);
+}
+
+static const struct drm_plane_helper_funcs plane_helper_funcs = {
+       .atomic_check = tilcdc_plane_atomic_check,
+       .atomic_update = tilcdc_plane_atomic_update,
+};
+
+int tilcdc_plane_init(struct drm_device *dev,
+                     struct drm_plane *plane)
+{
+       struct tilcdc_drm_private *priv = dev->dev_private;
+       int ret;
+
+       ret = drm_plane_init(dev, plane, 1,
+                            &tilcdc_plane_funcs,
+                            priv->pixelformats,
+                            priv->num_pixelformats,
+                            true);
+       if (ret) {
+               dev_err(dev->dev, "Failed to initialize plane: %d\n", ret);
+               return ret;
+       }
+
+       drm_plane_helper_add(plane, &plane_helper_funcs);
+
+       return 0;
+}
index 1bf5e25..f57c0d6 100644 (file)
@@ -119,6 +119,20 @@ static inline void tilcdc_write(struct drm_device *dev, u32 reg, u32 data)
        iowrite32(data, priv->mmio + reg);
 }
 
+static inline void tilcdc_write64(struct drm_device *dev, u32 reg, u64 data)
+{
+       struct tilcdc_drm_private *priv = dev->dev_private;
+       volatile void __iomem *addr = priv->mmio + reg;
+
+#ifdef iowrite64
+       iowrite64(data, addr);
+#else
+       __iowmb();
+       /* This compiles to strd (=64-bit write) on ARM7 */
+       *(volatile u64 __force *)addr = __cpu_to_le64(data);
+#endif
+}
+
 static inline u32 tilcdc_read(struct drm_device *dev, u32 reg)
 {
        struct tilcdc_drm_private *priv = dev->dev_private;
index f9c79da..623a914 100644 (file)
@@ -139,7 +139,7 @@ static void __init tilcdc_node_disable(struct device_node *node)
        of_update_property(node, prop);
 }
 
-struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft)
+static struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft)
 {
        const int size = __dtb_tilcdc_slave_compat_end -
                __dtb_tilcdc_slave_compat_begin;
@@ -195,7 +195,7 @@ static const char * const tilcdc_slave_props[] __initconst = {
        NULL
 };
 
-void __init tilcdc_convert_slave_node(void)
+static void __init tilcdc_convert_slave_node(void)
 {
        struct device_node *slave = NULL, *lcdc = NULL;
        struct device_node *i2c = NULL, *fragment = NULL;
@@ -207,7 +207,7 @@ void __init tilcdc_convert_slave_node(void)
        int ret;
 
        if (kfree_table_init(&kft))
-               goto out;
+               return;
 
        lcdc = of_find_matching_node(NULL, tilcdc_of_match);
        slave = of_find_matching_node(NULL, tilcdc_slave_of_match);
@@ -261,7 +261,7 @@ out:
        of_node_put(fragment);
 }
 
-int __init tilcdc_slave_compat_init(void)
+static int __init tilcdc_slave_compat_init(void)
 {
        tilcdc_convert_slave_node();
        return 0;
index 6b8c5b3..458043a 100644 (file)
 #include <linux/of_gpio.h>
 #include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/consumer.h>
+#include <drm/drm_atomic_helper.h>
 
 #include "tilcdc_drv.h"
+#include "tilcdc_tfp410.h"
 
 struct tfp410_module {
        struct tilcdc_module base;
@@ -75,7 +77,6 @@ static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode)
 static void tfp410_encoder_prepare(struct drm_encoder *encoder)
 {
        tfp410_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-       tilcdc_crtc_set_panel_info(encoder->crtc, &dvi_info);
 }
 
 static void tfp410_encoder_commit(struct drm_encoder *encoder)
@@ -201,9 +202,12 @@ static struct drm_encoder *tfp410_connector_best_encoder(
 
 static const struct drm_connector_funcs tfp410_connector_funcs = {
        .destroy            = tfp410_connector_destroy,
-       .dpms               = drm_helper_connector_dpms,
+       .dpms               = drm_atomic_helper_connector_dpms,
        .detect             = tfp410_connector_detect,
        .fill_modes         = drm_helper_probe_single_connector_modes,
+       .reset              = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static const struct drm_connector_helper_funcs tfp410_connector_helper_funcs = {
@@ -276,6 +280,7 @@ static int tfp410_modeset_init(struct tilcdc_module *mod, struct drm_device *dev
        priv->encoders[priv->num_encoders++] = encoder;
        priv->connectors[priv->num_connectors++] = connector;
 
+       tilcdc_crtc_set_panel_info(priv->crtc, &dvi_info);
        return 0;
 }
 
@@ -323,8 +328,6 @@ static int tfp410_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       mod->preferred_bpp = dvi_info.bpp;
-
        i2c_node = of_find_node_by_phandle(i2c_phandle);
        if (!i2c_node) {
                dev_err(&pdev->dev, "could not get i2c bus node\n");
index 42c074a..fc6217d 100644 (file)
@@ -57,14 +57,14 @@ static struct attribute ttm_bo_count = {
 static inline int ttm_mem_type_from_place(const struct ttm_place *place,
                                          uint32_t *mem_type)
 {
-       int i;
+       int pos;
 
-       for (i = 0; i <= TTM_PL_PRIV5; i++)
-               if (place->flags & (1 << i)) {
-                       *mem_type = i;
-                       return 0;
-               }
-       return -EINVAL;
+       pos = ffs(place->flags & TTM_PL_MASK_MEM);
+       if (unlikely(!pos))
+               return -EINVAL;
+
+       *mem_type = pos - 1;
+       return 0;
 }
 
 static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
@@ -354,14 +354,12 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
 
        if (!(old_man->flags & TTM_MEMTYPE_FLAG_FIXED) &&
            !(new_man->flags & TTM_MEMTYPE_FLAG_FIXED))
-               ret = ttm_bo_move_ttm(bo, evict, interruptible, no_wait_gpu,
-                                     mem);
+               ret = ttm_bo_move_ttm(bo, interruptible, no_wait_gpu, mem);
        else if (bdev->driver->move)
                ret = bdev->driver->move(bo, evict, interruptible,
                                         no_wait_gpu, mem);
        else
-               ret = ttm_bo_move_memcpy(bo, evict, interruptible,
-                                        no_wait_gpu, mem);
+               ret = ttm_bo_move_memcpy(bo, interruptible, no_wait_gpu, mem);
 
        if (ret) {
                if (bdev->driver->move_notify) {
index f157a9e..bf6e216 100644 (file)
@@ -45,8 +45,8 @@ void ttm_bo_free_old_node(struct ttm_buffer_object *bo)
 }
 
 int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
-                   bool evict, bool interruptible,
-                   bool no_wait_gpu, struct ttm_mem_reg *new_mem)
+                   bool interruptible, bool no_wait_gpu,
+                   struct ttm_mem_reg *new_mem)
 {
        struct ttm_tt *ttm = bo->ttm;
        struct ttm_mem_reg *old_mem = &bo->mem;
@@ -329,8 +329,7 @@ static int ttm_copy_ttm_io_page(struct ttm_tt *ttm, void *dst,
 }
 
 int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
-                      bool evict, bool interruptible,
-                      bool no_wait_gpu,
+                      bool interruptible, bool no_wait_gpu,
                       struct ttm_mem_reg *new_mem)
 {
        struct ttm_bo_device *bdev = bo->bdev;
index a1803fb..29855be 100644 (file)
@@ -600,3 +600,9 @@ size_t ttm_round_pot(size_t size)
        return 0;
 }
 EXPORT_SYMBOL(ttm_round_pot);
+
+uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob)
+{
+       return glob->zone_kernel->max_mem;
+}
+EXPORT_SYMBOL(ttm_get_kernel_zone_memory_size);
index bef9f6f..cec4b4b 100644 (file)
@@ -858,7 +858,6 @@ static int ttm_dma_pool_get_pages(struct dma_pool *pool,
        if (count) {
                d_page = list_first_entry(&pool->free_list, struct dma_page, page_list);
                ttm->pages[index] = d_page->p;
-               ttm_dma->cpu_address[index] = d_page->vaddr;
                ttm_dma->dma_address[index] = d_page->dma;
                list_move_tail(&d_page->page_list, &ttm_dma->pages_list);
                r = 0;
@@ -989,7 +988,6 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev)
        INIT_LIST_HEAD(&ttm_dma->pages_list);
        for (i = 0; i < ttm->num_pages; i++) {
                ttm->pages[i] = NULL;
-               ttm_dma->cpu_address[i] = 0;
                ttm_dma->dma_address[i] = 0;
        }
 
index bc5aa57..aee3c00 100644 (file)
@@ -57,10 +57,8 @@ static void ttm_dma_tt_alloc_page_directory(struct ttm_dma_tt *ttm)
 {
        ttm->ttm.pages = drm_calloc_large(ttm->ttm.num_pages,
                                          sizeof(*ttm->ttm.pages) +
-                                         sizeof(*ttm->dma_address) +
-                                         sizeof(*ttm->cpu_address));
-       ttm->cpu_address = (void *) (ttm->ttm.pages + ttm->ttm.num_pages);
-       ttm->dma_address = (void *) (ttm->cpu_address + ttm->ttm.num_pages);
+                                         sizeof(*ttm->dma_address));
+       ttm->dma_address = (void *) (ttm->ttm.pages + ttm->ttm.num_pages);
 }
 
 #ifdef CONFIG_X86
@@ -244,7 +242,6 @@ void ttm_dma_tt_fini(struct ttm_dma_tt *ttm_dma)
 
        drm_free_large(ttm->pages);
        ttm->pages = NULL;
-       ttm_dma->cpu_address = NULL;
        ttm_dma->dma_address = NULL;
 }
 EXPORT_SYMBOL(ttm_dma_tt_fini);
index 4709b54..d2f57c5 100644 (file)
@@ -150,8 +150,5 @@ int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)
        drm_connector_register(connector);
        drm_mode_connector_attach_encoder(connector, encoder);
 
-       drm_object_attach_property(&connector->base,
-                                     dev->mode_config.dirty_info_property,
-                                     1);
        return 0;
 }
index e2243ed..ac90ffd 100644 (file)
@@ -209,7 +209,7 @@ struct dma_buf *udl_gem_prime_export(struct drm_device *dev,
        exp_info.flags = flags;
        exp_info.priv = obj;
 
-       return dma_buf_export(&exp_info);
+       return drm_gem_dmabuf_export(dev, &exp_info);
 }
 
 static int udl_prime_create(struct drm_device *dev,
index 17d34e0..cc45d98 100644 (file)
@@ -16,6 +16,20 @@ static int udl_driver_set_busid(struct drm_device *d, struct drm_master *m)
        return 0;
 }
 
+static int udl_usb_suspend(struct usb_interface *interface,
+                          pm_message_t message)
+{
+       return 0;
+}
+
+static int udl_usb_resume(struct usb_interface *interface)
+{
+       struct drm_device *dev = usb_get_intfdata(interface);
+
+       udl_modeset_restore(dev);
+       return 0;
+}
+
 static const struct vm_operations_struct udl_gem_vm_ops = {
        .fault = udl_gem_fault,
        .open = drm_gem_vm_open,
@@ -72,8 +86,8 @@ static int udl_usb_probe(struct usb_interface *interface,
        int r;
 
        dev = drm_dev_alloc(&driver, &interface->dev);
-       if (!dev)
-               return -ENOMEM;
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
 
        r = drm_dev_register(dev, (unsigned long)udev);
        if (r)
@@ -122,6 +136,8 @@ static struct usb_driver udl_driver = {
        .name = "udl",
        .probe = udl_usb_probe,
        .disconnect = udl_usb_disconnect,
+       .suspend = udl_usb_suspend,
+       .resume = udl_usb_resume,
        .id_table = id_table,
 };
 module_usb_driver(udl_driver);
index 0b03d34..f338a57 100644 (file)
@@ -52,6 +52,7 @@ struct udl_device {
        struct device *dev;
        struct drm_device *ddev;
        struct usb_device *udev;
+       struct drm_crtc *crtc;
 
        int sku_pixel_limit;
 
@@ -87,6 +88,7 @@ struct udl_framebuffer {
 
 /* modeset */
 int udl_modeset_init(struct drm_device *dev);
+void udl_modeset_restore(struct drm_device *dev);
 void udl_modeset_cleanup(struct drm_device *dev);
 int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder);
 
index 33dbfb2..29f0207 100644 (file)
@@ -16,6 +16,8 @@
 /* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */
 #define BULK_SIZE 512
 
+#define NR_USB_REQUEST_CHANNEL 0x12
+
 #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE)
 #define WRITES_IN_FLIGHT (4)
 #define MAX_VENDOR_DESCRIPTOR_SIZE 256
@@ -90,6 +92,26 @@ success:
        return true;
 }
 
+/*
+ * Need to ensure a channel is selected before submitting URBs
+ */
+static int udl_select_std_channel(struct udl_device *udl)
+{
+       int ret;
+       u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7,
+                           0x1C, 0x88, 0x5E, 0x15,
+                           0x60, 0xFE, 0xC6, 0x97,
+                           0x16, 0x3D, 0x47, 0xF2};
+
+       ret = usb_control_msg(udl->udev,
+                             usb_sndctrlpipe(udl->udev, 0),
+                             NR_USB_REQUEST_CHANNEL,
+                             (USB_DIR_OUT | USB_TYPE_VENDOR), 0, 0,
+                             set_def_chn, sizeof(set_def_chn),
+                             USB_CTRL_SET_TIMEOUT);
+       return ret < 0 ? ret : 0;
+}
+
 static void udl_release_urb_work(struct work_struct *work)
 {
        struct urb_node *unode = container_of(work, struct urb_node,
@@ -301,6 +323,9 @@ int udl_driver_load(struct drm_device *dev, unsigned long flags)
                goto err;
        }
 
+       if (udl_select_std_channel(udl))
+               DRM_ERROR("Selecting channel failed\n");
+
        if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
                DRM_ERROR("udl_alloc_urb_list failed\n");
                goto err;
index f92ea95..f2b2481 100644 (file)
@@ -309,6 +309,8 @@ static int udl_crtc_mode_set(struct drm_crtc *crtc,
        char *wrptr;
        int color_depth = 0;
 
+       udl->crtc = crtc;
+
        buf = (char *)udl->mode_buf;
 
        /* for now we just clip 24 -> 16 - if we fix that fix this */
@@ -441,8 +443,6 @@ int udl_modeset_init(struct drm_device *dev)
 
        dev->mode_config.funcs = &udl_mode_funcs;
 
-       drm_mode_create_dirty_info_property(dev);
-
        udl_crtc_init(dev);
 
        encoder = udl_encoder_init(dev);
@@ -452,6 +452,18 @@ int udl_modeset_init(struct drm_device *dev)
        return 0;
 }
 
+void udl_modeset_restore(struct drm_device *dev)
+{
+       struct udl_device *udl = dev->dev_private;
+       struct udl_framebuffer *ufb;
+
+       if (!udl->crtc || !udl->crtc->primary->fb)
+               return;
+       udl_crtc_commit(udl->crtc);
+       ufb = to_udl_fb(udl->crtc->primary->fb);
+       udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height);
+}
+
 void udl_modeset_cleanup(struct drm_device *dev)
 {
        drm_mode_config_cleanup(dev);
index 8fc2b73..7f08d68 100644 (file)
@@ -163,14 +163,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
        int vblank_lines;
        int ret = 0;
 
-       /*
-        * XXX Doesn't work well in interlaced mode yet, partially due
-        * to problems in vc4 kms or drm core interlaced mode handling,
-        * so disable for now in interlaced mode.
-        */
-       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-               return ret;
-
        /* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
 
        /* Get optional system timestamp before query. */
@@ -191,10 +183,15 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
 
        /* Vertical position of hvs composed scanline. */
        *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
+       *hpos = 0;
 
-       /* No hpos info available. */
-       if (hpos)
-               *hpos = 0;
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+               *vpos /= 2;
+
+               /* Use hpos to correct for field offset in interlaced mode. */
+               if (VC4_GET_FIELD(val, SCALER_DISPSTATX_FRAME_COUNT) % 2)
+                       *hpos += mode->crtc_htotal / 2;
+       }
 
        /* This is the offset we need for translating hvs -> pv scanout pos. */
        fifo_lines = vc4_crtc->cob_size / mode->crtc_hdisplay;
@@ -217,8 +214,6 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
                 * position of the PV.
                 */
                *vpos -= fifo_lines + 1;
-               if (mode->flags & DRM_MODE_FLAG_INTERLACE)
-                       *vpos /= 2;
 
                ret |= DRM_SCANOUTPOS_ACCURATE;
                return ret;
@@ -234,7 +229,7 @@ int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id,
         * and need to make things up in a approximative but consistent way.
         */
        ret |= DRM_SCANOUTPOS_IN_VBLANK;
-       vblank_lines = mode->crtc_vtotal - mode->crtc_vdisplay;
+       vblank_lines = mode->vtotal - mode->vdisplay;
 
        if (flags & DRM_CALLED_FROM_VBLIRQ) {
                /*
@@ -383,7 +378,7 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
        struct drm_crtc_state *state = crtc->state;
        struct drm_display_mode *mode = &state->adjusted_mode;
        bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
-       u32 vactive = (mode->vdisplay >> (interlace ? 1 : 0));
+       u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
        u32 format = PV_CONTROL_FORMAT_24;
        bool debug_dump_regs = false;
        int clock_select = vc4_get_clock_select(crtc);
@@ -399,47 +394,65 @@ static void vc4_crtc_mode_set_nofb(struct drm_crtc *crtc)
        CRTC_WRITE(PV_CONTROL, 0);
 
        CRTC_WRITE(PV_HORZA,
-                  VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+                  VC4_SET_FIELD((mode->htotal -
+                                 mode->hsync_end) * pixel_rep,
                                 PV_HORZA_HBP) |
-                  VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+                  VC4_SET_FIELD((mode->hsync_end -
+                                 mode->hsync_start) * pixel_rep,
                                 PV_HORZA_HSYNC));
        CRTC_WRITE(PV_HORZB,
-                  VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+                  VC4_SET_FIELD((mode->hsync_start -
+                                 mode->hdisplay) * pixel_rep,
                                 PV_HORZB_HFP) |
-                  VC4_SET_FIELD(mode->hdisplay, PV_HORZB_HACTIVE));
+                  VC4_SET_FIELD(mode->hdisplay * pixel_rep, PV_HORZB_HACTIVE));
 
        CRTC_WRITE(PV_VERTA,
-                  VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+                  VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
                                 PV_VERTA_VBP) |
-                  VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+                  VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
                                 PV_VERTA_VSYNC));
        CRTC_WRITE(PV_VERTB,
-                  VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                  VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
                                 PV_VERTB_VFP) |
-                  VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+                  VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
 
        if (interlace) {
                CRTC_WRITE(PV_VERTA_EVEN,
-                          VC4_SET_FIELD(mode->vtotal - mode->vsync_end - 1,
+                          VC4_SET_FIELD(mode->crtc_vtotal -
+                                        mode->crtc_vsync_end - 1,
                                         PV_VERTA_VBP) |
-                          VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+                          VC4_SET_FIELD(mode->crtc_vsync_end -
+                                        mode->crtc_vsync_start,
                                         PV_VERTA_VSYNC));
                CRTC_WRITE(PV_VERTB_EVEN,
-                          VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                          VC4_SET_FIELD(mode->crtc_vsync_start -
+                                        mode->crtc_vdisplay,
                                         PV_VERTB_VFP) |
-                          VC4_SET_FIELD(vactive, PV_VERTB_VACTIVE));
+                          VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
+
+               /* We set up first field even mode for HDMI.  VEC's
+                * NTSC mode would want first field odd instead, once
+                * we support it (to do so, set ODD_FIRST and put the
+                * delay in VSYNCD_EVEN instead).
+                */
+               CRTC_WRITE(PV_V_CONTROL,
+                          PV_VCONTROL_CONTINUOUS |
+                          PV_VCONTROL_INTERLACE |
+                          VC4_SET_FIELD(mode->htotal * pixel_rep / 2,
+                                        PV_VCONTROL_ODD_DELAY));
+               CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+       } else {
+               CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS);
        }
 
-       CRTC_WRITE(PV_HACT_ACT, mode->hdisplay);
+       CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
 
-       CRTC_WRITE(PV_V_CONTROL,
-                  PV_VCONTROL_CONTINUOUS |
-                  (interlace ? PV_VCONTROL_INTERLACE : 0));
 
        CRTC_WRITE(PV_CONTROL,
                   VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
                   VC4_SET_FIELD(vc4_get_fifo_full_level(format),
                                 PV_CONTROL_FIFO_LEVEL) |
+                  VC4_SET_FIELD(pixel_rep - 1, PV_CONTROL_PIXEL_REP) |
                   PV_CONTROL_CLR_AT_START |
                   PV_CONTROL_TRIGGER_UNDERFLOW |
                   PV_CONTROL_WAIT_HSTART |
@@ -480,6 +493,9 @@ static void vc4_crtc_disable(struct drm_crtc *crtc)
        int ret;
        require_hvs_enabled(dev);
 
+       /* Disable vblank irq handling before crtc is disabled. */
+       drm_crtc_vblank_off(crtc);
+
        CRTC_WRITE(PV_V_CONTROL,
                   CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
        ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
@@ -530,6 +546,23 @@ static void vc4_crtc_enable(struct drm_crtc *crtc)
        /* Turn on the pixel valve, which will emit the vstart signal. */
        CRTC_WRITE(PV_V_CONTROL,
                   CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
+
+       /* Enable vblank irq handling after crtc is started. */
+       drm_crtc_vblank_on(crtc);
+}
+
+static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc,
+                               const struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       /* Do not allow doublescan modes from user space */
+       if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+               DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n",
+                             crtc->base.id);
+               return false;
+       }
+
+       return true;
 }
 
 static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
@@ -819,6 +852,7 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
        .mode_set_nofb = vc4_crtc_mode_set_nofb,
        .disable = vc4_crtc_disable,
        .enable = vc4_crtc_enable,
+       .mode_fixup = vc4_crtc_mode_fixup,
        .atomic_check = vc4_crtc_atomic_check,
        .atomic_flush = vc4_crtc_atomic_flush,
 };
index 275fedb..1e1f6b8 100644 (file)
@@ -340,9 +340,20 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
        }
 }
 
+static bool vc4_dpi_encoder_mode_fixup(struct drm_encoder *encoder,
+                                      const struct drm_display_mode *mode,
+                                      struct drm_display_mode *adjusted_mode)
+{
+       if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE)
+               return false;
+
+       return true;
+}
+
 static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {
        .disable = vc4_dpi_encoder_disable,
        .enable = vc4_dpi_encoder_enable,
+       .mode_fixup = vc4_dpi_encoder_mode_fixup,
 };
 
 static const struct of_device_id vc4_dpi_dt_match[] = {
index 9ecef93..8703f56 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include "drm_fb_cma_helper.h"
+#include <drm/drm_fb_helper.h>
 
 #include "uapi/drm/vc4_drm.h"
 #include "vc4_drv.h"
@@ -214,7 +215,7 @@ static void vc4_kick_out_firmware_fb(void)
        ap->ranges[0].base = 0;
        ap->ranges[0].size = ~0;
 
-       remove_conflicting_framebuffers(ap, "vc4drmfb", false);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "vc4drmfb", false);
        kfree(ap);
 }
 
@@ -232,8 +233,8 @@ static int vc4_drm_bind(struct device *dev)
                return -ENOMEM;
 
        drm = drm_dev_alloc(&vc4_drm_driver, dev);
-       if (!drm)
-               return -ENOMEM;
+       if (IS_ERR(drm))
+               return PTR_ERR(drm);
        platform_set_drvdata(pdev, drm);
        vc4->dev = drm;
        drm->dev_private = vc4;
index 428e249..7c1e4d9 100644 (file)
@@ -122,9 +122,16 @@ to_vc4_dev(struct drm_device *dev)
 struct vc4_bo {
        struct drm_gem_cma_object base;
 
-       /* seqno of the last job to render to this BO. */
+       /* seqno of the last job to render using this BO. */
        uint64_t seqno;
 
+       /* seqno of the last job to use the RCL to write to this BO.
+        *
+        * Note that this doesn't include binner overflow memory
+        * writes.
+        */
+       uint64_t write_seqno;
+
        /* List entry for the BO's position in either
         * vc4_exec_info->unref_list or vc4_dev->bo_cache.time_list
         */
@@ -216,6 +223,9 @@ struct vc4_exec_info {
        /* Sequence number for this bin/render job. */
        uint64_t seqno;
 
+       /* Latest write_seqno of any BO that binning depends on. */
+       uint64_t bin_dep_seqno;
+
        /* Last current addresses the hardware was processing when the
         * hangcheck timer checked on us.
         */
@@ -230,6 +240,13 @@ struct vc4_exec_info {
        struct drm_gem_cma_object **bo;
        uint32_t bo_count;
 
+       /* List of BOs that are being written by the RCL.  Other than
+        * the binner temporary storage, this is all the BOs written
+        * by the job.
+        */
+       struct drm_gem_cma_object *rcl_write_bo[4];
+       uint32_t rcl_write_bo_count;
+
        /* Pointers for our position in vc4->job_list */
        struct list_head head;
 
@@ -307,18 +324,15 @@ struct vc4_exec_info {
 static inline struct vc4_exec_info *
 vc4_first_bin_job(struct vc4_dev *vc4)
 {
-       if (list_empty(&vc4->bin_job_list))
-               return NULL;
-       return list_first_entry(&vc4->bin_job_list, struct vc4_exec_info, head);
+       return list_first_entry_or_null(&vc4->bin_job_list,
+                                       struct vc4_exec_info, head);
 }
 
 static inline struct vc4_exec_info *
 vc4_first_render_job(struct vc4_dev *vc4)
 {
-       if (list_empty(&vc4->render_job_list))
-               return NULL;
-       return list_first_entry(&vc4->render_job_list,
-                               struct vc4_exec_info, head);
+       return list_first_entry_or_null(&vc4->render_job_list,
+                                       struct vc4_exec_info, head);
 }
 
 static inline struct vc4_exec_info *
index b262c5c..47a095f 100644 (file)
@@ -419,10 +419,6 @@ again:
 
        vc4_flush_caches(dev);
 
-       /* Disable the binner's pre-loaded overflow memory address */
-       V3D_WRITE(V3D_BPOA, 0);
-       V3D_WRITE(V3D_BPOS, 0);
-
        /* Either put the job in the binner if it uses the binner, or
         * immediately move it to the to-be-rendered queue.
         */
@@ -471,6 +467,11 @@ vc4_update_bo_seqnos(struct vc4_exec_info *exec, uint64_t seqno)
        list_for_each_entry(bo, &exec->unref_list, unref_head) {
                bo->seqno = seqno;
        }
+
+       for (i = 0; i < exec->rcl_write_bo_count; i++) {
+               bo = to_vc4_bo(&exec->rcl_write_bo[i]->base);
+               bo->write_seqno = seqno;
+       }
 }
 
 /* Queues a struct vc4_exec_info for execution.  If no job is
@@ -673,6 +674,14 @@ vc4_get_bcl(struct drm_device *dev, struct vc4_exec_info *exec)
                goto fail;
 
        ret = vc4_validate_shader_recs(dev, exec);
+       if (ret)
+               goto fail;
+
+       /* Block waiting on any previous rendering into the CS's VBO,
+        * IB, or textures, so that pixels are actually written by the
+        * time we try to read them.
+        */
+       ret = vc4_wait_for_seqno(dev, exec->bin_dep_seqno, ~0ull, true);
 
 fail:
        drm_free_large(temp);
index 4452f36..c4cb2e2 100644 (file)
@@ -62,6 +62,8 @@ struct vc4_hdmi {
 struct vc4_hdmi_encoder {
        struct vc4_encoder base;
        bool hdmi_monitor;
+       bool limited_rgb_range;
+       bool rgb_range_selectable;
 };
 
 static inline struct vc4_hdmi_encoder *
@@ -174,6 +176,9 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
                        return connector_status_disconnected;
        }
 
+       if (drm_probe_ddc(vc4->hdmi->ddc))
+               return connector_status_connected;
+
        if (HDMI_READ(VC4_HDMI_HOTPLUG) & VC4_HDMI_HOTPLUG_CONNECTED)
                return connector_status_connected;
        else
@@ -202,6 +207,12 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
                return -ENODEV;
 
        vc4_encoder->hdmi_monitor = drm_detect_hdmi_monitor(edid);
+
+       if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) {
+               vc4_encoder->rgb_range_selectable =
+                       drm_rgb_quant_range_selectable(edid);
+       }
+
        drm_mode_connector_update_edid_property(connector, edid);
        ret = drm_add_edid_modes(connector, edid);
 
@@ -246,7 +257,7 @@ static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
        connector->polled = (DRM_CONNECTOR_POLL_CONNECT |
                             DRM_CONNECTOR_POLL_DISCONNECT);
 
-       connector->interlace_allowed = 0;
+       connector->interlace_allowed = 1;
        connector->doublescan_allowed = 0;
 
        drm_mode_connector_attach_encoder(connector, encoder);
@@ -269,25 +280,143 @@ static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
        .destroy = vc4_hdmi_encoder_destroy,
 };
 
+static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
+                               enum hdmi_infoframe_type type)
+{
+       struct drm_device *dev = encoder->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       u32 packet_id = type - 0x80;
+
+       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+                  HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
+
+       return wait_for(!(HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
+                         BIT(packet_id)), 100);
+}
+
+static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
+                                    union hdmi_infoframe *frame)
+{
+       struct drm_device *dev = encoder->dev;
+       struct vc4_dev *vc4 = to_vc4_dev(dev);
+       u32 packet_id = frame->any.type - 0x80;
+       u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id;
+       uint8_t buffer[VC4_HDMI_PACKET_STRIDE];
+       ssize_t len, i;
+       int ret;
+
+       WARN_ONCE(!(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) &
+                   VC4_HDMI_RAM_PACKET_ENABLE),
+                 "Packet RAM has to be on to store the packet.");
+
+       len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
+       if (len < 0)
+               return;
+
+       ret = vc4_hdmi_stop_packet(encoder, frame->any.type);
+       if (ret) {
+               DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
+               return;
+       }
+
+       for (i = 0; i < len; i += 7) {
+               HDMI_WRITE(packet_reg,
+                          buffer[i + 0] << 0 |
+                          buffer[i + 1] << 8 |
+                          buffer[i + 2] << 16);
+               packet_reg += 4;
+
+               HDMI_WRITE(packet_reg,
+                          buffer[i + 3] << 0 |
+                          buffer[i + 4] << 8 |
+                          buffer[i + 5] << 16 |
+                          buffer[i + 6] << 24);
+               packet_reg += 4;
+       }
+
+       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+                  HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) | BIT(packet_id));
+       ret = wait_for((HDMI_READ(VC4_HDMI_RAM_PACKET_STATUS) &
+                       BIT(packet_id)), 100);
+       if (ret)
+               DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
+}
+
+static void vc4_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+{
+       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
+       struct drm_crtc *crtc = encoder->crtc;
+       const struct drm_display_mode *mode = &crtc->state->adjusted_mode;
+       union hdmi_infoframe frame;
+       int ret;
+
+       ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi, mode);
+       if (ret < 0) {
+               DRM_ERROR("couldn't fill AVI infoframe\n");
+               return;
+       }
+
+       if (vc4_encoder->rgb_range_selectable) {
+               if (vc4_encoder->limited_rgb_range) {
+                       frame.avi.quantization_range =
+                               HDMI_QUANTIZATION_RANGE_LIMITED;
+               } else {
+                       frame.avi.quantization_range =
+                               HDMI_QUANTIZATION_RANGE_FULL;
+               }
+       }
+
+       vc4_hdmi_write_infoframe(encoder, &frame);
+}
+
+static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
+{
+       union hdmi_infoframe frame;
+       int ret;
+
+       ret = hdmi_spd_infoframe_init(&frame.spd, "Broadcom", "Videocore");
+       if (ret < 0) {
+               DRM_ERROR("couldn't fill SPD infoframe\n");
+               return;
+       }
+
+       frame.spd.sdi = HDMI_SPD_SDI_PC;
+
+       vc4_hdmi_write_infoframe(encoder, &frame);
+}
+
+static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder)
+{
+       vc4_hdmi_set_avi_infoframe(encoder);
+       vc4_hdmi_set_spd_infoframe(encoder);
+}
+
 static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
                                      struct drm_display_mode *unadjusted_mode,
                                      struct drm_display_mode *mode)
 {
+       struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
        struct drm_device *dev = encoder->dev;
        struct vc4_dev *vc4 = to_vc4_dev(dev);
        bool debug_dump_regs = false;
        bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
        bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
-       u32 vactive = (mode->vdisplay >>
-                      ((mode->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0));
-       u32 verta = (VC4_SET_FIELD(mode->vsync_end - mode->vsync_start,
+       bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
+       u32 pixel_rep = (mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1;
+       u32 verta = (VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
                                   VC4_HDMI_VERTA_VSP) |
-                    VC4_SET_FIELD(mode->vsync_start - mode->vdisplay,
+                    VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
                                   VC4_HDMI_VERTA_VFP) |
-                    VC4_SET_FIELD(vactive, VC4_HDMI_VERTA_VAL));
+                    VC4_SET_FIELD(mode->crtc_vdisplay, VC4_HDMI_VERTA_VAL));
        u32 vertb = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
-                    VC4_SET_FIELD(mode->vtotal - mode->vsync_end,
+                    VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
                                   VC4_HDMI_VERTB_VBP));
+       u32 vertb_even = (VC4_SET_FIELD(0, VC4_HDMI_VERTB_VSPO) |
+                         VC4_SET_FIELD(mode->crtc_vtotal -
+                                       mode->crtc_vsync_end -
+                                       interlaced,
+                                       VC4_HDMI_VERTB_VBP));
+       u32 csc_ctl;
 
        if (debug_dump_regs) {
                DRM_INFO("HDMI regs before:\n");
@@ -296,7 +425,8 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
 
        HD_WRITE(VC4_HD_VID_CTL, 0);
 
-       clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000);
+       clk_set_rate(vc4->hdmi->pixel_clock, mode->clock * 1000 *
+                    ((mode->flags & DRM_MODE_FLAG_DBLCLK) ? 2 : 1));
 
        HDMI_WRITE(VC4_HDMI_SCHEDULER_CONTROL,
                   HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
@@ -306,29 +436,62 @@ static void vc4_hdmi_encoder_mode_set(struct drm_encoder *encoder,
        HDMI_WRITE(VC4_HDMI_HORZA,
                   (vsync_pos ? VC4_HDMI_HORZA_VPOS : 0) |
                   (hsync_pos ? VC4_HDMI_HORZA_HPOS : 0) |
-                  VC4_SET_FIELD(mode->hdisplay, VC4_HDMI_HORZA_HAP));
+                  VC4_SET_FIELD(mode->hdisplay * pixel_rep,
+                                VC4_HDMI_HORZA_HAP));
 
        HDMI_WRITE(VC4_HDMI_HORZB,
-                  VC4_SET_FIELD(mode->htotal - mode->hsync_end,
+                  VC4_SET_FIELD((mode->htotal -
+                                 mode->hsync_end) * pixel_rep,
                                 VC4_HDMI_HORZB_HBP) |
-                  VC4_SET_FIELD(mode->hsync_end - mode->hsync_start,
+                  VC4_SET_FIELD((mode->hsync_end -
+                                 mode->hsync_start) * pixel_rep,
                                 VC4_HDMI_HORZB_HSP) |
-                  VC4_SET_FIELD(mode->hsync_start - mode->hdisplay,
+                  VC4_SET_FIELD((mode->hsync_start -
+                                 mode->hdisplay) * pixel_rep,
                                 VC4_HDMI_HORZB_HFP));
 
        HDMI_WRITE(VC4_HDMI_VERTA0, verta);
        HDMI_WRITE(VC4_HDMI_VERTA1, verta);
 
-       HDMI_WRITE(VC4_HDMI_VERTB0, vertb);
+       HDMI_WRITE(VC4_HDMI_VERTB0, vertb_even);
        HDMI_WRITE(VC4_HDMI_VERTB1, vertb);
 
        HD_WRITE(VC4_HD_VID_CTL,
                 (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
                 (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
 
+       csc_ctl = VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
+                               VC4_HD_CSC_CTL_ORDER);
+
+       if (vc4_encoder->hdmi_monitor && drm_match_cea_mode(mode) > 1) {
+               /* CEA VICs other than #1 requre limited range RGB
+                * output unless overridden by an AVI infoframe.
+                * Apply a colorspace conversion to squash 0-255 down
+                * to 16-235.  The matrix here is:
+                *
+                * [ 0      0      0.8594 16]
+                * [ 0      0.8594 0      16]
+                * [ 0.8594 0      0      16]
+                * [ 0      0      0       1]
+                */
+               csc_ctl |= VC4_HD_CSC_CTL_ENABLE;
+               csc_ctl |= VC4_HD_CSC_CTL_RGB2YCC;
+               csc_ctl |= VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
+                                        VC4_HD_CSC_CTL_MODE);
+
+               HD_WRITE(VC4_HD_CSC_12_11, (0x000 << 16) | 0x000);
+               HD_WRITE(VC4_HD_CSC_14_13, (0x100 << 16) | 0x6e0);
+               HD_WRITE(VC4_HD_CSC_22_21, (0x6e0 << 16) | 0x000);
+               HD_WRITE(VC4_HD_CSC_24_23, (0x100 << 16) | 0x000);
+               HD_WRITE(VC4_HD_CSC_32_31, (0x000 << 16) | 0x6e0);
+               HD_WRITE(VC4_HD_CSC_34_33, (0x100 << 16) | 0x000);
+               vc4_encoder->limited_rgb_range = true;
+       } else {
+               vc4_encoder->limited_rgb_range = false;
+       }
+
        /* The RGB order applies even when CSC is disabled. */
-       HD_WRITE(VC4_HD_CSC_CTL, VC4_SET_FIELD(VC4_HD_CSC_CTL_ORDER_BGR,
-                                              VC4_HD_CSC_CTL_ORDER));
+       HD_WRITE(VC4_HD_CSC_CTL, csc_ctl);
 
        HDMI_WRITE(VC4_HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
 
@@ -343,6 +506,8 @@ static void vc4_hdmi_encoder_disable(struct drm_encoder *encoder)
        struct drm_device *dev = encoder->dev;
        struct vc4_dev *vc4 = to_vc4_dev(dev);
 
+       HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG, 0);
+
        HDMI_WRITE(VC4_HDMI_TX_PHY_RESET_CTL, 0xf << 16);
        HD_WRITE(VC4_HD_VID_CTL,
                 HD_READ(VC4_HD_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
@@ -369,7 +534,7 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
                           VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
 
                ret = wait_for(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
-                              VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1);
+                              VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE, 1000);
                WARN_ONCE(ret, "Timeout waiting for "
                          "VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
        } else {
@@ -381,7 +546,7 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
                           ~VC4_HDMI_SCHEDULER_CONTROL_MODE_HDMI);
 
                ret = wait_for(!(HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) &
-                                VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1);
+                                VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE), 1000);
                WARN_ONCE(ret, "Timeout waiting for "
                          "!VC4_HDMI_SCHEDULER_CONTROL_HDMI_ACTIVE\n");
        }
@@ -395,9 +560,10 @@ static void vc4_hdmi_encoder_enable(struct drm_encoder *encoder)
                           HDMI_READ(VC4_HDMI_SCHEDULER_CONTROL) |
                           VC4_HDMI_SCHEDULER_CONTROL_VERT_ALWAYS_KEEPOUT);
 
-               /* XXX: Set HDMI_RAM_PACKET_CONFIG (1 << 16) and set
-                * up the infoframe.
-                */
+               HDMI_WRITE(VC4_HDMI_RAM_PACKET_CONFIG,
+                          VC4_HDMI_RAM_PACKET_ENABLE);
+
+               vc4_hdmi_set_infoframes(encoder);
 
                drift = HDMI_READ(VC4_HDMI_FIFO_CTL);
                drift &= VC4_HDMI_FIFO_VALID_WRITE_MASK;
index 4ac894d..c1f65c6 100644 (file)
@@ -44,7 +44,7 @@ vc4_atomic_complete_commit(struct vc4_commit *c)
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
 
-       drm_atomic_helper_commit_planes(dev, state, false);
+       drm_atomic_helper_commit_planes(dev, state, 0);
 
        drm_atomic_helper_commit_modeset_enables(dev, state);
 
index 29e4b40..881bf48 100644 (file)
@@ -735,8 +735,6 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
 }
 
 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
-       .prepare_fb = NULL,
-       .cleanup_fb = NULL,
        .atomic_check = vc4_plane_atomic_check,
        .atomic_update = vc4_plane_atomic_update,
 };
index 160942a..1aa44c2 100644 (file)
 # define PV_CONTROL_CLR_AT_START               BIT(14)
 # define PV_CONTROL_TRIGGER_UNDERFLOW          BIT(13)
 # define PV_CONTROL_WAIT_HSTART                        BIT(12)
+# define PV_CONTROL_PIXEL_REP_MASK             VC4_MASK(5, 4)
+# define PV_CONTROL_PIXEL_REP_SHIFT            4
 # define PV_CONTROL_CLK_SELECT_DSI_VEC         0
 # define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI    1
 # define PV_CONTROL_CLK_SELECT_MASK            VC4_MASK(3, 2)
 # define PV_CONTROL_EN                         BIT(0)
 
 #define PV_V_CONTROL                           0x04
+# define PV_VCONTROL_ODD_DELAY_MASK            VC4_MASK(22, 6)
+# define PV_VCONTROL_ODD_DELAY_SHIFT           6
+# define PV_VCONTROL_ODD_FIRST                 BIT(5)
 # define PV_VCONTROL_INTERLACE                 BIT(4)
 # define PV_VCONTROL_CONTINUOUS                        BIT(1)
 # define PV_VCONTROL_VIDEN                     BIT(0)
 #define VC4_HDMI_RAM_PACKET_CONFIG             0x0a0
 # define VC4_HDMI_RAM_PACKET_ENABLE            BIT(16)
 
+#define VC4_HDMI_RAM_PACKET_STATUS             0x0a4
+
 #define VC4_HDMI_HORZA                         0x0c4
 # define VC4_HDMI_HORZA_VPOS                   BIT(14)
 # define VC4_HDMI_HORZA_HPOS                   BIT(13)
 
 #define VC4_HDMI_TX_PHY_RESET_CTL              0x2c0
 
+#define VC4_HDMI_GCP_0                         0x400
+#define VC4_HDMI_PACKET_STRIDE                 0x24
+
 #define VC4_HD_M_CTL                           0x00c
 # define VC4_HD_M_REGISTER_FILE_STANDBY                (3 << 6)
 # define VC4_HD_M_RAM_STANDBY                  (3 << 4)
 # define VC4_HD_CSC_CTL_MODE_SHIFT             2
 # define VC4_HD_CSC_CTL_MODE_RGB_TO_SD_YPRPB   0
 # define VC4_HD_CSC_CTL_MODE_RGB_TO_HD_YPRPB   1
-# define VC4_HD_CSC_CTL_MODE_CUSTOM            2
+# define VC4_HD_CSC_CTL_MODE_CUSTOM            3
 # define VC4_HD_CSC_CTL_RGB2YCC                        BIT(1)
 # define VC4_HD_CSC_CTL_ENABLE                 BIT(0)
 
+#define VC4_HD_CSC_12_11                       0x044
+#define VC4_HD_CSC_14_13                       0x048
+#define VC4_HD_CSC_22_21                       0x04c
+#define VC4_HD_CSC_24_23                       0x050
+#define VC4_HD_CSC_32_31                       0x054
+#define VC4_HD_CSC_34_33                       0x058
+
 #define VC4_HD_FRAME_COUNT                     0x068
 
 /* HVS display list information. */
index 0f12418..08886a3 100644 (file)
@@ -45,6 +45,8 @@ struct vc4_rcl_setup {
 
        struct drm_gem_cma_object *rcl;
        u32 next_offset;
+
+       u32 next_write_bo_index;
 };
 
 static inline void rcl_u8(struct vc4_rcl_setup *setup, u8 val)
@@ -407,6 +409,8 @@ static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec,
        if (!*obj)
                return -EINVAL;
 
+       exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj;
+
        if (surf->offset & 0xf) {
                DRM_ERROR("MSAA write must be 16b aligned.\n");
                return -EINVAL;
@@ -417,7 +421,8 @@ static int vc4_rcl_msaa_surface_setup(struct vc4_exec_info *exec,
 
 static int vc4_rcl_surface_setup(struct vc4_exec_info *exec,
                                 struct drm_gem_cma_object **obj,
-                                struct drm_vc4_submit_rcl_surface *surf)
+                                struct drm_vc4_submit_rcl_surface *surf,
+                                bool is_write)
 {
        uint8_t tiling = VC4_GET_FIELD(surf->bits,
                                       VC4_LOADSTORE_TILE_BUFFER_TILING);
@@ -440,6 +445,9 @@ static int vc4_rcl_surface_setup(struct vc4_exec_info *exec,
        if (!*obj)
                return -EINVAL;
 
+       if (is_write)
+               exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj;
+
        if (surf->flags & VC4_SUBMIT_RCL_SURFACE_READ_IS_FULL_RES) {
                if (surf == &exec->args->zs_write) {
                        DRM_ERROR("general zs write may not be a full-res.\n");
@@ -542,6 +550,8 @@ vc4_rcl_render_config_surface_setup(struct vc4_exec_info *exec,
        if (!*obj)
                return -EINVAL;
 
+       exec->rcl_write_bo[exec->rcl_write_bo_count++] = *obj;
+
        if (tiling > VC4_TILING_FORMAT_LT) {
                DRM_ERROR("Bad tiling format\n");
                return -EINVAL;
@@ -599,15 +609,18 @@ int vc4_get_rcl(struct drm_device *dev, struct vc4_exec_info *exec)
        if (ret)
                return ret;
 
-       ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read);
+       ret = vc4_rcl_surface_setup(exec, &setup.color_read, &args->color_read,
+                                   false);
        if (ret)
                return ret;
 
-       ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read);
+       ret = vc4_rcl_surface_setup(exec, &setup.zs_read, &args->zs_read,
+                                   false);
        if (ret)
                return ret;
 
-       ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write);
+       ret = vc4_rcl_surface_setup(exec, &setup.zs_write, &args->zs_write,
+                                   true);
        if (ret)
                return ret;
 
index 9ce1d0a..26503e3 100644 (file)
@@ -267,6 +267,9 @@ validate_indexed_prim_list(VALIDATE_ARGS)
        if (!ib)
                return -EINVAL;
 
+       exec->bin_dep_seqno = max(exec->bin_dep_seqno,
+                                 to_vc4_bo(&ib->base)->write_seqno);
+
        if (offset > ib->base.size ||
            (ib->base.size - offset) / index_size < length) {
                DRM_ERROR("IB access overflow (%d + %d*%d > %zd)\n",
@@ -555,8 +558,7 @@ static bool
 reloc_tex(struct vc4_exec_info *exec,
          void *uniform_data_u,
          struct vc4_texture_sample_info *sample,
-         uint32_t texture_handle_index)
-
+         uint32_t texture_handle_index, bool is_cs)
 {
        struct drm_gem_cma_object *tex;
        uint32_t p0 = *(uint32_t *)(uniform_data_u + sample->p_offset[0]);
@@ -714,6 +716,11 @@ reloc_tex(struct vc4_exec_info *exec,
 
        *validated_p0 = tex->paddr + p0;
 
+       if (is_cs) {
+               exec->bin_dep_seqno = max(exec->bin_dep_seqno,
+                                         to_vc4_bo(&tex->base)->write_seqno);
+       }
+
        return true;
  fail:
        DRM_INFO("Texture p0 at %d: 0x%08x\n", sample->p_offset[0], p0);
@@ -835,7 +842,8 @@ validate_gl_shader_rec(struct drm_device *dev,
                        if (!reloc_tex(exec,
                                       uniform_data_u,
                                       &validated_shader->texture_samples[tex],
-                                      texture_handles_u[tex])) {
+                                      texture_handles_u[tex],
+                                      i == 2)) {
                                return -EINVAL;
                        }
                }
@@ -867,6 +875,9 @@ validate_gl_shader_rec(struct drm_device *dev,
                uint32_t stride = *(uint8_t *)(pkt_u + o + 5);
                uint32_t max_index;
 
+               exec->bin_dep_seqno = max(exec->bin_dep_seqno,
+                                         to_vc4_bo(&vbo->base)->write_seqno);
+
                if (state->addr & 0x8)
                        stride |= (*(uint32_t *)(pkt_u + 100 + i * 4)) & ~0xff;
 
index c15bafb..f36c147 100644 (file)
@@ -334,8 +334,8 @@ static int __init vgem_init(void)
        int ret;
 
        vgem_device = drm_dev_alloc(&vgem_driver, NULL);
-       if (!vgem_device) {
-               ret = -ENOMEM;
+       if (IS_ERR(vgem_device)) {
+               ret = PTR_ERR(vgem_device);
                goto out;
        }
 
index ed8aa8f..e5582ba 100644 (file)
@@ -72,7 +72,7 @@ static const struct file_operations via_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features =
-           DRIVER_USE_AGP | DRIVER_HAVE_IRQ |
+           DRIVER_USE_AGP | DRIVER_HAVE_IRQ | DRIVER_LEGACY |
            DRIVER_IRQ_SHARED,
        .load = via_driver_load,
        .unload = via_driver_unload,
index 4e192aa..7cf3678 100644 (file)
@@ -338,7 +338,8 @@ static void vgdev_atomic_commit_tail(struct drm_atomic_state *state)
 
        drm_atomic_helper_commit_modeset_disables(dev, state);
        drm_atomic_helper_commit_modeset_enables(dev, state);
-       drm_atomic_helper_commit_planes(dev, state, true);
+       drm_atomic_helper_commit_planes(dev, state,
+                                       DRM_PLANE_COMMIT_ACTIVE_ONLY);
 
        drm_atomic_helper_commit_hw_done(state);
 
index 7f0e93f..49e5996 100644 (file)
  */
 
 #include <linux/pci.h>
+#include <drm/drm_fb_helper.h>
 
 #include "virtgpu_drv.h"
 
+int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master)
+{
+       struct pci_dev *pdev = dev->pdev;
+
+       if (pdev) {
+               return drm_pci_set_busid(dev, master);
+       }
+       return 0;
+}
+
 static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
 {
        struct apertures_struct *ap;
@@ -42,7 +53,7 @@ static void virtio_pci_kick_out_firmware_fb(struct pci_dev *pci_dev)
        primary = pci_dev->resource[PCI_ROM_RESOURCE].flags
                & IORESOURCE_ROM_SHADOW;
 
-       remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
+       drm_fb_helper_remove_conflicting_framebuffers(ap, "virtiodrmfb", primary);
 
        kfree(ap);
 }
@@ -53,8 +64,8 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev)
        int ret;
 
        dev = drm_dev_alloc(driver, &vdev->dev);
-       if (!dev)
-               return -ENOMEM;
+       if (IS_ERR(dev))
+               return PTR_ERR(dev);
        dev->virtdev = vdev;
        vdev->priv = dev;
 
index c13f70c..5820b70 100644 (file)
@@ -117,6 +117,7 @@ static const struct file_operations virtio_gpu_driver_fops = {
 
 static struct drm_driver driver = {
        .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER | DRIVER_ATOMIC,
+       .set_busid = drm_virtio_set_busid,
        .load = virtio_gpu_driver_load,
        .unload = virtio_gpu_driver_unload,
        .open = virtio_gpu_driver_open,
index b18ef31..ae59080 100644 (file)
@@ -49,6 +49,7 @@
 #define DRIVER_PATCHLEVEL 1
 
 /* virtgpu_drm_bus.c */
+int drm_virtio_set_busid(struct drm_device *dev, struct drm_master *master);
 int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev);
 
 struct virtio_gpu_object {
@@ -75,6 +76,7 @@ typedef void (*virtio_gpu_resp_cb)(struct virtio_gpu_device *vgdev,
 struct virtio_gpu_fence_driver {
        atomic64_t       last_seq;
        uint64_t         sync_seq;
+       uint64_t         context;
        struct list_head fences;
        spinlock_t       lock;
 };
index cf44187..f3f70fa 100644 (file)
@@ -89,7 +89,7 @@ int virtio_gpu_fence_emit(struct virtio_gpu_device *vgdev,
        (*fence)->drv = drv;
        (*fence)->seq = ++drv->sync_seq;
        fence_init(&(*fence)->f, &virtio_fence_ops, &drv->lock,
-                  0, (*fence)->seq);
+                  drv->context, (*fence)->seq);
        fence_get(&(*fence)->f);
        list_add_tail(&(*fence)->node, &drv->fences);
        spin_unlock_irqrestore(&drv->lock, irq_flags);
index c046903..818478b 100644 (file)
@@ -89,10 +89,16 @@ static void virtio_gpu_unref_list(struct list_head *head)
        }
 }
 
-static int virtio_gpu_execbuffer(struct drm_device *dev,
-                                struct drm_virtgpu_execbuffer *exbuf,
+/*
+ * Usage of execbuffer:
+ * Relocations need to take into account the full VIRTIO_GPUDrawable size.
+ * However, the command as passed from user space must *not* contain the initial
+ * VIRTIO_GPUReleaseInfo struct (first XXX bytes)
+ */
+static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
                                 struct drm_file *drm_file)
 {
+       struct drm_virtgpu_execbuffer *exbuf = data;
        struct virtio_gpu_device *vgdev = dev->dev_private;
        struct virtio_gpu_fpriv *vfpriv = drm_file->driver_priv;
        struct drm_gem_object *gobj;
@@ -152,15 +158,10 @@ static int virtio_gpu_execbuffer(struct drm_device *dev,
        if (ret)
                goto out_free;
 
-       buf = kmalloc(exbuf->size, GFP_KERNEL);
-       if (!buf) {
-               ret = -ENOMEM;
-               goto out_unresv;
-       }
-       if (copy_from_user(buf, (void __user *)(uintptr_t)exbuf->command,
-                          exbuf->size)) {
-               kfree(buf);
-               ret = -EFAULT;
+       buf = memdup_user((void __user *)(uintptr_t)exbuf->command,
+                         exbuf->size);
+       if (IS_ERR(buf)) {
+               ret = PTR_ERR(buf);
                goto out_unresv;
        }
        virtio_gpu_cmd_submit(vgdev, buf, exbuf->size,
@@ -182,20 +183,6 @@ out_free:
        return ret;
 }
 
-/*
- * Usage of execbuffer:
- * Relocations need to take into account the full VIRTIO_GPUDrawable size.
- * However, the command as passed from user space must *not* contain the initial
- * VIRTIO_GPUReleaseInfo struct (first XXX bytes)
- */
-static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
-                                      struct drm_file *file_priv)
-{
-       struct drm_virtgpu_execbuffer *execbuffer = data;
-       return virtio_gpu_execbuffer(dev, execbuffer, file_priv);
-}
-
-
 static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
                                     struct drm_file *file_priv)
 {
index 4150873..036b0fb 100644 (file)
@@ -159,6 +159,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags)
        virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func);
        virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func);
 
+       vgdev->fence_drv.context = fence_context_alloc(1);
        spin_lock_init(&vgdev->fence_drv.lock);
        INIT_LIST_HEAD(&vgdev->fence_drv.fences);
        INIT_LIST_HEAD(&vgdev->cap_cache);
index 925ca25..ba28c0f 100644 (file)
@@ -76,7 +76,8 @@ static void virtio_gpu_primary_plane_update(struct drm_plane *plane,
                output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
        if (old_state->crtc)
                output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
-       WARN_ON(!output);
+       if (WARN_ON(!output))
+               return;
 
        if (plane->state->fb) {
                vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
@@ -129,7 +130,8 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane,
                output = drm_crtc_to_virtio_gpu_output(plane->state->crtc);
        if (old_state->crtc)
                output = drm_crtc_to_virtio_gpu_output(old_state->crtc);
-       WARN_ON(!output);
+       if (WARN_ON(!output))
+               return;
 
        if (plane->state->fb) {
                vgfb = to_virtio_gpu_framebuffer(plane->state->fb);
index b49445d..fb7b82a 100644 (file)
@@ -6,6 +6,7 @@ config DRM_VMWGFX
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
        select DRM_TTM
+       select FB
        # Only needed for the transitional use of drm_crtc_init - can be removed
        # again once vmwgfx sets up the primary plane itself.
        select DRM_KMS_HELPER
index 74304b0..070d750 100644 (file)
                        VMWGFX_NUM_GB_SURFACE +\
                        VMWGFX_NUM_GB_SCREEN_TARGET)
 
-#define VMW_PL_GMR TTM_PL_PRIV0
-#define VMW_PL_FLAG_GMR TTM_PL_FLAG_PRIV0
-#define VMW_PL_MOB TTM_PL_PRIV1
-#define VMW_PL_FLAG_MOB TTM_PL_FLAG_PRIV1
+#define VMW_PL_GMR (TTM_PL_PRIV + 0)
+#define VMW_PL_FLAG_GMR (TTM_PL_FLAG_PRIV << 0)
+#define VMW_PL_MOB (TTM_PL_PRIV + 1)
+#define VMW_PL_FLAG_MOB (TTM_PL_FLAG_PRIV << 1)
 
 #define VMW_RES_CONTEXT ttm_driver_type0
 #define VMW_RES_SURFACE ttm_driver_type1
index 63ccd98..23ec673 100644 (file)
@@ -376,9 +376,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
 
        drm_mode_crtc_set_gamma_size(crtc, 256);
 
-       drm_object_attach_property(&connector->base,
-                                  dev->mode_config.dirty_info_property,
-                                  1);
        drm_object_attach_property(&connector->base,
                                   dev_priv->hotplug_mode_update_property, 1);
        drm_object_attach_property(&connector->base,
@@ -421,10 +418,6 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv)
        if (ret != 0)
                goto err_free;
 
-       ret = drm_mode_create_dirty_info_property(dev);
-       if (ret != 0)
-               goto err_vblank_cleanup;
-
        vmw_kms_create_implicit_placement_property(dev_priv, true);
 
        if (dev_priv->capabilities & SVGA_CAP_MULTIMON)
@@ -439,8 +432,6 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv)
 
        return 0;
 
-err_vblank_cleanup:
-       drm_vblank_cleanup(dev);
 err_free:
        kfree(dev_priv->ldu_priv);
        dev_priv->ldu_priv = NULL;
index b74eae2..f423590 100644 (file)
@@ -537,9 +537,6 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
 
        drm_mode_crtc_set_gamma_size(crtc, 256);
 
-       drm_object_attach_property(&connector->base,
-                                  dev->mode_config.dirty_info_property,
-                                  1);
        drm_object_attach_property(&connector->base,
                                   dev_priv->hotplug_mode_update_property, 1);
        drm_object_attach_property(&connector->base,
@@ -574,10 +571,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv)
        if (unlikely(ret != 0))
                return ret;
 
-       ret = drm_mode_create_dirty_info_property(dev);
-       if (unlikely(ret != 0))
-               goto err_vblank_cleanup;
-
        vmw_kms_create_implicit_placement_property(dev_priv, false);
 
        for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i)
@@ -588,10 +581,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv)
        DRM_INFO("Screen Objects Display Unit initialized\n");
 
        return 0;
-
-err_vblank_cleanup:
-       drm_vblank_cleanup(dev);
-       return ret;
 }
 
 int vmw_kms_sou_close_display(struct vmw_private *dev_priv)
index 41932a7..94ad8d2 100644 (file)
@@ -1130,9 +1130,6 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit)
 
        drm_mode_crtc_set_gamma_size(crtc, 256);
 
-       drm_object_attach_property(&connector->base,
-                                  dev->mode_config.dirty_info_property,
-                                  1);
        drm_object_attach_property(&connector->base,
                                   dev_priv->hotplug_mode_update_property, 1);
        drm_object_attach_property(&connector->base,
@@ -1202,10 +1199,6 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv)
        if (unlikely(ret != 0))
                return ret;
 
-       ret = drm_mode_create_dirty_info_property(dev);
-       if (unlikely(ret != 0))
-               goto err_vblank_cleanup;
-
        dev_priv->active_display_unit = vmw_du_screen_target;
 
        vmw_kms_create_implicit_placement_property(dev_priv, false);
index 107ec23..5f96141 100644 (file)
@@ -1,4 +1,5 @@
 obj-$(CONFIG_IMX_IPUV3_CORE) += imx-ipu-v3.o
 
 imx-ipu-v3-objs := ipu-common.o ipu-cpmem.o ipu-csi.o ipu-dc.o ipu-di.o \
-               ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-smfc.o
+               ipu-dp.o ipu-dmfc.o ipu-ic.o ipu-image-convert.o \
+               ipu-smfc.o ipu-vdi.o
index 99dcacf..b9539f7 100644 (file)
@@ -45,6 +45,12 @@ static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
        writel(value, ipu->cm_reg + offset);
 }
 
+int ipu_get_num(struct ipu_soc *ipu)
+{
+       return ipu->id;
+}
+EXPORT_SYMBOL_GPL(ipu_get_num);
+
 void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
 {
        u32 val;
@@ -724,6 +730,137 @@ void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi)
 }
 EXPORT_SYMBOL_GPL(ipu_set_ic_src_mux);
 
+
+/* Frame Synchronization Unit Channel Linking */
+
+struct fsu_link_reg_info {
+       int chno;
+       u32 reg;
+       u32 mask;
+       u32 val;
+};
+
+struct fsu_link_info {
+       struct fsu_link_reg_info src;
+       struct fsu_link_reg_info sink;
+};
+
+static const struct fsu_link_info fsu_link_info[] = {
+       {
+               .src  = { IPUV3_CHANNEL_IC_PRP_ENC_MEM, IPU_FS_PROC_FLOW2,
+                         FS_PRP_ENC_DEST_SEL_MASK, FS_PRP_ENC_DEST_SEL_IRT_ENC },
+               .sink = { IPUV3_CHANNEL_MEM_ROT_ENC, IPU_FS_PROC_FLOW1,
+                         FS_PRPENC_ROT_SRC_SEL_MASK, FS_PRPENC_ROT_SRC_SEL_ENC },
+       }, {
+               .src =  { IPUV3_CHANNEL_IC_PRP_VF_MEM, IPU_FS_PROC_FLOW2,
+                         FS_PRPVF_DEST_SEL_MASK, FS_PRPVF_DEST_SEL_IRT_VF },
+               .sink = { IPUV3_CHANNEL_MEM_ROT_VF, IPU_FS_PROC_FLOW1,
+                         FS_PRPVF_ROT_SRC_SEL_MASK, FS_PRPVF_ROT_SRC_SEL_VF },
+       }, {
+               .src =  { IPUV3_CHANNEL_IC_PP_MEM, IPU_FS_PROC_FLOW2,
+                         FS_PP_DEST_SEL_MASK, FS_PP_DEST_SEL_IRT_PP },
+               .sink = { IPUV3_CHANNEL_MEM_ROT_PP, IPU_FS_PROC_FLOW1,
+                         FS_PP_ROT_SRC_SEL_MASK, FS_PP_ROT_SRC_SEL_PP },
+       }, {
+               .src =  { IPUV3_CHANNEL_CSI_DIRECT, 0 },
+               .sink = { IPUV3_CHANNEL_CSI_VDI_PREV, IPU_FS_PROC_FLOW1,
+                         FS_VDI_SRC_SEL_MASK, FS_VDI_SRC_SEL_CSI_DIRECT },
+       },
+};
+
+static const struct fsu_link_info *find_fsu_link_info(int src, int sink)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(fsu_link_info); i++) {
+               if (src == fsu_link_info[i].src.chno &&
+                   sink == fsu_link_info[i].sink.chno)
+                       return &fsu_link_info[i];
+       }
+
+       return NULL;
+}
+
+/*
+ * Links a source channel to a sink channel in the FSU.
+ */
+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch)
+{
+       const struct fsu_link_info *link;
+       u32 src_reg, sink_reg;
+       unsigned long flags;
+
+       link = find_fsu_link_info(src_ch, sink_ch);
+       if (!link)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->lock, flags);
+
+       if (link->src.mask) {
+               src_reg = ipu_cm_read(ipu, link->src.reg);
+               src_reg &= ~link->src.mask;
+               src_reg |= link->src.val;
+               ipu_cm_write(ipu, src_reg, link->src.reg);
+       }
+
+       if (link->sink.mask) {
+               sink_reg = ipu_cm_read(ipu, link->sink.reg);
+               sink_reg &= ~link->sink.mask;
+               sink_reg |= link->sink.val;
+               ipu_cm_write(ipu, sink_reg, link->sink.reg);
+       }
+
+       spin_unlock_irqrestore(&ipu->lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_fsu_link);
+
+/*
+ * Unlinks source and sink channels in the FSU.
+ */
+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch)
+{
+       const struct fsu_link_info *link;
+       u32 src_reg, sink_reg;
+       unsigned long flags;
+
+       link = find_fsu_link_info(src_ch, sink_ch);
+       if (!link)
+               return -EINVAL;
+
+       spin_lock_irqsave(&ipu->lock, flags);
+
+       if (link->src.mask) {
+               src_reg = ipu_cm_read(ipu, link->src.reg);
+               src_reg &= ~link->src.mask;
+               ipu_cm_write(ipu, src_reg, link->src.reg);
+       }
+
+       if (link->sink.mask) {
+               sink_reg = ipu_cm_read(ipu, link->sink.reg);
+               sink_reg &= ~link->sink.mask;
+               ipu_cm_write(ipu, sink_reg, link->sink.reg);
+       }
+
+       spin_unlock_irqrestore(&ipu->lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_fsu_unlink);
+
+/* Link IDMAC channels in the FSU */
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+       return ipu_fsu_link(src->ipu, src->num, sink->num);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_link);
+
+/* Unlink IDMAC channels in the FSU */
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink)
+{
+       return ipu_fsu_unlink(src->ipu, src->num, sink->num);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_unlink);
+
 struct ipu_devtype {
        const char *name;
        unsigned long cm_ofs;
@@ -833,6 +970,20 @@ static int ipu_submodules_init(struct ipu_soc *ipu,
                goto err_ic;
        }
 
+       ret = ipu_vdi_init(ipu, dev, ipu_base + devtype->vdi_ofs,
+                          IPU_CONF_VDI_EN | IPU_CONF_ISP_EN |
+                          IPU_CONF_IC_INPUT);
+       if (ret) {
+               unit = "vdi";
+               goto err_vdi;
+       }
+
+       ret = ipu_image_convert_init(ipu, dev);
+       if (ret) {
+               unit = "image_convert";
+               goto err_image_convert;
+       }
+
        ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
                          IPU_CONF_DI0_EN, ipu_clk);
        if (ret) {
@@ -887,6 +1038,10 @@ err_dc:
 err_di_1:
        ipu_di_exit(ipu, 0);
 err_di_0:
+       ipu_image_convert_exit(ipu);
+err_image_convert:
+       ipu_vdi_exit(ipu);
+err_vdi:
        ipu_ic_exit(ipu);
 err_ic:
        ipu_csi_exit(ipu, 1);
@@ -971,6 +1126,8 @@ static void ipu_submodules_exit(struct ipu_soc *ipu)
        ipu_dc_exit(ipu);
        ipu_di_exit(ipu, 1);
        ipu_di_exit(ipu, 0);
+       ipu_image_convert_exit(ipu);
+       ipu_vdi_exit(ipu);
        ipu_ic_exit(ipu);
        ipu_csi_exit(ipu, 1);
        ipu_csi_exit(ipu, 0);
@@ -1004,14 +1161,14 @@ static struct ipu_platform_reg client_reg[] = {
                        .dma[0] = IPUV3_CHANNEL_CSI0,
                        .dma[1] = -EINVAL,
                },
-               .name = "imx-ipuv3-camera",
+               .name = "imx-ipuv3-csi",
        }, {
                .pdata = {
                        .csi = 1,
                        .dma[0] = IPUV3_CHANNEL_CSI1,
                        .dma[1] = -EINVAL,
                },
-               .name = "imx-ipuv3-camera",
+               .name = "imx-ipuv3-csi",
        }, {
                .pdata = {
                        .di = 0,
@@ -1207,15 +1364,16 @@ EXPORT_SYMBOL_GPL(ipu_dump);
 
 static int ipu_probe(struct platform_device *pdev)
 {
-       const struct of_device_id *of_id =
-                       of_match_device(imx_ipu_dt_ids, &pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
        struct ipu_soc *ipu;
        struct resource *res;
        unsigned long ipu_base;
        int i, ret, irq_sync, irq_err;
        const struct ipu_devtype *devtype;
 
-       devtype = of_id->data;
+       devtype = of_device_get_match_data(&pdev->dev);
+       if (!devtype)
+               return -EINVAL;
 
        irq_sync = platform_get_irq(pdev, 0);
        irq_err = platform_get_irq(pdev, 1);
@@ -1237,6 +1395,7 @@ static int ipu_probe(struct platform_device *pdev)
                ipu->channel[i].ipu = ipu;
        ipu->devtype = devtype;
        ipu->ipu_type = devtype->type;
+       ipu->id = of_alias_get_id(np, "ipu");
 
        spin_lock_init(&ipu->lock);
        mutex_init(&ipu->channel_lock);
index 6494a4d..fcb7dc8 100644 (file)
@@ -253,6 +253,13 @@ void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_buffer);
 
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off)
+{
+       ipu_ch_param_write_field(ch, IPU_FIELD_UBO, u_off / 8);
+       ipu_ch_param_write_field(ch, IPU_FIELD_VBO, v_off / 8);
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_uv_offset);
+
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride)
 {
        ipu_ch_param_write_field(ch, IPU_FIELD_SO, 1);
@@ -268,6 +275,12 @@ void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id)
 }
 EXPORT_SYMBOL_GPL(ipu_cpmem_set_axi_id);
 
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch)
+{
+       return ipu_ch_param_read_field(ch, IPU_FIELD_NPB) + 1;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_get_burstsize);
+
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize)
 {
        ipu_ch_param_write_field(ch, IPU_FIELD_NPB, burstsize - 1);
index 06631ac..d6e5ded 100644 (file)
@@ -258,12 +258,8 @@ static int mbus_code_to_bus_cfg(struct ipu_csi_bus_config *cfg, u32 mbus_code)
                cfg->data_width = IPU_CSI_DATA_WIDTH_8;
                break;
        case MEDIA_BUS_FMT_UYVY8_1X16:
-               cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_UYVY;
-               cfg->mipi_dt = MIPI_DT_YUV422;
-               cfg->data_width = IPU_CSI_DATA_WIDTH_16;
-               break;
        case MEDIA_BUS_FMT_YUYV8_1X16:
-               cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_YUV422_YUYV;
+               cfg->data_fmt = CSI_SENS_CONF_DATA_FMT_BAYER;
                cfg->mipi_dt = MIPI_DT_YUV422;
                cfg->data_width = IPU_CSI_DATA_WIDTH_16;
                break;
@@ -365,10 +361,14 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 {
        struct ipu_csi_bus_config cfg;
        unsigned long flags;
-       u32 data = 0;
+       u32 width, height, data = 0;
 
        fill_csi_bus_cfg(&cfg, mbus_cfg, mbus_fmt);
 
+       /* set default sensor frame width and height */
+       width = mbus_fmt->width;
+       height = mbus_fmt->height;
+
        /* Set the CSI_SENS_CONF register remaining fields */
        data |= cfg.data_width << CSI_SENS_CONF_DATA_WIDTH_SHIFT |
                cfg.data_fmt << CSI_SENS_CONF_DATA_FMT_SHIFT |
@@ -386,11 +386,6 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
 
        ipu_csi_write(csi, data, CSI_SENS_CONF);
 
-       /* Setup sensor frame size */
-       ipu_csi_write(csi,
-                     (mbus_fmt->width - 1) | ((mbus_fmt->height - 1) << 16),
-                     CSI_SENS_FRM_SIZE);
-
        /* Set CCIR registers */
 
        switch (cfg.clk_mode) {
@@ -408,11 +403,12 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
                         * Field1BlankEnd = 0x7, Field1BlankStart = 0x3,
                         * Field1ActiveEnd = 0x5, Field1ActiveStart = 0x1
                         */
+                       height = 625; /* framelines for PAL */
+
                        ipu_csi_write(csi, 0x40596 | CSI_CCIR_ERR_DET_EN,
                                          CSI_CCIR_CODE_1);
                        ipu_csi_write(csi, 0xD07DF, CSI_CCIR_CODE_2);
                        ipu_csi_write(csi, 0xFF0000, CSI_CCIR_CODE_3);
-
                } else if (mbus_fmt->width == 720 && mbus_fmt->height == 480) {
                        /*
                         * NTSC case
@@ -422,6 +418,8 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
                         * Field1BlankEnd = 0x6, Field1BlankStart = 0x2,
                         * Field1ActiveEnd = 0x4, Field1ActiveStart = 0
                         */
+                       height = 525; /* framelines for NTSC */
+
                        ipu_csi_write(csi, 0xD07DF | CSI_CCIR_ERR_DET_EN,
                                          CSI_CCIR_CODE_1);
                        ipu_csi_write(csi, 0x40596, CSI_CCIR_CODE_2);
@@ -447,6 +445,10 @@ int ipu_csi_init_interface(struct ipu_csi *csi,
                break;
        }
 
+       /* Setup sensor frame size */
+       ipu_csi_write(csi, (width - 1) | ((height - 1) << 16),
+                     CSI_SENS_FRM_SIZE);
+
        dev_dbg(csi->ipu->dev, "CSI_SENS_CONF = 0x%08X\n",
                ipu_csi_read(csi, CSI_SENS_CONF));
        dev_dbg(csi->ipu->dev, "CSI_ACT_FRM_SIZE = 0x%08X\n",
index 42705bb..a40f211 100644 (file)
@@ -123,20 +123,6 @@ int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
 }
 EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
 
-static void ipu_dmfc_wait_fifos(struct ipu_dmfc_priv *priv)
-{
-       unsigned long timeout = jiffies + msecs_to_jiffies(1000);
-
-       while ((readl(priv->base + DMFC_STAT) & 0x02fff000) != 0x02fff000) {
-               if (time_after(jiffies, timeout)) {
-                       dev_warn(priv->dev,
-                                "Timeout waiting for DMFC FIFOs to clear\n");
-                       break;
-               }
-               cpu_relax();
-       }
-}
-
 void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
 {
        struct ipu_dmfc_priv *priv = dmfc->priv;
@@ -145,10 +131,8 @@ void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
 
        priv->use_count--;
 
-       if (!priv->use_count) {
-               ipu_dmfc_wait_fifos(priv);
+       if (!priv->use_count)
                ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
-       }
 
        if (priv->use_count < 0)
                priv->use_count = 0;
index 1dcb96c..321eb98 100644 (file)
@@ -160,6 +160,7 @@ struct ipu_ic_priv {
        spinlock_t lock;
        struct ipu_soc *ipu;
        int use_count;
+       int irt_use_count;
        struct ipu_ic task[IC_NUM_TASKS];
 };
 
@@ -379,8 +380,6 @@ void ipu_ic_task_disable(struct ipu_ic *ic)
 
        ipu_ic_write(ic, ic_conf, IC_CONF);
 
-       ic->rotation = ic->graphics = false;
-
        spin_unlock_irqrestore(&priv->lock, flags);
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_disable);
@@ -620,7 +619,7 @@ int ipu_ic_task_idma_init(struct ipu_ic *ic, struct ipuv3_channel *channel,
        ipu_ic_write(ic, ic_idmac_2, IC_IDMAC_2);
        ipu_ic_write(ic, ic_idmac_3, IC_IDMAC_3);
 
-       if (rot >= IPU_ROTATE_90_RIGHT)
+       if (ipu_rot_mode_is_irt(rot))
                ic->rotation = true;
 
 unlock:
@@ -629,22 +628,41 @@ unlock:
 }
 EXPORT_SYMBOL_GPL(ipu_ic_task_idma_init);
 
+static void ipu_irt_enable(struct ipu_ic *ic)
+{
+       struct ipu_ic_priv *priv = ic->priv;
+
+       if (!priv->irt_use_count)
+               ipu_module_enable(priv->ipu, IPU_CONF_ROT_EN);
+
+       priv->irt_use_count++;
+}
+
+static void ipu_irt_disable(struct ipu_ic *ic)
+{
+       struct ipu_ic_priv *priv = ic->priv;
+
+       if (priv->irt_use_count) {
+               if (!--priv->irt_use_count)
+                       ipu_module_disable(priv->ipu, IPU_CONF_ROT_EN);
+       }
+}
+
 int ipu_ic_enable(struct ipu_ic *ic)
 {
        struct ipu_ic_priv *priv = ic->priv;
        unsigned long flags;
-       u32 module = IPU_CONF_IC_EN;
 
        spin_lock_irqsave(&priv->lock, flags);
 
-       if (ic->rotation)
-               module |= IPU_CONF_ROT_EN;
-
        if (!priv->use_count)
-               ipu_module_enable(priv->ipu, module);
+               ipu_module_enable(priv->ipu, IPU_CONF_IC_EN);
 
        priv->use_count++;
 
+       if (ic->rotation)
+               ipu_irt_enable(ic);
+
        spin_unlock_irqrestore(&priv->lock, flags);
 
        return 0;
@@ -655,18 +673,22 @@ int ipu_ic_disable(struct ipu_ic *ic)
 {
        struct ipu_ic_priv *priv = ic->priv;
        unsigned long flags;
-       u32 module = IPU_CONF_IC_EN | IPU_CONF_ROT_EN;
 
        spin_lock_irqsave(&priv->lock, flags);
 
        priv->use_count--;
 
        if (!priv->use_count)
-               ipu_module_disable(priv->ipu, module);
+               ipu_module_disable(priv->ipu, IPU_CONF_IC_EN);
 
        if (priv->use_count < 0)
                priv->use_count = 0;
 
+       if (ic->rotation)
+               ipu_irt_disable(ic);
+
+       ic->rotation = ic->graphics = false;
+
        spin_unlock_irqrestore(&priv->lock, flags);
 
        return 0;
diff --git a/drivers/gpu/ipu-v3/ipu-image-convert.c b/drivers/gpu/ipu-v3/ipu-image-convert.c
new file mode 100644 (file)
index 0000000..2ba7d43
--- /dev/null
@@ -0,0 +1,1709 @@
+/*
+ * Copyright (C) 2012-2016 Mentor Graphics Inc.
+ *
+ * Queued image conversion support, with tiling and rotation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <video/imx-ipu-image-convert.h>
+#include "ipu-prv.h"
+
+/*
+ * The IC Resizer has a restriction that the output frame from the
+ * resizer must be 1024 or less in both width (pixels) and height
+ * (lines).
+ *
+ * The image converter attempts to split up a conversion when
+ * the desired output (converted) frame resolution exceeds the
+ * IC resizer limit of 1024 in either dimension.
+ *
+ * If either dimension of the output frame exceeds the limit, the
+ * dimension is split into 1, 2, or 4 equal stripes, for a maximum
+ * of 4*4 or 16 tiles. A conversion is then carried out for each
+ * tile (but taking care to pass the full frame stride length to
+ * the DMA channel's parameter memory!). IDMA double-buffering is used
+ * to convert each tile back-to-back when possible (see note below
+ * when double_buffering boolean is set).
+ *
+ * Note that the input frame must be split up into the same number
+ * of tiles as the output frame.
+ *
+ * FIXME: at this point there is no attempt to deal with visible seams
+ * at the tile boundaries when upscaling. The seams are caused by a reset
+ * of the bilinear upscale interpolation when starting a new tile. The
+ * seams are barely visible for small upscale factors, but become
+ * increasingly visible as the upscale factor gets larger, since more
+ * interpolated pixels get thrown out at the tile boundaries. A possilble
+ * fix might be to overlap tiles of different sizes, but this must be done
+ * while also maintaining the IDMAC dma buffer address alignment and 8x8 IRT
+ * alignment restrictions of each tile.
+ */
+
+#define MAX_STRIPES_W    4
+#define MAX_STRIPES_H    4
+#define MAX_TILES (MAX_STRIPES_W * MAX_STRIPES_H)
+
+#define MIN_W     16
+#define MIN_H     8
+#define MAX_W     4096
+#define MAX_H     4096
+
+enum ipu_image_convert_type {
+       IMAGE_CONVERT_IN = 0,
+       IMAGE_CONVERT_OUT,
+};
+
+struct ipu_image_convert_dma_buf {
+       void          *virt;
+       dma_addr_t    phys;
+       unsigned long len;
+};
+
+struct ipu_image_convert_dma_chan {
+       int in;
+       int out;
+       int rot_in;
+       int rot_out;
+       int vdi_in_p;
+       int vdi_in;
+       int vdi_in_n;
+};
+
+/* dimensions of one tile */
+struct ipu_image_tile {
+       u32 width;
+       u32 height;
+       /* size and strides are in bytes */
+       u32 size;
+       u32 stride;
+       u32 rot_stride;
+       /* start Y or packed offset of this tile */
+       u32 offset;
+       /* offset from start to tile in U plane, for planar formats */
+       u32 u_off;
+       /* offset from start to tile in V plane, for planar formats */
+       u32 v_off;
+};
+
+struct ipu_image_convert_image {
+       struct ipu_image base;
+       enum ipu_image_convert_type type;
+
+       const struct ipu_image_pixfmt *fmt;
+       unsigned int stride;
+
+       /* # of rows (horizontal stripes) if dest height is > 1024 */
+       unsigned int num_rows;
+       /* # of columns (vertical stripes) if dest width is > 1024 */
+       unsigned int num_cols;
+
+       struct ipu_image_tile tile[MAX_TILES];
+};
+
+struct ipu_image_pixfmt {
+       u32     fourcc;        /* V4L2 fourcc */
+       int     bpp;           /* total bpp */
+       int     uv_width_dec;  /* decimation in width for U/V planes */
+       int     uv_height_dec; /* decimation in height for U/V planes */
+       bool    planar;        /* planar format */
+       bool    uv_swapped;    /* U and V planes are swapped */
+       bool    uv_packed;     /* partial planar (U and V in same plane) */
+};
+
+struct ipu_image_convert_ctx;
+struct ipu_image_convert_chan;
+struct ipu_image_convert_priv;
+
+struct ipu_image_convert_ctx {
+       struct ipu_image_convert_chan *chan;
+
+       ipu_image_convert_cb_t complete;
+       void *complete_context;
+
+       /* Source/destination image data and rotation mode */
+       struct ipu_image_convert_image in;
+       struct ipu_image_convert_image out;
+       enum ipu_rotate_mode rot_mode;
+
+       /* intermediate buffer for rotation */
+       struct ipu_image_convert_dma_buf rot_intermediate[2];
+
+       /* current buffer number for double buffering */
+       int cur_buf_num;
+
+       bool aborting;
+       struct completion aborted;
+
+       /* can we use double-buffering for this conversion operation? */
+       bool double_buffering;
+       /* num_rows * num_cols */
+       unsigned int num_tiles;
+       /* next tile to process */
+       unsigned int next_tile;
+       /* where to place converted tile in dest image */
+       unsigned int out_tile_map[MAX_TILES];
+
+       struct list_head list;
+};
+
+struct ipu_image_convert_chan {
+       struct ipu_image_convert_priv *priv;
+
+       enum ipu_ic_task ic_task;
+       const struct ipu_image_convert_dma_chan *dma_ch;
+
+       struct ipu_ic *ic;
+       struct ipuv3_channel *in_chan;
+       struct ipuv3_channel *out_chan;
+       struct ipuv3_channel *rotation_in_chan;
+       struct ipuv3_channel *rotation_out_chan;
+
+       /* the IPU end-of-frame irqs */
+       int out_eof_irq;
+       int rot_out_eof_irq;
+
+       spinlock_t irqlock;
+
+       /* list of convert contexts */
+       struct list_head ctx_list;
+       /* queue of conversion runs */
+       struct list_head pending_q;
+       /* queue of completed runs */
+       struct list_head done_q;
+
+       /* the current conversion run */
+       struct ipu_image_convert_run *current_run;
+};
+
+struct ipu_image_convert_priv {
+       struct ipu_image_convert_chan chan[IC_NUM_TASKS];
+       struct ipu_soc *ipu;
+};
+
+static const struct ipu_image_convert_dma_chan
+image_convert_dma_chan[IC_NUM_TASKS] = {
+       [IC_TASK_VIEWFINDER] = {
+               .in = IPUV3_CHANNEL_MEM_IC_PRP_VF,
+               .out = IPUV3_CHANNEL_IC_PRP_VF_MEM,
+               .rot_in = IPUV3_CHANNEL_MEM_ROT_VF,
+               .rot_out = IPUV3_CHANNEL_ROT_VF_MEM,
+               .vdi_in_p = IPUV3_CHANNEL_MEM_VDI_PREV,
+               .vdi_in = IPUV3_CHANNEL_MEM_VDI_CUR,
+               .vdi_in_n = IPUV3_CHANNEL_MEM_VDI_NEXT,
+       },
+       [IC_TASK_POST_PROCESSOR] = {
+               .in = IPUV3_CHANNEL_MEM_IC_PP,
+               .out = IPUV3_CHANNEL_IC_PP_MEM,
+               .rot_in = IPUV3_CHANNEL_MEM_ROT_PP,
+               .rot_out = IPUV3_CHANNEL_ROT_PP_MEM,
+       },
+};
+
+static const struct ipu_image_pixfmt image_convert_formats[] = {
+       {
+               .fourcc = V4L2_PIX_FMT_RGB565,
+               .bpp    = 16,
+       }, {
+               .fourcc = V4L2_PIX_FMT_RGB24,
+               .bpp    = 24,
+       }, {
+               .fourcc = V4L2_PIX_FMT_BGR24,
+               .bpp    = 24,
+       }, {
+               .fourcc = V4L2_PIX_FMT_RGB32,
+               .bpp    = 32,
+       }, {
+               .fourcc = V4L2_PIX_FMT_BGR32,
+               .bpp    = 32,
+       }, {
+               .fourcc = V4L2_PIX_FMT_YUYV,
+               .bpp    = 16,
+               .uv_width_dec = 2,
+               .uv_height_dec = 1,
+       }, {
+               .fourcc = V4L2_PIX_FMT_UYVY,
+               .bpp    = 16,
+               .uv_width_dec = 2,
+               .uv_height_dec = 1,
+       }, {
+               .fourcc = V4L2_PIX_FMT_YUV420,
+               .bpp    = 12,
+               .planar = true,
+               .uv_width_dec = 2,
+               .uv_height_dec = 2,
+       }, {
+               .fourcc = V4L2_PIX_FMT_YVU420,
+               .bpp    = 12,
+               .planar = true,
+               .uv_width_dec = 2,
+               .uv_height_dec = 2,
+               .uv_swapped = true,
+       }, {
+               .fourcc = V4L2_PIX_FMT_NV12,
+               .bpp    = 12,
+               .planar = true,
+               .uv_width_dec = 2,
+               .uv_height_dec = 2,
+               .uv_packed = true,
+       }, {
+               .fourcc = V4L2_PIX_FMT_YUV422P,
+               .bpp    = 16,
+               .planar = true,
+               .uv_width_dec = 2,
+               .uv_height_dec = 1,
+       }, {
+               .fourcc = V4L2_PIX_FMT_NV16,
+               .bpp    = 16,
+               .planar = true,
+               .uv_width_dec = 2,
+               .uv_height_dec = 1,
+               .uv_packed = true,
+       },
+};
+
+static const struct ipu_image_pixfmt *get_format(u32 fourcc)
+{
+       const struct ipu_image_pixfmt *ret = NULL;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(image_convert_formats); i++) {
+               if (image_convert_formats[i].fourcc == fourcc) {
+                       ret = &image_convert_formats[i];
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+static void dump_format(struct ipu_image_convert_ctx *ctx,
+                       struct ipu_image_convert_image *ic_image)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+
+       dev_dbg(priv->ipu->dev,
+               "task %u: ctx %p: %s format: %dx%d (%dx%d tiles of size %dx%d), %c%c%c%c\n",
+               chan->ic_task, ctx,
+               ic_image->type == IMAGE_CONVERT_OUT ? "Output" : "Input",
+               ic_image->base.pix.width, ic_image->base.pix.height,
+               ic_image->num_cols, ic_image->num_rows,
+               ic_image->tile[0].width, ic_image->tile[0].height,
+               ic_image->fmt->fourcc & 0xff,
+               (ic_image->fmt->fourcc >> 8) & 0xff,
+               (ic_image->fmt->fourcc >> 16) & 0xff,
+               (ic_image->fmt->fourcc >> 24) & 0xff);
+}
+
+int ipu_image_convert_enum_format(int index, u32 *fourcc)
+{
+       const struct ipu_image_pixfmt *fmt;
+
+       if (index >= (int)ARRAY_SIZE(image_convert_formats))
+               return -EINVAL;
+
+       /* Format found */
+       fmt = &image_convert_formats[index];
+       *fourcc = fmt->fourcc;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_enum_format);
+
+static void free_dma_buf(struct ipu_image_convert_priv *priv,
+                        struct ipu_image_convert_dma_buf *buf)
+{
+       if (buf->virt)
+               dma_free_coherent(priv->ipu->dev,
+                                 buf->len, buf->virt, buf->phys);
+       buf->virt = NULL;
+       buf->phys = 0;
+}
+
+static int alloc_dma_buf(struct ipu_image_convert_priv *priv,
+                        struct ipu_image_convert_dma_buf *buf,
+                        int size)
+{
+       buf->len = PAGE_ALIGN(size);
+       buf->virt = dma_alloc_coherent(priv->ipu->dev, buf->len, &buf->phys,
+                                      GFP_DMA | GFP_KERNEL);
+       if (!buf->virt) {
+               dev_err(priv->ipu->dev, "failed to alloc dma buffer\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static inline int num_stripes(int dim)
+{
+       if (dim <= 1024)
+               return 1;
+       else if (dim <= 2048)
+               return 2;
+       else
+               return 4;
+}
+
+static void calc_tile_dimensions(struct ipu_image_convert_ctx *ctx,
+                                struct ipu_image_convert_image *image)
+{
+       int i;
+
+       for (i = 0; i < ctx->num_tiles; i++) {
+               struct ipu_image_tile *tile = &image->tile[i];
+
+               tile->height = image->base.pix.height / image->num_rows;
+               tile->width = image->base.pix.width / image->num_cols;
+               tile->size = ((tile->height * image->fmt->bpp) >> 3) *
+                       tile->width;
+
+               if (image->fmt->planar) {
+                       tile->stride = tile->width;
+                       tile->rot_stride = tile->height;
+               } else {
+                       tile->stride =
+                               (image->fmt->bpp * tile->width) >> 3;
+                       tile->rot_stride =
+                               (image->fmt->bpp * tile->height) >> 3;
+               }
+       }
+}
+
+/*
+ * Use the rotation transformation to find the tile coordinates
+ * (row, col) of a tile in the destination frame that corresponds
+ * to the given tile coordinates of a source frame. The destination
+ * coordinate is then converted to a tile index.
+ */
+static int transform_tile_index(struct ipu_image_convert_ctx *ctx,
+                               int src_row, int src_col)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_image *s_image = &ctx->in;
+       struct ipu_image_convert_image *d_image = &ctx->out;
+       int dst_row, dst_col;
+
+       /* with no rotation it's a 1:1 mapping */
+       if (ctx->rot_mode == IPU_ROTATE_NONE)
+               return src_row * s_image->num_cols + src_col;
+
+       /*
+        * before doing the transform, first we have to translate
+        * source row,col for an origin in the center of s_image
+        */
+       src_row = src_row * 2 - (s_image->num_rows - 1);
+       src_col = src_col * 2 - (s_image->num_cols - 1);
+
+       /* do the rotation transform */
+       if (ctx->rot_mode & IPU_ROT_BIT_90) {
+               dst_col = -src_row;
+               dst_row = src_col;
+       } else {
+               dst_col = src_col;
+               dst_row = src_row;
+       }
+
+       /* apply flip */
+       if (ctx->rot_mode & IPU_ROT_BIT_HFLIP)
+               dst_col = -dst_col;
+       if (ctx->rot_mode & IPU_ROT_BIT_VFLIP)
+               dst_row = -dst_row;
+
+       dev_dbg(priv->ipu->dev, "task %u: ctx %p: [%d,%d] --> [%d,%d]\n",
+               chan->ic_task, ctx, src_col, src_row, dst_col, dst_row);
+
+       /*
+        * finally translate dest row,col using an origin in upper
+        * left of d_image
+        */
+       dst_row += d_image->num_rows - 1;
+       dst_col += d_image->num_cols - 1;
+       dst_row /= 2;
+       dst_col /= 2;
+
+       return dst_row * d_image->num_cols + dst_col;
+}
+
+/*
+ * Fill the out_tile_map[] with transformed destination tile indeces.
+ */
+static void calc_out_tile_map(struct ipu_image_convert_ctx *ctx)
+{
+       struct ipu_image_convert_image *s_image = &ctx->in;
+       unsigned int row, col, tile = 0;
+
+       for (row = 0; row < s_image->num_rows; row++) {
+               for (col = 0; col < s_image->num_cols; col++) {
+                       ctx->out_tile_map[tile] =
+                               transform_tile_index(ctx, row, col);
+                       tile++;
+               }
+       }
+}
+
+static void calc_tile_offsets_planar(struct ipu_image_convert_ctx *ctx,
+                                    struct ipu_image_convert_image *image)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       const struct ipu_image_pixfmt *fmt = image->fmt;
+       unsigned int row, col, tile = 0;
+       u32 H, w, h, y_stride, uv_stride;
+       u32 uv_row_off, uv_col_off, uv_off, u_off, v_off, tmp;
+       u32 y_row_off, y_col_off, y_off;
+       u32 y_size, uv_size;
+
+       /* setup some convenience vars */
+       H = image->base.pix.height;
+
+       y_stride = image->stride;
+       uv_stride = y_stride / fmt->uv_width_dec;
+       if (fmt->uv_packed)
+               uv_stride *= 2;
+
+       y_size = H * y_stride;
+       uv_size = y_size / (fmt->uv_width_dec * fmt->uv_height_dec);
+
+       for (row = 0; row < image->num_rows; row++) {
+               w = image->tile[tile].width;
+               h = image->tile[tile].height;
+               y_row_off = row * h * y_stride;
+               uv_row_off = (row * h * uv_stride) / fmt->uv_height_dec;
+
+               for (col = 0; col < image->num_cols; col++) {
+                       y_col_off = col * w;
+                       uv_col_off = y_col_off / fmt->uv_width_dec;
+                       if (fmt->uv_packed)
+                               uv_col_off *= 2;
+
+                       y_off = y_row_off + y_col_off;
+                       uv_off = uv_row_off + uv_col_off;
+
+                       u_off = y_size - y_off + uv_off;
+                       v_off = (fmt->uv_packed) ? 0 : u_off + uv_size;
+                       if (fmt->uv_swapped) {
+                               tmp = u_off;
+                               u_off = v_off;
+                               v_off = tmp;
+                       }
+
+                       image->tile[tile].offset = y_off;
+                       image->tile[tile].u_off = u_off;
+                       image->tile[tile++].v_off = v_off;
+
+                       dev_dbg(priv->ipu->dev,
+                               "task %u: ctx %p: %s@[%d,%d]: y_off %08x, u_off %08x, v_off %08x\n",
+                               chan->ic_task, ctx,
+                               image->type == IMAGE_CONVERT_IN ?
+                               "Input" : "Output", row, col,
+                               y_off, u_off, v_off);
+               }
+       }
+}
+
+static void calc_tile_offsets_packed(struct ipu_image_convert_ctx *ctx,
+                                    struct ipu_image_convert_image *image)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       const struct ipu_image_pixfmt *fmt = image->fmt;
+       unsigned int row, col, tile = 0;
+       u32 w, h, bpp, stride;
+       u32 row_off, col_off;
+
+       /* setup some convenience vars */
+       stride = image->stride;
+       bpp = fmt->bpp;
+
+       for (row = 0; row < image->num_rows; row++) {
+               w = image->tile[tile].width;
+               h = image->tile[tile].height;
+               row_off = row * h * stride;
+
+               for (col = 0; col < image->num_cols; col++) {
+                       col_off = (col * w * bpp) >> 3;
+
+                       image->tile[tile].offset = row_off + col_off;
+                       image->tile[tile].u_off = 0;
+                       image->tile[tile++].v_off = 0;
+
+                       dev_dbg(priv->ipu->dev,
+                               "task %u: ctx %p: %s@[%d,%d]: phys %08x\n",
+                               chan->ic_task, ctx,
+                               image->type == IMAGE_CONVERT_IN ?
+                               "Input" : "Output", row, col,
+                               row_off + col_off);
+               }
+       }
+}
+
+static void calc_tile_offsets(struct ipu_image_convert_ctx *ctx,
+                             struct ipu_image_convert_image *image)
+{
+       if (image->fmt->planar)
+               calc_tile_offsets_planar(ctx, image);
+       else
+               calc_tile_offsets_packed(ctx, image);
+}
+
+/*
+ * return the number of runs in given queue (pending_q or done_q)
+ * for this context. hold irqlock when calling.
+ */
+static int get_run_count(struct ipu_image_convert_ctx *ctx,
+                        struct list_head *q)
+{
+       struct ipu_image_convert_run *run;
+       int count = 0;
+
+       lockdep_assert_held(&ctx->chan->irqlock);
+
+       list_for_each_entry(run, q, list) {
+               if (run->ctx == ctx)
+                       count++;
+       }
+
+       return count;
+}
+
+static void convert_stop(struct ipu_image_convert_run *run)
+{
+       struct ipu_image_convert_ctx *ctx = run->ctx;
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: stopping ctx %p run %p\n",
+               __func__, chan->ic_task, ctx, run);
+
+       /* disable IC tasks and the channels */
+       ipu_ic_task_disable(chan->ic);
+       ipu_idmac_disable_channel(chan->in_chan);
+       ipu_idmac_disable_channel(chan->out_chan);
+
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               ipu_idmac_disable_channel(chan->rotation_in_chan);
+               ipu_idmac_disable_channel(chan->rotation_out_chan);
+               ipu_idmac_unlink(chan->out_chan, chan->rotation_in_chan);
+       }
+
+       ipu_ic_disable(chan->ic);
+}
+
+static void init_idmac_channel(struct ipu_image_convert_ctx *ctx,
+                              struct ipuv3_channel *channel,
+                              struct ipu_image_convert_image *image,
+                              enum ipu_rotate_mode rot_mode,
+                              bool rot_swap_width_height)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       unsigned int burst_size;
+       u32 width, height, stride;
+       dma_addr_t addr0, addr1 = 0;
+       struct ipu_image tile_image;
+       unsigned int tile_idx[2];
+
+       if (image->type == IMAGE_CONVERT_OUT) {
+               tile_idx[0] = ctx->out_tile_map[0];
+               tile_idx[1] = ctx->out_tile_map[1];
+       } else {
+               tile_idx[0] = 0;
+               tile_idx[1] = 1;
+       }
+
+       if (rot_swap_width_height) {
+               width = image->tile[0].height;
+               height = image->tile[0].width;
+               stride = image->tile[0].rot_stride;
+               addr0 = ctx->rot_intermediate[0].phys;
+               if (ctx->double_buffering)
+                       addr1 = ctx->rot_intermediate[1].phys;
+       } else {
+               width = image->tile[0].width;
+               height = image->tile[0].height;
+               stride = image->stride;
+               addr0 = image->base.phys0 +
+                       image->tile[tile_idx[0]].offset;
+               if (ctx->double_buffering)
+                       addr1 = image->base.phys0 +
+                               image->tile[tile_idx[1]].offset;
+       }
+
+       ipu_cpmem_zero(channel);
+
+       memset(&tile_image, 0, sizeof(tile_image));
+       tile_image.pix.width = tile_image.rect.width = width;
+       tile_image.pix.height = tile_image.rect.height = height;
+       tile_image.pix.bytesperline = stride;
+       tile_image.pix.pixelformat =  image->fmt->fourcc;
+       tile_image.phys0 = addr0;
+       tile_image.phys1 = addr1;
+       ipu_cpmem_set_image(channel, &tile_image);
+
+       if (image->fmt->planar && !rot_swap_width_height)
+               ipu_cpmem_set_uv_offset(channel,
+                                       image->tile[tile_idx[0]].u_off,
+                                       image->tile[tile_idx[0]].v_off);
+
+       if (rot_mode)
+               ipu_cpmem_set_rotation(channel, rot_mode);
+
+       if (channel == chan->rotation_in_chan ||
+           channel == chan->rotation_out_chan) {
+               burst_size = 8;
+               ipu_cpmem_set_block_mode(channel);
+       } else
+               burst_size = (width % 16) ? 8 : 16;
+
+       ipu_cpmem_set_burstsize(channel, burst_size);
+
+       ipu_ic_task_idma_init(chan->ic, channel, width, height,
+                             burst_size, rot_mode);
+
+       ipu_cpmem_set_axi_id(channel, 1);
+
+       ipu_idmac_set_double_buffer(channel, ctx->double_buffering);
+}
+
+static int convert_start(struct ipu_image_convert_run *run)
+{
+       struct ipu_image_convert_ctx *ctx = run->ctx;
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_image *s_image = &ctx->in;
+       struct ipu_image_convert_image *d_image = &ctx->out;
+       enum ipu_color_space src_cs, dest_cs;
+       unsigned int dest_width, dest_height;
+       int ret;
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: starting ctx %p run %p\n",
+               __func__, chan->ic_task, ctx, run);
+
+       src_cs = ipu_pixelformat_to_colorspace(s_image->fmt->fourcc);
+       dest_cs = ipu_pixelformat_to_colorspace(d_image->fmt->fourcc);
+
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               /* swap width/height for resizer */
+               dest_width = d_image->tile[0].height;
+               dest_height = d_image->tile[0].width;
+       } else {
+               dest_width = d_image->tile[0].width;
+               dest_height = d_image->tile[0].height;
+       }
+
+       /* setup the IC resizer and CSC */
+       ret = ipu_ic_task_init(chan->ic,
+                              s_image->tile[0].width,
+                              s_image->tile[0].height,
+                              dest_width,
+                              dest_height,
+                              src_cs, dest_cs);
+       if (ret) {
+               dev_err(priv->ipu->dev, "ipu_ic_task_init failed, %d\n", ret);
+               return ret;
+       }
+
+       /* init the source MEM-->IC PP IDMAC channel */
+       init_idmac_channel(ctx, chan->in_chan, s_image,
+                          IPU_ROTATE_NONE, false);
+
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               /* init the IC PP-->MEM IDMAC channel */
+               init_idmac_channel(ctx, chan->out_chan, d_image,
+                                  IPU_ROTATE_NONE, true);
+
+               /* init the MEM-->IC PP ROT IDMAC channel */
+               init_idmac_channel(ctx, chan->rotation_in_chan, d_image,
+                                  ctx->rot_mode, true);
+
+               /* init the destination IC PP ROT-->MEM IDMAC channel */
+               init_idmac_channel(ctx, chan->rotation_out_chan, d_image,
+                                  IPU_ROTATE_NONE, false);
+
+               /* now link IC PP-->MEM to MEM-->IC PP ROT */
+               ipu_idmac_link(chan->out_chan, chan->rotation_in_chan);
+       } else {
+               /* init the destination IC PP-->MEM IDMAC channel */
+               init_idmac_channel(ctx, chan->out_chan, d_image,
+                                  ctx->rot_mode, false);
+       }
+
+       /* enable the IC */
+       ipu_ic_enable(chan->ic);
+
+       /* set buffers ready */
+       ipu_idmac_select_buffer(chan->in_chan, 0);
+       ipu_idmac_select_buffer(chan->out_chan, 0);
+       if (ipu_rot_mode_is_irt(ctx->rot_mode))
+               ipu_idmac_select_buffer(chan->rotation_out_chan, 0);
+       if (ctx->double_buffering) {
+               ipu_idmac_select_buffer(chan->in_chan, 1);
+               ipu_idmac_select_buffer(chan->out_chan, 1);
+               if (ipu_rot_mode_is_irt(ctx->rot_mode))
+                       ipu_idmac_select_buffer(chan->rotation_out_chan, 1);
+       }
+
+       /* enable the channels! */
+       ipu_idmac_enable_channel(chan->in_chan);
+       ipu_idmac_enable_channel(chan->out_chan);
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               ipu_idmac_enable_channel(chan->rotation_in_chan);
+               ipu_idmac_enable_channel(chan->rotation_out_chan);
+       }
+
+       ipu_ic_task_enable(chan->ic);
+
+       ipu_cpmem_dump(chan->in_chan);
+       ipu_cpmem_dump(chan->out_chan);
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               ipu_cpmem_dump(chan->rotation_in_chan);
+               ipu_cpmem_dump(chan->rotation_out_chan);
+       }
+
+       ipu_dump(priv->ipu);
+
+       return 0;
+}
+
+/* hold irqlock when calling */
+static int do_run(struct ipu_image_convert_run *run)
+{
+       struct ipu_image_convert_ctx *ctx = run->ctx;
+       struct ipu_image_convert_chan *chan = ctx->chan;
+
+       lockdep_assert_held(&chan->irqlock);
+
+       ctx->in.base.phys0 = run->in_phys;
+       ctx->out.base.phys0 = run->out_phys;
+
+       ctx->cur_buf_num = 0;
+       ctx->next_tile = 1;
+
+       /* remove run from pending_q and set as current */
+       list_del(&run->list);
+       chan->current_run = run;
+
+       return convert_start(run);
+}
+
+/* hold irqlock when calling */
+static void run_next(struct ipu_image_convert_chan *chan)
+{
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_run *run, *tmp;
+       int ret;
+
+       lockdep_assert_held(&chan->irqlock);
+
+       list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {
+               /* skip contexts that are aborting */
+               if (run->ctx->aborting) {
+                       dev_dbg(priv->ipu->dev,
+                               "%s: task %u: skipping aborting ctx %p run %p\n",
+                               __func__, chan->ic_task, run->ctx, run);
+                       continue;
+               }
+
+               ret = do_run(run);
+               if (!ret)
+                       break;
+
+               /*
+                * something went wrong with start, add the run
+                * to done q and continue to the next run in the
+                * pending q.
+                */
+               run->status = ret;
+               list_add_tail(&run->list, &chan->done_q);
+               chan->current_run = NULL;
+       }
+}
+
+static void empty_done_q(struct ipu_image_convert_chan *chan)
+{
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_run *run;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       while (!list_empty(&chan->done_q)) {
+               run = list_entry(chan->done_q.next,
+                                struct ipu_image_convert_run,
+                                list);
+
+               list_del(&run->list);
+
+               dev_dbg(priv->ipu->dev,
+                       "%s: task %u: completing ctx %p run %p with %d\n",
+                       __func__, chan->ic_task, run->ctx, run, run->status);
+
+               /* call the completion callback and free the run */
+               spin_unlock_irqrestore(&chan->irqlock, flags);
+               run->ctx->complete(run, run->ctx->complete_context);
+               spin_lock_irqsave(&chan->irqlock, flags);
+       }
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+}
+
+/*
+ * the bottom half thread clears out the done_q, calling the
+ * completion handler for each.
+ */
+static irqreturn_t do_bh(int irq, void *dev_id)
+{
+       struct ipu_image_convert_chan *chan = dev_id;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_ctx *ctx;
+       unsigned long flags;
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: enter\n", __func__,
+               chan->ic_task);
+
+       empty_done_q(chan);
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       /*
+        * the done_q is cleared out, signal any contexts
+        * that are aborting that abort can complete.
+        */
+       list_for_each_entry(ctx, &chan->ctx_list, list) {
+               if (ctx->aborting) {
+                       dev_dbg(priv->ipu->dev,
+                               "%s: task %u: signaling abort for ctx %p\n",
+                               __func__, chan->ic_task, ctx);
+                       complete(&ctx->aborted);
+               }
+       }
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: exit\n", __func__,
+               chan->ic_task);
+
+       return IRQ_HANDLED;
+}
+
+/* hold irqlock when calling */
+static irqreturn_t do_irq(struct ipu_image_convert_run *run)
+{
+       struct ipu_image_convert_ctx *ctx = run->ctx;
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_tile *src_tile, *dst_tile;
+       struct ipu_image_convert_image *s_image = &ctx->in;
+       struct ipu_image_convert_image *d_image = &ctx->out;
+       struct ipuv3_channel *outch;
+       unsigned int dst_idx;
+
+       lockdep_assert_held(&chan->irqlock);
+
+       outch = ipu_rot_mode_is_irt(ctx->rot_mode) ?
+               chan->rotation_out_chan : chan->out_chan;
+
+       /*
+        * It is difficult to stop the channel DMA before the channels
+        * enter the paused state. Without double-buffering the channels
+        * are always in a paused state when the EOF irq occurs, so it
+        * is safe to stop the channels now. For double-buffering we
+        * just ignore the abort until the operation completes, when it
+        * is safe to shut down.
+        */
+       if (ctx->aborting && !ctx->double_buffering) {
+               convert_stop(run);
+               run->status = -EIO;
+               goto done;
+       }
+
+       if (ctx->next_tile == ctx->num_tiles) {
+               /*
+                * the conversion is complete
+                */
+               convert_stop(run);
+               run->status = 0;
+               goto done;
+       }
+
+       /*
+        * not done, place the next tile buffers.
+        */
+       if (!ctx->double_buffering) {
+
+               src_tile = &s_image->tile[ctx->next_tile];
+               dst_idx = ctx->out_tile_map[ctx->next_tile];
+               dst_tile = &d_image->tile[dst_idx];
+
+               ipu_cpmem_set_buffer(chan->in_chan, 0,
+                                    s_image->base.phys0 + src_tile->offset);
+               ipu_cpmem_set_buffer(outch, 0,
+                                    d_image->base.phys0 + dst_tile->offset);
+               if (s_image->fmt->planar)
+                       ipu_cpmem_set_uv_offset(chan->in_chan,
+                                               src_tile->u_off,
+                                               src_tile->v_off);
+               if (d_image->fmt->planar)
+                       ipu_cpmem_set_uv_offset(outch,
+                                               dst_tile->u_off,
+                                               dst_tile->v_off);
+
+               ipu_idmac_select_buffer(chan->in_chan, 0);
+               ipu_idmac_select_buffer(outch, 0);
+
+       } else if (ctx->next_tile < ctx->num_tiles - 1) {
+
+               src_tile = &s_image->tile[ctx->next_tile + 1];
+               dst_idx = ctx->out_tile_map[ctx->next_tile + 1];
+               dst_tile = &d_image->tile[dst_idx];
+
+               ipu_cpmem_set_buffer(chan->in_chan, ctx->cur_buf_num,
+                                    s_image->base.phys0 + src_tile->offset);
+               ipu_cpmem_set_buffer(outch, ctx->cur_buf_num,
+                                    d_image->base.phys0 + dst_tile->offset);
+
+               ipu_idmac_select_buffer(chan->in_chan, ctx->cur_buf_num);
+               ipu_idmac_select_buffer(outch, ctx->cur_buf_num);
+
+               ctx->cur_buf_num ^= 1;
+       }
+
+       ctx->next_tile++;
+       return IRQ_HANDLED;
+done:
+       list_add_tail(&run->list, &chan->done_q);
+       chan->current_run = NULL;
+       run_next(chan);
+       return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t norotate_irq(int irq, void *data)
+{
+       struct ipu_image_convert_chan *chan = data;
+       struct ipu_image_convert_ctx *ctx;
+       struct ipu_image_convert_run *run;
+       unsigned long flags;
+       irqreturn_t ret;
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       /* get current run and its context */
+       run = chan->current_run;
+       if (!run) {
+               ret = IRQ_NONE;
+               goto out;
+       }
+
+       ctx = run->ctx;
+
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               /* this is a rotation operation, just ignore */
+               spin_unlock_irqrestore(&chan->irqlock, flags);
+               return IRQ_HANDLED;
+       }
+
+       ret = do_irq(run);
+out:
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+       return ret;
+}
+
+static irqreturn_t rotate_irq(int irq, void *data)
+{
+       struct ipu_image_convert_chan *chan = data;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_ctx *ctx;
+       struct ipu_image_convert_run *run;
+       unsigned long flags;
+       irqreturn_t ret;
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       /* get current run and its context */
+       run = chan->current_run;
+       if (!run) {
+               ret = IRQ_NONE;
+               goto out;
+       }
+
+       ctx = run->ctx;
+
+       if (!ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               /* this was NOT a rotation operation, shouldn't happen */
+               dev_err(priv->ipu->dev, "Unexpected rotation interrupt\n");
+               spin_unlock_irqrestore(&chan->irqlock, flags);
+               return IRQ_HANDLED;
+       }
+
+       ret = do_irq(run);
+out:
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+       return ret;
+}
+
+/*
+ * try to force the completion of runs for this ctx. Called when
+ * abort wait times out in ipu_image_convert_abort().
+ */
+static void force_abort(struct ipu_image_convert_ctx *ctx)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_run *run;
+       unsigned long flags;
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       run = chan->current_run;
+       if (run && run->ctx == ctx) {
+               convert_stop(run);
+               run->status = -EIO;
+               list_add_tail(&run->list, &chan->done_q);
+               chan->current_run = NULL;
+               run_next(chan);
+       }
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+
+       empty_done_q(chan);
+}
+
+static void release_ipu_resources(struct ipu_image_convert_chan *chan)
+{
+       if (chan->out_eof_irq >= 0)
+               free_irq(chan->out_eof_irq, chan);
+       if (chan->rot_out_eof_irq >= 0)
+               free_irq(chan->rot_out_eof_irq, chan);
+
+       if (!IS_ERR_OR_NULL(chan->in_chan))
+               ipu_idmac_put(chan->in_chan);
+       if (!IS_ERR_OR_NULL(chan->out_chan))
+               ipu_idmac_put(chan->out_chan);
+       if (!IS_ERR_OR_NULL(chan->rotation_in_chan))
+               ipu_idmac_put(chan->rotation_in_chan);
+       if (!IS_ERR_OR_NULL(chan->rotation_out_chan))
+               ipu_idmac_put(chan->rotation_out_chan);
+       if (!IS_ERR_OR_NULL(chan->ic))
+               ipu_ic_put(chan->ic);
+
+       chan->in_chan = chan->out_chan = chan->rotation_in_chan =
+               chan->rotation_out_chan = NULL;
+       chan->out_eof_irq = chan->rot_out_eof_irq = -1;
+}
+
+static int get_ipu_resources(struct ipu_image_convert_chan *chan)
+{
+       const struct ipu_image_convert_dma_chan *dma = chan->dma_ch;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       int ret;
+
+       /* get IC */
+       chan->ic = ipu_ic_get(priv->ipu, chan->ic_task);
+       if (IS_ERR(chan->ic)) {
+               dev_err(priv->ipu->dev, "could not acquire IC\n");
+               ret = PTR_ERR(chan->ic);
+               goto err;
+       }
+
+       /* get IDMAC channels */
+       chan->in_chan = ipu_idmac_get(priv->ipu, dma->in);
+       chan->out_chan = ipu_idmac_get(priv->ipu, dma->out);
+       if (IS_ERR(chan->in_chan) || IS_ERR(chan->out_chan)) {
+               dev_err(priv->ipu->dev, "could not acquire idmac channels\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       chan->rotation_in_chan = ipu_idmac_get(priv->ipu, dma->rot_in);
+       chan->rotation_out_chan = ipu_idmac_get(priv->ipu, dma->rot_out);
+       if (IS_ERR(chan->rotation_in_chan) || IS_ERR(chan->rotation_out_chan)) {
+               dev_err(priv->ipu->dev,
+                       "could not acquire idmac rotation channels\n");
+               ret = -EBUSY;
+               goto err;
+       }
+
+       /* acquire the EOF interrupts */
+       chan->out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+                                                 chan->out_chan,
+                                                 IPU_IRQ_EOF);
+
+       ret = request_threaded_irq(chan->out_eof_irq, norotate_irq, do_bh,
+                                  0, "ipu-ic", chan);
+       if (ret < 0) {
+               dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+                        chan->out_eof_irq);
+               chan->out_eof_irq = -1;
+               goto err;
+       }
+
+       chan->rot_out_eof_irq = ipu_idmac_channel_irq(priv->ipu,
+                                                    chan->rotation_out_chan,
+                                                    IPU_IRQ_EOF);
+
+       ret = request_threaded_irq(chan->rot_out_eof_irq, rotate_irq, do_bh,
+                                  0, "ipu-ic", chan);
+       if (ret < 0) {
+               dev_err(priv->ipu->dev, "could not acquire irq %d\n",
+                       chan->rot_out_eof_irq);
+               chan->rot_out_eof_irq = -1;
+               goto err;
+       }
+
+       return 0;
+err:
+       release_ipu_resources(chan);
+       return ret;
+}
+
+static int fill_image(struct ipu_image_convert_ctx *ctx,
+                     struct ipu_image_convert_image *ic_image,
+                     struct ipu_image *image,
+                     enum ipu_image_convert_type type)
+{
+       struct ipu_image_convert_priv *priv = ctx->chan->priv;
+
+       ic_image->base = *image;
+       ic_image->type = type;
+
+       ic_image->fmt = get_format(image->pix.pixelformat);
+       if (!ic_image->fmt) {
+               dev_err(priv->ipu->dev, "pixelformat not supported for %s\n",
+                       type == IMAGE_CONVERT_OUT ? "Output" : "Input");
+               return -EINVAL;
+       }
+
+       if (ic_image->fmt->planar)
+               ic_image->stride = ic_image->base.pix.width;
+       else
+               ic_image->stride  = ic_image->base.pix.bytesperline;
+
+       calc_tile_dimensions(ctx, ic_image);
+       calc_tile_offsets(ctx, ic_image);
+
+       return 0;
+}
+
+/* borrowed from drivers/media/v4l2-core/v4l2-common.c */
+static unsigned int clamp_align(unsigned int x, unsigned int min,
+                               unsigned int max, unsigned int align)
+{
+       /* Bits that must be zero to be aligned */
+       unsigned int mask = ~((1 << align) - 1);
+
+       /* Clamp to aligned min and max */
+       x = clamp(x, (min + ~mask) & mask, max & mask);
+
+       /* Round to nearest aligned value */
+       if (align)
+               x = (x + (1 << (align - 1))) & mask;
+
+       return x;
+}
+
+/*
+ * We have to adjust the tile width such that the tile physaddrs and
+ * U and V plane offsets are multiples of 8 bytes as required by
+ * the IPU DMA Controller. For the planar formats, this corresponds
+ * to a pixel alignment of 16 (but use a more formal equation since
+ * the variables are available). For all the packed formats, 8 is
+ * good enough.
+ */
+static inline u32 tile_width_align(const struct ipu_image_pixfmt *fmt)
+{
+       return fmt->planar ? 8 * fmt->uv_width_dec : 8;
+}
+
+/*
+ * For tile height alignment, we have to ensure that the output tile
+ * heights are multiples of 8 lines if the IRT is required by the
+ * given rotation mode (the IRT performs rotations on 8x8 blocks
+ * at a time). If the IRT is not used, or for input image tiles,
+ * 2 lines are good enough.
+ */
+static inline u32 tile_height_align(enum ipu_image_convert_type type,
+                                   enum ipu_rotate_mode rot_mode)
+{
+       return (type == IMAGE_CONVERT_OUT &&
+               ipu_rot_mode_is_irt(rot_mode)) ? 8 : 2;
+}
+
+/* Adjusts input/output images to IPU restrictions */
+void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+                             enum ipu_rotate_mode rot_mode)
+{
+       const struct ipu_image_pixfmt *infmt, *outfmt;
+       unsigned int num_in_rows, num_in_cols;
+       unsigned int num_out_rows, num_out_cols;
+       u32 w_align, h_align;
+
+       infmt = get_format(in->pix.pixelformat);
+       outfmt = get_format(out->pix.pixelformat);
+
+       /* set some default pixel formats if needed */
+       if (!infmt) {
+               in->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+               infmt = get_format(V4L2_PIX_FMT_RGB24);
+       }
+       if (!outfmt) {
+               out->pix.pixelformat = V4L2_PIX_FMT_RGB24;
+               outfmt = get_format(V4L2_PIX_FMT_RGB24);
+       }
+
+       /* image converter does not handle fields */
+       in->pix.field = out->pix.field = V4L2_FIELD_NONE;
+
+       /* resizer cannot downsize more than 4:1 */
+       if (ipu_rot_mode_is_irt(rot_mode)) {
+               out->pix.height = max_t(__u32, out->pix.height,
+                                       in->pix.width / 4);
+               out->pix.width = max_t(__u32, out->pix.width,
+                                      in->pix.height / 4);
+       } else {
+               out->pix.width = max_t(__u32, out->pix.width,
+                                      in->pix.width / 4);
+               out->pix.height = max_t(__u32, out->pix.height,
+                                       in->pix.height / 4);
+       }
+
+       /* get tiling rows/cols from output format */
+       num_out_rows = num_stripes(out->pix.height);
+       num_out_cols = num_stripes(out->pix.width);
+       if (ipu_rot_mode_is_irt(rot_mode)) {
+               num_in_rows = num_out_cols;
+               num_in_cols = num_out_rows;
+       } else {
+               num_in_rows = num_out_rows;
+               num_in_cols = num_out_cols;
+       }
+
+       /* align input width/height */
+       w_align = ilog2(tile_width_align(infmt) * num_in_cols);
+       h_align = ilog2(tile_height_align(IMAGE_CONVERT_IN, rot_mode) *
+                       num_in_rows);
+       in->pix.width = clamp_align(in->pix.width, MIN_W, MAX_W, w_align);
+       in->pix.height = clamp_align(in->pix.height, MIN_H, MAX_H, h_align);
+
+       /* align output width/height */
+       w_align = ilog2(tile_width_align(outfmt) * num_out_cols);
+       h_align = ilog2(tile_height_align(IMAGE_CONVERT_OUT, rot_mode) *
+                       num_out_rows);
+       out->pix.width = clamp_align(out->pix.width, MIN_W, MAX_W, w_align);
+       out->pix.height = clamp_align(out->pix.height, MIN_H, MAX_H, h_align);
+
+       /* set input/output strides and image sizes */
+       in->pix.bytesperline = (in->pix.width * infmt->bpp) >> 3;
+       in->pix.sizeimage = in->pix.height * in->pix.bytesperline;
+       out->pix.bytesperline = (out->pix.width * outfmt->bpp) >> 3;
+       out->pix.sizeimage = out->pix.height * out->pix.bytesperline;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_adjust);
+
+/*
+ * this is used by ipu_image_convert_prepare() to verify set input and
+ * output images are valid before starting the conversion. Clients can
+ * also call it before calling ipu_image_convert_prepare().
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+                            enum ipu_rotate_mode rot_mode)
+{
+       struct ipu_image testin, testout;
+
+       testin = *in;
+       testout = *out;
+
+       ipu_image_convert_adjust(&testin, &testout, rot_mode);
+
+       if (testin.pix.width != in->pix.width ||
+           testin.pix.height != in->pix.height ||
+           testout.pix.width != out->pix.width ||
+           testout.pix.height != out->pix.height)
+               return -EINVAL;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_verify);
+
+/*
+ * Call ipu_image_convert_prepare() to prepare for the conversion of
+ * given images and rotation mode. Returns a new conversion context.
+ */
+struct ipu_image_convert_ctx *
+ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                         struct ipu_image *in, struct ipu_image *out,
+                         enum ipu_rotate_mode rot_mode,
+                         ipu_image_convert_cb_t complete,
+                         void *complete_context)
+{
+       struct ipu_image_convert_priv *priv = ipu->image_convert_priv;
+       struct ipu_image_convert_image *s_image, *d_image;
+       struct ipu_image_convert_chan *chan;
+       struct ipu_image_convert_ctx *ctx;
+       unsigned long flags;
+       bool get_res;
+       int ret;
+
+       if (!in || !out || !complete ||
+           (ic_task != IC_TASK_VIEWFINDER &&
+            ic_task != IC_TASK_POST_PROCESSOR))
+               return ERR_PTR(-EINVAL);
+
+       /* verify the in/out images before continuing */
+       ret = ipu_image_convert_verify(in, out, rot_mode);
+       if (ret) {
+               dev_err(priv->ipu->dev, "%s: in/out formats invalid\n",
+                       __func__);
+               return ERR_PTR(ret);
+       }
+
+       chan = &priv->chan[ic_task];
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p\n", __func__,
+               chan->ic_task, ctx);
+
+       ctx->chan = chan;
+       init_completion(&ctx->aborted);
+
+       s_image = &ctx->in;
+       d_image = &ctx->out;
+
+       /* set tiling and rotation */
+       d_image->num_rows = num_stripes(out->pix.height);
+       d_image->num_cols = num_stripes(out->pix.width);
+       if (ipu_rot_mode_is_irt(rot_mode)) {
+               s_image->num_rows = d_image->num_cols;
+               s_image->num_cols = d_image->num_rows;
+       } else {
+               s_image->num_rows = d_image->num_rows;
+               s_image->num_cols = d_image->num_cols;
+       }
+
+       ctx->num_tiles = d_image->num_cols * d_image->num_rows;
+       ctx->rot_mode = rot_mode;
+
+       ret = fill_image(ctx, s_image, in, IMAGE_CONVERT_IN);
+       if (ret)
+               goto out_free;
+       ret = fill_image(ctx, d_image, out, IMAGE_CONVERT_OUT);
+       if (ret)
+               goto out_free;
+
+       calc_out_tile_map(ctx);
+
+       dump_format(ctx, s_image);
+       dump_format(ctx, d_image);
+
+       ctx->complete = complete;
+       ctx->complete_context = complete_context;
+
+       /*
+        * Can we use double-buffering for this operation? If there is
+        * only one tile (the whole image can be converted in a single
+        * operation) there's no point in using double-buffering. Also,
+        * the IPU's IDMAC channels allow only a single U and V plane
+        * offset shared between both buffers, but these offsets change
+        * for every tile, and therefore would have to be updated for
+        * each buffer which is not possible. So double-buffering is
+        * impossible when either the source or destination images are
+        * a planar format (YUV420, YUV422P, etc.).
+        */
+       ctx->double_buffering = (ctx->num_tiles > 1 &&
+                                !s_image->fmt->planar &&
+                                !d_image->fmt->planar);
+
+       if (ipu_rot_mode_is_irt(ctx->rot_mode)) {
+               ret = alloc_dma_buf(priv, &ctx->rot_intermediate[0],
+                                   d_image->tile[0].size);
+               if (ret)
+                       goto out_free;
+               if (ctx->double_buffering) {
+                       ret = alloc_dma_buf(priv,
+                                           &ctx->rot_intermediate[1],
+                                           d_image->tile[0].size);
+                       if (ret)
+                               goto out_free_dmabuf0;
+               }
+       }
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       get_res = list_empty(&chan->ctx_list);
+
+       list_add_tail(&ctx->list, &chan->ctx_list);
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+
+       if (get_res) {
+               ret = get_ipu_resources(chan);
+               if (ret)
+                       goto out_free_dmabuf1;
+       }
+
+       return ctx;
+
+out_free_dmabuf1:
+       free_dma_buf(priv, &ctx->rot_intermediate[1]);
+       spin_lock_irqsave(&chan->irqlock, flags);
+       list_del(&ctx->list);
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+out_free_dmabuf0:
+       free_dma_buf(priv, &ctx->rot_intermediate[0]);
+out_free:
+       kfree(ctx);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_prepare);
+
+/*
+ * Carry out a single image conversion run. Only the physaddr's of the input
+ * and output image buffers are needed. The conversion context must have
+ * been created previously with ipu_image_convert_prepare().
+ */
+int ipu_image_convert_queue(struct ipu_image_convert_run *run)
+{
+       struct ipu_image_convert_chan *chan;
+       struct ipu_image_convert_priv *priv;
+       struct ipu_image_convert_ctx *ctx;
+       unsigned long flags;
+       int ret = 0;
+
+       if (!run || !run->ctx || !run->in_phys || !run->out_phys)
+               return -EINVAL;
+
+       ctx = run->ctx;
+       chan = ctx->chan;
+       priv = chan->priv;
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: ctx %p run %p\n", __func__,
+               chan->ic_task, ctx, run);
+
+       INIT_LIST_HEAD(&run->list);
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       if (ctx->aborting) {
+               ret = -EIO;
+               goto unlock;
+       }
+
+       list_add_tail(&run->list, &chan->pending_q);
+
+       if (!chan->current_run) {
+               ret = do_run(run);
+               if (ret)
+                       chan->current_run = NULL;
+       }
+unlock:
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_queue);
+
+/* Abort any active or pending conversions for this context */
+void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       struct ipu_image_convert_run *run, *active_run, *tmp;
+       unsigned long flags;
+       int run_count, ret;
+       bool need_abort;
+
+       reinit_completion(&ctx->aborted);
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       /* move all remaining pending runs in this context to done_q */
+       list_for_each_entry_safe(run, tmp, &chan->pending_q, list) {
+               if (run->ctx != ctx)
+                       continue;
+               run->status = -EIO;
+               list_move_tail(&run->list, &chan->done_q);
+       }
+
+       run_count = get_run_count(ctx, &chan->done_q);
+       active_run = (chan->current_run && chan->current_run->ctx == ctx) ?
+               chan->current_run : NULL;
+
+       need_abort = (run_count || active_run);
+
+       ctx->aborting = need_abort;
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+
+       if (!need_abort) {
+               dev_dbg(priv->ipu->dev,
+                       "%s: task %u: no abort needed for ctx %p\n",
+                       __func__, chan->ic_task, ctx);
+               return;
+       }
+
+       dev_dbg(priv->ipu->dev,
+               "%s: task %u: wait for completion: %d runs, active run %p\n",
+               __func__, chan->ic_task, run_count, active_run);
+
+       ret = wait_for_completion_timeout(&ctx->aborted,
+                                         msecs_to_jiffies(10000));
+       if (ret == 0) {
+               dev_warn(priv->ipu->dev, "%s: timeout\n", __func__);
+               force_abort(ctx);
+       }
+
+       ctx->aborting = false;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_abort);
+
+/* Unprepare image conversion context */
+void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx)
+{
+       struct ipu_image_convert_chan *chan = ctx->chan;
+       struct ipu_image_convert_priv *priv = chan->priv;
+       unsigned long flags;
+       bool put_res;
+
+       /* make sure no runs are hanging around */
+       ipu_image_convert_abort(ctx);
+
+       dev_dbg(priv->ipu->dev, "%s: task %u: removing ctx %p\n", __func__,
+               chan->ic_task, ctx);
+
+       spin_lock_irqsave(&chan->irqlock, flags);
+
+       list_del(&ctx->list);
+
+       put_res = list_empty(&chan->ctx_list);
+
+       spin_unlock_irqrestore(&chan->irqlock, flags);
+
+       if (put_res)
+               release_ipu_resources(chan);
+
+       free_dma_buf(priv, &ctx->rot_intermediate[1]);
+       free_dma_buf(priv, &ctx->rot_intermediate[0]);
+
+       kfree(ctx);
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_unprepare);
+
+/*
+ * "Canned" asynchronous single image conversion. Allocates and returns
+ * a new conversion run.  On successful return the caller must free the
+ * run and call ipu_image_convert_unprepare() after conversion completes.
+ */
+struct ipu_image_convert_run *
+ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                 struct ipu_image *in, struct ipu_image *out,
+                 enum ipu_rotate_mode rot_mode,
+                 ipu_image_convert_cb_t complete,
+                 void *complete_context)
+{
+       struct ipu_image_convert_ctx *ctx;
+       struct ipu_image_convert_run *run;
+       int ret;
+
+       ctx = ipu_image_convert_prepare(ipu, ic_task, in, out, rot_mode,
+                                       complete, complete_context);
+       if (IS_ERR(ctx))
+               return ERR_PTR(PTR_ERR(ctx));
+
+       run = kzalloc(sizeof(*run), GFP_KERNEL);
+       if (!run) {
+               ipu_image_convert_unprepare(ctx);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       run->ctx = ctx;
+       run->in_phys = in->phys0;
+       run->out_phys = out->phys0;
+
+       ret = ipu_image_convert_queue(run);
+       if (ret) {
+               ipu_image_convert_unprepare(ctx);
+               kfree(run);
+               return ERR_PTR(ret);
+       }
+
+       return run;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert);
+
+/* "Canned" synchronous single image conversion */
+static void image_convert_sync_complete(struct ipu_image_convert_run *run,
+                                       void *data)
+{
+       struct completion *comp = data;
+
+       complete(comp);
+}
+
+int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                          struct ipu_image *in, struct ipu_image *out,
+                          enum ipu_rotate_mode rot_mode)
+{
+       struct ipu_image_convert_run *run;
+       struct completion comp;
+       int ret;
+
+       init_completion(&comp);
+
+       run = ipu_image_convert(ipu, ic_task, in, out, rot_mode,
+                               image_convert_sync_complete, &comp);
+       if (IS_ERR(run))
+               return PTR_ERR(run);
+
+       ret = wait_for_completion_timeout(&comp, msecs_to_jiffies(10000));
+       ret = (ret == 0) ? -ETIMEDOUT : 0;
+
+       ipu_image_convert_unprepare(run->ctx);
+       kfree(run);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_image_convert_sync);
+
+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev)
+{
+       struct ipu_image_convert_priv *priv;
+       int i;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       ipu->image_convert_priv = priv;
+       priv->ipu = ipu;
+
+       for (i = 0; i < IC_NUM_TASKS; i++) {
+               struct ipu_image_convert_chan *chan = &priv->chan[i];
+
+               chan->ic_task = i;
+               chan->priv = priv;
+               chan->dma_ch = &image_convert_dma_chan[i];
+               chan->out_eof_irq = -1;
+               chan->rot_out_eof_irq = -1;
+
+               spin_lock_init(&chan->irqlock);
+               INIT_LIST_HEAD(&chan->ctx_list);
+               INIT_LIST_HEAD(&chan->pending_q);
+               INIT_LIST_HEAD(&chan->done_q);
+       }
+
+       return 0;
+}
+
+void ipu_image_convert_exit(struct ipu_soc *ipu)
+{
+}
index bfb1e8a..22e47b6 100644 (file)
@@ -75,6 +75,33 @@ struct ipu_soc;
 #define IPU_INT_CTRL(n)                IPU_CM_REG(0x003C + 4 * (n))
 #define IPU_INT_STAT(n)                IPU_CM_REG(0x0200 + 4 * (n))
 
+/* FS_PROC_FLOW1 */
+#define FS_PRPENC_ROT_SRC_SEL_MASK     (0xf << 0)
+#define FS_PRPENC_ROT_SRC_SEL_ENC              (0x7 << 0)
+#define FS_PRPVF_ROT_SRC_SEL_MASK      (0xf << 8)
+#define FS_PRPVF_ROT_SRC_SEL_VF                        (0x8 << 8)
+#define FS_PP_SRC_SEL_MASK             (0xf << 12)
+#define FS_PP_ROT_SRC_SEL_MASK         (0xf << 16)
+#define FS_PP_ROT_SRC_SEL_PP                   (0x5 << 16)
+#define FS_VDI1_SRC_SEL_MASK           (0x3 << 20)
+#define FS_VDI3_SRC_SEL_MASK           (0x3 << 20)
+#define FS_PRP_SRC_SEL_MASK            (0xf << 24)
+#define FS_VDI_SRC_SEL_MASK            (0x3 << 28)
+#define FS_VDI_SRC_SEL_CSI_DIRECT              (0x1 << 28)
+#define FS_VDI_SRC_SEL_VDOA                    (0x2 << 28)
+
+/* FS_PROC_FLOW2 */
+#define FS_PRP_ENC_DEST_SEL_MASK       (0xf << 0)
+#define FS_PRP_ENC_DEST_SEL_IRT_ENC            (0x1 << 0)
+#define FS_PRPVF_DEST_SEL_MASK         (0xf << 4)
+#define FS_PRPVF_DEST_SEL_IRT_VF               (0x1 << 4)
+#define FS_PRPVF_ROT_DEST_SEL_MASK     (0xf << 8)
+#define FS_PP_DEST_SEL_MASK            (0xf << 12)
+#define FS_PP_DEST_SEL_IRT_PP                  (0x3 << 12)
+#define FS_PP_ROT_DEST_SEL_MASK                (0xf << 16)
+#define FS_PRPENC_ROT_DEST_SEL_MASK    (0xf << 20)
+#define FS_PRP_DEST_SEL_MASK           (0xf << 24)
+
 #define IPU_DI0_COUNTER_RELEASE                        (1 << 24)
 #define IPU_DI1_COUNTER_RELEASE                        (1 << 25)
 
@@ -138,6 +165,8 @@ struct ipu_dc_priv;
 struct ipu_dmfc_priv;
 struct ipu_di;
 struct ipu_ic_priv;
+struct ipu_vdi;
+struct ipu_image_convert_priv;
 struct ipu_smfc_priv;
 
 struct ipu_devtype;
@@ -152,6 +181,7 @@ struct ipu_soc {
        void __iomem            *cm_reg;
        void __iomem            *idmac_reg;
 
+       int                     id;
        int                     usecount;
 
        struct clk              *clk;
@@ -169,6 +199,8 @@ struct ipu_soc {
        struct ipu_di           *di_priv[2];
        struct ipu_csi          *csi_priv[2];
        struct ipu_ic_priv      *ic_priv;
+       struct ipu_vdi          *vdi_priv;
+       struct ipu_image_convert_priv *image_convert_priv;
        struct ipu_smfc_priv    *smfc_priv;
 };
 
@@ -199,6 +231,13 @@ int ipu_ic_init(struct ipu_soc *ipu, struct device *dev,
                unsigned long base, unsigned long tpmem_base);
 void ipu_ic_exit(struct ipu_soc *ipu);
 
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+                unsigned long base, u32 module);
+void ipu_vdi_exit(struct ipu_soc *ipu);
+
+int ipu_image_convert_init(struct ipu_soc *ipu, struct device *dev);
+void ipu_image_convert_exit(struct ipu_soc *ipu);
+
 int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
                unsigned long base, u32 module, struct clk *ipu_clk);
 void ipu_di_exit(struct ipu_soc *ipu, int id);
diff --git a/drivers/gpu/ipu-v3/ipu-vdi.c b/drivers/gpu/ipu-v3/ipu-vdi.c
new file mode 100644 (file)
index 0000000..f27bf5a
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2012-2016 Mentor Graphics Inc.
+ * Copyright (C) 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#include <linux/io.h>
+#include "ipu-prv.h"
+
+struct ipu_vdi {
+       void __iomem *base;
+       u32 module;
+       spinlock_t lock;
+       int use_count;
+       struct ipu_soc *ipu;
+};
+
+
+/* VDI Register Offsets */
+#define VDI_FSIZE 0x0000
+#define VDI_C     0x0004
+
+/* VDI Register Fields */
+#define VDI_C_CH_420             (0 << 1)
+#define VDI_C_CH_422             (1 << 1)
+#define VDI_C_MOT_SEL_MASK       (0x3 << 2)
+#define VDI_C_MOT_SEL_FULL       (2 << 2)
+#define VDI_C_MOT_SEL_LOW        (1 << 2)
+#define VDI_C_MOT_SEL_MED        (0 << 2)
+#define VDI_C_BURST_SIZE1_4      (3 << 4)
+#define VDI_C_BURST_SIZE2_4      (3 << 8)
+#define VDI_C_BURST_SIZE3_4      (3 << 12)
+#define VDI_C_BURST_SIZE_MASK    0xF
+#define VDI_C_BURST_SIZE1_OFFSET 4
+#define VDI_C_BURST_SIZE2_OFFSET 8
+#define VDI_C_BURST_SIZE3_OFFSET 12
+#define VDI_C_VWM1_SET_1         (0 << 16)
+#define VDI_C_VWM1_SET_2         (1 << 16)
+#define VDI_C_VWM1_CLR_2         (1 << 19)
+#define VDI_C_VWM3_SET_1         (0 << 22)
+#define VDI_C_VWM3_SET_2         (1 << 22)
+#define VDI_C_VWM3_CLR_2         (1 << 25)
+#define VDI_C_TOP_FIELD_MAN_1    (1 << 30)
+#define VDI_C_TOP_FIELD_AUTO_1   (1 << 31)
+
+static inline u32 ipu_vdi_read(struct ipu_vdi *vdi, unsigned int offset)
+{
+       return readl(vdi->base + offset);
+}
+
+static inline void ipu_vdi_write(struct ipu_vdi *vdi, u32 value,
+                                unsigned int offset)
+{
+       writel(value, vdi->base + offset);
+}
+
+void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field)
+{
+       bool top_field_0 = false;
+       unsigned long flags;
+       u32 reg;
+
+       switch (field) {
+       case V4L2_FIELD_INTERLACED_TB:
+       case V4L2_FIELD_SEQ_TB:
+       case V4L2_FIELD_TOP:
+               top_field_0 = true;
+               break;
+       case V4L2_FIELD_INTERLACED_BT:
+       case V4L2_FIELD_SEQ_BT:
+       case V4L2_FIELD_BOTTOM:
+               top_field_0 = false;
+               break;
+       default:
+               top_field_0 = (std & V4L2_STD_525_60) ? true : false;
+               break;
+       }
+
+       spin_lock_irqsave(&vdi->lock, flags);
+
+       reg = ipu_vdi_read(vdi, VDI_C);
+       if (top_field_0)
+               reg &= ~VDI_C_TOP_FIELD_MAN_1;
+       else
+               reg |= VDI_C_TOP_FIELD_MAN_1;
+       ipu_vdi_write(vdi, reg, VDI_C);
+
+       spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_field_order);
+
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel)
+{
+       unsigned long flags;
+       u32 reg;
+
+       spin_lock_irqsave(&vdi->lock, flags);
+
+       reg = ipu_vdi_read(vdi, VDI_C);
+
+       reg &= ~VDI_C_MOT_SEL_MASK;
+
+       switch (motion_sel) {
+       case MED_MOTION:
+               reg |= VDI_C_MOT_SEL_MED;
+               break;
+       case HIGH_MOTION:
+               reg |= VDI_C_MOT_SEL_FULL;
+               break;
+       default:
+               reg |= VDI_C_MOT_SEL_LOW;
+               break;
+       }
+
+       ipu_vdi_write(vdi, reg, VDI_C);
+
+       spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_set_motion);
+
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres)
+{
+       unsigned long flags;
+       u32 pixel_fmt, reg;
+
+       spin_lock_irqsave(&vdi->lock, flags);
+
+       reg = ((yres - 1) << 16) | (xres - 1);
+       ipu_vdi_write(vdi, reg, VDI_FSIZE);
+
+       /*
+        * Full motion, only vertical filter is used.
+        * Burst size is 4 accesses
+        */
+       if (code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+           code == MEDIA_BUS_FMT_UYVY8_1X16 ||
+           code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+           code == MEDIA_BUS_FMT_YUYV8_1X16)
+               pixel_fmt = VDI_C_CH_422;
+       else
+               pixel_fmt = VDI_C_CH_420;
+
+       reg = ipu_vdi_read(vdi, VDI_C);
+       reg |= pixel_fmt;
+       reg |= VDI_C_BURST_SIZE2_4;
+       reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_CLR_2;
+       reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_CLR_2;
+       ipu_vdi_write(vdi, reg, VDI_C);
+
+       spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_setup);
+
+void ipu_vdi_unsetup(struct ipu_vdi *vdi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&vdi->lock, flags);
+       ipu_vdi_write(vdi, 0, VDI_FSIZE);
+       ipu_vdi_write(vdi, 0, VDI_C);
+       spin_unlock_irqrestore(&vdi->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_unsetup);
+
+int ipu_vdi_enable(struct ipu_vdi *vdi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&vdi->lock, flags);
+
+       if (!vdi->use_count)
+               ipu_module_enable(vdi->ipu, vdi->module);
+
+       vdi->use_count++;
+
+       spin_unlock_irqrestore(&vdi->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_enable);
+
+int ipu_vdi_disable(struct ipu_vdi *vdi)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&vdi->lock, flags);
+
+       if (vdi->use_count) {
+               if (!--vdi->use_count)
+                       ipu_module_disable(vdi->ipu, vdi->module);
+       }
+
+       spin_unlock_irqrestore(&vdi->lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_disable);
+
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu)
+{
+       return ipu->vdi_priv;
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_get);
+
+void ipu_vdi_put(struct ipu_vdi *vdi)
+{
+}
+EXPORT_SYMBOL_GPL(ipu_vdi_put);
+
+int ipu_vdi_init(struct ipu_soc *ipu, struct device *dev,
+                unsigned long base, u32 module)
+{
+       struct ipu_vdi *vdi;
+
+       vdi = devm_kzalloc(dev, sizeof(*vdi), GFP_KERNEL);
+       if (!vdi)
+               return -ENOMEM;
+
+       ipu->vdi_priv = vdi;
+
+       spin_lock_init(&vdi->lock);
+       vdi->module = module;
+       vdi->base = devm_ioremap(dev, base, PAGE_SIZE);
+       if (!vdi->base)
+               return -ENOMEM;
+
+       dev_dbg(dev, "VDI base: 0x%08lx remapped to %p\n", base, vdi->base);
+       vdi->ipu = ipu;
+
+       return 0;
+}
+
+void ipu_vdi_exit(struct ipu_soc *ipu)
+{
+}
index f17cb04..1887f19 100644 (file)
@@ -131,7 +131,24 @@ static struct vga_device *vgadev_find(struct pci_dev *pdev)
        return NULL;
 }
 
-/* Returns the default VGA device (vgacon's babe) */
+/**
+ * vga_default_device - return the default VGA device, for vgacon
+ *
+ * This can be defined by the platform. The default implementation
+ * is rather dumb and will probably only work properly on single
+ * vga card setups and/or x86 platforms.
+ *
+ * If your VGA default device is not PCI, you'll have to return
+ * NULL here. In this case, I assume it will not conflict with
+ * any PCI card. If this is not true, I'll have to define two archs
+ * hooks for enabling/disabling the VGA default device if that is
+ * possible. This may be a problem with real _ISA_ VGA cards, in
+ * addition to a PCI one. I don't know at this point how to deal
+ * with that card. Can theirs IOs be disabled at all ? If not, then
+ * I suppose it's a matter of having the proper arch hook telling
+ * us about it, so we basically never allow anybody to succeed a
+ * vga_get()...
+ */
 struct pci_dev *vga_default_device(void)
 {
        return vga_default;
@@ -356,6 +373,40 @@ static void __vga_put(struct vga_device *vgadev, unsigned int rsrc)
                wake_up_all(&vga_wait_queue);
 }
 
+/**
+ * vga_get - acquire & locks VGA resources
+ * @pdev: pci device of the VGA card or NULL for the system default
+ * @rsrc: bit mask of resources to acquire and lock
+ * @interruptible: blocking should be interruptible by signals ?
+ *
+ * This function acquires VGA resources for the given card and mark those
+ * resources locked. If the resource requested are "normal" (and not legacy)
+ * resources, the arbiter will first check whether the card is doing legacy
+ * decoding for that type of resource. If yes, the lock is "converted" into a
+ * legacy resource lock.
+ *
+ * The arbiter will first look for all VGA cards that might conflict and disable
+ * their IOs and/or Memory access, including VGA forwarding on P2P bridges if
+ * necessary, so that the requested resources can be used. Then, the card is
+ * marked as locking these resources and the IO and/or Memory accesses are
+ * enabled on the card (including VGA forwarding on parent P2P bridges if any).
+ *
+ * This function will block if some conflicting card is already locking one of
+ * the required resources (or any resource on a different bus segment, since P2P
+ * bridges don't differentiate VGA memory and IO afaik). You can indicate
+ * whether this blocking should be interruptible by a signal (for userland
+ * interface) or not.
+ *
+ * Must not be called at interrupt time or in atomic context.  If the card
+ * already owns the resources, the function succeeds.  Nested calls are
+ * supported (a per-resource counter is maintained)
+ *
+ * On success, release the VGA resource again with vga_put().
+ *
+ * Returns:
+ *
+ * 0 on success, negative error code on failure.
+ */
 int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
 {
        struct vga_device *vgadev, *conflict;
@@ -408,6 +459,21 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible)
 }
 EXPORT_SYMBOL(vga_get);
 
+/**
+ * vga_tryget - try to acquire & lock legacy VGA resources
+ * @pdev: pci devivce of VGA card or NULL for system default
+ * @rsrc: bit mask of resources to acquire and lock
+ *
+ * This function performs the same operation as vga_get(), but will return an
+ * error (-EBUSY) instead of blocking if the resources are already locked by
+ * another card. It can be called in any context
+ *
+ * On success, release the VGA resource again with vga_put().
+ *
+ * Returns:
+ *
+ * 0 on success, negative error code on failure.
+ */
 int vga_tryget(struct pci_dev *pdev, unsigned int rsrc)
 {
        struct vga_device *vgadev;
@@ -435,6 +501,16 @@ bail:
 }
 EXPORT_SYMBOL(vga_tryget);
 
+/**
+ * vga_put - release lock on legacy VGA resources
+ * @pdev: pci device of VGA card or NULL for system default
+ * @rsrc: but mask of resource to release
+ *
+ * This fuction releases resources previously locked by vga_get() or
+ * vga_tryget(). The resources aren't disabled right away, so that a subsequence
+ * vga_get() on the same card will succeed immediately. Resources have a
+ * counter, so locks are only released if the counter reaches 0.
+ */
 void vga_put(struct pci_dev *pdev, unsigned int rsrc)
 {
        struct vga_device *vgadev;
@@ -716,7 +792,37 @@ void vga_set_legacy_decoding(struct pci_dev *pdev, unsigned int decodes)
 }
 EXPORT_SYMBOL(vga_set_legacy_decoding);
 
-/* call with NULL to unregister */
+/**
+ * vga_client_register - register or unregister a VGA arbitration client
+ * @pdev: pci device of the VGA client
+ * @cookie: client cookie to be used in callbacks
+ * @irq_set_state: irq state change callback
+ * @set_vga_decode: vga decode change callback
+ *
+ * Clients have two callback mechanisms they can use.
+ *
+ * @irq_set_state callback: If a client can't disable its GPUs VGA
+ * resources, then we need to be able to ask it to turn off its irqs when we
+ * turn off its mem and io decoding.
+ *
+ * @set_vga_decode callback: If a client can disable its GPU VGA resource, it
+ * will get a callback from this to set the encode/decode state.
+ *
+ * Rationale: we cannot disable VGA decode resources unconditionally some single
+ * GPU laptops seem to require ACPI or BIOS access to the VGA registers to
+ * control things like backlights etc.  Hopefully newer multi-GPU laptops do
+ * something saner, and desktops won't have any special ACPI for this. The
+ * driver will get a callback when VGA arbitration is first used by userspace
+ * since some older X servers have issues.
+ *
+ * This function does not check whether a client for @pdev has been registered
+ * already.
+ *
+ * To unregister just call this function with @irq_set_state and @set_vga_decode
+ * both set to NULL for the same @pdev as originally used to register them.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
 int vga_client_register(struct pci_dev *pdev, void *cookie,
                        void (*irq_set_state)(void *cookie, bool state),
                        unsigned int (*set_vga_decode)(void *cookie,
index 98fffa3..5ab6721 100644 (file)
@@ -1680,7 +1680,7 @@ static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
 
 static void of_i2c_register_devices(struct i2c_adapter *adap)
 {
-       struct device_node *node;
+       struct device_node *bus, *node;
 
        /* Only register child devices if the adapter has a node pointer set */
        if (!adap->dev.of_node)
@@ -1688,11 +1688,17 @@ static void of_i2c_register_devices(struct i2c_adapter *adap)
 
        dev_dbg(&adap->dev, "of_i2c: walking child nodes\n");
 
-       for_each_available_child_of_node(adap->dev.of_node, node) {
+       bus = of_get_child_by_name(adap->dev.of_node, "i2c-bus");
+       if (!bus)
+               bus = of_node_get(adap->dev.of_node);
+
+       for_each_available_child_of_node(bus, node) {
                if (of_node_test_and_set_flag(node, OF_POPULATED))
                        continue;
                of_i2c_register_device(adap, node);
        }
+
+       of_node_put(bus);
 }
 
 static int of_dev_node_match(struct device *dev, void *data)
index 19a418a..fb3fb89 100644 (file)
@@ -89,4 +89,6 @@ source "drivers/infiniband/sw/rxe/Kconfig"
 
 source "drivers/infiniband/hw/hfi1/Kconfig"
 
+source "drivers/infiniband/hw/qedr/Kconfig"
+
 endif # INFINIBAND
index 21fe401..e7a5ed9 100644 (file)
@@ -10,3 +10,4 @@ obj-$(CONFIG_INFINIBAND_OCRDMA)               += ocrdma/
 obj-$(CONFIG_INFINIBAND_USNIC)         += usnic/
 obj-$(CONFIG_INFINIBAND_HFI1)          += hfi1/
 obj-$(CONFIG_INFINIBAND_HNS)           += hns/
+obj-$(CONFIG_INFINIBAND_QEDR)          += qedr/
index 875597b..0973659 100644 (file)
@@ -83,8 +83,7 @@ static int hns_roce_sw2hw_cq(struct hns_roce_dev *dev,
 static int hns_roce_cq_alloc(struct hns_roce_dev *hr_dev, int nent,
                             struct hns_roce_mtt *hr_mtt,
                             struct hns_roce_uar *hr_uar,
-                            struct hns_roce_cq *hr_cq, int vector,
-                            int collapsed)
+                            struct hns_roce_cq *hr_cq, int vector)
 {
        struct hns_roce_cmd_mailbox *mailbox = NULL;
        struct hns_roce_cq_table *cq_table = NULL;
@@ -153,6 +152,9 @@ static int hns_roce_cq_alloc(struct hns_roce_dev *hr_dev, int nent,
        hr_cq->cons_index = 0;
        hr_cq->uar = hr_uar;
 
+       atomic_set(&hr_cq->refcount, 1);
+       init_completion(&hr_cq->free);
+
        return 0;
 
 err_radix:
@@ -192,6 +194,11 @@ static void hns_roce_free_cq(struct hns_roce_dev *hr_dev,
        /* Waiting interrupt process procedure carried out */
        synchronize_irq(hr_dev->eq_table.eq[hr_cq->vector].irq);
 
+       /* wait for all interrupt processed */
+       if (atomic_dec_and_test(&hr_cq->refcount))
+               complete(&hr_cq->free);
+       wait_for_completion(&hr_cq->free);
+
        spin_lock_irq(&cq_table->lock);
        radix_tree_delete(&cq_table->tree, hr_cq->cqn);
        spin_unlock_irq(&cq_table->lock);
@@ -300,10 +307,7 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
 
        cq_entries = roundup_pow_of_two((unsigned int)cq_entries);
        hr_cq->ib_cq.cqe = cq_entries - 1;
-       mutex_init(&hr_cq->resize_mutex);
        spin_lock_init(&hr_cq->lock);
-       hr_cq->hr_resize_buf = NULL;
-       hr_cq->resize_umem = NULL;
 
        if (context) {
                if (ib_copy_from_udata(&ucmd, udata, sizeof(ucmd))) {
@@ -338,8 +342,8 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
        }
 
        /* Allocate cq index, fill cq_context */
-       ret = hns_roce_cq_alloc(hr_dev, cq_entries, &hr_cq->hr_buf.hr_mtt,
-                               uar, hr_cq, vector, 0);
+       ret = hns_roce_cq_alloc(hr_dev, cq_entries, &hr_cq->hr_buf.hr_mtt, uar,
+                               hr_cq, vector);
        if (ret) {
                dev_err(dev, "Creat CQ .Failed to cq_alloc.\n");
                goto err_mtt;
@@ -353,12 +357,15 @@ struct ib_cq *hns_roce_ib_create_cq(struct ib_device *ib_dev,
        if (context) {
                if (ib_copy_to_udata(udata, &hr_cq->cqn, sizeof(u64))) {
                        ret = -EFAULT;
-                       goto err_mtt;
+                       goto err_cqc;
                }
        }
 
        return &hr_cq->ib_cq;
 
+err_cqc:
+       hns_roce_free_cq(hr_dev, hr_cq);
+
 err_mtt:
        hns_roce_mtt_cleanup(hr_dev, &hr_cq->hr_buf.hr_mtt);
        if (context)
index ea73580..3417315 100644 (file)
@@ -62,7 +62,7 @@
 #define HNS_ROCE_AEQE_OF_VEC_NUM               1
 
 /* 4G/4K = 1M */
-#define HNS_ROCE_SL_SHIFT                      29
+#define HNS_ROCE_SL_SHIFT                      28
 #define HNS_ROCE_TCLASS_SHIFT                  20
 #define HNS_ROCE_FLOW_LABLE_MASK               0xfffff
 
@@ -74,7 +74,9 @@
 #define MR_TYPE_DMA                            0x03
 
 #define PKEY_ID                                        0xffff
+#define GUID_LEN                               8
 #define NODE_DESC_SIZE                         64
+#define DB_REG_OFFSET                          0x1000
 
 #define SERV_TYPE_RC                           0
 #define SERV_TYPE_RD                           1
@@ -282,20 +284,11 @@ struct hns_roce_cq_buf {
        struct hns_roce_mtt hr_mtt;
 };
 
-struct hns_roce_cq_resize {
-       struct hns_roce_cq_buf  hr_buf;
-       int                     cqe;
-};
-
 struct hns_roce_cq {
        struct ib_cq                    ib_cq;
        struct hns_roce_cq_buf          hr_buf;
-       /* pointer to store information after resize*/
-       struct hns_roce_cq_resize       *hr_resize_buf;
        spinlock_t                      lock;
-       struct mutex                    resize_mutex;
        struct ib_umem                  *umem;
-       struct ib_umem                  *resize_umem;
        void (*comp)(struct hns_roce_cq *);
        void (*event)(struct hns_roce_cq *, enum hns_roce_event);
 
@@ -408,6 +401,7 @@ struct hns_roce_qp {
        u32                     buff_size;
        struct mutex            mutex;
        u8                      port;
+       u8                      phy_port;
        u8                      sl;
        u8                      resp_depth;
        u8                      state;
@@ -471,7 +465,6 @@ struct hns_roce_caps {
        u32             max_rq_desc_sz; /* 64 */
        int             max_qp_init_rdma;
        int             max_qp_dest_rdma;
-       int             sqp_start;
        int             num_cqs;
        int             max_cqes;
        int             reserved_cqs;
@@ -512,6 +505,8 @@ struct hns_roce_hw {
        void (*write_cqc)(struct hns_roce_dev *hr_dev,
                          struct hns_roce_cq *hr_cq, void *mb_buf, u64 *mtts,
                          dma_addr_t dma_handle, int nent, u32 vector);
+       int (*clear_hem)(struct hns_roce_dev *hr_dev,
+                        struct hns_roce_hem_table *table, int obj);
        int (*query_qp)(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr,
                        int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr);
        int (*modify_qp)(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
@@ -533,7 +528,6 @@ struct hns_roce_dev {
        struct hns_roce_uar     priv_uar;
        const char              *irq_names[HNS_ROCE_MAX_IRQ_NUM];
        spinlock_t              sm_lock;
-       spinlock_t              cq_db_lock;
        spinlock_t              bt_cmd_lock;
        struct hns_roce_ib_iboe iboe;
 
index 98af7fe..21e21b0 100644 (file)
@@ -66,9 +66,6 @@ static void hns_roce_wq_catas_err_handle(struct hns_roce_dev *hr_dev,
 {
        struct device *dev = &hr_dev->pdev->dev;
 
-       qpn = roce_get_field(aeqe->event.qp_event.qp,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
        dev_warn(dev, "Local Work Queue Catastrophic Error.\n");
        switch (roce_get_field(aeqe->asyn, HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_M,
                               HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_S)) {
@@ -96,13 +93,6 @@ static void hns_roce_wq_catas_err_handle(struct hns_roce_dev *hr_dev,
        default:
                break;
        }
-
-       hns_roce_qp_event(hr_dev, roce_get_field(aeqe->event.qp_event.qp,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                         roce_get_field(aeqe->asyn,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
 }
 
 static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
@@ -111,9 +101,6 @@ static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
 {
        struct device *dev = &hr_dev->pdev->dev;
 
-       qpn = roce_get_field(aeqe->event.qp_event.qp,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
        dev_warn(dev, "Local Access Violation Work Queue Error.\n");
        switch (roce_get_field(aeqe->asyn, HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_M,
                               HNS_ROCE_AEQE_U32_4_EVENT_SUB_TYPE_S)) {
@@ -141,13 +128,69 @@ static void hns_roce_local_wq_access_err_handle(struct hns_roce_dev *hr_dev,
        default:
                break;
        }
+}
+
+static void hns_roce_qp_err_handle(struct hns_roce_dev *hr_dev,
+                                  struct hns_roce_aeqe *aeqe,
+                                  int event_type)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       int phy_port;
+       int qpn;
+
+       qpn = roce_get_field(aeqe->event.qp_event.qp,
+                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
+                            HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S);
+       phy_port = roce_get_field(aeqe->event.qp_event.qp,
+                       HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_M,
+                       HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S);
+       if (qpn <= 1)
+               qpn = HNS_ROCE_MAX_PORTS * qpn + phy_port;
+
+       switch (event_type) {
+       case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR:
+               dev_warn(dev, "Invalid Req Local Work Queue Error.\n"
+                             "QP %d, phy_port %d.\n", qpn, phy_port);
+               break;
+       case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR:
+               hns_roce_wq_catas_err_handle(hr_dev, aeqe, qpn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR:
+               hns_roce_local_wq_access_err_handle(hr_dev, aeqe, qpn);
+               break;
+       default:
+               break;
+       }
+
+       hns_roce_qp_event(hr_dev, qpn, event_type);
+}
+
+static void hns_roce_cq_err_handle(struct hns_roce_dev *hr_dev,
+                                  struct hns_roce_aeqe *aeqe,
+                                  int event_type)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       u32 cqn;
+
+       cqn = le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
+                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
+                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
+
+       switch (event_type) {
+       case HNS_ROCE_EVENT_TYPE_CQ_ACCESS_ERROR:
+               dev_warn(dev, "CQ 0x%x access err.\n", cqn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_CQ_OVERFLOW:
+               dev_warn(dev, "CQ 0x%x overflow\n", cqn);
+               break;
+       case HNS_ROCE_EVENT_TYPE_CQ_ID_INVALID:
+               dev_warn(dev, "CQ 0x%x ID invalid.\n", cqn);
+               break;
+       default:
+               break;
+       }
 
-       hns_roce_qp_event(hr_dev, roce_get_field(aeqe->event.qp_event.qp,
-                                        HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                        HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                         roce_get_field(aeqe->asyn,
-                                        HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                        HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
+       hns_roce_cq_event(hr_dev, cqn, event_type);
 }
 
 static void hns_roce_db_overflow_handle(struct hns_roce_dev *hr_dev,
@@ -185,7 +228,7 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
        struct device *dev = &hr_dev->pdev->dev;
        struct hns_roce_aeqe *aeqe;
        int aeqes_found = 0;
-       int qpn = 0;
+       int event_type;
 
        while ((aeqe = next_aeqe_sw(eq))) {
                dev_dbg(dev, "aeqe = %p, aeqe->asyn.event_type = 0x%lx\n", aeqe,
@@ -195,9 +238,10 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                /* Memory barrier */
                rmb();
 
-               switch (roce_get_field(aeqe->asyn,
-                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S)) {
+               event_type = roce_get_field(aeqe->asyn,
+                               HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
+                               HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S);
+               switch (event_type) {
                case HNS_ROCE_EVENT_TYPE_PATH_MIG:
                        dev_warn(dev, "PATH MIG not supported\n");
                        break;
@@ -211,23 +255,9 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                        dev_warn(dev, "PATH MIG failed\n");
                        break;
                case HNS_ROCE_EVENT_TYPE_INV_REQ_LOCAL_WQ_ERROR:
-                       dev_warn(dev, "qpn = 0x%lx\n",
-                       roce_get_field(aeqe->event.qp_event.qp,
-                                      HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                      HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S));
-                       hns_roce_qp_event(hr_dev,
-                               roce_get_field(aeqe->event.qp_event.qp,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M,
-                                       HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S),
-                               roce_get_field(aeqe->asyn,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                       HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_WQ_CATAS_ERROR:
-                       hns_roce_wq_catas_err_handle(hr_dev, aeqe, qpn);
-                       break;
                case HNS_ROCE_EVENT_TYPE_LOCAL_WQ_ACCESS_ERROR:
-                       hns_roce_local_wq_access_err_handle(hr_dev, aeqe, qpn);
+                       hns_roce_qp_err_handle(hr_dev, aeqe, event_type);
                        break;
                case HNS_ROCE_EVENT_TYPE_SRQ_LIMIT_REACH:
                case HNS_ROCE_EVENT_TYPE_SRQ_CATAS_ERROR:
@@ -235,40 +265,9 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                        dev_warn(dev, "SRQ not support!\n");
                        break;
                case HNS_ROCE_EVENT_TYPE_CQ_ACCESS_ERROR:
-                       dev_warn(dev, "CQ 0x%lx access err.\n",
-                       roce_get_field(aeqe->event.cq_event.cq,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_CQ_OVERFLOW:
-                       dev_warn(dev, "CQ 0x%lx overflow\n",
-                       roce_get_field(aeqe->event.cq_event.cq,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                      HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S));
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
-                       break;
                case HNS_ROCE_EVENT_TYPE_CQ_ID_INVALID:
-                       dev_warn(dev, "CQ ID invalid.\n");
-                       hns_roce_cq_event(hr_dev,
-                       le32_to_cpu(roce_get_field(aeqe->event.cq_event.cq,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M,
-                                   HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)),
-                       roce_get_field(aeqe->asyn,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                      HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S));
+                       hns_roce_cq_err_handle(hr_dev, aeqe, event_type);
                        break;
                case HNS_ROCE_EVENT_TYPE_PORT_CHANGE:
                        dev_warn(dev, "port change.\n");
@@ -290,11 +289,8 @@ static int hns_roce_aeq_int(struct hns_roce_dev *hr_dev, struct hns_roce_eq *eq)
                                     HNS_ROCE_AEQE_EVENT_CE_EVENT_CEQE_CEQN_S));
                        break;
                default:
-                       dev_warn(dev, "Unhandled event 0x%lx on EQ %d at index %u\n",
-                                roce_get_field(aeqe->asyn,
-                                             HNS_ROCE_AEQE_U32_4_EVENT_TYPE_M,
-                                             HNS_ROCE_AEQE_U32_4_EVENT_TYPE_S),
-                                eq->eqn, eq->cons_index);
+                       dev_warn(dev, "Unhandled event %d on EQ %d at index %u\n",
+                                event_type, eq->eqn, eq->cons_index);
                        break;
                };
 
index fe43881..c6d212d 100644 (file)
@@ -107,6 +107,10 @@ struct hns_roce_aeqe {
 #define HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_M   \
        (((1UL << 24) - 1) << HNS_ROCE_AEQE_EVENT_QP_EVENT_QP_QPN_S)
 
+#define HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S 25
+#define HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_M   \
+       (((1UL << 3) - 1) << HNS_ROCE_AEQE_EVENT_QP_EVENT_PORT_NUM_S)
+
 #define HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S 0
 #define HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_M   \
        (((1UL << 16) - 1) << HNS_ROCE_AEQE_EVENT_CQ_EVENT_CQ_CQN_S)
index d53d643..250d8f2 100644 (file)
 #include "hns_roce_hem.h"
 #include "hns_roce_common.h"
 
-#define HW_SYNC_TIMEOUT_MSECS          500
-#define HW_SYNC_SLEEP_TIME_INTERVAL    20
-
 #define HNS_ROCE_HEM_ALLOC_SIZE                (1 << 17)
 #define HNS_ROCE_TABLE_CHUNK_SIZE      (1 << 17)
 
 #define DMA_ADDR_T_SHIFT               12
-#define BT_CMD_SYNC_SHIFT              31
 #define BT_BA_SHIFT                    32
 
 struct hns_roce_hem *hns_roce_alloc_hem(struct hns_roce_dev *hr_dev, int npages,
@@ -213,74 +209,6 @@ static int hns_roce_set_hem(struct hns_roce_dev *hr_dev,
        return ret;
 }
 
-static int hns_roce_clear_hem(struct hns_roce_dev *hr_dev,
-                             struct hns_roce_hem_table *table,
-                             unsigned long obj)
-{
-       struct device *dev = &hr_dev->pdev->dev;
-       unsigned long end = 0;
-       unsigned long flags;
-       void __iomem *bt_cmd;
-       uint32_t bt_cmd_val[2];
-       u32 bt_cmd_h_val = 0;
-       int ret = 0;
-
-       switch (table->type) {
-       case HEM_TYPE_QPC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_QPC);
-               break;
-       case HEM_TYPE_MTPT:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
-                              HEM_TYPE_MTPT);
-               break;
-       case HEM_TYPE_CQC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_CQC);
-               break;
-       case HEM_TYPE_SRQC:
-               roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
-                              ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
-                              HEM_TYPE_SRQC);
-               break;
-       default:
-               return ret;
-       }
-       roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_M,
-                      ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_S, obj);
-       roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_S, 0);
-       roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_HW_SYNS_S, 1);
-       roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_M,
-                      ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_S, 0);
-
-       spin_lock_irqsave(&hr_dev->bt_cmd_lock, flags);
-
-       bt_cmd = hr_dev->reg_base + ROCEE_BT_CMD_H_REG;
-
-       end = msecs_to_jiffies(HW_SYNC_TIMEOUT_MSECS) + jiffies;
-       while (1) {
-               if (readl(bt_cmd) >> BT_CMD_SYNC_SHIFT) {
-                       if (!(time_before(jiffies, end))) {
-                               dev_err(dev, "Write bt_cmd err,hw_sync is not zero.\n");
-                               spin_unlock_irqrestore(&hr_dev->bt_cmd_lock,
-                                                      flags);
-                               return -EBUSY;
-                       }
-               } else {
-                       break;
-               }
-               msleep(HW_SYNC_SLEEP_TIME_INTERVAL);
-       }
-
-       bt_cmd_val[0] = 0;
-       bt_cmd_val[1] = bt_cmd_h_val;
-       hns_roce_write64_k(bt_cmd_val, hr_dev->reg_base + ROCEE_BT_CMD_L_REG);
-       spin_unlock_irqrestore(&hr_dev->bt_cmd_lock, flags);
-
-       return ret;
-}
-
 int hns_roce_table_get(struct hns_roce_dev *hr_dev,
                       struct hns_roce_hem_table *table, unsigned long obj)
 {
@@ -333,7 +261,7 @@ void hns_roce_table_put(struct hns_roce_dev *hr_dev,
 
        if (--table->hem[i]->refcount == 0) {
                /* Clear HEM base address */
-               if (hns_roce_clear_hem(hr_dev, table, obj))
+               if (hr_dev->hw->clear_hem(hr_dev, table, obj))
                        dev_warn(dev, "Clear HEM base address failed.\n");
 
                hns_roce_free_hem(hr_dev, table->hem[i]);
@@ -456,7 +384,7 @@ void hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev,
 
        for (i = 0; i < table->num_hem; ++i)
                if (table->hem[i]) {
-                       if (hns_roce_clear_hem(hr_dev, table,
+                       if (hr_dev->hw->clear_hem(hr_dev, table,
                            i * HNS_ROCE_TABLE_CHUNK_SIZE / table->obj_size))
                                dev_err(dev, "Clear HEM base address failed.\n");
 
index ad66175..4357488 100644 (file)
 #ifndef _HNS_ROCE_HEM_H
 #define _HNS_ROCE_HEM_H
 
+#define HW_SYNC_TIMEOUT_MSECS          500
+#define HW_SYNC_SLEEP_TIME_INTERVAL    20
+#define BT_CMD_SYNC_SHIFT              31
+
 enum {
        /* MAP HEM(Hardware Entry Memory) */
        HEM_TYPE_QPC = 0,
index 399f5de..71232e5 100644 (file)
@@ -73,8 +73,14 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
        u32 ind = 0;
        int ret = 0;
 
-       spin_lock_irqsave(&qp->sq.lock, flags);
+       if (unlikely(ibqp->qp_type != IB_QPT_GSI &&
+               ibqp->qp_type != IB_QPT_RC)) {
+               dev_err(dev, "un-supported QP type\n");
+               *bad_wr = NULL;
+               return -EOPNOTSUPP;
+       }
 
+       spin_lock_irqsave(&qp->sq.lock, flags);
        ind = qp->sq_next_wqe;
        for (nreq = 0; wr; ++nreq, wr = wr->next) {
                if (hns_roce_wq_overflow(&qp->sq, nreq, qp->ibqp.send_cq)) {
@@ -162,7 +168,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                        roce_set_field(ud_sq_wqe->u32_36,
                                       UD_SEND_WQE_U32_36_SGID_INDEX_M,
                                       UD_SEND_WQE_U32_36_SGID_INDEX_S,
-                                      hns_get_gid_index(hr_dev, qp->port,
+                                      hns_get_gid_index(hr_dev, qp->phy_port,
                                                         ah->av.gid_index));
 
                        roce_set_field(ud_sq_wqe->u32_40,
@@ -205,8 +211,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                      (wr->send_flags & IB_SEND_FENCE ?
                                      (cpu_to_le32(HNS_ROCE_WQE_FENCE)) : 0);
 
-                       wqe = (struct hns_roce_wqe_ctrl_seg *)wqe +
-                              sizeof(struct hns_roce_wqe_ctrl_seg);
+                       wqe += sizeof(struct hns_roce_wqe_ctrl_seg);
 
                        switch (wr->opcode) {
                        case IB_WR_RDMA_READ:
@@ -235,8 +240,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                break;
                        }
                        ctrl->flag |= cpu_to_le32(ps_opcode);
-                       wqe = (struct hns_roce_wqe_raddr_seg *)wqe +
-                              sizeof(struct hns_roce_wqe_raddr_seg);
+                       wqe += sizeof(struct hns_roce_wqe_raddr_seg);
 
                        dseg = wqe;
                        if (wr->send_flags & IB_SEND_INLINE && wr->num_sge) {
@@ -253,8 +257,7 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                        memcpy(wqe, ((void *) (uintptr_t)
                                               wr->sg_list[i].addr),
                                               wr->sg_list[i].length);
-                                       wqe = (struct hns_roce_wqe_raddr_seg *)
-                                              wqe + wr->sg_list[i].length;
+                                       wqe += wr->sg_list[i].length;
                                }
                                ctrl->flag |= HNS_ROCE_WQE_INLINE;
                        } else {
@@ -266,9 +269,6 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
                                              HNS_ROCE_WQE_SGE_NUM_BIT);
                        }
                        ind++;
-               } else {
-                       dev_dbg(dev, "unSupported QP type\n");
-                       break;
                }
        }
 
@@ -285,7 +285,7 @@ out:
                               SQ_DOORBELL_U32_4_SQ_HEAD_S,
                              (qp->sq.head & ((qp->sq.wqe_cnt << 1) - 1)));
                roce_set_field(sq_db.u32_4, SQ_DOORBELL_U32_4_PORT_M,
-                              SQ_DOORBELL_U32_4_PORT_S, qp->port);
+                              SQ_DOORBELL_U32_4_PORT_S, qp->phy_port);
                roce_set_field(sq_db.u32_8, SQ_DOORBELL_U32_8_QPN_M,
                               SQ_DOORBELL_U32_8_QPN_S, qp->doorbell_qpn);
                roce_set_bit(sq_db.u32_8, SQ_DOORBELL_HW_SYNC_S, 1);
@@ -365,14 +365,14 @@ out:
                        /* SW update GSI rq header */
                        reg_val = roce_read(to_hr_dev(ibqp->device),
                                            ROCEE_QP1C_CFG3_0_REG +
-                                           QP1C_CFGN_OFFSET * hr_qp->port);
+                                           QP1C_CFGN_OFFSET * hr_qp->phy_port);
                        roce_set_field(reg_val,
                                       ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_M,
                                       ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_S,
                                       hr_qp->rq.head);
                        roce_write(to_hr_dev(ibqp->device),
                                   ROCEE_QP1C_CFG3_0_REG +
-                                  QP1C_CFGN_OFFSET * hr_qp->port, reg_val);
+                                  QP1C_CFGN_OFFSET * hr_qp->phy_port, reg_val);
                } else {
                        rq_db.u32_4 = 0;
                        rq_db.u32_8 = 0;
@@ -789,6 +789,66 @@ static void hns_roce_port_enable(struct hns_roce_dev *hr_dev, int enable_flag)
        }
 }
 
+static int hns_roce_bt_init(struct hns_roce_dev *hr_dev)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+       int ret;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       priv->bt_table.qpc_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.qpc_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.qpc_buf.buf)
+               return -ENOMEM;
+
+       priv->bt_table.mtpt_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.mtpt_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.mtpt_buf.buf) {
+               ret = -ENOMEM;
+               goto err_failed_alloc_mtpt_buf;
+       }
+
+       priv->bt_table.cqc_buf.buf = dma_alloc_coherent(dev,
+               HNS_ROCE_BT_RSV_BUF_SIZE, &priv->bt_table.cqc_buf.map,
+               GFP_KERNEL);
+       if (!priv->bt_table.cqc_buf.buf) {
+               ret = -ENOMEM;
+               goto err_failed_alloc_cqc_buf;
+       }
+
+       return 0;
+
+err_failed_alloc_cqc_buf:
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.mtpt_buf.buf, priv->bt_table.mtpt_buf.map);
+
+err_failed_alloc_mtpt_buf:
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.qpc_buf.buf, priv->bt_table.qpc_buf.map);
+
+       return ret;
+}
+
+static void hns_roce_bt_free(struct hns_roce_dev *hr_dev)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.cqc_buf.buf, priv->bt_table.cqc_buf.map);
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.mtpt_buf.buf, priv->bt_table.mtpt_buf.map);
+
+       dma_free_coherent(dev, HNS_ROCE_BT_RSV_BUF_SIZE,
+               priv->bt_table.qpc_buf.buf, priv->bt_table.qpc_buf.map);
+}
+
 /**
  * hns_roce_v1_reset - reset RoCE
  * @hr_dev: RoCE device struct pointer
@@ -879,7 +939,6 @@ void hns_roce_v1_profile(struct hns_roce_dev *hr_dev)
        caps->mtt_entry_sz      = HNS_ROCE_V1_MTT_ENTRY_SIZE;
        caps->cq_entry_sz       = HNS_ROCE_V1_CQE_ENTRY_SIZE;
        caps->page_size_cap     = HNS_ROCE_V1_PAGE_SIZE_SUPPORT;
-       caps->sqp_start         = 0;
        caps->reserved_lkey     = 0;
        caps->reserved_pds      = 0;
        caps->reserved_mrws     = 1;
@@ -944,8 +1003,18 @@ int hns_roce_v1_init(struct hns_roce_dev *hr_dev)
 
        hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_UP);
 
+       ret = hns_roce_bt_init(hr_dev);
+       if (ret) {
+               dev_err(dev, "bt init failed!\n");
+               goto error_failed_bt_init;
+       }
+
        return 0;
 
+error_failed_bt_init:
+       hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_DOWN);
+       hns_roce_raq_free(hr_dev);
+
 error_failed_raq_init:
        hns_roce_db_free(hr_dev);
        return ret;
@@ -953,6 +1022,7 @@ error_failed_raq_init:
 
 void hns_roce_v1_exit(struct hns_roce_dev *hr_dev)
 {
+       hns_roce_bt_free(hr_dev);
        hns_roce_port_enable(hr_dev, HNS_ROCE_PORT_DOWN);
        hns_roce_raq_free(hr_dev);
        hns_roce_db_free(hr_dev);
@@ -1192,9 +1262,7 @@ static struct hns_roce_cqe *next_cqe_sw(struct hns_roce_cq *hr_cq)
        return get_sw_cqe(hr_cq, hr_cq->cons_index);
 }
 
-void hns_roce_v1_cq_set_ci(struct hns_roce_cq *hr_cq, u32 cons_index,
-                          spinlock_t *doorbell_lock)
-
+void hns_roce_v1_cq_set_ci(struct hns_roce_cq *hr_cq, u32 cons_index)
 {
        u32 doorbell[2];
 
@@ -1254,8 +1322,7 @@ static void __hns_roce_v1_cq_clean(struct hns_roce_cq *hr_cq, u32 qpn,
                */
                wmb();
 
-               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index,
-                                  &to_hr_dev(hr_cq->ib_cq.device)->cq_db_lock);
+               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index);
        }
 }
 
@@ -1485,7 +1552,8 @@ static int hns_roce_v1_poll_one(struct hns_roce_cq *hr_cq,
                /* SQ conrespond to CQE */
                sq_wqe = get_send_wqe(*cur_qp, roce_get_field(cqe->cqe_byte_4,
                                                CQE_BYTE_4_WQE_INDEX_M,
-                                               CQE_BYTE_4_WQE_INDEX_S));
+                                               CQE_BYTE_4_WQE_INDEX_S)&
+                                               ((*cur_qp)->sq.wqe_cnt-1));
                switch (sq_wqe->flag & HNS_ROCE_WQE_OPCODE_MASK) {
                case HNS_ROCE_WQE_OPCODE_SEND:
                        wc->opcode = IB_WC_SEND;
@@ -1591,10 +1659,8 @@ int hns_roce_v1_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
                        break;
        }
 
-       if (npolled) {
-               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index,
-                                     &to_hr_dev(ibcq->device)->cq_db_lock);
-       }
+       if (npolled)
+               hns_roce_v1_cq_set_ci(hr_cq, hr_cq->cons_index);
 
        spin_unlock_irqrestore(&hr_cq->lock, flags);
 
@@ -1604,6 +1670,74 @@ int hns_roce_v1_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
                return ret;
 }
 
+int hns_roce_v1_clear_hem(struct hns_roce_dev *hr_dev,
+               struct hns_roce_hem_table *table, int obj)
+{
+       struct device *dev = &hr_dev->pdev->dev;
+       struct hns_roce_v1_priv *priv;
+       unsigned long end = 0, flags = 0;
+       uint32_t bt_cmd_val[2] = {0};
+       void __iomem *bt_cmd;
+       u64 bt_ba = 0;
+
+       priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv;
+
+       switch (table->type) {
+       case HEM_TYPE_QPC:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_QPC);
+               bt_ba = priv->bt_table.qpc_buf.map >> 12;
+               break;
+       case HEM_TYPE_MTPT:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_MTPT);
+               bt_ba = priv->bt_table.mtpt_buf.map >> 12;
+               break;
+       case HEM_TYPE_CQC:
+               roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
+                       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_CQC);
+               bt_ba = priv->bt_table.cqc_buf.map >> 12;
+               break;
+       case HEM_TYPE_SRQC:
+               dev_dbg(dev, "HEM_TYPE_SRQC not support.\n");
+               return -EINVAL;
+       default:
+               return 0;
+       }
+       roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_M,
+               ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_S, obj);
+       roce_set_bit(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_S, 0);
+       roce_set_bit(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_HW_SYNS_S, 1);
+
+       spin_lock_irqsave(&hr_dev->bt_cmd_lock, flags);
+
+       bt_cmd = hr_dev->reg_base + ROCEE_BT_CMD_H_REG;
+
+       end = msecs_to_jiffies(HW_SYNC_TIMEOUT_MSECS) + jiffies;
+       while (1) {
+               if (readl(bt_cmd) >> BT_CMD_SYNC_SHIFT) {
+                       if (!(time_before(jiffies, end))) {
+                               dev_err(dev, "Write bt_cmd err,hw_sync is not zero.\n");
+                               spin_unlock_irqrestore(&hr_dev->bt_cmd_lock,
+                                       flags);
+                               return -EBUSY;
+                       }
+               } else {
+                       break;
+               }
+               msleep(HW_SYNC_SLEEP_TIME_INTERVAL);
+       }
+
+       bt_cmd_val[0] = (uint32_t)bt_ba;
+       roce_set_field(bt_cmd_val[1], ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_M,
+               ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_S, bt_ba >> 32);
+       hns_roce_write64_k(bt_cmd_val, hr_dev->reg_base + ROCEE_BT_CMD_L_REG);
+
+       spin_unlock_irqrestore(&hr_dev->bt_cmd_lock, flags);
+
+       return 0;
+}
+
 static int hns_roce_v1_qp_modify(struct hns_roce_dev *hr_dev,
                                 struct hns_roce_mtt *mtt,
                                 enum hns_roce_qp_state cur_state,
@@ -1733,13 +1867,10 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qp1c_bytes_16, QP1C_BYTES_16_RQ_HEAD_M,
                               QP1C_BYTES_16_RQ_HEAD_S, hr_qp->rq.head);
                roce_set_field(context->qp1c_bytes_16, QP1C_BYTES_16_PORT_NUM_M,
-                              QP1C_BYTES_16_PORT_NUM_S, hr_qp->port);
+                              QP1C_BYTES_16_PORT_NUM_S, hr_qp->phy_port);
                roce_set_bit(context->qp1c_bytes_16,
                             QP1C_BYTES_16_SIGNALING_TYPE_S,
                             hr_qp->sq_signal_bits);
-               roce_set_bit(context->qp1c_bytes_16,
-                            QP1C_BYTES_16_LOCAL_ENABLE_E2E_CREDIT_S,
-                            hr_qp->sq_signal_bits);
                roce_set_bit(context->qp1c_bytes_16, QP1C_BYTES_16_RQ_BA_FLG_S,
                             1);
                roce_set_bit(context->qp1c_bytes_16, QP1C_BYTES_16_SQ_BA_FLG_S,
@@ -1784,7 +1915,7 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
 
                /* Copy context to QP1C register */
                addr = (u32 *)(hr_dev->reg_base + ROCEE_QP1C_CFG0_0_REG +
-                       hr_qp->port * sizeof(*context));
+                       hr_qp->phy_port * sizeof(*context));
 
                writel(context->qp1c_bytes_4, addr);
                writel(context->sq_rq_bt_l, addr + 1);
@@ -1795,15 +1926,16 @@ static int hns_roce_v1_m_sqp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                writel(context->qp1c_bytes_28, addr + 6);
                writel(context->qp1c_bytes_32, addr + 7);
                writel(context->cur_sq_wqe_ba_l, addr + 8);
+               writel(context->qp1c_bytes_40, addr + 9);
        }
 
        /* Modify QP1C status */
        reg_val = roce_read(hr_dev, ROCEE_QP1C_CFG0_0_REG +
-                           hr_qp->port * sizeof(*context));
+                           hr_qp->phy_port * sizeof(*context));
        roce_set_field(reg_val, ROCEE_QP1C_CFG0_0_ROCEE_QP1C_QP_ST_M,
                       ROCEE_QP1C_CFG0_0_ROCEE_QP1C_QP_ST_S, new_state);
        roce_write(hr_dev, ROCEE_QP1C_CFG0_0_REG +
-                   hr_qp->port * sizeof(*context), reg_val);
+                   hr_qp->phy_port * sizeof(*context), reg_val);
 
        hr_qp->state = new_state;
        if (new_state == IB_QPS_RESET) {
@@ -1836,12 +1968,10 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
        struct hns_roce_qp *hr_qp = to_hr_qp(ibqp);
        struct device *dev = &hr_dev->pdev->dev;
        struct hns_roce_qp_context *context;
-       struct hns_roce_rq_db rq_db;
        dma_addr_t dma_handle_2 = 0;
        dma_addr_t dma_handle = 0;
        uint32_t doorbell[2] = {0};
        int rq_pa_start = 0;
-       u32 reg_val = 0;
        u64 *mtts_2 = NULL;
        int ret = -EINVAL;
        u64 *mtts = NULL;
@@ -2119,7 +2249,8 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
 
                roce_set_field(context->qpc_bytes_68,
                               QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_M,
-                              QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_S, 0);
+                              QP_CONTEXT_QPC_BYTES_68_RQ_HEAD_S,
+                              hr_qp->rq.head);
                roce_set_field(context->qpc_bytes_68,
                               QP_CONTEXT_QPC_BYTES_68_RQ_CUR_INDEX_M,
                               QP_CONTEXT_QPC_BYTES_68_RQ_CUR_INDEX_S, 0);
@@ -2186,7 +2317,7 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_M,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_S,
-                              hr_qp->port);
+                              hr_qp->phy_port);
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_SL_M,
                               QP_CONTEXT_QPC_BYTES_156_SL_S, attr->ah_attr.sl);
@@ -2257,20 +2388,17 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_bit(context->qpc_bytes_140,
                             QP_CONTEXT_QPC_BYTES_140_RNR_RETRY_FLG_S, 0);
 
-               roce_set_field(context->qpc_bytes_144,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_M,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_S,
-                              attr->qp_state);
-
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_CHECK_FLAG_M,
                               QP_CONTEXT_QPC_BYTES_148_CHECK_FLAG_S, 0);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_M,
-                              QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_S, 0);
+                              QP_CONTEXT_QPC_BYTES_148_RETRY_COUNT_S,
+                              attr->retry_cnt);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_M,
-                              QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_S, 0);
+                              QP_CONTEXT_QPC_BYTES_148_RNR_RETRY_COUNT_S,
+                              attr->rnr_retry);
                roce_set_field(context->qpc_bytes_148,
                               QP_CONTEXT_QPC_BYTES_148_LSN_M,
                               QP_CONTEXT_QPC_BYTES_148_LSN_S, 0x100);
@@ -2281,10 +2409,19 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                               QP_CONTEXT_QPC_BYTES_156_RETRY_COUNT_INIT_M,
                               QP_CONTEXT_QPC_BYTES_156_RETRY_COUNT_INIT_S,
                               attr->retry_cnt);
-               roce_set_field(context->qpc_bytes_156,
-                              QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
-                              QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
-                              attr->timeout);
+               if (attr->timeout < 0x12) {
+                       dev_info(dev, "ack timeout value(0x%x) must bigger than 0x12.\n",
+                                attr->timeout);
+                       roce_set_field(context->qpc_bytes_156,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
+                                      0x12);
+               } else {
+                       roce_set_field(context->qpc_bytes_156,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_M,
+                                      QP_CONTEXT_QPC_BYTES_156_ACK_TIMEOUT_S,
+                                      attr->timeout);
+               }
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_RNR_RETRY_COUNT_INIT_M,
                               QP_CONTEXT_QPC_BYTES_156_RNR_RETRY_COUNT_INIT_S,
@@ -2292,7 +2429,7 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_M,
                               QP_CONTEXT_QPC_BYTES_156_PORT_NUM_S,
-                              hr_qp->port);
+                              hr_qp->phy_port);
                roce_set_field(context->qpc_bytes_156,
                               QP_CONTEXT_QPC_BYTES_156_SL_M,
                               QP_CONTEXT_QPC_BYTES_156_SL_S, attr->ah_attr.sl);
@@ -2357,21 +2494,15 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
                               QP_CONTEXT_QPC_BYTES_188_TX_RETRY_CUR_INDEX_M,
                               QP_CONTEXT_QPC_BYTES_188_TX_RETRY_CUR_INDEX_S,
                               0);
-       } else if ((cur_state == IB_QPS_INIT && new_state == IB_QPS_RESET) ||
+       } else if (!((cur_state == IB_QPS_INIT && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_INIT && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_RTR && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_RTR && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_RTS && new_state == IB_QPS_RESET) ||
                   (cur_state == IB_QPS_RTS && new_state == IB_QPS_ERR) ||
                   (cur_state == IB_QPS_ERR && new_state == IB_QPS_RESET) ||
-                  (cur_state == IB_QPS_ERR && new_state == IB_QPS_ERR)) {
-               roce_set_field(context->qpc_bytes_144,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_M,
-                              QP_CONTEXT_QPC_BYTES_144_QP_STATE_S,
-                              attr->qp_state);
-
-       } else {
-               dev_err(dev, "not support this modify\n");
+                  (cur_state == IB_QPS_ERR && new_state == IB_QPS_ERR))) {
+               dev_err(dev, "not support this status migration\n");
                goto out;
        }
 
@@ -2397,43 +2528,32 @@ static int hns_roce_v1_m_qp(struct ib_qp *ibqp, const struct ib_qp_attr *attr,
        if (cur_state == IB_QPS_INIT && new_state == IB_QPS_INIT) {
                /* Memory barrier */
                wmb();
-               if (hr_qp->ibqp.qp_type == IB_QPT_GSI) {
-                       /* SW update GSI rq header */
-                       reg_val = roce_read(hr_dev, ROCEE_QP1C_CFG3_0_REG +
-                                           QP1C_CFGN_OFFSET * hr_qp->port);
-                       roce_set_field(reg_val,
-                                      ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_M,
-                                      ROCEE_QP1C_CFG3_0_ROCEE_QP1C_RQ_HEAD_S,
-                                      hr_qp->rq.head);
-                       roce_write(hr_dev, ROCEE_QP1C_CFG3_0_REG +
-                                   QP1C_CFGN_OFFSET * hr_qp->port, reg_val);
-               } else {
-                       rq_db.u32_4 = 0;
-                       rq_db.u32_8 = 0;
-
-                       roce_set_field(rq_db.u32_4, RQ_DOORBELL_U32_4_RQ_HEAD_M,
-                                      RQ_DOORBELL_U32_4_RQ_HEAD_S,
-                                      hr_qp->rq.head);
-                       roce_set_field(rq_db.u32_8, RQ_DOORBELL_U32_8_QPN_M,
-                                      RQ_DOORBELL_U32_8_QPN_S, hr_qp->qpn);
-                       roce_set_field(rq_db.u32_8, RQ_DOORBELL_U32_8_CMD_M,
-                                      RQ_DOORBELL_U32_8_CMD_S, 1);
-                       roce_set_bit(rq_db.u32_8, RQ_DOORBELL_U32_8_HW_SYNC_S,
-                                    1);
 
-                       doorbell[0] = rq_db.u32_4;
-                       doorbell[1] = rq_db.u32_8;
-
-                       hns_roce_write64_k(doorbell, hr_qp->rq.db_reg_l);
+               roce_set_field(doorbell[0], RQ_DOORBELL_U32_4_RQ_HEAD_M,
+                              RQ_DOORBELL_U32_4_RQ_HEAD_S, hr_qp->rq.head);
+               roce_set_field(doorbell[1], RQ_DOORBELL_U32_8_QPN_M,
+                              RQ_DOORBELL_U32_8_QPN_S, hr_qp->qpn);
+               roce_set_field(doorbell[1], RQ_DOORBELL_U32_8_CMD_M,
+                              RQ_DOORBELL_U32_8_CMD_S, 1);
+               roce_set_bit(doorbell[1], RQ_DOORBELL_U32_8_HW_SYNC_S, 1);
+
+               if (ibqp->uobject) {
+                       hr_qp->rq.db_reg_l = hr_dev->reg_base +
+                                    ROCEE_DB_OTHERS_L_0_REG +
+                                    DB_REG_OFFSET * hr_dev->priv_uar.index;
                }
+
+               hns_roce_write64_k(doorbell, hr_qp->rq.db_reg_l);
        }
 
        hr_qp->state = new_state;
 
        if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC)
                hr_qp->resp_depth = attr->max_dest_rd_atomic;
-       if (attr_mask & IB_QP_PORT)
-               hr_qp->port = (attr->port_num - 1);
+       if (attr_mask & IB_QP_PORT) {
+               hr_qp->port = attr->port_num - 1;
+               hr_qp->phy_port = hr_dev->iboe.phy_port[hr_qp->port];
+       }
 
        if (new_state == IB_QPS_RESET && !ibqp->uobject) {
                hns_roce_v1_cq_clean(to_hr_cq(ibqp->recv_cq), hr_qp->qpn,
@@ -2789,6 +2909,7 @@ struct hns_roce_hw hns_roce_hw_v1 = {
        .set_mtu = hns_roce_v1_set_mtu,
        .write_mtpt = hns_roce_v1_write_mtpt,
        .write_cqc = hns_roce_v1_write_cqc,
+       .clear_hem = hns_roce_v1_clear_hem,
        .modify_qp = hns_roce_v1_modify_qp,
        .query_qp = hns_roce_v1_query_qp,
        .destroy_qp = hns_roce_v1_destroy_qp,
index 316b592..539b0a3 100644 (file)
 #define HNS_ROCE_V1_EXT_ODB_ALFUL      \
        (HNS_ROCE_V1_EXT_ODB_DEPTH - HNS_ROCE_V1_DB_RSVD)
 
+#define HNS_ROCE_BT_RSV_BUF_SIZE                       (1 << 17)
+
 #define HNS_ROCE_ODB_POLL_MODE                         0
 
 #define HNS_ROCE_SDB_NORMAL_MODE                       0
@@ -971,9 +973,16 @@ struct hns_roce_db_table {
        struct hns_roce_ext_db *ext_db;
 };
 
+struct hns_roce_bt_table {
+       struct hns_roce_buf_list qpc_buf;
+       struct hns_roce_buf_list mtpt_buf;
+       struct hns_roce_buf_list cqc_buf;
+};
+
 struct hns_roce_v1_priv {
        struct hns_roce_db_table  db_table;
        struct hns_roce_raq_table raq_table;
+       struct hns_roce_bt_table  bt_table;
 };
 
 int hns_dsaf_roce_reset(struct fwnode_handle *dsaf_fwnode, bool dereset);
index f64f0dd..764e35a 100644 (file)
@@ -355,8 +355,7 @@ static int hns_roce_query_device(struct ib_device *ib_dev,
        props->max_qp = hr_dev->caps.num_qps;
        props->max_qp_wr = hr_dev->caps.max_wqes;
        props->device_cap_flags = IB_DEVICE_PORT_ACTIVE_EVENT |
-                                 IB_DEVICE_RC_RNR_NAK_GEN |
-                                 IB_DEVICE_LOCAL_DMA_LKEY;
+                                 IB_DEVICE_RC_RNR_NAK_GEN;
        props->max_sge = hr_dev->caps.max_sq_sg;
        props->max_sge_rd = 1;
        props->max_cq = hr_dev->caps.num_cqs;
@@ -372,6 +371,25 @@ static int hns_roce_query_device(struct ib_device *ib_dev,
        return 0;
 }
 
+static struct net_device *hns_roce_get_netdev(struct ib_device *ib_dev,
+                                             u8 port_num)
+{
+       struct hns_roce_dev *hr_dev = to_hr_dev(ib_dev);
+       struct net_device *ndev;
+
+       if (port_num < 1 || port_num > hr_dev->caps.num_ports)
+               return NULL;
+
+       rcu_read_lock();
+
+       ndev = hr_dev->iboe.netdevs[port_num - 1];
+       if (ndev)
+               dev_hold(ndev);
+
+       rcu_read_unlock();
+       return ndev;
+}
+
 static int hns_roce_query_port(struct ib_device *ib_dev, u8 port_num,
                               struct ib_port_attr *props)
 {
@@ -584,6 +602,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
        struct device *dev = &hr_dev->pdev->dev;
 
        iboe = &hr_dev->iboe;
+       spin_lock_init(&iboe->lock);
 
        ib_dev = &hr_dev->ib_dev;
        strlcpy(ib_dev->name, "hisi_%d", IB_DEVICE_NAME_MAX);
@@ -618,6 +637,7 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
        ib_dev->query_port              = hns_roce_query_port;
        ib_dev->modify_port             = hns_roce_modify_port;
        ib_dev->get_link_layer          = hns_roce_get_link_layer;
+       ib_dev->get_netdev              = hns_roce_get_netdev;
        ib_dev->query_gid               = hns_roce_query_gid;
        ib_dev->query_pkey              = hns_roce_query_pkey;
        ib_dev->alloc_ucontext          = hns_roce_alloc_ucontext;
@@ -667,8 +687,6 @@ static int hns_roce_register_device(struct hns_roce_dev *hr_dev)
                goto error_failed_setup_mtu_gids;
        }
 
-       spin_lock_init(&iboe->lock);
-
        iboe->nb.notifier_call = hns_roce_netdev_event;
        ret = register_netdevice_notifier(&iboe->nb);
        if (ret) {
@@ -777,6 +795,15 @@ static int hns_roce_get_cfg(struct hns_roce_dev *hr_dev)
        if (IS_ERR(hr_dev->reg_base))
                return PTR_ERR(hr_dev->reg_base);
 
+       /* read the node_guid of IB device from the DT or ACPI */
+       ret = device_property_read_u8_array(dev, "node-guid",
+                                           (u8 *)&hr_dev->ib_dev.node_guid,
+                                           GUID_LEN);
+       if (ret) {
+               dev_err(dev, "couldn't get node_guid from DT or ACPI!\n");
+               return ret;
+       }
+
        /* get the RoCE associated ethernet ports or netdevices */
        for (i = 0; i < HNS_ROCE_MAX_PORTS; i++) {
                if (dev_of_node(dev)) {
@@ -923,7 +950,6 @@ static int hns_roce_setup_hca(struct hns_roce_dev *hr_dev)
        struct device *dev = &hr_dev->pdev->dev;
 
        spin_lock_init(&hr_dev->sm_lock);
-       spin_lock_init(&hr_dev->cq_db_lock);
        spin_lock_init(&hr_dev->bt_cmd_lock);
 
        ret = hns_roce_init_uar_table(hr_dev);
index 59f5e2b..fb87883 100644 (file)
@@ -564,11 +564,14 @@ struct ib_mr *hns_roce_reg_user_mr(struct ib_pd *pd, u64 start, u64 length,
        if (mr->umem->page_size != HNS_ROCE_HEM_PAGE_SIZE) {
                dev_err(dev, "Just support 4K page size but is 0x%x now!\n",
                        mr->umem->page_size);
+               ret = -EINVAL;
+               goto err_umem;
        }
 
        if (n > HNS_ROCE_MAX_MTPT_PBL_NUM) {
                dev_err(dev, " MR len %lld err. MR is limited to 4G at most!\n",
                        length);
+               ret = -EINVAL;
                goto err_umem;
        }
 
index 16271b5..05db7d5 100644 (file)
 
 static int hns_roce_pd_alloc(struct hns_roce_dev *hr_dev, unsigned long *pdn)
 {
-       struct device *dev = &hr_dev->pdev->dev;
-       unsigned long pd_number;
-       int ret = 0;
-
-       ret = hns_roce_bitmap_alloc(&hr_dev->pd_bitmap, &pd_number);
-       if (ret == -1) {
-               dev_err(dev, "alloc pdn from pdbitmap failed\n");
-               return -ENOMEM;
-       }
-
-       *pdn = pd_number;
-
-       return 0;
+       return hns_roce_bitmap_alloc(&hr_dev->pd_bitmap, pdn);
 }
 
 static void hns_roce_pd_free(struct hns_roce_dev *hr_dev, unsigned long pdn)
@@ -117,9 +105,15 @@ int hns_roce_uar_alloc(struct hns_roce_dev *hr_dev, struct hns_roce_uar *uar)
        if (ret == -1)
                return -ENOMEM;
 
-       uar->index = (uar->index - 1) % hr_dev->caps.phy_num_uars + 1;
+       if (uar->index > 0)
+               uar->index = (uar->index - 1) %
+                            (hr_dev->caps.phy_num_uars - 1) + 1;
 
        res = platform_get_resource(hr_dev->pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&hr_dev->pdev->dev, "memory resource not found!\n");
+               return -EINVAL;
+       }
        uar->pfn = ((res->start) >> PAGE_SHIFT) + uar->index;
 
        return 0;
index 645c18d..e86dd8d 100644 (file)
  */
 
 #include <linux/platform_device.h>
+#include <rdma/ib_addr.h>
 #include <rdma/ib_umem.h>
 #include "hns_roce_common.h"
 #include "hns_roce_device.h"
 #include "hns_roce_hem.h"
 #include "hns_roce_user.h"
 
-#define DB_REG_OFFSET                  0x1000
-#define SQP_NUM                                12
+#define SQP_NUM                                (2 * HNS_ROCE_MAX_PORTS)
 
 void hns_roce_qp_event(struct hns_roce_dev *hr_dev, u32 qpn, int event_type)
 {
@@ -113,16 +113,8 @@ static int hns_roce_reserve_range_qp(struct hns_roce_dev *hr_dev, int cnt,
                                     int align, unsigned long *base)
 {
        struct hns_roce_qp_table *qp_table = &hr_dev->qp_table;
-       int ret = 0;
-       unsigned long qpn;
-
-       ret = hns_roce_bitmap_alloc_range(&qp_table->bitmap, cnt, align, &qpn);
-       if (ret == -1)
-               return -ENOMEM;
-
-       *base = qpn;
 
-       return 0;
+       return hns_roce_bitmap_alloc_range(&qp_table->bitmap, cnt, align, base);
 }
 
 enum hns_roce_qp_state to_hns_roce_state(enum ib_qp_state state)
@@ -255,7 +247,7 @@ void hns_roce_release_range_qp(struct hns_roce_dev *hr_dev, int base_qpn,
 {
        struct hns_roce_qp_table *qp_table = &hr_dev->qp_table;
 
-       if (base_qpn < (hr_dev->caps.sqp_start + 2 * hr_dev->caps.num_ports))
+       if (base_qpn < SQP_NUM)
                return;
 
        hns_roce_bitmap_free_range(&qp_table->bitmap, base_qpn, cnt);
@@ -345,12 +337,10 @@ static int hns_roce_set_user_sq_size(struct hns_roce_dev *hr_dev,
 
 static int hns_roce_set_kernel_sq_size(struct hns_roce_dev *hr_dev,
                                       struct ib_qp_cap *cap,
-                                      enum ib_qp_type type,
                                       struct hns_roce_qp *hr_qp)
 {
        struct device *dev = &hr_dev->pdev->dev;
        u32 max_cnt;
-       (void)type;
 
        if (cap->max_send_wr  > hr_dev->caps.max_wqes  ||
            cap->max_send_sge > hr_dev->caps.max_sq_sg ||
@@ -476,7 +466,7 @@ static int hns_roce_create_qp_common(struct hns_roce_dev *hr_dev,
 
                /* Set SQ size */
                ret = hns_roce_set_kernel_sq_size(hr_dev, &init_attr->cap,
-                                                 init_attr->qp_type, hr_qp);
+                                                 hr_qp);
                if (ret) {
                        dev_err(dev, "hns_roce_set_kernel_sq_size error!\n");
                        goto err_out;
@@ -617,21 +607,19 @@ struct ib_qp *hns_roce_create_qp(struct ib_pd *pd,
                        return ERR_PTR(-ENOMEM);
 
                hr_qp = &hr_sqp->hr_qp;
+               hr_qp->port = init_attr->port_num - 1;
+               hr_qp->phy_port = hr_dev->iboe.phy_port[hr_qp->port];
+               hr_qp->ibqp.qp_num = HNS_ROCE_MAX_PORTS +
+                                    hr_dev->iboe.phy_port[hr_qp->port];
 
                ret = hns_roce_create_qp_common(hr_dev, pd, init_attr, udata,
-                                               hr_dev->caps.sqp_start +
-                                               hr_dev->caps.num_ports +
-                                               init_attr->port_num - 1, hr_qp);
+                                               hr_qp->ibqp.qp_num, hr_qp);
                if (ret) {
                        dev_err(dev, "Create GSI QP failed!\n");
                        kfree(hr_sqp);
                        return ERR_PTR(ret);
                }
 
-               hr_qp->port = (init_attr->port_num - 1);
-               hr_qp->ibqp.qp_num = hr_dev->caps.sqp_start +
-                                    hr_dev->caps.num_ports +
-                                    init_attr->port_num - 1;
                break;
        }
        default:{
@@ -670,6 +658,7 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
        struct device *dev = &hr_dev->pdev->dev;
        int ret = -EINVAL;
        int p;
+       enum ib_mtu active_mtu;
 
        mutex_lock(&hr_qp->mutex);
 
@@ -700,6 +689,19 @@ int hns_roce_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
                }
        }
 
+       if (attr_mask & IB_QP_PATH_MTU) {
+               p = attr_mask & IB_QP_PORT ? (attr->port_num - 1) : hr_qp->port;
+               active_mtu = iboe_get_mtu(hr_dev->iboe.netdevs[p]->mtu);
+
+               if (attr->path_mtu > IB_MTU_2048 ||
+                   attr->path_mtu < IB_MTU_256 ||
+                   attr->path_mtu > active_mtu) {
+                       dev_err(dev, "attr path_mtu(%d)invalid while modify qp",
+                               attr->path_mtu);
+                       goto out;
+               }
+       }
+
        if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC &&
            attr->max_rd_atomic > hr_dev->caps.max_qp_init_rdma) {
                dev_err(dev, "attr max_rd_atomic invalid.attr->max_rd_atomic=%d\n",
@@ -782,29 +784,11 @@ static void *get_wqe(struct hns_roce_qp *hr_qp, int offset)
 
 void *get_recv_wqe(struct hns_roce_qp *hr_qp, int n)
 {
-       struct ib_qp *ibqp = &hr_qp->ibqp;
-       struct hns_roce_dev *hr_dev = to_hr_dev(ibqp->device);
-
-       if ((n < 0) || (n > hr_qp->rq.wqe_cnt)) {
-               dev_err(&hr_dev->pdev->dev, "rq wqe index:%d,rq wqe cnt:%d\r\n",
-                       n, hr_qp->rq.wqe_cnt);
-               return NULL;
-       }
-
        return get_wqe(hr_qp, hr_qp->rq.offset + (n << hr_qp->rq.wqe_shift));
 }
 
 void *get_send_wqe(struct hns_roce_qp *hr_qp, int n)
 {
-       struct ib_qp *ibqp = &hr_qp->ibqp;
-       struct hns_roce_dev *hr_dev = to_hr_dev(ibqp->device);
-
-       if ((n < 0) || (n > hr_qp->sq.wqe_cnt)) {
-               dev_err(&hr_dev->pdev->dev, "sq wqe index:%d,sq wqe cnt:%d\r\n",
-                       n, hr_qp->sq.wqe_cnt);
-               return NULL;
-       }
-
        return get_wqe(hr_qp, hr_qp->sq.offset + (n << hr_qp->sq.wqe_shift));
 }
 
@@ -837,8 +821,7 @@ int hns_roce_init_qp_table(struct hns_roce_dev *hr_dev)
 
        /* A port include two SQP, six port total 12 */
        ret = hns_roce_bitmap_init(&qp_table->bitmap, hr_dev->caps.num_qps,
-                                  hr_dev->caps.num_qps - 1,
-                                  hr_dev->caps.sqp_start + SQP_NUM,
+                                  hr_dev->caps.num_qps - 1, SQP_NUM,
                                   reserved_from_top);
        if (ret) {
                dev_err(&hr_dev->pdev->dev, "qp bitmap init failed!error=%d\n",
diff --git a/drivers/infiniband/hw/qedr/Kconfig b/drivers/infiniband/hw/qedr/Kconfig
new file mode 100644 (file)
index 0000000..7c06d85
--- /dev/null
@@ -0,0 +1,7 @@
+config INFINIBAND_QEDR
+       tristate "QLogic RoCE driver"
+       depends on 64BIT && QEDE
+       select QED_LL2
+       ---help---
+         This driver provides low-level InfiniBand over Ethernet
+         support for QLogic QED host channel adapters (HCAs).
diff --git a/drivers/infiniband/hw/qedr/Makefile b/drivers/infiniband/hw/qedr/Makefile
new file mode 100644 (file)
index 0000000..ba7067c
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_INFINIBAND_QEDR) := qedr.o
+
+qedr-y := main.o verbs.o qedr_cm.o
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c
new file mode 100644 (file)
index 0000000..7b74d09
--- /dev/null
@@ -0,0 +1,914 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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/module.h>
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_user_verbs.h>
+#include <linux/netdevice.h>
+#include <linux/iommu.h>
+#include <net/addrconf.h>
+#include <linux/qed/qede_roce.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_if.h>
+#include "qedr.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+
+MODULE_DESCRIPTION("QLogic 40G/100G ROCE Driver");
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_VERSION(QEDR_MODULE_VERSION);
+
+#define QEDR_WQ_MULTIPLIER_DFT (3)
+
+void qedr_ib_dispatch_event(struct qedr_dev *dev, u8 port_num,
+                           enum ib_event_type type)
+{
+       struct ib_event ibev;
+
+       ibev.device = &dev->ibdev;
+       ibev.element.port_num = port_num;
+       ibev.event = type;
+
+       ib_dispatch_event(&ibev);
+}
+
+static enum rdma_link_layer qedr_link_layer(struct ib_device *device,
+                                           u8 port_num)
+{
+       return IB_LINK_LAYER_ETHERNET;
+}
+
+static void qedr_get_dev_fw_str(struct ib_device *ibdev, char *str,
+                               size_t str_len)
+{
+       struct qedr_dev *qedr = get_qedr_dev(ibdev);
+       u32 fw_ver = (u32)qedr->attr.fw_ver;
+
+       snprintf(str, str_len, "%d. %d. %d. %d",
+                (fw_ver >> 24) & 0xFF, (fw_ver >> 16) & 0xFF,
+                (fw_ver >> 8) & 0xFF, fw_ver & 0xFF);
+}
+
+static struct net_device *qedr_get_netdev(struct ib_device *dev, u8 port_num)
+{
+       struct qedr_dev *qdev;
+
+       qdev = get_qedr_dev(dev);
+       dev_hold(qdev->ndev);
+
+       /* The HW vendor's device driver must guarantee
+        * that this function returns NULL before the net device reaches
+        * NETDEV_UNREGISTER_FINAL state.
+        */
+       return qdev->ndev;
+}
+
+static int qedr_register_device(struct qedr_dev *dev)
+{
+       strlcpy(dev->ibdev.name, "qedr%d", IB_DEVICE_NAME_MAX);
+
+       dev->ibdev.node_guid = dev->attr.node_guid;
+       memcpy(dev->ibdev.node_desc, QEDR_NODE_DESC, sizeof(QEDR_NODE_DESC));
+       dev->ibdev.owner = THIS_MODULE;
+       dev->ibdev.uverbs_abi_ver = QEDR_ABI_VERSION;
+
+       dev->ibdev.uverbs_cmd_mask = QEDR_UVERBS(GET_CONTEXT) |
+                                    QEDR_UVERBS(QUERY_DEVICE) |
+                                    QEDR_UVERBS(QUERY_PORT) |
+                                    QEDR_UVERBS(ALLOC_PD) |
+                                    QEDR_UVERBS(DEALLOC_PD) |
+                                    QEDR_UVERBS(CREATE_COMP_CHANNEL) |
+                                    QEDR_UVERBS(CREATE_CQ) |
+                                    QEDR_UVERBS(RESIZE_CQ) |
+                                    QEDR_UVERBS(DESTROY_CQ) |
+                                    QEDR_UVERBS(REQ_NOTIFY_CQ) |
+                                    QEDR_UVERBS(CREATE_QP) |
+                                    QEDR_UVERBS(MODIFY_QP) |
+                                    QEDR_UVERBS(QUERY_QP) |
+                                    QEDR_UVERBS(DESTROY_QP) |
+                                    QEDR_UVERBS(REG_MR) |
+                                    QEDR_UVERBS(DEREG_MR) |
+                                    QEDR_UVERBS(POLL_CQ) |
+                                    QEDR_UVERBS(POST_SEND) |
+                                    QEDR_UVERBS(POST_RECV);
+
+       dev->ibdev.phys_port_cnt = 1;
+       dev->ibdev.num_comp_vectors = dev->num_cnq;
+       dev->ibdev.node_type = RDMA_NODE_IB_CA;
+
+       dev->ibdev.query_device = qedr_query_device;
+       dev->ibdev.query_port = qedr_query_port;
+       dev->ibdev.modify_port = qedr_modify_port;
+
+       dev->ibdev.query_gid = qedr_query_gid;
+       dev->ibdev.add_gid = qedr_add_gid;
+       dev->ibdev.del_gid = qedr_del_gid;
+
+       dev->ibdev.alloc_ucontext = qedr_alloc_ucontext;
+       dev->ibdev.dealloc_ucontext = qedr_dealloc_ucontext;
+       dev->ibdev.mmap = qedr_mmap;
+
+       dev->ibdev.alloc_pd = qedr_alloc_pd;
+       dev->ibdev.dealloc_pd = qedr_dealloc_pd;
+
+       dev->ibdev.create_cq = qedr_create_cq;
+       dev->ibdev.destroy_cq = qedr_destroy_cq;
+       dev->ibdev.resize_cq = qedr_resize_cq;
+       dev->ibdev.req_notify_cq = qedr_arm_cq;
+
+       dev->ibdev.create_qp = qedr_create_qp;
+       dev->ibdev.modify_qp = qedr_modify_qp;
+       dev->ibdev.query_qp = qedr_query_qp;
+       dev->ibdev.destroy_qp = qedr_destroy_qp;
+
+       dev->ibdev.query_pkey = qedr_query_pkey;
+
+       dev->ibdev.create_ah = qedr_create_ah;
+       dev->ibdev.destroy_ah = qedr_destroy_ah;
+
+       dev->ibdev.get_dma_mr = qedr_get_dma_mr;
+       dev->ibdev.dereg_mr = qedr_dereg_mr;
+       dev->ibdev.reg_user_mr = qedr_reg_user_mr;
+       dev->ibdev.alloc_mr = qedr_alloc_mr;
+       dev->ibdev.map_mr_sg = qedr_map_mr_sg;
+
+       dev->ibdev.poll_cq = qedr_poll_cq;
+       dev->ibdev.post_send = qedr_post_send;
+       dev->ibdev.post_recv = qedr_post_recv;
+
+       dev->ibdev.process_mad = qedr_process_mad;
+       dev->ibdev.get_port_immutable = qedr_port_immutable;
+       dev->ibdev.get_netdev = qedr_get_netdev;
+
+       dev->ibdev.dma_device = &dev->pdev->dev;
+
+       dev->ibdev.get_link_layer = qedr_link_layer;
+       dev->ibdev.get_dev_fw_str = qedr_get_dev_fw_str;
+
+       return ib_register_device(&dev->ibdev, NULL);
+}
+
+/* This function allocates fast-path status block memory */
+static int qedr_alloc_mem_sb(struct qedr_dev *dev,
+                            struct qed_sb_info *sb_info, u16 sb_id)
+{
+       struct status_block *sb_virt;
+       dma_addr_t sb_phys;
+       int rc;
+
+       sb_virt = dma_alloc_coherent(&dev->pdev->dev,
+                                    sizeof(*sb_virt), &sb_phys, GFP_KERNEL);
+       if (!sb_virt)
+               return -ENOMEM;
+
+       rc = dev->ops->common->sb_init(dev->cdev, sb_info,
+                                      sb_virt, sb_phys, sb_id,
+                                      QED_SB_TYPE_CNQ);
+       if (rc) {
+               pr_err("Status block initialization failed\n");
+               dma_free_coherent(&dev->pdev->dev, sizeof(*sb_virt),
+                                 sb_virt, sb_phys);
+               return rc;
+       }
+
+       return 0;
+}
+
+static void qedr_free_mem_sb(struct qedr_dev *dev,
+                            struct qed_sb_info *sb_info, int sb_id)
+{
+       if (sb_info->sb_virt) {
+               dev->ops->common->sb_release(dev->cdev, sb_info, sb_id);
+               dma_free_coherent(&dev->pdev->dev, sizeof(*sb_info->sb_virt),
+                                 (void *)sb_info->sb_virt, sb_info->sb_phys);
+       }
+}
+
+static void qedr_free_resources(struct qedr_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < dev->num_cnq; i++) {
+               qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+               dev->ops->common->chain_free(dev->cdev, &dev->cnq_array[i].pbl);
+       }
+
+       kfree(dev->cnq_array);
+       kfree(dev->sb_array);
+       kfree(dev->sgid_tbl);
+}
+
+static int qedr_alloc_resources(struct qedr_dev *dev)
+{
+       struct qedr_cnq *cnq;
+       __le16 *cons_pi;
+       u16 n_entries;
+       int i, rc;
+
+       dev->sgid_tbl = kzalloc(sizeof(union ib_gid) *
+                               QEDR_MAX_SGID, GFP_KERNEL);
+       if (!dev->sgid_tbl)
+               return -ENOMEM;
+
+       spin_lock_init(&dev->sgid_lock);
+
+       /* Allocate Status blocks for CNQ */
+       dev->sb_array = kcalloc(dev->num_cnq, sizeof(*dev->sb_array),
+                               GFP_KERNEL);
+       if (!dev->sb_array) {
+               rc = -ENOMEM;
+               goto err1;
+       }
+
+       dev->cnq_array = kcalloc(dev->num_cnq,
+                                sizeof(*dev->cnq_array), GFP_KERNEL);
+       if (!dev->cnq_array) {
+               rc = -ENOMEM;
+               goto err2;
+       }
+
+       dev->sb_start = dev->ops->rdma_get_start_sb(dev->cdev);
+
+       /* Allocate CNQ PBLs */
+       n_entries = min_t(u32, QED_RDMA_MAX_CNQ_SIZE, QEDR_ROCE_MAX_CNQ_SIZE);
+       for (i = 0; i < dev->num_cnq; i++) {
+               cnq = &dev->cnq_array[i];
+
+               rc = qedr_alloc_mem_sb(dev, &dev->sb_array[i],
+                                      dev->sb_start + i);
+               if (rc)
+                       goto err3;
+
+               rc = dev->ops->common->chain_alloc(dev->cdev,
+                                                  QED_CHAIN_USE_TO_CONSUME,
+                                                  QED_CHAIN_MODE_PBL,
+                                                  QED_CHAIN_CNT_TYPE_U16,
+                                                  n_entries,
+                                                  sizeof(struct regpair *),
+                                                  &cnq->pbl);
+               if (rc)
+                       goto err4;
+
+               cnq->dev = dev;
+               cnq->sb = &dev->sb_array[i];
+               cons_pi = dev->sb_array[i].sb_virt->pi_array;
+               cnq->hw_cons_ptr = &cons_pi[QED_ROCE_PROTOCOL_INDEX];
+               cnq->index = i;
+               sprintf(cnq->name, "qedr%d@pci:%s", i, pci_name(dev->pdev));
+
+               DP_DEBUG(dev, QEDR_MSG_INIT, "cnq[%d].cons=%d\n",
+                        i, qed_chain_get_cons_idx(&cnq->pbl));
+       }
+
+       return 0;
+err4:
+       qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+err3:
+       for (--i; i >= 0; i--) {
+               dev->ops->common->chain_free(dev->cdev, &dev->cnq_array[i].pbl);
+               qedr_free_mem_sb(dev, &dev->sb_array[i], dev->sb_start + i);
+       }
+       kfree(dev->cnq_array);
+err2:
+       kfree(dev->sb_array);
+err1:
+       kfree(dev->sgid_tbl);
+       return rc;
+}
+
+/* QEDR sysfs interface */
+static ssize_t show_rev(struct device *device, struct device_attribute *attr,
+                       char *buf)
+{
+       struct qedr_dev *dev = dev_get_drvdata(device);
+
+       return scnprintf(buf, PAGE_SIZE, "0x%x\n", dev->pdev->vendor);
+}
+
+static ssize_t show_hca_type(struct device *device,
+                            struct device_attribute *attr, char *buf)
+{
+       return scnprintf(buf, PAGE_SIZE, "%s\n", "HCA_TYPE_TO_SET");
+}
+
+static DEVICE_ATTR(hw_rev, S_IRUGO, show_rev, NULL);
+static DEVICE_ATTR(hca_type, S_IRUGO, show_hca_type, NULL);
+
+static struct device_attribute *qedr_attributes[] = {
+       &dev_attr_hw_rev,
+       &dev_attr_hca_type
+};
+
+static void qedr_remove_sysfiles(struct qedr_dev *dev)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(qedr_attributes); i++)
+               device_remove_file(&dev->ibdev.dev, qedr_attributes[i]);
+}
+
+static void qedr_pci_set_atomic(struct qedr_dev *dev, struct pci_dev *pdev)
+{
+       struct pci_dev *bridge;
+       u32 val;
+
+       dev->atomic_cap = IB_ATOMIC_NONE;
+
+       bridge = pdev->bus->self;
+       if (!bridge)
+               return;
+
+       /* Check whether we are connected directly or via a switch */
+       while (bridge && bridge->bus->parent) {
+               DP_DEBUG(dev, QEDR_MSG_INIT,
+                        "Device is not connected directly to root. bridge->bus->number=%d primary=%d\n",
+                        bridge->bus->number, bridge->bus->primary);
+               /* Need to check Atomic Op Routing Supported all the way to
+                * root complex.
+                */
+               pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &val);
+               if (!(val & PCI_EXP_DEVCAP2_ATOMIC_ROUTE)) {
+                       pcie_capability_clear_word(pdev,
+                                                  PCI_EXP_DEVCTL2,
+                                                  PCI_EXP_DEVCTL2_ATOMIC_REQ);
+                       return;
+               }
+               bridge = bridge->bus->parent->self;
+       }
+       bridge = pdev->bus->self;
+
+       /* according to bridge capability */
+       pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &val);
+       if (val & PCI_EXP_DEVCAP2_ATOMIC_COMP64) {
+               pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2,
+                                        PCI_EXP_DEVCTL2_ATOMIC_REQ);
+               dev->atomic_cap = IB_ATOMIC_GLOB;
+       } else {
+               pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2,
+                                          PCI_EXP_DEVCTL2_ATOMIC_REQ);
+       }
+}
+
+static const struct qed_rdma_ops *qed_ops;
+
+#define HILO_U64(hi, lo)               ((((u64)(hi)) << 32) + (lo))
+
+static irqreturn_t qedr_irq_handler(int irq, void *handle)
+{
+       u16 hw_comp_cons, sw_comp_cons;
+       struct qedr_cnq *cnq = handle;
+       struct regpair *cq_handle;
+       struct qedr_cq *cq;
+
+       qed_sb_ack(cnq->sb, IGU_INT_DISABLE, 0);
+
+       qed_sb_update_sb_idx(cnq->sb);
+
+       hw_comp_cons = le16_to_cpu(*cnq->hw_cons_ptr);
+       sw_comp_cons = qed_chain_get_cons_idx(&cnq->pbl);
+
+       /* Align protocol-index and chain reads */
+       rmb();
+
+       while (sw_comp_cons != hw_comp_cons) {
+               cq_handle = (struct regpair *)qed_chain_consume(&cnq->pbl);
+               cq = (struct qedr_cq *)(uintptr_t)HILO_U64(cq_handle->hi,
+                               cq_handle->lo);
+
+               if (cq == NULL) {
+                       DP_ERR(cnq->dev,
+                              "Received NULL CQ cq_handle->hi=%d cq_handle->lo=%d sw_comp_cons=%d hw_comp_cons=%d\n",
+                              cq_handle->hi, cq_handle->lo, sw_comp_cons,
+                              hw_comp_cons);
+
+                       break;
+               }
+
+               if (cq->sig != QEDR_CQ_MAGIC_NUMBER) {
+                       DP_ERR(cnq->dev,
+                              "Problem with cq signature, cq_handle->hi=%d ch_handle->lo=%d cq=%p\n",
+                              cq_handle->hi, cq_handle->lo, cq);
+                       break;
+               }
+
+               cq->arm_flags = 0;
+
+               if (cq->ibcq.comp_handler)
+                       (*cq->ibcq.comp_handler)
+                               (&cq->ibcq, cq->ibcq.cq_context);
+
+               sw_comp_cons = qed_chain_get_cons_idx(&cnq->pbl);
+
+               cnq->n_comp++;
+
+       }
+
+       qed_ops->rdma_cnq_prod_update(cnq->dev->rdma_ctx, cnq->index,
+                                     sw_comp_cons);
+
+       qed_sb_ack(cnq->sb, IGU_INT_ENABLE, 1);
+
+       return IRQ_HANDLED;
+}
+
+static void qedr_sync_free_irqs(struct qedr_dev *dev)
+{
+       u32 vector;
+       int i;
+
+       for (i = 0; i < dev->int_info.used_cnt; i++) {
+               if (dev->int_info.msix_cnt) {
+                       vector = dev->int_info.msix[i * dev->num_hwfns].vector;
+                       synchronize_irq(vector);
+                       free_irq(vector, &dev->cnq_array[i]);
+               }
+       }
+
+       dev->int_info.used_cnt = 0;
+}
+
+static int qedr_req_msix_irqs(struct qedr_dev *dev)
+{
+       int i, rc = 0;
+
+       if (dev->num_cnq > dev->int_info.msix_cnt) {
+               DP_ERR(dev,
+                      "Interrupt mismatch: %d CNQ queues > %d MSI-x vectors\n",
+                      dev->num_cnq, dev->int_info.msix_cnt);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < dev->num_cnq; i++) {
+               rc = request_irq(dev->int_info.msix[i * dev->num_hwfns].vector,
+                                qedr_irq_handler, 0, dev->cnq_array[i].name,
+                                &dev->cnq_array[i]);
+               if (rc) {
+                       DP_ERR(dev, "Request cnq %d irq failed\n", i);
+                       qedr_sync_free_irqs(dev);
+               } else {
+                       DP_DEBUG(dev, QEDR_MSG_INIT,
+                                "Requested cnq irq for %s [entry %d]. Cookie is at %p\n",
+                                dev->cnq_array[i].name, i,
+                                &dev->cnq_array[i]);
+                       dev->int_info.used_cnt++;
+               }
+       }
+
+       return rc;
+}
+
+static int qedr_setup_irqs(struct qedr_dev *dev)
+{
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_setup_irqs\n");
+
+       /* Learn Interrupt configuration */
+       rc = dev->ops->rdma_set_rdma_int(dev->cdev, dev->num_cnq);
+       if (rc < 0)
+               return rc;
+
+       rc = dev->ops->rdma_get_rdma_int(dev->cdev, &dev->int_info);
+       if (rc) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "get_rdma_int failed\n");
+               return rc;
+       }
+
+       if (dev->int_info.msix_cnt) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "rdma msix_cnt = %d\n",
+                        dev->int_info.msix_cnt);
+               rc = qedr_req_msix_irqs(dev);
+               if (rc)
+                       return rc;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_setup_irqs succeeded\n");
+
+       return 0;
+}
+
+static int qedr_set_device_attr(struct qedr_dev *dev)
+{
+       struct qed_rdma_device *qed_attr;
+       struct qedr_device_attr *attr;
+       u32 page_size;
+
+       /* Part 1 - query core capabilities */
+       qed_attr = dev->ops->rdma_query_device(dev->rdma_ctx);
+
+       /* Part 2 - check capabilities */
+       page_size = ~dev->attr.page_size_caps + 1;
+       if (page_size > PAGE_SIZE) {
+               DP_ERR(dev,
+                      "Kernel PAGE_SIZE is %ld which is smaller than minimum page size (%d) required by qedr\n",
+                      PAGE_SIZE, page_size);
+               return -ENODEV;
+       }
+
+       /* Part 3 - copy and update capabilities */
+       attr = &dev->attr;
+       attr->vendor_id = qed_attr->vendor_id;
+       attr->vendor_part_id = qed_attr->vendor_part_id;
+       attr->hw_ver = qed_attr->hw_ver;
+       attr->fw_ver = qed_attr->fw_ver;
+       attr->node_guid = qed_attr->node_guid;
+       attr->sys_image_guid = qed_attr->sys_image_guid;
+       attr->max_cnq = qed_attr->max_cnq;
+       attr->max_sge = qed_attr->max_sge;
+       attr->max_inline = qed_attr->max_inline;
+       attr->max_sqe = min_t(u32, qed_attr->max_wqe, QEDR_MAX_SQE);
+       attr->max_rqe = min_t(u32, qed_attr->max_wqe, QEDR_MAX_RQE);
+       attr->max_qp_resp_rd_atomic_resc = qed_attr->max_qp_resp_rd_atomic_resc;
+       attr->max_qp_req_rd_atomic_resc = qed_attr->max_qp_req_rd_atomic_resc;
+       attr->max_dev_resp_rd_atomic_resc =
+           qed_attr->max_dev_resp_rd_atomic_resc;
+       attr->max_cq = qed_attr->max_cq;
+       attr->max_qp = qed_attr->max_qp;
+       attr->max_mr = qed_attr->max_mr;
+       attr->max_mr_size = qed_attr->max_mr_size;
+       attr->max_cqe = min_t(u64, qed_attr->max_cqe, QEDR_MAX_CQES);
+       attr->max_mw = qed_attr->max_mw;
+       attr->max_fmr = qed_attr->max_fmr;
+       attr->max_mr_mw_fmr_pbl = qed_attr->max_mr_mw_fmr_pbl;
+       attr->max_mr_mw_fmr_size = qed_attr->max_mr_mw_fmr_size;
+       attr->max_pd = qed_attr->max_pd;
+       attr->max_ah = qed_attr->max_ah;
+       attr->max_pkey = qed_attr->max_pkey;
+       attr->max_srq = qed_attr->max_srq;
+       attr->max_srq_wr = qed_attr->max_srq_wr;
+       attr->dev_caps = qed_attr->dev_caps;
+       attr->page_size_caps = qed_attr->page_size_caps;
+       attr->dev_ack_delay = qed_attr->dev_ack_delay;
+       attr->reserved_lkey = qed_attr->reserved_lkey;
+       attr->bad_pkey_counter = qed_attr->bad_pkey_counter;
+       attr->max_stats_queues = qed_attr->max_stats_queues;
+
+       return 0;
+}
+
+void qedr_unaffiliated_event(void *context,
+                            u8 event_code)
+{
+       pr_err("unaffiliated event not implemented yet\n");
+}
+
+void qedr_affiliated_event(void *context, u8 e_code, void *fw_handle)
+{
+#define EVENT_TYPE_NOT_DEFINED 0
+#define EVENT_TYPE_CQ          1
+#define EVENT_TYPE_QP          2
+       struct qedr_dev *dev = (struct qedr_dev *)context;
+       union event_ring_data *data = fw_handle;
+       u64 roce_handle64 = ((u64)data->roce_handle.hi << 32) +
+                           data->roce_handle.lo;
+       u8 event_type = EVENT_TYPE_NOT_DEFINED;
+       struct ib_event event;
+       struct ib_cq *ibcq;
+       struct ib_qp *ibqp;
+       struct qedr_cq *cq;
+       struct qedr_qp *qp;
+
+       switch (e_code) {
+       case ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR:
+               event.event = IB_EVENT_CQ_ERR;
+               event_type = EVENT_TYPE_CQ;
+               break;
+       case ROCE_ASYNC_EVENT_SQ_DRAINED:
+               event.event = IB_EVENT_SQ_DRAINED;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR:
+               event.event = IB_EVENT_QP_FATAL;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR:
+               event.event = IB_EVENT_QP_REQ_ERR;
+               event_type = EVENT_TYPE_QP;
+               break;
+       case ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR:
+               event.event = IB_EVENT_QP_ACCESS_ERR;
+               event_type = EVENT_TYPE_QP;
+               break;
+       default:
+               DP_ERR(dev, "unsupported event %d on handle=%llx\n", e_code,
+                      roce_handle64);
+       }
+
+       switch (event_type) {
+       case EVENT_TYPE_CQ:
+               cq = (struct qedr_cq *)(uintptr_t)roce_handle64;
+               if (cq) {
+                       ibcq = &cq->ibcq;
+                       if (ibcq->event_handler) {
+                               event.device = ibcq->device;
+                               event.element.cq = ibcq;
+                               ibcq->event_handler(&event, ibcq->cq_context);
+                       }
+               } else {
+                       WARN(1,
+                            "Error: CQ event with NULL pointer ibcq. Handle=%llx\n",
+                            roce_handle64);
+               }
+               DP_ERR(dev, "CQ event %d on hanlde %p\n", e_code, cq);
+               break;
+       case EVENT_TYPE_QP:
+               qp = (struct qedr_qp *)(uintptr_t)roce_handle64;
+               if (qp) {
+                       ibqp = &qp->ibqp;
+                       if (ibqp->event_handler) {
+                               event.device = ibqp->device;
+                               event.element.qp = ibqp;
+                               ibqp->event_handler(&event, ibqp->qp_context);
+                       }
+               } else {
+                       WARN(1,
+                            "Error: QP event with NULL pointer ibqp. Handle=%llx\n",
+                            roce_handle64);
+               }
+               DP_ERR(dev, "QP event %d on hanlde %p\n", e_code, qp);
+               break;
+       default:
+               break;
+       }
+}
+
+static int qedr_init_hw(struct qedr_dev *dev)
+{
+       struct qed_rdma_add_user_out_params out_params;
+       struct qed_rdma_start_in_params *in_params;
+       struct qed_rdma_cnq_params *cur_pbl;
+       struct qed_rdma_events events;
+       dma_addr_t p_phys_table;
+       u32 page_cnt;
+       int rc = 0;
+       int i;
+
+       in_params =  kzalloc(sizeof(*in_params), GFP_KERNEL);
+       if (!in_params) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       in_params->desired_cnq = dev->num_cnq;
+       for (i = 0; i < dev->num_cnq; i++) {
+               cur_pbl = &in_params->cnq_pbl_list[i];
+
+               page_cnt = qed_chain_get_page_cnt(&dev->cnq_array[i].pbl);
+               cur_pbl->num_pbl_pages = page_cnt;
+
+               p_phys_table = qed_chain_get_pbl_phys(&dev->cnq_array[i].pbl);
+               cur_pbl->pbl_ptr = (u64)p_phys_table;
+       }
+
+       events.affiliated_event = qedr_affiliated_event;
+       events.unaffiliated_event = qedr_unaffiliated_event;
+       events.context = dev;
+
+       in_params->events = &events;
+       in_params->cq_mode = QED_RDMA_CQ_MODE_32_BITS;
+       in_params->max_mtu = dev->ndev->mtu;
+       ether_addr_copy(&in_params->mac_addr[0], dev->ndev->dev_addr);
+
+       rc = dev->ops->rdma_init(dev->cdev, in_params);
+       if (rc)
+               goto out;
+
+       rc = dev->ops->rdma_add_user(dev->rdma_ctx, &out_params);
+       if (rc)
+               goto out;
+
+       dev->db_addr = (void *)(uintptr_t)out_params.dpi_addr;
+       dev->db_phys_addr = out_params.dpi_phys_addr;
+       dev->db_size = out_params.dpi_size;
+       dev->dpi = out_params.dpi;
+
+       rc = qedr_set_device_attr(dev);
+out:
+       kfree(in_params);
+       if (rc)
+               DP_ERR(dev, "Init HW Failed rc = %d\n", rc);
+
+       return rc;
+}
+
+void qedr_stop_hw(struct qedr_dev *dev)
+{
+       dev->ops->rdma_remove_user(dev->rdma_ctx, dev->dpi);
+       dev->ops->rdma_stop(dev->rdma_ctx);
+}
+
+static struct qedr_dev *qedr_add(struct qed_dev *cdev, struct pci_dev *pdev,
+                                struct net_device *ndev)
+{
+       struct qed_dev_rdma_info dev_info;
+       struct qedr_dev *dev;
+       int rc = 0, i;
+
+       dev = (struct qedr_dev *)ib_alloc_device(sizeof(*dev));
+       if (!dev) {
+               pr_err("Unable to allocate ib device\n");
+               return NULL;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr add device called\n");
+
+       dev->pdev = pdev;
+       dev->ndev = ndev;
+       dev->cdev = cdev;
+
+       qed_ops = qed_get_rdma_ops();
+       if (!qed_ops) {
+               DP_ERR(dev, "Failed to get qed roce operations\n");
+               goto init_err;
+       }
+
+       dev->ops = qed_ops;
+       rc = qed_ops->fill_dev_info(cdev, &dev_info);
+       if (rc)
+               goto init_err;
+
+       dev->num_hwfns = dev_info.common.num_hwfns;
+       dev->rdma_ctx = dev->ops->rdma_get_rdma_ctx(cdev);
+
+       dev->num_cnq = dev->ops->rdma_get_min_cnq_msix(cdev);
+       if (!dev->num_cnq) {
+               DP_ERR(dev, "not enough CNQ resources.\n");
+               goto init_err;
+       }
+
+       dev->wq_multiplier = QEDR_WQ_MULTIPLIER_DFT;
+
+       qedr_pci_set_atomic(dev, pdev);
+
+       rc = qedr_alloc_resources(dev);
+       if (rc)
+               goto init_err;
+
+       rc = qedr_init_hw(dev);
+       if (rc)
+               goto alloc_err;
+
+       rc = qedr_setup_irqs(dev);
+       if (rc)
+               goto irq_err;
+
+       rc = qedr_register_device(dev);
+       if (rc) {
+               DP_ERR(dev, "Unable to allocate register device\n");
+               goto reg_err;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(qedr_attributes); i++)
+               if (device_create_file(&dev->ibdev.dev, qedr_attributes[i]))
+                       goto sysfs_err;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr driver loaded successfully\n");
+       return dev;
+
+sysfs_err:
+       ib_unregister_device(&dev->ibdev);
+reg_err:
+       qedr_sync_free_irqs(dev);
+irq_err:
+       qedr_stop_hw(dev);
+alloc_err:
+       qedr_free_resources(dev);
+init_err:
+       ib_dealloc_device(&dev->ibdev);
+       DP_ERR(dev, "qedr driver load failed rc=%d\n", rc);
+
+       return NULL;
+}
+
+static void qedr_remove(struct qedr_dev *dev)
+{
+       /* First unregister with stack to stop all the active traffic
+        * of the registered clients.
+        */
+       qedr_remove_sysfiles(dev);
+       ib_unregister_device(&dev->ibdev);
+
+       qedr_stop_hw(dev);
+       qedr_sync_free_irqs(dev);
+       qedr_free_resources(dev);
+       ib_dealloc_device(&dev->ibdev);
+}
+
+static int qedr_close(struct qedr_dev *dev)
+{
+       qedr_ib_dispatch_event(dev, 1, IB_EVENT_PORT_ERR);
+
+       return 0;
+}
+
+static void qedr_shutdown(struct qedr_dev *dev)
+{
+       qedr_close(dev);
+       qedr_remove(dev);
+}
+
+static void qedr_mac_address_change(struct qedr_dev *dev)
+{
+       union ib_gid *sgid = &dev->sgid_tbl[0];
+       u8 guid[8], mac_addr[6];
+       int rc;
+
+       /* Update SGID */
+       ether_addr_copy(&mac_addr[0], dev->ndev->dev_addr);
+       guid[0] = mac_addr[0] ^ 2;
+       guid[1] = mac_addr[1];
+       guid[2] = mac_addr[2];
+       guid[3] = 0xff;
+       guid[4] = 0xfe;
+       guid[5] = mac_addr[3];
+       guid[6] = mac_addr[4];
+       guid[7] = mac_addr[5];
+       sgid->global.subnet_prefix = cpu_to_be64(0xfe80000000000000LL);
+       memcpy(&sgid->raw[8], guid, sizeof(guid));
+
+       /* Update LL2 */
+       rc = dev->ops->roce_ll2_set_mac_filter(dev->cdev,
+                                              dev->gsi_ll2_mac_address,
+                                              dev->ndev->dev_addr);
+
+       ether_addr_copy(dev->gsi_ll2_mac_address, dev->ndev->dev_addr);
+
+       qedr_ib_dispatch_event(dev, 1, IB_EVENT_GID_CHANGE);
+
+       if (rc)
+               DP_ERR(dev, "Error updating mac filter\n");
+}
+
+/* event handling via NIC driver ensures that all the NIC specific
+ * initialization done before RoCE driver notifies
+ * event to stack.
+ */
+static void qedr_notify(struct qedr_dev *dev, enum qede_roce_event event)
+{
+       switch (event) {
+       case QEDE_UP:
+               qedr_ib_dispatch_event(dev, 1, IB_EVENT_PORT_ACTIVE);
+               break;
+       case QEDE_DOWN:
+               qedr_close(dev);
+               break;
+       case QEDE_CLOSE:
+               qedr_shutdown(dev);
+               break;
+       case QEDE_CHANGE_ADDR:
+               qedr_mac_address_change(dev);
+               break;
+       default:
+               pr_err("Event not supported\n");
+       }
+}
+
+static struct qedr_driver qedr_drv = {
+       .name = "qedr_driver",
+       .add = qedr_add,
+       .remove = qedr_remove,
+       .notify = qedr_notify,
+};
+
+static int __init qedr_init_module(void)
+{
+       return qede_roce_register_driver(&qedr_drv);
+}
+
+static void __exit qedr_exit_module(void)
+{
+       qede_roce_unregister_driver(&qedr_drv);
+}
+
+module_init(qedr_init_module);
+module_exit(qedr_exit_module);
diff --git a/drivers/infiniband/hw/qedr/qedr.h b/drivers/infiniband/hw/qedr/qedr.h
new file mode 100644 (file)
index 0000000..620badd
--- /dev/null
@@ -0,0 +1,495 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 __QEDR_H__
+#define __QEDR_H__
+
+#include <linux/pci.h>
+#include <rdma/ib_addr.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_roce_if.h>
+#include <linux/qed/qede_roce.h>
+#include "qedr_hsi.h"
+
+#define QEDR_MODULE_VERSION    "8.10.10.0"
+#define QEDR_NODE_DESC "QLogic 579xx RoCE HCA"
+#define DP_NAME(dev) ((dev)->ibdev.name)
+
+#define DP_DEBUG(dev, module, fmt, ...)                                        \
+       pr_debug("(%s) " module ": " fmt,                               \
+                DP_NAME(dev) ? DP_NAME(dev) : "", ## __VA_ARGS__)
+
+#define QEDR_MSG_INIT "INIT"
+#define QEDR_MSG_MISC "MISC"
+#define QEDR_MSG_CQ   "  CQ"
+#define QEDR_MSG_MR   "  MR"
+#define QEDR_MSG_RQ   "  RQ"
+#define QEDR_MSG_SQ   "  SQ"
+#define QEDR_MSG_QP   "  QP"
+#define QEDR_MSG_GSI  " GSI"
+
+#define QEDR_CQ_MAGIC_NUMBER   (0x11223344)
+
+struct qedr_dev;
+
+struct qedr_cnq {
+       struct qedr_dev         *dev;
+       struct qed_chain        pbl;
+       struct qed_sb_info      *sb;
+       char                    name[32];
+       u64                     n_comp;
+       __le16                  *hw_cons_ptr;
+       u8                      index;
+};
+
+#define QEDR_MAX_SGID 128
+
+struct qedr_device_attr {
+       u32     vendor_id;
+       u32     vendor_part_id;
+       u32     hw_ver;
+       u64     fw_ver;
+       u64     node_guid;
+       u64     sys_image_guid;
+       u8      max_cnq;
+       u8      max_sge;
+       u16     max_inline;
+       u32     max_sqe;
+       u32     max_rqe;
+       u8      max_qp_resp_rd_atomic_resc;
+       u8      max_qp_req_rd_atomic_resc;
+       u64     max_dev_resp_rd_atomic_resc;
+       u32     max_cq;
+       u32     max_qp;
+       u32     max_mr;
+       u64     max_mr_size;
+       u32     max_cqe;
+       u32     max_mw;
+       u32     max_fmr;
+       u32     max_mr_mw_fmr_pbl;
+       u64     max_mr_mw_fmr_size;
+       u32     max_pd;
+       u32     max_ah;
+       u8      max_pkey;
+       u32     max_srq;
+       u32     max_srq_wr;
+       u8      max_srq_sge;
+       u8      max_stats_queues;
+       u32     dev_caps;
+
+       u64     page_size_caps;
+       u8      dev_ack_delay;
+       u32     reserved_lkey;
+       u32     bad_pkey_counter;
+       struct qed_rdma_events events;
+};
+
+struct qedr_dev {
+       struct ib_device        ibdev;
+       struct qed_dev          *cdev;
+       struct pci_dev          *pdev;
+       struct net_device       *ndev;
+
+       enum ib_atomic_cap      atomic_cap;
+
+       void *rdma_ctx;
+       struct qedr_device_attr attr;
+
+       const struct qed_rdma_ops *ops;
+       struct qed_int_info     int_info;
+
+       struct qed_sb_info      *sb_array;
+       struct qedr_cnq         *cnq_array;
+       int                     num_cnq;
+       int                     sb_start;
+
+       void __iomem            *db_addr;
+       u64                     db_phys_addr;
+       u32                     db_size;
+       u16                     dpi;
+
+       union ib_gid *sgid_tbl;
+
+       /* Lock for sgid table */
+       spinlock_t sgid_lock;
+
+       u64                     guid;
+
+       u32                     dp_module;
+       u8                      dp_level;
+       u8                      num_hwfns;
+       uint                    wq_multiplier;
+       u8                      gsi_ll2_mac_address[ETH_ALEN];
+       int                     gsi_qp_created;
+       struct qedr_cq          *gsi_sqcq;
+       struct qedr_cq          *gsi_rqcq;
+       struct qedr_qp          *gsi_qp;
+};
+
+#define QEDR_MAX_SQ_PBL                        (0x8000)
+#define QEDR_MAX_SQ_PBL_ENTRIES                (0x10000 / sizeof(void *))
+#define QEDR_SQE_ELEMENT_SIZE          (sizeof(struct rdma_sq_sge))
+#define QEDR_MAX_SQE_ELEMENTS_PER_SQE  (ROCE_REQ_MAX_SINGLE_SQ_WQE_SIZE / \
+                                        QEDR_SQE_ELEMENT_SIZE)
+#define QEDR_MAX_SQE_ELEMENTS_PER_PAGE ((RDMA_RING_PAGE_SIZE) / \
+                                        QEDR_SQE_ELEMENT_SIZE)
+#define QEDR_MAX_SQE                   ((QEDR_MAX_SQ_PBL_ENTRIES) *\
+                                        (RDMA_RING_PAGE_SIZE) / \
+                                        (QEDR_SQE_ELEMENT_SIZE) /\
+                                        (QEDR_MAX_SQE_ELEMENTS_PER_SQE))
+/* RQ */
+#define QEDR_MAX_RQ_PBL                        (0x2000)
+#define QEDR_MAX_RQ_PBL_ENTRIES                (0x10000 / sizeof(void *))
+#define QEDR_RQE_ELEMENT_SIZE          (sizeof(struct rdma_rq_sge))
+#define QEDR_MAX_RQE_ELEMENTS_PER_RQE  (RDMA_MAX_SGE_PER_RQ_WQE)
+#define QEDR_MAX_RQE_ELEMENTS_PER_PAGE ((RDMA_RING_PAGE_SIZE) / \
+                                        QEDR_RQE_ELEMENT_SIZE)
+#define QEDR_MAX_RQE                   ((QEDR_MAX_RQ_PBL_ENTRIES) *\
+                                        (RDMA_RING_PAGE_SIZE) / \
+                                        (QEDR_RQE_ELEMENT_SIZE) /\
+                                        (QEDR_MAX_RQE_ELEMENTS_PER_RQE))
+
+#define QEDR_CQE_SIZE  (sizeof(union rdma_cqe))
+#define QEDR_MAX_CQE_PBL_SIZE (512 * 1024)
+#define QEDR_MAX_CQE_PBL_ENTRIES (((QEDR_MAX_CQE_PBL_SIZE) / \
+                                 sizeof(u64)) - 1)
+#define QEDR_MAX_CQES ((u32)((QEDR_MAX_CQE_PBL_ENTRIES) * \
+                            (QED_CHAIN_PAGE_SIZE) / QEDR_CQE_SIZE))
+
+#define QEDR_ROCE_MAX_CNQ_SIZE         (0x4000)
+
+#define QEDR_MAX_PORT                  (1)
+
+#define QEDR_UVERBS(CMD_NAME) (1ull << IB_USER_VERBS_CMD_##CMD_NAME)
+
+#define QEDR_ROCE_PKEY_MAX 1
+#define QEDR_ROCE_PKEY_TABLE_LEN 1
+#define QEDR_ROCE_PKEY_DEFAULT 0xffff
+
+struct qedr_pbl {
+       struct list_head list_entry;
+       void *va;
+       dma_addr_t pa;
+};
+
+struct qedr_ucontext {
+       struct ib_ucontext ibucontext;
+       struct qedr_dev *dev;
+       struct qedr_pd *pd;
+       u64 dpi_addr;
+       u64 dpi_phys_addr;
+       u32 dpi_size;
+       u16 dpi;
+
+       struct list_head mm_head;
+
+       /* Lock to protect mm list */
+       struct mutex mm_list_lock;
+};
+
+union db_prod64 {
+       struct rdma_pwm_val32_data data;
+       u64 raw;
+};
+
+enum qedr_cq_type {
+       QEDR_CQ_TYPE_GSI,
+       QEDR_CQ_TYPE_KERNEL,
+       QEDR_CQ_TYPE_USER,
+};
+
+struct qedr_pbl_info {
+       u32 num_pbls;
+       u32 num_pbes;
+       u32 pbl_size;
+       u32 pbe_size;
+       bool two_layered;
+};
+
+struct qedr_userq {
+       struct ib_umem *umem;
+       struct qedr_pbl_info pbl_info;
+       struct qedr_pbl *pbl_tbl;
+       u64 buf_addr;
+       size_t buf_len;
+};
+
+struct qedr_cq {
+       struct ib_cq ibcq;
+
+       enum qedr_cq_type cq_type;
+       u32 sig;
+
+       u16 icid;
+
+       /* Lock to protect completion handler */
+       spinlock_t comp_handler_lock;
+
+       /* Lock to protect multiplem CQ's */
+       spinlock_t cq_lock;
+       u8 arm_flags;
+       struct qed_chain pbl;
+
+       void __iomem *db_addr;
+       union db_prod64 db;
+
+       u8 pbl_toggle;
+       union rdma_cqe *latest_cqe;
+       union rdma_cqe *toggle_cqe;
+
+       u32 cq_cons;
+
+       struct qedr_userq q;
+};
+
+struct qedr_pd {
+       struct ib_pd ibpd;
+       u32 pd_id;
+       struct qedr_ucontext *uctx;
+};
+
+struct qedr_mm {
+       struct {
+               u64 phy_addr;
+               unsigned long len;
+       } key;
+       struct list_head entry;
+};
+
+union db_prod32 {
+       struct rdma_pwm_val16_data data;
+       u32 raw;
+};
+
+struct qedr_qp_hwq_info {
+       /* WQE Elements */
+       struct qed_chain pbl;
+       u64 p_phys_addr_tbl;
+       u32 max_sges;
+
+       /* WQE */
+       u16 prod;
+       u16 cons;
+       u16 wqe_cons;
+       u16 gsi_cons;
+       u16 max_wr;
+
+       /* DB */
+       void __iomem *db;
+       union db_prod32 db_data;
+};
+
+#define QEDR_INC_SW_IDX(p_info, index)                                 \
+       do {                                                            \
+               p_info->index = (p_info->index + 1) &                   \
+                               qed_chain_get_capacity(p_info->pbl)     \
+       } while (0)
+
+enum qedr_qp_err_bitmap {
+       QEDR_QP_ERR_SQ_FULL = 1,
+       QEDR_QP_ERR_RQ_FULL = 2,
+       QEDR_QP_ERR_BAD_SR = 4,
+       QEDR_QP_ERR_BAD_RR = 8,
+       QEDR_QP_ERR_SQ_PBL_FULL = 16,
+       QEDR_QP_ERR_RQ_PBL_FULL = 32,
+};
+
+struct qedr_qp {
+       struct ib_qp ibqp;      /* must be first */
+       struct qedr_dev *dev;
+
+       struct qedr_qp_hwq_info sq;
+       struct qedr_qp_hwq_info rq;
+
+       u32 max_inline_data;
+
+       /* Lock for QP's */
+       spinlock_t q_lock;
+       struct qedr_cq *sq_cq;
+       struct qedr_cq *rq_cq;
+       struct qedr_srq *srq;
+       enum qed_roce_qp_state state;
+       u32 id;
+       struct qedr_pd *pd;
+       enum ib_qp_type qp_type;
+       struct qed_rdma_qp *qed_qp;
+       u32 qp_id;
+       u16 icid;
+       u16 mtu;
+       int sgid_idx;
+       u32 rq_psn;
+       u32 sq_psn;
+       u32 qkey;
+       u32 dest_qp_num;
+
+       /* Relevant to qps created from kernel space only (ULPs) */
+       u8 prev_wqe_size;
+       u16 wqe_cons;
+       u32 err_bitmap;
+       bool signaled;
+
+       /* SQ shadow */
+       struct {
+               u64 wr_id;
+               enum ib_wc_opcode opcode;
+               u32 bytes_len;
+               u8 wqe_size;
+               bool signaled;
+               dma_addr_t icrc_mapping;
+               u32 *icrc;
+               struct qedr_mr *mr;
+       } *wqe_wr_id;
+
+       /* RQ shadow */
+       struct {
+               u64 wr_id;
+               struct ib_sge sg_list[RDMA_MAX_SGE_PER_RQ_WQE];
+               u8 wqe_size;
+
+               u8 smac[ETH_ALEN];
+               u16 vlan_id;
+               int rc;
+       } *rqe_wr_id;
+
+       /* Relevant to qps created from user space only (applications) */
+       struct qedr_userq usq;
+       struct qedr_userq urq;
+};
+
+struct qedr_ah {
+       struct ib_ah ibah;
+       struct ib_ah_attr attr;
+};
+
+enum qedr_mr_type {
+       QEDR_MR_USER,
+       QEDR_MR_KERNEL,
+       QEDR_MR_DMA,
+       QEDR_MR_FRMR,
+};
+
+struct mr_info {
+       struct qedr_pbl *pbl_table;
+       struct qedr_pbl_info pbl_info;
+       struct list_head free_pbl_list;
+       struct list_head inuse_pbl_list;
+       u32 completed;
+       u32 completed_handled;
+};
+
+struct qedr_mr {
+       struct ib_mr ibmr;
+       struct ib_umem *umem;
+
+       struct qed_rdma_register_tid_in_params hw_mr;
+       enum qedr_mr_type type;
+
+       struct qedr_dev *dev;
+       struct mr_info info;
+
+       u64 *pages;
+       u32 npages;
+};
+
+#define SET_FIELD2(value, name, flag) ((value) |= ((flag) << (name ## _SHIFT)))
+
+#define QEDR_RESP_IMM  (RDMA_CQE_RESPONDER_IMM_FLG_MASK << \
+                        RDMA_CQE_RESPONDER_IMM_FLG_SHIFT)
+#define QEDR_RESP_RDMA (RDMA_CQE_RESPONDER_RDMA_FLG_MASK << \
+                        RDMA_CQE_RESPONDER_RDMA_FLG_SHIFT)
+#define QEDR_RESP_RDMA_IMM (QEDR_RESP_IMM | QEDR_RESP_RDMA)
+
+static inline void qedr_inc_sw_cons(struct qedr_qp_hwq_info *info)
+{
+       info->cons = (info->cons + 1) % info->max_wr;
+       info->wqe_cons++;
+}
+
+static inline void qedr_inc_sw_prod(struct qedr_qp_hwq_info *info)
+{
+       info->prod = (info->prod + 1) % info->max_wr;
+}
+
+static inline int qedr_get_dmac(struct qedr_dev *dev,
+                               struct ib_ah_attr *ah_attr, u8 *mac_addr)
+{
+       union ib_gid zero_sgid = { { 0 } };
+       struct in6_addr in6;
+
+       if (!memcmp(&ah_attr->grh.dgid, &zero_sgid, sizeof(union ib_gid))) {
+               DP_ERR(dev, "Local port GID not supported\n");
+               eth_zero_addr(mac_addr);
+               return -EINVAL;
+       }
+
+       memcpy(&in6, ah_attr->grh.dgid.raw, sizeof(in6));
+       ether_addr_copy(mac_addr, ah_attr->dmac);
+
+       return 0;
+}
+
+static inline
+struct qedr_ucontext *get_qedr_ucontext(struct ib_ucontext *ibucontext)
+{
+       return container_of(ibucontext, struct qedr_ucontext, ibucontext);
+}
+
+static inline struct qedr_dev *get_qedr_dev(struct ib_device *ibdev)
+{
+       return container_of(ibdev, struct qedr_dev, ibdev);
+}
+
+static inline struct qedr_pd *get_qedr_pd(struct ib_pd *ibpd)
+{
+       return container_of(ibpd, struct qedr_pd, ibpd);
+}
+
+static inline struct qedr_cq *get_qedr_cq(struct ib_cq *ibcq)
+{
+       return container_of(ibcq, struct qedr_cq, ibcq);
+}
+
+static inline struct qedr_qp *get_qedr_qp(struct ib_qp *ibqp)
+{
+       return container_of(ibqp, struct qedr_qp, ibqp);
+}
+
+static inline struct qedr_ah *get_qedr_ah(struct ib_ah *ibah)
+{
+       return container_of(ibah, struct qedr_ah, ibah);
+}
+
+static inline struct qedr_mr *get_qedr_mr(struct ib_mr *ibmr)
+{
+       return container_of(ibmr, struct qedr_mr, ibmr);
+}
+#endif
diff --git a/drivers/infiniband/hw/qedr/qedr_cm.c b/drivers/infiniband/hw/qedr/qedr_cm.c
new file mode 100644 (file)
index 0000000..63890eb
--- /dev/null
@@ -0,0 +1,622 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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/dma-mapping.h>
+#include <linux/crc32.h>
+#include <linux/iommu.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/iw_cm.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_cache.h>
+
+#include "qedr_hsi.h"
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_roce_if.h>
+#include "qedr.h"
+#include "qedr_hsi.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+#include "qedr_hsi.h"
+#include "qedr_cm.h"
+
+void qedr_inc_sw_gsi_cons(struct qedr_qp_hwq_info *info)
+{
+       info->gsi_cons = (info->gsi_cons + 1) % info->max_wr;
+}
+
+void qedr_store_gsi_qp_cq(struct qedr_dev *dev, struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs)
+{
+       dev->gsi_qp_created = 1;
+       dev->gsi_sqcq = get_qedr_cq(attrs->send_cq);
+       dev->gsi_rqcq = get_qedr_cq(attrs->recv_cq);
+       dev->gsi_qp = qp;
+}
+
+void qedr_ll2_tx_cb(void *_qdev, struct qed_roce_ll2_packet *pkt)
+{
+       struct qedr_dev *dev = (struct qedr_dev *)_qdev;
+       struct qedr_cq *cq = dev->gsi_sqcq;
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "LL2 TX CB: gsi_sqcq=%p, gsi_rqcq=%p, gsi_cons=%d, ibcq_comp=%s\n",
+                dev->gsi_sqcq, dev->gsi_rqcq, qp->sq.gsi_cons,
+                cq->ibcq.comp_handler ? "Yes" : "No");
+
+       dma_free_coherent(&dev->pdev->dev, pkt->header.len, pkt->header.vaddr,
+                         pkt->header.baddr);
+       kfree(pkt);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+       qedr_inc_sw_gsi_cons(&qp->sq);
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (cq->ibcq.comp_handler) {
+               spin_lock_irqsave(&cq->comp_handler_lock, flags);
+               (*cq->ibcq.comp_handler) (&cq->ibcq, cq->ibcq.cq_context);
+               spin_unlock_irqrestore(&cq->comp_handler_lock, flags);
+       }
+}
+
+void qedr_ll2_rx_cb(void *_dev, struct qed_roce_ll2_packet *pkt,
+                   struct qed_roce_ll2_rx_params *params)
+{
+       struct qedr_dev *dev = (struct qedr_dev *)_dev;
+       struct qedr_cq *cq = dev->gsi_rqcq;
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       qp->rqe_wr_id[qp->rq.gsi_cons].rc = params->rc;
+       qp->rqe_wr_id[qp->rq.gsi_cons].vlan_id = params->vlan_id;
+       qp->rqe_wr_id[qp->rq.gsi_cons].sg_list[0].length = pkt->payload[0].len;
+       ether_addr_copy(qp->rqe_wr_id[qp->rq.gsi_cons].smac, params->smac);
+
+       qedr_inc_sw_gsi_cons(&qp->rq);
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (cq->ibcq.comp_handler) {
+               spin_lock_irqsave(&cq->comp_handler_lock, flags);
+               (*cq->ibcq.comp_handler) (&cq->ibcq, cq->ibcq.cq_context);
+               spin_unlock_irqrestore(&cq->comp_handler_lock, flags);
+       }
+}
+
+static void qedr_destroy_gsi_cq(struct qedr_dev *dev,
+                               struct ib_qp_init_attr *attrs)
+{
+       struct qed_rdma_destroy_cq_in_params iparams;
+       struct qed_rdma_destroy_cq_out_params oparams;
+       struct qedr_cq *cq;
+
+       cq = get_qedr_cq(attrs->send_cq);
+       iparams.icid = cq->icid;
+       dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+       dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+
+       cq = get_qedr_cq(attrs->recv_cq);
+       /* if a dedicated recv_cq was used, delete it too */
+       if (iparams.icid != cq->icid) {
+               iparams.icid = cq->icid;
+               dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+       }
+}
+
+static inline int qedr_check_gsi_qp_attrs(struct qedr_dev *dev,
+                                         struct ib_qp_init_attr *attrs)
+{
+       if (attrs->cap.max_recv_sge > QEDR_GSI_MAX_RECV_SGE) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_recv_sge is larger the max %d>%d\n",
+                      attrs->cap.max_recv_sge, QEDR_GSI_MAX_RECV_SGE);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_recv_wr > QEDR_GSI_MAX_RECV_WR) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_recv_wr is too large %d>%d\n",
+                      attrs->cap.max_recv_wr, QEDR_GSI_MAX_RECV_WR);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_wr > QEDR_GSI_MAX_SEND_WR) {
+               DP_ERR(dev,
+                      " create gsi qp: failed. max_send_wr is too large %d>%d\n",
+                      attrs->cap.max_send_wr, QEDR_GSI_MAX_SEND_WR);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct ib_qp *qedr_create_gsi_qp(struct qedr_dev *dev,
+                                struct ib_qp_init_attr *attrs,
+                                struct qedr_qp *qp)
+{
+       struct qed_roce_ll2_params ll2_params;
+       int rc;
+
+       rc = qedr_check_gsi_qp_attrs(dev, attrs);
+       if (rc)
+               return ERR_PTR(rc);
+
+       /* configure and start LL2 */
+       memset(&ll2_params, 0, sizeof(ll2_params));
+       ll2_params.max_tx_buffers = attrs->cap.max_send_wr;
+       ll2_params.max_rx_buffers = attrs->cap.max_recv_wr;
+       ll2_params.cbs.tx_cb = qedr_ll2_tx_cb;
+       ll2_params.cbs.rx_cb = qedr_ll2_rx_cb;
+       ll2_params.cb_cookie = (void *)dev;
+       ll2_params.mtu = dev->ndev->mtu;
+       ether_addr_copy(ll2_params.mac_address, dev->ndev->dev_addr);
+       rc = dev->ops->roce_ll2_start(dev->cdev, &ll2_params);
+       if (rc) {
+               DP_ERR(dev, "create gsi qp: failed on ll2 start. rc=%d\n", rc);
+               return ERR_PTR(rc);
+       }
+
+       /* create QP */
+       qp->ibqp.qp_num = 1;
+       qp->rq.max_wr = attrs->cap.max_recv_wr;
+       qp->sq.max_wr = attrs->cap.max_send_wr;
+
+       qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->rqe_wr_id)
+               goto err;
+       qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->wqe_wr_id)
+               goto err;
+
+       qedr_store_gsi_qp_cq(dev, qp, attrs);
+       ether_addr_copy(dev->gsi_ll2_mac_address, dev->ndev->dev_addr);
+
+       /* the GSI CQ is handled by the driver so remove it from the FW */
+       qedr_destroy_gsi_cq(dev, attrs);
+       dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI;
+       dev->gsi_rqcq->cq_type = QEDR_CQ_TYPE_GSI;
+
+       DP_DEBUG(dev, QEDR_MSG_GSI, "created GSI QP %p\n", qp);
+
+       return &qp->ibqp;
+
+err:
+       kfree(qp->rqe_wr_id);
+
+       rc = dev->ops->roce_ll2_stop(dev->cdev);
+       if (rc)
+               DP_ERR(dev, "create gsi qp: failed destroy on create\n");
+
+       return ERR_PTR(-ENOMEM);
+}
+
+int qedr_destroy_gsi_qp(struct qedr_dev *dev)
+{
+       int rc;
+
+       rc = dev->ops->roce_ll2_stop(dev->cdev);
+       if (rc)
+               DP_ERR(dev, "destroy gsi qp: failed (rc=%d)\n", rc);
+       else
+               DP_DEBUG(dev, QEDR_MSG_GSI, "destroy gsi qp: success\n");
+
+       return rc;
+}
+
+#define QEDR_MAX_UD_HEADER_SIZE        (100)
+#define QEDR_GSI_QPN           (1)
+static inline int qedr_gsi_build_header(struct qedr_dev *dev,
+                                       struct qedr_qp *qp,
+                                       struct ib_send_wr *swr,
+                                       struct ib_ud_header *udh,
+                                       int *roce_mode)
+{
+       bool has_vlan = false, has_grh_ipv6 = true;
+       struct ib_ah_attr *ah_attr = &get_qedr_ah(ud_wr(swr)->ah)->attr;
+       struct ib_global_route *grh = &ah_attr->grh;
+       union ib_gid sgid;
+       int send_size = 0;
+       u16 vlan_id = 0;
+       u16 ether_type;
+       struct ib_gid_attr sgid_attr;
+       int rc;
+       int ip_ver = 0;
+
+       bool has_udp = false;
+       int i;
+
+       send_size = 0;
+       for (i = 0; i < swr->num_sge; ++i)
+               send_size += swr->sg_list[i].length;
+
+       rc = ib_get_cached_gid(qp->ibqp.device, ah_attr->port_num,
+                              grh->sgid_index, &sgid, &sgid_attr);
+       if (rc) {
+               DP_ERR(dev,
+                      "gsi post send: failed to get cached GID (port=%d, ix=%d)\n",
+                      ah_attr->port_num, grh->sgid_index);
+               return rc;
+       }
+
+       vlan_id = rdma_vlan_dev_vlan_id(sgid_attr.ndev);
+       if (vlan_id < VLAN_CFI_MASK)
+               has_vlan = true;
+       if (sgid_attr.ndev)
+               dev_put(sgid_attr.ndev);
+
+       if (!memcmp(&sgid, &zgid, sizeof(sgid))) {
+               DP_ERR(dev, "gsi post send: GID not found GID index %d\n",
+                      ah_attr->grh.sgid_index);
+               return -ENOENT;
+       }
+
+       has_udp = (sgid_attr.gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP);
+       if (!has_udp) {
+               /* RoCE v1 */
+               ether_type = ETH_P_ROCE;
+               *roce_mode = ROCE_V1;
+       } else if (ipv6_addr_v4mapped((struct in6_addr *)&sgid)) {
+               /* RoCE v2 IPv4 */
+               ip_ver = 4;
+               ether_type = ETH_P_IP;
+               has_grh_ipv6 = false;
+               *roce_mode = ROCE_V2_IPV4;
+       } else {
+               /* RoCE v2 IPv6 */
+               ip_ver = 6;
+               ether_type = ETH_P_IPV6;
+               *roce_mode = ROCE_V2_IPV6;
+       }
+
+       rc = ib_ud_header_init(send_size, false, true, has_vlan,
+                              has_grh_ipv6, ip_ver, has_udp, 0, udh);
+       if (rc) {
+               DP_ERR(dev, "gsi post send: failed to init header\n");
+               return rc;
+       }
+
+       /* ENET + VLAN headers */
+       ether_addr_copy(udh->eth.dmac_h, ah_attr->dmac);
+       ether_addr_copy(udh->eth.smac_h, dev->ndev->dev_addr);
+       if (has_vlan) {
+               udh->eth.type = htons(ETH_P_8021Q);
+               udh->vlan.tag = htons(vlan_id);
+               udh->vlan.type = htons(ether_type);
+       } else {
+               udh->eth.type = htons(ether_type);
+       }
+
+       /* BTH */
+       udh->bth.solicited_event = !!(swr->send_flags & IB_SEND_SOLICITED);
+       udh->bth.pkey = QEDR_ROCE_PKEY_DEFAULT;
+       udh->bth.destination_qpn = htonl(ud_wr(swr)->remote_qpn);
+       udh->bth.psn = htonl((qp->sq_psn++) & ((1 << 24) - 1));
+       udh->bth.opcode = IB_OPCODE_UD_SEND_ONLY;
+
+       /* DETH */
+       udh->deth.qkey = htonl(0x80010000);
+       udh->deth.source_qpn = htonl(QEDR_GSI_QPN);
+
+       if (has_grh_ipv6) {
+               /* GRH / IPv6 header */
+               udh->grh.traffic_class = grh->traffic_class;
+               udh->grh.flow_label = grh->flow_label;
+               udh->grh.hop_limit = grh->hop_limit;
+               udh->grh.destination_gid = grh->dgid;
+               memcpy(&udh->grh.source_gid.raw, &sgid.raw,
+                      sizeof(udh->grh.source_gid.raw));
+       } else {
+               /* IPv4 header */
+               u32 ipv4_addr;
+
+               udh->ip4.protocol = IPPROTO_UDP;
+               udh->ip4.tos = htonl(ah_attr->grh.flow_label);
+               udh->ip4.frag_off = htons(IP_DF);
+               udh->ip4.ttl = ah_attr->grh.hop_limit;
+
+               ipv4_addr = qedr_get_ipv4_from_gid(sgid.raw);
+               udh->ip4.saddr = ipv4_addr;
+               ipv4_addr = qedr_get_ipv4_from_gid(ah_attr->grh.dgid.raw);
+               udh->ip4.daddr = ipv4_addr;
+               /* note: checksum is calculated by the device */
+       }
+
+       /* UDP */
+       if (has_udp) {
+               udh->udp.sport = htons(QEDR_ROCE_V2_UDP_SPORT);
+               udh->udp.dport = htons(ROCE_V2_UDP_DPORT);
+               udh->udp.csum = 0;
+               /* UDP length is untouched hence is zero */
+       }
+       return 0;
+}
+
+static inline int qedr_gsi_build_packet(struct qedr_dev *dev,
+                                       struct qedr_qp *qp,
+                                       struct ib_send_wr *swr,
+                                       struct qed_roce_ll2_packet **p_packet)
+{
+       u8 ud_header_buffer[QEDR_MAX_UD_HEADER_SIZE];
+       struct qed_roce_ll2_packet *packet;
+       struct pci_dev *pdev = dev->pdev;
+       int roce_mode, header_size;
+       struct ib_ud_header udh;
+       int i, rc;
+
+       *p_packet = NULL;
+
+       rc = qedr_gsi_build_header(dev, qp, swr, &udh, &roce_mode);
+       if (rc)
+               return rc;
+
+       header_size = ib_ud_header_pack(&udh, &ud_header_buffer);
+
+       packet = kzalloc(sizeof(*packet), GFP_ATOMIC);
+       if (!packet)
+               return -ENOMEM;
+
+       packet->header.vaddr = dma_alloc_coherent(&pdev->dev, header_size,
+                                                 &packet->header.baddr,
+                                                 GFP_ATOMIC);
+       if (!packet->header.vaddr) {
+               kfree(packet);
+               return -ENOMEM;
+       }
+
+       if (ether_addr_equal(udh.eth.smac_h, udh.eth.dmac_h))
+               packet->tx_dest = QED_ROCE_LL2_TX_DEST_NW;
+       else
+               packet->tx_dest = QED_ROCE_LL2_TX_DEST_LB;
+
+       packet->roce_mode = roce_mode;
+       memcpy(packet->header.vaddr, ud_header_buffer, header_size);
+       packet->header.len = header_size;
+       packet->n_seg = swr->num_sge;
+       for (i = 0; i < packet->n_seg; i++) {
+               packet->payload[i].baddr = swr->sg_list[i].addr;
+               packet->payload[i].len = swr->sg_list[i].length;
+       }
+
+       *p_packet = packet;
+
+       return 0;
+}
+
+int qedr_gsi_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                      struct ib_send_wr **bad_wr)
+{
+       struct qed_roce_ll2_packet *pkt = NULL;
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_roce_ll2_tx_params params;
+       struct qedr_dev *dev = qp->dev;
+       unsigned long flags;
+       int rc;
+
+       if (qp->state != QED_ROCE_QP_STATE_RTS) {
+               *bad_wr = wr;
+               DP_ERR(dev,
+                      "gsi post recv: failed to post rx buffer. state is %d and not QED_ROCE_QP_STATE_RTS\n",
+                      qp->state);
+               return -EINVAL;
+       }
+
+       if (wr->num_sge > RDMA_MAX_SGE_PER_SQ_WQE) {
+               DP_ERR(dev, "gsi post send: num_sge is too large (%d>%d)\n",
+                      wr->num_sge, RDMA_MAX_SGE_PER_SQ_WQE);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       if (wr->opcode != IB_WR_SEND) {
+               DP_ERR(dev,
+                      "gsi post send: failed due to unsupported opcode %d\n",
+                      wr->opcode);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       memset(&params, 0, sizeof(params));
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       rc = qedr_gsi_build_packet(dev, qp, wr, &pkt);
+       if (rc) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               goto err;
+       }
+
+       rc = dev->ops->roce_ll2_tx(dev->cdev, pkt, &params);
+       if (!rc) {
+               qp->wqe_wr_id[qp->sq.prod].wr_id = wr->wr_id;
+               qedr_inc_sw_prod(&qp->sq);
+               DP_DEBUG(qp->dev, QEDR_MSG_GSI,
+                        "gsi post send: opcode=%d, in_irq=%ld, irqs_disabled=%d, wr_id=%llx\n",
+                        wr->opcode, in_irq(), irqs_disabled(), wr->wr_id);
+       } else {
+               if (rc == QED_ROCE_TX_HEAD_FAILURE) {
+                       /* TX failed while posting header - release resources */
+                       dma_free_coherent(&dev->pdev->dev, pkt->header.len,
+                                         pkt->header.vaddr, pkt->header.baddr);
+                       kfree(pkt);
+               } else if (rc == QED_ROCE_TX_FRAG_FAILURE) {
+                       /* NTD since TX failed while posting a fragment. We will
+                        * release the resources on TX callback
+                        */
+               }
+
+               DP_ERR(dev, "gsi post send: failed to transmit (rc=%d)\n", rc);
+               rc = -EAGAIN;
+               *bad_wr = wr;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       if (wr->next) {
+               DP_ERR(dev,
+                      "gsi post send: failed second WR. Only one WR may be passed at a time\n");
+               *bad_wr = wr->next;
+               rc = -EINVAL;
+       }
+
+       return rc;
+
+err:
+       *bad_wr = wr;
+       return rc;
+}
+
+int qedr_gsi_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_roce_ll2_buffer buf;
+       unsigned long flags;
+       int status = 0;
+       int rc;
+
+       if ((qp->state != QED_ROCE_QP_STATE_RTR) &&
+           (qp->state != QED_ROCE_QP_STATE_RTS)) {
+               *bad_wr = wr;
+               DP_ERR(dev,
+                      "gsi post recv: failed to post rx buffer. state is %d and not QED_ROCE_QP_STATE_RTR/S\n",
+                      qp->state);
+               return -EINVAL;
+       }
+
+       memset(&buf, 0, sizeof(buf));
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       while (wr) {
+               if (wr->num_sge > QEDR_GSI_MAX_RECV_SGE) {
+                       DP_ERR(dev,
+                              "gsi post recv: failed to post rx buffer. too many sges %d>%d\n",
+                              wr->num_sge, QEDR_GSI_MAX_RECV_SGE);
+                       goto err;
+               }
+
+               buf.baddr = wr->sg_list[0].addr;
+               buf.len = wr->sg_list[0].length;
+
+               rc = dev->ops->roce_ll2_post_rx_buffer(dev->cdev, &buf, 0, 1);
+               if (rc) {
+                       DP_ERR(dev,
+                              "gsi post recv: failed to post rx buffer (rc=%d)\n",
+                              rc);
+                       goto err;
+               }
+
+               memset(&qp->rqe_wr_id[qp->rq.prod], 0,
+                      sizeof(qp->rqe_wr_id[qp->rq.prod]));
+               qp->rqe_wr_id[qp->rq.prod].sg_list[0] = wr->sg_list[0];
+               qp->rqe_wr_id[qp->rq.prod].wr_id = wr->wr_id;
+
+               qedr_inc_sw_prod(&qp->rq);
+
+               wr = wr->next;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return status;
+err:
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+       *bad_wr = wr;
+       return -ENOMEM;
+}
+
+int qedr_gsi_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       struct qedr_qp *qp = dev->gsi_qp;
+       unsigned long flags;
+       int i = 0;
+
+       spin_lock_irqsave(&cq->cq_lock, flags);
+
+       while (i < num_entries && qp->rq.cons != qp->rq.gsi_cons) {
+               memset(&wc[i], 0, sizeof(*wc));
+
+               wc[i].qp = &qp->ibqp;
+               wc[i].wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+               wc[i].opcode = IB_WC_RECV;
+               wc[i].pkey_index = 0;
+               wc[i].status = (qp->rqe_wr_id[qp->rq.cons].rc) ?
+                   IB_WC_GENERAL_ERR : IB_WC_SUCCESS;
+               /* 0 - currently only one recv sg is supported */
+               wc[i].byte_len = qp->rqe_wr_id[qp->rq.cons].sg_list[0].length;
+               wc[i].wc_flags |= IB_WC_GRH | IB_WC_IP_CSUM_OK;
+               ether_addr_copy(wc[i].smac, qp->rqe_wr_id[qp->rq.cons].smac);
+               wc[i].wc_flags |= IB_WC_WITH_SMAC;
+               if (qp->rqe_wr_id[qp->rq.cons].vlan_id) {
+                       wc[i].wc_flags |= IB_WC_WITH_VLAN;
+                       wc[i].vlan_id = qp->rqe_wr_id[qp->rq.cons].vlan_id;
+               }
+
+               qedr_inc_sw_cons(&qp->rq);
+               i++;
+       }
+
+       while (i < num_entries && qp->sq.cons != qp->sq.gsi_cons) {
+               memset(&wc[i], 0, sizeof(*wc));
+
+               wc[i].qp = &qp->ibqp;
+               wc[i].wr_id = qp->wqe_wr_id[qp->sq.cons].wr_id;
+               wc[i].opcode = IB_WC_SEND;
+               wc[i].status = IB_WC_SUCCESS;
+
+               qedr_inc_sw_cons(&qp->sq);
+               i++;
+       }
+
+       spin_unlock_irqrestore(&cq->cq_lock, flags);
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "gsi poll_cq: requested entries=%d, actual=%d, qp->rq.cons=%d, qp->rq.gsi_cons=%x, qp->sq.cons=%d, qp->sq.gsi_cons=%d, qp_num=%d\n",
+                num_entries, i, qp->rq.cons, qp->rq.gsi_cons, qp->sq.cons,
+                qp->sq.gsi_cons, qp->ibqp.qp_num);
+
+       return i;
+}
diff --git a/drivers/infiniband/hw/qedr/qedr_cm.h b/drivers/infiniband/hw/qedr/qedr_cm.h
new file mode 100644 (file)
index 0000000..9ba6e15
--- /dev/null
@@ -0,0 +1,61 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 LINUX_QEDR_CM_H_
+#define LINUX_QEDR_CM_H_
+
+#define QEDR_GSI_MAX_RECV_WR   (4096)
+#define QEDR_GSI_MAX_SEND_WR   (4096)
+
+#define QEDR_GSI_MAX_RECV_SGE  (1)     /* LL2 FW limitation */
+
+#define ETH_P_ROCE             (0x8915)
+#define QEDR_ROCE_V2_UDP_SPORT (0000)
+
+static inline u32 qedr_get_ipv4_from_gid(u8 *gid)
+{
+       return *(u32 *)(void *)&gid[12];
+}
+
+/* RDMA CM */
+int qedr_gsi_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc);
+int qedr_gsi_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                      struct ib_recv_wr **bad_wr);
+int qedr_gsi_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                      struct ib_send_wr **bad_wr);
+struct ib_qp *qedr_create_gsi_qp(struct qedr_dev *dev,
+                                struct ib_qp_init_attr *attrs,
+                                struct qedr_qp *qp);
+void qedr_store_gsi_qp_cq(struct qedr_dev *dev,
+                         struct qedr_qp *qp, struct ib_qp_init_attr *attrs);
+int qedr_destroy_gsi_qp(struct qedr_dev *dev);
+void qedr_inc_sw_gsi_cons(struct qedr_qp_hwq_info *info);
+#endif
diff --git a/drivers/infiniband/hw/qedr/qedr_hsi.h b/drivers/infiniband/hw/qedr/qedr_hsi.h
new file mode 100644 (file)
index 0000000..66d2752
--- /dev/null
@@ -0,0 +1,56 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 __QED_HSI_ROCE__
+#define __QED_HSI_ROCE__
+
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/roce_common.h>
+#include "qedr_hsi_rdma.h"
+
+/* Affiliated asynchronous events / errors enumeration */
+enum roce_async_events_type {
+       ROCE_ASYNC_EVENT_NONE = 0,
+       ROCE_ASYNC_EVENT_COMM_EST = 1,
+       ROCE_ASYNC_EVENT_SQ_DRAINED,
+       ROCE_ASYNC_EVENT_SRQ_LIMIT,
+       ROCE_ASYNC_EVENT_LAST_WQE_REACHED,
+       ROCE_ASYNC_EVENT_CQ_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_INVALID_REQUEST_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_CATASTROPHIC_ERR,
+       ROCE_ASYNC_EVENT_LOCAL_ACCESS_ERR,
+       ROCE_ASYNC_EVENT_QP_CATASTROPHIC_ERR,
+       ROCE_ASYNC_EVENT_CQ_OVERFLOW_ERR,
+       ROCE_ASYNC_EVENT_SRQ_EMPTY,
+       MAX_ROCE_ASYNC_EVENTS_TYPE
+};
+
+#endif /* __QED_HSI_ROCE__ */
diff --git a/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h b/drivers/infiniband/hw/qedr/qedr_hsi_rdma.h
new file mode 100644 (file)
index 0000000..5c98d20
--- /dev/null
@@ -0,0 +1,748 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 __QED_HSI_RDMA__
+#define __QED_HSI_RDMA__
+
+#include <linux/qed/rdma_common.h>
+
+/* rdma completion notification queue element */
+struct rdma_cnqe {
+       struct regpair  cq_handle;
+};
+
+struct rdma_cqe_responder {
+       struct regpair srq_wr_id;
+       struct regpair qp_handle;
+       __le32 imm_data_or_inv_r_Key;
+       __le32 length;
+       __le32 imm_data_hi;
+       __le16 rq_cons;
+       u8 flags;
+#define RDMA_CQE_RESPONDER_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_RESPONDER_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_RESPONDER_TYPE_MASK        0x3
+#define RDMA_CQE_RESPONDER_TYPE_SHIFT       1
+#define RDMA_CQE_RESPONDER_INV_FLG_MASK     0x1
+#define RDMA_CQE_RESPONDER_INV_FLG_SHIFT    3
+#define RDMA_CQE_RESPONDER_IMM_FLG_MASK     0x1
+#define RDMA_CQE_RESPONDER_IMM_FLG_SHIFT    4
+#define RDMA_CQE_RESPONDER_RDMA_FLG_MASK    0x1
+#define RDMA_CQE_RESPONDER_RDMA_FLG_SHIFT   5
+#define RDMA_CQE_RESPONDER_RESERVED2_MASK   0x3
+#define RDMA_CQE_RESPONDER_RESERVED2_SHIFT  6
+       u8 status;
+};
+
+struct rdma_cqe_requester {
+       __le16 sq_cons;
+       __le16 reserved0;
+       __le32 reserved1;
+       struct regpair qp_handle;
+       struct regpair reserved2;
+       __le32 reserved3;
+       __le16 reserved4;
+       u8 flags;
+#define RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_REQUESTER_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_REQUESTER_TYPE_MASK        0x3
+#define RDMA_CQE_REQUESTER_TYPE_SHIFT       1
+#define RDMA_CQE_REQUESTER_RESERVED5_MASK   0x1F
+#define RDMA_CQE_REQUESTER_RESERVED5_SHIFT  3
+       u8 status;
+};
+
+struct rdma_cqe_common {
+       struct regpair reserved0;
+       struct regpair qp_handle;
+       __le16 reserved1[7];
+       u8 flags;
+#define RDMA_CQE_COMMON_TOGGLE_BIT_MASK  0x1
+#define RDMA_CQE_COMMON_TOGGLE_BIT_SHIFT 0
+#define RDMA_CQE_COMMON_TYPE_MASK        0x3
+#define RDMA_CQE_COMMON_TYPE_SHIFT       1
+#define RDMA_CQE_COMMON_RESERVED2_MASK   0x1F
+#define RDMA_CQE_COMMON_RESERVED2_SHIFT  3
+       u8 status;
+};
+
+/* rdma completion queue element */
+union rdma_cqe {
+       struct rdma_cqe_responder resp;
+       struct rdma_cqe_requester req;
+       struct rdma_cqe_common cmn;
+};
+
+/* * CQE requester status enumeration */
+enum rdma_cqe_requester_status_enum {
+       RDMA_CQE_REQ_STS_OK,
+       RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR,
+       RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR,
+       RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR,
+       RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR,
+       RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR,
+       RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR,
+       MAX_RDMA_CQE_REQUESTER_STATUS_ENUM
+};
+
+/* CQE responder status enumeration */
+enum rdma_cqe_responder_status_enum {
+       RDMA_CQE_RESP_STS_OK,
+       RDMA_CQE_RESP_STS_LOCAL_ACCESS_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_LENGTH_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_QP_OPERATION_ERR,
+       RDMA_CQE_RESP_STS_LOCAL_PROTECTION_ERR,
+       RDMA_CQE_RESP_STS_MEMORY_MGT_OPERATION_ERR,
+       RDMA_CQE_RESP_STS_REMOTE_INVALID_REQUEST_ERR,
+       RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR,
+       MAX_RDMA_CQE_RESPONDER_STATUS_ENUM
+};
+
+/* CQE type enumeration */
+enum rdma_cqe_type {
+       RDMA_CQE_TYPE_REQUESTER,
+       RDMA_CQE_TYPE_RESPONDER_RQ,
+       RDMA_CQE_TYPE_RESPONDER_SRQ,
+       RDMA_CQE_TYPE_INVALID,
+       MAX_RDMA_CQE_TYPE
+};
+
+struct rdma_sq_sge {
+       __le32 length;
+       struct regpair  addr;
+       __le32 l_key;
+};
+
+struct rdma_rq_sge {
+       struct regpair addr;
+       __le32 length;
+       __le32 flags;
+#define RDMA_RQ_SGE_L_KEY_MASK      0x3FFFFFF
+#define RDMA_RQ_SGE_L_KEY_SHIFT     0
+#define RDMA_RQ_SGE_NUM_SGES_MASK   0x7
+#define RDMA_RQ_SGE_NUM_SGES_SHIFT  26
+#define RDMA_RQ_SGE_RESERVED0_MASK  0x7
+#define RDMA_RQ_SGE_RESERVED0_SHIFT 29
+};
+
+struct rdma_srq_sge {
+       struct regpair addr;
+       __le32 length;
+       __le32 l_key;
+};
+
+/* Rdma doorbell data for SQ and RQ */
+struct rdma_pwm_val16_data {
+       __le16 icid;
+       __le16 value;
+};
+
+union rdma_pwm_val16_data_union {
+       struct rdma_pwm_val16_data as_struct;
+       __le32 as_dword;
+};
+
+/* Rdma doorbell data for CQ */
+struct rdma_pwm_val32_data {
+       __le16 icid;
+       u8 agg_flags;
+       u8 params;
+#define RDMA_PWM_VAL32_DATA_AGG_CMD_MASK    0x3
+#define RDMA_PWM_VAL32_DATA_AGG_CMD_SHIFT   0
+#define RDMA_PWM_VAL32_DATA_BYPASS_EN_MASK  0x1
+#define RDMA_PWM_VAL32_DATA_BYPASS_EN_SHIFT 2
+#define RDMA_PWM_VAL32_DATA_RESERVED_MASK   0x1F
+#define RDMA_PWM_VAL32_DATA_RESERVED_SHIFT  3
+       __le32 value;
+};
+
+/* DIF Block size options */
+enum rdma_dif_block_size {
+       RDMA_DIF_BLOCK_512 = 0,
+       RDMA_DIF_BLOCK_4096 = 1,
+       MAX_RDMA_DIF_BLOCK_SIZE
+};
+
+/* DIF CRC initial value */
+enum rdma_dif_crc_seed {
+       RDMA_DIF_CRC_SEED_0000 = 0,
+       RDMA_DIF_CRC_SEED_FFFF = 1,
+       MAX_RDMA_DIF_CRC_SEED
+};
+
+/* RDMA DIF Error Result Structure */
+struct rdma_dif_error_result {
+       __le32 error_intervals;
+       __le32 dif_error_1st_interval;
+       u8 flags;
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_CRC_MASK      0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_CRC_SHIFT     0
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_APP_TAG_MASK  0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_APP_TAG_SHIFT 1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_REF_TAG_MASK  0x1
+#define RDMA_DIF_ERROR_RESULT_DIF_ERROR_TYPE_REF_TAG_SHIFT 2
+#define RDMA_DIF_ERROR_RESULT_RESERVED0_MASK               0xF
+#define RDMA_DIF_ERROR_RESULT_RESERVED0_SHIFT              3
+#define RDMA_DIF_ERROR_RESULT_TOGGLE_BIT_MASK              0x1
+#define RDMA_DIF_ERROR_RESULT_TOGGLE_BIT_SHIFT             7
+       u8 reserved1[55];
+};
+
+/* DIF IO direction */
+enum rdma_dif_io_direction_flg {
+       RDMA_DIF_DIR_RX = 0,
+       RDMA_DIF_DIR_TX = 1,
+       MAX_RDMA_DIF_IO_DIRECTION_FLG
+};
+
+/* RDMA DIF Runt Result Structure */
+struct rdma_dif_runt_result {
+       __le16 guard_tag;
+       __le16 reserved[3];
+};
+
+/* Memory window type enumeration */
+enum rdma_mw_type {
+       RDMA_MW_TYPE_1,
+       RDMA_MW_TYPE_2A,
+       MAX_RDMA_MW_TYPE
+};
+
+struct rdma_sq_atomic_wqe {
+       __le32 reserved1;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_ATOMIC_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_ATOMIC_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_ATOMIC_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_ATOMIC_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_ATOMIC_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_ATOMIC_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_ATOMIC_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_ATOMIC_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_ATOMIC_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_ATOMIC_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_ATOMIC_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_ATOMIC_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_ATOMIC_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_ATOMIC_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       struct regpair remote_va;
+       __le32 r_key;
+       __le32 reserved2;
+       struct regpair cmp_data;
+       struct regpair swap_data;
+};
+
+/* First element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_1st {
+       __le32 reserved1;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_ATOMIC_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_ATOMIC_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_ATOMIC_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_ATOMIC_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_ATOMIC_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_ATOMIC_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_ATOMIC_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_2nd {
+       struct regpair remote_va;
+       __le32 r_key;
+       __le32 reserved2;
+};
+
+/* Third element (16 bytes) of atomic wqe */
+struct rdma_sq_atomic_wqe_3rd {
+       struct regpair cmp_data;
+       struct regpair swap_data;
+};
+
+struct rdma_sq_bind_wqe {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_BIND_WQE_COMP_FLG_MASK       0x1
+#define RDMA_SQ_BIND_WQE_COMP_FLG_SHIFT      0
+#define RDMA_SQ_BIND_WQE_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_BIND_WQE_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_BIND_WQE_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_BIND_WQE_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_BIND_WQE_SE_FLG_MASK         0x1
+#define RDMA_SQ_BIND_WQE_SE_FLG_SHIFT        3
+#define RDMA_SQ_BIND_WQE_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_BIND_WQE_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_BIND_WQE_RESERVED0_MASK      0x7
+#define RDMA_SQ_BIND_WQE_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       u8 bind_ctrl;
+#define RDMA_SQ_BIND_WQE_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_BIND_WQE_ZERO_BASED_SHIFT    0
+#define RDMA_SQ_BIND_WQE_MW_TYPE_MASK        0x1
+#define RDMA_SQ_BIND_WQE_MW_TYPE_SHIFT       1
+#define RDMA_SQ_BIND_WQE_RESERVED1_MASK      0x3F
+#define RDMA_SQ_BIND_WQE_RESERVED1_SHIFT     2
+       u8 access_ctrl;
+#define RDMA_SQ_BIND_WQE_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_BIND_WQE_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_BIND_WQE_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_BIND_WQE_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_BIND_WQE_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_BIND_WQE_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_BIND_WQE_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_BIND_WQE_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_BIND_WQE_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_BIND_WQE_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_BIND_WQE_RESERVED2_MASK      0x7
+#define RDMA_SQ_BIND_WQE_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       __le32 parent_l_key;
+       __le32 reserved4;
+};
+
+/* First element (16 bytes) of bind wqe */
+struct rdma_sq_bind_wqe_1st {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_BIND_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_BIND_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_BIND_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_BIND_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_BIND_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_BIND_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_BIND_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_BIND_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_BIND_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_BIND_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_BIND_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_BIND_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of bind wqe */
+struct rdma_sq_bind_wqe_2nd {
+       u8 bind_ctrl;
+#define RDMA_SQ_BIND_WQE_2ND_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_BIND_WQE_2ND_ZERO_BASED_SHIFT    0
+#define RDMA_SQ_BIND_WQE_2ND_MW_TYPE_MASK        0x1
+#define RDMA_SQ_BIND_WQE_2ND_MW_TYPE_SHIFT       1
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED1_MASK      0x3F
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED1_SHIFT     2
+       u8 access_ctrl;
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_BIND_WQE_2ND_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_BIND_WQE_2ND_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_BIND_WQE_2ND_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_BIND_WQE_2ND_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED2_MASK      0x7
+#define RDMA_SQ_BIND_WQE_2ND_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       __le32 parent_l_key;
+       __le32 reserved4;
+};
+
+/* Structure with only the SQ WQE common
+ * fields. Size is of one SQ element (16B)
+ */
+struct rdma_sq_common_wqe {
+       __le32 reserved1[3];
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_COMMON_WQE_COMP_FLG_MASK       0x1
+#define RDMA_SQ_COMMON_WQE_COMP_FLG_SHIFT      0
+#define RDMA_SQ_COMMON_WQE_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_COMMON_WQE_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_COMMON_WQE_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_COMMON_WQE_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_COMMON_WQE_SE_FLG_MASK         0x1
+#define RDMA_SQ_COMMON_WQE_SE_FLG_SHIFT        3
+#define RDMA_SQ_COMMON_WQE_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_COMMON_WQE_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_COMMON_WQE_RESERVED0_MASK      0x7
+#define RDMA_SQ_COMMON_WQE_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_fmr_wqe {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_FMR_WQE_COMP_FLG_MASK                0x1
+#define RDMA_SQ_FMR_WQE_COMP_FLG_SHIFT               0
+#define RDMA_SQ_FMR_WQE_RD_FENCE_FLG_MASK            0x1
+#define RDMA_SQ_FMR_WQE_RD_FENCE_FLG_SHIFT           1
+#define RDMA_SQ_FMR_WQE_INV_FENCE_FLG_MASK           0x1
+#define RDMA_SQ_FMR_WQE_INV_FENCE_FLG_SHIFT          2
+#define RDMA_SQ_FMR_WQE_SE_FLG_MASK                  0x1
+#define RDMA_SQ_FMR_WQE_SE_FLG_SHIFT                 3
+#define RDMA_SQ_FMR_WQE_INLINE_FLG_MASK              0x1
+#define RDMA_SQ_FMR_WQE_INLINE_FLG_SHIFT             4
+#define RDMA_SQ_FMR_WQE_DIF_ON_HOST_FLG_MASK         0x1
+#define RDMA_SQ_FMR_WQE_DIF_ON_HOST_FLG_SHIFT        5
+#define RDMA_SQ_FMR_WQE_RESERVED0_MASK               0x3
+#define RDMA_SQ_FMR_WQE_RESERVED0_SHIFT              6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       u8 fmr_ctrl;
+#define RDMA_SQ_FMR_WQE_PAGE_SIZE_LOG_MASK           0x1F
+#define RDMA_SQ_FMR_WQE_PAGE_SIZE_LOG_SHIFT          0
+#define RDMA_SQ_FMR_WQE_ZERO_BASED_MASK              0x1
+#define RDMA_SQ_FMR_WQE_ZERO_BASED_SHIFT             5
+#define RDMA_SQ_FMR_WQE_BIND_EN_MASK                 0x1
+#define RDMA_SQ_FMR_WQE_BIND_EN_SHIFT                6
+#define RDMA_SQ_FMR_WQE_RESERVED1_MASK               0x1
+#define RDMA_SQ_FMR_WQE_RESERVED1_SHIFT              7
+       u8 access_ctrl;
+#define RDMA_SQ_FMR_WQE_REMOTE_READ_MASK             0x1
+#define RDMA_SQ_FMR_WQE_REMOTE_READ_SHIFT            0
+#define RDMA_SQ_FMR_WQE_REMOTE_WRITE_MASK            0x1
+#define RDMA_SQ_FMR_WQE_REMOTE_WRITE_SHIFT           1
+#define RDMA_SQ_FMR_WQE_ENABLE_ATOMIC_MASK           0x1
+#define RDMA_SQ_FMR_WQE_ENABLE_ATOMIC_SHIFT          2
+#define RDMA_SQ_FMR_WQE_LOCAL_READ_MASK              0x1
+#define RDMA_SQ_FMR_WQE_LOCAL_READ_SHIFT             3
+#define RDMA_SQ_FMR_WQE_LOCAL_WRITE_MASK             0x1
+#define RDMA_SQ_FMR_WQE_LOCAL_WRITE_SHIFT            4
+#define RDMA_SQ_FMR_WQE_RESERVED2_MASK               0x7
+#define RDMA_SQ_FMR_WQE_RESERVED2_SHIFT              5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       struct regpair pbl_addr;
+       __le32 dif_base_ref_tag;
+       __le16 dif_app_tag;
+       __le16 dif_app_tag_mask;
+       __le16 dif_runt_crc_value;
+       __le16 dif_flags;
+#define RDMA_SQ_FMR_WQE_DIF_IO_DIRECTION_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_IO_DIRECTION_FLG_SHIFT   0
+#define RDMA_SQ_FMR_WQE_DIF_BLOCK_SIZE_MASK          0x1
+#define RDMA_SQ_FMR_WQE_DIF_BLOCK_SIZE_SHIFT         1
+#define RDMA_SQ_FMR_WQE_DIF_RUNT_VALID_FLG_MASK      0x1
+#define RDMA_SQ_FMR_WQE_DIF_RUNT_VALID_FLG_SHIFT     2
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_CRC_GUARD_MASK  0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_CRC_GUARD_SHIFT 3
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_REF_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_REF_TAG_SHIFT   4
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_APP_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_DIF_VALIDATE_APP_TAG_SHIFT   5
+#define RDMA_SQ_FMR_WQE_DIF_CRC_SEED_MASK            0x1
+#define RDMA_SQ_FMR_WQE_DIF_CRC_SEED_SHIFT           6
+#define RDMA_SQ_FMR_WQE_RESERVED4_MASK               0x1FF
+#define RDMA_SQ_FMR_WQE_RESERVED4_SHIFT              7
+       __le32 Reserved5;
+};
+
+/* First element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_1st {
+       struct regpair addr;
+       __le32 l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_FMR_WQE_1ST_COMP_FLG_MASK         0x1
+#define RDMA_SQ_FMR_WQE_1ST_COMP_FLG_SHIFT        0
+#define RDMA_SQ_FMR_WQE_1ST_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_FMR_WQE_1ST_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_FMR_WQE_1ST_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_1ST_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_FMR_WQE_1ST_SE_FLG_MASK           0x1
+#define RDMA_SQ_FMR_WQE_1ST_SE_FLG_SHIFT          3
+#define RDMA_SQ_FMR_WQE_1ST_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_FMR_WQE_1ST_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_FMR_WQE_1ST_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_FMR_WQE_1ST_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_FMR_WQE_1ST_RESERVED0_MASK        0x3
+#define RDMA_SQ_FMR_WQE_1ST_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_2nd {
+       u8 fmr_ctrl;
+#define RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG_MASK  0x1F
+#define RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG_SHIFT 0
+#define RDMA_SQ_FMR_WQE_2ND_ZERO_BASED_MASK     0x1
+#define RDMA_SQ_FMR_WQE_2ND_ZERO_BASED_SHIFT    5
+#define RDMA_SQ_FMR_WQE_2ND_BIND_EN_MASK        0x1
+#define RDMA_SQ_FMR_WQE_2ND_BIND_EN_SHIFT       6
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED1_MASK      0x1
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED1_SHIFT     7
+       u8 access_ctrl;
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_READ_MASK    0x1
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_READ_SHIFT   0
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE_MASK   0x1
+#define RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE_SHIFT  1
+#define RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC_MASK  0x1
+#define RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC_SHIFT 2
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_READ_MASK     0x1
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_READ_SHIFT    3
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE_MASK    0x1
+#define RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE_SHIFT   4
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED2_MASK      0x7
+#define RDMA_SQ_FMR_WQE_2ND_RESERVED2_SHIFT     5
+       u8 reserved3;
+       u8 length_hi;
+       __le32 length_lo;
+       struct regpair pbl_addr;
+};
+
+/* Third element (16 bytes) of fmr wqe */
+struct rdma_sq_fmr_wqe_3rd {
+       __le32 dif_base_ref_tag;
+       __le16 dif_app_tag;
+       __le16 dif_app_tag_mask;
+       __le16 dif_runt_crc_value;
+       __le16 dif_flags;
+#define RDMA_SQ_FMR_WQE_3RD_DIF_IO_DIRECTION_FLG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_IO_DIRECTION_FLG_SHIFT   0
+#define RDMA_SQ_FMR_WQE_3RD_DIF_BLOCK_SIZE_MASK          0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_BLOCK_SIZE_SHIFT         1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_RUNT_VALID_FLG_MASK      0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_RUNT_VALID_FLG_SHIFT     2
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_CRC_GUARD_MASK  0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_CRC_GUARD_SHIFT 3
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_REF_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_REF_TAG_SHIFT   4
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_APP_TAG_MASK    0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_VALIDATE_APP_TAG_SHIFT   5
+#define RDMA_SQ_FMR_WQE_3RD_DIF_CRC_SEED_MASK            0x1
+#define RDMA_SQ_FMR_WQE_3RD_DIF_CRC_SEED_SHIFT           6
+#define RDMA_SQ_FMR_WQE_3RD_RESERVED4_MASK               0x1FF
+#define RDMA_SQ_FMR_WQE_3RD_RESERVED4_SHIFT              7
+       __le32 Reserved5;
+};
+
+struct rdma_sq_local_inv_wqe {
+       struct regpair reserved;
+       __le32 inv_l_key;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_LOCAL_INV_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_LOCAL_INV_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_LOCAL_INV_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_LOCAL_INV_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_LOCAL_INV_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_LOCAL_INV_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_LOCAL_INV_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_LOCAL_INV_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_LOCAL_INV_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_LOCAL_INV_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_LOCAL_INV_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_LOCAL_INV_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_LOCAL_INV_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_LOCAL_INV_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_rdma_wqe {
+       __le32 imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_RDMA_WQE_COMP_FLG_MASK                  0x1
+#define RDMA_SQ_RDMA_WQE_COMP_FLG_SHIFT                 0
+#define RDMA_SQ_RDMA_WQE_RD_FENCE_FLG_MASK              0x1
+#define RDMA_SQ_RDMA_WQE_RD_FENCE_FLG_SHIFT             1
+#define RDMA_SQ_RDMA_WQE_INV_FENCE_FLG_MASK             0x1
+#define RDMA_SQ_RDMA_WQE_INV_FENCE_FLG_SHIFT            2
+#define RDMA_SQ_RDMA_WQE_SE_FLG_MASK                    0x1
+#define RDMA_SQ_RDMA_WQE_SE_FLG_SHIFT                   3
+#define RDMA_SQ_RDMA_WQE_INLINE_FLG_MASK                0x1
+#define RDMA_SQ_RDMA_WQE_INLINE_FLG_SHIFT               4
+#define RDMA_SQ_RDMA_WQE_DIF_ON_HOST_FLG_MASK           0x1
+#define RDMA_SQ_RDMA_WQE_DIF_ON_HOST_FLG_SHIFT          5
+#define RDMA_SQ_RDMA_WQE_RESERVED0_MASK                 0x3
+#define RDMA_SQ_RDMA_WQE_RESERVED0_SHIFT                6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       struct regpair remote_va;
+       __le32 r_key;
+       u8 dif_flags;
+#define RDMA_SQ_RDMA_WQE_DIF_BLOCK_SIZE_MASK            0x1
+#define RDMA_SQ_RDMA_WQE_DIF_BLOCK_SIZE_SHIFT           0
+#define RDMA_SQ_RDMA_WQE_DIF_FIRST_RDMA_IN_IO_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_DIF_FIRST_RDMA_IN_IO_FLG_SHIFT 1
+#define RDMA_SQ_RDMA_WQE_DIF_LAST_RDMA_IN_IO_FLG_MASK   0x1
+#define RDMA_SQ_RDMA_WQE_DIF_LAST_RDMA_IN_IO_FLG_SHIFT  2
+#define RDMA_SQ_RDMA_WQE_RESERVED1_MASK                 0x1F
+#define RDMA_SQ_RDMA_WQE_RESERVED1_SHIFT                3
+       u8 reserved2[3];
+};
+
+/* First element (16 bytes) of rdma wqe */
+struct rdma_sq_rdma_wqe_1st {
+       __le32 imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_RDMA_WQE_1ST_COMP_FLG_MASK         0x1
+#define RDMA_SQ_RDMA_WQE_1ST_COMP_FLG_SHIFT        0
+#define RDMA_SQ_RDMA_WQE_1ST_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_RDMA_WQE_1ST_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_RDMA_WQE_1ST_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_RDMA_WQE_1ST_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_RDMA_WQE_1ST_SE_FLG_MASK           0x1
+#define RDMA_SQ_RDMA_WQE_1ST_SE_FLG_SHIFT          3
+#define RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_RDMA_WQE_1ST_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_1ST_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_RDMA_WQE_1ST_RESERVED0_MASK        0x3
+#define RDMA_SQ_RDMA_WQE_1ST_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+/* Second element (16 bytes) of rdma wqe */
+struct rdma_sq_rdma_wqe_2nd {
+       struct regpair remote_va;
+       __le32 r_key;
+       u8 dif_flags;
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_BLOCK_SIZE_MASK         0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_BLOCK_SIZE_SHIFT        0
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_FIRST_SEGMENT_FLG_MASK  0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_FIRST_SEGMENT_FLG_SHIFT 1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_LAST_SEGMENT_FLG_MASK   0x1
+#define RDMA_SQ_RDMA_WQE_2ND_DIF_LAST_SEGMENT_FLG_SHIFT  2
+#define RDMA_SQ_RDMA_WQE_2ND_RESERVED1_MASK              0x1F
+#define RDMA_SQ_RDMA_WQE_2ND_RESERVED1_SHIFT             3
+       u8 reserved2[3];
+};
+
+/* SQ WQE req type enumeration */
+enum rdma_sq_req_type {
+       RDMA_SQ_REQ_TYPE_SEND,
+       RDMA_SQ_REQ_TYPE_SEND_WITH_IMM,
+       RDMA_SQ_REQ_TYPE_SEND_WITH_INVALIDATE,
+       RDMA_SQ_REQ_TYPE_RDMA_WR,
+       RDMA_SQ_REQ_TYPE_RDMA_WR_WITH_IMM,
+       RDMA_SQ_REQ_TYPE_RDMA_RD,
+       RDMA_SQ_REQ_TYPE_ATOMIC_CMP_AND_SWAP,
+       RDMA_SQ_REQ_TYPE_ATOMIC_ADD,
+       RDMA_SQ_REQ_TYPE_LOCAL_INVALIDATE,
+       RDMA_SQ_REQ_TYPE_FAST_MR,
+       RDMA_SQ_REQ_TYPE_BIND,
+       RDMA_SQ_REQ_TYPE_INVALID,
+       MAX_RDMA_SQ_REQ_TYPE
+};
+
+struct rdma_sq_send_wqe {
+       __le32 inv_key_or_imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_SEND_WQE_COMP_FLG_MASK         0x1
+#define RDMA_SQ_SEND_WQE_COMP_FLG_SHIFT        0
+#define RDMA_SQ_SEND_WQE_RD_FENCE_FLG_MASK     0x1
+#define RDMA_SQ_SEND_WQE_RD_FENCE_FLG_SHIFT    1
+#define RDMA_SQ_SEND_WQE_INV_FENCE_FLG_MASK    0x1
+#define RDMA_SQ_SEND_WQE_INV_FENCE_FLG_SHIFT   2
+#define RDMA_SQ_SEND_WQE_SE_FLG_MASK           0x1
+#define RDMA_SQ_SEND_WQE_SE_FLG_SHIFT          3
+#define RDMA_SQ_SEND_WQE_INLINE_FLG_MASK       0x1
+#define RDMA_SQ_SEND_WQE_INLINE_FLG_SHIFT      4
+#define RDMA_SQ_SEND_WQE_DIF_ON_HOST_FLG_MASK  0x1
+#define RDMA_SQ_SEND_WQE_DIF_ON_HOST_FLG_SHIFT 5
+#define RDMA_SQ_SEND_WQE_RESERVED0_MASK        0x3
+#define RDMA_SQ_SEND_WQE_RESERVED0_SHIFT       6
+       u8 wqe_size;
+       u8 prev_wqe_size;
+       __le32 reserved1[4];
+};
+
+struct rdma_sq_send_wqe_1st {
+       __le32 inv_key_or_imm_data;
+       __le32 length;
+       __le32 xrc_srq;
+       u8 req_type;
+       u8 flags;
+#define RDMA_SQ_SEND_WQE_1ST_COMP_FLG_MASK       0x1
+#define RDMA_SQ_SEND_WQE_1ST_COMP_FLG_SHIFT      0
+#define RDMA_SQ_SEND_WQE_1ST_RD_FENCE_FLG_MASK   0x1
+#define RDMA_SQ_SEND_WQE_1ST_RD_FENCE_FLG_SHIFT  1
+#define RDMA_SQ_SEND_WQE_1ST_INV_FENCE_FLG_MASK  0x1
+#define RDMA_SQ_SEND_WQE_1ST_INV_FENCE_FLG_SHIFT 2
+#define RDMA_SQ_SEND_WQE_1ST_SE_FLG_MASK         0x1
+#define RDMA_SQ_SEND_WQE_1ST_SE_FLG_SHIFT        3
+#define RDMA_SQ_SEND_WQE_1ST_INLINE_FLG_MASK     0x1
+#define RDMA_SQ_SEND_WQE_1ST_INLINE_FLG_SHIFT    4
+#define RDMA_SQ_SEND_WQE_1ST_RESERVED0_MASK      0x7
+#define RDMA_SQ_SEND_WQE_1ST_RESERVED0_SHIFT     5
+       u8 wqe_size;
+       u8 prev_wqe_size;
+};
+
+struct rdma_sq_send_wqe_2st {
+       __le32 reserved1[4];
+};
+
+#endif /* __QED_HSI_RDMA__ */
diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c
new file mode 100644 (file)
index 0000000..a615142
--- /dev/null
@@ -0,0 +1,3547 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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/dma-mapping.h>
+#include <linux/crc32.h>
+#include <net/ip.h>
+#include <net/ipv6.h>
+#include <net/udp.h>
+#include <linux/iommu.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_user_verbs.h>
+#include <rdma/iw_cm.h>
+#include <rdma/ib_umem.h>
+#include <rdma/ib_addr.h>
+#include <rdma/ib_cache.h>
+
+#include "qedr_hsi.h"
+#include <linux/qed/qed_if.h>
+#include "qedr.h"
+#include "verbs.h"
+#include <rdma/qedr-abi.h>
+#include "qedr_cm.h"
+
+#define DB_ADDR_SHIFT(addr)            ((addr) << DB_PWM_ADDR_OFFSET_SHIFT)
+
+int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey)
+{
+       if (index > QEDR_ROCE_PKEY_TABLE_LEN)
+               return -EINVAL;
+
+       *pkey = QEDR_ROCE_PKEY_DEFAULT;
+       return 0;
+}
+
+int qedr_query_gid(struct ib_device *ibdev, u8 port, int index,
+                  union ib_gid *sgid)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       int rc = 0;
+
+       if (!rdma_cap_roce_gid_table(ibdev, port))
+               return -ENODEV;
+
+       rc = ib_get_cached_gid(ibdev, port, index, sgid, NULL);
+       if (rc == -EAGAIN) {
+               memcpy(sgid, &zgid, sizeof(*sgid));
+               return 0;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "query gid: index=%d %llx:%llx\n", index,
+                sgid->global.interface_id, sgid->global.subnet_prefix);
+
+       return rc;
+}
+
+int qedr_add_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, const union ib_gid *gid,
+                const struct ib_gid_attr *attr, void **context)
+{
+       if (!rdma_cap_roce_gid_table(device, port_num))
+               return -EINVAL;
+
+       if (port_num > QEDR_MAX_PORT)
+               return -EINVAL;
+
+       if (!context)
+               return -EINVAL;
+
+       return 0;
+}
+
+int qedr_del_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, void **context)
+{
+       if (!rdma_cap_roce_gid_table(device, port_num))
+               return -EINVAL;
+
+       if (port_num > QEDR_MAX_PORT)
+               return -EINVAL;
+
+       if (!context)
+               return -EINVAL;
+
+       return 0;
+}
+
+int qedr_query_device(struct ib_device *ibdev,
+                     struct ib_device_attr *attr, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qedr_device_attr *qattr = &dev->attr;
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev,
+                      "qedr_query_device called with invalid params rdma_ctx=%p\n",
+                      dev->rdma_ctx);
+               return -EINVAL;
+       }
+
+       memset(attr, 0, sizeof(*attr));
+
+       attr->fw_ver = qattr->fw_ver;
+       attr->sys_image_guid = qattr->sys_image_guid;
+       attr->max_mr_size = qattr->max_mr_size;
+       attr->page_size_cap = qattr->page_size_caps;
+       attr->vendor_id = qattr->vendor_id;
+       attr->vendor_part_id = qattr->vendor_part_id;
+       attr->hw_ver = qattr->hw_ver;
+       attr->max_qp = qattr->max_qp;
+       attr->max_qp_wr = max_t(u32, qattr->max_sqe, qattr->max_rqe);
+       attr->device_cap_flags = IB_DEVICE_CURR_QP_STATE_MOD |
+           IB_DEVICE_RC_RNR_NAK_GEN |
+           IB_DEVICE_LOCAL_DMA_LKEY | IB_DEVICE_MEM_MGT_EXTENSIONS;
+
+       attr->max_sge = qattr->max_sge;
+       attr->max_sge_rd = qattr->max_sge;
+       attr->max_cq = qattr->max_cq;
+       attr->max_cqe = qattr->max_cqe;
+       attr->max_mr = qattr->max_mr;
+       attr->max_mw = qattr->max_mw;
+       attr->max_pd = qattr->max_pd;
+       attr->atomic_cap = dev->atomic_cap;
+       attr->max_fmr = qattr->max_fmr;
+       attr->max_map_per_fmr = 16;
+       attr->max_qp_init_rd_atom =
+           1 << (fls(qattr->max_qp_req_rd_atomic_resc) - 1);
+       attr->max_qp_rd_atom =
+           min(1 << (fls(qattr->max_qp_resp_rd_atomic_resc) - 1),
+               attr->max_qp_init_rd_atom);
+
+       attr->max_srq = qattr->max_srq;
+       attr->max_srq_sge = qattr->max_srq_sge;
+       attr->max_srq_wr = qattr->max_srq_wr;
+
+       attr->local_ca_ack_delay = qattr->dev_ack_delay;
+       attr->max_fast_reg_page_list_len = qattr->max_mr / 8;
+       attr->max_pkeys = QEDR_ROCE_PKEY_MAX;
+       attr->max_ah = qattr->max_ah;
+
+       return 0;
+}
+
+#define QEDR_SPEED_SDR         (1)
+#define QEDR_SPEED_DDR         (2)
+#define QEDR_SPEED_QDR         (4)
+#define QEDR_SPEED_FDR10       (8)
+#define QEDR_SPEED_FDR         (16)
+#define QEDR_SPEED_EDR         (32)
+
+static inline void get_link_speed_and_width(int speed, u8 *ib_speed,
+                                           u8 *ib_width)
+{
+       switch (speed) {
+       case 1000:
+               *ib_speed = QEDR_SPEED_SDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+       case 10000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+
+       case 20000:
+               *ib_speed = QEDR_SPEED_DDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 25000:
+               *ib_speed = QEDR_SPEED_EDR;
+               *ib_width = IB_WIDTH_1X;
+               break;
+
+       case 40000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 50000:
+               *ib_speed = QEDR_SPEED_QDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       case 100000:
+               *ib_speed = QEDR_SPEED_EDR;
+               *ib_width = IB_WIDTH_4X;
+               break;
+
+       default:
+               /* Unsupported */
+               *ib_speed = QEDR_SPEED_SDR;
+               *ib_width = IB_WIDTH_1X;
+       }
+}
+
+int qedr_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *attr)
+{
+       struct qedr_dev *dev;
+       struct qed_rdma_port *rdma_port;
+
+       dev = get_qedr_dev(ibdev);
+       if (port > 1) {
+               DP_ERR(dev, "invalid_port=0x%x\n", port);
+               return -EINVAL;
+       }
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev, "rdma_ctx is NULL\n");
+               return -EINVAL;
+       }
+
+       rdma_port = dev->ops->rdma_query_port(dev->rdma_ctx);
+       memset(attr, 0, sizeof(*attr));
+
+       if (rdma_port->port_state == QED_RDMA_PORT_UP) {
+               attr->state = IB_PORT_ACTIVE;
+               attr->phys_state = 5;
+       } else {
+               attr->state = IB_PORT_DOWN;
+               attr->phys_state = 3;
+       }
+       attr->max_mtu = IB_MTU_4096;
+       attr->active_mtu = iboe_get_mtu(dev->ndev->mtu);
+       attr->lid = 0;
+       attr->lmc = 0;
+       attr->sm_lid = 0;
+       attr->sm_sl = 0;
+       attr->port_cap_flags = IB_PORT_IP_BASED_GIDS;
+       attr->gid_tbl_len = QEDR_MAX_SGID;
+       attr->pkey_tbl_len = QEDR_ROCE_PKEY_TABLE_LEN;
+       attr->bad_pkey_cntr = rdma_port->pkey_bad_counter;
+       attr->qkey_viol_cntr = 0;
+       get_link_speed_and_width(rdma_port->link_speed,
+                                &attr->active_speed, &attr->active_width);
+       attr->max_msg_sz = rdma_port->max_msg_size;
+       attr->max_vl_num = 4;
+
+       return 0;
+}
+
+int qedr_modify_port(struct ib_device *ibdev, u8 port, int mask,
+                    struct ib_port_modify *props)
+{
+       struct qedr_dev *dev;
+
+       dev = get_qedr_dev(ibdev);
+       if (port > 1) {
+               DP_ERR(dev, "invalid_port=0x%x\n", port);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int qedr_add_mmap(struct qedr_ucontext *uctx, u64 phy_addr,
+                        unsigned long len)
+{
+       struct qedr_mm *mm;
+
+       mm = kzalloc(sizeof(*mm), GFP_KERNEL);
+       if (!mm)
+               return -ENOMEM;
+
+       mm->key.phy_addr = phy_addr;
+       /* This function might be called with a length which is not a multiple
+        * of PAGE_SIZE, while the mapping is PAGE_SIZE grained and the kernel
+        * forces this granularity by increasing the requested size if needed.
+        * When qedr_mmap is called, it will search the list with the updated
+        * length as a key. To prevent search failures, the length is rounded up
+        * in advance to PAGE_SIZE.
+        */
+       mm->key.len = roundup(len, PAGE_SIZE);
+       INIT_LIST_HEAD(&mm->entry);
+
+       mutex_lock(&uctx->mm_list_lock);
+       list_add(&mm->entry, &uctx->mm_head);
+       mutex_unlock(&uctx->mm_list_lock);
+
+       DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                "added (addr=0x%llx,len=0x%lx) for ctx=%p\n",
+                (unsigned long long)mm->key.phy_addr,
+                (unsigned long)mm->key.len, uctx);
+
+       return 0;
+}
+
+static bool qedr_search_mmap(struct qedr_ucontext *uctx, u64 phy_addr,
+                            unsigned long len)
+{
+       bool found = false;
+       struct qedr_mm *mm;
+
+       mutex_lock(&uctx->mm_list_lock);
+       list_for_each_entry(mm, &uctx->mm_head, entry) {
+               if (len != mm->key.len || phy_addr != mm->key.phy_addr)
+                       continue;
+
+               found = true;
+               break;
+       }
+       mutex_unlock(&uctx->mm_list_lock);
+       DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                "searched for (addr=0x%llx,len=0x%lx) for ctx=%p, result=%d\n",
+                mm->key.phy_addr, mm->key.len, uctx, found);
+
+       return found;
+}
+
+struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev,
+                                       struct ib_udata *udata)
+{
+       int rc;
+       struct qedr_ucontext *ctx;
+       struct qedr_alloc_ucontext_resp uresp;
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qed_rdma_add_user_out_params oparams;
+
+       if (!udata)
+               return ERR_PTR(-EFAULT);
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       rc = dev->ops->rdma_add_user(dev->rdma_ctx, &oparams);
+       if (rc) {
+               DP_ERR(dev,
+                      "failed to allocate a DPI for a new RoCE application, rc=%d. To overcome this consider to increase the number of DPIs, increase the doorbell BAR size or just close unnecessary RoCE applications. In order to increase the number of DPIs consult the qedr readme\n",
+                      rc);
+               goto err;
+       }
+
+       ctx->dpi = oparams.dpi;
+       ctx->dpi_addr = oparams.dpi_addr;
+       ctx->dpi_phys_addr = oparams.dpi_phys_addr;
+       ctx->dpi_size = oparams.dpi_size;
+       INIT_LIST_HEAD(&ctx->mm_head);
+       mutex_init(&ctx->mm_list_lock);
+
+       memset(&uresp, 0, sizeof(uresp));
+
+       uresp.db_pa = ctx->dpi_phys_addr;
+       uresp.db_size = ctx->dpi_size;
+       uresp.max_send_wr = dev->attr.max_sqe;
+       uresp.max_recv_wr = dev->attr.max_rqe;
+       uresp.max_srq_wr = dev->attr.max_srq_wr;
+       uresp.sges_per_send_wr = QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       uresp.sges_per_recv_wr = QEDR_MAX_RQE_ELEMENTS_PER_RQE;
+       uresp.sges_per_srq_wr = dev->attr.max_srq_sge;
+       uresp.max_cqes = QEDR_MAX_CQES;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               goto err;
+
+       ctx->dev = dev;
+
+       rc = qedr_add_mmap(ctx, ctx->dpi_phys_addr, ctx->dpi_size);
+       if (rc)
+               goto err;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Allocating user context %p\n",
+                &ctx->ibucontext);
+       return &ctx->ibucontext;
+
+err:
+       kfree(ctx);
+       return ERR_PTR(rc);
+}
+
+int qedr_dealloc_ucontext(struct ib_ucontext *ibctx)
+{
+       struct qedr_ucontext *uctx = get_qedr_ucontext(ibctx);
+       struct qedr_mm *mm, *tmp;
+       int status = 0;
+
+       DP_DEBUG(uctx->dev, QEDR_MSG_INIT, "Deallocating user context %p\n",
+                uctx);
+       uctx->dev->ops->rdma_remove_user(uctx->dev->rdma_ctx, uctx->dpi);
+
+       list_for_each_entry_safe(mm, tmp, &uctx->mm_head, entry) {
+               DP_DEBUG(uctx->dev, QEDR_MSG_MISC,
+                        "deleted (addr=0x%llx,len=0x%lx) for ctx=%p\n",
+                        mm->key.phy_addr, mm->key.len, uctx);
+               list_del(&mm->entry);
+               kfree(mm);
+       }
+
+       kfree(uctx);
+       return status;
+}
+
+int qedr_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
+{
+       struct qedr_ucontext *ucontext = get_qedr_ucontext(context);
+       struct qedr_dev *dev = get_qedr_dev(context->device);
+       unsigned long vm_page = vma->vm_pgoff << PAGE_SHIFT;
+       u64 unmapped_db = dev->db_phys_addr;
+       unsigned long len = (vma->vm_end - vma->vm_start);
+       int rc = 0;
+       bool found;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT,
+                "qedr_mmap called vm_page=0x%lx vm_pgoff=0x%lx unmapped_db=0x%llx db_size=%x, len=%lx\n",
+                vm_page, vma->vm_pgoff, unmapped_db, dev->db_size, len);
+       if (vma->vm_start & (PAGE_SIZE - 1)) {
+               DP_ERR(dev, "Vma_start not page aligned = %ld\n",
+                      vma->vm_start);
+               return -EINVAL;
+       }
+
+       found = qedr_search_mmap(ucontext, vm_page, len);
+       if (!found) {
+               DP_ERR(dev, "Vma_pgoff not found in mapped array = %ld\n",
+                      vma->vm_pgoff);
+               return -EINVAL;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping doorbell bar\n");
+
+       if ((vm_page >= unmapped_db) && (vm_page <= (unmapped_db +
+                                                    dev->db_size))) {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping doorbell bar\n");
+               if (vma->vm_flags & VM_READ) {
+                       DP_ERR(dev, "Trying to map doorbell bar for read\n");
+                       return -EPERM;
+               }
+
+               vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+               rc = io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+                                       PAGE_SIZE, vma->vm_page_prot);
+       } else {
+               DP_DEBUG(dev, QEDR_MSG_INIT, "Mapping chains\n");
+               rc = remap_pfn_range(vma, vma->vm_start,
+                                    vma->vm_pgoff, len, vma->vm_page_prot);
+       }
+       DP_DEBUG(dev, QEDR_MSG_INIT, "qedr_mmap return code: %d\n", rc);
+       return rc;
+}
+
+struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev,
+                           struct ib_ucontext *context, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qedr_ucontext *uctx = NULL;
+       struct qedr_alloc_pd_uresp uresp;
+       struct qedr_pd *pd;
+       u16 pd_id;
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Function called from: %s\n",
+                (udata && context) ? "User Lib" : "Kernel");
+
+       if (!dev->rdma_ctx) {
+               DP_ERR(dev, "invlaid RDMA context\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+       if (!pd)
+               return ERR_PTR(-ENOMEM);
+
+       dev->ops->rdma_alloc_pd(dev->rdma_ctx, &pd_id);
+
+       uresp.pd_id = pd_id;
+       pd->pd_id = pd_id;
+
+       if (udata && context) {
+               rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+               if (rc)
+                       DP_ERR(dev, "copy error pd_id=0x%x.\n", pd_id);
+               uctx = get_qedr_ucontext(context);
+               uctx->pd = pd;
+               pd->uctx = uctx;
+       }
+
+       return &pd->ibpd;
+}
+
+int qedr_dealloc_pd(struct ib_pd *ibpd)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+
+       if (!pd)
+               pr_err("Invalid PD received in dealloc_pd\n");
+
+       DP_DEBUG(dev, QEDR_MSG_INIT, "Deallocating PD %d\n", pd->pd_id);
+       dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd->pd_id);
+
+       kfree(pd);
+
+       return 0;
+}
+
+static void qedr_free_pbl(struct qedr_dev *dev,
+                         struct qedr_pbl_info *pbl_info, struct qedr_pbl *pbl)
+{
+       struct pci_dev *pdev = dev->pdev;
+       int i;
+
+       for (i = 0; i < pbl_info->num_pbls; i++) {
+               if (!pbl[i].va)
+                       continue;
+               dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
+                                 pbl[i].va, pbl[i].pa);
+       }
+
+       kfree(pbl);
+}
+
+#define MIN_FW_PBL_PAGE_SIZE (4 * 1024)
+#define MAX_FW_PBL_PAGE_SIZE (64 * 1024)
+
+#define NUM_PBES_ON_PAGE(_page_size) (_page_size / sizeof(u64))
+#define MAX_PBES_ON_PAGE NUM_PBES_ON_PAGE(MAX_FW_PBL_PAGE_SIZE)
+#define MAX_PBES_TWO_LAYER (MAX_PBES_ON_PAGE * MAX_PBES_ON_PAGE)
+
+static struct qedr_pbl *qedr_alloc_pbl_tbl(struct qedr_dev *dev,
+                                          struct qedr_pbl_info *pbl_info,
+                                          gfp_t flags)
+{
+       struct pci_dev *pdev = dev->pdev;
+       struct qedr_pbl *pbl_table;
+       dma_addr_t *pbl_main_tbl;
+       dma_addr_t pa;
+       void *va;
+       int i;
+
+       pbl_table = kcalloc(pbl_info->num_pbls, sizeof(*pbl_table), flags);
+       if (!pbl_table)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < pbl_info->num_pbls; i++) {
+               va = dma_alloc_coherent(&pdev->dev, pbl_info->pbl_size,
+                                       &pa, flags);
+               if (!va)
+                       goto err;
+
+               memset(va, 0, pbl_info->pbl_size);
+               pbl_table[i].va = va;
+               pbl_table[i].pa = pa;
+       }
+
+       /* Two-Layer PBLs, if we have more than one pbl we need to initialize
+        * the first one with physical pointers to all of the rest
+        */
+       pbl_main_tbl = (dma_addr_t *)pbl_table[0].va;
+       for (i = 0; i < pbl_info->num_pbls - 1; i++)
+               pbl_main_tbl[i] = pbl_table[i + 1].pa;
+
+       return pbl_table;
+
+err:
+       for (i--; i >= 0; i--)
+               dma_free_coherent(&pdev->dev, pbl_info->pbl_size,
+                                 pbl_table[i].va, pbl_table[i].pa);
+
+       qedr_free_pbl(dev, pbl_info, pbl_table);
+
+       return ERR_PTR(-ENOMEM);
+}
+
+static int qedr_prepare_pbl_tbl(struct qedr_dev *dev,
+                               struct qedr_pbl_info *pbl_info,
+                               u32 num_pbes, int two_layer_capable)
+{
+       u32 pbl_capacity;
+       u32 pbl_size;
+       u32 num_pbls;
+
+       if ((num_pbes > MAX_PBES_ON_PAGE) && two_layer_capable) {
+               if (num_pbes > MAX_PBES_TWO_LAYER) {
+                       DP_ERR(dev, "prepare pbl table: too many pages %d\n",
+                              num_pbes);
+                       return -EINVAL;
+               }
+
+               /* calculate required pbl page size */
+               pbl_size = MIN_FW_PBL_PAGE_SIZE;
+               pbl_capacity = NUM_PBES_ON_PAGE(pbl_size) *
+                              NUM_PBES_ON_PAGE(pbl_size);
+
+               while (pbl_capacity < num_pbes) {
+                       pbl_size *= 2;
+                       pbl_capacity = pbl_size / sizeof(u64);
+                       pbl_capacity = pbl_capacity * pbl_capacity;
+               }
+
+               num_pbls = DIV_ROUND_UP(num_pbes, NUM_PBES_ON_PAGE(pbl_size));
+               num_pbls++;     /* One for the layer0 ( points to the pbls) */
+               pbl_info->two_layered = true;
+       } else {
+               /* One layered PBL */
+               num_pbls = 1;
+               pbl_size = max_t(u32, MIN_FW_PBL_PAGE_SIZE,
+                                roundup_pow_of_two((num_pbes * sizeof(u64))));
+               pbl_info->two_layered = false;
+       }
+
+       pbl_info->num_pbls = num_pbls;
+       pbl_info->pbl_size = pbl_size;
+       pbl_info->num_pbes = num_pbes;
+
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "prepare pbl table: num_pbes=%d, num_pbls=%d, pbl_size=%d\n",
+                pbl_info->num_pbes, pbl_info->num_pbls, pbl_info->pbl_size);
+
+       return 0;
+}
+
+static void qedr_populate_pbls(struct qedr_dev *dev, struct ib_umem *umem,
+                              struct qedr_pbl *pbl,
+                              struct qedr_pbl_info *pbl_info)
+{
+       int shift, pg_cnt, pages, pbe_cnt, total_num_pbes = 0;
+       struct qedr_pbl *pbl_tbl;
+       struct scatterlist *sg;
+       struct regpair *pbe;
+       int entry;
+       u32 addr;
+
+       if (!pbl_info->num_pbes)
+               return;
+
+       /* If we have a two layered pbl, the first pbl points to the rest
+        * of the pbls and the first entry lays on the second pbl in the table
+        */
+       if (pbl_info->two_layered)
+               pbl_tbl = &pbl[1];
+       else
+               pbl_tbl = pbl;
+
+       pbe = (struct regpair *)pbl_tbl->va;
+       if (!pbe) {
+               DP_ERR(dev, "cannot populate PBL due to a NULL PBE\n");
+               return;
+       }
+
+       pbe_cnt = 0;
+
+       shift = ilog2(umem->page_size);
+
+       for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
+               pages = sg_dma_len(sg) >> shift;
+               for (pg_cnt = 0; pg_cnt < pages; pg_cnt++) {
+                       /* store the page address in pbe */
+                       pbe->lo = cpu_to_le32(sg_dma_address(sg) +
+                                             umem->page_size * pg_cnt);
+                       addr = upper_32_bits(sg_dma_address(sg) +
+                                            umem->page_size * pg_cnt);
+                       pbe->hi = cpu_to_le32(addr);
+                       pbe_cnt++;
+                       total_num_pbes++;
+                       pbe++;
+
+                       if (total_num_pbes == pbl_info->num_pbes)
+                               return;
+
+                       /* If the given pbl is full storing the pbes,
+                        * move to next pbl.
+                        */
+                       if (pbe_cnt == (pbl_info->pbl_size / sizeof(u64))) {
+                               pbl_tbl++;
+                               pbe = (struct regpair *)pbl_tbl->va;
+                               pbe_cnt = 0;
+                       }
+               }
+       }
+}
+
+static int qedr_copy_cq_uresp(struct qedr_dev *dev,
+                             struct qedr_cq *cq, struct ib_udata *udata)
+{
+       struct qedr_create_cq_uresp uresp;
+       int rc;
+
+       memset(&uresp, 0, sizeof(uresp));
+
+       uresp.db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT);
+       uresp.icid = cq->icid;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               DP_ERR(dev, "copy error cqid=0x%x.\n", cq->icid);
+
+       return rc;
+}
+
+static void consume_cqe(struct qedr_cq *cq)
+{
+       if (cq->latest_cqe == cq->toggle_cqe)
+               cq->pbl_toggle ^= RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
+
+       cq->latest_cqe = qed_chain_consume(&cq->pbl);
+}
+
+static inline int qedr_align_cq_entries(int entries)
+{
+       u64 size, aligned_size;
+
+       /* We allocate an extra entry that we don't report to the FW. */
+       size = (entries + 1) * QEDR_CQE_SIZE;
+       aligned_size = ALIGN(size, PAGE_SIZE);
+
+       return aligned_size / QEDR_CQE_SIZE;
+}
+
+static inline int qedr_init_user_queue(struct ib_ucontext *ib_ctx,
+                                      struct qedr_dev *dev,
+                                      struct qedr_userq *q,
+                                      u64 buf_addr, size_t buf_len,
+                                      int access, int dmasync)
+{
+       int page_cnt;
+       int rc;
+
+       q->buf_addr = buf_addr;
+       q->buf_len = buf_len;
+       q->umem = ib_umem_get(ib_ctx, q->buf_addr, q->buf_len, access, dmasync);
+       if (IS_ERR(q->umem)) {
+               DP_ERR(dev, "create user queue: failed ib_umem_get, got %ld\n",
+                      PTR_ERR(q->umem));
+               return PTR_ERR(q->umem);
+       }
+
+       page_cnt = ib_umem_page_count(q->umem);
+       rc = qedr_prepare_pbl_tbl(dev, &q->pbl_info, page_cnt, 0);
+       if (rc)
+               goto err0;
+
+       q->pbl_tbl = qedr_alloc_pbl_tbl(dev, &q->pbl_info, GFP_KERNEL);
+       if (IS_ERR_OR_NULL(q->pbl_tbl))
+               goto err0;
+
+       qedr_populate_pbls(dev, q->umem, q->pbl_tbl, &q->pbl_info);
+
+       return 0;
+
+err0:
+       ib_umem_release(q->umem);
+
+       return rc;
+}
+
+static inline void qedr_init_cq_params(struct qedr_cq *cq,
+                                      struct qedr_ucontext *ctx,
+                                      struct qedr_dev *dev, int vector,
+                                      int chain_entries, int page_cnt,
+                                      u64 pbl_ptr,
+                                      struct qed_rdma_create_cq_in_params
+                                      *params)
+{
+       memset(params, 0, sizeof(*params));
+       params->cq_handle_hi = upper_32_bits((uintptr_t)cq);
+       params->cq_handle_lo = lower_32_bits((uintptr_t)cq);
+       params->cnq_id = vector;
+       params->cq_size = chain_entries - 1;
+       params->dpi = (ctx) ? ctx->dpi : dev->dpi;
+       params->pbl_num_pages = page_cnt;
+       params->pbl_ptr = pbl_ptr;
+       params->pbl_two_level = 0;
+}
+
+static void doorbell_cq(struct qedr_cq *cq, u32 cons, u8 flags)
+{
+       /* Flush data before signalling doorbell */
+       wmb();
+       cq->db.data.agg_flags = flags;
+       cq->db.data.value = cpu_to_le32(cons);
+       writeq(cq->db.raw, cq->db_addr);
+
+       /* Make sure write would stick */
+       mmiowb();
+}
+
+int qedr_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags)
+{
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       unsigned long sflags;
+
+       if (cq->cq_type == QEDR_CQ_TYPE_GSI)
+               return 0;
+
+       spin_lock_irqsave(&cq->cq_lock, sflags);
+
+       cq->arm_flags = 0;
+
+       if (flags & IB_CQ_SOLICITED)
+               cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_SE_CF_CMD;
+
+       if (flags & IB_CQ_NEXT_COMP)
+               cq->arm_flags |= DQ_UCM_ROCE_CQ_ARM_CF_CMD;
+
+       doorbell_cq(cq, cq->cq_cons - 1, cq->arm_flags);
+
+       spin_unlock_irqrestore(&cq->cq_lock, sflags);
+
+       return 0;
+}
+
+struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_ctx, struct ib_udata *udata)
+{
+       struct qedr_ucontext *ctx = get_qedr_ucontext(ib_ctx);
+       struct qed_rdma_destroy_cq_out_params destroy_oparams;
+       struct qed_rdma_destroy_cq_in_params destroy_iparams;
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+       struct qed_rdma_create_cq_in_params params;
+       struct qedr_create_cq_ureq ureq;
+       int vector = attr->comp_vector;
+       int entries = attr->cqe;
+       struct qedr_cq *cq;
+       int chain_entries;
+       int page_cnt;
+       u64 pbl_ptr;
+       u16 icid;
+       int rc;
+
+       DP_DEBUG(dev, QEDR_MSG_INIT,
+                "create_cq: called from %s. entries=%d, vector=%d\n",
+                udata ? "User Lib" : "Kernel", entries, vector);
+
+       if (entries > QEDR_MAX_CQES) {
+               DP_ERR(dev,
+                      "create cq: the number of entries %d is too high. Must be equal or below %d.\n",
+                      entries, QEDR_MAX_CQES);
+               return ERR_PTR(-EINVAL);
+       }
+
+       chain_entries = qedr_align_cq_entries(entries);
+       chain_entries = min_t(int, chain_entries, QEDR_MAX_CQES);
+
+       cq = kzalloc(sizeof(*cq), GFP_KERNEL);
+       if (!cq)
+               return ERR_PTR(-ENOMEM);
+
+       if (udata) {
+               memset(&ureq, 0, sizeof(ureq));
+               if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
+                       DP_ERR(dev,
+                              "create cq: problem copying data from user space\n");
+                       goto err0;
+               }
+
+               if (!ureq.len) {
+                       DP_ERR(dev,
+                              "create cq: cannot create a cq with 0 entries\n");
+                       goto err0;
+               }
+
+               cq->cq_type = QEDR_CQ_TYPE_USER;
+
+               rc = qedr_init_user_queue(ib_ctx, dev, &cq->q, ureq.addr,
+                                         ureq.len, IB_ACCESS_LOCAL_WRITE, 1);
+               if (rc)
+                       goto err0;
+
+               pbl_ptr = cq->q.pbl_tbl->pa;
+               page_cnt = cq->q.pbl_info.num_pbes;
+       } else {
+               cq->cq_type = QEDR_CQ_TYPE_KERNEL;
+
+               rc = dev->ops->common->chain_alloc(dev->cdev,
+                                                  QED_CHAIN_USE_TO_CONSUME,
+                                                  QED_CHAIN_MODE_PBL,
+                                                  QED_CHAIN_CNT_TYPE_U32,
+                                                  chain_entries,
+                                                  sizeof(union rdma_cqe),
+                                                  &cq->pbl);
+               if (rc)
+                       goto err1;
+
+               page_cnt = qed_chain_get_page_cnt(&cq->pbl);
+               pbl_ptr = qed_chain_get_pbl_phys(&cq->pbl);
+       }
+
+       qedr_init_cq_params(cq, ctx, dev, vector, chain_entries, page_cnt,
+                           pbl_ptr, &params);
+
+       rc = dev->ops->rdma_create_cq(dev->rdma_ctx, &params, &icid);
+       if (rc)
+               goto err2;
+
+       cq->icid = icid;
+       cq->sig = QEDR_CQ_MAGIC_NUMBER;
+       spin_lock_init(&cq->cq_lock);
+
+       if (ib_ctx) {
+               rc = qedr_copy_cq_uresp(dev, cq, udata);
+               if (rc)
+                       goto err3;
+       } else {
+               /* Generate doorbell address. */
+               cq->db_addr = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT);
+               cq->db.data.icid = cq->icid;
+               cq->db.data.params = DB_AGG_CMD_SET <<
+                   RDMA_PWM_VAL32_DATA_AGG_CMD_SHIFT;
+
+               /* point to the very last element, passing it we will toggle */
+               cq->toggle_cqe = qed_chain_get_last_elem(&cq->pbl);
+               cq->pbl_toggle = RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK;
+               cq->latest_cqe = NULL;
+               consume_cqe(cq);
+               cq->cq_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_CQ,
+                "create cq: icid=0x%0x, addr=%p, size(entries)=0x%0x\n",
+                cq->icid, cq, params.cq_size);
+
+       return &cq->ibcq;
+
+err3:
+       destroy_iparams.icid = cq->icid;
+       dev->ops->rdma_destroy_cq(dev->rdma_ctx, &destroy_iparams,
+                                 &destroy_oparams);
+err2:
+       if (udata)
+               qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
+       else
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+err1:
+       if (udata)
+               ib_umem_release(cq->q.umem);
+err0:
+       kfree(cq);
+       return ERR_PTR(-EINVAL);
+}
+
+int qedr_resize_cq(struct ib_cq *ibcq, int new_cnt, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+
+       DP_ERR(dev, "cq %p RESIZE NOT SUPPORTED\n", cq);
+
+       return 0;
+}
+
+int qedr_destroy_cq(struct ib_cq *ibcq)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qed_rdma_destroy_cq_out_params oparams;
+       struct qed_rdma_destroy_cq_in_params iparams;
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+
+       DP_DEBUG(dev, QEDR_MSG_CQ, "destroy cq: cq_id %d", cq->icid);
+
+       /* GSIs CQs are handled by driver, so they don't exist in the FW */
+       if (cq->cq_type != QEDR_CQ_TYPE_GSI) {
+               iparams.icid = cq->icid;
+               dev->ops->rdma_destroy_cq(dev->rdma_ctx, &iparams, &oparams);
+               dev->ops->common->chain_free(dev->cdev, &cq->pbl);
+       }
+
+       if (ibcq->uobject && ibcq->uobject->context) {
+               qedr_free_pbl(dev, &cq->q.pbl_info, cq->q.pbl_tbl);
+               ib_umem_release(cq->q.umem);
+       }
+
+       kfree(cq);
+
+       return 0;
+}
+
+static inline int get_gid_info_from_table(struct ib_qp *ibqp,
+                                         struct ib_qp_attr *attr,
+                                         int attr_mask,
+                                         struct qed_rdma_modify_qp_in_params
+                                         *qp_params)
+{
+       enum rdma_network_type nw_type;
+       struct ib_gid_attr gid_attr;
+       union ib_gid gid;
+       u32 ipv4_addr;
+       int rc = 0;
+       int i;
+
+       rc = ib_get_cached_gid(ibqp->device, attr->ah_attr.port_num,
+                              attr->ah_attr.grh.sgid_index, &gid, &gid_attr);
+       if (rc)
+               return rc;
+
+       if (!memcmp(&gid, &zgid, sizeof(gid)))
+               return -ENOENT;
+
+       if (gid_attr.ndev) {
+               qp_params->vlan_id = rdma_vlan_dev_vlan_id(gid_attr.ndev);
+
+               dev_put(gid_attr.ndev);
+               nw_type = ib_gid_to_network_type(gid_attr.gid_type, &gid);
+               switch (nw_type) {
+               case RDMA_NETWORK_IPV6:
+                       memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+                              sizeof(qp_params->sgid));
+                       memcpy(&qp_params->dgid.bytes[0],
+                              &attr->ah_attr.grh.dgid,
+                              sizeof(qp_params->dgid));
+                       qp_params->roce_mode = ROCE_V2_IPV6;
+                       SET_FIELD(qp_params->modify_flags,
+                                 QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+                       break;
+               case RDMA_NETWORK_IB:
+                       memcpy(&qp_params->sgid.bytes[0], &gid.raw[0],
+                              sizeof(qp_params->sgid));
+                       memcpy(&qp_params->dgid.bytes[0],
+                              &attr->ah_attr.grh.dgid,
+                              sizeof(qp_params->dgid));
+                       qp_params->roce_mode = ROCE_V1;
+                       break;
+               case RDMA_NETWORK_IPV4:
+                       memset(&qp_params->sgid, 0, sizeof(qp_params->sgid));
+                       memset(&qp_params->dgid, 0, sizeof(qp_params->dgid));
+                       ipv4_addr = qedr_get_ipv4_from_gid(gid.raw);
+                       qp_params->sgid.ipv4_addr = ipv4_addr;
+                       ipv4_addr =
+                           qedr_get_ipv4_from_gid(attr->ah_attr.grh.dgid.raw);
+                       qp_params->dgid.ipv4_addr = ipv4_addr;
+                       SET_FIELD(qp_params->modify_flags,
+                                 QED_ROCE_MODIFY_QP_VALID_ROCE_MODE, 1);
+                       qp_params->roce_mode = ROCE_V2_IPV4;
+                       break;
+               }
+       }
+
+       for (i = 0; i < 4; i++) {
+               qp_params->sgid.dwords[i] = ntohl(qp_params->sgid.dwords[i]);
+               qp_params->dgid.dwords[i] = ntohl(qp_params->dgid.dwords[i]);
+       }
+
+       if (qp_params->vlan_id >= VLAN_CFI_MASK)
+               qp_params->vlan_id = 0;
+
+       return 0;
+}
+
+static void qedr_cleanup_user_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qedr_free_pbl(dev, &qp->usq.pbl_info, qp->usq.pbl_tbl);
+       ib_umem_release(qp->usq.umem);
+}
+
+static void qedr_cleanup_user_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qedr_free_pbl(dev, &qp->urq.pbl_info, qp->urq.pbl_tbl);
+       ib_umem_release(qp->urq.umem);
+}
+
+static void qedr_cleanup_kernel_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl);
+       kfree(qp->wqe_wr_id);
+}
+
+static void qedr_cleanup_kernel_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl);
+       kfree(qp->rqe_wr_id);
+}
+
+static int qedr_check_qp_attrs(struct ib_pd *ibpd, struct qedr_dev *dev,
+                              struct ib_qp_init_attr *attrs)
+{
+       struct qedr_device_attr *qattr = &dev->attr;
+
+       /* QP0... attrs->qp_type == IB_QPT_GSI */
+       if (attrs->qp_type != IB_QPT_RC && attrs->qp_type != IB_QPT_GSI) {
+               DP_DEBUG(dev, QEDR_MSG_QP,
+                        "create qp: unsupported qp type=0x%x requested\n",
+                        attrs->qp_type);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_wr > qattr->max_sqe) {
+               DP_ERR(dev,
+                      "create qp: cannot create a SQ with %d elements (max_send_wr=0x%x)\n",
+                      attrs->cap.max_send_wr, qattr->max_sqe);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_inline_data > qattr->max_inline) {
+               DP_ERR(dev,
+                      "create qp: unsupported inline data size=0x%x requested (max_inline=0x%x)\n",
+                      attrs->cap.max_inline_data, qattr->max_inline);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_send_sge > qattr->max_sge) {
+               DP_ERR(dev,
+                      "create qp: unsupported send_sge=0x%x requested (max_send_sge=0x%x)\n",
+                      attrs->cap.max_send_sge, qattr->max_sge);
+               return -EINVAL;
+       }
+
+       if (attrs->cap.max_recv_sge > qattr->max_sge) {
+               DP_ERR(dev,
+                      "create qp: unsupported recv_sge=0x%x requested (max_recv_sge=0x%x)\n",
+                      attrs->cap.max_recv_sge, qattr->max_sge);
+               return -EINVAL;
+       }
+
+       /* Unprivileged user space cannot create special QP */
+       if (ibpd->uobject && attrs->qp_type == IB_QPT_GSI) {
+               DP_ERR(dev,
+                      "create qp: userspace can't create special QPs of type=0x%x\n",
+                      attrs->qp_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void qedr_copy_rq_uresp(struct qedr_create_qp_uresp *uresp,
+                              struct qedr_qp *qp)
+{
+       uresp->rq_db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
+       uresp->rq_icid = qp->icid;
+}
+
+static void qedr_copy_sq_uresp(struct qedr_create_qp_uresp *uresp,
+                              struct qedr_qp *qp)
+{
+       uresp->sq_db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
+       uresp->sq_icid = qp->icid + 1;
+}
+
+static int qedr_copy_qp_uresp(struct qedr_dev *dev,
+                             struct qedr_qp *qp, struct ib_udata *udata)
+{
+       struct qedr_create_qp_uresp uresp;
+       int rc;
+
+       memset(&uresp, 0, sizeof(uresp));
+       qedr_copy_sq_uresp(&uresp, qp);
+       qedr_copy_rq_uresp(&uresp, qp);
+
+       uresp.atomic_supported = dev->atomic_cap != IB_ATOMIC_NONE;
+       uresp.qp_id = qp->qp_id;
+
+       rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp));
+       if (rc)
+               DP_ERR(dev,
+                      "create qp: failed a copy to user space with qp icid=0x%x.\n",
+                      qp->icid);
+
+       return rc;
+}
+
+static void qedr_set_qp_init_params(struct qedr_dev *dev,
+                                   struct qedr_qp *qp,
+                                   struct qedr_pd *pd,
+                                   struct ib_qp_init_attr *attrs)
+{
+       qp->pd = pd;
+
+       spin_lock_init(&qp->q_lock);
+
+       qp->qp_type = attrs->qp_type;
+       qp->max_inline_data = attrs->cap.max_inline_data;
+       qp->sq.max_sges = attrs->cap.max_send_sge;
+       qp->state = QED_ROCE_QP_STATE_RESET;
+       qp->signaled = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) ? true : false;
+       qp->sq_cq = get_qedr_cq(attrs->send_cq);
+       qp->rq_cq = get_qedr_cq(attrs->recv_cq);
+       qp->dev = dev;
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "QP params:\tpd = %d, qp_type = %d, max_inline_data = %d, state = %d, signaled = %d, use_srq=%d\n",
+                pd->pd_id, qp->qp_type, qp->max_inline_data,
+                qp->state, qp->signaled, (attrs->srq) ? 1 : 0);
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "SQ params:\tsq_max_sges = %d, sq_cq_id = %d\n",
+                qp->sq.max_sges, qp->sq_cq->icid);
+       qp->rq.max_sges = attrs->cap.max_recv_sge;
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "RQ params:\trq_max_sges = %d, rq_cq_id = %d\n",
+                qp->rq.max_sges, qp->rq_cq->icid);
+}
+
+static inline void
+qedr_init_qp_user_params(struct qed_rdma_create_qp_in_params *params,
+                        struct qedr_create_qp_ureq *ureq)
+{
+       /* QP handle to be written in CQE */
+       params->qp_handle_lo = ureq->qp_handle_lo;
+       params->qp_handle_hi = ureq->qp_handle_hi;
+}
+
+static inline void
+qedr_init_qp_kernel_doorbell_sq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qp->sq.db = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_XCM_RDMA_SQ_PROD);
+       qp->sq.db_data.data.icid = qp->icid + 1;
+}
+
+static inline void
+qedr_init_qp_kernel_doorbell_rq(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       qp->rq.db = dev->db_addr +
+                   DB_ADDR_SHIFT(DQ_PWM_OFFSET_TCM_ROCE_RQ_PROD);
+       qp->rq.db_data.data.icid = qp->icid;
+}
+
+static inline int
+qedr_init_qp_kernel_params_rq(struct qedr_dev *dev,
+                             struct qedr_qp *qp, struct ib_qp_init_attr *attrs)
+{
+       /* Allocate driver internal RQ array */
+       qp->rqe_wr_id = kcalloc(qp->rq.max_wr, sizeof(*qp->rqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->rqe_wr_id)
+               return -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "RQ max_wr set to %d.\n", qp->rq.max_wr);
+
+       return 0;
+}
+
+static inline int
+qedr_init_qp_kernel_params_sq(struct qedr_dev *dev,
+                             struct qedr_qp *qp,
+                             struct ib_qp_init_attr *attrs,
+                             struct qed_rdma_create_qp_in_params *params)
+{
+       u32 temp_max_wr;
+
+       /* Allocate driver internal SQ array */
+       temp_max_wr = attrs->cap.max_send_wr * dev->wq_multiplier;
+       temp_max_wr = min_t(u32, temp_max_wr, dev->attr.max_sqe);
+
+       /* temp_max_wr < attr->max_sqe < u16 so the casting is safe */
+       qp->sq.max_wr = (u16)temp_max_wr;
+       qp->wqe_wr_id = kcalloc(qp->sq.max_wr, sizeof(*qp->wqe_wr_id),
+                               GFP_KERNEL);
+       if (!qp->wqe_wr_id)
+               return -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "SQ max_wr set to %d.\n", qp->sq.max_wr);
+
+       /* QP handle to be written in CQE */
+       params->qp_handle_lo = lower_32_bits((uintptr_t)qp);
+       params->qp_handle_hi = upper_32_bits((uintptr_t)qp);
+
+       return 0;
+}
+
+static inline int qedr_init_qp_kernel_sq(struct qedr_dev *dev,
+                                        struct qedr_qp *qp,
+                                        struct ib_qp_init_attr *attrs)
+{
+       u32 n_sq_elems, n_sq_entries;
+       int rc;
+
+       /* A single work request may take up to QEDR_MAX_SQ_WQE_SIZE elements in
+        * the ring. The ring should allow at least a single WR, even if the
+        * user requested none, due to allocation issues.
+        */
+       n_sq_entries = attrs->cap.max_send_wr;
+       n_sq_entries = min_t(u32, n_sq_entries, dev->attr.max_sqe);
+       n_sq_entries = max_t(u32, n_sq_entries, 1);
+       n_sq_elems = n_sq_entries * QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       rc = dev->ops->common->chain_alloc(dev->cdev,
+                                          QED_CHAIN_USE_TO_PRODUCE,
+                                          QED_CHAIN_MODE_PBL,
+                                          QED_CHAIN_CNT_TYPE_U32,
+                                          n_sq_elems,
+                                          QEDR_SQE_ELEMENT_SIZE,
+                                          &qp->sq.pbl);
+       if (rc) {
+               DP_ERR(dev, "failed to allocate QP %p SQ\n", qp);
+               return rc;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_SQ,
+                "SQ Pbl base addr = %llx max_send_wr=%d max_wr=%d capacity=%d, rc=%d\n",
+                qed_chain_get_pbl_phys(&qp->sq.pbl), attrs->cap.max_send_wr,
+                n_sq_entries, qed_chain_get_capacity(&qp->sq.pbl), rc);
+       return 0;
+}
+
+static inline int qedr_init_qp_kernel_rq(struct qedr_dev *dev,
+                                        struct qedr_qp *qp,
+                                        struct ib_qp_init_attr *attrs)
+{
+       u32 n_rq_elems, n_rq_entries;
+       int rc;
+
+       /* A single work request may take up to QEDR_MAX_RQ_WQE_SIZE elements in
+        * the ring. There ring should allow at least a single WR, even if the
+        * user requested none, due to allocation issues.
+        */
+       n_rq_entries = max_t(u32, attrs->cap.max_recv_wr, 1);
+       n_rq_elems = n_rq_entries * QEDR_MAX_RQE_ELEMENTS_PER_RQE;
+       rc = dev->ops->common->chain_alloc(dev->cdev,
+                                          QED_CHAIN_USE_TO_CONSUME_PRODUCE,
+                                          QED_CHAIN_MODE_PBL,
+                                          QED_CHAIN_CNT_TYPE_U32,
+                                          n_rq_elems,
+                                          QEDR_RQE_ELEMENT_SIZE,
+                                          &qp->rq.pbl);
+
+       if (rc) {
+               DP_ERR(dev, "failed to allocate memory for QP %p RQ\n", qp);
+               return -ENOMEM;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_RQ,
+                "RQ Pbl base addr = %llx max_recv_wr=%d max_wr=%d capacity=%d, rc=%d\n",
+                qed_chain_get_pbl_phys(&qp->rq.pbl), attrs->cap.max_recv_wr,
+                n_rq_entries, qed_chain_get_capacity(&qp->rq.pbl), rc);
+
+       /* n_rq_entries < u16 so the casting is safe */
+       qp->rq.max_wr = (u16)n_rq_entries;
+
+       return 0;
+}
+
+static inline void
+qedr_init_qp_in_params_sq(struct qedr_dev *dev,
+                         struct qedr_pd *pd,
+                         struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs,
+                         struct ib_udata *udata,
+                         struct qed_rdma_create_qp_in_params *params)
+{
+       /* QP handle to be written in an async event */
+       params->qp_handle_async_lo = lower_32_bits((uintptr_t)qp);
+       params->qp_handle_async_hi = upper_32_bits((uintptr_t)qp);
+
+       params->signal_all = (attrs->sq_sig_type == IB_SIGNAL_ALL_WR);
+       params->fmr_and_reserved_lkey = !udata;
+       params->pd = pd->pd_id;
+       params->dpi = pd->uctx ? pd->uctx->dpi : dev->dpi;
+       params->sq_cq_id = get_qedr_cq(attrs->send_cq)->icid;
+       params->max_sq_sges = 0;
+       params->stats_queue = 0;
+
+       if (udata) {
+               params->sq_num_pages = qp->usq.pbl_info.num_pbes;
+               params->sq_pbl_ptr = qp->usq.pbl_tbl->pa;
+       } else {
+               params->sq_num_pages = qed_chain_get_page_cnt(&qp->sq.pbl);
+               params->sq_pbl_ptr = qed_chain_get_pbl_phys(&qp->sq.pbl);
+       }
+}
+
+static inline void
+qedr_init_qp_in_params_rq(struct qedr_qp *qp,
+                         struct ib_qp_init_attr *attrs,
+                         struct ib_udata *udata,
+                         struct qed_rdma_create_qp_in_params *params)
+{
+       params->rq_cq_id = get_qedr_cq(attrs->recv_cq)->icid;
+       params->srq_id = 0;
+       params->use_srq = false;
+
+       if (udata) {
+               params->rq_num_pages = qp->urq.pbl_info.num_pbes;
+               params->rq_pbl_ptr = qp->urq.pbl_tbl->pa;
+       } else {
+               params->rq_num_pages = qed_chain_get_page_cnt(&qp->rq.pbl);
+               params->rq_pbl_ptr = qed_chain_get_pbl_phys(&qp->rq.pbl);
+       }
+}
+
+static inline void qedr_qp_user_print(struct qedr_dev *dev, struct qedr_qp *qp)
+{
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "create qp: successfully created user QP. qp=%p, sq_addr=0x%llx, sq_len=%zd, rq_addr=0x%llx, rq_len=%zd\n",
+                qp, qp->usq.buf_addr, qp->usq.buf_len, qp->urq.buf_addr,
+                qp->urq.buf_len);
+}
+
+static inline int qedr_init_user_qp(struct ib_ucontext *ib_ctx,
+                                   struct qedr_dev *dev,
+                                   struct qedr_qp *qp,
+                                   struct qedr_create_qp_ureq *ureq)
+{
+       int rc;
+
+       /* SQ - read access only (0), dma sync not required (0) */
+       rc = qedr_init_user_queue(ib_ctx, dev, &qp->usq, ureq->sq_addr,
+                                 ureq->sq_len, 0, 0);
+       if (rc)
+               return rc;
+
+       /* RQ - read access only (0), dma sync not required (0) */
+       rc = qedr_init_user_queue(ib_ctx, dev, &qp->urq, ureq->rq_addr,
+                                 ureq->rq_len, 0, 0);
+
+       if (rc)
+               qedr_cleanup_user_sq(dev, qp);
+       return rc;
+}
+
+static inline int
+qedr_init_kernel_qp(struct qedr_dev *dev,
+                   struct qedr_qp *qp,
+                   struct ib_qp_init_attr *attrs,
+                   struct qed_rdma_create_qp_in_params *params)
+{
+       int rc;
+
+       rc = qedr_init_qp_kernel_sq(dev, qp, attrs);
+       if (rc) {
+               DP_ERR(dev, "failed to init kernel QP %p SQ\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_params_sq(dev, qp, attrs, params);
+       if (rc) {
+               dev->ops->common->chain_free(dev->cdev, &qp->sq.pbl);
+               DP_ERR(dev, "failed to init kernel QP %p SQ params\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_rq(dev, qp, attrs);
+       if (rc) {
+               qedr_cleanup_kernel_sq(dev, qp);
+               DP_ERR(dev, "failed to init kernel QP %p RQ\n", qp);
+               return rc;
+       }
+
+       rc = qedr_init_qp_kernel_params_rq(dev, qp, attrs);
+       if (rc) {
+               DP_ERR(dev, "failed to init kernel QP %p RQ params\n", qp);
+               qedr_cleanup_kernel_sq(dev, qp);
+               dev->ops->common->chain_free(dev->cdev, &qp->rq.pbl);
+               return rc;
+       }
+
+       return rc;
+}
+
+struct ib_qp *qedr_create_qp(struct ib_pd *ibpd,
+                            struct ib_qp_init_attr *attrs,
+                            struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qed_rdma_create_qp_out_params out_params;
+       struct qed_rdma_create_qp_in_params in_params;
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct ib_ucontext *ib_ctx = NULL;
+       struct qedr_ucontext *ctx = NULL;
+       struct qedr_create_qp_ureq ureq;
+       struct qedr_qp *qp;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "create qp: called from %s, pd=%p\n",
+                udata ? "user library" : "kernel", pd);
+
+       rc = qedr_check_qp_attrs(ibpd, dev, attrs);
+       if (rc)
+               return ERR_PTR(rc);
+
+       qp = kzalloc(sizeof(*qp), GFP_KERNEL);
+       if (!qp)
+               return ERR_PTR(-ENOMEM);
+
+       if (attrs->srq)
+               return ERR_PTR(-EINVAL);
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "create qp: sq_cq=%p, sq_icid=%d, rq_cq=%p, rq_icid=%d\n",
+                get_qedr_cq(attrs->send_cq),
+                get_qedr_cq(attrs->send_cq)->icid,
+                get_qedr_cq(attrs->recv_cq),
+                get_qedr_cq(attrs->recv_cq)->icid);
+
+       qedr_set_qp_init_params(dev, qp, pd, attrs);
+
+       if (attrs->qp_type == IB_QPT_GSI) {
+               if (udata) {
+                       DP_ERR(dev,
+                              "create qp: unexpected udata when creating GSI QP\n");
+                       goto err0;
+               }
+               return qedr_create_gsi_qp(dev, attrs, qp);
+       }
+
+       memset(&in_params, 0, sizeof(in_params));
+
+       if (udata) {
+               if (!(udata && ibpd->uobject && ibpd->uobject->context))
+                       goto err0;
+
+               ib_ctx = ibpd->uobject->context;
+               ctx = get_qedr_ucontext(ib_ctx);
+
+               memset(&ureq, 0, sizeof(ureq));
+               if (ib_copy_from_udata(&ureq, udata, sizeof(ureq))) {
+                       DP_ERR(dev,
+                              "create qp: problem copying data from user space\n");
+                       goto err0;
+               }
+
+               rc = qedr_init_user_qp(ib_ctx, dev, qp, &ureq);
+               if (rc)
+                       goto err0;
+
+               qedr_init_qp_user_params(&in_params, &ureq);
+       } else {
+               rc = qedr_init_kernel_qp(dev, qp, attrs, &in_params);
+               if (rc)
+                       goto err0;
+       }
+
+       qedr_init_qp_in_params_sq(dev, pd, qp, attrs, udata, &in_params);
+       qedr_init_qp_in_params_rq(qp, attrs, udata, &in_params);
+
+       qp->qed_qp = dev->ops->rdma_create_qp(dev->rdma_ctx,
+                                             &in_params, &out_params);
+
+       if (!qp->qed_qp)
+               goto err1;
+
+       qp->qp_id = out_params.qp_id;
+       qp->icid = out_params.icid;
+       qp->ibqp.qp_num = qp->qp_id;
+
+       if (udata) {
+               rc = qedr_copy_qp_uresp(dev, qp, udata);
+               if (rc)
+                       goto err2;
+
+               qedr_qp_user_print(dev, qp);
+       } else {
+               qedr_init_qp_kernel_doorbell_sq(dev, qp);
+               qedr_init_qp_kernel_doorbell_rq(dev, qp);
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "created %s space QP %p\n",
+                udata ? "user" : "kernel", qp);
+
+       return &qp->ibqp;
+
+err2:
+       rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
+       if (rc)
+               DP_ERR(dev, "create qp: fatal fault. rc=%d", rc);
+err1:
+       if (udata) {
+               qedr_cleanup_user_sq(dev, qp);
+               qedr_cleanup_user_rq(dev, qp);
+       } else {
+               qedr_cleanup_kernel_sq(dev, qp);
+               qedr_cleanup_kernel_rq(dev, qp);
+       }
+
+err0:
+       kfree(qp);
+
+       return ERR_PTR(-EFAULT);
+}
+
+enum ib_qp_state qedr_get_ibqp_state(enum qed_roce_qp_state qp_state)
+{
+       switch (qp_state) {
+       case QED_ROCE_QP_STATE_RESET:
+               return IB_QPS_RESET;
+       case QED_ROCE_QP_STATE_INIT:
+               return IB_QPS_INIT;
+       case QED_ROCE_QP_STATE_RTR:
+               return IB_QPS_RTR;
+       case QED_ROCE_QP_STATE_RTS:
+               return IB_QPS_RTS;
+       case QED_ROCE_QP_STATE_SQD:
+               return IB_QPS_SQD;
+       case QED_ROCE_QP_STATE_ERR:
+               return IB_QPS_ERR;
+       case QED_ROCE_QP_STATE_SQE:
+               return IB_QPS_SQE;
+       }
+       return IB_QPS_ERR;
+}
+
+enum qed_roce_qp_state qedr_get_state_from_ibqp(enum ib_qp_state qp_state)
+{
+       switch (qp_state) {
+       case IB_QPS_RESET:
+               return QED_ROCE_QP_STATE_RESET;
+       case IB_QPS_INIT:
+               return QED_ROCE_QP_STATE_INIT;
+       case IB_QPS_RTR:
+               return QED_ROCE_QP_STATE_RTR;
+       case IB_QPS_RTS:
+               return QED_ROCE_QP_STATE_RTS;
+       case IB_QPS_SQD:
+               return QED_ROCE_QP_STATE_SQD;
+       case IB_QPS_ERR:
+               return QED_ROCE_QP_STATE_ERR;
+       default:
+               return QED_ROCE_QP_STATE_ERR;
+       }
+}
+
+static void qedr_reset_qp_hwq_info(struct qedr_qp_hwq_info *qph)
+{
+       qed_chain_reset(&qph->pbl);
+       qph->prod = 0;
+       qph->cons = 0;
+       qph->wqe_cons = 0;
+       qph->db_data.data.value = cpu_to_le16(0);
+}
+
+static int qedr_update_qp_state(struct qedr_dev *dev,
+                               struct qedr_qp *qp,
+                               enum qed_roce_qp_state new_state)
+{
+       int status = 0;
+
+       if (new_state == qp->state)
+               return 1;
+
+       switch (qp->state) {
+       case QED_ROCE_QP_STATE_RESET:
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_INIT:
+                       qp->prev_wqe_size = 0;
+                       qedr_reset_qp_hwq_info(&qp->sq);
+                       qedr_reset_qp_hwq_info(&qp->rq);
+                       break;
+               default:
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_INIT:
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTR:
+                       /* Update doorbell (in case post_recv was
+                        * done before move to RTR)
+                        */
+                       wmb();
+                       writel(qp->rq.db_data.raw, qp->rq.db);
+                       /* Make sure write takes effect */
+                       mmiowb();
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_RTR:
+               /* RTR->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTS:
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_RTS:
+               /* RTS->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_SQD:
+                       break;
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_SQD:
+               /* SQD->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RTS:
+               case QED_ROCE_QP_STATE_ERR:
+                       break;
+               default:
+                       /* Invalid state change. */
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       case QED_ROCE_QP_STATE_ERR:
+               /* ERR->XXX */
+               switch (new_state) {
+               case QED_ROCE_QP_STATE_RESET:
+                       break;
+               default:
+                       status = -EINVAL;
+                       break;
+               };
+               break;
+       default:
+               status = -EINVAL;
+               break;
+       };
+
+       return status;
+}
+
+int qedr_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_udata *udata)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qed_rdma_modify_qp_in_params qp_params = { 0 };
+       struct qedr_dev *dev = get_qedr_dev(&qp->dev->ibdev);
+       enum ib_qp_state old_qp_state, new_qp_state;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP,
+                "modify qp: qp %p attr_mask=0x%x, state=%d", qp, attr_mask,
+                attr->qp_state);
+
+       old_qp_state = qedr_get_ibqp_state(qp->state);
+       if (attr_mask & IB_QP_STATE)
+               new_qp_state = attr->qp_state;
+       else
+               new_qp_state = old_qp_state;
+
+       if (!ib_modify_qp_is_ok
+           (old_qp_state, new_qp_state, ibqp->qp_type, attr_mask,
+            IB_LINK_LAYER_ETHERNET)) {
+               DP_ERR(dev,
+                      "modify qp: invalid attribute mask=0x%x specified for\n"
+                      "qpn=0x%x of type=0x%x old_qp_state=0x%x, new_qp_state=0x%x\n",
+                      attr_mask, qp->qp_id, ibqp->qp_type, old_qp_state,
+                      new_qp_state);
+               rc = -EINVAL;
+               goto err;
+       }
+
+       /* Translate the masks... */
+       if (attr_mask & IB_QP_STATE) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_NEW_STATE, 1);
+               qp_params.new_state = qedr_get_state_from_ibqp(attr->qp_state);
+       }
+
+       if (attr_mask & IB_QP_EN_SQD_ASYNC_NOTIFY)
+               qp_params.sqd_async = true;
+
+       if (attr_mask & IB_QP_PKEY_INDEX) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_PKEY, 1);
+               if (attr->pkey_index >= QEDR_ROCE_PKEY_TABLE_LEN) {
+                       rc = -EINVAL;
+                       goto err;
+               }
+
+               qp_params.pkey = QEDR_ROCE_PKEY_DEFAULT;
+       }
+
+       if (attr_mask & IB_QP_QKEY)
+               qp->qkey = attr->qkey;
+
+       if (attr_mask & IB_QP_ACCESS_FLAGS) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_RDMA_OPS_EN, 1);
+               qp_params.incoming_rdma_read_en = attr->qp_access_flags &
+                                                 IB_ACCESS_REMOTE_READ;
+               qp_params.incoming_rdma_write_en = attr->qp_access_flags &
+                                                  IB_ACCESS_REMOTE_WRITE;
+               qp_params.incoming_atomic_en = attr->qp_access_flags &
+                                              IB_ACCESS_REMOTE_ATOMIC;
+       }
+
+       if (attr_mask & (IB_QP_AV | IB_QP_PATH_MTU)) {
+               if (attr_mask & IB_QP_PATH_MTU) {
+                       if (attr->path_mtu < IB_MTU_256 ||
+                           attr->path_mtu > IB_MTU_4096) {
+                               pr_err("error: Only MTU sizes of 256, 512, 1024, 2048 and 4096 are supported by RoCE\n");
+                               rc = -EINVAL;
+                               goto err;
+                       }
+                       qp->mtu = min(ib_mtu_enum_to_int(attr->path_mtu),
+                                     ib_mtu_enum_to_int(iboe_get_mtu
+                                                        (dev->ndev->mtu)));
+               }
+
+               if (!qp->mtu) {
+                       qp->mtu =
+                       ib_mtu_enum_to_int(iboe_get_mtu(dev->ndev->mtu));
+                       pr_err("Fixing zeroed MTU to qp->mtu = %d\n", qp->mtu);
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_ADDRESS_VECTOR, 1);
+
+               qp_params.traffic_class_tos = attr->ah_attr.grh.traffic_class;
+               qp_params.flow_label = attr->ah_attr.grh.flow_label;
+               qp_params.hop_limit_ttl = attr->ah_attr.grh.hop_limit;
+
+               qp->sgid_idx = attr->ah_attr.grh.sgid_index;
+
+               rc = get_gid_info_from_table(ibqp, attr, attr_mask, &qp_params);
+               if (rc) {
+                       DP_ERR(dev,
+                              "modify qp: problems with GID index %d (rc=%d)\n",
+                              attr->ah_attr.grh.sgid_index, rc);
+                       return rc;
+               }
+
+               rc = qedr_get_dmac(dev, &attr->ah_attr,
+                                  qp_params.remote_mac_addr);
+               if (rc)
+                       return rc;
+
+               qp_params.use_local_mac = true;
+               ether_addr_copy(qp_params.local_mac_addr, dev->ndev->dev_addr);
+
+               DP_DEBUG(dev, QEDR_MSG_QP, "dgid=%x:%x:%x:%x\n",
+                        qp_params.dgid.dwords[0], qp_params.dgid.dwords[1],
+                        qp_params.dgid.dwords[2], qp_params.dgid.dwords[3]);
+               DP_DEBUG(dev, QEDR_MSG_QP, "sgid=%x:%x:%x:%x\n",
+                        qp_params.sgid.dwords[0], qp_params.sgid.dwords[1],
+                        qp_params.sgid.dwords[2], qp_params.sgid.dwords[3]);
+               DP_DEBUG(dev, QEDR_MSG_QP, "remote_mac=[%pM]\n",
+                        qp_params.remote_mac_addr);
+;
+
+               qp_params.mtu = qp->mtu;
+               qp_params.lb_indication = false;
+       }
+
+       if (!qp_params.mtu) {
+               /* Stay with current MTU */
+               if (qp->mtu)
+                       qp_params.mtu = qp->mtu;
+               else
+                       qp_params.mtu =
+                           ib_mtu_enum_to_int(iboe_get_mtu(dev->ndev->mtu));
+       }
+
+       if (attr_mask & IB_QP_TIMEOUT) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_ACK_TIMEOUT, 1);
+
+               qp_params.ack_timeout = attr->timeout;
+               if (attr->timeout) {
+                       u32 temp;
+
+                       temp = 4096 * (1UL << attr->timeout) / 1000 / 1000;
+                       /* FW requires [msec] */
+                       qp_params.ack_timeout = temp;
+               } else {
+                       /* Infinite */
+                       qp_params.ack_timeout = 0;
+               }
+       }
+       if (attr_mask & IB_QP_RETRY_CNT) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RETRY_CNT, 1);
+               qp_params.retry_cnt = attr->retry_cnt;
+       }
+
+       if (attr_mask & IB_QP_RNR_RETRY) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RNR_RETRY_CNT, 1);
+               qp_params.rnr_retry_cnt = attr->rnr_retry;
+       }
+
+       if (attr_mask & IB_QP_RQ_PSN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_RQ_PSN, 1);
+               qp_params.rq_psn = attr->rq_psn;
+               qp->rq_psn = attr->rq_psn;
+       }
+
+       if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) {
+               if (attr->max_rd_atomic > dev->attr.max_qp_req_rd_atomic_resc) {
+                       rc = -EINVAL;
+                       DP_ERR(dev,
+                              "unsupported max_rd_atomic=%d, supported=%d\n",
+                              attr->max_rd_atomic,
+                              dev->attr.max_qp_req_rd_atomic_resc);
+                       goto err;
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_REQ, 1);
+               qp_params.max_rd_atomic_req = attr->max_rd_atomic;
+       }
+
+       if (attr_mask & IB_QP_MIN_RNR_TIMER) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_MIN_RNR_NAK_TIMER, 1);
+               qp_params.min_rnr_nak_timer = attr->min_rnr_timer;
+       }
+
+       if (attr_mask & IB_QP_SQ_PSN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_SQ_PSN, 1);
+               qp_params.sq_psn = attr->sq_psn;
+               qp->sq_psn = attr->sq_psn;
+       }
+
+       if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) {
+               if (attr->max_dest_rd_atomic >
+                   dev->attr.max_qp_resp_rd_atomic_resc) {
+                       DP_ERR(dev,
+                              "unsupported max_dest_rd_atomic=%d, supported=%d\n",
+                              attr->max_dest_rd_atomic,
+                              dev->attr.max_qp_resp_rd_atomic_resc);
+
+                       rc = -EINVAL;
+                       goto err;
+               }
+
+               SET_FIELD(qp_params.modify_flags,
+                         QED_RDMA_MODIFY_QP_VALID_MAX_RD_ATOMIC_RESP, 1);
+               qp_params.max_rd_atomic_resp = attr->max_dest_rd_atomic;
+       }
+
+       if (attr_mask & IB_QP_DEST_QPN) {
+               SET_FIELD(qp_params.modify_flags,
+                         QED_ROCE_MODIFY_QP_VALID_DEST_QP, 1);
+
+               qp_params.dest_qp = attr->dest_qp_num;
+               qp->dest_qp_num = attr->dest_qp_num;
+       }
+
+       if (qp->qp_type != IB_QPT_GSI)
+               rc = dev->ops->rdma_modify_qp(dev->rdma_ctx,
+                                             qp->qed_qp, &qp_params);
+
+       if (attr_mask & IB_QP_STATE) {
+               if ((qp->qp_type != IB_QPT_GSI) && (!udata))
+                       qedr_update_qp_state(dev, qp, qp_params.new_state);
+               qp->state = qp_params.new_state;
+       }
+
+err:
+       return rc;
+}
+
+static int qedr_to_ib_qp_acc_flags(struct qed_rdma_query_qp_out_params *params)
+{
+       int ib_qp_acc_flags = 0;
+
+       if (params->incoming_rdma_write_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_WRITE;
+       if (params->incoming_rdma_read_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_READ;
+       if (params->incoming_atomic_en)
+               ib_qp_acc_flags |= IB_ACCESS_REMOTE_ATOMIC;
+       ib_qp_acc_flags |= IB_ACCESS_LOCAL_WRITE;
+       return ib_qp_acc_flags;
+}
+
+int qedr_query_qp(struct ib_qp *ibqp,
+                 struct ib_qp_attr *qp_attr,
+                 int attr_mask, struct ib_qp_init_attr *qp_init_attr)
+{
+       struct qed_rdma_query_qp_out_params params;
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       int rc = 0;
+
+       memset(&params, 0, sizeof(params));
+
+       rc = dev->ops->rdma_query_qp(dev->rdma_ctx, qp->qed_qp, &params);
+       if (rc)
+               goto err;
+
+       memset(qp_attr, 0, sizeof(*qp_attr));
+       memset(qp_init_attr, 0, sizeof(*qp_init_attr));
+
+       qp_attr->qp_state = qedr_get_ibqp_state(params.state);
+       qp_attr->cur_qp_state = qedr_get_ibqp_state(params.state);
+       qp_attr->path_mtu = iboe_get_mtu(params.mtu);
+       qp_attr->path_mig_state = IB_MIG_MIGRATED;
+       qp_attr->rq_psn = params.rq_psn;
+       qp_attr->sq_psn = params.sq_psn;
+       qp_attr->dest_qp_num = params.dest_qp;
+
+       qp_attr->qp_access_flags = qedr_to_ib_qp_acc_flags(&params);
+
+       qp_attr->cap.max_send_wr = qp->sq.max_wr;
+       qp_attr->cap.max_recv_wr = qp->rq.max_wr;
+       qp_attr->cap.max_send_sge = qp->sq.max_sges;
+       qp_attr->cap.max_recv_sge = qp->rq.max_sges;
+       qp_attr->cap.max_inline_data = qp->max_inline_data;
+       qp_init_attr->cap = qp_attr->cap;
+
+       memcpy(&qp_attr->ah_attr.grh.dgid.raw[0], &params.dgid.bytes[0],
+              sizeof(qp_attr->ah_attr.grh.dgid.raw));
+
+       qp_attr->ah_attr.grh.flow_label = params.flow_label;
+       qp_attr->ah_attr.grh.sgid_index = qp->sgid_idx;
+       qp_attr->ah_attr.grh.hop_limit = params.hop_limit_ttl;
+       qp_attr->ah_attr.grh.traffic_class = params.traffic_class_tos;
+
+       qp_attr->ah_attr.ah_flags = IB_AH_GRH;
+       qp_attr->ah_attr.port_num = 1;
+       qp_attr->ah_attr.sl = 0;
+       qp_attr->timeout = params.timeout;
+       qp_attr->rnr_retry = params.rnr_retry;
+       qp_attr->retry_cnt = params.retry_cnt;
+       qp_attr->min_rnr_timer = params.min_rnr_nak_timer;
+       qp_attr->pkey_index = params.pkey_index;
+       qp_attr->port_num = 1;
+       qp_attr->ah_attr.src_path_bits = 0;
+       qp_attr->ah_attr.static_rate = 0;
+       qp_attr->alt_pkey_index = 0;
+       qp_attr->alt_port_num = 0;
+       qp_attr->alt_timeout = 0;
+       memset(&qp_attr->alt_ah_attr, 0, sizeof(qp_attr->alt_ah_attr));
+
+       qp_attr->sq_draining = (params.state == QED_ROCE_QP_STATE_SQD) ? 1 : 0;
+       qp_attr->max_dest_rd_atomic = params.max_dest_rd_atomic;
+       qp_attr->max_rd_atomic = params.max_rd_atomic;
+       qp_attr->en_sqd_async_notify = (params.sqd_async) ? 1 : 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "QEDR_QUERY_QP: max_inline_data=%d\n",
+                qp_attr->cap.max_inline_data);
+
+err:
+       return rc;
+}
+
+int qedr_destroy_qp(struct ib_qp *ibqp)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       struct ib_qp_attr attr;
+       int attr_mask = 0;
+       int rc = 0;
+
+       DP_DEBUG(dev, QEDR_MSG_QP, "destroy qp: destroying %p, qp type=%d\n",
+                qp, qp->qp_type);
+
+       if (qp->state != (QED_ROCE_QP_STATE_RESET | QED_ROCE_QP_STATE_ERR |
+                         QED_ROCE_QP_STATE_INIT)) {
+               attr.qp_state = IB_QPS_ERR;
+               attr_mask |= IB_QP_STATE;
+
+               /* Change the QP state to ERROR */
+               qedr_modify_qp(ibqp, &attr, attr_mask, NULL);
+       }
+
+       if (qp->qp_type != IB_QPT_GSI) {
+               rc = dev->ops->rdma_destroy_qp(dev->rdma_ctx, qp->qed_qp);
+               if (rc)
+                       return rc;
+       } else {
+               qedr_destroy_gsi_qp(dev);
+       }
+
+       if (ibqp->uobject && ibqp->uobject->context) {
+               qedr_cleanup_user_sq(dev, qp);
+               qedr_cleanup_user_rq(dev, qp);
+       } else {
+               qedr_cleanup_kernel_sq(dev, qp);
+               qedr_cleanup_kernel_rq(dev, qp);
+       }
+
+       kfree(qp);
+
+       return rc;
+}
+
+struct ib_ah *qedr_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr)
+{
+       struct qedr_ah *ah;
+
+       ah = kzalloc(sizeof(*ah), GFP_ATOMIC);
+       if (!ah)
+               return ERR_PTR(-ENOMEM);
+
+       ah->attr = *attr;
+
+       return &ah->ibah;
+}
+
+int qedr_destroy_ah(struct ib_ah *ibah)
+{
+       struct qedr_ah *ah = get_qedr_ah(ibah);
+
+       kfree(ah);
+       return 0;
+}
+
+static void free_mr_info(struct qedr_dev *dev, struct mr_info *info)
+{
+       struct qedr_pbl *pbl, *tmp;
+
+       if (info->pbl_table)
+               list_add_tail(&info->pbl_table->list_entry,
+                             &info->free_pbl_list);
+
+       if (!list_empty(&info->inuse_pbl_list))
+               list_splice(&info->inuse_pbl_list, &info->free_pbl_list);
+
+       list_for_each_entry_safe(pbl, tmp, &info->free_pbl_list, list_entry) {
+               list_del(&pbl->list_entry);
+               qedr_free_pbl(dev, &info->pbl_info, pbl);
+       }
+}
+
+static int init_mr_info(struct qedr_dev *dev, struct mr_info *info,
+                       size_t page_list_len, bool two_layered)
+{
+       struct qedr_pbl *tmp;
+       int rc;
+
+       INIT_LIST_HEAD(&info->free_pbl_list);
+       INIT_LIST_HEAD(&info->inuse_pbl_list);
+
+       rc = qedr_prepare_pbl_tbl(dev, &info->pbl_info,
+                                 page_list_len, two_layered);
+       if (rc)
+               goto done;
+
+       info->pbl_table = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL);
+       if (!info->pbl_table) {
+               rc = -ENOMEM;
+               goto done;
+       }
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "pbl_table_pa = %pa\n",
+                &info->pbl_table->pa);
+
+       /* in usual case we use 2 PBLs, so we add one to free
+        * list and allocating another one
+        */
+       tmp = qedr_alloc_pbl_tbl(dev, &info->pbl_info, GFP_KERNEL);
+       if (!tmp) {
+               DP_DEBUG(dev, QEDR_MSG_MR, "Extra PBL is not allocated\n");
+               goto done;
+       }
+
+       list_add_tail(&tmp->list_entry, &info->free_pbl_list);
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "extra pbl_table_pa = %pa\n", &tmp->pa);
+
+done:
+       if (rc)
+               free_mr_info(dev, info);
+
+       return rc;
+}
+
+struct ib_mr *qedr_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len,
+                              u64 usr_addr, int acc, struct ib_udata *udata)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_mr *mr;
+       struct qedr_pd *pd;
+       int rc = -ENOMEM;
+
+       pd = get_qedr_pd(ibpd);
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "qedr_register user mr pd = %d start = %lld, len = %lld, usr_addr = %lld, acc = %d\n",
+                pd->pd_id, start, len, usr_addr, acc);
+
+       if (acc & IB_ACCESS_REMOTE_WRITE && !(acc & IB_ACCESS_LOCAL_WRITE))
+               return ERR_PTR(-EINVAL);
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(rc);
+
+       mr->type = QEDR_MR_USER;
+
+       mr->umem = ib_umem_get(ibpd->uobject->context, start, len, acc, 0);
+       if (IS_ERR(mr->umem)) {
+               rc = -EFAULT;
+               goto err0;
+       }
+
+       rc = init_mr_info(dev, &mr->info, ib_umem_page_count(mr->umem), 1);
+       if (rc)
+               goto err1;
+
+       qedr_populate_pbls(dev, mr->umem, mr->info.pbl_table,
+                          &mr->info.pbl_info);
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       /* Index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_REGISTERED_MR;
+       mr->hw_mr.key = 0;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = (acc & IB_ACCESS_LOCAL_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_read = (acc & IB_ACCESS_REMOTE_READ) ? 1 : 0;
+       mr->hw_mr.remote_write = (acc & IB_ACCESS_REMOTE_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_atomic = (acc & IB_ACCESS_REMOTE_ATOMIC) ? 1 : 0;
+       mr->hw_mr.mw_bind = false;
+       mr->hw_mr.pbl_ptr = mr->info.pbl_table[0].pa;
+       mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered;
+       mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size);
+       mr->hw_mr.page_size_log = ilog2(mr->umem->page_size);
+       mr->hw_mr.fbo = ib_umem_offset(mr->umem);
+       mr->hw_mr.length = len;
+       mr->hw_mr.vaddr = usr_addr;
+       mr->hw_mr.zbva = false;
+       mr->hw_mr.phy_mr = false;
+       mr->hw_mr.dma_mr = false;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err2;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       if (mr->hw_mr.remote_write || mr->hw_mr.remote_read ||
+           mr->hw_mr.remote_atomic)
+               mr->ibmr.rkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "register user mr lkey: %x\n",
+                mr->ibmr.lkey);
+       return &mr->ibmr;
+
+err2:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err1:
+       qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table);
+err0:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+int qedr_dereg_mr(struct ib_mr *ib_mr)
+{
+       struct qedr_mr *mr = get_qedr_mr(ib_mr);
+       struct qedr_dev *dev = get_qedr_dev(ib_mr->device);
+       int rc = 0;
+
+       rc = dev->ops->rdma_deregister_tid(dev->rdma_ctx, mr->hw_mr.itid);
+       if (rc)
+               return rc;
+
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+
+       if ((mr->type != QEDR_MR_DMA) && (mr->type != QEDR_MR_FRMR))
+               qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table);
+
+       /* it could be user registered memory. */
+       if (mr->umem)
+               ib_umem_release(mr->umem);
+
+       kfree(mr);
+
+       return rc;
+}
+
+struct qedr_mr *__qedr_alloc_mr(struct ib_pd *ibpd, int max_page_list_len)
+{
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_mr *mr;
+       int rc = -ENOMEM;
+
+       DP_DEBUG(dev, QEDR_MSG_MR,
+                "qedr_alloc_frmr pd = %d max_page_list_len= %d\n", pd->pd_id,
+                max_page_list_len);
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(rc);
+
+       mr->dev = dev;
+       mr->type = QEDR_MR_FRMR;
+
+       rc = init_mr_info(dev, &mr->info, max_page_list_len, 1);
+       if (rc)
+               goto err0;
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err0;
+       }
+
+       /* Index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_FMR;
+       mr->hw_mr.key = 0;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = 0;
+       mr->hw_mr.remote_read = 0;
+       mr->hw_mr.remote_write = 0;
+       mr->hw_mr.remote_atomic = 0;
+       mr->hw_mr.mw_bind = false;
+       mr->hw_mr.pbl_ptr = 0;
+       mr->hw_mr.pbl_two_level = mr->info.pbl_info.two_layered;
+       mr->hw_mr.pbl_page_size_log = ilog2(mr->info.pbl_info.pbl_size);
+       mr->hw_mr.fbo = 0;
+       mr->hw_mr.length = 0;
+       mr->hw_mr.vaddr = 0;
+       mr->hw_mr.zbva = false;
+       mr->hw_mr.phy_mr = true;
+       mr->hw_mr.dma_mr = false;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       mr->ibmr.rkey = mr->ibmr.lkey;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "alloc frmr: %x\n", mr->ibmr.lkey);
+       return mr;
+
+err1:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err0:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+struct ib_mr *qedr_alloc_mr(struct ib_pd *ibpd,
+                           enum ib_mr_type mr_type, u32 max_num_sg)
+{
+       struct qedr_dev *dev;
+       struct qedr_mr *mr;
+
+       if (mr_type != IB_MR_TYPE_MEM_REG)
+               return ERR_PTR(-EINVAL);
+
+       mr = __qedr_alloc_mr(ibpd, max_num_sg);
+
+       if (IS_ERR(mr))
+               return ERR_PTR(-EINVAL);
+
+       dev = mr->dev;
+
+       return &mr->ibmr;
+}
+
+static int qedr_set_page(struct ib_mr *ibmr, u64 addr)
+{
+       struct qedr_mr *mr = get_qedr_mr(ibmr);
+       struct qedr_pbl *pbl_table;
+       struct regpair *pbe;
+       u32 pbes_in_page;
+
+       if (unlikely(mr->npages == mr->info.pbl_info.num_pbes)) {
+               DP_ERR(mr->dev, "qedr_set_page failes when %d\n", mr->npages);
+               return -ENOMEM;
+       }
+
+       DP_DEBUG(mr->dev, QEDR_MSG_MR, "qedr_set_page pages[%d] = 0x%llx\n",
+                mr->npages, addr);
+
+       pbes_in_page = mr->info.pbl_info.pbl_size / sizeof(u64);
+       pbl_table = mr->info.pbl_table + (mr->npages / pbes_in_page);
+       pbe = (struct regpair *)pbl_table->va;
+       pbe +=  mr->npages % pbes_in_page;
+       pbe->lo = cpu_to_le32((u32)addr);
+       pbe->hi = cpu_to_le32((u32)upper_32_bits(addr));
+
+       mr->npages++;
+
+       return 0;
+}
+
+static void handle_completed_mrs(struct qedr_dev *dev, struct mr_info *info)
+{
+       int work = info->completed - info->completed_handled - 1;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "Special FMR work = %d\n", work);
+       while (work-- > 0 && !list_empty(&info->inuse_pbl_list)) {
+               struct qedr_pbl *pbl;
+
+               /* Free all the page list that are possible to be freed
+                * (all the ones that were invalidated), under the assumption
+                * that if an FMR was completed successfully that means that
+                * if there was an invalidate operation before it also ended
+                */
+               pbl = list_first_entry(&info->inuse_pbl_list,
+                                      struct qedr_pbl, list_entry);
+               list_del(&pbl->list_entry);
+               list_add_tail(&pbl->list_entry, &info->free_pbl_list);
+               info->completed_handled++;
+       }
+}
+
+int qedr_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg,
+                  int sg_nents, unsigned int *sg_offset)
+{
+       struct qedr_mr *mr = get_qedr_mr(ibmr);
+
+       mr->npages = 0;
+
+       handle_completed_mrs(mr->dev, &mr->info);
+       return ib_sg_to_pages(ibmr, sg, sg_nents, NULL, qedr_set_page);
+}
+
+struct ib_mr *qedr_get_dma_mr(struct ib_pd *ibpd, int acc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibpd->device);
+       struct qedr_pd *pd = get_qedr_pd(ibpd);
+       struct qedr_mr *mr;
+       int rc;
+
+       mr = kzalloc(sizeof(*mr), GFP_KERNEL);
+       if (!mr)
+               return ERR_PTR(-ENOMEM);
+
+       mr->type = QEDR_MR_DMA;
+
+       rc = dev->ops->rdma_alloc_tid(dev->rdma_ctx, &mr->hw_mr.itid);
+       if (rc) {
+               DP_ERR(dev, "roce alloc tid returned an error %d\n", rc);
+               goto err1;
+       }
+
+       /* index only, 18 bit long, lkey = itid << 8 | key */
+       mr->hw_mr.tid_type = QED_RDMA_TID_REGISTERED_MR;
+       mr->hw_mr.pd = pd->pd_id;
+       mr->hw_mr.local_read = 1;
+       mr->hw_mr.local_write = (acc & IB_ACCESS_LOCAL_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_read = (acc & IB_ACCESS_REMOTE_READ) ? 1 : 0;
+       mr->hw_mr.remote_write = (acc & IB_ACCESS_REMOTE_WRITE) ? 1 : 0;
+       mr->hw_mr.remote_atomic = (acc & IB_ACCESS_REMOTE_ATOMIC) ? 1 : 0;
+       mr->hw_mr.dma_mr = true;
+
+       rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr);
+       if (rc) {
+               DP_ERR(dev, "roce register tid returned an error %d\n", rc);
+               goto err2;
+       }
+
+       mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+       if (mr->hw_mr.remote_write || mr->hw_mr.remote_read ||
+           mr->hw_mr.remote_atomic)
+               mr->ibmr.rkey = mr->hw_mr.itid << 8 | mr->hw_mr.key;
+
+       DP_DEBUG(dev, QEDR_MSG_MR, "get dma mr: lkey = %x\n", mr->ibmr.lkey);
+       return &mr->ibmr;
+
+err2:
+       dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid);
+err1:
+       kfree(mr);
+       return ERR_PTR(rc);
+}
+
+static inline int qedr_wq_is_full(struct qedr_qp_hwq_info *wq)
+{
+       return (((wq->prod + 1) % wq->max_wr) == wq->cons);
+}
+
+static int sge_data_len(struct ib_sge *sg_list, int num_sge)
+{
+       int i, len = 0;
+
+       for (i = 0; i < num_sge; i++)
+               len += sg_list[i].length;
+
+       return len;
+}
+
+static void swap_wqe_data64(u64 *p)
+{
+       int i;
+
+       for (i = 0; i < QEDR_SQE_ELEMENT_SIZE / sizeof(u64); i++, p++)
+               *p = cpu_to_be64(cpu_to_le64(*p));
+}
+
+static u32 qedr_prepare_sq_inline_data(struct qedr_dev *dev,
+                                      struct qedr_qp *qp, u8 *wqe_size,
+                                      struct ib_send_wr *wr,
+                                      struct ib_send_wr **bad_wr, u8 *bits,
+                                      u8 bit)
+{
+       u32 data_size = sge_data_len(wr->sg_list, wr->num_sge);
+       char *seg_prt, *wqe;
+       int i, seg_siz;
+
+       if (data_size > ROCE_REQ_MAX_INLINE_DATA_SIZE) {
+               DP_ERR(dev, "Too much inline data in WR: %d\n", data_size);
+               *bad_wr = wr;
+               return 0;
+       }
+
+       if (!data_size)
+               return data_size;
+
+       *bits |= bit;
+
+       seg_prt = NULL;
+       wqe = NULL;
+       seg_siz = 0;
+
+       /* Copy data inline */
+       for (i = 0; i < wr->num_sge; i++) {
+               u32 len = wr->sg_list[i].length;
+               void *src = (void *)(uintptr_t)wr->sg_list[i].addr;
+
+               while (len > 0) {
+                       u32 cur;
+
+                       /* New segment required */
+                       if (!seg_siz) {
+                               wqe = (char *)qed_chain_produce(&qp->sq.pbl);
+                               seg_prt = wqe;
+                               seg_siz = sizeof(struct rdma_sq_common_wqe);
+                               (*wqe_size)++;
+                       }
+
+                       /* Calculate currently allowed length */
+                       cur = min_t(u32, len, seg_siz);
+                       memcpy(seg_prt, src, cur);
+
+                       /* Update segment variables */
+                       seg_prt += cur;
+                       seg_siz -= cur;
+
+                       /* Update sge variables */
+                       src += cur;
+                       len -= cur;
+
+                       /* Swap fully-completed segments */
+                       if (!seg_siz)
+                               swap_wqe_data64((u64 *)wqe);
+               }
+       }
+
+       /* swap last not completed segment */
+       if (seg_siz)
+               swap_wqe_data64((u64 *)wqe);
+
+       return data_size;
+}
+
+#define RQ_SGE_SET(sge, vaddr, vlength, vflags)                        \
+       do {                                                    \
+               DMA_REGPAIR_LE(sge->addr, vaddr);               \
+               (sge)->length = cpu_to_le32(vlength);           \
+               (sge)->flags = cpu_to_le32(vflags);             \
+       } while (0)
+
+#define SRQ_HDR_SET(hdr, vwr_id, num_sge)                      \
+       do {                                                    \
+               DMA_REGPAIR_LE(hdr->wr_id, vwr_id);             \
+               (hdr)->num_sges = num_sge;                      \
+       } while (0)
+
+#define SRQ_SGE_SET(sge, vaddr, vlength, vlkey)                        \
+       do {                                                    \
+               DMA_REGPAIR_LE(sge->addr, vaddr);               \
+               (sge)->length = cpu_to_le32(vlength);           \
+               (sge)->l_key = cpu_to_le32(vlkey);              \
+       } while (0)
+
+static u32 qedr_prepare_sq_sges(struct qedr_qp *qp, u8 *wqe_size,
+                               struct ib_send_wr *wr)
+{
+       u32 data_size = 0;
+       int i;
+
+       for (i = 0; i < wr->num_sge; i++) {
+               struct rdma_sq_sge *sge = qed_chain_produce(&qp->sq.pbl);
+
+               DMA_REGPAIR_LE(sge->addr, wr->sg_list[i].addr);
+               sge->l_key = cpu_to_le32(wr->sg_list[i].lkey);
+               sge->length = cpu_to_le32(wr->sg_list[i].length);
+               data_size += wr->sg_list[i].length;
+       }
+
+       if (wqe_size)
+               *wqe_size += wr->num_sge;
+
+       return data_size;
+}
+
+static u32 qedr_prepare_sq_rdma_data(struct qedr_dev *dev,
+                                    struct qedr_qp *qp,
+                                    struct rdma_sq_rdma_wqe_1st *rwqe,
+                                    struct rdma_sq_rdma_wqe_2nd *rwqe2,
+                                    struct ib_send_wr *wr,
+                                    struct ib_send_wr **bad_wr)
+{
+       rwqe2->r_key = cpu_to_le32(rdma_wr(wr)->rkey);
+       DMA_REGPAIR_LE(rwqe2->remote_va, rdma_wr(wr)->remote_addr);
+
+       if (wr->send_flags & IB_SEND_INLINE) {
+               u8 flags = 0;
+
+               SET_FIELD2(flags, RDMA_SQ_RDMA_WQE_1ST_INLINE_FLG, 1);
+               return qedr_prepare_sq_inline_data(dev, qp, &rwqe->wqe_size, wr,
+                                                  bad_wr, &rwqe->flags, flags);
+       }
+
+       return qedr_prepare_sq_sges(qp, &rwqe->wqe_size, wr);
+}
+
+static u32 qedr_prepare_sq_send_data(struct qedr_dev *dev,
+                                    struct qedr_qp *qp,
+                                    struct rdma_sq_send_wqe_1st *swqe,
+                                    struct rdma_sq_send_wqe_2st *swqe2,
+                                    struct ib_send_wr *wr,
+                                    struct ib_send_wr **bad_wr)
+{
+       memset(swqe2, 0, sizeof(*swqe2));
+       if (wr->send_flags & IB_SEND_INLINE) {
+               u8 flags = 0;
+
+               SET_FIELD2(flags, RDMA_SQ_SEND_WQE_INLINE_FLG, 1);
+               return qedr_prepare_sq_inline_data(dev, qp, &swqe->wqe_size, wr,
+                                                  bad_wr, &swqe->flags, flags);
+       }
+
+       return qedr_prepare_sq_sges(qp, &swqe->wqe_size, wr);
+}
+
+static int qedr_prepare_reg(struct qedr_qp *qp,
+                           struct rdma_sq_fmr_wqe_1st *fwqe1,
+                           struct ib_reg_wr *wr)
+{
+       struct qedr_mr *mr = get_qedr_mr(wr->mr);
+       struct rdma_sq_fmr_wqe_2nd *fwqe2;
+
+       fwqe2 = (struct rdma_sq_fmr_wqe_2nd *)qed_chain_produce(&qp->sq.pbl);
+       fwqe1->addr.hi = upper_32_bits(mr->ibmr.iova);
+       fwqe1->addr.lo = lower_32_bits(mr->ibmr.iova);
+       fwqe1->l_key = wr->key;
+
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_REMOTE_READ,
+                  !!(wr->access & IB_ACCESS_REMOTE_READ));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_REMOTE_WRITE,
+                  !!(wr->access & IB_ACCESS_REMOTE_WRITE));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_ENABLE_ATOMIC,
+                  !!(wr->access & IB_ACCESS_REMOTE_ATOMIC));
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_LOCAL_READ, 1);
+       SET_FIELD2(fwqe2->access_ctrl, RDMA_SQ_FMR_WQE_2ND_LOCAL_WRITE,
+                  !!(wr->access & IB_ACCESS_LOCAL_WRITE));
+       fwqe2->fmr_ctrl = 0;
+
+       SET_FIELD2(fwqe2->fmr_ctrl, RDMA_SQ_FMR_WQE_2ND_PAGE_SIZE_LOG,
+                  ilog2(mr->ibmr.page_size) - 12);
+
+       fwqe2->length_hi = 0;
+       fwqe2->length_lo = mr->ibmr.length;
+       fwqe2->pbl_addr.hi = upper_32_bits(mr->info.pbl_table->pa);
+       fwqe2->pbl_addr.lo = lower_32_bits(mr->info.pbl_table->pa);
+
+       qp->wqe_wr_id[qp->sq.prod].mr = mr;
+
+       return 0;
+}
+
+enum ib_wc_opcode qedr_ib_to_wc_opcode(enum ib_wr_opcode opcode)
+{
+       switch (opcode) {
+       case IB_WR_RDMA_WRITE:
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               return IB_WC_RDMA_WRITE;
+       case IB_WR_SEND_WITH_IMM:
+       case IB_WR_SEND:
+       case IB_WR_SEND_WITH_INV:
+               return IB_WC_SEND;
+       case IB_WR_RDMA_READ:
+               return IB_WC_RDMA_READ;
+       case IB_WR_ATOMIC_CMP_AND_SWP:
+               return IB_WC_COMP_SWAP;
+       case IB_WR_ATOMIC_FETCH_AND_ADD:
+               return IB_WC_FETCH_ADD;
+       case IB_WR_REG_MR:
+               return IB_WC_REG_MR;
+       case IB_WR_LOCAL_INV:
+               return IB_WC_LOCAL_INV;
+       default:
+               return IB_WC_SEND;
+       }
+}
+
+inline bool qedr_can_post_send(struct qedr_qp *qp, struct ib_send_wr *wr)
+{
+       int wq_is_full, err_wr, pbl_is_full;
+       struct qedr_dev *dev = qp->dev;
+
+       /* prevent SQ overflow and/or processing of a bad WR */
+       err_wr = wr->num_sge > qp->sq.max_sges;
+       wq_is_full = qedr_wq_is_full(&qp->sq);
+       pbl_is_full = qed_chain_get_elem_left_u32(&qp->sq.pbl) <
+                     QEDR_MAX_SQE_ELEMENTS_PER_SQE;
+       if (wq_is_full || err_wr || pbl_is_full) {
+               if (wq_is_full && !(qp->err_bitmap & QEDR_QP_ERR_SQ_FULL)) {
+                       DP_ERR(dev,
+                              "error: WQ is full. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_SQ_FULL;
+               }
+
+               if (err_wr && !(qp->err_bitmap & QEDR_QP_ERR_BAD_SR)) {
+                       DP_ERR(dev,
+                              "error: WR is bad. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_BAD_SR;
+               }
+
+               if (pbl_is_full &&
+                   !(qp->err_bitmap & QEDR_QP_ERR_SQ_PBL_FULL)) {
+                       DP_ERR(dev,
+                              "error: WQ PBL is full. Post send on QP %p failed (this error appears only once)\n",
+                              qp);
+                       qp->err_bitmap |= QEDR_QP_ERR_SQ_PBL_FULL;
+               }
+               return false;
+       }
+       return true;
+}
+
+int __qedr_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                    struct ib_send_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct rdma_sq_atomic_wqe_1st *awqe1;
+       struct rdma_sq_atomic_wqe_2nd *awqe2;
+       struct rdma_sq_atomic_wqe_3rd *awqe3;
+       struct rdma_sq_send_wqe_2st *swqe2;
+       struct rdma_sq_local_inv_wqe *iwqe;
+       struct rdma_sq_rdma_wqe_2nd *rwqe2;
+       struct rdma_sq_send_wqe_1st *swqe;
+       struct rdma_sq_rdma_wqe_1st *rwqe;
+       struct rdma_sq_fmr_wqe_1st *fwqe1;
+       struct rdma_sq_common_wqe *wqe;
+       u32 length;
+       int rc = 0;
+       bool comp;
+
+       if (!qedr_can_post_send(qp, wr)) {
+               *bad_wr = wr;
+               return -ENOMEM;
+       }
+
+       wqe = qed_chain_produce(&qp->sq.pbl);
+       qp->wqe_wr_id[qp->sq.prod].signaled =
+               !!(wr->send_flags & IB_SEND_SIGNALED) || qp->signaled;
+
+       wqe->flags = 0;
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_SE_FLG,
+                  !!(wr->send_flags & IB_SEND_SOLICITED));
+       comp = (!!(wr->send_flags & IB_SEND_SIGNALED)) || qp->signaled;
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_COMP_FLG, comp);
+       SET_FIELD2(wqe->flags, RDMA_SQ_SEND_WQE_RD_FENCE_FLG,
+                  !!(wr->send_flags & IB_SEND_FENCE));
+       wqe->prev_wqe_size = qp->prev_wqe_size;
+
+       qp->wqe_wr_id[qp->sq.prod].opcode = qedr_ib_to_wc_opcode(wr->opcode);
+
+       switch (wr->opcode) {
+       case IB_WR_SEND_WITH_IMM:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND_WITH_IMM;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+               swqe->wqe_size = 2;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+
+               swqe->inv_key_or_imm_data = cpu_to_le32(wr->ex.imm_data);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+       case IB_WR_SEND:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+
+               swqe->wqe_size = 2;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+       case IB_WR_SEND_WITH_INV:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_SEND_WITH_INVALIDATE;
+               swqe = (struct rdma_sq_send_wqe_1st *)wqe;
+               swqe2 = qed_chain_produce(&qp->sq.pbl);
+               swqe->wqe_size = 2;
+               swqe->inv_key_or_imm_data = cpu_to_le32(wr->ex.invalidate_rkey);
+               length = qedr_prepare_sq_send_data(dev, qp, swqe, swqe2,
+                                                  wr, bad_wr);
+               swqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = swqe->wqe_size;
+               qp->prev_wqe_size = swqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = swqe->length;
+               break;
+
+       case IB_WR_RDMA_WRITE_WITH_IMM:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_WR_WITH_IMM;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe->imm_data = htonl(cpu_to_le32(wr->ex.imm_data));
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+       case IB_WR_RDMA_WRITE:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_WR;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+       case IB_WR_RDMA_READ_WITH_INV:
+               DP_ERR(dev,
+                      "RDMA READ WITH INVALIDATE not supported\n");
+               *bad_wr = wr;
+               rc = -EINVAL;
+               break;
+
+       case IB_WR_RDMA_READ:
+               wqe->req_type = RDMA_SQ_REQ_TYPE_RDMA_RD;
+               rwqe = (struct rdma_sq_rdma_wqe_1st *)wqe;
+
+               rwqe->wqe_size = 2;
+               rwqe2 = qed_chain_produce(&qp->sq.pbl);
+               length = qedr_prepare_sq_rdma_data(dev, qp, rwqe, rwqe2,
+                                                  wr, bad_wr);
+               rwqe->length = cpu_to_le32(length);
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = rwqe->wqe_size;
+               qp->prev_wqe_size = rwqe->wqe_size;
+               qp->wqe_wr_id[qp->sq.prod].bytes_len = rwqe->length;
+               break;
+
+       case IB_WR_ATOMIC_CMP_AND_SWP:
+       case IB_WR_ATOMIC_FETCH_AND_ADD:
+               awqe1 = (struct rdma_sq_atomic_wqe_1st *)wqe;
+               awqe1->wqe_size = 4;
+
+               awqe2 = qed_chain_produce(&qp->sq.pbl);
+               DMA_REGPAIR_LE(awqe2->remote_va, atomic_wr(wr)->remote_addr);
+               awqe2->r_key = cpu_to_le32(atomic_wr(wr)->rkey);
+
+               awqe3 = qed_chain_produce(&qp->sq.pbl);
+
+               if (wr->opcode == IB_WR_ATOMIC_FETCH_AND_ADD) {
+                       wqe->req_type = RDMA_SQ_REQ_TYPE_ATOMIC_ADD;
+                       DMA_REGPAIR_LE(awqe3->swap_data,
+                                      atomic_wr(wr)->compare_add);
+               } else {
+                       wqe->req_type = RDMA_SQ_REQ_TYPE_ATOMIC_CMP_AND_SWAP;
+                       DMA_REGPAIR_LE(awqe3->swap_data,
+                                      atomic_wr(wr)->swap);
+                       DMA_REGPAIR_LE(awqe3->cmp_data,
+                                      atomic_wr(wr)->compare_add);
+               }
+
+               qedr_prepare_sq_sges(qp, NULL, wr);
+
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = awqe1->wqe_size;
+               qp->prev_wqe_size = awqe1->wqe_size;
+               break;
+
+       case IB_WR_LOCAL_INV:
+               iwqe = (struct rdma_sq_local_inv_wqe *)wqe;
+               iwqe->wqe_size = 1;
+
+               iwqe->req_type = RDMA_SQ_REQ_TYPE_LOCAL_INVALIDATE;
+               iwqe->inv_l_key = wr->ex.invalidate_rkey;
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = iwqe->wqe_size;
+               qp->prev_wqe_size = iwqe->wqe_size;
+               break;
+       case IB_WR_REG_MR:
+               DP_DEBUG(dev, QEDR_MSG_CQ, "REG_MR\n");
+               wqe->req_type = RDMA_SQ_REQ_TYPE_FAST_MR;
+               fwqe1 = (struct rdma_sq_fmr_wqe_1st *)wqe;
+               fwqe1->wqe_size = 2;
+
+               rc = qedr_prepare_reg(qp, fwqe1, reg_wr(wr));
+               if (rc) {
+                       DP_ERR(dev, "IB_REG_MR failed rc=%d\n", rc);
+                       *bad_wr = wr;
+                       break;
+               }
+
+               qp->wqe_wr_id[qp->sq.prod].wqe_size = fwqe1->wqe_size;
+               qp->prev_wqe_size = fwqe1->wqe_size;
+               break;
+       default:
+               DP_ERR(dev, "invalid opcode 0x%x!\n", wr->opcode);
+               rc = -EINVAL;
+               *bad_wr = wr;
+               break;
+       }
+
+       if (*bad_wr) {
+               u16 value;
+
+               /* Restore prod to its position before
+                * this WR was processed
+                */
+               value = le16_to_cpu(qp->sq.db_data.data.value);
+               qed_chain_set_prod(&qp->sq.pbl, value, wqe);
+
+               /* Restore prev_wqe_size */
+               qp->prev_wqe_size = wqe->prev_wqe_size;
+               rc = -EINVAL;
+               DP_ERR(dev, "POST SEND FAILED\n");
+       }
+
+       return rc;
+}
+
+int qedr_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr,
+                  struct ib_send_wr **bad_wr)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibqp->device);
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       unsigned long flags;
+       int rc = 0;
+
+       *bad_wr = NULL;
+
+       if (qp->qp_type == IB_QPT_GSI)
+               return qedr_gsi_post_send(ibqp, wr, bad_wr);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       if ((qp->state == QED_ROCE_QP_STATE_RESET) ||
+           (qp->state == QED_ROCE_QP_STATE_ERR)) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               *bad_wr = wr;
+               DP_DEBUG(dev, QEDR_MSG_CQ,
+                        "QP in wrong state! QP icid=0x%x state %d\n",
+                        qp->icid, qp->state);
+               return -EINVAL;
+       }
+
+       if (!wr) {
+               DP_ERR(dev, "Got an empty post send.\n");
+               return -EINVAL;
+       }
+
+       while (wr) {
+               rc = __qedr_post_send(ibqp, wr, bad_wr);
+               if (rc)
+                       break;
+
+               qp->wqe_wr_id[qp->sq.prod].wr_id = wr->wr_id;
+
+               qedr_inc_sw_prod(&qp->sq);
+
+               qp->sq.db_data.data.value++;
+
+               wr = wr->next;
+       }
+
+       /* Trigger doorbell
+        * If there was a failure in the first WR then it will be triggered in
+        * vane. However this is not harmful (as long as the producer value is
+        * unchanged). For performance reasons we avoid checking for this
+        * redundant doorbell.
+        */
+       wmb();
+       writel(qp->sq.db_data.raw, qp->sq.db);
+
+       /* Make sure write sticks */
+       mmiowb();
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return rc;
+}
+
+int qedr_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr,
+                  struct ib_recv_wr **bad_wr)
+{
+       struct qedr_qp *qp = get_qedr_qp(ibqp);
+       struct qedr_dev *dev = qp->dev;
+       unsigned long flags;
+       int status = 0;
+
+       if (qp->qp_type == IB_QPT_GSI)
+               return qedr_gsi_post_recv(ibqp, wr, bad_wr);
+
+       spin_lock_irqsave(&qp->q_lock, flags);
+
+       if ((qp->state == QED_ROCE_QP_STATE_RESET) ||
+           (qp->state == QED_ROCE_QP_STATE_ERR)) {
+               spin_unlock_irqrestore(&qp->q_lock, flags);
+               *bad_wr = wr;
+               return -EINVAL;
+       }
+
+       while (wr) {
+               int i;
+
+               if (qed_chain_get_elem_left_u32(&qp->rq.pbl) <
+                   QEDR_MAX_RQE_ELEMENTS_PER_RQE ||
+                   wr->num_sge > qp->rq.max_sges) {
+                       DP_ERR(dev, "Can't post WR  (%d < %d) || (%d > %d)\n",
+                              qed_chain_get_elem_left_u32(&qp->rq.pbl),
+                              QEDR_MAX_RQE_ELEMENTS_PER_RQE, wr->num_sge,
+                              qp->rq.max_sges);
+                       status = -ENOMEM;
+                       *bad_wr = wr;
+                       break;
+               }
+               for (i = 0; i < wr->num_sge; i++) {
+                       u32 flags = 0;
+                       struct rdma_rq_sge *rqe =
+                           qed_chain_produce(&qp->rq.pbl);
+
+                       /* First one must include the number
+                        * of SGE in the list
+                        */
+                       if (!i)
+                               SET_FIELD(flags, RDMA_RQ_SGE_NUM_SGES,
+                                         wr->num_sge);
+
+                       SET_FIELD(flags, RDMA_RQ_SGE_L_KEY,
+                                 wr->sg_list[i].lkey);
+
+                       RQ_SGE_SET(rqe, wr->sg_list[i].addr,
+                                  wr->sg_list[i].length, flags);
+               }
+
+               /* Special case of no sges. FW requires between 1-4 sges...
+                * in this case we need to post 1 sge with length zero. this is
+                * because rdma write with immediate consumes an RQ.
+                */
+               if (!wr->num_sge) {
+                       u32 flags = 0;
+                       struct rdma_rq_sge *rqe =
+                           qed_chain_produce(&qp->rq.pbl);
+
+                       /* First one must include the number
+                        * of SGE in the list
+                        */
+                       SET_FIELD(flags, RDMA_RQ_SGE_L_KEY, 0);
+                       SET_FIELD(flags, RDMA_RQ_SGE_NUM_SGES, 1);
+
+                       RQ_SGE_SET(rqe, 0, 0, flags);
+                       i = 1;
+               }
+
+               qp->rqe_wr_id[qp->rq.prod].wr_id = wr->wr_id;
+               qp->rqe_wr_id[qp->rq.prod].wqe_size = i;
+
+               qedr_inc_sw_prod(&qp->rq);
+
+               /* Flush all the writes before signalling doorbell */
+               wmb();
+
+               qp->rq.db_data.data.value++;
+
+               writel(qp->rq.db_data.raw, qp->rq.db);
+
+               /* Make sure write sticks */
+               mmiowb();
+
+               wr = wr->next;
+       }
+
+       spin_unlock_irqrestore(&qp->q_lock, flags);
+
+       return status;
+}
+
+static int is_valid_cqe(struct qedr_cq *cq, union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+
+       return (resp_cqe->flags & RDMA_CQE_REQUESTER_TOGGLE_BIT_MASK) ==
+               cq->pbl_toggle;
+}
+
+static struct qedr_qp *cqe_get_qp(union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+       struct qedr_qp *qp;
+
+       qp = (struct qedr_qp *)(uintptr_t)HILO_GEN(resp_cqe->qp_handle.hi,
+                                                  resp_cqe->qp_handle.lo,
+                                                  u64);
+       return qp;
+}
+
+static enum rdma_cqe_type cqe_get_type(union rdma_cqe *cqe)
+{
+       struct rdma_cqe_requester *resp_cqe = &cqe->req;
+
+       return GET_FIELD(resp_cqe->flags, RDMA_CQE_REQUESTER_TYPE);
+}
+
+/* Return latest CQE (needs processing) */
+static union rdma_cqe *get_cqe(struct qedr_cq *cq)
+{
+       return cq->latest_cqe;
+}
+
+/* In fmr we need to increase the number of fmr completed counter for the fmr
+ * algorithm determining whether we can free a pbl or not.
+ * we need to perform this whether the work request was signaled or not. for
+ * this purpose we call this function from the condition that checks if a wr
+ * should be skipped, to make sure we don't miss it ( possibly this fmr
+ * operation was not signalted)
+ */
+static inline void qedr_chk_if_fmr(struct qedr_qp *qp)
+{
+       if (qp->wqe_wr_id[qp->sq.cons].opcode == IB_WC_REG_MR)
+               qp->wqe_wr_id[qp->sq.cons].mr->info.completed++;
+}
+
+static int process_req(struct qedr_dev *dev, struct qedr_qp *qp,
+                      struct qedr_cq *cq, int num_entries,
+                      struct ib_wc *wc, u16 hw_cons, enum ib_wc_status status,
+                      int force)
+{
+       u16 cnt = 0;
+
+       while (num_entries && qp->sq.wqe_cons != hw_cons) {
+               if (!qp->wqe_wr_id[qp->sq.cons].signaled && !force) {
+                       qedr_chk_if_fmr(qp);
+                       /* skip WC */
+                       goto next_cqe;
+               }
+
+               /* fill WC */
+               wc->status = status;
+               wc->wc_flags = 0;
+               wc->src_qp = qp->id;
+               wc->qp = &qp->ibqp;
+
+               wc->wr_id = qp->wqe_wr_id[qp->sq.cons].wr_id;
+               wc->opcode = qp->wqe_wr_id[qp->sq.cons].opcode;
+
+               switch (wc->opcode) {
+               case IB_WC_RDMA_WRITE:
+                       wc->byte_len = qp->wqe_wr_id[qp->sq.cons].bytes_len;
+                       break;
+               case IB_WC_COMP_SWAP:
+               case IB_WC_FETCH_ADD:
+                       wc->byte_len = 8;
+                       break;
+               case IB_WC_REG_MR:
+                       qp->wqe_wr_id[qp->sq.cons].mr->info.completed++;
+                       break;
+               default:
+                       break;
+               }
+
+               num_entries--;
+               wc++;
+               cnt++;
+next_cqe:
+               while (qp->wqe_wr_id[qp->sq.cons].wqe_size--)
+                       qed_chain_consume(&qp->sq.pbl);
+               qedr_inc_sw_cons(&qp->sq);
+       }
+
+       return cnt;
+}
+
+static int qedr_poll_cq_req(struct qedr_dev *dev,
+                           struct qedr_qp *qp, struct qedr_cq *cq,
+                           int num_entries, struct ib_wc *wc,
+                           struct rdma_cqe_requester *req)
+{
+       int cnt = 0;
+
+       switch (req->status) {
+       case RDMA_CQE_REQ_STS_OK:
+               cnt = process_req(dev, qp, cq, num_entries, wc, req->sq_cons,
+                                 IB_WC_SUCCESS, 0);
+               break;
+       case RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR:
+               DP_ERR(dev,
+                      "Error: POLL CQ with RDMA_CQE_REQ_STS_WORK_REQUEST_FLUSHED_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                      cq->icid, qp->icid);
+               cnt = process_req(dev, qp, cq, num_entries, wc, req->sq_cons,
+                                 IB_WC_WR_FLUSH_ERR, 0);
+               break;
+       default:
+               /* process all WQE before the cosumer */
+               qp->state = QED_ROCE_QP_STATE_ERR;
+               cnt = process_req(dev, qp, cq, num_entries, wc,
+                                 req->sq_cons - 1, IB_WC_SUCCESS, 0);
+               wc += cnt;
+               /* if we have extra WC fill it with actual error info */
+               if (cnt < num_entries) {
+                       enum ib_wc_status wc_status;
+
+                       switch (req->status) {
+                       case RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_BAD_RESPONSE_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_BAD_RESP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_LENGTH_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_LEN_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_QP_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_QP_OP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_LOCAL_PROTECTION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_LOC_PROT_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_MEMORY_MGT_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_MW_BIND_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_INVALID_REQUEST_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_INV_REQ_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_ACCESS_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_ACCESS_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_REMOTE_OPERATION_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_REM_OP_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with RDMA_CQE_REQ_STS_RNR_NAK_RETRY_CNT_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_RNR_RETRY_EXC_ERR;
+                               break;
+                       case RDMA_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with ROCE_CQE_REQ_STS_TRANSPORT_RETRY_CNT_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_RETRY_EXC_ERR;
+                               break;
+                       default:
+                               DP_ERR(dev,
+                                      "Error: POLL CQ with IB_WC_GENERAL_ERR. CQ icid=0x%x, QP icid=0x%x\n",
+                                      cq->icid, qp->icid);
+                               wc_status = IB_WC_GENERAL_ERR;
+                       }
+                       cnt += process_req(dev, qp, cq, 1, wc, req->sq_cons,
+                                          wc_status, 1);
+               }
+       }
+
+       return cnt;
+}
+
+static void __process_resp_one(struct qedr_dev *dev, struct qedr_qp *qp,
+                              struct qedr_cq *cq, struct ib_wc *wc,
+                              struct rdma_cqe_responder *resp, u64 wr_id)
+{
+       enum ib_wc_status wc_status = IB_WC_SUCCESS;
+       u8 flags;
+
+       wc->opcode = IB_WC_RECV;
+       wc->wc_flags = 0;
+
+       switch (resp->status) {
+       case RDMA_CQE_RESP_STS_LOCAL_ACCESS_ERR:
+               wc_status = IB_WC_LOC_ACCESS_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_LENGTH_ERR:
+               wc_status = IB_WC_LOC_LEN_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_QP_OPERATION_ERR:
+               wc_status = IB_WC_LOC_QP_OP_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_LOCAL_PROTECTION_ERR:
+               wc_status = IB_WC_LOC_PROT_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_MEMORY_MGT_OPERATION_ERR:
+               wc_status = IB_WC_MW_BIND_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_REMOTE_INVALID_REQUEST_ERR:
+               wc_status = IB_WC_REM_INV_RD_REQ_ERR;
+               break;
+       case RDMA_CQE_RESP_STS_OK:
+               wc_status = IB_WC_SUCCESS;
+               wc->byte_len = le32_to_cpu(resp->length);
+
+               flags = resp->flags & QEDR_RESP_RDMA_IMM;
+
+               if (flags == QEDR_RESP_RDMA_IMM)
+                       wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+
+               if (flags == QEDR_RESP_RDMA_IMM || flags == QEDR_RESP_IMM) {
+                       wc->ex.imm_data =
+                               le32_to_cpu(resp->imm_data_or_inv_r_Key);
+                       wc->wc_flags |= IB_WC_WITH_IMM;
+               }
+               break;
+       default:
+               wc->status = IB_WC_GENERAL_ERR;
+               DP_ERR(dev, "Invalid CQE status detected\n");
+       }
+
+       /* fill WC */
+       wc->status = wc_status;
+       wc->src_qp = qp->id;
+       wc->qp = &qp->ibqp;
+       wc->wr_id = wr_id;
+}
+
+static int process_resp_one(struct qedr_dev *dev, struct qedr_qp *qp,
+                           struct qedr_cq *cq, struct ib_wc *wc,
+                           struct rdma_cqe_responder *resp)
+{
+       u64 wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+
+       __process_resp_one(dev, qp, cq, wc, resp, wr_id);
+
+       while (qp->rqe_wr_id[qp->rq.cons].wqe_size--)
+               qed_chain_consume(&qp->rq.pbl);
+       qedr_inc_sw_cons(&qp->rq);
+
+       return 1;
+}
+
+static int process_resp_flush(struct qedr_qp *qp, struct qedr_cq *cq,
+                             int num_entries, struct ib_wc *wc, u16 hw_cons)
+{
+       u16 cnt = 0;
+
+       while (num_entries && qp->rq.wqe_cons != hw_cons) {
+               /* fill WC */
+               wc->status = IB_WC_WR_FLUSH_ERR;
+               wc->wc_flags = 0;
+               wc->src_qp = qp->id;
+               wc->byte_len = 0;
+               wc->wr_id = qp->rqe_wr_id[qp->rq.cons].wr_id;
+               wc->qp = &qp->ibqp;
+               num_entries--;
+               wc++;
+               cnt++;
+               while (qp->rqe_wr_id[qp->rq.cons].wqe_size--)
+                       qed_chain_consume(&qp->rq.pbl);
+               qedr_inc_sw_cons(&qp->rq);
+       }
+
+       return cnt;
+}
+
+static void try_consume_resp_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
+                                struct rdma_cqe_responder *resp, int *update)
+{
+       if (le16_to_cpu(resp->rq_cons) == qp->rq.wqe_cons) {
+               consume_cqe(cq);
+               *update |= 1;
+       }
+}
+
+static int qedr_poll_cq_resp(struct qedr_dev *dev, struct qedr_qp *qp,
+                            struct qedr_cq *cq, int num_entries,
+                            struct ib_wc *wc, struct rdma_cqe_responder *resp,
+                            int *update)
+{
+       int cnt;
+
+       if (resp->status == RDMA_CQE_RESP_STS_WORK_REQUEST_FLUSHED_ERR) {
+               cnt = process_resp_flush(qp, cq, num_entries, wc,
+                                        resp->rq_cons);
+               try_consume_resp_cqe(cq, qp, resp, update);
+       } else {
+               cnt = process_resp_one(dev, qp, cq, wc, resp);
+               consume_cqe(cq);
+               *update |= 1;
+       }
+
+       return cnt;
+}
+
+static void try_consume_req_cqe(struct qedr_cq *cq, struct qedr_qp *qp,
+                               struct rdma_cqe_requester *req, int *update)
+{
+       if (le16_to_cpu(req->sq_cons) == qp->sq.wqe_cons) {
+               consume_cqe(cq);
+               *update |= 1;
+       }
+}
+
+int qedr_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibcq->device);
+       struct qedr_cq *cq = get_qedr_cq(ibcq);
+       union rdma_cqe *cqe = cq->latest_cqe;
+       u32 old_cons, new_cons;
+       unsigned long flags;
+       int update = 0;
+       int done = 0;
+
+       if (cq->cq_type == QEDR_CQ_TYPE_GSI)
+               return qedr_gsi_poll_cq(ibcq, num_entries, wc);
+
+       spin_lock_irqsave(&cq->cq_lock, flags);
+       old_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+       while (num_entries && is_valid_cqe(cq, cqe)) {
+               struct qedr_qp *qp;
+               int cnt = 0;
+
+               /* prevent speculative reads of any field of CQE */
+               rmb();
+
+               qp = cqe_get_qp(cqe);
+               if (!qp) {
+                       WARN(1, "Error: CQE QP pointer is NULL. CQE=%p\n", cqe);
+                       break;
+               }
+
+               wc->qp = &qp->ibqp;
+
+               switch (cqe_get_type(cqe)) {
+               case RDMA_CQE_TYPE_REQUESTER:
+                       cnt = qedr_poll_cq_req(dev, qp, cq, num_entries, wc,
+                                              &cqe->req);
+                       try_consume_req_cqe(cq, qp, &cqe->req, &update);
+                       break;
+               case RDMA_CQE_TYPE_RESPONDER_RQ:
+                       cnt = qedr_poll_cq_resp(dev, qp, cq, num_entries, wc,
+                                               &cqe->resp, &update);
+                       break;
+               case RDMA_CQE_TYPE_INVALID:
+               default:
+                       DP_ERR(dev, "Error: invalid CQE type = %d\n",
+                              cqe_get_type(cqe));
+               }
+               num_entries -= cnt;
+               wc += cnt;
+               done += cnt;
+
+               cqe = get_cqe(cq);
+       }
+       new_cons = qed_chain_get_cons_idx_u32(&cq->pbl);
+
+       cq->cq_cons += new_cons - old_cons;
+
+       if (update)
+               /* doorbell notifies abount latest VALID entry,
+                * but chain already point to the next INVALID one
+                */
+               doorbell_cq(cq, cq->cq_cons - 1, cq->arm_flags);
+
+       spin_unlock_irqrestore(&cq->cq_lock, flags);
+       return done;
+}
+
+int qedr_process_mad(struct ib_device *ibdev, int process_mad_flags,
+                    u8 port_num,
+                    const struct ib_wc *in_wc,
+                    const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *mad_hdr,
+                    size_t in_mad_size, struct ib_mad_hdr *out_mad,
+                    size_t *out_mad_size, u16 *out_mad_pkey_index)
+{
+       struct qedr_dev *dev = get_qedr_dev(ibdev);
+
+       DP_DEBUG(dev, QEDR_MSG_GSI,
+                "QEDR_PROCESS_MAD in_mad %x %x %x %x %x %x %x %x\n",
+                mad_hdr->attr_id, mad_hdr->base_version, mad_hdr->attr_mod,
+                mad_hdr->class_specific, mad_hdr->class_version,
+                mad_hdr->method, mad_hdr->mgmt_class, mad_hdr->status);
+       return IB_MAD_RESULT_SUCCESS;
+}
+
+int qedr_port_immutable(struct ib_device *ibdev, u8 port_num,
+                       struct ib_port_immutable *immutable)
+{
+       struct ib_port_attr attr;
+       int err;
+
+       err = qedr_query_port(ibdev, port_num, &attr);
+       if (err)
+               return err;
+
+       immutable->pkey_tbl_len = attr.pkey_tbl_len;
+       immutable->gid_tbl_len = attr.gid_tbl_len;
+       immutable->core_cap_flags = RDMA_CORE_PORT_IBA_ROCE |
+                                   RDMA_CORE_PORT_IBA_ROCE_UDP_ENCAP;
+       immutable->max_mad_size = IB_MGMT_MAD_SIZE;
+
+       return 0;
+}
diff --git a/drivers/infiniband/hw/qedr/verbs.h b/drivers/infiniband/hw/qedr/verbs.h
new file mode 100644 (file)
index 0000000..a9b5e67
--- /dev/null
@@ -0,0 +1,101 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 __QEDR_VERBS_H__
+#define __QEDR_VERBS_H__
+
+int qedr_query_device(struct ib_device *ibdev,
+                     struct ib_device_attr *attr, struct ib_udata *udata);
+int qedr_query_port(struct ib_device *, u8 port, struct ib_port_attr *props);
+int qedr_modify_port(struct ib_device *, u8 port, int mask,
+                    struct ib_port_modify *props);
+
+int qedr_query_gid(struct ib_device *, u8 port, int index, union ib_gid *gid);
+
+int qedr_query_pkey(struct ib_device *, u8 port, u16 index, u16 *pkey);
+
+struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *, struct ib_udata *);
+int qedr_dealloc_ucontext(struct ib_ucontext *);
+
+int qedr_mmap(struct ib_ucontext *, struct vm_area_struct *vma);
+int qedr_del_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, void **context);
+int qedr_add_gid(struct ib_device *device, u8 port_num,
+                unsigned int index, const union ib_gid *gid,
+                const struct ib_gid_attr *attr, void **context);
+struct ib_pd *qedr_alloc_pd(struct ib_device *,
+                           struct ib_ucontext *, struct ib_udata *);
+int qedr_dealloc_pd(struct ib_pd *pd);
+
+struct ib_cq *qedr_create_cq(struct ib_device *ibdev,
+                            const struct ib_cq_init_attr *attr,
+                            struct ib_ucontext *ib_ctx,
+                            struct ib_udata *udata);
+int qedr_resize_cq(struct ib_cq *, int cqe, struct ib_udata *);
+int qedr_destroy_cq(struct ib_cq *);
+int qedr_arm_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags);
+struct ib_qp *qedr_create_qp(struct ib_pd *, struct ib_qp_init_attr *attrs,
+                            struct ib_udata *);
+int qedr_modify_qp(struct ib_qp *, struct ib_qp_attr *attr,
+                  int attr_mask, struct ib_udata *udata);
+int qedr_query_qp(struct ib_qp *, struct ib_qp_attr *qp_attr,
+                 int qp_attr_mask, struct ib_qp_init_attr *);
+int qedr_destroy_qp(struct ib_qp *ibqp);
+
+struct ib_ah *qedr_create_ah(struct ib_pd *ibpd, struct ib_ah_attr *attr);
+int qedr_destroy_ah(struct ib_ah *ibah);
+
+int qedr_dereg_mr(struct ib_mr *);
+struct ib_mr *qedr_get_dma_mr(struct ib_pd *, int acc);
+
+struct ib_mr *qedr_reg_user_mr(struct ib_pd *, u64 start, u64 length,
+                              u64 virt, int acc, struct ib_udata *);
+
+int qedr_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg,
+                  int sg_nents, unsigned int *sg_offset);
+
+struct ib_mr *qedr_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type,
+                           u32 max_num_sg);
+int qedr_poll_cq(struct ib_cq *, int num_entries, struct ib_wc *wc);
+int qedr_post_send(struct ib_qp *, struct ib_send_wr *,
+                  struct ib_send_wr **bad_wr);
+int qedr_post_recv(struct ib_qp *, struct ib_recv_wr *,
+                  struct ib_recv_wr **bad_wr);
+int qedr_process_mad(struct ib_device *ibdev, int process_mad_flags,
+                    u8 port_num, const struct ib_wc *in_wc,
+                    const struct ib_grh *in_grh,
+                    const struct ib_mad_hdr *in_mad,
+                    size_t in_mad_size, struct ib_mad_hdr *out_mad,
+                    size_t *out_mad_size, u16 *out_mad_pkey_index);
+
+int qedr_port_immutable(struct ib_device *ibdev, u8 port_num,
+                       struct ib_port_immutable *immutable);
+#endif
index f2f229e..6d9904a 100644 (file)
@@ -129,7 +129,7 @@ void rvt_cq_enter(struct rvt_cq *cq, struct ib_wc *entry, bool solicited)
                if (likely(worker)) {
                        cq->notify = RVT_CQ_NONE;
                        cq->triggered++;
-                       queue_kthread_work(worker, &cq->comptask);
+                       kthread_queue_work(worker, &cq->comptask);
                }
        }
 
@@ -265,7 +265,7 @@ struct ib_cq *rvt_create_cq(struct ib_device *ibdev,
        cq->ibcq.cqe = entries;
        cq->notify = RVT_CQ_NONE;
        spin_lock_init(&cq->lock);
-       init_kthread_work(&cq->comptask, send_complete);
+       kthread_init_work(&cq->comptask, send_complete);
        cq->queue = wc;
 
        ret = &cq->ibcq;
@@ -295,7 +295,7 @@ int rvt_destroy_cq(struct ib_cq *ibcq)
        struct rvt_cq *cq = ibcq_to_rvtcq(ibcq);
        struct rvt_dev_info *rdi = cq->rdi;
 
-       flush_kthread_work(&cq->comptask);
+       kthread_flush_work(&cq->comptask);
        spin_lock(&rdi->n_cqs_lock);
        rdi->n_cqs_allocated--;
        spin_unlock(&rdi->n_cqs_lock);
@@ -514,7 +514,7 @@ int rvt_driver_cq_init(struct rvt_dev_info *rdi)
        rdi->worker = kzalloc(sizeof(*rdi->worker), GFP_KERNEL);
        if (!rdi->worker)
                return -ENOMEM;
-       init_kthread_worker(rdi->worker);
+       kthread_init_worker(rdi->worker);
        task = kthread_create_on_node(
                kthread_worker_fn,
                rdi->worker,
@@ -547,7 +547,7 @@ void rvt_cq_exit(struct rvt_dev_info *rdi)
        /* blocks future queuing from send_complete() */
        rdi->worker = NULL;
        smp_wmb(); /* See rdi_cq_enter */
-       flush_kthread_worker(worker);
+       kthread_flush_worker(worker);
        kthread_stop(worker->task);
        kfree(worker);
 }
index 936f07a..6d7de9b 100644 (file)
@@ -103,6 +103,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
                                           6-byte ALPS packet */
 #define ALPS_STICK_BITS                0x100   /* separate stick button bits */
 #define ALPS_BUTTONPAD         0x200   /* device is a clickpad */
+#define ALPS_DUALPOINT_WITH_PRESSURE   0x400   /* device can report trackpoint pressure */
 
 static const struct alps_model_info alps_model_data[] = {
        { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },      /* Toshiba Salellite Pro M10 */
@@ -1156,15 +1157,28 @@ static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
 {
        unsigned char pkt_id = SS4_PACKET_ID_IDLE;
 
-       if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
-           (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) {
-               pkt_id = SS4_PACKET_ID_IDLE;
-       } else if (!(byte[3] & 0x10)) {
-               pkt_id = SS4_PACKET_ID_ONE;
-       } else if (!(byte[3] & 0x20)) {
+       switch (byte[3] & 0x30) {
+       case 0x00:
+               if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
+                   (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 &&
+                   byte[5] == 0x00) {
+                       pkt_id = SS4_PACKET_ID_IDLE;
+               } else {
+                       pkt_id = SS4_PACKET_ID_ONE;
+               }
+               break;
+       case 0x10:
+               /* two-finger finger positions */
                pkt_id = SS4_PACKET_ID_TWO;
-       } else {
+               break;
+       case 0x20:
+               /* stick pointer */
+               pkt_id = SS4_PACKET_ID_STICK;
+               break;
+       case 0x30:
+               /* third and fourth finger positions */
                pkt_id = SS4_PACKET_ID_MULTI;
+               break;
        }
 
        return pkt_id;
@@ -1185,7 +1199,13 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
                f->mt[0].x = SS4_1F_X_V2(p);
                f->mt[0].y = SS4_1F_Y_V2(p);
                f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
-               f->fingers = 1;
+               /*
+                * When a button is held the device will give us events
+                * with x, y, and pressure of 0. This causes annoying jumps
+                * if a touch is released while the button is held.
+                * Handle this by claiming zero contacts.
+                */
+               f->fingers = f->pressure > 0 ? 1 : 0;
                f->first_mp = 0;
                f->is_mp = 0;
                break;
@@ -1246,16 +1266,40 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
                }
                break;
 
+       case SS4_PACKET_ID_STICK:
+               if (!(priv->flags & ALPS_DUALPOINT)) {
+                       psmouse_warn(psmouse,
+                                    "Rejected trackstick packet from non DualPoint device");
+               } else {
+                       int x = (s8)(((p[0] & 1) << 7) | (p[1] & 0x7f));
+                       int y = (s8)(((p[3] & 1) << 7) | (p[2] & 0x7f));
+                       int pressure = (s8)(p[4] & 0x7f);
+
+                       input_report_rel(priv->dev2, REL_X, x);
+                       input_report_rel(priv->dev2, REL_Y, -y);
+                       input_report_abs(priv->dev2, ABS_PRESSURE, pressure);
+               }
+               break;
+
        case SS4_PACKET_ID_IDLE:
        default:
                memset(f, 0, sizeof(struct alps_fields));
                break;
        }
 
-       f->left = !!(SS4_BTN_V2(p) & 0x01);
-       if (!(priv->flags & ALPS_BUTTONPAD)) {
-               f->right = !!(SS4_BTN_V2(p) & 0x02);
-               f->middle = !!(SS4_BTN_V2(p) & 0x04);
+       /* handle buttons */
+       if (pkt_id == SS4_PACKET_ID_STICK) {
+               f->ts_left = !!(SS4_BTN_V2(p) & 0x01);
+               if (!(priv->flags & ALPS_BUTTONPAD)) {
+                       f->ts_right = !!(SS4_BTN_V2(p) & 0x02);
+                       f->ts_middle = !!(SS4_BTN_V2(p) & 0x04);
+               }
+       } else {
+               f->left = !!(SS4_BTN_V2(p) & 0x01);
+               if (!(priv->flags & ALPS_BUTTONPAD)) {
+                       f->right = !!(SS4_BTN_V2(p) & 0x02);
+                       f->middle = !!(SS4_BTN_V2(p) & 0x04);
+               }
        }
 
        return 0;
@@ -1266,6 +1310,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
        struct alps_data *priv = psmouse->private;
        unsigned char *packet = psmouse->packet;
        struct input_dev *dev = psmouse->dev;
+       struct input_dev *dev2 = priv->dev2;
        struct alps_fields *f = &priv->f;
 
        memset(f, 0, sizeof(struct alps_fields));
@@ -1311,6 +1356,13 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
 
        input_report_abs(dev, ABS_PRESSURE, f->pressure);
        input_sync(dev);
+
+       if (priv->flags & ALPS_DUALPOINT) {
+               input_report_key(dev2, BTN_LEFT, f->ts_left);
+               input_report_key(dev2, BTN_RIGHT, f->ts_right);
+               input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
+               input_sync(dev2);
+       }
 }
 
 static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
@@ -2695,6 +2747,10 @@ static int alps_set_protocol(struct psmouse *psmouse,
                if (alps_set_defaults_ss4_v2(psmouse, priv))
                        return -EIO;
 
+               if (priv->fw_ver[1] == 0x1)
+                       priv->flags |= ALPS_DUALPOINT |
+                                       ALPS_DUALPOINT_WITH_PRESSURE;
+
                break;
        }
 
@@ -2767,6 +2823,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
                } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
                           e7[2] == 0x14 && ec[1] == 0x02) {
                        protocol = &alps_v8_protocol_data;
+               } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
+                          e7[2] == 0x28 && ec[1] == 0x01) {
+                       protocol = &alps_v8_protocol_data;
                } else {
                        psmouse_dbg(psmouse,
                                    "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
@@ -2949,6 +3008,10 @@ int alps_init(struct psmouse *psmouse)
 
                input_set_capability(dev2, EV_REL, REL_X);
                input_set_capability(dev2, EV_REL, REL_Y);
+               if (priv->flags & ALPS_DUALPOINT_WITH_PRESSURE) {
+                       input_set_capability(dev2, EV_ABS, ABS_PRESSURE);
+                       input_set_abs_params(dev2, ABS_PRESSURE, 0, 127, 0, 0);
+               }
                input_set_capability(dev2, EV_KEY, BTN_LEFT);
                input_set_capability(dev2, EV_KEY, BTN_RIGHT);
                input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
index d37f814..b9417e2 100644 (file)
  *  or there's button activities.
  * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
  * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
+ * SS4_PACKET_ID_STICK: A stick pointer packet
 */
 enum SS4_PACKET_ID {
        SS4_PACKET_ID_IDLE = 0,
        SS4_PACKET_ID_ONE,
        SS4_PACKET_ID_TWO,
        SS4_PACKET_ID_MULTI,
+       SS4_PACKET_ID_STICK,
 };
 
 #define SS4_COUNT_PER_ELECTRODE                256
index 08e252a..db7d1d6 100644 (file)
@@ -1134,7 +1134,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
  * System76 Pangolin       0x250f01        ?               2 hw buttons
  * (*) + 3 trackpoint buttons
  * (**) + 0 trackpoint buttons
- * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
+ * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
  */
 static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
 {
@@ -1159,6 +1159,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
                },
        },
+       {
+               /* Fujitsu H760 also has a middle button */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
+               },
+       },
 #endif
        { }
 };
@@ -1503,10 +1510,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
                },
        },
        {
-               /* Fujitsu LIFEBOOK E554  does not work with crc_enabled == 0 */
+               /* Fujitsu H760 does not work with crc_enabled == 0 */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
-                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
                },
        },
        {
@@ -1516,6 +1523,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"),
                },
        },
+       {
+               /* Fujitsu LIFEBOOK E554  does not work with crc_enabled == 0 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
+               },
+       },
+       {
+               /* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
+               },
+       },
        {
                /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
                .matches = {
index 09c769c..ef8c747 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <linux/kconfig.h>
 #include <linux/list.h>
 #include <linux/pm.h>
 #include <linux/rmi.h>
index c83bce8..4a88312 100644 (file)
@@ -17,7 +17,6 @@
 #include <linux/bitmap.h>
 #include <linux/delay.h>
 #include <linux/fs.h>
-#include <linux/kconfig.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
 #include <linux/of.h>
index fac81fc..b5d2dfc 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/kconfig.h>
 #include <linux/rmi.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
index 20c7134..f798f42 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/device.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
-#include <linux/kconfig.h>
 #include <linux/rmi.h>
 #include <linux/slab.h>
 #include <linux/of.h>
index 6f2e0e4..1ebc2c1 100644 (file)
@@ -221,6 +221,21 @@ static const struct of_device_id rmi_i2c_of_match[] = {
 MODULE_DEVICE_TABLE(of, rmi_i2c_of_match);
 #endif
 
+static void rmi_i2c_regulator_bulk_disable(void *data)
+{
+       struct rmi_i2c_xport *rmi_i2c = data;
+
+       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
+                              rmi_i2c->supplies);
+}
+
+static void rmi_i2c_unregister_transport(void *data)
+{
+       struct rmi_i2c_xport *rmi_i2c = data;
+
+       rmi_unregister_transport_device(&rmi_i2c->xport);
+}
+
 static int rmi_i2c_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
@@ -264,6 +279,12 @@ static int rmi_i2c_probe(struct i2c_client *client,
        if (retval < 0)
                return retval;
 
+       retval = devm_add_action_or_reset(&client->dev,
+                                         rmi_i2c_regulator_bulk_disable,
+                                         rmi_i2c);
+       if (retval)
+               return retval;
+
        of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
                             &rmi_i2c->startup_delay);
 
@@ -294,6 +315,11 @@ static int rmi_i2c_probe(struct i2c_client *client,
                        client->addr);
                return retval;
        }
+       retval = devm_add_action_or_reset(&client->dev,
+                                         rmi_i2c_unregister_transport,
+                                         rmi_i2c);
+       if (retval)
+               return retval;
 
        retval = rmi_i2c_init_irq(client);
        if (retval < 0)
@@ -304,17 +330,6 @@ static int rmi_i2c_probe(struct i2c_client *client,
        return 0;
 }
 
-static int rmi_i2c_remove(struct i2c_client *client)
-{
-       struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
-
-       rmi_unregister_transport_device(&rmi_i2c->xport);
-       regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
-                              rmi_i2c->supplies);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int rmi_i2c_suspend(struct device *dev)
 {
@@ -431,7 +446,6 @@ static struct i2c_driver rmi_i2c_driver = {
        },
        .id_table       = rmi_id,
        .probe          = rmi_i2c_probe,
-       .remove         = rmi_i2c_remove,
 };
 
 module_i2c_driver(rmi_i2c_driver);
index 55bd1b3..4ebef60 100644 (file)
@@ -396,6 +396,13 @@ static inline int rmi_spi_of_probe(struct spi_device *spi,
 }
 #endif
 
+static void rmi_spi_unregister_transport(void *data)
+{
+       struct rmi_spi_xport *rmi_spi = data;
+
+       rmi_unregister_transport_device(&rmi_spi->xport);
+}
+
 static int rmi_spi_probe(struct spi_device *spi)
 {
        struct rmi_spi_xport *rmi_spi;
@@ -464,6 +471,11 @@ static int rmi_spi_probe(struct spi_device *spi)
                dev_err(&spi->dev, "failed to register transport.\n");
                return retval;
        }
+       retval = devm_add_action_or_reset(&spi->dev,
+                                         rmi_spi_unregister_transport,
+                                         rmi_spi);
+       if (retval)
+               return retval;
 
        retval = rmi_spi_init_irq(spi);
        if (retval < 0)
@@ -473,15 +485,6 @@ static int rmi_spi_probe(struct spi_device *spi)
        return 0;
 }
 
-static int rmi_spi_remove(struct spi_device *spi)
-{
-       struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
-
-       rmi_unregister_transport_device(&rmi_spi->xport);
-
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int rmi_spi_suspend(struct device *dev)
 {
@@ -577,7 +580,6 @@ static struct spi_driver rmi_spi_driver = {
        },
        .id_table       = rmi_id,
        .probe          = rmi_spi_probe,
-       .remove         = rmi_spi_remove,
 };
 
 module_spi_driver(rmi_spi_driver);
index a5eed2a..34da81c 100644 (file)
@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void)
                return -EBUSY;
 #endif
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index ee1ad27..08a1c10 100644 (file)
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
                return -EBUSY;
 #endif
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
 
        return 0;
 }
index f708c75..1aabea4 100644 (file)
@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val)
 
 static inline int i8042_platform_init(void)
 {
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index afcd1c1..6231d63 100644 (file)
@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void)
                }
        }
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
 
        return 0;
 }
index 73f5cc1..4557475 100644 (file)
@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
        if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
                return -EBUSY;
 
-       i8042_reset = 1;
+       i8042_reset = I8042_RESET_ALWAYS;
        return 0;
 }
 
index 68f5f4a..f4bfb4b 100644 (file)
@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
        { }
 };
 
+/*
+ * On some Asus laptops, just running self tests cause problems.
+ */
+static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
+               },
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
+               },
+       },
+       { }
+};
 static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
        {
                /* MSI Wind U-100 */
@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void)
                return retval;
 
 #if defined(__ia64__)
-        i8042_reset = true;
+        i8042_reset = I8042_RESET_ALWAYS;
 #endif
 
 #ifdef CONFIG_X86
-       if (dmi_check_system(i8042_dmi_reset_table))
-               i8042_reset = true;
+       /* Honor module parameter when value is not default */
+       if (i8042_reset == I8042_RESET_DEFAULT) {
+               if (dmi_check_system(i8042_dmi_reset_table))
+                       i8042_reset = I8042_RESET_ALWAYS;
+
+               if (dmi_check_system(i8042_dmi_noselftest_table))
+                       i8042_reset = I8042_RESET_NEVER;
+       }
 
        if (dmi_check_system(i8042_dmi_noloop_table))
                i8042_noloop = true;
index 405252a..89abfdb 100644 (file)
@@ -48,9 +48,39 @@ static bool i8042_unlock;
 module_param_named(unlock, i8042_unlock, bool, 0);
 MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
 
-static bool i8042_reset;
-module_param_named(reset, i8042_reset, bool, 0);
-MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
+enum i8042_controller_reset_mode {
+       I8042_RESET_NEVER,
+       I8042_RESET_ALWAYS,
+       I8042_RESET_ON_S2RAM,
+#define I8042_RESET_DEFAULT    I8042_RESET_ON_S2RAM
+};
+static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
+static int i8042_set_reset(const char *val, const struct kernel_param *kp)
+{
+       enum i8042_controller_reset_mode *arg = kp->arg;
+       int error;
+       bool reset;
+
+       if (val) {
+               error = kstrtobool(val, &reset);
+               if (error)
+                       return error;
+       } else {
+               reset = true;
+       }
+
+       *arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
+       return 0;
+}
+
+static const struct kernel_param_ops param_ops_reset_param = {
+       .flags = KERNEL_PARAM_OPS_FL_NOARG,
+       .set = i8042_set_reset,
+};
+#define param_check_reset_param(name, p)       \
+       __param_check(name, p, enum i8042_controller_reset_mode)
+module_param_named(reset, i8042_reset, reset_param, 0);
+MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
 
 static bool i8042_direct;
 module_param_named(direct, i8042_direct, bool, 0);
@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void)
  * Reset the controller and reset CRT to the original value set by BIOS.
  */
 
-static void i8042_controller_reset(bool force_reset)
+static void i8042_controller_reset(bool s2r_wants_reset)
 {
        i8042_flush();
 
@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset)
  * Reset the controller if requested.
  */
 
-       if (i8042_reset || force_reset)
+       if (i8042_reset == I8042_RESET_ALWAYS ||
+           (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
                i8042_controller_selftest();
+       }
 
 /*
  * Restore the original control register setting.
@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void)
  * before suspending.
  */
 
-static int i8042_controller_resume(bool force_reset)
+static int i8042_controller_resume(bool s2r_wants_reset)
 {
        int error;
 
@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset)
        if (error)
                return error;
 
-       if (i8042_reset || force_reset) {
+       if (i8042_reset == I8042_RESET_ALWAYS ||
+           (i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
                error = i8042_controller_selftest();
                if (error)
                        return error;
@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev)
 
 static int i8042_pm_resume(struct device *dev)
 {
-       bool force_reset;
+       bool want_reset;
        int i;
 
        for (i = 0; i < I8042_NUM_PORTS; i++) {
@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev)
         * off control to the platform firmware, otherwise we can simply restore
         * the mode.
         */
-       force_reset = pm_resume_via_firmware();
+       want_reset = pm_resume_via_firmware();
 
-       return i8042_controller_resume(force_reset);
+       return i8042_controller_resume(want_reset);
 }
 
 static int i8042_pm_thaw(struct device *dev)
@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev)
 
        i8042_platform_device = dev;
 
-       if (i8042_reset) {
+       if (i8042_reset == I8042_RESET_ALWAYS) {
                error = i8042_controller_selftest();
                if (error)
                        return error;
index fb5fb91..552a377 100644 (file)
@@ -157,6 +157,7 @@ struct mip4_ts {
 
        char phys[32];
        char product_name[16];
+       char ic_name[4];
 
        unsigned int max_x;
        unsigned int max_y;
@@ -263,6 +264,18 @@ static int mip4_query_device(struct mip4_ts *ts)
                dev_dbg(&ts->client->dev, "product name: %.*s\n",
                        (int)sizeof(ts->product_name), ts->product_name);
 
+       /* IC name */
+       cmd[0] = MIP4_R0_INFO;
+       cmd[1] = MIP4_R1_INFO_IC_NAME;
+       error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
+                             ts->ic_name, sizeof(ts->ic_name));
+       if (error)
+               dev_warn(&ts->client->dev,
+                        "Failed to retrieve IC name: %d\n", error);
+       else
+               dev_dbg(&ts->client->dev, "IC name: %.*s\n",
+                       (int)sizeof(ts->ic_name), ts->ic_name);
+
        /* Firmware version */
        error = mip4_get_fw_version(ts);
        if (error)
@@ -1326,7 +1339,7 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
         * paired with current firmware in the chip.
         */
        count = snprintf(buf, PAGE_SIZE, "%.*s\n",
-               (int)sizeof(ts->product_name), ts->product_name);
+                        (int)sizeof(ts->product_name), ts->product_name);
 
        mutex_unlock(&ts->input->mutex);
 
@@ -1335,9 +1348,30 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
 
 static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL);
 
+static ssize_t mip4_sysfs_read_ic_name(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mip4_ts *ts = i2c_get_clientdata(client);
+       size_t count;
+
+       mutex_lock(&ts->input->mutex);
+
+       count = snprintf(buf, PAGE_SIZE, "%.*s\n",
+                        (int)sizeof(ts->ic_name), ts->ic_name);
+
+       mutex_unlock(&ts->input->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL);
+
 static struct attribute *mip4_attrs[] = {
        &dev_attr_fw_version.attr,
        &dev_attr_hw_version.attr,
+       &dev_attr_ic_name.attr,
        &dev_attr_update_fw.attr,
        NULL,
 };
@@ -1538,6 +1572,6 @@ static struct i2c_driver mip4_driver = {
 module_i2c_driver(mip4_driver);
 
 MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen");
-MODULE_VERSION("2016.03.12");
+MODULE_VERSION("2016.09.28");
 MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>");
 MODULE_LICENSE("GPL");
index b844c89..daa4ae8 100644 (file)
@@ -52,7 +52,6 @@
 
 #include <linux/bitops.h>
 #include <linux/cpumask.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 0fea985..353c549 100644 (file)
@@ -12,7 +12,6 @@
 #define pr_fmt(fmt)    KBUILD_MODNAME  ": " fmt
 
 #include <linux/bitops.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
index 0ec9263..64c2692 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
index 1d4a5b4..bddf169 100644 (file)
@@ -18,7 +18,6 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
-#include <linux/kconfig.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
 #include <linux/of.h>
index 5eacce1..dc75bea 100644 (file)
@@ -581,7 +581,7 @@ static void init_tio(struct dm_rq_target_io *tio, struct request *rq,
        if (!md->init_tio_pdu)
                memset(&tio->info, 0, sizeof(tio->info));
        if (md->kworker_task)
-               init_kthread_work(&tio->work, map_tio_request);
+               kthread_init_work(&tio->work, map_tio_request);
 }
 
 static struct dm_rq_target_io *dm_old_prep_tio(struct request *rq,
@@ -831,7 +831,7 @@ static void dm_old_request_fn(struct request_queue *q)
                tio = tio_from_request(rq);
                /* Establish tio->ti before queuing work (map_tio_request) */
                tio->ti = ti;
-               queue_kthread_work(&md->kworker, &tio->work);
+               kthread_queue_work(&md->kworker, &tio->work);
                BUG_ON(!irqs_disabled());
        }
 }
@@ -853,7 +853,7 @@ int dm_old_init_request_queue(struct mapped_device *md)
        blk_queue_prep_rq(md->queue, dm_old_prep_fn);
 
        /* Initialize the request-based DM worker thread */
-       init_kthread_worker(&md->kworker);
+       kthread_init_worker(&md->kworker);
        md->kworker_task = kthread_run(kthread_worker_fn, &md->kworker,
                                       "kdmwork-%s", dm_device_name(md));
        if (IS_ERR(md->kworker_task))
index be35258..147af95 100644 (file)
@@ -1891,7 +1891,7 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
        spin_unlock_irq(q->queue_lock);
 
        if (dm_request_based(md) && md->kworker_task)
-               flush_kthread_worker(&md->kworker);
+               kthread_flush_worker(&md->kworker);
 
        /*
         * Take suspend_lock so that presuspend and postsuspend methods
@@ -2147,7 +2147,7 @@ static int __dm_suspend(struct mapped_device *md, struct dm_table *map,
        if (dm_request_based(md)) {
                dm_stop_queue(md->queue);
                if (md->kworker_task)
-                       flush_kthread_worker(&md->kworker);
+                       kthread_flush_worker(&md->kworker);
        }
 
        flush_workqueue(md->wq);
index 1dcc936..dcdd163 100644 (file)
@@ -25,7 +25,6 @@
 #ifndef AF9013_H
 #define AF9013_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 /* AF9013/5 GPIOs (mostly guessed)
index 6ad22b6..5b83e4f 100644 (file)
@@ -22,8 +22,6 @@
 #ifndef AF9033_H
 #define AF9033_H
 
-#include <linux/kconfig.h>
-
 /*
  * I2C address (TODO: are these in 8-bit format?)
  * 0x38, 0x3a, 0x3c, 0x3e
index 6da4ae6..dc61bf7 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __DVB_ASCOT2E_H__
 #define __DVB_ASCOT2E_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
index 5446d13..bb86238 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __ATBM8830_H__
 #define __ATBM8830_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
index 78bf3f7..21c51a4 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __AU8522_H__
 #define __AU8522_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 enum au8522_if_freq {
index 68b69a7..a1956a9 100644 (file)
@@ -28,7 +28,6 @@
 #ifndef CX22702_H
 #define CX22702_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct cx22702_config {
index 962919b..194c703 100644 (file)
@@ -22,8 +22,6 @@
 #ifndef CX24113_H
 #define CX24113_H
 
-#include <linux/kconfig.h>
-
 struct dvb_frontend;
 
 struct cx24113_config {
index f6dbabc..9ff8df8 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef CX24116_H
 #define CX24116_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct cx24116_config {
index 1648ab4..445f13f 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef CX24117_H
 #define CX24117_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct cx24117_config {
index f097042..de4ca9a 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef CX24120_H
 #define CX24120_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/firmware.h>
 
index 975f3c9..aac2344 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef CX24123_H
 #define CX24123_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct cx24123_config {
index d77afe0..f3ff8f6 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef CXD2820R_H
 #define CXD2820R_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 #define CXD2820R_GPIO_D (0 << 0) /* disable */
index 62ad5f0..7f1acfb 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef CXD2841ER_H
 #define CXD2841ER_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 enum cxd2841er_xtal {
index b37e69e..67a6d50 100644 (file)
@@ -13,8 +13,6 @@
 #ifndef DIB3000MC_H
 #define DIB3000MC_H
 
-#include <linux/kconfig.h>
-
 #include "dibx000_common.h"
 
 struct dib3000mc_config {
index 6468c27..8f84dfa 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef DIB7000M_H
 #define DIB7000M_H
 
-#include <linux/kconfig.h>
-
 #include "dibx000_common.h"
 
 struct dib7000m_config {
index baa2789..205fbbf 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef DIB7000P_H
 #define DIB7000P_H
 
-#include <linux/kconfig.h>
-
 #include "dibx000_common.h"
 
 struct dib7000p_config {
index a47c22d..f0507cd 100644 (file)
@@ -24,7 +24,6 @@
 #ifndef _DRXD_H_
 #define _DRXD_H_
 
-#include <linux/kconfig.h>
 #include <linux/types.h>
 #include <linux/i2c.h>
 
index 8f0b9ee..a629897 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef _DRXK_H_
 #define _DRXK_H_
 
-#include <linux/kconfig.h>
 #include <linux/types.h>
 #include <linux/i2c.h>
 
index 153169d..82e8c25 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef DS3000_H
 #define DS3000_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct ds3000_config {
index 15e4cea..50f1af5 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef DVB_DUMMY_FE_H
 #define DVB_DUMMY_FE_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index 9544bab..e894bdc 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef EC100_H
 #define EC100_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct ec100_config {
index 48e9ab7..a14d6f3 100644 (file)
@@ -23,7 +23,6 @@
 #ifndef HD29L2_H
 #define HD29L2_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct hd29l2_config {
index e1b9224..3336154 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef __DVB_HELENE_H__
 #define __DVB_HELENE_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
index c1e2d18..672a556 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __DVB_HORUS3A_H__
 #define __DVB_HORUS3A_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
index af107a2..5eab397 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef DVB_IX2505V_H
 #define DVB_IX2505V_H
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index d20bd90..8c74ddc 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef _LG2160_H_
 #define _LG2160_H_
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index f91a1b4..e7dceb6 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef _LGDT3305_H_
 #define _LGDT3305_H_
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index a5b3faf..f36a7fd 100644 (file)
@@ -23,7 +23,6 @@
 #ifndef LGS8GL5_H
 #define LGS8GL5_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct lgs8gl5_config {
index 368c992..7519c02 100644 (file)
@@ -26,7 +26,6 @@
 #ifndef __LGS8GXX_H__
 #define __LGS8GXX_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/i2c.h>
 
index a088b8e..24431df 100644 (file)
@@ -23,8 +23,6 @@
 #ifndef _LNBH24_H
 #define _LNBH24_H
 
-#include <linux/kconfig.h>
-
 /* system register bits */
 #define LNBH24_OLF     0x01
 #define LNBH24_OTF     0x02
index 1f329ef..f13fd03 100644 (file)
@@ -22,7 +22,6 @@
 #define LNBH25_H
 
 #include <linux/i2c.h>
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 /* 22 kHz tone enabled. Tone output controlled by DSQIN pin */
index cd9101f..4bb6439 100644 (file)
@@ -27,8 +27,6 @@
 #ifndef _LNBP21_H
 #define _LNBP21_H
 
-#include <linux/kconfig.h>
-
 /* system register bits */
 /* [RO] 0=OK; 1=over current limit flag */
 #define LNBP21_OLF     0x01
index 5d01d92..0cb7212 100644 (file)
@@ -28,8 +28,6 @@
 #ifndef _LNBP22_H
 #define _LNBP22_H
 
-#include <linux/kconfig.h>
-
 /* Enable */
 #define LNBP22_EN        0x10
 /* Voltage selection */
index de74301..1a313b0 100644 (file)
@@ -20,7 +20,6 @@
 #ifndef M88RS2000_H
 #define M88RS2000_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index a113282..dfb02db 100644 (file)
@@ -16,7 +16,6 @@
 #ifndef MB86A20S_H
 #define MB86A20S_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 /**
index f58b9ca..b38557c 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __S5H1409_H__
 #define __S5H1409_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct s5h1409_config {
index f3a87f7..791bab0 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __S5H1411_H__
 #define __S5H1411_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 #define S5H1411_I2C_TOP_ADDR (0x32 >> 1)
index f490c5e..b81c9bd 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __S5H1432_H__
 #define __S5H1432_H__
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 #define S5H1432_I2C_TOP_ADDR (0x02 >> 1)
index f5b722d..a47ed89 100644 (file)
@@ -17,7 +17,6 @@
 #ifndef S921_H
 #define S921_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct s921_config {
index ef5f351..b1be62f 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef SI21XX_H
 #define SI21XX_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index 6cceea0..3901cd7 100644 (file)
@@ -17,7 +17,6 @@
 #ifndef SP2_H
 #define SP2_H
 
-#include <linux/kconfig.h>
 #include "dvb_ca_en50221.h"
 
 /*
index da581b6..78e75df 100644 (file)
@@ -23,7 +23,6 @@
 #ifndef __DVB_STB6000_H__
 #define __DVB_STB6000_H__
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index b58603c..803acb9 100644 (file)
@@ -27,7 +27,6 @@
 #ifndef STV0288_H
 #define STV0288_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index 92b3e85..b88166a 100644 (file)
@@ -26,7 +26,6 @@
 #ifndef STV0367_H
 #define STV0367_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index c90bf00..9ca2da9 100644 (file)
@@ -26,7 +26,6 @@
 #ifndef STV0900_H
 #define STV0900_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include "dvb_frontend.h"
 
index f3c8a5c..4604f79 100644 (file)
@@ -25,7 +25,6 @@
 #ifndef __DVB_STV6110_H__
 #define __DVB_STV6110_H__
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index bc77a73..a2cebb0 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef TDA10048_H
 #define TDA10048_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 #include <linux/firmware.h>
 
index 7ebd8ea..e6ccf24 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef _TDA18271C2DD_H_
 #define _TDA18271C2DD_H_
 
-#include <linux/kconfig.h>
-
 #if IS_REACHABLE(CONFIG_DVB_TDA18271C2DD)
 struct dvb_frontend *tda18271c2dd_attach(struct dvb_frontend *fe,
                                         struct i2c_adapter *i2c, u8 adr);
index 9220e5c..facc54f 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef TS2020_H
 #define TS2020_H
 
-#include <linux/kconfig.h>
 #include <linux/dvb/frontend.h>
 
 struct ts2020_config {
index 670e76a..c568d8d 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef DVB_ZL10036_H
 #define DVB_ZL10036_H
 
-#include <linux/kconfig.h>
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index 0709294..66e7085 100644 (file)
@@ -22,8 +22,6 @@
 #ifndef ZL10039_H
 #define ZL10039_H
 
-#include <linux/kconfig.h>
-
 #if IS_REACHABLE(CONFIG_DVB_ZL10039)
 struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe,
                                        u8 i2c_addr,
index 6c51172..57a40c8 100644 (file)
@@ -20,8 +20,6 @@
 #ifndef __ALTERA_CI_H
 #define __ALTERA_CI_H
 
-#include <linux/kconfig.h>
-
 #define ALT_DATA       0x000000ff
 #define ALT_TDI                0x00008000
 #define ALT_TDO                0x00004000
index 374033a..ee48c3e 100644 (file)
@@ -750,7 +750,7 @@ static int ivtv_init_struct1(struct ivtv *itv)
        spin_lock_init(&itv->lock);
        spin_lock_init(&itv->dma_reg_lock);
 
-       init_kthread_worker(&itv->irq_worker);
+       kthread_init_worker(&itv->irq_worker);
        itv->irq_worker_task = kthread_run(kthread_worker_fn, &itv->irq_worker,
                                           "%s", itv->v4l2_dev.name);
        if (IS_ERR(itv->irq_worker_task)) {
@@ -760,7 +760,7 @@ static int ivtv_init_struct1(struct ivtv *itv)
        /* must use the FIFO scheduler as it is realtime sensitive */
        sched_setscheduler(itv->irq_worker_task, SCHED_FIFO, &param);
 
-       init_kthread_work(&itv->irq_work, ivtv_irq_work_handler);
+       kthread_init_work(&itv->irq_work, ivtv_irq_work_handler);
 
        /* Initial settings */
        itv->cxhdl.port = CX2341X_PORT_MEMORY;
@@ -1441,7 +1441,7 @@ static void ivtv_remove(struct pci_dev *pdev)
        del_timer_sync(&itv->dma_timer);
 
        /* Kill irq worker */
-       flush_kthread_worker(&itv->irq_worker);
+       kthread_flush_worker(&itv->irq_worker);
        kthread_stop(itv->irq_worker_task);
 
        ivtv_streams_cleanup(itv);
index 36ca2d6..6efe1f7 100644 (file)
@@ -1062,7 +1062,7 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id)
        }
 
        if (test_and_clear_bit(IVTV_F_I_HAVE_WORK, &itv->i_flags)) {
-               queue_kthread_work(&itv->irq_worker, &itv->irq_work);
+               kthread_queue_work(&itv->irq_worker, &itv->irq_work);
        }
 
        spin_unlock(&itv->dma_reg_lock);
index 81bb568..438cf89 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef LINUX_FC0011_H_
 #define LINUX_FC0011_H_
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 
 
index 9ad3285..4a23e41 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef _FC0012_H_
 #define _FC0012_H_
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 #include "fc001x-common.h"
 
index e130bd7..8c34105 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef _FC0013_H_
 #define _FC0013_H_
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 #include "fc001x-common.h"
 
index 5054f01..aadd9fe 100644 (file)
@@ -22,8 +22,6 @@
 #ifndef __MAX2165_H__
 #define __MAX2165_H__
 
-#include <linux/kconfig.h>
-
 struct dvb_frontend;
 struct i2c_adapter;
 
index b3e614b..6b40df3 100644 (file)
@@ -22,8 +22,6 @@
 #ifndef MC44S803_H
 #define MC44S803_H
 
-#include <linux/kconfig.h>
-
 struct dvb_frontend;
 struct i2c_adapter;
 
index 5764b12..d842734 100644 (file)
@@ -23,8 +23,6 @@
 #ifndef __MXL5005S_H
 #define __MXL5005S_H
 
-#include <linux/kconfig.h>
-
 #include <linux/i2c.h>
 #include "dvb_frontend.h"
 
index b1e5661..fdcab91 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef R820T_H
 #define R820T_H
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 
 enum r820t_chip {
index 5f1a60b..76807f5 100644 (file)
@@ -17,7 +17,6 @@
 #ifndef SI2157_H
 #define SI2157_H
 
-#include <linux/kconfig.h>
 #include <media/media-device.h>
 #include "dvb_frontend.h"
 
index e58c909..6391daf 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef TDA18212_H
 #define TDA18212_H
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 
 struct tda18212_config {
index 1eacb4f..076b5f2 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef TDA18218_H
 #define TDA18218_H
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 
 struct tda18218_config {
index 00ba29e..336bd49 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __XC5000_H__
 #define __XC5000_H__
 
-#include <linux/kconfig.h>
 #include <linux/firmware.h>
 
 struct dvb_frontend;
index 7065aca..e6eae9d 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef __MXL111SF_DEMOD_H__
 #define __MXL111SF_DEMOD_H__
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 #include "mxl111sf.h"
 
index 509b550..e96d9a4 100644 (file)
@@ -21,7 +21,6 @@
 #ifndef __MXL111SF_TUNER_H__
 #define __MXL111SF_TUNER_H__
 
-#include <linux/kconfig.h>
 #include "dvb_frontend.h"
 #include "mxl111sf.h"
 
index 4b08c2a..18ed3bf 100644 (file)
@@ -9,7 +9,6 @@
  * see Documentation/dvb/README.dvb-usb for more information
  */
 
-#include <linux/kconfig.h>
 #include "dibusb.h"
 
 /* Max transfer size done by I2C transfer functions */
index 6d43d75..474c11e 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/kconfig.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/slab.h>
index 29b3436..367523a 100644 (file)
@@ -27,7 +27,7 @@ config VIDEO_FIXED_MINOR_RANGES
 
 config VIDEO_PCI_SKELETON
        tristate "Skeleton PCI V4L2 driver"
-       depends on PCI && BUILD_DOCSRC
+       depends on PCI
        depends on VIDEO_V4L2 && VIDEOBUF2_CORE
        depends on VIDEOBUF2_MEMOPS && VIDEOBUF2_DMA_CONTIG
        ---help---
index 4f27048..d46e4ad 100644 (file)
@@ -39,7 +39,6 @@
 #include <linux/gfp.h>
 #include <linux/slab.h>
 #include <linux/reboot.h>
-#include <linux/kconfig.h>
 #include <linux/leds.h>
 
 #include <linux/mtd/mtd.h>
index c1f34f0..fccdd49 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/err.h>
-#include <linux/kconfig.h>
 
 #include "mtdcore.h"
 
index 3f31ca3..5fa36eb 100644 (file)
@@ -471,9 +471,9 @@ static int bond_check_dev_link(struct bonding *bond,
                /* Yes, the mii is overlaid on the ifreq.ifr_ifru */
                strncpy(ifr.ifr_name, slave_dev->name, IFNAMSIZ);
                mii = if_mii(&ifr);
-               if (IOCTL(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
+               if (ioctl(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
                        mii->reg_num = MII_BMSR;
-                       if (IOCTL(slave_dev, &ifr, SIOCGMIIREG) == 0)
+                       if (ioctl(slave_dev, &ifr, SIOCGMIIREG) == 0)
                                return mii->val_out & BMSR_LSTATUS;
                }
        }
index cc9e6bd..76fb855 100644 (file)
@@ -17,7 +17,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/kconfig.h>
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/platform_device.h>
index bddb198..380a641 100644 (file)
@@ -693,7 +693,7 @@ static int cn23xx_enable_io_queues(struct octeon_device *oct)
                                while ((reg_val & CN23XX_PKT_INPUT_CTL_RST) &&
                                       !(reg_val &
                                         CN23XX_PKT_INPUT_CTL_QUIET) &&
-                                      loop--) {
+                                      --loop) {
                                        reg_val = octeon_read_csr64(
                                            oct,
                                            CN23XX_SLI_IQ_PKT_CONTROL64(q_no));
index e28d960..2d0cb60 100644 (file)
@@ -207,6 +207,7 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
        int ret;
        char *mac_addr = (char *)addr;
        struct hns_mac_cb *mac_cb = hns_get_mac_cb(handle);
+       u8 port_num;
 
        assert(mac_cb);
 
@@ -221,8 +222,11 @@ static int hns_ae_set_multicast_one(struct hnae_handle *handle, void *addr)
                return ret;
        }
 
-       ret = hns_mac_set_multi(mac_cb, DSAF_BASE_INNER_PORT_NUM,
-                               mac_addr, true);
+       ret = hns_mac_get_inner_port_num(mac_cb, handle->vf_id, &port_num);
+       if (ret)
+               return ret;
+
+       ret = hns_mac_set_multi(mac_cb, port_num, mac_addr, true);
        if (ret)
                dev_err(handle->owner_dev,
                        "mac add mul_mac:%pM port%d  fail, ret = %#x!\n",
@@ -678,9 +682,6 @@ static int hns_ae_config_loopback(struct hnae_handle *handle,
                ret = -EINVAL;
        }
 
-       if (!ret)
-               hns_dsaf_set_inner_lb(mac_cb->dsaf_dev, mac_cb->mac_id, en);
-
        return ret;
 }
 
index 22e1410..ec8c738 100644 (file)
@@ -141,9 +141,10 @@ void hns_mac_adjust_link(struct hns_mac_cb *mac_cb, int speed, int duplex)
  *@port_num:port number
  *
  */
-static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
-                                     u8 vmid, u8 *port_num)
+int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb, u8 vmid, u8 *port_num)
 {
+       int q_num_per_vf, vf_num_per_port;
+       int vm_queue_id;
        u8 tmp_port;
 
        if (mac_cb->dsaf_dev->dsaf_mode <= DSAF_MODE_ENABLE) {
@@ -174,6 +175,12 @@ static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
                return -EINVAL;
        }
 
+       q_num_per_vf = mac_cb->dsaf_dev->rcb_common[0]->max_q_per_vf;
+       vf_num_per_port = mac_cb->dsaf_dev->rcb_common[0]->max_vfn;
+
+       vm_queue_id = vmid * q_num_per_vf +
+                       vf_num_per_port * q_num_per_vf * mac_cb->mac_id;
+
        switch (mac_cb->dsaf_dev->dsaf_mode) {
        case DSAF_MODE_ENABLE_FIX:
                tmp_port = 0;
@@ -193,7 +200,7 @@ static int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
        case DSAF_MODE_DISABLE_6PORT_2VM:
        case DSAF_MODE_DISABLE_6PORT_4VM:
        case DSAF_MODE_DISABLE_6PORT_16VM:
-               tmp_port = vmid;
+               tmp_port = vm_queue_id;
                break;
        default:
                dev_err(mac_cb->dev, "dsaf mode invalid, %s mac%d!\n",
index 4cbdf14..d3a1f72 100644 (file)
@@ -461,5 +461,7 @@ void hns_set_led_opt(struct hns_mac_cb *mac_cb);
 int hns_cpld_led_set_id(struct hns_mac_cb *mac_cb,
                        enum hnae_led_state status);
 void hns_mac_set_promisc(struct hns_mac_cb *mac_cb, u8 en);
+int hns_mac_get_inner_port_num(struct hns_mac_cb *mac_cb,
+                              u8 vmid, u8 *port_num);
 
 #endif /* _HNS_DSAF_MAC_H */
index 8e5b3f5..8d70377 100644 (file)
@@ -760,16 +760,6 @@ void hns_dsaf_set_promisc_mode(struct dsaf_device *dsaf_dev, u32 en)
                                 DSAF_CFG_MIX_MODE_S, !!en);
 }
 
-void hns_dsaf_set_inner_lb(struct dsaf_device *dsaf_dev, u32 mac_id, u32 en)
-{
-       if (AE_IS_VER1(dsaf_dev->dsaf_ver) ||
-           dsaf_dev->mac_cb[mac_id]->mac_type == HNAE_PORT_DEBUG)
-               return;
-
-       dsaf_set_dev_bit(dsaf_dev, DSAFV2_SERDES_LBK_0_REG + 4 * mac_id,
-                        DSAFV2_SERDES_LBK_EN_B, !!en);
-}
-
 /**
  * hns_dsaf_tbl_stat_en - tbl
  * @dsaf_id: dsa fabric id
index 35df187..c494fc5 100644 (file)
@@ -466,6 +466,5 @@ void hns_dsaf_get_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id,
                                  u32 *en);
 int hns_dsaf_set_rx_mac_pause_en(struct dsaf_device *dsaf_dev, int mac_id,
                                 u32 en);
-void hns_dsaf_set_inner_lb(struct dsaf_device *dsaf_dev, u32 mac_id, u32 en);
 
 #endif /* __HNS_DSAF_MAIN_H__ */
index ef11077..f0ed80d 100644 (file)
@@ -543,6 +543,22 @@ int hns_rcb_set_coalesce_usecs(
                        "error: coalesce_usecs setting supports 0~1023us\n");
                return -EINVAL;
        }
+
+       if (!AE_IS_VER1(rcb_common->dsaf_dev->dsaf_ver)) {
+               if (timeout == 0)
+                       /* set timeout to 0, Disable gap time */
+                       dsaf_set_reg_field(rcb_common->io_base,
+                                          RCB_INT_GAP_TIME_REG + port_idx * 4,
+                                          PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B,
+                                          0);
+               else
+                       /* set timeout non 0, restore gap time to 1 */
+                       dsaf_set_reg_field(rcb_common->io_base,
+                                          RCB_INT_GAP_TIME_REG + port_idx * 4,
+                                          PPE_INT_GAPTIME_M, PPE_INT_GAPTIME_B,
+                                          1);
+       }
+
        hns_rcb_set_port_timeout(rcb_common, port_idx, timeout);
        return 0;
 }
index 4b8b803..878950a 100644 (file)
 #define RCB_CFG_OVERTIME_REG                   0x9300
 #define RCB_CFG_PKTLINE_INT_NUM_REG            0x9304
 #define RCB_CFG_OVERTIME_INT_NUM_REG           0x9308
+#define RCB_INT_GAP_TIME_REG                   0x9400
 #define RCB_PORT_CFG_OVERTIME_REG              0x9430
 
 #define RCB_RING_RX_RING_BASEADDR_L_REG                0x00000
 #define PPE_CNT_CLR_CE_B       0
 #define PPE_CNT_CLR_SNAP_EN_B  1
 
+#define PPE_INT_GAPTIME_B      0
+#define PPE_INT_GAPTIME_M      0x3ff
+
 #define PPE_COMMON_CNT_CLR_CE_B        0
 #define PPE_COMMON_CNT_CLR_SNAP_EN_B   1
 #define RCB_COM_TSO_MODE_B     0
index 059aaed..dff7b60 100644 (file)
@@ -574,7 +574,6 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
        struct sk_buff *skb;
        struct hnae_desc *desc;
        struct hnae_desc_cb *desc_cb;
-       struct ethhdr *eh;
        unsigned char *va;
        int bnum, length, i;
        int pull_len;
@@ -600,7 +599,6 @@ static int hns_nic_poll_rx_skb(struct hns_nic_ring_data *ring_data,
                ring->stats.sw_err_cnt++;
                return -ENOMEM;
        }
-       skb_reset_mac_header(skb);
 
        prefetchw(skb->data);
        length = le16_to_cpu(desc->rx.pkt_len);
@@ -682,14 +680,6 @@ out_bnum_err:
                return -EFAULT;
        }
 
-       /* filter out multicast pkt with the same src mac as this port */
-       eh = eth_hdr(skb);
-       if (unlikely(is_multicast_ether_addr(eh->h_dest) &&
-                    ether_addr_equal(ndev->dev_addr, eh->h_source))) {
-               dev_kfree_skb_any(skb);
-               return -EFAULT;
-       }
-
        ring->stats.rx_pkts++;
        ring->stats.rx_bytes += skb->len;
 
@@ -747,25 +737,37 @@ static void hns_nic_rx_up_pro(struct hns_nic_ring_data *ring_data,
        ndev->last_rx = jiffies;
 }
 
+static int hns_desc_unused(struct hnae_ring *ring)
+{
+       int ntc = ring->next_to_clean;
+       int ntu = ring->next_to_use;
+
+       return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu;
+}
+
 static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data,
                               int budget, void *v)
 {
        struct hnae_ring *ring = ring_data->ring;
        struct sk_buff *skb;
-       int num, bnum, ex_num;
+       int num, bnum;
 #define RCB_NOF_ALLOC_RX_BUFF_ONCE 16
        int recv_pkts, recv_bds, clean_count, err;
+       int unused_count = hns_desc_unused(ring);
 
        num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
        rmb(); /* make sure num taken effect before the other data is touched */
 
        recv_pkts = 0, recv_bds = 0, clean_count = 0;
-recv:
+       num -= unused_count;
+
        while (recv_pkts < budget && recv_bds < num) {
                /* reuse or realloc buffers */
-               if (clean_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) {
-                       hns_nic_alloc_rx_buffers(ring_data, clean_count);
+               if (clean_count + unused_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) {
+                       hns_nic_alloc_rx_buffers(ring_data,
+                                                clean_count + unused_count);
                        clean_count = 0;
+                       unused_count = hns_desc_unused(ring);
                }
 
                /* poll one pkt */
@@ -786,21 +788,11 @@ recv:
                recv_pkts++;
        }
 
-       /* make all data has been write before submit */
-       if (recv_pkts < budget) {
-               ex_num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
-
-               if (ex_num > clean_count) {
-                       num += ex_num - clean_count;
-                       rmb(); /*complete read rx ring bd number*/
-                       goto recv;
-               }
-       }
-
 out:
        /* make all data has been write before submit */
-       if (clean_count > 0)
-               hns_nic_alloc_rx_buffers(ring_data, clean_count);
+       if (clean_count + unused_count > 0)
+               hns_nic_alloc_rx_buffers(ring_data,
+                                        clean_count + unused_count);
 
        return recv_pkts;
 }
@@ -810,6 +802,8 @@ static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
        struct hnae_ring *ring = ring_data->ring;
        int num = 0;
 
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
+
        /* for hardware bug fixed */
        num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
 
@@ -821,6 +815,20 @@ static void hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
        }
 }
 
+static void hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int num = 0;
+
+       num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
+
+       if (num == 0)
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring, 0);
+       else
+               napi_schedule(&ring_data->napi);
+}
+
 static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring,
                                            int *bytes, int *pkts)
 {
@@ -922,7 +930,11 @@ static int hns_nic_tx_poll_one(struct hns_nic_ring_data *ring_data,
 static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data)
 {
        struct hnae_ring *ring = ring_data->ring;
-       int head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
+       int head;
+
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
+
+       head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
 
        if (head != ring->next_to_clean) {
                ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
@@ -932,6 +944,18 @@ static void hns_nic_tx_fini_pro(struct hns_nic_ring_data *ring_data)
        }
 }
 
+static void hns_nic_tx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       int head = readl_relaxed(ring->io_base + RCB_REG_HEAD);
+
+       if (head == ring->next_to_clean)
+               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
+                       ring, 0);
+       else
+               napi_schedule(&ring_data->napi);
+}
+
 static void hns_nic_tx_clr_all_bufs(struct hns_nic_ring_data *ring_data)
 {
        struct hnae_ring *ring = ring_data->ring;
@@ -963,10 +987,7 @@ static int hns_nic_common_poll(struct napi_struct *napi, int budget)
 
        if (clean_complete >= 0 && clean_complete < budget) {
                napi_complete(napi);
-               ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
-                       ring_data->ring, 0);
-               if (ring_data->fini_process)
-                       ring_data->fini_process(ring_data);
+               ring_data->fini_process(ring_data);
                return 0;
        }
 
@@ -1562,6 +1583,21 @@ struct rtnl_link_stats64 *hns_nic_get_stats64(struct net_device *ndev,
        return stats;
 }
 
+static u16
+hns_nic_select_queue(struct net_device *ndev, struct sk_buff *skb,
+                    void *accel_priv, select_queue_fallback_t fallback)
+{
+       struct ethhdr *eth_hdr = (struct ethhdr *)skb->data;
+       struct hns_nic_priv *priv = netdev_priv(ndev);
+
+       /* fix hardware broadcast/multicast packets queue loopback */
+       if (!AE_IS_VER1(priv->enet_ver) &&
+           is_multicast_ether_addr(eth_hdr->h_dest))
+               return 0;
+       else
+               return fallback(ndev, skb);
+}
+
 static const struct net_device_ops hns_nic_netdev_ops = {
        .ndo_open = hns_nic_net_open,
        .ndo_stop = hns_nic_net_stop,
@@ -1577,6 +1613,7 @@ static const struct net_device_ops hns_nic_netdev_ops = {
        .ndo_poll_controller = hns_nic_poll_controller,
 #endif
        .ndo_set_rx_mode = hns_nic_set_rx_mode,
+       .ndo_select_queue = hns_nic_select_queue,
 };
 
 static void hns_nic_update_link_status(struct net_device *netdev)
@@ -1738,7 +1775,8 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
                rd->queue_index = i;
                rd->ring = &h->qs[i]->tx_ring;
                rd->poll_one = hns_nic_tx_poll_one;
-               rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro : NULL;
+               rd->fini_process = is_ver1 ? hns_nic_tx_fini_pro :
+                       hns_nic_tx_fini_pro_v2;
 
                netif_napi_add(priv->netdev, &rd->napi,
                               hns_nic_common_poll, NIC_TX_CLEAN_MAX_NUM);
@@ -1750,7 +1788,8 @@ static int hns_nic_init_ring_data(struct hns_nic_priv *priv)
                rd->ring = &h->qs[i - h->q_num]->rx_ring;
                rd->poll_one = hns_nic_rx_poll_one;
                rd->ex_process = hns_nic_rx_up_pro;
-               rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro : NULL;
+               rd->fini_process = is_ver1 ? hns_nic_rx_fini_pro :
+                       hns_nic_rx_fini_pro_v2;
 
                netif_napi_add(priv->netdev, &rd->napi,
                               hns_nic_common_poll, NIC_RX_CLEAN_MAX_NUM);
index 47e59bb..87d5c94 100644 (file)
@@ -352,6 +352,13 @@ static int __lb_setup(struct net_device *ndev,
                break;
        }
 
+       if (!ret) {
+               if (loop == MAC_LOOP_NONE)
+                       h->dev->ops->set_promisc_mode(
+                               h, ndev->flags & IFF_PROMISC);
+               else
+                       h->dev->ops->set_promisc_mode(h, 1);
+       }
        return ret;
 }
 
index d458515..cc4fd61 100644 (file)
@@ -287,7 +287,7 @@ retry:
 
                        goto retry;
                }
-               MLX5_SET64(manage_pages_in, in, pas[i], addr);
+               MLX5_ARRAY_SET64(manage_pages_in, in, pas, i, addr);
        }
 
        MLX5_SET(manage_pages_in, in, opcode, MLX5_CMD_OP_MANAGE_PAGES);
@@ -344,7 +344,7 @@ static int reclaim_pages_cmd(struct mlx5_core_dev *dev,
                if (fwp->func_id != func_id)
                        continue;
 
-               MLX5_SET64(manage_pages_out, out, pas[i], fwp->addr);
+               MLX5_ARRAY_SET64(manage_pages_out, out, pas, i, fwp->addr);
                i++;
        }
 
index 42e3407..b14f030 100644 (file)
@@ -821,7 +821,7 @@ static void encx24j600_set_multicast_list(struct net_device *dev)
        }
 
        if (oldfilter != priv->rxfilter)
-               queue_kthread_work(&priv->kworker, &priv->setrx_work);
+               kthread_queue_work(&priv->kworker, &priv->setrx_work);
 }
 
 static void encx24j600_hw_tx(struct encx24j600_priv *priv)
@@ -879,7 +879,7 @@ static netdev_tx_t encx24j600_tx(struct sk_buff *skb, struct net_device *dev)
        /* Remember the skb for deferred processing */
        priv->tx_skb = skb;
 
-       queue_kthread_work(&priv->kworker, &priv->tx_work);
+       kthread_queue_work(&priv->kworker, &priv->tx_work);
 
        return NETDEV_TX_OK;
 }
@@ -1037,9 +1037,9 @@ static int encx24j600_spi_probe(struct spi_device *spi)
                goto out_free;
        }
 
-       init_kthread_worker(&priv->kworker);
-       init_kthread_work(&priv->tx_work, encx24j600_tx_proc);
-       init_kthread_work(&priv->setrx_work, encx24j600_setrx_proc);
+       kthread_init_worker(&priv->kworker);
+       kthread_init_work(&priv->tx_work, encx24j600_tx_proc);
+       kthread_init_work(&priv->setrx_work, encx24j600_setrx_proc);
 
        priv->kworker_task = kthread_run(kthread_worker_fn, &priv->kworker,
                                         "encx24j600");
index 0df1391..1e8339a 100644 (file)
@@ -107,15 +107,4 @@ config QEDE
        ---help---
          This enables the support for ...
 
-config INFINIBAND_QEDR
-       tristate "QLogic qede RoCE sources [debug]"
-       depends on QEDE && 64BIT
-       select QED_LL2
-       default n
-       ---help---
-         This provides a temporary node that allows the compilation
-         and logical testing of the InfiniBand over Ethernet support
-         for QLogic QED. This would be replaced by the 'real' option
-         once the QEDR driver is added [+relocated].
-
 endif # NET_VENDOR_QLOGIC
index a6db107..02a8be2 100644 (file)
@@ -1517,7 +1517,7 @@ static void qed_ll2_register_cb_ops(struct qed_dev *cdev,
 static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
 {
        struct qed_ll2_info ll2_info;
-       struct qed_ll2_buffer *buffer;
+       struct qed_ll2_buffer *buffer, *tmp_buffer;
        enum qed_ll2_conn_type conn_type;
        struct qed_ptt *p_ptt;
        int rc, i;
@@ -1587,7 +1587,7 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)
 
        /* Post all Rx buffers to FW */
        spin_lock_bh(&cdev->ll2->lock);
-       list_for_each_entry(buffer, &cdev->ll2->list, list) {
+       list_for_each_entry_safe(buffer, tmp_buffer, &cdev->ll2->list, list) {
                rc = qed_ll2_post_rx_buffer(QED_LEADING_HWFN(cdev),
                                            cdev->ll2->handle,
                                            buffer->phys_addr, 0, buffer, 1);
index 2343005..76831a3 100644 (file)
@@ -2947,7 +2947,7 @@ static const struct qed_rdma_ops qed_rdma_ops_pass = {
        .roce_ll2_stats = &qed_roce_ll2_stats,
 };
 
-const struct qed_rdma_ops *qed_get_rdma_ops()
+const struct qed_rdma_ops *qed_get_rdma_ops(void)
 {
        return &qed_rdma_ops_pass;
 }
index 4c8c60a..6c85b61 100644 (file)
@@ -650,20 +650,27 @@ static int stmmac_init_ptp(struct stmmac_priv *priv)
        if (IS_ERR(priv->clk_ptp_ref)) {
                priv->clk_ptp_rate = clk_get_rate(priv->stmmac_clk);
                priv->clk_ptp_ref = NULL;
+               netdev_dbg(priv->dev, "PTP uses main clock\n");
        } else {
                clk_prepare_enable(priv->clk_ptp_ref);
                priv->clk_ptp_rate = clk_get_rate(priv->clk_ptp_ref);
+               netdev_dbg(priv->dev, "PTP rate %d\n", priv->clk_ptp_rate);
        }
 
        priv->adv_ts = 0;
-       if (priv->dma_cap.atime_stamp && priv->extend_desc)
+       /* Check if adv_ts can be enabled for dwmac 4.x core */
+       if (priv->plat->has_gmac4 && priv->dma_cap.atime_stamp)
+               priv->adv_ts = 1;
+       /* Dwmac 3.x core with extend_desc can support adv_ts */
+       else if (priv->extend_desc && priv->dma_cap.atime_stamp)
                priv->adv_ts = 1;
 
-       if (netif_msg_hw(priv) && priv->dma_cap.time_stamp)
-               pr_debug("IEEE 1588-2002 Time Stamp supported\n");
+       if (priv->dma_cap.time_stamp)
+               netdev_info(priv->dev, "IEEE 1588-2002 Timestamp supported\n");
 
-       if (netif_msg_hw(priv) && priv->adv_ts)
-               pr_debug("IEEE 1588-2008 Advanced Time Stamp supported\n");
+       if (priv->adv_ts)
+               netdev_info(priv->dev,
+                           "IEEE 1588-2008 Advanced Timestamp supported\n");
 
        priv->hw->ptp = &stmmac_ptp;
        priv->hwts_tx_en = 0;
@@ -1702,8 +1709,8 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
 
        if (init_ptp) {
                ret = stmmac_init_ptp(priv);
-               if (ret && ret != -EOPNOTSUPP)
-                       pr_warn("%s: failed PTP initialisation\n", __func__);
+               if (ret)
+                       netdev_warn(priv->dev, "PTP support cannot init.\n");
        }
 
 #ifdef CONFIG_DEBUG_FS
index 6e3b829..289d527 100644 (file)
@@ -186,10 +186,12 @@ int stmmac_ptp_register(struct stmmac_priv *priv)
                                             priv->device);
        if (IS_ERR(priv->ptp_clock)) {
                priv->ptp_clock = NULL;
-               pr_err("ptp_clock_register() failed on %s\n", priv->dev->name);
-       } else if (priv->ptp_clock)
-               pr_debug("Added PTP HW clock successfully on %s\n",
-                        priv->dev->name);
+               return PTR_ERR(priv->ptp_clock);
+       }
+
+       spin_lock_init(&priv->ptp_lock);
+
+       netdev_dbg(priv->dev, "Added PTP HW clock successfully\n");
 
        return 0;
 }
index e15bf84..0ac449a 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/highmem.h>
 #include <linux/if_vlan.h>
 #include <linux/init.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
index ece0ea0..6c7ec1d 100644 (file)
@@ -610,8 +610,8 @@ err_out_regions:
 #ifdef CONFIG_PCI
        if (pdev)
                pci_release_regions(pdev);
-#endif
 err_out:
+#endif
        if (pdev)
                pci_disable_device(pdev);
        return rc;
index 37ab46c..d2349a1 100644 (file)
@@ -9,7 +9,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/kconfig.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/platform_device.h>
index 0b37ce9..ca31a57 100644 (file)
@@ -10,7 +10,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/kconfig.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/platform_device.h>
index 35f9f97..c688d68 100644 (file)
@@ -431,8 +431,7 @@ static void axienet_setoptions(struct net_device *ndev, u32 options)
        lp->options |= options;
 }
 
-static void __axienet_device_reset(struct axienet_local *lp,
-                                  struct device *dev, off_t offset)
+static void __axienet_device_reset(struct axienet_local *lp, off_t offset)
 {
        u32 timeout;
        /* Reset Axi DMA. This would reset Axi Ethernet core as well. The reset
@@ -468,8 +467,8 @@ static void axienet_device_reset(struct net_device *ndev)
        u32 axienet_status;
        struct axienet_local *lp = netdev_priv(ndev);
 
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
        lp->max_frm_size = XAE_MAX_VLAN_FRAME_SIZE;
        lp->options |= XAE_OPTION_VLAN;
@@ -1338,8 +1337,8 @@ static void axienet_dma_err_handler(unsigned long data)
        axienet_iow(lp, XAE_MDIO_MC_OFFSET, (mdio_mcreg &
                    ~XAE_MDIO_MC_MDIOEN_MASK));
 
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_TX_CR_OFFSET);
-       __axienet_device_reset(lp, &ndev->dev, XAXIDMA_RX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_TX_CR_OFFSET);
+       __axienet_device_reset(lp, XAXIDMA_RX_CR_OFFSET);
 
        axienet_iow(lp, XAE_MDIO_MC_OFFSET, mdio_mcreg);
        axienet_mdio_wait_until_ready(lp);
index 52eeb2f..f0919bd 100644 (file)
@@ -442,8 +442,6 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
        }
 
        net_trans_info = get_net_transport_info(skb, &hdr_offset);
-       if (net_trans_info == TRANSPORT_INFO_NOT_IP)
-               goto do_send;
 
        /*
         * Setup the sendside checksum offload only if this is not a
@@ -478,56 +476,29 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
                }
                lso_info->lso_v2_transmit.tcp_header_offset = hdr_offset;
                lso_info->lso_v2_transmit.mss = skb_shinfo(skb)->gso_size;
-               goto do_send;
-       }
-
-       if ((skb->ip_summed == CHECKSUM_NONE) ||
-           (skb->ip_summed == CHECKSUM_UNNECESSARY))
-               goto do_send;
-
-       rndis_msg_size += NDIS_CSUM_PPI_SIZE;
-       ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
-                           TCPIP_CHKSUM_PKTINFO);
-
-       csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
-                       ppi->ppi_offset);
-
-       if (net_trans_info & (INFO_IPV4 << 16))
-               csum_info->transmit.is_ipv4 = 1;
-       else
-               csum_info->transmit.is_ipv6 = 1;
-
-       if (net_trans_info & INFO_TCP) {
-               csum_info->transmit.tcp_checksum = 1;
-               csum_info->transmit.tcp_header_offset = hdr_offset;
-       } else if (net_trans_info & INFO_UDP) {
-               /* UDP checksum offload is not supported on ws2008r2.
-                * Furthermore, on ws2012 and ws2012r2, there are some
-                * issues with udp checksum offload from Linux guests.
-                * (these are host issues).
-                * For now compute the checksum here.
-                */
-               struct udphdr *uh;
-               u16 udp_len;
-
-               ret = skb_cow_head(skb, 0);
-               if (ret)
-                       goto no_memory;
-
-               uh = udp_hdr(skb);
-               udp_len = ntohs(uh->len);
-               uh->check = 0;
-               uh->check = csum_tcpudp_magic(ip_hdr(skb)->saddr,
-                                             ip_hdr(skb)->daddr,
-                                             udp_len, IPPROTO_UDP,
-                                             csum_partial(uh, udp_len, 0));
-               if (uh->check == 0)
-                       uh->check = CSUM_MANGLED_0;
-
-               csum_info->transmit.udp_checksum = 0;
+       } else if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               if (net_trans_info & INFO_TCP) {
+                       rndis_msg_size += NDIS_CSUM_PPI_SIZE;
+                       ppi = init_ppi_data(rndis_msg, NDIS_CSUM_PPI_SIZE,
+                                           TCPIP_CHKSUM_PKTINFO);
+
+                       csum_info = (struct ndis_tcp_ip_checksum_info *)((void *)ppi +
+                                                                        ppi->ppi_offset);
+
+                       if (net_trans_info & (INFO_IPV4 << 16))
+                               csum_info->transmit.is_ipv4 = 1;
+                       else
+                               csum_info->transmit.is_ipv6 = 1;
+
+                       csum_info->transmit.tcp_checksum = 1;
+                       csum_info->transmit.tcp_header_offset = hdr_offset;
+               } else {
+                       /* UDP checksum (and other) offload is not supported. */
+                       if (skb_checksum_help(skb))
+                               goto drop;
+               }
        }
 
-do_send:
        /* Start filling in the page buffers with the rndis hdr */
        rndis_msg->msg_len += rndis_msg_size;
        packet->total_data_buflen = rndis_msg->msg_len;
index c6f6683..f424b86 100644 (file)
@@ -607,6 +607,21 @@ void phy_start_machine(struct phy_device *phydev)
        queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
 }
 
+/**
+ * phy_trigger_machine - trigger the state machine to run
+ *
+ * @phydev: the phy_device struct
+ *
+ * Description: There has been a change in state which requires that the
+ *   state machine runs.
+ */
+
+static void phy_trigger_machine(struct phy_device *phydev)
+{
+       cancel_delayed_work_sync(&phydev->state_queue);
+       queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);
+}
+
 /**
  * phy_stop_machine - stop the PHY state machine tracking
  * @phydev: target phy_device struct
@@ -639,6 +654,8 @@ static void phy_error(struct phy_device *phydev)
        mutex_lock(&phydev->lock);
        phydev->state = PHY_HALTED;
        mutex_unlock(&phydev->lock);
+
+       phy_trigger_machine(phydev);
 }
 
 /**
@@ -800,8 +817,7 @@ void phy_change(struct work_struct *work)
        }
 
        /* reschedule state queue work to run as soon as possible */
-       cancel_delayed_work_sync(&phydev->state_queue);
-       queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, 0);
+       phy_trigger_machine(phydev);
        return;
 
 ignore:
@@ -890,6 +906,8 @@ void phy_start(struct phy_device *phydev)
        /* if phy was suspended, bring the physical link up again */
        if (do_resume)
                phy_resume(phydev);
+
+       phy_trigger_machine(phydev);
 }
 EXPORT_SYMBOL(phy_start);
 
index 9d1fce8..3ff76c6 100644 (file)
@@ -59,6 +59,10 @@ enum qmi_wwan_flags {
        QMI_WWAN_FLAG_RAWIP = 1 << 0,
 };
 
+enum qmi_wwan_quirks {
+       QMI_WWAN_QUIRK_DTR = 1 << 0,    /* needs "set DTR" request */
+};
+
 static void qmi_wwan_netdev_setup(struct net_device *net)
 {
        struct usbnet *dev = netdev_priv(net);
@@ -411,9 +415,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
         * clearing out state the clients might need.
         *
         * MDM9x30 is the first QMI chipset with USB3 support. Abuse
-        * this fact to enable the quirk.
+        * this fact to enable the quirk for all USB3 devices.
+        *
+        * There are also chipsets with the same "set DTR" requirement
+        * but without USB3 support.  Devices based on these chips
+        * need a quirk flag in the device ID table.
         */
-       if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+       if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR ||
+           le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
                qmi_wwan_manage_power(dev, 1);
                qmi_wwan_change_dtr(dev, true);
        }
@@ -526,6 +535,16 @@ static const struct driver_info    qmi_wwan_info = {
        .rx_fixup       = qmi_wwan_rx_fixup,
 };
 
+static const struct driver_info        qmi_wwan_info_quirk_dtr = {
+       .description    = "WWAN/QMI device",
+       .flags          = FLAG_WWAN,
+       .bind           = qmi_wwan_bind,
+       .unbind         = qmi_wwan_unbind,
+       .manage_power   = qmi_wwan_manage_power,
+       .rx_fixup       = qmi_wwan_rx_fixup,
+       .data           = QMI_WWAN_QUIRK_DTR,
+};
+
 #define HUAWEI_VENDOR_ID       0x12D1
 
 /* map QMI/wwan function by a fixed interface number */
@@ -533,6 +552,11 @@ static const struct driver_info    qmi_wwan_info = {
        USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
        .driver_info = (unsigned long)&qmi_wwan_info
 
+/* devices requiring "set DTR" quirk */
+#define QMI_QUIRK_SET_DTR(vend, prod, num) \
+       USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
+       .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr
+
 /* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
 #define QMI_GOBI1K_DEVICE(vend, prod) \
        QMI_FIXED_INTF(vend, prod, 3)
@@ -895,6 +919,8 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)},    /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
        {QMI_FIXED_INTF(0x22de, 0x9061, 3)},    /* WeTelecom WPD-600N */
        {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)},    /* SIMCom 7230E */
+       {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0  Mini PCIe */
+       {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
 
        /* 4. Gobi 1000 devices */
        {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
index cf68149..3ce1f7d 100644 (file)
@@ -407,4 +407,8 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len,
 
 void xenvif_set_skb_hash(struct xenvif *vif, struct sk_buff *skb);
 
+#ifdef CONFIG_DEBUG_FS
+void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m);
+#endif
+
 #endif /* __XEN_NETBACK__COMMON_H__ */
index 613bac0..e8c5ddd 100644 (file)
@@ -360,6 +360,74 @@ u32 xenvif_set_hash_mapping(struct xenvif *vif, u32 gref, u32 len,
        return XEN_NETIF_CTRL_STATUS_SUCCESS;
 }
 
+#ifdef CONFIG_DEBUG_FS
+void xenvif_dump_hash_info(struct xenvif *vif, struct seq_file *m)
+{
+       unsigned int i;
+
+       switch (vif->hash.alg) {
+       case XEN_NETIF_CTRL_HASH_ALGORITHM_TOEPLITZ:
+               seq_puts(m, "Hash Algorithm: TOEPLITZ\n");
+               break;
+
+       case XEN_NETIF_CTRL_HASH_ALGORITHM_NONE:
+               seq_puts(m, "Hash Algorithm: NONE\n");
+               /* FALLTHRU */
+       default:
+               return;
+       }
+
+       if (vif->hash.flags) {
+               seq_puts(m, "\nHash Flags:\n");
+
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4)
+                       seq_puts(m, "- IPv4\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV4_TCP)
+                       seq_puts(m, "- IPv4 + TCP\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6)
+                       seq_puts(m, "- IPv6\n");
+               if (vif->hash.flags & XEN_NETIF_CTRL_HASH_TYPE_IPV6_TCP)
+                       seq_puts(m, "- IPv6 + TCP\n");
+       }
+
+       seq_puts(m, "\nHash Key:\n");
+
+       for (i = 0; i < XEN_NETBK_MAX_HASH_KEY_SIZE; ) {
+               unsigned int j, n;
+
+               n = 8;
+               if (i + n >= XEN_NETBK_MAX_HASH_KEY_SIZE)
+                       n = XEN_NETBK_MAX_HASH_KEY_SIZE - i;
+
+               seq_printf(m, "[%2u - %2u]: ", i, i + n - 1);
+
+               for (j = 0; j < n; j++, i++)
+                       seq_printf(m, "%02x ", vif->hash.key[i]);
+
+               seq_puts(m, "\n");
+       }
+
+       if (vif->hash.size != 0) {
+               seq_puts(m, "\nHash Mapping:\n");
+
+               for (i = 0; i < vif->hash.size; ) {
+                       unsigned int j, n;
+
+                       n = 8;
+                       if (i + n >= vif->hash.size)
+                               n = vif->hash.size - i;
+
+                       seq_printf(m, "[%4u - %4u]: ", i, i + n - 1);
+
+                       for (j = 0; j < n; j++, i++)
+                               seq_printf(m, "%4u ", vif->hash.mapping[i]);
+
+                       seq_puts(m, "\n");
+               }
+       }
+}
+#endif /* CONFIG_DEBUG_FS */
+
 void xenvif_init_hash(struct xenvif *vif)
 {
        if (xenvif_hash_cache_size == 0)
index 8e9ade6..b1cf7c6 100644 (file)
@@ -337,9 +337,9 @@ static void xenvif_rx_next_chunk(struct xenvif_queue *queue,
        frag_data += pkt->frag_offset;
        frag_len -= pkt->frag_offset;
 
-       chunk_len = min(frag_len, XEN_PAGE_SIZE - offset);
-       chunk_len = min(chunk_len,
-                       XEN_PAGE_SIZE - xen_offset_in_page(frag_data));
+       chunk_len = min_t(size_t, frag_len, XEN_PAGE_SIZE - offset);
+       chunk_len = min_t(size_t, chunk_len, XEN_PAGE_SIZE -
+                                            xen_offset_in_page(frag_data));
 
        pkt->frag_offset += chunk_len;
 
@@ -425,6 +425,8 @@ void xenvif_rx_skb(struct xenvif_queue *queue)
 
        xenvif_rx_next_skb(queue, &pkt);
 
+       queue->last_rx_time = jiffies;
+
        do {
                struct xen_netif_rx_request *req;
                struct xen_netif_rx_response *rsp;
index 7056404..8674e18 100644 (file)
@@ -165,7 +165,7 @@ xenvif_write_io_ring(struct file *filp, const char __user *buf, size_t count,
        return count;
 }
 
-static int xenvif_dump_open(struct inode *inode, struct file *filp)
+static int xenvif_io_ring_open(struct inode *inode, struct file *filp)
 {
        int ret;
        void *queue = NULL;
@@ -179,13 +179,35 @@ static int xenvif_dump_open(struct inode *inode, struct file *filp)
 
 static const struct file_operations xenvif_dbg_io_ring_ops_fops = {
        .owner = THIS_MODULE,
-       .open = xenvif_dump_open,
+       .open = xenvif_io_ring_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = single_release,
        .write = xenvif_write_io_ring,
 };
 
+static int xenvif_read_ctrl(struct seq_file *m, void *v)
+{
+       struct xenvif *vif = m->private;
+
+       xenvif_dump_hash_info(vif, m);
+
+       return 0;
+}
+
+static int xenvif_ctrl_open(struct inode *inode, struct file *filp)
+{
+       return single_open(filp, xenvif_read_ctrl, inode->i_private);
+}
+
+static const struct file_operations xenvif_dbg_ctrl_ops_fops = {
+       .owner = THIS_MODULE,
+       .open = xenvif_ctrl_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
 static void xenvif_debugfs_addif(struct xenvif *vif)
 {
        struct dentry *pfile;
@@ -210,6 +232,17 @@ static void xenvif_debugfs_addif(struct xenvif *vif)
                                pr_warn("Creation of io_ring file returned %ld!\n",
                                        PTR_ERR(pfile));
                }
+
+               if (vif->ctrl_irq) {
+                       pfile = debugfs_create_file("ctrl",
+                                                   S_IRUSR,
+                                                   vif->xenvif_dbg_root,
+                                                   vif,
+                                                   &xenvif_dbg_ctrl_ops_fops);
+                       if (IS_ERR_OR_NULL(pfile))
+                               pr_warn("Creation of ctrl file returned %ld!\n",
+                                       PTR_ERR(pfile));
+               }
        } else
                netdev_warn(vif->dev,
                            "Creation of vif debugfs dir returned %ld!\n",
index 68ef187..0fc99f0 100644 (file)
@@ -515,7 +515,8 @@ static int nvme_map_data(struct nvme_dev *dev, struct request *req,
                goto out;
 
        ret = BLK_MQ_RQ_QUEUE_BUSY;
-       if (!dma_map_sg(dev->dev, iod->sg, iod->nents, dma_dir))
+       if (!dma_map_sg_attrs(dev->dev, iod->sg, iod->nents, dma_dir,
+                               DMA_ATTR_NO_WARN))
                goto out;
 
        if (!nvme_setup_prps(dev, req, size))
index e4a5b7e..4fce494 100644 (file)
@@ -230,20 +230,20 @@ static int advk_pcie_link_up(struct advk_pcie *pcie)
 
 static int advk_pcie_wait_for_link(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        int retries;
 
        /* check if the link is up or not */
        for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
                if (advk_pcie_link_up(pcie)) {
-                       dev_info(&pcie->pdev->dev, "link up\n");
+                       dev_info(dev, "link up\n");
                        return 0;
                }
 
                usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
        }
 
-       dev_err(&pcie->pdev->dev, "link never came up\n");
-
+       dev_err(dev, "link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -376,6 +376,7 @@ static void advk_pcie_setup_hw(struct advk_pcie *pcie)
 
 static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        u32 reg;
        unsigned int status;
        char *strcomp_status, *str_posted;
@@ -407,12 +408,13 @@ static void advk_pcie_check_pio_status(struct advk_pcie *pcie)
        else
                str_posted = "Posted";
 
-       dev_err(&pcie->pdev->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
+       dev_err(dev, "%s PIO Response Status: %s, %#x @ %#x\n",
                str_posted, strcomp_status, reg, advk_readl(pcie, PIO_ADDR_LS));
 }
 
 static int advk_pcie_wait_pio(struct advk_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        unsigned long timeout;
 
        timeout = jiffies + msecs_to_jiffies(PIO_TIMEOUT_MS);
@@ -426,7 +428,7 @@ static int advk_pcie_wait_pio(struct advk_pcie *pcie)
                        return 0;
        }
 
-       dev_err(&pcie->pdev->dev, "config read/write timed out\n");
+       dev_err(dev, "config read/write timed out\n");
        return -ETIMEDOUT;
 }
 
@@ -560,10 +562,11 @@ static int advk_pcie_alloc_msi(struct advk_pcie *pcie)
 
 static void advk_pcie_free_msi(struct advk_pcie *pcie, int hwirq)
 {
+       struct device *dev = &pcie->pdev->dev;
+
        mutex_lock(&pcie->msi_used_lock);
        if (!test_bit(hwirq, pcie->msi_irq_in_use))
-               dev_err(&pcie->pdev->dev, "trying to free unused MSI#%d\n",
-                       hwirq);
+               dev_err(dev, "trying to free unused MSI#%d\n", hwirq);
        else
                clear_bit(hwirq, pcie->msi_irq_in_use);
        mutex_unlock(&pcie->msi_used_lock);
@@ -910,6 +913,7 @@ out_release_res:
 
 static int advk_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct advk_pcie *pcie;
        struct resource *res;
        struct pci_bus *bus, *child;
@@ -917,31 +921,29 @@ static int advk_pcie_probe(struct platform_device *pdev)
        struct device_node *msi_node;
        int ret, irq;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct advk_pcie),
-                           GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(struct advk_pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
        pcie->pdev = pdev;
-       platform_set_drvdata(pdev, pcie);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pcie->base = devm_ioremap_resource(&pdev->dev, res);
+       pcie->base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->base))
                return PTR_ERR(pcie->base);
 
        irq = platform_get_irq(pdev, 0);
-       ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
+       ret = devm_request_irq(dev, irq, advk_pcie_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD, "advk-pcie",
                               pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to register interrupt\n");
+               dev_err(dev, "Failed to register interrupt\n");
                return ret;
        }
 
        ret = advk_pcie_parse_request_of_pci_ranges(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to parse resources\n");
+               dev_err(dev, "Failed to parse resources\n");
                return ret;
        }
 
@@ -949,24 +951,24 @@ static int advk_pcie_probe(struct platform_device *pdev)
 
        ret = advk_pcie_init_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize irq\n");
+               dev_err(dev, "Failed to initialize irq\n");
                return ret;
        }
 
        ret = advk_pcie_init_msi_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed to initialize irq\n");
+               dev_err(dev, "Failed to initialize irq\n");
                advk_pcie_remove_irq_domain(pcie);
                return ret;
        }
 
-       msi_node = of_parse_phandle(pdev->dev.of_node, "msi-parent", 0);
+       msi_node = of_parse_phandle(dev->of_node, "msi-parent", 0);
        if (msi_node)
                msi = of_pci_find_msi_chip_by_node(msi_node);
        else
                msi = NULL;
 
-       bus = pci_scan_root_bus_msi(&pdev->dev, 0, &advk_pcie_ops,
+       bus = pci_scan_root_bus_msi(dev, 0, &advk_pcie_ops,
                                    pcie, &pcie->resources, &pcie->msi);
        if (!bus) {
                advk_pcie_remove_msi_irq_domain(pcie);
@@ -980,7 +982,6 @@ static int advk_pcie_probe(struct platform_device *pdev)
                pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
-
        return 0;
 }
 
index 19223ed..9595fad 100644 (file)
 #define        DRA7XX_CPU_TO_BUS_ADDR                          0x0FFFFFFF
 
 struct dra7xx_pcie {
-       void __iomem            *base;
-       struct phy              **phy;
-       int                     phy_count;
-       struct device           *dev;
        struct pcie_port        pp;
+       void __iomem            *base;          /* DT ti_conf */
+       int                     phy_count;      /* DT phy-names count */
+       struct phy              **phy;
 };
 
 #define to_dra7xx_pcie(x)      container_of((x), struct dra7xx_pcie, pp)
@@ -84,17 +83,6 @@ static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
        writel(value, pcie->base + offset);
 }
 
-static inline u32 dra7xx_pcie_readl_rc(struct pcie_port *pp, u32 offset)
-{
-       return readl(pp->dbi_base + offset);
-}
-
-static inline void dra7xx_pcie_writel_rc(struct pcie_port *pp, u32 offset,
-                                        u32 value)
-{
-       writel(value, pp->dbi_base + offset);
-}
-
 static int dra7xx_pcie_link_up(struct pcie_port *pp)
 {
        struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
@@ -103,13 +91,14 @@ static int dra7xx_pcie_link_up(struct pcie_port *pp)
        return !!(reg & LINK_UP);
 }
 
-static int dra7xx_pcie_establish_link(struct pcie_port *pp)
+static int dra7xx_pcie_establish_link(struct dra7xx_pcie *dra7xx)
 {
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+       struct pcie_port *pp = &dra7xx->pp;
+       struct device *dev = pp->dev;
        u32 reg;
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "link is already up\n");
+               dev_err(dev, "link is already up\n");
                return 0;
        }
 
@@ -120,10 +109,8 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
        return dw_pcie_wait_for_link(pp);
 }
 
-static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
+static void dra7xx_pcie_enable_interrupts(struct dra7xx_pcie *dra7xx)
 {
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
-
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
                           ~INTERRUPTS);
        dra7xx_pcie_writel(dra7xx,
@@ -142,6 +129,8 @@ static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
 
 static void dra7xx_pcie_host_init(struct pcie_port *pp)
 {
+       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+
        pp->io_base &= DRA7XX_CPU_TO_BUS_ADDR;
        pp->mem_base &= DRA7XX_CPU_TO_BUS_ADDR;
        pp->cfg0_base &= DRA7XX_CPU_TO_BUS_ADDR;
@@ -149,10 +138,10 @@ static void dra7xx_pcie_host_init(struct pcie_port *pp)
 
        dw_pcie_setup_rc(pp);
 
-       dra7xx_pcie_establish_link(pp);
+       dra7xx_pcie_establish_link(dra7xx);
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
-       dra7xx_pcie_enable_interrupts(pp);
+       dra7xx_pcie_enable_interrupts(dra7xx);
 }
 
 static struct pcie_host_ops dra7xx_pcie_host_ops = {
@@ -196,8 +185,8 @@ static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
 
 static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
+       struct dra7xx_pcie *dra7xx = arg;
+       struct pcie_port *pp = &dra7xx->pp;
        u32 reg;
 
        reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
@@ -223,51 +212,51 @@ static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
 static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
 {
        struct dra7xx_pcie *dra7xx = arg;
+       struct device *dev = dra7xx->pp.dev;
        u32 reg;
 
        reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
 
        if (reg & ERR_SYS)
-               dev_dbg(dra7xx->dev, "System Error\n");
+               dev_dbg(dev, "System Error\n");
 
        if (reg & ERR_FATAL)
-               dev_dbg(dra7xx->dev, "Fatal Error\n");
+               dev_dbg(dev, "Fatal Error\n");
 
        if (reg & ERR_NONFATAL)
-               dev_dbg(dra7xx->dev, "Non Fatal Error\n");
+               dev_dbg(dev, "Non Fatal Error\n");
 
        if (reg & ERR_COR)
-               dev_dbg(dra7xx->dev, "Correctable Error\n");
+               dev_dbg(dev, "Correctable Error\n");
 
        if (reg & ERR_AXI)
-               dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n");
+               dev_dbg(dev, "AXI tag lookup fatal Error\n");
 
        if (reg & ERR_ECRC)
-               dev_dbg(dra7xx->dev, "ECRC Error\n");
+               dev_dbg(dev, "ECRC Error\n");
 
        if (reg & PME_TURN_OFF)
-               dev_dbg(dra7xx->dev,
+               dev_dbg(dev,
                        "Power Management Event Turn-Off message received\n");
 
        if (reg & PME_TO_ACK)
-               dev_dbg(dra7xx->dev,
+               dev_dbg(dev,
                        "Power Management Turn-Off Ack message received\n");
 
        if (reg & PM_PME)
-               dev_dbg(dra7xx->dev,
-                       "PM Power Management Event message received\n");
+               dev_dbg(dev, "PM Power Management Event message received\n");
 
        if (reg & LINK_REQ_RST)
-               dev_dbg(dra7xx->dev, "Link Request Reset\n");
+               dev_dbg(dev, "Link Request Reset\n");
 
        if (reg & LINK_UP_EVT)
-               dev_dbg(dra7xx->dev, "Link-up state change\n");
+               dev_dbg(dev, "Link-up state change\n");
 
        if (reg & CFG_BME_EVT)
-               dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n");
+               dev_dbg(dev, "CFG 'Bus Master Enable' change\n");
 
        if (reg & CFG_MSE_EVT)
-               dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n");
+               dev_dbg(dev, "CFG 'Memory Space Enable' change\n");
 
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg);
 
@@ -278,13 +267,9 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
                                       struct platform_device *pdev)
 {
        int ret;
-       struct pcie_port *pp;
+       struct pcie_port *pp = &dra7xx->pp;
+       struct device *dev = pp->dev;
        struct resource *res;
-       struct device *dev = &pdev->dev;
-
-       pp = &dra7xx->pp;
-       pp->dev = dev;
-       pp->ops = &dra7xx_pcie_host_ops;
 
        pp->irq = platform_get_irq(pdev, 1);
        if (pp->irq < 0) {
@@ -292,12 +277,11 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
                return -EINVAL;
        }
 
-       ret = devm_request_irq(&pdev->dev, pp->irq,
-                              dra7xx_pcie_msi_irq_handler,
+       ret = devm_request_irq(dev, pp->irq, dra7xx_pcie_msi_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD,
-                              "dra7-pcie-msi", pp);
+                              "dra7-pcie-msi", dra7xx);
        if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
+               dev_err(dev, "failed to request irq\n");
                return ret;
        }
 
@@ -314,7 +298,7 @@ static int __init dra7xx_add_pcie_port(struct dra7xx_pcie *dra7xx,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(dra7xx->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -332,6 +316,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        void __iomem *base;
        struct resource *res;
        struct dra7xx_pcie *dra7xx;
+       struct pcie_port *pp;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        char name[10];
@@ -343,6 +328,10 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        if (!dra7xx)
                return -ENOMEM;
 
+       pp = &dra7xx->pp;
+       pp->dev = dev;
+       pp->ops = &dra7xx_pcie_host_ops;
+
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                dev_err(dev, "missing IRQ resource\n");
@@ -390,7 +379,6 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
 
        dra7xx->base = base;
        dra7xx->phy = phy;
-       dra7xx->dev = dev;
        dra7xx->phy_count = phy_count;
 
        pm_runtime_enable(dev);
@@ -407,7 +395,7 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
                ret = devm_gpio_request_one(dev, gpio_sel, gpio_flags,
                                            "pcie_reset");
                if (ret) {
-                       dev_err(&pdev->dev, "gpio%d request failed, ret %d\n",
+                       dev_err(dev, "gpio%d request failed, ret %d\n",
                                gpio_sel, ret);
                        goto err_gpio;
                }
@@ -420,12 +408,11 @@ static int __init dra7xx_pcie_probe(struct platform_device *pdev)
        reg &= ~LTSSM_EN;
        dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-       platform_set_drvdata(pdev, dra7xx);
-
        ret = dra7xx_add_pcie_port(dra7xx, pdev);
        if (ret < 0)
                goto err_gpio;
 
+       platform_set_drvdata(pdev, dra7xx);
        return 0;
 
 err_gpio:
@@ -451,9 +438,9 @@ static int dra7xx_pcie_suspend(struct device *dev)
        u32 val;
 
        /* clear MSE */
-       val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND);
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val &= ~PCI_COMMAND_MEMORY;
-       dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        return 0;
 }
@@ -465,9 +452,9 @@ static int dra7xx_pcie_resume(struct device *dev)
        u32 val;
 
        /* set MSE */
-       val = dra7xx_pcie_readl_rc(pp, PCI_COMMAND);
+       val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val |= PCI_COMMAND_MEMORY;
-       dra7xx_pcie_writel_rc(pp, PCI_COMMAND, val);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        return 0;
 }
index 2e2d7f0..f1c544b 100644 (file)
 #define to_exynos_pcie(x)      container_of(x, struct exynos_pcie, pp)
 
 struct exynos_pcie {
-       void __iomem            *elbi_base;
-       void __iomem            *phy_base;
-       void __iomem            *block_base;
+       struct pcie_port        pp;
+       void __iomem            *elbi_base;     /* DT 0th resource */
+       void __iomem            *phy_base;      /* DT 1st resource */
+       void __iomem            *block_base;    /* DT 2nd resource */
        int                     reset_gpio;
        struct clk              *clk;
        struct clk              *bus_clk;
-       struct pcie_port        pp;
 };
 
 /* PCIe ELBI registers */
@@ -102,40 +102,40 @@ struct exynos_pcie {
 #define PCIE_PHY_TRSV3_PD_TSV          (0x1 << 7)
 #define PCIE_PHY_TRSV3_LVCC            0x31c
 
-static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_elb_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->elbi_base + reg);
+       writel(val, exynos_pcie->elbi_base + reg);
 }
 
-static inline u32 exynos_elb_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_elb_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->elbi_base + reg);
+       return readl(exynos_pcie->elbi_base + reg);
 }
 
-static inline void exynos_phy_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_phy_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->phy_base + reg);
+       writel(val, exynos_pcie->phy_base + reg);
 }
 
-static inline u32 exynos_phy_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_phy_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->phy_base + reg);
+       return readl(exynos_pcie->phy_base + reg);
 }
 
-static inline void exynos_blk_writel(struct exynos_pcie *pcie, u32 val, u32 reg)
+static void exynos_blk_writel(struct exynos_pcie *exynos_pcie, u32 val, u32 reg)
 {
-       writel(val, pcie->block_base + reg);
+       writel(val, exynos_pcie->block_base + reg);
 }
 
-static inline u32 exynos_blk_readl(struct exynos_pcie *pcie, u32 reg)
+static u32 exynos_blk_readl(struct exynos_pcie *exynos_pcie, u32 reg)
 {
-       return readl(pcie->block_base + reg);
+       return readl(exynos_pcie->block_base + reg);
 }
 
-static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
+static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *exynos_pcie,
+                                           bool on)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        if (on) {
                val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_AWMISC);
@@ -148,10 +148,10 @@ static void exynos_pcie_sideband_dbi_w_mode(struct pcie_port *pp, bool on)
        }
 }
 
-static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
+static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *exynos_pcie,
+                                           bool on)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        if (on) {
                val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_SLV_ARMISC);
@@ -164,10 +164,9 @@ static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
        }
 }
 
-static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_core_reset(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
        val &= ~PCIE_CORE_RESET_ENABLE;
@@ -177,10 +176,9 @@ static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
        exynos_elb_writel(exynos_pcie, 0, PCIE_NONSTICKY_RESET);
 }
 
-static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
+static void exynos_pcie_deassert_core_reset(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
        val |= PCIE_CORE_RESET_ENABLE;
@@ -193,18 +191,14 @@ static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
 }
 
-static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_phy_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET);
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET);
 }
 
-static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
+static void exynos_pcie_deassert_phy_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
        exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
@@ -213,10 +207,9 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
 }
 
-static void exynos_pcie_power_on_phy(struct pcie_port *pp)
+static void exynos_pcie_power_on_phy(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
        val &= ~PCIE_PHY_COMMON_PD_CMN;
@@ -239,10 +232,9 @@ static void exynos_pcie_power_on_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
 }
 
-static void exynos_pcie_power_off_phy(struct pcie_port *pp)
+static void exynos_pcie_power_off_phy(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER);
        val |= PCIE_PHY_COMMON_PD_CMN;
@@ -265,10 +257,8 @@ static void exynos_pcie_power_off_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER);
 }
 
-static void exynos_pcie_init_phy(struct pcie_port *pp)
+static void exynos_pcie_init_phy(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-
        /* DCC feedback control off */
        exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK);
 
@@ -305,51 +295,41 @@ static void exynos_pcie_init_phy(struct pcie_port *pp)
        exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC);
 }
 
-static void exynos_pcie_assert_reset(struct pcie_port *pp)
+static void exynos_pcie_assert_reset(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
 
        if (exynos_pcie->reset_gpio >= 0)
-               devm_gpio_request_one(pp->dev, exynos_pcie->reset_gpio,
+               devm_gpio_request_one(dev, exynos_pcie->reset_gpio,
                                GPIOF_OUT_INIT_HIGH, "RESET");
 }
 
-static int exynos_pcie_establish_link(struct pcie_port *pp)
+static int exynos_pcie_establish_link(struct exynos_pcie *exynos_pcie)
 {
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
        u32 val;
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "Link already up\n");
+               dev_err(dev, "Link already up\n");
                return 0;
        }
 
-       /* assert reset signals */
-       exynos_pcie_assert_core_reset(pp);
-       exynos_pcie_assert_phy_reset(pp);
-
-       /* de-assert phy reset */
-       exynos_pcie_deassert_phy_reset(pp);
-
-       /* power on phy */
-       exynos_pcie_power_on_phy(pp);
-
-       /* initialize phy */
-       exynos_pcie_init_phy(pp);
+       exynos_pcie_assert_core_reset(exynos_pcie);
+       exynos_pcie_assert_phy_reset(exynos_pcie);
+       exynos_pcie_deassert_phy_reset(exynos_pcie);
+       exynos_pcie_power_on_phy(exynos_pcie);
+       exynos_pcie_init_phy(exynos_pcie);
 
        /* pulse for common reset */
        exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
        udelay(500);
        exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
 
-       /* de-assert core reset */
-       exynos_pcie_deassert_core_reset(pp);
-
-       /* setup root complex */
+       exynos_pcie_deassert_core_reset(exynos_pcie);
        dw_pcie_setup_rc(pp);
-
-       /* assert reset signal */
-       exynos_pcie_assert_reset(pp);
+       exynos_pcie_assert_reset(exynos_pcie);
 
        /* assert LTSSM enable */
        exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE,
@@ -361,27 +341,23 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 
        while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
                val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
-               dev_info(pp->dev, "PLL Locked: 0x%x\n", val);
+               dev_info(dev, "PLL Locked: 0x%x\n", val);
        }
-       /* power off phy */
-       exynos_pcie_power_off_phy(pp);
-
+       exynos_pcie_power_off_phy(exynos_pcie);
        return -ETIMEDOUT;
 }
 
-static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp)
+static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_PULSE);
        exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_PULSE);
 }
 
-static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
+static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *exynos_pcie)
 {
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        /* enable INTX interrupt */
        val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT |
@@ -391,23 +367,24 @@ static void exynos_pcie_enable_irq_pulse(struct pcie_port *pp)
 
 static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct exynos_pcie *exynos_pcie = arg;
 
-       exynos_pcie_clear_irq_pulse(pp);
+       exynos_pcie_clear_irq_pulse(exynos_pcie);
        return IRQ_HANDLED;
 }
 
 static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct exynos_pcie *exynos_pcie = arg;
+       struct pcie_port *pp = &exynos_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static void exynos_pcie_msi_init(struct pcie_port *pp)
+static void exynos_pcie_msi_init(struct exynos_pcie *exynos_pcie)
 {
+       struct pcie_port *pp = &exynos_pcie->pp;
        u32 val;
-       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
        dw_pcie_msi_init(pp);
 
@@ -417,60 +394,64 @@ static void exynos_pcie_msi_init(struct pcie_port *pp)
        exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL);
 }
 
-static void exynos_pcie_enable_interrupts(struct pcie_port *pp)
+static void exynos_pcie_enable_interrupts(struct exynos_pcie *exynos_pcie)
 {
-       exynos_pcie_enable_irq_pulse(pp);
+       exynos_pcie_enable_irq_pulse(exynos_pcie);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
-               exynos_pcie_msi_init(pp);
+               exynos_pcie_msi_init(exynos_pcie);
 }
 
-static inline u32 exynos_pcie_readl_rc(struct pcie_port *pp,
-                                      void __iomem *dbi_base)
+static u32 exynos_pcie_readl_rc(struct pcie_port *pp, u32 reg)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        u32 val;
 
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
-       val = readl(dbi_base);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
+       val = readl(pp->dbi_base + reg);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
        return val;
 }
 
-static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
-                                       u32 val, void __iomem *dbi_base)
+static void exynos_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
 {
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
-       writel(val, dbi_base);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
+       writel(val, pp->dbi_base + reg);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
 }
 
 static int exynos_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
                                u32 *val)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        int ret;
 
-       exynos_pcie_sideband_dbi_r_mode(pp, true);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, true);
        ret = dw_pcie_cfg_read(pp->dbi_base + where, size, val);
-       exynos_pcie_sideband_dbi_r_mode(pp, false);
+       exynos_pcie_sideband_dbi_r_mode(exynos_pcie, false);
        return ret;
 }
 
 static int exynos_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
                                u32 val)
 {
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
        int ret;
 
-       exynos_pcie_sideband_dbi_w_mode(pp, true);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, true);
        ret = dw_pcie_cfg_write(pp->dbi_base + where, size, val);
-       exynos_pcie_sideband_dbi_w_mode(pp, false);
+       exynos_pcie_sideband_dbi_w_mode(exynos_pcie, false);
        return ret;
 }
 
 static int exynos_pcie_link_up(struct pcie_port *pp)
 {
        struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
-       u32 val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
+       u32 val;
 
+       val = exynos_elb_readl(exynos_pcie, PCIE_ELBI_RDLH_LINKUP);
        if (val == PCIE_ELBI_LTSSM_ENABLE)
                return 1;
 
@@ -479,8 +460,10 @@ static int exynos_pcie_link_up(struct pcie_port *pp)
 
 static void exynos_pcie_host_init(struct pcie_port *pp)
 {
-       exynos_pcie_establish_link(pp);
-       exynos_pcie_enable_interrupts(pp);
+       struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+       exynos_pcie_establish_link(exynos_pcie);
+       exynos_pcie_enable_interrupts(exynos_pcie);
 }
 
 static struct pcie_host_ops exynos_pcie_host_ops = {
@@ -492,36 +475,38 @@ static struct pcie_host_ops exynos_pcie_host_ops = {
        .host_init = exynos_pcie_host_init,
 };
 
-static int __init exynos_add_pcie_port(struct pcie_port *pp,
+static int __init exynos_add_pcie_port(struct exynos_pcie *exynos_pcie,
                                       struct platform_device *pdev)
 {
+       struct pcie_port *pp = &exynos_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 1);
        if (!pp->irq) {
-               dev_err(&pdev->dev, "failed to get irq\n");
+               dev_err(dev, "failed to get irq\n");
                return -ENODEV;
        }
-       ret = devm_request_irq(&pdev->dev, pp->irq, exynos_pcie_irq_handler,
-                               IRQF_SHARED, "exynos-pcie", pp);
+       ret = devm_request_irq(dev, pp->irq, exynos_pcie_irq_handler,
+                               IRQF_SHARED, "exynos-pcie", exynos_pcie);
        if (ret) {
-               dev_err(&pdev->dev, "failed to request irq\n");
+               dev_err(dev, "failed to request irq\n");
                return ret;
        }
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq(pdev, 0);
                if (!pp->msi_irq) {
-                       dev_err(&pdev->dev, "failed to get msi irq\n");
+                       dev_err(dev, "failed to get msi irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                        exynos_pcie_msi_irq_handler,
                                        IRQF_SHARED | IRQF_NO_THREAD,
-                                       "exynos-pcie", pp);
+                                       "exynos-pcie", exynos_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request msi irq\n");
+                       dev_err(dev, "failed to request msi irq\n");
                        return ret;
                }
        }
@@ -531,7 +516,7 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -540,37 +525,36 @@ static int __init exynos_add_pcie_port(struct pcie_port *pp,
 
 static int __init exynos_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct exynos_pcie *exynos_pcie;
        struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource *elbi_base;
        struct resource *phy_base;
        struct resource *block_base;
        int ret;
 
-       exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie),
-                               GFP_KERNEL);
+       exynos_pcie = devm_kzalloc(dev, sizeof(*exynos_pcie), GFP_KERNEL);
        if (!exynos_pcie)
                return -ENOMEM;
 
        pp = &exynos_pcie->pp;
-
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        exynos_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 
-       exynos_pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+       exynos_pcie->clk = devm_clk_get(dev, "pcie");
        if (IS_ERR(exynos_pcie->clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie rc clock\n");
+               dev_err(dev, "Failed to get pcie rc clock\n");
                return PTR_ERR(exynos_pcie->clk);
        }
        ret = clk_prepare_enable(exynos_pcie->clk);
        if (ret)
                return ret;
 
-       exynos_pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+       exynos_pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(exynos_pcie->bus_clk)) {
-               dev_err(&pdev->dev, "Failed to get pcie bus clock\n");
+               dev_err(dev, "Failed to get pcie bus clock\n");
                ret = PTR_ERR(exynos_pcie->bus_clk);
                goto fail_clk;
        }
@@ -579,27 +563,27 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
                goto fail_clk;
 
        elbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       exynos_pcie->elbi_base = devm_ioremap_resource(&pdev->dev, elbi_base);
+       exynos_pcie->elbi_base = devm_ioremap_resource(dev, elbi_base);
        if (IS_ERR(exynos_pcie->elbi_base)) {
                ret = PTR_ERR(exynos_pcie->elbi_base);
                goto fail_bus_clk;
        }
 
        phy_base = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       exynos_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+       exynos_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
        if (IS_ERR(exynos_pcie->phy_base)) {
                ret = PTR_ERR(exynos_pcie->phy_base);
                goto fail_bus_clk;
        }
 
        block_base = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       exynos_pcie->block_base = devm_ioremap_resource(&pdev->dev, block_base);
+       exynos_pcie->block_base = devm_ioremap_resource(dev, block_base);
        if (IS_ERR(exynos_pcie->block_base)) {
                ret = PTR_ERR(exynos_pcie->block_base);
                goto fail_bus_clk;
        }
 
-       ret = exynos_add_pcie_port(pp, pdev);
+       ret = exynos_add_pcie_port(exynos_pcie, pdev);
        if (ret < 0)
                goto fail_bus_clk;
 
index ead4a5c..c8cefb0 100644 (file)
@@ -39,16 +39,15 @@ enum imx6_pcie_variants {
 };
 
 struct imx6_pcie {
+       struct pcie_port        pp;     /* pp.dbi_base is DT 0th resource */
        int                     reset_gpio;
        bool                    gpio_active_high;
        struct clk              *pcie_bus;
        struct clk              *pcie_phy;
        struct clk              *pcie_inbound_axi;
        struct clk              *pcie;
-       struct pcie_port        pp;
        struct regmap           *iomuxc_gpr;
        enum imx6_pcie_variants variant;
-       void __iomem            *mem_base;
        u32                     tx_deemph_gen1;
        u32                     tx_deemph_gen2_3p5db;
        u32                     tx_deemph_gen2_6db;
@@ -96,14 +95,15 @@ struct imx6_pcie {
 #define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 #define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
 
-static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
+static int pcie_phy_poll_ack(struct imx6_pcie *imx6_pcie, int exp_val)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val;
        u32 max_iterations = 10;
        u32 wait_counter = 0;
 
        do {
-               val = readl(dbi_base + PCIE_PHY_STAT);
+               val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
                val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
                wait_counter++;
 
@@ -116,123 +116,126 @@ static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
        return -ETIMEDOUT;
 }
 
-static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
+static int pcie_phy_wait_ack(struct imx6_pcie *imx6_pcie, int addr)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val;
        int ret;
 
        val = addr << PCIE_PHY_CTRL_DATA_LOC;
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
        val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        val = addr << PCIE_PHY_CTRL_DATA_LOC;
-       writel(val, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, val);
 
-       return pcie_phy_poll_ack(dbi_base, 0);
+       return pcie_phy_poll_ack(imx6_pcie, 0);
 }
 
 /* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
-static int pcie_phy_read(void __iomem *dbi_base, int addr, int *data)
+static int pcie_phy_read(struct imx6_pcie *imx6_pcie, int addr, int *data)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val, phy_ctl;
        int ret;
 
-       ret = pcie_phy_wait_ack(dbi_base, addr);
+       ret = pcie_phy_wait_ack(imx6_pcie, addr);
        if (ret)
                return ret;
 
        /* assert Read signal */
        phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
-       writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, phy_ctl);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
-       val = readl(dbi_base + PCIE_PHY_STAT);
+       val = dw_pcie_readl_rc(pp, PCIE_PHY_STAT);
        *data = val & 0xffff;
 
        /* deassert Read signal */
-       writel(0x00, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x00);
 
-       return pcie_phy_poll_ack(dbi_base, 0);
+       return pcie_phy_poll_ack(imx6_pcie, 0);
 }
 
-static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
+static int pcie_phy_write(struct imx6_pcie *imx6_pcie, int addr, int data)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 var;
        int ret;
 
        /* write addr */
        /* cap addr */
-       ret = pcie_phy_wait_ack(dbi_base, addr);
+       ret = pcie_phy_wait_ack(imx6_pcie, addr);
        if (ret)
                return ret;
 
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* capture data */
        var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        /* deassert cap data */
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack de-assertion */
-       ret = pcie_phy_poll_ack(dbi_base, 0);
+       ret = pcie_phy_poll_ack(imx6_pcie, 0);
        if (ret)
                return ret;
 
        /* assert wr signal */
        var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack */
-       ret = pcie_phy_poll_ack(dbi_base, 1);
+       ret = pcie_phy_poll_ack(imx6_pcie, 1);
        if (ret)
                return ret;
 
        /* deassert wr signal */
        var = data << PCIE_PHY_CTRL_DATA_LOC;
-       writel(var, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, var);
 
        /* wait for ack de-assertion */
-       ret = pcie_phy_poll_ack(dbi_base, 0);
+       ret = pcie_phy_poll_ack(imx6_pcie, 0);
        if (ret)
                return ret;
 
-       writel(0x0, dbi_base + PCIE_PHY_CTRL);
+       dw_pcie_writel_rc(pp, PCIE_PHY_CTRL, 0x0);
 
        return 0;
 }
 
-static void imx6_pcie_reset_phy(struct pcie_port *pp)
+static void imx6_pcie_reset_phy(struct imx6_pcie *imx6_pcie)
 {
        u32 tmp;
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
        tmp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN |
                PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+       pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
 
        usleep_range(2000, 3000);
 
-       pcie_phy_read(pp->dbi_base, PHY_RX_OVRD_IN_LO, &tmp);
+       pcie_phy_read(imx6_pcie, PHY_RX_OVRD_IN_LO, &tmp);
        tmp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN |
                  PHY_RX_OVRD_IN_LO_RX_PLL_EN);
-       pcie_phy_write(pp->dbi_base, PHY_RX_OVRD_IN_LO, tmp);
+       pcie_phy_write(imx6_pcie, PHY_RX_OVRD_IN_LO, tmp);
 }
 
 /*  Added for PCI abort handling */
@@ -242,9 +245,9 @@ static int imx6q_pcie_abort_handler(unsigned long addr,
        return 0;
 }
 
-static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
+static void imx6_pcie_assert_core_reset(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
        u32 val, gpr1, gpr12;
 
        switch (imx6_pcie->variant) {
@@ -281,10 +284,10 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
 
                if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
                    (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
-                       val = readl(pp->dbi_base + PCIE_PL_PFLR);
+                       val = dw_pcie_readl_rc(pp, PCIE_PL_PFLR);
                        val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
                        val |= PCIE_PL_PFLR_FORCE_LINK;
-                       writel(val, pp->dbi_base + PCIE_PL_PFLR);
+                       dw_pcie_writel_rc(pp, PCIE_PL_PFLR, val);
 
                        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                           IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
@@ -296,20 +299,19 @@ static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
                                   IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
                break;
        }
-
-       return 0;
 }
 
 static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
 {
        struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret = 0;
 
        switch (imx6_pcie->variant) {
        case IMX6SX:
                ret = clk_prepare_enable(imx6_pcie->pcie_inbound_axi);
                if (ret) {
-                       dev_err(pp->dev, "unable to enable pcie_axi clock\n");
+                       dev_err(dev, "unable to enable pcie_axi clock\n");
                        break;
                }
 
@@ -336,32 +338,33 @@ static int imx6_pcie_enable_ref_clk(struct imx6_pcie *imx6_pcie)
        return ret;
 }
 
-static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
+static void imx6_pcie_deassert_core_reset(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        ret = clk_prepare_enable(imx6_pcie->pcie_phy);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie_phy clock\n");
-               goto err_pcie_phy;
+               dev_err(dev, "unable to enable pcie_phy clock\n");
+               return;
        }
 
        ret = clk_prepare_enable(imx6_pcie->pcie_bus);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie_bus clock\n");
+               dev_err(dev, "unable to enable pcie_bus clock\n");
                goto err_pcie_bus;
        }
 
        ret = clk_prepare_enable(imx6_pcie->pcie);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie clock\n");
+               dev_err(dev, "unable to enable pcie clock\n");
                goto err_pcie;
        }
 
        ret = imx6_pcie_enable_ref_clk(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "unable to enable pcie ref clock\n");
+               dev_err(dev, "unable to enable pcie ref clock\n");
                goto err_ref_clk;
        }
 
@@ -392,7 +395,7 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
                break;
        }
 
-       return 0;
+       return;
 
 err_ref_clk:
        clk_disable_unprepare(imx6_pcie->pcie);
@@ -400,14 +403,10 @@ err_pcie:
        clk_disable_unprepare(imx6_pcie->pcie_bus);
 err_pcie_bus:
        clk_disable_unprepare(imx6_pcie->pcie_phy);
-err_pcie_phy:
-       return ret;
 }
 
-static void imx6_pcie_init_phy(struct pcie_port *pp)
+static void imx6_pcie_init_phy(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
-
        if (imx6_pcie->variant == IMX6SX)
                regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                                   IMX6SX_GPR12_PCIE_RX_EQ_MASK,
@@ -439,45 +438,52 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
                           imx6_pcie->tx_swing_low << 25);
 }
 
-static int imx6_pcie_wait_for_link(struct pcie_port *pp)
+static int imx6_pcie_wait_for_link(struct imx6_pcie *imx6_pcie)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
+
        /* check if the link is up or not */
        if (!dw_pcie_wait_for_link(pp))
                return 0;
 
-       dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+       dev_dbg(dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
        return -ETIMEDOUT;
 }
 
-static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
+static int imx6_pcie_wait_for_speed_change(struct imx6_pcie *imx6_pcie)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        u32 tmp;
        unsigned int retries;
 
        for (retries = 0; retries < 200; retries++) {
-               tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+               tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
                /* Test if the speed change finished. */
                if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
                        return 0;
                usleep_range(100, 1000);
        }
 
-       dev_err(pp->dev, "Speed change timeout\n");
+       dev_err(dev, "Speed change timeout\n");
        return -EINVAL;
 }
 
 static irqreturn_t imx6_pcie_msi_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct imx6_pcie *imx6_pcie = arg;
+       struct pcie_port *pp = &imx6_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static int imx6_pcie_establish_link(struct pcie_port *pp)
+static int imx6_pcie_establish_link(struct imx6_pcie *imx6_pcie)
 {
-       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        u32 tmp;
        int ret;
 
@@ -486,76 +492,73 @@ static int imx6_pcie_establish_link(struct pcie_port *pp)
         * started in Gen2 mode, there is a possibility the devices on the
         * bus will not be detected at all.  This happens with PCIe switches.
         */
-       tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+       tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
        tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
        tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
-       writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+       dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
 
        /* Start LTSSM. */
        regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
                        IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 
-       ret = imx6_pcie_wait_for_link(pp);
+       ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret) {
-               dev_info(pp->dev, "Link never came up\n");
+               dev_info(dev, "Link never came up\n");
                goto err_reset_phy;
        }
 
        if (imx6_pcie->link_gen == 2) {
                /* Allow Gen2 mode after the link is up. */
-               tmp = readl(pp->dbi_base + PCIE_RC_LCR);
+               tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCR);
                tmp &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
                tmp |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
-               writel(tmp, pp->dbi_base + PCIE_RC_LCR);
+               dw_pcie_writel_rc(pp, PCIE_RC_LCR, tmp);
        } else {
-               dev_info(pp->dev, "Link: Gen2 disabled\n");
+               dev_info(dev, "Link: Gen2 disabled\n");
        }
 
        /*
         * Start Directed Speed Change so the best possible speed both link
         * partners support can be negotiated.
         */
-       tmp = readl(pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       tmp = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
        tmp |= PORT_LOGIC_SPEED_CHANGE;
-       writel(tmp, pp->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, tmp);
 
-       ret = imx6_pcie_wait_for_speed_change(pp);
+       ret = imx6_pcie_wait_for_speed_change(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "Failed to bring link up!\n");
+               dev_err(dev, "Failed to bring link up!\n");
                goto err_reset_phy;
        }
 
        /* Make sure link training is finished as well! */
-       ret = imx6_pcie_wait_for_link(pp);
+       ret = imx6_pcie_wait_for_link(imx6_pcie);
        if (ret) {
-               dev_err(pp->dev, "Failed to bring link up!\n");
+               dev_err(dev, "Failed to bring link up!\n");
                goto err_reset_phy;
        }
 
-       tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
-       dev_info(pp->dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
+       tmp = dw_pcie_readl_rc(pp, PCIE_RC_LCSR);
+       dev_info(dev, "Link up, Gen%i\n", (tmp >> 16) & 0xf);
        return 0;
 
 err_reset_phy:
-       dev_dbg(pp->dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
-       imx6_pcie_reset_phy(pp);
-
+       dev_dbg(dev, "PHY DEBUG_R0=0x%08x DEBUG_R1=0x%08x\n",
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
+       imx6_pcie_reset_phy(imx6_pcie);
        return ret;
 }
 
 static void imx6_pcie_host_init(struct pcie_port *pp)
 {
-       imx6_pcie_assert_core_reset(pp);
-
-       imx6_pcie_init_phy(pp);
-
-       imx6_pcie_deassert_core_reset(pp);
+       struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 
+       imx6_pcie_assert_core_reset(imx6_pcie);
+       imx6_pcie_init_phy(imx6_pcie);
+       imx6_pcie_deassert_core_reset(imx6_pcie);
        dw_pcie_setup_rc(pp);
-
-       imx6_pcie_establish_link(pp);
+       imx6_pcie_establish_link(imx6_pcie);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
@@ -563,7 +566,7 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
 
 static int imx6_pcie_link_up(struct pcie_port *pp)
 {
-       return readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) &
+       return dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1) &
                        PCIE_PHY_DEBUG_R1_XMLH_LINK_UP;
 }
 
@@ -572,24 +575,26 @@ static struct pcie_host_ops imx6_pcie_host_ops = {
        .host_init = imx6_pcie_host_init,
 };
 
-static int __init imx6_add_pcie_port(struct pcie_port *pp,
-                       struct platform_device *pdev)
+static int __init imx6_add_pcie_port(struct imx6_pcie *imx6_pcie,
+                                    struct platform_device *pdev)
 {
+       struct pcie_port *pp = &imx6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq_byname(pdev, "msi");
                if (pp->msi_irq <= 0) {
-                       dev_err(&pdev->dev, "failed to get MSI irq\n");
+                       dev_err(dev, "failed to get MSI irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                       imx6_pcie_msi_handler,
                                       IRQF_SHARED | IRQF_NO_THREAD,
-                                      "mx6-pcie-msi", pp);
+                                      "mx6-pcie-msi", imx6_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI irq\n");
+                       dev_err(dev, "failed to request MSI irq\n");
                        return ret;
                }
        }
@@ -599,7 +604,7 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -608,75 +613,72 @@ static int __init imx6_add_pcie_port(struct pcie_port *pp,
 
 static int __init imx6_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct imx6_pcie *imx6_pcie;
        struct pcie_port *pp;
-       struct device_node *np = pdev->dev.of_node;
        struct resource *dbi_base;
-       struct device_node *node = pdev->dev.of_node;
+       struct device_node *node = dev->of_node;
        int ret;
 
-       imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
+       imx6_pcie = devm_kzalloc(dev, sizeof(*imx6_pcie), GFP_KERNEL);
        if (!imx6_pcie)
                return -ENOMEM;
 
        pp = &imx6_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        imx6_pcie->variant =
-               (enum imx6_pcie_variants)of_device_get_match_data(&pdev->dev);
+               (enum imx6_pcie_variants)of_device_get_match_data(dev);
 
        /* Added for PCI abort handling */
        hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
                "imprecise external abort");
 
        dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+       pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
        /* Fetch GPIOs */
-       imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
-       imx6_pcie->gpio_active_high = of_property_read_bool(np,
+       imx6_pcie->reset_gpio = of_get_named_gpio(node, "reset-gpio", 0);
+       imx6_pcie->gpio_active_high = of_property_read_bool(node,
                                                "reset-gpio-active-high");
        if (gpio_is_valid(imx6_pcie->reset_gpio)) {
-               ret = devm_gpio_request_one(&pdev->dev, imx6_pcie->reset_gpio,
+               ret = devm_gpio_request_one(dev, imx6_pcie->reset_gpio,
                                imx6_pcie->gpio_active_high ?
                                        GPIOF_OUT_INIT_HIGH :
                                        GPIOF_OUT_INIT_LOW,
                                "PCIe reset");
                if (ret) {
-                       dev_err(&pdev->dev, "unable to get reset gpio\n");
+                       dev_err(dev, "unable to get reset gpio\n");
                        return ret;
                }
        }
 
        /* Fetch clocks */
-       imx6_pcie->pcie_phy = devm_clk_get(&pdev->dev, "pcie_phy");
+       imx6_pcie->pcie_phy = devm_clk_get(dev, "pcie_phy");
        if (IS_ERR(imx6_pcie->pcie_phy)) {
-               dev_err(&pdev->dev,
-                       "pcie_phy clock source missing or invalid\n");
+               dev_err(dev, "pcie_phy clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie_phy);
        }
 
-       imx6_pcie->pcie_bus = devm_clk_get(&pdev->dev, "pcie_bus");
+       imx6_pcie->pcie_bus = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(imx6_pcie->pcie_bus)) {
-               dev_err(&pdev->dev,
-                       "pcie_bus clock source missing or invalid\n");
+               dev_err(dev, "pcie_bus clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie_bus);
        }
 
-       imx6_pcie->pcie = devm_clk_get(&pdev->dev, "pcie");
+       imx6_pcie->pcie = devm_clk_get(dev, "pcie");
        if (IS_ERR(imx6_pcie->pcie)) {
-               dev_err(&pdev->dev,
-                       "pcie clock source missing or invalid\n");
+               dev_err(dev, "pcie clock source missing or invalid\n");
                return PTR_ERR(imx6_pcie->pcie);
        }
 
        if (imx6_pcie->variant == IMX6SX) {
-               imx6_pcie->pcie_inbound_axi = devm_clk_get(&pdev->dev,
+               imx6_pcie->pcie_inbound_axi = devm_clk_get(dev,
                                                           "pcie_inbound_axi");
                if (IS_ERR(imx6_pcie->pcie_inbound_axi)) {
-                       dev_err(&pdev->dev,
+                       dev_err(dev,
                                "pcie_incbound_axi clock missing or invalid\n");
                        return PTR_ERR(imx6_pcie->pcie_inbound_axi);
                }
@@ -686,7 +688,7 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
        imx6_pcie->iomuxc_gpr =
                 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
        if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
-               dev_err(&pdev->dev, "unable to find iomuxc registers\n");
+               dev_err(dev, "unable to find iomuxc registers\n");
                return PTR_ERR(imx6_pcie->iomuxc_gpr);
        }
 
@@ -712,12 +714,12 @@ static int __init imx6_pcie_probe(struct platform_device *pdev)
                imx6_pcie->tx_swing_low = 127;
 
        /* Limit link speed */
-       ret = of_property_read_u32(pp->dev->of_node, "fsl,max-link-speed",
+       ret = of_property_read_u32(node, "fsl,max-link-speed",
                                   &imx6_pcie->link_gen);
        if (ret)
                imx6_pcie->link_gen = 1;
 
-       ret = imx6_add_pcie_port(pp, pdev);
+       ret = imx6_add_pcie_port(imx6_pcie, pdev);
        if (ret < 0)
                return ret;
 
@@ -730,7 +732,7 @@ static void imx6_pcie_shutdown(struct platform_device *pdev)
        struct imx6_pcie *imx6_pcie = platform_get_drvdata(pdev);
 
        /* bring down link, so bootloader gets clean state in case of reboot */
-       imx6_pcie_assert_core_reset(&imx6_pcie->pp);
+       imx6_pcie_assert_core_reset(imx6_pcie);
 }
 
 static const struct of_device_id imx6_pcie_of_match[] = {
index 4151509..9397c46 100644 (file)
@@ -88,13 +88,24 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp)
        return ks_pcie->app.start + MSI_IRQ;
 }
 
+static u32 ks_dw_app_readl(struct keystone_pcie *ks_pcie, u32 offset)
+{
+       return readl(ks_pcie->va_app_base + offset);
+}
+
+static void ks_dw_app_writel(struct keystone_pcie *ks_pcie, u32 offset, u32 val)
+{
+       writel(val, ks_pcie->va_app_base + offset);
+}
+
 void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 pending, vector;
        int src, virq;
 
-       pending = readl(ks_pcie->va_app_base + MSI0_IRQ_STATUS + (offset << 4));
+       pending = ks_dw_app_readl(ks_pcie, MSI0_IRQ_STATUS + (offset << 4));
 
        /*
         * MSI0 status bit 0-3 shows vectors 0, 8, 16, 24, MSI1 status bit
@@ -104,7 +115,7 @@ void ks_dw_pcie_handle_msi_irq(struct keystone_pcie *ks_pcie, int offset)
                if (BIT(src) & pending) {
                        vector = offset + (src << 3);
                        virq = irq_linear_revmap(pp->irq_domain, vector);
-                       dev_dbg(pp->dev, "irq: bit %d, vector %d, virq %d\n",
+                       dev_dbg(dev, "irq: bit %d, vector %d, virq %d\n",
                                src, vector, virq);
                        generic_handle_irq(virq);
                }
@@ -124,9 +135,9 @@ static void ks_dw_pcie_msi_irq_ack(struct irq_data *d)
        offset = d->irq - irq_linear_revmap(pp->irq_domain, 0);
        update_reg_offset_bit_pos(offset, &reg_offset, &bit_pos);
 
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_STATUS + (reg_offset << 4));
-       writel(reg_offset + MSI_IRQ_OFFSET, ks_pcie->va_app_base + IRQ_EOI);
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_STATUS + (reg_offset << 4),
+                        BIT(bit_pos));
+       ks_dw_app_writel(ks_pcie, IRQ_EOI, reg_offset + MSI_IRQ_OFFSET);
 }
 
 void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
@@ -135,8 +146,8 @@ void ks_dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_SET + (reg_offset << 4));
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_SET + (reg_offset << 4),
+                        BIT(bit_pos));
 }
 
 void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
@@ -145,8 +156,8 @@ void ks_dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        update_reg_offset_bit_pos(irq, &reg_offset, &bit_pos);
-       writel(BIT(bit_pos),
-              ks_pcie->va_app_base + MSI0_IRQ_ENABLE_CLR + (reg_offset << 4));
+       ks_dw_app_writel(ks_pcie, MSI0_IRQ_ENABLE_CLR + (reg_offset << 4),
+                        BIT(bit_pos));
 }
 
 static void ks_dw_pcie_msi_irq_mask(struct irq_data *d)
@@ -215,6 +226,7 @@ static const struct irq_domain_ops ks_dw_pcie_msi_domain_ops = {
 int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
 {
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
+       struct device *dev = pp->dev;
        int i;
 
        pp->irq_domain = irq_domain_add_linear(ks_pcie->msi_intc_np,
@@ -222,7 +234,7 @@ int ks_dw_pcie_msi_host_init(struct pcie_port *pp, struct msi_controller *chip)
                                        &ks_dw_pcie_msi_domain_ops,
                                        chip);
        if (!pp->irq_domain) {
-               dev_err(pp->dev, "irq domain init failed\n");
+               dev_err(dev, "irq domain init failed\n");
                return -ENXIO;
        }
 
@@ -237,47 +249,47 @@ void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie)
        int i;
 
        for (i = 0; i < MAX_LEGACY_IRQS; i++)
-               writel(0x1, ks_pcie->va_app_base + IRQ_ENABLE_SET + (i << 4));
+               ks_dw_app_writel(ks_pcie, IRQ_ENABLE_SET + (i << 4), 0x1);
 }
 
 void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 pending;
        int virq;
 
-       pending = readl(ks_pcie->va_app_base + IRQ_STATUS + (offset << 4));
+       pending = ks_dw_app_readl(ks_pcie, IRQ_STATUS + (offset << 4));
 
        if (BIT(0) & pending) {
                virq = irq_linear_revmap(ks_pcie->legacy_irq_domain, offset);
-               dev_dbg(pp->dev, ": irq: irq_offset %d, virq %d\n", offset,
-                       virq);
+               dev_dbg(dev, ": irq: irq_offset %d, virq %d\n", offset, virq);
                generic_handle_irq(virq);
        }
 
        /* EOI the INTx interrupt */
-       writel(offset, ks_pcie->va_app_base + IRQ_EOI);
+       ks_dw_app_writel(ks_pcie, IRQ_EOI, offset);
 }
 
-void ks_dw_pcie_enable_error_irq(void __iomem *reg_base)
+void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie)
 {
-       writel(ERR_IRQ_ALL, reg_base + ERR_IRQ_ENABLE_SET);
+       ks_dw_app_writel(ks_pcie, ERR_IRQ_ENABLE_SET, ERR_IRQ_ALL);
 }
 
-irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
-                                       void __iomem *reg_base)
+irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie)
 {
        u32 status;
 
-       status = readl(reg_base + ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL;
+       status = ks_dw_app_readl(ks_pcie, ERR_IRQ_STATUS_RAW) & ERR_IRQ_ALL;
        if (!status)
                return IRQ_NONE;
 
        if (status & ERR_FATAL_IRQ)
-               dev_err(dev, "fatal error (status %#010x)\n", status);
+               dev_err(ks_pcie->pp.dev, "fatal error (status %#010x)\n",
+                       status);
 
        /* Ack the IRQ; status bits are RW1C */
-       writel(status, reg_base + ERR_IRQ_STATUS);
+       ks_dw_app_writel(ks_pcie, ERR_IRQ_STATUS, status);
        return IRQ_HANDLED;
 }
 
@@ -322,15 +334,15 @@ static const struct irq_domain_ops ks_dw_pcie_legacy_irq_domain_ops = {
  * Since modification of dbi_cs2 involves different clock domain, read the
  * status back to ensure the transition is complete.
  */
-static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
+static void ks_dw_pcie_set_dbi_mode(struct keystone_pcie *ks_pcie)
 {
        u32 val;
 
-       writel(DBI_CS2_EN_VAL | readl(reg_virt + CMD_STATUS),
-              reg_virt + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, DBI_CS2_EN_VAL | val);
 
        do {
-               val = readl(reg_virt + CMD_STATUS);
+               val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        } while (!(val & DBI_CS2_EN_VAL));
 }
 
@@ -340,15 +352,15 @@ static void ks_dw_pcie_set_dbi_mode(void __iomem *reg_virt)
  * Since modification of dbi_cs2 involves different clock domain, read the
  * status back to ensure the transition is complete.
  */
-static void ks_dw_pcie_clear_dbi_mode(void __iomem *reg_virt)
+static void ks_dw_pcie_clear_dbi_mode(struct keystone_pcie *ks_pcie)
 {
        u32 val;
 
-       writel(~DBI_CS2_EN_VAL & readl(reg_virt + CMD_STATUS),
-                    reg_virt + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, ~DBI_CS2_EN_VAL & val);
 
        do {
-               val = readl(reg_virt + CMD_STATUS);
+               val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        } while (val & DBI_CS2_EN_VAL);
 }
 
@@ -357,28 +369,29 @@ void ks_dw_pcie_setup_rc_app_regs(struct keystone_pcie *ks_pcie)
        struct pcie_port *pp = &ks_pcie->pp;
        u32 start = pp->mem->start, end = pp->mem->end;
        int i, tr_size;
+       u32 val;
 
        /* Disable BARs for inbound access */
-       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
-       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_0);
-       writel(0, pp->dbi_base + PCI_BASE_ADDRESS_1);
-       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_set_dbi_mode(ks_pcie);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0);
+       ks_dw_pcie_clear_dbi_mode(ks_pcie);
 
        /* Set outbound translation size per window division */
-       writel(CFG_PCIM_WIN_SZ_IDX & 0x7, ks_pcie->va_app_base + OB_SIZE);
+       ks_dw_app_writel(ks_pcie, OB_SIZE, CFG_PCIM_WIN_SZ_IDX & 0x7);
 
        tr_size = (1 << (CFG_PCIM_WIN_SZ_IDX & 0x7)) * SZ_1M;
 
        /* Using Direct 1:1 mapping of RC <-> PCI memory space */
        for (i = 0; (i < CFG_PCIM_WIN_CNT) && (start < end); i++) {
-               writel(start | 1, ks_pcie->va_app_base + OB_OFFSET_INDEX(i));
-               writel(0, ks_pcie->va_app_base + OB_OFFSET_HI(i));
+               ks_dw_app_writel(ks_pcie, OB_OFFSET_INDEX(i), start | 1);
+               ks_dw_app_writel(ks_pcie, OB_OFFSET_HI(i), 0);
                start += tr_size;
        }
 
        /* Enable OB translation */
-       writel(OB_XLAT_EN_VAL | readl(ks_pcie->va_app_base + CMD_STATUS),
-              ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, OB_XLAT_EN_VAL | val);
 }
 
 /**
@@ -418,7 +431,7 @@ static void __iomem *ks_pcie_cfg_setup(struct keystone_pcie *ks_pcie, u8 bus,
        if (bus != 1)
                regval |= BIT(24);
 
-       writel(regval, ks_pcie->va_app_base + CFG_SETUP);
+       ks_dw_app_writel(ks_pcie, CFG_SETUP, regval);
        return pp->va_cfg0_base;
 }
 
@@ -456,19 +469,19 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
        struct keystone_pcie *ks_pcie = to_keystone_pcie(pp);
 
        /* Configure and set up BAR0 */
-       ks_dw_pcie_set_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_set_dbi_mode(ks_pcie);
 
        /* Enable BAR0 */
-       writel(1, pp->dbi_base + PCI_BASE_ADDRESS_0);
-       writel(SZ_4K - 1, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 1);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, SZ_4K - 1);
 
-       ks_dw_pcie_clear_dbi_mode(ks_pcie->va_app_base);
+       ks_dw_pcie_clear_dbi_mode(ks_pcie);
 
         /*
          * For BAR0, just setting bus address for inbound writes (MSI) should
          * be sufficient.  Use physical address to avoid any conflicts.
          */
-       writel(ks_pcie->app.start, pp->dbi_base + PCI_BASE_ADDRESS_0);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, ks_pcie->app.start);
 }
 
 /**
@@ -476,8 +489,9 @@ void ks_dw_pcie_v3_65_scan_bus(struct pcie_port *pp)
  */
 int ks_dw_pcie_link_up(struct pcie_port *pp)
 {
-       u32 val = readl(pp->dbi_base + DEBUG0);
+       u32 val;
 
+       val = dw_pcie_readl_rc(pp, DEBUG0);
        return (val & LTSSM_STATE_MASK) == LTSSM_STATE_L0;
 }
 
@@ -486,13 +500,13 @@ void ks_dw_pcie_initiate_link_train(struct keystone_pcie *ks_pcie)
        u32 val;
 
        /* Disable Link training */
-       val = readl(ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
        val &= ~LTSSM_EN_VAL;
-       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);
 
        /* Initiate Link Training */
-       val = readl(ks_pcie->va_app_base + CMD_STATUS);
-       writel(LTSSM_EN_VAL | val,  ks_pcie->va_app_base + CMD_STATUS);
+       val = ks_dw_app_readl(ks_pcie, CMD_STATUS);
+       ks_dw_app_writel(ks_pcie, CMD_STATUS, LTSSM_EN_VAL | val);
 }
 
 /**
@@ -506,12 +520,13 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                                struct device_node *msi_intc_np)
 {
        struct pcie_port *pp = &ks_pcie->pp;
-       struct platform_device *pdev = to_platform_device(pp->dev);
+       struct device *dev = pp->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct resource *res;
 
        /* Index 0 is the config reg. space address */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       pp->dbi_base = devm_ioremap_resource(pp->dev, res);
+       pp->dbi_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
@@ -524,7 +539,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
 
        /* Index 1 is the application reg. space address */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       ks_pcie->va_app_base = devm_ioremap_resource(pp->dev, res);
+       ks_pcie->va_app_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(ks_pcie->va_app_base))
                return PTR_ERR(ks_pcie->va_app_base);
 
@@ -537,7 +552,7 @@ int __init ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                                        &ks_dw_pcie_legacy_irq_domain_ops,
                                        NULL);
        if (!ks_pcie->legacy_irq_domain) {
-               dev_err(pp->dev, "Failed to add irq domain for legacy irqs\n");
+               dev_err(dev, "Failed to add irq domain for legacy irqs\n");
                return -EINVAL;
        }
 
index 82b461b..043c19a 100644 (file)
@@ -89,12 +89,13 @@ DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, quirk_limit_mrrs);
 static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        unsigned int retries;
 
        dw_pcie_setup_rc(pp);
 
        if (dw_pcie_link_up(pp)) {
-               dev_err(pp->dev, "Link already up\n");
+               dev_err(dev, "Link already up\n");
                return 0;
        }
 
@@ -105,7 +106,7 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie)
                        return 0;
        }
 
-       dev_err(pp->dev, "phy link never came up\n");
+       dev_err(dev, "phy link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -115,9 +116,10 @@ static void ks_pcie_msi_irq_handler(struct irq_desc *desc)
        struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
        u32 offset = irq - ks_pcie->msi_host_irqs[0];
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        struct irq_chip *chip = irq_desc_get_chip(desc);
 
-       dev_dbg(pp->dev, "%s, irq %d\n", __func__, irq);
+       dev_dbg(dev, "%s, irq %d\n", __func__, irq);
 
        /*
         * The chained irq handler installation would have replaced normal
@@ -142,10 +144,11 @@ static void ks_pcie_legacy_irq_handler(struct irq_desc *desc)
        unsigned int irq = irq_desc_get_irq(desc);
        struct keystone_pcie *ks_pcie = irq_desc_get_handler_data(desc);
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        u32 irq_offset = irq - ks_pcie->legacy_host_irqs[0];
        struct irq_chip *chip = irq_desc_get_chip(desc);
 
-       dev_dbg(pp->dev, ": Handling legacy irq %d\n", irq);
+       dev_dbg(dev, ": Handling legacy irq %d\n", irq);
 
        /*
         * The chained irq handler installation would have replaced normal
@@ -234,7 +237,7 @@ static void ks_pcie_setup_interrupts(struct keystone_pcie *ks_pcie)
        }
 
        if (ks_pcie->error_irq > 0)
-               ks_dw_pcie_enable_error_irq(ks_pcie->va_app_base);
+               ks_dw_pcie_enable_error_irq(ks_pcie);
 }
 
 /*
@@ -302,14 +305,14 @@ static irqreturn_t pcie_err_irq_handler(int irq, void *priv)
 {
        struct keystone_pcie *ks_pcie = priv;
 
-       return ks_dw_pcie_handle_error_irq(ks_pcie->pp.dev,
-                                          ks_pcie->va_app_base);
+       return ks_dw_pcie_handle_error_irq(ks_pcie);
 }
 
 static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
                         struct platform_device *pdev)
 {
        struct pcie_port *pp = &ks_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        ret = ks_pcie_get_irq_controller_info(ks_pcie,
@@ -332,12 +335,12 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
         */
        ks_pcie->error_irq = irq_of_parse_and_map(ks_pcie->np, 0);
        if (ks_pcie->error_irq <= 0)
-               dev_info(&pdev->dev, "no error IRQ defined\n");
+               dev_info(dev, "no error IRQ defined\n");
        else {
                ret = request_irq(ks_pcie->error_irq, pcie_err_irq_handler,
                                  IRQF_SHARED, "pcie-error-irq", ks_pcie);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to request error IRQ %d\n",
+                       dev_err(dev, "failed to request error IRQ %d\n",
                                ks_pcie->error_irq);
                        return ret;
                }
@@ -347,7 +350,7 @@ static int __init ks_add_pcie_port(struct keystone_pcie *ks_pcie,
        pp->ops = &keystone_pcie_host_ops;
        ret = ks_dw_pcie_host_init(ks_pcie, ks_pcie->msi_intc_np);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -381,12 +384,12 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
        struct phy *phy;
        int ret;
 
-       ks_pcie = devm_kzalloc(&pdev->dev, sizeof(*ks_pcie),
-                               GFP_KERNEL);
+       ks_pcie = devm_kzalloc(dev, sizeof(*ks_pcie), GFP_KERNEL);
        if (!ks_pcie)
                return -ENOMEM;
 
        pp = &ks_pcie->pp;
+       pp->dev = dev;
 
        /* initialize SerDes Phy if present */
        phy = devm_phy_get(dev, "pcie-phy");
@@ -408,7 +411,6 @@ static int __init ks_pcie_probe(struct platform_device *pdev)
        devm_iounmap(dev, reg_p);
        devm_release_mem_region(dev, res->start, resource_size(res));
 
-       pp->dev = dev;
        ks_pcie->np = dev->of_node;
        platform_set_drvdata(pdev, ks_pcie);
        ks_pcie->clk = devm_clk_get(dev, "pcie");
index a5b0cb2..bc54baf 100644 (file)
@@ -17,8 +17,8 @@
 #define MAX_LEGACY_HOST_IRQS           4
 
 struct keystone_pcie {
+       struct  pcie_port       pp;             /* pp.dbi_base is DT 0th res */
        struct  clk             *clk;
-       struct  pcie_port       pp;
        /* PCI Device ID */
        u32                     device_id;
        int                     num_legacy_host_irqs;
@@ -34,7 +34,7 @@ struct keystone_pcie {
        int error_irq;
 
        /* Application register space */
-       void __iomem            *va_app_base;
+       void __iomem            *va_app_base;   /* DT 1st resource */
        struct resource         app;
 };
 
@@ -45,9 +45,8 @@ phys_addr_t ks_dw_pcie_get_msi_addr(struct pcie_port *pp);
 /* Keystone specific PCI controller APIs */
 void ks_dw_pcie_enable_legacy_irqs(struct keystone_pcie *ks_pcie);
 void ks_dw_pcie_handle_legacy_irq(struct keystone_pcie *ks_pcie, int offset);
-void ks_dw_pcie_enable_error_irq(void __iomem *reg_base);
-irqreturn_t ks_dw_pcie_handle_error_irq(struct device *dev,
-                                       void __iomem *reg_base);
+void ks_dw_pcie_enable_error_irq(struct keystone_pcie *ks_pcie);
+irqreturn_t ks_dw_pcie_handle_error_irq(struct keystone_pcie *ks_pcie);
 int  ks_dw_pcie_host_init(struct keystone_pcie *ks_pcie,
                        struct device_node *msi_intc_np);
 int ks_dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
index 114ba81..2cb7315 100644 (file)
@@ -45,10 +45,9 @@ struct ls_pcie_drvdata {
 };
 
 struct ls_pcie {
-       void __iomem *dbi;
+       struct pcie_port pp;            /* pp.dbi_base is DT regs */
        void __iomem *lut;
        struct regmap *scfg;
-       struct pcie_port pp;
        const struct ls_pcie_drvdata *drvdata;
        int index;
 };
@@ -59,7 +58,7 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
 {
        u32 header_type;
 
-       header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE);
+       header_type = ioread8(pcie->pp.dbi_base + PCI_HEADER_TYPE);
        header_type &= 0x7f;
 
        return header_type == PCI_HEADER_TYPE_BRIDGE;
@@ -68,13 +67,13 @@ static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
 /* Clear multi-function bit */
 static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
 {
-       iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE);
+       iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->pp.dbi_base + PCI_HEADER_TYPE);
 }
 
 /* Fix class value */
 static void ls_pcie_fix_class(struct ls_pcie *pcie)
 {
-       iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE);
+       iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->pp.dbi_base + PCI_CLASS_DEVICE);
 }
 
 /* Drop MSG TLP except for Vendor MSG */
@@ -82,9 +81,9 @@ static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
 {
        u32 val;
 
-       val = ioread32(pcie->dbi + PCIE_STRFMR1);
+       val = ioread32(pcie->pp.dbi_base + PCIE_STRFMR1);
        val &= 0xDFFFFFFF;
-       iowrite32(val, pcie->dbi + PCIE_STRFMR1);
+       iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1);
 }
 
 static int ls1021_pcie_link_up(struct pcie_port *pp)
@@ -106,18 +105,19 @@ static int ls1021_pcie_link_up(struct pcie_port *pp)
 
 static void ls1021_pcie_host_init(struct pcie_port *pp)
 {
+       struct device *dev = pp->dev;
        struct ls_pcie *pcie = to_ls_pcie(pp);
        u32 index[2];
 
-       pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node,
+       pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
                                                     "fsl,pcie-scfg");
        if (IS_ERR(pcie->scfg)) {
-               dev_err(pp->dev, "No syscfg phandle specified\n");
+               dev_err(dev, "No syscfg phandle specified\n");
                pcie->scfg = NULL;
                return;
        }
 
-       if (of_property_read_u32_array(pp->dev->of_node,
+       if (of_property_read_u32_array(dev->of_node,
                                       "fsl,pcie-scfg", index, 2)) {
                pcie->scfg = NULL;
                return;
@@ -148,18 +148,19 @@ static void ls_pcie_host_init(struct pcie_port *pp)
 {
        struct ls_pcie *pcie = to_ls_pcie(pp);
 
-       iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN);
+       iowrite32(1, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
        ls_pcie_fix_class(pcie);
        ls_pcie_clear_multifunction(pcie);
        ls_pcie_drop_msg_tlp(pcie);
-       iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN);
+       iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
 }
 
 static int ls_pcie_msi_host_init(struct pcie_port *pp,
                                 struct msi_controller *chip)
 {
+       struct device *dev = pp->dev;
+       struct device_node *np = dev->of_node;
        struct device_node *msi_node;
-       struct device_node *np = pp->dev->of_node;
 
        /*
         * The MSI domain is set by the generic of_msi_configure().  This
@@ -169,7 +170,7 @@ static int ls_pcie_msi_host_init(struct pcie_port *pp,
         */
        msi_node = of_parse_phandle(np, "msi-parent", 0);
        if (!msi_node) {
-               dev_err(pp->dev, "failed to find msi-parent\n");
+               dev_err(dev, "failed to find msi-parent\n");
                return -EINVAL;
        }
 
@@ -212,19 +213,15 @@ static const struct of_device_id ls_pcie_of_match[] = {
        { },
 };
 
-static int __init ls_add_pcie_port(struct pcie_port *pp,
-                                  struct platform_device *pdev)
+static int __init ls_add_pcie_port(struct ls_pcie *pcie)
 {
+       struct pcie_port *pp = &pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
-       struct ls_pcie *pcie = to_ls_pcie(pp);
-
-       pp->dev = &pdev->dev;
-       pp->dbi_base = pcie->dbi;
-       pp->ops = pcie->drvdata->ops;
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(pp->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -233,38 +230,42 @@ static int __init ls_add_pcie_port(struct pcie_port *pp,
 
 static int __init ls_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        const struct of_device_id *match;
        struct ls_pcie *pcie;
+       struct pcie_port *pp;
        struct resource *dbi_base;
        int ret;
 
-       match = of_match_device(ls_pcie_of_match, &pdev->dev);
+       match = of_match_device(ls_pcie_of_match, dev);
        if (!match)
                return -ENODEV;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
+       pp = &pcie->pp;
+       pp->dev = dev;
+       pp->ops = pcie->drvdata->ops;
+
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
-       pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
-       if (IS_ERR(pcie->dbi)) {
-               dev_err(&pdev->dev, "missing *regs* space\n");
-               return PTR_ERR(pcie->dbi);
+       pcie->pp.dbi_base = devm_ioremap_resource(dev, dbi_base);
+       if (IS_ERR(pcie->pp.dbi_base)) {
+               dev_err(dev, "missing *regs* space\n");
+               return PTR_ERR(pcie->pp.dbi_base);
        }
 
        pcie->drvdata = match->data;
-       pcie->lut = pcie->dbi + pcie->drvdata->lut_offset;
+       pcie->lut = pcie->pp.dbi_base + pcie->drvdata->lut_offset;
 
        if (!ls_pcie_is_bridge(pcie))
                return -ENODEV;
 
-       ret = ls_add_pcie_port(&pcie->pp, pdev);
+       ret = ls_add_pcie_port(pcie);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, pcie);
-
        return 0;
 }
 
index 307f81d..45a89d9 100644 (file)
@@ -1190,13 +1190,13 @@ static void mvebu_pcie_powerdown(struct mvebu_pcie_port *port)
 
 static int mvebu_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct mvebu_pcie *pcie;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct device_node *child;
        int num, i, ret;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_pcie),
-                           GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
@@ -1206,7 +1206,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
        /* Get the PCIe memory and I/O aperture */
        mvebu_mbus_get_pcie_mem_aperture(&pcie->mem);
        if (resource_size(&pcie->mem) == 0) {
-               dev_err(&pdev->dev, "invalid memory aperture size\n");
+               dev_err(dev, "invalid memory aperture size\n");
                return -EINVAL;
        }
 
@@ -1224,20 +1224,18 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
        /* Get the bus range */
        ret = of_pci_parse_bus_range(np, &pcie->busn);
        if (ret) {
-               dev_err(&pdev->dev, "failed to parse bus-range property: %d\n",
-                       ret);
+               dev_err(dev, "failed to parse bus-range property: %d\n", ret);
                return ret;
        }
 
-       num = of_get_available_child_count(pdev->dev.of_node);
+       num = of_get_available_child_count(np);
 
-       pcie->ports = devm_kcalloc(&pdev->dev, num, sizeof(*pcie->ports),
-                                  GFP_KERNEL);
+       pcie->ports = devm_kcalloc(dev, num, sizeof(*pcie->ports), GFP_KERNEL);
        if (!pcie->ports)
                return -ENOMEM;
 
        i = 0;
-       for_each_available_child_of_node(pdev->dev.of_node, child) {
+       for_each_available_child_of_node(np, child) {
                struct mvebu_pcie_port *port = &pcie->ports[i];
 
                ret = mvebu_pcie_parse_port(pcie, port, child);
@@ -1266,8 +1264,7 @@ static int mvebu_pcie_probe(struct platform_device *pdev)
 
                port->base = mvebu_pcie_map_registers(pdev, child, port);
                if (IS_ERR(port->base)) {
-                       dev_err(&pdev->dev, "%s: cannot map registers\n",
-                               port->name);
+                       dev_err(dev, "%s: cannot map registers\n", port->name);
                        port->base = NULL;
                        mvebu_pcie_powerdown(port);
                        continue;
index 597566f..1eeefa4 100644 (file)
@@ -154,10 +154,11 @@ static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 static irqreturn_t rcar_pci_err_irq(int irq, void *pw)
 {
        struct rcar_pci_priv *priv = pw;
+       struct device *dev = priv->dev;
        u32 status = ioread32(priv->reg + RCAR_PCI_INT_STATUS_REG);
 
        if (status & RCAR_PCI_INT_ALLERRORS) {
-               dev_err(priv->dev, "error irq: status %08x\n", status);
+               dev_err(dev, "error irq: status %08x\n", status);
 
                /* clear the error(s) */
                iowrite32(status & RCAR_PCI_INT_ALLERRORS,
@@ -170,13 +171,14 @@ static irqreturn_t rcar_pci_err_irq(int irq, void *pw)
 
 static void rcar_pci_setup_errirq(struct rcar_pci_priv *priv)
 {
+       struct device *dev = priv->dev;
        int ret;
        u32 val;
 
-       ret = devm_request_irq(priv->dev, priv->irq, rcar_pci_err_irq,
+       ret = devm_request_irq(dev, priv->irq, rcar_pci_err_irq,
                               IRQF_SHARED, "error irq", priv);
        if (ret) {
-               dev_err(priv->dev, "cannot claim IRQ for error handling\n");
+               dev_err(dev, "cannot claim IRQ for error handling\n");
                return;
        }
 
@@ -192,15 +194,16 @@ static inline void rcar_pci_setup_errirq(struct rcar_pci_priv *priv) { }
 static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
 {
        struct rcar_pci_priv *priv = sys->private_data;
+       struct device *dev = priv->dev;
        void __iomem *reg = priv->reg;
        u32 val;
        int ret;
 
-       pm_runtime_enable(priv->dev);
-       pm_runtime_get_sync(priv->dev);
+       pm_runtime_enable(dev);
+       pm_runtime_get_sync(dev);
 
        val = ioread32(reg + RCAR_PCI_UNIT_REV_REG);
-       dev_info(priv->dev, "PCI: bus%u revision %x\n", sys->busnr, val);
+       dev_info(dev, "PCI: bus%u revision %x\n", sys->busnr, val);
 
        /* Disable Direct Power Down State and assert reset */
        val = ioread32(reg + RCAR_USBCTR_REG) & ~RCAR_USBCTR_DIRPD;
@@ -275,7 +278,7 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys)
 
        /* Add PCI resources */
        pci_add_resource(&sys->resources, &priv->mem_res);
-       ret = devm_request_pci_bus_resources(priv->dev, &sys->resources);
+       ret = devm_request_pci_bus_resources(dev, &sys->resources);
        if (ret < 0)
                return ret;
 
@@ -311,6 +314,7 @@ static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
 static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
                                         struct device_node *np)
 {
+       struct device *dev = pci->dev;
        struct of_pci_range range;
        struct of_pci_range_parser parser;
        int index = 0;
@@ -331,14 +335,14 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
 
                /* Catch HW limitations */
                if (!(range.flags & IORESOURCE_PREFETCH)) {
-                       dev_err(pci->dev, "window must be prefetchable\n");
+                       dev_err(dev, "window must be prefetchable\n");
                        return -EINVAL;
                }
                if (pci->window_addr) {
                        u32 lowaddr = 1 << (ffs(pci->window_addr) - 1);
 
                        if (lowaddr < pci->window_size) {
-                               dev_err(pci->dev, "invalid window size/addr\n");
+                               dev_err(dev, "invalid window size/addr\n");
                                return -EINVAL;
                        }
                }
@@ -350,6 +354,7 @@ static int rcar_pci_parse_map_dma_ranges(struct rcar_pci_priv *pci,
 
 static int rcar_pci_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct resource *cfg_res, *mem_res;
        struct rcar_pci_priv *priv;
        void __iomem *reg;
@@ -357,7 +362,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        void *hw_private[1];
 
        cfg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       reg = devm_ioremap_resource(&pdev->dev, cfg_res);
+       reg = devm_ioremap_resource(dev, cfg_res);
        if (IS_ERR(reg))
                return PTR_ERR(reg);
 
@@ -368,8 +373,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        if (mem_res->start & 0xFFFF)
                return -EINVAL;
 
-       priv = devm_kzalloc(&pdev->dev,
-                           sizeof(struct rcar_pci_priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(struct rcar_pci_priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
@@ -378,10 +382,10 @@ static int rcar_pci_probe(struct platform_device *pdev)
 
        priv->irq = platform_get_irq(pdev, 0);
        priv->reg = reg;
-       priv->dev = &pdev->dev;
+       priv->dev = dev;
 
        if (priv->irq < 0) {
-               dev_err(&pdev->dev, "no valid irq found\n");
+               dev_err(dev, "no valid irq found\n");
                return priv->irq;
        }
 
@@ -390,23 +394,23 @@ static int rcar_pci_probe(struct platform_device *pdev)
        priv->window_pci = 0x40000000;
        priv->window_size = SZ_1G;
 
-       if (pdev->dev.of_node) {
+       if (dev->of_node) {
                struct resource busnr;
                int ret;
 
-               ret = of_pci_parse_bus_range(pdev->dev.of_node, &busnr);
+               ret = of_pci_parse_bus_range(dev->of_node, &busnr);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to parse bus-range\n");
+                       dev_err(dev, "failed to parse bus-range\n");
                        return ret;
                }
 
                priv->busnr = busnr.start;
                if (busnr.end != busnr.start)
-                       dev_warn(&pdev->dev, "only one bus number supported\n");
+                       dev_warn(dev, "only one bus number supported\n");
 
-               ret = rcar_pci_parse_map_dma_ranges(priv, pdev->dev.of_node);
+               ret = rcar_pci_parse_map_dma_ranges(priv, dev->of_node);
                if (ret < 0) {
-                       dev_err(&pdev->dev, "failed to parse dma-range\n");
+                       dev_err(dev, "failed to parse dma-range\n");
                        return ret;
                }
        } else {
@@ -421,7 +425,7 @@ static int rcar_pci_probe(struct platform_device *pdev)
        hw.map_irq = rcar_pci_map_irq;
        hw.ops = &rcar_pci_ops;
        hw.setup = rcar_pci_setup;
-       pci_common_init_dev(&pdev->dev, &hw);
+       pci_common_init_dev(dev, &hw);
        return 0;
 }
 
index e2a8e4c..8dfccf7 100644 (file)
@@ -384,6 +384,7 @@ static unsigned long tegra_pcie_conf_offset(unsigned int devfn, int where)
 static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
                                                   unsigned int busnr)
 {
+       struct device *dev = pcie->dev;
        pgprot_t prot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
                                 L_PTE_XN | L_PTE_MT_DEV_SHARED | L_PTE_SHARED);
        phys_addr_t cs = pcie->cs->start;
@@ -413,8 +414,7 @@ static struct tegra_pcie_bus *tegra_pcie_bus_alloc(struct tegra_pcie *pcie,
 
                err = ioremap_page_range(virt, virt + SZ_64K, phys, prot);
                if (err < 0) {
-                       dev_err(pcie->dev, "ioremap_page_range() failed: %d\n",
-                               err);
+                       dev_err(dev, "ioremap_page_range() failed: %d\n", err);
                        goto unmap;
                }
        }
@@ -462,6 +462,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
                                        int where)
 {
        struct tegra_pcie *pcie = sys_to_pcie(bus->sysdata);
+       struct device *dev = pcie->dev;
        void __iomem *addr = NULL;
 
        if (bus->number == 0) {
@@ -482,8 +483,7 @@ static void __iomem *tegra_pcie_map_bus(struct pci_bus *bus,
                                addr = (void __iomem *)b->area->addr;
 
                if (!addr) {
-                       dev_err(pcie->dev,
-                               "failed to map cfg. space for bus %u\n",
+                       dev_err(dev, "failed to map cfg. space for bus %u\n",
                                bus->number);
                        return NULL;
                }
@@ -584,12 +584,13 @@ static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
 static void tegra_pcie_port_free(struct tegra_pcie_port *port)
 {
        struct tegra_pcie *pcie = port->pcie;
+       struct device *dev = pcie->dev;
 
-       devm_iounmap(pcie->dev, port->base);
-       devm_release_mem_region(pcie->dev, port->regs.start,
+       devm_iounmap(dev, port->base);
+       devm_release_mem_region(dev, port->regs.start,
                                resource_size(&port->regs));
        list_del(&port->list);
-       devm_kfree(pcie->dev, port);
+       devm_kfree(dev, port);
 }
 
 /* Tegra PCIE root complex wrongly reports device class */
@@ -612,12 +613,13 @@ DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
 static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
 {
        struct tegra_pcie *pcie = sys_to_pcie(sys);
+       struct device *dev = pcie->dev;
        int err;
 
        sys->mem_offset = pcie->offset.mem;
        sys->io_offset = pcie->offset.io;
 
-       err = devm_request_resource(pcie->dev, &iomem_resource, &pcie->io);
+       err = devm_request_resource(dev, &iomem_resource, &pcie->io);
        if (err < 0)
                return err;
 
@@ -631,7 +633,7 @@ static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
                                sys->mem_offset);
        pci_add_resource(&sys->resources, &pcie->busn);
 
-       err = devm_request_pci_bus_resources(pcie->dev, &sys->resources);
+       err = devm_request_pci_bus_resources(dev, &sys->resources);
        if (err < 0)
                return err;
 
@@ -672,6 +674,7 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
                "Peer2Peer error",
        };
        struct tegra_pcie *pcie = arg;
+       struct device *dev = pcie->dev;
        u32 code, signature;
 
        code = afi_readl(pcie, AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
@@ -689,11 +692,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
         * happen a lot during enumeration
         */
        if (code == AFI_INTR_MASTER_ABORT)
-               dev_dbg(pcie->dev, "%s, signature: %08x\n", err_msg[code],
-                       signature);
+               dev_dbg(dev, "%s, signature: %08x\n", err_msg[code], signature);
        else
-               dev_err(pcie->dev, "%s, signature: %08x\n", err_msg[code],
-                       signature);
+               dev_err(dev, "%s, signature: %08x\n", err_msg[code], signature);
 
        if (code == AFI_INTR_TARGET_ABORT || code == AFI_INTR_MASTER_ABORT ||
            code == AFI_INTR_FPCI_DECODE_ERROR) {
@@ -701,9 +702,9 @@ static irqreturn_t tegra_pcie_isr(int irq, void *arg)
                u64 address = (u64)fpci << 32 | (signature & 0xfffffffc);
 
                if (code == AFI_INTR_MASTER_ABORT)
-                       dev_dbg(pcie->dev, "  FPCI address: %10llx\n", address);
+                       dev_dbg(dev, "  FPCI address: %10llx\n", address);
                else
-                       dev_err(pcie->dev, "  FPCI address: %10llx\n", address);
+                       dev_err(dev, "  FPCI address: %10llx\n", address);
        }
 
        return IRQ_HANDLED;
@@ -793,6 +794,7 @@ static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
 
 static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        u32 value;
        int err;
@@ -829,7 +831,7 @@ static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
        /* wait for the PLL to lock */
        err = tegra_pcie_pll_wait(pcie, 500);
        if (err < 0) {
-               dev_err(pcie->dev, "PLL failed to lock: %d\n", err);
+               dev_err(dev, "PLL failed to lock: %d\n", err);
                return err;
        }
 
@@ -859,7 +861,7 @@ static int tegra_pcie_phy_disable(struct tegra_pcie *pcie)
        /* override IDDQ */
        value = pads_readl(pcie, PADS_CTL);
        value |= PADS_CTL_IDDQ_1L;
-       pads_writel(pcie, PADS_CTL, value);
+       pads_writel(pcie, value, PADS_CTL);
 
        /* reset PLL */
        value = pads_readl(pcie, soc->pads_pll_ctl);
@@ -880,8 +882,7 @@ static int tegra_pcie_port_phy_power_on(struct tegra_pcie_port *port)
        for (i = 0; i < port->lanes; i++) {
                err = phy_power_on(port->phys[i]);
                if (err < 0) {
-                       dev_err(dev, "failed to power on PHY#%u: %d\n", i,
-                               err);
+                       dev_err(dev, "failed to power on PHY#%u: %d\n", i, err);
                        return err;
                }
        }
@@ -909,6 +910,7 @@ static int tegra_pcie_port_phy_power_off(struct tegra_pcie_port *port)
 
 static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_pcie_port *port;
        int err;
@@ -920,7 +922,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
                        err = tegra_pcie_phy_enable(pcie);
 
                if (err < 0)
-                       dev_err(pcie->dev, "failed to power on PHY: %d\n", err);
+                       dev_err(dev, "failed to power on PHY: %d\n", err);
 
                return err;
        }
@@ -928,7 +930,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
        list_for_each_entry(port, &pcie->ports, list) {
                err = tegra_pcie_port_phy_power_on(port);
                if (err < 0) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "failed to power on PCIe port %u PHY: %d\n",
                                port->index, err);
                        return err;
@@ -946,6 +948,7 @@ static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
 
 static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct tegra_pcie_port *port;
        int err;
 
@@ -956,8 +959,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
                        err = tegra_pcie_phy_disable(pcie);
 
                if (err < 0)
-                       dev_err(pcie->dev, "failed to power off PHY: %d\n",
-                               err);
+                       dev_err(dev, "failed to power off PHY: %d\n", err);
 
                return err;
        }
@@ -965,7 +967,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
        list_for_each_entry(port, &pcie->ports, list) {
                err = tegra_pcie_port_phy_power_off(port);
                if (err < 0) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "failed to power off PCIe port %u PHY: %d\n",
                                port->index, err);
                        return err;
@@ -977,6 +979,7 @@ static int tegra_pcie_phy_power_off(struct tegra_pcie *pcie)
 
 static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_pcie_port *port;
        unsigned long value;
@@ -1016,7 +1019,7 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 
        err = tegra_pcie_phy_power_on(pcie);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to power on PHY(s): %d\n", err);
+               dev_err(dev, "failed to power on PHY(s): %d\n", err);
                return err;
        }
 
@@ -1049,13 +1052,14 @@ static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
 
 static void tegra_pcie_power_off(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
        /* TODO: disable and unprepare clocks? */
 
        err = tegra_pcie_phy_power_off(pcie);
        if (err < 0)
-               dev_err(pcie->dev, "failed to power off PHY(s): %d\n", err);
+               dev_err(dev, "failed to power off PHY(s): %d\n", err);
 
        reset_control_assert(pcie->pcie_xrst);
        reset_control_assert(pcie->afi_rst);
@@ -1065,11 +1069,12 @@ static void tegra_pcie_power_off(struct tegra_pcie *pcie)
 
        err = regulator_bulk_disable(pcie->num_supplies, pcie->supplies);
        if (err < 0)
-               dev_warn(pcie->dev, "failed to disable regulators: %d\n", err);
+               dev_warn(dev, "failed to disable regulators: %d\n", err);
 }
 
 static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
        int err;
 
@@ -1082,13 +1087,13 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
        /* enable regulators */
        err = regulator_bulk_enable(pcie->num_supplies, pcie->supplies);
        if (err < 0)
-               dev_err(pcie->dev, "failed to enable regulators: %d\n", err);
+               dev_err(dev, "failed to enable regulators: %d\n", err);
 
        err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_PCIE,
                                                pcie->pex_clk,
                                                pcie->pex_rst);
        if (err) {
-               dev_err(pcie->dev, "powerup sequence failed: %d\n", err);
+               dev_err(dev, "powerup sequence failed: %d\n", err);
                return err;
        }
 
@@ -1096,22 +1101,21 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 
        err = clk_prepare_enable(pcie->afi_clk);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to enable AFI clock: %d\n", err);
+               dev_err(dev, "failed to enable AFI clock: %d\n", err);
                return err;
        }
 
        if (soc->has_cml_clk) {
                err = clk_prepare_enable(pcie->cml_clk);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to enable CML clock: %d\n",
-                               err);
+                       dev_err(dev, "failed to enable CML clock: %d\n", err);
                        return err;
                }
        }
 
        err = clk_prepare_enable(pcie->pll_e);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to enable PLLE clock: %d\n", err);
+               dev_err(dev, "failed to enable PLLE clock: %d\n", err);
                return err;
        }
 
@@ -1120,22 +1124,23 @@ static int tegra_pcie_power_on(struct tegra_pcie *pcie)
 
 static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        const struct tegra_pcie_soc *soc = pcie->soc;
 
-       pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
+       pcie->pex_clk = devm_clk_get(dev, "pex");
        if (IS_ERR(pcie->pex_clk))
                return PTR_ERR(pcie->pex_clk);
 
-       pcie->afi_clk = devm_clk_get(pcie->dev, "afi");
+       pcie->afi_clk = devm_clk_get(dev, "afi");
        if (IS_ERR(pcie->afi_clk))
                return PTR_ERR(pcie->afi_clk);
 
-       pcie->pll_e = devm_clk_get(pcie->dev, "pll_e");
+       pcie->pll_e = devm_clk_get(dev, "pll_e");
        if (IS_ERR(pcie->pll_e))
                return PTR_ERR(pcie->pll_e);
 
        if (soc->has_cml_clk) {
-               pcie->cml_clk = devm_clk_get(pcie->dev, "cml");
+               pcie->cml_clk = devm_clk_get(dev, "cml");
                if (IS_ERR(pcie->cml_clk))
                        return PTR_ERR(pcie->cml_clk);
        }
@@ -1145,15 +1150,17 @@ static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
 {
-       pcie->pex_rst = devm_reset_control_get(pcie->dev, "pex");
+       struct device *dev = pcie->dev;
+
+       pcie->pex_rst = devm_reset_control_get(dev, "pex");
        if (IS_ERR(pcie->pex_rst))
                return PTR_ERR(pcie->pex_rst);
 
-       pcie->afi_rst = devm_reset_control_get(pcie->dev, "afi");
+       pcie->afi_rst = devm_reset_control_get(dev, "afi");
        if (IS_ERR(pcie->afi_rst))
                return PTR_ERR(pcie->afi_rst);
 
-       pcie->pcie_xrst = devm_reset_control_get(pcie->dev, "pcie_x");
+       pcie->pcie_xrst = devm_reset_control_get(dev, "pcie_x");
        if (IS_ERR(pcie->pcie_xrst))
                return PTR_ERR(pcie->pcie_xrst);
 
@@ -1162,18 +1169,19 @@ static int tegra_pcie_resets_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_phys_get_legacy(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
-       pcie->phy = devm_phy_optional_get(pcie->dev, "pcie");
+       pcie->phy = devm_phy_optional_get(dev, "pcie");
        if (IS_ERR(pcie->phy)) {
                err = PTR_ERR(pcie->phy);
-               dev_err(pcie->dev, "failed to get PHY: %d\n", err);
+               dev_err(dev, "failed to get PHY: %d\n", err);
                return err;
        }
 
        err = phy_init(pcie->phy);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to initialize PHY: %d\n", err);
+               dev_err(dev, "failed to initialize PHY: %d\n", err);
                return err;
        }
 
@@ -1256,43 +1264,44 @@ static int tegra_pcie_phys_get(struct tegra_pcie *pcie)
 
 static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct resource *pads, *afi, *res;
        int err;
 
        err = tegra_pcie_clocks_get(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to get clocks: %d\n", err);
+               dev_err(dev, "failed to get clocks: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_resets_get(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to get resets: %d\n", err);
+               dev_err(dev, "failed to get resets: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_phys_get(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get PHYs: %d\n", err);
+               dev_err(dev, "failed to get PHYs: %d\n", err);
                return err;
        }
 
        err = tegra_pcie_power_on(pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to power up: %d\n", err);
+               dev_err(dev, "failed to power up: %d\n", err);
                return err;
        }
 
        pads = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pads");
-       pcie->pads = devm_ioremap_resource(&pdev->dev, pads);
+       pcie->pads = devm_ioremap_resource(dev, pads);
        if (IS_ERR(pcie->pads)) {
                err = PTR_ERR(pcie->pads);
                goto poweroff;
        }
 
        afi = platform_get_resource_byname(pdev, IORESOURCE_MEM, "afi");
-       pcie->afi = devm_ioremap_resource(&pdev->dev, afi);
+       pcie->afi = devm_ioremap_resource(dev, afi);
        if (IS_ERR(pcie->afi)) {
                err = PTR_ERR(pcie->afi);
                goto poweroff;
@@ -1305,7 +1314,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
                goto poweroff;
        }
 
-       pcie->cs = devm_request_mem_region(pcie->dev, res->start,
+       pcie->cs = devm_request_mem_region(dev, res->start,
                                           resource_size(res), res->name);
        if (!pcie->cs) {
                err = -EADDRNOTAVAIL;
@@ -1315,7 +1324,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
        /* request interrupt */
        err = platform_get_irq_byname(pdev, "intr");
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+               dev_err(dev, "failed to get IRQ: %d\n", err);
                goto poweroff;
        }
 
@@ -1323,7 +1332,7 @@ static int tegra_pcie_get_resources(struct tegra_pcie *pcie)
 
        err = request_irq(pcie->irq, tegra_pcie_isr, IRQF_SHARED, "PCIE", pcie);
        if (err) {
-               dev_err(&pdev->dev, "failed to register IRQ: %d\n", err);
+               dev_err(dev, "failed to register IRQ: %d\n", err);
                goto poweroff;
        }
 
@@ -1336,6 +1345,7 @@ poweroff:
 
 static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int err;
 
        if (pcie->irq > 0)
@@ -1345,7 +1355,7 @@ static int tegra_pcie_put_resources(struct tegra_pcie *pcie)
 
        err = phy_exit(pcie->phy);
        if (err < 0)
-               dev_err(pcie->dev, "failed to teardown PHY: %d\n", err);
+               dev_err(dev, "failed to teardown PHY: %d\n", err);
 
        return 0;
 }
@@ -1384,6 +1394,7 @@ static void tegra_msi_free(struct tegra_msi *chip, unsigned long irq)
 static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
 {
        struct tegra_pcie *pcie = data;
+       struct device *dev = pcie->dev;
        struct tegra_msi *msi = &pcie->msi;
        unsigned int i, processed = 0;
 
@@ -1403,13 +1414,13 @@ static irqreturn_t tegra_pcie_msi_irq(int irq, void *data)
                                if (test_bit(index, msi->used))
                                        generic_handle_irq(irq);
                                else
-                                       dev_info(pcie->dev, "unhandled MSI\n");
+                                       dev_info(dev, "unhandled MSI\n");
                        } else {
                                /*
                                 * that's weird who triggered this?
                                 * just clear it
                                 */
-                               dev_info(pcie->dev, "unexpected MSI\n");
+                               dev_info(dev, "unexpected MSI\n");
                        }
 
                        /* see if there's any more pending in this vector */
@@ -1488,7 +1499,8 @@ static const struct irq_domain_ops msi_domain_ops = {
 
 static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct tegra_msi *msi = &pcie->msi;
        unsigned long base;
@@ -1497,20 +1509,20 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
 
        mutex_init(&msi->lock);
 
-       msi->chip.dev = pcie->dev;
+       msi->chip.dev = dev;
        msi->chip.setup_irq = tegra_msi_setup_irq;
        msi->chip.teardown_irq = tegra_msi_teardown_irq;
 
-       msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+       msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR,
                                            &msi_domain_ops, &msi->chip);
        if (!msi->domain) {
-               dev_err(&pdev->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
        err = platform_get_irq_byname(pdev, "msi");
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", err);
+               dev_err(dev, "failed to get IRQ: %d\n", err);
                goto err;
        }
 
@@ -1519,7 +1531,7 @@ static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
        err = request_irq(msi->irq, tegra_pcie_msi_irq, IRQF_NO_THREAD,
                          tegra_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
@@ -1594,46 +1606,47 @@ static int tegra_pcie_disable_msi(struct tegra_pcie *pcie)
 static int tegra_pcie_get_xbar_config(struct tegra_pcie *pcie, u32 lanes,
                                      u32 *xbar)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
 
        if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
                switch (lanes) {
                case 0x0000104:
-                       dev_info(pcie->dev, "4x1, 1x1 configuration\n");
+                       dev_info(dev, "4x1, 1x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X4_X1;
                        return 0;
 
                case 0x0000102:
-                       dev_info(pcie->dev, "2x1, 1x1 configuration\n");
+                       dev_info(dev, "2x1, 1x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_X2_X1;
                        return 0;
                }
        } else if (of_device_is_compatible(np, "nvidia,tegra30-pcie")) {
                switch (lanes) {
                case 0x00000204:
-                       dev_info(pcie->dev, "4x1, 2x1 configuration\n");
+                       dev_info(dev, "4x1, 2x1 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_420;
                        return 0;
 
                case 0x00020202:
-                       dev_info(pcie->dev, "2x3 configuration\n");
+                       dev_info(dev, "2x3 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_222;
                        return 0;
 
                case 0x00010104:
-                       dev_info(pcie->dev, "4x1, 1x2 configuration\n");
+                       dev_info(dev, "4x1, 1x2 configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_411;
                        return 0;
                }
        } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
                switch (lanes) {
                case 0x00000004:
-                       dev_info(pcie->dev, "single-mode configuration\n");
+                       dev_info(dev, "single-mode configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE;
                        return 0;
 
                case 0x00000202:
-                       dev_info(pcie->dev, "dual-mode configuration\n");
+                       dev_info(dev, "dual-mode configuration\n");
                        *xbar = AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
                        return 0;
                }
@@ -1673,7 +1686,8 @@ static bool of_regulator_bulk_available(struct device_node *np,
  */
 static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
 
        if (of_device_is_compatible(np, "nvidia,tegra30-pcie"))
                pcie->num_supplies = 3;
@@ -1681,12 +1695,12 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
                pcie->num_supplies = 2;
 
        if (pcie->num_supplies == 0) {
-               dev_err(pcie->dev, "device %s not supported in legacy mode\n",
+               dev_err(dev, "device %s not supported in legacy mode\n",
                        np->full_name);
                return -ENODEV;
        }
 
-       pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+       pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                      sizeof(*pcie->supplies),
                                      GFP_KERNEL);
        if (!pcie->supplies)
@@ -1698,8 +1712,7 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
        if (pcie->num_supplies > 2)
                pcie->supplies[2].supply = "avdd";
 
-       return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
-                                      pcie->supplies);
+       return devm_regulator_bulk_get(dev, pcie->num_supplies, pcie->supplies);
 }
 
 /*
@@ -1713,13 +1726,14 @@ static int tegra_pcie_get_legacy_regulators(struct tegra_pcie *pcie)
  */
 static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
 {
-       struct device_node *np = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node;
        unsigned int i = 0;
 
        if (of_device_is_compatible(np, "nvidia,tegra124-pcie")) {
                pcie->num_supplies = 7;
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1746,7 +1760,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
                pcie->num_supplies = 4 + (need_pexa ? 2 : 0) +
                                         (need_pexb ? 2 : 0);
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1769,7 +1783,7 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
        } else if (of_device_is_compatible(np, "nvidia,tegra20-pcie")) {
                pcie->num_supplies = 5;
 
-               pcie->supplies = devm_kcalloc(pcie->dev, pcie->num_supplies,
+               pcie->supplies = devm_kcalloc(dev, pcie->num_supplies,
                                              sizeof(*pcie->supplies),
                                              GFP_KERNEL);
                if (!pcie->supplies)
@@ -1782,9 +1796,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
                pcie->supplies[4].supply = "vddio-pex-clk";
        }
 
-       if (of_regulator_bulk_available(pcie->dev->of_node, pcie->supplies,
+       if (of_regulator_bulk_available(dev->of_node, pcie->supplies,
                                        pcie->num_supplies))
-               return devm_regulator_bulk_get(pcie->dev, pcie->num_supplies,
+               return devm_regulator_bulk_get(dev, pcie->num_supplies,
                                               pcie->supplies);
 
        /*
@@ -1792,9 +1806,9 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
         * that the device tree complies with an older version of the device
         * tree binding.
         */
-       dev_info(pcie->dev, "using legacy DT binding for power supplies\n");
+       dev_info(dev, "using legacy DT binding for power supplies\n");
 
-       devm_kfree(pcie->dev, pcie->supplies);
+       devm_kfree(dev, pcie->supplies);
        pcie->num_supplies = 0;
 
        return tegra_pcie_get_legacy_regulators(pcie);
@@ -1802,7 +1816,8 @@ static int tegra_pcie_get_regulators(struct tegra_pcie *pcie, u32 lane_mask)
 
 static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 {
-       struct device_node *np = pcie->dev->of_node, *port;
+       struct device *dev = pcie->dev;
+       struct device_node *np = dev->of_node, *port;
        const struct tegra_pcie_soc *soc = pcie->soc;
        struct of_pci_range_parser parser;
        struct of_pci_range range;
@@ -1812,7 +1827,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
        int err;
 
        if (of_pci_range_parser_init(&parser, np)) {
-               dev_err(pcie->dev, "missing \"ranges\" property\n");
+               dev_err(dev, "missing \"ranges\" property\n");
                return -EINVAL;
        }
 
@@ -1867,8 +1882,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
        err = of_pci_parse_bus_range(np, &pcie->busn);
        if (err < 0) {
-               dev_err(pcie->dev, "failed to parse ranges property: %d\n",
-                       err);
+               dev_err(dev, "failed to parse ranges property: %d\n", err);
                pcie->busn.name = np->name;
                pcie->busn.start = 0;
                pcie->busn.end = 0xff;
@@ -1883,15 +1897,14 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                err = of_pci_get_devfn(port);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse address: %d\n",
-                               err);
+                       dev_err(dev, "failed to parse address: %d\n", err);
                        return err;
                }
 
                index = PCI_SLOT(err);
 
                if (index < 1 || index > soc->num_ports) {
-                       dev_err(pcie->dev, "invalid port number: %d\n", index);
+                       dev_err(dev, "invalid port number: %d\n", index);
                        return -EINVAL;
                }
 
@@ -1899,13 +1912,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
                err = of_property_read_u32(port, "nvidia,num-lanes", &value);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse # of lanes: %d\n",
+                       dev_err(dev, "failed to parse # of lanes: %d\n",
                                err);
                        return err;
                }
 
                if (value > 16) {
-                       dev_err(pcie->dev, "invalid # of lanes: %u\n", value);
+                       dev_err(dev, "invalid # of lanes: %u\n", value);
                        return -EINVAL;
                }
 
@@ -1919,14 +1932,13 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                mask |= ((1 << value) - 1) << lane;
                lane += value;
 
-               rp = devm_kzalloc(pcie->dev, sizeof(*rp), GFP_KERNEL);
+               rp = devm_kzalloc(dev, sizeof(*rp), GFP_KERNEL);
                if (!rp)
                        return -ENOMEM;
 
                err = of_address_to_resource(port, 0, &rp->regs);
                if (err < 0) {
-                       dev_err(pcie->dev, "failed to parse address: %d\n",
-                               err);
+                       dev_err(dev, "failed to parse address: %d\n", err);
                        return err;
                }
 
@@ -1936,7 +1948,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
                rp->pcie = pcie;
                rp->np = port;
 
-               rp->base = devm_ioremap_resource(pcie->dev, &rp->regs);
+               rp->base = devm_ioremap_resource(dev, &rp->regs);
                if (IS_ERR(rp->base))
                        return PTR_ERR(rp->base);
 
@@ -1945,7 +1957,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 
        err = tegra_pcie_get_xbar_config(pcie, lanes, &pcie->xbar_config);
        if (err < 0) {
-               dev_err(pcie->dev, "invalid lane configuration\n");
+               dev_err(dev, "invalid lane configuration\n");
                return err;
        }
 
@@ -1964,6 +1976,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 #define TEGRA_PCIE_LINKUP_TIMEOUT      200     /* up to 1.2 seconds */
 static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
 {
+       struct device *dev = port->pcie->dev;
        unsigned int retries = 3;
        unsigned long value;
 
@@ -1986,8 +1999,7 @@ static bool tegra_pcie_port_check_link(struct tegra_pcie_port *port)
                } while (--timeout);
 
                if (!timeout) {
-                       dev_err(port->pcie->dev, "link %u down, retrying\n",
-                               port->index);
+                       dev_err(dev, "link %u down, retrying\n", port->index);
                        goto retry;
                }
 
@@ -2011,11 +2023,12 @@ retry:
 
 static int tegra_pcie_enable(struct tegra_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct tegra_pcie_port *port, *tmp;
        struct hw_pci hw;
 
        list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
-               dev_info(pcie->dev, "probing port %u, using %u lanes\n",
+               dev_info(dev, "probing port %u, using %u lanes\n",
                         port->index, port->lanes);
 
                tegra_pcie_port_enable(port);
@@ -2023,7 +2036,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
                if (tegra_pcie_port_check_link(port))
                        continue;
 
-               dev_info(pcie->dev, "link %u down, ignoring\n", port->index);
+               dev_info(dev, "link %u down, ignoring\n", port->index);
 
                tegra_pcie_port_disable(port);
                tegra_pcie_port_free(port);
@@ -2041,8 +2054,7 @@ static int tegra_pcie_enable(struct tegra_pcie *pcie)
        hw.map_irq = tegra_pcie_map_irq;
        hw.ops = &tegra_pcie_ops;
 
-       pci_common_init_dev(pcie->dev, &hw);
-
+       pci_common_init_dev(dev, &hw);
        return 0;
 }
 
@@ -2204,17 +2216,18 @@ remove:
 
 static int tegra_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct tegra_pcie *pcie;
        int err;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->soc = of_device_get_match_data(&pdev->dev);
+       pcie->soc = of_device_get_match_data(dev);
        INIT_LIST_HEAD(&pcie->buses);
        INIT_LIST_HEAD(&pcie->ports);
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
 
        err = tegra_pcie_parse_dt(pcie);
        if (err < 0)
@@ -2222,7 +2235,7 @@ static int tegra_pcie_probe(struct platform_device *pdev)
 
        err = tegra_pcie_get_resources(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+               dev_err(dev, "failed to request resources: %d\n", err);
                return err;
        }
 
@@ -2236,27 +2249,23 @@ static int tegra_pcie_probe(struct platform_device *pdev)
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = tegra_pcie_enable_msi(pcie);
                if (err < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to enable MSI support: %d\n",
-                               err);
+                       dev_err(dev, "failed to enable MSI support: %d\n", err);
                        goto put_resources;
                }
        }
 
        err = tegra_pcie_enable(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to enable PCIe ports: %d\n", err);
+               dev_err(dev, "failed to enable PCIe ports: %d\n", err);
                goto disable_msi;
        }
 
        if (IS_ENABLED(CONFIG_DEBUG_FS)) {
                err = tegra_pcie_debugfs_init(pcie);
                if (err < 0)
-                       dev_err(&pdev->dev, "failed to setup debugfs: %d\n",
-                               err);
+                       dev_err(dev, "failed to setup debugfs: %d\n", err);
        }
 
-       platform_set_drvdata(pdev, pcie);
        return 0;
 
 disable_msi:
index a81273c..1de23d7 100644 (file)
@@ -76,6 +76,16 @@ struct xgene_pcie_port {
        u32                     version;
 };
 
+static u32 xgene_pcie_readl(struct xgene_pcie_port *port, u32 reg)
+{
+       return readl(port->csr_base + reg);
+}
+
+static void xgene_pcie_writel(struct xgene_pcie_port *port, u32 reg, u32 val)
+{
+       writel(val, port->csr_base + reg);
+}
+
 static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
 {
        return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags;
@@ -112,9 +122,9 @@ static void xgene_pcie_set_rtdid_reg(struct pci_bus *bus, uint devfn)
        if (!pci_is_root_bus(bus))
                rtdid_val = (b << 8) | (d << 3) | f;
 
-       writel(rtdid_val, port->csr_base + RTDID);
+       xgene_pcie_writel(port, RTDID, rtdid_val);
        /* read the register back to ensure flush */
-       readl(port->csr_base + RTDID);
+       xgene_pcie_readl(port, RTDID);
 }
 
 /*
@@ -179,28 +189,28 @@ static struct pci_ops xgene_pcie_ops = {
        .write = pci_generic_config_write32,
 };
 
-static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
+static u64 xgene_pcie_set_ib_mask(struct xgene_pcie_port *port, u32 addr,
                                  u32 flags, u64 size)
 {
        u64 mask = (~(size - 1) & PCI_BASE_ADDRESS_MEM_MASK) | flags;
        u32 val32 = 0;
        u32 val;
 
-       val32 = readl(csr_base + addr);
+       val32 = xgene_pcie_readl(port, addr);
        val = (val32 & 0x0000ffff) | (lower_32_bits(mask) << 16);
-       writel(val, csr_base + addr);
+       xgene_pcie_writel(port, addr, val);
 
-       val32 = readl(csr_base + addr + 0x04);
+       val32 = xgene_pcie_readl(port, addr + 0x04);
        val = (val32 & 0xffff0000) | (lower_32_bits(mask) >> 16);
-       writel(val, csr_base + addr + 0x04);
+       xgene_pcie_writel(port, addr + 0x04, val);
 
-       val32 = readl(csr_base + addr + 0x04);
+       val32 = xgene_pcie_readl(port, addr + 0x04);
        val = (val32 & 0x0000ffff) | (upper_32_bits(mask) << 16);
-       writel(val, csr_base + addr + 0x04);
+       xgene_pcie_writel(port, addr + 0x04, val);
 
-       val32 = readl(csr_base + addr + 0x08);
+       val32 = xgene_pcie_readl(port, addr + 0x08);
        val = (val32 & 0xffff0000) | (upper_32_bits(mask) >> 16);
-       writel(val, csr_base + addr + 0x08);
+       xgene_pcie_writel(port, addr + 0x08, val);
 
        return mask;
 }
@@ -208,32 +218,32 @@ static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr,
 static void xgene_pcie_linkup(struct xgene_pcie_port *port,
                                   u32 *lanes, u32 *speed)
 {
-       void __iomem *csr_base = port->csr_base;
        u32 val32;
 
        port->link_up = false;
-       val32 = readl(csr_base + PCIECORE_CTLANDSTATUS);
+       val32 = xgene_pcie_readl(port, PCIECORE_CTLANDSTATUS);
        if (val32 & LINK_UP_MASK) {
                port->link_up = true;
                *speed = PIPE_PHY_RATE_RD(val32);
-               val32 = readl(csr_base + BRIDGE_STATUS_0);
+               val32 = xgene_pcie_readl(port, BRIDGE_STATUS_0);
                *lanes = val32 >> 26;
        }
 }
 
 static int xgene_pcie_init_port(struct xgene_pcie_port *port)
 {
+       struct device *dev = port->dev;
        int rc;
 
-       port->clk = clk_get(port->dev, NULL);
+       port->clk = clk_get(dev, NULL);
        if (IS_ERR(port->clk)) {
-               dev_err(port->dev, "clock not available\n");
+               dev_err(dev, "clock not available\n");
                return -ENODEV;
        }
 
        rc = clk_prepare_enable(port->clk);
        if (rc) {
-               dev_err(port->dev, "clock enable failed\n");
+               dev_err(dev, "clock enable failed\n");
                return rc;
        }
 
@@ -243,15 +253,16 @@ static int xgene_pcie_init_port(struct xgene_pcie_port *port)
 static int xgene_pcie_map_reg(struct xgene_pcie_port *port,
                              struct platform_device *pdev)
 {
+       struct device *dev = port->dev;
        struct resource *res;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
-       port->csr_base = devm_ioremap_resource(port->dev, res);
+       port->csr_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(port->csr_base))
                return PTR_ERR(port->csr_base);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
-       port->cfg_base = devm_ioremap_resource(port->dev, res);
+       port->cfg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(port->cfg_base))
                return PTR_ERR(port->cfg_base);
        port->cfg_addr = res->start;
@@ -263,7 +274,7 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
                                    struct resource *res, u32 offset,
                                    u64 cpu_addr, u64 pci_addr)
 {
-       void __iomem *base = port->csr_base + offset;
+       struct device *dev = port->dev;
        resource_size_t size = resource_size(res);
        u64 restype = resource_type(res);
        u64 mask = 0;
@@ -280,22 +291,24 @@ static void xgene_pcie_setup_ob_reg(struct xgene_pcie_port *port,
        if (size >= min_size)
                mask = ~(size - 1) | flag;
        else
-               dev_warn(port->dev, "res size 0x%llx less than minimum 0x%x\n",
+               dev_warn(dev, "res size 0x%llx less than minimum 0x%x\n",
                         (u64)size, min_size);
 
-       writel(lower_32_bits(cpu_addr), base);
-       writel(upper_32_bits(cpu_addr), base + 0x04);
-       writel(lower_32_bits(mask), base + 0x08);
-       writel(upper_32_bits(mask), base + 0x0c);
-       writel(lower_32_bits(pci_addr), base + 0x10);
-       writel(upper_32_bits(pci_addr), base + 0x14);
+       xgene_pcie_writel(port, offset, lower_32_bits(cpu_addr));
+       xgene_pcie_writel(port, offset + 0x04, upper_32_bits(cpu_addr));
+       xgene_pcie_writel(port, offset + 0x08, lower_32_bits(mask));
+       xgene_pcie_writel(port, offset + 0x0c, upper_32_bits(mask));
+       xgene_pcie_writel(port, offset + 0x10, lower_32_bits(pci_addr));
+       xgene_pcie_writel(port, offset + 0x14, upper_32_bits(pci_addr));
 }
 
-static void xgene_pcie_setup_cfg_reg(void __iomem *csr_base, u64 addr)
+static void xgene_pcie_setup_cfg_reg(struct xgene_pcie_port *port)
 {
-       writel(lower_32_bits(addr), csr_base + CFGBARL);
-       writel(upper_32_bits(addr), csr_base + CFGBARH);
-       writel(EN_REG, csr_base + CFGCTL);
+       u64 addr = port->cfg_addr;
+
+       xgene_pcie_writel(port, CFGBARL, lower_32_bits(addr));
+       xgene_pcie_writel(port, CFGBARH, upper_32_bits(addr));
+       xgene_pcie_writel(port, CFGCTL, EN_REG);
 }
 
 static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
@@ -310,7 +323,7 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
                struct resource *res = window->res;
                u64 restype = resource_type(res);
 
-               dev_dbg(port->dev, "%pR\n", res);
+               dev_dbg(dev, "%pR\n", res);
 
                switch (restype) {
                case IORESOURCE_IO:
@@ -339,17 +352,18 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port,
                        return -EINVAL;
                }
        }
-       xgene_pcie_setup_cfg_reg(port->csr_base, port->cfg_addr);
-
+       xgene_pcie_setup_cfg_reg(port);
        return 0;
 }
 
-static void xgene_pcie_setup_pims(void *addr, u64 pim, u64 size)
+static void xgene_pcie_setup_pims(struct xgene_pcie_port *port, u32 pim_reg,
+                                 u64 pim, u64 size)
 {
-       writel(lower_32_bits(pim), addr);
-       writel(upper_32_bits(pim) | EN_COHERENCY, addr + 0x04);
-       writel(lower_32_bits(size), addr + 0x10);
-       writel(upper_32_bits(size), addr + 0x14);
+       xgene_pcie_writel(port, pim_reg, lower_32_bits(pim));
+       xgene_pcie_writel(port, pim_reg + 0x04,
+                         upper_32_bits(pim) | EN_COHERENCY);
+       xgene_pcie_writel(port, pim_reg + 0x10, lower_32_bits(size));
+       xgene_pcie_writel(port, pim_reg + 0x14, upper_32_bits(size));
 }
 
 /*
@@ -379,10 +393,10 @@ static int xgene_pcie_select_ib_reg(u8 *ib_reg_mask, u64 size)
 static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
                                    struct of_pci_range *range, u8 *ib_reg_mask)
 {
-       void __iomem *csr_base = port->csr_base;
        void __iomem *cfg_base = port->cfg_base;
+       struct device *dev = port->dev;
        void *bar_addr;
-       void *pim_addr;
+       u32 pim_reg;
        u64 cpu_addr = range->cpu_addr;
        u64 pci_addr = range->pci_addr;
        u64 size = range->size;
@@ -393,7 +407,7 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
 
        region = xgene_pcie_select_ib_reg(ib_reg_mask, range->size);
        if (region < 0) {
-               dev_warn(port->dev, "invalid pcie dma-range config\n");
+               dev_warn(dev, "invalid pcie dma-range config\n");
                return;
        }
 
@@ -403,29 +417,27 @@ static void xgene_pcie_setup_ib_reg(struct xgene_pcie_port *port,
        bar_low = pcie_bar_low_val((u32)cpu_addr, flags);
        switch (region) {
        case 0:
-               xgene_pcie_set_ib_mask(csr_base, BRIDGE_CFG_4, flags, size);
+               xgene_pcie_set_ib_mask(port, BRIDGE_CFG_4, flags, size);
                bar_addr = cfg_base + PCI_BASE_ADDRESS_0;
                writel(bar_low, bar_addr);
                writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
-               pim_addr = csr_base + PIM1_1L;
+               pim_reg = PIM1_1L;
                break;
        case 1:
-               bar_addr = csr_base + IBAR2;
-               writel(bar_low, bar_addr);
-               writel(lower_32_bits(mask), csr_base + IR2MSK);
-               pim_addr = csr_base + PIM2_1L;
+               xgene_pcie_writel(port, IBAR2, bar_low);
+               xgene_pcie_writel(port, IR2MSK, lower_32_bits(mask));
+               pim_reg = PIM2_1L;
                break;
        case 2:
-               bar_addr = csr_base + IBAR3L;
-               writel(bar_low, bar_addr);
-               writel(upper_32_bits(cpu_addr), bar_addr + 0x4);
-               writel(lower_32_bits(mask), csr_base + IR3MSKL);
-               writel(upper_32_bits(mask), csr_base + IR3MSKL + 0x4);
-               pim_addr = csr_base + PIM3_1L;
+               xgene_pcie_writel(port, IBAR3L, bar_low);
+               xgene_pcie_writel(port, IBAR3L + 0x4, upper_32_bits(cpu_addr));
+               xgene_pcie_writel(port, IR3MSKL, lower_32_bits(mask));
+               xgene_pcie_writel(port, IR3MSKL + 0x4, upper_32_bits(mask));
+               pim_reg = PIM3_1L;
                break;
        }
 
-       xgene_pcie_setup_pims(pim_addr, pci_addr, ~(size - 1));
+       xgene_pcie_setup_pims(port, pim_reg, pci_addr, ~(size - 1));
 }
 
 static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
@@ -463,7 +475,7 @@ static int xgene_pcie_parse_map_dma_ranges(struct xgene_pcie_port *port)
        for_each_of_pci_range(&parser, &range) {
                u64 end = range.cpu_addr + range.size - 1;
 
-               dev_dbg(port->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
+               dev_dbg(dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
                        range.flags, range.cpu_addr, end, range.pci_addr);
                xgene_pcie_setup_ib_reg(port, &range, &ib_reg_mask);
        }
@@ -476,13 +488,14 @@ static void xgene_pcie_clear_config(struct xgene_pcie_port *port)
        int i;
 
        for (i = PIM1_1L; i <= CFGCTL; i += 4)
-               writel(0x0, port->csr_base + i);
+               xgene_pcie_writel(port, i, 0);
 }
 
 static int xgene_pcie_setup(struct xgene_pcie_port *port,
                            struct list_head *res,
                            resource_size_t io_base)
 {
+       struct device *dev = port->dev;
        u32 val, lanes = 0, speed = 0;
        int ret;
 
@@ -490,7 +503,7 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 
        /* setup the vendor and device IDs correctly */
        val = (XGENE_PCIE_DEVICEID << 16) | XGENE_PCIE_VENDORID;
-       writel(val, port->csr_base + BRIDGE_CFG_0);
+       xgene_pcie_writel(port, BRIDGE_CFG_0, val);
 
        ret = xgene_pcie_map_ranges(port, res, io_base);
        if (ret)
@@ -502,27 +515,28 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port,
 
        xgene_pcie_linkup(port, &lanes, &speed);
        if (!port->link_up)
-               dev_info(port->dev, "(rc) link down\n");
+               dev_info(dev, "(rc) link down\n");
        else
-               dev_info(port->dev, "(rc) x%d gen-%d link up\n",
-                               lanes, speed + 1);
+               dev_info(dev, "(rc) x%d gen-%d link up\n", lanes, speed + 1);
        return 0;
 }
 
 static int xgene_pcie_probe_bridge(struct platform_device *pdev)
 {
-       struct device_node *dn = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct device_node *dn = dev->of_node;
        struct xgene_pcie_port *port;
        resource_size_t iobase = 0;
        struct pci_bus *bus;
        int ret;
        LIST_HEAD(res);
 
-       port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+       port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
        if (!port)
                return -ENOMEM;
-       port->node = of_node_get(pdev->dev.of_node);
-       port->dev = &pdev->dev;
+
+       port->node = of_node_get(dn);
+       port->dev = dev;
 
        port->version = XGENE_PCIE_IP_VER_UNKN;
        if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
@@ -540,7 +554,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = devm_request_pci_bus_resources(&pdev->dev, &res);
+       ret = devm_request_pci_bus_resources(dev, &res);
        if (ret)
                goto error;
 
@@ -548,8 +562,7 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        if (ret)
                goto error;
 
-       bus = pci_create_root_bus(&pdev->dev, 0,
-                                       &xgene_pcie_ops, port, &res);
+       bus = pci_create_root_bus(dev, 0, &xgene_pcie_ops, port, &res);
        if (!bus) {
                ret = -ENOMEM;
                goto error;
@@ -558,8 +571,6 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
        pci_bus_add_devices(bus);
-
-       platform_set_drvdata(pdev, port);
        return 0;
 
 error:
index c24e965..b0ac4df 100644 (file)
 #define TLP_PAYLOAD_SIZE               0x01
 #define TLP_READ_TAG                   0x1d
 #define TLP_WRITE_TAG                  0x10
-#define TLP_CFG_DW0(fmttype)           (((fmttype) << 24) | TLP_PAYLOAD_SIZE)
-#define TLP_CFG_DW1(reqid, tag, be)    (((reqid) << 16) | (tag << 8) | (be))
+#define RP_DEVFN                       0
+#define TLP_REQ_ID(bus, devfn)         (((bus) << 8) | (devfn))
+#define TLP_CFG_DW0(pcie, bus)                                         \
+    ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0                 \
+                                   : TLP_FMTTYPE_CFGRD1) << 24) |      \
+     TLP_PAYLOAD_SIZE)
+#define TLP_CFG_DW1(pcie, tag, be)     \
+    (((TLP_REQ_ID(pcie->root_bus_nr,  RP_DEVFN)) << 16) | (tag << 8) | (be))
 #define TLP_CFG_DW2(bus, devfn, offset)        \
                                (((bus) << 24) | ((devfn) << 16) | (offset))
-#define TLP_REQ_ID(bus, devfn)         (((bus) << 8) | (devfn))
 #define TLP_COMP_STATUS(s)             (((s) >> 12) & 7)
 #define TLP_HDR_SIZE                   3
 #define TLP_LOOP                       500
-#define RP_DEVFN                       0
 
 #define LINK_UP_TIMEOUT                        HZ
 #define LINK_RETRAIN_TIMEOUT           HZ
@@ -74,7 +78,7 @@
 
 struct altera_pcie {
        struct platform_device  *pdev;
-       void __iomem            *cra_base;
+       void __iomem            *cra_base;      /* DT Cra */
        int                     irq;
        u8                      root_bus_nr;
        struct irq_domain       *irq_domain;
@@ -131,7 +135,7 @@ static void tlp_write_tx(struct altera_pcie *pcie,
        cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL);
 }
 
-static bool altera_pcie_valid_config(struct altera_pcie *pcie,
+static bool altera_pcie_valid_device(struct altera_pcie *pcie,
                                     struct pci_bus *bus, int dev)
 {
        /* If there is no link, then there is no device */
@@ -218,13 +222,8 @@ static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn,
 {
        u32 headers[TLP_HDR_SIZE];
 
-       if (bus == pcie->root_bus_nr)
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD0);
-       else
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGRD1);
-
-       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
-                                       TLP_READ_TAG, byte_en);
+       headers[0] = TLP_CFG_DW0(pcie, bus);
+       headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
        headers[2] = TLP_CFG_DW2(bus, devfn, where);
 
        tlp_write_packet(pcie, headers, 0, false);
@@ -238,13 +237,8 @@ static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn,
        u32 headers[TLP_HDR_SIZE];
        int ret;
 
-       if (bus == pcie->root_bus_nr)
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR0);
-       else
-               headers[0] = TLP_CFG_DW0(TLP_FMTTYPE_CFGWR1);
-
-       headers[1] = TLP_CFG_DW1(TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN),
-                                       TLP_WRITE_TAG, byte_en);
+       headers[0] = TLP_CFG_DW0(pcie, bus);
+       headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en);
        headers[2] = TLP_CFG_DW2(bus, devfn, where);
 
        /* check alignment to Qword */
@@ -342,7 +336,7 @@ static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
        if (altera_pcie_hide_rc_bar(bus, devfn, where))
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
-       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
+       if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn))) {
                *value = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -359,7 +353,7 @@ static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
        if (altera_pcie_hide_rc_bar(bus, devfn, where))
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
-       if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
+       if (!altera_pcie_valid_device(pcie, bus, PCI_SLOT(devfn)))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size,
@@ -394,6 +388,7 @@ static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
 
 static void altera_wait_link_retrain(struct altera_pcie *pcie)
 {
+       struct device *dev = &pcie->pdev->dev;
        u16 reg16;
        unsigned long start_jiffies;
 
@@ -406,7 +401,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
                        break;
 
                if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) {
-                       dev_err(&pcie->pdev->dev, "link retrain timeout\n");
+                       dev_err(dev, "link retrain timeout\n");
                        break;
                }
                udelay(100);
@@ -419,7 +414,7 @@ static void altera_wait_link_retrain(struct altera_pcie *pcie)
                        break;
 
                if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
-                       dev_err(&pcie->pdev->dev, "link up timeout\n");
+                       dev_err(dev, "link up timeout\n");
                        break;
                }
                udelay(100);
@@ -460,7 +455,6 @@ static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
 {
        irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
        irq_set_chip_data(irq, domain->host_data);
-
        return 0;
 }
 
@@ -472,12 +466,14 @@ static void altera_pcie_isr(struct irq_desc *desc)
 {
        struct irq_chip *chip = irq_desc_get_chip(desc);
        struct altera_pcie *pcie;
+       struct device *dev;
        unsigned long status;
        u32 bit;
        u32 virq;
 
        chained_irq_enter(chip, desc);
        pcie = irq_desc_get_handler_data(desc);
+       dev = &pcie->pdev->dev;
 
        while ((status = cra_readl(pcie, P2A_INT_STATUS)
                & P2A_INT_STS_ALL) != 0) {
@@ -489,8 +485,7 @@ static void altera_pcie_isr(struct irq_desc *desc)
                        if (virq)
                                generic_handle_irq(virq);
                        else
-                               dev_err(&pcie->pdev->dev,
-                                       "unexpected IRQ, INT%d\n", bit);
+                               dev_err(dev, "unexpected IRQ, INT%d\n", bit);
                }
        }
 
@@ -549,30 +544,25 @@ static int altera_pcie_init_irq_domain(struct altera_pcie *pcie)
 
 static int altera_pcie_parse_dt(struct altera_pcie *pcie)
 {
-       struct resource *cra;
+       struct device *dev = &pcie->pdev->dev;
        struct platform_device *pdev = pcie->pdev;
+       struct resource *cra;
 
        cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra");
-       if (!cra) {
-               dev_err(&pdev->dev, "no Cra memory resource defined\n");
-               return -ENODEV;
-       }
-
-       pcie->cra_base = devm_ioremap_resource(&pdev->dev, cra);
+       pcie->cra_base = devm_ioremap_resource(dev, cra);
        if (IS_ERR(pcie->cra_base)) {
-               dev_err(&pdev->dev, "failed to map cra memory\n");
+               dev_err(dev, "failed to map cra memory\n");
                return PTR_ERR(pcie->cra_base);
        }
 
        /* setup IRQ */
        pcie->irq = platform_get_irq(pdev, 0);
        if (pcie->irq <= 0) {
-               dev_err(&pdev->dev, "failed to get IRQ: %d\n", pcie->irq);
+               dev_err(dev, "failed to get IRQ: %d\n", pcie->irq);
                return -EINVAL;
        }
 
        irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie);
-
        return 0;
 }
 
@@ -583,12 +573,13 @@ static void altera_pcie_host_init(struct altera_pcie *pcie)
 
 static int altera_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct altera_pcie *pcie;
        struct pci_bus *bus;
        struct pci_bus *child;
        int ret;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
@@ -596,7 +587,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
 
        ret = altera_pcie_parse_dt(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Parsing DT failed\n");
+               dev_err(dev, "Parsing DT failed\n");
                return ret;
        }
 
@@ -604,13 +595,13 @@ static int altera_pcie_probe(struct platform_device *pdev)
 
        ret = altera_pcie_parse_request_of_pci_ranges(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed add resources\n");
+               dev_err(dev, "Failed add resources\n");
                return ret;
        }
 
        ret = altera_pcie_init_irq_domain(pcie);
        if (ret) {
-               dev_err(&pdev->dev, "Failed creating IRQ Domain\n");
+               dev_err(dev, "Failed creating IRQ Domain\n");
                return ret;
        }
 
@@ -620,7 +611,7 @@ static int altera_pcie_probe(struct platform_device *pdev)
        cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
        altera_pcie_host_init(pcie);
 
-       bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
+       bus = pci_scan_root_bus(dev, pcie->root_bus_nr, &altera_pcie_ops,
                                pcie, &pcie->resources);
        if (!bus)
                return -ENOMEM;
@@ -633,8 +624,6 @@ static int altera_pcie_probe(struct platform_device *pdev)
                pcie_bus_configure_settings(child);
 
        pci_bus_add_devices(bus);
-
-       platform_set_drvdata(pdev, pcie);
        return ret;
 }
 
index 0f4f570..0ac0f18 100644 (file)
 #include "pcie-designware.h"
 
 struct armada8k_pcie {
-       void __iomem *base;
+       struct pcie_port pp;            /* pp.dbi_base is DT ctrl */
        struct clk *clk;
-       struct pcie_port pp;
 };
 
 #define PCIE_VENDOR_REGS_OFFSET                0x8000
 
-#define PCIE_GLOBAL_CONTROL_REG                0x0
+#define PCIE_GLOBAL_CONTROL_REG                (PCIE_VENDOR_REGS_OFFSET + 0x0)
 #define PCIE_APP_LTSSM_EN              BIT(2)
 #define PCIE_DEVICE_TYPE_SHIFT         4
 #define PCIE_DEVICE_TYPE_MASK          0xF
 #define PCIE_DEVICE_TYPE_RC            0x4 /* Root complex */
 
-#define PCIE_GLOBAL_STATUS_REG         0x8
+#define PCIE_GLOBAL_STATUS_REG         (PCIE_VENDOR_REGS_OFFSET + 0x8)
 #define PCIE_GLB_STS_RDLH_LINK_UP      BIT(1)
 #define PCIE_GLB_STS_PHY_LINK_UP       BIT(9)
 
-#define PCIE_GLOBAL_INT_CAUSE1_REG     0x1C
-#define PCIE_GLOBAL_INT_MASK1_REG      0x20
+#define PCIE_GLOBAL_INT_CAUSE1_REG     (PCIE_VENDOR_REGS_OFFSET + 0x1C)
+#define PCIE_GLOBAL_INT_MASK1_REG      (PCIE_VENDOR_REGS_OFFSET + 0x20)
 #define PCIE_INT_A_ASSERT_MASK         BIT(9)
 #define PCIE_INT_B_ASSERT_MASK         BIT(10)
 #define PCIE_INT_C_ASSERT_MASK         BIT(11)
 #define PCIE_INT_D_ASSERT_MASK         BIT(12)
 
-#define PCIE_ARCACHE_TRC_REG           0x50
-#define PCIE_AWCACHE_TRC_REG           0x54
-#define PCIE_ARUSER_REG                        0x5C
-#define PCIE_AWUSER_REG                        0x60
+#define PCIE_ARCACHE_TRC_REG           (PCIE_VENDOR_REGS_OFFSET + 0x50)
+#define PCIE_AWCACHE_TRC_REG           (PCIE_VENDOR_REGS_OFFSET + 0x54)
+#define PCIE_ARUSER_REG                        (PCIE_VENDOR_REGS_OFFSET + 0x5C)
+#define PCIE_AWUSER_REG                        (PCIE_VENDOR_REGS_OFFSET + 0x60)
 /*
  * AR/AW Cache defauls: Normal memory, Write-Back, Read / Write
  * allocate
@@ -72,11 +71,10 @@ struct armada8k_pcie {
 
 static int armada8k_pcie_link_up(struct pcie_port *pp)
 {
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
        u32 reg;
        u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
 
-       reg = readl(pcie->base + PCIE_GLOBAL_STATUS_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_STATUS_REG);
 
        if ((reg & mask) == mask)
                return 1;
@@ -85,51 +83,50 @@ static int armada8k_pcie_link_up(struct pcie_port *pp)
        return 0;
 }
 
-static void armada8k_pcie_establish_link(struct pcie_port *pp)
+static void armada8k_pcie_establish_link(struct armada8k_pcie *pcie)
 {
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
-       void __iomem *base = pcie->base;
+       struct pcie_port *pp = &pcie->pp;
        u32 reg;
 
        if (!dw_pcie_link_up(pp)) {
                /* Disable LTSSM state machine to enable configuration */
-               reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+               reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
                reg &= ~(PCIE_APP_LTSSM_EN);
-               writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+               dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
        }
 
        /* Set the device to root complex mode */
-       reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
        reg &= ~(PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_SHIFT);
        reg |= PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_SHIFT;
-       writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
 
        /* Set the PCIe master AxCache attributes */
-       writel(ARCACHE_DEFAULT_VALUE, base + PCIE_ARCACHE_TRC_REG);
-       writel(AWCACHE_DEFAULT_VALUE, base + PCIE_AWCACHE_TRC_REG);
+       dw_pcie_writel_rc(pp, PCIE_ARCACHE_TRC_REG, ARCACHE_DEFAULT_VALUE);
+       dw_pcie_writel_rc(pp, PCIE_AWCACHE_TRC_REG, AWCACHE_DEFAULT_VALUE);
 
        /* Set the PCIe master AxDomain attributes */
-       reg = readl(base + PCIE_ARUSER_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_ARUSER_REG);
        reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
        reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
-       writel(reg, base + PCIE_ARUSER_REG);
+       dw_pcie_writel_rc(pp, PCIE_ARUSER_REG, reg);
 
-       reg = readl(base + PCIE_AWUSER_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_AWUSER_REG);
        reg &= ~(AX_USER_DOMAIN_MASK << AX_USER_DOMAIN_SHIFT);
        reg |= DOMAIN_OUTER_SHAREABLE << AX_USER_DOMAIN_SHIFT;
-       writel(reg, base + PCIE_AWUSER_REG);
+       dw_pcie_writel_rc(pp, PCIE_AWUSER_REG, reg);
 
        /* Enable INT A-D interrupts */
-       reg = readl(base + PCIE_GLOBAL_INT_MASK1_REG);
+       reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_MASK1_REG);
        reg |= PCIE_INT_A_ASSERT_MASK | PCIE_INT_B_ASSERT_MASK |
               PCIE_INT_C_ASSERT_MASK | PCIE_INT_D_ASSERT_MASK;
-       writel(reg, base + PCIE_GLOBAL_INT_MASK1_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_MASK1_REG, reg);
 
        if (!dw_pcie_link_up(pp)) {
                /* Configuration done. Start LTSSM */
-               reg = readl(base + PCIE_GLOBAL_CONTROL_REG);
+               reg = dw_pcie_readl_rc(pp, PCIE_GLOBAL_CONTROL_REG);
                reg |= PCIE_APP_LTSSM_EN;
-               writel(reg, base + PCIE_GLOBAL_CONTROL_REG);
+               dw_pcie_writel_rc(pp, PCIE_GLOBAL_CONTROL_REG, reg);
        }
 
        /* Wait until the link becomes active again */
@@ -139,15 +136,16 @@ static void armada8k_pcie_establish_link(struct pcie_port *pp)
 
 static void armada8k_pcie_host_init(struct pcie_port *pp)
 {
+       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
+
        dw_pcie_setup_rc(pp);
-       armada8k_pcie_establish_link(pp);
+       armada8k_pcie_establish_link(pcie);
 }
 
 static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct armada8k_pcie *pcie = to_armada8k_pcie(pp);
-       void __iomem *base = pcie->base;
+       struct armada8k_pcie *pcie = arg;
+       struct pcie_port *pp = &pcie->pp;
        u32 val;
 
        /*
@@ -155,8 +153,8 @@ static irqreturn_t armada8k_pcie_irq_handler(int irq, void *arg)
         * PCI device. However, they are also latched into the PCIe
         * controller, so we simply discard them.
         */
-       val = readl(base + PCIE_GLOBAL_INT_CAUSE1_REG);
-       writel(val, base + PCIE_GLOBAL_INT_CAUSE1_REG);
+       val = dw_pcie_readl_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG);
+       dw_pcie_writel_rc(pp, PCIE_GLOBAL_INT_CAUSE1_REG, val);
 
        return IRQ_HANDLED;
 }
@@ -166,9 +164,10 @@ static struct pcie_host_ops armada8k_pcie_host_ops = {
        .host_init = armada8k_pcie_host_init,
 };
 
-static int armada8k_add_pcie_port(struct pcie_port *pp,
+static int armada8k_add_pcie_port(struct armada8k_pcie *pcie,
                                  struct platform_device *pdev)
 {
+       struct pcie_port *pp = &pcie->pp;
        struct device *dev = &pdev->dev;
        int ret;
 
@@ -182,7 +181,7 @@ static int armada8k_add_pcie_port(struct pcie_port *pp,
        }
 
        ret = devm_request_irq(dev, pp->irq, armada8k_pcie_irq_handler,
-                              IRQF_SHARED, "armada8k-pcie", pp);
+                              IRQF_SHARED, "armada8k-pcie", pcie);
        if (ret) {
                dev_err(dev, "failed to request irq %d\n", pp->irq);
                return ret;
@@ -217,7 +216,6 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
 
        pp = &pcie->pp;
        pp->dev = dev;
-       platform_set_drvdata(pdev, pcie);
 
        /* Get the dw-pcie unit configuration/control registers base. */
        base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
@@ -228,9 +226,7 @@ static int armada8k_pcie_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       pcie->base = pp->dbi_base + PCIE_VENDOR_REGS_OFFSET;
-
-       ret = armada8k_add_pcie_port(pp, pdev);
+       ret = armada8k_add_pcie_port(pcie, pdev);
        if (ret)
                goto fail;
 
index 39bf1a6..212786b 100644 (file)
@@ -27,9 +27,9 @@
 #define to_artpec6_pcie(x)     container_of(x, struct artpec6_pcie, pp)
 
 struct artpec6_pcie {
-       struct pcie_port        pp;
-       struct regmap           *regmap;
-       void __iomem            *phy_base;
+       struct pcie_port        pp;             /* pp.dbi_base is DT dbi */
+       struct regmap           *regmap;        /* DT axis,syscon-pcie */
+       void __iomem            *phy_base;      /* DT phy */
 };
 
 /* PCIe Port Logic registers (memory-mapped) */
@@ -65,18 +65,31 @@ struct artpec6_pcie {
 
 #define ARTPEC6_CPU_TO_BUS_ADDR                0x0fffffff
 
-static int artpec6_pcie_establish_link(struct pcie_port *pp)
+static u32 artpec6_pcie_readl(struct artpec6_pcie *artpec6_pcie, u32 offset)
 {
-       struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp);
+       u32 val;
+
+       regmap_read(artpec6_pcie->regmap, offset, &val);
+       return val;
+}
+
+static void artpec6_pcie_writel(struct artpec6_pcie *artpec6_pcie, u32 offset, u32 val)
+{
+       regmap_write(artpec6_pcie->regmap, offset, val);
+}
+
+static int artpec6_pcie_establish_link(struct artpec6_pcie *artpec6_pcie)
+{
+       struct pcie_port *pp = &artpec6_pcie->pp;
        u32 val;
        unsigned int retries;
 
        /* Hold DW core in reset */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_CORE_RESET_REQ;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
 
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |=  PCIECFG_RISRCREN |      /* Receiver term. 50 Ohm */
                PCIECFG_MODE_TX_DRV_EN |
                PCIECFG_CISRREN |       /* Reference clock term. 100 Ohm */
@@ -84,27 +97,27 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        val |= PCIECFG_REFCLK_ENABLE;
        val &= ~PCIECFG_DBG_OEN;
        val &= ~PCIECFG_CLKREQ_B;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(5000, 6000);
 
-       regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
        val |= NOCCFG_ENABLE_CLK_PCIE;
-       regmap_write(artpec6_pcie->regmap, NOCCFG, val);
+       artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
        usleep_range(20, 30);
 
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_PCLK_ENABLE | PCIECFG_PLL_ENABLE;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(6000, 7000);
 
-       regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
        val &= ~NOCCFG_POWER_PCIE_IDLEREQ;
-       regmap_write(artpec6_pcie->regmap, NOCCFG, val);
+       artpec6_pcie_writel(artpec6_pcie, NOCCFG, val);
 
        retries = 50;
        do {
                usleep_range(1000, 2000);
-               regmap_read(artpec6_pcie->regmap, NOCCFG, &val);
+               val = artpec6_pcie_readl(artpec6_pcie, NOCCFG);
                retries--;
        } while (retries &&
                (val & (NOCCFG_POWER_PCIE_IDLEACK | NOCCFG_POWER_PCIE_IDLE)));
@@ -117,16 +130,16 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        } while (retries && !(val & PHY_COSPLLLOCK));
 
        /* Take DW core out of reset */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val &= ~PCIECFG_CORE_RESET_REQ;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
        usleep_range(100, 200);
 
        /*
         * Enable writing to config regs. This is required as the Synopsys
         * driver changes the class code. That register needs DBI write enable.
         */
-       writel(DBI_RO_WR_EN, pp->dbi_base + MISC_CONTROL_1_OFF);
+       dw_pcie_writel_rc(pp, MISC_CONTROL_1_OFF, DBI_RO_WR_EN);
 
        pp->io_base &= ARTPEC6_CPU_TO_BUS_ADDR;
        pp->mem_base &= ARTPEC6_CPU_TO_BUS_ADDR;
@@ -137,78 +150,69 @@ static int artpec6_pcie_establish_link(struct pcie_port *pp)
        dw_pcie_setup_rc(pp);
 
        /* assert LTSSM enable */
-       regmap_read(artpec6_pcie->regmap, PCIECFG, &val);
+       val = artpec6_pcie_readl(artpec6_pcie, PCIECFG);
        val |= PCIECFG_LTSSM_ENABLE;
-       regmap_write(artpec6_pcie->regmap, PCIECFG, val);
+       artpec6_pcie_writel(artpec6_pcie, PCIECFG, val);
 
        /* check if the link is up or not */
        if (!dw_pcie_wait_for_link(pp))
                return 0;
 
        dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
-               readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R0),
+               dw_pcie_readl_rc(pp, PCIE_PHY_DEBUG_R1));
 
        return -ETIMEDOUT;
 }
 
-static void artpec6_pcie_enable_interrupts(struct pcie_port *pp)
+static void artpec6_pcie_enable_interrupts(struct artpec6_pcie *artpec6_pcie)
 {
+       struct pcie_port *pp = &artpec6_pcie->pp;
+
        if (IS_ENABLED(CONFIG_PCI_MSI))
                dw_pcie_msi_init(pp);
 }
 
 static void artpec6_pcie_host_init(struct pcie_port *pp)
 {
-       artpec6_pcie_establish_link(pp);
-       artpec6_pcie_enable_interrupts(pp);
-}
-
-static int artpec6_pcie_link_up(struct pcie_port *pp)
-{
-       u32 rc;
-
-       /*
-        * Get status from Synopsys IP
-        * link is debug bit 36, debug register 1 starts at bit 32
-        */
-       rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32));
-       if (rc)
-               return 1;
+       struct artpec6_pcie *artpec6_pcie = to_artpec6_pcie(pp);
 
-       return 0;
+       artpec6_pcie_establish_link(artpec6_pcie);
+       artpec6_pcie_enable_interrupts(artpec6_pcie);
 }
 
 static struct pcie_host_ops artpec6_pcie_host_ops = {
-       .link_up = artpec6_pcie_link_up,
        .host_init = artpec6_pcie_host_init,
 };
 
 static irqreturn_t artpec6_pcie_msi_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
+       struct artpec6_pcie *artpec6_pcie = arg;
+       struct pcie_port *pp = &artpec6_pcie->pp;
 
        return dw_handle_msi_irq(pp);
 }
 
-static int artpec6_add_pcie_port(struct pcie_port *pp,
+static int artpec6_add_pcie_port(struct artpec6_pcie *artpec6_pcie,
                                 struct platform_device *pdev)
 {
+       struct pcie_port *pp = &artpec6_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                pp->msi_irq = platform_get_irq_byname(pdev, "msi");
                if (pp->msi_irq <= 0) {
-                       dev_err(&pdev->dev, "failed to get MSI irq\n");
+                       dev_err(dev, "failed to get MSI irq\n");
                        return -ENODEV;
                }
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                       artpec6_pcie_msi_handler,
                                       IRQF_SHARED | IRQF_NO_THREAD,
-                                      "artpec6-pcie-msi", pp);
+                                      "artpec6-pcie-msi", artpec6_pcie);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI irq\n");
+                       dev_err(dev, "failed to request MSI irq\n");
                        return ret;
                }
        }
@@ -218,7 +222,7 @@ static int artpec6_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -227,41 +231,40 @@ static int artpec6_add_pcie_port(struct pcie_port *pp,
 
 static int artpec6_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct artpec6_pcie *artpec6_pcie;
        struct pcie_port *pp;
        struct resource *dbi_base;
        struct resource *phy_base;
        int ret;
 
-       artpec6_pcie = devm_kzalloc(&pdev->dev, sizeof(*artpec6_pcie),
-                                   GFP_KERNEL);
+       artpec6_pcie = devm_kzalloc(dev, sizeof(*artpec6_pcie), GFP_KERNEL);
        if (!artpec6_pcie)
                return -ENOMEM;
 
        pp = &artpec6_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
-       pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+       pp->dbi_base = devm_ioremap_resource(dev, dbi_base);
        if (IS_ERR(pp->dbi_base))
                return PTR_ERR(pp->dbi_base);
 
        phy_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
-       artpec6_pcie->phy_base = devm_ioremap_resource(&pdev->dev, phy_base);
+       artpec6_pcie->phy_base = devm_ioremap_resource(dev, phy_base);
        if (IS_ERR(artpec6_pcie->phy_base))
                return PTR_ERR(artpec6_pcie->phy_base);
 
        artpec6_pcie->regmap =
-               syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+               syscon_regmap_lookup_by_phandle(dev->of_node,
                                                "axis,syscon-pcie");
        if (IS_ERR(artpec6_pcie->regmap))
                return PTR_ERR(artpec6_pcie->regmap);
 
-       ret = artpec6_add_pcie_port(pp, pdev);
+       ret = artpec6_add_pcie_port(artpec6_pcie, pdev);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, artpec6_pcie);
        return 0;
 }
 
index 17da005..537f58a 100644 (file)
@@ -25,8 +25,7 @@
 #include "pcie-designware.h"
 
 struct dw_plat_pcie {
-       void __iomem            *mem_base;
-       struct pcie_port        pp;
+       struct pcie_port        pp;     /* pp.dbi_base is DT 0th resource */
 };
 
 static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
@@ -52,6 +51,7 @@ static struct pcie_host_ops dw_plat_pcie_host_ops = {
 static int dw_plat_add_pcie_port(struct pcie_port *pp,
                                 struct platform_device *pdev)
 {
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 1);
@@ -63,11 +63,11 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
                if (pp->msi_irq < 0)
                        return pp->msi_irq;
 
-               ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+               ret = devm_request_irq(dev, pp->msi_irq,
                                        dw_plat_pcie_msi_irq_handler,
                                        IRQF_SHARED, "dw-plat-pcie-msi", pp);
                if (ret) {
-                       dev_err(&pdev->dev, "failed to request MSI IRQ\n");
+                       dev_err(dev, "failed to request MSI IRQ\n");
                        return ret;
                }
        }
@@ -77,7 +77,7 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -86,31 +86,28 @@ static int dw_plat_add_pcie_port(struct pcie_port *pp,
 
 static int dw_plat_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct dw_plat_pcie *dw_plat_pcie;
        struct pcie_port *pp;
        struct resource *res;  /* Resource from DT */
        int ret;
 
-       dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
-                                       GFP_KERNEL);
+       dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL);
        if (!dw_plat_pcie)
                return -ENOMEM;
 
        pp = &dw_plat_pcie->pp;
-       pp->dev = &pdev->dev;
+       pp->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dw_plat_pcie->mem_base))
-               return PTR_ERR(dw_plat_pcie->mem_base);
-
-       pp->dbi_base = dw_plat_pcie->mem_base;
+       pp->dbi_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pp->dbi_base))
+               return PTR_ERR(pp->dbi_base);
 
        ret = dw_plat_add_pcie_port(pp, pdev);
        if (ret < 0)
                return ret;
 
-       platform_set_drvdata(pdev, dw_plat_pcie);
        return 0;
 }
 
index 74da71e..035f50c 100644 (file)
@@ -141,41 +141,35 @@ int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val)
        return PCIBIOS_SUCCESSFUL;
 }
 
-static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
+u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
 {
        if (pp->ops->readl_rc)
-               return pp->ops->readl_rc(pp, pp->dbi_base + reg);
+               return pp->ops->readl_rc(pp, reg);
 
        return readl(pp->dbi_base + reg);
 }
 
-static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
+void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val)
 {
        if (pp->ops->writel_rc)
-               pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
+               pp->ops->writel_rc(pp, reg, val);
        else
                writel(val, pp->dbi_base + reg);
 }
 
-static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
+static u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
-       if (pp->ops->readl_rc)
-               return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);
-
-       return readl(pp->dbi_base + offset + reg);
+       return dw_pcie_readl_rc(pp, offset + reg);
 }
 
-static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
-                                        u32 val, u32 reg)
+static void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index, u32 reg,
+                                 u32 val)
 {
        u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
 
-       if (pp->ops->writel_rc)
-               pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
-       else
-               writel(val, pp->dbi_base + offset + reg);
+       dw_pcie_writel_rc(pp, offset + reg, val);
 }
 
 static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
@@ -202,35 +196,35 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
        u32 retries, val;
 
        if (pp->iatu_unroll_enabled) {
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
-               dw_pcie_writel_unroll(pp, index,
-                       upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
-               dw_pcie_writel_unroll(pp, index,
-                       lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
-               dw_pcie_writel_unroll(pp, index,
-                       upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
-               dw_pcie_writel_unroll(pp, index,
-                       type, PCIE_ATU_UNR_REGION_CTRL1);
-               dw_pcie_writel_unroll(pp, index,
-                       PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_BASE,
+                       lower_32_bits(cpu_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_BASE,
+                       upper_32_bits(cpu_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LIMIT,
+                       lower_32_bits(cpu_addr + size - 1));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_LOWER_TARGET,
+                       lower_32_bits(pci_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_UPPER_TARGET,
+                       upper_32_bits(pci_addr));
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL1,
+                       type);
+               dw_pcie_writel_unroll(pp, index, PCIE_ATU_UNR_REGION_CTRL2,
+                       PCIE_ATU_ENABLE);
        } else {
-               dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
-                                               PCIE_ATU_VIEWPORT);
-               dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
-                                               PCIE_ATU_LOWER_BASE);
-               dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
-                                               PCIE_ATU_UPPER_BASE);
-               dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
-                                               PCIE_ATU_LIMIT);
-               dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
-                                               PCIE_ATU_LOWER_TARGET);
-               dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
-                                               PCIE_ATU_UPPER_TARGET);
-               dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
-               dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
+               dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT,
+                                 PCIE_ATU_REGION_OUTBOUND | index);
+               dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_BASE,
+                                 lower_32_bits(cpu_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_BASE,
+                                 upper_32_bits(cpu_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_LIMIT,
+                                 lower_32_bits(cpu_addr + size - 1));
+               dw_pcie_writel_rc(pp, PCIE_ATU_LOWER_TARGET,
+                                 lower_32_bits(pci_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_UPPER_TARGET,
+                                 upper_32_bits(pci_addr));
+               dw_pcie_writel_rc(pp, PCIE_ATU_CR1, type);
+               dw_pcie_writel_rc(pp, PCIE_ATU_CR2, PCIE_ATU_ENABLE);
        }
 
        /*
@@ -760,8 +754,8 @@ static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
        return ret;
 }
 
-static int dw_pcie_valid_config(struct pcie_port *pp,
-                               struct pci_bus *bus, int dev)
+static int dw_pcie_valid_device(struct pcie_port *pp, struct pci_bus *bus,
+                               int dev)
 {
        /* If there is no link, then there is no device */
        if (bus->number != pp->root_bus_nr) {
@@ -781,7 +775,7 @@ static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 {
        struct pcie_port *pp = bus->sysdata;
 
-       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
+       if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn))) {
                *val = 0xffffffff;
                return PCIBIOS_DEVICE_NOT_FOUND;
        }
@@ -797,7 +791,7 @@ static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 {
        struct pcie_port *pp = bus->sysdata;
 
-       if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
+       if (!dw_pcie_valid_device(pp, bus, PCI_SLOT(devfn)))
                return PCIBIOS_DEVICE_NOT_FOUND;
 
        if (bus->number == pp->root_bus_nr)
@@ -835,7 +829,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                dev_err(pp->dev, "num-lanes %u: invalid value\n", pp->lanes);
                return;
        }
-       dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_PORT_LINK_CONTROL, val);
 
        /* set link width speed control register */
        val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
@@ -854,30 +848,30 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                val |= PORT_LOGIC_LINK_WIDTH_8_LANES;
                break;
        }
-       dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
+       dw_pcie_writel_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
 
        /* setup RC BARs */
-       dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0);
-       dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_0, 0x00000004);
+       dw_pcie_writel_rc(pp, PCI_BASE_ADDRESS_1, 0x00000000);
 
        /* setup interrupt pins */
        val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE);
        val &= 0xffff00ff;
        val |= 0x00000100;
-       dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
+       dw_pcie_writel_rc(pp, PCI_INTERRUPT_LINE, val);
 
        /* setup bus numbers */
        val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
        val &= 0xff000000;
        val |= 0x00010100;
-       dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
+       dw_pcie_writel_rc(pp, PCI_PRIMARY_BUS, val);
 
        /* setup command register */
        val = dw_pcie_readl_rc(pp, PCI_COMMAND);
        val &= 0xffff0000;
        val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
                PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-       dw_pcie_writel_rc(pp, val, PCI_COMMAND);
+       dw_pcie_writel_rc(pp, PCI_COMMAND, val);
 
        /*
         * If the platform provides ->rd_other_conf, it means the platform
index c8e5bc6..a567ea2 100644 (file)
@@ -54,9 +54,8 @@ struct pcie_port {
 };
 
 struct pcie_host_ops {
-       u32 (*readl_rc)(struct pcie_port *pp, void __iomem *dbi_base);
-       void (*writel_rc)(struct pcie_port *pp,
-                       u32 val, void __iomem *dbi_base);
+       u32 (*readl_rc)(struct pcie_port *pp, u32 reg);
+       void (*writel_rc)(struct pcie_port *pp, u32 reg, u32 val);
        int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
        int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
        int (*rd_other_conf)(struct pcie_port *pp, struct pci_bus *bus,
@@ -73,6 +72,8 @@ struct pcie_host_ops {
        int (*msi_host_init)(struct pcie_port *pp, struct msi_controller *chip);
 };
 
+u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg);
+void dw_pcie_writel_rc(struct pcie_port *pp, u32 reg, u32 val);
 int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
 int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
index 7ee9dfc..56154c2 100644 (file)
 
 #include "pcie-designware.h"
 
-#define PCIE_LTSSM_LINKUP_STATE                                0x11
-#define PCIE_LTSSM_STATE_MASK                          0x3F
-#define PCIE_SUBCTRL_SYS_STATE4_REG                    0x6818
-#define PCIE_SYS_STATE4                                                0x31c
-#define PCIE_HIP06_CTRL_OFF                                    0x1000
+#define PCIE_SUBCTRL_SYS_STATE4_REG            0x6818
+#define PCIE_HIP06_CTRL_OFF                    0x1000
+#define PCIE_SYS_STATE4                                (PCIE_HIP06_CTRL_OFF + 0x31c)
+#define PCIE_LTSSM_LINKUP_STATE                        0x11
+#define PCIE_LTSSM_STATE_MASK                  0x3F
 
 #define to_hisi_pcie(x)        container_of(x, struct hisi_pcie, pp)
 
 struct hisi_pcie;
 
 struct pcie_soc_ops {
-       int (*hisi_pcie_link_up)(struct hisi_pcie *pcie);
+       int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie);
 };
 
 struct hisi_pcie {
+       struct pcie_port pp;            /* pp.dbi_base is DT rc_dbi */
        struct regmap *subctrl;
-       void __iomem *reg_base;
        u32 port_id;
-       struct pcie_port pp;
        struct pcie_soc_ops *soc_ops;
 };
 
-static inline void hisi_pcie_apb_writel(struct hisi_pcie *pcie,
-                                       u32 val, u32 reg)
-{
-       writel(val, pcie->reg_base + reg);
-}
-
-static inline u32 hisi_pcie_apb_readl(struct hisi_pcie *pcie, u32 reg)
-{
-       return readl(pcie->reg_base + reg);
-}
-
 /* HipXX PCIe host only supports 32-bit config access */
 static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size,
                              u32 *val)
 {
        u32 reg;
        u32 reg_val;
-       struct hisi_pcie *pcie = to_hisi_pcie(pp);
        void *walker = &reg_val;
 
        walker += (where & 0x3);
        reg = where & ~0x3;
-       reg_val = hisi_pcie_apb_readl(pcie, reg);
+       reg_val = dw_pcie_readl_rc(pp, reg);
 
        if (size == 1)
                *val = *(u8 __force *) walker;
@@ -86,21 +73,20 @@ static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int  size,
 {
        u32 reg_val;
        u32 reg;
-       struct hisi_pcie *pcie = to_hisi_pcie(pp);
        void *walker = &reg_val;
 
        walker += (where & 0x3);
        reg = where & ~0x3;
        if (size == 4)
-               hisi_pcie_apb_writel(pcie, val, reg);
+               dw_pcie_writel_rc(pp, reg, val);
        else if (size == 2) {
-               reg_val = hisi_pcie_apb_readl(pcie, reg);
+               reg_val = dw_pcie_readl_rc(pp, reg);
                *(u16 __force *) walker = val;
-               hisi_pcie_apb_writel(pcie, reg_val, reg);
+               dw_pcie_writel_rc(pp, reg, reg_val);
        } else if (size == 1) {
-               reg_val = hisi_pcie_apb_readl(pcie, reg);
+               reg_val = dw_pcie_readl_rc(pp, reg);
                *(u8 __force *) walker = val;
-               hisi_pcie_apb_writel(pcie, reg_val, reg);
+               dw_pcie_writel_rc(pp, reg, reg_val);
        } else
                return PCIBIOS_BAD_REGISTER_NUMBER;
 
@@ -119,10 +105,10 @@ static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie)
 
 static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie)
 {
+       struct pcie_port *pp = &hisi_pcie->pp;
        u32 val;
 
-       val = hisi_pcie_apb_readl(hisi_pcie, PCIE_HIP06_CTRL_OFF +
-                       PCIE_SYS_STATE4);
+       val = dw_pcie_readl_rc(pp, PCIE_SYS_STATE4);
 
        return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE);
 }
@@ -140,19 +126,20 @@ static struct pcie_host_ops hisi_pcie_host_ops = {
        .link_up = hisi_pcie_link_up,
 };
 
-static int hisi_add_pcie_port(struct pcie_port *pp,
-                                    struct platform_device *pdev)
+static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie,
+                             struct platform_device *pdev)
 {
+       struct pcie_port *pp = &hisi_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
        u32 port_id;
-       struct hisi_pcie *hisi_pcie = to_hisi_pcie(pp);
 
-       if (of_property_read_u32(pdev->dev.of_node, "port-id", &port_id)) {
-               dev_err(&pdev->dev, "failed to read port-id\n");
+       if (of_property_read_u32(dev->of_node, "port-id", &port_id)) {
+               dev_err(dev, "failed to read port-id\n");
                return -EINVAL;
        }
        if (port_id > 3) {
-               dev_err(&pdev->dev, "Invalid port-id: %d\n", port_id);
+               dev_err(dev, "Invalid port-id: %d\n", port_id);
                return -EINVAL;
        }
        hisi_pcie->port_id = port_id;
@@ -161,7 +148,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp,
 
        ret = dw_pcie_host_init(pp);
        if (ret) {
-               dev_err(&pdev->dev, "failed to initialize host\n");
+               dev_err(dev, "failed to initialize host\n");
                return ret;
        }
 
@@ -170,6 +157,7 @@ static int hisi_add_pcie_port(struct pcie_port *pp,
 
 static int hisi_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct hisi_pcie *hisi_pcie;
        struct pcie_port *pp;
        const struct of_device_id *match;
@@ -177,40 +165,36 @@ static int hisi_pcie_probe(struct platform_device *pdev)
        struct device_driver *driver;
        int ret;
 
-       hisi_pcie = devm_kzalloc(&pdev->dev, sizeof(*hisi_pcie), GFP_KERNEL);
+       hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL);
        if (!hisi_pcie)
                return -ENOMEM;
 
        pp = &hisi_pcie->pp;
-       pp->dev = &pdev->dev;
-       driver = (pdev->dev).driver;
+       pp->dev = dev;
+       driver = dev->driver;
 
-       match = of_match_device(driver->of_match_table, &pdev->dev);
+       match = of_match_device(driver->of_match_table, dev);
        hisi_pcie->soc_ops = (struct pcie_soc_ops *) match->data;
 
        hisi_pcie->subctrl =
        syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl");
        if (IS_ERR(hisi_pcie->subctrl)) {
-               dev_err(pp->dev, "cannot get subctrl base\n");
+               dev_err(dev, "cannot get subctrl base\n");
                return PTR_ERR(hisi_pcie->subctrl);
        }
 
        reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi");
-       hisi_pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg);
-       if (IS_ERR(hisi_pcie->reg_base)) {
-               dev_err(pp->dev, "cannot get rc_dbi base\n");
-               return PTR_ERR(hisi_pcie->reg_base);
+       pp->dbi_base = devm_ioremap_resource(dev, reg);
+       if (IS_ERR(pp->dbi_base)) {
+               dev_err(dev, "cannot get rc_dbi base\n");
+               return PTR_ERR(pp->dbi_base);
        }
 
-       hisi_pcie->pp.dbi_base = hisi_pcie->reg_base;
-
-       ret = hisi_add_pcie_port(pp, pdev);
+       ret = hisi_add_pcie_port(hisi_pcie, pdev);
        if (ret)
                return ret;
 
-       platform_set_drvdata(pdev, hisi_pcie);
-
-       dev_warn(pp->dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
+       dev_warn(dev, "only 32-bit config accesses supported; smaller writes may corrupt adjacent RW1C fields\n");
 
        return 0;
 }
index 0d7bee4..8ce0890 100644 (file)
@@ -42,19 +42,24 @@ static int iproc_pcie_bcma_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 
 static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 {
+       struct device *dev = &bdev->dev;
        struct iproc_pcie *pcie;
        LIST_HEAD(res);
        struct resource res_mem;
        int ret;
 
-       pcie = devm_kzalloc(&bdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &bdev->dev;
-       bcma_set_drvdata(bdev, pcie);
+       pcie->dev = dev;
 
        pcie->base = bdev->io_addr;
+       if (!pcie->base) {
+               dev_err(dev, "no controller registers\n");
+               return -ENOMEM;
+       }
+
        pcie->base_addr = bdev->addr;
 
        res_mem.start = bdev->addr_s[0];
@@ -67,10 +72,11 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)
 
        ret = iproc_pcie_setup(pcie, &res);
        if (ret)
-               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               dev_err(dev, "PCIe controller setup failed\n");
 
        pci_free_resource_list(&res);
 
+       bcma_set_drvdata(bdev, pcie);
        return ret;
 }
 
index 1738c52..a3de087 100644 (file)
@@ -40,35 +40,35 @@ MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
 
 static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        const struct of_device_id *of_id;
        struct iproc_pcie *pcie;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource reg;
        resource_size_t iobase = 0;
        LIST_HEAD(res);
        int ret;
 
-       of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
+       of_id = of_match_device(iproc_pcie_of_match_table, dev);
        if (!of_id)
                return -EINVAL;
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
        pcie->type = (enum iproc_pcie_type)of_id->data;
-       platform_set_drvdata(pdev, pcie);
 
        ret = of_address_to_resource(np, 0, &reg);
        if (ret < 0) {
-               dev_err(pcie->dev, "unable to obtain controller resources\n");
+               dev_err(dev, "unable to obtain controller resources\n");
                return ret;
        }
 
-       pcie->base = devm_ioremap(pcie->dev, reg.start, resource_size(&reg));
+       pcie->base = devm_ioremap(dev, reg.start, resource_size(&reg));
        if (!pcie->base) {
-               dev_err(pcie->dev, "unable to map controller registers\n");
+               dev_err(dev, "unable to map controller registers\n");
                return -ENOMEM;
        }
        pcie->base_addr = reg.start;
@@ -79,7 +79,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
                                           &val);
                if (ret) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "missing brcm,pcie-ob-axi-offset property\n");
                        return ret;
                }
@@ -88,7 +88,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
                ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
                                           &val);
                if (ret) {
-                       dev_err(pcie->dev,
+                       dev_err(dev,
                                "missing brcm,pcie-ob-window-size property\n");
                        return ret;
                }
@@ -101,7 +101,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
        }
 
        /* PHY use is optional */
-       pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
+       pcie->phy = devm_phy_get(dev, "pcie-phy");
        if (IS_ERR(pcie->phy)) {
                if (PTR_ERR(pcie->phy) == -EPROBE_DEFER)
                        return -EPROBE_DEFER;
@@ -110,7 +110,7 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 
        ret = of_pci_get_host_bridge_resources(np, 0, 0xff, &res, &iobase);
        if (ret) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "unable to get PCI host bridge resources\n");
                return ret;
        }
@@ -119,10 +119,11 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
 
        ret = iproc_pcie_setup(pcie, &res);
        if (ret)
-               dev_err(pcie->dev, "PCIe controller setup failed\n");
+               dev_err(dev, "PCIe controller setup failed\n");
 
        pci_free_resource_list(&res);
 
+       platform_set_drvdata(pdev, pcie);
        return ret;
 }
 
index e167b2f..0b999a9 100644 (file)
@@ -63,6 +63,8 @@
 #define OARR_SIZE_CFG_SHIFT          1
 #define OARR_SIZE_CFG                BIT(OARR_SIZE_CFG_SHIFT)
 
+#define PCI_EXP_CAP                    0xac
+
 #define MAX_NUM_OB_WINDOWS           2
 
 #define IPROC_PCIE_REG_INVALID 0xffff
@@ -258,9 +260,10 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
 
 static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 {
+       struct device *dev = pcie->dev;
        u8 hdr_type;
        u32 link_ctrl, class, val;
-       u16 pos, link_status;
+       u16 pos = PCI_EXP_CAP, link_status;
        bool link_is_active = false;
 
        /*
@@ -272,14 +275,14 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
 
        val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
        if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
-               dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+               dev_err(dev, "PHY or data link is INACTIVE!\n");
                return -ENODEV;
        }
 
        /* make sure we are not in EP mode */
        pci_bus_read_config_byte(bus, 0, PCI_HEADER_TYPE, &hdr_type);
        if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) {
-               dev_err(pcie->dev, "in EP mode, hdr=%#02x\n", hdr_type);
+               dev_err(dev, "in EP mode, hdr=%#02x\n", hdr_type);
                return -EFAULT;
        }
 
@@ -293,30 +296,27 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
        pci_bus_write_config_dword(bus, 0, PCI_BRIDGE_CTRL_REG_OFFSET, class);
 
        /* check link status to see if link is active */
-       pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA, &link_status);
        if (link_status & PCI_EXP_LNKSTA_NLW)
                link_is_active = true;
 
        if (!link_is_active) {
                /* try GEN 1 link speed */
-#define PCI_LINK_STATUS_CTRL_2_OFFSET 0x0dc
 #define PCI_TARGET_LINK_SPEED_MASK    0xf
 #define PCI_TARGET_LINK_SPEED_GEN2    0x2
 #define PCI_TARGET_LINK_SPEED_GEN1    0x1
                pci_bus_read_config_dword(bus, 0,
-                                         PCI_LINK_STATUS_CTRL_2_OFFSET,
+                                         pos + PCI_EXP_LNKCTL2,
                                          &link_ctrl);
                if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) ==
                    PCI_TARGET_LINK_SPEED_GEN2) {
                        link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK;
                        link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1;
                        pci_bus_write_config_dword(bus, 0,
-                                          PCI_LINK_STATUS_CTRL_2_OFFSET,
+                                          pos + PCI_EXP_LNKCTL2,
                                           link_ctrl);
                        msleep(100);
 
-                       pos = pci_bus_find_capability(bus, 0, PCI_CAP_ID_EXP);
                        pci_bus_read_config_word(bus, 0, pos + PCI_EXP_LNKSTA,
                                                 &link_status);
                        if (link_status & PCI_EXP_LNKSTA_NLW)
@@ -324,7 +324,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
                }
        }
 
-       dev_info(pcie->dev, "link: %s\n", link_is_active ? "UP" : "DOWN");
+       dev_info(dev, "link: %s\n", link_is_active ? "UP" : "DOWN");
 
        return link_is_active ? 0 : -ENODEV;
 }
@@ -349,12 +349,13 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
                               u64 pci_addr, resource_size_t size)
 {
        struct iproc_pcie_ob *ob = &pcie->ob;
+       struct device *dev = pcie->dev;
        unsigned i;
        u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
        u64 remainder;
 
        if (size > max_size) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "res size %pap exceeds max supported size 0x%llx\n",
                        &size, max_size);
                return -EINVAL;
@@ -362,15 +363,14 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 
        div64_u64_rem(size, ob->window_size, &remainder);
        if (remainder) {
-               dev_err(pcie->dev,
+               dev_err(dev,
                        "res size %pap needs to be multiple of window size %pap\n",
                        &size, &ob->window_size);
                return -EINVAL;
        }
 
        if (axi_addr < ob->axi_offset) {
-               dev_err(pcie->dev,
-                       "axi address %pap less than offset %pap\n",
+               dev_err(dev, "axi address %pap less than offset %pap\n",
                        &axi_addr, &ob->axi_offset);
                return -EINVAL;
        }
@@ -406,6 +406,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
 static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
                                 struct list_head *resources)
 {
+       struct device *dev = pcie->dev;
        struct resource_entry *window;
        int ret;
 
@@ -425,7 +426,7 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
                                return ret;
                        break;
                default:
-                       dev_err(pcie->dev, "invalid resource %pR\n", res);
+                       dev_err(dev, "invalid resource %pR\n", res);
                        return -EINVAL;
                }
        }
@@ -455,26 +456,25 @@ static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
 
 int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 {
+       struct device *dev;
        int ret;
        void *sysdata;
        struct pci_bus *bus;
 
-       if (!pcie || !pcie->dev || !pcie->base)
-               return -EINVAL;
-
-       ret = devm_request_pci_bus_resources(pcie->dev, res);
+       dev = pcie->dev;
+       ret = devm_request_pci_bus_resources(dev, res);
        if (ret)
                return ret;
 
        ret = phy_init(pcie->phy);
        if (ret) {
-               dev_err(pcie->dev, "unable to initialize PCIe PHY\n");
+               dev_err(dev, "unable to initialize PCIe PHY\n");
                return ret;
        }
 
        ret = phy_power_on(pcie->phy);
        if (ret) {
-               dev_err(pcie->dev, "unable to power on PCIe PHY\n");
+               dev_err(dev, "unable to power on PCIe PHY\n");
                goto err_exit_phy;
        }
 
@@ -486,7 +486,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
                pcie->reg_offsets = iproc_pcie_reg_paxc;
                break;
        default:
-               dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
+               dev_err(dev, "incompatible iProc PCIe interface\n");
                ret = -EINVAL;
                goto err_power_off_phy;
        }
@@ -496,7 +496,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
        if (pcie->need_ob_cfg) {
                ret = iproc_pcie_map_ranges(pcie, res);
                if (ret) {
-                       dev_err(pcie->dev, "map failed\n");
+                       dev_err(dev, "map failed\n");
                        goto err_power_off_phy;
                }
        }
@@ -508,9 +508,9 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
        sysdata = pcie;
 #endif
 
-       bus = pci_create_root_bus(pcie->dev, 0, &iproc_pcie_ops, sysdata, res);
+       bus = pci_create_root_bus(dev, 0, &iproc_pcie_ops, sysdata, res);
        if (!bus) {
-               dev_err(pcie->dev, "unable to create PCI root bus\n");
+               dev_err(dev, "unable to create PCI root bus\n");
                ret = -ENOMEM;
                goto err_power_off_phy;
        }
@@ -518,7 +518,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        ret = iproc_pcie_check_link(pcie, bus);
        if (ret) {
-               dev_err(pcie->dev, "no PCIe EP device detected\n");
+               dev_err(dev, "no PCIe EP device detected\n");
                goto err_rm_root_bus;
        }
 
@@ -526,7 +526,7 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
                if (iproc_pcie_msi_enable(pcie))
-                       dev_info(pcie->dev, "not using iProc MSI\n");
+                       dev_info(dev, "not using iProc MSI\n");
 
        pci_scan_child_bus(bus);
        pci_assign_unassigned_bus_resources(bus);
index 5ec2d44..ef0a84c 100644 (file)
@@ -86,12 +86,10 @@ struct qcom_pcie_ops {
 };
 
 struct qcom_pcie {
-       struct pcie_port pp;
-       struct device *dev;
+       struct pcie_port pp;                    /* pp.dbi_base is DT dbi */
+       void __iomem *parf;                     /* DT parf */
+       void __iomem *elbi;                     /* DT elbi */
        union qcom_pcie_resources res;
-       void __iomem *parf;
-       void __iomem *dbi;
-       void __iomem *elbi;
        struct phy *phy;
        struct gpio_desc *reset;
        struct qcom_pcie_ops *ops;
@@ -136,7 +134,7 @@ static int qcom_pcie_establish_link(struct qcom_pcie *pcie)
 static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
 
        res->vdda = devm_regulator_get(dev, "vdda");
        if (IS_ERR(res->vdda))
@@ -188,7 +186,7 @@ static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie)
 static int qcom_pcie_get_resources_v1(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
 
        res->vdda = devm_regulator_get(dev, "vdda");
        if (IS_ERR(res->vdda))
@@ -237,7 +235,7 @@ static void qcom_pcie_deinit_v0(struct qcom_pcie *pcie)
 static int qcom_pcie_init_v0(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v0 *res = &pcie->res.v0;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
        u32 val;
        int ret;
 
@@ -359,7 +357,7 @@ static void qcom_pcie_deinit_v1(struct qcom_pcie *pcie)
 static int qcom_pcie_init_v1(struct qcom_pcie *pcie)
 {
        struct qcom_pcie_resources_v1 *res = &pcie->res.v1;
-       struct device *dev = pcie->dev;
+       struct device *dev = pcie->pp.dev;
        int ret;
 
        ret = reset_control_deassert(res->core);
@@ -426,7 +424,7 @@ err_res:
 static int qcom_pcie_link_up(struct pcie_port *pp)
 {
        struct qcom_pcie *pcie = to_qcom_pcie(pp);
-       u16 val = readw(pcie->dbi + PCIE20_CAP + PCI_EXP_LNKSTA);
+       u16 val = readw(pcie->pp.dbi_base + PCIE20_CAP + PCI_EXP_LNKSTA);
 
        return !!(val & PCI_EXP_LNKSTA_DLLLA);
 }
@@ -509,8 +507,8 @@ static int qcom_pcie_probe(struct platform_device *pdev)
        if (!pcie)
                return -ENOMEM;
 
+       pp = &pcie->pp;
        pcie->ops = (struct qcom_pcie_ops *)of_device_get_match_data(dev);
-       pcie->dev = dev;
 
        pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_LOW);
        if (IS_ERR(pcie->reset))
@@ -522,9 +520,9 @@ static int qcom_pcie_probe(struct platform_device *pdev)
                return PTR_ERR(pcie->parf);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
-       pcie->dbi = devm_ioremap_resource(dev, res);
-       if (IS_ERR(pcie->dbi))
-               return PTR_ERR(pcie->dbi);
+       pp->dbi_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pp->dbi_base))
+               return PTR_ERR(pp->dbi_base);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi");
        pcie->elbi = devm_ioremap_resource(dev, res);
@@ -539,9 +537,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       pp = &pcie->pp;
        pp->dev = dev;
-       pp->dbi_base = pcie->dbi;
        pp->root_bus_nr = -1;
        pp->ops = &qcom_pcie_dw_ops;
 
@@ -569,8 +565,6 @@ static int qcom_pcie_probe(struct platform_device *pdev)
                return ret;
        }
 
-       platform_set_drvdata(pdev, pcie);
-
        return 0;
 }
 
index e06b1d3..62700d1 100644 (file)
@@ -31,8 +31,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
-#define DRV_NAME "rcar-pcie"
-
 #define PCIECAR                        0x000010
 #define PCIECCTLR              0x000018
 #define  CONFIG_SEND_ENABLE    (1 << 31)
@@ -397,6 +395,7 @@ static int rcar_pcie_setup(struct list_head *resource, struct rcar_pcie *pci)
 
 static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        unsigned int timeout = 1000;
        u32 macsr;
 
@@ -404,7 +403,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                return;
 
        if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) {
-               dev_err(pcie->dev, "Speed change already in progress\n");
+               dev_err(dev, "Speed change already in progress\n");
                return;
        }
 
@@ -433,7 +432,7 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                        rcar_pci_write_reg(pcie, macsr, MACSR);
 
                        if (macsr & SPCHGFAIL)
-                               dev_err(pcie->dev, "Speed change failed\n");
+                               dev_err(dev, "Speed change failed\n");
 
                        goto done;
                }
@@ -441,15 +440,16 @@ static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
                msleep(1);
        };
 
-       dev_err(pcie->dev, "Speed change timed out\n");
+       dev_err(dev, "Speed change timed out\n");
 
 done:
-       dev_info(pcie->dev, "Current link speed is %s GT/s\n",
+       dev_info(dev, "Current link speed is %s GT/s\n",
                 (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5");
 }
 
 static int rcar_pcie_enable(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct pci_bus *bus, *child;
        LIST_HEAD(res);
 
@@ -461,14 +461,14 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
        pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
 
        if (IS_ENABLED(CONFIG_PCI_MSI))
-               bus = pci_scan_root_bus_msi(pcie->dev, pcie->root_bus_nr,
+               bus = pci_scan_root_bus_msi(dev, pcie->root_bus_nr,
                                &rcar_pcie_ops, pcie, &res, &pcie->msi.chip);
        else
-               bus = pci_scan_root_bus(pcie->dev, pcie->root_bus_nr,
+               bus = pci_scan_root_bus(dev, pcie->root_bus_nr,
                                &rcar_pcie_ops, pcie, &res);
 
        if (!bus) {
-               dev_err(pcie->dev, "Scanning rootbus failed");
+               dev_err(dev, "Scanning rootbus failed");
                return -ENODEV;
        }
 
@@ -487,6 +487,7 @@ static int rcar_pcie_enable(struct rcar_pcie *pcie)
 
 static int phy_wait_for_ack(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        unsigned int timeout = 100;
 
        while (timeout--) {
@@ -496,7 +497,7 @@ static int phy_wait_for_ack(struct rcar_pcie *pcie)
                udelay(100);
        }
 
-       dev_err(pcie->dev, "Access to PCIe phy timed out\n");
+       dev_err(dev, "Access to PCIe phy timed out\n");
 
        return -ETIMEDOUT;
 }
@@ -697,6 +698,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
 {
        struct rcar_pcie *pcie = data;
        struct rcar_msi *msi = &pcie->msi;
+       struct device *dev = pcie->dev;
        unsigned long reg;
 
        reg = rcar_pci_read_reg(pcie, PCIEMSIFR);
@@ -717,10 +719,10 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data)
                        if (test_bit(index, msi->used))
                                generic_handle_irq(irq);
                        else
-                               dev_info(pcie->dev, "unhandled MSI\n");
+                               dev_info(dev, "unhandled MSI\n");
                } else {
                        /* Unknown MSI, just clear it */
-                       dev_dbg(pcie->dev, "unexpected MSI\n");
+                       dev_dbg(dev, "unexpected MSI\n");
                }
 
                /* see if there's any more pending in this vector */
@@ -843,22 +845,22 @@ static const struct irq_domain_ops msi_domain_ops = {
 
 static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
        struct rcar_msi *msi = &pcie->msi;
        unsigned long base;
        int err, i;
 
        mutex_init(&msi->lock);
 
-       msi->chip.dev = pcie->dev;
+       msi->chip.dev = dev;
        msi->chip.setup_irq = rcar_msi_setup_irq;
        msi->chip.setup_irqs = rcar_msi_setup_irqs;
        msi->chip.teardown_irq = rcar_msi_teardown_irq;
 
-       msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
+       msi->domain = irq_domain_add_linear(dev->of_node, INT_PCI_MSI_NR,
                                            &msi_domain_ops, &msi->chip);
        if (!msi->domain) {
-               dev_err(&pdev->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
@@ -866,19 +868,19 @@ static int rcar_pcie_enable_msi(struct rcar_pcie *pcie)
                irq_create_mapping(msi->domain, i);
 
        /* Two irqs are for MSI, but they are also used for non-MSI irqs */
-       err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
+       err = devm_request_irq(dev, msi->irq1, rcar_pcie_msi_irq,
                               IRQF_SHARED | IRQF_NO_THREAD,
                               rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
-       err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq,
+       err = devm_request_irq(dev, msi->irq2, rcar_pcie_msi_irq,
                               IRQF_SHARED | IRQF_NO_THREAD,
                               rcar_msi_irq_chip.name, pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request IRQ: %d\n", err);
+               dev_err(dev, "failed to request IRQ: %d\n", err);
                goto err;
        }
 
@@ -899,32 +901,32 @@ err:
        return err;
 }
 
-static int rcar_pcie_get_resources(struct platform_device *pdev,
-                                  struct rcar_pcie *pcie)
+static int rcar_pcie_get_resources(struct rcar_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        struct resource res;
        int err, i;
 
-       err = of_address_to_resource(pdev->dev.of_node, 0, &res);
+       err = of_address_to_resource(dev->of_node, 0, &res);
        if (err)
                return err;
 
-       pcie->base = devm_ioremap_resource(&pdev->dev, &res);
+       pcie->base = devm_ioremap_resource(dev, &res);
        if (IS_ERR(pcie->base))
                return PTR_ERR(pcie->base);
 
-       pcie->clk = devm_clk_get(&pdev->dev, "pcie");
+       pcie->clk = devm_clk_get(dev, "pcie");
        if (IS_ERR(pcie->clk)) {
-               dev_err(pcie->dev, "cannot get platform clock\n");
+               dev_err(dev, "cannot get platform clock\n");
                return PTR_ERR(pcie->clk);
        }
        err = clk_prepare_enable(pcie->clk);
        if (err)
                return err;
 
-       pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
+       pcie->bus_clk = devm_clk_get(dev, "pcie_bus");
        if (IS_ERR(pcie->bus_clk)) {
-               dev_err(pcie->dev, "cannot get pcie bus clock\n");
+               dev_err(dev, "cannot get pcie bus clock\n");
                err = PTR_ERR(pcie->bus_clk);
                goto fail_clk;
        }
@@ -932,17 +934,17 @@ static int rcar_pcie_get_resources(struct platform_device *pdev,
        if (err)
                goto fail_clk;
 
-       i = irq_of_parse_and_map(pdev->dev.of_node, 0);
+       i = irq_of_parse_and_map(dev->of_node, 0);
        if (!i) {
-               dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+               dev_err(dev, "cannot get platform resources for msi interrupt\n");
                err = -ENOENT;
                goto err_map_reg;
        }
        pcie->msi.irq1 = i;
 
-       i = irq_of_parse_and_map(pdev->dev.of_node, 1);
+       i = irq_of_parse_and_map(dev->of_node, 1);
        if (!i) {
-               dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n");
+               dev_err(dev, "cannot get platform resources for msi interrupt\n");
                err = -ENOENT;
                goto err_map_reg;
        }
@@ -1119,60 +1121,60 @@ out_release_res:
 
 static int rcar_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct rcar_pcie *pcie;
        unsigned int data;
        const struct of_device_id *of_id;
        int err;
        int (*hw_init_fn)(struct rcar_pcie *);
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
-       platform_set_drvdata(pdev, pcie);
+       pcie->dev = dev;
 
        INIT_LIST_HEAD(&pcie->resources);
 
        rcar_pcie_parse_request_of_pci_ranges(pcie);
 
-       err = rcar_pcie_get_resources(pdev, pcie);
+       err = rcar_pcie_get_resources(pcie);
        if (err < 0) {
-               dev_err(&pdev->dev, "failed to request resources: %d\n", err);
+               dev_err(dev, "failed to request resources: %d\n", err);
                return err;
        }
 
-       err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
+       err = rcar_pcie_parse_map_dma_ranges(pcie, dev->of_node);
        if (err)
                return err;
 
-       of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
+       of_id = of_match_device(rcar_pcie_of_match, dev);
        if (!of_id || !of_id->data)
                return -EINVAL;
        hw_init_fn = of_id->data;
 
-       pm_runtime_enable(pcie->dev);
-       err = pm_runtime_get_sync(pcie->dev);
+       pm_runtime_enable(dev);
+       err = pm_runtime_get_sync(dev);
        if (err < 0) {
-               dev_err(pcie->dev, "pm_runtime_get_sync failed\n");
+               dev_err(dev, "pm_runtime_get_sync failed\n");
                goto err_pm_disable;
        }
 
        /* Failure to get a link might just be that no cards are inserted */
        err = hw_init_fn(pcie);
        if (err) {
-               dev_info(&pdev->dev, "PCIe link down\n");
+               dev_info(dev, "PCIe link down\n");
                err = 0;
                goto err_pm_put;
        }
 
        data = rcar_pci_read_reg(pcie, MACSR);
-       dev_info(&pdev->dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
+       dev_info(dev, "PCIe x%d: link up\n", (data >> 20) & 0x3f);
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = rcar_pcie_enable_msi(pcie);
                if (err < 0) {
-                       dev_err(&pdev->dev,
+                       dev_err(dev,
                                "failed to enable MSI support: %d\n",
                                err);
                        goto err_pm_put;
@@ -1186,16 +1188,16 @@ static int rcar_pcie_probe(struct platform_device *pdev)
        return 0;
 
 err_pm_put:
-       pm_runtime_put(pcie->dev);
+       pm_runtime_put(dev);
 
 err_pm_disable:
-       pm_runtime_disable(pcie->dev);
+       pm_runtime_disable(dev);
        return err;
 }
 
 static struct platform_driver rcar_pcie_driver = {
        .driver = {
-               .name = DRV_NAME,
+               .name = "rcar-pcie",
                .of_match_table = rcar_pcie_of_match,
                .suppress_bind_attrs = true,
        },
index b8c82fc..e0b22da 100644 (file)
@@ -972,7 +972,7 @@ static int rockchip_pcie_prog_ob_atu(struct rockchip_pcie *rockchip,
                return -EINVAL;
        if (region_no == 0) {
                if (AXI_REGION_0_SIZE < (2ULL << num_pass_bits))
-               return -EINVAL;
+                       return -EINVAL;
        }
        if (region_no != 0) {
                if (AXI_REGION_SIZE < (2ULL << num_pass_bits))
@@ -1091,8 +1091,6 @@ static int rockchip_pcie_probe(struct platform_device *pdev)
        if (err)
                goto err_vpcie;
 
-       platform_set_drvdata(pdev, rockchip);
-
        rockchip_pcie_enable_interrupts(rockchip);
 
        err = rockchip_pcie_init_irq_domain(rockchip);
index 09aed85..3cf197b 100644 (file)
 #include "pcie-designware.h"
 
 struct spear13xx_pcie {
+       struct pcie_port        pp;             /* DT dbi is pp.dbi_base */
        void __iomem            *app_base;
        struct phy              *phy;
        struct clk              *clk;
-       struct pcie_port        pp;
        bool                    is_gen1;
 };
 
@@ -57,96 +57,26 @@ struct pcie_app_reg {
 };
 
 /* CR0 ID */
-#define RX_LANE_FLIP_EN_ID                     0
-#define TX_LANE_FLIP_EN_ID                     1
-#define SYS_AUX_PWR_DET_ID                     2
 #define APP_LTSSM_ENABLE_ID                    3
-#define SYS_ATTEN_BUTTON_PRESSED_ID            4
-#define SYS_MRL_SENSOR_STATE_ID                        5
-#define SYS_PWR_FAULT_DET_ID                   6
-#define SYS_MRL_SENSOR_CHGED_ID                        7
-#define SYS_PRE_DET_CHGED_ID                   8
-#define SYS_CMD_CPLED_INT_ID                   9
-#define APP_INIT_RST_0_ID                      11
-#define APP_REQ_ENTR_L1_ID                     12
-#define APP_READY_ENTR_L23_ID                  13
-#define APP_REQ_EXIT_L1_ID                     14
-#define DEVICE_TYPE_EP                         (0 << 25)
-#define DEVICE_TYPE_LEP                                (1 << 25)
 #define DEVICE_TYPE_RC                         (4 << 25)
-#define SYS_INT_ID                             29
 #define MISCTRL_EN_ID                          30
 #define REG_TRANSLATION_ENABLE                 31
 
-/* CR1 ID */
-#define APPS_PM_XMT_TURNOFF_ID                 2
-#define APPS_PM_XMT_PME_ID                     5
-
 /* CR3 ID */
-#define XMLH_LTSSM_STATE_DETECT_QUIET          0x00
-#define XMLH_LTSSM_STATE_DETECT_ACT            0x01
-#define XMLH_LTSSM_STATE_POLL_ACTIVE           0x02
-#define XMLH_LTSSM_STATE_POLL_COMPLIANCE       0x03
-#define XMLH_LTSSM_STATE_POLL_CONFIG           0x04
-#define XMLH_LTSSM_STATE_PRE_DETECT_QUIET      0x05
-#define XMLH_LTSSM_STATE_DETECT_WAIT           0x06
-#define XMLH_LTSSM_STATE_CFG_LINKWD_START      0x07
-#define XMLH_LTSSM_STATE_CFG_LINKWD_ACEPT      0x08
-#define XMLH_LTSSM_STATE_CFG_LANENUM_WAIT      0x09
-#define XMLH_LTSSM_STATE_CFG_LANENUM_ACEPT     0x0A
-#define XMLH_LTSSM_STATE_CFG_COMPLETE          0x0B
-#define XMLH_LTSSM_STATE_CFG_IDLE              0x0C
-#define XMLH_LTSSM_STATE_RCVRY_LOCK            0x0D
-#define XMLH_LTSSM_STATE_RCVRY_SPEED           0x0E
-#define XMLH_LTSSM_STATE_RCVRY_RCVRCFG         0x0F
-#define XMLH_LTSSM_STATE_RCVRY_IDLE            0x10
-#define XMLH_LTSSM_STATE_L0                    0x11
-#define XMLH_LTSSM_STATE_L0S                   0x12
-#define XMLH_LTSSM_STATE_L123_SEND_EIDLE       0x13
-#define XMLH_LTSSM_STATE_L1_IDLE               0x14
-#define XMLH_LTSSM_STATE_L2_IDLE               0x15
-#define XMLH_LTSSM_STATE_L2_WAKE               0x16
-#define XMLH_LTSSM_STATE_DISABLED_ENTRY                0x17
-#define XMLH_LTSSM_STATE_DISABLED_IDLE         0x18
-#define XMLH_LTSSM_STATE_DISABLED              0x19
-#define XMLH_LTSSM_STATE_LPBK_ENTRY            0x1A
-#define XMLH_LTSSM_STATE_LPBK_ACTIVE           0x1B
-#define XMLH_LTSSM_STATE_LPBK_EXIT             0x1C
-#define XMLH_LTSSM_STATE_LPBK_EXIT_TIMEOUT     0x1D
-#define XMLH_LTSSM_STATE_HOT_RESET_ENTRY       0x1E
-#define XMLH_LTSSM_STATE_HOT_RESET             0x1F
-#define XMLH_LTSSM_STATE_MASK                  0x3F
 #define XMLH_LINK_UP                           (1 << 6)
 
-/* CR4 ID */
-#define CFG_MSI_EN_ID                          18
-
 /* CR6 */
-#define INTA_CTRL_INT                          (1 << 7)
-#define INTB_CTRL_INT                          (1 << 8)
-#define INTC_CTRL_INT                          (1 << 9)
-#define INTD_CTRL_INT                          (1 << 10)
 #define MSI_CTRL_INT                           (1 << 26)
 
-/* CR19 ID */
-#define VEN_MSI_REQ_ID                         11
-#define VEN_MSI_FUN_NUM_ID                     8
-#define VEN_MSI_TC_ID                          5
-#define VEN_MSI_VECTOR_ID                      0
-#define VEN_MSI_REQ_EN         ((u32)0x1 << VEN_MSI_REQ_ID)
-#define VEN_MSI_FUN_NUM_MASK   ((u32)0x7 << VEN_MSI_FUN_NUM_ID)
-#define VEN_MSI_TC_MASK                ((u32)0x7 << VEN_MSI_TC_ID)
-#define VEN_MSI_VECTOR_MASK    ((u32)0x1F << VEN_MSI_VECTOR_ID)
-
 #define EXP_CAP_ID_OFFSET                      0x70
 
 #define to_spear13xx_pcie(x)   container_of(x, struct spear13xx_pcie, pp)
 
-static int spear13xx_pcie_establish_link(struct pcie_port *pp)
+static int spear13xx_pcie_establish_link(struct spear13xx_pcie *spear13xx_pcie)
 {
-       u32 val;
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
+       u32 val;
        u32 exp_cap_off = EXP_CAP_ID_OFFSET;
 
        if (dw_pcie_link_up(pp)) {
@@ -203,9 +133,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
 
 static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
 {
-       struct pcie_port *pp = arg;
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct spear13xx_pcie *spear13xx_pcie = arg;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        unsigned int status;
 
        status = readl(&app_reg->int_sts);
@@ -220,9 +150,9 @@ static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
-static void spear13xx_pcie_enable_interrupts(struct pcie_port *pp)
+static void spear13xx_pcie_enable_interrupts(struct spear13xx_pcie *spear13xx_pcie)
 {
-       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+       struct pcie_port *pp = &spear13xx_pcie->pp;
        struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
 
        /* Enable MSI interrupt */
@@ -246,8 +176,10 @@ static int spear13xx_pcie_link_up(struct pcie_port *pp)
 
 static void spear13xx_pcie_host_init(struct pcie_port *pp)
 {
-       spear13xx_pcie_establish_link(pp);
-       spear13xx_pcie_enable_interrupts(pp);
+       struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
+
+       spear13xx_pcie_establish_link(spear13xx_pcie);
+       spear13xx_pcie_enable_interrupts(spear13xx_pcie);
 }
 
 static struct pcie_host_ops spear13xx_pcie_host_ops = {
@@ -255,10 +187,11 @@ static struct pcie_host_ops spear13xx_pcie_host_ops = {
        .host_init = spear13xx_pcie_host_init,
 };
 
-static int spear13xx_add_pcie_port(struct pcie_port *pp,
-                                        struct platform_device *pdev)
+static int spear13xx_add_pcie_port(struct spear13xx_pcie *spear13xx_pcie,
+                                  struct platform_device *pdev)
 {
-       struct device *dev = &pdev->dev;
+       struct pcie_port *pp = &spear13xx_pcie->pp;
+       struct device *dev = pp->dev;
        int ret;
 
        pp->irq = platform_get_irq(pdev, 0);
@@ -268,7 +201,7 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
        }
        ret = devm_request_irq(dev, pp->irq, spear13xx_pcie_irq_handler,
                               IRQF_SHARED | IRQF_NO_THREAD,
-                              "spear1340-pcie", pp);
+                              "spear1340-pcie", spear13xx_pcie);
        if (ret) {
                dev_err(dev, "failed to request irq %d\n", pp->irq);
                return ret;
@@ -288,10 +221,10 @@ static int spear13xx_add_pcie_port(struct pcie_port *pp,
 
 static int spear13xx_pcie_probe(struct platform_device *pdev)
 {
+       struct device *dev = &pdev->dev;
        struct spear13xx_pcie *spear13xx_pcie;
        struct pcie_port *pp;
-       struct device *dev = &pdev->dev;
-       struct device_node *np = pdev->dev.of_node;
+       struct device_node *np = dev->of_node;
        struct resource *dbi_base;
        int ret;
 
@@ -323,7 +256,6 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
        }
 
        pp = &spear13xx_pcie->pp;
-
        pp->dev = dev;
 
        dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi");
@@ -338,7 +270,7 @@ static int spear13xx_pcie_probe(struct platform_device *pdev)
        if (of_property_read_bool(np, "st,pcie-is-gen1"))
                spear13xx_pcie->is_gen1 = true;
 
-       ret = spear13xx_add_pcie_port(pp, pdev);
+       ret = spear13xx_add_pcie_port(spear13xx_pcie, pdev);
        if (ret < 0)
                goto fail_clk;
 
index 67eae41..43eaa4a 100644 (file)
@@ -212,6 +212,7 @@ static bool nwl_phy_link_up(struct nwl_pcie *pcie)
 
 static int nwl_wait_for_link(struct nwl_pcie *pcie)
 {
+       struct device *dev = pcie->dev;
        int retries;
 
        /* check if the link is up or not */
@@ -221,7 +222,7 @@ static int nwl_wait_for_link(struct nwl_pcie *pcie)
                usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
        }
 
-       dev_err(pcie->dev, "PHY link never came up\n");
+       dev_err(dev, "PHY link never came up\n");
        return -ETIMEDOUT;
 }
 
@@ -277,6 +278,7 @@ static struct pci_ops nwl_pcie_ops = {
 static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
 {
        struct nwl_pcie *pcie = data;
+       struct device *dev = pcie->dev;
        u32 misc_stat;
 
        /* Checking for misc interrupts */
@@ -286,45 +288,43 @@ static irqreturn_t nwl_pcie_misc_handler(int irq, void *data)
                return IRQ_NONE;
 
        if (misc_stat & MSGF_MISC_SR_RXMSG_OVER)
-               dev_err(pcie->dev, "Received Message FIFO Overflow\n");
+               dev_err(dev, "Received Message FIFO Overflow\n");
 
        if (misc_stat & MSGF_MISC_SR_SLAVE_ERR)
-               dev_err(pcie->dev, "Slave error\n");
+               dev_err(dev, "Slave error\n");
 
        if (misc_stat & MSGF_MISC_SR_MASTER_ERR)
-               dev_err(pcie->dev, "Master error\n");
+               dev_err(dev, "Master error\n");
 
        if (misc_stat & MSGF_MISC_SR_I_ADDR_ERR)
-               dev_err(pcie->dev,
-                       "In Misc Ingress address translation error\n");
+               dev_err(dev, "In Misc Ingress address translation error\n");
 
        if (misc_stat & MSGF_MISC_SR_E_ADDR_ERR)
-               dev_err(pcie->dev,
-                       "In Misc Egress address translation error\n");
+               dev_err(dev, "In Misc Egress address translation error\n");
 
        if (misc_stat & MSGF_MISC_SR_FATAL_AER)
-               dev_err(pcie->dev, "Fatal Error in AER Capability\n");
+               dev_err(dev, "Fatal Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER)
-               dev_err(pcie->dev, "Non-Fatal Error in AER Capability\n");
+               dev_err(dev, "Non-Fatal Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_CORR_AER)
-               dev_err(pcie->dev, "Correctable Error in AER Capability\n");
+               dev_err(dev, "Correctable Error in AER Capability\n");
 
        if (misc_stat & MSGF_MISC_SR_UR_DETECT)
-               dev_err(pcie->dev, "Unsupported request Detected\n");
+               dev_err(dev, "Unsupported request Detected\n");
 
        if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV)
-               dev_err(pcie->dev, "Non-Fatal Error Detected\n");
+               dev_err(dev, "Non-Fatal Error Detected\n");
 
        if (misc_stat & MSGF_MISC_SR_FATAL_DEV)
-               dev_err(pcie->dev, "Fatal Error Detected\n");
+               dev_err(dev, "Fatal Error Detected\n");
 
        if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH)
-               dev_info(pcie->dev, "Link Autonomous Bandwidth Management Status bit set\n");
+               dev_info(dev, "Link Autonomous Bandwidth Management Status bit set\n");
 
        if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH)
-               dev_info(pcie->dev, "Link Bandwidth Management Status bit set\n");
+               dev_info(dev, "Link Bandwidth Management Status bit set\n");
 
        /* Clear misc interrupt status */
        nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS);
@@ -494,20 +494,21 @@ static const struct irq_domain_ops dev_msi_domain_ops = {
 static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
 {
 #ifdef CONFIG_PCI_MSI
-       struct fwnode_handle *fwnode = of_node_to_fwnode(pcie->dev->of_node);
+       struct device *dev = pcie->dev;
+       struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
        struct nwl_msi *msi = &pcie->msi;
 
        msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR,
                                                &dev_msi_domain_ops, pcie);
        if (!msi->dev_domain) {
-               dev_err(pcie->dev, "failed to create dev IRQ domain\n");
+               dev_err(dev, "failed to create dev IRQ domain\n");
                return -ENOMEM;
        }
        msi->msi_domain = pci_msi_create_irq_domain(fwnode,
                                                    &nwl_msi_domain_info,
                                                    msi->dev_domain);
        if (!msi->msi_domain) {
-               dev_err(pcie->dev, "failed to create msi IRQ domain\n");
+               dev_err(dev, "failed to create msi IRQ domain\n");
                irq_domain_remove(msi->dev_domain);
                return -ENOMEM;
        }
@@ -517,12 +518,13 @@ static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
 
 static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
 {
-       struct device_node *node = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *node = dev->of_node;
        struct device_node *legacy_intc_node;
 
        legacy_intc_node = of_get_next_child(node, NULL);
        if (!legacy_intc_node) {
-               dev_err(pcie->dev, "No legacy intc node found\n");
+               dev_err(dev, "No legacy intc node found\n");
                return -EINVAL;
        }
 
@@ -532,7 +534,7 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
                                                        pcie);
 
        if (!pcie->legacy_irq_domain) {
-               dev_err(pcie->dev, "failed to create IRQ domain\n");
+               dev_err(dev, "failed to create IRQ domain\n");
                return -ENOMEM;
        }
 
@@ -542,7 +544,8 @@ static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie)
 
 static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        struct nwl_msi *msi = &pcie->msi;
        unsigned long base;
        int ret;
@@ -557,7 +560,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Get msi_1 IRQ number */
        msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1");
        if (msi->irq_msi1 < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi1);
+               dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi1);
                ret = -EINVAL;
                goto err;
        }
@@ -568,7 +571,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Get msi_0 IRQ number */
        msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0");
        if (msi->irq_msi0 < 0) {
-               dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi0);
+               dev_err(dev, "failed to get IRQ#%d\n", msi->irq_msi0);
                ret = -EINVAL;
                goto err;
        }
@@ -579,7 +582,7 @@ static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus)
        /* Check for msii_present bit */
        ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT;
        if (!ret) {
-               dev_err(pcie->dev, "MSI not present\n");
+               dev_err(dev, "MSI not present\n");
                ret = -EIO;
                goto err;
        }
@@ -628,13 +631,14 @@ err:
 
 static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 {
-       struct platform_device *pdev = to_platform_device(pcie->dev);
+       struct device *dev = pcie->dev;
+       struct platform_device *pdev = to_platform_device(dev);
        u32 breg_val, ecam_val, first_busno = 0;
        int err;
 
        breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT;
        if (!breg_val) {
-               dev_err(pcie->dev, "BREG is not present\n");
+               dev_err(dev, "BREG is not present\n");
                return breg_val;
        }
 
@@ -665,7 +669,7 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 
        ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT;
        if (!ecam_val) {
-               dev_err(pcie->dev, "ECAM is not present\n");
+               dev_err(dev, "ECAM is not present\n");
                return ecam_val;
        }
 
@@ -692,23 +696,23 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
        writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS));
 
        if (nwl_pcie_link_up(pcie))
-               dev_info(pcie->dev, "Link is UP\n");
+               dev_info(dev, "Link is UP\n");
        else
-               dev_info(pcie->dev, "Link is DOWN\n");
+               dev_info(dev, "Link is DOWN\n");
 
        /* Get misc IRQ number */
        pcie->irq_misc = platform_get_irq_byname(pdev, "misc");
        if (pcie->irq_misc < 0) {
-               dev_err(&pdev->dev, "failed to get misc IRQ %d\n",
+               dev_err(dev, "failed to get misc IRQ %d\n",
                        pcie->irq_misc);
                return -EINVAL;
        }
 
-       err = devm_request_irq(pcie->dev, pcie->irq_misc,
+       err = devm_request_irq(dev, pcie->irq_misc,
                               nwl_pcie_misc_handler, IRQF_SHARED,
                               "nwl_pcie:misc", pcie);
        if (err) {
-               dev_err(pcie->dev, "fail to register misc IRQ#%d\n",
+               dev_err(dev, "fail to register misc IRQ#%d\n",
                        pcie->irq_misc);
                return err;
        }
@@ -744,31 +748,32 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie)
 static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
                             struct platform_device *pdev)
 {
-       struct device_node *node = pcie->dev->of_node;
+       struct device *dev = pcie->dev;
+       struct device_node *node = dev->of_node;
        struct resource *res;
        const char *type;
 
        /* Check for device type */
        type = of_get_property(node, "device_type", NULL);
        if (!type || strcmp(type, "pci")) {
-               dev_err(pcie->dev, "invalid \"device_type\" %s\n", type);
+               dev_err(dev, "invalid \"device_type\" %s\n", type);
                return -EINVAL;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "breg");
-       pcie->breg_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->breg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->breg_base))
                return PTR_ERR(pcie->breg_base);
        pcie->phys_breg_base = res->start;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pcireg");
-       pcie->pcireg_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->pcireg_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->pcireg_base))
                return PTR_ERR(pcie->pcireg_base);
        pcie->phys_pcie_reg_base = res->start;
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
-       pcie->ecam_base = devm_ioremap_resource(pcie->dev, res);
+       pcie->ecam_base = devm_ioremap_resource(dev, res);
        if (IS_ERR(pcie->ecam_base))
                return PTR_ERR(pcie->ecam_base);
        pcie->phys_ecam_base = res->start;
@@ -776,8 +781,7 @@ static int nwl_pcie_parse_dt(struct nwl_pcie *pcie,
        /* Get intx IRQ number */
        pcie->irq_intx = platform_get_irq_byname(pdev, "intx");
        if (pcie->irq_intx < 0) {
-               dev_err(&pdev->dev, "failed to get intx IRQ %d\n",
-                       pcie->irq_intx);
+               dev_err(dev, "failed to get intx IRQ %d\n", pcie->irq_intx);
                return -EINVAL;
        }
 
@@ -794,7 +798,8 @@ static const struct of_device_id nwl_pcie_of_match[] = {
 
 static int nwl_pcie_probe(struct platform_device *pdev)
 {
-       struct device_node *node = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
        struct nwl_pcie *pcie;
        struct pci_bus *bus;
        struct pci_bus *child;
@@ -802,42 +807,42 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        resource_size_t iobase = 0;
        LIST_HEAD(res);
 
-       pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+       pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
        if (!pcie)
                return -ENOMEM;
 
-       pcie->dev = &pdev->dev;
+       pcie->dev = dev;
        pcie->ecam_value = NWL_ECAM_VALUE_DEFAULT;
 
        err = nwl_pcie_parse_dt(pcie, pdev);
        if (err) {
-               dev_err(pcie->dev, "Parsing DT failed\n");
+               dev_err(dev, "Parsing DT failed\n");
                return err;
        }
 
        err = nwl_pcie_bridge_init(pcie);
        if (err) {
-               dev_err(pcie->dev, "HW Initialization failed\n");
+               dev_err(dev, "HW Initialization failed\n");
                return err;
        }
 
        err = of_pci_get_host_bridge_resources(node, 0, 0xff, &res, &iobase);
        if (err) {
-               dev_err(pcie->dev, "Getting bridge resources failed\n");
+               dev_err(dev, "Getting bridge resources failed\n");
                return err;
        }
 
-       err = devm_request_pci_bus_resources(pcie->dev, &res);
+       err = devm_request_pci_bus_resources(dev, &res);
        if (err)
                goto error;
 
        err = nwl_pcie_init_irq_domain(pcie);
        if (err) {
-               dev_err(pcie->dev, "Failed creating IRQ Domain\n");
+               dev_err(dev, "Failed creating IRQ Domain\n");
                goto error;
        }
 
-       bus = pci_create_root_bus(&pdev->dev, pcie->root_busno,
+       bus = pci_create_root_bus(dev, pcie->root_busno,
                                  &nwl_pcie_ops, pcie, &res);
        if (!bus) {
                err = -ENOMEM;
@@ -847,8 +852,7 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                err = nwl_pcie_enable_msi(pcie, bus);
                if (err < 0) {
-                       dev_err(&pdev->dev,
-                               "failed to enable MSI support: %d\n", err);
+                       dev_err(dev, "failed to enable MSI support: %d\n", err);
                        goto error;
                }
        }
@@ -857,7 +861,6 @@ static int nwl_pcie_probe(struct platform_device *pdev)
        list_for_each_entry(child, &bus->children, node)
                pcie_bus_configure_settings(child);
        pci_bus_add_devices(bus);
-       platform_set_drvdata(pdev, pcie);
        return 0;
 
 error:
index be56803..c8616fa 100644 (file)
@@ -140,10 +140,11 @@ static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port)
  */
 static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port)
 {
+       struct device *dev = port->dev;
        unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR);
 
        if (val & XILINX_PCIE_RPEFR_ERR_VALID) {
-               dev_dbg(port->dev, "Requester ID %lu\n",
+               dev_dbg(dev, "Requester ID %lu\n",
                        val & XILINX_PCIE_RPEFR_REQ_ID);
                pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK,
                           XILINX_PCIE_REG_RPEFR);
@@ -228,11 +229,10 @@ static void xilinx_pcie_destroy_msi(unsigned int irq)
 
 /**
  * xilinx_pcie_assign_msi - Allocate MSI number
- * @port: PCIe port structure
  *
  * Return: A valid IRQ on success and error value on failure.
  */
-static int xilinx_pcie_assign_msi(struct xilinx_pcie_port *port)
+static int xilinx_pcie_assign_msi(void)
 {
        int pos;
 
@@ -275,7 +275,7 @@ static int xilinx_pcie_msi_setup_irq(struct msi_controller *chip,
        struct msi_msg msg;
        phys_addr_t msg_addr;
 
-       hwirq = xilinx_pcie_assign_msi(port);
+       hwirq = xilinx_pcie_assign_msi();
        if (hwirq < 0)
                return hwirq;
 
@@ -383,6 +383,7 @@ static const struct irq_domain_ops intx_domain_ops = {
 static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
 {
        struct xilinx_pcie_port *port = (struct xilinx_pcie_port *)data;
+       struct device *dev = port->dev;
        u32 val, mask, status, msi_data;
 
        /* Read interrupt decode and mask registers */
@@ -394,32 +395,32 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
                return IRQ_NONE;
 
        if (status & XILINX_PCIE_INTR_LINK_DOWN)
-               dev_warn(port->dev, "Link Down\n");
+               dev_warn(dev, "Link Down\n");
 
        if (status & XILINX_PCIE_INTR_ECRC_ERR)
-               dev_warn(port->dev, "ECRC failed\n");
+               dev_warn(dev, "ECRC failed\n");
 
        if (status & XILINX_PCIE_INTR_STR_ERR)
-               dev_warn(port->dev, "Streaming error\n");
+               dev_warn(dev, "Streaming error\n");
 
        if (status & XILINX_PCIE_INTR_HOT_RESET)
-               dev_info(port->dev, "Hot reset\n");
+               dev_info(dev, "Hot reset\n");
 
        if (status & XILINX_PCIE_INTR_CFG_TIMEOUT)
-               dev_warn(port->dev, "ECAM access timeout\n");
+               dev_warn(dev, "ECAM access timeout\n");
 
        if (status & XILINX_PCIE_INTR_CORRECTABLE) {
-               dev_warn(port->dev, "Correctable error message\n");
+               dev_warn(dev, "Correctable error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
        if (status & XILINX_PCIE_INTR_NONFATAL) {
-               dev_warn(port->dev, "Non fatal error message\n");
+               dev_warn(dev, "Non fatal error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
        if (status & XILINX_PCIE_INTR_FATAL) {
-               dev_warn(port->dev, "Fatal error message\n");
+               dev_warn(dev, "Fatal error message\n");
                xilinx_pcie_clear_err_interrupts(port);
        }
 
@@ -429,7 +430,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
 
                /* Check whether interrupt valid */
                if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
-                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       dev_warn(dev, "RP Intr FIFO1 read error\n");
                        goto error;
                }
 
@@ -451,7 +452,7 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
                val = pcie_read(port, XILINX_PCIE_REG_RPIFR1);
 
                if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
-                       dev_warn(port->dev, "RP Intr FIFO1 read error\n");
+                       dev_warn(dev, "RP Intr FIFO1 read error\n");
                        goto error;
                }
 
@@ -471,31 +472,31 @@ static irqreturn_t xilinx_pcie_intr_handler(int irq, void *data)
        }
 
        if (status & XILINX_PCIE_INTR_SLV_UNSUPP)
-               dev_warn(port->dev, "Slave unsupported request\n");
+               dev_warn(dev, "Slave unsupported request\n");
 
        if (status & XILINX_PCIE_INTR_SLV_UNEXP)
-               dev_warn(port->dev, "Slave unexpected completion\n");
+               dev_warn(dev, "Slave unexpected completion\n");
 
        if (status & XILINX_PCIE_INTR_SLV_COMPL)
-               dev_warn(port->dev, "Slave completion timeout\n");
+               dev_warn(dev, "Slave completion timeout\n");
 
        if (status & XILINX_PCIE_INTR_SLV_ERRP)
-               dev_warn(port->dev, "Slave Error Poison\n");
+               dev_warn(dev, "Slave Error Poison\n");
 
        if (status & XILINX_PCIE_INTR_SLV_CMPABT)
-               dev_warn(port->dev, "Slave Completer Abort\n");
+               dev_warn(dev, "Slave Completer Abort\n");
 
        if (status & XILINX_PCIE_INTR_SLV_ILLBUR)
-               dev_warn(port->dev, "Slave Illegal Burst\n");
+               dev_warn(dev, "Slave Illegal Burst\n");
 
        if (status & XILINX_PCIE_INTR_MST_DECERR)
-               dev_warn(port->dev, "Master decode error\n");
+               dev_warn(dev, "Master decode error\n");
 
        if (status & XILINX_PCIE_INTR_MST_SLVERR)
-               dev_warn(port->dev, "Master slave error\n");
+               dev_warn(dev, "Master slave error\n");
 
        if (status & XILINX_PCIE_INTR_MST_ERRP)
-               dev_warn(port->dev, "Master error poison\n");
+               dev_warn(dev, "Master error poison\n");
 
 error:
        /* Clear the Interrupt Decode register */
@@ -554,10 +555,12 @@ static int xilinx_pcie_init_irq_domain(struct xilinx_pcie_port *port)
  */
 static void xilinx_pcie_init_port(struct xilinx_pcie_port *port)
 {
+       struct device *dev = port->dev;
+
        if (xilinx_pcie_link_is_up(port))
-               dev_info(port->dev, "PCIe Link is UP\n");
+               dev_info(dev, "PCIe Link is UP\n");
        else
-               dev_info(port->dev, "PCIe Link is DOWN\n");
+               dev_info(dev, "PCIe Link is DOWN\n");
 
        /* Disable all interrupts */
        pcie_write(port, ~XILINX_PCIE_IDR_ALL_MASK,
@@ -627,8 +630,8 @@ static int xilinx_pcie_parse_dt(struct xilinx_pcie_port *port)
  */
 static int xilinx_pcie_probe(struct platform_device *pdev)
 {
-       struct xilinx_pcie_port *port;
        struct device *dev = &pdev->dev;
+       struct xilinx_pcie_port *port;
        struct pci_bus *bus;
        int err;
        resource_size_t iobase = 0;
@@ -668,15 +671,14 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
        if (err)
                goto error;
 
-       bus = pci_create_root_bus(&pdev->dev, 0,
-                                 &xilinx_pcie_ops, port, &res);
+       bus = pci_create_root_bus(dev, 0, &xilinx_pcie_ops, port, &res);
        if (!bus) {
                err = -ENOMEM;
                goto error;
        }
 
 #ifdef CONFIG_PCI_MSI
-       xilinx_pcie_msi_chip.dev = port->dev;
+       xilinx_pcie_msi_chip.dev = dev;
        bus->msi = &xilinx_pcie_msi_chip;
 #endif
        pci_scan_child_bus(bus);
@@ -685,8 +687,6 @@ static int xilinx_pcie_probe(struct platform_device *pdev)
        pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
 #endif
        pci_bus_add_devices(bus);
-       platform_set_drvdata(pdev, port);
-
        return 0;
 
 error:
index 460fa67..2acdb0d 100644 (file)
@@ -405,7 +405,7 @@ static inline void acerhdf_enable_kernelmode(void)
        kernelmode = 1;
 
        thz_dev->polling_delay = interval*1000;
-       thermal_zone_device_update(thz_dev);
+       thermal_zone_device_update(thz_dev, THERMAL_EVENT_UNSPECIFIED);
        pr_notice("kernel mode fan control ON\n");
 }
 
index 15f1311..28551f5 100644 (file)
@@ -932,30 +932,19 @@ static ssize_t infos_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(infos);
 
-static int parse_arg(const char *buf, unsigned long count, int *val)
-{
-       if (!count)
-               return 0;
-       if (count > 31)
-               return -EINVAL;
-       if (sscanf(buf, "%i", val) != 1)
-               return -EINVAL;
-       return count;
-}
-
 static ssize_t sysfs_acpi_set(struct asus_laptop *asus,
                              const char *buf, size_t count,
                              const char *method)
 {
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv <= 0)
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
                return rv;
 
        if (write_acpi_int(asus->handle, method, value))
                return -ENODEV;
-       return rv;
+       return count;
 }
 
 /*
@@ -975,15 +964,17 @@ static ssize_t ledd_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
-                       pr_warn("LED display write failed\n");
-                       return -ENODEV;
-               }
-               asus->ledd_status = (u32) value;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       if (write_acpi_int(asus->handle, METHOD_LEDD, value)) {
+               pr_warn("LED display write failed\n");
+               return -ENODEV;
        }
-       return rv;
+
+       asus->ledd_status = (u32) value;
+       return count;
 }
 static DEVICE_ATTR_RW(ledd);
 
@@ -1148,10 +1139,12 @@ static ssize_t display_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               asus_set_display(asus, value);
-       return rv;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       asus_set_display(asus, value);
+       return count;
 }
 static DEVICE_ATTR_WO(display);
 
@@ -1190,11 +1183,12 @@ static ssize_t ls_switch_store(struct device *dev,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0)
-               asus_als_switch(asus, value ? 1 : 0);
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
 
-       return rv;
+       asus_als_switch(asus, value ? 1 : 0);
+       return count;
 }
 static DEVICE_ATTR_RW(ls_switch);
 
@@ -1219,14 +1213,15 @@ static ssize_t ls_level_store(struct device *dev, struct device_attribute *attr,
        struct asus_laptop *asus = dev_get_drvdata(dev);
        int rv, value;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv > 0) {
-               value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
-               /* 0 <= value <= 15 */
-               asus_als_level(asus, value);
-       }
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
+
+       value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
+       /* 0 <= value <= 15 */
+       asus_als_level(asus, value);
 
-       return rv;
+       return count;
 }
 static DEVICE_ATTR_RW(ls_level);
 
@@ -1301,14 +1296,14 @@ static ssize_t gps_store(struct device *dev, struct device_attribute *attr,
        int rv, value;
        int ret;
 
-       rv = parse_arg(buf, count, &value);
-       if (rv <= 0)
-               return -EINVAL;
+       rv = kstrtoint(buf, 0, &value);
+       if (rv < 0)
+               return rv;
        ret = asus_gps_switch(asus, !!value);
        if (ret)
                return ret;
        rfkill_set_sw_state(asus->gps.rfkill, !value);
-       return rv;
+       return count;
 }
 static DEVICE_ATTR_RW(gps);
 
index adecc1c..26e4cbc 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/input/sparse-keymap.h>
 #include <linux/fb.h>
 #include <linux/dmi.h>
+#include <linux/i8042.h>
 
 #include "asus-wmi.h"
 
@@ -55,10 +56,34 @@ MODULE_PARM_DESC(wapf, "WAPF value");
 
 static struct quirk_entry *quirks;
 
+static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str,
+                             struct serio *port)
+{
+       static bool extended;
+       bool ret = false;
+
+       if (str & I8042_STR_AUXDATA)
+               return false;
+
+       if (unlikely(data == 0xe1)) {
+               extended = true;
+               ret = true;
+       } else if (unlikely(extended)) {
+               extended = false;
+               ret = true;
+       }
+
+       return ret;
+}
+
 static struct quirk_entry quirk_asus_unknown = {
        .wapf = 0,
 };
 
+static struct quirk_entry quirk_asus_q500a = {
+       .i8042_filter = asus_q500a_i8042_filter,
+};
+
 /*
  * For those machines that need software to control bt/wifi status
  * and can't adjust brightness through ACPI interface
@@ -87,6 +112,10 @@ static struct quirk_entry quirk_no_rfkill_wapf4 = {
        .no_rfkill = true,
 };
 
+static struct quirk_entry quirk_asus_ux303ub = {
+       .wmi_backlight_native = true,
+};
+
 static int dmi_matched(const struct dmi_system_id *dmi)
 {
        quirks = dmi->driver_data;
@@ -94,6 +123,15 @@ static int dmi_matched(const struct dmi_system_id *dmi)
 }
 
 static const struct dmi_system_id asus_quirks[] = {
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. Q500A",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Q500A"),
+               },
+               .driver_data = &quirk_asus_q500a,
+       },
        {
                .callback = dmi_matched,
                .ident = "ASUSTeK COMPUTER INC. U32U",
@@ -351,11 +389,22 @@ static const struct dmi_system_id asus_quirks[] = {
                },
                .driver_data = &quirk_no_rfkill,
        },
+       {
+               .callback = dmi_matched,
+               .ident = "ASUSTeK COMPUTER INC. UX303UB",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "UX303UB"),
+               },
+               .driver_data = &quirk_asus_ux303ub,
+       },
        {},
 };
 
 static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
 {
+       int ret;
+
        quirks = &quirk_asus_unknown;
        dmi_check_system(asus_quirks);
 
@@ -367,6 +416,15 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
                quirks->wapf = wapf;
        else
                wapf = quirks->wapf;
+
+       if (quirks->i8042_filter) {
+               ret = i8042_install_filter(quirks->i8042_filter);
+               if (ret) {
+                       pr_warn("Unable to install key filter\n");
+                       return;
+               }
+               pr_info("Using i8042 filter function for receiving events\n");
+       }
 }
 
 static const struct key_entry asus_nb_wmi_keymap[] = {
index 7c093a0..ce6ca31 100644 (file)
@@ -2084,6 +2084,9 @@ static int asus_wmi_add(struct platform_device *pdev)
        if (asus->driver->quirks->wmi_backlight_power)
                acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
 
+       if (asus->driver->quirks->wmi_backlight_native)
+               acpi_video_set_dmi_backlight_type(acpi_backlight_native);
+
        if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
                err = asus_wmi_backlight_init(asus);
                if (err && err != -ENODEV)
index 5de1df5..0e19014 100644 (file)
@@ -28,6 +28,7 @@
 #define _ASUS_WMI_H_
 
 #include <linux/platform_device.h>
+#include <linux/i8042.h>
 
 #define ASUS_WMI_KEY_IGNORE (-1)
 #define ASUS_WMI_BRN_DOWN      0x20
@@ -43,6 +44,7 @@ struct quirk_entry {
        bool scalar_panel_brightness;
        bool store_backlight_power;
        bool wmi_backlight_power;
+       bool wmi_backlight_native;
        int wapf;
        /*
         * For machines with AMD graphic chips, it will send out WMI event
@@ -51,6 +53,9 @@ struct quirk_entry {
         * and let the ACPI interrupt to send out the key event.
         */
        int no_display_toggle;
+
+       bool (*i8042_filter)(unsigned char data, unsigned char str,
+                            struct serio *serio);
 };
 
 struct asus_wmi_driver {
index 0aec4fd..37e6460 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/acpi.h>
 #include <linux/interrupt.h>
 #include <linux/miscdevice.h>
+#include <linux/uaccess.h>
 
 struct smo8800_device {
        u32 irq;                     /* acpi device irq */
index 520b58a..e8b1b83 100644 (file)
@@ -100,7 +100,7 @@ static int pmc_core_dbgfs_register(struct pmc_dev *pmcdev)
        struct dentry *dir, *file;
 
        dir = debugfs_create_dir("pmc_core", NULL);
-       if (IS_ERR_OR_NULL(dir))
+       if (!dir)
                return -ENOMEM;
 
        pmcdev->dbgfs_dir = dir;
index a511d51..0bf51d5 100644 (file)
@@ -522,48 +522,36 @@ static struct resource telemetry_res[] = {
 static int ipc_create_punit_device(void)
 {
        struct platform_device *pdev;
-       int ret;
-
-       pdev = platform_device_alloc(PUNIT_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev, "Failed to alloc punit platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
-       ret = platform_device_add_resources(pdev, punit_res_array,
-                                           ARRAY_SIZE(punit_res_array));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add platform punit resources\n");
-               goto err;
-       }
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = PUNIT_DEVICE_NAME,
+               .id = -1,
+               .res = punit_res_array,
+               .num_res = ARRAY_SIZE(punit_res_array),
+               };
+
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add punit platform device\n");
-               goto err;
-       }
        ipcdev.punit_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_tco_device(void)
 {
        struct platform_device *pdev;
        struct resource *res;
-       int ret;
-
-       pdev = platform_device_alloc(TCO_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev, "Failed to alloc tco platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = TCO_DEVICE_NAME,
+               .id = -1,
+               .res = tco_res,
+               .num_res = ARRAY_SIZE(tco_res),
+               .data = &tco_info,
+               .size_data = sizeof(tco_info),
+               };
 
        res = tco_res + TCO_RESOURCE_ACPI_IO;
        res->start = ipcdev.acpi_io_base + TCO_BASE_OFFSET;
@@ -577,45 +565,26 @@ static int ipc_create_tco_device(void)
        res->start = ipcdev.gcr_base + TCO_PMC_OFFSET;
        res->end = res->start + TCO_PMC_SIZE - 1;
 
-       ret = platform_device_add_resources(pdev, tco_res, ARRAY_SIZE(tco_res));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform resources\n");
-               goto err;
-       }
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add_data(pdev, &tco_info, sizeof(tco_info));
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform data\n");
-               goto err;
-       }
-
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev, "Failed to add tco platform device\n");
-               goto err;
-       }
        ipcdev.tco_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_telemetry_device(void)
 {
        struct platform_device *pdev;
        struct resource *res;
-       int ret;
-
-       pdev = platform_device_alloc(TELEMETRY_DEVICE_NAME, -1);
-       if (!pdev) {
-               dev_err(ipcdev.dev,
-                       "Failed to allocate telemetry platform device\n");
-               return -ENOMEM;
-       }
-
-       pdev->dev.parent = ipcdev.dev;
+       const struct platform_device_info pdevinfo = {
+               .parent = ipcdev.dev,
+               .name = TELEMETRY_DEVICE_NAME,
+               .id = -1,
+               .res = telemetry_res,
+               .num_res = ARRAY_SIZE(telemetry_res),
+               };
 
        res = telemetry_res + TELEMETRY_RESOURCE_PUNIT_SSRAM;
        res->start = ipcdev.telem_punit_ssram_base;
@@ -625,26 +594,13 @@ static int ipc_create_telemetry_device(void)
        res->start = ipcdev.telem_pmc_ssram_base;
        res->end = res->start + ipcdev.telem_pmc_ssram_size - 1;
 
-       ret = platform_device_add_resources(pdev, telemetry_res,
-                                           ARRAY_SIZE(telemetry_res));
-       if (ret) {
-               dev_err(ipcdev.dev,
-                       "Failed to add telemetry platform resources\n");
-               goto err;
-       }
+       pdev = platform_device_register_full(&pdevinfo);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
 
-       ret = platform_device_add(pdev);
-       if (ret) {
-               dev_err(ipcdev.dev,
-                       "Failed to add telemetry platform device\n");
-               goto err;
-       }
        ipcdev.telemetry_dev = pdev;
 
        return 0;
-err:
-       platform_device_put(pdev);
-       return ret;
 }
 
 static int ipc_create_pmc_devices(void)
index 9d60a40..074bf2f 100644 (file)
@@ -321,10 +321,9 @@ static int write_acpi_int(const char *methodName, int val)
 static acpi_status tci_raw(struct toshiba_acpi_dev *dev,
                           const u32 in[TCI_WORDS], u32 out[TCI_WORDS])
 {
+       union acpi_object in_objs[TCI_WORDS], out_objs[TCI_WORDS + 1];
        struct acpi_object_list params;
-       union acpi_object in_objs[TCI_WORDS];
        struct acpi_buffer results;
-       union acpi_object out_objs[TCI_WORDS + 1];
        acpi_status status;
        int i;
 
@@ -387,9 +386,8 @@ static int sci_open(struct toshiba_acpi_dev *dev)
 {
        u32 in[TCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
-       acpi_status status;
+       acpi_status status = tci_raw(dev, in, out);
 
-       status = tci_raw(dev, in, out);
        if  (ACPI_FAILURE(status)) {
                pr_err("ACPI call to open SCI failed\n");
                return 0;
@@ -425,9 +423,8 @@ static void sci_close(struct toshiba_acpi_dev *dev)
 {
        u32 in[TCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
-       acpi_status status;
+       acpi_status status = tci_raw(dev, in, out);
 
-       status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to close SCI failed\n");
                return;
@@ -479,10 +476,15 @@ static void toshiba_illumination_available(struct toshiba_acpi_dev *dev)
 
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query Illumination support failed\n");
-       else if (out[0] == TOS_SUCCESS)
-               dev->illumination_supported = 1;
+               return;
+       }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->illumination_supported = 1;
 }
 
 static void toshiba_illumination_set(struct led_classdev *cdev,
@@ -509,7 +511,8 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
 {
        struct toshiba_acpi_dev *dev = container_of(cdev,
                        struct toshiba_acpi_dev, led_dev);
-       u32 state, result;
+       u32 result;
+       u32 state;
 
        /* First request : initialize communication. */
        if (!sci_open(dev))
@@ -546,24 +549,28 @@ static void toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev)
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query kbd illumination support failed\n");
-       } else if (out[0] == TOS_SUCCESS) {
-               /*
-                * Check for keyboard backlight timeout max value,
-                * previous kbd backlight implementation set this to
-                * 0x3c0003, and now the new implementation set this
-                * to 0x3c001a, use this to distinguish between them.
-                */
-               if (out[3] == SCI_KBD_TIME_MAX)
-                       dev->kbd_type = 2;
-               else
-                       dev->kbd_type = 1;
-               /* Get the current keyboard backlight mode */
-               dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
-               /* Get the current time (1-60 seconds) */
-               dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
-               /* Flag as supported */
-               dev->kbd_illum_supported = 1;
+               return;
        }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       /*
+        * Check for keyboard backlight timeout max value,
+        * previous kbd backlight implementation set this to
+        * 0x3c0003, and now the new implementation set this
+        * to 0x3c001a, use this to distinguish between them.
+        */
+       if (out[3] == SCI_KBD_TIME_MAX)
+               dev->kbd_type = 2;
+       else
+               dev->kbd_type = 1;
+       /* Get the current keyboard backlight mode */
+       dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK;
+       /* Get the current time (1-60 seconds) */
+       dev->kbd_time = out[2] >> HCI_MISC_SHIFT;
+       /* Flag as supported */
+       dev->kbd_illum_supported = 1;
 }
 
 static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
@@ -672,9 +679,9 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
 /* Eco Mode support */
 static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
 {
-       acpi_status status;
        u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 0, 0, 0 };
        u32 out[TCI_WORDS];
+       acpi_status status;
 
        dev->eco_supported = 0;
        dev->eco_led_registered = false;
@@ -682,7 +689,10 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
        status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get ECO led failed\n");
-       } else if (out[0] == TOS_INPUT_DATA_ERROR) {
+               return;
+       }
+
+       if (out[0] == TOS_INPUT_DATA_ERROR) {
                /*
                 * If we receive 0x8300 (Input Data Error), it means that the
                 * LED device is present, but that we just screwed the input
@@ -694,10 +704,15 @@ static void toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
                 */
                in[3] = 1;
                status = tci_raw(dev, in, out);
-               if (ACPI_FAILURE(status))
+               if (ACPI_FAILURE(status)) {
                        pr_err("ACPI call to get ECO led failed\n");
-               else if (out[0] == TOS_SUCCESS)
-                       dev->eco_supported = 1;
+                       return;
+               }
+
+               if (out[0] != TOS_SUCCESS)
+                       return;
+
+               dev->eco_supported = 1;
        }
 }
 
@@ -714,10 +729,11 @@ toshiba_eco_mode_get_status(struct led_classdev *cdev)
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get ECO led failed\n");
                return LED_OFF;
-       } else if (out[0] != TOS_SUCCESS) {
-               return LED_OFF;
        }
 
+       if (out[0] != TOS_SUCCESS)
+               return LED_OFF;
+
        return out[2] ? LED_FULL : LED_OFF;
 }
 
@@ -751,10 +767,15 @@ static void toshiba_accelerometer_available(struct toshiba_acpi_dev *dev)
         * this call also serves as initialization
         */
        status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query the accelerometer failed\n");
-       else if (out[0] == TOS_SUCCESS)
-               dev->accelerometer_supported = 1;
+               return;
+       }
+
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->accelerometer_supported = 1;
 }
 
 static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
@@ -769,15 +790,18 @@ static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to query the accelerometer failed\n");
                return -EIO;
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *xy = out[2];
-               *z = out[4];
-               return 0;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *xy = out[2];
+       *z = out[4];
+
+       return 0;
 }
 
 /* Sleep (Charge and Music) utilities support */
@@ -797,24 +821,29 @@ static void toshiba_usb_sleep_charge_available(struct toshiba_acpi_dev *dev)
                pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
                sci_close(dev);
                return;
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
+       }
+
+       if (out[0] != TOS_SUCCESS) {
                sci_close(dev);
                return;
-       } else if (out[0] == TOS_SUCCESS) {
-               dev->usbsc_mode_base = out[4];
        }
 
+       dev->usbsc_mode_base = out[4];
+
        in[5] = SCI_USB_CHARGE_BAT_LVL;
        status = tci_raw(dev, in, out);
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB Sleep and Charge mode failed\n");
-       } else if (out[0] == TOS_SUCCESS) {
-               dev->usbsc_bat_level = out[2];
-               /* Flag as supported */
-               dev->usb_sleep_charge_supported = 1;
+               return;
        }
 
+       if (out[0] != TOS_SUCCESS)
+               return;
+
+       dev->usbsc_bat_level = out[2];
+       /* Flag as supported */
+       dev->usb_sleep_charge_supported = 1;
 }
 
 static int toshiba_usb_sleep_charge_get(struct toshiba_acpi_dev *dev,
@@ -868,14 +897,19 @@ static int toshiba_sleep_functions_status_get(struct toshiba_acpi_dev *dev,
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB S&C battery level failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *mode = out[2];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *mode = out[2];
+
+       return 0;
+
 }
 
 static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
@@ -892,9 +926,12 @@ static int toshiba_sleep_functions_status_set(struct toshiba_acpi_dev *dev,
        in[5] = SCI_USB_CHARGE_BAT_LVL;
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set USB S&C battery level failed\n");
-       else if (out[0] == TOS_NOT_SUPPORTED)
+               return -EIO;
+       }
+
+       if (out[0] == TOS_NOT_SUPPORTED)
                return -ENODEV;
 
        return out[0] == TOS_SUCCESS ? 0 : -EIO;
@@ -915,14 +952,18 @@ static int toshiba_usb_rapid_charge_get(struct toshiba_acpi_dev *dev,
        sci_close(dev);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get USB Rapid Charge failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) {
-               *state = out[2];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
+               return -EIO;
+
+       *state = out[2];
+
+       return 0;
 }
 
 static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
@@ -939,9 +980,12 @@ static int toshiba_usb_rapid_charge_set(struct toshiba_acpi_dev *dev,
        in[5] = SCI_USB_CHARGE_RAPID_DSP;
        status = tci_raw(dev, in, out);
        sci_close(dev);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set USB Rapid Charge failed\n");
-       else if (out[0] == TOS_NOT_SUPPORTED)
+               return -EIO;
+       }
+
+       if (out[0] == TOS_NOT_SUPPORTED)
                return -ENODEV;
 
        return (out[0] == TOS_SUCCESS || out[0] == TOS_SUCCESS2) ? 0 : -EIO;
@@ -1097,14 +1141,18 @@ static int toshiba_hotkey_event_type_get(struct toshiba_acpi_dev *dev,
        status = tci_raw(dev, in, out);
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get System type failed\n");
-       } else if (out[0] == TOS_NOT_SUPPORTED) {
-               return -ENODEV;
-       } else if (out[0] == TOS_SUCCESS) {
-               *type = out[3];
-               return 0;
+               return -EIO;
        }
 
-       return -EIO;
+       if (out[0] == TOS_NOT_SUPPORTED)
+               return -ENODEV;
+
+       if (out[0] != TOS_SUCCESS)
+               return -EIO;
+
+       *type = out[3];
+
+       return 0;
 }
 
 /* Wireless status (RFKill, WLAN, BT, WWAN) */
@@ -1154,7 +1202,6 @@ static void toshiba_wwan_available(struct toshiba_acpi_dev *dev)
         */
        in[3] = HCI_WIRELESS_WWAN;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get WWAN status failed\n");
                return;
@@ -1174,7 +1221,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
 
        in[3] = HCI_WIRELESS_WWAN_STATUS;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set WWAN status failed\n");
                return -EIO;
@@ -1193,7 +1239,6 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
         */
        in[3] = HCI_WIRELESS_WWAN_POWER;
        status = tci_raw(dev, in, out);
-
        if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to set WWAN power failed\n");
                return -EIO;
@@ -1216,8 +1261,10 @@ static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev)
        dev->max_cooling_method = 0;
 
        status = tci_raw(dev, in, out);
-       if (ACPI_FAILURE(status))
+       if (ACPI_FAILURE(status)) {
                pr_err("ACPI call to get Cooling Method failed\n");
+               return;
+       }
 
        if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
                return;
@@ -1244,7 +1291,7 @@ static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state)
        u32 result = hci_write(dev, HCI_COOLING_METHOD, state);
 
        if (result == TOS_FAILURE)
-               pr_err("ACPI call to get Cooling Method failed\n");
+               pr_err("ACPI call to set Cooling Method failed\n");
 
        if (result == TOS_NOT_SUPPORTED)
                return -ENODEV;
@@ -1282,9 +1329,9 @@ static struct proc_dir_entry *toshiba_proc_dir;
 /* LCD Brightness */
 static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
 {
+       int brightness = 0;
        u32 result;
        u32 value;
-       int brightness = 0;
 
        if (dev->tr_backlight_supported) {
                int ret = get_tr_backlight_status(dev, &value);
@@ -1301,10 +1348,10 @@ static int __get_lcd_brightness(struct toshiba_acpi_dev *dev)
                pr_err("ACPI call to get LCD Brightness failed\n");
        else if (result == TOS_NOT_SUPPORTED)
                return -ENODEV;
-       if (result == TOS_SUCCESS)
-               return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT);
 
-       return -EIO;
+       return result == TOS_SUCCESS ?
+                       brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT) :
+                       -EIO;
 }
 
 static int get_lcd_brightness(struct backlight_device *bd)
@@ -1325,15 +1372,15 @@ static int lcd_proc_show(struct seq_file *m, void *v)
 
        levels = dev->backlight_dev->props.max_brightness + 1;
        value = get_lcd_brightness(dev->backlight_dev);
-       if (value >= 0) {
-               seq_printf(m, "brightness:              %d\n", value);
-               seq_printf(m, "brightness_levels:       %d\n", levels);
-               return 0;
+       if (value < 0) {
+               pr_err("Error reading LCD brightness\n");
+               return value;
        }
 
-       pr_err("Error reading LCD brightness\n");
+       seq_printf(m, "brightness:              %d\n", value);
+       seq_printf(m, "brightness_levels:       %d\n", levels);
 
-       return -EIO;
+       return 0;
 }
 
 static int lcd_proc_open(struct inode *inode, struct file *file)
@@ -1377,7 +1424,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
        struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
        char cmd[42];
        size_t len;
-       int levels = dev->backlight_dev->props.max_brightness + 1;
+       int levels;
        int value;
 
        len = min(count, sizeof(cmd) - 1);
@@ -1385,6 +1432,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
                return -EFAULT;
        cmd[len] = '\0';
 
+       levels = dev->backlight_dev->props.max_brightness + 1;
        if (sscanf(cmd, " brightness : %i", &value) != 1 &&
            value < 0 && value > levels)
                return -EINVAL;
@@ -1420,20 +1468,21 @@ static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status)
 static int video_proc_show(struct seq_file *m, void *v)
 {
        struct toshiba_acpi_dev *dev = m->private;
+       int is_lcd, is_crt, is_tv;
        u32 value;
 
-       if (!get_video_status(dev, &value)) {
-               int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
-               int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
-               int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
+       if (get_video_status(dev, &value))
+               return -EIO;
 
-               seq_printf(m, "lcd_out:                 %d\n", is_lcd);
-               seq_printf(m, "crt_out:                 %d\n", is_crt);
-               seq_printf(m, "tv_out:                  %d\n", is_tv);
-               return 0;
-       }
+       is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0;
+       is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0;
+       is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0;
 
-       return -EIO;
+       seq_printf(m, "lcd_out:                 %d\n", is_lcd);
+       seq_printf(m, "crt_out:                 %d\n", is_crt);
+       seq_printf(m, "tv_out:                  %d\n", is_tv);
+
+       return 0;
 }
 
 static int video_proc_open(struct inode *inode, struct file *file)
@@ -1447,10 +1496,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
        struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
        char *buffer;
        char *cmd;
+       int lcd_out, crt_out, tv_out;
        int remain = count;
-       int lcd_out = -1;
-       int crt_out = -1;
-       int tv_out = -1;
        int value;
        int ret;
        u32 video_out;
@@ -1486,6 +1533,7 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf,
 
        kfree(cmd);
 
+       lcd_out = crt_out = tv_out = -1;
        ret = get_video_status(dev, &video_out);
        if (!ret) {
                unsigned int new_video_out = video_out;
@@ -1980,8 +2028,8 @@ static ssize_t usb_sleep_charge_store(struct device *dev,
                                      const char *buf, size_t count)
 {
        struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
-       u32 mode;
        int state;
+       u32 mode;
        int ret;
 
        ret = kstrtoint(buf, 0, &state);
@@ -2021,9 +2069,8 @@ static ssize_t sleep_functions_on_battery_show(struct device *dev,
                                               char *buf)
 {
        struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+       int bat_lvl, status;
        u32 state;
-       int bat_lvl;
-       int status;
        int ret;
        int tmp;
 
index 5db495d..be1d137 100644 (file)
@@ -80,7 +80,9 @@ static int toshiba_bluetooth_present(acpi_handle handle)
        if (ACPI_FAILURE(result)) {
                pr_err("ACPI call to query Bluetooth presence failed\n");
                return -ENXIO;
-       } else if (!bt_present) {
+       }
+
+       if (!bt_present) {
                pr_info("Bluetooth device not present\n");
                return -ENODEV;
        }
index 7f2afc6..b3dec52 100644 (file)
@@ -59,7 +59,7 @@ static int toshiba_haps_protection_level(acpi_handle handle, int level)
                return -EIO;
        }
 
-       pr_info("HDD protection level set to: %d\n", level);
+       pr_debug("HDD protection level set to: %d\n", level);
 
        return 0;
 }
@@ -141,7 +141,7 @@ static struct attribute_group haps_attr_group = {
  */
 static void toshiba_haps_notify(struct acpi_device *device, u32 event)
 {
-       pr_info("Received event: 0x%x", event);
+       pr_debug("Received event: 0x%x", event);
 
        acpi_bus_generate_netlink_event(device->pnp.device_class,
                                        dev_name(&device->dev),
@@ -168,9 +168,13 @@ static int toshiba_haps_available(acpi_handle handle)
         * A non existent device as well as having (only)
         * Solid State Drives can cause the call to fail.
         */
-       status = acpi_evaluate_integer(handle, "_STA", NULL,
-                                      &hdd_present);
-       if (ACPI_FAILURE(status) || !hdd_present) {
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &hdd_present);
+       if (ACPI_FAILURE(status)) {
+               pr_err("ACPI call to query HDD protection failed\n");
+               return 0;
+       }
+
+       if (!hdd_present) {
                pr_info("HDD protection not available or using SSD\n");
                return 0;
        }
index 7512e98..564a51a 100644 (file)
@@ -31,7 +31,7 @@ config PPS_DEBUG
 
 config NTP_PPS
        bool "PPS kernel consumer support"
-       depends on !NO_HZ
+       depends on !NO_HZ_COMMON
        help
          This option adds support for direct in-kernel time
          synchronization using an external PPS signal.
index d637c93..58a97d4 100644 (file)
@@ -193,6 +193,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg)
                if (err)
                        break;
 
+               memset(&precise_offset, 0, sizeof(precise_offset));
                ts = ktime_to_timespec64(xtstamp.device);
                precise_offset.device.sec = ts.tv_sec;
                precise_offset.device.nsec = ts.tv_nsec;
index 80a566a..bf01288 100644 (file)
@@ -262,6 +262,15 @@ config PWM_LPSS_PLATFORM
          To compile this driver as a module, choose M here: the module
          will be called pwm-lpss-platform.
 
+config PWM_MESON
+       tristate "Amlogic Meson PWM driver"
+       depends on ARCH_MESON
+       help
+         The platform driver for Amlogic Meson PWM controller.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-meson.
+
 config PWM_MTK_DISP
        tristate "MediaTek display PWM driver"
        depends on ARCH_MEDIATEK || COMPILE_TEST
index feef1dd..1194c54 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_PWM_LPC32XX)     += pwm-lpc32xx.o
 obj-$(CONFIG_PWM_LPSS)         += pwm-lpss.o
 obj-$(CONFIG_PWM_LPSS_PCI)     += pwm-lpss-pci.o
 obj-$(CONFIG_PWM_LPSS_PLATFORM)        += pwm-lpss-platform.o
+obj-$(CONFIG_PWM_MESON)                += pwm-meson.o
 obj-$(CONFIG_PWM_MTK_DISP)     += pwm-mtk-disp.o
 obj-$(CONFIG_PWM_MXS)          += pwm-mxs.o
 obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-omap-dmtimer.o
index 0dbd29e..172ef82 100644 (file)
@@ -339,6 +339,8 @@ int pwmchip_remove(struct pwm_chip *chip)
        unsigned int i;
        int ret = 0;
 
+       pwmchip_sysfs_unexport_children(chip);
+
        mutex_lock(&pwm_lock);
 
        for (i = 0; i < chip->npwm; i++) {
index 6510812..01339c1 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
+#include <linux/slab.h>
 
 #define BERLIN_PWM_EN                  0x0
 #define  BERLIN_PWM_ENABLE             BIT(0)
 #define BERLIN_PWM_TCNT                        0xc
 #define  BERLIN_PWM_MAX_TCNT           65535
 
+struct berlin_pwm_channel {
+       u32 enable;
+       u32 ctrl;
+       u32 duty;
+       u32 tcnt;
+};
+
 struct berlin_pwm_chip {
        struct pwm_chip chip;
        struct clk *clk;
@@ -55,6 +63,25 @@ static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip,
        writel_relaxed(value, chip->base + channel * 0x10 + offset);
 }
 
+static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct berlin_pwm_channel *channel;
+
+       channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+       if (!channel)
+               return -ENOMEM;
+
+       return pwm_set_chip_data(pwm, channel);
+}
+
+static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct berlin_pwm_channel *channel = pwm_get_chip_data(pwm);
+
+       pwm_set_chip_data(pwm, NULL);
+       kfree(channel);
+}
+
 static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev,
                             int duty_ns, int period_ns)
 {
@@ -137,6 +164,8 @@ static void berlin_pwm_disable(struct pwm_chip *chip,
 }
 
 static const struct pwm_ops berlin_pwm_ops = {
+       .request = berlin_pwm_request,
+       .free = berlin_pwm_free,
        .config = berlin_pwm_config,
        .set_polarity = berlin_pwm_set_polarity,
        .enable = berlin_pwm_enable,
@@ -204,12 +233,67 @@ static int berlin_pwm_remove(struct platform_device *pdev)
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int berlin_pwm_suspend(struct device *dev)
+{
+       struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
+       unsigned int i;
+
+       for (i = 0; i < pwm->chip.npwm; i++) {
+               struct berlin_pwm_channel *channel;
+
+               channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
+               if (!channel)
+                       continue;
+
+               channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE);
+               channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL);
+               channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY);
+               channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT);
+       }
+
+       clk_disable_unprepare(pwm->clk);
+
+       return 0;
+}
+
+static int berlin_pwm_resume(struct device *dev)
+{
+       struct berlin_pwm_chip *pwm = dev_get_drvdata(dev);
+       unsigned int i;
+       int ret;
+
+       ret = clk_prepare_enable(pwm->clk);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < pwm->chip.npwm; i++) {
+               struct berlin_pwm_channel *channel;
+
+               channel = pwm_get_chip_data(&pwm->chip.pwms[i]);
+               if (!channel)
+                       continue;
+
+               berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL);
+               berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY);
+               berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT);
+               berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE);
+       }
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(berlin_pwm_pm_ops, berlin_pwm_suspend,
+                        berlin_pwm_resume);
+
 static struct platform_driver berlin_pwm_driver = {
        .probe = berlin_pwm_probe,
        .remove = berlin_pwm_remove,
        .driver = {
                .name = "berlin-pwm",
                .of_match_table = berlin_pwm_match,
+               .pm = &berlin_pwm_pm_ops,
        },
 };
 module_platform_driver(berlin_pwm_driver);
index 99b9acc..f6ca4e8 100644 (file)
@@ -38,7 +38,7 @@ static int cros_ec_pwm_set_duty(struct cros_ec_device *ec, u8 index, u16 duty)
        struct {
                struct cros_ec_command msg;
                struct ec_params_pwm_set_duty params;
-       } buf;
+       } __packed buf;
        struct ec_params_pwm_set_duty *params = &buf.params;
        struct cros_ec_command *msg = &buf.msg;
 
@@ -65,7 +65,7 @@ static int __cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index,
                        struct ec_params_pwm_get_duty params;
                        struct ec_response_pwm_get_duty resp;
                };
-       } buf;
+       } __packed buf;
        struct ec_params_pwm_get_duty *params = &buf.params;
        struct ec_response_pwm_get_duty *resp = &buf.resp;
        struct cros_ec_command *msg = &buf.msg;
index 19dc64c..d7f5f7d 100644 (file)
@@ -413,14 +413,18 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev)
        }
 
        for (i = 0; i < lpc18xx_pwm->chip.npwm; i++) {
+               struct lpc18xx_pwm_data *data;
+
                pwm = &lpc18xx_pwm->chip.pwms[i];
-               pwm->chip_data = devm_kzalloc(lpc18xx_pwm->dev,
-                                             sizeof(struct lpc18xx_pwm_data),
-                                             GFP_KERNEL);
-               if (!pwm->chip_data) {
+
+               data = devm_kzalloc(lpc18xx_pwm->dev, sizeof(*data),
+                                   GFP_KERNEL);
+               if (!data) {
                        ret = -ENOMEM;
                        goto remove_pwmchip;
                }
+
+               pwm_set_chip_data(pwm, data);
        }
 
        platform_set_drvdata(pdev, lpc18xx_pwm);
diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c
new file mode 100644 (file)
index 0000000..381871b
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Amlogic, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ *
+ * BSD LICENSE
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2014 Amlogic, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the
+ *     distribution.
+ *   * Neither the name of Intel Corporation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define REG_PWM_A              0x0
+#define REG_PWM_B              0x4
+#define PWM_HIGH_SHIFT         16
+
+#define REG_MISC_AB            0x8
+#define MISC_B_CLK_EN          BIT(23)
+#define MISC_A_CLK_EN          BIT(15)
+#define MISC_CLK_DIV_MASK      0x7f
+#define MISC_B_CLK_DIV_SHIFT   16
+#define MISC_A_CLK_DIV_SHIFT   8
+#define MISC_B_CLK_SEL_SHIFT   6
+#define MISC_A_CLK_SEL_SHIFT   4
+#define MISC_CLK_SEL_WIDTH     2
+#define MISC_B_EN              BIT(1)
+#define MISC_A_EN              BIT(0)
+
+static const unsigned int mux_reg_shifts[] = {
+       MISC_A_CLK_SEL_SHIFT,
+       MISC_B_CLK_SEL_SHIFT
+};
+
+struct meson_pwm_channel {
+       unsigned int hi;
+       unsigned int lo;
+       u8 pre_div;
+
+       struct pwm_state state;
+
+       struct clk *clk_parent;
+       struct clk_mux mux;
+       struct clk *clk;
+};
+
+struct meson_pwm_data {
+       const char * const *parent_names;
+};
+
+struct meson_pwm {
+       struct pwm_chip chip;
+       const struct meson_pwm_data *data;
+       void __iomem *base;
+       u8 inverter_mask;
+       spinlock_t lock;
+};
+
+static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
+{
+       return container_of(chip, struct meson_pwm, chip);
+}
+
+static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+       struct device *dev = chip->dev;
+       int err;
+
+       if (!channel)
+               return -ENODEV;
+
+       if (channel->clk_parent) {
+               err = clk_set_parent(channel->clk, channel->clk_parent);
+               if (err < 0) {
+                       dev_err(dev, "failed to set parent %s for %s: %d\n",
+                               __clk_get_name(channel->clk_parent),
+                               __clk_get_name(channel->clk), err);
+                               return err;
+               }
+       }
+
+       err = clk_prepare_enable(channel->clk);
+       if (err < 0) {
+               dev_err(dev, "failed to enable clock %s: %d\n",
+                       __clk_get_name(channel->clk), err);
+               return err;
+       }
+
+       chip->ops->get_state(chip, pwm, &channel->state);
+
+       return 0;
+}
+
+static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+
+       if (channel)
+               clk_disable_unprepare(channel->clk);
+}
+
+static int meson_pwm_calc(struct meson_pwm *meson,
+                         struct meson_pwm_channel *channel, unsigned int id,
+                         unsigned int duty, unsigned int period)
+{
+       unsigned int pre_div, cnt, duty_cnt;
+       unsigned long fin_freq = -1, fin_ns;
+
+       if (~(meson->inverter_mask >> id) & 0x1)
+               duty = period - duty;
+
+       if (period == channel->state.period &&
+           duty == channel->state.duty_cycle)
+               return 0;
+
+       fin_freq = clk_get_rate(channel->clk);
+       if (fin_freq == 0) {
+               dev_err(meson->chip.dev, "invalid source clock frequency\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(meson->chip.dev, "fin_freq: %lu Hz\n", fin_freq);
+       fin_ns = NSEC_PER_SEC / fin_freq;
+
+       /* Calc pre_div with the period */
+       for (pre_div = 0; pre_div < MISC_CLK_DIV_MASK; pre_div++) {
+               cnt = DIV_ROUND_CLOSEST(period, fin_ns * (pre_div + 1));
+               dev_dbg(meson->chip.dev, "fin_ns=%lu pre_div=%u cnt=%u\n",
+                       fin_ns, pre_div, cnt);
+               if (cnt <= 0xffff)
+                       break;
+       }
+
+       if (pre_div == MISC_CLK_DIV_MASK) {
+               dev_err(meson->chip.dev, "unable to get period pre_div\n");
+               return -EINVAL;
+       }
+
+       dev_dbg(meson->chip.dev, "period=%u pre_div=%u cnt=%u\n", period,
+               pre_div, cnt);
+
+       if (duty == period) {
+               channel->pre_div = pre_div;
+               channel->hi = cnt;
+               channel->lo = 0;
+       } else if (duty == 0) {
+               channel->pre_div = pre_div;
+               channel->hi = 0;
+               channel->lo = cnt;
+       } else {
+               /* Then check is we can have the duty with the same pre_div */
+               duty_cnt = DIV_ROUND_CLOSEST(duty, fin_ns * (pre_div + 1));
+               if (duty_cnt > 0xffff) {
+                       dev_err(meson->chip.dev, "unable to get duty cycle\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(meson->chip.dev, "duty=%u pre_div=%u duty_cnt=%u\n",
+                       duty, pre_div, duty_cnt);
+
+               channel->pre_div = pre_div;
+               channel->hi = duty_cnt;
+               channel->lo = cnt - duty_cnt;
+       }
+
+       return 0;
+}
+
+static void meson_pwm_enable(struct meson_pwm *meson,
+                            struct meson_pwm_channel *channel,
+                            unsigned int id)
+{
+       u32 value, clk_shift, clk_enable, enable;
+       unsigned int offset;
+
+       switch (id) {
+       case 0:
+               clk_shift = MISC_A_CLK_DIV_SHIFT;
+               clk_enable = MISC_A_CLK_EN;
+               enable = MISC_A_EN;
+               offset = REG_PWM_A;
+               break;
+
+       case 1:
+               clk_shift = MISC_B_CLK_DIV_SHIFT;
+               clk_enable = MISC_B_CLK_EN;
+               enable = MISC_B_EN;
+               offset = REG_PWM_B;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       value &= ~(MISC_CLK_DIV_MASK << clk_shift);
+       value |= channel->pre_div << clk_shift;
+       value |= clk_enable;
+       writel(value, meson->base + REG_MISC_AB);
+
+       value = (channel->hi << PWM_HIGH_SHIFT) | channel->lo;
+       writel(value, meson->base + offset);
+
+       value = readl(meson->base + REG_MISC_AB);
+       value |= enable;
+       writel(value, meson->base + REG_MISC_AB);
+}
+
+static void meson_pwm_disable(struct meson_pwm *meson, unsigned int id)
+{
+       u32 value, enable;
+
+       switch (id) {
+       case 0:
+               enable = MISC_A_EN;
+               break;
+
+       case 1:
+               enable = MISC_B_EN;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       value &= ~enable;
+       writel(value, meson->base + REG_MISC_AB);
+}
+
+static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                          struct pwm_state *state)
+{
+       struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
+       struct meson_pwm *meson = to_meson_pwm(chip);
+       unsigned long flags;
+       int err = 0;
+
+       if (!state)
+               return -EINVAL;
+
+       spin_lock_irqsave(&meson->lock, flags);
+
+       if (!state->enabled) {
+               meson_pwm_disable(meson, pwm->hwpwm);
+               channel->state.enabled = false;
+
+               goto unlock;
+       }
+
+       if (state->period != channel->state.period ||
+           state->duty_cycle != channel->state.duty_cycle ||
+           state->polarity != channel->state.polarity) {
+               if (channel->state.enabled) {
+                       meson_pwm_disable(meson, pwm->hwpwm);
+                       channel->state.enabled = false;
+               }
+
+               if (state->polarity != channel->state.polarity) {
+                       if (state->polarity == PWM_POLARITY_NORMAL)
+                               meson->inverter_mask |= BIT(pwm->hwpwm);
+                       else
+                               meson->inverter_mask &= ~BIT(pwm->hwpwm);
+               }
+
+               err = meson_pwm_calc(meson, channel, pwm->hwpwm,
+                                    state->duty_cycle, state->period);
+               if (err < 0)
+                       goto unlock;
+
+               channel->state.polarity = state->polarity;
+               channel->state.period = state->period;
+               channel->state.duty_cycle = state->duty_cycle;
+       }
+
+       if (state->enabled && !channel->state.enabled) {
+               meson_pwm_enable(meson, channel, pwm->hwpwm);
+               channel->state.enabled = true;
+       }
+
+unlock:
+       spin_unlock_irqrestore(&meson->lock, flags);
+       return err;
+}
+
+static void meson_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+                               struct pwm_state *state)
+{
+       struct meson_pwm *meson = to_meson_pwm(chip);
+       u32 value, mask;
+
+       if (!state)
+               return;
+
+       switch (pwm->hwpwm) {
+       case 0:
+               mask = MISC_A_EN;
+               break;
+
+       case 1:
+               mask = MISC_B_EN;
+               break;
+
+       default:
+               return;
+       }
+
+       value = readl(meson->base + REG_MISC_AB);
+       state->enabled = (value & mask) != 0;
+}
+
+static const struct pwm_ops meson_pwm_ops = {
+       .request = meson_pwm_request,
+       .free = meson_pwm_free,
+       .apply = meson_pwm_apply,
+       .get_state = meson_pwm_get_state,
+       .owner = THIS_MODULE,
+};
+
+static const char * const pwm_meson8b_parent_names[] = {
+       "xtal", "vid_pll", "fclk_div4", "fclk_div3"
+};
+
+static const struct meson_pwm_data pwm_meson8b_data = {
+       .parent_names = pwm_meson8b_parent_names,
+};
+
+static const char * const pwm_gxbb_parent_names[] = {
+       "xtal", "hdmi_pll", "fclk_div4", "fclk_div3"
+};
+
+static const struct meson_pwm_data pwm_gxbb_data = {
+       .parent_names = pwm_gxbb_parent_names,
+};
+
+static const struct of_device_id meson_pwm_matches[] = {
+       { .compatible = "amlogic,meson8b-pwm", .data = &pwm_meson8b_data },
+       { .compatible = "amlogic,meson-gxbb-pwm", .data = &pwm_gxbb_data },
+       {},
+};
+MODULE_DEVICE_TABLE(of, meson_pwm_matches);
+
+static int meson_pwm_init_channels(struct meson_pwm *meson,
+                                  struct meson_pwm_channel *channels)
+{
+       struct device *dev = meson->chip.dev;
+       struct device_node *np = dev->of_node;
+       struct clk_init_data init;
+       unsigned int i;
+       char name[255];
+       int err;
+
+       for (i = 0; i < meson->chip.npwm; i++) {
+               struct meson_pwm_channel *channel = &channels[i];
+
+               snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i);
+
+               init.name = name;
+               init.ops = &clk_mux_ops;
+               init.flags = CLK_IS_BASIC;
+               init.parent_names = meson->data->parent_names;
+               init.num_parents = 1 << MISC_CLK_SEL_WIDTH;
+
+               channel->mux.reg = meson->base + REG_MISC_AB;
+               channel->mux.shift = mux_reg_shifts[i];
+               channel->mux.mask = BIT(MISC_CLK_SEL_WIDTH) - 1;
+               channel->mux.flags = 0;
+               channel->mux.lock = &meson->lock;
+               channel->mux.table = NULL;
+               channel->mux.hw.init = &init;
+
+               channel->clk = devm_clk_register(dev, &channel->mux.hw);
+               if (IS_ERR(channel->clk)) {
+                       err = PTR_ERR(channel->clk);
+                       dev_err(dev, "failed to register %s: %d\n", name, err);
+                       return err;
+               }
+
+               snprintf(name, sizeof(name), "clkin%u", i);
+
+               channel->clk_parent = devm_clk_get(dev, name);
+               if (IS_ERR(channel->clk_parent)) {
+                       err = PTR_ERR(channel->clk_parent);
+                       if (err == -EPROBE_DEFER)
+                               return err;
+
+                       channel->clk_parent = NULL;
+               }
+       }
+
+       return 0;
+}
+
+static void meson_pwm_add_channels(struct meson_pwm *meson,
+                                  struct meson_pwm_channel *channels)
+{
+       unsigned int i;
+
+       for (i = 0; i < meson->chip.npwm; i++)
+               pwm_set_chip_data(&meson->chip.pwms[i], &channels[i]);
+}
+
+static int meson_pwm_probe(struct platform_device *pdev)
+{
+       struct meson_pwm_channel *channels;
+       struct meson_pwm *meson;
+       struct resource *regs;
+       int err;
+
+       meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
+       if (!meson)
+               return -ENOMEM;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       meson->base = devm_ioremap_resource(&pdev->dev, regs);
+       if (IS_ERR(meson->base))
+               return PTR_ERR(meson->base);
+
+       meson->chip.dev = &pdev->dev;
+       meson->chip.ops = &meson_pwm_ops;
+       meson->chip.base = -1;
+       meson->chip.npwm = 2;
+       meson->chip.of_xlate = of_pwm_xlate_with_flags;
+       meson->chip.of_pwm_n_cells = 3;
+
+       meson->data = of_device_get_match_data(&pdev->dev);
+       meson->inverter_mask = BIT(meson->chip.npwm) - 1;
+
+       channels = devm_kcalloc(&pdev->dev, meson->chip.npwm, sizeof(*meson),
+                               GFP_KERNEL);
+       if (!channels)
+               return -ENOMEM;
+
+       err = meson_pwm_init_channels(meson, channels);
+       if (err < 0)
+               return err;
+
+       err = pwmchip_add(&meson->chip);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err);
+               return err;
+       }
+
+       meson_pwm_add_channels(meson, channels);
+
+       platform_set_drvdata(pdev, meson);
+
+       return 0;
+}
+
+static int meson_pwm_remove(struct platform_device *pdev)
+{
+       struct meson_pwm *meson = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&meson->chip);
+}
+
+static struct platform_driver meson_pwm_driver = {
+       .driver = {
+               .name = "meson-pwm",
+               .of_match_table = meson_pwm_matches,
+       },
+       .probe = meson_pwm_probe,
+       .remove = meson_pwm_remove,
+};
+module_platform_driver(meson_pwm_driver);
+
+MODULE_ALIAS("platform:meson-pwm");
+MODULE_DESCRIPTION("Amlogic Meson PWM Generator driver");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_LICENSE("Dual BSD/GPL");
index 0ad3385..893940d 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
 
 #define DISP_PWM_EN            0x00
-#define PWM_ENABLE_MASK                BIT(0)
 
-#define DISP_PWM_COMMIT                0x08
-#define PWM_COMMIT_MASK                BIT(0)
-
-#define DISP_PWM_CON_0         0x10
 #define PWM_CLKDIV_SHIFT       16
 #define PWM_CLKDIV_MAX         0x3ff
 #define PWM_CLKDIV_MASK                (PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
 
-#define DISP_PWM_CON_1         0x14
 #define PWM_PERIOD_BIT_WIDTH   12
 #define PWM_PERIOD_MASK                ((1 << PWM_PERIOD_BIT_WIDTH) - 1)
 
 #define PWM_HIGH_WIDTH_SHIFT   16
 #define PWM_HIGH_WIDTH_MASK    (0x1fff << PWM_HIGH_WIDTH_SHIFT)
 
+struct mtk_pwm_data {
+       u32 enable_mask;
+       unsigned int con0;
+       u32 con0_sel;
+       unsigned int con1;
+
+       bool has_commit;
+       unsigned int commit;
+       unsigned int commit_mask;
+
+       unsigned int bls_debug;
+       u32 bls_debug_mask;
+};
+
 struct mtk_disp_pwm {
        struct pwm_chip chip;
+       const struct mtk_pwm_data *data;
        struct clk *clk_main;
        struct clk *clk_mm;
        void __iomem *base;
@@ -106,12 +116,21 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                return err;
        }
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
+       mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+                                PWM_CLKDIV_MASK,
                                 clk_div << PWM_CLKDIV_SHIFT);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
-                                PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
+       mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
+                                PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
+                                value);
+
+       if (mdp->data->has_commit) {
+               mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+                                        mdp->data->commit_mask,
+                                        mdp->data->commit_mask);
+               mtk_disp_pwm_update_bits(mdp, mdp->data->commit,
+                                        mdp->data->commit_mask,
+                                        0x0);
+       }
 
        clk_disable(mdp->clk_mm);
        clk_disable(mdp->clk_main);
@@ -134,7 +153,8 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
                return err;
        }
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
+       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
+                                mdp->data->enable_mask);
 
        return 0;
 }
@@ -143,7 +163,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
        struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
 
-       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
+       mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, mdp->data->enable_mask,
+                                0x0);
 
        clk_disable(mdp->clk_mm);
        clk_disable(mdp->clk_main);
@@ -166,6 +187,8 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
        if (!mdp)
                return -ENOMEM;
 
+       mdp->data = of_device_get_match_data(&pdev->dev);
+
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        mdp->base = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(mdp->base))
@@ -200,6 +223,19 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, mdp);
 
+       /*
+        * For MT2701, disable double buffer before writing register
+        * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
+        */
+       if (!mdp->data->has_commit) {
+               mtk_disp_pwm_update_bits(mdp, mdp->data->bls_debug,
+                                        mdp->data->bls_debug_mask,
+                                        mdp->data->bls_debug_mask);
+               mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
+                                        mdp->data->con0_sel,
+                                        mdp->data->con0_sel);
+       }
+
        return 0;
 
 disable_clk_mm:
@@ -221,9 +257,30 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
        return ret;
 }
 
+static const struct mtk_pwm_data mt2701_pwm_data = {
+       .enable_mask = BIT(16),
+       .con0 = 0xa8,
+       .con0_sel = 0x2,
+       .con1 = 0xac,
+       .has_commit = false,
+       .bls_debug = 0xb0,
+       .bls_debug_mask = 0x3,
+};
+
+static const struct mtk_pwm_data mt8173_pwm_data = {
+       .enable_mask = BIT(0),
+       .con0 = 0x10,
+       .con0_sel = 0x0,
+       .con1 = 0x14,
+       .has_commit = true,
+       .commit = 0x8,
+       .commit_mask = 0x1,
+};
+
 static const struct of_device_id mtk_disp_pwm_of_match[] = {
-       { .compatible = "mediatek,mt8173-disp-pwm" },
-       { .compatible = "mediatek,mt6595-disp-pwm" },
+       { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data},
+       { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
+       { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
        { }
 };
 MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
index ada2d32..f113cda 100644 (file)
@@ -193,9 +193,18 @@ static unsigned long pwm_samsung_calc_tin(struct samsung_pwm_chip *chip,
         * divider settings and choose the lowest divisor that can generate
         * frequencies lower than requested.
         */
-       for (div = variant->div_base; div < 4; ++div)
-               if ((rate >> (variant->bits + div)) < freq)
-                       break;
+       if (variant->bits < 32) {
+               /* Only for s3c24xx */
+               for (div = variant->div_base; div < 4; ++div)
+                       if ((rate >> (variant->bits + div)) < freq)
+                               break;
+       } else {
+               /*
+                * Other variants have enough counter bits to generate any
+                * requested rate, so no need to check higher divisors.
+                */
+               div = variant->div_base;
+       }
 
        pwm_samsung_set_divisor(chip, chan, BIT(div));
 
index 92abbd5..dd82dc8 100644 (file)
@@ -1,8 +1,10 @@
 /*
- * PWM device driver for ST SoCs.
- * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ * PWM device driver for ST SoCs
+ *
+ * Copyright (C) 2013-2016 STMicroelectronics (R&D) Limited
  *
- * Copyright (C) 2013-2014 STMicroelectronics (R&D) Limited
+ * Author: Ajit Pal Singh <ajitpal.singh@st.com>
+ *         Lee Jones <lee.jones@linaro.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -11,6 +13,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/interrupt.h>
 #include <linux/math64.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/wait.h>
+
+#define PWM_OUT_VAL(x) (0x00 + (4 * (x))) /* Device's Duty Cycle register */
+#define PWM_CPT_VAL(x) (0x10 + (4 * (x))) /* Capture value */
+#define PWM_CPT_EDGE(x) (0x30 + (4 * (x))) /* Edge to capture on */
 
-#define STI_DS_REG(ch) (4 * (ch))      /* Channel's Duty Cycle register */
-#define STI_PWMCR      0x50            /* Control/Config register */
-#define STI_INTEN      0x54            /* Interrupt Enable/Disable register */
-#define PWM_PRESCALE_LOW_MASK          0x0f
-#define PWM_PRESCALE_HIGH_MASK         0xf0
+#define STI_PWM_CTRL           0x50    /* Control/Config register */
+#define STI_INT_EN             0x54    /* Interrupt Enable/Disable register */
+#define STI_INT_STA            0x58    /* Interrupt Status register */
+#define PWM_INT_ACK            0x5c
+#define PWM_PRESCALE_LOW_MASK  0x0f
+#define PWM_PRESCALE_HIGH_MASK 0xf0
+#define PWM_CPT_EDGE_MASK      0x03
+#define PWM_INT_ACK_MASK       0x1ff
+
+#define STI_MAX_CPT_DEVS       4
+#define CPT_DC_MAX             0xff
 
 /* Regfield IDs */
 enum {
+       /* Bits in PWM_CTRL*/
        PWMCLK_PRESCALE_LOW,
        PWMCLK_PRESCALE_HIGH,
-       PWM_EN,
-       PWM_INT_EN,
+       CPTCLK_PRESCALE,
+
+       PWM_OUT_EN,
+       PWM_CPT_EN,
+
+       PWM_CPT_INT_EN,
+       PWM_CPT_INT_STAT,
 
        /* Keep last */
        MAX_REGFIELDS
 };
 
+/*
+ * Each capture input can be programmed to detect rising-edge, falling-edge,
+ * either edge or neither egde.
+ */
+enum sti_cpt_edge {
+       CPT_EDGE_DISABLED,
+       CPT_EDGE_RISING,
+       CPT_EDGE_FALLING,
+       CPT_EDGE_BOTH,
+};
+
+struct sti_cpt_ddata {
+       u32 snapshot[3];
+       unsigned int index;
+       struct mutex lock;
+       wait_queue_head_t wait;
+};
+
 struct sti_pwm_compat_data {
        const struct reg_field *reg_fields;
-       unsigned int num_chan;
+       unsigned int pwm_num_devs;
+       unsigned int cpt_num_devs;
        unsigned int max_pwm_cnt;
        unsigned int max_prescale;
 };
 
 struct sti_pwm_chip {
        struct device *dev;
-       struct clk *clk;
-       unsigned long clk_rate;
+       struct clk *pwm_clk;
+       struct clk *cpt_clk;
        struct regmap *regmap;
        struct sti_pwm_compat_data *cdata;
        struct regmap_field *prescale_low;
        struct regmap_field *prescale_high;
-       struct regmap_field *pwm_en;
-       struct regmap_field *pwm_int_en;
+       struct regmap_field *pwm_out_en;
+       struct regmap_field *pwm_cpt_en;
+       struct regmap_field *pwm_cpt_int_en;
+       struct regmap_field *pwm_cpt_int_stat;
        struct pwm_chip chip;
        struct pwm_device *cur;
        unsigned long configured;
@@ -64,10 +106,13 @@ struct sti_pwm_chip {
 };
 
 static const struct reg_field sti_pwm_regfields[MAX_REGFIELDS] = {
-       [PWMCLK_PRESCALE_LOW]   = REG_FIELD(STI_PWMCR, 0, 3),
-       [PWMCLK_PRESCALE_HIGH]  = REG_FIELD(STI_PWMCR, 11, 14),
-       [PWM_EN]                = REG_FIELD(STI_PWMCR, 9, 9),
-       [PWM_INT_EN]            = REG_FIELD(STI_INTEN, 0, 0),
+       [PWMCLK_PRESCALE_LOW] = REG_FIELD(STI_PWM_CTRL, 0, 3),
+       [PWMCLK_PRESCALE_HIGH] = REG_FIELD(STI_PWM_CTRL, 11, 14),
+       [CPTCLK_PRESCALE] = REG_FIELD(STI_PWM_CTRL, 4, 8),
+       [PWM_OUT_EN] = REG_FIELD(STI_PWM_CTRL, 9, 9),
+       [PWM_CPT_EN] = REG_FIELD(STI_PWM_CTRL, 10, 10),
+       [PWM_CPT_INT_EN] = REG_FIELD(STI_INT_EN, 1, 4),
+       [PWM_CPT_INT_STAT] = REG_FIELD(STI_INT_STA, 1, 4),
 };
 
 static inline struct sti_pwm_chip *to_sti_pwmchip(struct pwm_chip *chip)
@@ -82,61 +127,68 @@ static int sti_pwm_get_prescale(struct sti_pwm_chip *pc, unsigned long period,
                                unsigned int *prescale)
 {
        struct sti_pwm_compat_data *cdata = pc->cdata;
-       unsigned long val;
+       unsigned long clk_rate;
+       unsigned long value;
        unsigned int ps;
 
+       clk_rate = clk_get_rate(pc->pwm_clk);
+       if (!clk_rate) {
+               dev_err(pc->dev, "failed to get clock rate\n");
+               return -EINVAL;
+       }
+
        /*
-        * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_count + 1)) - 1
+        * prescale = ((period_ns * clk_rate) / (10^9 * (max_pwm_cnt + 1)) - 1
         */
-       val = NSEC_PER_SEC / pc->clk_rate;
-       val *= cdata->max_pwm_cnt + 1;
+       value = NSEC_PER_SEC / clk_rate;
+       value *= cdata->max_pwm_cnt + 1;
 
-       if (period % val) {
+       if (period % value)
                return -EINVAL;
-       } else {
-               ps  = period / val - 1;
-               if (ps > cdata->max_prescale)
-                       return -EINVAL;
-       }
+
+       ps  = period / value - 1;
+       if (ps > cdata->max_prescale)
+               return -EINVAL;
+
        *prescale = ps;
 
        return 0;
 }
 
 /*
- * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles.
- * The only way to change the period (apart from changing the PWM input clock)
- * is to change the PWM clock prescaler.
- * The prescaler is of 8 bits, so 256 prescaler values and hence
- * 256 possible period values are supported (for a particular clock rate).
- * The requested period will be applied only if it matches one of these
- * 256 values.
+ * For STiH4xx PWM IP, the PWM period is fixed to 256 local clock cycles. The
+ * only way to change the period (apart from changing the PWM input clock) is
+ * to change the PWM clock prescaler.
+ *
+ * The prescaler is of 8 bits, so 256 prescaler values and hence 256 possible
+ * period values are supported (for a particular clock rate). The requested
+ * period will be applied only if it matches one of these 256 values.
  */
 static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                        int duty_ns, int period_ns)
+                         int duty_ns, int period_ns)
 {
        struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
        struct sti_pwm_compat_data *cdata = pc->cdata;
+       unsigned int ncfg, value, prescale = 0;
        struct pwm_device *cur = pc->cur;
        struct device *dev = pc->dev;
-       unsigned int prescale = 0, pwmvalx;
-       int ret;
-       unsigned int ncfg;
        bool period_same = false;
+       int ret;
 
        ncfg = hweight_long(pc->configured);
        if (ncfg)
                period_same = (period_ns == pwm_get_period(cur));
 
-       /* Allow configuration changes if one of the
-        * following conditions satisfy.
-        * 1. No channels have been configured.
-        * 2. Only one channel has been configured and the new request
-        *    is for the same channel.
-        * 3. Only one channel has been configured and the new request is
-        *    for a new channel and period of the new channel is same as
-        *    the current configured period.
-        * 4. More than one channels are configured and period of the new
+       /*
+        * Allow configuration changes if one of the following conditions
+        * satisfy.
+        * 1. No devices have been configured.
+        * 2. Only one device has been configured and the new request is for
+        *    the same device.
+        * 3. Only one device has been configured and the new request is for
+        *    a new device and period of the new device is same as the current
+        *    configured period.
+        * 4. More than one devices are configured and period of the new
         *    requestis the same as the current period.
         */
        if (!ncfg ||
@@ -144,7 +196,11 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
            ((ncfg == 1) && (pwm->hwpwm != cur->hwpwm) && period_same) ||
            ((ncfg > 1) && period_same)) {
                /* Enable clock before writing to PWM registers. */
-               ret = clk_enable(pc->clk);
+               ret = clk_enable(pc->pwm_clk);
+               if (ret)
+                       return ret;
+
+               ret = clk_enable(pc->cpt_clk);
                if (ret)
                        return ret;
 
@@ -153,15 +209,15 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                        if (ret)
                                goto clk_dis;
 
-                       ret =
-                       regmap_field_write(pc->prescale_low,
-                                          prescale & PWM_PRESCALE_LOW_MASK);
+                       value = prescale & PWM_PRESCALE_LOW_MASK;
+
+                       ret = regmap_field_write(pc->prescale_low, value);
                        if (ret)
                                goto clk_dis;
 
-                       ret =
-                       regmap_field_write(pc->prescale_high,
-                               (prescale & PWM_PRESCALE_HIGH_MASK) >> 4);
+                       value = (prescale & PWM_PRESCALE_HIGH_MASK) >> 4;
+
+                       ret = regmap_field_write(pc->prescale_high, value);
                        if (ret)
                                goto clk_dis;
                }
@@ -172,25 +228,26 @@ static int sti_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
                 * PWM pulse = (max_pwm_count + 1) local cycles,
                 * that is continuous pulse: signal never goes low.
                 */
-               pwmvalx = cdata->max_pwm_cnt * duty_ns / period_ns;
+               value = cdata->max_pwm_cnt * duty_ns / period_ns;
 
-               ret = regmap_write(pc->regmap, STI_DS_REG(pwm->hwpwm), pwmvalx);
+               ret = regmap_write(pc->regmap, PWM_OUT_VAL(pwm->hwpwm), value);
                if (ret)
                        goto clk_dis;
 
-               ret = regmap_field_write(pc->pwm_int_en, 0);
+               ret = regmap_field_write(pc->pwm_cpt_int_en, 0);
 
                set_bit(pwm->hwpwm, &pc->configured);
                pc->cur = pwm;
 
-               dev_dbg(dev, "prescale:%u, period:%i, duty:%i, pwmvalx:%u\n",
-                       prescale, period_ns, duty_ns, pwmvalx);
+               dev_dbg(dev, "prescale:%u, period:%i, duty:%i, value:%u\n",
+                       prescale, period_ns, duty_ns, value);
        } else {
                return -EINVAL;
        }
 
 clk_dis:
-       clk_disable(pc->clk);
+       clk_disable(pc->pwm_clk);
+       clk_disable(pc->cpt_clk);
        return ret;
 }
 
@@ -201,23 +258,30 @@ static int sti_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        int ret = 0;
 
        /*
-        * Since we have a common enable for all PWM channels,
-        * do not enable if already enabled.
+        * Since we have a common enable for all PWM devices, do not enable if
+        * already enabled.
         */
        mutex_lock(&pc->sti_pwm_lock);
+
        if (!pc->en_count) {
-               ret = clk_enable(pc->clk);
+               ret = clk_enable(pc->pwm_clk);
+               if (ret)
+                       goto out;
+
+               ret = clk_enable(pc->cpt_clk);
                if (ret)
                        goto out;
 
-               ret = regmap_field_write(pc->pwm_en, 1);
+               ret = regmap_field_write(pc->pwm_out_en, 1);
                if (ret) {
-                       dev_err(dev, "failed to enable PWM device:%d\n",
-                               pwm->hwpwm);
+                       dev_err(dev, "failed to enable PWM device %u: %d\n",
+                               pwm->hwpwm, ret);
                        goto out;
                }
        }
+
        pc->en_count++;
+
 out:
        mutex_unlock(&pc->sti_pwm_lock);
        return ret;
@@ -228,13 +292,17 @@ static void sti_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
        struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
 
        mutex_lock(&pc->sti_pwm_lock);
+
        if (--pc->en_count) {
                mutex_unlock(&pc->sti_pwm_lock);
                return;
        }
-       regmap_field_write(pc->pwm_en, 0);
 
-       clk_disable(pc->clk);
+       regmap_field_write(pc->pwm_out_en, 0);
+
+       clk_disable(pc->pwm_clk);
+       clk_disable(pc->cpt_clk);
+
        mutex_unlock(&pc->sti_pwm_lock);
 }
 
@@ -245,7 +313,90 @@ static void sti_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
        clear_bit(pwm->hwpwm, &pc->configured);
 }
 
+static int sti_pwm_capture(struct pwm_chip *chip, struct pwm_device *pwm,
+                          struct pwm_capture *result, unsigned long timeout)
+{
+       struct sti_pwm_chip *pc = to_sti_pwmchip(chip);
+       struct sti_pwm_compat_data *cdata = pc->cdata;
+       struct sti_cpt_ddata *ddata = pwm_get_chip_data(pwm);
+       struct device *dev = pc->dev;
+       unsigned int effective_ticks;
+       unsigned long long high, low;
+       int ret;
+
+       if (pwm->hwpwm >= cdata->cpt_num_devs) {
+               dev_err(dev, "device %u is not valid\n", pwm->hwpwm);
+               return -EINVAL;
+       }
+
+       mutex_lock(&ddata->lock);
+       ddata->index = 0;
+
+       /* Prepare capture measurement */
+       regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_RISING);
+       regmap_field_write(pc->pwm_cpt_int_en, BIT(pwm->hwpwm));
+
+       /* Enable capture */
+       ret = regmap_field_write(pc->pwm_cpt_en, 1);
+       if (ret) {
+               dev_err(dev, "failed to enable PWM capture %u: %d\n",
+                       pwm->hwpwm, ret);
+               goto out;
+       }
+
+       ret = wait_event_interruptible_timeout(ddata->wait, ddata->index > 1,
+                                              msecs_to_jiffies(timeout));
+
+       regmap_write(pc->regmap, PWM_CPT_EDGE(pwm->hwpwm), CPT_EDGE_DISABLED);
+
+       if (ret == -ERESTARTSYS)
+               goto out;
+
+       switch (ddata->index) {
+       case 0:
+       case 1:
+               /*
+                * Getting here could mean:
+                *  - input signal is constant of less than 1 Hz
+                *  - there is no input signal at all
+                *
+                * In such case the frequency is rounded down to 0
+                */
+               result->period = 0;
+               result->duty_cycle = 0;
+
+               break;
+
+       case 2:
+               /* We have everying we need */
+               high = ddata->snapshot[1] - ddata->snapshot[0];
+               low = ddata->snapshot[2] - ddata->snapshot[1];
+
+               effective_ticks = clk_get_rate(pc->cpt_clk);
+
+               result->period = (high + low) * NSEC_PER_SEC;
+               result->period /= effective_ticks;
+
+               result->duty_cycle = high * NSEC_PER_SEC;
+               result->duty_cycle /= effective_ticks;
+
+               break;
+
+       default:
+               dev_err(dev, "internal error\n");
+               break;
+       }
+
+out:
+       /* Disable capture */
+       regmap_field_write(pc->pwm_cpt_en, 0);
+
+       mutex_unlock(&ddata->lock);
+       return ret;
+}
+
 static const struct pwm_ops sti_pwm_ops = {
+       .capture = sti_pwm_capture,
        .config = sti_pwm_config,
        .enable = sti_pwm_enable,
        .disable = sti_pwm_disable,
@@ -253,17 +404,98 @@ static const struct pwm_ops sti_pwm_ops = {
        .owner = THIS_MODULE,
 };
 
+static irqreturn_t sti_pwm_interrupt(int irq, void *data)
+{
+       struct sti_pwm_chip *pc = data;
+       struct device *dev = pc->dev;
+       struct sti_cpt_ddata *ddata;
+       int devicenum;
+       unsigned int cpt_int_stat;
+       unsigned int reg;
+       int ret = IRQ_NONE;
+
+       ret = regmap_field_read(pc->pwm_cpt_int_stat, &cpt_int_stat);
+       if (ret)
+               return ret;
+
+       while (cpt_int_stat) {
+               devicenum = ffs(cpt_int_stat) - 1;
+
+               ddata = pwm_get_chip_data(&pc->chip.pwms[devicenum]);
+
+               /*
+                * Capture input:
+                *    _______                   _______
+                *   |       |                 |       |
+                * __|       |_________________|       |________
+                *   ^0      ^1                ^2
+                *
+                * Capture start by the first available rising edge. When a
+                * capture event occurs, capture value (CPT_VALx) is stored,
+                * index incremented, capture edge changed.
+                *
+                * After the capture, if the index > 1, we have collected the
+                * necessary data so we signal the thread waiting for it and
+                * disable the capture by setting capture edge to none
+                */
+
+               regmap_read(pc->regmap,
+                           PWM_CPT_VAL(devicenum),
+                           &ddata->snapshot[ddata->index]);
+
+               switch (ddata->index) {
+               case 0:
+               case 1:
+                       regmap_read(pc->regmap, PWM_CPT_EDGE(devicenum), &reg);
+                       reg ^= PWM_CPT_EDGE_MASK;
+                       regmap_write(pc->regmap, PWM_CPT_EDGE(devicenum), reg);
+
+                       ddata->index++;
+                       break;
+
+               case 2:
+                       regmap_write(pc->regmap,
+                                    PWM_CPT_EDGE(devicenum),
+                                    CPT_EDGE_DISABLED);
+                       wake_up(&ddata->wait);
+                       break;
+
+               default:
+                       dev_err(dev, "Internal error\n");
+               }
+
+               cpt_int_stat &= ~BIT_MASK(devicenum);
+
+               ret = IRQ_HANDLED;
+       }
+
+       /* Just ACK everything */
+       regmap_write(pc->regmap, PWM_INT_ACK, PWM_INT_ACK_MASK);
+
+       return ret;
+}
+
 static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
 {
        struct device *dev = pc->dev;
        const struct reg_field *reg_fields;
        struct device_node *np = dev->of_node;
        struct sti_pwm_compat_data *cdata = pc->cdata;
-       u32 num_chan;
+       u32 num_devs;
+       int ret;
+
+       ret = of_property_read_u32(np, "st,pwm-num-chan", &num_devs);
+       if (!ret)
+               cdata->pwm_num_devs = num_devs;
+
+       ret = of_property_read_u32(np, "st,capture-num-chan", &num_devs);
+       if (!ret)
+               cdata->cpt_num_devs = num_devs;
 
-       of_property_read_u32(np, "st,pwm-num-chan", &num_chan);
-       if (num_chan)
-               cdata->num_chan = num_chan;
+       if (!cdata->pwm_num_devs && !cdata->cpt_num_devs) {
+               dev_err(dev, "No channels configured\n");
+               return -EINVAL;
+       }
 
        reg_fields = cdata->reg_fields;
 
@@ -277,15 +509,26 @@ static int sti_pwm_probe_dt(struct sti_pwm_chip *pc)
        if (IS_ERR(pc->prescale_high))
                return PTR_ERR(pc->prescale_high);
 
-       pc->pwm_en = devm_regmap_field_alloc(dev, pc->regmap,
-                                            reg_fields[PWM_EN]);
-       if (IS_ERR(pc->pwm_en))
-               return PTR_ERR(pc->pwm_en);
 
-       pc->pwm_int_en = devm_regmap_field_alloc(dev, pc->regmap,
-                                                reg_fields[PWM_INT_EN]);
-       if (IS_ERR(pc->pwm_int_en))
-               return PTR_ERR(pc->pwm_int_en);
+       pc->pwm_out_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                                reg_fields[PWM_OUT_EN]);
+       if (IS_ERR(pc->pwm_out_en))
+               return PTR_ERR(pc->pwm_out_en);
+
+       pc->pwm_cpt_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                                reg_fields[PWM_CPT_EN]);
+       if (IS_ERR(pc->pwm_cpt_en))
+               return PTR_ERR(pc->pwm_cpt_en);
+
+       pc->pwm_cpt_int_en = devm_regmap_field_alloc(dev, pc->regmap,
+                                               reg_fields[PWM_CPT_INT_EN]);
+       if (IS_ERR(pc->pwm_cpt_int_en))
+               return PTR_ERR(pc->pwm_cpt_int_en);
+
+       pc->pwm_cpt_int_stat = devm_regmap_field_alloc(dev, pc->regmap,
+                                               reg_fields[PWM_CPT_INT_STAT]);
+       if (PTR_ERR_OR_ZERO(pc->pwm_cpt_int_stat))
+               return PTR_ERR(pc->pwm_cpt_int_stat);
 
        return 0;
 }
@@ -302,7 +545,8 @@ static int sti_pwm_probe(struct platform_device *pdev)
        struct sti_pwm_compat_data *cdata;
        struct sti_pwm_chip *pc;
        struct resource *res;
-       int ret;
+       unsigned int i;
+       int irq, ret;
 
        pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
        if (!pc)
@@ -323,14 +567,28 @@ static int sti_pwm_probe(struct platform_device *pdev)
        if (IS_ERR(pc->regmap))
                return PTR_ERR(pc->regmap);
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Failed to obtain IRQ\n");
+               return irq;
+       }
+
+       ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
+                              pdev->name, pc);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request IRQ\n");
+               return ret;
+       }
+
        /*
         * Setup PWM data with default values: some values could be replaced
         * with specific ones provided from Device Tree.
         */
-       cdata->reg_fields   = &sti_pwm_regfields[0];
+       cdata->reg_fields = sti_pwm_regfields;
        cdata->max_prescale = 0xff;
-       cdata->max_pwm_cnt  = 255;
-       cdata->num_chan     = 1;
+       cdata->max_pwm_cnt = 255;
+       cdata->pwm_num_devs = 0;
+       cdata->cpt_num_devs = 0;
 
        pc->cdata = cdata;
        pc->dev = dev;
@@ -341,36 +599,64 @@ static int sti_pwm_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       pc->clk = of_clk_get_by_name(dev->of_node, "pwm");
-       if (IS_ERR(pc->clk)) {
+       if (!cdata->pwm_num_devs)
+               goto skip_pwm;
+
+       pc->pwm_clk = of_clk_get_by_name(dev->of_node, "pwm");
+       if (IS_ERR(pc->pwm_clk)) {
                dev_err(dev, "failed to get PWM clock\n");
-               return PTR_ERR(pc->clk);
+               return PTR_ERR(pc->pwm_clk);
        }
 
-       pc->clk_rate = clk_get_rate(pc->clk);
-       if (!pc->clk_rate) {
-               dev_err(dev, "failed to get clock rate\n");
-               return -EINVAL;
+       ret = clk_prepare(pc->pwm_clk);
+       if (ret) {
+               dev_err(dev, "failed to prepare clock\n");
+               return ret;
        }
 
-       ret = clk_prepare(pc->clk);
+skip_pwm:
+       if (!cdata->cpt_num_devs)
+               goto skip_cpt;
+
+       pc->cpt_clk = of_clk_get_by_name(dev->of_node, "capture");
+       if (IS_ERR(pc->cpt_clk)) {
+               dev_err(dev, "failed to get PWM capture clock\n");
+               return PTR_ERR(pc->cpt_clk);
+       }
+
+       ret = clk_prepare(pc->cpt_clk);
        if (ret) {
                dev_err(dev, "failed to prepare clock\n");
                return ret;
        }
 
+skip_cpt:
        pc->chip.dev = dev;
        pc->chip.ops = &sti_pwm_ops;
        pc->chip.base = -1;
-       pc->chip.npwm = pc->cdata->num_chan;
+       pc->chip.npwm = pc->cdata->pwm_num_devs;
        pc->chip.can_sleep = true;
 
        ret = pwmchip_add(&pc->chip);
        if (ret < 0) {
-               clk_unprepare(pc->clk);
+               clk_unprepare(pc->pwm_clk);
+               clk_unprepare(pc->cpt_clk);
                return ret;
        }
 
+       for (i = 0; i < cdata->cpt_num_devs; i++) {
+               struct sti_cpt_ddata *ddata;
+
+               ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+               if (!ddata)
+                       return -ENOMEM;
+
+               init_waitqueue_head(&ddata->wait);
+               mutex_init(&ddata->lock);
+
+               pwm_set_chip_data(&pc->chip.pwms[i], ddata);
+       }
+
        platform_set_drvdata(pdev, pc);
 
        return 0;
@@ -381,10 +667,11 @@ static int sti_pwm_remove(struct platform_device *pdev)
        struct sti_pwm_chip *pc = platform_get_drvdata(pdev);
        unsigned int i;
 
-       for (i = 0; i < pc->cdata->num_chan; i++)
+       for (i = 0; i < pc->cdata->pwm_num_devs; i++)
                pwm_disable(&pc->chip.pwms[i]);
 
-       clk_unprepare(pc->clk);
+       clk_unprepare(pc->pwm_clk);
+       clk_unprepare(pc->cpt_clk);
 
        return pwmchip_remove(&pc->chip);
 }
index 03a99a5..b0803f6 100644 (file)
@@ -284,6 +284,12 @@ static const struct sun4i_pwm_data sun4i_pwm_data_a20 = {
        .npwm = 2,
 };
 
+static const struct sun4i_pwm_data sun4i_pwm_data_h3 = {
+       .has_prescaler_bypass = true,
+       .has_rdy = true,
+       .npwm = 1,
+};
+
 static const struct of_device_id sun4i_pwm_dt_ids[] = {
        {
                .compatible = "allwinner,sun4i-a10-pwm",
@@ -297,6 +303,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = {
        }, {
                .compatible = "allwinner,sun7i-a20-pwm",
                .data = &sun4i_pwm_data_a20,
+       }, {
+               .compatible = "allwinner,sun8i-h3-pwm",
+               .data = &sun4i_pwm_data_h3,
        }, {
                /* sentinel */
        },
index 829f499..7fa85a1 100644 (file)
@@ -34,7 +34,6 @@ static int pwmss_probe(struct platform_device *pdev)
        struct device_node *node = pdev->dev.of_node;
 
        pm_runtime_enable(&pdev->dev);
-       pm_runtime_get_sync(&pdev->dev);
 
        /* Populate all the child nodes here... */
        ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
@@ -46,31 +45,13 @@ static int pwmss_probe(struct platform_device *pdev)
 
 static int pwmss_remove(struct platform_device *pdev)
 {
-       pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int pwmss_suspend(struct device *dev)
-{
-       pm_runtime_put_sync(dev);
-       return 0;
-}
-
-static int pwmss_resume(struct device *dev)
-{
-       pm_runtime_get_sync(dev);
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume);
-
 static struct platform_driver pwmss_driver = {
        .driver = {
                .name   = "pwmss",
-               .pm     = &pwmss_pm_ops,
                .of_match_table = pwmss_of_match,
        },
        .probe  = pwmss_probe,
index 04f7672..7a993b0 100644 (file)
@@ -269,6 +269,22 @@ static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
                goto out;
        }
 
+       val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
+               goto out;
+       }
+
+       val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXEN);
+
+       ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG);
+       if (ret < 0) {
+               dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label);
+               goto out;
+       }
+
        twl->twl6030_toggle3 = val;
 out:
        mutex_unlock(&twl->mutex);
index 18ed725..0296d81 100644 (file)
@@ -409,6 +409,24 @@ void pwmchip_sysfs_unexport(struct pwm_chip *chip)
        }
 }
 
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+       struct device *parent;
+       unsigned int i;
+
+       parent = class_find_device(&pwm_class, NULL, chip,
+                                  pwmchip_sysfs_match);
+       if (!parent)
+               return;
+
+       for (i = 0; i < chip->npwm; i++) {
+               struct pwm_device *pwm = &chip->pwms[i];
+
+               if (test_bit(PWMF_EXPORTED, &pwm->flags))
+                       pwm_unexport_child(parent, pwm);
+       }
+}
+
 static int __init pwm_sysfs_init(void)
 {
        return class_register(&pwm_class);
index cebc296..bad0e0e 100644 (file)
@@ -1841,24 +1841,19 @@ static int cm_chan_msg_send(void __user *arg)
 {
        struct rio_cm_msg msg;
        void *buf;
-       int ret = 0;
+       int ret;
 
        if (copy_from_user(&msg, arg, sizeof(msg)))
                return -EFAULT;
        if (msg.size > RIO_MAX_MSG_SIZE)
                return -EINVAL;
 
-       buf = kmalloc(msg.size, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       if (copy_from_user(buf, (void __user *)(uintptr_t)msg.msg, msg.size)) {
-               ret = -EFAULT;
-               goto out;
-       }
+       buf = memdup_user((void __user *)(uintptr_t)msg.msg, msg.size);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
 
        ret = riocm_ch_send(msg.ch_num, buf, msg.size);
-out:
+
        kfree(buf);
        return ret;
 }
index 3958f50..e0c747a 100644 (file)
@@ -495,7 +495,8 @@ static irqreturn_t max8973_thermal_irq(int irq, void *data)
 {
        struct max8973_chip *mchip = data;
 
-       thermal_zone_device_update(mchip->tz_device);
+       thermal_zone_device_update(mchip->tz_device,
+                                  THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index d1e0807..e859d14 100644 (file)
@@ -208,14 +208,14 @@ config RTC_DRV_AS3722
          will be called rtc-as3722.
 
 config RTC_DRV_DS1307
-       tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
+       tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025, ISL12057"
        help
          If you say yes here you get support for various compatible RTC
          chips (often with battery backup) connected with I2C. This driver
          should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
-         EPSON RX-8025 and probably other chips. In some cases the RTC
-         must already have been initialized (by manufacturing or a
-         bootloader).
+         EPSON RX-8025, Intersil ISL12057 and probably other chips. In some
+         cases the RTC must already have been initialized (by manufacturing or
+         bootloader).
 
          The first seven registers on these chips hold an RTC, and other
          registers may add features such as NVRAM, a trickle charger for
@@ -234,6 +234,20 @@ config RTC_DRV_DS1307_HWMON
          Say Y here if you want to expose temperature sensor data on
          rtc-ds1307 (only DS3231)
 
+config RTC_DRV_DS1307_CENTURY
+       bool "Century bit support for rtc-ds1307"
+       depends on RTC_DRV_DS1307
+       default n
+       help
+         The DS1307 driver suffered from a bug where it was enabling the
+         century bit inconditionnally but never used it when reading the time.
+         It made the driver unable to support dates beyond 2099.
+         Setting this option will add proper support for the century bit but if
+         the time was previously set using a kernel predating this option,
+         reading the date will return a date in the next century.
+         To solve that, you could boot a kernel without this option set, set
+         the RTC date and then boot a kernel with this option set.
+
 config RTC_DRV_DS1374
        tristate "Dallas/Maxim DS1374"
        help
@@ -374,16 +388,6 @@ config RTC_DRV_ISL12022
          This driver can also be built as a module. If so, the module
          will be called rtc-isl12022.
 
-config RTC_DRV_ISL12057
-       select REGMAP_I2C
-       tristate "Intersil ISL12057"
-       help
-         If you say yes here you get support for the Intersil ISL12057
-         I2C RTC chip.
-
-         This driver can also be built as a module. If so, the module
-         will be called rtc-isl12057.
-
 config RTC_DRV_X1205
        tristate "Xicor/Intersil X1205"
        help
@@ -661,6 +665,7 @@ config RTC_DRV_DS1343
          will be called rtc-ds1343.
 
 config RTC_DRV_DS1347
+       select REGMAP_SPI
        tristate "Dallas/Maxim DS1347"
        help
          If you say yes here you get support for the
@@ -1201,7 +1206,7 @@ comment "on-CPU RTC drivers"
 
 config RTC_DRV_ASM9260
        tristate "Alphascale asm9260 RTC"
-       depends on MACH_ASM9260
+       depends on MACH_ASM9260 || COMPILE_TEST
        help
          If you say yes here you get support for the RTC on the
          Alphascale asm9260 SoC.
@@ -1241,6 +1246,9 @@ config RTC_DRV_IMXDI
 config RTC_DRV_OMAP
        tristate "TI OMAP Real Time Clock"
        depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
+       depends on OF
+       depends on PINCTRL
+       select GENERIC_PINCONF
        help
          Say "yes" here to support the on chip real time clock
          present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx.
index 8fb994b..1ac694a 100644 (file)
@@ -72,7 +72,6 @@ obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
 obj-$(CONFIG_RTC_DRV_HYM8563)  += rtc-hym8563.o
 obj-$(CONFIG_RTC_DRV_IMXDI)    += rtc-imxdi.o
 obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
-obj-$(CONFIG_RTC_DRV_ISL12057) += rtc-isl12057.o
 obj-$(CONFIG_RTC_DRV_ISL1208)  += rtc-isl1208.o
 obj-$(CONFIG_RTC_DRV_JZ4740)   += rtc-jz4740.o
 obj-$(CONFIG_RTC_DRV_LP8788)   += rtc-lp8788.o
index 70b4fd0..9e33618 100644 (file)
@@ -327,6 +327,8 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip)
                        .flags = 0,
                };
 
+               of_property_read_string_index(np, "clock-output-names",
+                                             i, &init.name);
                clk->regmap = chip->regmap;
                clk->offset = AC100_CLKOUT_CTRL1 + i;
                clk->hw.init = &init;
@@ -552,6 +554,9 @@ static int ac100_rtc_probe(struct platform_device *pdev)
        int ret;
 
        chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
        platform_set_drvdata(pdev, chip);
        chip->dev = &pdev->dev;
        chip->regmap = ac100->regmap;
index 5219916..18a93d3 100644 (file)
@@ -112,8 +112,6 @@ struct asm9260_rtc_priv {
        void __iomem            *iobase;
        struct rtc_device       *rtc;
        struct clk              *clk;
-       /* io lock */
-       spinlock_t              lock;
 };
 
 static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
@@ -122,11 +120,15 @@ static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
        u32 isr;
        unsigned long events = 0;
 
+       mutex_lock(&priv->rtc->ops_lock);
        isr = ioread32(priv->iobase + HW_CIIR);
-       if (!isr)
+       if (!isr) {
+               mutex_unlock(&priv->rtc->ops_lock);
                return IRQ_NONE;
+       }
 
        iowrite32(0, priv->iobase + HW_CIIR);
+       mutex_unlock(&priv->rtc->ops_lock);
 
        events |= RTC_AF | RTC_IRQF;
 
@@ -139,9 +141,7 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
        u32 ctime0, ctime1, ctime2;
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        ctime0 = ioread32(priv->iobase + HW_CTIME0);
        ctime1 = ioread32(priv->iobase + HW_CTIME1);
        ctime2 = ioread32(priv->iobase + HW_CTIME2);
@@ -155,7 +155,6 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
                ctime1 = ioread32(priv->iobase + HW_CTIME1);
                ctime2 = ioread32(priv->iobase + HW_CTIME2);
        }
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        tm->tm_sec  = (ctime0 >> BM_CTIME0_SEC_S)  & BM_CTIME0_SEC_M;
        tm->tm_min  = (ctime0 >> BM_CTIME0_MIN_S)  & BM_CTIME0_MIN_M;
@@ -174,9 +173,7 @@ static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm)
 static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        /*
         * make sure SEC counter will not flip other counter on write time,
         * real value will be written at the enf of sequence.
@@ -191,7 +188,6 @@ static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
        iowrite32(tm->tm_hour, priv->iobase + HW_HOUR);
        iowrite32(tm->tm_min,  priv->iobase + HW_MIN);
        iowrite32(tm->tm_sec,  priv->iobase + HW_SEC);
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return 0;
 }
@@ -199,9 +195,7 @@ static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm)
 static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR);
        alrm->time.tm_mon  = ioread32(priv->iobase + HW_ALMON);
        alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM);
@@ -213,7 +207,6 @@ static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 
        alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0;
        alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0;
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return rtc_valid_tm(&alrm->time);
 }
@@ -221,9 +214,7 @@ static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
 {
        struct asm9260_rtc_priv *priv = dev_get_drvdata(dev);
-       unsigned long irq_flags;
 
-       spin_lock_irqsave(&priv->lock, irq_flags);
        iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR);
        iowrite32(alrm->time.tm_mon,  priv->iobase + HW_ALMON);
        iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM);
@@ -234,7 +225,6 @@ static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        iowrite32(alrm->time.tm_sec,  priv->iobase + HW_ALSEC);
 
        iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR);
-       spin_unlock_irqrestore(&priv->lock, irq_flags);
 
        return 0;
 }
index 83ac233..de8bf56 100644 (file)
@@ -187,7 +187,7 @@ static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id)
        return ret;
 }
 
-static struct rtc_class_ops at32_rtc_ops = {
+static const struct rtc_class_ops at32_rtc_ops = {
        .read_time      = at32_rtc_readtime,
        .set_time       = at32_rtc_settime,
        .read_alarm     = at32_rtc_readalarm,
index 0299988..3977424 100644 (file)
@@ -93,8 +93,15 @@ static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
        if (error)
                return error;
 
+       /*
+        * In case of oscillator failure, the register contents should be
+        * considered invalid. The flag is cleared the next time the RTC is set.
+        */
+       if (regs.minutes & BQ32K_OF)
+               return -EINVAL;
+
        tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK);
-       tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK);
+       tm->tm_min = bcd2bin(regs.minutes & BQ32K_MINUTES_MASK);
        tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK);
        tm->tm_mday = bcd2bin(regs.date);
        tm->tm_wday = bcd2bin(regs.day) - 1;
@@ -204,13 +211,10 @@ static int bq32k_probe(struct i2c_client *client,
 
        /* Check Oscillator Failure flag */
        error = bq32k_read(dev, &reg, BQ32K_MINUTES, 1);
-       if (!error && (reg & BQ32K_OF)) {
-               dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
-               reg &= ~BQ32K_OF;
-               error = bq32k_write(dev, &reg, BQ32K_MINUTES, 1);
-       }
        if (error)
                return error;
+       if (reg & BQ32K_OF)
+               dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
 
        if (client->dev.of_node)
                trickle_charger_of_init(dev, client->dev.of_node);
index 43745ca..dd3d598 100644 (file)
@@ -62,6 +62,8 @@ struct cmos_rtc {
        u8                      day_alrm;
        u8                      mon_alrm;
        u8                      century;
+
+       struct rtc_wkalrm       saved_wkalrm;
 };
 
 /* both platform and pnp busses use negative numbers for invalid irqs */
@@ -707,6 +709,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                goto cleanup1;
        }
 
+       hpet_rtc_timer_init();
+
        if (is_valid_irq(rtc_irq)) {
                irq_handler_t rtc_cmos_int_handler;
 
@@ -714,6 +718,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                        rtc_cmos_int_handler = hpet_rtc_interrupt;
                        retval = hpet_register_irq_handler(cmos_interrupt);
                        if (retval) {
+                               hpet_mask_rtc_irq_bit(RTC_IRQMASK);
                                dev_warn(dev, "hpet_register_irq_handler "
                                                " failed in rtc_init().");
                                goto cleanup1;
@@ -729,7 +734,6 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
                        goto cleanup1;
                }
        }
-       hpet_rtc_timer_init();
 
        /* export at least the first block of NVRAM */
        nvram.size = address_space - NVRAM_OFFSET;
@@ -844,8 +848,6 @@ static int cmos_aie_poweroff(struct device *dev)
        return retval;
 }
 
-#ifdef CONFIG_PM
-
 static int cmos_suspend(struct device *dev)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
@@ -877,6 +879,8 @@ static int cmos_suspend(struct device *dev)
                        enable_irq_wake(cmos->irq);
        }
 
+       cmos_read_alarm(dev, &cmos->saved_wkalrm);
+
        dev_dbg(dev, "suspend%s, ctrl %02x\n",
                        (tmp & RTC_AIE) ? ", alarm may wake" : "",
                        tmp);
@@ -892,12 +896,32 @@ static int cmos_suspend(struct device *dev)
  */
 static inline int cmos_poweroff(struct device *dev)
 {
+       if (!IS_ENABLED(CONFIG_PM))
+               return -ENOSYS;
+
        return cmos_suspend(dev);
 }
 
-#ifdef CONFIG_PM_SLEEP
+static void cmos_check_wkalrm(struct device *dev)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       struct rtc_wkalrm current_alarm;
+       time64_t t_current_expires;
+       time64_t t_saved_expires;
+
+       cmos_read_alarm(dev, &current_alarm);
+       t_current_expires = rtc_tm_to_time64(&current_alarm.time);
+       t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time);
+       if (t_current_expires != t_saved_expires ||
+           cmos->saved_wkalrm.enabled != current_alarm.enabled) {
+               cmos_set_alarm(dev, &cmos->saved_wkalrm);
+       }
+}
+
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control);
 
-static int cmos_resume(struct device *dev)
+static int __maybe_unused cmos_resume(struct device *dev)
 {
        struct cmos_rtc *cmos = dev_get_drvdata(dev);
        unsigned char tmp;
@@ -910,6 +934,9 @@ static int cmos_resume(struct device *dev)
                cmos->enabled_wake = 0;
        }
 
+       /* The BIOS might have changed the alarm, restore it */
+       cmos_check_wkalrm(dev);
+
        spin_lock_irq(&rtc_lock);
        tmp = cmos->suspend_ctrl;
        cmos->suspend_ctrl = 0;
@@ -936,6 +963,9 @@ static int cmos_resume(struct device *dev)
                        tmp &= ~RTC_AIE;
                        hpet_mask_rtc_irq_bit(RTC_AIE);
                } while (mask & RTC_AIE);
+
+               if (tmp & RTC_AIE)
+                       cmos_check_acpi_rtc_status(dev, &tmp);
        }
        spin_unlock_irq(&rtc_lock);
 
@@ -944,16 +974,6 @@ static int cmos_resume(struct device *dev)
        return 0;
 }
 
-#endif
-#else
-
-static inline int cmos_poweroff(struct device *dev)
-{
-       return -ENOSYS;
-}
-
-#endif
-
 static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
 
 /*----------------------------------------------------------------*/
@@ -973,6 +993,20 @@ static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
 static u32 rtc_handler(void *context)
 {
        struct device *dev = context;
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       unsigned char rtc_control = 0;
+       unsigned char rtc_intr;
+
+       spin_lock_irq(&rtc_lock);
+       if (cmos_rtc.suspend_ctrl)
+               rtc_control = CMOS_READ(RTC_CONTROL);
+       if (rtc_control & RTC_AIE) {
+               cmos_rtc.suspend_ctrl &= ~RTC_AIE;
+               CMOS_WRITE(rtc_control, RTC_CONTROL);
+               rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
+               rtc_update_irq(cmos->rtc, 1, rtc_intr);
+       }
+       spin_unlock_irq(&rtc_lock);
 
        pm_wakeup_event(dev, 0);
        acpi_clear_event(ACPI_EVENT_RTC);
@@ -1039,12 +1073,39 @@ static void cmos_wake_setup(struct device *dev)
        device_init_wakeup(dev, 1);
 }
 
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control)
+{
+       struct cmos_rtc *cmos = dev_get_drvdata(dev);
+       acpi_event_status rtc_status;
+       acpi_status status;
+
+       if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
+               return;
+
+       status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "Could not get RTC status\n");
+       } else if (rtc_status & ACPI_EVENT_FLAG_SET) {
+               unsigned char mask;
+               *rtc_control &= ~RTC_AIE;
+               CMOS_WRITE(*rtc_control, RTC_CONTROL);
+               mask = CMOS_READ(RTC_INTR_FLAGS);
+               rtc_update_irq(cmos->rtc, 1, mask);
+       }
+}
+
 #else
 
 static void cmos_wake_setup(struct device *dev)
 {
 }
 
+static void cmos_check_acpi_rtc_status(struct device *dev,
+                                      unsigned char *rtc_control)
+{
+}
+
 #endif
 
 #ifdef CONFIG_PNP
@@ -1206,9 +1267,7 @@ static struct platform_driver cmos_platform_driver = {
        .shutdown       = cmos_platform_shutdown,
        .driver = {
                .name           = driver_name,
-#ifdef CONFIG_PM
                .pm             = &cmos_pm_ops,
-#endif
                .of_match_table = of_match_ptr(of_cmos_match),
        }
 };
index 101b7a2..cfc4141 100644 (file)
@@ -140,7 +140,7 @@ static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops coh901331_ops = {
+static const struct rtc_class_ops coh901331_ops = {
        .read_time = coh901331_read_time,
        .set_mmss = coh901331_set_mmss,
        .read_alarm = coh901331_read_alarm,
index dba60c1..caf3556 100644 (file)
@@ -469,7 +469,7 @@ static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
        return 0;
 }
 
-static struct rtc_class_ops davinci_rtc_ops = {
+static const struct rtc_class_ops davinci_rtc_ops = {
        .ioctl                  = davinci_rtc_ioctl,
        .read_time              = davinci_rtc_read_time,
        .set_time               = davinci_rtc_set_time,
index 8d05596..b253bf1 100644 (file)
@@ -159,7 +159,7 @@ static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops dc_rtc_ops = {
+static const struct rtc_class_ops dc_rtc_ops = {
        .read_time              = dc_rtc_read_time,
        .set_mmss               = dc_rtc_set_mmss,
        .read_alarm             = dc_rtc_read_alarm,
index f5dd09f..0ec4be6 100644 (file)
@@ -102,7 +102,7 @@ static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time)
        return rtc_valid_tm(time);
 }
 
-static struct rtc_class_ops ds1302_rtc_ops = {
+static const struct rtc_class_ops ds1302_rtc_ops = {
        .read_time      = ds1302_rtc_get_time,
        .set_time       = ds1302_rtc_set_time,
 };
index 8e1c5cb..4e31036 100644 (file)
@@ -186,6 +186,7 @@ static const struct i2c_device_id ds1307_id[] = {
        { "mcp7941x", mcp794xx },
        { "pt7c4338", ds_1307 },
        { "rx8025", rx_8025 },
+       { "isl12057", ds_1337 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ds1307_id);
@@ -382,10 +383,25 @@ static int ds1307_get_time(struct device *dev, struct rtc_time *t)
        t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
        tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
        t->tm_mon = bcd2bin(tmp) - 1;
-
-       /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
        t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
 
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+       switch (ds1307->type) {
+       case ds_1337:
+       case ds_1339:
+       case ds_3231:
+               if (ds1307->regs[DS1307_REG_MONTH] & DS1337_BIT_CENTURY)
+                       t->tm_year += 100;
+               break;
+       case ds_1340:
+               if (ds1307->regs[DS1307_REG_HOUR] & DS1340_BIT_CENTURY)
+                       t->tm_year += 100;
+               break;
+       default:
+               break;
+       }
+#endif
+
        dev_dbg(dev, "%s secs=%d, mins=%d, "
                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
                "read", t->tm_sec, t->tm_min,
@@ -409,6 +425,27 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
                t->tm_hour, t->tm_mday,
                t->tm_mon, t->tm_year, t->tm_wday);
 
+#ifdef CONFIG_RTC_DRV_DS1307_CENTURY
+       if (t->tm_year < 100)
+               return -EINVAL;
+
+       switch (ds1307->type) {
+       case ds_1337:
+       case ds_1339:
+       case ds_3231:
+       case ds_1340:
+               if (t->tm_year > 299)
+                       return -EINVAL;
+       default:
+               if (t->tm_year > 199)
+                       return -EINVAL;
+               break;
+       }
+#else
+       if (t->tm_year < 100 || t->tm_year > 199)
+               return -EINVAL;
+#endif
+
        buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
        buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
        buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
@@ -424,11 +461,13 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
        case ds_1337:
        case ds_1339:
        case ds_3231:
-               buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
+               if (t->tm_year > 199)
+                       buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
                break;
        case ds_1340:
-               buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
-                               | DS1340_BIT_CENTURY;
+               buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN;
+               if (t->tm_year > 199)
+                       buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY;
                break;
        case mcp794xx:
                /*
@@ -1295,6 +1334,11 @@ static int ds1307_probe(struct i2c_client *client,
        if (of_property_read_bool(client->dev.of_node, "wakeup-source")) {
                ds1307_can_wakeup_device = true;
        }
+       /* Intersil ISL12057 DT backward compatibility */
+       if (of_property_read_bool(client->dev.of_node,
+                                 "isil,irq2-can-wakeup-machine")) {
+               ds1307_can_wakeup_device = true;
+       }
 #endif
 
        switch (ds1307->type) {
index 641e8e8..ccfc9d4 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/rtc.h>
 #include <linux/spi/spi.h>
 #include <linux/bcd.h>
+#include <linux/regmap.h>
 
 /* Registers in ds1347 rtc */
 
 #define DS1347_STATUS_REG      0x17
 #define DS1347_CLOCK_BURST     0x3F
 
-static int ds1347_read_reg(struct device *dev, unsigned char address,
-                               unsigned char *data)
-{
-       struct spi_device *spi = to_spi_device(dev);
-
-       *data = address | 0x80;
-
-       return spi_write_then_read(spi, data, 1, data, 1);
-}
-
-static int ds1347_write_reg(struct device *dev, unsigned char address,
-                               unsigned char data)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       unsigned char buf[2];
-
-       buf[0] = address & 0x7F;
-       buf[1] = data;
+static const struct regmap_range ds1347_ranges[] = {
+       {
+               .range_min = DS1347_SECONDS_REG,
+               .range_max = DS1347_STATUS_REG,
+       },
+};
 
-       return spi_write_then_read(spi, buf, 2, NULL, 0);
-}
+static const struct regmap_access_table ds1347_access_table = {
+       .yes_ranges = ds1347_ranges,
+       .n_yes_ranges = ARRAY_SIZE(ds1347_ranges),
+};
 
 static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
 {
        struct spi_device *spi = to_spi_device(dev);
+       struct regmap *map;
        int err;
        unsigned char buf[8];
 
-       buf[0] = DS1347_CLOCK_BURST | 0x80;
+       map = spi_get_drvdata(spi);
 
-       err = spi_write_then_read(spi, buf, 1, buf, 8);
+       err = regmap_bulk_read(map, DS1347_CLOCK_BURST, buf, 8);
        if (err)
                return err;
 
@@ -80,25 +72,27 @@ static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
 static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
 {
        struct spi_device *spi = to_spi_device(dev);
-       unsigned char buf[9];
+       struct regmap *map;
+       unsigned char buf[8];
+
+       map = spi_get_drvdata(spi);
 
-       buf[0] = DS1347_CLOCK_BURST & 0x7F;
-       buf[1] = bin2bcd(dt->tm_sec);
-       buf[2] = bin2bcd(dt->tm_min);
-       buf[3] = (bin2bcd(dt->tm_hour) & 0x3F);
-       buf[4] = bin2bcd(dt->tm_mday);
-       buf[5] = bin2bcd(dt->tm_mon + 1);
-       buf[6] = bin2bcd(dt->tm_wday + 1);
+       buf[0] = bin2bcd(dt->tm_sec);
+       buf[1] = bin2bcd(dt->tm_min);
+       buf[2] = (bin2bcd(dt->tm_hour) & 0x3F);
+       buf[3] = bin2bcd(dt->tm_mday);
+       buf[4] = bin2bcd(dt->tm_mon + 1);
+       buf[5] = bin2bcd(dt->tm_wday + 1);
 
        /* year in linux is from 1900 i.e in range of 100
        in rtc it is from 00 to 99 */
        dt->tm_year = dt->tm_year % 100;
 
-       buf[7] = bin2bcd(dt->tm_year);
-       buf[8] = bin2bcd(0x00);
+       buf[6] = bin2bcd(dt->tm_year);
+       buf[7] = bin2bcd(0x00);
 
        /* write the rtc settings */
-       return spi_write_then_read(spi, buf, 9, NULL, 0);
+       return regmap_bulk_write(map, DS1347_CLOCK_BURST, buf, 8);
 }
 
 static const struct rtc_class_ops ds1347_rtc_ops = {
@@ -109,35 +103,53 @@ static const struct rtc_class_ops ds1347_rtc_ops = {
 static int ds1347_probe(struct spi_device *spi)
 {
        struct rtc_device *rtc;
-       unsigned char data;
+       struct regmap_config config;
+       struct regmap *map;
+       unsigned int data;
        int res;
 
+       memset(&config, 0, sizeof(config));
+       config.reg_bits = 8;
+       config.val_bits = 8;
+       config.read_flag_mask = 0x80;
+       config.max_register = 0x3F;
+       config.wr_table = &ds1347_access_table;
+
        /* spi setup with ds1347 in mode 3 and bits per word as 8 */
        spi->mode = SPI_MODE_3;
        spi->bits_per_word = 8;
        spi_setup(spi);
 
+       map = devm_regmap_init_spi(spi, &config);
+
+       if (IS_ERR(map)) {
+               dev_err(&spi->dev, "ds1347 regmap init spi failed\n");
+               return PTR_ERR(map);
+       }
+
+       spi_set_drvdata(spi, map);
+
        /* RTC Settings */
-       res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data);
+       res = regmap_read(map, DS1347_SECONDS_REG, &data);
        if (res)
                return res;
 
        /* Disable the write protect of rtc */
-       ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+       regmap_read(map, DS1347_CONTROL_REG, &data);
        data = data & ~(1<<7);
-       ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data);
+       regmap_write(map, DS1347_CONTROL_REG, data);
 
        /* Enable the oscillator , disable the oscillator stop flag,
         and glitch filter to reduce current consumption */
-       ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+       regmap_read(map, DS1347_STATUS_REG, &data);
        data = data & 0x1B;
-       ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data);
+       regmap_write(map, DS1347_STATUS_REG, data);
 
        /* display the settings */
-       ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+       regmap_read(map, DS1347_CONTROL_REG, &data);
        dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data);
 
-       ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+       regmap_read(map, DS1347_STATUS_REG, &data);
        dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data);
 
        rtc = devm_rtc_device_register(&spi->dev, "ds1347",
@@ -146,8 +158,6 @@ static int ds1347_probe(struct spi_device *spi)
        if (IS_ERR(rtc))
                return PTR_ERR(rtc);
 
-       spi_set_drvdata(spi, rtc);
-
        return 0;
 }
 
index b57505e..688debc 100644 (file)
@@ -110,7 +110,7 @@ static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
        return 0;
 }
 
-static struct rtc_class_ops gemini_rtc_ops = {
+static const struct rtc_class_ops gemini_rtc_ops = {
        .read_time     = gemini_rtc_read_time,
        .set_time      = gemini_rtc_set_time,
 };
diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c
deleted file mode 100644 (file)
index 0e7f0f5..0000000
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * rtc-isl12057 - Driver for Intersil ISL12057 I2C Real Time Clock
- *
- * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org>
- *
- * This work is largely based on Intersil ISL1208 driver developed by
- * Hebert Valerio Riedel <hvr@gnu.org>.
- *
- * Detailed datasheet on which this development is based is available here:
- *
- *  http://natisbad.org/NAS2/refs/ISL12057.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/rtc.h>
-#include <linux/i2c.h>
-#include <linux/bcd.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/regmap.h>
-
-#define DRV_NAME "rtc-isl12057"
-
-/* RTC section */
-#define ISL12057_REG_RTC_SC    0x00    /* Seconds */
-#define ISL12057_REG_RTC_MN    0x01    /* Minutes */
-#define ISL12057_REG_RTC_HR    0x02    /* Hours */
-#define ISL12057_REG_RTC_HR_PM BIT(5)  /* AM/PM bit in 12h format */
-#define ISL12057_REG_RTC_HR_MIL BIT(6) /* 24h/12h format */
-#define ISL12057_REG_RTC_DW    0x03    /* Day of the Week */
-#define ISL12057_REG_RTC_DT    0x04    /* Date */
-#define ISL12057_REG_RTC_MO    0x05    /* Month */
-#define ISL12057_REG_RTC_MO_CEN        BIT(7)  /* Century bit */
-#define ISL12057_REG_RTC_YR    0x06    /* Year */
-#define ISL12057_RTC_SEC_LEN   7
-
-/* Alarm 1 section */
-#define ISL12057_REG_A1_SC     0x07    /* Alarm 1 Seconds */
-#define ISL12057_REG_A1_MN     0x08    /* Alarm 1 Minutes */
-#define ISL12057_REG_A1_HR     0x09    /* Alarm 1 Hours */
-#define ISL12057_REG_A1_HR_PM  BIT(5)  /* AM/PM bit in 12h format */
-#define ISL12057_REG_A1_HR_MIL BIT(6)  /* 24h/12h format */
-#define ISL12057_REG_A1_DWDT   0x0A    /* Alarm 1 Date / Day of the week */
-#define ISL12057_REG_A1_DWDT_B BIT(6)  /* DW / DT selection bit */
-#define ISL12057_A1_SEC_LEN    4
-
-/* Alarm 2 section */
-#define ISL12057_REG_A2_MN     0x0B    /* Alarm 2 Minutes */
-#define ISL12057_REG_A2_HR     0x0C    /* Alarm 2 Hours */
-#define ISL12057_REG_A2_DWDT   0x0D    /* Alarm 2 Date / Day of the week */
-#define ISL12057_A2_SEC_LEN    3
-
-/* Control/Status registers */
-#define ISL12057_REG_INT       0x0E
-#define ISL12057_REG_INT_A1IE  BIT(0)  /* Alarm 1 interrupt enable bit */
-#define ISL12057_REG_INT_A2IE  BIT(1)  /* Alarm 2 interrupt enable bit */
-#define ISL12057_REG_INT_INTCN BIT(2)  /* Interrupt control enable bit */
-#define ISL12057_REG_INT_RS1   BIT(3)  /* Freq out control bit 1 */
-#define ISL12057_REG_INT_RS2   BIT(4)  /* Freq out control bit 2 */
-#define ISL12057_REG_INT_EOSC  BIT(7)  /* Oscillator enable bit */
-
-#define ISL12057_REG_SR                0x0F
-#define ISL12057_REG_SR_A1F    BIT(0)  /* Alarm 1 interrupt bit */
-#define ISL12057_REG_SR_A2F    BIT(1)  /* Alarm 2 interrupt bit */
-#define ISL12057_REG_SR_OSF    BIT(7)  /* Oscillator failure bit */
-
-/* Register memory map length */
-#define ISL12057_MEM_MAP_LEN   0x10
-
-struct isl12057_rtc_data {
-       struct rtc_device *rtc;
-       struct regmap *regmap;
-       struct mutex lock;
-       int irq;
-};
-
-static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs)
-{
-       tm->tm_sec = bcd2bin(regs[ISL12057_REG_RTC_SC]);
-       tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]);
-
-       if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */
-               tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x1f);
-               if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM)
-                       tm->tm_hour += 12;
-       } else {                                            /* 24 hour mode */
-               tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x3f);
-       }
-
-       tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]);
-       tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */
-       tm->tm_mon  = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */
-       tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100;
-
-       /* Check if years register has overflown from 99 to 00 */
-       if (regs[ISL12057_REG_RTC_MO] & ISL12057_REG_RTC_MO_CEN)
-               tm->tm_year += 100;
-}
-
-static int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm)
-{
-       u8 century_bit;
-
-       /*
-        * The clock has an 8 bit wide bcd-coded register for the year.
-        * It also has a century bit encoded in MO flag which provides
-        * information about overflow of year register from 99 to 00.
-        * tm_year is an offset from 1900 and we are interested in the
-        * 2000-2199 range, so any value less than 100 or larger than
-        * 299 is invalid.
-        */
-       if (tm->tm_year < 100 || tm->tm_year > 299)
-               return -EINVAL;
-
-       century_bit = (tm->tm_year > 199) ? ISL12057_REG_RTC_MO_CEN : 0;
-
-       regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec);
-       regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min);
-       regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */
-       regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday);
-       regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1) | century_bit;
-       regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year % 100);
-       regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1);
-
-       return 0;
-}
-
-/*
- * Try and match register bits w/ fixed null values to see whether we
- * are dealing with an ISL12057. Note: this function is called early
- * during init and hence does need mutex protection.
- */
-static int isl12057_i2c_validate_chip(struct regmap *regmap)
-{
-       u8 regs[ISL12057_MEM_MAP_LEN];
-       static const u8 mask[ISL12057_MEM_MAP_LEN] = { 0x80, 0x80, 0x80, 0xf8,
-                                                      0xc0, 0x60, 0x00, 0x00,
-                                                      0x00, 0x00, 0x00, 0x00,
-                                                      0x00, 0x00, 0x60, 0x7c };
-       int ret, i;
-
-       ret = regmap_bulk_read(regmap, 0, regs, ISL12057_MEM_MAP_LEN);
-       if (ret)
-               return ret;
-
-       for (i = 0; i < ISL12057_MEM_MAP_LEN; ++i) {
-               if (regs[i] & mask[i])  /* check if bits are cleared */
-                       return -ENODEV;
-       }
-
-       return 0;
-}
-
-static int _isl12057_rtc_clear_alarm(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_A1F, 0);
-       if (ret)
-               dev_err(dev, "%s: clearing alarm failed (%d)\n", __func__, ret);
-
-       return ret;
-}
-
-static int _isl12057_rtc_update_alarm(struct device *dev, int enable)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_INT,
-                                ISL12057_REG_INT_A1IE,
-                                enable ? ISL12057_REG_INT_A1IE : 0);
-       if (ret)
-               dev_err(dev, "%s: changing alarm interrupt flag failed (%d)\n",
-                       __func__, ret);
-
-       return ret;
-}
-
-/*
- * Note: as we only read from device and do not perform any update, there is
- * no need for an equivalent function which would try and get driver's main
- * lock. Here, it is safe for everyone if we just use regmap internal lock
- * on the device when reading.
- */
-static int _isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       u8 regs[ISL12057_RTC_SEC_LEN];
-       unsigned int sr;
-       int ret;
-
-       ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr);
-       if (ret) {
-               dev_err(dev, "%s: unable to read oscillator status flag (%d)\n",
-                       __func__, ret);
-               goto out;
-       } else {
-               if (sr & ISL12057_REG_SR_OSF) {
-                       ret = -ENODATA;
-                       goto out;
-               }
-       }
-
-       ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs,
-                              ISL12057_RTC_SEC_LEN);
-       if (ret)
-               dev_err(dev, "%s: unable to read RTC time section (%d)\n",
-                       __func__, ret);
-
-out:
-       if (ret)
-               return ret;
-
-       isl12057_rtc_regs_to_tm(tm, regs);
-
-       return rtc_valid_tm(tm);
-}
-
-static int isl12057_rtc_update_alarm(struct device *dev, int enable)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       int ret;
-
-       mutex_lock(&data->lock);
-       ret = _isl12057_rtc_update_alarm(dev, enable);
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       struct rtc_time *alarm_tm = &alarm->time;
-       u8 regs[ISL12057_A1_SEC_LEN];
-       unsigned int ir;
-       int ret;
-
-       mutex_lock(&data->lock);
-       ret = regmap_bulk_read(data->regmap, ISL12057_REG_A1_SC, regs,
-                              ISL12057_A1_SEC_LEN);
-       if (ret) {
-               dev_err(dev, "%s: reading alarm section failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       alarm_tm->tm_sec  = bcd2bin(regs[0] & 0x7f);
-       alarm_tm->tm_min  = bcd2bin(regs[1] & 0x7f);
-       alarm_tm->tm_hour = bcd2bin(regs[2] & 0x3f);
-       alarm_tm->tm_mday = bcd2bin(regs[3] & 0x3f);
-
-       ret = regmap_read(data->regmap, ISL12057_REG_INT, &ir);
-       if (ret) {
-               dev_err(dev, "%s: reading alarm interrupt flag failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       alarm->enabled = !!(ir & ISL12057_REG_INT_A1IE);
-
-err_unlock:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       struct rtc_time *alarm_tm = &alarm->time;
-       unsigned long rtc_secs, alarm_secs;
-       u8 regs[ISL12057_A1_SEC_LEN];
-       struct rtc_time rtc_tm;
-       int ret, enable = 1;
-
-       mutex_lock(&data->lock);
-       ret = _isl12057_rtc_read_time(dev, &rtc_tm);
-       if (ret)
-               goto err_unlock;
-
-       ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
-       if (ret)
-               goto err_unlock;
-
-       ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
-       if (ret)
-               goto err_unlock;
-
-       /* If alarm time is before current time, disable the alarm */
-       if (!alarm->enabled || alarm_secs <= rtc_secs) {
-               enable = 0;
-       } else {
-               /*
-                * Chip only support alarms up to one month in the future. Let's
-                * return an error if we get something after that limit.
-                * Comparison is done by incrementing rtc_tm month field by one
-                * and checking alarm value is still below.
-                */
-               if (rtc_tm.tm_mon == 11) { /* handle year wrapping */
-                       rtc_tm.tm_mon = 0;
-                       rtc_tm.tm_year += 1;
-               } else {
-                       rtc_tm.tm_mon += 1;
-               }
-
-               ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
-               if (ret)
-                       goto err_unlock;
-
-               if (alarm_secs > rtc_secs) {
-                       dev_err(dev, "%s: max for alarm is one month (%d)\n",
-                               __func__, ret);
-                       ret = -EINVAL;
-                       goto err_unlock;
-               }
-       }
-
-       /* Disable the alarm before modifying it */
-       ret = _isl12057_rtc_update_alarm(dev, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to disable the alarm (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       /* Program alarm registers */
-       regs[0] = bin2bcd(alarm_tm->tm_sec) & 0x7f;
-       regs[1] = bin2bcd(alarm_tm->tm_min) & 0x7f;
-       regs[2] = bin2bcd(alarm_tm->tm_hour) & 0x3f;
-       regs[3] = bin2bcd(alarm_tm->tm_mday) & 0x3f;
-
-       ret = regmap_bulk_write(data->regmap, ISL12057_REG_A1_SC, regs,
-                               ISL12057_A1_SEC_LEN);
-       if (ret < 0) {
-               dev_err(dev, "%s: writing alarm section failed (%d)\n",
-                       __func__, ret);
-               goto err_unlock;
-       }
-
-       /* Enable or disable alarm */
-       ret = _isl12057_rtc_update_alarm(dev, enable);
-
-err_unlock:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-       u8 regs[ISL12057_RTC_SEC_LEN];
-       int ret;
-
-       ret = isl12057_rtc_tm_to_regs(regs, tm);
-       if (ret)
-               return ret;
-
-       mutex_lock(&data->lock);
-       ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs,
-                               ISL12057_RTC_SEC_LEN);
-       if (ret) {
-               dev_err(dev, "%s: unable to write RTC time section (%d)\n",
-                       __func__, ret);
-               goto out;
-       }
-
-       /*
-        * Now that RTC time has been updated, let's clear oscillator
-        * failure flag, if needed.
-        */
-       ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_OSF, 0);
-       if (ret < 0)
-               dev_err(dev, "%s: unable to clear osc. failure bit (%d)\n",
-                       __func__, ret);
-
-out:
-       mutex_unlock(&data->lock);
-
-       return ret;
-}
-
-/*
- * Check current RTC status and enable/disable what needs to be. Return 0 if
- * everything went ok and a negative value upon error. Note: this function
- * is called early during init and hence does need mutex protection.
- */
-static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap)
-{
-       int ret;
-
-       /* Enable oscillator if not already running */
-       ret = regmap_update_bits(regmap, ISL12057_REG_INT,
-                                ISL12057_REG_INT_EOSC, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to enable oscillator (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       /* Clear alarm bit if needed */
-       ret = regmap_update_bits(regmap, ISL12057_REG_SR,
-                                ISL12057_REG_SR_A1F, 0);
-       if (ret < 0) {
-               dev_err(dev, "%s: unable to clear alarm bit (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-/*
- * One would expect the device to be marked as a wakeup source only
- * when an IRQ pin of the RTC is routed to an interrupt line of the
- * CPU. In practice, such an IRQ pin can be connected to a PMIC and
- * this allows the device to be powered up when RTC alarm rings. This
- * is for instance the case on ReadyNAS 102, 104 and 2120. On those
- * devices with no IRQ driectly connected to the SoC, the RTC chip
- * can be forced as a wakeup source by stating that explicitly in
- * the device's .dts file using the "wakeup-source" boolean property.
- * This will guarantee 'wakealarm' sysfs entry is available on the device.
- *
- * The function below returns 1, i.e. the capability of the chip to
- * wakeup the device, based on IRQ availability or if the boolean
- * property has been set in the .dts file. Otherwise, it returns 0.
- */
-
-static bool isl12057_can_wakeup_machine(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-
-       return data->irq || of_property_read_bool(dev->of_node, "wakeup-source")
-               || of_property_read_bool(dev->of_node, /* legacy */
-                                        "isil,irq2-can-wakeup-machine");
-}
-#else
-static bool isl12057_can_wakeup_machine(struct device *dev)
-{
-       struct isl12057_rtc_data *data = dev_get_drvdata(dev);
-
-       return !!data->irq;
-}
-#endif
-
-static int isl12057_rtc_alarm_irq_enable(struct device *dev,
-                                        unsigned int enable)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-       int ret = -ENOTTY;
-
-       if (rtc_data->irq)
-               ret = isl12057_rtc_update_alarm(dev, enable);
-
-       return ret;
-}
-
-static irqreturn_t isl12057_rtc_interrupt(int irq, void *data)
-{
-       struct i2c_client *client = data;
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(&client->dev);
-       struct rtc_device *rtc = rtc_data->rtc;
-       int ret, handled = IRQ_NONE;
-       unsigned int sr;
-
-       ret = regmap_read(rtc_data->regmap, ISL12057_REG_SR, &sr);
-       if (!ret && (sr & ISL12057_REG_SR_A1F)) {
-               dev_dbg(&client->dev, "RTC alarm!\n");
-
-               rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
-
-               /* Acknowledge and disable the alarm */
-               _isl12057_rtc_clear_alarm(&client->dev);
-               _isl12057_rtc_update_alarm(&client->dev, 0);
-
-               handled = IRQ_HANDLED;
-       }
-
-       return handled;
-}
-
-static const struct rtc_class_ops rtc_ops = {
-       .read_time = _isl12057_rtc_read_time,
-       .set_time = isl12057_rtc_set_time,
-       .read_alarm = isl12057_rtc_read_alarm,
-       .set_alarm = isl12057_rtc_set_alarm,
-       .alarm_irq_enable = isl12057_rtc_alarm_irq_enable,
-};
-
-static const struct regmap_config isl12057_rtc_regmap_config = {
-       .reg_bits = 8,
-       .val_bits = 8,
-};
-
-static int isl12057_probe(struct i2c_client *client,
-                         const struct i2c_device_id *id)
-{
-       struct device *dev = &client->dev;
-       struct isl12057_rtc_data *data;
-       struct regmap *regmap;
-       int ret;
-
-       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
-                                    I2C_FUNC_SMBUS_BYTE_DATA |
-                                    I2C_FUNC_SMBUS_I2C_BLOCK))
-               return -ENODEV;
-
-       regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config);
-       if (IS_ERR(regmap)) {
-               ret = PTR_ERR(regmap);
-               dev_err(dev, "%s: regmap allocation failed (%d)\n",
-                       __func__, ret);
-               return ret;
-       }
-
-       ret = isl12057_i2c_validate_chip(regmap);
-       if (ret)
-               return ret;
-
-       ret = isl12057_check_rtc_status(dev, regmap);
-       if (ret)
-               return ret;
-
-       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       mutex_init(&data->lock);
-       data->regmap = regmap;
-       dev_set_drvdata(dev, data);
-
-       if (client->irq > 0) {
-               ret = devm_request_threaded_irq(dev, client->irq, NULL,
-                                               isl12057_rtc_interrupt,
-                                               IRQF_SHARED|IRQF_ONESHOT,
-                                               DRV_NAME, client);
-               if (!ret)
-                       data->irq = client->irq;
-               else
-                       dev_err(dev, "%s: irq %d unavailable (%d)\n", __func__,
-                               client->irq, ret);
-       }
-
-       if (isl12057_can_wakeup_machine(dev))
-               device_init_wakeup(dev, true);
-
-       data->rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops,
-                                            THIS_MODULE);
-       ret = PTR_ERR_OR_ZERO(data->rtc);
-       if (ret) {
-               dev_err(dev, "%s: unable to register RTC device (%d)\n",
-                       __func__, ret);
-               goto err;
-       }
-
-       /* We cannot support UIE mode if we do not have an IRQ line */
-       if (!data->irq)
-               data->rtc->uie_unsupported = 1;
-
-err:
-       return ret;
-}
-
-static int isl12057_remove(struct i2c_client *client)
-{
-       if (isl12057_can_wakeup_machine(&client->dev))
-               device_init_wakeup(&client->dev, false);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int isl12057_rtc_suspend(struct device *dev)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-
-       if (rtc_data->irq && device_may_wakeup(dev))
-               return enable_irq_wake(rtc_data->irq);
-
-       return 0;
-}
-
-static int isl12057_rtc_resume(struct device *dev)
-{
-       struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
-
-       if (rtc_data->irq && device_may_wakeup(dev))
-               return disable_irq_wake(rtc_data->irq);
-
-       return 0;
-}
-#endif
-
-static SIMPLE_DEV_PM_OPS(isl12057_rtc_pm_ops, isl12057_rtc_suspend,
-                        isl12057_rtc_resume);
-
-#ifdef CONFIG_OF
-static const struct of_device_id isl12057_dt_match[] = {
-       { .compatible = "isl,isl12057" }, /* for backward compat., don't use */
-       { .compatible = "isil,isl12057" },
-       { },
-};
-MODULE_DEVICE_TABLE(of, isl12057_dt_match);
-#endif
-
-static const struct i2c_device_id isl12057_id[] = {
-       { "isl12057", 0 },
-       { }
-};
-MODULE_DEVICE_TABLE(i2c, isl12057_id);
-
-static struct i2c_driver isl12057_driver = {
-       .driver = {
-               .name = DRV_NAME,
-               .pm = &isl12057_rtc_pm_ops,
-               .of_match_table = of_match_ptr(isl12057_dt_match),
-       },
-       .probe    = isl12057_probe,
-       .remove   = isl12057_remove,
-       .id_table = isl12057_id,
-};
-module_i2c_driver(isl12057_driver);
-
-MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
-MODULE_DESCRIPTION("Intersil ISL12057 RTC driver");
-MODULE_LICENSE("GPL");
index b2bcfc0..5e14651 100644 (file)
@@ -174,7 +174,7 @@ static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
        return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable);
 }
 
-static struct rtc_class_ops jz4740_rtc_ops = {
+static const struct rtc_class_ops jz4740_rtc_ops = {
        .read_time      = jz4740_rtc_read_time,
        .set_mmss       = jz4740_rtc_set_mmss,
        .read_alarm     = jz4740_rtc_read_alarm,
index 025bb33..4021fd0 100644 (file)
@@ -151,7 +151,7 @@ static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
        return rtc_valid_tm(tim);
 }
 
-static struct rtc_class_ops mcp795_rtc_ops = {
+static const struct rtc_class_ops mcp795_rtc_ops = {
                .read_time = mcp795_read_time,
                .set_time = mcp795_set_time
 };
index 44f622c..1a61fa5 100644 (file)
@@ -301,7 +301,7 @@ exit:
        return ret;
 }
 
-static struct rtc_class_ops mtk_rtc_ops = {
+static const struct rtc_class_ops mtk_rtc_ops = {
        .read_time  = mtk_rtc_read_time,
        .set_time   = mtk_rtc_set_time,
        .read_alarm = mtk_rtc_read_alarm,
index 09fc1c1..b1b6b30 100644 (file)
@@ -214,7 +214,7 @@ static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        return 0;
 }
 
-static struct rtc_class_ops nuc900_rtc_ops = {
+static const struct rtc_class_ops nuc900_rtc_ops = {
        .read_time = nuc900_rtc_read_time,
        .set_time = nuc900_rtc_set_time,
        .read_alarm = nuc900_rtc_read_alarm,
index ec2e9c5..b04ea9b 100644 (file)
  * 2 of the License, or (at your option) any later version.
  */
 
-#include <linux/kernel.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/init.h>
-#include <linux/module.h>
+#include <linux/io.h>
 #include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/io.h>
-#include <linux/clk.h>
+#include <linux/rtc.h>
 
 /*
  * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock
 
 /* OMAP_RTC_PMIC bit fields: */
 #define OMAP_RTC_PMIC_POWER_EN_EN      BIT(16)
+#define OMAP_RTC_PMIC_EXT_WKUP_EN(x)   BIT(x)
+#define OMAP_RTC_PMIC_EXT_WKUP_POL(x)  BIT(4 + x)
 
 /* OMAP_RTC_KICKER values */
 #define        KICK0_VALUE                     0x83e70b13
@@ -141,6 +147,7 @@ struct omap_rtc {
        bool is_pmic_controller;
        bool has_ext_clk;
        const struct omap_rtc_device_type *type;
+       struct pinctrl_dev *pctldev;
 };
 
 static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg)
@@ -469,7 +476,7 @@ static void omap_rtc_power_off(void)
        mdelay(2500);
 }
 
-static struct rtc_class_ops omap_rtc_ops = {
+static const struct rtc_class_ops omap_rtc_ops = {
        .read_time      = omap_rtc_read_time,
        .set_time       = omap_rtc_set_time,
        .read_alarm     = omap_rtc_read_alarm,
@@ -525,6 +532,139 @@ static const struct of_device_id omap_rtc_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, omap_rtc_of_match);
 
+static const struct pinctrl_pin_desc rtc_pins_desc[] = {
+       PINCTRL_PIN(0, "ext_wakeup0"),
+       PINCTRL_PIN(1, "ext_wakeup1"),
+       PINCTRL_PIN(2, "ext_wakeup2"),
+       PINCTRL_PIN(3, "ext_wakeup3"),
+};
+
+static int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+       return 0;
+}
+
+static const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+                                       unsigned int group)
+{
+       return NULL;
+}
+
+static const struct pinctrl_ops rtc_pinctrl_ops = {
+       .get_groups_count = rtc_pinctrl_get_groups_count,
+       .get_group_name = rtc_pinctrl_get_group_name,
+       .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
+       .dt_free_map = pinconf_generic_dt_free_map,
+};
+
+enum rtc_pin_config_param {
+       PIN_CONFIG_ACTIVE_HIGH = PIN_CONFIG_END + 1,
+};
+
+static const struct pinconf_generic_params rtc_params[] = {
+       {"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0},
+};
+
+#ifdef CONFIG_DEBUG_FS
+static const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = {
+       PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false),
+};
+#endif
+
+static int rtc_pinconf_get(struct pinctrl_dev *pctldev,
+                       unsigned int pin, unsigned long *config)
+{
+       struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
+       unsigned int param = pinconf_to_config_param(*config);
+       u32 val;
+       u16 arg = 0;
+
+       rtc->type->unlock(rtc);
+       val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
+       rtc->type->lock(rtc);
+
+       switch (param) {
+       case PIN_CONFIG_INPUT_ENABLE:
+               if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin)))
+                       return -EINVAL;
+               break;
+       case PIN_CONFIG_ACTIVE_HIGH:
+               if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin))
+                       return -EINVAL;
+               break;
+       default:
+               return -ENOTSUPP;
+       };
+
+       *config = pinconf_to_config_packed(param, arg);
+
+       return 0;
+}
+
+static int rtc_pinconf_set(struct pinctrl_dev *pctldev,
+                       unsigned int pin, unsigned long *configs,
+                       unsigned int num_configs)
+{
+       struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev);
+       u32 val;
+       unsigned int param;
+       u16 param_val;
+       int i;
+
+       rtc->type->unlock(rtc);
+       val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
+       rtc->type->lock(rtc);
+
+       /* active low by default */
+       val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
+
+       for (i = 0; i < num_configs; i++) {
+               param = pinconf_to_config_param(configs[i]);
+               param_val = pinconf_to_config_argument(configs[i]);
+
+               switch (param) {
+               case PIN_CONFIG_INPUT_ENABLE:
+                       if (param_val)
+                               val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
+                       else
+                               val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin);
+                       break;
+               case PIN_CONFIG_ACTIVE_HIGH:
+                       val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin);
+                       break;
+               default:
+                       dev_err(&rtc->rtc->dev, "Property %u not supported\n",
+                               param);
+                       return -ENOTSUPP;
+               }
+       }
+
+       rtc->type->unlock(rtc);
+       rtc_writel(rtc, OMAP_RTC_PMIC_REG, val);
+       rtc->type->lock(rtc);
+
+       return 0;
+}
+
+static const struct pinconf_ops rtc_pinconf_ops = {
+       .is_generic = true,
+       .pin_config_get = rtc_pinconf_get,
+       .pin_config_set = rtc_pinconf_set,
+};
+
+static struct pinctrl_desc rtc_pinctrl_desc = {
+       .pins = rtc_pins_desc,
+       .npins = ARRAY_SIZE(rtc_pins_desc),
+       .pctlops = &rtc_pinctrl_ops,
+       .confops = &rtc_pinconf_ops,
+       .custom_params = rtc_params,
+       .num_custom_params = ARRAY_SIZE(rtc_params),
+#ifdef CONFIG_DEBUG_FS
+       .custom_conf_items = rtc_conf_items,
+#endif
+       .owner = THIS_MODULE,
+};
+
 static int omap_rtc_probe(struct platform_device *pdev)
 {
        struct omap_rtc *rtc;
@@ -681,6 +821,15 @@ static int omap_rtc_probe(struct platform_device *pdev)
                }
        }
 
+       /* Support ext_wakeup pinconf */
+       rtc_pinctrl_desc.name = dev_name(&pdev->dev);
+
+       rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc);
+       if (IS_ERR(rtc->pctldev)) {
+               dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+               return PTR_ERR(rtc->pctldev);
+       }
+
        return 0;
 
 err:
@@ -724,6 +873,9 @@ static int __exit omap_rtc_remove(struct platform_device *pdev)
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
+       /* Remove ext_wakeup pinconf */
+       pinctrl_unregister(rtc->pctldev);
+
        return 0;
 }
 
index 6080e0e..4bcfb88 100644 (file)
@@ -225,7 +225,7 @@ static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
        return IRQ_HANDLED;
 }
 
-static struct rtc_class_ops palmas_rtc_ops = {
+static const struct rtc_class_ops palmas_rtc_ops = {
        .read_time      = palmas_rtc_read_time,
        .set_time       = palmas_rtc_set_time,
        .read_alarm     = palmas_rtc_read_alarm,
index b4478cc..8895f77 100644 (file)
@@ -182,7 +182,8 @@ static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
 }
 
 static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
-                            const char *buffer, size_t count) {
+                            const char *buffer, size_t count)
+{
        struct pcf2123_sysfs_reg *r;
        unsigned long reg;
        unsigned long val;
@@ -199,7 +200,7 @@ static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       pcf2123_write_reg(dev, reg, val);
+       ret = pcf2123_write_reg(dev, reg, val);
        if (ret < 0)
                return -EIO;
        return count;
index e6b6911..00c31c9 100644 (file)
@@ -232,7 +232,7 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
        return ret;
 }
 
-static struct rtc_class_ops pcf50633_rtc_ops = {
+static const struct rtc_class_ops pcf50633_rtc_ops = {
        .read_time              = pcf50633_rtc_read_time,
        .set_time               = pcf50633_rtc_set_time,
        .read_alarm             = pcf50633_rtc_read_alarm,
index 64e1e45..5cfb6df 100644 (file)
@@ -400,7 +400,6 @@ static struct platform_driver pic32_rtc_driver = {
        .remove         = pic32_rtc_remove,
        .driver         = {
                .name   = "pic32-rtc",
-               .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(pic32_rtc_dt_ids),
        },
 };
index 9a2f6a9..f9277e5 100644 (file)
 #define RV8803_CTRL_TIE                        BIT(4)
 #define RV8803_CTRL_UIE                        BIT(5)
 
+#define RX8900_BACKUP_CTRL             0x18
+#define RX8900_FLAG_SWOFF              BIT(2)
+#define RX8900_FLAG_VDETOFF            BIT(3)
+
+enum rv8803_type {
+       rv_8803,
+       rx_8900
+};
+
 struct rv8803_data {
        struct i2c_client *client;
        struct rtc_device *rtc;
        struct mutex flags_lock;
        u8 ctrl;
+       enum rv8803_type type;
 };
 
 static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
@@ -497,6 +507,35 @@ static struct rtc_class_ops rv8803_rtc_ops = {
        .ioctl = rv8803_ioctl,
 };
 
+static int rx8900_trickle_charger_init(struct rv8803_data *rv8803)
+{
+       struct i2c_client *client = rv8803->client;
+       struct device_node *node = client->dev.of_node;
+       int err;
+       u8 flags;
+
+       if (!node)
+               return 0;
+
+       if (rv8803->type != rx_8900)
+               return 0;
+
+       err = i2c_smbus_read_byte_data(rv8803->client, RX8900_BACKUP_CTRL);
+       if (err < 0)
+               return err;
+
+       flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err;
+
+       if (of_property_read_bool(node, "epson,vdet-disable"))
+               flags |= RX8900_FLAG_VDETOFF;
+
+       if (of_property_read_bool(node, "trickle-diode-disable"))
+               flags |= RX8900_FLAG_SWOFF;
+
+       return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL,
+                                        flags);
+}
+
 static int rv8803_probe(struct i2c_client *client,
                        const struct i2c_device_id *id)
 {
@@ -517,6 +556,7 @@ static int rv8803_probe(struct i2c_client *client,
 
        mutex_init(&rv8803->flags_lock);
        rv8803->client = client;
+       rv8803->type = id->driver_data;
        i2c_set_clientdata(client, rv8803);
 
        flags = rv8803_read_reg(client, RV8803_FLAG);
@@ -558,6 +598,12 @@ static int rv8803_probe(struct i2c_client *client,
        if (err)
                return err;
 
+       err = rx8900_trickle_charger_init(rv8803);
+       if (err) {
+               dev_err(&client->dev, "failed to init charger\n");
+               return err;
+       }
+
        err = device_create_bin_file(&client->dev, &rv8803_nvram_attr);
        if (err)
                return err;
@@ -575,8 +621,8 @@ static int rv8803_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id rv8803_id[] = {
-       { "rv8803", 0 },
-       { "rx8900", 0 },
+       { "rv8803", rv_8803 },
+       { "rx8900", rx_8900 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rv8803_id);
index bbad00b..7c9c08e 100644 (file)
@@ -317,7 +317,7 @@ static int rx6110_init(struct rx6110_data *rx6110)
        return ret;
 }
 
-static struct rtc_class_ops rx6110_rtc_ops = {
+static const struct rtc_class_ops rx6110_rtc_ops = {
        .read_time = rx6110_get_time,
        .set_time = rx6110_set_time,
 };
@@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(spi, rx6110_id);
 static struct spi_driver rx6110_driver = {
        .driver = {
                .name = RX6110_DRIVER_NAME,
-               .owner = THIS_MODULE,
        },
        .probe          = rx6110_probe,
        .remove         = rx6110_remove,
index 2b85cc7..91857d8 100644 (file)
@@ -403,7 +403,7 @@ static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return 0;
 }
 
-static struct rtc_class_ops rx8025_rtc_ops = {
+static const struct rtc_class_ops rx8025_rtc_ops = {
        .read_time = rx8025_get_time,
        .set_time = rx8025_set_time,
        .read_alarm = rx8025_read_alarm,
index f05ef85..e377f42 100644 (file)
@@ -343,7 +343,7 @@ static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled)
        return ret;
 }
 
-static struct rtc_class_ops spear_rtc_ops = {
+static const struct rtc_class_ops spear_rtc_ops = {
        .read_time = spear_rtc_read_time,
        .set_time = spear_rtc_set_time,
        .read_alarm = spear_rtc_read_alarm,
index e6aaaa5..d578e40 100644 (file)
@@ -231,7 +231,7 @@ static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
        return 0;
 }
 
-static struct rtc_class_ops stmp3xxx_rtc_ops = {
+static const struct rtc_class_ops stmp3xxx_rtc_ops = {
        .alarm_irq_enable =
                          stmp3xxx_alarm_irq_enable,
        .read_time      = stmp3xxx_rtc_gettime,
index 63b9fb1..1218d5d 100644 (file)
@@ -160,7 +160,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
        unsigned long push = 0;
        struct rtc_wkalrm alm;
        struct rtc_device *rtc = to_rtc_device(dev);
-       char *buf_ptr;
+       const char *buf_ptr;
        int adjust = 0;
 
        /* Only request alarms that trigger in the future.  Disable them
@@ -171,7 +171,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
                return retval;
        rtc_tm_to_time(&alm.time, &now);
 
-       buf_ptr = (char *)buf;
+       buf_ptr = buf;
        if (*buf_ptr == '+') {
                buf_ptr++;
                if (*buf_ptr == '=') {
index 15ac597..3853ba9 100644 (file)
@@ -291,7 +291,7 @@ static irqreturn_t tegra_rtc_irq_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static struct rtc_class_ops tegra_rtc_ops = {
+static const struct rtc_class_ops tegra_rtc_ops = {
        .read_time      = tegra_rtc_read_time,
        .set_time       = tegra_rtc_set_time,
        .read_alarm     = tegra_rtc_read_alarm,
index 2dc787d..176720b 100644 (file)
@@ -462,7 +462,7 @@ out:
        return ret;
 }
 
-static struct rtc_class_ops twl_rtc_ops = {
+static const struct rtc_class_ops twl_rtc_ops = {
        .read_time      = twl_rtc_read_time,
        .set_time       = twl_rtc_set_time,
        .read_alarm     = twl_rtc_read_alarm,
index 6a6906f..68138a6 100644 (file)
@@ -61,7 +61,7 @@ MODULE_PARM_DESC(be_max_phys_size,
                "memory that can be allocated. Range is 16 - 128");
 
 #define beiscsi_disp_param(_name)\
-ssize_t        \
+static ssize_t \
 beiscsi_##_name##_disp(struct device *dev,\
                        struct device_attribute *attrib, char *buf)     \
 {      \
@@ -74,7 +74,7 @@ beiscsi_##_name##_disp(struct device *dev,\
 }
 
 #define beiscsi_change_param(_name, _minval, _maxval, _defaval)\
-int \
+static int \
 beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
 {\
        if (val >= _minval && val <= _maxval) {\
@@ -93,7 +93,7 @@ beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
 }
 
 #define beiscsi_store_param(_name)  \
-ssize_t \
+static ssize_t \
 beiscsi_##_name##_store(struct device *dev,\
                         struct device_attribute *attr, const char *buf,\
                         size_t count) \
@@ -112,7 +112,7 @@ beiscsi_##_name##_store(struct device *dev,\
 }
 
 #define beiscsi_init_param(_name, _minval, _maxval, _defval) \
-int \
+static int \
 beiscsi_##_name##_init(struct beiscsi_hba *phba, uint32_t val) \
 { \
        if (val >= _minval && val <= _maxval) {\
@@ -4584,7 +4584,7 @@ free_hndls:
        io_task->cmd_bhs = NULL;
        return -ENOMEM;
 }
-int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
+static int beiscsi_iotask_v2(struct iscsi_task *task, struct scatterlist *sg,
                       unsigned int num_sg, unsigned int xferlen,
                       unsigned int writedir)
 {
@@ -4973,7 +4973,7 @@ static int beiscsi_bsg_request(struct bsg_job *job)
        return rc;
 }
 
-void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
+static void beiscsi_hba_attrs_init(struct beiscsi_hba *phba)
 {
        /* Set the logging parameter */
        beiscsi_log_enable_init(phba, beiscsi_log_enable);
index 7c0d7af..0039beb 100644 (file)
@@ -685,6 +685,11 @@ static int push_tx_frames(struct cxgbi_sock *csk, int req_completion)
                                        req_completion);
                        csk->snd_nxt += len;
                        cxgbi_skcb_clear_flag(skb, SKCBF_TX_NEED_HDR);
+               } else if (cxgbi_skcb_test_flag(skb, SKCBF_TX_FLAG_COMPL) &&
+                          (csk->wr_una_cred >= (csk->wr_max_cred / 2))) {
+                       struct cpl_close_con_req *req =
+                               (struct cpl_close_con_req *)skb->data;
+                       req->wr.wr_hi |= htonl(FW_WR_COMPL_F);
                }
                total_size += skb->truesize;
                t4_set_arp_err_handler(skb, csk, arp_failure_skb_discard);
index 516bd6c..cbf0103 100644 (file)
 #include "NCR5380.h"
 #include <linux/init.h>
 #include <linux/ioport.h>
-#include <linux/isapnp.h>
+#include <linux/isa.h>
+#include <linux/pnp.h>
 #include <linux/interrupt.h>
 
+#define MAX_CARDS 8
+
+/* old-style parameters for compatibility */
 static int ncr_irq;
-static int ncr_dma;
 static int ncr_addr;
 static int ncr_5380;
 static int ncr_53c400;
 static int ncr_53c400a;
 static int dtc_3181e;
 static int hp_c2502;
+module_param(ncr_irq, int, 0);
+module_param(ncr_addr, int, 0);
+module_param(ncr_5380, int, 0);
+module_param(ncr_53c400, int, 0);
+module_param(ncr_53c400a, int, 0);
+module_param(dtc_3181e, int, 0);
+module_param(hp_c2502, int, 0);
 
-static struct override {
-       NCR5380_map_type NCR5380_map_name;
-       int irq;
-       int dma;
-       int board;              /* Use NCR53c400, Ricoh, etc. extensions ? */
-} overrides
-#ifdef GENERIC_NCR5380_OVERRIDE
-[] __initdata = GENERIC_NCR5380_OVERRIDE;
-#else
-[1] __initdata = { { 0,},};
-#endif
-
-#define NO_OVERRIDES ARRAY_SIZE(overrides)
-
-#ifndef MODULE
-
-/**
- *     internal_setup          -       handle lilo command string override
- *     @board: BOARD_* identifier for the board
- *     @str: unused
- *     @ints: numeric parameters
- *
- *     Do LILO command line initialization of the overrides array. Display
- *     errors when needed
- *
- *     Locks: none
- */
-
-static void __init internal_setup(int board, char *str, int *ints)
-{
-       static int commandline_current;
-       switch (board) {
-       case BOARD_NCR5380:
-               if (ints[0] != 2 && ints[0] != 3) {
-                       printk(KERN_ERR "generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
-                       return;
-               }
-               break;
-       case BOARD_NCR53C400:
-               if (ints[0] != 2) {
-                       printk(KERN_ERR "generic_NCR53C400_setup : usage ncr53c400=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       case BOARD_NCR53C400A:
-               if (ints[0] != 2) {
-                       printk(KERN_ERR "generic_NCR53C400A_setup : usage ncr53c400a=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       case BOARD_DTC3181E:
-               if (ints[0] != 2) {
-                       printk("generic_DTC3181E_setup : usage dtc3181e=" STRVAL(NCR5380_map_name) ",irq\n");
-                       return;
-               }
-               break;
-       }
-
-       if (commandline_current < NO_OVERRIDES) {
-               overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type) ints[1];
-               overrides[commandline_current].irq = ints[2];
-               if (ints[0] == 3)
-                       overrides[commandline_current].dma = ints[3];
-               else
-                       overrides[commandline_current].dma = DMA_NONE;
-               overrides[commandline_current].board = board;
-               ++commandline_current;
-       }
-}
-
-
-/**
- *     do_NCR53C80_setup               -       set up entry point
- *     @str: unused
- *
- *     Setup function invoked at boot to parse the ncr5380= command
- *     line.
- */
-
-static int __init do_NCR5380_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR5380, str, ints);
-       return 1;
-}
-
-/**
- *     do_NCR53C400_setup              -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the ncr53c400= command
- *     line.
- */
-
-static int __init do_NCR53C400_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR53C400, str, ints);
-       return 1;
-}
-
-/**
- *     do_NCR53C400A_setup     -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the ncr53c400a= command
- *     line.
- */
-
-static int __init do_NCR53C400A_setup(char *str)
-{
-       int ints[10];
-
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_NCR53C400A, str, ints);
-       return 1;
-}
-
-/**
- *     do_DTC3181E_setup       -       set up entry point
- *     @str: unused
- *     @ints: integer parameters from kernel setup code
- *
- *     Setup function invoked at boot to parse the dtc3181e= command
- *     line.
- */
+static int irq[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(irq, "IRQ number(s)");
 
-static int __init do_DTC3181E_setup(char *str)
-{
-       int ints[10];
+static int base[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+module_param_array(base, int, NULL, 0);
+MODULE_PARM_DESC(base, "base address(es)");
 
-       get_options(str, ARRAY_SIZE(ints), ints);
-       internal_setup(BOARD_DTC3181E, str, ints);
-       return 1;
-}
+static int card[] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+module_param_array(card, int, NULL, 0);
+MODULE_PARM_DESC(card, "card type (0=NCR5380, 1=NCR53C400, 2=NCR53C400A, 3=DTC3181E, 4=HP C2502)");
 
-#endif
+MODULE_LICENSE("GPL");
 
 #ifndef SCSI_G_NCR5380_MEM
 /*
@@ -210,21 +90,9 @@ static void magic_configure(int idx, u8 irq, u8 magic[])
 }
 #endif
 
-/**
- *     generic_NCR5380_detect  -       look for NCR5380 controllers
- *     @tpnt: the scsi template
- *
- *     Scan for the present of NCR5380, NCR53C400, NCR53C400A, DTC3181E
- *     and DTC436(ISAPnP) controllers. If overrides have been set we use
- *     them.
- *
- *     Locks: none
- */
-
-static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
+static int generic_NCR5380_init_one(struct scsi_host_template *tpnt,
+                       struct device *pdev, int base, int irq, int board)
 {
-       static int current_override;
-       int count;
        unsigned int *ports;
        u8 *magic = NULL;
 #ifndef SCSI_G_NCR5380_MEM
@@ -232,272 +100,222 @@ static int __init generic_NCR5380_detect(struct scsi_host_template *tpnt)
        int port_idx = -1;
        unsigned long region_size;
 #endif
-       static unsigned int __initdata ncr_53c400a_ports[] = {
+       static unsigned int ncr_53c400a_ports[] = {
                0x280, 0x290, 0x300, 0x310, 0x330, 0x340, 0x348, 0x350, 0
        };
-       static unsigned int __initdata dtc_3181e_ports[] = {
+       static unsigned int dtc_3181e_ports[] = {
                0x220, 0x240, 0x280, 0x2a0, 0x2c0, 0x300, 0x320, 0x340, 0
        };
-       static u8 ncr_53c400a_magic[] __initdata = {    /* 53C400A & DTC436 */
+       static u8 ncr_53c400a_magic[] = {       /* 53C400A & DTC436 */
                0x59, 0xb9, 0xc5, 0xae, 0xa6
        };
-       static u8 hp_c2502_magic[] __initdata = {       /* HP C2502 */
+       static u8 hp_c2502_magic[] = {  /* HP C2502 */
                0x0f, 0x22, 0xf0, 0x20, 0x80
        };
-       int flags;
+       int flags, ret;
        struct Scsi_Host *instance;
        struct NCR5380_hostdata *hostdata;
 #ifdef SCSI_G_NCR5380_MEM
-       unsigned long base;
        void __iomem *iomem;
        resource_size_t iomem_size;
 #endif
 
-       if (ncr_irq)
-               overrides[0].irq = ncr_irq;
-       if (ncr_dma)
-               overrides[0].dma = ncr_dma;
-       if (ncr_addr)
-               overrides[0].NCR5380_map_name = (NCR5380_map_type) ncr_addr;
-       if (ncr_5380)
-               overrides[0].board = BOARD_NCR5380;
-       else if (ncr_53c400)
-               overrides[0].board = BOARD_NCR53C400;
-       else if (ncr_53c400a)
-               overrides[0].board = BOARD_NCR53C400A;
-       else if (dtc_3181e)
-               overrides[0].board = BOARD_DTC3181E;
-       else if (hp_c2502)
-               overrides[0].board = BOARD_HP_C2502;
-#ifndef SCSI_G_NCR5380_MEM
-       if (!current_override && isapnp_present()) {
-               struct pnp_dev *dev = NULL;
-               count = 0;
-               while ((dev = pnp_find_dev(NULL, ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e), dev))) {
-                       if (count >= NO_OVERRIDES)
-                               break;
-                       if (pnp_device_attach(dev) < 0)
-                               continue;
-                       if (pnp_activate_dev(dev) < 0) {
-                               printk(KERN_ERR "dtc436e probe: activate failed\n");
-                               pnp_device_detach(dev);
-                               continue;
-                       }
-                       if (!pnp_port_valid(dev, 0)) {
-                               printk(KERN_ERR "dtc436e probe: no valid port\n");
-                               pnp_device_detach(dev);
-                               continue;
-                       }
-                       if (pnp_irq_valid(dev, 0))
-                               overrides[count].irq = pnp_irq(dev, 0);
-                       else
-                               overrides[count].irq = NO_IRQ;
-                       if (pnp_dma_valid(dev, 0))
-                               overrides[count].dma = pnp_dma(dev, 0);
-                       else
-                               overrides[count].dma = DMA_NONE;
-                       overrides[count].NCR5380_map_name = (NCR5380_map_type) pnp_port_start(dev, 0);
-                       overrides[count].board = BOARD_DTC3181E;
-                       count++;
-               }
+       ports = NULL;
+       flags = 0;
+       switch (board) {
+       case BOARD_NCR5380:
+               flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
+               break;
+       case BOARD_NCR53C400A:
+               ports = ncr_53c400a_ports;
+               magic = ncr_53c400a_magic;
+               break;
+       case BOARD_HP_C2502:
+               ports = ncr_53c400a_ports;
+               magic = hp_c2502_magic;
+               break;
+       case BOARD_DTC3181E:
+               ports = dtc_3181e_ports;
+               magic = ncr_53c400a_magic;
+               break;
        }
-#endif
-
-       for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
-               if (!(overrides[current_override].NCR5380_map_name))
-                       continue;
-
-               ports = NULL;
-               flags = 0;
-               switch (overrides[current_override].board) {
-               case BOARD_NCR5380:
-                       flags = FLAG_NO_PSEUDO_DMA | FLAG_DMA_FIXUP;
-                       break;
-               case BOARD_NCR53C400A:
-                       ports = ncr_53c400a_ports;
-                       magic = ncr_53c400a_magic;
-                       break;
-               case BOARD_HP_C2502:
-                       ports = ncr_53c400a_ports;
-                       magic = hp_c2502_magic;
-                       break;
-               case BOARD_DTC3181E:
-                       ports = dtc_3181e_ports;
-                       magic = ncr_53c400a_magic;
-                       break;
-               }
 
 #ifndef SCSI_G_NCR5380_MEM
-               if (ports && magic) {
-                       /* wakeup sequence for the NCR53C400A and DTC3181E */
-
-                       /* Disable the adapter and look for a free io port */
-                       magic_configure(-1, 0, magic);
-
-                       region_size = 16;
-
-                       if (overrides[current_override].NCR5380_map_name != PORT_AUTO)
-                               for (i = 0; ports[i]; i++) {
-                                       if (!request_region(ports[i], region_size, "ncr53c80"))
-                                               continue;
-                                       if (overrides[current_override].NCR5380_map_name == ports[i])
-                                               break;
-                                       release_region(ports[i], region_size);
-                       } else
-                               for (i = 0; ports[i]; i++) {
-                                       if (!request_region(ports[i], region_size, "ncr53c80"))
-                                               continue;
-                                       if (inb(ports[i]) == 0xff)
-                                               break;
-                                       release_region(ports[i], region_size);
+       if (ports && magic) {
+               /* wakeup sequence for the NCR53C400A and DTC3181E */
+
+               /* Disable the adapter and look for a free io port */
+               magic_configure(-1, 0, magic);
+
+               region_size = 16;
+               if (base)
+                       for (i = 0; ports[i]; i++) {
+                               if (base == ports[i]) { /* index found */
+                                       if (!request_region(ports[i],
+                                                           region_size,
+                                                           "ncr53c80"))
+                                               return -EBUSY;
+                                       break;
                                }
-                       if (ports[i]) {
-                               /* At this point we have our region reserved */
-                               magic_configure(i, 0, magic); /* no IRQ yet */
-                               outb(0xc0, ports[i] + 9);
-                               if (inb(ports[i] + 9) != 0x80)
-                                       continue;
-                               overrides[current_override].NCR5380_map_name = ports[i];
-                               port_idx = i;
-                       } else
-                               continue;
-               }
+                       }
                else
-               {
-                       /* Not a 53C400A style setup - just grab */
-                       region_size = 8;
-                       if (!request_region(overrides[current_override].NCR5380_map_name,
-                                           region_size, "ncr5380"))
-                               continue;
-               }
+                       for (i = 0; ports[i]; i++) {
+                               if (!request_region(ports[i], region_size,
+                                                   "ncr53c80"))
+                                       continue;
+                               if (inb(ports[i]) == 0xff)
+                                       break;
+                               release_region(ports[i], region_size);
+                       }
+               if (ports[i]) {
+                       /* At this point we have our region reserved */
+                       magic_configure(i, 0, magic); /* no IRQ yet */
+                       outb(0xc0, ports[i] + 9);
+                       if (inb(ports[i] + 9) != 0x80) {
+                               ret = -ENODEV;
+                               goto out_release;
+                       }
+                       base = ports[i];
+                       port_idx = i;
+               } else
+                       return -EINVAL;
+       }
+       else
+       {
+               /* NCR5380 - no configuration, just grab */
+               region_size = 8;
+               if (!base || !request_region(base, region_size, "ncr5380"))
+                       return -EBUSY;
+       }
 #else
-               base = overrides[current_override].NCR5380_map_name;
-               iomem_size = NCR53C400_region_size;
-               if (!request_mem_region(base, iomem_size, "ncr5380"))
-                       continue;
-               iomem = ioremap(base, iomem_size);
-               if (!iomem) {
-                       release_mem_region(base, iomem_size);
-                       continue;
-               }
+       iomem_size = NCR53C400_region_size;
+       if (!request_mem_region(base, iomem_size, "ncr5380"))
+               return -EBUSY;
+       iomem = ioremap(base, iomem_size);
+       if (!iomem) {
+               release_mem_region(base, iomem_size);
+               return -ENOMEM;
+       }
 #endif
-               instance = scsi_register(tpnt, sizeof(struct NCR5380_hostdata));
-               if (instance == NULL)
-                       goto out_release;
-               hostdata = shost_priv(instance);
+       instance = scsi_host_alloc(tpnt, sizeof(struct NCR5380_hostdata));
+       if (instance == NULL) {
+               ret = -ENOMEM;
+               goto out_release;
+       }
+       hostdata = shost_priv(instance);
 
 #ifndef SCSI_G_NCR5380_MEM
-               instance->io_port = overrides[current_override].NCR5380_map_name;
-               instance->n_io_port = region_size;
-               hostdata->io_width = 1; /* 8-bit PDMA by default */
-
-               /*
-                * On NCR53C400 boards, NCR5380 registers are mapped 8 past
-                * the base address.
-                */
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-                       instance->io_port += 8;
-                       hostdata->c400_ctl_status = 0;
-                       hostdata->c400_blk_cnt = 1;
-                       hostdata->c400_host_buf = 4;
-                       break;
-               case BOARD_DTC3181E:
-                       hostdata->io_width = 2; /* 16-bit PDMA */
-                       /* fall through */
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       hostdata->c400_ctl_status = 9;
-                       hostdata->c400_blk_cnt = 10;
-                       hostdata->c400_host_buf = 8;
-                       break;
-               }
+       instance->io_port = base;
+       instance->n_io_port = region_size;
+       hostdata->io_width = 1; /* 8-bit PDMA by default */
+
+       /*
+        * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+        * the base address.
+        */
+       switch (board) {
+       case BOARD_NCR53C400:
+               instance->io_port += 8;
+               hostdata->c400_ctl_status = 0;
+               hostdata->c400_blk_cnt = 1;
+               hostdata->c400_host_buf = 4;
+               break;
+       case BOARD_DTC3181E:
+               hostdata->io_width = 2; /* 16-bit PDMA */
+               /* fall through */
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               hostdata->c400_ctl_status = 9;
+               hostdata->c400_blk_cnt = 10;
+               hostdata->c400_host_buf = 8;
+               break;
+       }
 #else
-               instance->base = overrides[current_override].NCR5380_map_name;
-               hostdata->iomem = iomem;
-               hostdata->iomem_size = iomem_size;
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-                       hostdata->c400_ctl_status = 0x100;
-                       hostdata->c400_blk_cnt = 0x101;
-                       hostdata->c400_host_buf = 0x104;
-                       break;
-               case BOARD_DTC3181E:
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
-                       goto out_unregister;
-               }
+       instance->base = base;
+       hostdata->iomem = iomem;
+       hostdata->iomem_size = iomem_size;
+       switch (board) {
+       case BOARD_NCR53C400:
+               hostdata->c400_ctl_status = 0x100;
+               hostdata->c400_blk_cnt = 0x101;
+               hostdata->c400_host_buf = 0x104;
+               break;
+       case BOARD_DTC3181E:
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               pr_err(DRV_MODULE_NAME ": unknown register offsets\n");
+               ret = -EINVAL;
+               goto out_unregister;
+       }
 #endif
 
-               if (NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP))
-                       goto out_unregister;
+       ret = NCR5380_init(instance, flags | FLAG_LATE_DMA_SETUP);
+       if (ret)
+               goto out_unregister;
 
-               switch (overrides[current_override].board) {
-               case BOARD_NCR53C400:
-               case BOARD_DTC3181E:
-               case BOARD_NCR53C400A:
-               case BOARD_HP_C2502:
-                       NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
-               }
+       switch (board) {
+       case BOARD_NCR53C400:
+       case BOARD_DTC3181E:
+       case BOARD_NCR53C400A:
+       case BOARD_HP_C2502:
+               NCR5380_write(hostdata->c400_ctl_status, CSR_BASE);
+       }
 
-               NCR5380_maybe_reset_bus(instance);
+       NCR5380_maybe_reset_bus(instance);
 
-               if (overrides[current_override].irq != IRQ_AUTO)
-                       instance->irq = overrides[current_override].irq;
-               else
-                       instance->irq = NCR5380_probe_irq(instance, 0xffff);
+       if (irq != IRQ_AUTO)
+               instance->irq = irq;
+       else
+               instance->irq = NCR5380_probe_irq(instance, 0xffff);
 
-               /* Compatibility with documented NCR5380 kernel parameters */
-               if (instance->irq == 255)
-                       instance->irq = NO_IRQ;
+       /* Compatibility with documented NCR5380 kernel parameters */
+       if (instance->irq == 255)
+               instance->irq = NO_IRQ;
 
-               if (instance->irq != NO_IRQ) {
+       if (instance->irq != NO_IRQ) {
 #ifndef SCSI_G_NCR5380_MEM
-                       /* set IRQ for HP C2502 */
-                       if (overrides[current_override].board == BOARD_HP_C2502)
-                               magic_configure(port_idx, instance->irq, magic);
+               /* set IRQ for HP C2502 */
+               if (board == BOARD_HP_C2502)
+                       magic_configure(port_idx, instance->irq, magic);
 #endif
-                       if (request_irq(instance->irq, generic_NCR5380_intr,
-                                       0, "NCR5380", instance)) {
-                               printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
-                               instance->irq = NO_IRQ;
-                       }
-               }
-
-               if (instance->irq == NO_IRQ) {
-                       printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
-                       printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+               if (request_irq(instance->irq, generic_NCR5380_intr,
+                               0, "NCR5380", instance)) {
+                       printk(KERN_WARNING "scsi%d : IRQ%d not free, interrupts disabled\n", instance->host_no, instance->irq);
+                       instance->irq = NO_IRQ;
                }
+       }
 
-               ++current_override;
-               ++count;
+       if (instance->irq == NO_IRQ) {
+               printk(KERN_INFO "scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+               printk(KERN_INFO "scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
        }
-       return count;
 
+       ret = scsi_add_host(instance, pdev);
+       if (ret)
+               goto out_free_irq;
+       scsi_scan_host(instance);
+       dev_set_drvdata(pdev, instance);
+       return 0;
+
+out_free_irq:
+       if (instance->irq != NO_IRQ)
+               free_irq(instance->irq, instance);
+       NCR5380_exit(instance);
 out_unregister:
-       scsi_unregister(instance);
+       scsi_host_put(instance);
 out_release:
 #ifndef SCSI_G_NCR5380_MEM
-       release_region(overrides[current_override].NCR5380_map_name, region_size);
+       release_region(base, region_size);
 #else
        iounmap(iomem);
        release_mem_region(base, iomem_size);
 #endif
-       return count;
+       return ret;
 }
 
-/**
- *     generic_NCR5380_release_resources       -       free resources
- *     @instance: host adapter to clean up 
- *
- *     Free the generic interface resources from this adapter.
- *
- *     Locks: none
- */
-static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
+static void generic_NCR5380_release_resources(struct Scsi_Host *instance)
 {
+       scsi_remove_host(instance);
        if (instance->irq != NO_IRQ)
                free_irq(instance->irq, instance);
        NCR5380_exit(instance);
@@ -511,7 +329,7 @@ static int generic_NCR5380_release_resources(struct Scsi_Host *instance)
                release_mem_region(instance->base, hostdata->iomem_size);
        }
 #endif
-       return 0;
+       scsi_host_put(instance);
 }
 
 /**
@@ -701,10 +519,9 @@ static int generic_NCR5380_dma_xfer_len(struct Scsi_Host *instance,
 #include "NCR5380.c"
 
 static struct scsi_host_template driver_template = {
+       .module                 = THIS_MODULE,
        .proc_name              = DRV_MODULE_NAME,
        .name                   = "Generic NCR5380/NCR53C400 SCSI",
-       .detect                 = generic_NCR5380_detect,
-       .release                = generic_NCR5380_release_resources,
        .info                   = generic_NCR5380_info,
        .queuecommand           = generic_NCR5380_queue_command,
        .eh_abort_handler       = generic_NCR5380_abort,
@@ -718,31 +535,115 @@ static struct scsi_host_template driver_template = {
        .max_sectors            = 128,
 };
 
-#include "scsi_module.c"
 
-module_param(ncr_irq, int, 0);
-module_param(ncr_dma, int, 0);
-module_param(ncr_addr, int, 0);
-module_param(ncr_5380, int, 0);
-module_param(ncr_53c400, int, 0);
-module_param(ncr_53c400a, int, 0);
-module_param(dtc_3181e, int, 0);
-module_param(hp_c2502, int, 0);
-MODULE_LICENSE("GPL");
+static int generic_NCR5380_isa_match(struct device *pdev, unsigned int ndev)
+{
+       int ret = generic_NCR5380_init_one(&driver_template, pdev, base[ndev],
+                                         irq[ndev], card[ndev]);
+       if (ret) {
+               if (base[ndev])
+                       printk(KERN_WARNING "Card not found at address 0x%03x\n",
+                              base[ndev]);
+               return 0;
+       }
 
-#if !defined(SCSI_G_NCR5380_MEM) && defined(MODULE)
-static struct isapnp_device_id id_table[] = {
-       {
-        ISAPNP_ANY_ID, ISAPNP_ANY_ID,
-        ISAPNP_VENDOR('D', 'T', 'C'), ISAPNP_FUNCTION(0x436e),
-        0},
-       {0}
+       return 1;
+}
+
+static int generic_NCR5380_isa_remove(struct device *pdev,
+                                  unsigned int ndev)
+{
+       generic_NCR5380_release_resources(dev_get_drvdata(pdev));
+       dev_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct isa_driver generic_NCR5380_isa_driver = {
+       .match          = generic_NCR5380_isa_match,
+       .remove         = generic_NCR5380_isa_remove,
+       .driver         = {
+               .name   = DRV_MODULE_NAME
+       },
+};
+
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+static struct pnp_device_id generic_NCR5380_pnp_ids[] = {
+       { .id = "DTC436e", .driver_data = BOARD_DTC3181E },
+       { .id = "" }
+};
+MODULE_DEVICE_TABLE(pnp, generic_NCR5380_pnp_ids);
+
+static int generic_NCR5380_pnp_probe(struct pnp_dev *pdev,
+                              const struct pnp_device_id *id)
+{
+       int base, irq;
+
+       if (pnp_activate_dev(pdev) < 0)
+               return -EBUSY;
+
+       base = pnp_port_start(pdev, 0);
+       irq = pnp_irq(pdev, 0);
+
+       return generic_NCR5380_init_one(&driver_template, &pdev->dev, base, irq,
+                                      id->driver_data);
+}
+
+static void generic_NCR5380_pnp_remove(struct pnp_dev *pdev)
+{
+       generic_NCR5380_release_resources(pnp_get_drvdata(pdev));
+       pnp_set_drvdata(pdev, NULL);
+}
+
+static struct pnp_driver generic_NCR5380_pnp_driver = {
+       .name           = DRV_MODULE_NAME,
+       .id_table       = generic_NCR5380_pnp_ids,
+       .probe          = generic_NCR5380_pnp_probe,
+       .remove         = generic_NCR5380_pnp_remove,
 };
+#endif /* !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP) */
+
+static int pnp_registered, isa_registered;
+
+static int __init generic_NCR5380_init(void)
+{
+       int ret = 0;
+
+       /* compatibility with old-style parameters */
+       if (irq[0] == 0 && base[0] == 0 && card[0] == -1) {
+               irq[0] = ncr_irq;
+               base[0] = ncr_addr;
+               if (ncr_5380)
+                       card[0] = BOARD_NCR5380;
+               if (ncr_53c400)
+                       card[0] = BOARD_NCR53C400;
+               if (ncr_53c400a)
+                       card[0] = BOARD_NCR53C400A;
+               if (dtc_3181e)
+                       card[0] = BOARD_DTC3181E;
+               if (hp_c2502)
+                       card[0] = BOARD_HP_C2502;
+       }
 
-MODULE_DEVICE_TABLE(isapnp, id_table);
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+       if (!pnp_register_driver(&generic_NCR5380_pnp_driver))
+               pnp_registered = 1;
 #endif
+       ret = isa_register_driver(&generic_NCR5380_isa_driver, MAX_CARDS);
+       if (!ret)
+               isa_registered = 1;
+
+       return (pnp_registered || isa_registered) ? 0 : ret;
+}
+
+static void __exit generic_NCR5380_exit(void)
+{
+#if !defined(SCSI_G_NCR5380_MEM) && defined(CONFIG_PNP)
+       if (pnp_registered)
+               pnp_unregister_driver(&generic_NCR5380_pnp_driver);
+#endif
+       if (isa_registered)
+               isa_unregister_driver(&generic_NCR5380_isa_driver);
+}
 
-__setup("ncr5380=", do_NCR5380_setup);
-__setup("ncr53c400=", do_NCR53C400_setup);
-__setup("ncr53c400a=", do_NCR53C400A_setup);
-__setup("dtc3181e=", do_DTC3181E_setup);
+module_init(generic_NCR5380_init);
+module_exit(generic_NCR5380_exit);
index 5951774..b175b92 100644 (file)
 #ifndef GENERIC_NCR5380_H
 #define GENERIC_NCR5380_H
 
-#define __STRVAL(x) #x
-#define STRVAL(x) __STRVAL(x)
-
 #ifndef SCSI_G_NCR5380_MEM
 #define DRV_MODULE_NAME "g_NCR5380"
 
-#define NCR5380_map_type int
-#define NCR5380_map_name port
-
 #define NCR5380_read(reg) \
        inb(instance->io_port + (reg))
 #define NCR5380_write(reg, value) \
@@ -38,8 +32,6 @@
 /* therefore SCSI_G_NCR5380_MEM */
 #define DRV_MODULE_NAME "g_NCR5380_mmio"
 
-#define NCR5380_map_type unsigned long
-#define NCR5380_map_name base
 #define NCR53C400_mem_base 0x3880
 #define NCR53C400_host_buffer 0x3900
 #define NCR53C400_region_size 0x3a00
index 4796690..e27b4d4 100644 (file)
@@ -63,7 +63,7 @@ config SCSI_UFSHCD_PCI
 
 config SCSI_UFS_DWC_TC_PCI
        tristate "DesignWare pci support using a G210 Test Chip"
-       depends on SCSI_UFSHCD && PCI
+       depends on SCSI_UFSHCD_PCI
        ---help---
          Synopsys Test Chip is a PHY for prototyping purposes.
 
index ee4ab85..22f881e 100644 (file)
@@ -25,6 +25,7 @@
 
 #define UFS_VENDOR_TOSHIBA     0x198
 #define UFS_VENDOR_SAMSUNG     0x1CE
+#define UFS_VENDOR_SKHYNIX     0x1AD
 
 /**
  * ufs_device_info - ufs device details
@@ -145,6 +146,7 @@ static struct ufs_dev_fix ufs_fixups[] = {
                UFS_DEVICE_QUIRK_PA_TACTIVATE),
        UFS_FIX(UFS_VENDOR_TOSHIBA, "THGLF2G9D8KBADG",
                UFS_DEVICE_QUIRK_PA_TACTIVATE),
+       UFS_FIX(UFS_VENDOR_SKHYNIX, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
 
        END_FIX
 };
index 37f3c51..05c7456 100644 (file)
@@ -1266,9 +1266,12 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba,
        ucd_req_ptr->header.dword_1 = UPIU_HEADER_DWORD(
                        0, query->request.query_func, 0, 0);
 
-       /* Data segment length */
-       ucd_req_ptr->header.dword_2 = UPIU_HEADER_DWORD(
-                       0, 0, len >> 8, (u8)len);
+       /* Data segment length only need for WRITE_DESC */
+       if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC)
+               ucd_req_ptr->header.dword_2 =
+                       UPIU_HEADER_DWORD(0, 0, (len >> 8), (u8)len);
+       else
+               ucd_req_ptr->header.dword_2 = 0;
 
        /* Copy the Query Request buffer as is */
        memcpy(&ucd_req_ptr->qr, &query->request.upiu_req,
@@ -6500,6 +6503,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
                if (IS_ERR(hba->devfreq)) {
                        dev_err(hba->dev, "Unable to register with devfreq %ld\n",
                                        PTR_ERR(hba->devfreq));
+                       err = PTR_ERR(hba->devfreq);
                        goto out_remove_scsi_host;
                }
                /* Suspend devfreq until the UFS device is detected */
index fe42a2f..e6e90e8 100644 (file)
@@ -1,6 +1,7 @@
 menu "SOC (System On Chip) specific Drivers"
 
 source "drivers/soc/bcm/Kconfig"
+source "drivers/soc/fsl/qbman/Kconfig"
 source "drivers/soc/fsl/qe/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
index 203307f..75e1f53 100644 (file)
@@ -2,5 +2,6 @@
 # Makefile for the Linux Kernel SOC fsl specific device drivers
 #
 
+obj-$(CONFIG_FSL_DPAA)                 += qbman/
 obj-$(CONFIG_QUICC_ENGINE)             += qe/
 obj-$(CONFIG_CPM)                      += qe/
diff --git a/drivers/soc/fsl/qbman/Kconfig b/drivers/soc/fsl/qbman/Kconfig
new file mode 100644 (file)
index 0000000..757033c
--- /dev/null
@@ -0,0 +1,67 @@
+menuconfig FSL_DPAA
+       bool "Freescale DPAA 1.x support"
+       depends on FSL_SOC_BOOKE
+       select GENERIC_ALLOCATOR
+       help
+         The Freescale Data Path Acceleration Architecture (DPAA) is a set of
+         hardware components on specific QorIQ multicore processors.
+         This architecture provides the infrastructure to support simplified
+         sharing of networking interfaces and accelerators by multiple CPUs.
+         The major h/w blocks composing DPAA are BMan and QMan.
+
+         The Buffer Manager (BMan) is a hardware buffer pool management block
+         that allows software and accelerators on the datapath to acquire and
+         release buffers in order to build frames.
+
+         The Queue Manager (QMan) is a hardware queue management block
+         that allows software and accelerators on the datapath to enqueue and
+         dequeue frames in order to communicate.
+
+if FSL_DPAA
+
+config FSL_DPAA_CHECKING
+       bool "Additional driver checking"
+       help
+         Compiles in additional checks, to sanity-check the drivers and
+         any use of the exported API. Not recommended for performance.
+
+config FSL_BMAN_TEST
+       tristate "BMan self-tests"
+       help
+         Compile the BMan self-test code. These tests will
+         exercise the BMan APIs to confirm functionality
+         of both the software drivers and hardware device.
+
+config FSL_BMAN_TEST_API
+       bool "High-level API self-test"
+       depends on FSL_BMAN_TEST
+       default y
+       help
+         This requires the presence of cpu-affine portals, and performs
+         high-level API testing with them (whichever portal(s) are affine
+         to the cpu(s) the test executes on).
+
+config FSL_QMAN_TEST
+       tristate "QMan self-tests"
+       help
+         Compile self-test code for QMan.
+
+config FSL_QMAN_TEST_API
+       bool "QMan high-level self-test"
+       depends on FSL_QMAN_TEST
+       default y
+       help
+         This requires the presence of cpu-affine portals, and performs
+         high-level API testing with them (whichever portal(s) are affine to
+         the cpu(s) the test executes on).
+
+config FSL_QMAN_TEST_STASH
+       bool "QMan 'hot potato' data-stashing self-test"
+       depends on FSL_QMAN_TEST
+       default y
+       help
+         This performs a "hot potato" style test enqueuing/dequeuing a frame
+         across a series of FQs scheduled to different portals (and cpus), with
+         DQRR, data and context stashing always on.
+
+endif # FSL_DPAA
diff --git a/drivers/soc/fsl/qbman/Makefile b/drivers/soc/fsl/qbman/Makefile
new file mode 100644 (file)
index 0000000..7ae199f
--- /dev/null
@@ -0,0 +1,12 @@
+obj-$(CONFIG_FSL_DPAA)                          += bman_ccsr.o qman_ccsr.o \
+                                                  bman_portal.o qman_portal.o \
+                                                  bman.o qman.o
+
+obj-$(CONFIG_FSL_BMAN_TEST)                     += bman-test.o
+bman-test-y                                      = bman_test.o
+bman-test-$(CONFIG_FSL_BMAN_TEST_API)           += bman_test_api.o
+
+obj-$(CONFIG_FSL_QMAN_TEST)                    += qman-test.o
+qman-test-y                                     = qman_test.o
+qman-test-$(CONFIG_FSL_QMAN_TEST_API)          += qman_test_api.o
+qman-test-$(CONFIG_FSL_QMAN_TEST_STASH)                += qman_test_stash.o
diff --git a/drivers/soc/fsl/qbman/bman.c b/drivers/soc/fsl/qbman/bman.c
new file mode 100644 (file)
index 0000000..ffa48fd
--- /dev/null
@@ -0,0 +1,797 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_priv.h"
+
+#define IRQNAME                "BMan portal %d"
+#define MAX_IRQNAME    16      /* big enough for "BMan portal %d" */
+
+/* Portal register assists */
+
+/* Cache-inhibited register offsets */
+#define BM_REG_RCR_PI_CINH     0x0000
+#define BM_REG_RCR_CI_CINH     0x0004
+#define BM_REG_RCR_ITR         0x0008
+#define BM_REG_CFG             0x0100
+#define BM_REG_SCN(n)          (0x0200 + ((n) << 2))
+#define BM_REG_ISR             0x0e00
+#define BM_REG_IER             0x0e04
+#define BM_REG_ISDR            0x0e08
+#define BM_REG_IIR             0x0e0c
+
+/* Cache-enabled register offsets */
+#define BM_CL_CR               0x0000
+#define BM_CL_RR0              0x0100
+#define BM_CL_RR1              0x0140
+#define BM_CL_RCR              0x1000
+#define BM_CL_RCR_PI_CENA      0x3000
+#define BM_CL_RCR_CI_CENA      0x3100
+
+/*
+ * Portal modes.
+ *   Enum types;
+ *     pmode == production mode
+ *     cmode == consumption mode,
+ *   Enum values use 3 letter codes. First letter matches the portal mode,
+ *   remaining two letters indicate;
+ *     ci == cache-inhibited portal register
+ *     ce == cache-enabled portal register
+ *     vb == in-band valid-bit (cache-enabled)
+ */
+enum bm_rcr_pmode {            /* matches BCSP_CFG::RPM */
+       bm_rcr_pci = 0,         /* PI index, cache-inhibited */
+       bm_rcr_pce = 1,         /* PI index, cache-enabled */
+       bm_rcr_pvb = 2          /* valid-bit */
+};
+enum bm_rcr_cmode {            /* s/w-only */
+       bm_rcr_cci,             /* CI index, cache-inhibited */
+       bm_rcr_cce              /* CI index, cache-enabled */
+};
+
+
+/* --- Portal structures --- */
+
+#define BM_RCR_SIZE            8
+
+/* Release Command */
+struct bm_rcr_entry {
+       union {
+               struct {
+                       u8 _ncw_verb; /* writes to this are non-coherent */
+                       u8 bpid; /* used with BM_RCR_VERB_CMD_BPID_SINGLE */
+                       u8 __reserved1[62];
+               };
+               struct bm_buffer bufs[8];
+       };
+};
+#define BM_RCR_VERB_VBIT               0x80
+#define BM_RCR_VERB_CMD_MASK           0x70    /* one of two values; */
+#define BM_RCR_VERB_CMD_BPID_SINGLE    0x20
+#define BM_RCR_VERB_CMD_BPID_MULTI     0x30
+#define BM_RCR_VERB_BUFCOUNT_MASK      0x0f    /* values 1..8 */
+
+struct bm_rcr {
+       struct bm_rcr_entry *ring, *cursor;
+       u8 ci, available, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       u32 busy;
+       enum bm_rcr_pmode pmode;
+       enum bm_rcr_cmode cmode;
+#endif
+};
+
+/* MC (Management Command) command */
+struct bm_mc_command {
+       u8 _ncw_verb; /* writes to this are non-coherent */
+       u8 bpid; /* used by acquire command */
+       u8 __reserved[62];
+};
+#define BM_MCC_VERB_VBIT               0x80
+#define BM_MCC_VERB_CMD_MASK           0x70    /* where the verb contains; */
+#define BM_MCC_VERB_CMD_ACQUIRE                0x10
+#define BM_MCC_VERB_CMD_QUERY          0x40
+#define BM_MCC_VERB_ACQUIRE_BUFCOUNT   0x0f    /* values 1..8 go here */
+
+/* MC result, Acquire and Query Response */
+union bm_mc_result {
+       struct {
+               u8 verb;
+               u8 bpid;
+               u8 __reserved[62];
+       };
+       struct bm_buffer bufs[8];
+};
+#define BM_MCR_VERB_VBIT               0x80
+#define BM_MCR_VERB_CMD_MASK           BM_MCC_VERB_CMD_MASK
+#define BM_MCR_VERB_CMD_ACQUIRE                BM_MCC_VERB_CMD_ACQUIRE
+#define BM_MCR_VERB_CMD_QUERY          BM_MCC_VERB_CMD_QUERY
+#define BM_MCR_VERB_CMD_ERR_INVALID    0x60
+#define BM_MCR_VERB_CMD_ERR_ECC                0x70
+#define BM_MCR_VERB_ACQUIRE_BUFCOUNT   BM_MCC_VERB_ACQUIRE_BUFCOUNT /* 0..8 */
+#define BM_MCR_TIMEOUT                 10000 /* us */
+
+struct bm_mc {
+       struct bm_mc_command *cr;
+       union bm_mc_result *rr;
+       u8 rridx, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum {
+               /* Can only be _mc_start()ed */
+               mc_idle,
+               /* Can only be _mc_commit()ed or _mc_abort()ed */
+               mc_user,
+               /* Can only be _mc_retry()ed */
+               mc_hw
+       } state;
+#endif
+};
+
+struct bm_addr {
+       void __iomem *ce;       /* cache-enabled */
+       void __iomem *ci;       /* cache-inhibited */
+};
+
+struct bm_portal {
+       struct bm_addr addr;
+       struct bm_rcr rcr;
+       struct bm_mc mc;
+} ____cacheline_aligned;
+
+/* Cache-inhibited register access. */
+static inline u32 bm_in(struct bm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ci + offset);
+}
+
+static inline void bm_out(struct bm_portal *p, u32 offset, u32 val)
+{
+       __raw_writel(val, p->addr.ci + offset);
+}
+
+/* Cache Enabled Portal Access */
+static inline void bm_cl_invalidate(struct bm_portal *p, u32 offset)
+{
+       dpaa_invalidate(p->addr.ce + offset);
+}
+
+static inline void bm_cl_touch_ro(struct bm_portal *p, u32 offset)
+{
+       dpaa_touch_ro(p->addr.ce + offset);
+}
+
+static inline u32 bm_ce_in(struct bm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ce + offset);
+}
+
+struct bman_portal {
+       struct bm_portal p;
+       /* interrupt sources processed by portal_isr(), configurable */
+       unsigned long irq_sources;
+       /* probing time config params for cpu-affine portals */
+       const struct bm_portal_config *config;
+       char irqname[MAX_IRQNAME];
+};
+
+static cpumask_t affine_mask;
+static DEFINE_SPINLOCK(affine_mask_lock);
+static DEFINE_PER_CPU(struct bman_portal, bman_affine_portal);
+
+static inline struct bman_portal *get_affine_portal(void)
+{
+       return &get_cpu_var(bman_affine_portal);
+}
+
+static inline void put_affine_portal(void)
+{
+       put_cpu_var(bman_affine_portal);
+}
+
+/*
+ * This object type refers to a pool, it isn't *the* pool. There may be
+ * more than one such object per BMan buffer pool, eg. if different users of the
+ * pool are operating via different portals.
+ */
+struct bman_pool {
+       /* index of the buffer pool to encapsulate (0-63) */
+       u32 bpid;
+       /* Used for hash-table admin when using depletion notifications. */
+       struct bman_portal *portal;
+       struct bman_pool *next;
+};
+
+static u32 poll_portal_slow(struct bman_portal *p, u32 is);
+
+static irqreturn_t portal_isr(int irq, void *ptr)
+{
+       struct bman_portal *p = ptr;
+       struct bm_portal *portal = &p->p;
+       u32 clear = p->irq_sources;
+       u32 is = bm_in(portal, BM_REG_ISR) & p->irq_sources;
+
+       if (unlikely(!is))
+               return IRQ_NONE;
+
+       clear |= poll_portal_slow(p, is);
+       bm_out(portal, BM_REG_ISR, clear);
+       return IRQ_HANDLED;
+}
+
+/* --- RCR API --- */
+
+#define RCR_SHIFT      ilog2(sizeof(struct bm_rcr_entry))
+#define RCR_CARRY      (uintptr_t)(BM_RCR_SIZE << RCR_SHIFT)
+
+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
+static struct bm_rcr_entry *rcr_carryclear(struct bm_rcr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~RCR_CARRY;
+
+       return (struct bm_rcr_entry *)addr;
+}
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+/* Bit-wise logic to convert a ring pointer to a ring index */
+static int rcr_ptr2idx(struct bm_rcr_entry *e)
+{
+       return ((uintptr_t)e >> RCR_SHIFT) & (BM_RCR_SIZE - 1);
+}
+#endif
+
+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
+static inline void rcr_inc(struct bm_rcr *rcr)
+{
+       /* increment to the next RCR pointer and handle overflow and 'vbit' */
+       struct bm_rcr_entry *partial = rcr->cursor + 1;
+
+       rcr->cursor = rcr_carryclear(partial);
+       if (partial != rcr->cursor)
+               rcr->vbit ^= BM_RCR_VERB_VBIT;
+}
+
+static int bm_rcr_get_avail(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       return rcr->available;
+}
+
+static int bm_rcr_get_fill(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       return BM_RCR_SIZE - 1 - rcr->available;
+}
+
+static void bm_rcr_set_ithresh(struct bm_portal *portal, u8 ithresh)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       rcr->ithresh = ithresh;
+       bm_out(portal, BM_REG_RCR_ITR, ithresh);
+}
+
+static void bm_rcr_cce_prefetch(struct bm_portal *portal)
+{
+       __maybe_unused struct bm_rcr *rcr = &portal->rcr;
+
+       DPAA_ASSERT(rcr->cmode == bm_rcr_cce);
+       bm_cl_touch_ro(portal, BM_CL_RCR_CI_CENA);
+}
+
+static u8 bm_rcr_cce_update(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       u8 diff, old_ci = rcr->ci;
+
+       DPAA_ASSERT(rcr->cmode == bm_rcr_cce);
+       rcr->ci = bm_ce_in(portal, BM_CL_RCR_CI_CENA) & (BM_RCR_SIZE - 1);
+       bm_cl_invalidate(portal, BM_CL_RCR_CI_CENA);
+       diff = dpaa_cyc_diff(BM_RCR_SIZE, old_ci, rcr->ci);
+       rcr->available += diff;
+       return diff;
+}
+
+static inline struct bm_rcr_entry *bm_rcr_start(struct bm_portal *portal)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+
+       DPAA_ASSERT(!rcr->busy);
+       if (!rcr->available)
+               return NULL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 1;
+#endif
+       dpaa_zero(rcr->cursor);
+       return rcr->cursor;
+}
+
+static inline void bm_rcr_pvb_commit(struct bm_portal *portal, u8 myverb)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       struct bm_rcr_entry *rcursor;
+
+       DPAA_ASSERT(rcr->busy);
+       DPAA_ASSERT(rcr->pmode == bm_rcr_pvb);
+       DPAA_ASSERT(rcr->available >= 1);
+       dma_wmb();
+       rcursor = rcr->cursor;
+       rcursor->_ncw_verb = myverb | rcr->vbit;
+       dpaa_flush(rcursor);
+       rcr_inc(rcr);
+       rcr->available--;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 0;
+#endif
+}
+
+static int bm_rcr_init(struct bm_portal *portal, enum bm_rcr_pmode pmode,
+                      enum bm_rcr_cmode cmode)
+{
+       struct bm_rcr *rcr = &portal->rcr;
+       u32 cfg;
+       u8 pi;
+
+       rcr->ring = portal->addr.ce + BM_CL_RCR;
+       rcr->ci = bm_in(portal, BM_REG_RCR_CI_CINH) & (BM_RCR_SIZE - 1);
+       pi = bm_in(portal, BM_REG_RCR_PI_CINH) & (BM_RCR_SIZE - 1);
+       rcr->cursor = rcr->ring + pi;
+       rcr->vbit = (bm_in(portal, BM_REG_RCR_PI_CINH) & BM_RCR_SIZE) ?
+               BM_RCR_VERB_VBIT : 0;
+       rcr->available = BM_RCR_SIZE - 1
+               - dpaa_cyc_diff(BM_RCR_SIZE, rcr->ci, pi);
+       rcr->ithresh = bm_in(portal, BM_REG_RCR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       rcr->busy = 0;
+       rcr->pmode = pmode;
+       rcr->cmode = cmode;
+#endif
+       cfg = (bm_in(portal, BM_REG_CFG) & 0xffffffe0)
+               | (pmode & 0x3); /* BCSP_CFG::RPM */
+       bm_out(portal, BM_REG_CFG, cfg);
+       return 0;
+}
+
+static void bm_rcr_finish(struct bm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct bm_rcr *rcr = &portal->rcr;
+       int i;
+
+       DPAA_ASSERT(!rcr->busy);
+
+       i = bm_in(portal, BM_REG_RCR_PI_CINH) & (BM_RCR_SIZE - 1);
+       if (i != rcr_ptr2idx(rcr->cursor))
+               pr_crit("losing uncommited RCR entries\n");
+
+       i = bm_in(portal, BM_REG_RCR_CI_CINH) & (BM_RCR_SIZE - 1);
+       if (i != rcr->ci)
+               pr_crit("missing existing RCR completions\n");
+       if (rcr->ci != rcr_ptr2idx(rcr->cursor))
+               pr_crit("RCR destroyed unquiesced\n");
+#endif
+}
+
+/* --- Management command API --- */
+static int bm_mc_init(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+
+       mc->cr = portal->addr.ce + BM_CL_CR;
+       mc->rr = portal->addr.ce + BM_CL_RR0;
+       mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & BM_MCC_VERB_VBIT) ?
+                   0 : 1;
+       mc->vbit = mc->rridx ? BM_MCC_VERB_VBIT : 0;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_idle;
+#endif
+       return 0;
+}
+
+static void bm_mc_finish(struct bm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct bm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == mc_idle);
+       if (mc->state != mc_idle)
+               pr_crit("Losing incomplete MC command\n");
+#endif
+}
+
+static inline struct bm_mc_command *bm_mc_start(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == mc_idle);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_user;
+#endif
+       dpaa_zero(mc->cr);
+       return mc->cr;
+}
+
+static inline void bm_mc_commit(struct bm_portal *portal, u8 myverb)
+{
+       struct bm_mc *mc = &portal->mc;
+       union bm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == mc_user);
+       dma_wmb();
+       mc->cr->_ncw_verb = myverb | mc->vbit;
+       dpaa_flush(mc->cr);
+       dpaa_invalidate_touch_ro(rr);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_hw;
+#endif
+}
+
+static inline union bm_mc_result *bm_mc_result(struct bm_portal *portal)
+{
+       struct bm_mc *mc = &portal->mc;
+       union bm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == mc_hw);
+       /*
+        * The inactive response register's verb byte always returns zero until
+        * its command is submitted and completed. This includes the valid-bit,
+        * in case you were wondering...
+        */
+       if (!__raw_readb(&rr->verb)) {
+               dpaa_invalidate_touch_ro(rr);
+               return NULL;
+       }
+       mc->rridx ^= 1;
+       mc->vbit ^= BM_MCC_VERB_VBIT;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = mc_idle;
+#endif
+       return rr;
+}
+
+static inline int bm_mc_result_timeout(struct bm_portal *portal,
+                                      union bm_mc_result **mcr)
+{
+       int timeout = BM_MCR_TIMEOUT;
+
+       do {
+               *mcr = bm_mc_result(portal);
+               if (*mcr)
+                       break;
+               udelay(1);
+       } while (--timeout);
+
+       return timeout;
+}
+
+/* Disable all BSCN interrupts for the portal */
+static void bm_isr_bscn_disable(struct bm_portal *portal)
+{
+       bm_out(portal, BM_REG_SCN(0), 0);
+       bm_out(portal, BM_REG_SCN(1), 0);
+}
+
+static int bman_create_portal(struct bman_portal *portal,
+                             const struct bm_portal_config *c)
+{
+       struct bm_portal *p;
+       int ret;
+
+       p = &portal->p;
+       /*
+        * prep the low-level portal struct with the mapped addresses from the
+        * config, everything that follows depends on it and "config" is more
+        * for (de)reference...
+        */
+       p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
+       p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
+       if (bm_rcr_init(p, bm_rcr_pvb, bm_rcr_cce)) {
+               dev_err(c->dev, "RCR initialisation failed\n");
+               goto fail_rcr;
+       }
+       if (bm_mc_init(p)) {
+               dev_err(c->dev, "MC initialisation failed\n");
+               goto fail_mc;
+       }
+       /*
+        * Default to all BPIDs disabled, we enable as required at
+        * run-time.
+        */
+       bm_isr_bscn_disable(p);
+
+       /* Write-to-clear any stale interrupt status bits */
+       bm_out(p, BM_REG_ISDR, 0xffffffff);
+       portal->irq_sources = 0;
+       bm_out(p, BM_REG_IER, 0);
+       bm_out(p, BM_REG_ISR, 0xffffffff);
+       snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
+       if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
+               dev_err(c->dev, "request_irq() failed\n");
+               goto fail_irq;
+       }
+       if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
+           irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
+               dev_err(c->dev, "irq_set_affinity() failed\n");
+               goto fail_affinity;
+       }
+
+       /* Need RCR to be empty before continuing */
+       ret = bm_rcr_get_fill(p);
+       if (ret) {
+               dev_err(c->dev, "RCR unclean\n");
+               goto fail_rcr_empty;
+       }
+       /* Success */
+       portal->config = c;
+
+       bm_out(p, BM_REG_ISDR, 0);
+       bm_out(p, BM_REG_IIR, 0);
+
+       return 0;
+
+fail_rcr_empty:
+fail_affinity:
+       free_irq(c->irq, portal);
+fail_irq:
+       bm_mc_finish(p);
+fail_mc:
+       bm_rcr_finish(p);
+fail_rcr:
+       return -EIO;
+}
+
+struct bman_portal *bman_create_affine_portal(const struct bm_portal_config *c)
+{
+       struct bman_portal *portal;
+       int err;
+
+       portal = &per_cpu(bman_affine_portal, c->cpu);
+       err = bman_create_portal(portal, c);
+       if (err)
+               return NULL;
+
+       spin_lock(&affine_mask_lock);
+       cpumask_set_cpu(c->cpu, &affine_mask);
+       spin_unlock(&affine_mask_lock);
+
+       return portal;
+}
+
+static u32 poll_portal_slow(struct bman_portal *p, u32 is)
+{
+       u32 ret = is;
+
+       if (is & BM_PIRQ_RCRI) {
+               bm_rcr_cce_update(&p->p);
+               bm_rcr_set_ithresh(&p->p, 0);
+               bm_out(&p->p, BM_REG_ISR, BM_PIRQ_RCRI);
+               is &= ~BM_PIRQ_RCRI;
+       }
+
+       /* There should be no status register bits left undefined */
+       DPAA_ASSERT(!is);
+       return ret;
+}
+
+int bman_p_irqsource_add(struct bman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       set_bits(bits & BM_PIRQ_VISIBLE, &p->irq_sources);
+       bm_out(&p->p, BM_REG_IER, p->irq_sources);
+       local_irq_restore(irqflags);
+       return 0;
+}
+
+static int bm_shutdown_pool(u32 bpid)
+{
+       struct bm_mc_command *bm_cmd;
+       union bm_mc_result *bm_res;
+
+       while (1) {
+               struct bman_portal *p = get_affine_portal();
+               /* Acquire buffers until empty */
+               bm_cmd = bm_mc_start(&p->p);
+               bm_cmd->bpid = bpid;
+               bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE | 1);
+               if (!bm_mc_result_timeout(&p->p, &bm_res)) {
+                       put_affine_portal();
+                       pr_crit("BMan Acquire Command timedout\n");
+                       return -ETIMEDOUT;
+               }
+               if (!(bm_res->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT)) {
+                       put_affine_portal();
+                       /* Pool is empty */
+                       return 0;
+               }
+               put_affine_portal();
+       }
+
+       return 0;
+}
+
+struct gen_pool *bm_bpalloc;
+
+static int bm_alloc_bpid_range(u32 *result, u32 count)
+{
+       unsigned long addr;
+
+       addr = gen_pool_alloc(bm_bpalloc, count);
+       if (!addr)
+               return -ENOMEM;
+
+       *result = addr & ~DPAA_GENALLOC_OFF;
+
+       return 0;
+}
+
+static int bm_release_bpid(u32 bpid)
+{
+       int ret;
+
+       ret = bm_shutdown_pool(bpid);
+       if (ret) {
+               pr_debug("BPID %d leaked\n", bpid);
+               return ret;
+       }
+
+       gen_pool_free(bm_bpalloc, bpid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+
+struct bman_pool *bman_new_pool(void)
+{
+       struct bman_pool *pool = NULL;
+       u32 bpid;
+
+       if (bm_alloc_bpid_range(&bpid, 1))
+               return NULL;
+
+       pool = kmalloc(sizeof(*pool), GFP_KERNEL);
+       if (!pool)
+               goto err;
+
+       pool->bpid = bpid;
+
+       return pool;
+err:
+       bm_release_bpid(bpid);
+       kfree(pool);
+       return NULL;
+}
+EXPORT_SYMBOL(bman_new_pool);
+
+void bman_free_pool(struct bman_pool *pool)
+{
+       bm_release_bpid(pool->bpid);
+
+       kfree(pool);
+}
+EXPORT_SYMBOL(bman_free_pool);
+
+int bman_get_bpid(const struct bman_pool *pool)
+{
+       return pool->bpid;
+}
+EXPORT_SYMBOL(bman_get_bpid);
+
+static void update_rcr_ci(struct bman_portal *p, int avail)
+{
+       if (avail)
+               bm_rcr_cce_prefetch(&p->p);
+       else
+               bm_rcr_cce_update(&p->p);
+}
+
+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num)
+{
+       struct bman_portal *p;
+       struct bm_rcr_entry *r;
+       unsigned long irqflags;
+       int avail, timeout = 1000; /* 1ms */
+       int i = num - 1;
+
+       DPAA_ASSERT(num > 0 && num <= 8);
+
+       do {
+               p = get_affine_portal();
+               local_irq_save(irqflags);
+               avail = bm_rcr_get_avail(&p->p);
+               if (avail < 2)
+                       update_rcr_ci(p, avail);
+               r = bm_rcr_start(&p->p);
+               local_irq_restore(irqflags);
+               put_affine_portal();
+               if (likely(r))
+                       break;
+
+               udelay(1);
+       } while (--timeout);
+
+       if (unlikely(!timeout))
+               return -ETIMEDOUT;
+
+       p = get_affine_portal();
+       local_irq_save(irqflags);
+       /*
+        * we can copy all but the first entry, as this can trigger badness
+        * with the valid-bit
+        */
+       bm_buffer_set64(r->bufs, bm_buffer_get64(bufs));
+       bm_buffer_set_bpid(r->bufs, pool->bpid);
+       if (i)
+               memcpy(&r->bufs[1], &bufs[1], i * sizeof(bufs[0]));
+
+       bm_rcr_pvb_commit(&p->p, BM_RCR_VERB_CMD_BPID_SINGLE |
+                         (num & BM_RCR_VERB_BUFCOUNT_MASK));
+
+       local_irq_restore(irqflags);
+       put_affine_portal();
+       return 0;
+}
+EXPORT_SYMBOL(bman_release);
+
+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num)
+{
+       struct bman_portal *p = get_affine_portal();
+       struct bm_mc_command *mcc;
+       union bm_mc_result *mcr;
+       int ret;
+
+       DPAA_ASSERT(num > 0 && num <= 8);
+
+       mcc = bm_mc_start(&p->p);
+       mcc->bpid = pool->bpid;
+       bm_mc_commit(&p->p, BM_MCC_VERB_CMD_ACQUIRE |
+                    (num & BM_MCC_VERB_ACQUIRE_BUFCOUNT));
+       if (!bm_mc_result_timeout(&p->p, &mcr)) {
+               put_affine_portal();
+               pr_crit("BMan Acquire Timeout\n");
+               return -ETIMEDOUT;
+       }
+       ret = mcr->verb & BM_MCR_VERB_ACQUIRE_BUFCOUNT;
+       if (bufs)
+               memcpy(&bufs[0], &mcr->bufs[0], num * sizeof(bufs[0]));
+
+       put_affine_portal();
+       if (ret != num)
+               ret = -ENOMEM;
+       return ret;
+}
+EXPORT_SYMBOL(bman_acquire);
+
+const struct bm_portal_config *
+bman_get_bm_portal_config(const struct bman_portal *portal)
+{
+       return portal->config;
+}
diff --git a/drivers/soc/fsl/qbman/bman_ccsr.c b/drivers/soc/fsl/qbman/bman_ccsr.c
new file mode 100644 (file)
index 0000000..9deb052
--- /dev/null
@@ -0,0 +1,263 @@
+/* Copyright (c) 2009 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_priv.h"
+
+u16 bman_ip_rev;
+EXPORT_SYMBOL(bman_ip_rev);
+
+/* Register offsets */
+#define REG_FBPR_FPC           0x0800
+#define REG_ECSR               0x0a00
+#define REG_ECIR               0x0a04
+#define REG_EADR               0x0a08
+#define REG_EDATA(n)           (0x0a10 + ((n) * 0x04))
+#define REG_SBEC(n)            (0x0a80 + ((n) * 0x04))
+#define REG_IP_REV_1           0x0bf8
+#define REG_IP_REV_2           0x0bfc
+#define REG_FBPR_BARE          0x0c00
+#define REG_FBPR_BAR           0x0c04
+#define REG_FBPR_AR            0x0c10
+#define REG_SRCIDR             0x0d04
+#define REG_LIODNR             0x0d08
+#define REG_ERR_ISR            0x0e00
+#define REG_ERR_IER            0x0e04
+#define REG_ERR_ISDR           0x0e08
+
+/* Used by all error interrupt registers except 'inhibit' */
+#define BM_EIRQ_IVCI   0x00000010      /* Invalid Command Verb */
+#define BM_EIRQ_FLWI   0x00000008      /* FBPR Low Watermark */
+#define BM_EIRQ_MBEI   0x00000004      /* Multi-bit ECC Error */
+#define BM_EIRQ_SBEI   0x00000002      /* Single-bit ECC Error */
+#define BM_EIRQ_BSCN   0x00000001      /* pool State Change Notification */
+
+struct bman_hwerr_txt {
+       u32 mask;
+       const char *txt;
+};
+
+static const struct bman_hwerr_txt bman_hwerr_txts[] = {
+       { BM_EIRQ_IVCI, "Invalid Command Verb" },
+       { BM_EIRQ_FLWI, "FBPR Low Watermark" },
+       { BM_EIRQ_MBEI, "Multi-bit ECC Error" },
+       { BM_EIRQ_SBEI, "Single-bit ECC Error" },
+       { BM_EIRQ_BSCN, "Pool State Change Notification" },
+};
+
+/* Only trigger low water mark interrupt once only */
+#define BMAN_ERRS_TO_DISABLE BM_EIRQ_FLWI
+
+/* Pointer to the start of the BMan's CCSR space */
+static u32 __iomem *bm_ccsr_start;
+
+static inline u32 bm_ccsr_in(u32 offset)
+{
+       return ioread32be(bm_ccsr_start + offset/4);
+}
+static inline void bm_ccsr_out(u32 offset, u32 val)
+{
+       iowrite32be(val, bm_ccsr_start + offset/4);
+}
+
+static void bm_get_version(u16 *id, u8 *major, u8 *minor)
+{
+       u32 v = bm_ccsr_in(REG_IP_REV_1);
+       *id = (v >> 16);
+       *major = (v >> 8) & 0xff;
+       *minor = v & 0xff;
+}
+
+/* signal transactions for FBPRs with higher priority */
+#define FBPR_AR_RPRIO_HI BIT(30)
+
+static void bm_set_memory(u64 ba, u32 size)
+{
+       u32 exp = ilog2(size);
+       /* choke if size isn't within range */
+       DPAA_ASSERT(size >= 4096 && size <= 1024*1024*1024 &&
+                  is_power_of_2(size));
+       /* choke if '[e]ba' has lower-alignment than 'size' */
+       DPAA_ASSERT(!(ba & (size - 1)));
+       bm_ccsr_out(REG_FBPR_BARE, upper_32_bits(ba));
+       bm_ccsr_out(REG_FBPR_BAR, lower_32_bits(ba));
+       bm_ccsr_out(REG_FBPR_AR, exp - 1);
+}
+
+/*
+ * Location and size of BMan private memory
+ *
+ * Ideally we would use the DMA API to turn rmem->base into a DMA address
+ * (especially if iommu translations ever get involved).  Unfortunately, the
+ * DMA API currently does not allow mapping anything that is not backed with
+ * a struct page.
+ */
+static dma_addr_t fbpr_a;
+static size_t fbpr_sz;
+
+static int bman_fbpr(struct reserved_mem *rmem)
+{
+       fbpr_a = rmem->base;
+       fbpr_sz = rmem->size;
+
+       WARN_ON(!(fbpr_a && fbpr_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(bman_fbpr, "fsl,bman-fbpr", bman_fbpr);
+
+static irqreturn_t bman_isr(int irq, void *ptr)
+{
+       u32 isr_val, ier_val, ecsr_val, isr_mask, i;
+       struct device *dev = ptr;
+
+       ier_val = bm_ccsr_in(REG_ERR_IER);
+       isr_val = bm_ccsr_in(REG_ERR_ISR);
+       ecsr_val = bm_ccsr_in(REG_ECSR);
+       isr_mask = isr_val & ier_val;
+
+       if (!isr_mask)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(bman_hwerr_txts); i++) {
+               if (bman_hwerr_txts[i].mask & isr_mask) {
+                       dev_err_ratelimited(dev, "ErrInt: %s\n",
+                                           bman_hwerr_txts[i].txt);
+                       if (bman_hwerr_txts[i].mask & ecsr_val) {
+                               /* Re-arm error capture registers */
+                               bm_ccsr_out(REG_ECSR, ecsr_val);
+                       }
+                       if (bman_hwerr_txts[i].mask & BMAN_ERRS_TO_DISABLE) {
+                               dev_dbg(dev, "Disabling error 0x%x\n",
+                                       bman_hwerr_txts[i].mask);
+                               ier_val &= ~bman_hwerr_txts[i].mask;
+                               bm_ccsr_out(REG_ERR_IER, ier_val);
+                       }
+               }
+       }
+       bm_ccsr_out(REG_ERR_ISR, isr_val);
+
+       return IRQ_HANDLED;
+}
+
+static int fsl_bman_probe(struct platform_device *pdev)
+{
+       int ret, err_irq;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       u16 id, bm_pool_cnt;
+       u8 major, minor;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       bm_ccsr_start = devm_ioremap(dev, res->start,
+                                    res->end - res->start + 1);
+       if (!bm_ccsr_start)
+               return -ENXIO;
+
+       bm_get_version(&id, &major, &minor);
+       if (major == 1 && minor == 0) {
+               bman_ip_rev = BMAN_REV10;
+               bm_pool_cnt = BM_POOL_MAX;
+       } else if (major == 2 && minor == 0) {
+               bman_ip_rev = BMAN_REV20;
+               bm_pool_cnt = 8;
+       } else if (major == 2 && minor == 1) {
+               bman_ip_rev = BMAN_REV21;
+               bm_pool_cnt = BM_POOL_MAX;
+       } else {
+               dev_err(dev, "Unknown Bman version:%04x,%02x,%02x\n",
+                       id, major, minor);
+               return -ENODEV;
+       }
+
+       bm_set_memory(fbpr_a, fbpr_sz);
+
+       err_irq = platform_get_irq(pdev, 0);
+       if (err_irq <= 0) {
+               dev_info(dev, "Can't get %s IRQ\n", node->full_name);
+               return -ENODEV;
+       }
+       ret = devm_request_irq(dev, err_irq, bman_isr, IRQF_SHARED, "bman-err",
+                              dev);
+       if (ret)  {
+               dev_err(dev, "devm_request_irq() failed %d for '%s'\n",
+                       ret, node->full_name);
+               return ret;
+       }
+       /* Disable Buffer Pool State Change */
+       bm_ccsr_out(REG_ERR_ISDR, BM_EIRQ_BSCN);
+       /*
+        * Write-to-clear any stale bits, (eg. starvation being asserted prior
+        * to resource allocation during driver init).
+        */
+       bm_ccsr_out(REG_ERR_ISR, 0xffffffff);
+       /* Enable Error Interrupts */
+       bm_ccsr_out(REG_ERR_IER, 0xffffffff);
+
+       bm_bpalloc = devm_gen_pool_create(dev, 0, -1, "bman-bpalloc");
+       if (IS_ERR(bm_bpalloc)) {
+               ret = PTR_ERR(bm_bpalloc);
+               dev_err(dev, "bman-bpalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       /* seed BMan resource pool */
+       ret = gen_pool_add(bm_bpalloc, DPAA_GENALLOC_OFF, bm_pool_cnt, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed BPID range [%d..%d] (%d)\n",
+                       0, bm_pool_cnt - 1, ret);
+               return ret;
+       }
+
+       return 0;
+};
+
+static const struct of_device_id fsl_bman_ids[] = {
+       {
+               .compatible = "fsl,bman",
+       },
+       {}
+};
+
+static struct platform_driver fsl_bman_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = fsl_bman_ids,
+               .suppress_bind_attrs = true,
+       },
+       .probe = fsl_bman_probe,
+};
+
+builtin_platform_driver(fsl_bman_driver);
diff --git a/drivers/soc/fsl/qbman/bman_portal.c b/drivers/soc/fsl/qbman/bman_portal.c
new file mode 100644 (file)
index 0000000..6579cc1
--- /dev/null
@@ -0,0 +1,219 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_priv.h"
+
+static struct bman_portal *affine_bportals[NR_CPUS];
+static struct cpumask portal_cpus;
+/* protect bman global registers and global data shared among portals */
+static DEFINE_SPINLOCK(bman_lock);
+
+static struct bman_portal *init_pcfg(struct bm_portal_config *pcfg)
+{
+       struct bman_portal *p = bman_create_affine_portal(pcfg);
+
+       if (!p) {
+               dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
+                        __func__, pcfg->cpu);
+               return NULL;
+       }
+
+       bman_p_irqsource_add(p, BM_PIRQ_RCRI);
+       affine_bportals[pcfg->cpu] = p;
+
+       dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
+
+       return p;
+}
+
+static void bman_offline_cpu(unsigned int cpu)
+{
+       struct bman_portal *p = affine_bportals[cpu];
+       const struct bm_portal_config *pcfg;
+
+       if (!p)
+               return;
+
+       pcfg = bman_get_bm_portal_config(p);
+       if (!pcfg)
+               return;
+
+       irq_set_affinity(pcfg->irq, cpumask_of(0));
+}
+
+static void bman_online_cpu(unsigned int cpu)
+{
+       struct bman_portal *p = affine_bportals[cpu];
+       const struct bm_portal_config *pcfg;
+
+       if (!p)
+               return;
+
+       pcfg = bman_get_bm_portal_config(p);
+       if (!pcfg)
+               return;
+
+       irq_set_affinity(pcfg->irq, cpumask_of(cpu));
+}
+
+static int bman_hotplug_cpu_callback(struct notifier_block *nfb,
+                                    unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               bman_online_cpu(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
+               bman_offline_cpu(cpu);
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block bman_hotplug_cpu_notifier = {
+       .notifier_call = bman_hotplug_cpu_callback,
+};
+
+static int bman_portal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct bm_portal_config *pcfg;
+       struct resource *addr_phys[2];
+       void __iomem *va;
+       int irq, cpu;
+
+       pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
+       if (!pcfg)
+               return -ENOMEM;
+
+       pcfg->dev = dev;
+
+       addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CE);
+       if (!addr_phys[0]) {
+               dev_err(dev, "Can't get %s property 'reg::CE'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CI);
+       if (!addr_phys[1]) {
+               dev_err(dev, "Can't get %s property 'reg::CI'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       pcfg->cpu = -1;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(dev, "Can't get %s IRQ'\n", node->full_name);
+               return -ENXIO;
+       }
+       pcfg->irq = irq;
+
+       va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
+       if (!va)
+               goto err_ioremap1;
+
+       pcfg->addr_virt[DPAA_PORTAL_CE] = va;
+
+       va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
+                         _PAGE_GUARDED | _PAGE_NO_CACHE);
+       if (!va)
+               goto err_ioremap2;
+
+       pcfg->addr_virt[DPAA_PORTAL_CI] = va;
+
+       spin_lock(&bman_lock);
+       cpu = cpumask_next_zero(-1, &portal_cpus);
+       if (cpu >= nr_cpu_ids) {
+               /* unassigned portal, skip init */
+               spin_unlock(&bman_lock);
+               return 0;
+       }
+
+       cpumask_set_cpu(cpu, &portal_cpus);
+       spin_unlock(&bman_lock);
+       pcfg->cpu = cpu;
+
+       if (!init_pcfg(pcfg))
+               goto err_ioremap2;
+
+       /* clear irq affinity if assigned cpu is offline */
+       if (!cpu_online(cpu))
+               bman_offline_cpu(cpu);
+
+       return 0;
+
+err_ioremap2:
+       iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
+err_ioremap1:
+       dev_err(dev, "ioremap failed\n");
+       return -ENXIO;
+}
+
+static const struct of_device_id bman_portal_ids[] = {
+       {
+               .compatible = "fsl,bman-portal",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, bman_portal_ids);
+
+static struct platform_driver bman_portal_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = bman_portal_ids,
+       },
+       .probe = bman_portal_probe,
+};
+
+static int __init bman_portal_driver_register(struct platform_driver *drv)
+{
+       int ret;
+
+       ret = platform_driver_register(drv);
+       if (ret < 0)
+               return ret;
+
+       register_hotcpu_notifier(&bman_hotplug_cpu_notifier);
+
+       return 0;
+}
+
+module_driver(bman_portal_driver,
+             bman_portal_driver_register, platform_driver_unregister);
diff --git a/drivers/soc/fsl/qbman/bman_priv.h b/drivers/soc/fsl/qbman/bman_priv.h
new file mode 100644 (file)
index 0000000..f6896a2
--- /dev/null
@@ -0,0 +1,80 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "dpaa_sys.h"
+
+#include <soc/fsl/bman.h>
+
+/* Portal processing (interrupt) sources */
+#define BM_PIRQ_RCRI   0x00000002      /* RCR Ring (below threshold) */
+
+/* Revision info (for errata and feature handling) */
+#define BMAN_REV10 0x0100
+#define BMAN_REV20 0x0200
+#define BMAN_REV21 0x0201
+extern u16 bman_ip_rev;        /* 0 if uninitialised, otherwise BMAN_REVx */
+
+extern struct gen_pool *bm_bpalloc;
+
+struct bm_portal_config {
+       /*
+        * Corenet portal addresses;
+        * [0]==cache-enabled, [1]==cache-inhibited.
+        */
+       void __iomem *addr_virt[2];
+       /* Allow these to be joined in lists */
+       struct list_head list;
+       struct device *dev;
+       /* User-visible portal configuration settings */
+       /* portal is affined to this cpu */
+       int cpu;
+       /* portal interrupt line */
+       int irq;
+};
+
+struct bman_portal *bman_create_affine_portal(
+                       const struct bm_portal_config *config);
+/*
+ * The below bman_p_***() variant might be called in a situation that the cpu
+ * which the portal affine to is not online yet.
+ * @bman_portal specifies which portal the API will use.
+ */
+int bman_p_irqsource_add(struct bman_portal *p, u32 bits);
+
+/*
+ * Used by all portal interrupt registers except 'inhibit'
+ * This mask contains all the "irqsource" bits visible to API users
+ */
+#define BM_PIRQ_VISIBLE        BM_PIRQ_RCRI
+
+const struct bm_portal_config *
+bman_get_bm_portal_config(const struct bman_portal *portal);
diff --git a/drivers/soc/fsl/qbman/bman_test.c b/drivers/soc/fsl/qbman/bman_test.c
new file mode 100644 (file)
index 0000000..09b1c96
--- /dev/null
@@ -0,0 +1,53 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_test.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("BMan testing");
+
+static int test_init(void)
+{
+#ifdef CONFIG_FSL_BMAN_TEST_API
+       int loop = 1;
+
+       while (loop--)
+               bman_test_api();
+#endif
+       return 0;
+}
+
+static void test_exit(void)
+{
+}
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/drivers/soc/fsl/qbman/bman_test.h b/drivers/soc/fsl/qbman/bman_test.h
new file mode 100644 (file)
index 0000000..037ed34
--- /dev/null
@@ -0,0 +1,35 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_priv.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+void bman_test_api(void);
diff --git a/drivers/soc/fsl/qbman/bman_test_api.c b/drivers/soc/fsl/qbman/bman_test_api.c
new file mode 100644 (file)
index 0000000..6f6bdd1
--- /dev/null
@@ -0,0 +1,151 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bman_test.h"
+
+#define NUM_BUFS       93
+#define LOOPS          3
+#define BMAN_TOKEN_MASK 0x00FFFFFFFFFFLLU
+
+static struct bman_pool *pool;
+static struct bm_buffer bufs_in[NUM_BUFS] ____cacheline_aligned;
+static struct bm_buffer bufs_out[NUM_BUFS] ____cacheline_aligned;
+static int bufs_received;
+
+static void bufs_init(void)
+{
+       int i;
+
+       for (i = 0; i < NUM_BUFS; i++)
+               bm_buffer_set64(&bufs_in[i], 0xfedc01234567LLU * i);
+       bufs_received = 0;
+}
+
+static inline int bufs_cmp(const struct bm_buffer *a, const struct bm_buffer *b)
+{
+       if (bman_ip_rev == BMAN_REV20 || bman_ip_rev == BMAN_REV21) {
+
+               /*
+                * On SoCs with BMan revison 2.0, BMan only respects the 40
+                * LS-bits of buffer addresses, masking off the upper 8-bits on
+                * release commands. The API provides for 48-bit addresses
+                * because some SoCs support all 48-bits. When generating
+                * garbage addresses for testing, we either need to zero the
+                * upper 8-bits when releasing to BMan (otherwise we'll be
+                * disappointed when the buffers we acquire back from BMan
+                * don't match), or we need to mask the upper 8-bits off when
+                * comparing. We do the latter.
+                */
+               if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) <
+                   (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
+                       return -1;
+               if ((bm_buffer_get64(a) & BMAN_TOKEN_MASK) >
+                   (bm_buffer_get64(b) & BMAN_TOKEN_MASK))
+                       return 1;
+       } else {
+               if (bm_buffer_get64(a) < bm_buffer_get64(b))
+                       return -1;
+               if (bm_buffer_get64(a) > bm_buffer_get64(b))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static void bufs_confirm(void)
+{
+       int i, j;
+
+       for (i = 0; i < NUM_BUFS; i++) {
+               int matches = 0;
+
+               for (j = 0; j < NUM_BUFS; j++)
+                       if (!bufs_cmp(&bufs_in[i], &bufs_out[j]))
+                               matches++;
+               WARN_ON(matches != 1);
+       }
+}
+
+/* test */
+void bman_test_api(void)
+{
+       int i, loops = LOOPS;
+
+       bufs_init();
+
+       pr_info("%s(): Starting\n", __func__);
+
+       pool = bman_new_pool();
+       if (!pool) {
+               pr_crit("bman_new_pool() failed\n");
+               goto failed;
+       }
+
+       /* Release buffers */
+do_loop:
+       i = 0;
+       while (i < NUM_BUFS) {
+               int num = 8;
+
+               if (i + num > NUM_BUFS)
+                       num = NUM_BUFS - i;
+               if (bman_release(pool, bufs_in + i, num)) {
+                       pr_crit("bman_release() failed\n");
+                       goto failed;
+               }
+               i += num;
+       }
+
+       /* Acquire buffers */
+       while (i > 0) {
+               int tmp, num = 8;
+
+               if (num > i)
+                       num = i;
+               tmp = bman_acquire(pool, bufs_out + i - num, num);
+               WARN_ON(tmp != num);
+               i -= num;
+       }
+       i = bman_acquire(pool, NULL, 1);
+       WARN_ON(i > 0);
+
+       bufs_confirm();
+
+       if (--loops)
+               goto do_loop;
+
+       /* Clean up */
+       bman_free_pool(pool);
+       pr_info("%s(): Finished\n", __func__);
+       return;
+
+failed:
+       WARN_ON(1);
+}
diff --git a/drivers/soc/fsl/qbman/dpaa_sys.h b/drivers/soc/fsl/qbman/dpaa_sys.h
new file mode 100644 (file)
index 0000000..b63fd72
--- /dev/null
@@ -0,0 +1,103 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __DPAA_SYS_H
+#define __DPAA_SYS_H
+
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/platform_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/prefetch.h>
+#include <linux/genalloc.h>
+#include <asm/cacheflush.h>
+
+/* For 2-element tables related to cache-inhibited and cache-enabled mappings */
+#define DPAA_PORTAL_CE 0
+#define DPAA_PORTAL_CI 1
+
+#if (L1_CACHE_BYTES != 32) && (L1_CACHE_BYTES != 64)
+#error "Unsupported Cacheline Size"
+#endif
+
+static inline void dpaa_flush(void *p)
+{
+#ifdef CONFIG_PPC
+       flush_dcache_range((unsigned long)p, (unsigned long)p+64);
+#elif defined(CONFIG_ARM32)
+       __cpuc_flush_dcache_area(p, 64);
+#elif defined(CONFIG_ARM64)
+       __flush_dcache_area(p, 64);
+#endif
+}
+
+#define dpaa_invalidate(p) dpaa_flush(p)
+
+#define dpaa_zero(p) memset(p, 0, 64)
+
+static inline void dpaa_touch_ro(void *p)
+{
+#if (L1_CACHE_BYTES == 32)
+       prefetch(p+32);
+#endif
+       prefetch(p);
+}
+
+/* Commonly used combo */
+static inline void dpaa_invalidate_touch_ro(void *p)
+{
+       dpaa_invalidate(p);
+       dpaa_touch_ro(p);
+}
+
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+#define DPAA_ASSERT(x) WARN_ON(!(x))
+#else
+#define DPAA_ASSERT(x)
+#endif
+
+/* cyclic helper for rings */
+static inline u8 dpaa_cyc_diff(u8 ringsize, u8 first, u8 last)
+{
+       /* 'first' is included, 'last' is excluded */
+       if (first <= last)
+               return last - first;
+       return ringsize + last - first;
+}
+
+/* Offset applied to genalloc pools due to zero being an error return */
+#define DPAA_GENALLOC_OFF      0x80000000
+
+#endif /* __DPAA_SYS_H */
diff --git a/drivers/soc/fsl/qbman/qman.c b/drivers/soc/fsl/qbman/qman.c
new file mode 100644 (file)
index 0000000..119054b
--- /dev/null
@@ -0,0 +1,2881 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_priv.h"
+
+#define DQRR_MAXFILL   15
+#define EQCR_ITHRESH   4       /* if EQCR congests, interrupt threshold */
+#define IRQNAME                "QMan portal %d"
+#define MAX_IRQNAME    16      /* big enough for "QMan portal %d" */
+#define QMAN_POLL_LIMIT 32
+#define QMAN_PIRQ_DQRR_ITHRESH 12
+#define QMAN_PIRQ_MR_ITHRESH 4
+#define QMAN_PIRQ_IPERIOD 100
+
+/* Portal register assists */
+
+/* Cache-inhibited register offsets */
+#define QM_REG_EQCR_PI_CINH    0x0000
+#define QM_REG_EQCR_CI_CINH    0x0004
+#define QM_REG_EQCR_ITR                0x0008
+#define QM_REG_DQRR_PI_CINH    0x0040
+#define QM_REG_DQRR_CI_CINH    0x0044
+#define QM_REG_DQRR_ITR                0x0048
+#define QM_REG_DQRR_DCAP       0x0050
+#define QM_REG_DQRR_SDQCR      0x0054
+#define QM_REG_DQRR_VDQCR      0x0058
+#define QM_REG_DQRR_PDQCR      0x005c
+#define QM_REG_MR_PI_CINH      0x0080
+#define QM_REG_MR_CI_CINH      0x0084
+#define QM_REG_MR_ITR          0x0088
+#define QM_REG_CFG             0x0100
+#define QM_REG_ISR             0x0e00
+#define QM_REG_IER             0x0e04
+#define QM_REG_ISDR            0x0e08
+#define QM_REG_IIR             0x0e0c
+#define QM_REG_ITPR            0x0e14
+
+/* Cache-enabled register offsets */
+#define QM_CL_EQCR             0x0000
+#define QM_CL_DQRR             0x1000
+#define QM_CL_MR               0x2000
+#define QM_CL_EQCR_PI_CENA     0x3000
+#define QM_CL_EQCR_CI_CENA     0x3100
+#define QM_CL_DQRR_PI_CENA     0x3200
+#define QM_CL_DQRR_CI_CENA     0x3300
+#define QM_CL_MR_PI_CENA       0x3400
+#define QM_CL_MR_CI_CENA       0x3500
+#define QM_CL_CR               0x3800
+#define QM_CL_RR0              0x3900
+#define QM_CL_RR1              0x3940
+
+/*
+ * BTW, the drivers (and h/w programming model) already obtain the required
+ * synchronisation for portal accesses and data-dependencies. Use of barrier()s
+ * or other order-preserving primitives simply degrade performance. Hence the
+ * use of the __raw_*() interfaces, which simply ensure that the compiler treats
+ * the portal registers as volatile
+ */
+
+/* Cache-enabled ring access */
+#define qm_cl(base, idx)       ((void *)base + ((idx) << 6))
+
+/*
+ * Portal modes.
+ *   Enum types;
+ *     pmode == production mode
+ *     cmode == consumption mode,
+ *     dmode == h/w dequeue mode.
+ *   Enum values use 3 letter codes. First letter matches the portal mode,
+ *   remaining two letters indicate;
+ *     ci == cache-inhibited portal register
+ *     ce == cache-enabled portal register
+ *     vb == in-band valid-bit (cache-enabled)
+ *     dc == DCA (Discrete Consumption Acknowledgment), DQRR-only
+ *   As for "enum qm_dqrr_dmode", it should be self-explanatory.
+ */
+enum qm_eqcr_pmode {           /* matches QCSP_CFG::EPM */
+       qm_eqcr_pci = 0,        /* PI index, cache-inhibited */
+       qm_eqcr_pce = 1,        /* PI index, cache-enabled */
+       qm_eqcr_pvb = 2         /* valid-bit */
+};
+enum qm_dqrr_dmode {           /* matches QCSP_CFG::DP */
+       qm_dqrr_dpush = 0,      /* SDQCR  + VDQCR */
+       qm_dqrr_dpull = 1       /* PDQCR */
+};
+enum qm_dqrr_pmode {           /* s/w-only */
+       qm_dqrr_pci,            /* reads DQRR_PI_CINH */
+       qm_dqrr_pce,            /* reads DQRR_PI_CENA */
+       qm_dqrr_pvb             /* reads valid-bit */
+};
+enum qm_dqrr_cmode {           /* matches QCSP_CFG::DCM */
+       qm_dqrr_cci = 0,        /* CI index, cache-inhibited */
+       qm_dqrr_cce = 1,        /* CI index, cache-enabled */
+       qm_dqrr_cdc = 2         /* Discrete Consumption Acknowledgment */
+};
+enum qm_mr_pmode {             /* s/w-only */
+       qm_mr_pci,              /* reads MR_PI_CINH */
+       qm_mr_pce,              /* reads MR_PI_CENA */
+       qm_mr_pvb               /* reads valid-bit */
+};
+enum qm_mr_cmode {             /* matches QCSP_CFG::MM */
+       qm_mr_cci = 0,          /* CI index, cache-inhibited */
+       qm_mr_cce = 1           /* CI index, cache-enabled */
+};
+
+/* --- Portal structures --- */
+
+#define QM_EQCR_SIZE           8
+#define QM_DQRR_SIZE           16
+#define QM_MR_SIZE             8
+
+/* "Enqueue Command" */
+struct qm_eqcr_entry {
+       u8 _ncw_verb; /* writes to this are non-coherent */
+       u8 dca;
+       u16 seqnum;
+       u32 orp;        /* 24-bit */
+       u32 fqid;       /* 24-bit */
+       u32 tag;
+       struct qm_fd fd;
+       u8 __reserved3[32];
+} __packed;
+#define QM_EQCR_VERB_VBIT              0x80
+#define QM_EQCR_VERB_CMD_MASK          0x61    /* but only one value; */
+#define QM_EQCR_VERB_CMD_ENQUEUE       0x01
+#define QM_EQCR_SEQNUM_NESN            0x8000  /* Advance NESN */
+#define QM_EQCR_SEQNUM_NLIS            0x4000  /* More fragments to come */
+#define QM_EQCR_SEQNUM_SEQMASK         0x3fff  /* sequence number goes here */
+
+struct qm_eqcr {
+       struct qm_eqcr_entry *ring, *cursor;
+       u8 ci, available, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       u32 busy;
+       enum qm_eqcr_pmode pmode;
+#endif
+};
+
+struct qm_dqrr {
+       const struct qm_dqrr_entry *ring, *cursor;
+       u8 pi, ci, fill, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum qm_dqrr_dmode dmode;
+       enum qm_dqrr_pmode pmode;
+       enum qm_dqrr_cmode cmode;
+#endif
+};
+
+struct qm_mr {
+       union qm_mr_entry *ring, *cursor;
+       u8 pi, ci, fill, ithresh, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum qm_mr_pmode pmode;
+       enum qm_mr_cmode cmode;
+#endif
+};
+
+/* MC (Management Command) command */
+/* "Query FQ" */
+struct qm_mcc_queryfq {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2[56];
+} __packed;
+/* "Alter FQ State Commands " */
+struct qm_mcc_alterfq {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2;
+       u8 count;       /* number of consecutive FQID */
+       u8 __reserved3[10];
+       u32 context_b;  /* frame queue context b */
+       u8 __reserved4[40];
+} __packed;
+
+/* "Query CGR" */
+struct qm_mcc_querycgr {
+       u8 _ncw_verb;
+       u8 __reserved1[30];
+       u8 cgid;
+       u8 __reserved2[32];
+};
+
+struct qm_mcc_querywq {
+       u8 _ncw_verb;
+       u8 __reserved;
+       /* select channel if verb != QUERYWQ_DEDICATED */
+       u16 channel_wq; /* ignores wq (3 lsbits): _res[0-2] */
+       u8 __reserved2[60];
+} __packed;
+
+#define QM_MCC_VERB_VBIT               0x80
+#define QM_MCC_VERB_MASK               0x7f    /* where the verb contains; */
+#define QM_MCC_VERB_INITFQ_PARKED      0x40
+#define QM_MCC_VERB_INITFQ_SCHED       0x41
+#define QM_MCC_VERB_QUERYFQ            0x44
+#define QM_MCC_VERB_QUERYFQ_NP         0x45    /* "non-programmable" fields */
+#define QM_MCC_VERB_QUERYWQ            0x46
+#define QM_MCC_VERB_QUERYWQ_DEDICATED  0x47
+#define QM_MCC_VERB_ALTER_SCHED                0x48    /* Schedule FQ */
+#define QM_MCC_VERB_ALTER_FE           0x49    /* Force Eligible FQ */
+#define QM_MCC_VERB_ALTER_RETIRE       0x4a    /* Retire FQ */
+#define QM_MCC_VERB_ALTER_OOS          0x4b    /* Take FQ out of service */
+#define QM_MCC_VERB_ALTER_FQXON                0x4d    /* FQ XON */
+#define QM_MCC_VERB_ALTER_FQXOFF       0x4e    /* FQ XOFF */
+#define QM_MCC_VERB_INITCGR            0x50
+#define QM_MCC_VERB_MODIFYCGR          0x51
+#define QM_MCC_VERB_CGRTESTWRITE       0x52
+#define QM_MCC_VERB_QUERYCGR           0x58
+#define QM_MCC_VERB_QUERYCONGESTION    0x59
+union qm_mc_command {
+       struct {
+               u8 _ncw_verb; /* writes to this are non-coherent */
+               u8 __reserved[63];
+       };
+       struct qm_mcc_initfq initfq;
+       struct qm_mcc_queryfq queryfq;
+       struct qm_mcc_alterfq alterfq;
+       struct qm_mcc_initcgr initcgr;
+       struct qm_mcc_querycgr querycgr;
+       struct qm_mcc_querywq querywq;
+       struct qm_mcc_queryfq_np queryfq_np;
+};
+
+/* MC (Management Command) result */
+/* "Query FQ" */
+struct qm_mcr_queryfq {
+       u8 verb;
+       u8 result;
+       u8 __reserved1[8];
+       struct qm_fqd fqd;      /* the FQD fields are here */
+       u8 __reserved2[30];
+} __packed;
+
+/* "Alter FQ State Commands" */
+struct qm_mcr_alterfq {
+       u8 verb;
+       u8 result;
+       u8 fqs;         /* Frame Queue Status */
+       u8 __reserved1[61];
+};
+#define QM_MCR_VERB_RRID               0x80
+#define QM_MCR_VERB_MASK               QM_MCC_VERB_MASK
+#define QM_MCR_VERB_INITFQ_PARKED      QM_MCC_VERB_INITFQ_PARKED
+#define QM_MCR_VERB_INITFQ_SCHED       QM_MCC_VERB_INITFQ_SCHED
+#define QM_MCR_VERB_QUERYFQ            QM_MCC_VERB_QUERYFQ
+#define QM_MCR_VERB_QUERYFQ_NP         QM_MCC_VERB_QUERYFQ_NP
+#define QM_MCR_VERB_QUERYWQ            QM_MCC_VERB_QUERYWQ
+#define QM_MCR_VERB_QUERYWQ_DEDICATED  QM_MCC_VERB_QUERYWQ_DEDICATED
+#define QM_MCR_VERB_ALTER_SCHED                QM_MCC_VERB_ALTER_SCHED
+#define QM_MCR_VERB_ALTER_FE           QM_MCC_VERB_ALTER_FE
+#define QM_MCR_VERB_ALTER_RETIRE       QM_MCC_VERB_ALTER_RETIRE
+#define QM_MCR_VERB_ALTER_OOS          QM_MCC_VERB_ALTER_OOS
+#define QM_MCR_RESULT_NULL             0x00
+#define QM_MCR_RESULT_OK               0xf0
+#define QM_MCR_RESULT_ERR_FQID         0xf1
+#define QM_MCR_RESULT_ERR_FQSTATE      0xf2
+#define QM_MCR_RESULT_ERR_NOTEMPTY     0xf3    /* OOS fails if FQ is !empty */
+#define QM_MCR_RESULT_ERR_BADCHANNEL   0xf4
+#define QM_MCR_RESULT_PENDING          0xf8
+#define QM_MCR_RESULT_ERR_BADCOMMAND   0xff
+#define QM_MCR_FQS_ORLPRESENT          0x02    /* ORL fragments to come */
+#define QM_MCR_FQS_NOTEMPTY            0x01    /* FQ has enqueued frames */
+#define QM_MCR_TIMEOUT                 10000   /* us */
+union qm_mc_result {
+       struct {
+               u8 verb;
+               u8 result;
+               u8 __reserved1[62];
+       };
+       struct qm_mcr_queryfq queryfq;
+       struct qm_mcr_alterfq alterfq;
+       struct qm_mcr_querycgr querycgr;
+       struct qm_mcr_querycongestion querycongestion;
+       struct qm_mcr_querywq querywq;
+       struct qm_mcr_queryfq_np queryfq_np;
+};
+
+struct qm_mc {
+       union qm_mc_command *cr;
+       union qm_mc_result *rr;
+       u8 rridx, vbit;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       enum {
+               /* Can be _mc_start()ed */
+               qman_mc_idle,
+               /* Can be _mc_commit()ed or _mc_abort()ed */
+               qman_mc_user,
+               /* Can only be _mc_retry()ed */
+               qman_mc_hw
+       } state;
+#endif
+};
+
+struct qm_addr {
+       void __iomem *ce;       /* cache-enabled */
+       void __iomem *ci;       /* cache-inhibited */
+};
+
+struct qm_portal {
+       /*
+        * In the non-CONFIG_FSL_DPAA_CHECKING case, the following stuff up to
+        * and including 'mc' fits within a cacheline (yay!). The 'config' part
+        * is setup-only, so isn't a cause for a concern. In other words, don't
+        * rearrange this structure on a whim, there be dragons ...
+        */
+       struct qm_addr addr;
+       struct qm_eqcr eqcr;
+       struct qm_dqrr dqrr;
+       struct qm_mr mr;
+       struct qm_mc mc;
+} ____cacheline_aligned;
+
+/* Cache-inhibited register access. */
+static inline u32 qm_in(struct qm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ci + offset);
+}
+
+static inline void qm_out(struct qm_portal *p, u32 offset, u32 val)
+{
+       __raw_writel(val, p->addr.ci + offset);
+}
+
+/* Cache Enabled Portal Access */
+static inline void qm_cl_invalidate(struct qm_portal *p, u32 offset)
+{
+       dpaa_invalidate(p->addr.ce + offset);
+}
+
+static inline void qm_cl_touch_ro(struct qm_portal *p, u32 offset)
+{
+       dpaa_touch_ro(p->addr.ce + offset);
+}
+
+static inline u32 qm_ce_in(struct qm_portal *p, u32 offset)
+{
+       return __raw_readl(p->addr.ce + offset);
+}
+
+/* --- EQCR API --- */
+
+#define EQCR_SHIFT     ilog2(sizeof(struct qm_eqcr_entry))
+#define EQCR_CARRY     (uintptr_t)(QM_EQCR_SIZE << EQCR_SHIFT)
+
+/* Bit-wise logic to wrap a ring pointer by clearing the "carry bit" */
+static struct qm_eqcr_entry *eqcr_carryclear(struct qm_eqcr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~EQCR_CARRY;
+
+       return (struct qm_eqcr_entry *)addr;
+}
+
+/* Bit-wise logic to convert a ring pointer to a ring index */
+static int eqcr_ptr2idx(struct qm_eqcr_entry *e)
+{
+       return ((uintptr_t)e >> EQCR_SHIFT) & (QM_EQCR_SIZE - 1);
+}
+
+/* Increment the 'cursor' ring pointer, taking 'vbit' into account */
+static inline void eqcr_inc(struct qm_eqcr *eqcr)
+{
+       /* increment to the next EQCR pointer and handle overflow and 'vbit' */
+       struct qm_eqcr_entry *partial = eqcr->cursor + 1;
+
+       eqcr->cursor = eqcr_carryclear(partial);
+       if (partial != eqcr->cursor)
+               eqcr->vbit ^= QM_EQCR_VERB_VBIT;
+}
+
+static inline int qm_eqcr_init(struct qm_portal *portal,
+                               enum qm_eqcr_pmode pmode,
+                               unsigned int eq_stash_thresh,
+                               int eq_stash_prio)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u32 cfg;
+       u8 pi;
+
+       eqcr->ring = portal->addr.ce + QM_CL_EQCR;
+       eqcr->ci = qm_in(portal, QM_REG_EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
+       qm_cl_invalidate(portal, QM_CL_EQCR_CI_CENA);
+       pi = qm_in(portal, QM_REG_EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
+       eqcr->cursor = eqcr->ring + pi;
+       eqcr->vbit = (qm_in(portal, QM_REG_EQCR_PI_CINH) & QM_EQCR_SIZE) ?
+                    QM_EQCR_VERB_VBIT : 0;
+       eqcr->available = QM_EQCR_SIZE - 1 -
+                         dpaa_cyc_diff(QM_EQCR_SIZE, eqcr->ci, pi);
+       eqcr->ithresh = qm_in(portal, QM_REG_EQCR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 0;
+       eqcr->pmode = pmode;
+#endif
+       cfg = (qm_in(portal, QM_REG_CFG) & 0x00ffffff) |
+             (eq_stash_thresh << 28) | /* QCSP_CFG: EST */
+             (eq_stash_prio << 26) | /* QCSP_CFG: EP */
+             ((pmode & 0x3) << 24); /* QCSP_CFG::EPM */
+       qm_out(portal, QM_REG_CFG, cfg);
+       return 0;
+}
+
+static inline unsigned int qm_eqcr_get_ci_stashing(struct qm_portal *portal)
+{
+       return (qm_in(portal, QM_REG_CFG) >> 28) & 0x7;
+}
+
+static inline void qm_eqcr_finish(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 pi = qm_in(portal, QM_REG_EQCR_PI_CINH) & (QM_EQCR_SIZE - 1);
+       u8 ci = qm_in(portal, QM_REG_EQCR_CI_CINH) & (QM_EQCR_SIZE - 1);
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (pi != eqcr_ptr2idx(eqcr->cursor))
+               pr_crit("losing uncommited EQCR entries\n");
+       if (ci != eqcr->ci)
+               pr_crit("missing existing EQCR completions\n");
+       if (eqcr->ci != eqcr_ptr2idx(eqcr->cursor))
+               pr_crit("EQCR destroyed unquiesced\n");
+}
+
+static inline struct qm_eqcr_entry *qm_eqcr_start_no_stash(struct qm_portal
+                                                                *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (!eqcr->available)
+               return NULL;
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 1;
+#endif
+       dpaa_zero(eqcr->cursor);
+       return eqcr->cursor;
+}
+
+static inline struct qm_eqcr_entry *qm_eqcr_start_stash(struct qm_portal
+                                                               *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 diff, old_ci;
+
+       DPAA_ASSERT(!eqcr->busy);
+       if (!eqcr->available) {
+               old_ci = eqcr->ci;
+               eqcr->ci = qm_ce_in(portal, QM_CL_EQCR_CI_CENA) &
+                          (QM_EQCR_SIZE - 1);
+               diff = dpaa_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
+               eqcr->available += diff;
+               if (!diff)
+                       return NULL;
+       }
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 1;
+#endif
+       dpaa_zero(eqcr->cursor);
+       return eqcr->cursor;
+}
+
+static inline void eqcr_commit_checks(struct qm_eqcr *eqcr)
+{
+       DPAA_ASSERT(eqcr->busy);
+       DPAA_ASSERT(eqcr->cursor->orp == (eqcr->cursor->orp & 0x00ffffff));
+       DPAA_ASSERT(eqcr->cursor->fqid == (eqcr->cursor->fqid & 0x00ffffff));
+       DPAA_ASSERT(eqcr->available >= 1);
+}
+
+static inline void qm_eqcr_pvb_commit(struct qm_portal *portal, u8 myverb)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       struct qm_eqcr_entry *eqcursor;
+
+       eqcr_commit_checks(eqcr);
+       DPAA_ASSERT(eqcr->pmode == qm_eqcr_pvb);
+       dma_wmb();
+       eqcursor = eqcr->cursor;
+       eqcursor->_ncw_verb = myverb | eqcr->vbit;
+       dpaa_flush(eqcursor);
+       eqcr_inc(eqcr);
+       eqcr->available--;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       eqcr->busy = 0;
+#endif
+}
+
+static inline void qm_eqcr_cce_prefetch(struct qm_portal *portal)
+{
+       qm_cl_touch_ro(portal, QM_CL_EQCR_CI_CENA);
+}
+
+static inline u8 qm_eqcr_cce_update(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+       u8 diff, old_ci = eqcr->ci;
+
+       eqcr->ci = qm_ce_in(portal, QM_CL_EQCR_CI_CENA) & (QM_EQCR_SIZE - 1);
+       qm_cl_invalidate(portal, QM_CL_EQCR_CI_CENA);
+       diff = dpaa_cyc_diff(QM_EQCR_SIZE, old_ci, eqcr->ci);
+       eqcr->available += diff;
+       return diff;
+}
+
+static inline void qm_eqcr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       eqcr->ithresh = ithresh;
+       qm_out(portal, QM_REG_EQCR_ITR, ithresh);
+}
+
+static inline u8 qm_eqcr_get_avail(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       return eqcr->available;
+}
+
+static inline u8 qm_eqcr_get_fill(struct qm_portal *portal)
+{
+       struct qm_eqcr *eqcr = &portal->eqcr;
+
+       return QM_EQCR_SIZE - 1 - eqcr->available;
+}
+
+/* --- DQRR API --- */
+
+#define DQRR_SHIFT     ilog2(sizeof(struct qm_dqrr_entry))
+#define DQRR_CARRY     (uintptr_t)(QM_DQRR_SIZE << DQRR_SHIFT)
+
+static const struct qm_dqrr_entry *dqrr_carryclear(
+                                       const struct qm_dqrr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~DQRR_CARRY;
+
+       return (const struct qm_dqrr_entry *)addr;
+}
+
+static inline int dqrr_ptr2idx(const struct qm_dqrr_entry *e)
+{
+       return ((uintptr_t)e >> DQRR_SHIFT) & (QM_DQRR_SIZE - 1);
+}
+
+static const struct qm_dqrr_entry *dqrr_inc(const struct qm_dqrr_entry *e)
+{
+       return dqrr_carryclear(e + 1);
+}
+
+static inline void qm_dqrr_set_maxfill(struct qm_portal *portal, u8 mf)
+{
+       qm_out(portal, QM_REG_CFG, (qm_in(portal, QM_REG_CFG) & 0xff0fffff) |
+                                  ((mf & (QM_DQRR_SIZE - 1)) << 20));
+}
+
+static inline int qm_dqrr_init(struct qm_portal *portal,
+                              const struct qm_portal_config *config,
+                              enum qm_dqrr_dmode dmode,
+                              enum qm_dqrr_pmode pmode,
+                              enum qm_dqrr_cmode cmode, u8 max_fill)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+       u32 cfg;
+
+       /* Make sure the DQRR will be idle when we enable */
+       qm_out(portal, QM_REG_DQRR_SDQCR, 0);
+       qm_out(portal, QM_REG_DQRR_VDQCR, 0);
+       qm_out(portal, QM_REG_DQRR_PDQCR, 0);
+       dqrr->ring = portal->addr.ce + QM_CL_DQRR;
+       dqrr->pi = qm_in(portal, QM_REG_DQRR_PI_CINH) & (QM_DQRR_SIZE - 1);
+       dqrr->ci = qm_in(portal, QM_REG_DQRR_CI_CINH) & (QM_DQRR_SIZE - 1);
+       dqrr->cursor = dqrr->ring + dqrr->ci;
+       dqrr->fill = dpaa_cyc_diff(QM_DQRR_SIZE, dqrr->ci, dqrr->pi);
+       dqrr->vbit = (qm_in(portal, QM_REG_DQRR_PI_CINH) & QM_DQRR_SIZE) ?
+                       QM_DQRR_VERB_VBIT : 0;
+       dqrr->ithresh = qm_in(portal, QM_REG_DQRR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       dqrr->dmode = dmode;
+       dqrr->pmode = pmode;
+       dqrr->cmode = cmode;
+#endif
+       /* Invalidate every ring entry before beginning */
+       for (cfg = 0; cfg < QM_DQRR_SIZE; cfg++)
+               dpaa_invalidate(qm_cl(dqrr->ring, cfg));
+       cfg = (qm_in(portal, QM_REG_CFG) & 0xff000f00) |
+               ((max_fill & (QM_DQRR_SIZE - 1)) << 20) | /* DQRR_MF */
+               ((dmode & 1) << 18) |                   /* DP */
+               ((cmode & 3) << 16) |                   /* DCM */
+               0xa0 |                                  /* RE+SE */
+               (0 ? 0x40 : 0) |                        /* Ignore RP */
+               (0 ? 0x10 : 0);                         /* Ignore SP */
+       qm_out(portal, QM_REG_CFG, cfg);
+       qm_dqrr_set_maxfill(portal, max_fill);
+       return 0;
+}
+
+static inline void qm_dqrr_finish(struct qm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       if (dqrr->cmode != qm_dqrr_cdc &&
+           dqrr->ci != dqrr_ptr2idx(dqrr->cursor))
+               pr_crit("Ignoring completed DQRR entries\n");
+#endif
+}
+
+static inline const struct qm_dqrr_entry *qm_dqrr_current(
+                                               struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       if (!dqrr->fill)
+               return NULL;
+       return dqrr->cursor;
+}
+
+static inline u8 qm_dqrr_next(struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+
+       DPAA_ASSERT(dqrr->fill);
+       dqrr->cursor = dqrr_inc(dqrr->cursor);
+       return --dqrr->fill;
+}
+
+static inline void qm_dqrr_pvb_update(struct qm_portal *portal)
+{
+       struct qm_dqrr *dqrr = &portal->dqrr;
+       struct qm_dqrr_entry *res = qm_cl(dqrr->ring, dqrr->pi);
+
+       DPAA_ASSERT(dqrr->pmode == qm_dqrr_pvb);
+#ifndef CONFIG_FSL_PAMU
+       /*
+        * If PAMU is not available we need to invalidate the cache.
+        * When PAMU is available the cache is updated by stash
+        */
+       dpaa_invalidate_touch_ro(res);
+#endif
+       /*
+        *  when accessing 'verb', use __raw_readb() to ensure that compiler
+        * inlining doesn't try to optimise out "excess reads".
+        */
+       if ((__raw_readb(&res->verb) & QM_DQRR_VERB_VBIT) == dqrr->vbit) {
+               dqrr->pi = (dqrr->pi + 1) & (QM_DQRR_SIZE - 1);
+               if (!dqrr->pi)
+                       dqrr->vbit ^= QM_DQRR_VERB_VBIT;
+               dqrr->fill++;
+       }
+}
+
+static inline void qm_dqrr_cdc_consume_1ptr(struct qm_portal *portal,
+                                       const struct qm_dqrr_entry *dq,
+                                       int park)
+{
+       __maybe_unused struct qm_dqrr *dqrr = &portal->dqrr;
+       int idx = dqrr_ptr2idx(dq);
+
+       DPAA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
+       DPAA_ASSERT((dqrr->ring + idx) == dq);
+       DPAA_ASSERT(idx < QM_DQRR_SIZE);
+       qm_out(portal, QM_REG_DQRR_DCAP, (0 << 8) | /* DQRR_DCAP::S */
+              ((park ? 1 : 0) << 6) |              /* DQRR_DCAP::PK */
+              idx);                                /* DQRR_DCAP::DCAP_CI */
+}
+
+static inline void qm_dqrr_cdc_consume_n(struct qm_portal *portal, u32 bitmask)
+{
+       __maybe_unused struct qm_dqrr *dqrr = &portal->dqrr;
+
+       DPAA_ASSERT(dqrr->cmode == qm_dqrr_cdc);
+       qm_out(portal, QM_REG_DQRR_DCAP, (1 << 8) | /* DQRR_DCAP::S */
+              (bitmask << 16));                    /* DQRR_DCAP::DCAP_CI */
+}
+
+static inline void qm_dqrr_sdqcr_set(struct qm_portal *portal, u32 sdqcr)
+{
+       qm_out(portal, QM_REG_DQRR_SDQCR, sdqcr);
+}
+
+static inline void qm_dqrr_vdqcr_set(struct qm_portal *portal, u32 vdqcr)
+{
+       qm_out(portal, QM_REG_DQRR_VDQCR, vdqcr);
+}
+
+static inline void qm_dqrr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       qm_out(portal, QM_REG_DQRR_ITR, ithresh);
+}
+
+/* --- MR API --- */
+
+#define MR_SHIFT       ilog2(sizeof(union qm_mr_entry))
+#define MR_CARRY       (uintptr_t)(QM_MR_SIZE << MR_SHIFT)
+
+static union qm_mr_entry *mr_carryclear(union qm_mr_entry *p)
+{
+       uintptr_t addr = (uintptr_t)p;
+
+       addr &= ~MR_CARRY;
+
+       return (union qm_mr_entry *)addr;
+}
+
+static inline int mr_ptr2idx(const union qm_mr_entry *e)
+{
+       return ((uintptr_t)e >> MR_SHIFT) & (QM_MR_SIZE - 1);
+}
+
+static inline union qm_mr_entry *mr_inc(union qm_mr_entry *e)
+{
+       return mr_carryclear(e + 1);
+}
+
+static inline int qm_mr_init(struct qm_portal *portal, enum qm_mr_pmode pmode,
+                            enum qm_mr_cmode cmode)
+{
+       struct qm_mr *mr = &portal->mr;
+       u32 cfg;
+
+       mr->ring = portal->addr.ce + QM_CL_MR;
+       mr->pi = qm_in(portal, QM_REG_MR_PI_CINH) & (QM_MR_SIZE - 1);
+       mr->ci = qm_in(portal, QM_REG_MR_CI_CINH) & (QM_MR_SIZE - 1);
+       mr->cursor = mr->ring + mr->ci;
+       mr->fill = dpaa_cyc_diff(QM_MR_SIZE, mr->ci, mr->pi);
+       mr->vbit = (qm_in(portal, QM_REG_MR_PI_CINH) & QM_MR_SIZE)
+               ? QM_MR_VERB_VBIT : 0;
+       mr->ithresh = qm_in(portal, QM_REG_MR_ITR);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mr->pmode = pmode;
+       mr->cmode = cmode;
+#endif
+       cfg = (qm_in(portal, QM_REG_CFG) & 0xfffff0ff) |
+             ((cmode & 1) << 8);       /* QCSP_CFG:MM */
+       qm_out(portal, QM_REG_CFG, cfg);
+       return 0;
+}
+
+static inline void qm_mr_finish(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       if (mr->ci != mr_ptr2idx(mr->cursor))
+               pr_crit("Ignoring completed MR entries\n");
+}
+
+static inline const union qm_mr_entry *qm_mr_current(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       if (!mr->fill)
+               return NULL;
+       return mr->cursor;
+}
+
+static inline int qm_mr_next(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->fill);
+       mr->cursor = mr_inc(mr->cursor);
+       return --mr->fill;
+}
+
+static inline void qm_mr_pvb_update(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+       union qm_mr_entry *res = qm_cl(mr->ring, mr->pi);
+
+       DPAA_ASSERT(mr->pmode == qm_mr_pvb);
+       /*
+        *  when accessing 'verb', use __raw_readb() to ensure that compiler
+        * inlining doesn't try to optimise out "excess reads".
+        */
+       if ((__raw_readb(&res->verb) & QM_MR_VERB_VBIT) == mr->vbit) {
+               mr->pi = (mr->pi + 1) & (QM_MR_SIZE - 1);
+               if (!mr->pi)
+                       mr->vbit ^= QM_MR_VERB_VBIT;
+               mr->fill++;
+               res = mr_inc(res);
+       }
+       dpaa_invalidate_touch_ro(res);
+}
+
+static inline void qm_mr_cci_consume(struct qm_portal *portal, u8 num)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->cmode == qm_mr_cci);
+       mr->ci = (mr->ci + num) & (QM_MR_SIZE - 1);
+       qm_out(portal, QM_REG_MR_CI_CINH, mr->ci);
+}
+
+static inline void qm_mr_cci_consume_to_current(struct qm_portal *portal)
+{
+       struct qm_mr *mr = &portal->mr;
+
+       DPAA_ASSERT(mr->cmode == qm_mr_cci);
+       mr->ci = mr_ptr2idx(mr->cursor);
+       qm_out(portal, QM_REG_MR_CI_CINH, mr->ci);
+}
+
+static inline void qm_mr_set_ithresh(struct qm_portal *portal, u8 ithresh)
+{
+       qm_out(portal, QM_REG_MR_ITR, ithresh);
+}
+
+/* --- Management command API --- */
+
+static inline int qm_mc_init(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+
+       mc->cr = portal->addr.ce + QM_CL_CR;
+       mc->rr = portal->addr.ce + QM_CL_RR0;
+       mc->rridx = (__raw_readb(&mc->cr->_ncw_verb) & QM_MCC_VERB_VBIT)
+                   ? 0 : 1;
+       mc->vbit = mc->rridx ? QM_MCC_VERB_VBIT : 0;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_idle;
+#endif
+       return 0;
+}
+
+static inline void qm_mc_finish(struct qm_portal *portal)
+{
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       struct qm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == qman_mc_idle);
+       if (mc->state != qman_mc_idle)
+               pr_crit("Losing incomplete MC command\n");
+#endif
+}
+
+static inline union qm_mc_command *qm_mc_start(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+
+       DPAA_ASSERT(mc->state == qman_mc_idle);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_user;
+#endif
+       dpaa_zero(mc->cr);
+       return mc->cr;
+}
+
+static inline void qm_mc_commit(struct qm_portal *portal, u8 myverb)
+{
+       struct qm_mc *mc = &portal->mc;
+       union qm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == qman_mc_user);
+       dma_wmb();
+       mc->cr->_ncw_verb = myverb | mc->vbit;
+       dpaa_flush(mc->cr);
+       dpaa_invalidate_touch_ro(rr);
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_hw;
+#endif
+}
+
+static inline union qm_mc_result *qm_mc_result(struct qm_portal *portal)
+{
+       struct qm_mc *mc = &portal->mc;
+       union qm_mc_result *rr = mc->rr + mc->rridx;
+
+       DPAA_ASSERT(mc->state == qman_mc_hw);
+       /*
+        *  The inactive response register's verb byte always returns zero until
+        * its command is submitted and completed. This includes the valid-bit,
+        * in case you were wondering...
+        */
+       if (!__raw_readb(&rr->verb)) {
+               dpaa_invalidate_touch_ro(rr);
+               return NULL;
+       }
+       mc->rridx ^= 1;
+       mc->vbit ^= QM_MCC_VERB_VBIT;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       mc->state = qman_mc_idle;
+#endif
+       return rr;
+}
+
+static inline int qm_mc_result_timeout(struct qm_portal *portal,
+                                      union qm_mc_result **mcr)
+{
+       int timeout = QM_MCR_TIMEOUT;
+
+       do {
+               *mcr = qm_mc_result(portal);
+               if (*mcr)
+                       break;
+               udelay(1);
+       } while (--timeout);
+
+       return timeout;
+}
+
+static inline void fq_set(struct qman_fq *fq, u32 mask)
+{
+       set_bits(mask, &fq->flags);
+}
+
+static inline void fq_clear(struct qman_fq *fq, u32 mask)
+{
+       clear_bits(mask, &fq->flags);
+}
+
+static inline int fq_isset(struct qman_fq *fq, u32 mask)
+{
+       return fq->flags & mask;
+}
+
+static inline int fq_isclear(struct qman_fq *fq, u32 mask)
+{
+       return !(fq->flags & mask);
+}
+
+struct qman_portal {
+       struct qm_portal p;
+       /* PORTAL_BITS_*** - dynamic, strictly internal */
+       unsigned long bits;
+       /* interrupt sources processed by portal_isr(), configurable */
+       unsigned long irq_sources;
+       u32 use_eqcr_ci_stashing;
+       /* only 1 volatile dequeue at a time */
+       struct qman_fq *vdqcr_owned;
+       u32 sdqcr;
+       /* probing time config params for cpu-affine portals */
+       const struct qm_portal_config *config;
+       /* needed for providing a non-NULL device to dma_map_***() */
+       struct platform_device *pdev;
+       /* 2-element array. cgrs[0] is mask, cgrs[1] is snapshot. */
+       struct qman_cgrs *cgrs;
+       /* linked-list of CSCN handlers. */
+       struct list_head cgr_cbs;
+       /* list lock */
+       spinlock_t cgr_lock;
+       struct work_struct congestion_work;
+       struct work_struct mr_work;
+       char irqname[MAX_IRQNAME];
+};
+
+static cpumask_t affine_mask;
+static DEFINE_SPINLOCK(affine_mask_lock);
+static u16 affine_channels[NR_CPUS];
+static DEFINE_PER_CPU(struct qman_portal, qman_affine_portal);
+struct qman_portal *affine_portals[NR_CPUS];
+
+static inline struct qman_portal *get_affine_portal(void)
+{
+       return &get_cpu_var(qman_affine_portal);
+}
+
+static inline void put_affine_portal(void)
+{
+       put_cpu_var(qman_affine_portal);
+}
+
+static struct workqueue_struct *qm_portal_wq;
+
+int qman_wq_alloc(void)
+{
+       qm_portal_wq = alloc_workqueue("qman_portal_wq", 0, 1);
+       if (!qm_portal_wq)
+               return -ENOMEM;
+       return 0;
+}
+
+/*
+ * This is what everything can wait on, even if it migrates to a different cpu
+ * to the one whose affine portal it is waiting on.
+ */
+static DECLARE_WAIT_QUEUE_HEAD(affine_queue);
+
+static struct qman_fq **fq_table;
+static u32 num_fqids;
+
+int qman_alloc_fq_table(u32 _num_fqids)
+{
+       num_fqids = _num_fqids;
+
+       fq_table = vzalloc(num_fqids * 2 * sizeof(struct qman_fq *));
+       if (!fq_table)
+               return -ENOMEM;
+
+       pr_debug("Allocated fq lookup table at %p, entry count %u\n",
+                fq_table, num_fqids * 2);
+       return 0;
+}
+
+static struct qman_fq *idx_to_fq(u32 idx)
+{
+       struct qman_fq *fq;
+
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (WARN_ON(idx >= num_fqids * 2))
+               return NULL;
+#endif
+       fq = fq_table[idx];
+       DPAA_ASSERT(!fq || idx == fq->idx);
+
+       return fq;
+}
+
+/*
+ * Only returns full-service fq objects, not enqueue-only
+ * references (QMAN_FQ_FLAG_NO_MODIFY).
+ */
+static struct qman_fq *fqid_to_fq(u32 fqid)
+{
+       return idx_to_fq(fqid * 2);
+}
+
+static struct qman_fq *tag_to_fq(u32 tag)
+{
+#if BITS_PER_LONG == 64
+       return idx_to_fq(tag);
+#else
+       return (struct qman_fq *)tag;
+#endif
+}
+
+static u32 fq_to_tag(struct qman_fq *fq)
+{
+#if BITS_PER_LONG == 64
+       return fq->idx;
+#else
+       return (u32)fq;
+#endif
+}
+
+static u32 __poll_portal_slow(struct qman_portal *p, u32 is);
+static inline unsigned int __poll_portal_fast(struct qman_portal *p,
+                                       unsigned int poll_limit);
+static void qm_congestion_task(struct work_struct *work);
+static void qm_mr_process_task(struct work_struct *work);
+
+static irqreturn_t portal_isr(int irq, void *ptr)
+{
+       struct qman_portal *p = ptr;
+
+       u32 clear = QM_DQAVAIL_MASK | p->irq_sources;
+       u32 is = qm_in(&p->p, QM_REG_ISR) & p->irq_sources;
+
+       if (unlikely(!is))
+               return IRQ_NONE;
+
+       /* DQRR-handling if it's interrupt-driven */
+       if (is & QM_PIRQ_DQRI)
+               __poll_portal_fast(p, QMAN_POLL_LIMIT);
+       /* Handling of anything else that's interrupt-driven */
+       clear |= __poll_portal_slow(p, is);
+       qm_out(&p->p, QM_REG_ISR, clear);
+       return IRQ_HANDLED;
+}
+
+static int drain_mr_fqrni(struct qm_portal *p)
+{
+       const union qm_mr_entry *msg;
+loop:
+       msg = qm_mr_current(p);
+       if (!msg) {
+               /*
+                * if MR was full and h/w had other FQRNI entries to produce, we
+                * need to allow it time to produce those entries once the
+                * existing entries are consumed. A worst-case situation
+                * (fully-loaded system) means h/w sequencers may have to do 3-4
+                * other things before servicing the portal's MR pump, each of
+                * which (if slow) may take ~50 qman cycles (which is ~200
+                * processor cycles). So rounding up and then multiplying this
+                * worst-case estimate by a factor of 10, just to be
+                * ultra-paranoid, goes as high as 10,000 cycles. NB, we consume
+                * one entry at a time, so h/w has an opportunity to produce new
+                * entries well before the ring has been fully consumed, so
+                * we're being *really* paranoid here.
+                */
+               u64 now, then = jiffies;
+
+               do {
+                       now = jiffies;
+               } while ((then + 10000) > now);
+               msg = qm_mr_current(p);
+               if (!msg)
+                       return 0;
+       }
+       if ((msg->verb & QM_MR_VERB_TYPE_MASK) != QM_MR_VERB_FQRNI) {
+               /* We aren't draining anything but FQRNIs */
+               pr_err("Found verb 0x%x in MR\n", msg->verb);
+               return -1;
+       }
+       qm_mr_next(p);
+       qm_mr_cci_consume(p, 1);
+       goto loop;
+}
+
+static int qman_create_portal(struct qman_portal *portal,
+                             const struct qm_portal_config *c,
+                             const struct qman_cgrs *cgrs)
+{
+       struct qm_portal *p;
+       char buf[16];
+       int ret;
+       u32 isdr;
+
+       p = &portal->p;
+
+#ifdef CONFIG_FSL_PAMU
+       /* PAMU is required for stashing */
+       portal->use_eqcr_ci_stashing = ((qman_ip_rev >= QMAN_REV30) ? 1 : 0);
+#else
+       portal->use_eqcr_ci_stashing = 0;
+#endif
+       /*
+        * prep the low-level portal struct with the mapped addresses from the
+        * config, everything that follows depends on it and "config" is more
+        * for (de)reference
+        */
+       p->addr.ce = c->addr_virt[DPAA_PORTAL_CE];
+       p->addr.ci = c->addr_virt[DPAA_PORTAL_CI];
+       /*
+        * If CI-stashing is used, the current defaults use a threshold of 3,
+        * and stash with high-than-DQRR priority.
+        */
+       if (qm_eqcr_init(p, qm_eqcr_pvb,
+                       portal->use_eqcr_ci_stashing ? 3 : 0, 1)) {
+               dev_err(c->dev, "EQCR initialisation failed\n");
+               goto fail_eqcr;
+       }
+       if (qm_dqrr_init(p, c, qm_dqrr_dpush, qm_dqrr_pvb,
+                       qm_dqrr_cdc, DQRR_MAXFILL)) {
+               dev_err(c->dev, "DQRR initialisation failed\n");
+               goto fail_dqrr;
+       }
+       if (qm_mr_init(p, qm_mr_pvb, qm_mr_cci)) {
+               dev_err(c->dev, "MR initialisation failed\n");
+               goto fail_mr;
+       }
+       if (qm_mc_init(p)) {
+               dev_err(c->dev, "MC initialisation failed\n");
+               goto fail_mc;
+       }
+       /* static interrupt-gating controls */
+       qm_dqrr_set_ithresh(p, QMAN_PIRQ_DQRR_ITHRESH);
+       qm_mr_set_ithresh(p, QMAN_PIRQ_MR_ITHRESH);
+       qm_out(p, QM_REG_ITPR, QMAN_PIRQ_IPERIOD);
+       portal->cgrs = kmalloc(2 * sizeof(*cgrs), GFP_KERNEL);
+       if (!portal->cgrs)
+               goto fail_cgrs;
+       /* initial snapshot is no-depletion */
+       qman_cgrs_init(&portal->cgrs[1]);
+       if (cgrs)
+               portal->cgrs[0] = *cgrs;
+       else
+               /* if the given mask is NULL, assume all CGRs can be seen */
+               qman_cgrs_fill(&portal->cgrs[0]);
+       INIT_LIST_HEAD(&portal->cgr_cbs);
+       spin_lock_init(&portal->cgr_lock);
+       INIT_WORK(&portal->congestion_work, qm_congestion_task);
+       INIT_WORK(&portal->mr_work, qm_mr_process_task);
+       portal->bits = 0;
+       portal->sdqcr = QM_SDQCR_SOURCE_CHANNELS | QM_SDQCR_COUNT_UPTO3 |
+                       QM_SDQCR_DEDICATED_PRECEDENCE | QM_SDQCR_TYPE_PRIO_QOS |
+                       QM_SDQCR_TOKEN_SET(0xab) | QM_SDQCR_CHANNELS_DEDICATED;
+       sprintf(buf, "qportal-%d", c->channel);
+       portal->pdev = platform_device_alloc(buf, -1);
+       if (!portal->pdev)
+               goto fail_devalloc;
+       if (dma_set_mask(&portal->pdev->dev, DMA_BIT_MASK(40)))
+               goto fail_devadd;
+       ret = platform_device_add(portal->pdev);
+       if (ret)
+               goto fail_devadd;
+       isdr = 0xffffffff;
+       qm_out(p, QM_REG_ISDR, isdr);
+       portal->irq_sources = 0;
+       qm_out(p, QM_REG_IER, 0);
+       qm_out(p, QM_REG_ISR, 0xffffffff);
+       snprintf(portal->irqname, MAX_IRQNAME, IRQNAME, c->cpu);
+       if (request_irq(c->irq, portal_isr, 0, portal->irqname, portal)) {
+               dev_err(c->dev, "request_irq() failed\n");
+               goto fail_irq;
+       }
+       if (c->cpu != -1 && irq_can_set_affinity(c->irq) &&
+           irq_set_affinity(c->irq, cpumask_of(c->cpu))) {
+               dev_err(c->dev, "irq_set_affinity() failed\n");
+               goto fail_affinity;
+       }
+
+       /* Need EQCR to be empty before continuing */
+       isdr &= ~QM_PIRQ_EQCI;
+       qm_out(p, QM_REG_ISDR, isdr);
+       ret = qm_eqcr_get_fill(p);
+       if (ret) {
+               dev_err(c->dev, "EQCR unclean\n");
+               goto fail_eqcr_empty;
+       }
+       isdr &= ~(QM_PIRQ_DQRI | QM_PIRQ_MRI);
+       qm_out(p, QM_REG_ISDR, isdr);
+       if (qm_dqrr_current(p)) {
+               dev_err(c->dev, "DQRR unclean\n");
+               qm_dqrr_cdc_consume_n(p, 0xffff);
+       }
+       if (qm_mr_current(p) && drain_mr_fqrni(p)) {
+               /* special handling, drain just in case it's a few FQRNIs */
+               const union qm_mr_entry *e = qm_mr_current(p);
+
+               dev_err(c->dev, "MR dirty, VB 0x%x, rc 0x%x\n, addr 0x%x",
+                       e->verb, e->ern.rc, e->ern.fd.addr_lo);
+               goto fail_dqrr_mr_empty;
+       }
+       /* Success */
+       portal->config = c;
+       qm_out(p, QM_REG_ISDR, 0);
+       qm_out(p, QM_REG_IIR, 0);
+       /* Write a sane SDQCR */
+       qm_dqrr_sdqcr_set(p, portal->sdqcr);
+       return 0;
+
+fail_dqrr_mr_empty:
+fail_eqcr_empty:
+fail_affinity:
+       free_irq(c->irq, portal);
+fail_irq:
+       platform_device_del(portal->pdev);
+fail_devadd:
+       platform_device_put(portal->pdev);
+fail_devalloc:
+       kfree(portal->cgrs);
+fail_cgrs:
+       qm_mc_finish(p);
+fail_mc:
+       qm_mr_finish(p);
+fail_mr:
+       qm_dqrr_finish(p);
+fail_dqrr:
+       qm_eqcr_finish(p);
+fail_eqcr:
+       return -EIO;
+}
+
+struct qman_portal *qman_create_affine_portal(const struct qm_portal_config *c,
+                                             const struct qman_cgrs *cgrs)
+{
+       struct qman_portal *portal;
+       int err;
+
+       portal = &per_cpu(qman_affine_portal, c->cpu);
+       err = qman_create_portal(portal, c, cgrs);
+       if (err)
+               return NULL;
+
+       spin_lock(&affine_mask_lock);
+       cpumask_set_cpu(c->cpu, &affine_mask);
+       affine_channels[c->cpu] = c->channel;
+       affine_portals[c->cpu] = portal;
+       spin_unlock(&affine_mask_lock);
+
+       return portal;
+}
+
+static void qman_destroy_portal(struct qman_portal *qm)
+{
+       const struct qm_portal_config *pcfg;
+
+       /* Stop dequeues on the portal */
+       qm_dqrr_sdqcr_set(&qm->p, 0);
+
+       /*
+        * NB we do this to "quiesce" EQCR. If we add enqueue-completions or
+        * something related to QM_PIRQ_EQCI, this may need fixing.
+        * Also, due to the prefetching model used for CI updates in the enqueue
+        * path, this update will only invalidate the CI cacheline *after*
+        * working on it, so we need to call this twice to ensure a full update
+        * irrespective of where the enqueue processing was at when the teardown
+        * began.
+        */
+       qm_eqcr_cce_update(&qm->p);
+       qm_eqcr_cce_update(&qm->p);
+       pcfg = qm->config;
+
+       free_irq(pcfg->irq, qm);
+
+       kfree(qm->cgrs);
+       qm_mc_finish(&qm->p);
+       qm_mr_finish(&qm->p);
+       qm_dqrr_finish(&qm->p);
+       qm_eqcr_finish(&qm->p);
+
+       platform_device_del(qm->pdev);
+       platform_device_put(qm->pdev);
+
+       qm->config = NULL;
+}
+
+const struct qm_portal_config *qman_destroy_affine_portal(void)
+{
+       struct qman_portal *qm = get_affine_portal();
+       const struct qm_portal_config *pcfg;
+       int cpu;
+
+       pcfg = qm->config;
+       cpu = pcfg->cpu;
+
+       qman_destroy_portal(qm);
+
+       spin_lock(&affine_mask_lock);
+       cpumask_clear_cpu(cpu, &affine_mask);
+       spin_unlock(&affine_mask_lock);
+       put_affine_portal();
+       return pcfg;
+}
+
+/* Inline helper to reduce nesting in __poll_portal_slow() */
+static inline void fq_state_change(struct qman_portal *p, struct qman_fq *fq,
+                                  const union qm_mr_entry *msg, u8 verb)
+{
+       switch (verb) {
+       case QM_MR_VERB_FQRL:
+               DPAA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_ORL));
+               fq_clear(fq, QMAN_FQ_STATE_ORL);
+               break;
+       case QM_MR_VERB_FQRN:
+               DPAA_ASSERT(fq->state == qman_fq_state_parked ||
+                           fq->state == qman_fq_state_sched);
+               DPAA_ASSERT(fq_isset(fq, QMAN_FQ_STATE_CHANGING));
+               fq_clear(fq, QMAN_FQ_STATE_CHANGING);
+               if (msg->fq.fqs & QM_MR_FQS_NOTEMPTY)
+                       fq_set(fq, QMAN_FQ_STATE_NE);
+               if (msg->fq.fqs & QM_MR_FQS_ORLPRESENT)
+                       fq_set(fq, QMAN_FQ_STATE_ORL);
+               fq->state = qman_fq_state_retired;
+               break;
+       case QM_MR_VERB_FQPN:
+               DPAA_ASSERT(fq->state == qman_fq_state_sched);
+               DPAA_ASSERT(fq_isclear(fq, QMAN_FQ_STATE_CHANGING));
+               fq->state = qman_fq_state_parked;
+       }
+}
+
+static void qm_congestion_task(struct work_struct *work)
+{
+       struct qman_portal *p = container_of(work, struct qman_portal,
+                                            congestion_work);
+       struct qman_cgrs rr, c;
+       union qm_mc_result *mcr;
+       struct qman_cgr *cgr;
+
+       spin_lock(&p->cgr_lock);
+       qm_mc_start(&p->p);
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               spin_unlock(&p->cgr_lock);
+               dev_crit(p->config->dev, "QUERYCONGESTION timeout\n");
+               return;
+       }
+       /* mask out the ones I'm not interested in */
+       qman_cgrs_and(&rr, (struct qman_cgrs *)&mcr->querycongestion.state,
+                     &p->cgrs[0]);
+       /* check previous snapshot for delta, enter/exit congestion */
+       qman_cgrs_xor(&c, &rr, &p->cgrs[1]);
+       /* update snapshot */
+       qman_cgrs_cp(&p->cgrs[1], &rr);
+       /* Invoke callback */
+       list_for_each_entry(cgr, &p->cgr_cbs, node)
+               if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid))
+                       cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid));
+       spin_unlock(&p->cgr_lock);
+}
+
+static void qm_mr_process_task(struct work_struct *work)
+{
+       struct qman_portal *p = container_of(work, struct qman_portal,
+                                            mr_work);
+       const union qm_mr_entry *msg;
+       struct qman_fq *fq;
+       u8 verb, num = 0;
+
+       preempt_disable();
+
+       while (1) {
+               qm_mr_pvb_update(&p->p);
+               msg = qm_mr_current(&p->p);
+               if (!msg)
+                       break;
+
+               verb = msg->verb & QM_MR_VERB_TYPE_MASK;
+               /* The message is a software ERN iff the 0x20 bit is clear */
+               if (verb & 0x20) {
+                       switch (verb) {
+                       case QM_MR_VERB_FQRNI:
+                               /* nada, we drop FQRNIs on the floor */
+                               break;
+                       case QM_MR_VERB_FQRN:
+                       case QM_MR_VERB_FQRL:
+                               /* Lookup in the retirement table */
+                               fq = fqid_to_fq(msg->fq.fqid);
+                               if (WARN_ON(!fq))
+                                       break;
+                               fq_state_change(p, fq, msg, verb);
+                               if (fq->cb.fqs)
+                                       fq->cb.fqs(p, fq, msg);
+                               break;
+                       case QM_MR_VERB_FQPN:
+                               /* Parked */
+                               fq = tag_to_fq(msg->fq.contextB);
+                               fq_state_change(p, fq, msg, verb);
+                               if (fq->cb.fqs)
+                                       fq->cb.fqs(p, fq, msg);
+                               break;
+                       case QM_MR_VERB_DC_ERN:
+                               /* DCP ERN */
+                               pr_crit_once("Leaking DCP ERNs!\n");
+                               break;
+                       default:
+                               pr_crit("Invalid MR verb 0x%02x\n", verb);
+                       }
+               } else {
+                       /* Its a software ERN */
+                       fq = tag_to_fq(msg->ern.tag);
+                       fq->cb.ern(p, fq, msg);
+               }
+               num++;
+               qm_mr_next(&p->p);
+       }
+
+       qm_mr_cci_consume(&p->p, num);
+       preempt_enable();
+}
+
+static u32 __poll_portal_slow(struct qman_portal *p, u32 is)
+{
+       if (is & QM_PIRQ_CSCI) {
+               queue_work_on(smp_processor_id(), qm_portal_wq,
+                             &p->congestion_work);
+       }
+
+       if (is & QM_PIRQ_EQRI) {
+               qm_eqcr_cce_update(&p->p);
+               qm_eqcr_set_ithresh(&p->p, 0);
+               wake_up(&affine_queue);
+       }
+
+       if (is & QM_PIRQ_MRI) {
+               queue_work_on(smp_processor_id(), qm_portal_wq,
+                             &p->mr_work);
+       }
+
+       return is;
+}
+
+/*
+ * remove some slowish-path stuff from the "fast path" and make sure it isn't
+ * inlined.
+ */
+static noinline void clear_vdqcr(struct qman_portal *p, struct qman_fq *fq)
+{
+       p->vdqcr_owned = NULL;
+       fq_clear(fq, QMAN_FQ_STATE_VDQCR);
+       wake_up(&affine_queue);
+}
+
+/*
+ * The only states that would conflict with other things if they ran at the
+ * same time on the same cpu are:
+ *
+ *   (i) setting/clearing vdqcr_owned, and
+ *  (ii) clearing the NE (Not Empty) flag.
+ *
+ * Both are safe. Because;
+ *
+ *   (i) this clearing can only occur after qman_volatile_dequeue() has set the
+ *      vdqcr_owned field (which it does before setting VDQCR), and
+ *      qman_volatile_dequeue() blocks interrupts and preemption while this is
+ *      done so that we can't interfere.
+ *  (ii) the NE flag is only cleared after qman_retire_fq() has set it, and as
+ *      with (i) that API prevents us from interfering until it's safe.
+ *
+ * The good thing is that qman_volatile_dequeue() and qman_retire_fq() run far
+ * less frequently (ie. per-FQ) than __poll_portal_fast() does, so the nett
+ * advantage comes from this function not having to "lock" anything at all.
+ *
+ * Note also that the callbacks are invoked at points which are safe against the
+ * above potential conflicts, but that this function itself is not re-entrant
+ * (this is because the function tracks one end of each FIFO in the portal and
+ * we do *not* want to lock that). So the consequence is that it is safe for
+ * user callbacks to call into any QMan API.
+ */
+static inline unsigned int __poll_portal_fast(struct qman_portal *p,
+                                       unsigned int poll_limit)
+{
+       const struct qm_dqrr_entry *dq;
+       struct qman_fq *fq;
+       enum qman_cb_dqrr_result res;
+       unsigned int limit = 0;
+
+       do {
+               qm_dqrr_pvb_update(&p->p);
+               dq = qm_dqrr_current(&p->p);
+               if (!dq)
+                       break;
+
+               if (dq->stat & QM_DQRR_STAT_UNSCHEDULED) {
+                       /*
+                        * VDQCR: don't trust contextB as the FQ may have
+                        * been configured for h/w consumption and we're
+                        * draining it post-retirement.
+                        */
+                       fq = p->vdqcr_owned;
+                       /*
+                        * We only set QMAN_FQ_STATE_NE when retiring, so we
+                        * only need to check for clearing it when doing
+                        * volatile dequeues.  It's one less thing to check
+                        * in the critical path (SDQCR).
+                        */
+                       if (dq->stat & QM_DQRR_STAT_FQ_EMPTY)
+                               fq_clear(fq, QMAN_FQ_STATE_NE);
+                       /*
+                        * This is duplicated from the SDQCR code, but we
+                        * have stuff to do before *and* after this callback,
+                        * and we don't want multiple if()s in the critical
+                        * path (SDQCR).
+                        */
+                       res = fq->cb.dqrr(p, fq, dq);
+                       if (res == qman_cb_dqrr_stop)
+                               break;
+                       /* Check for VDQCR completion */
+                       if (dq->stat & QM_DQRR_STAT_DQCR_EXPIRED)
+                               clear_vdqcr(p, fq);
+               } else {
+                       /* SDQCR: contextB points to the FQ */
+                       fq = tag_to_fq(dq->contextB);
+                       /* Now let the callback do its stuff */
+                       res = fq->cb.dqrr(p, fq, dq);
+                       /*
+                        * The callback can request that we exit without
+                        * consuming this entry nor advancing;
+                        */
+                       if (res == qman_cb_dqrr_stop)
+                               break;
+               }
+               /* Interpret 'dq' from a driver perspective. */
+               /*
+                * Parking isn't possible unless HELDACTIVE was set. NB,
+                * FORCEELIGIBLE implies HELDACTIVE, so we only need to
+                * check for HELDACTIVE to cover both.
+                */
+               DPAA_ASSERT((dq->stat & QM_DQRR_STAT_FQ_HELDACTIVE) ||
+                           (res != qman_cb_dqrr_park));
+               /* just means "skip it, I'll consume it myself later on" */
+               if (res != qman_cb_dqrr_defer)
+                       qm_dqrr_cdc_consume_1ptr(&p->p, dq,
+                                                res == qman_cb_dqrr_park);
+               /* Move forward */
+               qm_dqrr_next(&p->p);
+               /*
+                * Entry processed and consumed, increment our counter.  The
+                * callback can request that we exit after consuming the
+                * entry, and we also exit if we reach our processing limit,
+                * so loop back only if neither of these conditions is met.
+                */
+       } while (++limit < poll_limit && res != qman_cb_dqrr_consume_stop);
+
+       return limit;
+}
+
+void qman_p_irqsource_add(struct qman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       set_bits(bits & QM_PIRQ_VISIBLE, &p->irq_sources);
+       qm_out(&p->p, QM_REG_IER, p->irq_sources);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_irqsource_add);
+
+void qman_p_irqsource_remove(struct qman_portal *p, u32 bits)
+{
+       unsigned long irqflags;
+       u32 ier;
+
+       /*
+        * Our interrupt handler only processes+clears status register bits that
+        * are in p->irq_sources. As we're trimming that mask, if one of them
+        * were to assert in the status register just before we remove it from
+        * the enable register, there would be an interrupt-storm when we
+        * release the IRQ lock. So we wait for the enable register update to
+        * take effect in h/w (by reading it back) and then clear all other bits
+        * in the status register. Ie. we clear them from ISR once it's certain
+        * IER won't allow them to reassert.
+        */
+       local_irq_save(irqflags);
+       bits &= QM_PIRQ_VISIBLE;
+       clear_bits(bits, &p->irq_sources);
+       qm_out(&p->p, QM_REG_IER, p->irq_sources);
+       ier = qm_in(&p->p, QM_REG_IER);
+       /*
+        * Using "~ier" (rather than "bits" or "~p->irq_sources") creates a
+        * data-dependency, ie. to protect against re-ordering.
+        */
+       qm_out(&p->p, QM_REG_ISR, ~ier);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_irqsource_remove);
+
+const cpumask_t *qman_affine_cpus(void)
+{
+       return &affine_mask;
+}
+EXPORT_SYMBOL(qman_affine_cpus);
+
+u16 qman_affine_channel(int cpu)
+{
+       if (cpu < 0) {
+               struct qman_portal *portal = get_affine_portal();
+
+               cpu = portal->config->cpu;
+               put_affine_portal();
+       }
+       WARN_ON(!cpumask_test_cpu(cpu, &affine_mask));
+       return affine_channels[cpu];
+}
+EXPORT_SYMBOL(qman_affine_channel);
+
+struct qman_portal *qman_get_affine_portal(int cpu)
+{
+       return affine_portals[cpu];
+}
+EXPORT_SYMBOL(qman_get_affine_portal);
+
+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit)
+{
+       return __poll_portal_fast(p, limit);
+}
+EXPORT_SYMBOL(qman_p_poll_dqrr);
+
+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools)
+{
+       unsigned long irqflags;
+
+       local_irq_save(irqflags);
+       pools &= p->config->pools;
+       p->sdqcr |= pools;
+       qm_dqrr_sdqcr_set(&p->p, p->sdqcr);
+       local_irq_restore(irqflags);
+}
+EXPORT_SYMBOL(qman_p_static_dequeue_add);
+
+/* Frame queue API */
+
+static const char *mcr_result_str(u8 result)
+{
+       switch (result) {
+       case QM_MCR_RESULT_NULL:
+               return "QM_MCR_RESULT_NULL";
+       case QM_MCR_RESULT_OK:
+               return "QM_MCR_RESULT_OK";
+       case QM_MCR_RESULT_ERR_FQID:
+               return "QM_MCR_RESULT_ERR_FQID";
+       case QM_MCR_RESULT_ERR_FQSTATE:
+               return "QM_MCR_RESULT_ERR_FQSTATE";
+       case QM_MCR_RESULT_ERR_NOTEMPTY:
+               return "QM_MCR_RESULT_ERR_NOTEMPTY";
+       case QM_MCR_RESULT_PENDING:
+               return "QM_MCR_RESULT_PENDING";
+       case QM_MCR_RESULT_ERR_BADCOMMAND:
+               return "QM_MCR_RESULT_ERR_BADCOMMAND";
+       }
+       return "<unknown MCR result>";
+}
+
+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq)
+{
+       if (flags & QMAN_FQ_FLAG_DYNAMIC_FQID) {
+               int ret = qman_alloc_fqid(&fqid);
+
+               if (ret)
+                       return ret;
+       }
+       fq->fqid = fqid;
+       fq->flags = flags;
+       fq->state = qman_fq_state_oos;
+       fq->cgr_groupid = 0;
+
+       /* A context_b of 0 is allegedly special, so don't use that fqid */
+       if (fqid == 0 || fqid >= num_fqids) {
+               WARN(1, "bad fqid %d\n", fqid);
+               return -EINVAL;
+       }
+
+       fq->idx = fqid * 2;
+       if (flags & QMAN_FQ_FLAG_NO_MODIFY)
+               fq->idx++;
+
+       WARN_ON(fq_table[fq->idx]);
+       fq_table[fq->idx] = fq;
+
+       return 0;
+}
+EXPORT_SYMBOL(qman_create_fq);
+
+void qman_destroy_fq(struct qman_fq *fq)
+{
+       /*
+        * We don't need to lock the FQ as it is a pre-condition that the FQ be
+        * quiesced. Instead, run some checks.
+        */
+       switch (fq->state) {
+       case qman_fq_state_parked:
+       case qman_fq_state_oos:
+               if (fq_isset(fq, QMAN_FQ_FLAG_DYNAMIC_FQID))
+                       qman_release_fqid(fq->fqid);
+
+               DPAA_ASSERT(fq_table[fq->idx]);
+               fq_table[fq->idx] = NULL;
+               return;
+       default:
+               break;
+       }
+       DPAA_ASSERT(NULL == "qman_free_fq() on unquiesced FQ!");
+}
+EXPORT_SYMBOL(qman_destroy_fq);
+
+u32 qman_fq_fqid(struct qman_fq *fq)
+{
+       return fq->fqid;
+}
+EXPORT_SYMBOL(qman_fq_fqid);
+
+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       u8 res, myverb;
+       int ret = 0;
+
+       myverb = (flags & QMAN_INITFQ_FLAG_SCHED)
+               ? QM_MCC_VERB_INITFQ_SCHED : QM_MCC_VERB_INITFQ_PARKED;
+
+       if (fq->state != qman_fq_state_oos &&
+           fq->state != qman_fq_state_parked)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       if (opts && (opts->we_mask & QM_INITFQ_WE_OAC)) {
+               /* And can't be set at the same time as TDTHRESH */
+               if (opts->we_mask & QM_INITFQ_WE_TDTHRESH)
+                       return -EINVAL;
+       }
+       /* Issue an INITFQ_[PARKED|SCHED] management command */
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           (fq->state != qman_fq_state_oos &&
+            fq->state != qman_fq_state_parked)) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       if (opts)
+               mcc->initfq = *opts;
+       mcc->initfq.fqid = fq->fqid;
+       mcc->initfq.count = 0;
+       /*
+        * If the FQ does *not* have the TO_DCPORTAL flag, contextB is set as a
+        * demux pointer. Otherwise, the caller-provided value is allowed to
+        * stand, don't overwrite it.
+        */
+       if (fq_isclear(fq, QMAN_FQ_FLAG_TO_DCPORTAL)) {
+               dma_addr_t phys_fq;
+
+               mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTB;
+               mcc->initfq.fqd.context_b = fq_to_tag(fq);
+               /*
+                *  and the physical address - NB, if the user wasn't trying to
+                * set CONTEXTA, clear the stashing settings.
+                */
+               if (!(mcc->initfq.we_mask & QM_INITFQ_WE_CONTEXTA)) {
+                       mcc->initfq.we_mask |= QM_INITFQ_WE_CONTEXTA;
+                       memset(&mcc->initfq.fqd.context_a, 0,
+                               sizeof(mcc->initfq.fqd.context_a));
+               } else {
+                       phys_fq = dma_map_single(&p->pdev->dev, fq, sizeof(*fq),
+                                                DMA_TO_DEVICE);
+                       qm_fqd_stashing_set64(&mcc->initfq.fqd, phys_fq);
+               }
+       }
+       if (flags & QMAN_INITFQ_FLAG_LOCAL) {
+               int wq = 0;
+
+               if (!(mcc->initfq.we_mask & QM_INITFQ_WE_DESTWQ)) {
+                       mcc->initfq.we_mask |= QM_INITFQ_WE_DESTWQ;
+                       wq = 4;
+               }
+               qm_fqd_set_destwq(&mcc->initfq.fqd, p->config->channel, wq);
+       }
+       qm_mc_commit(&p->p, myverb);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(p->config->dev, "MCR timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == myverb);
+       res = mcr->result;
+       if (res != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       if (opts) {
+               if (opts->we_mask & QM_INITFQ_WE_FQCTRL) {
+                       if (opts->fqd.fq_ctrl & QM_FQCTRL_CGE)
+                               fq_set(fq, QMAN_FQ_STATE_CGR_EN);
+                       else
+                               fq_clear(fq, QMAN_FQ_STATE_CGR_EN);
+               }
+               if (opts->we_mask & QM_INITFQ_WE_CGID)
+                       fq->cgr_groupid = opts->fqd.cgid;
+       }
+       fq->state = (flags & QMAN_INITFQ_FLAG_SCHED) ?
+               qman_fq_state_sched : qman_fq_state_parked;
+
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_init_fq);
+
+int qman_schedule_fq(struct qman_fq *fq)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret = 0;
+
+       if (fq->state != qman_fq_state_parked)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       /* Issue a ALTERFQ_SCHED management command */
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           fq->state != qman_fq_state_parked) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_SCHED);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(p->config->dev, "ALTER_SCHED timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_SCHED);
+       if (mcr->result != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       fq->state = qman_fq_state_sched;
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_schedule_fq);
+
+int qman_retire_fq(struct qman_fq *fq, u32 *flags)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret;
+       u8 res;
+
+       if (fq->state != qman_fq_state_parked &&
+           fq->state != qman_fq_state_sched)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_CHANGING) ||
+           fq->state == qman_fq_state_retired ||
+           fq->state == qman_fq_state_oos) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_crit(p->config->dev, "ALTER_RETIRE timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_RETIRE);
+       res = mcr->result;
+       /*
+        * "Elegant" would be to treat OK/PENDING the same way; set CHANGING,
+        * and defer the flags until FQRNI or FQRN (respectively) show up. But
+        * "Friendly" is to process OK immediately, and not set CHANGING. We do
+        * friendly, otherwise the caller doesn't necessarily have a fully
+        * "retired" FQ on return even if the retirement was immediate. However
+        * this does mean some code duplication between here and
+        * fq_state_change().
+        */
+       if (res == QM_MCR_RESULT_OK) {
+               ret = 0;
+               /* Process 'fq' right away, we'll ignore FQRNI */
+               if (mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY)
+                       fq_set(fq, QMAN_FQ_STATE_NE);
+               if (mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)
+                       fq_set(fq, QMAN_FQ_STATE_ORL);
+               if (flags)
+                       *flags = fq->flags;
+               fq->state = qman_fq_state_retired;
+               if (fq->cb.fqs) {
+                       /*
+                        * Another issue with supporting "immediate" retirement
+                        * is that we're forced to drop FQRNIs, because by the
+                        * time they're seen it may already be "too late" (the
+                        * fq may have been OOS'd and free()'d already). But if
+                        * the upper layer wants a callback whether it's
+                        * immediate or not, we have to fake a "MR" entry to
+                        * look like an FQRNI...
+                        */
+                       union qm_mr_entry msg;
+
+                       msg.verb = QM_MR_VERB_FQRNI;
+                       msg.fq.fqs = mcr->alterfq.fqs;
+                       msg.fq.fqid = fq->fqid;
+                       msg.fq.contextB = fq_to_tag(fq);
+                       fq->cb.fqs(p, fq, &msg);
+               }
+       } else if (res == QM_MCR_RESULT_PENDING) {
+               ret = 1;
+               fq_set(fq, QMAN_FQ_STATE_CHANGING);
+       } else {
+               ret = -EIO;
+       }
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_retire_fq);
+
+int qman_oos_fq(struct qman_fq *fq)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p;
+       int ret = 0;
+
+       if (fq->state != qman_fq_state_retired)
+               return -EINVAL;
+#ifdef CONFIG_FSL_DPAA_CHECKING
+       if (fq_isset(fq, QMAN_FQ_FLAG_NO_MODIFY))
+               return -EINVAL;
+#endif
+       p = get_affine_portal();
+       if (fq_isset(fq, QMAN_FQ_STATE_BLOCKOOS) ||
+           fq->state != qman_fq_state_retired) {
+               ret = -EBUSY;
+               goto out;
+       }
+       mcc = qm_mc_start(&p->p);
+       mcc->alterfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_ALTER_OOS);
+       if (mcr->result != QM_MCR_RESULT_OK) {
+               ret = -EIO;
+               goto out;
+       }
+       fq->state = qman_fq_state_oos;
+out:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_oos_fq);
+
+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *fqd = mcr->queryfq.fqd;
+       else
+               ret = -EIO;
+out:
+       put_affine_portal();
+       return ret;
+}
+
+static int qman_query_fq_np(struct qman_fq *fq,
+                           struct qm_mcr_queryfq_np *np)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fq->fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *np = mcr->queryfq_np;
+       else if (mcr->result == QM_MCR_RESULT_ERR_FQID)
+               ret = -ERANGE;
+       else
+               ret = -EIO;
+out:
+       put_affine_portal();
+       return ret;
+}
+
+static int qman_query_cgr(struct qman_cgr *cgr,
+                         struct qm_mcr_querycgr *cgrd)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       mcc->querycgr.cgid = cgr->cgrid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCGR);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCC_VERB_QUERYCGR);
+       if (mcr->result == QM_MCR_RESULT_OK)
+               *cgrd = mcr->querycgr;
+       else {
+               dev_err(p->config->dev, "QUERY_CGR failed: %s\n",
+                       mcr_result_str(mcr->result));
+               ret = -EIO;
+       }
+out:
+       put_affine_portal();
+       return ret;
+}
+
+int qman_query_cgr_congested(struct qman_cgr *cgr, bool *result)
+{
+       struct qm_mcr_querycgr query_cgr;
+       int err;
+
+       err = qman_query_cgr(cgr, &query_cgr);
+       if (err)
+               return err;
+
+       *result = !!query_cgr.cgr.cs;
+       return 0;
+}
+EXPORT_SYMBOL(qman_query_cgr_congested);
+
+/* internal function used as a wait_event() expression */
+static int set_p_vdqcr(struct qman_portal *p, struct qman_fq *fq, u32 vdqcr)
+{
+       unsigned long irqflags;
+       int ret = -EBUSY;
+
+       local_irq_save(irqflags);
+       if (p->vdqcr_owned)
+               goto out;
+       if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
+               goto out;
+
+       fq_set(fq, QMAN_FQ_STATE_VDQCR);
+       p->vdqcr_owned = fq;
+       qm_dqrr_vdqcr_set(&p->p, vdqcr);
+       ret = 0;
+out:
+       local_irq_restore(irqflags);
+       return ret;
+}
+
+static int set_vdqcr(struct qman_portal **p, struct qman_fq *fq, u32 vdqcr)
+{
+       int ret;
+
+       *p = get_affine_portal();
+       ret = set_p_vdqcr(*p, fq, vdqcr);
+       put_affine_portal();
+       return ret;
+}
+
+static int wait_vdqcr_start(struct qman_portal **p, struct qman_fq *fq,
+                               u32 vdqcr, u32 flags)
+{
+       int ret = 0;
+
+       if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
+               ret = wait_event_interruptible(affine_queue,
+                               !set_vdqcr(p, fq, vdqcr));
+       else
+               wait_event(affine_queue, !set_vdqcr(p, fq, vdqcr));
+       return ret;
+}
+
+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr)
+{
+       struct qman_portal *p;
+       int ret;
+
+       if (fq->state != qman_fq_state_parked &&
+           fq->state != qman_fq_state_retired)
+               return -EINVAL;
+       if (vdqcr & QM_VDQCR_FQID_MASK)
+               return -EINVAL;
+       if (fq_isset(fq, QMAN_FQ_STATE_VDQCR))
+               return -EBUSY;
+       vdqcr = (vdqcr & ~QM_VDQCR_FQID_MASK) | fq->fqid;
+       if (flags & QMAN_VOLATILE_FLAG_WAIT)
+               ret = wait_vdqcr_start(&p, fq, vdqcr, flags);
+       else
+               ret = set_vdqcr(&p, fq, vdqcr);
+       if (ret)
+               return ret;
+       /* VDQCR is set */
+       if (flags & QMAN_VOLATILE_FLAG_FINISH) {
+               if (flags & QMAN_VOLATILE_FLAG_WAIT_INT)
+                       /*
+                        * NB: don't propagate any error - the caller wouldn't
+                        * know whether the VDQCR was issued or not. A signal
+                        * could arrive after returning anyway, so the caller
+                        * can check signal_pending() if that's an issue.
+                        */
+                       wait_event_interruptible(affine_queue,
+                               !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
+               else
+                       wait_event(affine_queue,
+                               !fq_isset(fq, QMAN_FQ_STATE_VDQCR));
+       }
+       return 0;
+}
+EXPORT_SYMBOL(qman_volatile_dequeue);
+
+static void update_eqcr_ci(struct qman_portal *p, u8 avail)
+{
+       if (avail)
+               qm_eqcr_cce_prefetch(&p->p);
+       else
+               qm_eqcr_cce_update(&p->p);
+}
+
+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd)
+{
+       struct qman_portal *p;
+       struct qm_eqcr_entry *eq;
+       unsigned long irqflags;
+       u8 avail;
+
+       p = get_affine_portal();
+       local_irq_save(irqflags);
+
+       if (p->use_eqcr_ci_stashing) {
+               /*
+                * The stashing case is easy, only update if we need to in
+                * order to try and liberate ring entries.
+                */
+               eq = qm_eqcr_start_stash(&p->p);
+       } else {
+               /*
+                * The non-stashing case is harder, need to prefetch ahead of
+                * time.
+                */
+               avail = qm_eqcr_get_avail(&p->p);
+               if (avail < 2)
+                       update_eqcr_ci(p, avail);
+               eq = qm_eqcr_start_no_stash(&p->p);
+       }
+
+       if (unlikely(!eq))
+               goto out;
+
+       eq->fqid = fq->fqid;
+       eq->tag = fq_to_tag(fq);
+       eq->fd = *fd;
+
+       qm_eqcr_pvb_commit(&p->p, QM_EQCR_VERB_CMD_ENQUEUE);
+out:
+       local_irq_restore(irqflags);
+       put_affine_portal();
+       return 0;
+}
+EXPORT_SYMBOL(qman_enqueue);
+
+static int qm_modify_cgr(struct qman_cgr *cgr, u32 flags,
+                        struct qm_mcc_initcgr *opts)
+{
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       struct qman_portal *p = get_affine_portal();
+       u8 verb = QM_MCC_VERB_MODIFYCGR;
+       int ret = 0;
+
+       mcc = qm_mc_start(&p->p);
+       if (opts)
+               mcc->initcgr = *opts;
+       mcc->initcgr.cgid = cgr->cgrid;
+       if (flags & QMAN_CGR_FLAG_USE_INIT)
+               verb = QM_MCC_VERB_INITCGR;
+       qm_mc_commit(&p->p, verb);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == verb);
+       if (mcr->result != QM_MCR_RESULT_OK)
+               ret = -EIO;
+
+out:
+       put_affine_portal();
+       return ret;
+}
+
+#define PORTAL_IDX(n)  (n->config->channel - QM_CHANNEL_SWPORTAL0)
+#define TARG_MASK(n)   (BIT(31) >> PORTAL_IDX(n))
+
+static u8 qman_cgr_cpus[CGR_NUM];
+
+void qman_init_cgr_all(void)
+{
+       struct qman_cgr cgr;
+       int err_cnt = 0;
+
+       for (cgr.cgrid = 0; cgr.cgrid < CGR_NUM; cgr.cgrid++) {
+               if (qm_modify_cgr(&cgr, QMAN_CGR_FLAG_USE_INIT, NULL))
+                       err_cnt++;
+       }
+
+       if (err_cnt)
+               pr_err("Warning: %d error%s while initialising CGR h/w\n",
+                      err_cnt, (err_cnt > 1) ? "s" : "");
+}
+
+int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
+                   struct qm_mcc_initcgr *opts)
+{
+       struct qm_mcr_querycgr cgr_state;
+       struct qm_mcc_initcgr local_opts = {};
+       int ret;
+       struct qman_portal *p;
+
+       /*
+        * We have to check that the provided CGRID is within the limits of the
+        * data-structures, for obvious reasons. However we'll let h/w take
+        * care of determining whether it's within the limits of what exists on
+        * the SoC.
+        */
+       if (cgr->cgrid >= CGR_NUM)
+               return -EINVAL;
+
+       preempt_disable();
+       p = get_affine_portal();
+       qman_cgr_cpus[cgr->cgrid] = smp_processor_id();
+       preempt_enable();
+
+       cgr->chan = p->config->channel;
+       spin_lock(&p->cgr_lock);
+
+       if (opts) {
+               ret = qman_query_cgr(cgr, &cgr_state);
+               if (ret)
+                       goto out;
+               if (opts)
+                       local_opts = *opts;
+               if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+                       local_opts.cgr.cscn_targ_upd_ctrl =
+                               QM_CGR_TARG_UDP_CTRL_WRITE_BIT | PORTAL_IDX(p);
+               else
+                       /* Overwrite TARG */
+                       local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ |
+                                                  TARG_MASK(p);
+               local_opts.we_mask |= QM_CGR_WE_CSCN_TARG;
+
+               /* send init if flags indicate so */
+               if (opts && (flags & QMAN_CGR_FLAG_USE_INIT))
+                       ret = qm_modify_cgr(cgr, QMAN_CGR_FLAG_USE_INIT,
+                                           &local_opts);
+               else
+                       ret = qm_modify_cgr(cgr, 0, &local_opts);
+               if (ret)
+                       goto out;
+       }
+
+       list_add(&cgr->node, &p->cgr_cbs);
+
+       /* Determine if newly added object requires its callback to be called */
+       ret = qman_query_cgr(cgr, &cgr_state);
+       if (ret) {
+               /* we can't go back, so proceed and return success */
+               dev_err(p->config->dev, "CGR HW state partially modified\n");
+               ret = 0;
+               goto out;
+       }
+       if (cgr->cb && cgr_state.cgr.cscn_en &&
+           qman_cgrs_get(&p->cgrs[1], cgr->cgrid))
+               cgr->cb(p, cgr, 1);
+out:
+       spin_unlock(&p->cgr_lock);
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_create_cgr);
+
+int qman_delete_cgr(struct qman_cgr *cgr)
+{
+       unsigned long irqflags;
+       struct qm_mcr_querycgr cgr_state;
+       struct qm_mcc_initcgr local_opts;
+       int ret = 0;
+       struct qman_cgr *i;
+       struct qman_portal *p = get_affine_portal();
+
+       if (cgr->chan != p->config->channel) {
+               /* attempt to delete from other portal than creator */
+               dev_err(p->config->dev, "CGR not owned by current portal");
+               dev_dbg(p->config->dev, " create 0x%x, delete 0x%x\n",
+                       cgr->chan, p->config->channel);
+
+               ret = -EINVAL;
+               goto put_portal;
+       }
+       memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr));
+       spin_lock_irqsave(&p->cgr_lock, irqflags);
+       list_del(&cgr->node);
+       /*
+        * If there are no other CGR objects for this CGRID in the list,
+        * update CSCN_TARG accordingly
+        */
+       list_for_each_entry(i, &p->cgr_cbs, node)
+               if (i->cgrid == cgr->cgrid && i->cb)
+                       goto release_lock;
+       ret = qman_query_cgr(cgr, &cgr_state);
+       if (ret)  {
+               /* add back to the list */
+               list_add(&cgr->node, &p->cgr_cbs);
+               goto release_lock;
+       }
+       /* Overwrite TARG */
+       local_opts.we_mask = QM_CGR_WE_CSCN_TARG;
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               local_opts.cgr.cscn_targ_upd_ctrl = PORTAL_IDX(p);
+       else
+               local_opts.cgr.cscn_targ = cgr_state.cgr.cscn_targ &
+                                                        ~(TARG_MASK(p));
+       ret = qm_modify_cgr(cgr, 0, &local_opts);
+       if (ret)
+               /* add back to the list */
+               list_add(&cgr->node, &p->cgr_cbs);
+release_lock:
+       spin_unlock_irqrestore(&p->cgr_lock, irqflags);
+put_portal:
+       put_affine_portal();
+       return ret;
+}
+EXPORT_SYMBOL(qman_delete_cgr);
+
+struct cgr_comp {
+       struct qman_cgr *cgr;
+       struct completion completion;
+};
+
+static int qman_delete_cgr_thread(void *p)
+{
+       struct cgr_comp *cgr_comp = (struct cgr_comp *)p;
+       int ret;
+
+       ret = qman_delete_cgr(cgr_comp->cgr);
+       complete(&cgr_comp->completion);
+
+       return ret;
+}
+
+void qman_delete_cgr_safe(struct qman_cgr *cgr)
+{
+       struct task_struct *thread;
+       struct cgr_comp cgr_comp;
+
+       preempt_disable();
+       if (qman_cgr_cpus[cgr->cgrid] != smp_processor_id()) {
+               init_completion(&cgr_comp.completion);
+               cgr_comp.cgr = cgr;
+               thread = kthread_create(qman_delete_cgr_thread, &cgr_comp,
+                                       "cgr_del");
+
+               if (IS_ERR(thread))
+                       goto out;
+
+               kthread_bind(thread, qman_cgr_cpus[cgr->cgrid]);
+               wake_up_process(thread);
+               wait_for_completion(&cgr_comp.completion);
+               preempt_enable();
+               return;
+       }
+out:
+       qman_delete_cgr(cgr);
+       preempt_enable();
+}
+EXPORT_SYMBOL(qman_delete_cgr_safe);
+
+/* Cleanup FQs */
+
+static int _qm_mr_consume_and_match_verb(struct qm_portal *p, int v)
+{
+       const union qm_mr_entry *msg;
+       int found = 0;
+
+       qm_mr_pvb_update(p);
+       msg = qm_mr_current(p);
+       while (msg) {
+               if ((msg->verb & QM_MR_VERB_TYPE_MASK) == v)
+                       found = 1;
+               qm_mr_next(p);
+               qm_mr_cci_consume_to_current(p);
+               qm_mr_pvb_update(p);
+               msg = qm_mr_current(p);
+       }
+       return found;
+}
+
+static int _qm_dqrr_consume_and_match(struct qm_portal *p, u32 fqid, int s,
+                                     bool wait)
+{
+       const struct qm_dqrr_entry *dqrr;
+       int found = 0;
+
+       do {
+               qm_dqrr_pvb_update(p);
+               dqrr = qm_dqrr_current(p);
+               if (!dqrr)
+                       cpu_relax();
+       } while (wait && !dqrr);
+
+       while (dqrr) {
+               if (dqrr->fqid == fqid && (dqrr->stat & s))
+                       found = 1;
+               qm_dqrr_cdc_consume_1ptr(p, dqrr, 0);
+               qm_dqrr_pvb_update(p);
+               qm_dqrr_next(p);
+               dqrr = qm_dqrr_current(p);
+       }
+       return found;
+}
+
+#define qm_mr_drain(p, V) \
+       _qm_mr_consume_and_match_verb(p, QM_MR_VERB_##V)
+
+#define qm_dqrr_drain(p, f, S) \
+       _qm_dqrr_consume_and_match(p, f, QM_DQRR_STAT_##S, false)
+
+#define qm_dqrr_drain_wait(p, f, S) \
+       _qm_dqrr_consume_and_match(p, f, QM_DQRR_STAT_##S, true)
+
+#define qm_dqrr_drain_nomatch(p) \
+       _qm_dqrr_consume_and_match(p, 0, 0, false)
+
+static int qman_shutdown_fq(u32 fqid)
+{
+       struct qman_portal *p;
+       struct device *dev;
+       union qm_mc_command *mcc;
+       union qm_mc_result *mcr;
+       int orl_empty, drain = 0, ret = 0;
+       u32 channel, wq, res;
+       u8 state;
+
+       p = get_affine_portal();
+       dev = p->config->dev;
+       /* Determine the state of the FQID */
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq_np.fqid = fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ_NP);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(dev, "QUERYFQ_NP timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ_NP);
+       state = mcr->queryfq_np.state & QM_MCR_NP_STATE_MASK;
+       if (state == QM_MCR_NP_STATE_OOS)
+               goto out; /* Already OOS, no need to do anymore checks */
+
+       /* Query which channel the FQ is using */
+       mcc = qm_mc_start(&p->p);
+       mcc->queryfq.fqid = fqid;
+       qm_mc_commit(&p->p, QM_MCC_VERB_QUERYFQ);
+       if (!qm_mc_result_timeout(&p->p, &mcr)) {
+               dev_err(dev, "QUERYFQ timeout\n");
+               ret = -ETIMEDOUT;
+               goto out;
+       }
+
+       DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) == QM_MCR_VERB_QUERYFQ);
+       /* Need to store these since the MCR gets reused */
+       channel = qm_fqd_get_chan(&mcr->queryfq.fqd);
+       wq = qm_fqd_get_wq(&mcr->queryfq.fqd);
+
+       switch (state) {
+       case QM_MCR_NP_STATE_TEN_SCHED:
+       case QM_MCR_NP_STATE_TRU_SCHED:
+       case QM_MCR_NP_STATE_ACTIVE:
+       case QM_MCR_NP_STATE_PARKED:
+               orl_empty = 0;
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_RETIRE);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       dev_err(dev, "QUERYFQ_NP timeout\n");
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_RETIRE);
+               res = mcr->result; /* Make a copy as we reuse MCR below */
+
+               if (res == QM_MCR_RESULT_PENDING) {
+                       /*
+                        * Need to wait for the FQRN in the message ring, which
+                        * will only occur once the FQ has been drained.  In
+                        * order for the FQ to drain the portal needs to be set
+                        * to dequeue from the channel the FQ is scheduled on
+                        */
+                       int found_fqrn = 0;
+                       u16 dequeue_wq = 0;
+
+                       /* Flag that we need to drain FQ */
+                       drain = 1;
+
+                       if (channel >= qm_channel_pool1 &&
+                           channel < qm_channel_pool1 + 15) {
+                               /* Pool channel, enable the bit in the portal */
+                               dequeue_wq = (channel -
+                                             qm_channel_pool1 + 1)<<4 | wq;
+                       } else if (channel < qm_channel_pool1) {
+                               /* Dedicated channel */
+                               dequeue_wq = wq;
+                       } else {
+                               dev_err(dev, "Can't recover FQ 0x%x, ch: 0x%x",
+                                       fqid, channel);
+                               ret = -EBUSY;
+                               goto out;
+                       }
+                       /* Set the sdqcr to drain this channel */
+                       if (channel < qm_channel_pool1)
+                               qm_dqrr_sdqcr_set(&p->p,
+                                                 QM_SDQCR_TYPE_ACTIVE |
+                                                 QM_SDQCR_CHANNELS_DEDICATED);
+                       else
+                               qm_dqrr_sdqcr_set(&p->p,
+                                                 QM_SDQCR_TYPE_ACTIVE |
+                                                 QM_SDQCR_CHANNELS_POOL_CONV
+                                                 (channel));
+                       do {
+                               /* Keep draining DQRR while checking the MR*/
+                               qm_dqrr_drain_nomatch(&p->p);
+                               /* Process message ring too */
+                               found_fqrn = qm_mr_drain(&p->p, FQRN);
+                               cpu_relax();
+                       } while (!found_fqrn);
+
+               }
+               if (res != QM_MCR_RESULT_OK &&
+                   res != QM_MCR_RESULT_PENDING) {
+                       dev_err(dev, "retire_fq failed: FQ 0x%x, res=0x%x\n",
+                               fqid, res);
+                       ret = -EIO;
+                       goto out;
+               }
+               if (!(mcr->alterfq.fqs & QM_MCR_FQS_ORLPRESENT)) {
+                       /*
+                        * ORL had no entries, no need to wait until the
+                        * ERNs come in
+                        */
+                       orl_empty = 1;
+               }
+               /*
+                * Retirement succeeded, check to see if FQ needs
+                * to be drained
+                */
+               if (drain || mcr->alterfq.fqs & QM_MCR_FQS_NOTEMPTY) {
+                       /* FQ is Not Empty, drain using volatile DQ commands */
+                       do {
+                               u32 vdqcr = fqid | QM_VDQCR_NUMFRAMES_SET(3);
+
+                               qm_dqrr_vdqcr_set(&p->p, vdqcr);
+                               /*
+                                * Wait for a dequeue and process the dequeues,
+                                * making sure to empty the ring completely
+                                */
+                       } while (qm_dqrr_drain_wait(&p->p, fqid, FQ_EMPTY));
+               }
+               qm_dqrr_sdqcr_set(&p->p, 0);
+
+               while (!orl_empty) {
+                       /* Wait for the ORL to have been completely drained */
+                       orl_empty = qm_mr_drain(&p->p, FQRL);
+                       cpu_relax();
+               }
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_OOS);
+               if (mcr->result != QM_MCR_RESULT_OK) {
+                       dev_err(dev, "OOS after drain fail: FQ 0x%x (0x%x)\n",
+                               fqid, mcr->result);
+                       ret = -EIO;
+                       goto out;
+               }
+               break;
+
+       case QM_MCR_NP_STATE_RETIRED:
+               /* Send OOS Command */
+               mcc = qm_mc_start(&p->p);
+               mcc->alterfq.fqid = fqid;
+               qm_mc_commit(&p->p, QM_MCC_VERB_ALTER_OOS);
+               if (!qm_mc_result_timeout(&p->p, &mcr)) {
+                       ret = -ETIMEDOUT;
+                       goto out;
+               }
+
+               DPAA_ASSERT((mcr->verb & QM_MCR_VERB_MASK) ==
+                           QM_MCR_VERB_ALTER_OOS);
+               if (mcr->result) {
+                       dev_err(dev, "OOS fail: FQ 0x%x (0x%x)\n",
+                               fqid, mcr->result);
+                       ret = -EIO;
+                       goto out;
+               }
+               break;
+
+       case QM_MCR_NP_STATE_OOS:
+               /*  Done */
+               break;
+
+       default:
+               ret = -EIO;
+       }
+
+out:
+       put_affine_portal();
+       return ret;
+}
+
+const struct qm_portal_config *qman_get_qm_portal_config(
+                                               struct qman_portal *portal)
+{
+       return portal->config;
+}
+
+struct gen_pool *qm_fqalloc; /* FQID allocator */
+struct gen_pool *qm_qpalloc; /* pool-channel allocator */
+struct gen_pool *qm_cgralloc; /* CGR ID allocator */
+
+static int qman_alloc_range(struct gen_pool *p, u32 *result, u32 cnt)
+{
+       unsigned long addr;
+
+       addr = gen_pool_alloc(p, cnt);
+       if (!addr)
+               return -ENOMEM;
+
+       *result = addr & ~DPAA_GENALLOC_OFF;
+
+       return 0;
+}
+
+int qman_alloc_fqid_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_fqalloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_fqid_range);
+
+int qman_alloc_pool_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_qpalloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_pool_range);
+
+int qman_alloc_cgrid_range(u32 *result, u32 count)
+{
+       return qman_alloc_range(qm_cgralloc, result, count);
+}
+EXPORT_SYMBOL(qman_alloc_cgrid_range);
+
+int qman_release_fqid(u32 fqid)
+{
+       int ret = qman_shutdown_fq(fqid);
+
+       if (ret) {
+               pr_debug("FQID %d leaked\n", fqid);
+               return ret;
+       }
+
+       gen_pool_free(qm_fqalloc, fqid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_fqid);
+
+static int qpool_cleanup(u32 qp)
+{
+       /*
+        * We query all FQDs starting from
+        * FQID 1 until we get an "invalid FQID" error, looking for non-OOS FQDs
+        * whose destination channel is the pool-channel being released.
+        * When a non-OOS FQD is found we attempt to clean it up
+        */
+       struct qman_fq fq = {
+               .fqid = QM_FQID_RANGE_START
+       };
+       int err;
+
+       do {
+               struct qm_mcr_queryfq_np np;
+
+               err = qman_query_fq_np(&fq, &np);
+               if (err)
+                       /* FQID range exceeded, found no problems */
+                       return 0;
+               if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
+                       struct qm_fqd fqd;
+
+                       err = qman_query_fq(&fq, &fqd);
+                       if (WARN_ON(err))
+                               return 0;
+                       if (qm_fqd_get_chan(&fqd) == qp) {
+                               /* The channel is the FQ's target, clean it */
+                               err = qman_shutdown_fq(fq.fqid);
+                               if (err)
+                                       /*
+                                        * Couldn't shut down the FQ
+                                        * so the pool must be leaked
+                                        */
+                                       return err;
+                       }
+               }
+               /* Move to the next FQID */
+               fq.fqid++;
+       } while (1);
+}
+
+int qman_release_pool(u32 qp)
+{
+       int ret;
+
+       ret = qpool_cleanup(qp);
+       if (ret) {
+               pr_debug("CHID %d leaked\n", qp);
+               return ret;
+       }
+
+       gen_pool_free(qm_qpalloc, qp | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_pool);
+
+static int cgr_cleanup(u32 cgrid)
+{
+       /*
+        * query all FQDs starting from FQID 1 until we get an "invalid FQID"
+        * error, looking for non-OOS FQDs whose CGR is the CGR being released
+        */
+       struct qman_fq fq = {
+               .fqid = 1
+       };
+       int err;
+
+       do {
+               struct qm_mcr_queryfq_np np;
+
+               err = qman_query_fq_np(&fq, &np);
+               if (err)
+                       /* FQID range exceeded, found no problems */
+                       return 0;
+               if ((np.state & QM_MCR_NP_STATE_MASK) != QM_MCR_NP_STATE_OOS) {
+                       struct qm_fqd fqd;
+
+                       err = qman_query_fq(&fq, &fqd);
+                       if (WARN_ON(err))
+                               return 0;
+                       if ((fqd.fq_ctrl & QM_FQCTRL_CGE) &&
+                           fqd.cgid == cgrid) {
+                               pr_err("CRGID 0x%x is being used by FQID 0x%x, CGR will be leaked\n",
+                                      cgrid, fq.fqid);
+                               return -EIO;
+                       }
+               }
+               /* Move to the next FQID */
+               fq.fqid++;
+       } while (1);
+}
+
+int qman_release_cgrid(u32 cgrid)
+{
+       int ret;
+
+       ret = cgr_cleanup(cgrid);
+       if (ret) {
+               pr_debug("CGRID %d leaked\n", cgrid);
+               return ret;
+       }
+
+       gen_pool_free(qm_cgralloc, cgrid | DPAA_GENALLOC_OFF, 1);
+       return 0;
+}
+EXPORT_SYMBOL(qman_release_cgrid);
diff --git a/drivers/soc/fsl/qbman/qman_ccsr.c b/drivers/soc/fsl/qbman/qman_ccsr.c
new file mode 100644 (file)
index 0000000..0cace9e
--- /dev/null
@@ -0,0 +1,808 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_priv.h"
+
+u16 qman_ip_rev;
+EXPORT_SYMBOL(qman_ip_rev);
+u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1;
+EXPORT_SYMBOL(qm_channel_pool1);
+
+/* Register offsets */
+#define REG_QCSP_LIO_CFG(n)    (0x0000 + ((n) * 0x10))
+#define REG_QCSP_IO_CFG(n)     (0x0004 + ((n) * 0x10))
+#define REG_QCSP_DD_CFG(n)     (0x000c + ((n) * 0x10))
+#define REG_DD_CFG             0x0200
+#define REG_DCP_CFG(n)         (0x0300 + ((n) * 0x10))
+#define REG_DCP_DD_CFG(n)      (0x0304 + ((n) * 0x10))
+#define REG_DCP_DLM_AVG(n)     (0x030c + ((n) * 0x10))
+#define REG_PFDR_FPC           0x0400
+#define REG_PFDR_FP_HEAD       0x0404
+#define REG_PFDR_FP_TAIL       0x0408
+#define REG_PFDR_FP_LWIT       0x0410
+#define REG_PFDR_CFG           0x0414
+#define REG_SFDR_CFG           0x0500
+#define REG_SFDR_IN_USE                0x0504
+#define REG_WQ_CS_CFG(n)       (0x0600 + ((n) * 0x04))
+#define REG_WQ_DEF_ENC_WQID    0x0630
+#define REG_WQ_SC_DD_CFG(n)    (0x640 + ((n) * 0x04))
+#define REG_WQ_PC_DD_CFG(n)    (0x680 + ((n) * 0x04))
+#define REG_WQ_DC0_DD_CFG(n)   (0x6c0 + ((n) * 0x04))
+#define REG_WQ_DC1_DD_CFG(n)   (0x700 + ((n) * 0x04))
+#define REG_WQ_DCn_DD_CFG(n)   (0x6c0 + ((n) * 0x40)) /* n=2,3 */
+#define REG_CM_CFG             0x0800
+#define REG_ECSR               0x0a00
+#define REG_ECIR               0x0a04
+#define REG_EADR               0x0a08
+#define REG_ECIR2              0x0a0c
+#define REG_EDATA(n)           (0x0a10 + ((n) * 0x04))
+#define REG_SBEC(n)            (0x0a80 + ((n) * 0x04))
+#define REG_MCR                        0x0b00
+#define REG_MCP(n)             (0x0b04 + ((n) * 0x04))
+#define REG_MISC_CFG           0x0be0
+#define REG_HID_CFG            0x0bf0
+#define REG_IDLE_STAT          0x0bf4
+#define REG_IP_REV_1           0x0bf8
+#define REG_IP_REV_2           0x0bfc
+#define REG_FQD_BARE           0x0c00
+#define REG_PFDR_BARE          0x0c20
+#define REG_offset_BAR         0x0004  /* relative to REG_[FQD|PFDR]_BARE */
+#define REG_offset_AR          0x0010  /* relative to REG_[FQD|PFDR]_BARE */
+#define REG_QCSP_BARE          0x0c80
+#define REG_QCSP_BAR           0x0c84
+#define REG_CI_SCHED_CFG       0x0d00
+#define REG_SRCIDR             0x0d04
+#define REG_LIODNR             0x0d08
+#define REG_CI_RLM_AVG         0x0d14
+#define REG_ERR_ISR            0x0e00
+#define REG_ERR_IER            0x0e04
+#define REG_REV3_QCSP_LIO_CFG(n)       (0x1000 + ((n) * 0x10))
+#define REG_REV3_QCSP_IO_CFG(n)        (0x1004 + ((n) * 0x10))
+#define REG_REV3_QCSP_DD_CFG(n)        (0x100c + ((n) * 0x10))
+
+/* Assists for QMAN_MCR */
+#define MCR_INIT_PFDR          0x01000000
+#define MCR_get_rslt(v)                (u8)((v) >> 24)
+#define MCR_rslt_idle(r)       (!(r) || ((r) >= 0xf0))
+#define MCR_rslt_ok(r)         ((r) == 0xf0)
+#define MCR_rslt_eaccess(r)    ((r) == 0xf8)
+#define MCR_rslt_inval(r)      ((r) == 0xff)
+
+/*
+ * Corenet initiator settings. Stash request queues are 4-deep to match cores
+ * ability to snarf. Stash priority is 3, other priorities are 2.
+ */
+#define QM_CI_SCHED_CFG_SRCCIV         4
+#define QM_CI_SCHED_CFG_SRQ_W          3
+#define QM_CI_SCHED_CFG_RW_W           2
+#define QM_CI_SCHED_CFG_BMAN_W         2
+/* write SRCCIV enable */
+#define QM_CI_SCHED_CFG_SRCCIV_EN      BIT(31)
+
+/* Follows WQ_CS_CFG0-5 */
+enum qm_wq_class {
+       qm_wq_portal = 0,
+       qm_wq_pool = 1,
+       qm_wq_fman0 = 2,
+       qm_wq_fman1 = 3,
+       qm_wq_caam = 4,
+       qm_wq_pme = 5,
+       qm_wq_first = qm_wq_portal,
+       qm_wq_last = qm_wq_pme
+};
+
+/* Follows FQD_[BARE|BAR|AR] and PFDR_[BARE|BAR|AR] */
+enum qm_memory {
+       qm_memory_fqd,
+       qm_memory_pfdr
+};
+
+/* Used by all error interrupt registers except 'inhibit' */
+#define QM_EIRQ_CIDE   0x20000000      /* Corenet Initiator Data Error */
+#define QM_EIRQ_CTDE   0x10000000      /* Corenet Target Data Error */
+#define QM_EIRQ_CITT   0x08000000      /* Corenet Invalid Target Transaction */
+#define QM_EIRQ_PLWI   0x04000000      /* PFDR Low Watermark */
+#define QM_EIRQ_MBEI   0x02000000      /* Multi-bit ECC Error */
+#define QM_EIRQ_SBEI   0x01000000      /* Single-bit ECC Error */
+#define QM_EIRQ_PEBI   0x00800000      /* PFDR Enqueues Blocked Interrupt */
+#define QM_EIRQ_IFSI   0x00020000      /* Invalid FQ Flow Control State */
+#define QM_EIRQ_ICVI   0x00010000      /* Invalid Command Verb */
+#define QM_EIRQ_IDDI   0x00000800      /* Invalid Dequeue (Direct-connect) */
+#define QM_EIRQ_IDFI   0x00000400      /* Invalid Dequeue FQ */
+#define QM_EIRQ_IDSI   0x00000200      /* Invalid Dequeue Source */
+#define QM_EIRQ_IDQI   0x00000100      /* Invalid Dequeue Queue */
+#define QM_EIRQ_IECE   0x00000010      /* Invalid Enqueue Configuration */
+#define QM_EIRQ_IEOI   0x00000008      /* Invalid Enqueue Overflow */
+#define QM_EIRQ_IESI   0x00000004      /* Invalid Enqueue State */
+#define QM_EIRQ_IECI   0x00000002      /* Invalid Enqueue Channel */
+#define QM_EIRQ_IEQI   0x00000001      /* Invalid Enqueue Queue */
+
+/* QMAN_ECIR valid error bit */
+#define PORTAL_ECSR_ERR        (QM_EIRQ_IEQI | QM_EIRQ_IESI | QM_EIRQ_IEOI | \
+                        QM_EIRQ_IDQI | QM_EIRQ_IDSI | QM_EIRQ_IDFI | \
+                        QM_EIRQ_IDDI | QM_EIRQ_ICVI | QM_EIRQ_IFSI)
+#define FQID_ECSR_ERR  (QM_EIRQ_IEQI | QM_EIRQ_IECI | QM_EIRQ_IESI | \
+                        QM_EIRQ_IEOI | QM_EIRQ_IDQI | QM_EIRQ_IDFI | \
+                        QM_EIRQ_IFSI)
+
+struct qm_ecir {
+       u32 info; /* res[30-31], ptyp[29], pnum[24-28], fqid[0-23] */
+};
+
+static bool qm_ecir_is_dcp(const struct qm_ecir *p)
+{
+       return p->info & BIT(29);
+}
+
+static int qm_ecir_get_pnum(const struct qm_ecir *p)
+{
+       return (p->info >> 24) & 0x1f;
+}
+
+static int qm_ecir_get_fqid(const struct qm_ecir *p)
+{
+       return p->info & (BIT(24) - 1);
+}
+
+struct qm_ecir2 {
+       u32 info; /* ptyp[31], res[10-30], pnum[0-9] */
+};
+
+static bool qm_ecir2_is_dcp(const struct qm_ecir2 *p)
+{
+       return p->info & BIT(31);
+}
+
+static int qm_ecir2_get_pnum(const struct qm_ecir2 *p)
+{
+       return p->info & (BIT(10) - 1);
+}
+
+struct qm_eadr {
+       u32 info; /* memid[24-27], eadr[0-11] */
+                 /* v3: memid[24-28], eadr[0-15] */
+};
+
+static int qm_eadr_get_memid(const struct qm_eadr *p)
+{
+       return (p->info >> 24) & 0xf;
+}
+
+static int qm_eadr_get_eadr(const struct qm_eadr *p)
+{
+       return p->info & (BIT(12) - 1);
+}
+
+static int qm_eadr_v3_get_memid(const struct qm_eadr *p)
+{
+       return (p->info >> 24) & 0x1f;
+}
+
+static int qm_eadr_v3_get_eadr(const struct qm_eadr *p)
+{
+       return p->info & (BIT(16) - 1);
+}
+
+struct qman_hwerr_txt {
+       u32 mask;
+       const char *txt;
+};
+
+
+static const struct qman_hwerr_txt qman_hwerr_txts[] = {
+       { QM_EIRQ_CIDE, "Corenet Initiator Data Error" },
+       { QM_EIRQ_CTDE, "Corenet Target Data Error" },
+       { QM_EIRQ_CITT, "Corenet Invalid Target Transaction" },
+       { QM_EIRQ_PLWI, "PFDR Low Watermark" },
+       { QM_EIRQ_MBEI, "Multi-bit ECC Error" },
+       { QM_EIRQ_SBEI, "Single-bit ECC Error" },
+       { QM_EIRQ_PEBI, "PFDR Enqueues Blocked Interrupt" },
+       { QM_EIRQ_ICVI, "Invalid Command Verb" },
+       { QM_EIRQ_IFSI, "Invalid Flow Control State" },
+       { QM_EIRQ_IDDI, "Invalid Dequeue (Direct-connect)" },
+       { QM_EIRQ_IDFI, "Invalid Dequeue FQ" },
+       { QM_EIRQ_IDSI, "Invalid Dequeue Source" },
+       { QM_EIRQ_IDQI, "Invalid Dequeue Queue" },
+       { QM_EIRQ_IECE, "Invalid Enqueue Configuration" },
+       { QM_EIRQ_IEOI, "Invalid Enqueue Overflow" },
+       { QM_EIRQ_IESI, "Invalid Enqueue State" },
+       { QM_EIRQ_IECI, "Invalid Enqueue Channel" },
+       { QM_EIRQ_IEQI, "Invalid Enqueue Queue" },
+};
+
+struct qman_error_info_mdata {
+       u16 addr_mask;
+       u16 bits;
+       const char *txt;
+};
+
+static const struct qman_error_info_mdata error_mdata[] = {
+       { 0x01FF, 24, "FQD cache tag memory 0" },
+       { 0x01FF, 24, "FQD cache tag memory 1" },
+       { 0x01FF, 24, "FQD cache tag memory 2" },
+       { 0x01FF, 24, "FQD cache tag memory 3" },
+       { 0x0FFF, 512, "FQD cache memory" },
+       { 0x07FF, 128, "SFDR memory" },
+       { 0x01FF, 72, "WQ context memory" },
+       { 0x00FF, 240, "CGR memory" },
+       { 0x00FF, 302, "Internal Order Restoration List memory" },
+       { 0x01FF, 256, "SW portal ring memory" },
+};
+
+#define QMAN_ERRS_TO_DISABLE (QM_EIRQ_PLWI | QM_EIRQ_PEBI)
+
+/*
+ * TODO: unimplemented registers
+ *
+ * Keeping a list here of QMan registers I have not yet covered;
+ * QCSP_DD_IHRSR, QCSP_DD_IHRFR, QCSP_DD_HASR,
+ * DCP_DD_IHRSR, DCP_DD_IHRFR, DCP_DD_HASR, CM_CFG,
+ * QMAN_EECC, QMAN_SBET, QMAN_EINJ, QMAN_SBEC0-12
+ */
+
+/* Pointer to the start of the QMan's CCSR space */
+static u32 __iomem *qm_ccsr_start;
+/* A SDQCR mask comprising all the available/visible pool channels */
+static u32 qm_pools_sdqcr;
+
+static inline u32 qm_ccsr_in(u32 offset)
+{
+       return ioread32be(qm_ccsr_start + offset/4);
+}
+
+static inline void qm_ccsr_out(u32 offset, u32 val)
+{
+       iowrite32be(val, qm_ccsr_start + offset/4);
+}
+
+u32 qm_get_pools_sdqcr(void)
+{
+       return qm_pools_sdqcr;
+}
+
+enum qm_dc_portal {
+       qm_dc_portal_fman0 = 0,
+       qm_dc_portal_fman1 = 1
+};
+
+static void qm_set_dc(enum qm_dc_portal portal, int ed, u8 sernd)
+{
+       DPAA_ASSERT(!ed || portal == qm_dc_portal_fman0 ||
+                   portal == qm_dc_portal_fman1);
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               qm_ccsr_out(REG_DCP_CFG(portal),
+                           (ed ? 0x1000 : 0) | (sernd & 0x3ff));
+       else
+               qm_ccsr_out(REG_DCP_CFG(portal),
+                           (ed ? 0x100 : 0) | (sernd & 0x1f));
+}
+
+static void qm_set_wq_scheduling(enum qm_wq_class wq_class,
+                                u8 cs_elev, u8 csw2, u8 csw3, u8 csw4,
+                                u8 csw5, u8 csw6, u8 csw7)
+{
+       qm_ccsr_out(REG_WQ_CS_CFG(wq_class), ((cs_elev & 0xff) << 24) |
+                   ((csw2 & 0x7) << 20) | ((csw3 & 0x7) << 16) |
+                   ((csw4 & 0x7) << 12) | ((csw5 & 0x7) << 8) |
+                   ((csw6 & 0x7) << 4) | (csw7 & 0x7));
+}
+
+static void qm_set_hid(void)
+{
+       qm_ccsr_out(REG_HID_CFG, 0);
+}
+
+static void qm_set_corenet_initiator(void)
+{
+       qm_ccsr_out(REG_CI_SCHED_CFG, QM_CI_SCHED_CFG_SRCCIV_EN |
+                   (QM_CI_SCHED_CFG_SRCCIV << 24) |
+                   (QM_CI_SCHED_CFG_SRQ_W << 8) |
+                   (QM_CI_SCHED_CFG_RW_W << 4) |
+                   QM_CI_SCHED_CFG_BMAN_W);
+}
+
+static void qm_get_version(u16 *id, u8 *major, u8 *minor)
+{
+       u32 v = qm_ccsr_in(REG_IP_REV_1);
+       *id = (v >> 16);
+       *major = (v >> 8) & 0xff;
+       *minor = v & 0xff;
+}
+
+#define PFDR_AR_EN             BIT(31)
+static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
+{
+       u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
+       u32 exp = ilog2(size);
+
+       /* choke if size isn't within range */
+       DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
+                   is_power_of_2(size));
+       /* choke if 'ba' has lower-alignment than 'size' */
+       DPAA_ASSERT(!(ba & (size - 1)));
+       qm_ccsr_out(offset, upper_32_bits(ba));
+       qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
+       qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
+}
+
+static void qm_set_pfdr_threshold(u32 th, u8 k)
+{
+       qm_ccsr_out(REG_PFDR_FP_LWIT, th & 0xffffff);
+       qm_ccsr_out(REG_PFDR_CFG, k);
+}
+
+static void qm_set_sfdr_threshold(u16 th)
+{
+       qm_ccsr_out(REG_SFDR_CFG, th & 0x3ff);
+}
+
+static int qm_init_pfdr(struct device *dev, u32 pfdr_start, u32 num)
+{
+       u8 rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
+
+       DPAA_ASSERT(pfdr_start && !(pfdr_start & 7) && !(num & 7) && num);
+       /* Make sure the command interface is 'idle' */
+       if (!MCR_rslt_idle(rslt)) {
+               dev_crit(dev, "QMAN_MCR isn't idle");
+               WARN_ON(1);
+       }
+
+       /* Write the MCR command params then the verb */
+       qm_ccsr_out(REG_MCP(0), pfdr_start);
+       /*
+        * TODO: remove this - it's a workaround for a model bug that is
+        * corrected in more recent versions. We use the workaround until
+        * everyone has upgraded.
+        */
+       qm_ccsr_out(REG_MCP(1), pfdr_start + num - 16);
+       dma_wmb();
+       qm_ccsr_out(REG_MCR, MCR_INIT_PFDR);
+       /* Poll for the result */
+       do {
+               rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
+       } while (!MCR_rslt_idle(rslt));
+       if (MCR_rslt_ok(rslt))
+               return 0;
+       if (MCR_rslt_eaccess(rslt))
+               return -EACCES;
+       if (MCR_rslt_inval(rslt))
+               return -EINVAL;
+       dev_crit(dev, "Unexpected result from MCR_INIT_PFDR: %02x\n", rslt);
+       return -ENODEV;
+}
+
+/*
+ * Ideally we would use the DMA API to turn rmem->base into a DMA address
+ * (especially if iommu translations ever get involved).  Unfortunately, the
+ * DMA API currently does not allow mapping anything that is not backed with
+ * a struct page.
+ */
+static dma_addr_t fqd_a, pfdr_a;
+static size_t fqd_sz, pfdr_sz;
+
+static int qman_fqd(struct reserved_mem *rmem)
+{
+       fqd_a = rmem->base;
+       fqd_sz = rmem->size;
+
+       WARN_ON(!(fqd_a && fqd_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd);
+
+static int qman_pfdr(struct reserved_mem *rmem)
+{
+       pfdr_a = rmem->base;
+       pfdr_sz = rmem->size;
+
+       WARN_ON(!(pfdr_a && pfdr_sz));
+
+       return 0;
+}
+RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
+
+static unsigned int qm_get_fqid_maxcnt(void)
+{
+       return fqd_sz / 64;
+}
+
+/*
+ * Flush this memory range from data cache so that QMAN originated
+ * transactions for this memory region could be marked non-coherent.
+ */
+static int zero_priv_mem(struct device *dev, struct device_node *node,
+                        phys_addr_t addr, size_t sz)
+{
+       /* map as cacheable, non-guarded */
+       void __iomem *tmpp = ioremap_prot(addr, sz, 0);
+
+       memset_io(tmpp, 0, sz);
+       flush_dcache_range((unsigned long)tmpp,
+                          (unsigned long)tmpp + sz);
+       iounmap(tmpp);
+
+       return 0;
+}
+
+static void log_edata_bits(struct device *dev, u32 bit_count)
+{
+       u32 i, j, mask = 0xffffffff;
+
+       dev_warn(dev, "ErrInt, EDATA:\n");
+       i = bit_count / 32;
+       if (bit_count % 32) {
+               i++;
+               mask = ~(mask << bit_count % 32);
+       }
+       j = 16 - i;
+       dev_warn(dev, "  0x%08x\n", qm_ccsr_in(REG_EDATA(j)) & mask);
+       j++;
+       for (; j < 16; j++)
+               dev_warn(dev, "  0x%08x\n", qm_ccsr_in(REG_EDATA(j)));
+}
+
+static void log_additional_error_info(struct device *dev, u32 isr_val,
+                                     u32 ecsr_val)
+{
+       struct qm_ecir ecir_val;
+       struct qm_eadr eadr_val;
+       int memid;
+
+       ecir_val.info = qm_ccsr_in(REG_ECIR);
+       /* Is portal info valid */
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
+               struct qm_ecir2 ecir2_val;
+
+               ecir2_val.info = qm_ccsr_in(REG_ECIR2);
+               if (ecsr_val & PORTAL_ECSR_ERR) {
+                       dev_warn(dev, "ErrInt: %s id %d\n",
+                                qm_ecir2_is_dcp(&ecir2_val) ? "DCP" : "SWP",
+                                qm_ecir2_get_pnum(&ecir2_val));
+               }
+               if (ecsr_val & (FQID_ECSR_ERR | QM_EIRQ_IECE))
+                       dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
+                                qm_ecir_get_fqid(&ecir_val));
+
+               if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
+                       eadr_val.info = qm_ccsr_in(REG_EADR);
+                       memid = qm_eadr_v3_get_memid(&eadr_val);
+                       dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
+                                error_mdata[memid].txt,
+                                error_mdata[memid].addr_mask
+                                       & qm_eadr_v3_get_eadr(&eadr_val));
+                       log_edata_bits(dev, error_mdata[memid].bits);
+               }
+       } else {
+               if (ecsr_val & PORTAL_ECSR_ERR) {
+                       dev_warn(dev, "ErrInt: %s id %d\n",
+                                qm_ecir_is_dcp(&ecir_val) ? "DCP" : "SWP",
+                                qm_ecir_get_pnum(&ecir_val));
+               }
+               if (ecsr_val & FQID_ECSR_ERR)
+                       dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
+                                qm_ecir_get_fqid(&ecir_val));
+
+               if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
+                       eadr_val.info = qm_ccsr_in(REG_EADR);
+                       memid = qm_eadr_get_memid(&eadr_val);
+                       dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
+                                error_mdata[memid].txt,
+                                error_mdata[memid].addr_mask
+                                       & qm_eadr_get_eadr(&eadr_val));
+                       log_edata_bits(dev, error_mdata[memid].bits);
+               }
+       }
+}
+
+static irqreturn_t qman_isr(int irq, void *ptr)
+{
+       u32 isr_val, ier_val, ecsr_val, isr_mask, i;
+       struct device *dev = ptr;
+
+       ier_val = qm_ccsr_in(REG_ERR_IER);
+       isr_val = qm_ccsr_in(REG_ERR_ISR);
+       ecsr_val = qm_ccsr_in(REG_ECSR);
+       isr_mask = isr_val & ier_val;
+
+       if (!isr_mask)
+               return IRQ_NONE;
+
+       for (i = 0; i < ARRAY_SIZE(qman_hwerr_txts); i++) {
+               if (qman_hwerr_txts[i].mask & isr_mask) {
+                       dev_err_ratelimited(dev, "ErrInt: %s\n",
+                                           qman_hwerr_txts[i].txt);
+                       if (qman_hwerr_txts[i].mask & ecsr_val) {
+                               log_additional_error_info(dev, isr_mask,
+                                                         ecsr_val);
+                               /* Re-arm error capture registers */
+                               qm_ccsr_out(REG_ECSR, ecsr_val);
+                       }
+                       if (qman_hwerr_txts[i].mask & QMAN_ERRS_TO_DISABLE) {
+                               dev_dbg(dev, "Disabling error 0x%x\n",
+                                       qman_hwerr_txts[i].mask);
+                               ier_val &= ~qman_hwerr_txts[i].mask;
+                               qm_ccsr_out(REG_ERR_IER, ier_val);
+                       }
+               }
+       }
+       qm_ccsr_out(REG_ERR_ISR, isr_val);
+
+       return IRQ_HANDLED;
+}
+
+static int qman_init_ccsr(struct device *dev)
+{
+       int i, err;
+
+       /* FQD memory */
+       qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
+       /* PFDR memory */
+       qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
+       err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
+       if (err)
+               return err;
+       /* thresholds */
+       qm_set_pfdr_threshold(512, 64);
+       qm_set_sfdr_threshold(128);
+       /* clear stale PEBI bit from interrupt status register */
+       qm_ccsr_out(REG_ERR_ISR, QM_EIRQ_PEBI);
+       /* corenet initiator settings */
+       qm_set_corenet_initiator();
+       /* HID settings */
+       qm_set_hid();
+       /* Set scheduling weights to defaults */
+       for (i = qm_wq_first; i <= qm_wq_last; i++)
+               qm_set_wq_scheduling(i, 0, 0, 0, 0, 0, 0, 0);
+       /* We are not prepared to accept ERNs for hardware enqueues */
+       qm_set_dc(qm_dc_portal_fman0, 1, 0);
+       qm_set_dc(qm_dc_portal_fman1, 1, 0);
+       return 0;
+}
+
+#define LIO_CFG_LIODN_MASK 0x0fff0000
+void qman_liodn_fixup(u16 channel)
+{
+       static int done;
+       static u32 liodn_offset;
+       u32 before, after;
+       int idx = channel - QM_CHANNEL_SWPORTAL0;
+
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               before = qm_ccsr_in(REG_REV3_QCSP_LIO_CFG(idx));
+       else
+               before = qm_ccsr_in(REG_QCSP_LIO_CFG(idx));
+       if (!done) {
+               liodn_offset = before & LIO_CFG_LIODN_MASK;
+               done = 1;
+               return;
+       }
+       after = (before & (~LIO_CFG_LIODN_MASK)) | liodn_offset;
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
+               qm_ccsr_out(REG_REV3_QCSP_LIO_CFG(idx), after);
+       else
+               qm_ccsr_out(REG_QCSP_LIO_CFG(idx), after);
+}
+
+#define IO_CFG_SDEST_MASK 0x00ff0000
+void qman_set_sdest(u16 channel, unsigned int cpu_idx)
+{
+       int idx = channel - QM_CHANNEL_SWPORTAL0;
+       u32 before, after;
+
+       if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
+               before = qm_ccsr_in(REG_REV3_QCSP_IO_CFG(idx));
+               /* Each pair of vcpu share the same SRQ(SDEST) */
+               cpu_idx /= 2;
+               after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
+               qm_ccsr_out(REG_REV3_QCSP_IO_CFG(idx), after);
+       } else {
+               before = qm_ccsr_in(REG_QCSP_IO_CFG(idx));
+               after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
+               qm_ccsr_out(REG_QCSP_IO_CFG(idx), after);
+       }
+}
+
+static int qman_resource_init(struct device *dev)
+{
+       int pool_chan_num, cgrid_num;
+       int ret, i;
+
+       switch (qman_ip_rev >> 8) {
+       case 1:
+               pool_chan_num = 15;
+               cgrid_num = 256;
+               break;
+       case 2:
+               pool_chan_num = 3;
+               cgrid_num = 64;
+               break;
+       case 3:
+               pool_chan_num = 15;
+               cgrid_num = 256;
+               break;
+       default:
+               return -ENODEV;
+       }
+
+       ret = gen_pool_add(qm_qpalloc, qm_channel_pool1 | DPAA_GENALLOC_OFF,
+                          pool_chan_num, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed pool channels (%d)\n", ret);
+               return ret;
+       }
+
+       ret = gen_pool_add(qm_cgralloc, DPAA_GENALLOC_OFF, cgrid_num, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed CGRID range (%d)\n", ret);
+               return ret;
+       }
+
+       /* parse pool channels into the SDQCR mask */
+       for (i = 0; i < cgrid_num; i++)
+               qm_pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(i);
+
+       ret = gen_pool_add(qm_fqalloc, QM_FQID_RANGE_START | DPAA_GENALLOC_OFF,
+                          qm_get_fqid_maxcnt() - QM_FQID_RANGE_START, -1);
+       if (ret) {
+               dev_err(dev, "Failed to seed FQID range (%d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int fsl_qman_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct resource *res;
+       int ret, err_irq;
+       u16 id;
+       u8 major, minor;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       qm_ccsr_start = devm_ioremap(dev, res->start, resource_size(res));
+       if (!qm_ccsr_start)
+               return -ENXIO;
+
+       qm_get_version(&id, &major, &minor);
+       if (major == 1 && minor == 0) {
+               dev_err(dev, "Rev1.0 on P4080 rev1 is not supported!\n");
+                       return -ENODEV;
+       } else if (major == 1 && minor == 1)
+               qman_ip_rev = QMAN_REV11;
+       else if (major == 1 && minor == 2)
+               qman_ip_rev = QMAN_REV12;
+       else if (major == 2 && minor == 0)
+               qman_ip_rev = QMAN_REV20;
+       else if (major == 3 && minor == 0)
+               qman_ip_rev = QMAN_REV30;
+       else if (major == 3 && minor == 1)
+               qman_ip_rev = QMAN_REV31;
+       else {
+               dev_err(dev, "Unknown QMan version\n");
+               return -ENODEV;
+       }
+
+       if ((qman_ip_rev & 0xff00) >= QMAN_REV30)
+               qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3;
+
+       ret = zero_priv_mem(dev, node, fqd_a, fqd_sz);
+       WARN_ON(ret);
+       if (ret)
+               return -ENODEV;
+
+       ret = qman_init_ccsr(dev);
+       if (ret) {
+               dev_err(dev, "CCSR setup failed\n");
+               return ret;
+       }
+
+       err_irq = platform_get_irq(pdev, 0);
+       if (err_irq <= 0) {
+               dev_info(dev, "Can't get %s property 'interrupts'\n",
+                        node->full_name);
+               return -ENODEV;
+       }
+       ret = devm_request_irq(dev, err_irq, qman_isr, IRQF_SHARED, "qman-err",
+                              dev);
+       if (ret)  {
+               dev_err(dev, "devm_request_irq() failed %d for '%s'\n",
+                       ret, node->full_name);
+               return ret;
+       }
+
+       /*
+        * Write-to-clear any stale bits, (eg. starvation being asserted prior
+        * to resource allocation during driver init).
+        */
+       qm_ccsr_out(REG_ERR_ISR, 0xffffffff);
+       /* Enable Error Interrupts */
+       qm_ccsr_out(REG_ERR_IER, 0xffffffff);
+
+       qm_fqalloc = devm_gen_pool_create(dev, 0, -1, "qman-fqalloc");
+       if (IS_ERR(qm_fqalloc)) {
+               ret = PTR_ERR(qm_fqalloc);
+               dev_err(dev, "qman-fqalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       qm_qpalloc = devm_gen_pool_create(dev, 0, -1, "qman-qpalloc");
+       if (IS_ERR(qm_qpalloc)) {
+               ret = PTR_ERR(qm_qpalloc);
+               dev_err(dev, "qman-qpalloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       qm_cgralloc = devm_gen_pool_create(dev, 0, -1, "qman-cgralloc");
+       if (IS_ERR(qm_cgralloc)) {
+               ret = PTR_ERR(qm_cgralloc);
+               dev_err(dev, "qman-cgralloc pool init failed (%d)\n", ret);
+               return ret;
+       }
+
+       ret = qman_resource_init(dev);
+       if (ret)
+               return ret;
+
+       ret = qman_alloc_fq_table(qm_get_fqid_maxcnt());
+       if (ret)
+               return ret;
+
+       ret = qman_wq_alloc();
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static const struct of_device_id fsl_qman_ids[] = {
+       {
+               .compatible = "fsl,qman",
+       },
+       {}
+};
+
+static struct platform_driver fsl_qman_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = fsl_qman_ids,
+               .suppress_bind_attrs = true,
+       },
+       .probe = fsl_qman_probe,
+};
+
+builtin_platform_driver(fsl_qman_driver);
diff --git a/drivers/soc/fsl/qbman/qman_portal.c b/drivers/soc/fsl/qbman/qman_portal.c
new file mode 100644 (file)
index 0000000..1486143
--- /dev/null
@@ -0,0 +1,355 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_priv.h"
+
+/* Enable portal interupts (as opposed to polling mode) */
+#define CONFIG_FSL_DPA_PIRQ_SLOW  1
+#define CONFIG_FSL_DPA_PIRQ_FAST  1
+
+static struct cpumask portal_cpus;
+/* protect qman global registers and global data shared among portals */
+static DEFINE_SPINLOCK(qman_lock);
+
+static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
+{
+#ifdef CONFIG_FSL_PAMU
+       struct device *dev = pcfg->dev;
+       int window_count = 1;
+       struct iommu_domain_geometry geom_attr;
+       struct pamu_stash_attribute stash_attr;
+       int ret;
+
+       pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
+       if (!pcfg->iommu_domain) {
+               dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
+               goto no_iommu;
+       }
+       geom_attr.aperture_start = 0;
+       geom_attr.aperture_end =
+               ((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
+       geom_attr.force_aperture = true;
+       ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
+                                   &geom_attr);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
+                                   &window_count);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       stash_attr.cpu = cpu;
+       stash_attr.cache = PAMU_ATTR_CACHE_L1;
+       ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                                   DOMAIN_ATTR_FSL_PAMU_STASH,
+                                   &stash_attr);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d",
+                       __func__, ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
+                                        IOMMU_READ | IOMMU_WRITE);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_window_enable() = %d",
+                       __func__, ret);
+               goto out_domain_free;
+       }
+       ret = iommu_attach_device(pcfg->iommu_domain, dev);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_device_attach() = %d", __func__,
+                       ret);
+               goto out_domain_free;
+       }
+       ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                                   DOMAIN_ATTR_FSL_PAMU_ENABLE,
+                                   &window_count);
+       if (ret < 0) {
+               dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
+                       ret);
+               goto out_detach_device;
+       }
+
+no_iommu:
+#endif
+       qman_set_sdest(pcfg->channel, cpu);
+
+       return;
+
+#ifdef CONFIG_FSL_PAMU
+out_detach_device:
+       iommu_detach_device(pcfg->iommu_domain, NULL);
+out_domain_free:
+       iommu_domain_free(pcfg->iommu_domain);
+       pcfg->iommu_domain = NULL;
+#endif
+}
+
+static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
+{
+       struct qman_portal *p;
+       u32 irq_sources = 0;
+
+       /* We need the same LIODN offset for all portals */
+       qman_liodn_fixup(pcfg->channel);
+
+       pcfg->iommu_domain = NULL;
+       portal_set_cpu(pcfg, pcfg->cpu);
+
+       p = qman_create_affine_portal(pcfg, NULL);
+       if (!p) {
+               dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
+                        __func__, pcfg->cpu);
+               return NULL;
+       }
+
+       /* Determine what should be interrupt-vs-poll driven */
+#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
+       irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
+                      QM_PIRQ_CSCI;
+#endif
+#ifdef CONFIG_FSL_DPA_PIRQ_FAST
+       irq_sources |= QM_PIRQ_DQRI;
+#endif
+       qman_p_irqsource_add(p, irq_sources);
+
+       spin_lock(&qman_lock);
+       if (cpumask_equal(&portal_cpus, cpu_possible_mask)) {
+               /* all assigned portals are initialized now */
+               qman_init_cgr_all();
+       }
+       spin_unlock(&qman_lock);
+
+       dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
+
+       return p;
+}
+
+static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
+                                                       unsigned int cpu)
+{
+#ifdef CONFIG_FSL_PAMU /* TODO */
+       struct pamu_stash_attribute stash_attr;
+       int ret;
+
+       if (pcfg->iommu_domain) {
+               stash_attr.cpu = cpu;
+               stash_attr.cache = PAMU_ATTR_CACHE_L1;
+               ret = iommu_domain_set_attr(pcfg->iommu_domain,
+                               DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr);
+               if (ret < 0) {
+                       dev_err(pcfg->dev,
+                               "Failed to update pamu stash setting\n");
+                       return;
+               }
+       }
+#endif
+       qman_set_sdest(pcfg->channel, cpu);
+}
+
+static void qman_offline_cpu(unsigned int cpu)
+{
+       struct qman_portal *p;
+       const struct qm_portal_config *pcfg;
+
+       p = affine_portals[cpu];
+       if (p) {
+               pcfg = qman_get_qm_portal_config(p);
+               if (pcfg) {
+                       irq_set_affinity(pcfg->irq, cpumask_of(0));
+                       qman_portal_update_sdest(pcfg, 0);
+               }
+       }
+}
+
+static void qman_online_cpu(unsigned int cpu)
+{
+       struct qman_portal *p;
+       const struct qm_portal_config *pcfg;
+
+       p = affine_portals[cpu];
+       if (p) {
+               pcfg = qman_get_qm_portal_config(p);
+               if (pcfg) {
+                       irq_set_affinity(pcfg->irq, cpumask_of(cpu));
+                       qman_portal_update_sdest(pcfg, cpu);
+               }
+       }
+}
+
+static int qman_hotplug_cpu_callback(struct notifier_block *nfb,
+                                    unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_ONLINE_FROZEN:
+               qman_online_cpu(cpu);
+               break;
+       case CPU_DOWN_PREPARE:
+       case CPU_DOWN_PREPARE_FROZEN:
+               qman_offline_cpu(cpu);
+       default:
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block qman_hotplug_cpu_notifier = {
+       .notifier_call = qman_hotplug_cpu_callback,
+};
+
+static int qman_portal_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct qm_portal_config *pcfg;
+       struct resource *addr_phys[2];
+       const u32 *channel;
+       void __iomem *va;
+       int irq, len, cpu;
+
+       pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
+       if (!pcfg)
+               return -ENOMEM;
+
+       pcfg->dev = dev;
+
+       addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CE);
+       if (!addr_phys[0]) {
+               dev_err(dev, "Can't get %s property 'reg::CE'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
+                                            DPAA_PORTAL_CI);
+       if (!addr_phys[1]) {
+               dev_err(dev, "Can't get %s property 'reg::CI'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+
+       channel = of_get_property(node, "cell-index", &len);
+       if (!channel || (len != 4)) {
+               dev_err(dev, "Can't get %s property 'cell-index'\n",
+                       node->full_name);
+               return -ENXIO;
+       }
+       pcfg->channel = *channel;
+       pcfg->cpu = -1;
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0) {
+               dev_err(dev, "Can't get %s IRQ\n", node->full_name);
+               return -ENXIO;
+       }
+       pcfg->irq = irq;
+
+       va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
+       if (!va)
+               goto err_ioremap1;
+
+       pcfg->addr_virt[DPAA_PORTAL_CE] = va;
+
+       va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
+                         _PAGE_GUARDED | _PAGE_NO_CACHE);
+       if (!va)
+               goto err_ioremap2;
+
+       pcfg->addr_virt[DPAA_PORTAL_CI] = va;
+
+       pcfg->pools = qm_get_pools_sdqcr();
+
+       spin_lock(&qman_lock);
+       cpu = cpumask_next_zero(-1, &portal_cpus);
+       if (cpu >= nr_cpu_ids) {
+               /* unassigned portal, skip init */
+               spin_unlock(&qman_lock);
+               return 0;
+       }
+
+       cpumask_set_cpu(cpu, &portal_cpus);
+       spin_unlock(&qman_lock);
+       pcfg->cpu = cpu;
+
+       if (!init_pcfg(pcfg))
+               goto err_ioremap2;
+
+       /* clear irq affinity if assigned cpu is offline */
+       if (!cpu_online(cpu))
+               qman_offline_cpu(cpu);
+
+       return 0;
+
+err_ioremap2:
+       iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
+err_ioremap1:
+       dev_err(dev, "ioremap failed\n");
+       return -ENXIO;
+}
+
+static const struct of_device_id qman_portal_ids[] = {
+       {
+               .compatible = "fsl,qman-portal",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, qman_portal_ids);
+
+static struct platform_driver qman_portal_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = qman_portal_ids,
+       },
+       .probe = qman_portal_probe,
+};
+
+static int __init qman_portal_driver_register(struct platform_driver *drv)
+{
+       int ret;
+
+       ret = platform_driver_register(drv);
+       if (ret < 0)
+               return ret;
+
+       register_hotcpu_notifier(&qman_hotplug_cpu_notifier);
+
+       return 0;
+}
+
+module_driver(qman_portal_driver,
+             qman_portal_driver_register, platform_driver_unregister);
diff --git a/drivers/soc/fsl/qbman/qman_priv.h b/drivers/soc/fsl/qbman/qman_priv.h
new file mode 100644 (file)
index 0000000..5cf821e
--- /dev/null
@@ -0,0 +1,371 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "dpaa_sys.h"
+
+#include <soc/fsl/qman.h>
+#include <linux/iommu.h>
+
+#if defined(CONFIG_FSL_PAMU)
+#include <asm/fsl_pamu_stash.h>
+#endif
+
+struct qm_mcr_querywq {
+       u8 verb;
+       u8 result;
+       u16 channel_wq; /* ignores wq (3 lsbits): _res[0-2] */
+       u8 __reserved[28];
+       u32 wq_len[8];
+} __packed;
+
+static inline u16 qm_mcr_querywq_get_chan(const struct qm_mcr_querywq *wq)
+{
+       return wq->channel_wq >> 3;
+}
+
+struct __qm_mcr_querycongestion {
+       u32 state[8];
+};
+
+/* "Query Congestion Group State" */
+struct qm_mcr_querycongestion {
+       u8 verb;
+       u8 result;
+       u8 __reserved[30];
+       /* Access this struct using qman_cgrs_get() */
+       struct __qm_mcr_querycongestion state;
+} __packed;
+
+/* "Query CGR" */
+struct qm_mcr_querycgr {
+       u8 verb;
+       u8 result;
+       u16 __reserved1;
+       struct __qm_mc_cgr cgr; /* CGR fields */
+       u8 __reserved2[6];
+       u8 i_bcnt_hi;   /* high 8-bits of 40-bit "Instant" */
+       u32 i_bcnt_lo;  /* low 32-bits of 40-bit */
+       u8 __reserved3[3];
+       u8 a_bcnt_hi;   /* high 8-bits of 40-bit "Average" */
+       u32 a_bcnt_lo;  /* low 32-bits of 40-bit */
+       u32 cscn_targ_swp[4];
+} __packed;
+
+static inline u64 qm_mcr_querycgr_i_get64(const struct qm_mcr_querycgr *q)
+{
+       return ((u64)q->i_bcnt_hi << 32) | (u64)q->i_bcnt_lo;
+}
+static inline u64 qm_mcr_querycgr_a_get64(const struct qm_mcr_querycgr *q)
+{
+       return ((u64)q->a_bcnt_hi << 32) | (u64)q->a_bcnt_lo;
+}
+
+/* "Query FQ Non-Programmable Fields" */
+struct qm_mcc_queryfq_np {
+       u8 _ncw_verb;
+       u8 __reserved1[3];
+       u32 fqid;       /* 24-bit */
+       u8 __reserved2[56];
+} __packed;
+
+struct qm_mcr_queryfq_np {
+       u8 verb;
+       u8 result;
+       u8 __reserved1;
+       u8 state;               /* QM_MCR_NP_STATE_*** */
+       u32 fqd_link;           /* 24-bit, _res2[24-31] */
+       u16 odp_seq;            /* 14-bit, _res3[14-15] */
+       u16 orp_nesn;           /* 14-bit, _res4[14-15] */
+       u16 orp_ea_hseq;        /* 15-bit, _res5[15] */
+       u16 orp_ea_tseq;        /* 15-bit, _res6[15] */
+       u32 orp_ea_hptr;        /* 24-bit, _res7[24-31] */
+       u32 orp_ea_tptr;        /* 24-bit, _res8[24-31] */
+       u32 pfdr_hptr;          /* 24-bit, _res9[24-31] */
+       u32 pfdr_tptr;          /* 24-bit, _res10[24-31] */
+       u8 __reserved2[5];
+       u8 is;                  /* 1-bit, _res12[1-7] */
+       u16 ics_surp;
+       u32 byte_cnt;
+       u32 frm_cnt;            /* 24-bit, _res13[24-31] */
+       u32 __reserved3;
+       u16 ra1_sfdr;           /* QM_MCR_NP_RA1_*** */
+       u16 ra2_sfdr;           /* QM_MCR_NP_RA2_*** */
+       u16 __reserved4;
+       u16 od1_sfdr;           /* QM_MCR_NP_OD1_*** */
+       u16 od2_sfdr;           /* QM_MCR_NP_OD2_*** */
+       u16 od3_sfdr;           /* QM_MCR_NP_OD3_*** */
+} __packed;
+
+#define QM_MCR_NP_STATE_FE             0x10
+#define QM_MCR_NP_STATE_R              0x08
+#define QM_MCR_NP_STATE_MASK           0x07    /* Reads FQD::STATE; */
+#define QM_MCR_NP_STATE_OOS            0x00
+#define QM_MCR_NP_STATE_RETIRED                0x01
+#define QM_MCR_NP_STATE_TEN_SCHED      0x02
+#define QM_MCR_NP_STATE_TRU_SCHED      0x03
+#define QM_MCR_NP_STATE_PARKED         0x04
+#define QM_MCR_NP_STATE_ACTIVE         0x05
+#define QM_MCR_NP_PTR_MASK             0x07ff  /* for RA[12] & OD[123] */
+#define QM_MCR_NP_RA1_NRA(v)           (((v) >> 14) & 0x3)     /* FQD::NRA */
+#define QM_MCR_NP_RA2_IT(v)            (((v) >> 14) & 0x1)     /* FQD::IT */
+#define QM_MCR_NP_OD1_NOD(v)           (((v) >> 14) & 0x3)     /* FQD::NOD */
+#define QM_MCR_NP_OD3_NPC(v)           (((v) >> 14) & 0x3)     /* FQD::NPC */
+
+enum qm_mcr_queryfq_np_masks {
+       qm_mcr_fqd_link_mask = BIT(24)-1,
+       qm_mcr_odp_seq_mask = BIT(14)-1,
+       qm_mcr_orp_nesn_mask = BIT(14)-1,
+       qm_mcr_orp_ea_hseq_mask = BIT(15)-1,
+       qm_mcr_orp_ea_tseq_mask = BIT(15)-1,
+       qm_mcr_orp_ea_hptr_mask = BIT(24)-1,
+       qm_mcr_orp_ea_tptr_mask = BIT(24)-1,
+       qm_mcr_pfdr_hptr_mask = BIT(24)-1,
+       qm_mcr_pfdr_tptr_mask = BIT(24)-1,
+       qm_mcr_is_mask = BIT(1)-1,
+       qm_mcr_frm_cnt_mask = BIT(24)-1,
+};
+#define qm_mcr_np_get(np, field) \
+       ((np)->field & (qm_mcr_##field##_mask))
+
+/* Congestion Groups */
+
+/*
+ * This wrapper represents a bit-array for the state of the 256 QMan congestion
+ * groups. Is also used as a *mask* for congestion groups, eg. so we ignore
+ * those that don't concern us. We harness the structure and accessor details
+ * already used in the management command to query congestion groups.
+ */
+#define CGR_BITS_PER_WORD 5
+#define CGR_WORD(x)    ((x) >> CGR_BITS_PER_WORD)
+#define CGR_BIT(x)     (BIT(31) >> ((x) & 0x1f))
+#define CGR_NUM        (sizeof(struct __qm_mcr_querycongestion) << 3)
+
+struct qman_cgrs {
+       struct __qm_mcr_querycongestion q;
+};
+
+static inline void qman_cgrs_init(struct qman_cgrs *c)
+{
+       memset(c, 0, sizeof(*c));
+}
+
+static inline void qman_cgrs_fill(struct qman_cgrs *c)
+{
+       memset(c, 0xff, sizeof(*c));
+}
+
+static inline int qman_cgrs_get(struct qman_cgrs *c, u8 cgr)
+{
+       return c->q.state[CGR_WORD(cgr)] & CGR_BIT(cgr);
+}
+
+static inline void qman_cgrs_cp(struct qman_cgrs *dest,
+                               const struct qman_cgrs *src)
+{
+       *dest = *src;
+}
+
+static inline void qman_cgrs_and(struct qman_cgrs *dest,
+                       const struct qman_cgrs *a, const struct qman_cgrs *b)
+{
+       int ret;
+       u32 *_d = dest->q.state;
+       const u32 *_a = a->q.state;
+       const u32 *_b = b->q.state;
+
+       for (ret = 0; ret < 8; ret++)
+               *_d++ = *_a++ & *_b++;
+}
+
+static inline void qman_cgrs_xor(struct qman_cgrs *dest,
+                       const struct qman_cgrs *a, const struct qman_cgrs *b)
+{
+       int ret;
+       u32 *_d = dest->q.state;
+       const u32 *_a = a->q.state;
+       const u32 *_b = b->q.state;
+
+       for (ret = 0; ret < 8; ret++)
+               *_d++ = *_a++ ^ *_b++;
+}
+
+void qman_init_cgr_all(void);
+
+struct qm_portal_config {
+       /*
+        * Corenet portal addresses;
+        * [0]==cache-enabled, [1]==cache-inhibited.
+        */
+       void __iomem *addr_virt[2];
+       struct device *dev;
+       struct iommu_domain *iommu_domain;
+       /* Allow these to be joined in lists */
+       struct list_head list;
+       /* User-visible portal configuration settings */
+       /* portal is affined to this cpu */
+       int cpu;
+       /* portal interrupt line */
+       int irq;
+       /*
+        * the portal's dedicated channel id, used initialising
+        * frame queues to target this portal when scheduled
+        */
+       u16 channel;
+       /*
+        * mask of pool channels this portal has dequeue access to
+        * (using QM_SDQCR_CHANNELS_POOL(n) for the bitmask)
+        */
+       u32 pools;
+};
+
+/* Revision info (for errata and feature handling) */
+#define QMAN_REV11 0x0101
+#define QMAN_REV12 0x0102
+#define QMAN_REV20 0x0200
+#define QMAN_REV30 0x0300
+#define QMAN_REV31 0x0301
+extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
+
+#define QM_FQID_RANGE_START 1 /* FQID 0 reserved for internal use */
+extern struct gen_pool *qm_fqalloc; /* FQID allocator */
+extern struct gen_pool *qm_qpalloc; /* pool-channel allocator */
+extern struct gen_pool *qm_cgralloc; /* CGR ID allocator */
+u32 qm_get_pools_sdqcr(void);
+
+int qman_wq_alloc(void);
+void qman_liodn_fixup(u16 channel);
+void qman_set_sdest(u16 channel, unsigned int cpu_idx);
+
+struct qman_portal *qman_create_affine_portal(
+                       const struct qm_portal_config *config,
+                       const struct qman_cgrs *cgrs);
+const struct qm_portal_config *qman_destroy_affine_portal(void);
+
+/*
+ * qman_query_fq - Queries FQD fields (via h/w query command)
+ * @fq: the frame queue object to be queried
+ * @fqd: storage for the queried FQD fields
+ */
+int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd);
+
+/*
+ * For qman_volatile_dequeue(); Choose one PRECEDENCE. EXACT is optional. Use
+ * NUMFRAMES(n) (6-bit) or NUMFRAMES_TILLEMPTY to fill in the frame-count. Use
+ * FQID(n) to fill in the frame queue ID.
+ */
+#define QM_VDQCR_PRECEDENCE_VDQCR      0x0
+#define QM_VDQCR_PRECEDENCE_SDQCR      0x80000000
+#define QM_VDQCR_EXACT                 0x40000000
+#define QM_VDQCR_NUMFRAMES_MASK                0x3f000000
+#define QM_VDQCR_NUMFRAMES_SET(n)      (((n) & 0x3f) << 24)
+#define QM_VDQCR_NUMFRAMES_GET(n)      (((n) >> 24) & 0x3f)
+#define QM_VDQCR_NUMFRAMES_TILLEMPTY   QM_VDQCR_NUMFRAMES_SET(0)
+
+#define QMAN_VOLATILE_FLAG_WAIT             0x00000001 /* wait if VDQCR is in use */
+#define QMAN_VOLATILE_FLAG_WAIT_INT  0x00000002 /* if wait, interruptible? */
+#define QMAN_VOLATILE_FLAG_FINISH    0x00000004 /* wait till VDQCR completes */
+
+/*
+ * qman_volatile_dequeue - Issue a volatile dequeue command
+ * @fq: the frame queue object to dequeue from
+ * @flags: a bit-mask of QMAN_VOLATILE_FLAG_*** options
+ * @vdqcr: bit mask of QM_VDQCR_*** options, as per qm_dqrr_vdqcr_set()
+ *
+ * Attempts to lock access to the portal's VDQCR volatile dequeue functionality.
+ * The function will block and sleep if QMAN_VOLATILE_FLAG_WAIT is specified and
+ * the VDQCR is already in use, otherwise returns non-zero for failure. If
+ * QMAN_VOLATILE_FLAG_FINISH is specified, the function will only return once
+ * the VDQCR command has finished executing (ie. once the callback for the last
+ * DQRR entry resulting from the VDQCR command has been called). If not using
+ * the FINISH flag, completion can be determined either by detecting the
+ * presence of the QM_DQRR_STAT_UNSCHEDULED and QM_DQRR_STAT_DQCR_EXPIRED bits
+ * in the "stat" parameter passed to the FQ's dequeue callback, or by waiting
+ * for the QMAN_FQ_STATE_VDQCR bit to disappear.
+ */
+int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr);
+
+int qman_alloc_fq_table(u32 num_fqids);
+
+/*   QMan s/w corenet portal, low-level i/face  */
+
+/*
+ * For qm_dqrr_sdqcr_set(); Choose one SOURCE. Choose one COUNT. Choose one
+ * dequeue TYPE. Choose TOKEN (8-bit).
+ * If SOURCE == CHANNELS,
+ *   Choose CHANNELS_DEDICATED and/or CHANNELS_POOL(n).
+ *   You can choose DEDICATED_PRECEDENCE if the portal channel should have
+ *   priority.
+ * If SOURCE == SPECIFICWQ,
+ *     Either select the work-queue ID with SPECIFICWQ_WQ(), or select the
+ *     channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the
+ *     work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the
+ *     same value.
+ */
+#define QM_SDQCR_SOURCE_CHANNELS       0x0
+#define QM_SDQCR_SOURCE_SPECIFICWQ     0x40000000
+#define QM_SDQCR_COUNT_EXACT1          0x0
+#define QM_SDQCR_COUNT_UPTO3           0x20000000
+#define QM_SDQCR_DEDICATED_PRECEDENCE  0x10000000
+#define QM_SDQCR_TYPE_MASK             0x03000000
+#define QM_SDQCR_TYPE_NULL             0x0
+#define QM_SDQCR_TYPE_PRIO_QOS         0x01000000
+#define QM_SDQCR_TYPE_ACTIVE_QOS       0x02000000
+#define QM_SDQCR_TYPE_ACTIVE           0x03000000
+#define QM_SDQCR_TOKEN_MASK            0x00ff0000
+#define QM_SDQCR_TOKEN_SET(v)          (((v) & 0xff) << 16)
+#define QM_SDQCR_TOKEN_GET(v)          (((v) >> 16) & 0xff)
+#define QM_SDQCR_CHANNELS_DEDICATED    0x00008000
+#define QM_SDQCR_SPECIFICWQ_MASK       0x000000f7
+#define QM_SDQCR_SPECIFICWQ_DEDICATED  0x00000000
+#define QM_SDQCR_SPECIFICWQ_POOL(n)    ((n) << 4)
+#define QM_SDQCR_SPECIFICWQ_WQ(n)      (n)
+
+/* For qm_dqrr_vdqcr_set(): use FQID(n) to fill in the frame queue ID */
+#define QM_VDQCR_FQID_MASK             0x00ffffff
+#define QM_VDQCR_FQID(n)               ((n) & QM_VDQCR_FQID_MASK)
+
+/*
+ * Used by all portal interrupt registers except 'inhibit'
+ * Channels with frame availability
+ */
+#define QM_PIRQ_DQAVAIL        0x0000ffff
+
+/* The DQAVAIL interrupt fields break down into these bits; */
+#define QM_DQAVAIL_PORTAL      0x8000          /* Portal channel */
+#define QM_DQAVAIL_POOL(n)     (0x8000 >> (n)) /* Pool channel, n==[1..15] */
+#define QM_DQAVAIL_MASK                0xffff
+/* This mask contains all the "irqsource" bits visible to API users */
+#define QM_PIRQ_VISIBLE        (QM_PIRQ_SLOW | QM_PIRQ_DQRI)
+
+extern struct qman_portal *affine_portals[NR_CPUS];
+const struct qm_portal_config *qman_get_qm_portal_config(
+                                               struct qman_portal *portal);
diff --git a/drivers/soc/fsl/qbman/qman_test.c b/drivers/soc/fsl/qbman/qman_test.c
new file mode 100644 (file)
index 0000000..18f7f02
--- /dev/null
@@ -0,0 +1,62 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_test.h"
+
+MODULE_AUTHOR("Geoff Thorpe");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("QMan testing");
+
+static int test_init(void)
+{
+       int loop = 1;
+       int err = 0;
+
+       while (loop--) {
+#ifdef CONFIG_FSL_QMAN_TEST_STASH
+               err = qman_test_stash();
+               if (err)
+                       break;
+#endif
+#ifdef CONFIG_FSL_QMAN_TEST_API
+               err = qman_test_api();
+               if (err)
+                       break;
+#endif
+       }
+       return err;
+}
+
+static void test_exit(void)
+{
+}
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/drivers/soc/fsl/qbman/qman_test.h b/drivers/soc/fsl/qbman/qman_test.h
new file mode 100644 (file)
index 0000000..d5f8cb2
--- /dev/null
@@ -0,0 +1,36 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_priv.h"
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+int qman_test_stash(void);
+int qman_test_api(void);
diff --git a/drivers/soc/fsl/qbman/qman_test_api.c b/drivers/soc/fsl/qbman/qman_test_api.c
new file mode 100644 (file)
index 0000000..6880ff1
--- /dev/null
@@ -0,0 +1,252 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_test.h"
+
+#define CGR_ID         27
+#define POOL_ID                2
+#define FQ_FLAGS       QMAN_FQ_FLAG_DYNAMIC_FQID
+#define NUM_ENQUEUES   10
+#define NUM_PARTIAL    4
+#define PORTAL_SDQCR   (QM_SDQCR_SOURCE_CHANNELS | \
+                       QM_SDQCR_TYPE_PRIO_QOS | \
+                       QM_SDQCR_TOKEN_SET(0x98) | \
+                       QM_SDQCR_CHANNELS_DEDICATED | \
+                       QM_SDQCR_CHANNELS_POOL(POOL_ID))
+#define PORTAL_OPAQUE  ((void *)0xf00dbeef)
+#define VDQCR_FLAGS    (QMAN_VOLATILE_FLAG_WAIT | QMAN_VOLATILE_FLAG_FINISH)
+
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *,
+                                       struct qman_fq *,
+                                       const struct qm_dqrr_entry *);
+static void cb_ern(struct qman_portal *, struct qman_fq *,
+                  const union qm_mr_entry *);
+static void cb_fqs(struct qman_portal *, struct qman_fq *,
+                  const union qm_mr_entry *);
+
+static struct qm_fd fd, fd_dq;
+static struct qman_fq fq_base = {
+       .cb.dqrr = cb_dqrr,
+       .cb.ern = cb_ern,
+       .cb.fqs = cb_fqs
+};
+static DECLARE_WAIT_QUEUE_HEAD(waitqueue);
+static int retire_complete, sdqcr_complete;
+
+/* Helpers for initialising and "incrementing" a frame descriptor */
+static void fd_init(struct qm_fd *fd)
+{
+       qm_fd_addr_set64(fd, 0xabdeadbeefLLU);
+       qm_fd_set_contig_big(fd, 0x0000ffff);
+       fd->cmd = 0xfeedf00d;
+}
+
+static void fd_inc(struct qm_fd *fd)
+{
+       u64 t = qm_fd_addr_get64(fd);
+       int z = t >> 40;
+       unsigned int len, off;
+       enum qm_fd_format fmt;
+
+       t <<= 1;
+       if (z)
+               t |= 1;
+       qm_fd_addr_set64(fd, t);
+
+       fmt = qm_fd_get_format(fd);
+       off = qm_fd_get_offset(fd);
+       len = qm_fd_get_length(fd);
+       len--;
+       qm_fd_set_param(fd, fmt, off, len);
+
+       fd->cmd++;
+}
+
+/* The only part of the 'fd' we can't memcmp() is the ppid */
+static int fd_cmp(const struct qm_fd *a, const struct qm_fd *b)
+{
+       int r = (qm_fd_addr_get64(a) == qm_fd_addr_get64(b)) ? 0 : -1;
+
+       if (!r) {
+               enum qm_fd_format fmt_a, fmt_b;
+
+               fmt_a = qm_fd_get_format(a);
+               fmt_b = qm_fd_get_format(b);
+               r = fmt_a - fmt_b;
+       }
+       if (!r)
+               r = a->cfg - b->cfg;
+       if (!r)
+               r = a->cmd - b->cmd;
+       return r;
+}
+
+/* test */
+static int do_enqueues(struct qman_fq *fq)
+{
+       unsigned int loop;
+       int err = 0;
+
+       for (loop = 0; loop < NUM_ENQUEUES; loop++) {
+               if (qman_enqueue(fq, &fd)) {
+                       pr_crit("qman_enqueue() failed\n");
+                       err = -EIO;
+               }
+               fd_inc(&fd);
+       }
+
+       return err;
+}
+
+int qman_test_api(void)
+{
+       unsigned int flags, frmcnt;
+       int err;
+       struct qman_fq *fq = &fq_base;
+
+       pr_info("%s(): Starting\n", __func__);
+       fd_init(&fd);
+       fd_init(&fd_dq);
+
+       /* Initialise (parked) FQ */
+       err = qman_create_fq(0, FQ_FLAGS, fq);
+       if (err) {
+               pr_crit("qman_create_fq() failed\n");
+               goto failed;
+       }
+       err = qman_init_fq(fq, QMAN_INITFQ_FLAG_LOCAL, NULL);
+       if (err) {
+               pr_crit("qman_init_fq() failed\n");
+               goto failed;
+       }
+       /* Do enqueues + VDQCR, twice. (Parked FQ) */
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("VDQCR (till-empty);\n");
+       frmcnt = QM_VDQCR_NUMFRAMES_TILLEMPTY;
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_crit("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("VDQCR (%d of %d);\n", NUM_PARTIAL, NUM_ENQUEUES);
+       frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_PARTIAL);
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_crit("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+       pr_info("VDQCR (%d of %d);\n", NUM_ENQUEUES - NUM_PARTIAL,
+               NUM_ENQUEUES);
+       frmcnt = QM_VDQCR_NUMFRAMES_SET(NUM_ENQUEUES - NUM_PARTIAL);
+       err = qman_volatile_dequeue(fq, VDQCR_FLAGS, frmcnt);
+       if (err) {
+               pr_err("qman_volatile_dequeue() failed\n");
+               goto failed;
+       }
+
+       err = do_enqueues(fq);
+       if (err)
+               goto failed;
+       pr_info("scheduled dequeue (till-empty)\n");
+       err = qman_schedule_fq(fq);
+       if (err) {
+               pr_crit("qman_schedule_fq() failed\n");
+               goto failed;
+       }
+       wait_event(waitqueue, sdqcr_complete);
+
+       /* Retire and OOS the FQ */
+       err = qman_retire_fq(fq, &flags);
+       if (err < 0) {
+               pr_crit("qman_retire_fq() failed\n");
+               goto failed;
+       }
+       wait_event(waitqueue, retire_complete);
+       if (flags & QMAN_FQ_STATE_BLOCKOOS) {
+               err = -EIO;
+               pr_crit("leaking frames\n");
+               goto failed;
+       }
+       err = qman_oos_fq(fq);
+       if (err) {
+               pr_crit("qman_oos_fq() failed\n");
+               goto failed;
+       }
+       qman_destroy_fq(fq);
+       pr_info("%s(): Finished\n", __func__);
+       return 0;
+
+failed:
+       WARN_ON(1);
+       return err;
+}
+
+static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *p,
+                                       struct qman_fq *fq,
+                                       const struct qm_dqrr_entry *dq)
+{
+       if (WARN_ON(fd_cmp(&fd_dq, &dq->fd))) {
+               pr_err("BADNESS: dequeued frame doesn't match;\n");
+               return qman_cb_dqrr_consume;
+       }
+       fd_inc(&fd_dq);
+       if (!(dq->stat & QM_DQRR_STAT_UNSCHEDULED) && !fd_cmp(&fd_dq, &fd)) {
+               sdqcr_complete = 1;
+               wake_up(&waitqueue);
+       }
+       return qman_cb_dqrr_consume;
+}
+
+static void cb_ern(struct qman_portal *p, struct qman_fq *fq,
+                  const union qm_mr_entry *msg)
+{
+       pr_crit("cb_ern() unimplemented");
+       WARN_ON(1);
+}
+
+static void cb_fqs(struct qman_portal *p, struct qman_fq *fq,
+                  const union qm_mr_entry *msg)
+{
+       u8 verb = (msg->verb & QM_MR_VERB_TYPE_MASK);
+
+       if ((verb != QM_MR_VERB_FQRN) && (verb != QM_MR_VERB_FQRNI)) {
+               pr_crit("unexpected FQS message");
+               WARN_ON(1);
+               return;
+       }
+       pr_info("Retirement message received\n");
+       retire_complete = 1;
+       wake_up(&waitqueue);
+}
diff --git a/drivers/soc/fsl/qbman/qman_test_stash.c b/drivers/soc/fsl/qbman/qman_test_stash.c
new file mode 100644 (file)
index 0000000..43cf66b
--- /dev/null
@@ -0,0 +1,617 @@
+/* Copyright 2009 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qman_test.h"
+
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+
+/*
+ * Algorithm:
+ *
+ * Each cpu will have HP_PER_CPU "handlers" set up, each of which incorporates
+ * an rx/tx pair of FQ objects (both of which are stashed on dequeue). The
+ * organisation of FQIDs is such that the HP_PER_CPU*NUM_CPUS handlers will
+ * shuttle a "hot potato" frame around them such that every forwarding action
+ * moves it from one cpu to another. (The use of more than one handler per cpu
+ * is to allow enough handlers/FQs to truly test the significance of caching -
+ * ie. when cache-expiries are occurring.)
+ *
+ * The "hot potato" frame content will be HP_NUM_WORDS*4 bytes in size, and the
+ * first and last words of the frame data will undergo a transformation step on
+ * each forwarding action. To achieve this, each handler will be assigned a
+ * 32-bit "mixer", that is produced using a 32-bit LFSR. When a frame is
+ * received by a handler, the mixer of the expected sender is XOR'd into all
+ * words of the entire frame, which is then validated against the original
+ * values. Then, before forwarding, the entire frame is XOR'd with the mixer of
+ * the current handler. Apart from validating that the frame is taking the
+ * expected path, this also provides some quasi-realistic overheads to each
+ * forwarding action - dereferencing *all* the frame data, computation, and
+ * conditional branching. There is a "special" handler designated to act as the
+ * instigator of the test by creating an enqueuing the "hot potato" frame, and
+ * to determine when the test has completed by counting HP_LOOPS iterations.
+ *
+ * Init phases:
+ *
+ * 1. prepare each cpu's 'hp_cpu' struct using on_each_cpu(,,1) and link them
+ *    into 'hp_cpu_list'. Specifically, set processor_id, allocate HP_PER_CPU
+ *    handlers and link-list them (but do no other handler setup).
+ *
+ * 2. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
+ *    hp_cpu's 'iterator' to point to its first handler. With each loop,
+ *    allocate rx/tx FQIDs and mixer values to the hp_cpu's iterator handler
+ *    and advance the iterator for the next loop. This includes a final fixup,
+ *    which connects the last handler to the first (and which is why phase 2
+ *    and 3 are separate).
+ *
+ * 3. scan over 'hp_cpu_list' HP_PER_CPU times, the first time sets each
+ *    hp_cpu's 'iterator' to point to its first handler. With each loop,
+ *    initialise FQ objects and advance the iterator for the next loop.
+ *    Moreover, do this initialisation on the cpu it applies to so that Rx FQ
+ *    initialisation targets the correct cpu.
+ */
+
+/*
+ * helper to run something on all cpus (can't use on_each_cpu(), as that invokes
+ * the fn from irq context, which is too restrictive).
+ */
+struct bstrap {
+       int (*fn)(void);
+       atomic_t started;
+};
+static int bstrap_fn(void *bs)
+{
+       struct bstrap *bstrap = bs;
+       int err;
+
+       atomic_inc(&bstrap->started);
+       err = bstrap->fn();
+       if (err)
+               return err;
+       while (!kthread_should_stop())
+               msleep(20);
+       return 0;
+}
+static int on_all_cpus(int (*fn)(void))
+{
+       int cpu;
+
+       for_each_cpu(cpu, cpu_online_mask) {
+               struct bstrap bstrap = {
+                       .fn = fn,
+                       .started = ATOMIC_INIT(0)
+               };
+               struct task_struct *k = kthread_create(bstrap_fn, &bstrap,
+                       "hotpotato%d", cpu);
+               int ret;
+
+               if (IS_ERR(k))
+                       return -ENOMEM;
+               kthread_bind(k, cpu);
+               wake_up_process(k);
+               /*
+                * If we call kthread_stop() before the "wake up" has had an
+                * effect, then the thread may exit with -EINTR without ever
+                * running the function. So poll until it's started before
+                * requesting it to stop.
+                */
+               while (!atomic_read(&bstrap.started))
+                       msleep(20);
+               ret = kthread_stop(k);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+struct hp_handler {
+
+       /* The following data is stashed when 'rx' is dequeued; */
+       /* -------------- */
+       /* The Rx FQ, dequeues of which will stash the entire hp_handler */
+       struct qman_fq rx;
+       /* The Tx FQ we should forward to */
+       struct qman_fq tx;
+       /* The value we XOR post-dequeue, prior to validating */
+       u32 rx_mixer;
+       /* The value we XOR pre-enqueue, after validating */
+       u32 tx_mixer;
+       /* what the hotpotato address should be on dequeue */
+       dma_addr_t addr;
+       u32 *frame_ptr;
+
+       /* The following data isn't (necessarily) stashed on dequeue; */
+       /* -------------- */
+       u32 fqid_rx, fqid_tx;
+       /* list node for linking us into 'hp_cpu' */
+       struct list_head node;
+       /* Just to check ... */
+       unsigned int processor_id;
+} ____cacheline_aligned;
+
+struct hp_cpu {
+       /* identify the cpu we run on; */
+       unsigned int processor_id;
+       /* root node for the per-cpu list of handlers */
+       struct list_head handlers;
+       /* list node for linking us into 'hp_cpu_list' */
+       struct list_head node;
+       /*
+        * when repeatedly scanning 'hp_list', each time linking the n'th
+        * handlers together, this is used as per-cpu iterator state
+        */
+       struct hp_handler *iterator;
+};
+
+/* Each cpu has one of these */
+static DEFINE_PER_CPU(struct hp_cpu, hp_cpus);
+
+/* links together the hp_cpu structs, in first-come first-serve order. */
+static LIST_HEAD(hp_cpu_list);
+static spinlock_t hp_lock = __SPIN_LOCK_UNLOCKED(hp_lock);
+
+static unsigned int hp_cpu_list_length;
+
+/* the "special" handler, that starts and terminates the test. */
+static struct hp_handler *special_handler;
+static int loop_counter;
+
+/* handlers are allocated out of this, so they're properly aligned. */
+static struct kmem_cache *hp_handler_slab;
+
+/* this is the frame data */
+static void *__frame_ptr;
+static u32 *frame_ptr;
+static dma_addr_t frame_dma;
+
+/* the main function waits on this */
+static DECLARE_WAIT_QUEUE_HEAD(queue);
+
+#define HP_PER_CPU     2
+#define HP_LOOPS       8
+/* 80 bytes, like a small ethernet frame, and bleeds into a second cacheline */
+#define HP_NUM_WORDS   80
+/* First word of the LFSR-based frame data */
+#define HP_FIRST_WORD  0xabbaf00d
+
+static inline u32 do_lfsr(u32 prev)
+{
+       return (prev >> 1) ^ (-(prev & 1u) & 0xd0000001u);
+}
+
+static int allocate_frame_data(void)
+{
+       u32 lfsr = HP_FIRST_WORD;
+       int loop;
+       struct platform_device *pdev = platform_device_alloc("foobar", -1);
+
+       if (!pdev) {
+               pr_crit("platform_device_alloc() failed");
+               return -EIO;
+       }
+       if (platform_device_add(pdev)) {
+               pr_crit("platform_device_add() failed");
+               return -EIO;
+       }
+       __frame_ptr = kmalloc(4 * HP_NUM_WORDS, GFP_KERNEL);
+       if (!__frame_ptr)
+               return -ENOMEM;
+
+       frame_ptr = PTR_ALIGN(__frame_ptr, 64);
+       for (loop = 0; loop < HP_NUM_WORDS; loop++) {
+               frame_ptr[loop] = lfsr;
+               lfsr = do_lfsr(lfsr);
+       }
+       frame_dma = dma_map_single(&pdev->dev, frame_ptr, 4 * HP_NUM_WORDS,
+                                  DMA_BIDIRECTIONAL);
+       platform_device_del(pdev);
+       platform_device_put(pdev);
+       return 0;
+}
+
+static void deallocate_frame_data(void)
+{
+       kfree(__frame_ptr);
+}
+
+static inline int process_frame_data(struct hp_handler *handler,
+                                    const struct qm_fd *fd)
+{
+       u32 *p = handler->frame_ptr;
+       u32 lfsr = HP_FIRST_WORD;
+       int loop;
+
+       if (qm_fd_addr_get64(fd) != handler->addr) {
+               pr_crit("bad frame address");
+               return -EIO;
+       }
+       for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
+               *p ^= handler->rx_mixer;
+               if (*p != lfsr) {
+                       pr_crit("corrupt frame data");
+                       return -EIO;
+               }
+               *p ^= handler->tx_mixer;
+               lfsr = do_lfsr(lfsr);
+       }
+       return 0;
+}
+
+static enum qman_cb_dqrr_result normal_dqrr(struct qman_portal *portal,
+                                           struct qman_fq *fq,
+                                           const struct qm_dqrr_entry *dqrr)
+{
+       struct hp_handler *handler = (struct hp_handler *)fq;
+
+       if (process_frame_data(handler, &dqrr->fd)) {
+               WARN_ON(1);
+               goto skip;
+       }
+       if (qman_enqueue(&handler->tx, &dqrr->fd)) {
+               pr_crit("qman_enqueue() failed");
+               WARN_ON(1);
+       }
+skip:
+       return qman_cb_dqrr_consume;
+}
+
+static enum qman_cb_dqrr_result special_dqrr(struct qman_portal *portal,
+                                            struct qman_fq *fq,
+                                            const struct qm_dqrr_entry *dqrr)
+{
+       struct hp_handler *handler = (struct hp_handler *)fq;
+
+       process_frame_data(handler, &dqrr->fd);
+       if (++loop_counter < HP_LOOPS) {
+               if (qman_enqueue(&handler->tx, &dqrr->fd)) {
+                       pr_crit("qman_enqueue() failed");
+                       WARN_ON(1);
+                       goto skip;
+               }
+       } else {
+               pr_info("Received final (%dth) frame\n", loop_counter);
+               wake_up(&queue);
+       }
+skip:
+       return qman_cb_dqrr_consume;
+}
+
+static int create_per_cpu_handlers(void)
+{
+       struct hp_handler *handler;
+       int loop;
+       struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus);
+
+       hp_cpu->processor_id = smp_processor_id();
+       spin_lock(&hp_lock);
+       list_add_tail(&hp_cpu->node, &hp_cpu_list);
+       hp_cpu_list_length++;
+       spin_unlock(&hp_lock);
+       INIT_LIST_HEAD(&hp_cpu->handlers);
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               handler = kmem_cache_alloc(hp_handler_slab, GFP_KERNEL);
+               if (!handler) {
+                       pr_crit("kmem_cache_alloc() failed");
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               handler->processor_id = hp_cpu->processor_id;
+               handler->addr = frame_dma;
+               handler->frame_ptr = frame_ptr;
+               list_add_tail(&handler->node, &hp_cpu->handlers);
+       }
+       return 0;
+}
+
+static int destroy_per_cpu_handlers(void)
+{
+       struct list_head *loop, *tmp;
+       struct hp_cpu *hp_cpu = this_cpu_ptr(&hp_cpus);
+
+       spin_lock(&hp_lock);
+       list_del(&hp_cpu->node);
+       spin_unlock(&hp_lock);
+       list_for_each_safe(loop, tmp, &hp_cpu->handlers) {
+               u32 flags = 0;
+               struct hp_handler *handler = list_entry(loop, struct hp_handler,
+                                                       node);
+               if (qman_retire_fq(&handler->rx, &flags) ||
+                   (flags & QMAN_FQ_STATE_BLOCKOOS)) {
+                       pr_crit("qman_retire_fq(rx) failed, flags: %x", flags);
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               if (qman_oos_fq(&handler->rx)) {
+                       pr_crit("qman_oos_fq(rx) failed");
+                       WARN_ON(1);
+                       return -EIO;
+               }
+               qman_destroy_fq(&handler->rx);
+               qman_destroy_fq(&handler->tx);
+               qman_release_fqid(handler->fqid_rx);
+               list_del(&handler->node);
+               kmem_cache_free(hp_handler_slab, handler);
+       }
+       return 0;
+}
+
+static inline u8 num_cachelines(u32 offset)
+{
+       u8 res = (offset + (L1_CACHE_BYTES - 1))
+                        / (L1_CACHE_BYTES);
+       if (res > 3)
+               return 3;
+       return res;
+}
+#define STASH_DATA_CL \
+       num_cachelines(HP_NUM_WORDS * 4)
+#define STASH_CTX_CL \
+       num_cachelines(offsetof(struct hp_handler, fqid_rx))
+
+static int init_handler(void *h)
+{
+       struct qm_mcc_initfq opts;
+       struct hp_handler *handler = h;
+       int err;
+
+       if (handler->processor_id != smp_processor_id()) {
+               err = -EIO;
+               goto failed;
+       }
+       /* Set up rx */
+       memset(&handler->rx, 0, sizeof(handler->rx));
+       if (handler == special_handler)
+               handler->rx.cb.dqrr = special_dqrr;
+       else
+               handler->rx.cb.dqrr = normal_dqrr;
+       err = qman_create_fq(handler->fqid_rx, 0, &handler->rx);
+       if (err) {
+               pr_crit("qman_create_fq(rx) failed");
+               goto failed;
+       }
+       memset(&opts, 0, sizeof(opts));
+       opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_CONTEXTA;
+       opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING;
+       qm_fqd_set_stashing(&opts.fqd, 0, STASH_DATA_CL, STASH_CTX_CL);
+       err = qman_init_fq(&handler->rx, QMAN_INITFQ_FLAG_SCHED |
+                          QMAN_INITFQ_FLAG_LOCAL, &opts);
+       if (err) {
+               pr_crit("qman_init_fq(rx) failed");
+               goto failed;
+       }
+       /* Set up tx */
+       memset(&handler->tx, 0, sizeof(handler->tx));
+       err = qman_create_fq(handler->fqid_tx, QMAN_FQ_FLAG_NO_MODIFY,
+                            &handler->tx);
+       if (err) {
+               pr_crit("qman_create_fq(tx) failed");
+               goto failed;
+       }
+
+       return 0;
+failed:
+       return err;
+}
+
+static void init_handler_cb(void *h)
+{
+       if (init_handler(h))
+               WARN_ON(1);
+}
+
+static int init_phase2(void)
+{
+       int loop;
+       u32 fqid = 0;
+       u32 lfsr = 0xdeadbeef;
+       struct hp_cpu *hp_cpu;
+       struct hp_handler *handler;
+
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
+                       int err;
+
+                       if (!loop)
+                               hp_cpu->iterator = list_first_entry(
+                                               &hp_cpu->handlers,
+                                               struct hp_handler, node);
+                       else
+                               hp_cpu->iterator = list_entry(
+                                               hp_cpu->iterator->node.next,
+                                               struct hp_handler, node);
+                       /* Rx FQID is the previous handler's Tx FQID */
+                       hp_cpu->iterator->fqid_rx = fqid;
+                       /* Allocate new FQID for Tx */
+                       err = qman_alloc_fqid(&fqid);
+                       if (err) {
+                               pr_crit("qman_alloc_fqid() failed");
+                               return err;
+                       }
+                       hp_cpu->iterator->fqid_tx = fqid;
+                       /* Rx mixer is the previous handler's Tx mixer */
+                       hp_cpu->iterator->rx_mixer = lfsr;
+                       /* Get new mixer for Tx */
+                       lfsr = do_lfsr(lfsr);
+                       hp_cpu->iterator->tx_mixer = lfsr;
+               }
+       }
+       /* Fix up the first handler (fqid_rx==0, rx_mixer=0xdeadbeef) */
+       hp_cpu = list_first_entry(&hp_cpu_list, struct hp_cpu, node);
+       handler = list_first_entry(&hp_cpu->handlers, struct hp_handler, node);
+       if (handler->fqid_rx != 0 || handler->rx_mixer != 0xdeadbeef)
+               return 1;
+       handler->fqid_rx = fqid;
+       handler->rx_mixer = lfsr;
+       /* and tag it as our "special" handler */
+       special_handler = handler;
+       return 0;
+}
+
+static int init_phase3(void)
+{
+       int loop, err;
+       struct hp_cpu *hp_cpu;
+
+       for (loop = 0; loop < HP_PER_CPU; loop++) {
+               list_for_each_entry(hp_cpu, &hp_cpu_list, node) {
+                       if (!loop)
+                               hp_cpu->iterator = list_first_entry(
+                                               &hp_cpu->handlers,
+                                               struct hp_handler, node);
+                       else
+                               hp_cpu->iterator = list_entry(
+                                               hp_cpu->iterator->node.next,
+                                               struct hp_handler, node);
+                       preempt_disable();
+                       if (hp_cpu->processor_id == smp_processor_id()) {
+                               err = init_handler(hp_cpu->iterator);
+                               if (err)
+                                       return err;
+                       } else {
+                               smp_call_function_single(hp_cpu->processor_id,
+                                       init_handler_cb, hp_cpu->iterator, 1);
+                       }
+                       preempt_enable();
+               }
+       }
+       return 0;
+}
+
+static int send_first_frame(void *ignore)
+{
+       u32 *p = special_handler->frame_ptr;
+       u32 lfsr = HP_FIRST_WORD;
+       int loop, err;
+       struct qm_fd fd;
+
+       if (special_handler->processor_id != smp_processor_id()) {
+               err = -EIO;
+               goto failed;
+       }
+       memset(&fd, 0, sizeof(fd));
+       qm_fd_addr_set64(&fd, special_handler->addr);
+       qm_fd_set_contig_big(&fd, HP_NUM_WORDS * 4);
+       for (loop = 0; loop < HP_NUM_WORDS; loop++, p++) {
+               if (*p != lfsr) {
+                       err = -EIO;
+                       pr_crit("corrupt frame data");
+                       goto failed;
+               }
+               *p ^= special_handler->tx_mixer;
+               lfsr = do_lfsr(lfsr);
+       }
+       pr_info("Sending first frame\n");
+       err = qman_enqueue(&special_handler->tx, &fd);
+       if (err) {
+               pr_crit("qman_enqueue() failed");
+               goto failed;
+       }
+
+       return 0;
+failed:
+       return err;
+}
+
+static void send_first_frame_cb(void *ignore)
+{
+       if (send_first_frame(NULL))
+               WARN_ON(1);
+}
+
+int qman_test_stash(void)
+{
+       int err;
+
+       if (cpumask_weight(cpu_online_mask) < 2) {
+               pr_info("%s(): skip - only 1 CPU\n", __func__);
+               return 0;
+       }
+
+       pr_info("%s(): Starting\n", __func__);
+
+       hp_cpu_list_length = 0;
+       loop_counter = 0;
+       hp_handler_slab = kmem_cache_create("hp_handler_slab",
+                       sizeof(struct hp_handler), L1_CACHE_BYTES,
+                       SLAB_HWCACHE_ALIGN, NULL);
+       if (!hp_handler_slab) {
+               err = -EIO;
+               pr_crit("kmem_cache_create() failed");
+               goto failed;
+       }
+
+       err = allocate_frame_data();
+       if (err)
+               goto failed;
+
+       /* Init phase 1 */
+       pr_info("Creating %d handlers per cpu...\n", HP_PER_CPU);
+       if (on_all_cpus(create_per_cpu_handlers)) {
+               err = -EIO;
+               pr_crit("on_each_cpu() failed");
+               goto failed;
+       }
+       pr_info("Number of cpus: %d, total of %d handlers\n",
+               hp_cpu_list_length, hp_cpu_list_length * HP_PER_CPU);
+
+       err = init_phase2();
+       if (err)
+               goto failed;
+
+       err = init_phase3();
+       if (err)
+               goto failed;
+
+       preempt_disable();
+       if (special_handler->processor_id == smp_processor_id()) {
+               err = send_first_frame(NULL);
+               if (err)
+                       goto failed;
+       } else {
+               smp_call_function_single(special_handler->processor_id,
+                                        send_first_frame_cb, NULL, 1);
+       }
+       preempt_enable();
+
+       wait_event(queue, loop_counter == HP_LOOPS);
+       deallocate_frame_data();
+       if (on_all_cpus(destroy_per_cpu_handlers)) {
+               err = -EIO;
+               pr_crit("on_each_cpu() failed");
+               goto failed;
+       }
+       kmem_cache_destroy(hp_handler_slab);
+       pr_info("%s(): Finished\n", __func__);
+
+       return 0;
+failed:
+       WARN_ON(1);
+       return err;
+}
index 333eb22..0aaf429 100644 (file)
@@ -41,7 +41,8 @@ struct qe_gpio_chip {
 
 static void qe_gpio_save_regs(struct of_mm_gpio_chip *mm_gc)
 {
-       struct qe_gpio_chip *qe_gc = gpiochip_get_data(&mm_gc->gc);
+       struct qe_gpio_chip *qe_gc =
+               container_of(mm_gc, struct qe_gpio_chip, mm_gc);
        struct qe_pio_regs __iomem *regs = mm_gc->regs;
 
        qe_gc->cpdata = in_be32(&regs->cpdata);
index 7026507..2707a82 100644 (file)
@@ -69,8 +69,8 @@ static phys_addr_t qebase = -1;
 phys_addr_t get_qe_base(void)
 {
        struct device_node *qe;
-       int size;
-       const u32 *prop;
+       int ret;
+       struct resource res;
 
        if (qebase != -1)
                return qebase;
@@ -82,9 +82,9 @@ phys_addr_t get_qe_base(void)
                        return qebase;
        }
 
-       prop = of_get_property(qe, "reg", &size);
-       if (prop && size >= sizeof(*prop))
-               qebase = of_translate_address(qe, prop);
+       ret = of_address_to_resource(qe, 0, &res);
+       if (!ret)
+               qebase = res.start;
        of_node_put(qe);
 
        return qebase;
index 41eff80..104e68d 100644 (file)
@@ -70,6 +70,11 @@ int cpm_muram_init(void)
        }
 
        muram_pool = gen_pool_create(0, -1);
+       if (!muram_pool) {
+               pr_err("Cannot allocate memory pool for CPM/QE muram");
+               ret = -ENOMEM;
+               goto out_muram;
+       }
        muram_pbase = of_translate_address(np, zero);
        if (muram_pbase == (phys_addr_t)OF_BAD_ADDR) {
                pr_err("Cannot translate zero through CPM muram node");
@@ -116,6 +121,9 @@ static unsigned long cpm_muram_alloc_common(unsigned long size,
        struct muram_block *entry;
        unsigned long start;
 
+       if (!muram_pool && cpm_muram_init())
+               goto out2;
+
        start = gen_pool_alloc_algo(muram_pool, size, algo, data);
        if (!start)
                goto out2;
index 5e48b14..a1048b4 100644 (file)
@@ -99,7 +99,7 @@ int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
        utdm->tdm_port = val;
        ut_info->uf_info.tdm_num = utdm->tdm_port;
 
-       if (of_get_property(np, "fsl,tdm-internal-loopback", NULL))
+       if (of_property_read_bool(np, "fsl,tdm-internal-loopback"))
                utdm->tdm_mode = TDM_INTERNAL_LOOPBACK;
        else
                utdm->tdm_mode = TDM_NORMAL;
@@ -167,7 +167,7 @@ int ucc_of_parse_tdm(struct device_node *np, struct ucc_tdm *utdm,
        }
 
        if (siram_init_flag == 0) {
-               memset_io(utdm->siram, 0,  res->end - res->start + 1);
+               memset_io(utdm->siram, 0,  resource_size(res));
                siram_init_flag = 1;
        }
 
index 8146ccd..5787b72 100644 (file)
@@ -1112,7 +1112,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
 
        /* If another context is idling the device then defer */
        if (master->idling) {
-               queue_kthread_work(&master->kworker, &master->pump_messages);
+               kthread_queue_work(&master->kworker, &master->pump_messages);
                spin_unlock_irqrestore(&master->queue_lock, flags);
                return;
        }
@@ -1126,7 +1126,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
 
                /* Only do teardown in the thread */
                if (!in_kthread) {
-                       queue_kthread_work(&master->kworker,
+                       kthread_queue_work(&master->kworker,
                                           &master->pump_messages);
                        spin_unlock_irqrestore(&master->queue_lock, flags);
                        return;
@@ -1250,7 +1250,7 @@ static int spi_init_queue(struct spi_master *master)
        master->running = false;
        master->busy = false;
 
-       init_kthread_worker(&master->kworker);
+       kthread_init_worker(&master->kworker);
        master->kworker_task = kthread_run(kthread_worker_fn,
                                           &master->kworker, "%s",
                                           dev_name(&master->dev));
@@ -1258,7 +1258,7 @@ static int spi_init_queue(struct spi_master *master)
                dev_err(&master->dev, "failed to create message pump task\n");
                return PTR_ERR(master->kworker_task);
        }
-       init_kthread_work(&master->pump_messages, spi_pump_messages);
+       kthread_init_work(&master->pump_messages, spi_pump_messages);
 
        /*
         * Master config will indicate if this controller should run the
@@ -1331,7 +1331,7 @@ void spi_finalize_current_message(struct spi_master *master)
        spin_lock_irqsave(&master->queue_lock, flags);
        master->cur_msg = NULL;
        master->cur_msg_prepared = false;
-       queue_kthread_work(&master->kworker, &master->pump_messages);
+       kthread_queue_work(&master->kworker, &master->pump_messages);
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
        trace_spi_message_done(mesg);
@@ -1357,7 +1357,7 @@ static int spi_start_queue(struct spi_master *master)
        master->cur_msg = NULL;
        spin_unlock_irqrestore(&master->queue_lock, flags);
 
-       queue_kthread_work(&master->kworker, &master->pump_messages);
+       kthread_queue_work(&master->kworker, &master->pump_messages);
 
        return 0;
 }
@@ -1404,7 +1404,7 @@ static int spi_destroy_queue(struct spi_master *master)
        ret = spi_stop_queue(master);
 
        /*
-        * flush_kthread_worker will block until all work is done.
+        * kthread_flush_worker will block until all work is done.
         * If the reason that stop_queue timed out is that the work will never
         * finish, then it does no good to call flush/stop thread, so
         * return anyway.
@@ -1414,7 +1414,7 @@ static int spi_destroy_queue(struct spi_master *master)
                return ret;
        }
 
-       flush_kthread_worker(&master->kworker);
+       kthread_flush_worker(&master->kworker);
        kthread_stop(master->kworker_task);
 
        return 0;
@@ -1438,7 +1438,7 @@ static int __spi_queued_transfer(struct spi_device *spi,
 
        list_add_tail(&msg->queue, &master->queue);
        if (!master->busy && need_pump)
-               queue_kthread_work(&master->kworker, &master->pump_messages);
+               kthread_queue_work(&master->kworker, &master->pump_messages);
 
        spin_unlock_irqrestore(&master->queue_lock, flags);
        return 0;
index 5d79efc..046e84d 100644 (file)
@@ -241,10 +241,7 @@ static void vvp_vmpage_error(struct inode *inode, struct page *vmpage, int ioret
                obj->vob_discard_page_warned = 0;
        } else {
                SetPageError(vmpage);
-               if (ioret == -ENOSPC)
-                       set_bit(AS_ENOSPC, &inode->i_mapping->flags);
-               else
-                       set_bit(AS_EIO, &inode->i_mapping->flags);
+               mapping_set_error(inode->i_mapping, ioret);
 
                if ((ioret == -ESHUTDOWN || ioret == -EINTR) &&
                    obj->vob_discard_page_warned == 0) {
index 2d702ca..a13541b 100644 (file)
@@ -186,7 +186,7 @@ config HISI_THERMAL
 
 config IMX_THERMAL
        tristate "Temperature sensor driver for Freescale i.MX SoCs"
-       depends on CPU_THERMAL
+       depends on (ARCH_MXC && CPU_THERMAL) || COMPILE_TEST
        depends on MFD_SYSCON
        depends on OF
        help
@@ -195,6 +195,26 @@ config IMX_THERMAL
          cpufreq is used as the cooling device to throttle CPUs when the
          passive trip is crossed.
 
+config MAX77620_THERMAL
+       tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
+       depends on MFD_MAX77620
+       depends on OF
+       help
+         Support for die junction temperature warning alarm for Maxim
+         Semiconductor PMIC MAX77620 device. Device generates two alarm
+         interrupts when PMIC die temperature cross the threshold of
+         120 degC and 140 degC.
+
+config QORIQ_THERMAL
+       tristate "QorIQ Thermal Monitoring Unit"
+       depends on THERMAL_OF
+       depends on HAS_IOMEM
+       help
+         Support for Thermal Monitoring Unit (TMU) found on QorIQ platforms.
+         It supports one critical trip point and one passive trip point. The
+         cpufreq is used as the cooling device to throttle CPUs when the
+         passive trip is crossed.
+
 config SPEAR_THERMAL
        tristate "SPEAr thermal sensor driver"
        depends on PLAT_SPEAR || COMPILE_TEST
@@ -332,6 +352,16 @@ menu "ACPI INT340X thermal drivers"
 source drivers/thermal/int340x_thermal/Kconfig
 endmenu
 
+config INTEL_BXT_PMIC_THERMAL
+       tristate "Intel Broxton PMIC thermal driver"
+       depends on X86 && INTEL_SOC_PMIC && REGMAP
+       help
+         Select this driver for Intel Broxton PMIC with ADC channels monitoring
+         system temperature measurements and alerts.
+         This driver is used for monitoring the ADC channels of PMIC and handles
+         the alert trip point interrupts and notifies the thermal framework with
+         the trip point and temperature details of the zone.
+
 config INTEL_PCH_THERMAL
        tristate "Intel PCH Thermal Reporting Driver"
        depends on X86 && PCI
@@ -399,4 +429,9 @@ config GENERIC_ADC_THERMAL
          to this driver. This driver reports the temperature by reading ADC
          channel and converts it to temperature based on lookup table.
 
+menu "Qualcomm thermal drivers"
+depends on (ARCH_QCOM && OF) || COMPILE_TEST
+source "drivers/thermal/qcom/Kconfig"
+endmenu
+
 endif
index 10b07c1..c92eb22 100644 (file)
@@ -37,6 +37,8 @@ obj-$(CONFIG_DB8500_THERMAL)  += db8500_thermal.o
 obj-$(CONFIG_ARMADA_THERMAL)   += armada_thermal.o
 obj-$(CONFIG_TANGO_THERMAL)    += tango_thermal.o
 obj-$(CONFIG_IMX_THERMAL)      += imx_thermal.o
+obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
+obj-$(CONFIG_QORIQ_THERMAL)    += qoriq_thermal.o
 obj-$(CONFIG_DB8500_CPUFREQ_COOLING)   += db8500_cpufreq_cooling.o
 obj-$(CONFIG_INTEL_POWERCLAMP) += intel_powerclamp.o
 obj-$(CONFIG_X86_PKG_TEMP_THERMAL)     += x86_pkg_temp_thermal.o
@@ -45,8 +47,10 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL)  += intel_soc_dts_thermal.o
 obj-$(CONFIG_INTEL_QUARK_DTS_THERMAL)  += intel_quark_dts_thermal.o
 obj-$(CONFIG_TI_SOC_THERMAL)   += ti-soc-thermal/
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
+obj-$(CONFIG_INTEL_BXT_PMIC_THERMAL) += intel_bxt_pmic_thermal.o
 obj-$(CONFIG_INTEL_PCH_THERMAL)        += intel_pch_thermal.o
 obj-$(CONFIG_ST_THERMAL)       += st/
+obj-$(CONFIG_QCOM_TSENS)       += qcom/
 obj-$(CONFIG_TEGRA_SOCTHERM)   += tegra/
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
 obj-$(CONFIG_MTK_THERMAL)      += mtk_thermal.o
index a32b417..9ce0e9e 100644 (file)
@@ -74,7 +74,7 @@ struct power_table {
  *     cpufreq frequencies.
  * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
  * @node: list_head to link all cpufreq_cooling_device together.
- * @last_load: load measured by the latest call to cpufreq_get_actual_power()
+ * @last_load: load measured by the latest call to cpufreq_get_requested_power()
  * @time_in_idle: previous reading of the absolute time that this cpu was idle
  * @time_in_idle_timestamp: wall time of the last invocation of
  *     get_cpu_idle_time_us()
index 652acd8..e776cea 100644 (file)
@@ -306,7 +306,7 @@ static void db8500_thermal_work(struct work_struct *work)
        if (cur_mode == THERMAL_DEVICE_DISABLED)
                return;
 
-       thermal_zone_device_update(pzone->therm_dev);
+       thermal_zone_device_update(pzone->therm_dev, THERMAL_EVENT_UNSPECIFIED);
        dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n");
 }
 
index 01f0015..81631b1 100644 (file)
@@ -312,7 +312,7 @@ static int devfreq_cooling_state2power(struct thermal_cooling_device *cdev,
        unsigned long freq;
        u32 static_power;
 
-       if (state < 0 || state >= dfc->freq_table_size)
+       if (state >= dfc->freq_table_size)
                return -EINVAL;
 
        freq = dfc->freq_table[state];
index bb118a1..fc5e505 100644 (file)
@@ -65,7 +65,7 @@ static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
                if (instance->target == 0 && tz->temperature >= trip_temp)
                        instance->target = 1;
                else if (instance->target == 1 &&
-                               tz->temperature < trip_temp - trip_hyst)
+                               tz->temperature <= trip_temp - trip_hyst)
                        instance->target = 0;
 
                dev_dbg(&instance->cdev->device, "target=%d\n",
index 97fad8f..f642966 100644 (file)
@@ -237,7 +237,8 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev)
                if (!data->sensors[i].tzd)
                        continue;
 
-               thermal_zone_device_update(data->sensors[i].tzd);
+               thermal_zone_device_update(data->sensors[i].tzd,
+                                          THERMAL_EVENT_UNSPECIFIED);
        }
 
        return IRQ_HANDLED;
index e473548..06912f0 100644 (file)
@@ -246,7 +246,7 @@ static int imx_set_mode(struct thermal_zone_device *tz,
        }
 
        data->mode = mode;
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return 0;
 }
@@ -457,7 +457,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev)
        dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n",
                data->alarm_temp / 1000);
 
-       thermal_zone_device_update(data->tz);
+       thermal_zone_device_update(data->tz, THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index 69df3d9..8e90b31 100644 (file)
@@ -35,7 +35,8 @@ static void int3402_notify(acpi_handle handle, u32 event, void *data)
        case INT3402_PERF_CHANGED_EVENT:
                break;
        case INT3402_THERMAL_EVENT:
-               int340x_thermal_zone_device_update(priv->int340x_zone);
+               int340x_thermal_zone_device_update(priv->int340x_zone,
+                                                  THERMAL_TRIP_VIOLATED);
                break;
        default:
                break;
index 50a7a08..c4890c9 100644 (file)
@@ -25,6 +25,7 @@
 #define INT3403_TYPE_CHARGER           0x0B
 #define INT3403_TYPE_BATTERY           0x0C
 #define INT3403_PERF_CHANGED_EVENT     0x80
+#define INT3403_PERF_TRIP_POINT_CHANGED        0x81
 #define INT3403_THERMAL_EVENT          0x90
 
 /* Preserved structure for future expandbility */
@@ -72,7 +73,13 @@ static void int3403_notify(acpi_handle handle,
        case INT3403_PERF_CHANGED_EVENT:
                break;
        case INT3403_THERMAL_EVENT:
-               int340x_thermal_zone_device_update(obj->int340x_zone);
+               int340x_thermal_zone_device_update(obj->int340x_zone,
+                                                  THERMAL_TRIP_VIOLATED);
+               break;
+       case INT3403_PERF_TRIP_POINT_CHANGED:
+               int340x_thermal_read_trips(obj->int340x_zone);
+               int340x_thermal_zone_device_update(obj->int340x_zone,
+                                                  THERMAL_TRIP_CHANGED);
                break;
        default:
                dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
index b9b2666..145a5c5 100644 (file)
@@ -177,6 +177,42 @@ static int int340x_thermal_get_trip_config(acpi_handle handle, char *name,
        return 0;
 }
 
+int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone)
+{
+       int trip_cnt = int34x_zone->aux_trip_nr;
+       int i;
+
+       int34x_zone->crt_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_CRT",
+                                            &int34x_zone->crt_temp))
+               int34x_zone->crt_trip_id = trip_cnt++;
+
+       int34x_zone->hot_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_HOT",
+                                            &int34x_zone->hot_temp))
+               int34x_zone->hot_trip_id = trip_cnt++;
+
+       int34x_zone->psv_trip_id = -1;
+       if (!int340x_thermal_get_trip_config(int34x_zone->adev->handle, "_PSV",
+                                            &int34x_zone->psv_temp))
+               int34x_zone->psv_trip_id = trip_cnt++;
+
+       for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
+               char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+
+               if (int340x_thermal_get_trip_config(int34x_zone->adev->handle,
+                                       name,
+                                       &int34x_zone->act_trips[i].temp))
+                       break;
+
+               int34x_zone->act_trips[i].id = trip_cnt++;
+               int34x_zone->act_trips[i].valid = true;
+       }
+
+       return trip_cnt;
+}
+EXPORT_SYMBOL_GPL(int340x_thermal_read_trips);
+
 static struct thermal_zone_params int340x_thermal_params = {
        .governor_name = "user_space",
        .no_hwmon = true,
@@ -188,7 +224,7 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
        struct int34x_thermal_zone *int34x_thermal_zone;
        acpi_status status;
        unsigned long long trip_cnt;
-       int trip_mask = 0, i;
+       int trip_mask = 0;
        int ret;
 
        int34x_thermal_zone = kzalloc(sizeof(*int34x_thermal_zone),
@@ -214,28 +250,8 @@ struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *adev,
                int34x_thermal_zone->aux_trip_nr = trip_cnt;
        }
 
-       int34x_thermal_zone->crt_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_CRT",
-                                            &int34x_thermal_zone->crt_temp))
-               int34x_thermal_zone->crt_trip_id = trip_cnt++;
-       int34x_thermal_zone->hot_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_HOT",
-                                            &int34x_thermal_zone->hot_temp))
-               int34x_thermal_zone->hot_trip_id = trip_cnt++;
-       int34x_thermal_zone->psv_trip_id = -1;
-       if (!int340x_thermal_get_trip_config(adev->handle, "_PSV",
-                                            &int34x_thermal_zone->psv_temp))
-               int34x_thermal_zone->psv_trip_id = trip_cnt++;
-       for (i = 0; i < INT340X_THERMAL_MAX_ACT_TRIP_COUNT; i++) {
-               char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+       trip_cnt = int340x_thermal_read_trips(int34x_thermal_zone);
 
-               if (int340x_thermal_get_trip_config(adev->handle, name,
-                               &int34x_thermal_zone->act_trips[i].temp))
-                       break;
-
-               int34x_thermal_zone->act_trips[i].id = trip_cnt++;
-               int34x_thermal_zone->act_trips[i].valid = true;
-       }
        int34x_thermal_zone->lpat_table = acpi_lpat_get_conversion_table(
                                                                adev->handle);
 
index aaadf72..5f3ba47 100644 (file)
@@ -46,6 +46,7 @@ struct int34x_thermal_zone {
 struct int34x_thermal_zone *int340x_thermal_zone_add(struct acpi_device *,
                                struct thermal_zone_device_ops *override_ops);
 void int340x_thermal_zone_remove(struct int34x_thermal_zone *);
+int int340x_thermal_read_trips(struct int34x_thermal_zone *int34x_zone);
 
 static inline void int340x_thermal_zone_set_priv_data(
                        struct int34x_thermal_zone *tzone, void *priv_data)
@@ -60,9 +61,10 @@ static inline void *int340x_thermal_zone_get_priv_data(
 }
 
 static inline void int340x_thermal_zone_device_update(
-                       struct int34x_thermal_zone *tzone)
+                                       struct int34x_thermal_zone *tzone,
+                                       enum thermal_notify_event event)
 {
-       thermal_zone_device_update(tzone->zone);
+       thermal_zone_device_update(tzone->zone, event);
 }
 
 #endif
index 42c1ac0..ff3b36f 100644 (file)
@@ -258,7 +258,8 @@ static void proc_thermal_notify(acpi_handle handle, u32 event, void *data)
        switch (event) {
        case PROC_POWER_CAPABILITY_CHANGED:
                proc_thermal_read_ppcc(proc_priv);
-               int340x_thermal_zone_device_update(proc_priv->int340x_zone);
+               int340x_thermal_zone_device_update(proc_priv->int340x_zone,
+                               THERMAL_DEVICE_POWER_CAPABILITY_CHANGED);
                break;
        default:
                dev_err(proc_priv->dev, "Unsupported event [0x%x]\n", event);
diff --git a/drivers/thermal/intel_bxt_pmic_thermal.c b/drivers/thermal/intel_bxt_pmic_thermal.c
new file mode 100644 (file)
index 0000000..0f19a39
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Intel Broxton PMIC thermal driver
+ *
+ * Copyright (C) 2016 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/mfd/intel_soc_pmic.h>
+
+#define BXTWC_THRM0IRQ         0x4E04
+#define BXTWC_THRM1IRQ         0x4E05
+#define BXTWC_THRM2IRQ         0x4E06
+#define BXTWC_MTHRM0IRQ                0x4E12
+#define BXTWC_MTHRM1IRQ                0x4E13
+#define BXTWC_MTHRM2IRQ                0x4E14
+#define BXTWC_STHRM0IRQ                0x4F19
+#define BXTWC_STHRM1IRQ                0x4F1A
+#define BXTWC_STHRM2IRQ                0x4F1B
+
+struct trip_config_map {
+       u16 irq_reg;
+       u16 irq_en;
+       u16 evt_stat;
+       u8 irq_mask;
+       u8 irq_en_mask;
+       u8 evt_mask;
+       u8 trip_num;
+};
+
+struct thermal_irq_map {
+       char handle[20];
+       int num_trips;
+       const struct trip_config_map *trip_config;
+};
+
+struct pmic_thermal_data {
+       const struct thermal_irq_map *maps;
+       int num_maps;
+};
+
+static const struct trip_config_map bxtwc_str0_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x01,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x01,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x01,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x10,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x10,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x10,
+               .trip_num = 1
+       }
+};
+
+static const struct trip_config_map bxtwc_str1_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x02,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x02,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x02,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x20,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x20,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x20,
+               .trip_num = 1
+       },
+};
+
+static const struct trip_config_map bxtwc_str2_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x04,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x04,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x04,
+               .trip_num = 0
+       },
+       {
+               .irq_reg = BXTWC_THRM0IRQ,
+               .irq_mask = 0x40,
+               .irq_en = BXTWC_MTHRM0IRQ,
+               .irq_en_mask = 0x40,
+               .evt_stat = BXTWC_STHRM0IRQ,
+               .evt_mask = 0x40,
+               .trip_num = 1
+       },
+};
+
+static const struct trip_config_map bxtwc_str3_trip_config[] = {
+       {
+               .irq_reg = BXTWC_THRM2IRQ,
+               .irq_mask = 0x10,
+               .irq_en = BXTWC_MTHRM2IRQ,
+               .irq_en_mask = 0x10,
+               .evt_stat = BXTWC_STHRM2IRQ,
+               .evt_mask = 0x10,
+               .trip_num = 0
+       },
+};
+
+static const struct thermal_irq_map bxtwc_thermal_irq_map[] = {
+       {
+               .handle = "STR0",
+               .trip_config = bxtwc_str0_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str0_trip_config),
+       },
+       {
+               .handle = "STR1",
+               .trip_config = bxtwc_str1_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str1_trip_config),
+       },
+       {
+               .handle = "STR2",
+               .trip_config = bxtwc_str2_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str2_trip_config),
+       },
+       {
+               .handle = "STR3",
+               .trip_config = bxtwc_str3_trip_config,
+               .num_trips = ARRAY_SIZE(bxtwc_str3_trip_config),
+       },
+};
+
+static const struct pmic_thermal_data bxtwc_thermal_data = {
+       .maps = bxtwc_thermal_irq_map,
+       .num_maps = ARRAY_SIZE(bxtwc_thermal_irq_map),
+};
+
+static irqreturn_t pmic_thermal_irq_handler(int irq, void *data)
+{
+       struct platform_device *pdev = data;
+       struct thermal_zone_device *tzd;
+       struct pmic_thermal_data *td;
+       struct intel_soc_pmic *pmic;
+       struct regmap *regmap;
+       u8 reg_val, mask, irq_stat, trip;
+       u16 reg, evt_stat_reg;
+       int i, j, ret;
+
+       pmic = dev_get_drvdata(pdev->dev.parent);
+       regmap = pmic->regmap;
+       td = (struct pmic_thermal_data *)
+               platform_get_device_id(pdev)->driver_data;
+
+       /* Resolve thermal irqs */
+       for (i = 0; i < td->num_maps; i++) {
+               for (j = 0; j < td->maps[i].num_trips; j++) {
+                       reg = td->maps[i].trip_config[j].irq_reg;
+                       mask = td->maps[i].trip_config[j].irq_mask;
+                       /*
+                        * Read the irq register to resolve whether the
+                        * interrupt was triggered for this sensor
+                        */
+                       if (regmap_read(regmap, reg, &ret))
+                               return IRQ_HANDLED;
+
+                       reg_val = (u8)ret;
+                       irq_stat = ((u8)ret & mask);
+
+                       if (!irq_stat)
+                               continue;
+
+                       /*
+                        * Read the status register to find out what
+                        * event occurred i.e a high or a low
+                        */
+                       evt_stat_reg = td->maps[i].trip_config[j].evt_stat;
+                       if (regmap_read(regmap, evt_stat_reg, &ret))
+                               return IRQ_HANDLED;
+
+                       trip = td->maps[i].trip_config[j].trip_num;
+                       tzd = thermal_zone_get_zone_by_name(td->maps[i].handle);
+                       if (!IS_ERR(tzd))
+                               thermal_zone_device_update(tzd,
+                                               THERMAL_EVENT_UNSPECIFIED);
+
+                       /* Clear the appropriate irq */
+                       regmap_write(regmap, reg, reg_val & mask);
+               }
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int pmic_thermal_probe(struct platform_device *pdev)
+{
+       struct regmap_irq_chip_data *regmap_irq_chip;
+       struct pmic_thermal_data *thermal_data;
+       int ret, irq, virq, i, j, pmic_irq_count;
+       struct intel_soc_pmic *pmic;
+       struct regmap *regmap;
+       struct device *dev;
+       u16 reg;
+       u8 mask;
+
+       dev = &pdev->dev;
+       pmic = dev_get_drvdata(pdev->dev.parent);
+       if (!pmic) {
+               dev_err(dev, "Failed to get struct intel_soc_pmic pointer\n");
+               return -ENODEV;
+       }
+
+       thermal_data = (struct pmic_thermal_data *)
+                               platform_get_device_id(pdev)->driver_data;
+       if (!thermal_data) {
+               dev_err(dev, "No thermal data initialized!!\n");
+               return -ENODEV;
+       }
+
+       regmap = pmic->regmap;
+       regmap_irq_chip = pmic->irq_chip_data_level2;
+
+       pmic_irq_count = 0;
+       while ((irq = platform_get_irq(pdev, pmic_irq_count)) != -ENXIO) {
+               virq = regmap_irq_get_virq(regmap_irq_chip, irq);
+               if (virq < 0) {
+                       dev_err(dev, "failed to get virq by irq %d\n", irq);
+                       return virq;
+               }
+
+               ret = devm_request_threaded_irq(&pdev->dev, virq,
+                               NULL, pmic_thermal_irq_handler,
+                               IRQF_ONESHOT, "pmic_thermal", pdev);
+
+               if (ret) {
+                       dev_err(dev, "request irq(%d) failed: %d\n", virq, ret);
+                       return ret;
+               }
+               pmic_irq_count++;
+       }
+
+       /* Enable thermal interrupts */
+       for (i = 0; i < thermal_data->num_maps; i++) {
+               for (j = 0; j < thermal_data->maps[i].num_trips; j++) {
+                       reg = thermal_data->maps[i].trip_config[j].irq_en;
+                       mask = thermal_data->maps[i].trip_config[j].irq_en_mask;
+                       ret = regmap_update_bits(regmap, reg, mask, 0x00);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct platform_device_id pmic_thermal_id_table[] = {
+       {
+               .name = "bxt_wcove_thermal",
+               .driver_data = (kernel_ulong_t)&bxtwc_thermal_data,
+       },
+       {},
+};
+
+static struct platform_driver pmic_thermal_driver = {
+       .probe = pmic_thermal_probe,
+       .driver = {
+               .name = "pmic_thermal",
+       },
+       .id_table = pmic_thermal_id_table,
+};
+
+MODULE_DEVICE_TABLE(platform, pmic_thermal_id_table);
+module_platform_driver(pmic_thermal_driver);
+
+MODULE_AUTHOR("Yegnesh S Iyer <yegnesh.s.iyer@intel.com>");
+MODULE_DESCRIPTION("Intel Broxton PMIC Thermal Driver");
+MODULE_LICENSE("GPL v2");
index f72e1db..e0813df 100644 (file)
@@ -391,7 +391,8 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors)
 
                for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) {
                        pr_debug("TZD update for zone %d\n", i);
-                       thermal_zone_device_update(sensors->soc_dts[i].tzone);
+                       thermal_zone_device_update(sensors->soc_dts[i].tzone,
+                                                  THERMAL_EVENT_UNSPECIFIED);
                }
        } else
                spin_unlock_irqrestore(&sensors->intr_notify_lock, flags);
diff --git a/drivers/thermal/max77620_thermal.c b/drivers/thermal/max77620_thermal.c
new file mode 100644 (file)
index 0000000..83905ff
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Junction temperature thermal driver for Maxim Max77620.
+ *
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *        Mallikarjun Kasoju <mkasoju@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max77620.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define MAX77620_NORMAL_OPERATING_TEMP 100000
+#define MAX77620_TJALARM1_TEMP         120000
+#define MAX77620_TJALARM2_TEMP         140000
+
+struct max77620_therm_info {
+       struct device                   *dev;
+       struct regmap                   *rmap;
+       struct thermal_zone_device      *tz_device;
+       int                             irq_tjalarm1;
+       int                             irq_tjalarm2;
+};
+
+/**
+ * max77620_thermal_read_temp: Read PMIC die temperatue.
+ * @data:      Device specific data.
+ * temp:       Temperature in millidegrees Celsius
+ *
+ * The actual temperature of PMIC die is not available from PMIC.
+ * PMIC only tells the status if it has crossed or not the threshold level
+ * of 120degC or 140degC.
+ * If threshold has not been crossed then assume die temperature as 100degC
+ * else 120degC or 140deG based on the PMIC die temp threshold status.
+ *
+ * Return 0 on success otherwise error number to show reason of failure.
+ */
+
+static int max77620_thermal_read_temp(void *data, int *temp)
+{
+       struct max77620_therm_info *mtherm = data;
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(mtherm->rmap, MAX77620_REG_STATLBT, &val);
+       if (ret < 0) {
+               dev_err(mtherm->dev, "Failed to read STATLBT: %d\n", ret);
+               return ret;
+       }
+
+       if (val & MAX77620_IRQ_TJALRM2_MASK)
+               *temp = MAX77620_TJALARM2_TEMP;
+       else if (val & MAX77620_IRQ_TJALRM1_MASK)
+               *temp = MAX77620_TJALARM1_TEMP;
+       else
+               *temp = MAX77620_NORMAL_OPERATING_TEMP;
+
+       return 0;
+}
+
+static const struct thermal_zone_of_device_ops max77620_thermal_ops = {
+       .get_temp = max77620_thermal_read_temp,
+};
+
+static irqreturn_t max77620_thermal_irq(int irq, void *data)
+{
+       struct max77620_therm_info *mtherm = data;
+
+       if (irq == mtherm->irq_tjalarm1)
+               dev_warn(mtherm->dev, "Junction Temp Alarm1(120C) occurred\n");
+       else if (irq == mtherm->irq_tjalarm2)
+               dev_crit(mtherm->dev, "Junction Temp Alarm2(140C) occurred\n");
+
+       thermal_zone_device_update(mtherm->tz_device,
+                                  THERMAL_EVENT_UNSPECIFIED);
+
+       return IRQ_HANDLED;
+}
+
+static int max77620_thermal_probe(struct platform_device *pdev)
+{
+       struct max77620_therm_info *mtherm;
+       int ret;
+
+       mtherm = devm_kzalloc(&pdev->dev, sizeof(*mtherm), GFP_KERNEL);
+       if (!mtherm)
+               return -ENOMEM;
+
+       mtherm->irq_tjalarm1 = platform_get_irq(pdev, 0);
+       mtherm->irq_tjalarm2 = platform_get_irq(pdev, 1);
+       if ((mtherm->irq_tjalarm1 < 0) || (mtherm->irq_tjalarm2 < 0)) {
+               dev_err(&pdev->dev, "Alarm irq number not available\n");
+               return -EINVAL;
+       }
+
+       pdev->dev.of_node = pdev->dev.parent->of_node;
+
+       mtherm->dev = &pdev->dev;
+       mtherm->rmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!mtherm->rmap) {
+               dev_err(&pdev->dev, "Failed to get parent regmap\n");
+               return -ENODEV;
+       }
+
+       mtherm->tz_device = devm_thermal_zone_of_sensor_register(&pdev->dev, 0,
+                               mtherm, &max77620_thermal_ops);
+       if (IS_ERR(mtherm->tz_device)) {
+               ret = PTR_ERR(mtherm->tz_device);
+               dev_err(&pdev->dev, "Failed to register thermal zone: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm1, NULL,
+                                       max77620_thermal_irq,
+                                       IRQF_ONESHOT | IRQF_SHARED,
+                                       dev_name(&pdev->dev), mtherm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request irq1: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_request_threaded_irq(&pdev->dev, mtherm->irq_tjalarm2, NULL,
+                                       max77620_thermal_irq,
+                                       IRQF_ONESHOT | IRQF_SHARED,
+                                       dev_name(&pdev->dev), mtherm);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to request irq2: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, mtherm);
+
+       return 0;
+}
+
+static struct platform_device_id max77620_thermal_devtype[] = {
+       { .name = "max77620-thermal", },
+       {},
+};
+
+static struct platform_driver max77620_thermal_driver = {
+       .driver = {
+               .name = "max77620-thermal",
+       },
+       .probe = max77620_thermal_probe,
+       .id_table = max77620_thermal_devtype,
+};
+
+module_platform_driver(max77620_thermal_driver);
+
+MODULE_DESCRIPTION("Max77620 Junction temperature Thermal driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_AUTHOR("Mallikarjun Kasoju <mkasoju@nvidia.com>");
+MODULE_LICENSE("GPL v2");
index 262ab0a..34169c3 100644 (file)
@@ -2,6 +2,7 @@
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Hanyi Wu <hanyi.wu@mediatek.com>
  *         Sascha Hauer <s.hauer@pengutronix.de>
+ *         Dawei Chien <dawei.chien@mediatek.com>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -21,6 +22,7 @@
 #include <linux/nvmem-consumer.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/io.h>
@@ -88,6 +90,7 @@
 #define TEMP_ADCVALIDMASK_VALID_HIGH           BIT(5)
 #define TEMP_ADCVALIDMASK_VALID_POS(bit)       (bit)
 
+/* MT8173 thermal sensors */
 #define MT8173_TS1     0
 #define MT8173_TS2     1
 #define MT8173_TS3     2
 /* The number of sensing points per bank */
 #define MT8173_NUM_SENSORS_PER_ZONE    4
 
-/* Layout of the fuses providing the calibration data */
+/*
+ * Layout of the fuses providing the calibration data
+ * These macros could be used for both MT8173 and MT2701.
+ * MT8173 has five sensors and need five VTS calibration data,
+ * and MT2701 has three sensors and need three VTS calibration data.
+ */
 #define MT8173_CALIB_BUF0_VALID                BIT(0)
 #define MT8173_CALIB_BUF1_ADC_GE(x)    (((x) >> 22) & 0x3ff)
 #define MT8173_CALIB_BUF0_VTS_TS1(x)   (((x) >> 17) & 0x1ff)
 #define MT8173_CALIB_BUF0_DEGC_CALI(x) (((x) >> 1) & 0x3f)
 #define MT8173_CALIB_BUF0_O_SLOPE(x)   (((x) >> 26) & 0x3f)
 
+/* MT2701 thermal sensors */
+#define MT2701_TS1     0
+#define MT2701_TS2     1
+#define MT2701_TSABB   2
+
+/* AUXADC channel 11 is used for the temperature sensors */
+#define MT2701_TEMP_AUXADC_CHANNEL     11
+
+/* The total number of temperature sensors in the MT2701 */
+#define MT2701_NUM_SENSORS     3
+
 #define THERMAL_NAME    "mtk-thermal"
 
+/* The number of sensing points per bank */
+#define MT2701_NUM_SENSORS_PER_ZONE    3
+
 struct mtk_thermal;
 
+struct thermal_bank_cfg {
+       unsigned int num_sensors;
+       const int *sensors;
+};
+
 struct mtk_thermal_bank {
        struct mtk_thermal *mt;
        int id;
 };
 
+struct mtk_thermal_data {
+       s32 num_banks;
+       s32 num_sensors;
+       s32 auxadc_channel;
+       const int *sensor_mux_values;
+       const int *msr;
+       const int *adcpnp;
+       struct thermal_bank_cfg bank_data[];
+};
+
 struct mtk_thermal {
        struct device *dev;
        void __iomem *thermal_base;
 
        struct clk *clk_peri_therm;
        struct clk *clk_auxadc;
-
-       struct mtk_thermal_bank banks[MT8173_NUM_ZONES];
-
        /* lock: for getting and putting banks */
        struct mutex lock;
 
@@ -144,16 +178,44 @@ struct mtk_thermal {
        s32 o_slope;
        s32 vts[MT8173_NUM_SENSORS];
 
+       const struct mtk_thermal_data *conf;
+       struct mtk_thermal_bank banks[];
 };
 
-struct mtk_thermal_bank_cfg {
-       unsigned int num_sensors;
-       unsigned int sensors[MT8173_NUM_SENSORS_PER_ZONE];
+/* MT8173 thermal sensor data */
+const int mt8173_bank_data[MT8173_NUM_ZONES][3] = {
+       { MT8173_TS2, MT8173_TS3 },
+       { MT8173_TS2, MT8173_TS4 },
+       { MT8173_TS1, MT8173_TS2, MT8173_TSABB },
+       { MT8173_TS2 },
 };
 
-static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
+const int mt8173_msr[MT8173_NUM_SENSORS_PER_ZONE] = {
+       TEMP_MSR0, TEMP_MSR1, TEMP_MSR2, TEMP_MSR2
+};
 
-/*
+const int mt8173_adcpnp[MT8173_NUM_SENSORS_PER_ZONE] = {
+       TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2, TEMP_ADCPNP3
+};
+
+const int mt8173_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
+
+/* MT2701 thermal sensor data */
+const int mt2701_bank_data[MT2701_NUM_SENSORS] = {
+       MT2701_TS1, MT2701_TS2, MT2701_TSABB
+};
+
+const int mt2701_msr[MT2701_NUM_SENSORS_PER_ZONE] = {
+       TEMP_MSR0, TEMP_MSR1, TEMP_MSR2
+};
+
+const int mt2701_adcpnp[MT2701_NUM_SENSORS_PER_ZONE] = {
+       TEMP_ADCPNP0, TEMP_ADCPNP1, TEMP_ADCPNP2
+};
+
+const int mt2701_mux_values[MT2701_NUM_SENSORS] = { 0, 1, 16 };
+
+/**
  * The MT8173 thermal controller has four banks. Each bank can read up to
  * four temperature sensors simultaneously. The MT8173 has a total of 5
  * temperature sensors. We use each bank to measure a certain area of the
@@ -166,42 +228,53 @@ static const int sensor_mux_values[MT8173_NUM_SENSORS] = { 0, 1, 2, 3, 16 };
  * data, and this indeed needs the temperatures of the individual banks
  * for making better decisions.
  */
-static const struct mtk_thermal_bank_cfg bank_data[] = {
-       {
-               .num_sensors = 2,
-               .sensors = { MT8173_TS2, MT8173_TS3 },
-       }, {
-               .num_sensors = 2,
-               .sensors = { MT8173_TS2, MT8173_TS4 },
-       }, {
-               .num_sensors = 3,
-               .sensors = { MT8173_TS1, MT8173_TS2, MT8173_TSABB },
-       }, {
-               .num_sensors = 1,
-               .sensors = { MT8173_TS2 },
+static const struct mtk_thermal_data mt8173_thermal_data = {
+       .auxadc_channel = MT8173_TEMP_AUXADC_CHANNEL,
+       .num_banks = MT8173_NUM_ZONES,
+       .num_sensors = MT8173_NUM_SENSORS,
+       .bank_data = {
+               {
+                       .num_sensors = 2,
+                       .sensors = mt8173_bank_data[0],
+               }, {
+                       .num_sensors = 2,
+                       .sensors = mt8173_bank_data[1],
+               }, {
+                       .num_sensors = 3,
+                       .sensors = mt8173_bank_data[2],
+               }, {
+                       .num_sensors = 1,
+                       .sensors = mt8173_bank_data[3],
+               },
        },
+       .msr = mt8173_msr,
+       .adcpnp = mt8173_adcpnp,
+       .sensor_mux_values = mt8173_mux_values,
 };
 
-struct mtk_thermal_sense_point {
-       int msr;
-       int adcpnp;
-};
-
-static const struct mtk_thermal_sense_point
-               sensing_points[MT8173_NUM_SENSORS_PER_ZONE] = {
-       {
-               .msr = TEMP_MSR0,
-               .adcpnp = TEMP_ADCPNP0,
-       }, {
-               .msr = TEMP_MSR1,
-               .adcpnp = TEMP_ADCPNP1,
-       }, {
-               .msr = TEMP_MSR2,
-               .adcpnp = TEMP_ADCPNP2,
-       }, {
-               .msr = TEMP_MSR3,
-               .adcpnp = TEMP_ADCPNP3,
+/**
+ * The MT2701 thermal controller has one bank, which can read up to
+ * three temperature sensors simultaneously. The MT2701 has a total of 3
+ * temperature sensors.
+ *
+ * The thermal core only gets the maximum temperature of this one bank,
+ * so the bank concept wouldn't be necessary here. However, the SVS (Smart
+ * Voltage Scaling) unit makes its decisions based on the same bank
+ * data.
+ */
+static const struct mtk_thermal_data mt2701_thermal_data = {
+       .auxadc_channel = MT2701_TEMP_AUXADC_CHANNEL,
+       .num_banks = 1,
+       .num_sensors = MT2701_NUM_SENSORS,
+       .bank_data = {
+               {
+                       .num_sensors = 3,
+                       .sensors = mt2701_bank_data,
+               },
        },
+       .msr = mt2701_msr,
+       .adcpnp = mt2701_adcpnp,
+       .sensor_mux_values = mt2701_mux_values,
 };
 
 /**
@@ -270,13 +343,16 @@ static void mtk_thermal_put_bank(struct mtk_thermal_bank *bank)
 static int mtk_thermal_bank_temperature(struct mtk_thermal_bank *bank)
 {
        struct mtk_thermal *mt = bank->mt;
+       const struct mtk_thermal_data *conf = mt->conf;
        int i, temp = INT_MIN, max = INT_MIN;
        u32 raw;
 
-       for (i = 0; i < bank_data[bank->id].num_sensors; i++) {
-               raw = readl(mt->thermal_base + sensing_points[i].msr);
+       for (i = 0; i < conf->bank_data[bank->id].num_sensors; i++) {
+               raw = readl(mt->thermal_base + conf->msr[i]);
 
-               temp = raw_to_mcelsius(mt, bank_data[bank->id].sensors[i], raw);
+               temp = raw_to_mcelsius(mt,
+                                      conf->bank_data[bank->id].sensors[i],
+                                      raw);
 
                /*
                 * The first read of a sensor often contains very high bogus
@@ -299,7 +375,7 @@ static int mtk_read_temp(void *data, int *temperature)
        int i;
        int tempmax = INT_MIN;
 
-       for (i = 0; i < MT8173_NUM_ZONES; i++) {
+       for (i = 0; i < mt->conf->num_banks; i++) {
                struct mtk_thermal_bank *bank = &mt->banks[i];
 
                mtk_thermal_get_bank(bank);
@@ -322,7 +398,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
                                  u32 apmixed_phys_base, u32 auxadc_phys_base)
 {
        struct mtk_thermal_bank *bank = &mt->banks[num];
-       const struct mtk_thermal_bank_cfg *cfg = &bank_data[num];
+       const struct mtk_thermal_data *conf = mt->conf;
        int i;
 
        bank->id = num;
@@ -368,7 +444,7 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
         * this value will be stored to TEMP_PNPMUXADDR (TEMP_SPARE0)
         * automatically by hw
         */
-       writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCMUX);
+       writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCMUX);
 
        /* AHB address for auxadc mux selection */
        writel(auxadc_phys_base + AUXADC_CON1_CLR_V,
@@ -379,18 +455,18 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
               mt->thermal_base + TEMP_PNPMUXADDR);
 
        /* AHB value for auxadc enable */
-       writel(BIT(MT8173_TEMP_AUXADC_CHANNEL), mt->thermal_base + TEMP_ADCEN);
+       writel(BIT(conf->auxadc_channel), mt->thermal_base + TEMP_ADCEN);
 
        /* AHB address for auxadc enable (channel 0 immediate mode selected) */
        writel(auxadc_phys_base + AUXADC_CON1_SET_V,
               mt->thermal_base + TEMP_ADCENADDR);
 
        /* AHB address for auxadc valid bit */
-       writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL),
+       writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
               mt->thermal_base + TEMP_ADCVALIDADDR);
 
        /* AHB address for auxadc voltage output */
-       writel(auxadc_phys_base + AUXADC_DATA(MT8173_TEMP_AUXADC_CHANNEL),
+       writel(auxadc_phys_base + AUXADC_DATA(conf->auxadc_channel),
               mt->thermal_base + TEMP_ADCVOLTADDR);
 
        /* read valid & voltage are at the same register */
@@ -407,11 +483,12 @@ static void mtk_thermal_init_bank(struct mtk_thermal *mt, int num,
        writel(TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
               mt->thermal_base + TEMP_ADCWRITECTRL);
 
-       for (i = 0; i < cfg->num_sensors; i++)
-               writel(sensor_mux_values[cfg->sensors[i]],
-                      mt->thermal_base + sensing_points[i].adcpnp);
+       for (i = 0; i < conf->bank_data[num].num_sensors; i++)
+               writel(conf->sensor_mux_values[conf->bank_data[num].sensors[i]],
+                      mt->thermal_base + conf->adcpnp[i]);
 
-       writel((1 << cfg->num_sensors) - 1, mt->thermal_base + TEMP_MONCTL0);
+       writel((1 << conf->bank_data[num].num_sensors) - 1,
+              mt->thermal_base + TEMP_MONCTL0);
 
        writel(TEMP_ADCWRITECTRL_ADC_PNP_WRITE |
               TEMP_ADCWRITECTRL_ADC_MUX_WRITE,
@@ -442,7 +519,7 @@ static int mtk_thermal_get_calibration_data(struct device *dev,
 
        /* Start with default values */
        mt->adc_ge = 512;
-       for (i = 0; i < MT8173_NUM_SENSORS; i++)
+       for (i = 0; i < mt->conf->num_sensors; i++)
                mt->vts[i] = 260;
        mt->degc_cali = 40;
        mt->o_slope = 0;
@@ -486,18 +563,37 @@ out:
        return ret;
 }
 
+static const struct of_device_id mtk_thermal_of_match[] = {
+       {
+               .compatible = "mediatek,mt8173-thermal",
+               .data = (void *)&mt8173_thermal_data,
+       },
+       {
+               .compatible = "mediatek,mt2701-thermal",
+               .data = (void *)&mt2701_thermal_data,
+       }, {
+       },
+};
+MODULE_DEVICE_TABLE(of, mtk_thermal_of_match);
+
 static int mtk_thermal_probe(struct platform_device *pdev)
 {
        int ret, i;
        struct device_node *auxadc, *apmixedsys, *np = pdev->dev.of_node;
        struct mtk_thermal *mt;
        struct resource *res;
+       const struct of_device_id *of_id;
        u64 auxadc_phys_base, apmixed_phys_base;
+       struct thermal_zone_device *tzdev;
 
        mt = devm_kzalloc(&pdev->dev, sizeof(*mt), GFP_KERNEL);
        if (!mt)
                return -ENOMEM;
 
+       of_id = of_match_device(mtk_thermal_of_match, &pdev->dev);
+       if (of_id)
+               mt->conf = (const struct mtk_thermal_data *)of_id->data;
+
        mt->clk_peri_therm = devm_clk_get(&pdev->dev, "therm");
        if (IS_ERR(mt->clk_peri_therm))
                return PTR_ERR(mt->clk_peri_therm);
@@ -565,17 +661,23 @@ static int mtk_thermal_probe(struct platform_device *pdev)
                goto err_disable_clk_auxadc;
        }
 
-       for (i = 0; i < MT8173_NUM_ZONES; i++)
+       for (i = 0; i < mt->conf->num_banks; i++)
                mtk_thermal_init_bank(mt, i, apmixed_phys_base,
                                      auxadc_phys_base);
 
        platform_set_drvdata(pdev, mt);
 
-       devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
-                                            &mtk_thermal_ops);
+       tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, mt,
+                                                    &mtk_thermal_ops);
+       if (IS_ERR(tzdev)) {
+               ret = PTR_ERR(tzdev);
+               goto err_disable_clk_peri_therm;
+       }
 
        return 0;
 
+err_disable_clk_peri_therm:
+       clk_disable_unprepare(mt->clk_peri_therm);
 err_disable_clk_auxadc:
        clk_disable_unprepare(mt->clk_auxadc);
 
@@ -592,13 +694,6 @@ static int mtk_thermal_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id mtk_thermal_of_match[] = {
-       {
-               .compatible = "mediatek,mt8173-thermal",
-       }, {
-       },
-};
-
 static struct platform_driver mtk_thermal_driver = {
        .probe = mtk_thermal_probe,
        .remove = mtk_thermal_remove,
@@ -610,6 +705,7 @@ static struct platform_driver mtk_thermal_driver = {
 
 module_platform_driver(mtk_thermal_driver);
 
+MODULE_AUTHOR("Dawei Chien <dawei.chien@mediatek.com>");
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_AUTHOR("Hanyi Wu <hanyi.wu@mediatek.com>");
 MODULE_DESCRIPTION("Mediatek thermal driver");
index b8e509c..d04ec3b 100644 (file)
@@ -101,6 +101,17 @@ static int of_thermal_get_temp(struct thermal_zone_device *tz,
        return data->ops->get_temp(data->sensor_data, temp);
 }
 
+static int of_thermal_set_trips(struct thermal_zone_device *tz,
+                               int low, int high)
+{
+       struct __thermal_zone *data = tz->devdata;
+
+       if (!data->ops || !data->ops->set_trips)
+               return -EINVAL;
+
+       return data->ops->set_trips(data->sensor_data, low, high);
+}
+
 /**
  * of_thermal_get_ntrips - function to export number of available trip
  *                        points.
@@ -181,9 +192,6 @@ static int of_thermal_set_emul_temp(struct thermal_zone_device *tz,
 {
        struct __thermal_zone *data = tz->devdata;
 
-       if (!data->ops || !data->ops->set_emul_temp)
-               return -EINVAL;
-
        return data->ops->set_emul_temp(data->sensor_data, temp);
 }
 
@@ -191,25 +199,11 @@ static int of_thermal_get_trend(struct thermal_zone_device *tz, int trip,
                                enum thermal_trend *trend)
 {
        struct __thermal_zone *data = tz->devdata;
-       long dev_trend;
-       int r;
 
        if (!data->ops->get_trend)
                return -EINVAL;
 
-       r = data->ops->get_trend(data->sensor_data, &dev_trend);
-       if (r)
-               return r;
-
-       /* TODO: These intervals might have some thresholds, but in core code */
-       if (dev_trend > 0)
-               *trend = THERMAL_TREND_RAISING;
-       else if (dev_trend < 0)
-               *trend = THERMAL_TREND_DROPPING;
-       else
-               *trend = THERMAL_TREND_STABLE;
-
-       return 0;
+       return data->ops->get_trend(data->sensor_data, trip, trend);
 }
 
 static int of_thermal_bind(struct thermal_zone_device *thermal,
@@ -292,7 +286,7 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz,
        mutex_unlock(&tz->lock);
 
        data->mode = mode;
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return 0;
 }
@@ -427,7 +421,17 @@ thermal_zone_of_add_sensor(struct device_node *zone,
 
        tzd->ops->get_temp = of_thermal_get_temp;
        tzd->ops->get_trend = of_thermal_get_trend;
-       tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+
+       /*
+        * The thermal zone core will calculate the window if they have set the
+        * optional set_trips pointer.
+        */
+       if (ops->set_trips)
+               tzd->ops->set_trips = of_thermal_set_trips;
+
+       if (ops->set_emul_temp)
+               tzd->ops->set_emul_temp = of_thermal_set_emul_temp;
+
        mutex_unlock(&tzd->lock);
 
        return tzd;
@@ -596,7 +600,7 @@ static int devm_thermal_zone_of_sensor_match(struct device *dev, void *res,
  * Return: On success returns a valid struct thermal_zone_device,
  * otherwise, it returns a corresponding ERR_PTR(). Caller must
  * check the return value with help of IS_ERR() helper.
- * Registered hermal_zone_device device will automatically be
+ * Registered thermal_zone_device device will automatically be
  * released when device is unbounded.
  */
 struct thermal_zone_device *devm_thermal_zone_of_sensor_register(
index f8a3c60..819c6d5 100644 (file)
@@ -150,7 +150,7 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data)
 {
        struct qpnp_tm_chip *chip = data;
 
-       thermal_zone_device_update(chip->tz_dev);
+       thermal_zone_device_update(chip->tz_dev, THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
diff --git a/drivers/thermal/qcom/Kconfig b/drivers/thermal/qcom/Kconfig
new file mode 100644 (file)
index 0000000..be32e5a
--- /dev/null
@@ -0,0 +1,11 @@
+config QCOM_TSENS
+       tristate "Qualcomm TSENS Temperature Alarm"
+       depends on THERMAL
+       depends on QCOM_QFPROM
+       depends on ARCH_QCOM || COMPILE_TEST
+       help
+         This enables the thermal sysfs driver for the TSENS device. It shows
+         up in Sysfs as a thermal zone with multiple trip points. Disabling the
+         thermal zone device via the mode file results in disabling the sensor.
+         Also able to set threshold temperature for both hot and cold and update
+         when a threshold is reached.
diff --git a/drivers/thermal/qcom/Makefile b/drivers/thermal/qcom/Makefile
new file mode 100644 (file)
index 0000000..2cc2193
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_QCOM_TSENS)       += qcom_tsens.o
+qcom_tsens-y                   += tsens.o tsens-common.o tsens-8916.o tsens-8974.o tsens-8960.o tsens-8996.o
diff --git a/drivers/thermal/qcom/tsens-8916.c b/drivers/thermal/qcom/tsens-8916.c
new file mode 100644 (file)
index 0000000..fdf561b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* eeprom layout data for 8916 */
+#define BASE0_MASK     0x0000007f
+#define BASE1_MASK     0xfe000000
+#define BASE0_SHIFT    0
+#define BASE1_SHIFT    25
+
+#define S0_P1_MASK     0x00000f80
+#define S1_P1_MASK     0x003e0000
+#define S2_P1_MASK     0xf8000000
+#define S3_P1_MASK     0x000003e0
+#define S4_P1_MASK     0x000f8000
+
+#define S0_P2_MASK     0x0001f000
+#define S1_P2_MASK     0x07c00000
+#define S2_P2_MASK     0x0000001f
+#define S3_P2_MASK     0x00007c00
+#define S4_P2_MASK     0x01f00000
+
+#define S0_P1_SHIFT    7
+#define S1_P1_SHIFT    17
+#define S2_P1_SHIFT    27
+#define S3_P1_SHIFT    5
+#define S4_P1_SHIFT    15
+
+#define S0_P2_SHIFT    12
+#define S1_P2_SHIFT    22
+#define S2_P2_SHIFT    0
+#define S3_P2_SHIFT    10
+#define S4_P2_SHIFT    20
+
+#define CAL_SEL_MASK   0xe0000000
+#define CAL_SEL_SHIFT  29
+
+static int calibrate_8916(struct tsens_device *tmdev)
+{
+       int base0 = 0, base1 = 0, i;
+       u32 p1[5], p2[5];
+       int mode = 0;
+       u32 *qfprom_cdata, *qfprom_csel;
+
+       qfprom_cdata = (u32 *)qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(qfprom_cdata))
+               return PTR_ERR(qfprom_cdata);
+
+       qfprom_csel = (u32 *)qfprom_read(tmdev->dev, "calib_sel");
+       if (IS_ERR(qfprom_csel))
+               return PTR_ERR(qfprom_csel);
+
+       mode = (qfprom_csel[0] & CAL_SEL_MASK) >> CAL_SEL_SHIFT;
+       dev_dbg(tmdev->dev, "calibration mode is %d\n", mode);
+
+       switch (mode) {
+       case TWO_PT_CALIB:
+               base1 = (qfprom_cdata[1] & BASE1_MASK) >> BASE1_SHIFT;
+               p2[0] = (qfprom_cdata[0] & S0_P2_MASK) >> S0_P2_SHIFT;
+               p2[1] = (qfprom_cdata[0] & S1_P2_MASK) >> S1_P2_SHIFT;
+               p2[2] = (qfprom_cdata[1] & S2_P2_MASK) >> S2_P2_SHIFT;
+               p2[3] = (qfprom_cdata[1] & S3_P2_MASK) >> S3_P2_SHIFT;
+               p2[4] = (qfprom_cdata[1] & S4_P2_MASK) >> S4_P2_SHIFT;
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p2[i] = ((base1 + p2[i]) << 3);
+               /* Fall through */
+       case ONE_PT_CALIB2:
+               base0 = (qfprom_cdata[0] & BASE0_MASK);
+               p1[0] = (qfprom_cdata[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+               p1[1] = (qfprom_cdata[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+               p1[2] = (qfprom_cdata[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+               p1[3] = (qfprom_cdata[1] & S3_P1_MASK) >> S3_P1_SHIFT;
+               p1[4] = (qfprom_cdata[1] & S4_P1_MASK) >> S4_P1_SHIFT;
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p1[i] = (((base0) + p1[i]) << 3);
+               break;
+       default:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p1[i] = 500;
+                       p2[i] = 780;
+               }
+               break;
+       }
+
+       compute_intercept_slope(tmdev, p1, p2, mode);
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8916 = {
+       .init           = init_common,
+       .calibrate      = calibrate_8916,
+       .get_temp       = get_temp_common,
+};
+
+const struct tsens_data data_8916 = {
+       .num_sensors    = 5,
+       .ops            = &ops_8916,
+       .hw_ids         = (unsigned int []){0, 1, 2, 4, 5 },
+};
diff --git a/drivers/thermal/qcom/tsens-8960.c b/drivers/thermal/qcom/tsens-8960.c
new file mode 100644 (file)
index 0000000..0451277
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/thermal.h>
+#include "tsens.h"
+
+#define CAL_MDEGC              30000
+
+#define CONFIG_ADDR            0x3640
+#define CONFIG_ADDR_8660       0x3620
+/* CONFIG_ADDR bitmasks */
+#define CONFIG                 0x9b
+#define CONFIG_MASK            0xf
+#define CONFIG_8660            1
+#define CONFIG_SHIFT_8660      28
+#define CONFIG_MASK_8660       (3 << CONFIG_SHIFT_8660)
+
+#define STATUS_CNTL_ADDR_8064  0x3660
+#define CNTL_ADDR              0x3620
+/* CNTL_ADDR bitmasks */
+#define EN                     BIT(0)
+#define SW_RST                 BIT(1)
+#define SENSOR0_EN             BIT(3)
+#define SLP_CLK_ENA            BIT(26)
+#define SLP_CLK_ENA_8660       BIT(24)
+#define MEASURE_PERIOD         1
+#define SENSOR0_SHIFT          3
+
+/* INT_STATUS_ADDR bitmasks */
+#define MIN_STATUS_MASK                BIT(0)
+#define LOWER_STATUS_CLR       BIT(1)
+#define UPPER_STATUS_CLR       BIT(2)
+#define MAX_STATUS_MASK                BIT(3)
+
+#define THRESHOLD_ADDR         0x3624
+/* THRESHOLD_ADDR bitmasks */
+#define THRESHOLD_MAX_LIMIT_SHIFT      24
+#define THRESHOLD_MIN_LIMIT_SHIFT      16
+#define THRESHOLD_UPPER_LIMIT_SHIFT    8
+#define THRESHOLD_LOWER_LIMIT_SHIFT    0
+
+/* Initial temperature threshold values */
+#define LOWER_LIMIT_TH         0x50
+#define UPPER_LIMIT_TH         0xdf
+#define MIN_LIMIT_TH           0x0
+#define MAX_LIMIT_TH           0xff
+
+#define S0_STATUS_ADDR         0x3628
+#define INT_STATUS_ADDR                0x363c
+#define TRDY_MASK              BIT(7)
+#define TIMEOUT_US             100
+
+static int suspend_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       unsigned int mask;
+       struct regmap *map = tmdev->map;
+
+       ret = regmap_read(map, THRESHOLD_ADDR, &tmdev->ctx.threshold);
+       if (ret)
+               return ret;
+
+       ret = regmap_read(map, CNTL_ADDR, &tmdev->ctx.control);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1)
+               mask = SLP_CLK_ENA | EN;
+       else
+               mask = SLP_CLK_ENA_8660 | EN;
+
+       ret = regmap_update_bits(map, CNTL_ADDR, mask, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int resume_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       struct regmap *map = tmdev->map;
+
+       ret = regmap_update_bits(map, CNTL_ADDR, SW_RST, SW_RST);
+       if (ret)
+               return ret;
+
+       /*
+        * Separate CONFIG restore is not needed only for 8660 as
+        * config is part of CTRL Addr and its restored as such
+        */
+       if (tmdev->num_sensors > 1) {
+               ret = regmap_update_bits(map, CONFIG_ADDR, CONFIG_MASK, CONFIG);
+               if (ret)
+                       return ret;
+       }
+
+       ret = regmap_write(map, THRESHOLD_ADDR, tmdev->ctx.threshold);
+       if (ret)
+               return ret;
+
+       ret = regmap_write(map, CNTL_ADDR, tmdev->ctx.control);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int enable_8960(struct tsens_device *tmdev, int id)
+{
+       int ret;
+       u32 reg, mask;
+
+       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg);
+       if (ret)
+               return ret;
+
+       mask = BIT(id + SENSOR0_SHIFT);
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg | SW_RST);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1)
+               reg |= mask | SLP_CLK_ENA | EN;
+       else
+               reg |= mask | SLP_CLK_ENA_8660 | EN;
+
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void disable_8960(struct tsens_device *tmdev)
+{
+       int ret;
+       u32 reg_cntl;
+       u32 mask;
+
+       mask = GENMASK(tmdev->num_sensors - 1, 0);
+       mask <<= SENSOR0_SHIFT;
+       mask |= EN;
+
+       ret = regmap_read(tmdev->map, CNTL_ADDR, &reg_cntl);
+       if (ret)
+               return;
+
+       reg_cntl &= ~mask;
+
+       if (tmdev->num_sensors > 1)
+               reg_cntl &= ~SLP_CLK_ENA;
+       else
+               reg_cntl &= ~SLP_CLK_ENA_8660;
+
+       regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+}
+
+static int init_8960(struct tsens_device *tmdev)
+{
+       int ret, i;
+       u32 reg_cntl;
+
+       tmdev->map = dev_get_regmap(tmdev->dev, NULL);
+       if (!tmdev->map)
+               return -ENODEV;
+
+       /*
+        * The status registers for each sensor are discontiguous
+        * because some SoCs have 5 sensors while others have more
+        * but the control registers stay in the same place, i.e
+        * directly after the first 5 status registers.
+        */
+       for (i = 0; i < tmdev->num_sensors; i++) {
+               if (i >= 5)
+                       tmdev->sensor[i].status = S0_STATUS_ADDR + 40;
+               tmdev->sensor[i].status += i * 4;
+       }
+
+       reg_cntl = SW_RST;
+       ret = regmap_update_bits(tmdev->map, CNTL_ADDR, SW_RST, reg_cntl);
+       if (ret)
+               return ret;
+
+       if (tmdev->num_sensors > 1) {
+               reg_cntl |= SLP_CLK_ENA | (MEASURE_PERIOD << 18);
+               reg_cntl &= ~SW_RST;
+               ret = regmap_update_bits(tmdev->map, CONFIG_ADDR,
+                                        CONFIG_MASK, CONFIG);
+       } else {
+               reg_cntl |= SLP_CLK_ENA_8660 | (MEASURE_PERIOD << 16);
+               reg_cntl &= ~CONFIG_MASK_8660;
+               reg_cntl |= CONFIG_8660 << CONFIG_SHIFT_8660;
+       }
+
+       reg_cntl |= GENMASK(tmdev->num_sensors - 1, 0) << SENSOR0_SHIFT;
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       if (ret)
+               return ret;
+
+       reg_cntl |= EN;
+       ret = regmap_write(tmdev->map, CNTL_ADDR, reg_cntl);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int calibrate_8960(struct tsens_device *tmdev)
+{
+       int i;
+       char *data;
+
+       ssize_t num_read = tmdev->num_sensors;
+       struct tsens_sensor *s = tmdev->sensor;
+
+       data = qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(data))
+               data = qfprom_read(tmdev->dev, "calib_backup");
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+
+       for (i = 0; i < num_read; i++, s++)
+               s->offset = data[i];
+
+       return 0;
+}
+
+/* Temperature on y axis and ADC-code on x-axis */
+static inline int code_to_mdegC(u32 adc_code, const struct tsens_sensor *s)
+{
+       int slope, offset;
+
+       slope = thermal_zone_get_slope(s->tzd);
+       offset = CAL_MDEGC - slope * s->offset;
+
+       return adc_code * slope + offset;
+}
+
+static int get_temp_8960(struct tsens_device *tmdev, int id, int *temp)
+{
+       int ret;
+       u32 code, trdy;
+       const struct tsens_sensor *s = &tmdev->sensor[id];
+       unsigned long timeout;
+
+       timeout = jiffies + usecs_to_jiffies(TIMEOUT_US);
+       do {
+               ret = regmap_read(tmdev->map, INT_STATUS_ADDR, &trdy);
+               if (ret)
+                       return ret;
+               if (!(trdy & TRDY_MASK))
+                       continue;
+               ret = regmap_read(tmdev->map, s->status, &code);
+               if (ret)
+                       return ret;
+               *temp = code_to_mdegC(code, s);
+               return 0;
+       } while (time_before(jiffies, timeout));
+
+       return -ETIMEDOUT;
+}
+
+static const struct tsens_ops ops_8960 = {
+       .init           = init_8960,
+       .calibrate      = calibrate_8960,
+       .get_temp       = get_temp_8960,
+       .enable         = enable_8960,
+       .disable        = disable_8960,
+       .suspend        = suspend_8960,
+       .resume         = resume_8960,
+};
+
+const struct tsens_data data_8960 = {
+       .num_sensors    = 11,
+       .ops            = &ops_8960,
+};
diff --git a/drivers/thermal/qcom/tsens-8974.c b/drivers/thermal/qcom/tsens-8974.c
new file mode 100644 (file)
index 0000000..9baf77e
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include "tsens.h"
+
+/* eeprom layout data for 8974 */
+#define BASE1_MASK             0xff
+#define S0_P1_MASK             0x3f00
+#define S1_P1_MASK             0xfc000
+#define S2_P1_MASK             0x3f00000
+#define S3_P1_MASK             0xfc000000
+#define S4_P1_MASK             0x3f
+#define S5_P1_MASK             0xfc0
+#define S6_P1_MASK             0x3f000
+#define S7_P1_MASK             0xfc0000
+#define S8_P1_MASK             0x3f000000
+#define S8_P1_MASK_BKP         0x3f
+#define S9_P1_MASK             0x3f
+#define S9_P1_MASK_BKP         0xfc0
+#define S10_P1_MASK            0xfc0
+#define S10_P1_MASK_BKP                0x3f000
+#define CAL_SEL_0_1            0xc0000000
+#define CAL_SEL_2              0x40000000
+#define CAL_SEL_SHIFT          30
+#define CAL_SEL_SHIFT_2                28
+
+#define S0_P1_SHIFT            8
+#define S1_P1_SHIFT            14
+#define S2_P1_SHIFT            20
+#define S3_P1_SHIFT            26
+#define S5_P1_SHIFT            6
+#define S6_P1_SHIFT            12
+#define S7_P1_SHIFT            18
+#define S8_P1_SHIFT            24
+#define S9_P1_BKP_SHIFT                6
+#define S10_P1_SHIFT           6
+#define S10_P1_BKP_SHIFT       12
+
+#define BASE2_SHIFT            12
+#define BASE2_BKP_SHIFT                18
+#define S0_P2_SHIFT            20
+#define S0_P2_BKP_SHIFT                26
+#define S1_P2_SHIFT            26
+#define S2_P2_BKP_SHIFT                6
+#define S3_P2_SHIFT            6
+#define S3_P2_BKP_SHIFT                12
+#define S4_P2_SHIFT            12
+#define S4_P2_BKP_SHIFT                18
+#define S5_P2_SHIFT            18
+#define S5_P2_BKP_SHIFT                24
+#define S6_P2_SHIFT            24
+#define S7_P2_BKP_SHIFT                6
+#define S8_P2_SHIFT            6
+#define S8_P2_BKP_SHIFT                12
+#define S9_P2_SHIFT            12
+#define S9_P2_BKP_SHIFT                18
+#define S10_P2_SHIFT           18
+#define S10_P2_BKP_SHIFT       24
+
+#define BASE2_MASK             0xff000
+#define BASE2_BKP_MASK         0xfc0000
+#define S0_P2_MASK             0x3f00000
+#define S0_P2_BKP_MASK         0xfc000000
+#define S1_P2_MASK             0xfc000000
+#define S1_P2_BKP_MASK         0x3f
+#define S2_P2_MASK             0x3f
+#define S2_P2_BKP_MASK         0xfc0
+#define S3_P2_MASK             0xfc0
+#define S3_P2_BKP_MASK         0x3f000
+#define S4_P2_MASK             0x3f000
+#define S4_P2_BKP_MASK         0xfc0000
+#define S5_P2_MASK             0xfc0000
+#define S5_P2_BKP_MASK         0x3f000000
+#define S6_P2_MASK             0x3f000000
+#define S6_P2_BKP_MASK         0x3f
+#define S7_P2_MASK             0x3f
+#define S7_P2_BKP_MASK         0xfc0
+#define S8_P2_MASK             0xfc0
+#define S8_P2_BKP_MASK         0x3f000
+#define S9_P2_MASK             0x3f000
+#define S9_P2_BKP_MASK         0xfc0000
+#define S10_P2_MASK            0xfc0000
+#define S10_P2_BKP_MASK                0x3f000000
+
+#define BKP_SEL                        0x3
+#define BKP_REDUN_SEL          0xe0000000
+#define BKP_REDUN_SHIFT                29
+
+#define BIT_APPEND             0x3
+
+static int calibrate_8974(struct tsens_device *tmdev)
+{
+       int base1 = 0, base2 = 0, i;
+       u32 p1[11], p2[11];
+       int mode = 0;
+       u32 *calib, *bkp;
+       u32 calib_redun_sel;
+
+       calib = (u32 *)qfprom_read(tmdev->dev, "calib");
+       if (IS_ERR(calib))
+               return PTR_ERR(calib);
+
+       bkp = (u32 *)qfprom_read(tmdev->dev, "calib_backup");
+       if (IS_ERR(bkp))
+               return PTR_ERR(bkp);
+
+       calib_redun_sel =  bkp[1] & BKP_REDUN_SEL;
+       calib_redun_sel >>= BKP_REDUN_SHIFT;
+
+       if (calib_redun_sel == BKP_SEL) {
+               mode = (calib[4] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+               mode |= (calib[5] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+               switch (mode) {
+               case TWO_PT_CALIB:
+                       base2 = (bkp[2] & BASE2_BKP_MASK) >> BASE2_BKP_SHIFT;
+                       p2[0] = (bkp[2] & S0_P2_BKP_MASK) >> S0_P2_BKP_SHIFT;
+                       p2[1] = (bkp[3] & S1_P2_BKP_MASK);
+                       p2[2] = (bkp[3] & S2_P2_BKP_MASK) >> S2_P2_BKP_SHIFT;
+                       p2[3] = (bkp[3] & S3_P2_BKP_MASK) >> S3_P2_BKP_SHIFT;
+                       p2[4] = (bkp[3] & S4_P2_BKP_MASK) >> S4_P2_BKP_SHIFT;
+                       p2[5] = (calib[4] & S5_P2_BKP_MASK) >> S5_P2_BKP_SHIFT;
+                       p2[6] = (calib[5] & S6_P2_BKP_MASK);
+                       p2[7] = (calib[5] & S7_P2_BKP_MASK) >> S7_P2_BKP_SHIFT;
+                       p2[8] = (calib[5] & S8_P2_BKP_MASK) >> S8_P2_BKP_SHIFT;
+                       p2[9] = (calib[5] & S9_P2_BKP_MASK) >> S9_P2_BKP_SHIFT;
+                       p2[10] = (calib[5] & S10_P2_BKP_MASK) >> S10_P2_BKP_SHIFT;
+                       /* Fall through */
+               case ONE_PT_CALIB:
+               case ONE_PT_CALIB2:
+                       base1 = bkp[0] & BASE1_MASK;
+                       p1[0] = (bkp[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+                       p1[1] = (bkp[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+                       p1[2] = (bkp[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+                       p1[3] = (bkp[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+                       p1[4] = (bkp[1] & S4_P1_MASK);
+                       p1[5] = (bkp[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+                       p1[6] = (bkp[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+                       p1[7] = (bkp[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+                       p1[8] = (bkp[2] & S8_P1_MASK_BKP) >> S8_P1_SHIFT;
+                       p1[9] = (bkp[2] & S9_P1_MASK_BKP) >> S9_P1_BKP_SHIFT;
+                       p1[10] = (bkp[2] & S10_P1_MASK_BKP) >> S10_P1_BKP_SHIFT;
+                       break;
+               }
+       } else {
+               mode = (calib[1] & CAL_SEL_0_1) >> CAL_SEL_SHIFT;
+               mode |= (calib[3] & CAL_SEL_2) >> CAL_SEL_SHIFT_2;
+
+               switch (mode) {
+               case TWO_PT_CALIB:
+                       base2 = (calib[2] & BASE2_MASK) >> BASE2_SHIFT;
+                       p2[0] = (calib[2] & S0_P2_MASK) >> S0_P2_SHIFT;
+                       p2[1] = (calib[2] & S1_P2_MASK) >> S1_P2_SHIFT;
+                       p2[2] = (calib[3] & S2_P2_MASK);
+                       p2[3] = (calib[3] & S3_P2_MASK) >> S3_P2_SHIFT;
+                       p2[4] = (calib[3] & S4_P2_MASK) >> S4_P2_SHIFT;
+                       p2[5] = (calib[3] & S5_P2_MASK) >> S5_P2_SHIFT;
+                       p2[6] = (calib[3] & S6_P2_MASK) >> S6_P2_SHIFT;
+                       p2[7] = (calib[4] & S7_P2_MASK);
+                       p2[8] = (calib[4] & S8_P2_MASK) >> S8_P2_SHIFT;
+                       p2[9] = (calib[4] & S9_P2_MASK) >> S9_P2_SHIFT;
+                       p2[10] = (calib[4] & S10_P2_MASK) >> S10_P2_SHIFT;
+                       /* Fall through */
+               case ONE_PT_CALIB:
+               case ONE_PT_CALIB2:
+                       base1 = calib[0] & BASE1_MASK;
+                       p1[0] = (calib[0] & S0_P1_MASK) >> S0_P1_SHIFT;
+                       p1[1] = (calib[0] & S1_P1_MASK) >> S1_P1_SHIFT;
+                       p1[2] = (calib[0] & S2_P1_MASK) >> S2_P1_SHIFT;
+                       p1[3] = (calib[0] & S3_P1_MASK) >> S3_P1_SHIFT;
+                       p1[4] = (calib[1] & S4_P1_MASK);
+                       p1[5] = (calib[1] & S5_P1_MASK) >> S5_P1_SHIFT;
+                       p1[6] = (calib[1] & S6_P1_MASK) >> S6_P1_SHIFT;
+                       p1[7] = (calib[1] & S7_P1_MASK) >> S7_P1_SHIFT;
+                       p1[8] = (calib[1] & S8_P1_MASK) >> S8_P1_SHIFT;
+                       p1[9] = (calib[2] & S9_P1_MASK);
+                       p1[10] = (calib[2] & S10_P1_MASK) >> S10_P1_SHIFT;
+                       break;
+               }
+       }
+
+       switch (mode) {
+       case ONE_PT_CALIB:
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p1[i] += (base1 << 2) | BIT_APPEND;
+               break;
+       case TWO_PT_CALIB:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p2[i] += base2;
+                       p2[i] <<= 2;
+                       p2[i] |= BIT_APPEND;
+               }
+               /* Fall through */
+       case ONE_PT_CALIB2:
+               for (i = 0; i < tmdev->num_sensors; i++) {
+                       p1[i] += base1;
+                       p1[i] <<= 2;
+                       p1[i] |= BIT_APPEND;
+               }
+               break;
+       default:
+               for (i = 0; i < tmdev->num_sensors; i++)
+                       p2[i] = 780;
+               p1[0] = 502;
+               p1[1] = 509;
+               p1[2] = 503;
+               p1[3] = 509;
+               p1[4] = 505;
+               p1[5] = 509;
+               p1[6] = 507;
+               p1[7] = 510;
+               p1[8] = 508;
+               p1[9] = 509;
+               p1[10] = 508;
+               break;
+       }
+
+       compute_intercept_slope(tmdev, p1, p2, mode);
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8974 = {
+       .init           = init_common,
+       .calibrate      = calibrate_8974,
+       .get_temp       = get_temp_common,
+};
+
+const struct tsens_data data_8974 = {
+       .num_sensors    = 11,
+       .ops            = &ops_8974,
+};
diff --git a/drivers/thermal/qcom/tsens-8996.c b/drivers/thermal/qcom/tsens-8996.c
new file mode 100644 (file)
index 0000000..e1f7781
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "tsens.h"
+
+#define STATUS_OFFSET  0x10a0
+#define LAST_TEMP_MASK 0xfff
+#define STATUS_VALID_BIT       BIT(21)
+#define CODE_SIGN_BIT          BIT(11)
+
+static int get_temp_8996(struct tsens_device *tmdev, int id, int *temp)
+{
+       struct tsens_sensor *s = &tmdev->sensor[id];
+       u32 code;
+       unsigned int sensor_addr;
+       int last_temp = 0, last_temp2 = 0, last_temp3 = 0, ret;
+
+       sensor_addr = STATUS_OFFSET + s->hw_id * 4;
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       last_temp = code & LAST_TEMP_MASK;
+       if (code & STATUS_VALID_BIT)
+               goto done;
+
+       /* Try a second time */
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       if (code & STATUS_VALID_BIT) {
+               last_temp = code & LAST_TEMP_MASK;
+               goto done;
+       } else {
+               last_temp2 = code & LAST_TEMP_MASK;
+       }
+
+       /* Try a third/last time */
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       if (code & STATUS_VALID_BIT) {
+               last_temp = code & LAST_TEMP_MASK;
+               goto done;
+       } else {
+               last_temp3 = code & LAST_TEMP_MASK;
+       }
+
+       if (last_temp == last_temp2)
+               last_temp = last_temp2;
+       else if (last_temp2 == last_temp3)
+               last_temp = last_temp3;
+done:
+       /* Code sign bit is the sign extension for a negative value */
+       if (last_temp & CODE_SIGN_BIT)
+               last_temp |= ~CODE_SIGN_BIT;
+
+       /* Temperatures are in deciCelicius */
+       *temp = last_temp * 100;
+
+       return 0;
+}
+
+static const struct tsens_ops ops_8996 = {
+       .init           = init_common,
+       .get_temp       = get_temp_8996,
+};
+
+const struct tsens_data data_8996 = {
+       .num_sensors    = 13,
+       .ops            = &ops_8996,
+};
diff --git a/drivers/thermal/qcom/tsens-common.c b/drivers/thermal/qcom/tsens-common.c
new file mode 100644 (file)
index 0000000..b1449ad
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "tsens.h"
+
+#define S0_ST_ADDR             0x1030
+#define SN_ADDR_OFFSET         0x4
+#define SN_ST_TEMP_MASK                0x3ff
+#define CAL_DEGC_PT1           30
+#define CAL_DEGC_PT2           120
+#define SLOPE_FACTOR           1000
+#define SLOPE_DEFAULT          3200
+
+char *qfprom_read(struct device *dev, const char *cname)
+{
+       struct nvmem_cell *cell;
+       ssize_t data;
+       char *ret;
+
+       cell = nvmem_cell_get(dev, cname);
+       if (IS_ERR(cell))
+               return ERR_CAST(cell);
+
+       ret = nvmem_cell_read(cell, &data);
+       nvmem_cell_put(cell);
+
+       return ret;
+}
+
+/*
+ * Use this function on devices where slope and offset calculations
+ * depend on calibration data read from qfprom. On others the slope
+ * and offset values are derived from tz->tzp->slope and tz->tzp->offset
+ * resp.
+ */
+void compute_intercept_slope(struct tsens_device *tmdev, u32 *p1,
+                            u32 *p2, u32 mode)
+{
+       int i;
+       int num, den;
+
+       for (i = 0; i < tmdev->num_sensors; i++) {
+               dev_dbg(tmdev->dev,
+                       "sensor%d - data_point1:%#x data_point2:%#x\n",
+                       i, p1[i], p2[i]);
+
+               tmdev->sensor[i].slope = SLOPE_DEFAULT;
+               if (mode == TWO_PT_CALIB) {
+                       /*
+                        * slope (m) = adc_code2 - adc_code1 (y2 - y1)/
+                        *      temp_120_degc - temp_30_degc (x2 - x1)
+                        */
+                       num = p2[i] - p1[i];
+                       num *= SLOPE_FACTOR;
+                       den = CAL_DEGC_PT2 - CAL_DEGC_PT1;
+                       tmdev->sensor[i].slope = num / den;
+               }
+
+               tmdev->sensor[i].offset = (p1[i] * SLOPE_FACTOR) -
+                               (CAL_DEGC_PT1 *
+                               tmdev->sensor[i].slope);
+               dev_dbg(tmdev->dev, "offset:%d\n", tmdev->sensor[i].offset);
+       }
+}
+
+static inline int code_to_degc(u32 adc_code, const struct tsens_sensor *s)
+{
+       int degc, num, den;
+
+       num = (adc_code * SLOPE_FACTOR) - s->offset;
+       den = s->slope;
+
+       if (num > 0)
+               degc = num + (den / 2);
+       else if (num < 0)
+               degc = num - (den / 2);
+       else
+               degc = num;
+
+       degc /= den;
+
+       return degc;
+}
+
+int get_temp_common(struct tsens_device *tmdev, int id, int *temp)
+{
+       struct tsens_sensor *s = &tmdev->sensor[id];
+       u32 code;
+       unsigned int sensor_addr;
+       int last_temp = 0, ret;
+
+       sensor_addr = S0_ST_ADDR + s->hw_id * SN_ADDR_OFFSET;
+       ret = regmap_read(tmdev->map, sensor_addr, &code);
+       if (ret)
+               return ret;
+       last_temp = code & SN_ST_TEMP_MASK;
+
+       *temp = code_to_degc(last_temp, s) * 1000;
+
+       return 0;
+}
+
+static const struct regmap_config tsens_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+};
+
+int __init init_common(struct tsens_device *tmdev)
+{
+       void __iomem *base;
+
+       base = of_iomap(tmdev->dev->of_node, 0);
+       if (!base)
+               return -EINVAL;
+
+       tmdev->map = devm_regmap_init_mmio(tmdev->dev, base, &tsens_config);
+       if (IS_ERR(tmdev->map)) {
+               iounmap(base);
+               return PTR_ERR(tmdev->map);
+       }
+
+       return 0;
+}
diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c
new file mode 100644 (file)
index 0000000..3f9fe6a
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+#include "tsens.h"
+
+static int tsens_get_temp(void *data, int *temp)
+{
+       const struct tsens_sensor *s = data;
+       struct tsens_device *tmdev = s->tmdev;
+
+       return tmdev->ops->get_temp(tmdev, s->id, temp);
+}
+
+static int tsens_get_trend(void *p, int trip, enum thermal_trend *trend)
+{
+       const struct tsens_sensor *s = p;
+       struct tsens_device *tmdev = s->tmdev;
+
+       if (tmdev->ops->get_trend)
+               return  tmdev->ops->get_trend(tmdev, s->id, trend);
+
+       return -ENOTSUPP;
+}
+
+static int  __maybe_unused tsens_suspend(struct device *dev)
+{
+       struct tsens_device *tmdev = dev_get_drvdata(dev);
+
+       if (tmdev->ops && tmdev->ops->suspend)
+               return tmdev->ops->suspend(tmdev);
+
+       return 0;
+}
+
+static int __maybe_unused tsens_resume(struct device *dev)
+{
+       struct tsens_device *tmdev = dev_get_drvdata(dev);
+
+       if (tmdev->ops && tmdev->ops->resume)
+               return tmdev->ops->resume(tmdev);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume);
+
+static const struct of_device_id tsens_table[] = {
+       {
+               .compatible = "qcom,msm8916-tsens",
+               .data = &data_8916,
+       }, {
+               .compatible = "qcom,msm8974-tsens",
+               .data = &data_8974,
+       }, {
+               .compatible = "qcom,msm8996-tsens",
+               .data = &data_8996,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, tsens_table);
+
+static const struct thermal_zone_of_device_ops tsens_of_ops = {
+       .get_temp = tsens_get_temp,
+       .get_trend = tsens_get_trend,
+};
+
+static int tsens_register(struct tsens_device *tmdev)
+{
+       int i;
+       struct thermal_zone_device *tzd;
+       u32 *hw_id, n = tmdev->num_sensors;
+
+       hw_id = devm_kcalloc(tmdev->dev, n, sizeof(u32), GFP_KERNEL);
+       if (!hw_id)
+               return -ENOMEM;
+
+       for (i = 0;  i < tmdev->num_sensors; i++) {
+               tmdev->sensor[i].tmdev = tmdev;
+               tmdev->sensor[i].id = i;
+               tzd = devm_thermal_zone_of_sensor_register(tmdev->dev, i,
+                                                          &tmdev->sensor[i],
+                                                          &tsens_of_ops);
+               if (IS_ERR(tzd))
+                       continue;
+               tmdev->sensor[i].tzd = tzd;
+               if (tmdev->ops->enable)
+                       tmdev->ops->enable(tmdev, i);
+       }
+       return 0;
+}
+
+static int tsens_probe(struct platform_device *pdev)
+{
+       int ret, i;
+       struct device *dev;
+       struct device_node *np;
+       struct tsens_sensor *s;
+       struct tsens_device *tmdev;
+       const struct tsens_data *data;
+       const struct of_device_id *id;
+
+       if (pdev->dev.of_node)
+               dev = &pdev->dev;
+       else
+               dev = pdev->dev.parent;
+
+       np = dev->of_node;
+
+       id = of_match_node(tsens_table, np);
+       if (id)
+               data = id->data;
+       else
+               data = &data_8960;
+
+       if (data->num_sensors <= 0) {
+               dev_err(dev, "invalid number of sensors\n");
+               return -EINVAL;
+       }
+
+       tmdev = devm_kzalloc(dev, sizeof(*tmdev) +
+                            data->num_sensors * sizeof(*s), GFP_KERNEL);
+       if (!tmdev)
+               return -ENOMEM;
+
+       tmdev->dev = dev;
+       tmdev->num_sensors = data->num_sensors;
+       tmdev->ops = data->ops;
+       for (i = 0;  i < tmdev->num_sensors; i++) {
+               if (data->hw_ids)
+                       tmdev->sensor[i].hw_id = data->hw_ids[i];
+               else
+                       tmdev->sensor[i].hw_id = i;
+       }
+
+       if (!tmdev->ops || !tmdev->ops->init || !tmdev->ops->get_temp)
+               return -EINVAL;
+
+       ret = tmdev->ops->init(tmdev);
+       if (ret < 0) {
+               dev_err(dev, "tsens init failed\n");
+               return ret;
+       }
+
+       if (tmdev->ops->calibrate) {
+               ret = tmdev->ops->calibrate(tmdev);
+               if (ret < 0) {
+                       dev_err(dev, "tsens calibration failed\n");
+                       return ret;
+               }
+       }
+
+       ret = tsens_register(tmdev);
+
+       platform_set_drvdata(pdev, tmdev);
+
+       return ret;
+}
+
+static int tsens_remove(struct platform_device *pdev)
+{
+       struct tsens_device *tmdev = platform_get_drvdata(pdev);
+
+       if (tmdev->ops->disable)
+               tmdev->ops->disable(tmdev);
+
+       return 0;
+}
+
+static struct platform_driver tsens_driver = {
+       .probe = tsens_probe,
+       .remove = tsens_remove,
+       .driver = {
+               .name = "qcom-tsens",
+               .pm     = &tsens_pm_ops,
+               .of_match_table = tsens_table,
+       },
+};
+module_platform_driver(tsens_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("QCOM Temperature Sensor driver");
+MODULE_ALIAS("platform:qcom-tsens");
diff --git a/drivers/thermal/qcom/tsens.h b/drivers/thermal/qcom/tsens.h
new file mode 100644 (file)
index 0000000..911c197
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __QCOM_TSENS_H__
+#define __QCOM_TSENS_H__
+
+#define ONE_PT_CALIB           0x1
+#define ONE_PT_CALIB2          0x2
+#define TWO_PT_CALIB           0x3
+
+#include <linux/thermal.h>
+
+struct tsens_device;
+
+struct tsens_sensor {
+       struct tsens_device             *tmdev;
+       struct thermal_zone_device      *tzd;
+       int                             offset;
+       int                             id;
+       int                             hw_id;
+       int                             slope;
+       u32                             status;
+};
+
+/**
+ * struct tsens_ops - operations as supported by the tsens device
+ * @init: Function to initialize the tsens device
+ * @calibrate: Function to calibrate the tsens device
+ * @get_temp: Function which returns the temp in millidegC
+ * @enable: Function to enable (clocks/power) tsens device
+ * @disable: Function to disable the tsens device
+ * @suspend: Function to suspend the tsens device
+ * @resume: Function to resume the tsens device
+ * @get_trend: Function to get the thermal/temp trend
+ */
+struct tsens_ops {
+       /* mandatory callbacks */
+       int (*init)(struct tsens_device *);
+       int (*calibrate)(struct tsens_device *);
+       int (*get_temp)(struct tsens_device *, int, int *);
+       /* optional callbacks */
+       int (*enable)(struct tsens_device *, int);
+       void (*disable)(struct tsens_device *);
+       int (*suspend)(struct tsens_device *);
+       int (*resume)(struct tsens_device *);
+       int (*get_trend)(struct tsens_device *, int, enum thermal_trend *);
+};
+
+/**
+ * struct tsens_data - tsens instance specific data
+ * @num_sensors: Max number of sensors supported by platform
+ * @ops: operations the tsens instance supports
+ * @hw_ids: Subset of sensors ids supported by platform, if not the first n
+ */
+struct tsens_data {
+       const u32               num_sensors;
+       const struct tsens_ops  *ops;
+       unsigned int            *hw_ids;
+};
+
+/* Registers to be saved/restored across a context loss */
+struct tsens_context {
+       int     threshold;
+       int     control;
+};
+
+struct tsens_device {
+       struct device                   *dev;
+       u32                             num_sensors;
+       struct regmap                   *map;
+       struct regmap_field             *status_field;
+       struct tsens_context            ctx;
+       bool                            trdy;
+       const struct tsens_ops          *ops;
+       struct tsens_sensor             sensor[0];
+};
+
+char *qfprom_read(struct device *, const char *);
+void compute_intercept_slope(struct tsens_device *, u32 *, u32 *, u32);
+int init_common(struct tsens_device *);
+int get_temp_common(struct tsens_device *, int, int *);
+
+extern const struct tsens_data data_8916, data_8974, data_8960, data_8996;
+
+#endif /* __QCOM_TSENS_H__ */
diff --git a/drivers/thermal/qoriq_thermal.c b/drivers/thermal/qoriq_thermal.c
new file mode 100644 (file)
index 0000000..644ba52
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2016 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/thermal.h>
+
+#include "thermal_core.h"
+
+#define SITES_MAX      16
+
+/*
+ * QorIQ TMU Registers
+ */
+struct qoriq_tmu_site_regs {
+       u32 tritsr;             /* Immediate Temperature Site Register */
+       u32 tratsr;             /* Average Temperature Site Register */
+       u8 res0[0x8];
+};
+
+struct qoriq_tmu_regs {
+       u32 tmr;                /* Mode Register */
+#define TMR_DISABLE    0x0
+#define TMR_ME         0x80000000
+#define TMR_ALPF       0x0c000000
+       u32 tsr;                /* Status Register */
+       u32 tmtmir;             /* Temperature measurement interval Register */
+#define TMTMIR_DEFAULT 0x0000000f
+       u8 res0[0x14];
+       u32 tier;               /* Interrupt Enable Register */
+#define TIER_DISABLE   0x0
+       u32 tidr;               /* Interrupt Detect Register */
+       u32 tiscr;              /* Interrupt Site Capture Register */
+       u32 ticscr;             /* Interrupt Critical Site Capture Register */
+       u8 res1[0x10];
+       u32 tmhtcrh;            /* High Temperature Capture Register */
+       u32 tmhtcrl;            /* Low Temperature Capture Register */
+       u8 res2[0x8];
+       u32 tmhtitr;            /* High Temperature Immediate Threshold */
+       u32 tmhtatr;            /* High Temperature Average Threshold */
+       u32 tmhtactr;   /* High Temperature Average Crit Threshold */
+       u8 res3[0x24];
+       u32 ttcfgr;             /* Temperature Configuration Register */
+       u32 tscfgr;             /* Sensor Configuration Register */
+       u8 res4[0x78];
+       struct qoriq_tmu_site_regs site[SITES_MAX];
+       u8 res5[0x9f8];
+       u32 ipbrr0;             /* IP Block Revision Register 0 */
+       u32 ipbrr1;             /* IP Block Revision Register 1 */
+       u8 res6[0x310];
+       u32 ttr0cr;             /* Temperature Range 0 Control Register */
+       u32 ttr1cr;             /* Temperature Range 1 Control Register */
+       u32 ttr2cr;             /* Temperature Range 2 Control Register */
+       u32 ttr3cr;             /* Temperature Range 3 Control Register */
+};
+
+/*
+ * Thermal zone data
+ */
+struct qoriq_tmu_data {
+       struct thermal_zone_device *tz;
+       struct qoriq_tmu_regs __iomem *regs;
+       int sensor_id;
+       bool little_endian;
+};
+
+static void tmu_write(struct qoriq_tmu_data *p, u32 val, void __iomem *addr)
+{
+       if (p->little_endian)
+               iowrite32(val, addr);
+       else
+               iowrite32be(val, addr);
+}
+
+static u32 tmu_read(struct qoriq_tmu_data *p, void __iomem *addr)
+{
+       if (p->little_endian)
+               return ioread32(addr);
+       else
+               return ioread32be(addr);
+}
+
+static int tmu_get_temp(void *p, int *temp)
+{
+       u32 val;
+       struct qoriq_tmu_data *data = p;
+
+       val = tmu_read(data, &data->regs->site[data->sensor_id].tritsr);
+       *temp = (val & 0xff) * 1000;
+
+       return 0;
+}
+
+static int qoriq_tmu_get_sensor_id(void)
+{
+       int ret, id;
+       struct of_phandle_args sensor_specs;
+       struct device_node *np, *sensor_np;
+
+       np = of_find_node_by_name(NULL, "thermal-zones");
+       if (!np)
+               return -ENODEV;
+
+       sensor_np = of_get_next_child(np, NULL);
+       ret = of_parse_phandle_with_args(sensor_np, "thermal-sensors",
+                       "#thermal-sensor-cells",
+                       0, &sensor_specs);
+       if (ret) {
+               of_node_put(np);
+               of_node_put(sensor_np);
+               return ret;
+       }
+
+       if (sensor_specs.args_count >= 1) {
+               id = sensor_specs.args[0];
+               WARN(sensor_specs.args_count > 1,
+                               "%s: too many cells in sensor specifier %d\n",
+                               sensor_specs.np->name, sensor_specs.args_count);
+       } else {
+               id = 0;
+       }
+
+       of_node_put(np);
+       of_node_put(sensor_np);
+
+       return id;
+}
+
+static int qoriq_tmu_calibration(struct platform_device *pdev)
+{
+       int i, val, len;
+       u32 range[4];
+       const u32 *calibration;
+       struct device_node *np = pdev->dev.of_node;
+       struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+       if (of_property_read_u32_array(np, "fsl,tmu-range", range, 4)) {
+               dev_err(&pdev->dev, "missing calibration range.\n");
+               return -ENODEV;
+       }
+
+       /* Init temperature range registers */
+       tmu_write(data, range[0], &data->regs->ttr0cr);
+       tmu_write(data, range[1], &data->regs->ttr1cr);
+       tmu_write(data, range[2], &data->regs->ttr2cr);
+       tmu_write(data, range[3], &data->regs->ttr3cr);
+
+       calibration = of_get_property(np, "fsl,tmu-calibration", &len);
+       if (calibration == NULL || len % 8) {
+               dev_err(&pdev->dev, "invalid calibration data.\n");
+               return -ENODEV;
+       }
+
+       for (i = 0; i < len; i += 8, calibration += 2) {
+               val = of_read_number(calibration, 1);
+               tmu_write(data, val, &data->regs->ttcfgr);
+               val = of_read_number(calibration + 1, 1);
+               tmu_write(data, val, &data->regs->tscfgr);
+       }
+
+       return 0;
+}
+
+static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)
+{
+       /* Disable interrupt, using polling instead */
+       tmu_write(data, TIER_DISABLE, &data->regs->tier);
+
+       /* Set update_interval */
+       tmu_write(data, TMTMIR_DEFAULT, &data->regs->tmtmir);
+
+       /* Disable monitoring */
+       tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+}
+
+static struct thermal_zone_of_device_ops tmu_tz_ops = {
+       .get_temp = tmu_get_temp,
+};
+
+static int qoriq_tmu_probe(struct platform_device *pdev)
+{
+       int ret;
+       const struct thermal_trip *trip;
+       struct qoriq_tmu_data *data;
+       struct device_node *np = pdev->dev.of_node;
+       u32 site = 0;
+
+       if (!np) {
+               dev_err(&pdev->dev, "Device OF-Node is NULL");
+               return -ENODEV;
+       }
+
+       data = devm_kzalloc(&pdev->dev, sizeof(struct qoriq_tmu_data),
+                           GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, data);
+
+       data->little_endian = of_property_read_bool(np, "little-endian");
+
+       data->sensor_id = qoriq_tmu_get_sensor_id();
+       if (data->sensor_id < 0) {
+               dev_err(&pdev->dev, "Failed to get sensor id\n");
+               ret = -ENODEV;
+               goto err_iomap;
+       }
+
+       data->regs = of_iomap(np, 0);
+       if (!data->regs) {
+               dev_err(&pdev->dev, "Failed to get memory region\n");
+               ret = -ENODEV;
+               goto err_iomap;
+       }
+
+       qoriq_tmu_init_device(data);    /* TMU initialization */
+
+       ret = qoriq_tmu_calibration(pdev);      /* TMU calibration */
+       if (ret < 0)
+               goto err_tmu;
+
+       data->tz = thermal_zone_of_sensor_register(&pdev->dev, data->sensor_id,
+                               data, &tmu_tz_ops);
+       if (IS_ERR(data->tz)) {
+               ret = PTR_ERR(data->tz);
+               dev_err(&pdev->dev,
+                       "Failed to register thermal zone device %d\n", ret);
+               goto err_tmu;
+       }
+
+       trip = of_thermal_get_trip_points(data->tz);
+
+       /* Enable monitoring */
+       site |= 0x1 << (15 - data->sensor_id);
+       tmu_write(data, site | TMR_ME | TMR_ALPF, &data->regs->tmr);
+
+       return 0;
+
+err_tmu:
+       iounmap(data->regs);
+
+err_iomap:
+       platform_set_drvdata(pdev, NULL);
+
+       return ret;
+}
+
+static int qoriq_tmu_remove(struct platform_device *pdev)
+{
+       struct qoriq_tmu_data *data = platform_get_drvdata(pdev);
+
+       thermal_zone_of_sensor_unregister(&pdev->dev, data->tz);
+
+       /* Disable monitoring */
+       tmu_write(data, TMR_DISABLE, &data->regs->tmr);
+
+       iounmap(data->regs);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int qoriq_tmu_suspend(struct device *dev)
+{
+       u32 tmr;
+       struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+       /* Disable monitoring */
+       tmr = tmu_read(data, &data->regs->tmr);
+       tmr &= ~TMR_ME;
+       tmu_write(data, tmr, &data->regs->tmr);
+
+       return 0;
+}
+
+static int qoriq_tmu_resume(struct device *dev)
+{
+       u32 tmr;
+       struct qoriq_tmu_data *data = dev_get_drvdata(dev);
+
+       /* Enable monitoring */
+       tmr = tmu_read(data, &data->regs->tmr);
+       tmr |= TMR_ME;
+       tmu_write(data, tmr, &data->regs->tmr);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,
+                        qoriq_tmu_suspend, qoriq_tmu_resume);
+
+static const struct of_device_id qoriq_tmu_match[] = {
+       { .compatible = "fsl,qoriq-tmu", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, qoriq_tmu_match);
+
+static struct platform_driver qoriq_tmu = {
+       .driver = {
+               .name           = "qoriq_thermal",
+               .pm             = &qoriq_tmu_pm_ops,
+               .of_match_table = qoriq_tmu_match,
+       },
+       .probe  = qoriq_tmu_probe,
+       .remove = qoriq_tmu_remove,
+};
+module_platform_driver(qoriq_tmu);
+
+MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");
+MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");
+MODULE_LICENSE("GPL v2");
index 5f81792..73e5fee 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/spinlock.h>
 #include <linux/thermal.h>
 
+#include "thermal_hwmon.h"
+
 #define IDLE_INTERVAL  5000
 
 #define COMMON_STR     0x00
@@ -75,6 +77,8 @@ struct rcar_thermal_priv {
 #define rcar_priv_to_dev(priv)         ((priv)->common->dev)
 #define rcar_has_irq_support(priv)     ((priv)->common->base)
 #define rcar_id_to_shift(priv)         ((priv)->id * 8)
+#define rcar_of_data(dev)              ((unsigned long)of_device_get_match_data(dev))
+#define rcar_use_of_thermal(dev)       (rcar_of_data(dev) == USE_OF_THERMAL)
 
 #define USE_OF_THERMAL 1
 static const struct of_device_id rcar_thermal_dt_ids[] = {
@@ -354,7 +358,8 @@ static void rcar_thermal_work(struct work_struct *work)
                return;
 
        if (nctemp != cctemp)
-               thermal_zone_device_update(priv->zone);
+               thermal_zone_device_update(priv->zone,
+                                          THERMAL_EVENT_UNSPECIFIED);
 }
 
 static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status)
@@ -415,7 +420,10 @@ static int rcar_thermal_remove(struct platform_device *pdev)
 
        rcar_thermal_for_each_priv(priv, common) {
                rcar_thermal_irq_disable(priv);
-               thermal_zone_device_unregister(priv->zone);
+               if (rcar_use_of_thermal(dev))
+                       thermal_remove_hwmon_sysfs(priv->zone);
+               else
+                       thermal_zone_device_unregister(priv->zone);
        }
 
        pm_runtime_put(dev);
@@ -430,7 +438,6 @@ static int rcar_thermal_probe(struct platform_device *pdev)
        struct rcar_thermal_priv *priv;
        struct device *dev = &pdev->dev;
        struct resource *res, *irq;
-       unsigned long of_data = (unsigned long)of_device_get_match_data(dev);
        int mres = 0;
        int i;
        int ret = -ENODEV;
@@ -491,7 +498,7 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                if (ret < 0)
                        goto error_unregister;
 
-               if (of_data == USE_OF_THERMAL)
+               if (rcar_use_of_thermal(dev))
                        priv->zone = devm_thermal_zone_of_sensor_register(
                                                dev, i, priv,
                                                &rcar_thermal_zone_of_ops);
@@ -508,6 +515,17 @@ static int rcar_thermal_probe(struct platform_device *pdev)
                        goto error_unregister;
                }
 
+               if (rcar_use_of_thermal(dev)) {
+                       /*
+                        * thermal_zone doesn't enable hwmon as default,
+                        * but, enable it here to keep compatible
+                        */
+                       priv->zone->tzp->no_hwmon = false;
+                       ret = thermal_add_hwmon_sysfs(priv->zone);
+                       if (ret)
+                               goto error_unregister;
+               }
+
                rcar_thermal_irq_enable(priv);
 
                list_move_tail(&priv->list, &common->head);
index 5d491f1..e227a9f 100644 (file)
@@ -96,6 +96,7 @@ struct chip_tsadc_table {
  * @initialize: SoC special initialize tsadc controller method
  * @irq_ack: clear the interrupt
  * @get_temp: get the temperature
+ * @set_alarm_temp: set the high temperature interrupt
  * @set_tshut_temp: set the hardware-controlled shutdown temperature
  * @set_tshut_mode: set the hardware-controlled shutdown mode
  * @table: the chip-specific conversion table
@@ -119,6 +120,8 @@ struct rockchip_tsadc_chip {
        /* Per-sensor methods */
        int (*get_temp)(struct chip_tsadc_table table,
                        int chn, void __iomem *reg, int *temp);
+       void (*set_alarm_temp)(struct chip_tsadc_table table,
+                              int chn, void __iomem *reg, int temp);
        void (*set_tshut_temp)(struct chip_tsadc_table table,
                               int chn, void __iomem *reg, int temp);
        void (*set_tshut_mode)(int chn, void __iomem *reg, enum tshut_mode m);
@@ -183,6 +186,7 @@ struct rockchip_thermal_data {
 #define TSADCV2_INT_EN                         0x08
 #define TSADCV2_INT_PD                         0x0c
 #define TSADCV2_DATA(chn)                      (0x20 + (chn) * 0x04)
+#define TSADCV2_COMP_INT(chn)                  (0x30 + (chn) * 0x04)
 #define TSADCV2_COMP_SHUT(chn)                 (0x40 + (chn) * 0x04)
 #define TSADCV2_HIGHT_INT_DEBOUNCE             0x60
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE           0x64
@@ -207,18 +211,21 @@ struct rockchip_thermal_data {
 
 #define TSADCV2_HIGHT_INT_DEBOUNCE_COUNT       4
 #define TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT     4
-#define TSADCV2_AUTO_PERIOD_TIME               250 /* msec */
-#define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* msec */
+#define TSADCV2_AUTO_PERIOD_TIME               250 /* 250ms */
+#define TSADCV2_AUTO_PERIOD_HT_TIME            50  /* 50ms */
+#define TSADCV3_AUTO_PERIOD_TIME               1875 /* 2.5ms */
+#define TSADCV3_AUTO_PERIOD_HT_TIME            1875 /* 2.5ms */
+
 #define TSADCV2_USER_INTER_PD_SOC              0x340 /* 13 clocks */
 
 #define GRF_SARADC_TESTBIT                     0x0e644
 #define GRF_TSADC_TESTBIT_L                    0x0e648
 #define GRF_TSADC_TESTBIT_H                    0x0e64c
 
-#define GRF_TSADC_TSEN_PD_ON                   (0x30003 << 0)
-#define GRF_TSADC_TSEN_PD_OFF                  (0x30000 << 0)
 #define GRF_SARADC_TESTBIT_ON                  (0x10001 << 2)
 #define GRF_TSADC_TESTBIT_H_ON                 (0x10001 << 2)
+#define GRF_TSADC_VCM_EN_L                     (0x10001 << 7)
+#define GRF_TSADC_VCM_EN_H                     (0x10001 << 7)
 
 /**
  * struct tsadc_table - code to temperature conversion table
@@ -394,13 +401,17 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table,
                                   int temp)
 {
        int high, low, mid;
+       u32 error = 0;
 
        low = 0;
        high = table.length - 1;
        mid = (high + low) / 2;
 
-       if (temp < table.id[low].temp || temp > table.id[high].temp)
-               return 0;
+       /* Return mask code data when the temp is over table range */
+       if (temp < table.id[low].temp || temp > table.id[high].temp) {
+               error = table.data_mask;
+               goto exit;
+       }
 
        while (low <= high) {
                if (temp == table.id[mid].temp)
@@ -412,7 +423,9 @@ static u32 rk_tsadcv2_temp_to_code(struct chip_tsadc_table table,
                mid = (low + high) / 2;
        }
 
-       return 0;
+exit:
+       pr_err("Invalid the conversion, error=%d\n", error);
+       return error;
 }
 
 static int rk_tsadcv2_code_to_temp(struct chip_tsadc_table table, u32 code,
@@ -543,14 +556,34 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
                /* Set interleave value to workround ic time sync issue */
                writel_relaxed(TSADCV2_USER_INTER_PD_SOC, regs +
                               TSADCV2_USER_CON);
+
+               writel_relaxed(TSADCV2_AUTO_PERIOD_TIME,
+                              regs + TSADCV2_AUTO_PERIOD);
+               writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+               writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
+                              regs + TSADCV2_AUTO_PERIOD_HT);
+               writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
+
        } else {
-               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_ON);
-               mdelay(10);
-               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_TSEN_PD_OFF);
+               /* Enable the voltage common mode feature */
+               regmap_write(grf, GRF_TSADC_TESTBIT_L, GRF_TSADC_VCM_EN_L);
+               regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_VCM_EN_H);
+
                usleep_range(15, 100); /* The spec note says at least 15 us */
                regmap_write(grf, GRF_SARADC_TESTBIT, GRF_SARADC_TESTBIT_ON);
                regmap_write(grf, GRF_TSADC_TESTBIT_H, GRF_TSADC_TESTBIT_H_ON);
                usleep_range(90, 200); /* The spec note says at least 90 us */
+
+               writel_relaxed(TSADCV3_AUTO_PERIOD_TIME,
+                              regs + TSADCV2_AUTO_PERIOD);
+               writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_INT_DEBOUNCE);
+               writel_relaxed(TSADCV3_AUTO_PERIOD_HT_TIME,
+                              regs + TSADCV2_AUTO_PERIOD_HT);
+               writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
+                              regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
        }
 
        if (tshut_polarity == TSHUT_HIGH_ACTIVE)
@@ -559,14 +592,6 @@ static void rk_tsadcv3_initialize(struct regmap *grf, void __iomem *regs,
        else
                writel_relaxed(0U & ~TSADCV2_AUTO_TSHUT_POLARITY_HIGH,
                               regs + TSADCV2_AUTO_CON);
-
-       writel_relaxed(TSADCV2_AUTO_PERIOD_TIME, regs + TSADCV2_AUTO_PERIOD);
-       writel_relaxed(TSADCV2_HIGHT_INT_DEBOUNCE_COUNT,
-                      regs + TSADCV2_HIGHT_INT_DEBOUNCE);
-       writel_relaxed(TSADCV2_AUTO_PERIOD_HT_TIME,
-                      regs + TSADCV2_AUTO_PERIOD_HT);
-       writel_relaxed(TSADCV2_HIGHT_TSHUT_DEBOUNCE_COUNT,
-                      regs + TSADCV2_HIGHT_TSHUT_DEBOUNCE);
 }
 
 static void rk_tsadcv2_irq_ack(void __iomem *regs)
@@ -628,12 +653,34 @@ static int rk_tsadcv2_get_temp(struct chip_tsadc_table table,
        return rk_tsadcv2_code_to_temp(table, val, temp);
 }
 
+static void rk_tsadcv2_alarm_temp(struct chip_tsadc_table table,
+                                 int chn, void __iomem *regs, int temp)
+{
+       u32 alarm_value, int_en;
+
+       /* Make sure the value is valid */
+       alarm_value = rk_tsadcv2_temp_to_code(table, temp);
+       if (alarm_value == table.data_mask)
+               return;
+
+       writel_relaxed(alarm_value & table.data_mask,
+                      regs + TSADCV2_COMP_INT(chn));
+
+       int_en = readl_relaxed(regs + TSADCV2_INT_EN);
+       int_en |= TSADCV2_INT_SRC_EN(chn);
+       writel_relaxed(int_en, regs + TSADCV2_INT_EN);
+}
+
 static void rk_tsadcv2_tshut_temp(struct chip_tsadc_table table,
                                  int chn, void __iomem *regs, int temp)
 {
        u32 tshut_value, val;
 
+       /* Make sure the value is valid */
        tshut_value = rk_tsadcv2_temp_to_code(table, temp);
+       if (tshut_value == table.data_mask)
+               return;
+
        writel_relaxed(tshut_value, regs + TSADCV2_COMP_SHUT(chn));
 
        /* TSHUT will be valid */
@@ -670,6 +717,7 @@ static const struct rockchip_tsadc_chip rk3228_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -694,6 +742,7 @@ static const struct rockchip_tsadc_chip rk3288_tsadc_data = {
        .irq_ack = rk_tsadcv2_irq_ack,
        .control = rk_tsadcv2_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -718,6 +767,7 @@ static const struct rockchip_tsadc_chip rk3366_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -742,6 +792,7 @@ static const struct rockchip_tsadc_chip rk3368_tsadc_data = {
        .irq_ack = rk_tsadcv2_irq_ack,
        .control = rk_tsadcv2_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -766,6 +817,7 @@ static const struct rockchip_tsadc_chip rk3399_tsadc_data = {
        .irq_ack = rk_tsadcv3_irq_ack,
        .control = rk_tsadcv3_control,
        .get_temp = rk_tsadcv2_get_temp,
+       .set_alarm_temp = rk_tsadcv2_alarm_temp,
        .set_tshut_temp = rk_tsadcv2_tshut_temp,
        .set_tshut_mode = rk_tsadcv2_tshut_mode,
 
@@ -821,11 +873,27 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev)
        thermal->chip->irq_ack(thermal->regs);
 
        for (i = 0; i < thermal->chip->chn_num; i++)
-               thermal_zone_device_update(thermal->sensors[i].tzd);
+               thermal_zone_device_update(thermal->sensors[i].tzd,
+                                          THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
 
+static int rockchip_thermal_set_trips(void *_sensor, int low, int high)
+{
+       struct rockchip_thermal_sensor *sensor = _sensor;
+       struct rockchip_thermal_data *thermal = sensor->thermal;
+       const struct rockchip_tsadc_chip *tsadc = thermal->chip;
+
+       dev_dbg(&thermal->pdev->dev, "%s: sensor %d: low: %d, high %d\n",
+               __func__, sensor->id, low, high);
+
+       tsadc->set_alarm_temp(tsadc->table,
+                             sensor->id, thermal->regs, high);
+
+       return 0;
+}
+
 static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 {
        struct rockchip_thermal_sensor *sensor = _sensor;
@@ -843,6 +911,7 @@ static int rockchip_thermal_get_temp(void *_sensor, int *out_temp)
 
 static const struct thermal_zone_of_device_ops rockchip_of_thermal_ops = {
        .get_temp = rockchip_thermal_get_temp,
+       .set_trips = rockchip_thermal_set_trips,
 };
 
 static int rockchip_configure_from_dt(struct device *dev,
index f3ce94e..ad1186d 100644 (file)
@@ -225,7 +225,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p)
                return;
        }
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        mutex_lock(&tz->lock);
        /* Find the level for which trip happened */
index fc0c9e1..91d4231 100644 (file)
@@ -42,7 +42,8 @@ static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata)
 {
        struct st_thermal_sensor *sensor = sdata;
 
-       thermal_zone_device_update(sensor->thermal_dev);
+       thermal_zone_device_update(sensor->thermal_dev,
+                                  THERMAL_EVENT_UNSPECIFIED);
 
        return IRQ_HANDLED;
 }
index 70e0d9f..201304a 100644 (file)
@@ -64,6 +64,12 @@ static const struct thermal_zone_of_device_ops ops = {
        .get_temp       = tango_get_temp,
 };
 
+static void tango_thermal_init(struct tango_thermal_priv *priv)
+{
+       writel(0, priv->base + TEMPSI_CFG);
+       writel(CMD_ON, priv->base + TEMPSI_CMD);
+}
+
 static int tango_thermal_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -79,14 +85,22 @@ static int tango_thermal_probe(struct platform_device *pdev)
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
+       platform_set_drvdata(pdev, priv);
        priv->thresh_idx = IDX_MIN;
-       writel(0, priv->base + TEMPSI_CFG);
-       writel(CMD_ON, priv->base + TEMPSI_CMD);
+       tango_thermal_init(priv);
 
        tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
        return PTR_ERR_OR_ZERO(tzdev);
 }
 
+static int __maybe_unused tango_thermal_resume(struct device *dev)
+{
+       tango_thermal_init(dev_get_drvdata(dev));
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume);
+
 static const struct of_device_id tango_sensor_ids[] = {
        {
                .compatible = "sigma,smp8758-thermal",
@@ -99,6 +113,7 @@ static struct platform_driver tango_thermal_driver = {
        .driver = {
                .name           = "tango-thermal",
                .of_match_table = tango_sensor_ids,
+               .pm             = &tango_thermal_pm,
        },
 };
 
index b865172..7d2db23 100644 (file)
@@ -30,6 +30,7 @@
 
 #include <dt-bindings/thermal/tegra124-soctherm.h>
 
+#include "../thermal_core.h"
 #include "soctherm.h"
 
 #define SENSOR_CONFIG0                         0
 #define READBACK_ADD_HALF                      BIT(7)
 #define READBACK_NEGATE                                BIT(0)
 
+/*
+ * THERMCTL_LEVEL0_GROUP_CPU is defined in soctherm.h
+ * because it will be used by tegraxxx_soctherm.c
+ */
+#define THERMCTL_LVL0_CPU0_EN_MASK             BIT(8)
+#define THERMCTL_LVL0_CPU0_CPU_THROT_MASK      (0x3 << 5)
+#define THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT     0x1
+#define THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY     0x2
+#define THERMCTL_LVL0_CPU0_GPU_THROT_MASK      (0x3 << 3)
+#define THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT     0x1
+#define THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY     0x2
+#define THERMCTL_LVL0_CPU0_MEM_THROT_MASK      BIT(2)
+#define THERMCTL_LVL0_CPU0_STATUS_MASK         0x3
+
+#define THERMCTL_LVL0_UP_STATS                 0x10
+#define THERMCTL_LVL0_DN_STATS                 0x14
+
+#define THERMCTL_STATS_CTL                     0x94
+#define STATS_CTL_CLR_DN                       0x8
+#define STATS_CTL_EN_DN                                0x4
+#define STATS_CTL_CLR_UP                       0x2
+#define STATS_CTL_EN_UP                                0x1
+
+#define THROT_GLOBAL_CFG                       0x400
+#define THROT_GLOBAL_ENB_MASK                  BIT(0)
+
+#define CPU_PSKIP_STATUS                       0x418
+#define XPU_PSKIP_STATUS_M_MASK                        (0xff << 12)
+#define XPU_PSKIP_STATUS_N_MASK                        (0xff << 4)
+#define XPU_PSKIP_STATUS_SW_OVERRIDE_MASK      BIT(1)
+#define XPU_PSKIP_STATUS_ENABLED_MASK          BIT(0)
+
+#define THROT_PRIORITY_LOCK                    0x424
+#define THROT_PRIORITY_LOCK_PRIORITY_MASK      0xff
+
+#define THROT_STATUS                           0x428
+#define THROT_STATUS_BREACH_MASK               BIT(12)
+#define THROT_STATUS_STATE_MASK                        (0xff << 4)
+#define THROT_STATUS_ENABLED_MASK              BIT(0)
+
+#define THROT_PSKIP_CTRL_LITE_CPU              0x430
+#define THROT_PSKIP_CTRL_ENABLE_MASK            BIT(31)
+#define THROT_PSKIP_CTRL_DIVIDEND_MASK          (0xff << 8)
+#define THROT_PSKIP_CTRL_DIVISOR_MASK           0xff
+#define THROT_PSKIP_CTRL_VECT_GPU_MASK          (0x7 << 16)
+#define THROT_PSKIP_CTRL_VECT_CPU_MASK          (0x7 << 8)
+#define THROT_PSKIP_CTRL_VECT2_CPU_MASK         0x7
+
+#define THROT_VECT_NONE                                0x0 /* 3'b000 */
+#define THROT_VECT_LOW                         0x1 /* 3'b001 */
+#define THROT_VECT_MED                         0x3 /* 3'b011 */
+#define THROT_VECT_HIGH                                0x7 /* 3'b111 */
+
+#define THROT_PSKIP_RAMP_LITE_CPU              0x434
+#define THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK  BIT(31)
+#define THROT_PSKIP_RAMP_DURATION_MASK         (0xffff << 8)
+#define THROT_PSKIP_RAMP_STEP_MASK             0xff
+
+#define THROT_PRIORITY_LITE                    0x444
+#define THROT_PRIORITY_LITE_PRIO_MASK          0xff
+
+#define THROT_DELAY_LITE                       0x448
+#define THROT_DELAY_LITE_DELAY_MASK            0xff
+
+/* car register offsets needed for enabling HW throttling */
+#define CAR_SUPER_CCLKG_DIVIDER                        0x36c
+#define CDIVG_USE_THERM_CONTROLS_MASK          BIT(30)
+
+/* ccroc register offsets needed for enabling HW throttling for Tegra132 */
+#define CCROC_SUPER_CCLKG_DIVIDER              0x024
+
+#define CCROC_GLOBAL_CFG                       0x148
+
+#define CCROC_THROT_PSKIP_RAMP_CPU             0x150
+#define CCROC_THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK    BIT(31)
+#define CCROC_THROT_PSKIP_RAMP_DURATION_MASK   (0xffff << 8)
+#define CCROC_THROT_PSKIP_RAMP_STEP_MASK       0xff
+
+#define CCROC_THROT_PSKIP_CTRL_CPU             0x154
+#define CCROC_THROT_PSKIP_CTRL_ENB_MASK                BIT(31)
+#define CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK   (0xff << 8)
+#define CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK    0xff
+
 /* get val from register(r) mask bits(m) */
 #define REG_GET_MASK(r, m)     (((r) & (m)) >> (ffs(m) - 1))
 /* set val(v) to mask bits(m) of register(r) */
 #define REG_SET_MASK(r, m, v)  (((r) & ~(m)) | \
                                 (((v) & (m >> (ffs(m) - 1))) << (ffs(m) - 1)))
 
+/* get dividend from the depth */
+#define THROT_DEPTH_DIVIDEND(depth)    ((256 * (100 - (depth)) / 100) - 1)
+
+/* get THROT_PSKIP_xxx offset per LIGHT/HEAVY throt and CPU/GPU dev */
+#define THROT_OFFSET                   0x30
+#define THROT_PSKIP_CTRL(throt, dev)   (THROT_PSKIP_CTRL_LITE_CPU + \
+                                       (THROT_OFFSET * throt) + (8 * dev))
+#define THROT_PSKIP_RAMP(throt, dev)   (THROT_PSKIP_RAMP_LITE_CPU + \
+                                       (THROT_OFFSET * throt) + (8 * dev))
+
+/* get THROT_xxx_CTRL offset per LIGHT/HEAVY throt */
+#define THROT_PRIORITY_CTRL(throt)     (THROT_PRIORITY_LITE + \
+                                       (THROT_OFFSET * throt))
+#define THROT_DELAY_CTRL(throt)                (THROT_DELAY_LITE + \
+                                       (THROT_OFFSET * throt))
+
+/* get CCROC_THROT_PSKIP_xxx offset per HIGH/MED/LOW vect*/
+#define CCROC_THROT_OFFSET                     0x0c
+#define CCROC_THROT_PSKIP_CTRL_CPU_REG(vect)    (CCROC_THROT_PSKIP_CTRL_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+#define CCROC_THROT_PSKIP_RAMP_CPU_REG(vect)    (CCROC_THROT_PSKIP_RAMP_CPU + \
+                                               (CCROC_THROT_OFFSET * vect))
+
+/* get THERMCTL_LEVELx offset per CPU/GPU/MEM/TSENSE rg and LEVEL0~3 lv */
+#define THERMCTL_LVL_REGS_SIZE         0x20
+#define THERMCTL_LVL_REG(rg, lv)       ((rg) + ((lv) * THERMCTL_LVL_REGS_SIZE))
+
 static const int min_low_temp = -127000;
 static const int max_high_temp = 127000;
 
+enum soctherm_throttle_id {
+       THROTTLE_LIGHT = 0,
+       THROTTLE_HEAVY,
+       THROTTLE_SIZE,
+};
+
+enum soctherm_throttle_dev_id {
+       THROTTLE_DEV_CPU = 0,
+       THROTTLE_DEV_GPU,
+       THROTTLE_DEV_SIZE,
+};
+
+static const char *const throt_names[] = {
+       [THROTTLE_LIGHT] = "light",
+       [THROTTLE_HEAVY] = "heavy",
+};
+
+struct tegra_soctherm;
 struct tegra_thermctl_zone {
        void __iomem *reg;
        struct device *dev;
+       struct tegra_soctherm *ts;
        struct thermal_zone_device *tz;
        const struct tegra_tsensor_group *sg;
 };
 
+struct soctherm_throt_cfg {
+       const char *name;
+       unsigned int id;
+       u8 priority;
+       u8 cpu_throt_level;
+       u32 cpu_throt_depth;
+       struct thermal_cooling_device *cdev;
+       bool init;
+};
+
 struct tegra_soctherm {
        struct reset_control *reset;
        struct clk *clock_tsensor;
        struct clk *clock_soctherm;
        void __iomem *regs;
-       struct thermal_zone_device **thermctl_tzs;
+       void __iomem *clk_regs;
+       void __iomem *ccroc_regs;
 
        u32 *calib;
+       struct thermal_zone_device **thermctl_tzs;
        struct tegra_soctherm_soc *soc;
 
+       struct soctherm_throt_cfg throt_cfgs[THROTTLE_SIZE];
+
        struct dentry *debugfs_dir;
 };
 
+/**
+ * clk_writel() - writes a value to a CAR register
+ * @ts: pointer to a struct tegra_soctherm
+ * @v: the value to write
+ * @reg: the register offset
+ *
+ * Writes @v to @reg.  No return value.
+ */
+static inline void clk_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
+{
+       writel(value, (ts->clk_regs + reg));
+}
+
+/**
+ * clk_readl() - reads specified register from CAR IP block
+ * @ts: pointer to a struct tegra_soctherm
+ * @reg: register address to be read
+ *
+ * Return: the value of the register
+ */
+static inline u32 clk_readl(struct tegra_soctherm *ts, u32 reg)
+{
+       return readl(ts->clk_regs + reg);
+}
+
+/**
+ * ccroc_writel() - writes a value to a CCROC register
+ * @ts: pointer to a struct tegra_soctherm
+ * @v: the value to write
+ * @reg: the register offset
+ *
+ * Writes @v to @reg.  No return value.
+ */
+static inline void ccroc_writel(struct tegra_soctherm *ts, u32 value, u32 reg)
+{
+       writel(value, (ts->ccroc_regs + reg));
+}
+
+/**
+ * ccroc_readl() - reads specified register from CCROC IP block
+ * @ts: pointer to a struct tegra_soctherm
+ * @reg: register address to be read
+ *
+ * Return: the value of the register
+ */
+static inline u32 ccroc_readl(struct tegra_soctherm *ts, u32 reg)
+{
+       return readl(ts->ccroc_regs + reg);
+}
+
 static void enable_tsensor(struct tegra_soctherm *tegra, unsigned int i)
 {
        const struct tegra_tsensor *sensor = &tegra->soc->tsensors[i];
@@ -150,11 +344,17 @@ static int tegra_thermctl_get_temp(void *data, int *out_temp)
 static int
 thermtrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
                  int trip_temp);
+static int
+throttrip_program(struct device *dev, const struct tegra_tsensor_group *sg,
+                 struct soctherm_throt_cfg *stc, int trip_temp);
+static struct soctherm_throt_cfg *
+find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name);
 
 static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
 {
        struct tegra_thermctl_zone *zone = data;
        struct thermal_zone_device *tz = zone->tz;
+       struct tegra_soctherm *ts = zone->ts;
        const struct tegra_tsensor_group *sg = zone->sg;
        struct device *dev = zone->dev;
        enum thermal_trip_type type;
@@ -167,10 +367,29 @@ static int tegra_thermctl_set_trip_temp(void *data, int trip, int temp)
        if (ret)
                return ret;
 
-       if (type != THERMAL_TRIP_CRITICAL)
-               return 0;
+       if (type == THERMAL_TRIP_CRITICAL) {
+               return thermtrip_program(dev, sg, temp);
+       } else if (type == THERMAL_TRIP_HOT) {
+               int i;
+
+               for (i = 0; i < THROTTLE_SIZE; i++) {
+                       struct thermal_cooling_device *cdev;
+                       struct soctherm_throt_cfg *stc;
+
+                       if (!ts->throt_cfgs[i].init)
+                               continue;
+
+                       cdev = ts->throt_cfgs[i].cdev;
+                       if (get_thermal_instance(tz, cdev, trip))
+                               stc = find_throttle_cfg_by_name(ts, cdev->type);
+                       else
+                               continue;
+
+                       return throttrip_program(dev, sg, stc, temp);
+               }
+       }
 
-       return thermtrip_program(dev, sg, temp);
+       return 0;
 }
 
 static const struct thermal_zone_of_device_ops tegra_of_thermal_ops = {
@@ -237,15 +456,111 @@ static int thermtrip_program(struct device *dev,
        return 0;
 }
 
+/**
+ * throttrip_program() - Configures the hardware to throttle the
+ * pulse if a given sensor group reaches a given temperature
+ * @dev: ptr to the struct device for the SOC_THERM IP block
+ * @sg: pointer to the sensor group to set the thermtrip temperature for
+ * @stc: pointer to the throttle need to be triggered
+ * @trip_temp: the temperature in millicelsius to trigger the thermal trip at
+ *
+ * Sets the thermal trip threshold and throttle event of the given sensor
+ * group. If this threshold is crossed, the hardware will trigger the
+ * throttle.
+ *
+ * Note that, although @trip_temp is specified in millicelsius, the
+ * hardware is programmed in degrees Celsius.
+ *
+ * Return: 0 upon success, or %-EINVAL upon failure.
+ */
+static int throttrip_program(struct device *dev,
+                            const struct tegra_tsensor_group *sg,
+                            struct soctherm_throt_cfg *stc,
+                            int trip_temp)
+{
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       int temp, cpu_throt, gpu_throt;
+       unsigned int throt;
+       u32 r, reg_off;
+
+       if (!dev || !sg || !stc || !stc->init)
+               return -EINVAL;
+
+       temp = enforce_temp_range(dev, trip_temp) / ts->soc->thresh_grain;
+
+       /* Hardcode LIGHT on LEVEL1 and HEAVY on LEVEL2 */
+       throt = stc->id;
+       reg_off = THERMCTL_LVL_REG(sg->thermctl_lvl0_offset, throt + 1);
+
+       if (throt == THROTTLE_LIGHT) {
+               cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT;
+               gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT;
+       } else {
+               cpu_throt = THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY;
+               gpu_throt = THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY;
+               if (throt != THROTTLE_HEAVY)
+                       dev_warn(dev,
+                                "invalid throt id %d - assuming HEAVY",
+                                throt);
+       }
+
+       r = readl(ts->regs + reg_off);
+       r = REG_SET_MASK(r, sg->thermctl_lvl0_up_thresh_mask, temp);
+       r = REG_SET_MASK(r, sg->thermctl_lvl0_dn_thresh_mask, temp);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_CPU_THROT_MASK, cpu_throt);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_GPU_THROT_MASK, gpu_throt);
+       r = REG_SET_MASK(r, THERMCTL_LVL0_CPU0_EN_MASK, 1);
+       writel(r, ts->regs + reg_off);
+
+       return 0;
+}
+
+static struct soctherm_throt_cfg *
+find_throttle_cfg_by_name(struct tegra_soctherm *ts, const char *name)
+{
+       unsigned int i;
+
+       for (i = 0; ts->throt_cfgs[i].name; i++)
+               if (!strcmp(ts->throt_cfgs[i].name, name))
+                       return &ts->throt_cfgs[i];
+
+       return NULL;
+}
+
+static int get_hot_temp(struct thermal_zone_device *tz, int *trip, int *temp)
+{
+       int ntrips, i, ret;
+       enum thermal_trip_type type;
+
+       ntrips = of_thermal_get_ntrips(tz);
+       if (ntrips <= 0)
+               return -EINVAL;
+
+       for (i = 0; i < ntrips; i++) {
+               ret = tz->ops->get_trip_type(tz, i, &type);
+               if (ret)
+                       return -EINVAL;
+               if (type == THERMAL_TRIP_HOT) {
+                       ret = tz->ops->get_trip_temp(tz, i, temp);
+                       if (!ret)
+                               *trip = i;
+
+                       return ret;
+               }
+       }
+
+       return -EINVAL;
+}
+
 /**
  * tegra_soctherm_set_hwtrips() - set HW trip point from DT data
  * @dev: struct device * of the SOC_THERM instance
  *
  * Configure the SOC_THERM HW trip points, setting "THERMTRIP"
- * trip points , using "critical" type trip_temp from thermal
- * zone.
- * After they have been configured, THERMTRIP will take action
- * when the configured SoC thermal sensor group reaches a
+ * "THROTTLE" trip points , using "critical" or "hot" type trip_temp
+ * from thermal zone.
+ * After they have been configured, THERMTRIP or THROTTLE will take
+ * action when the configured SoC thermal sensor group reaches a
  * certain temperature.
  *
  * Return: 0 upon success, or a negative error code on failure.
@@ -254,19 +569,24 @@ static int thermtrip_program(struct device *dev,
  * THERMTRIP has been enabled successfully when a message similar to
  * this one appears on the serial console:
  * "thermtrip: will shut down when sensor group XXX reaches YYYYYY mC"
+ * THROTTLE has been enabled successfully when a message similar to
+ * this one appears on the serial console:
+ * ""throttrip: will throttle when sensor group XXX reaches YYYYYY mC"
  */
 static int tegra_soctherm_set_hwtrips(struct device *dev,
                                      const struct tegra_tsensor_group *sg,
                                      struct thermal_zone_device *tz)
 {
-       int temperature;
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       struct soctherm_throt_cfg *stc;
+       int i, trip, temperature;
        int ret;
 
        ret = tz->ops->get_crit_temp(tz, &temperature);
        if (ret) {
                dev_warn(dev, "thermtrip: %s: missing critical temperature\n",
                         sg->name);
-               return ret;
+               goto set_throttle;
        }
 
        ret = thermtrip_program(dev, sg, temperature);
@@ -280,6 +600,43 @@ static int tegra_soctherm_set_hwtrips(struct device *dev,
                 "thermtrip: will shut down when %s reaches %d mC\n",
                 sg->name, temperature);
 
+set_throttle:
+       ret = get_hot_temp(tz, &trip, &temperature);
+       if (ret) {
+               dev_warn(dev, "throttrip: %s: missing hot temperature\n",
+                        sg->name);
+               return 0;
+       }
+
+       for (i = 0; i < THROTTLE_SIZE; i++) {
+               struct thermal_cooling_device *cdev;
+
+               if (!ts->throt_cfgs[i].init)
+                       continue;
+
+               cdev = ts->throt_cfgs[i].cdev;
+               if (get_thermal_instance(tz, cdev, trip))
+                       stc = find_throttle_cfg_by_name(ts, cdev->type);
+               else
+                       continue;
+
+               ret = throttrip_program(dev, sg, stc, temperature);
+               if (ret) {
+                       dev_err(dev, "throttrip: %s: error during enable\n",
+                               sg->name);
+                       return ret;
+               }
+
+               dev_info(dev,
+                        "throttrip: will throttle when %s reaches %d mC\n",
+                        sg->name, temperature);
+               break;
+       }
+
+       if (i == THROTTLE_SIZE)
+               dev_warn(dev, "throttrip: %s: missing throttle cdev\n",
+                        sg->name);
+
        return 0;
 }
 
@@ -291,7 +648,7 @@ static int regs_show(struct seq_file *s, void *data)
        const struct tegra_tsensor *tsensors = ts->soc->tsensors;
        const struct tegra_tsensor_group **ttgs = ts->soc->ttgs;
        u32 r, state;
-       int i;
+       int i, level;
 
        seq_puts(s, "-----TSENSE (convert HW)-----\n");
 
@@ -365,6 +722,81 @@ static int regs_show(struct seq_file *s, void *data)
        state = REG_GET_MASK(r, SENSOR_TEMP2_MEM_TEMP_MASK);
        seq_printf(s, " MEM(%d)\n", translate_temp(state));
 
+       for (i = 0; i < ts->soc->num_ttgs; i++) {
+               seq_printf(s, "%s:\n", ttgs[i]->name);
+               for (level = 0; level < 4; level++) {
+                       s32 v;
+                       u32 mask;
+                       u16 off = ttgs[i]->thermctl_lvl0_offset;
+
+                       r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+
+                       mask = ttgs[i]->thermctl_lvl0_up_thresh_mask;
+                       state = REG_GET_MASK(r, mask);
+                       v = sign_extend32(state, ts->soc->bptt - 1);
+                       v *= ts->soc->thresh_grain;
+                       seq_printf(s, "   %d: Up/Dn(%d /", level, v);
+
+                       mask = ttgs[i]->thermctl_lvl0_dn_thresh_mask;
+                       state = REG_GET_MASK(r, mask);
+                       v = sign_extend32(state, ts->soc->bptt - 1);
+                       v *= ts->soc->thresh_grain;
+                       seq_printf(s, "%d ) ", v);
+
+                       mask = THERMCTL_LVL0_CPU0_EN_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_printf(s, "En(%d) ", state);
+
+                       mask = THERMCTL_LVL0_CPU0_CPU_THROT_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_puts(s, "CPU Throt");
+                       if (!state)
+                               seq_printf(s, "(%s) ", "none");
+                       else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_LIGHT)
+                               seq_printf(s, "(%s) ", "L");
+                       else if (state == THERMCTL_LVL0_CPU0_CPU_THROT_HEAVY)
+                               seq_printf(s, "(%s) ", "H");
+                       else
+                               seq_printf(s, "(%s) ", "H+L");
+
+                       mask = THERMCTL_LVL0_CPU0_GPU_THROT_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_puts(s, "GPU Throt");
+                       if (!state)
+                               seq_printf(s, "(%s) ", "none");
+                       else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_LIGHT)
+                               seq_printf(s, "(%s) ", "L");
+                       else if (state == THERMCTL_LVL0_CPU0_GPU_THROT_HEAVY)
+                               seq_printf(s, "(%s) ", "H");
+                       else
+                               seq_printf(s, "(%s) ", "H+L");
+
+                       mask = THERMCTL_LVL0_CPU0_STATUS_MASK;
+                       state = REG_GET_MASK(r, mask);
+                       seq_printf(s, "Status(%s)\n",
+                                  state == 0 ? "LO" :
+                                  state == 1 ? "In" :
+                                  state == 2 ? "Res" : "HI");
+               }
+       }
+
+       r = readl(ts->regs + THERMCTL_STATS_CTL);
+       seq_printf(s, "STATS: Up(%s) Dn(%s)\n",
+                  r & STATS_CTL_EN_UP ? "En" : "--",
+                  r & STATS_CTL_EN_DN ? "En" : "--");
+
+       for (level = 0; level < 4; level++) {
+               u16 off;
+
+               off = THERMCTL_LVL0_UP_STATS;
+               r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+               seq_printf(s, "  Level_%d Up(%d) ", level, r);
+
+               off = THERMCTL_LVL0_DN_STATS;
+               r = readl(ts->regs + THERMCTL_LVL_REG(off, level));
+               seq_printf(s, "Dn(%d)\n", r);
+       }
+
        r = readl(ts->regs + THERMCTL_THERMTRIP_CTL);
        state = REG_GET_MASK(r, ttgs[0]->thermtrip_any_en_mask);
        seq_printf(s, "Thermtrip Any En(%d)\n", state);
@@ -376,6 +808,32 @@ static int regs_show(struct seq_file *s, void *data)
                seq_printf(s, "Thresh(%d)\n", state);
        }
 
+       r = readl(ts->regs + THROT_GLOBAL_CFG);
+       seq_puts(s, "\n");
+       seq_printf(s, "GLOBAL THROTTLE CONFIG: 0x%08x\n", r);
+
+       seq_puts(s, "---------------------------------------------------\n");
+       r = readl(ts->regs + THROT_STATUS);
+       state = REG_GET_MASK(r, THROT_STATUS_BREACH_MASK);
+       seq_printf(s, "THROT STATUS: breach(%d) ", state);
+       state = REG_GET_MASK(r, THROT_STATUS_STATE_MASK);
+       seq_printf(s, "state(%d) ", state);
+       state = REG_GET_MASK(r, THROT_STATUS_ENABLED_MASK);
+       seq_printf(s, "enabled(%d)\n", state);
+
+       r = readl(ts->regs + CPU_PSKIP_STATUS);
+       if (ts->soc->use_ccroc) {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: enabled(%d)\n", state);
+       } else {
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_M_MASK);
+               seq_printf(s, "CPU PSKIP STATUS: M(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_N_MASK);
+               seq_printf(s, "N(%d) ", state);
+               state = REG_GET_MASK(r, XPU_PSKIP_STATUS_ENABLED_MASK);
+               seq_printf(s, "enabled(%d)\n", state);
+       }
+
        return 0;
 }
 
@@ -449,6 +907,326 @@ static int soctherm_clk_enable(struct platform_device *pdev, bool enable)
        return 0;
 }
 
+static int throt_get_cdev_max_state(struct thermal_cooling_device *cdev,
+                                   unsigned long *max_state)
+{
+       *max_state = 1;
+       return 0;
+}
+
+static int throt_get_cdev_cur_state(struct thermal_cooling_device *cdev,
+                                   unsigned long *cur_state)
+{
+       struct tegra_soctherm *ts = cdev->devdata;
+       u32 r;
+
+       r = readl(ts->regs + THROT_STATUS);
+       if (REG_GET_MASK(r, THROT_STATUS_STATE_MASK))
+               *cur_state = 1;
+       else
+               *cur_state = 0;
+
+       return 0;
+}
+
+static int throt_set_cdev_state(struct thermal_cooling_device *cdev,
+                               unsigned long cur_state)
+{
+       return 0;
+}
+
+static struct thermal_cooling_device_ops throt_cooling_ops = {
+       .get_max_state = throt_get_cdev_max_state,
+       .get_cur_state = throt_get_cdev_cur_state,
+       .set_cur_state = throt_set_cdev_state,
+};
+
+/**
+ * soctherm_init_hw_throt_cdev() - Parse the HW throttle configurations
+ * and register them as cooling devices.
+ */
+static void soctherm_init_hw_throt_cdev(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       struct device_node *np_stc, *np_stcc;
+       const char *name;
+       u32 val;
+       int i, r;
+
+       for (i = 0; i < THROTTLE_SIZE; i++) {
+               ts->throt_cfgs[i].name = throt_names[i];
+               ts->throt_cfgs[i].id = i;
+               ts->throt_cfgs[i].init = false;
+       }
+
+       np_stc = of_get_child_by_name(dev->of_node, "throttle-cfgs");
+       if (!np_stc) {
+               dev_info(dev,
+                        "throttle-cfg: no throttle-cfgs - not enabling\n");
+               return;
+       }
+
+       for_each_child_of_node(np_stc, np_stcc) {
+               struct soctherm_throt_cfg *stc;
+               struct thermal_cooling_device *tcd;
+
+               name = np_stcc->name;
+               stc = find_throttle_cfg_by_name(ts, name);
+               if (!stc) {
+                       dev_err(dev,
+                               "throttle-cfg: could not find %s\n", name);
+                       continue;
+               }
+
+               r = of_property_read_u32(np_stcc, "nvidia,priority", &val);
+               if (r) {
+                       dev_info(dev,
+                                "throttle-cfg: %s: missing priority\n", name);
+                       continue;
+               }
+               stc->priority = val;
+
+               if (ts->soc->use_ccroc) {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-level",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-level\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_level = val;
+               } else {
+                       r = of_property_read_u32(np_stcc,
+                                                "nvidia,cpu-throt-percent",
+                                                &val);
+                       if (r) {
+                               dev_info(dev,
+                                        "throttle-cfg: %s: missing cpu-throt-percent\n",
+                                        name);
+                               continue;
+                       }
+                       stc->cpu_throt_depth = val;
+               }
+
+               tcd = thermal_of_cooling_device_register(np_stcc,
+                                                        (char *)name, ts,
+                                                        &throt_cooling_ops);
+               of_node_put(np_stcc);
+               if (IS_ERR_OR_NULL(tcd)) {
+                       dev_err(dev,
+                               "throttle-cfg: %s: failed to register cooling device\n",
+                               name);
+                       continue;
+               }
+
+               stc->cdev = tcd;
+               stc->init = true;
+       }
+
+       of_node_put(np_stc);
+}
+
+/**
+ * throttlectl_cpu_level_cfg() - programs CCROC NV_THERM level config
+ * @level: describing the level LOW/MED/HIGH of throttling
+ *
+ * It's necessary to set up the CPU-local CCROC NV_THERM instance with
+ * the M/N values desired for each level. This function does this.
+ *
+ * This function pre-programs the CCROC NV_THERM levels in terms of
+ * pre-configured "Low", "Medium" or "Heavy" throttle levels which are
+ * mapped to THROT_LEVEL_LOW, THROT_LEVEL_MED and THROT_LEVEL_HVY.
+ */
+static void throttlectl_cpu_level_cfg(struct tegra_soctherm *ts, int level)
+{
+       u8 depth, dividend;
+       u32 r;
+
+       switch (level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               depth = 50;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               depth = 75;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               depth = 80;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_NONE:
+               return;
+       default:
+               return;
+       }
+
+       dividend = THROT_DEPTH_DIVIDEND(depth);
+
+       /* setup PSKIP in ccroc nv_therm registers */
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_DURATION_MASK, 0xff);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_RAMP_STEP_MASK, 0xf);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_RAMP_CPU_REG(level));
+
+       r = ccroc_readl(ts, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_ENB_MASK, 1);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend);
+       r = REG_SET_MASK(r, CCROC_THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff);
+       ccroc_writel(ts, r, CCROC_THROT_PSKIP_CTRL_CPU_REG(level));
+}
+
+/**
+ * throttlectl_cpu_level_select() - program CPU pulse skipper config
+ * @throt: the LIGHT/HEAVY of throttle event id
+ *
+ * Pulse skippers are used to throttle clock frequencies.  This
+ * function programs the pulse skippers based on @throt and platform
+ * data.  This function is used on SoCs which have CPU-local pulse
+ * skipper control, such as T13x. It programs soctherm's interface to
+ * Denver:CCROC NV_THERM in terms of Low, Medium and HIGH throttling
+ * vectors. PSKIP_BYPASS mode is set as required per HW spec.
+ */
+static void throttlectl_cpu_level_select(struct tegra_soctherm *ts,
+                                        enum soctherm_throttle_id throt)
+{
+       u32 r, throt_vect;
+
+       /* Denver:CCROC NV_THERM interface N:3 Mapping */
+       switch (ts->throt_cfgs[throt].cpu_throt_level) {
+       case TEGRA_SOCTHERM_THROT_LEVEL_LOW:
+               throt_vect = THROT_VECT_LOW;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_MED:
+               throt_vect = THROT_VECT_MED;
+               break;
+       case TEGRA_SOCTHERM_THROT_LEVEL_HIGH:
+               throt_vect = THROT_VECT_HIGH;
+               break;
+       default:
+               throt_vect = THROT_VECT_NONE;
+               break;
+       }
+
+       r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT_CPU_MASK, throt_vect);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_VECT2_CPU_MASK, throt_vect);
+       writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+
+       /* bypass sequencer in soc_therm as it is programmed in ccroc */
+       r = REG_SET_MASK(0, THROT_PSKIP_RAMP_SEQ_BYPASS_MODE_MASK, 1);
+       writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+}
+
+/**
+ * throttlectl_cpu_mn() - program CPU pulse skipper configuration
+ * @throt: the LIGHT/HEAVY of throttle event id
+ *
+ * Pulse skippers are used to throttle clock frequencies.  This
+ * function programs the pulse skippers based on @throt and platform
+ * data.  This function is used for CPUs that have "remote" pulse
+ * skipper control, e.g., the CPU pulse skipper is controlled by the
+ * SOC_THERM IP block.  (SOC_THERM is located outside the CPU
+ * complex.)
+ */
+static void throttlectl_cpu_mn(struct tegra_soctherm *ts,
+                              enum soctherm_throttle_id throt)
+{
+       u32 r;
+       int depth;
+       u8 dividend;
+
+       depth = ts->throt_cfgs[throt].cpu_throt_depth;
+       dividend = THROT_DEPTH_DIVIDEND(depth);
+
+       r = readl(ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_ENABLE_MASK, 1);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVIDEND_MASK, dividend);
+       r = REG_SET_MASK(r, THROT_PSKIP_CTRL_DIVISOR_MASK, 0xff);
+       writel(r, ts->regs + THROT_PSKIP_CTRL(throt, THROTTLE_DEV_CPU));
+
+       r = readl(ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+       r = REG_SET_MASK(r, THROT_PSKIP_RAMP_DURATION_MASK, 0xff);
+       r = REG_SET_MASK(r, THROT_PSKIP_RAMP_STEP_MASK, 0xf);
+       writel(r, ts->regs + THROT_PSKIP_RAMP(throt, THROTTLE_DEV_CPU));
+}
+
+/**
+ * soctherm_throttle_program() - programs pulse skippers' configuration
+ * @throt: the LIGHT/HEAVY of the throttle event id.
+ *
+ * Pulse skippers are used to throttle clock frequencies.
+ * This function programs the pulse skippers.
+ */
+static void soctherm_throttle_program(struct tegra_soctherm *ts,
+                                     enum soctherm_throttle_id throt)
+{
+       u32 r;
+       struct soctherm_throt_cfg stc = ts->throt_cfgs[throt];
+
+       if (!stc.init)
+               return;
+
+       /* Setup PSKIP parameters */
+       if (ts->soc->use_ccroc)
+               throttlectl_cpu_level_select(ts, throt);
+       else
+               throttlectl_cpu_mn(ts, throt);
+
+       r = REG_SET_MASK(0, THROT_PRIORITY_LITE_PRIO_MASK, stc.priority);
+       writel(r, ts->regs + THROT_PRIORITY_CTRL(throt));
+
+       r = REG_SET_MASK(0, THROT_DELAY_LITE_DELAY_MASK, 0);
+       writel(r, ts->regs + THROT_DELAY_CTRL(throt));
+
+       r = readl(ts->regs + THROT_PRIORITY_LOCK);
+       r = REG_GET_MASK(r, THROT_PRIORITY_LOCK_PRIORITY_MASK);
+       if (r >= stc.priority)
+               return;
+       r = REG_SET_MASK(0, THROT_PRIORITY_LOCK_PRIORITY_MASK,
+                        stc.priority);
+       writel(r, ts->regs + THROT_PRIORITY_LOCK);
+}
+
+static void tegra_soctherm_throttle(struct device *dev)
+{
+       struct tegra_soctherm *ts = dev_get_drvdata(dev);
+       u32 v;
+       int i;
+
+       /* configure LOW, MED and HIGH levels for CCROC NV_THERM */
+       if (ts->soc->use_ccroc) {
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_LOW);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_MED);
+               throttlectl_cpu_level_cfg(ts, TEGRA_SOCTHERM_THROT_LEVEL_HIGH);
+       }
+
+       /* Thermal HW throttle programming */
+       for (i = 0; i < THROTTLE_SIZE; i++)
+               soctherm_throttle_program(ts, i);
+
+       v = REG_SET_MASK(0, THROT_GLOBAL_ENB_MASK, 1);
+       if (ts->soc->use_ccroc) {
+               ccroc_writel(ts, v, CCROC_GLOBAL_CFG);
+
+               v = ccroc_readl(ts, CCROC_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               ccroc_writel(ts, v, CCROC_SUPER_CCLKG_DIVIDER);
+       } else {
+               writel(v, ts->regs + THROT_GLOBAL_CFG);
+
+               v = clk_readl(ts, CAR_SUPER_CCLKG_DIVIDER);
+               v = REG_SET_MASK(v, CDIVG_USE_THERM_CONTROLS_MASK, 1);
+               clk_writel(ts, v, CAR_SUPER_CCLKG_DIVIDER);
+       }
+
+       /* initialize stats collection */
+       v = STATS_CTL_CLR_DN | STATS_CTL_EN_DN |
+           STATS_CTL_CLR_UP | STATS_CTL_EN_UP;
+       writel(v, ts->regs + THERMCTL_STATS_CTL);
+}
+
 static void soctherm_init(struct platform_device *pdev)
 {
        struct tegra_soctherm *tegra = platform_get_drvdata(pdev);
@@ -475,6 +1253,9 @@ static void soctherm_init(struct platform_device *pdev)
        }
        writel(pdiv, tegra->regs + SENSOR_PDIV);
        writel(hotspot, tegra->regs + SENSOR_HOTSPOT_OFF);
+
+       /* Configure hw throttle */
+       tegra_soctherm_throttle(&pdev->dev);
 }
 
 static const struct of_device_id tegra_soctherm_of_match[] = {
@@ -527,10 +1308,31 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
 
        tegra->soc = soc;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "soctherm-reg");
        tegra->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(tegra->regs))
+       if (IS_ERR(tegra->regs)) {
+               dev_err(&pdev->dev, "can't get soctherm registers");
                return PTR_ERR(tegra->regs);
+       }
+
+       if (!tegra->soc->use_ccroc) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "car-reg");
+               tegra->clk_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(tegra->clk_regs)) {
+                       dev_err(&pdev->dev, "can't get car clk registers");
+                       return PTR_ERR(tegra->clk_regs);
+               }
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "ccroc-reg");
+               tegra->ccroc_regs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(tegra->ccroc_regs)) {
+                       dev_err(&pdev->dev, "can't get ccroc registers");
+                       return PTR_ERR(tegra->ccroc_regs);
+               }
+       }
 
        tegra->reset = devm_reset_control_get(&pdev->dev, "soctherm");
        if (IS_ERR(tegra->reset)) {
@@ -580,6 +1382,8 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
        if (err)
                return err;
 
+       soctherm_init_hw_throt_cdev(pdev);
+
        soctherm_init(pdev);
 
        for (i = 0; i < soc->num_ttgs; ++i) {
@@ -593,6 +1397,7 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
                zone->reg = tegra->regs + soc->ttgs[i]->sensor_temp_offset;
                zone->dev = &pdev->dev;
                zone->sg = soc->ttgs[i];
+               zone->ts = tegra;
 
                z = devm_thermal_zone_of_sensor_register(&pdev->dev,
                                                         soc->ttgs[i]->id, zone,
@@ -608,7 +1413,9 @@ static int tegra_soctherm_probe(struct platform_device *pdev)
                tegra->thermctl_tzs[soc->ttgs[i]->id] = z;
 
                /* Configure hw trip points */
-               tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
+               err = tegra_soctherm_set_hwtrips(&pdev->dev, soc->ttgs[i], z);
+               if (err)
+                       goto disable_clocks;
        }
 
        soctherm_debug_init(pdev);
@@ -661,7 +1468,12 @@ static int __maybe_unused soctherm_resume(struct device *dev)
                struct thermal_zone_device *tz;
 
                tz = tegra->thermctl_tzs[soc->ttgs[i]->id];
-               tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
+               err = tegra_soctherm_set_hwtrips(dev, soc->ttgs[i], tz);
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "Resume failed: set hwtrips failed\n");
+                       return err;
+               }
        }
 
        return 0;
index 28e18ec..e96ca73 100644 (file)
 #ifndef __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
 #define __DRIVERS_THERMAL_TEGRA_SOCTHERM_H
 
+#define THERMCTL_LEVEL0_GROUP_CPU               0x0
+#define THERMCTL_LEVEL0_GROUP_GPU              0x4
+#define THERMCTL_LEVEL0_GROUP_MEM              0x8
+#define THERMCTL_LEVEL0_GROUP_TSENSE           0xc
+
 #define SENSOR_CONFIG2                          8
 #define SENSOR_CONFIG2_THERMA_MASK             (0xffff << 16)
 #define SENSOR_CONFIG2_THERMA_SHIFT            16
@@ -65,6 +70,9 @@ struct tegra_tsensor_group {
        u32 thermtrip_enable_mask;
        u32 thermtrip_any_en_mask;
        u32 thermtrip_threshold_mask;
+       u16 thermctl_lvl0_offset;
+       u32 thermctl_lvl0_up_thresh_mask;
+       u32 thermctl_lvl0_dn_thresh_mask;
 };
 
 struct tegra_tsensor_configuration {
@@ -103,6 +111,8 @@ struct tegra_soctherm_soc {
        const unsigned int num_ttgs;
        const struct tegra_soctherm_fuse *tfuse;
        const int thresh_grain;
+       const unsigned int bptt;
+       const bool use_ccroc;
 };
 
 int tegra_calc_shared_calib(const struct tegra_soctherm_fuse *tfuse,
index beb9d36..3676863 100644 (file)
 #define TEGRA124_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
 #define TEGRA124_THERMTRIP_TSENSE_THRESH_MASK  0xff
 
+#define TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK  (0xff << 17)
+#define TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK  (0xff << 9)
+
 #define TEGRA124_THRESH_GRAIN                  1000
+#define TEGRA124_BPTT                          8
 
 static const struct tegra_tsensor_configuration tegra124_tsensor_config = {
        .tall = 16300,
@@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
@@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
@@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
@@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra124_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA124_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA124_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA124_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA124_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA124_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra124_tsensor_groups[] = {
@@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra124_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra124_tsensor_groups),
        .tfuse = &tegra124_soctherm_fuse,
        .thresh_grain = TEGRA124_THRESH_GRAIN,
+       .bptt = TEGRA124_BPTT,
+       .use_ccroc = false,
 };
index e2aa84e..97fa305 100644 (file)
 #define TEGRA132_THERMTRIP_CPU_THRESH_MASK     (0xff << 8)
 #define TEGRA132_THERMTRIP_TSENSE_THRESH_MASK  0xff
 
+#define TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK  (0xff << 17)
+#define TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK  (0xff << 9)
+
 #define TEGRA132_THRESH_GRAIN                  1000
+#define TEGRA132_BPTT                          8
 
 static const struct tegra_tsensor_configuration tegra132_tsensor_config = {
        .tall = 16300,
@@ -51,6 +55,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
@@ -66,6 +73,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
@@ -79,6 +89,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
@@ -94,6 +107,9 @@ static const struct tegra_tsensor_group tegra132_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA132_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA132_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA132_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA132_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA132_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra132_tsensor_groups[] = {
@@ -193,4 +209,6 @@ const struct tegra_soctherm_soc tegra132_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra132_tsensor_groups),
        .tfuse = &tegra132_soctherm_fuse,
        .thresh_grain = TEGRA132_THRESH_GRAIN,
+       .bptt = TEGRA132_BPTT,
+       .use_ccroc = true,
 };
index 19cc0ab..ad53169 100644 (file)
 #define TEGRA210_THERMTRIP_CPU_THRESH_MASK     (0x1ff << 9)
 #define TEGRA210_THERMTRIP_TSENSE_THRESH_MASK  0x1ff
 
+#define TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK  (0x1ff << 18)
+#define TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK  (0x1ff << 9)
+
 #define TEGRA210_THRESH_GRAIN                  500
+#define TEGRA210_BPTT                          9
 
 static const struct tegra_tsensor_configuration tegra210_tsensor_config = {
        .tall = 16300,
@@ -52,6 +56,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_cpu = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_CPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_CPU_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_CPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
@@ -67,6 +74,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_gpu = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_GPU_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_GPU,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
@@ -80,6 +90,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_pll = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_TSENSE_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_TSENSE_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_TSENSE,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
@@ -95,6 +108,9 @@ static const struct tegra_tsensor_group tegra210_tsensor_group_mem = {
        .thermtrip_any_en_mask = TEGRA210_THERMTRIP_ANY_EN_MASK,
        .thermtrip_enable_mask = TEGRA210_THERMTRIP_MEM_EN_MASK,
        .thermtrip_threshold_mask = TEGRA210_THERMTRIP_GPUMEM_THRESH_MASK,
+       .thermctl_lvl0_offset = THERMCTL_LEVEL0_GROUP_MEM,
+       .thermctl_lvl0_up_thresh_mask = TEGRA210_THERMCTL_LVL0_UP_THRESH_MASK,
+       .thermctl_lvl0_dn_thresh_mask = TEGRA210_THERMCTL_LVL0_DN_THRESH_MASK,
 };
 
 static const struct tegra_tsensor_group *tegra210_tsensor_groups[] = {
@@ -194,4 +210,6 @@ const struct tegra_soctherm_soc tegra210_soctherm = {
        .num_ttgs = ARRAY_SIZE(tegra210_tsensor_groups),
        .tfuse = &tegra210_soctherm_fuse,
        .thresh_grain = TEGRA210_THRESH_GRAIN,
+       .bptt = TEGRA210_BPTT,
+       .use_ccroc = false,
 };
index e2fc616..226b0b4 100644 (file)
@@ -520,6 +520,56 @@ exit:
 }
 EXPORT_SYMBOL_GPL(thermal_zone_get_temp);
 
+void thermal_zone_set_trips(struct thermal_zone_device *tz)
+{
+       int low = -INT_MAX;
+       int high = INT_MAX;
+       int trip_temp, hysteresis;
+       int i, ret;
+
+       mutex_lock(&tz->lock);
+
+       if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
+               goto exit;
+
+       for (i = 0; i < tz->trips; i++) {
+               int trip_low;
+
+               tz->ops->get_trip_temp(tz, i, &trip_temp);
+               tz->ops->get_trip_hyst(tz, i, &hysteresis);
+
+               trip_low = trip_temp - hysteresis;
+
+               if (trip_low < tz->temperature && trip_low > low)
+                       low = trip_low;
+
+               if (trip_temp > tz->temperature && trip_temp < high)
+                       high = trip_temp;
+       }
+
+       /* No need to change trip points */
+       if (tz->prev_low_trip == low && tz->prev_high_trip == high)
+               goto exit;
+
+       tz->prev_low_trip = low;
+       tz->prev_high_trip = high;
+
+       dev_dbg(&tz->device,
+               "new temperature boundaries: %d < x < %d\n", low, high);
+
+       /*
+        * Set a temperature window. When this window is left the driver
+        * must inform the thermal core via thermal_zone_device_update.
+        */
+       ret = tz->ops->set_trips(tz, low, high);
+       if (ret)
+               dev_err(&tz->device, "Failed to set trips: %d\n", ret);
+
+exit:
+       mutex_unlock(&tz->lock);
+}
+EXPORT_SYMBOL_GPL(thermal_zone_set_trips);
+
 static void update_temperature(struct thermal_zone_device *tz)
 {
        int temp, ret;
@@ -557,7 +607,8 @@ static void thermal_zone_device_reset(struct thermal_zone_device *tz)
                pos->initialized = false;
 }
 
-void thermal_zone_device_update(struct thermal_zone_device *tz)
+void thermal_zone_device_update(struct thermal_zone_device *tz,
+                               enum thermal_notify_event event)
 {
        int count;
 
@@ -569,6 +620,10 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
 
        update_temperature(tz);
 
+       thermal_zone_set_trips(tz);
+
+       tz->notify_event = event;
+
        for (count = 0; count < tz->trips; count++)
                handle_thermal_trip(tz, count);
 }
@@ -579,7 +634,7 @@ static void thermal_zone_device_check(struct work_struct *work)
        struct thermal_zone_device *tz = container_of(work, struct
                                                      thermal_zone_device,
                                                      poll_queue.work);
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 }
 
 /* sys I/F for thermal zone */
@@ -703,7 +758,7 @@ trip_point_temp_store(struct device *dev, struct device_attribute *attr,
        if (ret)
                return ret;
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return count;
 }
@@ -754,6 +809,9 @@ trip_point_hyst_store(struct device *dev, struct device_attribute *attr,
         */
        ret = tz->ops->set_trip_hyst(tz, trip, temperature);
 
+       if (!ret)
+               thermal_zone_set_trips(tz);
+
        return ret ? ret : count;
 }
 
@@ -822,7 +880,7 @@ passive_store(struct device *dev, struct device_attribute *attr,
 
        tz->forced_passive = state;
 
-       thermal_zone_device_update(tz);
+       thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return count;
 }
@@ -913,7 +971,7 @@ emul_temp_store(struct device *dev, struct device_attribute *attr,
        }
 
        if (!ret)
-               thermal_zone_device_update(tz);
+               thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return ret ? ret : count;
 }
@@ -1509,7 +1567,8 @@ __thermal_cooling_device_register(struct device_node *np,
        mutex_lock(&thermal_list_lock);
        list_for_each_entry(pos, &thermal_tz_list, node)
                if (atomic_cmpxchg(&pos->need_update, 1, 0))
-                       thermal_zone_device_update(pos);
+                       thermal_zone_device_update(pos,
+                                                  THERMAL_EVENT_UNSPECIFIED);
        mutex_unlock(&thermal_list_lock);
 
        return cdev;
@@ -1952,7 +2011,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type,
        thermal_zone_device_reset(tz);
        /* Update the new thermal zone and mark it as already updated. */
        if (atomic_cmpxchg(&tz->need_update, 1, 0))
-               thermal_zone_device_update(tz);
+               thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
 
        return tz;
 
@@ -2069,6 +2128,36 @@ exit:
 }
 EXPORT_SYMBOL_GPL(thermal_zone_get_zone_by_name);
 
+/**
+ * thermal_zone_get_slope - return the slope attribute of the thermal zone
+ * @tz: thermal zone device with the slope attribute
+ *
+ * Return: If the thermal zone device has a slope attribute, return it, else
+ * return 1.
+ */
+int thermal_zone_get_slope(struct thermal_zone_device *tz)
+{
+       if (tz && tz->tzp)
+               return tz->tzp->slope;
+       return 1;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_get_slope);
+
+/**
+ * thermal_zone_get_offset - return the offset attribute of the thermal zone
+ * @tz: thermal zone device with the offset attribute
+ *
+ * Return: If the thermal zone device has a offset attribute, return it, else
+ * return 0.
+ */
+int thermal_zone_get_offset(struct thermal_zone_device *tz)
+{
+       if (tz && tz->tzp)
+               return tz->tzp->offset;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(thermal_zone_get_offset);
+
 #ifdef CONFIG_NET
 static const struct genl_multicast_group thermal_event_mcgrps[] = {
        { .name = THERMAL_GENL_MCAST_GROUP_NAME, },
@@ -2209,7 +2298,8 @@ static int thermal_pm_notify(struct notifier_block *nb,
                atomic_set(&in_suspend, 0);
                list_for_each_entry(tz, &thermal_tz_list, node) {
                        thermal_zone_device_reset(tz);
-                       thermal_zone_device_update(tz);
+                       thermal_zone_device_update(tz,
+                                                  THERMAL_EVENT_UNSPECIFIED);
                }
                break;
        default:
index 15c0a9a..0586bd0 100644 (file)
@@ -52,7 +52,7 @@ static void ti_thermal_work(struct work_struct *work)
        struct ti_thermal_data *data = container_of(work,
                                        struct ti_thermal_data, thermal_wq);
 
-       thermal_zone_device_update(data->ti_thermal);
+       thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
 
        dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n",
                data->ti_thermal->type);
@@ -205,7 +205,7 @@ static int ti_thermal_set_mode(struct thermal_zone_device *thermal,
        data->mode = mode;
        ti_bandgap_write_update_interval(bgp, data->sensor_id,
                                        data->ti_thermal->polling_delay);
-       thermal_zone_device_update(data->ti_thermal);
+       thermal_zone_device_update(data->ti_thermal, THERMAL_EVENT_UNSPECIFIED);
        dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n",
                data->ti_thermal->polling_delay);
 
@@ -239,7 +239,7 @@ static int ti_thermal_get_trip_temp(struct thermal_zone_device *thermal,
        return 0;
 }
 
-static int __ti_thermal_get_trend(void *p, long *trend)
+static int __ti_thermal_get_trend(void *p, int trip, enum thermal_trend *trend)
 {
        struct ti_thermal_data *data = p;
        struct ti_bandgap *bgp;
@@ -252,22 +252,6 @@ static int __ti_thermal_get_trend(void *p, long *trend)
        if (ret)
                return ret;
 
-       *trend = tr;
-
-       return 0;
-}
-
-/* Get the temperature trend callback functions for thermal zone */
-static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
-                               int trip, enum thermal_trend *trend)
-{
-       int ret;
-       long tr;
-
-       ret = __ti_thermal_get_trend(thermal->devdata, &tr);
-       if (ret)
-               return ret;
-
        if (tr > 0)
                *trend = THERMAL_TREND_RAISING;
        else if (tr < 0)
@@ -278,6 +262,13 @@ static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
        return 0;
 }
 
+/* Get the temperature trend callback functions for thermal zone */
+static int ti_thermal_get_trend(struct thermal_zone_device *thermal,
+                               int trip, enum thermal_trend *trend)
+{
+       return __ti_thermal_get_trend(thermal->devdata, trip, trend);
+}
+
 /* Get critical temperature callback functions for thermal zone */
 static int ti_thermal_get_crit_temp(struct thermal_zone_device *thermal,
                                    int *temp)
index 10adcdd..c908150 100644 (file)
  */
 
 #include <linux/thermal.h>
-
+#include <linux/slab.h>
 #include "thermal_core.h"
 
 /**
  * notify_user_space - Notifies user space about thermal events
  * @tz - thermal_zone_device
+ * @trip - Trip point index
  *
  * This function notifies the user space through UEvents.
  */
 static int notify_user_space(struct thermal_zone_device *tz, int trip)
 {
+       char *thermal_prop[5];
+       int i;
+
        mutex_lock(&tz->lock);
-       kobject_uevent(&tz->device.kobj, KOBJ_CHANGE);
+       thermal_prop[0] = kasprintf(GFP_KERNEL, "NAME=%s", tz->type);
+       thermal_prop[1] = kasprintf(GFP_KERNEL, "TEMP=%d", tz->temperature);
+       thermal_prop[2] = kasprintf(GFP_KERNEL, "TRIP=%d", trip);
+       thermal_prop[3] = kasprintf(GFP_KERNEL, "EVENT=%d", tz->notify_event);
+       thermal_prop[4] = NULL;
+       kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, thermal_prop);
+       for (i = 0; i < 4; ++i)
+               kfree(thermal_prop[i]);
        mutex_unlock(&tz->lock);
        return 0;
 }
index 97f0a2b..95f4c1b 100644 (file)
@@ -348,7 +348,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work)
        }
        if (notify) {
                pr_debug("thermal_zone_device_update\n");
-               thermal_zone_device_update(phdev->tzone);
+               thermal_zone_device_update(phdev->tzone,
+                                          THERMAL_EVENT_UNSPECIFIED);
        }
 }
 
index a9d94f7..2675792 100644 (file)
@@ -708,7 +708,7 @@ static irqreturn_t sc16is7xx_irq(int irq, void *dev_id)
 {
        struct sc16is7xx_port *s = (struct sc16is7xx_port *)dev_id;
 
-       queue_kthread_work(&s->kworker, &s->irq_work);
+       kthread_queue_work(&s->kworker, &s->irq_work);
 
        return IRQ_HANDLED;
 }
@@ -784,7 +784,7 @@ static void sc16is7xx_ier_clear(struct uart_port *port, u8 bit)
 
        one->config.flags |= SC16IS7XX_RECONF_IER;
        one->config.ier_clear |= bit;
-       queue_kthread_work(&s->kworker, &one->reg_work);
+       kthread_queue_work(&s->kworker, &one->reg_work);
 }
 
 static void sc16is7xx_stop_tx(struct uart_port *port)
@@ -802,7 +802,7 @@ static void sc16is7xx_start_tx(struct uart_port *port)
        struct sc16is7xx_port *s = dev_get_drvdata(port->dev);
        struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
 
-       queue_kthread_work(&s->kworker, &one->tx_work);
+       kthread_queue_work(&s->kworker, &one->tx_work);
 }
 
 static unsigned int sc16is7xx_tx_empty(struct uart_port *port)
@@ -828,7 +828,7 @@ static void sc16is7xx_set_mctrl(struct uart_port *port, unsigned int mctrl)
        struct sc16is7xx_one *one = to_sc16is7xx_one(port, port);
 
        one->config.flags |= SC16IS7XX_RECONF_MD;
-       queue_kthread_work(&s->kworker, &one->reg_work);
+       kthread_queue_work(&s->kworker, &one->reg_work);
 }
 
 static void sc16is7xx_break_ctl(struct uart_port *port, int break_state)
@@ -957,7 +957,7 @@ static int sc16is7xx_config_rs485(struct uart_port *port,
 
        port->rs485 = *rs485;
        one->config.flags |= SC16IS7XX_RECONF_RS485;
-       queue_kthread_work(&s->kworker, &one->reg_work);
+       kthread_queue_work(&s->kworker, &one->reg_work);
 
        return 0;
 }
@@ -1030,7 +1030,7 @@ static void sc16is7xx_shutdown(struct uart_port *port)
 
        sc16is7xx_power(port, 0);
 
-       flush_kthread_worker(&s->kworker);
+       kthread_flush_worker(&s->kworker);
 }
 
 static const char *sc16is7xx_type(struct uart_port *port)
@@ -1176,8 +1176,8 @@ static int sc16is7xx_probe(struct device *dev,
        s->devtype = devtype;
        dev_set_drvdata(dev, s);
 
-       init_kthread_worker(&s->kworker);
-       init_kthread_work(&s->irq_work, sc16is7xx_ist);
+       kthread_init_worker(&s->kworker);
+       kthread_init_work(&s->irq_work, sc16is7xx_ist);
        s->kworker_task = kthread_run(kthread_worker_fn, &s->kworker,
                                      "sc16is7xx");
        if (IS_ERR(s->kworker_task)) {
@@ -1234,8 +1234,8 @@ static int sc16is7xx_probe(struct device *dev,
                                     SC16IS7XX_EFCR_RXDISABLE_BIT |
                                     SC16IS7XX_EFCR_TXDISABLE_BIT);
                /* Initialize kthread work structs */
-               init_kthread_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
-               init_kthread_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
+               kthread_init_work(&s->p[i].tx_work, sc16is7xx_tx_proc);
+               kthread_init_work(&s->p[i].reg_work, sc16is7xx_reg_proc);
                /* Register port */
                uart_add_one_port(&sc16is7xx_uart, &s->p[i].port);
 
@@ -1301,7 +1301,7 @@ static int sc16is7xx_remove(struct device *dev)
                sc16is7xx_power(&s->p[i].port, 0);
        }
 
-       flush_kthread_worker(&s->kworker);
+       kthread_flush_worker(&s->kworker);
        kthread_stop(s->kworker_task);
 
        if (!IS_ERR(s->clk))
index 12731e6..ea73afb 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/usb/ehci_def.h>
 #include <linux/delay.h>
 #include <linux/serial_core.h>
-#include <linux/kconfig.h>
 #include <linux/kgdb.h>
 #include <linux/kthread.h>
 #include <asm/io.h>
index f5fccb3..f785032 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/ioport.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/module.h>
index 35af362..d793f54 100644 (file)
@@ -9,7 +9,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
index 88b008f..af2f117 100644 (file)
@@ -284,12 +284,14 @@ config FB_PM2_FIFO_DISCONNECT
 config FB_ARMCLCD
        tristate "ARM PrimeCell PL110 support"
        depends on ARM || ARM64 || COMPILE_TEST
-       depends on FB && ARM_AMBA
+       depends on FB && ARM_AMBA && HAS_IOMEM
        select FB_CFB_FILLRECT
        select FB_CFB_COPYAREA
        select FB_CFB_IMAGEBLIT
        select FB_MODE_HELPERS if OF
        select VIDEOMODE_HELPERS if OF
+       select BACKLIGHT_LCD_SUPPORT if OF
+       select BACKLIGHT_CLASS_DEVICE if OF
        help
          This framebuffer device driver is for the ARM PrimeCell PL110
          Colour LCD controller.  ARM PrimeCells provide the building
@@ -305,6 +307,8 @@ config PLAT_VERSATILE_CLCD
        def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_INTEGRATOR
        depends on ARM
        depends on FB_ARMCLCD && FB=y
+       select REGMAP
+       select MFD_SYSCON
 
 config FB_ACORN
        bool "Acorn VIDC support"
@@ -2443,7 +2447,6 @@ config FB_SIMPLE
 
 source "drivers/video/fbdev/omap/Kconfig"
 source "drivers/video/fbdev/omap2/Kconfig"
-source "drivers/video/fbdev/exynos/Kconfig"
 source "drivers/video/fbdev/mmp/Kconfig"
 
 config FB_SH_MOBILE_MERAM
index f673186..ee8c814 100644 (file)
@@ -6,8 +6,6 @@
 
 obj-y                          += core/
 
-obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
-
 obj-$(CONFIG_FB_MACMODES)      += macmodes.o
 obj-$(CONFIG_FB_WMT_GE_ROPS)   += wmt_ge_rops.o
 
@@ -79,6 +77,7 @@ obj-$(CONFIG_FB_ATMEL)                  += atmel_lcdfb.o
 obj-$(CONFIG_FB_PVR2)             += pvr2fb.o
 obj-$(CONFIG_FB_VOODOO1)          += sstfb.o
 obj-$(CONFIG_FB_ARMCLCD)         += amba-clcd.o
+obj-$(CONFIG_ARCH_NOMADIK)       += amba-clcd-nomadik.o
 obj-$(CONFIG_PLAT_VERSATILE_CLCD) += amba-clcd-versatile.o
 obj-$(CONFIG_FB_GOLDFISH)         += goldfishfb.o
 obj-$(CONFIG_FB_68328)            += 68328fb.o
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.c b/drivers/video/fbdev/amba-clcd-nomadik.c
new file mode 100644 (file)
index 0000000..0c06fca
--- /dev/null
@@ -0,0 +1,259 @@
+#include <linux/amba/bus.h>
+#include <linux/amba/clcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "amba-clcd-nomadik.h"
+
+static struct gpio_desc *grestb;
+static struct gpio_desc *scen;
+static struct gpio_desc *scl;
+static struct gpio_desc *sda;
+
+static u8 tpg110_readwrite_reg(bool write, u8 address, u8 outval)
+{
+       int i;
+       u8 inval = 0;
+
+       /* Assert SCEN */
+       gpiod_set_value_cansleep(scen, 1);
+       ndelay(150);
+       /* Hammer out the address */
+       for (i = 5; i >= 0; i--) {
+               if (address & BIT(i))
+                       gpiod_set_value_cansleep(sda, 1);
+               else
+                       gpiod_set_value_cansleep(sda, 0);
+               ndelay(150);
+               /* Send an SCL pulse */
+               gpiod_set_value_cansleep(scl, 1);
+               ndelay(160);
+               gpiod_set_value_cansleep(scl, 0);
+               ndelay(160);
+       }
+
+       if (write) {
+               /* WRITE */
+               gpiod_set_value_cansleep(sda, 0);
+       } else {
+               /* READ */
+               gpiod_set_value_cansleep(sda, 1);
+       }
+       ndelay(150);
+       /* Send an SCL pulse */
+       gpiod_set_value_cansleep(scl, 1);
+       ndelay(160);
+       gpiod_set_value_cansleep(scl, 0);
+       ndelay(160);
+
+       if (!write)
+               /* HiZ turn-around cycle */
+               gpiod_direction_input(sda);
+       ndelay(150);
+       /* Send an SCL pulse */
+       gpiod_set_value_cansleep(scl, 1);
+       ndelay(160);
+       gpiod_set_value_cansleep(scl, 0);
+       ndelay(160);
+
+       /* Hammer in/out the data */
+       for (i = 7; i >= 0; i--) {
+               int value;
+
+               if (write) {
+                       value = !!(outval & BIT(i));
+                       gpiod_set_value_cansleep(sda, value);
+               } else {
+                       value = gpiod_get_value(sda);
+                       if (value)
+                               inval |= BIT(i);
+               }
+               ndelay(150);
+               /* Send an SCL pulse */
+               gpiod_set_value_cansleep(scl, 1);
+               ndelay(160);
+               gpiod_set_value_cansleep(scl, 0);
+               ndelay(160);
+       }
+
+       gpiod_direction_output(sda, 0);
+       /* Deassert SCEN */
+       gpiod_set_value_cansleep(scen, 0);
+       /* Satisfies SCEN pulse width */
+       udelay(1);
+
+       return inval;
+}
+
+static u8 tpg110_read_reg(u8 address)
+{
+       return tpg110_readwrite_reg(false, address, 0);
+}
+
+static void tpg110_write_reg(u8 address, u8 outval)
+{
+       tpg110_readwrite_reg(true, address, outval);
+}
+
+static void tpg110_startup(struct device *dev)
+{
+       u8 val;
+
+       dev_info(dev, "TPG110 display enable\n");
+       /* De-assert the reset signal */
+       gpiod_set_value_cansleep(grestb, 0);
+       mdelay(1);
+       dev_info(dev, "de-asserted GRESTB\n");
+
+       /* Test display communication */
+       tpg110_write_reg(0x00, 0x55);
+       val = tpg110_read_reg(0x00);
+       if (val == 0x55)
+               dev_info(dev, "passed communication test\n");
+       val = tpg110_read_reg(0x01);
+       dev_info(dev, "TPG110 chip ID: %d version: %d\n",
+               val>>4, val&0x0f);
+
+       /* Show display resolution */
+       val = tpg110_read_reg(0x02);
+       val &= 7;
+       switch (val) {
+       case 0x0:
+               dev_info(dev, "IN 400x240 RGB -> OUT 800x480 RGB (dual scan)");
+               break;
+       case 0x1:
+               dev_info(dev, "IN 480x272 RGB -> OUT 800x480 RGB (dual scan)");
+               break;
+       case 0x4:
+               dev_info(dev, "480x640 RGB");
+               break;
+       case 0x5:
+               dev_info(dev, "480x272 RGB");
+               break;
+       case 0x6:
+               dev_info(dev, "640x480 RGB");
+               break;
+       case 0x7:
+               dev_info(dev, "800x480 RGB");
+               break;
+       default:
+               dev_info(dev, "ILLEGAL RESOLUTION");
+               break;
+       }
+
+       val = tpg110_read_reg(0x03);
+       dev_info(dev, "resolution is controlled by %s\n",
+               (val & BIT(7)) ? "software" : "hardware");
+}
+
+static void tpg110_enable(struct clcd_fb *fb)
+{
+       struct device *dev = &fb->dev->dev;
+       static bool startup;
+       u8 val;
+
+       if (!startup) {
+               tpg110_startup(dev);
+               startup = true;
+       }
+
+       /* Take chip out of standby */
+       val = tpg110_read_reg(0x03);
+       val |= BIT(0);
+       tpg110_write_reg(0x03, val);
+}
+
+static void tpg110_disable(struct clcd_fb *fb)
+{
+       u8 val;
+
+       dev_info(&fb->dev->dev, "TPG110 display disable\n");
+       val = tpg110_read_reg(0x03);
+       /* Put into standby */
+       val &= ~BIT(0);
+       tpg110_write_reg(0x03, val);
+}
+
+static void tpg110_init(struct device *dev, struct device_node *np,
+                       struct clcd_board *board)
+{
+       dev_info(dev, "TPG110 display init\n");
+
+       grestb = devm_get_gpiod_from_child(dev, "grestb", &np->fwnode);
+       if (IS_ERR(grestb)) {
+               dev_err(dev, "no GRESTB GPIO\n");
+               return;
+       }
+       /* This asserts the GRESTB signal, putting the display into reset */
+       gpiod_direction_output(grestb, 1);
+
+       scen = devm_get_gpiod_from_child(dev, "scen", &np->fwnode);
+       if (IS_ERR(scen)) {
+               dev_err(dev, "no SCEN GPIO\n");
+               return;
+       }
+       gpiod_direction_output(scen, 0);
+       scl = devm_get_gpiod_from_child(dev, "scl", &np->fwnode);
+       if (IS_ERR(scl)) {
+               dev_err(dev, "no SCL GPIO\n");
+               return;
+       }
+       gpiod_direction_output(scl, 0);
+       sda = devm_get_gpiod_from_child(dev, "sda", &np->fwnode);
+       if (IS_ERR(sda)) {
+               dev_err(dev, "no SDA GPIO\n");
+               return;
+       }
+       gpiod_direction_output(sda, 0);
+       board->enable = tpg110_enable;
+       board->disable = tpg110_disable;
+}
+
+int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                           struct device_node *endpoint)
+{
+       struct device_node *panel;
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel)
+               return -ENODEV;
+
+       if (of_device_is_compatible(panel, "tpo,tpg110"))
+               tpg110_init(&fb->dev->dev, panel, fb->board);
+       else
+               dev_info(&fb->dev->dev, "unknown panel\n");
+
+       /* Unknown panel, fall through */
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nomadik_clcd_init_panel);
+
+#define PMU_CTRL_OFFSET 0x0000
+#define PMU_CTRL_LCDNDIF BIT(26)
+
+int nomadik_clcd_init_board(struct amba_device *adev,
+                           struct clcd_board *board)
+{
+       struct regmap *pmu_regmap;
+
+       dev_info(&adev->dev, "Nomadik CLCD board init\n");
+       pmu_regmap =
+               syscon_regmap_lookup_by_compatible("stericsson,nomadik-pmu");
+       if (IS_ERR(pmu_regmap)) {
+               dev_err(&adev->dev, "could not find PMU syscon regmap\n");
+               return PTR_ERR(pmu_regmap);
+       }
+       regmap_update_bits(pmu_regmap,
+                          PMU_CTRL_OFFSET,
+                          PMU_CTRL_LCDNDIF,
+                          0);
+       dev_info(&adev->dev, "set PMU mux to CLCD mode\n");
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nomadik_clcd_init_board);
diff --git a/drivers/video/fbdev/amba-clcd-nomadik.h b/drivers/video/fbdev/amba-clcd-nomadik.h
new file mode 100644 (file)
index 0000000..50aa9bd
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _AMBA_CLCD_NOMADIK_H
+#define _AMBA_CLCD_NOMADIK_H
+
+#include <linux/amba/bus.h>
+
+#ifdef CONFIG_ARCH_NOMADIK
+int nomadik_clcd_init_board(struct amba_device *adev,
+                            struct clcd_board *board);
+int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                           struct device_node *endpoint);
+#else
+static inline int nomadik_clcd_init_board(struct amba_device *adev,
+                                         struct clcd_board *board)
+{
+       return 0;
+}
+static inline int nomadik_clcd_init_panel(struct clcd_fb *fb,
+                                         struct device_node *endpoint)
+{
+       return 0;
+}
+#endif
+
+#endif /* inclusion guard */
index a8a22da..19ad864 100644 (file)
@@ -3,6 +3,12 @@
 #include <linux/amba/bus.h>
 #include <linux/amba/clcd.h>
 #include <linux/platform_data/video-clcd-versatile.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/bitops.h>
+#include "amba-clcd-versatile.h"
 
 static struct clcd_panel vga = {
        .mode           = {
@@ -178,3 +184,392 @@ void versatile_clcd_remove_dma(struct clcd_fb *fb)
        dma_free_wc(&fb->dev->dev, fb->fb.fix.smem_len, fb->fb.screen_base,
                    fb->fb.fix.smem_start);
 }
+
+#ifdef CONFIG_OF
+
+static struct regmap *versatile_syscon_map;
+static struct regmap *versatile_ib2_map;
+
+/*
+ * We detect the different syscon types from the compatible strings.
+ */
+enum versatile_clcd {
+       INTEGRATOR_CLCD_CM,
+       VERSATILE_CLCD,
+       REALVIEW_CLCD_EB,
+       REALVIEW_CLCD_PB1176,
+       REALVIEW_CLCD_PB11MP,
+       REALVIEW_CLCD_PBA8,
+       REALVIEW_CLCD_PBX,
+};
+
+static const struct of_device_id versatile_clcd_of_match[] = {
+       {
+               .compatible = "arm,core-module-integrator",
+               .data = (void *)INTEGRATOR_CLCD_CM,
+       },
+       {
+               .compatible = "arm,versatile-sysreg",
+               .data = (void *)VERSATILE_CLCD,
+       },
+       {
+               .compatible = "arm,realview-eb-syscon",
+               .data = (void *)REALVIEW_CLCD_EB,
+       },
+       {
+               .compatible = "arm,realview-pb1176-syscon",
+               .data = (void *)REALVIEW_CLCD_PB1176,
+       },
+       {
+               .compatible = "arm,realview-pb11mp-syscon",
+               .data = (void *)REALVIEW_CLCD_PB11MP,
+       },
+       {
+               .compatible = "arm,realview-pba8-syscon",
+               .data = (void *)REALVIEW_CLCD_PBA8,
+       },
+       {
+               .compatible = "arm,realview-pbx-syscon",
+               .data = (void *)REALVIEW_CLCD_PBX,
+       },
+       {},
+};
+
+/*
+ * Core module CLCD control on the Integrator/CP, bits
+ * 8 thru 19 of the CM_CONTROL register controls a bunch
+ * of CLCD settings.
+ */
+#define INTEGRATOR_HDR_CTRL_OFFSET     0x0C
+#define INTEGRATOR_CLCD_LCDBIASEN      BIT(8)
+#define INTEGRATOR_CLCD_LCDBIASUP      BIT(9)
+#define INTEGRATOR_CLCD_LCDBIASDN      BIT(10)
+/* Bits 11,12,13 controls the LCD type */
+#define INTEGRATOR_CLCD_LCDMUX_MASK    (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCDMUX_LCD24   BIT(11)
+#define INTEGRATOR_CLCD_LCDMUX_VGA565  BIT(12)
+#define INTEGRATOR_CLCD_LCDMUX_SHARP   (BIT(11)|BIT(12))
+#define INTEGRATOR_CLCD_LCDMUX_VGA555  BIT(13)
+#define INTEGRATOR_CLCD_LCDMUX_VGA24   (BIT(11)|BIT(12)|BIT(13))
+#define INTEGRATOR_CLCD_LCD0_EN                BIT(14)
+#define INTEGRATOR_CLCD_LCD1_EN                BIT(15)
+/* R/L flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC1    BIT(16)
+/* U/D flip on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC2    BIT(17)
+/* No connection on Sharp */
+#define INTEGRATOR_CLCD_LCD_STATIC     BIT(18)
+/* 0 = 24bit VGA, 1 = 18bit VGA */
+#define INTEGRATOR_CLCD_LCD_N24BITEN   BIT(19)
+
+#define INTEGRATOR_CLCD_MASK           (INTEGRATOR_CLCD_LCDBIASEN | \
+                                        INTEGRATOR_CLCD_LCDBIASUP | \
+                                        INTEGRATOR_CLCD_LCDBIASDN | \
+                                        INTEGRATOR_CLCD_LCDMUX_MASK | \
+                                        INTEGRATOR_CLCD_LCD0_EN | \
+                                        INTEGRATOR_CLCD_LCD1_EN | \
+                                        INTEGRATOR_CLCD_LCD_STATIC1 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC2 | \
+                                        INTEGRATOR_CLCD_LCD_STATIC | \
+                                        INTEGRATOR_CLCD_LCD_N24BITEN)
+
+static void integrator_clcd_enable(struct clcd_fb *fb)
+{
+       struct fb_var_screeninfo *var = &fb->fb.var;
+       u32 val;
+
+       dev_info(&fb->dev->dev, "enable Integrator CLCD connectors\n");
+
+       /* FIXME: really needed? */
+       val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
+               INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
+       if (var->bits_per_pixel <= 8 ||
+           (var->bits_per_pixel == 16 && var->green.length == 5))
+               /* Pseudocolor, RGB555, BGR555 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
+       else if (fb->fb.var.bits_per_pixel <= 16)
+               /* truecolor RGB565 */
+               val |= INTEGRATOR_CLCD_LCDMUX_VGA565;
+       else
+               val = 0; /* no idea for this, don't trust the docs */
+
+       regmap_update_bits(versatile_syscon_map,
+                          INTEGRATOR_HDR_CTRL_OFFSET,
+                          INTEGRATOR_CLCD_MASK,
+                          val);
+}
+
+/*
+ * This configuration register in the Versatile and RealView
+ * family is uniformly present but appears more and more
+ * unutilized starting with the RealView series.
+ */
+#define SYS_CLCD                       0x50
+#define SYS_CLCD_MODE_MASK             (BIT(0)|BIT(1))
+#define SYS_CLCD_MODE_888              0
+#define SYS_CLCD_MODE_5551             BIT(0)
+#define SYS_CLCD_MODE_565_R_LSB                BIT(1)
+#define SYS_CLCD_MODE_565_B_LSB                (BIT(0)|BIT(1))
+#define SYS_CLCD_CONNECTOR_MASK                (BIT(2)|BIT(3)|BIT(4)|BIT(5))
+#define SYS_CLCD_NLCDIOON              BIT(2)
+#define SYS_CLCD_VDDPOSSWITCH          BIT(3)
+#define SYS_CLCD_PWR3V5SWITCH          BIT(4)
+#define SYS_CLCD_VDDNEGSWITCH          BIT(5)
+#define SYS_CLCD_TSNSS                 BIT(6) /* touchscreen enable */
+#define SYS_CLCD_SSPEXP                        BIT(7) /* SSP expansion enable */
+
+/* The Versatile can detect the connected panel type */
+#define SYS_CLCD_CLCDID_MASK           (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
+#define SYS_CLCD_ID_SANYO_3_8          (0x00 << 8)
+#define SYS_CLCD_ID_SHARP_8_4          (0x01 << 8)
+#define SYS_CLCD_ID_EPSON_2_2          (0x02 << 8)
+#define SYS_CLCD_ID_SANYO_2_5          (0x07 << 8)
+#define SYS_CLCD_ID_VGA                        (0x1f << 8)
+
+#define SYS_CLCD_TSNDAV                        BIT(13) /* data ready from TS */
+
+/* IB2 control register for the Versatile daughterboard */
+#define IB2_CTRL                       0x00
+#define IB2_CTRL_LCD_SD                        BIT(1) /* 1 = shut down LCD */
+#define IB2_CTRL_LCD_BL_ON             BIT(0)
+#define IB2_CTRL_LCD_MASK              (BIT(0)|BIT(1))
+
+static void versatile_clcd_disable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "disable Versatile CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+
+       /* If we're on an IB2 daughterboard, turn off display */
+       if (versatile_ib2_map) {
+               dev_info(&fb->dev->dev, "disable IB2 display\n");
+               regmap_update_bits(versatile_ib2_map,
+                                  IB2_CTRL,
+                                  IB2_CTRL_LCD_MASK,
+                                  IB2_CTRL_LCD_SD);
+       }
+}
+
+static void versatile_clcd_enable(struct clcd_fb *fb)
+{
+       struct fb_var_screeninfo *var = &fb->fb.var;
+       u32 val = 0;
+
+       dev_info(&fb->dev->dev, "enable Versatile CLCD connectors\n");
+       switch (var->green.length) {
+       case 5:
+               val |= SYS_CLCD_MODE_5551;
+               break;
+       case 6:
+               if (var->red.offset == 0)
+                       val |= SYS_CLCD_MODE_565_R_LSB;
+               else
+                       val |= SYS_CLCD_MODE_565_B_LSB;
+               break;
+       case 8:
+               val |= SYS_CLCD_MODE_888;
+               break;
+       }
+
+       /* Set up the MUX */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_MODE_MASK,
+                          val);
+
+       /* Then enable the display */
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+
+       /* If we're on an IB2 daughterboard, turn on display */
+       if (versatile_ib2_map) {
+               dev_info(&fb->dev->dev, "enable IB2 display\n");
+               regmap_update_bits(versatile_ib2_map,
+                                  IB2_CTRL,
+                                  IB2_CTRL_LCD_MASK,
+                                  IB2_CTRL_LCD_BL_ON);
+       }
+}
+
+static void versatile_clcd_decode(struct clcd_fb *fb, struct clcd_regs *regs)
+{
+       clcdfb_decode(fb, regs);
+
+       /* Always clear BGR for RGB565: we do the routing externally */
+       if (fb->fb.var.green.length == 6)
+               regs->cntl &= ~CNTL_BGR;
+}
+
+static void realview_clcd_disable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "disable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          0);
+}
+
+static void realview_clcd_enable(struct clcd_fb *fb)
+{
+       dev_info(&fb->dev->dev, "enable RealView CLCD connectors\n");
+       regmap_update_bits(versatile_syscon_map,
+                          SYS_CLCD,
+                          SYS_CLCD_CONNECTOR_MASK,
+                          SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
+}
+
+struct versatile_panel {
+       u32 id;
+       char *compatible;
+       bool ib2;
+};
+
+static const struct versatile_panel versatile_panels[] = {
+       {
+               .id = SYS_CLCD_ID_VGA,
+               .compatible = "VGA",
+       },
+       {
+               .id = SYS_CLCD_ID_SANYO_3_8,
+               .compatible = "sanyo,tm38qv67a02a",
+       },
+       {
+               .id = SYS_CLCD_ID_SHARP_8_4,
+               .compatible = "sharp,lq084v1dg21",
+       },
+       {
+               .id = SYS_CLCD_ID_EPSON_2_2,
+               .compatible = "epson,l2f50113t00",
+       },
+       {
+               .id = SYS_CLCD_ID_SANYO_2_5,
+               .compatible = "sanyo,alr252rgt",
+               .ib2 = true,
+       },
+};
+
+static void versatile_panel_probe(struct device *dev,
+                                 struct device_node *endpoint)
+{
+       struct versatile_panel const *vpanel = NULL;
+       struct device_node *panel = NULL;
+       u32 val;
+       int ret;
+       int i;
+
+       /*
+        * The Versatile CLCD has a panel auto-detection mechanism.
+        * We use this and look for the compatible panel in the
+        * device tree.
+        */
+       ret = regmap_read(versatile_syscon_map, SYS_CLCD, &val);
+       if (ret) {
+               dev_err(dev, "cannot read CLCD syscon register\n");
+               return;
+       }
+       val &= SYS_CLCD_CLCDID_MASK;
+
+       /* First find corresponding panel information */
+       for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
+               vpanel = &versatile_panels[i];
+
+               if (val == vpanel->id) {
+                       dev_err(dev, "autodetected panel \"%s\"\n",
+                               vpanel->compatible);
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(versatile_panels)) {
+               dev_err(dev, "could not auto-detect panel\n");
+               return;
+       }
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel) {
+               dev_err(dev, "could not locate panel in DT\n");
+               return;
+       }
+       if (!of_device_is_compatible(panel, vpanel->compatible))
+               dev_err(dev, "panel in DT is not compatible with the "
+                       "auto-detected panel, continuing anyway\n");
+
+       /*
+        * If we have a Sanyo 2.5" port
+        * that we're running on an IB2 and proceed to look for the
+        * IB2 syscon regmap.
+        */
+       if (!vpanel->ib2)
+               return;
+
+       versatile_ib2_map = syscon_regmap_lookup_by_compatible(
+               "arm,versatile-ib2-syscon");
+       if (IS_ERR(versatile_ib2_map)) {
+               dev_err(dev, "could not locate IB2 control register\n");
+               versatile_ib2_map = NULL;
+               return;
+       }
+}
+
+int versatile_clcd_init_panel(struct clcd_fb *fb,
+                             struct device_node *endpoint)
+{
+       const struct of_device_id *clcd_id;
+       enum versatile_clcd versatile_clcd_type;
+       struct device_node *np;
+       struct regmap *map;
+       struct device *dev = &fb->dev->dev;
+
+       np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
+                                            &clcd_id);
+       if (!np) {
+               dev_err(dev, "no Versatile syscon node\n");
+               return -ENODEV;
+       }
+       versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
+
+       map = syscon_node_to_regmap(np);
+       if (IS_ERR(map)) {
+               dev_err(dev, "no Versatile syscon regmap\n");
+               return PTR_ERR(map);
+       }
+
+       switch (versatile_clcd_type) {
+       case INTEGRATOR_CLCD_CM:
+               versatile_syscon_map = map;
+               fb->board->enable = integrator_clcd_enable;
+               /* Override the caps, we have only these */
+               fb->board->caps = CLCD_CAP_5551 | CLCD_CAP_RGB565 |
+                       CLCD_CAP_888;
+               dev_info(dev, "set up callbacks for Integrator PL110\n");
+               break;
+       case VERSATILE_CLCD:
+               versatile_syscon_map = map;
+               fb->board->enable = versatile_clcd_enable;
+               fb->board->disable = versatile_clcd_disable;
+               fb->board->decode = versatile_clcd_decode;
+               versatile_panel_probe(dev, endpoint);
+               dev_info(dev, "set up callbacks for Versatile\n");
+               break;
+       case REALVIEW_CLCD_EB:
+       case REALVIEW_CLCD_PB1176:
+       case REALVIEW_CLCD_PB11MP:
+       case REALVIEW_CLCD_PBA8:
+       case REALVIEW_CLCD_PBX:
+               versatile_syscon_map = map;
+               fb->board->enable = realview_clcd_enable;
+               fb->board->disable = realview_clcd_disable;
+               dev_info(dev, "set up callbacks for RealView PL111\n");
+               break;
+       default:
+               dev_info(dev, "unknown Versatile system controller\n");
+               break;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(versatile_clcd_init_panel);
+#endif
diff --git a/drivers/video/fbdev/amba-clcd-versatile.h b/drivers/video/fbdev/amba-clcd-versatile.h
new file mode 100644 (file)
index 0000000..1b14359
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * Special local versatile callbacks
+ */
+#include <linux/of.h>
+#include <linux/amba/bus.h>
+#include <linux/platform_data/video-clcd-versatile.h>
+
+#if defined(CONFIG_PLAT_VERSATILE_CLCD) && defined(CONFIG_OF)
+int versatile_clcd_init_panel(struct clcd_fb *fb,
+                             struct device_node *endpoint);
+#else
+static inline int versatile_clcd_init_panel(struct clcd_fb *fb,
+                               struct device_node *endpoint)
+{
+       return 0;
+}
+#endif
index 9b15886..ec2671d 100644 (file)
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_graph.h>
+#include <linux/backlight.h>
 #include <video/display_timing.h>
 #include <video/of_display_timing.h>
 #include <video/videomode.h>
 
+#include "amba-clcd-nomadik.h"
+#include "amba-clcd-versatile.h"
+
 #define to_clcd(info)  container_of(info, struct clcd_fb, fb)
 
 /* This is limited to 16 characters when displayed by X startup */
@@ -71,6 +75,11 @@ static void clcdfb_disable(struct clcd_fb *fb)
        if (fb->board->disable)
                fb->board->disable(fb);
 
+       if (fb->panel->backlight) {
+               fb->panel->backlight->props.power = FB_BLANK_POWERDOWN;
+               backlight_update_status(fb->panel->backlight);
+       }
+
        val = readl(fb->regs + fb->off_cntl);
        if (val & CNTL_LCDPWR) {
                val &= ~CNTL_LCDPWR;
@@ -116,6 +125,14 @@ static void clcdfb_enable(struct clcd_fb *fb, u32 cntl)
        cntl |= CNTL_LCDPWR;
        writel(cntl, fb->regs + fb->off_cntl);
 
+       /*
+        * Turn on backlight
+        */
+       if (fb->panel->backlight) {
+               fb->panel->backlight->props.power = FB_BLANK_UNBLANK;
+               backlight_update_status(fb->panel->backlight);
+       }
+
        /*
         * finally, enable the interface.
         */
@@ -211,6 +228,15 @@ clcdfb_set_bitfields(struct clcd_fb *fb, struct fb_var_screeninfo *var)
                        var->blue.length = 4;
                }
                break;
+       case 24:
+               if (fb->vendor->packed_24_bit_pixels) {
+                       var->red.length = 8;
+                       var->green.length = 8;
+                       var->blue.length = 8;
+               } else {
+                       ret = -EINVAL;
+               }
+               break;
        case 32:
                /* If we can't do 888, reject */
                caps &= CLCD_CAP_888;
@@ -297,6 +323,12 @@ static int clcdfb_set_par(struct fb_info *info)
 
        clcdfb_disable(fb);
 
+       /* Some variants must be clocked here */
+       if (fb->vendor->clock_timregs && !fb->clk_enabled) {
+               fb->clk_enabled = true;
+               clk_enable(fb->clk);
+       }
+
        writel(regs.tim0, fb->regs + CLCD_TIM0);
        writel(regs.tim1, fb->regs + CLCD_TIM1);
        writel(regs.tim2, fb->regs + CLCD_TIM2);
@@ -551,7 +583,7 @@ static int clcdfb_register(struct clcd_fb *fb)
 
 #ifdef CONFIG_OF
 static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
-               struct fb_videomode *mode)
+               struct clcd_panel *clcd_panel)
 {
        int err;
        struct display_timing timing;
@@ -563,10 +595,31 @@ static int clcdfb_of_get_dpi_panel_mode(struct device_node *node,
 
        videomode_from_timing(&timing, &video);
 
-       err = fb_videomode_from_videomode(&video, mode);
+       err = fb_videomode_from_videomode(&video, &clcd_panel->mode);
        if (err)
                return err;
 
+       /* Set up some inversion flags */
+       if (timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+               clcd_panel->tim2 |= TIM2_IPC;
+       else if (!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE))
+               /*
+                * To preserve backwards compatibility, the IPC (inverted
+                * pixel clock) flag needs to be set on any display that
+                * doesn't explicitly specify that the pixel clock is
+                * active on the negative or positive edge.
+                */
+               clcd_panel->tim2 |= TIM2_IPC;
+
+       if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
+               clcd_panel->tim2 |= TIM2_IHS;
+
+       if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
+               clcd_panel->tim2 |= TIM2_IVS;
+
+       if (timing.flags & DISPLAY_FLAGS_DE_LOW)
+               clcd_panel->tim2 |= TIM2_IOE;
+
        return 0;
 }
 
@@ -576,11 +629,34 @@ static int clcdfb_snprintf_mode(char *buf, int size, struct fb_videomode *mode)
                        mode->refresh);
 }
 
+static int clcdfb_of_get_backlight(struct device_node *endpoint,
+                                  struct clcd_panel *clcd_panel)
+{
+       struct device_node *panel;
+       struct device_node *backlight;
+
+       panel = of_graph_get_remote_port_parent(endpoint);
+       if (!panel)
+               return -ENODEV;
+
+       /* Look up the optional backlight phandle */
+       backlight = of_parse_phandle(panel, "backlight", 0);
+       if (backlight) {
+               clcd_panel->backlight = of_find_backlight_by_node(backlight);
+               of_node_put(backlight);
+
+               if (!clcd_panel->backlight)
+                       return -EPROBE_DEFER;
+       }
+       return 0;
+}
+
 static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
-               struct fb_videomode *mode)
+               struct clcd_panel *clcd_panel)
 {
        int err;
        struct device_node *panel;
+       struct fb_videomode *mode;
        char *name;
        int len;
 
@@ -590,11 +666,12 @@ static int clcdfb_of_get_mode(struct device *dev, struct device_node *endpoint,
 
        /* Only directly connected DPI panels supported for now */
        if (of_device_is_compatible(panel, "panel-dpi"))
-               err = clcdfb_of_get_dpi_panel_mode(panel, mode);
+               err = clcdfb_of_get_dpi_panel_mode(panel, clcd_panel);
        else
                err = -ENOENT;
        if (err)
                return err;
+       mode = &clcd_panel->mode;
 
        len = clcdfb_snprintf_mode(NULL, 0, mode);
        name = devm_kzalloc(dev, len + 1, GFP_KERNEL);
@@ -616,6 +693,7 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
        } panels[] = {
                { 0x110, 1,  7, 13, CLCD_CAP_5551 },
                { 0x110, 0,  8, 16, CLCD_CAP_888 },
+               { 0x110, 16, 8, 0,  CLCD_CAP_888 },
                { 0x111, 4, 14, 20, CLCD_CAP_444 },
                { 0x111, 3, 11, 19, CLCD_CAP_444 | CLCD_CAP_5551 },
                { 0x111, 3, 10, 19, CLCD_CAP_444 | CLCD_CAP_5551 |
@@ -625,8 +703,8 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
        };
        int i;
 
-       /* Bypass pixel clock divider, data output on the falling edge */
-       fb->panel->tim2 = TIM2_BCD | TIM2_IPC;
+       /* Bypass pixel clock divider */
+       fb->panel->tim2 |= TIM2_BCD;
 
        /* TFT display, vert. comp. interrupt at the start of the back porch */
        fb->panel->cntl |= CNTL_LCDTFT | CNTL_LCDVCOMP(1);
@@ -643,6 +721,49 @@ static int clcdfb_of_init_tft_panel(struct clcd_fb *fb, u32 r0, u32 g0, u32 b0)
                        fb->panel->caps = panels[i].caps;
        }
 
+       /*
+        * If we actually physically connected the R lines to B and
+        * vice versa
+        */
+       if (r0 != 0 && b0 == 0)
+               fb->panel->bgr_connection = true;
+
+       if (fb->panel->caps && fb->vendor->st_bitmux_control) {
+               /*
+                * Set up the special bits for the Nomadik control register
+                * (other platforms tend to do this through an external
+                * register).
+                */
+
+               /* Offset of the highest used color */
+               int maxoff = max3(r0, g0, b0);
+               /* Most significant bit out, highest used bit */
+               int msb = 0;
+
+               if (fb->panel->caps & CLCD_CAP_888) {
+                       msb = maxoff + 8 - 1;
+               } else if (fb->panel->caps & CLCD_CAP_565) {
+                       msb = maxoff + 5 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_565;
+               } else if (fb->panel->caps & CLCD_CAP_5551) {
+                       msb = maxoff + 5 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_5551;
+               } else if (fb->panel->caps & CLCD_CAP_444) {
+                       msb = maxoff + 4 - 1;
+                       fb->panel->cntl |= CNTL_ST_1XBPP_444;
+               }
+
+               /* Send out as many bits as we need */
+               if (msb > 17)
+                       fb->panel->cntl |= CNTL_ST_CDWID_24;
+               else if (msb > 15)
+                       fb->panel->cntl |= CNTL_ST_CDWID_18;
+               else if (msb > 11)
+                       fb->panel->cntl |= CNTL_ST_CDWID_16;
+               else
+                       fb->panel->cntl |= CNTL_ST_CDWID_12;
+       }
+
        return fb->panel->caps ? 0 : -EINVAL;
 }
 
@@ -658,11 +779,24 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
        if (!fb->panel)
                return -ENOMEM;
 
+       /*
+        * Fetch the panel endpoint.
+        */
        endpoint = of_graph_get_next_endpoint(fb->dev->dev.of_node, NULL);
        if (!endpoint)
                return -ENODEV;
 
-       err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, &fb->panel->mode);
+       if (fb->vendor->init_panel) {
+               err = fb->vendor->init_panel(fb, endpoint);
+               if (err)
+                       return err;
+       }
+
+       err = clcdfb_of_get_backlight(endpoint, fb->panel);
+       if (err)
+               return err;
+
+       err = clcdfb_of_get_mode(&fb->dev->dev, endpoint, fb->panel);
        if (err)
                return err;
 
@@ -693,11 +827,11 @@ static int clcdfb_of_init_display(struct clcd_fb *fb)
 
        if (of_property_read_u32_array(endpoint,
                        "arm,pl11x,tft-r0g0b0-pads",
-                       tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) == 0)
-               return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
-                                tft_r0b0g0[1],  tft_r0b0g0[2]);
+                       tft_r0b0g0, ARRAY_SIZE(tft_r0b0g0)) != 0)
+               return -ENOENT;
 
-       return -ENOENT;
+       return clcdfb_of_init_tft_panel(fb, tft_r0b0g0[0],
+                                       tft_r0b0g0[1],  tft_r0b0g0[2]);
 }
 
 static int clcdfb_of_vram_setup(struct clcd_fb *fb)
@@ -818,6 +952,7 @@ static struct clcd_board *clcdfb_of_get_board(struct amba_device *dev)
 static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
 {
        struct clcd_board *board = dev_get_platdata(&dev->dev);
+       struct clcd_vendor_data *vendor = id->data;
        struct clcd_fb *fb;
        int ret;
 
@@ -827,6 +962,12 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
        if (!board)
                return -EINVAL;
 
+       if (vendor->init_board) {
+               ret = vendor->init_board(dev, board);
+               if (ret)
+                       return ret;
+       }
+
        ret = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
        if (ret)
                goto out;
@@ -845,17 +986,18 @@ static int clcdfb_probe(struct amba_device *dev, const struct amba_id *id)
        }
 
        fb->dev = dev;
+       fb->vendor = vendor;
        fb->board = board;
 
-       dev_info(&fb->dev->dev, "PL%03x rev%u at 0x%08llx\n",
-               amba_part(dev), amba_rev(dev),
+       dev_info(&fb->dev->dev, "PL%03x designer %02x rev%u at 0x%08llx\n",
+               amba_part(dev), amba_manf(dev), amba_rev(dev),
                (unsigned long long)dev->res.start);
 
        ret = fb->board->setup(fb);
        if (ret)
                goto free_fb;
 
-       ret = clcdfb_register(fb); 
+       ret = clcdfb_register(fb);
        if (ret == 0) {
                amba_set_drvdata(dev, fb);
                goto out;
@@ -891,10 +1033,30 @@ static int clcdfb_remove(struct amba_device *dev)
        return 0;
 }
 
+static struct clcd_vendor_data vendor_arm = {
+       /* Sets up the versatile board displays */
+       .init_panel = versatile_clcd_init_panel,
+};
+
+static struct clcd_vendor_data vendor_nomadik = {
+       .clock_timregs = true,
+       .packed_24_bit_pixels = true,
+       .st_bitmux_control = true,
+       .init_board = nomadik_clcd_init_board,
+       .init_panel = nomadik_clcd_init_panel,
+};
+
 static struct amba_id clcdfb_id_table[] = {
        {
                .id     = 0x00041110,
                .mask   = 0x000ffffe,
+               .data   = &vendor_arm,
+       },
+       /* ST Electronics Nomadik variant */
+       {
+               .id     = 0x00180110,
+               .mask   = 0x00fffffe,
+               .data   = &vendor_nomadik,
        },
        { 0, 0 },
 };
index 1b0b233..1928cb2 100644 (file)
@@ -79,7 +79,7 @@ struct arcfb_par {
        spinlock_t lock;
 };
 
-static struct fb_fix_screeninfo arcfb_fix = {
+static const struct fb_fix_screeninfo arcfb_fix = {
        .id =           "arcfb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_MONO01,
@@ -89,7 +89,7 @@ static struct fb_fix_screeninfo arcfb_fix = {
        .accel =        FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo arcfb_var = {
+static const struct fb_var_screeninfo arcfb_var = {
        .xres           = 128,
        .yres           = 64,
        .xres_virtual   = 128,
index 7e8ddf0..91eea45 100644 (file)
@@ -474,7 +474,7 @@ static void chips_hw_init(struct fb_info *p)
                write_fr(chips_init_fr[i].addr, chips_init_fr[i].data);
 }
 
-static struct fb_fix_screeninfo asiliantfb_fix = {
+static const struct fb_fix_screeninfo asiliantfb_fix = {
        .id =           "Asiliant 69000",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_PSEUDOCOLOR,
@@ -483,7 +483,7 @@ static struct fb_fix_screeninfo asiliantfb_fix = {
        .smem_len =     0x200000,       /* 2MB */
 };
 
-static struct fb_var_screeninfo asiliantfb_var = {
+static const struct fb_var_screeninfo asiliantfb_var = {
        .xres           = 640,
        .yres           = 480,
        .xres_virtual   = 640,
index 0a46268..fa07242 100644 (file)
@@ -93,7 +93,7 @@
 
 #ifndef CONFIG_PPC_PMAC
 /* default mode */
-static struct fb_var_screeninfo default_var = {
+static const struct fb_var_screeninfo default_var = {
        /* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
        640, 480, 640, 480, 0, 0, 8, 0,
        {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -104,7 +104,7 @@ static struct fb_var_screeninfo default_var = {
 #else /* CONFIG_PPC_PMAC */
 /* default to 1024x768 at 75Hz on PPC - this will work
  * on the iMac, the usual 640x480 @ 60Hz doesn't. */
-static struct fb_var_screeninfo default_var = {
+static const struct fb_var_screeninfo default_var = {
        /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */
        1024, 768, 1024, 768, 0, 0, 8, 0,
        {0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -375,7 +375,7 @@ static const struct aty128_meminfo ddr_sgram = {
        .name = "64-bit DDR SGRAM",
 };
 
-static struct fb_fix_screeninfo aty128fb_fix = {
+static const struct fb_fix_screeninfo aty128fb_fix = {
        .id             = "ATY Rage128",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
index f34ed47..11026e7 100644 (file)
@@ -212,7 +212,7 @@ struct pci_mmap_map {
        unsigned long prot_mask;
 };
 
-static struct fb_fix_screeninfo atyfb_fix = {
+static const struct fb_fix_screeninfo atyfb_fix = {
        .id             = "ATY Mach64",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
index f1ce229..278b421 100644 (file)
@@ -4,7 +4,7 @@
 
 #include "../edid.h"
 
-static struct fb_var_screeninfo radeonfb_default_var = {
+static const struct fb_var_screeninfo radeonfb_default_var = {
        .xres           = 640,
        .yres           = 480,
        .xres_virtual   = 640,
index f9507b1..6c2b2ca 100644 (file)
@@ -43,6 +43,7 @@
 #include <linux/ctype.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
+#include <linux/uaccess.h>
 
 #include <asm/mach-au1x00/au1000.h>
 #include <asm/mach-au1x00/au1200fb.h>  /* platform_data */
index e2d7d03..542ffad 100644 (file)
@@ -375,7 +375,6 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
 {
        int ret = 0;
        struct proc_dir_entry *entry;
-       int num_modes = ARRAY_SIZE(known_modes);
 
        struct adv7393fb_device *fbdev = NULL;
 
@@ -384,7 +383,7 @@ static int bfin_adv7393_fb_probe(struct i2c_client *client,
                return -EINVAL;
        }
 
-       if (mode > num_modes) {
+       if (mode >= ARRAY_SIZE(known_modes)) {
                dev_err(&client->dev, "mode %d: not supported", mode);
                return -EFAULT;
        }
@@ -797,7 +796,7 @@ static struct i2c_driver bfin_adv7393_fb_driver = {
 
 static int __init bfin_adv7393_fb_driver_init(void)
 {
-#if  defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE)
+#if IS_ENABLED(CONFIG_I2C_BLACKFIN_TWI)
        request_module("i2c-bfin-twi");
 #else
        request_module("i2c-gpio");
index 924bad4..37a37c4 100644 (file)
@@ -50,9 +50,9 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green,
                return 1;
 
        if (regno < 16) {
-               red   >>= 8;
-               green >>= 8;
-               blue  >>= 8;
+               red   >>= 16 - info->var.red.length;
+               green >>= 16 - info->var.green.length;
+               blue  >>= 16 - info->var.blue.length;
                ((u32 *)(info->pseudo_palette))[regno] =
                        (red   << info->var.red.offset)   |
                        (green << info->var.green.offset) |
diff --git a/drivers/video/fbdev/exynos/Kconfig b/drivers/video/fbdev/exynos/Kconfig
deleted file mode 100644 (file)
index d916bef..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#
-# Exynos Video configuration
-#
-
-menuconfig EXYNOS_VIDEO
-       tristate "Exynos Video driver support"
-       depends on ARCH_S5PV210 || ARCH_EXYNOS
-       help
-         This enables support for EXYNOS Video device.
-
-if EXYNOS_VIDEO
-
-#
-# MIPI DSI driver
-#
-
-config EXYNOS_MIPI_DSI
-       tristate "EXYNOS MIPI DSI driver support."
-       select GENERIC_PHY
-       help
-         This enables support for MIPI-DSI device.
-
-config EXYNOS_LCD_S6E8AX0
-       tristate "S6E8AX0 MIPI AMOLED LCD Driver"
-       depends on EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE
-       depends on (LCD_CLASS_DEVICE = y)
-       default n
-       help
-         If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its
-         LCD control driver.
-
-endif # EXYNOS_VIDEO
diff --git a/drivers/video/fbdev/exynos/Makefile b/drivers/video/fbdev/exynos/Makefile
deleted file mode 100644 (file)
index 02d8dc5..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Makefile for the exynos video drivers.
-#
-
-obj-$(CONFIG_EXYNOS_MIPI_DSI)          += exynos-mipi-dsi-mod.o
-
-exynos-mipi-dsi-mod-objs               += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
-                                          exynos_mipi_dsi_lowlevel.o
-obj-$(CONFIG_EXYNOS_LCD_S6E8AX0)       += s6e8ax0.o
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi.c
deleted file mode 100644 (file)
index 92e4af3..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi.c
- *
- * Samsung SoC MIPI-DSIM driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/fb.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/memory.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/kthread.h>
-#include <linux/notifier.h>
-#include <linux/phy/phy.h>
-#include <linux/regulator/consumer.h>
-#include <linux/pm_runtime.h>
-#include <linux/err.h>
-
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_common.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-
-struct mipi_dsim_ddi {
-       int                             bus_id;
-       struct list_head                list;
-       struct mipi_dsim_lcd_device     *dsim_lcd_dev;
-       struct mipi_dsim_lcd_driver     *dsim_lcd_drv;
-};
-
-static LIST_HEAD(dsim_ddi_list);
-
-static DEFINE_MUTEX(mipi_dsim_lock);
-
-static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device
-                                                       *pdev)
-{
-       return pdev->dev.platform_data;
-}
-
-static struct regulator_bulk_data supplies[] = {
-       { .supply = "vdd11", },
-       { .supply = "vdd18", },
-};
-
-static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim)
-{
-       int ret;
-
-       mutex_lock(&dsim->lock);
-       ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
-       mutex_unlock(&dsim->lock);
-
-       return ret;
-}
-
-static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim)
-{
-       int ret;
-
-       mutex_lock(&dsim->lock);
-       ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
-       mutex_unlock(&dsim->lock);
-
-       return ret;
-}
-
-/* update all register settings to MIPI DSI controller. */
-static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim)
-{
-       /*
-        * data from Display controller(FIMD) is not transferred in video mode
-        * but in case of command mode, all settings is not updated to
-        * registers.
-        */
-       exynos_mipi_dsi_stand_by(dsim, 0);
-
-       exynos_mipi_dsi_init_dsim(dsim);
-       exynos_mipi_dsi_init_link(dsim);
-
-       exynos_mipi_dsi_set_hs_enable(dsim);
-
-       /* set display timing. */
-       exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
-
-       exynos_mipi_dsi_init_interrupt(dsim);
-
-       /*
-        * data from Display controller(FIMD) is transferred in video mode
-        * but in case of command mode, all settings are updated to registers.
-        */
-       exynos_mipi_dsi_stand_by(dsim, 1);
-}
-
-static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim,
-               int power)
-{
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       switch (power) {
-       case FB_BLANK_POWERDOWN:
-               if (dsim->suspended)
-                       return 0;
-
-               if (client_drv && client_drv->suspend)
-                       client_drv->suspend(client_dev);
-
-               clk_disable(dsim->clock);
-
-               exynos_mipi_regulator_disable(dsim);
-
-               dsim->suspended = true;
-
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
-{
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       switch (power) {
-       case FB_BLANK_UNBLANK:
-               if (!dsim->suspended)
-                       return 0;
-
-               /* lcd panel power on. */
-               if (client_drv && client_drv->power_on)
-                       client_drv->power_on(client_dev, 1);
-
-               exynos_mipi_regulator_enable(dsim);
-
-               /* enable MIPI-DSI PHY. */
-               phy_power_on(dsim->phy);
-
-               clk_enable(dsim->clock);
-
-               exynos_mipi_update_cfg(dsim);
-
-               /* set lcd panel sequence commands. */
-               if (client_drv && client_drv->set_sequence)
-                       client_drv->set_sequence(client_dev);
-
-               dsim->suspended = false;
-
-               break;
-       case FB_BLANK_NORMAL:
-               /* TODO. */
-               break;
-       default:
-               break;
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
-{
-       struct mipi_dsim_ddi *dsim_ddi;
-
-       if (!lcd_dev->name) {
-               pr_err("dsim_lcd_device name is NULL.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
-       if (!dsim_ddi) {
-               pr_err("failed to allocate dsim_ddi object.\n");
-               return -ENOMEM;
-       }
-
-       dsim_ddi->dsim_lcd_dev = lcd_dev;
-
-       mutex_lock(&mipi_dsim_lock);
-       list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
-       mutex_unlock(&mipi_dsim_lock);
-
-       return 0;
-}
-
-static struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(
-                                       struct mipi_dsim_lcd_driver *lcd_drv)
-{
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_device *lcd_dev;
-
-       mutex_lock(&mipi_dsim_lock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               if (!dsim_ddi)
-                       goto out;
-
-               lcd_dev = dsim_ddi->dsim_lcd_dev;
-               if (!lcd_dev)
-                       continue;
-
-               if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
-                       /**
-                        * bus_id would be used to identify
-                        * connected bus.
-                        */
-                       dsim_ddi->bus_id = lcd_dev->bus_id;
-                       mutex_unlock(&mipi_dsim_lock);
-
-                       return dsim_ddi;
-               }
-
-               list_del(&dsim_ddi->list);
-               kfree(dsim_ddi);
-       }
-
-out:
-       mutex_unlock(&mipi_dsim_lock);
-
-       return NULL;
-}
-
-int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
-{
-       struct mipi_dsim_ddi *dsim_ddi;
-
-       if (!lcd_drv->name) {
-               pr_err("dsim_lcd_driver name is NULL.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
-       if (!dsim_ddi) {
-               pr_err("mipi_dsim_ddi object not found.\n");
-               return -EFAULT;
-       }
-
-       dsim_ddi->dsim_lcd_drv = lcd_drv;
-
-       pr_info("registered panel driver(%s) to mipi-dsi driver.\n",
-               lcd_drv->name);
-
-       return 0;
-
-}
-EXPORT_SYMBOL_GPL(exynos_mipi_dsi_register_lcd_driver);
-
-static struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(
-                                               struct mipi_dsim_device *dsim,
-                                               const char *name)
-{
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_driver *lcd_drv;
-       struct mipi_dsim_lcd_device *lcd_dev;
-       int ret;
-
-       mutex_lock(&dsim->lock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               lcd_drv = dsim_ddi->dsim_lcd_drv;
-               lcd_dev = dsim_ddi->dsim_lcd_dev;
-               if (!lcd_drv || !lcd_dev ||
-                       (dsim->id != dsim_ddi->bus_id))
-                               continue;
-
-               dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n",
-                               lcd_drv->id, lcd_dev->id);
-               dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n",
-                               lcd_dev->bus_id, dsim->id);
-
-               if ((strcmp(lcd_drv->name, name) == 0)) {
-                       lcd_dev->master = dsim;
-
-                       lcd_dev->dev.parent = dsim->dev;
-                       dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name);
-
-                       ret = device_register(&lcd_dev->dev);
-                       if (ret < 0) {
-                               dev_err(dsim->dev,
-                                       "can't register %s, status %d\n",
-                                       dev_name(&lcd_dev->dev), ret);
-                               mutex_unlock(&dsim->lock);
-
-                               return NULL;
-                       }
-
-                       dsim->dsim_lcd_dev = lcd_dev;
-                       dsim->dsim_lcd_drv = lcd_drv;
-
-                       mutex_unlock(&dsim->lock);
-
-                       return dsim_ddi;
-               }
-       }
-
-       mutex_unlock(&dsim->lock);
-
-       return NULL;
-}
-
-/* define MIPI-DSI Master operations. */
-static struct mipi_dsim_master_ops master_ops = {
-       .cmd_read                       = exynos_mipi_dsi_rd_data,
-       .cmd_write                      = exynos_mipi_dsi_wr_data,
-       .get_dsim_frame_done            = exynos_mipi_dsi_get_frame_done_status,
-       .clear_dsim_frame_done          = exynos_mipi_dsi_clear_frame_done,
-       .set_early_blank_mode           = exynos_mipi_dsi_early_blank_mode,
-       .set_blank_mode                 = exynos_mipi_dsi_blank_mode,
-};
-
-static int exynos_mipi_dsi_probe(struct platform_device *pdev)
-{
-       struct resource *res;
-       struct mipi_dsim_device *dsim;
-       struct mipi_dsim_config *dsim_config;
-       struct mipi_dsim_platform_data *dsim_pd;
-       struct mipi_dsim_ddi *dsim_ddi;
-       int ret = -EINVAL;
-
-       dsim = devm_kzalloc(&pdev->dev, sizeof(struct mipi_dsim_device),
-                               GFP_KERNEL);
-       if (!dsim) {
-               dev_err(&pdev->dev, "failed to allocate dsim object.\n");
-               return -ENOMEM;
-       }
-
-       dsim->pd = to_dsim_plat(pdev);
-       dsim->dev = &pdev->dev;
-       dsim->id = pdev->id;
-
-       /* get mipi_dsim_platform_data. */
-       dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
-       if (dsim_pd == NULL) {
-               dev_err(&pdev->dev, "failed to get platform data for dsim.\n");
-               return -EINVAL;
-       }
-       /* get mipi_dsim_config. */
-       dsim_config = dsim_pd->dsim_config;
-       if (dsim_config == NULL) {
-               dev_err(&pdev->dev, "failed to get dsim config data.\n");
-               return -EINVAL;
-       }
-
-       dsim->dsim_config = dsim_config;
-       dsim->master_ops = &master_ops;
-
-       mutex_init(&dsim->lock);
-
-       ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies),
-                                       supplies);
-       if (ret) {
-               dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
-               return ret;
-       }
-
-       dsim->phy = devm_phy_get(&pdev->dev, "dsim");
-       if (IS_ERR(dsim->phy))
-               return PTR_ERR(dsim->phy);
-
-       dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
-       if (IS_ERR(dsim->clock)) {
-               dev_err(&pdev->dev, "failed to get dsim clock source\n");
-               return -ENODEV;
-       }
-
-       clk_enable(dsim->clock);
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       dsim->reg_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dsim->reg_base)) {
-               ret = PTR_ERR(dsim->reg_base);
-               goto error;
-       }
-
-       mutex_init(&dsim->lock);
-
-       /* bind lcd ddi matched with panel name. */
-       dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
-       if (!dsim_ddi) {
-               dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       ret = platform_get_irq(pdev, 0);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "failed to request dsim irq resource\n");
-               goto error;
-       }
-       dsim->irq = ret;
-
-       init_completion(&dsim_wr_comp);
-       init_completion(&dsim_rd_comp);
-       platform_set_drvdata(pdev, dsim);
-
-       ret = devm_request_irq(&pdev->dev, dsim->irq,
-                       exynos_mipi_dsi_interrupt_handler,
-                       IRQF_SHARED, dev_name(&pdev->dev), dsim);
-       if (ret != 0) {
-               dev_err(&pdev->dev, "failed to request dsim irq\n");
-               ret = -EINVAL;
-               goto error;
-       }
-
-       /* enable interrupts */
-       exynos_mipi_dsi_init_interrupt(dsim);
-
-       /* initialize mipi-dsi client(lcd panel). */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe)
-               dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev);
-
-       /* in case mipi-dsi has been enabled by bootloader */
-       if (dsim_pd->enabled) {
-               exynos_mipi_regulator_enable(dsim);
-               goto done;
-       }
-
-       /* lcd panel power on. */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on)
-               dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1);
-
-       exynos_mipi_regulator_enable(dsim);
-
-       /* enable MIPI-DSI PHY. */
-       phy_power_on(dsim->phy);
-
-       exynos_mipi_update_cfg(dsim);
-
-       /* set lcd panel sequence commands. */
-       if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence)
-               dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev);
-
-       dsim->suspended = false;
-
-done:
-       platform_set_drvdata(pdev, dsim);
-
-       dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
-               dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
-
-       return 0;
-
-error:
-       clk_disable(dsim->clock);
-       return ret;
-}
-
-static int exynos_mipi_dsi_remove(struct platform_device *pdev)
-{
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_ddi *dsim_ddi, *next;
-       struct mipi_dsim_lcd_driver *dsim_lcd_drv;
-
-       clk_disable(dsim->clock);
-
-       list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
-               if (dsim_ddi) {
-                       if (dsim->id != dsim_ddi->bus_id)
-                               continue;
-
-                       dsim_lcd_drv = dsim_ddi->dsim_lcd_drv;
-
-                       if (dsim_lcd_drv->remove)
-                               dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev);
-
-                       kfree(dsim_ddi);
-               }
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int exynos_mipi_dsi_suspend(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       disable_irq(dsim->irq);
-
-       if (dsim->suspended)
-               return 0;
-
-       if (client_drv && client_drv->suspend)
-               client_drv->suspend(client_dev);
-
-       /* disable MIPI-DSI PHY. */
-       phy_power_off(dsim->phy);
-
-       clk_disable(dsim->clock);
-
-       exynos_mipi_regulator_disable(dsim);
-
-       dsim->suspended = true;
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_resume(struct device *dev)
-{
-       struct platform_device *pdev = to_platform_device(dev);
-       struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
-       struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
-       struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
-
-       enable_irq(dsim->irq);
-
-       if (!dsim->suspended)
-               return 0;
-
-       /* lcd panel power on. */
-       if (client_drv && client_drv->power_on)
-               client_drv->power_on(client_dev, 1);
-
-       exynos_mipi_regulator_enable(dsim);
-
-       /* enable MIPI-DSI PHY. */
-       phy_power_on(dsim->phy);
-
-       clk_enable(dsim->clock);
-
-       exynos_mipi_update_cfg(dsim);
-
-       /* set lcd panel sequence commands. */
-       if (client_drv && client_drv->set_sequence)
-               client_drv->set_sequence(client_dev);
-
-       dsim->suspended = false;
-
-       return 0;
-}
-#endif
-
-static const struct dev_pm_ops exynos_mipi_dsi_pm_ops = {
-       SET_SYSTEM_SLEEP_PM_OPS(exynos_mipi_dsi_suspend, exynos_mipi_dsi_resume)
-};
-
-static struct platform_driver exynos_mipi_dsi_driver = {
-       .probe = exynos_mipi_dsi_probe,
-       .remove = exynos_mipi_dsi_remove,
-       .driver = {
-                  .name = "exynos-mipi-dsim",
-                  .pm = &exynos_mipi_dsi_pm_ops,
-       },
-};
-
-module_platform_driver(exynos_mipi_dsi_driver);
-
-MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC MIPI-DSI driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.c
deleted file mode 100644 (file)
index 2358a2f..0000000
+++ /dev/null
@@ -1,880 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c
- *
- * Samsung SoC MIPI-DSI common driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/fb.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/memory.h>
-#include <linux/delay.h>
-#include <linux/irqreturn.h>
-#include <linux/kthread.h>
-
-#include <video/mipi_display.h>
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_regs.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-#include "exynos_mipi_dsi_common.h"
-
-#define MIPI_FIFO_TIMEOUT      msecs_to_jiffies(250)
-#define MIPI_RX_FIFO_READ_DONE  0x30800002
-#define MIPI_MAX_RX_FIFO        20
-#define MHZ                    (1000 * 1000)
-#define FIN_HZ                 (24 * MHZ)
-
-#define DFIN_PLL_MIN_HZ                (6 * MHZ)
-#define DFIN_PLL_MAX_HZ                (12 * MHZ)
-
-#define DFVCO_MIN_HZ           (500 * MHZ)
-#define DFVCO_MAX_HZ           (1000 * MHZ)
-
-#define TRY_GET_FIFO_TIMEOUT   (5000 * 2)
-#define TRY_FIFO_CLEAR         (10)
-
-/* MIPI-DSIM status types. */
-enum {
-       DSIM_STATE_INIT,        /* should be initialized. */
-       DSIM_STATE_STOP,        /* CPU and LCDC are LP mode. */
-       DSIM_STATE_HSCLKEN,     /* HS clock was enabled. */
-       DSIM_STATE_ULPS
-};
-
-/* define DSI lane types. */
-enum {
-       DSIM_LANE_CLOCK = (1 << 0),
-       DSIM_LANE_DATA0 = (1 << 1),
-       DSIM_LANE_DATA1 = (1 << 2),
-       DSIM_LANE_DATA2 = (1 << 3),
-       DSIM_LANE_DATA3 = (1 << 4)
-};
-
-static unsigned int dpll_table[15] = {
-       100, 120, 170, 220, 270,
-       320, 390, 450, 510, 560,
-       640, 690, 770, 870, 950
-};
-
-irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id)
-{
-       struct mipi_dsim_device *dsim = dev_id;
-       unsigned int intsrc, intmsk;
-
-       intsrc = exynos_mipi_dsi_read_interrupt(dsim);
-       intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim);
-       intmsk = ~intmsk & intsrc;
-
-       if (intsrc & INTMSK_RX_DONE) {
-               complete(&dsim_rd_comp);
-               dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n");
-       }
-       if (intsrc & INTMSK_FIFO_EMPTY) {
-               complete(&dsim_wr_comp);
-               dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n");
-       }
-
-       exynos_mipi_dsi_clear_interrupt(dsim, intmsk);
-
-       return IRQ_HANDLED;
-}
-
-/*
- * write long packet to mipi dsi slave
- * @dsim: mipi dsim device structure.
- * @data0: packet data to send.
- * @data1: size of packet data
- */
-static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
-               const unsigned char *data0, unsigned int data_size)
-{
-       unsigned int data_cnt = 0, payload = 0;
-
-       /* in case that data count is more then 4 */
-       for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) {
-               /*
-                * after sending 4bytes per one time,
-                * send remainder data less then 4.
-                */
-               if ((data_size - data_cnt) < 4) {
-                       if ((data_size - data_cnt) == 3) {
-                               payload = data0[data_cnt] |
-                                   data0[data_cnt + 1] << 8 |
-                                       data0[data_cnt + 2] << 16;
-                       dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n",
-                               payload, data0[data_cnt],
-                               data0[data_cnt + 1],
-                               data0[data_cnt + 2]);
-                       } else if ((data_size - data_cnt) == 2) {
-                               payload = data0[data_cnt] |
-                                       data0[data_cnt + 1] << 8;
-                       dev_dbg(dsim->dev,
-                               "count = 2 payload = %x, %x %x\n", payload,
-                               data0[data_cnt],
-                               data0[data_cnt + 1]);
-                       } else if ((data_size - data_cnt) == 1) {
-                               payload = data0[data_cnt];
-                       }
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-               /* send 4bytes per one time. */
-               } else {
-                       payload = data0[data_cnt] |
-                               data0[data_cnt + 1] << 8 |
-                               data0[data_cnt + 2] << 16 |
-                               data0[data_cnt + 3] << 24;
-
-                       dev_dbg(dsim->dev,
-                               "count = 4 payload = %x, %x %x %x %x\n",
-                               payload, *(u8 *)(data0 + data_cnt),
-                               data0[data_cnt + 1],
-                               data0[data_cnt + 2],
-                               data0[data_cnt + 3]);
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-               }
-       }
-}
-
-int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       const unsigned char *data0, unsigned int data_size)
-{
-       unsigned int check_rx_ack = 0;
-
-       if (dsim->state == DSIM_STATE_ULPS) {
-               dev_err(dsim->dev, "state is ULPS.\n");
-
-               return -EINVAL;
-       }
-
-       /* FIXME!!! why does it need this delay? */
-       msleep(20);
-
-       mutex_lock(&dsim->lock);
-
-       switch (data_id) {
-       /* short packet types of packet types for command. */
-       case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
-       case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
-       case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
-       case MIPI_DSI_DCS_SHORT_WRITE:
-       case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
-       case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
-               if (check_rx_ack) {
-                       /* process response func should be implemented */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-
-       /* general command */
-       case MIPI_DSI_COLOR_MODE_OFF:
-       case MIPI_DSI_COLOR_MODE_ON:
-       case MIPI_DSI_SHUTDOWN_PERIPHERAL:
-       case MIPI_DSI_TURN_ON_PERIPHERAL:
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-
-       /* packet types for video data */
-       case MIPI_DSI_V_SYNC_START:
-       case MIPI_DSI_V_SYNC_END:
-       case MIPI_DSI_H_SYNC_START:
-       case MIPI_DSI_H_SYNC_END:
-       case MIPI_DSI_END_OF_TRANSMISSION:
-               mutex_unlock(&dsim->lock);
-               return 0;
-
-       /* long packet type and null packet */
-       case MIPI_DSI_NULL_PACKET:
-       case MIPI_DSI_BLANKING_PACKET:
-               mutex_unlock(&dsim->lock);
-               return 0;
-       case MIPI_DSI_GENERIC_LONG_WRITE:
-       case MIPI_DSI_DCS_LONG_WRITE:
-       {
-               unsigned int size, payload = 0;
-               reinit_completion(&dsim_wr_comp);
-
-               size = data_size * 4;
-
-               /* if data count is less then 4, then send 3bytes data.  */
-               if (data_size < 4) {
-                       payload = data0[0] |
-                               data0[1] << 8 |
-                               data0[2] << 16;
-
-                       exynos_mipi_dsi_wr_tx_data(dsim, payload);
-
-                       dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n",
-                               data_size, payload, data0[0],
-                               data0[1], data0[2]);
-
-               /* in case that data count is more then 4 */
-               } else
-                       exynos_mipi_dsi_long_data_wr(dsim, data0, data_size);
-
-               /* put data into header fifo */
-               exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff,
-                       (data_size & 0xff00) >> 8);
-
-               if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp,
-                                                       MIPI_FIFO_TIMEOUT)) {
-                       dev_warn(dsim->dev, "command write timeout.\n");
-                       mutex_unlock(&dsim->lock);
-                       return -EAGAIN;
-               }
-
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-       }
-
-       /* packet typo for video data */
-       case MIPI_DSI_PACKED_PIXEL_STREAM_16:
-       case MIPI_DSI_PACKED_PIXEL_STREAM_18:
-       case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
-       case MIPI_DSI_PACKED_PIXEL_STREAM_24:
-               if (check_rx_ack) {
-                       /* process response func should be implemented. */
-                       mutex_unlock(&dsim->lock);
-                       return 0;
-               } else {
-                       mutex_unlock(&dsim->lock);
-                       return -EINVAL;
-               }
-       default:
-               dev_warn(dsim->dev,
-                       "data id %x is not supported current DSI spec.\n",
-                       data_id);
-
-               mutex_unlock(&dsim->lock);
-               return -EINVAL;
-       }
-}
-
-static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim,
-               unsigned int req_size, unsigned int rx_data, u8 *rx_buf)
-{
-       unsigned int rcv_pkt, i, j;
-       u16 rxsize;
-
-       /* for long packet */
-       rxsize = (u16)((rx_data & 0x00ffff00) >> 8);
-       dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize);
-       if (rxsize != req_size) {
-               dev_dbg(dsim->dev,
-                       "received size mismatch received: %d, requested: %d\n",
-                       rxsize, req_size);
-               goto err;
-       }
-
-       for (i = 0; i < (rxsize >> 2); i++) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
-               for (j = 0; j < 4; j++) {
-                       rx_buf[(i * 4) + j] =
-                                       (u8)(rcv_pkt >> (j * 8)) & 0xff;
-                       dev_dbg(dsim->dev, "received value : %02x\n",
-                                       (rcv_pkt >> (j * 8)) & 0xff);
-               }
-       }
-       if (rxsize % 4) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
-               for (j = 0; j < (rxsize % 4); j++) {
-                       rx_buf[(i * 4) + j] =
-                                       (u8)(rcv_pkt >> (j * 8)) & 0xff;
-                       dev_dbg(dsim->dev, "received value : %02x\n",
-                                       (rcv_pkt >> (j * 8)) & 0xff);
-               }
-       }
-
-       return rxsize;
-
-err:
-       return -EINVAL;
-}
-
-static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size)
-{
-       switch (req_size) {
-       case 1:
-               return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE;
-       case 2:
-               return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE;
-       default:
-               return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE;
-       }
-}
-
-int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       unsigned int data0, unsigned int req_size, u8 *rx_buf)
-{
-       unsigned int rx_data, rcv_pkt, i;
-       u8 response = 0;
-       u16 rxsize;
-
-       if (dsim->state == DSIM_STATE_ULPS) {
-               dev_err(dsim->dev, "state is ULPS.\n");
-
-               return -EINVAL;
-       }
-
-       /* FIXME!!! */
-       msleep(20);
-
-       mutex_lock(&dsim->lock);
-       reinit_completion(&dsim_rd_comp);
-       exynos_mipi_dsi_rd_tx_header(dsim,
-               MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size);
-
-       response = exynos_mipi_dsi_response_size(req_size);
-
-       switch (data_id) {
-       case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
-       case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
-       case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
-       case MIPI_DSI_DCS_READ:
-               exynos_mipi_dsi_rd_tx_header(dsim,
-                       data_id, data0);
-               /* process response func should be implemented. */
-               break;
-       default:
-               dev_warn(dsim->dev,
-                       "data id %x is not supported current DSI spec.\n",
-                       data_id);
-
-               mutex_unlock(&dsim->lock);
-               return -EINVAL;
-       }
-
-       if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp,
-                               MIPI_FIFO_TIMEOUT)) {
-               pr_err("RX done interrupt timeout\n");
-               mutex_unlock(&dsim->lock);
-               return 0;
-       }
-
-       msleep(20);
-
-       rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim);
-
-       if ((u8)(rx_data & 0xff) != response) {
-               printk(KERN_ERR
-                       "mipi dsi wrong response rx_data : %x, response:%x\n",
-                       rx_data, response);
-               goto clear_rx_fifo;
-       }
-
-       if (req_size <= 2) {
-               /* for short packet */
-               for (i = 0; i < req_size; i++)
-                       rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff;
-               rxsize = req_size;
-       } else {
-               /* for long packet */
-               rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data,
-                                                       rx_buf);
-               if (rxsize != req_size)
-                       goto clear_rx_fifo;
-       }
-
-       rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-
-       msleep(20);
-
-       if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) {
-               dev_info(dsim->dev,
-                       "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt);
-               goto clear_rx_fifo;
-       }
-
-       mutex_unlock(&dsim->lock);
-
-       return rxsize;
-
-clear_rx_fifo:
-       i = 0;
-       while (1) {
-               rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
-               if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE)
-                               || (i > MIPI_MAX_RX_FIFO))
-                       break;
-               dev_dbg(dsim->dev,
-                               "mipi dsi clear rx fifo : %08x\n", rcv_pkt);
-               i++;
-       }
-       dev_info(dsim->dev,
-               "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt);
-
-       mutex_unlock(&dsim->lock);
-
-       return 0;
-}
-
-static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim,
-                               unsigned int enable)
-{
-       int sw_timeout;
-
-       if (enable) {
-               sw_timeout = 1000;
-
-               exynos_mipi_dsi_enable_pll(dsim, 1);
-               while (1) {
-                       sw_timeout--;
-                       if (exynos_mipi_dsi_is_pll_stable(dsim))
-                               return 0;
-                       if (sw_timeout == 0)
-                               return -EINVAL;
-               }
-       } else
-               exynos_mipi_dsi_enable_pll(dsim, 0);
-
-       return 0;
-}
-
-static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
-       unsigned int pre_divider, unsigned int main_divider,
-       unsigned int scaler)
-{
-       unsigned long dfin_pll, dfvco, dpll_out;
-       unsigned int i, freq_band = 0xf;
-
-       dfin_pll = (FIN_HZ / pre_divider);
-
-       /******************************************************
-        *      Serial Clock(=ByteClk X 8)      FreqBand[3:0] *
-        ******************************************************
-        *      ~ 99.99 MHz                     0000
-        *      100 ~ 119.99 MHz                0001
-        *      120 ~ 159.99 MHz                0010
-        *      160 ~ 199.99 MHz                0011
-        *      200 ~ 239.99 MHz                0100
-        *      140 ~ 319.99 MHz                0101
-        *      320 ~ 389.99 MHz                0110
-        *      390 ~ 449.99 MHz                0111
-        *      450 ~ 509.99 MHz                1000
-        *      510 ~ 559.99 MHz                1001
-        *      560 ~ 639.99 MHz                1010
-        *      640 ~ 689.99 MHz                1011
-        *      690 ~ 769.99 MHz                1100
-        *      770 ~ 869.99 MHz                1101
-        *      870 ~ 949.99 MHz                1110
-        *      950 ~ 1000 MHz                  1111
-        ******************************************************/
-       if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
-               dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n");
-               exynos_mipi_dsi_enable_afc(dsim, 0, 0);
-       } else {
-               if (dfin_pll < 7 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
-               else if (dfin_pll < 8 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
-               else if (dfin_pll < 9 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
-               else if (dfin_pll < 10 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
-               else if (dfin_pll < 11 * MHZ)
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
-               else
-                       exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
-       }
-
-       dfvco = dfin_pll * main_divider;
-       dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
-                               dfvco, dfin_pll, main_divider);
-       if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
-               dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n");
-
-       dpll_out = dfvco / (1 << scaler);
-       dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n",
-               dpll_out, dfvco, scaler);
-
-       for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
-               if (dpll_out < dpll_table[i] * MHZ) {
-                       freq_band = i;
-                       break;
-               }
-       }
-
-       dev_dbg(dsim->dev, "freq_band = %d\n", freq_band);
-
-       exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
-
-       exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
-       exynos_mipi_dsi_prep_ctrl(dsim, 0);
-
-       /* Freq Band */
-       exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
-
-       /* Stable time */
-       exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time);
-
-       /* Enable PLL */
-       dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n",
-               (dpll_out / MHZ));
-
-       return dpll_out;
-}
-
-static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
-       unsigned int byte_clk_sel, unsigned int enable)
-{
-       unsigned int esc_div;
-       unsigned long esc_clk_error_rate;
-       unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
-
-       if (enable) {
-               dsim->e_clk_src = byte_clk_sel;
-
-               /* Escape mode clock and byte clock source */
-               exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
-
-               /* DPHY, DSIM Link : D-PHY clock out */
-               if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
-                       hs_clk = exynos_mipi_dsi_change_pll(dsim,
-                               dsim->dsim_config->p, dsim->dsim_config->m,
-                               dsim->dsim_config->s);
-                       if (hs_clk == 0) {
-                               dev_err(dsim->dev,
-                                       "failed to get hs clock.\n");
-                               return -EINVAL;
-                       }
-
-                       byte_clk = hs_clk / 8;
-                       exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
-                       exynos_mipi_dsi_pll_on(dsim, 1);
-               /* DPHY : D-PHY clock out, DSIM link : external clock out */
-               } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) {
-                       dev_warn(dsim->dev, "this project is not support\n");
-                       dev_warn(dsim->dev,
-                               "external clock source for MIPI DSIM.\n");
-               } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) {
-                       dev_warn(dsim->dev, "this project is not support\n");
-                       dev_warn(dsim->dev,
-                               "external clock source for MIPI DSIM\n");
-               }
-
-               /* escape clock divider */
-               esc_div = byte_clk / (dsim->dsim_config->esc_clk);
-               dev_dbg(dsim->dev,
-                       "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
-                       esc_div, byte_clk, dsim->dsim_config->esc_clk);
-               if ((byte_clk / esc_div) >= (20 * MHZ) ||
-                               (byte_clk / esc_div) >
-                                       dsim->dsim_config->esc_clk)
-                       esc_div += 1;
-
-               escape_clk = byte_clk / esc_div;
-               dev_dbg(dsim->dev,
-                       "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
-                       escape_clk, byte_clk, esc_div);
-
-               /* enable escape clock. */
-               exynos_mipi_dsi_enable_byte_clock(dsim, 1);
-
-               /* enable byte clk and escape clock */
-               exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
-               /* escape clock on lane */
-               exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
-                       (DSIM_LANE_CLOCK | dsim->data_lane), 1);
-
-               dev_dbg(dsim->dev, "byte clock is %luMHz\n",
-                       (byte_clk / MHZ));
-               dev_dbg(dsim->dev, "escape clock that user's need is %lu\n",
-                       (dsim->dsim_config->esc_clk / MHZ));
-               dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div);
-               dev_dbg(dsim->dev, "escape clock is %luMHz\n",
-                       ((byte_clk / esc_div) / MHZ));
-
-               if ((byte_clk / esc_div) > escape_clk) {
-                       esc_clk_error_rate = escape_clk /
-                               (byte_clk / esc_div);
-                       dev_warn(dsim->dev, "error rate is %lu over.\n",
-                               (esc_clk_error_rate / 100));
-               } else if ((byte_clk / esc_div) < (escape_clk)) {
-                       esc_clk_error_rate = (byte_clk / esc_div) /
-                               escape_clk;
-                       dev_warn(dsim->dev, "error rate is %lu under.\n",
-                               (esc_clk_error_rate / 100));
-               }
-       } else {
-               exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
-                       (DSIM_LANE_CLOCK | dsim->data_lane), 0);
-               exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
-
-               /* disable escape clock. */
-               exynos_mipi_dsi_enable_byte_clock(dsim, 0);
-
-               if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
-                       exynos_mipi_dsi_pll_on(dsim, 0);
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
-{
-       dsim->state = DSIM_STATE_INIT;
-
-       switch (dsim->dsim_config->e_no_data_lane) {
-       case DSIM_DATA_LANE_1:
-               dsim->data_lane = DSIM_LANE_DATA0;
-               break;
-       case DSIM_DATA_LANE_2:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
-               break;
-       case DSIM_DATA_LANE_3:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
-                       DSIM_LANE_DATA2;
-               break;
-       case DSIM_DATA_LANE_4:
-               dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
-                       DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
-               break;
-       default:
-               dev_info(dsim->dev, "data lane is invalid.\n");
-               return -EINVAL;
-       }
-
-       exynos_mipi_dsi_sw_reset(dsim);
-       exynos_mipi_dsi_func_reset(dsim);
-
-       exynos_mipi_dsi_dp_dn_swap(dsim, 0);
-
-       return 0;
-}
-
-void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim)
-{
-       unsigned int src = 0;
-
-       src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE);
-       exynos_mipi_dsi_set_interrupt(dsim, src, 1);
-
-       src = 0;
-       src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY);
-       exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1);
-}
-
-int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       /* enable only frame done interrupt */
-       exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
-
-       return 0;
-}
-
-void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-
-       /* consider Main display and Sub display. */
-
-       exynos_mipi_dsi_set_main_stand_by(dsim, enable);
-}
-
-int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
-       struct mipi_dsim_config *dsim_config)
-{
-       struct mipi_dsim_platform_data *dsim_pd;
-       struct fb_videomode *timing;
-
-       dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
-       timing = (struct fb_videomode *)dsim_pd->lcd_panel_info;
-
-       /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
-       if (dsim_config->e_interface == (u32) DSIM_VIDEO) {
-               if (dsim_config->auto_vertical_cnt == 0) {
-                       exynos_mipi_dsi_set_main_disp_vporch(dsim,
-                               dsim_config->cmd_allow,
-                               timing->lower_margin,
-                               timing->upper_margin);
-                       exynos_mipi_dsi_set_main_disp_hporch(dsim,
-                               timing->right_margin,
-                               timing->left_margin);
-                       exynos_mipi_dsi_set_main_disp_sync_area(dsim,
-                               timing->vsync_len,
-                               timing->hsync_len);
-               }
-       }
-
-       exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres,
-                       timing->yres);
-
-       exynos_mipi_dsi_display_config(dsim, dsim_config);
-
-       dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n",
-                       timing->xres, timing->yres);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
-{
-       unsigned int time_out = 100;
-
-       switch (dsim->state) {
-       case DSIM_STATE_INIT:
-               exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
-
-               /* dsi configuration */
-               exynos_mipi_dsi_init_config(dsim);
-               exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
-               exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
-
-               /* set clock configuration */
-               exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1);
-
-               /* check clock and data lane state are stop state */
-               while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
-                       time_out--;
-                       if (time_out == 0) {
-                               dev_err(dsim->dev,
-                                       "DSI Master is not stop state.\n");
-                               dev_err(dsim->dev,
-                                       "Check initialization process\n");
-
-                               return -EINVAL;
-                       }
-               }
-               if (time_out != 0) {
-                       dev_info(dsim->dev,
-                               "DSI Master driver has been completed.\n");
-                       dev_info(dsim->dev, "DSI Master state is stop state\n");
-               }
-
-               dsim->state = DSIM_STATE_STOP;
-
-               /* BTA sequence counters */
-               exynos_mipi_dsi_set_stop_state_counter(dsim,
-                       dsim->dsim_config->stop_holding_cnt);
-               exynos_mipi_dsi_set_bta_timeout(dsim,
-                       dsim->dsim_config->bta_timeout);
-               exynos_mipi_dsi_set_lpdr_timeout(dsim,
-                       dsim->dsim_config->rx_timeout);
-
-               return 0;
-       default:
-               dev_info(dsim->dev, "DSI Master is already init.\n");
-               return 0;
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
-{
-       if (dsim->state != DSIM_STATE_STOP) {
-               dev_warn(dsim->dev, "DSIM is not in stop state.\n");
-               return 0;
-       }
-
-       if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) {
-               dev_warn(dsim->dev, "clock source is external bypass.\n");
-               return 0;
-       }
-
-       dsim->state = DSIM_STATE_HSCLKEN;
-
-        /* set LCDC and CPU transfer mode to HS. */
-       exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
-       exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
-       exynos_mipi_dsi_enable_hs_clock(dsim, 1);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int mode)
-{
-       if (mode) {
-               if (dsim->state != DSIM_STATE_HSCLKEN) {
-                       dev_err(dsim->dev, "HS Clock lane is not enabled.\n");
-                       return -EINVAL;
-               }
-
-               exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
-       } else {
-               if (dsim->state == DSIM_STATE_INIT || dsim->state ==
-                       DSIM_STATE_ULPS) {
-                       dev_err(dsim->dev,
-                               "DSI Master is not STOP or HSDT state.\n");
-                       return -EINVAL;
-               }
-
-               exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
-       }
-
-       return 0;
-}
-
-int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
-{
-       return _exynos_mipi_dsi_get_frame_done_status(dsim);
-}
-
-int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
-{
-       _exynos_mipi_dsi_clear_frame_done(dsim);
-
-       return 0;
-}
-
-int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
-                               unsigned int val)
-{
-       int try = TRY_FIFO_CLEAR;
-
-       exynos_mipi_dsi_sw_reset_release(dsim);
-       exynos_mipi_dsi_func_reset(dsim);
-
-       do {
-               if (exynos_mipi_dsi_get_sw_reset_release(dsim)) {
-                       exynos_mipi_dsi_init_interrupt(dsim);
-                       dev_dbg(dsim->dev, "reset release done.\n");
-                       return 0;
-               }
-       } while (--try);
-
-       dev_err(dsim->dev, "failed to clear dsim fifo.\n");
-       return -EAGAIN;
-}
-
-MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC MIPI-DSI common driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_common.h
deleted file mode 100644 (file)
index 4125522..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/* linux/drivers/video/exynos_mipi_dsi_common.h
- *
- * Header file for Samsung SoC MIPI-DSI common driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef _EXYNOS_MIPI_DSI_COMMON_H
-#define _EXYNOS_MIPI_DSI_COMMON_H
-
-static DECLARE_COMPLETION(dsim_rd_comp);
-static DECLARE_COMPLETION(dsim_wr_comp);
-
-int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       const unsigned char *data0, unsigned int data_size);
-int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
-       unsigned int data0, unsigned int req_size, u8 *rx_buf);
-irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id);
-void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable);
-int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
-                       struct mipi_dsim_config *dsim_info);
-int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int mode);
-int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
-       unsigned int enable);
-int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
-
-extern struct fb_info *registered_fb[FB_MAX] __read_mostly;
-
-int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
-                               unsigned int val);
-
-#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.c
deleted file mode 100644 (file)
index c148d06..0000000
+++ /dev/null
@@ -1,618 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c
- *
- * Samsung SoC MIPI-DSI lowlevel driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/ctype.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-#include <video/exynos_mipi_dsim.h>
-
-#include "exynos_mipi_dsi_regs.h"
-#include "exynos_mipi_dsi_lowlevel.h"
-
-void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
-
-       reg |= DSIM_FUNCRST;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
-}
-
-void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
-
-       reg |= DSIM_SWRST;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
-}
-
-void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       reg |= INTSRC_SW_RST_RELEASE;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim)
-{
-       return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) &
-                       INTSRC_SW_RST_RELEASE;
-}
-
-unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK);
-
-       return reg;
-}
-
-void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
-               unsigned int mode, unsigned int mask)
-{
-       unsigned int reg = 0;
-
-       if (mask)
-               reg |= mode;
-       else
-               reg &= ~mode;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK);
-}
-
-void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
-               unsigned int cfg)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-
-       writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-       mdelay(10);
-       reg |= cfg;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
-}
-
-/*
- * this function set PLL P, M and S value in D-PHY
- */
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-               unsigned int value)
-{
-       writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-}
-
-void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-
-       reg &= ~DSIM_MAIN_STAND_BY;
-
-       if (enable)
-               reg |= DSIM_MAIN_STAND_BY;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-}
-
-void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
-       unsigned int width_resol, unsigned int height_resol)
-{
-       unsigned int reg;
-
-       /* standby should be set after configuration so set to not ready*/
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) &
-               ~(DSIM_MAIN_STAND_BY);
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-
-       reg &= ~((0x7ff << 16) | (0x7ff << 0));
-       reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
-
-       reg |= DSIM_MAIN_STAND_BY;
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
-}
-
-void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
-       unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) &
-               ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
-               (DSIM_MAIN_VBP_MASK));
-
-       reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) |
-               DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) |
-               DSIM_MAIN_VBP_SHIFT(vback & 0x7ff));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH);
-}
-
-void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
-       unsigned int front, unsigned int back)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) &
-               ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
-
-       reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH);
-}
-
-void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
-       unsigned int vert, unsigned int hori)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) &
-               ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
-
-       reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) |
-               DSIM_MAIN_HSA_SHIFT(hori));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC);
-}
-
-void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
-       unsigned int vert, unsigned int hori)
-{
-       unsigned int reg;
-
-       reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) &
-               ~(DSIM_SUB_STANDY_MASK);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-
-       reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
-       reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) |
-               DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff));
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-
-       reg |= DSIM_SUB_STANDY_SHIFT(1);
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
-}
-
-void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
-{
-       struct mipi_dsim_config *dsim_config = dsim->dsim_config;
-
-       unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
-               ~((1 << 28) | (0x1f << 20) | (0x3 << 5));
-
-       cfg =   ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) |
-               (DSIM_EOT_DISABLE(dsim_config->eot_disable)) |
-               (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) |
-               (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) |
-               (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) |
-               (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) |
-               (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) |
-               (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane)));
-
-       writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
-                               struct mipi_dsim_config *dsim_config)
-{
-       u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
-               ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) |
-               (0x3 << 16) | (0x7 << 8));
-
-       if (dsim_config->e_interface == DSIM_VIDEO)
-               reg |= (1 << 25);
-       else if (dsim_config->e_interface == DSIM_COMMAND)
-               reg &= ~(1 << 25);
-       else {
-               dev_err(dsim->dev, "unknown lcd type.\n");
-               return;
-       }
-
-       /* main lcd */
-       reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 |
-               ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 |
-               ((u8) (dsim_config->e_pixel_format) & 0x7) << 12;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
-       unsigned int enable)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG);
-
-       if (enable)
-               reg |= DSIM_LANE_ENx(lane);
-       else
-               reg &= ~DSIM_LANE_ENx(lane);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-       unsigned int count)
-{
-       unsigned int cfg;
-
-       /* get the data lane number. */
-       cfg = DSIM_NUM_OF_DATALANE_SHIFT(count);
-
-       writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
-}
-
-void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
-       unsigned int afc_code)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-
-       if (enable) {
-               reg |= (1 << 14);
-               reg &= ~(0x7 << 5);
-               reg |= (afc_code & 0x7) << 5;
-       } else
-               reg &= ~(1 << 14);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
-}
-
-void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_PLL_BYPASS_SHIFT(0x1));
-
-       reg |= DSIM_PLL_BYPASS_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
-       unsigned int m, unsigned int s)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-
-       reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
-               unsigned int freq_band)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(DSIM_FREQ_BAND_SHIFT(0x1f));
-
-       reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
-               unsigned int pre_divider, unsigned int main_divider,
-               unsigned int scaler)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0x7ffff << 1);
-
-       reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 |
-               (scaler & 0x7) << 1;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
-       unsigned int lock_time)
-{
-       writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR);
-}
-
-void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(DSIM_PLL_EN_SHIFT(0x1));
-
-       reg |= DSIM_PLL_EN_SHIFT(enable & 0x1);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
-               unsigned int src)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3));
-
-       reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_BYTE_CLKEN_SHIFT(0x1));
-
-       reg |= DSIM_BYTE_CLKEN_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
-               unsigned int enable, unsigned int prs_val)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff);
-
-       reg |= DSIM_ESC_CLKEN_SHIFT(enable);
-       if (enable)
-               reg |= prs_val;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
-               unsigned int lane_sel, unsigned int enable)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-
-       if (enable)
-               reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
-       else
-
-               reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
-       unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
-               ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1));
-
-       reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
-
-       /**
-        * check clock and data lane states.
-        * if MIPI-DSI controller was enabled at bootloader then
-        * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
-        * so it should be checked for two case.
-        */
-       if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
-                       ((reg & DSIM_STOP_STATE_CLK) ||
-                        (reg & DSIM_TX_READY_HS_CLK)))
-               return 1;
-
-       return 0;
-}
-
-void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
-               unsigned int cnt_val)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
-               ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff));
-
-       reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
-               unsigned int timeout)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
-               ~(DSIM_BTA_TOUT_SHIFT(0xff));
-
-       reg |= (DSIM_BTA_TOUT_SHIFT(timeout));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
-}
-
-void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
-               unsigned int timeout)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
-               ~(DSIM_LPDR_TOUT_SHIFT(0xffff));
-
-       reg |= (DSIM_LPDR_TOUT_SHIFT(timeout));
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
-}
-
-void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int lp)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-
-       reg &= ~DSIM_CMD_LPDT_LP;
-
-       if (lp)
-               reg |= DSIM_CMD_LPDT_LP;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
-               unsigned int lp)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-
-       reg &= ~DSIM_TX_LPDT_LP;
-
-       if (lp)
-               reg |= DSIM_TX_LPDT_LP;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
-}
-
-void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
-               unsigned int enable)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
-               ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1));
-
-       reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
-}
-
-void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
-               unsigned int swap_en)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
-
-       reg &= ~(0x3 << 0);
-       reg |= (swap_en & 0x3) << 0;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
-}
-
-void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
-               unsigned int hs_zero)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0xf << 28);
-
-       reg |= ((hs_zero & 0xf) << 28);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
-{
-       unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
-               ~(0x7 << 20);
-
-       reg |= ((prep & 0x7) << 20);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
-}
-
-unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       reg |= src;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src, unsigned int enable)
-{
-       unsigned int reg = 0;
-
-       if (enable)
-               reg |= src;
-       else
-               reg &= ~src;
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
-}
-
-unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg;
-
-       reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
-
-       return reg & (1 << 31) ? 1 : 0;
-}
-
-unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f);
-}
-
-void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
-       unsigned int di, unsigned int data0, unsigned int data1)
-{
-       unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
-}
-
-void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
-       unsigned int di, unsigned int data0)
-{
-       unsigned int reg = (data0 << 8) | (di << 0);
-
-       writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
-}
-
-unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim)
-{
-       return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO);
-}
-
-unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
-}
-
-void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
-{
-       unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
-
-       writel(reg | INTSRC_FRAME_DONE, dsim->reg_base +
-               EXYNOS_DSIM_INTSRC);
-}
-
-void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
-               unsigned int tx_data)
-{
-       writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD);
-}
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_lowlevel.h
deleted file mode 100644 (file)
index 8546070..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h
- *
- * Header file for Samsung SoC MIPI-DSI lowlevel driver.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H
-#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
-
-void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim);
-int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
-       unsigned int mode, unsigned int mask);
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-                                       unsigned int count);
-void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
-                                       unsigned int cfg);
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-                               unsigned int value);
-void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
-                               unsigned int value);
-void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
-               unsigned int enable);
-void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
-               unsigned int width_resol, unsigned int height_resol);
-void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
-       unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
-void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
-                       unsigned int front, unsigned int back);
-void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
-                               unsigned int vert, unsigned int hori);
-void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
-                               unsigned int vert, unsigned int hori);
-void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
-                               struct mipi_dsim_config *dsim_config);
-void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
-                               unsigned int count);
-void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
-                               unsigned int enable);
-void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
-                               unsigned int afc_code);
-void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
-                               unsigned int m, unsigned int s);
-void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
-                               unsigned int freq_band);
-void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
-                       unsigned int pre_divider, unsigned int main_divider,
-                       unsigned int scaler);
-void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
-                       unsigned int lock_time);
-void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
-                                       unsigned int enable);
-void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
-                                       unsigned int src);
-void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
-                                       unsigned int enable);
-void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
-                               unsigned int enable, unsigned int prs_val);
-void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
-                               unsigned int lane_sel, unsigned int enable);
-void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
-                               unsigned int cnt_val);
-void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
-                               unsigned int timeout);
-void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
-                               unsigned int timeout);
-void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
-                                       unsigned int lp);
-void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
-                                       unsigned int lp);
-void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
-                               unsigned int enable);
-void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
-                               unsigned int swap_en);
-void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
-                               unsigned int hs_zero);
-void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep);
-unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim);
-unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src);
-void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
-                                       unsigned int src, unsigned int enable);
-unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
-unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
-unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
-void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
-void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di,
-                               unsigned int data0, unsigned int data1);
-void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
-               unsigned int tx_data);
-void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
-               unsigned int data0, unsigned int data1);
-unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim);
-
-#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
diff --git a/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h b/drivers/video/fbdev/exynos/exynos_mipi_dsi_regs.h
deleted file mode 100644 (file)
index 4227106..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h
- *
- * Register definition file for Samsung MIPI-DSIM driver
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef _EXYNOS_MIPI_DSI_REGS_H
-#define _EXYNOS_MIPI_DSI_REGS_H
-
-#define EXYNOS_DSIM_STATUS             0x0     /* Status register */
-#define EXYNOS_DSIM_SWRST              0x4     /* Software reset register */
-#define EXYNOS_DSIM_CLKCTRL            0x8     /* Clock control register */
-#define EXYNOS_DSIM_TIMEOUT            0xc     /* Time out register */
-#define EXYNOS_DSIM_CONFIG             0x10    /* Configuration register */
-#define EXYNOS_DSIM_ESCMODE            0x14    /* Escape mode register */
-
-/* Main display image resolution register */
-#define EXYNOS_DSIM_MDRESOL            0x18
-#define EXYNOS_DSIM_MVPORCH            0x1c    /* Main display Vporch register */
-#define EXYNOS_DSIM_MHPORCH            0x20    /* Main display Hporch register */
-#define EXYNOS_DSIM_MSYNC              0x24    /* Main display sync area register */
-
-/* Sub display image resolution register */
-#define EXYNOS_DSIM_SDRESOL            0x28
-#define EXYNOS_DSIM_INTSRC             0x2c    /* Interrupt source register */
-#define EXYNOS_DSIM_INTMSK             0x30    /* Interrupt mask register */
-#define EXYNOS_DSIM_PKTHDR             0x34    /* Packet Header FIFO register */
-#define EXYNOS_DSIM_PAYLOAD            0x38    /* Payload FIFO register */
-#define EXYNOS_DSIM_RXFIFO             0x3c    /* Read FIFO register */
-#define EXYNOS_DSIM_FIFOTHLD           0x40    /* FIFO threshold level register */
-#define EXYNOS_DSIM_FIFOCTRL           0x44    /* FIFO status and control register */
-
-/* FIFO memory AC characteristic register */
-#define EXYNOS_DSIM_PLLCTRL            0x4c    /* PLL control register */
-#define EXYNOS_DSIM_PLLTMR             0x50    /* PLL timer register */
-#define EXYNOS_DSIM_PHYACCHR           0x54    /* D-PHY AC characteristic register */
-#define EXYNOS_DSIM_PHYACCHR1          0x58    /* D-PHY AC characteristic register1 */
-
-/* DSIM_STATUS */
-#define DSIM_STOP_STATE_DAT(x)         (((x) & 0xf) << 0)
-#define DSIM_STOP_STATE_CLK            (1 << 8)
-#define DSIM_TX_READY_HS_CLK           (1 << 10)
-
-/* DSIM_SWRST */
-#define DSIM_FUNCRST                   (1 << 16)
-#define DSIM_SWRST                     (1 << 0)
-
-/* EXYNOS_DSIM_TIMEOUT */
-#define DSIM_LPDR_TOUT_SHIFT(x)                ((x) << 0)
-#define DSIM_BTA_TOUT_SHIFT(x)         ((x) << 16)
-
-/* EXYNOS_DSIM_CLKCTRL */
-#define DSIM_LANE_ESC_CLKEN(x)         (((x) & 0x1f) << 19)
-#define DSIM_BYTE_CLKEN_SHIFT(x)       ((x) << 24)
-#define DSIM_BYTE_CLK_SRC_SHIFT(x)     ((x) << 25)
-#define DSIM_PLL_BYPASS_SHIFT(x)       ((x) << 27)
-#define DSIM_ESC_CLKEN_SHIFT(x)                ((x) << 28)
-#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31)
-
-/* EXYNOS_DSIM_CONFIG */
-#define DSIM_LANE_ENx(x)               (((x) & 0x1f) << 0)
-#define DSIM_NUM_OF_DATALANE_SHIFT(x)  ((x) << 5)
-#define DSIM_HSA_MODE_SHIFT(x)         ((x) << 20)
-#define DSIM_HBP_MODE_SHIFT(x)         ((x) << 21)
-#define DSIM_HFP_MODE_SHIFT(x)         ((x) << 22)
-#define DSIM_HSE_MODE_SHIFT(x)         ((x) << 23)
-#define DSIM_AUTO_MODE_SHIFT(x)                ((x) << 24)
-#define DSIM_EOT_DISABLE(x)            ((x) << 28)
-#define DSIM_AUTO_FLUSH(x)             ((x) << 29)
-
-#define DSIM_NUM_OF_DATA_LANE(x)       ((x) << DSIM_NUM_OF_DATALANE_SHIFT)
-
-/* EXYNOS_DSIM_ESCMODE */
-#define DSIM_TX_LPDT_LP                        (1 << 6)
-#define DSIM_CMD_LPDT_LP               (1 << 7)
-#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20)
-#define DSIM_STOP_STATE_CNT_SHIFT(x)   ((x) << 21)
-
-/* EXYNOS_DSIM_MDRESOL */
-#define DSIM_MAIN_STAND_BY             (1 << 31)
-#define DSIM_MAIN_VRESOL(x)            (((x) & 0x7ff) << 16)
-#define DSIM_MAIN_HRESOL(x)            (((x) & 0X7ff) << 0)
-
-/* EXYNOS_DSIM_MVPORCH */
-#define DSIM_CMD_ALLOW_SHIFT(x)                ((x) << 28)
-#define DSIM_STABLE_VFP_SHIFT(x)       ((x) << 16)
-#define DSIM_MAIN_VBP_SHIFT(x)         ((x) << 0)
-#define DSIM_CMD_ALLOW_MASK            (0xf << 28)
-#define DSIM_STABLE_VFP_MASK           (0x7ff << 16)
-#define DSIM_MAIN_VBP_MASK             (0x7ff << 0)
-
-/* EXYNOS_DSIM_MHPORCH */
-#define DSIM_MAIN_HFP_SHIFT(x)         ((x) << 16)
-#define DSIM_MAIN_HBP_SHIFT(x)         ((x) << 0)
-#define DSIM_MAIN_HFP_MASK             ((0xffff) << 16)
-#define DSIM_MAIN_HBP_MASK             ((0xffff) << 0)
-
-/* EXYNOS_DSIM_MSYNC */
-#define DSIM_MAIN_VSA_SHIFT(x)         ((x) << 22)
-#define DSIM_MAIN_HSA_SHIFT(x)         ((x) << 0)
-#define DSIM_MAIN_VSA_MASK             ((0x3ff) << 22)
-#define DSIM_MAIN_HSA_MASK             ((0xffff) << 0)
-
-/* EXYNOS_DSIM_SDRESOL */
-#define DSIM_SUB_STANDY_SHIFT(x)       ((x) << 31)
-#define DSIM_SUB_VRESOL_SHIFT(x)       ((x) << 16)
-#define DSIM_SUB_HRESOL_SHIFT(x)       ((x) << 0)
-#define DSIM_SUB_STANDY_MASK           ((0x1) << 31)
-#define DSIM_SUB_VRESOL_MASK           ((0x7ff) << 16)
-#define DSIM_SUB_HRESOL_MASK           ((0x7ff) << 0)
-
-/* EXYNOS_DSIM_INTSRC */
-#define INTSRC_PLL_STABLE              (1 << 31)
-#define INTSRC_SW_RST_RELEASE          (1 << 30)
-#define INTSRC_SFR_FIFO_EMPTY          (1 << 29)
-#define INTSRC_FRAME_DONE              (1 << 24)
-#define INTSRC_RX_DATA_DONE            (1 << 18)
-
-/* EXYNOS_DSIM_INTMSK */
-#define INTMSK_FIFO_EMPTY              (1 << 29)
-#define INTMSK_BTA                     (1 << 25)
-#define INTMSK_FRAME_DONE              (1 << 24)
-#define INTMSK_RX_TIMEOUT              (1 << 21)
-#define INTMSK_BTA_TIMEOUT             (1 << 20)
-#define INTMSK_RX_DONE                 (1 << 18)
-#define INTMSK_RX_TE                   (1 << 17)
-#define INTMSK_RX_ACK                  (1 << 16)
-#define INTMSK_RX_ECC_ERR              (1 << 15)
-#define INTMSK_RX_CRC_ERR              (1 << 14)
-
-/* EXYNOS_DSIM_FIFOCTRL */
-#define SFR_HEADER_EMPTY               (1 << 22)
-
-/* EXYNOS_DSIM_PHYACCHR */
-#define DSIM_AFC_CTL(x)                        (((x) & 0x7) << 5)
-
-/* EXYNOS_DSIM_PLLCTRL */
-#define DSIM_PLL_EN_SHIFT(x)           ((x) << 23)
-#define DSIM_FREQ_BAND_SHIFT(x)                ((x) << 24)
-
-#endif /* _EXYNOS_MIPI_DSI_REGS_H */
diff --git a/drivers/video/fbdev/exynos/s6e8ax0.c b/drivers/video/fbdev/exynos/s6e8ax0.c
deleted file mode 100644 (file)
index de2f3e7..0000000
+++ /dev/null
@@ -1,887 +0,0 @@
-/* linux/drivers/video/exynos/s6e8ax0.c
- *
- * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
- *
- * Inki Dae, <inki.dae@samsung.com>
- * Donghwa Lee, <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/mutex.h>
-#include <linux/wait.h>
-#include <linux/ctype.h>
-#include <linux/io.h>
-#include <linux/delay.h>
-#include <linux/irq.h>
-#include <linux/interrupt.h>
-#include <linux/lcd.h>
-#include <linux/fb.h>
-#include <linux/backlight.h>
-#include <linux/regulator/consumer.h>
-
-#include <video/mipi_display.h>
-#include <video/exynos_mipi_dsim.h>
-
-#define LDI_MTP_LENGTH         24
-#define DSIM_PM_STABLE_TIME    10
-#define MIN_BRIGHTNESS         0
-#define MAX_BRIGHTNESS         24
-#define GAMMA_TABLE_COUNT      26
-
-#define POWER_IS_ON(pwr)       ((pwr) == FB_BLANK_UNBLANK)
-#define POWER_IS_OFF(pwr)      ((pwr) == FB_BLANK_POWERDOWN)
-#define POWER_IS_NRM(pwr)      ((pwr) == FB_BLANK_NORMAL)
-
-#define lcd_to_master(a)       (a->dsim_dev->master)
-#define lcd_to_master_ops(a)   ((lcd_to_master(a))->master_ops)
-
-enum {
-       DSIM_NONE_STATE = 0,
-       DSIM_RESUME_COMPLETE = 1,
-       DSIM_FRAME_DONE = 2,
-};
-
-struct s6e8ax0 {
-       struct device   *dev;
-       unsigned int                    power;
-       unsigned int                    id;
-       unsigned int                    gamma;
-       unsigned int                    acl_enable;
-       unsigned int                    cur_acl;
-
-       struct lcd_device       *ld;
-       struct backlight_device *bd;
-
-       struct mipi_dsim_lcd_device     *dsim_dev;
-       struct lcd_platform_data        *ddi_pd;
-       struct mutex                    lock;
-       bool  enabled;
-};
-
-
-static struct regulator_bulk_data supplies[] = {
-       { .supply = "vdd3", },
-       { .supply = "vci", },
-};
-
-static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd)
-{
-       int ret = 0;
-       struct lcd_platform_data *pd = NULL;
-
-       pd = lcd->ddi_pd;
-       mutex_lock(&lcd->lock);
-       if (!lcd->enabled) {
-               ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
-               if (ret)
-                       goto out;
-
-               lcd->enabled = true;
-       }
-       msleep(pd->power_on_delay);
-out:
-       mutex_unlock(&lcd->lock);
-}
-
-static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd)
-{
-       int ret = 0;
-
-       mutex_lock(&lcd->lock);
-       if (lcd->enabled) {
-               ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
-               if (ret)
-                       goto out;
-
-               lcd->enabled = false;
-       }
-out:
-       mutex_unlock(&lcd->lock);
-}
-
-static const unsigned char s6e8ax0_22_gamma_30[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
-       0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
-       0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
-};
-
-static const unsigned char s6e8ax0_22_gamma_50[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
-       0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
-       0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
-};
-
-static const unsigned char s6e8ax0_22_gamma_60[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
-       0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
-       0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
-};
-
-static const unsigned char s6e8ax0_22_gamma_70[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
-       0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
-       0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
-};
-
-static const unsigned char s6e8ax0_22_gamma_80[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
-       0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
-       0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
-};
-
-static const unsigned char s6e8ax0_22_gamma_90[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
-       0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
-       0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
-};
-
-static const unsigned char s6e8ax0_22_gamma_100[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
-       0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
-       0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_120[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
-       0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
-       0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
-};
-
-static const unsigned char s6e8ax0_22_gamma_130[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
-       0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
-       0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
-};
-
-static const unsigned char s6e8ax0_22_gamma_140[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
-       0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
-       0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_150[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
-       0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
-       0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
-};
-
-static const unsigned char s6e8ax0_22_gamma_160[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
-       0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
-       0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
-};
-
-static const unsigned char s6e8ax0_22_gamma_170[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
-       0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
-       0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
-};
-
-static const unsigned char s6e8ax0_22_gamma_180[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
-       0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
-       0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
-};
-
-static const unsigned char s6e8ax0_22_gamma_190[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
-       0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
-       0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
-};
-
-static const unsigned char s6e8ax0_22_gamma_200[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
-       0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
-       0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
-};
-
-static const unsigned char s6e8ax0_22_gamma_210[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
-       0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
-       0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
-};
-
-static const unsigned char s6e8ax0_22_gamma_220[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
-       0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
-       0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
-};
-
-static const unsigned char s6e8ax0_22_gamma_230[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
-       0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
-       0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
-};
-
-static const unsigned char s6e8ax0_22_gamma_240[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
-       0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
-       0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
-};
-
-static const unsigned char s6e8ax0_22_gamma_250[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
-       0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
-       0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
-};
-
-static const unsigned char s6e8ax0_22_gamma_260[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
-       0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
-       0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
-};
-
-static const unsigned char s6e8ax0_22_gamma_270[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
-       0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
-       0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
-};
-
-static const unsigned char s6e8ax0_22_gamma_280[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
-       0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
-       0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
-};
-
-static const unsigned char s6e8ax0_22_gamma_300[] = {
-       0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
-       0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
-       0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
-};
-
-static const unsigned char *s6e8ax0_22_gamma_table[] = {
-       s6e8ax0_22_gamma_30,
-       s6e8ax0_22_gamma_50,
-       s6e8ax0_22_gamma_60,
-       s6e8ax0_22_gamma_70,
-       s6e8ax0_22_gamma_80,
-       s6e8ax0_22_gamma_90,
-       s6e8ax0_22_gamma_100,
-       s6e8ax0_22_gamma_120,
-       s6e8ax0_22_gamma_130,
-       s6e8ax0_22_gamma_140,
-       s6e8ax0_22_gamma_150,
-       s6e8ax0_22_gamma_160,
-       s6e8ax0_22_gamma_170,
-       s6e8ax0_22_gamma_180,
-       s6e8ax0_22_gamma_190,
-       s6e8ax0_22_gamma_200,
-       s6e8ax0_22_gamma_210,
-       s6e8ax0_22_gamma_220,
-       s6e8ax0_22_gamma_230,
-       s6e8ax0_22_gamma_240,
-       s6e8ax0_22_gamma_250,
-       s6e8ax0_22_gamma_260,
-       s6e8ax0_22_gamma_270,
-       s6e8ax0_22_gamma_280,
-       s6e8ax0_22_gamma_300,
-};
-
-static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       static const unsigned char data_to_send[] = {
-               0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
-               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
-               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
-               0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
-       };
-       static const unsigned char data_to_send_panel_reverse[] = {
-               0xf8, 0x19, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
-               0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
-               0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
-               0xc1, 0x01, 0x41, 0xc1, 0x00, 0xc1, 0xf6, 0xf6, 0xc1
-       };
-
-       if (lcd->dsim_dev->panel_reverse)
-               ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                               data_to_send_panel_reverse,
-                               ARRAY_SIZE(data_to_send_panel_reverse));
-       else
-               ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf2, 0x80, 0x03, 0x0d
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
-static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       unsigned int gamma = lcd->bd->props.brightness;
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                       s6e8ax0_22_gamma_table[gamma],
-                       GAMMA_TABLE_COUNT);
-}
-
-static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf7, 0x03
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
-               ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
-               0x0d, 0x00, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
-               0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe3, 0x40
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE_PARAM,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xb1, 0x04, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
-               0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
-               0x64, 0xaf
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x10, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x11, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x29, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0x28, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xf0, 0x5a, 0x5a
-       };
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xc0, 0x01
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       static const unsigned char data_to_send[] = {
-               0xc0, 0x00
-       };
-
-       ops->cmd_write(lcd_to_master(lcd),
-               MIPI_DSI_DCS_SHORT_WRITE,
-               data_to_send, ARRAY_SIZE(data_to_send));
-}
-
-/* Full white 50% reducing setting */
-static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       /* Full white 50% reducing setting */
-       static const unsigned char cutoff_50[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
-               0x3f, 0x46
-       };
-       /* Full white 45% reducing setting */
-       static const unsigned char cutoff_45[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
-               0x37, 0x3d
-       };
-       /* Full white 40% reducing setting */
-       static const unsigned char cutoff_40[] = {
-               0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
-               0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
-               0x31, 0x36
-       };
-
-       if (lcd->acl_enable) {
-               if (lcd->cur_acl == 0) {
-                       if (lcd->gamma == 0 || lcd->gamma == 1) {
-                               s6e8ax0_acl_off(lcd);
-                               dev_dbg(&lcd->ld->dev,
-                                       "cur_acl=%d\n", lcd->cur_acl);
-                       } else
-                               s6e8ax0_acl_on(lcd);
-               }
-               switch (lcd->gamma) {
-               case 0: /* 30cd */
-                       s6e8ax0_acl_off(lcd);
-                       lcd->cur_acl = 0;
-                       break;
-               case 1 ... 3: /* 50cd ~ 90cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_40,
-                               ARRAY_SIZE(cutoff_40));
-                       lcd->cur_acl = 40;
-                       break;
-               case 4 ... 7: /* 120cd ~ 210cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_45,
-                               ARRAY_SIZE(cutoff_45));
-                       lcd->cur_acl = 45;
-                       break;
-               case 8 ... 10: /* 220cd ~ 300cd */
-                       ops->cmd_write(lcd_to_master(lcd),
-                               MIPI_DSI_DCS_LONG_WRITE,
-                               cutoff_50,
-                               ARRAY_SIZE(cutoff_50));
-                       lcd->cur_acl = 50;
-                       break;
-               default:
-                       break;
-               }
-       } else {
-               s6e8ax0_acl_off(lcd);
-               lcd->cur_acl = 0;
-               dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
-       }
-}
-
-static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
-{
-       unsigned int ret;
-       unsigned int addr = 0xd1;       /* MTP ID */
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       ret = ops->cmd_read(lcd_to_master(lcd),
-                       MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
-                       addr, 3, mtp_id);
-}
-
-static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
-{
-       s6e8ax0_apply_level2_key(lcd);
-       s6e8ax0_sleep_out(lcd);
-       msleep(1);
-       s6e8ax0_panel_cond(lcd);
-       s6e8ax0_display_cond(lcd);
-       s6e8ax0_gamma_cond(lcd);
-       s6e8ax0_gamma_update(lcd);
-
-       s6e8ax0_etc_cond1(lcd);
-       s6e8ax0_etc_cond2(lcd);
-       s6e8ax0_etc_cond3(lcd);
-       s6e8ax0_etc_cond4(lcd);
-       s6e8ax0_etc_cond5(lcd);
-       s6e8ax0_etc_cond6(lcd);
-       s6e8ax0_etc_cond7(lcd);
-
-       s6e8ax0_elvss_nvm_set(lcd);
-       s6e8ax0_elvss_set(lcd);
-
-       s6e8ax0_acl_ctrl_set(lcd);
-       s6e8ax0_acl_on(lcd);
-
-       /* if ID3 value is not 33h, branch private elvss mode */
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       return 0;
-}
-
-static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
-{
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-
-       ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
-                       s6e8ax0_22_gamma_table[brightness],
-                       ARRAY_SIZE(s6e8ax0_22_gamma_table));
-
-       /* update gamma table. */
-       s6e8ax0_gamma_update(lcd);
-       lcd->gamma = brightness;
-
-       return 0;
-}
-
-static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
-{
-       s6e8ax0_update_gamma_ctrl(lcd, gamma);
-
-       return 0;
-}
-
-static int s6e8ax0_set_power(struct lcd_device *ld, int power)
-{
-       struct s6e8ax0 *lcd = lcd_get_data(ld);
-       struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
-       int ret = 0;
-
-       if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
-                       power != FB_BLANK_NORMAL) {
-               dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
-               return -EINVAL;
-       }
-
-       if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
-               /* LCD power on */
-               if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
-                       || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
-                       ret = ops->set_blank_mode(lcd_to_master(lcd), power);
-                       if (!ret && lcd->power != power)
-                               lcd->power = power;
-               }
-       } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
-               /* LCD power off */
-               if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
-               (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
-                       ret = ops->set_early_blank_mode(lcd_to_master(lcd),
-                                                       power);
-                       if (!ret && lcd->power != power)
-                               lcd->power = power;
-               }
-       }
-
-       return ret;
-}
-
-static int s6e8ax0_get_power(struct lcd_device *ld)
-{
-       struct s6e8ax0 *lcd = lcd_get_data(ld);
-
-       return lcd->power;
-}
-
-static int s6e8ax0_set_brightness(struct backlight_device *bd)
-{
-       int ret = 0, brightness = bd->props.brightness;
-       struct s6e8ax0 *lcd = bl_get_data(bd);
-
-       if (brightness < MIN_BRIGHTNESS ||
-               brightness > bd->props.max_brightness) {
-               dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
-                       MIN_BRIGHTNESS, MAX_BRIGHTNESS);
-               return -EINVAL;
-       }
-
-       ret = s6e8ax0_gamma_ctrl(lcd, brightness);
-       if (ret) {
-               dev_err(&bd->dev, "lcd brightness setting failed.\n");
-               return -EIO;
-       }
-
-       return ret;
-}
-
-static struct lcd_ops s6e8ax0_lcd_ops = {
-       .set_power = s6e8ax0_set_power,
-       .get_power = s6e8ax0_get_power,
-};
-
-static const struct backlight_ops s6e8ax0_backlight_ops = {
-       .update_status = s6e8ax0_set_brightness,
-};
-
-static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       /* lcd power on */
-       if (power)
-               s6e8ax0_regulator_enable(lcd);
-       else
-               s6e8ax0_regulator_disable(lcd);
-
-       msleep(lcd->ddi_pd->reset_delay);
-
-       /* lcd reset */
-       if (lcd->ddi_pd->reset)
-               lcd->ddi_pd->reset(lcd->ld);
-       msleep(5);
-}
-
-static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_panel_init(lcd);
-       s6e8ax0_display_on(lcd);
-
-       lcd->power = FB_BLANK_UNBLANK;
-}
-
-static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd;
-       int ret;
-       u8 mtp_id[3] = {0, };
-
-       lcd = devm_kzalloc(&dsim_dev->dev, sizeof(struct s6e8ax0), GFP_KERNEL);
-       if (!lcd) {
-               dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
-               return -ENOMEM;
-       }
-
-       lcd->dsim_dev = dsim_dev;
-       lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
-       lcd->dev = &dsim_dev->dev;
-
-       mutex_init(&lcd->lock);
-
-       ret = devm_regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
-       if (ret) {
-               dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
-               return ret;
-       }
-
-       lcd->ld = devm_lcd_device_register(lcd->dev, "s6e8ax0", lcd->dev, lcd,
-                       &s6e8ax0_lcd_ops);
-       if (IS_ERR(lcd->ld)) {
-               dev_err(lcd->dev, "failed to register lcd ops.\n");
-               return PTR_ERR(lcd->ld);
-       }
-
-       lcd->bd = devm_backlight_device_register(lcd->dev, "s6e8ax0-bl",
-                               lcd->dev, lcd, &s6e8ax0_backlight_ops, NULL);
-       if (IS_ERR(lcd->bd)) {
-               dev_err(lcd->dev, "failed to register backlight ops.\n");
-               return PTR_ERR(lcd->bd);
-       }
-
-       lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
-       lcd->bd->props.brightness = MAX_BRIGHTNESS;
-
-       s6e8ax0_read_id(lcd, mtp_id);
-       if (mtp_id[0] == 0x00)
-               dev_err(lcd->dev, "read id failed\n");
-
-       dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
-                       mtp_id[0], mtp_id[1], mtp_id[2]);
-
-       if (mtp_id[2] == 0x33)
-               dev_info(lcd->dev,
-                       "ID-3 is 0xff does not support dynamic elvss\n");
-       else
-               dev_info(lcd->dev,
-                       "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
-
-       lcd->acl_enable = 1;
-       lcd->cur_acl = 0;
-
-       dev_set_drvdata(&dsim_dev->dev, lcd);
-
-       dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
-
-       return 0;
-}
-
-static int __maybe_unused s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_sleep_in(lcd);
-       msleep(lcd->ddi_pd->power_off_delay);
-       s6e8ax0_display_off(lcd);
-
-       s6e8ax0_regulator_disable(lcd);
-
-       return 0;
-}
-
-static int __maybe_unused s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
-{
-       struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
-
-       s6e8ax0_sleep_out(lcd);
-       msleep(lcd->ddi_pd->power_on_delay);
-
-       s6e8ax0_regulator_enable(lcd);
-       s6e8ax0_set_sequence(dsim_dev);
-
-       return 0;
-}
-
-static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
-       .name = "s6e8ax0",
-       .id = -1,
-
-       .power_on = s6e8ax0_power_on,
-       .set_sequence = s6e8ax0_set_sequence,
-       .probe = s6e8ax0_probe,
-       .suspend = IS_ENABLED(CONFIG_PM) ? s6e8ax0_suspend : NULL,
-       .resume = IS_ENABLED(CONFIG_PM) ? s6e8ax0_resume : NULL,
-};
-
-static int s6e8ax0_init(void)
-{
-       exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
-
-       return 0;
-}
-
-static void s6e8ax0_exit(void)
-{
-       return;
-}
-
-module_init(s6e8ax0_init);
-module_exit(s6e8ax0_exit);
-
-MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
-MODULE_LICENSE("GPL");
index e4031ef..8577195 100644 (file)
@@ -47,7 +47,7 @@
 #define DPY_W 600
 #define DPY_H 800
 
-static struct fb_fix_screeninfo hecubafb_fix = {
+static const struct fb_fix_screeninfo hecubafb_fix = {
        .id =           "hecubafb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_MONO01,
@@ -58,7 +58,7 @@ static struct fb_fix_screeninfo hecubafb_fix = {
        .accel =        FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo hecubafb_var = {
+static const struct fb_var_screeninfo hecubafb_var = {
        .xres           = DPY_W,
        .yres           = DPY_H,
        .xres_virtual   = DPY_W,
index 15d3ccf..4630285 100644 (file)
@@ -106,7 +106,7 @@ static DEFINE_SPINLOCK(hga_reg_lock);
 
 /* Framebuffer driver structures */
 
-static struct fb_var_screeninfo hga_default_var = {
+static const struct fb_var_screeninfo hga_default_var = {
        .xres           = 720,
        .yres           = 348,
        .xres_virtual   = 720,
index cf5ccd0..7bc5f60 100644 (file)
@@ -82,7 +82,7 @@ struct i740fb_par {
 #define DACSPEED24_SD  128
 #define DACSPEED32     86
 
-static struct fb_fix_screeninfo i740fb_fix = {
+static const struct fb_fix_screeninfo i740fb_fix = {
        .id =           "i740fb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_TRUECOLOR,
index 025b882..483ab25 100644 (file)
@@ -1691,7 +1691,7 @@ static int i810_alloc_agp_mem(struct fb_info *info)
        if (!(par->i810_gtt.i810_cursor_memory = 
              agp_allocate_memory(bridge, par->cursor_heap.size >> 12,
                                  AGP_PHYSICAL_MEMORY))) {
-               printk("i810fb_alloc_cursormem:  can't allocate
+               printk("i810fb_alloc_cursormem:  can't allocate "
                       "cursor memory\n");
                agp_backend_release(bridge);
                return -ENOMEM;
index bf20744..ff2a5d2 100644 (file)
@@ -1301,11 +1301,6 @@ static int intelfb_check_var(struct fb_var_screeninfo *var,
                break;
        }
 
-       if (v.xoffset < 0)
-               v.xoffset = 0;
-       if (v.yoffset < 0)
-               v.yoffset = 0;
-
        if (v.xoffset > v.xres_virtual - v.xres)
                v.xoffset = v.xres_virtual - v.xres;
        if (v.yoffset > v.yres_virtual - v.yres)
index 5bb0153..f77478f 100644 (file)
@@ -44,7 +44,7 @@ static struct fb_fix_screeninfo kyro_fix = {
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo kyro_var = {
+static const struct fb_var_screeninfo kyro_var = {
        /* 640x480, 16bpp @ 60 Hz */
        .xres           = 640,
        .yres           = 480,
index 195ad7c..68fa037 100644 (file)
@@ -372,7 +372,7 @@ static int Ti3026_init(struct matrox_fb_info *minfo, struct my_timming *m)
 
        DBG(__func__)
 
-       memcpy(hw->DACreg, MGADACbpp32, sizeof(hw->DACreg));
+       memcpy(hw->DACreg, MGADACbpp32, sizeof(MGADACbpp32));
        switch (minfo->fbcon.var.bits_per_pixel) {
                case 4: hw->DACreg[POS3026_XLATCHCTRL] = TVP3026_XLATCHCTRL_16_1;       /* or _8_1, they are same */
                        hw->DACreg[POS3026_XTRUECOLORCTRL] = TVP3026_XTRUECOLORCTRL_PSEUDOCOLOR;
index cff0546..f108ae6 100644 (file)
@@ -433,7 +433,7 @@ static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct outp
                0x00,   /* 3E written multiple times */
                0x00,   /* 3F not written */
        } };
-       static struct mavenregs ntscregs = { {
+       static const struct mavenregs ntscregs = { {
                0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */
                0x00,
                0x00,   /* test */
index c87e17a..ba96c44 100644 (file)
@@ -157,17 +157,10 @@ static struct i2c_adapter mb862xx_i2c_adapter = {
 
 int mb862xx_i2c_init(struct mb862xxfb_par *par)
 {
-       int ret;
-
        mb862xx_i2c_adapter.algo_data = par;
        par->adap = &mb862xx_i2c_adapter;
 
-       ret = i2c_add_adapter(par->adap);
-       if (ret < 0) {
-               dev_err(par->dev, "failed to add %s\n",
-                       mb862xx_i2c_adapter.name);
-       }
-       return ret;
+       return i2c_add_adapter(par->adap);
 }
 
 void mb862xx_i2c_exit(struct mb862xxfb_par *par)
index f91b1db..8778e01 100644 (file)
@@ -845,7 +845,7 @@ static int __set_par(struct fb_info *fbi, bool lock)
                if (fbi->var.sync & FB_SYNC_SHARP_MODE)
                        mode = IPU_PANEL_SHARP_TFT;
 
-               dev_dbg(fbi->device, "pixclock = %ul Hz\n",
+               dev_dbg(fbi->device, "pixclock = %u Hz\n",
                        (u32) (PICOS2KHZ(fbi->var.pixclock) * 1000UL));
 
                if (sdc_init_panel(mx3fb, mode,
index 4e6608c..7846f0e 100644 (file)
@@ -800,6 +800,7 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
                        struct fb_videomode *vmode)
 {
        int ret;
+       struct device *dev = &host->pdev->dev;
        struct fb_info *fb_info = &host->fb_info;
        struct fb_var_screeninfo *var = &fb_info->var;
        dma_addr_t fb_phys;
@@ -825,12 +826,10 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
 
        /* Memory allocation for framebuffer */
        fb_size = SZ_2M;
-       fb_virt = alloc_pages_exact(fb_size, GFP_DMA);
+       fb_virt = dma_alloc_wc(dev, PAGE_ALIGN(fb_size), &fb_phys, GFP_KERNEL);
        if (!fb_virt)
                return -ENOMEM;
 
-       fb_phys = virt_to_phys(fb_virt);
-
        fb_info->fix.smem_start = fb_phys;
        fb_info->screen_base = fb_virt;
        fb_info->screen_size = fb_info->fix.smem_len = fb_size;
@@ -843,9 +842,11 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host,
 
 static void mxsfb_free_videomem(struct mxsfb_info *host)
 {
+       struct device *dev = &host->pdev->dev;
        struct fb_info *fb_info = &host->fb_info;
 
-       free_pages_exact(fb_info->screen_base, fb_info->fix.smem_len);
+       dma_free_wc(dev, fb_info->screen_size, fb_info->screen_base,
+                   fb_info->fix.smem_start);
 }
 
 static const struct platform_device_id mxsfb_devtype[] = {
index fb60a8f..906c6e7 100644 (file)
@@ -625,6 +625,21 @@ static void __init offb_init_nodriver(struct device_node *dp, int no_real_node)
        if (address == OF_BAD_ADDR && addr_prop)
                address = (u64)addr_prop;
        if (address != OF_BAD_ADDR) {
+#ifdef CONFIG_PCI
+               const __be32 *vidp, *didp;
+               u32 vid, did;
+               struct pci_dev *pdev;
+
+               vidp = of_get_property(dp, "vendor-id", NULL);
+               didp = of_get_property(dp, "device-id", NULL);
+               if (vidp && didp) {
+                       vid = be32_to_cpup(vidp);
+                       did = be32_to_cpup(didp);
+                       pdev = pci_get_device(vid, did, NULL);
+                       if (!pdev || pci_enable_device(pdev))
+                               return;
+               }
+#endif
                /* kludge for valkyrie */
                if (strcmp(dp->name, "valkyrie") == 0)
                        address += 0x1000;
index 0e4cee9..c81f150 100644 (file)
@@ -60,7 +60,6 @@ struct mipid_device {
        struct mutex            mutex;
        struct lcd_panel        panel;
 
-       struct workqueue_struct *esd_wq;
        struct delayed_work     esd_work;
        void                    (*esd_check)(struct mipid_device *m);
 };
@@ -390,7 +389,7 @@ static void ls041y3_esd_check(struct mipid_device *md)
 static void mipid_esd_start_check(struct mipid_device *md)
 {
        if (md->esd_check != NULL)
-               queue_delayed_work(md->esd_wq, &md->esd_work,
+               schedule_delayed_work(&md->esd_work,
                                   MIPID_ESD_CHECK_PERIOD);
 }
 
@@ -476,11 +475,6 @@ static int mipid_init(struct lcd_panel *panel,
        struct mipid_device *md = to_mipid_device(panel);
 
        md->fbdev = fbdev;
-       md->esd_wq = create_singlethread_workqueue("mipid_esd");
-       if (md->esd_wq == NULL) {
-               dev_err(&md->spi->dev, "can't create ESD workqueue\n");
-               return -ENOMEM;
-       }
        INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
        mutex_init(&md->mutex);
 
@@ -500,7 +494,6 @@ static void mipid_cleanup(struct lcd_panel *panel)
 
        if (md->enabled)
                mipid_esd_stop_check(md);
-       destroy_workqueue(md->esd_wq);
 }
 
 static struct lcd_panel mipid_panel = {
index b58012b..8b81069 100644 (file)
@@ -75,8 +75,6 @@ struct panel_drv_data {
 
        bool intro_printed;
 
-       struct workqueue_struct *workqueue;
-
        bool ulps_enabled;
        unsigned ulps_timeout;
        struct delayed_work ulps_work;
@@ -232,7 +230,7 @@ static int dsicm_set_update_window(struct panel_drv_data *ddata,
 static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
 {
        if (ddata->ulps_timeout > 0)
-               queue_delayed_work(ddata->workqueue, &ddata->ulps_work,
+               schedule_delayed_work(&ddata->ulps_work,
                                msecs_to_jiffies(ddata->ulps_timeout));
 }
 
@@ -1244,11 +1242,6 @@ static int dsicm_probe(struct platform_device *pdev)
                dev_dbg(dev, "Using GPIO TE\n");
        }
 
-       ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
-       if (ddata->workqueue == NULL) {
-               dev_err(dev, "can't create workqueue\n");
-               return -ENOMEM;
-       }
        INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
 
        dsicm_hw_reset(ddata);
@@ -1262,7 +1255,7 @@ static int dsicm_probe(struct platform_device *pdev)
                                dev, ddata, &dsicm_bl_ops, &props);
                if (IS_ERR(bldev)) {
                        r = PTR_ERR(bldev);
-                       goto err_bl;
+                       goto err_reg;
                }
 
                ddata->bldev = bldev;
@@ -1285,8 +1278,6 @@ static int dsicm_probe(struct platform_device *pdev)
 err_sysfs_create:
        if (bldev != NULL)
                backlight_device_unregister(bldev);
-err_bl:
-       destroy_workqueue(ddata->workqueue);
 err_reg:
        return r;
 }
@@ -1316,7 +1307,6 @@ static int __exit dsicm_remove(struct platform_device *pdev)
        omap_dss_put_device(ddata->in);
 
        dsicm_cancel_ulps_work(ddata);
-       destroy_workqueue(ddata->workqueue);
 
        /* reset, to be sure that the panel is in a valid state */
        dsicm_hw_reset(ddata);
index 3691bde..a864608 100644 (file)
@@ -644,6 +644,7 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
 {
 
        int r;
+       long time_left;
        DECLARE_COMPLETION_ONSTACK(completion);
 
        r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
@@ -652,15 +653,15 @@ int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
        if (r)
                return r;
 
-       timeout = wait_for_completion_interruptible_timeout(&completion,
+       time_left = wait_for_completion_interruptible_timeout(&completion,
                        timeout);
 
        omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
 
-       if (timeout == 0)
+       if (time_left == 0)
                return -ETIMEDOUT;
 
-       if (timeout == -ERESTARTSYS)
+       if (time_left == -ERESTARTSYS)
                return -ERESTARTSYS;
 
        return 0;
index 9e4800a..30d49f3 100644 (file)
@@ -1167,7 +1167,6 @@ static int dsi_regulator_init(struct platform_device *dsidev)
 {
        struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev);
        struct regulator *vdds_dsi;
-       int r;
 
        if (dsi->vdds_dsi_reg != NULL)
                return 0;
@@ -1180,13 +1179,6 @@ static int dsi_regulator_init(struct platform_device *dsidev)
                return PTR_ERR(vdds_dsi);
        }
 
-       r = regulator_set_voltage(vdds_dsi, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(vdds_dsi);
-               DSSERR("can't set the DSI regulator voltage\n");
-               return r;
-       }
-
        dsi->vdds_dsi_reg = vdds_dsi;
 
        return 0;
@@ -5348,7 +5340,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
        dsi->phy_base = devm_ioremap(&dsidev->dev, res->start,
                resource_size(res));
-       if (!dsi->proto_base) {
+       if (!dsi->phy_base) {
                DSSERR("can't ioremap DSI PHY\n");
                return -ENOMEM;
        }
@@ -5368,7 +5360,7 @@ static int dsi_bind(struct device *dev, struct device *master, void *data)
 
        dsi->pll_base = devm_ioremap(&dsidev->dev, res->start,
                resource_size(res));
-       if (!dsi->proto_base) {
+       if (!dsi->pll_base) {
                DSSERR("can't ioremap DSI PLL\n");
                return -ENOMEM;
        }
index 926a6f2..156a254 100644 (file)
@@ -100,7 +100,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
 
 static int hdmi_init_regulator(void)
 {
-       int r;
        struct regulator *reg;
 
        if (hdmi.vdda_reg != NULL)
@@ -114,13 +113,6 @@ static int hdmi_init_regulator(void)
                return PTR_ERR(reg);
        }
 
-       r = regulator_set_voltage(reg, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(reg);
-               DSSWARN("can't set the regulator voltage\n");
-               return r;
-       }
-
        hdmi.vdda_reg = reg;
 
        return 0;
index 0ee829a..4da36bc 100644 (file)
@@ -119,7 +119,6 @@ static irqreturn_t hdmi_irq_handler(int irq, void *data)
 
 static int hdmi_init_regulator(void)
 {
-       int r;
        struct regulator *reg;
 
        if (hdmi.vdda_reg != NULL)
@@ -131,13 +130,6 @@ static int hdmi_init_regulator(void)
                return PTR_ERR(reg);
        }
 
-       r = regulator_set_voltage(reg, 1800000, 1800000);
-       if (r) {
-               devm_regulator_put(reg);
-               DSSWARN("can't set the regulator voltage\n");
-               return r;
-       }
-
        hdmi.vdda_reg = reg;
 
        return 0;
index aa8d288..1a4070f 100644 (file)
@@ -113,7 +113,7 @@ static struct fb_fix_screeninfo pm2fb_fix = {
 /*
  * Default video mode. In case the modedb doesn't work.
  */
-static struct fb_var_screeninfo pm2fb_var = {
+static const struct fb_var_screeninfo pm2fb_var = {
        /* "640x480, 8 bpp @ 60 Hz */
        .xres =                 640,
        .yres =                 480,
index 2c0487f..ef73f14 100644 (file)
@@ -2125,7 +2125,7 @@ static int of_get_pxafb_display(struct device *dev, struct device_node *disp,
 
        timings = of_get_display_timings(disp);
        if (!timings)
-               goto out;
+               return -EINVAL;
 
        ret = -ENOMEM;
        info->modes = kmalloc_array(timings->num_timings,
@@ -2186,6 +2186,7 @@ static int of_get_pxafb_mode_info(struct device *dev,
        ret = of_property_read_u32(np, "bus-width", &bus_width);
        if (ret) {
                dev_err(dev, "no bus-width specified: %d\n", ret);
+               of_node_put(np);
                return ret;
        }
 
index 96aa46d..5d6179e 100644 (file)
@@ -83,7 +83,7 @@ static const char *s1d13xxxfb_prod_names[] = {
 /*
  * here we define the default struct fb_fix_screeninfo
  */
-static struct fb_fix_screeninfo s1d13xxxfb_fix = {
+static const struct fb_fix_screeninfo s1d13xxxfb_fix = {
        .id             = S1D_FBID,
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_PSEUDOCOLOR,
@@ -929,7 +929,7 @@ static int s1d13xxxfb_suspend(struct platform_device *dev, pm_message_t state)
                s1dfb->disp_save = kmalloc(info->fix.smem_len, GFP_KERNEL);
 
        if (!s1dfb->disp_save) {
-               printk(KERN_ERR PFX "no memory to save screen");
+               printk(KERN_ERR PFX "no memory to save screen\n");
                return -ENOMEM;
        }
 
index 0dd86be..a67e456 100644 (file)
@@ -767,7 +767,7 @@ static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
 
 static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
                                        unsigned long val, void *data)
index 47a17bd..cdd11e2 100644 (file)
@@ -32,7 +32,7 @@ struct s3c2410fb_info {
        unsigned long           clk_rate;
        unsigned int            palette_ready;
 
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
        struct notifier_block   freq_transition;
 #endif
 
index 6c77ab0..c30a91c 100644 (file)
@@ -1660,7 +1660,7 @@ static struct fb_ops savagefb_ops = {
 
 /* --------------------------------------------------------------------- */
 
-static struct fb_var_screeninfo savagefb_var800x600x8 = {
+static const struct fb_var_screeninfo savagefb_var800x600x8 = {
        .accel_flags =  FB_ACCELF_TEXT,
        .xres =         800,
        .yres =         600,
index e9cf199..61f799a 100644 (file)
 #include <linux/parser.h>
 #include <linux/regulator/consumer.h>
 
-static struct fb_fix_screeninfo simplefb_fix = {
+static const struct fb_fix_screeninfo simplefb_fix = {
        .id             = "simple",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_TRUECOLOR,
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo simplefb_var = {
+static const struct fb_var_screeninfo simplefb_var = {
        .height         = -1,
        .width          = -1,
        .activate       = FB_ACTIVATE_NOW,
@@ -74,8 +74,14 @@ static int simplefb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
        return 0;
 }
 
+struct simplefb_par;
+static void simplefb_clocks_destroy(struct simplefb_par *par);
+static void simplefb_regulators_destroy(struct simplefb_par *par);
+
 static void simplefb_destroy(struct fb_info *info)
 {
+       simplefb_regulators_destroy(info->par);
+       simplefb_clocks_destroy(info->par);
        if (info->screen_base)
                iounmap(info->screen_base);
 }
@@ -487,11 +493,8 @@ error_fb_release:
 static int simplefb_remove(struct platform_device *pdev)
 {
        struct fb_info *info = platform_get_drvdata(pdev);
-       struct simplefb_par *par = info->par;
 
        unregister_framebuffer(info);
-       simplefb_regulators_destroy(par);
-       simplefb_clocks_destroy(par);
        framebuffer_release(info);
 
        return 0;
index 86ae1d4..73cb4ff 100644 (file)
@@ -56,7 +56,7 @@ struct smtcfb_info {
 
 void __iomem *smtc_regbaseaddress;     /* Memory Map IO starting address */
 
-static struct fb_var_screeninfo smtcfb_var = {
+static const struct fb_var_screeninfo smtcfb_var = {
        .xres           = 1024,
        .yres           = 600,
        .xres_virtual   = 1024,
index 9279e5f..ec2e7e3 100644 (file)
@@ -1761,10 +1761,8 @@ error:
 static void ufx_usb_disconnect(struct usb_interface *interface)
 {
        struct ufx_data *dev;
-       struct fb_info *info;
 
        dev = usb_get_intfdata(interface);
-       info = dev->info;
 
        pr_debug("USB disconnect starting\n");
 
index a9c45c8..2925d5c 100644 (file)
@@ -64,7 +64,7 @@ struct ssd1307fb_par {
        u32 contrast;
        u32 dclk_div;
        u32 dclk_frq;
-       struct ssd1307fb_deviceinfo *device_info;
+       const struct ssd1307fb_deviceinfo *device_info;
        struct i2c_client *client;
        u32 height;
        struct fb_info *info;
@@ -84,7 +84,7 @@ struct ssd1307fb_array {
        u8      data[0];
 };
 
-static struct fb_fix_screeninfo ssd1307fb_fix = {
+static const struct fb_fix_screeninfo ssd1307fb_fix = {
        .id             = "Solomon SSD1307",
        .type           = FB_TYPE_PACKED_PIXELS,
        .visual         = FB_VISUAL_MONO10,
@@ -94,7 +94,7 @@ static struct fb_fix_screeninfo ssd1307fb_fix = {
        .accel          = FB_ACCEL_NONE,
 };
 
-static struct fb_var_screeninfo ssd1307fb_var = {
+static const struct fb_var_screeninfo ssd1307fb_var = {
        .bits_per_pixel = 1,
 };
 
@@ -559,8 +559,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
        par->info = info;
        par->client = client;
 
-       par->device_info = (struct ssd1307fb_deviceinfo *)of_match_device(
-                       ssd1307fb_of_match, &client->dev)->data;
+       par->device_info = of_device_get_match_data(&client->dev);
 
        par->reset = of_get_named_gpio(client->dev.of_node,
                                         "reset-gpios", 0);
index 621fa44..d5fa313 100644 (file)
@@ -82,7 +82,7 @@
 #define VOODOO3_MAX_PIXCLOCK 300000
 #define VOODOO5_MAX_PIXCLOCK 350000
 
-static struct fb_fix_screeninfo tdfx_fix = {
+static const struct fb_fix_screeninfo tdfx_fix = {
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_PSEUDOCOLOR,
        .ypanstep =     1,
@@ -90,7 +90,7 @@ static struct fb_fix_screeninfo tdfx_fix = {
        .accel =        FB_ACCEL_3DFX_BANSHEE
 };
 
-static struct fb_var_screeninfo tdfx_var = {
+static const struct fb_var_screeninfo tdfx_var = {
        /* "640x480, 8 bpp @ 60 Hz */
        .xres =         640,
        .yres =         480,
index 178ae93..98af9e0 100644 (file)
@@ -33,7 +33,7 @@ static struct cb_id uvesafb_cn_id = {
 static char v86d_path[PATH_MAX] = "/sbin/v86d";
 static char v86d_started;      /* has v86d been started by uvesafb? */
 
-static struct fb_fix_screeninfo uvesafb_fix = {
+static const struct fb_fix_screeninfo uvesafb_fix = {
        .id     = "VESA VGA",
        .type   = FB_TYPE_PACKED_PIXELS,
        .accel  = FB_ACCEL_NONE,
index b9c2f81..da653a0 100644 (file)
 static void *videomemory;
 static u_long videomemorysize = VIDEOMEMSIZE;
 module_param(videomemorysize, ulong, 0);
+MODULE_PARM_DESC(videomemorysize, "RAM available to frame buffer (in bytes)");
 
-/**********************************************************************
- *
- * Memory management
- *
- **********************************************************************/
-static void *rvmalloc(unsigned long size)
-{
-       void *mem;
-       unsigned long adr;
-
-       size = PAGE_ALIGN(size);
-       mem = vmalloc_32(size);
-       if (!mem)
-               return NULL;
-
-       /*
-        * VFB must clear memory to prevent kernel info
-        * leakage into userspace
-        * VGA-based drivers MUST NOT clear memory if
-        * they want to be able to take over vgacon
-        */
-
-       memset(mem, 0, size);
-       adr = (unsigned long) mem;
-       while (size > 0) {
-               SetPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-
-       return mem;
-}
-
-static void rvfree(void *mem, unsigned long size)
-{
-       unsigned long adr;
-
-       if (!mem)
-               return;
-
-       adr = (unsigned long) mem;
-       while ((long) size > 0) {
-               ClearPageReserved(vmalloc_to_page((void *)adr));
-               adr += PAGE_SIZE;
-               size -= PAGE_SIZE;
-       }
-       vfree(mem);
-}
+static char *mode_option = NULL;
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Preferred video mode (e.g. 640x480-8@60)");
 
-static struct fb_var_screeninfo vfb_default = {
+static const struct fb_videomode vfb_default = {
        .xres =         640,
        .yres =         480,
-       .xres_virtual = 640,
-       .yres_virtual = 480,
-       .bits_per_pixel = 8,
-       .red =          { 0, 8, 0 },
-       .green =        { 0, 8, 0 },
-       .blue =         { 0, 8, 0 },
-       .activate =     FB_ACTIVATE_TEST,
-       .height =       -1,
-       .width =        -1,
-       .pixclock =     20000,
-       .left_margin =  64,
-       .right_margin = 64,
-       .upper_margin = 32,
-       .lower_margin = 32,
-       .hsync_len =    64,
-       .vsync_len =    2,
-       .vmode =        FB_VMODE_NONINTERLACED,
+       .pixclock =     20000,
+       .left_margin =  64,
+       .right_margin = 64,
+       .upper_margin = 32,
+       .lower_margin = 32,
+       .hsync_len =    64,
+       .vsync_len =    2,
+       .vmode =        FB_VMODE_NONINTERLACED,
 };
 
 static struct fb_fix_screeninfo vfb_fix = {
@@ -119,6 +66,7 @@ static struct fb_fix_screeninfo vfb_fix = {
 
 static bool vfb_enable __initdata = 0; /* disabled by default */
 module_param(vfb_enable, bool, 0);
+MODULE_PARM_DESC(vfb_enable, "Enable Virtual FB driver");
 
 static int vfb_check_var(struct fb_var_screeninfo *var,
                         struct fb_info *info);
@@ -421,35 +369,7 @@ static int vfb_pan_display(struct fb_var_screeninfo *var,
 static int vfb_mmap(struct fb_info *info,
                    struct vm_area_struct *vma)
 {
-       unsigned long start = vma->vm_start;
-       unsigned long size = vma->vm_end - vma->vm_start;
-       unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
-       unsigned long page, pos;
-
-       if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
-               return -EINVAL;
-       if (size > info->fix.smem_len)
-               return -EINVAL;
-       if (offset > info->fix.smem_len - size)
-               return -EINVAL;
-
-       pos = (unsigned long)info->fix.smem_start + offset;
-
-       while (size > 0) {
-               page = vmalloc_to_pfn((void *)pos);
-               if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) {
-                       return -EAGAIN;
-               }
-               start += PAGE_SIZE;
-               pos += PAGE_SIZE;
-               if (size > PAGE_SIZE)
-                       size -= PAGE_SIZE;
-               else
-                       size = 0;
-       }
-
-       return 0;
-
+       return remap_vmalloc_range(vma, (void *)info->fix.smem_start, vma->vm_pgoff);
 }
 
 #ifndef MODULE
@@ -477,6 +397,8 @@ static int __init vfb_setup(char *options)
                /* Test disable for backwards compatibility */
                if (!strcmp(this_opt, "disable"))
                        vfb_enable = 0;
+               else
+                       mode_option = this_opt;
        }
        return 1;
 }
@@ -489,12 +411,13 @@ static int __init vfb_setup(char *options)
 static int vfb_probe(struct platform_device *dev)
 {
        struct fb_info *info;
+       unsigned int size = PAGE_ALIGN(videomemorysize);
        int retval = -ENOMEM;
 
        /*
         * For real video cards we use ioremap.
         */
-       if (!(videomemory = rvmalloc(videomemorysize)))
+       if (!(videomemory = vmalloc_32_user(size)))
                return retval;
 
        info = framebuffer_alloc(sizeof(u32) * 256, &dev->dev);
@@ -504,11 +427,13 @@ static int vfb_probe(struct platform_device *dev)
        info->screen_base = (char __iomem *)videomemory;
        info->fbops = &vfb_ops;
 
-       retval = fb_find_mode(&info->var, info, NULL,
-                             NULL, 0, NULL, 8);
+       if (!fb_find_mode(&info->var, info, mode_option,
+                         NULL, 0, &vfb_default, 8)){
+               fb_err(info, "Unable to find usable video mode.\n");
+               retval = -EINVAL;
+               goto err1;
+       }
 
-       if (!retval || (retval == 4))
-               info->var = vfb_default;
        vfb_fix.smem_start = (unsigned long) videomemory;
        vfb_fix.smem_len = videomemorysize;
        info->fix = vfb_fix;
@@ -533,7 +458,7 @@ err2:
 err1:
        framebuffer_release(info);
 err:
-       rvfree(videomemory, videomemorysize);
+       vfree(videomemory);
        return retval;
 }
 
@@ -543,7 +468,7 @@ static int vfb_remove(struct platform_device *dev)
 
        if (info) {
                unregister_framebuffer(info);
-               rvfree(videomemory, videomemorysize);
+               vfree(videomemory);
                fb_dealloc_cmap(&info->cmap);
                framebuffer_release(info);
        }
index 283d335..5f0690c 100644 (file)
@@ -85,7 +85,7 @@ static struct fb_var_screeninfo vga16fb_defined = {
 };
 
 /* name should not depend on EGA/VGA */
-static struct fb_fix_screeninfo vga16fb_fix = {
+static const struct fb_fix_screeninfo vga16fb_fix = {
        .id             = "VGA16 VGA",
        .smem_start     = VGA_FB_PHYS,
        .smem_len       = VGA_FB_PHYS_LEN,
index 50dbaa8..fdd3228 100644 (file)
@@ -1844,4 +1844,53 @@ config USBPCWATCHDOG
 
          Most people will say N.
 
+comment "Watchdog Pretimeout Governors"
+
+config WATCHDOG_PRETIMEOUT_GOV
+       bool "Enable watchdog pretimeout governors"
+       help
+         The option allows to select watchdog pretimeout governors.
+
+if WATCHDOG_PRETIMEOUT_GOV
+
+choice
+       prompt "Default Watchdog Pretimeout Governor"
+       default WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
+       help
+         This option selects a default watchdog pretimeout governor.
+         The governor takes its action, if a watchdog is capable
+         to report a pretimeout event.
+
+config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP
+       bool "noop"
+       select WATCHDOG_PRETIMEOUT_GOV_NOOP
+       help
+         Use noop watchdog pretimeout governor by default. If noop
+         governor is selected by a user, write a short message to
+         the kernel log buffer and don't do any system changes.
+
+config WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC
+       bool "panic"
+       select WATCHDOG_PRETIMEOUT_GOV_PANIC
+       help
+         Use panic watchdog pretimeout governor by default, if
+         a watchdog pretimeout event happens, consider that
+         a watchdog feeder is dead and reboot is unavoidable.
+
+endchoice
+
+config WATCHDOG_PRETIMEOUT_GOV_NOOP
+       tristate "Noop watchdog pretimeout governor"
+       help
+         Noop watchdog pretimeout governor, only an informational
+         message is added to kernel log buffer.
+
+config WATCHDOG_PRETIMEOUT_GOV_PANIC
+       tristate "Panic watchdog pretimeout governor"
+       help
+         Panic watchdog pretimeout governor, on watchdog pretimeout
+         event put the kernel into panic.
+
+endif # WATCHDOG_PRETIMEOUT_GOV
+
 endif # WATCHDOG
index cba0043..caa9f4a 100644 (file)
@@ -3,9 +3,15 @@
 #
 
 # The WatchDog Timer Driver Core.
-watchdog-objs  += watchdog_core.o watchdog_dev.o
 obj-$(CONFIG_WATCHDOG_CORE)    += watchdog.o
 
+watchdog-objs  += watchdog_core.o watchdog_dev.o
+
+watchdog-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV)     += watchdog_pretimeout.o
+
+obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_NOOP)     += pretimeout_noop.o
+obj-$(CONFIG_WATCHDOG_PRETIMEOUT_GOV_PANIC)    += pretimeout_panic.o
+
 # Only one watchdog can succeed. We probe the ISA/PCI/USB based
 # watchdog-cards first, then the architecture specific watchdog
 # drivers and then the architecture independent "softdog" driver.
index c9686b2..d0b59ba 100644 (file)
@@ -389,7 +389,6 @@ MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
 static struct platform_driver asm9260_wdt_driver = {
        .driver = {
                .name = "asm9260-wdt",
-               .owner = THIS_MODULE,
                .of_match_table = asm9260_wdt_of_match,
        },
        .probe = asm9260_wdt_probe,
index 835d310..e2209bf 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/err.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/uaccess.h>
 
 #define DRIVER_NAME    "ath79-wdt"
 
index 4245b65..e238df4 100644 (file)
@@ -107,7 +107,7 @@ static struct watchdog_info bcm7038_wdt_info = {
                                WDIOF_MAGICCLOSE
 };
 
-static struct watchdog_ops bcm7038_wdt_ops = {
+static const struct watchdog_ops bcm7038_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = bcm7038_wdt_start,
        .stop           = bcm7038_wdt_stop,
index 4dda902..98acef7 100644 (file)
@@ -269,7 +269,7 @@ static struct watchdog_info cdns_wdt_info = {
 };
 
 /* Watchdog Core Ops */
-static struct watchdog_ops cdns_wdt_ops = {
+static const struct watchdog_ops cdns_wdt_ops = {
        .owner = THIS_MODULE,
        .start = cdns_wdt_start,
        .stop = cdns_wdt_stop,
@@ -424,8 +424,10 @@ static int __maybe_unused cdns_wdt_suspend(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct cdns_wdt *wdt = platform_get_drvdata(pdev);
 
-       cdns_wdt_stop(&wdt->cdns_wdt_device);
-       clk_disable_unprepare(wdt->clk);
+       if (watchdog_active(&wdt->cdns_wdt_device)) {
+               cdns_wdt_stop(&wdt->cdns_wdt_device);
+               clk_disable_unprepare(wdt->clk);
+       }
 
        return 0;
 }
@@ -442,12 +444,14 @@ static int __maybe_unused cdns_wdt_resume(struct device *dev)
        struct platform_device *pdev = to_platform_device(dev);
        struct cdns_wdt *wdt = platform_get_drvdata(pdev);
 
-       ret = clk_prepare_enable(wdt->clk);
-       if (ret) {
-               dev_err(dev, "unable to enable clock\n");
-               return ret;
+       if (watchdog_active(&wdt->cdns_wdt_device)) {
+               ret = clk_prepare_enable(wdt->clk);
+               if (ret) {
+                       dev_err(dev, "unable to enable clock\n");
+                       return ret;
+               }
+               cdns_wdt_start(&wdt->cdns_wdt_device);
        }
-       cdns_wdt_start(&wdt->cdns_wdt_device);
 
        return 0;
 }
index 2acb51c..3c6a3de 100644 (file)
@@ -54,6 +54,7 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
 struct dw_wdt {
        void __iomem            *regs;
        struct clk              *clk;
+       unsigned long           rate;
        struct notifier_block   restart_handler;
        struct watchdog_device  wdd;
 };
@@ -72,7 +73,7 @@ static inline int dw_wdt_top_in_seconds(struct dw_wdt *dw_wdt, unsigned top)
         * There are 16 possible timeout values in 0..15 where the number of
         * cycles is 2 ^ (16 + i) and the watchdog counts down.
         */
-       return (1U << (16 + top)) / clk_get_rate(dw_wdt->clk);
+       return (1U << (16 + top)) / dw_wdt->rate;
 }
 
 static int dw_wdt_get_top(struct dw_wdt *dw_wdt)
@@ -163,7 +164,7 @@ static unsigned int dw_wdt_get_timeleft(struct watchdog_device *wdd)
        struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
 
        return readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
-               clk_get_rate(dw_wdt->clk);
+               dw_wdt->rate;
 }
 
 static const struct watchdog_info dw_wdt_ident = {
@@ -231,6 +232,12 @@ static int dw_wdt_drv_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       dw_wdt->rate = clk_get_rate(dw_wdt->clk);
+       if (dw_wdt->rate == 0) {
+               ret = -EINVAL;
+               goto out_disable_clk;
+       }
+
        wdd = &dw_wdt->wdd;
        wdd->info = &dw_wdt_ident;
        wdd->ops = &dw_wdt_ops;
index 8f89bd8..70c7194 100644 (file)
@@ -39,7 +39,7 @@
 #include <asm/nmi.h>
 #include <asm/frame.h>
 
-#define HPWDT_VERSION                  "1.3.3"
+#define HPWDT_VERSION                  "1.4.0"
 #define SECS_TO_TICKS(secs)            ((secs) * 1000 / 128)
 #define TICKS_TO_SECS(ticks)           ((ticks) * 128 / 1000)
 #define HPWDT_MAX_TIMER                        TICKS_TO_SECS(65535)
@@ -814,7 +814,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
         * not run on a legacy ASM box.
         * So we only support the G5 ProLiant servers and higher.
         */
-       if (dev->subsystem_vendor != PCI_VENDOR_ID_HP) {
+       if (dev->subsystem_vendor != PCI_VENDOR_ID_HP &&
+           dev->subsystem_vendor != PCI_VENDOR_ID_HP_3PAR) {
                dev_warn(&dev->dev,
                        "This server does not have an iLO2+ ASIC.\n");
                return -ENODEV;
@@ -823,7 +824,8 @@ static int hpwdt_init_one(struct pci_dev *dev,
        /*
         * Ignore all auxilary iLO devices with the following PCI ID
         */
-       if (dev->subsystem_device == 0x1979)
+       if (dev->subsystem_vendor == PCI_VENDOR_ID_HP &&
+           dev->subsystem_device == 0x1979)
                return -ENODEV;
 
        if (pci_enable_device(dev)) {
index 54cab18..06fcb6c 100644 (file)
@@ -629,7 +629,7 @@ static int iTCO_wdt_resume_noirq(struct device *dev)
        return 0;
 }
 
-static struct dev_pm_ops iTCO_wdt_pm = {
+static const struct dev_pm_ops iTCO_wdt_pm = {
        .suspend_noirq = iTCO_wdt_suspend_noirq,
        .resume_noirq = iTCO_wdt_resume_noirq,
 };
index 62f346b..4874b0f 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 
 #define IMX2_WDT_WCR           0x00            /* Control Register */
 #define IMX2_WDT_WCR_WT                (0xFF << 8)     /* -> Watchdog Timeout Field */
-#define IMX2_WDT_WCR_WDA       (1 << 5)        /* -> External Reset WDOG_B */
-#define IMX2_WDT_WCR_SRS       (1 << 4)        /* -> Software Reset Signal */
-#define IMX2_WDT_WCR_WRE       (1 << 3)        /* -> WDOG Reset Enable */
-#define IMX2_WDT_WCR_WDE       (1 << 2)        /* -> Watchdog Enable */
-#define IMX2_WDT_WCR_WDZST     (1 << 0)        /* -> Watchdog timer Suspend */
+#define IMX2_WDT_WCR_WDA       BIT(5)          /* -> External Reset WDOG_B */
+#define IMX2_WDT_WCR_SRS       BIT(4)          /* -> Software Reset Signal */
+#define IMX2_WDT_WCR_WRE       BIT(3)          /* -> WDOG Reset Enable */
+#define IMX2_WDT_WCR_WDE       BIT(2)          /* -> Watchdog Enable */
+#define IMX2_WDT_WCR_WDZST     BIT(0)          /* -> Watchdog timer Suspend */
 
 #define IMX2_WDT_WSR           0x02            /* Service Register */
 #define IMX2_WDT_SEQ1          0x5555          /* -> service sequence 1 */
 #define IMX2_WDT_SEQ2          0xAAAA          /* -> service sequence 2 */
 
 #define IMX2_WDT_WRSR          0x04            /* Reset Status Register */
-#define IMX2_WDT_WRSR_TOUT     (1 << 1)        /* -> Reset due to Timeout */
+#define IMX2_WDT_WRSR_TOUT     BIT(1)          /* -> Reset due to Timeout */
+
+#define IMX2_WDT_WICR          0x06            /* Interrupt Control Register */
+#define IMX2_WDT_WICR_WIE      BIT(15)         /* -> Interrupt Enable */
+#define IMX2_WDT_WICR_WTIS     BIT(14)         /* -> Interrupt Status */
+#define IMX2_WDT_WICR_WICT     0xFF            /* -> Interrupt Count Timeout */
 
 #define IMX2_WDT_WMCR          0x08            /* Misc Register */
 
@@ -80,6 +86,12 @@ static const struct watchdog_info imx2_wdt_info = {
        .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
 };
 
+static const struct watchdog_info imx2_wdt_pretimeout_info = {
+       .identity = "imx2+ watchdog",
+       .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
+                  WDIOF_PRETIMEOUT,
+};
+
 static int imx2_wdt_restart(struct watchdog_device *wdog, unsigned long action,
                            void *data)
 {
@@ -169,6 +181,35 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
        return 0;
 }
 
+static int imx2_wdt_set_pretimeout(struct watchdog_device *wdog,
+                                  unsigned int new_pretimeout)
+{
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       if (new_pretimeout >= IMX2_WDT_MAX_TIME)
+               return -EINVAL;
+
+       wdog->pretimeout = new_pretimeout;
+
+       regmap_update_bits(wdev->regmap, IMX2_WDT_WICR,
+                          IMX2_WDT_WICR_WIE | IMX2_WDT_WICR_WICT,
+                          IMX2_WDT_WICR_WIE | (new_pretimeout << 1));
+       return 0;
+}
+
+static irqreturn_t imx2_wdt_isr(int irq, void *wdog_arg)
+{
+       struct watchdog_device *wdog = wdog_arg;
+       struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
+
+       regmap_write_bits(wdev->regmap, IMX2_WDT_WICR,
+                         IMX2_WDT_WICR_WTIS, IMX2_WDT_WICR_WTIS);
+
+       watchdog_notify_pretimeout(wdog);
+
+       return IRQ_HANDLED;
+}
+
 static int imx2_wdt_start(struct watchdog_device *wdog)
 {
        struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog);
@@ -188,6 +229,7 @@ static const struct watchdog_ops imx2_wdt_ops = {
        .start = imx2_wdt_start,
        .ping = imx2_wdt_ping,
        .set_timeout = imx2_wdt_set_timeout,
+       .set_pretimeout = imx2_wdt_set_pretimeout,
        .restart = imx2_wdt_restart,
 };
 
@@ -236,6 +278,12 @@ static int __init imx2_wdt_probe(struct platform_device *pdev)
        wdog->max_hw_heartbeat_ms = IMX2_WDT_MAX_TIME * 1000;
        wdog->parent            = &pdev->dev;
 
+       ret = platform_get_irq(pdev, 0);
+       if (ret > 0)
+               if (!devm_request_irq(&pdev->dev, ret, imx2_wdt_isr, 0,
+                                     dev_name(&pdev->dev), wdog))
+                       wdog->info = &imx2_wdt_pretimeout_info;
+
        ret = clk_prepare_enable(wdev->clk);
        if (ret)
                return ret;
index 5bf931c..8e302d0 100644 (file)
@@ -430,7 +430,7 @@ static struct watchdog_info kempld_wdt_info = {
                        WDIOF_PRETIMEOUT
 };
 
-static struct watchdog_ops kempld_wdt_ops = {
+static const struct watchdog_ops kempld_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = kempld_wdt_start,
        .stop           = kempld_wdt_stop,
index 4a2290f..d5735c1 100644 (file)
@@ -139,7 +139,6 @@ static int mt7621_wdt_probe(struct platform_device *pdev)
        if (!IS_ERR(mt7621_wdt_reset))
                reset_control_deassert(mt7621_wdt_reset);
 
-       mt7621_wdt_dev.dev = &pdev->dev;
        mt7621_wdt_dev.bootstatus = mt7621_wdt_bootcause();
 
        watchdog_init_timeout(&mt7621_wdt_dev, mt7621_wdt_dev.max_timeout,
index b2e1b4c..fae7fe9 100644 (file)
@@ -10,6 +10,7 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/types.h>
@@ -45,6 +46,7 @@ struct xwdt_device {
        u32 wdt_interval;
        spinlock_t spinlock;
        struct watchdog_device xilinx_wdt_wdd;
+       struct clk              *clk;
 };
 
 static int xilinx_wdt_start(struct watchdog_device *wdd)
@@ -195,16 +197,30 @@ static int xwdt_probe(struct platform_device *pdev)
        spin_lock_init(&xdev->spinlock);
        watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
 
+       xdev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(xdev->clk)) {
+               if (PTR_ERR(xdev->clk) == -ENOENT)
+                       xdev->clk = NULL;
+               else
+                       return PTR_ERR(xdev->clk);
+       }
+
+       rc = clk_prepare_enable(xdev->clk);
+       if (rc) {
+               dev_err(&pdev->dev, "unable to enable clock\n");
+               return rc;
+       }
+
        rc = xwdt_selftest(xdev);
        if (rc == XWT_TIMER_FAILED) {
                dev_err(&pdev->dev, "SelfTest routine error\n");
-               return rc;
+               goto err_clk_disable;
        }
 
        rc = watchdog_register_device(xilinx_wdt_wdd);
        if (rc) {
                dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
-               return rc;
+               goto err_clk_disable;
        }
 
        dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
@@ -213,6 +229,10 @@ static int xwdt_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, xdev);
 
        return 0;
+err_clk_disable:
+       clk_disable_unprepare(xdev->clk);
+
+       return rc;
 }
 
 static int xwdt_remove(struct platform_device *pdev)
@@ -220,6 +240,7 @@ static int xwdt_remove(struct platform_device *pdev)
        struct xwdt_device *xdev = platform_get_drvdata(pdev);
 
        watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
+       clk_disable_unprepare(xdev->clk);
 
        return 0;
 }
diff --git a/drivers/watchdog/pretimeout_noop.c b/drivers/watchdog/pretimeout_noop.c
new file mode 100644 (file)
index 0000000..85f5299
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/**
+ * pretimeout_noop - No operation on watchdog pretimeout event
+ * @wdd - watchdog_device
+ *
+ * This function prints a message about pretimeout to kernel log.
+ */
+static void pretimeout_noop(struct watchdog_device *wdd)
+{
+       pr_alert("watchdog%d: pretimeout event\n", wdd->id);
+}
+
+static struct watchdog_governor watchdog_gov_noop = {
+       .name           = "noop",
+       .pretimeout     = pretimeout_noop,
+};
+
+static int __init watchdog_gov_noop_register(void)
+{
+       return watchdog_register_governor(&watchdog_gov_noop);
+}
+
+static void __exit watchdog_gov_noop_unregister(void)
+{
+       watchdog_unregister_governor(&watchdog_gov_noop);
+}
+module_init(watchdog_gov_noop_register);
+module_exit(watchdog_gov_noop_unregister);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
+MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/watchdog/pretimeout_panic.c b/drivers/watchdog/pretimeout_panic.c
new file mode 100644 (file)
index 0000000..0c197a1
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/**
+ * pretimeout_panic - Panic on watchdog pretimeout event
+ * @wdd - watchdog_device
+ *
+ * Panic, watchdog has not been fed till pretimeout event.
+ */
+static void pretimeout_panic(struct watchdog_device *wdd)
+{
+       panic("watchdog pretimeout event\n");
+}
+
+static struct watchdog_governor watchdog_gov_panic = {
+       .name           = "panic",
+       .pretimeout     = pretimeout_panic,
+};
+
+static int __init watchdog_gov_panic_register(void)
+{
+       return watchdog_register_governor(&watchdog_gov_panic);
+}
+
+static void __exit watchdog_gov_panic_unregister(void)
+{
+       watchdog_unregister_governor(&watchdog_gov_panic);
+}
+module_init(watchdog_gov_panic_register);
+module_exit(watchdog_gov_panic_unregister);
+
+MODULE_AUTHOR("Vladimir Zapolskiy <vladimir_zapolskiy@mentor.com>");
+MODULE_DESCRIPTION("Panic watchdog pretimeout governor");
+MODULE_LICENSE("GPL");
index d1c1227..0805ee2 100644 (file)
@@ -136,7 +136,7 @@ static struct watchdog_info rn5t618_wdt_info = {
        .identity       = DRIVER_NAME,
 };
 
-static struct watchdog_ops rn5t618_wdt_ops = {
+static const struct watchdog_ops rn5t618_wdt_ops = {
        .owner          = THIS_MODULE,
        .start          = rn5t618_wdt_start,
        .stop           = rn5t618_wdt_stop,
index 1967919..14b4fd4 100644 (file)
@@ -158,7 +158,6 @@ static int rt288x_wdt_probe(struct platform_device *pdev)
 
        rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
 
-       rt288x_wdt_dev.dev = &pdev->dev;
        rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
        rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
        rt288x_wdt_dev.parent = &pdev->dev;
index b067edf..c7bdc98 100644 (file)
@@ -72,10 +72,27 @@ static void softdog_fire(unsigned long data)
 static struct timer_list softdog_ticktock =
                TIMER_INITIALIZER(softdog_fire, 0, 0);
 
+static struct watchdog_device softdog_dev;
+
+static void softdog_pretimeout(unsigned long data)
+{
+       watchdog_notify_pretimeout(&softdog_dev);
+}
+
+static struct timer_list softdog_preticktock =
+               TIMER_INITIALIZER(softdog_pretimeout, 0, 0);
+
 static int softdog_ping(struct watchdog_device *w)
 {
        if (!mod_timer(&softdog_ticktock, jiffies + (w->timeout * HZ)))
                __module_get(THIS_MODULE);
+
+       if (w->pretimeout)
+               mod_timer(&softdog_preticktock, jiffies +
+                         (w->timeout - w->pretimeout) * HZ);
+       else
+               del_timer(&softdog_preticktock);
+
        return 0;
 }
 
@@ -84,15 +101,18 @@ static int softdog_stop(struct watchdog_device *w)
        if (del_timer(&softdog_ticktock))
                module_put(THIS_MODULE);
 
+       del_timer(&softdog_preticktock);
+
        return 0;
 }
 
 static struct watchdog_info softdog_info = {
        .identity = "Software Watchdog",
-       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+       .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
+                  WDIOF_PRETIMEOUT,
 };
 
-static struct watchdog_ops softdog_ops = {
+static const struct watchdog_ops softdog_ops = {
        .owner = THIS_MODULE,
        .start = softdog_ping,
        .stop = softdog_stop,
index 14e9bad..e6100e4 100644 (file)
@@ -52,27 +52,6 @@ struct st_wdog {
        bool warm_reset;
 };
 
-static struct st_wdog_syscfg stid127_syscfg = {
-       .reset_type_reg         = 0x004,
-       .reset_type_mask        = BIT(2),
-       .enable_reg             = 0x000,
-       .enable_mask            = BIT(2),
-};
-
-static struct st_wdog_syscfg stih415_syscfg = {
-       .reset_type_reg         = 0x0B8,
-       .reset_type_mask        = BIT(6),
-       .enable_reg             = 0x0B4,
-       .enable_mask            = BIT(7),
-};
-
-static struct st_wdog_syscfg stih416_syscfg = {
-       .reset_type_reg         = 0x88C,
-       .reset_type_mask        = BIT(6),
-       .enable_reg             = 0x888,
-       .enable_mask            = BIT(7),
-};
-
 static struct st_wdog_syscfg stih407_syscfg = {
        .enable_reg             = 0x204,
        .enable_mask            = BIT(19),
@@ -83,18 +62,6 @@ static const struct of_device_id st_wdog_match[] = {
                .compatible = "st,stih407-lpc",
                .data = &stih407_syscfg,
        },
-       {
-               .compatible = "st,stih416-lpc",
-               .data = &stih416_syscfg,
-       },
-       {
-               .compatible = "st,stih415-lpc",
-               .data = &stih415_syscfg,
-       },
-       {
-               .compatible = "st,stid127-lpc",
-               .data = &stid127_syscfg,
-       },
        {},
 };
 MODULE_DEVICE_TABLE(of, st_wdog_match);
index 9ec5760..2d53c3f 100644 (file)
@@ -178,7 +178,7 @@ static const struct watchdog_info tegra_wdt_info = {
        .identity       = "Tegra Watchdog",
 };
 
-static struct watchdog_ops tegra_wdt_ops = {
+static const struct watchdog_ops tegra_wdt_ops = {
        .owner = THIS_MODULE,
        .start = tegra_wdt_start,
        .stop = tegra_wdt_stop,
index c2da880..6f7a9de 100644 (file)
@@ -112,7 +112,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
                txx9_imclk = NULL;
                goto exit;
        }
-       ret = clk_enable(txx9_imclk);
+       ret = clk_prepare_enable(txx9_imclk);
        if (ret) {
                clk_put(txx9_imclk);
                txx9_imclk = NULL;
@@ -144,7 +144,7 @@ static int __init txx9wdt_probe(struct platform_device *dev)
        return 0;
 exit:
        if (txx9_imclk) {
-               clk_disable(txx9_imclk);
+               clk_disable_unprepare(txx9_imclk);
                clk_put(txx9_imclk);
        }
        return ret;
@@ -153,7 +153,7 @@ exit:
 static int __exit txx9wdt_remove(struct platform_device *dev)
 {
        watchdog_unregister_device(&txx9wdt);
-       clk_disable(txx9_imclk);
+       clk_disable_unprepare(txx9_imclk);
        clk_put(txx9_imclk);
        return 0;
 }
index 09e8003..ef2ecaf 100644 (file)
@@ -302,7 +302,7 @@ static struct watchdog_info wdt_info = {
        .identity = "W83627HF Watchdog",
 };
 
-static struct watchdog_ops wdt_ops = {
+static const struct watchdog_ops wdt_ops = {
        .owner = THIS_MODULE,
        .start = wdt_start,
        .stop = wdt_stop,
index 6abb83c..74265b2 100644 (file)
@@ -349,7 +349,7 @@ int devm_watchdog_register_device(struct device *dev,
        struct watchdog_device **rcwdd;
        int ret;
 
-       rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*wdd),
+       rcwdd = devres_alloc(devm_watchdog_unregister_device, sizeof(*rcwdd),
                             GFP_KERNEL);
        if (!rcwdd)
                return -ENOMEM;
index 040bf83..32930a0 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/uaccess.h>     /* For copy_to_user/put_user/... */
 
 #include "watchdog_core.h"
+#include "watchdog_pretimeout.h"
 
 /*
  * struct watchdog_core_data - watchdog core internal data
@@ -335,16 +336,45 @@ static int watchdog_set_timeout(struct watchdog_device *wdd,
        if (watchdog_timeout_invalid(wdd, timeout))
                return -EINVAL;
 
-       if (wdd->ops->set_timeout)
+       if (wdd->ops->set_timeout) {
                err = wdd->ops->set_timeout(wdd, timeout);
-       else
+       } else {
                wdd->timeout = timeout;
+               /* Disable pretimeout if it doesn't fit the new timeout */
+               if (wdd->pretimeout >= wdd->timeout)
+                       wdd->pretimeout = 0;
+       }
 
        watchdog_update_worker(wdd);
 
        return err;
 }
 
+/*
+ *     watchdog_set_pretimeout: set the watchdog timer pretimeout
+ *     @wdd: the watchdog device to set the timeout for
+ *     @timeout: pretimeout to set in seconds
+ */
+
+static int watchdog_set_pretimeout(struct watchdog_device *wdd,
+                                  unsigned int timeout)
+{
+       int err = 0;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return -EOPNOTSUPP;
+
+       if (watchdog_pretimeout_invalid(wdd, timeout))
+               return -EINVAL;
+
+       if (wdd->ops->set_pretimeout)
+               err = wdd->ops->set_pretimeout(wdd, timeout);
+       else
+               wdd->pretimeout = timeout;
+
+       return err;
+}
+
 /*
  *     watchdog_get_timeleft: wrapper to get the time left before a reboot
  *     @wdd: the watchdog device to get the remaining time from
@@ -429,6 +459,15 @@ static ssize_t timeout_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(timeout);
 
+static ssize_t pretimeout_show(struct device *dev,
+                              struct device_attribute *attr, char *buf)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", wdd->pretimeout);
+}
+static DEVICE_ATTR_RO(pretimeout);
+
 static ssize_t identity_show(struct device *dev, struct device_attribute *attr,
                                char *buf)
 {
@@ -450,6 +489,36 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(state);
 
+static ssize_t pretimeout_available_governors_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       return watchdog_pretimeout_available_governors_get(buf);
+}
+static DEVICE_ATTR_RO(pretimeout_available_governors);
+
+static ssize_t pretimeout_governor_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+
+       return watchdog_pretimeout_governor_get(wdd, buf);
+}
+
+static ssize_t pretimeout_governor_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count)
+{
+       struct watchdog_device *wdd = dev_get_drvdata(dev);
+       int ret = watchdog_pretimeout_governor_set(wdd, buf);
+
+       if (!ret)
+               ret = count;
+
+       return ret;
+}
+static DEVICE_ATTR_RW(pretimeout_governor);
+
 static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
                                int n)
 {
@@ -459,6 +528,14 @@ static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr,
 
        if (attr == &dev_attr_timeleft.attr && !wdd->ops->get_timeleft)
                mode = 0;
+       else if (attr == &dev_attr_pretimeout.attr &&
+                !(wdd->info->options & WDIOF_PRETIMEOUT))
+               mode = 0;
+       else if ((attr == &dev_attr_pretimeout_governor.attr ||
+                 attr == &dev_attr_pretimeout_available_governors.attr) &&
+                (!(wdd->info->options & WDIOF_PRETIMEOUT) ||
+                 !IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)))
+               mode = 0;
 
        return mode;
 }
@@ -466,10 +543,13 @@ static struct attribute *wdt_attrs[] = {
        &dev_attr_state.attr,
        &dev_attr_identity.attr,
        &dev_attr_timeout.attr,
+       &dev_attr_pretimeout.attr,
        &dev_attr_timeleft.attr,
        &dev_attr_bootstatus.attr,
        &dev_attr_status.attr,
        &dev_attr_nowayout.attr,
+       &dev_attr_pretimeout_governor.attr,
+       &dev_attr_pretimeout_available_governors.attr,
        NULL,
 };
 
@@ -646,6 +726,16 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd,
                        break;
                err = put_user(val, p);
                break;
+       case WDIOC_SETPRETIMEOUT:
+               if (get_user(val, p)) {
+                       err = -EFAULT;
+                       break;
+               }
+               err = watchdog_set_pretimeout(wdd, val);
+               break;
+       case WDIOC_GETPRETIMEOUT:
+               err = put_user(wdd->pretimeout, p);
+               break;
        default:
                err = -ENOTTY;
                break;
@@ -937,6 +1027,12 @@ int watchdog_dev_register(struct watchdog_device *wdd)
                return PTR_ERR(dev);
        }
 
+       ret = watchdog_register_pretimeout(wdd);
+       if (ret) {
+               device_destroy(&watchdog_class, devno);
+               watchdog_cdev_unregister(wdd);
+       }
+
        return ret;
 }
 
@@ -950,6 +1046,7 @@ int watchdog_dev_register(struct watchdog_device *wdd)
 
 void watchdog_dev_unregister(struct watchdog_device *wdd)
 {
+       watchdog_unregister_pretimeout(wdd);
        device_destroy(&watchdog_class, wdd->wd_data->cdev.dev);
        watchdog_cdev_unregister(wdd);
 }
diff --git a/drivers/watchdog/watchdog_pretimeout.c b/drivers/watchdog/watchdog_pretimeout.c
new file mode 100644 (file)
index 0000000..9db07bf
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2015-2016 Mentor Graphics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/watchdog.h>
+
+#include "watchdog_pretimeout.h"
+
+/* Default watchdog pretimeout governor */
+static struct watchdog_governor *default_gov;
+
+/* The spinlock protects default_gov, wdd->gov and pretimeout_list */
+static DEFINE_SPINLOCK(pretimeout_lock);
+
+/* List of watchdog devices, which can generate a pretimeout event */
+static LIST_HEAD(pretimeout_list);
+
+struct watchdog_pretimeout {
+       struct watchdog_device          *wdd;
+       struct list_head                entry;
+};
+
+/* The mutex protects governor list and serializes external interfaces */
+static DEFINE_MUTEX(governor_lock);
+
+/* List of the registered watchdog pretimeout governors */
+static LIST_HEAD(governor_list);
+
+struct governor_priv {
+       struct watchdog_governor        *gov;
+       struct list_head                entry;
+};
+
+static struct governor_priv *find_governor_by_name(const char *gov_name)
+{
+       struct governor_priv *priv;
+
+       list_for_each_entry(priv, &governor_list, entry)
+               if (sysfs_streq(gov_name, priv->gov->name))
+                       return priv;
+
+       return NULL;
+}
+
+int watchdog_pretimeout_available_governors_get(char *buf)
+{
+       struct governor_priv *priv;
+       int count = 0;
+
+       mutex_lock(&governor_lock);
+
+       list_for_each_entry(priv, &governor_list, entry)
+               count += sprintf(buf + count, "%s\n", priv->gov->name);
+
+       mutex_unlock(&governor_lock);
+
+       return count;
+}
+
+int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf)
+{
+       int count = 0;
+
+       spin_lock_irq(&pretimeout_lock);
+       if (wdd->gov)
+               count = sprintf(buf, "%s\n", wdd->gov->name);
+       spin_unlock_irq(&pretimeout_lock);
+
+       return count;
+}
+
+int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                    const char *buf)
+{
+       struct governor_priv *priv;
+
+       mutex_lock(&governor_lock);
+
+       priv = find_governor_by_name(buf);
+       if (!priv) {
+               mutex_unlock(&governor_lock);
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&pretimeout_lock);
+       wdd->gov = priv->gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       mutex_unlock(&governor_lock);
+
+       return 0;
+}
+
+void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&pretimeout_lock, flags);
+       if (!wdd->gov) {
+               spin_unlock_irqrestore(&pretimeout_lock, flags);
+               return;
+       }
+
+       wdd->gov->pretimeout(wdd);
+       spin_unlock_irqrestore(&pretimeout_lock, flags);
+}
+EXPORT_SYMBOL_GPL(watchdog_notify_pretimeout);
+
+int watchdog_register_governor(struct watchdog_governor *gov)
+{
+       struct watchdog_pretimeout *p;
+       struct governor_priv *priv;
+
+       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_lock(&governor_lock);
+
+       if (find_governor_by_name(gov->name)) {
+               mutex_unlock(&governor_lock);
+               kfree(priv);
+               return -EBUSY;
+       }
+
+       priv->gov = gov;
+       list_add(&priv->entry, &governor_list);
+
+       if (!strncmp(gov->name, WATCHDOG_PRETIMEOUT_DEFAULT_GOV,
+                    WATCHDOG_GOV_NAME_MAXLEN)) {
+               spin_lock_irq(&pretimeout_lock);
+               default_gov = gov;
+
+               list_for_each_entry(p, &pretimeout_list, entry)
+                       if (!p->wdd->gov)
+                               p->wdd->gov = default_gov;
+               spin_unlock_irq(&pretimeout_lock);
+       }
+
+       mutex_unlock(&governor_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(watchdog_register_governor);
+
+void watchdog_unregister_governor(struct watchdog_governor *gov)
+{
+       struct watchdog_pretimeout *p;
+       struct governor_priv *priv, *t;
+
+       mutex_lock(&governor_lock);
+
+       list_for_each_entry_safe(priv, t, &governor_list, entry) {
+               if (priv->gov == gov) {
+                       list_del(&priv->entry);
+                       kfree(priv);
+                       break;
+               }
+       }
+
+       spin_lock_irq(&pretimeout_lock);
+       list_for_each_entry(p, &pretimeout_list, entry)
+               if (p->wdd->gov == gov)
+                       p->wdd->gov = default_gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       mutex_unlock(&governor_lock);
+}
+EXPORT_SYMBOL(watchdog_unregister_governor);
+
+int watchdog_register_pretimeout(struct watchdog_device *wdd)
+{
+       struct watchdog_pretimeout *p;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return 0;
+
+       p = kzalloc(sizeof(*p), GFP_KERNEL);
+       if (!p)
+               return -ENOMEM;
+
+       spin_lock_irq(&pretimeout_lock);
+       list_add(&p->entry, &pretimeout_list);
+       p->wdd = wdd;
+       wdd->gov = default_gov;
+       spin_unlock_irq(&pretimeout_lock);
+
+       return 0;
+}
+
+void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
+{
+       struct watchdog_pretimeout *p, *t;
+
+       if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+               return;
+
+       spin_lock_irq(&pretimeout_lock);
+       wdd->gov = NULL;
+
+       list_for_each_entry_safe(p, t, &pretimeout_list, entry) {
+               if (p->wdd == wdd) {
+                       list_del(&p->entry);
+                       break;
+               }
+       }
+       spin_unlock_irq(&pretimeout_lock);
+
+       kfree(p);
+}
diff --git a/drivers/watchdog/watchdog_pretimeout.h b/drivers/watchdog/watchdog_pretimeout.h
new file mode 100644 (file)
index 0000000..a5a32b3
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __WATCHDOG_PRETIMEOUT_H
+#define __WATCHDOG_PRETIMEOUT_H
+
+#define WATCHDOG_GOV_NAME_MAXLEN       20
+
+struct watchdog_device;
+
+struct watchdog_governor {
+       const char      name[WATCHDOG_GOV_NAME_MAXLEN];
+       void            (*pretimeout)(struct watchdog_device *wdd);
+};
+
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
+/* Interfaces to watchdog pretimeout governors */
+int watchdog_register_governor(struct watchdog_governor *gov);
+void watchdog_unregister_governor(struct watchdog_governor *gov);
+
+/* Interfaces to watchdog_dev.c */
+int watchdog_register_pretimeout(struct watchdog_device *wdd);
+void watchdog_unregister_pretimeout(struct watchdog_device *wdd);
+int watchdog_pretimeout_available_governors_get(char *buf);
+int watchdog_pretimeout_governor_get(struct watchdog_device *wdd, char *buf);
+int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                    const char *buf);
+
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_NOOP)
+#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV                "noop"
+#elif IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_DEFAULT_GOV_PANIC)
+#define WATCHDOG_PRETIMEOUT_DEFAULT_GOV                "panic"
+#endif
+
+#else
+static inline int watchdog_register_pretimeout(struct watchdog_device *wdd)
+{
+       return 0;
+}
+
+static inline void watchdog_unregister_pretimeout(struct watchdog_device *wdd)
+{
+}
+
+static inline int watchdog_pretimeout_available_governors_get(char *buf)
+{
+       return -EINVAL;
+}
+
+static inline int watchdog_pretimeout_governor_get(struct watchdog_device *wdd,
+                                                  char *buf)
+{
+       return -EINVAL;
+}
+
+static inline int watchdog_pretimeout_governor_set(struct watchdog_device *wdd,
+                                                  const char *buf)
+{
+       return -EINVAL;
+}
+#endif
+
+#endif
index fa1efef..b4e0cea 100644 (file)
  * GNU General Public License for more details.
  */
 
+#include <linux/delay.h>
 #include <linux/i2c.h>
+#include <linux/ihex.h>
+#include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -36,6 +39,8 @@
 #define ZIIRAVE_STATE_OFF      0x1
 #define ZIIRAVE_STATE_ON       0x2
 
+#define ZIIRAVE_FW_NAME                "ziirave_wdt.fw"
+
 static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
                                  "host request", NULL, "illegal configuration",
                                  "illegal instruction", "illegal trap",
@@ -50,12 +55,35 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
 #define ZIIRAVE_WDT_PING               0x9
 #define ZIIRAVE_WDT_RESET_DURATION     0xa
 
+#define ZIIRAVE_FIRM_PKT_TOTAL_SIZE    20
+#define ZIIRAVE_FIRM_PKT_DATA_SIZE     16
+#define ZIIRAVE_FIRM_FLASH_MEMORY_START        0x1600
+#define ZIIRAVE_FIRM_FLASH_MEMORY_END  0x2bbf
+
+/* Received and ready for next Download packet. */
+#define ZIIRAVE_FIRM_DOWNLOAD_ACK      1
+/* Currently writing to flash. Retry Download status in a moment! */
+#define ZIIRAVE_FIRM_DOWNLOAD_BUSY     2
+
+/* Wait for ACK timeout in ms */
+#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT      50
+
+/* Firmware commands */
+#define ZIIRAVE_CMD_DOWNLOAD_START             0x10
+#define ZIIRAVE_CMD_DOWNLOAD_END               0x11
+#define ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR     0x12
+#define ZIIRAVE_CMD_DOWNLOAD_READ_BYTE         0x13
+#define ZIIRAVE_CMD_RESET_PROCESSOR            0x0b
+#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER         0x0c
+#define ZIIRAVE_CMD_DOWNLOAD_PACKET            0x0e
+
 struct ziirave_wdt_rev {
        unsigned char major;
        unsigned char minor;
 };
 
 struct ziirave_wdt_data {
+       struct mutex sysfs_mutex;
        struct watchdog_device wdd;
        struct ziirave_wdt_rev bootloader_rev;
        struct ziirave_wdt_rev firmware_rev;
@@ -146,6 +174,293 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
        return ret;
 }
 
+static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret;
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT);
+       do {
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+
+               usleep_range(5000, 10000);
+
+               ret = i2c_smbus_read_byte(client);
+               if (ret < 0) {
+                       dev_err(&client->dev, "Failed to read byte\n");
+                       return ret;
+               }
+       } while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY);
+
+       return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
+}
+
+static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       u8 address[2];
+
+       address[0] = addr & 0xff;
+       address[1] = (addr >> 8) & 0xff;
+
+       return i2c_smbus_write_block_data(client,
+                                         ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
+                                         ARRAY_SIZE(address), address);
+}
+
+static int ziirave_firm_write_block_data(struct watchdog_device *wdd,
+                                        u8 command, u8 length, const u8 *data,
+                                        bool wait_for_ack)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret;
+
+       ret = i2c_smbus_write_block_data(client, command, length, data);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to send command 0x%02x: %d\n", command, ret);
+               return ret;
+       }
+
+       if (wait_for_ack)
+               ret = ziirave_firm_wait_for_ack(wdd);
+
+       return ret;
+}
+
+static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
+                                  u8 byte, bool wait_for_ack)
+{
+       return ziirave_firm_write_block_data(wdd, command, 1, &byte,
+                                            wait_for_ack);
+}
+
+/*
+ * ziirave_firm_write_pkt() - Build and write a firmware packet
+ *
+ * A packet to send to the firmware is composed by following bytes:
+ *     Length | Addr0 | Addr1 | Data0 .. Data15 | Checksum |
+ * Where,
+ *     Length: A data byte containing the length of the data.
+ *     Addr0: Low byte of the address.
+ *     Addr1: High byte of the address.
+ *     Data0 .. Data15: Array of 16 bytes of data.
+ *     Checksum: Checksum byte to verify data integrity.
+ */
+static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
+                                 const struct ihex_binrec *rec)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
+       int ret;
+       u16 addr;
+
+       memset(packet, 0, ARRAY_SIZE(packet));
+
+       /* Packet length */
+       packet[0] = (u8)be16_to_cpu(rec->len);
+       /* Packet address */
+       addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
+       packet[1] = addr & 0xff;
+       packet[2] = (addr & 0xff00) >> 8;
+
+       /* Packet data */
+       if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE)
+               return -EMSGSIZE;
+       memcpy(packet + 3, rec->data, be16_to_cpu(rec->len));
+
+       /* Packet checksum */
+       for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++)
+               checksum += packet[i];
+       packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
+
+       ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET,
+                                           ARRAY_SIZE(packet), packet, true);
+       if (ret)
+               dev_err(&client->dev,
+                     "Failed to write firmware packet at address 0x%04x: %d\n",
+                     addr, ret);
+
+       return ret;
+}
+
+static int ziirave_firm_verify(struct watchdog_device *wdd,
+                              const struct firmware *fw)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       const struct ihex_binrec *rec;
+       int i, ret;
+       u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
+       u16 addr;
+
+       for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+               /* Zero length marks end of records */
+               if (!be16_to_cpu(rec->len))
+                       break;
+
+               addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
+               if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
+                   addr > ZIIRAVE_FIRM_FLASH_MEMORY_END)
+                       continue;
+
+               ret = ziirave_firm_set_read_addr(wdd, addr);
+               if (ret) {
+                       dev_err(&client->dev,
+                               "Failed to send SET_READ_ADDR command: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               for (i = 0; i < ARRAY_SIZE(data); i++) {
+                       ret = i2c_smbus_read_byte_data(client,
+                                               ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
+                       if (ret < 0) {
+                               dev_err(&client->dev,
+                                       "Failed to READ DATA: %d\n", ret);
+                               return ret;
+                       }
+                       data[i] = ret;
+               }
+
+               if (memcmp(data, rec->data, be16_to_cpu(rec->len))) {
+                       dev_err(&client->dev,
+                               "Firmware mismatch at address 0x%04x\n", addr);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+static int ziirave_firm_upload(struct watchdog_device *wdd,
+                              const struct firmware *fw)
+{
+       struct i2c_client *client = to_i2c_client(wdd->parent);
+       int ret, words_till_page_break;
+       const struct ihex_binrec *rec;
+       struct ihex_binrec *rec_new;
+
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1,
+                                     false);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+               /* Zero length marks end of records */
+               if (!be16_to_cpu(rec->len))
+                       break;
+
+               /* Check max data size */
+               if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
+                       dev_err(&client->dev, "Firmware packet too long (%d)\n",
+                               be16_to_cpu(rec->len));
+                       return -EMSGSIZE;
+               }
+
+               /* Calculate words till page break */
+               words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) &
+                                        0x3f));
+               if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) {
+                       /*
+                        * Data in passes page boundary, so we need to split in
+                        * two blocks of data. Create a packet with the first
+                        * block of data.
+                        */
+                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
+                                         (words_till_page_break << 1),
+                                         GFP_KERNEL);
+                       if (!rec_new)
+                               return -ENOMEM;
+
+                       rec_new->len = cpu_to_be16(words_till_page_break << 1);
+                       rec_new->addr = rec->addr;
+                       memcpy(rec_new->data, rec->data,
+                              be16_to_cpu(rec_new->len));
+
+                       ret = ziirave_firm_write_pkt(wdd, rec_new);
+                       kfree(rec_new);
+                       if (ret)
+                               return ret;
+
+                       /* Create a packet with the second block of data */
+                       rec_new = kzalloc(sizeof(struct ihex_binrec) +
+                                         be16_to_cpu(rec->len) -
+                                         (words_till_page_break << 1),
+                                         GFP_KERNEL);
+                       if (!rec_new)
+                               return -ENOMEM;
+
+                       /* Remaining bytes */
+                       rec_new->len = rec->len -
+                                      cpu_to_be16(words_till_page_break << 1);
+
+                       rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) +
+                                       (words_till_page_break << 1));
+
+                       memcpy(rec_new->data,
+                              rec->data + (words_till_page_break << 1),
+                              be16_to_cpu(rec_new->len));
+
+                       ret = ziirave_firm_write_pkt(wdd, rec_new);
+                       kfree(rec_new);
+                       if (ret)
+                               return ret;
+               } else {
+                       ret = ziirave_firm_write_pkt(wdd, rec);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       /* For end of download, the length field will be set to 0 */
+       rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL);
+       if (!rec_new)
+               return -ENOMEM;
+
+       ret = ziirave_firm_write_pkt(wdd, rec_new);
+       kfree(rec_new);
+       if (ret) {
+               dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret);
+               return ret;
+       }
+
+       /* This sleep seems to be required */
+       msleep(20);
+
+       /* Start firmware verification */
+       ret = ziirave_firm_verify(wdd, fw);
+       if (ret) {
+               dev_err(&client->dev,
+                       "Failed to verify firmware: %d\n", ret);
+               return ret;
+       }
+
+       /* End download operation */
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false);
+       if (ret)
+               return ret;
+
+       /* Reset the processor */
+       ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1,
+                                     false);
+       if (ret)
+               return ret;
+
+       msleep(500);
+
+       return 0;
+}
+
 static const struct watchdog_info ziirave_wdt_info = {
        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
        .identity = "Zodiac RAVE Watchdog",
@@ -166,9 +481,18 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
+
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
+                     w_priv->firmware_rev.minor);
 
-       return sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
-                      w_priv->firmware_rev.minor);
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(firmware_version, S_IRUGO, ziirave_wdt_sysfs_show_firm,
@@ -180,9 +504,18 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
 
-       return sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
-                      w_priv->bootloader_rev.minor);
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
+                     w_priv->bootloader_rev.minor);
+
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(bootloader_version, S_IRUGO, ziirave_wdt_sysfs_show_boot,
@@ -194,17 +527,81 @@ static ssize_t ziirave_wdt_sysfs_show_reason(struct device *dev,
 {
        struct i2c_client *client = to_i2c_client(dev->parent);
        struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       int ret;
+
+       ret = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (ret)
+               return ret;
+
+       ret = sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
 
-       return sprintf(buf, "%s", ziirave_reasons[w_priv->reset_reason]);
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+       return ret;
 }
 
 static DEVICE_ATTR(reset_reason, S_IRUGO, ziirave_wdt_sysfs_show_reason,
                   NULL);
 
+static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
+                                           struct device_attribute *attr,
+                                           const char *buf, size_t count)
+{
+       struct i2c_client *client = to_i2c_client(dev->parent);
+       struct ziirave_wdt_data *w_priv = i2c_get_clientdata(client);
+       const struct firmware *fw;
+       int err;
+
+       err = request_ihex_firmware(&fw, ZIIRAVE_FW_NAME, dev);
+       if (err) {
+               dev_err(&client->dev, "Failed to request ihex firmware\n");
+               return err;
+       }
+
+       err = mutex_lock_interruptible(&w_priv->sysfs_mutex);
+       if (err)
+               goto release_firmware;
+
+       err = ziirave_firm_upload(&w_priv->wdd, fw);
+       if (err) {
+               dev_err(&client->dev, "The firmware update failed: %d\n", err);
+               goto unlock_mutex;
+       }
+
+       /* Update firmware version */
+       err = ziirave_wdt_revision(client, &w_priv->firmware_rev,
+                                  ZIIRAVE_WDT_FIRM_VER_MAJOR);
+       if (err) {
+               dev_err(&client->dev, "Failed to read firmware version: %d\n",
+                       err);
+               goto unlock_mutex;
+       }
+
+       dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n",
+                w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
+
+       /* Restore the watchdog timeout */
+       err = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
+       if (err)
+               dev_err(&client->dev, "Failed to set timeout: %d\n", err);
+
+unlock_mutex:
+       mutex_unlock(&w_priv->sysfs_mutex);
+
+release_firmware:
+       release_firmware(fw);
+
+       return err ? err : count;
+}
+
+static DEVICE_ATTR(update_firmware, S_IWUSR, NULL,
+                  ziirave_wdt_sysfs_store_firm);
+
 static struct attribute *ziirave_wdt_attrs[] = {
        &dev_attr_firmware_version.attr,
        &dev_attr_bootloader_version.attr,
        &dev_attr_reset_reason.attr,
+       &dev_attr_update_firmware.attr,
        NULL
 };
 ATTRIBUTE_GROUPS(ziirave_wdt);
@@ -252,6 +649,8 @@ static int ziirave_wdt_probe(struct i2c_client *client,
        if (!w_priv)
                return -ENOMEM;
 
+       mutex_init(&w_priv->sysfs_mutex);
+
        w_priv->wdd.info = &ziirave_wdt_info;
        w_priv->wdd.ops = &ziirave_wdt_ops;
        w_priv->wdd.min_timeout = ZIIRAVE_TIMEOUT_MIN;
index 14d506e..f865c3f 100644 (file)
@@ -398,8 +398,7 @@ no_more:
                switch (ret) {
                case -EDQUOT:
                case -ENOSPC:
-                       set_bit(AS_ENOSPC,
-                               &wb->vnode->vfs_inode.i_mapping->flags);
+                       mapping_set_error(wb->vnode->vfs_inode.i_mapping, -ENOSPC);
                        break;
                case -EROFS:
                case -EIO:
@@ -409,7 +408,7 @@ no_more:
                case -ENOMEDIUM:
                case -ENXIO:
                        afs_kill_pages(wb->vnode, true, first, last);
-                       set_bit(AS_EIO, &wb->vnode->vfs_inode.i_mapping->flags);
+                       mapping_set_error(wb->vnode->vfs_inode.i_mapping, -EIO);
                        break;
                case -EACCES:
                case -EPERM:
index a439548..a1fba42 100644 (file)
@@ -20,7 +20,8 @@
 #define AUTOFS_IOC_COUNT     32
 
 #define AUTOFS_DEV_IOCTL_IOC_FIRST     (AUTOFS_DEV_IOCTL_VERSION)
-#define AUTOFS_DEV_IOCTL_IOC_COUNT     (AUTOFS_IOC_COUNT - 11)
+#define AUTOFS_DEV_IOCTL_IOC_COUNT \
+       (AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD - AUTOFS_DEV_IOCTL_VERSION_CMD)
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -33,8 +34,6 @@
 #include <asm/current.h>
 #include <linux/uaccess.h>
 
-/* #define DEBUG */
-
 #ifdef pr_fmt
 #undef pr_fmt
 #endif
@@ -111,8 +110,6 @@ struct autofs_sb_info {
        int max_proto;
        unsigned long exp_timeout;
        unsigned int type;
-       int reghost_enabled;
-       int needs_reghost;
        struct super_block *sb;
        struct mutex wq_mutex;
        struct mutex pipe_mutex;
@@ -271,4 +268,4 @@ static inline void autofs4_del_expiring(struct dentry *dentry)
        }
 }
 
-extern void autofs4_kill_sb(struct super_block *);
+void autofs4_kill_sb(struct super_block *);
index c7fcc74..fc09eb7 100644 (file)
@@ -75,7 +75,7 @@ static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param)
        if ((param->ver_major != AUTOFS_DEV_IOCTL_VERSION_MAJOR) ||
            (param->ver_minor > AUTOFS_DEV_IOCTL_VERSION_MINOR)) {
                pr_warn("ioctl control interface version mismatch: "
-                       "kernel(%u.%u), user(%u.%u), cmd(%d)\n",
+                       "kernel(%u.%u), user(%u.%u), cmd(0x%08x)\n",
                        AUTOFS_DEV_IOCTL_VERSION_MAJOR,
                        AUTOFS_DEV_IOCTL_VERSION_MINOR,
                        param->ver_major, param->ver_minor, cmd);
@@ -172,6 +172,17 @@ static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f)
        return sbi;
 }
 
+/* Return autofs dev ioctl version */
+static int autofs_dev_ioctl_version(struct file *fp,
+                                   struct autofs_sb_info *sbi,
+                                   struct autofs_dev_ioctl *param)
+{
+       /* This should have already been set. */
+       param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
+       param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
+       return 0;
+}
+
 /* Return autofs module protocol version */
 static int autofs_dev_ioctl_protover(struct file *fp,
                                     struct autofs_sb_info *sbi,
@@ -586,41 +597,25 @@ out:
 
 static ioctl_fn lookup_dev_ioctl(unsigned int cmd)
 {
-       static struct {
-               int cmd;
-               ioctl_fn fn;
-       } _ioctls[] = {
-               {cmd_idx(AUTOFS_DEV_IOCTL_VERSION_CMD), NULL},
-               {cmd_idx(AUTOFS_DEV_IOCTL_PROTOVER_CMD),
-                        autofs_dev_ioctl_protover},
-               {cmd_idx(AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD),
-                        autofs_dev_ioctl_protosubver},
-               {cmd_idx(AUTOFS_DEV_IOCTL_OPENMOUNT_CMD),
-                        autofs_dev_ioctl_openmount},
-               {cmd_idx(AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD),
-                        autofs_dev_ioctl_closemount},
-               {cmd_idx(AUTOFS_DEV_IOCTL_READY_CMD),
-                        autofs_dev_ioctl_ready},
-               {cmd_idx(AUTOFS_DEV_IOCTL_FAIL_CMD),
-                        autofs_dev_ioctl_fail},
-               {cmd_idx(AUTOFS_DEV_IOCTL_SETPIPEFD_CMD),
-                        autofs_dev_ioctl_setpipefd},
-               {cmd_idx(AUTOFS_DEV_IOCTL_CATATONIC_CMD),
-                        autofs_dev_ioctl_catatonic},
-               {cmd_idx(AUTOFS_DEV_IOCTL_TIMEOUT_CMD),
-                        autofs_dev_ioctl_timeout},
-               {cmd_idx(AUTOFS_DEV_IOCTL_REQUESTER_CMD),
-                        autofs_dev_ioctl_requester},
-               {cmd_idx(AUTOFS_DEV_IOCTL_EXPIRE_CMD),
-                        autofs_dev_ioctl_expire},
-               {cmd_idx(AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD),
-                        autofs_dev_ioctl_askumount},
-               {cmd_idx(AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD),
-                        autofs_dev_ioctl_ismountpoint}
+       static ioctl_fn _ioctls[] = {
+               autofs_dev_ioctl_version,
+               autofs_dev_ioctl_protover,
+               autofs_dev_ioctl_protosubver,
+               autofs_dev_ioctl_openmount,
+               autofs_dev_ioctl_closemount,
+               autofs_dev_ioctl_ready,
+               autofs_dev_ioctl_fail,
+               autofs_dev_ioctl_setpipefd,
+               autofs_dev_ioctl_catatonic,
+               autofs_dev_ioctl_timeout,
+               autofs_dev_ioctl_requester,
+               autofs_dev_ioctl_expire,
+               autofs_dev_ioctl_askumount,
+               autofs_dev_ioctl_ismountpoint,
        };
        unsigned int idx = cmd_idx(cmd);
 
-       return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx].fn;
+       return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx];
 }
 
 /* ioctl dispatcher */
@@ -642,7 +637,7 @@ static int _autofs_dev_ioctl(unsigned int command,
        cmd = _IOC_NR(command);
 
        if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) ||
-           cmd - cmd_first >= AUTOFS_DEV_IOCTL_IOC_COUNT) {
+           cmd - cmd_first > AUTOFS_DEV_IOCTL_IOC_COUNT) {
                return -ENOTTY;
        }
 
@@ -655,14 +650,11 @@ static int _autofs_dev_ioctl(unsigned int command,
        if (err)
                goto out;
 
-       /* The validate routine above always sets the version */
-       if (cmd == AUTOFS_DEV_IOCTL_VERSION_CMD)
-               goto done;
-
        fn = lookup_dev_ioctl(cmd);
        if (!fn) {
                pr_warn("unknown command 0x%08x\n", command);
-               return -ENOTTY;
+               err = -ENOTTY;
+               goto out;
        }
 
        fp = NULL;
@@ -671,9 +663,11 @@ static int _autofs_dev_ioctl(unsigned int command,
        /*
         * For obvious reasons the openmount can't have a file
         * descriptor yet. We don't take a reference to the
-        * file during close to allow for immediate release.
+        * file during close to allow for immediate release,
+        * and the same for retrieving ioctl version.
         */
-       if (cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD &&
+       if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD &&
+           cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD &&
            cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) {
                fp = fget(param->ioctlfd);
                if (!fp) {
@@ -706,7 +700,6 @@ cont:
 
        if (fp)
                fput(fp);
-done:
        if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE))
                err = -EFAULT;
 out:
index ca9cbd6..438b5bf 100644 (file)
@@ -274,6 +274,23 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
                goto fail_dput;
        }
 
+       /* Test versions first */
+       if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
+           sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
+               pr_err("kernel does not match daemon version "
+                      "daemon (%d, %d) kernel (%d, %d)\n",
+                      sbi->min_proto, sbi->max_proto,
+                      AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
+               goto fail_dput;
+       }
+
+       /* Establish highest kernel protocol version */
+       if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION)
+               sbi->version = AUTOFS_MAX_PROTO_VERSION;
+       else
+               sbi->version = sbi->max_proto;
+       sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
+
        if (pgrp_set) {
                sbi->oz_pgrp = find_get_pid(pgrp);
                if (!sbi->oz_pgrp) {
@@ -291,29 +308,12 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
        root_inode->i_fop = &autofs4_root_operations;
        root_inode->i_op = &autofs4_dir_inode_operations;
 
-       /* Couldn't this be tested earlier? */
-       if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
-           sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
-               pr_err("kernel does not match daemon version "
-                      "daemon (%d, %d) kernel (%d, %d)\n",
-                      sbi->min_proto, sbi->max_proto,
-                      AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
-               goto fail_dput;
-       }
-
-       /* Establish highest kernel protocol version */
-       if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION)
-               sbi->version = AUTOFS_MAX_PROTO_VERSION;
-       else
-               sbi->version = sbi->max_proto;
-       sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
-
        pr_debug("pipe fd = %d, pgrp = %u\n", pipefd, pid_nr(sbi->oz_pgrp));
        pipe = fget(pipefd);
 
        if (!pipe) {
                pr_err("could not open pipe file descriptor\n");
-               goto fail_dput;
+               goto fail_put_pid;
        }
        ret = autofs_prepare_pipe(pipe);
        if (ret < 0)
@@ -334,14 +334,14 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
 fail_fput:
        pr_err("pipe file descriptor does not contain proper ops\n");
        fput(pipe);
-       /* fall through */
+fail_put_pid:
+       put_pid(sbi->oz_pgrp);
 fail_dput:
        dput(root);
        goto fail_free;
 fail_ino:
-       kfree(ino);
+       autofs4_free_ino(ino);
 fail_free:
-       put_pid(sbi->oz_pgrp);
        kfree(sbi);
        s->s_fs_info = NULL;
        return ret;
@@ -368,7 +368,8 @@ struct inode *autofs4_get_inode(struct super_block *sb, umode_t mode)
                inode->i_fop = &autofs4_dir_operations;
        } else if (S_ISLNK(mode)) {
                inode->i_op = &autofs4_symlink_inode_operations;
-       }
+       } else
+               WARN_ON(1);
 
        return inode;
 }
index 623510e..a11f731 100644 (file)
@@ -577,8 +577,6 @@ static int autofs4_dir_symlink(struct inode *dir,
        inode = autofs4_get_inode(dir->i_sb, S_IFLNK | 0555);
        if (!inode) {
                kfree(cp);
-               if (!dentry->d_fsdata)
-                       kfree(ino);
                return -ENOMEM;
        }
        inode->i_private = cp;
@@ -842,7 +840,7 @@ static inline int autofs4_ask_umount(struct vfsmount *mnt, int __user *p)
        if (may_umount(mnt))
                status = 1;
 
-       pr_debug("returning %d\n", status);
+       pr_debug("may umount %d\n", status);
 
        status = put_user(status, p);
 
index 376e4e4..05b5533 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/cleancache.h>
 #include <linux/dax.h>
 #include <linux/badblocks.h>
+#include <linux/falloc.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -1775,6 +1776,81 @@ static const struct address_space_operations def_blk_aops = {
        .is_dirty_writeback = buffer_check_dirty_writeback,
 };
 
+#define        BLKDEV_FALLOC_FL_SUPPORTED                                      \
+               (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |           \
+                FALLOC_FL_ZERO_RANGE | FALLOC_FL_NO_HIDE_STALE)
+
+static long blkdev_fallocate(struct file *file, int mode, loff_t start,
+                            loff_t len)
+{
+       struct block_device *bdev = I_BDEV(bdev_file_inode(file));
+       struct request_queue *q = bdev_get_queue(bdev);
+       struct address_space *mapping;
+       loff_t end = start + len - 1;
+       loff_t isize;
+       int error;
+
+       /* Fail if we don't recognize the flags. */
+       if (mode & ~BLKDEV_FALLOC_FL_SUPPORTED)
+               return -EOPNOTSUPP;
+
+       /* Don't go off the end of the device. */
+       isize = i_size_read(bdev->bd_inode);
+       if (start >= isize)
+               return -EINVAL;
+       if (end >= isize) {
+               if (mode & FALLOC_FL_KEEP_SIZE) {
+                       len = isize - start;
+                       end = start + len - 1;
+               } else
+                       return -EINVAL;
+       }
+
+       /*
+        * Don't allow IO that isn't aligned to logical block size.
+        */
+       if ((start | len) & (bdev_logical_block_size(bdev) - 1))
+               return -EINVAL;
+
+       /* Invalidate the page cache, including dirty pages. */
+       mapping = bdev->bd_inode->i_mapping;
+       truncate_inode_pages_range(mapping, start, end);
+
+       switch (mode) {
+       case FALLOC_FL_ZERO_RANGE:
+       case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
+               error = blkdev_issue_zeroout(bdev, start >> 9, len >> 9,
+                                           GFP_KERNEL, false);
+               break;
+       case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
+               /* Only punch if the device can do zeroing discard. */
+               if (!blk_queue_discard(q) || !q->limits.discard_zeroes_data)
+                       return -EOPNOTSUPP;
+               error = blkdev_issue_discard(bdev, start >> 9, len >> 9,
+                                            GFP_KERNEL, 0);
+               break;
+       case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE | FALLOC_FL_NO_HIDE_STALE:
+               if (!blk_queue_discard(q))
+                       return -EOPNOTSUPP;
+               error = blkdev_issue_discard(bdev, start >> 9, len >> 9,
+                                            GFP_KERNEL, 0);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       if (error)
+               return error;
+
+       /*
+        * Invalidate again; if someone wandered in and dirtied a page,
+        * the caller will be given -EBUSY.  The third argument is
+        * inclusive, so the rounding here is safe.
+        */
+       return invalidate_inode_pages2_range(mapping,
+                                            start >> PAGE_SHIFT,
+                                            end >> PAGE_SHIFT);
+}
+
 const struct file_operations def_blk_fops = {
        .open           = blkdev_open,
        .release        = blkdev_close,
@@ -1789,6 +1865,7 @@ const struct file_operations def_blk_fops = {
 #endif
        .splice_read    = generic_file_splice_read,
        .splice_write   = iter_file_splice_write,
+       .fallocate      = blkdev_fallocate,
 };
 
 int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
index 6c21bad..0b8ce2b 100644 (file)
@@ -252,7 +252,8 @@ struct btrfs_super_block {
 #define BTRFS_FEATURE_COMPAT_SAFE_CLEAR                0ULL
 
 #define BTRFS_FEATURE_COMPAT_RO_SUPP                   \
-       (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE)
+       (BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE |      \
+        BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID)
 
 #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET       0ULL
 #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR     0ULL
index e720d3e..3a57f99 100644 (file)
@@ -2586,6 +2586,7 @@ int open_ctree(struct super_block *sb,
        int num_backups_tried = 0;
        int backup_index = 0;
        int max_active;
+       int clear_free_space_tree = 0;
 
        tree_root = fs_info->tree_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
        chunk_root = fs_info->chunk_root = btrfs_alloc_root(fs_info, GFP_KERNEL);
@@ -3148,6 +3149,26 @@ retry_root_backup:
        if (sb->s_flags & MS_RDONLY)
                return 0;
 
+       if (btrfs_test_opt(fs_info, CLEAR_CACHE) &&
+           btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
+               clear_free_space_tree = 1;
+       } else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
+                  !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID)) {
+               btrfs_warn(fs_info, "free space tree is invalid");
+               clear_free_space_tree = 1;
+       }
+
+       if (clear_free_space_tree) {
+               btrfs_info(fs_info, "clearing free space tree");
+               ret = btrfs_clear_free_space_tree(fs_info);
+               if (ret) {
+                       btrfs_warn(fs_info,
+                                  "failed to clear free space tree: %d", ret);
+                       close_ctree(tree_root);
+                       return ret;
+               }
+       }
+
        if (btrfs_test_opt(tree_root->fs_info, FREE_SPACE_TREE) &&
            !btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
                btrfs_info(fs_info, "creating free space tree");
@@ -3185,18 +3206,6 @@ retry_root_backup:
 
        btrfs_qgroup_rescan_resume(fs_info);
 
-       if (btrfs_test_opt(tree_root->fs_info, CLEAR_CACHE) &&
-           btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE)) {
-               btrfs_info(fs_info, "clearing free space tree");
-               ret = btrfs_clear_free_space_tree(fs_info);
-               if (ret) {
-                       btrfs_warn(fs_info,
-                               "failed to clear free space tree: %d", ret);
-                       close_ctree(tree_root);
-                       return ret;
-               }
-       }
-
        if (!fs_info->uuid_root) {
                btrfs_info(fs_info, "creating UUID tree");
                ret = btrfs_create_uuid_tree(fs_info);
index ee40384..66a7551 100644 (file)
@@ -5558,17 +5558,45 @@ void copy_extent_buffer(struct extent_buffer *dst, struct extent_buffer *src,
        }
 }
 
-/*
- * The extent buffer bitmap operations are done with byte granularity because
- * bitmap items are not guaranteed to be aligned to a word and therefore a
- * single word in a bitmap may straddle two pages in the extent buffer.
- */
-#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
-#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
-#define BITMAP_FIRST_BYTE_MASK(start) \
-       ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
-#define BITMAP_LAST_BYTE_MASK(nbits) \
-       (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
+void le_bitmap_set(u8 *map, unsigned int start, int len)
+{
+       u8 *p = map + BIT_BYTE(start);
+       const unsigned int size = start + len;
+       int bits_to_set = BITS_PER_BYTE - (start % BITS_PER_BYTE);
+       u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(start);
+
+       while (len - bits_to_set >= 0) {
+               *p |= mask_to_set;
+               len -= bits_to_set;
+               bits_to_set = BITS_PER_BYTE;
+               mask_to_set = ~(u8)0;
+               p++;
+       }
+       if (len) {
+               mask_to_set &= BITMAP_LAST_BYTE_MASK(size);
+               *p |= mask_to_set;
+       }
+}
+
+void le_bitmap_clear(u8 *map, unsigned int start, int len)
+{
+       u8 *p = map + BIT_BYTE(start);
+       const unsigned int size = start + len;
+       int bits_to_clear = BITS_PER_BYTE - (start % BITS_PER_BYTE);
+       u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(start);
+
+       while (len - bits_to_clear >= 0) {
+               *p &= ~mask_to_clear;
+               len -= bits_to_clear;
+               bits_to_clear = BITS_PER_BYTE;
+               mask_to_clear = ~(u8)0;
+               p++;
+       }
+       if (len) {
+               mask_to_clear &= BITMAP_LAST_BYTE_MASK(size);
+               *p &= ~mask_to_clear;
+       }
+}
 
 /*
  * eb_bitmap_offset() - calculate the page and offset of the byte containing the
@@ -5612,7 +5640,7 @@ static inline void eb_bitmap_offset(struct extent_buffer *eb,
 int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
                           unsigned long nr)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
@@ -5634,13 +5662,13 @@ int extent_buffer_test_bit(struct extent_buffer *eb, unsigned long start,
 void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
                              unsigned long pos, unsigned long len)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
        const unsigned int size = pos + len;
        int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       unsigned int mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
+       u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
 
        eb_bitmap_offset(eb, start, pos, &i, &offset);
        page = eb->pages[i];
@@ -5651,7 +5679,7 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
                kaddr[offset] |= mask_to_set;
                len -= bits_to_set;
                bits_to_set = BITS_PER_BYTE;
-               mask_to_set = ~0U;
+               mask_to_set = ~(u8)0;
                if (++offset >= PAGE_SIZE && len > 0) {
                        offset = 0;
                        page = eb->pages[++i];
@@ -5676,13 +5704,13 @@ void extent_buffer_bitmap_set(struct extent_buffer *eb, unsigned long start,
 void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
                                unsigned long pos, unsigned long len)
 {
-       char *kaddr;
+       u8 *kaddr;
        struct page *page;
        unsigned long i;
        size_t offset;
        const unsigned int size = pos + len;
        int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       unsigned int mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
+       u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
 
        eb_bitmap_offset(eb, start, pos, &i, &offset);
        page = eb->pages[i];
@@ -5693,7 +5721,7 @@ void extent_buffer_bitmap_clear(struct extent_buffer *eb, unsigned long start,
                kaddr[offset] &= ~mask_to_clear;
                len -= bits_to_clear;
                bits_to_clear = BITS_PER_BYTE;
-               mask_to_clear = ~0U;
+               mask_to_clear = ~(u8)0;
                if (++offset >= PAGE_SIZE && len > 0) {
                        offset = 0;
                        page = eb->pages[++i];
index 4a094f1..ab31d14 100644 (file)
  */
 #define EXTENT_PAGE_PRIVATE 1
 
+/*
+ * The extent buffer bitmap operations are done with byte granularity instead of
+ * word granularity for two reasons:
+ * 1. The bitmaps must be little-endian on disk.
+ * 2. Bitmap items are not guaranteed to be aligned to a word and therefore a
+ *    single word in a bitmap may straddle two pages in the extent buffer.
+ */
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+#define BYTE_MASK ((1 << BITS_PER_BYTE) - 1)
+#define BITMAP_FIRST_BYTE_MASK(start) \
+       ((BYTE_MASK << ((start) & (BITS_PER_BYTE - 1))) & BYTE_MASK)
+#define BITMAP_LAST_BYTE_MASK(nbits) \
+       (BYTE_MASK >> (-(nbits) & (BITS_PER_BYTE - 1)))
+
+static inline int le_test_bit(int nr, const u8 *addr)
+{
+       return 1U & (addr[BIT_BYTE(nr)] >> (nr & (BITS_PER_BYTE-1)));
+}
+
+extern void le_bitmap_set(u8 *map, unsigned int start, int len);
+extern void le_bitmap_clear(u8 *map, unsigned int start, int len);
+
 struct extent_state;
 struct btrfs_root;
 struct btrfs_io_bio;
index e4a42a8..57401b4 100644 (file)
@@ -151,7 +151,7 @@ static inline u32 free_space_bitmap_size(u64 size, u32 sectorsize)
        return DIV_ROUND_UP((u32)div_u64(size, sectorsize), BITS_PER_BYTE);
 }
 
-static unsigned long *alloc_bitmap(u32 bitmap_size)
+static u8 *alloc_bitmap(u32 bitmap_size)
 {
        void *mem;
 
@@ -180,8 +180,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
        struct btrfs_free_space_info *info;
        struct btrfs_key key, found_key;
        struct extent_buffer *leaf;
-       unsigned long *bitmap;
-       char *bitmap_cursor;
+       u8 *bitmap, *bitmap_cursor;
        u64 start, end;
        u64 bitmap_range, i;
        u32 bitmap_size, flags, expected_extent_count;
@@ -231,7 +230,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                                                block_group->sectorsize);
                                last = div_u64(found_key.objectid + found_key.offset - start,
                                               block_group->sectorsize);
-                               bitmap_set(bitmap, first, last - first);
+                               le_bitmap_set(bitmap, first, last - first);
 
                                extent_count++;
                                nr++;
@@ -270,7 +269,7 @@ int convert_free_space_to_bitmaps(struct btrfs_trans_handle *trans,
                goto out;
        }
 
-       bitmap_cursor = (char *)bitmap;
+       bitmap_cursor = bitmap;
        bitmap_range = block_group->sectorsize * BTRFS_FREE_SPACE_BITMAP_BITS;
        i = start;
        while (i < end) {
@@ -319,7 +318,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        struct btrfs_free_space_info *info;
        struct btrfs_key key, found_key;
        struct extent_buffer *leaf;
-       unsigned long *bitmap;
+       u8 *bitmap;
        u64 start, end;
        /* Initialize to silence GCC. */
        u64 extent_start = 0;
@@ -363,7 +362,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                break;
                        } else if (found_key.type == BTRFS_FREE_SPACE_BITMAP_KEY) {
                                unsigned long ptr;
-                               char *bitmap_cursor;
+                               u8 *bitmap_cursor;
                                u32 bitmap_pos, data_size;
 
                                ASSERT(found_key.objectid >= start);
@@ -373,7 +372,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
                                bitmap_pos = div_u64(found_key.objectid - start,
                                                     block_group->sectorsize *
                                                     BITS_PER_BYTE);
-                               bitmap_cursor = ((char *)bitmap) + bitmap_pos;
+                               bitmap_cursor = bitmap + bitmap_pos;
                                data_size = free_space_bitmap_size(found_key.offset,
                                                                   block_group->sectorsize);
 
@@ -410,7 +409,7 @@ int convert_free_space_to_extents(struct btrfs_trans_handle *trans,
        offset = start;
        bitnr = 0;
        while (offset < end) {
-               bit = !!test_bit(bitnr, bitmap);
+               bit = !!le_test_bit(bitnr, bitmap);
                if (prev_bit == 0 && bit == 1) {
                        extent_start = offset;
                } else if (prev_bit == 1 && bit == 0) {
@@ -1185,6 +1184,7 @@ int btrfs_create_free_space_tree(struct btrfs_fs_info *fs_info)
        }
 
        btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE);
+       btrfs_set_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
        clear_bit(BTRFS_FS_CREATING_FREE_SPACE_TREE, &fs_info->flags);
 
        ret = btrfs_commit_transaction(trans, tree_root);
@@ -1253,6 +1253,7 @@ int btrfs_clear_free_space_tree(struct btrfs_fs_info *fs_info)
                return PTR_ERR(trans);
 
        btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE);
+       btrfs_clear_fs_compat_ro(fs_info, FREE_SPACE_TREE_VALID);
        fs_info->free_space_root = NULL;
 
        ret = clear_free_space_tree(trans, free_space_root);
index d19ab03..caad80b 100644 (file)
@@ -273,20 +273,37 @@ out:
        return ret;
 }
 
-/**
- * test_bit_in_byte - Determine whether a bit is set in a byte
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
-static inline int test_bit_in_byte(int nr, const u8 *addr)
+static int check_eb_bitmap(unsigned long *bitmap, struct extent_buffer *eb,
+                          unsigned long len)
 {
-       return 1UL & (addr[nr / BITS_PER_BYTE] >> (nr & (BITS_PER_BYTE - 1)));
+       unsigned long i;
+
+       for (i = 0; i < len * BITS_PER_BYTE; i++) {
+               int bit, bit1;
+
+               bit = !!test_bit(i, bitmap);
+               bit1 = !!extent_buffer_test_bit(eb, 0, i);
+               if (bit1 != bit) {
+                       test_msg("Bits do not match\n");
+                       return -EINVAL;
+               }
+
+               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
+                                               i % BITS_PER_BYTE);
+               if (bit1 != bit) {
+                       test_msg("Offset bits do not match\n");
+                       return -EINVAL;
+               }
+       }
+       return 0;
 }
 
 static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                             unsigned long len)
 {
-       unsigned long i, x;
+       unsigned long i, j;
+       u32 x;
+       int ret;
 
        memset(bitmap, 0, len);
        memset_extent_buffer(eb, 0, 0, len);
@@ -297,16 +314,18 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
 
        bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
        extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
-       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
                test_msg("Setting all bits failed\n");
-               return -EINVAL;
+               return ret;
        }
 
        bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
        extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
-       if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
                test_msg("Clearing all bits failed\n");
-               return -EINVAL;
+               return ret;
        }
 
        /* Straddling pages test */
@@ -316,9 +335,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                        sizeof(long) * BITS_PER_BYTE);
                extent_buffer_bitmap_set(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        sizeof(long) * BITS_PER_BYTE);
-               if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               ret = check_eb_bitmap(bitmap, eb, len);
+               if (ret) {
                        test_msg("Setting straddling pages failed\n");
-                       return -EINVAL;
+                       return ret;
                }
 
                bitmap_set(bitmap, 0, len * BITS_PER_BYTE);
@@ -328,9 +348,10 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
                extent_buffer_bitmap_set(eb, 0, 0, len * BITS_PER_BYTE);
                extent_buffer_bitmap_clear(eb, PAGE_SIZE - sizeof(long) / 2, 0,
                                        sizeof(long) * BITS_PER_BYTE);
-               if (memcmp_extent_buffer(eb, bitmap, 0, len) != 0) {
+               ret = check_eb_bitmap(bitmap, eb, len);
+               if (ret) {
                        test_msg("Clearing straddling pages failed\n");
-                       return -EINVAL;
+                       return ret;
                }
        }
 
@@ -339,28 +360,22 @@ static int __test_eb_bitmaps(unsigned long *bitmap, struct extent_buffer *eb,
         * something repetitive that could miss some hypothetical off-by-n bug.
         */
        x = 0;
-       for (i = 0; i < len / sizeof(long); i++) {
-               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffUL;
-               bitmap[i] = x;
-       }
-       write_extent_buffer(eb, bitmap, 0, len);
-
-       for (i = 0; i < len * BITS_PER_BYTE; i++) {
-               int bit, bit1;
-
-               bit = !!test_bit_in_byte(i, (u8 *)bitmap);
-               bit1 = !!extent_buffer_test_bit(eb, 0, i);
-               if (bit1 != bit) {
-                       test_msg("Testing bit pattern failed\n");
-                       return -EINVAL;
+       bitmap_clear(bitmap, 0, len * BITS_PER_BYTE);
+       extent_buffer_bitmap_clear(eb, 0, 0, len * BITS_PER_BYTE);
+       for (i = 0; i < len * BITS_PER_BYTE / 32; i++) {
+               x = (0x19660dULL * (u64)x + 0x3c6ef35fULL) & 0xffffffffU;
+               for (j = 0; j < 32; j++) {
+                       if (x & (1U << j)) {
+                               bitmap_set(bitmap, i * 32 + j, 1);
+                               extent_buffer_bitmap_set(eb, 0, i * 32 + j, 1);
+                       }
                }
+       }
 
-               bit1 = !!extent_buffer_test_bit(eb, i / BITS_PER_BYTE,
-                                               i % BITS_PER_BYTE);
-               if (bit1 != bit) {
-                       test_msg("Testing bit pattern with offset failed\n");
-                       return -EINVAL;
-               }
+       ret = check_eb_bitmap(bitmap, eb, len);
+       if (ret) {
+               test_msg("Random bit pattern failed\n");
+               return ret;
        }
 
        return 0;
index 7508d3b..6e14404 100644 (file)
 #include "../transaction.h"
 
 struct free_space_extent {
-       u64 start, length;
+       u64 start;
+       u64 length;
 };
 
-/*
- * The test cases align their operations to this in order to hit some of the
- * edge cases in the bitmap code.
- */
-#define BITMAP_RANGE (BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE)
-
 static int __check_free_space_extents(struct btrfs_trans_handle *trans,
                                      struct btrfs_fs_info *fs_info,
                                      struct btrfs_block_group_cache *cache,
                                      struct btrfs_path *path,
-                                     struct free_space_extent *extents,
+                                     const struct free_space_extent * const extents,
                                      unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -126,7 +121,7 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
                                    struct btrfs_fs_info *fs_info,
                                    struct btrfs_block_group_cache *cache,
                                    struct btrfs_path *path,
-                                   struct free_space_extent *extents,
+                                   const struct free_space_extent * const extents,
                                    unsigned int num_extents)
 {
        struct btrfs_free_space_info *info;
@@ -168,9 +163,10 @@ static int check_free_space_extents(struct btrfs_trans_handle *trans,
 static int test_empty_block_group(struct btrfs_trans_handle *trans,
                                  struct btrfs_fs_info *fs_info,
                                  struct btrfs_block_group_cache *cache,
-                                 struct btrfs_path *path)
+                                 struct btrfs_path *path,
+                                 u32 alignment)
 {
-       struct free_space_extent extents[] = {
+       const struct free_space_extent extents[] = {
                {cache->key.objectid, cache->key.offset},
        };
 
@@ -181,9 +177,10 @@ static int test_empty_block_group(struct btrfs_trans_handle *trans,
 static int test_remove_all(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {};
+       const struct free_space_extent extents[] = {};
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
@@ -201,16 +198,17 @@ static int test_remove_all(struct btrfs_trans_handle *trans,
 static int test_remove_beginning(struct btrfs_trans_handle *trans,
                                 struct btrfs_fs_info *fs_info,
                                 struct btrfs_block_group_cache *cache,
-                                struct btrfs_path *path)
+                                struct btrfs_path *path,
+                                u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE,
-                       cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment,
+                       cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid, BITMAP_RANGE);
+                                           cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -224,17 +222,18 @@ static int test_remove_beginning(struct btrfs_trans_handle *trans,
 static int test_remove_end(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, cache->key.offset - BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, cache->key.offset - alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
                                            cache->key.objectid +
-                                           cache->key.offset - BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.offset - alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -247,18 +246,19 @@ static int test_remove_end(struct btrfs_trans_handle *trans,
 static int test_remove_middle(struct btrfs_trans_handle *trans,
                              struct btrfs_fs_info *fs_info,
                              struct btrfs_block_group_cache *cache,
-                             struct btrfs_path *path)
+                             struct btrfs_path *path,
+                             u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE,
-                       cache->key.offset - 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment,
+                       cache->key.offset - 2 * alignment},
        };
        int ret;
 
        ret = __remove_from_free_space_tree(trans, fs_info, cache, path,
-                                           cache->key.objectid + BITMAP_RANGE,
-                                           BITMAP_RANGE);
+                                           cache->key.objectid + alignment,
+                                           alignment);
        if (ret) {
                test_msg("Could not remove free space\n");
                return ret;
@@ -271,10 +271,11 @@ static int test_remove_middle(struct btrfs_trans_handle *trans,
 static int test_merge_left(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 2 * alignment},
        };
        int ret;
 
@@ -287,15 +288,15 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -308,10 +309,11 @@ static int test_merge_left(struct btrfs_trans_handle *trans,
 static int test_merge_right(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid + BITMAP_RANGE, 2 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid + alignment, 2 * alignment},
        };
        int ret;
 
@@ -324,16 +326,16 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -346,10 +348,11 @@ static int test_merge_right(struct btrfs_trans_handle *trans,
 static int test_merge_both(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, 3 * BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, 3 * alignment},
        };
        int ret;
 
@@ -362,23 +365,23 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -391,12 +394,13 @@ static int test_merge_both(struct btrfs_trans_handle *trans,
 static int test_merge_none(struct btrfs_trans_handle *trans,
                           struct btrfs_fs_info *fs_info,
                           struct btrfs_block_group_cache *cache,
-                          struct btrfs_path *path)
+                          struct btrfs_path *path,
+                          u32 alignment)
 {
-       struct free_space_extent extents[] = {
-               {cache->key.objectid, BITMAP_RANGE},
-               {cache->key.objectid + 2 * BITMAP_RANGE, BITMAP_RANGE},
-               {cache->key.objectid + 4 * BITMAP_RANGE, BITMAP_RANGE},
+       const struct free_space_extent extents[] = {
+               {cache->key.objectid, alignment},
+               {cache->key.objectid + 2 * alignment, alignment},
+               {cache->key.objectid + 4 * alignment, alignment},
        };
        int ret;
 
@@ -409,23 +413,23 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid, BITMAP_RANGE);
+                                      cache->key.objectid, alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 4 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 4 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
        }
 
        ret = __add_to_free_space_tree(trans, fs_info, cache, path,
-                                      cache->key.objectid + 2 * BITMAP_RANGE,
-                                      BITMAP_RANGE);
+                                      cache->key.objectid + 2 * alignment,
+                                      alignment);
        if (ret) {
                test_msg("Could not add free space\n");
                return ret;
@@ -438,10 +442,11 @@ static int test_merge_none(struct btrfs_trans_handle *trans,
 typedef int (*test_func_t)(struct btrfs_trans_handle *,
                           struct btrfs_fs_info *,
                           struct btrfs_block_group_cache *,
-                          struct btrfs_path *);
+                          struct btrfs_path *,
+                          u32 alignment);
 
-static int run_test(test_func_t test_func, int bitmaps,
-               u32 sectorsize, u32 nodesize)
+static int run_test(test_func_t test_func, int bitmaps, u32 sectorsize,
+                   u32 nodesize, u32 alignment)
 {
        struct btrfs_fs_info *fs_info;
        struct btrfs_root *root = NULL;
@@ -480,7 +485,7 @@ static int run_test(test_func_t test_func, int bitmaps,
        btrfs_set_header_nritems(root->node, 0);
        root->alloc_bytenr += 2 * nodesize;
 
-       cache = btrfs_alloc_dummy_block_group(8 * BITMAP_RANGE, sectorsize);
+       cache = btrfs_alloc_dummy_block_group(8 * alignment, sectorsize);
        if (!cache) {
                test_msg("Couldn't allocate dummy block group cache\n");
                ret = -ENOMEM;
@@ -514,7 +519,7 @@ static int run_test(test_func_t test_func, int bitmaps,
                }
        }
 
-       ret = test_func(&trans, root->fs_info, cache, path);
+       ret = test_func(&trans, root->fs_info, cache, path, alignment);
        if (ret)
                goto out;
 
@@ -539,15 +544,27 @@ out:
        return ret;
 }
 
-static int run_test_both_formats(test_func_t test_func,
-       u32 sectorsize, u32 nodesize)
+static int run_test_both_formats(test_func_t test_func, u32 sectorsize,
+                                u32 nodesize, u32 alignment)
 {
+       int test_ret = 0;
        int ret;
 
-       ret = run_test(test_func, 0, sectorsize, nodesize);
-       if (ret)
-               return ret;
-       return run_test(test_func, 1, sectorsize, nodesize);
+       ret = run_test(test_func, 0, sectorsize, nodesize, alignment);
+       if (ret) {
+               test_msg("%pf failed with extents, sectorsize=%u, nodesize=%u, alignment=%u\n",
+                        test_func, sectorsize, nodesize, alignment);
+               test_ret = ret;
+       }
+
+       ret = run_test(test_func, 1, sectorsize, nodesize, alignment);
+       if (ret) {
+               test_msg("%pf failed with bitmaps, sectorsize=%u, nodesize=%u, alignment=%u\n",
+                        test_func, sectorsize, nodesize, alignment);
+               test_ret = ret;
+       }
+
+       return test_ret;
 }
 
 int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
@@ -563,18 +580,30 @@ int btrfs_test_free_space_tree(u32 sectorsize, u32 nodesize)
                test_merge_both,
                test_merge_none,
        };
+       u32 bitmap_alignment;
+       int test_ret = 0;
        int i;
 
+       /*
+        * Align some operations to a page to flush out bugs in the extent
+        * buffer bitmap handling of highmem.
+        */
+       bitmap_alignment = BTRFS_FREE_SPACE_BITMAP_BITS * PAGE_SIZE;
+
        test_msg("Running free space tree tests\n");
        for (i = 0; i < ARRAY_SIZE(tests); i++) {
-               int ret = run_test_both_formats(tests[i], sectorsize,
-                       nodesize);
-               if (ret) {
-                       test_msg("%pf : sectorsize %u failed\n",
-                               tests[i], sectorsize);
-                       return ret;
-               }
+               int ret;
+
+               ret = run_test_both_formats(tests[i], sectorsize, nodesize,
+                                           sectorsize);
+               if (ret)
+                       test_ret = ret;
+
+               ret = run_test_both_formats(tests[i], sectorsize, nodesize,
+                                           bitmap_alignment);
+               if (ret)
+                       test_ret = ret;
        }
 
-       return 0;
+       return test_ret;
 }
index 7dad871..b205a62 100644 (file)
@@ -351,7 +351,7 @@ void end_buffer_async_write(struct buffer_head *bh, int uptodate)
                set_buffer_uptodate(bh);
        } else {
                buffer_io_error(bh, ", lost async page write");
-               set_bit(AS_EIO, &page->mapping->flags);
+               mapping_set_error(page->mapping, -EIO);
                set_buffer_write_io_error(bh);
                clear_buffer_uptodate(bh);
                SetPageError(page);
@@ -3249,7 +3249,7 @@ drop_buffers(struct page *page, struct buffer_head **buffers_to_free)
        bh = head;
        do {
                if (buffer_write_io_error(bh) && page->mapping)
-                       set_bit(AS_EIO, &page->mapping->flags);
+                       mapping_set_error(page->mapping, -EIO);
                if (buffer_busy(bh))
                        goto failed;
                bh = bh->b_this_page;
index 6c58e13..3d03e48 100644 (file)
@@ -152,6 +152,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
        list_for_each(tmp1, &cifs_tcp_ses_list) {
                server = list_entry(tmp1, struct TCP_Server_Info,
                                    tcp_ses_list);
+               seq_printf(m, "\nNumber of credits: %d", server->credits);
                i++;
                list_for_each(tmp2, &server->smb_ses_list) {
                        ses = list_entry(tmp2, struct cifs_ses,
index 1418daa..07ed81c 100644 (file)
@@ -49,6 +49,7 @@
 #define CIFS_MOUNT_USE_PREFIX_PATH 0x1000000 /* make subpath with unaccessible
                                              * root mountable
                                              */
+#define CIFS_MOUNT_UID_FROM_ACL 0x2000000 /* try to get UID via special SID */
 
 struct cifs_sb_info {
        struct rb_root tlink_tree;
index 0065256..57ff075 100644 (file)
@@ -36,7 +36,15 @@ struct smb_mnt_fs_info {
        __u64   cifs_posix_caps;
 } __packed;
 
+struct smb_snapshot_array {
+       __u32   number_of_snapshots;
+       __u32   number_of_snapshots_returned;
+       __u32   snapshot_array_size;
+       /*      snapshots[]; */
+} __packed;
+
 #define CIFS_IOCTL_MAGIC       0xCF
 #define CIFS_IOC_COPYCHUNK_FILE        _IOW(CIFS_IOCTL_MAGIC, 3, int)
 #define CIFS_IOC_SET_INTEGRITY  _IO(CIFS_IOCTL_MAGIC, 4)
 #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
+#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
index 71e8a56..15bac39 100644 (file)
@@ -42,6 +42,35 @@ static const struct cifs_sid sid_authusers = {
 /* group users */
 static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
 
+/* S-1-22-1 Unmapped Unix users */
+static const struct cifs_sid sid_unix_users = {1, 1, {0, 0, 0, 0, 0, 22},
+               {cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-22-2 Unmapped Unix groups */
+static const struct cifs_sid sid_unix_groups = { 1, 1, {0, 0, 0, 0, 0, 22},
+               {cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/*
+ * See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
+ */
+
+/* S-1-5-88 MS NFS and Apple style UID/GID/mode */
+
+/* S-1-5-88-1 Unix uid */
+static const struct cifs_sid sid_unix_NFS_users = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(1), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-2 Unix gid */
+static const struct cifs_sid sid_unix_NFS_groups = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(2), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+/* S-1-5-88-3 Unix mode */
+static const struct cifs_sid sid_unix_NFS_mode = { 1, 2, {0, 0, 0, 0, 0, 5},
+       {cpu_to_le32(88),
+        cpu_to_le32(3), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} };
+
 static const struct cred *root_cred;
 
 static int
@@ -183,6 +212,62 @@ compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
        return 0; /* sids compare/match */
 }
 
+static bool
+is_well_known_sid(const struct cifs_sid *psid, uint32_t *puid, bool is_group)
+{
+       int i;
+       int num_subauth;
+       const struct cifs_sid *pwell_known_sid;
+
+       if (!psid || (puid == NULL))
+               return false;
+
+       num_subauth = psid->num_subauth;
+
+       /* check if Mac (or Windows NFS) vs. Samba format for Unix owner SID */
+       if (num_subauth == 2) {
+               if (is_group)
+                       pwell_known_sid = &sid_unix_groups;
+               else
+                       pwell_known_sid = &sid_unix_users;
+       } else if (num_subauth == 3) {
+               if (is_group)
+                       pwell_known_sid = &sid_unix_NFS_groups;
+               else
+                       pwell_known_sid = &sid_unix_NFS_users;
+       } else
+               return false;
+
+       /* compare the revision */
+       if (psid->revision != pwell_known_sid->revision)
+               return false;
+
+       /* compare all of the six auth values */
+       for (i = 0; i < NUM_AUTHS; ++i) {
+               if (psid->authority[i] != pwell_known_sid->authority[i]) {
+                       cifs_dbg(FYI, "auth %d did not match\n", i);
+                       return false;
+               }
+       }
+
+       if (num_subauth == 2) {
+               if (psid->sub_auth[0] != pwell_known_sid->sub_auth[0])
+                       return false;
+
+               *puid = le32_to_cpu(psid->sub_auth[1]);
+       } else /* 3 subauths, ie Windows/Mac style */ {
+               *puid = le32_to_cpu(psid->sub_auth[0]);
+               if ((psid->sub_auth[0] != pwell_known_sid->sub_auth[0]) ||
+                   (psid->sub_auth[1] != pwell_known_sid->sub_auth[1]))
+                       return false;
+
+               *puid = le32_to_cpu(psid->sub_auth[2]);
+       }
+
+       cifs_dbg(FYI, "Unix UID %d returned from SID\n", *puid);
+       return true; /* well known sid found, uid returned */
+}
+
 static void
 cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
 {
@@ -276,6 +361,43 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid,
                return -EIO;
        }
 
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL) {
+               uint32_t unix_id;
+               bool is_group;
+
+               if (sidtype != SIDOWNER)
+                       is_group = true;
+               else
+                       is_group = false;
+
+               if (is_well_known_sid(psid, &unix_id, is_group) == false)
+                       goto try_upcall_to_get_id;
+
+               if (is_group) {
+                       kgid_t gid;
+                       gid_t id;
+
+                       id = (gid_t)unix_id;
+                       gid = make_kgid(&init_user_ns, id);
+                       if (gid_valid(gid)) {
+                               fgid = gid;
+                               goto got_valid_id;
+                       }
+               } else {
+                       kuid_t uid;
+                       uid_t id;
+
+                       id = (uid_t)unix_id;
+                       uid = make_kuid(&init_user_ns, id);
+                       if (uid_valid(uid)) {
+                               fuid = uid;
+                               goto got_valid_id;
+                       }
+               }
+               /* If unable to find uid/gid easily from SID try via upcall */
+       }
+
+try_upcall_to_get_id:
        sidstr = sid_to_key_str(psid, sidtype);
        if (!sidstr)
                return -ENOMEM;
@@ -329,6 +451,7 @@ out_revert_creds:
         * Note that we return 0 here unconditionally. If the mapping
         * fails then we just fall back to using the mnt_uid/mnt_gid.
         */
+got_valid_id:
        if (sidtype == SIDOWNER)
                fattr->cf_uid = fuid;
        else
index cca04e7..15261ba 100644 (file)
@@ -64,15 +64,15 @@ unsigned int global_secflags = CIFSSEC_DEF;
 unsigned int sign_CIFS_PDUs = 1;
 static const struct super_operations cifs_super_ops;
 unsigned int CIFSMaxBufSize = CIFS_MAX_MSGSIZE;
-module_param(CIFSMaxBufSize, uint, 0);
+module_param(CIFSMaxBufSize, uint, 0444);
 MODULE_PARM_DESC(CIFSMaxBufSize, "Network buffer size (not including header). "
                                 "Default: 16384 Range: 8192 to 130048");
 unsigned int cifs_min_rcv = CIFS_MIN_RCV_POOL;
-module_param(cifs_min_rcv, uint, 0);
+module_param(cifs_min_rcv, uint, 0444);
 MODULE_PARM_DESC(cifs_min_rcv, "Network buffers in pool. Default: 4 Range: "
                                "1 to 64");
 unsigned int cifs_min_small = 30;
-module_param(cifs_min_small, uint, 0);
+module_param(cifs_min_small, uint, 0444);
 MODULE_PARM_DESC(cifs_min_small, "Small network buffers in pool. Default: 30 "
                                 "Range: 2 to 256");
 unsigned int cifs_max_pending = CIFS_MAX_REQ;
@@ -271,7 +271,7 @@ cifs_alloc_inode(struct super_block *sb)
        cifs_inode->createtime = 0;
        cifs_inode->epoch = 0;
 #ifdef CONFIG_CIFS_SMB2
-       get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
+       generate_random_uuid(cifs_inode->lease_key);
 #endif
        /*
         * Can not set i_flags here - they get immediately overwritten to zero
@@ -469,6 +469,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
                seq_puts(s, ",posixpaths");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)
                seq_puts(s, ",setuids");
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
+               seq_puts(s, ",idsfromsid");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
                seq_puts(s, ",serverino");
        if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
@@ -1262,7 +1264,6 @@ init_cifs(void)
        GlobalTotalActiveXid = 0;
        GlobalMaxActiveXid = 0;
        spin_lock_init(&cifs_tcp_ses_lock);
-       spin_lock_init(&cifs_file_list_lock);
        spin_lock_init(&GlobalMid_Lock);
 
        get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret));
index 8f1d8c1..1f17f6b 100644 (file)
 #define SMB_ECHO_INTERVAL_MAX 600
 #define SMB_ECHO_INTERVAL_DEFAULT 60
 
+/*
+ * Default number of credits to keep available for SMB3.
+ * This value is chosen somewhat arbitrarily. The Windows client
+ * defaults to 128 credits, the Windows server allows clients up to
+ * 512 credits (or 8K for later versions), and the NetApp server
+ * does not limit clients at all.  Choose a high enough default value
+ * such that the client shouldn't limit performance, but allow mount
+ * to override (until you approach 64K, where we limit credits to 65000
+ * to reduce possibility of seeing more server credit overflow bugs.
+ */
+#define SMB2_MAX_CREDITS_AVAILABLE 32000
+
 #include "cifspdu.h"
 
 #ifndef XATTR_DOS_ATTRIB
@@ -376,6 +388,8 @@ struct smb_version_operations {
        int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
        int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
                             struct cifsFileInfo *src_file);
+       int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
+                            struct cifsFileInfo *src_file, void __user *);
        int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
                                struct cifs_sb_info *, const unsigned char *,
                                char *, unsigned int *);
@@ -464,6 +478,7 @@ struct smb_vol {
        bool retry:1;
        bool intr:1;
        bool setuids:1;
+       bool setuidfromacl:1;
        bool override_uid:1;
        bool override_gid:1;
        bool dynperm:1;
@@ -510,6 +525,7 @@ struct smb_vol {
        struct sockaddr_storage srcaddr; /* allow binding to a local IP */
        struct nls_table *local_nls;
        unsigned int echo_interval; /* echo interval in secs */
+       unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
 };
 
 #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \
@@ -567,7 +583,8 @@ struct TCP_Server_Info {
        bool noblocksnd;                /* use blocking sendmsg */
        bool noautotune;                /* do not autotune send buf sizes */
        bool tcp_nodelay;
-       int credits;  /* send no more requests at once */
+       unsigned int credits;  /* send no more requests at once */
+       unsigned int max_credits; /* can override large 32000 default at mnt */
        unsigned int in_flight;  /* number of requests on the wire to server */
        spinlock_t req_lock;  /* protect the two values above */
        struct mutex srv_mutex;
@@ -833,6 +850,7 @@ struct cifs_tcon {
        struct list_head tcon_list;
        int tc_count;
        struct list_head openFileList;
+       spinlock_t open_file_lock; /* protects list above */
        struct cifs_ses *ses;   /* pointer to session associated with */
        char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
        char *nativeFileSystem;
@@ -889,7 +907,7 @@ struct cifs_tcon {
 #endif /* CONFIG_CIFS_STATS2 */
        __u64    bytes_read;
        __u64    bytes_written;
-       spinlock_t stat_lock;
+       spinlock_t stat_lock;  /* protects the two fields above */
 #endif /* CONFIG_CIFS_STATS */
        FILE_SYSTEM_DEVICE_INFO fsDevInfo;
        FILE_SYSTEM_ATTRIBUTE_INFO fsAttrInfo; /* ok if fs name truncated */
@@ -1040,20 +1058,24 @@ struct cifs_fid_locks {
 };
 
 struct cifsFileInfo {
+       /* following two lists are protected by tcon->open_file_lock */
        struct list_head tlist; /* pointer to next fid owned by tcon */
        struct list_head flist; /* next fid (file instance) for this inode */
+       /* lock list below protected by cifsi->lock_sem */
        struct cifs_fid_locks *llist;   /* brlocks held by this fid */
        kuid_t uid;             /* allows finding which FileInfo structure */
        __u32 pid;              /* process id who opened file */
        struct cifs_fid fid;    /* file id from remote */
+       struct list_head rlist; /* reconnect list */
        /* BB add lock scope info here if needed */ ;
        /* lock scope id (0 if none) */
        struct dentry *dentry;
-       unsigned int f_flags;
        struct tcon_link *tlink;
+       unsigned int f_flags;
        bool invalidHandle:1;   /* file closed via session abend */
        bool oplock_break_cancelled:1;
-       int count;              /* refcount protected by cifs_file_list_lock */
+       int count;
+       spinlock_t file_info_lock; /* protects four flag/count fields above */
        struct mutex fh_mutex; /* prevents reopen race after dead ses*/
        struct cifs_search_info srch_inf;
        struct work_struct oplock_break; /* work for oplock breaks */
@@ -1120,7 +1142,7 @@ struct cifs_writedata {
 
 /*
  * Take a reference on the file private data. Must be called with
- * cifs_file_list_lock held.
+ * cfile->file_info_lock held.
  */
 static inline void
 cifsFileInfo_get_locked(struct cifsFileInfo *cifs_file)
@@ -1514,8 +1536,10 @@ require use of the stronger protocol */
  *  GlobalMid_Lock protects:
  *     list operations on pending_mid_q and oplockQ
  *      updates to XID counters, multiplex id  and SMB sequence numbers
- *  cifs_file_list_lock protects:
- *     list operations on tcp and SMB session lists and tCon lists
+ *  tcp_ses_lock protects:
+ *     list operations on tcp and SMB session lists
+ *  tcon->open_file_lock protects the list of open files hanging off the tcon
+ *  cfile->file_info_lock protects counters and fields in cifs file struct
  *  f_owner.lock protects certain per file struct operations
  *  mapping->page_lock protects certain per page operations
  *
@@ -1547,18 +1571,12 @@ GLOBAL_EXTERN struct list_head          cifs_tcp_ses_list;
  * tcp session, and the list of tcon's per smb session. It also protects
  * the reference counters for the server, smb session, and tcon. Finally,
  * changes to the tcon->tidStatus should be done while holding this lock.
+ * generally the locks should be taken in order tcp_ses_lock before
+ * tcon->open_file_lock and that before file->file_info_lock since the
+ * structure order is cifs_socket-->cifs_ses-->cifs_tcon-->cifs_file
  */
 GLOBAL_EXTERN spinlock_t               cifs_tcp_ses_lock;
 
-/*
- * This lock protects the cifs_file->llist and cifs_file->flist
- * list operations, and updates to some flags (cifs_file->invalidHandle)
- * It will be moved to either use the tcon->stat_lock or equivalent later.
- * If cifs_tcp_ses_lock and the lock below are both needed to be held, then
- * the cifs_tcp_ses_lock must be grabbed first and released last.
- */
-GLOBAL_EXTERN spinlock_t       cifs_file_list_lock;
-
 #ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
 /* Outstanding dir notify requests */
 GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
index 4ead72a..ced0e42 100644 (file)
@@ -193,6 +193,8 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
 extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
 extern void cifs_umount(struct cifs_sb_info *);
 extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
+extern void cifs_reopen_persistent_handles(struct cifs_tcon *tcon);
+
 extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
                                    __u64 length, __u8 type,
                                    struct cifsLockInfo **conf_lock,
index f82d282..3f3185f 100644 (file)
@@ -98,13 +98,13 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
        struct list_head *tmp1;
 
        /* list all files open on tree connection and mark them invalid */
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
                open_file = list_entry(tmp, struct cifsFileInfo, tlist);
                open_file->invalidHandle = true;
                open_file->oplock_break_cancelled = true;
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        /*
         * BB Add call to invalidate_inodes(sb) for all superblocks mounted
         * to this tcon.
index 2e4f4ba..aab5227 100644 (file)
@@ -63,7 +63,6 @@ extern mempool_t *cifs_req_poolp;
 #define TLINK_IDLE_EXPIRE      (600 * HZ)
 
 enum {
-
        /* Mount options that take no arguments */
        Opt_user_xattr, Opt_nouser_xattr,
        Opt_forceuid, Opt_noforceuid,
@@ -76,7 +75,7 @@ enum {
        Opt_noposixpaths, Opt_nounix,
        Opt_nocase,
        Opt_brl, Opt_nobrl,
-       Opt_forcemandatorylock, Opt_setuids,
+       Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids,
        Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
        Opt_nohard, Opt_nosoft,
        Opt_nointr, Opt_intr,
@@ -95,7 +94,7 @@ enum {
        Opt_cruid, Opt_gid, Opt_file_mode,
        Opt_dirmode, Opt_port,
        Opt_rsize, Opt_wsize, Opt_actimeo,
-       Opt_echo_interval,
+       Opt_echo_interval, Opt_max_credits,
 
        /* Mount options which take string value */
        Opt_user, Opt_pass, Opt_ip,
@@ -148,6 +147,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_forcemandatorylock, "forcemand" },
        { Opt_setuids, "setuids" },
        { Opt_nosetuids, "nosetuids" },
+       { Opt_setuidfromacl, "idsfromsid" },
        { Opt_dynperm, "dynperm" },
        { Opt_nodynperm, "nodynperm" },
        { Opt_nohard, "nohard" },
@@ -190,6 +190,7 @@ static const match_table_t cifs_mount_option_tokens = {
        { Opt_wsize, "wsize=%s" },
        { Opt_actimeo, "actimeo=%s" },
        { Opt_echo_interval, "echo_interval=%s" },
+       { Opt_max_credits, "max_credits=%s" },
 
        { Opt_blank_user, "user=" },
        { Opt_blank_user, "username=" },
@@ -1376,6 +1377,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                case Opt_nosetuids:
                        vol->setuids = 0;
                        break;
+               case Opt_setuidfromacl:
+                       vol->setuidfromacl = 1;
+                       break;
                case Opt_dynperm:
                        vol->dynperm = true;
                        break;
@@ -1586,6 +1590,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
                        }
                        vol->echo_interval = option;
                        break;
+               case Opt_max_credits:
+                       if (get_option_ul(args, &option) || (option < 20) ||
+                           (option > 60000)) {
+                               cifs_dbg(VFS, "%s: Invalid max_credits value\n",
+                                        __func__);
+                               goto cifs_parse_mount_err;
+                       }
+                       vol->max_credits = option;
+                       break;
 
                /* String Arguments */
 
@@ -2163,7 +2176,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
        memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
                sizeof(tcp_ses->dstaddr));
 #ifdef CONFIG_CIFS_SMB2
-       get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE);
+       generate_random_uuid(tcp_ses->client_guid);
 #endif
        /*
         * at this point we are the only ones with the pointer
@@ -3270,6 +3283,8 @@ int cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
        if (pvolume_info->setuids)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+       if (pvolume_info->setuidfromacl)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL;
        if (pvolume_info->server_ino)
                cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
        if (pvolume_info->remap)
@@ -3598,7 +3613,11 @@ try_mount_again:
                bdi_destroy(&cifs_sb->bdi);
                goto out;
        }
-
+       if ((volume_info->max_credits < 20) ||
+            (volume_info->max_credits > 60000))
+               server->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
+       else
+               server->max_credits = volume_info->max_credits;
        /* get a reference to a SMB session */
        ses = cifs_get_smb_ses(server, volume_info);
        if (IS_ERR(ses)) {
@@ -3688,14 +3707,16 @@ remote_path_check:
                        goto mount_fail_check;
                }
 
-               rc = cifs_are_all_path_components_accessible(server,
+               if (rc != -EREMOTE) {
+                       rc = cifs_are_all_path_components_accessible(server,
                                                             xid, tcon, cifs_sb,
                                                             full_path);
-               if (rc != 0) {
-                       cifs_dbg(VFS, "cannot query dirs between root and final path, "
-                                "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
-                       rc = 0;
+                       if (rc != 0) {
+                               cifs_dbg(VFS, "cannot query dirs between root and final path, "
+                                        "enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
+                               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+                               rc = 0;
+                       }
                }
                kfree(full_path);
        }
index a95fe8b..7f5f617 100644 (file)
@@ -305,6 +305,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
        cfile->tlink = cifs_get_tlink(tlink);
        INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
        mutex_init(&cfile->fh_mutex);
+       spin_lock_init(&cfile->file_info_lock);
 
        cifs_sb_active(inode->i_sb);
 
@@ -317,7 +318,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
                oplock = 0;
        }
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
                oplock = fid->pending_open->oplock;
        list_del(&fid->pending_open->olist);
@@ -326,12 +327,13 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
        server->ops->set_fid(cfile, fid, oplock);
 
        list_add(&cfile->tlist, &tcon->openFileList);
+
        /* if readable file instance put first in list*/
        if (file->f_mode & FMODE_READ)
                list_add(&cfile->flist, &cinode->openFileList);
        else
                list_add_tail(&cfile->flist, &cinode->openFileList);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
 
        if (fid->purge_cache)
                cifs_zap_mapping(inode);
@@ -343,16 +345,16 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
 struct cifsFileInfo *
 cifsFileInfo_get(struct cifsFileInfo *cifs_file)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&cifs_file->file_info_lock);
        cifsFileInfo_get_locked(cifs_file);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&cifs_file->file_info_lock);
        return cifs_file;
 }
 
 /*
  * Release a reference on the file private data. This may involve closing
  * the filehandle out on the server. Must be called without holding
- * cifs_file_list_lock.
+ * tcon->open_file_lock and cifs_file->file_info_lock.
  */
 void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
 {
@@ -367,11 +369,15 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
        struct cifs_pending_open open;
        bool oplock_break_cancelled;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
+
+       spin_lock(&cifs_file->file_info_lock);
        if (--cifs_file->count > 0) {
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cifs_file->file_info_lock);
+               spin_unlock(&tcon->open_file_lock);
                return;
        }
+       spin_unlock(&cifs_file->file_info_lock);
 
        if (server->ops->get_lease_key)
                server->ops->get_lease_key(inode, &fid);
@@ -395,7 +401,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
                        set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);
                cifs_set_oplock_level(cifsi, 0);
        }
-       spin_unlock(&cifs_file_list_lock);
+
+       spin_unlock(&tcon->open_file_lock);
 
        oplock_break_cancelled = cancel_work_sync(&cifs_file->oplock_break);
 
@@ -732,6 +739,15 @@ reopen_success:
         * to the server to get the new inode info.
         */
 
+       /*
+        * If the server returned a read oplock and we have mandatory brlocks,
+        * set oplock level to None.
+        */
+       if (server->ops->is_read_op(oplock) && cifs_has_mand_locks(cinode)) {
+               cifs_dbg(FYI, "Reset oplock val from read to None due to mand locks\n");
+               oplock = 0;
+       }
+
        server->ops->set_fid(cfile, &cfile->fid, oplock);
        if (oparms.reconnect)
                cifs_relock_file(cfile);
@@ -753,6 +769,36 @@ int cifs_close(struct inode *inode, struct file *file)
        return 0;
 }
 
+void
+cifs_reopen_persistent_handles(struct cifs_tcon *tcon)
+{
+       struct cifsFileInfo *open_file;
+       struct list_head *tmp;
+       struct list_head *tmp1;
+       struct list_head tmp_list;
+
+       cifs_dbg(FYI, "Reopen persistent handles");
+       INIT_LIST_HEAD(&tmp_list);
+
+       /* list all files open on tree connection, reopen resilient handles  */
+       spin_lock(&tcon->open_file_lock);
+       list_for_each(tmp, &tcon->openFileList) {
+               open_file = list_entry(tmp, struct cifsFileInfo, tlist);
+               if (!open_file->invalidHandle)
+                       continue;
+               cifsFileInfo_get(open_file);
+               list_add_tail(&open_file->rlist, &tmp_list);
+       }
+       spin_unlock(&tcon->open_file_lock);
+
+       list_for_each_safe(tmp, tmp1, &tmp_list) {
+               open_file = list_entry(tmp, struct cifsFileInfo, rlist);
+               cifs_reopen_file(open_file, false /* do not flush */);
+               list_del_init(&open_file->rlist);
+               cifsFileInfo_put(open_file);
+       }
+}
+
 int cifs_closedir(struct inode *inode, struct file *file)
 {
        int rc = 0;
@@ -772,10 +818,10 @@ int cifs_closedir(struct inode *inode, struct file *file)
        server = tcon->ses->server;
 
        cifs_dbg(FYI, "Freeing private data in close dir\n");
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&cfile->file_info_lock);
        if (server->ops->dir_needs_close(cfile)) {
                cfile->invalidHandle = true;
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cfile->file_info_lock);
                if (server->ops->close_dir)
                        rc = server->ops->close_dir(xid, tcon, &cfile->fid);
                else
@@ -784,7 +830,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
                /* not much we can do if it fails anyway, ignore rc */
                rc = 0;
        } else
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&cfile->file_info_lock);
 
        buf = cfile->srch_inf.ntwrk_buf_start;
        if (buf) {
@@ -1728,12 +1774,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
 {
        struct cifsFileInfo *open_file = NULL;
        struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+       struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
 
        /* only filter by fsuid on multiuser mounts */
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
                fsuid_only = false;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        /* we could simply get the first_list_entry since write-only entries
           are always at the end of the list but since the first entry might
           have a close pending, we go through the whole list */
@@ -1744,8 +1791,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
                        if (!open_file->invalidHandle) {
                                /* found a good file */
                                /* lock it so it will not be closed on us */
-                               cifsFileInfo_get_locked(open_file);
-                               spin_unlock(&cifs_file_list_lock);
+                               cifsFileInfo_get(open_file);
+                               spin_unlock(&tcon->open_file_lock);
                                return open_file;
                        } /* else might as well continue, and look for
                             another, or simply have the caller reopen it
@@ -1753,7 +1800,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
                } else /* write only file */
                        break; /* write only files are last so must be done */
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        return NULL;
 }
 
@@ -1762,6 +1809,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
 {
        struct cifsFileInfo *open_file, *inv_file = NULL;
        struct cifs_sb_info *cifs_sb;
+       struct cifs_tcon *tcon;
        bool any_available = false;
        int rc;
        unsigned int refind = 0;
@@ -1777,15 +1825,16 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
        }
 
        cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
+       tcon = cifs_sb_master_tcon(cifs_sb);
 
        /* only filter by fsuid on multiuser mounts */
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
                fsuid_only = false;
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
 refind_writable:
        if (refind > MAX_REOPEN_ATT) {
-               spin_unlock(&cifs_file_list_lock);
+               spin_unlock(&tcon->open_file_lock);
                return NULL;
        }
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
@@ -1796,8 +1845,8 @@ refind_writable:
                if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
                        if (!open_file->invalidHandle) {
                                /* found a good writable file */
-                               cifsFileInfo_get_locked(open_file);
-                               spin_unlock(&cifs_file_list_lock);
+                               cifsFileInfo_get(open_file);
+                               spin_unlock(&tcon->open_file_lock);
                                return open_file;
                        } else {
                                if (!inv_file)
@@ -1813,24 +1862,24 @@ refind_writable:
 
        if (inv_file) {
                any_available = false;
-               cifsFileInfo_get_locked(inv_file);
+               cifsFileInfo_get(inv_file);
        }
 
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
 
        if (inv_file) {
                rc = cifs_reopen_file(inv_file, false);
                if (!rc)
                        return inv_file;
                else {
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_move_tail(&inv_file->flist,
                                        &cifs_inode->openFileList);
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        cifsFileInfo_put(inv_file);
-                       spin_lock(&cifs_file_list_lock);
                        ++refind;
                        inv_file = NULL;
+                       spin_lock(&tcon->open_file_lock);
                        goto refind_writable;
                }
        }
@@ -3612,15 +3661,17 @@ static int cifs_readpage(struct file *file, struct page *page)
 static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
 {
        struct cifsFileInfo *open_file;
+       struct cifs_tcon *tcon =
+               cifs_sb_master_tcon(CIFS_SB(cifs_inode->vfs_inode.i_sb));
 
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tcon->open_file_lock);
        list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
                if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        return 1;
                }
        }
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tcon->open_file_lock);
        return 0;
 }
 
index 7a3b84e..9f51b81 100644 (file)
@@ -189,7 +189,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
        xid = get_xid();
 
        cifs_sb = CIFS_SB(inode->i_sb);
-
+       cifs_dbg(VFS, "cifs ioctl 0x%x\n", command);
        switch (command) {
                case FS_IOC_GETFLAGS:
                        if (pSMBFile == NULL)
@@ -267,11 +267,23 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
                        tcon = tlink_tcon(pSMBFile->tlink);
                        rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
                        break;
+               case CIFS_ENUMERATE_SNAPSHOTS:
+                       if (arg == 0) {
+                               rc = -EINVAL;
+                               goto cifs_ioc_exit;
+                       }
+                       tcon = tlink_tcon(pSMBFile->tlink);
+                       if (tcon->ses->server->ops->enum_snapshots)
+                               rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
+                                               pSMBFile, (void __user *)arg);
+                       else
+                               rc = -EOPNOTSUPP;
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
        }
-
+cifs_ioc_exit:
        free_xid(xid);
        return rc;
 }
index 813fe13..c672915 100644 (file)
@@ -120,6 +120,7 @@ tconInfoAlloc(void)
                ++ret_buf->tc_count;
                INIT_LIST_HEAD(&ret_buf->openFileList);
                INIT_LIST_HEAD(&ret_buf->tcon_list);
+               spin_lock_init(&ret_buf->open_file_lock);
 #ifdef CONFIG_CIFS_STATS
                spin_lock_init(&ret_buf->stat_lock);
 #endif
@@ -465,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
                                continue;
 
                        cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_for_each(tmp2, &tcon->openFileList) {
                                netfile = list_entry(tmp2, struct cifsFileInfo,
                                                     tlist);
@@ -495,11 +496,11 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
                                           &netfile->oplock_break);
                                netfile->oplock_break_cancelled = false;
 
-                               spin_unlock(&cifs_file_list_lock);
+                               spin_unlock(&tcon->open_file_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                return true;
                        }
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        spin_unlock(&cifs_tcp_ses_lock);
                        cifs_dbg(FYI, "No matching file for oplock break\n");
                        return true;
@@ -613,9 +614,9 @@ backup_cred(struct cifs_sb_info *cifs_sb)
 void
 cifs_del_pending_open(struct cifs_pending_open *open)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tlink_tcon(open->tlink)->open_file_lock);
        list_del(&open->olist);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
 
 void
@@ -635,7 +636,7 @@ void
 cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
                      struct cifs_pending_open *open)
 {
-       spin_lock(&cifs_file_list_lock);
+       spin_lock(&tlink_tcon(tlink)->open_file_lock);
        cifs_add_pending_open_locked(fid, tlink, open);
-       spin_unlock(&cifs_file_list_lock);
+       spin_unlock(&tlink_tcon(open->tlink)->open_file_lock);
 }
index 65cf85d..8f6a2a5 100644 (file)
@@ -597,14 +597,14 @@ find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
             is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
                /* close and restart search */
                cifs_dbg(FYI, "search backing up - close and restart search\n");
-               spin_lock(&cifs_file_list_lock);
+               spin_lock(&cfile->file_info_lock);
                if (server->ops->dir_needs_close(cfile)) {
                        cfile->invalidHandle = true;
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&cfile->file_info_lock);
                        if (server->ops->close_dir)
                                server->ops->close_dir(xid, tcon, &cfile->fid);
                } else
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&cfile->file_info_lock);
                if (cfile->srch_inf.ntwrk_buf_start) {
                        cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
                        if (cfile->srch_inf.smallBuf)
index 4f0231e..1238cd3 100644 (file)
@@ -266,9 +266,15 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
        struct tcon_link *tlink;
        int rc;
 
+       if ((buf->CreationTime == 0) && (buf->LastAccessTime == 0) &&
+           (buf->LastWriteTime == 0) && (buf->ChangeTime) &&
+           (buf->Attributes == 0))
+               return 0; /* would be a no op, no sense sending this */
+
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
                return PTR_ERR(tlink);
+
        rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
                                FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
                                SMB2_OP_SET_INFO);
index 389fb9f..3d38348 100644 (file)
@@ -549,19 +549,19 @@ smb2_is_valid_lease_break(char *buffer)
                list_for_each(tmp1, &server->smb_ses_list) {
                        ses = list_entry(tmp1, struct cifs_ses, smb_ses_list);
 
-                       spin_lock(&cifs_file_list_lock);
                        list_for_each(tmp2, &ses->tcon_list) {
                                tcon = list_entry(tmp2, struct cifs_tcon,
                                                  tcon_list);
+                               spin_lock(&tcon->open_file_lock);
                                cifs_stats_inc(
                                    &tcon->stats.cifs_stats.num_oplock_brks);
                                if (smb2_tcon_has_lease(tcon, rsp, lw)) {
-                                       spin_unlock(&cifs_file_list_lock);
+                                       spin_unlock(&tcon->open_file_lock);
                                        spin_unlock(&cifs_tcp_ses_lock);
                                        return true;
                                }
+                               spin_unlock(&tcon->open_file_lock);
                        }
-                       spin_unlock(&cifs_file_list_lock);
                }
        }
        spin_unlock(&cifs_tcp_ses_lock);
@@ -603,7 +603,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
                        tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
 
                        cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
-                       spin_lock(&cifs_file_list_lock);
+                       spin_lock(&tcon->open_file_lock);
                        list_for_each(tmp2, &tcon->openFileList) {
                                cfile = list_entry(tmp2, struct cifsFileInfo,
                                                     tlist);
@@ -615,7 +615,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
 
                                cifs_dbg(FYI, "file id match, oplock break\n");
                                cinode = CIFS_I(d_inode(cfile->dentry));
-
+                               spin_lock(&cfile->file_info_lock);
                                if (!CIFS_CACHE_WRITE(cinode) &&
                                    rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
                                        cfile->oplock_break_cancelled = true;
@@ -637,14 +637,14 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
                                        clear_bit(
                                           CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2,
                                           &cinode->flags);
-
+                               spin_unlock(&cfile->file_info_lock);
                                queue_work(cifsiod_wq, &cfile->oplock_break);
 
-                               spin_unlock(&cifs_file_list_lock);
+                               spin_unlock(&tcon->open_file_lock);
                                spin_unlock(&cifs_tcp_ses_lock);
                                return true;
                        }
-                       spin_unlock(&cifs_file_list_lock);
+                       spin_unlock(&tcon->open_file_lock);
                        spin_unlock(&cifs_tcp_ses_lock);
                        cifs_dbg(FYI, "No matching file for oplock break\n");
                        return true;
index d203c03..5d456eb 100644 (file)
@@ -28,6 +28,7 @@
 #include "cifs_unicode.h"
 #include "smb2status.h"
 #include "smb2glob.h"
+#include "cifs_ioctl.h"
 
 static int
 change_conf(struct TCP_Server_Info *server)
@@ -70,6 +71,10 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
        spin_lock(&server->req_lock);
        val = server->ops->get_credits_field(server, optype);
        *val += add;
+       if (*val > 65000) {
+               *val = 65000; /* Don't get near 64K credits, avoid srv bugs */
+               printk_once(KERN_WARNING "server overflowed SMB3 credits\n");
+       }
        server->in_flight--;
        if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
                rc = change_conf(server);
@@ -287,7 +292,7 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
                cifs_dbg(FYI, "Link Speed %lld\n",
                        le64_to_cpu(out_buf->LinkSpeed));
        }
-
+       kfree(out_buf);
        return rc;
 }
 #endif /* STATS2 */
@@ -541,6 +546,7 @@ smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
        server->ops->set_oplock_level(cinode, oplock, fid->epoch,
                                      &fid->purge_cache);
        cinode->can_cache_brlcks = CIFS_CACHE_WRITE(cinode);
+       memcpy(cfile->fid.create_guid, fid->create_guid, 16);
 }
 
 static void
@@ -699,6 +705,7 @@ smb2_clone_range(const unsigned int xid,
 
 cchunk_out:
        kfree(pcchunk);
+       kfree(retbuf);
        return rc;
 }
 
@@ -823,7 +830,6 @@ smb2_duplicate_extents(const unsigned int xid,
 {
        int rc;
        unsigned int ret_data_len;
-       char *retbuf = NULL;
        struct duplicate_extents_to_file dup_ext_buf;
        struct cifs_tcon *tcon = tlink_tcon(trgtfile->tlink);
 
@@ -849,7 +855,7 @@ smb2_duplicate_extents(const unsigned int xid,
                        FSCTL_DUPLICATE_EXTENTS_TO_FILE,
                        true /* is_fsctl */, (char *)&dup_ext_buf,
                        sizeof(struct duplicate_extents_to_file),
-                       (char **)&retbuf,
+                       NULL,
                        &ret_data_len);
 
        if (ret_data_len > 0)
@@ -872,7 +878,6 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
                   struct cifsFileInfo *cfile)
 {
        struct fsctl_set_integrity_information_req integr_info;
-       char *retbuf = NULL;
        unsigned int ret_data_len;
 
        integr_info.ChecksumAlgorithm = cpu_to_le16(CHECKSUM_TYPE_UNCHANGED);
@@ -884,9 +889,53 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
                        FSCTL_SET_INTEGRITY_INFORMATION,
                        true /* is_fsctl */, (char *)&integr_info,
                        sizeof(struct fsctl_set_integrity_information_req),
+                       NULL,
+                       &ret_data_len);
+
+}
+
+static int
+smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
+                  struct cifsFileInfo *cfile, void __user *ioc_buf)
+{
+       char *retbuf = NULL;
+       unsigned int ret_data_len = 0;
+       int rc;
+       struct smb_snapshot_array snapshot_in;
+
+       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                       cfile->fid.volatile_fid,
+                       FSCTL_SRV_ENUMERATE_SNAPSHOTS,
+                       true /* is_fsctl */, NULL, 0 /* no input data */,
                        (char **)&retbuf,
                        &ret_data_len);
+       cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
+                       rc, ret_data_len);
+       if (rc)
+               return rc;
 
+       if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) {
+               /* Fixup buffer */
+               if (copy_from_user(&snapshot_in, ioc_buf,
+                   sizeof(struct smb_snapshot_array))) {
+                       rc = -EFAULT;
+                       kfree(retbuf);
+                       return rc;
+               }
+               if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
+                       rc = -ERANGE;
+                       return rc;
+               }
+
+               if (ret_data_len > snapshot_in.snapshot_array_size)
+                       ret_data_len = snapshot_in.snapshot_array_size;
+
+               if (copy_to_user(ioc_buf, retbuf, ret_data_len))
+                       rc = -EFAULT;
+       }
+
+       kfree(retbuf);
+       return rc;
 }
 
 static int
@@ -1041,7 +1090,7 @@ smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
 static void
 smb2_new_lease_key(struct cifs_fid *fid)
 {
-       get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
+       generate_random_uuid(fid->lease_key);
 }
 
 #define SMB2_SYMLINK_STRUCT_SIZE \
@@ -1654,6 +1703,7 @@ struct smb_version_operations smb21_operations = {
        .clone_range = smb2_clone_range,
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -1740,6 +1790,7 @@ struct smb_version_operations smb30_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -1827,6 +1878,7 @@ struct smb_version_operations smb311_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 #endif /* CIFS_SMB311 */
 
index 29e06db..5ca5ea4 100644 (file)
@@ -100,7 +100,21 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
        hdr->ProtocolId = SMB2_PROTO_NUMBER;
        hdr->StructureSize = cpu_to_le16(64);
        hdr->Command = smb2_cmd;
-       hdr->CreditRequest = cpu_to_le16(2); /* BB make this dynamic */
+       if (tcon && tcon->ses && tcon->ses->server) {
+               struct TCP_Server_Info *server = tcon->ses->server;
+
+               spin_lock(&server->req_lock);
+               /* Request up to 2 credits but don't go over the limit. */
+               if (server->credits >= server->max_credits)
+                       hdr->CreditRequest = cpu_to_le16(0);
+               else
+                       hdr->CreditRequest = cpu_to_le16(
+                               min_t(int, server->max_credits -
+                                               server->credits, 2));
+               spin_unlock(&server->req_lock);
+       } else {
+               hdr->CreditRequest = cpu_to_le16(2);
+       }
        hdr->ProcessId = cpu_to_le32((__u16)current->tgid);
 
        if (!tcon)
@@ -236,8 +250,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
        }
 
        cifs_mark_open_files_invalid(tcon);
+
        rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
        mutex_unlock(&tcon->ses->session_mutex);
+
+       if (tcon->use_persistent)
+               cifs_reopen_persistent_handles(tcon);
+
        cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
        if (rc)
                goto out;
@@ -574,59 +593,42 @@ vneg_out:
        return -EIO;
 }
 
-int
-SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
-               const struct nls_table *nls_cp)
+struct SMB2_sess_data {
+       unsigned int xid;
+       struct cifs_ses *ses;
+       struct nls_table *nls_cp;
+       void (*func)(struct SMB2_sess_data *);
+       int result;
+       u64 previous_session;
+
+       /* we will send the SMB in three pieces:
+        * a fixed length beginning part, an optional
+        * SPNEGO blob (which can be zero length), and a
+        * last part which will include the strings
+        * and rest of bcc area. This allows us to avoid
+        * a large buffer 17K allocation
+        */
+       int buf0_type;
+       struct kvec iov[2];
+};
+
+static int
+SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
 {
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
        struct smb2_sess_setup_req *req;
-       struct smb2_sess_setup_rsp *rsp = NULL;
-       struct kvec iov[2];
-       int rc = 0;
-       int resp_buftype = CIFS_NO_BUFFER;
-       __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
        struct TCP_Server_Info *server = ses->server;
-       u16 blob_length = 0;
-       struct key *spnego_key = NULL;
-       char *security_blob = NULL;
-       unsigned char *ntlmssp_blob = NULL;
-       bool use_spnego = false; /* else use raw ntlmssp */
-
-       cifs_dbg(FYI, "Session Setup\n");
-
-       if (!server) {
-               WARN(1, "%s: server is NULL!\n", __func__);
-               return -EIO;
-       }
-
-       /*
-        * If we are here due to reconnect, free per-smb session key
-        * in case signing was required.
-        */
-       kfree(ses->auth_key.response);
-       ses->auth_key.response = NULL;
-
-       /*
-        * If memory allocation is successful, caller of this function
-        * frees it.
-        */
-       ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
-       if (!ses->ntlmssp)
-               return -ENOMEM;
-       ses->ntlmssp->sesskey_per_smbsess = true;
-
-       /* FIXME: allow for other auth types besides NTLMSSP (e.g. krb5) */
-       if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
-               ses->sectype = RawNTLMSSP;
-
-ssetup_ntlmssp_authenticate:
-       if (phase == NtLmChallenge)
-               phase = NtLmAuthenticate; /* if ntlmssp, now final phase */
 
        rc = small_smb2_init(SMB2_SESSION_SETUP, NULL, (void **) &req);
        if (rc)
                return rc;
 
        req->hdr.SessionId = 0; /* First session, not a reauthenticate */
+
+       /* if reconnect, we need to send previous sess id, otherwise it is 0 */
+       req->PreviousSessionId = sess_data->previous_session;
+
        req->Flags = 0; /* MBZ */
        /* to enable echos and oplocks */
        req->hdr.CreditRequest = cpu_to_le16(3);
@@ -642,199 +644,368 @@ ssetup_ntlmssp_authenticate:
        req->Capabilities = 0;
        req->Channel = 0; /* MBZ */
 
-       iov[0].iov_base = (char *)req;
+       sess_data->iov[0].iov_base = (char *)req;
        /* 4 for rfc1002 length field and 1 for pad */
-       iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+       sess_data->iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+       /*
+        * This variable will be used to clear the buffer
+        * allocated above in case of any error in the calling function.
+        */
+       sess_data->buf0_type = CIFS_SMALL_BUFFER;
 
-       if (ses->sectype == Kerberos) {
-#ifdef CONFIG_CIFS_UPCALL
-               struct cifs_spnego_msg *msg;
+       return 0;
+}
 
-               spnego_key = cifs_get_spnego_key(ses);
-               if (IS_ERR(spnego_key)) {
-                       rc = PTR_ERR(spnego_key);
-                       spnego_key = NULL;
-                       goto ssetup_exit;
-               }
+static void
+SMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
+{
+       free_rsp_buf(sess_data->buf0_type, sess_data->iov[0].iov_base);
+       sess_data->buf0_type = CIFS_NO_BUFFER;
+}
 
-               msg = spnego_key->payload.data[0];
-               /*
-                * check version field to make sure that cifs.upcall is
-                * sending us a response in an expected form
-                */
-               if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
-                       cifs_dbg(VFS,
-                                 "bad cifs.upcall version. Expected %d got %d",
-                                 CIFS_SPNEGO_UPCALL_VERSION, msg->version);
-                       rc = -EKEYREJECTED;
-                       goto ssetup_exit;
-               }
-               ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
-                                                GFP_KERNEL);
-               if (!ses->auth_key.response) {
-                       cifs_dbg(VFS,
-                               "Kerberos can't allocate (%u bytes) memory",
-                               msg->sesskey_len);
-                       rc = -ENOMEM;
-                       goto ssetup_exit;
-               }
-               ses->auth_key.len = msg->sesskey_len;
-               blob_length = msg->secblob_len;
-               iov[1].iov_base = msg->data + msg->sesskey_len;
-               iov[1].iov_len = blob_length;
-#else
-               rc = -EOPNOTSUPP;
-               goto ssetup_exit;
-#endif /* CONFIG_CIFS_UPCALL */
-       } else if (phase == NtLmNegotiate) { /* if not krb5 must be ntlmssp */
-               ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
-                                      GFP_KERNEL);
-               if (ntlmssp_blob == NULL) {
-                       rc = -ENOMEM;
-                       goto ssetup_exit;
-               }
-               build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
-               if (use_spnego) {
-                       /* blob_length = build_spnego_ntlmssp_blob(
-                                       &security_blob,
-                                       sizeof(struct _NEGOTIATE_MESSAGE),
-                                       ntlmssp_blob); */
-                       /* BB eventually need to add this */
-                       cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
-                       rc = -EOPNOTSUPP;
-                       kfree(ntlmssp_blob);
-                       goto ssetup_exit;
-               } else {
-                       blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
-                       /* with raw NTLMSSP we don't encapsulate in SPNEGO */
-                       security_blob = ntlmssp_blob;
-               }
-               iov[1].iov_base = security_blob;
-               iov[1].iov_len = blob_length;
-       } else if (phase == NtLmAuthenticate) {
-               req->hdr.SessionId = ses->Suid;
-               rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
-                                            nls_cp);
-               if (rc) {
-                       cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n",
-                                rc);
-                       goto ssetup_exit; /* BB double check error handling */
-               }
-               if (use_spnego) {
-                       /* blob_length = build_spnego_ntlmssp_blob(
-                                                       &security_blob,
-                                                       blob_length,
-                                                       ntlmssp_blob); */
-                       cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
-                       rc = -EOPNOTSUPP;
-                       kfree(ntlmssp_blob);
-                       goto ssetup_exit;
-               } else {
-                       security_blob = ntlmssp_blob;
-               }
-               iov[1].iov_base = security_blob;
-               iov[1].iov_len = blob_length;
-       } else {
-               cifs_dbg(VFS, "illegal ntlmssp phase\n");
-               rc = -EIO;
-               goto ssetup_exit;
-       }
+static int
+SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
 
        /* Testing shows that buffer offset must be at location of Buffer[0] */
        req->SecurityBufferOffset =
-                               cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
-                                           1 /* pad */ - 4 /* rfc1001 len */);
-       req->SecurityBufferLength = cpu_to_le16(blob_length);
+               cpu_to_le16(sizeof(struct smb2_sess_setup_req) -
+                       1 /* pad */ - 4 /* rfc1001 len */);
+       req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
 
-       inc_rfc1001_len(req, blob_length - 1 /* pad */);
+       inc_rfc1001_len(req, sess_data->iov[1].iov_len - 1 /* pad */);
 
        /* BB add code to build os and lm fields */
 
-       rc = SendReceive2(xid, ses, iov, 2, &resp_buftype,
-                         CIFS_LOG_ERROR | CIFS_NEG_OP);
+       rc = SendReceive2(sess_data->xid, sess_data->ses,
+                               sess_data->iov, 2,
+                               &sess_data->buf0_type,
+                               CIFS_LOG_ERROR | CIFS_NEG_OP);
 
-       kfree(security_blob);
-       rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
-       ses->Suid = rsp->hdr.SessionId;
-       if (resp_buftype != CIFS_NO_BUFFER &&
-           rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
-               if (phase != NtLmNegotiate) {
-                       cifs_dbg(VFS, "Unexpected more processing error\n");
-                       goto ssetup_exit;
-               }
-               if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
-                               le16_to_cpu(rsp->SecurityBufferOffset)) {
-                       cifs_dbg(VFS, "Invalid security buffer offset %d\n",
-                                le16_to_cpu(rsp->SecurityBufferOffset));
-                       rc = -EIO;
-                       goto ssetup_exit;
+       return rc;
+}
+
+static int
+SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
+{
+       int rc = 0;
+       struct cifs_ses *ses = sess_data->ses;
+
+       mutex_lock(&ses->server->srv_mutex);
+       if (ses->server->sign && ses->server->ops->generate_signingkey) {
+               rc = ses->server->ops->generate_signingkey(ses);
+               kfree(ses->auth_key.response);
+               ses->auth_key.response = NULL;
+               if (rc) {
+                       cifs_dbg(FYI,
+                               "SMB3 session key generation failed\n");
+                       mutex_unlock(&ses->server->srv_mutex);
+                       goto keygen_exit;
                }
+       }
+       if (!ses->server->session_estab) {
+               ses->server->sequence_number = 0x2;
+               ses->server->session_estab = true;
+       }
+       mutex_unlock(&ses->server->srv_mutex);
+
+       cifs_dbg(FYI, "SMB2/3 session established successfully\n");
+       spin_lock(&GlobalMid_Lock);
+       ses->status = CifsGood;
+       ses->need_reconnect = false;
+       spin_unlock(&GlobalMid_Lock);
 
-               /* NTLMSSP Negotiate sent now processing challenge (response) */
-               phase = NtLmChallenge; /* process ntlmssp challenge */
-               rc = 0; /* MORE_PROCESSING is not an error here but expected */
-               rc = decode_ntlmssp_challenge(rsp->Buffer,
-                               le16_to_cpu(rsp->SecurityBufferLength), ses);
+keygen_exit:
+       if (!ses->server->sign) {
+               kfree(ses->auth_key.response);
+               ses->auth_key.response = NULL;
+       }
+       return rc;
+}
+
+#ifdef CONFIG_CIFS_UPCALL
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct cifs_spnego_msg *msg;
+       struct key *spnego_key = NULL;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out;
+
+       spnego_key = cifs_get_spnego_key(ses);
+       if (IS_ERR(spnego_key)) {
+               rc = PTR_ERR(spnego_key);
+               spnego_key = NULL;
+               goto out;
        }
 
+       msg = spnego_key->payload.data[0];
        /*
-        * BB eventually add code for SPNEGO decoding of NtlmChallenge blob,
-        * but at least the raw NTLMSSP case works.
+        * check version field to make sure that cifs.upcall is
+        * sending us a response in an expected form
         */
+       if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
+               cifs_dbg(VFS,
+                         "bad cifs.upcall version. Expected %d got %d",
+                         CIFS_SPNEGO_UPCALL_VERSION, msg->version);
+               rc = -EKEYREJECTED;
+               goto out_put_spnego_key;
+       }
+
+       ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+                                        GFP_KERNEL);
+       if (!ses->auth_key.response) {
+               cifs_dbg(VFS,
+                       "Kerberos can't allocate (%u bytes) memory",
+                       msg->sesskey_len);
+               rc = -ENOMEM;
+               goto out_put_spnego_key;
+       }
+       ses->auth_key.len = msg->sesskey_len;
+
+       sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
+       sess_data->iov[1].iov_len = msg->secblob_len;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       if (rc)
+               goto out_put_spnego_key;
+
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+       ses->Suid = rsp->hdr.SessionId;
+
+       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
+
+       rc = SMB2_sess_establish_session(sess_data);
+out_put_spnego_key:
+       key_invalidate(spnego_key);
+       key_put(spnego_key);
+out:
+       sess_data->result = rc;
+       sess_data->func = NULL;
+       SMB2_sess_free_buffer(sess_data);
+}
+#else
+static void
+SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
+{
+       cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
+       sess_data->result = -EOPNOTSUPP;
+       sess_data->func = NULL;
+}
+#endif
+
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
+
+static void
+SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+       char *ntlmssp_blob = NULL;
+       bool use_spnego = false; /* else use raw ntlmssp */
+       u16 blob_length = 0;
+
        /*
-        * No tcon so can't do
-        * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
+        * If memory allocation is successful, caller of this function
+        * frees it.
         */
-       if (rc != 0)
-               goto ssetup_exit;
+       ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
+       if (!ses->ntlmssp) {
+               rc = -ENOMEM;
+               goto out_err;
+       }
+       ses->ntlmssp->sesskey_per_smbsess = true;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out_err;
+
+       ntlmssp_blob = kmalloc(sizeof(struct _NEGOTIATE_MESSAGE),
+                              GFP_KERNEL);
+       if (ntlmssp_blob == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       build_ntlmssp_negotiate_blob(ntlmssp_blob, ses);
+       if (use_spnego) {
+               /* BB eventually need to add this */
+               cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+               rc = -EOPNOTSUPP;
+               goto out;
+       } else {
+               blob_length = sizeof(struct _NEGOTIATE_MESSAGE);
+               /* with raw NTLMSSP we don't encapsulate in SPNEGO */
+       }
+       sess_data->iov[1].iov_base = ntlmssp_blob;
+       sess_data->iov[1].iov_len = blob_length;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+       /* If true, rc here is expected and not an error */
+       if (sess_data->buf0_type != CIFS_NO_BUFFER &&
+               rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
+               rc = 0;
+
+       if (rc)
+               goto out;
+
+       if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
+                       le16_to_cpu(rsp->SecurityBufferOffset)) {
+               cifs_dbg(VFS, "Invalid security buffer offset %d\n",
+                       le16_to_cpu(rsp->SecurityBufferOffset));
+               rc = -EIO;
+               goto out;
+       }
+       rc = decode_ntlmssp_challenge(rsp->Buffer,
+                       le16_to_cpu(rsp->SecurityBufferLength), ses);
+       if (rc)
+               goto out;
+
+       cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
+
 
+       ses->Suid = rsp->hdr.SessionId;
        ses->session_flags = le16_to_cpu(rsp->SessionFlags);
        if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
                cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
-ssetup_exit:
-       free_rsp_buf(resp_buftype, rsp);
-
-       /* if ntlmssp, and negotiate succeeded, proceed to authenticate phase */
-       if ((phase == NtLmChallenge) && (rc == 0))
-               goto ssetup_ntlmssp_authenticate;
 
+out:
+       kfree(ntlmssp_blob);
+       SMB2_sess_free_buffer(sess_data);
        if (!rc) {
-               mutex_lock(&server->srv_mutex);
-               if (server->sign && server->ops->generate_signingkey) {
-                       rc = server->ops->generate_signingkey(ses);
-                       kfree(ses->auth_key.response);
-                       ses->auth_key.response = NULL;
-                       if (rc) {
-                               cifs_dbg(FYI,
-                                       "SMB3 session key generation failed\n");
-                               mutex_unlock(&server->srv_mutex);
-                               goto keygen_exit;
-                       }
-               }
-               if (!server->session_estab) {
-                       server->sequence_number = 0x2;
-                       server->session_estab = true;
-               }
-               mutex_unlock(&server->srv_mutex);
-
-               cifs_dbg(FYI, "SMB2/3 session established successfully\n");
-               spin_lock(&GlobalMid_Lock);
-               ses->status = CifsGood;
-               ses->need_reconnect = false;
-               spin_unlock(&GlobalMid_Lock);
+               sess_data->result = 0;
+               sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
+               return;
        }
+out_err:
+       kfree(ses->ntlmssp);
+       ses->ntlmssp = NULL;
+       sess_data->result = rc;
+       sess_data->func = NULL;
+}
 
-keygen_exit:
-       if (!server->sign) {
-               kfree(ses->auth_key.response);
-               ses->auth_key.response = NULL;
+static void
+SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
+{
+       int rc;
+       struct cifs_ses *ses = sess_data->ses;
+       struct smb2_sess_setup_req *req;
+       struct smb2_sess_setup_rsp *rsp = NULL;
+       unsigned char *ntlmssp_blob = NULL;
+       bool use_spnego = false; /* else use raw ntlmssp */
+       u16 blob_length = 0;
+
+       rc = SMB2_sess_alloc_buffer(sess_data);
+       if (rc)
+               goto out;
+
+       req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
+       req->hdr.SessionId = ses->Suid;
+
+       rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses,
+                                       sess_data->nls_cp);
+       if (rc) {
+               cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
+               goto out;
        }
-       if (spnego_key) {
-               key_invalidate(spnego_key);
-               key_put(spnego_key);
+
+       if (use_spnego) {
+               /* BB eventually need to add this */
+               cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
+               rc = -EOPNOTSUPP;
+               goto out;
        }
+       sess_data->iov[1].iov_base = ntlmssp_blob;
+       sess_data->iov[1].iov_len = blob_length;
+
+       rc = SMB2_sess_sendreceive(sess_data);
+       if (rc)
+               goto out;
+
+       rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
+
+       ses->Suid = rsp->hdr.SessionId;
+       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+       if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
+               cifs_dbg(VFS, "SMB3 encryption not supported yet\n");
+
+       rc = SMB2_sess_establish_session(sess_data);
+out:
+       kfree(ntlmssp_blob);
+       SMB2_sess_free_buffer(sess_data);
        kfree(ses->ntlmssp);
+       ses->ntlmssp = NULL;
+       sess_data->result = rc;
+       sess_data->func = NULL;
+}
 
+static int
+SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
+{
+       if (ses->sectype != Kerberos && ses->sectype != RawNTLMSSP)
+               ses->sectype = RawNTLMSSP;
+
+       switch (ses->sectype) {
+       case Kerberos:
+               sess_data->func = SMB2_auth_kerberos;
+               break;
+       case RawNTLMSSP:
+               sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
+               break;
+       default:
+               cifs_dbg(VFS, "secType %d not supported!\n", ses->sectype);
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+int
+SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
+               const struct nls_table *nls_cp)
+{
+       int rc = 0;
+       struct TCP_Server_Info *server = ses->server;
+       struct SMB2_sess_data *sess_data;
+
+       cifs_dbg(FYI, "Session Setup\n");
+
+       if (!server) {
+               WARN(1, "%s: server is NULL!\n", __func__);
+               return -EIO;
+       }
+
+       sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
+       if (!sess_data)
+               return -ENOMEM;
+
+       rc = SMB2_select_sec(ses, sess_data);
+       if (rc)
+               goto out;
+       sess_data->xid = xid;
+       sess_data->ses = ses;
+       sess_data->buf0_type = CIFS_NO_BUFFER;
+       sess_data->nls_cp = (struct nls_table *) nls_cp;
+
+       while (sess_data->func)
+               sess_data->func(sess_data);
+
+       rc = sess_data->result;
+out:
+       kfree(sess_data);
        return rc;
 }
 
@@ -1164,7 +1335,7 @@ create_durable_v2_buf(struct cifs_fid *pfid)
 
        buf->dcontext.Timeout = 0; /* Should this be configurable by workload */
        buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
-       get_random_bytes(buf->dcontext.CreateGuid, 16);
+       generate_random_uuid(buf->dcontext.CreateGuid);
        memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
 
        /* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
@@ -2057,6 +2228,7 @@ smb2_async_readv(struct cifs_readdata *rdata)
        if (rdata->credits) {
                buf->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
                                                SMB2_MAX_BUFFER_SIZE));
+               buf->CreditRequest = buf->CreditCharge;
                spin_lock(&server->req_lock);
                server->credits += rdata->credits -
                                                le16_to_cpu(buf->CreditCharge);
@@ -2243,6 +2415,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
        if (wdata->credits) {
                req->hdr.CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
                                                    SMB2_MAX_BUFFER_SIZE));
+               req->hdr.CreditRequest = req->hdr.CreditCharge;
                spin_lock(&server->req_lock);
                server->credits += wdata->credits -
                                        le16_to_cpu(req->hdr.CreditCharge);
index ff88d9f..fd3709e 100644 (file)
@@ -276,7 +276,7 @@ struct smb2_sess_setup_req {
        __le32 Channel;
        __le16 SecurityBufferOffset;
        __le16 SecurityBufferLength;
-       __le64 PreviousSessionId;
+       __u64 PreviousSessionId;
        __u8   Buffer[1];       /* variable length GSS security buffer */
 } __packed;
 
index 5e23f64..20af518 100644 (file)
@@ -33,7 +33,8 @@
 
 #define MAX_EA_VALUE_SIZE 65535
 #define CIFS_XATTR_CIFS_ACL "system.cifs_acl"
-
+#define CIFS_XATTR_ATTRIB "cifs.dosattrib"  /* full name: user.cifs.dosattrib */
+#define CIFS_XATTR_CREATETIME "cifs.creationtime"  /* user.cifs.creationtime */
 /* BB need to add server (Samba e.g) support for security and trusted prefix */
 
 enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT };
@@ -144,6 +145,54 @@ out:
        return rc;
 }
 
+static int cifs_attrib_get(struct dentry *dentry,
+                          struct inode *inode, void *value,
+                          size_t size)
+{
+       ssize_t rc;
+       __u32 *pattribute;
+
+       rc = cifs_revalidate_dentry_attr(dentry);
+
+       if (rc)
+               return rc;
+
+       if ((value == NULL) || (size == 0))
+               return sizeof(__u32);
+       else if (size < sizeof(__u32))
+               return -ERANGE;
+
+       /* return dos attributes as pseudo xattr */
+       pattribute = (__u32 *)value;
+       *pattribute = CIFS_I(inode)->cifsAttrs;
+
+       return sizeof(__u32);
+}
+
+static int cifs_creation_time_get(struct dentry *dentry, struct inode *inode,
+                                 void *value, size_t size)
+{
+       ssize_t rc;
+       __u64 * pcreatetime;
+
+       rc = cifs_revalidate_dentry_attr(dentry);
+       if (rc)
+               return rc;
+
+       if ((value == NULL) || (size == 0))
+               return sizeof(__u64);
+       else if (size < sizeof(__u64))
+               return -ERANGE;
+
+       /* return dos attributes as pseudo xattr */
+       pcreatetime = (__u64 *)value;
+       *pcreatetime = CIFS_I(inode)->createtime;
+       return sizeof(__u64);
+
+       return rc;
+}
+
+
 static int cifs_xattr_get(const struct xattr_handler *handler,
                          struct dentry *dentry, struct inode *inode,
                          const char *name, void *value, size_t size)
@@ -168,10 +217,19 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
                rc = -ENOMEM;
                goto out;
        }
-       /* return dos attributes as pseudo xattr */
+
        /* return alt name if available as pseudo attr */
        switch (handler->flags) {
        case XATTR_USER:
+               cifs_dbg(FYI, "%s:querying user xattr %s\n", __func__, name);
+               if (strcmp(name, CIFS_XATTR_ATTRIB) == 0) {
+                       rc = cifs_attrib_get(dentry, inode, value, size);
+                       break;
+               } else if (strcmp(name, CIFS_XATTR_CREATETIME) == 0) {
+                       rc = cifs_creation_time_get(dentry, inode, value, size);
+                       break;
+               }
+
                if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
                        goto out;
 
index c1e9f29..f2d7402 100644 (file)
@@ -1209,6 +1209,8 @@ COMPATIBLE_IOCTL(WDIOC_SETOPTIONS)
 COMPATIBLE_IOCTL(WDIOC_KEEPALIVE)
 COMPATIBLE_IOCTL(WDIOC_SETTIMEOUT)
 COMPATIBLE_IOCTL(WDIOC_GETTIMEOUT)
+COMPATIBLE_IOCTL(WDIOC_SETPRETIMEOUT)
+COMPATIBLE_IOCTL(WDIOC_GETPRETIMEOUT)
 /* Big R */
 COMPATIBLE_IOCTL(RNDGETENTCNT)
 COMPATIBLE_IOCTL(RNDADDTOENTCNT)
index d42ff52..d8072bc 100644 (file)
@@ -778,7 +778,7 @@ try_again:
 fail:
        EXOFS_DBGMSG("Error: writepage_strip(0x%lx, 0x%lx)=>%d\n",
                     inode->i_ino, page->index, ret);
-       set_bit(AS_EIO, &page->mapping->flags);
+       mapping_set_error(page->mapping, -EIO);
        unlock_page(page);
        return ret;
 }
index 207ba8d..a4b531b 100644 (file)
@@ -428,10 +428,10 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
        if (!nop || !nop->fh_to_dentry)
                return ERR_PTR(-ESTALE);
        result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
-       if (!result)
-               result = ERR_PTR(-ESTALE);
-       if (IS_ERR(result))
-               return result;
+       if (PTR_ERR(result) == -ENOMEM)
+               return ERR_CAST(result);
+       if (IS_ERR_OR_NULL(result))
+               return ERR_PTR(-ESTALE);
 
        if (d_is_dir(result)) {
                /*
@@ -541,6 +541,8 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
 
  err_result:
        dput(result);
+       if (err != -ENOMEM)
+               err = -ESTALE;
        return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(exportfs_decode_fh);
index b4cbee9..0094923 100644 (file)
@@ -88,7 +88,7 @@ static void ext4_finish_bio(struct bio *bio)
 
                if (bio->bi_error) {
                        SetPageError(page);
-                       set_bit(AS_EIO, &page->mapping->flags);
+                       mapping_set_error(page->mapping, -EIO);
                }
                bh = head = page_buffers(page);
                /*
index 0d0177c..9ae194f 100644 (file)
@@ -75,7 +75,7 @@ static void f2fs_write_end_io(struct bio *bio)
                fscrypt_pullback_bio_page(&page, true);
 
                if (unlikely(bio->bi_error)) {
-                       set_bit(AS_EIO, &page->mapping->flags);
+                       mapping_set_error(page->mapping, -EIO);
                        f2fs_stop_checkpoint(sbi, true);
                }
                end_page_writeback(page);
index 5bb565f..31f8ca0 100644 (file)
@@ -269,8 +269,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal,
                         * filemap_fdatawait_range(), set it again so
                         * that user process can get -EIO from fsync().
                         */
-                       set_bit(AS_EIO,
-                               &jinode->i_vfs_inode->i_mapping->flags);
+                       mapping_set_error(jinode->i_vfs_inode->i_mapping, -EIO);
 
                        if (!ret)
                                ret = err;
index dcd96aa..cf4c636 100644 (file)
@@ -110,8 +110,9 @@ static struct kernfs_node *kernfs_common_ancestor(struct kernfs_node *a,
  * kn_to:   /n1/n2/n3         [depth=3]
  * result:  /../..
  *
- * return value: length of the string.  If greater than buflen,
- * then contents of buf are undefined.  On error, -1 is returned.
+ * Returns the length of the full path.  If the full length is equal to or
+ * greater than @buflen, @buf contains the truncated path with the trailing
+ * '\0'.  On error, -errno is returned.
  */
 static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
                                        struct kernfs_node *kn_from,
@@ -119,9 +120,8 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
 {
        struct kernfs_node *kn, *common;
        const char parent_str[] = "/..";
-       size_t depth_from, depth_to, len = 0, nlen = 0;
-       char *p;
-       int i;
+       size_t depth_from, depth_to, len = 0;
+       int i, j;
 
        if (!kn_from)
                kn_from = kernfs_root(kn_to)->kn;
@@ -131,7 +131,7 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
 
        common = kernfs_common_ancestor(kn_from, kn_to);
        if (WARN_ON(!common))
-               return -1;
+               return -EINVAL;
 
        depth_to = kernfs_depth(common, kn_to);
        depth_from = kernfs_depth(common, kn_from);
@@ -144,22 +144,16 @@ static int kernfs_path_from_node_locked(struct kernfs_node *kn_to,
                               len < buflen ? buflen - len : 0);
 
        /* Calculate how many bytes we need for the rest */
-       for (kn = kn_to; kn != common; kn = kn->parent)
-               nlen += strlen(kn->name) + 1;
-
-       if (len + nlen >= buflen)
-               return len + nlen;
-
-       p = buf + len + nlen;
-       *p = '\0';
-       for (kn = kn_to; kn != common; kn = kn->parent) {
-               size_t tmp = strlen(kn->name);
-               p -= tmp;
-               memcpy(p, kn->name, tmp);
-               *(--p) = '/';
+       for (i = depth_to - 1; i >= 0; i--) {
+               for (kn = kn_to, j = 0; j < i; j++)
+                       kn = kn->parent;
+               len += strlcpy(buf + len, "/",
+                              len < buflen ? buflen - len : 0);
+               len += strlcpy(buf + len, kn->name,
+                              len < buflen ? buflen - len : 0);
        }
 
-       return len + nlen;
+       return len;
 }
 
 /**
@@ -185,29 +179,6 @@ int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
        return ret;
 }
 
-/**
- * kernfs_path_len - determine the length of the full path of a given node
- * @kn: kernfs_node of interest
- *
- * The returned length doesn't include the space for the terminating '\0'.
- */
-size_t kernfs_path_len(struct kernfs_node *kn)
-{
-       size_t len = 0;
-       unsigned long flags;
-
-       spin_lock_irqsave(&kernfs_rename_lock, flags);
-
-       do {
-               len += strlen(kn->name) + 1;
-               kn = kn->parent;
-       } while (kn && kn->parent);
-
-       spin_unlock_irqrestore(&kernfs_rename_lock, flags);
-
-       return len;
-}
-
 /**
  * kernfs_path_from_node - build path of node @to relative to @from.
  * @from: parent kernfs_node relative to which we need to build the path
@@ -220,8 +191,9 @@ size_t kernfs_path_len(struct kernfs_node *kn)
  * path (which includes '..'s) as needed to reach from @from to @to is
  * returned.
  *
- * If @buf isn't long enough, the return value will be greater than @buflen
- * and @buf contents are undefined.
+ * Returns the length of the full path.  If the full length is equal to or
+ * greater than @buflen, @buf contains the truncated path with the trailing
+ * '\0'.  On error, -errno is returned.
  */
 int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
                          char *buf, size_t buflen)
@@ -236,28 +208,6 @@ int kernfs_path_from_node(struct kernfs_node *to, struct kernfs_node *from,
 }
 EXPORT_SYMBOL_GPL(kernfs_path_from_node);
 
-/**
- * kernfs_path - build full path of a given node
- * @kn: kernfs_node of interest
- * @buf: buffer to copy @kn's name into
- * @buflen: size of @buf
- *
- * Builds and returns the full path of @kn in @buf of @buflen bytes.  The
- * path is built from the end of @buf so the returned pointer usually
- * doesn't match @buf.  If @buf isn't long enough, @buf is nul terminated
- * and %NULL is returned.
- */
-char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
-{
-       int ret;
-
-       ret = kernfs_path_from_node(kn, NULL, buf, buflen);
-       if (ret < 0 || ret >= buflen)
-               return NULL;
-       return buf;
-}
-EXPORT_SYMBOL_GPL(kernfs_path);
-
 /**
  * pr_cont_kernfs_name - pr_cont name of a kernfs_node
  * @kn: kernfs_node of interest
index 2257a13..184a15e 100644 (file)
@@ -6,8 +6,6 @@
 #ifndef _LOCKD_PROCFS_H
 #define _LOCKD_PROCFS_H
 
-#include <linux/kconfig.h>
-
 #if IS_ENABLED(CONFIG_PROC_FS)
 int lockd_create_procfs(void);
 void lockd_remove_procfs(void);
index a7f601c..5b4eed2 100644 (file)
@@ -4668,6 +4668,31 @@ int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen)
 }
 EXPORT_SYMBOL(generic_readlink);
 
+/**
+ * vfs_get_link - get symlink body
+ * @dentry: dentry on which to get symbolic link
+ * @done: caller needs to free returned data with this
+ *
+ * Calls security hook and i_op->get_link() on the supplied inode.
+ *
+ * It does not touch atime.  That's up to the caller if necessary.
+ *
+ * Does not work on "special" symlinks like /proc/$$/fd/N
+ */
+const char *vfs_get_link(struct dentry *dentry, struct delayed_call *done)
+{
+       const char *res = ERR_PTR(-EINVAL);
+       struct inode *inode = d_inode(dentry);
+
+       if (d_is_symlink(dentry)) {
+               res = ERR_PTR(security_inode_readlink(dentry));
+               if (!res)
+                       res = inode->i_op->get_link(dentry, inode, done);
+       }
+       return res;
+}
+EXPORT_SYMBOL(vfs_get_link);
+
 /* get the link contents into pagecache */
 const char *page_get_link(struct dentry *dentry, struct inode *inode,
                          struct delayed_call *callback)
index 5f7b053..6de1570 100644 (file)
@@ -76,7 +76,7 @@ static void nfs_dns_cache_revisit(struct cache_deferred_req *d, int toomany)
 
        dreq = container_of(d, struct nfs_cache_defer_req, deferred_req);
 
-       complete_all(&dreq->completion);
+       complete(&dreq->completion);
        nfs_cache_defer_req_put(dreq);
 }
 
index 52a2831..532d8e2 100644 (file)
@@ -31,8 +31,6 @@
 struct nfs_callback_data {
        unsigned int users;
        struct svc_serv *serv;
-       struct svc_rqst *rqst;
-       struct task_struct *task;
 };
 
 static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
@@ -89,15 +87,6 @@ nfs4_callback_svc(void *vrqstp)
        return 0;
 }
 
-/*
- * Prepare to bring up the NFSv4 callback service
- */
-static struct svc_rqst *
-nfs4_callback_up(struct svc_serv *serv)
-{
-       return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
-}
-
 #if defined(CONFIG_NFS_V4_1)
 /*
  * The callback service for NFSv4.1 callbacks
@@ -139,29 +128,6 @@ nfs41_callback_svc(void *vrqstp)
        return 0;
 }
 
-/*
- * Bring up the NFSv4.1 callback service
- */
-static struct svc_rqst *
-nfs41_callback_up(struct svc_serv *serv)
-{
-       struct svc_rqst *rqstp;
-
-       INIT_LIST_HEAD(&serv->sv_cb_list);
-       spin_lock_init(&serv->sv_cb_lock);
-       init_waitqueue_head(&serv->sv_cb_waitq);
-       rqstp = svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
-       dprintk("--> %s return %d\n", __func__, PTR_ERR_OR_ZERO(rqstp));
-       return rqstp;
-}
-
-static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
-               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
-{
-       *rqstpp = nfs41_callback_up(serv);
-       *callback_svc = nfs41_callback_svc;
-}
-
 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                struct svc_serv *serv)
 {
@@ -173,13 +139,6 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                xprt->bc_serv = serv;
 }
 #else
-static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
-               struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
-{
-       *rqstpp = ERR_PTR(-ENOTSUPP);
-       *callback_svc = ERR_PTR(-ENOTSUPP);
-}
-
 static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
                struct svc_serv *serv)
 {
@@ -189,45 +148,22 @@ static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
 static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
                                  struct svc_serv *serv)
 {
-       struct svc_rqst *rqstp;
-       int (*callback_svc)(void *vrqstp);
-       struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+       int nrservs = nfs_callback_nr_threads;
        int ret;
 
        nfs_callback_bc_serv(minorversion, xprt, serv);
 
-       if (cb_info->task)
-               return 0;
+       if (nrservs < NFS4_MIN_NR_CALLBACK_THREADS)
+               nrservs = NFS4_MIN_NR_CALLBACK_THREADS;
 
-       switch (minorversion) {
-       case 0:
-               /* v4.0 callback setup */
-               rqstp = nfs4_callback_up(serv);
-               callback_svc = nfs4_callback_svc;
-               break;
-       default:
-               nfs_minorversion_callback_svc_setup(serv,
-                               &rqstp, &callback_svc);
-       }
-
-       if (IS_ERR(rqstp))
-               return PTR_ERR(rqstp);
-
-       svc_sock_update_bufs(serv);
+       if (serv->sv_nrthreads-1 == nrservs)
+               return 0;
 
-       cb_info->serv = serv;
-       cb_info->rqst = rqstp;
-       cb_info->task = kthread_create(callback_svc, cb_info->rqst,
-                                   "nfsv4.%u-svc", minorversion);
-       if (IS_ERR(cb_info->task)) {
-               ret = PTR_ERR(cb_info->task);
-               svc_exit_thread(cb_info->rqst);
-               cb_info->rqst = NULL;
-               cb_info->task = NULL;
+       ret = serv->sv_ops->svo_setup(serv, NULL, nrservs);
+       if (ret) {
+               serv->sv_ops->svo_setup(serv, NULL, 0);
                return ret;
        }
-       rqstp->rq_task = cb_info->task;
-       wake_up_process(cb_info->task);
        dprintk("nfs_callback_up: service started\n");
        return 0;
 }
@@ -281,19 +217,41 @@ err_bind:
        return ret;
 }
 
-static struct svc_serv_ops nfs_cb_sv_ops = {
+static struct svc_serv_ops nfs40_cb_sv_ops = {
+       .svo_function           = nfs4_callback_svc,
        .svo_enqueue_xprt       = svc_xprt_do_enqueue,
+       .svo_setup              = svc_set_num_threads,
+       .svo_module             = THIS_MODULE,
+};
+#if defined(CONFIG_NFS_V4_1)
+static struct svc_serv_ops nfs41_cb_sv_ops = {
+       .svo_function           = nfs41_callback_svc,
+       .svo_enqueue_xprt       = svc_xprt_do_enqueue,
+       .svo_setup              = svc_set_num_threads,
+       .svo_module             = THIS_MODULE,
+};
+
+struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+       [0] = &nfs40_cb_sv_ops,
+       [1] = &nfs41_cb_sv_ops,
+};
+#else
+struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+       [0] = &nfs40_cb_sv_ops,
+       [1] = NULL,
 };
+#endif
 
 static struct svc_serv *nfs_callback_create_svc(int minorversion)
 {
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
        struct svc_serv *serv;
+       struct svc_serv_ops *sv_ops;
 
        /*
         * Check whether we're already up and running.
         */
-       if (cb_info->task) {
+       if (cb_info->serv) {
                /*
                 * Note: increase service usage, because later in case of error
                 * svc_destroy() will be called.
@@ -302,6 +260,17 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
                return cb_info->serv;
        }
 
+       switch (minorversion) {
+       case 0:
+               sv_ops = nfs4_cb_sv_ops[0];
+               break;
+       default:
+               sv_ops = nfs4_cb_sv_ops[1];
+       }
+
+       if (sv_ops == NULL)
+               return ERR_PTR(-ENOTSUPP);
+
        /*
         * Sanity check: if there's no task,
         * we should be the first user ...
@@ -310,11 +279,12 @@ static struct svc_serv *nfs_callback_create_svc(int minorversion)
                printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
                        cb_info->users);
 
-       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, &nfs_cb_sv_ops);
+       serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, sv_ops);
        if (!serv) {
                printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
                return ERR_PTR(-ENOMEM);
        }
+       cb_info->serv = serv;
        /* As there is only one thread we need to over-ride the
         * default maximum of 80 connections
         */
@@ -357,6 +327,8 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
         * thread exits.
         */
 err_net:
+       if (!cb_info->users)
+               cb_info->serv = NULL;
        svc_destroy(serv);
 err_create:
        mutex_unlock(&nfs_callback_mutex);
@@ -374,18 +346,18 @@ err_start:
 void nfs_callback_down(int minorversion, struct net *net)
 {
        struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
+       struct svc_serv *serv;
 
        mutex_lock(&nfs_callback_mutex);
-       nfs_callback_down_net(minorversion, cb_info->serv, net);
+       serv = cb_info->serv;
+       nfs_callback_down_net(minorversion, serv, net);
        cb_info->users--;
-       if (cb_info->users == 0 && cb_info->task != NULL) {
-               kthread_stop(cb_info->task);
-               dprintk("nfs_callback_down: service stopped\n");
-               svc_exit_thread(cb_info->rqst);
+       if (cb_info->users == 0) {
+               svc_get(serv);
+               serv->sv_ops->svo_setup(serv, NULL, 0);
+               svc_destroy(serv);
                dprintk("nfs_callback_down: service destroyed\n");
                cb_info->serv = NULL;
-               cb_info->rqst = NULL;
-               cb_info->task = NULL;
        }
        mutex_unlock(&nfs_callback_mutex);
 }
index 5fe1cec..c701c30 100644 (file)
@@ -179,6 +179,15 @@ extern __be32 nfs4_callback_devicenotify(
        struct cb_devicenotifyargs *args,
        void *dummy, struct cb_process_state *cps);
 
+struct cb_notify_lock_args {
+       struct nfs_fh                   cbnl_fh;
+       struct nfs_lowner               cbnl_owner;
+       bool                            cbnl_valid;
+};
+
+extern __be32 nfs4_callback_notify_lock(struct cb_notify_lock_args *args,
+                                        void *dummy,
+                                        struct cb_process_state *cps);
 #endif /* CONFIG_NFS_V4_1 */
 extern int check_gss_callback_principal(struct nfs_client *, struct svc_rqst *);
 extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
@@ -198,6 +207,9 @@ extern void nfs_callback_down(int minorversion, struct net *net);
 #define NFS41_BC_MIN_CALLBACKS 1
 #define NFS41_BC_MAX_CALLBACKS 1
 
+#define NFS4_MIN_NR_CALLBACK_THREADS 1
+
 extern unsigned int nfs_callback_set_tcpport;
+extern unsigned short nfs_callback_nr_threads;
 
 #endif /* __LINUX_FS_NFS_CALLBACK_H */
index f953ef6..e9aa235 100644 (file)
@@ -628,4 +628,20 @@ out:
        dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
        return status;
 }
+
+__be32 nfs4_callback_notify_lock(struct cb_notify_lock_args *args, void *dummy,
+                                struct cb_process_state *cps)
+{
+       if (!cps->clp) /* set in cb_sequence */
+               return htonl(NFS4ERR_OP_NOT_IN_SESSION);
+
+       dprintk_rcu("NFS: CB_NOTIFY_LOCK request from %s\n",
+               rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
+
+       /* Don't wake anybody if the string looked bogus */
+       if (args->cbnl_valid)
+               __wake_up(&cps->clp->cl_lock_waitq, TASK_NORMAL, 0, args);
+
+       return htonl(NFS4_OK);
+}
 #endif /* CONFIG_NFS_V4_1 */
index 656f68f..eb094c6 100644 (file)
@@ -35,6 +35,7 @@
                                         (1 + 3) * 4) // seqid, 3 slotids
 #define CB_OP_RECALLANY_RES_MAXSZ      (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_RECALLSLOT_RES_MAXSZ     (CB_OP_HDR_RES_MAXSZ)
+#define CB_OP_NOTIFY_LOCK_RES_MAXSZ    (CB_OP_HDR_RES_MAXSZ)
 #endif /* CONFIG_NFS_V4_1 */
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
@@ -72,7 +73,7 @@ static int nfs4_encode_void(struct svc_rqst *rqstp, __be32 *p, void *dummy)
        return xdr_ressize_check(rqstp, p);
 }
 
-static __be32 *read_buf(struct xdr_stream *xdr, int nbytes)
+static __be32 *read_buf(struct xdr_stream *xdr, size_t nbytes)
 {
        __be32 *p;
 
@@ -534,6 +535,49 @@ static __be32 decode_recallslot_args(struct svc_rqst *rqstp,
        return 0;
 }
 
+static __be32 decode_lockowner(struct xdr_stream *xdr, struct cb_notify_lock_args *args)
+{
+       __be32          *p;
+       unsigned int    len;
+
+       p = read_buf(xdr, 12);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_BADXDR);
+
+       p = xdr_decode_hyper(p, &args->cbnl_owner.clientid);
+       len = be32_to_cpu(*p);
+
+       p = read_buf(xdr, len);
+       if (unlikely(p == NULL))
+               return htonl(NFS4ERR_BADXDR);
+
+       /* Only try to decode if the length is right */
+       if (len == 20) {
+               p += 2; /* skip "lock id:" */
+               args->cbnl_owner.s_dev = be32_to_cpu(*p++);
+               xdr_decode_hyper(p, &args->cbnl_owner.id);
+               args->cbnl_valid = true;
+       } else {
+               args->cbnl_owner.s_dev = 0;
+               args->cbnl_owner.id = 0;
+               args->cbnl_valid = false;
+       }
+       return 0;
+}
+
+static __be32 decode_notify_lock_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, struct cb_notify_lock_args *args)
+{
+       __be32 status;
+
+       status = decode_fh(xdr, &args->cbnl_fh);
+       if (unlikely(status != 0))
+               goto out;
+       status = decode_lockowner(xdr, args);
+out:
+       dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
+       return status;
+}
+
 #endif /* CONFIG_NFS_V4_1 */
 
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
@@ -746,6 +790,7 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_RECALL_SLOT:
        case OP_CB_LAYOUTRECALL:
        case OP_CB_NOTIFY_DEVICEID:
+       case OP_CB_NOTIFY_LOCK:
                *op = &callback_ops[op_nr];
                break;
 
@@ -753,7 +798,6 @@ preprocess_nfs41_op(int nop, unsigned int op_nr, struct callback_op **op)
        case OP_CB_PUSH_DELEG:
        case OP_CB_RECALLABLE_OBJ_AVAIL:
        case OP_CB_WANTS_CANCELLED:
-       case OP_CB_NOTIFY_LOCK:
                return htonl(NFS4ERR_NOTSUPP);
 
        default:
@@ -1006,6 +1050,11 @@ static struct callback_op callback_ops[] = {
                .decode_args = (callback_decode_arg_t)decode_recallslot_args,
                .res_maxsize = CB_OP_RECALLSLOT_RES_MAXSZ,
        },
+       [OP_CB_NOTIFY_LOCK] = {
+               .process_op = (callback_process_op_t)nfs4_callback_notify_lock,
+               .decode_args = (callback_decode_arg_t)decode_notify_lock_args,
+               .res_maxsize = CB_OP_NOTIFY_LOCK_RES_MAXSZ,
+       },
 #endif /* CONFIG_NFS_V4_1 */
 };
 
index 1e10678..7555ba8 100644 (file)
@@ -313,7 +313,10 @@ static struct nfs_client *nfs_match_client(const struct nfs_client_initdata *dat
                        continue;
                /* Match the full socket address */
                if (!rpc_cmp_addr_port(sap, clap))
-                       continue;
+                       /* Match all xprt_switch full socket addresses */
+                       if (!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient,
+                                                          sap))
+                               continue;
 
                atomic_inc(&clp->cl_count);
                return clp;
@@ -785,7 +788,8 @@ int nfs_probe_fsinfo(struct nfs_server *server, struct nfs_fh *mntfh, struct nfs
        }
 
        fsinfo.fattr = fattr;
-       fsinfo.layouttype = 0;
+       fsinfo.nlayouttypes = 0;
+       memset(fsinfo.layouttype, 0, sizeof(fsinfo.layouttype));
        error = clp->rpc_ops->fsinfo(server, mntfh, &fsinfo);
        if (error < 0)
                goto out_error;
@@ -1078,7 +1082,7 @@ void nfs_clients_init(struct net *net)
        idr_init(&nn->cb_ident_idr);
 #endif
        spin_lock_init(&nn->nfs_client_lock);
-       nn->boot_time = CURRENT_TIME;
+       nn->boot_time = ktime_get_real();
 }
 
 #ifdef CONFIG_PROC_FS
index 322c258..dff600a 100644 (file)
@@ -41,6 +41,17 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
        set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
 }
 
+static bool
+nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
+               fmode_t flags)
+{
+       if (delegation != NULL && (delegation->type & flags) == flags &&
+           !test_bit(NFS_DELEGATION_REVOKED, &delegation->flags) &&
+           !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
+               return true;
+       return false;
+}
+
 static int
 nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
 {
@@ -50,8 +61,7 @@ nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
        flags &= FMODE_READ|FMODE_WRITE;
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL && (delegation->type & flags) == flags &&
-           !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
+       if (nfs4_is_valid_delegation(delegation, flags)) {
                if (mark)
                        nfs_mark_delegation_referenced(delegation);
                ret = 1;
@@ -185,15 +195,13 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred,
                        rcu_read_unlock();
                        put_rpccred(oldcred);
                        trace_nfs4_reclaim_delegation(inode, res->delegation_type);
-               } else {
-                       /* We appear to have raced with a delegation return. */
-                       spin_unlock(&delegation->lock);
-                       rcu_read_unlock();
-                       nfs_inode_set_delegation(inode, cred, res);
+                       return;
                }
-       } else {
-               rcu_read_unlock();
+               /* We appear to have raced with a delegation return. */
+               spin_unlock(&delegation->lock);
        }
+       rcu_read_unlock();
+       nfs_inode_set_delegation(inode, cred, res);
 }
 
 static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
@@ -642,28 +650,49 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
        rcu_read_unlock();
 }
 
-static void nfs_revoke_delegation(struct inode *inode)
+static void nfs_mark_delegation_revoked(struct nfs_server *server,
+               struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
+       delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
+       nfs_mark_return_delegation(server, delegation);
+}
+
+static bool nfs_revoke_delegation(struct inode *inode,
+               const nfs4_stateid *stateid)
 {
        struct nfs_delegation *delegation;
+       nfs4_stateid tmp;
+       bool ret = false;
+
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL) {
-               set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
-               nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
-       }
+       if (delegation == NULL)
+               goto out;
+       if (stateid == NULL) {
+               nfs4_stateid_copy(&tmp, &delegation->stateid);
+               stateid = &tmp;
+       } else if (!nfs4_stateid_match(stateid, &delegation->stateid))
+               goto out;
+       nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
+       ret = true;
+out:
        rcu_read_unlock();
+       if (ret)
+               nfs_inode_find_state_and_recover(inode, stateid);
+       return ret;
 }
 
-void nfs_remove_bad_delegation(struct inode *inode)
+void nfs_remove_bad_delegation(struct inode *inode,
+               const nfs4_stateid *stateid)
 {
        struct nfs_delegation *delegation;
 
-       nfs_revoke_delegation(inode);
+       if (!nfs_revoke_delegation(inode, stateid))
+               return;
        delegation = nfs_inode_detach_delegation(inode);
-       if (delegation) {
-               nfs_inode_find_state_and_recover(inode, &delegation->stateid);
+       if (delegation)
                nfs_free_delegation(delegation);
-       }
 }
 EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
 
@@ -786,8 +815,15 @@ static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
 {
        struct nfs_delegation *delegation;
 
-       list_for_each_entry_rcu(delegation, &server->delegations, super_list)
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list) {
+               /*
+                * If the delegation may have been admin revoked, then we
+                * cannot reclaim it.
+                */
+               if (test_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags))
+                       continue;
                set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+       }
 }
 
 /**
@@ -851,6 +887,141 @@ restart:
        rcu_read_unlock();
 }
 
+static inline bool nfs4_server_rebooted(const struct nfs_client *clp)
+{
+       return (clp->cl_state & (BIT(NFS4CLNT_CHECK_LEASE) |
+                               BIT(NFS4CLNT_LEASE_EXPIRED) |
+                               BIT(NFS4CLNT_SESSION_RESET))) != 0;
+}
+
+static void nfs_mark_test_expired_delegation(struct nfs_server *server,
+           struct nfs_delegation *delegation)
+{
+       if (delegation->stateid.type == NFS4_INVALID_STATEID_TYPE)
+               return;
+       clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
+       set_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGATION_EXPIRED, &server->nfs_client->cl_state);
+}
+
+static void nfs_inode_mark_test_expired_delegation(struct nfs_server *server,
+               struct inode *inode)
+{
+       struct nfs_delegation *delegation;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation)
+               nfs_mark_test_expired_delegation(server, delegation);
+       rcu_read_unlock();
+
+}
+
+static void nfs_delegation_mark_test_expired_server(struct nfs_server *server)
+{
+       struct nfs_delegation *delegation;
+
+       list_for_each_entry_rcu(delegation, &server->delegations, super_list)
+               nfs_mark_test_expired_delegation(server, delegation);
+}
+
+/**
+ * nfs_mark_test_expired_all_delegations - mark all delegations for testing
+ * @clp: nfs_client to process
+ *
+ * Iterates through all the delegations associated with this server and
+ * marks them as needing to be checked for validity.
+ */
+void nfs_mark_test_expired_all_delegations(struct nfs_client *clp)
+{
+       struct nfs_server *server;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link)
+               nfs_delegation_mark_test_expired_server(server);
+       rcu_read_unlock();
+}
+
+/**
+ * nfs_reap_expired_delegations - reap expired delegations
+ * @clp: nfs_client to process
+ *
+ * Iterates through all the delegations associated with this server and
+ * checks if they have may have been revoked. This function is usually
+ * expected to be called in cases where the server may have lost its
+ * lease.
+ */
+void nfs_reap_expired_delegations(struct nfs_client *clp)
+{
+       const struct nfs4_minor_version_ops *ops = clp->cl_mvops;
+       struct nfs_delegation *delegation;
+       struct nfs_server *server;
+       struct inode *inode;
+       struct rpc_cred *cred;
+       nfs4_stateid stateid;
+
+restart:
+       rcu_read_lock();
+       list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
+               list_for_each_entry_rcu(delegation, &server->delegations,
+                                                               super_list) {
+                       if (test_bit(NFS_DELEGATION_RETURNING,
+                                               &delegation->flags))
+                               continue;
+                       if (test_bit(NFS_DELEGATION_TEST_EXPIRED,
+                                               &delegation->flags) == 0)
+                               continue;
+                       if (!nfs_sb_active(server->super))
+                               continue;
+                       inode = nfs_delegation_grab_inode(delegation);
+                       if (inode == NULL) {
+                               rcu_read_unlock();
+                               nfs_sb_deactive(server->super);
+                               goto restart;
+                       }
+                       cred = get_rpccred_rcu(delegation->cred);
+                       nfs4_stateid_copy(&stateid, &delegation->stateid);
+                       clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags);
+                       rcu_read_unlock();
+                       if (cred != NULL &&
+                           ops->test_and_free_expired(server, &stateid, cred) < 0) {
+                               nfs_revoke_delegation(inode, &stateid);
+                               nfs_inode_find_state_and_recover(inode, &stateid);
+                       }
+                       put_rpccred(cred);
+                       if (nfs4_server_rebooted(clp)) {
+                               nfs_inode_mark_test_expired_delegation(server,inode);
+                               iput(inode);
+                               nfs_sb_deactive(server->super);
+                               return;
+                       }
+                       iput(inode);
+                       nfs_sb_deactive(server->super);
+                       goto restart;
+               }
+       }
+       rcu_read_unlock();
+}
+
+void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid)
+{
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_delegation *delegation;
+       bool found = false;
+
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation &&
+           nfs4_stateid_match_other(&delegation->stateid, stateid)) {
+               nfs_mark_test_expired_delegation(NFS_SERVER(inode), delegation);
+               found = true;
+       }
+       rcu_read_unlock();
+       if (found)
+               nfs4_schedule_state_manager(clp);
+}
+
 /**
  * nfs_delegations_present - check for existence of delegations
  * @clp: client state handle
@@ -893,7 +1064,7 @@ bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags,
        flags &= FMODE_READ|FMODE_WRITE;
        rcu_read_lock();
        delegation = rcu_dereference(nfsi->delegation);
-       ret = (delegation != NULL && (delegation->type & flags) == flags);
+       ret = nfs4_is_valid_delegation(delegation, flags);
        if (ret) {
                nfs4_stateid_copy(dst, &delegation->stateid);
                nfs_mark_delegation_referenced(delegation);
index 64724d2..e9d5557 100644 (file)
@@ -32,6 +32,7 @@ enum {
        NFS_DELEGATION_REFERENCED,
        NFS_DELEGATION_RETURNING,
        NFS_DELEGATION_REVOKED,
+       NFS_DELEGATION_TEST_EXPIRED,
 };
 
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
@@ -47,11 +48,14 @@ void nfs_expire_unused_delegation_types(struct nfs_client *clp, fmode_t flags);
 void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 int nfs_client_return_marked_delegations(struct nfs_client *clp);
 int nfs_delegations_present(struct nfs_client *clp);
-void nfs_remove_bad_delegation(struct inode *inode);
+void nfs_remove_bad_delegation(struct inode *inode, const nfs4_stateid *stateid);
 
 void nfs_delegation_mark_reclaim(struct nfs_client *clp);
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
 
+void nfs_mark_test_expired_all_delegations(struct nfs_client *clp);
+void nfs_reap_expired_delegations(struct nfs_client *clp);
+
 /* NFSv4 delegation-related procedures */
 int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4_stateid *stateid, int issync);
 int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state *state, const nfs4_stateid *stateid, fmode_t type);
@@ -62,6 +66,8 @@ void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
 int nfs4_have_delegation(struct inode *inode, fmode_t flags);
 int nfs4_check_delegation(struct inode *inode, fmode_t flags);
 bool nfs4_delegation_flush_on_close(const struct inode *inode);
+void nfs_inode_find_delegation_state_and_recover(struct inode *inode,
+               const nfs4_stateid *stateid);
 
 #endif
 
index 06e0bf0..5f1af4c 100644 (file)
@@ -435,11 +435,11 @@ int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
                return 0;
 
        nfsi = NFS_I(inode);
-       if (entry->fattr->fileid == nfsi->fileid)
-               return 1;
-       if (nfs_compare_fh(entry->fh, &nfsi->fh) == 0)
-               return 1;
-       return 0;
+       if (entry->fattr->fileid != nfsi->fileid)
+               return 0;
+       if (entry->fh->size && nfs_compare_fh(entry->fh, &nfsi->fh) != 0)
+               return 0;
+       return 1;
 }
 
 static
@@ -496,6 +496,14 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
                return;
        if (!(entry->fattr->valid & NFS_ATTR_FATTR_FSID))
                return;
+       if (filename.len == 0)
+               return;
+       /* Validate that the name doesn't contain any illegal '\0' */
+       if (strnlen(filename.name, filename.len) != filename.len)
+               return;
+       /* ...or '/' */
+       if (strnchr(filename.name, filename.len, '/'))
+               return;
        if (filename.name[0] == '.') {
                if (filename.len == 1)
                        return;
@@ -517,6 +525,8 @@ again:
                                        &entry->fattr->fsid))
                        goto out;
                if (nfs_same_file(dentry, entry)) {
+                       if (!entry->fh->size)
+                               goto out;
                        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                        status = nfs_refresh_inode(d_inode(dentry), entry->fattr);
                        if (!status)
@@ -529,6 +539,10 @@ again:
                        goto again;
                }
        }
+       if (!entry->fh->size) {
+               d_lookup_done(dentry);
+               goto out;
+       }
 
        inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr, entry->label);
        alias = d_splice_alias(inode, dentry);
index 72b7d13..bd81bcf 100644 (file)
@@ -387,7 +387,7 @@ static void nfs_direct_complete(struct nfs_direct_req *dreq)
                dreq->iocb->ki_complete(dreq->iocb, res, 0);
        }
 
-       complete_all(&dreq->completion);
+       complete(&dreq->completion);
 
        nfs_direct_req_release(dreq);
 }
index 2efbdde..9ea85ae 100644 (file)
@@ -520,7 +520,9 @@ const struct address_space_operations nfs_file_aops = {
        .invalidatepage = nfs_invalidate_page,
        .releasepage = nfs_release_page,
        .direct_IO = nfs_direct_IO,
+#ifdef CONFIG_MIGRATION
        .migratepage = nfs_migrate_page,
+#endif
        .launder_page = nfs_launder_page,
        .is_dirty_writeback = nfs_check_dirty_writeback,
        .error_remove_page = generic_error_remove_page,
@@ -685,11 +687,6 @@ out_noconflict:
        goto out;
 }
 
-static int do_vfs_lock(struct file *file, struct file_lock *fl)
-{
-       return locks_lock_file_wait(file, fl);
-}
-
 static int
 do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
 {
@@ -722,7 +719,7 @@ do_unlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
        if (!is_local)
                status = NFS_PROTO(inode)->lock(filp, cmd, fl);
        else
-               status = do_vfs_lock(filp, fl);
+               status = locks_lock_file_wait(filp, fl);
        return status;
 }
 
@@ -747,7 +744,7 @@ do_setlk(struct file *filp, int cmd, struct file_lock *fl, int is_local)
        if (!is_local)
                status = NFS_PROTO(inode)->lock(filp, cmd, fl);
        else
-               status = do_vfs_lock(filp, fl);
+               status = locks_lock_file_wait(filp, fl);
        if (status < 0)
                goto out;
 
index 51b5136..98ace12 100644 (file)
@@ -1080,7 +1080,7 @@ static int ff_layout_async_handle_error_v4(struct rpc_task *task,
        case -NFS4ERR_BAD_STATEID:
                if (state == NULL)
                        break;
-               nfs_remove_bad_delegation(state->inode);
+               nfs_remove_bad_delegation(state->inode, NULL);
        case -NFS4ERR_OPENMODE:
                if (state == NULL)
                        break;
index a6acce6..80bcc0b 100644 (file)
@@ -534,12 +534,9 @@ void nfs_clear_pnfs_ds_commit_verifiers(struct pnfs_ds_commit_info *cinfo)
 }
 #endif
 
-
 #ifdef CONFIG_MIGRATION
 extern int nfs_migrate_page(struct address_space *,
                struct page *, struct page *, enum migrate_mode);
-#else
-#define nfs_migrate_page NULL
 #endif
 
 static inline int
@@ -562,7 +559,6 @@ void nfs_init_cinfo_from_dreq(struct nfs_commit_info *cinfo,
 extern ssize_t nfs_dreq_bytes_left(struct nfs_direct_req *dreq);
 
 /* nfs4proc.c */
-extern void __nfs4_read_done_cb(struct nfs_pgio_header *);
 extern struct nfs_client *nfs4_init_client(struct nfs_client *clp,
                            const struct nfs_client_initdata *);
 extern int nfs40_walk_client_list(struct nfs_client *clp,
@@ -571,6 +567,9 @@ extern int nfs40_walk_client_list(struct nfs_client *clp,
 extern int nfs41_walk_client_list(struct nfs_client *clp,
                                struct nfs_client **result,
                                struct rpc_cred *cred);
+extern int nfs4_test_session_trunk(struct rpc_clnt *,
+                               struct rpc_xprt *,
+                               void *);
 
 static inline struct inode *nfs_igrab_and_active(struct inode *inode)
 {
index f0e06e4..fbce0d8 100644 (file)
@@ -29,7 +29,7 @@ struct nfs_net {
        int cb_users[NFS4_MAX_MINOR_VERSION + 1];
 #endif
        spinlock_t nfs_client_lock;
-       struct timespec boot_time;
+       ktime_t boot_time;
 #ifdef CONFIG_PROC_FS
        struct proc_dir_entry *proc_nfsfs;
 #endif
index 64b43b4..6085019 100644 (file)
@@ -443,6 +443,7 @@ int nfs42_proc_layoutstats_generic(struct nfs_server *server,
        task = rpc_run_task(&task_setup);
        if (IS_ERR(task))
                return PTR_ERR(task);
+       rpc_put_task(task);
        return 0;
 }
 
index 9bf64ea..9b3a82a 100644 (file)
@@ -39,6 +39,7 @@ enum nfs4_client_state {
        NFS4CLNT_BIND_CONN_TO_SESSION,
        NFS4CLNT_MOVED,
        NFS4CLNT_LEASE_MOVED,
+       NFS4CLNT_DELEGATION_EXPIRED,
 };
 
 #define NFS4_RENEW_TIMEOUT             0x01
@@ -57,8 +58,11 @@ struct nfs4_minor_version_ops {
                        struct nfs_fsinfo *);
        void    (*free_lock_state)(struct nfs_server *,
                        struct nfs4_lock_state *);
+       int     (*test_and_free_expired)(struct nfs_server *,
+                       nfs4_stateid *, struct rpc_cred *);
        struct nfs_seqid *
                (*alloc_seqid)(struct nfs_seqid_counter *, gfp_t);
+       int     (*session_trunk)(struct rpc_clnt *, struct rpc_xprt *, void *);
        const struct rpc_call_ops *call_sync_ops;
        const struct nfs4_state_recovery_ops *reboot_recovery_ops;
        const struct nfs4_state_recovery_ops *nograce_recovery_ops;
@@ -156,6 +160,7 @@ enum {
        NFS_STATE_RECLAIM_NOGRACE,      /* OPEN stateid needs to recover state */
        NFS_STATE_POSIX_LOCKS,          /* Posix locks are supported */
        NFS_STATE_RECOVERY_FAILED,      /* OPEN stateid state recovery failed */
+       NFS_STATE_MAY_NOTIFY_LOCK,      /* server may CB_NOTIFY_LOCK */
 };
 
 struct nfs4_state {
@@ -203,6 +208,11 @@ struct nfs4_state_recovery_ops {
                struct rpc_cred *);
 };
 
+struct nfs4_add_xprt_data {
+       struct nfs_client       *clp;
+       struct rpc_cred         *cred;
+};
+
 struct nfs4_state_maintenance_ops {
        int (*sched_state_renewal)(struct nfs_client *, struct rpc_cred *, unsigned);
        struct rpc_cred * (*get_state_renewal_cred_locked)(struct nfs_client *);
@@ -278,6 +288,8 @@ extern int nfs4_proc_get_lease_time(struct nfs_client *clp,
                struct nfs_fsinfo *fsinfo);
 extern int nfs4_proc_layoutcommit(struct nfs4_layoutcommit_data *data,
                                  bool sync);
+extern int nfs4_detect_session_trunking(struct nfs_client *clp,
+               struct nfs41_exchange_id_res *res, struct rpc_xprt *xprt);
 
 static inline bool
 is_ds_only_client(struct nfs_client *clp)
@@ -439,7 +451,7 @@ extern void nfs4_schedule_path_down_recovery(struct nfs_client *clp);
 extern int nfs4_schedule_stateid_recovery(const struct nfs_server *, struct nfs4_state *);
 extern int nfs4_schedule_migration_recovery(const struct nfs_server *);
 extern void nfs4_schedule_lease_moved_recovery(struct nfs_client *);
-extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags);
+extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags, bool);
 extern void nfs41_handle_server_scope(struct nfs_client *,
                                      struct nfs41_server_scope **);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
@@ -471,6 +483,7 @@ extern struct nfs_subversion nfs_v4;
 struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, struct nfs_subversion *);
 extern bool nfs4_disable_idmapping;
 extern unsigned short max_session_slots;
+extern unsigned short max_session_cb_slots;
 extern unsigned short send_implementation_id;
 extern bool recover_lost_locks;
 
index cd3b7cf..074ac71 100644 (file)
@@ -199,6 +199,9 @@ struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init)
        clp->cl_minorversion = cl_init->minorversion;
        clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion];
        clp->cl_mig_gen = 1;
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+       init_waitqueue_head(&clp->cl_lock_waitq);
+#endif
        return clp;
 
 error:
@@ -562,15 +565,15 @@ out:
 /*
  * Returns true if the client IDs match
  */
-static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
+static bool nfs4_match_clientids(u64 a, u64 b)
 {
-       if (a->cl_clientid != b->cl_clientid) {
+       if (a != b) {
                dprintk("NFS: --> %s client ID %llx does not match %llx\n",
-                       __func__, a->cl_clientid, b->cl_clientid);
+                       __func__, a, b);
                return false;
        }
        dprintk("NFS: --> %s client ID %llx matches %llx\n",
-               __func__, a->cl_clientid, b->cl_clientid);
+               __func__, a, b);
        return true;
 }
 
@@ -578,17 +581,15 @@ static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
  * Returns true if the server major ids match
  */
 static bool
-nfs4_check_clientid_trunking(struct nfs_client *a, struct nfs_client *b)
+nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1,
+                               struct nfs41_server_owner *o2)
 {
-       struct nfs41_server_owner *o1 = a->cl_serverowner;
-       struct nfs41_server_owner *o2 = b->cl_serverowner;
-
        if (o1->major_id_sz != o2->major_id_sz)
                goto out_major_mismatch;
        if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
                goto out_major_mismatch;
 
-       dprintk("NFS: --> %s server owners match\n", __func__);
+       dprintk("NFS: --> %s server owner major IDs match\n", __func__);
        return true;
 
 out_major_mismatch:
@@ -597,6 +598,100 @@ out_major_mismatch:
        return false;
 }
 
+/*
+ * Returns true if server minor ids match
+ */
+static bool
+nfs4_check_serverowner_minor_id(struct nfs41_server_owner *o1,
+                               struct nfs41_server_owner *o2)
+{
+       /* Check eir_server_owner so_minor_id */
+       if (o1->minor_id != o2->minor_id)
+               goto out_minor_mismatch;
+
+       dprintk("NFS: --> %s server owner minor IDs match\n", __func__);
+       return true;
+
+out_minor_mismatch:
+       dprintk("NFS: --> %s server owner minor IDs do not match\n", __func__);
+       return false;
+}
+
+/*
+ * Returns true if the server scopes match
+ */
+static bool
+nfs4_check_server_scope(struct nfs41_server_scope *s1,
+                       struct nfs41_server_scope *s2)
+{
+       if (s1->server_scope_sz != s2->server_scope_sz)
+               goto out_scope_mismatch;
+       if (memcmp(s1->server_scope, s2->server_scope,
+                  s1->server_scope_sz) != 0)
+               goto out_scope_mismatch;
+
+       dprintk("NFS: --> %s server scopes match\n", __func__);
+       return true;
+
+out_scope_mismatch:
+       dprintk("NFS: --> %s server scopes do not match\n",
+               __func__);
+       return false;
+}
+
+/**
+ * nfs4_detect_session_trunking - Checks for session trunking.
+ *
+ * Called after a successful EXCHANGE_ID on a multi-addr connection.
+ * Upon success, add the transport.
+ *
+ * @clp:    original mount nfs_client
+ * @res:    result structure from an exchange_id using the original mount
+ *          nfs_client with a new multi_addr transport
+ *
+ * Returns zero on success, otherwise -EINVAL
+ *
+ * Note: since the exchange_id for the new multi_addr transport uses the
+ * same nfs_client from the original mount, the cl_owner_id is reused,
+ * so eir_clientowner is the same.
+ */
+int nfs4_detect_session_trunking(struct nfs_client *clp,
+                                struct nfs41_exchange_id_res *res,
+                                struct rpc_xprt *xprt)
+{
+       /* Check eir_clientid */
+       if (!nfs4_match_clientids(clp->cl_clientid, res->clientid))
+               goto out_err;
+
+       /* Check eir_server_owner so_major_id */
+       if (!nfs4_check_serverowner_major_id(clp->cl_serverowner,
+                                            res->server_owner))
+               goto out_err;
+
+       /* Check eir_server_owner so_minor_id */
+       if (!nfs4_check_serverowner_minor_id(clp->cl_serverowner,
+                                            res->server_owner))
+               goto out_err;
+
+       /* Check eir_server_scope */
+       if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope))
+               goto out_err;
+
+       /* Session trunking passed, add the xprt */
+       rpc_clnt_xprt_switch_add_xprt(clp->cl_rpcclient, xprt);
+
+       pr_info("NFS:  %s: Session trunking succeeded for %s\n",
+               clp->cl_hostname,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       return 0;
+out_err:
+       pr_info("NFS:  %s: Session trunking failed for %s\n", clp->cl_hostname,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       return -EINVAL;
+}
+
 /**
  * nfs41_walk_client_list - Find nfs_client that matches a client/server owner
  *
@@ -650,7 +745,7 @@ int nfs41_walk_client_list(struct nfs_client *new,
                if (pos->cl_cons_state != NFS_CS_READY)
                        continue;
 
-               if (!nfs4_match_clientids(pos, new))
+               if (!nfs4_match_clientids(pos->cl_clientid, new->cl_clientid))
                        continue;
 
                /*
@@ -658,7 +753,8 @@ int nfs41_walk_client_list(struct nfs_client *new,
                 * client id trunking. In either case, we want to fall back
                 * to using the existing nfs_client.
                 */
-               if (!nfs4_check_clientid_trunking(pos, new))
+               if (!nfs4_check_serverowner_major_id(pos->cl_serverowner,
+                                                    new->cl_serverowner))
                        continue;
 
                /* Unlike NFSv4.0, we know that NFSv4.1 always uses the
index 0e32752..ad917bd 100644 (file)
@@ -99,8 +99,8 @@ static int nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred,
 #ifdef CONFIG_NFS_V4_1
 static int nfs41_test_stateid(struct nfs_server *, nfs4_stateid *,
                struct rpc_cred *);
-static int nfs41_free_stateid(struct nfs_server *, nfs4_stateid *,
-               struct rpc_cred *);
+static int nfs41_free_stateid(struct nfs_server *, const nfs4_stateid *,
+               struct rpc_cred *, bool);
 #endif
 
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
@@ -328,6 +328,33 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
        kunmap_atomic(start);
 }
 
+static void nfs4_test_and_free_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       const struct nfs4_minor_version_ops *ops = server->nfs_client->cl_mvops;
+
+       ops->test_and_free_expired(server, stateid, cred);
+}
+
+static void __nfs4_free_revoked_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       stateid->type = NFS4_REVOKED_STATEID_TYPE;
+       nfs4_test_and_free_stateid(server, stateid, cred);
+}
+
+static void nfs4_free_revoked_stateid(struct nfs_server *server,
+               const nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       nfs4_stateid tmp;
+
+       nfs4_stateid_copy(&tmp, stateid);
+       __nfs4_free_revoked_stateid(server, &tmp, cred);
+}
+
 static long nfs4_update_delay(long *timeout)
 {
        long ret;
@@ -370,13 +397,23 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
        exception->delay = 0;
        exception->recovering = 0;
        exception->retry = 0;
+
+       if (stateid == NULL && state != NULL)
+               stateid = &state->stateid;
+
        switch(errorcode) {
                case 0:
                        return 0;
-               case -NFS4ERR_OPENMODE:
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
                case -NFS4ERR_BAD_STATEID:
+                       if (inode != NULL && stateid != NULL) {
+                               nfs_inode_find_state_and_recover(inode,
+                                               stateid);
+                               goto wait_on_recovery;
+                       }
+               case -NFS4ERR_OPENMODE:
                        if (inode) {
                                int err;
 
@@ -395,12 +432,6 @@ static int nfs4_do_handle_exception(struct nfs_server *server,
                        if (ret < 0)
                                break;
                        goto wait_on_recovery;
-               case -NFS4ERR_EXPIRED:
-                       if (state != NULL) {
-                               ret = nfs4_schedule_stateid_recovery(server, state);
-                               if (ret < 0)
-                                       break;
-                       }
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_STALE_CLIENTID:
                        nfs4_schedule_lease_recovery(clp);
@@ -616,6 +647,7 @@ int nfs40_setup_sequence(struct nfs4_slot_table *tbl,
        }
        spin_unlock(&tbl->slot_tbl_lock);
 
+       slot->privileged = args->sa_privileged ? 1 : 0;
        args->sa_slot = slot;
        res->sr_slot = slot;
 
@@ -723,12 +755,20 @@ static int nfs41_sequence_process(struct rpc_task *task,
        /* Check the SEQUENCE operation status */
        switch (res->sr_status) {
        case 0:
+               /* If previous op on slot was interrupted and we reused
+                * the seq# and got a reply from the cache, then retry
+                */
+               if (task->tk_status == -EREMOTEIO && interrupted) {
+                       ++slot->seq_nr;
+                       goto retry_nowait;
+               }
                /* Update the slot's sequence and clientid lease timer */
                slot->seq_done = 1;
                clp = session->clp;
                do_renew_lease(clp, res->sr_timestamp);
                /* Check sequence flags */
-               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags);
+               nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags,
+                               !!slot->privileged);
                nfs41_update_target_slotid(slot->table, slot, res);
                break;
        case 1:
@@ -875,6 +915,7 @@ int nfs41_setup_sequence(struct nfs4_session *session,
        }
        spin_unlock(&tbl->slot_tbl_lock);
 
+       slot->privileged = args->sa_privileged ? 1 : 0;
        args->sa_slot = slot;
 
        dprintk("<-- %s slotid=%u seqid=%u\n", __func__,
@@ -1353,6 +1394,19 @@ static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
        nfs4_state_set_mode_locked(state, state->state | fmode);
 }
 
+#ifdef CONFIG_NFS_V4_1
+static bool nfs_open_stateid_recover_openmode(struct nfs4_state *state)
+{
+       if (state->n_rdonly && !test_bit(NFS_O_RDONLY_STATE, &state->flags))
+               return true;
+       if (state->n_wronly && !test_bit(NFS_O_WRONLY_STATE, &state->flags))
+               return true;
+       if (state->n_rdwr && !test_bit(NFS_O_RDWR_STATE, &state->flags))
+               return true;
+       return false;
+}
+#endif /* CONFIG_NFS_V4_1 */
+
 static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
 {
        struct nfs_client *clp = state->owner->so_server->nfs_client;
@@ -1369,11 +1423,12 @@ static void nfs_test_and_clear_all_open_stateid(struct nfs4_state *state)
 }
 
 static bool nfs_need_update_open_stateid(struct nfs4_state *state,
-               nfs4_stateid *stateid)
+               const nfs4_stateid *stateid, nfs4_stateid *freeme)
 {
        if (test_and_set_bit(NFS_OPEN_STATE, &state->flags) == 0)
                return true;
        if (!nfs4_stateid_match_other(stateid, &state->open_stateid)) {
+               nfs4_stateid_copy(freeme, &state->open_stateid);
                nfs_test_and_clear_all_open_stateid(state);
                return true;
        }
@@ -1437,7 +1492,9 @@ static void nfs_clear_open_stateid(struct nfs4_state *state,
                nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
 }
 
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
+static void nfs_set_open_stateid_locked(struct nfs4_state *state,
+               const nfs4_stateid *stateid, fmode_t fmode,
+               nfs4_stateid *freeme)
 {
        switch (fmode) {
                case FMODE_READ:
@@ -1449,14 +1506,18 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
                case FMODE_READ|FMODE_WRITE:
                        set_bit(NFS_O_RDWR_STATE, &state->flags);
        }
-       if (!nfs_need_update_open_stateid(state, stateid))
+       if (!nfs_need_update_open_stateid(state, stateid, freeme))
                return;
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                nfs4_stateid_copy(&state->stateid, stateid);
        nfs4_stateid_copy(&state->open_stateid, stateid);
 }
 
-static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
+static void __update_open_stateid(struct nfs4_state *state,
+               const nfs4_stateid *open_stateid,
+               const nfs4_stateid *deleg_stateid,
+               fmode_t fmode,
+               nfs4_stateid *freeme)
 {
        /*
         * Protect the call to nfs4_state_set_mode_locked and
@@ -1469,16 +1530,22 @@ static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_s
                set_bit(NFS_DELEGATED_STATE, &state->flags);
        }
        if (open_stateid != NULL)
-               nfs_set_open_stateid_locked(state, open_stateid, fmode);
+               nfs_set_open_stateid_locked(state, open_stateid, fmode, freeme);
        write_sequnlock(&state->seqlock);
        update_open_stateflags(state, fmode);
        spin_unlock(&state->owner->so_lock);
 }
 
-static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
+static int update_open_stateid(struct nfs4_state *state,
+               const nfs4_stateid *open_stateid,
+               const nfs4_stateid *delegation,
+               fmode_t fmode)
 {
+       struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs_client *clp = server->nfs_client;
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_delegation *deleg_cur;
+       nfs4_stateid freeme = {0};
        int ret = 0;
 
        fmode &= (FMODE_READ|FMODE_WRITE);
@@ -1500,7 +1567,8 @@ static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stat
                goto no_delegation_unlock;
 
        nfs_mark_delegation_referenced(deleg_cur);
-       __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
+       __update_open_stateid(state, open_stateid, &deleg_cur->stateid,
+                       fmode, &freeme);
        ret = 1;
 no_delegation_unlock:
        spin_unlock(&deleg_cur->lock);
@@ -1508,11 +1576,14 @@ no_delegation:
        rcu_read_unlock();
 
        if (!ret && open_stateid != NULL) {
-               __update_open_stateid(state, open_stateid, NULL, fmode);
+               __update_open_stateid(state, open_stateid, NULL, fmode, &freeme);
                ret = 1;
        }
        if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags))
-               nfs4_schedule_state_manager(state->owner->so_server->nfs_client);
+               nfs4_schedule_state_manager(clp);
+       if (freeme.type != 0)
+               nfs4_test_and_free_stateid(server, &freeme,
+                               state->owner->so_cred);
 
        return ret;
 }
@@ -1889,7 +1960,6 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_STALE_STATEID:
                        set_bit(NFS_DELEGATED_STATE, &state->flags);
-               case -NFS4ERR_EXPIRED:
                        /* Don't recall a delegation if it was lost */
                        nfs4_schedule_lease_recovery(server->nfs_client);
                        return -EAGAIN;
@@ -1901,6 +1971,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
                        return -EAGAIN;
                case -NFS4ERR_DELEG_REVOKED:
                case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_OPENMODE:
                        nfs_inode_find_state_and_recover(state->inode,
@@ -2382,9 +2453,10 @@ static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *sta
        return ret;
 }
 
-static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
+static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
 {
-       nfs_remove_bad_delegation(state->inode);
+       nfs_remove_bad_delegation(state->inode, stateid);
        write_seqlock(&state->seqlock);
        nfs4_stateid_copy(&state->stateid, &state->open_stateid);
        write_sequnlock(&state->seqlock);
@@ -2394,7 +2466,7 @@ static void nfs_finish_clear_delegation_stateid(struct nfs4_state *state)
 static void nfs40_clear_delegation_stateid(struct nfs4_state *state)
 {
        if (rcu_access_pointer(NFS_I(state->inode)->delegation) != NULL)
-               nfs_finish_clear_delegation_stateid(state);
+               nfs_finish_clear_delegation_stateid(state, NULL);
 }
 
 static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
@@ -2404,7 +2476,45 @@ static int nfs40_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
        return nfs4_open_expired(sp, state);
 }
 
+static int nfs40_test_and_free_expired_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       return -NFS4ERR_BAD_STATEID;
+}
+
 #if defined(CONFIG_NFS_V4_1)
+static int nfs41_test_and_free_expired_stateid(struct nfs_server *server,
+               nfs4_stateid *stateid,
+               struct rpc_cred *cred)
+{
+       int status;
+
+       switch (stateid->type) {
+       default:
+               break;
+       case NFS4_INVALID_STATEID_TYPE:
+       case NFS4_SPECIAL_STATEID_TYPE:
+               return -NFS4ERR_BAD_STATEID;
+       case NFS4_REVOKED_STATEID_TYPE:
+               goto out_free;
+       }
+
+       status = nfs41_test_stateid(server, stateid, cred);
+       switch (status) {
+       case -NFS4ERR_EXPIRED:
+       case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_DELEG_REVOKED:
+               break;
+       default:
+               return status;
+       }
+out_free:
+       /* Ack the revoked state to the server */
+       nfs41_free_stateid(server, stateid, cred, true);
+       return -NFS4ERR_EXPIRED;
+}
+
 static void nfs41_check_delegation_stateid(struct nfs4_state *state)
 {
        struct nfs_server *server = NFS_SERVER(state->inode);
@@ -2422,22 +2532,67 @@ static void nfs41_check_delegation_stateid(struct nfs4_state *state)
        }
 
        nfs4_stateid_copy(&stateid, &delegation->stateid);
+       if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags)) {
+               rcu_read_unlock();
+               nfs_finish_clear_delegation_stateid(state, &stateid);
+               return;
+       }
+
+       if (!test_and_clear_bit(NFS_DELEGATION_TEST_EXPIRED, &delegation->flags)) {
+               rcu_read_unlock();
+               return;
+       }
+
        cred = get_rpccred(delegation->cred);
        rcu_read_unlock();
-       status = nfs41_test_stateid(server, &stateid, cred);
+       status = nfs41_test_and_free_expired_stateid(server, &stateid, cred);
        trace_nfs4_test_delegation_stateid(state, NULL, status);
-
-       if (status != NFS_OK) {
-               /* Free the stateid unless the server explicitly
-                * informs us the stateid is unrecognized. */
-               if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, &stateid, cred);
-               nfs_finish_clear_delegation_stateid(state);
-       }
+       if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID)
+               nfs_finish_clear_delegation_stateid(state, &stateid);
 
        put_rpccred(cred);
 }
 
+/**
+ * nfs41_check_expired_locks - possibly free a lock stateid
+ *
+ * @state: NFSv4 state for an inode
+ *
+ * Returns NFS_OK if recovery for this stateid is now finished.
+ * Otherwise a negative NFS4ERR value is returned.
+ */
+static int nfs41_check_expired_locks(struct nfs4_state *state)
+{
+       int status, ret = NFS_OK;
+       struct nfs4_lock_state *lsp;
+       struct nfs_server *server = NFS_SERVER(state->inode);
+
+       if (!test_bit(LK_STATE_IN_USE, &state->flags))
+               goto out;
+       list_for_each_entry(lsp, &state->lock_states, ls_locks) {
+               if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
+                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
+
+                       status = nfs41_test_and_free_expired_stateid(server,
+                                       &lsp->ls_stateid,
+                                       cred);
+                       trace_nfs4_test_lock_stateid(state, lsp, status);
+                       if (status == -NFS4ERR_EXPIRED ||
+                           status == -NFS4ERR_BAD_STATEID) {
+                               clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
+                               lsp->ls_stateid.type = NFS4_INVALID_STATEID_TYPE;
+                               if (!recover_lost_locks)
+                                       set_bit(NFS_LOCK_LOST, &lsp->ls_flags);
+                       } else if (status != NFS_OK) {
+                               ret = status;
+                               break;
+                       }
+               }
+       };
+out:
+       return ret;
+}
+
 /**
  * nfs41_check_open_stateid - possibly free an open stateid
  *
@@ -2453,26 +2608,28 @@ static int nfs41_check_open_stateid(struct nfs4_state *state)
        struct rpc_cred *cred = state->owner->so_cred;
        int status;
 
-       /* If a state reset has been done, test_stateid is unneeded */
-       if ((test_bit(NFS_O_RDONLY_STATE, &state->flags) == 0) &&
-           (test_bit(NFS_O_WRONLY_STATE, &state->flags) == 0) &&
-           (test_bit(NFS_O_RDWR_STATE, &state->flags) == 0))
+       if (test_bit(NFS_OPEN_STATE, &state->flags) == 0) {
+               if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)  {
+                       if (nfs4_have_delegation(state->inode, state->state))
+                               return NFS_OK;
+                       return -NFS4ERR_OPENMODE;
+               }
                return -NFS4ERR_BAD_STATEID;
-
-       status = nfs41_test_stateid(server, stateid, cred);
+       }
+       status = nfs41_test_and_free_expired_stateid(server, stateid, cred);
        trace_nfs4_test_open_stateid(state, NULL, status);
-       if (status != NFS_OK) {
-               /* Free the stateid unless the server explicitly
-                * informs us the stateid is unrecognized. */
-               if (status != -NFS4ERR_BAD_STATEID)
-                       nfs41_free_stateid(server, stateid, cred);
-
+       if (status == -NFS4ERR_EXPIRED || status == -NFS4ERR_BAD_STATEID) {
                clear_bit(NFS_O_RDONLY_STATE, &state->flags);
                clear_bit(NFS_O_WRONLY_STATE, &state->flags);
                clear_bit(NFS_O_RDWR_STATE, &state->flags);
                clear_bit(NFS_OPEN_STATE, &state->flags);
+               stateid->type = NFS4_INVALID_STATEID_TYPE;
        }
-       return status;
+       if (status != NFS_OK)
+               return status;
+       if (nfs_open_stateid_recover_openmode(state))
+               return -NFS4ERR_OPENMODE;
+       return NFS_OK;
 }
 
 static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state)
@@ -2480,6 +2637,9 @@ static int nfs41_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *st
        int status;
 
        nfs41_check_delegation_stateid(state);
+       status = nfs41_check_expired_locks(state);
+       if (status != NFS_OK)
+               return status;
        status = nfs41_check_open_stateid(state);
        if (status != NFS_OK)
                status = nfs4_open_expired(sp, state);
@@ -2537,6 +2697,8 @@ static int _nfs4_open_and_get_state(struct nfs4_opendata *opendata,
                goto out;
        if (server->caps & NFS_CAP_POSIX_LOCK)
                set_bit(NFS_STATE_POSIX_LOCKS, &state->flags);
+       if (opendata->o_res.rflags & NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK)
+               set_bit(NFS_STATE_MAY_NOTIFY_LOCK, &state->flags);
 
        dentry = opendata->dentry;
        if (d_really_is_negative(dentry)) {
@@ -2899,9 +3061,12 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        break;
                case -NFS4ERR_ADMIN_REVOKED:
                case -NFS4ERR_STALE_STATEID:
+               case -NFS4ERR_EXPIRED:
+                       nfs4_free_revoked_stateid(server,
+                                       &calldata->arg.stateid,
+                                       task->tk_msg.rpc_cred);
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_BAD_STATEID:
-               case -NFS4ERR_EXPIRED:
                        if (!nfs4_stateid_match(&calldata->arg.stateid,
                                                &state->open_stateid)) {
                                rpc_restart_call_prepare(task);
@@ -4312,7 +4477,7 @@ static int nfs4_proc_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, s
        if (error == 0) {
                /* block layout checks this! */
                server->pnfs_blksize = fsinfo->blksize;
-               set_pnfs_layoutdriver(server, fhandle, fsinfo->layouttype);
+               set_pnfs_layoutdriver(server, fhandle, fsinfo);
        }
 
        return error;
@@ -4399,24 +4564,25 @@ static bool nfs4_error_stateid_expired(int err)
        return false;
 }
 
-void __nfs4_read_done_cb(struct nfs_pgio_header *hdr)
-{
-       nfs_invalidate_atime(hdr->inode);
-}
-
 static int nfs4_read_done_cb(struct rpc_task *task, struct nfs_pgio_header *hdr)
 {
        struct nfs_server *server = NFS_SERVER(hdr->inode);
 
        trace_nfs4_read(hdr, task->tk_status);
-       if (nfs4_async_handle_error(task, server,
-                                   hdr->args.context->state,
-                                   NULL) == -EAGAIN) {
-               rpc_restart_call_prepare(task);
-               return -EAGAIN;
+       if (task->tk_status < 0) {
+               struct nfs4_exception exception = {
+                       .inode = hdr->inode,
+                       .state = hdr->args.context->state,
+                       .stateid = &hdr->args.stateid,
+               };
+               task->tk_status = nfs4_async_handle_exception(task,
+                               server, task->tk_status, &exception);
+               if (exception.retry) {
+                       rpc_restart_call_prepare(task);
+                       return -EAGAIN;
+               }
        }
 
-       __nfs4_read_done_cb(hdr);
        if (task->tk_status > 0)
                renew_lease(server, hdr->timestamp);
        return 0;
@@ -4445,6 +4611,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_pgio_header *hdr)
                return -EAGAIN;
        if (nfs4_read_stateid_changed(task, &hdr->args))
                return -EAGAIN;
+       if (task->tk_status > 0)
+               nfs_invalidate_atime(hdr->inode);
        return hdr->pgio_done_cb ? hdr->pgio_done_cb(task, hdr) :
                                    nfs4_read_done_cb(task, hdr);
 }
@@ -4482,11 +4650,19 @@ static int nfs4_write_done_cb(struct rpc_task *task,
        struct inode *inode = hdr->inode;
 
        trace_nfs4_write(hdr, task->tk_status);
-       if (nfs4_async_handle_error(task, NFS_SERVER(inode),
-                                   hdr->args.context->state,
-                                   NULL) == -EAGAIN) {
-               rpc_restart_call_prepare(task);
-               return -EAGAIN;
+       if (task->tk_status < 0) {
+               struct nfs4_exception exception = {
+                       .inode = hdr->inode,
+                       .state = hdr->args.context->state,
+                       .stateid = &hdr->args.stateid,
+               };
+               task->tk_status = nfs4_async_handle_exception(task,
+                               NFS_SERVER(inode), task->tk_status,
+                               &exception);
+               if (exception.retry) {
+                       rpc_restart_call_prepare(task);
+                       return -EAGAIN;
+               }
        }
        if (task->tk_status >= 0) {
                renew_lease(NFS_SERVER(inode), hdr->timestamp);
@@ -5123,12 +5299,14 @@ static void nfs4_init_boot_verifier(const struct nfs_client *clp,
        if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
                /* An impossible timestamp guarantees this value
                 * will never match a generated boot time. */
-               verf[0] = 0;
-               verf[1] = cpu_to_be32(NSEC_PER_SEC + 1);
+               verf[0] = cpu_to_be32(U32_MAX);
+               verf[1] = cpu_to_be32(U32_MAX);
        } else {
                struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
-               verf[0] = cpu_to_be32(nn->boot_time.tv_sec);
-               verf[1] = cpu_to_be32(nn->boot_time.tv_nsec);
+               u64 ns = ktime_to_ns(nn->boot_time);
+
+               verf[0] = cpu_to_be32(ns >> 32);
+               verf[1] = cpu_to_be32(ns);
        }
        memcpy(bootverf->data, verf, sizeof(bootverf->data));
 }
@@ -5393,10 +5571,13 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata)
                renew_lease(data->res.server, data->timestamp);
        case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_EXPIRED:
+               nfs4_free_revoked_stateid(data->res.server,
+                               data->args.stateid,
+                               task->tk_msg.rpc_cred);
        case -NFS4ERR_BAD_STATEID:
        case -NFS4ERR_OLD_STATEID:
        case -NFS4ERR_STALE_STATEID:
-       case -NFS4ERR_EXPIRED:
                task->tk_status = 0;
                if (data->roc)
                        pnfs_roc_set_barrier(data->inode, data->roc_barrier);
@@ -5528,22 +5709,6 @@ int nfs4_proc_delegreturn(struct inode *inode, struct rpc_cred *cred, const nfs4
        return err;
 }
 
-#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
-#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
-
-/* 
- * sleep, with exponential backoff, and retry the LOCK operation. 
- */
-static unsigned long
-nfs4_set_lock_task_retry(unsigned long timeout)
-{
-       freezable_schedule_timeout_killable_unsafe(timeout);
-       timeout <<= 1;
-       if (timeout > NFS4_LOCK_MAXTIMEOUT)
-               return NFS4_LOCK_MAXTIMEOUT;
-       return timeout;
-}
-
 static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
        struct inode *inode = state->inode;
@@ -5600,11 +5765,6 @@ static int nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock *
        return err;
 }
 
-static int do_vfs_lock(struct inode *inode, struct file_lock *fl)
-{
-       return locks_lock_inode_wait(inode, fl);
-}
-
 struct nfs4_unlockdata {
        struct nfs_locku_args arg;
        struct nfs_locku_res res;
@@ -5657,14 +5817,18 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
        switch (task->tk_status) {
                case 0:
                        renew_lease(calldata->server, calldata->timestamp);
-                       do_vfs_lock(calldata->lsp->ls_state->inode, &calldata->fl);
+                       locks_lock_inode_wait(calldata->lsp->ls_state->inode, &calldata->fl);
                        if (nfs4_update_lock_stateid(calldata->lsp,
                                        &calldata->res.stateid))
                                break;
+               case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_EXPIRED:
+                       nfs4_free_revoked_stateid(calldata->server,
+                                       &calldata->arg.stateid,
+                                       task->tk_msg.rpc_cred);
                case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_STALE_STATEID:
-               case -NFS4ERR_EXPIRED:
                        if (!nfs4_stateid_match(&calldata->arg.stateid,
                                                &calldata->lsp->ls_stateid))
                                rpc_restart_call_prepare(task);
@@ -5765,7 +5929,7 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
        mutex_lock(&sp->so_delegreturn_mutex);
        /* Exclude nfs4_reclaim_open_stateid() - note nesting! */
        down_read(&nfsi->rwsem);
-       if (do_vfs_lock(inode, request) == -ENOENT) {
+       if (locks_lock_inode_wait(inode, request) == -ENOENT) {
                up_read(&nfsi->rwsem);
                mutex_unlock(&sp->so_delegreturn_mutex);
                goto out;
@@ -5906,7 +6070,7 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata)
                                data->timestamp);
                if (data->arg.new_lock) {
                        data->fl.fl_flags &= ~(FL_SLEEP | FL_ACCESS);
-                       if (do_vfs_lock(lsp->ls_state->inode, &data->fl) < 0) {
+                       if (locks_lock_inode_wait(lsp->ls_state->inode, &data->fl) < 0) {
                                rpc_restart_call_prepare(task);
                                break;
                        }
@@ -5965,6 +6129,7 @@ static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_
 {
        switch (error) {
        case -NFS4ERR_ADMIN_REVOKED:
+       case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
                lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
                if (new_lock_owner != 0 ||
@@ -5973,7 +6138,6 @@ static void nfs4_handle_setlk_error(struct nfs_server *server, struct nfs4_lock_
                break;
        case -NFS4ERR_STALE_STATEID:
                lsp->ls_seqid.flags &= ~NFS_SEQID_CONFIRMED;
-       case -NFS4ERR_EXPIRED:
                nfs4_schedule_lease_recovery(server->nfs_client);
        };
 }
@@ -6083,52 +6247,19 @@ out:
 }
 
 #if defined(CONFIG_NFS_V4_1)
-/**
- * nfs41_check_expired_locks - possibly free a lock stateid
- *
- * @state: NFSv4 state for an inode
- *
- * Returns NFS_OK if recovery for this stateid is now finished.
- * Otherwise a negative NFS4ERR value is returned.
- */
-static int nfs41_check_expired_locks(struct nfs4_state *state)
-{
-       int status, ret = -NFS4ERR_BAD_STATEID;
-       struct nfs4_lock_state *lsp;
-       struct nfs_server *server = NFS_SERVER(state->inode);
-
-       list_for_each_entry(lsp, &state->lock_states, ls_locks) {
-               if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) {
-                       struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
-
-                       status = nfs41_test_stateid(server,
-                                       &lsp->ls_stateid,
-                                       cred);
-                       trace_nfs4_test_lock_stateid(state, lsp, status);
-                       if (status != NFS_OK) {
-                               /* Free the stateid unless the server
-                                * informs us the stateid is unrecognized. */
-                               if (status != -NFS4ERR_BAD_STATEID)
-                                       nfs41_free_stateid(server,
-                                                       &lsp->ls_stateid,
-                                                       cred);
-                               clear_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags);
-                               ret = status;
-                       }
-               }
-       };
-
-       return ret;
-}
-
 static int nfs41_lock_expired(struct nfs4_state *state, struct file_lock *request)
 {
-       int status = NFS_OK;
+       struct nfs4_lock_state *lsp;
+       int status;
 
-       if (test_bit(LK_STATE_IN_USE, &state->flags))
-               status = nfs41_check_expired_locks(state);
-       if (status != NFS_OK)
-               status = nfs4_lock_expired(state, request);
+       status = nfs4_set_lock_state(state, request);
+       if (status != 0)
+               return status;
+       lsp = request->fl_u.nfs4_fl.owner;
+       if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) ||
+           test_bit(NFS_LOCK_LOST, &lsp->ls_flags))
+               return 0;
+       status = nfs4_lock_expired(state, request);
        return status;
 }
 #endif
@@ -6138,17 +6269,10 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs4_state_owner *sp = state->owner;
        unsigned char fl_flags = request->fl_flags;
-       int status = -ENOLCK;
+       int status;
 
-       if ((fl_flags & FL_POSIX) &&
-                       !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
-               goto out;
-       /* Is this a delegated open? */
-       status = nfs4_set_lock_state(state, request);
-       if (status != 0)
-               goto out;
        request->fl_flags |= FL_ACCESS;
-       status = do_vfs_lock(state->inode, request);
+       status = locks_lock_inode_wait(state->inode, request);
        if (status < 0)
                goto out;
        mutex_lock(&sp->so_delegreturn_mutex);
@@ -6157,7 +6281,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
                /* Yes: cache locks! */
                /* ...but avoid races with delegation recall... */
                request->fl_flags = fl_flags & ~FL_SLEEP;
-               status = do_vfs_lock(state->inode, request);
+               status = locks_lock_inode_wait(state->inode, request);
                up_read(&nfsi->rwsem);
                mutex_unlock(&sp->so_delegreturn_mutex);
                goto out;
@@ -6188,12 +6312,124 @@ static int nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *
        return err;
 }
 
+#define NFS4_LOCK_MINTIMEOUT (1 * HZ)
+#define NFS4_LOCK_MAXTIMEOUT (30 * HZ)
+
+static int
+nfs4_retry_setlk_simple(struct nfs4_state *state, int cmd,
+                       struct file_lock *request)
+{
+       int             status = -ERESTARTSYS;
+       unsigned long   timeout = NFS4_LOCK_MINTIMEOUT;
+
+       while(!signalled()) {
+               status = nfs4_proc_setlk(state, cmd, request);
+               if ((status != -EAGAIN) || IS_SETLK(cmd))
+                       break;
+               freezable_schedule_timeout_interruptible(timeout);
+               timeout *= 2;
+               timeout = min_t(unsigned long, NFS4_LOCK_MAXTIMEOUT, timeout);
+               status = -ERESTARTSYS;
+       }
+       return status;
+}
+
+#ifdef CONFIG_NFS_V4_1
+struct nfs4_lock_waiter {
+       struct task_struct      *task;
+       struct inode            *inode;
+       struct nfs_lowner       *owner;
+       bool                    notified;
+};
+
+static int
+nfs4_wake_lock_waiter(wait_queue_t *wait, unsigned int mode, int flags, void *key)
+{
+       int ret;
+       struct cb_notify_lock_args *cbnl = key;
+       struct nfs4_lock_waiter *waiter = wait->private;
+       struct nfs_lowner       *lowner = &cbnl->cbnl_owner,
+                               *wowner = waiter->owner;
+
+       /* Only wake if the callback was for the same owner */
+       if (lowner->clientid != wowner->clientid ||
+           lowner->id != wowner->id             ||
+           lowner->s_dev != wowner->s_dev)
+               return 0;
+
+       /* Make sure it's for the right inode */
+       if (nfs_compare_fh(NFS_FH(waiter->inode), &cbnl->cbnl_fh))
+               return 0;
+
+       waiter->notified = true;
+
+       /* override "private" so we can use default_wake_function */
+       wait->private = waiter->task;
+       ret = autoremove_wake_function(wait, mode, flags, key);
+       wait->private = waiter;
+       return ret;
+}
+
+static int
+nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
+{
+       int status = -ERESTARTSYS;
+       unsigned long flags;
+       struct nfs4_lock_state *lsp = request->fl_u.nfs4_fl.owner;
+       struct nfs_server *server = NFS_SERVER(state->inode);
+       struct nfs_client *clp = server->nfs_client;
+       wait_queue_head_t *q = &clp->cl_lock_waitq;
+       struct nfs_lowner owner = { .clientid = clp->cl_clientid,
+                                   .id = lsp->ls_seqid.owner_id,
+                                   .s_dev = server->s_dev };
+       struct nfs4_lock_waiter waiter = { .task  = current,
+                                          .inode = state->inode,
+                                          .owner = &owner,
+                                          .notified = false };
+       wait_queue_t wait;
+
+       /* Don't bother with waitqueue if we don't expect a callback */
+       if (!test_bit(NFS_STATE_MAY_NOTIFY_LOCK, &state->flags))
+               return nfs4_retry_setlk_simple(state, cmd, request);
+
+       init_wait(&wait);
+       wait.private = &waiter;
+       wait.func = nfs4_wake_lock_waiter;
+       add_wait_queue(q, &wait);
+
+       while(!signalled()) {
+               status = nfs4_proc_setlk(state, cmd, request);
+               if ((status != -EAGAIN) || IS_SETLK(cmd))
+                       break;
+
+               status = -ERESTARTSYS;
+               spin_lock_irqsave(&q->lock, flags);
+               if (waiter.notified) {
+                       spin_unlock_irqrestore(&q->lock, flags);
+                       continue;
+               }
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_unlock_irqrestore(&q->lock, flags);
+
+               freezable_schedule_timeout_interruptible(NFS4_LOCK_MAXTIMEOUT);
+       }
+
+       finish_wait(q, &wait);
+       return status;
+}
+#else /* !CONFIG_NFS_V4_1 */
+static inline int
+nfs4_retry_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
+{
+       return nfs4_retry_setlk_simple(state, cmd, request);
+}
+#endif
+
 static int
 nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 {
        struct nfs_open_context *ctx;
        struct nfs4_state *state;
-       unsigned long timeout = NFS4_LOCK_MINTIMEOUT;
        int status;
 
        /* verify open state */
@@ -6220,6 +6456,11 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
 
        if (state == NULL)
                return -ENOLCK;
+
+       if ((request->fl_flags & FL_POSIX) &&
+           !test_bit(NFS_STATE_POSIX_LOCKS, &state->flags))
+               return -ENOLCK;
+
        /*
         * Don't rely on the VFS having checked the file open mode,
         * since it won't do this for flock() locks.
@@ -6234,16 +6475,11 @@ nfs4_proc_lock(struct file *filp, int cmd, struct file_lock *request)
                        return -EBADF;
        }
 
-       do {
-               status = nfs4_proc_setlk(state, cmd, request);
-               if ((status != -EAGAIN) || IS_SETLK(cmd))
-                       break;
-               timeout = nfs4_set_lock_task_retry(timeout);
-               status = -ERESTARTSYS;
-               if (signalled())
-                       break;
-       } while(status < 0);
-       return status;
+       status = nfs4_set_lock_state(state, request);
+       if (status != 0)
+               return status;
+
+       return nfs4_retry_setlk(state, cmd, request);
 }
 
 int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state, const nfs4_stateid *stateid)
@@ -7104,75 +7340,161 @@ static int nfs4_sp4_select_mode(struct nfs_client *clp,
        return 0;
 }
 
+struct nfs41_exchange_id_data {
+       struct nfs41_exchange_id_res res;
+       struct nfs41_exchange_id_args args;
+       struct rpc_xprt *xprt;
+       int rpc_status;
+};
+
+static void nfs4_exchange_id_done(struct rpc_task *task, void *data)
+{
+       struct nfs41_exchange_id_data *cdata =
+                                       (struct nfs41_exchange_id_data *)data;
+       struct nfs_client *clp = cdata->args.client;
+       int status = task->tk_status;
+
+       trace_nfs4_exchange_id(clp, status);
+
+       if (status == 0)
+               status = nfs4_check_cl_exchange_flags(cdata->res.flags);
+
+       if (cdata->xprt && status == 0) {
+               status = nfs4_detect_session_trunking(clp, &cdata->res,
+                                                     cdata->xprt);
+               goto out;
+       }
+
+       if (status  == 0)
+               status = nfs4_sp4_select_mode(clp, &cdata->res.state_protect);
+
+       if (status == 0) {
+               clp->cl_clientid = cdata->res.clientid;
+               clp->cl_exchange_flags = cdata->res.flags;
+               /* Client ID is not confirmed */
+               if (!(cdata->res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
+                       clear_bit(NFS4_SESSION_ESTABLISHED,
+                       &clp->cl_session->session_state);
+                       clp->cl_seqid = cdata->res.seqid;
+               }
+
+               kfree(clp->cl_serverowner);
+               clp->cl_serverowner = cdata->res.server_owner;
+               cdata->res.server_owner = NULL;
+
+               /* use the most recent implementation id */
+               kfree(clp->cl_implid);
+               clp->cl_implid = cdata->res.impl_id;
+               cdata->res.impl_id = NULL;
+
+               if (clp->cl_serverscope != NULL &&
+                   !nfs41_same_server_scope(clp->cl_serverscope,
+                                       cdata->res.server_scope)) {
+                       dprintk("%s: server_scope mismatch detected\n",
+                               __func__);
+                       set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
+                       kfree(clp->cl_serverscope);
+                       clp->cl_serverscope = NULL;
+               }
+
+               if (clp->cl_serverscope == NULL) {
+                       clp->cl_serverscope = cdata->res.server_scope;
+                       cdata->res.server_scope = NULL;
+               }
+               /* Save the EXCHANGE_ID verifier session trunk tests */
+               memcpy(clp->cl_confirm.data, cdata->args.verifier->data,
+                      sizeof(clp->cl_confirm.data));
+       }
+out:
+       cdata->rpc_status = status;
+       return;
+}
+
+static void nfs4_exchange_id_release(void *data)
+{
+       struct nfs41_exchange_id_data *cdata =
+                                       (struct nfs41_exchange_id_data *)data;
+
+       nfs_put_client(cdata->args.client);
+       if (cdata->xprt) {
+               xprt_put(cdata->xprt);
+               rpc_clnt_xprt_switch_put(cdata->args.client->cl_rpcclient);
+       }
+       kfree(cdata->res.impl_id);
+       kfree(cdata->res.server_scope);
+       kfree(cdata->res.server_owner);
+       kfree(cdata);
+}
+
+static const struct rpc_call_ops nfs4_exchange_id_call_ops = {
+       .rpc_call_done = nfs4_exchange_id_done,
+       .rpc_release = nfs4_exchange_id_release,
+};
+
 /*
  * _nfs4_proc_exchange_id()
  *
  * Wrapper for EXCHANGE_ID operation.
  */
 static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
-       u32 sp4_how)
+                       u32 sp4_how, struct rpc_xprt *xprt)
 {
        nfs4_verifier verifier;
-       struct nfs41_exchange_id_args args = {
-               .verifier = &verifier,
-               .client = clp,
-#ifdef CONFIG_NFS_V4_1_MIGRATION
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
-                        EXCHGID4_FLAG_BIND_PRINC_STATEID |
-                        EXCHGID4_FLAG_SUPP_MOVED_MIGR,
-#else
-               .flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
-                        EXCHGID4_FLAG_BIND_PRINC_STATEID,
-#endif
-       };
-       struct nfs41_exchange_id_res res = {
-               0
-       };
-       int status;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
-               .rpc_argp = &args,
-               .rpc_resp = &res,
                .rpc_cred = cred,
        };
+       struct rpc_task_setup task_setup_data = {
+               .rpc_client = clp->cl_rpcclient,
+               .callback_ops = &nfs4_exchange_id_call_ops,
+               .rpc_message = &msg,
+               .flags = RPC_TASK_ASYNC | RPC_TASK_TIMEOUT,
+       };
+       struct nfs41_exchange_id_data *calldata;
+       struct rpc_task *task;
+       int status = -EIO;
+
+       if (!atomic_inc_not_zero(&clp->cl_count))
+               goto out;
+
+       status = -ENOMEM;
+       calldata = kzalloc(sizeof(*calldata), GFP_NOFS);
+       if (!calldata)
+               goto out;
 
-       nfs4_init_boot_verifier(clp, &verifier);
+       if (!xprt)
+               nfs4_init_boot_verifier(clp, &verifier);
 
        status = nfs4_init_uniform_client_string(clp);
        if (status)
-               goto out;
+               goto out_calldata;
 
        dprintk("NFS call  exchange_id auth=%s, '%s'\n",
                clp->cl_rpcclient->cl_auth->au_ops->au_name,
                clp->cl_owner_id);
 
-       res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
-                                       GFP_NOFS);
-       if (unlikely(res.server_owner == NULL)) {
-               status = -ENOMEM;
-               goto out;
-       }
+       calldata->res.server_owner = kzalloc(sizeof(struct nfs41_server_owner),
+                                               GFP_NOFS);
+       status = -ENOMEM;
+       if (unlikely(calldata->res.server_owner == NULL))
+               goto out_calldata;
 
-       res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
+       calldata->res.server_scope = kzalloc(sizeof(struct nfs41_server_scope),
                                        GFP_NOFS);
-       if (unlikely(res.server_scope == NULL)) {
-               status = -ENOMEM;
+       if (unlikely(calldata->res.server_scope == NULL))
                goto out_server_owner;
-       }
 
-       res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
-       if (unlikely(res.impl_id == NULL)) {
-               status = -ENOMEM;
+       calldata->res.impl_id = kzalloc(sizeof(struct nfs41_impl_id), GFP_NOFS);
+       if (unlikely(calldata->res.impl_id == NULL))
                goto out_server_scope;
-       }
 
        switch (sp4_how) {
        case SP4_NONE:
-               args.state_protect.how = SP4_NONE;
+               calldata->args.state_protect.how = SP4_NONE;
                break;
 
        case SP4_MACH_CRED:
-               args.state_protect = nfs4_sp4_mach_cred_request;
+               calldata->args.state_protect = nfs4_sp4_mach_cred_request;
                break;
 
        default:
@@ -7181,56 +7503,42 @@ static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
                status = -EINVAL;
                goto out_impl_id;
        }
+       if (xprt) {
+               calldata->xprt = xprt;
+               task_setup_data.rpc_xprt = xprt;
+               task_setup_data.flags =
+                               RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC;
+               calldata->args.verifier = &clp->cl_confirm;
+       } else {
+               calldata->args.verifier = &verifier;
+       }
+       calldata->args.client = clp;
+#ifdef CONFIG_NFS_V4_1_MIGRATION
+       calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+       EXCHGID4_FLAG_BIND_PRINC_STATEID |
+       EXCHGID4_FLAG_SUPP_MOVED_MIGR,
+#else
+       calldata->args.flags = EXCHGID4_FLAG_SUPP_MOVED_REFER |
+       EXCHGID4_FLAG_BIND_PRINC_STATEID,
+#endif
+       msg.rpc_argp = &calldata->args;
+       msg.rpc_resp = &calldata->res;
+       task_setup_data.callback_data = calldata;
 
-       status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
-       trace_nfs4_exchange_id(clp, status);
-       if (status == 0)
-               status = nfs4_check_cl_exchange_flags(res.flags);
-
-       if (status == 0)
-               status = nfs4_sp4_select_mode(clp, &res.state_protect);
-
-       if (status == 0) {
-               clp->cl_clientid = res.clientid;
-               clp->cl_exchange_flags = res.flags;
-               /* Client ID is not confirmed */
-               if (!(res.flags & EXCHGID4_FLAG_CONFIRMED_R)) {
-                       clear_bit(NFS4_SESSION_ESTABLISHED,
-                                       &clp->cl_session->session_state);
-                       clp->cl_seqid = res.seqid;
-               }
-
-               kfree(clp->cl_serverowner);
-               clp->cl_serverowner = res.server_owner;
-               res.server_owner = NULL;
-
-               /* use the most recent implementation id */
-               kfree(clp->cl_implid);
-               clp->cl_implid = res.impl_id;
-               res.impl_id = NULL;
-
-               if (clp->cl_serverscope != NULL &&
-                   !nfs41_same_server_scope(clp->cl_serverscope,
-                                            res.server_scope)) {
-                       dprintk("%s: server_scope mismatch detected\n",
-                               __func__);
-                       set_bit(NFS4CLNT_SERVER_SCOPE_MISMATCH, &clp->cl_state);
-                       kfree(clp->cl_serverscope);
-                       clp->cl_serverscope = NULL;
-               }
-
-               if (clp->cl_serverscope == NULL) {
-                       clp->cl_serverscope = res.server_scope;
-                       res.server_scope = NULL;
-               }
+       task = rpc_run_task(&task_setup_data);
+       if (IS_ERR(task)) {
+       status = PTR_ERR(task);
+               goto out_impl_id;
        }
 
-out_impl_id:
-       kfree(res.impl_id);
-out_server_scope:
-       kfree(res.server_scope);
-out_server_owner:
-       kfree(res.server_owner);
+       if (!xprt) {
+               status = rpc_wait_for_completion_task(task);
+               if (!status)
+                       status = calldata->rpc_status;
+       } else  /* session trunking test */
+               status = calldata->rpc_status;
+
+       rpc_put_task(task);
 out:
        if (clp->cl_implid != NULL)
                dprintk("NFS reply exchange_id: Server Implementation ID: "
@@ -7240,6 +7548,16 @@ out:
                        clp->cl_implid->date.nseconds);
        dprintk("NFS reply exchange_id: %d\n", status);
        return status;
+
+out_impl_id:
+       kfree(calldata->res.impl_id);
+out_server_scope:
+       kfree(calldata->res.server_scope);
+out_server_owner:
+       kfree(calldata->res.server_owner);
+out_calldata:
+       kfree(calldata);
+       goto out;
 }
 
 /*
@@ -7262,14 +7580,45 @@ int nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred)
        /* try SP4_MACH_CRED if krb5i/p */
        if (authflavor == RPC_AUTH_GSS_KRB5I ||
            authflavor == RPC_AUTH_GSS_KRB5P) {
-               status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED);
+               status = _nfs4_proc_exchange_id(clp, cred, SP4_MACH_CRED, NULL);
                if (!status)
                        return 0;
        }
 
        /* try SP4_NONE */
-       return _nfs4_proc_exchange_id(clp, cred, SP4_NONE);
+       return _nfs4_proc_exchange_id(clp, cred, SP4_NONE, NULL);
+}
+
+/**
+ * nfs4_test_session_trunk
+ *
+ * This is an add_xprt_test() test function called from
+ * rpc_clnt_setup_test_and_add_xprt.
+ *
+ * The rpc_xprt_switch is referrenced by rpc_clnt_setup_test_and_add_xprt
+ * and is dereferrenced in nfs4_exchange_id_release
+ *
+ * Upon success, add the new transport to the rpc_clnt
+ *
+ * @clnt: struct rpc_clnt to get new transport
+ * @xprt: the rpc_xprt to test
+ * @data: call data for _nfs4_proc_exchange_id.
+ */
+int nfs4_test_session_trunk(struct rpc_clnt *clnt, struct rpc_xprt *xprt,
+                           void *data)
+{
+       struct nfs4_add_xprt_data *adata = (struct nfs4_add_xprt_data *)data;
+       u32 sp4_how;
+
+       dprintk("--> %s try %s\n", __func__,
+               xprt->address_strings[RPC_DISPLAY_ADDR]);
+
+       sp4_how = (adata->clp->cl_sp4_flags == 0 ? SP4_NONE : SP4_MACH_CRED);
+
+       /* Test connection for session trunking. Async exchange_id call */
+       return  _nfs4_proc_exchange_id(adata->clp, adata->cred, sp4_how, xprt);
 }
+EXPORT_SYMBOL_GPL(nfs4_test_session_trunk);
 
 static int _nfs4_proc_destroy_clientid(struct nfs_client *clp,
                struct rpc_cred *cred)
@@ -7463,7 +7812,7 @@ static void nfs4_init_channel_attrs(struct nfs41_create_session_args *args,
        args->bc_attrs.max_resp_sz = max_bc_payload;
        args->bc_attrs.max_resp_sz_cached = 0;
        args->bc_attrs.max_ops = NFS4_MAX_BACK_CHANNEL_OPS;
-       args->bc_attrs.max_reqs = NFS41_BC_MAX_CALLBACKS;
+       args->bc_attrs.max_reqs = min_t(unsigned short, max_session_cb_slots, 1);
 
        dprintk("%s: Back Channel : max_rqst_sz=%u max_resp_sz=%u "
                "max_resp_sz_cached=%u max_ops=%u max_reqs=%u\n",
@@ -7510,10 +7859,9 @@ static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args
                return -EINVAL;
        if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
                return -EINVAL;
-       /* These would render the backchannel useless: */
-       if (rcvd->max_ops != sent->max_ops)
+       if (rcvd->max_ops > sent->max_ops)
                return -EINVAL;
-       if (rcvd->max_reqs != sent->max_reqs)
+       if (rcvd->max_reqs > sent->max_reqs)
                return -EINVAL;
 out:
        return 0;
@@ -7982,6 +8330,8 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
        case -NFS4ERR_RECALLCONFLICT:
                status = -ERECALLCONFLICT;
                break;
+       case -NFS4ERR_DELEG_REVOKED:
+       case -NFS4ERR_ADMIN_REVOKED:
        case -NFS4ERR_EXPIRED:
        case -NFS4ERR_BAD_STATEID:
                exception->timeout = 0;
@@ -7993,6 +8343,7 @@ nfs4_layoutget_handle_exception(struct rpc_task *task,
                                        &lgp->args.ctx->state->stateid)) {
                        spin_unlock(&inode->i_lock);
                        exception->state = lgp->args.ctx->state;
+                       exception->stateid = &lgp->args.stateid;
                        break;
                }
 
@@ -8591,6 +8942,24 @@ static int _nfs41_test_stateid(struct nfs_server *server,
        return -res.status;
 }
 
+static void nfs4_handle_delay_or_session_error(struct nfs_server *server,
+               int err, struct nfs4_exception *exception)
+{
+       exception->retry = 0;
+       switch(err) {
+       case -NFS4ERR_DELAY:
+       case -NFS4ERR_RETRY_UNCACHED_REP:
+               nfs4_handle_exception(server, err, exception);
+               break;
+       case -NFS4ERR_BADSESSION:
+       case -NFS4ERR_BADSLOT:
+       case -NFS4ERR_BAD_HIGH_SLOT:
+       case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
+       case -NFS4ERR_DEADSESSION:
+               nfs4_do_handle_exception(server, err, exception);
+       }
+}
+
 /**
  * nfs41_test_stateid - perform a TEST_STATEID operation
  *
@@ -8610,9 +8979,7 @@ static int nfs41_test_stateid(struct nfs_server *server,
        int err;
        do {
                err = _nfs41_test_stateid(server, stateid, cred);
-               if (err != -NFS4ERR_DELAY)
-                       break;
-               nfs4_handle_exception(server, err, &exception);
+               nfs4_handle_delay_or_session_error(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -8657,7 +9024,7 @@ static const struct rpc_call_ops nfs41_free_stateid_ops = {
 };
 
 static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
-               nfs4_stateid *stateid,
+               const nfs4_stateid *stateid,
                struct rpc_cred *cred,
                bool privileged)
 {
@@ -8687,7 +9054,7 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
 
        msg.rpc_argp = &data->args;
        msg.rpc_resp = &data->res;
-       nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 0);
+       nfs4_init_sequence(&data->args.seq_args, &data->res.seq_res, 1);
        if (privileged)
                nfs4_set_sequence_privileged(&data->args.seq_args);
 
@@ -8700,38 +9067,31 @@ static struct rpc_task *_nfs41_free_stateid(struct nfs_server *server,
  * @server: server / transport on which to perform the operation
  * @stateid: state ID to release
  * @cred: credential
+ * @is_recovery: set to true if this call needs to be privileged
  *
- * Returns NFS_OK if the server freed "stateid".  Otherwise a
- * negative NFS4ERR value is returned.
+ * Note: this function is always asynchronous.
  */
 static int nfs41_free_stateid(struct nfs_server *server,
-               nfs4_stateid *stateid,
-               struct rpc_cred *cred)
+               const nfs4_stateid *stateid,
+               struct rpc_cred *cred,
+               bool is_recovery)
 {
        struct rpc_task *task;
-       int ret;
 
-       task = _nfs41_free_stateid(server, stateid, cred, true);
+       task = _nfs41_free_stateid(server, stateid, cred, is_recovery);
        if (IS_ERR(task))
                return PTR_ERR(task);
-       ret = rpc_wait_for_completion_task(task);
-       if (!ret)
-               ret = task->tk_status;
        rpc_put_task(task);
-       return ret;
+       return 0;
 }
 
 static void
 nfs41_free_lock_state(struct nfs_server *server, struct nfs4_lock_state *lsp)
 {
-       struct rpc_task *task;
        struct rpc_cred *cred = lsp->ls_state->owner->so_cred;
 
-       task = _nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
+       nfs41_free_stateid(server, &lsp->ls_stateid, cred, false);
        nfs4_free_lock_state(server, lsp);
-       if (IS_ERR(task))
-               return;
-       rpc_put_task(task);
 }
 
 static bool nfs41_match_stateid(const nfs4_stateid *s1,
@@ -8835,6 +9195,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = {
        .match_stateid = nfs4_match_stateid,
        .find_root_sec = nfs4_find_root_sec,
        .free_lock_state = nfs4_release_lockowner,
+       .test_and_free_expired = nfs40_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_seqid,
        .call_sync_ops = &nfs40_call_sync_ops,
        .reboot_recovery_ops = &nfs40_reboot_recovery_ops,
@@ -8862,7 +9223,9 @@ static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = {
        .match_stateid = nfs41_match_stateid,
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
+       .test_and_free_expired = nfs41_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_no_seqid,
+       .session_trunk = nfs4_test_session_trunk,
        .call_sync_ops = &nfs41_call_sync_ops,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
@@ -8891,7 +9254,9 @@ static const struct nfs4_minor_version_ops nfs_v4_2_minor_ops = {
        .find_root_sec = nfs41_find_root_sec,
        .free_lock_state = nfs41_free_lock_state,
        .call_sync_ops = &nfs41_call_sync_ops,
+       .test_and_free_expired = nfs41_test_and_free_expired_stateid,
        .alloc_seqid = nfs_alloc_no_seqid,
+       .session_trunk = nfs4_test_session_trunk,
        .reboot_recovery_ops = &nfs41_reboot_recovery_ops,
        .nograce_recovery_ops = &nfs41_nograce_recovery_ops,
        .state_renewal_ops = &nfs41_state_renewal_ops,
index f703b75..dae3855 100644 (file)
@@ -9,6 +9,7 @@
 
 /* maximum number of slots to use */
 #define NFS4_DEF_SLOT_TABLE_SIZE (64U)
+#define NFS4_DEF_CB_SLOT_TABLE_SIZE (1U)
 #define NFS4_MAX_SLOT_TABLE (1024U)
 #define NFS4_NO_SLOT ((u32)-1)
 
@@ -22,6 +23,7 @@ struct nfs4_slot {
        u32                     slot_nr;
        u32                     seq_nr;
        unsigned int            interrupted : 1,
+                               privileged : 1,
                                seq_done : 1;
 };
 
index cada00a..5f4281e 100644 (file)
@@ -991,6 +991,8 @@ int nfs4_select_rw_stateid(struct nfs4_state *state,
 {
        int ret;
 
+       if (!nfs4_valid_open_stateid(state))
+               return -EIO;
        if (cred != NULL)
                *cred = NULL;
        ret = nfs4_copy_lock_stateid(dst, state, lockowner);
@@ -1303,6 +1305,8 @@ void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
 static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
 {
 
+       if (!nfs4_valid_open_stateid(state))
+               return 0;
        set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
        /* Don't recover state that expired before the reboot */
        if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
@@ -1316,6 +1320,8 @@ static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_st
 
 int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
 {
+       if (!nfs4_valid_open_stateid(state))
+               return 0;
        set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
        clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
        set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
@@ -1327,9 +1333,8 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
 {
        struct nfs_client *clp = server->nfs_client;
 
-       if (!nfs4_valid_open_stateid(state))
+       if (!nfs4_state_mark_reclaim_nograce(clp, state))
                return -EBADF;
-       nfs4_state_mark_reclaim_nograce(clp, state);
        dprintk("%s: scheduling stateid recovery for server %s\n", __func__,
                        clp->cl_hostname);
        nfs4_schedule_state_manager(clp);
@@ -1337,6 +1342,35 @@ int nfs4_schedule_stateid_recovery(const struct nfs_server *server, struct nfs4_
 }
 EXPORT_SYMBOL_GPL(nfs4_schedule_stateid_recovery);
 
+static struct nfs4_lock_state *
+nfs_state_find_lock_state_by_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
+{
+       struct nfs4_lock_state *pos;
+
+       list_for_each_entry(pos, &state->lock_states, ls_locks) {
+               if (!test_bit(NFS_LOCK_INITIALIZED, &pos->ls_flags))
+                       continue;
+               if (nfs4_stateid_match_other(&pos->ls_stateid, stateid))
+                       return pos;
+       }
+       return NULL;
+}
+
+static bool nfs_state_lock_state_matches_stateid(struct nfs4_state *state,
+               const nfs4_stateid *stateid)
+{
+       bool found = false;
+
+       if (test_bit(LK_STATE_IN_USE, &state->flags)) {
+               spin_lock(&state->state_lock);
+               if (nfs_state_find_lock_state_by_stateid(state, stateid))
+                       found = true;
+               spin_unlock(&state->state_lock);
+       }
+       return found;
+}
+
 void nfs_inode_find_state_and_recover(struct inode *inode,
                const nfs4_stateid *stateid)
 {
@@ -1351,14 +1385,18 @@ void nfs_inode_find_state_and_recover(struct inode *inode,
                state = ctx->state;
                if (state == NULL)
                        continue;
-               if (!test_bit(NFS_DELEGATED_STATE, &state->flags))
+               if (nfs4_stateid_match_other(&state->stateid, stateid) &&
+                   nfs4_state_mark_reclaim_nograce(clp, state)) {
+                       found = true;
                        continue;
-               if (!nfs4_stateid_match(&state->stateid, stateid))
-                       continue;
-               nfs4_state_mark_reclaim_nograce(clp, state);
-               found = true;
+               }
+               if (nfs_state_lock_state_matches_stateid(state, stateid) &&
+                   nfs4_state_mark_reclaim_nograce(clp, state))
+                       found = true;
        }
        spin_unlock(&inode->i_lock);
+
+       nfs_inode_find_delegation_state_and_recover(inode, stateid);
        if (found)
                nfs4_schedule_state_manager(clp);
 }
@@ -1498,6 +1536,9 @@ restart:
                                        __func__, status);
                        case -ENOENT:
                        case -ENOMEM:
+                       case -EACCES:
+                       case -EROFS:
+                       case -EIO:
                        case -ESTALE:
                                /* Open state on this file cannot be recovered */
                                nfs4_state_mark_recovery_failed(state, status);
@@ -1656,15 +1697,9 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
        put_rpccred(cred);
 }
 
-static void nfs_delegation_clear_all(struct nfs_client *clp)
-{
-       nfs_delegation_mark_reclaim(clp);
-       nfs_delegation_reap_unclaimed(clp);
-}
-
 static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
 {
-       nfs_delegation_clear_all(clp);
+       nfs_mark_test_expired_all_delegations(clp);
        nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
 }
 
@@ -2195,7 +2230,7 @@ static void nfs41_handle_all_state_revoked(struct nfs_client *clp)
 
 static void nfs41_handle_some_state_revoked(struct nfs_client *clp)
 {
-       nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
+       nfs4_state_start_reclaim_nograce(clp);
        nfs4_schedule_state_manager(clp);
 
        dprintk("%s: state revoked on server %s\n", __func__, clp->cl_hostname);
@@ -2227,13 +2262,22 @@ static void nfs41_handle_cb_path_down(struct nfs_client *clp)
                nfs4_schedule_state_manager(clp);
 }
 
-void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
+void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags,
+               bool recovery)
 {
        if (!flags)
                return;
 
        dprintk("%s: \"%s\" (client ID %llx) flags=0x%08x\n",
                __func__, clp->cl_hostname, clp->cl_clientid, flags);
+       /*
+        * If we're called from the state manager thread, then assume we're
+        * already handling the RECLAIM_NEEDED and/or STATE_REVOKED.
+        * Those flags are expected to remain set until we're done
+        * recovering (see RFC5661, section 18.46.3).
+        */
+       if (recovery)
+               goto out_recovery;
 
        if (flags & SEQ4_STATUS_RESTART_RECLAIM_NEEDED)
                nfs41_handle_server_reboot(clp);
@@ -2246,6 +2290,7 @@ void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags)
                nfs4_schedule_lease_moved_recovery(clp);
        if (flags & SEQ4_STATUS_RECALLABLE_STATE_REVOKED)
                nfs41_handle_recallable_state_revoked(clp);
+out_recovery:
        if (flags & SEQ4_STATUS_BACKCHANNEL_FAULT)
                nfs41_handle_backchannel_fault(clp);
        else if (flags & (SEQ4_STATUS_CB_PATH_DOWN |
@@ -2410,6 +2455,13 @@ static void nfs4_state_manager(struct nfs_client *clp)
                        nfs4_state_end_reclaim_reboot(clp);
                }
 
+               /* Detect expired delegations... */
+               if (test_and_clear_bit(NFS4CLNT_DELEGATION_EXPIRED, &clp->cl_state)) {
+                       section = "detect expired delegations";
+                       nfs_reap_expired_delegations(clp);
+                       continue;
+               }
+
                /* Now recover expired state... */
                if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
                        section = "reclaim nograce";
index 7bd3a5c..fc89e5e 100644 (file)
@@ -1850,7 +1850,7 @@ static void encode_create_session(struct xdr_stream *xdr,
        *p++ = cpu_to_be32(RPC_AUTH_UNIX);                      /* auth_sys */
 
        /* authsys_parms rfc1831 */
-       *p++ = cpu_to_be32(nn->boot_time.tv_nsec);      /* stamp */
+       *p++ = cpu_to_be32(ktime_to_ns(nn->boot_time)); /* stamp */
        p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
        *p++ = cpu_to_be32(0);                          /* UID */
        *p++ = cpu_to_be32(0);                          /* GID */
@@ -4725,34 +4725,37 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
 }
 
 /*
- * Decode potentially multiple layout types. Currently we only support
- * one layout driver per file system.
+ * Decode potentially multiple layout types.
  */
-static int decode_first_pnfs_layout_type(struct xdr_stream *xdr,
-                                        uint32_t *layouttype)
+static int decode_pnfs_layout_types(struct xdr_stream *xdr,
+                                   struct nfs_fsinfo *fsinfo)
 {
        __be32 *p;
-       int num;
+       uint32_t i;
 
        p = xdr_inline_decode(xdr, 4);
        if (unlikely(!p))
                goto out_overflow;
-       num = be32_to_cpup(p);
+       fsinfo->nlayouttypes = be32_to_cpup(p);
 
        /* pNFS is not supported by the underlying file system */
-       if (num == 0) {
-               *layouttype = 0;
+       if (fsinfo->nlayouttypes == 0)
                return 0;
-       }
-       if (num > 1)
-               printk(KERN_INFO "NFS: %s: Warning: Multiple pNFS layout "
-                       "drivers per filesystem not supported\n", __func__);
 
        /* Decode and set first layout type, move xdr->p past unused types */
-       p = xdr_inline_decode(xdr, num * 4);
+       p = xdr_inline_decode(xdr, fsinfo->nlayouttypes * 4);
        if (unlikely(!p))
                goto out_overflow;
-       *layouttype = be32_to_cpup(p);
+
+       /* If we get too many, then just cap it at the max */
+       if (fsinfo->nlayouttypes > NFS_MAX_LAYOUT_TYPES) {
+               printk(KERN_INFO "NFS: %s: Warning: Too many (%u) pNFS layout types\n",
+                       __func__, fsinfo->nlayouttypes);
+               fsinfo->nlayouttypes = NFS_MAX_LAYOUT_TYPES;
+       }
+
+       for(i = 0; i < fsinfo->nlayouttypes; ++i)
+               fsinfo->layouttype[i] = be32_to_cpup(p++);
        return 0;
 out_overflow:
        print_overflow_msg(__func__, xdr);
@@ -4764,7 +4767,7 @@ out_overflow:
  * Note we must ensure that layouttype is set in any non-error case.
  */
 static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
-                               uint32_t *layouttype)
+                               struct nfs_fsinfo *fsinfo)
 {
        int status = 0;
 
@@ -4772,10 +4775,9 @@ static int decode_attr_pnfstype(struct xdr_stream *xdr, uint32_t *bitmap,
        if (unlikely(bitmap[1] & (FATTR4_WORD1_FS_LAYOUT_TYPES - 1U)))
                return -EIO;
        if (bitmap[1] & FATTR4_WORD1_FS_LAYOUT_TYPES) {
-               status = decode_first_pnfs_layout_type(xdr, layouttype);
+               status = decode_pnfs_layout_types(xdr, fsinfo);
                bitmap[1] &= ~FATTR4_WORD1_FS_LAYOUT_TYPES;
-       } else
-               *layouttype = 0;
+       }
        return status;
 }
 
@@ -4856,7 +4858,7 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
        status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta);
        if (status != 0)
                goto xdr_error;
-       status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype);
+       status = decode_attr_pnfstype(xdr, bitmap, fsinfo);
        if (status != 0)
                goto xdr_error;
 
index 2c93a85..56b2d96 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/nfs_fs.h>
 #include <linux/nfs_page.h>
 #include <linux/module.h>
+#include <linux/sort.h>
 #include "internal.h"
 #include "pnfs.h"
 #include "iostat.h"
@@ -98,36 +99,80 @@ unset_pnfs_layoutdriver(struct nfs_server *nfss)
        nfss->pnfs_curr_ld = NULL;
 }
 
+/*
+ * When the server sends a list of layout types, we choose one in the order
+ * given in the list below.
+ *
+ * FIXME: should this list be configurable in some fashion? module param?
+ *       mount option? something else?
+ */
+static const u32 ld_prefs[] = {
+       LAYOUT_SCSI,
+       LAYOUT_BLOCK_VOLUME,
+       LAYOUT_OSD2_OBJECTS,
+       LAYOUT_FLEX_FILES,
+       LAYOUT_NFSV4_1_FILES,
+       0
+};
+
+static int
+ld_cmp(const void *e1, const void *e2)
+{
+       u32 ld1 = *((u32 *)e1);
+       u32 ld2 = *((u32 *)e2);
+       int i;
+
+       for (i = 0; ld_prefs[i] != 0; i++) {
+               if (ld1 == ld_prefs[i])
+                       return -1;
+
+               if (ld2 == ld_prefs[i])
+                       return 1;
+       }
+       return 0;
+}
+
 /*
  * Try to set the server's pnfs module to the pnfs layout type specified by id.
  * Currently only one pNFS layout driver per filesystem is supported.
  *
- * @id layout type. Zero (illegal layout type) indicates pNFS not in use.
+ * @ids array of layout types supported by MDS.
  */
 void
 set_pnfs_layoutdriver(struct nfs_server *server, const struct nfs_fh *mntfh,
-                     u32 id)
+                     struct nfs_fsinfo *fsinfo)
 {
        struct pnfs_layoutdriver_type *ld_type = NULL;
+       u32 id;
+       int i;
 
-       if (id == 0)
-               goto out_no_driver;
        if (!(server->nfs_client->cl_exchange_flags &
                 (EXCHGID4_FLAG_USE_NON_PNFS | EXCHGID4_FLAG_USE_PNFS_MDS))) {
-               printk(KERN_ERR "NFS: %s: id %u cl_exchange_flags 0x%x\n",
-                       __func__, id, server->nfs_client->cl_exchange_flags);
+               printk(KERN_ERR "NFS: %s: cl_exchange_flags 0x%x\n",
+                       __func__, server->nfs_client->cl_exchange_flags);
                goto out_no_driver;
        }
-       ld_type = find_pnfs_driver(id);
-       if (!ld_type) {
-               request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX, id);
+
+       sort(fsinfo->layouttype, fsinfo->nlayouttypes,
+               sizeof(*fsinfo->layouttype), ld_cmp, NULL);
+
+       for (i = 0; i < fsinfo->nlayouttypes; i++) {
+               id = fsinfo->layouttype[i];
                ld_type = find_pnfs_driver(id);
                if (!ld_type) {
-                       dprintk("%s: No pNFS module found for %u.\n",
-                               __func__, id);
-                       goto out_no_driver;
+                       request_module("%s-%u", LAYOUT_NFSV4_1_MODULE_PREFIX,
+                                       id);
+                       ld_type = find_pnfs_driver(id);
                }
+               if (ld_type)
+                       break;
+       }
+
+       if (!ld_type) {
+               dprintk("%s: No pNFS module found!\n", __func__);
+               goto out_no_driver;
        }
+
        server->pnfs_curr_ld = ld_type;
        if (ld_type->set_layoutdriver
            && ld_type->set_layoutdriver(server, mntfh)) {
@@ -2185,10 +2230,8 @@ static void pnfs_ld_handle_read_error(struct nfs_pgio_header *hdr)
  */
 void pnfs_ld_read_done(struct nfs_pgio_header *hdr)
 {
-       if (likely(!hdr->pnfs_error)) {
-               __nfs4_read_done_cb(hdr);
+       if (likely(!hdr->pnfs_error))
                hdr->mds_ops->rpc_call_done(&hdr->task, hdr);
-       }
        trace_nfs4_pnfs_read(hdr, hdr->pnfs_error);
        if (unlikely(hdr->pnfs_error))
                pnfs_ld_handle_read_error(hdr);
index 31d99b2..5c29551 100644 (file)
@@ -236,7 +236,7 @@ void pnfs_get_layout_hdr(struct pnfs_layout_hdr *lo);
 void pnfs_put_lseg(struct pnfs_layout_segment *lseg);
 void pnfs_put_lseg_locked(struct pnfs_layout_segment *lseg);
 
-void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, u32);
+void set_pnfs_layoutdriver(struct nfs_server *, const struct nfs_fh *, struct nfs_fsinfo *);
 void unset_pnfs_layoutdriver(struct nfs_server *);
 void pnfs_generic_pg_init_read(struct nfs_pageio_descriptor *, struct nfs_page *);
 int pnfs_generic_pg_readpages(struct nfs_pageio_descriptor *desc);
@@ -657,7 +657,8 @@ pnfs_wait_on_layoutreturn(struct inode *ino, struct rpc_task *task)
 }
 
 static inline void set_pnfs_layoutdriver(struct nfs_server *s,
-                                        const struct nfs_fh *mntfh, u32 id)
+                                        const struct nfs_fh *mntfh,
+                                        struct nfs_fsinfo *fsinfo)
 {
 }
 
index f3468b5..53b4705 100644 (file)
@@ -690,13 +690,50 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
                dprintk("%s: DS %s: trying address %s\n",
                        __func__, ds->ds_remotestr, da->da_remotestr);
 
-               clp = nfs4_set_ds_client(mds_srv,
-                                       (struct sockaddr *)&da->da_addr,
-                                       da->da_addrlen, IPPROTO_TCP,
-                                       timeo, retrans, minor_version,
-                                       au_flavor);
-               if (!IS_ERR(clp))
-                       break;
+               if (!IS_ERR(clp) && clp->cl_mvops->session_trunk) {
+                       struct xprt_create xprt_args = {
+                               .ident = XPRT_TRANSPORT_TCP,
+                               .net = clp->cl_net,
+                               .dstaddr = (struct sockaddr *)&da->da_addr,
+                               .addrlen = da->da_addrlen,
+                               .servername = clp->cl_hostname,
+                       };
+                       struct nfs4_add_xprt_data xprtdata = {
+                               .clp = clp,
+                               .cred = nfs4_get_clid_cred(clp),
+                       };
+                       struct rpc_add_xprt_test rpcdata = {
+                               .add_xprt_test = clp->cl_mvops->session_trunk,
+                               .data = &xprtdata,
+                       };
+
+                       /**
+                       * Test this address for session trunking and
+                       * add as an alias
+                       */
+                       rpc_clnt_add_xprt(clp->cl_rpcclient, &xprt_args,
+                                         rpc_clnt_setup_test_and_add_xprt,
+                                         &rpcdata);
+                       if (xprtdata.cred)
+                               put_rpccred(xprtdata.cred);
+               } else {
+                       clp = nfs4_set_ds_client(mds_srv,
+                                               (struct sockaddr *)&da->da_addr,
+                                               da->da_addrlen, IPPROTO_TCP,
+                                               timeo, retrans, minor_version,
+                                               au_flavor);
+                       if (IS_ERR(clp))
+                               continue;
+
+                       status = nfs4_init_ds_session(clp,
+                                       mds_srv->nfs_client->cl_lease_time);
+                       if (status) {
+                               nfs_put_client(clp);
+                               clp = ERR_PTR(-EIO);
+                               continue;
+                       }
+
+               }
        }
 
        if (IS_ERR(clp)) {
@@ -704,18 +741,11 @@ static int _nfs4_pnfs_v4_ds_connect(struct nfs_server *mds_srv,
                goto out;
        }
 
-       status = nfs4_init_ds_session(clp, mds_srv->nfs_client->cl_lease_time);
-       if (status)
-               goto out_put;
-
        smp_wmb();
        ds->ds_clp = clp;
        dprintk("%s [new] addr: %s\n", __func__, ds->ds_remotestr);
 out:
        return status;
-out_put:
-       nfs_put_client(clp);
-       goto out;
 }
 
 /*
index d396013..001796b 100644 (file)
@@ -2848,19 +2848,23 @@ out_invalid_transport_udp:
  * NFS client for backwards compatibility
  */
 unsigned int nfs_callback_set_tcpport;
+unsigned short nfs_callback_nr_threads;
 /* Default cache timeout is 10 minutes */
 unsigned int nfs_idmap_cache_timeout = 600;
 /* Turn off NFSv4 uid/gid mapping when using AUTH_SYS */
 bool nfs4_disable_idmapping = true;
 unsigned short max_session_slots = NFS4_DEF_SLOT_TABLE_SIZE;
+unsigned short max_session_cb_slots = NFS4_DEF_CB_SLOT_TABLE_SIZE;
 unsigned short send_implementation_id = 1;
 char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN] = "";
 bool recover_lost_locks = false;
 
+EXPORT_SYMBOL_GPL(nfs_callback_nr_threads);
 EXPORT_SYMBOL_GPL(nfs_callback_set_tcpport);
 EXPORT_SYMBOL_GPL(nfs_idmap_cache_timeout);
 EXPORT_SYMBOL_GPL(nfs4_disable_idmapping);
 EXPORT_SYMBOL_GPL(max_session_slots);
+EXPORT_SYMBOL_GPL(max_session_cb_slots);
 EXPORT_SYMBOL_GPL(send_implementation_id);
 EXPORT_SYMBOL_GPL(nfs4_client_id_uniquifier);
 EXPORT_SYMBOL_GPL(recover_lost_locks);
@@ -2887,6 +2891,9 @@ static const struct kernel_param_ops param_ops_portnr = {
 #define param_check_portnr(name, p) __param_check(name, p, unsigned int);
 
 module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644);
+module_param_named(callback_nr_threads, nfs_callback_nr_threads, ushort, 0644);
+MODULE_PARM_DESC(callback_nr_threads, "Number of threads that will be "
+               "assigned to the NFSv4 callback channels.");
 module_param(nfs_idmap_cache_timeout, int, 0644);
 module_param(nfs4_disable_idmapping, bool, 0644);
 module_param_string(nfs4_unique_id, nfs4_client_id_uniquifier,
@@ -2896,6 +2903,9 @@ MODULE_PARM_DESC(nfs4_disable_idmapping,
 module_param(max_session_slots, ushort, 0644);
 MODULE_PARM_DESC(max_session_slots, "Maximum number of outstanding NFSv4.1 "
                "requests the client will negotiate");
+module_param(max_session_cb_slots, ushort, 0644);
+MODULE_PARM_DESC(max_session_slots, "Maximum number of parallel NFSv4.1 "
+               "callbacks the client will process for a given server");
 module_param(send_implementation_id, ushort, 0644);
 MODULE_PARM_DESC(send_implementation_id,
                "Send implementation ID with NFSv4.1 exchange_id");
index df880e9..b672873 100644 (file)
@@ -126,6 +126,7 @@ nfsd4_ff_proc_getdeviceinfo(struct super_block *sb, struct svc_rqst *rqstp,
 const struct nfsd4_layout_ops ff_layout_ops = {
        .notify_types           =
                        NOTIFY_DEVICEID4_DELETE | NOTIFY_DEVICEID4_CHANGE,
+       .disable_recalls        = true,
        .proc_getdeviceinfo     = nfsd4_ff_proc_getdeviceinfo,
        .encode_getdeviceinfo   = nfsd4_ff_encode_getdeviceinfo,
        .proc_layoutget         = nfsd4_ff_proc_layoutget,
index 5fbf3bb..b10d557 100644 (file)
@@ -84,6 +84,7 @@ struct nfsd_net {
        struct list_head client_lru;
        struct list_head close_lru;
        struct list_head del_recall_lru;
+       struct list_head blocked_locks_lru;
 
        struct delayed_work laundromat_work;
 
index 04c68d9..211dc2a 100644 (file)
@@ -448,7 +448,7 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr,
 {
        int status;
 
-       if (cb->cb_minorversion == 0)
+       if (cb->cb_clp->cl_minorversion == 0)
                return 0;
 
        status = decode_cb_op_status(xdr, OP_CB_SEQUENCE, &cb->cb_seq_status);
@@ -485,7 +485,7 @@ static void nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, struct xdr_stream *xdr,
        const struct nfs4_delegation *dp = cb_to_delegation(cb);
        struct nfs4_cb_compound_hdr hdr = {
                .ident = cb->cb_clp->cl_cb_ident,
-               .minorversion = cb->cb_minorversion,
+               .minorversion = cb->cb_clp->cl_minorversion,
        };
 
        encode_cb_compound4args(xdr, &hdr);
@@ -594,7 +594,7 @@ static void nfs4_xdr_enc_cb_layout(struct rpc_rqst *req,
                container_of(cb, struct nfs4_layout_stateid, ls_recall);
        struct nfs4_cb_compound_hdr hdr = {
                .ident = 0,
-               .minorversion = cb->cb_minorversion,
+               .minorversion = cb->cb_clp->cl_minorversion,
        };
 
        encode_cb_compound4args(xdr, &hdr);
@@ -623,6 +623,62 @@ static int nfs4_xdr_dec_cb_layout(struct rpc_rqst *rqstp,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static void encode_stateowner(struct xdr_stream *xdr, struct nfs4_stateowner *so)
+{
+       __be32  *p;
+
+       p = xdr_reserve_space(xdr, 8 + 4 + so->so_owner.len);
+       p = xdr_encode_opaque_fixed(p, &so->so_client->cl_clientid, 8);
+       xdr_encode_opaque(p, so->so_owner.data, so->so_owner.len);
+}
+
+static void nfs4_xdr_enc_cb_notify_lock(struct rpc_rqst *req,
+                                       struct xdr_stream *xdr,
+                                       const struct nfsd4_callback *cb)
+{
+       const struct nfsd4_blocked_lock *nbl =
+               container_of(cb, struct nfsd4_blocked_lock, nbl_cb);
+       struct nfs4_lockowner *lo = (struct nfs4_lockowner *)nbl->nbl_lock.fl_owner;
+       struct nfs4_cb_compound_hdr hdr = {
+               .ident = 0,
+               .minorversion = cb->cb_clp->cl_minorversion,
+       };
+
+       __be32 *p;
+
+       BUG_ON(hdr.minorversion == 0);
+
+       encode_cb_compound4args(xdr, &hdr);
+       encode_cb_sequence4args(xdr, cb, &hdr);
+
+       p = xdr_reserve_space(xdr, 4);
+       *p = cpu_to_be32(OP_CB_NOTIFY_LOCK);
+       encode_nfs_fh4(xdr, &nbl->nbl_fh);
+       encode_stateowner(xdr, &lo->lo_owner);
+       hdr.nops++;
+
+       encode_cb_nops(&hdr);
+}
+
+static int nfs4_xdr_dec_cb_notify_lock(struct rpc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       struct nfsd4_callback *cb)
+{
+       struct nfs4_cb_compound_hdr hdr;
+       int status;
+
+       status = decode_cb_compound4res(xdr, &hdr);
+       if (unlikely(status))
+               return status;
+
+       if (cb) {
+               status = decode_cb_sequence4res(xdr, cb);
+               if (unlikely(status || cb->cb_seq_status))
+                       return status;
+       }
+       return decode_cb_op_status(xdr, OP_CB_NOTIFY_LOCK, &cb->cb_status);
+}
+
 /*
  * RPC procedure tables
  */
@@ -643,6 +699,7 @@ static struct rpc_procinfo nfs4_cb_procedures[] = {
 #ifdef CONFIG_NFSD_PNFS
        PROC(CB_LAYOUT, COMPOUND,       cb_layout,      cb_layout),
 #endif
+       PROC(CB_NOTIFY_LOCK,    COMPOUND,       cb_notify_lock, cb_notify_lock),
 };
 
 static struct rpc_version nfs_cb_version4 = {
@@ -862,7 +919,6 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
        struct nfs4_client *clp = cb->cb_clp;
        u32 minorversion = clp->cl_minorversion;
 
-       cb->cb_minorversion = minorversion;
        /*
         * cb_seq_status is only set in decode_cb_sequence4res,
         * and so will remain 1 if an rpc level failure occurs.
index 2be9602..42aace4 100644 (file)
@@ -174,7 +174,8 @@ nfsd4_free_layout_stateid(struct nfs4_stid *stid)
        list_del_init(&ls->ls_perfile);
        spin_unlock(&fp->fi_lock);
 
-       vfs_setlease(ls->ls_file, F_UNLCK, NULL, (void **)&ls);
+       if (!nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
+               vfs_setlease(ls->ls_file, F_UNLCK, NULL, (void **)&ls);
        fput(ls->ls_file);
 
        if (ls->ls_recalled)
@@ -189,6 +190,9 @@ nfsd4_layout_setlease(struct nfs4_layout_stateid *ls)
        struct file_lock *fl;
        int status;
 
+       if (nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
+               return 0;
+
        fl = locks_alloc_lock();
        if (!fl)
                return -ENOMEM;
index 1fb2227..abb09b5 100644 (file)
@@ -1010,46 +1010,96 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 }
 
 static __be32
-nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
-               struct nfsd4_clone *clone)
+nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+                 stateid_t *src_stateid, struct file **src,
+                 stateid_t *dst_stateid, struct file **dst)
 {
-       struct file *src, *dst;
        __be32 status;
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
-                                           &clone->cl_src_stateid, RD_STATE,
-                                           &src, NULL);
+                                           src_stateid, RD_STATE, src, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
                goto out;
        }
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
-                                           &clone->cl_dst_stateid, WR_STATE,
-                                           &dst, NULL);
+                                           dst_stateid, WR_STATE, dst, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
                goto out_put_src;
        }
 
        /* fix up for NFS-specific error code */
-       if (!S_ISREG(file_inode(src)->i_mode) ||
-           !S_ISREG(file_inode(dst)->i_mode)) {
+       if (!S_ISREG(file_inode(*src)->i_mode) ||
+           !S_ISREG(file_inode(*dst)->i_mode)) {
                status = nfserr_wrong_type;
                goto out_put_dst;
        }
 
+out:
+       return status;
+out_put_dst:
+       fput(*dst);
+out_put_src:
+       fput(*src);
+       goto out;
+}
+
+static __be32
+nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_clone *clone)
+{
+       struct file *src, *dst;
+       __be32 status;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &clone->cl_src_stateid, &src,
+                                  &clone->cl_dst_stateid, &dst);
+       if (status)
+               goto out;
+
        status = nfsd4_clone_file_range(src, clone->cl_src_pos,
                        dst, clone->cl_dst_pos, clone->cl_count);
 
-out_put_dst:
        fput(dst);
-out_put_src:
        fput(src);
 out:
        return status;
 }
 
+static __be32
+nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_copy *copy)
+{
+       struct file *src, *dst;
+       __be32 status;
+       ssize_t bytes;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid, &src,
+                                  &copy->cp_dst_stateid, &dst);
+       if (status)
+               goto out;
+
+       bytes = nfsd_copy_file_range(src, copy->cp_src_pos,
+                       dst, copy->cp_dst_pos, copy->cp_count);
+
+       if (bytes < 0)
+               status = nfserrno(bytes);
+       else {
+               copy->cp_res.wr_bytes_written = bytes;
+               copy->cp_res.wr_stable_how = NFS_UNSTABLE;
+               copy->cp_consecutive = 1;
+               copy->cp_synchronous = 1;
+               gen_boot_verifier(&copy->cp_res.wr_verifier, SVC_NET(rqstp));
+               status = nfs_ok;
+       }
+
+       fput(src);
+       fput(dst);
+out:
+       return status;
+}
+
 static __be32
 nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                struct nfsd4_fallocate *fallocate, int flags)
@@ -1966,6 +2016,18 @@ static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd
                op_encode_channel_attrs_maxsz) * sizeof(__be32);
 }
 
+static inline u32 nfsd4_copy_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+       return (op_encode_hdr_size +
+               1 /* wr_callback */ +
+               op_encode_stateid_maxsz /* wr_callback */ +
+               2 /* wr_count */ +
+               1 /* wr_committed */ +
+               op_encode_verifier_maxsz +
+               1 /* cr_consecutive */ +
+               1 /* cr_synchronous */) * sizeof(__be32);
+}
+
 #ifdef CONFIG_NFSD_PNFS
 /*
  * At this stage we don't really know what layout driver will handle the request,
@@ -2328,6 +2390,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_name = "OP_CLONE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
+       [OP_COPY] = {
+               .op_func = (nfsd4op_func)nfsd4_copy,
+               .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+               .op_name = "OP_COPY",
+               .op_rsize_bop = (nfsd4op_rsize)nfsd4_copy_rsize,
+       },
        [OP_SEEK] = {
                .op_func = (nfsd4op_func)nfsd4_seek,
                .op_name = "OP_SEEK",
index 39bfaba..9752beb 100644 (file)
@@ -99,6 +99,7 @@ static struct kmem_cache *odstate_slab;
 static void free_session(struct nfsd4_session *);
 
 static const struct nfsd4_callback_ops nfsd4_cb_recall_ops;
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops;
 
 static bool is_session_dead(struct nfsd4_session *ses)
 {
@@ -210,6 +211,85 @@ static void nfsd4_put_session(struct nfsd4_session *ses)
        spin_unlock(&nn->client_lock);
 }
 
+static struct nfsd4_blocked_lock *
+find_blocked_lock(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+                       struct nfsd_net *nn)
+{
+       struct nfsd4_blocked_lock *cur, *found = NULL;
+
+       spin_lock(&nn->client_lock);
+       list_for_each_entry(cur, &lo->lo_blocked, nbl_list) {
+               if (fh_match(fh, &cur->nbl_fh)) {
+                       list_del_init(&cur->nbl_list);
+                       list_del_init(&cur->nbl_lru);
+                       found = cur;
+                       break;
+               }
+       }
+       spin_unlock(&nn->client_lock);
+       if (found)
+               posix_unblock_lock(&found->nbl_lock);
+       return found;
+}
+
+static struct nfsd4_blocked_lock *
+find_or_allocate_block(struct nfs4_lockowner *lo, struct knfsd_fh *fh,
+                       struct nfsd_net *nn)
+{
+       struct nfsd4_blocked_lock *nbl;
+
+       nbl = find_blocked_lock(lo, fh, nn);
+       if (!nbl) {
+               nbl= kmalloc(sizeof(*nbl), GFP_KERNEL);
+               if (nbl) {
+                       fh_copy_shallow(&nbl->nbl_fh, fh);
+                       locks_init_lock(&nbl->nbl_lock);
+                       nfsd4_init_cb(&nbl->nbl_cb, lo->lo_owner.so_client,
+                                       &nfsd4_cb_notify_lock_ops,
+                                       NFSPROC4_CLNT_CB_NOTIFY_LOCK);
+               }
+       }
+       return nbl;
+}
+
+static void
+free_blocked_lock(struct nfsd4_blocked_lock *nbl)
+{
+       locks_release_private(&nbl->nbl_lock);
+       kfree(nbl);
+}
+
+static int
+nfsd4_cb_notify_lock_done(struct nfsd4_callback *cb, struct rpc_task *task)
+{
+       /*
+        * Since this is just an optimization, we don't try very hard if it
+        * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and
+        * just quit trying on anything else.
+        */
+       switch (task->tk_status) {
+       case -NFS4ERR_DELAY:
+               rpc_delay(task, 1 * HZ);
+               return 0;
+       default:
+               return 1;
+       }
+}
+
+static void
+nfsd4_cb_notify_lock_release(struct nfsd4_callback *cb)
+{
+       struct nfsd4_blocked_lock       *nbl = container_of(cb,
+                                               struct nfsd4_blocked_lock, nbl_cb);
+
+       free_blocked_lock(nbl);
+}
+
+static const struct nfsd4_callback_ops nfsd4_cb_notify_lock_ops = {
+       .done           = nfsd4_cb_notify_lock_done,
+       .release        = nfsd4_cb_notify_lock_release,
+};
+
 static inline struct nfs4_stateowner *
 nfs4_get_stateowner(struct nfs4_stateowner *sop)
 {
@@ -3224,9 +3304,10 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                goto out;
        /* cases below refer to rfc 3530 section 14.2.34: */
        if (!unconf || !same_verf(&confirm, &unconf->cl_confirm)) {
-               if (conf && !unconf) /* case 2: probable retransmit */
+               if (conf && same_verf(&confirm, &conf->cl_confirm)) {
+                       /* case 2: probable retransmit */
                        status = nfs_ok;
-               else /* case 4: client hasn't noticed we rebooted yet? */
+               else /* case 4: client hasn't noticed we rebooted yet? */
                        status = nfserr_stale_clientid;
                goto out;
        }
@@ -4410,9 +4491,11 @@ out:
        * To finish the open response, we just need to set the rflags.
        */
        open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX;
-       if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED) &&
-           !nfsd4_has_session(&resp->cstate))
+       if (nfsd4_has_session(&resp->cstate))
+               open->op_rflags |= NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK;
+       else if (!(open->op_openowner->oo_flags & NFS4_OO_CONFIRMED))
                open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
+
        if (dp)
                nfs4_put_stid(&dp->dl_stid);
        if (stp)
@@ -4501,6 +4584,7 @@ nfs4_laundromat(struct nfsd_net *nn)
        struct nfs4_openowner *oo;
        struct nfs4_delegation *dp;
        struct nfs4_ol_stateid *stp;
+       struct nfsd4_blocked_lock *nbl;
        struct list_head *pos, *next, reaplist;
        time_t cutoff = get_seconds() - nn->nfsd4_lease;
        time_t t, new_timeo = nn->nfsd4_lease;
@@ -4569,6 +4653,41 @@ nfs4_laundromat(struct nfsd_net *nn)
        }
        spin_unlock(&nn->client_lock);
 
+       /*
+        * It's possible for a client to try and acquire an already held lock
+        * that is being held for a long time, and then lose interest in it.
+        * So, we clean out any un-revisited request after a lease period
+        * under the assumption that the client is no longer interested.
+        *
+        * RFC5661, sec. 9.6 states that the client must not rely on getting
+        * notifications and must continue to poll for locks, even when the
+        * server supports them. Thus this shouldn't lead to clients blocking
+        * indefinitely once the lock does become free.
+        */
+       BUG_ON(!list_empty(&reaplist));
+       spin_lock(&nn->client_lock);
+       while (!list_empty(&nn->blocked_locks_lru)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               if (time_after((unsigned long)nbl->nbl_time,
+                              (unsigned long)cutoff)) {
+                       t = nbl->nbl_time - cutoff;
+                       new_timeo = min(new_timeo, t);
+                       break;
+               }
+               list_move(&nbl->nbl_lru, &reaplist);
+               list_del_init(&nbl->nbl_list);
+       }
+       spin_unlock(&nn->client_lock);
+
+       while (!list_empty(&reaplist)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_del_init(&nbl->nbl_lru);
+               posix_unblock_lock(&nbl->nbl_lock);
+               free_blocked_lock(nbl);
+       }
+
        new_timeo = max_t(time_t, new_timeo, NFSD_LAUNDROMAT_MINTIMEOUT);
        return new_timeo;
 }
@@ -5309,7 +5428,31 @@ nfsd4_fl_put_owner(fl_owner_t owner)
                nfs4_put_stateowner(&lo->lo_owner);
 }
 
+static void
+nfsd4_lm_notify(struct file_lock *fl)
+{
+       struct nfs4_lockowner           *lo = (struct nfs4_lockowner *)fl->fl_owner;
+       struct net                      *net = lo->lo_owner.so_client->net;
+       struct nfsd_net                 *nn = net_generic(net, nfsd_net_id);
+       struct nfsd4_blocked_lock       *nbl = container_of(fl,
+                                               struct nfsd4_blocked_lock, nbl_lock);
+       bool queue = false;
+
+       /* An empty list means that something else is going to be using it */
+       spin_lock(&nn->client_lock);
+       if (!list_empty(&nbl->nbl_list)) {
+               list_del_init(&nbl->nbl_list);
+               list_del_init(&nbl->nbl_lru);
+               queue = true;
+       }
+       spin_unlock(&nn->client_lock);
+
+       if (queue)
+               nfsd4_run_cb(&nbl->nbl_cb);
+}
+
 static const struct lock_manager_operations nfsd_posix_mng_ops  = {
+       .lm_notify = nfsd4_lm_notify,
        .lm_get_owner = nfsd4_fl_get_owner,
        .lm_put_owner = nfsd4_fl_put_owner,
 };
@@ -5407,6 +5550,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp,
        lo = alloc_stateowner(lockowner_slab, &lock->lk_new_owner, clp);
        if (!lo)
                return NULL;
+       INIT_LIST_HEAD(&lo->lo_blocked);
        INIT_LIST_HEAD(&lo->lo_owner.so_stateids);
        lo->lo_owner.so_is_open_owner = 0;
        lo->lo_owner.so_seqid = lock->lk_new_lock_seqid;
@@ -5588,12 +5732,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        struct nfs4_ol_stateid *open_stp = NULL;
        struct nfs4_file *fp;
        struct file *filp = NULL;
+       struct nfsd4_blocked_lock *nbl = NULL;
        struct file_lock *file_lock = NULL;
        struct file_lock *conflock = NULL;
        __be32 status = 0;
        int lkflg;
        int err;
        bool new = false;
+       unsigned char fl_type;
+       unsigned int fl_flags = FL_POSIX;
        struct net *net = SVC_NET(rqstp);
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
@@ -5658,46 +5805,55 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!locks_in_grace(net) && lock->lk_reclaim)
                goto out;
 
-       file_lock = locks_alloc_lock();
-       if (!file_lock) {
-               dprintk("NFSD: %s: unable to allocate lock!\n", __func__);
-               status = nfserr_jukebox;
-               goto out;
-       }
-
        fp = lock_stp->st_stid.sc_file;
        switch (lock->lk_type) {
-               case NFS4_READ_LT:
                case NFS4_READW_LT:
+                       if (nfsd4_has_session(cstate))
+                               fl_flags |= FL_SLEEP;
+                       /* Fallthrough */
+               case NFS4_READ_LT:
                        spin_lock(&fp->fi_lock);
                        filp = find_readable_file_locked(fp);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_READ);
                        spin_unlock(&fp->fi_lock);
-                       file_lock->fl_type = F_RDLCK;
+                       fl_type = F_RDLCK;
                        break;
-               case NFS4_WRITE_LT:
                case NFS4_WRITEW_LT:
+                       if (nfsd4_has_session(cstate))
+                               fl_flags |= FL_SLEEP;
+                       /* Fallthrough */
+               case NFS4_WRITE_LT:
                        spin_lock(&fp->fi_lock);
                        filp = find_writeable_file_locked(fp);
                        if (filp)
                                get_lock_access(lock_stp, NFS4_SHARE_ACCESS_WRITE);
                        spin_unlock(&fp->fi_lock);
-                       file_lock->fl_type = F_WRLCK;
+                       fl_type = F_WRLCK;
                        break;
                default:
                        status = nfserr_inval;
                goto out;
        }
+
        if (!filp) {
                status = nfserr_openmode;
                goto out;
        }
 
+       nbl = find_or_allocate_block(lock_sop, &fp->fi_fhandle, nn);
+       if (!nbl) {
+               dprintk("NFSD: %s: unable to allocate block!\n", __func__);
+               status = nfserr_jukebox;
+               goto out;
+       }
+
+       file_lock = &nbl->nbl_lock;
+       file_lock->fl_type = fl_type;
        file_lock->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lock_sop->lo_owner));
        file_lock->fl_pid = current->tgid;
        file_lock->fl_file = filp;
-       file_lock->fl_flags = FL_POSIX;
+       file_lock->fl_flags = fl_flags;
        file_lock->fl_lmops = &nfsd_posix_mng_ops;
        file_lock->fl_start = lock->lk_offset;
        file_lock->fl_end = last_byte_offset(lock->lk_offset, lock->lk_length);
@@ -5710,18 +5866,29 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                goto out;
        }
 
+       if (fl_flags & FL_SLEEP) {
+               nbl->nbl_time = jiffies;
+               spin_lock(&nn->client_lock);
+               list_add_tail(&nbl->nbl_list, &lock_sop->lo_blocked);
+               list_add_tail(&nbl->nbl_lru, &nn->blocked_locks_lru);
+               spin_unlock(&nn->client_lock);
+       }
+
        err = vfs_lock_file(filp, F_SETLK, file_lock, conflock);
-       switch (-err) {
+       switch (err) {
        case 0: /* success! */
                nfs4_inc_and_copy_stateid(&lock->lk_resp_stateid, &lock_stp->st_stid);
                status = 0;
                break;
-       case (EAGAIN):          /* conflock holds conflicting lock */
+       case FILE_LOCK_DEFERRED:
+               nbl = NULL;
+               /* Fallthrough */
+       case -EAGAIN:           /* conflock holds conflicting lock */
                status = nfserr_denied;
                dprintk("NFSD: nfsd4_lock: conflicting lock found!\n");
                nfs4_set_lock_denied(conflock, &lock->lk_denied);
                break;
-       case (EDEADLK):
+       case -EDEADLK:
                status = nfserr_deadlock;
                break;
        default:
@@ -5730,6 +5897,16 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                break;
        }
 out:
+       if (nbl) {
+               /* dequeue it if we queued it before */
+               if (fl_flags & FL_SLEEP) {
+                       spin_lock(&nn->client_lock);
+                       list_del_init(&nbl->nbl_list);
+                       list_del_init(&nbl->nbl_lru);
+                       spin_unlock(&nn->client_lock);
+               }
+               free_blocked_lock(nbl);
+       }
        if (filp)
                fput(filp);
        if (lock_stp) {
@@ -5753,8 +5930,6 @@ out:
        if (open_stp)
                nfs4_put_stid(&open_stp->st_stid);
        nfsd4_bump_seqid(cstate, status);
-       if (file_lock)
-               locks_free_lock(file_lock);
        if (conflock)
                locks_free_lock(conflock);
        return status;
@@ -6768,6 +6943,7 @@ static int nfs4_state_create_net(struct net *net)
        INIT_LIST_HEAD(&nn->client_lru);
        INIT_LIST_HEAD(&nn->close_lru);
        INIT_LIST_HEAD(&nn->del_recall_lru);
+       INIT_LIST_HEAD(&nn->blocked_locks_lru);
        spin_lock_init(&nn->client_lock);
 
        INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main);
@@ -6865,6 +7041,7 @@ nfs4_state_shutdown_net(struct net *net)
        struct nfs4_delegation *dp = NULL;
        struct list_head *pos, *next, reaplist;
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
+       struct nfsd4_blocked_lock *nbl;
 
        cancel_delayed_work_sync(&nn->laundromat_work);
        locks_end_grace(&nn->nfsd4_manager);
@@ -6885,6 +7062,24 @@ nfs4_state_shutdown_net(struct net *net)
                nfs4_put_stid(&dp->dl_stid);
        }
 
+       BUG_ON(!list_empty(&reaplist));
+       spin_lock(&nn->client_lock);
+       while (!list_empty(&nn->blocked_locks_lru)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_move(&nbl->nbl_lru, &reaplist);
+               list_del_init(&nbl->nbl_list);
+       }
+       spin_unlock(&nn->client_lock);
+
+       while (!list_empty(&reaplist)) {
+               nbl = list_first_entry(&nn->blocked_locks_lru,
+                                       struct nfsd4_blocked_lock, nbl_lru);
+               list_del_init(&nbl->nbl_lru);
+               posix_unblock_lock(&nbl->nbl_lock);
+               free_blocked_lock(nbl);
+       }
+
        nfsd4_client_tracking_exit(net);
        nfs4_state_destroy_net(net);
 }
index 0aa0236..c2d2895 100644 (file)
@@ -1693,6 +1693,30 @@ nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
+{
+       DECODE_HEAD;
+       unsigned int tmp;
+
+       status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
+       if (status)
+               return status;
+       status = nfsd4_decode_stateid(argp, &copy->cp_dst_stateid);
+       if (status)
+               return status;
+
+       READ_BUF(8 + 8 + 8 + 4 + 4 + 4);
+       p = xdr_decode_hyper(p, &copy->cp_src_pos);
+       p = xdr_decode_hyper(p, &copy->cp_dst_pos);
+       p = xdr_decode_hyper(p, &copy->cp_count);
+       copy->cp_consecutive = be32_to_cpup(p++);
+       copy->cp_synchronous = be32_to_cpup(p++);
+       tmp = be32_to_cpup(p); /* Source server list not supported */
+
+       DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
 {
@@ -1793,7 +1817,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
 
        /* new operations for NFSv4.2 */
        [OP_ALLOCATE]           = (nfsd4_dec)nfsd4_decode_fallocate,
-       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_DEALLOCATE]         = (nfsd4_dec)nfsd4_decode_fallocate,
        [OP_IO_ADVISE]          = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -4062,7 +4086,7 @@ nfsd4_encode_getdeviceinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
        u32 starting_len = xdr->buf->len, needed_len;
        __be32 *p;
 
-       dprintk("%s: err %d\n", __func__, nfserr);
+       dprintk("%s: err %d\n", __func__, be32_to_cpu(nfserr));
        if (nfserr)
                goto out;
 
@@ -4201,6 +4225,41 @@ nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static __be32
+nfsd42_encode_write_res(struct nfsd4_compoundres *resp, struct nfsd42_write_res *write)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(&resp->xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
+       if (!p)
+               return nfserr_resource;
+
+       *p++ = cpu_to_be32(0);
+       p = xdr_encode_hyper(p, write->wr_bytes_written);
+       *p++ = cpu_to_be32(write->wr_stable_how);
+       p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
+                                   NFS4_VERIFIER_SIZE);
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
+                 struct nfsd4_copy *copy)
+{
+       __be32 *p;
+
+       if (!nfserr) {
+               nfserr = nfsd42_encode_write_res(resp, &copy->cp_res);
+               if (nfserr)
+                       return nfserr;
+
+               p = xdr_reserve_space(&resp->xdr, 4 + 4);
+               *p++ = cpu_to_be32(copy->cp_consecutive);
+               *p++ = cpu_to_be32(copy->cp_synchronous);
+       }
+       return nfserr;
+}
+
 static __be32
 nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
                  struct nfsd4_seek *seek)
@@ -4300,7 +4359,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 
        /* NFSv4.2 operations */
        [OP_ALLOCATE]           = (nfsd4_enc)nfsd4_encode_noop,
-       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_enc)nfsd4_encode_noop,
        [OP_DEALLOCATE]         = (nfsd4_enc)nfsd4_encode_noop,
        [OP_IO_ADVISE]          = (nfsd4_enc)nfsd4_encode_noop,
index 65ad016..36b2af9 100644 (file)
@@ -1216,6 +1216,8 @@ static __net_init int nfsd_init_net(struct net *net)
                goto out_idmap_error;
        nn->nfsd4_lease = 90;   /* default lease time */
        nn->nfsd4_grace = 90;
+       nn->clverifier_counter = prandom_u32();
+       nn->clientid_counter = prandom_u32();
        return 0;
 
 out_idmap_error:
index 0818874..010aff5 100644 (file)
@@ -789,6 +789,7 @@ nfserrno (int errno)
                { nfserr_toosmall, -ETOOSMALL },
                { nfserr_serverfault, -ESERVERFAULT },
                { nfserr_serverfault, -ENFILE },
+               { nfserr_io, -EUCLEAN },
        };
        int     i;
 
@@ -796,7 +797,7 @@ nfserrno (int errno)
                if (nfs_errtbl[i].syserr == errno)
                        return nfs_errtbl[i].nfserr;
        }
-       WARN(1, "nfsd: non-standard errno: %d\n", errno);
+       WARN_ONCE(1, "nfsd: non-standard errno: %d\n", errno);
        return nfserr_io;
 }
 
index 45007ac..a2b65fc 100644 (file)
@@ -366,14 +366,21 @@ static struct notifier_block nfsd_inet6addr_notifier = {
 };
 #endif
 
+/* Only used under nfsd_mutex, so this atomic may be overkill: */
+static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
+
 static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
 {
        struct nfsd_net *nn = net_generic(net, nfsd_net_id);
 
-       unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
+       /* check if the notifier still has clients */
+       if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
+               unregister_inetaddr_notifier(&nfsd_inetaddr_notifier);
 #if IS_ENABLED(CONFIG_IPV6)
-       unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
+               unregister_inet6addr_notifier(&nfsd_inet6addr_notifier);
 #endif
+       }
+
        /*
         * write_ports can create the server without actually starting
         * any threads--if we get shut down before any threads are
@@ -488,10 +495,13 @@ int nfsd_create_serv(struct net *net)
        }
 
        set_max_drc();
-       register_inetaddr_notifier(&nfsd_inetaddr_notifier);
+       /* check if the notifier is already set */
+       if (atomic_inc_return(&nfsd_notifier_refcount) == 1) {
+               register_inetaddr_notifier(&nfsd_inetaddr_notifier);
 #if IS_ENABLED(CONFIG_IPV6)
-       register_inet6addr_notifier(&nfsd_inet6addr_notifier);
+               register_inet6addr_notifier(&nfsd_inet6addr_notifier);
 #endif
+       }
        do_gettimeofday(&nn->nfssvc_boot);              /* record boot time */
        return 0;
 }
index 0c2a716..d27a5aa 100644 (file)
@@ -19,6 +19,7 @@ struct nfsd4_deviceid_map {
 
 struct nfsd4_layout_ops {
        u32             notify_types;
+       bool            disable_recalls;
 
        __be32 (*proc_getdeviceinfo)(struct super_block *sb,
                        struct svc_rqst *rqstp,
index b95adf9..c939936 100644 (file)
@@ -63,7 +63,6 @@ typedef struct {
 
 struct nfsd4_callback {
        struct nfs4_client *cb_clp;
-       u32 cb_minorversion;
        struct rpc_message cb_msg;
        const struct nfsd4_callback_ops *cb_ops;
        struct work_struct cb_work;
@@ -441,11 +440,11 @@ struct nfs4_openowner {
 /*
  * Represents a generic "lockowner". Similar to an openowner. References to it
  * are held by the lock stateids that are created on its behalf. This object is
- * a superset of the nfs4_stateowner struct (or would be if it needed any extra
- * fields).
+ * a superset of the nfs4_stateowner struct.
  */
 struct nfs4_lockowner {
-       struct nfs4_stateowner  lo_owner; /* must be first element */
+       struct nfs4_stateowner  lo_owner;       /* must be first element */
+       struct list_head        lo_blocked;     /* blocked file_locks */
 };
 
 static inline struct nfs4_openowner * openowner(struct nfs4_stateowner *so)
@@ -572,6 +571,7 @@ enum nfsd4_cb_op {
        NFSPROC4_CLNT_CB_RECALL,
        NFSPROC4_CLNT_CB_LAYOUT,
        NFSPROC4_CLNT_CB_SEQUENCE,
+       NFSPROC4_CLNT_CB_NOTIFY_LOCK,
 };
 
 /* Returns true iff a is later than b: */
@@ -580,6 +580,20 @@ static inline bool nfsd4_stateid_generation_after(stateid_t *a, stateid_t *b)
        return (s32)(a->si_generation - b->si_generation) > 0;
 }
 
+/*
+ * When a client tries to get a lock on a file, we set one of these objects
+ * on the blocking lock. When the lock becomes free, we can then issue a
+ * CB_NOTIFY_LOCK to the server.
+ */
+struct nfsd4_blocked_lock {
+       struct list_head        nbl_list;
+       struct list_head        nbl_lru;
+       unsigned long           nbl_time;
+       struct file_lock        nbl_lock;
+       struct knfsd_fh         nbl_fh;
+       struct nfsd4_callback   nbl_cb;
+};
+
 struct nfsd4_compound_state;
 struct nfsd_net;
 
index ff476e6..8ca642f 100644 (file)
@@ -513,6 +513,22 @@ __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
                        count));
 }
 
+ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
+                            u64 dst_pos, u64 count)
+{
+
+       /*
+        * Limit copy to 4MB to prevent indefinitely blocking an nfsd
+        * thread and client rpc slot.  The choice of 4MB is somewhat
+        * arbitrary.  We might instead base this on r/wsize, or make it
+        * tunable, or use a time instead of a byte limit, or implement
+        * asynchronous copy.  In theory a client could also recognize a
+        * limit like this and pipeline multiple COPY requests.
+        */
+       count = min_t(u64, count, 1 << 22);
+       return vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0);
+}
+
 __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
                           struct file *file, loff_t offset, loff_t len,
                           int flags)
index 3cbb1b3..0bf9e7b 100644 (file)
@@ -96,6 +96,8 @@ __be32                nfsd_symlink(struct svc_rqst *, struct svc_fh *,
                                struct svc_fh *res);
 __be32         nfsd_link(struct svc_rqst *, struct svc_fh *,
                                char *, int, struct svc_fh *);
+ssize_t                nfsd_copy_file_range(struct file *, u64,
+                                    struct file *, u64, u64);
 __be32         nfsd_rename(struct svc_rqst *,
                                struct svc_fh *, char *, int,
                                struct svc_fh *, char *, int);
index beea0c5..8fda4ab 100644 (file)
@@ -503,6 +503,28 @@ struct nfsd4_clone {
        u64             cl_count;
 };
 
+struct nfsd42_write_res {
+       u64                     wr_bytes_written;
+       u32                     wr_stable_how;
+       nfs4_verifier           wr_verifier;
+};
+
+struct nfsd4_copy {
+       /* request */
+       stateid_t       cp_src_stateid;
+       stateid_t       cp_dst_stateid;
+       u64             cp_src_pos;
+       u64             cp_dst_pos;
+       u64             cp_count;
+
+       /* both */
+       bool            cp_consecutive;
+       bool            cp_synchronous;
+
+       /* response */
+       struct nfsd42_write_res cp_res;
+};
+
 struct nfsd4_seek {
        /* request */
        stateid_t       seek_stateid;
@@ -568,6 +590,7 @@ struct nfsd4_op {
                struct nfsd4_fallocate          allocate;
                struct nfsd4_fallocate          deallocate;
                struct nfsd4_clone              clone;
+               struct nfsd4_copy               copy;
                struct nfsd4_seek               seek;
        } u;
        struct nfs4_replay *                    replay;
index c47f6fd..49b719d 100644 (file)
 #define NFS4_dec_cb_layout_sz          (cb_compound_dec_hdr_sz  +      \
                                        cb_sequence_dec_sz +            \
                                        op_dec_sz)
+
+#define NFS4_enc_cb_notify_lock_sz     (cb_compound_enc_hdr_sz +        \
+                                       cb_sequence_enc_sz +             \
+                                       2 + 1 +                          \
+                                       XDR_QUADLEN(NFS4_OPAQUE_LIMIT) + \
+                                       enc_nfs4_fh_sz)
+#define NFS4_dec_cb_notify_lock_sz     (cb_compound_dec_hdr_sz  +      \
+                                       cb_sequence_dec_sz +            \
+                                       op_dec_sz)
index 6ea06f8..3f828a1 100644 (file)
@@ -3188,6 +3188,9 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data,
                                    migrate->new_master,
                                    migrate->master);
 
+       if (ret < 0)
+               kmem_cache_free(dlm_mle_cache, mle);
+
        spin_unlock(&dlm->master_lock);
 unlock:
        spin_unlock(&dlm->spinlock);
index 8aeb08b..d3ed817 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -267,6 +267,11 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
            (mode & ~FALLOC_FL_INSERT_RANGE))
                return -EINVAL;
 
+       /* Unshare range should only be used with allocate mode. */
+       if ((mode & FALLOC_FL_UNSHARE_RANGE) &&
+           (mode & ~(FALLOC_FL_UNSHARE_RANGE | FALLOC_FL_KEEP_SIZE)))
+               return -EINVAL;
+
        if (!(file->f_mode & FMODE_WRITE))
                return -EBADF;
 
@@ -300,7 +305,8 @@ int vfs_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
         * Let individual file system decide if it supports preallocation
         * for directories or not.
         */
-       if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
+       if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) &&
+           !S_ISBLK(inode->i_mode))
                return -ENODEV;
 
        /* Check for wrap through zero too */
index 3f803b3..aeb60f7 100644 (file)
@@ -57,6 +57,7 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
        ssize_t list_size, size, value_size = 0;
        char *buf, *name, *value = NULL;
        int uninitialized_var(error);
+       size_t slen;
 
        if (!(old->d_inode->i_opflags & IOP_XATTR) ||
            !(new->d_inode->i_opflags & IOP_XATTR))
@@ -79,7 +80,16 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new)
                goto out;
        }
 
-       for (name = buf; name < (buf + list_size); name += strlen(name) + 1) {
+       for (name = buf; list_size; name += slen) {
+               slen = strnlen(name, list_size) + 1;
+
+               /* underlying fs providing us with an broken xattr list? */
+               if (WARN_ON(slen > list_size)) {
+                       error = -EIO;
+                       break;
+               }
+               list_size -= slen;
+
                if (ovl_is_private_xattr(name))
                        continue;
 retry:
@@ -174,40 +184,6 @@ out_fput:
        return error;
 }
 
-static char *ovl_read_symlink(struct dentry *realdentry)
-{
-       int res;
-       char *buf;
-       struct inode *inode = realdentry->d_inode;
-       mm_segment_t old_fs;
-
-       res = -EINVAL;
-       if (!inode->i_op->readlink)
-               goto err;
-
-       res = -ENOMEM;
-       buf = (char *) __get_free_page(GFP_KERNEL);
-       if (!buf)
-               goto err;
-
-       old_fs = get_fs();
-       set_fs(get_ds());
-       /* The cast to a user pointer is valid due to the set_fs() */
-       res = inode->i_op->readlink(realdentry,
-                                   (char __user *)buf, PAGE_SIZE - 1);
-       set_fs(old_fs);
-       if (res < 0) {
-               free_page((unsigned long) buf);
-               goto err;
-       }
-       buf[res] = '\0';
-
-       return buf;
-
-err:
-       return ERR_PTR(res);
-}
-
 static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
 {
        struct iattr attr = {
@@ -354,19 +330,20 @@ out_cleanup:
 int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                    struct path *lowerpath, struct kstat *stat)
 {
+       DEFINE_DELAYED_CALL(done);
        struct dentry *workdir = ovl_workdir(dentry);
        int err;
        struct kstat pstat;
        struct path parentpath;
+       struct dentry *lowerdentry = lowerpath->dentry;
        struct dentry *upperdir;
        struct dentry *upperdentry;
-       const struct cred *old_cred;
-       char *link = NULL;
+       const char *link = NULL;
 
        if (WARN_ON(!workdir))
                return -EROFS;
 
-       ovl_do_check_copy_up(lowerpath->dentry);
+       ovl_do_check_copy_up(lowerdentry);
 
        ovl_path_upper(parent, &parentpath);
        upperdir = parentpath.dentry;
@@ -376,13 +353,11 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
                return err;
 
        if (S_ISLNK(stat->mode)) {
-               link = ovl_read_symlink(lowerpath->dentry);
+               link = vfs_get_link(lowerdentry, &done);
                if (IS_ERR(link))
                        return PTR_ERR(link);
        }
 
-       old_cred = ovl_override_creds(dentry->d_sb);
-
        err = -EIO;
        if (lock_rename(workdir, upperdir) != NULL) {
                pr_err("overlayfs: failed to lock workdir+upperdir\n");
@@ -403,19 +378,16 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
        }
 out_unlock:
        unlock_rename(workdir, upperdir);
-       revert_creds(old_cred);
-
-       if (link)
-               free_page((unsigned long) link);
+       do_delayed_call(&done);
 
        return err;
 }
 
 int ovl_copy_up(struct dentry *dentry)
 {
-       int err;
+       int err = 0;
+       const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
 
-       err = 0;
        while (!err) {
                struct dentry *next;
                struct dentry *parent;
@@ -447,6 +419,7 @@ int ovl_copy_up(struct dentry *dentry)
                dput(parent);
                dput(next);
        }
+       revert_creds(old_cred);
 
        return err;
 }
index 5f90ddf..306b6c1 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/cred.h>
 #include <linux/posix_acl.h>
 #include <linux/posix_acl_xattr.h>
+#include <linux/atomic.h>
 #include "overlayfs.h"
 
 void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
@@ -37,8 +38,10 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry)
 {
        struct dentry *temp;
        char name[20];
+       static atomic_t temp_id = ATOMIC_INIT(0);
 
-       snprintf(name, sizeof(name), "#%lx", (unsigned long) dentry);
+       /* counter is allowed to wrap, since temp dentries are ephemeral */
+       snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
 
        temp = lookup_one_len(name, workdir, strlen(name));
        if (!IS_ERR(temp) && temp->d_inode) {
index c18d6a4..c58f01b 100644 (file)
@@ -19,6 +19,7 @@ static int ovl_copy_up_truncate(struct dentry *dentry)
        struct dentry *parent;
        struct kstat stat;
        struct path lowerpath;
+       const struct cred *old_cred;
 
        parent = dget_parent(dentry);
        err = ovl_copy_up(parent);
@@ -26,12 +27,14 @@ static int ovl_copy_up_truncate(struct dentry *dentry)
                goto out_dput_parent;
 
        ovl_path_lower(dentry, &lowerpath);
-       err = vfs_getattr(&lowerpath, &stat);
-       if (err)
-               goto out_dput_parent;
 
-       stat.size = 0;
-       err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
+       old_cred = ovl_override_creds(dentry->d_sb);
+       err = vfs_getattr(&lowerpath, &stat);
+       if (!err) {
+               stat.size = 0;
+               err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
+       }
+       revert_creds(old_cred);
 
 out_dput_parent:
        dput(parent);
@@ -153,45 +156,18 @@ static const char *ovl_get_link(struct dentry *dentry,
                                struct inode *inode,
                                struct delayed_call *done)
 {
-       struct dentry *realdentry;
-       struct inode *realinode;
        const struct cred *old_cred;
        const char *p;
 
        if (!dentry)
                return ERR_PTR(-ECHILD);
 
-       realdentry = ovl_dentry_real(dentry);
-       realinode = realdentry->d_inode;
-
-       if (WARN_ON(!realinode->i_op->get_link))
-               return ERR_PTR(-EPERM);
-
        old_cred = ovl_override_creds(dentry->d_sb);
-       p = realinode->i_op->get_link(realdentry, realinode, done);
+       p = vfs_get_link(ovl_dentry_real(dentry), done);
        revert_creds(old_cred);
        return p;
 }
 
-static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
-{
-       struct path realpath;
-       struct inode *realinode;
-       const struct cred *old_cred;
-       int err;
-
-       ovl_path_real(dentry, &realpath);
-       realinode = realpath.dentry->d_inode;
-
-       if (!realinode->i_op->readlink)
-               return -EINVAL;
-
-       old_cred = ovl_override_creds(dentry->d_sb);
-       err = realinode->i_op->readlink(realpath.dentry, buf, bufsiz);
-       revert_creds(old_cred);
-       return err;
-}
-
 bool ovl_is_private_xattr(const char *name)
 {
        return strncmp(name, OVL_XATTR_PREFIX,
@@ -375,7 +351,7 @@ static const struct inode_operations ovl_file_inode_operations = {
 static const struct inode_operations ovl_symlink_inode_operations = {
        .setattr        = ovl_setattr,
        .get_link       = ovl_get_link,
-       .readlink       = ovl_readlink,
+       .readlink       = generic_readlink,
        .getattr        = ovl_getattr,
        .listxattr      = ovl_listxattr,
        .update_time    = ovl_update_time,
index 0ffc8da..bcf3965 100644 (file)
@@ -273,12 +273,11 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
 {
        int res;
        char val;
-       struct inode *inode = dentry->d_inode;
 
-       if (!S_ISDIR(inode->i_mode) || !(inode->i_opflags & IOP_XATTR))
+       if (!d_is_dir(dentry))
                return false;
 
-       res = __vfs_getxattr(dentry, inode, OVL_XATTR_OPAQUE, &val, 1);
+       res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
        if (res == 1 && val == 'y')
                return true;
 
@@ -419,16 +418,12 @@ static bool ovl_dentry_weird(struct dentry *dentry)
                                  DCACHE_OP_COMPARE);
 }
 
-static inline struct dentry *ovl_lookup_real(struct super_block *ovl_sb,
-                                            struct dentry *dir,
+static inline struct dentry *ovl_lookup_real(struct dentry *dir,
                                             const struct qstr *name)
 {
-       const struct cred *old_cred;
        struct dentry *dentry;
 
-       old_cred = ovl_override_creds(ovl_sb);
        dentry = lookup_one_len_unlocked(name->name, dir, name->len);
-       revert_creds(old_cred);
 
        if (IS_ERR(dentry)) {
                if (PTR_ERR(dentry) == -ENOENT)
@@ -469,6 +464,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                          unsigned int flags)
 {
        struct ovl_entry *oe;
+       const struct cred *old_cred;
        struct ovl_entry *poe = dentry->d_parent->d_fsdata;
        struct path *stack = NULL;
        struct dentry *upperdir, *upperdentry = NULL;
@@ -479,9 +475,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
        unsigned int i;
        int err;
 
+       old_cred = ovl_override_creds(dentry->d_sb);
        upperdir = ovl_upperdentry_dereference(poe);
        if (upperdir) {
-               this = ovl_lookup_real(dentry->d_sb, upperdir, &dentry->d_name);
+               this = ovl_lookup_real(upperdir, &dentry->d_name);
                err = PTR_ERR(this);
                if (IS_ERR(this))
                        goto out;
@@ -514,8 +511,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                bool opaque = false;
                struct path lowerpath = poe->lowerstack[i];
 
-               this = ovl_lookup_real(dentry->d_sb,
-                                      lowerpath.dentry, &dentry->d_name);
+               this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
                err = PTR_ERR(this);
                if (IS_ERR(this)) {
                        /*
@@ -588,6 +584,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
                ovl_copyattr(realdentry->d_inode, inode);
        }
 
+       revert_creds(old_cred);
        oe->opaque = upperopaque;
        oe->__upperdentry = upperdentry;
        memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
@@ -606,6 +603,7 @@ out_put:
 out_put_upper:
        dput(upperdentry);
 out:
+       revert_creds(old_cred);
        return ERR_PTR(err);
 }
 
@@ -834,6 +832,19 @@ retry:
                if (err)
                        goto out_dput;
 
+               /*
+                * Try to remove POSIX ACL xattrs from workdir.  We are good if:
+                *
+                * a) success (there was a POSIX ACL xattr and was removed)
+                * b) -ENODATA (there was no POSIX ACL xattr)
+                * c) -EOPNOTSUPP (POSIX ACL xattrs are not supported)
+                *
+                * There are various other error values that could effectively
+                * mean that the xattr doesn't exist (e.g. -ERANGE is returned
+                * if the xattr name is too long), but the set of filesystems
+                * allowed as upper are limited to "normal" ones, where checking
+                * for the above two errors is sufficient.
+                */
                err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
                if (err && err != -ENODATA && err != -EOPNOTSUPP)
                        goto out_dput;
index 1f559f0..8e0d9f2 100644 (file)
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -601,54 +601,63 @@ pipe_fasync(int fd, struct file *filp, int on)
        return retval;
 }
 
-static void account_pipe_buffers(struct pipe_inode_info *pipe,
+static unsigned long account_pipe_buffers(struct user_struct *user,
                                  unsigned long old, unsigned long new)
 {
-       atomic_long_add(new - old, &pipe->user->pipe_bufs);
+       return atomic_long_add_return(new - old, &user->pipe_bufs);
 }
 
-static bool too_many_pipe_buffers_soft(struct user_struct *user)
+static bool too_many_pipe_buffers_soft(unsigned long user_bufs)
 {
-       return pipe_user_pages_soft &&
-              atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft;
+       return pipe_user_pages_soft && user_bufs >= pipe_user_pages_soft;
 }
 
-static bool too_many_pipe_buffers_hard(struct user_struct *user)
+static bool too_many_pipe_buffers_hard(unsigned long user_bufs)
 {
-       return pipe_user_pages_hard &&
-              atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard;
+       return pipe_user_pages_hard && user_bufs >= pipe_user_pages_hard;
 }
 
 struct pipe_inode_info *alloc_pipe_info(void)
 {
        struct pipe_inode_info *pipe;
+       unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
+       struct user_struct *user = get_current_user();
+       unsigned long user_bufs;
 
        pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL_ACCOUNT);
-       if (pipe) {
-               unsigned long pipe_bufs = PIPE_DEF_BUFFERS;
-               struct user_struct *user = get_current_user();
-
-               if (!too_many_pipe_buffers_hard(user)) {
-                       if (too_many_pipe_buffers_soft(user))
-                               pipe_bufs = 1;
-                       pipe->bufs = kcalloc(pipe_bufs,
-                                            sizeof(struct pipe_buffer),
-                                            GFP_KERNEL_ACCOUNT);
-               }
+       if (pipe == NULL)
+               goto out_free_uid;
 
-               if (pipe->bufs) {
-                       init_waitqueue_head(&pipe->wait);
-                       pipe->r_counter = pipe->w_counter = 1;
-                       pipe->buffers = pipe_bufs;
-                       pipe->user = user;
-                       account_pipe_buffers(pipe, 0, pipe_bufs);
-                       mutex_init(&pipe->mutex);
-                       return pipe;
-               }
-               free_uid(user);
-               kfree(pipe);
+       if (pipe_bufs * PAGE_SIZE > pipe_max_size && !capable(CAP_SYS_RESOURCE))
+               pipe_bufs = pipe_max_size >> PAGE_SHIFT;
+
+       user_bufs = account_pipe_buffers(user, 0, pipe_bufs);
+
+       if (too_many_pipe_buffers_soft(user_bufs)) {
+               user_bufs = account_pipe_buffers(user, pipe_bufs, 1);
+               pipe_bufs = 1;
+       }
+
+       if (too_many_pipe_buffers_hard(user_bufs))
+               goto out_revert_acct;
+
+       pipe->bufs = kcalloc(pipe_bufs, sizeof(struct pipe_buffer),
+                            GFP_KERNEL_ACCOUNT);
+
+       if (pipe->bufs) {
+               init_waitqueue_head(&pipe->wait);
+               pipe->r_counter = pipe->w_counter = 1;
+               pipe->buffers = pipe_bufs;
+               pipe->user = user;
+               mutex_init(&pipe->mutex);
+               return pipe;
        }
 
+out_revert_acct:
+       (void) account_pipe_buffers(user, pipe_bufs, 0);
+       kfree(pipe);
+out_free_uid:
+       free_uid(user);
        return NULL;
 }
 
@@ -656,7 +665,7 @@ void free_pipe_info(struct pipe_inode_info *pipe)
 {
        int i;
 
-       account_pipe_buffers(pipe, pipe->buffers, 0);
+       (void) account_pipe_buffers(pipe->user, pipe->buffers, 0);
        free_uid(pipe->user);
        for (i = 0; i < pipe->buffers; i++) {
                struct pipe_buffer *buf = pipe->bufs + i;
@@ -1007,13 +1016,55 @@ const struct file_operations pipefifo_fops = {
        .fasync         = pipe_fasync,
 };
 
+/*
+ * Currently we rely on the pipe array holding a power-of-2 number
+ * of pages.
+ */
+static inline unsigned int round_pipe_size(unsigned int size)
+{
+       unsigned long nr_pages;
+
+       nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
+       return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
+}
+
 /*
  * Allocate a new array of pipe buffers and copy the info over. Returns the
  * pipe size if successful, or return -ERROR on error.
  */
-static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
+static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
 {
        struct pipe_buffer *bufs;
+       unsigned int size, nr_pages;
+       unsigned long user_bufs;
+       long ret = 0;
+
+       size = round_pipe_size(arg);
+       nr_pages = size >> PAGE_SHIFT;
+
+       if (!nr_pages)
+               return -EINVAL;
+
+       /*
+        * If trying to increase the pipe capacity, check that an
+        * unprivileged user is not trying to exceed various limits
+        * (soft limit check here, hard limit check just below).
+        * Decreasing the pipe capacity is always permitted, even
+        * if the user is currently over a limit.
+        */
+       if (nr_pages > pipe->buffers &&
+                       size > pipe_max_size && !capable(CAP_SYS_RESOURCE))
+               return -EPERM;
+
+       user_bufs = account_pipe_buffers(pipe->user, pipe->buffers, nr_pages);
+
+       if (nr_pages > pipe->buffers &&
+                       (too_many_pipe_buffers_hard(user_bufs) ||
+                        too_many_pipe_buffers_soft(user_bufs)) &&
+                       !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
+               ret = -EPERM;
+               goto out_revert_acct;
+       }
 
        /*
         * We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't
@@ -1021,13 +1072,17 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
         * again like we would do for growing. If the pipe currently
         * contains more buffers than arg, then return busy.
         */
-       if (nr_pages < pipe->nrbufs)
-               return -EBUSY;
+       if (nr_pages < pipe->nrbufs) {
+               ret = -EBUSY;
+               goto out_revert_acct;
+       }
 
        bufs = kcalloc(nr_pages, sizeof(*bufs),
                       GFP_KERNEL_ACCOUNT | __GFP_NOWARN);
-       if (unlikely(!bufs))
-               return -ENOMEM;
+       if (unlikely(!bufs)) {
+               ret = -ENOMEM;
+               goto out_revert_acct;
+       }
 
        /*
         * The pipe array wraps around, so just start the new one at zero
@@ -1050,24 +1105,15 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages)
                        memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer));
        }
 
-       account_pipe_buffers(pipe, pipe->buffers, nr_pages);
        pipe->curbuf = 0;
        kfree(pipe->bufs);
        pipe->bufs = bufs;
        pipe->buffers = nr_pages;
        return nr_pages * PAGE_SIZE;
-}
-
-/*
- * Currently we rely on the pipe array holding a power-of-2 number
- * of pages.
- */
-static inline unsigned int round_pipe_size(unsigned int size)
-{
-       unsigned long nr_pages;
 
-       nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
-       return roundup_pow_of_two(nr_pages) << PAGE_SHIFT;
+out_revert_acct:
+       (void) account_pipe_buffers(pipe->user, nr_pages, pipe->buffers);
+       return ret;
 }
 
 /*
@@ -1109,28 +1155,9 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
        __pipe_lock(pipe);
 
        switch (cmd) {
-       case F_SETPIPE_SZ: {
-               unsigned int size, nr_pages;
-
-               size = round_pipe_size(arg);
-               nr_pages = size >> PAGE_SHIFT;
-
-               ret = -EINVAL;
-               if (!nr_pages)
-                       goto out;
-
-               if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) {
-                       ret = -EPERM;
-                       goto out;
-               } else if ((too_many_pipe_buffers_hard(pipe->user) ||
-                           too_many_pipe_buffers_soft(pipe->user)) &&
-                          !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) {
-                       ret = -EPERM;
-                       goto out;
-               }
-               ret = pipe_set_size(pipe, nr_pages);
+       case F_SETPIPE_SZ:
+               ret = pipe_set_size(pipe, arg);
                break;
-               }
        case F_GETPIPE_SZ:
                ret = pipe->buffers * PAGE_SIZE;
                break;
@@ -1139,7 +1166,6 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg)
                break;
        }
 
-out:
        __pipe_unlock(pipe);
        return ret;
 }
index 8ed9da5..3d4f85d 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/sched/rt.h>
 #include <linux/freezer.h>
 #include <net/busy_poll.h>
+#include <linux/vmalloc.h>
 
 #include <asm/uaccess.h>
 
@@ -554,7 +555,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
        fd_set_bits fds;
        void *bits;
        int ret, max_fds;
-       unsigned int size;
+       size_t size, alloc_size;
        struct fdtable *fdt;
        /* Allocate small arguments on the stack to save memory and be faster */
        long stack_fds[SELECT_STACK_ALLOC/sizeof(long)];
@@ -581,7 +582,14 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
        if (size > sizeof(stack_fds) / 6) {
                /* Not enough space in on-stack array; must use kmalloc */
                ret = -ENOMEM;
-               bits = kmalloc(6 * size, GFP_KERNEL);
+               if (size > (SIZE_MAX / 6))
+                       goto out_nofds;
+
+               alloc_size = 6 * size;
+               bits = kmalloc(alloc_size, GFP_KERNEL|__GFP_NOWARN);
+               if (!bits && alloc_size > PAGE_SIZE)
+                       bits = vmalloc(alloc_size);
+
                if (!bits)
                        goto out_nofds;
        }
@@ -618,7 +626,7 @@ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp,
 
 out:
        if (bits != stack_fds)
-               kfree(bits);
+               kvfree(bits);
 out_nofds:
        return ret;
 }
index 94374e4..2b67bda 100644 (file)
@@ -21,14 +21,14 @@ DEFINE_SPINLOCK(sysfs_symlink_target_lock);
 
 void sysfs_warn_dup(struct kernfs_node *parent, const char *name)
 {
-       char *buf, *path = NULL;
+       char *buf;
 
        buf = kzalloc(PATH_MAX, GFP_KERNEL);
        if (buf)
-               path = kernfs_path(parent, buf, PATH_MAX);
+               kernfs_path(parent, buf, PATH_MAX);
 
        WARN(1, KERN_WARNING "sysfs: cannot create duplicate filename '%s/%s'\n",
-            path, name);
+            buf, name);
 
        kfree(buf);
 }
index 584e87e..26ef195 100644 (file)
@@ -55,6 +55,8 @@ xfs-y                         += $(addprefix libxfs/, \
                                   xfs_ag_resv.o \
                                   xfs_rmap.o \
                                   xfs_rmap_btree.o \
+                                  xfs_refcount.o \
+                                  xfs_refcount_btree.o \
                                   xfs_sb.o \
                                   xfs_symlink_remote.o \
                                   xfs_trans_resv.o \
@@ -88,6 +90,7 @@ xfs-y                         += xfs_aops.o \
                                   xfs_message.o \
                                   xfs_mount.o \
                                   xfs_mru_cache.o \
+                                  xfs_reflink.o \
                                   xfs_stats.o \
                                   xfs_super.o \
                                   xfs_symlink.o \
@@ -100,16 +103,20 @@ xfs-y                             += xfs_aops.o \
 # low-level transaction/log code
 xfs-y                          += xfs_log.o \
                                   xfs_log_cil.o \
+                                  xfs_bmap_item.o \
                                   xfs_buf_item.o \
                                   xfs_extfree_item.o \
                                   xfs_icreate_item.o \
                                   xfs_inode_item.o \
+                                  xfs_refcount_item.o \
                                   xfs_rmap_item.o \
                                   xfs_log_recover.o \
                                   xfs_trans_ail.o \
+                                  xfs_trans_bmap.o \
                                   xfs_trans_buf.o \
                                   xfs_trans_extfree.o \
                                   xfs_trans_inode.o \
+                                  xfs_trans_refcount.o \
                                   xfs_trans_rmap.o \
 
 # optional features
index e3ae0f2..e5ebc37 100644 (file)
@@ -38,6 +38,7 @@
 #include "xfs_trans_space.h"
 #include "xfs_rmap_btree.h"
 #include "xfs_btree.h"
+#include "xfs_refcount_btree.h"
 
 /*
  * Per-AG Block Reservations
@@ -108,7 +109,9 @@ xfs_ag_resv_critical(
        trace_xfs_ag_resv_critical(pag, type, avail);
 
        /* Critically low if less than 10% or max btree height remains. */
-       return avail < orig / 10 || avail < XFS_BTREE_MAXLEVELS;
+       return XFS_TEST_ERROR(avail < orig / 10 || avail < XFS_BTREE_MAXLEVELS,
+                       pag->pag_mount, XFS_ERRTAG_AG_RESV_CRITICAL,
+                       XFS_RANDOM_AG_RESV_CRITICAL);
 }
 
 /*
@@ -228,6 +231,11 @@ xfs_ag_resv_init(
        if (pag->pag_meta_resv.ar_asked == 0) {
                ask = used = 0;
 
+               error = xfs_refcountbt_calc_reserves(pag->pag_mount,
+                               pag->pag_agno, &ask, &used);
+               if (error)
+                       goto out;
+
                error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
                                ask, used);
                if (error)
@@ -238,6 +246,11 @@ xfs_ag_resv_init(
        if (pag->pag_agfl_resv.ar_asked == 0) {
                ask = used = 0;
 
+               error = xfs_rmapbt_calc_reserves(pag->pag_mount, pag->pag_agno,
+                               &ask, &used);
+               if (error)
+                       goto out;
+
                error = __xfs_ag_resv_init(pag, XFS_AG_RESV_AGFL, ask, used);
                if (error)
                        goto out;
index ca75dc9..effb64c 100644 (file)
@@ -52,10 +52,23 @@ STATIC int xfs_alloc_ag_vextent_size(xfs_alloc_arg_t *);
 STATIC int xfs_alloc_ag_vextent_small(xfs_alloc_arg_t *,
                xfs_btree_cur_t *, xfs_agblock_t *, xfs_extlen_t *, int *);
 
+unsigned int
+xfs_refc_block(
+       struct xfs_mount        *mp)
+{
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return XFS_RMAP_BLOCK(mp) + 1;
+       if (xfs_sb_version_hasfinobt(&mp->m_sb))
+               return XFS_FIBT_BLOCK(mp) + 1;
+       return XFS_IBT_BLOCK(mp) + 1;
+}
+
 xfs_extlen_t
 xfs_prealloc_blocks(
        struct xfs_mount        *mp)
 {
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               return xfs_refc_block(mp) + 1;
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                return XFS_RMAP_BLOCK(mp) + 1;
        if (xfs_sb_version_hasfinobt(&mp->m_sb))
@@ -115,6 +128,8 @@ xfs_alloc_ag_max_usable(
                blocks++;               /* finobt root block */
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                blocks++;               /* rmap root block */
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               blocks++;               /* refcount root block */
 
        return mp->m_sb.sb_agblocks - blocks;
 }
@@ -2321,6 +2336,9 @@ xfs_alloc_log_agf(
                offsetof(xfs_agf_t, agf_btreeblks),
                offsetof(xfs_agf_t, agf_uuid),
                offsetof(xfs_agf_t, agf_rmap_blocks),
+               offsetof(xfs_agf_t, agf_refcount_blocks),
+               offsetof(xfs_agf_t, agf_refcount_root),
+               offsetof(xfs_agf_t, agf_refcount_level),
                /* needed so that we don't log the whole rest of the structure: */
                offsetof(xfs_agf_t, agf_spare64),
                sizeof(xfs_agf_t)
@@ -2458,6 +2476,10 @@ xfs_agf_verify(
            be32_to_cpu(agf->agf_btreeblks) > be32_to_cpu(agf->agf_length))
                return false;
 
+       if (xfs_sb_version_hasreflink(&mp->m_sb) &&
+           be32_to_cpu(agf->agf_refcount_level) > XFS_BTREE_MAXLEVELS)
+               return false;
+
        return true;;
 
 }
@@ -2578,6 +2600,7 @@ xfs_alloc_read_agf(
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi]);
                pag->pagf_levels[XFS_BTNUM_RMAPi] =
                        be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]);
+               pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level);
                spin_lock_init(&pag->pagb_lock);
                pag->pagb_count = 0;
                pag->pagb_tree = RB_ROOT;
index 9d7f61d..c27344c 100644 (file)
@@ -48,6 +48,7 @@
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
 #include "xfs_ag_resv.h"
+#include "xfs_refcount.h"
 
 
 kmem_zone_t            *xfs_bmap_free_item_zone;
@@ -140,7 +141,8 @@ xfs_bmbt_lookup_ge(
  */
 static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
 {
-       return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
+       return whichfork != XFS_COW_FORK &&
+               XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS &&
                XFS_IFORK_NEXTENTS(ip, whichfork) >
                        XFS_IFORK_MAXEXT(ip, whichfork);
 }
@@ -150,7 +152,8 @@ static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork)
  */
 static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork)
 {
-       return XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
+       return whichfork != XFS_COW_FORK &&
+               XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE &&
                XFS_IFORK_NEXTENTS(ip, whichfork) <=
                        XFS_IFORK_MAXEXT(ip, whichfork);
 }
@@ -640,6 +643,7 @@ xfs_bmap_btree_to_extents(
 
        mp = ip->i_mount;
        ifp = XFS_IFORK_PTR(ip, whichfork);
+       ASSERT(whichfork != XFS_COW_FORK);
        ASSERT(ifp->if_flags & XFS_IFEXTENTS);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_BTREE);
        rblock = ifp->if_broot;
@@ -706,6 +710,7 @@ xfs_bmap_extents_to_btree(
        xfs_bmbt_ptr_t          *pp;            /* root block address pointer */
 
        mp = ip->i_mount;
+       ASSERT(whichfork != XFS_COW_FORK);
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_EXTENTS);
 
@@ -748,6 +753,7 @@ xfs_bmap_extents_to_btree(
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = XFS_INO_TO_FSB(mp, ip->i_ino);
        } else if (dfops->dop_low) {
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                args.fsbno = *firstblock;
        } else {
@@ -762,6 +768,21 @@ xfs_bmap_extents_to_btree(
                xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
                return error;
        }
+
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               dfops->dop_low = true;
+               goto try_another_ag;
+       }
        /*
         * Allocation can't fail, the space was reserved.
         */
@@ -837,6 +858,7 @@ xfs_bmap_local_to_extents_empty(
 {
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
 
+       ASSERT(whichfork != XFS_COW_FORK);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) == XFS_DINODE_FMT_LOCAL);
        ASSERT(ifp->if_bytes == 0);
        ASSERT(XFS_IFORK_NEXTENTS(ip, whichfork) == 0);
@@ -896,6 +918,7 @@ xfs_bmap_local_to_extents(
         * file currently fits in an inode.
         */
        if (*firstblock == NULLFSBLOCK) {
+try_another_ag:
                args.fsbno = XFS_INO_TO_FSB(args.mp, ip->i_ino);
                args.type = XFS_ALLOCTYPE_START_BNO;
        } else {
@@ -908,6 +931,19 @@ xfs_bmap_local_to_extents(
        if (error)
                goto done;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&ip->i_mount->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               goto try_another_ag;
+       }
        /* Can't fail, the space was reserved. */
        ASSERT(args.fsbno != NULLFSBLOCK);
        ASSERT(args.len == 1);
@@ -1670,7 +1706,8 @@ xfs_bmap_one_block(
  */
 STATIC int                             /* error */
 xfs_bmap_add_extent_delay_real(
-       struct xfs_bmalloca     *bma)
+       struct xfs_bmalloca     *bma,
+       int                     whichfork)
 {
        struct xfs_bmbt_irec    *new = &bma->got;
        int                     diff;   /* temp value */
@@ -1688,11 +1725,14 @@ xfs_bmap_add_extent_delay_real(
        xfs_filblks_t           temp=0; /* value for da_new calculations */
        xfs_filblks_t           temp2=0;/* value for da_new calculations */
        int                     tmp_rval;       /* partial logging flags */
-       int                     whichfork = XFS_DATA_FORK;
        struct xfs_mount        *mp;
+       xfs_extnum_t            *nextents;
 
        mp = bma->ip->i_mount;
        ifp = XFS_IFORK_PTR(bma->ip, whichfork);
+       ASSERT(whichfork != XFS_ATTR_FORK);
+       nextents = (whichfork == XFS_COW_FORK ? &bma->ip->i_cnextents :
+                                               &bma->ip->i_d.di_nextents);
 
        ASSERT(bma->idx >= 0);
        ASSERT(bma->idx <= ifp->if_bytes / sizeof(struct xfs_bmbt_rec));
@@ -1706,6 +1746,9 @@ xfs_bmap_add_extent_delay_real(
 #define        RIGHT           r[1]
 #define        PREV            r[2]
 
+       if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
+
        /*
         * Set up a bunch of variables to make the tests simpler.
         */
@@ -1792,7 +1835,7 @@ xfs_bmap_add_extent_delay_real(
                trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
 
                xfs_iext_remove(bma->ip, bma->idx + 1, 2, state);
-               bma->ip->i_d.di_nextents--;
+               (*nextents)--;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -1894,7 +1937,7 @@ xfs_bmap_add_extent_delay_real(
                xfs_bmbt_set_startblock(ep, new->br_startblock);
                trace_xfs_bmap_post_update(bma->ip, bma->idx, state, _THIS_IP_);
 
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -1964,7 +2007,7 @@ xfs_bmap_add_extent_delay_real(
                temp = PREV.br_blockcount - new->br_blockcount;
                xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx, 1, new, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2048,7 +2091,7 @@ xfs_bmap_add_extent_delay_real(
                trace_xfs_bmap_pre_update(bma->ip, bma->idx, state, _THIS_IP_);
                xfs_bmbt_set_blockcount(ep, temp);
                xfs_iext_insert(bma->ip, bma->idx + 1, 1, new, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2117,7 +2160,7 @@ xfs_bmap_add_extent_delay_real(
                RIGHT.br_blockcount = temp2;
                /* insert LEFT (r[0]) and RIGHT (r[1]) at the same time */
                xfs_iext_insert(bma->ip, bma->idx + 1, 2, &LEFT, state);
-               bma->ip->i_d.di_nextents++;
+               (*nextents)++;
                if (bma->cur == NULL)
                        rval = XFS_ILOG_CORE | XFS_ILOG_DEXT;
                else {
@@ -2215,7 +2258,8 @@ xfs_bmap_add_extent_delay_real(
 
        xfs_bmap_check_leaf_extents(bma->cur, bma->ip, whichfork);
 done:
-       bma->logflags |= rval;
+       if (whichfork != XFS_COW_FORK)
+               bma->logflags |= rval;
        return error;
 #undef LEFT
 #undef RIGHT
@@ -2759,6 +2803,7 @@ done:
 STATIC void
 xfs_bmap_add_extent_hole_delay(
        xfs_inode_t             *ip,    /* incore inode pointer */
+       int                     whichfork,
        xfs_extnum_t            *idx,   /* extent number to update/insert */
        xfs_bmbt_irec_t         *new)   /* new data to add to file extents */
 {
@@ -2770,8 +2815,10 @@ xfs_bmap_add_extent_hole_delay(
        int                     state;  /* state bits, accessed thru macros */
        xfs_filblks_t           temp=0; /* temp for indirect calculations */
 
-       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       ifp = XFS_IFORK_PTR(ip, whichfork);
        state = 0;
+       if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
        ASSERT(isnullstartblock(new->br_startblock));
 
        /*
@@ -2789,7 +2836,7 @@ xfs_bmap_add_extent_hole_delay(
         * Check and set flags if the current (right) segment exists.
         * If it doesn't exist, we're converting the hole at end-of-file.
         */
-       if (*idx < ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
+       if (*idx < ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) {
                state |= BMAP_RIGHT_VALID;
                xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *idx), &right);
 
@@ -2923,6 +2970,7 @@ xfs_bmap_add_extent_hole_real(
        ASSERT(!isnullstartblock(new->br_startblock));
        ASSERT(!bma->cur ||
               !(bma->cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL));
+       ASSERT(whichfork != XFS_COW_FORK);
 
        XFS_STATS_INC(mp, xs_add_exlist);
 
@@ -3648,7 +3696,9 @@ xfs_bmap_btalloc(
        else if (mp->m_dalign)
                stripe_align = mp->m_dalign;
 
-       if (xfs_alloc_is_userdata(ap->datatype))
+       if (ap->flags & XFS_BMAPI_COWFORK)
+               align = xfs_get_cowextsz_hint(ap->ip);
+       else if (xfs_alloc_is_userdata(ap->datatype))
                align = xfs_get_extsz_hint(ap->ip);
        if (unlikely(align)) {
                error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
@@ -3856,7 +3906,8 @@ xfs_bmap_btalloc(
                ASSERT(nullfb || fb_agno == args.agno ||
                       (ap->dfops->dop_low && fb_agno < args.agno));
                ap->length = args.len;
-               ap->ip->i_d.di_nblocks += args.len;
+               if (!(ap->flags & XFS_BMAPI_COWFORK))
+                       ap->ip->i_d.di_nblocks += args.len;
                xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
                if (ap->wasdel)
                        ap->ip->i_delayed_blks -= args.len;
@@ -3875,6 +3926,63 @@ xfs_bmap_btalloc(
        return 0;
 }
 
+/*
+ * For a remap operation, just "allocate" an extent at the address that the
+ * caller passed in, and ensure that the AGFL is the right size.  The caller
+ * will then map the "allocated" extent into the file somewhere.
+ */
+STATIC int
+xfs_bmap_remap_alloc(
+       struct xfs_bmalloca     *ap)
+{
+       struct xfs_trans        *tp = ap->tp;
+       struct xfs_mount        *mp = tp->t_mountp;
+       xfs_agblock_t           bno;
+       struct xfs_alloc_arg    args;
+       int                     error;
+
+       /*
+        * validate that the block number is legal - the enables us to detect
+        * and handle a silent filesystem corruption rather than crashing.
+        */
+       memset(&args, 0, sizeof(struct xfs_alloc_arg));
+       args.tp = ap->tp;
+       args.mp = ap->tp->t_mountp;
+       bno = *ap->firstblock;
+       args.agno = XFS_FSB_TO_AGNO(mp, bno);
+       args.agbno = XFS_FSB_TO_AGBNO(mp, bno);
+       if (args.agno >= mp->m_sb.sb_agcount ||
+           args.agbno >= mp->m_sb.sb_agblocks)
+               return -EFSCORRUPTED;
+
+       /* "Allocate" the extent from the range we passed in. */
+       trace_xfs_bmap_remap_alloc(ap->ip, *ap->firstblock, ap->length);
+       ap->blkno = bno;
+       ap->ip->i_d.di_nblocks += ap->length;
+       xfs_trans_log_inode(ap->tp, ap->ip, XFS_ILOG_CORE);
+
+       /* Fix the freelist, like a real allocator does. */
+       args.datatype = ap->datatype;
+       args.pag = xfs_perag_get(args.mp, args.agno);
+       ASSERT(args.pag);
+
+       /*
+        * The freelist fixing code will decline the allocation if
+        * the size and shape of the free space doesn't allow for
+        * allocating the extent and updating all the metadata that
+        * happens during an allocation.  We're remapping, not
+        * allocating, so skip that check by pretending to be freeing.
+        */
+       error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING);
+       if (error)
+               goto error0;
+error0:
+       xfs_perag_put(args.pag);
+       if (error)
+               trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_);
+       return error;
+}
+
 /*
  * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file.
  * It figures out where to ask the underlying allocator to put the new extent.
@@ -3883,6 +3991,8 @@ STATIC int
 xfs_bmap_alloc(
        struct xfs_bmalloca     *ap)    /* bmap alloc argument struct */
 {
+       if (ap->flags & XFS_BMAPI_REMAP)
+               return xfs_bmap_remap_alloc(ap);
        if (XFS_IS_REALTIME_INODE(ap->ip) &&
            xfs_alloc_is_userdata(ap->datatype))
                return xfs_bmap_rtalloc(ap);
@@ -4012,12 +4122,11 @@ xfs_bmapi_read(
        int                     error;
        int                     eof;
        int                     n = 0;
-       int                     whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(flags);
 
        ASSERT(*nmap >= 1);
        ASSERT(!(flags & ~(XFS_BMAPI_ATTRFORK|XFS_BMAPI_ENTIRE|
-                          XFS_BMAPI_IGSTATE)));
+                          XFS_BMAPI_IGSTATE|XFS_BMAPI_COWFORK)));
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED|XFS_ILOCK_EXCL));
 
        if (unlikely(XFS_TEST_ERROR(
@@ -4035,6 +4144,16 @@ xfs_bmapi_read(
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
 
+       /* No CoW fork?  Return a hole. */
+       if (whichfork == XFS_COW_FORK && !ifp) {
+               mval->br_startoff = bno;
+               mval->br_startblock = HOLESTARTBLOCK;
+               mval->br_blockcount = len;
+               mval->br_state = XFS_EXT_NORM;
+               *nmap = 1;
+               return 0;
+       }
+
        if (!(ifp->if_flags & XFS_IFEXTENTS)) {
                error = xfs_iread_extents(NULL, ip, whichfork);
                if (error)
@@ -4084,6 +4203,7 @@ xfs_bmapi_read(
 int
 xfs_bmapi_reserve_delalloc(
        struct xfs_inode        *ip,
+       int                     whichfork,
        xfs_fileoff_t           aoff,
        xfs_filblks_t           len,
        struct xfs_bmbt_irec    *got,
@@ -4092,7 +4212,7 @@ xfs_bmapi_reserve_delalloc(
        int                     eof)
 {
        struct xfs_mount        *mp = ip->i_mount;
-       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
        xfs_extlen_t            alen;
        xfs_extlen_t            indlen;
        char                    rt = XFS_IS_REALTIME_INODE(ip);
@@ -4104,7 +4224,10 @@ xfs_bmapi_reserve_delalloc(
                alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
 
        /* Figure out the extent size, adjust alen */
-       extsz = xfs_get_extsz_hint(ip);
+       if (whichfork == XFS_COW_FORK)
+               extsz = xfs_get_cowextsz_hint(ip);
+       else
+               extsz = xfs_get_extsz_hint(ip);
        if (extsz) {
                error = xfs_bmap_extsize_align(mp, got, prev, extsz, rt, eof,
                                               1, 0, &aoff, &alen);
@@ -4151,7 +4274,7 @@ xfs_bmapi_reserve_delalloc(
        got->br_startblock = nullstartblock(indlen);
        got->br_blockcount = alen;
        got->br_state = XFS_EXT_NORM;
-       xfs_bmap_add_extent_hole_delay(ip, lastx, got);
+       xfs_bmap_add_extent_hole_delay(ip, whichfork, lastx, got);
 
        /*
         * Update our extent pointer, given that xfs_bmap_add_extent_hole_delay
@@ -4182,8 +4305,7 @@ xfs_bmapi_allocate(
        struct xfs_bmalloca     *bma)
 {
        struct xfs_mount        *mp = bma->ip->i_mount;
-       int                     whichfork = (bma->flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(bma->flags);
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(bma->ip, whichfork);
        int                     tmp_logflags = 0;
        int                     error;
@@ -4278,7 +4400,7 @@ xfs_bmapi_allocate(
                bma->got.br_state = XFS_EXT_UNWRITTEN;
 
        if (bma->wasdel)
-               error = xfs_bmap_add_extent_delay_real(bma);
+               error = xfs_bmap_add_extent_delay_real(bma, whichfork);
        else
                error = xfs_bmap_add_extent_hole_real(bma, whichfork);
 
@@ -4308,8 +4430,7 @@ xfs_bmapi_convert_unwritten(
        xfs_filblks_t           len,
        int                     flags)
 {
-       int                     whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-                                               XFS_ATTR_FORK : XFS_DATA_FORK;
+       int                     whichfork = xfs_bmapi_whichfork(flags);
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(bma->ip, whichfork);
        int                     tmp_logflags = 0;
        int                     error;
@@ -4325,6 +4446,8 @@ xfs_bmapi_convert_unwritten(
                        (XFS_BMAPI_PREALLOC | XFS_BMAPI_CONVERT))
                return 0;
 
+       ASSERT(whichfork != XFS_COW_FORK);
+
        /*
         * Modify (by adding) the state flag, if writing.
         */
@@ -4431,8 +4554,7 @@ xfs_bmapi_write(
        orig_mval = mval;
        orig_nmap = *nmap;
 #endif
-       whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-               XFS_ATTR_FORK : XFS_DATA_FORK;
+       whichfork = xfs_bmapi_whichfork(flags);
 
        ASSERT(*nmap >= 1);
        ASSERT(*nmap <= XFS_BMAP_MAX_NMAP);
@@ -4441,6 +4563,11 @@ xfs_bmapi_write(
        ASSERT(len > 0);
        ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL);
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+       ASSERT(!(flags & XFS_BMAPI_REMAP) || whichfork == XFS_DATA_FORK);
+       ASSERT(!(flags & XFS_BMAPI_PREALLOC) || !(flags & XFS_BMAPI_REMAP));
+       ASSERT(!(flags & XFS_BMAPI_CONVERT) || !(flags & XFS_BMAPI_REMAP));
+       ASSERT(!(flags & XFS_BMAPI_PREALLOC) || whichfork != XFS_COW_FORK);
+       ASSERT(!(flags & XFS_BMAPI_CONVERT) || whichfork != XFS_COW_FORK);
 
        /* zeroing is for currently only for data extents, not metadata */
        ASSERT((flags & (XFS_BMAPI_METADATA | XFS_BMAPI_ZERO)) !=
@@ -4501,6 +4628,14 @@ xfs_bmapi_write(
                inhole = eof || bma.got.br_startoff > bno;
                wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
 
+               /*
+                * Make sure we only reflink into a hole.
+                */
+               if (flags & XFS_BMAPI_REMAP)
+                       ASSERT(inhole);
+               if (flags & XFS_BMAPI_COWFORK)
+                       ASSERT(!inhole);
+
                /*
                 * First, deal with the hole before the allocated space
                 * that we found, if any.
@@ -4531,6 +4666,17 @@ xfs_bmapi_write(
                                goto error0;
                        if (bma.blkno == NULLFSBLOCK)
                                break;
+
+                       /*
+                        * If this is a CoW allocation, record the data in
+                        * the refcount btree for orphan recovery.
+                        */
+                       if (whichfork == XFS_COW_FORK) {
+                               error = xfs_refcount_alloc_cow_extent(mp, dfops,
+                                               bma.blkno, bma.length);
+                               if (error)
+                                       goto error0;
+                       }
                }
 
                /* Deal with the allocated space we found.  */
@@ -4696,7 +4842,8 @@ xfs_bmap_del_extent(
        xfs_btree_cur_t         *cur,   /* if null, not a btree */
        xfs_bmbt_irec_t         *del,   /* data to remove from extents */
        int                     *logflagsp, /* inode logging flags */
-       int                     whichfork) /* data or attr fork */
+       int                     whichfork, /* data or attr fork */
+       int                     bflags) /* bmapi flags */
 {
        xfs_filblks_t           da_new; /* new delay-alloc indirect blocks */
        xfs_filblks_t           da_old; /* old delay-alloc indirect blocks */
@@ -4725,6 +4872,8 @@ xfs_bmap_del_extent(
 
        if (whichfork == XFS_ATTR_FORK)
                state |= BMAP_ATTRFORK;
+       else if (whichfork == XFS_COW_FORK)
+               state |= BMAP_COWFORK;
 
        ifp = XFS_IFORK_PTR(ip, whichfork);
        ASSERT((*idx >= 0) && (*idx < ifp->if_bytes /
@@ -4805,6 +4954,7 @@ xfs_bmap_del_extent(
                /*
                 * Matches the whole extent.  Delete the entry.
                 */
+               trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_);
                xfs_iext_remove(ip, *idx, 1,
                                whichfork == XFS_ATTR_FORK ? BMAP_ATTRFORK : 0);
                --*idx;
@@ -4988,9 +5138,16 @@ xfs_bmap_del_extent(
        /*
         * If we need to, add to list of extents to delete.
         */
-       if (do_fx)
-               xfs_bmap_add_free(mp, dfops, del->br_startblock,
-                               del->br_blockcount, NULL);
+       if (do_fx && !(bflags & XFS_BMAPI_REMAP)) {
+               if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK) {
+                       error = xfs_refcount_decrease_extent(mp, dfops, del);
+                       if (error)
+                               goto done;
+               } else
+                       xfs_bmap_add_free(mp, dfops, del->br_startblock,
+                                       del->br_blockcount, NULL);
+       }
+
        /*
         * Adjust inode # blocks in the file.
         */
@@ -4999,7 +5156,7 @@ xfs_bmap_del_extent(
        /*
         * Adjust quota data.
         */
-       if (qfield)
+       if (qfield && !(bflags & XFS_BMAPI_REMAP))
                xfs_trans_mod_dquot_byino(tp, ip, qfield, (long)-nblks);
 
        /*
@@ -5014,6 +5171,175 @@ done:
        return error;
 }
 
+/* Remove an extent from the CoW fork.  Similar to xfs_bmap_del_extent. */
+int
+xfs_bunmapi_cow(
+       struct xfs_inode                *ip,
+       struct xfs_bmbt_irec            *del)
+{
+       xfs_filblks_t                   da_new;
+       xfs_filblks_t                   da_old;
+       xfs_fsblock_t                   del_endblock = 0;
+       xfs_fileoff_t                   del_endoff;
+       int                             delay;
+       struct xfs_bmbt_rec_host        *ep;
+       int                             error;
+       struct xfs_bmbt_irec            got;
+       xfs_fileoff_t                   got_endoff;
+       struct xfs_ifork                *ifp;
+       struct xfs_mount                *mp;
+       xfs_filblks_t                   nblks;
+       struct xfs_bmbt_irec            new;
+       /* REFERENCED */
+       uint                            qfield;
+       xfs_filblks_t                   temp;
+       xfs_filblks_t                   temp2;
+       int                             state = BMAP_COWFORK;
+       int                             eof;
+       xfs_extnum_t                    eidx;
+
+       mp = ip->i_mount;
+       XFS_STATS_INC(mp, xs_del_exlist);
+
+       ep = xfs_bmap_search_extents(ip, del->br_startoff, XFS_COW_FORK, &eof,
+                       &eidx, &got, &new);
+
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); ifp = ifp;
+       ASSERT((eidx >= 0) && (eidx < ifp->if_bytes /
+               (uint)sizeof(xfs_bmbt_rec_t)));
+       ASSERT(del->br_blockcount > 0);
+       ASSERT(got.br_startoff <= del->br_startoff);
+       del_endoff = del->br_startoff + del->br_blockcount;
+       got_endoff = got.br_startoff + got.br_blockcount;
+       ASSERT(got_endoff >= del_endoff);
+       delay = isnullstartblock(got.br_startblock);
+       ASSERT(isnullstartblock(del->br_startblock) == delay);
+       qfield = 0;
+       error = 0;
+       /*
+        * If deleting a real allocation, must free up the disk space.
+        */
+       if (!delay) {
+               nblks = del->br_blockcount;
+               qfield = XFS_TRANS_DQ_BCOUNT;
+               /*
+                * Set up del_endblock and cur for later.
+                */
+               del_endblock = del->br_startblock + del->br_blockcount;
+               da_old = da_new = 0;
+       } else {
+               da_old = startblockval(got.br_startblock);
+               da_new = 0;
+               nblks = 0;
+       }
+       qfield = qfield;
+       nblks = nblks;
+
+       /*
+        * Set flag value to use in switch statement.
+        * Left-contig is 2, right-contig is 1.
+        */
+       switch (((got.br_startoff == del->br_startoff) << 1) |
+               (got_endoff == del_endoff)) {
+       case 3:
+               /*
+                * Matches the whole extent.  Delete the entry.
+                */
+               xfs_iext_remove(ip, eidx, 1, BMAP_COWFORK);
+               --eidx;
+               break;
+
+       case 2:
+               /*
+                * Deleting the first part of the extent.
+                */
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_startoff(ep, del_endoff);
+               temp = got.br_blockcount - del->br_blockcount;
+               xfs_bmbt_set_blockcount(ep, temp);
+               if (delay) {
+                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                               da_old);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+                       da_new = temp;
+                       break;
+               }
+               xfs_bmbt_set_startblock(ep, del_endblock);
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               break;
+
+       case 1:
+               /*
+                * Deleting the last part of the extent.
+                */
+               temp = got.br_blockcount - del->br_blockcount;
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(ep, temp);
+               if (delay) {
+                       temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp),
+                               da_old);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+                       da_new = temp;
+                       break;
+               }
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               break;
+
+       case 0:
+               /*
+                * Deleting the middle of the extent.
+                */
+               temp = del->br_startoff - got.br_startoff;
+               trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_);
+               xfs_bmbt_set_blockcount(ep, temp);
+               new.br_startoff = del_endoff;
+               temp2 = got_endoff - del_endoff;
+               new.br_blockcount = temp2;
+               new.br_state = got.br_state;
+               if (!delay) {
+                       new.br_startblock = del_endblock;
+               } else {
+                       temp = xfs_bmap_worst_indlen(ip, temp);
+                       xfs_bmbt_set_startblock(ep, nullstartblock((int)temp));
+                       temp2 = xfs_bmap_worst_indlen(ip, temp2);
+                       new.br_startblock = nullstartblock((int)temp2);
+                       da_new = temp + temp2;
+                       while (da_new > da_old) {
+                               if (temp) {
+                                       temp--;
+                                       da_new--;
+                                       xfs_bmbt_set_startblock(ep,
+                                               nullstartblock((int)temp));
+                               }
+                               if (da_new == da_old)
+                                       break;
+                               if (temp2) {
+                                       temp2--;
+                                       da_new--;
+                                       new.br_startblock =
+                                               nullstartblock((int)temp2);
+                               }
+                       }
+               }
+               trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_);
+               xfs_iext_insert(ip, eidx + 1, 1, &new, state);
+               ++eidx;
+               break;
+       }
+
+       /*
+        * Account for change in delayed indirect blocks.
+        * Nothing to do for disk quota accounting here.
+        */
+       ASSERT(da_old >= da_new);
+       if (da_old > da_new)
+               xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false);
+
+       return error;
+}
+
 /*
  * Unmap (remove) blocks from a file.
  * If nexts is nonzero then the number of extents to remove is limited to
@@ -5021,17 +5347,16 @@ done:
  * *done is set.
  */
 int                                            /* error */
-xfs_bunmapi(
+__xfs_bunmapi(
        xfs_trans_t             *tp,            /* transaction pointer */
        struct xfs_inode        *ip,            /* incore inode */
        xfs_fileoff_t           bno,            /* starting offset to unmap */
-       xfs_filblks_t           len,            /* length to unmap in file */
+       xfs_filblks_t           *rlen,          /* i/o: amount remaining */
        int                     flags,          /* misc flags */
        xfs_extnum_t            nexts,          /* number of extents max */
        xfs_fsblock_t           *firstblock,    /* first allocated block
                                                   controls a.g. for allocs */
-       struct xfs_defer_ops    *dfops,         /* i/o: list extents to free */
-       int                     *done)          /* set if not done yet */
+       struct xfs_defer_ops    *dfops)         /* i/o: deferred updates */
 {
        xfs_btree_cur_t         *cur;           /* bmap btree cursor */
        xfs_bmbt_irec_t         del;            /* extent being deleted */
@@ -5053,11 +5378,12 @@ xfs_bunmapi(
        int                     wasdel;         /* was a delayed alloc extent */
        int                     whichfork;      /* data or attribute fork */
        xfs_fsblock_t           sum;
+       xfs_filblks_t           len = *rlen;    /* length to unmap in file */
 
        trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
-       whichfork = (flags & XFS_BMAPI_ATTRFORK) ?
-               XFS_ATTR_FORK : XFS_DATA_FORK;
+       whichfork = xfs_bmapi_whichfork(flags);
+       ASSERT(whichfork != XFS_COW_FORK);
        ifp = XFS_IFORK_PTR(ip, whichfork);
        if (unlikely(
            XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS &&
@@ -5079,7 +5405,7 @@ xfs_bunmapi(
                return error;
        nextents = ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t);
        if (nextents == 0) {
-               *done = 1;
+               *rlen = 0;
                return 0;
        }
        XFS_STATS_INC(mp, xs_blk_unmap);
@@ -5324,7 +5650,7 @@ xfs_bunmapi(
                        cur->bc_private.b.flags &= ~XFS_BTCUR_BPRV_WASDEL;
 
                error = xfs_bmap_del_extent(ip, tp, &lastx, dfops, cur, &del,
-                               &tmp_logflags, whichfork);
+                               &tmp_logflags, whichfork, flags);
                logflags |= tmp_logflags;
                if (error)
                        goto error0;
@@ -5350,7 +5676,10 @@ nodelete:
                        extno++;
                }
        }
-       *done = bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0;
+       if (bno == (xfs_fileoff_t)-1 || bno < start || lastx < 0)
+               *rlen = 0;
+       else
+               *rlen = bno - start + 1;
 
        /*
         * Convert to a btree if necessary.
@@ -5406,6 +5735,27 @@ error0:
        return error;
 }
 
+/* Unmap a range of a file. */
+int
+xfs_bunmapi(
+       xfs_trans_t             *tp,
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           bno,
+       xfs_filblks_t           len,
+       int                     flags,
+       xfs_extnum_t            nexts,
+       xfs_fsblock_t           *firstblock,
+       struct xfs_defer_ops    *dfops,
+       int                     *done)
+{
+       int                     error;
+
+       error = __xfs_bunmapi(tp, ip, bno, &len, flags, nexts, firstblock,
+                       dfops);
+       *done = (len == 0);
+       return error;
+}
+
 /*
  * Determine whether an extent shift can be accomplished by a merge with the
  * extent that precedes the target hole of the shift.
@@ -5985,3 +6335,146 @@ out:
        xfs_trans_cancel(tp);
        return error;
 }
+
+/* Deferred mapping is only for real extents in the data fork. */
+static bool
+xfs_bmap_is_update_needed(
+       struct xfs_bmbt_irec    *bmap)
+{
+       return  bmap->br_startblock != HOLESTARTBLOCK &&
+               bmap->br_startblock != DELAYSTARTBLOCK;
+}
+
+/* Record a bmap intent. */
+static int
+__xfs_bmap_add(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_bmap_intent_type       type,
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       struct xfs_bmbt_irec            *bmap)
+{
+       int                             error;
+       struct xfs_bmap_intent          *bi;
+
+       trace_xfs_bmap_defer(mp,
+                       XFS_FSB_TO_AGNO(mp, bmap->br_startblock),
+                       type,
+                       XFS_FSB_TO_AGBNO(mp, bmap->br_startblock),
+                       ip->i_ino, whichfork,
+                       bmap->br_startoff,
+                       bmap->br_blockcount,
+                       bmap->br_state);
+
+       bi = kmem_alloc(sizeof(struct xfs_bmap_intent), KM_SLEEP | KM_NOFS);
+       INIT_LIST_HEAD(&bi->bi_list);
+       bi->bi_type = type;
+       bi->bi_owner = ip;
+       bi->bi_whichfork = whichfork;
+       bi->bi_bmap = *bmap;
+
+       error = xfs_defer_join(dfops, bi->bi_owner);
+       if (error) {
+               kmem_free(bi);
+               return error;
+       }
+
+       xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_BMAP, &bi->bi_list);
+       return 0;
+}
+
+/* Map an extent into a file. */
+int
+xfs_bmap_map_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_MAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/* Unmap an extent out of a file. */
+int
+xfs_bmap_unmap_extent(
+       struct xfs_mount        *mp,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *PREV)
+{
+       if (!xfs_bmap_is_update_needed(PREV))
+               return 0;
+
+       return __xfs_bmap_add(mp, dfops, XFS_BMAP_UNMAP, ip,
+                       XFS_DATA_FORK, PREV);
+}
+
+/*
+ * Process one of the deferred bmap operations.  We pass back the
+ * btree cursor to maintain our lock on the bmapbt between calls.
+ */
+int
+xfs_bmap_finish_one(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_inode                *ip,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state)
+{
+       struct xfs_bmbt_irec            bmap;
+       int                             nimaps = 1;
+       xfs_fsblock_t                   firstfsb;
+       int                             flags = XFS_BMAPI_REMAP;
+       int                             done;
+       int                             error = 0;
+
+       bmap.br_startblock = startblock;
+       bmap.br_startoff = startoff;
+       bmap.br_blockcount = blockcount;
+       bmap.br_state = state;
+
+       trace_xfs_bmap_deferred(tp->t_mountp,
+                       XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
+                       XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
+                       ip->i_ino, whichfork, startoff, blockcount, state);
+
+       if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
+               return -EFSCORRUPTED;
+       if (whichfork == XFS_ATTR_FORK)
+               flags |= XFS_BMAPI_ATTRFORK;
+
+       if (XFS_TEST_ERROR(false, tp->t_mountp,
+                       XFS_ERRTAG_BMAP_FINISH_ONE,
+                       XFS_RANDOM_BMAP_FINISH_ONE))
+               return -EIO;
+
+       switch (type) {
+       case XFS_BMAP_MAP:
+               firstfsb = bmap.br_startblock;
+               error = xfs_bmapi_write(tp, ip, bmap.br_startoff,
+                                       bmap.br_blockcount, flags, &firstfsb,
+                                       bmap.br_blockcount, &bmap, &nimaps,
+                                       dfops);
+               break;
+       case XFS_BMAP_UNMAP:
+               error = xfs_bunmapi(tp, ip, bmap.br_startoff,
+                               bmap.br_blockcount, flags, 1, &firstfsb,
+                               dfops, &done);
+               ASSERT(done);
+               break;
+       default:
+               ASSERT(0);
+               error = -EFSCORRUPTED;
+       }
+
+       return error;
+}
index 8395f6e..f97db71 100644 (file)
@@ -97,6 +97,19 @@ struct xfs_extent_free_item
  */
 #define XFS_BMAPI_ZERO         0x080
 
+/*
+ * Map the inode offset to the block given in ap->firstblock.  Primarily
+ * used for reflink.  The range must be in a hole, and this flag cannot be
+ * turned on with PREALLOC or CONVERT, and cannot be used on the attr fork.
+ *
+ * For bunmapi, this flag unmaps the range without adjusting quota, reducing
+ * refcount, or freeing the blocks.
+ */
+#define XFS_BMAPI_REMAP                0x100
+
+/* Map something in the CoW fork. */
+#define XFS_BMAPI_COWFORK      0x200
+
 #define XFS_BMAPI_FLAGS \
        { XFS_BMAPI_ENTIRE,     "ENTIRE" }, \
        { XFS_BMAPI_METADATA,   "METADATA" }, \
@@ -105,12 +118,24 @@ struct xfs_extent_free_item
        { XFS_BMAPI_IGSTATE,    "IGSTATE" }, \
        { XFS_BMAPI_CONTIG,     "CONTIG" }, \
        { XFS_BMAPI_CONVERT,    "CONVERT" }, \
-       { XFS_BMAPI_ZERO,       "ZERO" }
+       { XFS_BMAPI_ZERO,       "ZERO" }, \
+       { XFS_BMAPI_REMAP,      "REMAP" }, \
+       { XFS_BMAPI_COWFORK,    "COWFORK" }
 
 
 static inline int xfs_bmapi_aflag(int w)
 {
-       return (w == XFS_ATTR_FORK ? XFS_BMAPI_ATTRFORK : 0);
+       return (w == XFS_ATTR_FORK ? XFS_BMAPI_ATTRFORK :
+              (w == XFS_COW_FORK ? XFS_BMAPI_COWFORK : 0));
+}
+
+static inline int xfs_bmapi_whichfork(int bmapi_flags)
+{
+       if (bmapi_flags & XFS_BMAPI_COWFORK)
+               return XFS_COW_FORK;
+       else if (bmapi_flags & XFS_BMAPI_ATTRFORK)
+               return XFS_ATTR_FORK;
+       return XFS_DATA_FORK;
 }
 
 /*
@@ -131,13 +156,15 @@ static inline int xfs_bmapi_aflag(int w)
 #define BMAP_LEFT_VALID                (1 << 6)
 #define BMAP_RIGHT_VALID       (1 << 7)
 #define BMAP_ATTRFORK          (1 << 8)
+#define BMAP_COWFORK           (1 << 9)
 
 #define XFS_BMAP_EXT_FLAGS \
        { BMAP_LEFT_CONTIG,     "LC" }, \
        { BMAP_RIGHT_CONTIG,    "RC" }, \
        { BMAP_LEFT_FILLING,    "LF" }, \
        { BMAP_RIGHT_FILLING,   "RF" }, \
-       { BMAP_ATTRFORK,        "ATTR" }
+       { BMAP_ATTRFORK,        "ATTR" }, \
+       { BMAP_COWFORK,         "COW" }
 
 
 /*
@@ -186,10 +213,15 @@ int       xfs_bmapi_write(struct xfs_trans *tp, struct xfs_inode *ip,
                xfs_fsblock_t *firstblock, xfs_extlen_t total,
                struct xfs_bmbt_irec *mval, int *nmap,
                struct xfs_defer_ops *dfops);
+int    __xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
+               xfs_fileoff_t bno, xfs_filblks_t *rlen, int flags,
+               xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
+               struct xfs_defer_ops *dfops);
 int    xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip,
                xfs_fileoff_t bno, xfs_filblks_t len, int flags,
                xfs_extnum_t nexts, xfs_fsblock_t *firstblock,
                struct xfs_defer_ops *dfops, int *done);
+int    xfs_bunmapi_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *del);
 int    xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx,
                xfs_extnum_t num);
 uint   xfs_default_attroffset(struct xfs_inode *ip);
@@ -203,8 +235,31 @@ struct xfs_bmbt_rec_host *
        xfs_bmap_search_extents(struct xfs_inode *ip, xfs_fileoff_t bno,
                int fork, int *eofp, xfs_extnum_t *lastxp,
                struct xfs_bmbt_irec *gotp, struct xfs_bmbt_irec *prevp);
-int    xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, xfs_fileoff_t aoff,
-               xfs_filblks_t len, struct xfs_bmbt_irec *got,
-               struct xfs_bmbt_irec *prev, xfs_extnum_t *lastx, int eof);
+int    xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
+               xfs_fileoff_t aoff, xfs_filblks_t len,
+               struct xfs_bmbt_irec *got, struct xfs_bmbt_irec *prev,
+               xfs_extnum_t *lastx, int eof);
+
+enum xfs_bmap_intent_type {
+       XFS_BMAP_MAP = 1,
+       XFS_BMAP_UNMAP,
+};
+
+struct xfs_bmap_intent {
+       struct list_head                        bi_list;
+       enum xfs_bmap_intent_type               bi_type;
+       struct xfs_inode                        *bi_owner;
+       int                                     bi_whichfork;
+       struct xfs_bmbt_irec                    bi_bmap;
+};
+
+int    xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, enum xfs_bmap_intent_type type,
+               int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
+               xfs_filblks_t blockcount, xfs_exntst_t state);
+int    xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
+int    xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
+               struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
 
 #endif /* __XFS_BMAP_H__ */
index cd85274..8007d2b 100644 (file)
@@ -453,6 +453,7 @@ xfs_bmbt_alloc_block(
 
        if (args.fsbno == NULLFSBLOCK) {
                args.fsbno = be64_to_cpu(start->l);
+try_another_ag:
                args.type = XFS_ALLOCTYPE_START_BNO;
                /*
                 * Make sure there is sufficient room left in the AG to
@@ -482,6 +483,22 @@ xfs_bmbt_alloc_block(
        if (error)
                goto error0;
 
+       /*
+        * During a CoW operation, the allocation and bmbt updates occur in
+        * different transactions.  The mapping code tries to put new bmbt
+        * blocks near extents being mapped, but the only way to guarantee this
+        * is if the alloc and the mapping happen in a single transaction that
+        * has a block reservation.  That isn't the case here, so if we run out
+        * of space we'll try again with another AG.
+        */
+       if (xfs_sb_version_hasreflink(&cur->bc_mp->m_sb) &&
+           args.fsbno == NULLFSBLOCK &&
+           args.type == XFS_ALLOCTYPE_NEAR_BNO) {
+               cur->bc_private.b.dfops->dop_low = true;
+               args.fsbno = cur->bc_private.b.firstblock;
+               goto try_another_ag;
+       }
+
        if (args.fsbno == NULLFSBLOCK && args.minleft) {
                /*
                 * Could not find an AG with enough free space to satisfy
@@ -777,6 +794,7 @@ xfs_bmbt_init_cursor(
 {
        struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, whichfork);
        struct xfs_btree_cur    *cur;
+       ASSERT(whichfork != XFS_COW_FORK);
 
        cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_SLEEP);
 
index aa1752f..5c8e6f2 100644 (file)
@@ -45,9 +45,10 @@ kmem_zone_t  *xfs_btree_cur_zone;
  */
 static const __uint32_t xfs_magics[2][XFS_BTNUM_MAX] = {
        { XFS_ABTB_MAGIC, XFS_ABTC_MAGIC, 0, XFS_BMAP_MAGIC, XFS_IBT_MAGIC,
-         XFS_FIBT_MAGIC },
+         XFS_FIBT_MAGIC, 0 },
        { XFS_ABTB_CRC_MAGIC, XFS_ABTC_CRC_MAGIC, XFS_RMAP_CRC_MAGIC,
-         XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC, XFS_FIBT_CRC_MAGIC }
+         XFS_BMAP_CRC_MAGIC, XFS_IBT_CRC_MAGIC, XFS_FIBT_CRC_MAGIC,
+         XFS_REFC_CRC_MAGIC }
 };
 #define xfs_btree_magic(cur) \
        xfs_magics[!!((cur)->bc_flags & XFS_BTREE_CRC_BLOCKS)][cur->bc_btnum]
@@ -1216,6 +1217,9 @@ xfs_btree_set_refs(
        case XFS_BTNUM_RMAP:
                xfs_buf_set_ref(bp, XFS_RMAP_BTREE_REF);
                break;
+       case XFS_BTNUM_REFC:
+               xfs_buf_set_ref(bp, XFS_REFC_BTREE_REF);
+               break;
        default:
                ASSERT(0);
        }
index 3f8556a..c2b01d1 100644 (file)
@@ -49,6 +49,7 @@ union xfs_btree_key {
        struct xfs_inobt_key            inobt;
        struct xfs_rmap_key             rmap;
        struct xfs_rmap_key             __rmap_bigkey[2];
+       struct xfs_refcount_key         refc;
 };
 
 union xfs_btree_rec {
@@ -57,6 +58,7 @@ union xfs_btree_rec {
        struct xfs_alloc_rec            alloc;
        struct xfs_inobt_rec            inobt;
        struct xfs_rmap_rec             rmap;
+       struct xfs_refcount_rec         refc;
 };
 
 /*
@@ -72,6 +74,7 @@ union xfs_btree_rec {
 #define        XFS_BTNUM_INO   ((xfs_btnum_t)XFS_BTNUM_INOi)
 #define        XFS_BTNUM_FINO  ((xfs_btnum_t)XFS_BTNUM_FINOi)
 #define        XFS_BTNUM_RMAP  ((xfs_btnum_t)XFS_BTNUM_RMAPi)
+#define        XFS_BTNUM_REFC  ((xfs_btnum_t)XFS_BTNUM_REFCi)
 
 /*
  * For logging record fields.
@@ -105,6 +108,7 @@ do {    \
        case XFS_BTNUM_INO: __XFS_BTREE_STATS_INC(__mp, ibt, stat); break; \
        case XFS_BTNUM_FINO: __XFS_BTREE_STATS_INC(__mp, fibt, stat); break; \
        case XFS_BTNUM_RMAP: __XFS_BTREE_STATS_INC(__mp, rmap, stat); break; \
+       case XFS_BTNUM_REFC: __XFS_BTREE_STATS_INC(__mp, refcbt, stat); break; \
        case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break;       \
        }       \
 } while (0)
@@ -127,6 +131,8 @@ do {    \
                __XFS_BTREE_STATS_ADD(__mp, fibt, stat, val); break; \
        case XFS_BTNUM_RMAP:    \
                __XFS_BTREE_STATS_ADD(__mp, rmap, stat, val); break; \
+       case XFS_BTNUM_REFC:    \
+               __XFS_BTREE_STATS_ADD(__mp, refcbt, stat, val); break; \
        case XFS_BTNUM_MAX: ASSERT(0); /* fucking gcc */ ; break; \
        }       \
 } while (0)
@@ -217,6 +223,15 @@ union xfs_btree_irec {
        struct xfs_bmbt_irec            b;
        struct xfs_inobt_rec_incore     i;
        struct xfs_rmap_irec            r;
+       struct xfs_refcount_irec        rc;
+};
+
+/* Per-AG btree private information. */
+union xfs_btree_cur_private {
+       struct {
+               unsigned long   nr_ops;         /* # record updates */
+               int             shape_changes;  /* # of extent splits */
+       } refc;
 };
 
 /*
@@ -243,6 +258,7 @@ typedef struct xfs_btree_cur
                        struct xfs_buf  *agbp;  /* agf/agi buffer pointer */
                        struct xfs_defer_ops *dfops;    /* deferred updates */
                        xfs_agnumber_t  agno;   /* ag number */
+                       union xfs_btree_cur_private     priv;
                } a;
                struct {                        /* needed for BMAP */
                        struct xfs_inode *ip;   /* pointer to our inode */
index e96533d..f6e93ef 100644 (file)
@@ -51,6 +51,8 @@ struct xfs_defer_pending {
  * find all the space it needs.
  */
 enum xfs_defer_ops_type {
+       XFS_DEFER_OPS_TYPE_BMAP,
+       XFS_DEFER_OPS_TYPE_REFCOUNT,
        XFS_DEFER_OPS_TYPE_RMAP,
        XFS_DEFER_OPS_TYPE_FREE,
        XFS_DEFER_OPS_TYPE_MAX,
index 270fb5c..f6547fc 100644 (file)
@@ -456,9 +456,11 @@ xfs_sb_has_compat_feature(
 
 #define XFS_SB_FEAT_RO_COMPAT_FINOBT   (1 << 0)                /* free inode btree */
 #define XFS_SB_FEAT_RO_COMPAT_RMAPBT   (1 << 1)                /* reverse map btree */
+#define XFS_SB_FEAT_RO_COMPAT_REFLINK  (1 << 2)                /* reflinked files */
 #define XFS_SB_FEAT_RO_COMPAT_ALL \
                (XFS_SB_FEAT_RO_COMPAT_FINOBT | \
-                XFS_SB_FEAT_RO_COMPAT_RMAPBT)
+                XFS_SB_FEAT_RO_COMPAT_RMAPBT | \
+                XFS_SB_FEAT_RO_COMPAT_REFLINK)
 #define XFS_SB_FEAT_RO_COMPAT_UNKNOWN  ~XFS_SB_FEAT_RO_COMPAT_ALL
 static inline bool
 xfs_sb_has_ro_compat_feature(
@@ -546,6 +548,12 @@ static inline bool xfs_sb_version_hasrmapbt(struct xfs_sb *sbp)
                (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_RMAPBT);
 }
 
+static inline bool xfs_sb_version_hasreflink(struct xfs_sb *sbp)
+{
+       return XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5 &&
+               (sbp->sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_REFLINK);
+}
+
 /*
  * end of superblock version macros
  */
@@ -641,14 +649,17 @@ typedef struct xfs_agf {
        uuid_t          agf_uuid;       /* uuid of filesystem */
 
        __be32          agf_rmap_blocks;        /* rmapbt blocks used */
-       __be32          agf_padding;            /* padding */
+       __be32          agf_refcount_blocks;    /* refcountbt blocks used */
+
+       __be32          agf_refcount_root;      /* refcount tree root block */
+       __be32          agf_refcount_level;     /* refcount btree levels */
 
        /*
         * reserve some contiguous space for future logged fields before we add
         * the unlogged fields. This makes the range logging via flags and
         * structure offsets much simpler.
         */
-       __be64          agf_spare64[15];
+       __be64          agf_spare64[14];
 
        /* unlogged fields, written during buffer writeback. */
        __be64          agf_lsn;        /* last write sequence */
@@ -674,8 +685,11 @@ typedef struct xfs_agf {
 #define        XFS_AGF_BTREEBLKS       0x00000800
 #define        XFS_AGF_UUID            0x00001000
 #define        XFS_AGF_RMAP_BLOCKS     0x00002000
-#define        XFS_AGF_SPARE64         0x00004000
-#define        XFS_AGF_NUM_BITS        15
+#define        XFS_AGF_REFCOUNT_BLOCKS 0x00004000
+#define        XFS_AGF_REFCOUNT_ROOT   0x00008000
+#define        XFS_AGF_REFCOUNT_LEVEL  0x00010000
+#define        XFS_AGF_SPARE64         0x00020000
+#define        XFS_AGF_NUM_BITS        18
 #define        XFS_AGF_ALL_BITS        ((1 << XFS_AGF_NUM_BITS) - 1)
 
 #define XFS_AGF_FLAGS \
@@ -693,6 +707,9 @@ typedef struct xfs_agf {
        { XFS_AGF_BTREEBLKS,    "BTREEBLKS" }, \
        { XFS_AGF_UUID,         "UUID" }, \
        { XFS_AGF_RMAP_BLOCKS,  "RMAP_BLOCKS" }, \
+       { XFS_AGF_REFCOUNT_BLOCKS,      "REFCOUNT_BLOCKS" }, \
+       { XFS_AGF_REFCOUNT_ROOT,        "REFCOUNT_ROOT" }, \
+       { XFS_AGF_REFCOUNT_LEVEL,       "REFCOUNT_LEVEL" }, \
        { XFS_AGF_SPARE64,      "SPARE64" }
 
 /* disk block (xfs_daddr_t) in the AG */
@@ -885,7 +902,8 @@ typedef struct xfs_dinode {
        __be64          di_changecount; /* number of attribute changes */
        __be64          di_lsn;         /* flush sequence */
        __be64          di_flags2;      /* more random flags */
-       __u8            di_pad2[16];    /* more padding for future expansion */
+       __be32          di_cowextsize;  /* basic cow extent size for file */
+       __u8            di_pad2[12];    /* more padding for future expansion */
 
        /* fields only written to during inode creation */
        xfs_timestamp_t di_crtime;      /* time created */
@@ -1041,9 +1059,14 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)
  * 16 bits of the XFS_XFLAG_s range.
  */
 #define XFS_DIFLAG2_DAX_BIT    0       /* use DAX for this inode */
+#define XFS_DIFLAG2_REFLINK_BIT        1       /* file's blocks may be shared */
+#define XFS_DIFLAG2_COWEXTSIZE_BIT   2  /* copy on write extent size hint */
 #define XFS_DIFLAG2_DAX                (1 << XFS_DIFLAG2_DAX_BIT)
+#define XFS_DIFLAG2_REFLINK     (1 << XFS_DIFLAG2_REFLINK_BIT)
+#define XFS_DIFLAG2_COWEXTSIZE  (1 << XFS_DIFLAG2_COWEXTSIZE_BIT)
 
-#define XFS_DIFLAG2_ANY                (XFS_DIFLAG2_DAX)
+#define XFS_DIFLAG2_ANY \
+       (XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)
 
 /*
  * Inode number format:
@@ -1353,7 +1376,9 @@ struct xfs_owner_info {
 #define XFS_RMAP_OWN_AG                (-5ULL) /* AG freespace btree blocks */
 #define XFS_RMAP_OWN_INOBT     (-6ULL) /* Inode btree blocks */
 #define XFS_RMAP_OWN_INODES    (-7ULL) /* Inode chunk */
-#define XFS_RMAP_OWN_MIN       (-8ULL) /* guard */
+#define XFS_RMAP_OWN_REFC      (-8ULL) /* refcount tree */
+#define XFS_RMAP_OWN_COW       (-9ULL) /* cow allocations */
+#define XFS_RMAP_OWN_MIN       (-10ULL) /* guard */
 
 #define XFS_RMAP_NON_INODE_OWNER(owner)        (!!((owner) & (1ULL << 63)))
 
@@ -1433,6 +1458,62 @@ typedef __be32 xfs_rmap_ptr_t;
         XFS_FIBT_BLOCK(mp) + 1 : \
         XFS_IBT_BLOCK(mp) + 1)
 
+/*
+ * Reference Count Btree format definitions
+ *
+ */
+#define        XFS_REFC_CRC_MAGIC      0x52334643      /* 'R3FC' */
+
+unsigned int xfs_refc_block(struct xfs_mount *mp);
+
+/*
+ * Data record/key structure
+ *
+ * Each record associates a range of physical blocks (starting at
+ * rc_startblock and ending rc_blockcount blocks later) with a reference
+ * count (rc_refcount).  Extents that are being used to stage a copy on
+ * write (CoW) operation are recorded in the refcount btree with a
+ * refcount of 1.  All other records must have a refcount > 1 and must
+ * track an extent mapped only by file data forks.
+ *
+ * Extents with a single owner (attributes, metadata, non-shared file
+ * data) are not tracked here.  Free space is also not tracked here.
+ * This is consistent with pre-reflink XFS.
+ */
+
+/*
+ * Extents that are being used to stage a copy on write are stored
+ * in the refcount btree with a refcount of 1 and the upper bit set
+ * on the startblock.  This speeds up mount time deletion of stale
+ * staging extents because they're all at the right side of the tree.
+ */
+#define XFS_REFC_COW_START             ((xfs_agblock_t)(1U << 31))
+#define REFCNTBT_COWFLAG_BITLEN                1
+#define REFCNTBT_AGBLOCK_BITLEN                31
+
+struct xfs_refcount_rec {
+       __be32          rc_startblock;  /* starting block number */
+       __be32          rc_blockcount;  /* count of blocks */
+       __be32          rc_refcount;    /* number of inodes linked here */
+};
+
+struct xfs_refcount_key {
+       __be32          rc_startblock;  /* starting block number */
+};
+
+struct xfs_refcount_irec {
+       xfs_agblock_t   rc_startblock;  /* starting block number */
+       xfs_extlen_t    rc_blockcount;  /* count of free blocks */
+       xfs_nlink_t     rc_refcount;    /* number of inodes linked here */
+};
+
+#define MAXREFCOUNT    ((xfs_nlink_t)~0U)
+#define MAXREFCEXTLEN  ((xfs_extlen_t)~0U)
+
+/* btree pointer type */
+typedef __be32 xfs_refcount_ptr_t;
+
+
 /*
  * BMAP Btree format definitions
  *
index 7945505..b72dc82 100644 (file)
@@ -81,14 +81,16 @@ struct getbmapx {
 #define BMV_IF_PREALLOC                0x4     /* rtn status BMV_OF_PREALLOC if req */
 #define BMV_IF_DELALLOC                0x8     /* rtn status BMV_OF_DELALLOC if req */
 #define BMV_IF_NO_HOLES                0x10    /* Do not return holes */
+#define BMV_IF_COWFORK         0x20    /* return CoW fork rather than data */
 #define BMV_IF_VALID   \
        (BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC|  \
-        BMV_IF_DELALLOC|BMV_IF_NO_HOLES)
+        BMV_IF_DELALLOC|BMV_IF_NO_HOLES|BMV_IF_COWFORK)
 
 /*     bmv_oflags values - returned for each non-header segment */
 #define BMV_OF_PREALLOC                0x1     /* segment = unwritten pre-allocation */
 #define BMV_OF_DELALLOC                0x2     /* segment = delayed allocation */
 #define BMV_OF_LAST            0x4     /* segment is the last in the file */
+#define BMV_OF_SHARED          0x8     /* segment shared with another file */
 
 /*
  * Structure for XFS_IOC_FSSETDM.
@@ -206,7 +208,8 @@ typedef struct xfs_fsop_resblks {
 #define XFS_FSOP_GEOM_FLAGS_FTYPE      0x10000 /* inode directory types */
 #define XFS_FSOP_GEOM_FLAGS_FINOBT     0x20000 /* free inode btree */
 #define XFS_FSOP_GEOM_FLAGS_SPINODES   0x40000 /* sparse inode chunks  */
-#define XFS_FSOP_GEOM_FLAGS_RMAPBT     0x80000 /* Reverse mapping btree */
+#define XFS_FSOP_GEOM_FLAGS_RMAPBT     0x80000 /* reverse mapping btree */
+#define XFS_FSOP_GEOM_FLAGS_REFLINK    0x100000 /* files can share blocks */
 
 /*
  * Minimum and maximum sizes need for growth checks.
@@ -275,7 +278,8 @@ typedef struct xfs_bstat {
 #define        bs_projid       bs_projid_lo    /* (previously just bs_projid)  */
        __u16           bs_forkoff;     /* inode fork offset in bytes   */
        __u16           bs_projid_hi;   /* higher part of project id    */
-       unsigned char   bs_pad[10];     /* pad space, unused            */
+       unsigned char   bs_pad[6];      /* pad space, unused            */
+       __u32           bs_cowextsize;  /* cow extent size              */
        __u32           bs_dmevmask;    /* DMIG event mask              */
        __u16           bs_dmstate;     /* DMIG state info              */
        __u16           bs_aextents;    /* attribute number of extents  */
index 4b9769e..8de9a3a 100644 (file)
@@ -256,6 +256,7 @@ xfs_inode_from_disk(
                to->di_crtime.t_sec = be32_to_cpu(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = be32_to_cpu(from->di_crtime.t_nsec);
                to->di_flags2 = be64_to_cpu(from->di_flags2);
+               to->di_cowextsize = be32_to_cpu(from->di_cowextsize);
        }
 }
 
@@ -305,7 +306,7 @@ xfs_inode_to_disk(
                to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
                to->di_flags2 = cpu_to_be64(from->di_flags2);
-
+               to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
                to->di_ino = cpu_to_be64(ip->i_ino);
                to->di_lsn = cpu_to_be64(lsn);
                memset(to->di_pad2, 0, sizeof(to->di_pad2));
@@ -357,6 +358,7 @@ xfs_log_dinode_to_disk(
                to->di_crtime.t_sec = cpu_to_be32(from->di_crtime.t_sec);
                to->di_crtime.t_nsec = cpu_to_be32(from->di_crtime.t_nsec);
                to->di_flags2 = cpu_to_be64(from->di_flags2);
+               to->di_cowextsize = cpu_to_be32(from->di_cowextsize);
                to->di_ino = cpu_to_be64(from->di_ino);
                to->di_lsn = cpu_to_be64(from->di_lsn);
                memcpy(to->di_pad2, from->di_pad2, sizeof(to->di_pad2));
@@ -373,6 +375,9 @@ xfs_dinode_verify(
        struct xfs_inode        *ip,
        struct xfs_dinode       *dip)
 {
+       uint16_t                flags;
+       uint64_t                flags2;
+
        if (dip->di_magic != cpu_to_be16(XFS_DINODE_MAGIC))
                return false;
 
@@ -389,6 +394,23 @@ xfs_dinode_verify(
                return false;
        if (!uuid_equal(&dip->di_uuid, &mp->m_sb.sb_meta_uuid))
                return false;
+
+       flags = be16_to_cpu(dip->di_flags);
+       flags2 = be64_to_cpu(dip->di_flags2);
+
+       /* don't allow reflink/cowextsize if we don't have reflink */
+       if ((flags2 & (XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)) &&
+            !xfs_sb_version_hasreflink(&mp->m_sb))
+               return false;
+
+       /* don't let reflink and realtime mix */
+       if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags & XFS_DIFLAG_REALTIME))
+               return false;
+
+       /* don't let reflink and dax mix */
+       if ((flags2 & XFS_DIFLAG2_REFLINK) && (flags2 & XFS_DIFLAG2_DAX))
+               return false;
+
        return true;
 }
 
index 7c4dd32..62d9d46 100644 (file)
@@ -47,6 +47,7 @@ struct xfs_icdinode {
        __uint16_t      di_flags;       /* random flags, XFS_DIFLAG_... */
 
        __uint64_t      di_flags2;      /* more random flags */
+       __uint32_t      di_cowextsize;  /* basic cow extent size for file */
 
        xfs_ictimestamp_t di_crtime;    /* time created */
 };
index bbcc8c7..5dd56d3 100644 (file)
@@ -121,6 +121,26 @@ xfs_iformat_fork(
                return -EFSCORRUPTED;
        }
 
+       if (unlikely(xfs_is_reflink_inode(ip) &&
+           (VFS_I(ip)->i_mode & S_IFMT) != S_IFREG)) {
+               xfs_warn(ip->i_mount,
+                       "corrupt dinode %llu, wrong file type for reflink.",
+                       ip->i_ino);
+               XFS_CORRUPTION_ERROR("xfs_iformat(reflink)",
+                                    XFS_ERRLEVEL_LOW, ip->i_mount, dip);
+               return -EFSCORRUPTED;
+       }
+
+       if (unlikely(xfs_is_reflink_inode(ip) &&
+           (ip->i_d.di_flags & XFS_DIFLAG_REALTIME))) {
+               xfs_warn(ip->i_mount,
+                       "corrupt dinode %llu, has reflink+realtime flag set.",
+                       ip->i_ino);
+               XFS_CORRUPTION_ERROR("xfs_iformat(reflink)",
+                                    XFS_ERRLEVEL_LOW, ip->i_mount, dip);
+               return -EFSCORRUPTED;
+       }
+
        switch (VFS_I(ip)->i_mode & S_IFMT) {
        case S_IFIFO:
        case S_IFCHR:
@@ -186,9 +206,14 @@ xfs_iformat_fork(
                XFS_ERROR_REPORT("xfs_iformat(7)", XFS_ERRLEVEL_LOW, ip->i_mount);
                return -EFSCORRUPTED;
        }
-       if (error) {
+       if (error)
                return error;
+
+       if (xfs_is_reflink_inode(ip)) {
+               ASSERT(ip->i_cowfp == NULL);
+               xfs_ifork_init_cow(ip);
        }
+
        if (!XFS_DFORK_Q(dip))
                return 0;
 
@@ -208,7 +233,8 @@ xfs_iformat_fork(
                        XFS_CORRUPTION_ERROR("xfs_iformat(8)",
                                             XFS_ERRLEVEL_LOW,
                                             ip->i_mount, dip);
-                       return -EFSCORRUPTED;
+                       error = -EFSCORRUPTED;
+                       break;
                }
 
                error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, size);
@@ -226,6 +252,9 @@ xfs_iformat_fork(
        if (error) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+               if (ip->i_cowfp)
+                       kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
                xfs_idestroy_fork(ip, XFS_DATA_FORK);
        }
        return error;
@@ -740,6 +769,9 @@ xfs_idestroy_fork(
        if (whichfork == XFS_ATTR_FORK) {
                kmem_zone_free(xfs_ifork_zone, ip->i_afp);
                ip->i_afp = NULL;
+       } else if (whichfork == XFS_COW_FORK) {
+               kmem_zone_free(xfs_ifork_zone, ip->i_cowfp);
+               ip->i_cowfp = NULL;
        }
 }
 
@@ -927,6 +959,19 @@ xfs_iext_get_ext(
        }
 }
 
+/* Convert bmap state flags to an inode fork. */
+struct xfs_ifork *
+xfs_iext_state_to_fork(
+       struct xfs_inode        *ip,
+       int                     state)
+{
+       if (state & BMAP_COWFORK)
+               return ip->i_cowfp;
+       else if (state & BMAP_ATTRFORK)
+               return ip->i_afp;
+       return &ip->i_df;
+}
+
 /*
  * Insert new item(s) into the extent records for incore inode
  * fork 'ifp'.  'count' new items are inserted at index 'idx'.
@@ -939,7 +984,7 @@ xfs_iext_insert(
        xfs_bmbt_irec_t *new,           /* items to insert */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    i;              /* extent record index */
 
        trace_xfs_iext_insert(ip, idx, new, state, _RET_IP_);
@@ -1189,7 +1234,7 @@ xfs_iext_remove(
        int             ext_diff,       /* number of extents to remove */
        int             state)          /* type of extent conversion */
 {
-       xfs_ifork_t     *ifp = (state & BMAP_ATTRFORK) ? ip->i_afp : &ip->i_df;
+       xfs_ifork_t     *ifp = xfs_iext_state_to_fork(ip, state);
        xfs_extnum_t    nextents;       /* number of extents in file */
        int             new_size;       /* size of extents after removal */
 
@@ -1934,3 +1979,20 @@ xfs_iext_irec_update_extoffs(
                ifp->if_u1.if_ext_irec[i].er_extoff += ext_diff;
        }
 }
+
+/*
+ * Initialize an inode's copy-on-write fork.
+ */
+void
+xfs_ifork_init_cow(
+       struct xfs_inode        *ip)
+{
+       if (ip->i_cowfp)
+               return;
+
+       ip->i_cowfp = kmem_zone_zalloc(xfs_ifork_zone,
+                                      KM_SLEEP | KM_NOFS);
+       ip->i_cowfp->if_flags = XFS_IFEXTENTS;
+       ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
+       ip->i_cnextents = 0;
+}
index f95e072..c9476f5 100644 (file)
@@ -92,7 +92,9 @@ typedef struct xfs_ifork {
 #define XFS_IFORK_PTR(ip,w)            \
        ((w) == XFS_DATA_FORK ? \
                &(ip)->i_df : \
-               (ip)->i_afp)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_afp : \
+                       (ip)->i_cowfp))
 #define XFS_IFORK_DSIZE(ip) \
        (XFS_IFORK_Q(ip) ? \
                XFS_IFORK_BOFF(ip) : \
@@ -105,26 +107,38 @@ typedef struct xfs_ifork {
 #define XFS_IFORK_SIZE(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                XFS_IFORK_DSIZE(ip) : \
-               XFS_IFORK_ASIZE(ip))
+               ((w) == XFS_ATTR_FORK ? \
+                       XFS_IFORK_ASIZE(ip) : \
+                       0))
 #define XFS_IFORK_FORMAT(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                (ip)->i_d.di_format : \
-               (ip)->i_d.di_aformat)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_d.di_aformat : \
+                       (ip)->i_cformat))
 #define XFS_IFORK_FMT_SET(ip,w,n) \
        ((w) == XFS_DATA_FORK ? \
                ((ip)->i_d.di_format = (n)) : \
-               ((ip)->i_d.di_aformat = (n)))
+               ((w) == XFS_ATTR_FORK ? \
+                       ((ip)->i_d.di_aformat = (n)) : \
+                       ((ip)->i_cformat = (n))))
 #define XFS_IFORK_NEXTENTS(ip,w) \
        ((w) == XFS_DATA_FORK ? \
                (ip)->i_d.di_nextents : \
-               (ip)->i_d.di_anextents)
+               ((w) == XFS_ATTR_FORK ? \
+                       (ip)->i_d.di_anextents : \
+                       (ip)->i_cnextents))
 #define XFS_IFORK_NEXT_SET(ip,w,n) \
        ((w) == XFS_DATA_FORK ? \
                ((ip)->i_d.di_nextents = (n)) : \
-               ((ip)->i_d.di_anextents = (n)))
+               ((w) == XFS_ATTR_FORK ? \
+                       ((ip)->i_d.di_anextents = (n)) : \
+                       ((ip)->i_cnextents = (n))))
 #define XFS_IFORK_MAXEXT(ip, w) \
        (XFS_IFORK_SIZE(ip, w) / sizeof(xfs_bmbt_rec_t))
 
+struct xfs_ifork *xfs_iext_state_to_fork(struct xfs_inode *ip, int state);
+
 int            xfs_iformat_fork(struct xfs_inode *, struct xfs_dinode *);
 void           xfs_iflush_fork(struct xfs_inode *, struct xfs_dinode *,
                                struct xfs_inode_log_item *, int);
@@ -169,4 +183,6 @@ void                xfs_iext_irec_update_extoffs(struct xfs_ifork *, int, int);
 
 extern struct kmem_zone        *xfs_ifork_zone;
 
+extern void xfs_ifork_init_cow(struct xfs_inode *ip);
+
 #endif /* __XFS_INODE_FORK_H__ */
index fc5eef8..083cdd6 100644 (file)
@@ -112,7 +112,11 @@ static inline uint xlog_get_cycle(char *ptr)
 #define XLOG_REG_TYPE_ICREATE          20
 #define XLOG_REG_TYPE_RUI_FORMAT       21
 #define XLOG_REG_TYPE_RUD_FORMAT       22
-#define XLOG_REG_TYPE_MAX              22
+#define XLOG_REG_TYPE_CUI_FORMAT       23
+#define XLOG_REG_TYPE_CUD_FORMAT       24
+#define XLOG_REG_TYPE_BUI_FORMAT       25
+#define XLOG_REG_TYPE_BUD_FORMAT       26
+#define XLOG_REG_TYPE_MAX              26
 
 /*
  * Flags to log operation header
@@ -231,6 +235,10 @@ typedef struct xfs_trans_header {
 #define        XFS_LI_ICREATE          0x123f
 #define        XFS_LI_RUI              0x1240  /* rmap update intent */
 #define        XFS_LI_RUD              0x1241
+#define        XFS_LI_CUI              0x1242  /* refcount update intent */
+#define        XFS_LI_CUD              0x1243
+#define        XFS_LI_BUI              0x1244  /* bmbt update intent */
+#define        XFS_LI_BUD              0x1245
 
 #define XFS_LI_TYPE_DESC \
        { XFS_LI_EFI,           "XFS_LI_EFI" }, \
@@ -242,7 +250,11 @@ typedef struct xfs_trans_header {
        { XFS_LI_QUOTAOFF,      "XFS_LI_QUOTAOFF" }, \
        { XFS_LI_ICREATE,       "XFS_LI_ICREATE" }, \
        { XFS_LI_RUI,           "XFS_LI_RUI" }, \
-       { XFS_LI_RUD,           "XFS_LI_RUD" }
+       { XFS_LI_RUD,           "XFS_LI_RUD" }, \
+       { XFS_LI_CUI,           "XFS_LI_CUI" }, \
+       { XFS_LI_CUD,           "XFS_LI_CUD" }, \
+       { XFS_LI_BUI,           "XFS_LI_BUI" }, \
+       { XFS_LI_BUD,           "XFS_LI_BUD" }
 
 /*
  * Inode Log Item Format definitions.
@@ -411,7 +423,8 @@ struct xfs_log_dinode {
        __uint64_t      di_changecount; /* number of attribute changes */
        xfs_lsn_t       di_lsn;         /* flush sequence */
        __uint64_t      di_flags2;      /* more random flags */
-       __uint8_t       di_pad2[16];    /* more padding for future expansion */
+       __uint32_t      di_cowextsize;  /* basic cow extent size for file */
+       __uint8_t       di_pad2[12];    /* more padding for future expansion */
 
        /* fields only written to during inode creation */
        xfs_ictimestamp_t di_crtime;    /* time created */
@@ -622,8 +635,11 @@ struct xfs_map_extent {
 
 /* rmap me_flags: upper bits are flags, lower byte is type code */
 #define XFS_RMAP_EXTENT_MAP            1
+#define XFS_RMAP_EXTENT_MAP_SHARED     2
 #define XFS_RMAP_EXTENT_UNMAP          3
+#define XFS_RMAP_EXTENT_UNMAP_SHARED   4
 #define XFS_RMAP_EXTENT_CONVERT                5
+#define XFS_RMAP_EXTENT_CONVERT_SHARED 6
 #define XFS_RMAP_EXTENT_ALLOC          7
 #define XFS_RMAP_EXTENT_FREE           8
 #define XFS_RMAP_EXTENT_TYPE_MASK      0xFF
@@ -670,6 +686,102 @@ struct xfs_rud_log_format {
        __uint64_t              rud_rui_id;     /* id of corresponding rui */
 };
 
+/*
+ * CUI/CUD (refcount update) log format definitions
+ */
+struct xfs_phys_extent {
+       __uint64_t              pe_startblock;
+       __uint32_t              pe_len;
+       __uint32_t              pe_flags;
+};
+
+/* refcount pe_flags: upper bits are flags, lower byte is type code */
+/* Type codes are taken directly from enum xfs_refcount_intent_type. */
+#define XFS_REFCOUNT_EXTENT_TYPE_MASK  0xFF
+
+#define XFS_REFCOUNT_EXTENT_FLAGS      (XFS_REFCOUNT_EXTENT_TYPE_MASK)
+
+/*
+ * This is the structure used to lay out a cui log item in the
+ * log.  The cui_extents field is a variable size array whose
+ * size is given by cui_nextents.
+ */
+struct xfs_cui_log_format {
+       __uint16_t              cui_type;       /* cui log item type */
+       __uint16_t              cui_size;       /* size of this item */
+       __uint32_t              cui_nextents;   /* # extents to free */
+       __uint64_t              cui_id;         /* cui identifier */
+       struct xfs_phys_extent  cui_extents[];  /* array of extents */
+};
+
+static inline size_t
+xfs_cui_log_format_sizeof(
+       unsigned int            nr)
+{
+       return sizeof(struct xfs_cui_log_format) +
+                       nr * sizeof(struct xfs_phys_extent);
+}
+
+/*
+ * This is the structure used to lay out a cud log item in the
+ * log.  The cud_extents array is a variable size array whose
+ * size is given by cud_nextents;
+ */
+struct xfs_cud_log_format {
+       __uint16_t              cud_type;       /* cud log item type */
+       __uint16_t              cud_size;       /* size of this item */
+       __uint32_t              __pad;
+       __uint64_t              cud_cui_id;     /* id of corresponding cui */
+};
+
+/*
+ * BUI/BUD (inode block mapping) log format definitions
+ */
+
+/* bmbt me_flags: upper bits are flags, lower byte is type code */
+/* Type codes are taken directly from enum xfs_bmap_intent_type. */
+#define XFS_BMAP_EXTENT_TYPE_MASK      0xFF
+
+#define XFS_BMAP_EXTENT_ATTR_FORK      (1U << 31)
+#define XFS_BMAP_EXTENT_UNWRITTEN      (1U << 30)
+
+#define XFS_BMAP_EXTENT_FLAGS          (XFS_BMAP_EXTENT_TYPE_MASK | \
+                                        XFS_BMAP_EXTENT_ATTR_FORK | \
+                                        XFS_BMAP_EXTENT_UNWRITTEN)
+
+/*
+ * This is the structure used to lay out an bui log item in the
+ * log.  The bui_extents field is a variable size array whose
+ * size is given by bui_nextents.
+ */
+struct xfs_bui_log_format {
+       __uint16_t              bui_type;       /* bui log item type */
+       __uint16_t              bui_size;       /* size of this item */
+       __uint32_t              bui_nextents;   /* # extents to free */
+       __uint64_t              bui_id;         /* bui identifier */
+       struct xfs_map_extent   bui_extents[];  /* array of extents to bmap */
+};
+
+static inline size_t
+xfs_bui_log_format_sizeof(
+       unsigned int            nr)
+{
+       return sizeof(struct xfs_bui_log_format) +
+                       nr * sizeof(struct xfs_map_extent);
+}
+
+/*
+ * This is the structure used to lay out an bud log item in the
+ * log.  The bud_extents array is a variable size array whose
+ * size is given by bud_nextents;
+ */
+struct xfs_bud_log_format {
+       __uint16_t              bud_type;       /* bud log item type */
+       __uint16_t              bud_size;       /* size of this item */
+       __uint32_t              __pad;
+       __uint64_t              bud_bui_id;     /* id of corresponding bui */
+};
+
 /*
  * Dquot Log format definitions.
  *
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
new file mode 100644 (file)
index 0000000..b177ef3
--- /dev/null
@@ -0,0 +1,1698 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_cksum.h"
+#include "xfs_trans.h"
+#include "xfs_bit.h"
+#include "xfs_refcount.h"
+#include "xfs_rmap.h"
+
+/* Allowable refcount adjustment amounts. */
+enum xfs_refc_adjust_op {
+       XFS_REFCOUNT_ADJUST_INCREASE    = 1,
+       XFS_REFCOUNT_ADJUST_DECREASE    = -1,
+       XFS_REFCOUNT_ADJUST_COW_ALLOC   = 0,
+       XFS_REFCOUNT_ADJUST_COW_FREE    = -1,
+};
+
+STATIC int __xfs_refcount_cow_alloc(struct xfs_btree_cur *rcur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen,
+               struct xfs_defer_ops *dfops);
+STATIC int __xfs_refcount_cow_free(struct xfs_btree_cur *rcur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen,
+               struct xfs_defer_ops *dfops);
+
+/*
+ * Look up the first record less than or equal to [bno, len] in the btree
+ * given by cur.
+ */
+int
+xfs_refcount_lookup_le(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       int                     *stat)
+{
+       trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
+                       XFS_LOOKUP_LE);
+       cur->bc_rec.rc.rc_startblock = bno;
+       cur->bc_rec.rc.rc_blockcount = 0;
+       return xfs_btree_lookup(cur, XFS_LOOKUP_LE, stat);
+}
+
+/*
+ * Look up the first record greater than or equal to [bno, len] in the btree
+ * given by cur.
+ */
+int
+xfs_refcount_lookup_ge(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       int                     *stat)
+{
+       trace_xfs_refcount_lookup(cur->bc_mp, cur->bc_private.a.agno, bno,
+                       XFS_LOOKUP_GE);
+       cur->bc_rec.rc.rc_startblock = bno;
+       cur->bc_rec.rc.rc_blockcount = 0;
+       return xfs_btree_lookup(cur, XFS_LOOKUP_GE, stat);
+}
+
+/* Convert on-disk record to in-core format. */
+static inline void
+xfs_refcount_btrec_to_irec(
+       union xfs_btree_rec             *rec,
+       struct xfs_refcount_irec        *irec)
+{
+       irec->rc_startblock = be32_to_cpu(rec->refc.rc_startblock);
+       irec->rc_blockcount = be32_to_cpu(rec->refc.rc_blockcount);
+       irec->rc_refcount = be32_to_cpu(rec->refc.rc_refcount);
+}
+
+/*
+ * Get the data from the pointed-to record.
+ */
+int
+xfs_refcount_get_rec(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec,
+       int                             *stat)
+{
+       union xfs_btree_rec             *rec;
+       int                             error;
+
+       error = xfs_btree_get_rec(cur, &rec, stat);
+       if (!error && *stat == 1) {
+               xfs_refcount_btrec_to_irec(rec, irec);
+               trace_xfs_refcount_get(cur->bc_mp, cur->bc_private.a.agno,
+                               irec);
+       }
+       return error;
+}
+
+/*
+ * Update the record referred to by cur to the value given
+ * by [bno, len, refcount].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_update(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec)
+{
+       union xfs_btree_rec     rec;
+       int                     error;
+
+       trace_xfs_refcount_update(cur->bc_mp, cur->bc_private.a.agno, irec);
+       rec.refc.rc_startblock = cpu_to_be32(irec->rc_startblock);
+       rec.refc.rc_blockcount = cpu_to_be32(irec->rc_blockcount);
+       rec.refc.rc_refcount = cpu_to_be32(irec->rc_refcount);
+       error = xfs_btree_update(cur, &rec);
+       if (error)
+               trace_xfs_refcount_update_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Insert the record referred to by cur to the value given
+ * by [bno, len, refcount].
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_insert(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *irec,
+       int                             *i)
+{
+       int                             error;
+
+       trace_xfs_refcount_insert(cur->bc_mp, cur->bc_private.a.agno, irec);
+       cur->bc_rec.rc.rc_startblock = irec->rc_startblock;
+       cur->bc_rec.rc.rc_blockcount = irec->rc_blockcount;
+       cur->bc_rec.rc.rc_refcount = irec->rc_refcount;
+       error = xfs_btree_insert(cur, i);
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
+out_error:
+       if (error)
+               trace_xfs_refcount_insert_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Remove the record referred to by cur, then set the pointer to the spot
+ * where the record could be re-inserted, in case we want to increment or
+ * decrement the cursor.
+ * This either works (return 0) or gets an EFSCORRUPTED error.
+ */
+STATIC int
+xfs_refcount_delete(
+       struct xfs_btree_cur    *cur,
+       int                     *i)
+{
+       struct xfs_refcount_irec        irec;
+       int                     found_rec;
+       int                     error;
+
+       error = xfs_refcount_get_rec(cur, &irec, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       trace_xfs_refcount_delete(cur->bc_mp, cur->bc_private.a.agno, &irec);
+       error = xfs_btree_delete(cur, i);
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, *i == 1, out_error);
+       if (error)
+               goto out_error;
+       error = xfs_refcount_lookup_ge(cur, irec.rc_startblock, &found_rec);
+out_error:
+       if (error)
+               trace_xfs_refcount_delete_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Adjusting the Reference Count
+ *
+ * As stated elsewhere, the reference count btree (refcbt) stores
+ * >1 reference counts for extents of physical blocks.  In this
+ * operation, we're either raising or lowering the reference count of
+ * some subrange stored in the tree:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+-----+ +--+--------+---------
+ *  2  |   | 3 |  4  | |17|   55   |   10
+ * ----+   +---+-----+ +--+--------+---------
+ * X axis is physical blocks number;
+ * reference counts are the numbers inside the rectangles
+ *
+ * The first thing we need to do is to ensure that there are no
+ * refcount extents crossing either boundary of the range to be
+ * adjusted.  For any extent that does cross a boundary, split it into
+ * two extents so that we can increment the refcount of one of the
+ * pieces later:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+-----+ +--+--------+----+----
+ *  2  |   | 3 |  2  | |17|   55   | 10 | 10
+ * ----+   +---+-----+ +--+--------+----+----
+ *
+ * For this next step, let's assume that all the physical blocks in
+ * the adjustment range are mapped to a file and are therefore in use
+ * at least once.  Therefore, we can infer that any gap in the
+ * refcount tree within the adjustment range represents a physical
+ * extent with refcount == 1:
+ *
+ *      <------ adjustment range ------>
+ * ----+---+---+-----+-+--+--------+----+----
+ *  2  |"1"| 3 |  2  |1|17|   55   | 10 | 10
+ * ----+---+---+-----+-+--+--------+----+----
+ *      ^
+ *
+ * For each extent that falls within the interval range, figure out
+ * which extent is to the left or the right of that extent.  Now we
+ * have a left, current, and right extent.  If the new reference count
+ * of the center extent enables us to merge left, center, and right
+ * into one record covering all three, do so.  If the center extent is
+ * at the left end of the range, abuts the left extent, and its new
+ * reference count matches the left extent's record, then merge them.
+ * If the center extent is at the right end of the range, abuts the
+ * right extent, and the reference counts match, merge those.  In the
+ * example, we can left merge (assuming an increment operation):
+ *
+ *      <------ adjustment range ------>
+ * --------+---+-----+-+--+--------+----+----
+ *    2    | 3 |  2  |1|17|   55   | 10 | 10
+ * --------+---+-----+-+--+--------+----+----
+ *          ^
+ *
+ * For all other extents within the range, adjust the reference count
+ * or delete it if the refcount falls below 2.  If we were
+ * incrementing, the end result looks like this:
+ *
+ *      <------ adjustment range ------>
+ * --------+---+-----+-+--+--------+----+----
+ *    2    | 4 |  3  |2|18|   56   | 11 | 10
+ * --------+---+-----+-+--+--------+----+----
+ *
+ * The result of a decrement operation looks as such:
+ *
+ *      <------ adjustment range ------>
+ * ----+   +---+       +--+--------+----+----
+ *  2  |   | 2 |       |16|   54   |  9 | 10
+ * ----+   +---+       +--+--------+----+----
+ *      DDDD    111111DD
+ *
+ * The blocks marked "D" are freed; the blocks marked "1" are only
+ * referenced once and therefore the record is removed from the
+ * refcount btree.
+ */
+
+/* Next block after this extent. */
+static inline xfs_agblock_t
+xfs_refc_next(
+       struct xfs_refcount_irec        *rc)
+{
+       return rc->rc_startblock + rc->rc_blockcount;
+}
+
+/*
+ * Split a refcount extent that crosses agbno.
+ */
+STATIC int
+xfs_refcount_split_extent(
+       struct xfs_btree_cur            *cur,
+       xfs_agblock_t                   agbno,
+       bool                            *shape_changed)
+{
+       struct xfs_refcount_irec        rcext, tmp;
+       int                             found_rec;
+       int                             error;
+
+       *shape_changed = false;
+       error = xfs_refcount_lookup_le(cur, agbno, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &rcext, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       if (rcext.rc_startblock == agbno || xfs_refc_next(&rcext) <= agbno)
+               return 0;
+
+       *shape_changed = true;
+       trace_xfs_refcount_split_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       &rcext, agbno);
+
+       /* Establish the right extent. */
+       tmp = rcext;
+       tmp.rc_startblock = agbno;
+       tmp.rc_blockcount -= (agbno - rcext.rc_startblock);
+       error = xfs_refcount_update(cur, &tmp);
+       if (error)
+               goto out_error;
+
+       /* Insert the left extent. */
+       tmp = rcext;
+       tmp.rc_blockcount = agbno - rcext.rc_startblock;
+       error = xfs_refcount_insert(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+       return error;
+
+out_error:
+       trace_xfs_refcount_split_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge the left, center, and right extents.
+ */
+STATIC int
+xfs_refcount_merge_center_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *center,
+       struct xfs_refcount_irec        *right,
+       unsigned long long              extlen,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_center_extents(cur->bc_mp,
+                       cur->bc_private.a.agno, left, center, right);
+
+       /*
+        * Make sure the center and right extents are not in the btree.
+        * If the center extent was synthesized, the first delete call
+        * removes the right extent and we skip the second deletion.
+        * If center and right were in the btree, then the first delete
+        * call removes the center and the second one removes the right
+        * extent.
+        */
+       error = xfs_refcount_lookup_ge(cur, center->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       error = xfs_refcount_delete(cur, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (center->rc_refcount > 1) {
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the left extent. */
+       error = xfs_refcount_lookup_le(cur, left->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       left->rc_blockcount = extlen;
+       error = xfs_refcount_update(cur, left);
+       if (error)
+               goto out_error;
+
+       *aglen = 0;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_center_extents_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge with the left extent.
+ */
+STATIC int
+xfs_refcount_merge_left_extent(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *cleft,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_left_extent(cur->bc_mp,
+                       cur->bc_private.a.agno, left, cleft);
+
+       /* If the extent at agbno (cleft) wasn't synthesized, remove it. */
+       if (cleft->rc_refcount > 1) {
+               error = xfs_refcount_lookup_le(cur, cleft->rc_startblock,
+                               &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the left extent. */
+       error = xfs_refcount_lookup_le(cur, left->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       left->rc_blockcount += cleft->rc_blockcount;
+       error = xfs_refcount_update(cur, left);
+       if (error)
+               goto out_error;
+
+       *agbno += cleft->rc_blockcount;
+       *aglen -= cleft->rc_blockcount;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_left_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Merge with the right extent.
+ */
+STATIC int
+xfs_refcount_merge_right_extent(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *right,
+       struct xfs_refcount_irec        *cright,
+       xfs_agblock_t                   *agbno,
+       xfs_extlen_t                    *aglen)
+{
+       int                             error;
+       int                             found_rec;
+
+       trace_xfs_refcount_merge_right_extent(cur->bc_mp,
+                       cur->bc_private.a.agno, cright, right);
+
+       /*
+        * If the extent ending at agbno+aglen (cright) wasn't synthesized,
+        * remove it.
+        */
+       if (cright->rc_refcount > 1) {
+               error = xfs_refcount_lookup_le(cur, cright->rc_startblock,
+                       &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+       }
+
+       /* Enlarge the right extent. */
+       error = xfs_refcount_lookup_le(cur, right->rc_startblock,
+                       &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       right->rc_startblock -= cright->rc_blockcount;
+       right->rc_blockcount += cright->rc_blockcount;
+       error = xfs_refcount_update(cur, right);
+       if (error)
+               goto out_error;
+
+       *aglen -= cright->rc_blockcount;
+       return error;
+
+out_error:
+       trace_xfs_refcount_merge_right_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+#define XFS_FIND_RCEXT_SHARED  1
+#define XFS_FIND_RCEXT_COW     2
+/*
+ * Find the left extent and the one after it (cleft).  This function assumes
+ * that we've already split any extent crossing agbno.
+ */
+STATIC int
+xfs_refcount_find_left_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *left,
+       struct xfs_refcount_irec        *cleft,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       int                             flags)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             error;
+       int                             found_rec;
+
+       left->rc_startblock = cleft->rc_startblock = NULLAGBLOCK;
+       error = xfs_refcount_lookup_le(cur, agbno - 1, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (xfs_refc_next(&tmp) != agbno)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
+               return 0;
+       /* We have a left extent; retrieve (or invent) the next right one */
+       *left = tmp;
+
+       error = xfs_btree_increment(cur, 0, &found_rec);
+       if (error)
+               goto out_error;
+       if (found_rec) {
+               error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               /* if tmp starts at the end of our range, just use that */
+               if (tmp.rc_startblock == agbno)
+                       *cleft = tmp;
+               else {
+                       /*
+                        * There's a gap in the refcntbt at the start of the
+                        * range we're interested in (refcount == 1) so
+                        * synthesize the implied extent and pass it back.
+                        * We assume here that the agbno/aglen range was
+                        * passed in from a data fork extent mapping and
+                        * therefore is allocated to exactly one owner.
+                        */
+                       cleft->rc_startblock = agbno;
+                       cleft->rc_blockcount = min(aglen,
+                                       tmp.rc_startblock - agbno);
+                       cleft->rc_refcount = 1;
+               }
+       } else {
+               /*
+                * No extents, so pretend that there's one covering the whole
+                * range.
+                */
+               cleft->rc_startblock = agbno;
+               cleft->rc_blockcount = aglen;
+               cleft->rc_refcount = 1;
+       }
+       trace_xfs_refcount_find_left_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       left, cleft, agbno);
+       return error;
+
+out_error:
+       trace_xfs_refcount_find_left_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Find the right extent and the one before it (cright).  This function
+ * assumes that we've already split any extents crossing agbno + aglen.
+ */
+STATIC int
+xfs_refcount_find_right_extents(
+       struct xfs_btree_cur            *cur,
+       struct xfs_refcount_irec        *right,
+       struct xfs_refcount_irec        *cright,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       int                             flags)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             error;
+       int                             found_rec;
+
+       right->rc_startblock = cright->rc_startblock = NULLAGBLOCK;
+       error = xfs_refcount_lookup_ge(cur, agbno + aglen, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec)
+               return 0;
+
+       error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1, out_error);
+
+       if (tmp.rc_startblock != agbno + aglen)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_SHARED) && tmp.rc_refcount < 2)
+               return 0;
+       if ((flags & XFS_FIND_RCEXT_COW) && tmp.rc_refcount > 1)
+               return 0;
+       /* We have a right extent; retrieve (or invent) the next left one */
+       *right = tmp;
+
+       error = xfs_btree_decrement(cur, 0, &found_rec);
+       if (error)
+               goto out_error;
+       if (found_rec) {
+               error = xfs_refcount_get_rec(cur, &tmp, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, found_rec == 1,
+                               out_error);
+
+               /* if tmp ends at the end of our range, just use that */
+               if (xfs_refc_next(&tmp) == agbno + aglen)
+                       *cright = tmp;
+               else {
+                       /*
+                        * There's a gap in the refcntbt at the end of the
+                        * range we're interested in (refcount == 1) so
+                        * create the implied extent and pass it back.
+                        * We assume here that the agbno/aglen range was
+                        * passed in from a data fork extent mapping and
+                        * therefore is allocated to exactly one owner.
+                        */
+                       cright->rc_startblock = max(agbno, xfs_refc_next(&tmp));
+                       cright->rc_blockcount = right->rc_startblock -
+                                       cright->rc_startblock;
+                       cright->rc_refcount = 1;
+               }
+       } else {
+               /*
+                * No extents, so pretend that there's one covering the whole
+                * range.
+                */
+               cright->rc_startblock = agbno;
+               cright->rc_blockcount = aglen;
+               cright->rc_refcount = 1;
+       }
+       trace_xfs_refcount_find_right_extent(cur->bc_mp, cur->bc_private.a.agno,
+                       cright, right, agbno + aglen);
+       return error;
+
+out_error:
+       trace_xfs_refcount_find_right_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/* Is this extent valid? */
+static inline bool
+xfs_refc_valid(
+       struct xfs_refcount_irec        *rc)
+{
+       return rc->rc_startblock != NULLAGBLOCK;
+}
+
+/*
+ * Try to merge with any extents on the boundaries of the adjustment range.
+ */
+STATIC int
+xfs_refcount_merge_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           *agbno,
+       xfs_extlen_t            *aglen,
+       enum xfs_refc_adjust_op adjust,
+       int                     flags,
+       bool                    *shape_changed)
+{
+       struct xfs_refcount_irec        left = {0}, cleft = {0};
+       struct xfs_refcount_irec        cright = {0}, right = {0};
+       int                             error;
+       unsigned long long              ulen;
+       bool                            cequal;
+
+       *shape_changed = false;
+       /*
+        * Find the extent just below agbno [left], just above agbno [cleft],
+        * just below (agbno + aglen) [cright], and just above (agbno + aglen)
+        * [right].
+        */
+       error = xfs_refcount_find_left_extents(cur, &left, &cleft, *agbno,
+                       *aglen, flags);
+       if (error)
+               return error;
+       error = xfs_refcount_find_right_extents(cur, &right, &cright, *agbno,
+                       *aglen, flags);
+       if (error)
+               return error;
+
+       /* No left or right extent to merge; exit. */
+       if (!xfs_refc_valid(&left) && !xfs_refc_valid(&right))
+               return 0;
+
+       cequal = (cleft.rc_startblock == cright.rc_startblock) &&
+                (cleft.rc_blockcount == cright.rc_blockcount);
+
+       /* Try to merge left, cleft, and right.  cleft must == cright. */
+       ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount +
+                       right.rc_blockcount;
+       if (xfs_refc_valid(&left) && xfs_refc_valid(&right) &&
+           xfs_refc_valid(&cleft) && xfs_refc_valid(&cright) && cequal &&
+           left.rc_refcount == cleft.rc_refcount + adjust &&
+           right.rc_refcount == cleft.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               return xfs_refcount_merge_center_extents(cur, &left, &cleft,
+                               &right, ulen, agbno, aglen);
+       }
+
+       /* Try to merge left and cleft. */
+       ulen = (unsigned long long)left.rc_blockcount + cleft.rc_blockcount;
+       if (xfs_refc_valid(&left) && xfs_refc_valid(&cleft) &&
+           left.rc_refcount == cleft.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               error = xfs_refcount_merge_left_extent(cur, &left, &cleft,
+                               agbno, aglen);
+               if (error)
+                       return error;
+
+               /*
+                * If we just merged left + cleft and cleft == cright,
+                * we no longer have a cright to merge with right.  We're done.
+                */
+               if (cequal)
+                       return 0;
+       }
+
+       /* Try to merge cright and right. */
+       ulen = (unsigned long long)right.rc_blockcount + cright.rc_blockcount;
+       if (xfs_refc_valid(&right) && xfs_refc_valid(&cright) &&
+           right.rc_refcount == cright.rc_refcount + adjust &&
+           ulen < MAXREFCEXTLEN) {
+               *shape_changed = true;
+               return xfs_refcount_merge_right_extent(cur, &right, &cright,
+                               agbno, aglen);
+       }
+
+       return error;
+}
+
+/*
+ * While we're adjusting the refcounts records of an extent, we have
+ * to keep an eye on the number of extents we're dirtying -- run too
+ * many in a single transaction and we'll exceed the transaction's
+ * reservation and crash the fs.  Each record adds 12 bytes to the
+ * log (plus any key updates) so we'll conservatively assume 24 bytes
+ * per record.  We must also leave space for btree splits on both ends
+ * of the range and space for the CUD and a new CUI.
+ *
+ * XXX: This is a pretty hand-wavy estimate.  The penalty for guessing
+ * true incorrectly is a shutdown FS; the penalty for guessing false
+ * incorrectly is more transaction rolls than might be necessary.
+ * Be conservative here.
+ */
+static bool
+xfs_refcount_still_have_space(
+       struct xfs_btree_cur            *cur)
+{
+       unsigned long                   overhead;
+
+       overhead = cur->bc_private.a.priv.refc.shape_changes *
+                       xfs_allocfree_log_count(cur->bc_mp, 1);
+       overhead *= cur->bc_mp->m_sb.sb_blocksize;
+
+       /*
+        * Only allow 2 refcount extent updates per transaction if the
+        * refcount continue update "error" has been injected.
+        */
+       if (cur->bc_private.a.priv.refc.nr_ops > 2 &&
+           XFS_TEST_ERROR(false, cur->bc_mp,
+                       XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE,
+                       XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE))
+               return false;
+
+       if (cur->bc_private.a.priv.refc.nr_ops == 0)
+               return true;
+       else if (overhead > cur->bc_tp->t_log_res)
+               return false;
+       return  cur->bc_tp->t_log_res - overhead >
+               cur->bc_private.a.priv.refc.nr_ops * 32;
+}
+
+/*
+ * Adjust the refcounts of middle extents.  At this point we should have
+ * split extents that crossed the adjustment range; merged with adjacent
+ * extents; and updated agbno/aglen to reflect the merges.  Therefore,
+ * all we have to do is update the extents inside [agbno, agbno + aglen].
+ */
+STATIC int
+xfs_refcount_adjust_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           *agbno,
+       xfs_extlen_t            *aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_refcount_irec        ext, tmp;
+       int                             error;
+       int                             found_rec, found_tmp;
+       xfs_fsblock_t                   fsbno;
+
+       /* Merging did all the work already. */
+       if (*aglen == 0)
+               return 0;
+
+       error = xfs_refcount_lookup_ge(cur, *agbno, &found_rec);
+       if (error)
+               goto out_error;
+
+       while (*aglen > 0 && xfs_refcount_still_have_space(cur)) {
+               error = xfs_refcount_get_rec(cur, &ext, &found_rec);
+               if (error)
+                       goto out_error;
+               if (!found_rec) {
+                       ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks;
+                       ext.rc_blockcount = 0;
+                       ext.rc_refcount = 0;
+               }
+
+               /*
+                * Deal with a hole in the refcount tree; if a file maps to
+                * these blocks and there's no refcountbt record, pretend that
+                * there is one with refcount == 1.
+                */
+               if (ext.rc_startblock != *agbno) {
+                       tmp.rc_startblock = *agbno;
+                       tmp.rc_blockcount = min(*aglen,
+                                       ext.rc_startblock - *agbno);
+                       tmp.rc_refcount = 1 + adj;
+                       trace_xfs_refcount_modify_extent(cur->bc_mp,
+                                       cur->bc_private.a.agno, &tmp);
+
+                       /*
+                        * Either cover the hole (increment) or
+                        * delete the range (decrement).
+                        */
+                       if (tmp.rc_refcount) {
+                               error = xfs_refcount_insert(cur, &tmp,
+                                               &found_tmp);
+                               if (error)
+                                       goto out_error;
+                               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                                               found_tmp == 1, out_error);
+                               cur->bc_private.a.priv.refc.nr_ops++;
+                       } else {
+                               fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
+                                               cur->bc_private.a.agno,
+                                               tmp.rc_startblock);
+                               xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
+                                               tmp.rc_blockcount, oinfo);
+                       }
+
+                       (*agbno) += tmp.rc_blockcount;
+                       (*aglen) -= tmp.rc_blockcount;
+
+                       error = xfs_refcount_lookup_ge(cur, *agbno,
+                                       &found_rec);
+                       if (error)
+                               goto out_error;
+               }
+
+               /* Stop if there's nothing left to modify */
+               if (*aglen == 0 || !xfs_refcount_still_have_space(cur))
+                       break;
+
+               /*
+                * Adjust the reference count and either update the tree
+                * (incr) or free the blocks (decr).
+                */
+               if (ext.rc_refcount == MAXREFCOUNT)
+                       goto skip;
+               ext.rc_refcount += adj;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &ext);
+               if (ext.rc_refcount > 1) {
+                       error = xfs_refcount_update(cur, &ext);
+                       if (error)
+                               goto out_error;
+                       cur->bc_private.a.priv.refc.nr_ops++;
+               } else if (ext.rc_refcount == 1) {
+                       error = xfs_refcount_delete(cur, &found_rec);
+                       if (error)
+                               goto out_error;
+                       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                                       found_rec == 1, out_error);
+                       cur->bc_private.a.priv.refc.nr_ops++;
+                       goto advloop;
+               } else {
+                       fsbno = XFS_AGB_TO_FSB(cur->bc_mp,
+                                       cur->bc_private.a.agno,
+                                       ext.rc_startblock);
+                       xfs_bmap_add_free(cur->bc_mp, dfops, fsbno,
+                                       ext.rc_blockcount, oinfo);
+               }
+
+skip:
+               error = xfs_btree_increment(cur, 0, &found_rec);
+               if (error)
+                       goto out_error;
+
+advloop:
+               (*agbno) += ext.rc_blockcount;
+               (*aglen) -= ext.rc_blockcount;
+       }
+
+       return error;
+out_error:
+       trace_xfs_refcount_modify_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/* Adjust the reference count of a range of AG blocks. */
+STATIC int
+xfs_refcount_adjust(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       xfs_agblock_t           *new_agbno,
+       xfs_extlen_t            *new_aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       bool                    shape_changed;
+       int                     shape_changes = 0;
+       int                     error;
+
+       *new_agbno = agbno;
+       *new_aglen = aglen;
+       if (adj == XFS_REFCOUNT_ADJUST_INCREASE)
+               trace_xfs_refcount_increase(cur->bc_mp, cur->bc_private.a.agno,
+                               agbno, aglen);
+       else
+               trace_xfs_refcount_decrease(cur->bc_mp, cur->bc_private.a.agno,
+                               agbno, aglen);
+
+       /*
+        * Ensure that no rcextents cross the boundary of the adjustment range.
+        */
+       error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+
+       error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+
+       /*
+        * Try to merge with the left or right extents of the range.
+        */
+       error = xfs_refcount_merge_extents(cur, new_agbno, new_aglen, adj,
+                       XFS_FIND_RCEXT_SHARED, &shape_changed);
+       if (error)
+               goto out_error;
+       if (shape_changed)
+               shape_changes++;
+       if (shape_changes)
+               cur->bc_private.a.priv.refc.shape_changes++;
+
+       /* Now that we've taken care of the ends, adjust the middle extents */
+       error = xfs_refcount_adjust_extents(cur, new_agbno, new_aglen,
+                       adj, dfops, oinfo);
+       if (error)
+               goto out_error;
+
+       return 0;
+
+out_error:
+       trace_xfs_refcount_adjust_error(cur->bc_mp, cur->bc_private.a.agno,
+                       error, _RET_IP_);
+       return error;
+}
+
+/* Clean up after calling xfs_refcount_finish_one. */
+void
+xfs_refcount_finish_one_cleanup(
+       struct xfs_trans        *tp,
+       struct xfs_btree_cur    *rcur,
+       int                     error)
+{
+       struct xfs_buf          *agbp;
+
+       if (rcur == NULL)
+               return;
+       agbp = rcur->bc_private.a.agbp;
+       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       if (error)
+               xfs_trans_brelse(tp, agbp);
+}
+
+/*
+ * Process one of the deferred refcount operations.  We pass back the
+ * btree cursor to maintain our lock on the btree between calls.
+ * This saves time and eliminates a buffer deadlock between the
+ * superblock and the AGF because we'll always grab them in the same
+ * order.
+ */
+int
+xfs_refcount_finish_one(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount,
+       xfs_fsblock_t                   *new_fsb,
+       xfs_extlen_t                    *new_len,
+       struct xfs_btree_cur            **pcur)
+{
+       struct xfs_mount                *mp = tp->t_mountp;
+       struct xfs_btree_cur            *rcur;
+       struct xfs_buf                  *agbp = NULL;
+       int                             error = 0;
+       xfs_agnumber_t                  agno;
+       xfs_agblock_t                   bno;
+       xfs_agblock_t                   new_agbno;
+       unsigned long                   nr_ops = 0;
+       int                             shape_changes = 0;
+
+       agno = XFS_FSB_TO_AGNO(mp, startblock);
+       ASSERT(agno != NULLAGNUMBER);
+       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+
+       trace_xfs_refcount_deferred(mp, XFS_FSB_TO_AGNO(mp, startblock),
+                       type, XFS_FSB_TO_AGBNO(mp, startblock),
+                       blockcount);
+
+       if (XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_REFCOUNT_FINISH_ONE,
+                       XFS_RANDOM_REFCOUNT_FINISH_ONE))
+               return -EIO;
+
+       /*
+        * If we haven't gotten a cursor or the cursor AG doesn't match
+        * the startblock, get one now.
+        */
+       rcur = *pcur;
+       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
+               nr_ops = rcur->bc_private.a.priv.refc.nr_ops;
+               shape_changes = rcur->bc_private.a.priv.refc.shape_changes;
+               xfs_refcount_finish_one_cleanup(tp, rcur, 0);
+               rcur = NULL;
+               *pcur = NULL;
+       }
+       if (rcur == NULL) {
+               error = xfs_alloc_read_agf(tp->t_mountp, tp, agno,
+                               XFS_ALLOC_FLAG_FREEING, &agbp);
+               if (error)
+                       return error;
+               if (!agbp)
+                       return -EFSCORRUPTED;
+
+               rcur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, dfops);
+               if (!rcur) {
+                       error = -ENOMEM;
+                       goto out_cur;
+               }
+               rcur->bc_private.a.priv.refc.nr_ops = nr_ops;
+               rcur->bc_private.a.priv.refc.shape_changes = shape_changes;
+       }
+       *pcur = rcur;
+
+       switch (type) {
+       case XFS_REFCOUNT_INCREASE:
+               error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
+                       new_len, XFS_REFCOUNT_ADJUST_INCREASE, dfops, NULL);
+               *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
+               break;
+       case XFS_REFCOUNT_DECREASE:
+               error = xfs_refcount_adjust(rcur, bno, blockcount, &new_agbno,
+                       new_len, XFS_REFCOUNT_ADJUST_DECREASE, dfops, NULL);
+               *new_fsb = XFS_AGB_TO_FSB(mp, agno, new_agbno);
+               break;
+       case XFS_REFCOUNT_ALLOC_COW:
+               *new_fsb = startblock + blockcount;
+               *new_len = 0;
+               error = __xfs_refcount_cow_alloc(rcur, bno, blockcount, dfops);
+               break;
+       case XFS_REFCOUNT_FREE_COW:
+               *new_fsb = startblock + blockcount;
+               *new_len = 0;
+               error = __xfs_refcount_cow_free(rcur, bno, blockcount, dfops);
+               break;
+       default:
+               ASSERT(0);
+               error = -EFSCORRUPTED;
+       }
+       if (!error && *new_len > 0)
+               trace_xfs_refcount_finish_one_leftover(mp, agno, type,
+                               bno, blockcount, new_agbno, *new_len);
+       return error;
+
+out_cur:
+       xfs_trans_brelse(tp, agbp);
+
+       return error;
+}
+
+/*
+ * Record a refcount intent for later processing.
+ */
+static int
+__xfs_refcount_add(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount)
+{
+       struct xfs_refcount_intent      *ri;
+
+       trace_xfs_refcount_defer(mp, XFS_FSB_TO_AGNO(mp, startblock),
+                       type, XFS_FSB_TO_AGBNO(mp, startblock),
+                       blockcount);
+
+       ri = kmem_alloc(sizeof(struct xfs_refcount_intent),
+                       KM_SLEEP | KM_NOFS);
+       INIT_LIST_HEAD(&ri->ri_list);
+       ri->ri_type = type;
+       ri->ri_startblock = startblock;
+       ri->ri_blockcount = blockcount;
+
+       xfs_defer_add(dfops, XFS_DEFER_OPS_TYPE_REFCOUNT, &ri->ri_list);
+       return 0;
+}
+
+/*
+ * Increase the reference count of the blocks backing a file's extent.
+ */
+int
+xfs_refcount_increase_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_bmbt_irec            *PREV)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_INCREASE,
+                       PREV->br_startblock, PREV->br_blockcount);
+}
+
+/*
+ * Decrease the reference count of the blocks backing a file's extent.
+ */
+int
+xfs_refcount_decrease_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       struct xfs_bmbt_irec            *PREV)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_DECREASE,
+                       PREV->br_startblock, PREV->br_blockcount);
+}
+
+/*
+ * Given an AG extent, find the lowest-numbered run of shared blocks
+ * within that range and return the range in fbno/flen.  If
+ * find_end_of_shared is set, return the longest contiguous extent of
+ * shared blocks; if not, just return the first extent we find.  If no
+ * shared blocks are found, fbno and flen will be set to NULLAGBLOCK
+ * and 0, respectively.
+ */
+int
+xfs_refcount_find_shared(
+       struct xfs_btree_cur            *cur,
+       xfs_agblock_t                   agbno,
+       xfs_extlen_t                    aglen,
+       xfs_agblock_t                   *fbno,
+       xfs_extlen_t                    *flen,
+       bool                            find_end_of_shared)
+{
+       struct xfs_refcount_irec        tmp;
+       int                             i;
+       int                             have;
+       int                             error;
+
+       trace_xfs_refcount_find_shared(cur->bc_mp, cur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* By default, skip the whole range */
+       *fbno = NULLAGBLOCK;
+       *flen = 0;
+
+       /* Try to find a refcount extent that crosses the start */
+       error = xfs_refcount_lookup_le(cur, agbno, &have);
+       if (error)
+               goto out_error;
+       if (!have) {
+               /* No left extent, look at the next one */
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       goto done;
+       }
+       error = xfs_refcount_get_rec(cur, &tmp, &i);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+
+       /* If the extent ends before the start, look at the next one */
+       if (tmp.rc_startblock + tmp.rc_blockcount <= agbno) {
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       goto done;
+               error = xfs_refcount_get_rec(cur, &tmp, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+       }
+
+       /* If the extent starts after the range we want, bail out */
+       if (tmp.rc_startblock >= agbno + aglen)
+               goto done;
+
+       /* We found the start of a shared extent! */
+       if (tmp.rc_startblock < agbno) {
+               tmp.rc_blockcount -= (agbno - tmp.rc_startblock);
+               tmp.rc_startblock = agbno;
+       }
+
+       *fbno = tmp.rc_startblock;
+       *flen = min(tmp.rc_blockcount, agbno + aglen - *fbno);
+       if (!find_end_of_shared)
+               goto done;
+
+       /* Otherwise, find the end of this shared extent */
+       while (*fbno + *flen < agbno + aglen) {
+               error = xfs_btree_increment(cur, 0, &have);
+               if (error)
+                       goto out_error;
+               if (!have)
+                       break;
+               error = xfs_refcount_get_rec(cur, &tmp, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp, i == 1, out_error);
+               if (tmp.rc_startblock >= agbno + aglen ||
+                   tmp.rc_startblock != *fbno + *flen)
+                       break;
+               *flen = min(*flen + tmp.rc_blockcount, agbno + aglen - *fbno);
+       }
+
+done:
+       trace_xfs_refcount_find_shared_result(cur->bc_mp,
+                       cur->bc_private.a.agno, *fbno, *flen);
+
+out_error:
+       if (error)
+               trace_xfs_refcount_find_shared_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Recovering CoW Blocks After a Crash
+ *
+ * Due to the way that the copy on write mechanism works, there's a window of
+ * opportunity in which we can lose track of allocated blocks during a crash.
+ * Because CoW uses delayed allocation in the in-core CoW fork, writeback
+ * causes blocks to be allocated and stored in the CoW fork.  The blocks are
+ * no longer in the free space btree but are not otherwise recorded anywhere
+ * until the write completes and the blocks are mapped into the file.  A crash
+ * in between allocation and remapping results in the replacement blocks being
+ * lost.  This situation is exacerbated by the CoW extent size hint because
+ * allocations can hang around for long time.
+ *
+ * However, there is a place where we can record these allocations before they
+ * become mappings -- the reference count btree.  The btree does not record
+ * extents with refcount == 1, so we can record allocations with a refcount of
+ * 1.  Blocks being used for CoW writeout cannot be shared, so there should be
+ * no conflict with shared block records.  These mappings should be created
+ * when we allocate blocks to the CoW fork and deleted when they're removed
+ * from the CoW fork.
+ *
+ * Minor nit: records for in-progress CoW allocations and records for shared
+ * extents must never be merged, to preserve the property that (except for CoW
+ * allocations) there are no refcount btree entries with refcount == 1.  The
+ * only time this could potentially happen is when unsharing a block that's
+ * adjacent to CoW allocations, so we must be careful to avoid this.
+ *
+ * At mount time we recover lost CoW allocations by searching the refcount
+ * btree for these refcount == 1 mappings.  These represent CoW allocations
+ * that were in progress at the time the filesystem went down, so we can free
+ * them to get the space back.
+ *
+ * This mechanism is superior to creating EFIs for unmapped CoW extents for
+ * several reasons -- first, EFIs pin the tail of the log and would have to be
+ * periodically relogged to avoid filling up the log.  Second, CoW completions
+ * will have to file an EFD and create new EFIs for whatever remains in the
+ * CoW fork; this partially takes care of (1) but extent-size reservations
+ * will have to periodically relog even if there's no writeout in progress.
+ * This can happen if the CoW extent size hint is set, which you really want.
+ * Third, EFIs cannot currently be automatically relogged into newer
+ * transactions to advance the log tail.  Fourth, stuffing the log full of
+ * EFIs places an upper bound on the number of CoW allocations that can be
+ * held filesystem-wide at any given time.  Recording them in the refcount
+ * btree doesn't require us to maintain any state in memory and doesn't pin
+ * the log.
+ */
+/*
+ * Adjust the refcounts of CoW allocations.  These allocations are "magic"
+ * in that they're not referenced anywhere else in the filesystem, so we
+ * stash them in the refcount btree with a refcount of 1 until either file
+ * remapping (or CoW cancellation) happens.
+ */
+STATIC int
+xfs_refcount_adjust_cow_extents(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_refcount_irec        ext, tmp;
+       int                             error;
+       int                             found_rec, found_tmp;
+
+       if (aglen == 0)
+               return 0;
+
+       /* Find any overlapping refcount records */
+       error = xfs_refcount_lookup_ge(cur, agbno, &found_rec);
+       if (error)
+               goto out_error;
+       error = xfs_refcount_get_rec(cur, &ext, &found_rec);
+       if (error)
+               goto out_error;
+       if (!found_rec) {
+               ext.rc_startblock = cur->bc_mp->m_sb.sb_agblocks +
+                               XFS_REFC_COW_START;
+               ext.rc_blockcount = 0;
+               ext.rc_refcount = 0;
+       }
+
+       switch (adj) {
+       case XFS_REFCOUNT_ADJUST_COW_ALLOC:
+               /* Adding a CoW reservation, there should be nothing here. */
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               ext.rc_startblock >= agbno + aglen, out_error);
+
+               tmp.rc_startblock = agbno;
+               tmp.rc_blockcount = aglen;
+               tmp.rc_refcount = 1;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &tmp);
+
+               error = xfs_refcount_insert(cur, &tmp,
+                               &found_tmp);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               found_tmp == 1, out_error);
+               break;
+       case XFS_REFCOUNT_ADJUST_COW_FREE:
+               /* Removing a CoW reservation, there should be one extent. */
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_startblock == agbno, out_error);
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_blockcount == aglen, out_error);
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                       ext.rc_refcount == 1, out_error);
+
+               ext.rc_refcount = 0;
+               trace_xfs_refcount_modify_extent(cur->bc_mp,
+                               cur->bc_private.a.agno, &ext);
+               error = xfs_refcount_delete(cur, &found_rec);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(cur->bc_mp,
+                               found_rec == 1, out_error);
+               break;
+       default:
+               ASSERT(0);
+       }
+
+       return error;
+out_error:
+       trace_xfs_refcount_modify_extent_error(cur->bc_mp,
+                       cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Add or remove refcount btree entries for CoW reservations.
+ */
+STATIC int
+xfs_refcount_adjust_cow(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       enum xfs_refc_adjust_op adj,
+       struct xfs_defer_ops    *dfops)
+{
+       bool                    shape_changed;
+       int                     error;
+
+       agbno += XFS_REFC_COW_START;
+
+       /*
+        * Ensure that no rcextents cross the boundary of the adjustment range.
+        */
+       error = xfs_refcount_split_extent(cur, agbno, &shape_changed);
+       if (error)
+               goto out_error;
+
+       error = xfs_refcount_split_extent(cur, agbno + aglen, &shape_changed);
+       if (error)
+               goto out_error;
+
+       /*
+        * Try to merge with the left or right extents of the range.
+        */
+       error = xfs_refcount_merge_extents(cur, &agbno, &aglen, adj,
+                       XFS_FIND_RCEXT_COW, &shape_changed);
+       if (error)
+               goto out_error;
+
+       /* Now that we've taken care of the ends, adjust the middle extents */
+       error = xfs_refcount_adjust_cow_extents(cur, agbno, aglen, adj,
+                       dfops, NULL);
+       if (error)
+               goto out_error;
+
+       return 0;
+
+out_error:
+       trace_xfs_refcount_adjust_cow_error(cur->bc_mp, cur->bc_private.a.agno,
+                       error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Record a CoW allocation in the refcount btree.
+ */
+STATIC int
+__xfs_refcount_cow_alloc(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       struct xfs_defer_ops    *dfops)
+{
+       int                     error;
+
+       trace_xfs_refcount_cow_increase(rcur->bc_mp, rcur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* Add refcount btree reservation */
+       error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
+                       XFS_REFCOUNT_ADJUST_COW_ALLOC, dfops);
+       if (error)
+               return error;
+
+       /* Add rmap entry */
+       if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
+               error = xfs_rmap_alloc_extent(rcur->bc_mp, dfops,
+                               rcur->bc_private.a.agno,
+                               agbno, aglen, XFS_RMAP_OWN_COW);
+               if (error)
+                       return error;
+       }
+
+       return error;
+}
+
+/*
+ * Remove a CoW allocation from the refcount btree.
+ */
+STATIC int
+__xfs_refcount_cow_free(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       struct xfs_defer_ops    *dfops)
+{
+       int                     error;
+
+       trace_xfs_refcount_cow_decrease(rcur->bc_mp, rcur->bc_private.a.agno,
+                       agbno, aglen);
+
+       /* Remove refcount btree reservation */
+       error = xfs_refcount_adjust_cow(rcur, agbno, aglen,
+                       XFS_REFCOUNT_ADJUST_COW_FREE, dfops);
+       if (error)
+               return error;
+
+       /* Remove rmap entry */
+       if (xfs_sb_version_hasrmapbt(&rcur->bc_mp->m_sb)) {
+               error = xfs_rmap_free_extent(rcur->bc_mp, dfops,
+                               rcur->bc_private.a.agno,
+                               agbno, aglen, XFS_RMAP_OWN_COW);
+               if (error)
+                       return error;
+       }
+
+       return error;
+}
+
+/* Record a CoW staging extent in the refcount btree. */
+int
+xfs_refcount_alloc_cow_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       xfs_fsblock_t                   fsb,
+       xfs_extlen_t                    len)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_ALLOC_COW,
+                       fsb, len);
+}
+
+/* Forget a CoW staging event in the refcount btree. */
+int
+xfs_refcount_free_cow_extent(
+       struct xfs_mount                *mp,
+       struct xfs_defer_ops            *dfops,
+       xfs_fsblock_t                   fsb,
+       xfs_extlen_t                    len)
+{
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       return __xfs_refcount_add(mp, dfops, XFS_REFCOUNT_FREE_COW,
+                       fsb, len);
+}
+
+struct xfs_refcount_recovery {
+       struct list_head                rr_list;
+       struct xfs_refcount_irec        rr_rrec;
+};
+
+/* Stuff an extent on the recovery list. */
+STATIC int
+xfs_refcount_recover_extent(
+       struct xfs_btree_cur            *cur,
+       union xfs_btree_rec             *rec,
+       void                            *priv)
+{
+       struct list_head                *debris = priv;
+       struct xfs_refcount_recovery    *rr;
+
+       if (be32_to_cpu(rec->refc.rc_refcount) != 1)
+               return -EFSCORRUPTED;
+
+       rr = kmem_alloc(sizeof(struct xfs_refcount_recovery), KM_SLEEP);
+       xfs_refcount_btrec_to_irec(rec, &rr->rr_rrec);
+       list_add_tail(&rr->rr_list, debris);
+
+       return 0;
+}
+
+/* Find and remove leftover CoW reservations. */
+int
+xfs_refcount_recover_cow_leftovers(
+       struct xfs_mount                *mp,
+       xfs_agnumber_t                  agno)
+{
+       struct xfs_trans                *tp;
+       struct xfs_btree_cur            *cur;
+       struct xfs_buf                  *agbp;
+       struct xfs_refcount_recovery    *rr, *n;
+       struct list_head                debris;
+       union xfs_btree_irec            low;
+       union xfs_btree_irec            high;
+       struct xfs_defer_ops            dfops;
+       xfs_fsblock_t                   fsb;
+       xfs_agblock_t                   agbno;
+       int                             error;
+
+       if (mp->m_sb.sb_agblocks >= XFS_REFC_COW_START)
+               return -EOPNOTSUPP;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+       cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+
+       /* Find all the leftover CoW staging extents. */
+       INIT_LIST_HEAD(&debris);
+       memset(&low, 0, sizeof(low));
+       memset(&high, 0, sizeof(high));
+       low.rc.rc_startblock = XFS_REFC_COW_START;
+       high.rc.rc_startblock = -1U;
+       error = xfs_btree_query_range(cur, &low, &high,
+                       xfs_refcount_recover_extent, &debris);
+       if (error)
+               goto out_cursor;
+       xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+       xfs_buf_relse(agbp);
+
+       /* Now iterate the list to free the leftovers */
+       list_for_each_entry(rr, &debris, rr_list) {
+               /* Set up transaction. */
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
+               if (error)
+                       goto out_free;
+
+               trace_xfs_refcount_recover_extent(mp, agno, &rr->rr_rrec);
+
+               /* Free the orphan record */
+               xfs_defer_init(&dfops, &fsb);
+               agbno = rr->rr_rrec.rc_startblock - XFS_REFC_COW_START;
+               fsb = XFS_AGB_TO_FSB(mp, agno, agbno);
+               error = xfs_refcount_free_cow_extent(mp, &dfops, fsb,
+                               rr->rr_rrec.rc_blockcount);
+               if (error)
+                       goto out_defer;
+
+               /* Free the block. */
+               xfs_bmap_add_free(mp, &dfops, fsb,
+                               rr->rr_rrec.rc_blockcount, NULL);
+
+               error = xfs_defer_finish(&tp, &dfops, NULL);
+               if (error)
+                       goto out_defer;
+
+               error = xfs_trans_commit(tp);
+               if (error)
+                       goto out_free;
+       }
+
+out_free:
+       /* Free the leftover list */
+       list_for_each_entry_safe(rr, n, &debris, rr_list) {
+               list_del(&rr->rr_list);
+               kmem_free(rr);
+       }
+       return error;
+
+out_cursor:
+       xfs_btree_del_cursor(cur, XFS_BTREE_ERROR);
+       xfs_buf_relse(agbp);
+       goto out_free;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       goto out_free;
+}
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
new file mode 100644 (file)
index 0000000..098dc66
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFCOUNT_H__
+#define __XFS_REFCOUNT_H__
+
+extern int xfs_refcount_lookup_le(struct xfs_btree_cur *cur,
+               xfs_agblock_t bno, int *stat);
+extern int xfs_refcount_lookup_ge(struct xfs_btree_cur *cur,
+               xfs_agblock_t bno, int *stat);
+extern int xfs_refcount_get_rec(struct xfs_btree_cur *cur,
+               struct xfs_refcount_irec *irec, int *stat);
+
+enum xfs_refcount_intent_type {
+       XFS_REFCOUNT_INCREASE = 1,
+       XFS_REFCOUNT_DECREASE,
+       XFS_REFCOUNT_ALLOC_COW,
+       XFS_REFCOUNT_FREE_COW,
+};
+
+struct xfs_refcount_intent {
+       struct list_head                        ri_list;
+       enum xfs_refcount_intent_type           ri_type;
+       xfs_fsblock_t                           ri_startblock;
+       xfs_extlen_t                            ri_blockcount;
+};
+
+extern int xfs_refcount_increase_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
+extern int xfs_refcount_decrease_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, struct xfs_bmbt_irec *irec);
+
+extern void xfs_refcount_finish_one_cleanup(struct xfs_trans *tp,
+               struct xfs_btree_cur *rcur, int error);
+extern int xfs_refcount_finish_one(struct xfs_trans *tp,
+               struct xfs_defer_ops *dfops, enum xfs_refcount_intent_type type,
+               xfs_fsblock_t startblock, xfs_extlen_t blockcount,
+               xfs_fsblock_t *new_fsb, xfs_extlen_t *new_len,
+               struct xfs_btree_cur **pcur);
+
+extern int xfs_refcount_find_shared(struct xfs_btree_cur *cur,
+               xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno,
+               xfs_extlen_t *flen, bool find_end_of_shared);
+
+extern int xfs_refcount_alloc_cow_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
+               xfs_extlen_t len);
+extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
+               struct xfs_defer_ops *dfops, xfs_fsblock_t fsb,
+               xfs_extlen_t len);
+extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
+               xfs_agnumber_t agno);
+
+#endif /* __XFS_REFCOUNT_H__ */
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c
new file mode 100644 (file)
index 0000000..453bb27
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_sb.h"
+#include "xfs_mount.h"
+#include "xfs_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_alloc.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_cksum.h"
+#include "xfs_trans.h"
+#include "xfs_bit.h"
+#include "xfs_rmap.h"
+
+static struct xfs_btree_cur *
+xfs_refcountbt_dup_cursor(
+       struct xfs_btree_cur    *cur)
+{
+       return xfs_refcountbt_init_cursor(cur->bc_mp, cur->bc_tp,
+                       cur->bc_private.a.agbp, cur->bc_private.a.agno,
+                       cur->bc_private.a.dfops);
+}
+
+STATIC void
+xfs_refcountbt_set_root(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *ptr,
+       int                     inc)
+{
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       xfs_agnumber_t          seqno = be32_to_cpu(agf->agf_seqno);
+       struct xfs_perag        *pag = xfs_perag_get(cur->bc_mp, seqno);
+
+       ASSERT(ptr->s != 0);
+
+       agf->agf_refcount_root = ptr->s;
+       be32_add_cpu(&agf->agf_refcount_level, inc);
+       pag->pagf_refcount_level += inc;
+       xfs_perag_put(pag);
+
+       xfs_alloc_log_agf(cur->bc_tp, agbp,
+                       XFS_AGF_REFCOUNT_ROOT | XFS_AGF_REFCOUNT_LEVEL);
+}
+
+STATIC int
+xfs_refcountbt_alloc_block(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *start,
+       union xfs_btree_ptr     *new,
+       int                     *stat)
+{
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       struct xfs_alloc_arg    args;           /* block allocation args */
+       int                     error;          /* error return value */
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ENTRY);
+
+       memset(&args, 0, sizeof(args));
+       args.tp = cur->bc_tp;
+       args.mp = cur->bc_mp;
+       args.type = XFS_ALLOCTYPE_NEAR_BNO;
+       args.fsbno = XFS_AGB_TO_FSB(cur->bc_mp, cur->bc_private.a.agno,
+                       xfs_refc_block(args.mp));
+       args.firstblock = args.fsbno;
+       xfs_rmap_ag_owner(&args.oinfo, XFS_RMAP_OWN_REFC);
+       args.minlen = args.maxlen = args.prod = 1;
+       args.resv = XFS_AG_RESV_METADATA;
+
+       error = xfs_alloc_vextent(&args);
+       if (error)
+               goto out_error;
+       trace_xfs_refcountbt_alloc_block(cur->bc_mp, cur->bc_private.a.agno,
+                       args.agbno, 1);
+       if (args.fsbno == NULLFSBLOCK) {
+               XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+               *stat = 0;
+               return 0;
+       }
+       ASSERT(args.agno == cur->bc_private.a.agno);
+       ASSERT(args.len == 1);
+
+       new->s = cpu_to_be32(args.agbno);
+       be32_add_cpu(&agf->agf_refcount_blocks, 1);
+       xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_BLOCKS);
+
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_EXIT);
+       *stat = 1;
+       return 0;
+
+out_error:
+       XFS_BTREE_TRACE_CURSOR(cur, XBT_ERROR);
+       return error;
+}
+
+STATIC int
+xfs_refcountbt_free_block(
+       struct xfs_btree_cur    *cur,
+       struct xfs_buf          *bp)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_buf          *agbp = cur->bc_private.a.agbp;
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       xfs_fsblock_t           fsbno = XFS_DADDR_TO_FSB(mp, XFS_BUF_ADDR(bp));
+       struct xfs_owner_info   oinfo;
+       int                     error;
+
+       trace_xfs_refcountbt_free_block(cur->bc_mp, cur->bc_private.a.agno,
+                       XFS_FSB_TO_AGBNO(cur->bc_mp, fsbno), 1);
+       xfs_rmap_ag_owner(&oinfo, XFS_RMAP_OWN_REFC);
+       be32_add_cpu(&agf->agf_refcount_blocks, -1);
+       xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_REFCOUNT_BLOCKS);
+       error = xfs_free_extent(cur->bc_tp, fsbno, 1, &oinfo,
+                       XFS_AG_RESV_METADATA);
+       if (error)
+               return error;
+
+       return error;
+}
+
+STATIC int
+xfs_refcountbt_get_minrecs(
+       struct xfs_btree_cur    *cur,
+       int                     level)
+{
+       return cur->bc_mp->m_refc_mnr[level != 0];
+}
+
+STATIC int
+xfs_refcountbt_get_maxrecs(
+       struct xfs_btree_cur    *cur,
+       int                     level)
+{
+       return cur->bc_mp->m_refc_mxr[level != 0];
+}
+
+STATIC void
+xfs_refcountbt_init_key_from_rec(
+       union xfs_btree_key     *key,
+       union xfs_btree_rec     *rec)
+{
+       key->refc.rc_startblock = rec->refc.rc_startblock;
+}
+
+STATIC void
+xfs_refcountbt_init_high_key_from_rec(
+       union xfs_btree_key     *key,
+       union xfs_btree_rec     *rec)
+{
+       __u32                   x;
+
+       x = be32_to_cpu(rec->refc.rc_startblock);
+       x += be32_to_cpu(rec->refc.rc_blockcount) - 1;
+       key->refc.rc_startblock = cpu_to_be32(x);
+}
+
+STATIC void
+xfs_refcountbt_init_rec_from_cur(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *rec)
+{
+       rec->refc.rc_startblock = cpu_to_be32(cur->bc_rec.rc.rc_startblock);
+       rec->refc.rc_blockcount = cpu_to_be32(cur->bc_rec.rc.rc_blockcount);
+       rec->refc.rc_refcount = cpu_to_be32(cur->bc_rec.rc.rc_refcount);
+}
+
+STATIC void
+xfs_refcountbt_init_ptr_from_cur(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_ptr     *ptr)
+{
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(cur->bc_private.a.agbp);
+
+       ASSERT(cur->bc_private.a.agno == be32_to_cpu(agf->agf_seqno));
+       ASSERT(agf->agf_refcount_root != 0);
+
+       ptr->s = agf->agf_refcount_root;
+}
+
+STATIC __int64_t
+xfs_refcountbt_key_diff(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *key)
+{
+       struct xfs_refcount_irec        *rec = &cur->bc_rec.rc;
+       struct xfs_refcount_key         *kp = &key->refc;
+
+       return (__int64_t)be32_to_cpu(kp->rc_startblock) - rec->rc_startblock;
+}
+
+STATIC __int64_t
+xfs_refcountbt_diff_two_keys(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *k1,
+       union xfs_btree_key     *k2)
+{
+       return (__int64_t)be32_to_cpu(k1->refc.rc_startblock) -
+                         be32_to_cpu(k2->refc.rc_startblock);
+}
+
+STATIC bool
+xfs_refcountbt_verify(
+       struct xfs_buf          *bp)
+{
+       struct xfs_mount        *mp = bp->b_target->bt_mount;
+       struct xfs_btree_block  *block = XFS_BUF_TO_BLOCK(bp);
+       struct xfs_perag        *pag = bp->b_pag;
+       unsigned int            level;
+
+       if (block->bb_magic != cpu_to_be32(XFS_REFC_CRC_MAGIC))
+               return false;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return false;
+       if (!xfs_btree_sblock_v5hdr_verify(bp))
+               return false;
+
+       level = be16_to_cpu(block->bb_level);
+       if (pag && pag->pagf_init) {
+               if (level >= pag->pagf_refcount_level)
+                       return false;
+       } else if (level >= mp->m_refc_maxlevels)
+               return false;
+
+       return xfs_btree_sblock_verify(bp, mp->m_refc_mxr[level != 0]);
+}
+
+STATIC void
+xfs_refcountbt_read_verify(
+       struct xfs_buf  *bp)
+{
+       if (!xfs_btree_sblock_verify_crc(bp))
+               xfs_buf_ioerror(bp, -EFSBADCRC);
+       else if (!xfs_refcountbt_verify(bp))
+               xfs_buf_ioerror(bp, -EFSCORRUPTED);
+
+       if (bp->b_error) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               xfs_verifier_error(bp);
+       }
+}
+
+STATIC void
+xfs_refcountbt_write_verify(
+       struct xfs_buf  *bp)
+{
+       if (!xfs_refcountbt_verify(bp)) {
+               trace_xfs_btree_corrupt(bp, _RET_IP_);
+               xfs_buf_ioerror(bp, -EFSCORRUPTED);
+               xfs_verifier_error(bp);
+               return;
+       }
+       xfs_btree_sblock_calc_crc(bp);
+
+}
+
+const struct xfs_buf_ops xfs_refcountbt_buf_ops = {
+       .name                   = "xfs_refcountbt",
+       .verify_read            = xfs_refcountbt_read_verify,
+       .verify_write           = xfs_refcountbt_write_verify,
+};
+
+#if defined(DEBUG) || defined(XFS_WARN)
+STATIC int
+xfs_refcountbt_keys_inorder(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_key     *k1,
+       union xfs_btree_key     *k2)
+{
+       return be32_to_cpu(k1->refc.rc_startblock) <
+              be32_to_cpu(k2->refc.rc_startblock);
+}
+
+STATIC int
+xfs_refcountbt_recs_inorder(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *r1,
+       union xfs_btree_rec     *r2)
+{
+       return  be32_to_cpu(r1->refc.rc_startblock) +
+               be32_to_cpu(r1->refc.rc_blockcount) <=
+               be32_to_cpu(r2->refc.rc_startblock);
+}
+#endif
+
+static const struct xfs_btree_ops xfs_refcountbt_ops = {
+       .rec_len                = sizeof(struct xfs_refcount_rec),
+       .key_len                = sizeof(struct xfs_refcount_key),
+
+       .dup_cursor             = xfs_refcountbt_dup_cursor,
+       .set_root               = xfs_refcountbt_set_root,
+       .alloc_block            = xfs_refcountbt_alloc_block,
+       .free_block             = xfs_refcountbt_free_block,
+       .get_minrecs            = xfs_refcountbt_get_minrecs,
+       .get_maxrecs            = xfs_refcountbt_get_maxrecs,
+       .init_key_from_rec      = xfs_refcountbt_init_key_from_rec,
+       .init_high_key_from_rec = xfs_refcountbt_init_high_key_from_rec,
+       .init_rec_from_cur      = xfs_refcountbt_init_rec_from_cur,
+       .init_ptr_from_cur      = xfs_refcountbt_init_ptr_from_cur,
+       .key_diff               = xfs_refcountbt_key_diff,
+       .buf_ops                = &xfs_refcountbt_buf_ops,
+       .diff_two_keys          = xfs_refcountbt_diff_two_keys,
+#if defined(DEBUG) || defined(XFS_WARN)
+       .keys_inorder           = xfs_refcountbt_keys_inorder,
+       .recs_inorder           = xfs_refcountbt_recs_inorder,
+#endif
+};
+
+/*
+ * Allocate a new refcount btree cursor.
+ */
+struct xfs_btree_cur *
+xfs_refcountbt_init_cursor(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       struct xfs_buf          *agbp,
+       xfs_agnumber_t          agno,
+       struct xfs_defer_ops    *dfops)
+{
+       struct xfs_agf          *agf = XFS_BUF_TO_AGF(agbp);
+       struct xfs_btree_cur    *cur;
+
+       ASSERT(agno != NULLAGNUMBER);
+       ASSERT(agno < mp->m_sb.sb_agcount);
+       cur = kmem_zone_zalloc(xfs_btree_cur_zone, KM_NOFS);
+
+       cur->bc_tp = tp;
+       cur->bc_mp = mp;
+       cur->bc_btnum = XFS_BTNUM_REFC;
+       cur->bc_blocklog = mp->m_sb.sb_blocklog;
+       cur->bc_ops = &xfs_refcountbt_ops;
+
+       cur->bc_nlevels = be32_to_cpu(agf->agf_refcount_level);
+
+       cur->bc_private.a.agbp = agbp;
+       cur->bc_private.a.agno = agno;
+       cur->bc_private.a.dfops = dfops;
+       cur->bc_flags |= XFS_BTREE_CRC_BLOCKS;
+
+       cur->bc_private.a.priv.refc.nr_ops = 0;
+       cur->bc_private.a.priv.refc.shape_changes = 0;
+
+       return cur;
+}
+
+/*
+ * Calculate the number of records in a refcount btree block.
+ */
+int
+xfs_refcountbt_maxrecs(
+       struct xfs_mount        *mp,
+       int                     blocklen,
+       bool                    leaf)
+{
+       blocklen -= XFS_REFCOUNT_BLOCK_LEN;
+
+       if (leaf)
+               return blocklen / sizeof(struct xfs_refcount_rec);
+       return blocklen / (sizeof(struct xfs_refcount_key) +
+                          sizeof(xfs_refcount_ptr_t));
+}
+
+/* Compute the maximum height of a refcount btree. */
+void
+xfs_refcountbt_compute_maxlevels(
+       struct xfs_mount                *mp)
+{
+       mp->m_refc_maxlevels = xfs_btree_compute_maxlevels(mp,
+                       mp->m_refc_mnr, mp->m_sb.sb_agblocks);
+}
+
+/* Calculate the refcount btree size for some records. */
+xfs_extlen_t
+xfs_refcountbt_calc_size(
+       struct xfs_mount        *mp,
+       unsigned long long      len)
+{
+       return xfs_btree_calc_size(mp, mp->m_refc_mnr, len);
+}
+
+/*
+ * Calculate the maximum refcount btree size.
+ */
+xfs_extlen_t
+xfs_refcountbt_max_size(
+       struct xfs_mount        *mp)
+{
+       /* Bail out if we're uninitialized, which can happen in mkfs. */
+       if (mp->m_refc_mxr[0] == 0)
+               return 0;
+
+       return xfs_refcountbt_calc_size(mp, mp->m_sb.sb_agblocks);
+}
+
+/*
+ * Figure out how many blocks to reserve and how many are used by this btree.
+ */
+int
+xfs_refcountbt_calc_reserves(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_extlen_t            *ask,
+       xfs_extlen_t            *used)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_agf          *agf;
+       xfs_extlen_t            tree_len;
+       int                     error;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       *ask += xfs_refcountbt_max_size(mp);
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       agf = XFS_BUF_TO_AGF(agbp);
+       tree_len = be32_to_cpu(agf->agf_refcount_blocks);
+       xfs_buf_relse(agbp);
+
+       *used += tree_len;
+
+       return error;
+}
diff --git a/fs/xfs/libxfs/xfs_refcount_btree.h b/fs/xfs/libxfs/xfs_refcount_btree.h
new file mode 100644 (file)
index 0000000..3be7768
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFCOUNT_BTREE_H__
+#define        __XFS_REFCOUNT_BTREE_H__
+
+/*
+ * Reference Count Btree on-disk structures
+ */
+
+struct xfs_buf;
+struct xfs_btree_cur;
+struct xfs_mount;
+
+/*
+ * Btree block header size
+ */
+#define XFS_REFCOUNT_BLOCK_LEN XFS_BTREE_SBLOCK_CRC_LEN
+
+/*
+ * Record, key, and pointer address macros for btree blocks.
+ *
+ * (note that some of these may appear unused, but they are used in userspace)
+ */
+#define XFS_REFCOUNT_REC_ADDR(block, index) \
+       ((struct xfs_refcount_rec *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                (((index) - 1) * sizeof(struct xfs_refcount_rec))))
+
+#define XFS_REFCOUNT_KEY_ADDR(block, index) \
+       ((struct xfs_refcount_key *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                ((index) - 1) * sizeof(struct xfs_refcount_key)))
+
+#define XFS_REFCOUNT_PTR_ADDR(block, index, maxrecs) \
+       ((xfs_refcount_ptr_t *) \
+               ((char *)(block) + \
+                XFS_REFCOUNT_BLOCK_LEN + \
+                (maxrecs) * sizeof(struct xfs_refcount_key) + \
+                ((index) - 1) * sizeof(xfs_refcount_ptr_t)))
+
+extern struct xfs_btree_cur *xfs_refcountbt_init_cursor(struct xfs_mount *mp,
+               struct xfs_trans *tp, struct xfs_buf *agbp, xfs_agnumber_t agno,
+               struct xfs_defer_ops *dfops);
+extern int xfs_refcountbt_maxrecs(struct xfs_mount *mp, int blocklen,
+               bool leaf);
+extern void xfs_refcountbt_compute_maxlevels(struct xfs_mount *mp);
+
+extern xfs_extlen_t xfs_refcountbt_calc_size(struct xfs_mount *mp,
+               unsigned long long len);
+extern xfs_extlen_t xfs_refcountbt_max_size(struct xfs_mount *mp);
+
+extern int xfs_refcountbt_calc_reserves(struct xfs_mount *mp,
+               xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
+
+#endif /* __XFS_REFCOUNT_BTREE_H__ */
index 73d0540..3a8cc71 100644 (file)
@@ -148,6 +148,37 @@ done:
        return error;
 }
 
+STATIC int
+xfs_rmap_delete(
+       struct xfs_btree_cur    *rcur,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            len,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags)
+{
+       int                     i;
+       int                     error;
+
+       trace_xfs_rmap_delete(rcur->bc_mp, rcur->bc_private.a.agno, agbno,
+                       len, owner, offset, flags);
+
+       error = xfs_rmap_lookup_eq(rcur, agbno, len, owner, offset, flags, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+
+       error = xfs_btree_delete(rcur, &i);
+       if (error)
+               goto done;
+       XFS_WANT_CORRUPTED_GOTO(rcur->bc_mp, i == 1, done);
+done:
+       if (error)
+               trace_xfs_rmap_delete_error(rcur->bc_mp,
+                               rcur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
 static int
 xfs_rmap_btrec_to_irec(
        union xfs_btree_rec     *rec,
@@ -180,6 +211,160 @@ xfs_rmap_get_rec(
        return xfs_rmap_btrec_to_irec(rec, irec);
 }
 
+struct xfs_find_left_neighbor_info {
+       struct xfs_rmap_irec    high;
+       struct xfs_rmap_irec    *irec;
+       int                     *stat;
+};
+
+/* For each rmap given, figure out if it matches the key we want. */
+STATIC int
+xfs_rmap_find_left_neighbor_helper(
+       struct xfs_btree_cur    *cur,
+       struct xfs_rmap_irec    *rec,
+       void                    *priv)
+{
+       struct xfs_find_left_neighbor_info      *info = priv;
+
+       trace_xfs_rmap_find_left_neighbor_candidate(cur->bc_mp,
+                       cur->bc_private.a.agno, rec->rm_startblock,
+                       rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
+                       rec->rm_flags);
+
+       if (rec->rm_owner != info->high.rm_owner)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+       if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
+           !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
+           rec->rm_offset + rec->rm_blockcount - 1 != info->high.rm_offset)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+
+       *info->irec = *rec;
+       *info->stat = 1;
+       return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/*
+ * Find the record to the left of the given extent, being careful only to
+ * return a match with the same owner and adjacent physical and logical
+ * block ranges.
+ */
+int
+xfs_rmap_find_left_neighbor(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags,
+       struct xfs_rmap_irec    *irec,
+       int                     *stat)
+{
+       struct xfs_find_left_neighbor_info      info;
+       int                     error;
+
+       *stat = 0;
+       if (bno == 0)
+               return 0;
+       info.high.rm_startblock = bno - 1;
+       info.high.rm_owner = owner;
+       if (!XFS_RMAP_NON_INODE_OWNER(owner) &&
+           !(flags & XFS_RMAP_BMBT_BLOCK)) {
+               if (offset == 0)
+                       return 0;
+               info.high.rm_offset = offset - 1;
+       } else
+               info.high.rm_offset = 0;
+       info.high.rm_flags = flags;
+       info.high.rm_blockcount = 0;
+       info.irec = irec;
+       info.stat = stat;
+
+       trace_xfs_rmap_find_left_neighbor_query(cur->bc_mp,
+                       cur->bc_private.a.agno, bno, 0, owner, offset, flags);
+
+       error = xfs_rmap_query_range(cur, &info.high, &info.high,
+                       xfs_rmap_find_left_neighbor_helper, &info);
+       if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+               error = 0;
+       if (*stat)
+               trace_xfs_rmap_find_left_neighbor_result(cur->bc_mp,
+                               cur->bc_private.a.agno, irec->rm_startblock,
+                               irec->rm_blockcount, irec->rm_owner,
+                               irec->rm_offset, irec->rm_flags);
+       return error;
+}
+
+/* For each rmap given, figure out if it matches the key we want. */
+STATIC int
+xfs_rmap_lookup_le_range_helper(
+       struct xfs_btree_cur    *cur,
+       struct xfs_rmap_irec    *rec,
+       void                    *priv)
+{
+       struct xfs_find_left_neighbor_info      *info = priv;
+
+       trace_xfs_rmap_lookup_le_range_candidate(cur->bc_mp,
+                       cur->bc_private.a.agno, rec->rm_startblock,
+                       rec->rm_blockcount, rec->rm_owner, rec->rm_offset,
+                       rec->rm_flags);
+
+       if (rec->rm_owner != info->high.rm_owner)
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+       if (!XFS_RMAP_NON_INODE_OWNER(rec->rm_owner) &&
+           !(rec->rm_flags & XFS_RMAP_BMBT_BLOCK) &&
+           (rec->rm_offset > info->high.rm_offset ||
+            rec->rm_offset + rec->rm_blockcount <= info->high.rm_offset))
+               return XFS_BTREE_QUERY_RANGE_CONTINUE;
+
+       *info->irec = *rec;
+       *info->stat = 1;
+       return XFS_BTREE_QUERY_RANGE_ABORT;
+}
+
+/*
+ * Find the record to the left of the given extent, being careful only to
+ * return a match with the same owner and overlapping physical and logical
+ * block ranges.  This is the overlapping-interval version of
+ * xfs_rmap_lookup_le.
+ */
+int
+xfs_rmap_lookup_le_range(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       uint64_t                owner,
+       uint64_t                offset,
+       unsigned int            flags,
+       struct xfs_rmap_irec    *irec,
+       int                     *stat)
+{
+       struct xfs_find_left_neighbor_info      info;
+       int                     error;
+
+       info.high.rm_startblock = bno;
+       info.high.rm_owner = owner;
+       if (!XFS_RMAP_NON_INODE_OWNER(owner) && !(flags & XFS_RMAP_BMBT_BLOCK))
+               info.high.rm_offset = offset;
+       else
+               info.high.rm_offset = 0;
+       info.high.rm_flags = flags;
+       info.high.rm_blockcount = 0;
+       *stat = 0;
+       info.irec = irec;
+       info.stat = stat;
+
+       trace_xfs_rmap_lookup_le_range(cur->bc_mp,
+                       cur->bc_private.a.agno, bno, 0, owner, offset, flags);
+       error = xfs_rmap_query_range(cur, &info.high, &info.high,
+                       xfs_rmap_lookup_le_range_helper, &info);
+       if (error == XFS_BTREE_QUERY_RANGE_ABORT)
+               error = 0;
+       if (*stat)
+               trace_xfs_rmap_lookup_le_range_result(cur->bc_mp,
+                               cur->bc_private.a.agno, irec->rm_startblock,
+                               irec->rm_blockcount, irec->rm_owner,
+                               irec->rm_offset, irec->rm_flags);
+       return error;
+}
+
 /*
  * Find the extent in the rmap btree and remove it.
  *
@@ -1093,123 +1278,816 @@ done:
        return error;
 }
 
-#undef NEW
-#undef LEFT
-#undef RIGHT
-#undef PREV
-
-struct xfs_rmap_query_range_info {
-       xfs_rmap_query_range_fn fn;
-       void                            *priv;
-};
-
-/* Format btree record and pass to our callback. */
+/*
+ * Convert an unwritten extent to a real extent or vice versa.  If there is no
+ * possibility of overlapping extents, delegate to the simpler convert
+ * function.
+ */
 STATIC int
-xfs_rmap_query_range_helper(
+xfs_rmap_convert_shared(
        struct xfs_btree_cur    *cur,
-       union xfs_btree_rec     *rec,
-       void                    *priv)
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
 {
-       struct xfs_rmap_query_range_info        *query = priv;
-       struct xfs_rmap_irec                    irec;
-       int                                     error;
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    r[4];   /* neighbor extent entries */
+                                       /* left is 0, right is 1, prev is 2 */
+                                       /* new is 3 */
+       uint64_t                owner;
+       uint64_t                offset;
+       uint64_t                new_endoff;
+       unsigned int            oldext;
+       unsigned int            newext;
+       unsigned int            flags = 0;
+       int                     i;
+       int                     state = 0;
+       int                     error;
 
-       error = xfs_rmap_btrec_to_irec(rec, &irec);
-       if (error)
-               return error;
-       return query->fn(cur, &irec, query->priv);
-}
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       ASSERT(!(XFS_RMAP_NON_INODE_OWNER(owner) ||
+                       (flags & (XFS_RMAP_ATTR_FORK | XFS_RMAP_BMBT_BLOCK))));
+       oldext = unwritten ? XFS_RMAP_UNWRITTEN : 0;
+       new_endoff = offset + len;
+       trace_xfs_rmap_convert(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
 
-/* Find all rmaps between two keys. */
-int
-xfs_rmap_query_range(
-       struct xfs_btree_cur            *cur,
-       struct xfs_rmap_irec            *low_rec,
-       struct xfs_rmap_irec            *high_rec,
-       xfs_rmap_query_range_fn fn,
-       void                            *priv)
-{
-       union xfs_btree_irec            low_brec;
-       union xfs_btree_irec            high_brec;
-       struct xfs_rmap_query_range_info        query;
+       /*
+        * For the initial lookup, look for and exact match or the left-adjacent
+        * record for our insertion point. This will also give us the record for
+        * start block contiguity tests.
+        */
+       error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
+                       &PREV, &i);
+       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
 
-       low_brec.r = *low_rec;
-       high_brec.r = *high_rec;
-       query.priv = priv;
-       query.fn = fn;
-       return xfs_btree_query_range(cur, &low_brec, &high_brec,
-                       xfs_rmap_query_range_helper, &query);
-}
+       ASSERT(PREV.rm_offset <= offset);
+       ASSERT(PREV.rm_offset + PREV.rm_blockcount >= new_endoff);
+       ASSERT((PREV.rm_flags & XFS_RMAP_UNWRITTEN) == oldext);
+       newext = ~oldext & XFS_RMAP_UNWRITTEN;
 
-/* Clean up after calling xfs_rmap_finish_one. */
-void
-xfs_rmap_finish_one_cleanup(
-       struct xfs_trans        *tp,
-       struct xfs_btree_cur    *rcur,
-       int                     error)
-{
-       struct xfs_buf          *agbp;
+       /*
+        * Set flags determining what part of the previous oldext allocation
+        * extent is being replaced by a newext allocation.
+        */
+       if (PREV.rm_offset == offset)
+               state |= RMAP_LEFT_FILLING;
+       if (PREV.rm_offset + PREV.rm_blockcount == new_endoff)
+               state |= RMAP_RIGHT_FILLING;
 
-       if (rcur == NULL)
-               return;
-       agbp = rcur->bc_private.a.agbp;
-       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       /* Is there a left record that abuts our range? */
+       error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, newext,
+                       &LEFT, &i);
        if (error)
-               xfs_trans_brelse(tp, agbp);
-}
-
-/*
- * Process one of the deferred rmap operations.  We pass back the
- * btree cursor to maintain our lock on the rmapbt between calls.
- * This saves time and eliminates a buffer deadlock between the
- * superblock and the AGF because we'll always grab them in the same
- * order.
- */
-int
-xfs_rmap_finish_one(
-       struct xfs_trans                *tp,
-       enum xfs_rmap_intent_type       type,
-       __uint64_t                      owner,
-       int                             whichfork,
-       xfs_fileoff_t                   startoff,
-       xfs_fsblock_t                   startblock,
-       xfs_filblks_t                   blockcount,
-       xfs_exntst_t                    state,
-       struct xfs_btree_cur            **pcur)
-{
-       struct xfs_mount                *mp = tp->t_mountp;
-       struct xfs_btree_cur            *rcur;
-       struct xfs_buf                  *agbp = NULL;
-       int                             error = 0;
-       xfs_agnumber_t                  agno;
-       struct xfs_owner_info           oinfo;
-       xfs_agblock_t                   bno;
-       bool                            unwritten;
-
-       agno = XFS_FSB_TO_AGNO(mp, startblock);
-       ASSERT(agno != NULLAGNUMBER);
-       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+               goto done;
+       if (i) {
+               state |= RMAP_LEFT_VALID;
+               XFS_WANT_CORRUPTED_GOTO(mp,
+                               LEFT.rm_startblock + LEFT.rm_blockcount <= bno,
+                               done);
+               if (xfs_rmap_is_mergeable(&LEFT, owner, newext))
+                       state |= RMAP_LEFT_CONTIG;
+       }
 
-       trace_xfs_rmap_deferred(mp, agno, type, bno, owner, whichfork,
-                       startoff, blockcount, state);
+       /* Is there a right record that abuts our range? */
+       error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
+                       newext, &i);
+       if (error)
+               goto done;
+       if (i) {
+               state |= RMAP_RIGHT_VALID;
+               error = xfs_rmap_get_rec(cur, &RIGHT, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               XFS_WANT_CORRUPTED_GOTO(mp, bno + len <= RIGHT.rm_startblock,
+                               done);
+               trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
+                               cur->bc_private.a.agno, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (xfs_rmap_is_mergeable(&RIGHT, owner, newext))
+                       state |= RMAP_RIGHT_CONTIG;
+       }
 
-       if (XFS_TEST_ERROR(false, mp,
-                       XFS_ERRTAG_RMAP_FINISH_ONE,
-                       XFS_RANDOM_RMAP_FINISH_ONE))
-               return -EIO;
+       /* check that left + prev + right is not too long */
+       if ((state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+                        RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) ==
+           (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+            RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG) &&
+           (unsigned long)LEFT.rm_blockcount + len +
+            RIGHT.rm_blockcount > XFS_RMAP_LEN_MAX)
+               state &= ~RMAP_RIGHT_CONTIG;
 
+       trace_xfs_rmap_convert_state(mp, cur->bc_private.a.agno, state,
+                       _RET_IP_);
        /*
-        * If we haven't gotten a cursor or the cursor AG doesn't match
-        * the startblock, get one now.
+        * Switch out based on the FILLING and CONTIG state bits.
         */
-       rcur = *pcur;
-       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
-               xfs_rmap_finish_one_cleanup(tp, rcur, 0);
-               rcur = NULL;
-               *pcur = NULL;
-       }
-       if (rcur == NULL) {
-               /*
+       switch (state & (RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+                        RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG)) {
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG |
+            RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The left and right neighbors are both contiguous with new.
+                */
+               error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (error)
+                       goto done;
+               error = xfs_rmap_delete(cur, PREV.rm_startblock,
+                               PREV.rm_blockcount, PREV.rm_owner,
+                               PREV.rm_offset, PREV.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += PREV.rm_blockcount + RIGHT.rm_blockcount;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The left neighbor is contiguous, the right is not.
+                */
+               error = xfs_rmap_delete(cur, PREV.rm_startblock,
+                               PREV.rm_blockcount, PREV.rm_owner,
+                               PREV.rm_offset, PREV.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += PREV.rm_blockcount;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * The right neighbor is contiguous, the left is not.
+                */
+               error = xfs_rmap_delete(cur, RIGHT.rm_startblock,
+                               RIGHT.rm_blockcount, RIGHT.rm_owner,
+                               RIGHT.rm_offset, RIGHT.rm_flags);
+               if (error)
+                       goto done;
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += RIGHT.rm_blockcount;
+               NEW.rm_flags = RIGHT.rm_flags;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_FILLING:
+               /*
+                * Setting all of a previous oldext extent to newext.
+                * Neither the left nor right neighbors are contiguous with
+                * the new one.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_flags = newext;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG:
+               /*
+                * Setting the first part of a previous oldext extent to newext.
+                * The left neighbor is contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset += len;
+               NEW.rm_startblock += len;
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW = LEFT;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount += len;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING:
+               /*
+                * Setting the first part of a previous oldext extent to newext.
+                * The left neighbor is not contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset += len;
+               NEW.rm_startblock += len;
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_RIGHT_FILLING | RMAP_RIGHT_CONTIG:
+               /*
+                * Setting the last part of a previous oldext extent to newext.
+                * The right neighbor is contiguous with the new allocation.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount = offset - NEW.rm_offset;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               NEW = RIGHT;
+               error = xfs_rmap_delete(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               NEW.rm_offset = offset;
+               NEW.rm_startblock = bno;
+               NEW.rm_blockcount += len;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_RIGHT_FILLING:
+               /*
+                * Setting the last part of a previous oldext extent to newext.
+                * The right neighbor is not contiguous.
+                */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount -= len;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, newext);
+               if (error)
+                       goto done;
+               break;
+
+       case 0:
+               /*
+                * Setting the middle part of a previous oldext extent to
+                * newext.  Contiguity is impossible here.
+                * One extent becomes three extents.
+                */
+               /* new right extent - oldext */
+               NEW.rm_startblock = bno + len;
+               NEW.rm_owner = owner;
+               NEW.rm_offset = new_endoff;
+               NEW.rm_blockcount = PREV.rm_offset + PREV.rm_blockcount -
+                               new_endoff;
+               NEW.rm_flags = PREV.rm_flags;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner, NEW.rm_offset,
+                               NEW.rm_flags);
+               if (error)
+                       goto done;
+               /* new left extent - oldext */
+               NEW = PREV;
+               error = xfs_rmap_lookup_eq(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner,
+                               NEW.rm_offset, NEW.rm_flags, &i);
+               if (error)
+                       goto done;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, done);
+               NEW.rm_blockcount = offset - NEW.rm_offset;
+               error = xfs_rmap_update(cur, &NEW);
+               if (error)
+                       goto done;
+               /* new middle extent - newext */
+               NEW.rm_startblock = bno;
+               NEW.rm_blockcount = len;
+               NEW.rm_owner = owner;
+               NEW.rm_offset = offset;
+               NEW.rm_flags = newext;
+               error = xfs_rmap_insert(cur, NEW.rm_startblock,
+                               NEW.rm_blockcount, NEW.rm_owner, NEW.rm_offset,
+                               NEW.rm_flags);
+               if (error)
+                       goto done;
+               break;
+
+       case RMAP_LEFT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_LEFT_FILLING | RMAP_RIGHT_CONTIG:
+       case RMAP_RIGHT_FILLING | RMAP_LEFT_CONTIG:
+       case RMAP_LEFT_CONTIG | RMAP_RIGHT_CONTIG:
+       case RMAP_LEFT_CONTIG:
+       case RMAP_RIGHT_CONTIG:
+               /*
+                * These cases are all impossible.
+                */
+               ASSERT(0);
+       }
+
+       trace_xfs_rmap_convert_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+done:
+       if (error)
+               trace_xfs_rmap_convert_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+#undef NEW
+#undef LEFT
+#undef RIGHT
+#undef PREV
+
+/*
+ * Find an extent in the rmap btree and unmap it.  For rmap extent types that
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
+ * that the prev/next records in the btree might belong to another owner.
+ * Therefore we must use delete+insert to alter any of the key fields.
+ *
+ * For every other situation there can only be one owner for a given extent,
+ * so we can call the regular _free function.
+ */
+STATIC int
+xfs_rmap_unmap_shared(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    ltrec;
+       uint64_t                ltoff;
+       int                     error = 0;
+       int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
+       unsigned int            flags;
+
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       if (unwritten)
+               flags |= XFS_RMAP_UNWRITTEN;
+       trace_xfs_rmap_unmap(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+
+       /*
+        * We should always have a left record because there's a static record
+        * for the AG headers at rm_startblock == 0 created by mkfs/growfs that
+        * will not ever be removed from the tree.
+        */
+       error = xfs_rmap_lookup_le_range(cur, bno, owner, offset, flags,
+                       &ltrec, &i);
+       if (error)
+               goto out_error;
+       XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+       ltoff = ltrec.rm_offset;
+
+       /* Make sure the extent we found covers the entire freeing range. */
+       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_startblock <= bno &&
+               ltrec.rm_startblock + ltrec.rm_blockcount >=
+               bno + len, out_error);
+
+       /* Make sure the owner matches what we expect to find in the tree. */
+       XFS_WANT_CORRUPTED_GOTO(mp, owner == ltrec.rm_owner, out_error);
+
+       /* Make sure the unwritten flag matches. */
+       XFS_WANT_CORRUPTED_GOTO(mp, (flags & XFS_RMAP_UNWRITTEN) ==
+                       (ltrec.rm_flags & XFS_RMAP_UNWRITTEN), out_error);
+
+       /* Check the offset. */
+       XFS_WANT_CORRUPTED_GOTO(mp, ltrec.rm_offset <= offset, out_error);
+       XFS_WANT_CORRUPTED_GOTO(mp, offset <= ltoff + ltrec.rm_blockcount,
+                       out_error);
+
+       if (ltrec.rm_startblock == bno && ltrec.rm_blockcount == len) {
+               /* Exact match, simply remove the record from rmap tree. */
+               error = xfs_rmap_delete(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else if (ltrec.rm_startblock == bno) {
+               /*
+                * Overlap left hand side of extent: move the start, trim the
+                * length and update the current record.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing: |fffffffff|
+                * Result:            |rrrrrrrrrr|
+                *         bno       len
+                */
+
+               /* Delete prev rmap. */
+               error = xfs_rmap_delete(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+
+               /* Add an rmap at the new offset. */
+               ltrec.rm_startblock += len;
+               ltrec.rm_blockcount -= len;
+               ltrec.rm_offset += len;
+               error = xfs_rmap_insert(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else if (ltrec.rm_startblock + ltrec.rm_blockcount == bno + len) {
+               /*
+                * Overlap right hand side of extent: trim the length and
+                * update the current record.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing:            |fffffffff|
+                * Result:  |rrrrrrrrrr|
+                *                    bno       len
+                */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+               ltrec.rm_blockcount -= len;
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+       } else {
+               /*
+                * Overlap middle of extent: trim the length of the existing
+                * record to the length of the new left-extent size, increment
+                * the insertion position so we can insert a new record
+                * containing the remaining right-extent space.
+                *
+                *       ltbno                ltlen
+                * Orig:    |oooooooooooooooooooo|
+                * Freeing:       |fffffffff|
+                * Result:  |rrrrr|         |rrrr|
+                *               bno       len
+                */
+               xfs_extlen_t    orig_len = ltrec.rm_blockcount;
+
+               /* Shrink the left side of the rmap */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+               ltrec.rm_blockcount = bno - ltrec.rm_startblock;
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+
+               /* Add an rmap at the new offset */
+               error = xfs_rmap_insert(cur, bno + len,
+                               orig_len - len - ltrec.rm_blockcount,
+                               ltrec.rm_owner, offset + len,
+                               ltrec.rm_flags);
+               if (error)
+                       goto out_error;
+       }
+
+       trace_xfs_rmap_unmap_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+out_error:
+       if (error)
+               trace_xfs_rmap_unmap_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Find an extent in the rmap btree and map it.  For rmap extent types that
+ * can overlap (data fork rmaps on reflink filesystems) we must be careful
+ * that the prev/next records in the btree might belong to another owner.
+ * Therefore we must use delete+insert to alter any of the key fields.
+ *
+ * For every other situation there can only be one owner for a given extent,
+ * so we can call the regular _alloc function.
+ */
+STATIC int
+xfs_rmap_map_shared(
+       struct xfs_btree_cur    *cur,
+       xfs_agblock_t           bno,
+       xfs_extlen_t            len,
+       bool                    unwritten,
+       struct xfs_owner_info   *oinfo)
+{
+       struct xfs_mount        *mp = cur->bc_mp;
+       struct xfs_rmap_irec    ltrec;
+       struct xfs_rmap_irec    gtrec;
+       int                     have_gt;
+       int                     have_lt;
+       int                     error = 0;
+       int                     i;
+       uint64_t                owner;
+       uint64_t                offset;
+       unsigned int            flags = 0;
+
+       xfs_owner_info_unpack(oinfo, &owner, &offset, &flags);
+       if (unwritten)
+               flags |= XFS_RMAP_UNWRITTEN;
+       trace_xfs_rmap_map(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+
+       /* Is there a left record that abuts our range? */
+       error = xfs_rmap_find_left_neighbor(cur, bno, owner, offset, flags,
+                       &ltrec, &have_lt);
+       if (error)
+               goto out_error;
+       if (have_lt &&
+           !xfs_rmap_is_mergeable(&ltrec, owner, flags))
+               have_lt = 0;
+
+       /* Is there a right record that abuts our range? */
+       error = xfs_rmap_lookup_eq(cur, bno + len, len, owner, offset + len,
+                       flags, &have_gt);
+       if (error)
+               goto out_error;
+       if (have_gt) {
+               error = xfs_rmap_get_rec(cur, &gtrec, &have_gt);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, have_gt == 1, out_error);
+               trace_xfs_rmap_find_right_neighbor_result(cur->bc_mp,
+                       cur->bc_private.a.agno, gtrec.rm_startblock,
+                       gtrec.rm_blockcount, gtrec.rm_owner,
+                       gtrec.rm_offset, gtrec.rm_flags);
+
+               if (!xfs_rmap_is_mergeable(&gtrec, owner, flags))
+                       have_gt = 0;
+       }
+
+       if (have_lt &&
+           ltrec.rm_startblock + ltrec.rm_blockcount == bno &&
+           ltrec.rm_offset + ltrec.rm_blockcount == offset) {
+               /*
+                * Left edge contiguous, merge into left record.
+                *
+                *       ltbno     ltlen
+                * orig:   |ooooooooo|
+                * adding:           |aaaaaaaaa|
+                * result: |rrrrrrrrrrrrrrrrrrr|
+                *                  bno       len
+                */
+               ltrec.rm_blockcount += len;
+               if (have_gt &&
+                   bno + len == gtrec.rm_startblock &&
+                   offset + len == gtrec.rm_offset) {
+                       /*
+                        * Right edge also contiguous, delete right record
+                        * and merge into left record.
+                        *
+                        *       ltbno     ltlen    gtbno     gtlen
+                        * orig:   |ooooooooo|         |ooooooooo|
+                        * adding:           |aaaaaaaaa|
+                        * result: |rrrrrrrrrrrrrrrrrrrrrrrrrrrrr|
+                        */
+                       ltrec.rm_blockcount += gtrec.rm_blockcount;
+                       error = xfs_rmap_delete(cur, gtrec.rm_startblock,
+                                       gtrec.rm_blockcount, gtrec.rm_owner,
+                                       gtrec.rm_offset, gtrec.rm_flags);
+                       if (error)
+                               goto out_error;
+               }
+
+               /* Point the cursor back to the left record and update. */
+               error = xfs_rmap_lookup_eq(cur, ltrec.rm_startblock,
+                               ltrec.rm_blockcount, ltrec.rm_owner,
+                               ltrec.rm_offset, ltrec.rm_flags, &i);
+               if (error)
+                       goto out_error;
+               XFS_WANT_CORRUPTED_GOTO(mp, i == 1, out_error);
+
+               error = xfs_rmap_update(cur, &ltrec);
+               if (error)
+                       goto out_error;
+       } else if (have_gt &&
+                  bno + len == gtrec.rm_startblock &&
+                  offset + len == gtrec.rm_offset) {
+               /*
+                * Right edge contiguous, merge into right record.
+                *
+                *                 gtbno     gtlen
+                * Orig:             |ooooooooo|
+                * adding: |aaaaaaaaa|
+                * Result: |rrrrrrrrrrrrrrrrrrr|
+                *        bno       len
+                */
+               /* Delete the old record. */
+               error = xfs_rmap_delete(cur, gtrec.rm_startblock,
+                               gtrec.rm_blockcount, gtrec.rm_owner,
+                               gtrec.rm_offset, gtrec.rm_flags);
+               if (error)
+                       goto out_error;
+
+               /* Move the start and re-add it. */
+               gtrec.rm_startblock = bno;
+               gtrec.rm_blockcount += len;
+               gtrec.rm_offset = offset;
+               error = xfs_rmap_insert(cur, gtrec.rm_startblock,
+                               gtrec.rm_blockcount, gtrec.rm_owner,
+                               gtrec.rm_offset, gtrec.rm_flags);
+               if (error)
+                       goto out_error;
+       } else {
+               /*
+                * No contiguous edge with identical owner, insert
+                * new record at current cursor position.
+                */
+               error = xfs_rmap_insert(cur, bno, len, owner, offset, flags);
+               if (error)
+                       goto out_error;
+       }
+
+       trace_xfs_rmap_map_done(mp, cur->bc_private.a.agno, bno, len,
+                       unwritten, oinfo);
+out_error:
+       if (error)
+               trace_xfs_rmap_map_error(cur->bc_mp,
+                               cur->bc_private.a.agno, error, _RET_IP_);
+       return error;
+}
+
+struct xfs_rmap_query_range_info {
+       xfs_rmap_query_range_fn fn;
+       void                            *priv;
+};
+
+/* Format btree record and pass to our callback. */
+STATIC int
+xfs_rmap_query_range_helper(
+       struct xfs_btree_cur    *cur,
+       union xfs_btree_rec     *rec,
+       void                    *priv)
+{
+       struct xfs_rmap_query_range_info        *query = priv;
+       struct xfs_rmap_irec                    irec;
+       int                                     error;
+
+       error = xfs_rmap_btrec_to_irec(rec, &irec);
+       if (error)
+               return error;
+       return query->fn(cur, &irec, query->priv);
+}
+
+/* Find all rmaps between two keys. */
+int
+xfs_rmap_query_range(
+       struct xfs_btree_cur            *cur,
+       struct xfs_rmap_irec            *low_rec,
+       struct xfs_rmap_irec            *high_rec,
+       xfs_rmap_query_range_fn fn,
+       void                            *priv)
+{
+       union xfs_btree_irec            low_brec;
+       union xfs_btree_irec            high_brec;
+       struct xfs_rmap_query_range_info        query;
+
+       low_brec.r = *low_rec;
+       high_brec.r = *high_rec;
+       query.priv = priv;
+       query.fn = fn;
+       return xfs_btree_query_range(cur, &low_brec, &high_brec,
+                       xfs_rmap_query_range_helper, &query);
+}
+
+/* Clean up after calling xfs_rmap_finish_one. */
+void
+xfs_rmap_finish_one_cleanup(
+       struct xfs_trans        *tp,
+       struct xfs_btree_cur    *rcur,
+       int                     error)
+{
+       struct xfs_buf          *agbp;
+
+       if (rcur == NULL)
+               return;
+       agbp = rcur->bc_private.a.agbp;
+       xfs_btree_del_cursor(rcur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+       if (error)
+               xfs_trans_brelse(tp, agbp);
+}
+
+/*
+ * Process one of the deferred rmap operations.  We pass back the
+ * btree cursor to maintain our lock on the rmapbt between calls.
+ * This saves time and eliminates a buffer deadlock between the
+ * superblock and the AGF because we'll always grab them in the same
+ * order.
+ */
+int
+xfs_rmap_finish_one(
+       struct xfs_trans                *tp,
+       enum xfs_rmap_intent_type       type,
+       __uint64_t                      owner,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state,
+       struct xfs_btree_cur            **pcur)
+{
+       struct xfs_mount                *mp = tp->t_mountp;
+       struct xfs_btree_cur            *rcur;
+       struct xfs_buf                  *agbp = NULL;
+       int                             error = 0;
+       xfs_agnumber_t                  agno;
+       struct xfs_owner_info           oinfo;
+       xfs_agblock_t                   bno;
+       bool                            unwritten;
+
+       agno = XFS_FSB_TO_AGNO(mp, startblock);
+       ASSERT(agno != NULLAGNUMBER);
+       bno = XFS_FSB_TO_AGBNO(mp, startblock);
+
+       trace_xfs_rmap_deferred(mp, agno, type, bno, owner, whichfork,
+                       startoff, blockcount, state);
+
+       if (XFS_TEST_ERROR(false, mp,
+                       XFS_ERRTAG_RMAP_FINISH_ONE,
+                       XFS_RANDOM_RMAP_FINISH_ONE))
+               return -EIO;
+
+       /*
+        * If we haven't gotten a cursor or the cursor AG doesn't match
+        * the startblock, get one now.
+        */
+       rcur = *pcur;
+       if (rcur != NULL && rcur->bc_private.a.agno != agno) {
+               xfs_rmap_finish_one_cleanup(tp, rcur, 0);
+               rcur = NULL;
+               *pcur = NULL;
+       }
+       if (rcur == NULL) {
+               /*
                 * Refresh the freelist before we start changing the
                 * rmapbt, because a shape change could cause us to
                 * allocate blocks.
@@ -1237,15 +2115,27 @@ xfs_rmap_finish_one(
        case XFS_RMAP_MAP:
                error = xfs_rmap_map(rcur, bno, blockcount, unwritten, &oinfo);
                break;
+       case XFS_RMAP_MAP_SHARED:
+               error = xfs_rmap_map_shared(rcur, bno, blockcount, unwritten,
+                               &oinfo);
+               break;
        case XFS_RMAP_FREE:
        case XFS_RMAP_UNMAP:
                error = xfs_rmap_unmap(rcur, bno, blockcount, unwritten,
                                &oinfo);
                break;
+       case XFS_RMAP_UNMAP_SHARED:
+               error = xfs_rmap_unmap_shared(rcur, bno, blockcount, unwritten,
+                               &oinfo);
+               break;
        case XFS_RMAP_CONVERT:
                error = xfs_rmap_convert(rcur, bno, blockcount, !unwritten,
                                &oinfo);
                break;
+       case XFS_RMAP_CONVERT_SHARED:
+               error = xfs_rmap_convert_shared(rcur, bno, blockcount,
+                               !unwritten, &oinfo);
+               break;
        default:
                ASSERT(0);
                error = -EFSCORRUPTED;
@@ -1263,9 +2153,10 @@ out_cur:
  */
 static bool
 xfs_rmap_update_is_needed(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       int                     whichfork)
 {
-       return xfs_sb_version_hasrmapbt(&mp->m_sb);
+       return xfs_sb_version_hasrmapbt(&mp->m_sb) && whichfork != XFS_COW_FORK;
 }
 
 /*
@@ -1311,10 +2202,11 @@ xfs_rmap_map_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_MAP, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_MAP_SHARED : XFS_RMAP_MAP, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1327,10 +2219,11 @@ xfs_rmap_unmap_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_UNMAP, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_UNMAP_SHARED : XFS_RMAP_UNMAP, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1343,10 +2236,11 @@ xfs_rmap_convert_extent(
        int                     whichfork,
        struct xfs_bmbt_irec    *PREV)
 {
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, whichfork))
                return 0;
 
-       return __xfs_rmap_add(mp, dfops, XFS_RMAP_CONVERT, ip->i_ino,
+       return __xfs_rmap_add(mp, dfops, xfs_is_reflink_inode(ip) ?
+                       XFS_RMAP_CONVERT_SHARED : XFS_RMAP_CONVERT, ip->i_ino,
                        whichfork, PREV);
 }
 
@@ -1362,7 +2256,7 @@ xfs_rmap_alloc_extent(
 {
        struct xfs_bmbt_irec    bmap;
 
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, XFS_DATA_FORK))
                return 0;
 
        bmap.br_startblock = XFS_AGB_TO_FSB(mp, agno, bno);
@@ -1386,7 +2280,7 @@ xfs_rmap_free_extent(
 {
        struct xfs_bmbt_irec    bmap;
 
-       if (!xfs_rmap_update_is_needed(mp))
+       if (!xfs_rmap_update_is_needed(mp, XFS_DATA_FORK))
                return 0;
 
        bmap.br_startblock = XFS_AGB_TO_FSB(mp, agno, bno);
index 71cf99a..7899305 100644 (file)
@@ -206,4 +206,11 @@ int xfs_rmap_finish_one(struct xfs_trans *tp, enum xfs_rmap_intent_type type,
                xfs_fsblock_t startblock, xfs_filblks_t blockcount,
                xfs_exntst_t state, struct xfs_btree_cur **pcur);
 
+int xfs_rmap_find_left_neighbor(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+               uint64_t owner, uint64_t offset, unsigned int flags,
+               struct xfs_rmap_irec *irec, int *stat);
+int xfs_rmap_lookup_le_range(struct xfs_btree_cur *cur, xfs_agblock_t bno,
+               uint64_t owner, uint64_t offset, unsigned int flags,
+               struct xfs_rmap_irec *irec, int *stat);
+
 #endif /* __XFS_RMAP_H__ */
index 17b8eeb..83e672f 100644 (file)
@@ -35,6 +35,7 @@
 #include "xfs_cksum.h"
 #include "xfs_error.h"
 #include "xfs_extent_busy.h"
+#include "xfs_ag_resv.h"
 
 /*
  * Reverse map btree.
@@ -512,6 +513,83 @@ void
 xfs_rmapbt_compute_maxlevels(
        struct xfs_mount                *mp)
 {
-       mp->m_rmap_maxlevels = xfs_btree_compute_maxlevels(mp,
-                       mp->m_rmap_mnr, mp->m_sb.sb_agblocks);
+       /*
+        * On a non-reflink filesystem, the maximum number of rmap
+        * records is the number of blocks in the AG, hence the max
+        * rmapbt height is log_$maxrecs($agblocks).  However, with
+        * reflink each AG block can have up to 2^32 (per the refcount
+        * record format) owners, which means that theoretically we
+        * could face up to 2^64 rmap records.
+        *
+        * That effectively means that the max rmapbt height must be
+        * XFS_BTREE_MAXLEVELS.  "Fortunately" we'll run out of AG
+        * blocks to feed the rmapbt long before the rmapbt reaches
+        * maximum height.  The reflink code uses ag_resv_critical to
+        * disallow reflinking when less than 10% of the per-AG metadata
+        * block reservation since the fallback is a regular file copy.
+        */
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               mp->m_rmap_maxlevels = XFS_BTREE_MAXLEVELS;
+       else
+               mp->m_rmap_maxlevels = xfs_btree_compute_maxlevels(mp,
+                               mp->m_rmap_mnr, mp->m_sb.sb_agblocks);
+}
+
+/* Calculate the refcount btree size for some records. */
+xfs_extlen_t
+xfs_rmapbt_calc_size(
+       struct xfs_mount        *mp,
+       unsigned long long      len)
+{
+       return xfs_btree_calc_size(mp, mp->m_rmap_mnr, len);
+}
+
+/*
+ * Calculate the maximum refcount btree size.
+ */
+xfs_extlen_t
+xfs_rmapbt_max_size(
+       struct xfs_mount        *mp)
+{
+       /* Bail out if we're uninitialized, which can happen in mkfs. */
+       if (mp->m_rmap_mxr[0] == 0)
+               return 0;
+
+       return xfs_rmapbt_calc_size(mp, mp->m_sb.sb_agblocks);
+}
+
+/*
+ * Figure out how many blocks to reserve and how many are used by this btree.
+ */
+int
+xfs_rmapbt_calc_reserves(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_extlen_t            *ask,
+       xfs_extlen_t            *used)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_agf          *agf;
+       xfs_extlen_t            pool_len;
+       xfs_extlen_t            tree_len;
+       int                     error;
+
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return 0;
+
+       /* Reserve 1% of the AG or enough for 1 block per record. */
+       pool_len = max(mp->m_sb.sb_agblocks / 100, xfs_rmapbt_max_size(mp));
+       *ask += pool_len;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       agf = XFS_BUF_TO_AGF(agbp);
+       tree_len = be32_to_cpu(agf->agf_rmap_blocks);
+       xfs_buf_relse(agbp);
+
+       *used += tree_len;
+
+       return error;
 }
index e73a553..2a9ac47 100644 (file)
@@ -58,4 +58,11 @@ struct xfs_btree_cur *xfs_rmapbt_init_cursor(struct xfs_mount *mp,
 int xfs_rmapbt_maxrecs(struct xfs_mount *mp, int blocklen, int leaf);
 extern void xfs_rmapbt_compute_maxlevels(struct xfs_mount *mp);
 
+extern xfs_extlen_t xfs_rmapbt_calc_size(struct xfs_mount *mp,
+               unsigned long long len);
+extern xfs_extlen_t xfs_rmapbt_max_size(struct xfs_mount *mp);
+
+extern int xfs_rmapbt_calc_reserves(struct xfs_mount *mp,
+               xfs_agnumber_t agno, xfs_extlen_t *ask, xfs_extlen_t *used);
+
 #endif /* __XFS_RMAP_BTREE_H__ */
index 4aecc5f..a70aec9 100644 (file)
@@ -38,6 +38,8 @@
 #include "xfs_ialloc_btree.h"
 #include "xfs_log.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_bmap.h"
+#include "xfs_refcount_btree.h"
 
 /*
  * Physical superblock buffer manipulations. Shared with libxfs in userspace.
@@ -737,6 +739,13 @@ xfs_sb_mount_common(
        mp->m_rmap_mnr[0] = mp->m_rmap_mxr[0] / 2;
        mp->m_rmap_mnr[1] = mp->m_rmap_mxr[1] / 2;
 
+       mp->m_refc_mxr[0] = xfs_refcountbt_maxrecs(mp, sbp->sb_blocksize,
+                       true);
+       mp->m_refc_mxr[1] = xfs_refcountbt_maxrecs(mp, sbp->sb_blocksize,
+                       false);
+       mp->m_refc_mnr[0] = mp->m_refc_mxr[0] / 2;
+       mp->m_refc_mnr[1] = mp->m_refc_mxr[1] / 2;
+
        mp->m_bsize = XFS_FSB_TO_BB(mp, 1);
        mp->m_ialloc_inos = (int)MAX((__uint16_t)XFS_INODES_PER_CHUNK,
                                        sbp->sb_inopblock);
index 0c5b30b..c6f4eb4 100644 (file)
@@ -39,6 +39,7 @@ extern const struct xfs_buf_ops xfs_agf_buf_ops;
 extern const struct xfs_buf_ops xfs_agfl_buf_ops;
 extern const struct xfs_buf_ops xfs_allocbt_buf_ops;
 extern const struct xfs_buf_ops xfs_rmapbt_buf_ops;
+extern const struct xfs_buf_ops xfs_refcountbt_buf_ops;
 extern const struct xfs_buf_ops xfs_attr3_leaf_buf_ops;
 extern const struct xfs_buf_ops xfs_attr3_rmt_buf_ops;
 extern const struct xfs_buf_ops xfs_bmbt_buf_ops;
@@ -122,6 +123,7 @@ int xfs_log_calc_minimum_size(struct xfs_mount *);
 #define        XFS_INO_REF             2
 #define        XFS_ATTR_BTREE_REF      1
 #define        XFS_DQUOT_REF           1
+#define        XFS_REFC_BTREE_REF      1
 
 /*
  * Flags for xfs_trans_ichgtime().
index 301ef2f..b456cca 100644 (file)
@@ -67,13 +67,14 @@ xfs_calc_buf_res(
  * Per-extent log reservation for the btree changes involved in freeing or
  * allocating an extent.  In classic XFS there were two trees that will be
  * modified (bnobt + cntbt).  With rmap enabled, there are three trees
- * (rmapbt).  The number of blocks reserved is based on the formula:
+ * (rmapbt).  With reflink, there are four trees (refcountbt).  The number of
+ * blocks reserved is based on the formula:
  *
  * num trees * ((2 blocks/level * max depth) - 1)
  *
  * Keep in mind that max depth is calculated separately for each type of tree.
  */
-static uint
+uint
 xfs_allocfree_log_count(
        struct xfs_mount *mp,
        uint            num_ops)
@@ -83,6 +84,8 @@ xfs_allocfree_log_count(
        blocks = num_ops * 2 * (2 * mp->m_ag_maxlevels - 1);
        if (xfs_sb_version_hasrmapbt(&mp->m_sb))
                blocks += num_ops * (2 * mp->m_rmap_maxlevels - 1);
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               blocks += num_ops * (2 * mp->m_refc_maxlevels - 1);
 
        return blocks;
 }
@@ -809,11 +812,18 @@ xfs_trans_resv_calc(
         * require a permanent reservation on space.
         */
        resp->tr_write.tr_logres = xfs_calc_write_reservation(mp);
-       resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_write.tr_logcount = XFS_WRITE_LOG_COUNT;
        resp->tr_write.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_itruncate.tr_logres = xfs_calc_itruncate_reservation(mp);
-       resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_itruncate.tr_logcount =
+                               XFS_ITRUNCATE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
        resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
@@ -870,7 +880,10 @@ xfs_trans_resv_calc(
        resp->tr_growrtalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        resp->tr_qm_dqalloc.tr_logres = xfs_calc_qm_dqalloc_reservation(mp);
-       resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT_REFLINK;
+       else
+               resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
        resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
 
        /*
index 0eb46ed..b7e5357 100644 (file)
@@ -87,6 +87,7 @@ struct xfs_trans_resv {
 #define        XFS_DEFAULT_LOG_COUNT           1
 #define        XFS_DEFAULT_PERM_LOG_COUNT      2
 #define        XFS_ITRUNCATE_LOG_COUNT         2
+#define        XFS_ITRUNCATE_LOG_COUNT_REFLINK 8
 #define XFS_INACTIVE_LOG_COUNT         2
 #define        XFS_CREATE_LOG_COUNT            2
 #define        XFS_CREATE_TMPFILE_LOG_COUNT    2
@@ -96,11 +97,13 @@ struct xfs_trans_resv {
 #define        XFS_LINK_LOG_COUNT              2
 #define        XFS_RENAME_LOG_COUNT            2
 #define        XFS_WRITE_LOG_COUNT             2
+#define        XFS_WRITE_LOG_COUNT_REFLINK     8
 #define        XFS_ADDAFORK_LOG_COUNT          2
 #define        XFS_ATTRINVAL_LOG_COUNT         1
 #define        XFS_ATTRSET_LOG_COUNT           3
 #define        XFS_ATTRRM_LOG_COUNT            3
 
 void xfs_trans_resv_calc(struct xfs_mount *mp, struct xfs_trans_resv *resp);
+uint xfs_allocfree_log_count(struct xfs_mount *mp, uint num_ops);
 
 #endif /* __XFS_TRANS_RESV_H__ */
index 41e0428..7917f6e 100644 (file)
@@ -21,6 +21,8 @@
 /*
  * Components of space reservations.
  */
+#define XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)    \
+               (((mp)->m_rmap_mxr[0]) - ((mp)->m_rmap_mnr[0]))
 #define XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)    \
                (((mp)->m_alloc_mxr[0]) - ((mp)->m_alloc_mnr[0]))
 #define        XFS_EXTENTADD_SPACE_RES(mp,w)   (XFS_BM_MAXLEVELS(mp,w) - 1)
        (((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
          XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
          XFS_EXTENTADD_SPACE_RES(mp,w))
+#define XFS_SWAP_RMAP_SPACE_RES(mp,b,w)\
+       (((b + XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp) - 1) / \
+         XFS_MAX_CONTIG_EXTENTS_PER_BLOCK(mp)) * \
+         XFS_EXTENTADD_SPACE_RES(mp,w) + \
+        ((b + XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp) - 1) / \
+         XFS_MAX_CONTIG_RMAPS_PER_BLOCK(mp)) * \
+         (mp)->m_rmap_maxlevels)
 #define        XFS_DAENTER_1B(mp,w)    \
        ((w) == XFS_DATA_FORK ? (mp)->m_dir_geo->fsbcount : 1)
 #define        XFS_DAENTER_DBS(mp,w)   \
index 3d50364..8d74870 100644 (file)
@@ -90,6 +90,7 @@ typedef __int64_t     xfs_sfiloff_t;  /* signed block number in a file */
  */
 #define        XFS_DATA_FORK   0
 #define        XFS_ATTR_FORK   1
+#define        XFS_COW_FORK    2
 
 /*
  * Min numbers of data/attr fork btree root pointers.
@@ -109,7 +110,7 @@ typedef enum {
 
 typedef enum {
        XFS_BTNUM_BNOi, XFS_BTNUM_CNTi, XFS_BTNUM_RMAPi, XFS_BTNUM_BMAPi,
-       XFS_BTNUM_INOi, XFS_BTNUM_FINOi, XFS_BTNUM_MAX
+       XFS_BTNUM_INOi, XFS_BTNUM_FINOi, XFS_BTNUM_REFCi, XFS_BTNUM_MAX
 } xfs_btnum_t;
 
 struct xfs_name {
index 4a28fa9..3e57a56 100644 (file)
@@ -31,6 +31,7 @@
 #include "xfs_bmap.h"
 #include "xfs_bmap_util.h"
 #include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
 #include <linux/gfp.h>
 #include <linux/mpage.h>
 #include <linux/pagevec.h>
@@ -39,6 +40,7 @@
 /* flags for direct write completions */
 #define XFS_DIO_FLAG_UNWRITTEN (1 << 0)
 #define XFS_DIO_FLAG_APPEND    (1 << 1)
+#define XFS_DIO_FLAG_COW       (1 << 2)
 
 /*
  * structure owned by writepages passed to individual writepage calls
@@ -286,6 +288,25 @@ xfs_end_io(
        if (XFS_FORCED_SHUTDOWN(ip->i_mount))
                error = -EIO;
 
+       /*
+        * For a CoW extent, we need to move the mapping from the CoW fork
+        * to the data fork.  If instead an error happened, just dump the
+        * new blocks.
+        */
+       if (ioend->io_type == XFS_IO_COW) {
+               if (error)
+                       goto done;
+               if (ioend->io_bio->bi_error) {
+                       error = xfs_reflink_cancel_cow_range(ip,
+                                       ioend->io_offset, ioend->io_size);
+                       goto done;
+               }
+               error = xfs_reflink_end_cow(ip, ioend->io_offset,
+                               ioend->io_size);
+               if (error)
+                       goto done;
+       }
+
        /*
         * For unwritten extents we need to issue transactions to convert a
         * range to normal written extens after the data I/O has finished.
@@ -301,7 +322,8 @@ xfs_end_io(
        } else if (ioend->io_append_trans) {
                error = xfs_setfilesize_ioend(ioend, error);
        } else {
-               ASSERT(!xfs_ioend_is_append(ioend));
+               ASSERT(!xfs_ioend_is_append(ioend) ||
+                      ioend->io_type == XFS_IO_COW);
        }
 
 done:
@@ -315,7 +337,7 @@ xfs_end_bio(
        struct xfs_ioend        *ioend = bio->bi_private;
        struct xfs_mount        *mp = XFS_I(ioend->io_inode)->i_mount;
 
-       if (ioend->io_type == XFS_IO_UNWRITTEN)
+       if (ioend->io_type == XFS_IO_UNWRITTEN || ioend->io_type == XFS_IO_COW)
                queue_work(mp->m_unwritten_workqueue, &ioend->io_work);
        else if (ioend->io_append_trans)
                queue_work(mp->m_data_workqueue, &ioend->io_work);
@@ -341,6 +363,7 @@ xfs_map_blocks(
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
+       ASSERT(type != XFS_IO_COW);
        if (type == XFS_IO_UNWRITTEN)
                bmapi_flags |= XFS_BMAPI_IGSTATE;
 
@@ -355,6 +378,13 @@ xfs_map_blocks(
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
                                imap, &nimaps, bmapi_flags);
+       /*
+        * Truncate an overwrite extent if there's a pending CoW
+        * reservation before the end of this extent.  This forces us
+        * to come back to writepage to take care of the CoW.
+        */
+       if (nimaps && type == XFS_IO_OVERWRITE)
+               xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
        if (error)
@@ -362,7 +392,8 @@ xfs_map_blocks(
 
        if (type == XFS_IO_DELALLOC &&
            (!nimaps || isnullstartblock(imap->br_startblock))) {
-               error = xfs_iomap_write_allocate(ip, offset, imap);
+               error = xfs_iomap_write_allocate(ip, XFS_DATA_FORK, offset,
+                               imap);
                if (!error)
                        trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
                return error;
@@ -737,6 +768,56 @@ out_invalidate:
        return;
 }
 
+static int
+xfs_map_cow(
+       struct xfs_writepage_ctx *wpc,
+       struct inode            *inode,
+       loff_t                  offset,
+       unsigned int            *new_type)
+{
+       struct xfs_inode        *ip = XFS_I(inode);
+       struct xfs_bmbt_irec    imap;
+       bool                    is_cow = false, need_alloc = false;
+       int                     error;
+
+       /*
+        * If we already have a valid COW mapping keep using it.
+        */
+       if (wpc->io_type == XFS_IO_COW) {
+               wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
+               if (wpc->imap_valid) {
+                       *new_type = XFS_IO_COW;
+                       return 0;
+               }
+       }
+
+       /*
+        * Else we need to check if there is a COW mapping at this offset.
+        */
+       xfs_ilock(ip, XFS_ILOCK_SHARED);
+       is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
+       xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+       if (!is_cow)
+               return 0;
+
+       /*
+        * And if the COW mapping has a delayed extent here we need to
+        * allocate real space for it now.
+        */
+       if (need_alloc) {
+               error = xfs_iomap_write_allocate(ip, XFS_COW_FORK, offset,
+                               &imap);
+               if (error)
+                       return error;
+       }
+
+       wpc->io_type = *new_type = XFS_IO_COW;
+       wpc->imap_valid = true;
+       wpc->imap = imap;
+       return 0;
+}
+
 /*
  * We implement an immediate ioend submission policy here to avoid needing to
  * chain multiple ioends and hence nest mempool allocations which can violate
@@ -769,6 +850,7 @@ xfs_writepage_map(
        int                     error = 0;
        int                     count = 0;
        int                     uptodate = 1;
+       unsigned int            new_type;
 
        bh = head = page_buffers(page);
        offset = page_offset(page);
@@ -789,22 +871,13 @@ xfs_writepage_map(
                        continue;
                }
 
-               if (buffer_unwritten(bh)) {
-                       if (wpc->io_type != XFS_IO_UNWRITTEN) {
-                               wpc->io_type = XFS_IO_UNWRITTEN;
-                               wpc->imap_valid = false;
-                       }
-               } else if (buffer_delay(bh)) {
-                       if (wpc->io_type != XFS_IO_DELALLOC) {
-                               wpc->io_type = XFS_IO_DELALLOC;
-                               wpc->imap_valid = false;
-                       }
-               } else if (buffer_uptodate(bh)) {
-                       if (wpc->io_type != XFS_IO_OVERWRITE) {
-                               wpc->io_type = XFS_IO_OVERWRITE;
-                               wpc->imap_valid = false;
-                       }
-               } else {
+               if (buffer_unwritten(bh))
+                       new_type = XFS_IO_UNWRITTEN;
+               else if (buffer_delay(bh))
+                       new_type = XFS_IO_DELALLOC;
+               else if (buffer_uptodate(bh))
+                       new_type = XFS_IO_OVERWRITE;
+               else {
                        if (PageUptodate(page))
                                ASSERT(buffer_mapped(bh));
                        /*
@@ -817,6 +890,17 @@ xfs_writepage_map(
                        continue;
                }
 
+               if (xfs_is_reflink_inode(XFS_I(inode))) {
+                       error = xfs_map_cow(wpc, inode, offset, &new_type);
+                       if (error)
+                               goto out;
+               }
+
+               if (wpc->io_type != new_type) {
+                       wpc->io_type = new_type;
+                       wpc->imap_valid = false;
+               }
+
                if (wpc->imap_valid)
                        wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap,
                                                         offset);
@@ -1107,18 +1191,24 @@ xfs_map_direct(
        struct inode            *inode,
        struct buffer_head      *bh_result,
        struct xfs_bmbt_irec    *imap,
-       xfs_off_t               offset)
+       xfs_off_t               offset,
+       bool                    is_cow)
 {
        uintptr_t               *flags = (uintptr_t *)&bh_result->b_private;
        xfs_off_t               size = bh_result->b_size;
 
        trace_xfs_get_blocks_map_direct(XFS_I(inode), offset, size,
-               ISUNWRITTEN(imap) ? XFS_IO_UNWRITTEN : XFS_IO_OVERWRITE, imap);
+               ISUNWRITTEN(imap) ? XFS_IO_UNWRITTEN : is_cow ? XFS_IO_COW :
+               XFS_IO_OVERWRITE, imap);
 
        if (ISUNWRITTEN(imap)) {
                *flags |= XFS_DIO_FLAG_UNWRITTEN;
                set_buffer_defer_completion(bh_result);
-       } else if (offset + size > i_size_read(inode) || offset + size < 0) {
+       } else if (is_cow) {
+               *flags |= XFS_DIO_FLAG_COW;
+               set_buffer_defer_completion(bh_result);
+       }
+       if (offset + size > i_size_read(inode) || offset + size < 0) {
                *flags |= XFS_DIO_FLAG_APPEND;
                set_buffer_defer_completion(bh_result);
        }
@@ -1164,6 +1254,44 @@ xfs_map_trim_size(
        bh_result->b_size = mapping_size;
 }
 
+/* Bounce unaligned directio writes to the page cache. */
+static int
+xfs_bounce_unaligned_dio_write(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           offset_fsb,
+       struct xfs_bmbt_irec    *imap)
+{
+       struct xfs_bmbt_irec    irec;
+       xfs_fileoff_t           delta;
+       bool                    shared;
+       bool                    x;
+       int                     error;
+
+       irec = *imap;
+       if (offset_fsb > irec.br_startoff) {
+               delta = offset_fsb - irec.br_startoff;
+               irec.br_blockcount -= delta;
+               irec.br_startblock += delta;
+               irec.br_startoff = offset_fsb;
+       }
+       error = xfs_reflink_trim_around_shared(ip, &irec, &shared, &x);
+       if (error)
+               return error;
+
+       /*
+        * We're here because we're trying to do a directio write to a
+        * region that isn't aligned to a filesystem block.  If any part
+        * of the extent is shared, fall back to buffered mode to handle
+        * the RMW.  This is done by returning -EREMCHG ("remote addr
+        * changed"), which is caught further up the call stack.
+        */
+       if (shared) {
+               trace_xfs_reflink_bounce_dio_write(ip, imap);
+               return -EREMCHG;
+       }
+       return 0;
+}
+
 STATIC int
 __xfs_get_blocks(
        struct inode            *inode,
@@ -1183,6 +1311,8 @@ __xfs_get_blocks(
        xfs_off_t               offset;
        ssize_t                 size;
        int                     new = 0;
+       bool                    is_cow = false;
+       bool                    need_alloc = false;
 
        BUG_ON(create && !direct);
 
@@ -1208,8 +1338,26 @@ __xfs_get_blocks(
        end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
        offset_fsb = XFS_B_TO_FSBT(mp, offset);
 
-       error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
-                               &imap, &nimaps, XFS_BMAPI_ENTIRE);
+       if (create && direct && xfs_is_reflink_inode(ip))
+               is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap,
+                                       &need_alloc);
+       if (!is_cow) {
+               error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
+                                       &imap, &nimaps, XFS_BMAPI_ENTIRE);
+               /*
+                * Truncate an overwrite extent if there's a pending CoW
+                * reservation before the end of this extent.  This
+                * forces us to come back to get_blocks to take care of
+                * the CoW.
+                */
+               if (create && direct && nimaps &&
+                   imap.br_startblock != HOLESTARTBLOCK &&
+                   imap.br_startblock != DELAYSTARTBLOCK &&
+                   !ISUNWRITTEN(&imap))
+                       xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb,
+                                       &imap);
+       }
+       ASSERT(!need_alloc);
        if (error)
                goto out_unlock;
 
@@ -1261,6 +1409,13 @@ __xfs_get_blocks(
        if (imap.br_startblock != HOLESTARTBLOCK &&
            imap.br_startblock != DELAYSTARTBLOCK &&
            (create || !ISUNWRITTEN(&imap))) {
+               if (create && direct && !is_cow) {
+                       error = xfs_bounce_unaligned_dio_write(ip, offset_fsb,
+                                       &imap);
+                       if (error)
+                               return error;
+               }
+
                xfs_map_buffer(inode, bh_result, &imap, offset);
                if (ISUNWRITTEN(&imap))
                        set_buffer_unwritten(bh_result);
@@ -1269,7 +1424,8 @@ __xfs_get_blocks(
                        if (dax_fault)
                                ASSERT(!ISUNWRITTEN(&imap));
                        else
-                               xfs_map_direct(inode, bh_result, &imap, offset);
+                               xfs_map_direct(inode, bh_result, &imap, offset,
+                                               is_cow);
                }
        }
 
@@ -1391,11 +1547,14 @@ xfs_end_io_direct_write(
                i_size_write(inode, offset + size);
        spin_unlock(&ip->i_flags_lock);
 
+       if (flags & XFS_DIO_FLAG_COW)
+               error = xfs_reflink_end_cow(ip, offset, size);
        if (flags & XFS_DIO_FLAG_UNWRITTEN) {
                trace_xfs_end_io_direct_write_unwritten(ip, offset, size);
 
                error = xfs_iomap_write_unwritten(ip, offset, size);
-       } else if (flags & XFS_DIO_FLAG_APPEND) {
+       }
+       if (flags & XFS_DIO_FLAG_APPEND) {
                trace_xfs_end_io_direct_write_append(ip, offset, size);
 
                error = xfs_setfilesize(ip, offset, size);
@@ -1425,6 +1584,17 @@ xfs_vm_bmap(
 
        trace_xfs_vm_bmap(XFS_I(inode));
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
+
+       /*
+        * The swap code (ab-)uses ->bmap to get a block mapping and then
+        * bypasseѕ the file system for actual I/O.  We really can't allow
+        * that on reflinks inodes, so we have to skip out here.  And yes,
+        * 0 is the magic code for a bmap error..
+        */
+       if (xfs_is_reflink_inode(ip)) {
+               xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+               return 0;
+       }
        filemap_write_and_wait(mapping);
        xfs_iunlock(ip, XFS_IOLOCK_SHARED);
        return generic_block_bmap(mapping, block, xfs_get_blocks);
index 1950e3b..b3c6634 100644 (file)
@@ -28,13 +28,15 @@ enum {
        XFS_IO_DELALLOC,        /* covers delalloc region */
        XFS_IO_UNWRITTEN,       /* covers allocated but uninitialized data */
        XFS_IO_OVERWRITE,       /* covers already allocated extent */
+       XFS_IO_COW,             /* covers copy-on-write extent */
 };
 
 #define XFS_IO_TYPES \
        { XFS_IO_INVALID,               "invalid" }, \
        { XFS_IO_DELALLOC,              "delalloc" }, \
        { XFS_IO_UNWRITTEN,             "unwritten" }, \
-       { XFS_IO_OVERWRITE,             "overwrite" }
+       { XFS_IO_OVERWRITE,             "overwrite" }, \
+       { XFS_IO_COW,                   "CoW" }
 
 /*
  * Structure for buffered I/O completions.
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
new file mode 100644 (file)
index 0000000..9bf57c7
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_bmap_item.h"
+#include "xfs_log.h"
+#include "xfs_bmap.h"
+#include "xfs_icache.h"
+#include "xfs_trace.h"
+
+
+kmem_zone_t    *xfs_bui_zone;
+kmem_zone_t    *xfs_bud_zone;
+
+static inline struct xfs_bui_log_item *BUI_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_bui_log_item, bui_item);
+}
+
+void
+xfs_bui_item_free(
+       struct xfs_bui_log_item *buip)
+{
+       kmem_zone_free(xfs_bui_zone, buip);
+}
+
+STATIC void
+xfs_bui_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+
+       *nvecs += 1;
+       *nbytes += xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given bui log item. We use only 1 iovec, and we point that
+ * at the bui_log_format structure embedded in the bui item.
+ * It is at this point that we assert that all of the extent
+ * slots in the bui item have been filled.
+ */
+STATIC void
+xfs_bui_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       ASSERT(atomic_read(&buip->bui_next_extent) ==
+                       buip->bui_format.bui_nextents);
+
+       buip->bui_format.bui_type = XFS_LI_BUI;
+       buip->bui_format.bui_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUI_FORMAT, &buip->bui_format,
+                       xfs_bui_log_format_sizeof(buip->bui_format.bui_nextents));
+}
+
+/*
+ * Pinning has no meaning for an bui item, so just return.
+ */
+STATIC void
+xfs_bui_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * The unpin operation is the last place an BUI is manipulated in the log. It is
+ * either inserted in the AIL or aborted in the event of a log I/O error. In
+ * either case, the BUI transaction has been successfully committed to make it
+ * this far. Therefore, we expect whoever committed the BUI to either construct
+ * and commit the BUD or drop the BUD's reference in the event of error. Simply
+ * drop the log's BUI reference now that the log is done with it.
+ */
+STATIC void
+xfs_bui_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+       struct xfs_bui_log_item *buip = BUI_ITEM(lip);
+
+       xfs_bui_release(buip);
+}
+
+/*
+ * BUI items have no locking or pushing.  However, since BUIs are pulled from
+ * the AIL when their corresponding BUDs are committed to disk, their situation
+ * is very similar to being pinned.  Return XFS_ITEM_PINNED so that the caller
+ * will eventually flush the log.  This should help in getting the BUI out of
+ * the AIL.
+ */
+STATIC uint
+xfs_bui_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The BUI has been either committed or aborted if the transaction has been
+ * cancelled. If the transaction was cancelled, an BUD isn't going to be
+ * constructed and thus we free the BUI here directly.
+ */
+STATIC void
+xfs_bui_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       if (lip->li_flags & XFS_LI_ABORTED)
+               xfs_bui_item_free(BUI_ITEM(lip));
+}
+
+/*
+ * The BUI is logged only once and cannot be moved in the log, so simply return
+ * the lsn at which it's been logged.
+ */
+STATIC xfs_lsn_t
+xfs_bui_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       return lsn;
+}
+
+/*
+ * The BUI dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_bui_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all bui log items.
+ */
+static const struct xfs_item_ops xfs_bui_item_ops = {
+       .iop_size       = xfs_bui_item_size,
+       .iop_format     = xfs_bui_item_format,
+       .iop_pin        = xfs_bui_item_pin,
+       .iop_unpin      = xfs_bui_item_unpin,
+       .iop_unlock     = xfs_bui_item_unlock,
+       .iop_committed  = xfs_bui_item_committed,
+       .iop_push       = xfs_bui_item_push,
+       .iop_committing = xfs_bui_item_committing,
+};
+
+/*
+ * Allocate and initialize an bui item with the given number of extents.
+ */
+struct xfs_bui_log_item *
+xfs_bui_init(
+       struct xfs_mount                *mp)
+
+{
+       struct xfs_bui_log_item         *buip;
+
+       buip = kmem_zone_zalloc(xfs_bui_zone, KM_SLEEP);
+
+       xfs_log_item_init(mp, &buip->bui_item, XFS_LI_BUI, &xfs_bui_item_ops);
+       buip->bui_format.bui_nextents = XFS_BUI_MAX_FAST_EXTENTS;
+       buip->bui_format.bui_id = (uintptr_t)(void *)buip;
+       atomic_set(&buip->bui_next_extent, 0);
+       atomic_set(&buip->bui_refcount, 2);
+
+       return buip;
+}
+
+/*
+ * Freeing the BUI requires that we remove it from the AIL if it has already
+ * been placed there. However, the BUI may not yet have been placed in the AIL
+ * when called by xfs_bui_release() from BUD processing due to the ordering of
+ * committed vs unpin operations in bulk insert operations. Hence the reference
+ * count to ensure only the last caller frees the BUI.
+ */
+void
+xfs_bui_release(
+       struct xfs_bui_log_item *buip)
+{
+       if (atomic_dec_and_test(&buip->bui_refcount)) {
+               xfs_trans_ail_remove(&buip->bui_item, SHUTDOWN_LOG_IO_ERROR);
+               xfs_bui_item_free(buip);
+       }
+}
+
+static inline struct xfs_bud_log_item *BUD_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_bud_log_item, bud_item);
+}
+
+STATIC void
+xfs_bud_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       *nvecs += 1;
+       *nbytes += sizeof(struct xfs_bud_log_format);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given bud log item. We use only 1 iovec, and we point that
+ * at the bud_log_format structure embedded in the bud item.
+ * It is at this point that we assert that all of the extent
+ * slots in the bud item have been filled.
+ */
+STATIC void
+xfs_bud_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       budp->bud_format.bud_type = XFS_LI_BUD;
+       budp->bud_format.bud_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_BUD_FORMAT, &budp->bud_format,
+                       sizeof(struct xfs_bud_log_format));
+}
+
+/*
+ * Pinning has no meaning for an bud item, so just return.
+ */
+STATIC void
+xfs_bud_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * Since pinning has no meaning for an bud item, unpinning does
+ * not either.
+ */
+STATIC void
+xfs_bud_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+}
+
+/*
+ * There isn't much you can do to push on an bud item.  It is simply stuck
+ * waiting for the log to be flushed to disk.
+ */
+STATIC uint
+xfs_bud_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The BUD is either committed or aborted if the transaction is cancelled. If
+ * the transaction is cancelled, drop our reference to the BUI and free the
+ * BUD.
+ */
+STATIC void
+xfs_bud_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+
+       if (lip->li_flags & XFS_LI_ABORTED) {
+               xfs_bui_release(budp->bud_buip);
+               kmem_zone_free(xfs_bud_zone, budp);
+       }
+}
+
+/*
+ * When the bud item is committed to disk, all we need to do is delete our
+ * reference to our partner bui item and then free ourselves. Since we're
+ * freeing ourselves we must return -1 to keep the transaction code from
+ * further referencing this item.
+ */
+STATIC xfs_lsn_t
+xfs_bud_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       struct xfs_bud_log_item *budp = BUD_ITEM(lip);
+
+       /*
+        * Drop the BUI reference regardless of whether the BUD has been
+        * aborted. Once the BUD transaction is constructed, it is the sole
+        * responsibility of the BUD to release the BUI (even if the BUI is
+        * aborted due to log I/O error).
+        */
+       xfs_bui_release(budp->bud_buip);
+       kmem_zone_free(xfs_bud_zone, budp);
+
+       return (xfs_lsn_t)-1;
+}
+
+/*
+ * The BUD dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_bud_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all bud log items.
+ */
+static const struct xfs_item_ops xfs_bud_item_ops = {
+       .iop_size       = xfs_bud_item_size,
+       .iop_format     = xfs_bud_item_format,
+       .iop_pin        = xfs_bud_item_pin,
+       .iop_unpin      = xfs_bud_item_unpin,
+       .iop_unlock     = xfs_bud_item_unlock,
+       .iop_committed  = xfs_bud_item_committed,
+       .iop_push       = xfs_bud_item_push,
+       .iop_committing = xfs_bud_item_committing,
+};
+
+/*
+ * Allocate and initialize an bud item with the given number of extents.
+ */
+struct xfs_bud_log_item *
+xfs_bud_init(
+       struct xfs_mount                *mp,
+       struct xfs_bui_log_item         *buip)
+
+{
+       struct xfs_bud_log_item *budp;
+
+       budp = kmem_zone_zalloc(xfs_bud_zone, KM_SLEEP);
+       xfs_log_item_init(mp, &budp->bud_item, XFS_LI_BUD, &xfs_bud_item_ops);
+       budp->bud_buip = buip;
+       budp->bud_format.bud_bui_id = buip->bui_format.bui_id;
+
+       return budp;
+}
+
+/*
+ * Process a bmap update intent item that was recovered from the log.
+ * We need to update some inode's bmbt.
+ */
+int
+xfs_bui_recover(
+       struct xfs_mount                *mp,
+       struct xfs_bui_log_item         *buip)
+{
+       int                             error = 0;
+       unsigned int                    bui_type;
+       struct xfs_map_extent           *bmap;
+       xfs_fsblock_t                   startblock_fsb;
+       xfs_fsblock_t                   inode_fsb;
+       bool                            op_ok;
+       struct xfs_bud_log_item         *budp;
+       enum xfs_bmap_intent_type       type;
+       int                             whichfork;
+       xfs_exntst_t                    state;
+       struct xfs_trans                *tp;
+       struct xfs_inode                *ip = NULL;
+       struct xfs_defer_ops            dfops;
+       xfs_fsblock_t                   firstfsb;
+
+       ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
+
+       /* Only one mapping operation per BUI... */
+       if (buip->bui_format.bui_nextents != XFS_BUI_MAX_FAST_EXTENTS) {
+               set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+               xfs_bui_release(buip);
+               return -EIO;
+       }
+
+       /*
+        * First check the validity of the extent described by the
+        * BUI.  If anything is bad, then toss the BUI.
+        */
+       bmap = &buip->bui_format.bui_extents[0];
+       startblock_fsb = XFS_BB_TO_FSB(mp,
+                          XFS_FSB_TO_DADDR(mp, bmap->me_startblock));
+       inode_fsb = XFS_BB_TO_FSB(mp, XFS_FSB_TO_DADDR(mp,
+                       XFS_INO_TO_FSB(mp, bmap->me_owner)));
+       switch (bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               op_ok = true;
+               break;
+       default:
+               op_ok = false;
+               break;
+       }
+       if (!op_ok || startblock_fsb == 0 ||
+           bmap->me_len == 0 ||
+           inode_fsb == 0 ||
+           startblock_fsb >= mp->m_sb.sb_dblocks ||
+           bmap->me_len >= mp->m_sb.sb_agblocks ||
+           inode_fsb >= mp->m_sb.sb_dblocks ||
+           (bmap->me_flags & ~XFS_BMAP_EXTENT_FLAGS)) {
+               /*
+                * This will pull the BUI from the AIL and
+                * free the memory associated with it.
+                */
+               set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+               xfs_bui_release(buip);
+               return -EIO;
+       }
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+       if (error)
+               return error;
+       budp = xfs_trans_get_bud(tp, buip);
+
+       /* Grab the inode. */
+       error = xfs_iget(mp, tp, bmap->me_owner, 0, XFS_ILOCK_EXCL, &ip);
+       if (error)
+               goto err_inode;
+
+       if (VFS_I(ip)->i_nlink == 0)
+               xfs_iflags_set(ip, XFS_IRECOVERY);
+       xfs_defer_init(&dfops, &firstfsb);
+
+       /* Process deferred bmap item. */
+       state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
+                       XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+       whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
+                       XFS_ATTR_FORK : XFS_DATA_FORK;
+       bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
+       switch (bui_type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               type = bui_type;
+               break;
+       default:
+               error = -EFSCORRUPTED;
+               goto err_dfops;
+       }
+       xfs_trans_ijoin(tp, ip, 0);
+
+       error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
+                       ip, whichfork, bmap->me_startoff,
+                       bmap->me_startblock, bmap->me_len,
+                       state);
+       if (error)
+               goto err_dfops;
+
+       /* Finish transaction, free inodes. */
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto err_dfops;
+
+       set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       IRELE(ip);
+
+       return error;
+
+err_dfops:
+       xfs_defer_cancel(&dfops);
+err_inode:
+       xfs_trans_cancel(tp);
+       if (ip) {
+               xfs_iunlock(ip, XFS_ILOCK_EXCL);
+               IRELE(ip);
+       }
+       return error;
+}
diff --git a/fs/xfs/xfs_bmap_item.h b/fs/xfs/xfs_bmap_item.h
new file mode 100644 (file)
index 0000000..c867daa
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef        __XFS_BMAP_ITEM_H__
+#define        __XFS_BMAP_ITEM_H__
+
+/*
+ * There are (currently) two pairs of bmap btree redo item types: map & unmap.
+ * The common abbreviations for these are BUI (bmap update intent) and BUD
+ * (bmap update done).  The redo item type is encoded in the flags field of
+ * each xfs_map_extent.
+ *
+ * *I items should be recorded in the *first* of a series of rolled
+ * transactions, and the *D items should be recorded in the same transaction
+ * that records the associated bmbt updates.
+ *
+ * Should the system crash after the commit of the first transaction but
+ * before the commit of the final transaction in a series, log recovery will
+ * use the redo information recorded by the intent items to replay the
+ * bmbt metadata updates in the non-first transaction.
+ */
+
+/* kernel only BUI/BUD definitions */
+
+struct xfs_mount;
+struct kmem_zone;
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define        XFS_BUI_MAX_FAST_EXTENTS        1
+
+/*
+ * Define BUI flag bits. Manipulated by set/clear/test_bit operators.
+ */
+#define        XFS_BUI_RECOVERED               1
+
+/*
+ * This is the "bmap update intent" log item.  It is used to log the fact that
+ * some reverse mappings need to change.  It is used in conjunction with the
+ * "bmap update done" log item described below.
+ *
+ * These log items follow the same rules as struct xfs_efi_log_item; see the
+ * comments about that structure (in xfs_extfree_item.h) for more details.
+ */
+struct xfs_bui_log_item {
+       struct xfs_log_item             bui_item;
+       atomic_t                        bui_refcount;
+       atomic_t                        bui_next_extent;
+       unsigned long                   bui_flags;      /* misc flags */
+       struct xfs_bui_log_format       bui_format;
+};
+
+static inline size_t
+xfs_bui_log_item_sizeof(
+       unsigned int            nr)
+{
+       return offsetof(struct xfs_bui_log_item, bui_format) +
+                       xfs_bui_log_format_sizeof(nr);
+}
+
+/*
+ * This is the "bmap update done" log item.  It is used to log the fact that
+ * some bmbt updates mentioned in an earlier bui item have been performed.
+ */
+struct xfs_bud_log_item {
+       struct xfs_log_item             bud_item;
+       struct xfs_bui_log_item         *bud_buip;
+       struct xfs_bud_log_format       bud_format;
+};
+
+extern struct kmem_zone        *xfs_bui_zone;
+extern struct kmem_zone        *xfs_bud_zone;
+
+struct xfs_bui_log_item *xfs_bui_init(struct xfs_mount *);
+struct xfs_bud_log_item *xfs_bud_init(struct xfs_mount *,
+               struct xfs_bui_log_item *);
+void xfs_bui_item_free(struct xfs_bui_log_item *);
+void xfs_bui_release(struct xfs_bui_log_item *);
+int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip);
+
+#endif /* __XFS_BMAP_ITEM_H__ */
index e827d65..552465e 100644 (file)
@@ -42,6 +42,9 @@
 #include "xfs_icache.h"
 #include "xfs_log.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_iomap.h"
+#include "xfs_reflink.h"
+#include "xfs_refcount.h"
 
 /* Kernel only BMAP related definitions and functions */
 
@@ -389,11 +392,13 @@ xfs_bmap_count_blocks(
 STATIC int
 xfs_getbmapx_fix_eof_hole(
        xfs_inode_t             *ip,            /* xfs incore inode pointer */
+       int                     whichfork,
        struct getbmapx         *out,           /* output structure */
        int                     prealloced,     /* this is a file with
                                                 * preallocated data space */
        __int64_t               end,            /* last block requested */
-       xfs_fsblock_t           startblock)
+       xfs_fsblock_t           startblock,
+       bool                    moretocome)
 {
        __int64_t               fixlen;
        xfs_mount_t             *mp;            /* file system mount point */
@@ -418,8 +423,9 @@ xfs_getbmapx_fix_eof_hole(
                else
                        out->bmv_block = xfs_fsb_to_db(ip, startblock);
                fileblock = XFS_BB_TO_FSB(ip->i_mount, out->bmv_offset);
-               ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
-               if (xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
+               ifp = XFS_IFORK_PTR(ip, whichfork);
+               if (!moretocome &&
+                   xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
                   (lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
                        out->bmv_oflags |= BMV_OF_LAST;
        }
@@ -427,6 +433,81 @@ xfs_getbmapx_fix_eof_hole(
        return 1;
 }
 
+/* Adjust the reported bmap around shared/unshared extent transitions. */
+STATIC int
+xfs_getbmap_adjust_shared(
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       struct xfs_bmbt_irec            *map,
+       struct getbmapx                 *out,
+       struct xfs_bmbt_irec            *next_map)
+{
+       struct xfs_mount                *mp = ip->i_mount;
+       xfs_agnumber_t                  agno;
+       xfs_agblock_t                   agbno;
+       xfs_agblock_t                   ebno;
+       xfs_extlen_t                    elen;
+       xfs_extlen_t                    nlen;
+       int                             error;
+
+       next_map->br_startblock = NULLFSBLOCK;
+       next_map->br_startoff = NULLFILEOFF;
+       next_map->br_blockcount = 0;
+
+       /* Only written data blocks can be shared. */
+       if (!xfs_is_reflink_inode(ip) || whichfork != XFS_DATA_FORK ||
+           map->br_startblock == DELAYSTARTBLOCK ||
+           map->br_startblock == HOLESTARTBLOCK ||
+           ISUNWRITTEN(map))
+               return 0;
+
+       agno = XFS_FSB_TO_AGNO(mp, map->br_startblock);
+       agbno = XFS_FSB_TO_AGBNO(mp, map->br_startblock);
+       error = xfs_reflink_find_shared(mp, agno, agbno, map->br_blockcount,
+                       &ebno, &elen, true);
+       if (error)
+               return error;
+
+       if (ebno == NULLAGBLOCK) {
+               /* No shared blocks at all. */
+               return 0;
+       } else if (agbno == ebno) {
+               /*
+                * Shared extent at (agbno, elen).  Shrink the reported
+                * extent length and prepare to move the start of map[i]
+                * to agbno+elen, with the aim of (re)formatting the new
+                * map[i] the next time through the inner loop.
+                */
+               out->bmv_length = XFS_FSB_TO_BB(mp, elen);
+               out->bmv_oflags |= BMV_OF_SHARED;
+               if (elen != map->br_blockcount) {
+                       *next_map = *map;
+                       next_map->br_startblock += elen;
+                       next_map->br_startoff += elen;
+                       next_map->br_blockcount -= elen;
+               }
+               map->br_blockcount -= elen;
+       } else {
+               /*
+                * There's an unshared extent (agbno, ebno - agbno)
+                * followed by shared extent at (ebno, elen).  Shrink
+                * the reported extent length to cover only the unshared
+                * extent and prepare to move up the start of map[i] to
+                * ebno, with the aim of (re)formatting the new map[i]
+                * the next time through the inner loop.
+                */
+               *next_map = *map;
+               nlen = ebno - agbno;
+               out->bmv_length = XFS_FSB_TO_BB(mp, nlen);
+               next_map->br_startblock += nlen;
+               next_map->br_startoff += nlen;
+               next_map->br_blockcount -= nlen;
+               map->br_blockcount -= nlen;
+       }
+
+       return 0;
+}
+
 /*
  * Get inode's extents as described in bmv, and format for output.
  * Calls formatter to fill the user's buffer until all extents
@@ -459,12 +540,28 @@ xfs_getbmap(
        int                     iflags;         /* interface flags */
        int                     bmapi_flags;    /* flags for xfs_bmapi */
        int                     cur_ext = 0;
+       struct xfs_bmbt_irec    inject_map;
 
        mp = ip->i_mount;
        iflags = bmv->bmv_iflags;
-       whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
 
-       if (whichfork == XFS_ATTR_FORK) {
+#ifndef DEBUG
+       /* Only allow CoW fork queries if we're debugging. */
+       if (iflags & BMV_IF_COWFORK)
+               return -EINVAL;
+#endif
+       if ((iflags & BMV_IF_ATTRFORK) && (iflags & BMV_IF_COWFORK))
+               return -EINVAL;
+
+       if (iflags & BMV_IF_ATTRFORK)
+               whichfork = XFS_ATTR_FORK;
+       else if (iflags & BMV_IF_COWFORK)
+               whichfork = XFS_COW_FORK;
+       else
+               whichfork = XFS_DATA_FORK;
+
+       switch (whichfork) {
+       case XFS_ATTR_FORK:
                if (XFS_IFORK_Q(ip)) {
                        if (ip->i_d.di_aformat != XFS_DINODE_FMT_EXTENTS &&
                            ip->i_d.di_aformat != XFS_DINODE_FMT_BTREE &&
@@ -480,7 +577,20 @@ xfs_getbmap(
 
                prealloced = 0;
                fixlen = 1LL << 32;
-       } else {
+               break;
+       case XFS_COW_FORK:
+               if (ip->i_cformat != XFS_DINODE_FMT_EXTENTS)
+                       return -EINVAL;
+
+               if (xfs_get_cowextsz_hint(ip)) {
+                       prealloced = 1;
+                       fixlen = mp->m_super->s_maxbytes;
+               } else {
+                       prealloced = 0;
+                       fixlen = XFS_ISIZE(ip);
+               }
+               break;
+       default:
                if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS &&
                    ip->i_d.di_format != XFS_DINODE_FMT_BTREE &&
                    ip->i_d.di_format != XFS_DINODE_FMT_LOCAL)
@@ -494,6 +604,7 @@ xfs_getbmap(
                        prealloced = 0;
                        fixlen = XFS_ISIZE(ip);
                }
+               break;
        }
 
        if (bmv->bmv_length == -1) {
@@ -520,7 +631,8 @@ xfs_getbmap(
                return -ENOMEM;
 
        xfs_ilock(ip, XFS_IOLOCK_SHARED);
-       if (whichfork == XFS_DATA_FORK) {
+       switch (whichfork) {
+       case XFS_DATA_FORK:
                if (!(iflags & BMV_IF_DELALLOC) &&
                    (ip->i_delayed_blks || XFS_ISIZE(ip) > ip->i_d.di_size)) {
                        error = filemap_write_and_wait(VFS_I(ip)->i_mapping);
@@ -538,8 +650,14 @@ xfs_getbmap(
                }
 
                lock = xfs_ilock_data_map_shared(ip);
-       } else {
+               break;
+       case XFS_COW_FORK:
+               lock = XFS_ILOCK_SHARED;
+               xfs_ilock(ip, lock);
+               break;
+       case XFS_ATTR_FORK:
                lock = xfs_ilock_attr_map_shared(ip);
+               break;
        }
 
        /*
@@ -581,7 +699,8 @@ xfs_getbmap(
                        goto out_free_map;
                ASSERT(nmap <= subnex);
 
-               for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
+               for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
+                               cur_ext < bmv->bmv_count; i++) {
                        out[cur_ext].bmv_oflags = 0;
                        if (map[i].br_state == XFS_EXT_UNWRITTEN)
                                out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -614,9 +733,16 @@ xfs_getbmap(
                                goto out_free_map;
                        }
 
-                       if (!xfs_getbmapx_fix_eof_hole(ip, &out[cur_ext],
-                                       prealloced, bmvend,
-                                       map[i].br_startblock))
+                       /* Is this a shared block? */
+                       error = xfs_getbmap_adjust_shared(ip, whichfork,
+                                       &map[i], &out[cur_ext], &inject_map);
+                       if (error)
+                               goto out_free_map;
+
+                       if (!xfs_getbmapx_fix_eof_hole(ip, whichfork,
+                                       &out[cur_ext], prealloced, bmvend,
+                                       map[i].br_startblock,
+                                       inject_map.br_startblock != NULLFSBLOCK))
                                goto out_free_map;
 
                        bmv->bmv_offset =
@@ -636,11 +762,16 @@ xfs_getbmap(
                                continue;
                        }
 
-                       nexleft--;
+                       if (inject_map.br_startblock != NULLFSBLOCK) {
+                               map[i] = inject_map;
+                               i--;
+                       } else
+                               nexleft--;
                        bmv->bmv_entries++;
                        cur_ext++;
                }
-       } while (nmap && nexleft && bmv->bmv_length);
+       } while (nmap && nexleft && bmv->bmv_length &&
+                cur_ext < bmv->bmv_count);
 
  out_free_map:
        kmem_free(map);
@@ -1433,8 +1564,8 @@ xfs_insert_file_space(
  */
 static int
 xfs_swap_extents_check_format(
-       xfs_inode_t     *ip,    /* target inode */
-       xfs_inode_t     *tip)   /* tmp inode */
+       struct xfs_inode        *ip,    /* target inode */
+       struct xfs_inode        *tip)   /* tmp inode */
 {
 
        /* Should never get a local format */
@@ -1449,6 +1580,13 @@ xfs_swap_extents_check_format(
        if (ip->i_d.di_nextents < tip->i_d.di_nextents)
                return -EINVAL;
 
+       /*
+        * If we have to use the (expensive) rmap swap method, we can
+        * handle any number of extents and any format.
+        */
+       if (xfs_sb_version_hasrmapbt(&ip->i_mount->m_sb))
+               return 0;
+
        /*
         * if the target inode is in extent form and the temp inode is in btree
         * form then we will end up with the target inode in the wrong format
@@ -1518,125 +1656,161 @@ xfs_swap_extent_flush(
        return 0;
 }
 
-int
-xfs_swap_extents(
-       xfs_inode_t     *ip,    /* target inode */
-       xfs_inode_t     *tip,   /* tmp inode */
-       xfs_swapext_t   *sxp)
+/*
+ * Move extents from one file to another, when rmap is enabled.
+ */
+STATIC int
+xfs_swap_extent_rmap(
+       struct xfs_trans                **tpp,
+       struct xfs_inode                *ip,
+       struct xfs_inode                *tip)
 {
-       xfs_mount_t     *mp = ip->i_mount;
-       xfs_trans_t     *tp;
-       xfs_bstat_t     *sbp = &sxp->sx_stat;
-       xfs_ifork_t     *tempifp, *ifp, *tifp;
-       int             src_log_flags, target_log_flags;
-       int             error = 0;
-       int             aforkblks = 0;
-       int             taforkblks = 0;
-       __uint64_t      tmp;
-       int             lock_flags;
-
-       /* XXX: we can't do this with rmap, will fix later */
-       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
-               return -EOPNOTSUPP;
-
-       tempifp = kmem_alloc(sizeof(xfs_ifork_t), KM_MAYFAIL);
-       if (!tempifp) {
-               error = -ENOMEM;
-               goto out;
-       }
+       struct xfs_bmbt_irec            irec;
+       struct xfs_bmbt_irec            uirec;
+       struct xfs_bmbt_irec            tirec;
+       xfs_fileoff_t                   offset_fsb;
+       xfs_fileoff_t                   end_fsb;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error;
+       xfs_filblks_t                   ilen;
+       xfs_filblks_t                   rlen;
+       int                             nimaps;
+       __uint64_t                      tip_flags2;
 
        /*
-        * Lock the inodes against other IO, page faults and truncate to
-        * begin with.  Then we can ensure the inodes are flushed and have no
-        * page cache safely. Once we have done this we can take the ilocks and
-        * do the rest of the checks.
+        * If the source file has shared blocks, we must flag the donor
+        * file as having shared blocks so that we get the shared-block
+        * rmap functions when we go to fix up the rmaps.  The flags
+        * will be switch for reals later.
         */
-       lock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
-       xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
-       xfs_lock_two_inodes(ip, tip, XFS_MMAPLOCK_EXCL);
-
-       /* Verify that both files have the same format */
-       if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) {
-               error = -EINVAL;
-               goto out_unlock;
-       }
+       tip_flags2 = tip->i_d.di_flags2;
+       if (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK)
+               tip->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+
+       offset_fsb = 0;
+       end_fsb = XFS_B_TO_FSB(ip->i_mount, i_size_read(VFS_I(ip)));
+       count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+
+       while (count_fsb) {
+               /* Read extent from the donor file */
+               nimaps = 1;
+               error = xfs_bmapi_read(tip, offset_fsb, count_fsb, &tirec,
+                               &nimaps, 0);
+               if (error)
+                       goto out;
+               ASSERT(nimaps == 1);
+               ASSERT(tirec.br_startblock != DELAYSTARTBLOCK);
+
+               trace_xfs_swap_extent_rmap_remap(tip, &tirec);
+               ilen = tirec.br_blockcount;
+
+               /* Unmap the old blocks in the source file. */
+               while (tirec.br_blockcount) {
+                       xfs_defer_init(&dfops, &firstfsb);
+                       trace_xfs_swap_extent_rmap_remap_piece(tip, &tirec);
+
+                       /* Read extent from the source file */
+                       nimaps = 1;
+                       error = xfs_bmapi_read(ip, tirec.br_startoff,
+                                       tirec.br_blockcount, &irec,
+                                       &nimaps, 0);
+                       if (error)
+                               goto out_defer;
+                       ASSERT(nimaps == 1);
+                       ASSERT(tirec.br_startoff == irec.br_startoff);
+                       trace_xfs_swap_extent_rmap_remap_piece(ip, &irec);
+
+                       /* Trim the extent. */
+                       uirec = tirec;
+                       uirec.br_blockcount = rlen = min_t(xfs_filblks_t,
+                                       tirec.br_blockcount,
+                                       irec.br_blockcount);
+                       trace_xfs_swap_extent_rmap_remap_piece(tip, &uirec);
+
+                       /* Remove the mapping from the donor file. */
+                       error = xfs_bmap_unmap_extent((*tpp)->t_mountp, &dfops,
+                                       tip, &uirec);
+                       if (error)
+                               goto out_defer;
 
-       /* Verify both files are either real-time or non-realtime */
-       if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
-               error = -EINVAL;
-               goto out_unlock;
-       }
+                       /* Remove the mapping from the source file. */
+                       error = xfs_bmap_unmap_extent((*tpp)->t_mountp, &dfops,
+                                       ip, &irec);
+                       if (error)
+                               goto out_defer;
 
-       error = xfs_swap_extent_flush(ip);
-       if (error)
-               goto out_unlock;
-       error = xfs_swap_extent_flush(tip);
-       if (error)
-               goto out_unlock;
+                       /* Map the donor file's blocks into the source file. */
+                       error = xfs_bmap_map_extent((*tpp)->t_mountp, &dfops,
+                                       ip, &uirec);
+                       if (error)
+                               goto out_defer;
 
-       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
-       if (error)
-               goto out_unlock;
+                       /* Map the source file's blocks into the donor file. */
+                       error = xfs_bmap_map_extent((*tpp)->t_mountp, &dfops,
+                                       tip, &irec);
+                       if (error)
+                               goto out_defer;
 
-       /*
-        * Lock and join the inodes to the tansaction so that transaction commit
-        * or cancel will unlock the inodes from this point onwards.
-        */
-       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
-       lock_flags |= XFS_ILOCK_EXCL;
-       xfs_trans_ijoin(tp, ip, lock_flags);
-       xfs_trans_ijoin(tp, tip, lock_flags);
+                       error = xfs_defer_finish(tpp, &dfops, ip);
+                       if (error)
+                               goto out_defer;
 
+                       tirec.br_startoff += rlen;
+                       if (tirec.br_startblock != HOLESTARTBLOCK &&
+                           tirec.br_startblock != DELAYSTARTBLOCK)
+                               tirec.br_startblock += rlen;
+                       tirec.br_blockcount -= rlen;
+               }
 
-       /* Verify all data are being swapped */
-       if (sxp->sx_offset != 0 ||
-           sxp->sx_length != ip->i_d.di_size ||
-           sxp->sx_length != tip->i_d.di_size) {
-               error = -EFAULT;
-               goto out_trans_cancel;
+               /* Roll on... */
+               count_fsb -= ilen;
+               offset_fsb += ilen;
        }
 
-       trace_xfs_swap_extent_before(ip, 0);
-       trace_xfs_swap_extent_before(tip, 1);
+       tip->i_d.di_flags2 = tip_flags2;
+       return 0;
 
-       /* check inode formats now that data is flushed */
-       error = xfs_swap_extents_check_format(ip, tip);
-       if (error) {
-               xfs_notice(mp,
-                   "%s: inode 0x%llx format is incompatible for exchanging.",
-                               __func__, ip->i_ino);
-               goto out_trans_cancel;
-       }
+out_defer:
+       xfs_defer_cancel(&dfops);
+out:
+       trace_xfs_swap_extent_rmap_error(ip, error, _RET_IP_);
+       tip->i_d.di_flags2 = tip_flags2;
+       return error;
+}
+
+/* Swap the extents of two files by swapping data forks. */
+STATIC int
+xfs_swap_extent_forks(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       struct xfs_inode        *tip,
+       int                     *src_log_flags,
+       int                     *target_log_flags)
+{
+       struct xfs_ifork        tempifp, *ifp, *tifp;
+       int                     aforkblks = 0;
+       int                     taforkblks = 0;
+       __uint64_t              tmp;
+       int                     error;
 
-       /*
-        * Compare the current change & modify times with that
-        * passed in.  If they differ, we abort this swap.
-        * This is the mechanism used to ensure the calling
-        * process that the file was not changed out from
-        * under it.
-        */
-       if ((sbp->bs_ctime.tv_sec != VFS_I(ip)->i_ctime.tv_sec) ||
-           (sbp->bs_ctime.tv_nsec != VFS_I(ip)->i_ctime.tv_nsec) ||
-           (sbp->bs_mtime.tv_sec != VFS_I(ip)->i_mtime.tv_sec) ||
-           (sbp->bs_mtime.tv_nsec != VFS_I(ip)->i_mtime.tv_nsec)) {
-               error = -EBUSY;
-               goto out_trans_cancel;
-       }
        /*
         * Count the number of extended attribute blocks
         */
        if ( ((XFS_IFORK_Q(ip) != 0) && (ip->i_d.di_anextents > 0)) &&
             (ip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
-               error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &aforkblks);
+               error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK,
+                               &aforkblks);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
        if ( ((XFS_IFORK_Q(tip) != 0) && (tip->i_d.di_anextents > 0)) &&
             (tip->i_d.di_aformat != XFS_DINODE_FMT_LOCAL)) {
                error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK,
-                       &taforkblks);
+                               &taforkblks);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        /*
@@ -1645,31 +1819,23 @@ xfs_swap_extents(
         * buffers, and so the validation done on read will expect the owner
         * field to be correctly set. Once we change the owners, we can swap the
         * inode forks.
-        *
-        * Note the trickiness in setting the log flags - we set the owner log
-        * flag on the opposite inode (i.e. the inode we are setting the new
-        * owner to be) because once we swap the forks and log that, log
-        * recovery is going to see the fork as owned by the swapped inode,
-        * not the pre-swapped inodes.
         */
-       src_log_flags = XFS_ILOG_CORE;
-       target_log_flags = XFS_ILOG_CORE;
        if (ip->i_d.di_version == 3 &&
            ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
-               target_log_flags |= XFS_ILOG_DOWNER;
+               (*target_log_flags) |= XFS_ILOG_DOWNER;
                error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK,
                                              tip->i_ino, NULL);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        if (tip->i_d.di_version == 3 &&
            tip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
-               src_log_flags |= XFS_ILOG_DOWNER;
+               (*src_log_flags) |= XFS_ILOG_DOWNER;
                error = xfs_bmbt_change_owner(tp, tip, XFS_DATA_FORK,
                                              ip->i_ino, NULL);
                if (error)
-                       goto out_trans_cancel;
+                       return error;
        }
 
        /*
@@ -1677,9 +1843,9 @@ xfs_swap_extents(
         */
        ifp = &ip->i_df;
        tifp = &tip->i_df;
-       *tempifp = *ifp;        /* struct copy */
+       tempifp = *ifp;         /* struct copy */
        *ifp = *tifp;           /* struct copy */
-       *tifp = *tempifp;       /* struct copy */
+       *tifp = tempifp;        /* struct copy */
 
        /*
         * Fix the on-disk inode values
@@ -1719,12 +1885,12 @@ xfs_swap_extents(
                        ifp->if_u1.if_extents =
                                ifp->if_u2.if_inline_ext;
                }
-               src_log_flags |= XFS_ILOG_DEXT;
+               (*src_log_flags) |= XFS_ILOG_DEXT;
                break;
        case XFS_DINODE_FMT_BTREE:
                ASSERT(ip->i_d.di_version < 3 ||
-                      (src_log_flags & XFS_ILOG_DOWNER));
-               src_log_flags |= XFS_ILOG_DBROOT;
+                      (*src_log_flags & XFS_ILOG_DOWNER));
+               (*src_log_flags) |= XFS_ILOG_DBROOT;
                break;
        }
 
@@ -1738,15 +1904,166 @@ xfs_swap_extents(
                        tifp->if_u1.if_extents =
                                tifp->if_u2.if_inline_ext;
                }
-               target_log_flags |= XFS_ILOG_DEXT;
+               (*target_log_flags) |= XFS_ILOG_DEXT;
                break;
        case XFS_DINODE_FMT_BTREE:
-               target_log_flags |= XFS_ILOG_DBROOT;
+               (*target_log_flags) |= XFS_ILOG_DBROOT;
                ASSERT(tip->i_d.di_version < 3 ||
-                      (target_log_flags & XFS_ILOG_DOWNER));
+                      (*target_log_flags & XFS_ILOG_DOWNER));
                break;
        }
 
+       return 0;
+}
+
+int
+xfs_swap_extents(
+       struct xfs_inode        *ip,    /* target inode */
+       struct xfs_inode        *tip,   /* tmp inode */
+       struct xfs_swapext      *sxp)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       struct xfs_bstat        *sbp = &sxp->sx_stat;
+       int                     src_log_flags, target_log_flags;
+       int                     error = 0;
+       int                     lock_flags;
+       struct xfs_ifork        *cowfp;
+       __uint64_t              f;
+       int                     resblks;
+
+       /*
+        * Lock the inodes against other IO, page faults and truncate to
+        * begin with.  Then we can ensure the inodes are flushed and have no
+        * page cache safely. Once we have done this we can take the ilocks and
+        * do the rest of the checks.
+        */
+       lock_flags = XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL;
+       xfs_lock_two_inodes(ip, tip, XFS_IOLOCK_EXCL);
+       xfs_lock_two_inodes(ip, tip, XFS_MMAPLOCK_EXCL);
+
+       /* Verify that both files have the same format */
+       if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) {
+               error = -EINVAL;
+               goto out_unlock;
+       }
+
+       /* Verify both files are either real-time or non-realtime */
+       if (XFS_IS_REALTIME_INODE(ip) != XFS_IS_REALTIME_INODE(tip)) {
+               error = -EINVAL;
+               goto out_unlock;
+       }
+
+       error = xfs_swap_extent_flush(ip);
+       if (error)
+               goto out_unlock;
+       error = xfs_swap_extent_flush(tip);
+       if (error)
+               goto out_unlock;
+
+       /*
+        * Extent "swapping" with rmap requires a permanent reservation and
+        * a block reservation because it's really just a remap operation
+        * performed with log redo items!
+        */
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
+               /*
+                * Conceptually this shouldn't affect the shape of either
+                * bmbt, but since we atomically move extents one by one,
+                * we reserve enough space to rebuild both trees.
+                */
+               resblks = XFS_SWAP_RMAP_SPACE_RES(mp,
+                               XFS_IFORK_NEXTENTS(ip, XFS_DATA_FORK),
+                               XFS_DATA_FORK) +
+                         XFS_SWAP_RMAP_SPACE_RES(mp,
+                               XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK),
+                               XFS_DATA_FORK);
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
+                               0, 0, &tp);
+       } else
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0,
+                               0, 0, &tp);
+       if (error)
+               goto out_unlock;
+
+       /*
+        * Lock and join the inodes to the tansaction so that transaction commit
+        * or cancel will unlock the inodes from this point onwards.
+        */
+       xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL);
+       lock_flags |= XFS_ILOCK_EXCL;
+       xfs_trans_ijoin(tp, ip, 0);
+       xfs_trans_ijoin(tp, tip, 0);
+
+
+       /* Verify all data are being swapped */
+       if (sxp->sx_offset != 0 ||
+           sxp->sx_length != ip->i_d.di_size ||
+           sxp->sx_length != tip->i_d.di_size) {
+               error = -EFAULT;
+               goto out_trans_cancel;
+       }
+
+       trace_xfs_swap_extent_before(ip, 0);
+       trace_xfs_swap_extent_before(tip, 1);
+
+       /* check inode formats now that data is flushed */
+       error = xfs_swap_extents_check_format(ip, tip);
+       if (error) {
+               xfs_notice(mp,
+                   "%s: inode 0x%llx format is incompatible for exchanging.",
+                               __func__, ip->i_ino);
+               goto out_trans_cancel;
+       }
+
+       /*
+        * Compare the current change & modify times with that
+        * passed in.  If they differ, we abort this swap.
+        * This is the mechanism used to ensure the calling
+        * process that the file was not changed out from
+        * under it.
+        */
+       if ((sbp->bs_ctime.tv_sec != VFS_I(ip)->i_ctime.tv_sec) ||
+           (sbp->bs_ctime.tv_nsec != VFS_I(ip)->i_ctime.tv_nsec) ||
+           (sbp->bs_mtime.tv_sec != VFS_I(ip)->i_mtime.tv_sec) ||
+           (sbp->bs_mtime.tv_nsec != VFS_I(ip)->i_mtime.tv_nsec)) {
+               error = -EBUSY;
+               goto out_trans_cancel;
+       }
+
+       /*
+        * Note the trickiness in setting the log flags - we set the owner log
+        * flag on the opposite inode (i.e. the inode we are setting the new
+        * owner to be) because once we swap the forks and log that, log
+        * recovery is going to see the fork as owned by the swapped inode,
+        * not the pre-swapped inodes.
+        */
+       src_log_flags = XFS_ILOG_CORE;
+       target_log_flags = XFS_ILOG_CORE;
+
+       if (xfs_sb_version_hasrmapbt(&mp->m_sb))
+               error = xfs_swap_extent_rmap(&tp, ip, tip);
+       else
+               error = xfs_swap_extent_forks(tp, ip, tip, &src_log_flags,
+                               &target_log_flags);
+       if (error)
+               goto out_trans_cancel;
+
+       /* Do we have to swap reflink flags? */
+       if ((ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK) ^
+           (tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK)) {
+               f = ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               ip->i_d.di_flags2 |= tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+               tip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               tip->i_d.di_flags2 |= f & XFS_DIFLAG2_REFLINK;
+               cowfp = ip->i_cowfp;
+               ip->i_cowfp = tip->i_cowfp;
+               tip->i_cowfp = cowfp;
+               xfs_inode_set_cowblocks_tag(ip);
+               xfs_inode_set_cowblocks_tag(tip);
+       }
+
        xfs_trans_log_inode(tp, ip,  src_log_flags);
        xfs_trans_log_inode(tp, tip, target_log_flags);
 
@@ -1761,16 +2078,16 @@ xfs_swap_extents(
 
        trace_xfs_swap_extent_after(ip, 0);
        trace_xfs_swap_extent_after(tip, 1);
-out:
-       kmem_free(tempifp);
-       return error;
 
-out_unlock:
        xfs_iunlock(ip, lock_flags);
        xfs_iunlock(tip, lock_flags);
-       goto out;
+       return error;
 
 out_trans_cancel:
        xfs_trans_cancel(tp);
-       goto out;
+
+out_unlock:
+       xfs_iunlock(ip, lock_flags);
+       xfs_iunlock(tip, lock_flags);
+       return error;
 }
index f44f799..2981698 100644 (file)
@@ -84,7 +84,8 @@ xfs_dir2_sf_getdents(
 
        sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data;
 
-       ASSERT(dp->i_d.di_size >= xfs_dir2_sf_hdr_size(sfp->i8count));
+       if (dp->i_d.di_size < xfs_dir2_sf_hdr_size(sfp->i8count))
+               return -EFSCORRUPTED;
 
        /*
         * If the block number in the offset is out of range, we're done.
index 3d22470..05f8666 100644 (file)
@@ -92,7 +92,11 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define XFS_ERRTAG_BMAPIFORMAT                         21
 #define XFS_ERRTAG_FREE_EXTENT                         22
 #define XFS_ERRTAG_RMAP_FINISH_ONE                     23
-#define XFS_ERRTAG_MAX                                 24
+#define XFS_ERRTAG_REFCOUNT_CONTINUE_UPDATE            24
+#define XFS_ERRTAG_REFCOUNT_FINISH_ONE                 25
+#define XFS_ERRTAG_BMAP_FINISH_ONE                     26
+#define XFS_ERRTAG_AG_RESV_CRITICAL                    27
+#define XFS_ERRTAG_MAX                                 28
 
 /*
  * Random factors for above tags, 1 means always, 2 means 1/2 time, etc.
@@ -121,6 +125,10 @@ extern void xfs_verifier_error(struct xfs_buf *bp);
 #define        XFS_RANDOM_BMAPIFORMAT                          XFS_RANDOM_DEFAULT
 #define XFS_RANDOM_FREE_EXTENT                         1
 #define XFS_RANDOM_RMAP_FINISH_ONE                     1
+#define XFS_RANDOM_REFCOUNT_CONTINUE_UPDATE            1
+#define XFS_RANDOM_REFCOUNT_FINISH_ONE                 1
+#define XFS_RANDOM_BMAP_FINISH_ONE                     1
+#define XFS_RANDOM_AG_RESV_CRITICAL                    4
 
 #ifdef DEBUG
 extern int xfs_error_test_active;
index 2bc58b3..a314fc7 100644 (file)
@@ -38,6 +38,7 @@
 #include "xfs_icache.h"
 #include "xfs_pnfs.h"
 #include "xfs_iomap.h"
+#include "xfs_reflink.h"
 
 #include <linux/dcache.h>
 #include <linux/falloc.h>
@@ -634,6 +635,13 @@ xfs_file_dio_aio_write(
 
        trace_xfs_file_direct_write(ip, count, iocb->ki_pos);
 
+       /* If this is a block-aligned directio CoW, remap immediately. */
+       if (xfs_is_reflink_inode(ip) && !unaligned_io) {
+               ret = xfs_reflink_allocate_cow_range(ip, iocb->ki_pos, count);
+               if (ret)
+                       goto out;
+       }
+
        data = *from;
        ret = __blockdev_direct_IO(iocb, inode, target->bt_bdev, &data,
                        xfs_get_blocks_direct, xfs_end_io_direct_write,
@@ -735,6 +743,9 @@ write_retry:
                enospc = xfs_inode_free_quota_eofblocks(ip);
                if (enospc)
                        goto write_retry;
+               enospc = xfs_inode_free_quota_cowblocks(ip);
+               if (enospc)
+                       goto write_retry;
        } else if (ret == -ENOSPC && !enospc) {
                struct xfs_eofblocks eofb = {0};
 
@@ -774,10 +785,20 @@ xfs_file_write_iter(
 
        if (IS_DAX(inode))
                ret = xfs_file_dax_write(iocb, from);
-       else if (iocb->ki_flags & IOCB_DIRECT)
+       else if (iocb->ki_flags & IOCB_DIRECT) {
+               /*
+                * Allow a directio write to fall back to a buffered
+                * write *only* in the case that we're doing a reflink
+                * CoW.  In all other directio scenarios we do not
+                * allow an operation to fall back to buffered mode.
+                */
                ret = xfs_file_dio_aio_write(iocb, from);
-       else
+               if (ret == -EREMCHG)
+                       goto buffered;
+       } else {
+buffered:
                ret = xfs_file_buffered_aio_write(iocb, from);
+       }
 
        if (ret > 0) {
                XFS_STATS_ADD(ip->i_mount, xs_write_bytes, ret);
@@ -791,7 +812,7 @@ xfs_file_write_iter(
 #define        XFS_FALLOC_FL_SUPPORTED                                         \
                (FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |           \
                 FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE |      \
-                FALLOC_FL_INSERT_RANGE)
+                FALLOC_FL_INSERT_RANGE | FALLOC_FL_UNSHARE_RANGE)
 
 STATIC long
 xfs_file_fallocate(
@@ -881,9 +902,15 @@ xfs_file_fallocate(
 
                if (mode & FALLOC_FL_ZERO_RANGE)
                        error = xfs_zero_file_space(ip, offset, len);
-               else
+               else {
+                       if (mode & FALLOC_FL_UNSHARE_RANGE) {
+                               error = xfs_reflink_unshare(ip, offset, len);
+                               if (error)
+                                       goto out_unlock;
+                       }
                        error = xfs_alloc_file_space(ip, offset, len,
                                                     XFS_BMAPI_PREALLOC);
+               }
                if (error)
                        goto out_unlock;
        }
@@ -920,6 +947,189 @@ out_unlock:
        return error;
 }
 
+/*
+ * Flush all file writes out to disk.
+ */
+static int
+xfs_file_wait_for_io(
+       struct inode    *inode,
+       loff_t          offset,
+       size_t          len)
+{
+       loff_t          rounding;
+       loff_t          ioffset;
+       loff_t          iendoffset;
+       loff_t          bs;
+       int             ret;
+
+       bs = inode->i_sb->s_blocksize;
+       inode_dio_wait(inode);
+
+       rounding = max_t(xfs_off_t, bs, PAGE_SIZE);
+       ioffset = round_down(offset, rounding);
+       iendoffset = round_up(offset + len, rounding) - 1;
+       ret = filemap_write_and_wait_range(inode->i_mapping, ioffset,
+                                          iendoffset);
+       return ret;
+}
+
+/* Hook up to the VFS reflink function */
+STATIC int
+xfs_file_share_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       u64             len,
+       bool            is_dedupe)
+{
+       struct inode    *inode_in;
+       struct inode    *inode_out;
+       ssize_t         ret;
+       loff_t          bs;
+       loff_t          isize;
+       int             same_inode;
+       loff_t          blen;
+       unsigned int    flags = 0;
+
+       inode_in = file_inode(file_in);
+       inode_out = file_inode(file_out);
+       bs = inode_out->i_sb->s_blocksize;
+
+       /* Don't touch certain kinds of inodes */
+       if (IS_IMMUTABLE(inode_out))
+               return -EPERM;
+       if (IS_SWAPFILE(inode_in) ||
+           IS_SWAPFILE(inode_out))
+               return -ETXTBSY;
+
+       /* Reflink only works within this filesystem. */
+       if (inode_in->i_sb != inode_out->i_sb)
+               return -EXDEV;
+       same_inode = (inode_in->i_ino == inode_out->i_ino);
+
+       /* Don't reflink dirs, pipes, sockets... */
+       if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+               return -EISDIR;
+       if (S_ISFIFO(inode_in->i_mode) || S_ISFIFO(inode_out->i_mode))
+               return -EINVAL;
+       if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
+               return -EINVAL;
+
+       /* Don't share DAX file data for now. */
+       if (IS_DAX(inode_in) || IS_DAX(inode_out))
+               return -EINVAL;
+
+       /* Are we going all the way to the end? */
+       isize = i_size_read(inode_in);
+       if (isize == 0)
+               return 0;
+       if (len == 0)
+               len = isize - pos_in;
+
+       /* Ensure offsets don't wrap and the input is inside i_size */
+       if (pos_in + len < pos_in || pos_out + len < pos_out ||
+           pos_in + len > isize)
+               return -EINVAL;
+
+       /* Don't allow dedupe past EOF in the dest file */
+       if (is_dedupe) {
+               loff_t  disize;
+
+               disize = i_size_read(inode_out);
+               if (pos_out >= disize || pos_out + len > disize)
+                       return -EINVAL;
+       }
+
+       /* If we're linking to EOF, continue to the block boundary. */
+       if (pos_in + len == isize)
+               blen = ALIGN(isize, bs) - pos_in;
+       else
+               blen = len;
+
+       /* Only reflink if we're aligned to block boundaries */
+       if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_in + blen, bs) ||
+           !IS_ALIGNED(pos_out, bs) || !IS_ALIGNED(pos_out + blen, bs))
+               return -EINVAL;
+
+       /* Don't allow overlapped reflink within the same file */
+       if (same_inode && pos_out + blen > pos_in && pos_out < pos_in + blen)
+               return -EINVAL;
+
+       /* Wait for the completion of any pending IOs on srcfile */
+       ret = xfs_file_wait_for_io(inode_in, pos_in, len);
+       if (ret)
+               goto out;
+       ret = xfs_file_wait_for_io(inode_out, pos_out, len);
+       if (ret)
+               goto out;
+
+       if (is_dedupe)
+               flags |= XFS_REFLINK_DEDUPE;
+       ret = xfs_reflink_remap_range(XFS_I(inode_in), pos_in, XFS_I(inode_out),
+                       pos_out, len, flags);
+       if (ret < 0)
+               goto out;
+
+out:
+       return ret;
+}
+
+STATIC ssize_t
+xfs_file_copy_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       size_t          len,
+       unsigned int    flags)
+{
+       int             error;
+
+       error = xfs_file_share_range(file_in, pos_in, file_out, pos_out,
+                                    len, false);
+       if (error)
+               return error;
+       return len;
+}
+
+STATIC int
+xfs_file_clone_range(
+       struct file     *file_in,
+       loff_t          pos_in,
+       struct file     *file_out,
+       loff_t          pos_out,
+       u64             len)
+{
+       return xfs_file_share_range(file_in, pos_in, file_out, pos_out,
+                                    len, false);
+}
+
+#define XFS_MAX_DEDUPE_LEN     (16 * 1024 * 1024)
+STATIC ssize_t
+xfs_file_dedupe_range(
+       struct file     *src_file,
+       u64             loff,
+       u64             len,
+       struct file     *dst_file,
+       u64             dst_loff)
+{
+       int             error;
+
+       /*
+        * Limit the total length we will dedupe for each operation.
+        * This is intended to bound the total time spent in this
+        * ioctl to something sane.
+        */
+       if (len > XFS_MAX_DEDUPE_LEN)
+               len = XFS_MAX_DEDUPE_LEN;
+
+       error = xfs_file_share_range(src_file, loff, dst_file, dst_loff,
+                                    len, true);
+       if (error)
+               return error;
+       return len;
+}
 
 STATIC int
 xfs_file_open(
@@ -1581,6 +1791,9 @@ const struct file_operations xfs_file_operations = {
        .fsync          = xfs_file_fsync,
        .get_unmapped_area = thp_get_unmapped_area,
        .fallocate      = xfs_file_fallocate,
+       .copy_file_range = xfs_file_copy_range,
+       .clone_file_range = xfs_file_clone_range,
+       .dedupe_file_range = xfs_file_dedupe_range,
 };
 
 const struct file_operations xfs_dir_file_operations = {
index 94ac06f..93d12fa 100644 (file)
@@ -43,6 +43,7 @@
 #include "xfs_log.h"
 #include "xfs_filestream.h"
 #include "xfs_rmap.h"
+#include "xfs_ag_resv.h"
 
 /*
  * File system operations
@@ -108,7 +109,9 @@ xfs_fs_geometry(
                        (xfs_sb_version_hassparseinodes(&mp->m_sb) ?
                                XFS_FSOP_GEOM_FLAGS_SPINODES : 0) |
                        (xfs_sb_version_hasrmapbt(&mp->m_sb) ?
-                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0);
+                               XFS_FSOP_GEOM_FLAGS_RMAPBT : 0) |
+                       (xfs_sb_version_hasreflink(&mp->m_sb) ?
+                               XFS_FSOP_GEOM_FLAGS_REFLINK : 0);
                geo->logsectsize = xfs_sb_version_hassector(&mp->m_sb) ?
                                mp->m_sb.sb_logsectsize : BBSIZE;
                geo->rtsectsize = mp->m_sb.sb_blocksize;
@@ -259,6 +262,12 @@ xfs_growfs_data_private(
                agf->agf_longest = cpu_to_be32(tmpsize);
                if (xfs_sb_version_hascrc(&mp->m_sb))
                        uuid_copy(&agf->agf_uuid, &mp->m_sb.sb_meta_uuid);
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       agf->agf_refcount_root = cpu_to_be32(
+                                       xfs_refc_block(mp));
+                       agf->agf_refcount_level = cpu_to_be32(1);
+                       agf->agf_refcount_blocks = cpu_to_be32(1);
+               }
 
                error = xfs_bwrite(bp);
                xfs_buf_relse(bp);
@@ -450,6 +459,17 @@ xfs_growfs_data_private(
                        rrec->rm_offset = 0;
                        be16_add_cpu(&block->bb_numrecs, 1);
 
+                       /* account for refc btree root */
+                       if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                               rrec = XFS_RMAP_REC_ADDR(block, 5);
+                               rrec->rm_startblock = cpu_to_be32(
+                                               xfs_refc_block(mp));
+                               rrec->rm_blockcount = cpu_to_be32(1);
+                               rrec->rm_owner = cpu_to_be64(XFS_RMAP_OWN_REFC);
+                               rrec->rm_offset = 0;
+                               be16_add_cpu(&block->bb_numrecs, 1);
+                       }
+
                        error = xfs_bwrite(bp);
                        xfs_buf_relse(bp);
                        if (error)
@@ -507,6 +527,28 @@ xfs_growfs_data_private(
                                goto error0;
                }
 
+               /*
+                * refcount btree root block
+                */
+               if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+                       bp = xfs_growfs_get_hdr_buf(mp,
+                               XFS_AGB_TO_DADDR(mp, agno, xfs_refc_block(mp)),
+                               BTOBB(mp->m_sb.sb_blocksize), 0,
+                               &xfs_refcountbt_buf_ops);
+                       if (!bp) {
+                               error = -ENOMEM;
+                               goto error0;
+                       }
+
+                       xfs_btree_init_block(mp, bp, XFS_REFC_CRC_MAGIC,
+                                            0, 0, agno,
+                                            XFS_BTREE_CRC_BLOCKS);
+
+                       error = xfs_bwrite(bp);
+                       xfs_buf_relse(bp);
+                       if (error)
+                               goto error0;
+               }
        }
        xfs_trans_agblocks_delta(tp, nfree);
        /*
@@ -589,6 +631,11 @@ xfs_growfs_data_private(
        xfs_set_low_space_thresholds(mp);
        mp->m_alloc_set_aside = xfs_alloc_set_aside(mp);
 
+       /* Reserve AG metadata blocks. */
+       error = xfs_fs_reserve_ag_blocks(mp);
+       if (error && error != -ENOSPC)
+               goto out;
+
        /* update secondary superblocks. */
        for (agno = 1; agno < nagcount; agno++) {
                error = 0;
@@ -639,6 +686,8 @@ xfs_growfs_data_private(
                        continue;
                }
        }
+
+ out:
        return saved_error ? saved_error : error;
 
  error0:
@@ -948,3 +997,59 @@ xfs_do_force_shutdown(
        "Please umount the filesystem and rectify the problem(s)");
        }
 }
+
+/*
+ * Reserve free space for per-AG metadata.
+ */
+int
+xfs_fs_reserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_init(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error && error != -ENOSPC) {
+               xfs_warn(mp,
+       "Error %d reserving per-AG metadata reserve pool.", error);
+               xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+       }
+
+       return error;
+}
+
+/*
+ * Free space reserved for per-AG metadata.
+ */
+int
+xfs_fs_unreserve_ag_blocks(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       struct xfs_perag        *pag;
+       int                     error = 0;
+       int                     err2;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               pag = xfs_perag_get(mp, agno);
+               err2 = xfs_ag_resv_free(pag);
+               xfs_perag_put(pag);
+               if (err2 && !error)
+                       error = err2;
+       }
+
+       if (error)
+               xfs_warn(mp,
+       "Error %d freeing per-AG metadata reserve pool.", error);
+
+       return error;
+}
index f32713f..f349158 100644 (file)
@@ -26,4 +26,7 @@ extern int xfs_reserve_blocks(xfs_mount_t *mp, __uint64_t *inval,
                                xfs_fsop_resblks_t *outval);
 extern int xfs_fs_goingdown(xfs_mount_t *mp, __uint32_t inflags);
 
+extern int xfs_fs_reserve_ag_blocks(struct xfs_mount *mp);
+extern int xfs_fs_unreserve_ag_blocks(struct xfs_mount *mp);
+
 #endif /* __XFS_FSOPS_H__ */
index 4d41b24..687a4b0 100644 (file)
@@ -21,8 +21,8 @@
 /*
  * Tunable XFS parameters.  xfs_params is required even when CONFIG_SYSCTL=n,
  * other XFS code uses these values.  Times are measured in centisecs (i.e.
- * 100ths of a second) with the exception of eofb_timer, which is measured in
- * seconds.
+ * 100ths of a second) with the exception of eofb_timer and cowb_timer, which
+ * are measured in seconds.
  */
 xfs_param_t xfs_params = {
                          /*    MIN             DFLT            MAX     */
@@ -42,6 +42,7 @@ xfs_param_t xfs_params = {
        .inherit_nodfrg = {     0,              1,              1       },
        .fstrm_timer    = {     1,              30*100,         3600*100},
        .eofb_timer     = {     1,              300,            3600*24},
+       .cowb_timer     = {     1,              1800,           3600*24},
 };
 
 struct xfs_globals xfs_globals = {
index 65b2e3f..14796b7 100644 (file)
@@ -33,6 +33,7 @@
 #include "xfs_bmap_util.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_reflink.h"
 
 #include <linux/kthread.h>
 #include <linux/freezer.h>
@@ -76,6 +77,9 @@ xfs_inode_alloc(
        ip->i_mount = mp;
        memset(&ip->i_imap, 0, sizeof(struct xfs_imap));
        ip->i_afp = NULL;
+       ip->i_cowfp = NULL;
+       ip->i_cnextents = 0;
+       ip->i_cformat = XFS_DINODE_FMT_EXTENTS;
        memset(&ip->i_df, 0, sizeof(xfs_ifork_t));
        ip->i_flags = 0;
        ip->i_delayed_blks = 0;
@@ -101,6 +105,8 @@ xfs_inode_free_callback(
 
        if (ip->i_afp)
                xfs_idestroy_fork(ip, XFS_ATTR_FORK);
+       if (ip->i_cowfp)
+               xfs_idestroy_fork(ip, XFS_COW_FORK);
 
        if (ip->i_itemp) {
                ASSERT(!(ip->i_itemp->ili_item.li_flags & XFS_LI_IN_AIL));
@@ -787,6 +793,33 @@ xfs_eofblocks_worker(
        xfs_queue_eofblocks(mp);
 }
 
+/*
+ * Background scanning to trim preallocated CoW space. This is queued
+ * based on the 'speculative_cow_prealloc_lifetime' tunable (5m by default).
+ * (We'll just piggyback on the post-EOF prealloc space workqueue.)
+ */
+STATIC void
+xfs_queue_cowblocks(
+       struct xfs_mount *mp)
+{
+       rcu_read_lock();
+       if (radix_tree_tagged(&mp->m_perag_tree, XFS_ICI_COWBLOCKS_TAG))
+               queue_delayed_work(mp->m_eofblocks_workqueue,
+                                  &mp->m_cowblocks_work,
+                                  msecs_to_jiffies(xfs_cowb_secs * 1000));
+       rcu_read_unlock();
+}
+
+void
+xfs_cowblocks_worker(
+       struct work_struct *work)
+{
+       struct xfs_mount *mp = container_of(to_delayed_work(work),
+                               struct xfs_mount, m_cowblocks_work);
+       xfs_icache_free_cowblocks(mp, NULL);
+       xfs_queue_cowblocks(mp);
+}
+
 int
 xfs_inode_ag_iterator(
        struct xfs_mount        *mp,
@@ -1343,18 +1376,30 @@ xfs_inode_free_eofblocks(
        return ret;
 }
 
-int
-xfs_icache_free_eofblocks(
+static int
+__xfs_icache_free_eofblocks(
        struct xfs_mount        *mp,
-       struct xfs_eofblocks    *eofb)
+       struct xfs_eofblocks    *eofb,
+       int                     (*execute)(struct xfs_inode *ip, int flags,
+                                          void *args),
+       int                     tag)
 {
        int flags = SYNC_TRYLOCK;
 
        if (eofb && (eofb->eof_flags & XFS_EOF_FLAGS_SYNC))
                flags = SYNC_WAIT;
 
-       return xfs_inode_ag_iterator_tag(mp, xfs_inode_free_eofblocks, flags,
-                                        eofb, XFS_ICI_EOFBLOCKS_TAG);
+       return xfs_inode_ag_iterator_tag(mp, execute, flags,
+                                        eofb, tag);
+}
+
+int
+xfs_icache_free_eofblocks(
+       struct xfs_mount        *mp,
+       struct xfs_eofblocks    *eofb)
+{
+       return __xfs_icache_free_eofblocks(mp, eofb, xfs_inode_free_eofblocks,
+                       XFS_ICI_EOFBLOCKS_TAG);
 }
 
 /*
@@ -1363,9 +1408,11 @@ xfs_icache_free_eofblocks(
  * failure. We make a best effort by including each quota under low free space
  * conditions (less than 1% free space) in the scan.
  */
-int
-xfs_inode_free_quota_eofblocks(
-       struct xfs_inode *ip)
+static int
+__xfs_inode_free_quota_eofblocks(
+       struct xfs_inode        *ip,
+       int                     (*execute)(struct xfs_mount *mp,
+                                          struct xfs_eofblocks *eofb))
 {
        int scan = 0;
        struct xfs_eofblocks eofb = {0};
@@ -1401,14 +1448,25 @@ xfs_inode_free_quota_eofblocks(
        }
 
        if (scan)
-               xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+               execute(ip->i_mount, &eofb);
 
        return scan;
 }
 
-void
-xfs_inode_set_eofblocks_tag(
-       xfs_inode_t     *ip)
+int
+xfs_inode_free_quota_eofblocks(
+       struct xfs_inode *ip)
+{
+       return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_eofblocks);
+}
+
+static void
+__xfs_inode_set_eofblocks_tag(
+       xfs_inode_t     *ip,
+       void            (*execute)(struct xfs_mount *mp),
+       void            (*set_tp)(struct xfs_mount *mp, xfs_agnumber_t agno,
+                                 int error, unsigned long caller_ip),
+       int             tag)
 {
        struct xfs_mount *mp = ip->i_mount;
        struct xfs_perag *pag;
@@ -1426,26 +1484,22 @@ xfs_inode_set_eofblocks_tag(
 
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
        spin_lock(&pag->pag_ici_lock);
-       trace_xfs_inode_set_eofblocks_tag(ip);
 
-       tagged = radix_tree_tagged(&pag->pag_ici_root,
-                                  XFS_ICI_EOFBLOCKS_TAG);
+       tagged = radix_tree_tagged(&pag->pag_ici_root, tag);
        radix_tree_tag_set(&pag->pag_ici_root,
-                          XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
-                          XFS_ICI_EOFBLOCKS_TAG);
+                          XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), tag);
        if (!tagged) {
                /* propagate the eofblocks tag up into the perag radix tree */
                spin_lock(&ip->i_mount->m_perag_lock);
                radix_tree_tag_set(&ip->i_mount->m_perag_tree,
                                   XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
-                                  XFS_ICI_EOFBLOCKS_TAG);
+                                  tag);
                spin_unlock(&ip->i_mount->m_perag_lock);
 
                /* kick off background trimming */
-               xfs_queue_eofblocks(ip->i_mount);
+               execute(ip->i_mount);
 
-               trace_xfs_perag_set_eofblocks(ip->i_mount, pag->pag_agno,
-                                             -1, _RET_IP_);
+               set_tp(ip->i_mount, pag->pag_agno, -1, _RET_IP_);
        }
 
        spin_unlock(&pag->pag_ici_lock);
@@ -1453,8 +1507,21 @@ xfs_inode_set_eofblocks_tag(
 }
 
 void
-xfs_inode_clear_eofblocks_tag(
+xfs_inode_set_eofblocks_tag(
        xfs_inode_t     *ip)
+{
+       trace_xfs_inode_set_eofblocks_tag(ip);
+       return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_eofblocks,
+                       trace_xfs_perag_set_eofblocks,
+                       XFS_ICI_EOFBLOCKS_TAG);
+}
+
+static void
+__xfs_inode_clear_eofblocks_tag(
+       xfs_inode_t     *ip,
+       void            (*clear_tp)(struct xfs_mount *mp, xfs_agnumber_t agno,
+                                   int error, unsigned long caller_ip),
+       int             tag)
 {
        struct xfs_mount *mp = ip->i_mount;
        struct xfs_perag *pag;
@@ -1465,23 +1532,141 @@ xfs_inode_clear_eofblocks_tag(
 
        pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino));
        spin_lock(&pag->pag_ici_lock);
-       trace_xfs_inode_clear_eofblocks_tag(ip);
 
        radix_tree_tag_clear(&pag->pag_ici_root,
-                            XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino),
-                            XFS_ICI_EOFBLOCKS_TAG);
-       if (!radix_tree_tagged(&pag->pag_ici_root, XFS_ICI_EOFBLOCKS_TAG)) {
+                            XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), tag);
+       if (!radix_tree_tagged(&pag->pag_ici_root, tag)) {
                /* clear the eofblocks tag from the perag radix tree */
                spin_lock(&ip->i_mount->m_perag_lock);
                radix_tree_tag_clear(&ip->i_mount->m_perag_tree,
                                     XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino),
-                                    XFS_ICI_EOFBLOCKS_TAG);
+                                    tag);
                spin_unlock(&ip->i_mount->m_perag_lock);
-               trace_xfs_perag_clear_eofblocks(ip->i_mount, pag->pag_agno,
-                                              -1, _RET_IP_);
+               clear_tp(ip->i_mount, pag->pag_agno, -1, _RET_IP_);
        }
 
        spin_unlock(&pag->pag_ici_lock);
        xfs_perag_put(pag);
 }
 
+void
+xfs_inode_clear_eofblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_clear_eofblocks_tag(ip);
+       return __xfs_inode_clear_eofblocks_tag(ip,
+                       trace_xfs_perag_clear_eofblocks, XFS_ICI_EOFBLOCKS_TAG);
+}
+
+/*
+ * Automatic CoW Reservation Freeing
+ *
+ * These functions automatically garbage collect leftover CoW reservations
+ * that were made on behalf of a cowextsize hint when we start to run out
+ * of quota or when the reservations sit around for too long.  If the file
+ * has dirty pages or is undergoing writeback, its CoW reservations will
+ * be retained.
+ *
+ * The actual garbage collection piggybacks off the same code that runs
+ * the speculative EOF preallocation garbage collector.
+ */
+STATIC int
+xfs_inode_free_cowblocks(
+       struct xfs_inode        *ip,
+       int                     flags,
+       void                    *args)
+{
+       int ret;
+       struct xfs_eofblocks *eofb = args;
+       bool need_iolock = true;
+       int match;
+
+       ASSERT(!eofb || (eofb && eofb->eof_scan_owner != 0));
+
+       if (!xfs_reflink_has_real_cow_blocks(ip)) {
+               trace_xfs_inode_free_cowblocks_invalid(ip);
+               xfs_inode_clear_cowblocks_tag(ip);
+               return 0;
+       }
+
+       /*
+        * If the mapping is dirty or under writeback we cannot touch the
+        * CoW fork.  Leave it alone if we're in the midst of a directio.
+        */
+       if (mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_DIRTY) ||
+           mapping_tagged(VFS_I(ip)->i_mapping, PAGECACHE_TAG_WRITEBACK) ||
+           atomic_read(&VFS_I(ip)->i_dio_count))
+               return 0;
+
+       if (eofb) {
+               if (eofb->eof_flags & XFS_EOF_FLAGS_UNION)
+                       match = xfs_inode_match_id_union(ip, eofb);
+               else
+                       match = xfs_inode_match_id(ip, eofb);
+               if (!match)
+                       return 0;
+
+               /* skip the inode if the file size is too small */
+               if (eofb->eof_flags & XFS_EOF_FLAGS_MINFILESIZE &&
+                   XFS_ISIZE(ip) < eofb->eof_min_file_size)
+                       return 0;
+
+               /*
+                * A scan owner implies we already hold the iolock. Skip it in
+                * xfs_free_eofblocks() to avoid deadlock. This also eliminates
+                * the possibility of EAGAIN being returned.
+                */
+               if (eofb->eof_scan_owner == ip->i_ino)
+                       need_iolock = false;
+       }
+
+       /* Free the CoW blocks */
+       if (need_iolock) {
+               xfs_ilock(ip, XFS_IOLOCK_EXCL);
+               xfs_ilock(ip, XFS_MMAPLOCK_EXCL);
+       }
+
+       ret = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF);
+
+       if (need_iolock) {
+               xfs_iunlock(ip, XFS_MMAPLOCK_EXCL);
+               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
+       }
+
+       return ret;
+}
+
+int
+xfs_icache_free_cowblocks(
+       struct xfs_mount        *mp,
+       struct xfs_eofblocks    *eofb)
+{
+       return __xfs_icache_free_eofblocks(mp, eofb, xfs_inode_free_cowblocks,
+                       XFS_ICI_COWBLOCKS_TAG);
+}
+
+int
+xfs_inode_free_quota_cowblocks(
+       struct xfs_inode *ip)
+{
+       return __xfs_inode_free_quota_eofblocks(ip, xfs_icache_free_cowblocks);
+}
+
+void
+xfs_inode_set_cowblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_set_eofblocks_tag(ip);
+       return __xfs_inode_set_eofblocks_tag(ip, xfs_queue_cowblocks,
+                       trace_xfs_perag_set_eofblocks,
+                       XFS_ICI_COWBLOCKS_TAG);
+}
+
+void
+xfs_inode_clear_cowblocks_tag(
+       xfs_inode_t     *ip)
+{
+       trace_xfs_inode_clear_eofblocks_tag(ip);
+       return __xfs_inode_clear_eofblocks_tag(ip,
+                       trace_xfs_perag_clear_eofblocks, XFS_ICI_COWBLOCKS_TAG);
+}
index 05bac99..a1e02f4 100644 (file)
@@ -40,6 +40,7 @@ struct xfs_eofblocks {
                                           in xfs_inode_ag_iterator */
 #define XFS_ICI_RECLAIM_TAG    0       /* inode is to be reclaimed */
 #define XFS_ICI_EOFBLOCKS_TAG  1       /* inode has blocks beyond EOF */
+#define XFS_ICI_COWBLOCKS_TAG  2       /* inode can have cow blocks to gc */
 
 /*
  * Flags for xfs_iget()
@@ -70,6 +71,12 @@ int xfs_inode_free_quota_eofblocks(struct xfs_inode *ip);
 void xfs_eofblocks_worker(struct work_struct *);
 void xfs_queue_eofblocks(struct xfs_mount *);
 
+void xfs_inode_set_cowblocks_tag(struct xfs_inode *ip);
+void xfs_inode_clear_cowblocks_tag(struct xfs_inode *ip);
+int xfs_icache_free_cowblocks(struct xfs_mount *, struct xfs_eofblocks *);
+int xfs_inode_free_quota_cowblocks(struct xfs_inode *ip);
+void xfs_cowblocks_worker(struct work_struct *);
+
 int xfs_inode_ag_iterator(struct xfs_mount *mp,
        int (*execute)(struct xfs_inode *ip, int flags, void *args),
        int flags, void *args);
index 624e1df..4e560e6 100644 (file)
@@ -49,6 +49,7 @@
 #include "xfs_trans_priv.h"
 #include "xfs_log.h"
 #include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
 
 kmem_zone_t *xfs_inode_zone;
 
@@ -76,6 +77,29 @@ xfs_get_extsz_hint(
        return 0;
 }
 
+/*
+ * Helper function to extract CoW extent size hint from inode.
+ * Between the extent size hint and the CoW extent size hint, we
+ * return the greater of the two.  If the value is zero (automatic),
+ * use the default size.
+ */
+xfs_extlen_t
+xfs_get_cowextsz_hint(
+       struct xfs_inode        *ip)
+{
+       xfs_extlen_t            a, b;
+
+       a = 0;
+       if (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+               a = ip->i_d.di_cowextsize;
+       b = xfs_get_extsz_hint(ip);
+
+       a = max(a, b);
+       if (a == 0)
+               return XFS_DEFAULT_COWEXTSZ_HINT;
+       return a;
+}
+
 /*
  * These two are wrapper routines around the xfs_ilock() routine used to
  * centralize some grungy code.  They are used in places that wish to lock the
@@ -651,6 +675,8 @@ _xfs_dic2xflags(
        if (di_flags2 & XFS_DIFLAG2_ANY) {
                if (di_flags2 & XFS_DIFLAG2_DAX)
                        flags |= FS_XFLAG_DAX;
+               if (di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+                       flags |= FS_XFLAG_COWEXTSIZE;
        }
 
        if (has_attr)
@@ -834,6 +860,7 @@ xfs_ialloc(
        if (ip->i_d.di_version == 3) {
                inode->i_version = 1;
                ip->i_d.di_flags2 = 0;
+               ip->i_d.di_cowextsize = 0;
                ip->i_d.di_crtime.t_sec = (__int32_t)tv.tv_sec;
                ip->i_d.di_crtime.t_nsec = (__int32_t)tv.tv_nsec;
        }
@@ -896,6 +923,15 @@ xfs_ialloc(
                        ip->i_d.di_flags |= di_flags;
                        ip->i_d.di_flags2 |= di_flags2;
                }
+               if (pip &&
+                   (pip->i_d.di_flags2 & XFS_DIFLAG2_ANY) &&
+                   pip->i_d.di_version == 3 &&
+                   ip->i_d.di_version == 3) {
+                       if (pip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
+                               ip->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+                               ip->i_d.di_cowextsize = pip->i_d.di_cowextsize;
+                       }
+               }
                /* FALLTHROUGH */
        case S_IFLNK:
                ip->i_d.di_format = XFS_DINODE_FMT_EXTENTS;
@@ -1586,6 +1622,20 @@ xfs_itruncate_extents(
                        goto out;
        }
 
+       /* Remove all pending CoW reservations. */
+       error = xfs_reflink_cancel_cow_blocks(ip, &tp, first_unmap_block,
+                       last_block);
+       if (error)
+               goto out;
+
+       /*
+        * Clear the reflink flag if we truncated everything.
+        */
+       if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) {
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+               xfs_inode_clear_cowblocks_tag(ip);
+       }
+
        /*
         * Always re-log the inode so that our permanent transaction can keep
         * on rolling it forward in the log.
@@ -1850,6 +1900,7 @@ xfs_inactive(
        }
 
        mp = ip->i_mount;
+       ASSERT(!xfs_iflags_test(ip, XFS_IRECOVERY));
 
        /* If this is a read-only mount, don't do this (would generate I/O) */
        if (mp->m_flags & XFS_MOUNT_RDONLY)
index 8f30d25..f14c1de 100644 (file)
@@ -47,6 +47,7 @@ typedef struct xfs_inode {
 
        /* Extent information. */
        xfs_ifork_t             *i_afp;         /* attribute fork pointer */
+       xfs_ifork_t             *i_cowfp;       /* copy on write extents */
        xfs_ifork_t             i_df;           /* data fork */
 
        /* operations vectors */
@@ -65,6 +66,9 @@ typedef struct xfs_inode {
 
        struct xfs_icdinode     i_d;            /* most of ondisk inode */
 
+       xfs_extnum_t            i_cnextents;    /* # of extents in cow fork */
+       unsigned int            i_cformat;      /* format of cow fork */
+
        /* VFS inode */
        struct inode            i_vnode;        /* embedded VFS inode */
 } xfs_inode_t;
@@ -202,6 +206,11 @@ xfs_get_initial_prid(struct xfs_inode *dp)
        return XFS_PROJID_DEFAULT;
 }
 
+static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)
+{
+       return ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
+}
+
 /*
  * In-core inode flags.
  */
@@ -217,6 +226,12 @@ xfs_get_initial_prid(struct xfs_inode *dp)
 #define XFS_IPINNED            (1 << __XFS_IPINNED_BIT)
 #define XFS_IDONTCACHE         (1 << 9) /* don't cache the inode long term */
 #define XFS_IEOFBLOCKS         (1 << 10)/* has the preallocblocks tag set */
+/*
+ * If this unlinked inode is in the middle of recovery, don't let drop_inode
+ * truncate and free the inode.  This can happen if we iget the inode during
+ * log recovery to replay a bmap operation on the inode.
+ */
+#define XFS_IRECOVERY          (1 << 11)
 
 /*
  * Per-lifetime flags need to be reset when re-using a reclaimable inode during
@@ -411,6 +426,7 @@ int         xfs_iflush(struct xfs_inode *, struct xfs_buf **);
 void           xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint);
 
 xfs_extlen_t   xfs_get_extsz_hint(struct xfs_inode *ip);
+xfs_extlen_t   xfs_get_cowextsz_hint(struct xfs_inode *ip);
 
 int            xfs_dir_ialloc(struct xfs_trans **, struct xfs_inode *, umode_t,
                               xfs_nlink_t, xfs_dev_t, prid_t, int,
@@ -474,4 +490,7 @@ do { \
 
 extern struct kmem_zone        *xfs_inode_zone;
 
+/* The default CoW extent size hint. */
+#define XFS_DEFAULT_COWEXTSZ_HINT 32
+
 #endif /* __XFS_INODE_H__ */
index 892c2ac..9610e9c 100644 (file)
@@ -368,7 +368,7 @@ xfs_inode_to_log_dinode(
                to->di_crtime.t_sec = from->di_crtime.t_sec;
                to->di_crtime.t_nsec = from->di_crtime.t_nsec;
                to->di_flags2 = from->di_flags2;
-
+               to->di_cowextsize = from->di_cowextsize;
                to->di_ino = ip->i_ino;
                to->di_lsn = lsn;
                memset(to->di_pad2, 0, sizeof(to->di_pad2));
index 0d9021f..c245bed 100644 (file)
@@ -903,6 +903,8 @@ xfs_ioc_fsgetxattr(
        xfs_ilock(ip, XFS_ILOCK_SHARED);
        fa.fsx_xflags = xfs_ip2xflags(ip);
        fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
+       fa.fsx_cowextsize = ip->i_d.di_cowextsize <<
+                       ip->i_mount->m_sb.sb_blocklog;
        fa.fsx_projid = xfs_get_projid(ip);
 
        if (attr) {
@@ -973,12 +975,13 @@ xfs_set_diflags(
        if (ip->i_d.di_version < 3)
                return;
 
-       di_flags2 = 0;
+       di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
        if (xflags & FS_XFLAG_DAX)
                di_flags2 |= XFS_DIFLAG2_DAX;
+       if (xflags & FS_XFLAG_COWEXTSIZE)
+               di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
 
        ip->i_d.di_flags2 = di_flags2;
-
 }
 
 STATIC void
@@ -1031,6 +1034,14 @@ xfs_ioctl_setattr_xflags(
                        return -EINVAL;
        }
 
+       /* Clear reflink if we are actually able to set the rt flag. */
+       if ((fa->fsx_xflags & FS_XFLAG_REALTIME) && xfs_is_reflink_inode(ip))
+               ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+
+       /* Don't allow us to set DAX mode for a reflinked file for now. */
+       if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip))
+               return -EINVAL;
+
        /*
         * Can't modify an immutable/append-only file unless
         * we have appropriate permission.
@@ -1219,6 +1230,56 @@ xfs_ioctl_setattr_check_extsize(
        return 0;
 }
 
+/*
+ * CoW extent size hint validation rules are:
+ *
+ * 1. CoW extent size hint can only be set if reflink is enabled on the fs.
+ *    The inode does not have to have any shared blocks, but it must be a v3.
+ * 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
+ *    for a directory, the hint is propagated to new files.
+ * 3. Can be changed on files & directories at any time.
+ * 4. CoW extsize hint of 0 turns off hints, clears inode flags.
+ * 5. Extent size must be a multiple of the appropriate block size.
+ * 6. The extent size hint must be limited to half the AG size to avoid
+ *    alignment extending the extent beyond the limits of the AG.
+ */
+static int
+xfs_ioctl_setattr_check_cowextsize(
+       struct xfs_inode        *ip,
+       struct fsxattr          *fa)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+
+       if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
+               return 0;
+
+       if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb) ||
+           ip->i_d.di_version != 3)
+               return -EINVAL;
+
+       if (!S_ISREG(VFS_I(ip)->i_mode) && !S_ISDIR(VFS_I(ip)->i_mode))
+               return -EINVAL;
+
+       if (fa->fsx_cowextsize != 0) {
+               xfs_extlen_t    size;
+               xfs_fsblock_t   cowextsize_fsb;
+
+               cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
+               if (cowextsize_fsb > MAXEXTLEN)
+                       return -EINVAL;
+
+               size = mp->m_sb.sb_blocksize;
+               if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
+                       return -EINVAL;
+
+               if (fa->fsx_cowextsize % size)
+                       return -EINVAL;
+       } else
+               fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE;
+
+       return 0;
+}
+
 static int
 xfs_ioctl_setattr_check_projid(
        struct xfs_inode        *ip,
@@ -1311,6 +1372,10 @@ xfs_ioctl_setattr(
        if (code)
                goto error_trans_cancel;
 
+       code = xfs_ioctl_setattr_check_cowextsize(ip, fa);
+       if (code)
+               goto error_trans_cancel;
+
        code = xfs_ioctl_setattr_xflags(tp, ip, fa);
        if (code)
                goto error_trans_cancel;
@@ -1346,6 +1411,12 @@ xfs_ioctl_setattr(
                ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog;
        else
                ip->i_d.di_extsize = 0;
+       if (ip->i_d.di_version == 3 &&
+           (ip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+               ip->i_d.di_cowextsize = fa->fsx_cowextsize >>
+                               mp->m_sb.sb_blocklog;
+       else
+               ip->i_d.di_cowextsize = 0;
 
        code = xfs_trans_commit(tp);
 
index c08253e..d907eb9 100644 (file)
@@ -39,6 +39,7 @@
 #include "xfs_quota.h"
 #include "xfs_dquot_item.h"
 #include "xfs_dquot.h"
+#include "xfs_reflink.h"
 
 
 #define XFS_WRITEIO_ALIGN(mp,off)      (((off) >> mp->m_writeio_log) \
@@ -70,7 +71,7 @@ xfs_bmbt_to_iomap(
        iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
 }
 
-static xfs_extlen_t
+xfs_extlen_t
 xfs_eof_alignment(
        struct xfs_inode        *ip,
        xfs_extlen_t            extsize)
@@ -609,7 +610,7 @@ xfs_file_iomap_begin_delay(
        }
 
 retry:
-       error = xfs_bmapi_reserve_delalloc(ip, offset_fsb,
+       error = xfs_bmapi_reserve_delalloc(ip, XFS_DATA_FORK, offset_fsb,
                        end_fsb - offset_fsb, &got,
                        &prev, &idx, eof);
        switch (error) {
@@ -666,6 +667,7 @@ out_unlock:
 int
 xfs_iomap_write_allocate(
        xfs_inode_t     *ip,
+       int             whichfork,
        xfs_off_t       offset,
        xfs_bmbt_irec_t *imap)
 {
@@ -678,8 +680,12 @@ xfs_iomap_write_allocate(
        xfs_trans_t     *tp;
        int             nimaps;
        int             error = 0;
+       int             flags = 0;
        int             nres;
 
+       if (whichfork == XFS_COW_FORK)
+               flags |= XFS_BMAPI_COWFORK;
+
        /*
         * Make sure that the dquots are there.
         */
@@ -773,7 +779,7 @@ xfs_iomap_write_allocate(
                         * pointer that the caller gave to us.
                         */
                        error = xfs_bmapi_write(tp, ip, map_start_fsb,
-                                               count_fsb, 0, &first_block,
+                                               count_fsb, flags, &first_block,
                                                nres, imap, &nimaps,
                                                &dfops);
                        if (error)
@@ -955,14 +961,22 @@ xfs_file_iomap_begin(
        struct xfs_mount        *mp = ip->i_mount;
        struct xfs_bmbt_irec    imap;
        xfs_fileoff_t           offset_fsb, end_fsb;
+       bool                    shared, trimmed;
        int                     nimaps = 1, error = 0;
        unsigned                lockmode;
 
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
 
-       if ((flags & IOMAP_WRITE) &&
-           !IS_DAX(inode) && !xfs_get_extsz_hint(ip)) {
+       if ((flags & (IOMAP_WRITE | IOMAP_ZERO)) && xfs_is_reflink_inode(ip)) {
+               error = xfs_reflink_reserve_cow_range(ip, offset, length);
+               if (error < 0)
+                       return error;
+       }
+
+       if ((flags & IOMAP_WRITE) && !IS_DAX(inode) &&
+                  !xfs_get_extsz_hint(ip)) {
+               /* Reserve delalloc blocks for regular writeback. */
                return xfs_file_iomap_begin_delay(inode, offset, length, flags,
                                iomap);
        }
@@ -976,7 +990,14 @@ xfs_file_iomap_begin(
        end_fsb = XFS_B_TO_FSB(mp, offset + length);
 
        error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap,
-                              &nimaps, XFS_BMAPI_ENTIRE);
+                              &nimaps, 0);
+       if (error) {
+               xfs_iunlock(ip, lockmode);
+               return error;
+       }
+
+       /* Trim the mapping to the nearest shared extent boundary. */
+       error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed);
        if (error) {
                xfs_iunlock(ip, lockmode);
                return error;
@@ -1015,6 +1036,8 @@ xfs_file_iomap_begin(
        }
 
        xfs_bmbt_to_iomap(ip, iomap, &imap);
+       if (shared)
+               iomap->flags |= IOMAP_F_SHARED;
        return 0;
 }
 
index 6498be4..6d45cf0 100644 (file)
@@ -25,12 +25,13 @@ struct xfs_bmbt_irec;
 
 int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
                        struct xfs_bmbt_irec *, int);
-int xfs_iomap_write_allocate(struct xfs_inode *, xfs_off_t,
+int xfs_iomap_write_allocate(struct xfs_inode *, int, xfs_off_t,
                        struct xfs_bmbt_irec *);
 int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
 
 void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
                struct xfs_bmbt_irec *);
+xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
 
 extern struct iomap_ops xfs_iomap_ops;
 extern struct iomap_ops xfs_xattr_iomap_ops;
index c5da95e..405a65c 100644 (file)
@@ -1159,6 +1159,7 @@ xfs_diflags_to_iflags(
                inode->i_flags |= S_NOATIME;
        if (S_ISREG(inode->i_mode) &&
            ip->i_mount->m_sb.sb_blocksize == PAGE_SIZE &&
+           !xfs_is_reflink_inode(ip) &&
            (ip->i_mount->m_flags & XFS_MOUNT_DAX ||
             ip->i_d.di_flags2 & XFS_DIFLAG2_DAX))
                inode->i_flags |= S_DAX;
index ce73eb3..66e8817 100644 (file)
@@ -66,7 +66,7 @@ xfs_bulkstat_one_int(
        if (!buffer || xfs_internal_inum(mp, ino))
                return -EINVAL;
 
-       buf = kmem_alloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
+       buf = kmem_zalloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL);
        if (!buf)
                return -ENOMEM;
 
@@ -111,6 +111,12 @@ xfs_bulkstat_one_int(
        buf->bs_aextents = dic->di_anextents;
        buf->bs_forkoff = XFS_IFORK_BOFF(ip);
 
+       if (dic->di_version == 3) {
+               if (dic->di_flags2 & XFS_DIFLAG2_COWEXTSIZE)
+                       buf->bs_cowextsize = dic->di_cowextsize <<
+                                       mp->m_sb.sb_blocklog;
+       }
+
        switch (dic->di_format) {
        case XFS_DINODE_FMT_DEV:
                buf->bs_rdev = ip->i_df.if_u2.if_rdev;
index b8d64d5..68640fb 100644 (file)
@@ -116,6 +116,7 @@ typedef __u32                       xfs_nlink_t;
 #define xfs_inherit_nodefrag   xfs_params.inherit_nodfrg.val
 #define xfs_fstrm_centisecs    xfs_params.fstrm_timer.val
 #define xfs_eofb_secs          xfs_params.eofb_timer.val
+#define xfs_cowb_secs          xfs_params.cowb_timer.val
 
 #define current_cpu()          (raw_smp_processor_id())
 #define current_pid()          (current->pid)
index 846483d..9b3d7c7 100644 (file)
@@ -45,6 +45,8 @@
 #include "xfs_dir2.h"
 #include "xfs_rmap_item.h"
 #include "xfs_buf_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_bmap_item.h"
 
 #define BLK_AVG(blk1, blk2)    ((blk1+blk2) >> 1)
 
@@ -1924,6 +1926,10 @@ xlog_recover_reorder_trans(
                case XFS_LI_EFI:
                case XFS_LI_RUI:
                case XFS_LI_RUD:
+               case XFS_LI_CUI:
+               case XFS_LI_CUD:
+               case XFS_LI_BUI:
+               case XFS_LI_BUD:
                        trace_xfs_log_recover_item_reorder_tail(log,
                                                        trans, item, pass);
                        list_move_tail(&item->ri_list, &inode_list);
@@ -2242,6 +2248,7 @@ xlog_recover_get_buf_lsn(
        case XFS_ABTB_MAGIC:
        case XFS_ABTC_MAGIC:
        case XFS_RMAP_CRC_MAGIC:
+       case XFS_REFC_CRC_MAGIC:
        case XFS_IBT_CRC_MAGIC:
        case XFS_IBT_MAGIC: {
                struct xfs_btree_block *btb = blk;
@@ -2415,6 +2422,9 @@ xlog_recover_validate_buf_type(
                case XFS_RMAP_CRC_MAGIC:
                        bp->b_ops = &xfs_rmapbt_buf_ops;
                        break;
+               case XFS_REFC_CRC_MAGIC:
+                       bp->b_ops = &xfs_refcountbt_buf_ops;
+                       break;
                default:
                        warnmsg = "Bad btree block magic!";
                        break;
@@ -3546,6 +3556,242 @@ xlog_recover_rud_pass2(
        return 0;
 }
 
+/*
+ * Copy an CUI format buffer from the given buf, and into the destination
+ * CUI format structure.  The CUI/CUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_cui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_cui_log_format       *dst_cui_fmt)
+{
+       struct xfs_cui_log_format       *src_cui_fmt;
+       uint                            len;
+
+       src_cui_fmt = buf->i_addr;
+       len = xfs_cui_log_format_sizeof(src_cui_fmt->cui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_cui_fmt, src_cui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent refcount update
+ * item from the cui format structure which was logged on disk.
+ * It allocates an in-core cui, copies the extents from the format
+ * structure into it, and adds the cui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_cui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_cui_log_item         *cuip;
+       struct xfs_cui_log_format       *cui_formatp;
+
+       cui_formatp = item->ri_buf[0].i_addr;
+
+       cuip = xfs_cui_init(mp, cui_formatp->cui_nextents);
+       error = xfs_cui_copy_format(&item->ri_buf[0], &cuip->cui_format);
+       if (error) {
+               xfs_cui_item_free(cuip);
+               return error;
+       }
+       atomic_set(&cuip->cui_next_extent, cui_formatp->cui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The CUI has two references. One for the CUD and one for CUI to ensure
+        * it makes it into the AIL. Insert the CUI into the AIL directly and
+        * drop the CUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &cuip->cui_item, lsn);
+       xfs_cui_release(cuip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an CUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding CUI if it
+ * was still in the log. To do this it searches the AIL for the CUI with an id
+ * equal to that in the CUD format structure. If we find it we drop the CUD
+ * reference, which removes the CUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_cud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_cud_log_format       *cud_formatp;
+       struct xfs_cui_log_item         *cuip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      cui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       cud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_cud_log_format))
+               return -EFSCORRUPTED;
+       cui_id = cud_formatp->cud_cui_id;
+
+       /*
+        * Search for the CUI with the id in the CUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_CUI) {
+                       cuip = (struct xfs_cui_log_item *)lip;
+                       if (cuip->cui_format.cui_id == cui_id) {
+                               /*
+                                * Drop the CUD reference to the CUI. This
+                                * removes the CUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_cui_release(cuip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
+/*
+ * Copy an BUI format buffer from the given buf, and into the destination
+ * BUI format structure.  The BUI/BUD items were designed not to need any
+ * special alignment handling.
+ */
+static int
+xfs_bui_copy_format(
+       struct xfs_log_iovec            *buf,
+       struct xfs_bui_log_format       *dst_bui_fmt)
+{
+       struct xfs_bui_log_format       *src_bui_fmt;
+       uint                            len;
+
+       src_bui_fmt = buf->i_addr;
+       len = xfs_bui_log_format_sizeof(src_bui_fmt->bui_nextents);
+
+       if (buf->i_len == len) {
+               memcpy(dst_bui_fmt, src_bui_fmt, len);
+               return 0;
+       }
+       return -EFSCORRUPTED;
+}
+
+/*
+ * This routine is called to create an in-core extent bmap update
+ * item from the bui format structure which was logged on disk.
+ * It allocates an in-core bui, copies the extents from the format
+ * structure into it, and adds the bui to the AIL with the given
+ * LSN.
+ */
+STATIC int
+xlog_recover_bui_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item,
+       xfs_lsn_t                       lsn)
+{
+       int                             error;
+       struct xfs_mount                *mp = log->l_mp;
+       struct xfs_bui_log_item         *buip;
+       struct xfs_bui_log_format       *bui_formatp;
+
+       bui_formatp = item->ri_buf[0].i_addr;
+
+       if (bui_formatp->bui_nextents != XFS_BUI_MAX_FAST_EXTENTS)
+               return -EFSCORRUPTED;
+       buip = xfs_bui_init(mp);
+       error = xfs_bui_copy_format(&item->ri_buf[0], &buip->bui_format);
+       if (error) {
+               xfs_bui_item_free(buip);
+               return error;
+       }
+       atomic_set(&buip->bui_next_extent, bui_formatp->bui_nextents);
+
+       spin_lock(&log->l_ailp->xa_lock);
+       /*
+        * The RUI has two references. One for the RUD and one for RUI to ensure
+        * it makes it into the AIL. Insert the RUI into the AIL directly and
+        * drop the RUI reference. Note that xfs_trans_ail_update() drops the
+        * AIL lock.
+        */
+       xfs_trans_ail_update(log->l_ailp, &buip->bui_item, lsn);
+       xfs_bui_release(buip);
+       return 0;
+}
+
+
+/*
+ * This routine is called when an BUD format structure is found in a committed
+ * transaction in the log. Its purpose is to cancel the corresponding BUI if it
+ * was still in the log. To do this it searches the AIL for the BUI with an id
+ * equal to that in the BUD format structure. If we find it we drop the BUD
+ * reference, which removes the BUI from the AIL and frees it.
+ */
+STATIC int
+xlog_recover_bud_pass2(
+       struct xlog                     *log,
+       struct xlog_recover_item        *item)
+{
+       struct xfs_bud_log_format       *bud_formatp;
+       struct xfs_bui_log_item         *buip = NULL;
+       struct xfs_log_item             *lip;
+       __uint64_t                      bui_id;
+       struct xfs_ail_cursor           cur;
+       struct xfs_ail                  *ailp = log->l_ailp;
+
+       bud_formatp = item->ri_buf[0].i_addr;
+       if (item->ri_buf[0].i_len != sizeof(struct xfs_bud_log_format))
+               return -EFSCORRUPTED;
+       bui_id = bud_formatp->bud_bui_id;
+
+       /*
+        * Search for the BUI with the id in the BUD format structure in the
+        * AIL.
+        */
+       spin_lock(&ailp->xa_lock);
+       lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+       while (lip != NULL) {
+               if (lip->li_type == XFS_LI_BUI) {
+                       buip = (struct xfs_bui_log_item *)lip;
+                       if (buip->bui_format.bui_id == bui_id) {
+                               /*
+                                * Drop the BUD reference to the BUI. This
+                                * removes the BUI from the AIL and frees it.
+                                */
+                               spin_unlock(&ailp->xa_lock);
+                               xfs_bui_release(buip);
+                               spin_lock(&ailp->xa_lock);
+                               break;
+                       }
+               }
+               lip = xfs_trans_ail_cursor_next(ailp, &cur);
+       }
+
+       xfs_trans_ail_cursor_done(&cur);
+       spin_unlock(&ailp->xa_lock);
+
+       return 0;
+}
+
 /*
  * This routine is called when an inode create format structure is found in a
  * committed transaction in the log.  It's purpose is to initialise the inodes
@@ -3773,6 +4019,10 @@ xlog_recover_ra_pass2(
        case XFS_LI_QUOTAOFF:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
        default:
                break;
        }
@@ -3798,6 +4048,10 @@ xlog_recover_commit_pass1(
        case XFS_LI_ICREATE:
        case XFS_LI_RUI:
        case XFS_LI_RUD:
+       case XFS_LI_CUI:
+       case XFS_LI_CUD:
+       case XFS_LI_BUI:
+       case XFS_LI_BUD:
                /* nothing to do in pass 1 */
                return 0;
        default:
@@ -3832,6 +4086,14 @@ xlog_recover_commit_pass2(
                return xlog_recover_rui_pass2(log, item, trans->r_lsn);
        case XFS_LI_RUD:
                return xlog_recover_rud_pass2(log, item);
+       case XFS_LI_CUI:
+               return xlog_recover_cui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_CUD:
+               return xlog_recover_cud_pass2(log, item);
+       case XFS_LI_BUI:
+               return xlog_recover_bui_pass2(log, item, trans->r_lsn);
+       case XFS_LI_BUD:
+               return xlog_recover_bud_pass2(log, item);
        case XFS_LI_DQUOT:
                return xlog_recover_dquot_pass2(log, buffer_list, item,
                                                trans->r_lsn);
@@ -4419,12 +4681,94 @@ xlog_recover_cancel_rui(
        spin_lock(&ailp->xa_lock);
 }
 
+/* Recover the CUI if necessary. */
+STATIC int
+xlog_recover_process_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+       int                             error;
+
+       /*
+        * Skip CUIs that we've already processed.
+        */
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+       if (test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_cui_recover(mp, cuip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the CUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_cui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_cui_log_item         *cuip;
+
+       cuip = container_of(lip, struct xfs_cui_log_item, cui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_cui_release(cuip);
+       spin_lock(&ailp->xa_lock);
+}
+
+/* Recover the BUI if necessary. */
+STATIC int
+xlog_recover_process_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+       int                             error;
+
+       /*
+        * Skip BUIs that we've already processed.
+        */
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+       if (test_bit(XFS_BUI_RECOVERED, &buip->bui_flags))
+               return 0;
+
+       spin_unlock(&ailp->xa_lock);
+       error = xfs_bui_recover(mp, buip);
+       spin_lock(&ailp->xa_lock);
+
+       return error;
+}
+
+/* Release the BUI since we're cancelling everything. */
+STATIC void
+xlog_recover_cancel_bui(
+       struct xfs_mount                *mp,
+       struct xfs_ail                  *ailp,
+       struct xfs_log_item             *lip)
+{
+       struct xfs_bui_log_item         *buip;
+
+       buip = container_of(lip, struct xfs_bui_log_item, bui_item);
+
+       spin_unlock(&ailp->xa_lock);
+       xfs_bui_release(buip);
+       spin_lock(&ailp->xa_lock);
+}
+
 /* Is this log item a deferred action intent? */
 static inline bool xlog_item_is_intent(struct xfs_log_item *lip)
 {
        switch (lip->li_type) {
        case XFS_LI_EFI:
        case XFS_LI_RUI:
+       case XFS_LI_CUI:
+       case XFS_LI_BUI:
                return true;
        default:
                return false;
@@ -4488,6 +4832,12 @@ xlog_recover_process_intents(
                case XFS_LI_RUI:
                        error = xlog_recover_process_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       error = xlog_recover_process_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       error = xlog_recover_process_bui(log->l_mp, ailp, lip);
+                       break;
                }
                if (error)
                        goto out;
@@ -4535,6 +4885,12 @@ xlog_recover_cancel_intents(
                case XFS_LI_RUI:
                        xlog_recover_cancel_rui(log->l_mp, ailp, lip);
                        break;
+               case XFS_LI_CUI:
+                       xlog_recover_cancel_cui(log->l_mp, ailp, lip);
+                       break;
+               case XFS_LI_BUI:
+                       xlog_recover_cancel_bui(log->l_mp, ailp, lip);
+                       break;
                }
 
                lip = xfs_trans_ail_cursor_next(ailp, &cur);
@@ -4613,6 +4969,7 @@ xlog_recover_process_one_iunlink(
        if (error)
                goto fail_iput;
 
+       xfs_iflags_clear(ip, XFS_IRECOVERY);
        ASSERT(VFS_I(ip)->i_nlink == 0);
        ASSERT(VFS_I(ip)->i_mode != 0);
 
index 56e85a6..fc78739 100644 (file)
@@ -43,6 +43,8 @@
 #include "xfs_icache.h"
 #include "xfs_sysfs.h"
 #include "xfs_rmap_btree.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_reflink.h"
 
 
 static DEFINE_MUTEX(xfs_uuid_table_mutex);
@@ -684,6 +686,7 @@ xfs_mountfs(
        xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
        xfs_ialloc_compute_maxlevels(mp);
        xfs_rmapbt_compute_maxlevels(mp);
+       xfs_refcountbt_compute_maxlevels(mp);
 
        xfs_set_maxicount(mp);
 
@@ -922,6 +925,15 @@ xfs_mountfs(
                }
        }
 
+       /*
+        * During the second phase of log recovery, we need iget and
+        * iput to behave like they do for an active filesystem.
+        * xfs_fs_drop_inode needs to be able to prevent the deletion
+        * of inodes before we're done replaying log items on those
+        * inodes.
+        */
+       mp->m_super->s_flags |= MS_ACTIVE;
+
        /*
         * Finish recovering the file system.  This part needed to be delayed
         * until after the root and real-time bitmap inodes were consistently
@@ -974,10 +986,28 @@ xfs_mountfs(
                if (error)
                        xfs_warn(mp,
        "Unable to allocate reserve blocks. Continuing without reserve pool.");
+
+               /* Recover any CoW blocks that never got remapped. */
+               error = xfs_reflink_recover_cow(mp);
+               if (error) {
+                       xfs_err(mp,
+       "Error %d recovering leftover CoW allocations.", error);
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       goto out_quota;
+               }
+
+               /* Reserve AG blocks for future btree expansion. */
+               error = xfs_fs_reserve_ag_blocks(mp);
+               if (error && error != -ENOSPC)
+                       goto out_agresv;
        }
 
        return 0;
 
+ out_agresv:
+       xfs_fs_unreserve_ag_blocks(mp);
+ out_quota:
+       xfs_qm_unmount_quotas(mp);
  out_rtunmount:
        xfs_rtunmount_inodes(mp);
  out_rele_rip:
@@ -1019,7 +1049,9 @@ xfs_unmountfs(
        int                     error;
 
        cancel_delayed_work_sync(&mp->m_eofblocks_work);
+       cancel_delayed_work_sync(&mp->m_cowblocks_work);
 
+       xfs_fs_unreserve_ag_blocks(mp);
        xfs_qm_unmount_quotas(mp);
        xfs_rtunmount_inodes(mp);
        IRELE(mp->m_rootip);
index 041d949..819b80b 100644 (file)
@@ -124,10 +124,13 @@ typedef struct xfs_mount {
        uint                    m_inobt_mnr[2]; /* min inobt btree records */
        uint                    m_rmap_mxr[2];  /* max rmap btree records */
        uint                    m_rmap_mnr[2];  /* min rmap btree records */
+       uint                    m_refc_mxr[2];  /* max refc btree records */
+       uint                    m_refc_mnr[2];  /* min refc btree records */
        uint                    m_ag_maxlevels; /* XFS_AG_MAXLEVELS */
        uint                    m_bm_maxlevels[2]; /* XFS_BM_MAXLEVELS */
        uint                    m_in_maxlevels; /* max inobt btree levels. */
        uint                    m_rmap_maxlevels; /* max rmap btree levels */
+       uint                    m_refc_maxlevels; /* max refcount btree level */
        xfs_extlen_t            m_ag_prealloc_blocks; /* reserved ag blocks */
        uint                    m_alloc_set_aside; /* space we can't use */
        uint                    m_ag_max_usable; /* max space per AG */
@@ -161,6 +164,8 @@ typedef struct xfs_mount {
        struct delayed_work     m_reclaim_work; /* background inode reclaim */
        struct delayed_work     m_eofblocks_work; /* background eof blocks
                                                     trimming */
+       struct delayed_work     m_cowblocks_work; /* background cow blocks
+                                                    trimming */
        bool                    m_update_sb;    /* sb needs update in mount */
        int64_t                 m_low_space[XFS_LOWSP_MAX];
                                                /* low free space thresholds */
@@ -399,6 +404,9 @@ typedef struct xfs_perag {
        struct xfs_ag_resv      pag_meta_resv;
        /* Blocks reserved for just AGFL-based metadata. */
        struct xfs_ag_resv      pag_agfl_resv;
+
+       /* reference count */
+       __uint8_t               pagf_refcount_level;
 } xfs_perag_t;
 
 static inline struct xfs_ag_resv *
index 69e2986..0c381d7 100644 (file)
@@ -49,6 +49,8 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(struct xfs_dsymlink_hdr,          56);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_key,             4);
        XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_rec,             16);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_refcount_key,          4);
+       XFS_CHECK_STRUCT_SIZE(struct xfs_refcount_rec,          12);
        XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_key,              20);
        XFS_CHECK_STRUCT_SIZE(struct xfs_rmap_rec,              24);
        XFS_CHECK_STRUCT_SIZE(struct xfs_timestamp,             8);
@@ -56,6 +58,7 @@ xfs_check_ondisk_structs(void)
        XFS_CHECK_STRUCT_SIZE(xfs_alloc_ptr_t,                  4);
        XFS_CHECK_STRUCT_SIZE(xfs_alloc_rec_t,                  8);
        XFS_CHECK_STRUCT_SIZE(xfs_inobt_ptr_t,                  4);
+       XFS_CHECK_STRUCT_SIZE(xfs_refcount_ptr_t,               4);
        XFS_CHECK_STRUCT_SIZE(xfs_rmap_ptr_t,                   4);
 
        /* dir/attr trees */
index 0f14b2e..93a7aaf 100644 (file)
@@ -113,6 +113,13 @@ xfs_fs_map_blocks(
        if (XFS_IS_REALTIME_INODE(ip))
                return -ENXIO;
 
+       /*
+        * The pNFS block layout spec actually supports reflink like
+        * functionality, but the Linux pNFS server doesn't implement it yet.
+        */
+       if (xfs_is_reflink_inode(ip))
+               return -ENXIO;
+
        /*
         * Lock out any other I/O before we flush and invalidate the pagecache,
         * and then hand out a layout to the remote system.  This is very
diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
new file mode 100644 (file)
index 0000000..fe86a66
--- /dev/null
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_bit.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_log.h"
+#include "xfs_refcount.h"
+
+
+kmem_zone_t    *xfs_cui_zone;
+kmem_zone_t    *xfs_cud_zone;
+
+static inline struct xfs_cui_log_item *CUI_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_cui_log_item, cui_item);
+}
+
+void
+xfs_cui_item_free(
+       struct xfs_cui_log_item *cuip)
+{
+       if (cuip->cui_format.cui_nextents > XFS_CUI_MAX_FAST_EXTENTS)
+               kmem_free(cuip);
+       else
+               kmem_zone_free(xfs_cui_zone, cuip);
+}
+
+STATIC void
+xfs_cui_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+
+       *nvecs += 1;
+       *nbytes += xfs_cui_log_format_sizeof(cuip->cui_format.cui_nextents);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given cui log item. We use only 1 iovec, and we point that
+ * at the cui_log_format structure embedded in the cui item.
+ * It is at this point that we assert that all of the extent
+ * slots in the cui item have been filled.
+ */
+STATIC void
+xfs_cui_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       ASSERT(atomic_read(&cuip->cui_next_extent) ==
+                       cuip->cui_format.cui_nextents);
+
+       cuip->cui_format.cui_type = XFS_LI_CUI;
+       cuip->cui_format.cui_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUI_FORMAT, &cuip->cui_format,
+                       xfs_cui_log_format_sizeof(cuip->cui_format.cui_nextents));
+}
+
+/*
+ * Pinning has no meaning for an cui item, so just return.
+ */
+STATIC void
+xfs_cui_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * The unpin operation is the last place an CUI is manipulated in the log. It is
+ * either inserted in the AIL or aborted in the event of a log I/O error. In
+ * either case, the CUI transaction has been successfully committed to make it
+ * this far. Therefore, we expect whoever committed the CUI to either construct
+ * and commit the CUD or drop the CUD's reference in the event of error. Simply
+ * drop the log's CUI reference now that the log is done with it.
+ */
+STATIC void
+xfs_cui_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+       struct xfs_cui_log_item *cuip = CUI_ITEM(lip);
+
+       xfs_cui_release(cuip);
+}
+
+/*
+ * CUI items have no locking or pushing.  However, since CUIs are pulled from
+ * the AIL when their corresponding CUDs are committed to disk, their situation
+ * is very similar to being pinned.  Return XFS_ITEM_PINNED so that the caller
+ * will eventually flush the log.  This should help in getting the CUI out of
+ * the AIL.
+ */
+STATIC uint
+xfs_cui_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The CUI has been either committed or aborted if the transaction has been
+ * cancelled. If the transaction was cancelled, an CUD isn't going to be
+ * constructed and thus we free the CUI here directly.
+ */
+STATIC void
+xfs_cui_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       if (lip->li_flags & XFS_LI_ABORTED)
+               xfs_cui_item_free(CUI_ITEM(lip));
+}
+
+/*
+ * The CUI is logged only once and cannot be moved in the log, so simply return
+ * the lsn at which it's been logged.
+ */
+STATIC xfs_lsn_t
+xfs_cui_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       return lsn;
+}
+
+/*
+ * The CUI dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_cui_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all cui log items.
+ */
+static const struct xfs_item_ops xfs_cui_item_ops = {
+       .iop_size       = xfs_cui_item_size,
+       .iop_format     = xfs_cui_item_format,
+       .iop_pin        = xfs_cui_item_pin,
+       .iop_unpin      = xfs_cui_item_unpin,
+       .iop_unlock     = xfs_cui_item_unlock,
+       .iop_committed  = xfs_cui_item_committed,
+       .iop_push       = xfs_cui_item_push,
+       .iop_committing = xfs_cui_item_committing,
+};
+
+/*
+ * Allocate and initialize an cui item with the given number of extents.
+ */
+struct xfs_cui_log_item *
+xfs_cui_init(
+       struct xfs_mount                *mp,
+       uint                            nextents)
+
+{
+       struct xfs_cui_log_item         *cuip;
+
+       ASSERT(nextents > 0);
+       if (nextents > XFS_CUI_MAX_FAST_EXTENTS)
+               cuip = kmem_zalloc(xfs_cui_log_item_sizeof(nextents),
+                               KM_SLEEP);
+       else
+               cuip = kmem_zone_zalloc(xfs_cui_zone, KM_SLEEP);
+
+       xfs_log_item_init(mp, &cuip->cui_item, XFS_LI_CUI, &xfs_cui_item_ops);
+       cuip->cui_format.cui_nextents = nextents;
+       cuip->cui_format.cui_id = (uintptr_t)(void *)cuip;
+       atomic_set(&cuip->cui_next_extent, 0);
+       atomic_set(&cuip->cui_refcount, 2);
+
+       return cuip;
+}
+
+/*
+ * Freeing the CUI requires that we remove it from the AIL if it has already
+ * been placed there. However, the CUI may not yet have been placed in the AIL
+ * when called by xfs_cui_release() from CUD processing due to the ordering of
+ * committed vs unpin operations in bulk insert operations. Hence the reference
+ * count to ensure only the last caller frees the CUI.
+ */
+void
+xfs_cui_release(
+       struct xfs_cui_log_item *cuip)
+{
+       if (atomic_dec_and_test(&cuip->cui_refcount)) {
+               xfs_trans_ail_remove(&cuip->cui_item, SHUTDOWN_LOG_IO_ERROR);
+               xfs_cui_item_free(cuip);
+       }
+}
+
+static inline struct xfs_cud_log_item *CUD_ITEM(struct xfs_log_item *lip)
+{
+       return container_of(lip, struct xfs_cud_log_item, cud_item);
+}
+
+STATIC void
+xfs_cud_item_size(
+       struct xfs_log_item     *lip,
+       int                     *nvecs,
+       int                     *nbytes)
+{
+       *nvecs += 1;
+       *nbytes += sizeof(struct xfs_cud_log_format);
+}
+
+/*
+ * This is called to fill in the vector of log iovecs for the
+ * given cud log item. We use only 1 iovec, and we point that
+ * at the cud_log_format structure embedded in the cud item.
+ * It is at this point that we assert that all of the extent
+ * slots in the cud item have been filled.
+ */
+STATIC void
+xfs_cud_item_format(
+       struct xfs_log_item     *lip,
+       struct xfs_log_vec      *lv)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+       struct xfs_log_iovec    *vecp = NULL;
+
+       cudp->cud_format.cud_type = XFS_LI_CUD;
+       cudp->cud_format.cud_size = 1;
+
+       xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_CUD_FORMAT, &cudp->cud_format,
+                       sizeof(struct xfs_cud_log_format));
+}
+
+/*
+ * Pinning has no meaning for an cud item, so just return.
+ */
+STATIC void
+xfs_cud_item_pin(
+       struct xfs_log_item     *lip)
+{
+}
+
+/*
+ * Since pinning has no meaning for an cud item, unpinning does
+ * not either.
+ */
+STATIC void
+xfs_cud_item_unpin(
+       struct xfs_log_item     *lip,
+       int                     remove)
+{
+}
+
+/*
+ * There isn't much you can do to push on an cud item.  It is simply stuck
+ * waiting for the log to be flushed to disk.
+ */
+STATIC uint
+xfs_cud_item_push(
+       struct xfs_log_item     *lip,
+       struct list_head        *buffer_list)
+{
+       return XFS_ITEM_PINNED;
+}
+
+/*
+ * The CUD is either committed or aborted if the transaction is cancelled. If
+ * the transaction is cancelled, drop our reference to the CUI and free the
+ * CUD.
+ */
+STATIC void
+xfs_cud_item_unlock(
+       struct xfs_log_item     *lip)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+
+       if (lip->li_flags & XFS_LI_ABORTED) {
+               xfs_cui_release(cudp->cud_cuip);
+               kmem_zone_free(xfs_cud_zone, cudp);
+       }
+}
+
+/*
+ * When the cud item is committed to disk, all we need to do is delete our
+ * reference to our partner cui item and then free ourselves. Since we're
+ * freeing ourselves we must return -1 to keep the transaction code from
+ * further referencing this item.
+ */
+STATIC xfs_lsn_t
+xfs_cud_item_committed(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+       struct xfs_cud_log_item *cudp = CUD_ITEM(lip);
+
+       /*
+        * Drop the CUI reference regardless of whether the CUD has been
+        * aborted. Once the CUD transaction is constructed, it is the sole
+        * responsibility of the CUD to release the CUI (even if the CUI is
+        * aborted due to log I/O error).
+        */
+       xfs_cui_release(cudp->cud_cuip);
+       kmem_zone_free(xfs_cud_zone, cudp);
+
+       return (xfs_lsn_t)-1;
+}
+
+/*
+ * The CUD dependency tracking op doesn't do squat.  It can't because
+ * it doesn't know where the free extent is coming from.  The dependency
+ * tracking has to be handled by the "enclosing" metadata object.  For
+ * example, for inodes, the inode is locked throughout the extent freeing
+ * so the dependency should be recorded there.
+ */
+STATIC void
+xfs_cud_item_committing(
+       struct xfs_log_item     *lip,
+       xfs_lsn_t               lsn)
+{
+}
+
+/*
+ * This is the ops vector shared by all cud log items.
+ */
+static const struct xfs_item_ops xfs_cud_item_ops = {
+       .iop_size       = xfs_cud_item_size,
+       .iop_format     = xfs_cud_item_format,
+       .iop_pin        = xfs_cud_item_pin,
+       .iop_unpin      = xfs_cud_item_unpin,
+       .iop_unlock     = xfs_cud_item_unlock,
+       .iop_committed  = xfs_cud_item_committed,
+       .iop_push       = xfs_cud_item_push,
+       .iop_committing = xfs_cud_item_committing,
+};
+
+/*
+ * Allocate and initialize an cud item with the given number of extents.
+ */
+struct xfs_cud_log_item *
+xfs_cud_init(
+       struct xfs_mount                *mp,
+       struct xfs_cui_log_item         *cuip)
+
+{
+       struct xfs_cud_log_item *cudp;
+
+       cudp = kmem_zone_zalloc(xfs_cud_zone, KM_SLEEP);
+       xfs_log_item_init(mp, &cudp->cud_item, XFS_LI_CUD, &xfs_cud_item_ops);
+       cudp->cud_cuip = cuip;
+       cudp->cud_format.cud_cui_id = cuip->cui_format.cui_id;
+
+       return cudp;
+}
+
+/*
+ * Process a refcount update intent item that was recovered from the log.
+ * We need to update the refcountbt.
+ */
+int
+xfs_cui_recover(
+       struct xfs_mount                *mp,
+       struct xfs_cui_log_item         *cuip)
+{
+       int                             i;
+       int                             error = 0;
+       unsigned int                    refc_type;
+       struct xfs_phys_extent          *refc;
+       xfs_fsblock_t                   startblock_fsb;
+       bool                            op_ok;
+       struct xfs_cud_log_item         *cudp;
+       struct xfs_trans                *tp;
+       struct xfs_btree_cur            *rcur = NULL;
+       enum xfs_refcount_intent_type   type;
+       xfs_fsblock_t                   firstfsb;
+       xfs_fsblock_t                   new_fsb;
+       xfs_extlen_t                    new_len;
+       struct xfs_bmbt_irec            irec;
+       struct xfs_defer_ops            dfops;
+       bool                            requeue_only = false;
+
+       ASSERT(!test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags));
+
+       /*
+        * First check the validity of the extents described by the
+        * CUI.  If any are bad, then assume that all are bad and
+        * just toss the CUI.
+        */
+       for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+               refc = &cuip->cui_format.cui_extents[i];
+               startblock_fsb = XFS_BB_TO_FSB(mp,
+                                  XFS_FSB_TO_DADDR(mp, refc->pe_startblock));
+               switch (refc->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK) {
+               case XFS_REFCOUNT_INCREASE:
+               case XFS_REFCOUNT_DECREASE:
+               case XFS_REFCOUNT_ALLOC_COW:
+               case XFS_REFCOUNT_FREE_COW:
+                       op_ok = true;
+                       break;
+               default:
+                       op_ok = false;
+                       break;
+               }
+               if (!op_ok || startblock_fsb == 0 ||
+                   refc->pe_len == 0 ||
+                   startblock_fsb >= mp->m_sb.sb_dblocks ||
+                   refc->pe_len >= mp->m_sb.sb_agblocks ||
+                   (refc->pe_flags & ~XFS_REFCOUNT_EXTENT_FLAGS)) {
+                       /*
+                        * This will pull the CUI from the AIL and
+                        * free the memory associated with it.
+                        */
+                       set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
+                       xfs_cui_release(cuip);
+                       return -EIO;
+               }
+       }
+
+       /*
+        * Under normal operation, refcount updates are deferred, so we
+        * wouldn't be adding them directly to a transaction.  All
+        * refcount updates manage reservation usage internally and
+        * dynamically by deferring work that won't fit in the
+        * transaction.  Normally, any work that needs to be deferred
+        * gets attached to the same defer_ops that scheduled the
+        * refcount update.  However, we're in log recovery here, so we
+        * we create our own defer_ops and use that to finish up any
+        * work that doesn't fit.
+        */
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
+       if (error)
+               return error;
+       cudp = xfs_trans_get_cud(tp, cuip);
+
+       xfs_defer_init(&dfops, &firstfsb);
+       for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+               refc = &cuip->cui_format.cui_extents[i];
+               refc_type = refc->pe_flags & XFS_REFCOUNT_EXTENT_TYPE_MASK;
+               switch (refc_type) {
+               case XFS_REFCOUNT_INCREASE:
+               case XFS_REFCOUNT_DECREASE:
+               case XFS_REFCOUNT_ALLOC_COW:
+               case XFS_REFCOUNT_FREE_COW:
+                       type = refc_type;
+                       break;
+               default:
+                       error = -EFSCORRUPTED;
+                       goto abort_error;
+               }
+               if (requeue_only) {
+                       new_fsb = refc->pe_startblock;
+                       new_len = refc->pe_len;
+               } else
+                       error = xfs_trans_log_finish_refcount_update(tp, cudp,
+                               &dfops, type, refc->pe_startblock, refc->pe_len,
+                               &new_fsb, &new_len, &rcur);
+               if (error)
+                       goto abort_error;
+
+               /* Requeue what we didn't finish. */
+               if (new_len > 0) {
+                       irec.br_startblock = new_fsb;
+                       irec.br_blockcount = new_len;
+                       switch (type) {
+                       case XFS_REFCOUNT_INCREASE:
+                               error = xfs_refcount_increase_extent(
+                                               tp->t_mountp, &dfops, &irec);
+                               break;
+                       case XFS_REFCOUNT_DECREASE:
+                               error = xfs_refcount_decrease_extent(
+                                               tp->t_mountp, &dfops, &irec);
+                               break;
+                       case XFS_REFCOUNT_ALLOC_COW:
+                               error = xfs_refcount_alloc_cow_extent(
+                                               tp->t_mountp, &dfops,
+                                               irec.br_startblock,
+                                               irec.br_blockcount);
+                               break;
+                       case XFS_REFCOUNT_FREE_COW:
+                               error = xfs_refcount_free_cow_extent(
+                                               tp->t_mountp, &dfops,
+                                               irec.br_startblock,
+                                               irec.br_blockcount);
+                               break;
+                       default:
+                               ASSERT(0);
+                       }
+                       if (error)
+                               goto abort_error;
+                       requeue_only = true;
+               }
+       }
+
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto abort_error;
+       set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
+       error = xfs_trans_commit(tp);
+       return error;
+
+abort_error:
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       return error;
+}
diff --git a/fs/xfs/xfs_refcount_item.h b/fs/xfs/xfs_refcount_item.h
new file mode 100644 (file)
index 0000000..5b74ddd
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef        __XFS_REFCOUNT_ITEM_H__
+#define        __XFS_REFCOUNT_ITEM_H__
+
+/*
+ * There are (currently) two pairs of refcount btree redo item types:
+ * increase and decrease.  The log items for these are CUI (refcount
+ * update intent) and CUD (refcount update done).  The redo item type
+ * is encoded in the flags field of each xfs_map_extent.
+ *
+ * *I items should be recorded in the *first* of a series of rolled
+ * transactions, and the *D items should be recorded in the same
+ * transaction that records the associated refcountbt updates.
+ *
+ * Should the system crash after the commit of the first transaction
+ * but before the commit of the final transaction in a series, log
+ * recovery will use the redo information recorded by the intent items
+ * to replay the refcountbt metadata updates.
+ */
+
+/* kernel only CUI/CUD definitions */
+
+struct xfs_mount;
+struct kmem_zone;
+
+/*
+ * Max number of extents in fast allocation path.
+ */
+#define        XFS_CUI_MAX_FAST_EXTENTS        16
+
+/*
+ * Define CUI flag bits. Manipulated by set/clear/test_bit operators.
+ */
+#define        XFS_CUI_RECOVERED               1
+
+/*
+ * This is the "refcount update intent" log item.  It is used to log
+ * the fact that some reverse mappings need to change.  It is used in
+ * conjunction with the "refcount update done" log item described
+ * below.
+ *
+ * These log items follow the same rules as struct xfs_efi_log_item;
+ * see the comments about that structure (in xfs_extfree_item.h) for
+ * more details.
+ */
+struct xfs_cui_log_item {
+       struct xfs_log_item             cui_item;
+       atomic_t                        cui_refcount;
+       atomic_t                        cui_next_extent;
+       unsigned long                   cui_flags;      /* misc flags */
+       struct xfs_cui_log_format       cui_format;
+};
+
+static inline size_t
+xfs_cui_log_item_sizeof(
+       unsigned int            nr)
+{
+       return offsetof(struct xfs_cui_log_item, cui_format) +
+                       xfs_cui_log_format_sizeof(nr);
+}
+
+/*
+ * This is the "refcount update done" log item.  It is used to log the
+ * fact that some refcountbt updates mentioned in an earlier cui item
+ * have been performed.
+ */
+struct xfs_cud_log_item {
+       struct xfs_log_item             cud_item;
+       struct xfs_cui_log_item         *cud_cuip;
+       struct xfs_cud_log_format       cud_format;
+};
+
+extern struct kmem_zone        *xfs_cui_zone;
+extern struct kmem_zone        *xfs_cud_zone;
+
+struct xfs_cui_log_item *xfs_cui_init(struct xfs_mount *, uint);
+struct xfs_cud_log_item *xfs_cud_init(struct xfs_mount *,
+               struct xfs_cui_log_item *);
+void xfs_cui_item_free(struct xfs_cui_log_item *);
+void xfs_cui_release(struct xfs_cui_log_item *);
+int xfs_cui_recover(struct xfs_mount *mp, struct xfs_cui_log_item *cuip);
+
+#endif /* __XFS_REFCOUNT_ITEM_H__ */
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
new file mode 100644 (file)
index 0000000..5965e94
--- /dev/null
@@ -0,0 +1,1688 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_inode.h"
+#include "xfs_trans.h"
+#include "xfs_inode_item.h"
+#include "xfs_bmap.h"
+#include "xfs_bmap_util.h"
+#include "xfs_error.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_ioctl.h"
+#include "xfs_trace.h"
+#include "xfs_log.h"
+#include "xfs_icache.h"
+#include "xfs_pnfs.h"
+#include "xfs_btree.h"
+#include "xfs_refcount_btree.h"
+#include "xfs_refcount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_bit.h"
+#include "xfs_alloc.h"
+#include "xfs_quota_defs.h"
+#include "xfs_quota.h"
+#include "xfs_btree.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_reflink.h"
+#include "xfs_iomap.h"
+#include "xfs_rmap_btree.h"
+#include "xfs_sb.h"
+#include "xfs_ag_resv.h"
+
+/*
+ * Copy on Write of Shared Blocks
+ *
+ * XFS must preserve "the usual" file semantics even when two files share
+ * the same physical blocks.  This means that a write to one file must not
+ * alter the blocks in a different file; the way that we'll do that is
+ * through the use of a copy-on-write mechanism.  At a high level, that
+ * means that when we want to write to a shared block, we allocate a new
+ * block, write the data to the new block, and if that succeeds we map the
+ * new block into the file.
+ *
+ * XFS provides a "delayed allocation" mechanism that defers the allocation
+ * of disk blocks to dirty-but-not-yet-mapped file blocks as long as
+ * possible.  This reduces fragmentation by enabling the filesystem to ask
+ * for bigger chunks less often, which is exactly what we want for CoW.
+ *
+ * The delalloc mechanism begins when the kernel wants to make a block
+ * writable (write_begin or page_mkwrite).  If the offset is not mapped, we
+ * create a delalloc mapping, which is a regular in-core extent, but without
+ * a real startblock.  (For delalloc mappings, the startblock encodes both
+ * a flag that this is a delalloc mapping, and a worst-case estimate of how
+ * many blocks might be required to put the mapping into the BMBT.)  delalloc
+ * mappings are a reservation against the free space in the filesystem;
+ * adjacent mappings can also be combined into fewer larger mappings.
+ *
+ * When dirty pages are being written out (typically in writepage), the
+ * delalloc reservations are converted into real mappings by allocating
+ * blocks and replacing the delalloc mapping with real ones.  A delalloc
+ * mapping can be replaced by several real ones if the free space is
+ * fragmented.
+ *
+ * We want to adapt the delalloc mechanism for copy-on-write, since the
+ * write paths are similar.  The first two steps (creating the reservation
+ * and allocating the blocks) are exactly the same as delalloc except that
+ * the mappings must be stored in a separate CoW fork because we do not want
+ * to disturb the mapping in the data fork until we're sure that the write
+ * succeeded.  IO completion in this case is the process of removing the old
+ * mapping from the data fork and moving the new mapping from the CoW fork to
+ * the data fork.  This will be discussed shortly.
+ *
+ * For now, unaligned directio writes will be bounced back to the page cache.
+ * Block-aligned directio writes will use the same mechanism as buffered
+ * writes.
+ *
+ * CoW remapping must be done after the data block write completes,
+ * because we don't want to destroy the old data fork map until we're sure
+ * the new block has been written.  Since the new mappings are kept in a
+ * separate fork, we can simply iterate these mappings to find the ones
+ * that cover the file blocks that we just CoW'd.  For each extent, simply
+ * unmap the corresponding range in the data fork, map the new range into
+ * the data fork, and remove the extent from the CoW fork.
+ *
+ * Since the remapping operation can be applied to an arbitrary file
+ * range, we record the need for the remap step as a flag in the ioend
+ * instead of declaring a new IO type.  This is required for direct io
+ * because we only have ioend for the whole dio, and we have to be able to
+ * remember the presence of unwritten blocks and CoW blocks with a single
+ * ioend structure.  Better yet, the more ground we can cover with one
+ * ioend, the better.
+ */
+
+/*
+ * Given an AG extent, find the lowest-numbered run of shared blocks
+ * within that range and return the range in fbno/flen.  If
+ * find_end_of_shared is true, return the longest contiguous extent of
+ * shared blocks.  If there are no shared extents, fbno and flen will
+ * be set to NULLAGBLOCK and 0, respectively.
+ */
+int
+xfs_reflink_find_shared(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno,
+       xfs_agblock_t           agbno,
+       xfs_extlen_t            aglen,
+       xfs_agblock_t           *fbno,
+       xfs_extlen_t            *flen,
+       bool                    find_end_of_shared)
+{
+       struct xfs_buf          *agbp;
+       struct xfs_btree_cur    *cur;
+       int                     error;
+
+       error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
+       if (error)
+               return error;
+
+       cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
+
+       error = xfs_refcount_find_shared(cur, agbno, aglen, fbno, flen,
+                       find_end_of_shared);
+
+       xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
+
+       xfs_buf_relse(agbp);
+       return error;
+}
+
+/*
+ * Trim the mapping to the next block where there's a change in the
+ * shared/unshared status.  More specifically, this means that we
+ * find the lowest-numbered extent of shared blocks that coincides with
+ * the given block mapping.  If the shared extent overlaps the start of
+ * the mapping, trim the mapping to the end of the shared extent.  If
+ * the shared region intersects the mapping, trim the mapping to the
+ * start of the shared extent.  If there are no shared regions that
+ * overlap, just return the original extent.
+ */
+int
+xfs_reflink_trim_around_shared(
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *irec,
+       bool                    *shared,
+       bool                    *trimmed)
+{
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           fbno;
+       xfs_extlen_t            flen;
+       int                     error = 0;
+
+       /* Holes, unwritten, and delalloc extents cannot be shared */
+       if (!xfs_is_reflink_inode(ip) ||
+           ISUNWRITTEN(irec) ||
+           irec->br_startblock == HOLESTARTBLOCK ||
+           irec->br_startblock == DELAYSTARTBLOCK) {
+               *shared = false;
+               return 0;
+       }
+
+       trace_xfs_reflink_trim_around_shared(ip, irec);
+
+       agno = XFS_FSB_TO_AGNO(ip->i_mount, irec->br_startblock);
+       agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock);
+       aglen = irec->br_blockcount;
+
+       error = xfs_reflink_find_shared(ip->i_mount, agno, agbno,
+                       aglen, &fbno, &flen, true);
+       if (error)
+               return error;
+
+       *shared = *trimmed = false;
+       if (fbno == NULLAGBLOCK) {
+               /* No shared blocks at all. */
+               return 0;
+       } else if (fbno == agbno) {
+               /*
+                * The start of this extent is shared.  Truncate the
+                * mapping at the end of the shared region so that a
+                * subsequent iteration starts at the start of the
+                * unshared region.
+                */
+               irec->br_blockcount = flen;
+               *shared = true;
+               if (flen != aglen)
+                       *trimmed = true;
+               return 0;
+       } else {
+               /*
+                * There's a shared extent midway through this extent.
+                * Truncate the mapping at the start of the shared
+                * extent so that a subsequent iteration starts at the
+                * start of the shared region.
+                */
+               irec->br_blockcount = fbno - agbno;
+               *trimmed = true;
+               return 0;
+       }
+}
+
+/* Create a CoW reservation for a range of blocks within a file. */
+static int
+__xfs_reflink_reserve_cow(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *offset_fsb,
+       xfs_fileoff_t           end_fsb,
+       bool                    *skipped)
+{
+       struct xfs_bmbt_irec    got, prev, imap;
+       xfs_fileoff_t           orig_end_fsb;
+       int                     nimaps, eof = 0, error = 0;
+       bool                    shared = false, trimmed = false;
+       xfs_extnum_t            idx;
+       xfs_extlen_t            align;
+
+       /* Already reserved?  Skip the refcount btree access. */
+       xfs_bmap_search_extents(ip, *offset_fsb, XFS_COW_FORK, &eof, &idx,
+                       &got, &prev);
+       if (!eof && got.br_startoff <= *offset_fsb) {
+               end_fsb = orig_end_fsb = got.br_startoff + got.br_blockcount;
+               trace_xfs_reflink_cow_found(ip, &got);
+               goto done;
+       }
+
+       /* Read extent from the source file. */
+       nimaps = 1;
+       error = xfs_bmapi_read(ip, *offset_fsb, end_fsb - *offset_fsb,
+                       &imap, &nimaps, 0);
+       if (error)
+               goto out_unlock;
+       ASSERT(nimaps == 1);
+
+       /* Trim the mapping to the nearest shared extent boundary. */
+       error = xfs_reflink_trim_around_shared(ip, &imap, &shared, &trimmed);
+       if (error)
+               goto out_unlock;
+
+       end_fsb = orig_end_fsb = imap.br_startoff + imap.br_blockcount;
+
+       /* Not shared?  Just report the (potentially capped) extent. */
+       if (!shared) {
+               *skipped = true;
+               goto done;
+       }
+
+       /*
+        * Fork all the shared blocks from our write offset until the end of
+        * the extent.
+        */
+       error = xfs_qm_dqattach_locked(ip, 0);
+       if (error)
+               goto out_unlock;
+
+       align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
+       if (align)
+               end_fsb = roundup_64(end_fsb, align);
+
+retry:
+       error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, *offset_fsb,
+                       end_fsb - *offset_fsb, &got,
+                       &prev, &idx, eof);
+       switch (error) {
+       case 0:
+               break;
+       case -ENOSPC:
+       case -EDQUOT:
+               /* retry without any preallocation */
+               trace_xfs_reflink_cow_enospc(ip, &imap);
+               if (end_fsb != orig_end_fsb) {
+                       end_fsb = orig_end_fsb;
+                       goto retry;
+               }
+               /*FALLTHRU*/
+       default:
+               goto out_unlock;
+       }
+
+       if (end_fsb != orig_end_fsb)
+               xfs_inode_set_cowblocks_tag(ip);
+
+       trace_xfs_reflink_cow_alloc(ip, &got);
+done:
+       *offset_fsb = end_fsb;
+out_unlock:
+       return error;
+}
+
+/* Create a CoW reservation for part of a file. */
+int
+xfs_reflink_reserve_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           offset_fsb, end_fsb;
+       bool                    skipped = false;
+       int                     error;
+
+       trace_xfs_reflink_reserve_cow_range(ip, offset, count);
+
+       offset_fsb = XFS_B_TO_FSBT(mp, offset);
+       end_fsb = XFS_B_TO_FSB(mp, offset + count);
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       while (offset_fsb < end_fsb) {
+               error = __xfs_reflink_reserve_cow(ip, &offset_fsb, end_fsb,
+                               &skipped);
+               if (error) {
+                       trace_xfs_reflink_reserve_cow_range_error(ip, error,
+                               _RET_IP_);
+                       break;
+               }
+       }
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       return error;
+}
+
+/* Allocate all CoW reservations covering a range of blocks in a file. */
+static int
+__xfs_reflink_allocate_cow(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           *offset_fsb,
+       xfs_fileoff_t           end_fsb)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_bmbt_irec    imap;
+       struct xfs_defer_ops    dfops;
+       struct xfs_trans        *tp;
+       xfs_fsblock_t           first_block;
+       xfs_fileoff_t           next_fsb;
+       int                     nimaps = 1, error;
+       bool                    skipped = false;
+
+       xfs_defer_init(&dfops, &first_block);
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0,
+                       XFS_TRANS_RESERVE, &tp);
+       if (error)
+               return error;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+
+       next_fsb = *offset_fsb;
+       error = __xfs_reflink_reserve_cow(ip, &next_fsb, end_fsb, &skipped);
+       if (error)
+               goto out_trans_cancel;
+
+       if (skipped) {
+               *offset_fsb = next_fsb;
+               goto out_trans_cancel;
+       }
+
+       xfs_trans_ijoin(tp, ip, 0);
+       error = xfs_bmapi_write(tp, ip, *offset_fsb, next_fsb - *offset_fsb,
+                       XFS_BMAPI_COWFORK, &first_block,
+                       XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK),
+                       &imap, &nimaps, &dfops);
+       if (error)
+               goto out_trans_cancel;
+
+       /* We might not have been able to map the whole delalloc extent */
+       *offset_fsb = min(*offset_fsb + imap.br_blockcount, next_fsb);
+
+       error = xfs_defer_finish(&tp, &dfops, NULL);
+       if (error)
+               goto out_trans_cancel;
+
+       error = xfs_trans_commit(tp);
+
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+out_trans_cancel:
+       xfs_defer_cancel(&dfops);
+       xfs_trans_cancel(tp);
+       goto out_unlock;
+}
+
+/* Allocate all CoW reservations covering a part of a file. */
+int
+xfs_reflink_allocate_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           offset_fsb = XFS_B_TO_FSBT(mp, offset);
+       xfs_fileoff_t           end_fsb = XFS_B_TO_FSB(mp, offset + count);
+       int                     error;
+
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       trace_xfs_reflink_allocate_cow_range(ip, offset, count);
+
+       /*
+        * Make sure that the dquots are there.
+        */
+       error = xfs_qm_dqattach(ip, 0);
+       if (error)
+               return error;
+
+       while (offset_fsb < end_fsb) {
+               error = __xfs_reflink_allocate_cow(ip, &offset_fsb, end_fsb);
+               if (error) {
+                       trace_xfs_reflink_allocate_cow_range_error(ip, error,
+                                       _RET_IP_);
+                       break;
+               }
+       }
+
+       return error;
+}
+
+/*
+ * Find the CoW reservation (and whether or not it needs block allocation)
+ * for a given byte offset of a file.
+ */
+bool
+xfs_reflink_find_cow_mapping(
+       struct xfs_inode                *ip,
+       xfs_off_t                       offset,
+       struct xfs_bmbt_irec            *imap,
+       bool                            *need_alloc)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_fileoff_t                   bno;
+       xfs_extnum_t                    idx;
+
+       ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       /* Find the extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       bno = XFS_B_TO_FSBT(ip->i_mount, offset);
+       gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
+       if (!gotp)
+               return false;
+
+       xfs_bmbt_get_all(gotp, &irec);
+       if (bno >= irec.br_startoff + irec.br_blockcount ||
+           bno < irec.br_startoff)
+               return false;
+
+       trace_xfs_reflink_find_cow_mapping(ip, offset, 1, XFS_IO_OVERWRITE,
+                       &irec);
+
+       /* If it's still delalloc, we must allocate later. */
+       *imap = irec;
+       *need_alloc = !!(isnullstartblock(irec.br_startblock));
+
+       return true;
+}
+
+/*
+ * Trim an extent to end at the next CoW reservation past offset_fsb.
+ */
+int
+xfs_reflink_trim_irec_to_next_cow(
+       struct xfs_inode                *ip,
+       xfs_fileoff_t                   offset_fsb,
+       struct xfs_bmbt_irec            *imap)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_extnum_t                    idx;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       /* Find the extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       gotp = xfs_iext_bno_to_ext(ifp, offset_fsb, &idx);
+       if (!gotp)
+               return 0;
+       xfs_bmbt_get_all(gotp, &irec);
+
+       /* This is the extent before; try sliding up one. */
+       if (irec.br_startoff < offset_fsb) {
+               idx++;
+               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       return 0;
+               gotp = xfs_iext_get_ext(ifp, idx);
+               xfs_bmbt_get_all(gotp, &irec);
+       }
+
+       if (irec.br_startoff >= imap->br_startoff + imap->br_blockcount)
+               return 0;
+
+       imap->br_blockcount = irec.br_startoff - imap->br_startoff;
+       trace_xfs_reflink_trim_irec(ip, imap);
+
+       return 0;
+}
+
+/*
+ * Cancel all pending CoW reservations for some block range of an inode.
+ */
+int
+xfs_reflink_cancel_cow_blocks(
+       struct xfs_inode                *ip,
+       struct xfs_trans                **tpp,
+       xfs_fileoff_t                   offset_fsb,
+       xfs_fileoff_t                   end_fsb)
+{
+       struct xfs_bmbt_irec            irec;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error = 0;
+       int                             nimaps;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       /* Go find the old extent in the CoW fork. */
+       while (offset_fsb < end_fsb) {
+               nimaps = 1;
+               count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+               error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec,
+                               &nimaps, XFS_BMAPI_COWFORK);
+               if (error)
+                       break;
+               ASSERT(nimaps == 1);
+
+               trace_xfs_reflink_cancel_cow(ip, &irec);
+
+               if (irec.br_startblock == DELAYSTARTBLOCK) {
+                       /* Free a delayed allocation. */
+                       xfs_mod_fdblocks(ip->i_mount, irec.br_blockcount,
+                                       false);
+                       ip->i_delayed_blks -= irec.br_blockcount;
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &irec);
+                       if (error)
+                               break;
+               } else if (irec.br_startblock == HOLESTARTBLOCK) {
+                       /* empty */
+               } else {
+                       xfs_trans_ijoin(*tpp, ip, 0);
+                       xfs_defer_init(&dfops, &firstfsb);
+
+                       /* Free the CoW orphan record. */
+                       error = xfs_refcount_free_cow_extent(ip->i_mount,
+                                       &dfops, irec.br_startblock,
+                                       irec.br_blockcount);
+                       if (error)
+                               break;
+
+                       xfs_bmap_add_free(ip->i_mount, &dfops,
+                                       irec.br_startblock, irec.br_blockcount,
+                                       NULL);
+
+                       /* Update quota accounting */
+                       xfs_trans_mod_dquot_byino(*tpp, ip, XFS_TRANS_DQ_BCOUNT,
+                                       -(long)irec.br_blockcount);
+
+                       /* Roll the transaction */
+                       error = xfs_defer_finish(tpp, &dfops, ip);
+                       if (error) {
+                               xfs_defer_cancel(&dfops);
+                               break;
+                       }
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &irec);
+                       if (error)
+                               break;
+               }
+
+               /* Roll on... */
+               offset_fsb = irec.br_startoff + irec.br_blockcount;
+       }
+
+       return error;
+}
+
+/*
+ * Cancel all pending CoW reservations for some byte range of an inode.
+ */
+int
+xfs_reflink_cancel_cow_range(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               count)
+{
+       struct xfs_trans        *tp;
+       xfs_fileoff_t           offset_fsb;
+       xfs_fileoff_t           end_fsb;
+       int                     error;
+
+       trace_xfs_reflink_cancel_cow_range(ip, offset, count);
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+       if (count == NULLFILEOFF)
+               end_fsb = NULLFILEOFF;
+       else
+               end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
+
+       /* Start a rolling transaction to remove the mappings */
+       error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
+                       0, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* Scrape out the old CoW reservations */
+       error = xfs_reflink_cancel_cow_blocks(ip, &tp, offset_fsb, end_fsb);
+       if (error)
+               goto out_cancel;
+
+       error = xfs_trans_commit(tp);
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_cancel_cow_range_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Remap parts of a file's data fork after a successful CoW.
+ */
+int
+xfs_reflink_end_cow(
+       struct xfs_inode                *ip,
+       xfs_off_t                       offset,
+       xfs_off_t                       count)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_bmbt_irec            uirec;
+       struct xfs_trans                *tp;
+       xfs_fileoff_t                   offset_fsb;
+       xfs_fileoff_t                   end_fsb;
+       xfs_filblks_t                   count_fsb;
+       xfs_fsblock_t                   firstfsb;
+       struct xfs_defer_ops            dfops;
+       int                             error;
+       unsigned int                    resblks;
+       xfs_filblks_t                   ilen;
+       xfs_filblks_t                   rlen;
+       int                             nimaps;
+
+       trace_xfs_reflink_end_cow(ip, offset, count);
+
+       offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+       end_fsb = XFS_B_TO_FSB(ip->i_mount, offset + count);
+       count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+
+       /* Start a rolling transaction to switch the mappings */
+       resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
+       error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
+                       resblks, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* Go find the old extent in the CoW fork. */
+       while (offset_fsb < end_fsb) {
+               /* Read extent from the source file */
+               nimaps = 1;
+               count_fsb = (xfs_filblks_t)(end_fsb - offset_fsb);
+               error = xfs_bmapi_read(ip, offset_fsb, count_fsb, &irec,
+                               &nimaps, XFS_BMAPI_COWFORK);
+               if (error)
+                       goto out_cancel;
+               ASSERT(nimaps == 1);
+
+               ASSERT(irec.br_startblock != DELAYSTARTBLOCK);
+               trace_xfs_reflink_cow_remap(ip, &irec);
+
+               /*
+                * We can have a hole in the CoW fork if part of a directio
+                * write is CoW but part of it isn't.
+                */
+               rlen = ilen = irec.br_blockcount;
+               if (irec.br_startblock == HOLESTARTBLOCK)
+                       goto next_extent;
+
+               /* Unmap the old blocks in the data fork. */
+               while (rlen) {
+                       xfs_defer_init(&dfops, &firstfsb);
+                       error = __xfs_bunmapi(tp, ip, irec.br_startoff,
+                                       &rlen, 0, 1, &firstfsb, &dfops);
+                       if (error)
+                               goto out_defer;
+
+                       /*
+                        * Trim the extent to whatever got unmapped.
+                        * Remember, bunmapi works backwards.
+                        */
+                       uirec.br_startblock = irec.br_startblock + rlen;
+                       uirec.br_startoff = irec.br_startoff + rlen;
+                       uirec.br_blockcount = irec.br_blockcount - rlen;
+                       irec.br_blockcount = rlen;
+                       trace_xfs_reflink_cow_remap_piece(ip, &uirec);
+
+                       /* Free the CoW orphan record. */
+                       error = xfs_refcount_free_cow_extent(tp->t_mountp,
+                                       &dfops, uirec.br_startblock,
+                                       uirec.br_blockcount);
+                       if (error)
+                               goto out_defer;
+
+                       /* Map the new blocks into the data fork. */
+                       error = xfs_bmap_map_extent(tp->t_mountp, &dfops,
+                                       ip, &uirec);
+                       if (error)
+                               goto out_defer;
+
+                       /* Remove the mapping from the CoW fork. */
+                       error = xfs_bunmapi_cow(ip, &uirec);
+                       if (error)
+                               goto out_defer;
+
+                       error = xfs_defer_finish(&tp, &dfops, ip);
+                       if (error)
+                               goto out_defer;
+               }
+
+next_extent:
+               /* Roll on... */
+               offset_fsb = irec.br_startoff + ilen;
+       }
+
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       if (error)
+               goto out;
+       return 0;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_end_cow_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Free leftover CoW reservations that didn't get cleaned out.
+ */
+int
+xfs_reflink_recover_cow(
+       struct xfs_mount        *mp)
+{
+       xfs_agnumber_t          agno;
+       int                     error = 0;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return 0;
+
+       for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+               error = xfs_refcount_recover_cow_leftovers(mp, agno);
+               if (error)
+                       break;
+       }
+
+       return error;
+}
+
+/*
+ * Reflinking (Block) Ranges of Two Files Together
+ *
+ * First, ensure that the reflink flag is set on both inodes.  The flag is an
+ * optimization to avoid unnecessary refcount btree lookups in the write path.
+ *
+ * Now we can iteratively remap the range of extents (and holes) in src to the
+ * corresponding ranges in dest.  Let drange and srange denote the ranges of
+ * logical blocks in dest and src touched by the reflink operation.
+ *
+ * While the length of drange is greater than zero,
+ *    - Read src's bmbt at the start of srange ("imap")
+ *    - If imap doesn't exist, make imap appear to start at the end of srange
+ *      with zero length.
+ *    - If imap starts before srange, advance imap to start at srange.
+ *    - If imap goes beyond srange, truncate imap to end at the end of srange.
+ *    - Punch (imap start - srange start + imap len) blocks from dest at
+ *      offset (drange start).
+ *    - If imap points to a real range of pblks,
+ *         > Increase the refcount of the imap's pblks
+ *         > Map imap's pblks into dest at the offset
+ *           (drange start + imap start - srange start)
+ *    - Advance drange and srange by (imap start - srange start + imap len)
+ *
+ * Finally, if the reflink made dest longer, update both the in-core and
+ * on-disk file sizes.
+ *
+ * ASCII Art Demonstration:
+ *
+ * Let's say we want to reflink this source file:
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS (src file)
+ *   <-------------------->
+ *
+ * into this destination file:
+ *
+ * --DDDDDDDDDDDDDDDDDDD--DDD (dest file)
+ *        <-------------------->
+ * '-' means a hole, and 'S' and 'D' are written blocks in the src and dest.
+ * Observe that the range has different logical offsets in either file.
+ *
+ * Consider that the first extent in the source file doesn't line up with our
+ * reflink range.  Unmapping  and remapping are separate operations, so we can
+ * unmap more blocks from the destination file than we remap.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *   <------->
+ * --DDDDD---------DDDDD--DDD
+ *        <------->
+ *
+ * Now remap the source extent into the destination file:
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *   <------->
+ * --DDDDD--SSSSSSSDDDDD--DDD
+ *        <------->
+ *
+ * Do likewise with the second hole and extent in our range.  Holes in the
+ * unmap range don't affect our operation.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *            <---->
+ * --DDDDD--SSSSSSS-SSSSS-DDD
+ *                 <---->
+ *
+ * Finally, unmap and remap part of the third extent.  This will increase the
+ * size of the destination file.
+ *
+ * ----SSSSSSS-SSSSS----SSSSSS
+ *                  <----->
+ * --DDDDD--SSSSSSS-SSSSS----SSS
+ *                       <----->
+ *
+ * Once we update the destination file's i_size, we're done.
+ */
+
+/*
+ * Ensure the reflink bit is set in both inodes.
+ */
+STATIC int
+xfs_reflink_set_inode_flag(
+       struct xfs_inode        *src,
+       struct xfs_inode        *dest)
+{
+       struct xfs_mount        *mp = src->i_mount;
+       int                     error;
+       struct xfs_trans        *tp;
+
+       if (xfs_is_reflink_inode(src) && xfs_is_reflink_inode(dest))
+               return 0;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
+       if (error)
+               goto out_error;
+
+       /* Lock both files against IO */
+       if (src->i_ino == dest->i_ino)
+               xfs_ilock(src, XFS_ILOCK_EXCL);
+       else
+               xfs_lock_two_inodes(src, dest, XFS_ILOCK_EXCL);
+
+       if (!xfs_is_reflink_inode(src)) {
+               trace_xfs_reflink_set_inode_flag(src);
+               xfs_trans_ijoin(tp, src, XFS_ILOCK_EXCL);
+               src->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+               xfs_trans_log_inode(tp, src, XFS_ILOG_CORE);
+               xfs_ifork_init_cow(src);
+       } else
+               xfs_iunlock(src, XFS_ILOCK_EXCL);
+
+       if (src->i_ino == dest->i_ino)
+               goto commit_flags;
+
+       if (!xfs_is_reflink_inode(dest)) {
+               trace_xfs_reflink_set_inode_flag(dest);
+               xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
+               dest->i_d.di_flags2 |= XFS_DIFLAG2_REFLINK;
+               xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
+               xfs_ifork_init_cow(dest);
+       } else
+               xfs_iunlock(dest, XFS_ILOCK_EXCL);
+
+commit_flags:
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out_error;
+       return error;
+
+out_error:
+       trace_xfs_reflink_set_inode_flag_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Update destination inode size & cowextsize hint, if necessary.
+ */
+STATIC int
+xfs_reflink_update_dest(
+       struct xfs_inode        *dest,
+       xfs_off_t               newlen,
+       xfs_extlen_t            cowextsize)
+{
+       struct xfs_mount        *mp = dest->i_mount;
+       struct xfs_trans        *tp;
+       int                     error;
+
+       if (newlen <= i_size_read(VFS_I(dest)) && cowextsize == 0)
+               return 0;
+
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
+       if (error)
+               goto out_error;
+
+       xfs_ilock(dest, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, dest, XFS_ILOCK_EXCL);
+
+       if (newlen > i_size_read(VFS_I(dest))) {
+               trace_xfs_reflink_update_inode_size(dest, newlen);
+               i_size_write(VFS_I(dest), newlen);
+               dest->i_d.di_size = newlen;
+       }
+
+       if (cowextsize) {
+               dest->i_d.di_cowextsize = cowextsize;
+               dest->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+       }
+
+       xfs_trans_log_inode(tp, dest, XFS_ILOG_CORE);
+
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out_error;
+       return error;
+
+out_error:
+       trace_xfs_reflink_update_inode_size_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Do we have enough reserve in this AG to handle a reflink?  The refcount
+ * btree already reserved all the space it needs, but the rmap btree can grow
+ * infinitely, so we won't allow more reflinks when the AG is down to the
+ * btree reserves.
+ */
+static int
+xfs_reflink_ag_has_free_space(
+       struct xfs_mount        *mp,
+       xfs_agnumber_t          agno)
+{
+       struct xfs_perag        *pag;
+       int                     error = 0;
+
+       if (!xfs_sb_version_hasrmapbt(&mp->m_sb))
+               return 0;
+
+       pag = xfs_perag_get(mp, agno);
+       if (xfs_ag_resv_critical(pag, XFS_AG_RESV_AGFL) ||
+           xfs_ag_resv_critical(pag, XFS_AG_RESV_METADATA))
+               error = -ENOSPC;
+       xfs_perag_put(pag);
+       return error;
+}
+
+/*
+ * Unmap a range of blocks from a file, then map other blocks into the hole.
+ * The range to unmap is (destoff : destoff + srcioff + irec->br_blockcount).
+ * The extent irec is mapped into dest at irec->br_startoff.
+ */
+STATIC int
+xfs_reflink_remap_extent(
+       struct xfs_inode        *ip,
+       struct xfs_bmbt_irec    *irec,
+       xfs_fileoff_t           destoff,
+       xfs_off_t               new_isize)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       xfs_fsblock_t           firstfsb;
+       unsigned int            resblks;
+       struct xfs_defer_ops    dfops;
+       struct xfs_bmbt_irec    uirec;
+       bool                    real_extent;
+       xfs_filblks_t           rlen;
+       xfs_filblks_t           unmap_len;
+       xfs_off_t               newlen;
+       int                     error;
+
+       unmap_len = irec->br_startoff + irec->br_blockcount - destoff;
+       trace_xfs_reflink_punch_range(ip, destoff, unmap_len);
+
+       /* Only remap normal extents. */
+       real_extent =  (irec->br_startblock != HOLESTARTBLOCK &&
+                       irec->br_startblock != DELAYSTARTBLOCK &&
+                       !ISUNWRITTEN(irec));
+
+       /* No reflinking if we're low on space */
+       if (real_extent) {
+               error = xfs_reflink_ag_has_free_space(mp,
+                               XFS_FSB_TO_AGNO(mp, irec->br_startblock));
+               if (error)
+                       goto out;
+       }
+
+       /* Start a rolling transaction to switch the mappings */
+       resblks = XFS_EXTENTADD_SPACE_RES(ip->i_mount, XFS_DATA_FORK);
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
+       if (error)
+               goto out;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       /* If we're not just clearing space, then do we have enough quota? */
+       if (real_extent) {
+               error = xfs_trans_reserve_quota_nblks(tp, ip,
+                               irec->br_blockcount, 0, XFS_QMOPT_RES_REGBLKS);
+               if (error)
+                       goto out_cancel;
+       }
+
+       trace_xfs_reflink_remap(ip, irec->br_startoff,
+                               irec->br_blockcount, irec->br_startblock);
+
+       /* Unmap the old blocks in the data fork. */
+       rlen = unmap_len;
+       while (rlen) {
+               xfs_defer_init(&dfops, &firstfsb);
+               error = __xfs_bunmapi(tp, ip, destoff, &rlen, 0, 1,
+                               &firstfsb, &dfops);
+               if (error)
+                       goto out_defer;
+
+               /*
+                * Trim the extent to whatever got unmapped.
+                * Remember, bunmapi works backwards.
+                */
+               uirec.br_startblock = irec->br_startblock + rlen;
+               uirec.br_startoff = irec->br_startoff + rlen;
+               uirec.br_blockcount = unmap_len - rlen;
+               unmap_len = rlen;
+
+               /* If this isn't a real mapping, we're done. */
+               if (!real_extent || uirec.br_blockcount == 0)
+                       goto next_extent;
+
+               trace_xfs_reflink_remap(ip, uirec.br_startoff,
+                               uirec.br_blockcount, uirec.br_startblock);
+
+               /* Update the refcount tree */
+               error = xfs_refcount_increase_extent(mp, &dfops, &uirec);
+               if (error)
+                       goto out_defer;
+
+               /* Map the new blocks into the data fork. */
+               error = xfs_bmap_map_extent(mp, &dfops, ip, &uirec);
+               if (error)
+                       goto out_defer;
+
+               /* Update quota accounting. */
+               xfs_trans_mod_dquot_byino(tp, ip, XFS_TRANS_DQ_BCOUNT,
+                               uirec.br_blockcount);
+
+               /* Update dest isize if needed. */
+               newlen = XFS_FSB_TO_B(mp,
+                               uirec.br_startoff + uirec.br_blockcount);
+               newlen = min_t(xfs_off_t, newlen, new_isize);
+               if (newlen > i_size_read(VFS_I(ip))) {
+                       trace_xfs_reflink_update_inode_size(ip, newlen);
+                       i_size_write(VFS_I(ip), newlen);
+                       ip->i_d.di_size = newlen;
+                       xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+               }
+
+next_extent:
+               /* Process all the deferred stuff. */
+               error = xfs_defer_finish(&tp, &dfops, ip);
+               if (error)
+                       goto out_defer;
+       }
+
+       error = xfs_trans_commit(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       if (error)
+               goto out;
+       return 0;
+
+out_defer:
+       xfs_defer_cancel(&dfops);
+out_cancel:
+       xfs_trans_cancel(tp);
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_remap_extent_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Iteratively remap one file's extents (and holes) to another's.
+ */
+STATIC int
+xfs_reflink_remap_blocks(
+       struct xfs_inode        *src,
+       xfs_fileoff_t           srcoff,
+       struct xfs_inode        *dest,
+       xfs_fileoff_t           destoff,
+       xfs_filblks_t           len,
+       xfs_off_t               new_isize)
+{
+       struct xfs_bmbt_irec    imap;
+       int                     nimaps;
+       int                     error = 0;
+       xfs_filblks_t           range_len;
+
+       /* drange = (destoff, destoff + len); srange = (srcoff, srcoff + len) */
+       while (len) {
+               trace_xfs_reflink_remap_blocks_loop(src, srcoff, len,
+                               dest, destoff);
+               /* Read extent from the source file */
+               nimaps = 1;
+               xfs_ilock(src, XFS_ILOCK_EXCL);
+               error = xfs_bmapi_read(src, srcoff, len, &imap, &nimaps, 0);
+               xfs_iunlock(src, XFS_ILOCK_EXCL);
+               if (error)
+                       goto err;
+               ASSERT(nimaps == 1);
+
+               trace_xfs_reflink_remap_imap(src, srcoff, len, XFS_IO_OVERWRITE,
+                               &imap);
+
+               /* Translate imap into the destination file. */
+               range_len = imap.br_startoff + imap.br_blockcount - srcoff;
+               imap.br_startoff += destoff - srcoff;
+
+               /* Clear dest from destoff to the end of imap and map it in. */
+               error = xfs_reflink_remap_extent(dest, &imap, destoff,
+                               new_isize);
+               if (error)
+                       goto err;
+
+               if (fatal_signal_pending(current)) {
+                       error = -EINTR;
+                       goto err;
+               }
+
+               /* Advance drange/srange */
+               srcoff += range_len;
+               destoff += range_len;
+               len -= range_len;
+       }
+
+       return 0;
+
+err:
+       trace_xfs_reflink_remap_blocks_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Read a page's worth of file data into the page cache.  Return the page
+ * locked.
+ */
+static struct page *
+xfs_get_page(
+       struct inode    *inode,
+       xfs_off_t       offset)
+{
+       struct address_space    *mapping;
+       struct page             *page;
+       pgoff_t                 n;
+
+       n = offset >> PAGE_SHIFT;
+       mapping = inode->i_mapping;
+       page = read_mapping_page(mapping, n, NULL);
+       if (IS_ERR(page))
+               return page;
+       if (!PageUptodate(page)) {
+               put_page(page);
+               return ERR_PTR(-EIO);
+       }
+       lock_page(page);
+       return page;
+}
+
+/*
+ * Compare extents of two files to see if they are the same.
+ */
+static int
+xfs_compare_extents(
+       struct inode    *src,
+       xfs_off_t       srcoff,
+       struct inode    *dest,
+       xfs_off_t       destoff,
+       xfs_off_t       len,
+       bool            *is_same)
+{
+       xfs_off_t       src_poff;
+       xfs_off_t       dest_poff;
+       void            *src_addr;
+       void            *dest_addr;
+       struct page     *src_page;
+       struct page     *dest_page;
+       xfs_off_t       cmp_len;
+       bool            same;
+       int             error;
+
+       error = -EINVAL;
+       same = true;
+       while (len) {
+               src_poff = srcoff & (PAGE_SIZE - 1);
+               dest_poff = destoff & (PAGE_SIZE - 1);
+               cmp_len = min(PAGE_SIZE - src_poff,
+                             PAGE_SIZE - dest_poff);
+               cmp_len = min(cmp_len, len);
+               ASSERT(cmp_len > 0);
+
+               trace_xfs_reflink_compare_extents(XFS_I(src), srcoff, cmp_len,
+                               XFS_I(dest), destoff);
+
+               src_page = xfs_get_page(src, srcoff);
+               if (IS_ERR(src_page)) {
+                       error = PTR_ERR(src_page);
+                       goto out_error;
+               }
+               dest_page = xfs_get_page(dest, destoff);
+               if (IS_ERR(dest_page)) {
+                       error = PTR_ERR(dest_page);
+                       unlock_page(src_page);
+                       put_page(src_page);
+                       goto out_error;
+               }
+               src_addr = kmap_atomic(src_page);
+               dest_addr = kmap_atomic(dest_page);
+
+               flush_dcache_page(src_page);
+               flush_dcache_page(dest_page);
+
+               if (memcmp(src_addr + src_poff, dest_addr + dest_poff, cmp_len))
+                       same = false;
+
+               kunmap_atomic(dest_addr);
+               kunmap_atomic(src_addr);
+               unlock_page(dest_page);
+               unlock_page(src_page);
+               put_page(dest_page);
+               put_page(src_page);
+
+               if (!same)
+                       break;
+
+               srcoff += cmp_len;
+               destoff += cmp_len;
+               len -= cmp_len;
+       }
+
+       *is_same = same;
+       return 0;
+
+out_error:
+       trace_xfs_reflink_compare_extents_error(XFS_I(dest), error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Link a range of blocks from one file to another.
+ */
+int
+xfs_reflink_remap_range(
+       struct xfs_inode        *src,
+       xfs_off_t               srcoff,
+       struct xfs_inode        *dest,
+       xfs_off_t               destoff,
+       xfs_off_t               len,
+       unsigned int            flags)
+{
+       struct xfs_mount        *mp = src->i_mount;
+       xfs_fileoff_t           sfsbno, dfsbno;
+       xfs_filblks_t           fsblen;
+       int                     error;
+       xfs_extlen_t            cowextsize;
+       bool                    is_same;
+
+       if (!xfs_sb_version_hasreflink(&mp->m_sb))
+               return -EOPNOTSUPP;
+
+       if (XFS_FORCED_SHUTDOWN(mp))
+               return -EIO;
+
+       /* Don't reflink realtime inodes */
+       if (XFS_IS_REALTIME_INODE(src) || XFS_IS_REALTIME_INODE(dest))
+               return -EINVAL;
+
+       if (flags & ~XFS_REFLINK_ALL)
+               return -EINVAL;
+
+       trace_xfs_reflink_remap_range(src, srcoff, len, dest, destoff);
+
+       /* Lock both files against IO */
+       if (src->i_ino == dest->i_ino) {
+               xfs_ilock(src, XFS_IOLOCK_EXCL);
+               xfs_ilock(src, XFS_MMAPLOCK_EXCL);
+       } else {
+               xfs_lock_two_inodes(src, dest, XFS_IOLOCK_EXCL);
+               xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL);
+       }
+
+       /*
+        * Check that the extents are the same.
+        */
+       if (flags & XFS_REFLINK_DEDUPE) {
+               is_same = false;
+               error = xfs_compare_extents(VFS_I(src), srcoff, VFS_I(dest),
+                               destoff, len, &is_same);
+               if (error)
+                       goto out_error;
+               if (!is_same) {
+                       error = -EBADE;
+                       goto out_error;
+               }
+       }
+
+       error = xfs_reflink_set_inode_flag(src, dest);
+       if (error)
+               goto out_error;
+
+       /*
+        * Invalidate the page cache so that we can clear any CoW mappings
+        * in the destination file.
+        */
+       truncate_inode_pages_range(&VFS_I(dest)->i_data, destoff,
+                                  PAGE_ALIGN(destoff + len) - 1);
+
+       dfsbno = XFS_B_TO_FSBT(mp, destoff);
+       sfsbno = XFS_B_TO_FSBT(mp, srcoff);
+       fsblen = XFS_B_TO_FSB(mp, len);
+       error = xfs_reflink_remap_blocks(src, sfsbno, dest, dfsbno, fsblen,
+                       destoff + len);
+       if (error)
+               goto out_error;
+
+       /*
+        * Carry the cowextsize hint from src to dest if we're sharing the
+        * entire source file to the entire destination file, the source file
+        * has a cowextsize hint, and the destination file does not.
+        */
+       cowextsize = 0;
+       if (srcoff == 0 && len == i_size_read(VFS_I(src)) &&
+           (src->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) &&
+           destoff == 0 && len >= i_size_read(VFS_I(dest)) &&
+           !(dest->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE))
+               cowextsize = src->i_d.di_cowextsize;
+
+       error = xfs_reflink_update_dest(dest, destoff + len, cowextsize);
+       if (error)
+               goto out_error;
+
+out_error:
+       xfs_iunlock(src, XFS_MMAPLOCK_EXCL);
+       xfs_iunlock(src, XFS_IOLOCK_EXCL);
+       if (src->i_ino != dest->i_ino) {
+               xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
+               xfs_iunlock(dest, XFS_IOLOCK_EXCL);
+       }
+       if (error)
+               trace_xfs_reflink_remap_range_error(dest, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * The user wants to preemptively CoW all shared blocks in this file,
+ * which enables us to turn off the reflink flag.  Iterate all
+ * extents which are not prealloc/delalloc to see which ranges are
+ * mentioned in the refcount tree, then read those blocks into the
+ * pagecache, dirty them, fsync them back out, and then we can update
+ * the inode flag.  What happens if we run out of memory? :)
+ */
+STATIC int
+xfs_reflink_dirty_extents(
+       struct xfs_inode        *ip,
+       xfs_fileoff_t           fbno,
+       xfs_filblks_t           end,
+       xfs_off_t               isize)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           rbno;
+       xfs_extlen_t            rlen;
+       xfs_off_t               fpos;
+       xfs_off_t               flen;
+       struct xfs_bmbt_irec    map[2];
+       int                     nmaps;
+       int                     error = 0;
+
+       while (end - fbno > 0) {
+               nmaps = 1;
+               /*
+                * Look for extents in the file.  Skip holes, delalloc, or
+                * unwritten extents; they can't be reflinked.
+                */
+               error = xfs_bmapi_read(ip, fbno, end - fbno, map, &nmaps, 0);
+               if (error)
+                       goto out;
+               if (nmaps == 0)
+                       break;
+               if (map[0].br_startblock == HOLESTARTBLOCK ||
+                   map[0].br_startblock == DELAYSTARTBLOCK ||
+                   ISUNWRITTEN(&map[0]))
+                       goto next;
+
+               map[1] = map[0];
+               while (map[1].br_blockcount) {
+                       agno = XFS_FSB_TO_AGNO(mp, map[1].br_startblock);
+                       agbno = XFS_FSB_TO_AGBNO(mp, map[1].br_startblock);
+                       aglen = map[1].br_blockcount;
+
+                       error = xfs_reflink_find_shared(mp, agno, agbno, aglen,
+                                       &rbno, &rlen, true);
+                       if (error)
+                               goto out;
+                       if (rbno == NULLAGBLOCK)
+                               break;
+
+                       /* Dirty the pages */
+                       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+                       fpos = XFS_FSB_TO_B(mp, map[1].br_startoff +
+                                       (rbno - agbno));
+                       flen = XFS_FSB_TO_B(mp, rlen);
+                       if (fpos + flen > isize)
+                               flen = isize - fpos;
+                       error = iomap_file_dirty(VFS_I(ip), fpos, flen,
+                                       &xfs_iomap_ops);
+                       xfs_ilock(ip, XFS_ILOCK_EXCL);
+                       if (error)
+                               goto out;
+
+                       map[1].br_blockcount -= (rbno - agbno + rlen);
+                       map[1].br_startoff += (rbno - agbno + rlen);
+                       map[1].br_startblock += (rbno - agbno + rlen);
+               }
+
+next:
+               fbno = map[0].br_startoff + map[0].br_blockcount;
+       }
+out:
+       return error;
+}
+
+/* Clear the inode reflink flag if there are no shared extents. */
+int
+xfs_reflink_clear_inode_flag(
+       struct xfs_inode        *ip,
+       struct xfs_trans        **tpp)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           fbno;
+       xfs_filblks_t           end;
+       xfs_agnumber_t          agno;
+       xfs_agblock_t           agbno;
+       xfs_extlen_t            aglen;
+       xfs_agblock_t           rbno;
+       xfs_extlen_t            rlen;
+       struct xfs_bmbt_irec    map;
+       int                     nmaps;
+       int                     error = 0;
+
+       ASSERT(xfs_is_reflink_inode(ip));
+
+       fbno = 0;
+       end = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip)));
+       while (end - fbno > 0) {
+               nmaps = 1;
+               /*
+                * Look for extents in the file.  Skip holes, delalloc, or
+                * unwritten extents; they can't be reflinked.
+                */
+               error = xfs_bmapi_read(ip, fbno, end - fbno, &map, &nmaps, 0);
+               if (error)
+                       return error;
+               if (nmaps == 0)
+                       break;
+               if (map.br_startblock == HOLESTARTBLOCK ||
+                   map.br_startblock == DELAYSTARTBLOCK ||
+                   ISUNWRITTEN(&map))
+                       goto next;
+
+               agno = XFS_FSB_TO_AGNO(mp, map.br_startblock);
+               agbno = XFS_FSB_TO_AGBNO(mp, map.br_startblock);
+               aglen = map.br_blockcount;
+
+               error = xfs_reflink_find_shared(mp, agno, agbno, aglen,
+                               &rbno, &rlen, false);
+               if (error)
+                       return error;
+               /* Is there still a shared block here? */
+               if (rbno != NULLAGBLOCK)
+                       return 0;
+next:
+               fbno = map.br_startoff + map.br_blockcount;
+       }
+
+       /*
+        * We didn't find any shared blocks so turn off the reflink flag.
+        * First, get rid of any leftover CoW mappings.
+        */
+       error = xfs_reflink_cancel_cow_blocks(ip, tpp, 0, NULLFILEOFF);
+       if (error)
+               return error;
+
+       /* Clear the inode flag. */
+       trace_xfs_reflink_unset_inode_flag(ip);
+       ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+       xfs_inode_clear_cowblocks_tag(ip);
+       xfs_trans_ijoin(*tpp, ip, 0);
+       xfs_trans_log_inode(*tpp, ip, XFS_ILOG_CORE);
+
+       return error;
+}
+
+/*
+ * Clear the inode reflink flag if there are no shared extents and the size
+ * hasn't changed.
+ */
+STATIC int
+xfs_reflink_try_clear_inode_flag(
+       struct xfs_inode        *ip)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp;
+       int                     error = 0;
+
+       /* Start a rolling transaction to remove the mappings */
+       error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, 0, 0, 0, &tp);
+       if (error)
+               return error;
+
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       xfs_trans_ijoin(tp, ip, 0);
+
+       error = xfs_reflink_clear_inode_flag(ip, &tp);
+       if (error)
+               goto cancel;
+
+       error = xfs_trans_commit(tp);
+       if (error)
+               goto out;
+
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return 0;
+cancel:
+       xfs_trans_cancel(tp);
+out:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+       return error;
+}
+
+/*
+ * Pre-COW all shared blocks within a given byte range of a file and turn off
+ * the reflink flag if we unshare all of the file's blocks.
+ */
+int
+xfs_reflink_unshare(
+       struct xfs_inode        *ip,
+       xfs_off_t               offset,
+       xfs_off_t               len)
+{
+       struct xfs_mount        *mp = ip->i_mount;
+       xfs_fileoff_t           fbno;
+       xfs_filblks_t           end;
+       xfs_off_t               isize;
+       int                     error;
+
+       if (!xfs_is_reflink_inode(ip))
+               return 0;
+
+       trace_xfs_reflink_unshare(ip, offset, len);
+
+       inode_dio_wait(VFS_I(ip));
+
+       /* Try to CoW the selected ranges */
+       xfs_ilock(ip, XFS_ILOCK_EXCL);
+       fbno = XFS_B_TO_FSBT(mp, offset);
+       isize = i_size_read(VFS_I(ip));
+       end = XFS_B_TO_FSB(mp, offset + len);
+       error = xfs_reflink_dirty_extents(ip, fbno, end, isize);
+       if (error)
+               goto out_unlock;
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+
+       /* Wait for the IO to finish */
+       error = filemap_write_and_wait(VFS_I(ip)->i_mapping);
+       if (error)
+               goto out;
+
+       /* Turn off the reflink flag if possible. */
+       error = xfs_reflink_try_clear_inode_flag(ip);
+       if (error)
+               goto out;
+
+       return 0;
+
+out_unlock:
+       xfs_iunlock(ip, XFS_ILOCK_EXCL);
+out:
+       trace_xfs_reflink_unshare_error(ip, error, _RET_IP_);
+       return error;
+}
+
+/*
+ * Does this inode have any real CoW reservations?
+ */
+bool
+xfs_reflink_has_real_cow_blocks(
+       struct xfs_inode                *ip)
+{
+       struct xfs_bmbt_irec            irec;
+       struct xfs_ifork                *ifp;
+       struct xfs_bmbt_rec_host        *gotp;
+       xfs_extnum_t                    idx;
+
+       if (!xfs_is_reflink_inode(ip))
+               return false;
+
+       /* Go find the old extent in the CoW fork. */
+       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       gotp = xfs_iext_bno_to_ext(ifp, 0, &idx);
+       while (gotp) {
+               xfs_bmbt_get_all(gotp, &irec);
+
+               if (!isnullstartblock(irec.br_startblock))
+                       return true;
+
+               /* Roll on... */
+               idx++;
+               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
+                       break;
+               gotp = xfs_iext_get_ext(ifp, idx);
+       }
+
+       return false;
+}
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
new file mode 100644 (file)
index 0000000..5dc3c8a
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#ifndef __XFS_REFLINK_H
+#define __XFS_REFLINK_H 1
+
+extern int xfs_reflink_find_shared(struct xfs_mount *mp, xfs_agnumber_t agno,
+               xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno,
+               xfs_extlen_t *flen, bool find_maximal);
+extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip,
+               struct xfs_bmbt_irec *irec, bool *shared, bool *trimmed);
+
+extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip,
+               xfs_off_t offset, xfs_off_t count);
+extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip,
+               xfs_off_t offset, xfs_off_t count);
+extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
+               struct xfs_bmbt_irec *imap, bool *need_alloc);
+extern int xfs_reflink_trim_irec_to_next_cow(struct xfs_inode *ip,
+               xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap);
+
+extern int xfs_reflink_cancel_cow_blocks(struct xfs_inode *ip,
+               struct xfs_trans **tpp, xfs_fileoff_t offset_fsb,
+               xfs_fileoff_t end_fsb);
+extern int xfs_reflink_cancel_cow_range(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t count);
+extern int xfs_reflink_end_cow(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t count);
+extern int xfs_reflink_recover_cow(struct xfs_mount *mp);
+#define XFS_REFLINK_DEDUPE     1       /* only reflink if contents match */
+#define XFS_REFLINK_ALL                (XFS_REFLINK_DEDUPE)
+extern int xfs_reflink_remap_range(struct xfs_inode *src, xfs_off_t srcoff,
+               struct xfs_inode *dest, xfs_off_t destoff, xfs_off_t len,
+               unsigned int flags);
+extern int xfs_reflink_clear_inode_flag(struct xfs_inode *ip,
+               struct xfs_trans **tpp);
+extern int xfs_reflink_unshare(struct xfs_inode *ip, xfs_off_t offset,
+               xfs_off_t len);
+
+extern bool xfs_reflink_has_real_cow_blocks(struct xfs_inode *ip);
+
+#endif /* __XFS_REFLINK_H */
index 0432a45..73c8278 100644 (file)
@@ -441,8 +441,11 @@ xfs_rui_recover(
                                   XFS_FSB_TO_DADDR(mp, rmap->me_startblock));
                switch (rmap->me_flags & XFS_RMAP_EXTENT_TYPE_MASK) {
                case XFS_RMAP_EXTENT_MAP:
+               case XFS_RMAP_EXTENT_MAP_SHARED:
                case XFS_RMAP_EXTENT_UNMAP:
+               case XFS_RMAP_EXTENT_UNMAP_SHARED:
                case XFS_RMAP_EXTENT_CONVERT:
+               case XFS_RMAP_EXTENT_CONVERT_SHARED:
                case XFS_RMAP_EXTENT_ALLOC:
                case XFS_RMAP_EXTENT_FREE:
                        op_ok = true;
@@ -481,12 +484,21 @@ xfs_rui_recover(
                case XFS_RMAP_EXTENT_MAP:
                        type = XFS_RMAP_MAP;
                        break;
+               case XFS_RMAP_EXTENT_MAP_SHARED:
+                       type = XFS_RMAP_MAP_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_UNMAP:
                        type = XFS_RMAP_UNMAP;
                        break;
+               case XFS_RMAP_EXTENT_UNMAP_SHARED:
+                       type = XFS_RMAP_UNMAP_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_CONVERT:
                        type = XFS_RMAP_CONVERT;
                        break;
+               case XFS_RMAP_EXTENT_CONVERT_SHARED:
+                       type = XFS_RMAP_CONVERT_SHARED;
+                       break;
                case XFS_RMAP_EXTENT_ALLOC:
                        type = XFS_RMAP_ALLOC;
                        break;
index 6e812fe..12d48cd 100644 (file)
@@ -62,6 +62,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
                { "ibt2",               XFSSTAT_END_IBT_V2              },
                { "fibt2",              XFSSTAT_END_FIBT_V2             },
                { "rmapbt",             XFSSTAT_END_RMAP_V2             },
+               { "refcntbt",           XFSSTAT_END_REFCOUNT            },
                /* we print both series of quota information together */
                { "qm",                 XFSSTAT_END_QM                  },
        };
index 657865f..79ad2e6 100644 (file)
@@ -213,7 +213,23 @@ struct xfsstats {
        __uint32_t              xs_rmap_2_alloc;
        __uint32_t              xs_rmap_2_free;
        __uint32_t              xs_rmap_2_moves;
-#define XFSSTAT_END_XQMSTAT            (XFSSTAT_END_RMAP_V2+6)
+#define XFSSTAT_END_REFCOUNT           (XFSSTAT_END_RMAP_V2 + 15)
+       __uint32_t              xs_refcbt_2_lookup;
+       __uint32_t              xs_refcbt_2_compare;
+       __uint32_t              xs_refcbt_2_insrec;
+       __uint32_t              xs_refcbt_2_delrec;
+       __uint32_t              xs_refcbt_2_newroot;
+       __uint32_t              xs_refcbt_2_killroot;
+       __uint32_t              xs_refcbt_2_increment;
+       __uint32_t              xs_refcbt_2_decrement;
+       __uint32_t              xs_refcbt_2_lshift;
+       __uint32_t              xs_refcbt_2_rshift;
+       __uint32_t              xs_refcbt_2_split;
+       __uint32_t              xs_refcbt_2_join;
+       __uint32_t              xs_refcbt_2_alloc;
+       __uint32_t              xs_refcbt_2_free;
+       __uint32_t              xs_refcbt_2_moves;
+#define XFSSTAT_END_XQMSTAT            (XFSSTAT_END_REFCOUNT + 6)
        __uint32_t              xs_qm_dqreclaims;
        __uint32_t              xs_qm_dqreclaim_misses;
        __uint32_t              xs_qm_dquot_dups;
index 2d092f9..ade4691 100644 (file)
@@ -47,6 +47,9 @@
 #include "xfs_sysfs.h"
 #include "xfs_ondisk.h"
 #include "xfs_rmap_item.h"
+#include "xfs_refcount_item.h"
+#include "xfs_bmap_item.h"
+#include "xfs_reflink.h"
 
 #include <linux/namei.h>
 #include <linux/init.h>
@@ -936,6 +939,7 @@ xfs_fs_destroy_inode(
        struct inode            *inode)
 {
        struct xfs_inode        *ip = XFS_I(inode);
+       int                     error;
 
        trace_xfs_destroy_inode(ip);
 
@@ -943,6 +947,14 @@ xfs_fs_destroy_inode(
        XFS_STATS_INC(ip->i_mount, vn_rele);
        XFS_STATS_INC(ip->i_mount, vn_remove);
 
+       if (xfs_is_reflink_inode(ip)) {
+               error = xfs_reflink_cancel_cow_range(ip, 0, NULLFILEOFF);
+               if (error && !XFS_FORCED_SHUTDOWN(ip->i_mount))
+                       xfs_warn(ip->i_mount,
+"Error %d while evicting CoW blocks for inode %llu.",
+                                       error, ip->i_ino);
+       }
+
        xfs_inactive(ip);
 
        ASSERT(XFS_FORCED_SHUTDOWN(ip->i_mount) || ip->i_delayed_blks == 0);
@@ -1006,6 +1018,16 @@ xfs_fs_drop_inode(
 {
        struct xfs_inode        *ip = XFS_I(inode);
 
+       /*
+        * If this unlinked inode is in the middle of recovery, don't
+        * drop the inode just yet; log recovery will take care of
+        * that.  See the comment for this inode flag.
+        */
+       if (ip->i_flags & XFS_IRECOVERY) {
+               ASSERT(ip->i_mount->m_log->l_flags & XLOG_RECOVERY_NEEDED);
+               return 0;
+       }
+
        return generic_drop_inode(inode) || (ip->i_flags & XFS_IDONTCACHE);
 }
 
@@ -1296,10 +1318,31 @@ xfs_fs_remount(
                xfs_restore_resvblks(mp);
                xfs_log_work_queue(mp);
                xfs_queue_eofblocks(mp);
+
+               /* Recover any CoW blocks that never got remapped. */
+               error = xfs_reflink_recover_cow(mp);
+               if (error) {
+                       xfs_err(mp,
+       "Error %d recovering leftover CoW allocations.", error);
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       return error;
+               }
+
+               /* Create the per-AG metadata reservation pool .*/
+               error = xfs_fs_reserve_ag_blocks(mp);
+               if (error && error != -ENOSPC)
+                       return error;
        }
 
        /* rw -> ro */
        if (!(mp->m_flags & XFS_MOUNT_RDONLY) && (*flags & MS_RDONLY)) {
+               /* Free the per-AG metadata reservation pool. */
+               error = xfs_fs_unreserve_ag_blocks(mp);
+               if (error) {
+                       xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
+                       return error;
+               }
+
                /*
                 * Before we sync the metadata, we need to free up the reserve
                 * block pool so that the used block count in the superblock on
@@ -1490,6 +1533,7 @@ xfs_fs_fill_super(
        atomic_set(&mp->m_active_trans, 0);
        INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
        INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
+       INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
        mp->m_kobj.kobject.kset = xfs_kset;
 
        mp->m_super = sb;
@@ -1572,6 +1616,9 @@ xfs_fs_fill_super(
                        "DAX unsupported by block device. Turning off DAX.");
                        mp->m_flags &= ~XFS_MOUNT_DAX;
                }
+               if (xfs_sb_version_hasreflink(&mp->m_sb))
+                       xfs_alert(mp,
+               "DAX and reflink have not been tested together!");
        }
 
        if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
@@ -1585,6 +1632,10 @@ xfs_fs_fill_super(
        "EXPERIMENTAL reverse mapping btree feature enabled. Use at your own risk!");
        }
 
+       if (xfs_sb_version_hasreflink(&mp->m_sb))
+               xfs_alert(mp,
+       "EXPERIMENTAL reflink feature enabled. Use at your own risk!");
+
        error = xfs_mountfs(mp);
        if (error)
                goto out_filestream_unmount;
@@ -1788,8 +1839,38 @@ xfs_init_zones(void)
        if (!xfs_rui_zone)
                goto out_destroy_rud_zone;
 
+       xfs_cud_zone = kmem_zone_init(sizeof(struct xfs_cud_log_item),
+                       "xfs_cud_item");
+       if (!xfs_cud_zone)
+               goto out_destroy_rui_zone;
+
+       xfs_cui_zone = kmem_zone_init(
+                       xfs_cui_log_item_sizeof(XFS_CUI_MAX_FAST_EXTENTS),
+                       "xfs_cui_item");
+       if (!xfs_cui_zone)
+               goto out_destroy_cud_zone;
+
+       xfs_bud_zone = kmem_zone_init(sizeof(struct xfs_bud_log_item),
+                       "xfs_bud_item");
+       if (!xfs_bud_zone)
+               goto out_destroy_cui_zone;
+
+       xfs_bui_zone = kmem_zone_init(
+                       xfs_bui_log_item_sizeof(XFS_BUI_MAX_FAST_EXTENTS),
+                       "xfs_bui_item");
+       if (!xfs_bui_zone)
+               goto out_destroy_bud_zone;
+
        return 0;
 
+ out_destroy_bud_zone:
+       kmem_zone_destroy(xfs_bud_zone);
+ out_destroy_cui_zone:
+       kmem_zone_destroy(xfs_cui_zone);
+ out_destroy_cud_zone:
+       kmem_zone_destroy(xfs_cud_zone);
+ out_destroy_rui_zone:
+       kmem_zone_destroy(xfs_rui_zone);
  out_destroy_rud_zone:
        kmem_zone_destroy(xfs_rud_zone);
  out_destroy_icreate_zone:
@@ -1832,6 +1913,10 @@ xfs_destroy_zones(void)
         * destroy caches.
         */
        rcu_barrier();
+       kmem_zone_destroy(xfs_bui_zone);
+       kmem_zone_destroy(xfs_bud_zone);
+       kmem_zone_destroy(xfs_cui_zone);
+       kmem_zone_destroy(xfs_cud_zone);
        kmem_zone_destroy(xfs_rui_zone);
        kmem_zone_destroy(xfs_rud_zone);
        kmem_zone_destroy(xfs_icreate_zone);
@@ -1885,6 +1970,8 @@ init_xfs_fs(void)
 
        xfs_extent_free_init_defer_op();
        xfs_rmap_update_init_defer_op();
+       xfs_refcount_update_init_defer_op();
+       xfs_bmap_update_init_defer_op();
 
        xfs_dir_startup();
 
index aed74d3..afe1f66 100644 (file)
@@ -184,6 +184,15 @@ static struct ctl_table xfs_table[] = {
                .extra1         = &xfs_params.eofb_timer.min,
                .extra2         = &xfs_params.eofb_timer.max,
        },
+       {
+               .procname       = "speculative_cow_prealloc_lifetime",
+               .data           = &xfs_params.cowb_timer.val,
+               .maxlen         = sizeof(int),
+               .mode           = 0644,
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &xfs_params.cowb_timer.min,
+               .extra2         = &xfs_params.cowb_timer.max,
+       },
        /* please keep this the last entry */
 #ifdef CONFIG_PROC_FS
        {
index ffef453..984a349 100644 (file)
@@ -48,6 +48,7 @@ typedef struct xfs_param {
        xfs_sysctl_val_t inherit_nodfrg;/* Inherit the "nodefrag" inode flag. */
        xfs_sysctl_val_t fstrm_timer;   /* Filestream dir-AG assoc'n timeout. */
        xfs_sysctl_val_t eofb_timer;    /* Interval between eofb scan wakeups */
+       xfs_sysctl_val_t cowb_timer;    /* Interval between cowb scan wakeups */
 } xfs_param_t;
 
 /*
index 16093c7..ad188d3 100644 (file)
@@ -39,6 +39,7 @@ struct xfs_buf_log_format;
 struct xfs_inode_log_format;
 struct xfs_bmbt_irec;
 struct xfs_btree_cur;
+struct xfs_refcount_irec;
 
 DECLARE_EVENT_CLASS(xfs_attr_list_class,
        TP_PROTO(struct xfs_attr_list_context *ctx),
@@ -135,6 +136,8 @@ DEFINE_PERAG_REF_EVENT(xfs_perag_set_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim);
 DEFINE_PERAG_REF_EVENT(xfs_perag_set_eofblocks);
 DEFINE_PERAG_REF_EVENT(xfs_perag_clear_eofblocks);
+DEFINE_PERAG_REF_EVENT(xfs_perag_set_cowblocks);
+DEFINE_PERAG_REF_EVENT(xfs_perag_clear_cowblocks);
 
 DECLARE_EVENT_CLASS(xfs_ag_class,
        TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno),
@@ -268,10 +271,10 @@ DECLARE_EVENT_CLASS(xfs_bmap_class,
                __field(unsigned long, caller_ip)
        ),
        TP_fast_assign(
-               struct xfs_ifork        *ifp = (state & BMAP_ATTRFORK) ?
-                                               ip->i_afp : &ip->i_df;
+               struct xfs_ifork        *ifp;
                struct xfs_bmbt_irec    r;
 
+               ifp = xfs_iext_state_to_fork(ip, state);
                xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &r);
                __entry->dev = VFS_I(ip)->i_sb->s_dev;
                __entry->ino = ip->i_ino;
@@ -686,6 +689,9 @@ DEFINE_INODE_EVENT(xfs_dquot_dqdetach);
 DEFINE_INODE_EVENT(xfs_inode_set_eofblocks_tag);
 DEFINE_INODE_EVENT(xfs_inode_clear_eofblocks_tag);
 DEFINE_INODE_EVENT(xfs_inode_free_eofblocks_invalid);
+DEFINE_INODE_EVENT(xfs_inode_set_cowblocks_tag);
+DEFINE_INODE_EVENT(xfs_inode_clear_cowblocks_tag);
+DEFINE_INODE_EVENT(xfs_inode_free_cowblocks_invalid);
 
 DEFINE_INODE_EVENT(xfs_filemap_fault);
 DEFINE_INODE_EVENT(xfs_filemap_pmd_fault);
@@ -2581,10 +2587,20 @@ DEFINE_RMAPBT_EVENT(xfs_rmap_delete);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_insert_error);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_delete_error);
 DEFINE_AG_ERROR_EVENT(xfs_rmap_update_error);
+
+DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_candidate);
+DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_query);
+DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_candidate);
+DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range);
 DEFINE_RMAPBT_EVENT(xfs_rmap_lookup_le_range_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_right_neighbor_result);
 DEFINE_RMAPBT_EVENT(xfs_rmap_find_left_neighbor_result);
 
+/* deferred bmbt updates */
+#define DEFINE_BMAP_DEFERRED_EVENT     DEFINE_RMAP_DEFERRED_EVENT
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_defer);
+DEFINE_BMAP_DEFERRED_EVENT(xfs_bmap_deferred);
+
 /* per-AG reservation */
 DECLARE_EVENT_CLASS(xfs_ag_resv_class,
        TP_PROTO(struct xfs_perag *pag, enum xfs_ag_resv_type resv,
@@ -2639,6 +2655,728 @@ DEFINE_AG_RESV_EVENT(xfs_ag_resv_needed);
 DEFINE_AG_ERROR_EVENT(xfs_ag_resv_free_error);
 DEFINE_AG_ERROR_EVENT(xfs_ag_resv_init_error);
 
+/* refcount tracepoint classes */
+
+/* reuse the discard trace class for agbno/aglen-based traces */
+#define DEFINE_AG_EXTENT_EVENT(name) DEFINE_DISCARD_EVENT(name)
+
+/* ag btree lookup tracepoint class */
+#define XFS_AG_BTREE_CMP_FORMAT_STR \
+       { XFS_LOOKUP_EQ,        "eq" }, \
+       { XFS_LOOKUP_LE,        "le" }, \
+       { XFS_LOOKUP_GE,        "ge" }
+DECLARE_EVENT_CLASS(xfs_ag_btree_lookup_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                xfs_agblock_t agbno, xfs_lookup_t dir),
+       TP_ARGS(mp, agno, agbno, dir),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, agbno)
+               __field(xfs_lookup_t, dir)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->agbno = agbno;
+               __entry->dir = dir;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u cmp %s(%d)\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->agbno,
+                 __print_symbolic(__entry->dir, XFS_AG_BTREE_CMP_FORMAT_STR),
+                 __entry->dir)
+)
+
+#define DEFINE_AG_BTREE_LOOKUP_EVENT(name) \
+DEFINE_EVENT(xfs_ag_btree_lookup_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                xfs_agblock_t agbno, xfs_lookup_t dir), \
+       TP_ARGS(mp, agno, agbno, dir))
+
+/* single-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *irec),
+       TP_ARGS(mp, agno, irec),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, startblock)
+               __field(xfs_extlen_t, blockcount)
+               __field(xfs_nlink_t, refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->startblock = irec->rc_startblock;
+               __entry->blockcount = irec->rc_blockcount;
+               __entry->refcount = irec->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->startblock,
+                 __entry->blockcount,
+                 __entry->refcount)
+)
+
+#define DEFINE_REFCOUNT_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *irec), \
+       TP_ARGS(mp, agno, irec))
+
+/* single-rcext and an agbno tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_extent_at_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *irec, xfs_agblock_t agbno),
+       TP_ARGS(mp, agno, irec, agbno),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, startblock)
+               __field(xfs_extlen_t, blockcount)
+               __field(xfs_nlink_t, refcount)
+               __field(xfs_agblock_t, agbno)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->startblock = irec->rc_startblock;
+               __entry->blockcount = irec->rc_blockcount;
+               __entry->refcount = irec->rc_refcount;
+               __entry->agbno = agbno;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u @ agbno %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->startblock,
+                 __entry->blockcount,
+                 __entry->refcount,
+                 __entry->agbno)
+)
+
+#define DEFINE_REFCOUNT_EXTENT_AT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_extent_at_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *irec, xfs_agblock_t agbno), \
+       TP_ARGS(mp, agno, irec, agbno))
+
+/* double-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_double_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2),
+       TP_ARGS(mp, agno, i1, i2),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount)
+)
+
+#define DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_double_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2), \
+       TP_ARGS(mp, agno, i1, i2))
+
+/* double-rcext and an agbno tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_double_extent_at_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2,
+                xfs_agblock_t agbno),
+       TP_ARGS(mp, agno, i1, i2, agbno),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+               __field(xfs_agblock_t, agbno)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+               __entry->agbno = agbno;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u @ agbno %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount,
+                 __entry->agbno)
+)
+
+#define DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_double_extent_at_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2, \
+                xfs_agblock_t agbno), \
+       TP_ARGS(mp, agno, i1, i2, agbno))
+
+/* triple-rcext tracepoint class */
+DECLARE_EVENT_CLASS(xfs_refcount_triple_extent_class,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2,
+                struct xfs_refcount_irec *i3),
+       TP_ARGS(mp, agno, i1, i2, i3),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(xfs_agblock_t, i1_startblock)
+               __field(xfs_extlen_t, i1_blockcount)
+               __field(xfs_nlink_t, i1_refcount)
+               __field(xfs_agblock_t, i2_startblock)
+               __field(xfs_extlen_t, i2_blockcount)
+               __field(xfs_nlink_t, i2_refcount)
+               __field(xfs_agblock_t, i3_startblock)
+               __field(xfs_extlen_t, i3_blockcount)
+               __field(xfs_nlink_t, i3_refcount)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->i1_startblock = i1->rc_startblock;
+               __entry->i1_blockcount = i1->rc_blockcount;
+               __entry->i1_refcount = i1->rc_refcount;
+               __entry->i2_startblock = i2->rc_startblock;
+               __entry->i2_blockcount = i2->rc_blockcount;
+               __entry->i2_refcount = i2->rc_refcount;
+               __entry->i3_startblock = i3->rc_startblock;
+               __entry->i3_blockcount = i3->rc_blockcount;
+               __entry->i3_refcount = i3->rc_refcount;
+       ),
+       TP_printk("dev %d:%d agno %u agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u -- "
+                 "agbno %u len %u refcount %u\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->agno,
+                 __entry->i1_startblock,
+                 __entry->i1_blockcount,
+                 __entry->i1_refcount,
+                 __entry->i2_startblock,
+                 __entry->i2_blockcount,
+                 __entry->i2_refcount,
+                 __entry->i3_startblock,
+                 __entry->i3_blockcount,
+                 __entry->i3_refcount)
+);
+
+#define DEFINE_REFCOUNT_TRIPLE_EXTENT_EVENT(name) \
+DEFINE_EVENT(xfs_refcount_triple_extent_class, name, \
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, \
+                struct xfs_refcount_irec *i1, struct xfs_refcount_irec *i2, \
+                struct xfs_refcount_irec *i3), \
+       TP_ARGS(mp, agno, i1, i2, i3))
+
+/* refcount btree tracepoints */
+DEFINE_BUSY_EVENT(xfs_refcountbt_alloc_block);
+DEFINE_BUSY_EVENT(xfs_refcountbt_free_block);
+DEFINE_AG_BTREE_LOOKUP_EVENT(xfs_refcount_lookup);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_get);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_update);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_insert);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_delete);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_insert_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_delete_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_update_error);
+
+/* refcount adjustment tracepoints */
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_increase);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_decrease);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_cow_increase);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_cow_decrease);
+DEFINE_REFCOUNT_TRIPLE_EXTENT_EVENT(xfs_refcount_merge_center_extents);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_modify_extent);
+DEFINE_REFCOUNT_EXTENT_EVENT(xfs_refcount_recover_extent);
+DEFINE_REFCOUNT_EXTENT_AT_EVENT(xfs_refcount_split_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(xfs_refcount_merge_left_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_EVENT(xfs_refcount_merge_right_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(xfs_refcount_find_left_extent);
+DEFINE_REFCOUNT_DOUBLE_EXTENT_AT_EVENT(xfs_refcount_find_right_extent);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_adjust_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_adjust_cow_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_center_extents_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_modify_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_split_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_left_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_merge_right_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_left_extent_error);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_right_extent_error);
+
+/* reflink helpers */
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_find_shared);
+DEFINE_AG_EXTENT_EVENT(xfs_refcount_find_shared_result);
+DEFINE_AG_ERROR_EVENT(xfs_refcount_find_shared_error);
+#define DEFINE_REFCOUNT_DEFERRED_EVENT DEFINE_PHYS_EXTENT_DEFERRED_EVENT
+DEFINE_REFCOUNT_DEFERRED_EVENT(xfs_refcount_defer);
+DEFINE_REFCOUNT_DEFERRED_EVENT(xfs_refcount_deferred);
+
+TRACE_EVENT(xfs_refcount_finish_one_leftover,
+       TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno,
+                int type, xfs_agblock_t agbno, xfs_extlen_t len,
+                xfs_agblock_t new_agbno, xfs_extlen_t new_len),
+       TP_ARGS(mp, agno, type, agbno, len, new_agbno, new_len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_agnumber_t, agno)
+               __field(int, type)
+               __field(xfs_agblock_t, agbno)
+               __field(xfs_extlen_t, len)
+               __field(xfs_agblock_t, new_agbno)
+               __field(xfs_extlen_t, new_len)
+       ),
+       TP_fast_assign(
+               __entry->dev = mp->m_super->s_dev;
+               __entry->agno = agno;
+               __entry->type = type;
+               __entry->agbno = agbno;
+               __entry->len = len;
+               __entry->new_agbno = new_agbno;
+               __entry->new_len = new_len;
+       ),
+       TP_printk("dev %d:%d type %d agno %u agbno %u len %u new_agbno %u new_len %u",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->type,
+                 __entry->agno,
+                 __entry->agbno,
+                 __entry->len,
+                 __entry->new_agbno,
+                 __entry->new_len)
+);
+
+/* simple inode-based error/%ip tracepoint class */
+DECLARE_EVENT_CLASS(xfs_inode_error_class,
+       TP_PROTO(struct xfs_inode *ip, int error, unsigned long caller_ip),
+       TP_ARGS(ip, error, caller_ip),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(int, error)
+               __field(unsigned long, caller_ip)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->error = error;
+               __entry->caller_ip = caller_ip;
+       ),
+       TP_printk("dev %d:%d ino %llx error %d caller %ps",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->error,
+                 (char *)__entry->caller_ip)
+);
+
+#define DEFINE_INODE_ERROR_EVENT(name) \
+DEFINE_EVENT(xfs_inode_error_class, name, \
+       TP_PROTO(struct xfs_inode *ip, int error, \
+                unsigned long caller_ip), \
+       TP_ARGS(ip, error, caller_ip))
+
+/* reflink allocator */
+TRACE_EVENT(xfs_bmap_remap_alloc,
+       TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t fsbno,
+                xfs_extlen_t len),
+       TP_ARGS(ip, fsbno, len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fsblock_t, fsbno)
+               __field(xfs_extlen_t, len)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->fsbno = fsbno;
+               __entry->len = len;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx fsbno 0x%llx len %x",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->fsbno,
+                 __entry->len)
+);
+DEFINE_INODE_ERROR_EVENT(xfs_bmap_remap_alloc_error);
+
+/* reflink tracepoint classes */
+
+/* two-file io tracepoint class */
+DECLARE_EVENT_CLASS(xfs_double_io_class,
+       TP_PROTO(struct xfs_inode *src, xfs_off_t soffset, xfs_off_t len,
+                struct xfs_inode *dest, xfs_off_t doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, src_ino)
+               __field(loff_t, src_isize)
+               __field(loff_t, src_disize)
+               __field(loff_t, src_offset)
+               __field(size_t, len)
+               __field(xfs_ino_t, dest_ino)
+               __field(loff_t, dest_isize)
+               __field(loff_t, dest_disize)
+               __field(loff_t, dest_offset)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(src)->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = VFS_I(src)->i_size;
+               __entry->src_disize = src->i_d.di_size;
+               __entry->src_offset = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = VFS_I(dest)->i_size;
+               __entry->dest_disize = dest->i_d.di_size;
+               __entry->dest_offset = doffset;
+       ),
+       TP_printk("dev %d:%d count %zd "
+                 "ino 0x%llx isize 0x%llx disize 0x%llx offset 0x%llx -> "
+                 "ino 0x%llx isize 0x%llx disize 0x%llx offset 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->src_disize,
+                 __entry->src_offset,
+                 __entry->dest_ino,
+                 __entry->dest_isize,
+                 __entry->dest_disize,
+                 __entry->dest_offset)
+)
+
+#define DEFINE_DOUBLE_IO_EVENT(name)   \
+DEFINE_EVENT(xfs_double_io_class, name,        \
+       TP_PROTO(struct xfs_inode *src, xfs_off_t soffset, xfs_off_t len, \
+                struct xfs_inode *dest, xfs_off_t doffset), \
+       TP_ARGS(src, soffset, len, dest, doffset))
+
+/* two-file vfs io tracepoint class */
+DECLARE_EVENT_CLASS(xfs_double_vfs_io_class,
+       TP_PROTO(struct inode *src, u64 soffset, u64 len,
+                struct inode *dest, u64 doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned long, src_ino)
+               __field(loff_t, src_isize)
+               __field(loff_t, src_offset)
+               __field(size_t, len)
+               __field(unsigned long, dest_ino)
+               __field(loff_t, dest_isize)
+               __field(loff_t, dest_offset)
+       ),
+       TP_fast_assign(
+               __entry->dev = src->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = i_size_read(src);
+               __entry->src_offset = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = i_size_read(dest);
+               __entry->dest_offset = doffset;
+       ),
+       TP_printk("dev %d:%d count %zd "
+                 "ino 0x%lx isize 0x%llx offset 0x%llx -> "
+                 "ino 0x%lx isize 0x%llx offset 0x%llx",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->src_offset,
+                 __entry->dest_ino,
+                 __entry->dest_isize,
+                 __entry->dest_offset)
+)
+
+#define DEFINE_DOUBLE_VFS_IO_EVENT(name)       \
+DEFINE_EVENT(xfs_double_vfs_io_class, name,    \
+       TP_PROTO(struct inode *src, u64 soffset, u64 len, \
+                struct inode *dest, u64 doffset), \
+       TP_ARGS(src, soffset, len, dest, doffset))
+
+/* CoW write tracepoint */
+DECLARE_EVENT_CLASS(xfs_copy_on_write_class,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, xfs_fsblock_t pblk,
+                xfs_extlen_t len, xfs_fsblock_t new_pblk),
+       TP_ARGS(ip, lblk, pblk, len, new_pblk),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_fsblock_t, pblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, new_pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->pblk = pblk;
+               __entry->len = len;
+               __entry->new_pblk = new_pblk;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx pblk 0x%llx "
+                 "len 0x%x new_pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->pblk,
+                 __entry->len,
+                 __entry->new_pblk)
+)
+
+#define DEFINE_COW_EVENT(name) \
+DEFINE_EVENT(xfs_copy_on_write_class, name,    \
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk, xfs_fsblock_t pblk, \
+                xfs_extlen_t len, xfs_fsblock_t new_pblk), \
+       TP_ARGS(ip, lblk, pblk, len, new_pblk))
+
+/* inode/irec events */
+DECLARE_EVENT_CLASS(xfs_inode_irec_class,
+       TP_PROTO(struct xfs_inode *ip, struct xfs_bmbt_irec *irec),
+       TP_ARGS(ip, irec),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = irec->br_startoff;
+               __entry->len = irec->br_blockcount;
+               __entry->pblk = irec->br_startblock;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len,
+                 __entry->pblk)
+);
+#define DEFINE_INODE_IREC_EVENT(name) \
+DEFINE_EVENT(xfs_inode_irec_class, name, \
+       TP_PROTO(struct xfs_inode *ip, struct xfs_bmbt_irec *irec), \
+       TP_ARGS(ip, irec))
+
+/* refcount/reflink tracepoint definitions */
+
+/* reflink tracepoints */
+DEFINE_INODE_EVENT(xfs_reflink_set_inode_flag);
+DEFINE_INODE_EVENT(xfs_reflink_unset_inode_flag);
+DEFINE_ITRUNC_EVENT(xfs_reflink_update_inode_size);
+DEFINE_IOMAP_EVENT(xfs_reflink_remap_imap);
+TRACE_EVENT(xfs_reflink_remap_blocks_loop,
+       TP_PROTO(struct xfs_inode *src, xfs_fileoff_t soffset,
+                xfs_filblks_t len, struct xfs_inode *dest,
+                xfs_fileoff_t doffset),
+       TP_ARGS(src, soffset, len, dest, doffset),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, src_ino)
+               __field(xfs_fileoff_t, src_lblk)
+               __field(xfs_filblks_t, len)
+               __field(xfs_ino_t, dest_ino)
+               __field(xfs_fileoff_t, dest_lblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(src)->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_lblk = soffset;
+               __entry->len = len;
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_lblk = doffset;
+       ),
+       TP_printk("dev %d:%d len 0x%llx "
+                 "ino 0x%llx offset 0x%llx blocks -> "
+                 "ino 0x%llx offset 0x%llx blocks",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->len,
+                 __entry->src_ino,
+                 __entry->src_lblk,
+                 __entry->dest_ino,
+                 __entry->dest_lblk)
+);
+TRACE_EVENT(xfs_reflink_punch_range,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk,
+                xfs_extlen_t len),
+       TP_ARGS(ip, lblk, len),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->len = len;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len)
+);
+TRACE_EVENT(xfs_reflink_remap,
+       TP_PROTO(struct xfs_inode *ip, xfs_fileoff_t lblk,
+                xfs_extlen_t len, xfs_fsblock_t new_pblk),
+       TP_ARGS(ip, lblk, len, new_pblk),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(xfs_ino_t, ino)
+               __field(xfs_fileoff_t, lblk)
+               __field(xfs_extlen_t, len)
+               __field(xfs_fsblock_t, new_pblk)
+       ),
+       TP_fast_assign(
+               __entry->dev = VFS_I(ip)->i_sb->s_dev;
+               __entry->ino = ip->i_ino;
+               __entry->lblk = lblk;
+               __entry->len = len;
+               __entry->new_pblk = new_pblk;
+       ),
+       TP_printk("dev %d:%d ino 0x%llx lblk 0x%llx len 0x%x new_pblk %llu",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->ino,
+                 __entry->lblk,
+                 __entry->len,
+                 __entry->new_pblk)
+);
+DEFINE_DOUBLE_IO_EVENT(xfs_reflink_remap_range);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_set_inode_flag_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_update_inode_size_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_reflink_main_loop_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_read_iomap_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_blocks_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_remap_extent_error);
+
+/* dedupe tracepoints */
+DEFINE_DOUBLE_IO_EVENT(xfs_reflink_compare_extents);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_compare_extents_error);
+
+/* ioctl tracepoints */
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_reflink);
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_clone_range);
+DEFINE_DOUBLE_VFS_IO_EVENT(xfs_ioctl_file_extent_same);
+TRACE_EVENT(xfs_ioctl_clone,
+       TP_PROTO(struct inode *src, struct inode *dest),
+       TP_ARGS(src, dest),
+       TP_STRUCT__entry(
+               __field(dev_t, dev)
+               __field(unsigned long, src_ino)
+               __field(loff_t, src_isize)
+               __field(unsigned long, dest_ino)
+               __field(loff_t, dest_isize)
+       ),
+       TP_fast_assign(
+               __entry->dev = src->i_sb->s_dev;
+               __entry->src_ino = src->i_ino;
+               __entry->src_isize = i_size_read(src);
+               __entry->dest_ino = dest->i_ino;
+               __entry->dest_isize = i_size_read(dest);
+       ),
+       TP_printk("dev %d:%d "
+                 "ino 0x%lx isize 0x%llx -> "
+                 "ino 0x%lx isize 0x%llx\n",
+                 MAJOR(__entry->dev), MINOR(__entry->dev),
+                 __entry->src_ino,
+                 __entry->src_isize,
+                 __entry->dest_ino,
+                 __entry->dest_isize)
+);
+
+/* unshare tracepoints */
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_unshare);
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cow_eof_block);
+DEFINE_PAGE_EVENT(xfs_reflink_unshare_page);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_unshare_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cow_eof_block_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_dirty_page_error);
+
+/* copy on write */
+DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_around_shared);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_alloc);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_found);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_enospc);
+
+DEFINE_RW_EVENT(xfs_reflink_reserve_cow_range);
+DEFINE_RW_EVENT(xfs_reflink_allocate_cow_range);
+
+DEFINE_INODE_IREC_EVENT(xfs_reflink_bounce_dio_write);
+DEFINE_IOMAP_EVENT(xfs_reflink_find_cow_mapping);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_trim_irec);
+
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_cancel_cow_range);
+DEFINE_SIMPLE_IO_EVENT(xfs_reflink_end_cow);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cow_remap_piece);
+
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_reserve_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_allocate_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_cow_range_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_end_cow_error);
+
+DEFINE_COW_EVENT(xfs_reflink_fork_buf);
+DEFINE_COW_EVENT(xfs_reflink_finish_fork_buf);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_fork_buf_error);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_finish_fork_buf_error);
+
+DEFINE_INODE_EVENT(xfs_reflink_cancel_pending_cow);
+DEFINE_INODE_IREC_EVENT(xfs_reflink_cancel_cow);
+DEFINE_INODE_ERROR_EVENT(xfs_reflink_cancel_pending_cow_error);
+
+/* rmap swapext tracepoints */
+DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap);
+DEFINE_INODE_IREC_EVENT(xfs_swap_extent_rmap_remap_piece);
+DEFINE_INODE_ERROR_EVENT(xfs_swap_extent_rmap_error);
+
 #endif /* _TRACE_XFS_H */
 
 #undef TRACE_INCLUDE_PATH
index e2bf86a..61b7fbd 100644 (file)
@@ -36,6 +36,11 @@ struct xfs_busy_extent;
 struct xfs_rud_log_item;
 struct xfs_rui_log_item;
 struct xfs_btree_cur;
+struct xfs_cui_log_item;
+struct xfs_cud_log_item;
+struct xfs_defer_ops;
+struct xfs_bui_log_item;
+struct xfs_bud_log_item;
 
 typedef struct xfs_log_item {
        struct list_head                li_ail;         /* AIL pointers */
@@ -248,4 +253,28 @@ int xfs_trans_log_finish_rmap_update(struct xfs_trans *tp,
                xfs_fsblock_t startblock, xfs_filblks_t blockcount,
                xfs_exntst_t state, struct xfs_btree_cur **pcur);
 
+/* refcount updates */
+enum xfs_refcount_intent_type;
+
+void xfs_refcount_update_init_defer_op(void);
+struct xfs_cud_log_item *xfs_trans_get_cud(struct xfs_trans *tp,
+               struct xfs_cui_log_item *cuip);
+int xfs_trans_log_finish_refcount_update(struct xfs_trans *tp,
+               struct xfs_cud_log_item *cudp, struct xfs_defer_ops *dfops,
+               enum xfs_refcount_intent_type type, xfs_fsblock_t startblock,
+               xfs_extlen_t blockcount, xfs_fsblock_t *new_fsb,
+               xfs_extlen_t *new_len, struct xfs_btree_cur **pcur);
+
+/* mapping updates */
+enum xfs_bmap_intent_type;
+
+void xfs_bmap_update_init_defer_op(void);
+struct xfs_bud_log_item *xfs_trans_get_bud(struct xfs_trans *tp,
+               struct xfs_bui_log_item *buip);
+int xfs_trans_log_finish_bmap_update(struct xfs_trans *tp,
+               struct xfs_bud_log_item *rudp, struct xfs_defer_ops *dfops,
+               enum xfs_bmap_intent_type type, struct xfs_inode *ip,
+               int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
+               xfs_filblks_t blockcount, xfs_exntst_t state);
+
 #endif /* __XFS_TRANS_H__ */
diff --git a/fs/xfs/xfs_trans_bmap.c b/fs/xfs/xfs_trans_bmap.c
new file mode 100644 (file)
index 0000000..6408e7d
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_bmap_item.h"
+#include "xfs_alloc.h"
+#include "xfs_bmap.h"
+#include "xfs_inode.h"
+
+/*
+ * This routine is called to allocate a "bmap update done"
+ * log item.
+ */
+struct xfs_bud_log_item *
+xfs_trans_get_bud(
+       struct xfs_trans                *tp,
+       struct xfs_bui_log_item         *buip)
+{
+       struct xfs_bud_log_item         *budp;
+
+       budp = xfs_bud_init(tp->t_mountp, buip);
+       xfs_trans_add_item(tp, &budp->bud_item);
+       return budp;
+}
+
+/*
+ * Finish an bmap update and log it to the BUD. Note that the
+ * transaction is marked dirty regardless of whether the bmap update
+ * succeeds or fails to support the BUI/BUD lifecycle rules.
+ */
+int
+xfs_trans_log_finish_bmap_update(
+       struct xfs_trans                *tp,
+       struct xfs_bud_log_item         *budp,
+       struct xfs_defer_ops            *dop,
+       enum xfs_bmap_intent_type       type,
+       struct xfs_inode                *ip,
+       int                             whichfork,
+       xfs_fileoff_t                   startoff,
+       xfs_fsblock_t                   startblock,
+       xfs_filblks_t                   blockcount,
+       xfs_exntst_t                    state)
+{
+       int                             error;
+
+       error = xfs_bmap_finish_one(tp, dop, ip, type, whichfork, startoff,
+                       startblock, blockcount, state);
+
+       /*
+        * Mark the transaction dirty, even on error. This ensures the
+        * transaction is aborted, which:
+        *
+        * 1.) releases the BUI and frees the BUD
+        * 2.) shuts down the filesystem
+        */
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       budp->bud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       return error;
+}
+
+/* Sort bmap intents by inode. */
+static int
+xfs_bmap_update_diff_items(
+       void                            *priv,
+       struct list_head                *a,
+       struct list_head                *b)
+{
+       struct xfs_bmap_intent          *ba;
+       struct xfs_bmap_intent          *bb;
+
+       ba = container_of(a, struct xfs_bmap_intent, bi_list);
+       bb = container_of(b, struct xfs_bmap_intent, bi_list);
+       return ba->bi_owner->i_ino - bb->bi_owner->i_ino;
+}
+
+/* Get an BUI. */
+STATIC void *
+xfs_bmap_update_create_intent(
+       struct xfs_trans                *tp,
+       unsigned int                    count)
+{
+       struct xfs_bui_log_item         *buip;
+
+       ASSERT(count == XFS_BUI_MAX_FAST_EXTENTS);
+       ASSERT(tp != NULL);
+
+       buip = xfs_bui_init(tp->t_mountp);
+       ASSERT(buip != NULL);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       xfs_trans_add_item(tp, &buip->bui_item);
+       return buip;
+}
+
+/* Set the map extent flags for this mapping. */
+static void
+xfs_trans_set_bmap_flags(
+       struct xfs_map_extent           *bmap,
+       enum xfs_bmap_intent_type       type,
+       int                             whichfork,
+       xfs_exntst_t                    state)
+{
+       bmap->me_flags = 0;
+       switch (type) {
+       case XFS_BMAP_MAP:
+       case XFS_BMAP_UNMAP:
+               bmap->me_flags = type;
+               break;
+       default:
+               ASSERT(0);
+       }
+       if (state == XFS_EXT_UNWRITTEN)
+               bmap->me_flags |= XFS_BMAP_EXTENT_UNWRITTEN;
+       if (whichfork == XFS_ATTR_FORK)
+               bmap->me_flags |= XFS_BMAP_EXTENT_ATTR_FORK;
+}
+
+/* Log bmap updates in the intent item. */
+STATIC void
+xfs_bmap_update_log_item(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       struct list_head                *item)
+{
+       struct xfs_bui_log_item         *buip = intent;
+       struct xfs_bmap_intent          *bmap;
+       uint                            next_extent;
+       struct xfs_map_extent           *map;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       buip->bui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       /*
+        * atomic_inc_return gives us the value after the increment;
+        * we want to use it as an array index so we need to subtract 1 from
+        * it.
+        */
+       next_extent = atomic_inc_return(&buip->bui_next_extent) - 1;
+       ASSERT(next_extent < buip->bui_format.bui_nextents);
+       map = &buip->bui_format.bui_extents[next_extent];
+       map->me_owner = bmap->bi_owner->i_ino;
+       map->me_startblock = bmap->bi_bmap.br_startblock;
+       map->me_startoff = bmap->bi_bmap.br_startoff;
+       map->me_len = bmap->bi_bmap.br_blockcount;
+       xfs_trans_set_bmap_flags(map, bmap->bi_type, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_state);
+}
+
+/* Get an BUD so we can process all the deferred rmap updates. */
+STATIC void *
+xfs_bmap_update_create_done(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       unsigned int                    count)
+{
+       return xfs_trans_get_bud(tp, intent);
+}
+
+/* Process a deferred rmap update. */
+STATIC int
+xfs_bmap_update_finish_item(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dop,
+       struct list_head                *item,
+       void                            *done_item,
+       void                            **state)
+{
+       struct xfs_bmap_intent          *bmap;
+       int                             error;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       error = xfs_trans_log_finish_bmap_update(tp, done_item, dop,
+                       bmap->bi_type,
+                       bmap->bi_owner, bmap->bi_whichfork,
+                       bmap->bi_bmap.br_startoff,
+                       bmap->bi_bmap.br_startblock,
+                       bmap->bi_bmap.br_blockcount,
+                       bmap->bi_bmap.br_state);
+       kmem_free(bmap);
+       return error;
+}
+
+/* Abort all pending BUIs. */
+STATIC void
+xfs_bmap_update_abort_intent(
+       void                            *intent)
+{
+       xfs_bui_release(intent);
+}
+
+/* Cancel a deferred rmap update. */
+STATIC void
+xfs_bmap_update_cancel_item(
+       struct list_head                *item)
+{
+       struct xfs_bmap_intent          *bmap;
+
+       bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       kmem_free(bmap);
+}
+
+static const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+       .type           = XFS_DEFER_OPS_TYPE_BMAP,
+       .max_items      = XFS_BUI_MAX_FAST_EXTENTS,
+       .diff_items     = xfs_bmap_update_diff_items,
+       .create_intent  = xfs_bmap_update_create_intent,
+       .abort_intent   = xfs_bmap_update_abort_intent,
+       .log_item       = xfs_bmap_update_log_item,
+       .create_done    = xfs_bmap_update_create_done,
+       .finish_item    = xfs_bmap_update_finish_item,
+       .cancel_item    = xfs_bmap_update_cancel_item,
+};
+
+/* Register the deferred op type. */
+void
+xfs_bmap_update_init_defer_op(void)
+{
+       xfs_defer_init_op_type(&xfs_bmap_update_defer_type);
+}
diff --git a/fs/xfs/xfs_trans_refcount.c b/fs/xfs/xfs_trans_refcount.c
new file mode 100644 (file)
index 0000000..94c1877
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_trans.h"
+#include "xfs_trans_priv.h"
+#include "xfs_refcount_item.h"
+#include "xfs_alloc.h"
+#include "xfs_refcount.h"
+
+/*
+ * This routine is called to allocate a "refcount update done"
+ * log item.
+ */
+struct xfs_cud_log_item *
+xfs_trans_get_cud(
+       struct xfs_trans                *tp,
+       struct xfs_cui_log_item         *cuip)
+{
+       struct xfs_cud_log_item         *cudp;
+
+       cudp = xfs_cud_init(tp->t_mountp, cuip);
+       xfs_trans_add_item(tp, &cudp->cud_item);
+       return cudp;
+}
+
+/*
+ * Finish an refcount update and log it to the CUD. Note that the
+ * transaction is marked dirty regardless of whether the refcount
+ * update succeeds or fails to support the CUI/CUD lifecycle rules.
+ */
+int
+xfs_trans_log_finish_refcount_update(
+       struct xfs_trans                *tp,
+       struct xfs_cud_log_item         *cudp,
+       struct xfs_defer_ops            *dop,
+       enum xfs_refcount_intent_type   type,
+       xfs_fsblock_t                   startblock,
+       xfs_extlen_t                    blockcount,
+       xfs_fsblock_t                   *new_fsb,
+       xfs_extlen_t                    *new_len,
+       struct xfs_btree_cur            **pcur)
+{
+       int                             error;
+
+       error = xfs_refcount_finish_one(tp, dop, type, startblock,
+                       blockcount, new_fsb, new_len, pcur);
+
+       /*
+        * Mark the transaction dirty, even on error. This ensures the
+        * transaction is aborted, which:
+        *
+        * 1.) releases the CUI and frees the CUD
+        * 2.) shuts down the filesystem
+        */
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       cudp->cud_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       return error;
+}
+
+/* Sort refcount intents by AG. */
+static int
+xfs_refcount_update_diff_items(
+       void                            *priv,
+       struct list_head                *a,
+       struct list_head                *b)
+{
+       struct xfs_mount                *mp = priv;
+       struct xfs_refcount_intent      *ra;
+       struct xfs_refcount_intent      *rb;
+
+       ra = container_of(a, struct xfs_refcount_intent, ri_list);
+       rb = container_of(b, struct xfs_refcount_intent, ri_list);
+       return  XFS_FSB_TO_AGNO(mp, ra->ri_startblock) -
+               XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
+}
+
+/* Get an CUI. */
+STATIC void *
+xfs_refcount_update_create_intent(
+       struct xfs_trans                *tp,
+       unsigned int                    count)
+{
+       struct xfs_cui_log_item         *cuip;
+
+       ASSERT(tp != NULL);
+       ASSERT(count > 0);
+
+       cuip = xfs_cui_init(tp->t_mountp, count);
+       ASSERT(cuip != NULL);
+
+       /*
+        * Get a log_item_desc to point at the new item.
+        */
+       xfs_trans_add_item(tp, &cuip->cui_item);
+       return cuip;
+}
+
+/* Set the phys extent flags for this reverse mapping. */
+static void
+xfs_trans_set_refcount_flags(
+       struct xfs_phys_extent          *refc,
+       enum xfs_refcount_intent_type   type)
+{
+       refc->pe_flags = 0;
+       switch (type) {
+       case XFS_REFCOUNT_INCREASE:
+       case XFS_REFCOUNT_DECREASE:
+       case XFS_REFCOUNT_ALLOC_COW:
+       case XFS_REFCOUNT_FREE_COW:
+               refc->pe_flags |= type;
+               break;
+       default:
+               ASSERT(0);
+       }
+}
+
+/* Log refcount updates in the intent item. */
+STATIC void
+xfs_refcount_update_log_item(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       struct list_head                *item)
+{
+       struct xfs_cui_log_item         *cuip = intent;
+       struct xfs_refcount_intent      *refc;
+       uint                            next_extent;
+       struct xfs_phys_extent          *ext;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+
+       tp->t_flags |= XFS_TRANS_DIRTY;
+       cuip->cui_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+
+       /*
+        * atomic_inc_return gives us the value after the increment;
+        * we want to use it as an array index so we need to subtract 1 from
+        * it.
+        */
+       next_extent = atomic_inc_return(&cuip->cui_next_extent) - 1;
+       ASSERT(next_extent < cuip->cui_format.cui_nextents);
+       ext = &cuip->cui_format.cui_extents[next_extent];
+       ext->pe_startblock = refc->ri_startblock;
+       ext->pe_len = refc->ri_blockcount;
+       xfs_trans_set_refcount_flags(ext, refc->ri_type);
+}
+
+/* Get an CUD so we can process all the deferred refcount updates. */
+STATIC void *
+xfs_refcount_update_create_done(
+       struct xfs_trans                *tp,
+       void                            *intent,
+       unsigned int                    count)
+{
+       return xfs_trans_get_cud(tp, intent);
+}
+
+/* Process a deferred refcount update. */
+STATIC int
+xfs_refcount_update_finish_item(
+       struct xfs_trans                *tp,
+       struct xfs_defer_ops            *dop,
+       struct list_head                *item,
+       void                            *done_item,
+       void                            **state)
+{
+       struct xfs_refcount_intent      *refc;
+       xfs_fsblock_t                   new_fsb;
+       xfs_extlen_t                    new_aglen;
+       int                             error;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+       error = xfs_trans_log_finish_refcount_update(tp, done_item, dop,
+                       refc->ri_type,
+                       refc->ri_startblock,
+                       refc->ri_blockcount,
+                       &new_fsb, &new_aglen,
+                       (struct xfs_btree_cur **)state);
+       /* Did we run out of reservation?  Requeue what we didn't finish. */
+       if (!error && new_aglen > 0) {
+               ASSERT(refc->ri_type == XFS_REFCOUNT_INCREASE ||
+                      refc->ri_type == XFS_REFCOUNT_DECREASE);
+               refc->ri_startblock = new_fsb;
+               refc->ri_blockcount = new_aglen;
+               return -EAGAIN;
+       }
+       kmem_free(refc);
+       return error;
+}
+
+/* Clean up after processing deferred refcounts. */
+STATIC void
+xfs_refcount_update_finish_cleanup(
+       struct xfs_trans        *tp,
+       void                    *state,
+       int                     error)
+{
+       struct xfs_btree_cur    *rcur = state;
+
+       xfs_refcount_finish_one_cleanup(tp, rcur, error);
+}
+
+/* Abort all pending CUIs. */
+STATIC void
+xfs_refcount_update_abort_intent(
+       void                            *intent)
+{
+       xfs_cui_release(intent);
+}
+
+/* Cancel a deferred refcount update. */
+STATIC void
+xfs_refcount_update_cancel_item(
+       struct list_head                *item)
+{
+       struct xfs_refcount_intent      *refc;
+
+       refc = container_of(item, struct xfs_refcount_intent, ri_list);
+       kmem_free(refc);
+}
+
+static const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
+       .type           = XFS_DEFER_OPS_TYPE_REFCOUNT,
+       .max_items      = XFS_CUI_MAX_FAST_EXTENTS,
+       .diff_items     = xfs_refcount_update_diff_items,
+       .create_intent  = xfs_refcount_update_create_intent,
+       .abort_intent   = xfs_refcount_update_abort_intent,
+       .log_item       = xfs_refcount_update_log_item,
+       .create_done    = xfs_refcount_update_create_done,
+       .finish_item    = xfs_refcount_update_finish_item,
+       .finish_cleanup = xfs_refcount_update_finish_cleanup,
+       .cancel_item    = xfs_refcount_update_cancel_item,
+};
+
+/* Register the deferred op type. */
+void
+xfs_refcount_update_init_defer_op(void)
+{
+       xfs_defer_init_op_type(&xfs_refcount_update_defer_type);
+}
index 5a50ef8..9ead064 100644 (file)
@@ -48,12 +48,21 @@ xfs_trans_set_rmap_flags(
        case XFS_RMAP_MAP:
                rmap->me_flags |= XFS_RMAP_EXTENT_MAP;
                break;
+       case XFS_RMAP_MAP_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_MAP_SHARED;
+               break;
        case XFS_RMAP_UNMAP:
                rmap->me_flags |= XFS_RMAP_EXTENT_UNMAP;
                break;
+       case XFS_RMAP_UNMAP_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_UNMAP_SHARED;
+               break;
        case XFS_RMAP_CONVERT:
                rmap->me_flags |= XFS_RMAP_EXTENT_CONVERT;
                break;
+       case XFS_RMAP_CONVERT_SHARED:
+               rmap->me_flags |= XFS_RMAP_EXTENT_CONVERT_SHARED;
+               break;
        case XFS_RMAP_ALLOC:
                rmap->me_flags |= XFS_RMAP_EXTENT_ALLOC;
                break;
diff --git a/include/asm-generic/export.h b/include/asm-generic/export.h
new file mode 100644 (file)
index 0000000..43199a0
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef __ASM_GENERIC_EXPORT_H
+#define __ASM_GENERIC_EXPORT_H
+
+#ifndef KSYM_FUNC
+#define KSYM_FUNC(x) x
+#endif
+#ifdef CONFIG_64BIT
+#define __put .quad
+#ifndef KSYM_ALIGN
+#define KSYM_ALIGN 8
+#endif
+#ifndef KCRC_ALIGN
+#define KCRC_ALIGN 8
+#endif
+#else
+#define __put .long
+#ifndef KSYM_ALIGN
+#define KSYM_ALIGN 4
+#endif
+#ifndef KCRC_ALIGN
+#define KCRC_ALIGN 4
+#endif
+#endif
+
+#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+#define KSYM(name) _##name
+#else
+#define KSYM(name) name
+#endif
+
+/*
+ * note on .section use: @progbits vs %progbits nastiness doesn't matter,
+ * since we immediately emit into those sections anyway.
+ */
+.macro ___EXPORT_SYMBOL name,val,sec
+#ifdef CONFIG_MODULES
+       .globl KSYM(__ksymtab_\name)
+       .section ___ksymtab\sec+\name,"a"
+       .balign KSYM_ALIGN
+KSYM(__ksymtab_\name):
+       __put \val, KSYM(__kstrtab_\name)
+       .previous
+       .section __ksymtab_strings,"a"
+KSYM(__kstrtab_\name):
+#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+       .asciz "_\name"
+#else
+       .asciz "\name"
+#endif
+       .previous
+#ifdef CONFIG_MODVERSIONS
+       .section ___kcrctab\sec+\name,"a"
+       .balign KCRC_ALIGN
+KSYM(__kcrctab_\name):
+       __put KSYM(__crc_\name)
+       .weak KSYM(__crc_\name)
+       .previous
+#endif
+#endif
+.endm
+#undef __put
+
+#if defined(__KSYM_DEPS__)
+
+#define __EXPORT_SYMBOL(sym, val, sec) === __KSYM_##sym ===
+
+#elif defined(CONFIG_TRIM_UNUSED_KSYMS)
+
+#include <linux/kconfig.h>
+#include <generated/autoksyms.h>
+
+#define __EXPORT_SYMBOL(sym, val, sec)                         \
+       __cond_export_sym(sym, val, sec, config_enabled(__KSYM_##sym))
+#define __cond_export_sym(sym, val, sec, conf)                 \
+       ___cond_export_sym(sym, val, sec, conf)
+#define ___cond_export_sym(sym, val, sec, enabled)             \
+       __cond_export_sym_##enabled(sym, val, sec)
+#define __cond_export_sym_1(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
+#define __cond_export_sym_0(sym, val, sec) /* nothing */
+
+#else
+#define __EXPORT_SYMBOL(sym, val, sec) ___EXPORT_SYMBOL sym, val, sec
+#endif
+
+#define EXPORT_SYMBOL(name)                                    \
+       __EXPORT_SYMBOL(name, KSYM_FUNC(KSYM(name)),)
+#define EXPORT_SYMBOL_GPL(name)                                \
+       __EXPORT_SYMBOL(name, KSYM_FUNC(KSYM(name)), _gpl)
+#define EXPORT_DATA_SYMBOL(name)                               \
+       __EXPORT_SYMBOL(name, KSYM(name),)
+#define EXPORT_DATA_SYMBOL_GPL(name)                           \
+       __EXPORT_SYMBOL(name, KSYM(name),_gpl)
+
+#endif
diff --git a/include/asm-generic/libata-portmap.h b/include/asm-generic/libata-portmap.h
deleted file mode 100644 (file)
index cf14f2f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef __ASM_GENERIC_LIBATA_PORTMAP_H
-#define __ASM_GENERIC_LIBATA_PORTMAP_H
-
-#define ATA_PRIMARY_IRQ(dev)   14
-#define ATA_SECONDARY_IRQ(dev) 15
-
-#endif
index 4d9f233..40e8870 100644 (file)
@@ -65,6 +65,11 @@ extern void setup_per_cpu_areas(void);
 #define PER_CPU_DEF_ATTRIBUTES
 #endif
 
+#define raw_cpu_generic_read(pcp)                                      \
+({                                                                     \
+       *raw_cpu_ptr(&(pcp));                                           \
+})
+
 #define raw_cpu_generic_to_op(pcp, val, op)                            \
 do {                                                                   \
        *raw_cpu_ptr(&(pcp)) op val;                                    \
@@ -72,34 +77,39 @@ do {                                                                        \
 
 #define raw_cpu_generic_add_return(pcp, val)                           \
 ({                                                                     \
-       raw_cpu_add(pcp, val);                                          \
-       raw_cpu_read(pcp);                                              \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
+                                                                       \
+       *__p += val;                                                    \
+       *__p;                                                           \
 })
 
 #define raw_cpu_generic_xchg(pcp, nval)                                        \
 ({                                                                     \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
        typeof(pcp) __ret;                                              \
-       __ret = raw_cpu_read(pcp);                                      \
-       raw_cpu_write(pcp, nval);                                       \
+       __ret = *__p;                                                   \
+       *__p = nval;                                                    \
        __ret;                                                          \
 })
 
 #define raw_cpu_generic_cmpxchg(pcp, oval, nval)                       \
 ({                                                                     \
+       typeof(&(pcp)) __p = raw_cpu_ptr(&(pcp));                       \
        typeof(pcp) __ret;                                              \
-       __ret = raw_cpu_read(pcp);                                      \
+       __ret = *__p;                                                   \
        if (__ret == (oval))                                            \
-               raw_cpu_write(pcp, nval);                               \
+               *__p = nval;                                            \
        __ret;                                                          \
 })
 
 #define raw_cpu_generic_cmpxchg_double(pcp1, pcp2, oval1, oval2, nval1, nval2) \
 ({                                                                     \
+       typeof(&(pcp1)) __p1 = raw_cpu_ptr(&(pcp1));                    \
+       typeof(&(pcp2)) __p2 = raw_cpu_ptr(&(pcp2));                    \
        int __ret = 0;                                                  \
-       if (raw_cpu_read(pcp1) == (oval1) &&                            \
-                        raw_cpu_read(pcp2)  == (oval2)) {              \
-               raw_cpu_write(pcp1, nval1);                             \
-               raw_cpu_write(pcp2, nval2);                             \
+       if (*__p1 == (oval1) && *__p2  == (oval2)) {                    \
+               *__p1 = nval1;                                          \
+               *__p2 = nval2;                                          \
                __ret = 1;                                              \
        }                                                               \
        (__ret);                                                        \
@@ -109,7 +119,7 @@ do {                                                                        \
 ({                                                                     \
        typeof(pcp) __ret;                                              \
        preempt_disable();                                              \
-       __ret = *this_cpu_ptr(&(pcp));                                  \
+       __ret = raw_cpu_generic_read(pcp);                              \
        preempt_enable();                                               \
        __ret;                                                          \
 })
@@ -118,17 +128,17 @@ do {                                                                      \
 do {                                                                   \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       *raw_cpu_ptr(&(pcp)) op val;                                    \
+       raw_cpu_generic_to_op(pcp, val, op);                            \
        raw_local_irq_restore(__flags);                                 \
 } while (0)
 
+
 #define this_cpu_generic_add_return(pcp, val)                          \
 ({                                                                     \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       raw_cpu_add(pcp, val);                                          \
-       __ret = raw_cpu_read(pcp);                                      \
+       __ret = raw_cpu_generic_add_return(pcp, val);                   \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -138,8 +148,7 @@ do {                                                                        \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       __ret = raw_cpu_read(pcp);                                      \
-       raw_cpu_write(pcp, nval);                                       \
+       __ret = raw_cpu_generic_xchg(pcp, nval);                        \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -149,9 +158,7 @@ do {                                                                        \
        typeof(pcp) __ret;                                              \
        unsigned long __flags;                                          \
        raw_local_irq_save(__flags);                                    \
-       __ret = raw_cpu_read(pcp);                                      \
-       if (__ret == (oval))                                            \
-               raw_cpu_write(pcp, nval);                               \
+       __ret = raw_cpu_generic_cmpxchg(pcp, oval, nval);               \
        raw_local_irq_restore(__flags);                                 \
        __ret;                                                          \
 })
@@ -168,16 +175,16 @@ do {                                                                      \
 })
 
 #ifndef raw_cpu_read_1
-#define raw_cpu_read_1(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_1(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_2
-#define raw_cpu_read_2(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_2(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_4
-#define raw_cpu_read_4(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_4(pcp)            raw_cpu_generic_read(pcp)
 #endif
 #ifndef raw_cpu_read_8
-#define raw_cpu_read_8(pcp)            (*raw_cpu_ptr(&(pcp)))
+#define raw_cpu_read_8(pcp)            raw_cpu_generic_read(pcp)
 #endif
 
 #ifndef raw_cpu_write_1
index 6df9b07..cc6bb31 100644 (file)
@@ -69,10 +69,6 @@ struct exception_table_entry
        unsigned long insn, fixup;
 };
 
-/* Returns 0 if exception not found and fixup otherwise.  */
-extern unsigned long search_exception_table(unsigned long);
-
-
 /*
  * architectures with an MMU should override these two
  */
index 3e42bcd..3074796 100644 (file)
        *(.dtb.init.rodata)                                             \
        VMLINUX_SYMBOL(__dtb_end) = .;
 
-/* .data section */
+/*
+ * .data section
+ * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections generates
+ * .data.identifier which needs to be pulled in with .data, but don't want to
+ * pull in .data..stuff which has its own requirements. Same for bss.
+ */
 #define DATA_DATA                                                      \
-       *(.data)                                                        \
+       *(.data .data.[0-9a-zA-Z_]*)                                    \
        *(.ref.data)                                                    \
        *(.data..shared_aligned) /* percpu related */                   \
        MEM_KEEP(init.data)                                             \
        /* Kernel symbol table: Normal symbols */                       \
        __ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___ksymtab) = .;                  \
-               *(SORT(___ksymtab+*))                                   \
+               KEEP(*(SORT(___ksymtab+*)))                             \
                VMLINUX_SYMBOL(__stop___ksymtab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___ksymtab_gpl) = .;              \
-               *(SORT(___ksymtab_gpl+*))                               \
+               KEEP(*(SORT(___ksymtab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___ksymtab_unused) = .;           \
-               *(SORT(___ksymtab_unused+*))                            \
+               KEEP(*(SORT(___ksymtab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___ksymtab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_unused_gpl) = .;       \
-               *(SORT(___ksymtab_unused_gpl+*))                        \
+               KEEP(*(SORT(___ksymtab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___ksymtab_gpl_future) = .;       \
-               *(SORT(___ksymtab_gpl_future+*))                        \
+               KEEP(*(SORT(___ksymtab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___ksymtab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal symbols */                       \
        __kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {         \
                VMLINUX_SYMBOL(__start___kcrctab) = .;                  \
-               *(SORT(___kcrctab+*))                                   \
+               KEEP(*(SORT(___kcrctab+*)))                             \
                VMLINUX_SYMBOL(__stop___kcrctab) = .;                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {     \
                VMLINUX_SYMBOL(__start___kcrctab_gpl) = .;              \
-               *(SORT(___kcrctab_gpl+*))                               \
+               KEEP(*(SORT(___kcrctab_gpl+*)))                         \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl) = .;               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __kcrctab_unused  : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) {  \
                VMLINUX_SYMBOL(__start___kcrctab_unused) = .;           \
-               *(SORT(___kcrctab_unused+*))                            \
+               KEEP(*(SORT(___kcrctab_unused+*)))                      \
                VMLINUX_SYMBOL(__stop___kcrctab_unused) = .;            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_unused_gpl) = .;       \
-               *(SORT(___kcrctab_unused_gpl+*))                        \
+               KEEP(*(SORT(___kcrctab_unused_gpl+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_unused_gpl) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__start___kcrctab_gpl_future) = .;       \
-               *(SORT(___kcrctab_gpl_future+*))                        \
+               KEEP(*(SORT(___kcrctab_gpl_future+*)))                  \
                VMLINUX_SYMBOL(__stop___kcrctab_gpl_future) = .;        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: strings */                              \
         __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) {        \
-               *(__ksymtab_strings)                                    \
+               KEEP(*(__ksymtab_strings))                              \
        }                                                               \
                                                                        \
        /* __*init sections */                                          \
 #define SECURITY_INIT                                                  \
        .security_initcall.init : AT(ADDR(.security_initcall.init) - LOAD_OFFSET) { \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;            \
        }
 
 /* .text section. Map to function alignment to avoid address changes
- * during second ld run in second ld pass when generating System.map */
+ * during second ld run in second ld pass when generating System.map
+ * LD_DEAD_CODE_DATA_ELIMINATION option enables -ffunction-sections generates
+ * .text.identifier which needs to be pulled in with .text , but some
+ * architectures define .text.foo which is not intended to be pulled in here.
+ * Those enabling LD_DEAD_CODE_DATA_ELIMINATION must ensure they don't have
+ * conflicting section names, and must pull in .text.[0-9a-zA-Z_]* */
 #define TEXT_TEXT                                                      \
                ALIGN_FUNCTION();                                       \
                *(.text.hot .text .text.fixup .text.unlikely)           \
 
 /* init and exit section handling */
 #define INIT_DATA                                                      \
+       KEEP(*(SORT(___kentry+*)))                                      \
        *(.init.data)                                                   \
        MEM_DISCARD(init.data)                                          \
        KERNEL_CTORS()                                                  \
                BSS_FIRST_SECTIONS                                      \
                *(.bss..page_aligned)                                   \
                *(.dynbss)                                              \
-               *(.bss)                                                 \
+               *(.bss .bss.[0-9a-zA-Z_]*)                              \
                *(COMMON)                                               \
        }
 
 
 #define INIT_CALLS_LEVEL(level)                                                \
                VMLINUX_SYMBOL(__initcall##level##_start) = .;          \
-               *(.initcall##level##.init)                              \
-               *(.initcall##level##s.init)                             \
+               KEEP(*(.initcall##level##.init))                        \
+               KEEP(*(.initcall##level##s.init))                       \
 
 #define INIT_CALLS                                                     \
                VMLINUX_SYMBOL(__initcall_start) = .;                   \
-               *(.initcallearly.init)                                  \
+               KEEP(*(.initcallearly.init))                            \
                INIT_CALLS_LEVEL(0)                                     \
                INIT_CALLS_LEVEL(1)                                     \
                INIT_CALLS_LEVEL(2)                                     \
 
 #define CON_INITCALL                                                   \
                VMLINUX_SYMBOL(__con_initcall_start) = .;               \
-               *(.con_initcall.init)                                   \
+               KEEP(*(.con_initcall.init))                             \
                VMLINUX_SYMBOL(__con_initcall_end) = .;
 
 #define SECURITY_INITCALL                                              \
                VMLINUX_SYMBOL(__security_initcall_start) = .;          \
-               *(.security_initcall.init)                              \
+               KEEP(*(.security_initcall.init))                        \
                VMLINUX_SYMBOL(__security_initcall_end) = .;
 
 #ifdef CONFIG_BLK_DEV_INITRD
 #define INIT_RAM_FS                                                    \
        . = ALIGN(4);                                                   \
        VMLINUX_SYMBOL(__initramfs_start) = .;                          \
-       *(.init.ramfs)                                                  \
+       KEEP(*(.init.ramfs))                                            \
        . = ALIGN(8);                                                   \
-       *(.init.ramfs.info)
+       KEEP(*(.init.ramfs.info))
 #else
 #define INIT_RAM_FS
 #endif
index 261b86d..f6f0c06 100644 (file)
@@ -38,6 +38,10 @@ struct analogix_dp_plat_data {
                         struct drm_connector *);
 };
 
+int analogix_dp_psr_supported(struct device *dev);
+int analogix_dp_enable_psr(struct device *dev);
+int analogix_dp_disable_psr(struct device *dev);
+
 int analogix_dp_resume(struct device *dev);
 int analogix_dp_suspend(struct device *dev);
 
index d377865..6726440 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/platform_device.h>
 #include <linux/poll.h>
 #include <linux/ratelimit.h>
+#include <linux/rbtree.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/types.h>
@@ -127,6 +128,7 @@ struct dma_buf_attachment;
  * run-time by echoing the debug value in its sysfs node:
  *   # echo 0xf > /sys/module/drm/parameters/debug
  */
+#define DRM_UT_NONE            0x00
 #define DRM_UT_CORE            0x01
 #define DRM_UT_DRIVER          0x02
 #define DRM_UT_KMS             0x04
@@ -134,11 +136,14 @@ struct dma_buf_attachment;
 #define DRM_UT_ATOMIC          0x10
 #define DRM_UT_VBL             0x20
 
-extern __printf(2, 3)
-void drm_ut_debug_printk(const char *function_name,
-                        const char *format, ...);
-extern __printf(1, 2)
-void drm_err(const char *format, ...);
+extern __printf(6, 7)
+void drm_dev_printk(const struct device *dev, const char *level,
+                   unsigned int category, const char *function_name,
+                   const char *prefix, const char *format, ...);
+
+extern __printf(3, 4)
+void drm_printk(const char *level, unsigned int category,
+               const char *format, ...);
 
 /***********************************************************************/
 /** \name DRM template customization defaults */
@@ -146,6 +151,7 @@ void drm_err(const char *format, ...);
 
 /* driver capabilities and requirements mask */
 #define DRIVER_USE_AGP                 0x1
+#define DRIVER_LEGACY                  0x2
 #define DRIVER_PCI_DMA                 0x8
 #define DRIVER_SG                      0x10
 #define DRIVER_HAVE_DMA                        0x20
@@ -162,14 +168,37 @@ void drm_err(const char *format, ...);
 /** \name Macros to make printk easier */
 /*@{*/
 
+#define _DRM_PRINTK(once, level, fmt, ...)                             \
+       do {                                                            \
+               printk##once(KERN_##level "[" DRM_NAME "] " fmt,        \
+                            ##__VA_ARGS__);                            \
+       } while (0)
+
+#define DRM_INFO(fmt, ...)                                             \
+       _DRM_PRINTK(, INFO, fmt, ##__VA_ARGS__)
+#define DRM_NOTE(fmt, ...)                                             \
+       _DRM_PRINTK(, NOTICE, fmt, ##__VA_ARGS__)
+#define DRM_WARN(fmt, ...)                                             \
+       _DRM_PRINTK(, WARNING, fmt, ##__VA_ARGS__)
+
+#define DRM_INFO_ONCE(fmt, ...)                                                \
+       _DRM_PRINTK(_once, INFO, fmt, ##__VA_ARGS__)
+#define DRM_NOTE_ONCE(fmt, ...)                                                \
+       _DRM_PRINTK(_once, NOTICE, fmt, ##__VA_ARGS__)
+#define DRM_WARN_ONCE(fmt, ...)                                                \
+       _DRM_PRINTK(_once, WARNING, fmt, ##__VA_ARGS__)
+
 /**
  * Error output.
  *
  * \param fmt printf() like format string.
  * \param arg arguments
  */
-#define DRM_ERROR(fmt, ...)                            \
-       drm_err(fmt, ##__VA_ARGS__)
+#define DRM_DEV_ERROR(dev, fmt, ...)                                   \
+       drm_dev_printk(dev, KERN_ERR, DRM_UT_NONE, __func__, " *ERROR*",\
+                      fmt, ##__VA_ARGS__)
+#define DRM_ERROR(fmt, ...)                                            \
+       drm_printk(KERN_ERR, DRM_UT_NONE, fmt,  ##__VA_ARGS__)
 
 /**
  * Rate limited error output.  Like DRM_ERROR() but won't flood the log.
@@ -177,21 +206,30 @@ void drm_err(const char *format, ...);
  * \param fmt printf() like format string.
  * \param arg arguments
  */
-#define DRM_ERROR_RATELIMITED(fmt, ...)                                \
+#define DRM_DEV_ERROR_RATELIMITED(dev, fmt, ...)                       \
 ({                                                                     \
        static DEFINE_RATELIMIT_STATE(_rs,                              \
                                      DEFAULT_RATELIMIT_INTERVAL,       \
                                      DEFAULT_RATELIMIT_BURST);         \
                                                                        \
        if (__ratelimit(&_rs))                                          \
-               drm_err(fmt, ##__VA_ARGS__);                            \
+               DRM_DEV_ERROR(dev, fmt, ##__VA_ARGS__);                 \
 })
+#define DRM_ERROR_RATELIMITED(fmt, ...)                                        \
+       DRM_DEV_ERROR_RATELIMITED(NULL, fmt, ##__VA_ARGS__)
 
-#define DRM_INFO(fmt, ...)                             \
-       printk(KERN_INFO "[" DRM_NAME "] " fmt, ##__VA_ARGS__)
+#define DRM_DEV_INFO(dev, fmt, ...)                                    \
+       drm_dev_printk(dev, KERN_INFO, DRM_UT_NONE, __func__, "", fmt,  \
+                      ##__VA_ARGS__)
 
-#define DRM_INFO_ONCE(fmt, ...)                                \
-       printk_once(KERN_INFO "[" DRM_NAME "] " fmt, ##__VA_ARGS__)
+#define DRM_DEV_INFO_ONCE(dev, fmt, ...)                               \
+({                                                                     \
+       static bool __print_once __read_mostly;                         \
+       if (!__print_once) {                                            \
+               __print_once = true;                                    \
+               DRM_DEV_INFO(dev, fmt, ##__VA_ARGS__);                  \
+       }                                                               \
+})
 
 /**
  * Debug output.
@@ -199,37 +237,74 @@ void drm_err(const char *format, ...);
  * \param fmt printf() like format string.
  * \param arg arguments
  */
-#define DRM_DEBUG(fmt, args...)                                                \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_CORE))                  \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
+#define DRM_DEV_DEBUG(dev, fmt, args...)                               \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_CORE, __func__, "", fmt, \
+                      ##args)
+#define DRM_DEBUG(fmt, ...)                                            \
+       drm_printk(KERN_DEBUG, DRM_UT_CORE, fmt, ##__VA_ARGS__)
+
+#define DRM_DEV_DEBUG_DRIVER(dev, fmt, args...)                                \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_DRIVER, __func__, "",    \
+                      fmt, ##args)
+#define DRM_DEBUG_DRIVER(fmt, ...)                                     \
+       drm_printk(KERN_DEBUG, DRM_UT_DRIVER, fmt, ##__VA_ARGS__)
+
+#define DRM_DEV_DEBUG_KMS(dev, fmt, args...)                           \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_KMS, __func__, "", fmt,  \
+                      ##args)
+#define DRM_DEBUG_KMS(fmt, ...)                                        \
+       drm_printk(KERN_DEBUG, DRM_UT_KMS, fmt, ##__VA_ARGS__)
+
+#define DRM_DEV_DEBUG_PRIME(dev, fmt, args...)                         \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_PRIME, __func__, "",     \
+                      fmt, ##args)
+#define DRM_DEBUG_PRIME(fmt, ...)                                      \
+       drm_printk(KERN_DEBUG, DRM_UT_PRIME, fmt, ##__VA_ARGS__)
+
+#define DRM_DEV_DEBUG_ATOMIC(dev, fmt, args...)                                \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_ATOMIC, __func__, "",    \
+                      fmt, ##args)
+#define DRM_DEBUG_ATOMIC(fmt, ...)                                     \
+       drm_printk(KERN_DEBUG, DRM_UT_ATOMIC, fmt, ##__VA_ARGS__)
+
+#define DRM_DEV_DEBUG_VBL(dev, fmt, args...)                           \
+       drm_dev_printk(dev, KERN_DEBUG, DRM_UT_VBL, __func__, "", fmt,  \
+                      ##args)
+#define DRM_DEBUG_VBL(fmt, ...)                                        \
+       drm_printk(KERN_DEBUG, DRM_UT_VBL, fmt, ##__VA_ARGS__)
+
+#define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, level, fmt, args...)    \
+({                                                                     \
+       static DEFINE_RATELIMIT_STATE(_rs,                              \
+                                     DEFAULT_RATELIMIT_INTERVAL,       \
+                                     DEFAULT_RATELIMIT_BURST);         \
+       if (__ratelimit(&_rs))                                          \
+               drm_dev_printk(dev, KERN_DEBUG, DRM_UT_ ## level,       \
+                              __func__, "", fmt, ##args);              \
+})
 
-#define DRM_DEBUG_DRIVER(fmt, args...)                                 \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_DRIVER))                \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
-#define DRM_DEBUG_KMS(fmt, args...)                                    \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_KMS))                   \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
-#define DRM_DEBUG_PRIME(fmt, args...)                                  \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_PRIME))                 \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
-#define DRM_DEBUG_ATOMIC(fmt, args...)                                 \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_ATOMIC))                \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
-#define DRM_DEBUG_VBL(fmt, args...)                                    \
-       do {                                                            \
-               if (unlikely(drm_debug & DRM_UT_VBL))                   \
-                       drm_ut_debug_printk(__func__, fmt, ##args);     \
-       } while (0)
+/**
+ * Rate limited debug output. Like DRM_DEBUG() but won't flood the log.
+ *
+ * \param fmt printf() like format string.
+ * \param arg arguments
+ */
+#define DRM_DEV_DEBUG_RATELIMITED(dev, fmt, args...)                   \
+       DEV__DRM_DEFINE_DEBUG_RATELIMITED(dev, CORE, fmt, ##args)
+#define DRM_DEBUG_RATELIMITED(fmt, args...)                            \
+       DRM_DEV_DEBUG_RATELIMITED(NULL, fmt, ##args)
+#define DRM_DEV_DEBUG_DRIVER_RATELIMITED(dev, fmt, args...)            \
+       _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, DRIVER, fmt, ##args)
+#define DRM_DEBUG_DRIVER_RATELIMITED(fmt, args...)                     \
+       DRM_DEV_DEBUG_DRIVER_RATELIMITED(NULL, fmt, ##args)
+#define DRM_DEV_DEBUG_KMS_RATELIMITED(dev, fmt, args...)               \
+       _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, KMS, fmt, ##args)
+#define DRM_DEBUG_KMS_RATELIMITED(fmt, args...)                                \
+       DRM_DEV_DEBUG_KMS_RATELIMITED(NULL, fmt, ##args)
+#define DRM_DEV_DEBUG_PRIME_RATELIMITED(dev, fmt, args...)             \
+       _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, PRIME, fmt, ##args)
+#define DRM_DEBUG_PRIME_RATELIMITED(fmt, args...)                      \
+       DRM_DEV_DEBUG_PRIME_RATELIMITED(NULL, fmt, ##args)
 
 /*@}*/
 
@@ -295,10 +370,10 @@ struct drm_pending_event {
                      we deliver the event, for tracing only */
 };
 
-/* initial implementaton using a linked list - todo hashtab */
 struct drm_prime_file_private {
-       struct list_head head;
        struct mutex lock;
+       struct rb_root dmabufs;
+       struct rb_root handles;
 };
 
 /** File private data */
@@ -320,7 +395,6 @@ struct drm_file {
        unsigned is_master:1;
 
        struct pid *pid;
-       kuid_t uid;
        drm_magic_t magic;
        struct list_head lhead;
        struct drm_minor *minor;
@@ -642,7 +716,7 @@ struct drm_driver {
 };
 
 enum drm_minor_type {
-       DRM_MINOR_LEGACY,
+       DRM_MINOR_PRIMARY,
        DRM_MINOR_CONTROL,
        DRM_MINOR_RENDER,
        DRM_MINOR_CNT,
@@ -856,7 +930,7 @@ static inline bool drm_is_control_client(const struct drm_file *file_priv)
 
 static inline bool drm_is_primary_client(const struct drm_file *file_priv)
 {
-       return file_priv->minor->type == DRM_MINOR_LEGACY;
+       return file_priv->minor->type == DRM_MINOR_PRIMARY;
 }
 
 /******************************************************************/
@@ -937,8 +1011,11 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files,
 }
 #endif
 
+struct dma_buf_export_info;
+
 extern struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
-               struct drm_gem_object *obj, int flags);
+                                           struct drm_gem_object *obj,
+                                           int flags);
 extern int drm_gem_prime_handle_to_fd(struct drm_device *dev,
                struct drm_file *file_priv, uint32_t handle, uint32_t flags,
                int *prime_fd);
@@ -946,6 +1023,8 @@ extern struct drm_gem_object *drm_gem_prime_import(struct drm_device *dev,
                struct dma_buf *dma_buf);
 extern int drm_gem_prime_fd_to_handle(struct drm_device *dev,
                struct drm_file *file_priv, int prime_fd, uint32_t *handle);
+struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev,
+                                     struct dma_buf_export_info *exp_info);
 extern void drm_gem_dmabuf_release(struct dma_buf *dma_buf);
 
 extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, struct page **pages,
index 856a9c8..9701f2d 100644 (file)
 
 #include <drm/drm_crtc.h>
 
+/**
+ * struct drm_crtc_commit - track modeset commits on a CRTC
+ *
+ * This structure is used to track pending modeset changes and atomic commit on
+ * a per-CRTC basis. Since updating the list should never block this structure
+ * is reference counted to allow waiters to safely wait on an event to complete,
+ * without holding any locks.
+ *
+ * It has 3 different events in total to allow a fine-grained synchronization
+ * between outstanding updates::
+ *
+ *     atomic commit thread                    hardware
+ *
+ *     write new state into hardware   ---->   ...
+ *     signal hw_done
+ *                                             switch to new state on next
+ *     ...                                     v/hblank
+ *
+ *     wait for buffers to show up             ...
+ *
+ *     ...                                     send completion irq
+ *                                             irq handler signals flip_done
+ *     cleanup old buffers
+ *
+ *     signal cleanup_done
+ *
+ *     wait for flip_done              <----
+ *     clean up atomic state
+ *
+ * The important bit to know is that cleanup_done is the terminal event, but the
+ * ordering between flip_done and hw_done is entirely up to the specific driver
+ * and modeset state change.
+ *
+ * For an implementation of how to use this look at
+ * drm_atomic_helper_setup_commit() from the atomic helper library.
+ */
+struct drm_crtc_commit {
+       /**
+        * @crtc:
+        *
+        * DRM CRTC for this commit.
+        */
+       struct drm_crtc *crtc;
+
+       /**
+        * @ref:
+        *
+        * Reference count for this structure. Needed to allow blocking on
+        * completions without the risk of the completion disappearing
+        * meanwhile.
+        */
+       struct kref ref;
+
+       /**
+        * @flip_done:
+        *
+        * Will be signaled when the hardware has flipped to the new set of
+        * buffers. Signals at the same time as when the drm event for this
+        * commit is sent to userspace, or when an out-fence is singalled. Note
+        * that for most hardware, in most cases this happens after @hw_done is
+        * signalled.
+        */
+       struct completion flip_done;
+
+       /**
+        * @hw_done:
+        *
+        * Will be signalled when all hw register changes for this commit have
+        * been written out. Especially when disabling a pipe this can be much
+        * later than than @flip_done, since that can signal already when the
+        * screen goes black, whereas to fully shut down a pipe more register
+        * I/O is required.
+        *
+        * Note that this does not need to include separately reference-counted
+        * resources like backing storage buffer pinning, or runtime pm
+        * management.
+        */
+       struct completion hw_done;
+
+       /**
+        * @cleanup_done:
+        *
+        * Will be signalled after old buffers have been cleaned up by calling
+        * drm_atomic_helper_cleanup_planes(). Since this can only happen after
+        * a vblank wait completed it might be a bit later. This completion is
+        * useful to throttle updates and avoid hardware updates getting ahead
+        * of the buffer cleanup too much.
+        */
+       struct completion cleanup_done;
+
+       /**
+        * @commit_entry:
+        *
+        * Entry on the per-CRTC commit_list. Protected by crtc->commit_lock.
+        */
+       struct list_head commit_entry;
+
+       /**
+        * @event:
+        *
+        * &drm_pending_vblank_event pointer to clean up private events.
+        */
+       struct drm_pending_vblank_event *event;
+};
+
+struct __drm_planes_state {
+       struct drm_plane *ptr;
+       struct drm_plane_state *state;
+};
+
+struct __drm_crtcs_state {
+       struct drm_crtc *ptr;
+       struct drm_crtc_state *state;
+       struct drm_crtc_commit *commit;
+};
+
+struct __drm_connnectors_state {
+       struct drm_connector *ptr;
+       struct drm_connector_state *state;
+};
+
+/**
+ * struct drm_atomic_state - the global state object for atomic updates
+ * @dev: parent DRM device
+ * @allow_modeset: allow full modeset
+ * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics
+ * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL.
+ * @planes: pointer to array of structures with per-plane data
+ * @crtcs: pointer to array of CRTC pointers
+ * @num_connector: size of the @connectors and @connector_states arrays
+ * @connectors: pointer to array of structures with per-connector data
+ * @acquire_ctx: acquire context for this atomic modeset state update
+ */
+struct drm_atomic_state {
+       struct drm_device *dev;
+       bool allow_modeset : 1;
+       bool legacy_cursor_update : 1;
+       bool legacy_set_config : 1;
+       struct __drm_planes_state *planes;
+       struct __drm_crtcs_state *crtcs;
+       int num_connector;
+       struct __drm_connnectors_state *connectors;
+
+       struct drm_modeset_acquire_ctx *acquire_ctx;
+
+       /**
+        * @commit_work:
+        *
+        * Work item which can be used by the driver or helpers to execute the
+        * commit without blocking.
+        */
+       struct work_struct commit_work;
+};
+
 void drm_crtc_commit_put(struct drm_crtc_commit *commit);
 static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit)
 {
index d86ae5d..7ff92b0 100644 (file)
@@ -29,6 +29,8 @@
 #define DRM_ATOMIC_HELPER_H_
 
 #include <drm/drm_crtc.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_modeset_helper.h>
 
 struct drm_atomic_state;
 
@@ -43,8 +45,9 @@ int drm_atomic_helper_commit(struct drm_device *dev,
                             struct drm_atomic_state *state,
                             bool nonblock);
 
-void drm_atomic_helper_wait_for_fences(struct drm_device *dev,
-                                       struct drm_atomic_state *state);
+int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
+                                       struct drm_atomic_state *state,
+                                       bool pre_swap);
 bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev,
                                           struct drm_atomic_state *old_state,
                                           struct drm_crtc *crtc);
@@ -63,14 +66,19 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 
 int drm_atomic_helper_prepare_planes(struct drm_device *dev,
                                     struct drm_atomic_state *state);
+
+#define DRM_PLANE_COMMIT_ACTIVE_ONLY                   BIT(0)
+#define DRM_PLANE_COMMIT_NO_DISABLE_AFTER_MODESET      BIT(1)
+
 void drm_atomic_helper_commit_planes(struct drm_device *dev,
                                     struct drm_atomic_state *state,
-                                    bool active_only);
+                                    uint32_t flags);
 void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
                                      struct drm_atomic_state *old_state);
 void drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state);
-void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
-                                             bool atomic);
+void
+drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc_state *old_crtc_state,
+                                        bool atomic);
 
 void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
                                  bool stall);
diff --git a/include/drm/drm_blend.h b/include/drm/drm_blend.h
new file mode 100644 (file)
index 0000000..36baa17
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_BLEND_H__
+#define __DRM_BLEND_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+
+struct drm_device;
+struct drm_atomic_state;
+
+/*
+ * Rotation property bits. DRM_ROTATE_<degrees> rotates the image by the
+ * specified amount in degrees in counter clockwise direction. DRM_REFLECT_X and
+ * DRM_REFLECT_Y reflects the image along the specified axis prior to rotation
+ *
+ * WARNING: These defines are UABI since they're exposed in the rotation
+ * property.
+ */
+#define DRM_ROTATE_0   BIT(0)
+#define DRM_ROTATE_90  BIT(1)
+#define DRM_ROTATE_180 BIT(2)
+#define DRM_ROTATE_270 BIT(3)
+#define DRM_ROTATE_MASK (DRM_ROTATE_0   | DRM_ROTATE_90 | \
+                        DRM_ROTATE_180 | DRM_ROTATE_270)
+#define DRM_REFLECT_X  BIT(4)
+#define DRM_REFLECT_Y  BIT(5)
+#define DRM_REFLECT_MASK (DRM_REFLECT_X | DRM_REFLECT_Y)
+
+struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
+                                                      unsigned int supported_rotations);
+unsigned int drm_rotation_simplify(unsigned int rotation,
+                                  unsigned int supported_rotations);
+
+int drm_plane_create_zpos_property(struct drm_plane *plane,
+                                  unsigned int zpos,
+                                  unsigned int min, unsigned int max);
+int drm_plane_create_zpos_immutable_property(struct drm_plane *plane,
+                                            unsigned int zpos);
+int drm_atomic_normalize_zpos(struct drm_device *dev,
+                             struct drm_atomic_state *state);
+#endif
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
new file mode 100644 (file)
index 0000000..530a1d6
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_BRIDGE_H__
+#define __DRM_BRIDGE_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+#include <drm/drm_modes.h>
+
+struct drm_bridge;
+
+/**
+ * struct drm_bridge_funcs - drm_bridge control functions
+ */
+struct drm_bridge_funcs {
+       /**
+        * @attach:
+        *
+        * This callback is invoked whenever our bridge is being attached to a
+        * &drm_encoder.
+        *
+        * The attach callback is optional.
+        *
+        * RETURNS:
+        *
+        * Zero on success, error code on failure.
+        */
+       int (*attach)(struct drm_bridge *bridge);
+
+       /**
+        * @detach:
+        *
+        * This callback is invoked whenever our bridge is being detached from a
+        * &drm_encoder.
+        *
+        * The detach callback is optional.
+        */
+       void (*detach)(struct drm_bridge *bridge);
+
+       /**
+        * @mode_fixup:
+        *
+        * This callback is used to validate and adjust a mode. The paramater
+        * mode is the display mode that should be fed to the next element in
+        * the display chain, either the final &drm_connector or the next
+        * &drm_bridge. The parameter adjusted_mode is the input mode the bridge
+        * requires. It can be modified by this callback and does not need to
+        * match mode.
+        *
+        * This is the only hook that allows a bridge to reject a modeset. If
+        * this function passes all other callbacks must succeed for this
+        * configuration.
+        *
+        * The mode_fixup callback is optional.
+        *
+        * NOTE:
+        *
+        * This function is called in the check phase of atomic modesets, which
+        * can be aborted for any reason (including on userspace's request to
+        * just check whether a configuration would be possible). Drivers MUST
+        * NOT touch any persistent state (hardware or software) or data
+        * structures except the passed in @state parameter.
+        *
+        * RETURNS:
+        *
+        * True if an acceptable configuration is possible, false if the modeset
+        * operation should be rejected.
+        */
+       bool (*mode_fixup)(struct drm_bridge *bridge,
+                          const struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+       /**
+        * @disable:
+        *
+        * This callback should disable the bridge. It is called right before
+        * the preceding element in the display pipe is disabled. If the
+        * preceding element is a bridge this means it's called before that
+        * bridge's ->disable() function. If the preceding element is a
+        * &drm_encoder it's called right before the encoder's ->disable(),
+        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge can assume that the display pipe (i.e. clocks and timing
+        * signals) feeding it is still running when this callback is called.
+        *
+        * The disable callback is optional.
+        */
+       void (*disable)(struct drm_bridge *bridge);
+
+       /**
+        * @post_disable:
+        *
+        * This callback should disable the bridge. It is called right after
+        * the preceding element in the display pipe is disabled. If the
+        * preceding element is a bridge this means it's called after that
+        * bridge's ->post_disable() function. If the preceding element is a
+        * &drm_encoder it's called right after the encoder's ->disable(),
+        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge must assume that the display pipe (i.e. clocks and timing
+        * singals) feeding it is no longer running when this callback is
+        * called.
+        *
+        * The post_disable callback is optional.
+        */
+       void (*post_disable)(struct drm_bridge *bridge);
+
+       /**
+        * @mode_set:
+        *
+        * This callback should set the given mode on the bridge. It is called
+        * after the ->mode_set() callback for the preceding element in the
+        * display pipeline has been called already. The display pipe (i.e.
+        * clocks and timing signals) is off when this function is called.
+        */
+       void (*mode_set)(struct drm_bridge *bridge,
+                        struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode);
+       /**
+        * @pre_enable:
+        *
+        * This callback should enable the bridge. It is called right before
+        * the preceding element in the display pipe is enabled. If the
+        * preceding element is a bridge this means it's called before that
+        * bridge's ->pre_enable() function. If the preceding element is a
+        * &drm_encoder it's called right before the encoder's ->enable(),
+        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The display pipe (i.e. clocks and timing signals) feeding this bridge
+        * will not yet be running when this callback is called. The bridge must
+        * not enable the display link feeding the next bridge in the chain (if
+        * there is one) when this callback is called.
+        *
+        * The pre_enable callback is optional.
+        */
+       void (*pre_enable)(struct drm_bridge *bridge);
+
+       /**
+        * @enable:
+        *
+        * This callback should enable the bridge. It is called right after
+        * the preceding element in the display pipe is enabled. If the
+        * preceding element is a bridge this means it's called after that
+        * bridge's ->enable() function. If the preceding element is a
+        * &drm_encoder it's called right after the encoder's ->enable(),
+        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
+        *
+        * The bridge can assume that the display pipe (i.e. clocks and timing
+        * signals) feeding it is running when this callback is called. This
+        * callback must enable the display link feeding the next bridge in the
+        * chain if there is one.
+        *
+        * The enable callback is optional.
+        */
+       void (*enable)(struct drm_bridge *bridge);
+};
+
+/**
+ * struct drm_bridge - central DRM bridge control structure
+ * @dev: DRM device this bridge belongs to
+ * @encoder: encoder to which this bridge is connected
+ * @next: the next bridge in the encoder chain
+ * @of_node: device node pointer to the bridge
+ * @list: to keep track of all added bridges
+ * @funcs: control functions
+ * @driver_private: pointer to the bridge driver's internal context
+ */
+struct drm_bridge {
+       struct drm_device *dev;
+       struct drm_encoder *encoder;
+       struct drm_bridge *next;
+#ifdef CONFIG_OF
+       struct device_node *of_node;
+#endif
+       struct list_head list;
+
+       const struct drm_bridge_funcs *funcs;
+       void *driver_private;
+};
+
+int drm_bridge_add(struct drm_bridge *bridge);
+void drm_bridge_remove(struct drm_bridge *bridge);
+struct drm_bridge *of_drm_find_bridge(struct device_node *np);
+int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge);
+void drm_bridge_detach(struct drm_bridge *bridge);
+
+bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
+                       const struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);
+void drm_bridge_disable(struct drm_bridge *bridge);
+void drm_bridge_post_disable(struct drm_bridge *bridge);
+void drm_bridge_mode_set(struct drm_bridge *bridge,
+                       struct drm_display_mode *mode,
+                       struct drm_display_mode *adjusted_mode);
+void drm_bridge_pre_enable(struct drm_bridge *bridge);
+void drm_bridge_enable(struct drm_bridge *bridge);
+
+#endif
diff --git a/include/drm/drm_color_mgmt.h b/include/drm/drm_color_mgmt.h
new file mode 100644 (file)
index 0000000..c767238
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_COLOR_MGMT_H__
+#define __DRM_COLOR_MGMT_H__
+
+#include <linux/ctype.h>
+
+void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
+                               uint degamma_lut_size,
+                               bool has_ctm,
+                               uint gamma_lut_size);
+
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+                                int gamma_size);
+
+/**
+ * drm_color_lut_extract - clamp&round LUT entries
+ * @user_input: input value
+ * @bit_precision: number of bits the hw LUT supports
+ *
+ * Extract a degamma/gamma LUT value provided by user (in the form of
+ * &drm_color_lut entries) and round it to the precision supported by the
+ * hardware.
+ */
+static inline uint32_t drm_color_lut_extract(uint32_t user_input,
+                                            uint32_t bit_precision)
+{
+       uint32_t val = user_input;
+       uint32_t max = 0xffff >> (16 - bit_precision);
+
+       /* Round only if we're not using full precision. */
+       if (bit_precision < 16) {
+               val += 1UL << (16 - bit_precision - 1);
+               val >>= 16 - bit_precision;
+       }
+
+       return clamp_val(val, 0, max);
+}
+
+
+#endif
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
new file mode 100644 (file)
index 0000000..ac9d7d8
--- /dev/null
@@ -0,0 +1,778 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_CONNECTOR_H__
+#define __DRM_CONNECTOR_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+
+#include <uapi/drm/drm_mode.h>
+
+struct drm_device;
+
+struct drm_connector_helper_funcs;
+struct drm_device;
+struct drm_crtc;
+struct drm_encoder;
+struct drm_property;
+struct drm_property_blob;
+struct edid;
+
+enum drm_connector_force {
+       DRM_FORCE_UNSPECIFIED,
+       DRM_FORCE_OFF,
+       DRM_FORCE_ON,         /* force on analog part normally */
+       DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */
+};
+
+/**
+ * enum drm_connector_status - status for a &drm_connector
+ *
+ * This enum is used to track the connector status. There are no separate
+ * #defines for the uapi!
+ */
+enum drm_connector_status {
+       /**
+        * @connector_status_connected: The connector is definitely connected to
+        * a sink device, and can be enabled.
+        */
+       connector_status_connected = 1,
+       /**
+        * @connector_status_disconnected: The connector isn't connected to a
+        * sink device which can be autodetect. For digital outputs like DP or
+        * HDMI (which can be realiable probed) this means there's really
+        * nothing there. It is driver-dependent whether a connector with this
+        * status can be lit up or not.
+        */
+       connector_status_disconnected = 2,
+       /**
+        * @connector_status_unknown: The connector's status could not be
+        * reliably detected. This happens when probing would either cause
+        * flicker (like load-detection when the connector is in use), or when a
+        * hardware resource isn't available (like when load-detection needs a
+        * free CRTC). It should be possible to light up the connector with one
+        * of the listed fallback modes. For default configuration userspace
+        * should only try to light up connectors with unknown status when
+        * there's not connector with @connector_status_connected.
+        */
+       connector_status_unknown = 3,
+};
+
+enum subpixel_order {
+       SubPixelUnknown = 0,
+       SubPixelHorizontalRGB,
+       SubPixelHorizontalBGR,
+       SubPixelVerticalRGB,
+       SubPixelVerticalBGR,
+       SubPixelNone,
+};
+
+/**
+ * struct drm_display_info - runtime data about the connected sink
+ *
+ * Describes a given display (e.g. CRT or flat panel) and its limitations. For
+ * fixed display sinks like built-in panels there's not much difference between
+ * this and struct &drm_connector. But for sinks with a real cable this
+ * structure is meant to describe all the things at the other end of the cable.
+ *
+ * For sinks which provide an EDID this can be filled out by calling
+ * drm_add_edid_modes().
+ */
+struct drm_display_info {
+       /**
+        * @name: Name of the display.
+        */
+       char name[DRM_DISPLAY_INFO_LEN];
+
+       /**
+        * @width_mm: Physical width in mm.
+        */
+        unsigned int width_mm;
+       /**
+        * @height_mm: Physical height in mm.
+        */
+       unsigned int height_mm;
+
+       /**
+        * @pixel_clock: Maximum pixel clock supported by the sink, in units of
+        * 100Hz. This mismatches the clok in &drm_display_mode (which is in
+        * kHZ), because that's what the EDID uses as base unit.
+        */
+       unsigned int pixel_clock;
+       /**
+        * @bpc: Maximum bits per color channel. Used by HDMI and DP outputs.
+        */
+       unsigned int bpc;
+
+       /**
+        * @subpixel_order: Subpixel order of LCD panels.
+        */
+       enum subpixel_order subpixel_order;
+
+#define DRM_COLOR_FORMAT_RGB444                (1<<0)
+#define DRM_COLOR_FORMAT_YCRCB444      (1<<1)
+#define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
+
+       /**
+        * @color_formats: HDMI Color formats, selects between RGB and YCrCb
+        * modes. Used DRM_COLOR_FORMAT\_ defines, which are _not_ the same ones
+        * as used to describe the pixel format in framebuffers, and also don't
+        * match the formats in @bus_formats which are shared with v4l.
+        */
+       u32 color_formats;
+
+       /**
+        * @bus_formats: Pixel data format on the wire, somewhat redundant with
+        * @color_formats. Array of size @num_bus_formats encoded using
+        * MEDIA_BUS_FMT\_ defines shared with v4l and media drivers.
+        */
+       const u32 *bus_formats;
+       /**
+        * @num_bus_formats: Size of @bus_formats array.
+        */
+       unsigned int num_bus_formats;
+
+#define DRM_BUS_FLAG_DE_LOW            (1<<0)
+#define DRM_BUS_FLAG_DE_HIGH           (1<<1)
+/* drive data on pos. edge */
+#define DRM_BUS_FLAG_PIXDATA_POSEDGE   (1<<2)
+/* drive data on neg. edge */
+#define DRM_BUS_FLAG_PIXDATA_NEGEDGE   (1<<3)
+
+       /**
+        * @bus_flags: Additional information (like pixel signal polarity) for
+        * the pixel data on the bus, using DRM_BUS_FLAGS\_ defines.
+        */
+       u32 bus_flags;
+
+       /**
+        * @max_tmds_clock: Maximum TMDS clock rate supported by the
+        * sink in kHz. 0 means undefined.
+        */
+       int max_tmds_clock;
+
+       /**
+        * @dvi_dual: Dual-link DVI sink?
+        */
+       bool dvi_dual;
+
+       /**
+        * @edid_hdmi_dc_modes: Mask of supported hdmi deep color modes. Even
+        * more stuff redundant with @bus_formats.
+        */
+       u8 edid_hdmi_dc_modes;
+
+       /**
+        * @cea_rev: CEA revision of the HDMI sink.
+        */
+       u8 cea_rev;
+};
+
+int drm_display_info_set_bus_formats(struct drm_display_info *info,
+                                    const u32 *formats,
+                                    unsigned int num_formats);
+
+/**
+ * struct drm_connector_state - mutable connector state
+ * @connector: backpointer to the connector
+ * @best_encoder: can be used by helpers and drivers to select the encoder
+ * @state: backpointer to global drm_atomic_state
+ */
+struct drm_connector_state {
+       struct drm_connector *connector;
+
+       /**
+        * @crtc: CRTC to connect connector to, NULL if disabled.
+        *
+        * Do not change this directly, use drm_atomic_set_crtc_for_connector()
+        * instead.
+        */
+       struct drm_crtc *crtc;
+
+       struct drm_encoder *best_encoder;
+
+       struct drm_atomic_state *state;
+};
+
+/**
+ * struct drm_connector_funcs - control connectors on a given device
+ *
+ * Each CRTC may have one or more connectors attached to it.  The functions
+ * below allow the core DRM code to control connectors, enumerate available modes,
+ * etc.
+ */
+struct drm_connector_funcs {
+       /**
+        * @dpms:
+        *
+        * Legacy entry point to set the per-connector DPMS state. Legacy DPMS
+        * is exposed as a standard property on the connector, but diverted to
+        * this callback in the drm core. Note that atomic drivers don't
+        * implement the 4 level DPMS support on the connector any more, but
+        * instead only have an on/off "ACTIVE" property on the CRTC object.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_connector_dpms() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*dpms)(struct drm_connector *connector, int mode);
+
+       /**
+        * @reset:
+        *
+        * Reset connector hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        *
+        * Atomic drivers can use drm_atomic_helper_connector_reset() to reset
+        * atomic state using this hook.
+        */
+       void (*reset)(struct drm_connector *connector);
+
+       /**
+        * @detect:
+        *
+        * Check to see if anything is attached to the connector. The parameter
+        * force is set to false whilst polling, true when checking the
+        * connector due to a user request. force can be used by the driver to
+        * avoid expensive, destructive operations during automated probing.
+        *
+        * FIXME:
+        *
+        * Note that this hook is only called by the probe helper. It's not in
+        * the helper library vtable purely for historical reasons. The only DRM
+        * core entry point to probe connector state is @fill_modes.
+        *
+        * RETURNS:
+        *
+        * drm_connector_status indicating the connector's status.
+        */
+       enum drm_connector_status (*detect)(struct drm_connector *connector,
+                                           bool force);
+
+       /**
+        * @force:
+        *
+        * This function is called to update internal encoder state when the
+        * connector is forced to a certain state by userspace, either through
+        * the sysfs interfaces or on the kernel cmdline. In that case the
+        * @detect callback isn't called.
+        *
+        * FIXME:
+        *
+        * Note that this hook is only called by the probe helper. It's not in
+        * the helper library vtable purely for historical reasons. The only DRM
+        * core entry point to probe connector state is @fill_modes.
+        */
+       void (*force)(struct drm_connector *connector);
+
+       /**
+        * @fill_modes:
+        *
+        * Entry point for output detection and basic mode validation. The
+        * driver should reprobe the output if needed (e.g. when hotplug
+        * handling is unreliable), add all detected modes to connector->modes
+        * and filter out any the device can't support in any configuration. It
+        * also needs to filter out any modes wider or higher than the
+        * parameters max_width and max_height indicate.
+        *
+        * The drivers must also prune any modes no longer valid from
+        * connector->modes. Furthermore it must update connector->status and
+        * connector->edid.  If no EDID has been received for this output
+        * connector->edid must be NULL.
+        *
+        * Drivers using the probe helpers should use
+        * drm_helper_probe_single_connector_modes() or
+        * drm_helper_probe_single_connector_modes_nomerge() to implement this
+        * function.
+        *
+        * RETURNS:
+        *
+        * The number of modes detected and filled into connector->modes.
+        */
+       int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
+
+       /**
+        * @set_property:
+        *
+        * This is the legacy entry point to update a property attached to the
+        * connector.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_connector_set_property() to implement this hook.
+        *
+        * This callback is optional if the driver does not support any legacy
+        * driver-private properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*set_property)(struct drm_connector *connector, struct drm_property *property,
+                            uint64_t val);
+
+       /**
+        * @late_register:
+        *
+        * This optional hook can be used to register additional userspace
+        * interfaces attached to the connector, light backlight control, i2c,
+        * DP aux or similar interfaces. It is called late in the driver load
+        * sequence from drm_connector_register() when registering all the
+        * core drm connector interfaces. Everything added from this callback
+        * should be unregistered in the early_unregister callback.
+        *
+        * Returns:
+        *
+        * 0 on success, or a negative error code on failure.
+        */
+       int (*late_register)(struct drm_connector *connector);
+
+       /**
+        * @early_unregister:
+        *
+        * This optional hook should be used to unregister the additional
+        * userspace interfaces attached to the connector from
+        * late_register(). It is called from drm_connector_unregister(),
+        * early in the driver unload sequence to disable userspace access
+        * before data structures are torndown.
+        */
+       void (*early_unregister)(struct drm_connector *connector);
+
+       /**
+        * @destroy:
+        *
+        * Clean up connector resources. This is called at driver unload time
+        * through drm_mode_config_cleanup(). It can also be called at runtime
+        * when a connector is being hot-unplugged for drivers that support
+        * connector hotplugging (e.g. DisplayPort MST).
+        */
+       void (*destroy)(struct drm_connector *connector);
+
+       /**
+        * @atomic_duplicate_state:
+        *
+        * Duplicate the current atomic state for this connector and return it.
+        * The core and helpers guarantee that any atomic state duplicated with
+        * this hook and still owned by the caller (i.e. not transferred to the
+        * driver by calling ->atomic_commit() from struct
+        * &drm_mode_config_funcs) will be cleaned up by calling the
+        * @atomic_destroy_state hook in this structure.
+        *
+        * Atomic drivers which don't subclass struct &drm_connector_state should use
+        * drm_atomic_helper_connector_duplicate_state(). Drivers that subclass the
+        * state structure to extend it with driver-private state should use
+        * __drm_atomic_helper_connector_duplicate_state() to make sure shared state is
+        * duplicated in a consistent fashion across drivers.
+        *
+        * It is an error to call this hook before connector->state has been
+        * initialized correctly.
+        *
+        * NOTE:
+        *
+        * If the duplicate state references refcounted resources this hook must
+        * acquire a reference for each of them. The driver must release these
+        * references again in @atomic_destroy_state.
+        *
+        * RETURNS:
+        *
+        * Duplicated atomic state or NULL when the allocation failed.
+        */
+       struct drm_connector_state *(*atomic_duplicate_state)(struct drm_connector *connector);
+
+       /**
+        * @atomic_destroy_state:
+        *
+        * Destroy a state duplicated with @atomic_duplicate_state and release
+        * or unreference all resources it references
+        */
+       void (*atomic_destroy_state)(struct drm_connector *connector,
+                                    struct drm_connector_state *state);
+
+       /**
+        * @atomic_set_property:
+        *
+        * Decode a driver-private property value and store the decoded value
+        * into the passed-in state structure. Since the atomic core decodes all
+        * standardized properties (even for extensions beyond the core set of
+        * properties which might not be implemented by all drivers) this
+        * requires drivers to subclass the state structure.
+        *
+        * Such driver-private properties should really only be implemented for
+        * truly hardware/vendor specific state. Instead it is preferred to
+        * standardize atomic extension and decode the properties used to expose
+        * such an extension in the core.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_connector_set_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * NOTE:
+        *
+        * This function is called in the state assembly phase of atomic
+        * modesets, which can be aborted for any reason (including on
+        * userspace's request to just check whether a configuration would be
+        * possible). Drivers MUST NOT touch any persistent state (hardware or
+        * software) or data structures except the passed in @state parameter.
+        *
+        * Also since userspace controls in which order properties are set this
+        * function must not do any input validation (since the state update is
+        * incomplete and hence likely inconsistent). Instead any such input
+        * validation must be done in the various atomic_check callbacks.
+        *
+        * RETURNS:
+        *
+        * 0 if the property has been found, -EINVAL if the property isn't
+        * implemented by the driver (which shouldn't ever happen, the core only
+        * asks for properties attached to this connector). No other validation
+        * is allowed by the driver. The core already checks that the property
+        * value is within the range (integer, valid enum value, ...) the driver
+        * set when registering the property.
+        */
+       int (*atomic_set_property)(struct drm_connector *connector,
+                                  struct drm_connector_state *state,
+                                  struct drm_property *property,
+                                  uint64_t val);
+
+       /**
+        * @atomic_get_property:
+        *
+        * Reads out the decoded driver-private property. This is used to
+        * implement the GETCONNECTOR IOCTL.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_connector_get_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the property isn't implemented by the
+        * driver (which shouldn't ever happen, the core only asks for
+        * properties attached to this connector).
+        */
+       int (*atomic_get_property)(struct drm_connector *connector,
+                                  const struct drm_connector_state *state,
+                                  struct drm_property *property,
+                                  uint64_t *val);
+};
+
+/* mode specified on the command line */
+struct drm_cmdline_mode {
+       bool specified;
+       bool refresh_specified;
+       bool bpp_specified;
+       int xres, yres;
+       int bpp;
+       int refresh;
+       bool rb;
+       bool interlace;
+       bool cvt;
+       bool margins;
+       enum drm_connector_force force;
+};
+
+/**
+ * struct drm_connector - central DRM connector control structure
+ * @dev: parent DRM device
+ * @kdev: kernel device for sysfs attributes
+ * @attr: sysfs attributes
+ * @head: list management
+ * @base: base KMS object
+ * @name: human readable name, can be overwritten by the driver
+ * @connector_type: one of the DRM_MODE_CONNECTOR_<foo> types from drm_mode.h
+ * @connector_type_id: index into connector type enum
+ * @interlace_allowed: can this connector handle interlaced modes?
+ * @doublescan_allowed: can this connector handle doublescan?
+ * @stereo_allowed: can this connector handle stereo modes?
+ * @registered: is this connector exposed (registered) with userspace?
+ * @modes: modes available on this connector (from fill_modes() + user)
+ * @status: one of the drm_connector_status enums (connected, not, or unknown)
+ * @probed_modes: list of modes derived directly from the display
+ * @funcs: connector control functions
+ * @edid_blob_ptr: DRM property containing EDID if present
+ * @properties: property tracking for this connector
+ * @dpms: current dpms state
+ * @helper_private: mid-layer private data
+ * @cmdline_mode: mode line parsed from the kernel cmdline for this connector
+ * @force: a DRM_FORCE_<foo> state for forced mode sets
+ * @override_edid: has the EDID been overwritten through debugfs for testing?
+ * @encoder_ids: valid encoders for this connector
+ * @encoder: encoder driving this connector, if any
+ * @eld: EDID-like data, if present
+ * @latency_present: AV delay info from ELD, if found
+ * @video_latency: video latency info from ELD, if found
+ * @audio_latency: audio latency info from ELD, if found
+ * @null_edid_counter: track sinks that give us all zeros for the EDID
+ * @bad_edid_counter: track sinks that give us an EDID with invalid checksum
+ * @edid_corrupt: indicates whether the last read EDID was corrupt
+ * @debugfs_entry: debugfs directory for this connector
+ * @state: current atomic state for this connector
+ * @has_tile: is this connector connected to a tiled monitor
+ * @tile_group: tile group for the connected monitor
+ * @tile_is_single_monitor: whether the tile is one monitor housing
+ * @num_h_tile: number of horizontal tiles in the tile group
+ * @num_v_tile: number of vertical tiles in the tile group
+ * @tile_h_loc: horizontal location of this tile
+ * @tile_v_loc: vertical location of this tile
+ * @tile_h_size: horizontal size of this tile.
+ * @tile_v_size: vertical size of this tile.
+ *
+ * Each connector may be connected to one or more CRTCs, or may be clonable by
+ * another connector if they can share a CRTC.  Each connector also has a specific
+ * position in the broader display (referred to as a 'screen' though it could
+ * span multiple monitors).
+ */
+struct drm_connector {
+       struct drm_device *dev;
+       struct device *kdev;
+       struct device_attribute *attr;
+       struct list_head head;
+
+       struct drm_mode_object base;
+
+       char *name;
+
+       /**
+        * @index: Compacted connector index, which matches the position inside
+        * the mode_config.list for drivers not supporting hot-add/removing. Can
+        * be used as an array index. It is invariant over the lifetime of the
+        * connector.
+        */
+       unsigned index;
+
+       int connector_type;
+       int connector_type_id;
+       bool interlace_allowed;
+       bool doublescan_allowed;
+       bool stereo_allowed;
+       bool registered;
+       struct list_head modes; /* list of modes on this connector */
+
+       enum drm_connector_status status;
+
+       /* these are modes added by probing with DDC or the BIOS */
+       struct list_head probed_modes;
+
+       /**
+        * @display_info: Display information is filled from EDID information
+        * when a display is detected. For non hot-pluggable displays such as
+        * flat panels in embedded systems, the driver should initialize the
+        * display_info.width_mm and display_info.height_mm fields with the
+        * physical size of the display.
+        */
+       struct drm_display_info display_info;
+       const struct drm_connector_funcs *funcs;
+
+       struct drm_property_blob *edid_blob_ptr;
+       struct drm_object_properties properties;
+
+       /**
+        * @path_blob_ptr:
+        *
+        * DRM blob property data for the DP MST path property.
+        */
+       struct drm_property_blob *path_blob_ptr;
+
+       /**
+        * @tile_blob_ptr:
+        *
+        * DRM blob property data for the tile property (used mostly by DP MST).
+        * This is meant for screens which are driven through separate display
+        * pipelines represented by &drm_crtc, which might not be running with
+        * genlocked clocks. For tiled panels which are genlocked, like
+        * dual-link LVDS or dual-link DSI, the driver should try to not expose
+        * the tiling and virtualize both &drm_crtc and &drm_plane if needed.
+        */
+       struct drm_property_blob *tile_blob_ptr;
+
+/* should we poll this connector for connects and disconnects */
+/* hot plug detectable */
+#define DRM_CONNECTOR_POLL_HPD (1 << 0)
+/* poll for connections */
+#define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
+/* can cleanly poll for disconnections without flickering the screen */
+/* DACs should rarely do this without a lot of testing */
+#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
+
+       /**
+        * @polled:
+        *
+        * Connector polling mode, a combination of
+        *
+        * DRM_CONNECTOR_POLL_HPD
+        *     The connector generates hotplug events and doesn't need to be
+        *     periodically polled. The CONNECT and DISCONNECT flags must not
+        *     be set together with the HPD flag.
+        *
+        * DRM_CONNECTOR_POLL_CONNECT
+        *     Periodically poll the connector for connection.
+        *
+        * DRM_CONNECTOR_POLL_DISCONNECT
+        *     Periodically poll the connector for disconnection.
+        *
+        * Set to 0 for connectors that don't support connection status
+        * discovery.
+        */
+       uint8_t polled;
+
+       /* requested DPMS state */
+       int dpms;
+
+       const struct drm_connector_helper_funcs *helper_private;
+
+       /* forced on connector */
+       struct drm_cmdline_mode cmdline_mode;
+       enum drm_connector_force force;
+       bool override_edid;
+
+#define DRM_CONNECTOR_MAX_ENCODER 3
+       uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
+       struct drm_encoder *encoder; /* currently active encoder */
+
+#define MAX_ELD_BYTES  128
+       /* EDID bits */
+       uint8_t eld[MAX_ELD_BYTES];
+       bool latency_present[2];
+       int video_latency[2];   /* [0]: progressive, [1]: interlaced */
+       int audio_latency[2];
+       int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
+       unsigned bad_edid_counter;
+
+       /* Flag for raw EDID header corruption - used in Displayport
+        * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
+        */
+       bool edid_corrupt;
+
+       struct dentry *debugfs_entry;
+
+       struct drm_connector_state *state;
+
+       /* DisplayID bits */
+       bool has_tile;
+       struct drm_tile_group *tile_group;
+       bool tile_is_single_monitor;
+
+       uint8_t num_h_tile, num_v_tile;
+       uint8_t tile_h_loc, tile_v_loc;
+       uint16_t tile_h_size, tile_v_size;
+};
+
+#define obj_to_connector(x) container_of(x, struct drm_connector, base)
+
+int drm_connector_init(struct drm_device *dev,
+                      struct drm_connector *connector,
+                      const struct drm_connector_funcs *funcs,
+                      int connector_type);
+int drm_connector_register(struct drm_connector *connector);
+void drm_connector_unregister(struct drm_connector *connector);
+int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+                                     struct drm_encoder *encoder);
+
+void drm_connector_cleanup(struct drm_connector *connector);
+static inline unsigned drm_connector_index(struct drm_connector *connector)
+{
+       return connector->index;
+}
+
+/**
+ * drm_connector_lookup - lookup connector object
+ * @dev: DRM device
+ * @id: connector object id
+ *
+ * This function looks up the connector object specified by id
+ * add takes a reference to it.
+ */
+static inline struct drm_connector *drm_connector_lookup(struct drm_device *dev,
+               uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
+       return mo ? obj_to_connector(mo) : NULL;
+}
+
+/**
+ * drm_connector_reference - incr the connector refcnt
+ * @connector: connector
+ *
+ * This function increments the connector's refcount.
+ */
+static inline void drm_connector_reference(struct drm_connector *connector)
+{
+       drm_mode_object_reference(&connector->base);
+}
+
+/**
+ * drm_connector_unreference - unref a connector
+ * @connector: connector to unref
+ *
+ * This function decrements the connector's refcount and frees it if it drops to zero.
+ */
+static inline void drm_connector_unreference(struct drm_connector *connector)
+{
+       drm_mode_object_unreference(&connector->base);
+}
+
+const char *drm_get_connector_status_name(enum drm_connector_status status);
+const char *drm_get_subpixel_order_name(enum subpixel_order order);
+const char *drm_get_dpms_name(int val);
+const char *drm_get_dvi_i_subconnector_name(int val);
+const char *drm_get_dvi_i_select_name(int val);
+const char *drm_get_tv_subconnector_name(int val);
+const char *drm_get_tv_select_name(int val);
+
+int drm_mode_create_dvi_i_properties(struct drm_device *dev);
+int drm_mode_create_tv_properties(struct drm_device *dev,
+                                 unsigned int num_modes,
+                                 const char * const modes[]);
+int drm_mode_create_scaling_mode_property(struct drm_device *dev);
+int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
+int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
+
+int drm_mode_connector_set_path_property(struct drm_connector *connector,
+                                        const char *path);
+int drm_mode_connector_set_tile_property(struct drm_connector *connector);
+int drm_mode_connector_update_edid_property(struct drm_connector *connector,
+                                           const struct edid *edid);
+
+/**
+ * drm_for_each_connector - iterate over all connectors
+ * @connector: the loop cursor
+ * @dev: the DRM device
+ *
+ * Iterate over all connectors of @dev.
+ */
+#define drm_for_each_connector(connector, dev) \
+       for (assert_drm_connector_list_read_locked(&(dev)->mode_config),        \
+            connector = list_first_entry(&(dev)->mode_config.connector_list,   \
+                                         struct drm_connector, head);          \
+            &connector->head != (&(dev)->mode_config.connector_list);          \
+            connector = list_next_entry(connector, head))
+
+#endif
diff --git a/include/drm/drm_core.h b/include/drm/drm_core.h
deleted file mode 100644 (file)
index 4e75238..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2004 Jon Smirl <jonsmirl@gmail.com>
- *
- * 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, sub license,
- * 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, 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.
- */
-#define CORE_AUTHOR            "Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl"
-
-#define CORE_NAME              "drm"
-#define CORE_DESC              "DRM shared core routines"
-#define CORE_DATE              "20060810"
-
-#define DRM_IF_MAJOR   1
-#define DRM_IF_MINOR   4
-
-#define CORE_MAJOR     1
-#define CORE_MINOR     1
-#define CORE_PATCHLEVEL 0
index 44e0708..0aa2925 100644 (file)
 #include <uapi/drm/drm_mode.h>
 #include <uapi/drm/drm_fourcc.h>
 #include <drm/drm_modeset_lock.h>
+#include <drm/drm_rect.h>
+#include <drm/drm_mode_object.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_property.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_color_mgmt.h>
 
 struct drm_device;
 struct drm_mode_set;
-struct drm_framebuffer;
-struct drm_object_properties;
 struct drm_file;
 struct drm_clip_rect;
 struct device_node;
 struct fence;
 struct edid;
 
-struct drm_mode_object {
-       uint32_t id;
-       uint32_t type;
-       struct drm_object_properties *properties;
-       struct kref refcount;
-       void (*free_cb)(struct kref *kref);
-};
-
-#define DRM_OBJECT_MAX_PROPERTY 24
-struct drm_object_properties {
-       int count, atomic_count;
-       /* NOTE: if we ever start dynamically destroying properties (ie.
-        * not at drm_mode_config_cleanup() time), then we'd have to do
-        * a better job of detaching property from mode objects to avoid
-        * dangling property pointers:
-        */
-       struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY];
-       /* do not read/write values directly, but use drm_object_property_get_value()
-        * and drm_object_property_set_value():
-        */
-       uint64_t values[DRM_OBJECT_MAX_PROPERTY];
-};
-
 static inline int64_t U642I64(uint64_t val)
 {
        return (int64_t)*((int64_t *)&val);
@@ -78,84 +65,6 @@ static inline uint64_t I642U64(int64_t val)
        return (uint64_t)*((uint64_t *)&val);
 }
 
-/*
- * Rotation property bits. DRM_ROTATE_<degrees> rotates the image by the
- * specified amount in degrees in counter clockwise direction. DRM_REFLECT_X and
- * DRM_REFLECT_Y reflects the image along the specified axis prior to rotation
- */
-#define DRM_ROTATE_MASK 0x0f
-#define DRM_ROTATE_0   0
-#define DRM_ROTATE_90  1
-#define DRM_ROTATE_180 2
-#define DRM_ROTATE_270 3
-#define DRM_REFLECT_MASK (~DRM_ROTATE_MASK)
-#define DRM_REFLECT_X  4
-#define DRM_REFLECT_Y  5
-
-enum drm_connector_force {
-       DRM_FORCE_UNSPECIFIED,
-       DRM_FORCE_OFF,
-       DRM_FORCE_ON,         /* force on analog part normally */
-       DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */
-};
-
-#include <drm/drm_modes.h>
-
-enum drm_connector_status {
-       connector_status_connected = 1,
-       connector_status_disconnected = 2,
-       connector_status_unknown = 3,
-};
-
-enum subpixel_order {
-       SubPixelUnknown = 0,
-       SubPixelHorizontalRGB,
-       SubPixelHorizontalBGR,
-       SubPixelVerticalRGB,
-       SubPixelVerticalBGR,
-       SubPixelNone,
-};
-
-#define DRM_COLOR_FORMAT_RGB444                (1<<0)
-#define DRM_COLOR_FORMAT_YCRCB444      (1<<1)
-#define DRM_COLOR_FORMAT_YCRCB422      (1<<2)
-
-#define DRM_BUS_FLAG_DE_LOW            (1<<0)
-#define DRM_BUS_FLAG_DE_HIGH           (1<<1)
-/* drive data on pos. edge */
-#define DRM_BUS_FLAG_PIXDATA_POSEDGE   (1<<2)
-/* drive data on neg. edge */
-#define DRM_BUS_FLAG_PIXDATA_NEGEDGE   (1<<3)
-
-/*
- * Describes a given display (e.g. CRT or flat panel) and its limitations.
- */
-struct drm_display_info {
-       char name[DRM_DISPLAY_INFO_LEN];
-
-       /* Physical size */
-        unsigned int width_mm;
-       unsigned int height_mm;
-
-       /* Clock limits FIXME: storage format */
-       unsigned int min_vfreq, max_vfreq;
-       unsigned int min_hfreq, max_hfreq;
-       unsigned int pixel_clock;
-       unsigned int bpc;
-
-       enum subpixel_order subpixel_order;
-       u32 color_formats;
-
-       const u32 *bus_formats;
-       unsigned int num_bus_formats;
-       u32 bus_flags;
-
-       /* Mask of supported hdmi deep color modes */
-       u8 edid_hdmi_dc_modes;
-
-       u8 cea_rev;
-};
-
 /* data corresponds to displayid vend/prod/serial */
 struct drm_tile_group {
        struct kref refcount;
@@ -164,130 +73,7 @@ struct drm_tile_group {
        u8 group_data[8];
 };
 
-/**
- * struct drm_framebuffer_funcs - framebuffer hooks
- */
-struct drm_framebuffer_funcs {
-       /**
-        * @destroy:
-        *
-        * Clean up framebuffer resources, specifically also unreference the
-        * backing storage. The core guarantees to call this function for every
-        * framebuffer successfully created by ->fb_create() in
-        * &drm_mode_config_funcs. Drivers must also call
-        * drm_framebuffer_cleanup() to release DRM core resources for this
-        * framebuffer.
-        */
-       void (*destroy)(struct drm_framebuffer *framebuffer);
-
-       /**
-        * @create_handle:
-        *
-        * Create a buffer handle in the driver-specific buffer manager (either
-        * GEM or TTM) valid for the passed-in struct &drm_file. This is used by
-        * the core to implement the GETFB IOCTL, which returns (for
-        * sufficiently priviledged user) also a native buffer handle. This can
-        * be used for seamless transitions between modesetting clients by
-        * copying the current screen contents to a private buffer and blending
-        * between that and the new contents.
-        *
-        * GEM based drivers should call drm_gem_handle_create() to create the
-        * handle.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*create_handle)(struct drm_framebuffer *fb,
-                            struct drm_file *file_priv,
-                            unsigned int *handle);
-       /**
-        * @dirty:
-        *
-        * Optional callback for the dirty fb IOCTL.
-        *
-        * Userspace can notify the driver via this callback that an area of the
-        * framebuffer has changed and should be flushed to the display
-        * hardware. This can also be used internally, e.g. by the fbdev
-        * emulation, though that's not the case currently.
-        *
-        * See documentation in drm_mode.h for the struct drm_mode_fb_dirty_cmd
-        * for more information as all the semantics and arguments have a one to
-        * one mapping on this function.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*dirty)(struct drm_framebuffer *framebuffer,
-                    struct drm_file *file_priv, unsigned flags,
-                    unsigned color, struct drm_clip_rect *clips,
-                    unsigned num_clips);
-};
-
-struct drm_framebuffer {
-       struct drm_device *dev;
-       /*
-        * Note that the fb is refcounted for the benefit of driver internals,
-        * for example some hw, disabling a CRTC/plane is asynchronous, and
-        * scanout does not actually complete until the next vblank.  So some
-        * cleanup (like releasing the reference(s) on the backing GEM bo(s))
-        * should be deferred.  In cases like this, the driver would like to
-        * hold a ref to the fb even though it has already been removed from
-        * userspace perspective.
-        * The refcount is stored inside the mode object.
-        */
-       /*
-        * Place on the dev->mode_config.fb_list, access protected by
-        * dev->mode_config.fb_lock.
-        */
-       struct list_head head;
-       struct drm_mode_object base;
-       const struct drm_framebuffer_funcs *funcs;
-       unsigned int pitches[4];
-       unsigned int offsets[4];
-       uint64_t modifier[4];
-       unsigned int width;
-       unsigned int height;
-       /* depth can be 15 or 16 */
-       unsigned int depth;
-       int bits_per_pixel;
-       int flags;
-       uint32_t pixel_format; /* fourcc format */
-       int hot_x;
-       int hot_y;
-       struct list_head filp_head;
-};
-
-struct drm_property_blob {
-       struct drm_mode_object base;
-       struct drm_device *dev;
-       struct list_head head_global;
-       struct list_head head_file;
-       size_t length;
-       unsigned char data[];
-};
-
-struct drm_property_enum {
-       uint64_t value;
-       struct list_head head;
-       char name[DRM_PROP_NAME_LEN];
-};
-
-struct drm_property {
-       struct list_head head;
-       struct drm_mode_object base;
-       uint32_t flags;
-       char name[DRM_PROP_NAME_LEN];
-       uint32_t num_values;
-       uint64_t *values;
-       struct drm_device *dev;
-
-       struct list_head enum_list;
-};
-
 struct drm_crtc;
-struct drm_connector;
 struct drm_encoder;
 struct drm_pending_vblank_event;
 struct drm_plane;
@@ -296,7 +82,6 @@ struct drm_atomic_state;
 
 struct drm_crtc_helper_funcs;
 struct drm_encoder_helper_funcs;
-struct drm_connector_helper_funcs;
 struct drm_plane_helper_funcs;
 
 /**
@@ -324,8 +109,6 @@ struct drm_plane_helper_funcs;
  * @ctm: Transformation matrix
  * @gamma_lut: Lookup table for converting pixel data after the
  *     conversion matrix
- * @event: optional pointer to a DRM event to signal upon completion of the
- *     state update
  * @state: backpointer to global drm_atomic_state
  *
  * Note that the distinction between @enable and @active is rather subtile:
@@ -374,6 +157,46 @@ struct drm_crtc_state {
        struct drm_property_blob *ctm;
        struct drm_property_blob *gamma_lut;
 
+       /**
+        * @event:
+        *
+        * Optional pointer to a DRM event to signal upon completion of the
+        * state update. The driver must send out the event when the atomic
+        * commit operation completes. There are two cases:
+        *
+        *  - The event is for a CRTC which is being disabled through this
+        *    atomic commit. In that case the event can be send out any time
+        *    after the hardware has stopped scanning out the current
+        *    framebuffers. It should contain the timestamp and counter for the
+        *    last vblank before the display pipeline was shut off.
+        *
+        *  - For a CRTC which is enabled at the end of the commit (even when it
+        *    undergoes an full modeset) the vblank timestamp and counter must
+        *    be for the vblank right before the first frame that scans out the
+        *    new set of buffers. Again the event can only be sent out after the
+        *    hardware has stopped scanning out the old buffers.
+        *
+        *  - Events for disabled CRTCs are not allowed, and drivers can ignore
+        *    that case.
+        *
+        * This can be handled by the drm_crtc_send_vblank_event() function,
+        * which the driver should call on the provided event upon completion of
+        * the atomic commit. Note that if the driver supports vblank signalling
+        * and timestamping the vblank counters and timestamps must agree with
+        * the ones returned from page flip events. With the current vblank
+        * helper infrastructure this can be achieved by holding a vblank
+        * reference while the page flip is pending, acquired through
+        * drm_crtc_vblank_get() and released with drm_crtc_vblank_put().
+        * Drivers are free to implement their own vblank counter and timestamp
+        * tracking though, e.g. if they have accurate timestamp registers in
+        * hardware.
+        *
+        * For hardware which supports some means to synchronize vblank
+        * interrupt delivery with committing display state there's also
+        * drm_crtc_arm_vblank_event(). See the documentation of that function
+        * for a detailed discussion of the constraints it needs to be used
+        * safely.
+        */
        struct drm_pending_vblank_event *event;
 
        struct drm_atomic_state *state;
@@ -545,16 +368,6 @@ struct drm_crtc_funcs {
         * counter and timestamp tracking though, e.g. if they have accurate
         * timestamp registers in hardware.
         *
-        * FIXME:
-        *
-        * Up to that point drivers need to manage events themselves and can use
-        * even->base.list freely for that. Specifically they need to ensure
-        * that they don't send out page flip (or vblank) events for which the
-        * corresponding drm file has been closed already. The drm core
-        * unfortunately does not (yet) take care of that. Therefore drivers
-        * currently must clean up and release pending events in their
-        * ->preclose driver function.
-        *
         * This callback is optional.
         *
         * NOTE:
@@ -580,6 +393,24 @@ struct drm_crtc_funcs {
                         struct drm_pending_vblank_event *event,
                         uint32_t flags);
 
+       /**
+        * @page_flip_target:
+        *
+        * Same as @page_flip but with an additional parameter specifying the
+        * absolute target vertical blank period (as reported by
+        * drm_crtc_vblank_count()) when the flip should take effect.
+        *
+        * Note that the core code calls drm_crtc_vblank_get before this entry
+        * point, and will call drm_crtc_vblank_put if this entry point returns
+        * any non-0 error code. It's the driver's responsibility to call
+        * drm_crtc_vblank_put after this entry point returns 0, typically when
+        * the flip completes.
+        */
+       int (*page_flip_target)(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               struct drm_pending_vblank_event *event,
+                               uint32_t flags, uint32_t target);
+
        /**
         * @set_property:
         *
@@ -717,1335 +548,140 @@ struct drm_crtc_funcs {
         * Everything added from this callback should be unregistered in
         * the early_unregister callback.
         *
-        * Returns:
-        *
-        * 0 on success, or a negative error code on failure.
-        */
-       int (*late_register)(struct drm_crtc *crtc);
-
-       /**
-        * @early_unregister:
-        *
-        * This optional hook should be used to unregister the additional
-        * userspace interfaces attached to the crtc from
-        * late_unregister(). It is called from drm_dev_unregister(),
-        * early in the driver unload sequence to disable userspace access
-        * before data structures are torndown.
-        */
-       void (*early_unregister)(struct drm_crtc *crtc);
-};
-
-/**
- * struct drm_crtc - central CRTC control structure
- * @dev: parent DRM device
- * @port: OF node used by drm_of_find_possible_crtcs()
- * @head: list management
- * @name: human readable name, can be overwritten by the driver
- * @mutex: per-CRTC locking
- * @base: base KMS object for ID tracking etc.
- * @primary: primary plane for this CRTC
- * @cursor: cursor plane for this CRTC
- * @cursor_x: current x position of the cursor, used for universal cursor planes
- * @cursor_y: current y position of the cursor, used for universal cursor planes
- * @enabled: is this CRTC enabled?
- * @mode: current mode timings
- * @hwmode: mode timings as programmed to hw regs
- * @x: x position on screen
- * @y: y position on screen
- * @funcs: CRTC control functions
- * @gamma_size: size of gamma ramp
- * @gamma_store: gamma ramp values
- * @helper_private: mid-layer private data
- * @properties: property tracking for this CRTC
- *
- * Each CRTC may have one or more connectors associated with it.  This structure
- * allows the CRTC to be controlled.
- */
-struct drm_crtc {
-       struct drm_device *dev;
-       struct device_node *port;
-       struct list_head head;
-
-       char *name;
-
-       /**
-        * @mutex:
-        *
-        * This provides a read lock for the overall crtc state (mode, dpms
-        * state, ...) and a write lock for everything which can be update
-        * without a full modeset (fb, cursor data, crtc properties ...). Full
-        * modeset also need to grab dev->mode_config.connection_mutex.
-        */
-       struct drm_modeset_lock mutex;
-
-       struct drm_mode_object base;
-
-       /* primary and cursor planes for CRTC */
-       struct drm_plane *primary;
-       struct drm_plane *cursor;
-
-       /**
-        * @index: Position inside the mode_config.list, can be used as an array
-        * index. It is invariant over the lifetime of the CRTC.
-        */
-       unsigned index;
-
-       /* position of cursor plane on crtc */
-       int cursor_x;
-       int cursor_y;
-
-       bool enabled;
-
-       /* Requested mode from modesetting. */
-       struct drm_display_mode mode;
-
-       /* Programmed mode in hw, after adjustments for encoders,
-        * crtc, panel scaling etc. Needed for timestamping etc.
-        */
-       struct drm_display_mode hwmode;
-
-       int x, y;
-       const struct drm_crtc_funcs *funcs;
-
-       /* Legacy FB CRTC gamma size for reporting to userspace */
-       uint32_t gamma_size;
-       uint16_t *gamma_store;
-
-       /* if you are using the helper */
-       const struct drm_crtc_helper_funcs *helper_private;
-
-       struct drm_object_properties properties;
-
-       /**
-        * @state:
-        *
-        * Current atomic state for this CRTC.
-        */
-       struct drm_crtc_state *state;
-
-       /**
-        * @commit_list:
-        *
-        * List of &drm_crtc_commit structures tracking pending commits.
-        * Protected by @commit_lock. This list doesn't hold its own full
-        * reference, but burrows it from the ongoing commit. Commit entries
-        * must be removed from this list once the commit is fully completed,
-        * but before it's correspoding &drm_atomic_state gets destroyed.
-        */
-       struct list_head commit_list;
-
-       /**
-        * @commit_lock:
-        *
-        * Spinlock to protect @commit_list.
-        */
-       spinlock_t commit_lock;
-
-       /**
-        * @acquire_ctx:
-        *
-        * Per-CRTC implicit acquire context used by atomic drivers for legacy
-        * IOCTLs, so that atomic drivers can get at the locking acquire
-        * context.
-        */
-       struct drm_modeset_acquire_ctx *acquire_ctx;
-};
-
-/**
- * struct drm_connector_state - mutable connector state
- * @connector: backpointer to the connector
- * @crtc: CRTC to connect connector to, NULL if disabled
- * @best_encoder: can be used by helpers and drivers to select the encoder
- * @state: backpointer to global drm_atomic_state
- */
-struct drm_connector_state {
-       struct drm_connector *connector;
-
-       struct drm_crtc *crtc;  /* do not write directly, use drm_atomic_set_crtc_for_connector() */
-
-       struct drm_encoder *best_encoder;
-
-       struct drm_atomic_state *state;
-};
-
-/**
- * struct drm_connector_funcs - control connectors on a given device
- *
- * Each CRTC may have one or more connectors attached to it.  The functions
- * below allow the core DRM code to control connectors, enumerate available modes,
- * etc.
- */
-struct drm_connector_funcs {
-       /**
-        * @dpms:
-        *
-        * Legacy entry point to set the per-connector DPMS state. Legacy DPMS
-        * is exposed as a standard property on the connector, but diverted to
-        * this callback in the drm core. Note that atomic drivers don't
-        * implement the 4 level DPMS support on the connector any more, but
-        * instead only have an on/off "ACTIVE" property on the CRTC object.
-        *
-        * Drivers implementing atomic modeset should use
-        * drm_atomic_helper_connector_dpms() to implement this hook.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*dpms)(struct drm_connector *connector, int mode);
-
-       /**
-        * @reset:
-        *
-        * Reset connector hardware and software state to off. This function isn't
-        * called by the core directly, only through drm_mode_config_reset().
-        * It's not a helper hook only for historical reasons.
-        *
-        * Atomic drivers can use drm_atomic_helper_connector_reset() to reset
-        * atomic state using this hook.
-        */
-       void (*reset)(struct drm_connector *connector);
-
-       /**
-        * @detect:
-        *
-        * Check to see if anything is attached to the connector. The parameter
-        * force is set to false whilst polling, true when checking the
-        * connector due to a user request. force can be used by the driver to
-        * avoid expensive, destructive operations during automated probing.
-        *
-        * FIXME:
-        *
-        * Note that this hook is only called by the probe helper. It's not in
-        * the helper library vtable purely for historical reasons. The only DRM
-        * core entry point to probe connector state is @fill_modes.
-        *
-        * RETURNS:
-        *
-        * drm_connector_status indicating the connector's status.
-        */
-       enum drm_connector_status (*detect)(struct drm_connector *connector,
-                                           bool force);
-
-       /**
-        * @force:
-        *
-        * This function is called to update internal encoder state when the
-        * connector is forced to a certain state by userspace, either through
-        * the sysfs interfaces or on the kernel cmdline. In that case the
-        * @detect callback isn't called.
-        *
-        * FIXME:
-        *
-        * Note that this hook is only called by the probe helper. It's not in
-        * the helper library vtable purely for historical reasons. The only DRM
-        * core entry point to probe connector state is @fill_modes.
-        */
-       void (*force)(struct drm_connector *connector);
-
-       /**
-        * @fill_modes:
-        *
-        * Entry point for output detection and basic mode validation. The
-        * driver should reprobe the output if needed (e.g. when hotplug
-        * handling is unreliable), add all detected modes to connector->modes
-        * and filter out any the device can't support in any configuration. It
-        * also needs to filter out any modes wider or higher than the
-        * parameters max_width and max_height indicate.
-        *
-        * The drivers must also prune any modes no longer valid from
-        * connector->modes. Furthermore it must update connector->status and
-        * connector->edid.  If no EDID has been received for this output
-        * connector->edid must be NULL.
-        *
-        * Drivers using the probe helpers should use
-        * drm_helper_probe_single_connector_modes() or
-        * drm_helper_probe_single_connector_modes_nomerge() to implement this
-        * function.
-        *
-        * RETURNS:
-        *
-        * The number of modes detected and filled into connector->modes.
-        */
-       int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height);
-
-       /**
-        * @set_property:
-        *
-        * This is the legacy entry point to update a property attached to the
-        * connector.
-        *
-        * Drivers implementing atomic modeset should use
-        * drm_atomic_helper_connector_set_property() to implement this hook.
-        *
-        * This callback is optional if the driver does not support any legacy
-        * driver-private properties.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*set_property)(struct drm_connector *connector, struct drm_property *property,
-                            uint64_t val);
-
-       /**
-        * @late_register:
-        *
-        * This optional hook can be used to register additional userspace
-        * interfaces attached to the connector, light backlight control, i2c,
-        * DP aux or similar interfaces. It is called late in the driver load
-        * sequence from drm_connector_register() when registering all the
-        * core drm connector interfaces. Everything added from this callback
-        * should be unregistered in the early_unregister callback.
-        *
-        * Returns:
-        *
-        * 0 on success, or a negative error code on failure.
-        */
-       int (*late_register)(struct drm_connector *connector);
-
-       /**
-        * @early_unregister:
-        *
-        * This optional hook should be used to unregister the additional
-        * userspace interfaces attached to the connector from
-        * late_unregister(). It is called from drm_connector_unregister(),
-        * early in the driver unload sequence to disable userspace access
-        * before data structures are torndown.
-        */
-       void (*early_unregister)(struct drm_connector *connector);
-
-       /**
-        * @destroy:
-        *
-        * Clean up connector resources. This is called at driver unload time
-        * through drm_mode_config_cleanup(). It can also be called at runtime
-        * when a connector is being hot-unplugged for drivers that support
-        * connector hotplugging (e.g. DisplayPort MST).
-        */
-       void (*destroy)(struct drm_connector *connector);
-
-       /**
-        * @atomic_duplicate_state:
-        *
-        * Duplicate the current atomic state for this connector and return it.
-        * The core and helpers gurantee that any atomic state duplicated with
-        * this hook and still owned by the caller (i.e. not transferred to the
-        * driver by calling ->atomic_commit() from struct
-        * &drm_mode_config_funcs) will be cleaned up by calling the
-        * @atomic_destroy_state hook in this structure.
-        *
-        * Atomic drivers which don't subclass struct &drm_connector_state should use
-        * drm_atomic_helper_connector_duplicate_state(). Drivers that subclass the
-        * state structure to extend it with driver-private state should use
-        * __drm_atomic_helper_connector_duplicate_state() to make sure shared state is
-        * duplicated in a consistent fashion across drivers.
-        *
-        * It is an error to call this hook before connector->state has been
-        * initialized correctly.
-        *
-        * NOTE:
-        *
-        * If the duplicate state references refcounted resources this hook must
-        * acquire a reference for each of them. The driver must release these
-        * references again in @atomic_destroy_state.
-        *
-        * RETURNS:
-        *
-        * Duplicated atomic state or NULL when the allocation failed.
-        */
-       struct drm_connector_state *(*atomic_duplicate_state)(struct drm_connector *connector);
-
-       /**
-        * @atomic_destroy_state:
-        *
-        * Destroy a state duplicated with @atomic_duplicate_state and release
-        * or unreference all resources it references
-        */
-       void (*atomic_destroy_state)(struct drm_connector *connector,
-                                    struct drm_connector_state *state);
-
-       /**
-        * @atomic_set_property:
-        *
-        * Decode a driver-private property value and store the decoded value
-        * into the passed-in state structure. Since the atomic core decodes all
-        * standardized properties (even for extensions beyond the core set of
-        * properties which might not be implemented by all drivers) this
-        * requires drivers to subclass the state structure.
-        *
-        * Such driver-private properties should really only be implemented for
-        * truly hardware/vendor specific state. Instead it is preferred to
-        * standardize atomic extension and decode the properties used to expose
-        * such an extension in the core.
-        *
-        * Do not call this function directly, use
-        * drm_atomic_connector_set_property() instead.
-        *
-        * This callback is optional if the driver does not support any
-        * driver-private atomic properties.
-        *
-        * NOTE:
-        *
-        * This function is called in the state assembly phase of atomic
-        * modesets, which can be aborted for any reason (including on
-        * userspace's request to just check whether a configuration would be
-        * possible). Drivers MUST NOT touch any persistent state (hardware or
-        * software) or data structures except the passed in @state parameter.
-        *
-        * Also since userspace controls in which order properties are set this
-        * function must not do any input validation (since the state update is
-        * incomplete and hence likely inconsistent). Instead any such input
-        * validation must be done in the various atomic_check callbacks.
-        *
-        * RETURNS:
-        *
-        * 0 if the property has been found, -EINVAL if the property isn't
-        * implemented by the driver (which shouldn't ever happen, the core only
-        * asks for properties attached to this connector). No other validation
-        * is allowed by the driver. The core already checks that the property
-        * value is within the range (integer, valid enum value, ...) the driver
-        * set when registering the property.
-        */
-       int (*atomic_set_property)(struct drm_connector *connector,
-                                  struct drm_connector_state *state,
-                                  struct drm_property *property,
-                                  uint64_t val);
-
-       /**
-        * @atomic_get_property:
-        *
-        * Reads out the decoded driver-private property. This is used to
-        * implement the GETCONNECTOR IOCTL.
-        *
-        * Do not call this function directly, use
-        * drm_atomic_connector_get_property() instead.
-        *
-        * This callback is optional if the driver does not support any
-        * driver-private atomic properties.
-        *
-        * RETURNS:
-        *
-        * 0 on success, -EINVAL if the property isn't implemented by the
-        * driver (which shouldn't ever happen, the core only asks for
-        * properties attached to this connector).
-        */
-       int (*atomic_get_property)(struct drm_connector *connector,
-                                  const struct drm_connector_state *state,
-                                  struct drm_property *property,
-                                  uint64_t *val);
-};
-
-/**
- * struct drm_encoder_funcs - encoder controls
- *
- * Encoders sit between CRTCs and connectors.
- */
-struct drm_encoder_funcs {
-       /**
-        * @reset:
-        *
-        * Reset encoder hardware and software state to off. This function isn't
-        * called by the core directly, only through drm_mode_config_reset().
-        * It's not a helper hook only for historical reasons.
-        */
-       void (*reset)(struct drm_encoder *encoder);
-
-       /**
-        * @destroy:
-        *
-        * Clean up encoder resources. This is only called at driver unload time
-        * through drm_mode_config_cleanup() since an encoder cannot be
-        * hotplugged in DRM.
-        */
-       void (*destroy)(struct drm_encoder *encoder);
-
-       /**
-        * @late_register:
-        *
-        * This optional hook can be used to register additional userspace
-        * interfaces attached to the encoder like debugfs interfaces.
-        * It is called late in the driver load sequence from drm_dev_register().
-        * Everything added from this callback should be unregistered in
-        * the early_unregister callback.
-        *
-        * Returns:
-        *
-        * 0 on success, or a negative error code on failure.
-        */
-       int (*late_register)(struct drm_encoder *encoder);
-
-       /**
-        * @early_unregister:
-        *
-        * This optional hook should be used to unregister the additional
-        * userspace interfaces attached to the encoder from
-        * late_unregister(). It is called from drm_dev_unregister(),
-        * early in the driver unload sequence to disable userspace access
-        * before data structures are torndown.
-        */
-       void (*early_unregister)(struct drm_encoder *encoder);
-};
-
-#define DRM_CONNECTOR_MAX_ENCODER 3
-
-/**
- * struct drm_encoder - central DRM encoder structure
- * @dev: parent DRM device
- * @head: list management
- * @base: base KMS object
- * @name: human readable name, can be overwritten by the driver
- * @encoder_type: one of the %DRM_MODE_ENCODER_<foo> types in drm_mode.h
- * @possible_crtcs: bitmask of potential CRTC bindings
- * @possible_clones: bitmask of potential sibling encoders for cloning
- * @crtc: currently bound CRTC
- * @bridge: bridge associated to the encoder
- * @funcs: control functions
- * @helper_private: mid-layer private data
- *
- * CRTCs drive pixels to encoders, which convert them into signals
- * appropriate for a given connector or set of connectors.
- */
-struct drm_encoder {
-       struct drm_device *dev;
-       struct list_head head;
-
-       struct drm_mode_object base;
-       char *name;
-       int encoder_type;
-
-       /**
-        * @index: Position inside the mode_config.list, can be used as an array
-        * index. It is invariant over the lifetime of the encoder.
-        */
-       unsigned index;
-
-       uint32_t possible_crtcs;
-       uint32_t possible_clones;
-
-       struct drm_crtc *crtc;
-       struct drm_bridge *bridge;
-       const struct drm_encoder_funcs *funcs;
-       const struct drm_encoder_helper_funcs *helper_private;
-};
-
-/* should we poll this connector for connects and disconnects */
-/* hot plug detectable */
-#define DRM_CONNECTOR_POLL_HPD (1 << 0)
-/* poll for connections */
-#define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
-/* can cleanly poll for disconnections without flickering the screen */
-/* DACs should rarely do this without a lot of testing */
-#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
-
-#define MAX_ELD_BYTES  128
-
-/**
- * struct drm_connector - central DRM connector control structure
- * @dev: parent DRM device
- * @kdev: kernel device for sysfs attributes
- * @attr: sysfs attributes
- * @head: list management
- * @base: base KMS object
- * @name: human readable name, can be overwritten by the driver
- * @connector_type: one of the %DRM_MODE_CONNECTOR_<foo> types from drm_mode.h
- * @connector_type_id: index into connector type enum
- * @interlace_allowed: can this connector handle interlaced modes?
- * @doublescan_allowed: can this connector handle doublescan?
- * @stereo_allowed: can this connector handle stereo modes?
- * @registered: is this connector exposed (registered) with userspace?
- * @modes: modes available on this connector (from fill_modes() + user)
- * @status: one of the drm_connector_status enums (connected, not, or unknown)
- * @probed_modes: list of modes derived directly from the display
- * @display_info: information about attached display (e.g. from EDID)
- * @funcs: connector control functions
- * @edid_blob_ptr: DRM property containing EDID if present
- * @properties: property tracking for this connector
- * @polled: a %DRM_CONNECTOR_POLL_<foo> value for core driven polling
- * @dpms: current dpms state
- * @helper_private: mid-layer private data
- * @cmdline_mode: mode line parsed from the kernel cmdline for this connector
- * @force: a %DRM_FORCE_<foo> state for forced mode sets
- * @override_edid: has the EDID been overwritten through debugfs for testing?
- * @encoder_ids: valid encoders for this connector
- * @encoder: encoder driving this connector, if any
- * @eld: EDID-like data, if present
- * @dvi_dual: dual link DVI, if found
- * @max_tmds_clock: max clock rate, if found
- * @latency_present: AV delay info from ELD, if found
- * @video_latency: video latency info from ELD, if found
- * @audio_latency: audio latency info from ELD, if found
- * @null_edid_counter: track sinks that give us all zeros for the EDID
- * @bad_edid_counter: track sinks that give us an EDID with invalid checksum
- * @edid_corrupt: indicates whether the last read EDID was corrupt
- * @debugfs_entry: debugfs directory for this connector
- * @state: current atomic state for this connector
- * @has_tile: is this connector connected to a tiled monitor
- * @tile_group: tile group for the connected monitor
- * @tile_is_single_monitor: whether the tile is one monitor housing
- * @num_h_tile: number of horizontal tiles in the tile group
- * @num_v_tile: number of vertical tiles in the tile group
- * @tile_h_loc: horizontal location of this tile
- * @tile_v_loc: vertical location of this tile
- * @tile_h_size: horizontal size of this tile.
- * @tile_v_size: vertical size of this tile.
- *
- * Each connector may be connected to one or more CRTCs, or may be clonable by
- * another connector if they can share a CRTC.  Each connector also has a specific
- * position in the broader display (referred to as a 'screen' though it could
- * span multiple monitors).
- */
-struct drm_connector {
-       struct drm_device *dev;
-       struct device *kdev;
-       struct device_attribute *attr;
-       struct list_head head;
-
-       struct drm_mode_object base;
-
-       char *name;
-
-       /**
-        * @index: Compacted connector index, which matches the position inside
-        * the mode_config.list for drivers not supporting hot-add/removing. Can
-        * be used as an array index. It is invariant over the lifetime of the
-        * connector.
-        */
-       unsigned index;
-
-       int connector_type;
-       int connector_type_id;
-       bool interlace_allowed;
-       bool doublescan_allowed;
-       bool stereo_allowed;
-       bool registered;
-       struct list_head modes; /* list of modes on this connector */
-
-       enum drm_connector_status status;
-
-       /* these are modes added by probing with DDC or the BIOS */
-       struct list_head probed_modes;
-
-       struct drm_display_info display_info;
-       const struct drm_connector_funcs *funcs;
-
-       struct drm_property_blob *edid_blob_ptr;
-       struct drm_object_properties properties;
-
-       /**
-        * @path_blob_ptr:
-        *
-        * DRM blob property data for the DP MST path property.
-        */
-       struct drm_property_blob *path_blob_ptr;
-
-       /**
-        * @tile_blob_ptr:
-        *
-        * DRM blob property data for the tile property (used mostly by DP MST).
-        * This is meant for screens which are driven through separate display
-        * pipelines represented by &drm_crtc, which might not be running with
-        * genlocked clocks. For tiled panels which are genlocked, like
-        * dual-link LVDS or dual-link DSI, the driver should try to not expose
-        * the tiling and virtualize both &drm_crtc and &drm_plane if needed.
-        */
-       struct drm_property_blob *tile_blob_ptr;
-
-       uint8_t polled; /* DRM_CONNECTOR_POLL_* */
-
-       /* requested DPMS state */
-       int dpms;
-
-       const struct drm_connector_helper_funcs *helper_private;
-
-       /* forced on connector */
-       struct drm_cmdline_mode cmdline_mode;
-       enum drm_connector_force force;
-       bool override_edid;
-       uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
-       struct drm_encoder *encoder; /* currently active encoder */
-
-       /* EDID bits */
-       uint8_t eld[MAX_ELD_BYTES];
-       bool dvi_dual;
-       int max_tmds_clock;     /* in MHz */
-       bool latency_present[2];
-       int video_latency[2];   /* [0]: progressive, [1]: interlaced */
-       int audio_latency[2];
-       int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */
-       unsigned bad_edid_counter;
-
-       /* Flag for raw EDID header corruption - used in Displayport
-        * compliance testing - * Displayport Link CTS Core 1.2 rev1.1 4.2.2.6
-        */
-       bool edid_corrupt;
-
-       struct dentry *debugfs_entry;
-
-       struct drm_connector_state *state;
-
-       /* DisplayID bits */
-       bool has_tile;
-       struct drm_tile_group *tile_group;
-       bool tile_is_single_monitor;
-
-       uint8_t num_h_tile, num_v_tile;
-       uint8_t tile_h_loc, tile_v_loc;
-       uint16_t tile_h_size, tile_v_size;
-};
-
-/**
- * struct drm_plane_state - mutable plane state
- * @plane: backpointer to the plane
- * @crtc: currently bound CRTC, NULL if disabled
- * @fb: currently bound framebuffer
- * @fence: optional fence to wait for before scanning out @fb
- * @crtc_x: left position of visible portion of plane on crtc
- * @crtc_y: upper position of visible portion of plane on crtc
- * @crtc_w: width of visible portion of plane on crtc
- * @crtc_h: height of visible portion of plane on crtc
- * @src_x: left position of visible portion of plane within
- *     plane (in 16.16)
- * @src_y: upper position of visible portion of plane within
- *     plane (in 16.16)
- * @src_w: width of visible portion of plane (in 16.16)
- * @src_h: height of visible portion of plane (in 16.16)
- * @rotation: rotation of the plane
- * @zpos: priority of the given plane on crtc (optional)
- * @normalized_zpos: normalized value of zpos: unique, range from 0 to N-1
- *     where N is the number of active planes for given crtc
- * @state: backpointer to global drm_atomic_state
- */
-struct drm_plane_state {
-       struct drm_plane *plane;
-
-       struct drm_crtc *crtc;   /* do not write directly, use drm_atomic_set_crtc_for_plane() */
-       struct drm_framebuffer *fb;  /* do not write directly, use drm_atomic_set_fb_for_plane() */
-       struct fence *fence;
-
-       /* Signed dest location allows it to be partially off screen */
-       int32_t crtc_x, crtc_y;
-       uint32_t crtc_w, crtc_h;
-
-       /* Source values are 16.16 fixed point */
-       uint32_t src_x, src_y;
-       uint32_t src_h, src_w;
-
-       /* Plane rotation */
-       unsigned int rotation;
-
-       /* Plane zpos */
-       unsigned int zpos;
-       unsigned int normalized_zpos;
-
-       struct drm_atomic_state *state;
-};
-
-
-/**
- * struct drm_plane_funcs - driver plane control functions
- */
-struct drm_plane_funcs {
-       /**
-        * @update_plane:
-        *
-        * This is the legacy entry point to enable and configure the plane for
-        * the given CRTC and framebuffer. It is never called to disable the
-        * plane, i.e. the passed-in crtc and fb paramters are never NULL.
-        *
-        * The source rectangle in frame buffer memory coordinates is given by
-        * the src_x, src_y, src_w and src_h parameters (as 16.16 fixed point
-        * values). Devices that don't support subpixel plane coordinates can
-        * ignore the fractional part.
-        *
-        * The destination rectangle in CRTC coordinates is given by the
-        * crtc_x, crtc_y, crtc_w and crtc_h parameters (as integer values).
-        * Devices scale the source rectangle to the destination rectangle. If
-        * scaling is not supported, and the source rectangle size doesn't match
-        * the destination rectangle size, the driver must return a
-        * -<errorname>EINVAL</errorname> error.
-        *
-        * Drivers implementing atomic modeset should use
-        * drm_atomic_helper_update_plane() to implement this hook.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*update_plane)(struct drm_plane *plane,
-                           struct drm_crtc *crtc, struct drm_framebuffer *fb,
-                           int crtc_x, int crtc_y,
-                           unsigned int crtc_w, unsigned int crtc_h,
-                           uint32_t src_x, uint32_t src_y,
-                           uint32_t src_w, uint32_t src_h);
-
-       /**
-        * @disable_plane:
-        *
-        * This is the legacy entry point to disable the plane. The DRM core
-        * calls this method in response to a DRM_IOCTL_MODE_SETPLANE IOCTL call
-        * with the frame buffer ID set to 0.  Disabled planes must not be
-        * processed by the CRTC.
-        *
-        * Drivers implementing atomic modeset should use
-        * drm_atomic_helper_disable_plane() to implement this hook.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*disable_plane)(struct drm_plane *plane);
-
-       /**
-        * @destroy:
-        *
-        * Clean up plane resources. This is only called at driver unload time
-        * through drm_mode_config_cleanup() since a plane cannot be hotplugged
-        * in DRM.
-        */
-       void (*destroy)(struct drm_plane *plane);
-
-       /**
-        * @reset:
-        *
-        * Reset plane hardware and software state to off. This function isn't
-        * called by the core directly, only through drm_mode_config_reset().
-        * It's not a helper hook only for historical reasons.
-        *
-        * Atomic drivers can use drm_atomic_helper_plane_reset() to reset
-        * atomic state using this hook.
-        */
-       void (*reset)(struct drm_plane *plane);
-
-       /**
-        * @set_property:
-        *
-        * This is the legacy entry point to update a property attached to the
-        * plane.
-        *
-        * Drivers implementing atomic modeset should use
-        * drm_atomic_helper_plane_set_property() to implement this hook.
-        *
-        * This callback is optional if the driver does not support any legacy
-        * driver-private properties.
-        *
-        * RETURNS:
-        *
-        * 0 on success or a negative error code on failure.
-        */
-       int (*set_property)(struct drm_plane *plane,
-                           struct drm_property *property, uint64_t val);
-
-       /**
-        * @atomic_duplicate_state:
-        *
-        * Duplicate the current atomic state for this plane and return it.
-        * The core and helpers gurantee that any atomic state duplicated with
-        * this hook and still owned by the caller (i.e. not transferred to the
-        * driver by calling ->atomic_commit() from struct
-        * &drm_mode_config_funcs) will be cleaned up by calling the
-        * @atomic_destroy_state hook in this structure.
-        *
-        * Atomic drivers which don't subclass struct &drm_plane_state should use
-        * drm_atomic_helper_plane_duplicate_state(). Drivers that subclass the
-        * state structure to extend it with driver-private state should use
-        * __drm_atomic_helper_plane_duplicate_state() to make sure shared state is
-        * duplicated in a consistent fashion across drivers.
-        *
-        * It is an error to call this hook before plane->state has been
-        * initialized correctly.
-        *
-        * NOTE:
-        *
-        * If the duplicate state references refcounted resources this hook must
-        * acquire a reference for each of them. The driver must release these
-        * references again in @atomic_destroy_state.
-        *
-        * RETURNS:
-        *
-        * Duplicated atomic state or NULL when the allocation failed.
-        */
-       struct drm_plane_state *(*atomic_duplicate_state)(struct drm_plane *plane);
-
-       /**
-        * @atomic_destroy_state:
-        *
-        * Destroy a state duplicated with @atomic_duplicate_state and release
-        * or unreference all resources it references
-        */
-       void (*atomic_destroy_state)(struct drm_plane *plane,
-                                    struct drm_plane_state *state);
-
-       /**
-        * @atomic_set_property:
-        *
-        * Decode a driver-private property value and store the decoded value
-        * into the passed-in state structure. Since the atomic core decodes all
-        * standardized properties (even for extensions beyond the core set of
-        * properties which might not be implemented by all drivers) this
-        * requires drivers to subclass the state structure.
-        *
-        * Such driver-private properties should really only be implemented for
-        * truly hardware/vendor specific state. Instead it is preferred to
-        * standardize atomic extension and decode the properties used to expose
-        * such an extension in the core.
-        *
-        * Do not call this function directly, use
-        * drm_atomic_plane_set_property() instead.
-        *
-        * This callback is optional if the driver does not support any
-        * driver-private atomic properties.
-        *
-        * NOTE:
-        *
-        * This function is called in the state assembly phase of atomic
-        * modesets, which can be aborted for any reason (including on
-        * userspace's request to just check whether a configuration would be
-        * possible). Drivers MUST NOT touch any persistent state (hardware or
-        * software) or data structures except the passed in @state parameter.
-        *
-        * Also since userspace controls in which order properties are set this
-        * function must not do any input validation (since the state update is
-        * incomplete and hence likely inconsistent). Instead any such input
-        * validation must be done in the various atomic_check callbacks.
-        *
-        * RETURNS:
-        *
-        * 0 if the property has been found, -EINVAL if the property isn't
-        * implemented by the driver (which shouldn't ever happen, the core only
-        * asks for properties attached to this plane). No other validation is
-        * allowed by the driver. The core already checks that the property
-        * value is within the range (integer, valid enum value, ...) the driver
-        * set when registering the property.
-        */
-       int (*atomic_set_property)(struct drm_plane *plane,
-                                  struct drm_plane_state *state,
-                                  struct drm_property *property,
-                                  uint64_t val);
-
-       /**
-        * @atomic_get_property:
-        *
-        * Reads out the decoded driver-private property. This is used to
-        * implement the GETPLANE IOCTL.
-        *
-        * Do not call this function directly, use
-        * drm_atomic_plane_get_property() instead.
-        *
-        * This callback is optional if the driver does not support any
-        * driver-private atomic properties.
-        *
-        * RETURNS:
-        *
-        * 0 on success, -EINVAL if the property isn't implemented by the
-        * driver (which should never happen, the core only asks for
-        * properties attached to this plane).
-        */
-       int (*atomic_get_property)(struct drm_plane *plane,
-                                  const struct drm_plane_state *state,
-                                  struct drm_property *property,
-                                  uint64_t *val);
-       /**
-        * @late_register:
-        *
-        * This optional hook can be used to register additional userspace
-        * interfaces attached to the plane like debugfs interfaces.
-        * It is called late in the driver load sequence from drm_dev_register().
-        * Everything added from this callback should be unregistered in
-        * the early_unregister callback.
-        *
-        * Returns:
-        *
-        * 0 on success, or a negative error code on failure.
-        */
-       int (*late_register)(struct drm_plane *plane);
-
-       /**
-        * @early_unregister:
-        *
-        * This optional hook should be used to unregister the additional
-        * userspace interfaces attached to the plane from
-        * late_unregister(). It is called from drm_dev_unregister(),
-        * early in the driver unload sequence to disable userspace access
-        * before data structures are torndown.
-        */
-       void (*early_unregister)(struct drm_plane *plane);
-};
-
-enum drm_plane_type {
-       DRM_PLANE_TYPE_OVERLAY,
-       DRM_PLANE_TYPE_PRIMARY,
-       DRM_PLANE_TYPE_CURSOR,
-};
-
-
-/**
- * struct drm_plane - central DRM plane control structure
- * @dev: DRM device this plane belongs to
- * @head: for list management
- * @name: human readable name, can be overwritten by the driver
- * @base: base mode object
- * @possible_crtcs: pipes this plane can be bound to
- * @format_types: array of formats supported by this plane
- * @format_count: number of formats supported
- * @format_default: driver hasn't supplied supported formats for the plane
- * @crtc: currently bound CRTC
- * @fb: currently bound fb
- * @old_fb: Temporary tracking of the old fb while a modeset is ongoing. Used by
- *     drm_mode_set_config_internal() to implement correct refcounting.
- * @funcs: helper functions
- * @properties: property tracking for this plane
- * @type: type of plane (overlay, primary, cursor)
- * @state: current atomic state for this plane
- * @zpos_property: zpos property for this plane
- * @helper_private: mid-layer private data
- */
-struct drm_plane {
-       struct drm_device *dev;
-       struct list_head head;
-
-       char *name;
-
-       /**
-        * @mutex:
-        *
-        * Protects modeset plane state, together with the mutex of &drm_crtc
-        * this plane is linked to (when active, getting actived or getting
-        * disabled).
-        */
-       struct drm_modeset_lock mutex;
-
-       struct drm_mode_object base;
-
-       uint32_t possible_crtcs;
-       uint32_t *format_types;
-       unsigned int format_count;
-       bool format_default;
-
-       struct drm_crtc *crtc;
-       struct drm_framebuffer *fb;
-
-       struct drm_framebuffer *old_fb;
-
-       const struct drm_plane_funcs *funcs;
-
-       struct drm_object_properties properties;
-
-       enum drm_plane_type type;
-
-       /**
-        * @index: Position inside the mode_config.list, can be used as an array
-        * index. It is invariant over the lifetime of the plane.
-        */
-       unsigned index;
-
-       const struct drm_plane_helper_funcs *helper_private;
-
-       struct drm_plane_state *state;
-
-       struct drm_property *zpos_property;
-};
-
-/**
- * struct drm_bridge_funcs - drm_bridge control functions
- * @attach: Called during drm_bridge_attach
- */
-struct drm_bridge_funcs {
-       int (*attach)(struct drm_bridge *bridge);
-
-       /**
-        * @mode_fixup:
-        *
-        * This callback is used to validate and adjust a mode. The paramater
-        * mode is the display mode that should be fed to the next element in
-        * the display chain, either the final &drm_connector or the next
-        * &drm_bridge. The parameter adjusted_mode is the input mode the bridge
-        * requires. It can be modified by this callback and does not need to
-        * match mode.
-        *
-        * This is the only hook that allows a bridge to reject a modeset. If
-        * this function passes all other callbacks must succeed for this
-        * configuration.
-        *
-        * NOTE:
-        *
-        * This function is called in the check phase of atomic modesets, which
-        * can be aborted for any reason (including on userspace's request to
-        * just check whether a configuration would be possible). Drivers MUST
-        * NOT touch any persistent state (hardware or software) or data
-        * structures except the passed in @state parameter.
-        *
-        * RETURNS:
-        *
-        * True if an acceptable configuration is possible, false if the modeset
-        * operation should be rejected.
-        */
-       bool (*mode_fixup)(struct drm_bridge *bridge,
-                          const struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode);
-       /**
-        * @disable:
-        *
-        * This callback should disable the bridge. It is called right before
-        * the preceding element in the display pipe is disabled. If the
-        * preceding element is a bridge this means it's called before that
-        * bridge's ->disable() function. If the preceding element is a
-        * &drm_encoder it's called right before the encoder's ->disable(),
-        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
-        *
-        * The bridge can assume that the display pipe (i.e. clocks and timing
-        * signals) feeding it is still running when this callback is called.
-        *
-        * The disable callback is optional.
-        */
-       void (*disable)(struct drm_bridge *bridge);
-
-       /**
-        * @post_disable:
-        *
-        * This callback should disable the bridge. It is called right after
-        * the preceding element in the display pipe is disabled. If the
-        * preceding element is a bridge this means it's called after that
-        * bridge's ->post_disable() function. If the preceding element is a
-        * &drm_encoder it's called right after the encoder's ->disable(),
-        * ->prepare() or ->dpms() hook from struct &drm_encoder_helper_funcs.
-        *
-        * The bridge must assume that the display pipe (i.e. clocks and timing
-        * singals) feeding it is no longer running when this callback is
-        * called.
-        *
-        * The post_disable callback is optional.
-        */
-       void (*post_disable)(struct drm_bridge *bridge);
-
-       /**
-        * @mode_set:
-        *
-        * This callback should set the given mode on the bridge. It is called
-        * after the ->mode_set() callback for the preceding element in the
-        * display pipeline has been called already. The display pipe (i.e.
-        * clocks and timing signals) is off when this function is called.
-        */
-       void (*mode_set)(struct drm_bridge *bridge,
-                        struct drm_display_mode *mode,
-                        struct drm_display_mode *adjusted_mode);
-       /**
-        * @pre_enable:
-        *
-        * This callback should enable the bridge. It is called right before
-        * the preceding element in the display pipe is enabled. If the
-        * preceding element is a bridge this means it's called before that
-        * bridge's ->pre_enable() function. If the preceding element is a
-        * &drm_encoder it's called right before the encoder's ->enable(),
-        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
-        *
-        * The display pipe (i.e. clocks and timing signals) feeding this bridge
-        * will not yet be running when this callback is called. The bridge must
-        * not enable the display link feeding the next bridge in the chain (if
-        * there is one) when this callback is called.
+        * Returns:
         *
-        * The pre_enable callback is optional.
+        * 0 on success, or a negative error code on failure.
         */
-       void (*pre_enable)(struct drm_bridge *bridge);
+       int (*late_register)(struct drm_crtc *crtc);
 
        /**
-        * @enable:
-        *
-        * This callback should enable the bridge. It is called right after
-        * the preceding element in the display pipe is enabled. If the
-        * preceding element is a bridge this means it's called after that
-        * bridge's ->enable() function. If the preceding element is a
-        * &drm_encoder it's called right after the encoder's ->enable(),
-        * ->commit() or ->dpms() hook from struct &drm_encoder_helper_funcs.
-        *
-        * The bridge can assume that the display pipe (i.e. clocks and timing
-        * signals) feeding it is running when this callback is called. This
-        * callback must enable the display link feeding the next bridge in the
-        * chain if there is one.
+        * @early_unregister:
         *
-        * The enable callback is optional.
+        * This optional hook should be used to unregister the additional
+        * userspace interfaces attached to the crtc from
+        * late_unregister(). It is called from drm_dev_unregister(),
+        * early in the driver unload sequence to disable userspace access
+        * before data structures are torndown.
         */
-       void (*enable)(struct drm_bridge *bridge);
+       void (*early_unregister)(struct drm_crtc *crtc);
 };
 
 /**
- * struct drm_bridge - central DRM bridge control structure
- * @dev: DRM device this bridge belongs to
- * @encoder: encoder to which this bridge is connected
- * @next: the next bridge in the encoder chain
- * @of_node: device node pointer to the bridge
- * @list: to keep track of all added bridges
- * @funcs: control functions
- * @driver_private: pointer to the bridge driver's internal context
+ * struct drm_crtc - central CRTC control structure
+ * @dev: parent DRM device
+ * @port: OF node used by drm_of_find_possible_crtcs()
+ * @head: list management
+ * @name: human readable name, can be overwritten by the driver
+ * @mutex: per-CRTC locking
+ * @base: base KMS object for ID tracking etc.
+ * @primary: primary plane for this CRTC
+ * @cursor: cursor plane for this CRTC
+ * @cursor_x: current x position of the cursor, used for universal cursor planes
+ * @cursor_y: current y position of the cursor, used for universal cursor planes
+ * @enabled: is this CRTC enabled?
+ * @mode: current mode timings
+ * @hwmode: mode timings as programmed to hw regs
+ * @x: x position on screen
+ * @y: y position on screen
+ * @funcs: CRTC control functions
+ * @gamma_size: size of gamma ramp
+ * @gamma_store: gamma ramp values
+ * @helper_private: mid-layer private data
+ * @properties: property tracking for this CRTC
+ *
+ * Each CRTC may have one or more connectors associated with it.  This structure
+ * allows the CRTC to be controlled.
  */
-struct drm_bridge {
+struct drm_crtc {
        struct drm_device *dev;
-       struct drm_encoder *encoder;
-       struct drm_bridge *next;
-#ifdef CONFIG_OF
-       struct device_node *of_node;
-#endif
-       struct list_head list;
-
-       const struct drm_bridge_funcs *funcs;
-       void *driver_private;
-};
+       struct device_node *port;
+       struct list_head head;
 
-/**
- * struct drm_crtc_commit - track modeset commits on a CRTC
- *
- * This structure is used to track pending modeset changes and atomic commit on
- * a per-CRTC basis. Since updating the list should never block this structure
- * is reference counted to allow waiters to safely wait on an event to complete,
- * without holding any locks.
- *
- * It has 3 different events in total to allow a fine-grained synchronization
- * between outstanding updates::
- *
- *     atomic commit thread                    hardware
- *
- *     write new state into hardware   ---->   ...
- *     signal hw_done
- *                                             switch to new state on next
- *     ...                                     v/hblank
- *
- *     wait for buffers to show up             ...
- *
- *     ...                                     send completion irq
- *                                             irq handler signals flip_done
- *     cleanup old buffers
- *
- *     signal cleanup_done
- *
- *     wait for flip_done              <----
- *     clean up atomic state
- *
- * The important bit to know is that cleanup_done is the terminal event, but the
- * ordering between flip_done and hw_done is entirely up to the specific driver
- * and modeset state change.
- *
- * For an implementation of how to use this look at
- * drm_atomic_helper_setup_commit() from the atomic helper library.
- */
-struct drm_crtc_commit {
-       /**
-        * @crtc:
-        *
-        * DRM CRTC for this commit.
-        */
-       struct drm_crtc *crtc;
+       char *name;
 
        /**
-        * @ref:
+        * @mutex:
         *
-        * Reference count for this structure. Needed to allow blocking on
-        * completions without the risk of the completion disappearing
-        * meanwhile.
+        * This provides a read lock for the overall crtc state (mode, dpms
+        * state, ...) and a write lock for everything which can be update
+        * without a full modeset (fb, cursor data, crtc properties ...). Full
+        * modeset also need to grab dev->mode_config.connection_mutex.
         */
-       struct kref ref;
+       struct drm_modeset_lock mutex;
+
+       struct drm_mode_object base;
+
+       /* primary and cursor planes for CRTC */
+       struct drm_plane *primary;
+       struct drm_plane *cursor;
 
        /**
-        * @flip_done:
-        *
-        * Will be signaled when the hardware has flipped to the new set of
-        * buffers. Signals at the same time as when the drm event for this
-        * commit is sent to userspace, or when an out-fence is singalled. Note
-        * that for most hardware, in most cases this happens after @hw_done is
-        * signalled.
+        * @index: Position inside the mode_config.list, can be used as an array
+        * index. It is invariant over the lifetime of the CRTC.
         */
-       struct completion flip_done;
+       unsigned index;
 
-       /**
-        * @hw_done:
-        *
-        * Will be signalled when all hw register changes for this commit have
-        * been written out. Especially when disabling a pipe this can be much
-        * later than than @flip_done, since that can signal already when the
-        * screen goes black, whereas to fully shut down a pipe more register
-        * I/O is required.
-        *
-        * Note that this does not need to include separately reference-counted
-        * resources like backing storage buffer pinning, or runtime pm
-        * management.
+       /* position of cursor plane on crtc */
+       int cursor_x;
+       int cursor_y;
+
+       bool enabled;
+
+       /* Requested mode from modesetting. */
+       struct drm_display_mode mode;
+
+       /* Programmed mode in hw, after adjustments for encoders,
+        * crtc, panel scaling etc. Needed for timestamping etc.
         */
-       struct completion hw_done;
+       struct drm_display_mode hwmode;
+
+       int x, y;
+       const struct drm_crtc_funcs *funcs;
+
+       /* Legacy FB CRTC gamma size for reporting to userspace */
+       uint32_t gamma_size;
+       uint16_t *gamma_store;
+
+       /* if you are using the helper */
+       const struct drm_crtc_helper_funcs *helper_private;
+
+       struct drm_object_properties properties;
 
        /**
-        * @cleanup_done:
+        * @state:
         *
-        * Will be signalled after old buffers have been cleaned up by calling
-        * drm_atomic_helper_cleanup_planes(). Since this can only happen after
-        * a vblank wait completed it might be a bit later. This completion is
-        * useful to throttle updates and avoid hardware updates getting ahead
-        * of the buffer cleanup too much.
+        * Current atomic state for this CRTC.
         */
-       struct completion cleanup_done;
+       struct drm_crtc_state *state;
 
        /**
-        * @commit_entry:
+        * @commit_list:
         *
-        * Entry on the per-CRTC commit_list. Protected by crtc->commit_lock.
+        * List of &drm_crtc_commit structures tracking pending commits.
+        * Protected by @commit_lock. This list doesn't hold its own full
+        * reference, but burrows it from the ongoing commit. Commit entries
+        * must be removed from this list once the commit is fully completed,
+        * but before it's correspoding &drm_atomic_state gets destroyed.
         */
-       struct list_head commit_entry;
+       struct list_head commit_list;
 
        /**
-        * @event:
+        * @commit_lock:
         *
-        * &drm_pending_vblank_event pointer to clean up private events.
+        * Spinlock to protect @commit_list.
         */
-       struct drm_pending_vblank_event *event;
-};
-
-struct __drm_planes_state {
-       struct drm_plane *ptr;
-       struct drm_plane_state *state;
-};
-
-struct __drm_crtcs_state {
-       struct drm_crtc *ptr;
-       struct drm_crtc_state *state;
-       struct drm_crtc_commit *commit;
-};
-
-struct __drm_connnectors_state {
-       struct drm_connector *ptr;
-       struct drm_connector_state *state;
-};
-
-/**
- * struct drm_atomic_state - the global state object for atomic updates
- * @dev: parent DRM device
- * @allow_modeset: allow full modeset
- * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics
- * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL.
- * @planes: pointer to array of structures with per-plane data
- * @crtcs: pointer to array of CRTC pointers
- * @num_connector: size of the @connectors and @connector_states arrays
- * @connectors: pointer to array of structures with per-connector data
- * @acquire_ctx: acquire context for this atomic modeset state update
- */
-struct drm_atomic_state {
-       struct drm_device *dev;
-       bool allow_modeset : 1;
-       bool legacy_cursor_update : 1;
-       bool legacy_set_config : 1;
-       struct __drm_planes_state *planes;
-       struct __drm_crtcs_state *crtcs;
-       int num_connector;
-       struct __drm_connnectors_state *connectors;
-
-       struct drm_modeset_acquire_ctx *acquire_ctx;
+       spinlock_t commit_lock;
 
        /**
-        * @commit_work:
+        * @acquire_ctx:
         *
-        * Work item which can be used by the driver or helpers to execute the
-        * commit without blocking.
+        * Per-CRTC implicit acquire context used by atomic drivers for legacy
+        * IOCTLs, so that atomic drivers can get at the locking acquire
+        * context.
         */
-       struct work_struct commit_work;
+       struct drm_modeset_acquire_ctx *acquire_ctx;
 };
 
-
 /**
  * struct drm_mode_set - new values for a CRTC config change
  * @fb: framebuffer to use for new config
@@ -2237,17 +873,9 @@ struct drm_mode_config_funcs {
         * CRTC index supplied in &drm_event to userspace.
         *
         * The drm core will supply a struct &drm_event in the event
-        * member of each CRTC's &drm_crtc_state structure. This can be handled by the
-        * drm_crtc_send_vblank_event() function, which the driver should call on
-        * the provided event upon completion of the atomic commit. Note that if
-        * the driver supports vblank signalling and timestamping the vblank
-        * counters and timestamps must agree with the ones returned from page
-        * flip events. With the current vblank helper infrastructure this can
-        * be achieved by holding a vblank reference while the page flip is
-        * pending, acquired through drm_crtc_vblank_get() and released with
-        * drm_crtc_vblank_put(). Drivers are free to implement their own vblank
-        * counter and timestamp tracking though, e.g. if they have accurate
-        * timestamp registers in hardware.
+        * member of each CRTC's &drm_crtc_state structure. See the
+        * documentation for &drm_crtc_state for more details about the precise
+        * semantics of this event.
         *
         * NOTE:
         *
@@ -2635,12 +1263,6 @@ struct drm_mode_config {
         * HDMI infoframe aspect ratio setting.
         */
        struct drm_property *aspect_ratio_property;
-       /**
-        * @dirty_info_property: Optional connector property to give userspace a
-        * hint that the DIRTY_FB ioctl should be used.
-        */
-       struct drm_property *dirty_info_property;
-
        /**
         * @degamma_lut_property: Optional CRTC property to set the LUT used to
         * convert the framebuffer's colors to linear gamma.
@@ -2702,43 +1324,7 @@ struct drm_mode_config {
        struct drm_mode_config_helper_funcs *helper_private;
 };
 
-/**
- * drm_for_each_plane_mask - iterate over planes specified by bitmask
- * @plane: the loop cursor
- * @dev: the DRM device
- * @plane_mask: bitmask of plane indices
- *
- * Iterate over all planes specified by bitmask.
- */
-#define drm_for_each_plane_mask(plane, dev, plane_mask) \
-       list_for_each_entry((plane), &(dev)->mode_config.plane_list, head) \
-               for_each_if ((plane_mask) & (1 << drm_plane_index(plane)))
-
-/**
- * drm_for_each_encoder_mask - iterate over encoders specified by bitmask
- * @encoder: the loop cursor
- * @dev: the DRM device
- * @encoder_mask: bitmask of encoder indices
- *
- * Iterate over all encoders specified by bitmask.
- */
-#define drm_for_each_encoder_mask(encoder, dev, encoder_mask) \
-       list_for_each_entry((encoder), &(dev)->mode_config.encoder_list, head) \
-               for_each_if ((encoder_mask) & (1 << drm_encoder_index(encoder)))
-
 #define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
-#define obj_to_connector(x) container_of(x, struct drm_connector, base)
-#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
-#define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
-#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base)
-#define obj_to_property(x) container_of(x, struct drm_property, base)
-#define obj_to_blob(x) container_of(x, struct drm_property_blob, base)
-#define obj_to_plane(x) container_of(x, struct drm_plane, base)
-
-struct drm_prop_enum_list {
-       int type;
-       char *name;
-};
 
 extern __printf(6, 7)
 int drm_crtc_init_with_planes(struct drm_device *dev,
@@ -2756,7 +1342,7 @@ extern void drm_crtc_cleanup(struct drm_crtc *crtc);
  * Given a registered CRTC, return the index of that CRTC within a DRM
  * device's list of CRTCs.
  */
-static inline unsigned int drm_crtc_index(struct drm_crtc *crtc)
+static inline unsigned int drm_crtc_index(const struct drm_crtc *crtc)
 {
        return crtc->index;
 }
@@ -2773,184 +1359,17 @@ static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
        return 1 << drm_crtc_index(crtc);
 }
 
-int drm_connector_init(struct drm_device *dev,
-                      struct drm_connector *connector,
-                      const struct drm_connector_funcs *funcs,
-                      int connector_type);
-int drm_connector_register(struct drm_connector *connector);
-void drm_connector_unregister(struct drm_connector *connector);
-
-extern void drm_connector_cleanup(struct drm_connector *connector);
-static inline unsigned drm_connector_index(struct drm_connector *connector)
-{
-       return connector->index;
-}
-
-extern __printf(5, 6)
-int drm_encoder_init(struct drm_device *dev,
-                    struct drm_encoder *encoder,
-                    const struct drm_encoder_funcs *funcs,
-                    int encoder_type, const char *name, ...);
-
-/**
- * drm_encoder_index - find the index of a registered encoder
- * @encoder: encoder to find index for
- *
- * Given a registered encoder, return the index of that encoder within a DRM
- * device's list of encoders.
- */
-static inline unsigned int drm_encoder_index(struct drm_encoder *encoder)
-{
-       return encoder->index;
-}
-
-/**
- * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
- * @encoder: encoder to test
- * @crtc: crtc to test
- *
- * Return false if @encoder can't be driven by @crtc, true otherwise.
- */
-static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder,
-                                      struct drm_crtc *crtc)
-{
-       return !!(encoder->possible_crtcs & drm_crtc_mask(crtc));
-}
-
-extern __printf(8, 9)
-int drm_universal_plane_init(struct drm_device *dev,
-                            struct drm_plane *plane,
-                            unsigned long possible_crtcs,
-                            const struct drm_plane_funcs *funcs,
-                            const uint32_t *formats,
-                            unsigned int format_count,
-                            enum drm_plane_type type,
-                            const char *name, ...);
-extern int drm_plane_init(struct drm_device *dev,
-                         struct drm_plane *plane,
-                         unsigned long possible_crtcs,
-                         const struct drm_plane_funcs *funcs,
-                         const uint32_t *formats, unsigned int format_count,
-                         bool is_primary);
-extern void drm_plane_cleanup(struct drm_plane *plane);
-
-/**
- * drm_plane_index - find the index of a registered plane
- * @plane: plane to find index for
- *
- * Given a registered plane, return the index of that plane within a DRM
- * device's list of planes.
- */
-static inline unsigned int drm_plane_index(struct drm_plane *plane)
-{
-       return plane->index;
-}
-extern struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx);
-extern void drm_plane_force_disable(struct drm_plane *plane);
 extern void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
                                   int *hdisplay, int *vdisplay);
 extern int drm_crtc_force_disable(struct drm_crtc *crtc);
 extern int drm_crtc_force_disable_all(struct drm_device *dev);
 
-extern void drm_encoder_cleanup(struct drm_encoder *encoder);
-
-extern const char *drm_get_connector_status_name(enum drm_connector_status status);
-extern const char *drm_get_subpixel_order_name(enum subpixel_order order);
-extern const char *drm_get_dpms_name(int val);
-extern const char *drm_get_dvi_i_subconnector_name(int val);
-extern const char *drm_get_dvi_i_select_name(int val);
-extern const char *drm_get_tv_subconnector_name(int val);
-extern const char *drm_get_tv_select_name(int val);
 extern void drm_mode_config_init(struct drm_device *dev);
 extern void drm_mode_config_reset(struct drm_device *dev);
 extern void drm_mode_config_cleanup(struct drm_device *dev);
 
-extern int drm_mode_connector_set_path_property(struct drm_connector *connector,
-                                               const char *path);
-int drm_mode_connector_set_tile_property(struct drm_connector *connector);
-extern int drm_mode_connector_update_edid_property(struct drm_connector *connector,
-                                                  const struct edid *edid);
-
-extern int drm_display_info_set_bus_formats(struct drm_display_info *info,
-                                           const u32 *formats,
-                                           unsigned int num_formats);
-
-static inline bool drm_property_type_is(struct drm_property *property,
-               uint32_t type)
-{
-       /* instanceof for props.. handles extended type vs original types: */
-       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
-               return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
-       return property->flags & type;
-}
-
-extern int drm_object_property_set_value(struct drm_mode_object *obj,
-                                        struct drm_property *property,
-                                        uint64_t val);
-extern int drm_object_property_get_value(struct drm_mode_object *obj,
-                                        struct drm_property *property,
-                                        uint64_t *value);
-extern int drm_framebuffer_init(struct drm_device *dev,
-                               struct drm_framebuffer *fb,
-                               const struct drm_framebuffer_funcs *funcs);
-extern struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
-                                                     uint32_t id);
-extern void drm_framebuffer_remove(struct drm_framebuffer *fb);
-extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
-extern void drm_framebuffer_unregister_private(struct drm_framebuffer *fb);
-
-extern void drm_object_attach_property(struct drm_mode_object *obj,
-                                      struct drm_property *property,
-                                      uint64_t init_val);
-extern struct drm_property *drm_property_create(struct drm_device *dev, int flags,
-                                               const char *name, int num_values);
-extern struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
-                                        const char *name,
-                                        const struct drm_prop_enum_list *props,
-                                        int num_values);
-struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
-                                        int flags, const char *name,
-                                        const struct drm_prop_enum_list *props,
-                                        int num_props,
-                                        uint64_t supported_bits);
-struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
-                                        const char *name,
-                                        uint64_t min, uint64_t max);
-struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
-                                        int flags, const char *name,
-                                        int64_t min, int64_t max);
-struct drm_property *drm_property_create_object(struct drm_device *dev,
-                                        int flags, const char *name, uint32_t type);
-struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
-                                        const char *name);
-struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
-                                                   size_t length,
-                                                   const void *data);
-struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
-                                                   uint32_t id);
-struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob);
-void drm_property_unreference_blob(struct drm_property_blob *blob);
-extern void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
-extern int drm_property_add_enum(struct drm_property *property, int index,
-                                uint64_t value, const char *name);
-extern int drm_mode_create_dvi_i_properties(struct drm_device *dev);
-extern int drm_mode_create_tv_properties(struct drm_device *dev,
-                                        unsigned int num_modes,
-                                        const char * const modes[]);
-extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
-extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
-extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
-extern int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
-
-extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
-                                            struct drm_encoder *encoder);
-extern int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
-                                        int gamma_size);
-
 extern int drm_mode_set_config_internal(struct drm_mode_set *set);
 
-extern uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth);
-
 extern struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev,
                                                         char topology[8]);
 extern struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
@@ -2958,40 +1377,7 @@ extern struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev,
 extern void drm_mode_put_tile_group(struct drm_device *dev,
                                   struct drm_tile_group *tg);
 
-extern int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
-                                      struct drm_property *property,
-                                      uint64_t value);
-
-extern struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev,
-                                                             unsigned int supported_rotations);
-extern unsigned int drm_rotation_simplify(unsigned int rotation,
-                                         unsigned int supported_rotations);
-extern void drm_crtc_enable_color_mgmt(struct drm_crtc *crtc,
-                                      uint degamma_lut_size,
-                                      bool has_ctm,
-                                      uint gamma_lut_size);
-
-int drm_plane_create_zpos_property(struct drm_plane *plane,
-                                  unsigned int zpos,
-                                  unsigned int min, unsigned int max);
-
-int drm_plane_create_zpos_immutable_property(struct drm_plane *plane,
-                                            unsigned int zpos);
-
 /* Helpers */
-struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
-                                            uint32_t id, uint32_t type);
-void drm_mode_object_reference(struct drm_mode_object *obj);
-void drm_mode_object_unreference(struct drm_mode_object *obj);
-
-static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
-               uint32_t id)
-{
-       struct drm_mode_object *mo;
-       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
-       return mo ? obj_to_plane(mo) : NULL;
-}
-
 static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
        uint32_t id)
 {
@@ -3000,120 +1386,6 @@ static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev,
        return mo ? obj_to_crtc(mo) : NULL;
 }
 
-static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
-       uint32_t id)
-{
-       struct drm_mode_object *mo;
-       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
-       return mo ? obj_to_encoder(mo) : NULL;
-}
-
-/**
- * drm_connector_lookup - lookup connector object
- * @dev: DRM device
- * @id: connector object id
- *
- * This function looks up the connector object specified by id
- * add takes a reference to it.
- */
-static inline struct drm_connector *drm_connector_lookup(struct drm_device *dev,
-               uint32_t id)
-{
-       struct drm_mode_object *mo;
-       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR);
-       return mo ? obj_to_connector(mo) : NULL;
-}
-
-static inline struct drm_property *drm_property_find(struct drm_device *dev,
-               uint32_t id)
-{
-       struct drm_mode_object *mo;
-       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
-       return mo ? obj_to_property(mo) : NULL;
-}
-
-/*
- * Extract a degamma/gamma LUT value provided by user and round it to the
- * precision supported by the hardware.
- */
-static inline uint32_t drm_color_lut_extract(uint32_t user_input,
-                                            uint32_t bit_precision)
-{
-       uint32_t val = user_input;
-       uint32_t max = 0xffff >> (16 - bit_precision);
-
-       /* Round only if we're not using full precision. */
-       if (bit_precision < 16) {
-               val += 1UL << (16 - bit_precision - 1);
-               val >>= 16 - bit_precision;
-       }
-
-       return clamp_val(val, 0, max);
-}
-
-/**
- * drm_framebuffer_reference - incr the fb refcnt
- * @fb: framebuffer
- *
- * This functions increments the fb's refcount.
- */
-static inline void drm_framebuffer_reference(struct drm_framebuffer *fb)
-{
-       drm_mode_object_reference(&fb->base);
-}
-
-/**
- * drm_framebuffer_unreference - unref a framebuffer
- * @fb: framebuffer to unref
- *
- * This functions decrements the fb's refcount and frees it if it drops to zero.
- */
-static inline void drm_framebuffer_unreference(struct drm_framebuffer *fb)
-{
-       drm_mode_object_unreference(&fb->base);
-}
-
-/**
- * drm_framebuffer_read_refcount - read the framebuffer reference count.
- * @fb: framebuffer
- *
- * This functions returns the framebuffer's reference count.
- */
-static inline uint32_t drm_framebuffer_read_refcount(struct drm_framebuffer *fb)
-{
-       return atomic_read(&fb->base.refcount.refcount);
-}
-
-/**
- * drm_connector_reference - incr the connector refcnt
- * @connector: connector
- *
- * This function increments the connector's refcount.
- */
-static inline void drm_connector_reference(struct drm_connector *connector)
-{
-       drm_mode_object_reference(&connector->base);
-}
-
-/**
- * drm_connector_unreference - unref a connector
- * @connector: connector to unref
- *
- * This function decrements the connector's refcount and frees it if it drops to zero.
- */
-static inline void drm_connector_unreference(struct drm_connector *connector)
-{
-       drm_mode_object_unreference(&connector->base);
-}
-
-/* Plane list iterator for legacy (overlay only) planes. */
-#define drm_for_each_legacy_plane(plane, dev) \
-       list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) \
-               for_each_if (plane->type == DRM_PLANE_TYPE_OVERLAY)
-
-#define drm_for_each_plane(plane, dev) \
-       list_for_each_entry(plane, &(dev)->mode_config.plane_list, head)
-
 #define drm_for_each_crtc(crtc, dev) \
        list_for_each_entry(crtc, &(dev)->mode_config.crtc_list, head)
 
@@ -3131,67 +1403,4 @@ assert_drm_connector_list_read_locked(struct drm_mode_config *mode_config)
                !drm_modeset_is_locked(&mode_config->connection_mutex));
 }
 
-#define drm_for_each_connector(connector, dev) \
-       for (assert_drm_connector_list_read_locked(&(dev)->mode_config),        \
-            connector = list_first_entry(&(dev)->mode_config.connector_list,   \
-                                         struct drm_connector, head);          \
-            &connector->head != (&(dev)->mode_config.connector_list);          \
-            connector = list_next_entry(connector, head))
-
-#define drm_for_each_encoder(encoder, dev) \
-       list_for_each_entry(encoder, &(dev)->mode_config.encoder_list, head)
-
-#define drm_for_each_fb(fb, dev) \
-       for (WARN_ON(!mutex_is_locked(&(dev)->mode_config.fb_lock)),            \
-            fb = list_first_entry(&(dev)->mode_config.fb_list, \
-                                         struct drm_framebuffer, head);        \
-            &fb->head != (&(dev)->mode_config.fb_list);                        \
-            fb = list_next_entry(fb, head))
-
-/* drm_edid.c */
-bool drm_probe_ddc(struct i2c_adapter *adapter);
-struct edid *drm_get_edid(struct drm_connector *connector,
-                         struct i2c_adapter *adapter);
-struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
-                                    struct i2c_adapter *adapter);
-struct edid *drm_edid_duplicate(const struct edid *edid);
-int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
-
-u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
-enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);
-bool drm_detect_hdmi_monitor(struct edid *edid);
-bool drm_detect_monitor_audio(struct edid *edid);
-bool drm_rgb_quant_range_selectable(struct edid *edid);
-int drm_add_modes_noedid(struct drm_connector *connector,
-                        int hdisplay, int vdisplay);
-void drm_set_preferred_mode(struct drm_connector *connector,
-                           int hpref, int vpref);
-
-int drm_edid_header_is_valid(const u8 *raw_edid);
-bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
-                         bool *edid_corrupt);
-bool drm_edid_is_valid(struct edid *edid);
-void drm_edid_get_monitor_name(struct edid *edid, char *name,
-                              int buflen);
-struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
-                                          int hsize, int vsize, int fresh,
-                                          bool rb);
-
-/* drm_bridge.c */
-extern int drm_bridge_add(struct drm_bridge *bridge);
-extern void drm_bridge_remove(struct drm_bridge *bridge);
-extern struct drm_bridge *of_drm_find_bridge(struct device_node *np);
-extern int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge);
-
-bool drm_bridge_mode_fixup(struct drm_bridge *bridge,
-                       const struct drm_display_mode *mode,
-                       struct drm_display_mode *adjusted_mode);
-void drm_bridge_disable(struct drm_bridge *bridge);
-void drm_bridge_post_disable(struct drm_bridge *bridge);
-void drm_bridge_mode_set(struct drm_bridge *bridge,
-                       struct drm_display_mode *mode,
-                       struct drm_display_mode *adjusted_mode);
-void drm_bridge_pre_enable(struct drm_bridge *bridge);
-void drm_bridge_enable(struct drm_bridge *bridge);
-
 #endif /* __DRM_CRTC_H__ */
index 4b37afa..982c299 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_modeset_helper.h>
 
 extern void drm_helper_disable_unused_functions(struct drm_device *dev);
 extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
@@ -53,11 +54,6 @@ extern bool drm_helper_encoder_in_use(struct drm_encoder *encoder);
 
 extern int drm_helper_connector_dpms(struct drm_connector *connector, int mode);
 
-extern void drm_helper_move_panel_connectors_to_head(struct drm_device *);
-
-extern void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
-                                          const struct drm_mode_fb_cmd2 *mode_cmd);
-
 extern void drm_helper_resume_force_mode(struct drm_device *dev);
 
 int drm_helper_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
diff --git a/include/drm/drm_dp_aux_dev.h b/include/drm/drm_dp_aux_dev.h
deleted file mode 100644 (file)
index 1b76d99..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright © 2015 Intel Corporation
- *
- * 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 AUTHORS OR COPYRIGHT HOLDERS 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:
- *    Rafael Antognolli <rafael.antognolli@intel.com>
- *
- */
-
-#ifndef DRM_DP_AUX_DEV
-#define DRM_DP_AUX_DEV
-
-#include <drm/drm_dp_helper.h>
-
-#ifdef CONFIG_DRM_DP_AUX_CHARDEV
-
-int drm_dp_aux_dev_init(void);
-void drm_dp_aux_dev_exit(void);
-int drm_dp_aux_register_devnode(struct drm_dp_aux *aux);
-void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux);
-
-#else
-
-static inline int drm_dp_aux_dev_init(void)
-{
-       return 0;
-}
-
-static inline void drm_dp_aux_dev_exit(void)
-{
-}
-
-static inline int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
-{
-       return 0;
-}
-
-static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
-{
-}
-
-#endif
-
-#endif
index 63b8bd5..2a79882 100644 (file)
 # define DP_DS_PORT_TYPE_DVI               2
 # define DP_DS_PORT_TYPE_HDMI              3
 # define DP_DS_PORT_TYPE_NON_EDID          4
+# define DP_DS_PORT_TYPE_DP_DUALMODE        5
+# define DP_DS_PORT_TYPE_WIRELESS           6
 # define DP_DS_PORT_HPD                            (1 << 3)
 /* offset 1 for VGA is maximum megapixels per second / 8 */
 /* offset 2 */
-# define DP_DS_VGA_MAX_BPC_MASK                    (3 << 0)
-# define DP_DS_VGA_8BPC                            0
-# define DP_DS_VGA_10BPC                   1
-# define DP_DS_VGA_12BPC                   2
-# define DP_DS_VGA_16BPC                   3
+# define DP_DS_MAX_BPC_MASK                (3 << 0)
+# define DP_DS_8BPC                        0
+# define DP_DS_10BPC                       1
+# define DP_DS_12BPC                       2
+# define DP_DS_16BPC                       3
 
 /* link configuration */
 #define        DP_LINK_BW_SET                      0x100
 #define DP_SOURCE_OUI                      0x300
 #define DP_SINK_OUI                        0x400
 #define DP_BRANCH_OUI                      0x500
+#define DP_BRANCH_ID                        0x503
+#define DP_BRANCH_HW_REV                    0x509
+#define DP_BRANCH_SW_REV                    0x50A
 
 #define DP_SET_POWER                        0x600
 # define DP_SET_POWER_D0                    0x1
@@ -813,6 +818,13 @@ int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_power_down(struct drm_dp_aux *aux, struct drm_dp_link *link);
 int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_downstream_max_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                               const u8 port_cap[4]);
+int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                             const u8 port_cap[4]);
+int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]);
+void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE],
+                            const u8 port_cap[4], struct drm_dp_aux *aux);
 
 void drm_dp_aux_init(struct drm_dp_aux *aux);
 int drm_dp_aux_register(struct drm_dp_aux *aux);
index 919933d..c3a7d44 100644 (file)
@@ -25,6 +25,9 @@
 
 #include <linux/types.h>
 
+struct drm_device;
+struct i2c_adapter;
+
 #define EDID_LENGTH 128
 #define DDC_ADDR 0x50
 #define DDC_ADDR2 0x52 /* E-DDC 1.2 - where DisplayID can hide */
@@ -423,9 +426,36 @@ static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
        return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
 }
 
+bool drm_probe_ddc(struct i2c_adapter *adapter);
 struct edid *drm_do_get_edid(struct drm_connector *connector,
        int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
                              size_t len),
        void *data);
+struct edid *drm_get_edid(struct drm_connector *connector,
+                         struct i2c_adapter *adapter);
+struct edid *drm_get_edid_switcheroo(struct drm_connector *connector,
+                                    struct i2c_adapter *adapter);
+struct edid *drm_edid_duplicate(const struct edid *edid);
+int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
+
+u8 drm_match_cea_mode(const struct drm_display_mode *to_match);
+enum hdmi_picture_aspect drm_get_cea_aspect_ratio(const u8 video_code);
+bool drm_detect_hdmi_monitor(struct edid *edid);
+bool drm_detect_monitor_audio(struct edid *edid);
+bool drm_rgb_quant_range_selectable(struct edid *edid);
+int drm_add_modes_noedid(struct drm_connector *connector,
+                        int hdisplay, int vdisplay);
+void drm_set_preferred_mode(struct drm_connector *connector,
+                           int hpref, int vpref);
+
+int drm_edid_header_is_valid(const u8 *raw_edid);
+bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid,
+                         bool *edid_corrupt);
+bool drm_edid_is_valid(struct edid *edid);
+void drm_edid_get_monitor_name(struct edid *edid, char *name,
+                              int buflen);
+struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev,
+                                          int hsize, int vsize, int fresh,
+                                          bool rb);
 
 #endif /* __DRM_EDID_H__ */
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
new file mode 100644 (file)
index 0000000..387e33a
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_ENCODER_H__
+#define __DRM_ENCODER_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+
+/**
+ * struct drm_encoder_funcs - encoder controls
+ *
+ * Encoders sit between CRTCs and connectors.
+ */
+struct drm_encoder_funcs {
+       /**
+        * @reset:
+        *
+        * Reset encoder hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        */
+       void (*reset)(struct drm_encoder *encoder);
+
+       /**
+        * @destroy:
+        *
+        * Clean up encoder resources. This is only called at driver unload time
+        * through drm_mode_config_cleanup() since an encoder cannot be
+        * hotplugged in DRM.
+        */
+       void (*destroy)(struct drm_encoder *encoder);
+
+       /**
+        * @late_register:
+        *
+        * This optional hook can be used to register additional userspace
+        * interfaces attached to the encoder like debugfs interfaces.
+        * It is called late in the driver load sequence from drm_dev_register().
+        * Everything added from this callback should be unregistered in
+        * the early_unregister callback.
+        *
+        * Returns:
+        *
+        * 0 on success, or a negative error code on failure.
+        */
+       int (*late_register)(struct drm_encoder *encoder);
+
+       /**
+        * @early_unregister:
+        *
+        * This optional hook should be used to unregister the additional
+        * userspace interfaces attached to the encoder from
+        * late_unregister(). It is called from drm_dev_unregister(),
+        * early in the driver unload sequence to disable userspace access
+        * before data structures are torndown.
+        */
+       void (*early_unregister)(struct drm_encoder *encoder);
+};
+
+/**
+ * struct drm_encoder - central DRM encoder structure
+ * @dev: parent DRM device
+ * @head: list management
+ * @base: base KMS object
+ * @name: human readable name, can be overwritten by the driver
+ * @crtc: currently bound CRTC
+ * @bridge: bridge associated to the encoder
+ * @funcs: control functions
+ * @helper_private: mid-layer private data
+ *
+ * CRTCs drive pixels to encoders, which convert them into signals
+ * appropriate for a given connector or set of connectors.
+ */
+struct drm_encoder {
+       struct drm_device *dev;
+       struct list_head head;
+
+       struct drm_mode_object base;
+       char *name;
+       /**
+        * @encoder_type:
+        *
+        * One of the DRM_MODE_ENCODER_<foo> types in drm_mode.h. The following
+        * encoder types are defined thus far:
+        *
+        * - DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A.
+        *
+        * - DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort.
+        *
+        * - DRM_MODE_ENCODER_LVDS for display panels, or in general any panel
+        *   with a proprietary parallel connector.
+        *
+        * - DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video,
+        *   Component, SCART).
+        *
+        * - DRM_MODE_ENCODER_VIRTUAL for virtual machine displays
+        *
+        * - DRM_MODE_ENCODER_DSI for panels connected using the DSI serial bus.
+        *
+        * - DRM_MODE_ENCODER_DPI for panels connected using the DPI parallel
+        *   bus.
+        *
+        * - DRM_MODE_ENCODER_DPMST for special fake encoders used to allow
+        *   mutliple DP MST streams to share one physical encoder.
+        */
+       int encoder_type;
+
+       /**
+        * @index: Position inside the mode_config.list, can be used as an array
+        * index. It is invariant over the lifetime of the encoder.
+        */
+       unsigned index;
+
+       /**
+        * @possible_crtcs: Bitmask of potential CRTC bindings, using
+        * drm_crtc_index() as the index into the bitfield. The driver must set
+        * the bits for all &drm_crtc objects this encoder can be connected to
+        * before calling drm_encoder_init().
+        *
+        * In reality almost every driver gets this wrong.
+        *
+        * Note that since CRTC objects can't be hotplugged the assigned indices
+        * are stable and hence known before registering all objects.
+        */
+       uint32_t possible_crtcs;
+
+       /**
+        * @possible_clones: Bitmask of potential sibling encoders for cloning,
+        * using drm_encoder_index() as the index into the bitfield. The driver
+        * must set the bits for all &drm_encoder objects which can clone a
+        * &drm_crtc together with this encoder before calling
+        * drm_encoder_init(). Drivers should set the bit representing the
+        * encoder itself, too. Cloning bits should be set such that when two
+        * encoders can be used in a cloned configuration, they both should have
+        * each another bits set.
+        *
+        * In reality almost every driver gets this wrong.
+        *
+        * Note that since encoder objects can't be hotplugged the assigned indices
+        * are stable and hence known before registering all objects.
+        */
+       uint32_t possible_clones;
+
+       struct drm_crtc *crtc;
+       struct drm_bridge *bridge;
+       const struct drm_encoder_funcs *funcs;
+       const struct drm_encoder_helper_funcs *helper_private;
+};
+
+#define obj_to_encoder(x) container_of(x, struct drm_encoder, base)
+
+__printf(5, 6)
+int drm_encoder_init(struct drm_device *dev,
+                    struct drm_encoder *encoder,
+                    const struct drm_encoder_funcs *funcs,
+                    int encoder_type, const char *name, ...);
+
+/**
+ * drm_encoder_index - find the index of a registered encoder
+ * @encoder: encoder to find index for
+ *
+ * Given a registered encoder, return the index of that encoder within a DRM
+ * device's list of encoders.
+ */
+static inline unsigned int drm_encoder_index(struct drm_encoder *encoder)
+{
+       return encoder->index;
+}
+
+/* FIXME: We have an include file mess still, drm_crtc.h needs untangling. */
+static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc);
+
+/**
+ * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
+ * @encoder: encoder to test
+ * @crtc: crtc to test
+ *
+ * Returns false if @encoder can't be driven by @crtc, true otherwise.
+ */
+static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder,
+                                      struct drm_crtc *crtc)
+{
+       return !!(encoder->possible_crtcs & drm_crtc_mask(crtc));
+}
+
+/**
+ * drm_encoder_find - find a &drm_encoder
+ * @dev: DRM device
+ * @id: encoder id
+ *
+ * Returns the encoder with @id, NULL if it doesn't exist. Simple wrapper around
+ * drm_mode_object_find().
+ */
+static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev,
+                                                  uint32_t id)
+{
+       struct drm_mode_object *mo;
+
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+
+       return mo ? obj_to_encoder(mo) : NULL;
+}
+
+void drm_encoder_cleanup(struct drm_encoder *encoder);
+
+/**
+ * drm_for_each_encoder_mask - iterate over encoders specified by bitmask
+ * @encoder: the loop cursor
+ * @dev: the DRM device
+ * @encoder_mask: bitmask of encoder indices
+ *
+ * Iterate over all encoders specified by bitmask.
+ */
+#define drm_for_each_encoder_mask(encoder, dev, encoder_mask) \
+       list_for_each_entry((encoder), &(dev)->mode_config.encoder_list, head) \
+               for_each_if ((encoder_mask) & (1 << drm_encoder_index(encoder)))
+
+/**
+ * drm_for_each_encoder - iterate over all encoders
+ * @encoder: the loop cursor
+ * @dev: the DRM device
+ *
+ * Iterate over all encoders of @dev.
+ */
+#define drm_for_each_encoder(encoder, dev) \
+       list_for_each_entry(encoder, &(dev)->mode_config.encoder_list, head)
+
+#endif
index db8d478..ed8edfe 100644 (file)
@@ -32,6 +32,7 @@
 
 struct drm_fb_helper;
 
+#include <drm/drm_crtc.h>
 #include <linux/kgdb.h>
 
 enum mode_set_atomic {
@@ -176,6 +177,7 @@ struct drm_fb_helper_connector {
  *              the screen buffer
  * @dirty_lock: spinlock protecting @dirty_clip
  * @dirty_work: worker used to flush the framebuffer
+ * @resume_work: worker used during resume if the console lock is already taken
  *
  * This is the main structure used by the fbdev helpers. Drivers supporting
  * fbdev emulation should embedded this into their overall driver structure.
@@ -196,6 +198,7 @@ struct drm_fb_helper {
        struct drm_clip_rect dirty_clip;
        spinlock_t dirty_lock;
        struct work_struct dirty_work;
+       struct work_struct resume_work;
 
        /**
         * @kernel_fb_list:
@@ -214,8 +217,20 @@ struct drm_fb_helper {
        bool delayed_hotplug;
 };
 
+/**
+ * define DRM_FB_HELPER_DEFAULT_OPS - helper define for drm drivers
+ *
+ * Helper define to register default implementations of drm_fb_helper
+ * functions. To be used in struct fb_ops of drm drivers.
+ */
+#define DRM_FB_HELPER_DEFAULT_OPS \
+       .fb_check_var   = drm_fb_helper_check_var, \
+       .fb_set_par     = drm_fb_helper_set_par, \
+       .fb_setcmap     = drm_fb_helper_setcmap, \
+       .fb_blank       = drm_fb_helper_blank, \
+       .fb_pan_display = drm_fb_helper_pan_display
+
 #ifdef CONFIG_DRM_FBDEV_EMULATION
-int drm_fb_helper_modinit(void);
 void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
                           const struct drm_fb_helper_funcs *funcs);
 int drm_fb_helper_init(struct drm_device *dev,
@@ -263,7 +278,9 @@ void drm_fb_helper_cfb_copyarea(struct fb_info *info,
 void drm_fb_helper_cfb_imageblit(struct fb_info *info,
                                 const struct fb_image *image);
 
-void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, int state);
+void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend);
+void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
+                                       bool suspend);
 
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 
@@ -283,11 +300,6 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_
 int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
                                       struct drm_connector *connector);
 #else
-static inline int drm_fb_helper_modinit(void)
-{
-       return 0;
-}
-
 static inline void drm_fb_helper_prepare(struct drm_device *dev,
                                        struct drm_fb_helper *helper,
                                        const struct drm_fb_helper_funcs *funcs)
@@ -417,7 +429,12 @@ static inline void drm_fb_helper_cfb_imageblit(struct fb_info *info,
 }
 
 static inline void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper,
-                                            int state)
+                                            bool suspend)
+{
+}
+
+static inline void
+drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper, bool suspend)
 {
 }
 
@@ -475,5 +492,18 @@ drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
 {
        return 0;
 }
+
+#endif
+
+static inline int
+drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a,
+                                             const char *name, bool primary)
+{
+#if IS_REACHABLE(CONFIG_FB)
+       return remove_conflicting_framebuffers(a, name, primary);
+#else
+       return 0;
 #endif
+}
+
 #endif
index 7f90a39..30c30fa 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/types.h>
 #include <uapi/drm/drm_fourcc.h>
 
+uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth);
 void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp);
 int drm_format_num_planes(uint32_t format);
 int drm_format_plane_cpp(uint32_t format, int plane);
@@ -32,6 +33,6 @@ int drm_format_horz_chroma_subsampling(uint32_t format);
 int drm_format_vert_chroma_subsampling(uint32_t format);
 int drm_format_plane_width(int width, uint32_t format, int plane);
 int drm_format_plane_height(int height, uint32_t format, int plane);
-const char *drm_get_format_name(uint32_t format);
+char *drm_get_format_name(uint32_t format) __malloc;
 
 #endif /* __DRM_FOURCC_H__ */
diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h
new file mode 100644 (file)
index 0000000..f5ae1f4
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_FRAMEBUFFER_H__
+#define __DRM_FRAMEBUFFER_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+
+struct drm_framebuffer;
+struct drm_file;
+struct drm_device;
+
+/**
+ * struct drm_framebuffer_funcs - framebuffer hooks
+ */
+struct drm_framebuffer_funcs {
+       /**
+        * @destroy:
+        *
+        * Clean up framebuffer resources, specifically also unreference the
+        * backing storage. The core guarantees to call this function for every
+        * framebuffer successfully created by ->fb_create() in
+        * &drm_mode_config_funcs. Drivers must also call
+        * drm_framebuffer_cleanup() to release DRM core resources for this
+        * framebuffer.
+        */
+       void (*destroy)(struct drm_framebuffer *framebuffer);
+
+       /**
+        * @create_handle:
+        *
+        * Create a buffer handle in the driver-specific buffer manager (either
+        * GEM or TTM) valid for the passed-in struct &drm_file. This is used by
+        * the core to implement the GETFB IOCTL, which returns (for
+        * sufficiently priviledged user) also a native buffer handle. This can
+        * be used for seamless transitions between modesetting clients by
+        * copying the current screen contents to a private buffer and blending
+        * between that and the new contents.
+        *
+        * GEM based drivers should call drm_gem_handle_create() to create the
+        * handle.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*create_handle)(struct drm_framebuffer *fb,
+                            struct drm_file *file_priv,
+                            unsigned int *handle);
+       /**
+        * @dirty:
+        *
+        * Optional callback for the dirty fb IOCTL.
+        *
+        * Userspace can notify the driver via this callback that an area of the
+        * framebuffer has changed and should be flushed to the display
+        * hardware. This can also be used internally, e.g. by the fbdev
+        * emulation, though that's not the case currently.
+        *
+        * See documentation in drm_mode.h for the struct drm_mode_fb_dirty_cmd
+        * for more information as all the semantics and arguments have a one to
+        * one mapping on this function.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*dirty)(struct drm_framebuffer *framebuffer,
+                    struct drm_file *file_priv, unsigned flags,
+                    unsigned color, struct drm_clip_rect *clips,
+                    unsigned num_clips);
+};
+
+/**
+ * struct drm_framebuffer - frame buffer object
+ *
+ * Note that the fb is refcounted for the benefit of driver internals,
+ * for example some hw, disabling a CRTC/plane is asynchronous, and
+ * scanout does not actually complete until the next vblank.  So some
+ * cleanup (like releasing the reference(s) on the backing GEM bo(s))
+ * should be deferred.  In cases like this, the driver would like to
+ * hold a ref to the fb even though it has already been removed from
+ * userspace perspective. See drm_framebuffer_reference() and
+ * drm_framebuffer_unreference().
+ *
+ * The refcount is stored inside the mode object @base.
+ */
+struct drm_framebuffer {
+       /**
+        * @dev: DRM device this framebuffer belongs to
+        */
+       struct drm_device *dev;
+       /**
+        * @head: Place on the dev->mode_config.fb_list, access protected by
+        * dev->mode_config.fb_lock.
+        */
+       struct list_head head;
+
+       /**
+        * @base: base modeset object structure, contains the reference count.
+        */
+       struct drm_mode_object base;
+       /**
+        * @funcs: framebuffer vfunc table
+        */
+       const struct drm_framebuffer_funcs *funcs;
+       /**
+        * @pitches: Line stride per buffer. For userspace created object this
+        * is copied from drm_mode_fb_cmd2.
+        */
+       unsigned int pitches[4];
+       /**
+        * @offsets: Offset from buffer start to the actual pixel data in bytes,
+        * per buffer. For userspace created object this is copied from
+        * drm_mode_fb_cmd2.
+        *
+        * Note that this is a linear offset and does not take into account
+        * tiling or buffer laytou per @modifier. It meant to be used when the
+        * actual pixel data for this framebuffer plane starts at an offset,
+        * e.g.  when multiple planes are allocated within the same backing
+        * storage buffer object. For tiled layouts this generally means it
+        * @offsets must at least be tile-size aligned, but hardware often has
+        * stricter requirements.
+        *
+        * This should not be used to specifiy x/y pixel offsets into the buffer
+        * data (even for linear buffers). Specifying an x/y pixel offset is
+        * instead done through the source rectangle in struct &drm_plane_state.
+        */
+       unsigned int offsets[4];
+       /**
+        * @modifier: Data layout modifier, per buffer. This is used to describe
+        * tiling, or also special layouts (like compression) of auxiliary
+        * buffers. For userspace created object this is copied from
+        * drm_mode_fb_cmd2.
+        */
+       uint64_t modifier[4];
+       /**
+        * @width: Logical width of the visible area of the framebuffer, in
+        * pixels.
+        */
+       unsigned int width;
+       /**
+        * @height: Logical height of the visible area of the framebuffer, in
+        * pixels.
+        */
+       unsigned int height;
+       /**
+        * @depth: Depth in bits per pixel for RGB formats. 0 for everything
+        * else. Legacy information derived from @pixel_format, it's suggested to use
+        * the DRM FOURCC codes and helper functions directly instead.
+        */
+       unsigned int depth;
+       /**
+        * @bits_per_pixel: Storage used bits per pixel for RGB formats. 0 for
+        * everything else. Legacy information derived from @pixel_format, it's
+        * suggested to use the DRM FOURCC codes and helper functions directly
+        * instead.
+        */
+       int bits_per_pixel;
+       /**
+        * @flags: Framebuffer flags like DRM_MODE_FB_INTERLACED or
+        * DRM_MODE_FB_MODIFIERS.
+        */
+       int flags;
+       /**
+        * @pixel_format: DRM FOURCC code describing the pixel format.
+        */
+       uint32_t pixel_format; /* fourcc format */
+       /**
+        * @hot_x: X coordinate of the cursor hotspot. Used by the legacy cursor
+        * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
+        * universal plane.
+        */
+       int hot_x;
+       /**
+        * @hot_y: Y coordinate of the cursor hotspot. Used by the legacy cursor
+        * IOCTL when the driver supports cursor through a DRM_PLANE_TYPE_CURSOR
+        * universal plane.
+        */
+       int hot_y;
+       /**
+        * @filp_head: Placed on struct &drm_file fbs list_head, protected by
+        * fbs_lock in the same structure.
+        */
+       struct list_head filp_head;
+};
+
+#define obj_to_fb(x) container_of(x, struct drm_framebuffer, base)
+
+int drm_framebuffer_init(struct drm_device *dev,
+                        struct drm_framebuffer *fb,
+                        const struct drm_framebuffer_funcs *funcs);
+struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev,
+                                              uint32_t id);
+void drm_framebuffer_remove(struct drm_framebuffer *fb);
+void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
+void drm_framebuffer_unregister_private(struct drm_framebuffer *fb);
+
+/**
+ * drm_framebuffer_reference - incr the fb refcnt
+ * @fb: framebuffer
+ *
+ * This functions increments the fb's refcount.
+ */
+static inline void drm_framebuffer_reference(struct drm_framebuffer *fb)
+{
+       drm_mode_object_reference(&fb->base);
+}
+
+/**
+ * drm_framebuffer_unreference - unref a framebuffer
+ * @fb: framebuffer to unref
+ *
+ * This functions decrements the fb's refcount and frees it if it drops to zero.
+ */
+static inline void drm_framebuffer_unreference(struct drm_framebuffer *fb)
+{
+       drm_mode_object_unreference(&fb->base);
+}
+
+/**
+ * drm_framebuffer_read_refcount - read the framebuffer reference count.
+ * @fb: framebuffer
+ *
+ * This functions returns the framebuffer's reference count.
+ */
+static inline uint32_t drm_framebuffer_read_refcount(struct drm_framebuffer *fb)
+{
+       return atomic_read(&fb->base.refcount.refcount);
+}
+
+/**
+ * drm_for_each_fb - iterate over all framebuffers
+ * @fb: the loop cursor
+ * @dev: the DRM device
+ *
+ * Iterate over all framebuffers of @dev. User must hold the fb_lock from
+ * &drm_mode_config.
+ */
+#define drm_for_each_fb(fb, dev) \
+       for (WARN_ON(!mutex_is_locked(&(dev)->mode_config.fb_lock)),            \
+            fb = list_first_entry(&(dev)->mode_config.fb_list, \
+                                         struct drm_framebuffer, head);        \
+            &fb->head != (&(dev)->mode_config.fb_list);                        \
+            fb = list_next_entry(fb, head))
+#endif
index fca1cd1..9f63736 100644 (file)
@@ -210,8 +210,8 @@ drm_gem_object_reference(struct drm_gem_object *obj)
  * drm_gem_object_unreference_unlocked().
  *
  * Drivers should never call this directly in their code. Instead they should
- * wrap it up into a driver_gem_object_unreference(struct driver_gem_object
- * *obj) wrapper function, and use that. Shared code should never call this, to
+ * wrap it up into a ``driver_gem_object_unreference(struct driver_gem_object
+ * *obj)`` wrapper function, and use that. Shared code should never call this, to
  * avoid breaking drivers by accident which still depend upon dev->struct_mutex
  * locking.
  */
index 47ac925..4fef190 100644 (file)
@@ -265,11 +265,15 @@ int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
                                    u16 end);
 int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
                                  u16 end);
-int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline);
 int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi);
 int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
                             enum mipi_dsi_dcs_tear_mode mode);
 int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format);
+int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline);
+int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
+                                       u16 brightness);
+int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
+                                       u16 *brightness);
 
 /**
  * struct mipi_dsi_driver - DSI driver
index fc65118..205ddcf 100644 (file)
@@ -37,6 +37,7 @@
  * Generic range manager structs
  */
 #include <linux/bug.h>
+#include <linux/rbtree.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/spinlock.h>
@@ -61,6 +62,7 @@ enum drm_mm_allocator_flags {
 struct drm_mm_node {
        struct list_head node_list;
        struct list_head hole_stack;
+       struct rb_node rb;
        unsigned hole_follows : 1;
        unsigned scanned_block : 1;
        unsigned scanned_prev_free : 1;
@@ -70,6 +72,7 @@ struct drm_mm_node {
        unsigned long color;
        u64 start;
        u64 size;
+       u64 __subtree_last;
        struct drm_mm *mm;
 };
 
@@ -79,6 +82,9 @@ struct drm_mm {
        /* head_node.node_list is the list of all memory nodes, ordered
         * according to the (increasing) start address of the memory node. */
        struct drm_mm_node head_node;
+       /* Keep an interval_tree for fast lookup of drm_mm_nodes by address. */
+       struct rb_root interval_tree;
+
        unsigned int scan_check_range : 1;
        unsigned scan_alignment;
        unsigned long scan_color;
@@ -295,6 +301,12 @@ void drm_mm_init(struct drm_mm *mm,
 void drm_mm_takedown(struct drm_mm *mm);
 bool drm_mm_clean(struct drm_mm *mm);
 
+struct drm_mm_node *
+drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last);
+
+struct drm_mm_node *
+drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last);
+
 void drm_mm_init_scan(struct drm_mm *mm,
                      u64 size,
                      unsigned alignment,
diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h
new file mode 100644 (file)
index 0000000..43460b2
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_MODESET_H__
+#define __DRM_MODESET_H__
+
+#include <linux/kref.h>
+struct drm_object_properties;
+struct drm_property;
+struct drm_device;
+
+/**
+ * struct drm_mode_object - base structure for modeset objects
+ * @id: userspace visible identifier
+ * @type: type of the object, one of DRM_MODE_OBJECT\_\*
+ * @properties: properties attached to this object, including values
+ * @refcount: reference count for objects which with dynamic lifetime
+ * @free_cb: free function callback, only set for objects with dynamic lifetime
+ *
+ * Base structure for modeset objects visible to userspace. Objects can be
+ * looked up using drm_mode_object_find(). Besides basic uapi interface
+ * properties like @id and @type it provides two services:
+ *
+ * - It tracks attached properties and their values. This is used by &drm_crtc,
+ *   &drm_plane and &drm_connector. Properties are attached by calling
+ *   drm_object_attach_property() before the object is visible to userspace.
+ *
+ * - For objects with dynamic lifetimes (as indicated by a non-NULL @free_cb) it
+ *   provides reference counting through drm_mode_object_reference() and
+ *   drm_mode_object_unreference(). This is used by &drm_framebuffer,
+ *   &drm_connector and &drm_property_blob. These objects provide specialized
+ *   reference counting wrappers.
+ */
+struct drm_mode_object {
+       uint32_t id;
+       uint32_t type;
+       struct drm_object_properties *properties;
+       struct kref refcount;
+       void (*free_cb)(struct kref *kref);
+};
+
+#define DRM_OBJECT_MAX_PROPERTY 24
+/**
+ * struct drm_object_properties - property tracking for &drm_mode_object
+ */
+struct drm_object_properties {
+       /**
+        * @count: number of valid properties, must be less than or equal to
+        * DRM_OBJECT_MAX_PROPERTY.
+        */
+
+       int count;
+       /**
+        * @properties: Array of pointers to &drm_property.
+        *
+        * NOTE: if we ever start dynamically destroying properties (ie.
+        * not at drm_mode_config_cleanup() time), then we'd have to do
+        * a better job of detaching property from mode objects to avoid
+        * dangling property pointers:
+        */
+       struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY];
+
+       /**
+        * @values: Array to store the property values, matching @properties. Do
+        * not read/write values directly, but use
+        * drm_object_property_get_value() and drm_object_property_set_value().
+        *
+        * Note that atomic drivers do not store mutable properties in this
+        * array, but only the decoded values in the corresponding state
+        * structure. The decoding is done using the ->atomic_get_property and
+        * ->atomic_set_property hooks of the corresponding object. Hence atomic
+        * drivers should not use drm_object_property_set_value() and
+        * drm_object_property_get_value() on mutable objects, i.e. those
+        * without the DRM_MODE_PROP_IMMUTABLE flag set.
+        */
+       uint64_t values[DRM_OBJECT_MAX_PROPERTY];
+};
+
+/* Avoid boilerplate.  I'm tired of typing. */
+#define DRM_ENUM_NAME_FN(fnname, list)                         \
+       const char *fnname(int val)                             \
+       {                                                       \
+               int i;                                          \
+               for (i = 0; i < ARRAY_SIZE(list); i++) {        \
+                       if (list[i].type == val)                \
+                               return list[i].name;            \
+               }                                               \
+               return "(unknown)";                             \
+       }
+
+struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
+                                            uint32_t id, uint32_t type);
+void drm_mode_object_reference(struct drm_mode_object *obj);
+void drm_mode_object_unreference(struct drm_mode_object *obj);
+
+int drm_object_property_set_value(struct drm_mode_object *obj,
+                                 struct drm_property *property,
+                                 uint64_t val);
+int drm_object_property_get_value(struct drm_mode_object *obj,
+                                 struct drm_property *property,
+                                 uint64_t *value);
+
+void drm_object_attach_property(struct drm_mode_object *obj,
+                               struct drm_property *property,
+                               uint64_t init_val);
+#endif
index ff48177..9934d91 100644 (file)
 #ifndef __DRM_MODES_H__
 #define __DRM_MODES_H__
 
+#include <linux/hdmi.h>
+
+#include <drm/drm_mode_object.h>
+#include <drm/drm_connector.h>
+
+struct videomode;
+
 /*
  * Note on terminology:  here, for brevity and convenience, we refer to connector
  * control chips as 'CRTCs'.  They can control any type of connector, VGA, LVDS,
@@ -400,20 +407,7 @@ struct drm_display_mode {
        enum hdmi_picture_aspect picture_aspect_ratio;
 };
 
-/* mode specified on the command line */
-struct drm_cmdline_mode {
-       bool specified;
-       bool refresh_specified;
-       bool bpp_specified;
-       int xres, yres;
-       int bpp;
-       int refresh;
-       bool rb;
-       bool interlace;
-       bool cvt;
-       bool margins;
-       enum drm_connector_force force;
-};
+#define obj_to_mode(x) container_of(x, struct drm_display_mode, base)
 
 /**
  * drm_mode_is_stereo - check for stereo mode flags
@@ -434,7 +428,7 @@ struct drm_cmdline_mode;
 struct drm_display_mode *drm_mode_create(struct drm_device *dev);
 void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode);
 void drm_mode_convert_to_umode(struct drm_mode_modeinfo *out,
-                               const struct drm_display_mode *in);
+                              const struct drm_display_mode *in);
 int drm_mode_convert_umode(struct drm_display_mode *out,
                           const struct drm_mode_modeinfo *in);
 void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
@@ -457,8 +451,9 @@ void drm_display_mode_from_videomode(const struct videomode *vm,
                                     struct drm_display_mode *dmode);
 void drm_display_mode_to_videomode(const struct drm_display_mode *dmode,
                                   struct videomode *vm);
+void drm_bus_flags_from_videomode(const struct videomode *vm, u32 *bus_flags);
 int of_get_drm_display_mode(struct device_node *np,
-                           struct drm_display_mode *dmode,
+                           struct drm_display_mode *dmode, u32 *bus_flags,
                            int index);
 
 void drm_mode_set_name(struct drm_display_mode *mode);
diff --git a/include/drm/drm_modeset_helper.h b/include/drm/drm_modeset_helper.h
new file mode 100644 (file)
index 0000000..b8051d5
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_KMS_HELPER_H__
+#define __DRM_KMS_HELPER_H__
+
+#include <drm/drmP.h>
+
+void drm_helper_move_panel_connectors_to_head(struct drm_device *);
+
+void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
+                                   const struct drm_mode_fb_cmd2 *mode_cmd);
+
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+                 const struct drm_crtc_funcs *funcs);
+
+#endif
index b55f218..10e449c 100644 (file)
@@ -266,6 +266,8 @@ struct drm_crtc_helper_funcs {
         * disable anything at the CRTC level. To ensure that runtime PM
         * handling (using either DPMS or the new "ACTIVE" property) works
         * @disable must be the inverse of @enable for atomic drivers.
+        * Atomic drivers should consider to use @atomic_disable instead of
+        * this one.
         *
         * NOTE:
         *
@@ -391,6 +393,28 @@ struct drm_crtc_helper_funcs {
         */
        void (*atomic_flush)(struct drm_crtc *crtc,
                             struct drm_crtc_state *old_crtc_state);
+
+       /**
+        * @atomic_disable:
+        *
+        * This callback should be used to disable the CRTC. With the atomic
+        * drivers it is called after all encoders connected to this CRTC have
+        * been shut off already using their own ->disable hook. If that
+        * sequence is too simple drivers can just add their own hooks and call
+        * it from this CRTC callback here by looping over all encoders
+        * connected to it using for_each_encoder_on_crtc().
+        *
+        * This hook is used only by atomic helpers. Atomic drivers don't
+        * need to implement it if there's no need to disable anything at the
+        * CRTC level.
+        *
+        * Comparing to @disable, this one provides the additional input
+        * parameter @old_crtc_state which could be used to access the old
+        * state. Atomic drivers should consider to use this one instead
+        * of @disable.
+        */
+       void (*atomic_disable)(struct drm_crtc *crtc,
+                              struct drm_crtc_state *old_crtc_state);
 };
 
 /**
@@ -523,11 +547,40 @@ struct drm_encoder_helper_funcs {
         *
         * This callback is used both by the legacy CRTC helpers and the atomic
         * modeset helpers. It is optional in the atomic helpers.
+        *
+        * NOTE:
+        *
+        * If the driver uses the atomic modeset helpers and needs to inspect
+        * the connector state or connector display info during mode setting,
+        * @atomic_mode_set can be used instead.
         */
        void (*mode_set)(struct drm_encoder *encoder,
                         struct drm_display_mode *mode,
                         struct drm_display_mode *adjusted_mode);
 
+       /**
+        * @atomic_mode_set:
+        *
+        * This callback is used to update the display mode of an encoder.
+        *
+        * Note that the display pipe is completely off when this function is
+        * called. Drivers which need hardware to be running before they program
+        * the new display mode (because they implement runtime PM) should not
+        * use this hook, because the helper library calls it only once and not
+        * every time the display pipeline is suspended using either DPMS or the
+        * new "ACTIVE" property. Such drivers should instead move all their
+        * encoder setup into the ->enable() callback.
+        *
+        * This callback is used by the atomic modeset helpers in place of the
+        * @mode_set callback, if set by the driver. It is optional and should
+        * be used instead of @mode_set if the driver needs to inspect the
+        * connector state or display info, since there is no direct way to
+        * go from the encoder to the current connector.
+        */
+       void (*atomic_mode_set)(struct drm_encoder *encoder,
+                               struct drm_crtc_state *crtc_state,
+                               struct drm_connector_state *conn_state);
+
        /**
         * @get_crtc:
         *
@@ -826,7 +879,7 @@ struct drm_plane_helper_funcs {
         * everything else must complete successfully.
         */
        int (*prepare_fb)(struct drm_plane *plane,
-                         const struct drm_plane_state *new_state);
+                         struct drm_plane_state *new_state);
        /**
         * @cleanup_fb:
         *
@@ -837,7 +890,7 @@ struct drm_plane_helper_funcs {
         * transitional plane helpers, but it is optional.
         */
        void (*cleanup_fb)(struct drm_plane *plane,
-                          const struct drm_plane_state *old_state);
+                          struct drm_plane_state *old_state);
 
        /**
         * @atomic_check:
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
new file mode 100644 (file)
index 0000000..43cf193
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_PLANE_H__
+#define __DRM_PLANE_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+
+struct drm_crtc;
+
+/**
+ * struct drm_plane_state - mutable plane state
+ * @plane: backpointer to the plane
+ * @crtc: currently bound CRTC, NULL if disabled
+ * @fb: currently bound framebuffer
+ * @fence: optional fence to wait for before scanning out @fb
+ * @crtc_x: left position of visible portion of plane on crtc
+ * @crtc_y: upper position of visible portion of plane on crtc
+ * @crtc_w: width of visible portion of plane on crtc
+ * @crtc_h: height of visible portion of plane on crtc
+ * @src_x: left position of visible portion of plane within
+ *     plane (in 16.16)
+ * @src_y: upper position of visible portion of plane within
+ *     plane (in 16.16)
+ * @src_w: width of visible portion of plane (in 16.16)
+ * @src_h: height of visible portion of plane (in 16.16)
+ * @rotation: rotation of the plane
+ * @zpos: priority of the given plane on crtc (optional)
+ * @normalized_zpos: normalized value of zpos: unique, range from 0 to N-1
+ *     where N is the number of active planes for given crtc
+ * @src: clipped source coordinates of the plane (in 16.16)
+ * @dst: clipped destination coordinates of the plane
+ * @visible: visibility of the plane
+ * @state: backpointer to global drm_atomic_state
+ */
+struct drm_plane_state {
+       struct drm_plane *plane;
+
+       struct drm_crtc *crtc;   /* do not write directly, use drm_atomic_set_crtc_for_plane() */
+       struct drm_framebuffer *fb;  /* do not write directly, use drm_atomic_set_fb_for_plane() */
+       struct fence *fence;
+
+       /* Signed dest location allows it to be partially off screen */
+       int32_t crtc_x, crtc_y;
+       uint32_t crtc_w, crtc_h;
+
+       /* Source values are 16.16 fixed point */
+       uint32_t src_x, src_y;
+       uint32_t src_h, src_w;
+
+       /* Plane rotation */
+       unsigned int rotation;
+
+       /* Plane zpos */
+       unsigned int zpos;
+       unsigned int normalized_zpos;
+
+       /* Clipped coordinates */
+       struct drm_rect src, dst;
+
+       /*
+        * Is the plane actually visible? Can be false even
+        * if fb!=NULL and crtc!=NULL, due to clipping.
+        */
+       bool visible;
+
+       struct drm_atomic_state *state;
+};
+
+
+/**
+ * struct drm_plane_funcs - driver plane control functions
+ */
+struct drm_plane_funcs {
+       /**
+        * @update_plane:
+        *
+        * This is the legacy entry point to enable and configure the plane for
+        * the given CRTC and framebuffer. It is never called to disable the
+        * plane, i.e. the passed-in crtc and fb paramters are never NULL.
+        *
+        * The source rectangle in frame buffer memory coordinates is given by
+        * the src_x, src_y, src_w and src_h parameters (as 16.16 fixed point
+        * values). Devices that don't support subpixel plane coordinates can
+        * ignore the fractional part.
+        *
+        * The destination rectangle in CRTC coordinates is given by the
+        * crtc_x, crtc_y, crtc_w and crtc_h parameters (as integer values).
+        * Devices scale the source rectangle to the destination rectangle. If
+        * scaling is not supported, and the source rectangle size doesn't match
+        * the destination rectangle size, the driver must return a
+        * -<errorname>EINVAL</errorname> error.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_update_plane() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*update_plane)(struct drm_plane *plane,
+                           struct drm_crtc *crtc, struct drm_framebuffer *fb,
+                           int crtc_x, int crtc_y,
+                           unsigned int crtc_w, unsigned int crtc_h,
+                           uint32_t src_x, uint32_t src_y,
+                           uint32_t src_w, uint32_t src_h);
+
+       /**
+        * @disable_plane:
+        *
+        * This is the legacy entry point to disable the plane. The DRM core
+        * calls this method in response to a DRM_IOCTL_MODE_SETPLANE IOCTL call
+        * with the frame buffer ID set to 0.  Disabled planes must not be
+        * processed by the CRTC.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_disable_plane() to implement this hook.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*disable_plane)(struct drm_plane *plane);
+
+       /**
+        * @destroy:
+        *
+        * Clean up plane resources. This is only called at driver unload time
+        * through drm_mode_config_cleanup() since a plane cannot be hotplugged
+        * in DRM.
+        */
+       void (*destroy)(struct drm_plane *plane);
+
+       /**
+        * @reset:
+        *
+        * Reset plane hardware and software state to off. This function isn't
+        * called by the core directly, only through drm_mode_config_reset().
+        * It's not a helper hook only for historical reasons.
+        *
+        * Atomic drivers can use drm_atomic_helper_plane_reset() to reset
+        * atomic state using this hook.
+        */
+       void (*reset)(struct drm_plane *plane);
+
+       /**
+        * @set_property:
+        *
+        * This is the legacy entry point to update a property attached to the
+        * plane.
+        *
+        * Drivers implementing atomic modeset should use
+        * drm_atomic_helper_plane_set_property() to implement this hook.
+        *
+        * This callback is optional if the driver does not support any legacy
+        * driver-private properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success or a negative error code on failure.
+        */
+       int (*set_property)(struct drm_plane *plane,
+                           struct drm_property *property, uint64_t val);
+
+       /**
+        * @atomic_duplicate_state:
+        *
+        * Duplicate the current atomic state for this plane and return it.
+        * The core and helpers gurantee that any atomic state duplicated with
+        * this hook and still owned by the caller (i.e. not transferred to the
+        * driver by calling ->atomic_commit() from struct
+        * &drm_mode_config_funcs) will be cleaned up by calling the
+        * @atomic_destroy_state hook in this structure.
+        *
+        * Atomic drivers which don't subclass struct &drm_plane_state should use
+        * drm_atomic_helper_plane_duplicate_state(). Drivers that subclass the
+        * state structure to extend it with driver-private state should use
+        * __drm_atomic_helper_plane_duplicate_state() to make sure shared state is
+        * duplicated in a consistent fashion across drivers.
+        *
+        * It is an error to call this hook before plane->state has been
+        * initialized correctly.
+        *
+        * NOTE:
+        *
+        * If the duplicate state references refcounted resources this hook must
+        * acquire a reference for each of them. The driver must release these
+        * references again in @atomic_destroy_state.
+        *
+        * RETURNS:
+        *
+        * Duplicated atomic state or NULL when the allocation failed.
+        */
+       struct drm_plane_state *(*atomic_duplicate_state)(struct drm_plane *plane);
+
+       /**
+        * @atomic_destroy_state:
+        *
+        * Destroy a state duplicated with @atomic_duplicate_state and release
+        * or unreference all resources it references
+        */
+       void (*atomic_destroy_state)(struct drm_plane *plane,
+                                    struct drm_plane_state *state);
+
+       /**
+        * @atomic_set_property:
+        *
+        * Decode a driver-private property value and store the decoded value
+        * into the passed-in state structure. Since the atomic core decodes all
+        * standardized properties (even for extensions beyond the core set of
+        * properties which might not be implemented by all drivers) this
+        * requires drivers to subclass the state structure.
+        *
+        * Such driver-private properties should really only be implemented for
+        * truly hardware/vendor specific state. Instead it is preferred to
+        * standardize atomic extension and decode the properties used to expose
+        * such an extension in the core.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_plane_set_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * NOTE:
+        *
+        * This function is called in the state assembly phase of atomic
+        * modesets, which can be aborted for any reason (including on
+        * userspace's request to just check whether a configuration would be
+        * possible). Drivers MUST NOT touch any persistent state (hardware or
+        * software) or data structures except the passed in @state parameter.
+        *
+        * Also since userspace controls in which order properties are set this
+        * function must not do any input validation (since the state update is
+        * incomplete and hence likely inconsistent). Instead any such input
+        * validation must be done in the various atomic_check callbacks.
+        *
+        * RETURNS:
+        *
+        * 0 if the property has been found, -EINVAL if the property isn't
+        * implemented by the driver (which shouldn't ever happen, the core only
+        * asks for properties attached to this plane). No other validation is
+        * allowed by the driver. The core already checks that the property
+        * value is within the range (integer, valid enum value, ...) the driver
+        * set when registering the property.
+        */
+       int (*atomic_set_property)(struct drm_plane *plane,
+                                  struct drm_plane_state *state,
+                                  struct drm_property *property,
+                                  uint64_t val);
+
+       /**
+        * @atomic_get_property:
+        *
+        * Reads out the decoded driver-private property. This is used to
+        * implement the GETPLANE IOCTL.
+        *
+        * Do not call this function directly, use
+        * drm_atomic_plane_get_property() instead.
+        *
+        * This callback is optional if the driver does not support any
+        * driver-private atomic properties.
+        *
+        * RETURNS:
+        *
+        * 0 on success, -EINVAL if the property isn't implemented by the
+        * driver (which should never happen, the core only asks for
+        * properties attached to this plane).
+        */
+       int (*atomic_get_property)(struct drm_plane *plane,
+                                  const struct drm_plane_state *state,
+                                  struct drm_property *property,
+                                  uint64_t *val);
+       /**
+        * @late_register:
+        *
+        * This optional hook can be used to register additional userspace
+        * interfaces attached to the plane like debugfs interfaces.
+        * It is called late in the driver load sequence from drm_dev_register().
+        * Everything added from this callback should be unregistered in
+        * the early_unregister callback.
+        *
+        * Returns:
+        *
+        * 0 on success, or a negative error code on failure.
+        */
+       int (*late_register)(struct drm_plane *plane);
+
+       /**
+        * @early_unregister:
+        *
+        * This optional hook should be used to unregister the additional
+        * userspace interfaces attached to the plane from
+        * late_unregister(). It is called from drm_dev_unregister(),
+        * early in the driver unload sequence to disable userspace access
+        * before data structures are torndown.
+        */
+       void (*early_unregister)(struct drm_plane *plane);
+};
+
+/**
+ * enum drm_plane_type - uapi plane type enumeration
+ *
+ * For historical reasons not all planes are made the same. This enumeration is
+ * used to tell the different types of planes apart to implement the different
+ * uapi semantics for them. For userspace which is universal plane aware and
+ * which is using that atomic IOCTL there's no difference between these planes
+ * (beyong what the driver and hardware can support of course).
+ *
+ * For compatibility with legacy userspace, only overlay planes are made
+ * available to userspace by default. Userspace clients may set the
+ * DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that they
+ * wish to receive a universal plane list containing all plane types. See also
+ * drm_for_each_legacy_plane().
+ *
+ * WARNING: The values of this enum is UABI since they're exposed in the "type"
+ * property.
+ */
+enum drm_plane_type {
+       /**
+        * @DRM_PLANE_TYPE_OVERLAY:
+        *
+        * Overlay planes represent all non-primary, non-cursor planes. Some
+        * drivers refer to these types of planes as "sprites" internally.
+        */
+       DRM_PLANE_TYPE_OVERLAY,
+
+       /**
+        * @DRM_PLANE_TYPE_PRIMARY:
+        *
+        * Primary planes represent a "main" plane for a CRTC.  Primary planes
+        * are the planes operated upon by CRTC modesetting and flipping
+        * operations described in the page_flip and set_config hooks in struct
+        * &drm_crtc_funcs.
+        */
+       DRM_PLANE_TYPE_PRIMARY,
+
+       /**
+        * @DRM_PLANE_TYPE_CURSOR:
+        *
+        * Cursor planes represent a "cursor" plane for a CRTC.  Cursor planes
+        * are the planes operated upon by the DRM_IOCTL_MODE_CURSOR and
+        * DRM_IOCTL_MODE_CURSOR2 IOCTLs.
+        */
+       DRM_PLANE_TYPE_CURSOR,
+};
+
+
+/**
+ * struct drm_plane - central DRM plane control structure
+ * @dev: DRM device this plane belongs to
+ * @head: for list management
+ * @name: human readable name, can be overwritten by the driver
+ * @base: base mode object
+ * @possible_crtcs: pipes this plane can be bound to
+ * @format_types: array of formats supported by this plane
+ * @format_count: number of formats supported
+ * @format_default: driver hasn't supplied supported formats for the plane
+ * @crtc: currently bound CRTC
+ * @fb: currently bound fb
+ * @old_fb: Temporary tracking of the old fb while a modeset is ongoing. Used by
+ *     drm_mode_set_config_internal() to implement correct refcounting.
+ * @funcs: helper functions
+ * @properties: property tracking for this plane
+ * @type: type of plane (overlay, primary, cursor)
+ * @state: current atomic state for this plane
+ * @zpos_property: zpos property for this plane
+ * @helper_private: mid-layer private data
+ */
+struct drm_plane {
+       struct drm_device *dev;
+       struct list_head head;
+
+       char *name;
+
+       /**
+        * @mutex:
+        *
+        * Protects modeset plane state, together with the mutex of &drm_crtc
+        * this plane is linked to (when active, getting actived or getting
+        * disabled).
+        */
+       struct drm_modeset_lock mutex;
+
+       struct drm_mode_object base;
+
+       uint32_t possible_crtcs;
+       uint32_t *format_types;
+       unsigned int format_count;
+       bool format_default;
+
+       struct drm_crtc *crtc;
+       struct drm_framebuffer *fb;
+
+       struct drm_framebuffer *old_fb;
+
+       const struct drm_plane_funcs *funcs;
+
+       struct drm_object_properties properties;
+
+       enum drm_plane_type type;
+
+       /**
+        * @index: Position inside the mode_config.list, can be used as an array
+        * index. It is invariant over the lifetime of the plane.
+        */
+       unsigned index;
+
+       const struct drm_plane_helper_funcs *helper_private;
+
+       struct drm_plane_state *state;
+
+       struct drm_property *zpos_property;
+};
+
+#define obj_to_plane(x) container_of(x, struct drm_plane, base)
+
+extern __printf(8, 9)
+int drm_universal_plane_init(struct drm_device *dev,
+                            struct drm_plane *plane,
+                            unsigned long possible_crtcs,
+                            const struct drm_plane_funcs *funcs,
+                            const uint32_t *formats,
+                            unsigned int format_count,
+                            enum drm_plane_type type,
+                            const char *name, ...);
+extern int drm_plane_init(struct drm_device *dev,
+                         struct drm_plane *plane,
+                         unsigned long possible_crtcs,
+                         const struct drm_plane_funcs *funcs,
+                         const uint32_t *formats, unsigned int format_count,
+                         bool is_primary);
+extern void drm_plane_cleanup(struct drm_plane *plane);
+
+/**
+ * drm_plane_index - find the index of a registered plane
+ * @plane: plane to find index for
+ *
+ * Given a registered plane, return the index of that plane within a DRM
+ * device's list of planes.
+ */
+static inline unsigned int drm_plane_index(struct drm_plane *plane)
+{
+       return plane->index;
+}
+extern struct drm_plane * drm_plane_from_index(struct drm_device *dev, int idx);
+extern void drm_plane_force_disable(struct drm_plane *plane);
+
+int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
+                                      struct drm_property *property,
+                                      uint64_t value);
+
+/**
+ * drm_plane_find - find a &drm_plane
+ * @dev: DRM device
+ * @id: plane id
+ *
+ * Returns the plane with @id, NULL if it doesn't exist. Simple wrapper around
+ * drm_mode_object_find().
+ */
+static inline struct drm_plane *drm_plane_find(struct drm_device *dev,
+               uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE);
+       return mo ? obj_to_plane(mo) : NULL;
+}
+
+/**
+ * drm_for_each_plane_mask - iterate over planes specified by bitmask
+ * @plane: the loop cursor
+ * @dev: the DRM device
+ * @plane_mask: bitmask of plane indices
+ *
+ * Iterate over all planes specified by bitmask.
+ */
+#define drm_for_each_plane_mask(plane, dev, plane_mask) \
+       list_for_each_entry((plane), &(dev)->mode_config.plane_list, head) \
+               for_each_if ((plane_mask) & (1 << drm_plane_index(plane)))
+
+/**
+ * drm_for_each_legacy_plane - iterate over all planes for legacy userspace
+ * @plane: the loop cursor
+ * @dev: the DRM device
+ *
+ * Iterate over all legacy planes of @dev, excluding primary and cursor planes.
+ * This is useful for implementing userspace apis when userspace is not
+ * universal plane aware. See also enum &drm_plane_type.
+ */
+#define drm_for_each_legacy_plane(plane, dev) \
+       list_for_each_entry(plane, &(dev)->mode_config.plane_list, head) \
+               for_each_if (plane->type == DRM_PLANE_TYPE_OVERLAY)
+
+/**
+ * drm_for_each_plane - iterate over all planes
+ * @plane: the loop cursor
+ * @dev: the DRM device
+ *
+ * Iterate over all planes of @dev, include primary and cursor planes.
+ */
+#define drm_for_each_plane(plane, dev) \
+       list_for_each_entry(plane, &(dev)->mode_config.plane_list, head)
+
+
+#endif
index 0e0c357..c189596 100644 (file)
@@ -27,6 +27,7 @@
 #include <drm/drm_rect.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_modeset_helper.h>
 
 /*
  * Drivers that don't allow primary plane scaling may pass this macro in place
  */
 #define DRM_PLANE_HELPER_NO_SCALING (1<<16)
 
-int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
-                 const struct drm_crtc_funcs *funcs);
-
+int drm_plane_helper_check_state(struct drm_plane_state *state,
+                                const struct drm_rect *clip,
+                                int min_scale, int max_scale,
+                                bool can_position,
+                                bool can_update_disabled);
 int drm_plane_helper_check_update(struct drm_plane *plane,
                                  struct drm_crtc *crtc,
                                  struct drm_framebuffer *fb,
diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h
new file mode 100644 (file)
index 0000000..43c4b6a
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2016 Intel Corporation
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef __DRM_PROPERTY_H__
+#define __DRM_PROPERTY_H__
+
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <drm/drm_mode_object.h>
+
+/**
+ * struct drm_property_enum - symbolic values for enumerations
+ * @value: numeric property value for this enum entry
+ * @head: list of enum values, linked to enum_list in &drm_property
+ * @name: symbolic name for the enum
+ *
+ * For enumeration and bitmask properties this structure stores the symbolic
+ * decoding for each value. This is used for example for the rotation property.
+ */
+struct drm_property_enum {
+       uint64_t value;
+       struct list_head head;
+       char name[DRM_PROP_NAME_LEN];
+};
+
+/**
+ * struct drm_property - modeset object property
+ *
+ * This structure represent a modeset object property. It combines both the name
+ * of the property with the set of permissible values. This means that when a
+ * driver wants to use a property with the same name on different objects, but
+ * with different value ranges, then it must create property for each one. An
+ * example would be rotation of &drm_plane, when e.g. the primary plane cannot
+ * be rotated. But if both the name and the value range match, then the same
+ * property structure can be instantiated multiple times for the same object.
+ * Userspace must be able to cope with this and cannot assume that the same
+ * symbolic property will have the same modeset object ID on all modeset
+ * objects.
+ *
+ * Properties are created by one of the special functions, as explained in
+ * detail in the @flags structure member.
+ *
+ * To actually expose a property it must be attached to each object using
+ * drm_object_attach_property(). Currently properties can only be attached to
+ * &drm_connector, &drm_crtc and &drm_plane.
+ *
+ * Properties are also used as the generic metadatatransport for the atomic
+ * IOCTL. Everything that was set directly in structures in the legacy modeset
+ * IOCTLs (like the plane source or destination windows, or e.g. the links to
+ * the CRTC) is exposed as a property with the DRM_MODE_PROP_ATOMIC flag set.
+ */
+struct drm_property {
+       /**
+        * @head: per-device list of properties, for cleanup.
+        */
+       struct list_head head;
+
+       /**
+        * @base: base KMS object
+        */
+       struct drm_mode_object base;
+
+       /**
+        * @flags:
+        *
+        * Property flags and type. A property needs to be one of the following
+        * types:
+        *
+        * DRM_MODE_PROP_RANGE
+        *     Range properties report their minimum and maximum admissible unsigned values.
+        *     The KMS core verifies that values set by application fit in that
+        *     range. The range is unsigned. Range properties are created using
+        *     drm_property_create_range().
+        *
+        * DRM_MODE_PROP_SIGNED_RANGE
+        *     Range properties report their minimum and maximum admissible unsigned values.
+        *     The KMS core verifies that values set by application fit in that
+        *     range. The range is signed. Range properties are created using
+        *     drm_property_create_signed_range().
+        *
+        * DRM_MODE_PROP_ENUM
+        *     Enumerated properties take a numerical value that ranges from 0 to
+        *     the number of enumerated values defined by the property minus one,
+        *     and associate a free-formed string name to each value. Applications
+        *     can retrieve the list of defined value-name pairs and use the
+        *     numerical value to get and set property instance values. Enum
+        *     properties are created using drm_property_create_enum().
+        *
+        * DRM_MODE_PROP_BITMASK
+        *     Bitmask properties are enumeration properties that additionally
+        *     restrict all enumerated values to the 0..63 range. Bitmask property
+        *     instance values combine one or more of the enumerated bits defined
+        *     by the property. Bitmask properties are created using
+        *     drm_property_create_bitmask().
+        *
+        * DRM_MODE_PROB_OBJECT
+        *     Object properties are used to link modeset objects. This is used
+        *     extensively in the atomic support to create the display pipeline,
+        *     by linking &drm_framebuffer to &drm_plane, &drm_plane to
+        *     &drm_crtc and &drm_connector to &drm_crtc. An object property can
+        *     only link to a specific type of &drm_mode_object, this limit is
+        *     enforced by the core. Object properties are created using
+        *     drm_property_create_object().
+        *
+        *     Object properties work like blob properties, but in a more
+        *     general fashion. They are limited to atomic drivers and must have
+        *     the DRM_MODE_PROP_ATOMIC flag set.
+        *
+        * DRM_MODE_PROP_BLOB
+        *     Blob properties store a binary blob without any format restriction.
+        *     The binary blobs are created as KMS standalone objects, and blob
+        *     property instance values store the ID of their associated blob
+        *     object. Blob properties are created by calling
+        *     drm_property_create() with DRM_MODE_PROP_BLOB as the type.
+        *
+        *     Actual blob objects to contain blob data are created using
+        *     drm_property_create_blob(), or through the corresponding IOCTL.
+        *
+        *     Besides the built-in limit to only accept blob objects blob
+        *     properties work exactly like object properties. The only reasons
+        *     blob properties exist is backwards compatibility with existing
+        *     userspace.
+        *
+        * In addition a property can have any combination of the below flags:
+        *
+        * DRM_MODE_PROP_ATOMIC
+        *     Set for properties which encode atomic modeset state. Such
+        *     properties are not exposed to legacy userspace.
+        *
+        * DRM_MODE_PROP_IMMUTABLE
+        *     Set for properties where userspace cannot be changed by
+        *     userspace. The kernel is allowed to update the value of these
+        *     properties. This is generally used to expose probe state to
+        *     usersapce, e.g. the EDID, or the connector path property on DP
+        *     MST sinks.
+        */
+       uint32_t flags;
+
+       /**
+        * @name: symbolic name of the properties
+        */
+       char name[DRM_PROP_NAME_LEN];
+
+       /**
+        * @num_values: size of the @values array.
+        */
+       uint32_t num_values;
+
+       /**
+        * @values:
+        *
+        * Array with limits and values for the property. The
+        * interpretation of these limits is dependent upon the type per @flags.
+        */
+       uint64_t *values;
+
+       /**
+        * @dev: DRM device
+        */
+       struct drm_device *dev;
+
+       /**
+        * @enum_list:
+        *
+        * List of &drm_prop_enum_list structures with the symbolic names for
+        * enum and bitmask values.
+        */
+       struct list_head enum_list;
+};
+
+/**
+ * struct drm_property_blob - Blob data for &drm_property
+ * @base: base KMS object
+ * @dev: DRM device
+ * @head_global: entry on the global blob list in &drm_mode_config
+ *     property_blob_list.
+ * @head_file: entry on the per-file blob list in &drm_file blobs list.
+ * @length: size of the blob in bytes, invariant over the lifetime of the object
+ * @data: actual data, embedded at the end of this structure
+ *
+ * Blobs are used to store bigger values than what fits directly into the 64
+ * bits available for a &drm_property.
+ *
+ * Blobs are reference counted using drm_property_reference_blob() and
+ * drm_property_unreference_blob(). They are created using
+ * drm_property_create_blob().
+ */
+struct drm_property_blob {
+       struct drm_mode_object base;
+       struct drm_device *dev;
+       struct list_head head_global;
+       struct list_head head_file;
+       size_t length;
+       unsigned char data[];
+};
+
+struct drm_prop_enum_list {
+       int type;
+       char *name;
+};
+
+#define obj_to_property(x) container_of(x, struct drm_property, base)
+#define obj_to_blob(x) container_of(x, struct drm_property_blob, base)
+
+/**
+ * drm_property_type_is - check the type of a property
+ * @property: property to check
+ * @type: property type to compare with
+ *
+ * This is a helper function becauase the uapi encoding of property types is
+ * a bit special for historical reasons.
+ */
+static inline bool drm_property_type_is(struct drm_property *property,
+                                       uint32_t type)
+{
+       /* instanceof for props.. handles extended type vs original types: */
+       if (property->flags & DRM_MODE_PROP_EXTENDED_TYPE)
+               return (property->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
+       return property->flags & type;
+}
+
+struct drm_property *drm_property_create(struct drm_device *dev, int flags,
+                                        const char *name, int num_values);
+struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+                                             const char *name,
+                                             const struct drm_prop_enum_list *props,
+                                             int num_values);
+struct drm_property *drm_property_create_bitmask(struct drm_device *dev,
+                                                int flags, const char *name,
+                                                const struct drm_prop_enum_list *props,
+                                                int num_props,
+                                                uint64_t supported_bits);
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+                                              const char *name,
+                                              uint64_t min, uint64_t max);
+struct drm_property *drm_property_create_signed_range(struct drm_device *dev,
+                                                     int flags, const char *name,
+                                                     int64_t min, int64_t max);
+struct drm_property *drm_property_create_object(struct drm_device *dev,
+                                               int flags, const char *name, uint32_t type);
+struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags,
+                                             const char *name);
+int drm_property_add_enum(struct drm_property *property, int index,
+                         uint64_t value, const char *name);
+void drm_property_destroy(struct drm_device *dev, struct drm_property *property);
+
+struct drm_property_blob *drm_property_create_blob(struct drm_device *dev,
+                                                  size_t length,
+                                                  const void *data);
+struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev,
+                                                  uint32_t id);
+int drm_property_replace_global_blob(struct drm_device *dev,
+                                    struct drm_property_blob **replace,
+                                    size_t length,
+                                    const void *data,
+                                    struct drm_mode_object *obj_holds_id,
+                                    struct drm_property *prop_holds_id);
+struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob);
+void drm_property_unreference_blob(struct drm_property_blob *blob);
+
+/**
+ * drm_connector_find - find property object
+ * @dev: DRM device
+ * @id: property object id
+ *
+ * This function looks up the property object specified by id and returns it.
+ */
+static inline struct drm_property *drm_property_find(struct drm_device *dev,
+                                                    uint32_t id)
+{
+       struct drm_mode_object *mo;
+       mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY);
+       return mo ? obj_to_property(mo) : NULL;
+}
+
+#endif
index 2690397..01a8436 100644 (file)
@@ -60,9 +60,35 @@ struct drm_simple_display_pipe_funcs {
         *
         * This function is called when the underlying plane state is updated.
         * This hook is optional.
+        *
+        * This is the function drivers should submit the
+        * &drm_pending_vblank_event from. Using either
+        * drm_crtc_arm_vblank_event(), when the driver supports vblank
+        * interrupt handling, or drm_crtc_send_vblank_event() directly in case
+        * the hardware lacks vblank support entirely.
         */
        void (*update)(struct drm_simple_display_pipe *pipe,
                       struct drm_plane_state *plane_state);
+
+       /**
+        * @prepare_fb:
+        *
+        * Optional, called by struct &drm_plane_helper_funcs ->prepare_fb .
+        * Please read the documentation for the ->prepare_fb hook in
+        * struct &drm_plane_helper_funcs for more details.
+        */
+       int (*prepare_fb)(struct drm_simple_display_pipe *pipe,
+                         struct drm_plane_state *plane_state);
+
+       /**
+        * @cleanup_fb:
+        *
+        * Optional, called by struct &drm_plane_helper_funcs ->cleanup_fb .
+        * Please read the documentation for the ->cleanup_fb hook in
+        * struct &drm_plane_helper_funcs for more details.
+        */
+       void (*cleanup_fb)(struct drm_simple_display_pipe *pipe,
+                          struct drm_plane_state *plane_state);
 };
 
 /**
@@ -85,6 +111,11 @@ struct drm_simple_display_pipe {
        const struct drm_simple_display_pipe_funcs *funcs;
 };
 
+int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
+                                         struct drm_bridge *bridge);
+
+void drm_simple_display_pipe_detach_bridge(struct drm_simple_display_pipe *pipe);
+
 int drm_simple_display_pipe_init(struct drm_device *dev,
                        struct drm_simple_display_pipe *pipe,
                        const struct drm_simple_display_pipe_funcs *funcs,
index 06ea8e0..9c03895 100644 (file)
  */
 
 #include <drm/drm_mm.h>
-#include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/module.h>
 #include <linux/rbtree.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 
+struct drm_file;
+
 struct drm_vma_offset_file {
        struct rb_node vm_rb;
-       struct file *vm_filp;
+       struct drm_file *vm_tag;
        unsigned long vm_count;
 };
 
 struct drm_vma_offset_node {
        rwlock_t vm_lock;
        struct drm_mm_node vm_node;
-       struct rb_node vm_rb;
        struct rb_root vm_files;
 };
 
 struct drm_vma_offset_manager {
        rwlock_t vm_lock;
-       struct rb_root vm_addr_space_rb;
        struct drm_mm vm_addr_space_mm;
 };
 
@@ -62,10 +61,11 @@ int drm_vma_offset_add(struct drm_vma_offset_manager *mgr,
 void drm_vma_offset_remove(struct drm_vma_offset_manager *mgr,
                           struct drm_vma_offset_node *node);
 
-int drm_vma_node_allow(struct drm_vma_offset_node *node, struct file *filp);
-void drm_vma_node_revoke(struct drm_vma_offset_node *node, struct file *filp);
+int drm_vma_node_allow(struct drm_vma_offset_node *node, struct drm_file *tag);
+void drm_vma_node_revoke(struct drm_vma_offset_node *node,
+                        struct drm_file *tag);
 bool drm_vma_node_is_allowed(struct drm_vma_offset_node *node,
-                            struct file *filp);
+                            struct drm_file *tag);
 
 /**
  * drm_vma_offset_exact_lookup_locked() - Look up node by exact address
@@ -216,9 +216,9 @@ static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
 /**
  * drm_vma_node_verify_access() - Access verification helper for TTM
  * @node: Offset node
- * @filp: Open-file
+ * @tag: Tag of file to check
  *
- * This checks whether @filp is granted access to @node. It is the same as
+ * This checks whether @tag is granted access to @node. It is the same as
  * drm_vma_node_is_allowed() but suitable as drop-in helper for TTM
  * verify_access() callbacks.
  *
@@ -226,9 +226,9 @@ static inline void drm_vma_node_unmap(struct drm_vma_offset_node *node,
  * 0 if access is granted, -EACCES otherwise.
  */
 static inline int drm_vma_node_verify_access(struct drm_vma_offset_node *node,
-                                            struct file *filp)
+                                            struct drm_file *tag)
 {
-       return drm_vma_node_is_allowed(node, filp) ? 0 : -EACCES;
+       return drm_vma_node_is_allowed(node, tag) ? 0 : -EACCES;
 }
 
 #endif /* __DRM_VMA_MANAGER_H__ */
index 3e419d9..a254830 100644 (file)
@@ -1,6 +1,24 @@
 #ifndef __DRM_I2C_TDA998X_H__
 #define __DRM_I2C_TDA998X_H__
 
+#include <linux/hdmi.h>
+#include <dt-bindings/display/tda998x.h>
+
+enum {
+       AFMT_UNUSED =   0,
+       AFMT_SPDIF =    TDA998x_SPDIF,
+       AFMT_I2S =      TDA998x_I2S,
+};
+
+struct tda998x_audio_params {
+       u8 config;
+       u8 format;
+       unsigned sample_width;
+       unsigned sample_rate;
+       struct hdmi_audio_infoframe cea;
+       u8 status[5];
+};
+
 struct tda998x_encoder_params {
        u8 swap_b:3;
        u8 mirr_b:1;
@@ -15,16 +33,7 @@ struct tda998x_encoder_params {
        u8 swap_e:3;
        u8 mirr_e:1;
 
-       u8 audio_cfg;
-       u8 audio_clk_cfg;
-       u8 audio_frame[6];
-
-       enum {
-               AFMT_SPDIF,
-               AFMT_I2S
-       } audio_format;
-
-       unsigned audio_sample_rate;
+       struct tda998x_audio_params audio_params;
 };
 
 #endif
index b1755f8..4e1b274 100644 (file)
@@ -93,6 +93,6 @@ extern bool i915_gpu_turbo_disable(void);
 #define    I845_TSEG_SIZE_1M   (3 << 1)
 
 #define INTEL_BSM 0x5c
-#define   INTEL_BSM_MASK (0xFFFF << 20)
+#define   INTEL_BSM_MASK       (-(1u << 20))
 
 #endif                         /* _I915_DRM_H_ */
index 33466bf..0d5f426 100644 (file)
 #define INTEL_IVB_Q_IDS(info) \
        INTEL_QUANTA_VGA_DEVICE(info) /* Quanta transcode */
 
-#define INTEL_HSW_D_IDS(info) \
+#define INTEL_HSW_IDS(info) \
        INTEL_VGA_DEVICE(0x0402, info), /* GT1 desktop */ \
        INTEL_VGA_DEVICE(0x0412, info), /* GT2 desktop */ \
        INTEL_VGA_DEVICE(0x0422, info), /* GT3 desktop */ \
        INTEL_VGA_DEVICE(0x0D2B, info), /* CRW GT3 reserved */ \
        INTEL_VGA_DEVICE(0x0D0E, info), /* CRW GT1 reserved */ \
        INTEL_VGA_DEVICE(0x0D1E, info), /* CRW GT2 reserved */ \
-       INTEL_VGA_DEVICE(0x0D2E, info)  /* CRW GT3 reserved */ \
-
-#define INTEL_HSW_M_IDS(info) \
+       INTEL_VGA_DEVICE(0x0D2E, info),  /* CRW GT3 reserved */ \
        INTEL_VGA_DEVICE(0x0406, info), /* GT1 mobile */ \
        INTEL_VGA_DEVICE(0x0416, info), /* GT2 mobile */ \
        INTEL_VGA_DEVICE(0x0426, info), /* GT2 mobile */ \
        INTEL_VGA_DEVICE(0x0D16, info), /* CRW GT2 mobile */ \
        INTEL_VGA_DEVICE(0x0D26, info)  /* CRW GT3 mobile */
 
-#define INTEL_VLV_M_IDS(info) \
+#define INTEL_VLV_IDS(info) \
        INTEL_VGA_DEVICE(0x0f30, info), \
        INTEL_VGA_DEVICE(0x0f31, info), \
        INTEL_VGA_DEVICE(0x0f32, info), \
        INTEL_VGA_DEVICE(0x0f33, info), \
-       INTEL_VGA_DEVICE(0x0157, info)
-
-#define INTEL_VLV_D_IDS(info) \
+       INTEL_VGA_DEVICE(0x0157, info), \
        INTEL_VGA_DEVICE(0x0155, info)
 
-#define INTEL_BDW_GT12M_IDS(info)  \
+#define INTEL_BDW_GT12_IDS(info)  \
        INTEL_VGA_DEVICE(0x1602, info), /* GT1 ULT */ \
        INTEL_VGA_DEVICE(0x1606, info), /* GT1 ULT */ \
        INTEL_VGA_DEVICE(0x160B, info), /* GT1 Iris */ \
        INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \
        INTEL_VGA_DEVICE(0x1616, info), /* GT2 ULT */ \
        INTEL_VGA_DEVICE(0x161B, info), /* GT2 ULT */ \
-       INTEL_VGA_DEVICE(0x161E, info)  /* GT2 ULX */
-
-#define INTEL_BDW_GT12D_IDS(info) \
+       INTEL_VGA_DEVICE(0x161E, info),  /* GT2 ULX */ \
        INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \
        INTEL_VGA_DEVICE(0x160D, info), /* GT1 Workstation */ \
        INTEL_VGA_DEVICE(0x161A, info), /* GT2 Server */ \
        INTEL_VGA_DEVICE(0x161D, info)  /* GT2 Workstation */
 
-#define INTEL_BDW_GT3M_IDS(info) \
+#define INTEL_BDW_GT3_IDS(info) \
        INTEL_VGA_DEVICE(0x1622, info), /* ULT */ \
        INTEL_VGA_DEVICE(0x1626, info), /* ULT */ \
        INTEL_VGA_DEVICE(0x162B, info), /* Iris */ \
-       INTEL_VGA_DEVICE(0x162E, info)  /* ULX */
-
-#define INTEL_BDW_GT3D_IDS(info) \
+       INTEL_VGA_DEVICE(0x162E, info),  /* ULX */\
        INTEL_VGA_DEVICE(0x162A, info), /* Server */ \
        INTEL_VGA_DEVICE(0x162D, info)  /* Workstation */
 
        INTEL_VGA_DEVICE(0x163A, info), /* Server */ \
        INTEL_VGA_DEVICE(0x163D, info)  /* Workstation */
 
-#define INTEL_BDW_M_IDS(info) \
-       INTEL_BDW_GT12M_IDS(info), \
-       INTEL_BDW_GT3M_IDS(info), \
-       INTEL_BDW_RSVDM_IDS(info)
-
-#define INTEL_BDW_D_IDS(info) \
-       INTEL_BDW_GT12D_IDS(info), \
-       INTEL_BDW_GT3D_IDS(info), \
+#define INTEL_BDW_IDS(info) \
+       INTEL_BDW_GT12_IDS(info), \
+       INTEL_BDW_GT3_IDS(info), \
+       INTEL_BDW_RSVDM_IDS(info), \
+       INTEL_BDW_GT12_IDS(info), \
+       INTEL_BDW_GT3_IDS(info), \
        INTEL_BDW_RSVDD_IDS(info)
 
 #define INTEL_CHV_IDS(info) \
index 6f2c598..9eb940d 100644 (file)
@@ -45,37 +45,7 @@ struct ttm_bo_device;
 
 struct drm_mm_node;
 
-/**
- * struct ttm_place
- *
- * @fpfn:      first valid page frame number to put the object
- * @lpfn:      last valid page frame number to put the object
- * @flags:     memory domain and caching flags for the object
- *
- * Structure indicating a possible place to put an object.
- */
-struct ttm_place {
-       unsigned        fpfn;
-       unsigned        lpfn;
-       uint32_t        flags;
-};
-
-/**
- * struct ttm_placement
- *
- * @num_placement:     number of preferred placements
- * @placement:         preferred placements
- * @num_busy_placement:        number of preferred placements when need to evict buffer
- * @busy_placement:    preferred placements when need to evict buffer
- *
- * Structure indicating the placement you request for an object.
- */
-struct ttm_placement {
-       unsigned                num_placement;
-       const struct ttm_place  *placement;
-       unsigned                num_busy_placement;
-       const struct ttm_place  *busy_placement;
-};
+struct ttm_placement;
 
 /**
  * struct ttm_bus_placement
index 99c6d01..4f0a921 100644 (file)
@@ -133,7 +133,6 @@ struct ttm_tt {
  * struct ttm_dma_tt
  *
  * @ttm: Base ttm_tt struct.
- * @cpu_address: The CPU address of the pages
  * @dma_address: The DMA (bus) addresses of the pages
  * @pages_list: used by some page allocation backend
  *
@@ -143,7 +142,6 @@ struct ttm_tt {
  */
 struct ttm_dma_tt {
        struct ttm_tt ttm;
-       void **cpu_address;
        dma_addr_t *dma_address;
        struct list_head pages_list;
 };
@@ -961,7 +959,6 @@ void ttm_mem_io_free(struct ttm_bo_device *bdev,
  * ttm_bo_move_ttm
  *
  * @bo: A pointer to a struct ttm_buffer_object.
- * @evict: 1: This is an eviction. Don't try to pipeline.
  * @interruptible: Sleep interruptible if waiting.
  * @no_wait_gpu: Return immediately if the GPU is busy.
  * @new_mem: struct ttm_mem_reg indicating where to move.
@@ -977,14 +974,13 @@ void ttm_mem_io_free(struct ttm_bo_device *bdev,
  */
 
 extern int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
-                          bool evict, bool interruptible, bool no_wait_gpu,
+                          bool interruptible, bool no_wait_gpu,
                           struct ttm_mem_reg *new_mem);
 
 /**
  * ttm_bo_move_memcpy
  *
  * @bo: A pointer to a struct ttm_buffer_object.
- * @evict: 1: This is an eviction. Don't try to pipeline.
  * @interruptible: Sleep interruptible if waiting.
  * @no_wait_gpu: Return immediately if the GPU is busy.
  * @new_mem: struct ttm_mem_reg indicating where to move.
@@ -1000,8 +996,7 @@ extern int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
  */
 
 extern int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
-                             bool evict, bool interruptible,
-                             bool no_wait_gpu,
+                             bool interruptible, bool no_wait_gpu,
                              struct ttm_mem_reg *new_mem);
 
 /**
index 72dcbe8..c452089 100644 (file)
@@ -155,4 +155,5 @@ extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
 extern void ttm_mem_global_free_page(struct ttm_mem_global *glob,
                                     struct page *page);
 extern size_t ttm_round_pot(size_t size);
+extern uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob);
 #endif
index 8ed44f9..932be0c 100644 (file)
@@ -30,6 +30,9 @@
 
 #ifndef _TTM_PLACEMENT_H_
 #define _TTM_PLACEMENT_H_
+
+#include <linux/types.h>
+
 /*
  * Memory regions for data placement.
  */
 #define TTM_PL_SYSTEM           0
 #define TTM_PL_TT               1
 #define TTM_PL_VRAM             2
-#define TTM_PL_PRIV0            3
-#define TTM_PL_PRIV1            4
-#define TTM_PL_PRIV2            5
-#define TTM_PL_PRIV3            6
-#define TTM_PL_PRIV4            7
-#define TTM_PL_PRIV5            8
-#define TTM_PL_SWAPPED          15
+#define TTM_PL_PRIV             3
 
 #define TTM_PL_FLAG_SYSTEM      (1 << TTM_PL_SYSTEM)
 #define TTM_PL_FLAG_TT          (1 << TTM_PL_TT)
 #define TTM_PL_FLAG_VRAM        (1 << TTM_PL_VRAM)
-#define TTM_PL_FLAG_PRIV0       (1 << TTM_PL_PRIV0)
-#define TTM_PL_FLAG_PRIV1       (1 << TTM_PL_PRIV1)
-#define TTM_PL_FLAG_PRIV2       (1 << TTM_PL_PRIV2)
-#define TTM_PL_FLAG_PRIV3       (1 << TTM_PL_PRIV3)
-#define TTM_PL_FLAG_PRIV4       (1 << TTM_PL_PRIV4)
-#define TTM_PL_FLAG_PRIV5       (1 << TTM_PL_PRIV5)
-#define TTM_PL_FLAG_SWAPPED     (1 << TTM_PL_SWAPPED)
+#define TTM_PL_FLAG_PRIV        (1 << TTM_PL_PRIV)
 #define TTM_PL_MASK_MEM         0x0000FFFF
 
 /*
@@ -72,7 +63,6 @@
 #define TTM_PL_FLAG_CACHED      (1 << 16)
 #define TTM_PL_FLAG_UNCACHED    (1 << 17)
 #define TTM_PL_FLAG_WC          (1 << 18)
-#define TTM_PL_FLAG_SHARED      (1 << 20)
 #define TTM_PL_FLAG_NO_EVICT    (1 << 21)
 #define TTM_PL_FLAG_TOPDOWN     (1 << 22)
 
 
 #define TTM_PL_MASK_MEMTYPE     (TTM_PL_MASK_MEM | TTM_PL_MASK_CACHING)
 
-/*
- * Access flags to be used for CPU- and GPU- mappings.
- * The idea is that the TTM synchronization mechanism will
- * allow concurrent READ access and exclusive write access.
- * Currently GPU- and CPU accesses are exclusive.
+/**
+ * struct ttm_place
+ *
+ * @fpfn:      first valid page frame number to put the object
+ * @lpfn:      last valid page frame number to put the object
+ * @flags:     memory domain and caching flags for the object
+ *
+ * Structure indicating a possible place to put an object.
  */
+struct ttm_place {
+       unsigned        fpfn;
+       unsigned        lpfn;
+       uint32_t        flags;
+};
 
-#define TTM_ACCESS_READ         (1 << 0)
-#define TTM_ACCESS_WRITE        (1 << 1)
+/**
+ * struct ttm_placement
+ *
+ * @num_placement:     number of preferred placements
+ * @placement:         preferred placements
+ * @num_busy_placement:        number of preferred placements when need to evict buffer
+ * @busy_placement:    preferred placements when need to evict buffer
+ *
+ * Structure indicating the placement you request for an object.
+ */
+struct ttm_placement {
+       unsigned                num_placement;
+       const struct ttm_place  *placement;
+       unsigned                num_busy_placement;
+       const struct ttm_place  *busy_placement;
+};
 
 #endif
diff --git a/include/dt-bindings/display/tda998x.h b/include/dt-bindings/display/tda998x.h
new file mode 100644 (file)
index 0000000..34757a3
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef _DT_BINDINGS_TDA998X_H
+#define _DT_BINDINGS_TDA998X_H
+
+#define TDA998x_SPDIF  1
+#define TDA998x_I2S    2
+
+#endif /*_DT_BINDINGS_TDA998X_H */
index 729ab9f..2a99f1d 100644 (file)
@@ -11,4 +11,9 @@
 #define TEGRA124_SOCTHERM_SENSOR_PLLX 3
 #define TEGRA124_SOCTHERM_SENSOR_NUM 4
 
+#define TEGRA_SOCTHERM_THROT_LEVEL_LOW  0
+#define TEGRA_SOCTHERM_THROT_LEVEL_MED  1
+#define TEGRA_SOCTHERM_THROT_LEVEL_HIGH 2
+#define TEGRA_SOCTHERM_THROT_LEVEL_NONE -1
+
 #endif
index 94afcb2..ddbeda6 100644 (file)
@@ -946,9 +946,17 @@ struct acpi_reference_args {
 #ifdef CONFIG_ACPI
 int acpi_dev_get_property(struct acpi_device *adev, const char *name,
                          acpi_object_type type, const union acpi_object **obj);
-int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
-                                    const char *name, size_t index,
-                                    struct acpi_reference_args *args);
+int __acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index, size_t num_args,
+                               struct acpi_reference_args *args);
+
+static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index,
+                               struct acpi_reference_args *args)
+{
+       return __acpi_node_get_property_reference(fwnode, name, index,
+               MAX_ACPI_REFERENCE_ARGS, args);
+}
 
 int acpi_node_prop_get(struct fwnode_handle *fwnode, const char *propname,
                       void **valptr);
@@ -1024,6 +1032,14 @@ static inline int acpi_dev_get_property(struct acpi_device *adev,
        return -ENXIO;
 }
 
+static inline int
+__acpi_node_get_property_reference(struct fwnode_handle *fwnode,
+                               const char *name, size_t index, size_t num_args,
+                               struct acpi_reference_args *args)
+{
+       return -ENXIO;
+}
+
 static inline int acpi_node_get_property_reference(struct fwnode_handle *fwnode,
                                const char *name, size_t index,
                                struct acpi_reference_args *args)
index e82e3ee..1035879 100644 (file)
 #define CNTL_LDMAFIFOTIME      (1 << 15)
 #define CNTL_WATERMARK         (1 << 16)
 
+/* ST Microelectronics variant bits */
+#define CNTL_ST_1XBPP_444      0x0
+#define CNTL_ST_1XBPP_5551     (1 << 17)
+#define CNTL_ST_1XBPP_565      (1 << 18)
+#define CNTL_ST_CDWID_12       0x0
+#define CNTL_ST_CDWID_16       (1 << 19)
+#define CNTL_ST_CDWID_18       (1 << 20)
+#define CNTL_ST_CDWID_24       ((1 << 19)|(1 << 20))
+#define CNTL_ST_CEAEN          (1 << 21)
+#define CNTL_ST_LCDBPP24_PACKED        (6 << 1)
+
 enum {
        /* individual formats */
        CLCD_CAP_RGB444         = (1 << 0),
@@ -93,6 +104,8 @@ enum {
        CLCD_CAP_ALL            = CLCD_CAP_BGR | CLCD_CAP_RGB,
 };
 
+struct backlight_device;
+
 struct clcd_panel {
        struct fb_videomode     mode;
        signed short            width;  /* width in mm */
@@ -105,6 +118,13 @@ struct clcd_panel {
                                fixedtimings:1,
                                grayscale:1;
        unsigned int            connector;
+       struct backlight_device *backlight;
+       /*
+        * If the B/R lines are switched between the CLCD
+        * and the panel we need to know this and not try to
+        * compensate with the BGR bit in the control register.
+        */
+       bool                    bgr_connection;
 };
 
 struct clcd_regs {
@@ -170,11 +190,38 @@ struct clcd_board {
 struct amba_device;
 struct clk;
 
+/**
+ * struct clcd_vendor_data - holds hardware (IP-block) vendor-specific
+ * variant information
+ *
+ * @clock_timregs: the CLCD needs to be clocked when accessing the
+ * timer registers, or the hardware will hang.
+ * @packed_24_bit_pixels: this variant supports 24bit packed pixel data,
+ * so that RGB accesses 3 bytes at a time, not just on even 32bit
+ * boundaries, packing the pixel data in memory. ST Microelectronics
+ * have this.
+ * @st_bitmux_control: ST Microelectronics have implemented output
+ * bit line multiplexing into the CLCD control register. This indicates
+ * that we need to use this.
+ * @init_board: custom board init function for this variant
+ * @init_panel: custom panel init function for this variant
+ */
+struct clcd_vendor_data {
+       bool    clock_timregs;
+       bool    packed_24_bit_pixels;
+       bool    st_bitmux_control;
+       int     (*init_board)(struct amba_device *adev,
+                             struct clcd_board *board);
+       int     (*init_panel)(struct clcd_fb *fb,
+                             struct device_node *panel);
+};
+
 /* this data structure describes each frame buffer device we find */
 struct clcd_fb {
        struct fb_info          fb;
        struct amba_device      *dev;
        struct clk              *clk;
+       struct clcd_vendor_data *vendor;
        struct clcd_panel       *panel;
        struct clcd_board       *board;
        void                    *board_data;
@@ -231,16 +278,22 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
        if (var->grayscale)
                val |= CNTL_LCDBW;
 
-       if (fb->panel->caps && fb->board->caps &&
-           var->bits_per_pixel >= 16) {
+       if (fb->panel->caps && fb->board->caps && var->bits_per_pixel >= 16) {
                /*
                 * if board and panel supply capabilities, we can support
-                * changing BGR/RGB depending on supplied parameters
+                * changing BGR/RGB depending on supplied parameters. Here
+                * we switch to what the framebuffer is providing if need
+                * be, so if the framebuffer is BGR but the display connection
+                * is RGB (first case) we switch it around. Vice versa mutatis
+                * mutandis if the framebuffer is RGB but the display connection
+                * is BGR, we flip it around.
                 */
                if (var->red.offset == 0)
                        val &= ~CNTL_BGR;
                else
                        val |= CNTL_BGR;
+               if (fb->panel->bgr_connection)
+                       val ^= CNTL_BGR;
        }
 
        switch (var->bits_per_pixel) {
@@ -270,6 +323,10 @@ static inline void clcdfb_decode(struct clcd_fb *fb, struct clcd_regs *regs)
                else
                        val |= CNTL_LCDBPP16_444;
                break;
+       case 24:
+               /* Modified variant supporting 24 bit packed pixels */
+               val |= CNTL_ST_LCDBPP24_PACKED;
+               break;
        case 32:
                val |= CNTL_LCDBPP24;
                break;
index adbc812..fdb1803 100644 (file)
@@ -105,6 +105,7 @@ enum {
        ATA_ID_CFA_KEY_MGMT     = 162,
        ATA_ID_CFA_MODES        = 163,
        ATA_ID_DATA_SET_MGMT    = 169,
+       ATA_ID_SCT_CMD_XPORT    = 206,
        ATA_ID_ROT_SPEED        = 217,
        ATA_ID_PIO4             = (1 << 1),
 
@@ -788,6 +789,48 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id)
        return id[ATA_ID_COMMAND_SET_4] & (1 << 6);
 }
 
+/**
+ *
+ * Word: 206 - SCT Command Transport
+ *    15:12 - Vendor Specific
+ *     11:6 - Reserved
+ *        5 - SCT Command Transport Data Tables supported
+ *        4 - SCT Command Transport Features Control supported
+ *        3 - SCT Command Transport Error Recovery Control supported
+ *        2 - SCT Command Transport Write Same supported
+ *        1 - SCT Command Transport Long Sector Access supported
+ *        0 - SCT Command Transport supported
+ */
+static inline bool ata_id_sct_data_tables(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 5) ? true : false;
+}
+
+static inline bool ata_id_sct_features_ctrl(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 4) ? true : false;
+}
+
+static inline bool ata_id_sct_error_recovery_ctrl(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 3) ? true : false;
+}
+
+static inline bool ata_id_sct_write_same(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 2) ? true : false;
+}
+
+static inline bool ata_id_sct_long_sector_access(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 1) ? true : false;
+}
+
+static inline bool ata_id_sct_supported(const u16 *id)
+{
+       return id[ATA_ID_SCT_CMD_XPORT] & (1 << 0) ? true : false;
+}
+
 /**
  *     ata_id_major_version    -       get ATA level of drive
  *     @id: Identify data
@@ -1071,32 +1114,6 @@ static inline void ata_id_to_hd_driveid(u16 *id)
 #endif
 }
 
-/*
- * Write LBA Range Entries to the buffer that will cover the extent from
- * sector to sector + count.  This is used for TRIM and for ADD LBA(S)
- * TO NV CACHE PINNED SET.
- */
-static inline unsigned ata_set_lba_range_entries(void *_buffer,
-               unsigned num, u64 sector, unsigned long count)
-{
-       __le64 *buffer = _buffer;
-       unsigned i = 0, used_bytes;
-
-       while (i < num) {
-               u64 entry = sector |
-                       ((u64)(count > 0xffff ? 0xffff : count) << 48);
-               buffer[i++] = __cpu_to_le64(entry);
-               if (count <= 0xffff)
-                       break;
-               count -= 0xffff;
-               sector += 0xffff;
-       }
-
-       used_bytes = ALIGN(i * 8, 512);
-       memset(buffer + i, 0, used_bytes - i * 8);
-       return used_bytes;
-}
-
 static inline bool ata_ok(u8 status)
 {
        return ((status & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR))
index 7caaf29..28c1505 100644 (file)
 #ifndef _LINUX_AUTO_DEV_IOCTL_H
 #define _LINUX_AUTO_DEV_IOCTL_H
 
-#include <linux/auto_fs.h>
-#include <linux/string.h>
-
-#define AUTOFS_DEVICE_NAME             "autofs"
-
-#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1
-#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0
-
-#define AUTOFS_DEVID_LEN               16
-
-#define AUTOFS_DEV_IOCTL_SIZE          sizeof(struct autofs_dev_ioctl)
-
-/*
- * An ioctl interface for autofs mount point control.
- */
-
-struct args_protover {
-       __u32   version;
-};
-
-struct args_protosubver {
-       __u32   sub_version;
-};
-
-struct args_openmount {
-       __u32   devid;
-};
-
-struct args_ready {
-       __u32   token;
-};
-
-struct args_fail {
-       __u32   token;
-       __s32   status;
-};
-
-struct args_setpipefd {
-       __s32   pipefd;
-};
-
-struct args_timeout {
-       __u64   timeout;
-};
-
-struct args_requester {
-       __u32   uid;
-       __u32   gid;
-};
-
-struct args_expire {
-       __u32   how;
-};
-
-struct args_askumount {
-       __u32   may_umount;
-};
-
-struct args_ismountpoint {
-       union {
-               struct args_in {
-                       __u32   type;
-               } in;
-               struct args_out {
-                       __u32   devid;
-                       __u32   magic;
-               } out;
-       };
-};
-
-/*
- * All the ioctls use this structure.
- * When sending a path size must account for the total length
- * of the chunk of memory otherwise is is the size of the
- * structure.
- */
-
-struct autofs_dev_ioctl {
-       __u32 ver_major;
-       __u32 ver_minor;
-       __u32 size;             /* total size of data passed in
-                                * including this struct */
-       __s32 ioctlfd;          /* automount command fd */
-
-       /* Command parameters */
-
-       union {
-               struct args_protover            protover;
-               struct args_protosubver         protosubver;
-               struct args_openmount           openmount;
-               struct args_ready               ready;
-               struct args_fail                fail;
-               struct args_setpipefd           setpipefd;
-               struct args_timeout             timeout;
-               struct args_requester           requester;
-               struct args_expire              expire;
-               struct args_askumount           askumount;
-               struct args_ismountpoint        ismountpoint;
-       };
-
-       char path[0];
-};
-
-static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in)
-{
-       memset(in, 0, sizeof(struct autofs_dev_ioctl));
-       in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
-       in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
-       in->size = sizeof(struct autofs_dev_ioctl);
-       in->ioctlfd = -1;
-}
-
-/*
- * If you change this make sure you make the corresponding change
- * to autofs-dev-ioctl.c:lookup_ioctl()
- */
-enum {
-       /* Get various version info */
-       AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71,
-       AUTOFS_DEV_IOCTL_PROTOVER_CMD,
-       AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD,
-
-       /* Open mount ioctl fd */
-       AUTOFS_DEV_IOCTL_OPENMOUNT_CMD,
-
-       /* Close mount ioctl fd */
-       AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD,
-
-       /* Mount/expire status returns */
-       AUTOFS_DEV_IOCTL_READY_CMD,
-       AUTOFS_DEV_IOCTL_FAIL_CMD,
-
-       /* Activate/deactivate autofs mount */
-       AUTOFS_DEV_IOCTL_SETPIPEFD_CMD,
-       AUTOFS_DEV_IOCTL_CATATONIC_CMD,
-
-       /* Expiry timeout */
-       AUTOFS_DEV_IOCTL_TIMEOUT_CMD,
-
-       /* Get mount last requesting uid and gid */
-       AUTOFS_DEV_IOCTL_REQUESTER_CMD,
-
-       /* Check for eligible expire candidates */
-       AUTOFS_DEV_IOCTL_EXPIRE_CMD,
-
-       /* Request busy status */
-       AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD,
-
-       /* Check if path is a mountpoint */
-       AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD,
-};
-
-#define AUTOFS_IOCTL 0x93
-
-#define AUTOFS_DEV_IOCTL_VERSION \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_PROTOVER \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_PROTOSUBVER \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_OPENMOUNT \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_READY \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_FAIL \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_SETPIPEFD \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_CATATONIC \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_TIMEOUT \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_REQUESTER \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_EXPIRE \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_ASKUMOUNT \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl)
-
-#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \
-       _IOWR(AUTOFS_IOCTL, \
-             AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl)
-
+#include <uapi/linux/auto_dev-ioctl.h>
 #endif /* _LINUX_AUTO_DEV_IOCTL_H */
index b4066bb..b8f814c 100644 (file)
@@ -10,7 +10,6 @@
 #define _LINUX_AUTO_FS_H
 
 #include <linux/fs.h>
-#include <linux/limits.h>
 #include <linux/ioctl.h>
 #include <uapi/linux/auto_fs.h>
 #endif /* _LINUX_AUTO_FS_H */
index cbdbf34..3bf5d33 100644 (file)
@@ -343,16 +343,7 @@ static inline struct blkcg *cpd_to_blkcg(struct blkcg_policy_data *cpd)
  */
 static inline int blkg_path(struct blkcg_gq *blkg, char *buf, int buflen)
 {
-       char *p;
-
-       p = cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
-       if (!p) {
-               strncpy(buf, "<unavailable>", buflen);
-               return -ENAMETOOLONG;
-       }
-
-       memmove(buf, p, buf + buflen - p);
-       return 0;
+       return cgroup_path(blkg->blkcg->css.cgroup, buf, buflen);
 }
 
 /**
index 440a721..c83c23f 100644 (file)
@@ -97,7 +97,7 @@ int cgroup_add_legacy_cftypes(struct cgroup_subsys *ss, struct cftype *cfts);
 int cgroup_rm_cftypes(struct cftype *cfts);
 void cgroup_file_notify(struct cgroup_file *cfile);
 
-char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
+int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen);
 int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry);
 int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk);
@@ -555,8 +555,7 @@ static inline int cgroup_name(struct cgroup *cgrp, char *buf, size_t buflen)
        return kernfs_name(cgrp->kn, buf, buflen);
 }
 
-static inline char * __must_check cgroup_path(struct cgroup *cgrp, char *buf,
-                                             size_t buflen)
+static inline int cgroup_path(struct cgroup *cgrp, char *buf, size_t buflen)
 {
        return kernfs_path(cgrp->kn, buf, buflen);
 }
@@ -658,8 +657,8 @@ struct cgroup_namespace *copy_cgroup_ns(unsigned long flags,
                                        struct user_namespace *user_ns,
                                        struct cgroup_namespace *old_ns);
 
-char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
-                    struct cgroup_namespace *ns);
+int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
+                  struct cgroup_namespace *ns);
 
 #else /* !CONFIG_CGROUPS */
 
index 6685698..f1bfa15 100644 (file)
@@ -182,6 +182,29 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 # define unreachable() do { } while (1)
 #endif
 
+/*
+ * KENTRY - kernel entry point
+ * This can be used to annotate symbols (functions or data) that are used
+ * without their linker symbol being referenced explicitly. For example,
+ * interrupt vector handlers, or functions in the kernel image that are found
+ * programatically.
+ *
+ * Not required for symbols exported with EXPORT_SYMBOL, or initcalls. Those
+ * are handled in their own way (with KEEP() in linker scripts).
+ *
+ * KENTRY can be avoided if the symbols in question are marked as KEEP() in the
+ * linker script. For example an architecture could KEEP() its entire
+ * boot/exception vector code rather than annotate each function and data.
+ */
+#ifndef KENTRY
+# define KENTRY(sym)                                           \
+       extern typeof(sym) sym;                                 \
+       static const unsigned long __kentry_##sym               \
+       __used                                                  \
+       __attribute__((section("___kentry" "+" #sym ), used))   \
+       = (unsigned long)&sym;
+#endif
+
 #ifndef RELOC_HIDE
 # define RELOC_HIDE(ptr, off)                                  \
   ({ unsigned long __ptr;                                      \
index 631ba33..5fa55fc 100644 (file)
@@ -639,19 +639,19 @@ static inline int cpufreq_table_find_index_al(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq >= target_freq)
-                       return i;
+                       return pos - table;
 
-               best = i;
+               best = pos;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find lowest freq at or above target in a table in descending order */
@@ -659,28 +659,28 @@ static inline int cpufreq_table_find_index_dl(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq > target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found above target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
-               return best;
+               return best - pos;
        }
 
-       return best;
+       return best - pos;
 }
 
 /* Works only on sorted freq-tables */
@@ -700,28 +700,28 @@ static inline int cpufreq_table_find_index_ah(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq < target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found below target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find highest freq at or below target in a table in descending order */
@@ -729,19 +729,19 @@ static inline int cpufreq_table_find_index_dh(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq <= target_freq)
-                       return i;
+                       return pos - table;
 
-               best = i;
+               best = pos;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Works only on sorted freq-tables */
@@ -761,32 +761,32 @@ static inline int cpufreq_table_find_index_ac(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq < target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found below target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
                /* Choose the closest freq */
-               if (target_freq - table[best].frequency > freq - target_freq)
-                       return i;
+               if (target_freq - best->frequency > freq - target_freq)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Find closest freq to target in a table in descending order */
@@ -794,32 +794,32 @@ static inline int cpufreq_table_find_index_dc(struct cpufreq_policy *policy,
                                              unsigned int target_freq)
 {
        struct cpufreq_frequency_table *table = policy->freq_table;
+       struct cpufreq_frequency_table *pos, *best = table - 1;
        unsigned int freq;
-       int i, best = -1;
 
-       for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
-               freq = table[i].frequency;
+       cpufreq_for_each_valid_entry(pos, table) {
+               freq = pos->frequency;
 
                if (freq == target_freq)
-                       return i;
+                       return pos - table;
 
                if (freq > target_freq) {
-                       best = i;
+                       best = pos;
                        continue;
                }
 
                /* No freq found above target_freq */
-               if (best == -1)
-                       return i;
+               if (best == table - 1)
+                       return pos - table;
 
                /* Choose the closest freq */
-               if (table[best].frequency - target_freq > target_freq - freq)
-                       return i;
+               if (best->frequency - target_freq > target_freq - freq)
+                       return pos - table;
 
-               return best;
+               return best - table;
        }
 
-       return best;
+       return best - table;
 }
 
 /* Works only on sorted freq-tables */
index 653589e..f13e4ff 100644 (file)
@@ -22,7 +22,10 @@ extern const unsigned char _ctype[];
 #define isalnum(c)     ((__ismask(c)&(_U|_L|_D)) != 0)
 #define isalpha(c)     ((__ismask(c)&(_U|_L)) != 0)
 #define iscntrl(c)     ((__ismask(c)&(_C)) != 0)
-#define isdigit(c)     ((__ismask(c)&(_D)) != 0)
+static inline int isdigit(int c)
+{
+       return '0' <= c && c <= '9';
+}
 #define isgraph(c)     ((__ismask(c)&(_P|_U|_L|_D)) != 0)
 #define islower(c)     ((__ismask(c)&(_L)) != 0)
 #define isprint(c)     ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
index 0f90eb5..08528af 100644 (file)
  * that gives better TLB efficiency.
  */
 #define DMA_ATTR_ALLOC_SINGLE_PAGES    (1UL << 7)
+/*
+ * DMA_ATTR_NO_WARN: This tells the DMA-mapping subsystem to suppress
+ * allocation failure reports (similarly to __GFP_NOWARN).
+ */
+#define DMA_ATTR_NO_WARN       (1UL << 8)
 
 /*
  * A dma_addr_t can hold any valid DMA or bus address for the platform.
index c565f87..2a0f61f 100644 (file)
@@ -1,5 +1,6 @@
 #ifndef _LINUX_EXPORT_H
 #define _LINUX_EXPORT_H
+
 /*
  * Export symbols from the kernel to modules.  Forked from module.h
  * to reduce the amount of pointless cruft we feed to gcc when only
@@ -42,27 +43,26 @@ extern struct module __this_module;
 #ifdef CONFIG_MODVERSIONS
 /* Mark the CRC weak since genksyms apparently decides not to
  * generate a checksums for some symbols */
-#define __CRC_SYMBOL(sym, sec)                                 \
-       extern __visible void *__crc_##sym __attribute__((weak));               \
-       static const unsigned long __kcrctab_##sym              \
-       __used                                                  \
-       __attribute__((section("___kcrctab" sec "+" #sym), unused))     \
+#define __CRC_SYMBOL(sym, sec)                                         \
+       extern __visible void *__crc_##sym __attribute__((weak));       \
+       static const unsigned long __kcrctab_##sym                      \
+       __used                                                          \
+       __attribute__((section("___kcrctab" sec "+" #sym), used))       \
        = (unsigned long) &__crc_##sym;
 #else
 #define __CRC_SYMBOL(sym, sec)
 #endif
 
 /* For every exported symbol, place a struct in the __ksymtab section */
-#define ___EXPORT_SYMBOL(sym, sec)                             \
-       extern typeof(sym) sym;                                 \
-       __CRC_SYMBOL(sym, sec)                                  \
-       static const char __kstrtab_##sym[]                     \
-       __attribute__((section("__ksymtab_strings"), aligned(1))) \
-       = VMLINUX_SYMBOL_STR(sym);                              \
-       extern const struct kernel_symbol __ksymtab_##sym;      \
-       __visible const struct kernel_symbol __ksymtab_##sym    \
-       __used                                                  \
-       __attribute__((section("___ksymtab" sec "+" #sym), unused))     \
+#define ___EXPORT_SYMBOL(sym, sec)                                     \
+       extern typeof(sym) sym;                                         \
+       __CRC_SYMBOL(sym, sec)                                          \
+       static const char __kstrtab_##sym[]                             \
+       __attribute__((section("__ksymtab_strings"), aligned(1)))       \
+       = VMLINUX_SYMBOL_STR(sym);                                      \
+       static const struct kernel_symbol __ksymtab_##sym               \
+       __used                                                          \
+       __attribute__((section("___ksymtab" sec "+" #sym), used))       \
        = { (unsigned long)&sym, __kstrtab_##sym }
 
 #if defined(__KSYM_DEPS__)
@@ -78,7 +78,6 @@ extern struct module __this_module;
 
 #elif defined(CONFIG_TRIM_UNUSED_KSYMS)
 
-#include <linux/kconfig.h>
 #include <generated/autoksyms.h>
 
 #define __EXPORT_SYMBOL(sym, sec)                              \
index b03c062..5ab958c 100644 (file)
@@ -157,12 +157,13 @@ struct fid {
  *    @fh_to_dentry is given a &struct super_block (@sb) and a file handle
  *    fragment (@fh, @fh_len). It should return a &struct dentry which refers
  *    to the same file that the file handle fragment refers to.  If it cannot,
- *    it should return a %NULL pointer if the file was found but no acceptable
- *    &dentries were available, or an %ERR_PTR error code indicating why it
- *    couldn't be found (e.g. %ENOENT or %ENOMEM).  Any suitable dentry can be
- *    returned including, if necessary, a new dentry created with d_alloc_root.
- *    The caller can then find any other extant dentries by following the
- *    d_alias links.
+ *    it should return a %NULL pointer if the file cannot be found, or an
+ *    %ERR_PTR error code of %ENOMEM if a memory allocation failure occurred.
+ *    Any other error code is treated like %NULL, and will cause an %ESTALE error
+ *    for callers of exportfs_decode_fh().
+ *    Any suitable dentry can be returned including, if necessary, a new dentry
+ *    created with d_alloc_root.  The caller can then find any other extant
+ *    dentries by following the d_alias links.
  *
  * fh_to_parent:
  *    Same as @fh_to_dentry, except that it returns a pointer to the parent
index 9961110..7494dc6 100644 (file)
@@ -25,6 +25,7 @@ struct space_resv {
                                         FALLOC_FL_PUNCH_HOLE |         \
                                         FALLOC_FL_COLLAPSE_RANGE |     \
                                         FALLOC_FL_ZERO_RANGE |         \
-                                        FALLOC_FL_INSERT_RANGE)
+                                        FALLOC_FL_INSERT_RANGE |       \
+                                        FALLOC_FL_UNSHARE_RANGE)
 
 #endif /* _FALLOC_H_ */
index 86baaa4..a44794e 100644 (file)
@@ -51,6 +51,16 @@ struct fence_array {
 
 extern const struct fence_ops fence_array_ops;
 
+/**
+ * fence_is_array - check if a fence is from the array subsclass
+ *
+ * Return true if it is a fence_array and false otherwise.
+ */
+static inline bool fence_is_array(struct fence *fence)
+{
+       return fence->ops == &fence_array_ops;
+}
+
 /**
  * to_fence_array - cast a fence to a fence_array
  * @fence: fence to cast to a fence_array
index 2ac6fa5..0d76305 100644 (file)
@@ -60,7 +60,7 @@ struct fence_cb;
  * implementer of the fence for its own purposes. Can be used in different
  * ways by different fence implementers, so do not rely on this.
  *
- * *) Since atomic bitops are used, this is not guaranteed to be the case.
+ * Since atomic bitops are used, this is not guaranteed to be the case.
  * Particularly, if the bit was set, but fence_signal was called right
  * before this bit was set, it would have been able to set the
  * FENCE_FLAG_SIGNALED_BIT, before enable_signaling was called.
index c145219..16d2b6e 100644 (file)
@@ -440,8 +440,9 @@ struct address_space {
        unsigned long           nrexceptional;
        pgoff_t                 writeback_index;/* writeback starts here */
        const struct address_space_operations *a_ops;   /* methods */
-       unsigned long           flags;          /* error bits/gfp mask */
+       unsigned long           flags;          /* error bits */
        spinlock_t              private_lock;   /* for use by the address_space */
+       gfp_t                   gfp_mask;       /* implicit gfp mask for allocations */
        struct list_head        private_list;   /* ditto */
        void                    *private_data;  /* ditto */
 } __attribute__((aligned(sizeof(long))));
@@ -2933,6 +2934,7 @@ extern int vfs_stat(const char __user *, struct kstat *);
 extern int vfs_lstat(const char __user *, struct kstat *);
 extern int vfs_fstat(unsigned int, struct kstat *);
 extern int vfs_fstatat(int , const char __user *, struct kstat *, int);
+extern const char *vfs_get_link(struct dentry *, struct delayed_call *);
 
 extern int __generic_block_fiemap(struct inode *inode,
                                  struct fiemap_extent_info *fieinfo,
index 1f0be72..24e2cc5 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/irqdomain.h>
 #include <linux/lockdep.h>
 #include <linux/pinctrl/pinctrl.h>
-#include <linux/kconfig.h>
 
 struct gpio_desc;
 struct of_phandle_args;
index 5a3321a..024a0b5 100644 (file)
@@ -139,24 +139,8 @@ extern bool initcall_debug;
 
 #ifndef __ASSEMBLY__
 
-#ifdef CONFIG_LTO
-/* Work around a LTO gcc problem: when there is no reference to a variable
- * in a module it will be moved to the end of the program. This causes
- * reordering of initcalls which the kernel does not like.
- * Add a dummy reference function to avoid this. The function is
- * deleted by the linker.
- */
-#define LTO_REFERENCE_INITCALL(x) \
-       ; /* yes this is needed */                      \
-       static __used __exit void *reference_##x(void)  \
-       {                                               \
-               return &x;                              \
-       }
-#else
-#define LTO_REFERENCE_INITCALL(x)
-#endif
-
-/* initcalls are now grouped by functionality into separate 
+/*
+ * initcalls are now grouped by functionality into separate
  * subsections. Ordering inside the subsections is determined
  * by link order. 
  * For backwards compatibility, initcall() puts the call in 
@@ -164,12 +148,16 @@ extern bool initcall_debug;
  *
  * The `id' arg to __define_initcall() is needed so that multiple initcalls
  * can point at the same handler without causing duplicate-symbol build errors.
+ *
+ * Initcalls are run by placing pointers in initcall sections that the
+ * kernel iterates at runtime. The linker can do dead code / data elimination
+ * and remove that completely, so the initcall sections have to be marked
+ * as KEEP() in the linker script.
  */
 
 #define __define_initcall(fn, id) \
        static initcall_t __initcall_##fn##id __used \
-       __attribute__((__section__(".initcall" #id ".init"))) = fn; \
-       LTO_REFERENCE_INITCALL(__initcall_##fn##id)
+       __attribute__((__section__(".initcall" #id ".init"))) = fn;
 
 /*
  * Early initcalls run before initializing SMP.
@@ -205,15 +193,15 @@ extern bool initcall_debug;
 
 #define __initcall(fn) device_initcall(fn)
 
-#define __exitcall(fn) \
+#define __exitcall(fn)                                         \
        static exitcall_t __exitcall_##fn __exit_call = fn
 
-#define console_initcall(fn) \
-       static initcall_t __initcall_##fn \
+#define console_initcall(fn)                                   \
+       static initcall_t __initcall_##fn                       \
        __used __section(.con_initcall.init) = fn
 
-#define security_initcall(fn) \
-       static initcall_t __initcall_##fn \
+#define security_initcall(fn)                                  \
+       static initcall_t __initcall_##fn                       \
        __used __section(.security_initcall.init) = fn
 
 struct obs_kernel_param {
index 645ad06..58df02b 100644 (file)
  * See Documentation/io-mapping.txt
  */
 
-#ifdef CONFIG_HAVE_ATOMIC_IOMAP
-
-#include <asm/iomap.h>
-
 struct io_mapping {
        resource_size_t base;
        unsigned long size;
        pgprot_t prot;
+       void __iomem *iomem;
 };
 
+#ifdef CONFIG_HAVE_ATOMIC_IOMAP
+
+#include <asm/iomap.h>
 /*
  * For small address space machines, mapping large objects
  * into the kernel virtual space isn't practical. Where
@@ -49,34 +49,25 @@ struct io_mapping {
  */
 
 static inline struct io_mapping *
-io_mapping_create_wc(resource_size_t base, unsigned long size)
+io_mapping_init_wc(struct io_mapping *iomap,
+                  resource_size_t base,
+                  unsigned long size)
 {
-       struct io_mapping *iomap;
        pgprot_t prot;
 
-       iomap = kmalloc(sizeof(*iomap), GFP_KERNEL);
-       if (!iomap)
-               goto out_err;
-
        if (iomap_create_wc(base, size, &prot))
-               goto out_free;
+               return NULL;
 
        iomap->base = base;
        iomap->size = size;
        iomap->prot = prot;
        return iomap;
-
-out_free:
-       kfree(iomap);
-out_err:
-       return NULL;
 }
 
 static inline void
-io_mapping_free(struct io_mapping *mapping)
+io_mapping_fini(struct io_mapping *mapping)
 {
        iomap_free(mapping->base, mapping->size);
-       kfree(mapping);
 }
 
 /* Atomic map/unmap */
@@ -121,21 +112,46 @@ io_mapping_unmap(void __iomem *vaddr)
 #else
 
 #include <linux/uaccess.h>
-
-/* this struct isn't actually defined anywhere */
-struct io_mapping;
+#include <asm/pgtable.h>
 
 /* Create the io_mapping object*/
 static inline struct io_mapping *
-io_mapping_create_wc(resource_size_t base, unsigned long size)
+io_mapping_init_wc(struct io_mapping *iomap,
+                  resource_size_t base,
+                  unsigned long size)
 {
-       return (struct io_mapping __force *) ioremap_wc(base, size);
+       iomap->base = base;
+       iomap->size = size;
+       iomap->iomem = ioremap_wc(base, size);
+#if defined(pgprot_noncached_wc) /* archs can't agree on a name ... */
+       iomap->prot = pgprot_noncached_wc(PAGE_KERNEL);
+#elif defined(pgprot_writecombine)
+       iomap->prot = pgprot_writecombine(PAGE_KERNEL);
+#else
+       iomap->prot = pgprot_noncached(PAGE_KERNEL);
+#endif
+
+       return iomap;
 }
 
 static inline void
-io_mapping_free(struct io_mapping *mapping)
+io_mapping_fini(struct io_mapping *mapping)
+{
+       iounmap(mapping->iomem);
+}
+
+/* Non-atomic map/unmap */
+static inline void __iomem *
+io_mapping_map_wc(struct io_mapping *mapping,
+                 unsigned long offset,
+                 unsigned long size)
+{
+       return mapping->iomem + offset;
+}
+
+static inline void
+io_mapping_unmap(void __iomem *vaddr)
 {
-       iounmap((void __force __iomem *) mapping);
 }
 
 /* Atomic map/unmap */
@@ -145,30 +161,42 @@ io_mapping_map_atomic_wc(struct io_mapping *mapping,
 {
        preempt_disable();
        pagefault_disable();
-       return ((char __force __iomem *) mapping) + offset;
+       return io_mapping_map_wc(mapping, offset, PAGE_SIZE);
 }
 
 static inline void
 io_mapping_unmap_atomic(void __iomem *vaddr)
 {
+       io_mapping_unmap(vaddr);
        pagefault_enable();
        preempt_enable();
 }
 
-/* Non-atomic map/unmap */
-static inline void __iomem *
-io_mapping_map_wc(struct io_mapping *mapping,
-                 unsigned long offset,
-                 unsigned long size)
+#endif /* HAVE_ATOMIC_IOMAP */
+
+static inline struct io_mapping *
+io_mapping_create_wc(resource_size_t base,
+                    unsigned long size)
 {
-       return ((char __force __iomem *) mapping) + offset;
+       struct io_mapping *iomap;
+
+       iomap = kmalloc(sizeof(*iomap), GFP_KERNEL);
+       if (!iomap)
+               return NULL;
+
+       if (!io_mapping_init_wc(iomap, base, size)) {
+               kfree(iomap);
+               return NULL;
+       }
+
+       return iomap;
 }
 
 static inline void
-io_mapping_unmap(void __iomem *vaddr)
+io_mapping_free(struct io_mapping *iomap)
 {
+       io_mapping_fini(iomap);
+       kfree(iomap);
 }
 
-#endif /* HAVE_ATOMIC_IOMAP */
-
 #endif /* _LINUX_IO_MAPPING_H */
index 96356ef..7056238 100644 (file)
@@ -269,10 +269,8 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
 }
 
 int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen);
-size_t kernfs_path_len(struct kernfs_node *kn);
 int kernfs_path_from_node(struct kernfs_node *root_kn, struct kernfs_node *kn,
                          char *buf, size_t buflen);
-char *kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen);
 void pr_cont_kernfs_name(struct kernfs_node *kn);
 void pr_cont_kernfs_path(struct kernfs_node *kn);
 struct kernfs_node *kernfs_get_parent(struct kernfs_node *kn);
@@ -341,12 +339,10 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)
 static inline int kernfs_name(struct kernfs_node *kn, char *buf, size_t buflen)
 { return -ENOSYS; }
 
-static inline size_t kernfs_path_len(struct kernfs_node *kn)
-{ return 0; }
-
-static inline char *kernfs_path(struct kernfs_node *kn, char *buf,
-                               size_t buflen)
-{ return NULL; }
+static inline int kernfs_path_from_node(struct kernfs_node *root_kn,
+                                       struct kernfs_node *kn,
+                                       char *buf, size_t buflen)
+{ return -ENOSYS; }
 
 static inline void pr_cont_kernfs_name(struct kernfs_node *kn) { }
 static inline void pr_cont_kernfs_path(struct kernfs_node *kn) { }
@@ -436,6 +432,22 @@ static inline void kernfs_init(void) { }
 
 #endif /* CONFIG_KERNFS */
 
+/**
+ * kernfs_path - build full path of a given node
+ * @kn: kernfs_node of interest
+ * @buf: buffer to copy @kn's name into
+ * @buflen: size of @buf
+ *
+ * Builds and returns the full path of @kn in @buf of @buflen bytes.  The
+ * path is built from the end of @buf so the returned pointer usually
+ * doesn't match @buf.  If @buf isn't long enough, @buf is nul terminated
+ * and %NULL is returned.
+ */
+static inline int kernfs_path(struct kernfs_node *kn, char *buf, size_t buflen)
+{
+       return kernfs_path_from_node(kn, NULL, buf, buflen);
+}
+
 static inline struct kernfs_node *
 kernfs_find_and_get(struct kernfs_node *kn, const char *name)
 {
index d743777..406c33d 100644 (file)
@@ -259,6 +259,12 @@ phys_addr_t paddr_vmcoreinfo_note(void);
        vmcoreinfo_append_str("NUMBER(%s)=%ld\n", #name, (long)name)
 #define VMCOREINFO_CONFIG(name) \
        vmcoreinfo_append_str("CONFIG_%s=y\n", #name)
+#define VMCOREINFO_PAGE_OFFSET(value) \
+       vmcoreinfo_append_str("PAGE_OFFSET=%lx\n", (unsigned long)value)
+#define VMCOREINFO_VMALLOC_START(value) \
+       vmcoreinfo_append_str("VMALLOC_START=%lx\n", (unsigned long)value)
+#define VMCOREINFO_VMEMMAP_START(value) \
+       vmcoreinfo_append_str("VMEMMAP_START=%lx\n", (unsigned long)value)
 
 extern struct kimage *kexec_image;
 extern struct kimage *kexec_crash_image;
index 4894c68..1c2a328 100644 (file)
@@ -38,6 +38,11 @@ extern void kmemleak_not_leak(const void *ptr) __ref;
 extern void kmemleak_ignore(const void *ptr) __ref;
 extern void kmemleak_scan_area(const void *ptr, size_t size, gfp_t gfp) __ref;
 extern void kmemleak_no_scan(const void *ptr) __ref;
+extern void kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
+                               gfp_t gfp) __ref;
+extern void kmemleak_free_part_phys(phys_addr_t phys, size_t size) __ref;
+extern void kmemleak_not_leak_phys(phys_addr_t phys) __ref;
+extern void kmemleak_ignore_phys(phys_addr_t phys) __ref;
 
 static inline void kmemleak_alloc_recursive(const void *ptr, size_t size,
                                            int min_count, unsigned long flags,
@@ -106,6 +111,19 @@ static inline void kmemleak_erase(void **ptr)
 static inline void kmemleak_no_scan(const void *ptr)
 {
 }
+static inline void kmemleak_alloc_phys(phys_addr_t phys, size_t size,
+                                      int min_count, gfp_t gfp)
+{
+}
+static inline void kmemleak_free_part_phys(phys_addr_t phys, size_t size)
+{
+}
+static inline void kmemleak_not_leak_phys(phys_addr_t phys)
+{
+}
+static inline void kmemleak_ignore_phys(phys_addr_t phys)
+{
+}
 
 #endif /* CONFIG_DEBUG_KMEMLEAK */
 
index e691b6a..a6e82a6 100644 (file)
@@ -10,6 +10,17 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                                           int node,
                                           const char namefmt[], ...);
 
+/**
+ * kthread_create - create a kthread on the current node
+ * @threadfn: the function to run in the thread
+ * @data: data pointer for @threadfn()
+ * @namefmt: printf-style format string for the thread name
+ * @...: arguments for @namefmt.
+ *
+ * This macro will create a kthread on the current node, leaving it in
+ * the stopped state.  This is just a helper for kthread_create_on_node();
+ * see the documentation there for more details.
+ */
 #define kthread_create(threadfn, data, namefmt, arg...) \
        kthread_create_on_node(threadfn, data, NUMA_NO_NODE, namefmt, ##arg)
 
@@ -44,7 +55,7 @@ bool kthread_should_stop(void);
 bool kthread_should_park(void);
 bool kthread_freezable_should_stop(bool *was_frozen);
 void *kthread_data(struct task_struct *k);
-void *probe_kthread_data(struct task_struct *k);
+void *kthread_probe_data(struct task_struct *k);
 int kthread_park(struct task_struct *k);
 void kthread_unpark(struct task_struct *k);
 void kthread_parkme(void);
@@ -57,16 +68,23 @@ extern int tsk_fork_get_node(struct task_struct *tsk);
  * Simple work processor based on kthread.
  *
  * This provides easier way to make use of kthreads.  A kthread_work
- * can be queued and flushed using queue/flush_kthread_work()
+ * can be queued and flushed using queue/kthread_flush_work()
  * respectively.  Queued kthread_works are processed by a kthread
  * running kthread_worker_fn().
  */
 struct kthread_work;
 typedef void (*kthread_work_func_t)(struct kthread_work *work);
+void kthread_delayed_work_timer_fn(unsigned long __data);
+
+enum {
+       KTW_FREEZABLE           = 1 << 0,       /* freeze during suspend */
+};
 
 struct kthread_worker {
+       unsigned int            flags;
        spinlock_t              lock;
        struct list_head        work_list;
+       struct list_head        delayed_work_list;
        struct task_struct      *task;
        struct kthread_work     *current_work;
 };
@@ -75,11 +93,19 @@ struct kthread_work {
        struct list_head        node;
        kthread_work_func_t     func;
        struct kthread_worker   *worker;
+       /* Number of canceling calls that are running at the moment. */
+       int                     canceling;
+};
+
+struct kthread_delayed_work {
+       struct kthread_work work;
+       struct timer_list timer;
 };
 
 #define KTHREAD_WORKER_INIT(worker)    {                               \
        .lock = __SPIN_LOCK_UNLOCKED((worker).lock),                    \
        .work_list = LIST_HEAD_INIT((worker).work_list),                \
+       .delayed_work_list = LIST_HEAD_INIT((worker).delayed_work_list),\
        }
 
 #define KTHREAD_WORK_INIT(work, fn)    {                               \
@@ -87,46 +113,88 @@ struct kthread_work {
        .func = (fn),                                                   \
        }
 
+#define KTHREAD_DELAYED_WORK_INIT(dwork, fn) {                         \
+       .work = KTHREAD_WORK_INIT((dwork).work, (fn)),                  \
+       .timer = __TIMER_INITIALIZER(kthread_delayed_work_timer_fn,     \
+                                    0, (unsigned long)&(dwork),        \
+                                    TIMER_IRQSAFE),                    \
+       }
+
 #define DEFINE_KTHREAD_WORKER(worker)                                  \
        struct kthread_worker worker = KTHREAD_WORKER_INIT(worker)
 
 #define DEFINE_KTHREAD_WORK(work, fn)                                  \
        struct kthread_work work = KTHREAD_WORK_INIT(work, fn)
 
+#define DEFINE_KTHREAD_DELAYED_WORK(dwork, fn)                         \
+       struct kthread_delayed_work dwork =                             \
+               KTHREAD_DELAYED_WORK_INIT(dwork, fn)
+
 /*
  * kthread_worker.lock needs its own lockdep class key when defined on
  * stack with lockdep enabled.  Use the following macros in such cases.
  */
 #ifdef CONFIG_LOCKDEP
 # define KTHREAD_WORKER_INIT_ONSTACK(worker)                           \
-       ({ init_kthread_worker(&worker); worker; })
+       ({ kthread_init_worker(&worker); worker; })
 # define DEFINE_KTHREAD_WORKER_ONSTACK(worker)                         \
        struct kthread_worker worker = KTHREAD_WORKER_INIT_ONSTACK(worker)
 #else
 # define DEFINE_KTHREAD_WORKER_ONSTACK(worker) DEFINE_KTHREAD_WORKER(worker)
 #endif
 
-extern void __init_kthread_worker(struct kthread_worker *worker,
+extern void __kthread_init_worker(struct kthread_worker *worker,
                        const char *name, struct lock_class_key *key);
 
-#define init_kthread_worker(worker)                                    \
+#define kthread_init_worker(worker)                                    \
        do {                                                            \
                static struct lock_class_key __key;                     \
-               __init_kthread_worker((worker), "("#worker")->lock", &__key); \
+               __kthread_init_worker((worker), "("#worker")->lock", &__key); \
        } while (0)
 
-#define init_kthread_work(work, fn)                                    \
+#define kthread_init_work(work, fn)                                    \
        do {                                                            \
                memset((work), 0, sizeof(struct kthread_work));         \
                INIT_LIST_HEAD(&(work)->node);                          \
                (work)->func = (fn);                                    \
        } while (0)
 
+#define kthread_init_delayed_work(dwork, fn)                           \
+       do {                                                            \
+               kthread_init_work(&(dwork)->work, (fn));                \
+               __setup_timer(&(dwork)->timer,                          \
+                             kthread_delayed_work_timer_fn,            \
+                             (unsigned long)(dwork),                   \
+                             TIMER_IRQSAFE);                           \
+       } while (0)
+
 int kthread_worker_fn(void *worker_ptr);
 
-bool queue_kthread_work(struct kthread_worker *worker,
+__printf(2, 3)
+struct kthread_worker *
+kthread_create_worker(unsigned int flags, const char namefmt[], ...);
+
+struct kthread_worker *
+kthread_create_worker_on_cpu(int cpu, unsigned int flags,
+                            const char namefmt[], ...);
+
+bool kthread_queue_work(struct kthread_worker *worker,
                        struct kthread_work *work);
-void flush_kthread_work(struct kthread_work *work);
-void flush_kthread_worker(struct kthread_worker *worker);
+
+bool kthread_queue_delayed_work(struct kthread_worker *worker,
+                               struct kthread_delayed_work *dwork,
+                               unsigned long delay);
+
+bool kthread_mod_delayed_work(struct kthread_worker *worker,
+                             struct kthread_delayed_work *dwork,
+                             unsigned long delay);
+
+void kthread_flush_work(struct kthread_work *work);
+void kthread_flush_worker(struct kthread_worker *worker);
+
+bool kthread_cancel_work_sync(struct kthread_work *work);
+bool kthread_cancel_delayed_work_sync(struct kthread_delayed_work *work);
+
+void kthread_destroy_worker(struct kthread_worker *worker);
 
 #endif /* _LINUX_KTHREAD_H */
index e37d4f9..616eef4 100644 (file)
@@ -46,7 +46,8 @@
 #ifdef CONFIG_ATA_NONSTANDARD
 #include <asm/libata-portmap.h>
 #else
-#include <asm-generic/libata-portmap.h>
+#define ATA_PRIMARY_IRQ(dev)   14
+#define ATA_SECONDARY_IRQ(dev) 15
 #endif
 
 /*
index 77c1417..5827614 100644 (file)
@@ -92,12 +92,21 @@ __mlx5_mask(typ, fld))
        ___t; \
 })
 
-#define MLX5_SET64(typ, p, fld, v) do { \
+#define __MLX5_SET64(typ, p, fld, v) do { \
        BUILD_BUG_ON(__mlx5_bit_sz(typ, fld) != 64); \
-       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
        *((__be64 *)(p) + __mlx5_64_off(typ, fld)) = cpu_to_be64(v); \
 } while (0)
 
+#define MLX5_SET64(typ, p, fld, v) do { \
+       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
+       __MLX5_SET64(typ, p, fld, v); \
+} while (0)
+
+#define MLX5_ARRAY_SET64(typ, p, fld, idx, v) do { \
+       BUILD_BUG_ON(__mlx5_bit_off(typ, fld) % 64); \
+       __MLX5_SET64(typ, p, fld[idx], v); \
+} while (0)
+
 #define MLX5_GET64(typ, p, fld) be64_to_cpu(*((__be64 *)(p) + __mlx5_64_off(typ, fld)))
 
 #define MLX5_GET64_PR(typ, p, fld) ({ \
index c6564ad..9094faf 100644 (file)
@@ -67,6 +67,7 @@ struct nfs4_stateid_struct {
                NFS4_DELEGATION_STATEID_TYPE,
                NFS4_LAYOUT_STATEID_TYPE,
                NFS4_PNFS_DS_STATEID_TYPE,
+               NFS4_REVOKED_STATEID_TYPE,
        } type;
 };
 
index 14a762d..b34097c 100644 (file)
@@ -103,6 +103,9 @@ struct nfs_client {
 #define NFS_SP4_MACH_CRED_WRITE    5   /* WRITE */
 #define NFS_SP4_MACH_CRED_COMMIT   6   /* COMMIT */
 #define NFS_SP4_MACH_CRED_PNFS_CLEANUP  7 /* LAYOUTRETURN */
+#if IS_ENABLED(CONFIG_NFS_V4_1)
+       wait_queue_head_t       cl_lock_waitq;
+#endif /* CONFIG_NFS_V4_1 */
 #endif /* CONFIG_NFS_V4 */
 
        /* Our own IP address, as a null-terminated string.
index 7cc0dee..beb1e10 100644 (file)
@@ -124,6 +124,11 @@ struct nfs_fattr {
                | NFS_ATTR_FATTR_SPACE_USED \
                | NFS_ATTR_FATTR_V4_SECURITY_LABEL)
 
+/*
+ * Maximal number of supported layout drivers.
+ */
+#define NFS_MAX_LAYOUT_TYPES 8
+
 /*
  * Info on the file system
  */
@@ -139,7 +144,8 @@ struct nfs_fsinfo {
        __u64                   maxfilesize;
        struct timespec         time_delta; /* server time granularity */
        __u32                   lease_time; /* in seconds */
-       __u32                   layouttype; /* supported pnfs layout driver */
+       __u32                   nlayouttypes; /* number of layouttypes */
+       __u32                   layouttype[NFS_MAX_LAYOUT_TYPES]; /* supported pnfs layout driver */
        __u32                   blksize; /* preferred pnfs io block size */
        __u32                   clone_blksize; /* granularity of a CLONE operation */
 };
index 747f401..dd15d39 100644 (file)
 #include <linux/hugetlb_inline.h>
 
 /*
- * Bits in mapping->flags.  The lower __GFP_BITS_SHIFT bits are the page
- * allocation mode flags.
+ * Bits in mapping->flags.
  */
 enum mapping_flags {
-       AS_EIO          = __GFP_BITS_SHIFT + 0, /* IO error on async write */
-       AS_ENOSPC       = __GFP_BITS_SHIFT + 1, /* ENOSPC on async write */
-       AS_MM_ALL_LOCKS = __GFP_BITS_SHIFT + 2, /* under mm_take_all_locks() */
-       AS_UNEVICTABLE  = __GFP_BITS_SHIFT + 3, /* e.g., ramdisk, SHM_LOCK */
-       AS_EXITING      = __GFP_BITS_SHIFT + 4, /* final truncate in progress */
+       AS_EIO          = 0,    /* IO error on async write */
+       AS_ENOSPC       = 1,    /* ENOSPC on async write */
+       AS_MM_ALL_LOCKS = 2,    /* under mm_take_all_locks() */
+       AS_UNEVICTABLE  = 3,    /* e.g., ramdisk, SHM_LOCK */
+       AS_EXITING      = 4,    /* final truncate in progress */
        /* writeback related tags are not used */
-       AS_NO_WRITEBACK_TAGS = __GFP_BITS_SHIFT + 5,
+       AS_NO_WRITEBACK_TAGS = 5,
 };
 
 static inline void mapping_set_error(struct address_space *mapping, int error)
@@ -78,7 +77,7 @@ static inline int mapping_use_writeback_tags(struct address_space *mapping)
 
 static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
 {
-       return (__force gfp_t)mapping->flags & __GFP_BITS_MASK;
+       return mapping->gfp_mask;
 }
 
 /* Restricts the given gfp_mask to what the mapping allows. */
@@ -94,8 +93,7 @@ static inline gfp_t mapping_gfp_constraint(struct address_space *mapping,
  */
 static inline void mapping_set_gfp_mask(struct address_space *m, gfp_t mask)
 {
-       m->flags = (m->flags & ~(__force unsigned long)__GFP_BITS_MASK) |
-                               (__force unsigned long)mask;
+       m->gfp_mask = mask;
 }
 
 void release_pages(struct page **pages, int nr, bool cold);
index e4c08c1..a1bacf1 100644 (file)
@@ -25,7 +25,6 @@ static inline int mm_pkey_alloc(struct mm_struct *mm)
 
 static inline int mm_pkey_free(struct mm_struct *mm, int pkey)
 {
-       WARN_ONCE(1, "free of protection key when disabled");
        return -EINVAL;
 }
 
index f1bbae0..2c6c511 100644 (file)
@@ -641,6 +641,7 @@ static inline void pwm_remove_table(struct pwm_lookup *table, size_t num)
 #ifdef CONFIG_PWM_SYSFS
 void pwmchip_sysfs_export(struct pwm_chip *chip);
 void pwmchip_sysfs_unexport(struct pwm_chip *chip);
+void pwmchip_sysfs_unexport_children(struct pwm_chip *chip);
 #else
 static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
 {
@@ -649,6 +650,10 @@ static inline void pwmchip_sysfs_export(struct pwm_chip *chip)
 static inline void pwmchip_sysfs_unexport(struct pwm_chip *chip)
 {
 }
+
+static inline void pwmchip_sysfs_unexport_children(struct pwm_chip *chip)
+{
+}
 #endif /* CONFIG_PWM_SYSFS */
 
 #endif /* __LINUX_PWM_H */
index 52b97db..af3581b 100644 (file)
@@ -461,6 +461,14 @@ static inline struct radix_tree_node *entry_to_node(void *ptr)
  *
  * This function updates @iter->index in the case of a successful lookup.
  * For tagged lookup it also eats @iter->tags.
+ *
+ * There are several cases where 'slot' can be passed in as NULL to this
+ * function.  These cases result from the use of radix_tree_iter_next() or
+ * radix_tree_iter_retry().  In these cases we don't end up dereferencing
+ * 'slot' because either:
+ * a) we are doing tagged iteration and iter->tags has been set to 0, or
+ * b) we are doing non-tagged iteration, and iter->index and iter->next_index
+ *    have been set up so that radix_tree_chunk_size() returns 1 or 0.
  */
 static __always_inline void **
 radix_tree_next_slot(void **slot, struct radix_tree_iter *iter, unsigned flags)
index 3d6e981..f7bb7a3 100644 (file)
@@ -34,7 +34,7 @@ extern const struct file_operations random_fops, urandom_fops;
 
 unsigned int get_random_int(void);
 unsigned long get_random_long(void);
-unsigned long randomize_range(unsigned long start, unsigned long end, unsigned long len);
+unsigned long randomize_page(unsigned long start, unsigned long range);
 
 u32 prandom_u32(void);
 void prandom_bytes(void *buf, size_t nbytes);
index ecbb34a..68c1448 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/timer.h>
 #include <linux/wait.h>
 #include <linux/list.h>
+#include <linux/irq_work.h>
 #include <linux/bug.h>
 #include <linux/fs.h>
 #include <linux/poll.h>
@@ -38,7 +39,7 @@ struct rchan_buf
        size_t subbufs_consumed;        /* count of sub-buffers consumed */
        struct rchan *chan;             /* associated channel */
        wait_queue_head_t read_wait;    /* reader wait queue */
-       struct timer_list timer;        /* reader wake-up timer */
+       struct irq_work wakeup_work;    /* reader wakeup */
        struct dentry *dentry;          /* channel file dentry */
        struct kref kref;               /* channel buffer refcount */
        struct page **page_array;       /* array of current buffer pages */
index 976ce3a..d0efd6e 100644 (file)
@@ -21,6 +21,7 @@ struct sem_array {
        struct list_head        list_id;        /* undo requests on this array */
        int                     sem_nsems;      /* no. of semaphores in array */
        int                     complex_count;  /* pending complex operations */
+       bool                    complex_mode;   /* no parallel simple ops */
 };
 
 #ifdef CONFIG_SYSVIPC
index 4ccf184..b1bc62b 100644 (file)
@@ -131,6 +131,7 @@ struct rpc_authops {
        struct rpc_auth *       (*create)(struct rpc_auth_create_args *, struct rpc_clnt *);
        void                    (*destroy)(struct rpc_auth *);
 
+       int                     (*hash_cred)(struct auth_cred *, unsigned int);
        struct rpc_cred *       (*lookup_cred)(struct rpc_auth *, struct auth_cred *, int);
        struct rpc_cred *       (*crcreate)(struct rpc_auth*, struct auth_cred *, int, gfp_t);
        int                     (*list_pseudoflavors)(rpc_authflavor_t *, int);
index 5c02b06..85cc819 100644 (file)
@@ -125,6 +125,13 @@ struct rpc_create_args {
        struct svc_xprt         *bc_xprt;       /* NFSv4.1 backchannel */
 };
 
+struct rpc_add_xprt_test {
+       int (*add_xprt_test)(struct rpc_clnt *,
+               struct rpc_xprt *,
+               void *calldata);
+       void *data;
+};
+
 /* Values for "flags" field */
 #define RPC_CLNT_CREATE_HARDRTRY       (1UL << 0)
 #define RPC_CLNT_CREATE_AUTOBIND       (1UL << 2)
@@ -198,6 +205,16 @@ int                rpc_clnt_add_xprt(struct rpc_clnt *, struct xprt_create *,
 void           rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt,
                        unsigned long timeo);
 
+int            rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *,
+                       struct rpc_xprt_switch *,
+                       struct rpc_xprt *,
+                       void *);
+
 const char *rpc_proc_name(const struct rpc_task *task);
+
+void rpc_clnt_xprt_switch_put(struct rpc_clnt *);
+void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *, struct rpc_xprt *);
+bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+                       const struct sockaddr *sap);
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_CLNT_H */
index 3b1ff38..cfda6ad 100644 (file)
 #define _LINUX_SUNRPC_RPC_RDMA_H
 
 #include <linux/types.h>
+#include <linux/bitops.h>
 
 #define RPCRDMA_VERSION                1
 #define rpcrdma_version                cpu_to_be32(RPCRDMA_VERSION)
 
+enum {
+       RPCRDMA_V1_DEF_INLINE_SIZE      = 1024,
+};
+
 struct rpcrdma_segment {
        __be32 rs_handle;       /* Registered memory handle */
        __be32 rs_length;       /* Length of the chunk in bytes */
@@ -129,4 +134,38 @@ enum rpcrdma_proc {
 #define rdma_done      cpu_to_be32(RDMA_DONE)
 #define rdma_error     cpu_to_be32(RDMA_ERROR)
 
+/*
+ * Private extension to RPC-over-RDMA Version One.
+ * Message passed during RDMA-CM connection set-up.
+ *
+ * Add new fields at the end, and don't permute existing
+ * fields.
+ */
+struct rpcrdma_connect_private {
+       __be32                  cp_magic;
+       u8                      cp_version;
+       u8                      cp_flags;
+       u8                      cp_send_size;
+       u8                      cp_recv_size;
+} __packed;
+
+#define rpcrdma_cmp_magic      __cpu_to_be32(0xf6ab0e18)
+
+enum {
+       RPCRDMA_CMP_VERSION             = 1,
+       RPCRDMA_CMP_F_SND_W_INV_OK      = BIT(0),
+};
+
+static inline u8
+rpcrdma_encode_buffer_size(unsigned int size)
+{
+       return (size >> 10) - 1;
+}
+
+static inline unsigned int
+rpcrdma_decode_buffer_size(u8 val)
+{
+       return ((unsigned int)val + 1) << 10;
+}
+
 #endif                         /* _LINUX_SUNRPC_RPC_RDMA_H */
index 817af0b..7ba040c 100644 (file)
@@ -239,8 +239,8 @@ struct rpc_task *rpc_wake_up_first(struct rpc_wait_queue *,
                                        void *);
 void           rpc_wake_up_status(struct rpc_wait_queue *, int);
 void           rpc_delay(struct rpc_task *, unsigned long);
-void *         rpc_malloc(struct rpc_task *, size_t);
-void           rpc_free(void *);
+int            rpc_malloc(struct rpc_task *);
+void           rpc_free(struct rpc_task *);
 int            rpciod_up(void);
 void           rpciod_down(void);
 int            __rpc_wait_for_completion_task(struct rpc_task *task, wait_bit_action_f *);
index d6917b8..cc3ae16 100644 (file)
@@ -86,6 +86,7 @@ struct svc_rdma_op_ctxt {
        unsigned long flags;
        enum dma_data_direction direction;
        int count;
+       unsigned int mapped_sges;
        struct ib_sge sge[RPCSVC_MAXPAGES];
        struct page *pages[RPCSVC_MAXPAGES];
 };
@@ -136,6 +137,7 @@ struct svcxprt_rdma {
        int                  sc_ord;            /* RDMA read limit */
        int                  sc_max_sge;
        int                  sc_max_sge_rd;     /* max sge for read target */
+       bool                 sc_snd_w_inv;      /* OK to use Send With Invalidate */
 
        atomic_t             sc_sq_count;       /* Number of SQ WR on queue */
        unsigned int         sc_sq_depth;       /* Depth of SQ */
@@ -193,6 +195,14 @@ struct svcxprt_rdma {
 
 #define RPCSVC_MAXPAYLOAD_RDMA RPCSVC_MAXPAYLOAD
 
+/* Track DMA maps for this transport and context */
+static inline void svc_rdma_count_mappings(struct svcxprt_rdma *rdma,
+                                          struct svc_rdma_op_ctxt *ctxt)
+{
+       ctxt->mapped_sges++;
+       atomic_inc(&rdma->sc_dma_used);
+}
+
 /* svc_rdma_backchannel.c */
 extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt,
                                    struct rpcrdma_msg *rmsgp,
index 70c6b92..56c48c8 100644 (file)
@@ -67,6 +67,18 @@ struct xdr_buf {
                        len;            /* Length of XDR encoded message */
 };
 
+static inline void
+xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
+{
+       buf->head[0].iov_base = start;
+       buf->head[0].iov_len = len;
+       buf->tail[0].iov_len = 0;
+       buf->page_len = 0;
+       buf->flags = 0;
+       buf->len = 0;
+       buf->buflen = len;
+}
+
 /*
  * pre-xdr'ed macros.
  */
index a16070d..a5da60b 100644 (file)
@@ -83,9 +83,11 @@ struct rpc_rqst {
        void (*rq_release_snd_buf)(struct rpc_rqst *); /* release rq_enc_pages */
        struct list_head        rq_list;
 
-       __u32 *                 rq_buffer;      /* XDR encode buffer */
-       size_t                  rq_callsize,
-                               rq_rcvsize;
+       void                    *rq_xprtdata;   /* Per-xprt private data */
+       void                    *rq_buffer;     /* Call XDR encode buffer */
+       size_t                  rq_callsize;
+       void                    *rq_rbuffer;    /* Reply XDR decode buffer */
+       size_t                  rq_rcvsize;
        size_t                  rq_xmit_bytes_sent;     /* total bytes sent */
        size_t                  rq_reply_bytes_recvd;   /* total reply bytes */
                                                        /* received */
@@ -127,8 +129,8 @@ struct rpc_xprt_ops {
        void            (*rpcbind)(struct rpc_task *task);
        void            (*set_port)(struct rpc_xprt *xprt, unsigned short port);
        void            (*connect)(struct rpc_xprt *xprt, struct rpc_task *task);
-       void *          (*buf_alloc)(struct rpc_task *task, size_t size);
-       void            (*buf_free)(void *buffer);
+       int             (*buf_alloc)(struct rpc_task *task);
+       void            (*buf_free)(struct rpc_task *task);
        int             (*send_request)(struct rpc_task *task);
        void            (*set_retrans_timeout)(struct rpc_task *task);
        void            (*timer)(struct rpc_xprt *xprt, struct rpc_task *task);
index 5a9acff..507418c 100644 (file)
@@ -66,4 +66,6 @@ extern struct rpc_xprt *xprt_iter_xprt(struct rpc_xprt_iter *xpi);
 extern struct rpc_xprt *xprt_iter_get_xprt(struct rpc_xprt_iter *xpi);
 extern struct rpc_xprt *xprt_iter_get_next(struct rpc_xprt_iter *xpi);
 
+extern bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+               const struct sockaddr *sap);
 #endif
index 39267dc..221b7a2 100644 (file)
@@ -53,8 +53,8 @@
 #define RPCRDMA_MAX_SLOT_TABLE (256U)
 
 #define RPCRDMA_MIN_INLINE  (1024)     /* min inline thresh */
-#define RPCRDMA_DEF_INLINE  (1024)     /* default inline thresh */
-#define RPCRDMA_MAX_INLINE  (3068)     /* max inline thresh */
+#define RPCRDMA_DEF_INLINE  (4096)     /* default inline thresh */
+#define RPCRDMA_MAX_INLINE  (65536)    /* max inline thresh */
 
 /* Memory registration strategies, by number.
  * This is part of a kernel / user space API. Do not remove. */
index c6ffe8b..aa17ccf 100644 (file)
 #include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/fence.h>
-
-struct sync_file_cb {
-       struct fence_cb cb;
-       struct fence *fence;
-       struct sync_file *sync_file;
-};
+#include <linux/fence-array.h>
 
 /**
  * struct sync_file - sync file to export to the userspace
@@ -32,10 +27,9 @@ struct sync_file_cb {
  * @kref:              reference count on fence.
  * @name:              name of sync_file.  Useful for debugging
  * @sync_file_list:    membership in global file list
- * @num_fences:                number of sync_pts in the fence
  * @wq:                        wait queue for fence signaling
- * @status:            0: signaled, >0:active, <0: error
- * @cbs:               sync_pts callback information
+ * @fence:             fence with the fences in the sync_file
+ * @cb:                        fence callback information
  */
 struct sync_file {
        struct file             *file;
@@ -44,14 +38,16 @@ struct sync_file {
 #ifdef CONFIG_DEBUG_FS
        struct list_head        sync_file_list;
 #endif
-       int num_fences;
 
        wait_queue_head_t       wq;
-       atomic_t                status;
 
-       struct sync_file_cb     cbs[];
+       struct fence            *fence;
+       struct fence_cb cb;
 };
 
+#define POLL_ENABLED FENCE_FLAG_USER_BITS
+
 struct sync_file *sync_file_create(struct fence *fence);
+struct fence *sync_file_get_fence(int fd);
 
 #endif /* _LINUX_SYNC_H */
index ee517be..511182a 100644 (file)
@@ -92,12 +92,24 @@ enum thermal_trend {
        THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */
 };
 
+/* Thermal notification reason */
+enum thermal_notify_event {
+       THERMAL_EVENT_UNSPECIFIED, /* Unspecified event */
+       THERMAL_EVENT_TEMP_SAMPLE, /* New Temperature sample */
+       THERMAL_TRIP_VIOLATED, /* TRIP Point violation */
+       THERMAL_TRIP_CHANGED, /* TRIP Point temperature changed */
+       THERMAL_DEVICE_DOWN, /* Thermal device is down */
+       THERMAL_DEVICE_UP, /* Thermal device is up after a down event */
+       THERMAL_DEVICE_POWER_CAPABILITY_CHANGED, /* power capability changed */
+};
+
 struct thermal_zone_device_ops {
        int (*bind) (struct thermal_zone_device *,
                     struct thermal_cooling_device *);
        int (*unbind) (struct thermal_zone_device *,
                       struct thermal_cooling_device *);
        int (*get_temp) (struct thermal_zone_device *, int *);
+       int (*set_trips) (struct thermal_zone_device *, int, int);
        int (*get_mode) (struct thermal_zone_device *,
                         enum thermal_device_mode *);
        int (*set_mode) (struct thermal_zone_device *,
@@ -168,6 +180,10 @@ struct thermal_attr {
  * @last_temperature:  previous temperature read
  * @emul_temperature:  emulated temperature when using CONFIG_THERMAL_EMULATION
  * @passive:           1 if you've crossed a passive trip point, 0 otherwise.
+ * @prev_low_trip:     the low current temperature if you've crossed a passive
+                       trip point.
+ * @prev_high_trip:    the above current temperature if you've crossed a
+                       passive trip point.
  * @forced_passive:    If > 0, temperature at which to switch on all ACPI
  *                     processor cooling devices.  Currently only used by the
  *                     step-wise governor.
@@ -182,6 +198,7 @@ struct thermal_attr {
  * @lock:      lock to protect thermal_instances list
  * @node:      node in thermal_tz_list (in thermal_core.c)
  * @poll_queue:        delayed work for polling
+ * @notify_event: Last notification event
  */
 struct thermal_zone_device {
        int id;
@@ -199,6 +216,8 @@ struct thermal_zone_device {
        int last_temperature;
        int emul_temperature;
        int passive;
+       int prev_low_trip;
+       int prev_high_trip;
        unsigned int forced_passive;
        atomic_t need_update;
        struct thermal_zone_device_ops *ops;
@@ -210,6 +229,7 @@ struct thermal_zone_device {
        struct mutex lock;
        struct list_head node;
        struct delayed_work poll_queue;
+       enum thermal_notify_event notify_event;
 };
 
 /**
@@ -333,6 +353,9 @@ struct thermal_genl_event {
  *
  * Optional:
  * @get_trend: a pointer to a function that reads the sensor temperature trend.
+ * @set_trips: a pointer to a function that sets a temperature window. When
+ *            this window is left the driver must inform the thermal core via
+ *            thermal_zone_device_update.
  * @set_emul_temp: a pointer to a function that sets sensor emulated
  *                temperature.
  * @set_trip_temp: a pointer to a function that sets the trip temperature on
@@ -340,7 +363,8 @@ struct thermal_genl_event {
  */
 struct thermal_zone_of_device_ops {
        int (*get_temp)(void *, int *);
-       int (*get_trend)(void *, long *);
+       int (*get_trend)(void *, int, enum thermal_trend *);
+       int (*set_trips)(void *, int, int);
        int (*set_emul_temp)(void *, int);
        int (*set_trip_temp)(void *, int, int);
 };
@@ -425,7 +449,9 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int,
                                     unsigned int);
 int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int,
                                       struct thermal_cooling_device *);
-void thermal_zone_device_update(struct thermal_zone_device *);
+void thermal_zone_device_update(struct thermal_zone_device *,
+                               enum thermal_notify_event);
+void thermal_zone_set_trips(struct thermal_zone_device *);
 
 struct thermal_cooling_device *thermal_cooling_device_register(char *, void *,
                const struct thermal_cooling_device_ops *);
@@ -435,6 +461,8 @@ thermal_of_cooling_device_register(struct device_node *np, char *, void *,
 void thermal_cooling_device_unregister(struct thermal_cooling_device *);
 struct thermal_zone_device *thermal_zone_get_zone_by_name(const char *name);
 int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp);
+int thermal_zone_get_slope(struct thermal_zone_device *tz);
+int thermal_zone_get_offset(struct thermal_zone_device *tz);
 
 int get_tz_trend(struct thermal_zone_device *, int);
 struct thermal_instance *get_thermal_instance(struct thermal_zone_device *,
@@ -473,7 +501,10 @@ static inline int thermal_zone_unbind_cooling_device(
        struct thermal_zone_device *tz, int trip,
        struct thermal_cooling_device *cdev)
 { return -ENODEV; }
-static inline void thermal_zone_device_update(struct thermal_zone_device *tz)
+static inline void thermal_zone_device_update(struct thermal_zone_device *tz,
+                                             enum thermal_notify_event event)
+{ }
+static inline void thermal_zone_set_trips(struct thermal_zone_device *tz)
 { }
 static inline struct thermal_cooling_device *
 thermal_cooling_device_register(char *type, void *devdata,
@@ -492,6 +523,12 @@ static inline struct thermal_zone_device *thermal_zone_get_zone_by_name(
 static inline int thermal_zone_get_temp(
                struct thermal_zone_device *tz, int *temp)
 { return -ENODEV; }
+static inline int thermal_zone_get_slope(
+               struct thermal_zone_device *tz)
+{ return -ENODEV; }
+static inline int thermal_zone_get_offset(
+               struct thermal_zone_device *tz)
+{ return -ENODEV; }
 static inline int get_tz_trend(struct thermal_zone_device *tz, int trip)
 { return -ENODEV; }
 static inline struct thermal_instance *
index 8c3b412..ee162e3 100644 (file)
@@ -73,34 +73,6 @@ static inline void vga_set_legacy_decoding(struct pci_dev *pdev,
                                           unsigned int decodes) { };
 #endif
 
-/**
- *     vga_get         - acquire & locks VGA resources
- *
- *     @pdev: pci device of the VGA card or NULL for the system default
- *     @rsrc: bit mask of resources to acquire and lock
- *     @interruptible: blocking should be interruptible by signals ?
- *
- *     This function acquires VGA resources for the given
- *     card and mark those resources locked. If the resource requested
- *     are "normal" (and not legacy) resources, the arbiter will first check
- *     whether the card is doing legacy decoding for that type of resource. If
- *     yes, the lock is "converted" into a legacy resource lock.
- *     The arbiter will first look for all VGA cards that might conflict
- *     and disable their IOs and/or Memory access, including VGA forwarding
- *     on P2P bridges if necessary, so that the requested resources can
- *     be used. Then, the card is marked as locking these resources and
- *     the IO and/or Memory accesse are enabled on the card (including
- *     VGA forwarding on parent P2P bridges if any).
- *     This function will block if some conflicting card is already locking
- *     one of the required resources (or any resource on a different bus
- *     segment, since P2P bridges don't differenciate VGA memory and IO
- *     afaik). You can indicate whether this blocking should be interruptible
- *     by a signal (for userland interface) or not.
- *     Must not be called at interrupt time or in atomic context.
- *     If the card already owns the resources, the function succeeds.
- *     Nested calls are supported (a per-resource counter is maintained)
- */
-
 #if defined(CONFIG_VGA_ARB)
 extern int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible);
 #else
@@ -108,11 +80,14 @@ static inline int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interrupt
 #endif
 
 /**
- *     vga_get_interruptible
+ * vga_get_interruptible
+ * @pdev: pci device of the VGA card or NULL for the system default
+ * @rsrc: bit mask of resources to acquire and lock
  *
- *     Shortcut to vga_get
+ * Shortcut to vga_get with interruptible set to true.
+ *
+ * On success, release the VGA resource again with vga_put().
  */
-
 static inline int vga_get_interruptible(struct pci_dev *pdev,
                                        unsigned int rsrc)
 {
@@ -120,47 +95,26 @@ static inline int vga_get_interruptible(struct pci_dev *pdev,
 }
 
 /**
- *     vga_get_uninterruptible
+ * vga_get_uninterruptible - shortcut to vga_get()
+ * @pdev: pci device of the VGA card or NULL for the system default
+ * @rsrc: bit mask of resources to acquire and lock
  *
- *     Shortcut to vga_get
+ * Shortcut to vga_get with interruptible set to false.
+ *
+ * On success, release the VGA resource again with vga_put().
  */
-
 static inline int vga_get_uninterruptible(struct pci_dev *pdev,
                                          unsigned int rsrc)
 {
        return vga_get(pdev, rsrc, 0);
 }
 
-/**
- *     vga_tryget      - try to acquire & lock legacy VGA resources
- *
- *     @pdev: pci devivce of VGA card or NULL for system default
- *     @rsrc: bit mask of resources to acquire and lock
- *
- *     This function performs the same operation as vga_get(), but
- *     will return an error (-EBUSY) instead of blocking if the resources
- *     are already locked by another card. It can be called in any context
- */
-
 #if defined(CONFIG_VGA_ARB)
 extern int vga_tryget(struct pci_dev *pdev, unsigned int rsrc);
 #else
 static inline int vga_tryget(struct pci_dev *pdev, unsigned int rsrc) { return 0; }
 #endif
 
-/**
- *     vga_put         - release lock on legacy VGA resources
- *
- *     @pdev: pci device of VGA card or NULL for system default
- *     @rsrc: but mask of resource to release
- *
- *     This function releases resources previously locked by vga_get()
- *     or vga_tryget(). The resources aren't disabled right away, so
- *     that a subsequence vga_get() on the same card will succeed
- *     immediately. Resources have a counter, so locks are only
- *     released if the counter reaches 0.
- */
-
 #if defined(CONFIG_VGA_ARB)
 extern void vga_put(struct pci_dev *pdev, unsigned int rsrc);
 #else
@@ -168,25 +122,6 @@ extern void vga_put(struct pci_dev *pdev, unsigned int rsrc);
 #endif
 
 
-/**
- *     vga_default_device
- *
- *     This can be defined by the platform. The default implementation
- *     is rather dumb and will probably only work properly on single
- *     vga card setups and/or x86 platforms.
- *
- *     If your VGA default device is not PCI, you'll have to return
- *     NULL here. In this case, I assume it will not conflict with
- *     any PCI card. If this is not true, I'll have to define two archs
- *     hooks for enabling/disabling the VGA default device if that is
- *     possible. This may be a problem with real _ISA_ VGA cards, in
- *     addition to a PCI one. I don't know at this point how to deal
- *     with that card. Can theirs IOs be disabled at all ? If not, then
- *     I suppose it's a matter of having the proper arch hook telling
- *     us about it, so we basically never allow anybody to succeed a
- *     vga_get()...
- */
-
 #ifdef CONFIG_VGA_ARB
 extern struct pci_dev *vga_default_device(void);
 extern void vga_set_default_device(struct pci_dev *pdev);
@@ -195,14 +130,11 @@ static inline struct pci_dev *vga_default_device(void) { return NULL; };
 static inline void vga_set_default_device(struct pci_dev *pdev) { };
 #endif
 
-/**
- *     vga_conflicts
- *
- *     Architectures should define this if they have several
- *     independent PCI domains that can afford concurrent VGA
- *     decoding
+/*
+ * Architectures should define this if they have several
+ * independent PCI domains that can afford concurrent VGA
+ * decoding
  */
-
 #ifndef __ARCH_HAS_VGA_CONFLICT
 static inline int vga_conflicts(struct pci_dev *p1, struct pci_dev *p2)
 {
@@ -210,34 +142,6 @@ static inline int vga_conflicts(struct pci_dev *p1, struct pci_dev *p2)
 }
 #endif
 
-/**
- *     vga_client_register
- *
- *     @pdev: pci device of the VGA client
- *     @cookie: client cookie to be used in callbacks
- *     @irq_set_state: irq state change callback
- *     @set_vga_decode: vga decode change callback
- *
- *     return value: 0 on success, -1 on failure
- *     Register a client with the VGA arbitration logic
- *
- *     Clients have two callback mechanisms they can use.
- *     irq enable/disable callback -
- *             If a client can't disable its GPUs VGA resources, then we
- *             need to be able to ask it to turn off its irqs when we
- *             turn off its mem and io decoding.
- *     set_vga_decode
- *             If a client can disable its GPU VGA resource, it will
- *             get a callback from this to set the encode/decode state
- *
- * Rationale: we cannot disable VGA decode resources unconditionally
- * some single GPU laptops seem to require ACPI or BIOS access to the
- * VGA registers to control things like backlights etc.
- * Hopefully newer multi-GPU laptops do something saner, and desktops
- * won't have any special ACPI for this.
- * They driver will get a callback when VGA arbitration is first used
- * by userspace since we some older X servers have issues.
- */
 #if defined(CONFIG_VGA_ARB)
 int vga_client_register(struct pci_dev *pdev, void *cookie,
                        void (*irq_set_state)(void *cookie, bool state),
index 7047bc7..35a4d81 100644 (file)
@@ -19,6 +19,7 @@
 struct watchdog_ops;
 struct watchdog_device;
 struct watchdog_core_data;
+struct watchdog_governor;
 
 /** struct watchdog_ops - The watchdog-devices operations
  *
@@ -28,6 +29,7 @@ struct watchdog_core_data;
  * @ping:      The routine that sends a keepalive ping to the watchdog device.
  * @status:    The routine that shows the status of the watchdog device.
  * @set_timeout:The routine for setting the watchdog devices timeout value (in seconds).
+ * @set_pretimeout:The routine for setting the watchdog devices pretimeout.
  * @get_timeleft:The routine that gets the time left before a reset (in seconds).
  * @restart:   The routine for restarting the machine.
  * @ioctl:     The routines that handles extra ioctl calls.
@@ -46,6 +48,7 @@ struct watchdog_ops {
        int (*ping)(struct watchdog_device *);
        unsigned int (*status)(struct watchdog_device *);
        int (*set_timeout)(struct watchdog_device *, unsigned int);
+       int (*set_pretimeout)(struct watchdog_device *, unsigned int);
        unsigned int (*get_timeleft)(struct watchdog_device *);
        int (*restart)(struct watchdog_device *, unsigned long, void *);
        long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
@@ -59,8 +62,10 @@ struct watchdog_ops {
  *             watchdog device.
  * @info:      Pointer to a watchdog_info structure.
  * @ops:       Pointer to the list of watchdog operations.
+ * @gov:       Pointer to watchdog pretimeout governor.
  * @bootstatus:        Status of the watchdog device at boot.
  * @timeout:   The watchdog devices timeout value (in seconds).
+ * @pretimeout: The watchdog devices pre_timeout value.
  * @min_timeout:The watchdog devices minimum timeout value (in seconds).
  * @max_timeout:The watchdog devices maximum timeout value (in seconds)
  *             as configurable from user space. Only relevant if
@@ -94,8 +99,10 @@ struct watchdog_device {
        const struct attribute_group **groups;
        const struct watchdog_info *info;
        const struct watchdog_ops *ops;
+       const struct watchdog_governor *gov;
        unsigned int bootstatus;
        unsigned int timeout;
+       unsigned int pretimeout;
        unsigned int min_timeout;
        unsigned int max_timeout;
        unsigned int min_hw_heartbeat_ms;
@@ -163,6 +170,13 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne
                 t > wdd->max_timeout);
 }
 
+/* Use the following function to check if a pretimeout value is invalid */
+static inline bool watchdog_pretimeout_invalid(struct watchdog_device *wdd,
+                                              unsigned int t)
+{
+       return t && wdd->timeout && t >= wdd->timeout;
+}
+
 /* Use the following functions to manipulate watchdog driver specific data */
 static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
 {
@@ -174,6 +188,16 @@ static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
        return wdd->driver_data;
 }
 
+/* Use the following functions to report watchdog pretimeout event */
+#if IS_ENABLED(CONFIG_WATCHDOG_PRETIMEOUT_GOV)
+void watchdog_notify_pretimeout(struct watchdog_device *wdd);
+#else
+static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd)
+{
+       pr_alert("watchdog%d: pretimeout event\n", wdd->id);
+}
+#endif
+
 /* drivers/watchdog/watchdog_core.c */
 void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
 extern int watchdog_init_timeout(struct watchdog_device *wdd,
index 6360c25..f32f7ef 100644 (file)
 #ifndef __long_aligned
 #define __long_aligned __attribute__((aligned((sizeof(long)))))
 #endif
-/*
- * Less bad way to call ioctl from within the kernel; this needs to be
- * done some other way to get the call out of interrupt context.
- * Needs "ioctl" variable to be supplied by calling context.
- */
-#define IOCTL(dev, arg, cmd) ({                \
-       int res = 0;                    \
-       mm_segment_t fs = get_fs();     \
-       set_fs(get_ds());               \
-       res = ioctl(dev, arg, cmd);     \
-       set_fs(fs);                     \
-       res; })
 
 #define BOND_MODE(bond) ((bond)->params.mode)
 
index fe78f02..bd19faa 100644 (file)
@@ -796,9 +796,9 @@ enum station_parameters_apply_mask {
  *     (or NULL for no change)
  * @supported_rates_len: number of supported rates
  * @sta_flags_mask: station flags that changed
- *     (bitmask of BIT(NL80211_STA_FLAG_...))
+ *     (bitmask of BIT(%NL80211_STA_FLAG_...))
  * @sta_flags_set: station flags values
- *     (bitmask of BIT(NL80211_STA_FLAG_...))
+ *     (bitmask of BIT(%NL80211_STA_FLAG_...))
  * @listen_interval: listen interval or -1 for no change
  * @aid: AID or zero for no change
  * @peer_aid: mesh peer AID or zero for no change
@@ -3088,47 +3088,54 @@ struct ieee80211_iface_limit {
  *
  * 1. Allow #STA <= 1, #AP <= 1, matching BI, channels = 1, 2 total:
  *
- *  struct ieee80211_iface_limit limits1[] = {
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
- *  };
- *  struct ieee80211_iface_combination combination1 = {
- *     .limits = limits1,
- *     .n_limits = ARRAY_SIZE(limits1),
- *     .max_interfaces = 2,
- *     .beacon_int_infra_match = true,
- *  };
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits1[] = {
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_AP}, },
+ *     };
+ *     struct ieee80211_iface_combination combination1 = {
+ *             .limits = limits1,
+ *             .n_limits = ARRAY_SIZE(limits1),
+ *             .max_interfaces = 2,
+ *             .beacon_int_infra_match = true,
+ *     };
  *
  *
  * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
  *
- *  struct ieee80211_iface_limit limits2[] = {
- *     { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
- *                          BIT(NL80211_IFTYPE_P2P_GO), },
- *  };
- *  struct ieee80211_iface_combination combination2 = {
- *     .limits = limits2,
- *     .n_limits = ARRAY_SIZE(limits2),
- *     .max_interfaces = 8,
- *     .num_different_channels = 1,
- *  };
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits2[] = {
+ *             { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
+ *                                  BIT(NL80211_IFTYPE_P2P_GO), },
+ *     };
+ *     struct ieee80211_iface_combination combination2 = {
+ *             .limits = limits2,
+ *             .n_limits = ARRAY_SIZE(limits2),
+ *             .max_interfaces = 8,
+ *             .num_different_channels = 1,
+ *     };
  *
  *
  * 3. Allow #STA <= 1, #{P2P-client,P2P-GO} <= 3 on two channels, 4 total.
  *
- * This allows for an infrastructure connection and three P2P connections.
- *
- *  struct ieee80211_iface_limit limits3[] = {
- *     { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
- *     { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) |
- *                          BIT(NL80211_IFTYPE_P2P_CLIENT), },
- *  };
- *  struct ieee80211_iface_combination combination3 = {
- *     .limits = limits3,
- *     .n_limits = ARRAY_SIZE(limits3),
- *     .max_interfaces = 4,
- *     .num_different_channels = 2,
- *  };
+ *    This allows for an infrastructure connection and three P2P connections.
+ *
+ *    .. code-block:: c
+ *
+ *     struct ieee80211_iface_limit limits3[] = {
+ *             { .max = 1, .types = BIT(NL80211_IFTYPE_STATION), },
+ *             { .max = 3, .types = BIT(NL80211_IFTYPE_P2P_GO) |
+ *                                  BIT(NL80211_IFTYPE_P2P_CLIENT), },
+ *     };
+ *     struct ieee80211_iface_combination combination3 = {
+ *             .limits = limits3,
+ *             .n_limits = ARRAY_SIZE(limits3),
+ *             .max_interfaces = 4,
+ *             .num_different_channels = 2,
+ *     };
+ *
  */
 struct ieee80211_iface_combination {
        const struct ieee80211_iface_limit *limits;
index b220dab..3832099 100644 (file)
@@ -114,6 +114,25 @@ static inline u32 l3mdev_fib_table(const struct net_device *dev)
        return tb_id;
 }
 
+static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
+{
+       struct net_device *dev;
+       bool rc = false;
+
+       if (ifindex == 0)
+               return false;
+
+       rcu_read_lock();
+
+       dev = dev_get_by_index_rcu(net, ifindex);
+       if (dev)
+               rc = netif_is_l3_master(dev);
+
+       rcu_read_unlock();
+
+       return rc;
+}
+
 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, struct flowi6 *fl6);
 
 static inline
@@ -207,6 +226,11 @@ static inline u32 l3mdev_fib_table_by_index(struct net *net, int ifindex)
        return 0;
 }
 
+static inline bool netif_index_is_l3_master(struct net *net, int ifindex)
+{
+       return false;
+}
+
 static inline
 struct dst_entry *l3mdev_link_scope_lookup(struct net *net, struct flowi6 *fl6)
 {
diff --git a/include/soc/fsl/bman.h b/include/soc/fsl/bman.h
new file mode 100644 (file)
index 0000000..eaaf56d
--- /dev/null
@@ -0,0 +1,129 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FSL_BMAN_H
+#define __FSL_BMAN_H
+
+/* wrapper for 48-bit buffers */
+struct bm_buffer {
+       union {
+               struct {
+                       __be16 bpid; /* hi 8-bits reserved */
+                       __be16 hi; /* High 16-bits of 48-bit address */
+                       __be32 lo; /* Low 32-bits of 48-bit address */
+               };
+               __be64 data;
+       };
+} __aligned(8);
+/*
+ * Restore the 48 bit address previously stored in BMan
+ * hardware pools as a dma_addr_t
+ */
+static inline dma_addr_t bm_buf_addr(const struct bm_buffer *buf)
+{
+       return be64_to_cpu(buf->data) & 0xffffffffffffLLU;
+}
+
+static inline u64 bm_buffer_get64(const struct bm_buffer *buf)
+{
+       return be64_to_cpu(buf->data) & 0xffffffffffffLLU;
+}
+
+static inline void bm_buffer_set64(struct bm_buffer *buf, u64 addr)
+{
+       buf->hi = cpu_to_be16(upper_32_bits(addr));
+       buf->lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+static inline u8 bm_buffer_get_bpid(const struct bm_buffer *buf)
+{
+       return be16_to_cpu(buf->bpid) & 0xff;
+}
+
+static inline void bm_buffer_set_bpid(struct bm_buffer *buf, int bpid)
+{
+       buf->bpid = cpu_to_be16(bpid & 0xff);
+}
+
+/* Managed portal, high-level i/face */
+
+/* Portal and Buffer Pools */
+struct bman_portal;
+struct bman_pool;
+
+#define BM_POOL_MAX            64 /* max # of buffer pools */
+
+/**
+ * bman_new_pool - Allocates a Buffer Pool object
+ *
+ * Creates a pool object, and returns a reference to it or NULL on error.
+ */
+struct bman_pool *bman_new_pool(void);
+
+/**
+ * bman_free_pool - Deallocates a Buffer Pool object
+ * @pool: the pool object to release
+ */
+void bman_free_pool(struct bman_pool *pool);
+
+/**
+ * bman_get_bpid - Returns a pool object's BPID.
+ * @pool: the pool object
+ *
+ * The returned value is the index of the encapsulated buffer pool,
+ * in the range of [0, @BM_POOL_MAX-1].
+ */
+int bman_get_bpid(const struct bman_pool *pool);
+
+/**
+ * bman_release - Release buffer(s) to the buffer pool
+ * @pool: the buffer pool object to release to
+ * @bufs: an array of buffers to release
+ * @num: the number of buffers in @bufs (1-8)
+ *
+ * Adds the given buffers to RCR entries. If the RCR ring is unresponsive,
+ * the function will return -ETIMEDOUT. Otherwise, it returns zero.
+ */
+int bman_release(struct bman_pool *pool, const struct bm_buffer *bufs, u8 num);
+
+/**
+ * bman_acquire - Acquire buffer(s) from a buffer pool
+ * @pool: the buffer pool object to acquire from
+ * @bufs: array for storing the acquired buffers
+ * @num: the number of buffers desired (@bufs is at least this big)
+ *
+ * Issues an "Acquire" command via the portal's management command interface.
+ * The return value will be the number of buffers obtained from the pool, or a
+ * negative error code if a h/w error or pool starvation was encountered. In
+ * the latter case, the content of @bufs is undefined.
+ */
+int bman_acquire(struct bman_pool *pool, struct bm_buffer *bufs, u8 num);
+
+#endif /* __FSL_BMAN_H */
diff --git a/include/soc/fsl/qman.h b/include/soc/fsl/qman.h
new file mode 100644 (file)
index 0000000..37f3eb0
--- /dev/null
@@ -0,0 +1,1074 @@
+/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Freescale Semiconductor nor the
+ *      names of its contributors may be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __FSL_QMAN_H
+#define __FSL_QMAN_H
+
+#include <linux/bitops.h>
+
+/* Hardware constants */
+#define QM_CHANNEL_SWPORTAL0 0
+#define QMAN_CHANNEL_POOL1 0x21
+#define QMAN_CHANNEL_POOL1_REV3 0x401
+extern u16 qm_channel_pool1;
+
+/* Portal processing (interrupt) sources */
+#define QM_PIRQ_CSCI   0x00100000      /* Congestion State Change */
+#define QM_PIRQ_EQCI   0x00080000      /* Enqueue Command Committed */
+#define QM_PIRQ_EQRI   0x00040000      /* EQCR Ring (below threshold) */
+#define QM_PIRQ_DQRI   0x00020000      /* DQRR Ring (non-empty) */
+#define QM_PIRQ_MRI    0x00010000      /* MR Ring (non-empty) */
+/*
+ * This mask contains all the interrupt sources that need handling except DQRI,
+ * ie. that if present should trigger slow-path processing.
+ */
+#define QM_PIRQ_SLOW   (QM_PIRQ_CSCI | QM_PIRQ_EQCI | QM_PIRQ_EQRI | \
+                        QM_PIRQ_MRI)
+
+/* For qman_static_dequeue_*** APIs */
+#define QM_SDQCR_CHANNELS_POOL_MASK    0x00007fff
+/* for n in [1,15] */
+#define QM_SDQCR_CHANNELS_POOL(n)      (0x00008000 >> (n))
+/* for conversion from n of qm_channel */
+static inline u32 QM_SDQCR_CHANNELS_POOL_CONV(u16 channel)
+{
+       return QM_SDQCR_CHANNELS_POOL(channel + 1 - qm_channel_pool1);
+}
+
+/* --- QMan data structures (and associated constants) --- */
+
+/* "Frame Descriptor (FD)" */
+struct qm_fd {
+       union {
+               struct {
+                       u8 cfg8b_w1;
+                       u8 bpid;        /* Buffer Pool ID */
+                       u8 cfg8b_w3;
+                       u8 addr_hi;     /* high 8-bits of 40-bit address */
+                       __be32 addr_lo; /* low 32-bits of 40-bit address */
+               } __packed;
+               __be64 data;
+       };
+       __be32 cfg;     /* format, offset, length / congestion */
+       union {
+               __be32 cmd;
+               __be32 status;
+       };
+} __aligned(8);
+
+#define QM_FD_FORMAT_SG                BIT(31)
+#define QM_FD_FORMAT_LONG      BIT(30)
+#define QM_FD_FORMAT_COMPOUND  BIT(29)
+#define QM_FD_FORMAT_MASK      GENMASK(31, 29)
+#define QM_FD_OFF_SHIFT                20
+#define QM_FD_OFF_MASK         GENMASK(28, 20)
+#define QM_FD_LEN_MASK         GENMASK(19, 0)
+#define QM_FD_LEN_BIG_MASK     GENMASK(28, 0)
+
+enum qm_fd_format {
+       /*
+        * 'contig' implies a contiguous buffer, whereas 'sg' implies a
+        * scatter-gather table. 'big' implies a 29-bit length with no offset
+        * field, otherwise length is 20-bit and offset is 9-bit. 'compound'
+        * implies a s/g-like table, where each entry itself represents a frame
+        * (contiguous or scatter-gather) and the 29-bit "length" is
+        * interpreted purely for congestion calculations, ie. a "congestion
+        * weight".
+        */
+       qm_fd_contig = 0,
+       qm_fd_contig_big = QM_FD_FORMAT_LONG,
+       qm_fd_sg = QM_FD_FORMAT_SG,
+       qm_fd_sg_big = QM_FD_FORMAT_SG | QM_FD_FORMAT_LONG,
+       qm_fd_compound = QM_FD_FORMAT_COMPOUND
+};
+
+static inline dma_addr_t qm_fd_addr(const struct qm_fd *fd)
+{
+       return be64_to_cpu(fd->data) & 0xffffffffffLLU;
+}
+
+static inline u64 qm_fd_addr_get64(const struct qm_fd *fd)
+{
+       return be64_to_cpu(fd->data) & 0xffffffffffLLU;
+}
+
+static inline void qm_fd_addr_set64(struct qm_fd *fd, u64 addr)
+{
+       fd->addr_hi = upper_32_bits(addr);
+       fd->addr_lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+/*
+ * The 'format' field indicates the interpretation of the remaining
+ * 29 bits of the 32-bit word.
+ * If 'format' is _contig or _sg, 20b length and 9b offset.
+ * If 'format' is _contig_big or _sg_big, 29b length.
+ * If 'format' is _compound, 29b "congestion weight".
+ */
+static inline enum qm_fd_format qm_fd_get_format(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_FORMAT_MASK;
+}
+
+static inline int qm_fd_get_offset(const struct qm_fd *fd)
+{
+       return (be32_to_cpu(fd->cfg) & QM_FD_OFF_MASK) >> QM_FD_OFF_SHIFT;
+}
+
+static inline int qm_fd_get_length(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_LEN_MASK;
+}
+
+static inline int qm_fd_get_len_big(const struct qm_fd *fd)
+{
+       return be32_to_cpu(fd->cfg) & QM_FD_LEN_BIG_MASK;
+}
+
+static inline void qm_fd_set_param(struct qm_fd *fd, enum qm_fd_format fmt,
+                                  int off, int len)
+{
+       fd->cfg = cpu_to_be32(fmt | (len & QM_FD_LEN_BIG_MASK) |
+                             ((off << QM_FD_OFF_SHIFT) & QM_FD_OFF_MASK));
+}
+
+#define qm_fd_set_contig(fd, off, len) \
+       qm_fd_set_param(fd, qm_fd_contig, off, len)
+#define qm_fd_set_sg(fd, off, len) qm_fd_set_param(fd, qm_fd_sg, off, len)
+#define qm_fd_set_contig_big(fd, len) \
+       qm_fd_set_param(fd, qm_fd_contig_big, 0, len)
+#define qm_fd_set_sg_big(fd, len) qm_fd_set_param(fd, qm_fd_sg_big, 0, len)
+
+static inline void qm_fd_clear_fd(struct qm_fd *fd)
+{
+       fd->data = 0;
+       fd->cfg = 0;
+       fd->cmd = 0;
+}
+
+/* Scatter/Gather table entry */
+struct qm_sg_entry {
+       union {
+               struct {
+                       u8 __reserved1[3];
+                       u8 addr_hi;     /* high 8-bits of 40-bit address */
+                       __be32 addr_lo; /* low 32-bits of 40-bit address */
+               };
+               __be64 data;
+       };
+       __be32 cfg;     /* E bit, F bit, length */
+       u8 __reserved2;
+       u8 bpid;
+       __be16 offset; /* 13-bit, _res[13-15]*/
+} __packed;
+
+#define QM_SG_LEN_MASK GENMASK(29, 0)
+#define QM_SG_OFF_MASK GENMASK(12, 0)
+#define QM_SG_FIN      BIT(30)
+#define QM_SG_EXT      BIT(31)
+
+static inline dma_addr_t qm_sg_addr(const struct qm_sg_entry *sg)
+{
+       return be64_to_cpu(sg->data) & 0xffffffffffLLU;
+}
+
+static inline u64 qm_sg_entry_get64(const struct qm_sg_entry *sg)
+{
+       return be64_to_cpu(sg->data) & 0xffffffffffLLU;
+}
+
+static inline void qm_sg_entry_set64(struct qm_sg_entry *sg, u64 addr)
+{
+       sg->addr_hi = upper_32_bits(addr);
+       sg->addr_lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+static inline bool qm_sg_entry_is_final(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_FIN;
+}
+
+static inline bool qm_sg_entry_is_ext(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_EXT;
+}
+
+static inline int qm_sg_entry_get_len(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->cfg) & QM_SG_LEN_MASK;
+}
+
+static inline void qm_sg_entry_set_len(struct qm_sg_entry *sg, int len)
+{
+       sg->cfg = cpu_to_be32(len & QM_SG_LEN_MASK);
+}
+
+static inline void qm_sg_entry_set_f(struct qm_sg_entry *sg, int len)
+{
+       sg->cfg = cpu_to_be32(QM_SG_FIN | (len & QM_SG_LEN_MASK));
+}
+
+static inline int qm_sg_entry_get_off(const struct qm_sg_entry *sg)
+{
+       return be32_to_cpu(sg->offset) & QM_SG_OFF_MASK;
+}
+
+/* "Frame Dequeue Response" */
+struct qm_dqrr_entry {
+       u8 verb;
+       u8 stat;
+       u16 seqnum;     /* 15-bit */
+       u8 tok;
+       u8 __reserved2[3];
+       u32 fqid;       /* 24-bit */
+       u32 contextB;
+       struct qm_fd fd;
+       u8 __reserved4[32];
+} __packed;
+#define QM_DQRR_VERB_VBIT              0x80
+#define QM_DQRR_VERB_MASK              0x7f    /* where the verb contains; */
+#define QM_DQRR_VERB_FRAME_DEQUEUE     0x60    /* "this format" */
+#define QM_DQRR_STAT_FQ_EMPTY          0x80    /* FQ empty */
+#define QM_DQRR_STAT_FQ_HELDACTIVE     0x40    /* FQ held active */
+#define QM_DQRR_STAT_FQ_FORCEELIGIBLE  0x20    /* FQ was force-eligible'd */
+#define QM_DQRR_STAT_FD_VALID          0x10    /* has a non-NULL FD */
+#define QM_DQRR_STAT_UNSCHEDULED       0x02    /* Unscheduled dequeue */
+#define QM_DQRR_STAT_DQCR_EXPIRED      0x01    /* VDQCR or PDQCR expired*/
+
+/* "ERN Message Response" */
+/* "FQ State Change Notification" */
+union qm_mr_entry {
+       struct {
+               u8 verb;
+               u8 __reserved[63];
+       };
+       struct {
+               u8 verb;
+               u8 dca;
+               u16 seqnum;
+               u8 rc;          /* Rej Code: 8-bit */
+               u8 orp_hi;      /* ORP: 24-bit */
+               u16 orp_lo;
+               u32 fqid;       /* 24-bit */
+               u32 tag;
+               struct qm_fd fd;
+               u8 __reserved1[32];
+       } __packed ern;
+       struct {
+               u8 verb;
+               u8 fqs;         /* Frame Queue Status */
+               u8 __reserved1[6];
+               u32 fqid;       /* 24-bit */
+               u32 contextB;
+               u8 __reserved2[48];
+       } __packed fq;          /* FQRN/FQRNI/FQRL/FQPN */
+};
+#define QM_MR_VERB_VBIT                        0x80
+/*
+ * ERNs originating from direct-connect portals ("dcern") use 0x20 as a verb
+ * which would be invalid as a s/w enqueue verb. A s/w ERN can be distinguished
+ * from the other MR types by noting if the 0x20 bit is unset.
+ */
+#define QM_MR_VERB_TYPE_MASK           0x27
+#define QM_MR_VERB_DC_ERN              0x20
+#define QM_MR_VERB_FQRN                        0x21
+#define QM_MR_VERB_FQRNI               0x22
+#define QM_MR_VERB_FQRL                        0x23
+#define QM_MR_VERB_FQPN                        0x24
+#define QM_MR_RC_MASK                  0xf0    /* contains one of; */
+#define QM_MR_RC_CGR_TAILDROP          0x00
+#define QM_MR_RC_WRED                  0x10
+#define QM_MR_RC_ERROR                 0x20
+#define QM_MR_RC_ORPWINDOW_EARLY       0x30
+#define QM_MR_RC_ORPWINDOW_LATE                0x40
+#define QM_MR_RC_FQ_TAILDROP           0x50
+#define QM_MR_RC_ORPWINDOW_RETIRED     0x60
+#define QM_MR_RC_ORP_ZERO              0x70
+#define QM_MR_FQS_ORLPRESENT           0x02    /* ORL fragments to come */
+#define QM_MR_FQS_NOTEMPTY             0x01    /* FQ has enqueued frames */
+
+/*
+ * An identical structure of FQD fields is present in the "Init FQ" command and
+ * the "Query FQ" result, it's suctioned out into the "struct qm_fqd" type.
+ * Within that, the 'stashing' and 'taildrop' pieces are also factored out, the
+ * latter has two inlines to assist with converting to/from the mant+exp
+ * representation.
+ */
+struct qm_fqd_stashing {
+       /* See QM_STASHING_EXCL_<...> */
+       u8 exclusive;
+       /* Numbers of cachelines */
+       u8 cl; /* _res[6-7], as[4-5], ds[2-3], cs[0-1] */
+};
+
+struct qm_fqd_oac {
+       /* "Overhead Accounting Control", see QM_OAC_<...> */
+       u8 oac; /* oac[6-7], _res[0-5] */
+       /* Two's-complement value (-128 to +127) */
+       s8 oal; /* "Overhead Accounting Length" */
+};
+
+struct qm_fqd {
+       /* _res[6-7], orprws[3-5], oa[2], olws[0-1] */
+       u8 orpc;
+       u8 cgid;
+       __be16 fq_ctrl; /* See QM_FQCTRL_<...> */
+       __be16 dest_wq; /* channel[3-15], wq[0-2] */
+       __be16 ics_cred; /* 15-bit */
+       /*
+        * For "Initialize Frame Queue" commands, the write-enable mask
+        * determines whether 'td' or 'oac_init' is observed. For query
+        * commands, this field is always 'td', and 'oac_query' (below) reflects
+        * the Overhead ACcounting values.
+        */
+       union {
+               __be16 td; /* "Taildrop": _res[13-15], mant[5-12], exp[0-4] */
+               struct qm_fqd_oac oac_init;
+       };
+       __be32 context_b;
+       union {
+               /* Treat it as 64-bit opaque */
+               __be64 opaque;
+               struct {
+                       __be32 hi;
+                       __be32 lo;
+               };
+               /* Treat it as s/w portal stashing config */
+               /* see "FQD Context_A field used for [...]" */
+               struct {
+                       struct qm_fqd_stashing stashing;
+                       /*
+                        * 48-bit address of FQ context to
+                        * stash, must be cacheline-aligned
+                        */
+                       __be16 context_hi;
+                       __be32 context_lo;
+               } __packed;
+       } context_a;
+       struct qm_fqd_oac oac_query;
+} __packed;
+
+#define QM_FQD_CHAN_OFF                3
+#define QM_FQD_WQ_MASK         GENMASK(2, 0)
+#define QM_FQD_TD_EXP_MASK     GENMASK(4, 0)
+#define QM_FQD_TD_MANT_OFF     5
+#define QM_FQD_TD_MANT_MASK    GENMASK(12, 5)
+#define QM_FQD_TD_MAX          0xe0000000
+#define QM_FQD_TD_MANT_MAX     0xff
+#define QM_FQD_OAC_OFF         6
+#define QM_FQD_AS_OFF          4
+#define QM_FQD_DS_OFF          2
+#define QM_FQD_XS_MASK         0x3
+
+/* 64-bit converters for context_hi/lo */
+static inline u64 qm_fqd_stashing_get64(const struct qm_fqd *fqd)
+{
+       return be64_to_cpu(fqd->context_a.opaque) & 0xffffffffffffULL;
+}
+
+static inline dma_addr_t qm_fqd_stashing_addr(const struct qm_fqd *fqd)
+{
+       return be64_to_cpu(fqd->context_a.opaque) & 0xffffffffffffULL;
+}
+
+static inline u64 qm_fqd_context_a_get64(const struct qm_fqd *fqd)
+{
+       return qm_fqd_stashing_get64(fqd);
+}
+
+static inline void qm_fqd_stashing_set64(struct qm_fqd *fqd, u64 addr)
+{
+       fqd->context_a.context_hi = upper_32_bits(addr);
+       fqd->context_a.context_lo = lower_32_bits(addr);
+}
+
+static inline void qm_fqd_context_a_set64(struct qm_fqd *fqd, u64 addr)
+{
+       fqd->context_a.hi = cpu_to_be16(upper_32_bits(addr));
+       fqd->context_a.lo = cpu_to_be32(lower_32_bits(addr));
+}
+
+/* convert a threshold value into mant+exp representation */
+static inline int qm_fqd_set_taildrop(struct qm_fqd *fqd, u32 val,
+                                     int roundup)
+{
+       u32 e = 0;
+       int td, oddbit = 0;
+
+       if (val > QM_FQD_TD_MAX)
+               return -ERANGE;
+
+       while (val > QM_FQD_TD_MANT_MAX) {
+               oddbit = val & 1;
+               val >>= 1;
+               e++;
+               if (roundup && oddbit)
+                       val++;
+       }
+
+       td = (val << QM_FQD_TD_MANT_OFF) & QM_FQD_TD_MANT_MASK;
+       td |= (e & QM_FQD_TD_EXP_MASK);
+       fqd->td = cpu_to_be16(td);
+       return 0;
+}
+/* and the other direction */
+static inline int qm_fqd_get_taildrop(const struct qm_fqd *fqd)
+{
+       int td = be16_to_cpu(fqd->td);
+
+       return ((td & QM_FQD_TD_MANT_MASK) >> QM_FQD_TD_MANT_OFF)
+               << (td & QM_FQD_TD_EXP_MASK);
+}
+
+static inline void qm_fqd_set_stashing(struct qm_fqd *fqd, u8 as, u8 ds, u8 cs)
+{
+       struct qm_fqd_stashing *st = &fqd->context_a.stashing;
+
+       st->cl = ((as & QM_FQD_XS_MASK) << QM_FQD_AS_OFF) |
+                ((ds & QM_FQD_XS_MASK) << QM_FQD_DS_OFF) |
+                (cs & QM_FQD_XS_MASK);
+}
+
+static inline u8 qm_fqd_get_stashing(const struct qm_fqd *fqd)
+{
+       return fqd->context_a.stashing.cl;
+}
+
+static inline void qm_fqd_set_oac(struct qm_fqd *fqd, u8 val)
+{
+       fqd->oac_init.oac = val << QM_FQD_OAC_OFF;
+}
+
+static inline void qm_fqd_set_oal(struct qm_fqd *fqd, s8 val)
+{
+       fqd->oac_init.oal = val;
+}
+
+static inline void qm_fqd_set_destwq(struct qm_fqd *fqd, int ch, int wq)
+{
+       fqd->dest_wq = cpu_to_be16((ch << QM_FQD_CHAN_OFF) |
+                                  (wq & QM_FQD_WQ_MASK));
+}
+
+static inline int qm_fqd_get_chan(const struct qm_fqd *fqd)
+{
+       return be16_to_cpu(fqd->dest_wq) >> QM_FQD_CHAN_OFF;
+}
+
+static inline int qm_fqd_get_wq(const struct qm_fqd *fqd)
+{
+       return be16_to_cpu(fqd->dest_wq) & QM_FQD_WQ_MASK;
+}
+
+/* See "Frame Queue Descriptor (FQD)" */
+/* Frame Queue Descriptor (FQD) field 'fq_ctrl' uses these constants */
+#define QM_FQCTRL_MASK         0x07ff  /* 'fq_ctrl' flags; */
+#define QM_FQCTRL_CGE          0x0400  /* Congestion Group Enable */
+#define QM_FQCTRL_TDE          0x0200  /* Tail-Drop Enable */
+#define QM_FQCTRL_CTXASTASHING 0x0080  /* Context-A stashing */
+#define QM_FQCTRL_CPCSTASH     0x0040  /* CPC Stash Enable */
+#define QM_FQCTRL_FORCESFDR    0x0008  /* High-priority SFDRs */
+#define QM_FQCTRL_AVOIDBLOCK   0x0004  /* Don't block active */
+#define QM_FQCTRL_HOLDACTIVE   0x0002  /* Hold active in portal */
+#define QM_FQCTRL_PREFERINCACHE        0x0001  /* Aggressively cache FQD */
+#define QM_FQCTRL_LOCKINCACHE  QM_FQCTRL_PREFERINCACHE /* older naming */
+
+/* See "FQD Context_A field used for [...] */
+/* Frame Queue Descriptor (FQD) field 'CONTEXT_A' uses these constants */
+#define QM_STASHING_EXCL_ANNOTATION    0x04
+#define QM_STASHING_EXCL_DATA          0x02
+#define QM_STASHING_EXCL_CTX           0x01
+
+/* See "Intra Class Scheduling" */
+/* FQD field 'OAC' (Overhead ACcounting) uses these constants */
+#define QM_OAC_ICS             0x2 /* Accounting for Intra-Class Scheduling */
+#define QM_OAC_CG              0x1 /* Accounting for Congestion Groups */
+
+/*
+ * This struct represents the 32-bit "WR_PARM_[GYR]" parameters in CGR fields
+ * and associated commands/responses. The WRED parameters are calculated from
+ * these fields as follows;
+ *   MaxTH = MA * (2 ^ Mn)
+ *   Slope = SA / (2 ^ Sn)
+ *    MaxP = 4 * (Pn + 1)
+ */
+struct qm_cgr_wr_parm {
+       /* MA[24-31], Mn[19-23], SA[12-18], Sn[6-11], Pn[0-5] */
+       u32 word;
+};
+/*
+ * This struct represents the 13-bit "CS_THRES" CGR field. In the corresponding
+ * management commands, this is padded to a 16-bit structure field, so that's
+ * how we represent it here. The congestion state threshold is calculated from
+ * these fields as follows;
+ *   CS threshold = TA * (2 ^ Tn)
+ */
+struct qm_cgr_cs_thres {
+       /* _res[13-15], TA[5-12], Tn[0-4] */
+       u16 word;
+};
+/*
+ * This identical structure of CGR fields is present in the "Init/Modify CGR"
+ * commands and the "Query CGR" result. It's suctioned out here into its own
+ * struct.
+ */
+struct __qm_mc_cgr {
+       struct qm_cgr_wr_parm wr_parm_g;
+       struct qm_cgr_wr_parm wr_parm_y;
+       struct qm_cgr_wr_parm wr_parm_r;
+       u8 wr_en_g;     /* boolean, use QM_CGR_EN */
+       u8 wr_en_y;     /* boolean, use QM_CGR_EN */
+       u8 wr_en_r;     /* boolean, use QM_CGR_EN */
+       u8 cscn_en;     /* boolean, use QM_CGR_EN */
+       union {
+               struct {
+                       u16 cscn_targ_upd_ctrl; /* use QM_CSCN_TARG_UDP_ */
+                       u16 cscn_targ_dcp_low;  /* CSCN_TARG_DCP low-16bits */
+               };
+               u32 cscn_targ;  /* use QM_CGR_TARG_* */
+       };
+       u8 cstd_en;     /* boolean, use QM_CGR_EN */
+       u8 cs;          /* boolean, only used in query response */
+       struct qm_cgr_cs_thres cs_thres; /* use qm_cgr_cs_thres_set64() */
+       u8 mode;        /* QMAN_CGR_MODE_FRAME not supported in rev1.0 */
+} __packed;
+#define QM_CGR_EN              0x01 /* For wr_en_*, cscn_en, cstd_en */
+#define QM_CGR_TARG_UDP_CTRL_WRITE_BIT 0x8000 /* value written to portal bit*/
+#define QM_CGR_TARG_UDP_CTRL_DCP       0x4000 /* 0: SWP, 1: DCP */
+#define QM_CGR_TARG_PORTAL(n)  (0x80000000 >> (n)) /* s/w portal, 0-9 */
+#define QM_CGR_TARG_FMAN0      0x00200000 /* direct-connect portal: fman0 */
+#define QM_CGR_TARG_FMAN1      0x00100000 /*                      : fman1 */
+/* Convert CGR thresholds to/from "cs_thres" format */
+static inline u64 qm_cgr_cs_thres_get64(const struct qm_cgr_cs_thres *th)
+{
+       return ((th->word >> 5) & 0xff) << (th->word & 0x1f);
+}
+
+static inline int qm_cgr_cs_thres_set64(struct qm_cgr_cs_thres *th, u64 val,
+                                       int roundup)
+{
+       u32 e = 0;
+       int oddbit = 0;
+
+       while (val > 0xff) {
+               oddbit = val & 1;
+               val >>= 1;
+               e++;
+               if (roundup && oddbit)
+                       val++;
+       }
+       th->word = ((val & 0xff) << 5) | (e & 0x1f);
+       return 0;
+}
+
+/* "Initialize FQ" */
+struct qm_mcc_initfq {
+       u8 __reserved1[2];
+       u16 we_mask;    /* Write Enable Mask */
+       u32 fqid;       /* 24-bit */
+       u16 count;      /* Initialises 'count+1' FQDs */
+       struct qm_fqd fqd; /* the FQD fields go here */
+       u8 __reserved2[30];
+} __packed;
+/* "Initialize/Modify CGR" */
+struct qm_mcc_initcgr {
+       u8 __reserve1[2];
+       u16 we_mask;    /* Write Enable Mask */
+       struct __qm_mc_cgr cgr; /* CGR fields */
+       u8 __reserved2[2];
+       u8 cgid;
+       u8 __reserved3[32];
+} __packed;
+
+/* INITFQ-specific flags */
+#define QM_INITFQ_WE_MASK              0x01ff  /* 'Write Enable' flags; */
+#define QM_INITFQ_WE_OAC               0x0100
+#define QM_INITFQ_WE_ORPC              0x0080
+#define QM_INITFQ_WE_CGID              0x0040
+#define QM_INITFQ_WE_FQCTRL            0x0020
+#define QM_INITFQ_WE_DESTWQ            0x0010
+#define QM_INITFQ_WE_ICSCRED           0x0008
+#define QM_INITFQ_WE_TDTHRESH          0x0004
+#define QM_INITFQ_WE_CONTEXTB          0x0002
+#define QM_INITFQ_WE_CONTEXTA          0x0001
+/* INITCGR/MODIFYCGR-specific flags */
+#define QM_CGR_WE_MASK                 0x07ff  /* 'Write Enable Mask'; */
+#define QM_CGR_WE_WR_PARM_G            0x0400
+#define QM_CGR_WE_WR_PARM_Y            0x0200
+#define QM_CGR_WE_WR_PARM_R            0x0100
+#define QM_CGR_WE_WR_EN_G              0x0080
+#define QM_CGR_WE_WR_EN_Y              0x0040
+#define QM_CGR_WE_WR_EN_R              0x0020
+#define QM_CGR_WE_CSCN_EN              0x0010
+#define QM_CGR_WE_CSCN_TARG            0x0008
+#define QM_CGR_WE_CSTD_EN              0x0004
+#define QM_CGR_WE_CS_THRES             0x0002
+#define QM_CGR_WE_MODE                 0x0001
+
+#define QMAN_CGR_FLAG_USE_INIT      0x00000001
+
+       /* Portal and Frame Queues */
+/* Represents a managed portal */
+struct qman_portal;
+
+/*
+ * This object type represents QMan frame queue descriptors (FQD), it is
+ * cacheline-aligned, and initialised by qman_create_fq(). The structure is
+ * defined further down.
+ */
+struct qman_fq;
+
+/*
+ * This object type represents a QMan congestion group, it is defined further
+ * down.
+ */
+struct qman_cgr;
+
+/*
+ * This enum, and the callback type that returns it, are used when handling
+ * dequeued frames via DQRR. Note that for "null" callbacks registered with the
+ * portal object (for handling dequeues that do not demux because contextB is
+ * NULL), the return value *MUST* be qman_cb_dqrr_consume.
+ */
+enum qman_cb_dqrr_result {
+       /* DQRR entry can be consumed */
+       qman_cb_dqrr_consume,
+       /* Like _consume, but requests parking - FQ must be held-active */
+       qman_cb_dqrr_park,
+       /* Does not consume, for DCA mode only. */
+       qman_cb_dqrr_defer,
+       /*
+        * Stop processing without consuming this ring entry. Exits the current
+        * qman_p_poll_dqrr() or interrupt-handling, as appropriate. If within
+        * an interrupt handler, the callback would typically call
+        * qman_irqsource_remove(QM_PIRQ_DQRI) before returning this value,
+        * otherwise the interrupt will reassert immediately.
+        */
+       qman_cb_dqrr_stop,
+       /* Like qman_cb_dqrr_stop, but consumes the current entry. */
+       qman_cb_dqrr_consume_stop
+};
+typedef enum qman_cb_dqrr_result (*qman_cb_dqrr)(struct qman_portal *qm,
+                                       struct qman_fq *fq,
+                                       const struct qm_dqrr_entry *dqrr);
+
+/*
+ * This callback type is used when handling ERNs, FQRNs and FQRLs via MR. They
+ * are always consumed after the callback returns.
+ */
+typedef void (*qman_cb_mr)(struct qman_portal *qm, struct qman_fq *fq,
+                          const union qm_mr_entry *msg);
+
+/*
+ * s/w-visible states. Ie. tentatively scheduled + truly scheduled + active +
+ * held-active + held-suspended are just "sched". Things like "retired" will not
+ * be assumed until it is complete (ie. QMAN_FQ_STATE_CHANGING is set until
+ * then, to indicate it's completing and to gate attempts to retry the retire
+ * command). Note, park commands do not set QMAN_FQ_STATE_CHANGING because it's
+ * technically impossible in the case of enqueue DCAs (which refer to DQRR ring
+ * index rather than the FQ that ring entry corresponds to), so repeated park
+ * commands are allowed (if you're silly enough to try) but won't change FQ
+ * state, and the resulting park notifications move FQs from "sched" to
+ * "parked".
+ */
+enum qman_fq_state {
+       qman_fq_state_oos,
+       qman_fq_state_parked,
+       qman_fq_state_sched,
+       qman_fq_state_retired
+};
+
+#define QMAN_FQ_STATE_CHANGING      0x80000000 /* 'state' is changing */
+#define QMAN_FQ_STATE_NE            0x40000000 /* retired FQ isn't empty */
+#define QMAN_FQ_STATE_ORL           0x20000000 /* retired FQ has ORL */
+#define QMAN_FQ_STATE_BLOCKOOS      0xe0000000 /* if any are set, no OOS */
+#define QMAN_FQ_STATE_CGR_EN        0x10000000 /* CGR enabled */
+#define QMAN_FQ_STATE_VDQCR         0x08000000 /* being volatile dequeued */
+
+/*
+ * Frame queue objects (struct qman_fq) are stored within memory passed to
+ * qman_create_fq(), as this allows stashing of caller-provided demux callback
+ * pointers at no extra cost to stashing of (driver-internal) FQ state. If the
+ * caller wishes to add per-FQ state and have it benefit from dequeue-stashing,
+ * they should;
+ *
+ * (a) extend the qman_fq structure with their state; eg.
+ *
+ *     // myfq is allocated and driver_fq callbacks filled in;
+ *     struct my_fq {
+ *        struct qman_fq base;
+ *        int an_extra_field;
+ *        [ ... add other fields to be associated with each FQ ...]
+ *     } *myfq = some_my_fq_allocator();
+ *     struct qman_fq *fq = qman_create_fq(fqid, flags, &myfq->base);
+ *
+ *     // in a dequeue callback, access extra fields from 'fq' via a cast;
+ *     struct my_fq *myfq = (struct my_fq *)fq;
+ *     do_something_with(myfq->an_extra_field);
+ *     [...]
+ *
+ * (b) when and if configuring the FQ for context stashing, specify how ever
+ *     many cachelines are required to stash 'struct my_fq', to accelerate not
+ *     only the QMan driver but the callback as well.
+ */
+
+struct qman_fq_cb {
+       qman_cb_dqrr dqrr;      /* for dequeued frames */
+       qman_cb_mr ern;         /* for s/w ERNs */
+       qman_cb_mr fqs;         /* frame-queue state changes*/
+};
+
+struct qman_fq {
+       /* Caller of qman_create_fq() provides these demux callbacks */
+       struct qman_fq_cb cb;
+       /*
+        * These are internal to the driver, don't touch. In particular, they
+        * may change, be removed, or extended (so you shouldn't rely on
+        * sizeof(qman_fq) being a constant).
+        */
+       u32 fqid, idx;
+       unsigned long flags;
+       enum qman_fq_state state;
+       int cgr_groupid;
+};
+
+/*
+ * This callback type is used when handling congestion group entry/exit.
+ * 'congested' is non-zero on congestion-entry, and zero on congestion-exit.
+ */
+typedef void (*qman_cb_cgr)(struct qman_portal *qm,
+                           struct qman_cgr *cgr, int congested);
+
+struct qman_cgr {
+       /* Set these prior to qman_create_cgr() */
+       u32 cgrid; /* 0..255, but u32 to allow specials like -1, 256, etc.*/
+       qman_cb_cgr cb;
+       /* These are private to the driver */
+       u16 chan; /* portal channel this object is created on */
+       struct list_head node;
+};
+
+/* Flags to qman_create_fq() */
+#define QMAN_FQ_FLAG_NO_ENQUEUE             0x00000001 /* can't enqueue */
+#define QMAN_FQ_FLAG_NO_MODIFY      0x00000002 /* can only enqueue */
+#define QMAN_FQ_FLAG_TO_DCPORTAL     0x00000004 /* consumed by CAAM/PME/Fman */
+#define QMAN_FQ_FLAG_DYNAMIC_FQID    0x00000020 /* (de)allocate fqid */
+
+/* Flags to qman_init_fq() */
+#define QMAN_INITFQ_FLAG_SCHED      0x00000001 /* schedule rather than park */
+#define QMAN_INITFQ_FLAG_LOCAL      0x00000004 /* set dest portal */
+
+       /* Portal Management */
+/**
+ * qman_p_irqsource_add - add processing sources to be interrupt-driven
+ * @bits: bitmask of QM_PIRQ_**I processing sources
+ *
+ * Adds processing sources that should be interrupt-driven (rather than
+ * processed via qman_poll_***() functions).
+ */
+void qman_p_irqsource_add(struct qman_portal *p, u32 bits);
+
+/**
+ * qman_p_irqsource_remove - remove processing sources from being int-driven
+ * @bits: bitmask of QM_PIRQ_**I processing sources
+ *
+ * Removes processing sources from being interrupt-driven, so that they will
+ * instead be processed via qman_poll_***() functions.
+ */
+void qman_p_irqsource_remove(struct qman_portal *p, u32 bits);
+
+/**
+ * qman_affine_cpus - return a mask of cpus that have affine portals
+ */
+const cpumask_t *qman_affine_cpus(void);
+
+/**
+ * qman_affine_channel - return the channel ID of an portal
+ * @cpu: the cpu whose affine portal is the subject of the query
+ *
+ * If @cpu is -1, the affine portal for the current CPU will be used. It is a
+ * bug to call this function for any value of @cpu (other than -1) that is not a
+ * member of the mask returned from qman_affine_cpus().
+ */
+u16 qman_affine_channel(int cpu);
+
+/**
+ * qman_get_affine_portal - return the portal pointer affine to cpu
+ * @cpu: the cpu whose affine portal is the subject of the query
+ */
+struct qman_portal *qman_get_affine_portal(int cpu);
+
+/**
+ * qman_p_poll_dqrr - process DQRR (fast-path) entries
+ * @limit: the maximum number of DQRR entries to process
+ *
+ * Use of this function requires that DQRR processing not be interrupt-driven.
+ * The return value represents the number of DQRR entries processed.
+ */
+int qman_p_poll_dqrr(struct qman_portal *p, unsigned int limit);
+
+/**
+ * qman_p_static_dequeue_add - Add pool channels to the portal SDQCR
+ * @pools: bit-mask of pool channels, using QM_SDQCR_CHANNELS_POOL(n)
+ *
+ * Adds a set of pool channels to the portal's static dequeue command register
+ * (SDQCR). The requested pools are limited to those the portal has dequeue
+ * access to.
+ */
+void qman_p_static_dequeue_add(struct qman_portal *p, u32 pools);
+
+       /* FQ management */
+/**
+ * qman_create_fq - Allocates a FQ
+ * @fqid: the index of the FQD to encapsulate, must be "Out of Service"
+ * @flags: bit-mask of QMAN_FQ_FLAG_*** options
+ * @fq: memory for storing the 'fq', with callbacks filled in
+ *
+ * Creates a frame queue object for the given @fqid, unless the
+ * QMAN_FQ_FLAG_DYNAMIC_FQID flag is set in @flags, in which case a FQID is
+ * dynamically allocated (or the function fails if none are available). Once
+ * created, the caller should not touch the memory at 'fq' except as extended to
+ * adjacent memory for user-defined fields (see the definition of "struct
+ * qman_fq" for more info). NO_MODIFY is only intended for enqueuing to
+ * pre-existing frame-queues that aren't to be otherwise interfered with, it
+ * prevents all other modifications to the frame queue. The TO_DCPORTAL flag
+ * causes the driver to honour any contextB modifications requested in the
+ * qm_init_fq() API, as this indicates the frame queue will be consumed by a
+ * direct-connect portal (PME, CAAM, or Fman). When frame queues are consumed by
+ * software portals, the contextB field is controlled by the driver and can't be
+ * modified by the caller.
+ */
+int qman_create_fq(u32 fqid, u32 flags, struct qman_fq *fq);
+
+/**
+ * qman_destroy_fq - Deallocates a FQ
+ * @fq: the frame queue object to release
+ *
+ * The memory for this frame queue object ('fq' provided in qman_create_fq()) is
+ * not deallocated but the caller regains ownership, to do with as desired. The
+ * FQ must be in the 'out-of-service' or in the 'parked' state.
+ */
+void qman_destroy_fq(struct qman_fq *fq);
+
+/**
+ * qman_fq_fqid - Queries the frame queue ID of a FQ object
+ * @fq: the frame queue object to query
+ */
+u32 qman_fq_fqid(struct qman_fq *fq);
+
+/**
+ * qman_init_fq - Initialises FQ fields, leaves the FQ "parked" or "scheduled"
+ * @fq: the frame queue object to modify, must be 'parked' or new.
+ * @flags: bit-mask of QMAN_INITFQ_FLAG_*** options
+ * @opts: the FQ-modification settings, as defined in the low-level API
+ *
+ * The @opts parameter comes from the low-level portal API. Select
+ * QMAN_INITFQ_FLAG_SCHED in @flags to cause the frame queue to be scheduled
+ * rather than parked. NB, @opts can be NULL.
+ *
+ * Note that some fields and options within @opts may be ignored or overwritten
+ * by the driver;
+ * 1. the 'count' and 'fqid' fields are always ignored (this operation only
+ * affects one frame queue: @fq).
+ * 2. the QM_INITFQ_WE_CONTEXTB option of the 'we_mask' field and the associated
+ * 'fqd' structure's 'context_b' field are sometimes overwritten;
+ *   - if @fq was not created with QMAN_FQ_FLAG_TO_DCPORTAL, then context_b is
+ *     initialised to a value used by the driver for demux.
+ *   - if context_b is initialised for demux, so is context_a in case stashing
+ *     is requested (see item 4).
+ * (So caller control of context_b is only possible for TO_DCPORTAL frame queue
+ * objects.)
+ * 3. if @flags contains QMAN_INITFQ_FLAG_LOCAL, the 'fqd' structure's
+ * 'dest::channel' field will be overwritten to match the portal used to issue
+ * the command. If the WE_DESTWQ write-enable bit had already been set by the
+ * caller, the channel workqueue will be left as-is, otherwise the write-enable
+ * bit is set and the workqueue is set to a default of 4. If the "LOCAL" flag
+ * isn't set, the destination channel/workqueue fields and the write-enable bit
+ * are left as-is.
+ * 4. if the driver overwrites context_a/b for demux, then if
+ * QM_INITFQ_WE_CONTEXTA is set, the driver will only overwrite
+ * context_a.address fields and will leave the stashing fields provided by the
+ * user alone, otherwise it will zero out the context_a.stashing fields.
+ */
+int qman_init_fq(struct qman_fq *fq, u32 flags, struct qm_mcc_initfq *opts);
+
+/**
+ * qman_schedule_fq - Schedules a FQ
+ * @fq: the frame queue object to schedule, must be 'parked'
+ *
+ * Schedules the frame queue, which must be Parked, which takes it to
+ * Tentatively-Scheduled or Truly-Scheduled depending on its fill-level.
+ */
+int qman_schedule_fq(struct qman_fq *fq);
+
+/**
+ * qman_retire_fq - Retires a FQ
+ * @fq: the frame queue object to retire
+ * @flags: FQ flags (QMAN_FQ_STATE*) if retirement completes immediately
+ *
+ * Retires the frame queue. This returns zero if it succeeds immediately, +1 if
+ * the retirement was started asynchronously, otherwise it returns negative for
+ * failure. When this function returns zero, @flags is set to indicate whether
+ * the retired FQ is empty and/or whether it has any ORL fragments (to show up
+ * as ERNs). Otherwise the corresponding flags will be known when a subsequent
+ * FQRN message shows up on the portal's message ring.
+ *
+ * NB, if the retirement is asynchronous (the FQ was in the Truly Scheduled or
+ * Active state), the completion will be via the message ring as a FQRN - but
+ * the corresponding callback may occur before this function returns!! Ie. the
+ * caller should be prepared to accept the callback as the function is called,
+ * not only once it has returned.
+ */
+int qman_retire_fq(struct qman_fq *fq, u32 *flags);
+
+/**
+ * qman_oos_fq - Puts a FQ "out of service"
+ * @fq: the frame queue object to be put out-of-service, must be 'retired'
+ *
+ * The frame queue must be retired and empty, and if any order restoration list
+ * was released as ERNs at the time of retirement, they must all be consumed.
+ */
+int qman_oos_fq(struct qman_fq *fq);
+
+/**
+ * qman_enqueue - Enqueue a frame to a frame queue
+ * @fq: the frame queue object to enqueue to
+ * @fd: a descriptor of the frame to be enqueued
+ *
+ * Fills an entry in the EQCR of portal @qm to enqueue the frame described by
+ * @fd. The descriptor details are copied from @fd to the EQCR entry, the 'pid'
+ * field is ignored. The return value is non-zero on error, such as ring full.
+ */
+int qman_enqueue(struct qman_fq *fq, const struct qm_fd *fd);
+
+/**
+ * qman_alloc_fqid_range - Allocate a contiguous range of FQIDs
+ * @result: is set by the API to the base FQID of the allocated range
+ * @count: the number of FQIDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_fqid_range(u32 *result, u32 count);
+#define qman_alloc_fqid(result) qman_alloc_fqid_range(result, 1)
+
+/**
+ * qman_release_fqid - Release the specified frame queue ID
+ * @fqid: the FQID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * FQID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_fqid(u32 fqid);
+
+       /* Pool-channel management */
+/**
+ * qman_alloc_pool_range - Allocate a contiguous range of pool-channel IDs
+ * @result: is set by the API to the base pool-channel ID of the allocated range
+ * @count: the number of pool-channel IDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_pool_range(u32 *result, u32 count);
+#define qman_alloc_pool(result) qman_alloc_pool_range(result, 1)
+
+/**
+ * qman_release_pool - Release the specified pool-channel ID
+ * @id: the pool-chan ID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * pool-channel ID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_pool(u32 id);
+
+       /* CGR management */
+/**
+ * qman_create_cgr - Register a congestion group object
+ * @cgr: the 'cgr' object, with fields filled in
+ * @flags: QMAN_CGR_FLAG_* values
+ * @opts: optional state of CGR settings
+ *
+ * Registers this object to receiving congestion entry/exit callbacks on the
+ * portal affine to the cpu portal on which this API is executed. If opts is
+ * NULL then only the callback (cgr->cb) function is registered. If @flags
+ * contains QMAN_CGR_FLAG_USE_INIT, then an init hw command (which will reset
+ * any unspecified parameters) will be used rather than a modify hw hardware
+ * (which only modifies the specified parameters).
+ */
+int qman_create_cgr(struct qman_cgr *cgr, u32 flags,
+                   struct qm_mcc_initcgr *opts);
+
+/**
+ * qman_delete_cgr - Deregisters a congestion group object
+ * @cgr: the 'cgr' object to deregister
+ *
+ * "Unplugs" this CGR object from the portal affine to the cpu on which this API
+ * is executed. This must be excuted on the same affine portal on which it was
+ * created.
+ */
+int qman_delete_cgr(struct qman_cgr *cgr);
+
+/**
+ * qman_delete_cgr_safe - Deregisters a congestion group object from any CPU
+ * @cgr: the 'cgr' object to deregister
+ *
+ * This will select the proper CPU and run there qman_delete_cgr().
+ */
+void qman_delete_cgr_safe(struct qman_cgr *cgr);
+
+/**
+ * qman_query_cgr_congested - Queries CGR's congestion status
+ * @cgr: the 'cgr' object to query
+ * @result: returns 'cgr's congestion status, 1 (true) if congested
+ */
+int qman_query_cgr_congested(struct qman_cgr *cgr, bool *result);
+
+/**
+ * qman_alloc_cgrid_range - Allocate a contiguous range of CGR IDs
+ * @result: is set by the API to the base CGR ID of the allocated range
+ * @count: the number of CGR IDs required
+ *
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_alloc_cgrid_range(u32 *result, u32 count);
+#define qman_alloc_cgrid(result) qman_alloc_cgrid_range(result, 1)
+
+/**
+ * qman_release_cgrid - Release the specified CGR ID
+ * @id: the CGR ID to be released back to the resource pool
+ *
+ * This function can also be used to seed the allocator with
+ * CGR ID ranges that it can subsequently allocate from.
+ * Returns 0 on success, or a negative error code.
+ */
+int qman_release_cgrid(u32 id);
+
+#endif /* __FSL_QMAN_H */
diff --git a/include/trace/events/cgroup.h b/include/trace/events/cgroup.h
new file mode 100644 (file)
index 0000000..ab68640
--- /dev/null
@@ -0,0 +1,163 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cgroup
+
+#if !defined(_TRACE_CGROUP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_CGROUP_H
+
+#include <linux/cgroup.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(cgroup_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root),
+
+       TP_STRUCT__entry(
+               __field(        int,            root                    )
+               __field(        u16,            ss_mask                 )
+               __string(       name,           root->name              )
+       ),
+
+       TP_fast_assign(
+               __entry->root = root->hierarchy_id;
+               __entry->ss_mask = root->subsys_mask;
+               __assign_str(name, root->name);
+       ),
+
+       TP_printk("root=%d ss_mask=%#x name=%s",
+                 __entry->root, __entry->ss_mask, __get_str(name))
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_setup_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_destroy_root,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DEFINE_EVENT(cgroup_root, cgroup_remount,
+
+       TP_PROTO(struct cgroup_root *root),
+
+       TP_ARGS(root)
+);
+
+DECLARE_EVENT_CLASS(cgroup,
+
+       TP_PROTO(struct cgroup *cgrp),
+
+       TP_ARGS(cgrp),
+
+       TP_STRUCT__entry(
+               __field(        int,            root                    )
+               __field(        int,            id                      )
+               __field(        int,            level                   )
+               __dynamic_array(char,           path,
+                               cgrp->kn ? cgroup_path(cgrp, NULL, 0) + 1
+                                        : strlen("(null)"))
+       ),
+
+       TP_fast_assign(
+               __entry->root = cgrp->root->hierarchy_id;
+               __entry->id = cgrp->id;
+               __entry->level = cgrp->level;
+               if (cgrp->kn)
+                       cgroup_path(cgrp, __get_dynamic_array(path),
+                                   __get_dynamic_array_len(path));
+               else
+                       __assign_str(path, "(null)");
+       ),
+
+       TP_printk("root=%d id=%d level=%d path=%s",
+                 __entry->root, __entry->id, __entry->level, __get_str(path))
+);
+
+DEFINE_EVENT(cgroup, cgroup_mkdir,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_rmdir,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_release,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DEFINE_EVENT(cgroup, cgroup_rename,
+
+       TP_PROTO(struct cgroup *cgroup),
+
+       TP_ARGS(cgroup)
+);
+
+DECLARE_EVENT_CLASS(cgroup_migrate,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup),
+
+       TP_STRUCT__entry(
+               __field(        int,            dst_root                )
+               __field(        int,            dst_id                  )
+               __field(        int,            dst_level               )
+               __dynamic_array(char,           dst_path,
+                               dst_cgrp->kn ? cgroup_path(dst_cgrp, NULL, 0) + 1
+                                            : strlen("(null)"))
+               __field(        int,            pid                     )
+               __string(       comm,           task->comm              )
+       ),
+
+       TP_fast_assign(
+               __entry->dst_root = dst_cgrp->root->hierarchy_id;
+               __entry->dst_id = dst_cgrp->id;
+               __entry->dst_level = dst_cgrp->level;
+               if (dst_cgrp->kn)
+                       cgroup_path(dst_cgrp, __get_dynamic_array(dst_path),
+                                   __get_dynamic_array_len(dst_path));
+               else
+                       __assign_str(dst_path, "(null)");
+               __entry->pid = task->pid;
+               __assign_str(comm, task->comm);
+       ),
+
+       TP_printk("dst_root=%d dst_id=%d dst_level=%d dst_path=%s pid=%d comm=%s",
+                 __entry->dst_root, __entry->dst_id, __entry->dst_level,
+                 __get_str(dst_path), __entry->pid, __get_str(comm))
+);
+
+DEFINE_EVENT(cgroup_migrate, cgroup_attach_task,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup)
+);
+
+DEFINE_EVENT(cgroup_migrate, cgroup_transfer_tasks,
+
+       TP_PROTO(struct cgroup *dst_cgrp, struct task_struct *task, bool threadgroup),
+
+       TP_ARGS(dst_cgrp, task, threadgroup)
+);
+
+#endif /* _TRACE_CGROUP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
index 462246a..d6b5a21 100644 (file)
@@ -77,6 +77,10 @@ extern "C" {
 #define AMDGPU_GEM_CREATE_NO_CPU_ACCESS                (1 << 1)
 /* Flag that USWC attributes should be used for GTT */
 #define AMDGPU_GEM_CREATE_CPU_GTT_USWC         (1 << 2)
+/* Flag that the memory should be in VRAM and cleared */
+#define AMDGPU_GEM_CREATE_VRAM_CLEARED         (1 << 3)
+/* Flag that create shadow bo(GTT) while allocating vram bo */
+#define AMDGPU_GEM_CREATE_SHADOW               (1 << 4)
 
 struct drm_amdgpu_gem_create_in  {
        /** the requested memory size */
@@ -481,6 +485,8 @@ struct drm_amdgpu_cs_chunk_data {
 #define AMDGPU_INFO_DEV_INFO                   0x16
 /* visible vram usage */
 #define AMDGPU_INFO_VIS_VRAM_USAGE             0x17
+/* number of TTM buffer evictions */
+#define AMDGPU_INFO_NUM_EVICTIONS              0x18
 
 #define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0
 #define AMDGPU_INFO_MMR_SE_INDEX_MASK  0xff
@@ -643,6 +649,7 @@ struct drm_amdgpu_info_hw_ip {
  * Supported GPU families
  */
 #define AMDGPU_FAMILY_UNKNOWN                  0
+#define AMDGPU_FAMILY_SI                       110 /* Hainan, Oland, Verde, Pitcairn, Tahiti */
 #define AMDGPU_FAMILY_CI                       120 /* Bonaire, Hawaii */
 #define AMDGPU_FAMILY_KV                       125 /* Kaveri, Kabini, Mullins */
 #define AMDGPU_FAMILY_VI                       130 /* Iceland, Tonga */
index 452675f..b2c5284 100644 (file)
@@ -646,6 +646,7 @@ struct drm_gem_open {
 #define DRM_CAP_CURSOR_WIDTH           0x8
 #define DRM_CAP_CURSOR_HEIGHT          0x9
 #define DRM_CAP_ADDFB2_MODIFIERS       0x10
+#define DRM_CAP_PAGE_FLIP_TARGET       0x11
 
 /** DRM_IOCTL_GET_CAP ioctl argument type */
 struct drm_get_cap {
index 49a7265..df0e350 100644 (file)
@@ -520,7 +520,13 @@ struct drm_color_lut {
 
 #define DRM_MODE_PAGE_FLIP_EVENT 0x01
 #define DRM_MODE_PAGE_FLIP_ASYNC 0x02
-#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT|DRM_MODE_PAGE_FLIP_ASYNC)
+#define DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE 0x4
+#define DRM_MODE_PAGE_FLIP_TARGET_RELATIVE 0x8
+#define DRM_MODE_PAGE_FLIP_TARGET (DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE | \
+                                  DRM_MODE_PAGE_FLIP_TARGET_RELATIVE)
+#define DRM_MODE_PAGE_FLIP_FLAGS (DRM_MODE_PAGE_FLIP_EVENT | \
+                                 DRM_MODE_PAGE_FLIP_ASYNC | \
+                                 DRM_MODE_PAGE_FLIP_TARGET)
 
 /*
  * Request a page flip on the specified crtc.
@@ -543,8 +549,7 @@ struct drm_color_lut {
  * 'as soon as possible', meaning that it not delay waiting for vblank.
  * This may cause tearing on the screen.
  *
- * The reserved field must be zero until we figure out something
- * clever to use it for.
+ * The reserved field must be zero.
  */
 
 struct drm_mode_crtc_page_flip {
@@ -555,6 +560,34 @@ struct drm_mode_crtc_page_flip {
        __u64 user_data;
 };
 
+/*
+ * Request a page flip on the specified crtc.
+ *
+ * Same as struct drm_mode_crtc_page_flip, but supports new flags and
+ * re-purposes the reserved field:
+ *
+ * The sequence field must be zero unless either of the
+ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is specified. When
+ * the ABSOLUTE flag is specified, the sequence field denotes the absolute
+ * vblank sequence when the flip should take effect. When the RELATIVE
+ * flag is specified, the sequence field denotes the relative (to the
+ * current one when the ioctl is called) vblank sequence when the flip
+ * should take effect. NOTE: DRM_IOCTL_WAIT_VBLANK must still be used to
+ * make sure the vblank sequence before the target one has passed before
+ * calling this ioctl. The purpose of the
+ * DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags is merely to clarify
+ * the target for when code dealing with a page flip runs during a
+ * vertical blank period.
+ */
+
+struct drm_mode_crtc_page_flip_target {
+       __u32 crtc_id;
+       __u32 fb_id;
+       __u32 flags;
+       __u32 sequence;
+       __u64 user_data;
+};
+
 /* create a dumb scanout buffer */
 struct drm_mode_create_dumb {
        __u32 height;
index d7e81a3..03725fe 100644 (file)
@@ -62,6 +62,30 @@ extern "C" {
 #define I915_ERROR_UEVENT              "ERROR"
 #define I915_RESET_UEVENT              "RESET"
 
+/*
+ * MOCS indexes used for GPU surfaces, defining the cacheability of the
+ * surface data and the coherency for this data wrt. CPU vs. GPU accesses.
+ */
+enum i915_mocs_table_index {
+       /*
+        * Not cached anywhere, coherency between CPU and GPU accesses is
+        * guaranteed.
+        */
+       I915_MOCS_UNCACHED,
+       /*
+        * Cacheability and coherency controlled by the kernel automatically
+        * based on the DRM_I915_GEM_SET_CACHING IOCTL setting and the current
+        * usage of the surface (used for display scanout or not).
+        */
+       I915_MOCS_PTE,
+       /*
+        * Cached in all GPU caches available on the platform.
+        * Coherency between CPU and GPU accesses to the surface is not
+        * guaranteed without extra synchronization.
+        */
+       I915_MOCS_CACHED,
+};
+
 /* Each region is a minimum of 16k, and there are at most 255 of them.
  */
 #define I915_NR_TEX_REGIONS 255        /* table size 2k - maximum due to use
@@ -363,6 +387,7 @@ typedef struct drm_i915_irq_wait {
 #define I915_PARAM_HAS_EXEC_SOFTPIN     37
 #define I915_PARAM_HAS_POOLED_EU        38
 #define I915_PARAM_MIN_EU_IN_POOL       39
+#define I915_PARAM_MMAP_GTT_VERSION     40
 
 typedef struct drm_i915_getparam {
        __s32 param;
@@ -698,15 +723,20 @@ struct drm_i915_gem_exec_object2 {
         */
        __u64 offset;
 
-#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
-#define EXEC_OBJECT_NEEDS_GTT  (1<<1)
-#define EXEC_OBJECT_WRITE      (1<<2)
+#define EXEC_OBJECT_NEEDS_FENCE                 (1<<0)
+#define EXEC_OBJECT_NEEDS_GTT           (1<<1)
+#define EXEC_OBJECT_WRITE               (1<<2)
 #define EXEC_OBJECT_SUPPORTS_48B_ADDRESS (1<<3)
-#define EXEC_OBJECT_PINNED     (1<<4)
-#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_PINNED<<1)
+#define EXEC_OBJECT_PINNED              (1<<4)
+#define EXEC_OBJECT_PAD_TO_SIZE                 (1<<5)
+/* All remaining bits are MBZ and RESERVED FOR FUTURE USE */
+#define __EXEC_OBJECT_UNKNOWN_FLAGS -(EXEC_OBJECT_PAD_TO_SIZE<<1)
        __u64 flags;
 
-       __u64 rsvd1;
+       union {
+               __u64 rsvd1;
+               __u64 pad_to_size;
+       };
        __u64 rsvd2;
 };
 
@@ -826,7 +856,16 @@ struct drm_i915_gem_busy {
         * having flushed any pending activity), and a non-zero return that
         * the object is still in-flight on the GPU. (The GPU has not yet
         * signaled completion for all pending requests that reference the
-        * object.)
+        * object.) An object is guaranteed to become idle eventually (so
+        * long as no new GPU commands are executed upon it). Due to the
+        * asynchronous nature of the hardware, an object reported
+        * as busy may become idle before the ioctl is completed.
+        *
+        * Furthermore, if the object is busy, which engine is busy is only
+        * provided as a guide. There are race conditions which prevent the
+        * report of which engines are busy from being always accurate.
+        * However, the converse is not true. If the object is idle, the
+        * result of the ioctl, that all engines are idle, is accurate.
         *
         * The returned dword is split into two fields to indicate both
         * the engines on which the object is being read, and the
@@ -849,6 +888,11 @@ struct drm_i915_gem_busy {
         * execution engines, e.g. multiple media engines, which are
         * mapped to the same identifier in the EXECBUFFER2 ioctl and
         * so are not separately reported for busyness.
+        *
+        * Caveat emptor:
+        * Only the boolean result of this query is reliable; that is whether
+        * the object is idle or busy. The report of which engines are busy
+        * should be only used as a heuristic.
         */
        __u32 busy;
 };
@@ -897,6 +941,7 @@ struct drm_i915_gem_caching {
 #define I915_TILING_NONE       0
 #define I915_TILING_X          1
 #define I915_TILING_Y          2
+#define I915_TILING_LAST       I915_TILING_Y
 
 #define I915_BIT_6_SWIZZLE_NONE                0
 #define I915_BIT_6_SWIZZLE_9           1
index 49f778d..8c51e8a 100644 (file)
@@ -42,6 +42,15 @@ extern "C" {
 #define MSM_PIPE_2D1         0x02
 #define MSM_PIPE_3D0         0x10
 
+/* The pipe-id just uses the lower bits, so can be OR'd with flags in
+ * the upper 16 bits (which could be extended further, if needed, maybe
+ * we extend/overload the pipe-id some day to deal with multiple rings,
+ * but even then I don't think we need the full lower 16 bits).
+ */
+#define MSM_PIPE_ID_MASK     0xffff
+#define MSM_PIPE_ID(x)       ((x) & MSM_PIPE_ID_MASK)
+#define MSM_PIPE_FLAGS(x)    ((x) & ~MSM_PIPE_ID_MASK)
+
 /* timeouts are specified in clock-monotonic absolute times (to simplify
  * restarting interrupted ioctls).  The following struct is logically the
  * same as 'struct timespec' but 32/64b ABI safe.
@@ -175,17 +184,28 @@ struct drm_msm_gem_submit_bo {
        __u64 presumed;       /* in/out, presumed buffer address */
 };
 
+/* Valid submit ioctl flags: */
+#define MSM_SUBMIT_NO_IMPLICIT   0x80000000 /* disable implicit sync */
+#define MSM_SUBMIT_FENCE_FD_IN   0x40000000 /* enable input fence_fd */
+#define MSM_SUBMIT_FENCE_FD_OUT  0x20000000 /* enable output fence_fd */
+#define MSM_SUBMIT_FLAGS                ( \
+               MSM_SUBMIT_NO_IMPLICIT   | \
+               MSM_SUBMIT_FENCE_FD_IN   | \
+               MSM_SUBMIT_FENCE_FD_OUT  | \
+               0)
+
 /* Each cmdstream submit consists of a table of buffers involved, and
  * one or more cmdstream buffers.  This allows for conditional execution
  * (context-restore), and IB buffers needed for per tile/bin draw cmds.
  */
 struct drm_msm_gem_submit {
-       __u32 pipe;           /* in, MSM_PIPE_x */
+       __u32 flags;          /* MSM_PIPE_x | MSM_SUBMIT_x */
        __u32 fence;          /* out */
        __u32 nr_bos;         /* in, number of submit_bo's */
        __u32 nr_cmds;        /* in, number of submit_cmd's */
        __u64 __user bos;     /* in, ptr to array of submit_bo's */
        __u64 __user cmds;    /* in, ptr to array of submit_cmd's */
+       __s32 fence_fd;       /* in/out fence fd (see MSM_SUBMIT_FENCE_FD_IN/OUT) */
 };
 
 /* The normal way to synchronize with the GPU is just to CPU_PREP on
index 929aba2..6965d09 100644 (file)
@@ -399,6 +399,7 @@ header-y += string.h
 header-y += suspend_ioctls.h
 header-y += swab.h
 header-y += synclink.h
+header-y += sync_file.h
 header-y += sysctl.h
 header-y += sysinfo.h
 header-y += target_core_user.h
diff --git a/include/uapi/linux/auto_dev-ioctl.h b/include/uapi/linux/auto_dev-ioctl.h
new file mode 100644 (file)
index 0000000..021ed33
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright 2008 Red Hat, Inc. All rights reserved.
+ * Copyright 2008 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#ifndef _UAPI_LINUX_AUTO_DEV_IOCTL_H
+#define _UAPI_LINUX_AUTO_DEV_IOCTL_H
+
+#include <linux/auto_fs.h>
+#include <linux/string.h>
+
+#define AUTOFS_DEVICE_NAME             "autofs"
+
+#define AUTOFS_DEV_IOCTL_VERSION_MAJOR 1
+#define AUTOFS_DEV_IOCTL_VERSION_MINOR 0
+
+#define AUTOFS_DEV_IOCTL_SIZE          sizeof(struct autofs_dev_ioctl)
+
+/*
+ * An ioctl interface for autofs mount point control.
+ */
+
+struct args_protover {
+       __u32   version;
+};
+
+struct args_protosubver {
+       __u32   sub_version;
+};
+
+struct args_openmount {
+       __u32   devid;
+};
+
+struct args_ready {
+       __u32   token;
+};
+
+struct args_fail {
+       __u32   token;
+       __s32   status;
+};
+
+struct args_setpipefd {
+       __s32   pipefd;
+};
+
+struct args_timeout {
+       __u64   timeout;
+};
+
+struct args_requester {
+       __u32   uid;
+       __u32   gid;
+};
+
+struct args_expire {
+       __u32   how;
+};
+
+struct args_askumount {
+       __u32   may_umount;
+};
+
+struct args_ismountpoint {
+       union {
+               struct args_in {
+                       __u32   type;
+               } in;
+               struct args_out {
+                       __u32   devid;
+                       __u32   magic;
+               } out;
+       };
+};
+
+/*
+ * All the ioctls use this structure.
+ * When sending a path size must account for the total length
+ * of the chunk of memory otherwise is is the size of the
+ * structure.
+ */
+
+struct autofs_dev_ioctl {
+       __u32 ver_major;
+       __u32 ver_minor;
+       __u32 size;             /* total size of data passed in
+                                * including this struct */
+       __s32 ioctlfd;          /* automount command fd */
+
+       /* Command parameters */
+
+       union {
+               struct args_protover            protover;
+               struct args_protosubver         protosubver;
+               struct args_openmount           openmount;
+               struct args_ready               ready;
+               struct args_fail                fail;
+               struct args_setpipefd           setpipefd;
+               struct args_timeout             timeout;
+               struct args_requester           requester;
+               struct args_expire              expire;
+               struct args_askumount           askumount;
+               struct args_ismountpoint        ismountpoint;
+       };
+
+       char path[0];
+};
+
+static inline void init_autofs_dev_ioctl(struct autofs_dev_ioctl *in)
+{
+       memset(in, 0, sizeof(struct autofs_dev_ioctl));
+       in->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
+       in->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
+       in->size = sizeof(struct autofs_dev_ioctl);
+       in->ioctlfd = -1;
+}
+
+/*
+ * If you change this make sure you make the corresponding change
+ * to autofs-dev-ioctl.c:lookup_ioctl()
+ */
+enum {
+       /* Get various version info */
+       AUTOFS_DEV_IOCTL_VERSION_CMD = 0x71,
+       AUTOFS_DEV_IOCTL_PROTOVER_CMD,
+       AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD,
+
+       /* Open mount ioctl fd */
+       AUTOFS_DEV_IOCTL_OPENMOUNT_CMD,
+
+       /* Close mount ioctl fd */
+       AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD,
+
+       /* Mount/expire status returns */
+       AUTOFS_DEV_IOCTL_READY_CMD,
+       AUTOFS_DEV_IOCTL_FAIL_CMD,
+
+       /* Activate/deactivate autofs mount */
+       AUTOFS_DEV_IOCTL_SETPIPEFD_CMD,
+       AUTOFS_DEV_IOCTL_CATATONIC_CMD,
+
+       /* Expiry timeout */
+       AUTOFS_DEV_IOCTL_TIMEOUT_CMD,
+
+       /* Get mount last requesting uid and gid */
+       AUTOFS_DEV_IOCTL_REQUESTER_CMD,
+
+       /* Check for eligible expire candidates */
+       AUTOFS_DEV_IOCTL_EXPIRE_CMD,
+
+       /* Request busy status */
+       AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD,
+
+       /* Check if path is a mountpoint */
+       AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD,
+};
+
+#define AUTOFS_IOCTL 0x93
+
+#define AUTOFS_DEV_IOCTL_VERSION \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_VERSION_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_PROTOVER \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_PROTOVER_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_PROTOSUBVER \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_OPENMOUNT \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_OPENMOUNT_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_CLOSEMOUNT \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_READY \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_READY_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_FAIL \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_FAIL_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_SETPIPEFD \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_SETPIPEFD_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_CATATONIC \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_CATATONIC_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_TIMEOUT \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_TIMEOUT_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_REQUESTER \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_REQUESTER_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_EXPIRE \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_EXPIRE_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_ASKUMOUNT \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD, struct autofs_dev_ioctl)
+
+#define AUTOFS_DEV_IOCTL_ISMOUNTPOINT \
+       _IOWR(AUTOFS_IOCTL, \
+             AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD, struct autofs_dev_ioctl)
+
+#endif /* _UAPI_LINUX_AUTO_DEV_IOCTL_H */
index 9175a1b..1bfc3ed 100644 (file)
@@ -12,6 +12,7 @@
 #define _UAPI_LINUX_AUTO_FS_H
 
 #include <linux/types.h>
+#include <linux/limits.h>
 #ifndef __KERNEL__
 #include <sys/ioctl.h>
 #endif /* __KERNEL__ */
index ac5eacd..db4c253 100644 (file)
@@ -239,7 +239,17 @@ struct btrfs_ioctl_fs_info_args {
  * Used by:
  * struct btrfs_ioctl_feature_flags
  */
-#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE        (1ULL << 0)
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE                (1ULL << 0)
+/*
+ * Older kernels (< 4.9) on big-endian systems produced broken free space tree
+ * bitmaps, and btrfs-progs also used to corrupt the free space tree (versions
+ * < 4.7.3).  If this bit is clear, then the free space tree cannot be trusted.
+ * btrfs-progs can also intentionally clear this bit to ask the kernel to
+ * rebuild the free space tree, however this might not work on older kernels
+ * that do not know about this bit. If not sure, clear the cache manually on
+ * first mount when booting older kernel versions.
+ */
+#define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID  (1ULL << 1)
 
 #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF   (1ULL << 0)
 #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL  (1ULL << 1)
index 3e445a7..b075f60 100644 (file)
  */
 #define FALLOC_FL_INSERT_RANGE         0x20
 
+/*
+ * FALLOC_FL_UNSHARE_RANGE is used to unshare shared blocks within the
+ * file size without overwriting any existing data. The purpose of this
+ * call is to preemptively reallocate any blocks that are subject to
+ * copy-on-write.
+ *
+ * Different filesystems may implement different limitations on the
+ * granularity of the operation. Most will limit operations to filesystem
+ * block size boundaries, but this boundary may be larger or smaller
+ * depending on the filesystem and/or the configuration of the filesystem
+ * or file.
+ *
+ * This flag can only be used with allocate-mode fallocate, which is
+ * to say that it cannot be used with the punch, zero, collapse, or
+ * insert range modes.
+ */
+#define FALLOC_FL_UNSHARE_RANGE                0x40
+
 #endif /* _UAPI_FALLOC_H_ */
index 2473272..acb2b61 100644 (file)
@@ -158,7 +158,8 @@ struct fsxattr {
        __u32           fsx_extsize;    /* extsize field value (get/set)*/
        __u32           fsx_nextents;   /* nextents field value (get)   */
        __u32           fsx_projid;     /* project identifier (get/set) */
-       unsigned char   fsx_pad[12];
+       __u32           fsx_cowextsize; /* CoW extsize field value (get/set)*/
+       unsigned char   fsx_pad[8];
 };
 
 /*
@@ -179,6 +180,7 @@ struct fsxattr {
 #define FS_XFLAG_NODEFRAG      0x00002000      /* do not defragment */
 #define FS_XFLAG_FILESTREAM    0x00004000      /* use filestream allocator */
 #define FS_XFLAG_DAX           0x00008000      /* use DAX for IO */
+#define FS_XFLAG_COWEXTSIZE    0x00010000      /* CoW extent size allocator hint */
 #define FS_XFLAG_HASATTR       0x80000000      /* no DIFLAG for this   */
 
 /* the read-only stuff doesn't really belong here, but any other place is
index 2b871e0..4ae6279 100644 (file)
@@ -39,8 +39,9 @@
 #define NFS4_FH_VOL_MIGRATION          0x0004
 #define NFS4_FH_VOL_RENAME             0x0008
 
-#define NFS4_OPEN_RESULT_CONFIRM 0x0002
-#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX 0x0004
+#define NFS4_OPEN_RESULT_CONFIRM               0x0002
+#define NFS4_OPEN_RESULT_LOCKTYPE_POSIX                0x0004
+#define NFS4_OPEN_RESULT_MAY_NOTIFY_LOCK       0x0020
 
 #define NFS4_SHARE_ACCESS_MASK 0x000F
 #define NFS4_SHARE_ACCESS_READ 0x0001
index d812172..e5a2e68 100644 (file)
  */
 #define PCI_EXP_DEVCAP2                36      /* Device Capabilities 2 */
 #define  PCI_EXP_DEVCAP2_ARI           0x00000020 /* Alternative Routing-ID */
+#define  PCI_EXP_DEVCAP2_ATOMIC_ROUTE  0x00000040 /* Atomic Op routing */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP64  0x00000100 /* Atomic 64-bit compare */
 #define  PCI_EXP_DEVCAP2_LTR           0x00000800 /* Latency tolerance reporting */
 #define  PCI_EXP_DEVCAP2_OBFF_MASK     0x000c0000 /* OBFF support mechanism */
 #define  PCI_EXP_DEVCAP2_OBFF_MSG      0x00040000 /* New message signaling */
 #define PCI_EXP_DEVCTL2                40      /* Device Control 2 */
 #define  PCI_EXP_DEVCTL2_COMP_TIMEOUT  0x000f  /* Completion Timeout Value */
 #define  PCI_EXP_DEVCTL2_ARI           0x0020  /* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2_ATOMIC_REQ     0x0040  /* Set Atomic requests */
 #define  PCI_EXP_DEVCTL2_IDO_REQ_EN    0x0100  /* Allow IDO for requests */
 #define  PCI_EXP_DEVCTL2_IDO_CMP_EN    0x0200  /* Allow IDO for completions */
 #define  PCI_EXP_DEVCTL2_LTR_EN                0x0400  /* Enable LTR mechanism */
index 413303d..5b287d6 100644 (file)
@@ -85,15 +85,12 @@ struct sync_file_info {
 #define SYNC_IOC_MERGE         _IOWR(SYNC_IOC_MAGIC, 3, struct sync_merge_data)
 
 /**
- * DOC: SYNC_IOC_FENCE_INFO - get detailed information on a fence
+ * DOC: SYNC_IOC_FILE_INFO - get detailed information on a sync_file
  *
- * Takes a struct sync_file_info_data with extra space allocated for pt_info.
- * Caller should write the size of the buffer into len.  On return, len is
- * updated to reflect the total size of the sync_file_info_data including
- * pt_info.
- *
- * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.
- * To iterate over the sync_pt_infos, use the sync_pt_info.len field.
+ * Takes a struct sync_file_info. If num_fences is 0, the field is updated
+ * with the actual number of fences. If num_fences is > 0, the system will
+ * use the pointer provided on sync_fence_info to return up to num_fences of
+ * struct sync_fence_info, with detailed fence information.
  */
 #define SYNC_IOC_FILE_INFO     _IOWR(SYNC_IOC_MAGIC, 4, struct sync_file_info)
 
diff --git a/include/uapi/rdma/qedr-abi.h b/include/uapi/rdma/qedr-abi.h
new file mode 100644 (file)
index 0000000..75c270d
--- /dev/null
@@ -0,0 +1,106 @@
+/* QLogic qedr NIC Driver
+ * Copyright (c) 2015-2016  QLogic Corporation
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and /or other materials
+ *        provided with the distribution.
+ *
+ * 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 AUTHORS OR COPYRIGHT HOLDERS
+ * 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 __QEDR_USER_H__
+#define __QEDR_USER_H__
+
+#include <linux/types.h>
+
+#define QEDR_ABI_VERSION               (8)
+
+/* user kernel communication data structures. */
+
+struct qedr_alloc_ucontext_resp {
+       __u64 db_pa;
+       __u32 db_size;
+
+       __u32 max_send_wr;
+       __u32 max_recv_wr;
+       __u32 max_srq_wr;
+       __u32 sges_per_send_wr;
+       __u32 sges_per_recv_wr;
+       __u32 sges_per_srq_wr;
+       __u32 max_cqes;
+};
+
+struct qedr_alloc_pd_ureq {
+       __u64 rsvd1;
+};
+
+struct qedr_alloc_pd_uresp {
+       __u32 pd_id;
+};
+
+struct qedr_create_cq_ureq {
+       __u64 addr;
+       __u64 len;
+};
+
+struct qedr_create_cq_uresp {
+       __u32 db_offset;
+       __u16 icid;
+};
+
+struct qedr_create_qp_ureq {
+       __u32 qp_handle_hi;
+       __u32 qp_handle_lo;
+
+       /* SQ */
+       /* user space virtual address of SQ buffer */
+       __u64 sq_addr;
+
+       /* length of SQ buffer */
+       __u64 sq_len;
+
+       /* RQ */
+       /* user space virtual address of RQ buffer */
+       __u64 rq_addr;
+
+       /* length of RQ buffer */
+       __u64 rq_len;
+};
+
+struct qedr_create_qp_uresp {
+       __u32 qp_id;
+       __u32 atomic_supported;
+
+       /* SQ */
+       __u32 sq_db_offset;
+       __u16 sq_icid;
+
+       /* RQ */
+       __u32 rq_db_offset;
+       __u16 rq_icid;
+
+       __u32 rq_db2_offset;
+};
+
+#endif /* __QEDR_USER_H__ */
diff --git a/include/video/exynos_mipi_dsim.h b/include/video/exynos_mipi_dsim.h
deleted file mode 100644 (file)
index 6a578f8..0000000
+++ /dev/null
@@ -1,358 +0,0 @@
-/* include/video/exynos_mipi_dsim.h
- *
- * Platform data header for Samsung SoC MIPI-DSIM.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd
- *
- * InKi Dae <inki.dae@samsung.com>
- * Donghwa Lee <dh09.lee@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#ifndef _EXYNOS_MIPI_DSIM_H
-#define _EXYNOS_MIPI_DSIM_H
-
-#include <linux/device.h>
-#include <linux/fb.h>
-
-#define PANEL_NAME_SIZE                (32)
-
-/*
- * Enumerate display interface type.
- *
- * DSIM_COMMAND means cpu interface and rgb interface for DSIM_VIDEO.
- *
- * P.S. MIPI DSI Master has two display controller intefaces, RGB Interface
- *     for main display and CPU Interface(same as I80 Interface) for main
- *     and sub display.
- */
-enum mipi_dsim_interface_type {
-       DSIM_COMMAND,
-       DSIM_VIDEO
-};
-
-enum mipi_dsim_virtual_ch_no {
-       DSIM_VIRTUAL_CH_0,
-       DSIM_VIRTUAL_CH_1,
-       DSIM_VIRTUAL_CH_2,
-       DSIM_VIRTUAL_CH_3
-};
-
-enum mipi_dsim_burst_mode_type {
-       DSIM_NON_BURST_SYNC_EVENT,
-       DSIM_BURST_SYNC_EVENT,
-       DSIM_NON_BURST_SYNC_PULSE,
-       DSIM_BURST,
-       DSIM_NON_VIDEO_MODE
-};
-
-enum mipi_dsim_no_of_data_lane {
-       DSIM_DATA_LANE_1,
-       DSIM_DATA_LANE_2,
-       DSIM_DATA_LANE_3,
-       DSIM_DATA_LANE_4
-};
-
-enum mipi_dsim_byte_clk_src {
-       DSIM_PLL_OUT_DIV8,
-       DSIM_EXT_CLK_DIV8,
-       DSIM_EXT_CLK_BYPASS
-};
-
-enum mipi_dsim_pixel_format {
-       DSIM_CMD_3BPP,
-       DSIM_CMD_8BPP,
-       DSIM_CMD_12BPP,
-       DSIM_CMD_16BPP,
-       DSIM_VID_16BPP_565,
-       DSIM_VID_18BPP_666PACKED,
-       DSIM_18BPP_666LOOSELYPACKED,
-       DSIM_24BPP_888
-};
-
-/*
- * struct mipi_dsim_config - interface for configuring mipi-dsi controller.
- *
- * @auto_flush: enable or disable Auto flush of MD FIFO using VSYNC pulse.
- * @eot_disable: enable or disable EoT packet in HS mode.
- * @auto_vertical_cnt: specifies auto vertical count mode.
- *     in Video mode, the vertical line transition uses line counter
- *     configured by VSA, VBP, and Vertical resolution.
- *     If this bit is set to '1', the line counter does not use VSA and VBP
- *     registers.(in command mode, this variable is ignored)
- * @hse: set horizontal sync event mode.
- *     In VSYNC pulse and Vporch area, MIPI DSI master transfers only HSYNC
- *     start packet to MIPI DSI slave at MIPI DSI spec1.1r02.
- *     this bit transfers HSYNC end packet in VSYNC pulse and Vporch area
- *     (in mommand mode, this variable is ignored)
- * @hfp: specifies HFP disable mode.
- *     if this variable is set, DSI master ignores HFP area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @hbp: specifies HBP disable mode.
- *     if this variable is set, DSI master ignores HBP area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @hsa: specifies HSA disable mode.
- *     if this variable is set, DSI master ignores HSA area in VIDEO mode.
- *     (in command mode, this variable is ignored)
- * @cma_allow: specifies the number of horizontal lines, where command packet
- *     transmission is allowed after Stable VFP period.
- * @e_interface: specifies interface to be used.(CPU or RGB interface)
- * @e_virtual_ch: specifies virtual channel number that main or
- *     sub diaplsy uses.
- * @e_pixel_format: specifies pixel stream format for main or sub display.
- * @e_burst_mode: selects Burst mode in Video mode.
- *     in Non-burst mode, RGB data area is filled with RGB data and NULL
- *     packets, according to input bandwidth of RGB interface.
- *     In Burst mode, RGB data area is filled with RGB data only.
- * @e_no_data_lane: specifies data lane count to be used by Master.
- * @e_byte_clk: select byte clock source. (it must be DSIM_PLL_OUT_DIV8)
- *     DSIM_EXT_CLK_DIV8 and DSIM_EXT_CLK_BYPASSS are not supported.
- * @pll_stable_time: specifies the PLL Timer for stability of the ganerated
- *     clock(System clock cycle base)
- *     if the timer value goes to 0x00000000, the clock stable bit of status
- *     and interrupt register is set.
- * @esc_clk: specifies escape clock frequency for getting the escape clock
- *     prescaler value.
- * @stop_holding_cnt: specifies the interval value between transmitting
- *     read packet(or write "set_tear_on" command) and BTA request.
- *     after transmitting read packet or write "set_tear_on" command,
- *     BTA requests to D-PHY automatically. this counter value specifies
- *     the interval between them.
- * @bta_timeout: specifies the timer for BTA.
- *     this register specifies time out from BTA request to change
- *     the direction with respect to Tx escape clock.
- * @rx_timeout: specifies the timer for LP Rx mode timeout.
- *     this register specifies time out on how long RxValid deasserts,
- *     after RxLpdt asserts with respect to Tx escape clock.
- *     - RxValid specifies Rx data valid indicator.
- *     - RxLpdt specifies an indicator that D-PHY is under RxLpdt mode.
- *     - RxValid and RxLpdt specifies signal from D-PHY.
- */
-struct mipi_dsim_config {
-       unsigned char                   auto_flush;
-       unsigned char                   eot_disable;
-
-       unsigned char                   auto_vertical_cnt;
-       unsigned char                   hse;
-       unsigned char                   hfp;
-       unsigned char                   hbp;
-       unsigned char                   hsa;
-       unsigned char                   cmd_allow;
-
-       enum mipi_dsim_interface_type   e_interface;
-       enum mipi_dsim_virtual_ch_no    e_virtual_ch;
-       enum mipi_dsim_pixel_format     e_pixel_format;
-       enum mipi_dsim_burst_mode_type  e_burst_mode;
-       enum mipi_dsim_no_of_data_lane  e_no_data_lane;
-       enum mipi_dsim_byte_clk_src     e_byte_clk;
-
-       /*
-        * ===========================================
-        * |    P    |    M    |    S    |    MHz    |
-        * -------------------------------------------
-        * |    3    |   100   |    3    |    100    |
-        * |    3    |   100   |    2    |    200    |
-        * |    3    |    63   |    1    |    252    |
-        * |    4    |   100   |    1    |    300    |
-        * |    4    |   110   |    1    |    330    |
-        * |   12    |   350   |    1    |    350    |
-        * |    3    |   100   |    1    |    400    |
-        * |    4    |   150   |    1    |    450    |
-        * |    6    |   118   |    1    |    472    |
-        * |    3    |   120   |    1    |    480    |
-        * |   12    |   250   |    0    |    500    |
-        * |    4    |   100   |    0    |    600    |
-        * |    3    |    81   |    0    |    648    |
-        * |    3    |    88   |    0    |    704    |
-        * |    3    |    90   |    0    |    720    |
-        * |    3    |   100   |    0    |    800    |
-        * |   12    |   425   |    0    |    850    |
-        * |    4    |   150   |    0    |    900    |
-        * |   12    |   475   |    0    |    950    |
-        * |    6    |   250   |    0    |   1000    |
-        * -------------------------------------------
-        */
-
-       /*
-        * pms could be calculated as the following.
-        * M * 24 / P * 2 ^ S = MHz
-        */
-       unsigned char                   p;
-       unsigned short                  m;
-       unsigned char                   s;
-
-       unsigned int                    pll_stable_time;
-       unsigned long                   esc_clk;
-
-       unsigned short                  stop_holding_cnt;
-       unsigned char                   bta_timeout;
-       unsigned short                  rx_timeout;
-};
-
-/*
- * struct mipi_dsim_device - global interface for mipi-dsi driver.
- *
- * @dev: driver model representation of the device.
- * @id: unique device id.
- * @clock: pointer to MIPI-DSI clock of clock framework.
- * @irq: interrupt number to MIPI-DSI controller.
- * @reg_base: base address to memory mapped SRF of MIPI-DSI controller.
- *     (virtual address)
- * @lock: the mutex protecting this data structure.
- * @dsim_info: infomation for configuring mipi-dsi controller.
- * @master_ops: callbacks to mipi-dsi operations.
- * @dsim_lcd_dev: pointer to activated ddi device.
- *     (it would be registered by mipi-dsi driver.)
- * @dsim_lcd_drv: pointer to activated_ddi driver.
- *     (it would be registered by mipi-dsi driver.)
- * @lcd_info: pointer to mipi_lcd_info structure.
- * @state: specifies status of MIPI-DSI controller.
- *     the status could be RESET, INIT, STOP, HSCLKEN and ULPS.
- * @data_lane: specifiec enabled data lane number.
- *     this variable would be set by driver according to e_no_data_lane
- *     automatically.
- * @e_clk_src: select byte clock source.
- * @pd: pointer to MIPI-DSI driver platform data.
- * @phy: pointer to the MIPI-DSI PHY
- */
-struct mipi_dsim_device {
-       struct device                   *dev;
-       int                             id;
-       struct clk                      *clock;
-       unsigned int                    irq;
-       void __iomem                    *reg_base;
-       struct mutex                    lock;
-
-       struct mipi_dsim_config         *dsim_config;
-       struct mipi_dsim_master_ops     *master_ops;
-       struct mipi_dsim_lcd_device     *dsim_lcd_dev;
-       struct mipi_dsim_lcd_driver     *dsim_lcd_drv;
-
-       unsigned int                    state;
-       unsigned int                    data_lane;
-       unsigned int                    e_clk_src;
-       bool                            suspended;
-
-       struct mipi_dsim_platform_data  *pd;
-       struct phy                      *phy;
-};
-
-/*
- * struct mipi_dsim_platform_data - interface to platform data
- *     for mipi-dsi driver.
- *
- * @lcd_panel_name: specifies lcd panel name registered to mipi-dsi driver.
- *     lcd panel driver searched would be actived.
- * @dsim_config: pointer of structure for configuring mipi-dsi controller.
- * @enabled: indicate whether mipi controller got enabled or not.
- * @lcd_panel_info: pointer for lcd panel specific structure.
- *     this structure specifies width, height, timing and polarity and so on.
- */
-struct mipi_dsim_platform_data {
-       char                            lcd_panel_name[PANEL_NAME_SIZE];
-
-       struct mipi_dsim_config         *dsim_config;
-       unsigned int                    enabled;
-       void                            *lcd_panel_info;
-};
-
-/*
- * struct mipi_dsim_master_ops - callbacks to mipi-dsi operations.
- *
- * @cmd_write: transfer command to lcd panel at LP mode.
- * @cmd_read: read command from rx register.
- * @get_dsim_frame_done: get the status that all screen data have been
- *     transferred to mipi-dsi.
- * @clear_dsim_frame_done: clear frame done status.
- * @get_fb_frame_done: get frame done status of display controller.
- * @trigger: trigger display controller.
- *     - this one would be used only in case of CPU mode.
- *  @set_early_blank_mode: set framebuffer blank mode.
- *     - this callback should be called prior to fb_blank() by a client driver
- *     only if needing.
- *  @set_blank_mode: set framebuffer blank mode.
- *     - this callback should be called after fb_blank() by a client driver
- *     only if needing.
- */
-
-struct mipi_dsim_master_ops {
-       int (*cmd_write)(struct mipi_dsim_device *dsim, unsigned int data_id,
-               const unsigned char *data0, unsigned int data1);
-       int (*cmd_read)(struct mipi_dsim_device *dsim, unsigned int data_id,
-               unsigned int data0, unsigned int req_size, u8 *rx_buf);
-       int (*get_dsim_frame_done)(struct mipi_dsim_device *dsim);
-       int (*clear_dsim_frame_done)(struct mipi_dsim_device *dsim);
-
-       int (*get_fb_frame_done)(struct fb_info *info);
-       void (*trigger)(struct fb_info *info);
-       int (*set_early_blank_mode)(struct mipi_dsim_device *dsim, int power);
-       int (*set_blank_mode)(struct mipi_dsim_device *dsim, int power);
-};
-
-/*
- * device structure for mipi-dsi based lcd panel.
- *
- * @name: name of the device to use with this device, or an
- *     alias for that name.
- * @dev: driver model representation of the device.
- * @id: id of device to be registered.
- * @bus_id: bus id for identifing connected bus
- *     and this bus id should be same as id of mipi_dsim_device.
- * @irq: irq number for signaling when framebuffer transfer of
- *     lcd panel module is completed.
- *     this irq would be used only for MIPI-DSI based CPU mode lcd panel.
- * @master: pointer to mipi-dsi master device object.
- * @platform_data: lcd panel specific platform data.
- */
-struct mipi_dsim_lcd_device {
-       char                    *name;
-       struct device           dev;
-       int                     id;
-       int                     bus_id;
-       int                     irq;
-       int                     panel_reverse;
-
-       struct mipi_dsim_device *master;
-       void                    *platform_data;
-};
-
-/*
- * driver structure for mipi-dsi based lcd panel.
- *
- * this structure should be registered by lcd panel driver.
- * mipi-dsi driver seeks lcd panel registered through name field
- * and calls these callback functions in appropriate time.
- *
- * @name: name of the driver to use with this device, or an
- *     alias for that name.
- * @id: id of driver to be registered.
- *     this id would be used for finding device object registered.
- */
-struct mipi_dsim_lcd_driver {
-       char                    *name;
-       int                     id;
-
-       void    (*power_on)(struct mipi_dsim_lcd_device *dsim_dev, int enable);
-       void    (*set_sequence)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*probe)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*remove)(struct mipi_dsim_lcd_device *dsim_dev);
-       void    (*shutdown)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*suspend)(struct mipi_dsim_lcd_device *dsim_dev);
-       int     (*resume)(struct mipi_dsim_lcd_device *dsim_dev);
-};
-
-/*
- * register mipi_dsim_lcd_device to mipi-dsi master.
- */
-int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device
-                                               *lcd_dev);
-/**
- * register mipi_dsim_lcd_driver object defined by lcd panel driver
- * to mipi-dsi driver.
- */
-int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver
-                                               *lcd_drv);
-#endif /* _EXYNOS_MIPI_DSIM_H */
diff --git a/include/video/imx-ipu-image-convert.h b/include/video/imx-ipu-image-convert.h
new file mode 100644 (file)
index 0000000..7b87efc
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2012-2016 Mentor Graphics Inc.
+ *
+ * i.MX Queued image conversion support, with tiling and rotation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+#ifndef __IMX_IPU_IMAGE_CONVERT_H__
+#define __IMX_IPU_IMAGE_CONVERT_H__
+
+#include <video/imx-ipu-v3.h>
+
+struct ipu_image_convert_ctx;
+
+/**
+ * struct ipu_image_convert_run - image conversion run request struct
+ *
+ * @ctx:       the conversion context
+ * @in_phys:   dma addr of input image buffer for this run
+ * @out_phys:  dma addr of output image buffer for this run
+ * @status:    completion status of this run
+ */
+struct ipu_image_convert_run {
+       struct ipu_image_convert_ctx *ctx;
+
+       dma_addr_t in_phys;
+       dma_addr_t out_phys;
+
+       int status;
+
+       /* internal to image converter, callers don't touch */
+       struct list_head list;
+};
+
+/**
+ * ipu_image_convert_cb_t - conversion callback function prototype
+ *
+ * @run:       the completed conversion run pointer
+ * @ctx:       a private context pointer for the callback
+ */
+typedef void (*ipu_image_convert_cb_t)(struct ipu_image_convert_run *run,
+                                      void *ctx);
+
+/**
+ * ipu_image_convert_enum_format() - enumerate the image converter's
+ *     supported input and output pixel formats.
+ *
+ * @index:     pixel format index
+ * @fourcc:    v4l2 fourcc for this index
+ *
+ * Returns 0 with a valid index and fills in v4l2 fourcc, -EINVAL otherwise.
+ *
+ * In V4L2, drivers can call ipu_image_enum_format() in .enum_fmt.
+ */
+int ipu_image_convert_enum_format(int index, u32 *fourcc);
+
+/**
+ * ipu_image_convert_adjust() - adjust input/output images to IPU restrictions.
+ *
+ * @in:                input image format, adjusted on return
+ * @out:       output image format, adjusted on return
+ * @rot_mode:  rotation mode
+ *
+ * In V4L2, drivers can call ipu_image_convert_adjust() in .try_fmt.
+ */
+void ipu_image_convert_adjust(struct ipu_image *in, struct ipu_image *out,
+                             enum ipu_rotate_mode rot_mode);
+
+/**
+ * ipu_image_convert_verify() - verify that input/output image formats
+ *         and rotation mode meet IPU restrictions.
+ *
+ * @in:                input image format
+ * @out:       output image format
+ * @rot_mode:  rotation mode
+ *
+ * Returns 0 if the formats and rotation mode meet IPU restrictions,
+ * -EINVAL otherwise.
+ */
+int ipu_image_convert_verify(struct ipu_image *in, struct ipu_image *out,
+                            enum ipu_rotate_mode rot_mode);
+
+/**
+ * ipu_image_convert_prepare() - prepare a conversion context.
+ *
+ * @ipu:       the IPU handle to use for the conversions
+ * @ic_task:   the IC task to use for the conversions
+ * @in:                input image format
+ * @out:       output image format
+ * @rot_mode:  rotation mode
+ * @complete:  run completion callback
+ * @complete_context:  a context pointer for the completion callback
+ *
+ * Returns an opaque conversion context pointer on success, error pointer
+ * on failure. The input/output formats and rotation mode must already meet
+ * IPU retrictions.
+ *
+ * In V4L2, drivers should call ipu_image_convert_prepare() at streamon.
+ */
+struct ipu_image_convert_ctx *
+ipu_image_convert_prepare(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                         struct ipu_image *in, struct ipu_image *out,
+                         enum ipu_rotate_mode rot_mode,
+                         ipu_image_convert_cb_t complete,
+                         void *complete_context);
+
+/**
+ * ipu_image_convert_unprepare() - unprepare a conversion context.
+ *
+ * @ctx: the conversion context pointer to unprepare
+ *
+ * Aborts any active or pending conversions for this context and
+ * frees the context. Any currently active or pending runs belonging
+ * to this context are returned via the completion callback with an
+ * error run status.
+ *
+ * In V4L2, drivers should call ipu_image_convert_unprepare() at
+ * streamoff.
+ */
+void ipu_image_convert_unprepare(struct ipu_image_convert_ctx *ctx);
+
+/**
+ * ipu_image_convert_queue() - queue a conversion run
+ *
+ * @run: the run request pointer
+ *
+ * ipu_image_convert_run must be dynamically allocated (_not_ as a local
+ * var) by callers and filled in with a previously prepared conversion
+ * context handle and the dma addr's of the input and output image buffers
+ * for this conversion run.
+ *
+ * When this conversion completes, the run pointer is returned via the
+ * completion callback. The caller is responsible for freeing the run
+ * object after it completes.
+ *
+ * In V4L2, drivers should call ipu_image_convert_queue() while
+ * streaming to queue the conversion of a received input buffer.
+ * For example mem2mem devices this would be called in .device_run.
+ */
+int ipu_image_convert_queue(struct ipu_image_convert_run *run);
+
+/**
+ * ipu_image_convert_abort() - abort conversions
+ *
+ * @ctx: the conversion context pointer
+ *
+ * This will abort any active or pending conversions for this context.
+ * Any currently active or pending runs belonging to this context are
+ * returned via the completion callback with an error run status.
+ */
+void ipu_image_convert_abort(struct ipu_image_convert_ctx *ctx);
+
+/**
+ * ipu_image_convert() - asynchronous image conversion request
+ *
+ * @ipu:       the IPU handle to use for the conversion
+ * @ic_task:   the IC task to use for the conversion
+ * @in:                input image format
+ * @out:       output image format
+ * @rot_mode:  rotation mode
+ * @complete:  run completion callback
+ * @complete_context:  a context pointer for the completion callback
+ *
+ * Request a single image conversion. Returns the run that has been queued.
+ * A conversion context is automatically created and is available in run->ctx.
+ * As with ipu_image_convert_prepare(), the input/output formats and rotation
+ * mode must already meet IPU retrictions.
+ *
+ * On successful return the caller can queue more run requests if needed, using
+ * the prepared context in run->ctx. The caller is responsible for unpreparing
+ * the context when no more conversion requests are needed.
+ */
+struct ipu_image_convert_run *
+ipu_image_convert(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                 struct ipu_image *in, struct ipu_image *out,
+                 enum ipu_rotate_mode rot_mode,
+                 ipu_image_convert_cb_t complete,
+                 void *complete_context);
+
+/**
+ * ipu_image_convert_sync() - synchronous single image conversion request
+ *
+ * @ipu:       the IPU handle to use for the conversion
+ * @ic_task:   the IC task to use for the conversion
+ * @in:                input image format
+ * @out:       output image format
+ * @rot_mode:  rotation mode
+ *
+ * Carry out a single image conversion. Returns when the conversion
+ * completes. The input/output formats and rotation mode must already
+ * meet IPU retrictions. The created context is automatically unprepared
+ * and the run freed on return.
+ */
+int ipu_image_convert_sync(struct ipu_soc *ipu, enum ipu_ic_task ic_task,
+                          struct ipu_image *in, struct ipu_image *out,
+                          enum ipu_rotate_mode rot_mode);
+
+
+#endif /* __IMX_IPU_IMAGE_CONVERT_H__ */
index 7adeaae..173073e 100644 (file)
@@ -63,23 +63,41 @@ enum ipu_csi_dest {
 /*
  * Enumeration of IPU rotation modes
  */
+#define IPU_ROT_BIT_VFLIP (1 << 0)
+#define IPU_ROT_BIT_HFLIP (1 << 1)
+#define IPU_ROT_BIT_90    (1 << 2)
+
 enum ipu_rotate_mode {
        IPU_ROTATE_NONE = 0,
-       IPU_ROTATE_VERT_FLIP,
-       IPU_ROTATE_HORIZ_FLIP,
-       IPU_ROTATE_180,
-       IPU_ROTATE_90_RIGHT,
-       IPU_ROTATE_90_RIGHT_VFLIP,
-       IPU_ROTATE_90_RIGHT_HFLIP,
-       IPU_ROTATE_90_LEFT,
+       IPU_ROTATE_VERT_FLIP = IPU_ROT_BIT_VFLIP,
+       IPU_ROTATE_HORIZ_FLIP = IPU_ROT_BIT_HFLIP,
+       IPU_ROTATE_180 = (IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
+       IPU_ROTATE_90_RIGHT = IPU_ROT_BIT_90,
+       IPU_ROTATE_90_RIGHT_VFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_VFLIP),
+       IPU_ROTATE_90_RIGHT_HFLIP = (IPU_ROT_BIT_90 | IPU_ROT_BIT_HFLIP),
+       IPU_ROTATE_90_LEFT = (IPU_ROT_BIT_90 |
+                             IPU_ROT_BIT_VFLIP | IPU_ROT_BIT_HFLIP),
 };
 
+/* 90-degree rotations require the IRT unit */
+#define ipu_rot_mode_is_irt(m) (((m) & IPU_ROT_BIT_90) != 0)
+
 enum ipu_color_space {
        IPUV3_COLORSPACE_RGB,
        IPUV3_COLORSPACE_YUV,
        IPUV3_COLORSPACE_UNKNOWN,
 };
 
+/*
+ * Enumeration of VDI MOTION select
+ */
+enum ipu_motion_sel {
+       MOTION_NONE = 0,
+       LOW_MOTION,
+       MED_MOTION,
+       HIGH_MOTION,
+};
+
 struct ipuv3_channel;
 
 enum ipu_channel_irq {
@@ -97,20 +115,42 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_CSI2                      2
 #define IPUV3_CHANNEL_CSI3                      3
 #define IPUV3_CHANNEL_VDI_MEM_IC_VF             5
+/*
+ * NOTE: channels 6,7 are unused in the IPU and are not IDMAC channels,
+ * but the direct CSI->VDI linking is handled the same way as IDMAC
+ * channel linking in the FSU via the IPU_FS_PROC_FLOW registers, so
+ * these channel names are used to support the direct CSI->VDI link.
+ */
+#define IPUV3_CHANNEL_CSI_DIRECT                6
+#define IPUV3_CHANNEL_CSI_VDI_PREV              7
+#define IPUV3_CHANNEL_MEM_VDI_PREV              8
+#define IPUV3_CHANNEL_MEM_VDI_CUR               9
+#define IPUV3_CHANNEL_MEM_VDI_NEXT             10
 #define IPUV3_CHANNEL_MEM_IC_PP                        11
 #define IPUV3_CHANNEL_MEM_IC_PRP_VF            12
+#define IPUV3_CHANNEL_VDI_MEM_RECENT           13
 #define IPUV3_CHANNEL_G_MEM_IC_PRP_VF          14
 #define IPUV3_CHANNEL_G_MEM_IC_PP              15
+#define IPUV3_CHANNEL_G_MEM_IC_PRP_VF_ALPHA    17
+#define IPUV3_CHANNEL_G_MEM_IC_PP_ALPHA                18
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB_ALPHA        19
 #define IPUV3_CHANNEL_IC_PRP_ENC_MEM           20
 #define IPUV3_CHANNEL_IC_PRP_VF_MEM            21
 #define IPUV3_CHANNEL_IC_PP_MEM                        22
 #define IPUV3_CHANNEL_MEM_BG_SYNC              23
 #define IPUV3_CHANNEL_MEM_BG_ASYNC             24
+#define IPUV3_CHANNEL_MEM_VDI_PLANE1_COMB      25
+#define IPUV3_CHANNEL_MEM_VDI_PLANE3_COMB      26
 #define IPUV3_CHANNEL_MEM_FG_SYNC              27
 #define IPUV3_CHANNEL_MEM_DC_SYNC              28
 #define IPUV3_CHANNEL_MEM_FG_ASYNC             29
 #define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA                31
+#define IPUV3_CHANNEL_MEM_FG_ASYNC_ALPHA       33
+#define IPUV3_CHANNEL_DC_MEM_READ              40
 #define IPUV3_CHANNEL_MEM_DC_ASYNC             41
+#define IPUV3_CHANNEL_MEM_DC_COMMAND           42
+#define IPUV3_CHANNEL_MEM_DC_COMMAND2          43
+#define IPUV3_CHANNEL_MEM_DC_OUTPUT_MASK       44
 #define IPUV3_CHANNEL_MEM_ROT_ENC              45
 #define IPUV3_CHANNEL_MEM_ROT_VF               46
 #define IPUV3_CHANNEL_MEM_ROT_PP               47
@@ -118,6 +158,8 @@ enum ipu_channel_irq {
 #define IPUV3_CHANNEL_ROT_VF_MEM               49
 #define IPUV3_CHANNEL_ROT_PP_MEM               50
 #define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA                51
+#define IPUV3_CHANNEL_MEM_BG_ASYNC_ALPHA       52
+#define IPUV3_NUM_CHANNELS                     64
 
 int ipu_map_irq(struct ipu_soc *ipu, int irq);
 int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
@@ -138,6 +180,7 @@ int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
 /*
  * IPU Common functions
  */
+int ipu_get_num(struct ipu_soc *ipu);
 void ipu_set_csi_src_mux(struct ipu_soc *ipu, int csi_id, bool mipi_csi2);
 void ipu_set_ic_src_mux(struct ipu_soc *ipu, int csi_id, bool vdi);
 void ipu_dump(struct ipu_soc *ipu);
@@ -160,6 +203,10 @@ int ipu_idmac_get_current_buffer(struct ipuv3_channel *channel);
 bool ipu_idmac_buffer_is_ready(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
 void ipu_idmac_clear_buffer(struct ipuv3_channel *channel, u32 buf_num);
+int ipu_fsu_link(struct ipu_soc *ipu, int src_ch, int sink_ch);
+int ipu_fsu_unlink(struct ipu_soc *ipu, int src_ch, int sink_ch);
+int ipu_idmac_link(struct ipuv3_channel *src, struct ipuv3_channel *sink);
+int ipu_idmac_unlink(struct ipuv3_channel *src, struct ipuv3_channel *sink);
 
 /*
  * IPU Channel Parameter Memory (cpmem) functions
@@ -184,8 +231,10 @@ void ipu_cpmem_set_resolution(struct ipuv3_channel *ch, int xres, int yres);
 void ipu_cpmem_set_stride(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_high_priority(struct ipuv3_channel *ch);
 void ipu_cpmem_set_buffer(struct ipuv3_channel *ch, int bufnum, dma_addr_t buf);
+void ipu_cpmem_set_uv_offset(struct ipuv3_channel *ch, u32 u_off, u32 v_off);
 void ipu_cpmem_interlaced_scan(struct ipuv3_channel *ch, int stride);
 void ipu_cpmem_set_axi_id(struct ipuv3_channel *ch, u32 id);
+int ipu_cpmem_get_burstsize(struct ipuv3_channel *ch);
 void ipu_cpmem_set_burstsize(struct ipuv3_channel *ch, int burstsize);
 void ipu_cpmem_set_block_mode(struct ipuv3_channel *ch);
 void ipu_cpmem_set_rotation(struct ipuv3_channel *ch,
@@ -316,6 +365,19 @@ struct ipu_ic *ipu_ic_get(struct ipu_soc *ipu, enum ipu_ic_task task);
 void ipu_ic_put(struct ipu_ic *ic);
 void ipu_ic_dump(struct ipu_ic *ic);
 
+/*
+ * IPU Video De-Interlacer (vdi) functions
+ */
+struct ipu_vdi;
+void ipu_vdi_set_field_order(struct ipu_vdi *vdi, v4l2_std_id std, u32 field);
+void ipu_vdi_set_motion(struct ipu_vdi *vdi, enum ipu_motion_sel motion_sel);
+void ipu_vdi_setup(struct ipu_vdi *vdi, u32 code, int xres, int yres);
+void ipu_vdi_unsetup(struct ipu_vdi *vdi);
+int ipu_vdi_enable(struct ipu_vdi *vdi);
+int ipu_vdi_disable(struct ipu_vdi *vdi);
+struct ipu_vdi *ipu_vdi_get(struct ipu_soc *ipu);
+void ipu_vdi_put(struct ipu_vdi *vdi);
+
 /*
  * IPU Sensor Multiple FIFO Controller (SMFC) functions
  */
index d7fc226..34407f1 100644 (file)
@@ -1288,6 +1288,7 @@ config SYSFS_DEPRECATED_V2
 
 config RELAY
        bool "Kernel->user space relay support (formerly relayfs)"
+       select IRQ_WORK
        help
          This option enables support for relay interface support in
          certain file systems (such as debugfs).
index 7bc47ee..c4fb455 100644 (file)
@@ -2,6 +2,8 @@
 # Makefile for the linux kernel.
 #
 
+ccflags-y := -fno-function-sections -fno-data-sections
+
 obj-y                          := main.o version.o mounts.o
 ifneq ($(CONFIG_BLK_DEV_INITRD),y)
 obj-y                          += noinitramfs.o
index c6521c2..e12307d 100644 (file)
--- a/ipc/msg.c
+++ b/ipc/msg.c
@@ -51,19 +51,14 @@ struct msg_receiver {
        long                    r_msgtype;
        long                    r_maxsize;
 
-       /*
-        * Mark r_msg volatile so that the compiler
-        * does not try to get smart and optimize
-        * it. We rely on this for the lockless
-        * receive algorithm.
-        */
-       struct msg_msg          *volatile r_msg;
+       struct msg_msg          *r_msg;
 };
 
 /* one msg_sender for each sleeping sender */
 struct msg_sender {
        struct list_head        list;
        struct task_struct      *tsk;
+       size_t                  msgsz;
 };
 
 #define SEARCH_ANY             1
@@ -159,45 +154,72 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params)
        return msq->q_perm.id;
 }
 
-static inline void ss_add(struct msg_queue *msq, struct msg_sender *mss)
+static inline bool msg_fits_inqueue(struct msg_queue *msq, size_t msgsz)
+{
+       return msgsz + msq->q_cbytes <= msq->q_qbytes &&
+               1 + msq->q_qnum <= msq->q_qbytes;
+}
+
+static inline void ss_add(struct msg_queue *msq,
+                         struct msg_sender *mss, size_t msgsz)
 {
        mss->tsk = current;
+       mss->msgsz = msgsz;
        __set_current_state(TASK_INTERRUPTIBLE);
        list_add_tail(&mss->list, &msq->q_senders);
 }
 
 static inline void ss_del(struct msg_sender *mss)
 {
-       if (mss->list.next != NULL)
+       if (mss->list.next)
                list_del(&mss->list);
 }
 
-static void ss_wakeup(struct list_head *h, int kill)
+static void ss_wakeup(struct msg_queue *msq,
+                     struct wake_q_head *wake_q, bool kill)
 {
        struct msg_sender *mss, *t;
+       struct task_struct *stop_tsk = NULL;
+       struct list_head *h = &msq->q_senders;
 
        list_for_each_entry_safe(mss, t, h, list) {
                if (kill)
                        mss->list.next = NULL;
-               wake_up_process(mss->tsk);
+
+               /*
+                * Stop at the first task we don't wakeup,
+                * we've already iterated the original
+                * sender queue.
+                */
+               else if (stop_tsk == mss->tsk)
+                       break;
+               /*
+                * We are not in an EIDRM scenario here, therefore
+                * verify that we really need to wakeup the task.
+                * To maintain current semantics and wakeup order,
+                * move the sender to the tail on behalf of the
+                * blocked task.
+                */
+               else if (!msg_fits_inqueue(msq, mss->msgsz)) {
+                       if (!stop_tsk)
+                               stop_tsk = mss->tsk;
+
+                       list_move_tail(&mss->list, &msq->q_senders);
+                       continue;
+               }
+
+               wake_q_add(wake_q, mss->tsk);
        }
 }
 
-static void expunge_all(struct msg_queue *msq, int res)
+static void expunge_all(struct msg_queue *msq, int res,
+                       struct wake_q_head *wake_q)
 {
        struct msg_receiver *msr, *t;
 
        list_for_each_entry_safe(msr, t, &msq->q_receivers, r_list) {
-               msr->r_msg = NULL; /* initialize expunge ordering */
-               wake_up_process(msr->r_tsk);
-               /*
-                * Ensure that the wakeup is visible before setting r_msg as
-                * the receiving end depends on it: either spinning on a nil,
-                * or dealing with -EAGAIN cases. See lockless receive part 1
-                * and 2 in do_msgrcv().
-                */
-               smp_wmb(); /* barrier (B) */
-               msr->r_msg = ERR_PTR(res);
+               wake_q_add(wake_q, msr->r_tsk);
+               WRITE_ONCE(msr->r_msg, ERR_PTR(res));
        }
 }
 
@@ -213,11 +235,13 @@ static void freeque(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
 {
        struct msg_msg *msg, *t;
        struct msg_queue *msq = container_of(ipcp, struct msg_queue, q_perm);
+       WAKE_Q(wake_q);
 
-       expunge_all(msq, -EIDRM);
-       ss_wakeup(&msq->q_senders, 1);
+       expunge_all(msq, -EIDRM, &wake_q);
+       ss_wakeup(msq, &wake_q, true);
        msg_rmid(ns, msq);
        ipc_unlock_object(&msq->q_perm);
+       wake_up_q(&wake_q);
        rcu_read_unlock();
 
        list_for_each_entry_safe(msg, t, &msq->q_messages, m_list) {
@@ -372,6 +396,9 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                freeque(ns, ipcp);
                goto out_up;
        case IPC_SET:
+       {
+               WAKE_Q(wake_q);
+
                if (msqid64.msg_qbytes > ns->msg_ctlmnb &&
                    !capable(CAP_SYS_RESOURCE)) {
                        err = -EPERM;
@@ -386,15 +413,21 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
                msq->q_qbytes = msqid64.msg_qbytes;
 
                msq->q_ctime = get_seconds();
-               /* sleeping receivers might be excluded by
+               /*
+                * Sleeping receivers might be excluded by
                 * stricter permissions.
                 */
-               expunge_all(msq, -EAGAIN);
-               /* sleeping senders might be able to send
+               expunge_all(msq, -EAGAIN, &wake_q);
+               /*
+                * Sleeping senders might be able to send
                 * due to a larger queue size.
                 */
-               ss_wakeup(&msq->q_senders, 0);
-               break;
+               ss_wakeup(msq, &wake_q, false);
+               ipc_unlock_object(&msq->q_perm);
+               wake_up_q(&wake_q);
+
+               goto out_unlock1;
+       }
        default:
                err = -EINVAL;
                goto out_unlock1;
@@ -566,7 +599,8 @@ static int testmsg(struct msg_msg *msg, long type, int mode)
        return 0;
 }
 
-static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
+static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg,
+                                struct wake_q_head *wake_q)
 {
        struct msg_receiver *msr, *t;
 
@@ -577,27 +611,14 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg)
 
                        list_del(&msr->r_list);
                        if (msr->r_maxsize < msg->m_ts) {
-                               /* initialize pipelined send ordering */
-                               msr->r_msg = NULL;
-                               wake_up_process(msr->r_tsk);
-                               /* barrier (B) see barrier comment below */
-                               smp_wmb();
-                               msr->r_msg = ERR_PTR(-E2BIG);
+                               wake_q_add(wake_q, msr->r_tsk);
+                               WRITE_ONCE(msr->r_msg, ERR_PTR(-E2BIG));
                        } else {
-                               msr->r_msg = NULL;
                                msq->q_lrpid = task_pid_vnr(msr->r_tsk);
                                msq->q_rtime = get_seconds();
-                               wake_up_process(msr->r_tsk);
-                               /*
-                                * Ensure that the wakeup is visible before
-                                * setting r_msg, as the receiving can otherwise
-                                * exit - once r_msg is set, the receiver can
-                                * continue. See lockless receive part 1 and 2
-                                * in do_msgrcv(). Barrier (B).
-                                */
-                               smp_wmb();
-                               msr->r_msg = msg;
 
+                               wake_q_add(wake_q, msr->r_tsk);
+                               WRITE_ONCE(msr->r_msg, msg);
                                return 1;
                        }
                }
@@ -613,6 +634,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
        struct msg_msg *msg;
        int err;
        struct ipc_namespace *ns;
+       WAKE_Q(wake_q);
 
        ns = current->nsproxy->ipc_ns;
 
@@ -654,10 +676,8 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                if (err)
                        goto out_unlock0;
 
-               if (msgsz + msq->q_cbytes <= msq->q_qbytes &&
-                               1 + msq->q_qnum <= msq->q_qbytes) {
+               if (msg_fits_inqueue(msq, msgsz))
                        break;
-               }
 
                /* queue full, wait: */
                if (msgflg & IPC_NOWAIT) {
@@ -666,7 +686,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                }
 
                /* enqueue the sender and prepare to block */
-               ss_add(msq, &s);
+               ss_add(msq, &s, msgsz);
 
                if (!ipc_rcu_getref(msq)) {
                        err = -EIDRM;
@@ -686,7 +706,6 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                        err = -EIDRM;
                        goto out_unlock0;
                }
-
                ss_del(&s);
 
                if (signal_pending(current)) {
@@ -695,10 +714,11 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
                }
 
        }
+
        msq->q_lspid = task_tgid_vnr(current);
        msq->q_stime = get_seconds();
 
-       if (!pipelined_send(msq, msg)) {
+       if (!pipelined_send(msq, msg, &wake_q)) {
                /* no one is waiting for this message, enqueue it */
                list_add_tail(&msg->m_list, &msq->q_messages);
                msq->q_cbytes += msgsz;
@@ -712,6 +732,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
 
 out_unlock0:
        ipc_unlock_object(&msq->q_perm);
+       wake_up_q(&wake_q);
 out_unlock1:
        rcu_read_unlock();
        if (msg != NULL)
@@ -829,6 +850,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
        struct msg_queue *msq;
        struct ipc_namespace *ns;
        struct msg_msg *msg, *copy = NULL;
+       WAKE_Q(wake_q);
 
        ns = current->nsproxy->ipc_ns;
 
@@ -893,7 +915,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
                        msq->q_cbytes -= msg->m_ts;
                        atomic_sub(msg->m_ts, &ns->msg_bytes);
                        atomic_dec(&ns->msg_hdrs);
-                       ss_wakeup(&msq->q_senders, 0);
+                       ss_wakeup(msq, &wake_q, false);
 
                        goto out_unlock0;
                }
@@ -919,71 +941,38 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
                rcu_read_unlock();
                schedule();
 
-               /* Lockless receive, part 1:
-                * Disable preemption.  We don't hold a reference to the queue
-                * and getting a reference would defeat the idea of a lockless
-                * operation, thus the code relies on rcu to guarantee the
-                * existence of msq:
+               /*
+                * Lockless receive, part 1:
+                * We don't hold a reference to the queue and getting a
+                * reference would defeat the idea of a lockless operation,
+                * thus the code relies on rcu to guarantee the existence of
+                * msq:
                 * Prior to destruction, expunge_all(-EIRDM) changes r_msg.
                 * Thus if r_msg is -EAGAIN, then the queue not yet destroyed.
-                * rcu_read_lock() prevents preemption between reading r_msg
-                * and acquiring the q_perm.lock in ipc_lock_object().
                 */
                rcu_read_lock();
 
-               /* Lockless receive, part 2:
-                * Wait until pipelined_send or expunge_all are outside of
-                * wake_up_process(). There is a race with exit(), see
-                * ipc/mqueue.c for the details. The correct serialization
-                * ensures that a receiver cannot continue without the wakeup
-                * being visibible _before_ setting r_msg:
-                *
-                * CPU 0                             CPU 1
-                * <loop receiver>
-                *   smp_rmb(); (A) <-- pair -.      <waker thread>
-                *   <load ->r_msg>           |        msr->r_msg = NULL;
-                *                            |        wake_up_process();
-                * <continue>                 `------> smp_wmb(); (B)
-                *                                     msr->r_msg = msg;
+               /*
+                * Lockless receive, part 2:
+                * The work in pipelined_send() and expunge_all():
+                * - Set pointer to message
+                * - Queue the receiver task for later wakeup
+                * - Wake up the process after the lock is dropped.
                 *
-                * Where (A) orders the message value read and where (B) orders
-                * the write to the r_msg -- done in both pipelined_send and
-                * expunge_all.
-                */
-               for (;;) {
-                       /*
-                        * Pairs with writer barrier in pipelined_send
-                        * or expunge_all.
-                        */
-                       smp_rmb(); /* barrier (A) */
-                       msg = (struct msg_msg *)msr_d.r_msg;
-                       if (msg)
-                               break;
-
-                       /*
-                        * The cpu_relax() call is a compiler barrier
-                        * which forces everything in this loop to be
-                        * re-loaded.
-                        */
-                       cpu_relax();
-               }
-
-               /* Lockless receive, part 3:
-                * If there is a message or an error then accept it without
-                * locking.
+                * Should the process wake up before this wakeup (due to a
+                * signal) it will either see the message and continue ...
                 */
+               msg = READ_ONCE(msr_d.r_msg);
                if (msg != ERR_PTR(-EAGAIN))
                        goto out_unlock1;
 
-               /* Lockless receive, part 3:
-                * Acquire the queue spinlock.
-                */
+                /*
+                 * ... or see -EAGAIN, acquire the lock to check the message
+                 * again.
+                 */
                ipc_lock_object(&msq->q_perm);
 
-               /* Lockless receive, part 4:
-                * Repeat test after acquiring the spinlock.
-                */
-               msg = (struct msg_msg *)msr_d.r_msg;
+               msg = msr_d.r_msg;
                if (msg != ERR_PTR(-EAGAIN))
                        goto out_unlock0;
 
@@ -998,6 +987,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
 
 out_unlock0:
        ipc_unlock_object(&msq->q_perm);
+       wake_up_q(&wake_q);
 out_unlock1:
        rcu_read_unlock();
        if (IS_ERR(msg)) {
index 7c9d4f7..10b94bc 100644 (file)
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -162,14 +162,21 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
 
 /*
  * Locking:
+ * a) global sem_lock() for read/write
  *     sem_undo.id_next,
  *     sem_array.complex_count,
- *     sem_array.pending{_alter,_cont},
- *     sem_array.sem_undo: global sem_lock() for read/write
- *     sem_undo.proc_next: only "current" is allowed to read/write that field.
+ *     sem_array.complex_mode
+ *     sem_array.pending{_alter,_const},
+ *     sem_array.sem_undo
  *
+ * b) global or semaphore sem_lock() for read/write:
  *     sem_array.sem_base[i].pending_{const,alter}:
- *             global or semaphore sem_lock() for read/write
+ *     sem_array.complex_mode (for read)
+ *
+ * c) special:
+ *     sem_undo_list.list_proc:
+ *     * undo_list->lock for write
+ *     * rcu for read
  */
 
 #define sc_semmsl      sem_ctls[0]
@@ -260,30 +267,61 @@ static void sem_rcu_free(struct rcu_head *head)
 }
 
 /*
- * Wait until all currently ongoing simple ops have completed.
+ * Enter the mode suitable for non-simple operations:
  * Caller must own sem_perm.lock.
- * New simple ops cannot start, because simple ops first check
- * that sem_perm.lock is free.
- * that a) sem_perm.lock is free and b) complex_count is 0.
  */
-static void sem_wait_array(struct sem_array *sma)
+static void complexmode_enter(struct sem_array *sma)
 {
        int i;
        struct sem *sem;
 
-       if (sma->complex_count)  {
-               /* The thread that increased sma->complex_count waited on
-                * all sem->lock locks. Thus we don't need to wait again.
-                */
+       if (sma->complex_mode)  {
+               /* We are already in complex_mode. Nothing to do */
                return;
        }
 
+       /* We need a full barrier after seting complex_mode:
+        * The write to complex_mode must be visible
+        * before we read the first sem->lock spinlock state.
+        */
+       smp_store_mb(sma->complex_mode, true);
+
        for (i = 0; i < sma->sem_nsems; i++) {
                sem = sma->sem_base + i;
                spin_unlock_wait(&sem->lock);
        }
+       /*
+        * spin_unlock_wait() is not a memory barriers, it is only a
+        * control barrier. The code must pair with spin_unlock(&sem->lock),
+        * thus just the control barrier is insufficient.
+        *
+        * smp_rmb() is sufficient, as writes cannot pass the control barrier.
+        */
+       smp_rmb();
 }
 
+/*
+ * Try to leave the mode that disallows simple operations:
+ * Caller must own sem_perm.lock.
+ */
+static void complexmode_tryleave(struct sem_array *sma)
+{
+       if (sma->complex_count)  {
+               /* Complex ops are sleeping.
+                * We must stay in complex mode
+                */
+               return;
+       }
+       /*
+        * Immediately after setting complex_mode to false,
+        * a simple op can start. Thus: all memory writes
+        * performed by the current operation must be visible
+        * before we set complex_mode to false.
+        */
+       smp_store_release(&sma->complex_mode, false);
+}
+
+#define SEM_GLOBAL_LOCK        (-1)
 /*
  * If the request contains only one semaphore operation, and there are
  * no complex transactions pending, lock only the semaphore involved.
@@ -300,56 +338,42 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
                /* Complex operation - acquire a full lock */
                ipc_lock_object(&sma->sem_perm);
 
-               /* And wait until all simple ops that are processed
-                * right now have dropped their locks.
-                */
-               sem_wait_array(sma);
-               return -1;
+               /* Prevent parallel simple ops */
+               complexmode_enter(sma);
+               return SEM_GLOBAL_LOCK;
        }
 
        /*
         * Only one semaphore affected - try to optimize locking.
-        * The rules are:
-        * - optimized locking is possible if no complex operation
-        *   is either enqueued or processed right now.
-        * - The test for enqueued complex ops is simple:
-        *      sma->complex_count != 0
-        * - Testing for complex ops that are processed right now is
-        *   a bit more difficult. Complex ops acquire the full lock
-        *   and first wait that the running simple ops have completed.
-        *   (see above)
-        *   Thus: If we own a simple lock and the global lock is free
-        *      and complex_count is now 0, then it will stay 0 and
-        *      thus just locking sem->lock is sufficient.
+        * Optimized locking is possible if no complex operation
+        * is either enqueued or processed right now.
+        *
+        * Both facts are tracked by complex_mode.
         */
        sem = sma->sem_base + sops->sem_num;
 
-       if (sma->complex_count == 0) {
+       /*
+        * Initial check for complex_mode. Just an optimization,
+        * no locking, no memory barrier.
+        */
+       if (!sma->complex_mode) {
                /*
                 * It appears that no complex operation is around.
                 * Acquire the per-semaphore lock.
                 */
                spin_lock(&sem->lock);
 
-               /* Then check that the global lock is free */
-               if (!spin_is_locked(&sma->sem_perm.lock)) {
-                       /*
-                        * We need a memory barrier with acquire semantics,
-                        * otherwise we can race with another thread that does:
-                        *      complex_count++;
-                        *      spin_unlock(sem_perm.lock);
-                        */
-                       smp_acquire__after_ctrl_dep();
+               /*
+                * See 51d7d5205d33
+                * ("powerpc: Add smp_mb() to arch_spin_is_locked()"):
+                * A full barrier is required: the write of sem->lock
+                * must be visible before the read is executed
+                */
+               smp_mb();
 
-                       /*
-                        * Now repeat the test of complex_count:
-                        * It can't change anymore until we drop sem->lock.
-                        * Thus: if is now 0, then it will stay 0.
-                        */
-                       if (sma->complex_count == 0) {
-                               /* fast path successful! */
-                               return sops->sem_num;
-                       }
+               if (!smp_load_acquire(&sma->complex_mode)) {
+                       /* fast path successful! */
+                       return sops->sem_num;
                }
                spin_unlock(&sem->lock);
        }
@@ -369,15 +393,16 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,
                /* Not a false alarm, thus complete the sequence for a
                 * full lock.
                 */
-               sem_wait_array(sma);
-               return -1;
+               complexmode_enter(sma);
+               return SEM_GLOBAL_LOCK;
        }
 }
 
 static inline void sem_unlock(struct sem_array *sma, int locknum)
 {
-       if (locknum == -1) {
+       if (locknum == SEM_GLOBAL_LOCK) {
                unmerge_queues(sma);
+               complexmode_tryleave(sma);
                ipc_unlock_object(&sma->sem_perm);
        } else {
                struct sem *sem = sma->sem_base + locknum;
@@ -529,6 +554,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params)
        }
 
        sma->complex_count = 0;
+       sma->complex_mode = true; /* dropped by sem_unlock below */
        INIT_LIST_HEAD(&sma->pending_alter);
        INIT_LIST_HEAD(&sma->pending_const);
        INIT_LIST_HEAD(&sma->list_id);
@@ -2079,6 +2105,8 @@ void exit_sem(struct task_struct *tsk)
                struct list_head tasks;
                int semid, i;
 
+               cond_resched();
+
                rcu_read_lock();
                un = list_entry_rcu(ulp->list_proc.next,
                                    struct sem_undo, list_proc);
@@ -2184,10 +2212,10 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
        /*
         * The proc interface isn't aware of sem_lock(), it calls
         * ipc_lock_object() directly (in sysvipc_find_ipc).
-        * In order to stay compatible with sem_lock(), we must wait until
-        * all simple semop() calls have left their critical regions.
+        * In order to stay compatible with sem_lock(), we must
+        * enter / leave complex_mode.
         */
-       sem_wait_array(sma);
+       complexmode_enter(sma);
 
        sem_otime = get_semotime(sma);
 
@@ -2204,6 +2232,8 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
                   sem_otime,
                   sma->sem_ctime);
 
+       complexmode_tryleave(sma);
+
        return 0;
 }
 #endif
index 4406615..85bc9be 100644 (file)
@@ -64,6 +64,9 @@
 #include <linux/file.h>
 #include <net/sock.h>
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/cgroup.h>
+
 /*
  * pidlists linger the following amount before being destroyed.  The goal
  * is avoiding frequent destruction in the middle of consecutive read calls
@@ -1176,6 +1179,8 @@ static void cgroup_destroy_root(struct cgroup_root *root)
        struct cgroup *cgrp = &root->cgrp;
        struct cgrp_cset_link *link, *tmp_link;
 
+       trace_cgroup_destroy_root(root);
+
        cgroup_lock_and_drain_offline(&cgrp_dfl_root.cgrp);
 
        BUG_ON(atomic_read(&root->nr_cgrps));
@@ -1874,6 +1879,9 @@ static int cgroup_remount(struct kernfs_root *kf_root, int *flags, char *data)
                strcpy(root->release_agent_path, opts.release_agent);
                spin_unlock(&release_agent_path_lock);
        }
+
+       trace_cgroup_remount(root);
+
  out_unlock:
        kfree(opts.release_agent);
        kfree(opts.name);
@@ -2031,6 +2039,8 @@ static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
        if (ret)
                goto destroy_root;
 
+       trace_cgroup_setup_root(root);
+
        /*
         * There must be no failure case after here, since rebinding takes
         * care of subsystems' refcounts, which are explicitly dropped in
@@ -2315,22 +2325,18 @@ static struct file_system_type cgroup2_fs_type = {
        .fs_flags = FS_USERNS_MOUNT,
 };
 
-static char *cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen,
-                                  struct cgroup_namespace *ns)
+static int cgroup_path_ns_locked(struct cgroup *cgrp, char *buf, size_t buflen,
+                                struct cgroup_namespace *ns)
 {
        struct cgroup *root = cset_cgroup_from_root(ns->root_cset, cgrp->root);
-       int ret;
 
-       ret = kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen);
-       if (ret < 0 || ret >= buflen)
-               return NULL;
-       return buf;
+       return kernfs_path_from_node(cgrp->kn, root->kn, buf, buflen);
 }
 
-char *cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
-                    struct cgroup_namespace *ns)
+int cgroup_path_ns(struct cgroup *cgrp, char *buf, size_t buflen,
+                  struct cgroup_namespace *ns)
 {
-       char *ret;
+       int ret;
 
        mutex_lock(&cgroup_mutex);
        spin_lock_irq(&css_set_lock);
@@ -2357,12 +2363,12 @@ EXPORT_SYMBOL_GPL(cgroup_path_ns);
  *
  * Return value is the same as kernfs_path().
  */
-char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
+int task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
 {
        struct cgroup_root *root;
        struct cgroup *cgrp;
        int hierarchy_id = 1;
-       char *path = NULL;
+       int ret;
 
        mutex_lock(&cgroup_mutex);
        spin_lock_irq(&css_set_lock);
@@ -2371,16 +2377,15 @@ char *task_cgroup_path(struct task_struct *task, char *buf, size_t buflen)
 
        if (root) {
                cgrp = task_cgroup_from_root(task, root);
-               path = cgroup_path_ns_locked(cgrp, buf, buflen, &init_cgroup_ns);
+               ret = cgroup_path_ns_locked(cgrp, buf, buflen, &init_cgroup_ns);
        } else {
                /* if no hierarchy exists, everyone is in "/" */
-               if (strlcpy(buf, "/", buflen) < buflen)
-                       path = buf;
+               ret = strlcpy(buf, "/", buflen);
        }
 
        spin_unlock_irq(&css_set_lock);
        mutex_unlock(&cgroup_mutex);
-       return path;
+       return ret;
 }
 EXPORT_SYMBOL_GPL(task_cgroup_path);
 
@@ -2830,6 +2835,10 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
                ret = cgroup_migrate(leader, threadgroup, dst_cgrp->root);
 
        cgroup_migrate_finish(&preloaded_csets);
+
+       if (!ret)
+               trace_cgroup_attach_task(dst_cgrp, leader, threadgroup);
+
        return ret;
 }
 
@@ -3611,6 +3620,8 @@ static int cgroup_rename(struct kernfs_node *kn, struct kernfs_node *new_parent,
        mutex_lock(&cgroup_mutex);
 
        ret = kernfs_rename(kn, new_parent, new_name_str);
+       if (!ret)
+               trace_cgroup_rename(cgrp);
 
        mutex_unlock(&cgroup_mutex);
 
@@ -4381,6 +4392,8 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
 
                if (task) {
                        ret = cgroup_migrate(task, false, to->root);
+                       if (!ret)
+                               trace_cgroup_transfer_tasks(to, task, false);
                        put_task_struct(task);
                }
        } while (task && !ret);
@@ -5046,6 +5059,8 @@ static void css_release_work_fn(struct work_struct *work)
                        ss->css_released(css);
        } else {
                /* cgroup release path */
+               trace_cgroup_release(cgrp);
+
                cgroup_idr_remove(&cgrp->root->cgroup_idr, cgrp->id);
                cgrp->id = -1;
 
@@ -5332,6 +5347,8 @@ static int cgroup_mkdir(struct kernfs_node *parent_kn, const char *name,
        if (ret)
                goto out_destroy;
 
+       trace_cgroup_mkdir(cgrp);
+
        /* let's create and online css's */
        kernfs_activate(kn);
 
@@ -5507,6 +5524,9 @@ static int cgroup_rmdir(struct kernfs_node *kn)
 
        ret = cgroup_destroy_locked(cgrp);
 
+       if (!ret)
+               trace_cgroup_rmdir(cgrp);
+
        cgroup_kn_unlock(kn);
        return ret;
 }
@@ -5743,7 +5763,7 @@ core_initcall(cgroup_wq_init);
 int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk)
 {
-       char *buf, *path;
+       char *buf;
        int retval;
        struct cgroup_root *root;
 
@@ -5786,18 +5806,18 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns,
                 * " (deleted)" is appended to the cgroup path.
                 */
                if (cgroup_on_dfl(cgrp) || !(tsk->flags & PF_EXITING)) {
-                       path = cgroup_path_ns_locked(cgrp, buf, PATH_MAX,
+                       retval = cgroup_path_ns_locked(cgrp, buf, PATH_MAX,
                                                current->nsproxy->cgroup_ns);
-                       if (!path) {
+                       if (retval >= PATH_MAX)
                                retval = -ENAMETOOLONG;
+                       if (retval < 0)
                                goto out_unlock;
-                       }
+
+                       seq_puts(m, buf);
                } else {
-                       path = "/";
+                       seq_puts(m, "/");
                }
 
-               seq_puts(m, path);
-
                if (cgroup_on_dfl(cgrp) && cgroup_is_dead(cgrp))
                        seq_puts(m, " (deleted)\n");
                else
@@ -6062,8 +6082,9 @@ static void cgroup_release_agent(struct work_struct *work)
 {
        struct cgroup *cgrp =
                container_of(work, struct cgroup, release_agent_work);
-       char *pathbuf = NULL, *agentbuf = NULL, *path;
+       char *pathbuf = NULL, *agentbuf = NULL;
        char *argv[3], *envp[3];
+       int ret;
 
        mutex_lock(&cgroup_mutex);
 
@@ -6073,13 +6094,13 @@ static void cgroup_release_agent(struct work_struct *work)
                goto out;
 
        spin_lock_irq(&css_set_lock);
-       path = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
+       ret = cgroup_path_ns_locked(cgrp, pathbuf, PATH_MAX, &init_cgroup_ns);
        spin_unlock_irq(&css_set_lock);
-       if (!path)
+       if (ret < 0 || ret >= PATH_MAX)
                goto out;
 
        argv[0] = agentbuf;
-       argv[1] = path;
+       argv[1] = pathbuf;
        argv[2] = NULL;
 
        /* minimal command environment */
index 9f748ed..1a8f34f 100644 (file)
@@ -11,7 +11,6 @@ CONFIG_ANDROID_LOW_MEMORY_KILLER=y
 CONFIG_ARMV8_DEPRECATED=y
 CONFIG_ASHMEM=y
 CONFIG_AUDIT=y
-CONFIG_BLK_DEV_DM=y
 CONFIG_BLK_DEV_INITRD=y
 CONFIG_CGROUPS=y
 CONFIG_CGROUP_CPUACCT=y
@@ -19,9 +18,7 @@ CONFIG_CGROUP_DEBUG=y
 CONFIG_CGROUP_FREEZER=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_CP15_BARRIER_EMULATION=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_VERITY=y
-CONFIG_DM_VERITY_FEC=y
+CONFIG_DEFAULT_SECURITY_SELINUX=y
 CONFIG_EMBEDDED=y
 CONFIG_FB=y
 CONFIG_HIGH_RES_TIMERS=y
@@ -41,7 +38,6 @@ CONFIG_IPV6=y
 CONFIG_IPV6_MIP6=y
 CONFIG_IPV6_MULTIPLE_TABLES=y
 CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_IPV6_PRIVACY=y
 CONFIG_IPV6_ROUTER_PREF=y
 CONFIG_IPV6_ROUTE_INFO=y
 CONFIG_IP_ADVANCED_ROUTER=y
@@ -135,6 +131,7 @@ CONFIG_PREEMPT=y
 CONFIG_QUOTA=y
 CONFIG_RTC_CLASS=y
 CONFIG_RT_GROUP_SCHED=y
+CONFIG_SECCOMP=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_NETWORK=y
 CONFIG_SECURITY_SELINUX=y
index e3b953e..297756b 100644 (file)
@@ -6,12 +6,16 @@
 # CONFIG_PM_WAKELOCKS_GC is not set
 # CONFIG_VT is not set
 CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BLK_DEV_DM=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=8192
 CONFIG_COMPACTION=y
 CONFIG_DEBUG_RODATA=y
+CONFIG_DM_CRYPT=y
 CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
 CONFIG_DRAGONRISE_FF=y
 CONFIG_ENABLE_DEFAULT_TRACERS=y
 CONFIG_EXT4_FS=y
index 2b4c20a..29f815d 100644 (file)
@@ -2715,7 +2715,7 @@ void __cpuset_memory_pressure_bump(void)
 int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
                     struct pid *pid, struct task_struct *tsk)
 {
-       char *buf, *p;
+       char *buf;
        struct cgroup_subsys_state *css;
        int retval;
 
@@ -2724,14 +2724,15 @@ int proc_cpuset_show(struct seq_file *m, struct pid_namespace *ns,
        if (!buf)
                goto out;
 
-       retval = -ENAMETOOLONG;
        css = task_get_css(tsk, cpuset_cgrp_id);
-       p = cgroup_path_ns(css->cgroup, buf, PATH_MAX,
-                          current->nsproxy->cgroup_ns);
+       retval = cgroup_path_ns(css->cgroup, buf, PATH_MAX,
+                               current->nsproxy->cgroup_ns);
        css_put(css);
-       if (!p)
+       if (retval >= PATH_MAX)
+               retval = -ENAMETOOLONG;
+       if (retval < 0)
                goto out_free;
-       seq_puts(m, p);
+       seq_puts(m, buf);
        seq_putc(m, '\n');
        retval = 0;
 out_free:
index 432c3d7..2b59c82 100644 (file)
@@ -98,26 +98,26 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)
 
        trace_sched_process_hang(t);
 
-       if (!sysctl_hung_task_warnings)
+       if (!sysctl_hung_task_warnings && !sysctl_hung_task_panic)
                return;
 
-       if (sysctl_hung_task_warnings > 0)
-               sysctl_hung_task_warnings--;
-
        /*
         * Ok, the task did not get scheduled for more than 2 minutes,
         * complain:
         */
-       pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n",
-               t->comm, t->pid, timeout);
-       pr_err("      %s %s %.*s\n",
-               print_tainted(), init_utsname()->release,
-               (int)strcspn(init_utsname()->version, " "),
-               init_utsname()->version);
-       pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
-               " disables this message.\n");
-       sched_show_task(t);
-       debug_show_all_locks();
+       if (sysctl_hung_task_warnings) {
+               sysctl_hung_task_warnings--;
+               pr_err("INFO: task %s:%d blocked for more than %ld seconds.\n",
+                       t->comm, t->pid, timeout);
+               pr_err("      %s %s %.*s\n",
+                       print_tainted(), init_utsname()->release,
+                       (int)strcspn(init_utsname()->version, " "),
+                       init_utsname()->version);
+               pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
+                       " disables this message.\n");
+               sched_show_task(t);
+               debug_show_all_locks();
+       }
 
        touch_nmi_watchdog();
 
index d10ab6b..d630954 100644 (file)
@@ -49,7 +49,7 @@
 #include <linux/cpu.h>
 #include <linux/jump_label.h>
 
-#include <asm-generic/sections.h>
+#include <asm/sections.h>
 #include <asm/cacheflush.h>
 #include <asm/errno.h>
 #include <asm/uaccess.h>
index 4ab4c37..be2cc1f 100644 (file)
@@ -138,7 +138,7 @@ void *kthread_data(struct task_struct *task)
 }
 
 /**
- * probe_kthread_data - speculative version of kthread_data()
+ * kthread_probe_data - speculative version of kthread_data()
  * @task: possible kthread task in question
  *
  * @task could be a kthread task.  Return the data value specified when it
@@ -146,7 +146,7 @@ void *kthread_data(struct task_struct *task)
  * inaccessible for any reason, %NULL is returned.  This function requires
  * that @task itself is safe to dereference.
  */
-void *probe_kthread_data(struct task_struct *task)
+void *kthread_probe_data(struct task_struct *task)
 {
        struct kthread *kthread = to_kthread(task);
        void *data = NULL;
@@ -244,33 +244,10 @@ static void create_kthread(struct kthread_create_info *create)
        }
 }
 
-/**
- * kthread_create_on_node - create a kthread.
- * @threadfn: the function to run until signal_pending(current).
- * @data: data ptr for @threadfn.
- * @node: task and thread structures for the thread are allocated on this node
- * @namefmt: printf-style name for the thread.
- *
- * Description: This helper function creates and names a kernel
- * thread.  The thread will be stopped: use wake_up_process() to start
- * it.  See also kthread_run().  The new thread has SCHED_NORMAL policy and
- * is affine to all CPUs.
- *
- * If thread is going to be bound on a particular cpu, give its node
- * in @node, to get NUMA affinity for kthread stack, or else give NUMA_NO_NODE.
- * When woken, the thread will run @threadfn() with @data as its
- * argument. @threadfn() can either call do_exit() directly if it is a
- * standalone thread for which no one will call kthread_stop(), or
- * return when 'kthread_should_stop()' is true (which means
- * kthread_stop() has been called).  The return value should be zero
- * or a negative error number; it will be passed to kthread_stop().
- *
- * Returns a task_struct or ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR).
- */
-struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
-                                          void *data, int node,
-                                          const char namefmt[],
-                                          ...)
+static struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
+                                                   void *data, int node,
+                                                   const char namefmt[],
+                                                   va_list args)
 {
        DECLARE_COMPLETION_ONSTACK(done);
        struct task_struct *task;
@@ -311,11 +288,8 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
        task = create->result;
        if (!IS_ERR(task)) {
                static const struct sched_param param = { .sched_priority = 0 };
-               va_list args;
 
-               va_start(args, namefmt);
                vsnprintf(task->comm, sizeof(task->comm), namefmt, args);
-               va_end(args);
                /*
                 * root may have changed our (kthreadd's) priority or CPU mask.
                 * The kernel thread should not inherit these properties.
@@ -326,6 +300,44 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
        kfree(create);
        return task;
 }
+
+/**
+ * kthread_create_on_node - create a kthread.
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @node: task and thread structures for the thread are allocated on this node
+ * @namefmt: printf-style name for the thread.
+ *
+ * Description: This helper function creates and names a kernel
+ * thread.  The thread will be stopped: use wake_up_process() to start
+ * it.  See also kthread_run().  The new thread has SCHED_NORMAL policy and
+ * is affine to all CPUs.
+ *
+ * If thread is going to be bound on a particular cpu, give its node
+ * in @node, to get NUMA affinity for kthread stack, or else give NUMA_NO_NODE.
+ * When woken, the thread will run @threadfn() with @data as its
+ * argument. @threadfn() can either call do_exit() directly if it is a
+ * standalone thread for which no one will call kthread_stop(), or
+ * return when 'kthread_should_stop()' is true (which means
+ * kthread_stop() has been called).  The return value should be zero
+ * or a negative error number; it will be passed to kthread_stop().
+ *
+ * Returns a task_struct or ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR).
+ */
+struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
+                                          void *data, int node,
+                                          const char namefmt[],
+                                          ...)
+{
+       struct task_struct *task;
+       va_list args;
+
+       va_start(args, namefmt);
+       task = __kthread_create_on_node(threadfn, data, node, namefmt, args);
+       va_end(args);
+
+       return task;
+}
 EXPORT_SYMBOL(kthread_create_on_node);
 
 static void __kthread_bind_mask(struct task_struct *p, const struct cpumask *mask, long state)
@@ -390,10 +402,10 @@ struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
                                   cpu);
        if (IS_ERR(p))
                return p;
+       kthread_bind(p, cpu);
+       /* CPU hotplug need to bind once again when unparking the thread. */
        set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags);
        to_kthread(p)->cpu = cpu;
-       /* Park the thread to get it out of TASK_UNINTERRUPTIBLE state */
-       kthread_park(p);
        return p;
 }
 
@@ -407,6 +419,10 @@ static void __kthread_unpark(struct task_struct *k, struct kthread *kthread)
         * which might be about to be cleared.
         */
        if (test_and_clear_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
+               /*
+                * Newly created kthread was parked when the CPU was offline.
+                * The binding was lost and we need to set it again.
+                */
                if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))
                        __kthread_bind(k, kthread->cpu, TASK_PARKED);
                wake_up_state(k, TASK_PARKED);
@@ -540,39 +556,48 @@ int kthreadd(void *unused)
        return 0;
 }
 
-void __init_kthread_worker(struct kthread_worker *worker,
+void __kthread_init_worker(struct kthread_worker *worker,
                                const char *name,
                                struct lock_class_key *key)
 {
+       memset(worker, 0, sizeof(struct kthread_worker));
        spin_lock_init(&worker->lock);
        lockdep_set_class_and_name(&worker->lock, key, name);
        INIT_LIST_HEAD(&worker->work_list);
-       worker->task = NULL;
+       INIT_LIST_HEAD(&worker->delayed_work_list);
 }
-EXPORT_SYMBOL_GPL(__init_kthread_worker);
+EXPORT_SYMBOL_GPL(__kthread_init_worker);
 
 /**
  * kthread_worker_fn - kthread function to process kthread_worker
  * @worker_ptr: pointer to initialized kthread_worker
  *
- * This function can be used as @threadfn to kthread_create() or
- * kthread_run() with @worker_ptr argument pointing to an initialized
- * kthread_worker.  The started kthread will process work_list until
- * the it is stopped with kthread_stop().  A kthread can also call
- * this function directly after extra initialization.
+ * This function implements the main cycle of kthread worker. It processes
+ * work_list until it is stopped with kthread_stop(). It sleeps when the queue
+ * is empty.
+ *
+ * The works are not allowed to keep any locks, disable preemption or interrupts
+ * when they finish. There is defined a safe point for freezing when one work
+ * finishes and before a new one is started.
  *
- * Different kthreads can be used for the same kthread_worker as long
- * as there's only one kthread attached to it at any given time.  A
- * kthread_worker without an attached kthread simply collects queued
- * kthread_works.
+ * Also the works must not be handled by more than one worker at the same time,
+ * see also kthread_queue_work().
  */
 int kthread_worker_fn(void *worker_ptr)
 {
        struct kthread_worker *worker = worker_ptr;
        struct kthread_work *work;
 
-       WARN_ON(worker->task);
+       /*
+        * FIXME: Update the check and remove the assignment when all kthread
+        * worker users are created using kthread_create_worker*() functions.
+        */
+       WARN_ON(worker->task && worker->task != current);
        worker->task = current;
+
+       if (worker->flags & KTW_FREEZABLE)
+               set_freezable();
+
 repeat:
        set_current_state(TASK_INTERRUPTIBLE);  /* mb paired w/ kthread_stop */
 
@@ -605,12 +630,131 @@ repeat:
 }
 EXPORT_SYMBOL_GPL(kthread_worker_fn);
 
-/* insert @work before @pos in @worker */
-static void insert_kthread_work(struct kthread_worker *worker,
-                              struct kthread_work *work,
-                              struct list_head *pos)
+static struct kthread_worker *
+__kthread_create_worker(int cpu, unsigned int flags,
+                       const char namefmt[], va_list args)
+{
+       struct kthread_worker *worker;
+       struct task_struct *task;
+
+       worker = kzalloc(sizeof(*worker), GFP_KERNEL);
+       if (!worker)
+               return ERR_PTR(-ENOMEM);
+
+       kthread_init_worker(worker);
+
+       if (cpu >= 0) {
+               char name[TASK_COMM_LEN];
+
+               /*
+                * kthread_create_worker_on_cpu() allows to pass a generic
+                * namefmt in compare with kthread_create_on_cpu. We need
+                * to format it here.
+                */
+               vsnprintf(name, sizeof(name), namefmt, args);
+               task = kthread_create_on_cpu(kthread_worker_fn, worker,
+                                            cpu, name);
+       } else {
+               task = __kthread_create_on_node(kthread_worker_fn, worker,
+                                               -1, namefmt, args);
+       }
+
+       if (IS_ERR(task))
+               goto fail_task;
+
+       worker->flags = flags;
+       worker->task = task;
+       wake_up_process(task);
+       return worker;
+
+fail_task:
+       kfree(worker);
+       return ERR_CAST(task);
+}
+
+/**
+ * kthread_create_worker - create a kthread worker
+ * @flags: flags modifying the default behavior of the worker
+ * @namefmt: printf-style name for the kthread worker (task).
+ *
+ * Returns a pointer to the allocated worker on success, ERR_PTR(-ENOMEM)
+ * when the needed structures could not get allocated, and ERR_PTR(-EINTR)
+ * when the worker was SIGKILLed.
+ */
+struct kthread_worker *
+kthread_create_worker(unsigned int flags, const char namefmt[], ...)
+{
+       struct kthread_worker *worker;
+       va_list args;
+
+       va_start(args, namefmt);
+       worker = __kthread_create_worker(-1, flags, namefmt, args);
+       va_end(args);
+
+       return worker;
+}
+EXPORT_SYMBOL(kthread_create_worker);
+
+/**
+ * kthread_create_worker_on_cpu - create a kthread worker and bind it
+ *     it to a given CPU and the associated NUMA node.
+ * @cpu: CPU number
+ * @flags: flags modifying the default behavior of the worker
+ * @namefmt: printf-style name for the kthread worker (task).
+ *
+ * Use a valid CPU number if you want to bind the kthread worker
+ * to the given CPU and the associated NUMA node.
+ *
+ * A good practice is to add the cpu number also into the worker name.
+ * For example, use kthread_create_worker_on_cpu(cpu, "helper/%d", cpu).
+ *
+ * Returns a pointer to the allocated worker on success, ERR_PTR(-ENOMEM)
+ * when the needed structures could not get allocated, and ERR_PTR(-EINTR)
+ * when the worker was SIGKILLed.
+ */
+struct kthread_worker *
+kthread_create_worker_on_cpu(int cpu, unsigned int flags,
+                            const char namefmt[], ...)
+{
+       struct kthread_worker *worker;
+       va_list args;
+
+       va_start(args, namefmt);
+       worker = __kthread_create_worker(cpu, flags, namefmt, args);
+       va_end(args);
+
+       return worker;
+}
+EXPORT_SYMBOL(kthread_create_worker_on_cpu);
+
+/*
+ * Returns true when the work could not be queued at the moment.
+ * It happens when it is already pending in a worker list
+ * or when it is being cancelled.
+ */
+static inline bool queuing_blocked(struct kthread_worker *worker,
+                                  struct kthread_work *work)
+{
+       lockdep_assert_held(&worker->lock);
+
+       return !list_empty(&work->node) || work->canceling;
+}
+
+static void kthread_insert_work_sanity_check(struct kthread_worker *worker,
+                                            struct kthread_work *work)
 {
        lockdep_assert_held(&worker->lock);
+       WARN_ON_ONCE(!list_empty(&work->node));
+       /* Do not use a work with >1 worker, see kthread_queue_work() */
+       WARN_ON_ONCE(work->worker && work->worker != worker);
+}
+
+/* insert @work before @pos in @worker */
+static void kthread_insert_work(struct kthread_worker *worker,
+                               struct kthread_work *work,
+                               struct list_head *pos)
+{
+       kthread_insert_work_sanity_check(worker, work);
 
        list_add_tail(&work->node, pos);
        work->worker = worker;
@@ -619,29 +763,133 @@ static void insert_kthread_work(struct kthread_worker *worker,
 }
 
 /**
- * queue_kthread_work - queue a kthread_work
+ * kthread_queue_work - queue a kthread_work
  * @worker: target kthread_worker
  * @work: kthread_work to queue
  *
  * Queue @work to work processor @task for async execution.  @task
  * must have been created with kthread_worker_create().  Returns %true
  * if @work was successfully queued, %false if it was already pending.
+ *
+ * Reinitialize the work if it needs to be used by another worker.
+ * For example, when the worker was stopped and started again.
  */
-bool queue_kthread_work(struct kthread_worker *worker,
+bool kthread_queue_work(struct kthread_worker *worker,
                        struct kthread_work *work)
 {
        bool ret = false;
        unsigned long flags;
 
        spin_lock_irqsave(&worker->lock, flags);
-       if (list_empty(&work->node)) {
-               insert_kthread_work(worker, work, &worker->work_list);
+       if (!queuing_blocked(worker, work)) {
+               kthread_insert_work(worker, work, &worker->work_list);
+               ret = true;
+       }
+       spin_unlock_irqrestore(&worker->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(kthread_queue_work);
+
+/**
+ * kthread_delayed_work_timer_fn - callback that queues the associated kthread
+ *     delayed work when the timer expires.
+ * @__data: pointer to the data associated with the timer
+ *
+ * The format of the function is defined by struct timer_list.
+ * It should have been called from irqsafe timer with irq already off.
+ */
+void kthread_delayed_work_timer_fn(unsigned long __data)
+{
+       struct kthread_delayed_work *dwork =
+               (struct kthread_delayed_work *)__data;
+       struct kthread_work *work = &dwork->work;
+       struct kthread_worker *worker = work->worker;
+
+       /*
+        * This might happen when a pending work is reinitialized.
+        * It means that it is used a wrong way.
+        */
+       if (WARN_ON_ONCE(!worker))
+               return;
+
+       spin_lock(&worker->lock);
+       /* Work must not be used with >1 worker, see kthread_queue_work(). */
+       WARN_ON_ONCE(work->worker != worker);
+
+       /* Move the work from worker->delayed_work_list. */
+       WARN_ON_ONCE(list_empty(&work->node));
+       list_del_init(&work->node);
+       kthread_insert_work(worker, work, &worker->work_list);
+
+       spin_unlock(&worker->lock);
+}
+EXPORT_SYMBOL(kthread_delayed_work_timer_fn);
+
+void __kthread_queue_delayed_work(struct kthread_worker *worker,
+                                 struct kthread_delayed_work *dwork,
+                                 unsigned long delay)
+{
+       struct timer_list *timer = &dwork->timer;
+       struct kthread_work *work = &dwork->work;
+
+       WARN_ON_ONCE(timer->function != kthread_delayed_work_timer_fn ||
+                    timer->data != (unsigned long)dwork);
+
+       /*
+        * If @delay is 0, queue @dwork->work immediately.  This is for
+        * both optimization and correctness.  The earliest @timer can
+        * expire is on the closest next tick and delayed_work users depend
+        * on that there's no such delay when @delay is 0.
+        */
+       if (!delay) {
+               kthread_insert_work(worker, work, &worker->work_list);
+               return;
+       }
+
+       /* Be paranoid and try to detect possible races already now. */
+       kthread_insert_work_sanity_check(worker, work);
+
+       list_add(&work->node, &worker->delayed_work_list);
+       work->worker = worker;
+       timer_stats_timer_set_start_info(&dwork->timer);
+       timer->expires = jiffies + delay;
+       add_timer(timer);
+}
+
+/**
+ * kthread_queue_delayed_work - queue the associated kthread work
+ *     after a delay.
+ * @worker: target kthread_worker
+ * @dwork: kthread_delayed_work to queue
+ * @delay: number of jiffies to wait before queuing
+ *
+ * If the work has not been pending it starts a timer that will queue
+ * the work after the given @delay. If @delay is zero, it queues the
+ * work immediately.
+ *
+ * Return: %false if the @work has already been pending. It means that
+ * either the timer was running or the work was queued. It returns %true
+ * otherwise.
+ */
+bool kthread_queue_delayed_work(struct kthread_worker *worker,
+                               struct kthread_delayed_work *dwork,
+                               unsigned long delay)
+{
+       struct kthread_work *work = &dwork->work;
+       unsigned long flags;
+       bool ret = false;
+
+       spin_lock_irqsave(&worker->lock, flags);
+
+       if (!queuing_blocked(worker, work)) {
+               __kthread_queue_delayed_work(worker, dwork, delay);
                ret = true;
        }
+
        spin_unlock_irqrestore(&worker->lock, flags);
        return ret;
 }
-EXPORT_SYMBOL_GPL(queue_kthread_work);
+EXPORT_SYMBOL_GPL(kthread_queue_delayed_work);
 
 struct kthread_flush_work {
        struct kthread_work     work;
@@ -656,12 +904,12 @@ static void kthread_flush_work_fn(struct kthread_work *work)
 }
 
 /**
- * flush_kthread_work - flush a kthread_work
+ * kthread_flush_work - flush a kthread_work
  * @work: work to flush
  *
  * If @work is queued or executing, wait for it to finish execution.
  */
-void flush_kthread_work(struct kthread_work *work)
+void kthread_flush_work(struct kthread_work *work)
 {
        struct kthread_flush_work fwork = {
                KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
@@ -670,21 +918,19 @@ void flush_kthread_work(struct kthread_work *work)
        struct kthread_worker *worker;
        bool noop = false;
 
-retry:
        worker = work->worker;
        if (!worker)
                return;
 
        spin_lock_irq(&worker->lock);
-       if (work->worker != worker) {
-               spin_unlock_irq(&worker->lock);
-               goto retry;
-       }
+       /* Work must not be used with >1 worker, see kthread_queue_work(). */
+       WARN_ON_ONCE(work->worker != worker);
 
        if (!list_empty(&work->node))
-               insert_kthread_work(worker, &fwork.work, work->node.next);
+               kthread_insert_work(worker, &fwork.work, work->node.next);
        else if (worker->current_work == work)
-               insert_kthread_work(worker, &fwork.work, worker->work_list.next);
+               kthread_insert_work(worker, &fwork.work,
+                                   worker->work_list.next);
        else
                noop = true;
 
@@ -693,23 +939,214 @@ retry:
        if (!noop)
                wait_for_completion(&fwork.done);
 }
-EXPORT_SYMBOL_GPL(flush_kthread_work);
+EXPORT_SYMBOL_GPL(kthread_flush_work);
+
+/*
+ * This function removes the work from the worker queue. Also it makes sure
+ * that it won't get queued later via the delayed work's timer.
+ *
+ * The work might still be in use when this function finishes. See the
+ * current_work proceed by the worker.
+ *
+ * Return: %true if @work was pending and successfully canceled,
+ *     %false if @work was not pending
+ */
+static bool __kthread_cancel_work(struct kthread_work *work, bool is_dwork,
+                                 unsigned long *flags)
+{
+       /* Try to cancel the timer if exists. */
+       if (is_dwork) {
+               struct kthread_delayed_work *dwork =
+                       container_of(work, struct kthread_delayed_work, work);
+               struct kthread_worker *worker = work->worker;
+
+               /*
+                * del_timer_sync() must be called to make sure that the timer
+                * callback is not running. The lock must be temporary released
+                * to avoid a deadlock with the callback. In the meantime,
+                * any queuing is blocked by setting the canceling counter.
+                */
+               work->canceling++;
+               spin_unlock_irqrestore(&worker->lock, *flags);
+               del_timer_sync(&dwork->timer);
+               spin_lock_irqsave(&worker->lock, *flags);
+               work->canceling--;
+       }
+
+       /*
+        * Try to remove the work from a worker list. It might either
+        * be from worker->work_list or from worker->delayed_work_list.
+        */
+       if (!list_empty(&work->node)) {
+               list_del_init(&work->node);
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * kthread_mod_delayed_work - modify delay of or queue a kthread delayed work
+ * @worker: kthread worker to use
+ * @dwork: kthread delayed work to queue
+ * @delay: number of jiffies to wait before queuing
+ *
+ * If @dwork is idle, equivalent to kthread_queue_delayed_work(). Otherwise,
+ * modify @dwork's timer so that it expires after @delay. If @delay is zero,
+ * @work is guaranteed to be queued immediately.
+ *
+ * Return: %true if @dwork was pending and its timer was modified,
+ * %false otherwise.
+ *
+ * A special case is when the work is being canceled in parallel.
+ * It might be caused either by the real kthread_cancel_delayed_work_sync()
+ * or yet another kthread_mod_delayed_work() call. We let the other command
+ * win and return %false here. The caller is supposed to synchronize these
+ * operations a reasonable way.
+ *
+ * This function is safe to call from any context including IRQ handler.
+ * See __kthread_cancel_work() and kthread_delayed_work_timer_fn()
+ * for details.
+ */
+bool kthread_mod_delayed_work(struct kthread_worker *worker,
+                             struct kthread_delayed_work *dwork,
+                             unsigned long delay)
+{
+       struct kthread_work *work = &dwork->work;
+       unsigned long flags;
+       int ret = false;
+
+       spin_lock_irqsave(&worker->lock, flags);
+
+       /* Do not bother with canceling when never queued. */
+       if (!work->worker)
+               goto fast_queue;
+
+       /* Work must not be used with >1 worker, see kthread_queue_work() */
+       WARN_ON_ONCE(work->worker != worker);
+
+       /* Do not fight with another command that is canceling this work. */
+       if (work->canceling)
+               goto out;
+
+       ret = __kthread_cancel_work(work, true, &flags);
+fast_queue:
+       __kthread_queue_delayed_work(worker, dwork, delay);
+out:
+       spin_unlock_irqrestore(&worker->lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(kthread_mod_delayed_work);
+
+static bool __kthread_cancel_work_sync(struct kthread_work *work, bool is_dwork)
+{
+       struct kthread_worker *worker = work->worker;
+       unsigned long flags;
+       int ret = false;
+
+       if (!worker)
+               goto out;
+
+       spin_lock_irqsave(&worker->lock, flags);
+       /* Work must not be used with >1 worker, see kthread_queue_work(). */
+       WARN_ON_ONCE(work->worker != worker);
+
+       ret = __kthread_cancel_work(work, is_dwork, &flags);
+
+       if (worker->current_work != work)
+               goto out_fast;
+
+       /*
+        * The work is in progress and we need to wait with the lock released.
+        * In the meantime, block any queuing by setting the canceling counter.
+        */
+       work->canceling++;
+       spin_unlock_irqrestore(&worker->lock, flags);
+       kthread_flush_work(work);
+       spin_lock_irqsave(&worker->lock, flags);
+       work->canceling--;
+
+out_fast:
+       spin_unlock_irqrestore(&worker->lock, flags);
+out:
+       return ret;
+}
+
+/**
+ * kthread_cancel_work_sync - cancel a kthread work and wait for it to finish
+ * @work: the kthread work to cancel
+ *
+ * Cancel @work and wait for its execution to finish.  This function
+ * can be used even if the work re-queues itself. On return from this
+ * function, @work is guaranteed to be not pending or executing on any CPU.
+ *
+ * kthread_cancel_work_sync(&delayed_work->work) must not be used for
+ * delayed_work's. Use kthread_cancel_delayed_work_sync() instead.
+ *
+ * The caller must ensure that the worker on which @work was last
+ * queued can't be destroyed before this function returns.
+ *
+ * Return: %true if @work was pending, %false otherwise.
+ */
+bool kthread_cancel_work_sync(struct kthread_work *work)
+{
+       return __kthread_cancel_work_sync(work, false);
+}
+EXPORT_SYMBOL_GPL(kthread_cancel_work_sync);
+
+/**
+ * kthread_cancel_delayed_work_sync - cancel a kthread delayed work and
+ *     wait for it to finish.
+ * @dwork: the kthread delayed work to cancel
+ *
+ * This is kthread_cancel_work_sync() for delayed works.
+ *
+ * Return: %true if @dwork was pending, %false otherwise.
+ */
+bool kthread_cancel_delayed_work_sync(struct kthread_delayed_work *dwork)
+{
+       return __kthread_cancel_work_sync(&dwork->work, true);
+}
+EXPORT_SYMBOL_GPL(kthread_cancel_delayed_work_sync);
 
 /**
- * flush_kthread_worker - flush all current works on a kthread_worker
+ * kthread_flush_worker - flush all current works on a kthread_worker
  * @worker: worker to flush
  *
  * Wait until all currently executing or pending works on @worker are
  * finished.
  */
-void flush_kthread_worker(struct kthread_worker *worker)
+void kthread_flush_worker(struct kthread_worker *worker)
 {
        struct kthread_flush_work fwork = {
                KTHREAD_WORK_INIT(fwork.work, kthread_flush_work_fn),
                COMPLETION_INITIALIZER_ONSTACK(fwork.done),
        };
 
-       queue_kthread_work(worker, &fwork.work);
+       kthread_queue_work(worker, &fwork.work);
        wait_for_completion(&fwork.done);
 }
-EXPORT_SYMBOL_GPL(flush_kthread_worker);
+EXPORT_SYMBOL_GPL(kthread_flush_worker);
+
+/**
+ * kthread_destroy_worker - destroy a kthread worker
+ * @worker: worker to be destroyed
+ *
+ * Flush and destroy @worker.  The simple flush is enough because the kthread
+ * worker API is used only in trivial scenarios.  There are no multi-step state
+ * machines needed.
+ */
+void kthread_destroy_worker(struct kthread_worker *worker)
+{
+       struct task_struct *task;
+
+       task = worker->task;
+       if (WARN_ON(!task))
+               return;
+
+       kthread_flush_worker(worker);
+       kthread_stop(task);
+       WARN_ON(!list_empty(&worker->work_list));
+       kfree(worker);
+}
+EXPORT_SYMBOL(kthread_destroy_worker);
index ca8cea1..e6480e2 100644 (file)
@@ -71,6 +71,32 @@ void __weak nmi_panic_self_stop(struct pt_regs *regs)
        panic_smp_self_stop();
 }
 
+/*
+ * Stop other CPUs in panic.  Architecture dependent code may override this
+ * with more suitable version.  For example, if the architecture supports
+ * crash dump, it should save registers of each stopped CPU and disable
+ * per-CPU features such as virtualization extensions.
+ */
+void __weak crash_smp_send_stop(void)
+{
+       static int cpus_stopped;
+
+       /*
+        * This function can be called twice in panic path, but obviously
+        * we execute this only once.
+        */
+       if (cpus_stopped)
+               return;
+
+       /*
+        * Note smp_send_stop is the usual smp shutdown function, which
+        * unfortunately means it may not be hardened to work in a panic
+        * situation.
+        */
+       smp_send_stop();
+       cpus_stopped = 1;
+}
+
 atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID);
 
 /*
@@ -164,14 +190,21 @@ void panic(const char *fmt, ...)
        if (!_crash_kexec_post_notifiers) {
                printk_nmi_flush_on_panic();
                __crash_kexec(NULL);
-       }
 
-       /*
-        * Note smp_send_stop is the usual smp shutdown function, which
-        * unfortunately means it may not be hardened to work in a panic
-        * situation.
-        */
-       smp_send_stop();
+               /*
+                * Note smp_send_stop is the usual smp shutdown function, which
+                * unfortunately means it may not be hardened to work in a
+                * panic situation.
+                */
+               smp_send_stop();
+       } else {
+               /*
+                * If we want to do crash dump after notifier calls and
+                * kmsg_dump, we will need architecture dependent extra
+                * works in addition to stopping other CPUs.
+                */
+               crash_smp_send_stop();
+       }
 
        /*
         * Run any panic handlers, including those that might need to
index 1d3b766..2a99027 100644 (file)
@@ -73,6 +73,8 @@ void __ptrace_unlink(struct task_struct *child)
 {
        BUG_ON(!child->ptrace);
 
+       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+
        child->parent = child->real_parent;
        list_del_init(&child->ptrace_entry);
 
@@ -489,7 +491,6 @@ static int ptrace_detach(struct task_struct *child, unsigned int data)
 
        /* Architecture-specific hardware disable .. */
        ptrace_disable(child);
-       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 
        write_lock_irq(&tasklist_lock);
        /*
index 9988f5c..da79a10 100644 (file)
@@ -328,13 +328,15 @@ static struct rchan_callbacks default_channel_callbacks = {
 
 /**
  *     wakeup_readers - wake up readers waiting on a channel
- *     @data: contains the channel buffer
+ *     @work: contains the channel buffer
  *
- *     This is the timer function used to defer reader waking.
+ *     This is the function used to defer reader waking
  */
-static void wakeup_readers(unsigned long data)
+static void wakeup_readers(struct irq_work *work)
 {
-       struct rchan_buf *buf = (struct rchan_buf *)data;
+       struct rchan_buf *buf;
+
+       buf = container_of(work, struct rchan_buf, wakeup_work);
        wake_up_interruptible(&buf->read_wait);
 }
 
@@ -352,9 +354,10 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init)
        if (init) {
                init_waitqueue_head(&buf->read_wait);
                kref_init(&buf->kref);
-               setup_timer(&buf->timer, wakeup_readers, (unsigned long)buf);
-       } else
-               del_timer_sync(&buf->timer);
+               init_irq_work(&buf->wakeup_work, wakeup_readers);
+       } else {
+               irq_work_sync(&buf->wakeup_work);
+       }
 
        buf->subbufs_produced = 0;
        buf->subbufs_consumed = 0;
@@ -487,7 +490,7 @@ free_buf:
 static void relay_close_buf(struct rchan_buf *buf)
 {
        buf->finalized = 1;
-       del_timer_sync(&buf->timer);
+       irq_work_sync(&buf->wakeup_work);
        buf->chan->cb->remove_buf_file(buf->dentry);
        kref_put(&buf->kref, relay_remove_buf);
 }
@@ -754,14 +757,15 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
                        buf->early_bytes += buf->chan->subbuf_size -
                                            buf->padding[old_subbuf];
                smp_mb();
-               if (waitqueue_active(&buf->read_wait))
+               if (waitqueue_active(&buf->read_wait)) {
                        /*
                         * Calling wake_up_interruptible() from here
                         * will deadlock if we happen to be logging
                         * from the scheduler (trying to re-grab
                         * rq->lock), so defer it.
                         */
-                       mod_timer(&buf->timer, jiffies + 1);
+                       irq_work_queue(&buf->wakeup_work);
+               }
        }
 
        old = buf->data;
index 1393588..fa178b6 100644 (file)
@@ -415,7 +415,8 @@ static char *task_group_path(struct task_group *tg)
        if (autogroup_path(tg, group_path, PATH_MAX))
                return group_path;
 
-       return cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
+       cgroup_path(tg->css.cgroup, group_path, PATH_MAX);
+       return group_path;
 }
 #endif
 
index 13bc43d..4a5c6e7 100644 (file)
@@ -186,6 +186,11 @@ __smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
                kfree(td);
                return PTR_ERR(tsk);
        }
+       /*
+        * Park the thread so that it could start right on the CPU
+        * when it is available.
+        */
+       kthread_park(tsk);
        get_task_struct(tsk);
        *per_cpu_ptr(ht->store, cpu) = tsk;
        if (ht->create) {
index 992ab9d..e579808 100644 (file)
@@ -1,8 +1,4 @@
 
-# We are fully aware of the dangers of __builtin_return_address()
-FRAME_CFLAGS := $(call cc-disable-warning,frame-address)
-KBUILD_CFLAGS += $(FRAME_CFLAGS)
-
 # Do not instrument the tracer itself:
 
 ifdef CONFIG_FUNCTION_TRACER
index bd81f03..479d840 100644 (file)
@@ -4261,7 +4261,7 @@ void print_worker_info(const char *log_lvl, struct task_struct *task)
         * This function is called without any synchronization and @task
         * could be in any state.  Be careful with dereferences.
         */
-       worker = probe_kthread_data(task);
+       worker = kthread_probe_data(task);
 
        /*
         * Carefully copy the associated workqueue's workfn and name.  Keep
index 39d07e7..33bc56c 100644 (file)
@@ -1857,15 +1857,6 @@ config PROVIDE_OHCI1394_DMA_INIT
 
          See Documentation/debugging-via-ohci1394.txt for more information.
 
-config BUILD_DOCSRC
-       bool "Build targets in Documentation/ tree"
-       depends on HEADERS_CHECK
-       help
-         This option attempts to build objects from the source files in the
-         kernel Documentation/ tree.
-
-         Say N if you are unsure.
-
 config DMA_API_DEBUG
        bool "Enable debugging of DMA-API usage"
        depends on HAVE_DMA_API_DEBUG
index f3ca8c0..50144a3 100644 (file)
@@ -180,6 +180,7 @@ obj-$(CONFIG_IRQ_POLL) += irq_poll.o
 
 obj-$(CONFIG_STACKDEPOT) += stackdepot.o
 KASAN_SANITIZE_stackdepot.o := n
+KCOV_INSTRUMENT_stackdepot.o := n
 
 libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
               fdt_empty_tree.o
index eca8808..0b66f0e 100644 (file)
@@ -496,6 +496,11 @@ EXPORT_SYMBOL(bitmap_print_to_pagebuf);
  * ranges.  Consecutively set bits are shown as two hyphen-separated
  * decimal numbers, the smallest and largest bit numbers set in
  * the range.
+ * Optionally each range can be postfixed to denote that only parts of it
+ * should be set. The range will divided to groups of specific size.
+ * From each group will be used only defined amount of bits.
+ * Syntax: range:used_size/group_size
+ * Example: 0-1023:2/256 ==> 0,1,256,257,512,513,768,769
  *
  * Returns 0 on success, -errno on invalid input strings.
  * Error values:
@@ -507,16 +512,20 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
                int is_user, unsigned long *maskp,
                int nmaskbits)
 {
-       unsigned a, b;
+       unsigned int a, b, old_a, old_b;
+       unsigned int group_size, used_size;
        int c, old_c, totaldigits, ndigits;
        const char __user __force *ubuf = (const char __user __force *)buf;
-       int at_start, in_range;
+       int at_start, in_range, in_partial_range;
 
        totaldigits = c = 0;
+       old_a = old_b = 0;
+       group_size = used_size = 0;
        bitmap_zero(maskp, nmaskbits);
        do {
                at_start = 1;
                in_range = 0;
+               in_partial_range = 0;
                a = b = 0;
                ndigits = totaldigits;
 
@@ -547,6 +556,24 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
                        if ((totaldigits != ndigits) && isspace(old_c))
                                return -EINVAL;
 
+                       if (c == '/') {
+                               used_size = a;
+                               at_start = 1;
+                               in_range = 0;
+                               a = b = 0;
+                               continue;
+                       }
+
+                       if (c == ':') {
+                               old_a = a;
+                               old_b = b;
+                               at_start = 1;
+                               in_range = 0;
+                               in_partial_range = 1;
+                               a = b = 0;
+                               continue;
+                       }
+
                        if (c == '-') {
                                if (at_start || in_range)
                                        return -EINVAL;
@@ -567,15 +594,30 @@ static int __bitmap_parselist(const char *buf, unsigned int buflen,
                }
                if (ndigits == totaldigits)
                        continue;
+               if (in_partial_range) {
+                       group_size = a;
+                       a = old_a;
+                       b = old_b;
+                       old_a = old_b = 0;
+               }
                /* if no digit is after '-', it's wrong*/
                if (at_start && in_range)
                        return -EINVAL;
-               if (!(a <= b))
+               if (!(a <= b) || !(used_size <= group_size))
                        return -EINVAL;
                if (b >= nmaskbits)
                        return -ERANGE;
                while (a <= b) {
-                       set_bit(a, maskp);
+                       if (in_partial_range) {
+                               static int pos_in_group = 1;
+
+                               if (pos_in_group <= used_size)
+                                       set_bit(a, maskp);
+
+                               if (a == b || ++pos_in_group > group_size)
+                                       pos_in_group = 1;
+                       } else
+                               set_bit(a, maskp);
                        a++;
                }
        } while (buflen && c == ',');
index d8a5cf6..b8e2080 100644 (file)
@@ -48,11 +48,9 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long
 {
        unsigned long long res;
        unsigned int rv;
-       int overflow;
 
        res = 0;
        rv = 0;
-       overflow = 0;
        while (*s) {
                unsigned int val;
 
@@ -71,15 +69,13 @@ unsigned int _parse_integer(const char *s, unsigned int base, unsigned long long
                 */
                if (unlikely(res & (~0ull << 60))) {
                        if (res > div_u64(ULLONG_MAX - val, base))
-                               overflow = 1;
+                               rv |= KSTRTOX_OVERFLOW;
                }
                res = res * base + val;
                rv++;
                s++;
        }
        *p = res;
-       if (overflow)
-               rv |= KSTRTOX_OVERFLOW;
        return rv;
 }
 
index 27fe749..9ac959e 100644 (file)
@@ -33,6 +33,7 @@
 
 #define PERCPU_COUNT_BIAS      (1LU << (BITS_PER_LONG - 1))
 
+static DEFINE_SPINLOCK(percpu_ref_switch_lock);
 static DECLARE_WAIT_QUEUE_HEAD(percpu_ref_switch_waitq);
 
 static unsigned long __percpu *percpu_count_ptr(struct percpu_ref *ref)
@@ -82,6 +83,7 @@ int percpu_ref_init(struct percpu_ref *ref, percpu_ref_func_t *release,
        atomic_long_set(&ref->count, start_count);
 
        ref->release = release;
+       ref->confirm_switch = NULL;
        return 0;
 }
 EXPORT_SYMBOL_GPL(percpu_ref_init);
@@ -101,6 +103,8 @@ void percpu_ref_exit(struct percpu_ref *ref)
        unsigned long __percpu *percpu_count = percpu_count_ptr(ref);
 
        if (percpu_count) {
+               /* non-NULL confirm_switch indicates switching in progress */
+               WARN_ON_ONCE(ref->confirm_switch);
                free_percpu(percpu_count);
                ref->percpu_count_ptr = __PERCPU_REF_ATOMIC_DEAD;
        }
@@ -161,66 +165,23 @@ static void percpu_ref_noop_confirm_switch(struct percpu_ref *ref)
 static void __percpu_ref_switch_to_atomic(struct percpu_ref *ref,
                                          percpu_ref_func_t *confirm_switch)
 {
-       if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC)) {
-               /* switching from percpu to atomic */
-               ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
-
-               /*
-                * Non-NULL ->confirm_switch is used to indicate that
-                * switching is in progress.  Use noop one if unspecified.
-                */
-               WARN_ON_ONCE(ref->confirm_switch);
-               ref->confirm_switch =
-                       confirm_switch ?: percpu_ref_noop_confirm_switch;
-
-               percpu_ref_get(ref);    /* put after confirmation */
-               call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu);
-       } else if (confirm_switch) {
-               /*
-                * Somebody already set ATOMIC.  Switching may still be in
-                * progress.  @confirm_switch must be invoked after the
-                * switching is complete and a full sched RCU grace period
-                * has passed.  Wait synchronously for the previous
-                * switching and schedule @confirm_switch invocation.
-                */
-               wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
-               ref->confirm_switch = confirm_switch;
-
-               percpu_ref_get(ref);    /* put after confirmation */
-               call_rcu_sched(&ref->rcu, percpu_ref_call_confirm_rcu);
+       if (ref->percpu_count_ptr & __PERCPU_REF_ATOMIC) {
+               if (confirm_switch)
+                       confirm_switch(ref);
+               return;
        }
-}
 
-/**
- * percpu_ref_switch_to_atomic - switch a percpu_ref to atomic mode
- * @ref: percpu_ref to switch to atomic mode
- * @confirm_switch: optional confirmation callback
- *
- * There's no reason to use this function for the usual reference counting.
- * Use percpu_ref_kill[_and_confirm]().
- *
- * Schedule switching of @ref to atomic mode.  All its percpu counts will
- * be collected to the main atomic counter.  On completion, when all CPUs
- * are guaraneed to be in atomic mode, @confirm_switch, which may not
- * block, is invoked.  This function may be invoked concurrently with all
- * the get/put operations and can safely be mixed with kill and reinit
- * operations.  Note that @ref will stay in atomic mode across kill/reinit
- * cycles until percpu_ref_switch_to_percpu() is called.
- *
- * This function normally doesn't block and can be called from any context
- * but it may block if @confirm_kill is specified and @ref is already in
- * the process of switching to atomic mode.  In such cases, @confirm_switch
- * will be invoked after the switching is complete.
- *
- * Due to the way percpu_ref is implemented, @confirm_switch will be called
- * after at least one full sched RCU grace period has passed but this is an
- * implementation detail and must not be depended upon.
- */
-void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
-                                percpu_ref_func_t *confirm_switch)
-{
-       ref->force_atomic = true;
-       __percpu_ref_switch_to_atomic(ref, confirm_switch);
+       /* switching from percpu to atomic */
+       ref->percpu_count_ptr |= __PERCPU_REF_ATOMIC;
+
+       /*
+        * Non-NULL ->confirm_switch is used to indicate that switching is
+        * in progress.  Use noop one if unspecified.
+        */
+       ref->confirm_switch = confirm_switch ?: percpu_ref_noop_confirm_switch;
+
+       percpu_ref_get(ref);    /* put after confirmation */
+       call_rcu_sched(&ref->rcu, percpu_ref_switch_to_atomic_rcu);
 }
 
 static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
@@ -233,8 +194,6 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
        if (!(ref->percpu_count_ptr & __PERCPU_REF_ATOMIC))
                return;
 
-       wait_event(percpu_ref_switch_waitq, !ref->confirm_switch);
-
        atomic_long_add(PERCPU_COUNT_BIAS, &ref->count);
 
        /*
@@ -250,6 +209,58 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
                          ref->percpu_count_ptr & ~__PERCPU_REF_ATOMIC);
 }
 
+static void __percpu_ref_switch_mode(struct percpu_ref *ref,
+                                    percpu_ref_func_t *confirm_switch)
+{
+       lockdep_assert_held(&percpu_ref_switch_lock);
+
+       /*
+        * If the previous ATOMIC switching hasn't finished yet, wait for
+        * its completion.  If the caller ensures that ATOMIC switching
+        * isn't in progress, this function can be called from any context.
+        */
+       wait_event_lock_irq(percpu_ref_switch_waitq, !ref->confirm_switch,
+                           percpu_ref_switch_lock);
+
+       if (ref->force_atomic || (ref->percpu_count_ptr & __PERCPU_REF_DEAD))
+               __percpu_ref_switch_to_atomic(ref, confirm_switch);
+       else
+               __percpu_ref_switch_to_percpu(ref);
+}
+
+/**
+ * percpu_ref_switch_to_atomic - switch a percpu_ref to atomic mode
+ * @ref: percpu_ref to switch to atomic mode
+ * @confirm_switch: optional confirmation callback
+ *
+ * There's no reason to use this function for the usual reference counting.
+ * Use percpu_ref_kill[_and_confirm]().
+ *
+ * Schedule switching of @ref to atomic mode.  All its percpu counts will
+ * be collected to the main atomic counter.  On completion, when all CPUs
+ * are guaraneed to be in atomic mode, @confirm_switch, which may not
+ * block, is invoked.  This function may be invoked concurrently with all
+ * the get/put operations and can safely be mixed with kill and reinit
+ * operations.  Note that @ref will stay in atomic mode across kill/reinit
+ * cycles until percpu_ref_switch_to_percpu() is called.
+ *
+ * This function may block if @ref is in the process of switching to atomic
+ * mode.  If the caller ensures that @ref is not in the process of
+ * switching to atomic mode, this function can be called from any context.
+ */
+void percpu_ref_switch_to_atomic(struct percpu_ref *ref,
+                                percpu_ref_func_t *confirm_switch)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
+       ref->force_atomic = true;
+       __percpu_ref_switch_mode(ref, confirm_switch);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
+}
+
 /**
  * percpu_ref_switch_to_percpu - switch a percpu_ref to percpu mode
  * @ref: percpu_ref to switch to percpu mode
@@ -264,17 +275,20 @@ static void __percpu_ref_switch_to_percpu(struct percpu_ref *ref)
  * dying or dead, the actual switching takes place on the following
  * percpu_ref_reinit().
  *
- * This function normally doesn't block and can be called from any context
- * but it may block if @ref is in the process of switching to atomic mode
- * by percpu_ref_switch_atomic().
+ * This function may block if @ref is in the process of switching to atomic
+ * mode.  If the caller ensures that @ref is not in the process of
+ * switching to atomic mode, this function can be called from any context.
  */
 void percpu_ref_switch_to_percpu(struct percpu_ref *ref)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        ref->force_atomic = false;
+       __percpu_ref_switch_mode(ref, NULL);
 
-       /* a dying or dead ref can't be switched to percpu mode w/o reinit */
-       if (!(ref->percpu_count_ptr & __PERCPU_REF_DEAD))
-               __percpu_ref_switch_to_percpu(ref);
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 
 /**
@@ -290,21 +304,23 @@ void percpu_ref_switch_to_percpu(struct percpu_ref *ref)
  *
  * This function normally doesn't block and can be called from any context
  * but it may block if @confirm_kill is specified and @ref is in the
- * process of switching to atomic mode by percpu_ref_switch_atomic().
- *
- * Due to the way percpu_ref is implemented, @confirm_switch will be called
- * after at least one full sched RCU grace period has passed but this is an
- * implementation detail and must not be depended upon.
+ * process of switching to atomic mode by percpu_ref_switch_to_atomic().
  */
 void percpu_ref_kill_and_confirm(struct percpu_ref *ref,
                                 percpu_ref_func_t *confirm_kill)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        WARN_ONCE(ref->percpu_count_ptr & __PERCPU_REF_DEAD,
                  "%s called more than once on %pf!", __func__, ref->release);
 
        ref->percpu_count_ptr |= __PERCPU_REF_DEAD;
-       __percpu_ref_switch_to_atomic(ref, confirm_kill);
+       __percpu_ref_switch_mode(ref, confirm_kill);
        percpu_ref_put(ref);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
 
@@ -321,11 +337,16 @@ EXPORT_SYMBOL_GPL(percpu_ref_kill_and_confirm);
  */
 void percpu_ref_reinit(struct percpu_ref *ref)
 {
+       unsigned long flags;
+
+       spin_lock_irqsave(&percpu_ref_switch_lock, flags);
+
        WARN_ON_ONCE(!percpu_ref_is_zero(ref));
 
        ref->percpu_count_ptr &= ~__PERCPU_REF_DEAD;
        percpu_ref_get(ref);
-       if (!ref->force_atomic)
-               __percpu_ref_switch_to_percpu(ref);
+       __percpu_ref_switch_mode(ref, NULL);
+
+       spin_unlock_irqrestore(&percpu_ref_switch_lock, flags);
 }
 EXPORT_SYMBOL_GPL(percpu_ref_reinit);
index 9c5fe81..7e35fc4 100644 (file)
@@ -1,6 +1,7 @@
 #include <linux/compiler.h>
 #include <linux/export.h>
 #include <linux/kasan-checks.h>
+#include <linux/thread_info.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -111,6 +112,7 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
                long retval;
 
                kasan_check_write(dst, count);
+               check_object_size(dst, count, false);
                user_access_begin();
                retval = do_strncpy_from_user(dst, src, count, max);
                user_access_end();
index 2ca1faf..295bd7a 100644 (file)
@@ -21,9 +21,6 @@ KCOV_INSTRUMENT_memcontrol.o := n
 KCOV_INSTRUMENT_mmzone.o := n
 KCOV_INSTRUMENT_vmstat.o := n
 
-# Since __builtin_frame_address does work as used, disable the warning.
-CFLAGS_usercopy.o += $(call cc-disable-warning, frame-address)
-
 mmu-y                  := nommu.o
 mmu-$(CONFIG_MMU)      := gup.o highmem.o memory.o mincore.o \
                           mlock.o mmap.o mprotect.o mremap.o msync.o rmap.o \
index a869f84..e8a55a3 100644 (file)
@@ -155,7 +155,7 @@ void __init free_bootmem_late(unsigned long physaddr, unsigned long size)
 {
        unsigned long cursor, end;
 
-       kmemleak_free_part(__va(physaddr), size);
+       kmemleak_free_part_phys(physaddr, size);
 
        cursor = PFN_UP(physaddr);
        end = PFN_DOWN(physaddr + size);
@@ -399,7 +399,7 @@ void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
 {
        unsigned long start, end;
 
-       kmemleak_free_part(__va(physaddr), size);
+       kmemleak_free_part_phys(physaddr, size);
 
        start = PFN_UP(physaddr);
        end = PFN_DOWN(physaddr + size);
@@ -420,7 +420,7 @@ void __init free_bootmem(unsigned long physaddr, unsigned long size)
 {
        unsigned long start, end;
 
-       kmemleak_free_part(__va(physaddr), size);
+       kmemleak_free_part_phys(physaddr, size);
 
        start = PFN_UP(physaddr);
        end = PFN_DOWN(physaddr + size);
index bd0e141..384c2cb 100644 (file)
--- a/mm/cma.c
+++ b/mm/cma.c
@@ -336,7 +336,7 @@ int __init cma_declare_contiguous(phys_addr_t base,
                 * kmemleak scans/reads tracked objects for pointers to other
                 * objects but this address isn't mapped and accessible
                 */
-               kmemleak_ignore(phys_to_virt(addr));
+               kmemleak_ignore_phys(addr);
                base = addr;
        }
 
index 086292f..a5e453c 100644 (file)
@@ -90,6 +90,8 @@
 #include <linux/cache.h>
 #include <linux/percpu.h>
 #include <linux/hardirq.h>
+#include <linux/bootmem.h>
+#include <linux/pfn.h>
 #include <linux/mmzone.h>
 #include <linux/slab.h>
 #include <linux/thread_info.h>
@@ -1121,6 +1123,51 @@ void __ref kmemleak_no_scan(const void *ptr)
 }
 EXPORT_SYMBOL(kmemleak_no_scan);
 
+/**
+ * kmemleak_alloc_phys - similar to kmemleak_alloc but taking a physical
+ *                      address argument
+ */
+void __ref kmemleak_alloc_phys(phys_addr_t phys, size_t size, int min_count,
+                              gfp_t gfp)
+{
+       if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+               kmemleak_alloc(__va(phys), size, min_count, gfp);
+}
+EXPORT_SYMBOL(kmemleak_alloc_phys);
+
+/**
+ * kmemleak_free_part_phys - similar to kmemleak_free_part but taking a
+ *                          physical address argument
+ */
+void __ref kmemleak_free_part_phys(phys_addr_t phys, size_t size)
+{
+       if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+               kmemleak_free_part(__va(phys), size);
+}
+EXPORT_SYMBOL(kmemleak_free_part_phys);
+
+/**
+ * kmemleak_not_leak_phys - similar to kmemleak_not_leak but taking a physical
+ *                         address argument
+ */
+void __ref kmemleak_not_leak_phys(phys_addr_t phys)
+{
+       if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+               kmemleak_not_leak(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_not_leak_phys);
+
+/**
+ * kmemleak_ignore_phys - similar to kmemleak_ignore but taking a physical
+ *                       address argument
+ */
+void __ref kmemleak_ignore_phys(phys_addr_t phys)
+{
+       if (!IS_ENABLED(CONFIG_HIGHMEM) || PHYS_PFN(phys) < max_low_pfn)
+               kmemleak_ignore(__va(phys));
+}
+EXPORT_SYMBOL(kmemleak_ignore_phys);
+
 /*
  * Update an object's checksum and return true if it was modified.
  */
index c8dfa43..7608bc3 100644 (file)
@@ -723,7 +723,7 @@ int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size)
                     (unsigned long long)base + size - 1,
                     (void *)_RET_IP_);
 
-       kmemleak_free_part(__va(base), size);
+       kmemleak_free_part_phys(base, size);
        return memblock_remove_range(&memblock.reserved, base, size);
 }
 
@@ -1152,7 +1152,7 @@ static phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size,
                 * The min_count is set to 0 so that memblock allocations are
                 * never reported as leaks.
                 */
-               kmemleak_alloc(__va(found), size, 0, 0);
+               kmemleak_alloc_phys(found, size, 0, 0);
                return found;
        }
        return 0;
@@ -1399,7 +1399,7 @@ void __init __memblock_free_early(phys_addr_t base, phys_addr_t size)
        memblock_dbg("%s: [%#016llx-%#016llx] %pF\n",
                     __func__, (u64)base, (u64)base + size - 1,
                     (void *)_RET_IP_);
-       kmemleak_free_part(__va(base), size);
+       kmemleak_free_part_phys(base, size);
        memblock_remove_range(&memblock.reserved, base, size);
 }
 
@@ -1419,7 +1419,7 @@ void __init __memblock_free_late(phys_addr_t base, phys_addr_t size)
        memblock_dbg("%s: [%#016llx-%#016llx] %pF\n",
                     __func__, (u64)base, (u64)base + size - 1,
                     (void *)_RET_IP_);
-       kmemleak_free_part(__va(base), size);
+       kmemleak_free_part_phys(base, size);
        cursor = PFN_UP(base);
        end = PFN_DOWN(base + size);
 
index ba609b6..487dad6 100644 (file)
@@ -84,7 +84,7 @@ void __init free_bootmem_late(unsigned long addr, unsigned long size)
 {
        unsigned long cursor, end;
 
-       kmemleak_free_part(__va(addr), size);
+       kmemleak_free_part_phys(addr, size);
 
        cursor = PFN_UP(addr);
        end = PFN_DOWN(addr + size);
index 9903830..2557143 100644 (file)
@@ -1961,8 +1961,9 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
        void *base = (void *)ULONG_MAX;
        void **areas = NULL;
        struct pcpu_alloc_info *ai;
-       size_t size_sum, areas_size, max_distance;
-       int group, i, rc;
+       size_t size_sum, areas_size;
+       unsigned long max_distance;
+       int group, i, highest_group, rc;
 
        ai = pcpu_build_alloc_info(reserved_size, dyn_size, atom_size,
                                   cpu_distance_fn);
@@ -1978,7 +1979,8 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
                goto out_free;
        }
 
-       /* allocate, copy and determine base address */
+       /* allocate, copy and determine base address & max_distance */
+       highest_group = 0;
        for (group = 0; group < ai->nr_groups; group++) {
                struct pcpu_group_info *gi = &ai->groups[group];
                unsigned int cpu = NR_CPUS;
@@ -1999,6 +2001,21 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
                areas[group] = ptr;
 
                base = min(ptr, base);
+               if (ptr > areas[highest_group])
+                       highest_group = group;
+       }
+       max_distance = areas[highest_group] - base;
+       max_distance += ai->unit_size * ai->groups[highest_group].nr_units;
+
+       /* warn if maximum distance is further than 75% of vmalloc space */
+       if (max_distance > VMALLOC_TOTAL * 3 / 4) {
+               pr_warn("max_distance=0x%lx too large for vmalloc space 0x%lx\n",
+                               max_distance, VMALLOC_TOTAL);
+#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
+               /* and fail if we have fallback */
+               rc = -EINVAL;
+               goto out_free_areas;
+#endif
        }
 
        /*
@@ -2023,23 +2040,8 @@ int __init pcpu_embed_first_chunk(size_t reserved_size, size_t dyn_size,
        }
 
        /* base address is now known, determine group base offsets */
-       max_distance = 0;
        for (group = 0; group < ai->nr_groups; group++) {
                ai->groups[group].base_offset = areas[group] - base;
-               max_distance = max_t(size_t, max_distance,
-                                    ai->groups[group].base_offset);
-       }
-       max_distance += ai->unit_size;
-
-       /* warn if maximum distance is further than 75% of vmalloc space */
-       if (max_distance > VMALLOC_TOTAL * 3 / 4) {
-               pr_warn("max_distance=0x%zx too large for vmalloc space 0x%lx\n",
-                       max_distance, VMALLOC_TOTAL);
-#ifdef CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK
-               /* and fail if we have fallback */
-               rc = -EINVAL;
-               goto out_free;
-#endif
        }
 
        pr_info("Embedded %zu pages/cpu @%p s%zu r%zu d%zu u%zu\n",
index c68ff3d..e49121e 100644 (file)
@@ -20,8 +20,6 @@
 
 #include "main.h"
 
-#include <linux/kconfig.h>
-
 struct net_device;
 
 #define BATADV_DEBUGFS_SUBDIR "batman_adv"
index e657258..8bd5696 100644 (file)
@@ -217,6 +217,7 @@ static const struct brport_attribute *brport_attrs[] = {
 #endif
        &brport_attr_proxyarp,
        &brport_attr_proxyarp_wifi,
+       &brport_attr_multicast_flood,
        NULL
 };
 
index b06d2f4..fb7348f 100644 (file)
@@ -1144,6 +1144,8 @@ static noinline_for_stack int rtnl_fill_vfinfo(struct sk_buff *skb,
        if (dev->netdev_ops->ndo_get_vf_config(dev, vfs_num, &ivi))
                return 0;
 
+       memset(&vf_vlan_info, 0, sizeof(vf_vlan_info));
+
        vf_mac.vf =
                vf_vlan.vf =
                vf_vlan_info.vf =
index f2be689..62d4d90 100644 (file)
@@ -2265,7 +2265,8 @@ struct rtable *__ip_route_output_key_hash(struct net *net, struct flowi4 *fl4,
        if (err) {
                res.fi = NULL;
                res.table = NULL;
-               if (fl4->flowi4_oif) {
+               if (fl4->flowi4_oif &&
+                   !netif_index_is_l3_master(net, fl4->flowi4_oif)) {
                        /* Apparently, routing tables are wrong. Assume,
                           that the destination is on link.
 
index 54cf719..5a27ab4 100644 (file)
@@ -1190,6 +1190,16 @@ out:
        return NULL;
 }
 
+static void tcp_v6_restore_cb(struct sk_buff *skb)
+{
+       /* We need to move header back to the beginning if xfrm6_policy_check()
+        * and tcp_v6_fill_cb() are going to be called again.
+        * ip6_datagram_recv_specific_ctl() also expects IP6CB to be there.
+        */
+       memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
+               sizeof(struct inet6_skb_parm));
+}
+
 /* The socket must have it's spinlock held when we get
  * here, unless it is a TCP_LISTEN socket.
  *
@@ -1319,6 +1329,7 @@ ipv6_pktoptions:
                        np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
                if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
                        skb_set_owner_r(opt_skb, sk);
+                       tcp_v6_restore_cb(opt_skb);
                        opt_skb = xchg(&np->pktoptions, opt_skb);
                } else {
                        __kfree_skb(opt_skb);
@@ -1352,15 +1363,6 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr,
        TCP_SKB_CB(skb)->sacked = 0;
 }
 
-static void tcp_v6_restore_cb(struct sk_buff *skb)
-{
-       /* We need to move header back to the beginning if xfrm6_policy_check()
-        * and tcp_v6_fill_cb() are going to be called again.
-        */
-       memmove(IP6CB(skb), &TCP_SKB_CB(skb)->header.h6,
-               sizeof(struct inet6_skb_parm));
-}
-
 static int tcp_v6_rcv(struct sk_buff *skb)
 {
        const struct tcphdr *th;
index c8c82e1..2208706 100644 (file)
@@ -343,7 +343,7 @@ static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
        key->eth.cvlan.tci = 0;
        key->eth.cvlan.tpid = 0;
 
-       if (likely(skb_vlan_tag_present(skb))) {
+       if (skb_vlan_tag_present(skb)) {
                key->eth.vlan.tci = htons(skb->vlan_tci);
                key->eth.vlan.tpid = skb->vlan_proto;
        } else {
index 95c3614..e7da290 100644 (file)
@@ -176,7 +176,7 @@ static void do_setup(struct net_device *netdev)
 
        netdev->vlan_features = netdev->features;
        netdev->hw_enc_features = netdev->features;
-       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX;
+       netdev->features |= NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
        netdev->hw_features = netdev->features & ~NETIF_F_LLTX;
 
        eth_hw_addr_random(netdev);
index 8f19843..7387418 100644 (file)
@@ -485,7 +485,8 @@ static unsigned int packet_length(const struct sk_buff *skb)
 {
        unsigned int length = skb->len - ETH_HLEN;
 
-       if (skb_vlan_tagged(skb))
+       if (!skb_vlan_tag_present(skb) &&
+           eth_type_vlan(skb->protocol))
                length -= VLAN_HLEN;
 
        /* Don't subtract for multiple VLAN tags. Most (all?) drivers allow
index c910217..a512b18 100644 (file)
@@ -341,22 +341,25 @@ int tcf_register_action(struct tc_action_ops *act,
        if (!act->act || !act->dump || !act->init || !act->walk || !act->lookup)
                return -EINVAL;
 
+       /* We have to register pernet ops before making the action ops visible,
+        * otherwise tcf_action_init_1() could get a partially initialized
+        * netns.
+        */
+       ret = register_pernet_subsys(ops);
+       if (ret)
+               return ret;
+
        write_lock(&act_mod_lock);
        list_for_each_entry(a, &act_base, head) {
                if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
                        write_unlock(&act_mod_lock);
+                       unregister_pernet_subsys(ops);
                        return -EEXIST;
                }
        }
        list_add_tail(&act->head, &act_base);
        write_unlock(&act_mod_lock);
 
-       ret = register_pernet_subsys(ops);
-       if (ret) {
-               tcf_unregister_action(act, ops);
-               return ret;
-       }
-
        return 0;
 }
 EXPORT_SYMBOL(tcf_register_action);
@@ -367,8 +370,6 @@ int tcf_unregister_action(struct tc_action_ops *act,
        struct tc_action_ops *a;
        int err = -ENOENT;
 
-       unregister_pernet_subsys(ops);
-
        write_lock(&act_mod_lock);
        list_for_each_entry(a, &act_base, head) {
                if (a == act) {
@@ -378,6 +379,8 @@ int tcf_unregister_action(struct tc_action_ops *act,
                }
        }
        write_unlock(&act_mod_lock);
+       if (!err)
+               unregister_pernet_subsys(ops);
        return err;
 }
 EXPORT_SYMBOL(tcf_unregister_action);
index 11da7da..2ee29a3 100644 (file)
@@ -101,7 +101,7 @@ EXPORT_SYMBOL(unregister_tcf_proto_ops);
 
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                          struct nlmsghdr *n, struct tcf_proto *tp,
-                         unsigned long fh, int event);
+                         unsigned long fh, int event, bool unicast);
 
 static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
                                 struct nlmsghdr *n,
@@ -112,7 +112,7 @@ static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
 
        for (it_chain = chain; (tp = rtnl_dereference(*it_chain)) != NULL;
             it_chain = &tp->next)
-               tfilter_notify(net, oskb, n, tp, 0, event);
+               tfilter_notify(net, oskb, n, tp, 0, event, false);
 }
 
 /* Select new prio value from the range, managed by kernel. */
@@ -319,7 +319,8 @@ replay:
 
                        RCU_INIT_POINTER(*back, next);
 
-                       tfilter_notify(net, skb, n, tp, fh, RTM_DELTFILTER);
+                       tfilter_notify(net, skb, n, tp, fh,
+                                      RTM_DELTFILTER, false);
                        tcf_destroy(tp, true);
                        err = 0;
                        goto errout;
@@ -345,14 +346,14 @@ replay:
                                struct tcf_proto *next = rtnl_dereference(tp->next);
 
                                tfilter_notify(net, skb, n, tp, fh,
-                                              RTM_DELTFILTER);
+                                              RTM_DELTFILTER, false);
                                if (tcf_destroy(tp, false))
                                        RCU_INIT_POINTER(*back, next);
                        }
                        goto errout;
                case RTM_GETTFILTER:
                        err = tfilter_notify(net, skb, n, tp, fh,
-                                            RTM_NEWTFILTER);
+                                            RTM_NEWTFILTER, true);
                        goto errout;
                default:
                        err = -EINVAL;
@@ -367,7 +368,7 @@ replay:
                        RCU_INIT_POINTER(tp->next, rtnl_dereference(*back));
                        rcu_assign_pointer(*back, tp);
                }
-               tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER);
+               tfilter_notify(net, skb, n, tp, fh, RTM_NEWTFILTER, false);
        } else {
                if (tp_created)
                        tcf_destroy(tp, true);
@@ -419,7 +420,7 @@ nla_put_failure:
 
 static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                          struct nlmsghdr *n, struct tcf_proto *tp,
-                         unsigned long fh, int event)
+                         unsigned long fh, int event, bool unicast)
 {
        struct sk_buff *skb;
        u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
@@ -433,6 +434,9 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb,
                return -EINVAL;
        }
 
+       if (unicast)
+               return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
+
        return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
                              n->nlmsg_flags & NLM_F_ECHO);
 }
index 5c7549b..41adf36 100644 (file)
@@ -246,7 +246,7 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb,
                                } else {
                                        strp->rx_interrupted = 1;
                                }
-                               strp_parser_err(strp, err, desc);
+                               strp_parser_err(strp, len, desc);
                                break;
                        } else if (len > strp->sk->sk_rcvbuf) {
                                /* Message length exceeds maximum allowed */
index a7e42f9..2bff63a 100644 (file)
@@ -551,7 +551,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
                        *entry, *new;
        unsigned int nr;
 
-       nr = hash_long(from_kuid(&init_user_ns, acred->uid), cache->hashbits);
+       nr = auth->au_ops->hash_cred(acred, cache->hashbits);
 
        rcu_read_lock();
        hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
index 83dffea..f1df983 100644 (file)
@@ -78,6 +78,14 @@ static struct rpc_cred *generic_bind_cred(struct rpc_task *task,
        return auth->au_ops->lookup_cred(auth, acred, lookupflags);
 }
 
+static int
+generic_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kgid(&init_user_ns, acred->gid) |
+               ((u64)from_kuid(&init_user_ns, acred->uid) <<
+                       (sizeof(gid_t) * 8)), hashbits);
+}
+
 /*
  * Lookup generic creds for current process
  */
@@ -258,6 +266,7 @@ generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred)
 static const struct rpc_authops generic_auth_ops = {
        .owner = THIS_MODULE,
        .au_name = "Generic",
+       .hash_cred = generic_hash_cred,
        .lookup_cred = generic_lookup_cred,
        .crcreate = generic_create_cred,
        .key_timeout = generic_key_timeout,
index 976c781..d8bd97a 100644 (file)
@@ -1298,6 +1298,12 @@ gss_destroy_cred(struct rpc_cred *cred)
        gss_destroy_nullcred(cred);
 }
 
+static int
+gss_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kuid(&init_user_ns, acred->uid), hashbits);
+}
+
 /*
  * Lookup RPCSEC_GSS cred for the current process
  */
@@ -1982,6 +1988,7 @@ static const struct rpc_authops authgss_ops = {
        .au_name        = "RPCSEC_GSS",
        .create         = gss_create,
        .destroy        = gss_destroy,
+       .hash_cred      = gss_hash_cred,
        .lookup_cred    = gss_lookup_cred,
        .crcreate       = gss_create_cred,
        .list_pseudoflavors = gss_mech_list_pseudoflavors,
index a1d768a..306fc0f 100644 (file)
@@ -46,6 +46,14 @@ unx_destroy(struct rpc_auth *auth)
        rpcauth_clear_credcache(auth->au_credcache);
 }
 
+static int
+unx_hash_cred(struct auth_cred *acred, unsigned int hashbits)
+{
+       return hash_64(from_kgid(&init_user_ns, acred->gid) |
+               ((u64)from_kuid(&init_user_ns, acred->uid) <<
+                       (sizeof(gid_t) * 8)), hashbits);
+}
+
 /*
  * Lookup AUTH_UNIX creds for current process
  */
@@ -220,6 +228,7 @@ const struct rpc_authops authunix_ops = {
        .au_name        = "UNIX",
        .create         = unx_create,
        .destroy        = unx_destroy,
+       .hash_cred      = unx_hash_cred,
        .lookup_cred    = unx_lookup_cred,
        .crcreate       = unx_create_cred,
 };
index 229956b..ac701c2 100644 (file)
@@ -76,13 +76,7 @@ static int xprt_alloc_xdr_buf(struct xdr_buf *buf, gfp_t gfp_flags)
        page = alloc_page(gfp_flags);
        if (page == NULL)
                return -ENOMEM;
-       buf->head[0].iov_base = page_address(page);
-       buf->head[0].iov_len = PAGE_SIZE;
-       buf->tail[0].iov_base = NULL;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->len = 0;
-       buf->buflen = PAGE_SIZE;
+       xdr_buf_init(buf, page_address(page), PAGE_SIZE);
        return 0;
 }
 
index 4d8e11f..8aabe12 100644 (file)
@@ -353,7 +353,7 @@ void sunrpc_init_cache_detail(struct cache_detail *cd)
        spin_unlock(&cache_list_lock);
 
        /* start the cleaning process */
-       schedule_delayed_work(&cache_cleaner, 0);
+       queue_delayed_work(system_power_efficient_wq, &cache_cleaner, 0);
 }
 EXPORT_SYMBOL_GPL(sunrpc_init_cache_detail);
 
@@ -476,7 +476,8 @@ static void do_cache_clean(struct work_struct *work)
                delay = 0;
 
        if (delay)
-               schedule_delayed_work(&cache_cleaner, delay);
+               queue_delayed_work(system_power_efficient_wq,
+                                  &cache_cleaner, delay);
 }
 
 
index 66f23b3..34dd7b2 100644 (file)
@@ -184,7 +184,6 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                                   struct super_block *sb)
 {
        struct dentry *dentry;
-       int err = 0;
 
        switch (event) {
        case RPC_PIPEFS_MOUNT:
@@ -201,7 +200,7 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,
                printk(KERN_ERR "%s: unknown event: %ld\n", __func__, event);
                return -ENOTSUPP;
        }
-       return err;
+       return 0;
 }
 
 static int __rpc_pipefs_event(struct rpc_clnt *clnt, unsigned long event,
@@ -988,7 +987,6 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)
 {
 
        if (clnt != NULL) {
-               rpc_task_release_client(task);
                if (task->tk_xprt == NULL)
                        task->tk_xprt = xprt_iter_get_next(&clnt->cl_xpi);
                task->tk_client = clnt;
@@ -1693,6 +1691,7 @@ call_allocate(struct rpc_task *task)
        struct rpc_rqst *req = task->tk_rqstp;
        struct rpc_xprt *xprt = req->rq_xprt;
        struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
+       int status;
 
        dprint_status(task);
 
@@ -1718,11 +1717,14 @@ call_allocate(struct rpc_task *task)
        req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen;
        req->rq_rcvsize <<= 2;
 
-       req->rq_buffer = xprt->ops->buf_alloc(task,
-                                       req->rq_callsize + req->rq_rcvsize);
-       if (req->rq_buffer != NULL)
-               return;
+       status = xprt->ops->buf_alloc(task);
        xprt_inject_disconnect(xprt);
+       if (status == 0)
+               return;
+       if (status != -ENOMEM) {
+               rpc_exit(task, status);
+               return;
+       }
 
        dprintk("RPC: %5u rpc_buffer allocation failed\n", task->tk_pid);
 
@@ -1748,18 +1750,6 @@ rpc_task_force_reencode(struct rpc_task *task)
        task->tk_rqstp->rq_bytes_sent = 0;
 }
 
-static inline void
-rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
-{
-       buf->head[0].iov_base = start;
-       buf->head[0].iov_len = len;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->flags = 0;
-       buf->len = 0;
-       buf->buflen = len;
-}
-
 /*
  * 3.  Encode arguments of an RPC call
  */
@@ -1772,12 +1762,12 @@ rpc_xdr_encode(struct rpc_task *task)
 
        dprint_status(task);
 
-       rpc_xdr_buf_init(&req->rq_snd_buf,
-                        req->rq_buffer,
-                        req->rq_callsize);
-       rpc_xdr_buf_init(&req->rq_rcv_buf,
-                        (char *)req->rq_buffer + req->rq_callsize,
-                        req->rq_rcvsize);
+       xdr_buf_init(&req->rq_snd_buf,
+                    req->rq_buffer,
+                    req->rq_callsize);
+       xdr_buf_init(&req->rq_rcv_buf,
+                    req->rq_rbuffer,
+                    req->rq_rcvsize);
 
        p = rpc_encode_header(task);
        if (p == NULL) {
@@ -2615,6 +2605,70 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,
 }
 EXPORT_SYMBOL_GPL(rpc_clnt_test_and_add_xprt);
 
+/**
+ * rpc_clnt_setup_test_and_add_xprt()
+ *
+ * This is an rpc_clnt_add_xprt setup() function which returns 1 so:
+ *   1) caller of the test function must dereference the rpc_xprt_switch
+ *   and the rpc_xprt.
+ *   2) test function must call rpc_xprt_switch_add_xprt, usually in
+ *   the rpc_call_done routine.
+ *
+ * Upon success (return of 1), the test function adds the new
+ * transport to the rpc_clnt xprt switch
+ *
+ * @clnt: struct rpc_clnt to get the new transport
+ * @xps:  the rpc_xprt_switch to hold the new transport
+ * @xprt: the rpc_xprt to test
+ * @data: a struct rpc_add_xprt_test pointer that holds the test function
+ *        and test function call data
+ */
+int rpc_clnt_setup_test_and_add_xprt(struct rpc_clnt *clnt,
+                                    struct rpc_xprt_switch *xps,
+                                    struct rpc_xprt *xprt,
+                                    void *data)
+{
+       struct rpc_cred *cred;
+       struct rpc_task *task;
+       struct rpc_add_xprt_test *xtest = (struct rpc_add_xprt_test *)data;
+       int status = -EADDRINUSE;
+
+       xprt = xprt_get(xprt);
+       xprt_switch_get(xps);
+
+       if (rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
+               goto out_err;
+
+       /* Test the connection */
+       cred = authnull_ops.lookup_cred(NULL, NULL, 0);
+       task = rpc_call_null_helper(clnt, xprt, cred,
+                                   RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+                                   NULL, NULL);
+       put_rpccred(cred);
+       if (IS_ERR(task)) {
+               status = PTR_ERR(task);
+               goto out_err;
+       }
+       status = task->tk_status;
+       rpc_put_task(task);
+
+       if (status < 0)
+               goto out_err;
+
+       /* rpc_xprt_switch and rpc_xprt are deferrenced by add_xprt_test() */
+       xtest->add_xprt_test(clnt, xprt, xtest->data);
+
+       /* so that rpc_clnt_add_xprt does not call rpc_xprt_switch_add_xprt */
+       return 1;
+out_err:
+       xprt_put(xprt);
+       xprt_switch_put(xps);
+       pr_info("RPC:   rpc_clnt_test_xprt failed: %d addr %s not added\n",
+               status, xprt->address_strings[RPC_DISPLAY_ADDR]);
+       return status;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_setup_test_and_add_xprt);
+
 /**
  * rpc_clnt_add_xprt - Add a new transport to a rpc_clnt
  * @clnt: pointer to struct rpc_clnt
@@ -2697,6 +2751,34 @@ rpc_cap_max_reconnect_timeout(struct rpc_clnt *clnt, unsigned long timeo)
 }
 EXPORT_SYMBOL_GPL(rpc_cap_max_reconnect_timeout);
 
+void rpc_clnt_xprt_switch_put(struct rpc_clnt *clnt)
+{
+       xprt_switch_put(rcu_dereference(clnt->cl_xpi.xpi_xpswitch));
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_put);
+
+void rpc_clnt_xprt_switch_add_xprt(struct rpc_clnt *clnt, struct rpc_xprt *xprt)
+{
+       rpc_xprt_switch_add_xprt(rcu_dereference(clnt->cl_xpi.xpi_xpswitch),
+                                xprt);
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_add_xprt);
+
+bool rpc_clnt_xprt_switch_has_addr(struct rpc_clnt *clnt,
+                                  const struct sockaddr *sap)
+{
+       struct rpc_xprt_switch *xps;
+       bool ret;
+
+       xps = rcu_dereference(clnt->cl_xpi.xpi_xpswitch);
+
+       rcu_read_lock();
+       ret = rpc_xprt_switch_has_addr(xps, sap);
+       rcu_read_unlock();
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rpc_clnt_xprt_switch_has_addr);
+
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
 static void rpc_show_header(void)
 {
index 9ae5885..5db68b3 100644 (file)
@@ -849,14 +849,17 @@ static void rpc_async_schedule(struct work_struct *work)
 }
 
 /**
- * rpc_malloc - allocate an RPC buffer
- * @task: RPC task that will use this buffer
- * @size: requested byte size
+ * rpc_malloc - allocate RPC buffer resources
+ * @task: RPC task
+ *
+ * A single memory region is allocated, which is split between the
+ * RPC call and RPC reply that this task is being used for. When
+ * this RPC is retired, the memory is released by calling rpc_free.
  *
  * To prevent rpciod from hanging, this allocator never sleeps,
- * returning NULL and suppressing warning if the request cannot be serviced
- * immediately.
- * The caller can arrange to sleep in a way that is safe for rpciod.
+ * returning -ENOMEM and suppressing warning if the request cannot
+ * be serviced immediately. The caller can arrange to sleep in a
+ * way that is safe for rpciod.
  *
  * Most requests are 'small' (under 2KiB) and can be serviced from a
  * mempool, ensuring that NFS reads and writes can always proceed,
@@ -865,8 +868,10 @@ static void rpc_async_schedule(struct work_struct *work)
  * In order to avoid memory starvation triggering more writebacks of
  * NFS requests, we avoid using GFP_KERNEL.
  */
-void *rpc_malloc(struct rpc_task *task, size_t size)
+int rpc_malloc(struct rpc_task *task)
 {
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       size_t size = rqst->rq_callsize + rqst->rq_rcvsize;
        struct rpc_buffer *buf;
        gfp_t gfp = GFP_NOIO | __GFP_NOWARN;
 
@@ -880,28 +885,28 @@ void *rpc_malloc(struct rpc_task *task, size_t size)
                buf = kmalloc(size, gfp);
 
        if (!buf)
-               return NULL;
+               return -ENOMEM;
 
        buf->len = size;
        dprintk("RPC: %5u allocated buffer of size %zu at %p\n",
                        task->tk_pid, size, buf);
-       return &buf->data;
+       rqst->rq_buffer = buf->data;
+       rqst->rq_rbuffer = (char *)rqst->rq_buffer + rqst->rq_callsize;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(rpc_malloc);
 
 /**
- * rpc_free - free buffer allocated via rpc_malloc
- * @buffer: buffer to free
+ * rpc_free - free RPC buffer resources allocated via rpc_malloc
+ * @task: RPC task
  *
  */
-void rpc_free(void *buffer)
+void rpc_free(struct rpc_task *task)
 {
+       void *buffer = task->tk_rqstp->rq_buffer;
        size_t size;
        struct rpc_buffer *buf;
 
-       if (!buffer)
-               return;
-
        buf = container_of(buffer, struct rpc_buffer, data);
        size = buf->len;
 
index c5b0cb4..7c8070e 100644 (file)
@@ -401,6 +401,21 @@ int svc_bind(struct svc_serv *serv, struct net *net)
 }
 EXPORT_SYMBOL_GPL(svc_bind);
 
+#if defined(CONFIG_SUNRPC_BACKCHANNEL)
+static void
+__svc_init_bc(struct svc_serv *serv)
+{
+       INIT_LIST_HEAD(&serv->sv_cb_list);
+       spin_lock_init(&serv->sv_cb_lock);
+       init_waitqueue_head(&serv->sv_cb_waitq);
+}
+#else
+static void
+__svc_init_bc(struct svc_serv *serv)
+{
+}
+#endif
+
 /*
  * Create an RPC service
  */
@@ -443,6 +458,8 @@ __svc_create(struct svc_program *prog, unsigned int bufsize, int npools,
        init_timer(&serv->sv_temptimer);
        spin_lock_init(&serv->sv_lock);
 
+       __svc_init_bc(serv);
+
        serv->sv_nrpools = npools;
        serv->sv_pools =
                kcalloc(serv->sv_nrpools, sizeof(struct svc_pool),
index c4f3cc0..7f1071e 100644 (file)
@@ -767,7 +767,7 @@ static void xdr_set_next_page(struct xdr_stream *xdr)
        newbase -= xdr->buf->page_base;
 
        if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0)
-               xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
+               xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2);
 }
 
 static bool xdr_set_next_buffer(struct xdr_stream *xdr)
@@ -776,7 +776,7 @@ static bool xdr_set_next_buffer(struct xdr_stream *xdr)
                xdr_set_next_page(xdr);
        else if (xdr->iov == xdr->buf->head) {
                if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0)
-                       xdr_set_iov(xdr, xdr->buf->tail, xdr->buf->len);
+                       xdr_set_iov(xdr, xdr->buf->tail, xdr->nwords << 2);
        }
        return xdr->p != xdr->end;
 }
@@ -859,12 +859,15 @@ EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
 static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes)
 {
        __be32 *p;
-       void *cpdest = xdr->scratch.iov_base;
+       char *cpdest = xdr->scratch.iov_base;
        size_t cplen = (char *)xdr->end - (char *)xdr->p;
 
        if (nbytes > xdr->scratch.iov_len)
                return NULL;
-       memcpy(cpdest, xdr->p, cplen);
+       p = __xdr_inline_decode(xdr, cplen);
+       if (p == NULL)
+               return NULL;
+       memcpy(cpdest, p, cplen);
        cpdest += cplen;
        nbytes -= cplen;
        if (!xdr_set_next_buffer(xdr))
index ea244b2..685e6d2 100644 (file)
@@ -1295,7 +1295,7 @@ void xprt_release(struct rpc_task *task)
        xprt_schedule_autodisconnect(xprt);
        spin_unlock_bh(&xprt->transport_lock);
        if (req->rq_buffer)
-               xprt->ops->buf_free(req->rq_buffer);
+               xprt->ops->buf_free(task);
        xprt_inject_disconnect(xprt);
        if (req->rq_cred != NULL)
                put_rpccred(req->rq_cred);
index 66c9d63..ae92a9e 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/cmpxchg.h>
 #include <linux/spinlock.h>
 #include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/addr.h>
 #include <linux/sunrpc/xprtmultipath.h>
 
 typedef struct rpc_xprt *(*xprt_switch_find_xprt_t)(struct list_head *head,
@@ -49,7 +50,8 @@ void rpc_xprt_switch_add_xprt(struct rpc_xprt_switch *xps,
        if (xprt == NULL)
                return;
        spin_lock(&xps->xps_lock);
-       if (xps->xps_net == xprt->xprt_net || xps->xps_net == NULL)
+       if ((xps->xps_net == xprt->xprt_net || xps->xps_net == NULL) &&
+           !rpc_xprt_switch_has_addr(xps, (struct sockaddr *)&xprt->addr))
                xprt_switch_add_xprt_locked(xps, xprt);
        spin_unlock(&xps->xps_lock);
 }
@@ -232,6 +234,26 @@ struct rpc_xprt *xprt_iter_current_entry(struct rpc_xprt_iter *xpi)
        return xprt_switch_find_current_entry(head, xpi->xpi_cursor);
 }
 
+bool rpc_xprt_switch_has_addr(struct rpc_xprt_switch *xps,
+                             const struct sockaddr *sap)
+{
+       struct list_head *head;
+       struct rpc_xprt *pos;
+
+       if (xps == NULL || sap == NULL)
+               return false;
+
+       head = &xps->xps_xprt_list;
+       list_for_each_entry_rcu(pos, head, xprt_switch) {
+               if (rpc_cmp_addr_port(sap, (struct sockaddr *)&pos->addr)) {
+                       pr_info("RPC:   addr %s already in xprt switch\n",
+                               pos->address_strings[RPC_DISPLAY_ADDR]);
+                       return true;
+               }
+       }
+       return false;
+}
+
 static
 struct rpc_xprt *xprt_switch_find_next_entry(struct list_head *head,
                const struct rpc_xprt *cur)
index 87762d9..2c472e1 100644 (file)
@@ -27,7 +27,7 @@ static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt,
        list_del(&req->rl_all);
        spin_unlock(&buf->rb_reqslock);
 
-       rpcrdma_destroy_req(&r_xprt->rx_ia, req);
+       rpcrdma_destroy_req(req);
 
        kfree(rqst);
 }
@@ -35,10 +35,8 @@ static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt,
 static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
                                 struct rpc_rqst *rqst)
 {
-       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
        struct rpcrdma_regbuf *rb;
        struct rpcrdma_req *req;
-       struct xdr_buf *buf;
        size_t size;
 
        req = rpcrdma_create_req(r_xprt);
@@ -46,30 +44,19 @@ static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt,
                return PTR_ERR(req);
        req->rl_backchannel = true;
 
-       size = RPCRDMA_INLINE_WRITE_THRESHOLD(rqst);
-       rb = rpcrdma_alloc_regbuf(ia, size, GFP_KERNEL);
+       rb = rpcrdma_alloc_regbuf(RPCRDMA_HDRBUF_SIZE,
+                                 DMA_TO_DEVICE, GFP_KERNEL);
        if (IS_ERR(rb))
                goto out_fail;
        req->rl_rdmabuf = rb;
 
-       size += RPCRDMA_INLINE_READ_THRESHOLD(rqst);
-       rb = rpcrdma_alloc_regbuf(ia, size, GFP_KERNEL);
+       size = r_xprt->rx_data.inline_rsize;
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL);
        if (IS_ERR(rb))
                goto out_fail;
-       rb->rg_owner = req;
        req->rl_sendbuf = rb;
-       /* so that rpcr_to_rdmar works when receiving a request */
-       rqst->rq_buffer = (void *)req->rl_sendbuf->rg_base;
-
-       buf = &rqst->rq_snd_buf;
-       buf->head[0].iov_base = rqst->rq_buffer;
-       buf->head[0].iov_len = 0;
-       buf->tail[0].iov_base = NULL;
-       buf->tail[0].iov_len = 0;
-       buf->page_len = 0;
-       buf->len = 0;
-       buf->buflen = size;
-
+       xdr_buf_init(&rqst->rq_snd_buf, rb->rg_base, size);
+       rpcrdma_set_xprtdata(rqst, req);
        return 0;
 
 out_fail:
@@ -219,7 +206,6 @@ int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst)
        struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
        struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
        struct rpcrdma_msg *headerp;
-       size_t rpclen;
 
        headerp = rdmab_to_msg(req->rl_rdmabuf);
        headerp->rm_xid = rqst->rq_xid;
@@ -231,26 +217,9 @@ int rpcrdma_bc_marshal_reply(struct rpc_rqst *rqst)
        headerp->rm_body.rm_chunks[1] = xdr_zero;
        headerp->rm_body.rm_chunks[2] = xdr_zero;
 
-       rpclen = rqst->rq_svec[0].iov_len;
-
-#ifdef RPCRDMA_BACKCHANNEL_DEBUG
-       pr_info("RPC:       %s: rpclen %zd headerp 0x%p lkey 0x%x\n",
-               __func__, rpclen, headerp, rdmab_lkey(req->rl_rdmabuf));
-       pr_info("RPC:       %s: RPC/RDMA: %*ph\n",
-               __func__, (int)RPCRDMA_HDRLEN_MIN, headerp);
-       pr_info("RPC:       %s:      RPC: %*ph\n",
-               __func__, (int)rpclen, rqst->rq_svec[0].iov_base);
-#endif
-
-       req->rl_send_iov[0].addr = rdmab_addr(req->rl_rdmabuf);
-       req->rl_send_iov[0].length = RPCRDMA_HDRLEN_MIN;
-       req->rl_send_iov[0].lkey = rdmab_lkey(req->rl_rdmabuf);
-
-       req->rl_send_iov[1].addr = rdmab_addr(req->rl_sendbuf);
-       req->rl_send_iov[1].length = rpclen;
-       req->rl_send_iov[1].lkey = rdmab_lkey(req->rl_sendbuf);
-
-       req->rl_niovs = 2;
+       if (!rpcrdma_prepare_send_sges(&r_xprt->rx_ia, req, RPCRDMA_HDRLEN_MIN,
+                                      &rqst->rq_snd_buf, rpcrdma_noch))
+               return -EIO;
        return 0;
 }
 
@@ -402,7 +371,7 @@ out_overflow:
 out_short:
        pr_warn("RPC/RDMA short backward direction call\n");
 
-       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, &r_xprt->rx_ep, rep))
+       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, rep))
                xprt_disconnect_done(xprt);
        else
                pr_warn("RPC:       %s: reposting rep %p\n",
index 21cb3b1..1ebb09e 100644 (file)
@@ -160,9 +160,8 @@ static int
 fmr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
            struct rpcrdma_create_data_internal *cdata)
 {
-       rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
-                                                     RPCRDMA_MAX_DATA_SEGS /
-                                                     RPCRDMA_MAX_FMR_SGES));
+       ia->ri_max_segs = max_t(unsigned int, 1, RPCRDMA_MAX_DATA_SEGS /
+                               RPCRDMA_MAX_FMR_SGES);
        return 0;
 }
 
@@ -274,6 +273,7 @@ fmr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         */
        list_for_each_entry(mw, &req->rl_registered, mw_list)
                list_add_tail(&mw->fmr.fm_mr->list, &unmap_list);
+       r_xprt->rx_stats.local_inv_needed++;
        rc = ib_unmap_fmr(&unmap_list);
        if (rc)
                goto out_reset;
@@ -331,4 +331,5 @@ const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops = {
        .ro_init_mr                     = fmr_op_init_mr,
        .ro_release_mr                  = fmr_op_release_mr,
        .ro_displayname                 = "fmr",
+       .ro_send_w_inv_ok               = 0,
 };
index 892b5e1..2109495 100644 (file)
@@ -67,6 +67,8 @@
  * pending send queue WRs before the transport is reconnected.
  */
 
+#include <linux/sunrpc/rpc_rdma.h>
+
 #include "xprt_rdma.h"
 
 #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
@@ -161,7 +163,7 @@ __frwr_reset_mr(struct rpcrdma_ia *ia, struct rpcrdma_mw *r)
                return PTR_ERR(f->fr_mr);
        }
 
-       dprintk("RPC:       %s: recovered FRMR %p\n", __func__, r);
+       dprintk("RPC:       %s: recovered FRMR %p\n", __func__, f);
        f->fr_state = FRMR_IS_INVALID;
        return 0;
 }
@@ -242,9 +244,8 @@ frwr_op_open(struct rpcrdma_ia *ia, struct rpcrdma_ep *ep,
                                               depth;
        }
 
-       rpcrdma_set_max_header_sizes(ia, cdata, max_t(unsigned int, 1,
-                                                     RPCRDMA_MAX_DATA_SEGS /
-                                                     ia->ri_max_frmr_depth));
+       ia->ri_max_segs = max_t(unsigned int, 1, RPCRDMA_MAX_DATA_SEGS /
+                               ia->ri_max_frmr_depth);
        return 0;
 }
 
@@ -329,7 +330,7 @@ frwr_wc_localinv_wake(struct ib_cq *cq, struct ib_wc *wc)
        frmr = container_of(cqe, struct rpcrdma_frmr, fr_cqe);
        if (wc->status != IB_WC_SUCCESS)
                __frwr_sendcompletion_flush(wc, frmr, "localinv");
-       complete_all(&frmr->fr_linv_done);
+       complete(&frmr->fr_linv_done);
 }
 
 /* Post a REG_MR Work Request to register a memory region
@@ -396,7 +397,7 @@ frwr_op_map(struct rpcrdma_xprt *r_xprt, struct rpcrdma_mr_seg *seg,
                goto out_mapmr_err;
 
        dprintk("RPC:       %s: Using frmr %p to map %u segments (%u bytes)\n",
-               __func__, mw, mw->mw_nents, mr->length);
+               __func__, frmr, mw->mw_nents, mr->length);
 
        key = (u8)(mr->rkey & 0x000000FF);
        ib_update_fast_reg_key(mr, ++key);
@@ -449,6 +450,8 @@ __frwr_prepare_linv_wr(struct rpcrdma_mw *mw)
        struct rpcrdma_frmr *f = &mw->frmr;
        struct ib_send_wr *invalidate_wr;
 
+       dprintk("RPC:       %s: invalidating frmr %p\n", __func__, f);
+
        f->fr_state = FRMR_IS_INVALID;
        invalidate_wr = &f->fr_invwr;
 
@@ -472,6 +475,7 @@ static void
 frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
 {
        struct ib_send_wr *invalidate_wrs, *pos, *prev, *bad_wr;
+       struct rpcrdma_rep *rep = req->rl_reply;
        struct rpcrdma_ia *ia = &r_xprt->rx_ia;
        struct rpcrdma_mw *mw, *tmp;
        struct rpcrdma_frmr *f;
@@ -487,6 +491,12 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
        f = NULL;
        invalidate_wrs = pos = prev = NULL;
        list_for_each_entry(mw, &req->rl_registered, mw_list) {
+               if ((rep->rr_wc_flags & IB_WC_WITH_INVALIDATE) &&
+                   (mw->mw_handle == rep->rr_inv_rkey)) {
+                       mw->frmr.fr_state = FRMR_IS_INVALID;
+                       continue;
+               }
+
                pos = __frwr_prepare_linv_wr(mw);
 
                if (!invalidate_wrs)
@@ -496,6 +506,8 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
                prev = pos;
                f = &mw->frmr;
        }
+       if (!f)
+               goto unmap;
 
        /* Strong send queue ordering guarantees that when the
         * last WR in the chain completes, all WRs in the chain
@@ -510,6 +522,7 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         * replaces the QP. The RPC reply handler won't call us
         * unless ri_id->qp is a valid pointer.
         */
+       r_xprt->rx_stats.local_inv_needed++;
        rc = ib_post_send(ia->ri_id->qp, invalidate_wrs, &bad_wr);
        if (rc)
                goto reset_mrs;
@@ -521,6 +534,8 @@ frwr_op_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)
         */
 unmap:
        list_for_each_entry_safe(mw, tmp, &req->rl_registered, mw_list) {
+               dprintk("RPC:       %s: unmapping frmr %p\n",
+                       __func__, &mw->frmr);
                list_del_init(&mw->mw_list);
                ib_dma_unmap_sg(ia->ri_device,
                                mw->mw_sg, mw->mw_nents, mw->mw_dir);
@@ -576,4 +591,5 @@ const struct rpcrdma_memreg_ops rpcrdma_frwr_memreg_ops = {
        .ro_init_mr                     = frwr_op_init_mr,
        .ro_release_mr                  = frwr_op_release_mr,
        .ro_displayname                 = "frwr",
+       .ro_send_w_inv_ok               = RPCRDMA_CMP_F_SND_W_INV_OK,
 };
index a47f170..d987c2d 100644 (file)
 # define RPCDBG_FACILITY       RPCDBG_TRANS
 #endif
 
-enum rpcrdma_chunktype {
-       rpcrdma_noch = 0,
-       rpcrdma_readch,
-       rpcrdma_areadch,
-       rpcrdma_writech,
-       rpcrdma_replych
-};
-
 static const char transfertypes[][12] = {
        "inline",       /* no chunks */
        "read list",    /* some argument via rdma read */
@@ -118,10 +110,12 @@ static unsigned int rpcrdma_max_reply_header_size(unsigned int maxsegs)
        return size;
 }
 
-void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *ia,
-                                 struct rpcrdma_create_data_internal *cdata,
-                                 unsigned int maxsegs)
+void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *r_xprt)
 {
+       struct rpcrdma_create_data_internal *cdata = &r_xprt->rx_data;
+       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
+       unsigned int maxsegs = ia->ri_max_segs;
+
        ia->ri_max_inline_write = cdata->inline_wsize -
                                  rpcrdma_max_call_header_size(maxsegs);
        ia->ri_max_inline_read = cdata->inline_rsize -
@@ -155,42 +149,6 @@ static bool rpcrdma_results_inline(struct rpcrdma_xprt *r_xprt,
        return rqst->rq_rcv_buf.buflen <= ia->ri_max_inline_read;
 }
 
-static int
-rpcrdma_tail_pullup(struct xdr_buf *buf)
-{
-       size_t tlen = buf->tail[0].iov_len;
-       size_t skip = tlen & 3;
-
-       /* Do not include the tail if it is only an XDR pad */
-       if (tlen < 4)
-               return 0;
-
-       /* xdr_write_pages() adds a pad at the beginning of the tail
-        * if the content in "buf->pages" is unaligned. Force the
-        * tail's actual content to land at the next XDR position
-        * after the head instead.
-        */
-       if (skip) {
-               unsigned char *src, *dst;
-               unsigned int count;
-
-               src = buf->tail[0].iov_base;
-               dst = buf->head[0].iov_base;
-               dst += buf->head[0].iov_len;
-
-               src += skip;
-               tlen -= skip;
-
-               dprintk("RPC:       %s: skip=%zu, memmove(%p, %p, %zu)\n",
-                       __func__, skip, dst, src, tlen);
-
-               for (count = tlen; count; count--)
-                       *dst++ = *src++;
-       }
-
-       return tlen;
-}
-
 /* Split "vec" on page boundaries into segments. FMR registers pages,
  * not a byte range. Other modes coalesce these segments into a single
  * MR when they can.
@@ -229,7 +187,8 @@ rpcrdma_convert_kvec(struct kvec *vec, struct rpcrdma_mr_seg *seg, int n)
 
 static int
 rpcrdma_convert_iovs(struct xdr_buf *xdrbuf, unsigned int pos,
-       enum rpcrdma_chunktype type, struct rpcrdma_mr_seg *seg)
+       enum rpcrdma_chunktype type, struct rpcrdma_mr_seg *seg,
+       bool reminv_expected)
 {
        int len, n, p, page_base;
        struct page **ppages;
@@ -271,6 +230,13 @@ rpcrdma_convert_iovs(struct xdr_buf *xdrbuf, unsigned int pos,
        if (type == rpcrdma_readch)
                return n;
 
+       /* When encoding the Write list, some servers need to see an extra
+        * segment for odd-length Write chunks. The upper layer provides
+        * space in the tail iovec for this purpose.
+        */
+       if (type == rpcrdma_writech && reminv_expected)
+               return n;
+
        if (xdrbuf->tail[0].iov_len) {
                /* the rpcrdma protocol allows us to omit any trailing
                 * xdr pad bytes, saving the server an RDMA operation. */
@@ -327,7 +293,7 @@ rpcrdma_encode_read_list(struct rpcrdma_xprt *r_xprt,
        if (rtype == rpcrdma_areadch)
                pos = 0;
        seg = req->rl_segments;
-       nsegs = rpcrdma_convert_iovs(&rqst->rq_snd_buf, pos, rtype, seg);
+       nsegs = rpcrdma_convert_iovs(&rqst->rq_snd_buf, pos, rtype, seg, false);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -391,7 +357,8 @@ rpcrdma_encode_write_list(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
        seg = req->rl_segments;
        nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf,
                                     rqst->rq_rcv_buf.head[0].iov_len,
-                                    wtype, seg);
+                                    wtype, seg,
+                                    r_xprt->rx_ia.ri_reminv_expected);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -456,7 +423,8 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
        }
 
        seg = req->rl_segments;
-       nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf, 0, wtype, seg);
+       nsegs = rpcrdma_convert_iovs(&rqst->rq_rcv_buf, 0, wtype, seg,
+                                    r_xprt->rx_ia.ri_reminv_expected);
        if (nsegs < 0)
                return ERR_PTR(nsegs);
 
@@ -491,74 +459,184 @@ rpcrdma_encode_reply_chunk(struct rpcrdma_xprt *r_xprt,
        return iptr;
 }
 
-/*
- * Copy write data inline.
- * This function is used for "small" requests. Data which is passed
- * to RPC via iovecs (or page list) is copied directly into the
- * pre-registered memory buffer for this request. For small amounts
- * of data, this is efficient. The cutoff value is tunable.
+/* Prepare the RPC-over-RDMA header SGE.
  */
-static void rpcrdma_inline_pullup(struct rpc_rqst *rqst)
+static bool
+rpcrdma_prepare_hdr_sge(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                       u32 len)
 {
-       int i, npages, curlen;
-       int copy_len;
-       unsigned char *srcp, *destp;
-       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
-       int page_base;
-       struct page **ppages;
+       struct rpcrdma_regbuf *rb = req->rl_rdmabuf;
+       struct ib_sge *sge = &req->rl_send_sge[0];
+
+       if (unlikely(!rpcrdma_regbuf_is_mapped(rb))) {
+               if (!__rpcrdma_dma_map_regbuf(ia, rb))
+                       return false;
+               sge->addr = rdmab_addr(rb);
+               sge->lkey = rdmab_lkey(rb);
+       }
+       sge->length = len;
+
+       ib_dma_sync_single_for_device(ia->ri_device, sge->addr,
+                                     sge->length, DMA_TO_DEVICE);
+       req->rl_send_wr.num_sge++;
+       return true;
+}
 
-       destp = rqst->rq_svec[0].iov_base;
-       curlen = rqst->rq_svec[0].iov_len;
-       destp += curlen;
+/* Prepare the Send SGEs. The head and tail iovec, and each entry
+ * in the page list, gets its own SGE.
+ */
+static bool
+rpcrdma_prepare_msg_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                        struct xdr_buf *xdr, enum rpcrdma_chunktype rtype)
+{
+       unsigned int sge_no, page_base, len, remaining;
+       struct rpcrdma_regbuf *rb = req->rl_sendbuf;
+       struct ib_device *device = ia->ri_device;
+       struct ib_sge *sge = req->rl_send_sge;
+       u32 lkey = ia->ri_pd->local_dma_lkey;
+       struct page *page, **ppages;
+
+       /* The head iovec is straightforward, as it is already
+        * DMA-mapped. Sync the content that has changed.
+        */
+       if (!rpcrdma_dma_map_regbuf(ia, rb))
+               return false;
+       sge_no = 1;
+       sge[sge_no].addr = rdmab_addr(rb);
+       sge[sge_no].length = xdr->head[0].iov_len;
+       sge[sge_no].lkey = rdmab_lkey(rb);
+       ib_dma_sync_single_for_device(device, sge[sge_no].addr,
+                                     sge[sge_no].length, DMA_TO_DEVICE);
+
+       /* If there is a Read chunk, the page list is being handled
+        * via explicit RDMA, and thus is skipped here. However, the
+        * tail iovec may include an XDR pad for the page list, as
+        * well as additional content, and may not reside in the
+        * same page as the head iovec.
+        */
+       if (rtype == rpcrdma_readch) {
+               len = xdr->tail[0].iov_len;
 
-       dprintk("RPC:       %s: destp 0x%p len %d hdrlen %d\n",
-               __func__, destp, rqst->rq_slen, curlen);
+               /* Do not include the tail if it is only an XDR pad */
+               if (len < 4)
+                       goto out;
 
-       copy_len = rqst->rq_snd_buf.page_len;
+               page = virt_to_page(xdr->tail[0].iov_base);
+               page_base = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
 
-       if (rqst->rq_snd_buf.tail[0].iov_len) {
-               curlen = rqst->rq_snd_buf.tail[0].iov_len;
-               if (destp + copy_len != rqst->rq_snd_buf.tail[0].iov_base) {
-                       memmove(destp + copy_len,
-                               rqst->rq_snd_buf.tail[0].iov_base, curlen);
-                       r_xprt->rx_stats.pullup_copy_count += curlen;
+               /* If the content in the page list is an odd length,
+                * xdr_write_pages() has added a pad at the beginning
+                * of the tail iovec. Force the tail's non-pad content
+                * to land at the next XDR position in the Send message.
+                */
+               page_base += len & 3;
+               len -= len & 3;
+               goto map_tail;
+       }
+
+       /* If there is a page list present, temporarily DMA map
+        * and prepare an SGE for each page to be sent.
+        */
+       if (xdr->page_len) {
+               ppages = xdr->pages + (xdr->page_base >> PAGE_SHIFT);
+               page_base = xdr->page_base & ~PAGE_MASK;
+               remaining = xdr->page_len;
+               while (remaining) {
+                       sge_no++;
+                       if (sge_no > RPCRDMA_MAX_SEND_SGES - 2)
+                               goto out_mapping_overflow;
+
+                       len = min_t(u32, PAGE_SIZE - page_base, remaining);
+                       sge[sge_no].addr = ib_dma_map_page(device, *ppages,
+                                                          page_base, len,
+                                                          DMA_TO_DEVICE);
+                       if (ib_dma_mapping_error(device, sge[sge_no].addr))
+                               goto out_mapping_err;
+                       sge[sge_no].length = len;
+                       sge[sge_no].lkey = lkey;
+
+                       req->rl_mapped_sges++;
+                       ppages++;
+                       remaining -= len;
+                       page_base = 0;
                }
-               dprintk("RPC:       %s: tail destp 0x%p len %d\n",
-                       __func__, destp + copy_len, curlen);
-               rqst->rq_svec[0].iov_len += curlen;
        }
-       r_xprt->rx_stats.pullup_copy_count += copy_len;
 
-       page_base = rqst->rq_snd_buf.page_base;
-       ppages = rqst->rq_snd_buf.pages + (page_base >> PAGE_SHIFT);
-       page_base &= ~PAGE_MASK;
-       npages = PAGE_ALIGN(page_base+copy_len) >> PAGE_SHIFT;
-       for (i = 0; copy_len && i < npages; i++) {
-               curlen = PAGE_SIZE - page_base;
-               if (curlen > copy_len)
-                       curlen = copy_len;
-               dprintk("RPC:       %s: page %d destp 0x%p len %d curlen %d\n",
-                       __func__, i, destp, copy_len, curlen);
-               srcp = kmap_atomic(ppages[i]);
-               memcpy(destp, srcp+page_base, curlen);
-               kunmap_atomic(srcp);
-               rqst->rq_svec[0].iov_len += curlen;
-               destp += curlen;
-               copy_len -= curlen;
-               page_base = 0;
+       /* The tail iovec is not always constructed in the same
+        * page where the head iovec resides (see, for example,
+        * gss_wrap_req_priv). To neatly accommodate that case,
+        * DMA map it separately.
+        */
+       if (xdr->tail[0].iov_len) {
+               page = virt_to_page(xdr->tail[0].iov_base);
+               page_base = (unsigned long)xdr->tail[0].iov_base & ~PAGE_MASK;
+               len = xdr->tail[0].iov_len;
+
+map_tail:
+               sge_no++;
+               sge[sge_no].addr = ib_dma_map_page(device, page,
+                                                  page_base, len,
+                                                  DMA_TO_DEVICE);
+               if (ib_dma_mapping_error(device, sge[sge_no].addr))
+                       goto out_mapping_err;
+               sge[sge_no].length = len;
+               sge[sge_no].lkey = lkey;
+               req->rl_mapped_sges++;
        }
-       /* header now contains entire send message */
+
+out:
+       req->rl_send_wr.num_sge = sge_no + 1;
+       return true;
+
+out_mapping_overflow:
+       pr_err("rpcrdma: too many Send SGEs (%u)\n", sge_no);
+       return false;
+
+out_mapping_err:
+       pr_err("rpcrdma: Send mapping error\n");
+       return false;
+}
+
+bool
+rpcrdma_prepare_send_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req,
+                         u32 hdrlen, struct xdr_buf *xdr,
+                         enum rpcrdma_chunktype rtype)
+{
+       req->rl_send_wr.num_sge = 0;
+       req->rl_mapped_sges = 0;
+
+       if (!rpcrdma_prepare_hdr_sge(ia, req, hdrlen))
+               goto out_map;
+
+       if (rtype != rpcrdma_areadch)
+               if (!rpcrdma_prepare_msg_sges(ia, req, xdr, rtype))
+                       goto out_map;
+
+       return true;
+
+out_map:
+       pr_err("rpcrdma: failed to DMA map a Send buffer\n");
+       return false;
+}
+
+void
+rpcrdma_unmap_sges(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
+{
+       struct ib_device *device = ia->ri_device;
+       struct ib_sge *sge;
+       int count;
+
+       sge = &req->rl_send_sge[2];
+       for (count = req->rl_mapped_sges; count--; sge++)
+               ib_dma_unmap_page(device, sge->addr, sge->length,
+                                 DMA_TO_DEVICE);
+       req->rl_mapped_sges = 0;
 }
 
 /*
  * Marshal a request: the primary job of this routine is to choose
  * the transfer modes. See comments below.
  *
- * Prepares up to two IOVs per Call message:
- *
- *  [0] -- RPC RDMA header
- *  [1] -- the RPC header/data
- *
  * Returns zero on success, otherwise a negative errno.
  */
 
@@ -626,12 +704,11 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
         */
        if (rpcrdma_args_inline(r_xprt, rqst)) {
                rtype = rpcrdma_noch;
-               rpcrdma_inline_pullup(rqst);
-               rpclen = rqst->rq_svec[0].iov_len;
+               rpclen = rqst->rq_snd_buf.len;
        } else if (ddp_allowed && rqst->rq_snd_buf.flags & XDRBUF_WRITE) {
                rtype = rpcrdma_readch;
-               rpclen = rqst->rq_svec[0].iov_len;
-               rpclen += rpcrdma_tail_pullup(&rqst->rq_snd_buf);
+               rpclen = rqst->rq_snd_buf.head[0].iov_len +
+                        rqst->rq_snd_buf.tail[0].iov_len;
        } else {
                r_xprt->rx_stats.nomsg_call_count++;
                headerp->rm_type = htonl(RDMA_NOMSG);
@@ -673,34 +750,18 @@ rpcrdma_marshal_req(struct rpc_rqst *rqst)
                goto out_unmap;
        hdrlen = (unsigned char *)iptr - (unsigned char *)headerp;
 
-       if (hdrlen + rpclen > RPCRDMA_INLINE_WRITE_THRESHOLD(rqst))
-               goto out_overflow;
-
        dprintk("RPC: %5u %s: %s/%s: hdrlen %zd rpclen %zd\n",
                rqst->rq_task->tk_pid, __func__,
                transfertypes[rtype], transfertypes[wtype],
                hdrlen, rpclen);
 
-       req->rl_send_iov[0].addr = rdmab_addr(req->rl_rdmabuf);
-       req->rl_send_iov[0].length = hdrlen;
-       req->rl_send_iov[0].lkey = rdmab_lkey(req->rl_rdmabuf);
-
-       req->rl_niovs = 1;
-       if (rtype == rpcrdma_areadch)
-               return 0;
-
-       req->rl_send_iov[1].addr = rdmab_addr(req->rl_sendbuf);
-       req->rl_send_iov[1].length = rpclen;
-       req->rl_send_iov[1].lkey = rdmab_lkey(req->rl_sendbuf);
-
-       req->rl_niovs = 2;
+       if (!rpcrdma_prepare_send_sges(&r_xprt->rx_ia, req, hdrlen,
+                                      &rqst->rq_snd_buf, rtype)) {
+               iptr = ERR_PTR(-EIO);
+               goto out_unmap;
+       }
        return 0;
 
-out_overflow:
-       pr_err("rpcrdma: send overflow: hdrlen %zd rpclen %zu %s/%s\n",
-               hdrlen, rpclen, transfertypes[rtype], transfertypes[wtype]);
-       iptr = ERR_PTR(-EIO);
-
 out_unmap:
        r_xprt->rx_ia.ri_ops->ro_unmap_safe(r_xprt, req, false);
        return PTR_ERR(iptr);
@@ -916,8 +977,10 @@ rpcrdma_conn_func(struct rpcrdma_ep *ep)
  * allowed to timeout, to discover the errors at that time.
  */
 void
-rpcrdma_reply_handler(struct rpcrdma_rep *rep)
+rpcrdma_reply_handler(struct work_struct *work)
 {
+       struct rpcrdma_rep *rep =
+                       container_of(work, struct rpcrdma_rep, rr_work);
        struct rpcrdma_msg *headerp;
        struct rpcrdma_req *req;
        struct rpc_rqst *rqst;
@@ -1132,6 +1195,6 @@ out_duplicate:
 
 repost:
        r_xprt->rx_stats.bad_reply_count++;
-       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, &r_xprt->rx_ep, rep))
+       if (rpcrdma_ep_post_recv(&r_xprt->rx_ia, rep))
                rpcrdma_recv_buffer_put(rep);
 }
index a2a7519..2d8545c 100644 (file)
@@ -129,7 +129,7 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma,
                ret = -EIO;
                goto out_unmap;
        }
-       atomic_inc(&rdma->sc_dma_used);
+       svc_rdma_count_mappings(rdma, ctxt);
 
        memset(&send_wr, 0, sizeof(send_wr));
        ctxt->cqe.done = svc_rdma_wc_send;
@@ -159,33 +159,34 @@ out_unmap:
 /* Server-side transport endpoint wants a whole page for its send
  * buffer. The client RPC code constructs the RPC header in this
  * buffer before it invokes ->send_request.
- *
- * Returns NULL if there was a temporary allocation failure.
  */
-static void *
-xprt_rdma_bc_allocate(struct rpc_task *task, size_t size)
+static int
+xprt_rdma_bc_allocate(struct rpc_task *task)
 {
        struct rpc_rqst *rqst = task->tk_rqstp;
        struct svc_xprt *sxprt = rqst->rq_xprt->bc_xprt;
+       size_t size = rqst->rq_callsize;
        struct svcxprt_rdma *rdma;
        struct page *page;
 
        rdma = container_of(sxprt, struct svcxprt_rdma, sc_xprt);
 
-       /* Prevent an infinite loop: try to make this case work */
-       if (size > PAGE_SIZE)
+       if (size > PAGE_SIZE) {
                WARN_ONCE(1, "svcrdma: large bc buffer request (size %zu)\n",
                          size);
+               return -EINVAL;
+       }
 
        page = alloc_page(RPCRDMA_DEF_GFP);
        if (!page)
-               return NULL;
+               return -ENOMEM;
 
-       return page_address(page);
+       rqst->rq_buffer = page_address(page);
+       return 0;
 }
 
 static void
-xprt_rdma_bc_free(void *buffer)
+xprt_rdma_bc_free(struct rpc_task *task)
 {
        /* No-op: ctxt and page have already been freed. */
 }
index 2c25606..ad1df97 100644 (file)
@@ -159,7 +159,7 @@ int rdma_read_chunk_lcl(struct svcxprt_rdma *xprt,
                                           ctxt->sge[pno].addr);
                if (ret)
                        goto err;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
 
                ctxt->sge[pno].lkey = xprt->sc_pd->local_dma_lkey;
                ctxt->sge[pno].length = len;
index 54d5333..f5a91ed 100644 (file)
@@ -225,6 +225,48 @@ svc_rdma_get_reply_array(struct rpcrdma_msg *rmsgp,
        return rp_ary;
 }
 
+/* RPC-over-RDMA Version One private extension: Remote Invalidation.
+ * Responder's choice: requester signals it can handle Send With
+ * Invalidate, and responder chooses one rkey to invalidate.
+ *
+ * Find a candidate rkey to invalidate when sending a reply.  Picks the
+ * first rkey it finds in the chunks lists.
+ *
+ * Returns zero if RPC's chunk lists are empty.
+ */
+static u32 svc_rdma_get_inv_rkey(struct rpcrdma_msg *rdma_argp,
+                                struct rpcrdma_write_array *wr_ary,
+                                struct rpcrdma_write_array *rp_ary)
+{
+       struct rpcrdma_read_chunk *rd_ary;
+       struct rpcrdma_segment *arg_ch;
+       u32 inv_rkey;
+
+       inv_rkey = 0;
+
+       rd_ary = svc_rdma_get_read_chunk(rdma_argp);
+       if (rd_ary) {
+               inv_rkey = be32_to_cpu(rd_ary->rc_target.rs_handle);
+               goto out;
+       }
+
+       if (wr_ary && be32_to_cpu(wr_ary->wc_nchunks)) {
+               arg_ch = &wr_ary->wc_array[0].wc_target;
+               inv_rkey = be32_to_cpu(arg_ch->rs_handle);
+               goto out;
+       }
+
+       if (rp_ary && be32_to_cpu(rp_ary->wc_nchunks)) {
+               arg_ch = &rp_ary->wc_array[0].wc_target;
+               inv_rkey = be32_to_cpu(arg_ch->rs_handle);
+               goto out;
+       }
+
+out:
+       dprintk("svcrdma: Send With Invalidate rkey=%08x\n", inv_rkey);
+       return inv_rkey;
+}
+
 /* Assumptions:
  * - The specified write_len can be represented in sc_max_sge * PAGE_SIZE
  */
@@ -280,7 +322,7 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
                if (ib_dma_mapping_error(xprt->sc_cm_id->device,
                                         sge[sge_no].addr))
                        goto err;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
                sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey;
                ctxt->count++;
                sge_off = 0;
@@ -464,7 +506,8 @@ static int send_reply(struct svcxprt_rdma *rdma,
                      struct page *page,
                      struct rpcrdma_msg *rdma_resp,
                      struct svc_rdma_req_map *vec,
-                     int byte_count)
+                     int byte_count,
+                     u32 inv_rkey)
 {
        struct svc_rdma_op_ctxt *ctxt;
        struct ib_send_wr send_wr;
@@ -489,7 +532,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
                            ctxt->sge[0].length, DMA_TO_DEVICE);
        if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
                goto err;
-       atomic_inc(&rdma->sc_dma_used);
+       svc_rdma_count_mappings(rdma, ctxt);
 
        ctxt->direction = DMA_TO_DEVICE;
 
@@ -505,7 +548,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
                if (ib_dma_mapping_error(rdma->sc_cm_id->device,
                                         ctxt->sge[sge_no].addr))
                        goto err;
-               atomic_inc(&rdma->sc_dma_used);
+               svc_rdma_count_mappings(rdma, ctxt);
                ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey;
                ctxt->sge[sge_no].length = sge_bytes;
        }
@@ -523,23 +566,9 @@ static int send_reply(struct svcxprt_rdma *rdma,
                ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
                ctxt->count++;
                rqstp->rq_respages[page_no] = NULL;
-               /*
-                * If there are more pages than SGE, terminate SGE
-                * list so that svc_rdma_unmap_dma doesn't attempt to
-                * unmap garbage.
-                */
-               if (page_no+1 >= sge_no)
-                       ctxt->sge[page_no+1].length = 0;
        }
        rqstp->rq_next_page = rqstp->rq_respages + 1;
 
-       /* The loop above bumps sc_dma_used for each sge. The
-        * xdr_buf.tail gets a separate sge, but resides in the
-        * same page as xdr_buf.head. Don't count it twice.
-        */
-       if (sge_no > ctxt->count)
-               atomic_dec(&rdma->sc_dma_used);
-
        if (sge_no > rdma->sc_max_sge) {
                pr_err("svcrdma: Too many sges (%d)\n", sge_no);
                goto err;
@@ -549,7 +578,11 @@ static int send_reply(struct svcxprt_rdma *rdma,
        send_wr.wr_cqe = &ctxt->cqe;
        send_wr.sg_list = ctxt->sge;
        send_wr.num_sge = sge_no;
-       send_wr.opcode = IB_WR_SEND;
+       if (inv_rkey) {
+               send_wr.opcode = IB_WR_SEND_WITH_INV;
+               send_wr.ex.invalidate_rkey = inv_rkey;
+       } else
+               send_wr.opcode = IB_WR_SEND;
        send_wr.send_flags =  IB_SEND_SIGNALED;
 
        ret = svc_rdma_send(rdma, &send_wr);
@@ -581,6 +614,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        int inline_bytes;
        struct page *res_page;
        struct svc_rdma_req_map *vec;
+       u32 inv_rkey;
 
        dprintk("svcrdma: sending response for rqstp=%p\n", rqstp);
 
@@ -591,6 +625,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
        wr_ary = svc_rdma_get_write_array(rdma_argp);
        rp_ary = svc_rdma_get_reply_array(rdma_argp, wr_ary);
 
+       inv_rkey = 0;
+       if (rdma->sc_snd_w_inv)
+               inv_rkey = svc_rdma_get_inv_rkey(rdma_argp, wr_ary, rp_ary);
+
        /* Build an req vec for the XDR */
        vec = svc_rdma_get_req_map(rdma);
        ret = svc_rdma_map_xdr(rdma, &rqstp->rq_res, vec, wr_ary != NULL);
@@ -633,9 +671,9 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
                goto err1;
 
        ret = send_reply(rdma, rqstp, res_page, rdma_resp, vec,
-                        inline_bytes);
+                        inline_bytes, inv_rkey);
        if (ret < 0)
-               goto err1;
+               goto err0;
 
        svc_rdma_put_req_map(rdma, vec);
        dprintk("svcrdma: send_reply returns %d\n", ret);
@@ -692,7 +730,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
                svc_rdma_put_context(ctxt, 1);
                return;
        }
-       atomic_inc(&xprt->sc_dma_used);
+       svc_rdma_count_mappings(xprt, ctxt);
 
        /* Prepare SEND WR */
        memset(&err_wr, 0, sizeof(err_wr));
index eb2857f..6864fb9 100644 (file)
@@ -198,6 +198,7 @@ struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
 
 out:
        ctxt->count = 0;
+       ctxt->mapped_sges = 0;
        ctxt->frmr = NULL;
        return ctxt;
 
@@ -221,22 +222,27 @@ out_empty:
 void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
 {
        struct svcxprt_rdma *xprt = ctxt->xprt;
-       int i;
-       for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) {
+       struct ib_device *device = xprt->sc_cm_id->device;
+       u32 lkey = xprt->sc_pd->local_dma_lkey;
+       unsigned int i, count;
+
+       for (count = 0, i = 0; i < ctxt->mapped_sges; i++) {
                /*
                 * Unmap the DMA addr in the SGE if the lkey matches
                 * the local_dma_lkey, otherwise, ignore it since it is
                 * an FRMR lkey and will be unmapped later when the
                 * last WR that uses it completes.
                 */
-               if (ctxt->sge[i].lkey == xprt->sc_pd->local_dma_lkey) {
-                       atomic_dec(&xprt->sc_dma_used);
-                       ib_dma_unmap_page(xprt->sc_cm_id->device,
+               if (ctxt->sge[i].lkey == lkey) {
+                       count++;
+                       ib_dma_unmap_page(device,
                                            ctxt->sge[i].addr,
                                            ctxt->sge[i].length,
                                            ctxt->direction);
                }
        }
+       ctxt->mapped_sges = 0;
+       atomic_sub(count, &xprt->sc_dma_used);
 }
 
 void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages)
@@ -600,7 +606,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt, gfp_t flags)
                                     DMA_FROM_DEVICE);
                if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
                        goto err_put_ctxt;
-               atomic_inc(&xprt->sc_dma_used);
+               svc_rdma_count_mappings(xprt, ctxt);
                ctxt->sge[sge_no].addr = pa;
                ctxt->sge[sge_no].length = PAGE_SIZE;
                ctxt->sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey;
@@ -642,6 +648,26 @@ int svc_rdma_repost_recv(struct svcxprt_rdma *xprt, gfp_t flags)
        return ret;
 }
 
+static void
+svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt,
+                              struct rdma_conn_param *param)
+{
+       const struct rpcrdma_connect_private *pmsg = param->private_data;
+
+       if (pmsg &&
+           pmsg->cp_magic == rpcrdma_cmp_magic &&
+           pmsg->cp_version == RPCRDMA_CMP_VERSION) {
+               newxprt->sc_snd_w_inv = pmsg->cp_flags &
+                                       RPCRDMA_CMP_F_SND_W_INV_OK;
+
+               dprintk("svcrdma: client send_size %u, recv_size %u "
+                       "remote inv %ssupported\n",
+                       rpcrdma_decode_buffer_size(pmsg->cp_send_size),
+                       rpcrdma_decode_buffer_size(pmsg->cp_recv_size),
+                       newxprt->sc_snd_w_inv ? "" : "un");
+       }
+}
+
 /*
  * This function handles the CONNECT_REQUEST event on a listening
  * endpoint. It is passed the cma_id for the _new_ connection. The context in
@@ -653,7 +679,8 @@ int svc_rdma_repost_recv(struct svcxprt_rdma *xprt, gfp_t flags)
  * will call the recvfrom method on the listen xprt which will accept the new
  * connection.
  */
-static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
+static void handle_connect_req(struct rdma_cm_id *new_cma_id,
+                              struct rdma_conn_param *param)
 {
        struct svcxprt_rdma *listen_xprt = new_cma_id->context;
        struct svcxprt_rdma *newxprt;
@@ -669,9 +696,10 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
        new_cma_id->context = newxprt;
        dprintk("svcrdma: Creating newxprt=%p, cm_id=%p, listenxprt=%p\n",
                newxprt, newxprt->sc_cm_id, listen_xprt);
+       svc_rdma_parse_connect_private(newxprt, param);
 
        /* Save client advertised inbound read limit for use later in accept. */
-       newxprt->sc_ord = client_ird;
+       newxprt->sc_ord = param->initiator_depth;
 
        /* Set the local and remote addresses in the transport */
        sa = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr;
@@ -706,8 +734,7 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
                dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
                        "event = %s (%d)\n", cma_id, cma_id->context,
                        rdma_event_msg(event->event), event->event);
-               handle_connect_req(cma_id,
-                                  event->param.conn.initiator_depth);
+               handle_connect_req(cma_id, &event->param.conn);
                break;
 
        case RDMA_CM_EVENT_ESTABLISHED:
@@ -941,6 +968,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        struct svcxprt_rdma *listen_rdma;
        struct svcxprt_rdma *newxprt = NULL;
        struct rdma_conn_param conn_param;
+       struct rpcrdma_connect_private pmsg;
        struct ib_qp_init_attr qp_attr;
        struct ib_device *dev;
        unsigned int i;
@@ -1070,7 +1098,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
                        dev->attrs.max_fast_reg_page_list_len;
                newxprt->sc_dev_caps |= SVCRDMA_DEVCAP_FAST_REG;
                newxprt->sc_reader = rdma_read_chunk_frmr;
-       }
+       } else
+               newxprt->sc_snd_w_inv = false;
 
        /*
         * Determine if a DMA MR is required and if so, what privs are required
@@ -1094,11 +1123,20 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
        /* Swap out the handler */
        newxprt->sc_cm_id->event_handler = rdma_cma_handler;
 
+       /* Construct RDMA-CM private message */
+       pmsg.cp_magic = rpcrdma_cmp_magic;
+       pmsg.cp_version = RPCRDMA_CMP_VERSION;
+       pmsg.cp_flags = 0;
+       pmsg.cp_send_size = pmsg.cp_recv_size =
+               rpcrdma_encode_buffer_size(newxprt->sc_max_req_size);
+
        /* Accept Connection */
        set_bit(RDMAXPRT_CONN_PENDING, &newxprt->sc_flags);
        memset(&conn_param, 0, sizeof conn_param);
        conn_param.responder_resources = 0;
        conn_param.initiator_depth = newxprt->sc_ord;
+       conn_param.private_data = &pmsg;
+       conn_param.private_data_len = sizeof(pmsg);
        ret = rdma_accept(newxprt->sc_cm_id, &conn_param);
        if (ret) {
                dprintk("svcrdma: failed to accept new connection, ret=%d\n",
index 81f0e87..ed5e285 100644 (file)
@@ -97,7 +97,7 @@ static struct ctl_table xr_tunables_table[] = {
                .data           = &xprt_rdma_max_inline_read,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = &min_inline_size,
                .extra2         = &max_inline_size,
        },
@@ -106,7 +106,7 @@ static struct ctl_table xr_tunables_table[] = {
                .data           = &xprt_rdma_max_inline_write,
                .maxlen         = sizeof(unsigned int),
                .mode           = 0644,
-               .proc_handler   = proc_dointvec,
+               .proc_handler   = proc_dointvec_minmax,
                .extra1         = &min_inline_size,
                .extra2         = &max_inline_size,
        },
@@ -477,115 +477,152 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)
        }
 }
 
-/*
- * The RDMA allocate/free functions need the task structure as a place
- * to hide the struct rpcrdma_req, which is necessary for the actual send/recv
- * sequence.
+/* Allocate a fixed-size buffer in which to construct and send the
+ * RPC-over-RDMA header for this request.
+ */
+static bool
+rpcrdma_get_rdmabuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   gfp_t flags)
+{
+       size_t size = RPCRDMA_HDRBUF_SIZE;
+       struct rpcrdma_regbuf *rb;
+
+       if (req->rl_rdmabuf)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_rdmabuf = rb;
+       return true;
+}
+
+static bool
+rpcrdma_get_sendbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   size_t size, gfp_t flags)
+{
+       struct rpcrdma_regbuf *rb;
+
+       if (req->rl_sendbuf && rdmab_length(req->rl_sendbuf) >= size)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       rpcrdma_free_regbuf(req->rl_sendbuf);
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_sendbuf = rb;
+       return true;
+}
+
+/* The rq_rcv_buf is used only if a Reply chunk is necessary.
+ * The decision to use a Reply chunk is made later in
+ * rpcrdma_marshal_req. This buffer is registered at that time.
  *
- * The RPC layer allocates both send and receive buffers in the same call
- * (rq_send_buf and rq_rcv_buf are both part of a single contiguous buffer).
- * We may register rq_rcv_buf when using reply chunks.
+ * Otherwise, the associated RPC Reply arrives in a separate
+ * Receive buffer, arbitrarily chosen by the HCA. The buffer
+ * allocated here for the RPC Reply is not utilized in that
+ * case. See rpcrdma_inline_fixup.
+ *
+ * A regbuf is used here to remember the buffer size.
  */
-static void *
-xprt_rdma_allocate(struct rpc_task *task, size_t size)
+static bool
+rpcrdma_get_recvbuf(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req,
+                   size_t size, gfp_t flags)
 {
-       struct rpc_xprt *xprt = task->tk_rqstp->rq_xprt;
-       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt);
        struct rpcrdma_regbuf *rb;
+
+       if (req->rl_recvbuf && rdmab_length(req->rl_recvbuf) >= size)
+               return true;
+
+       rb = rpcrdma_alloc_regbuf(size, DMA_NONE, flags);
+       if (IS_ERR(rb))
+               return false;
+
+       rpcrdma_free_regbuf(req->rl_recvbuf);
+       r_xprt->rx_stats.hardway_register_count += size;
+       req->rl_recvbuf = rb;
+       return true;
+}
+
+/**
+ * xprt_rdma_allocate - allocate transport resources for an RPC
+ * @task: RPC task
+ *
+ * Return values:
+ *        0:   Success; rq_buffer points to RPC buffer to use
+ *   ENOMEM:   Out of memory, call again later
+ *      EIO:   A permanent error occurred, do not retry
+ *
+ * The RDMA allocate/free functions need the task structure as a place
+ * to hide the struct rpcrdma_req, which is necessary for the actual
+ * send/recv sequence.
+ *
+ * xprt_rdma_allocate provides buffers that are already mapped for
+ * DMA, and a local DMA lkey is provided for each.
+ */
+static int
+xprt_rdma_allocate(struct rpc_task *task)
+{
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
        struct rpcrdma_req *req;
-       size_t min_size;
        gfp_t flags;
 
        req = rpcrdma_buffer_get(&r_xprt->rx_buf);
        if (req == NULL)
-               return NULL;
+               return -ENOMEM;
 
        flags = RPCRDMA_DEF_GFP;
        if (RPC_IS_SWAPPER(task))
                flags = __GFP_MEMALLOC | GFP_NOWAIT | __GFP_NOWARN;
 
-       if (req->rl_rdmabuf == NULL)
-               goto out_rdmabuf;
-       if (req->rl_sendbuf == NULL)
-               goto out_sendbuf;
-       if (size > req->rl_sendbuf->rg_size)
-               goto out_sendbuf;
-
-out:
-       dprintk("RPC:       %s: size %zd, request 0x%p\n", __func__, size, req);
-       req->rl_connect_cookie = 0;     /* our reserved value */
-       req->rl_task = task;
-       return req->rl_sendbuf->rg_base;
-
-out_rdmabuf:
-       min_size = RPCRDMA_INLINE_WRITE_THRESHOLD(task->tk_rqstp);
-       rb = rpcrdma_alloc_regbuf(&r_xprt->rx_ia, min_size, flags);
-       if (IS_ERR(rb))
+       if (!rpcrdma_get_rdmabuf(r_xprt, req, flags))
                goto out_fail;
-       req->rl_rdmabuf = rb;
-
-out_sendbuf:
-       /* XDR encoding and RPC/RDMA marshaling of this request has not
-        * yet occurred. Thus a lower bound is needed to prevent buffer
-        * overrun during marshaling.
-        *
-        * RPC/RDMA marshaling may choose to send payload bearing ops
-        * inline, if the result is smaller than the inline threshold.
-        * The value of the "size" argument accounts for header
-        * requirements but not for the payload in these cases.
-        *
-        * Likewise, allocate enough space to receive a reply up to the
-        * size of the inline threshold.
-        *
-        * It's unlikely that both the send header and the received
-        * reply will be large, but slush is provided here to allow
-        * flexibility when marshaling.
-        */
-       min_size = RPCRDMA_INLINE_READ_THRESHOLD(task->tk_rqstp);
-       min_size += RPCRDMA_INLINE_WRITE_THRESHOLD(task->tk_rqstp);
-       if (size < min_size)
-               size = min_size;
-
-       rb = rpcrdma_alloc_regbuf(&r_xprt->rx_ia, size, flags);
-       if (IS_ERR(rb))
+       if (!rpcrdma_get_sendbuf(r_xprt, req, rqst->rq_callsize, flags))
+               goto out_fail;
+       if (!rpcrdma_get_recvbuf(r_xprt, req, rqst->rq_rcvsize, flags))
                goto out_fail;
-       rb->rg_owner = req;
 
-       r_xprt->rx_stats.hardway_register_count += size;
-       rpcrdma_free_regbuf(&r_xprt->rx_ia, req->rl_sendbuf);
-       req->rl_sendbuf = rb;
-       goto out;
+       dprintk("RPC: %5u %s: send size = %zd, recv size = %zd, req = %p\n",
+               task->tk_pid, __func__, rqst->rq_callsize,
+               rqst->rq_rcvsize, req);
+
+       req->rl_connect_cookie = 0;     /* our reserved value */
+       rpcrdma_set_xprtdata(rqst, req);
+       rqst->rq_buffer = req->rl_sendbuf->rg_base;
+       rqst->rq_rbuffer = req->rl_recvbuf->rg_base;
+       return 0;
 
 out_fail:
        rpcrdma_buffer_put(req);
-       return NULL;
+       return -ENOMEM;
 }
 
-/*
- * This function returns all RDMA resources to the pool.
+/**
+ * xprt_rdma_free - release resources allocated by xprt_rdma_allocate
+ * @task: RPC task
+ *
+ * Caller guarantees rqst->rq_buffer is non-NULL.
  */
 static void
-xprt_rdma_free(void *buffer)
+xprt_rdma_free(struct rpc_task *task)
 {
-       struct rpcrdma_req *req;
-       struct rpcrdma_xprt *r_xprt;
-       struct rpcrdma_regbuf *rb;
-
-       if (buffer == NULL)
-               return;
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt);
+       struct rpcrdma_req *req = rpcr_to_rdmar(rqst);
+       struct rpcrdma_ia *ia = &r_xprt->rx_ia;
 
-       rb = container_of(buffer, struct rpcrdma_regbuf, rg_base[0]);
-       req = rb->rg_owner;
        if (req->rl_backchannel)
                return;
 
-       r_xprt = container_of(req->rl_buffer, struct rpcrdma_xprt, rx_buf);
-
        dprintk("RPC:       %s: called on 0x%p\n", __func__, req->rl_reply);
 
-       r_xprt->rx_ia.ri_ops->ro_unmap_safe(r_xprt, req,
-                                           !RPC_IS_ASYNC(req->rl_task));
-
+       ia->ri_ops->ro_unmap_safe(r_xprt, req, !RPC_IS_ASYNC(task));
+       rpcrdma_unmap_sges(ia, req);
        rpcrdma_buffer_put(req);
 }
 
@@ -685,10 +722,11 @@ void xprt_rdma_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
                   r_xprt->rx_stats.failed_marshal_count,
                   r_xprt->rx_stats.bad_reply_count,
                   r_xprt->rx_stats.nomsg_call_count);
-       seq_printf(seq, "%lu %lu %lu\n",
+       seq_printf(seq, "%lu %lu %lu %lu\n",
                   r_xprt->rx_stats.mrs_recovered,
                   r_xprt->rx_stats.mrs_orphaned,
-                  r_xprt->rx_stats.mrs_allocated);
+                  r_xprt->rx_stats.mrs_allocated,
+                  r_xprt->rx_stats.local_inv_needed);
 }
 
 static int
index be3178e..ec74289 100644 (file)
@@ -129,15 +129,6 @@ rpcrdma_wc_send(struct ib_cq *cq, struct ib_wc *wc)
                       wc->status, wc->vendor_err);
 }
 
-static void
-rpcrdma_receive_worker(struct work_struct *work)
-{
-       struct rpcrdma_rep *rep =
-                       container_of(work, struct rpcrdma_rep, rr_work);
-
-       rpcrdma_reply_handler(rep);
-}
-
 /* Perform basic sanity checking to avoid using garbage
  * to update the credit grant value.
  */
@@ -161,13 +152,13 @@ rpcrdma_update_granted_credits(struct rpcrdma_rep *rep)
 }
 
 /**
- * rpcrdma_receive_wc - Invoked by RDMA provider for each polled Receive WC
+ * rpcrdma_wc_receive - Invoked by RDMA provider for each polled Receive WC
  * @cq:        completion queue (ignored)
  * @wc:        completed WR
  *
  */
 static void
-rpcrdma_receive_wc(struct ib_cq *cq, struct ib_wc *wc)
+rpcrdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
 {
        struct ib_cqe *cqe = wc->wr_cqe;
        struct rpcrdma_rep *rep = container_of(cqe, struct rpcrdma_rep,
@@ -185,6 +176,9 @@ rpcrdma_receive_wc(struct ib_cq *cq, struct ib_wc *wc)
                __func__, rep, wc->byte_len);
 
        rep->rr_len = wc->byte_len;
+       rep->rr_wc_flags = wc->wc_flags;
+       rep->rr_inv_rkey = wc->ex.invalidate_rkey;
+
        ib_dma_sync_single_for_cpu(rep->rr_device,
                                   rdmab_addr(rep->rr_rdmabuf),
                                   rep->rr_len, DMA_FROM_DEVICE);
@@ -204,6 +198,36 @@ out_fail:
        goto out_schedule;
 }
 
+static void
+rpcrdma_update_connect_private(struct rpcrdma_xprt *r_xprt,
+                              struct rdma_conn_param *param)
+{
+       struct rpcrdma_create_data_internal *cdata = &r_xprt->rx_data;
+       const struct rpcrdma_connect_private *pmsg = param->private_data;
+       unsigned int rsize, wsize;
+
+       /* Default settings for RPC-over-RDMA Version One */
+       r_xprt->rx_ia.ri_reminv_expected = false;
+       rsize = RPCRDMA_V1_DEF_INLINE_SIZE;
+       wsize = RPCRDMA_V1_DEF_INLINE_SIZE;
+
+       if (pmsg &&
+           pmsg->cp_magic == rpcrdma_cmp_magic &&
+           pmsg->cp_version == RPCRDMA_CMP_VERSION) {
+               r_xprt->rx_ia.ri_reminv_expected = true;
+               rsize = rpcrdma_decode_buffer_size(pmsg->cp_send_size);
+               wsize = rpcrdma_decode_buffer_size(pmsg->cp_recv_size);
+       }
+
+       if (rsize < cdata->inline_rsize)
+               cdata->inline_rsize = rsize;
+       if (wsize < cdata->inline_wsize)
+               cdata->inline_wsize = wsize;
+       pr_info("rpcrdma: max send %u, max recv %u\n",
+               cdata->inline_wsize, cdata->inline_rsize);
+       rpcrdma_set_max_header_sizes(r_xprt);
+}
+
 static int
 rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
 {
@@ -244,6 +268,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
                        " (%d initiator)\n",
                        __func__, attr->max_dest_rd_atomic,
                        attr->max_rd_atomic);
+               rpcrdma_update_connect_private(xprt, &event->param.conn);
                goto connected;
        case RDMA_CM_EVENT_CONNECT_ERROR:
                connstate = -ENOTCONN;
@@ -454,11 +479,12 @@ int
 rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
                                struct rpcrdma_create_data_internal *cdata)
 {
+       struct rpcrdma_connect_private *pmsg = &ep->rep_cm_private;
        struct ib_cq *sendcq, *recvcq;
        unsigned int max_qp_wr;
        int rc;
 
-       if (ia->ri_device->attrs.max_sge < RPCRDMA_MAX_IOVS) {
+       if (ia->ri_device->attrs.max_sge < RPCRDMA_MAX_SEND_SGES) {
                dprintk("RPC:       %s: insufficient sge's available\n",
                        __func__);
                return -ENOMEM;
@@ -487,7 +513,7 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
        ep->rep_attr.cap.max_recv_wr = cdata->max_requests;
        ep->rep_attr.cap.max_recv_wr += RPCRDMA_BACKWARD_WRS;
        ep->rep_attr.cap.max_recv_wr += 1;      /* drain cqe */
-       ep->rep_attr.cap.max_send_sge = RPCRDMA_MAX_IOVS;
+       ep->rep_attr.cap.max_send_sge = RPCRDMA_MAX_SEND_SGES;
        ep->rep_attr.cap.max_recv_sge = 1;
        ep->rep_attr.cap.max_inline_data = 0;
        ep->rep_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
@@ -536,9 +562,14 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
        /* Initialize cma parameters */
        memset(&ep->rep_remote_cma, 0, sizeof(ep->rep_remote_cma));
 
-       /* RPC/RDMA does not use private data */
-       ep->rep_remote_cma.private_data = NULL;
-       ep->rep_remote_cma.private_data_len = 0;
+       /* Prepare RDMA-CM private message */
+       pmsg->cp_magic = rpcrdma_cmp_magic;
+       pmsg->cp_version = RPCRDMA_CMP_VERSION;
+       pmsg->cp_flags |= ia->ri_ops->ro_send_w_inv_ok;
+       pmsg->cp_send_size = rpcrdma_encode_buffer_size(cdata->inline_wsize);
+       pmsg->cp_recv_size = rpcrdma_encode_buffer_size(cdata->inline_rsize);
+       ep->rep_remote_cma.private_data = pmsg;
+       ep->rep_remote_cma.private_data_len = sizeof(*pmsg);
 
        /* Client offers RDMA Read but does not initiate */
        ep->rep_remote_cma.initiator_depth = 0;
@@ -849,6 +880,10 @@ rpcrdma_create_req(struct rpcrdma_xprt *r_xprt)
        req->rl_cqe.done = rpcrdma_wc_send;
        req->rl_buffer = &r_xprt->rx_buf;
        INIT_LIST_HEAD(&req->rl_registered);
+       req->rl_send_wr.next = NULL;
+       req->rl_send_wr.wr_cqe = &req->rl_cqe;
+       req->rl_send_wr.sg_list = req->rl_send_sge;
+       req->rl_send_wr.opcode = IB_WR_SEND;
        return req;
 }
 
@@ -865,17 +900,21 @@ rpcrdma_create_rep(struct rpcrdma_xprt *r_xprt)
        if (rep == NULL)
                goto out;
 
-       rep->rr_rdmabuf = rpcrdma_alloc_regbuf(ia, cdata->inline_rsize,
-                                              GFP_KERNEL);
+       rep->rr_rdmabuf = rpcrdma_alloc_regbuf(cdata->inline_rsize,
+                                              DMA_FROM_DEVICE, GFP_KERNEL);
        if (IS_ERR(rep->rr_rdmabuf)) {
                rc = PTR_ERR(rep->rr_rdmabuf);
                goto out_free;
        }
 
        rep->rr_device = ia->ri_device;
-       rep->rr_cqe.done = rpcrdma_receive_wc;
+       rep->rr_cqe.done = rpcrdma_wc_receive;
        rep->rr_rxprt = r_xprt;
-       INIT_WORK(&rep->rr_work, rpcrdma_receive_worker);
+       INIT_WORK(&rep->rr_work, rpcrdma_reply_handler);
+       rep->rr_recv_wr.next = NULL;
+       rep->rr_recv_wr.wr_cqe = &rep->rr_cqe;
+       rep->rr_recv_wr.sg_list = &rep->rr_rdmabuf->rg_iov;
+       rep->rr_recv_wr.num_sge = 1;
        return rep;
 
 out_free:
@@ -966,17 +1005,18 @@ rpcrdma_buffer_get_rep_locked(struct rpcrdma_buffer *buf)
 }
 
 static void
-rpcrdma_destroy_rep(struct rpcrdma_ia *ia, struct rpcrdma_rep *rep)
+rpcrdma_destroy_rep(struct rpcrdma_rep *rep)
 {
-       rpcrdma_free_regbuf(ia, rep->rr_rdmabuf);
+       rpcrdma_free_regbuf(rep->rr_rdmabuf);
        kfree(rep);
 }
 
 void
-rpcrdma_destroy_req(struct rpcrdma_ia *ia, struct rpcrdma_req *req)
+rpcrdma_destroy_req(struct rpcrdma_req *req)
 {
-       rpcrdma_free_regbuf(ia, req->rl_sendbuf);
-       rpcrdma_free_regbuf(ia, req->rl_rdmabuf);
+       rpcrdma_free_regbuf(req->rl_recvbuf);
+       rpcrdma_free_regbuf(req->rl_sendbuf);
+       rpcrdma_free_regbuf(req->rl_rdmabuf);
        kfree(req);
 }
 
@@ -1009,15 +1049,13 @@ rpcrdma_destroy_mrs(struct rpcrdma_buffer *buf)
 void
 rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
 {
-       struct rpcrdma_ia *ia = rdmab_to_ia(buf);
-
        cancel_delayed_work_sync(&buf->rb_recovery_worker);
 
        while (!list_empty(&buf->rb_recv_bufs)) {
                struct rpcrdma_rep *rep;
 
                rep = rpcrdma_buffer_get_rep_locked(buf);
-               rpcrdma_destroy_rep(ia, rep);
+               rpcrdma_destroy_rep(rep);
        }
        buf->rb_send_count = 0;
 
@@ -1030,7 +1068,7 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
                list_del(&req->rl_all);
 
                spin_unlock(&buf->rb_reqslock);
-               rpcrdma_destroy_req(ia, req);
+               rpcrdma_destroy_req(req);
                spin_lock(&buf->rb_reqslock);
        }
        spin_unlock(&buf->rb_reqslock);
@@ -1129,7 +1167,7 @@ rpcrdma_buffer_put(struct rpcrdma_req *req)
        struct rpcrdma_buffer *buffers = req->rl_buffer;
        struct rpcrdma_rep *rep = req->rl_reply;
 
-       req->rl_niovs = 0;
+       req->rl_send_wr.num_sge = 0;
        req->rl_reply = NULL;
 
        spin_lock(&buffers->rb_lock);
@@ -1171,70 +1209,81 @@ rpcrdma_recv_buffer_put(struct rpcrdma_rep *rep)
        spin_unlock(&buffers->rb_lock);
 }
 
-/*
- * Wrappers for internal-use kmalloc memory registration, used by buffer code.
- */
-
 /**
- * rpcrdma_alloc_regbuf - kmalloc and register memory for SEND/RECV buffers
- * @ia: controlling rpcrdma_ia
+ * rpcrdma_alloc_regbuf - allocate and DMA-map memory for SEND/RECV buffers
  * @size: size of buffer to be allocated, in bytes
+ * @direction: direction of data movement
  * @flags: GFP flags
  *
- * Returns pointer to private header of an area of internally
- * registered memory, or an ERR_PTR. The registered buffer follows
- * the end of the private header.
+ * Returns an ERR_PTR, or a pointer to a regbuf, a buffer that
+ * can be persistently DMA-mapped for I/O.
  *
  * xprtrdma uses a regbuf for posting an outgoing RDMA SEND, or for
- * receiving the payload of RDMA RECV operations. regbufs are not
- * used for RDMA READ/WRITE operations, thus are registered only for
- * LOCAL access.
+ * receiving the payload of RDMA RECV operations. During Long Calls
+ * or Replies they may be registered externally via ro_map.
  */
 struct rpcrdma_regbuf *
-rpcrdma_alloc_regbuf(struct rpcrdma_ia *ia, size_t size, gfp_t flags)
+rpcrdma_alloc_regbuf(size_t size, enum dma_data_direction direction,
+                    gfp_t flags)
 {
        struct rpcrdma_regbuf *rb;
-       struct ib_sge *iov;
 
        rb = kmalloc(sizeof(*rb) + size, flags);
        if (rb == NULL)
-               goto out;
+               return ERR_PTR(-ENOMEM);
 
-       iov = &rb->rg_iov;
-       iov->addr = ib_dma_map_single(ia->ri_device,
-                                     (void *)rb->rg_base, size,
-                                     DMA_BIDIRECTIONAL);
-       if (ib_dma_mapping_error(ia->ri_device, iov->addr))
-               goto out_free;
+       rb->rg_device = NULL;
+       rb->rg_direction = direction;
+       rb->rg_iov.length = size;
 
-       iov->length = size;
-       iov->lkey = ia->ri_pd->local_dma_lkey;
-       rb->rg_size = size;
-       rb->rg_owner = NULL;
        return rb;
+}
 
-out_free:
-       kfree(rb);
-out:
-       return ERR_PTR(-ENOMEM);
+/**
+ * __rpcrdma_map_regbuf - DMA-map a regbuf
+ * @ia: controlling rpcrdma_ia
+ * @rb: regbuf to be mapped
+ */
+bool
+__rpcrdma_dma_map_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+{
+       if (rb->rg_direction == DMA_NONE)
+               return false;
+
+       rb->rg_iov.addr = ib_dma_map_single(ia->ri_device,
+                                           (void *)rb->rg_base,
+                                           rdmab_length(rb),
+                                           rb->rg_direction);
+       if (ib_dma_mapping_error(ia->ri_device, rdmab_addr(rb)))
+               return false;
+
+       rb->rg_device = ia->ri_device;
+       rb->rg_iov.lkey = ia->ri_pd->local_dma_lkey;
+       return true;
+}
+
+static void
+rpcrdma_dma_unmap_regbuf(struct rpcrdma_regbuf *rb)
+{
+       if (!rpcrdma_regbuf_is_mapped(rb))
+               return;
+
+       ib_dma_unmap_single(rb->rg_device, rdmab_addr(rb),
+                           rdmab_length(rb), rb->rg_direction);
+       rb->rg_device = NULL;
 }
 
 /**
  * rpcrdma_free_regbuf - deregister and free registered buffer
- * @ia: controlling rpcrdma_ia
  * @rb: regbuf to be deregistered and freed
  */
 void
-rpcrdma_free_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+rpcrdma_free_regbuf(struct rpcrdma_regbuf *rb)
 {
-       struct ib_sge *iov;
-
        if (!rb)
                return;
 
-       iov = &rb->rg_iov;
-       ib_dma_unmap_single(ia->ri_device,
-                           iov->addr, iov->length, DMA_BIDIRECTIONAL);
+       rpcrdma_dma_unmap_regbuf(rb);
        kfree(rb);
 }
 
@@ -1248,39 +1297,28 @@ rpcrdma_ep_post(struct rpcrdma_ia *ia,
                struct rpcrdma_ep *ep,
                struct rpcrdma_req *req)
 {
-       struct ib_device *device = ia->ri_device;
-       struct ib_send_wr send_wr, *send_wr_fail;
-       struct rpcrdma_rep *rep = req->rl_reply;
-       struct ib_sge *iov = req->rl_send_iov;
-       int i, rc;
+       struct ib_send_wr *send_wr = &req->rl_send_wr;
+       struct ib_send_wr *send_wr_fail;
+       int rc;
 
-       if (rep) {
-               rc = rpcrdma_ep_post_recv(ia, ep, rep);
+       if (req->rl_reply) {
+               rc = rpcrdma_ep_post_recv(ia, req->rl_reply);
                if (rc)
                        return rc;
                req->rl_reply = NULL;
        }
 
-       send_wr.next = NULL;
-       send_wr.wr_cqe = &req->rl_cqe;
-       send_wr.sg_list = iov;
-       send_wr.num_sge = req->rl_niovs;
-       send_wr.opcode = IB_WR_SEND;
-
-       for (i = 0; i < send_wr.num_sge; i++)
-               ib_dma_sync_single_for_device(device, iov[i].addr,
-                                             iov[i].length, DMA_TO_DEVICE);
        dprintk("RPC:       %s: posting %d s/g entries\n",
-               __func__, send_wr.num_sge);
+               __func__, send_wr->num_sge);
 
        if (DECR_CQCOUNT(ep) > 0)
-               send_wr.send_flags = 0;
+               send_wr->send_flags = 0;
        else { /* Provider must take a send completion every now and then */
                INIT_CQCOUNT(ep);
-               send_wr.send_flags = IB_SEND_SIGNALED;
+               send_wr->send_flags = IB_SEND_SIGNALED;
        }
 
-       rc = ib_post_send(ia->ri_id->qp, &send_wr, &send_wr_fail);
+       rc = ib_post_send(ia->ri_id->qp, send_wr, &send_wr_fail);
        if (rc)
                goto out_postsend_err;
        return 0;
@@ -1290,32 +1328,24 @@ out_postsend_err:
        return -ENOTCONN;
 }
 
-/*
- * (Re)post a receive buffer.
- */
 int
 rpcrdma_ep_post_recv(struct rpcrdma_ia *ia,
-                    struct rpcrdma_ep *ep,
                     struct rpcrdma_rep *rep)
 {
-       struct ib_recv_wr recv_wr, *recv_wr_fail;
+       struct ib_recv_wr *recv_wr_fail;
        int rc;
 
-       recv_wr.next = NULL;
-       recv_wr.wr_cqe = &rep->rr_cqe;
-       recv_wr.sg_list = &rep->rr_rdmabuf->rg_iov;
-       recv_wr.num_sge = 1;
-
-       ib_dma_sync_single_for_cpu(ia->ri_device,
-                                  rdmab_addr(rep->rr_rdmabuf),
-                                  rdmab_length(rep->rr_rdmabuf),
-                                  DMA_BIDIRECTIONAL);
-
-       rc = ib_post_recv(ia->ri_id->qp, &recv_wr, &recv_wr_fail);
+       if (!rpcrdma_dma_map_regbuf(ia, rep->rr_rdmabuf))
+               goto out_map;
+       rc = ib_post_recv(ia->ri_id->qp, &rep->rr_recv_wr, &recv_wr_fail);
        if (rc)
                goto out_postrecv;
        return 0;
 
+out_map:
+       pr_err("rpcrdma: failed to DMA map the Receive buffer\n");
+       return -EIO;
+
 out_postrecv:
        pr_err("rpcrdma: ib_post_recv returned %i\n", rc);
        return -ENOTCONN;
@@ -1333,7 +1363,6 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
 {
        struct rpcrdma_buffer *buffers = &r_xprt->rx_buf;
        struct rpcrdma_ia *ia = &r_xprt->rx_ia;
-       struct rpcrdma_ep *ep = &r_xprt->rx_ep;
        struct rpcrdma_rep *rep;
        int rc;
 
@@ -1344,7 +1373,7 @@ rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *r_xprt, unsigned int count)
                rep = rpcrdma_buffer_get_rep_locked(buffers);
                spin_unlock(&buffers->rb_lock);
 
-               rc = rpcrdma_ep_post_recv(ia, ep, rep);
+               rc = rpcrdma_ep_post_recv(ia, rep);
                if (rc)
                        goto out_rc;
        }
index a71b0f5..0d35b76 100644 (file)
@@ -70,9 +70,11 @@ struct rpcrdma_ia {
        struct ib_pd            *ri_pd;
        struct completion       ri_done;
        int                     ri_async_rc;
+       unsigned int            ri_max_segs;
        unsigned int            ri_max_frmr_depth;
        unsigned int            ri_max_inline_write;
        unsigned int            ri_max_inline_read;
+       bool                    ri_reminv_expected;
        struct ib_qp_attr       ri_qp_attr;
        struct ib_qp_init_attr  ri_qp_init_attr;
 };
@@ -87,6 +89,7 @@ struct rpcrdma_ep {
        int                     rep_connected;
        struct ib_qp_init_attr  rep_attr;
        wait_queue_head_t       rep_connect_wait;
+       struct rpcrdma_connect_private  rep_cm_private;
        struct rdma_conn_param  rep_remote_cma;
        struct sockaddr_storage rep_remote_addr;
        struct delayed_work     rep_connect_worker;
@@ -112,9 +115,9 @@ struct rpcrdma_ep {
  */
 
 struct rpcrdma_regbuf {
-       size_t                  rg_size;
-       struct rpcrdma_req      *rg_owner;
        struct ib_sge           rg_iov;
+       struct ib_device        *rg_device;
+       enum dma_data_direction rg_direction;
        __be32                  rg_base[0] __attribute__ ((aligned(256)));
 };
 
@@ -162,7 +165,10 @@ rdmab_to_msg(struct rpcrdma_regbuf *rb)
  * The smallest inline threshold is 1024 bytes, ensuring that
  * at least 750 bytes are available for RPC messages.
  */
-#define RPCRDMA_MAX_HDR_SEGS   (8)
+enum {
+       RPCRDMA_MAX_HDR_SEGS = 8,
+       RPCRDMA_HDRBUF_SIZE = 256,
+};
 
 /*
  * struct rpcrdma_rep -- this structure encapsulates state required to recv
@@ -182,10 +188,13 @@ rdmab_to_msg(struct rpcrdma_regbuf *rb)
 struct rpcrdma_rep {
        struct ib_cqe           rr_cqe;
        unsigned int            rr_len;
+       int                     rr_wc_flags;
+       u32                     rr_inv_rkey;
        struct ib_device        *rr_device;
        struct rpcrdma_xprt     *rr_rxprt;
        struct work_struct      rr_work;
        struct list_head        rr_list;
+       struct ib_recv_wr       rr_recv_wr;
        struct rpcrdma_regbuf   *rr_rdmabuf;
 };
 
@@ -276,19 +285,30 @@ struct rpcrdma_mr_seg {           /* chunk descriptors */
        char            *mr_offset;     /* kva if no page, else offset */
 };
 
-#define RPCRDMA_MAX_IOVS       (2)
+/* Reserve enough Send SGEs to send a maximum size inline request:
+ * - RPC-over-RDMA header
+ * - xdr_buf head iovec
+ * - RPCRDMA_MAX_INLINE bytes, possibly unaligned, in pages
+ * - xdr_buf tail iovec
+ */
+enum {
+       RPCRDMA_MAX_SEND_PAGES = PAGE_SIZE + RPCRDMA_MAX_INLINE - 1,
+       RPCRDMA_MAX_PAGE_SGES = (RPCRDMA_MAX_SEND_PAGES >> PAGE_SHIFT) + 1,
+       RPCRDMA_MAX_SEND_SGES = 1 + 1 + RPCRDMA_MAX_PAGE_SGES + 1,
+};
 
 struct rpcrdma_buffer;
 struct rpcrdma_req {
        struct list_head        rl_free;
-       unsigned int            rl_niovs;
+       unsigned int            rl_mapped_sges;
        unsigned int            rl_connect_cookie;
-       struct rpc_task         *rl_task;
        struct rpcrdma_buffer   *rl_buffer;
-       struct rpcrdma_rep      *rl_reply;/* holder for reply buffer */
-       struct ib_sge           rl_send_iov[RPCRDMA_MAX_IOVS];
-       struct rpcrdma_regbuf   *rl_rdmabuf;
-       struct rpcrdma_regbuf   *rl_sendbuf;
+       struct rpcrdma_rep      *rl_reply;
+       struct ib_send_wr       rl_send_wr;
+       struct ib_sge           rl_send_sge[RPCRDMA_MAX_SEND_SGES];
+       struct rpcrdma_regbuf   *rl_rdmabuf;    /* xprt header */
+       struct rpcrdma_regbuf   *rl_sendbuf;    /* rq_snd_buf */
+       struct rpcrdma_regbuf   *rl_recvbuf;    /* rq_rcv_buf */
 
        struct ib_cqe           rl_cqe;
        struct list_head        rl_all;
@@ -298,14 +318,16 @@ struct rpcrdma_req {
        struct rpcrdma_mr_seg   rl_segments[RPCRDMA_MAX_SEGS];
 };
 
+static inline void
+rpcrdma_set_xprtdata(struct rpc_rqst *rqst, struct rpcrdma_req *req)
+{
+       rqst->rq_xprtdata = req;
+}
+
 static inline struct rpcrdma_req *
 rpcr_to_rdmar(struct rpc_rqst *rqst)
 {
-       void *buffer = rqst->rq_buffer;
-       struct rpcrdma_regbuf *rb;
-
-       rb = container_of(buffer, struct rpcrdma_regbuf, rg_base);
-       return rb->rg_owner;
+       return rqst->rq_xprtdata;
 }
 
 /*
@@ -356,15 +378,6 @@ struct rpcrdma_create_data_internal {
        unsigned int    padding;        /* non-rdma write header padding */
 };
 
-#define RPCRDMA_INLINE_READ_THRESHOLD(rq) \
-       (rpcx_to_rdmad(rq->rq_xprt).inline_rsize)
-
-#define RPCRDMA_INLINE_WRITE_THRESHOLD(rq)\
-       (rpcx_to_rdmad(rq->rq_xprt).inline_wsize)
-
-#define RPCRDMA_INLINE_PAD_VALUE(rq)\
-       rpcx_to_rdmad(rq->rq_xprt).padding
-
 /*
  * Statistics for RPCRDMA
  */
@@ -386,6 +399,7 @@ struct rpcrdma_stats {
        unsigned long           mrs_recovered;
        unsigned long           mrs_orphaned;
        unsigned long           mrs_allocated;
+       unsigned long           local_inv_needed;
 };
 
 /*
@@ -409,6 +423,7 @@ struct rpcrdma_memreg_ops {
                                      struct rpcrdma_mw *);
        void            (*ro_release_mr)(struct rpcrdma_mw *);
        const char      *ro_displayname;
+       const int       ro_send_w_inv_ok;
 };
 
 extern const struct rpcrdma_memreg_ops rpcrdma_fmr_memreg_ops;
@@ -461,15 +476,14 @@ void rpcrdma_ep_disconnect(struct rpcrdma_ep *, struct rpcrdma_ia *);
 
 int rpcrdma_ep_post(struct rpcrdma_ia *, struct rpcrdma_ep *,
                                struct rpcrdma_req *);
-int rpcrdma_ep_post_recv(struct rpcrdma_ia *, struct rpcrdma_ep *,
-                               struct rpcrdma_rep *);
+int rpcrdma_ep_post_recv(struct rpcrdma_ia *, struct rpcrdma_rep *);
 
 /*
  * Buffer calls - xprtrdma/verbs.c
  */
 struct rpcrdma_req *rpcrdma_create_req(struct rpcrdma_xprt *);
 struct rpcrdma_rep *rpcrdma_create_rep(struct rpcrdma_xprt *);
-void rpcrdma_destroy_req(struct rpcrdma_ia *, struct rpcrdma_req *);
+void rpcrdma_destroy_req(struct rpcrdma_req *);
 int rpcrdma_buffer_create(struct rpcrdma_xprt *);
 void rpcrdma_buffer_destroy(struct rpcrdma_buffer *);
 
@@ -482,10 +496,24 @@ void rpcrdma_recv_buffer_put(struct rpcrdma_rep *);
 
 void rpcrdma_defer_mr_recovery(struct rpcrdma_mw *);
 
-struct rpcrdma_regbuf *rpcrdma_alloc_regbuf(struct rpcrdma_ia *,
-                                           size_t, gfp_t);
-void rpcrdma_free_regbuf(struct rpcrdma_ia *,
-                        struct rpcrdma_regbuf *);
+struct rpcrdma_regbuf *rpcrdma_alloc_regbuf(size_t, enum dma_data_direction,
+                                           gfp_t);
+bool __rpcrdma_dma_map_regbuf(struct rpcrdma_ia *, struct rpcrdma_regbuf *);
+void rpcrdma_free_regbuf(struct rpcrdma_regbuf *);
+
+static inline bool
+rpcrdma_regbuf_is_mapped(struct rpcrdma_regbuf *rb)
+{
+       return rb->rg_device != NULL;
+}
+
+static inline bool
+rpcrdma_dma_map_regbuf(struct rpcrdma_ia *ia, struct rpcrdma_regbuf *rb)
+{
+       if (likely(rpcrdma_regbuf_is_mapped(rb)))
+               return true;
+       return __rpcrdma_dma_map_regbuf(ia, rb);
+}
 
 int rpcrdma_ep_post_extra_recv(struct rpcrdma_xprt *, unsigned int);
 
@@ -507,15 +535,25 @@ rpcrdma_data_dir(bool writing)
  */
 void rpcrdma_connect_worker(struct work_struct *);
 void rpcrdma_conn_func(struct rpcrdma_ep *);
-void rpcrdma_reply_handler(struct rpcrdma_rep *);
+void rpcrdma_reply_handler(struct work_struct *);
 
 /*
  * RPC/RDMA protocol calls - xprtrdma/rpc_rdma.c
  */
+
+enum rpcrdma_chunktype {
+       rpcrdma_noch = 0,
+       rpcrdma_readch,
+       rpcrdma_areadch,
+       rpcrdma_writech,
+       rpcrdma_replych
+};
+
+bool rpcrdma_prepare_send_sges(struct rpcrdma_ia *, struct rpcrdma_req *,
+                              u32, struct xdr_buf *, enum rpcrdma_chunktype);
+void rpcrdma_unmap_sges(struct rpcrdma_ia *, struct rpcrdma_req *);
 int rpcrdma_marshal_req(struct rpc_rqst *);
-void rpcrdma_set_max_header_sizes(struct rpcrdma_ia *,
-                                 struct rpcrdma_create_data_internal *,
-                                 unsigned int);
+void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *);
 
 /* RPC/RDMA module init - xprtrdma/transport.c
  */
index bf16883..0137af1 100644 (file)
@@ -473,7 +473,16 @@ static int xs_nospace(struct rpc_task *task)
        spin_unlock_bh(&xprt->transport_lock);
 
        /* Race breaker in case memory is freed before above code is called */
-       sk->sk_write_space(sk);
+       if (ret == -EAGAIN) {
+               struct socket_wq *wq;
+
+               rcu_read_lock();
+               wq = rcu_dereference(sk->sk_wq);
+               set_bit(SOCKWQ_ASYNC_NOSPACE, &wq->flags);
+               rcu_read_unlock();
+
+               sk->sk_write_space(sk);
+       }
        return ret;
 }
 
@@ -2533,35 +2542,38 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
  * we allocate pages instead doing a kmalloc like rpc_malloc is because we want
  * to use the server side send routines.
  */
-static void *bc_malloc(struct rpc_task *task, size_t size)
+static int bc_malloc(struct rpc_task *task)
 {
+       struct rpc_rqst *rqst = task->tk_rqstp;
+       size_t size = rqst->rq_callsize;
        struct page *page;
        struct rpc_buffer *buf;
 
-       WARN_ON_ONCE(size > PAGE_SIZE - sizeof(struct rpc_buffer));
-       if (size > PAGE_SIZE - sizeof(struct rpc_buffer))
-               return NULL;
+       if (size > PAGE_SIZE - sizeof(struct rpc_buffer)) {
+               WARN_ONCE(1, "xprtsock: large bc buffer request (size %zu)\n",
+                         size);
+               return -EINVAL;
+       }
 
        page = alloc_page(GFP_KERNEL);
        if (!page)
-               return NULL;
+               return -ENOMEM;
 
        buf = page_address(page);
        buf->len = PAGE_SIZE;
 
-       return buf->data;
+       rqst->rq_buffer = buf->data;
+       return 0;
 }
 
 /*
  * Free the space allocated in the bc_alloc routine
  */
-static void bc_free(void *buffer)
+static void bc_free(struct rpc_task *task)
 {
+       void *buffer = task->tk_rqstp->rq_buffer;
        struct rpc_buffer *buf;
 
-       if (!buffer)
-               return;
-
        buf = container_of(buffer, struct rpc_buffer, data);
        free_page((unsigned long)buf);
 }
index d80cd3f..78cab9c 100644 (file)
@@ -407,6 +407,7 @@ static int __tipc_nl_add_udp_addr(struct sk_buff *skb,
        if (ntohs(addr->proto) == ETH_P_IP) {
                struct sockaddr_in ip4;
 
+               memset(&ip4, 0, sizeof(ip4));
                ip4.sin_family = AF_INET;
                ip4.sin_port = addr->port;
                ip4.sin_addr.s_addr = addr->ipv4.s_addr;
@@ -417,6 +418,7 @@ static int __tipc_nl_add_udp_addr(struct sk_buff *skb,
        } else if (ntohs(addr->proto) == ETH_P_IPV6) {
                struct sockaddr_in6 ip6;
 
+               memset(&ip6, 0, sizeof(ip6));
                ip6.sin6_family = AF_INET6;
                ip6.sin6_port  = addr->port;
                memcpy(&ip6.sin6_addr, &addr->ipv6, sizeof(struct in6_addr));
index 85c405f..a6d2a43 100644 (file)
@@ -99,4 +99,10 @@ config SAMPLE_SECCOMP
          Build samples of seccomp filters using various methods of
          BPF filter construction.
 
+config SAMPLE_BLACKFIN_GPTIMERS
+       tristate "Build blackfin gptimers sample code -- loadable modules only"
+       depends on BLACKFIN && BFIN_GPTIMERS && m
+       help
+         Build samples of blackfin gptimers sample module.
+
 endif # SAMPLES
index 1a20169..e17d66d 100644 (file)
@@ -2,4 +2,4 @@
 
 obj-$(CONFIG_SAMPLES)  += kobject/ kprobes/ trace_events/ livepatch/ \
                           hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
-                          configfs/ connector/ v4l/ trace_printk/
+                          configfs/ connector/ v4l/ trace_printk/ blackfin/
diff --git a/samples/auxdisplay/.gitignore b/samples/auxdisplay/.gitignore
new file mode 100644 (file)
index 0000000..7af2228
--- /dev/null
@@ -0,0 +1 @@
+cfag12864b-example
diff --git a/samples/auxdisplay/Makefile b/samples/auxdisplay/Makefile
new file mode 100644 (file)
index 0000000..05e471f
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := cfag12864b-example
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/samples/auxdisplay/cfag12864b-example.c b/samples/auxdisplay/cfag12864b-example.c
new file mode 100644 (file)
index 0000000..e7823ff
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ *    Filename: cfag12864b-example.c
+ *     Version: 0.1.0
+ * Description: cfag12864b LCD userspace example program
+ *     License: GPLv2
+ *
+ *      Author: Copyright (C) Miguel Ojeda Sandonis
+ *        Date: 2006-10-31
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * ------------------------
+ * start of cfag12864b code
+ * ------------------------
+ */
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#define CFAG12864B_WIDTH               (128)
+#define CFAG12864B_HEIGHT              (64)
+#define CFAG12864B_SIZE                        (128 * 64 / 8)
+#define CFAG12864B_BPB                 (8)
+#define CFAG12864B_ADDRESS(x, y)       ((y) * CFAG12864B_WIDTH / \
+                                       CFAG12864B_BPB + (x) / CFAG12864B_BPB)
+#define CFAG12864B_BIT(n)              (((unsigned char) 1) << (n))
+
+#undef CFAG12864B_DOCHECK
+#ifdef CFAG12864B_DOCHECK
+       #define CFAG12864B_CHECK(x, y)          ((x) < CFAG12864B_WIDTH && \
+                                               (y) < CFAG12864B_HEIGHT)
+#else
+       #define CFAG12864B_CHECK(x, y)          (1)
+#endif
+
+int cfag12864b_fd;
+unsigned char * cfag12864b_mem;
+unsigned char cfag12864b_buffer[CFAG12864B_SIZE];
+
+/*
+ * init a cfag12864b framebuffer device
+ *
+ * No error:       return = 0
+ * Unable to open: return = -1
+ * Unable to mmap: return = -2
+ */
+static int cfag12864b_init(char *path)
+{
+       cfag12864b_fd = open(path, O_RDWR);
+       if (cfag12864b_fd == -1)
+               return -1;
+
+       cfag12864b_mem = mmap(0, CFAG12864B_SIZE, PROT_READ | PROT_WRITE,
+               MAP_SHARED, cfag12864b_fd, 0);
+       if (cfag12864b_mem == MAP_FAILED) {
+               close(cfag12864b_fd);
+               return -2;
+       }
+
+       return 0;
+}
+
+/*
+ * exit a cfag12864b framebuffer device
+ */
+static void cfag12864b_exit(void)
+{
+       munmap(cfag12864b_mem, CFAG12864B_SIZE);
+       close(cfag12864b_fd);
+}
+
+/*
+ * set (x, y) pixel
+ */
+static void cfag12864b_set(unsigned char x, unsigned char y)
+{
+       if (CFAG12864B_CHECK(x, y))
+               cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] |=
+                       CFAG12864B_BIT(x % CFAG12864B_BPB);
+}
+
+/*
+ * unset (x, y) pixel
+ */
+static void cfag12864b_unset(unsigned char x, unsigned char y)
+{
+       if (CFAG12864B_CHECK(x, y))
+               cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &=
+                       ~CFAG12864B_BIT(x % CFAG12864B_BPB);
+}
+
+/*
+ * is set (x, y) pixel?
+ *
+ * Pixel off: return = 0
+ * Pixel on:  return = 1
+ */
+static unsigned char cfag12864b_isset(unsigned char x, unsigned char y)
+{
+       if (CFAG12864B_CHECK(x, y))
+               if (cfag12864b_buffer[CFAG12864B_ADDRESS(x, y)] &
+                       CFAG12864B_BIT(x % CFAG12864B_BPB))
+                       return 1;
+
+       return 0;
+}
+
+/*
+ * not (x, y) pixel
+ */
+static void cfag12864b_not(unsigned char x, unsigned char y)
+{
+       if (cfag12864b_isset(x, y))
+               cfag12864b_unset(x, y);
+       else
+               cfag12864b_set(x, y);
+}
+
+/*
+ * fill (set all pixels)
+ */
+static void cfag12864b_fill(void)
+{
+       unsigned short i;
+
+       for (i = 0; i < CFAG12864B_SIZE; i++)
+               cfag12864b_buffer[i] = 0xFF;
+}
+
+/*
+ * clear (unset all pixels)
+ */
+static void cfag12864b_clear(void)
+{
+       unsigned short i;
+
+       for (i = 0; i < CFAG12864B_SIZE; i++)
+               cfag12864b_buffer[i] = 0;
+}
+
+/*
+ * format a [128*64] matrix
+ *
+ * Pixel off: src[i] = 0
+ * Pixel on:  src[i] > 0
+ */
+static void cfag12864b_format(unsigned char * matrix)
+{
+       unsigned char i, j, n;
+
+       for (i = 0; i < CFAG12864B_HEIGHT; i++)
+       for (j = 0; j < CFAG12864B_WIDTH / CFAG12864B_BPB; j++) {
+               cfag12864b_buffer[i * CFAG12864B_WIDTH / CFAG12864B_BPB +
+                       j] = 0;
+               for (n = 0; n < CFAG12864B_BPB; n++)
+                       if (matrix[i * CFAG12864B_WIDTH +
+                               j * CFAG12864B_BPB + n])
+                               cfag12864b_buffer[i * CFAG12864B_WIDTH /
+                                       CFAG12864B_BPB + j] |=
+                                       CFAG12864B_BIT(n);
+       }
+}
+
+/*
+ * blit buffer to lcd
+ */
+static void cfag12864b_blit(void)
+{
+       memcpy(cfag12864b_mem, cfag12864b_buffer, CFAG12864B_SIZE);
+}
+
+/*
+ * ----------------------
+ * end of cfag12864b code
+ * ----------------------
+ */
+
+#include <stdio.h>
+
+#define EXAMPLES       6
+
+static void example(unsigned char n)
+{
+       unsigned short i, j;
+       unsigned char matrix[CFAG12864B_WIDTH * CFAG12864B_HEIGHT];
+
+       if (n > EXAMPLES)
+               return;
+
+       printf("Example %i/%i - ", n, EXAMPLES);
+
+       switch (n) {
+       case 1:
+               printf("Draw points setting bits");
+               cfag12864b_clear();
+               for (i = 0; i < CFAG12864B_WIDTH; i += 2)
+                       for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
+                               cfag12864b_set(i, j);
+               break;
+
+       case 2:
+               printf("Clear the LCD");
+               cfag12864b_clear();
+               break;
+
+       case 3:
+               printf("Draw rows formatting a [128*64] matrix");
+               memset(matrix, 0, CFAG12864B_WIDTH * CFAG12864B_HEIGHT);
+               for (i = 0; i < CFAG12864B_WIDTH; i++)
+                       for (j = 0; j < CFAG12864B_HEIGHT; j += 2)
+                               matrix[j * CFAG12864B_WIDTH + i] = 1;
+               cfag12864b_format(matrix);
+               break;
+
+       case 4:
+               printf("Fill the lcd");
+               cfag12864b_fill();
+               break;
+
+       case 5:
+               printf("Draw columns unsetting bits");
+               for (i = 0; i < CFAG12864B_WIDTH; i += 2)
+                       for (j = 0; j < CFAG12864B_HEIGHT; j++)
+                               cfag12864b_unset(i, j);
+               break;
+
+       case 6:
+               printf("Do negative not-ing all bits");
+               for (i = 0; i < CFAG12864B_WIDTH; i++)
+                       for (j = 0; j < CFAG12864B_HEIGHT; j ++)
+                               cfag12864b_not(i, j);
+               break;
+       }
+
+       puts(" - [Press Enter]");
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned char n;
+
+       if (argc != 2) {
+               printf(
+                       "Sintax:  %s fbdev\n"
+                       "Usually: /dev/fb0, /dev/fb1...\n", argv[0]);
+               return -1;
+       }
+
+       if (cfag12864b_init(argv[1])) {
+               printf("Can't init %s fbdev\n", argv[1]);
+               return -2;
+       }
+
+       for (n = 1; n <= EXAMPLES; n++) {
+               example(n);
+               cfag12864b_blit();
+               while (getchar() != '\n');
+       }
+
+       cfag12864b_exit();
+
+       return 0;
+}
diff --git a/samples/blackfin/Makefile b/samples/blackfin/Makefile
new file mode 100644 (file)
index 0000000..89b86cf
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SAMPLE_BLACKFIN_GPTIMERS) += gptimers-example.o
diff --git a/samples/blackfin/gptimers-example.c b/samples/blackfin/gptimers-example.c
new file mode 100644 (file)
index 0000000..283eba9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Simple gptimers example
+ *     http://docs.blackfin.uclinux.org/doku.php?id=linux-kernel:drivers:gptimers
+ *
+ * Copyright 2007-2009 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+/* ... random driver includes ... */
+
+#define DRIVER_NAME "gptimer_example"
+
+#ifdef IRQ_TIMER5
+#define SAMPLE_IRQ_TIMER IRQ_TIMER5
+#else
+#define SAMPLE_IRQ_TIMER IRQ_TIMER2
+#endif
+
+struct gptimer_data {
+       uint32_t period, width;
+};
+static struct gptimer_data data;
+
+/* ... random driver state ... */
+
+static irqreturn_t gptimer_example_irq(int irq, void *dev_id)
+{
+       struct gptimer_data *data = dev_id;
+
+       /* make sure it was our timer which caused the interrupt */
+       if (!get_gptimer_intr(TIMER5_id))
+               return IRQ_NONE;
+
+       /* read the width/period values that were captured for the waveform */
+       data->width = get_gptimer_pwidth(TIMER5_id);
+       data->period = get_gptimer_period(TIMER5_id);
+
+       /* acknowledge the interrupt */
+       clear_gptimer_intr(TIMER5_id);
+
+       /* tell the upper layers we took care of things */
+       return IRQ_HANDLED;
+}
+
+/* ... random driver code ... */
+
+static int __init gptimer_example_init(void)
+{
+       int ret;
+
+       /* grab the peripheral pins */
+       ret = peripheral_request(P_TMR5, DRIVER_NAME);
+       if (ret) {
+               printk(KERN_NOTICE DRIVER_NAME ": peripheral request failed\n");
+               return ret;
+       }
+
+       /* grab the IRQ for the timer */
+       ret = request_irq(SAMPLE_IRQ_TIMER, gptimer_example_irq,
+                       IRQF_SHARED, DRIVER_NAME, &data);
+       if (ret) {
+               printk(KERN_NOTICE DRIVER_NAME ": IRQ request failed\n");
+               peripheral_free(P_TMR5);
+               return ret;
+       }
+
+       /* setup the timer and enable it */
+       set_gptimer_config(TIMER5_id,
+                       WDTH_CAP | PULSE_HI | PERIOD_CNT | IRQ_ENA);
+       enable_gptimers(TIMER5bit);
+
+       return 0;
+}
+module_init(gptimer_example_init);
+
+static void __exit gptimer_example_exit(void)
+{
+       disable_gptimers(TIMER5bit);
+       free_irq(SAMPLE_IRQ_TIMER, &data);
+       peripheral_free(P_TMR5);
+}
+module_exit(gptimer_example_exit);
+
+MODULE_LICENSE("BSD");
diff --git a/samples/mei/.gitignore b/samples/mei/.gitignore
new file mode 100644 (file)
index 0000000..f356b81
--- /dev/null
@@ -0,0 +1 @@
+mei-amt-version
diff --git a/samples/mei/Makefile b/samples/mei/Makefile
new file mode 100644 (file)
index 0000000..7aac216
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := mei-amt-version
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/samples/mei/TODO b/samples/mei/TODO
new file mode 100644 (file)
index 0000000..6b3625d
--- /dev/null
@@ -0,0 +1,2 @@
+TODO:
+       - Cleanup and split the timer function
diff --git a/samples/mei/mei-amt-version.c b/samples/mei/mei-amt-version.c
new file mode 100644 (file)
index 0000000..57d0d87
--- /dev/null
@@ -0,0 +1,479 @@
+/******************************************************************************
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Intel MEI Interface Header
+ *
+ * This file is provided under a dual BSD/GPLv2 license.  When using or
+ * redistributing this file, you may do so under either license.
+ *
+ * GPL LICENSE SUMMARY
+ *
+ * Copyright(c) 2012 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110,
+ * USA
+ *
+ * The full GNU General Public License is included in this distribution
+ * in the file called LICENSE.GPL.
+ *
+ * Contact Information:
+ *     Intel Corporation.
+ *     linux-mei@linux.intel.com
+ *     http://www.intel.com
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2003 - 2012 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *  * Neither the name Intel Corporation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <bits/wordsize.h>
+#include <linux/mei.h>
+
+/*****************************************************************************
+ * Intel Management Engine Interface
+ *****************************************************************************/
+
+#define mei_msg(_me, fmt, ARGS...) do {         \
+       if (_me->verbose)                       \
+               fprintf(stderr, fmt, ##ARGS);   \
+} while (0)
+
+#define mei_err(_me, fmt, ARGS...) do {         \
+       fprintf(stderr, "Error: " fmt, ##ARGS); \
+} while (0)
+
+struct mei {
+       uuid_le guid;
+       bool initialized;
+       bool verbose;
+       unsigned int buf_size;
+       unsigned char prot_ver;
+       int fd;
+};
+
+static void mei_deinit(struct mei *cl)
+{
+       if (cl->fd != -1)
+               close(cl->fd);
+       cl->fd = -1;
+       cl->buf_size = 0;
+       cl->prot_ver = 0;
+       cl->initialized = false;
+}
+
+static bool mei_init(struct mei *me, const uuid_le *guid,
+               unsigned char req_protocol_version, bool verbose)
+{
+       int result;
+       struct mei_client *cl;
+       struct mei_connect_client_data data;
+
+       me->verbose = verbose;
+
+       me->fd = open("/dev/mei", O_RDWR);
+       if (me->fd == -1) {
+               mei_err(me, "Cannot establish a handle to the Intel MEI driver\n");
+               goto err;
+       }
+       memcpy(&me->guid, guid, sizeof(*guid));
+       memset(&data, 0, sizeof(data));
+       me->initialized = true;
+
+       memcpy(&data.in_client_uuid, &me->guid, sizeof(me->guid));
+       result = ioctl(me->fd, IOCTL_MEI_CONNECT_CLIENT, &data);
+       if (result) {
+               mei_err(me, "IOCTL_MEI_CONNECT_CLIENT receive message. err=%d\n", result);
+               goto err;
+       }
+       cl = &data.out_client_properties;
+       mei_msg(me, "max_message_length %d\n", cl->max_msg_length);
+       mei_msg(me, "protocol_version %d\n", cl->protocol_version);
+
+       if ((req_protocol_version > 0) &&
+            (cl->protocol_version != req_protocol_version)) {
+               mei_err(me, "Intel MEI protocol version not supported\n");
+               goto err;
+       }
+
+       me->buf_size = cl->max_msg_length;
+       me->prot_ver = cl->protocol_version;
+
+       return true;
+err:
+       mei_deinit(me);
+       return false;
+}
+
+static ssize_t mei_recv_msg(struct mei *me, unsigned char *buffer,
+                       ssize_t len, unsigned long timeout)
+{
+       ssize_t rc;
+
+       mei_msg(me, "call read length = %zd\n", len);
+
+       rc = read(me->fd, buffer, len);
+       if (rc < 0) {
+               mei_err(me, "read failed with status %zd %s\n",
+                               rc, strerror(errno));
+               mei_deinit(me);
+       } else {
+               mei_msg(me, "read succeeded with result %zd\n", rc);
+       }
+       return rc;
+}
+
+static ssize_t mei_send_msg(struct mei *me, const unsigned char *buffer,
+                       ssize_t len, unsigned long timeout)
+{
+       struct timeval tv;
+       ssize_t written;
+       ssize_t rc;
+       fd_set set;
+
+       tv.tv_sec = timeout / 1000;
+       tv.tv_usec = (timeout % 1000) * 1000000;
+
+       mei_msg(me, "call write length = %zd\n", len);
+
+       written = write(me->fd, buffer, len);
+       if (written < 0) {
+               rc = -errno;
+               mei_err(me, "write failed with status %zd %s\n",
+                       written, strerror(errno));
+               goto out;
+       }
+
+       FD_ZERO(&set);
+       FD_SET(me->fd, &set);
+       rc = select(me->fd + 1 , &set, NULL, NULL, &tv);
+       if (rc > 0 && FD_ISSET(me->fd, &set)) {
+               mei_msg(me, "write success\n");
+       } else if (rc == 0) {
+               mei_err(me, "write failed on timeout with status\n");
+               goto out;
+       } else { /* rc < 0 */
+               mei_err(me, "write failed on select with status %zd\n", rc);
+               goto out;
+       }
+
+       rc = written;
+out:
+       if (rc < 0)
+               mei_deinit(me);
+
+       return rc;
+}
+
+/***************************************************************************
+ * Intel Advanced Management Technology ME Client
+ ***************************************************************************/
+
+#define AMT_MAJOR_VERSION 1
+#define AMT_MINOR_VERSION 1
+
+#define AMT_STATUS_SUCCESS                0x0
+#define AMT_STATUS_INTERNAL_ERROR         0x1
+#define AMT_STATUS_NOT_READY              0x2
+#define AMT_STATUS_INVALID_AMT_MODE       0x3
+#define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4
+
+#define AMT_STATUS_HOST_IF_EMPTY_RESPONSE  0x4000
+#define AMT_STATUS_SDK_RESOURCES      0x1004
+
+
+#define AMT_BIOS_VERSION_LEN   65
+#define AMT_VERSIONS_NUMBER    50
+#define AMT_UNICODE_STRING_LEN 20
+
+struct amt_unicode_string {
+       uint16_t length;
+       char string[AMT_UNICODE_STRING_LEN];
+} __attribute__((packed));
+
+struct amt_version_type {
+       struct amt_unicode_string description;
+       struct amt_unicode_string version;
+} __attribute__((packed));
+
+struct amt_version {
+       uint8_t major;
+       uint8_t minor;
+} __attribute__((packed));
+
+struct amt_code_versions {
+       uint8_t bios[AMT_BIOS_VERSION_LEN];
+       uint32_t count;
+       struct amt_version_type versions[AMT_VERSIONS_NUMBER];
+} __attribute__((packed));
+
+/***************************************************************************
+ * Intel Advanced Management Technology Host Interface
+ ***************************************************************************/
+
+struct amt_host_if_msg_header {
+       struct amt_version version;
+       uint16_t _reserved;
+       uint32_t command;
+       uint32_t length;
+} __attribute__((packed));
+
+struct amt_host_if_resp_header {
+       struct amt_host_if_msg_header header;
+       uint32_t status;
+       unsigned char data[0];
+} __attribute__((packed));
+
+const uuid_le MEI_IAMTHIF = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,  \
+                               0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c);
+
+#define AMT_HOST_IF_CODE_VERSIONS_REQUEST  0x0400001A
+#define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A
+
+const struct amt_host_if_msg_header CODE_VERSION_REQ = {
+       .version = {AMT_MAJOR_VERSION, AMT_MINOR_VERSION},
+       ._reserved = 0,
+       .command = AMT_HOST_IF_CODE_VERSIONS_REQUEST,
+       .length = 0
+};
+
+
+struct amt_host_if {
+       struct mei mei_cl;
+       unsigned long send_timeout;
+       bool initialized;
+};
+
+
+static bool amt_host_if_init(struct amt_host_if *acmd,
+                     unsigned long send_timeout, bool verbose)
+{
+       acmd->send_timeout = (send_timeout) ? send_timeout : 20000;
+       acmd->initialized = mei_init(&acmd->mei_cl, &MEI_IAMTHIF, 0, verbose);
+       return acmd->initialized;
+}
+
+static void amt_host_if_deinit(struct amt_host_if *acmd)
+{
+       mei_deinit(&acmd->mei_cl);
+       acmd->initialized = false;
+}
+
+static uint32_t amt_verify_code_versions(const struct amt_host_if_resp_header *resp)
+{
+       uint32_t status = AMT_STATUS_SUCCESS;
+       struct amt_code_versions *code_ver;
+       size_t code_ver_len;
+       uint32_t ver_type_cnt;
+       uint32_t len;
+       uint32_t i;
+
+       code_ver = (struct amt_code_versions *)resp->data;
+       /* length - sizeof(status) */
+       code_ver_len = resp->header.length - sizeof(uint32_t);
+       ver_type_cnt = code_ver_len -
+                       sizeof(code_ver->bios) -
+                       sizeof(code_ver->count);
+       if (code_ver->count != ver_type_cnt / sizeof(struct amt_version_type)) {
+               status = AMT_STATUS_INTERNAL_ERROR;
+               goto out;
+       }
+
+       for (i = 0; i < code_ver->count; i++) {
+               len = code_ver->versions[i].description.length;
+
+               if (len > AMT_UNICODE_STRING_LEN) {
+                       status = AMT_STATUS_INTERNAL_ERROR;
+                       goto out;
+               }
+
+               len = code_ver->versions[i].version.length;
+               if (code_ver->versions[i].version.string[len] != '\0' ||
+                   len != strlen(code_ver->versions[i].version.string)) {
+                       status = AMT_STATUS_INTERNAL_ERROR;
+                       goto out;
+               }
+       }
+out:
+       return status;
+}
+
+static uint32_t amt_verify_response_header(uint32_t command,
+                               const struct amt_host_if_msg_header *resp_hdr,
+                               uint32_t response_size)
+{
+       if (response_size < sizeof(struct amt_host_if_resp_header)) {
+               return AMT_STATUS_INTERNAL_ERROR;
+       } else if (response_size != (resp_hdr->length +
+                               sizeof(struct amt_host_if_msg_header))) {
+               return AMT_STATUS_INTERNAL_ERROR;
+       } else if (resp_hdr->command != command) {
+               return AMT_STATUS_INTERNAL_ERROR;
+       } else if (resp_hdr->_reserved != 0) {
+               return AMT_STATUS_INTERNAL_ERROR;
+       } else if (resp_hdr->version.major != AMT_MAJOR_VERSION ||
+                  resp_hdr->version.minor < AMT_MINOR_VERSION) {
+               return AMT_STATUS_INTERNAL_ERROR;
+       }
+       return AMT_STATUS_SUCCESS;
+}
+
+static uint32_t amt_host_if_call(struct amt_host_if *acmd,
+                       const unsigned char *command, ssize_t command_sz,
+                       uint8_t **read_buf, uint32_t rcmd,
+                       unsigned int expected_sz)
+{
+       uint32_t in_buf_sz;
+       uint32_t out_buf_sz;
+       ssize_t written;
+       uint32_t status;
+       struct amt_host_if_resp_header *msg_hdr;
+
+       in_buf_sz = acmd->mei_cl.buf_size;
+       *read_buf = (uint8_t *)malloc(sizeof(uint8_t) * in_buf_sz);
+       if (*read_buf == NULL)
+               return AMT_STATUS_SDK_RESOURCES;
+       memset(*read_buf, 0, in_buf_sz);
+       msg_hdr = (struct amt_host_if_resp_header *)*read_buf;
+
+       written = mei_send_msg(&acmd->mei_cl,
+                               command, command_sz, acmd->send_timeout);
+       if (written != command_sz)
+               return AMT_STATUS_INTERNAL_ERROR;
+
+       out_buf_sz = mei_recv_msg(&acmd->mei_cl, *read_buf, in_buf_sz, 2000);
+       if (out_buf_sz <= 0)
+               return AMT_STATUS_HOST_IF_EMPTY_RESPONSE;
+
+       status = msg_hdr->status;
+       if (status != AMT_STATUS_SUCCESS)
+               return status;
+
+       status = amt_verify_response_header(rcmd,
+                               &msg_hdr->header, out_buf_sz);
+       if (status != AMT_STATUS_SUCCESS)
+               return status;
+
+       if (expected_sz && expected_sz != out_buf_sz)
+               return AMT_STATUS_INTERNAL_ERROR;
+
+       return AMT_STATUS_SUCCESS;
+}
+
+
+static uint32_t amt_get_code_versions(struct amt_host_if *cmd,
+                              struct amt_code_versions *versions)
+{
+       struct amt_host_if_resp_header *response = NULL;
+       uint32_t status;
+
+       status = amt_host_if_call(cmd,
+                       (const unsigned char *)&CODE_VERSION_REQ,
+                       sizeof(CODE_VERSION_REQ),
+                       (uint8_t **)&response,
+                       AMT_HOST_IF_CODE_VERSIONS_RESPONSE, 0);
+
+       if (status != AMT_STATUS_SUCCESS)
+               goto out;
+
+       status = amt_verify_code_versions(response);
+       if (status != AMT_STATUS_SUCCESS)
+               goto out;
+
+       memcpy(versions, response->data, sizeof(struct amt_code_versions));
+out:
+       if (response != NULL)
+               free(response);
+
+       return status;
+}
+
+/************************** end of amt_host_if_command ***********************/
+int main(int argc, char **argv)
+{
+       struct amt_code_versions ver;
+       struct amt_host_if acmd;
+       unsigned int i;
+       uint32_t status;
+       int ret;
+       bool verbose;
+
+       verbose = (argc > 1 && strcmp(argv[1], "-v") == 0);
+
+       if (!amt_host_if_init(&acmd, 5000, verbose)) {
+               ret = 1;
+               goto out;
+       }
+
+       status = amt_get_code_versions(&acmd, &ver);
+
+       amt_host_if_deinit(&acmd);
+
+       switch (status) {
+       case AMT_STATUS_HOST_IF_EMPTY_RESPONSE:
+               printf("Intel AMT: DISABLED\n");
+               ret = 0;
+               break;
+       case AMT_STATUS_SUCCESS:
+               printf("Intel AMT: ENABLED\n");
+               for (i = 0; i < ver.count; i++) {
+                       printf("%s:\t%s\n", ver.versions[i].description.string,
+                               ver.versions[i].version.string);
+               }
+               ret = 0;
+               break;
+       default:
+               printf("An error has occurred\n");
+               ret = 1;
+               break;
+       }
+
+out:
+       return ret;
+}
diff --git a/samples/mic/mpssd/.gitignore b/samples/mic/mpssd/.gitignore
new file mode 100644 (file)
index 0000000..8b7c72f
--- /dev/null
@@ -0,0 +1 @@
+mpssd
diff --git a/samples/mic/mpssd/Makefile b/samples/mic/mpssd/Makefile
new file mode 100644 (file)
index 0000000..3e3ef91
--- /dev/null
@@ -0,0 +1,27 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+
+PROGS := mpssd
+CC = $(CROSS_COMPILE)gcc
+CFLAGS := -I../../../usr/include -I../../../tools/include
+
+ifdef DEBUG
+CFLAGS += -DDEBUG=$(DEBUG)
+endif
+
+all: $(PROGS)
+mpssd: mpssd.c sysfs.c
+       $(CC) $(CFLAGS) mpssd.c sysfs.c -o mpssd -lpthread
+
+install:
+       install mpssd /usr/sbin/mpssd
+       install micctrl /usr/sbin/micctrl
+
+clean:
+       rm -fr $(PROGS)
+
+endif
+endif
diff --git a/samples/mic/mpssd/micctrl b/samples/mic/mpssd/micctrl
new file mode 100755 (executable)
index 0000000..8f2629b
--- /dev/null
@@ -0,0 +1,173 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License, version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# micctrl - Controls MIC boot/start/stop.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: micctrl
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+sysfs="/sys/class/mic"
+
+_status()
+{
+       f=$sysfs/$1
+       echo -e $1 state: "`cat $f/state`" shutdown_status: "`cat $f/shutdown_status`"
+}
+
+status()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _status $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _status `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_reset()
+{
+       f=$sysfs/$1
+       echo reset > $f/state
+}
+
+reset()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _reset $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _reset `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_boot()
+{
+       f=$sysfs/$1
+       echo "linux" > $f/bootmode
+       echo "mic/uos.img" > $f/firmware
+       echo "mic/$1.image" > $f/ramdisk
+       echo "boot" > $f/state
+}
+
+boot()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _boot $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _boot `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_shutdown()
+{
+       f=$sysfs/$1
+       echo shutdown > $f/state
+}
+
+shutdown()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _shutdown $1
+               return $?
+       fi
+       for f in $sysfs/*
+       do
+               _shutdown `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+_wait()
+{
+       f=$sysfs/$1
+       while [ "`cat $f/state`" != "offline" -a "`cat $f/state`" != "online" ]
+       do
+               sleep 1
+               echo -e "Waiting for $1 to go offline"
+       done
+}
+
+wait()
+{
+       if [ "`echo $1 | head -c3`" == "mic" ]; then
+               _wait $1
+               return $?
+       fi
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               _wait `basename $f`
+               RETVAL=$?
+               [ $RETVAL -ne 0 ] && return $RETVAL
+       done
+       return 0
+}
+
+if [ ! -d "$sysfs" ]; then
+       echo -e $"Module unloaded "
+       exit 3
+fi
+
+case $1 in
+       -s)
+               status $2
+               ;;
+       -r)
+               reset $2
+               ;;
+       -b)
+               boot $2
+               ;;
+       -S)
+               shutdown $2
+               ;;
+       -w)
+               wait $2
+               ;;
+       *)
+               echo $"Usage: $0 {-s (status) |-r (reset) |-b (boot) |-S (shutdown) |-w (wait)}"
+               exit 2
+esac
+
+exit $?
diff --git a/samples/mic/mpssd/mpss b/samples/mic/mpssd/mpss
new file mode 100755 (executable)
index 0000000..5fcf9fa
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/bash
+# Intel MIC Platform Software Stack (MPSS)
+#
+# Copyright(c) 2013 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License, version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Intel MIC User Space Tools.
+#
+# mpss Start mpssd.
+#
+# chkconfig: 2345 95 05
+# description: start MPSS stack processing.
+#
+### BEGIN INIT INFO
+# Provides: mpss
+# Required-Start:
+# Required-Stop:
+# Short-Description: MPSS stack control
+# Description: MPSS stack control
+### END INIT INFO
+
+# Source function library.
+. /etc/init.d/functions
+
+exec=/usr/sbin/mpssd
+sysfs="/sys/class/mic"
+mic_modules="mic_host mic_x100_dma scif vop"
+
+start()
+{
+       [ -x $exec ] || exit 5
+
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -1`" = "mpssd" ]; then
+               echo -e $"MPSSD already running! "
+               success
+               echo
+               return 0
+       fi
+
+       echo -e $"Starting MPSS Stack"
+       echo -e $"Loading MIC drivers:" $mic_modules
+
+       modprobe -a $mic_modules
+       RETVAL=$?
+       if [ $RETVAL -ne 0 ]; then
+               failure
+               echo
+               return $RETVAL
+       fi
+
+       # Start the daemon
+       echo -n $"Starting MPSSD "
+       $exec
+       RETVAL=$?
+       if [ $RETVAL -ne 0 ]; then
+               failure
+               echo
+               return $RETVAL
+       fi
+       success
+       echo
+
+       sleep 5
+
+       # Boot the cards
+       micctrl -b
+
+       # Wait till ping works
+       for f in $sysfs/*
+       do
+               count=100
+               ipaddr=`cat $f/cmdline`
+               ipaddr=${ipaddr#*address,}
+               ipaddr=`echo $ipaddr | cut -d, -f1 | cut -d\; -f1`
+               while [ $count -ge 0 ]
+               do
+                       echo -e "Pinging "`basename $f`" "
+                       ping -c 1 $ipaddr &> /dev/null
+                       RETVAL=$?
+                       if [ $RETVAL -eq 0 ]; then
+                               success
+                               break
+                       fi
+                       sleep 1
+                       count=`expr $count - 1`
+               done
+               [ $RETVAL -ne 0 ] && failure || success
+               echo
+       done
+       return $RETVAL
+}
+
+stop()
+{
+       echo -e $"Shutting down MPSS Stack: "
+
+       # Bail out if module is unloaded
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"Module unloaded "
+               success
+               echo
+               return 0
+       fi
+
+       # Shut down the cards.
+       micctrl -S
+
+       # Wait for the cards to go offline
+       for f in $sysfs/*
+       do
+               while [ "`cat $f/state`" != "ready" ]
+               do
+                       sleep 1
+                       echo -e "Waiting for "`basename $f`" to become ready"
+               done
+       done
+
+       # Display the status of the cards
+       micctrl -s
+
+       # Kill MPSSD now
+       echo -n $"Killing MPSSD"
+       killall -9 mpssd 2>/dev/null
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+restart()
+{
+       stop
+       sleep 5
+       start
+}
+
+status()
+{
+       micctrl -s
+       if [ "`ps -e | awk '{print $4}' | grep mpssd | head -n 1`" = "mpssd" ]; then
+               echo "mpssd is running"
+       else
+               echo "mpssd is stopped"
+       fi
+       return 0
+}
+
+unload()
+{
+       if [ ! -d "$sysfs" ]; then
+               echo -n $"No MIC_HOST Module: "
+               success
+               echo
+               return
+       fi
+
+       stop
+
+       sleep 5
+       echo -n $"Removing MIC drivers:" $mic_modules
+       modprobe -r $mic_modules
+       RETVAL=$?
+       [ $RETVAL -ne 0 ] && failure || success
+       echo
+       return $RETVAL
+}
+
+case $1 in
+       start)
+               start
+               ;;
+       stop)
+               stop
+               ;;
+       restart)
+               restart
+               ;;
+       status)
+               status
+               ;;
+       unload)
+               unload
+               ;;
+       *)
+               echo $"Usage: $0 {start|stop|restart|status|unload}"
+               exit 2
+esac
+
+exit $?
diff --git a/samples/mic/mpssd/mpssd.c b/samples/mic/mpssd/mpssd.c
new file mode 100644 (file)
index 0000000..49db1de
--- /dev/null
@@ -0,0 +1,1826 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <signal.h>
+#include <poll.h>
+#include <features.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_net.h>
+#include <linux/virtio_console.h>
+#include <linux/virtio_blk.h>
+#include <linux/version.h>
+#include "mpssd.h"
+#include <linux/mic_ioctl.h>
+#include <linux/mic_common.h>
+#include <tools/endian.h>
+
+static void *init_mic(void *arg);
+
+static FILE *logfp;
+static struct mic_info mic_list;
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#define min_t(type, x, y) ({                           \
+               type __min1 = (x);                      \
+               type __min2 = (y);                      \
+               __min1 < __min2 ? __min1 : __min2; })
+
+/* align addr on a size boundary - adjust address up/down if needed */
+#define _ALIGN_DOWN(addr, size)  ((addr)&(~((size)-1)))
+#define _ALIGN_UP(addr, size)    _ALIGN_DOWN(addr + size - 1, size)
+
+/* align addr on a size boundary - adjust address up if needed */
+#define _ALIGN(addr, size)     _ALIGN_UP(addr, size)
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr)        _ALIGN(addr, PAGE_SIZE)
+
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+#define GSO_ENABLED            1
+#define MAX_GSO_SIZE           (64 * 1024)
+#define ETH_H_LEN              14
+#define MAX_NET_PKT_SIZE       (_ALIGN_UP(MAX_GSO_SIZE + ETH_H_LEN, 64))
+#define MIC_DEVICE_PAGE_END    0x1000
+
+#ifndef VIRTIO_NET_HDR_F_DATA_VALID
+#define VIRTIO_NET_HDR_F_DATA_VALID    2       /* Csum is valid */
+#endif
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_console_config cons_config;
+} virtcons_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_CONSOLE,
+               .num_vq = ARRAY_SIZE(virtcons_dev_page.vqconfig),
+               .feature_len = sizeof(virtcons_dev_page.host_features),
+               .config_len = sizeof(virtcons_dev_page.cons_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+};
+
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[2];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_net_config net_config;
+} virtnet_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_NET,
+               .num_vq = ARRAY_SIZE(virtnet_dev_page.vqconfig),
+               .feature_len = sizeof(virtnet_dev_page.host_features),
+               .config_len = sizeof(virtnet_dev_page.net_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .vqconfig[1] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+#if GSO_ENABLED
+       .host_features = htole32(
+               1 << VIRTIO_NET_F_CSUM |
+               1 << VIRTIO_NET_F_GSO |
+               1 << VIRTIO_NET_F_GUEST_TSO4 |
+               1 << VIRTIO_NET_F_GUEST_TSO6 |
+               1 << VIRTIO_NET_F_GUEST_ECN),
+#else
+               .host_features = 0,
+#endif
+};
+
+static const char *mic_config_dir = "/etc/mpss";
+static const char *virtblk_backend = "VIRTBLK_BACKEND";
+static struct {
+       struct mic_device_desc dd;
+       struct mic_vqconfig vqconfig[1];
+       __u32 host_features, guest_acknowledgements;
+       struct virtio_blk_config blk_config;
+} virtblk_dev_page = {
+       .dd = {
+               .type = VIRTIO_ID_BLOCK,
+               .num_vq = ARRAY_SIZE(virtblk_dev_page.vqconfig),
+               .feature_len = sizeof(virtblk_dev_page.host_features),
+               .config_len = sizeof(virtblk_dev_page.blk_config),
+       },
+       .vqconfig[0] = {
+               .num = htole16(MIC_VRING_ENTRIES),
+       },
+       .host_features =
+               htole32(1<<VIRTIO_BLK_F_SEG_MAX),
+       .blk_config = {
+               .seg_max = htole32(MIC_VRING_ENTRIES - 2),
+               .capacity = htole64(0),
+        }
+};
+
+static char *myname;
+
+static int
+tap_configure(struct mic_info *mic, char *dev)
+{
+       pid_t pid;
+       char *ifargv[7];
+       char ipaddr[IFNAMSIZ];
+       int ret = 0;
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "link";
+               ifargv[2] = "set";
+               ifargv[3] = dev;
+               ifargv[4] = "up";
+               ifargv[5] = NULL;
+               mpsslog("Configuring %s\n", dev);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       snprintf(ipaddr, IFNAMSIZ, "172.31.%d.254/24", mic->id + 1);
+
+       pid = fork();
+       if (pid == 0) {
+               ifargv[0] = "ip";
+               ifargv[1] = "addr";
+               ifargv[2] = "add";
+               ifargv[3] = ipaddr;
+               ifargv[4] = "dev";
+               ifargv[5] = dev;
+               ifargv[6] = NULL;
+               mpsslog("Configuring %s ipaddr %s\n", dev, ipaddr);
+               ret = execvp("ip", ifargv);
+               if (ret < 0) {
+                       mpsslog("%s execvp failed errno %s\n",
+                               mic->name, strerror(errno));
+                       return ret;
+               }
+       }
+       if (pid < 0) {
+               mpsslog("%s fork failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+
+       ret = waitpid(pid, NULL, 0);
+       if (ret < 0) {
+               mpsslog("%s waitpid failed errno %s\n",
+                       mic->name, strerror(errno));
+               return ret;
+       }
+       mpsslog("MIC name %s %s %d DONE!\n",
+               mic->name, __func__, __LINE__);
+       return 0;
+}
+
+static int tun_alloc(struct mic_info *mic, char *dev)
+{
+       struct ifreq ifr;
+       int fd, err;
+#if GSO_ENABLED
+       unsigned offload;
+#endif
+       fd = open("/dev/net/tun", O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open /dev/net/tun %s\n", strerror(errno));
+               goto done;
+       }
+
+       memset(&ifr, 0, sizeof(ifr));
+
+       ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
+       if (*dev)
+               strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+       err = ioctl(fd, TUNSETIFF, (void *)&ifr);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETIFF failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#if GSO_ENABLED
+       offload = TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | TUN_F_TSO_ECN;
+
+       err = ioctl(fd, TUNSETOFFLOAD, offload);
+       if (err < 0) {
+               mpsslog("%s %s %d TUNSETOFFLOAD failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               close(fd);
+               return err;
+       }
+#endif
+       strcpy(dev, ifr.ifr_name);
+       mpsslog("Created TAP %s\n", dev);
+done:
+       return fd;
+}
+
+#define NET_FD_VIRTIO_NET 0
+#define NET_FD_TUN 1
+#define MAX_NET_FD 2
+
+static void set_dp(struct mic_info *mic, int type, void *dp)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.console_dp = dp;
+               return;
+       case VIRTIO_ID_NET:
+               mic->mic_net.net_dp = dp;
+               return;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.block_dp = dp;
+               return;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+}
+
+static void *get_dp(struct mic_info *mic, int type)
+{
+       switch (type) {
+       case VIRTIO_ID_CONSOLE:
+               return mic->mic_console.console_dp;
+       case VIRTIO_ID_NET:
+               return mic->mic_net.net_dp;
+       case VIRTIO_ID_BLOCK:
+               return mic->mic_virtblk.block_dp;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       assert(0);
+       return NULL;
+}
+
+static struct mic_device_desc *get_device_desc(struct mic_info *mic, int type)
+{
+       struct mic_device_desc *d;
+       int i;
+       void *dp = get_dp(mic, type);
+
+       for (i = sizeof(struct mic_bootparam); i < PAGE_SIZE;
+               i += mic_total_desc_size(d)) {
+               d = dp + i;
+
+               /* End of list */
+               if (d->type == 0)
+                       break;
+
+               if (d->type == -1)
+                       continue;
+
+               mpsslog("%s %s d-> type %d d %p\n",
+                       mic->name, __func__, d->type, d);
+
+               if (d->type == (__u8)type)
+                       return d;
+       }
+       mpsslog("%s %s %d not found\n", mic->name, __func__, type);
+       return NULL;
+}
+
+/* See comments in vhost.c for explanation of next_desc() */
+static unsigned next_desc(struct vring_desc *desc)
+{
+       unsigned int next;
+
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT))
+               return -1U;
+       next = le16toh(desc->next);
+       return next;
+}
+
+/* Sum up all the IOVEC length */
+static ssize_t
+sum_iovec_len(struct mic_copy_desc *copy)
+{
+       ssize_t sum = 0;
+       unsigned int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               sum += copy->iov[i].iov_len;
+       return sum;
+}
+
+static inline void verify_out_len(struct mic_info *mic,
+       struct mic_copy_desc *copy)
+{
+       if (copy->out_len != sum_iovec_len(copy)) {
+               mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%zx\n",
+                       mic->name, __func__, __LINE__,
+                       copy->out_len, sum_iovec_len(copy));
+               assert(copy->out_len == sum_iovec_len(copy));
+       }
+}
+
+/* Display an iovec */
+static void
+disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy,
+          const char *s, int line)
+{
+       unsigned int i;
+
+       for (i = 0; i < copy->iovcnt; i++)
+               mpsslog("%s %s %d copy->iov[%d] addr %p len 0x%zx\n",
+                       mic->name, s, line, i,
+                       copy->iov[i].iov_base, copy->iov[i].iov_len);
+}
+
+static inline __u16 read_avail_idx(struct mic_vring *vr)
+{
+       return ACCESS_ONCE(vr->info->avail_idx);
+}
+
+static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr,
+                               struct mic_copy_desc *copy, ssize_t len)
+{
+       copy->vr_idx = tx ? 0 : 1;
+       copy->update_used = true;
+       if (type == VIRTIO_ID_NET)
+               copy->iov[1].iov_len = len - sizeof(struct virtio_net_hdr);
+       else
+               copy->iov[0].iov_len = len;
+}
+
+/* Central API which triggers the copies */
+static int
+mic_virtio_copy(struct mic_info *mic, int fd,
+               struct mic_vring *vr, struct mic_copy_desc *copy)
+{
+       int ret;
+
+       ret = ioctl(fd, MIC_VIRTIO_COPY_DESC, copy);
+       if (ret) {
+               mpsslog("%s %s %d errno %s ret %d\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno), ret);
+       }
+       return ret;
+}
+
+static inline unsigned _vring_size(unsigned int num, unsigned long align)
+{
+       return ((sizeof(struct vring_desc) * num + sizeof(__u16) * (3 + num)
+                               + align - 1) & ~(align - 1))
+               + sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+/*
+ * This initialization routine requires at least one
+ * vring i.e. vr0. vr1 is optional.
+ */
+static void *
+init_vr(struct mic_info *mic, int fd, int type,
+       struct mic_vring *vr0, struct mic_vring *vr1, int num_vq)
+{
+       int vr_size;
+       char *va;
+
+       vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
+                                        MIC_VIRTIO_RING_ALIGN) +
+                            sizeof(struct _mic_vring_info));
+       va = mmap(NULL, MIC_DEVICE_PAGE_END + vr_size * num_vq,
+               PROT_READ, MAP_SHARED, fd, 0);
+       if (MAP_FAILED == va) {
+               mpsslog("%s %s %d mmap failed errno %s\n",
+                       mic->name, __func__, __LINE__,
+                       strerror(errno));
+               goto done;
+       }
+       set_dp(mic, type, va);
+       vr0->va = (struct mic_vring *)&va[MIC_DEVICE_PAGE_END];
+       vr0->info = vr0->va +
+               _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN);
+       vring_init(&vr0->vr,
+                  MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN);
+       mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ",
+               __func__, mic->name, vr0->va, vr0->info, vr_size,
+               _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+       mpsslog("magic 0x%x expected 0x%x\n",
+               le32toh(vr0->info->magic), MIC_MAGIC + type);
+       assert(le32toh(vr0->info->magic) == MIC_MAGIC + type);
+       if (vr1) {
+               vr1->va = (struct mic_vring *)
+                       &va[MIC_DEVICE_PAGE_END + vr_size];
+               vr1->info = vr1->va + _vring_size(MIC_VRING_ENTRIES,
+                       MIC_VIRTIO_RING_ALIGN);
+               vring_init(&vr1->vr,
+                          MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN);
+               mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ",
+                       __func__, mic->name, vr1->va, vr1->info, vr_size,
+                       _vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN));
+               mpsslog("magic 0x%x expected 0x%x\n",
+                       le32toh(vr1->info->magic), MIC_MAGIC + type + 1);
+               assert(le32toh(vr1->info->magic) == MIC_MAGIC + type + 1);
+       }
+done:
+       return va;
+}
+
+static int
+wait_for_card_driver(struct mic_info *mic, int fd, int type)
+{
+       struct pollfd pollfd;
+       int err;
+       struct mic_device_desc *desc = get_device_desc(mic, type);
+       __u8 prev_status;
+
+       if (!desc)
+               return -ENODEV;
+       prev_status = desc->status;
+       pollfd.fd = fd;
+       mpsslog("%s %s Waiting .... desc-> type %d status 0x%x\n",
+               mic->name, __func__, type, desc->status);
+
+       while (1) {
+               pollfd.events = POLLIN;
+               pollfd.revents = 0;
+               err = poll(&pollfd, 1, -1);
+               if (err < 0) {
+                       mpsslog("%s %s poll failed %s\n",
+                               mic->name, __func__, strerror(errno));
+                       continue;
+               }
+
+               if (pollfd.revents) {
+                       if (desc->status != prev_status) {
+                               mpsslog("%s %s Waiting... desc-> type %d "
+                                       "status 0x%x\n",
+                                       mic->name, __func__, type,
+                                       desc->status);
+                               prev_status = desc->status;
+                       }
+                       if (desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
+                               mpsslog("%s %s poll.revents %d\n",
+                                       mic->name, __func__, pollfd.revents);
+                               mpsslog("%s %s desc-> type %d status 0x%x\n",
+                                       mic->name, __func__, type,
+                                       desc->status);
+                               break;
+                       }
+               }
+       }
+       return 0;
+}
+
+/* Spin till we have some descriptors */
+static void
+spin_for_descriptors(struct mic_info *mic, struct mic_vring *vr)
+{
+       __u16 avail_idx = read_avail_idx(vr);
+
+       while (avail_idx == le16toh(ACCESS_ONCE(vr->vr.avail->idx))) {
+#ifdef DEBUG
+               mpsslog("%s %s waiting for desc avail %d info_avail %d\n",
+                       mic->name, __func__,
+                       le16toh(vr->vr.avail->idx), vr->info->avail_idx);
+#endif
+               sched_yield();
+       }
+}
+
+static void *
+virtio_net(void *arg)
+{
+       static __u8 vnet_hdr[2][sizeof(struct virtio_net_hdr)];
+       static __u8 vnet_buf[2][MAX_NET_PKT_SIZE] __attribute__ ((aligned(64)));
+       struct iovec vnet_iov[2][2] = {
+               { { .iov_base = vnet_hdr[0], .iov_len = sizeof(vnet_hdr[0]) },
+                 { .iov_base = vnet_buf[0], .iov_len = sizeof(vnet_buf[0]) } },
+               { { .iov_base = vnet_hdr[1], .iov_len = sizeof(vnet_hdr[1]) },
+                 { .iov_base = vnet_buf[1], .iov_len = sizeof(vnet_buf[1]) } },
+       };
+       struct iovec *iov0 = vnet_iov[0], *iov1 = vnet_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       char if_name[IFNAMSIZ];
+       struct pollfd net_poll[MAX_NET_FD];
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+       int err;
+
+       snprintf(if_name, IFNAMSIZ, "mic%d", mic->id);
+       mic->mic_net.tap_fd = tun_alloc(mic, if_name);
+       if (mic->mic_net.tap_fd < 0)
+               goto done;
+
+       if (tap_configure(mic, if_name))
+               goto done;
+       mpsslog("MIC name %s id %d\n", mic->name, mic->id);
+
+       net_poll[NET_FD_VIRTIO_NET].fd = mic->mic_net.virtio_net_fd;
+       net_poll[NET_FD_VIRTIO_NET].events = POLLIN;
+       net_poll[NET_FD_TUN].fd = mic->mic_net.tap_fd;
+       net_poll[NET_FD_TUN].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd,
+                                 VIRTIO_ID_NET, &tx_vr, &rx_vr,
+               virtnet_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto done;
+       }
+
+       copy.iovcnt = 2;
+       desc = get_device_desc(mic, VIRTIO_ID_NET);
+
+       while (1) {
+               ssize_t len;
+
+               net_poll[NET_FD_VIRTIO_NET].revents = 0;
+               net_poll[NET_FD_TUN].revents = 0;
+
+               /* Start polling for data from tap and virtio net */
+               err = poll(net_poll, 2, -1);
+               if (err < 0) {
+                       mpsslog("%s poll failed %s\n",
+                               __func__, strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+                       err = wait_for_card_driver(mic,
+                                                  mic->mic_net.virtio_net_fd,
+                                                  VIRTIO_ID_NET);
+                       if (err) {
+                               mpsslog("%s %s %d Exiting...\n",
+                                       mic->name, __func__, __LINE__);
+                               break;
+                       }
+               }
+               /*
+                * Check if there is data to be read from TUN and write to
+                * virtio net fd if there is.
+                */
+               if (net_poll[NET_FD_TUN].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(net_poll[NET_FD_TUN].fd,
+                               copy.iov, copy.iovcnt);
+                       if (len > 0) {
+                               struct virtio_net_hdr *hdr
+                                       = (struct virtio_net_hdr *)vnet_hdr[0];
+
+                               /* Disable checksums on the card since we are on
+                                  a reliable PCIe link */
+                               hdr->flags |= VIRTIO_NET_HDR_F_DATA_VALID;
+#ifdef DEBUG
+                               mpsslog("%s %s %d hdr->flags 0x%x ", mic->name,
+                                       __func__, __LINE__, hdr->flags);
+                               mpsslog("copy.out_len %d hdr->gso_type 0x%x\n",
+                                       copy.out_len, hdr->gso_type);
+#endif
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, &copy,
+                                            len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &tx_vr,
+                                       &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(&copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0[1].iov_len = MAX_NET_PKT_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ", mic->name,
+                                       __func__, __LINE__, strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               /*
+                * Check if there is data to be read from virtio net and
+                * write to TUN if there is.
+                */
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, &copy,
+                                            MAX_NET_PKT_SIZE
+                                       + sizeof(struct virtio_net_hdr));
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_net.virtio_net_fd, &rx_vr,
+                                       &copy);
+                               if (!err) {
+#ifdef DEBUG
+                                       struct virtio_net_hdr *hdr
+                                               = (struct virtio_net_hdr *)
+                                                       vnet_hdr[1];
+
+                                       mpsslog("%s %s %d hdr->flags 0x%x, ",
+                                               mic->name, __func__, __LINE__,
+                                               hdr->flags);
+                                       mpsslog("out_len %d gso_type 0x%x\n",
+                                               copy.out_len,
+                                               hdr->gso_type);
+#endif
+                                       /* Set the correct output iov_len */
+                                       iov1[1].iov_len = copy.out_len -
+                                               sizeof(struct virtio_net_hdr);
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(net_poll[NET_FD_TUN].fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, &copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (net_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+done:
+       pthread_exit(NULL);
+}
+
+/* virtio_console */
+#define VIRTIO_CONSOLE_FD 0
+#define MONITOR_FD (VIRTIO_CONSOLE_FD + 1)
+#define MAX_CONSOLE_FD (MONITOR_FD + 1)  /* must be the last one + 1 */
+#define MAX_BUFFER_SIZE PAGE_SIZE
+
+static void *
+virtio_console(void *arg)
+{
+       static __u8 vcons_buf[2][PAGE_SIZE];
+       struct iovec vcons_iov[2] = {
+               { .iov_base = vcons_buf[0], .iov_len = sizeof(vcons_buf[0]) },
+               { .iov_base = vcons_buf[1], .iov_len = sizeof(vcons_buf[1]) },
+       };
+       struct iovec *iov0 = &vcons_iov[0], *iov1 = &vcons_iov[1];
+       struct mic_info *mic = (struct mic_info *)arg;
+       int err;
+       struct pollfd console_poll[MAX_CONSOLE_FD];
+       int pty_fd;
+       char *pts_name;
+       ssize_t len;
+       struct mic_vring tx_vr, rx_vr;
+       struct mic_copy_desc copy;
+       struct mic_device_desc *desc;
+
+       pty_fd = posix_openpt(O_RDWR);
+       if (pty_fd < 0) {
+               mpsslog("can't open a pseudoterminal master device: %s\n",
+                       strerror(errno));
+               goto _return;
+       }
+       pts_name = ptsname(pty_fd);
+       if (pts_name == NULL) {
+               mpsslog("can't get pts name\n");
+               goto _close_pty;
+       }
+       printf("%s console message goes to %s\n", mic->name, pts_name);
+       mpsslog("%s console message goes to %s\n", mic->name, pts_name);
+       err = grantpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't grant access: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       err = unlockpt(pty_fd);
+       if (err < 0) {
+               mpsslog("can't unlock a pseudoterminal: %s %s\n",
+                       pts_name, strerror(errno));
+               goto _close_pty;
+       }
+       console_poll[MONITOR_FD].fd = pty_fd;
+       console_poll[MONITOR_FD].events = POLLIN;
+
+       console_poll[VIRTIO_CONSOLE_FD].fd = mic->mic_console.virtio_console_fd;
+       console_poll[VIRTIO_CONSOLE_FD].events = POLLIN;
+
+       if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd,
+                                 VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr,
+               virtcons_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               goto _close_pty;
+       }
+
+       copy.iovcnt = 1;
+       desc = get_device_desc(mic, VIRTIO_ID_CONSOLE);
+
+       for (;;) {
+               console_poll[MONITOR_FD].revents = 0;
+               console_poll[VIRTIO_CONSOLE_FD].revents = 0;
+               err = poll(console_poll, MAX_CONSOLE_FD, -1);
+               if (err < 0) {
+                       mpsslog("%s %d: poll failed: %s\n", __func__, __LINE__,
+                               strerror(errno));
+                       continue;
+               }
+               if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+                       err = wait_for_card_driver(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       VIRTIO_ID_CONSOLE);
+                       if (err) {
+                               mpsslog("%s %s %d Exiting...\n",
+                                       mic->name, __func__, __LINE__);
+                               break;
+                       }
+               }
+
+               if (console_poll[MONITOR_FD].revents & POLLIN) {
+                       copy.iov = iov0;
+                       len = readv(pty_fd, copy.iov, copy.iovcnt);
+                       if (len > 0) {
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read from tap 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       len);
+#endif
+                               spin_for_descriptors(mic, &tx_vr);
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr,
+                                            &copy, len);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &tx_vr, &copy);
+                               if (err < 0) {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                               }
+                               if (!err)
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                               disp_iovec(mic, copy, __func__, __LINE__);
+                               mpsslog("%s %s %d wrote to net 0x%lx\n",
+                                       mic->name, __func__, __LINE__,
+                                       sum_iovec_len(copy));
+#endif
+                               /* Reinitialize IOV for next run */
+                               iov0->iov_len = PAGE_SIZE;
+                       } else if (len < 0) {
+                               disp_iovec(mic, &copy, __func__, __LINE__);
+                               mpsslog("%s %s %d read failed %s ",
+                                       mic->name, __func__, __LINE__,
+                                       strerror(errno));
+                               mpsslog("cnt %d sum %zd\n",
+                                       copy.iovcnt, sum_iovec_len(&copy));
+                       }
+               }
+
+               if (console_poll[VIRTIO_CONSOLE_FD].revents & POLLIN) {
+                       while (rx_vr.info->avail_idx !=
+                               le16toh(rx_vr.vr.avail->idx)) {
+                               copy.iov = iov1;
+                               txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr,
+                                            &copy, PAGE_SIZE);
+
+                               err = mic_virtio_copy(mic,
+                                       mic->mic_console.virtio_console_fd,
+                                       &rx_vr, &copy);
+                               if (!err) {
+                                       /* Set the correct output iov_len */
+                                       iov1->iov_len = copy.out_len;
+                                       verify_out_len(mic, &copy);
+#ifdef DEBUG
+                                       disp_iovec(mic, copy, __func__,
+                                                  __LINE__);
+                                       mpsslog("%s %s %d ",
+                                               mic->name, __func__, __LINE__);
+                                       mpsslog("read from net 0x%lx\n",
+                                               sum_iovec_len(copy));
+#endif
+                                       len = writev(pty_fd,
+                                               copy.iov, copy.iovcnt);
+                                       if (len != sum_iovec_len(&copy)) {
+                                               mpsslog("Tun write failed %s ",
+                                                       strerror(errno));
+                                               mpsslog("len 0x%zx ", len);
+                                               mpsslog("read_len 0x%zx\n",
+                                                       sum_iovec_len(&copy));
+                                       } else {
+#ifdef DEBUG
+                                               disp_iovec(mic, copy, __func__,
+                                                          __LINE__);
+                                               mpsslog("%s %s %d ",
+                                                       mic->name, __func__,
+                                                       __LINE__);
+                                               mpsslog("wrote to tap 0x%lx\n",
+                                                       len);
+#endif
+                                       }
+                               } else {
+                                       mpsslog("%s %s %d mic_virtio_copy %s\n",
+                                               mic->name, __func__, __LINE__,
+                                               strerror(errno));
+                                       break;
+                               }
+                       }
+               }
+               if (console_poll[NET_FD_VIRTIO_NET].revents & POLLERR)
+                       mpsslog("%s: %s: POLLERR\n", __func__, mic->name);
+       }
+_close_pty:
+       close(pty_fd);
+_return:
+       pthread_exit(NULL);
+}
+
+static void
+add_virtio_device(struct mic_info *mic, struct mic_device_desc *dd)
+{
+       char path[PATH_MAX];
+       int fd, err;
+
+       snprintf(path, PATH_MAX, "/dev/vop_virtio%d", mic->id);
+       fd = open(path, O_RDWR);
+       if (fd < 0) {
+               mpsslog("Could not open %s %s\n", path, strerror(errno));
+               return;
+       }
+
+       err = ioctl(fd, MIC_VIRTIO_ADD_DEVICE, dd);
+       if (err < 0) {
+               mpsslog("Could not add %d %s\n", dd->type, strerror(errno));
+               close(fd);
+               return;
+       }
+       switch (dd->type) {
+       case VIRTIO_ID_NET:
+               mic->mic_net.virtio_net_fd = fd;
+               mpsslog("Added VIRTIO_ID_NET for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_CONSOLE:
+               mic->mic_console.virtio_console_fd = fd;
+               mpsslog("Added VIRTIO_ID_CONSOLE for %s\n", mic->name);
+               break;
+       case VIRTIO_ID_BLOCK:
+               mic->mic_virtblk.virtio_block_fd = fd;
+               mpsslog("Added VIRTIO_ID_BLOCK for %s\n", mic->name);
+               break;
+       }
+}
+
+static bool
+set_backend_file(struct mic_info *mic)
+{
+       FILE *config;
+       char buff[PATH_MAX], *line, *evv, *p;
+
+       snprintf(buff, PATH_MAX, "%s/mpssd%03d.conf", mic_config_dir, mic->id);
+       config = fopen(buff, "r");
+       if (config == NULL)
+               return false;
+       do {  /* look for "virtblk_backend=XXXX" */
+               line = fgets(buff, PATH_MAX, config);
+               if (line == NULL)
+                       break;
+               if (*line == '#')
+                       continue;
+               p = strchr(line, '\n');
+               if (p)
+                       *p = '\0';
+       } while (strncmp(line, virtblk_backend, strlen(virtblk_backend)) != 0);
+       fclose(config);
+       if (line == NULL)
+               return false;
+       evv = strchr(line, '=');
+       if (evv == NULL)
+               return false;
+       mic->mic_virtblk.backend_file = malloc(strlen(evv) + 1);
+       if (mic->mic_virtblk.backend_file == NULL) {
+               mpsslog("%s %d can't allocate memory\n", mic->name, mic->id);
+               return false;
+       }
+       strcpy(mic->mic_virtblk.backend_file, evv + 1);
+       return true;
+}
+
+#define SECTOR_SIZE 512
+static bool
+set_backend_size(struct mic_info *mic)
+{
+       mic->mic_virtblk.backend_size = lseek(mic->mic_virtblk.backend, 0,
+               SEEK_END);
+       if (mic->mic_virtblk.backend_size < 0) {
+               mpsslog("%s: can't seek: %s\n",
+                       mic->name, mic->mic_virtblk.backend_file);
+               return false;
+       }
+       virtblk_dev_page.blk_config.capacity =
+               mic->mic_virtblk.backend_size / SECTOR_SIZE;
+       if ((mic->mic_virtblk.backend_size % SECTOR_SIZE) != 0)
+               virtblk_dev_page.blk_config.capacity++;
+
+       virtblk_dev_page.blk_config.capacity =
+               htole64(virtblk_dev_page.blk_config.capacity);
+
+       return true;
+}
+
+static bool
+open_backend(struct mic_info *mic)
+{
+       if (!set_backend_file(mic))
+               goto _error_exit;
+       mic->mic_virtblk.backend = open(mic->mic_virtblk.backend_file, O_RDWR);
+       if (mic->mic_virtblk.backend < 0) {
+               mpsslog("%s: can't open: %s\n", mic->name,
+                       mic->mic_virtblk.backend_file);
+               goto _error_free;
+       }
+       if (!set_backend_size(mic))
+               goto _error_close;
+       mic->mic_virtblk.backend_addr = mmap(NULL,
+               mic->mic_virtblk.backend_size,
+               PROT_READ|PROT_WRITE, MAP_SHARED,
+               mic->mic_virtblk.backend, 0L);
+       if (mic->mic_virtblk.backend_addr == MAP_FAILED) {
+               mpsslog("%s: can't map: %s %s\n",
+                       mic->name, mic->mic_virtblk.backend_file,
+                       strerror(errno));
+               goto _error_close;
+       }
+       return true;
+
+ _error_close:
+       close(mic->mic_virtblk.backend);
+ _error_free:
+       free(mic->mic_virtblk.backend_file);
+ _error_exit:
+       return false;
+}
+
+static void
+close_backend(struct mic_info *mic)
+{
+       munmap(mic->mic_virtblk.backend_addr, mic->mic_virtblk.backend_size);
+       close(mic->mic_virtblk.backend);
+       free(mic->mic_virtblk.backend_file);
+}
+
+static bool
+start_virtblk(struct mic_info *mic, struct mic_vring *vring)
+{
+       if (((unsigned long)&virtblk_dev_page.blk_config % 8) != 0) {
+               mpsslog("%s: blk_config is not 8 byte aligned.\n",
+                       mic->name);
+               return false;
+       }
+       add_virtio_device(mic, &virtblk_dev_page.dd);
+       if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd,
+                                 VIRTIO_ID_BLOCK, vring, NULL,
+                                 virtblk_dev_page.dd.num_vq)) {
+               mpsslog("%s init_vr failed %s\n",
+                       mic->name, strerror(errno));
+               return false;
+       }
+       return true;
+}
+
+static void
+stop_virtblk(struct mic_info *mic)
+{
+       int vr_size, ret;
+
+       vr_size = PAGE_ALIGN(_vring_size(MIC_VRING_ENTRIES,
+                                        MIC_VIRTIO_RING_ALIGN) +
+                            sizeof(struct _mic_vring_info));
+       ret = munmap(mic->mic_virtblk.block_dp,
+               MIC_DEVICE_PAGE_END + vr_size * virtblk_dev_page.dd.num_vq);
+       if (ret < 0)
+               mpsslog("%s munmap errno %d\n", mic->name, errno);
+       close(mic->mic_virtblk.virtio_block_fd);
+}
+
+static __u8
+header_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) {
+               mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) {
+               mpsslog("%s() %d: alone\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       if (le16toh(desc->flags) & VRING_DESC_F_WRITE) {
+               mpsslog("%s() %d: not read\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+read_header(int fd, struct virtio_blk_outhdr *hdr, __u32 desc_idx)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_len = sizeof(*hdr);
+       iovec.iov_base = hdr;
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static int
+transfer_blocks(int fd, struct iovec *iovec, __u32 iovcnt)
+{
+       struct mic_copy_desc copy;
+
+       copy.iov = iovec;
+       copy.iovcnt = iovcnt;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = false;  /* do not update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+static __u8
+status_error_check(struct vring_desc *desc)
+{
+       if (le32toh(desc->len) != sizeof(__u8)) {
+               mpsslog("%s() %d: length is not sizeof(status)\n",
+                       __func__, __LINE__);
+               return -EIO;
+       }
+       return 0;
+}
+
+static int
+write_status(int fd, __u8 *status)
+{
+       struct iovec iovec;
+       struct mic_copy_desc copy;
+
+       iovec.iov_base = status;
+       iovec.iov_len = sizeof(*status);
+       copy.iov = &iovec;
+       copy.iovcnt = 1;
+       copy.vr_idx = 0;  /* only one vring on virtio_block */
+       copy.update_used = true; /* Update used index */
+       return ioctl(fd, MIC_VIRTIO_COPY_DESC, &copy);
+}
+
+#ifndef VIRTIO_BLK_T_GET_ID
+#define VIRTIO_BLK_T_GET_ID    8
+#endif
+
+static void *
+virtio_block(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       int ret;
+       struct pollfd block_poll;
+       struct mic_vring vring;
+       __u16 avail_idx;
+       __u32 desc_idx;
+       struct vring_desc *desc;
+       struct iovec *iovec, *piov;
+       __u8 status;
+       __u32 buffer_desc_idx;
+       struct virtio_blk_outhdr hdr;
+       void *fos;
+
+       for (;;) {  /* forever */
+               if (!open_backend(mic)) { /* No virtblk */
+                       for (mic->mic_virtblk.signaled = 0;
+                               !mic->mic_virtblk.signaled;)
+                               sleep(1);
+                       continue;
+               }
+
+               /* backend file is specified. */
+               if (!start_virtblk(mic, &vring))
+                       goto _close_backend;
+               iovec = malloc(sizeof(*iovec) *
+                       le32toh(virtblk_dev_page.blk_config.seg_max));
+               if (!iovec) {
+                       mpsslog("%s: can't alloc iovec: %s\n",
+                               mic->name, strerror(ENOMEM));
+                       goto _stop_virtblk;
+               }
+
+               block_poll.fd = mic->mic_virtblk.virtio_block_fd;
+               block_poll.events = POLLIN;
+               for (mic->mic_virtblk.signaled = 0;
+                    !mic->mic_virtblk.signaled;) {
+                       block_poll.revents = 0;
+                                       /* timeout in 1 sec to see signaled */
+                       ret = poll(&block_poll, 1, 1000);
+                       if (ret < 0) {
+                               mpsslog("%s %d: poll failed: %s\n",
+                                       __func__, __LINE__,
+                                       strerror(errno));
+                               continue;
+                       }
+
+                       if (!(block_poll.revents & POLLIN)) {
+#ifdef DEBUG
+                               mpsslog("%s %d: block_poll.revents=0x%x\n",
+                                       __func__, __LINE__, block_poll.revents);
+#endif
+                               continue;
+                       }
+
+                       /* POLLIN */
+                       while (vring.info->avail_idx !=
+                               le16toh(vring.vr.avail->idx)) {
+                               /* read header element */
+                               avail_idx =
+                                       vring.info->avail_idx &
+                                       (vring.vr.num - 1);
+                               desc_idx = le16toh(
+                                       vring.vr.avail->ring[avail_idx]);
+                               desc = &vring.vr.desc[desc_idx];
+#ifdef DEBUG
+                               mpsslog("%s() %d: avail_idx=%d ",
+                                       __func__, __LINE__,
+                                       vring.info->avail_idx);
+                               mpsslog("vring.vr.num=%d desc=%p\n",
+                                       vring.vr.num, desc);
+#endif
+                               status = header_error_check(desc);
+                               ret = read_header(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &hdr, desc_idx);
+                               if (ret < 0) {
+                                       mpsslog("%s() %d %s: ret=%d %s\n",
+                                               __func__, __LINE__,
+                                               mic->name, ret,
+                                               strerror(errno));
+                                       break;
+                               }
+                               /* buffer element */
+                               piov = iovec;
+                               status = 0;
+                               fos = mic->mic_virtblk.backend_addr +
+                                       (hdr.sector * SECTOR_SIZE);
+                               buffer_desc_idx = next_desc(desc);
+                               desc_idx = buffer_desc_idx;
+                               for (desc = &vring.vr.desc[buffer_desc_idx];
+                                    desc->flags & VRING_DESC_F_NEXT;
+                                    desc_idx = next_desc(desc),
+                                            desc = &vring.vr.desc[desc_idx]) {
+                                       piov->iov_len = desc->len;
+                                       piov->iov_base = fos;
+                                       piov++;
+                                       fos += desc->len;
+                               }
+                               /* Returning NULLs for VIRTIO_BLK_T_GET_ID. */
+                               if (hdr.type & ~(VIRTIO_BLK_T_OUT |
+                                       VIRTIO_BLK_T_GET_ID)) {
+                                       /*
+                                         VIRTIO_BLK_T_IN - does not do
+                                         anything. Probably for documenting.
+                                         VIRTIO_BLK_T_SCSI_CMD - for
+                                         virtio_scsi.
+                                         VIRTIO_BLK_T_FLUSH - turned off in
+                                         config space.
+                                         VIRTIO_BLK_T_BARRIER - defined but not
+                                         used in anywhere.
+                                       */
+                                       mpsslog("%s() %d: type %x ",
+                                               __func__, __LINE__,
+                                               hdr.type);
+                                       mpsslog("is not supported\n");
+                                       status = -ENOTSUP;
+
+                               } else {
+                                       ret = transfer_blocks(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                               iovec,
+                                               piov - iovec);
+                                       if (ret < 0 &&
+                                           status != 0)
+                                               status = ret;
+                               }
+                               /* write status and update used pointer */
+                               if (status != 0)
+                                       status = status_error_check(desc);
+                               ret = write_status(
+                                       mic->mic_virtblk.virtio_block_fd,
+                                       &status);
+#ifdef DEBUG
+                               mpsslog("%s() %d: write status=%d on desc=%p\n",
+                                       __func__, __LINE__,
+                                       status, desc);
+#endif
+                       }
+               }
+               free(iovec);
+_stop_virtblk:
+               stop_virtblk(mic);
+_close_backend:
+               close_backend(mic);
+       }  /* forever */
+
+       pthread_exit(NULL);
+}
+
+static void
+reset(struct mic_info *mic)
+{
+#define RESET_TIMEOUT 120
+       int i = RESET_TIMEOUT;
+       setsysfs(mic->name, "state", "reset");
+       while (i) {
+               char *state;
+               state = readsysfs(mic->name, "state");
+               if (!state)
+                       goto retry;
+               mpsslog("%s: %s %d state %s\n",
+                       mic->name, __func__, __LINE__, state);
+
+               if (!strcmp(state, "ready")) {
+                       free(state);
+                       break;
+               }
+               free(state);
+retry:
+               sleep(1);
+               i--;
+       }
+}
+
+static int
+get_mic_shutdown_status(struct mic_info *mic, char *shutdown_status)
+{
+       if (!strcmp(shutdown_status, "nop"))
+               return MIC_NOP;
+       if (!strcmp(shutdown_status, "crashed"))
+               return MIC_CRASHED;
+       if (!strcmp(shutdown_status, "halted"))
+               return MIC_HALTED;
+       if (!strcmp(shutdown_status, "poweroff"))
+               return MIC_POWER_OFF;
+       if (!strcmp(shutdown_status, "restart"))
+               return MIC_RESTART;
+       mpsslog("%s: BUG invalid status %s\n", mic->name, shutdown_status);
+       /* Invalid state */
+       assert(0);
+};
+
+static int get_mic_state(struct mic_info *mic)
+{
+       char *state = NULL;
+       enum mic_states mic_state;
+
+       while (!state) {
+               state = readsysfs(mic->name, "state");
+               sleep(1);
+       }
+       mpsslog("%s: %s %d state %s\n",
+               mic->name, __func__, __LINE__, state);
+
+       if (!strcmp(state, "ready")) {
+               mic_state = MIC_READY;
+       } else if (!strcmp(state, "booting")) {
+               mic_state = MIC_BOOTING;
+       } else if (!strcmp(state, "online")) {
+               mic_state = MIC_ONLINE;
+       } else if (!strcmp(state, "shutting_down")) {
+               mic_state = MIC_SHUTTING_DOWN;
+       } else if (!strcmp(state, "reset_failed")) {
+               mic_state = MIC_RESET_FAILED;
+       } else if (!strcmp(state, "resetting")) {
+               mic_state = MIC_RESETTING;
+       } else {
+               mpsslog("%s: BUG invalid state %s\n", mic->name, state);
+               assert(0);
+       }
+
+       free(state);
+       return mic_state;
+};
+
+static void mic_handle_shutdown(struct mic_info *mic)
+{
+#define SHUTDOWN_TIMEOUT 60
+       int i = SHUTDOWN_TIMEOUT;
+       char *shutdown_status;
+       while (i) {
+               shutdown_status = readsysfs(mic->name, "shutdown_status");
+               if (!shutdown_status) {
+                       sleep(1);
+                       continue;
+               }
+               mpsslog("%s: %s %d shutdown_status %s\n",
+                       mic->name, __func__, __LINE__, shutdown_status);
+               switch (get_mic_shutdown_status(mic, shutdown_status)) {
+               case MIC_RESTART:
+                       mic->restart = 1;
+               case MIC_HALTED:
+               case MIC_POWER_OFF:
+               case MIC_CRASHED:
+                       free(shutdown_status);
+                       goto reset;
+               default:
+                       break;
+               }
+               free(shutdown_status);
+               sleep(1);
+               i--;
+       }
+reset:
+       if (!i)
+               mpsslog("%s: %s %d timing out waiting for shutdown_status %s\n",
+                       mic->name, __func__, __LINE__, shutdown_status);
+       reset(mic);
+}
+
+static int open_state_fd(struct mic_info *mic)
+{
+       char pathname[PATH_MAX];
+       int fd;
+
+       snprintf(pathname, PATH_MAX - 1, "%s/%s/%s",
+                MICSYSFSDIR, mic->name, "state");
+
+       fd = open(pathname, O_RDONLY);
+       if (fd < 0)
+               mpsslog("%s: opening file %s failed %s\n",
+                       mic->name, pathname, strerror(errno));
+       return fd;
+}
+
+static int block_till_state_change(int fd, struct mic_info *mic)
+{
+       struct pollfd ufds[1];
+       char value[PAGE_SIZE];
+       int ret;
+
+       ufds[0].fd = fd;
+       ufds[0].events = POLLERR | POLLPRI;
+       ret = poll(ufds, 1, -1);
+       if (ret < 0) {
+               mpsslog("%s: %s %d poll failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               return ret;
+       }
+
+       ret = lseek(fd, 0, SEEK_SET);
+       if (ret < 0) {
+               mpsslog("%s: %s %d Failed to seek to 0: %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               return ret;
+       }
+
+       ret = read(fd, value, sizeof(value));
+       if (ret < 0) {
+               mpsslog("%s: %s %d Failed to read sysfs entry: %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               return ret;
+       }
+
+       return 0;
+}
+
+static void *
+mic_config(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       int fd, ret, stat = 0;
+
+       fd = open_state_fd(mic);
+       if (fd < 0) {
+               mpsslog("%s: %s %d open state fd failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               goto exit;
+       }
+
+       do {
+               ret = block_till_state_change(fd, mic);
+               if (ret < 0) {
+                       mpsslog("%s: %s %d block_till_state_change error %s\n",
+                               mic->name, __func__, __LINE__, strerror(errno));
+                       goto close_exit;
+               }
+
+               switch (get_mic_state(mic)) {
+               case MIC_SHUTTING_DOWN:
+                       mic_handle_shutdown(mic);
+                       break;
+               case MIC_READY:
+               case MIC_RESET_FAILED:
+                       ret = kill(mic->pid, SIGTERM);
+                       mpsslog("%s: %s %d kill pid %d ret %d\n",
+                               mic->name, __func__, __LINE__,
+                               mic->pid, ret);
+                       if (!ret) {
+                               ret = waitpid(mic->pid, &stat,
+                                             WIFSIGNALED(stat));
+                               mpsslog("%s: %s %d waitpid ret %d pid %d\n",
+                                       mic->name, __func__, __LINE__,
+                                       ret, mic->pid);
+                       }
+                       if (mic->boot_on_resume) {
+                               setsysfs(mic->name, "state", "boot");
+                               mic->boot_on_resume = 0;
+                       }
+                       goto close_exit;
+               default:
+                       break;
+               }
+       } while (1);
+
+close_exit:
+       close(fd);
+exit:
+       init_mic(mic);
+       pthread_exit(NULL);
+}
+
+static void
+set_cmdline(struct mic_info *mic)
+{
+       char buffer[PATH_MAX];
+       int len;
+
+       len = snprintf(buffer, PATH_MAX,
+               "clocksource=tsc highres=off nohz=off ");
+       len += snprintf(buffer + len, PATH_MAX - len,
+               "cpufreq_on;corec6_off;pc3_off;pc6_off ");
+       len += snprintf(buffer + len, PATH_MAX - len,
+               "ifcfg=static;address,172.31.%d.1;netmask,255.255.255.0",
+               mic->id + 1);
+
+       setsysfs(mic->name, "cmdline", buffer);
+       mpsslog("%s: Command line: \"%s\"\n", mic->name, buffer);
+       snprintf(buffer, PATH_MAX, "172.31.%d.1", mic->id + 1);
+       mpsslog("%s: IPADDR: \"%s\"\n", mic->name, buffer);
+}
+
+static void
+set_log_buf_info(struct mic_info *mic)
+{
+       int fd;
+       off_t len;
+       char system_map[] = "/lib/firmware/mic/System.map";
+       char *map, *temp, log_buf[17] = {'\0'};
+
+       fd = open(system_map, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("%s: Opening System.map failed: %d\n",
+                       mic->name, errno);
+               return;
+       }
+       len = lseek(fd, 0, SEEK_END);
+       if (len < 0) {
+               mpsslog("%s: Reading System.map size failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       map = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (map == MAP_FAILED) {
+               mpsslog("%s: mmap of System.map failed: %d\n",
+                       mic->name, errno);
+               close(fd);
+               return;
+       }
+       temp = strstr(map, "__log_buf");
+       if (!temp) {
+               mpsslog("%s: __log_buf not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_addr", log_buf);
+       mpsslog("%s: log_buf_addr: %s\n", mic->name, log_buf);
+       temp = strstr(map, "log_buf_len");
+       if (!temp) {
+               mpsslog("%s: log_buf_len not found: %d\n", mic->name, errno);
+               munmap(map, len);
+               close(fd);
+               return;
+       }
+       strncpy(log_buf, temp - 19, 16);
+       setsysfs(mic->name, "log_buf_len", log_buf);
+       mpsslog("%s: log_buf_len: %s\n", mic->name, log_buf);
+       munmap(map, len);
+       close(fd);
+}
+
+static void
+change_virtblk_backend(int x, siginfo_t *siginfo, void *p)
+{
+       struct mic_info *mic;
+
+       for (mic = mic_list.next; mic != NULL; mic = mic->next)
+               mic->mic_virtblk.signaled = 1/* true */;
+}
+
+static void
+set_mic_boot_params(struct mic_info *mic)
+{
+       set_log_buf_info(mic);
+       set_cmdline(mic);
+}
+
+static void *
+init_mic(void *arg)
+{
+       struct mic_info *mic = (struct mic_info *)arg;
+       struct sigaction ignore = {
+               .sa_flags = 0,
+               .sa_handler = SIG_IGN
+       };
+       struct sigaction act = {
+               .sa_flags = SA_SIGINFO,
+               .sa_sigaction = change_virtblk_backend,
+       };
+       char buffer[PATH_MAX];
+       int err, fd;
+
+       /*
+        * Currently, one virtio block device is supported for each MIC card
+        * at a time. Any user (or test) can send a SIGUSR1 to the MIC daemon.
+        * The signal informs the virtio block backend about a change in the
+        * configuration file which specifies the virtio backend file name on
+        * the host. Virtio block backend then re-reads the configuration file
+        * and switches to the new block device. This signalling mechanism may
+        * not be required once multiple virtio block devices are supported by
+        * the MIC daemon.
+        */
+       sigaction(SIGUSR1, &ignore, NULL);
+retry:
+       fd = open_state_fd(mic);
+       if (fd < 0) {
+               mpsslog("%s: %s %d open state fd failed %s\n",
+                       mic->name, __func__, __LINE__, strerror(errno));
+               sleep(2);
+               goto retry;
+       }
+
+       if (mic->restart) {
+               snprintf(buffer, PATH_MAX, "boot");
+               setsysfs(mic->name, "state", buffer);
+               mpsslog("%s restarting mic %d\n",
+                       mic->name, mic->restart);
+               mic->restart = 0;
+       }
+
+       while (1) {
+               while (block_till_state_change(fd, mic)) {
+                       mpsslog("%s: %s %d block_till_state_change error %s\n",
+                               mic->name, __func__, __LINE__, strerror(errno));
+                       sleep(2);
+                       continue;
+               }
+
+               if (get_mic_state(mic) == MIC_BOOTING)
+                       break;
+       }
+
+       mic->pid = fork();
+       switch (mic->pid) {
+       case 0:
+               add_virtio_device(mic, &virtcons_dev_page.dd);
+               add_virtio_device(mic, &virtnet_dev_page.dd);
+               err = pthread_create(&mic->mic_console.console_thread, NULL,
+                       virtio_console, mic);
+               if (err)
+                       mpsslog("%s virtcons pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_net.net_thread, NULL,
+                       virtio_net, mic);
+               if (err)
+                       mpsslog("%s virtnet pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               err = pthread_create(&mic->mic_virtblk.block_thread, NULL,
+                       virtio_block, mic);
+               if (err)
+                       mpsslog("%s virtblk pthread_create failed %s\n",
+                               mic->name, strerror(err));
+               sigemptyset(&act.sa_mask);
+               err = sigaction(SIGUSR1, &act, NULL);
+               if (err)
+                       mpsslog("%s sigaction SIGUSR1 failed %s\n",
+                               mic->name, strerror(errno));
+               while (1)
+                       sleep(60);
+       case -1:
+               mpsslog("fork failed MIC name %s id %d errno %d\n",
+                       mic->name, mic->id, errno);
+               break;
+       default:
+               err = pthread_create(&mic->config_thread, NULL,
+                                    mic_config, mic);
+               if (err)
+                       mpsslog("%s mic_config pthread_create failed %s\n",
+                               mic->name, strerror(err));
+       }
+
+       return NULL;
+}
+
+static void
+start_daemon(void)
+{
+       struct mic_info *mic;
+       int err;
+
+       for (mic = mic_list.next; mic; mic = mic->next) {
+               set_mic_boot_params(mic);
+               err = pthread_create(&mic->init_thread, NULL, init_mic, mic);
+               if (err)
+                       mpsslog("%s init_mic pthread_create failed %s\n",
+                               mic->name, strerror(err));
+       }
+
+       while (1)
+               sleep(60);
+}
+
+static int
+init_mic_list(void)
+{
+       struct mic_info *mic = &mic_list;
+       struct dirent *file;
+       DIR *dp;
+       int cnt = 0;
+
+       dp = opendir(MICSYSFSDIR);
+       if (!dp)
+               return 0;
+
+       while ((file = readdir(dp)) != NULL) {
+               if (!strncmp(file->d_name, "mic", 3)) {
+                       mic->next = calloc(1, sizeof(struct mic_info));
+                       if (mic->next) {
+                               mic = mic->next;
+                               mic->id = atoi(&file->d_name[3]);
+                               mic->name = malloc(strlen(file->d_name) + 16);
+                               if (mic->name)
+                                       strcpy(mic->name, file->d_name);
+                               mpsslog("MIC name %s id %d\n", mic->name,
+                                       mic->id);
+                               cnt++;
+                       }
+               }
+       }
+
+       closedir(dp);
+       return cnt;
+}
+
+void
+mpsslog(char *format, ...)
+{
+       va_list args;
+       char buffer[4096];
+       char ts[52], *ts1;
+       time_t t;
+
+       if (logfp == NULL)
+               return;
+
+       va_start(args, format);
+       vsprintf(buffer, format, args);
+       va_end(args);
+
+       time(&t);
+       ts1 = ctime_r(&t, ts);
+       ts1[strlen(ts1) - 1] = '\0';
+       fprintf(logfp, "%s: %s", ts1, buffer);
+
+       fflush(logfp);
+}
+
+int
+main(int argc, char *argv[])
+{
+       int cnt;
+       pid_t pid;
+
+       myname = argv[0];
+
+       logfp = fopen(LOGFILE_NAME, "a+");
+       if (!logfp) {
+               fprintf(stderr, "cannot open logfile '%s'\n", LOGFILE_NAME);
+               exit(1);
+       }
+       pid = fork();
+       switch (pid) {
+       case 0:
+               break;
+       case -1:
+               exit(2);
+       default:
+               exit(0);
+       }
+
+       mpsslog("MIC Daemon start\n");
+
+       cnt = init_mic_list();
+       if (cnt == 0) {
+               mpsslog("MIC module not loaded\n");
+               exit(3);
+       }
+       mpsslog("MIC found %d devices\n", cnt);
+
+       start_daemon();
+
+       exit(0);
+}
diff --git a/samples/mic/mpssd/mpssd.h b/samples/mic/mpssd/mpssd.h
new file mode 100644 (file)
index 0000000..8bd6494
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+#ifndef _MPSSD_H_
+#define _MPSSD_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/dir.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pthread.h>
+#include <signal.h>
+#include <limits.h>
+#include <syslog.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <linux/if_tun.h>
+#include <linux/if_tun.h>
+#include <linux/virtio_ids.h>
+
+#define MICSYSFSDIR "/sys/class/mic"
+#define LOGFILE_NAME "/var/log/mpssd"
+#define PAGE_SIZE 4096
+
+struct mic_console_info {
+       pthread_t       console_thread;
+       int             virtio_console_fd;
+       void            *console_dp;
+};
+
+struct mic_net_info {
+       pthread_t       net_thread;
+       int             virtio_net_fd;
+       int             tap_fd;
+       void            *net_dp;
+};
+
+struct mic_virtblk_info {
+       pthread_t       block_thread;
+       int             virtio_block_fd;
+       void            *block_dp;
+       volatile sig_atomic_t   signaled;
+       char            *backend_file;
+       int             backend;
+       void            *backend_addr;
+       long            backend_size;
+};
+
+struct mic_info {
+       int             id;
+       char            *name;
+       pthread_t       config_thread;
+       pthread_t       init_thread;
+       pid_t           pid;
+       struct mic_console_info mic_console;
+       struct mic_net_info     mic_net;
+       struct mic_virtblk_info mic_virtblk;
+       int             restart;
+       int             boot_on_resume;
+       struct mic_info *next;
+};
+
+__attribute__((format(printf, 1, 2)))
+void mpsslog(char *format, ...);
+char *readsysfs(char *dir, char *entry);
+int setsysfs(char *dir, char *entry, char *value);
+#endif
diff --git a/samples/mic/mpssd/sysfs.c b/samples/mic/mpssd/sysfs.c
new file mode 100644 (file)
index 0000000..8dd3269
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2013 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC User Space Tools.
+ */
+
+#include "mpssd.h"
+
+#define PAGE_SIZE 4096
+
+char *
+readsysfs(char *dir, char *entry)
+{
+       char filename[PATH_MAX];
+       char value[PAGE_SIZE];
+       char *string = NULL;
+       int fd;
+       int len;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX,
+                        "%s/%s/%s", MICSYSFSDIR, dir, entry);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               return NULL;
+       }
+
+       len = read(fd, value, sizeof(value));
+       if (len < 0) {
+               mpsslog("Failed to read sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto readsys_ret;
+       }
+       if (len == 0)
+               goto readsys_ret;
+
+       value[len - 1] = '\0';
+
+       string = malloc(strlen(value) + 1);
+       if (string)
+               strcpy(string, value);
+
+readsys_ret:
+       close(fd);
+       return string;
+}
+
+int
+setsysfs(char *dir, char *entry, char *value)
+{
+       char filename[PATH_MAX];
+       char *oldvalue;
+       int fd, ret = 0;
+
+       if (dir == NULL)
+               snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry);
+       else
+               snprintf(filename, PATH_MAX, "%s/%s/%s",
+                        MICSYSFSDIR, dir, entry);
+
+       oldvalue = readsysfs(dir, entry);
+
+       fd = open(filename, O_RDWR);
+       if (fd < 0) {
+               ret = errno;
+               mpsslog("Failed to open sysfs entry '%s': %s\n",
+                       filename, strerror(errno));
+               goto done;
+       }
+
+       if (!oldvalue || strcmp(value, oldvalue)) {
+               if (write(fd, value, strlen(value)) < 0) {
+                       ret = errno;
+                       mpsslog("Failed to write new sysfs entry '%s': %s\n",
+                               filename, strerror(errno));
+               }
+       }
+       close(fd);
+done:
+       if (oldvalue)
+               free(oldvalue);
+       return ret;
+}
diff --git a/samples/timers/.gitignore b/samples/timers/.gitignore
new file mode 100644 (file)
index 0000000..c5c45d7
--- /dev/null
@@ -0,0 +1 @@
+hpet_example
diff --git a/samples/timers/Makefile b/samples/timers/Makefile
new file mode 100644 (file)
index 0000000..a5c3c4a
--- /dev/null
@@ -0,0 +1,15 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+CC := $(CROSS_COMPILE)gcc
+PROGS := hpet_example
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
+
+endif
+endif
diff --git a/samples/timers/hpet_example.c b/samples/timers/hpet_example.c
new file mode 100644 (file)
index 0000000..3ab4993
--- /dev/null
@@ -0,0 +1,294 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <memory.h>
+#include <malloc.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <linux/hpet.h>
+
+
+extern void hpet_open_close(int, const char **);
+extern void hpet_info(int, const char **);
+extern void hpet_poll(int, const char **);
+extern void hpet_fasync(int, const char **);
+extern void hpet_read(int, const char **);
+
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+
+struct hpet_command {
+       char            *command;
+       void            (*func)(int argc, const char ** argv);
+} hpet_command[] = {
+       {
+               "open-close",
+               hpet_open_close
+       },
+       {
+               "info",
+               hpet_info
+       },
+       {
+               "poll",
+               hpet_poll
+       },
+       {
+               "fasync",
+               hpet_fasync
+       },
+};
+
+int
+main(int argc, const char ** argv)
+{
+       unsigned int    i;
+
+       argc--;
+       argv++;
+
+       if (!argc) {
+               fprintf(stderr, "-hpet: requires command\n");
+               return -1;
+       }
+
+
+       for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++)
+               if (!strcmp(argv[0], hpet_command[i].command)) {
+                       argc--;
+                       argv++;
+                       fprintf(stderr, "-hpet: executing %s\n",
+                               hpet_command[i].command);
+                       hpet_command[i].func(argc, argv);
+                       return 0;
+               }
+
+       fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]);
+
+       return -1;
+}
+
+void
+hpet_open_close(int argc, const char **argv)
+{
+       int     fd;
+
+       if (argc != 1) {
+               fprintf(stderr, "hpet_open_close: device-name\n");
+               return;
+       }
+
+       fd = open(argv[0], O_RDONLY);
+       if (fd < 0)
+               fprintf(stderr, "hpet_open_close: open failed\n");
+       else
+               close(fd);
+
+       return;
+}
+
+void
+hpet_info(int argc, const char **argv)
+{
+       struct hpet_info        info;
+       int                     fd;
+
+       if (argc != 1) {
+               fprintf(stderr, "hpet_info: device-name\n");
+               return;
+       }
+
+       fd = open(argv[0], O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "hpet_info: open of %s failed\n", argv[0]);
+               return;
+       }
+
+       if (ioctl(fd, HPET_INFO, &info) < 0) {
+               fprintf(stderr, "hpet_info: failed to get info\n");
+               goto out;
+       }
+
+       fprintf(stderr, "hpet_info: hi_irqfreq 0x%lx hi_flags 0x%lx ",
+               info.hi_ireqfreq, info.hi_flags);
+       fprintf(stderr, "hi_hpet %d hi_timer %d\n",
+               info.hi_hpet, info.hi_timer);
+
+out:
+       close(fd);
+       return;
+}
+
+void
+hpet_poll(int argc, const char **argv)
+{
+       unsigned long           freq;
+       int                     iterations, i, fd;
+       struct pollfd           pfd;
+       struct hpet_info        info;
+       struct timeval          stv, etv;
+       struct timezone         tz;
+       long                    usec;
+
+       if (argc != 3) {
+               fprintf(stderr, "hpet_poll: device-name freq iterations\n");
+               return;
+       }
+
+       freq = atoi(argv[1]);
+       iterations = atoi(argv[2]);
+
+       fd = open(argv[0], O_RDONLY);
+
+       if (fd < 0) {
+               fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]);
+               return;
+       }
+
+       if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
+               fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n");
+               goto out;
+       }
+
+       if (ioctl(fd, HPET_INFO, &info) < 0) {
+               fprintf(stderr, "hpet_poll: failed to get info\n");
+               goto out;
+       }
+
+       fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags);
+
+       if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
+               fprintf(stderr, "hpet_poll: HPET_EPI failed\n");
+               goto out;
+       }
+
+       if (ioctl(fd, HPET_IE_ON, 0) < 0) {
+               fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n");
+               goto out;
+       }
+
+       pfd.fd = fd;
+       pfd.events = POLLIN;
+
+       for (i = 0; i < iterations; i++) {
+               pfd.revents = 0;
+               gettimeofday(&stv, &tz);
+               if (poll(&pfd, 1, -1) < 0)
+                       fprintf(stderr, "hpet_poll: poll failed\n");
+               else {
+                       long    data;
+
+                       gettimeofday(&etv, &tz);
+                       usec = stv.tv_sec * 1000000 + stv.tv_usec;
+                       usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec;
+
+                       fprintf(stderr,
+                               "hpet_poll: expired time = 0x%lx\n", usec);
+
+                       fprintf(stderr, "hpet_poll: revents = 0x%x\n",
+                               pfd.revents);
+
+                       if (read(fd, &data, sizeof(data)) != sizeof(data)) {
+                               fprintf(stderr, "hpet_poll: read failed\n");
+                       }
+                       else
+                               fprintf(stderr, "hpet_poll: data 0x%lx\n",
+                                       data);
+               }
+       }
+
+out:
+       close(fd);
+       return;
+}
+
+static int hpet_sigio_count;
+
+static void
+hpet_sigio(int val)
+{
+       fprintf(stderr, "hpet_sigio: called\n");
+       hpet_sigio_count++;
+}
+
+void
+hpet_fasync(int argc, const char **argv)
+{
+       unsigned long           freq;
+       int                     iterations, i, fd, value;
+       sig_t                   oldsig;
+       struct hpet_info        info;
+
+       hpet_sigio_count = 0;
+       fd = -1;
+
+       if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) {
+               fprintf(stderr, "hpet_fasync: failed to set signal handler\n");
+               return;
+       }
+
+       if (argc != 3) {
+               fprintf(stderr, "hpet_fasync: device-name freq iterations\n");
+               goto out;
+       }
+
+       fd = open(argv[0], O_RDONLY);
+
+       if (fd < 0) {
+               fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]);
+               return;
+       }
+
+
+       if ((fcntl(fd, F_SETOWN, getpid()) == 1) ||
+               ((value = fcntl(fd, F_GETFL)) == 1) ||
+               (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) {
+               fprintf(stderr, "hpet_fasync: fcntl failed\n");
+               goto out;
+       }
+
+       freq = atoi(argv[1]);
+       iterations = atoi(argv[2]);
+
+       if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
+               fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n");
+               goto out;
+       }
+
+       if (ioctl(fd, HPET_INFO, &info) < 0) {
+               fprintf(stderr, "hpet_fasync: failed to get info\n");
+               goto out;
+       }
+
+       fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags);
+
+       if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
+               fprintf(stderr, "hpet_fasync: HPET_EPI failed\n");
+               goto out;
+       }
+
+       if (ioctl(fd, HPET_IE_ON, 0) < 0) {
+               fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n");
+               goto out;
+       }
+
+       for (i = 0; i < iterations; i++) {
+               (void) pause();
+               fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count);
+       }
+
+out:
+       signal(SIGIO, oldsig);
+
+       if (fd >= 0)
+               close(fd);
+
+       return;
+}
diff --git a/samples/watchdog/.gitignore b/samples/watchdog/.gitignore
new file mode 100644 (file)
index 0000000..ff0ebb5
--- /dev/null
@@ -0,0 +1 @@
+watchdog-simple
diff --git a/samples/watchdog/Makefile b/samples/watchdog/Makefile
new file mode 100644 (file)
index 0000000..9b53d89
--- /dev/null
@@ -0,0 +1,8 @@
+CC := $(CROSS_COMPILE)gcc
+PROGS := watchdog-simple
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
+
diff --git a/samples/watchdog/watchdog-simple.c b/samples/watchdog/watchdog-simple.c
new file mode 100644 (file)
index 0000000..ba45803
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+int main(void)
+{
+       int fd = open("/dev/watchdog", O_WRONLY);
+       int ret = 0;
+       if (fd == -1) {
+               perror("watchdog");
+               exit(EXIT_FAILURE);
+       }
+       while (1) {
+               ret = write(fd, "\0", 1);
+               if (ret != 1) {
+                       ret = -1;
+                       break;
+               }
+               sleep(10);
+       }
+       close(fd);
+       return ret;
+}
index 11602e5..de46ab0 100644 (file)
@@ -81,6 +81,7 @@ endif
 
 ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
 lib-target := $(obj)/lib.a
+obj-y += $(obj)/lib-ksyms.o
 endif
 
 ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
@@ -358,12 +359,22 @@ $(sort $(subdir-obj-y)): $(subdir-ym) ;
 # Rule to compile a set of .o files into one .o file
 #
 ifdef builtin-target
-quiet_cmd_link_o_target = LD      $@
+
+ifdef CONFIG_THIN_ARCHIVES
+  cmd_make_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+  cmd_make_empty_builtin = rm -f $@; $(AR) rcST$(KBUILD_ARFLAGS)
+  quiet_cmd_link_o_target = AR      $@
+else
+  cmd_make_builtin = $(LD) $(ld_flags) -r -o
+  cmd_make_empty_builtin = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS)
+  quiet_cmd_link_o_target = LD      $@
+endif
+
 # If the list of objects to link is empty, just create an empty built-in.o
 cmd_link_o_target = $(if $(strip $(obj-y)),\
-                     $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
+                     $(cmd_make_builtin) $@ $(filter $(obj-y), $^) \
                      $(cmd_secanalysis),\
-                     rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
+                     $(cmd_make_empty_builtin) $@)
 
 $(builtin-target): $(obj-y) FORCE
        $(call if_changed,link_o_target)
@@ -389,12 +400,36 @@ $(modorder-target): $(subdir-ym) FORCE
 #
 ifdef lib-target
 quiet_cmd_link_l_target = AR      $@
-cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
+
+ifdef CONFIG_THIN_ARCHIVES
+  cmd_link_l_target = rm -f $@; $(AR) rcsT$(KBUILD_ARFLAGS) $@ $(lib-y)
+else
+  cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
+endif
 
 $(lib-target): $(lib-y) FORCE
        $(call if_changed,link_l_target)
 
 targets += $(lib-target)
+
+dummy-object = $(obj)/.lib_exports.o
+ksyms-lds = $(dot-target).lds
+ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
+ref_prefix = EXTERN(_
+else
+ref_prefix = EXTERN(
+endif
+
+quiet_cmd_export_list = EXPORTS $@
+cmd_export_list = $(OBJDUMP) -h $< | \
+       sed -ne '/___ksymtab/{s/.*+/$(ref_prefix)/;s/ .*/)/;p}' >$(ksyms-lds);\
+       rm -f $(dummy-object);\
+       $(AR) rcs$(KBUILD_ARFLAGS) $(dummy-object);\
+       $(LD) $(ld_flags) -r -o $@ -T $(ksyms-lds) $(dummy-object);\
+       rm $(dummy-object) $(ksyms-lds)
+
+$(obj)/lib-ksyms.o: $(lib-target) FORCE
+       $(call if_changed,export_list)
 endif
 
 #
index 1366a94..16923ba 100644 (file)
@@ -115,14 +115,18 @@ $(modules:.ko=.mod.o): %.mod.o: %.mod.c FORCE
 
 targets += $(modules:.ko=.mod.o)
 
-# Step 6), final link of the modules
+ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink)
+
+# Step 6), final link of the modules with optional arch pass after final link
 quiet_cmd_ld_ko_o = LD [M]  $@
-      cmd_ld_ko_o = $(LD) -r $(LDFLAGS)                                 \
-                             $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-                             -o $@ $(filter-out FORCE,$^)
+      cmd_ld_ko_o =                                                     \
+       $(LD) -r $(LDFLAGS)                                             \
+                 $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE)             \
+                 -o $@ $(filter-out FORCE,$^) ;                         \
+       $(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) $@, true)
 
 $(modules): %.ko :%.o %.mod.o FORCE
-       $(call if_changed,ld_ko_o)
+       +$(call if_changed,ld_ko_o)
 
 targets += $(modules)
 
index 746ec1e..fff818b 100644 (file)
@@ -82,8 +82,7 @@
  * to date before even starting the recursive build, so it's too late
  * at this point anyway.
  *
- * The algorithm to grep for "CONFIG_..." is bit unusual, but should
- * be fast ;-) We don't even try to really parse the header files, but
+ * We don't even try to really parse the header files, but
  * merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
  * be picked up as well. It's not a problem with respect to
  * correctness, since that can only give too many dependencies, thus
 #include <ctype.h>
 #include <arpa/inet.h>
 
-#define INT_CONF ntohl(0x434f4e46)
-#define INT_ONFI ntohl(0x4f4e4649)
-#define INT_NFIG ntohl(0x4e464947)
-#define INT_FIG_ ntohl(0x4649475f)
-
 int insert_extra_deps;
 char *target;
 char *depfile;
@@ -241,37 +235,22 @@ static void use_config(const char *m, int slen)
        print_config(m, slen);
 }
 
-static void parse_config_file(const char *map, size_t len)
+static void parse_config_file(const char *p)
 {
-       const int *end = (const int *) (map + len);
-       /* start at +1, so that p can never be < map */
-       const int *m   = (const int *) map + 1;
-       const char *p, *q;
-
-       for (; m < end; m++) {
-               if (*m == INT_CONF) { p = (char *) m  ; goto conf; }
-               if (*m == INT_ONFI) { p = (char *) m-1; goto conf; }
-               if (*m == INT_NFIG) { p = (char *) m-2; goto conf; }
-               if (*m == INT_FIG_) { p = (char *) m-3; goto conf; }
-               continue;
-       conf:
-               if (p > map + len - 7)
-                       continue;
-               if (memcmp(p, "CONFIG_", 7))
-                       continue;
+       const char *q, *r;
+
+       while ((p = strstr(p, "CONFIG_"))) {
                p += 7;
-               for (q = p; q < map + len; q++) {
-                       if (!(isalnum(*q) || *q == '_'))
-                               goto found;
-               }
-               continue;
-
-       found:
-               if (!memcmp(q - 7, "_MODULE", 7))
-                       q -= 7;
-               if (q - p < 0)
-                       continue;
-               use_config(p, q - p);
+               q = p;
+               while (*q && (isalnum(*q) || *q == '_'))
+                       q++;
+               if (memcmp(q - 7, "_MODULE", 7) == 0)
+                       r = q - 7;
+               else
+                       r = q;
+               if (r > p)
+                       use_config(p, r - p);
+               p = q;
        }
 }
 
@@ -291,7 +270,7 @@ static void do_config_file(const char *filename)
 {
        struct stat st;
        int fd;
-       void *map;
+       char *map;
 
        fd = open(filename, O_RDONLY);
        if (fd < 0) {
@@ -308,18 +287,23 @@ static void do_config_file(const char *filename)
                close(fd);
                return;
        }
-       map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if ((long) map == -1) {
-               perror("fixdep: mmap");
+       map = malloc(st.st_size + 1);
+       if (!map) {
+               perror("fixdep: malloc");
                close(fd);
                return;
        }
+       if (read(fd, map, st.st_size) != st.st_size) {
+               perror("fixdep: read");
+               close(fd);
+               return;
+       }
+       map[st.st_size] = '\0';
+       close(fd);
 
-       parse_config_file(map, st.st_size);
-
-       munmap(map, st.st_size);
+       parse_config_file(map);
 
-       close(fd);
+       free(map);
 }
 
 /*
@@ -446,22 +430,8 @@ static void print_deps(void)
        close(fd);
 }
 
-static void traps(void)
-{
-       static char test[] __attribute__((aligned(sizeof(int)))) = "CONF";
-       int *p = (int *)test;
-
-       if (*p != INT_CONF) {
-               fprintf(stderr, "fixdep: sizeof(int) != 4 or wrong endianness? %#x\n",
-                       *p);
-               exit(2);
-       }
-}
-
 int main(int argc, char *argv[])
 {
-       traps();
-
        if (argc == 5 && !strcmp(argv[1], "-e")) {
                insert_extra_deps = 1;
                argv++;
index 206a6b3..a8368d1 100755 (executable)
@@ -54,6 +54,7 @@ my $min_conf_desc_length = 4;
 my $spelling_file = "$D/spelling.txt";
 my $codespell = 0;
 my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $conststructsfile = "$D/const_structs.checkpatch";
 my $color = 1;
 my $allow_c99_comments = 1;
 
@@ -523,7 +524,11 @@ our @mode_permission_funcs = (
        ["module_param_array_named", 5],
        ["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
        ["proc_create(?:_data|)", 2],
-       ["(?:CLASS|DEVICE|SENSOR)_ATTR", 2],
+       ["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
+       ["IIO_DEV_ATTR_[A-Z_]+", 1],
+       ["SENSOR_(?:DEVICE_|)ATTR_2", 2],
+       ["SENSOR_TEMPLATE(?:_2|)", 3],
+       ["__ATTR", 2],
 );
 
 #Create a search pattern for all these functions to speed up a loop below
@@ -541,6 +546,32 @@ our $mode_perms_world_writable = qr{
        0[0-7][0-7][2367]
 }x;
 
+our %mode_permission_string_types = (
+       "S_IRWXU" => 0700,
+       "S_IRUSR" => 0400,
+       "S_IWUSR" => 0200,
+       "S_IXUSR" => 0100,
+       "S_IRWXG" => 0070,
+       "S_IRGRP" => 0040,
+       "S_IWGRP" => 0020,
+       "S_IXGRP" => 0010,
+       "S_IRWXO" => 0007,
+       "S_IROTH" => 0004,
+       "S_IWOTH" => 0002,
+       "S_IXOTH" => 0001,
+       "S_IRWXUGO" => 0777,
+       "S_IRUGO" => 0444,
+       "S_IWUGO" => 0222,
+       "S_IXUGO" => 0111,
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $mode_perms_string_search = "";
+foreach my $entry (keys %mode_permission_string_types) {
+       $mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
+       $mode_perms_string_search .= $entry;
+}
+
 our $allowed_asm_includes = qr{(?x:
        irq|
        memory|
@@ -598,6 +629,29 @@ if ($codespell) {
 
 $misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
 
+my $const_structs = "";
+if (open(my $conststructs, '<', $conststructsfile)) {
+       while (<$conststructs>) {
+               my $line = $_;
+
+               $line =~ s/\s*\n?$//g;
+               $line =~ s/^\s*//g;
+
+               next if ($line =~ m/^\s*#/);
+               next if ($line =~ m/^\s*$/);
+               if ($line =~ /\s/) {
+                       print("$conststructsfile: '$line' invalid - ignored\n");
+                       next;
+               }
+
+               $const_structs .= '|' if ($const_structs ne "");
+               $const_structs .= $line;
+       }
+       close($conststructsfile);
+} else {
+       warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+}
+
 sub build_types {
        my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
        my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
@@ -704,6 +758,16 @@ sub seed_camelcase_file {
        }
 }
 
+sub is_maintained_obsolete {
+       my ($filename) = @_;
+
+       return 0 if (!(-e "$root/scripts/get_maintainer.pl"));
+
+       my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+
+       return $status =~ /obsolete/i;
+}
+
 my $camelcase_seeded = 0;
 sub seed_camelcase_includes {
        return if ($camelcase_seeded);
@@ -2289,6 +2353,10 @@ sub process {
                }
 
                if ($found_file) {
+                       if (is_maintained_obsolete($realfile)) {
+                               WARN("OBSOLETE",
+                                    "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy.  No unnecessary modifications please.\n");
+                       }
                        if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
                                $check = 1;
                        } else {
@@ -2939,6 +3007,30 @@ sub process {
                             "Block comments use a trailing */ on a separate line\n" . $herecurr);
                }
 
+# Block comment * alignment
+               if ($prevline =~ /$;[ \t]*$/ &&                 #ends in comment
+                   $line =~ /^\+[ \t]*$;/ &&                   #leading comment
+                   $rawline =~ /^\+[ \t]*\*/ &&                #leading *
+                   (($prevrawline =~ /^\+.*?\/\*/ &&           #leading /*
+                     $prevrawline !~ /\*\/[ \t]*$/) ||         #no trailing */
+                    $prevrawline =~ /^\+[ \t]*\*/)) {          #leading *
+                       my $oldindent;
+                       $prevrawline =~ m@^\+([ \t]*/?)\*@;
+                       if (defined($1)) {
+                               $oldindent = expand_tabs($1);
+                       } else {
+                               $prevrawline =~ m@^\+(.*/?)\*@;
+                               $oldindent = expand_tabs($1);
+                       }
+                       $rawline =~ m@^\+([ \t]*)\*@;
+                       my $newindent = $1;
+                       $newindent = expand_tabs($newindent);
+                       if (length($oldindent) ne length($newindent)) {
+                               WARN("BLOCK_COMMENT_STYLE",
+                                    "Block comments should align the * on each line\n" . $hereprev);
+                       }
+               }
+
 # check for missing blank lines after struct/union declarations
 # with exceptions for various attributes and macros
                if ($prevline =~ /^[\+ ]};?\s*$/ &&
@@ -4665,7 +4757,17 @@ sub process {
                        $has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/);
                        $has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/);
 
-                       $dstat =~ s/^.\s*\#\s*define\s+$Ident(?:\([^\)]*\))?\s*//;
+                       $dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//;
+                       my $define_args = $1;
+                       my $define_stmt = $dstat;
+                       my @def_args = ();
+
+                       if (defined $define_args && $define_args ne "") {
+                               $define_args = substr($define_args, 1, length($define_args) - 2);
+                               $define_args =~ s/\s*//g;
+                               @def_args = split(",", $define_args);
+                       }
+
                        $dstat =~ s/$;//g;
                        $dstat =~ s/\\\n.//g;
                        $dstat =~ s/^\s*//s;
@@ -4701,6 +4803,15 @@ sub process {
                                ^\[
                        }x;
                        #print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+
+                       $ctx =~ s/\n*$//;
+                       my $herectx = $here . "\n";
+                       my $stmt_cnt = statement_rawlines($ctx);
+
+                       for (my $n = 0; $n < $stmt_cnt; $n++) {
+                               $herectx .= raw_line($linenr, $n) . "\n";
+                       }
+
                        if ($dstat ne '' &&
                            $dstat !~ /^(?:$Ident|-?$Constant),$/ &&                    # 10, // foo(),
                            $dstat !~ /^(?:$Ident|-?$Constant);$/ &&                    # foo();
@@ -4716,13 +4827,6 @@ sub process {
                            $dstat !~ /^\(\{/ &&                                                # ({...
                            $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
                        {
-                               $ctx =~ s/\n*$//;
-                               my $herectx = $here . "\n";
-                               my $cnt = statement_rawlines($ctx);
-
-                               for (my $n = 0; $n < $cnt; $n++) {
-                                       $herectx .= raw_line($linenr, $n) . "\n";
-                               }
 
                                if ($dstat =~ /;/) {
                                        ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
@@ -4731,6 +4835,46 @@ sub process {
                                        ERROR("COMPLEX_MACRO",
                                              "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
                                }
+
+                       }
+
+                       # Make $define_stmt single line, comment-free, etc
+                       my @stmt_array = split('\n', $define_stmt);
+                       my $first = 1;
+                       $define_stmt = "";
+                       foreach my $l (@stmt_array) {
+                               $l =~ s/\\$//;
+                               if ($first) {
+                                       $define_stmt = $l;
+                                       $first = 0;
+                               } elsif ($l =~ /^[\+ ]/) {
+                                       $define_stmt .= substr($l, 1);
+                               }
+                       }
+                       $define_stmt =~ s/$;//g;
+                       $define_stmt =~ s/\s+/ /g;
+                       $define_stmt = trim($define_stmt);
+
+# check if any macro arguments are reused (ignore '...' and 'type')
+                       foreach my $arg (@def_args) {
+                               next if ($arg =~ /\.\.\./);
+                               next if ($arg =~ /^type$/i);
+                               my $tmp = $define_stmt;
+                               $tmp =~ s/\b(typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+                               $tmp =~ s/\#+\s*$arg\b//g;
+                               $tmp =~ s/\b$arg\s*\#\#//g;
+                               my $use_cnt = $tmp =~ s/\b$arg\b//g;
+                               if ($use_cnt > 1) {
+                                       CHK("MACRO_ARG_REUSE",
+                                           "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx");
+                                   }
+# check if any macro arguments may have other precedence issues
+                               if ($define_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m &&
+                                   ((defined($1) && $1 ne ',') ||
+                                    (defined($2) && $2 ne ','))) {
+                                       CHK("MACRO_ARG_PRECEDENCE",
+                                           "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
+                               }
                        }
 
 # check for macros with flow control, but without ## concatenation
@@ -5495,46 +5639,46 @@ sub process {
                }
 
 # Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar)
-               if ($^V && $^V ge 5.10.0 &&
-                   defined $stat &&
-                   $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
-                       if (WARN("PREFER_ETHER_ADDR_COPY",
-                                "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
-                           $fix) {
-                               $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
-                       }
-               }
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#                      if (WARN("PREFER_ETHER_ADDR_COPY",
+#                               "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
+#                          $fix) {
+#                              $fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
+#                      }
+#              }
 
 # Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar)
-               if ($^V && $^V ge 5.10.0 &&
-                   defined $stat &&
-                   $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
-                       WARN("PREFER_ETHER_ADDR_EQUAL",
-                            "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
-               }
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#                      WARN("PREFER_ETHER_ADDR_EQUAL",
+#                           "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
+#              }
 
 # check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr
 # check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr
-               if ($^V && $^V ge 5.10.0 &&
-                   defined $stat &&
-                   $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
-
-                       my $ms_val = $7;
-
-                       if ($ms_val =~ /^(?:0x|)0+$/i) {
-                               if (WARN("PREFER_ETH_ZERO_ADDR",
-                                        "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
-                                   $fix) {
-                                       $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
-                               }
-                       } elsif ($ms_val =~ /^(?:0xff|255)$/i) {
-                               if (WARN("PREFER_ETH_BROADCAST_ADDR",
-                                        "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
-                                   $fix) {
-                                       $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
-                               }
-                       }
-               }
+#              if ($^V && $^V ge 5.10.0 &&
+#                  defined $stat &&
+#                  $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#
+#                      my $ms_val = $7;
+#
+#                      if ($ms_val =~ /^(?:0x|)0+$/i) {
+#                              if (WARN("PREFER_ETH_ZERO_ADDR",
+#                                       "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
+#                                  $fix) {
+#                                      $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
+#                              }
+#                      } elsif ($ms_val =~ /^(?:0xff|255)$/i) {
+#                              if (WARN("PREFER_ETH_BROADCAST_ADDR",
+#                                       "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
+#                                  $fix) {
+#                                      $fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
+#                              }
+#                      }
+#              }
 
 # typecasts on min/max could be min_t/max_t
                if ($^V && $^V ge 5.10.0 &&
@@ -5654,6 +5798,19 @@ sub process {
                             "externs should be avoided in .c files\n" .  $herecurr);
                }
 
+               if ($realfile =~ /\.[ch]$/ && defined $stat &&
+                   $stat =~ /^.\s*(?:extern\s+)?$Type\s*$Ident\s*\(\s*([^{]+)\s*\)\s*;/s &&
+                   $1 ne "void") {
+                       my $args = trim($1);
+                       while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
+                               my $arg = trim($1);
+                               if ($arg =~ /^$Type$/ && $arg !~ /enum\s+$Ident$/) {
+                                       WARN("FUNCTION_ARGUMENTS",
+                                            "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
+                               }
+                       }
+               }
+
 # checks for new __setup's
                if ($rawline =~ /\b__setup\("([^"]*)"/) {
                        my $name = $1;
@@ -5853,46 +6010,6 @@ sub process {
                }
 
 # check for various structs that are normally const (ops, kgdb, device_tree)
-               my $const_structs = qr{
-                               acpi_dock_ops|
-                               address_space_operations|
-                               backlight_ops|
-                               block_device_operations|
-                               dentry_operations|
-                               dev_pm_ops|
-                               dma_map_ops|
-                               extent_io_ops|
-                               file_lock_operations|
-                               file_operations|
-                               hv_ops|
-                               ide_dma_ops|
-                               intel_dvo_dev_ops|
-                               item_operations|
-                               iwl_ops|
-                               kgdb_arch|
-                               kgdb_io|
-                               kset_uevent_ops|
-                               lock_manager_operations|
-                               microcode_ops|
-                               mtrr_ops|
-                               neigh_ops|
-                               nlmsvc_binding|
-                               of_device_id|
-                               pci_raw_ops|
-                               pipe_buf_operations|
-                               platform_hibernation_ops|
-                               platform_suspend_ops|
-                               proto_ops|
-                               rpc_pipe_ops|
-                               seq_operations|
-                               snd_ac97_build_ops|
-                               soc_pcmcia_socket_ops|
-                               stacktrace_ops|
-                               sysfs_ops|
-                               tty_operations|
-                               uart_ops|
-                               usb_mon_operations|
-                               wd_ops}x;
                if ($line !~ /\bconst\b/ &&
                    $line =~ /\bstruct\s+($const_structs)\b/) {
                        WARN("CONST_STRUCT",
@@ -5979,34 +6096,69 @@ sub process {
 # Mode permission misuses where it seems decimal should be octal
 # This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
                if ($^V && $^V ge 5.10.0 &&
+                   defined $stat &&
                    $line =~ /$mode_perms_search/) {
                        foreach my $entry (@mode_permission_funcs) {
                                my $func = $entry->[0];
                                my $arg_pos = $entry->[1];
 
+                               my $lc = $stat =~ tr@\n@@;
+                               $lc = $lc + $linenr;
+                               my $stat_real = raw_line($linenr, 0);
+                               for (my $count = $linenr + 1; $count <= $lc; $count++) {
+                                       $stat_real = $stat_real . "\n" . raw_line($count, 0);
+                               }
+
                                my $skip_args = "";
                                if ($arg_pos > 1) {
                                        $arg_pos--;
                                        $skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
                                }
-                               my $test = "\\b$func\\s*\\(${skip_args}([\\d]+)\\s*[,\\)]";
-                               if ($line =~ /$test/) {
+                               my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
+                               if ($stat =~ /$test/) {
                                        my $val = $1;
                                        $val = $6 if ($skip_args ne "");
-
-                                       if ($val !~ /^0$/ &&
-                                           (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
-                                            length($val) ne 4)) {
+                                       if (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
+                                           ($val =~ /^$Octal$/ && length($val) ne 4)) {
                                                ERROR("NON_OCTAL_PERMISSIONS",
-                                                     "Use 4 digit octal (0777) not decimal permissions\n" . $herecurr);
-                                       } elsif ($val =~ /^$Octal$/ && (oct($val) & 02)) {
+                                                     "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
+                                       }
+                                       if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
                                                ERROR("EXPORTED_WORLD_WRITABLE",
-                                                     "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+                                                     "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
                                        }
                                }
                        }
                }
 
+# check for uses of S_<PERMS> that could be octal for readability
+               if ($line =~ /\b$mode_perms_string_search\b/) {
+                       my $val = "";
+                       my $oval = "";
+                       my $to = 0;
+                       my $curpos = 0;
+                       my $lastpos = 0;
+                       while ($line =~ /\b(($mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
+                               $curpos = pos($line);
+                               my $match = $2;
+                               my $omatch = $1;
+                               last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
+                               $lastpos = $curpos;
+                               $to |= $mode_permission_string_types{$match};
+                               $val .= '\s*\|\s*' if ($val ne "");
+                               $val .= $match;
+                               $oval .= $omatch;
+                       }
+                       $oval =~ s/^\s*\|\s*//;
+                       $oval =~ s/\s*\|\s*$//;
+                       my $octal = sprintf("%04o", $to);
+                       if (WARN("SYMBOLIC_PERMS",
+                                "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
+                           $fix) {
+                               $fixed[$fixlinenr] =~ s/$val/$octal/;
+                       }
+               }
+
 # validate content of MODULE_LICENSE against list from include/linux/module.h
                if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
                        my $extracted_string = get_quoted_string($line, $rawline);
index c92c152..ec487b8 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/bash
 # Linux kernel coccicheck
 #
-# Read Documentation/coccinelle.txt
+# Read Documentation/dev-tools/coccinelle.rst
 #
 # This script requires at least spatch
 # version 1.0.0-rc11.
index c606231..2a5aea8 100644 (file)
@@ -15,11 +15,11 @@ virtual org
 virtual report
 
 @depends on patch@
-expression from,to,size,flag;
+expression from,to,size;
 identifier l1,l2;
 @@
 
--  to = \(kmalloc\|kzalloc\)(size,flag);
+-  to = \(kmalloc\|kzalloc\)(size,GFP_KERNEL);
 +  to = memdup_user(from,size);
    if (
 -      to==NULL
@@ -37,12 +37,12 @@ identifier l1,l2;
 -  }
 
 @r depends on !patch@
-expression from,to,size,flag;
+expression from,to,size;
 position p;
 statement S1,S2;
 @@
 
-*  to = \(kmalloc@p\|kzalloc@p\)(size,flag);
+*  to = \(kmalloc@p\|kzalloc@p\)(size,GFP_KERNEL);
    if (to==NULL || ...) S1
    if (copy_from_user(to, from, size) != 0)
    S2
index 89b98a2..d67ccf5 100644 (file)
@@ -17,9 +17,10 @@ virtual report
 
 @runtime_bad_err_handle exists@
 expression ret;
+position p;
 @@
 (
-ret = \(pm_runtime_idle\|
+ret@p = \(pm_runtime_idle\|
        pm_runtime_suspend\|
        pm_runtime_autosuspend\|
        pm_runtime_resume\|
@@ -47,12 +48,13 @@ IS_ERR_VALUE(ret)
 //  For context mode
 //----------------------------------------------------------
 
-@depends on runtime_bad_err_handle && context@
+@depends on context@
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api(...);
+ret@p = pm_runtime_api(...);
 ...
 * IS_ERR_VALUE(ret)
 ...
@@ -62,12 +64,13 @@ ret = pm_runtime_api(...);
 //  For patch mode
 //----------------------------------------------------------
 
-@depends on runtime_bad_err_handle && patch@
+@depends on patch@
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api(...);
+ret@p = pm_runtime_api(...);
 ...
 - IS_ERR_VALUE(ret)
 + ret < 0
@@ -78,13 +81,14 @@ ret = pm_runtime_api(...);
 //  For org and report mode
 //----------------------------------------------------------
 
-@r depends on runtime_bad_err_handle && (org || report) exists@
+@r depends on (org || report) exists@
 position p1, p2;
 identifier pm_runtime_api;
 expression ret;
+position runtime_bad_err_handle.p;
 @@
 (
-ret = pm_runtime_api@p1(...);
+ret@p = pm_runtime_api@p1(...);
 ...
 IS_ERR_VALUE@p2(ret)
 ...
diff --git a/scripts/coccinelle/misc/cond_no_effect.cocci b/scripts/coccinelle/misc/cond_no_effect.cocci
new file mode 100644 (file)
index 0000000..8467dbd
--- /dev/null
@@ -0,0 +1,64 @@
+///Find conditions where if and else branch are functionally
+// identical.
+//
+// There can be false positives in cases where the positional
+// information is used (as with lockdep) or where the identity
+// is a placeholder for not yet handled cases.
+// Unfortunately there also seems to be a tendency to use
+// the last if else/else as a "default behavior" - which some
+// might consider a legitimate coding pattern. From discussion
+// on kernelnewbies though it seems that this is not really an
+// accepted pattern and if at all it would need to be commented
+//
+// In the Linux kernel it does not seem to actually report
+// false positives except for those that were documented as
+// being intentional.
+// the two known cases are:
+//   arch/sh/kernel/traps_64.c:read_opcode()
+//        } else if ((pc & 1) == 0) {
+//              /* SHcompact */
+//              /* TODO : provide handling for this.  We don't really support
+//                 user-mode SHcompact yet, and for a kernel fault, this would
+//                 have to come from a module built for SHcompact.  */
+//              return -EFAULT;
+//      } else {
+//              /* misaligned */
+//              return -EFAULT;
+//      }
+//   fs/kernfs/file.c:kernfs_fop_open()
+//       * Both paths of the branch look the same.  They're supposed to
+//       * look that way and give @of->mutex different static lockdep keys.
+//       */
+//      if (has_mmap)
+//              mutex_init(&of->mutex);
+//      else
+//              mutex_init(&of->mutex);
+//
+// All other cases look like bugs or at least lack of documentation
+//
+// Confidence: Moderate
+// Copyright: (C) 2016 Nicholas Mc Guire, OSADL.  GPLv2.
+// Comments:
+// Options: --no-includes --include-headers
+
+virtual org
+virtual report
+
+@cond@
+statement S1;
+position p;
+@@
+
+* if@p (...) S1 else S1
+
+@script:python depends on org@
+p << cond.p;
+@@
+
+cocci.print_main("WARNING: possible condition with no effect (if == else)",p)
+
+@script:python depends on report@
+p << cond.p;
+@@
+
+coccilib.report.print_report(p[0],"WARNING: possible condition with no effect (if == else)")
diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch
new file mode 100644 (file)
index 0000000..ac5f126
--- /dev/null
@@ -0,0 +1,64 @@
+acpi_dock_ops
+address_space_operations
+backlight_ops
+block_device_operations
+clk_ops
+comedi_lrange
+component_ops
+dentry_operations
+dev_pm_ops
+dma_map_ops
+driver_info
+drm_connector_funcs
+drm_encoder_funcs
+drm_encoder_helper_funcs
+ethtool_ops
+extent_io_ops
+file_lock_operations
+file_operations
+hv_ops
+ide_dma_ops
+ide_port_ops
+inode_operations
+intel_dvo_dev_ops
+irq_domain_ops
+item_operations
+iwl_cfg
+iwl_ops
+kgdb_arch
+kgdb_io
+kset_uevent_ops
+lock_manager_operations
+machine_desc
+microcode_ops
+mlxsw_reg_info
+mtrr_ops
+neigh_ops
+net_device_ops
+nlmsvc_binding
+nvkm_device_chip
+of_device_id
+pci_raw_ops
+pipe_buf_operations
+platform_hibernation_ops
+platform_suspend_ops
+proto_ops
+regmap_access_table
+rpc_pipe_ops
+rtc_class_ops
+sd_desc
+seq_operations
+sirfsoc_padmux
+snd_ac97_build_ops
+snd_soc_component_driver
+soc_pcmcia_socket_ops
+stacktrace_ops
+sysfs_ops
+tty_operations
+uart_ops
+usb_mon_operations
+v4l2_ctrl_ops
+v4l2_ioctl_ops
+vm_operations_struct
+wacom_features
+wd_ops
index 17fa901..0055b07 100755 (executable)
@@ -97,7 +97,10 @@ print_mtime() {
 }
 
 list_parse() {
-       [ ! -L "$1" ] && echo "$1 \\" || :
+       if [ -L "$1" ]; then
+               return
+       fi
+       echo "$1" | sed 's/:/\\:/g; s/$/ \\/'
 }
 
 # for each file print a line in following format
index e583565..5235aa5 100644 (file)
@@ -289,6 +289,23 @@ repeat:
        }
       break;
 
+    case ST_TYPEOF_1:
+      if (token == IDENT)
+       {
+         if (is_reserved_word(yytext, yyleng)
+             || find_symbol(yytext, SYM_TYPEDEF, 1))
+           {
+             yyless(0);
+             unput('(');
+             lexstate = ST_NORMAL;
+             token = TYPEOF_KEYW;
+             break;
+           }
+         _APP("(", 1);
+       }
+       lexstate = ST_TYPEOF;
+       /* FALLTHRU */
+
     case ST_TYPEOF:
       switch (token)
        {
@@ -313,24 +330,6 @@ repeat:
        }
       break;
 
-    case ST_TYPEOF_1:
-      if (token == IDENT)
-       {
-         if (is_reserved_word(yytext, yyleng)
-             || find_symbol(yytext, SYM_TYPEDEF, 1))
-           {
-             yyless(0);
-             unput('(');
-             lexstate = ST_NORMAL;
-             token = TYPEOF_KEYW;
-             break;
-           }
-         _APP("(", 1);
-       }
-       APP;
-       lexstate = ST_TYPEOF;
-       goto repeat;
-
     case ST_BRACKET:
       APP;
       switch (token)
index f82740a..985c554 100644 (file)
@@ -2098,6 +2098,23 @@ repeat:
        }
       break;
 
+    case ST_TYPEOF_1:
+      if (token == IDENT)
+       {
+         if (is_reserved_word(yytext, yyleng)
+             || find_symbol(yytext, SYM_TYPEDEF, 1))
+           {
+             yyless(0);
+             unput('(');
+             lexstate = ST_NORMAL;
+             token = TYPEOF_KEYW;
+             break;
+           }
+         _APP("(", 1);
+       }
+       lexstate = ST_TYPEOF;
+       /* FALLTHRU */
+
     case ST_TYPEOF:
       switch (token)
        {
@@ -2122,24 +2139,6 @@ repeat:
        }
       break;
 
-    case ST_TYPEOF_1:
-      if (token == IDENT)
-       {
-         if (is_reserved_word(yytext, yyleng)
-             || find_symbol(yytext, SYM_TYPEDEF, 1))
-           {
-             yyless(0);
-             unput('(');
-             lexstate = ST_NORMAL;
-             token = TYPEOF_KEYW;
-             break;
-           }
-         _APP("(", 1);
-       }
-       APP;
-       lexstate = ST_TYPEOF;
-       goto repeat;
-
     case ST_BRACKET:
       APP;
       switch (token)
index 4f727eb..f742c65 100755 (executable)
@@ -37,12 +37,40 @@ info()
        fi
 }
 
+# Thin archive build here makes a final archive with
+# symbol table and indexes from vmlinux objects, which can be
+# used as input to linker.
+#
+# Traditional incremental style of link does not require this step
+#
+# built-in.o output file
+#
+archive_builtin()
+{
+       if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+               info AR built-in.o
+               rm -f built-in.o;
+               ${AR} rcsT${KBUILD_ARFLAGS} built-in.o                  \
+                                       ${KBUILD_VMLINUX_INIT}          \
+                                       ${KBUILD_VMLINUX_MAIN}
+       fi
+}
+
 # Link of vmlinux.o used for section mismatch analysis
 # ${1} output file
 modpost_link()
 {
-       ${LD} ${LDFLAGS} -r -o ${1} ${KBUILD_VMLINUX_INIT}                   \
-               --start-group ${KBUILD_VMLINUX_MAIN} --end-group
+       local objects
+
+       if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+               objects="--whole-archive built-in.o"
+       else
+               objects="${KBUILD_VMLINUX_INIT}                         \
+                       --start-group                                   \
+                       ${KBUILD_VMLINUX_MAIN}                          \
+                       --end-group"
+       fi
+       ${LD} ${LDFLAGS} -r -o ${1} ${objects}
 }
 
 # Link of vmlinux
@@ -51,18 +79,36 @@ modpost_link()
 vmlinux_link()
 {
        local lds="${objtree}/${KBUILD_LDS}"
+       local objects
 
        if [ "${SRCARCH}" != "um" ]; then
-               ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}                  \
-                       -T ${lds} ${KBUILD_VMLINUX_INIT}                     \
-                       --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
+               if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+                       objects="--whole-archive built-in.o ${1}"
+               else
+                       objects="${KBUILD_VMLINUX_INIT}                 \
+                               --start-group                           \
+                               ${KBUILD_VMLINUX_MAIN}                  \
+                               --end-group                             \
+                               ${1}"
+               fi
+
+               ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}             \
+                       -T ${lds} ${objects}
        else
-               ${CC} ${CFLAGS_vmlinux} -o ${2}                              \
-                       -Wl,-T,${lds} ${KBUILD_VMLINUX_INIT}                 \
-                       -Wl,--start-group                                    \
-                                ${KBUILD_VMLINUX_MAIN}                      \
-                       -Wl,--end-group                                      \
-                       -lutil -lrt -lpthread ${1}
+               if [ -n "${CONFIG_THIN_ARCHIVES}" ]; then
+                       objects="-Wl,--whole-archive built-in.o ${1}"
+               else
+                       objects="${KBUILD_VMLINUX_INIT}                 \
+                               -Wl,--start-group                       \
+                               ${KBUILD_VMLINUX_MAIN}                  \
+                               -Wl,--end-group                         \
+                               ${1}"
+               fi
+
+               ${CC} ${CFLAGS_vmlinux} -o ${2}                         \
+                       -Wl,-T,${lds}                                   \
+                       ${objects}                                      \
+                       -lutil -lrt -lpthread
                rm -f linux
        fi
 }
@@ -119,6 +165,7 @@ cleanup()
        rm -f .tmp_kallsyms*
        rm -f .tmp_version
        rm -f .tmp_vmlinux*
+       rm -f built-in.o
        rm -f System.map
        rm -f vmlinux
        rm -f vmlinux.o
@@ -162,6 +209,8 @@ case "${KCONFIG_CONFIG}" in
        . "./${KCONFIG_CONFIG}"
 esac
 
+archive_builtin
+
 #link vmlinux.o
 info LD vmlinux.o
 modpost_link vmlinux.o
index b3775a9..a2ff338 100755 (executable)
@@ -263,7 +263,8 @@ exuberant()
        -I EXPORT_SYMBOL,EXPORT_SYMBOL_GPL,ACPI_EXPORT_SYMBOL   \
        -I DEFINE_TRACE,EXPORT_TRACEPOINT_SYMBOL,EXPORT_TRACEPOINT_SYMBOL_GPL \
        -I static,const                                         \
-       --extra=+f --c-kinds=+px --langmap=c:+.h "${regex[@]}"
+       --extra=+fq --c-kinds=+px --fields=+iaS --langmap=c:+.h \
+       "${regex[@]}"
 
        setup_regex exuberant kconfig
        all_kconfigs | xargs $1 -a                              \
index c8455b4..7ab14ce 100644 (file)
@@ -338,7 +338,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context)
        spin_unlock_irqrestore(&sst->spinlock, flags);
 
        /* continue to send any remaining messages... */
-       queue_kthread_work(&ipc->kworker, &ipc->kwork);
+       kthread_queue_work(&ipc->kworker, &ipc->kwork);
 
        return IRQ_HANDLED;
 }
index 5d29493..0127422 100644 (file)
@@ -12,7 +12,6 @@
  *
  */
 
-#include <linux/kconfig.h>
 #include <linux/stddef.h>
 #include <linux/acpi.h>
 
index a12c7bb..6c672ac 100644 (file)
@@ -111,7 +111,7 @@ static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header,
        list_add_tail(&msg->list, &ipc->tx_list);
        spin_unlock_irqrestore(&ipc->dsp->spinlock, flags);
 
-       queue_kthread_work(&ipc->kworker, &ipc->kwork);
+       kthread_queue_work(&ipc->kworker, &ipc->kwork);
 
        if (wait)
                return tx_wait_done(ipc, msg, rx_data);
@@ -281,7 +281,7 @@ int sst_ipc_init(struct sst_generic_ipc *ipc)
                return -ENOMEM;
 
        /* start the IPC message thread */
-       init_kthread_worker(&ipc->kworker);
+       kthread_init_worker(&ipc->kworker);
        ipc->tx_thread = kthread_run(kthread_worker_fn,
                                        &ipc->kworker, "%s",
                                        dev_name(ipc->dev));
@@ -292,7 +292,7 @@ int sst_ipc_init(struct sst_generic_ipc *ipc)
                return ret;
        }
 
-       init_kthread_work(&ipc->kwork, ipc_tx_msgs);
+       kthread_init_work(&ipc->kwork, ipc_tx_msgs);
        return 0;
 }
 EXPORT_SYMBOL_GPL(sst_ipc_init);
index 9156522..e432a31 100644 (file)
@@ -818,7 +818,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context)
        spin_unlock_irqrestore(&sst->spinlock, flags);
 
        /* continue to send any remaining messages... */
-       queue_kthread_work(&ipc->kworker, &ipc->kwork);
+       kthread_queue_work(&ipc->kworker, &ipc->kwork);
 
        return IRQ_HANDLED;
 }
index 0bd01e6..797cf40 100644 (file)
@@ -464,7 +464,7 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
        skl_ipc_int_enable(dsp);
 
        /* continue to send any remaining messages... */
-       queue_kthread_work(&ipc->kworker, &ipc->kwork);
+       kthread_queue_work(&ipc->kworker, &ipc->kwork);
 
        return IRQ_HANDLED;
 }
diff --git a/tools/accounting/.gitignore b/tools/accounting/.gitignore
new file mode 100644 (file)
index 0000000..8648520
--- /dev/null
@@ -0,0 +1 @@
+getdelays
diff --git a/tools/accounting/Makefile b/tools/accounting/Makefile
new file mode 100644 (file)
index 0000000..647c94a
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := getdelays
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c
new file mode 100644 (file)
index 0000000..b5ca536
--- /dev/null
@@ -0,0 +1,550 @@
+/* getdelays.c
+ *
+ * Utility to get per-pid and per-tgid delay accounting statistics
+ * Also illustrates usage of the taskstats interface
+ *
+ * Copyright (C) Shailabh Nagar, IBM Corp. 2005
+ * Copyright (C) Balbir Singh, IBM Corp. 2006
+ * Copyright (c) Jay Lan, SGI. 2006
+ *
+ * Compile with
+ *     gcc -I/usr/src/linux/include getdelays.c -o getdelays
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <poll.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include <linux/genetlink.h>
+#include <linux/taskstats.h>
+#include <linux/cgroupstats.h>
+
+/*
+ * Generic macros for dealing with netlink sockets. Might be duplicated
+ * elsewhere. It is recommended that commercial grade applications use
+ * libnl or libnetlink and use the interfaces provided by the library
+ */
+#define GENLMSG_DATA(glh)      ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
+#define GENLMSG_PAYLOAD(glh)   (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN)
+#define NLA_DATA(na)           ((void *)((char*)(na) + NLA_HDRLEN))
+#define NLA_PAYLOAD(len)       (len - NLA_HDRLEN)
+
+#define err(code, fmt, arg...)                 \
+       do {                                    \
+               fprintf(stderr, fmt, ##arg);    \
+               exit(code);                     \
+       } while (0)
+
+int done;
+int rcvbufsz;
+char name[100];
+int dbg;
+int print_delays;
+int print_io_accounting;
+int print_task_context_switch_counts;
+
+#define PRINTF(fmt, arg...) {                  \
+           if (dbg) {                          \
+               printf(fmt, ##arg);             \
+           }                                   \
+       }
+
+/* Maximum size of response requested or message sent */
+#define MAX_MSG_SIZE   1024
+/* Maximum number of cpus expected to be specified in a cpumask */
+#define MAX_CPUS       32
+
+struct msgtemplate {
+       struct nlmsghdr n;
+       struct genlmsghdr g;
+       char buf[MAX_MSG_SIZE];
+};
+
+char cpumask[100+6*MAX_CPUS];
+
+static void usage(void)
+{
+       fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] "
+                       "[-m cpumask] [-t tgid] [-p pid]\n");
+       fprintf(stderr, "  -d: print delayacct stats\n");
+       fprintf(stderr, "  -i: print IO accounting (works only with -p)\n");
+       fprintf(stderr, "  -l: listen forever\n");
+       fprintf(stderr, "  -v: debug on\n");
+       fprintf(stderr, "  -C: container path\n");
+}
+
+/*
+ * Create a raw netlink socket and bind
+ */
+static int create_nl_socket(int protocol)
+{
+       int fd;
+       struct sockaddr_nl local;
+
+       fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+       if (fd < 0)
+               return -1;
+
+       if (rcvbufsz)
+               if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
+                               &rcvbufsz, sizeof(rcvbufsz)) < 0) {
+                       fprintf(stderr, "Unable to set socket rcv buf size to %d\n",
+                               rcvbufsz);
+                       goto error;
+               }
+
+       memset(&local, 0, sizeof(local));
+       local.nl_family = AF_NETLINK;
+
+       if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
+               goto error;
+
+       return fd;
+error:
+       close(fd);
+       return -1;
+}
+
+
+static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid,
+            __u8 genl_cmd, __u16 nla_type,
+            void *nla_data, int nla_len)
+{
+       struct nlattr *na;
+       struct sockaddr_nl nladdr;
+       int r, buflen;
+       char *buf;
+
+       struct msgtemplate msg;
+
+       msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+       msg.n.nlmsg_type = nlmsg_type;
+       msg.n.nlmsg_flags = NLM_F_REQUEST;
+       msg.n.nlmsg_seq = 0;
+       msg.n.nlmsg_pid = nlmsg_pid;
+       msg.g.cmd = genl_cmd;
+       msg.g.version = 0x1;
+       na = (struct nlattr *) GENLMSG_DATA(&msg);
+       na->nla_type = nla_type;
+       na->nla_len = nla_len + 1 + NLA_HDRLEN;
+       memcpy(NLA_DATA(na), nla_data, nla_len);
+       msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
+
+       buf = (char *) &msg;
+       buflen = msg.n.nlmsg_len ;
+       memset(&nladdr, 0, sizeof(nladdr));
+       nladdr.nl_family = AF_NETLINK;
+       while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr,
+                          sizeof(nladdr))) < buflen) {
+               if (r > 0) {
+                       buf += r;
+                       buflen -= r;
+               } else if (errno != EAGAIN)
+                       return -1;
+       }
+       return 0;
+}
+
+
+/*
+ * Probe the controller in genetlink to find the family id
+ * for the TASKSTATS family
+ */
+static int get_family_id(int sd)
+{
+       struct {
+               struct nlmsghdr n;
+               struct genlmsghdr g;
+               char buf[256];
+       } ans;
+
+       int id = 0, rc;
+       struct nlattr *na;
+       int rep_len;
+
+       strcpy(name, TASKSTATS_GENL_NAME);
+       rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY,
+                       CTRL_ATTR_FAMILY_NAME, (void *)name,
+                       strlen(TASKSTATS_GENL_NAME)+1);
+       if (rc < 0)
+               return 0;       /* sendto() failure? */
+
+       rep_len = recv(sd, &ans, sizeof(ans), 0);
+       if (ans.n.nlmsg_type == NLMSG_ERROR ||
+           (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len))
+               return 0;
+
+       na = (struct nlattr *) GENLMSG_DATA(&ans);
+       na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
+       if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
+               id = *(__u16 *) NLA_DATA(na);
+       }
+       return id;
+}
+
+#define average_ms(t, c) (t / 1000000ULL / (c ? c : 1))
+
+static void print_delayacct(struct taskstats *t)
+{
+       printf("\n\nCPU   %15s%15s%15s%15s%15s\n"
+              "      %15llu%15llu%15llu%15llu%15.3fms\n"
+              "IO    %15s%15s%15s\n"
+              "      %15llu%15llu%15llums\n"
+              "SWAP  %15s%15s%15s\n"
+              "      %15llu%15llu%15llums\n"
+              "RECLAIM  %12s%15s%15s\n"
+              "      %15llu%15llu%15llums\n",
+              "count", "real total", "virtual total",
+              "delay total", "delay average",
+              (unsigned long long)t->cpu_count,
+              (unsigned long long)t->cpu_run_real_total,
+              (unsigned long long)t->cpu_run_virtual_total,
+              (unsigned long long)t->cpu_delay_total,
+              average_ms((double)t->cpu_delay_total, t->cpu_count),
+              "count", "delay total", "delay average",
+              (unsigned long long)t->blkio_count,
+              (unsigned long long)t->blkio_delay_total,
+              average_ms(t->blkio_delay_total, t->blkio_count),
+              "count", "delay total", "delay average",
+              (unsigned long long)t->swapin_count,
+              (unsigned long long)t->swapin_delay_total,
+              average_ms(t->swapin_delay_total, t->swapin_count),
+              "count", "delay total", "delay average",
+              (unsigned long long)t->freepages_count,
+              (unsigned long long)t->freepages_delay_total,
+              average_ms(t->freepages_delay_total, t->freepages_count));
+}
+
+static void task_context_switch_counts(struct taskstats *t)
+{
+       printf("\n\nTask   %15s%15s\n"
+              "       %15llu%15llu\n",
+              "voluntary", "nonvoluntary",
+              (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw);
+}
+
+static void print_cgroupstats(struct cgroupstats *c)
+{
+       printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, "
+               "uninterruptible %llu\n", (unsigned long long)c->nr_sleeping,
+               (unsigned long long)c->nr_io_wait,
+               (unsigned long long)c->nr_running,
+               (unsigned long long)c->nr_stopped,
+               (unsigned long long)c->nr_uninterruptible);
+}
+
+
+static void print_ioacct(struct taskstats *t)
+{
+       printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n",
+               t->ac_comm,
+               (unsigned long long)t->read_bytes,
+               (unsigned long long)t->write_bytes,
+               (unsigned long long)t->cancelled_write_bytes);
+}
+
+int main(int argc, char *argv[])
+{
+       int c, rc, rep_len, aggr_len, len2;
+       int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC;
+       __u16 id;
+       __u32 mypid;
+
+       struct nlattr *na;
+       int nl_sd = -1;
+       int len = 0;
+       pid_t tid = 0;
+       pid_t rtid = 0;
+
+       int fd = 0;
+       int count = 0;
+       int write_file = 0;
+       int maskset = 0;
+       char *logfile = NULL;
+       int loop = 0;
+       int containerset = 0;
+       char *containerpath = NULL;
+       int cfd = 0;
+       int forking = 0;
+       sigset_t sigset;
+
+       struct msgtemplate msg;
+
+       while (!forking) {
+               c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:");
+               if (c < 0)
+                       break;
+
+               switch (c) {
+               case 'd':
+                       printf("print delayacct stats ON\n");
+                       print_delays = 1;
+                       break;
+               case 'i':
+                       printf("printing IO accounting\n");
+                       print_io_accounting = 1;
+                       break;
+               case 'q':
+                       printf("printing task/process context switch rates\n");
+                       print_task_context_switch_counts = 1;
+                       break;
+               case 'C':
+                       containerset = 1;
+                       containerpath = optarg;
+                       break;
+               case 'w':
+                       logfile = strdup(optarg);
+                       printf("write to file %s\n", logfile);
+                       write_file = 1;
+                       break;
+               case 'r':
+                       rcvbufsz = atoi(optarg);
+                       printf("receive buf size %d\n", rcvbufsz);
+                       if (rcvbufsz < 0)
+                               err(1, "Invalid rcv buf size\n");
+                       break;
+               case 'm':
+                       strncpy(cpumask, optarg, sizeof(cpumask));
+                       cpumask[sizeof(cpumask) - 1] = '\0';
+                       maskset = 1;
+                       printf("cpumask %s maskset %d\n", cpumask, maskset);
+                       break;
+               case 't':
+                       tid = atoi(optarg);
+                       if (!tid)
+                               err(1, "Invalid tgid\n");
+                       cmd_type = TASKSTATS_CMD_ATTR_TGID;
+                       break;
+               case 'p':
+                       tid = atoi(optarg);
+                       if (!tid)
+                               err(1, "Invalid pid\n");
+                       cmd_type = TASKSTATS_CMD_ATTR_PID;
+                       break;
+               case 'c':
+
+                       /* Block SIGCHLD for sigwait() later */
+                       if (sigemptyset(&sigset) == -1)
+                               err(1, "Failed to empty sigset");
+                       if (sigaddset(&sigset, SIGCHLD))
+                               err(1, "Failed to set sigchld in sigset");
+                       sigprocmask(SIG_BLOCK, &sigset, NULL);
+
+                       /* fork/exec a child */
+                       tid = fork();
+                       if (tid < 0)
+                               err(1, "Fork failed\n");
+                       if (tid == 0)
+                               if (execvp(argv[optind - 1],
+                                   &argv[optind - 1]) < 0)
+                                       exit(-1);
+
+                       /* Set the command type and avoid further processing */
+                       cmd_type = TASKSTATS_CMD_ATTR_PID;
+                       forking = 1;
+                       break;
+               case 'v':
+                       printf("debug on\n");
+                       dbg = 1;
+                       break;
+               case 'l':
+                       printf("listen forever\n");
+                       loop = 1;
+                       break;
+               default:
+                       usage();
+                       exit(-1);
+               }
+       }
+
+       if (write_file) {
+               fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC,
+                         S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+               if (fd == -1) {
+                       perror("Cannot open output file\n");
+                       exit(1);
+               }
+       }
+
+       nl_sd = create_nl_socket(NETLINK_GENERIC);
+       if (nl_sd < 0)
+               err(1, "error creating Netlink socket\n");
+
+
+       mypid = getpid();
+       id = get_family_id(nl_sd);
+       if (!id) {
+               fprintf(stderr, "Error getting family id, errno %d\n", errno);
+               goto err;
+       }
+       PRINTF("family id %d\n", id);
+
+       if (maskset) {
+               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
+                             TASKSTATS_CMD_ATTR_REGISTER_CPUMASK,
+                             &cpumask, strlen(cpumask) + 1);
+               PRINTF("Sent register cpumask, retval %d\n", rc);
+               if (rc < 0) {
+                       fprintf(stderr, "error sending register cpumask\n");
+                       goto err;
+               }
+       }
+
+       if (tid && containerset) {
+               fprintf(stderr, "Select either -t or -C, not both\n");
+               goto err;
+       }
+
+       /*
+        * If we forked a child, wait for it to exit. Cannot use waitpid()
+        * as all the delicious data would be reaped as part of the wait
+        */
+       if (tid && forking) {
+               int sig_received;
+               sigwait(&sigset, &sig_received);
+       }
+
+       if (tid) {
+               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
+                             cmd_type, &tid, sizeof(__u32));
+               PRINTF("Sent pid/tgid, retval %d\n", rc);
+               if (rc < 0) {
+                       fprintf(stderr, "error sending tid/tgid cmd\n");
+                       goto done;
+               }
+       }
+
+       if (containerset) {
+               cfd = open(containerpath, O_RDONLY);
+               if (cfd < 0) {
+                       perror("error opening container file");
+                       goto err;
+               }
+               rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET,
+                             CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32));
+               if (rc < 0) {
+                       perror("error sending cgroupstats command");
+                       goto err;
+               }
+       }
+       if (!maskset && !tid && !containerset) {
+               usage();
+               goto err;
+       }
+
+       do {
+               rep_len = recv(nl_sd, &msg, sizeof(msg), 0);
+               PRINTF("received %d bytes\n", rep_len);
+
+               if (rep_len < 0) {
+                       fprintf(stderr, "nonfatal reply error: errno %d\n",
+                               errno);
+                       continue;
+               }
+               if (msg.n.nlmsg_type == NLMSG_ERROR ||
+                   !NLMSG_OK((&msg.n), rep_len)) {
+                       struct nlmsgerr *err = NLMSG_DATA(&msg);
+                       fprintf(stderr, "fatal reply error,  errno %d\n",
+                               err->error);
+                       goto done;
+               }
+
+               PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n",
+                      sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len);
+
+
+               rep_len = GENLMSG_PAYLOAD(&msg.n);
+
+               na = (struct nlattr *) GENLMSG_DATA(&msg);
+               len = 0;
+               while (len < rep_len) {
+                       len += NLA_ALIGN(na->nla_len);
+                       switch (na->nla_type) {
+                       case TASKSTATS_TYPE_AGGR_TGID:
+                               /* Fall through */
+                       case TASKSTATS_TYPE_AGGR_PID:
+                               aggr_len = NLA_PAYLOAD(na->nla_len);
+                               len2 = 0;
+                               /* For nested attributes, na follows */
+                               na = (struct nlattr *) NLA_DATA(na);
+                               done = 0;
+                               while (len2 < aggr_len) {
+                                       switch (na->nla_type) {
+                                       case TASKSTATS_TYPE_PID:
+                                               rtid = *(int *) NLA_DATA(na);
+                                               if (print_delays)
+                                                       printf("PID\t%d\n", rtid);
+                                               break;
+                                       case TASKSTATS_TYPE_TGID:
+                                               rtid = *(int *) NLA_DATA(na);
+                                               if (print_delays)
+                                                       printf("TGID\t%d\n", rtid);
+                                               break;
+                                       case TASKSTATS_TYPE_STATS:
+                                               count++;
+                                               if (print_delays)
+                                                       print_delayacct((struct taskstats *) NLA_DATA(na));
+                                               if (print_io_accounting)
+                                                       print_ioacct((struct taskstats *) NLA_DATA(na));
+                                               if (print_task_context_switch_counts)
+                                                       task_context_switch_counts((struct taskstats *) NLA_DATA(na));
+                                               if (fd) {
+                                                       if (write(fd, NLA_DATA(na), na->nla_len) < 0) {
+                                                               err(1,"write error\n");
+                                                       }
+                                               }
+                                               if (!loop)
+                                                       goto done;
+                                               break;
+                                       case TASKSTATS_TYPE_NULL:
+                                               break;
+                                       default:
+                                               fprintf(stderr, "Unknown nested"
+                                                       " nla_type %d\n",
+                                                       na->nla_type);
+                                               break;
+                                       }
+                                       len2 += NLA_ALIGN(na->nla_len);
+                                       na = (struct nlattr *)((char *)na +
+                                                              NLA_ALIGN(na->nla_len));
+                               }
+                               break;
+
+                       case CGROUPSTATS_TYPE_CGROUP_STATS:
+                               print_cgroupstats(NLA_DATA(na));
+                               break;
+                       default:
+                               fprintf(stderr, "Unknown nla_type %d\n",
+                                       na->nla_type);
+                       case TASKSTATS_TYPE_NULL:
+                               break;
+                       }
+                       na = (struct nlattr *) (GENLMSG_DATA(&msg) + len);
+               }
+       } while (loop);
+done:
+       if (maskset) {
+               rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET,
+                             TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK,
+                             &cpumask, strlen(cpumask) + 1);
+               printf("Sent deregister mask, retval %d\n", rc);
+               if (rc < 0)
+                       err(rc, "error sending deregister cpumask\n");
+       }
+err:
+       close(nl_sd);
+       if (fd)
+               close(fd);
+       if (cfd)
+               close(cfd);
+       return 0;
+}
diff --git a/tools/laptop/dslm/.gitignore b/tools/laptop/dslm/.gitignore
new file mode 100644 (file)
index 0000000..9fc984e
--- /dev/null
@@ -0,0 +1 @@
+dslm
diff --git a/tools/laptop/dslm/Makefile b/tools/laptop/dslm/Makefile
new file mode 100644 (file)
index 0000000..ff613b3
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := dslm
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/tools/laptop/dslm/dslm.c b/tools/laptop/dslm/dslm.c
new file mode 100644 (file)
index 0000000..d5dd2d4
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * dslm.c
+ * Simple Disk Sleep Monitor
+ *  by Bartek Kania
+ * Licensed under the GPL
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/hdreg.h>
+
+#ifdef DEBUG
+#define D(x) x
+#else
+#define D(x)
+#endif
+
+int endit = 0;
+
+/* Check if the disk is in powersave-mode
+ * Most of the code is stolen from hdparm.
+ * 1 = active, 0 = standby/sleep, -1 = unknown */
+static int check_powermode(int fd)
+{
+    unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0};
+    int state;
+
+    if (ioctl(fd, HDIO_DRIVE_CMD, &args)
+       && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */
+       && ioctl(fd, HDIO_DRIVE_CMD, &args)) {
+       if (errno != EIO || args[0] != 0 || args[1] != 0) {
+           state = -1; /* "unknown"; */
+       } else
+           state = 0; /* "sleeping"; */
+    } else {
+       state = (args[2] == 255) ? 1 : 0;
+    }
+    D(printf(" drive state is:  %d\n", state));
+
+    return state;
+}
+
+static char *state_name(int i)
+{
+    if (i == -1) return "unknown";
+    if (i == 0) return "sleeping";
+    if (i == 1) return "active";
+
+    return "internal error";
+}
+
+static char *myctime(time_t time)
+{
+    char *ts = ctime(&time);
+    ts[strlen(ts) - 1] = 0;
+
+    return ts;
+}
+
+static void measure(int fd)
+{
+    time_t start_time;
+    int last_state;
+    time_t last_time;
+    int curr_state;
+    time_t curr_time = 0;
+    time_t time_diff;
+    time_t active_time = 0;
+    time_t sleep_time = 0;
+    time_t unknown_time = 0;
+    time_t total_time = 0;
+    int changes = 0;
+    float tmp;
+
+    printf("Starting measurements\n");
+
+    last_state = check_powermode(fd);
+    start_time = last_time = time(0);
+    printf("  System is in state %s\n\n", state_name(last_state));
+
+    while(!endit) {
+       sleep(1);
+       curr_state = check_powermode(fd);
+
+       if (curr_state != last_state || endit) {
+           changes++;
+           curr_time = time(0);
+           time_diff = curr_time - last_time;
+
+           if (last_state == 1) active_time += time_diff;
+           else if (last_state == 0) sleep_time += time_diff;
+           else unknown_time += time_diff;
+
+           last_state = curr_state;
+           last_time = curr_time;
+
+           printf("%s: State-change to %s\n", myctime(curr_time),
+                  state_name(curr_state));
+       }
+    }
+    changes--; /* Compensate for SIGINT */
+
+    total_time = time(0) - start_time;
+    printf("\nTotal running time:  %lus\n", curr_time - start_time);
+    printf(" State changed %d times\n", changes);
+
+    tmp = (float)sleep_time / (float)total_time * 100;
+    printf(" Time in sleep state:   %lus (%.2f%%)\n", sleep_time, tmp);
+    tmp = (float)active_time / (float)total_time * 100;
+    printf(" Time in active state:  %lus (%.2f%%)\n", active_time, tmp);
+    tmp = (float)unknown_time / (float)total_time * 100;
+    printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp);
+}
+
+static void ender(int s)
+{
+    endit = 1;
+}
+
+static void usage(void)
+{
+    puts("usage: dslm [-w <time>] <disk>");
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    int fd;
+    char *disk = 0;
+    int settle_time = 60;
+
+    /* Parse the simple command-line */
+    if (argc == 2)
+       disk = argv[1];
+    else if (argc == 4) {
+       settle_time = atoi(argv[2]);
+       disk = argv[3];
+    } else
+       usage();
+
+    if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) {
+       printf("Can't open %s, because: %s\n", disk, strerror(errno));
+       exit(-1);
+    }
+
+    if (settle_time) {
+       printf("Waiting %d seconds for the system to settle down to "
+              "'normal'\n", settle_time);
+       sleep(settle_time);
+    } else
+       puts("Not waiting for system to settle down");
+
+    signal(SIGINT, ender);
+
+    measure(fd);
+
+    close(fd);
+
+    return 0;
+}
diff --git a/tools/pcmcia/.gitignore b/tools/pcmcia/.gitignore
new file mode 100644 (file)
index 0000000..53d0813
--- /dev/null
@@ -0,0 +1 @@
+crc32hash
diff --git a/tools/pcmcia/Makefile b/tools/pcmcia/Makefile
new file mode 100644 (file)
index 0000000..81a7498
--- /dev/null
@@ -0,0 +1,9 @@
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -I../../usr/include
+
+PROGS := crc32hash
+
+all: $(PROGS)
+
+clean:
+       rm -fr $(PROGS)
diff --git a/tools/pcmcia/crc32hash.c b/tools/pcmcia/crc32hash.c
new file mode 100644 (file)
index 0000000..44f8bee
--- /dev/null
@@ -0,0 +1,32 @@
+/* crc32hash.c - derived from linux/lib/crc32.c, GNU GPL v2 */
+/* Usage example:
+$ ./crc32hash "Dual Speed"
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+static unsigned int crc32(unsigned char const *p, unsigned int len)
+{
+       int i;
+       unsigned int crc = 0;
+       while (len--) {
+               crc ^= *p++;
+               for (i = 0; i < 8; i++)
+                       crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
+       }
+       return crc;
+}
+
+int main(int argc, char **argv) {
+       unsigned int result;
+       if (argc != 2) {
+               printf("no string passed as argument\n");
+               return -1;
+       }
+       result = crc32((unsigned char const *)argv[1], strlen(argv[1]));
+       printf("0x%x\n", result);
+       return 0;
+}
index 878daf3..7dc5a0a 100644 (file)
@@ -1,4 +1,3 @@
-#include <linux/kconfig.h>
 #include <linux/bug.h>
 
 void check(void)
index 9d0919e..f2e07f2 100644 (file)
@@ -3,7 +3,8 @@ CFLAGS += -I. -g -O2 -Wall -D_LGPL_SOURCE
 LDFLAGS += -lpthread -lurcu
 TARGETS = main
 OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \
-        regression1.o regression2.o regression3.o multiorder.o
+        regression1.o regression2.o regression3.o multiorder.o \
+        iteration_check.o
 
 targets: $(TARGETS)
 
diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c
new file mode 100644 (file)
index 0000000..9adb8e7
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * iteration_check.c: test races having to do with radix tree iteration
+ * Copyright (c) 2016 Intel Corporation
+ * Author: Ross Zwisler <ross.zwisler@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ */
+#include <linux/radix-tree.h>
+#include <pthread.h>
+#include "test.h"
+
+#define NUM_THREADS 4
+#define TAG 0
+static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_t threads[NUM_THREADS];
+RADIX_TREE(tree, GFP_KERNEL);
+bool test_complete;
+
+/* relentlessly fill the tree with tagged entries */
+static void *add_entries_fn(void *arg)
+{
+       int pgoff;
+
+       while (!test_complete) {
+               for (pgoff = 0; pgoff < 100; pgoff++) {
+                       pthread_mutex_lock(&tree_lock);
+                       if (item_insert(&tree, pgoff) == 0)
+                               item_tag_set(&tree, pgoff, TAG);
+                       pthread_mutex_unlock(&tree_lock);
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find
+ * things that have been removed and randomly resetting our iteration to the
+ * next chunk with radix_tree_iter_next().  Both radix_tree_iter_retry() and
+ * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
+ * NULL 'slot' variable.
+ */
+static void *tagged_iteration_fn(void *arg)
+{
+       struct radix_tree_iter iter;
+       void **slot;
+
+       while (!test_complete) {
+               rcu_read_lock();
+               radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) {
+                       void *entry;
+                       int i;
+
+                       /* busy wait to let removals happen */
+                       for (i = 0; i < 1000000; i++)
+                               ;
+
+                       entry = radix_tree_deref_slot(slot);
+                       if (unlikely(!entry))
+                               continue;
+
+                       if (radix_tree_deref_retry(entry)) {
+                               slot = radix_tree_iter_retry(&iter);
+                               continue;
+                       }
+
+                       if (rand() % 50 == 0)
+                               slot = radix_tree_iter_next(&iter);
+               }
+               rcu_read_unlock();
+       }
+
+       return NULL;
+}
+
+/*
+ * Iterate over the entries, doing a radix_tree_iter_retry() as we find things
+ * that have been removed and randomly resetting our iteration to the next
+ * chunk with radix_tree_iter_next().  Both radix_tree_iter_retry() and
+ * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a
+ * NULL 'slot' variable.
+ */
+static void *untagged_iteration_fn(void *arg)
+{
+       struct radix_tree_iter iter;
+       void **slot;
+
+       while (!test_complete) {
+               rcu_read_lock();
+               radix_tree_for_each_slot(slot, &tree, &iter, 0) {
+                       void *entry;
+                       int i;
+
+                       /* busy wait to let removals happen */
+                       for (i = 0; i < 1000000; i++)
+                               ;
+
+                       entry = radix_tree_deref_slot(slot);
+                       if (unlikely(!entry))
+                               continue;
+
+                       if (radix_tree_deref_retry(entry)) {
+                               slot = radix_tree_iter_retry(&iter);
+                               continue;
+                       }
+
+                       if (rand() % 50 == 0)
+                               slot = radix_tree_iter_next(&iter);
+               }
+               rcu_read_unlock();
+       }
+
+       return NULL;
+}
+
+/*
+ * Randomly remove entries to help induce radix_tree_iter_retry() calls in the
+ * two iteration functions.
+ */
+static void *remove_entries_fn(void *arg)
+{
+       while (!test_complete) {
+               int pgoff;
+
+               pgoff = rand() % 100;
+
+               pthread_mutex_lock(&tree_lock);
+               item_delete(&tree, pgoff);
+               pthread_mutex_unlock(&tree_lock);
+       }
+
+       return NULL;
+}
+
+/* This is a unit test for a bug found by the syzkaller tester */
+void iteration_test(void)
+{
+       int i;
+
+       printf("Running iteration tests for 10 seconds\n");
+
+       srand(time(0));
+       test_complete = false;
+
+       if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) {
+               perror("pthread_create");
+               exit(1);
+       }
+       if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) {
+               perror("pthread_create");
+               exit(1);
+       }
+       if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) {
+               perror("pthread_create");
+               exit(1);
+       }
+       if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) {
+               perror("pthread_create");
+               exit(1);
+       }
+
+       sleep(10);
+       test_complete = true;
+
+       for (i = 0; i < NUM_THREADS; i++) {
+               if (pthread_join(threads[i], NULL)) {
+                       perror("pthread_join");
+                       exit(1);
+               }
+       }
+
+       item_kill_tree(&tree);
+}
index b7619ff..daa9010 100644 (file)
@@ -332,6 +332,7 @@ int main(int argc, char **argv)
        regression1_test();
        regression2_test();
        regression3_test();
+       iteration_test();
        single_thread_tests(long_run);
 
        sleep(1);
index 2d03a63..0d6813a 100644 (file)
@@ -43,7 +43,7 @@
 #include "regression.h"
 
 static RADIX_TREE(mt_tree, GFP_KERNEL);
-static pthread_mutex_t mt_lock;
+static pthread_mutex_t mt_lock = PTHREAD_MUTEX_INITIALIZER;
 
 struct page {
        pthread_mutex_t lock;
index e851313..217fb24 100644 (file)
@@ -27,6 +27,7 @@ void item_kill_tree(struct radix_tree_root *root);
 
 void tag_check(void);
 void multiorder_checks(void);
+void iteration_test(void);
 
 struct item *
 item_tag_set(struct radix_tree_root *root, unsigned long index, int tag);
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore
new file mode 100644 (file)
index 0000000..31d6e42
--- /dev/null
@@ -0,0 +1 @@
+dnotify_test
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile
new file mode 100644 (file)
index 0000000..0ab1130
--- /dev/null
@@ -0,0 +1,7 @@
+TEST_PROGS := dnotify_test
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/filesystems/dnotify_test.c b/tools/testing/selftests/filesystems/dnotify_test.c
new file mode 100644 (file)
index 0000000..8b37b4a
--- /dev/null
@@ -0,0 +1,34 @@
+#define _GNU_SOURCE    /* needed to get the defines */
+#include <fcntl.h>     /* in glibc 2.2 this has the needed
+                                  values defined */
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static volatile int event_fd;
+
+static void handler(int sig, siginfo_t *si, void *data)
+{
+       event_fd = si->si_fd;
+}
+
+int main(void)
+{
+       struct sigaction act;
+       int fd;
+
+       act.sa_sigaction = handler;
+       sigemptyset(&act.sa_mask);
+       act.sa_flags = SA_SIGINFO;
+       sigaction(SIGRTMIN + 1, &act, NULL);
+
+       fd = open(".", O_RDONLY);
+       fcntl(fd, F_SETSIG, SIGRTMIN + 1);
+       fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT);
+       /* we will now be notified if any of the files
+          in "." is modified or new files are created */
+       while (1) {
+               pause();
+               printf("Got event on fd=%d\n", event_fd);
+       }
+}
index e87dbe2..7ff002e 100755 (executable)
@@ -24,7 +24,7 @@
 
 # Test for a color capable console
 if [ -z "$USE_COLOR" ]; then
-    tput setf 7
+    tput setf 7 || tput setaf 7
     if [ $? -eq 0 ]; then
         USE_COLOR=1
         tput sgr0
index 4126312..88bcb17 100755 (executable)
@@ -23,7 +23,7 @@
 
 # Test for a color capable shell and pass the result to the subdir scripts
 USE_COLOR=0
-tput setf 7
+tput setf 7 || tput setaf 7
 if [ $? -eq 0 ]; then
     USE_COLOR=1
     tput sgr0
diff --git a/tools/testing/selftests/ia64/.gitignore b/tools/testing/selftests/ia64/.gitignore
new file mode 100644 (file)
index 0000000..ab806ed
--- /dev/null
@@ -0,0 +1 @@
+aliasing-test
diff --git a/tools/testing/selftests/ia64/Makefile b/tools/testing/selftests/ia64/Makefile
new file mode 100644 (file)
index 0000000..2b3de2d
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := aliasing-test
+
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/ia64/aliasing-test.c b/tools/testing/selftests/ia64/aliasing-test.c
new file mode 100644 (file)
index 0000000..62a190d
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Exercise /dev/mem mmap cases that have been troublesome in the past
+ *
+ * (c) Copyright 2007 Hewlett-Packard Development Company, L.P.
+ *     Bjorn Helgaas <bjorn.helgaas@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <linux/pci.h>
+
+int sum;
+
+static int map_mem(char *path, off_t offset, size_t length, int touch)
+{
+       int fd, rc;
+       void *addr;
+       int *c;
+
+       fd = open(path, O_RDWR);
+       if (fd == -1) {
+               perror(path);
+               return -1;
+       }
+
+       if (fnmatch("/proc/bus/pci/*", path, 0) == 0) {
+               rc = ioctl(fd, PCIIOC_MMAP_IS_MEM);
+               if (rc == -1)
+                       perror("PCIIOC_MMAP_IS_MEM ioctl");
+       }
+
+       addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset);
+       if (addr == MAP_FAILED)
+               return 1;
+
+       if (touch) {
+               c = (int *) addr;
+               while (c < (int *) (addr + length))
+                       sum += *c++;
+       }
+
+       rc = munmap(addr, length);
+       if (rc == -1) {
+               perror("munmap");
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch)
+{
+       struct dirent **namelist;
+       char *name, *path2;
+       int i, n, r, rc = 0, result = 0;
+       struct stat buf;
+
+       n = scandir(path, &namelist, 0, alphasort);
+       if (n < 0) {
+               perror("scandir");
+               return -1;
+       }
+
+       for (i = 0; i < n; i++) {
+               name = namelist[i]->d_name;
+
+               if (fnmatch(".", name, 0) == 0)
+                       goto skip;
+               if (fnmatch("..", name, 0) == 0)
+                       goto skip;
+
+               path2 = malloc(strlen(path) + strlen(name) + 3);
+               strcpy(path2, path);
+               strcat(path2, "/");
+               strcat(path2, name);
+
+               if (fnmatch(file, name, 0) == 0) {
+                       rc = map_mem(path2, offset, length, touch);
+                       if (rc == 0)
+                               fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable");
+                       else if (rc > 0)
+                               fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length);
+                       else {
+                               fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length);
+                               return rc;
+                       }
+               } else {
+                       r = lstat(path2, &buf);
+                       if (r == 0 && S_ISDIR(buf.st_mode)) {
+                               rc = scan_tree(path2, file, offset, length, touch);
+                               if (rc < 0)
+                                       return rc;
+                       }
+               }
+
+               result |= rc;
+               free(path2);
+
+skip:
+               free(namelist[i]);
+       }
+       free(namelist);
+       return result;
+}
+
+char buf[1024];
+
+static int read_rom(char *path)
+{
+       int fd, rc;
+       size_t size = 0;
+
+       fd = open(path, O_RDWR);
+       if (fd == -1) {
+               perror(path);
+               return -1;
+       }
+
+       rc = write(fd, "1", 2);
+       if (rc <= 0) {
+               close(fd);
+               perror("write");
+               return -1;
+       }
+
+       do {
+               rc = read(fd, buf, sizeof(buf));
+               if (rc > 0)
+                       size += rc;
+       } while (rc > 0);
+
+       close(fd);
+       return size;
+}
+
+static int scan_rom(char *path, char *file)
+{
+       struct dirent **namelist;
+       char *name, *path2;
+       int i, n, r, rc = 0, result = 0;
+       struct stat buf;
+
+       n = scandir(path, &namelist, 0, alphasort);
+       if (n < 0) {
+               perror("scandir");
+               return -1;
+       }
+
+       for (i = 0; i < n; i++) {
+               name = namelist[i]->d_name;
+
+               if (fnmatch(".", name, 0) == 0)
+                       goto skip;
+               if (fnmatch("..", name, 0) == 0)
+                       goto skip;
+
+               path2 = malloc(strlen(path) + strlen(name) + 3);
+               strcpy(path2, path);
+               strcat(path2, "/");
+               strcat(path2, name);
+
+               if (fnmatch(file, name, 0) == 0) {
+                       rc = read_rom(path2);
+
+                       /*
+                        * It's OK if the ROM is unreadable.  Maybe there
+                        * is no ROM, or some other error occurred.  The
+                        * important thing is that no MCA happened.
+                        */
+                       if (rc > 0)
+                               fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc);
+                       else {
+                               fprintf(stderr, "PASS: %s not readable\n", path2);
+                               return rc;
+                       }
+               } else {
+                       r = lstat(path2, &buf);
+                       if (r == 0 && S_ISDIR(buf.st_mode)) {
+                               rc = scan_rom(path2, file);
+                               if (rc < 0)
+                                       return rc;
+                       }
+               }
+
+               result |= rc;
+               free(path2);
+
+skip:
+               free(namelist[i]);
+       }
+       free(namelist);
+       return result;
+}
+
+int main(void)
+{
+       int rc;
+
+       if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0)
+               fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n");
+       else
+               fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n");
+
+       /*
+        * It's not safe to blindly read the VGA frame buffer.  If you know
+        * how to poke the card the right way, it should respond, but it's
+        * not safe in general.  Many machines, e.g., Intel chipsets, cover
+        * up a non-responding card by just returning -1, but others will
+        * report the failure as a machine check.
+        */
+       if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0)
+               fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n");
+       else
+               fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n");
+
+       if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0)
+               fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n");
+       else
+               fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n");
+
+       /*
+        * Often you can map all the individual pieces above (0-0xA0000,
+        * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole
+        * thing at once.  This is because the individual pieces use different
+        * attributes, and there's no single attribute supported over the
+        * whole region.
+        */
+       rc = map_mem("/dev/mem", 0, 1024*1024, 0);
+       if (rc == 0)
+               fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n");
+       else if (rc > 0)
+               fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n");
+       else
+               fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n");
+
+       scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1);
+       scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0);
+       scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1);
+       scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0);
+
+       scan_rom("/sys/devices", "rom");
+
+       scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1);
+       scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0);
+       scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1);
+       scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0);
+
+       return rc;
+}
diff --git a/tools/testing/selftests/networking/timestamping/.gitignore b/tools/testing/selftests/networking/timestamping/.gitignore
new file mode 100644 (file)
index 0000000..9e69e98
--- /dev/null
@@ -0,0 +1,3 @@
+timestamping
+txtimestamp
+hwtstamp_config
diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile
new file mode 100644 (file)
index 0000000..ccbb9ed
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := hwtstamp_config timestamping txtimestamp
+
+all: $(TEST_PROGS)
+
+include ../../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/networking/timestamping/hwtstamp_config.c b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c
new file mode 100644 (file)
index 0000000..e8b685a
--- /dev/null
@@ -0,0 +1,134 @@
+/* Test program for SIOC{G,S}HWTSTAMP
+ * Copyright 2013 Solarflare Communications
+ * Author: Ben Hutchings
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <linux/if.h>
+#include <linux/net_tstamp.h>
+#include <linux/sockios.h>
+
+static int
+lookup_value(const char **names, int size, const char *name)
+{
+       int value;
+
+       for (value = 0; value < size; value++)
+               if (names[value] && strcasecmp(names[value], name) == 0)
+                       return value;
+
+       return -1;
+}
+
+static const char *
+lookup_name(const char **names, int size, int value)
+{
+       return (value >= 0 && value < size) ? names[value] : NULL;
+}
+
+static void list_names(FILE *f, const char **names, int size)
+{
+       int value;
+
+       for (value = 0; value < size; value++)
+               if (names[value])
+                       fprintf(f, "    %s\n", names[value]);
+}
+
+static const char *tx_types[] = {
+#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name
+       TX_TYPE(OFF),
+       TX_TYPE(ON),
+       TX_TYPE(ONESTEP_SYNC)
+#undef TX_TYPE
+};
+#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0])))
+
+static const char *rx_filters[] = {
+#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name
+       RX_FILTER(NONE),
+       RX_FILTER(ALL),
+       RX_FILTER(SOME),
+       RX_FILTER(PTP_V1_L4_EVENT),
+       RX_FILTER(PTP_V1_L4_SYNC),
+       RX_FILTER(PTP_V1_L4_DELAY_REQ),
+       RX_FILTER(PTP_V2_L4_EVENT),
+       RX_FILTER(PTP_V2_L4_SYNC),
+       RX_FILTER(PTP_V2_L4_DELAY_REQ),
+       RX_FILTER(PTP_V2_L2_EVENT),
+       RX_FILTER(PTP_V2_L2_SYNC),
+       RX_FILTER(PTP_V2_L2_DELAY_REQ),
+       RX_FILTER(PTP_V2_EVENT),
+       RX_FILTER(PTP_V2_SYNC),
+       RX_FILTER(PTP_V2_DELAY_REQ),
+#undef RX_FILTER
+};
+#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0])))
+
+static void usage(void)
+{
+       fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n"
+             "tx_type is any of (case-insensitive):\n",
+             stderr);
+       list_names(stderr, tx_types, N_TX_TYPES);
+       fputs("rx_filter is any of (case-insensitive):\n", stderr);
+       list_names(stderr, rx_filters, N_RX_FILTERS);
+}
+
+int main(int argc, char **argv)
+{
+       struct ifreq ifr;
+       struct hwtstamp_config config;
+       const char *name;
+       int sock;
+
+       if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) {
+               usage();
+               return 2;
+       }
+
+       if (argc == 4) {
+               config.flags = 0;
+               config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]);
+               config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]);
+               if (config.tx_type < 0 || config.rx_filter < 0) {
+                       usage();
+                       return 2;
+               }
+       }
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0) {
+               perror("socket");
+               return 1;
+       }
+
+       strcpy(ifr.ifr_name, argv[1]);
+       ifr.ifr_data = (caddr_t)&config;
+
+       if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) {
+               perror("ioctl");
+               return 1;
+       }
+
+       printf("flags = %#x\n", config.flags);
+       name = lookup_name(tx_types, N_TX_TYPES, config.tx_type);
+       if (name)
+               printf("tx_type = %s\n", name);
+       else
+               printf("tx_type = %d\n", config.tx_type);
+       name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter);
+       if (name)
+               printf("rx_filter = %s\n", name);
+       else
+               printf("rx_filter = %d\n", config.rx_filter);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/networking/timestamping/timestamping.c b/tools/testing/selftests/networking/timestamping/timestamping.c
new file mode 100644 (file)
index 0000000..5cdfd74
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * This program demonstrates how the various time stamping features in
+ * the Linux kernel work. It emulates the behavior of a PTP
+ * implementation in stand-alone master mode by sending PTPv1 Sync
+ * multicasts once every second. It looks for similar packets, but
+ * beyond that doesn't actually implement PTP.
+ *
+ * Outgoing packets are time stamped with SO_TIMESTAMPING with or
+ * without hardware support.
+ *
+ * Incoming packets are time stamped with SO_TIMESTAMPING with or
+ * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
+ * SO_TIMESTAMP[NS].
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ * Author: Patrick Ohly <patrick.ohly@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include <asm/types.h>
+#include <linux/net_tstamp.h>
+#include <linux/errqueue.h>
+
+#ifndef SO_TIMESTAMPING
+# define SO_TIMESTAMPING         37
+# define SCM_TIMESTAMPING        SO_TIMESTAMPING
+#endif
+
+#ifndef SO_TIMESTAMPNS
+# define SO_TIMESTAMPNS 35
+#endif
+
+#ifndef SIOCGSTAMPNS
+# define SIOCGSTAMPNS 0x8907
+#endif
+
+#ifndef SIOCSHWTSTAMP
+# define SIOCSHWTSTAMP 0x89b0
+#endif
+
+static void usage(const char *error)
+{
+       if (error)
+               printf("invalid option: %s\n", error);
+       printf("timestamping interface option*\n\n"
+              "Options:\n"
+              "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
+              "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
+              "  SO_TIMESTAMPNS - more accurate software time stamping\n"
+              "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
+              "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
+              "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
+              "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
+              "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
+              "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
+              "  SIOCGSTAMP - check last socket time stamp\n"
+              "  SIOCGSTAMPNS - more accurate socket time stamp\n");
+       exit(1);
+}
+
+static void bail(const char *error)
+{
+       printf("%s: %s\n", error, strerror(errno));
+       exit(1);
+}
+
+static const unsigned char sync[] = {
+       0x00, 0x01, 0x00, 0x01,
+       0x5f, 0x44, 0x46, 0x4c,
+       0x54, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x01, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x01, 0x00, 0x37,
+       0x00, 0x00, 0x00, 0x08,
+       0x00, 0x00, 0x00, 0x00,
+       0x49, 0x05, 0xcd, 0x01,
+       0x29, 0xb1, 0x8d, 0xb0,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x00, 0x00, 0x37,
+       0x00, 0x00, 0x00, 0x04,
+       0x44, 0x46, 0x4c, 0x54,
+       0x00, 0x00, 0xf0, 0x60,
+       0x00, 0x01, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x01,
+       0x00, 0x00, 0xf0, 0x60,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x04,
+       0x44, 0x46, 0x4c, 0x54,
+       0x00, 0x01,
+
+       /* fake uuid */
+       0x00, 0x01,
+       0x02, 0x03, 0x04, 0x05,
+
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00
+};
+
+static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
+{
+       struct timeval now;
+       int res;
+
+       res = sendto(sock, sync, sizeof(sync), 0,
+               addr, addr_len);
+       gettimeofday(&now, 0);
+       if (res < 0)
+               printf("%s: %s\n", "send", strerror(errno));
+       else
+               printf("%ld.%06ld: sent %d bytes\n",
+                      (long)now.tv_sec, (long)now.tv_usec,
+                      res);
+}
+
+static void printpacket(struct msghdr *msg, int res,
+                       char *data,
+                       int sock, int recvmsg_flags,
+                       int siocgstamp, int siocgstampns)
+{
+       struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
+       struct cmsghdr *cmsg;
+       struct timeval tv;
+       struct timespec ts;
+       struct timeval now;
+
+       gettimeofday(&now, 0);
+
+       printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
+              (long)now.tv_sec, (long)now.tv_usec,
+              (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
+              res,
+              inet_ntoa(from_addr->sin_addr),
+              msg->msg_controllen);
+       for (cmsg = CMSG_FIRSTHDR(msg);
+            cmsg;
+            cmsg = CMSG_NXTHDR(msg, cmsg)) {
+               printf("   cmsg len %zu: ", cmsg->cmsg_len);
+               switch (cmsg->cmsg_level) {
+               case SOL_SOCKET:
+                       printf("SOL_SOCKET ");
+                       switch (cmsg->cmsg_type) {
+                       case SO_TIMESTAMP: {
+                               struct timeval *stamp =
+                                       (struct timeval *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMP %ld.%06ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_usec);
+                               break;
+                       }
+                       case SO_TIMESTAMPNS: {
+                               struct timespec *stamp =
+                                       (struct timespec *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMPNS %ld.%09ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               break;
+                       }
+                       case SO_TIMESTAMPING: {
+                               struct timespec *stamp =
+                                       (struct timespec *)CMSG_DATA(cmsg);
+                               printf("SO_TIMESTAMPING ");
+                               printf("SW %ld.%09ld ",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               stamp++;
+                               /* skip deprecated HW transformed */
+                               stamp++;
+                               printf("HW raw %ld.%09ld",
+                                      (long)stamp->tv_sec,
+                                      (long)stamp->tv_nsec);
+                               break;
+                       }
+                       default:
+                               printf("type %d", cmsg->cmsg_type);
+                               break;
+                       }
+                       break;
+               case IPPROTO_IP:
+                       printf("IPPROTO_IP ");
+                       switch (cmsg->cmsg_type) {
+                       case IP_RECVERR: {
+                               struct sock_extended_err *err =
+                                       (struct sock_extended_err *)CMSG_DATA(cmsg);
+                               printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
+                                       strerror(err->ee_errno),
+                                       err->ee_origin,
+#ifdef SO_EE_ORIGIN_TIMESTAMPING
+                                       err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
+                                       "bounced packet" : "unexpected origin"
+#else
+                                       "probably SO_EE_ORIGIN_TIMESTAMPING"
+#endif
+                                       );
+                               if (res < sizeof(sync))
+                                       printf(" => truncated data?!");
+                               else if (!memcmp(sync, data + res - sizeof(sync),
+                                                       sizeof(sync)))
+                                       printf(" => GOT OUR DATA BACK (HURRAY!)");
+                               break;
+                       }
+                       case IP_PKTINFO: {
+                               struct in_pktinfo *pktinfo =
+                                       (struct in_pktinfo *)CMSG_DATA(cmsg);
+                               printf("IP_PKTINFO interface index %u",
+                                       pktinfo->ipi_ifindex);
+                               break;
+                       }
+                       default:
+                               printf("type %d", cmsg->cmsg_type);
+                               break;
+                       }
+                       break;
+               default:
+                       printf("level %d type %d",
+                               cmsg->cmsg_level,
+                               cmsg->cmsg_type);
+                       break;
+               }
+               printf("\n");
+       }
+
+       if (siocgstamp) {
+               if (ioctl(sock, SIOCGSTAMP, &tv))
+                       printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
+               else
+                       printf("SIOCGSTAMP %ld.%06ld\n",
+                              (long)tv.tv_sec,
+                              (long)tv.tv_usec);
+       }
+       if (siocgstampns) {
+               if (ioctl(sock, SIOCGSTAMPNS, &ts))
+                       printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
+               else
+                       printf("SIOCGSTAMPNS %ld.%09ld\n",
+                              (long)ts.tv_sec,
+                              (long)ts.tv_nsec);
+       }
+}
+
+static void recvpacket(int sock, int recvmsg_flags,
+                      int siocgstamp, int siocgstampns)
+{
+       char data[256];
+       struct msghdr msg;
+       struct iovec entry;
+       struct sockaddr_in from_addr;
+       struct {
+               struct cmsghdr cm;
+               char control[512];
+       } control;
+       int res;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &entry;
+       msg.msg_iovlen = 1;
+       entry.iov_base = data;
+       entry.iov_len = sizeof(data);
+       msg.msg_name = (caddr_t)&from_addr;
+       msg.msg_namelen = sizeof(from_addr);
+       msg.msg_control = &control;
+       msg.msg_controllen = sizeof(control);
+
+       res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
+       if (res < 0) {
+               printf("%s %s: %s\n",
+                      "recvmsg",
+                      (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
+                      strerror(errno));
+       } else {
+               printpacket(&msg, res, data,
+                           sock, recvmsg_flags,
+                           siocgstamp, siocgstampns);
+       }
+}
+
+int main(int argc, char **argv)
+{
+       int so_timestamping_flags = 0;
+       int so_timestamp = 0;
+       int so_timestampns = 0;
+       int siocgstamp = 0;
+       int siocgstampns = 0;
+       int ip_multicast_loop = 0;
+       char *interface;
+       int i;
+       int enabled = 1;
+       int sock;
+       struct ifreq device;
+       struct ifreq hwtstamp;
+       struct hwtstamp_config hwconfig, hwconfig_requested;
+       struct sockaddr_in addr;
+       struct ip_mreq imr;
+       struct in_addr iaddr;
+       int val;
+       socklen_t len;
+       struct timeval next;
+
+       if (argc < 2)
+               usage(0);
+       interface = argv[1];
+
+       for (i = 2; i < argc; i++) {
+               if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
+                       so_timestamp = 1;
+               else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
+                       so_timestampns = 1;
+               else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
+                       siocgstamp = 1;
+               else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
+                       siocgstampns = 1;
+               else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
+                       ip_multicast_loop = 1;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
+               else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
+                       so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
+               else
+                       usage(argv[i]);
+       }
+
+       sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (sock < 0)
+               bail("socket");
+
+       memset(&device, 0, sizeof(device));
+       strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
+       if (ioctl(sock, SIOCGIFADDR, &device) < 0)
+               bail("getting interface IP address");
+
+       memset(&hwtstamp, 0, sizeof(hwtstamp));
+       strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
+       hwtstamp.ifr_data = (void *)&hwconfig;
+       memset(&hwconfig, 0, sizeof(hwconfig));
+       hwconfig.tx_type =
+               (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
+               HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
+       hwconfig.rx_filter =
+               (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
+               HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
+       hwconfig_requested = hwconfig;
+       if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
+               if ((errno == EINVAL || errno == ENOTSUP) &&
+                   hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
+                   hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
+                       printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
+               else
+                       bail("SIOCSHWTSTAMP");
+       }
+       printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
+              hwconfig_requested.tx_type, hwconfig.tx_type,
+              hwconfig_requested.rx_filter, hwconfig.rx_filter);
+
+       /* bind to PTP port */
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = htons(319 /* PTP event port */);
+       if (bind(sock,
+                (struct sockaddr *)&addr,
+                sizeof(struct sockaddr_in)) < 0)
+               bail("bind");
+
+       /* set multicast group for outgoing packets */
+       inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
+       addr.sin_addr = iaddr;
+       imr.imr_multiaddr.s_addr = iaddr.s_addr;
+       imr.imr_interface.s_addr =
+               ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
+       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
+                      &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
+               bail("set multicast");
+
+       /* join multicast group, loop our own packet */
+       if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                      &imr, sizeof(struct ip_mreq)) < 0)
+               bail("join multicast group");
+
+       if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
+                      &ip_multicast_loop, sizeof(enabled)) < 0) {
+               bail("loop multicast");
+       }
+
+       /* set socket options for time stamping */
+       if (so_timestamp &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
+                          &enabled, sizeof(enabled)) < 0)
+               bail("setsockopt SO_TIMESTAMP");
+
+       if (so_timestampns &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
+                          &enabled, sizeof(enabled)) < 0)
+               bail("setsockopt SO_TIMESTAMPNS");
+
+       if (so_timestamping_flags &&
+               setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
+                          &so_timestamping_flags,
+                          sizeof(so_timestamping_flags)) < 0)
+               bail("setsockopt SO_TIMESTAMPING");
+
+       /* request IP_PKTINFO for debugging purposes */
+       if (setsockopt(sock, SOL_IP, IP_PKTINFO,
+                      &enabled, sizeof(enabled)) < 0)
+               printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
+
+       /* verify socket options */
+       len = sizeof(val);
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
+       else
+               printf("SO_TIMESTAMP %d\n", val);
+
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
+                      strerror(errno));
+       else
+               printf("SO_TIMESTAMPNS %d\n", val);
+
+       if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
+               printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
+                      strerror(errno));
+       } else {
+               printf("SO_TIMESTAMPING %d\n", val);
+               if (val != so_timestamping_flags)
+                       printf("   not the expected value %d\n",
+                              so_timestamping_flags);
+       }
+
+       /* send packets forever every five seconds */
+       gettimeofday(&next, 0);
+       next.tv_sec = (next.tv_sec + 1) / 5 * 5;
+       next.tv_usec = 0;
+       while (1) {
+               struct timeval now;
+               struct timeval delta;
+               long delta_us;
+               int res;
+               fd_set readfs, errorfs;
+
+               gettimeofday(&now, 0);
+               delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
+                       (long)(next.tv_usec - now.tv_usec);
+               if (delta_us > 0) {
+                       /* continue waiting for timeout or data */
+                       delta.tv_sec = delta_us / 1000000;
+                       delta.tv_usec = delta_us % 1000000;
+
+                       FD_ZERO(&readfs);
+                       FD_ZERO(&errorfs);
+                       FD_SET(sock, &readfs);
+                       FD_SET(sock, &errorfs);
+                       printf("%ld.%06ld: select %ldus\n",
+                              (long)now.tv_sec, (long)now.tv_usec,
+                              delta_us);
+                       res = select(sock + 1, &readfs, 0, &errorfs, &delta);
+                       gettimeofday(&now, 0);
+                       printf("%ld.%06ld: select returned: %d, %s\n",
+                              (long)now.tv_sec, (long)now.tv_usec,
+                              res,
+                              res < 0 ? strerror(errno) : "success");
+                       if (res > 0) {
+                               if (FD_ISSET(sock, &readfs))
+                                       printf("ready for reading\n");
+                               if (FD_ISSET(sock, &errorfs))
+                                       printf("has error\n");
+                               recvpacket(sock, 0,
+                                          siocgstamp,
+                                          siocgstampns);
+                               recvpacket(sock, MSG_ERRQUEUE,
+                                          siocgstamp,
+                                          siocgstampns);
+                       }
+               } else {
+                       /* write one packet */
+                       sendpacket(sock,
+                                  (struct sockaddr *)&addr,
+                                  sizeof(addr));
+                       next.tv_sec += 5;
+                       continue;
+               }
+       }
+
+       return 0;
+}
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c
new file mode 100644 (file)
index 0000000..5df0704
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2014 Google Inc.
+ * Author: willemb@google.com (Willem de Bruijn)
+ *
+ * Test software tx timestamping, including
+ *
+ * - SCHED, SND and ACK timestamps
+ * - RAW, UDP and TCP
+ * - IPv4 and IPv6
+ * - various packet sizes (to test GSO and TSO)
+ *
+ * Consult the command line arguments for help on running
+ * the various testcases.
+ *
+ * This test requires a dummy TCP server.
+ * A simple `nc6 [-u] -l -p $DESTPORT` will do
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <error.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <linux/errqueue.h>
+#include <linux/if_ether.h>
+#include <linux/net_tstamp.h>
+#include <netdb.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <netpacket/packet.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+/* command line parameters */
+static int cfg_proto = SOCK_STREAM;
+static int cfg_ipproto = IPPROTO_TCP;
+static int cfg_num_pkts = 4;
+static int do_ipv4 = 1;
+static int do_ipv6 = 1;
+static int cfg_payload_len = 10;
+static bool cfg_show_payload;
+static bool cfg_do_pktinfo;
+static bool cfg_loop_nodata;
+static uint16_t dest_port = 9000;
+
+static struct sockaddr_in daddr;
+static struct sockaddr_in6 daddr6;
+static struct timespec ts_prev;
+
+static void __print_timestamp(const char *name, struct timespec *cur,
+                             uint32_t key, int payload_len)
+{
+       if (!(cur->tv_sec | cur->tv_nsec))
+               return;
+
+       fprintf(stderr, "  %s: %lu s %lu us (seq=%u, len=%u)",
+                       name, cur->tv_sec, cur->tv_nsec / 1000,
+                       key, payload_len);
+
+       if ((ts_prev.tv_sec | ts_prev.tv_nsec)) {
+               int64_t cur_ms, prev_ms;
+
+               cur_ms = (long) cur->tv_sec * 1000 * 1000;
+               cur_ms += cur->tv_nsec / 1000;
+
+               prev_ms = (long) ts_prev.tv_sec * 1000 * 1000;
+               prev_ms += ts_prev.tv_nsec / 1000;
+
+               fprintf(stderr, "  (%+" PRId64 " us)", cur_ms - prev_ms);
+       }
+
+       ts_prev = *cur;
+       fprintf(stderr, "\n");
+}
+
+static void print_timestamp_usr(void)
+{
+       struct timespec ts;
+       struct timeval tv;      /* avoid dependency on -lrt */
+
+       gettimeofday(&tv, NULL);
+       ts.tv_sec = tv.tv_sec;
+       ts.tv_nsec = tv.tv_usec * 1000;
+
+       __print_timestamp("  USR", &ts, 0, 0);
+}
+
+static void print_timestamp(struct scm_timestamping *tss, int tstype,
+                           int tskey, int payload_len)
+{
+       const char *tsname;
+
+       switch (tstype) {
+       case SCM_TSTAMP_SCHED:
+               tsname = "  ENQ";
+               break;
+       case SCM_TSTAMP_SND:
+               tsname = "  SND";
+               break;
+       case SCM_TSTAMP_ACK:
+               tsname = "  ACK";
+               break;
+       default:
+               error(1, 0, "unknown timestamp type: %u",
+               tstype);
+       }
+       __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
+}
+
+/* TODO: convert to check_and_print payload once API is stable */
+static void print_payload(char *data, int len)
+{
+       int i;
+
+       if (!len)
+               return;
+
+       if (len > 70)
+               len = 70;
+
+       fprintf(stderr, "payload: ");
+       for (i = 0; i < len; i++)
+               fprintf(stderr, "%02hhx ", data[i]);
+       fprintf(stderr, "\n");
+}
+
+static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
+{
+       char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
+
+       fprintf(stderr, "         pktinfo: ifindex=%u src=%s dst=%s\n",
+               ifindex,
+               saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
+               daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
+}
+
+static void __poll(int fd)
+{
+       struct pollfd pollfd;
+       int ret;
+
+       memset(&pollfd, 0, sizeof(pollfd));
+       pollfd.fd = fd;
+       ret = poll(&pollfd, 1, 100);
+       if (ret != 1)
+               error(1, errno, "poll");
+}
+
+static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
+{
+       struct sock_extended_err *serr = NULL;
+       struct scm_timestamping *tss = NULL;
+       struct cmsghdr *cm;
+       int batch = 0;
+
+       for (cm = CMSG_FIRSTHDR(msg);
+            cm && cm->cmsg_len;
+            cm = CMSG_NXTHDR(msg, cm)) {
+               if (cm->cmsg_level == SOL_SOCKET &&
+                   cm->cmsg_type == SCM_TIMESTAMPING) {
+                       tss = (void *) CMSG_DATA(cm);
+               } else if ((cm->cmsg_level == SOL_IP &&
+                           cm->cmsg_type == IP_RECVERR) ||
+                          (cm->cmsg_level == SOL_IPV6 &&
+                           cm->cmsg_type == IPV6_RECVERR)) {
+                       serr = (void *) CMSG_DATA(cm);
+                       if (serr->ee_errno != ENOMSG ||
+                           serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
+                               fprintf(stderr, "unknown ip error %d %d\n",
+                                               serr->ee_errno,
+                                               serr->ee_origin);
+                               serr = NULL;
+                       }
+               } else if (cm->cmsg_level == SOL_IP &&
+                          cm->cmsg_type == IP_PKTINFO) {
+                       struct in_pktinfo *info = (void *) CMSG_DATA(cm);
+                       print_pktinfo(AF_INET, info->ipi_ifindex,
+                                     &info->ipi_spec_dst, &info->ipi_addr);
+               } else if (cm->cmsg_level == SOL_IPV6 &&
+                          cm->cmsg_type == IPV6_PKTINFO) {
+                       struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
+                       print_pktinfo(AF_INET6, info6->ipi6_ifindex,
+                                     NULL, &info6->ipi6_addr);
+               } else
+                       fprintf(stderr, "unknown cmsg %d,%d\n",
+                                       cm->cmsg_level, cm->cmsg_type);
+
+               if (serr && tss) {
+                       print_timestamp(tss, serr->ee_info, serr->ee_data,
+                                       payload_len);
+                       serr = NULL;
+                       tss = NULL;
+                       batch++;
+               }
+       }
+
+       if (batch > 1)
+               fprintf(stderr, "batched %d timestamps\n", batch);
+}
+
+static int recv_errmsg(int fd)
+{
+       static char ctrl[1024 /* overprovision*/];
+       static struct msghdr msg;
+       struct iovec entry;
+       static char *data;
+       int ret = 0;
+
+       data = malloc(cfg_payload_len);
+       if (!data)
+               error(1, 0, "malloc");
+
+       memset(&msg, 0, sizeof(msg));
+       memset(&entry, 0, sizeof(entry));
+       memset(ctrl, 0, sizeof(ctrl));
+
+       entry.iov_base = data;
+       entry.iov_len = cfg_payload_len;
+       msg.msg_iov = &entry;
+       msg.msg_iovlen = 1;
+       msg.msg_name = NULL;
+       msg.msg_namelen = 0;
+       msg.msg_control = ctrl;
+       msg.msg_controllen = sizeof(ctrl);
+
+       ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
+       if (ret == -1 && errno != EAGAIN)
+               error(1, errno, "recvmsg");
+
+       if (ret >= 0) {
+               __recv_errmsg_cmsg(&msg, ret);
+               if (cfg_show_payload)
+                       print_payload(data, cfg_payload_len);
+       }
+
+       free(data);
+       return ret == -1;
+}
+
+static void do_test(int family, unsigned int opt)
+{
+       char *buf;
+       int fd, i, val = 1, total_len;
+
+       if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
+               /* due to lack of checksum generation code */
+               fprintf(stderr, "test: skipping datagram over IPv6\n");
+               return;
+       }
+
+       total_len = cfg_payload_len;
+       if (cfg_proto == SOCK_RAW) {
+               total_len += sizeof(struct udphdr);
+               if (cfg_ipproto == IPPROTO_RAW)
+                       total_len += sizeof(struct iphdr);
+       }
+
+       buf = malloc(total_len);
+       if (!buf)
+               error(1, 0, "malloc");
+
+       fd = socket(family, cfg_proto, cfg_ipproto);
+       if (fd < 0)
+               error(1, errno, "socket");
+
+       if (cfg_proto == SOCK_STREAM) {
+               if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
+                              (char*) &val, sizeof(val)))
+                       error(1, 0, "setsockopt no nagle");
+
+               if (family == PF_INET) {
+                       if (connect(fd, (void *) &daddr, sizeof(daddr)))
+                               error(1, errno, "connect ipv4");
+               } else {
+                       if (connect(fd, (void *) &daddr6, sizeof(daddr6)))
+                               error(1, errno, "connect ipv6");
+               }
+       }
+
+       if (cfg_do_pktinfo) {
+               if (family == AF_INET6) {
+                       if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
+                                      &val, sizeof(val)))
+                               error(1, errno, "setsockopt pktinfo ipv6");
+               } else {
+                       if (setsockopt(fd, SOL_IP, IP_PKTINFO,
+                                      &val, sizeof(val)))
+                               error(1, errno, "setsockopt pktinfo ipv4");
+               }
+       }
+
+       opt |= SOF_TIMESTAMPING_SOFTWARE |
+              SOF_TIMESTAMPING_OPT_CMSG |
+              SOF_TIMESTAMPING_OPT_ID;
+       if (cfg_loop_nodata)
+               opt |= SOF_TIMESTAMPING_OPT_TSONLY;
+
+       if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
+                      (char *) &opt, sizeof(opt)))
+               error(1, 0, "setsockopt timestamping");
+
+       for (i = 0; i < cfg_num_pkts; i++) {
+               memset(&ts_prev, 0, sizeof(ts_prev));
+               memset(buf, 'a' + i, total_len);
+
+               if (cfg_proto == SOCK_RAW) {
+                       struct udphdr *udph;
+                       int off = 0;
+
+                       if (cfg_ipproto == IPPROTO_RAW) {
+                               struct iphdr *iph = (void *) buf;
+
+                               memset(iph, 0, sizeof(*iph));
+                               iph->ihl      = 5;
+                               iph->version  = 4;
+                               iph->ttl      = 2;
+                               iph->daddr    = daddr.sin_addr.s_addr;
+                               iph->protocol = IPPROTO_UDP;
+                               /* kernel writes saddr, csum, len */
+
+                               off = sizeof(*iph);
+                       }
+
+                       udph = (void *) buf + off;
+                       udph->source = ntohs(9000);     /* random spoof */
+                       udph->dest   = ntohs(dest_port);
+                       udph->len    = ntohs(sizeof(*udph) + cfg_payload_len);
+                       udph->check  = 0;       /* not allowed for IPv6 */
+               }
+
+               print_timestamp_usr();
+               if (cfg_proto != SOCK_STREAM) {
+                       if (family == PF_INET)
+                               val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr));
+                       else
+                               val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6));
+               } else {
+                       val = send(fd, buf, cfg_payload_len, 0);
+               }
+               if (val != total_len)
+                       error(1, errno, "send");
+
+               /* wait for all errors to be queued, else ACKs arrive OOO */
+               usleep(50 * 1000);
+
+               __poll(fd);
+
+               while (!recv_errmsg(fd)) {}
+       }
+
+       if (close(fd))
+               error(1, errno, "close");
+
+       free(buf);
+       usleep(400 * 1000);
+}
+
+static void __attribute__((noreturn)) usage(const char *filepath)
+{
+       fprintf(stderr, "\nUsage: %s [options] hostname\n"
+                       "\nwhere options are:\n"
+                       "  -4:   only IPv4\n"
+                       "  -6:   only IPv6\n"
+                       "  -h:   show this message\n"
+                       "  -I:   request PKTINFO\n"
+                       "  -l N: send N bytes at a time\n"
+                       "  -n:   set no-payload option\n"
+                       "  -r:   use raw\n"
+                       "  -R:   use raw (IP_HDRINCL)\n"
+                       "  -p N: connect to port N\n"
+                       "  -u:   use udp\n"
+                       "  -x:   show payload (up to 70 bytes)\n",
+                       filepath);
+       exit(1);
+}
+
+static void parse_opt(int argc, char **argv)
+{
+       int proto_count = 0;
+       char c;
+
+       while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) {
+               switch (c) {
+               case '4':
+                       do_ipv6 = 0;
+                       break;
+               case '6':
+                       do_ipv4 = 0;
+                       break;
+               case 'I':
+                       cfg_do_pktinfo = true;
+                       break;
+               case 'n':
+                       cfg_loop_nodata = true;
+                       break;
+               case 'r':
+                       proto_count++;
+                       cfg_proto = SOCK_RAW;
+                       cfg_ipproto = IPPROTO_UDP;
+                       break;
+               case 'R':
+                       proto_count++;
+                       cfg_proto = SOCK_RAW;
+                       cfg_ipproto = IPPROTO_RAW;
+                       break;
+               case 'u':
+                       proto_count++;
+                       cfg_proto = SOCK_DGRAM;
+                       cfg_ipproto = IPPROTO_UDP;
+                       break;
+               case 'l':
+                       cfg_payload_len = strtoul(optarg, NULL, 10);
+                       break;
+               case 'p':
+                       dest_port = strtoul(optarg, NULL, 10);
+                       break;
+               case 'x':
+                       cfg_show_payload = true;
+                       break;
+               case 'h':
+               default:
+                       usage(argv[0]);
+               }
+       }
+
+       if (!cfg_payload_len)
+               error(1, 0, "payload may not be nonzero");
+       if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472)
+               error(1, 0, "udp packet might exceed expected MTU");
+       if (!do_ipv4 && !do_ipv6)
+               error(1, 0, "pass -4 or -6, not both");
+       if (proto_count > 1)
+               error(1, 0, "pass -r, -R or -u, not multiple");
+
+       if (optind != argc - 1)
+               error(1, 0, "missing required hostname argument");
+}
+
+static void resolve_hostname(const char *hostname)
+{
+       struct addrinfo *addrs, *cur;
+       int have_ipv4 = 0, have_ipv6 = 0;
+
+       if (getaddrinfo(hostname, NULL, NULL, &addrs))
+               error(1, errno, "getaddrinfo");
+
+       cur = addrs;
+       while (cur && !have_ipv4 && !have_ipv6) {
+               if (!have_ipv4 && cur->ai_family == AF_INET) {
+                       memcpy(&daddr, cur->ai_addr, sizeof(daddr));
+                       daddr.sin_port = htons(dest_port);
+                       have_ipv4 = 1;
+               }
+               else if (!have_ipv6 && cur->ai_family == AF_INET6) {
+                       memcpy(&daddr6, cur->ai_addr, sizeof(daddr6));
+                       daddr6.sin6_port = htons(dest_port);
+                       have_ipv6 = 1;
+               }
+               cur = cur->ai_next;
+       }
+       if (addrs)
+               freeaddrinfo(addrs);
+
+       do_ipv4 &= have_ipv4;
+       do_ipv6 &= have_ipv6;
+}
+
+static void do_main(int family)
+{
+       fprintf(stderr, "family:       %s\n",
+                       family == PF_INET ? "INET" : "INET6");
+
+       fprintf(stderr, "test SND\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE);
+
+       fprintf(stderr, "test ENQ\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SCHED);
+
+       fprintf(stderr, "test ENQ + SND\n");
+       do_test(family, SOF_TIMESTAMPING_TX_SCHED |
+                       SOF_TIMESTAMPING_TX_SOFTWARE);
+
+       if (cfg_proto == SOCK_STREAM) {
+               fprintf(stderr, "\ntest ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_ACK);
+
+               fprintf(stderr, "\ntest SND + ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE |
+                               SOF_TIMESTAMPING_TX_ACK);
+
+               fprintf(stderr, "\ntest ENQ + SND + ACK\n");
+               do_test(family, SOF_TIMESTAMPING_TX_SCHED |
+                               SOF_TIMESTAMPING_TX_SOFTWARE |
+                               SOF_TIMESTAMPING_TX_ACK);
+       }
+}
+
+const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" };
+
+int main(int argc, char **argv)
+{
+       if (argc == 1)
+               usage(argv[0]);
+
+       parse_opt(argc, argv);
+       resolve_hostname(argv[argc - 1]);
+
+       fprintf(stderr, "protocol:     %s\n", sock_names[cfg_proto]);
+       fprintf(stderr, "payload:      %u\n", cfg_payload_len);
+       fprintf(stderr, "server port:  %u\n", dest_port);
+       fprintf(stderr, "\n");
+
+       if (do_ipv4)
+               do_main(PF_INET);
+       if (do_ipv6)
+               do_main(PF_INET6);
+
+       return 0;
+}
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/export.h b/tools/testing/selftests/powerpc/copyloops/asm/export.h
new file mode 100644 (file)
index 0000000..2d14a9b
--- /dev/null
@@ -0,0 +1 @@
+#define EXPORT_SYMBOL(x)
diff --git a/tools/testing/selftests/powerpc/signal/.gitignore b/tools/testing/selftests/powerpc/signal/.gitignore
new file mode 100644 (file)
index 0000000..1b89224
--- /dev/null
@@ -0,0 +1,2 @@
+signal
+signal_tm
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/export.h b/tools/testing/selftests/powerpc/stringloops/asm/export.h
new file mode 100644 (file)
index 0000000..2d14a9b
--- /dev/null
@@ -0,0 +1 @@
+#define EXPORT_SYMBOL(x)
index 82c0a9c..4276217 100644 (file)
@@ -7,3 +7,7 @@ tm-fork
 tm-tar
 tm-tmspr
 tm-exec
+tm-signal-context-chk-fpu
+tm-signal-context-chk-gpr
+tm-signal-context-chk-vmx
+tm-signal-context-chk-vsx
diff --git a/tools/testing/selftests/prctl/.gitignore b/tools/testing/selftests/prctl/.gitignore
new file mode 100644 (file)
index 0000000..0b5c274
--- /dev/null
@@ -0,0 +1,3 @@
+disable-tsc-ctxt-sw-stress-test
+disable-tsc-on-off-stress-test
+disable-tsc-test
diff --git a/tools/testing/selftests/prctl/Makefile b/tools/testing/selftests/prctl/Makefile
new file mode 100644 (file)
index 0000000..35aa1c8
--- /dev/null
@@ -0,0 +1,15 @@
+ifndef CROSS_COMPILE
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/)
+
+ifeq ($(ARCH),x86)
+TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \
+               disable-tsc-test
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
+endif
+endif
diff --git a/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c
new file mode 100644 (file)
index 0000000..f7499d1
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
+ *
+ * Tests if the control register is updated correctly
+ * at context switches
+ *
+ * Warning: this test will cause a very high load for a few seconds
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <wait.h>
+
+
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+
+/* Get/set the process' ability to use the timestamp counter instruction */
+#ifndef PR_GET_TSC
+#define PR_GET_TSC 25
+#define PR_SET_TSC 26
+# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
+# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
+#endif
+
+static uint64_t rdtsc(void)
+{
+uint32_t lo, hi;
+/* We cannot use "=A", since this would use %rax on x86_64 */
+__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+return (uint64_t)hi << 32 | lo;
+}
+
+static void sigsegv_expect(int sig)
+{
+       /* */
+}
+
+static void segvtask(void)
+{
+       if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
+       {
+               perror("prctl");
+               exit(0);
+       }
+       signal(SIGSEGV, sigsegv_expect);
+       alarm(10);
+       rdtsc();
+       fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
+       exit(0);
+}
+
+
+static void sigsegv_fail(int sig)
+{
+       fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
+       exit(0);
+}
+
+static void rdtsctask(void)
+{
+       if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
+       {
+               perror("prctl");
+               exit(0);
+       }
+       signal(SIGSEGV, sigsegv_fail);
+       alarm(10);
+       for(;;) rdtsc();
+}
+
+
+int main(void)
+{
+       int n_tasks = 100, i;
+
+       fprintf(stderr, "[No further output means we're allright]\n");
+
+       for (i=0; i<n_tasks; i++)
+               if (fork() == 0)
+               {
+                       if (i & 1)
+                               segvtask();
+                       else
+                               rdtsctask();
+               }
+
+       for (i=0; i<n_tasks; i++)
+               wait(NULL);
+
+       exit(0);
+}
+
diff --git a/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c
new file mode 100644 (file)
index 0000000..a06f027
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
+ *
+ * Tests if the control register is updated correctly
+ * when set with prctl()
+ *
+ * Warning: this test will cause a very high load for a few seconds
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <wait.h>
+
+
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+
+/* Get/set the process' ability to use the timestamp counter instruction */
+#ifndef PR_GET_TSC
+#define PR_GET_TSC 25
+#define PR_SET_TSC 26
+# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
+# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
+#endif
+
+/* snippet from wikipedia :-) */
+
+static uint64_t rdtsc(void)
+{
+uint32_t lo, hi;
+/* We cannot use "=A", since this would use %rax on x86_64 */
+__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+return (uint64_t)hi << 32 | lo;
+}
+
+int should_segv = 0;
+
+static void sigsegv_cb(int sig)
+{
+       if (!should_segv)
+       {
+               fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n");
+               exit(0);
+       }
+       if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0)
+       {
+               perror("prctl");
+               exit(0);
+       }
+       should_segv = 0;
+
+       rdtsc();
+}
+
+static void task(void)
+{
+       signal(SIGSEGV, sigsegv_cb);
+       alarm(10);
+       for(;;)
+       {
+               rdtsc();
+               if (should_segv)
+               {
+                       fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n");
+                       exit(0);
+               }
+               if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0)
+               {
+                       perror("prctl");
+                       exit(0);
+               }
+               should_segv = 1;
+       }
+}
+
+
+int main(void)
+{
+       int n_tasks = 100, i;
+
+       fprintf(stderr, "[No further output means we're allright]\n");
+
+       for (i=0; i<n_tasks; i++)
+               if (fork() == 0)
+                       task();
+
+       for (i=0; i<n_tasks; i++)
+               wait(NULL);
+
+       exit(0);
+}
+
diff --git a/tools/testing/selftests/prctl/disable-tsc-test.c b/tools/testing/selftests/prctl/disable-tsc-test.c
new file mode 100644 (file)
index 0000000..8d494f7
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...)
+ *
+ * Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <inttypes.h>
+
+
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+
+/* Get/set the process' ability to use the timestamp counter instruction */
+#ifndef PR_GET_TSC
+#define PR_GET_TSC 25
+#define PR_SET_TSC 26
+# define PR_TSC_ENABLE         1   /* allow the use of the timestamp counter */
+# define PR_TSC_SIGSEGV                2   /* throw a SIGSEGV instead of reading the TSC */
+#endif
+
+const char *tsc_names[] =
+{
+       [0] = "[not set]",
+       [PR_TSC_ENABLE] = "PR_TSC_ENABLE",
+       [PR_TSC_SIGSEGV] = "PR_TSC_SIGSEGV",
+};
+
+static uint64_t rdtsc(void)
+{
+uint32_t lo, hi;
+/* We cannot use "=A", since this would use %rax on x86_64 */
+__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+return (uint64_t)hi << 32 | lo;
+}
+
+static void sigsegv_cb(int sig)
+{
+       int tsc_val = 0;
+
+       printf("[ SIG_SEGV ]\n");
+       printf("prctl(PR_GET_TSC, &tsc_val); ");
+       fflush(stdout);
+
+       if ( prctl(PR_GET_TSC, &tsc_val) == -1)
+               perror("prctl");
+
+       printf("tsc_val == %s\n", tsc_names[tsc_val]);
+       printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
+       fflush(stdout);
+       if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
+               perror("prctl");
+
+       printf("rdtsc() == ");
+}
+
+int main(void)
+{
+       int tsc_val = 0;
+
+       signal(SIGSEGV, sigsegv_cb);
+
+       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
+       printf("prctl(PR_GET_TSC, &tsc_val); ");
+       fflush(stdout);
+
+       if ( prctl(PR_GET_TSC, &tsc_val) == -1)
+               perror("prctl");
+
+       printf("tsc_val == %s\n", tsc_names[tsc_val]);
+       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
+       printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n");
+       fflush(stdout);
+
+       if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1)
+               perror("prctl");
+
+       printf("rdtsc() == %llu\n", (unsigned long long)rdtsc());
+       printf("prctl(PR_SET_TSC, PR_TSC_SIGSEGV)\n");
+       fflush(stdout);
+
+       if ( prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1)
+               perror("prctl");
+
+       printf("rdtsc() == ");
+       fflush(stdout);
+       printf("%llu\n", (unsigned long long)rdtsc());
+       fflush(stdout);
+
+       exit(EXIT_SUCCESS);
+}
+
diff --git a/tools/testing/selftests/ptp/.gitignore b/tools/testing/selftests/ptp/.gitignore
new file mode 100644 (file)
index 0000000..f562e49
--- /dev/null
@@ -0,0 +1 @@
+testptp
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile
new file mode 100644 (file)
index 0000000..83dd42b
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := testptp
+LDLIBS += -lrt
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c
new file mode 100644 (file)
index 0000000..5d2eae1
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * PTP 1588 clock support - User space test program
+ *
+ * Copyright (C) 2010 OMICRON electronics GmbH
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#define _GNU_SOURCE
+#define __SANE_USERSPACE_TYPES__        /* For PPC64, to get LL64 types */
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <linux/ptp_clock.h>
+
+#define DEVICE "/dev/ptp0"
+
+#ifndef ADJ_SETOFFSET
+#define ADJ_SETOFFSET 0x0100
+#endif
+
+#ifndef CLOCK_INVALID
+#define CLOCK_INVALID -1
+#endif
+
+/* clock_adjtime is not available in GLIBC < 2.14 */
+#if !__GLIBC_PREREQ(2, 14)
+#include <sys/syscall.h>
+static int clock_adjtime(clockid_t id, struct timex *tx)
+{
+       return syscall(__NR_clock_adjtime, id, tx);
+}
+#endif
+
+static clockid_t get_clockid(int fd)
+{
+#define CLOCKFD 3
+#define FD_TO_CLOCKID(fd)      ((~(clockid_t) (fd) << 3) | CLOCKFD)
+
+       return FD_TO_CLOCKID(fd);
+}
+
+static void handle_alarm(int s)
+{
+       printf("received signal %d\n", s);
+}
+
+static int install_handler(int signum, void (*handler)(int))
+{
+       struct sigaction action;
+       sigset_t mask;
+
+       /* Unblock the signal. */
+       sigemptyset(&mask);
+       sigaddset(&mask, signum);
+       sigprocmask(SIG_UNBLOCK, &mask, NULL);
+
+       /* Install the signal handler. */
+       action.sa_handler = handler;
+       action.sa_flags = 0;
+       sigemptyset(&action.sa_mask);
+       sigaction(signum, &action, NULL);
+
+       return 0;
+}
+
+static long ppb_to_scaled_ppm(int ppb)
+{
+       /*
+        * The 'freq' field in the 'struct timex' is in parts per
+        * million, but with a 16 bit binary fractional field.
+        * Instead of calculating either one of
+        *
+        *    scaled_ppm = (ppb / 1000) << 16  [1]
+        *    scaled_ppm = (ppb << 16) / 1000  [2]
+        *
+        * we simply use double precision math, in order to avoid the
+        * truncation in [1] and the possible overflow in [2].
+        */
+       return (long) (ppb * 65.536);
+}
+
+static int64_t pctns(struct ptp_clock_time *t)
+{
+       return t->sec * 1000000000LL + t->nsec;
+}
+
+static void usage(char *progname)
+{
+       fprintf(stderr,
+               "usage: %s [options]\n"
+               " -a val     request a one-shot alarm after 'val' seconds\n"
+               " -A val     request a periodic alarm every 'val' seconds\n"
+               " -c         query the ptp clock's capabilities\n"
+               " -d name    device to open\n"
+               " -e val     read 'val' external time stamp events\n"
+               " -f val     adjust the ptp clock frequency by 'val' ppb\n"
+               " -g         get the ptp clock time\n"
+               " -h         prints this message\n"
+               " -i val     index for event/trigger\n"
+               " -k val     measure the time offset between system and phc clock\n"
+               "            for 'val' times (Maximum 25)\n"
+               " -l         list the current pin configuration\n"
+               " -L pin,val configure pin index 'pin' with function 'val'\n"
+               "            the channel index is taken from the '-i' option\n"
+               "            'val' specifies the auxiliary function:\n"
+               "            0 - none\n"
+               "            1 - external time stamp\n"
+               "            2 - periodic output\n"
+               " -p val     enable output with a period of 'val' nanoseconds\n"
+               " -P val     enable or disable (val=1|0) the system clock PPS\n"
+               " -s         set the ptp clock time from the system time\n"
+               " -S         set the system time from the ptp clock time\n"
+               " -t val     shift the ptp clock time by 'val' seconds\n"
+               " -T val     set the ptp clock time to 'val' seconds\n",
+               progname);
+}
+
+int main(int argc, char *argv[])
+{
+       struct ptp_clock_caps caps;
+       struct ptp_extts_event event;
+       struct ptp_extts_request extts_request;
+       struct ptp_perout_request perout_request;
+       struct ptp_pin_desc desc;
+       struct timespec ts;
+       struct timex tx;
+
+       static timer_t timerid;
+       struct itimerspec timeout;
+       struct sigevent sigevent;
+
+       struct ptp_clock_time *pct;
+       struct ptp_sys_offset *sysoff;
+
+
+       char *progname;
+       unsigned int i;
+       int c, cnt, fd;
+
+       char *device = DEVICE;
+       clockid_t clkid;
+       int adjfreq = 0x7fffffff;
+       int adjtime = 0;
+       int capabilities = 0;
+       int extts = 0;
+       int gettime = 0;
+       int index = 0;
+       int list_pins = 0;
+       int oneshot = 0;
+       int pct_offset = 0;
+       int n_samples = 0;
+       int periodic = 0;
+       int perout = -1;
+       int pin_index = -1, pin_func;
+       int pps = -1;
+       int seconds = 0;
+       int settime = 0;
+
+       int64_t t1, t2, tp;
+       int64_t interval, offset;
+
+       progname = strrchr(argv[0], '/');
+       progname = progname ? 1+progname : argv[0];
+       while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) {
+               switch (c) {
+               case 'a':
+                       oneshot = atoi(optarg);
+                       break;
+               case 'A':
+                       periodic = atoi(optarg);
+                       break;
+               case 'c':
+                       capabilities = 1;
+                       break;
+               case 'd':
+                       device = optarg;
+                       break;
+               case 'e':
+                       extts = atoi(optarg);
+                       break;
+               case 'f':
+                       adjfreq = atoi(optarg);
+                       break;
+               case 'g':
+                       gettime = 1;
+                       break;
+               case 'i':
+                       index = atoi(optarg);
+                       break;
+               case 'k':
+                       pct_offset = 1;
+                       n_samples = atoi(optarg);
+                       break;
+               case 'l':
+                       list_pins = 1;
+                       break;
+               case 'L':
+                       cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func);
+                       if (cnt != 2) {
+                               usage(progname);
+                               return -1;
+                       }
+                       break;
+               case 'p':
+                       perout = atoi(optarg);
+                       break;
+               case 'P':
+                       pps = atoi(optarg);
+                       break;
+               case 's':
+                       settime = 1;
+                       break;
+               case 'S':
+                       settime = 2;
+                       break;
+               case 't':
+                       adjtime = atoi(optarg);
+                       break;
+               case 'T':
+                       settime = 3;
+                       seconds = atoi(optarg);
+                       break;
+               case 'h':
+                       usage(progname);
+                       return 0;
+               case '?':
+               default:
+                       usage(progname);
+                       return -1;
+               }
+       }
+
+       fd = open(device, O_RDWR);
+       if (fd < 0) {
+               fprintf(stderr, "opening %s: %s\n", device, strerror(errno));
+               return -1;
+       }
+
+       clkid = get_clockid(fd);
+       if (CLOCK_INVALID == clkid) {
+               fprintf(stderr, "failed to read clock id\n");
+               return -1;
+       }
+
+       if (capabilities) {
+               if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+                       perror("PTP_CLOCK_GETCAPS");
+               } else {
+                       printf("capabilities:\n"
+                              "  %d maximum frequency adjustment (ppb)\n"
+                              "  %d programmable alarms\n"
+                              "  %d external time stamp channels\n"
+                              "  %d programmable periodic signals\n"
+                              "  %d pulse per second\n"
+                              "  %d programmable pins\n"
+                              "  %d cross timestamping\n",
+                              caps.max_adj,
+                              caps.n_alarm,
+                              caps.n_ext_ts,
+                              caps.n_per_out,
+                              caps.pps,
+                              caps.n_pins,
+                              caps.cross_timestamping);
+               }
+       }
+
+       if (0x7fffffff != adjfreq) {
+               memset(&tx, 0, sizeof(tx));
+               tx.modes = ADJ_FREQUENCY;
+               tx.freq = ppb_to_scaled_ppm(adjfreq);
+               if (clock_adjtime(clkid, &tx)) {
+                       perror("clock_adjtime");
+               } else {
+                       puts("frequency adjustment okay");
+               }
+       }
+
+       if (adjtime) {
+               memset(&tx, 0, sizeof(tx));
+               tx.modes = ADJ_SETOFFSET;
+               tx.time.tv_sec = adjtime;
+               tx.time.tv_usec = 0;
+               if (clock_adjtime(clkid, &tx) < 0) {
+                       perror("clock_adjtime");
+               } else {
+                       puts("time shift okay");
+               }
+       }
+
+       if (gettime) {
+               if (clock_gettime(clkid, &ts)) {
+                       perror("clock_gettime");
+               } else {
+                       printf("clock time: %ld.%09ld or %s",
+                              ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec));
+               }
+       }
+
+       if (settime == 1) {
+               clock_gettime(CLOCK_REALTIME, &ts);
+               if (clock_settime(clkid, &ts)) {
+                       perror("clock_settime");
+               } else {
+                       puts("set time okay");
+               }
+       }
+
+       if (settime == 2) {
+               clock_gettime(clkid, &ts);
+               if (clock_settime(CLOCK_REALTIME, &ts)) {
+                       perror("clock_settime");
+               } else {
+                       puts("set time okay");
+               }
+       }
+
+       if (settime == 3) {
+               ts.tv_sec = seconds;
+               ts.tv_nsec = 0;
+               if (clock_settime(clkid, &ts)) {
+                       perror("clock_settime");
+               } else {
+                       puts("set time okay");
+               }
+       }
+
+       if (extts) {
+               memset(&extts_request, 0, sizeof(extts_request));
+               extts_request.index = index;
+               extts_request.flags = PTP_ENABLE_FEATURE;
+               if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+                       perror("PTP_EXTTS_REQUEST");
+                       extts = 0;
+               } else {
+                       puts("external time stamp request okay");
+               }
+               for (; extts; extts--) {
+                       cnt = read(fd, &event, sizeof(event));
+                       if (cnt != sizeof(event)) {
+                               perror("read");
+                               break;
+                       }
+                       printf("event index %u at %lld.%09u\n", event.index,
+                              event.t.sec, event.t.nsec);
+                       fflush(stdout);
+               }
+               /* Disable the feature again. */
+               extts_request.flags = 0;
+               if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) {
+                       perror("PTP_EXTTS_REQUEST");
+               }
+       }
+
+       if (list_pins) {
+               int n_pins = 0;
+               if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) {
+                       perror("PTP_CLOCK_GETCAPS");
+               } else {
+                       n_pins = caps.n_pins;
+               }
+               for (i = 0; i < n_pins; i++) {
+                       desc.index = i;
+                       if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) {
+                               perror("PTP_PIN_GETFUNC");
+                               break;
+                       }
+                       printf("name %s index %u func %u chan %u\n",
+                              desc.name, desc.index, desc.func, desc.chan);
+               }
+       }
+
+       if (oneshot) {
+               install_handler(SIGALRM, handle_alarm);
+               /* Create a timer. */
+               sigevent.sigev_notify = SIGEV_SIGNAL;
+               sigevent.sigev_signo = SIGALRM;
+               if (timer_create(clkid, &sigevent, &timerid)) {
+                       perror("timer_create");
+                       return -1;
+               }
+               /* Start the timer. */
+               memset(&timeout, 0, sizeof(timeout));
+               timeout.it_value.tv_sec = oneshot;
+               if (timer_settime(timerid, 0, &timeout, NULL)) {
+                       perror("timer_settime");
+                       return -1;
+               }
+               pause();
+               timer_delete(timerid);
+       }
+
+       if (periodic) {
+               install_handler(SIGALRM, handle_alarm);
+               /* Create a timer. */
+               sigevent.sigev_notify = SIGEV_SIGNAL;
+               sigevent.sigev_signo = SIGALRM;
+               if (timer_create(clkid, &sigevent, &timerid)) {
+                       perror("timer_create");
+                       return -1;
+               }
+               /* Start the timer. */
+               memset(&timeout, 0, sizeof(timeout));
+               timeout.it_interval.tv_sec = periodic;
+               timeout.it_value.tv_sec = periodic;
+               if (timer_settime(timerid, 0, &timeout, NULL)) {
+                       perror("timer_settime");
+                       return -1;
+               }
+               while (1) {
+                       pause();
+               }
+               timer_delete(timerid);
+       }
+
+       if (perout >= 0) {
+               if (clock_gettime(clkid, &ts)) {
+                       perror("clock_gettime");
+                       return -1;
+               }
+               memset(&perout_request, 0, sizeof(perout_request));
+               perout_request.index = index;
+               perout_request.start.sec = ts.tv_sec + 2;
+               perout_request.start.nsec = 0;
+               perout_request.period.sec = 0;
+               perout_request.period.nsec = perout;
+               if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) {
+                       perror("PTP_PEROUT_REQUEST");
+               } else {
+                       puts("periodic output request okay");
+               }
+       }
+
+       if (pin_index >= 0) {
+               memset(&desc, 0, sizeof(desc));
+               desc.index = pin_index;
+               desc.func = pin_func;
+               desc.chan = index;
+               if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) {
+                       perror("PTP_PIN_SETFUNC");
+               } else {
+                       puts("set pin function okay");
+               }
+       }
+
+       if (pps != -1) {
+               int enable = pps ? 1 : 0;
+               if (ioctl(fd, PTP_ENABLE_PPS, enable)) {
+                       perror("PTP_ENABLE_PPS");
+               } else {
+                       puts("pps for system time request okay");
+               }
+       }
+
+       if (pct_offset) {
+               if (n_samples <= 0 || n_samples > 25) {
+                       puts("n_samples should be between 1 and 25");
+                       usage(progname);
+                       return -1;
+               }
+
+               sysoff = calloc(1, sizeof(*sysoff));
+               if (!sysoff) {
+                       perror("calloc");
+                       return -1;
+               }
+               sysoff->n_samples = n_samples;
+
+               if (ioctl(fd, PTP_SYS_OFFSET, sysoff))
+                       perror("PTP_SYS_OFFSET");
+               else
+                       puts("system and phc clock time offset request okay");
+
+               pct = &sysoff->ts[0];
+               for (i = 0; i < sysoff->n_samples; i++) {
+                       t1 = pctns(pct+2*i);
+                       tp = pctns(pct+2*i+1);
+                       t2 = pctns(pct+2*i+2);
+                       interval = t2 - t1;
+                       offset = (t2 + t1) / 2 - tp;
+
+                       printf("system time: %lld.%u\n",
+                               (pct+2*i)->sec, (pct+2*i)->nsec);
+                       printf("phc    time: %lld.%u\n",
+                               (pct+2*i+1)->sec, (pct+2*i+1)->nsec);
+                       printf("system time: %lld.%u\n",
+                               (pct+2*i+2)->sec, (pct+2*i+2)->nsec);
+                       printf("system/phc clock time offset is %" PRId64 " ns\n"
+                              "system     clock time delay  is %" PRId64 " ns\n",
+                               offset, interval);
+               }
+
+               free(sysoff);
+       }
+
+       close(fd);
+       return 0;
+}
diff --git a/tools/testing/selftests/ptp/testptp.mk b/tools/testing/selftests/ptp/testptp.mk
new file mode 100644 (file)
index 0000000..4ef2d97
--- /dev/null
@@ -0,0 +1,33 @@
+# PTP 1588 clock support - User space test program
+#
+# Copyright (C) 2010 OMICRON electronics GmbH
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CC        = $(CROSS_COMPILE)gcc
+INC       = -I$(KBUILD_OUTPUT)/usr/include
+CFLAGS    = -Wall $(INC)
+LDLIBS    = -lrt
+PROGS     = testptp
+
+all: $(PROGS)
+
+testptp: testptp.o
+
+clean:
+       rm -f testptp.o
+
+distclean: clean
+       rm -f $(PROGS)
index 5a246a0..15cf56d 100644 (file)
@@ -122,7 +122,7 @@ static int check_itimer(int which)
        else if (which == ITIMER_REAL)
                idle_loop();
 
-       gettimeofday(&end, NULL);
+       err = gettimeofday(&end, NULL);
        if (err < 0) {
                perror("Can't call gettimeofday()\n");
                return -1;
@@ -175,7 +175,7 @@ static int check_timer_create(int which)
 
        user_loop();
 
-       gettimeofday(&end, NULL);
+       err = gettimeofday(&end, NULL);
        if (err < 0) {
                perror("Can't call gettimeofday()\n");
                return -1;
diff --git a/tools/testing/selftests/vDSO/.gitignore b/tools/testing/selftests/vDSO/.gitignore
new file mode 100644 (file)
index 0000000..133bf9e
--- /dev/null
@@ -0,0 +1,2 @@
+vdso_test
+vdso_standalone_test_x86
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile
new file mode 100644 (file)
index 0000000..706b68b
--- /dev/null
@@ -0,0 +1,20 @@
+ifndef CROSS_COMPILE
+CFLAGS := -std=gnu99
+CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector
+ifeq ($(CONFIG_X86_32),y)
+LDLIBS += -lgcc_s
+endif
+
+TEST_PROGS := vdso_test vdso_standalone_test_x86
+
+all: $(TEST_PROGS)
+vdso_test: parse_vdso.c vdso_test.c
+vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c
+       $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \
+               vdso_standalone_test_x86.c parse_vdso.c \
+               -o vdso_standalone_test_x86
+
+include ../lib.mk
+clean:
+       rm -fr $(TEST_PROGS)
+endif
diff --git a/tools/testing/selftests/vDSO/parse_vdso.c b/tools/testing/selftests/vDSO/parse_vdso.c
new file mode 100644 (file)
index 0000000..1dbb4b8
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * parse_vdso.c: Linux reference vDSO parser
+ * Written by Andrew Lutomirski, 2011-2014.
+ *
+ * This code is meant to be linked in to various programs that run on Linux.
+ * As such, it is available with as few restrictions as possible.  This file
+ * is licensed under the Creative Commons Zero License, version 1.0,
+ * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode
+ *
+ * The vDSO is a regular ELF DSO that the kernel maps into user space when
+ * it starts a program.  It works equally well in statically and dynamically
+ * linked binaries.
+ *
+ * This code is tested on x86.  In principle it should work on any
+ * architecture that has a vDSO.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <limits.h>
+#include <elf.h>
+
+/*
+ * To use this vDSO parser, first call one of the vdso_init_* functions.
+ * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR
+ * to vdso_init_from_sysinfo_ehdr.  Otherwise pass auxv to vdso_init_from_auxv.
+ * Then call vdso_sym for each symbol you want.  For example, to look up
+ * gettimeofday on x86_64, use:
+ *
+ *     <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday");
+ * or
+ *     <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
+ *
+ * vdso_sym will return 0 if the symbol doesn't exist or if the init function
+ * failed or was not called.  vdso_sym is a little slow, so its return value
+ * should be cached.
+ *
+ * vdso_sym is threadsafe; the init functions are not.
+ *
+ * These are the prototypes:
+ */
+extern void vdso_init_from_auxv(void *auxv);
+extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
+extern void *vdso_sym(const char *version, const char *name);
+
+
+/* And here's the code. */
+#ifndef ELF_BITS
+# if ULONG_MAX > 0xffffffffUL
+#  define ELF_BITS 64
+# else
+#  define ELF_BITS 32
+# endif
+#endif
+
+#define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x
+#define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x)
+#define ELF(x) ELF_BITS_XFORM(ELF_BITS, x)
+
+static struct vdso_info
+{
+       bool valid;
+
+       /* Load information */
+       uintptr_t load_addr;
+       uintptr_t load_offset;  /* load_addr - recorded vaddr */
+
+       /* Symbol table */
+       ELF(Sym) *symtab;
+       const char *symstrings;
+       ELF(Word) *bucket, *chain;
+       ELF(Word) nbucket, nchain;
+
+       /* Version table */
+       ELF(Versym) *versym;
+       ELF(Verdef) *verdef;
+} vdso_info;
+
+/* Straight from the ELF specification. */
+static unsigned long elf_hash(const unsigned char *name)
+{
+       unsigned long h = 0, g;
+       while (*name)
+       {
+               h = (h << 4) + *name++;
+               if (g = h & 0xf0000000)
+                       h ^= g >> 24;
+               h &= ~g;
+       }
+       return h;
+}
+
+void vdso_init_from_sysinfo_ehdr(uintptr_t base)
+{
+       size_t i;
+       bool found_vaddr = false;
+
+       vdso_info.valid = false;
+
+       vdso_info.load_addr = base;
+
+       ELF(Ehdr) *hdr = (ELF(Ehdr)*)base;
+       if (hdr->e_ident[EI_CLASS] !=
+           (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) {
+               return;  /* Wrong ELF class -- check ELF_BITS */
+       }
+
+       ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff);
+       ELF(Dyn) *dyn = 0;
+
+       /*
+        * We need two things from the segment table: the load offset
+        * and the dynamic table.
+        */
+       for (i = 0; i < hdr->e_phnum; i++)
+       {
+               if (pt[i].p_type == PT_LOAD && !found_vaddr) {
+                       found_vaddr = true;
+                       vdso_info.load_offset = base
+                               + (uintptr_t)pt[i].p_offset
+                               - (uintptr_t)pt[i].p_vaddr;
+               } else if (pt[i].p_type == PT_DYNAMIC) {
+                       dyn = (ELF(Dyn)*)(base + pt[i].p_offset);
+               }
+       }
+
+       if (!found_vaddr || !dyn)
+               return;  /* Failed */
+
+       /*
+        * Fish out the useful bits of the dynamic table.
+        */
+       ELF(Word) *hash = 0;
+       vdso_info.symstrings = 0;
+       vdso_info.symtab = 0;
+       vdso_info.versym = 0;
+       vdso_info.verdef = 0;
+       for (i = 0; dyn[i].d_tag != DT_NULL; i++) {
+               switch (dyn[i].d_tag) {
+               case DT_STRTAB:
+                       vdso_info.symstrings = (const char *)
+                               ((uintptr_t)dyn[i].d_un.d_ptr
+                                + vdso_info.load_offset);
+                       break;
+               case DT_SYMTAB:
+                       vdso_info.symtab = (ELF(Sym) *)
+                               ((uintptr_t)dyn[i].d_un.d_ptr
+                                + vdso_info.load_offset);
+                       break;
+               case DT_HASH:
+                       hash = (ELF(Word) *)
+                               ((uintptr_t)dyn[i].d_un.d_ptr
+                                + vdso_info.load_offset);
+                       break;
+               case DT_VERSYM:
+                       vdso_info.versym = (ELF(Versym) *)
+                               ((uintptr_t)dyn[i].d_un.d_ptr
+                                + vdso_info.load_offset);
+                       break;
+               case DT_VERDEF:
+                       vdso_info.verdef = (ELF(Verdef) *)
+                               ((uintptr_t)dyn[i].d_un.d_ptr
+                                + vdso_info.load_offset);
+                       break;
+               }
+       }
+       if (!vdso_info.symstrings || !vdso_info.symtab || !hash)
+               return;  /* Failed */
+
+       if (!vdso_info.verdef)
+               vdso_info.versym = 0;
+
+       /* Parse the hash table header. */
+       vdso_info.nbucket = hash[0];
+       vdso_info.nchain = hash[1];
+       vdso_info.bucket = &hash[2];
+       vdso_info.chain = &hash[vdso_info.nbucket + 2];
+
+       /* That's all we need. */
+       vdso_info.valid = true;
+}
+
+static bool vdso_match_version(ELF(Versym) ver,
+                              const char *name, ELF(Word) hash)
+{
+       /*
+        * This is a helper function to check if the version indexed by
+        * ver matches name (which hashes to hash).
+        *
+        * The version definition table is a mess, and I don't know how
+        * to do this in better than linear time without allocating memory
+        * to build an index.  I also don't know why the table has
+        * variable size entries in the first place.
+        *
+        * For added fun, I can't find a comprehensible specification of how
+        * to parse all the weird flags in the table.
+        *
+        * So I just parse the whole table every time.
+        */
+
+       /* First step: find the version definition */
+       ver &= 0x7fff;  /* Apparently bit 15 means "hidden" */
+       ELF(Verdef) *def = vdso_info.verdef;
+       while(true) {
+               if ((def->vd_flags & VER_FLG_BASE) == 0
+                   && (def->vd_ndx & 0x7fff) == ver)
+                       break;
+
+               if (def->vd_next == 0)
+                       return false;  /* No definition. */
+
+               def = (ELF(Verdef) *)((char *)def + def->vd_next);
+       }
+
+       /* Now figure out whether it matches. */
+       ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux);
+       return def->vd_hash == hash
+               && !strcmp(name, vdso_info.symstrings + aux->vda_name);
+}
+
+void *vdso_sym(const char *version, const char *name)
+{
+       unsigned long ver_hash;
+       if (!vdso_info.valid)
+               return 0;
+
+       ver_hash = elf_hash(version);
+       ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket];
+
+       for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) {
+               ELF(Sym) *sym = &vdso_info.symtab[chain];
+
+               /* Check for a defined global or weak function w/ right name. */
+               if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
+                       continue;
+               if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL &&
+                   ELF64_ST_BIND(sym->st_info) != STB_WEAK)
+                       continue;
+               if (sym->st_shndx == SHN_UNDEF)
+                       continue;
+               if (strcmp(name, vdso_info.symstrings + sym->st_name))
+                       continue;
+
+               /* Check symbol version. */
+               if (vdso_info.versym
+                   && !vdso_match_version(vdso_info.versym[chain],
+                                          version, ver_hash))
+                       continue;
+
+               return (void *)(vdso_info.load_offset + sym->st_value);
+       }
+
+       return 0;
+}
+
+void vdso_init_from_auxv(void *auxv)
+{
+       ELF(auxv_t) *elf_auxv = auxv;
+       for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++)
+       {
+               if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) {
+                       vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val);
+                       return;
+               }
+       }
+
+       vdso_info.valid = false;
+}
diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c
new file mode 100644 (file)
index 0000000..93b0ebf
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * vdso_test.c: Sample code to test parse_vdso.c on x86
+ * Copyright (c) 2011-2014 Andy Lutomirski
+ * Subject to the GNU General Public License, version 2
+ *
+ * You can amuse yourself by compiling with:
+ * gcc -std=gnu99 -nostdlib
+ *     -Os -fno-asynchronous-unwind-tables -flto -lgcc_s
+ *      vdso_standalone_test_x86.c parse_vdso.c
+ * to generate a small binary.  On x86_64, you can omit -lgcc_s
+ * if you want the binary to be completely standalone.
+ */
+
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdint.h>
+
+extern void *vdso_sym(const char *version, const char *name);
+extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
+extern void vdso_init_from_auxv(void *auxv);
+
+/* We need a libc functions... */
+int strcmp(const char *a, const char *b)
+{
+       /* This implementation is buggy: it never returns -1. */
+       while (*a || *b) {
+               if (*a != *b)
+                       return 1;
+               if (*a == 0 || *b == 0)
+                       return 1;
+               a++;
+               b++;
+       }
+
+       return 0;
+}
+
+/* ...and two syscalls.  This is x86-specific. */
+static inline long x86_syscall3(long nr, long a0, long a1, long a2)
+{
+       long ret;
+#ifdef __x86_64__
+       asm volatile ("syscall" : "=a" (ret) : "a" (nr),
+                     "D" (a0), "S" (a1), "d" (a2) :
+                     "cc", "memory", "rcx",
+                     "r8", "r9", "r10", "r11" );
+#else
+       asm volatile ("int $0x80" : "=a" (ret) : "a" (nr),
+                     "b" (a0), "c" (a1), "d" (a2) :
+                     "cc", "memory" );
+#endif
+       return ret;
+}
+
+static inline long linux_write(int fd, const void *data, size_t len)
+{
+       return x86_syscall3(__NR_write, fd, (long)data, (long)len);
+}
+
+static inline void linux_exit(int code)
+{
+       x86_syscall3(__NR_exit, code, 0, 0);
+}
+
+void to_base10(char *lastdig, time_t n)
+{
+       while (n) {
+               *lastdig = (n % 10) + '0';
+               n /= 10;
+               lastdig--;
+       }
+}
+
+__attribute__((externally_visible)) void c_main(void **stack)
+{
+       /* Parse the stack */
+       long argc = (long)*stack;
+       stack += argc + 2;
+
+       /* Now we're pointing at the environment.  Skip it. */
+       while(*stack)
+               stack++;
+       stack++;
+
+       /* Now we're pointing at auxv.  Initialize the vDSO parser. */
+       vdso_init_from_auxv((void *)stack);
+
+       /* Find gettimeofday. */
+       typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+       gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
+
+       if (!gtod)
+               linux_exit(1);
+
+       struct timeval tv;
+       long ret = gtod(&tv, 0);
+
+       if (ret == 0) {
+               char buf[] = "The time is                     .000000\n";
+               to_base10(buf + 31, tv.tv_sec);
+               to_base10(buf + 38, tv.tv_usec);
+               linux_write(1, buf, sizeof(buf) - 1);
+       } else {
+               linux_exit(ret);
+       }
+
+       linux_exit(0);
+}
+
+/*
+ * This is the real entry point.  It passes the initial stack into
+ * the C entry point.
+ */
+asm (
+       ".text\n"
+       ".global _start\n"
+       ".type _start,@function\n"
+       "_start:\n\t"
+#ifdef __x86_64__
+       "mov %rsp,%rdi\n\t"
+       "jmp c_main"
+#else
+       "push %esp\n\t"
+       "call c_main\n\t"
+       "int $3"
+#endif
+       );
diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c
new file mode 100644 (file)
index 0000000..8daeb7d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * vdso_test.c: Sample code to test parse_vdso.c
+ * Copyright (c) 2014 Andy Lutomirski
+ * Subject to the GNU General Public License, version 2
+ *
+ * Compile with:
+ * gcc -std=gnu99 vdso_test.c parse_vdso.c
+ *
+ * Tested on x86, 32-bit and 64-bit.  It may work on other architectures, too.
+ */
+
+#include <stdint.h>
+#include <elf.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+#include <sys/time.h>
+
+extern void *vdso_sym(const char *version, const char *name);
+extern void vdso_init_from_sysinfo_ehdr(uintptr_t base);
+extern void vdso_init_from_auxv(void *auxv);
+
+int main(int argc, char **argv)
+{
+       unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
+       if (!sysinfo_ehdr) {
+               printf("AT_SYSINFO_EHDR is not present!\n");
+               return 0;
+       }
+
+       vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
+
+       /* Find gettimeofday. */
+       typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz);
+       gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday");
+
+       if (!gtod) {
+               printf("Could not find __vdso_gettimeofday\n");
+               return 1;
+       }
+
+       struct timeval tv;
+       long ret = gtod(&tv, 0);
+
+       if (ret == 0) {
+               printf("The time is %lld.%06lld\n",
+                      (long long)tv.tv_sec, (long long)tv.tv_usec);
+       } else {
+               printf("__vdso_gettimeofday failed\n");
+       }
+
+       return 0;
+}
diff --git a/tools/testing/selftests/watchdog/.gitignore b/tools/testing/selftests/watchdog/.gitignore
new file mode 100644 (file)
index 0000000..5aac515
--- /dev/null
@@ -0,0 +1 @@
+watchdog-test
diff --git a/tools/testing/selftests/watchdog/Makefile b/tools/testing/selftests/watchdog/Makefile
new file mode 100644 (file)
index 0000000..f863c66
--- /dev/null
@@ -0,0 +1,8 @@
+TEST_PROGS := watchdog-test
+
+all: $(TEST_PROGS)
+
+include ../lib.mk
+
+clean:
+       rm -fr $(TEST_PROGS)
diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c
new file mode 100644 (file)
index 0000000..6983d05
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Watchdog Driver Test Program
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+int fd;
+const char v = 'V';
+
+/*
+ * This function simply sends an IOCTL to the driver, which in turn ticks
+ * the PC Watchdog card to reset its internal timer so it doesn't trigger
+ * a computer reset.
+ */
+static void keep_alive(void)
+{
+    int dummy;
+
+    printf(".");
+    ioctl(fd, WDIOC_KEEPALIVE, &dummy);
+}
+
+/*
+ * The main program.  Run the program with "-d" to disable the card,
+ * or "-e" to enable the card.
+ */
+
+static void term(int sig)
+{
+    int ret = write(fd, &v, 1);
+
+    close(fd);
+    if (ret < 0)
+       printf("\nStopping watchdog ticks failed (%d)...\n", errno);
+    else
+       printf("\nStopping watchdog ticks...\n");
+    exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+    int flags;
+    unsigned int ping_rate = 1;
+    int ret;
+
+    setbuf(stdout, NULL);
+
+    fd = open("/dev/watchdog", O_WRONLY);
+
+    if (fd == -1) {
+       printf("Watchdog device not enabled.\n");
+       exit(-1);
+    }
+
+    if (argc > 1) {
+       if (!strncasecmp(argv[1], "-d", 2)) {
+           flags = WDIOS_DISABLECARD;
+           ioctl(fd, WDIOC_SETOPTIONS, &flags);
+           printf("Watchdog card disabled.\n");
+           goto end;
+       } else if (!strncasecmp(argv[1], "-e", 2)) {
+           flags = WDIOS_ENABLECARD;
+           ioctl(fd, WDIOC_SETOPTIONS, &flags);
+           printf("Watchdog card enabled.\n");
+           goto end;
+       } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) {
+           flags = atoi(argv[2]);
+           ioctl(fd, WDIOC_SETTIMEOUT, &flags);
+           printf("Watchdog timeout set to %u seconds.\n", flags);
+           goto end;
+       } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) {
+           ping_rate = strtoul(argv[2], NULL, 0);
+           printf("Watchdog ping rate set to %u seconds.\n", ping_rate);
+       } else {
+           printf("-d to disable, -e to enable, -t <n> to set " \
+               "the timeout,\n-p <n> to set the ping rate, and \n");
+           printf("run by itself to tick the card.\n");
+           goto end;
+       }
+    }
+
+    printf("Watchdog Ticking Away!\n");
+
+    signal(SIGINT, term);
+
+    while(1) {
+       keep_alive();
+       sleep(ping_rate);
+    }
+end:
+    ret = write(fd, &v, 1);
+    if (ret < 0)
+       printf("Stopping watchdog ticks failed (%d)...\n", errno);
+    close(fd);
+    return 0;
+}
index eb17917..7972cc5 100644 (file)
@@ -13,7 +13,7 @@ Statistics for individual zram devices are exported through sysfs nodes at
 
 Kconfig required:
 CONFIG_ZRAM=y
-CONFIG_ZRAM_LZ4_COMPRESS=y
+CONFIG_CRYPTO_LZ4=y
 CONFIG_ZPOOL=y
 CONFIG_ZSMALLOC=y